1 /*
2    Bacula(R) - The Network Backup Solution
3 
4    Copyright (C) 2000-2020 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  *  This file handles the status command
21  *
22  *     Kern Sibbald, May MMIII
23  *
24  *
25  */
26 
27 #include "bacula.h"
28 #include "stored.h"
29 #include "lib/status.h"
30 #include "sd_plugins.h"
31 
32 /* Imported functions */
33 extern void dbg_print_plugin(FILE *fp);
34 
35 /* Imported variables */
36 extern BSOCK *filed_chan;
37 extern void *start_heap;
38 
39 /* Static variables */
40 static char OKqstatus[]   = "3000 OK .status\n";
41 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
42 
43 
44 /* Forward referenced functions */
45 static void sendit(POOL_MEM &msg, int len, STATUS_PKT *sp);
46 static void sendit(const char *msg, int len, void *arg);
47 static void dbg_sendit(const char *msg, int len, void *arg);
48 static void send_blocked_status(DEVICE *dev, STATUS_PKT *sp);
49 static void send_device_status(DEVICE *dev, STATUS_PKT *sp);
50 static void list_running_jobs(STATUS_PKT *sp);
51 static void list_jobs_waiting_on_reservation(STATUS_PKT *sp);
52 static void list_status_header(STATUS_PKT *sp);
53 static void list_devices(STATUS_PKT *sp, char *name=NULL);
54 static void list_plugins(STATUS_PKT *sp);
55 static void list_cloud_transfers(STATUS_PKT *sp, bool verbose);
56 static void list_collectors_status(STATUS_PKT *sp, char *collname);
57 static void api_collectors_status(STATUS_PKT *sp, char *collname);
58 
status_alert_callback(void * ctx,const char * short_msg,const char * long_msg,char * Volume,int severity,int flags,int alertno,utime_t alert_time)59 void status_alert_callback(void *ctx, const char *short_msg,
60    const char *long_msg, char *Volume, int severity,
61    int flags, int alertno, utime_t alert_time)
62 {
63    STATUS_PKT *sp = (STATUS_PKT *)ctx;
64    const char *type = "Unknown";
65    POOL_MEM send_msg(PM_MESSAGE);
66    char edt[50];
67    int len;
68 
69    switch (severity) {
70    case 'C':
71       type = "Critical";
72       break;
73    case 'W':
74       type = "Warning";
75       break;
76    case 'I':
77       type = "Info";
78       break;
79    }
80    bstrftimes(edt, sizeof(edt), alert_time);
81    if (chk_dbglvl(10)) {
82       len = Mmsg(send_msg, _("    %s Alert: at %s Volume=\"%s\" flags=0x%x alert=%s\n"),
83          type, edt, Volume, flags, long_msg);
84    } else {
85       len = Mmsg(send_msg, _("    %s Alert: at %s Volume=\"%s\" alert=%s\n"),
86          type, edt, Volume, short_msg);
87    }
88    sendit(send_msg, len, sp);
89 }
90 
91 
92 /*
93  * Status command from Director
94  */
output_status(STATUS_PKT * sp)95 void output_status(STATUS_PKT *sp)
96 {
97    POOL_MEM msg(PM_MESSAGE);
98    int len;
99 
100    list_status_header(sp);
101 
102    /*
103     * List running jobs
104     */
105    list_running_jobs(sp);
106 
107    /*
108     * List jobs stuck in reservation system
109     */
110    list_jobs_waiting_on_reservation(sp);
111 
112    /*
113     * List terminated jobs (defined in lib/status.h)
114     */
115    list_terminated_jobs(sp);
116 
117    /*
118     * List devices
119     */
120    list_devices(sp);
121 
122    /*
123     * List cloud transfers
124     */
125    list_cloud_transfers(sp, false);
126 
127 
128    len = Mmsg(msg, _("Used Volume status:\n"));
129    if (!sp->api) sendit(msg, len, sp);
130 
131    list_volumes(sendit, (void *)sp);
132    if (!sp->api) sendit("====\n\n", 6, sp);
133 
134    list_spool_stats(sendit, (void *)sp);
135    if (!sp->api) sendit("====\n\n", 6, sp);
136 
137    if (chk_dbglvl(10)) {
138       dbg_print_plugin(stdout);
139    }
140 }
141 
list_resources(STATUS_PKT * sp)142 static void list_resources(STATUS_PKT *sp)
143 {
144 #ifdef when_working
145    POOL_MEM msg(PM_MESSAGE);
146    int len;
147 
148    len = Mmsg(msg, _("\nSD Resources:\n"));
149    if (!sp->api) sendit(msg, len, sp);
150    dump_resource(R_DEVICE, resources[R_DEVICE-r_first], sp);
151    if (!sp->api) sendit("====\n\n", 6, sp);
152 #endif
153 }
154 
155 #ifdef xxxx
find_device(char * devname)156 static find_device(char *devname)
157 {
158    foreach_res(device, R_DEVICE) {
159       if (strcasecmp(device->hdr.name, devname) == 0) {
160          found = true;
161          break;
162       }
163    }
164    if (!found) {
165       foreach_res(changer, R_AUTOCHANGER) {
166          if (strcasecmp(changer->hdr.name, devname) == 0) {
167             break;
168          }
169       }
170    }
171 }
172 #endif
173 
api_list_one_device(char * name,DEVICE * dev,STATUS_PKT * sp)174 static void api_list_one_device(char *name, DEVICE *dev, STATUS_PKT *sp)
175 {
176    OutputWriter ow(sp->api_opts);
177    int zero=0;
178    int blocked=0;
179    uint64_t f, t;
180    const char *p=NULL;
181 
182    if (!dev) {
183       return;
184    }
185 
186    dev->get_freespace(&f, &t);
187 
188    ow.get_output(OT_START_OBJ,
189                  OT_STRING, "name", dev->device->hdr.name,
190                  OT_STRING, "archive_device", dev->archive_name(),
191                  OT_STRING, "type", dev->print_type(),
192                  OT_STRING, "media_type", dev->device->media_type,
193                  OT_INT,    "open", (int)dev->is_open(),
194                  OT_INT,    "writers",      dev->num_writers,
195                  OT_INT32,  "maximum_concurrent_jobs", dev->max_concurrent_jobs,
196                  OT_INT64,  "maximum_volume_size", dev->max_volume_size,
197                  OT_INT,    "read_only", dev->device->read_only,
198                  OT_INT,    "autoselect", dev->device->autoselect,
199                  OT_INT,    "enabled", dev->enabled,
200                  OT_INT64,  "free_space", f,
201                  OT_INT64,  "total_space", t,
202                  OT_INT64,  "devno",     dev->devno,
203                  OT_END);
204 
205    if (dev->is_open()) {
206       if (dev->is_labeled()) {
207          ow.get_output(OT_STRING, "mounted", dev->blocked()?"0":"1",
208                        OT_STRING, "waiting", dev->blocked()?"1":"0",
209                        OT_STRING, "volume",  dev->VolHdr.VolumeName,
210                        OT_STRING, "pool",    NPRTB(dev->pool_name),
211                        OT_END);
212       } else {
213          ow.get_output(OT_INT,    "mounted", zero,
214                        OT_INT,    "waiting", zero,
215                        OT_STRING, "volume",  "",
216                        OT_STRING, "pool",    "",
217                        OT_END);
218       }
219 
220       blocked = 1;
221       switch(dev->blocked()) {
222       case BST_UNMOUNTED:
223          p = "User unmounted";
224          break;
225       case BST_UNMOUNTED_WAITING_FOR_SYSOP:
226          p = "User unmounted during wait for media/mount";
227          break;
228       case BST_DOING_ACQUIRE:
229          p = "Device is being initialized";
230          break;
231       case BST_WAITING_FOR_SYSOP:
232          p = "Waiting for mount or create a volume";
233          break;
234       case BST_WRITING_LABEL:
235          p = "Labeling a Volume";
236          break;
237       default:
238          blocked=0;
239          p = NULL;
240       }
241 
242       /* TODO: give more information about blocked status
243        * and the volume needed if WAITING for SYSOP
244        */
245       ow.get_output(OT_STRING, "blocked_desc", NPRTB(p),
246                     OT_INT,    "blocked",      blocked,
247                     OT_END);
248 
249       ow.get_output(OT_INT, "append", (int)dev->can_append(),
250                     OT_END);
251 
252       if (dev->can_append()) {
253          ow.get_output(OT_INT64, "bytes",  dev->VolCatInfo.VolCatBytes,
254                        OT_INT32, "blocks", dev->VolCatInfo.VolCatBlocks,
255                        OT_END);
256 
257       } else {  /* reading */
258          ow.get_output(OT_INT64, "bytes",  dev->VolCatInfo.VolCatRBytes,
259                        OT_INT32, "blocks", dev->VolCatInfo.VolCatReads, /* might not be blocks */
260                        OT_END);
261 
262       }
263       ow.get_output(OT_INT, "file",  dev->file,
264                     OT_INT, "block", dev->block_num,
265                     OT_END);
266    } else {
267       ow.get_output(OT_INT,    "mounted", zero,
268                     OT_INT,    "waiting", zero,
269                     OT_STRING, "volume",  "",
270                     OT_STRING, "pool",    "",
271                     OT_STRING, "blocked_desc", "",
272                     OT_INT,    "blocked", zero,
273                     OT_INT,    "append",  zero,
274                     OT_INT,    "bytes",   zero,
275                     OT_INT,    "blocks",  zero,
276                     OT_INT,    "file",    zero,
277                     OT_INT,    "block",   zero,
278                     OT_END);
279    }
280 
281    p = ow.get_output(OT_END_OBJ, OT_END);
282    sendit(p, strlen(p), sp);
283 }
284 
285 
list_one_device(char * name,DEVICE * dev,STATUS_PKT * sp)286 static void list_one_device(char *name, DEVICE *dev, STATUS_PKT *sp)
287 {
288    char b1[35], b2[35], b3[35];
289    POOL_MEM msg(PM_MESSAGE);
290    int len;
291    int bpb;
292 
293    if (sp->api > 1) {
294       api_list_one_device(name, dev, sp);
295       return;
296    }
297 
298    if (!dev) {
299       len = Mmsg(msg, _("\nDevice \"%s\" is not open or does not exist.\n"),
300                  name);
301       sendit(msg, len, sp);
302       if (!sp->api) sendit("==\n", 4, sp);
303       return;
304    }
305 
306    if (dev->is_open()) {
307       if (dev->is_labeled()) {
308          len = Mmsg(msg, _("\nDevice %s is %s %s:\n"
309                            "    Volume:      %s\n"
310                            "    Pool:        %s\n"
311                            "    Media type:  %s\n"),
312             dev->print_type(), dev->print_name(),
313             dev->blocked()?_("waiting for"):_("mounted with"),
314             dev->VolHdr.VolumeName,
315             dev->pool_name[0]?dev->pool_name:_("*unknown*"),
316             dev->device->media_type);
317          sendit(msg, len, sp);
318       } else {
319          len = Mmsg(msg, _("\nDevice %s: %s open but no Bacula volume is currently mounted.\n"),
320             dev->print_type(), dev->print_name());
321          sendit(msg, len, sp);
322       }
323       if (dev->can_append()) {
324          bpb = dev->VolCatInfo.VolCatBlocks;
325          if (bpb <= 0) {
326             bpb = 1;
327          }
328          bpb = dev->VolCatInfo.VolCatBytes / bpb;
329          len = Mmsg(msg, _("    Total Bytes=%s Blocks=%s Bytes/block=%s\n"),
330             edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, b1),
331             edit_uint64_with_commas(dev->VolCatInfo.VolCatBlocks, b2),
332             edit_uint64_with_commas(bpb, b3));
333          sendit(msg, len, sp);
334       } else {  /* reading */
335          bpb = dev->VolCatInfo.VolCatReads;
336          if (bpb <= 0) {
337             bpb = 1;
338          }
339          if (dev->VolCatInfo.VolCatRBytes > 0) {
340             bpb = dev->VolCatInfo.VolCatRBytes / bpb;
341          } else {
342             bpb = 0;
343          }
344          len = Mmsg(msg, _("    Total Bytes Read=%s Blocks Read=%s Bytes/block=%s\n"),
345             edit_uint64_with_commas(dev->VolCatInfo.VolCatRBytes, b1),
346             edit_uint64_with_commas(dev->VolCatInfo.VolCatReads, b2),
347             edit_uint64_with_commas(bpb, b3));
348          sendit(msg, len, sp);
349       }
350       len = Mmsg(msg, _("    Positioned at File=%s Block=%s\n"),
351          edit_uint64_with_commas(dev->file, b1),
352          edit_uint64_with_commas(dev->block_num, b2));
353       sendit(msg, len, sp);
354    } else {
355       len = Mmsg(msg, _("\nDevice %s: %s is not open.\n"),
356                  dev->print_type(), dev->print_name());
357       sendit(msg, len, sp);
358    }
359    send_blocked_status(dev, sp);
360 
361    /* TODO: We need to check with Mount command, maybe we can
362     * display this number only when the device is open.
363     */
364    if (dev->is_file()) {
365       char ed1[50];
366       uint64_t f, t;
367       dev->get_freespace(&f, &t);
368       if (t > 0) {              /* We might not have access to numbers */
369          len = Mmsg(msg, _("   Available %sSpace=%sB\n"),
370                     dev->is_cloud() ? _("Cache ") : "",
371                     edit_uint64_with_suffix(f, ed1));
372          sendit(msg, len, sp);
373       }
374    }
375 
376    dev->show_tape_alerts((DCR *)sp, list_short, list_all, status_alert_callback);
377 
378    if (!sp->api) sendit("==\n", 4, sp);
379 }
380 
_dbg_list_one_device(char * name,DEVICE * dev,const char * file,int line)381 void _dbg_list_one_device(char *name, DEVICE *dev, const char *file, int line)
382 {
383    STATUS_PKT sp;
384    sp.bs = NULL;
385    sp.callback = dbg_sendit;
386    sp.context = NULL;
387    d_msg(file, line, 0, "Called dbg_list_one_device():");
388    list_one_device(name, dev, &sp);
389    send_device_status(dev, &sp);
390 }
391 
list_one_autochanger(char * name,AUTOCHANGER * changer,STATUS_PKT * sp)392 static void list_one_autochanger(char *name, AUTOCHANGER *changer, STATUS_PKT *sp)
393 {
394    int     len;
395    char   *p;
396    DEVRES *device;
397    POOL_MEM msg(PM_MESSAGE);
398    OutputWriter ow(sp->api_opts);
399 
400    if (sp->api > 1) {
401       ow.get_output(OT_START_OBJ,
402                     OT_STRING,    "autochanger",  changer->hdr.name,
403                     OT_END);
404 
405       ow.start_group("devices");
406 
407       foreach_alist(device, changer->device) {
408          ow.get_output(OT_START_OBJ,
409                        OT_STRING, "name",  device->hdr.name,
410                        OT_STRING, "device",device->device_name,
411                        OT_END_OBJ,
412                        OT_END);
413       }
414 
415       ow.end_group();
416 
417       p = ow.get_output(OT_END_OBJ, OT_END);
418       sendit(p, strlen(p), sp);
419 
420    } else {
421 
422       len = Mmsg(msg, _("Autochanger \"%s\" with devices:\n"),
423                  changer->hdr.name);
424       sendit(msg, len, sp);
425 
426       foreach_alist(device, changer->device) {
427          if (device->dev) {
428             len = Mmsg(msg, "   %s\n", device->dev->print_name());
429             sendit(msg, len, sp);
430          } else {
431             len = Mmsg(msg, "   %s\n", device->hdr.name);
432             sendit(msg, len, sp);
433          }
434       }
435    }
436 }
437 
list_devices(STATUS_PKT * sp,char * name)438 static void list_devices(STATUS_PKT *sp, char *name)
439 {
440    int len;
441    DEVRES *device;
442    AUTOCHANGER *changer;
443    POOL_MEM msg(PM_MESSAGE);
444 
445    if (!sp->api) {
446       len = Mmsg(msg, _("\nDevice status:\n"));
447       sendit(msg, len, sp);
448    }
449 
450    foreach_res(changer, R_AUTOCHANGER) {
451       if (!name || strcmp(changer->hdr.name, name) == 0) {
452          list_one_autochanger(changer->hdr.name, changer, sp);
453       }
454    }
455 
456    foreach_res(device, R_DEVICE) {
457       if (!name || strcmp(device->hdr.name, name) == 0) {
458          list_one_device(device->hdr.name, device->dev, sp);
459       }
460    }
461    if (!sp->api) sendit("====\n\n", 6, sp);
462 }
463 
list_cloud_transfers(STATUS_PKT * sp,bool verbose)464 static void list_cloud_transfers(STATUS_PKT *sp, bool verbose)
465 {
466    bool first=true;
467    int len;
468    DEVRES *device;
469    POOL_MEM msg(PM_MESSAGE);
470 
471    foreach_res(device, R_DEVICE) {
472       if (device->dev && device->dev->is_cloud()) {
473 
474          if (first) {
475             if (!sp->api) {
476                len = Mmsg(msg, _("Cloud transfer status:\n"));
477                sendit(msg, len, sp);
478             }
479             first = false;
480          }
481 
482          cloud_dev *cdev = (cloud_dev*)device->dev;
483          len = cdev->get_cloud_upload_transfer_status(msg, verbose);
484          sendit(msg, len, sp);
485          len = cdev->get_cloud_download_transfer_status(msg, verbose);
486          sendit(msg, len, sp);
487          break; /* only once, transfer mgr are shared */
488       }
489    }
490 
491    if (!first && !sp->api) sendit("====\n\n", 6, sp);
492 }
493 
api_list_sd_status_header(STATUS_PKT * sp)494 static void api_list_sd_status_header(STATUS_PKT *sp)
495 {
496    char *p;
497    alist drivers(10, not_owned_by_alist);
498    OutputWriter wt(sp->api_opts);
499 
500    sd_list_loaded_drivers(&drivers);
501    wt.start_group("header");
502    wt.get_output(
503       OT_STRING, "name",        my_name,
504       OT_STRING, "version",     VERSION " (" BDATE ")",
505       OT_STRING, "uname",       HOST_OS " " DISTNAME " " DISTVER,
506       OT_UTIME,  "started",     daemon_start_time,
507       OT_INT64,  "pid",         (int64_t)getpid(),
508       OT_INT,    "jobs_run",    num_jobs_run,
509       OT_INT,    "jobs_running",job_count(),
510       OT_INT,    "ndevices",    ((rblist *)res_head[R_DEVICE-r_first]->res_list)->size(),
511       OT_INT,    "nautochgr",   ((rblist *)res_head[R_AUTOCHANGER-r_first]->res_list)->size(),
512       OT_PLUGINS,"plugins",     b_plugin_list,
513       OT_ALIST_STR, "drivers",  &drivers,
514       OT_END);
515    p = wt.end_group();
516    sendit(p, strlen(p), sp);
517 }
518 
list_status_header(STATUS_PKT * sp)519 static void list_status_header(STATUS_PKT *sp)
520 {
521    char dt[MAX_TIME_LENGTH];
522    char b1[35], b2[35], b3[35], b4[35], b5[35];
523    POOL_MEM msg(PM_MESSAGE);
524    int len;
525 
526    if (sp->api) {
527       api_list_sd_status_header(sp);
528       return;
529    }
530 
531    len = Mmsg(msg, _("%s %sVersion: %s (%s) %s %s %s\n"),
532               my_name, "", VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
533    sendit(msg, len, sp);
534 
535    bstrftime_nc(dt, sizeof(dt), daemon_start_time);
536 
537 
538    len = Mmsg(msg, _("Daemon started %s. Jobs: run=%d, running=%d.\n"),
539         dt, num_jobs_run, job_count());
540    sendit(msg, len, sp);
541    len = Mmsg(msg, _(" Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
542          edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
543          edit_uint64_with_commas(sm_bytes, b2),
544          edit_uint64_with_commas(sm_max_bytes, b3),
545          edit_uint64_with_commas(sm_buffers, b4),
546          edit_uint64_with_commas(sm_max_buffers, b5));
547    sendit(msg, len, sp);
548    len = Mmsg(msg, " Sizes: boffset_t=%d size_t=%d int32_t=%d int64_t=%d "
549               "mode=%d,%d newbsr=%d\n",
550               (int)sizeof(boffset_t), (int)sizeof(size_t), (int)sizeof(int32_t),
551               (int)sizeof(int64_t), (int)DEVELOPER_MODE, 0, use_new_match_all);
552    sendit(msg, len, sp);
553    len = Mmsg(msg, _(" Res: ndevices=%d nautochgr=%d\n"),
554       ((rblist *)res_head[R_DEVICE-r_first]->res_list)->size(),
555       ((rblist *)res_head[R_AUTOCHANGER-r_first]->res_list)->size());
556    sendit(msg, len, sp);
557    list_plugins(sp);
558 }
559 
send_blocked_status(DEVICE * dev,STATUS_PKT * sp)560 static void send_blocked_status(DEVICE *dev, STATUS_PKT *sp)
561 {
562    POOL_MEM msg(PM_MESSAGE);
563    int len;
564 
565    if (!dev) {
566       len = Mmsg(msg, _("No DEVICE structure.\n\n"));
567       sendit(msg, len, sp);
568       return;
569    }
570    if (!dev->enabled) {
571       len = Mmsg(msg, _("   Device is disabled. User command.\n"));
572       sendit(msg, len, sp);
573    }
574    switch (dev->blocked()) {
575    case BST_UNMOUNTED:
576       len = Mmsg(msg, _("   Device is BLOCKED. User unmounted.\n"));
577       sendit(msg, len, sp);
578       break;
579    case BST_UNMOUNTED_WAITING_FOR_SYSOP:
580       len = Mmsg(msg, _("   Device is BLOCKED. User unmounted during wait for media/mount.\n"));
581       sendit(msg, len, sp);
582       break;
583    case BST_WAITING_FOR_SYSOP:
584       {
585          DCR *dcr;
586          bool found_jcr = false;
587          dev->Lock();
588          dev->Lock_dcrs();
589          foreach_dlist(dcr, dev->attached_dcrs) {
590             if (dcr->jcr->JobStatus == JS_WaitMount) {
591                len = Mmsg(msg, _("   Device is BLOCKED waiting for mount of volume \"%s\",\n"
592                                  "       Pool:        %s\n"
593                                  "       Media type:  %s\n"),
594                           dcr->VolumeName,
595                           dcr->pool_name,
596                           dcr->media_type);
597                sendit(msg, len, sp);
598                found_jcr = true;
599             } else if (dcr->jcr->JobStatus == JS_WaitMedia) {
600                len = Mmsg(msg, _("   Device is BLOCKED waiting to create a volume for:\n"
601                                  "       Pool:        %s\n"
602                                  "       Media type:  %s\n"),
603                           dcr->pool_name,
604                           dcr->media_type);
605                sendit(msg, len, sp);
606                found_jcr = true;
607             }
608          }
609          dev->Unlock_dcrs();
610          dev->Unlock();
611          if (!found_jcr) {
612             len = Mmsg(msg, _("   Device is BLOCKED waiting for media.\n"));
613             sendit(msg, len, sp);
614          }
615       }
616       break;
617    case BST_DOING_ACQUIRE:
618       len = Mmsg(msg, _("   Device is being initialized.\n"));
619       sendit(msg, len, sp);
620       break;
621    case BST_WRITING_LABEL:
622       len = Mmsg(msg, _("   Device is blocked labeling a Volume.\n"));
623       sendit(msg, len, sp);
624       break;
625    default:
626       break;
627    }
628    /* Send autochanger slot status */
629    if (dev->is_autochanger()) {
630       if (dev->get_slot() > 0) {
631          len = Mmsg(msg, _("   Slot %d %s loaded in drive %d.\n"),
632             dev->get_slot(), dev->is_open()?"is": "was last", dev->drive_index);
633          sendit(msg, len, sp);
634       } else if (dev->get_slot() <= 0) {
635          len = Mmsg(msg, _("   Drive %d is not loaded.\n"), dev->drive_index);
636          sendit(msg, len, sp);
637       }
638    }
639    if (chk_dbglvl(1)) {
640       send_device_status(dev, sp);
641    }
642 }
643 
send_device_status(DEVICE * dev,STATUS_PKT * sp)644 void send_device_status(DEVICE *dev, STATUS_PKT *sp)
645 {
646    POOL_MEM msg(PM_MESSAGE);
647    int len;
648    DCR *dcr = NULL;
649    bool found = false;
650    char b1[35];
651 
652 
653    if (chk_dbglvl(5)) {
654       len = Mmsg(msg, _("Configured device capabilities:\n"));
655       sendit(msg, len, sp);
656       len = Mmsg(msg, "   %sEOF %sBSR %sBSF %sFSR %sFSF %sEOM %sREM %sRACCESS %sAUTOMOUNT %sLABEL %sANONVOLS %sALWAYSOPEN %sSYNCONCLOSE\n",
657          dev->capabilities & CAP_EOF ? "" : "!",
658          dev->capabilities & CAP_BSR ? "" : "!",
659          dev->capabilities & CAP_BSF ? "" : "!",
660          dev->capabilities & CAP_FSR ? "" : "!",
661          dev->capabilities & CAP_FSF ? "" : "!",
662          dev->capabilities & CAP_EOM ? "" : "!",
663          dev->capabilities & CAP_REM ? "" : "!",
664          dev->capabilities & CAP_RACCESS ? "" : "!",
665          dev->capabilities & CAP_AUTOMOUNT ? "" : "!",
666          dev->capabilities & CAP_LABEL ? "" : "!",
667          dev->capabilities & CAP_ANONVOLS ? "" : "!",
668          dev->capabilities & CAP_ALWAYSOPEN ? "" : "!",
669          dev->capabilities & CAP_SYNCONCLOSE ? "" : "!");
670       sendit(msg, len, sp);
671    }
672 
673    len = Mmsg(msg, _("Device state:\n"));
674    sendit(msg, len, sp);
675    len = Mmsg(msg, "   %sOPENED %sTAPE %sLABEL %sAPPEND %sREAD %sEOT %sWEOT %sEOF %sWORM %sNEXTVOL %sSHORT %sMOUNTED %sMALLOC\n",
676       dev->is_open() ? "" : "!",
677       dev->is_tape() ? "" : "!",
678       dev->is_labeled() ? "" : "!",
679       dev->can_append() ? "" : "!",
680       dev->can_read() ? "" : "!",
681       dev->at_eot() ? "" : "!",
682       dev->state & ST_WEOT ? "" : "!",
683       dev->at_eof() ? "" : "!",
684       dev->is_worm() ?  "" : "!",
685       dev->state & ST_NEXTVOL ?  "" : "!",
686       dev->state & ST_SHORT ?  "" : "!",
687       dev->state & ST_MOUNTED ?  "" : "!",
688       dev->state & ST_MALLOC ?  "" : "!");
689    sendit(msg, len, sp);
690 
691    len = Mmsg(msg, _("   Writers=%d reserves=%d blocked=%d enabled=%d usage=%s\n"), dev->num_writers,
692               dev->num_reserved(), dev->blocked(), dev->enabled,
693                edit_uint64_with_commas(dev->usage, b1));
694 
695    sendit(msg, len, sp);
696 
697    len = Mmsg(msg, _("Attached JobIds: "));
698    sendit(msg, len, sp);
699    dev->Lock();
700    dev->Lock_dcrs();
701    foreach_dlist(dcr, dev->attached_dcrs) {
702       if (dcr->jcr) {
703          if (found) {
704             sendit(",", 1, sp);
705          }
706          len = Mmsg(msg, "%d", (int)dcr->jcr->JobId);
707          sendit(msg, len, sp);
708          found = true;
709       }
710    }
711    dev->Unlock_dcrs();
712    dev->Unlock();
713    sendit("\n", 1, sp);
714 
715    len = Mmsg(msg, _("Device parameters:\n"));
716    sendit(msg, len, sp);
717    len = Mmsg(msg, _("   Archive name: %s Device name: %s\n"), dev->archive_name(),
718       dev->name());
719    sendit(msg, len, sp);
720    len = Mmsg(msg, _("   File=%u block=%u\n"), dev->file, dev->block_num);
721    sendit(msg, len, sp);
722    len = Mmsg(msg, _("   Min block=%u Max block=%u\n"), dev->min_block_size, dev->max_block_size);
723    sendit(msg, len, sp);
724 }
725 
api_list_running_jobs(STATUS_PKT * sp)726 static void api_list_running_jobs(STATUS_PKT *sp)
727 {
728    char *p1, *p2, *p3;
729    int i1, i2, i3;
730    OutputWriter ow(sp->api_opts);
731 
732    uint64_t inst_bps, total_bps;
733    int inst_sec, total_sec;
734    JCR *jcr;
735    DCR *dcr, *rdcr;
736    time_t now = time(NULL);
737 
738    foreach_jcr(jcr) {
739       if (jcr->getJobType() == JT_SYSTEM) {
740          continue;
741       }
742       ow.get_output(OT_CLEAR,
743                     OT_START_OBJ,
744                     OT_INT32,   "jobid",     jcr->JobId,
745                     OT_STRING,  "job",       jcr->Job,
746                     OT_JOBLEVEL,"level",     jcr->getJobLevel(),
747                     OT_JOBTYPE, "type",      jcr->getJobType(),
748                     OT_JOBSTATUS,"status",   jcr->JobStatus,
749                     OT_PINT64,  "jobbytes",  jcr->JobBytes,
750                     OT_INT32,   "jobfiles",  jcr->JobFiles,
751                     OT_UTIME,   "starttime", jcr->start_time,
752                     OT_INT32,   "errors",    jcr->JobErrors,
753                     OT_INT32,   "newbsr",    (int32_t)jcr->use_new_match_all,
754                     OT_END);
755 
756       dcr = jcr->dcr;
757       rdcr = jcr->read_dcr;
758 
759       p1 = p2 = p3 = NULL;
760       if (rdcr && rdcr->device) {
761          p1 = rdcr->VolumeName;
762          p2 = rdcr->pool_name;
763          p3 = rdcr->device->hdr.name;
764       }
765       ow.get_output(OT_STRING,  "read_volume",  NPRTB(p1),
766                     OT_STRING,  "read_pool",    NPRTB(p2),
767                     OT_STRING,  "read_device",  NPRTB(p3),
768                     OT_END);
769 
770       p1 = p2 = p3 = NULL;
771       i1 = i2 = i3 = 0;
772       if (dcr && dcr->device) {
773          p1 = dcr->VolumeName;
774          p2 = dcr->pool_name;
775          p3 = dcr->device->hdr.name;
776          i1 = dcr->spooling;
777          i2 = dcr->despooling;
778          i3 = dcr->despool_wait;
779       }
780 
781       ow.get_output(OT_STRING,  "write_volume",  NPRTB(p1),
782                     OT_STRING,  "write_pool",    NPRTB(p2),
783                     OT_STRING,  "write_device",  NPRTB(p3),
784                     OT_INT,     "spooling",      i1,
785                     OT_INT,     "despooling",    i2,
786                     OT_INT,     "despool_wait",  i3,
787                     OT_END);
788 
789       if (jcr->last_time == 0) {
790          jcr->last_time = jcr->run_time;
791       }
792 
793       total_sec = now - jcr->run_time;
794       inst_sec = now - jcr->last_time;
795 
796       if (total_sec <= 0) {
797          total_sec = 1;
798       }
799       if (inst_sec <= 0) {
800          inst_sec = 1;
801       }
802 
803       /* Instanteous bps not smoothed */
804       inst_bps = (jcr->JobBytes - jcr->LastJobBytes) / inst_sec;
805       if (jcr->LastRate == 0) {
806          jcr->LastRate = inst_bps;
807       }
808 
809       /* Smooth the instantaneous bps a bit */
810       inst_bps = (2 * jcr->LastRate + inst_bps) / 3;
811       /* total bps (AveBytes/sec) since start of job */
812       total_bps = jcr->JobBytes / total_sec;
813 
814       p1 = ow.get_output(OT_PINT64, "avebytes_sec",   total_bps,
815                          OT_PINT64, "lastbytes_sec",  inst_bps,
816                          OT_END_OBJ,
817                          OT_END);
818 
819       sendit(p1, strlen(p1), sp);
820 
821       /* Update only every 10 seconds */
822       if (now - jcr->last_time > 10) {
823          jcr->LastRate = inst_bps;
824          jcr->LastJobBytes = jcr->JobBytes;
825          jcr->last_time = now;
826       }
827    }
828    endeach_jcr(jcr);
829 
830 }
831 
list_running_jobs(STATUS_PKT * sp)832 static void list_running_jobs(STATUS_PKT *sp)
833 {
834    bool found = false;
835    uint64_t inst_bps, total_bps;
836    int inst_sec, total_sec;
837    JCR *jcr;
838    DCR *dcr, *rdcr;
839    char JobName[MAX_NAME_LENGTH];
840    char b1[50], b2[50], b3[50], b4[50];
841    int len;
842    POOL_MEM msg(PM_MESSAGE);
843    time_t now = time(NULL);
844 
845    if (sp->api > 1) {
846       api_list_running_jobs(sp);
847       return;
848    }
849 
850    len = Mmsg(msg, _("\nRunning Jobs:\n"));
851    if (!sp->api) sendit(msg, len, sp);
852 
853    foreach_jcr(jcr) {
854       if (jcr->JobStatus == JS_WaitFD) {
855          len = Mmsg(msg, _("%s Job %s waiting for Client connection.\n"),
856             job_type_to_str(jcr->getJobType()), jcr->Job);
857          sendit(msg, len, sp);
858       }
859       dcr = jcr->dcr;
860       rdcr = jcr->read_dcr;
861       if ((dcr && dcr->device) || (rdcr && rdcr->device)) {
862          bstrncpy(JobName, jcr->Job, sizeof(JobName));
863          /* There are three periods after the Job name */
864          char *p;
865          for (int i=0; i<3; i++) {
866             if ((p=strrchr(JobName, '.')) != NULL) {
867                *p = 0;
868             }
869          }
870          if (rdcr && rdcr->device) {
871             len = Mmsg(msg, _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
872                             "    pool=\"%s\" device=%s newbsr=%d\n"),
873                    job_level_to_str(jcr->getJobLevel()),
874                    job_type_to_str(jcr->getJobType()),
875                    JobName,
876                    jcr->JobId,
877                    rdcr->VolumeName,
878                    rdcr->pool_name,
879                    rdcr->dev?rdcr->dev->print_name():
880                             rdcr->device->device_name,
881                    jcr->use_new_match_all
882                );
883             sendit(msg, len, sp);
884          } else if (dcr && dcr->device) {
885             len = Mmsg(msg, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
886                             "    pool=\"%s\" device=%s\n"),
887                    job_level_to_str(jcr->getJobLevel()),
888                    job_type_to_str(jcr->getJobType()),
889                    JobName,
890                    jcr->JobId,
891                    dcr->VolumeName,
892                    dcr->pool_name,
893                    dcr->dev?dcr->dev->print_name():
894                             dcr->device->device_name);
895             sendit(msg, len, sp);
896             len= Mmsg(msg, _("    spooling=%d despooling=%d despool_wait=%d\n"),
897                    dcr->spooling, dcr->despooling, dcr->despool_wait);
898             sendit(msg, len, sp);
899          }
900          if (jcr->last_time == 0) {
901             jcr->last_time = jcr->run_time;
902          }
903          total_sec = now - jcr->run_time;
904          inst_sec = now - jcr->last_time;
905          if (total_sec <= 0) {
906             total_sec = 1;
907          }
908          if (inst_sec <= 0) {
909             inst_sec = 1;
910          }
911          /* Instanteous bps not smoothed */
912          inst_bps = (jcr->JobBytes - jcr->LastJobBytes) / inst_sec;
913          if (jcr->LastRate == 0) {
914             jcr->LastRate = inst_bps;
915          }
916          /* Smooth the instantaneous bps a bit */
917          inst_bps = (2 * jcr->LastRate + inst_bps) / 3;
918          /* total bps (AveBytes/sec) since start of job */
919          total_bps = jcr->JobBytes / total_sec;
920          len = Mmsg(msg, _("    Files=%s Bytes=%s AveBytes/sec=%s LastBytes/sec=%s\n"),
921             edit_uint64_with_commas(jcr->JobFiles, b1),
922             edit_uint64_with_commas(jcr->JobBytes, b2),
923             edit_uint64_with_commas(total_bps, b3),
924             edit_uint64_with_commas(inst_bps, b4));
925          sendit(msg, len, sp);
926          /* Update only every 10 seconds */
927          if (now - jcr->last_time > 10) {
928             jcr->LastRate = inst_bps;
929             jcr->LastJobBytes = jcr->JobBytes;
930             jcr->last_time = now;
931          }
932          found = true;
933 #ifdef DEBUG
934          if (jcr->file_bsock) {
935             len = Mmsg(msg, _("    FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
936                edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
937                jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
938                jcr->file_bsock->m_fd);
939             sendit(msg, len, sp);
940          } else {
941             len = Mmsg(msg, _("    FDSocket closed\n"));
942             sendit(msg, len, sp);
943          }
944 #endif
945       }
946    }
947    endeach_jcr(jcr);
948 
949    if (!found) {
950       len = Mmsg(msg, _("No Jobs running.\n"));
951       if (!sp->api) sendit(msg, len, sp);
952    }
953    if (!sp->api) sendit("====\n", 5, sp);
954 }
955 
list_jobs_waiting_on_reservation(STATUS_PKT * sp)956 static void list_jobs_waiting_on_reservation(STATUS_PKT *sp)
957 {
958    JCR *jcr;
959    POOL_MEM msg(PM_MESSAGE);
960    int len;
961 
962    len = Mmsg(msg, _("\nJobs waiting to reserve a drive:\n"));
963    if (!sp->api) sendit(msg, len, sp);
964 
965    foreach_jcr(jcr) {
966       if (!jcr->reserve_msgs) {
967          continue;
968       }
969       send_drive_reserve_messages(jcr, sendit, sp);
970    }
971    endeach_jcr(jcr);
972 
973    if (!sp->api) sendit("====\n", 5, sp);
974 }
975 
976 
sendit(const char * msg,int len,void * sp)977 static void sendit(const char *msg, int len, void *sp)
978 {
979    sendit(msg, len, (STATUS_PKT *)sp);
980 }
981 
sendit(POOL_MEM & msg,int len,STATUS_PKT * sp)982 static void sendit(POOL_MEM &msg, int len, STATUS_PKT *sp)
983 {
984    BSOCK *bs = sp->bs;
985    if (bs) {
986       bs->msg = check_pool_memory_size(bs->msg, len+1);
987       memcpy(bs->msg, msg.c_str(), len+1);
988       bs->msglen = len+1;
989       bs->send();
990    } else {
991       sp->callback(msg.c_str(), len, sp->context);
992    }
993 }
994 
dbg_sendit(const char * msg,int len,void * sp)995 static void dbg_sendit(const char *msg, int len, void *sp)
996 {
997    if (len > 0) {
998       Dmsg0(-1, msg);
999    }
1000 }
1001 
1002 /*
1003  * Status command from Director
1004  */
status_cmd(JCR * jcr)1005 bool status_cmd(JCR *jcr)
1006 {
1007    BSOCK *dir = jcr->dir_bsock;
1008    STATUS_PKT sp;
1009 
1010    dir->fsend("\n");
1011    sp.bs = dir;
1012    output_status(&sp);
1013    dir->signal(BNET_EOD);
1014    return true;
1015 }
1016 
1017 /*
1018  * .status command from Director
1019  */
qstatus_cmd(JCR * jcr)1020 bool qstatus_cmd(JCR *jcr)
1021 {
1022    BSOCK *dir = jcr->dir_bsock;
1023    JCR *njcr;
1024    s_last_job* job;
1025    STATUS_PKT sp;
1026    POOLMEM *args = get_pool_memory(PM_MESSAGE);
1027    char *argk[MAX_CMD_ARGS];          /* argument keywords */
1028    char *argv[MAX_CMD_ARGS];          /* argument values */
1029    int argc;                          /* number of arguments */
1030    bool ret=true;
1031    char *cmd;
1032    char *device=NULL;
1033    char *collname=NULL;
1034    int api = true;
1035 
1036    sp.bs = dir;
1037 
1038    parse_args(dir->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
1039 
1040    /* .status xxxx at the minimum */
1041    if (argc < 2 || strcmp(argk[0], ".status") != 0) {
1042       pm_strcpy(jcr->errmsg, dir->msg);
1043       dir->fsend(_("3900 No arg in .status command: %s\n"), jcr->errmsg);
1044       dir->signal(BNET_EOD);
1045       return false;
1046    }
1047 
1048    cmd = argk[1];
1049    unbash_spaces(cmd);
1050 
1051    /* The status command can contain some arguments
1052     * i=0 => .status
1053     * i=1 => [running | current | last | ... ]
1054     */
1055    for (int i=0 ; i < argc ; i++) {
1056       if (!strcmp(argk[i], "device") && argv[i]) {
1057          device = argv[i];
1058          unbash_spaces(device);
1059 
1060       } else if (!strcmp(argk[i], "api") && argv[i]) {
1061          api = atoi(argv[i]);
1062 
1063       } else if (!strcmp(argk[i], "statistics") && argv[i]) {
1064          collname = argv[i];
1065          unbash_spaces(collname);
1066 
1067       } else if (!strcmp(argk[i], "api_opts") && argv[i]) {
1068          bstrncpy(sp.api_opts, argv[i], sizeof(sp.api_opts));
1069       }
1070    }
1071 
1072    Dmsg1(100, "cmd=%s\n", cmd);
1073 
1074    if (strcasecmp(cmd, "current") == 0) {
1075       dir->fsend(OKqstatus, cmd);
1076       foreach_jcr(njcr) {
1077          if (njcr->JobId != 0) {
1078             dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
1079          }
1080       }
1081       endeach_jcr(njcr);
1082    } else if (strcasecmp(cmd, "last") == 0) {
1083       dir->fsend(OKqstatus, cmd);
1084       if ((last_jobs) && (last_jobs->size() > 0)) {
1085          job = (s_last_job*)last_jobs->last();
1086          dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
1087       }
1088    } else if (strcasecmp(cmd, "header") == 0) {
1089        sp.api = api;
1090        list_status_header(&sp);
1091    } else if (strcasecmp(cmd, "running") == 0) {
1092        sp.api = api;
1093        list_running_jobs(&sp);
1094    } else if (strcasecmp(cmd, "waitreservation") == 0) {
1095        sp.api = api;
1096        list_jobs_waiting_on_reservation(&sp);
1097    } else if (strcasecmp(cmd, "devices") == 0) {
1098        sp.api = api;
1099        list_devices(&sp, device);
1100    } else if (strcasecmp(cmd, "volumes") == 0) {
1101        sp.api = api;
1102        list_volumes(sendit, &sp);
1103    } else if (strcasecmp(cmd, "spooling") == 0) {
1104        sp.api = api;
1105        list_spool_stats(sendit, &sp);
1106    } else if (strcasecmp(cmd, "terminated") == 0) {
1107        sp.api = api;
1108        list_terminated_jobs(&sp); /* defined in lib/status.h */
1109    } else if (strcasecmp(cmd, "resources") == 0) {
1110        sp.api = api;
1111        list_resources(&sp);
1112    } else if (strcasecmp(cmd, "cloud") == 0) {
1113       list_cloud_transfers(&sp, true);
1114    } else if (strcasecmp(cmd, "statistics") == 0) {
1115       sp.api = api;
1116       list_collectors_status(&sp, collname);
1117    } else {
1118       pm_strcpy(jcr->errmsg, dir->msg);
1119       dir->fsend(_("3900 Unknown arg in .status command: %s\n"), jcr->errmsg);
1120       dir->signal(BNET_EOD);
1121       ret = false;
1122    }
1123    dir->signal(BNET_EOD);
1124    free_pool_memory(args);
1125    return ret;
1126 }
1127 
1128 /* List plugins and drivers */
list_plugins(STATUS_PKT * sp)1129 static void list_plugins(STATUS_PKT *sp)
1130 {
1131    POOL_MEM msg(PM_MESSAGE);
1132    alist drivers(10, not_owned_by_alist);
1133    int len;
1134 
1135    if (b_plugin_list && b_plugin_list->size() > 0) {
1136       Plugin *plugin;
1137       pm_strcpy(msg, " Plugin: ");
1138       foreach_alist(plugin, b_plugin_list) {
1139          len = pm_strcat(msg, plugin->file);
1140          /* Print plugin version when debug activated */
1141          if (debug_level > 0 && plugin->pinfo) {
1142             pm_strcat(msg, "(");
1143             pm_strcat(msg, NPRT(sdplug_info(plugin)->plugin_version));
1144             len = pm_strcat(msg, ")");
1145          }
1146          if (len > 80) {
1147             pm_strcat(msg, "\n   ");
1148          } else {
1149             pm_strcat(msg, " ");
1150          }
1151       }
1152       len = pm_strcat(msg, "\n");
1153       sendit(msg.c_str(), len, sp);
1154    }
1155    sd_list_loaded_drivers(&drivers);
1156    if (drivers.size() > 0) {
1157       char *drv;
1158       pm_strcpy(msg, " Drivers: ");
1159       foreach_alist(drv, (&drivers)) {
1160          len = pm_strcat(msg, drv);
1161          if (len > 80) {
1162             pm_strcat(msg, "\n   ");
1163          } else {
1164             pm_strcat(msg, " ");
1165          }
1166       }
1167       len = pm_strcat(msg, "\n");
1168       sendit(msg.c_str(), len, sp);
1169    }
1170 }
1171 
list_collectors_status(STATUS_PKT * sp,char * collname)1172 static void list_collectors_status(STATUS_PKT *sp, char *collname)
1173 {
1174    URES *res;
1175    int len;
1176    POOL_MEM buf(PM_MESSAGE);
1177 
1178    Dmsg2(200, "enter list_collectors_status() api=%i coll=%s\n", sp->api, NPRTB(collname));
1179    if (sp->api > 1) {
1180       api_collectors_status(sp, collname);
1181       return;
1182    }
1183 
1184    LockRes();
1185    foreach_res(res, R_COLLECTOR) {
1186       if (collname && !bstrcmp(collname, res->res_collector.hdr.name)){
1187          continue;
1188       }
1189       Dmsg1(500, "processing: %s\n", res->res_collector.hdr.name);
1190       len = render_collector_status(res->res_collector, buf);
1191       sendit(buf.c_str(), len, sp);
1192    };
1193    UnlockRes();
1194    if (!collname){
1195       len = render_updcollector_status(buf);
1196       sendit(buf.c_str(), len, sp);
1197    }
1198    Dmsg0(200, "leave list_collectors_status()\n");
1199 }
1200 
api_collectors_status(STATUS_PKT * sp,char * collname)1201 static void api_collectors_status(STATUS_PKT *sp, char *collname)
1202 {
1203    URES *res;
1204    OutputWriter ow(sp->api_opts);
1205    POOLMEM *buf;
1206 
1207    Dmsg1(200, "enter api_collectors_status() %s\n", NPRTB(collname));
1208    ow.start_group("collector_backends");
1209    LockRes();
1210    foreach_res(res, R_COLLECTOR) {
1211       if (collname && !bstrcmp(collname, res->res_collector.hdr.name)){
1212          continue;
1213       }
1214       Dmsg1(500, "processing: %s\n", res->res_collector.hdr.name);
1215       api_render_collector_status(res->res_collector, ow);
1216    };
1217    UnlockRes();
1218    buf = ow.end_group();
1219    if (!collname){
1220       ow.start_group("collector_update");
1221       api_render_updcollector_status(ow);
1222       buf = ow.end_group();
1223    }
1224    sendit(buf, strlen(buf), sp);
1225    Dmsg0(200, "leave api_collectors_status()\n");
1226 };
1227