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