]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/status.c
Big backport from Enterprise
[bacula/bacula] / bacula / src / filed / status.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2017 Kern Sibbald
5
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  *  Bacula File Daemon Status routines
21  *
22  *    Kern Sibbald, August MMI
23  */
24
25 #include "bacula.h"
26 #include "filed.h"
27 #include "lib/status.h"
28
29 extern void *start_heap;
30
31 extern bool GetWindowsVersionString(char *buf, int maxsiz);
32
33
34 /* Forward referenced functions */
35 static void  list_running_jobs(STATUS_PKT *sp);
36 static void  list_status_header(STATUS_PKT *sp);
37
38 /* Static variables */
39 static char qstatus1[] = ".status %127s\n";
40 static char qstatus2[] = ".status %127s api=%d api_opts=%127s";
41
42 static char OKqstatus[]   = "2000 OK .status\n";
43 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
44
45 /*
46  * General status generator
47  */
48 void output_status(STATUS_PKT *sp)
49 {
50    list_status_header(sp);
51    list_running_jobs(sp);
52    list_terminated_jobs(sp);    /* defined in lib/status.h */
53 }
54
55 #if defined(HAVE_LZO)
56 static const bool have_lzo = true;
57 #else
58 static const bool have_lzo = false;
59 #endif
60
61
62 static void api_list_status_header(STATUS_PKT *sp)
63 {
64    char *p;
65    char buf[300];
66    OutputWriter wt(sp->api_opts);
67    *buf = 0;
68
69    wt.start_group("header");
70    wt.get_output(
71       OT_STRING, "name",        my_name,
72       OT_STRING, "version",     VERSION " (" BDATE ")",
73       OT_STRING, "uname",       HOST_OS " " DISTNAME " " DISTVER,
74       OT_UTIME,  "started",     daemon_start_time,
75       OT_INT,    "jobs_run",    num_jobs_run,
76       OT_INT,    "jobs_running",job_count(),
77       OT_STRING, "winver",      buf,
78       OT_INT64,  "debug",       debug_level,
79       OT_INT,    "trace",       get_trace(),
80       OT_INT64,  "bwlimit",     me->max_bandwidth_per_job,
81       OT_PLUGINS, "plugins",    b_plugin_list,
82       OT_END);
83    p = wt.end_group();
84    sendit(p, strlen(p), sp);
85 }
86
87 static void  list_status_header(STATUS_PKT *sp)
88 {
89    POOL_MEM msg(PM_MESSAGE);
90    char b1[32], b2[32], b3[32], b4[32], b5[35];
91    int64_t memused = (char *)sbrk(0)-(char *)start_heap;
92    int len;
93    char dt[MAX_TIME_LENGTH];
94
95    if (sp->api) {
96       api_list_status_header(sp);
97       return;
98    }
99
100    len = Mmsg(msg, _("%s %sVersion: %s (%s) %s %s %s\n"),
101               my_name, "", VERSION, BDATE, HOST_OS,
102               DISTNAME, DISTVER);
103    sendit(msg.c_str(), len, sp);
104    bstrftime_nc(dt, sizeof(dt), daemon_start_time);
105    len = Mmsg(msg, _("Daemon started %s. Jobs: run=%d running=%d.\n"),
106         dt, num_jobs_run, job_count());
107    sendit(msg.c_str(), len, sp);
108    len = Mmsg(msg, _(" Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
109          edit_uint64_with_commas(memused, b1),
110          edit_uint64_with_commas(sm_bytes, b2),
111          edit_uint64_with_commas(sm_max_bytes, b3),
112          edit_uint64_with_commas(sm_buffers, b4),
113          edit_uint64_with_commas(sm_max_buffers, b5));
114    sendit(msg.c_str(), len, sp);
115    len = Mmsg(msg, _(" Sizes: boffset_t=%d size_t=%d debug=%s trace=%d "
116                      "mode=%d,%d bwlimit=%skB/s\n"),
117               sizeof(boffset_t), sizeof(size_t),
118               edit_uint64(debug_level, b2), get_trace(), (int)DEVELOPER_MODE, 0,
119               edit_uint64_with_commas(me->max_bandwidth_per_job/1024, b1));
120    sendit(msg.c_str(), len, sp);
121    if (b_plugin_list && b_plugin_list->size() > 0) {
122       Plugin *plugin;
123       int len;
124       pm_strcpy(msg, " Plugin: ");
125       foreach_alist(plugin, b_plugin_list) {
126          len = pm_strcat(msg, plugin->file);
127          /* Print plugin version when debug activated */
128          if (debug_level > 0 && plugin->pinfo) {
129             pInfo *info = (pInfo *)plugin->pinfo;
130             pm_strcat(msg, "(");
131             pm_strcat(msg, NPRT(info->plugin_version));
132             len = pm_strcat(msg, ")");
133          }
134          if (len > 80) {
135             pm_strcat(msg, "\n   ");
136          } else {
137             pm_strcat(msg, " ");
138          }
139       }
140       len = pm_strcat(msg, "\n");
141       sendit(msg.c_str(), len, sp);
142    }
143 }
144
145 /*
146  * List running jobs in for humans.
147  */
148 static void  list_running_jobs_plain(STATUS_PKT *sp)
149 {
150    int total_sec, inst_sec;
151    uint64_t total_bps, inst_bps;
152    POOL_MEM msg(PM_MESSAGE);
153    char b1[50], b2[50], b3[50], b4[50], b5[50], b6[50];
154    int len;
155    bool found = false;
156    JCR *njcr;
157    time_t now = time(NULL);
158    char dt[MAX_TIME_LENGTH];
159
160    Dmsg0(1000, "Begin status jcr loop.\n");
161    len = Mmsg(msg, _("\nRunning Jobs:\n"));
162    sendit(msg.c_str(), len, sp);
163    foreach_jcr(njcr) {
164       bstrftime_nc(dt, sizeof(dt), njcr->start_time);
165       if (njcr->JobId == 0) {
166          len = Mmsg(msg, _("Director connected %sat: %s\n"),
167                     (njcr->dir_bsock && njcr->dir_bsock->tls)?_("using TLS "):"",
168                     dt);
169       } else {
170          len = Mmsg(msg, _("JobId %d Job %s is running.\n"),
171                     njcr->JobId, njcr->Job);
172          sendit(msg.c_str(), len, sp);
173          len = Mmsg(msg, _("    %s %s Job started: %s\n"),
174                     job_level_to_str(njcr->getJobLevel()),
175                     job_type_to_str(njcr->getJobType()), dt);
176       }
177       sendit(msg.c_str(), len, sp);
178       if (njcr->JobId == 0) {
179          continue;
180       }
181       if (njcr->last_time == 0) {
182          njcr->last_time = njcr->start_time;
183       }
184       total_sec = now - njcr->start_time;
185       inst_sec = now - njcr->last_time;
186       if (total_sec <= 0) {
187          total_sec = 1;
188       }
189       if (inst_sec <= 0) {
190          inst_sec = 1;
191       }
192       /* Instanteous bps not smoothed */
193       inst_bps = (njcr->JobBytes - njcr->LastJobBytes) / inst_sec;
194       if (njcr->LastRate <= 0) {
195          njcr->LastRate = inst_bps;
196       }
197       /* Smooth the instantaneous bps a bit */
198       inst_bps = (2 * njcr->LastRate + inst_bps) / 3;
199       /* total bps (AveBytes/sec) since start of job */
200       total_bps = njcr->JobBytes / total_sec;
201       len = Mmsg(msg,  _("    Files=%s Bytes=%s AveBytes/sec=%s LastBytes/sec=%s Errors=%d\n"
202                          "    Bwlimit=%s ReadBytes=%s\n"),
203            edit_uint64_with_commas(njcr->JobFiles, b1),
204            edit_uint64_with_commas(njcr->JobBytes, b2),
205            edit_uint64_with_commas(total_bps, b3),
206            edit_uint64_with_commas(inst_bps, b4),
207            njcr->JobErrors, edit_uint64_with_commas(njcr->max_bandwidth, b5),
208            edit_uint64_with_commas(njcr->ReadBytes, b6));
209       sendit(msg.c_str(), len, sp);
210
211       if (njcr->is_JobType(JT_RESTORE) && njcr->ExpectedFiles > 0) {
212          len = Mmsg(msg, _("    Files: Restored=%s Expected=%s Completed=%d%%\n"),
213             edit_uint64_with_commas(njcr->num_files_examined, b1),
214             edit_uint64_with_commas(njcr->ExpectedFiles, b2),
215             (100*njcr->num_files_examined)/njcr->ExpectedFiles);
216       } else {
217          len = Mmsg(msg, _("    Files: Examined=%s Backed up=%s\n"),
218             edit_uint64_with_commas(njcr->num_files_examined, b1),
219             edit_uint64_with_commas(njcr->JobFiles, b2));
220       }
221       /* Update only every 10 seconds */
222       if (now - njcr->last_time > 10) {
223          njcr->LastRate = inst_bps;
224          njcr->LastJobBytes = njcr->JobBytes;
225          njcr->last_time = now;
226       }
227       sendit(msg.c_str(), len, sp);
228       if (njcr->JobFiles > 0) {
229          njcr->lock();
230          len = Mmsg(msg, _("    Processing file: %s\n"), njcr->last_fname);
231          njcr->unlock();
232          sendit(msg.c_str(), len, sp);
233       }
234
235       found = true;
236       if (njcr->store_bsock) {
237          len = Mmsg(msg, "    SDReadSeqNo=%" lld " fd=%d SDtls=%d\n",
238                     njcr->store_bsock->read_seqno, njcr->store_bsock->m_fd,
239                     (njcr->store_bsock->tls)?1:0);
240          sendit(msg.c_str(), len, sp);
241       } else {
242          len = Mmsg(msg, _("    SDSocket closed.\n"));
243          sendit(msg.c_str(), len, sp);
244       }
245    }
246    endeach_jcr(njcr);
247
248    if (!found) {
249       len = Mmsg(msg, _("No Jobs running.\n"));
250       sendit(msg.c_str(), len, sp);
251    }
252    sendit(_("====\n"), 5, sp);
253 }
254
255 /*
256  * List running jobs for Bat or Bweb in a format
257  *  simpler to parse. Be careful when changing this
258  *  subroutine.
259  */
260 static void  list_running_jobs_api(STATUS_PKT *sp)
261 {
262    OutputWriter ow(sp->api_opts);
263    int sec, bps;
264    char *p;
265    JCR *njcr;
266
267    /* API v1, edit with comma, space before the name, sometime ' ' as separator */
268
269    foreach_jcr(njcr) {
270       p = ow.get_output(OT_CLEAR, OT_START_OBJ, OT_END);
271
272       if (njcr->JobId == 0) {
273          ow.get_output(OT_UTIME, "DirectorConnected", njcr->start_time,
274                        OT_INT, "DirTLS", (njcr->dir_bsock && njcr->dir_bsock->tls)?1:0,
275                        OT_END);
276       } else {
277          ow.get_output(OT_INT32,   "JobId", njcr->JobId,
278                        OT_STRING,  "Job",   njcr->Job,
279                        OT_JOBLEVEL,"Level", njcr->getJobLevel(),
280                        OT_JOBTYPE, "Type",  njcr->getJobType(),
281                        OT_JOBSTATUS, "Status", njcr->getJobStatus(),
282                        OT_UTIME,   "StartTime", njcr->start_time,
283                        OT_END);
284
285       }
286       sendit(p, strlen(p), sp);
287       if (njcr->JobId == 0) {
288          continue;
289       }
290       sec = time(NULL) - njcr->start_time;
291       if (sec <= 0) {
292          sec = 1;
293       }
294       bps = (int)(njcr->JobBytes / sec);
295       ow.get_output(OT_CLEAR,
296                     OT_INT32,   "JobFiles",  njcr->JobFiles,
297                     OT_SIZE,    "JobBytes",  njcr->JobBytes,
298                     OT_INT,     "Bytes/sec", bps,
299                     OT_INT,     "Errors",    njcr->JobErrors,
300                     OT_INT64,   "Bwlimit",   njcr->max_bandwidth,
301                     OT_SIZE,    "ReadBytes", njcr->ReadBytes,
302                     OT_END);
303
304       ow.get_output(OT_INT32,  "Files Examined",  njcr->num_files_examined, OT_END);
305
306       if (njcr->is_JobType(JT_RESTORE) && njcr->ExpectedFiles > 0) {
307          ow.get_output(OT_INT32,  "Expected Files",  njcr->ExpectedFiles,
308                        OT_INT32,  "Percent Complete", 100*(njcr->num_files_examined/njcr->ExpectedFiles),
309                        OT_END);
310       }
311
312       sendit(p, strlen(p), sp);
313       ow.get_output(OT_CLEAR, OT_END);
314
315       if (njcr->JobFiles > 0) {
316          njcr->lock();
317          ow.get_output(OT_STRING,  "Processing file", njcr->last_fname, OT_END);
318          njcr->unlock();
319       }
320
321       if (njcr->store_bsock) {
322          ow.get_output(OT_INT64, "SDReadSeqNo", (int64_t)njcr->store_bsock->read_seqno,
323                        OT_INT,   "fd",          njcr->store_bsock->m_fd,
324                        OT_INT,   "SDtls",       (njcr->store_bsock->tls)?1:0,
325                        OT_END);
326       } else {
327          ow.get_output(OT_STRING, "SDSocket", "closed", OT_END);
328       }
329       ow.get_output(OT_END_OBJ, OT_END);
330       sendit(p, strlen(p), sp);
331    }
332    endeach_jcr(njcr);
333 }
334
335 static void  list_running_jobs(STATUS_PKT *sp)
336 {
337    if (sp->api) {
338       list_running_jobs_api(sp);
339    } else {
340       list_running_jobs_plain(sp);
341    }
342 }
343
344 /*
345  * Status command from Director
346  */
347 int status_cmd(JCR *jcr)
348 {
349    BSOCK *user = jcr->dir_bsock;
350    STATUS_PKT sp;
351
352    user->fsend("\n");
353    sp.bs = user;
354    sp.api = false;                         /* no API output */
355    output_status(&sp);
356
357    user->signal(BNET_EOD);
358    return 1;
359 }
360
361 /*
362  * .status command from Director
363  */
364 int qstatus_cmd(JCR *jcr)
365 {
366    BSOCK *dir = jcr->dir_bsock;
367    POOLMEM *cmd;
368    JCR *njcr;
369    s_last_job* job;
370    STATUS_PKT sp;
371
372    sp.bs = dir;
373    cmd = get_memory(dir->msglen+1);
374
375    if (sscanf(dir->msg, qstatus2, cmd, &sp.api, sp.api_opts) != 3) {
376       if (sscanf(dir->msg, qstatus1, cmd) != 1) {
377          pm_strcpy(&jcr->errmsg, dir->msg);
378          Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
379          dir->fsend(_("2900 Bad .status command, missing argument.\n"));
380          dir->signal(BNET_EOD);
381          free_memory(cmd);
382          return 0;
383       }
384    }
385    unbash_spaces(cmd);
386
387    if (strcasecmp(cmd, "current") == 0) {
388       dir->fsend(OKqstatus, cmd);
389       foreach_jcr(njcr) {
390          if (njcr->JobId != 0) {
391             dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
392          }
393       }
394       endeach_jcr(njcr);
395    } else if (strcasecmp(cmd, "last") == 0) {
396       dir->fsend(OKqstatus, cmd);
397       if ((last_jobs) && (last_jobs->size() > 0)) {
398          job = (s_last_job*)last_jobs->last();
399          dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
400       }
401    } else if (strcasecmp(cmd, "header") == 0) {
402        sp.api = true;
403        list_status_header(&sp);
404    } else if (strcasecmp(cmd, "running") == 0) {
405        sp.api = true;
406        list_running_jobs(&sp);
407    } else if (strcasecmp(cmd, "terminated") == 0) {
408        sp.api = MAX(sp.api, 1);
409        list_terminated_jobs(&sp); /* defined in lib/status.h */
410    } else {
411       pm_strcpy(&jcr->errmsg, dir->msg);
412       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
413       dir->fsend(_("2900 Bad .status command, wrong argument.\n"));
414       dir->signal(BNET_EOD);
415       free_memory(cmd);
416       return 0;
417    }
418
419    dir->signal(BNET_EOD);
420    free_memory(cmd);
421    return 1;
422 }