]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/status.c
Fix some problems noted by clang
[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          ow.get_output(OT_UTIME, "DirectorConnected", njcr->start_time,
267                        OT_INT, "DirTLS", (njcr->dir_bsock && njcr->dir_bsock->tls)?1:0,
268                        OT_END);
269       } else {
270          ow.get_output(OT_INT32,   "JobId", njcr->JobId,
271                        OT_STRING,  "Job",   njcr->Job,
272                        OT_JOBLEVEL,"Level", njcr->getJobLevel(),
273                        OT_JOBTYPE, "Type",  njcr->getJobType(),
274                        OT_JOBSTATUS, "Status", njcr->getJobStatus(),
275                        OT_UTIME,   "StartTime", njcr->start_time,
276                        OT_END);
277
278       }
279       sendit(p, strlen(p), sp);
280       if (njcr->JobId == 0) {
281          continue;
282       }
283       sec = time(NULL) - njcr->start_time;
284       if (sec <= 0) {
285          sec = 1;
286       }
287       bps = (int)(njcr->JobBytes / sec);
288       ow.get_output(OT_CLEAR,
289                     OT_INT32,   "JobFiles",  njcr->JobFiles,
290                     OT_SIZE,    "JobBytes",  njcr->JobBytes,
291                     OT_INT,     "Bytes/sec", bps,
292                     OT_INT,     "Errors",    njcr->JobErrors,
293                     OT_INT64,   "Bwlimit",   njcr->max_bandwidth,
294                     OT_SIZE,    "ReadBytes", njcr->ReadBytes,
295                     OT_END);
296
297       ow.get_output(OT_INT32,  "Files Examined",  njcr->num_files_examined, OT_END);
298
299       if (njcr->is_JobType(JT_RESTORE) && njcr->ExpectedFiles > 0) {
300          ow.get_output(OT_INT32,  "Expected Files",  njcr->ExpectedFiles,
301                        OT_INT32,  "Percent Complete", 100*(njcr->num_files_examined/njcr->ExpectedFiles),
302                        OT_END);
303       }
304
305       sendit(p, strlen(p), sp);
306       ow.get_output(OT_CLEAR, OT_END);
307
308       if (njcr->JobFiles > 0) {
309          njcr->lock();
310          ow.get_output(OT_STRING,  "Processing file", njcr->last_fname, OT_END);
311          njcr->unlock();
312       }
313
314       if (njcr->store_bsock) {
315          ow.get_output(OT_INT64, "SDReadSeqNo", (int64_t)njcr->store_bsock->read_seqno,
316                        OT_INT,   "fd",          njcr->store_bsock->m_fd,
317                        OT_INT,   "SDtls",       (njcr->store_bsock->tls)?1:0,
318                        OT_END);
319       } else {
320          ow.get_output(OT_STRING, "SDSocket", "closed", OT_END);
321       }
322       ow.get_output(OT_END_OBJ, OT_END);
323       sendit(p, strlen(p), sp);
324    }
325    endeach_jcr(njcr);
326 }
327
328 static void  list_running_jobs(STATUS_PKT *sp)
329 {
330    if (sp->api) {
331       list_running_jobs_api(sp);
332    } else {
333       list_running_jobs_plain(sp);
334    }
335 }
336
337 /*
338  * Status command from Director
339  */
340 int status_cmd(JCR *jcr)
341 {
342    BSOCK *user = jcr->dir_bsock;
343    STATUS_PKT sp;
344
345    user->fsend("\n");
346    sp.bs = user;
347    sp.api = false;                         /* no API output */
348    output_status(&sp);
349
350    user->signal(BNET_EOD);
351    return 1;
352 }
353
354 /*
355  * .status command from Director
356  */
357 int qstatus_cmd(JCR *jcr)
358 {
359    BSOCK *dir = jcr->dir_bsock;
360    POOLMEM *cmd;
361    JCR *njcr;
362    s_last_job* job;
363    STATUS_PKT sp;
364
365    sp.bs = dir;
366    cmd = get_memory(dir->msglen+1);
367
368    if (sscanf(dir->msg, qstatus2, cmd, &sp.api, sp.api_opts) != 3) {
369       if (sscanf(dir->msg, qstatus1, cmd) != 1) {
370          pm_strcpy(&jcr->errmsg, dir->msg);
371          Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
372          dir->fsend(_("2900 Bad .status command, missing argument.\n"));
373          dir->signal(BNET_EOD);
374          free_memory(cmd);
375          return 0;
376       }
377    }
378    unbash_spaces(cmd);
379
380    if (strcasecmp(cmd, "current") == 0) {
381       dir->fsend(OKqstatus, cmd);
382       foreach_jcr(njcr) {
383          if (njcr->JobId != 0) {
384             dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
385          }
386       }
387       endeach_jcr(njcr);
388    } else if (strcasecmp(cmd, "last") == 0) {
389       dir->fsend(OKqstatus, cmd);
390       if ((last_jobs) && (last_jobs->size() > 0)) {
391          job = (s_last_job*)last_jobs->last();
392          dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
393       }
394    } else if (strcasecmp(cmd, "header") == 0) {
395        sp.api = true;
396        list_status_header(&sp);
397    } else if (strcasecmp(cmd, "running") == 0) {
398        sp.api = true;
399        list_running_jobs(&sp);
400    } else if (strcasecmp(cmd, "terminated") == 0) {
401        sp.api = MAX(sp.api, 1);
402        list_terminated_jobs(&sp); /* defined in lib/status.h */
403    } else {
404       pm_strcpy(&jcr->errmsg, dir->msg);
405       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
406       dir->fsend(_("2900 Bad .status command, wrong argument.\n"));
407       dir->signal(BNET_EOD);
408       free_memory(cmd);
409       return 0;
410    }
411
412    dir->signal(BNET_EOD);
413    free_memory(cmd);
414    return 1;
415 }