]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/status.c
Apply patches from bugs #2325 and #2326 to fix FIFO bugs
[bacula/bacula] / bacula / src / stored / 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  *  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
57 void status_alert_callback(void *ctx, const char *short_msg,
58    const char *long_msg, char *Volume, int severity,
59    int flags, int alertno, utime_t alert_time)
60 {
61    STATUS_PKT *sp = (STATUS_PKT *)ctx;
62    const char *type = "Unknown";
63    POOL_MEM send_msg(PM_MESSAGE);
64    char edt[50];
65    int len;
66
67    switch (severity) {
68    case 'C':
69       type = "Critical";
70       break;
71    case 'W':
72       type = "Warning";
73       break;
74    case 'I':
75       type = "Info";
76       break;
77    }
78    bstrftimes(edt, sizeof(edt), alert_time);
79    if (chk_dbglvl(10)) {
80       len = Mmsg(send_msg, _("    %s Alert: at %s Volume=\"%s\" flags=0x%x alert=%s\n"),
81          type, edt, Volume, flags, long_msg);
82    } else {
83       len = Mmsg(send_msg, _("    %s Alert: at %s Volume=\"%s\" alert=%s\n"),
84          type, edt, Volume, short_msg);
85    }
86    sendit(send_msg, len, sp);
87 }
88
89
90 /*
91  * Status command from Director
92  */
93 void output_status(STATUS_PKT *sp)
94 {
95    POOL_MEM msg(PM_MESSAGE);
96    int len;
97
98    list_status_header(sp);
99
100    /*
101     * List running jobs
102     */
103    list_running_jobs(sp);
104
105    /*
106     * List jobs stuck in reservation system
107     */
108    list_jobs_waiting_on_reservation(sp);
109
110    /*
111     * List terminated jobs (defined in lib/status.h)
112     */
113    list_terminated_jobs(sp);
114
115    /*
116     * List devices
117     */
118    list_devices(sp);
119
120    /*
121     * List cloud transfers
122     */
123    list_cloud_transfers(sp, false);
124
125
126    len = Mmsg(msg, _("Used Volume status:\n"));
127    if (!sp->api) sendit(msg, len, sp);
128
129    list_volumes(sendit, (void *)sp);
130    if (!sp->api) sendit("====\n\n", 6, sp);
131
132
133    list_spool_stats(sendit, (void *)sp);
134
135    if (!sp->api) sendit("====\n\n", 6, sp);
136
137    if (chk_dbglvl(10)) {
138       dbg_print_plugin(stdout);
139    }
140 }
141
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
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
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
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
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
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
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
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
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_INT,    "jobs_run",    num_jobs_run,
508       OT_INT,    "jobs_running",job_count(),
509       OT_INT,    "ndevices",    ((rblist *)res_head[R_DEVICE-r_first]->res_list)->size(),
510       OT_INT,    "nautochgr",   ((rblist *)res_head[R_AUTOCHANGER-r_first]->res_list)->size(),
511       OT_PLUGINS,"plugins",     b_plugin_list,
512       OT_ALIST_STR, "drivers",  &drivers,
513       OT_END);
514    p = wt.end_group();
515    sendit(p, strlen(p), sp);
516 }
517
518 static void list_status_header(STATUS_PKT *sp)
519 {
520    char dt[MAX_TIME_LENGTH];
521    char b1[35], b2[35], b3[35], b4[35], b5[35];
522    POOL_MEM msg(PM_MESSAGE);
523    int len;
524
525    if (sp->api) {
526       api_list_sd_status_header(sp);
527       return;
528    }
529
530    len = Mmsg(msg, _("%s %sVersion: %s (%s) %s %s %s\n"),
531               my_name, "", VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
532    sendit(msg, len, sp);
533
534    bstrftime_nc(dt, sizeof(dt), daemon_start_time);
535
536
537    len = Mmsg(msg, _("Daemon started %s. Jobs: run=%d, running=%d.\n"),
538         dt, num_jobs_run, job_count());
539    sendit(msg, len, sp);
540    len = Mmsg(msg, _(" Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
541          edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
542          edit_uint64_with_commas(sm_bytes, b2),
543          edit_uint64_with_commas(sm_max_bytes, b3),
544          edit_uint64_with_commas(sm_buffers, b4),
545          edit_uint64_with_commas(sm_max_buffers, b5));
546    sendit(msg, len, sp);
547    len = Mmsg(msg, " Sizes: boffset_t=%d size_t=%d int32_t=%d int64_t=%d "
548               "mode=%d,%d newbsr=%d\n",
549               (int)sizeof(boffset_t), (int)sizeof(size_t), (int)sizeof(int32_t),
550               (int)sizeof(int64_t), (int)DEVELOPER_MODE, 0, use_new_match_all);
551    sendit(msg, len, sp);
552    len = Mmsg(msg, _(" Res: ndevices=%d nautochgr=%d\n"),
553       ((rblist *)res_head[R_DEVICE-r_first]->res_list)->size(),
554       ((rblist *)res_head[R_AUTOCHANGER-r_first]->res_list)->size());
555    sendit(msg, len, sp);
556    list_plugins(sp);
557 }
558
559 static void send_blocked_status(DEVICE *dev, STATUS_PKT *sp)
560 {
561    POOL_MEM msg(PM_MESSAGE);
562    int len;
563
564    if (!dev) {
565       len = Mmsg(msg, _("No DEVICE structure.\n\n"));
566       sendit(msg, len, sp);
567       return;
568    }
569    if (!dev->enabled) {
570       len = Mmsg(msg, _("   Device is disabled. User command.\n"));
571       sendit(msg, len, sp);
572    }
573    switch (dev->blocked()) {
574    case BST_UNMOUNTED:
575       len = Mmsg(msg, _("   Device is BLOCKED. User unmounted.\n"));
576       sendit(msg, len, sp);
577       break;
578    case BST_UNMOUNTED_WAITING_FOR_SYSOP:
579       len = Mmsg(msg, _("   Device is BLOCKED. User unmounted during wait for media/mount.\n"));
580       sendit(msg, len, sp);
581       break;
582    case BST_WAITING_FOR_SYSOP:
583       {
584          DCR *dcr;
585          bool found_jcr = false;
586          dev->Lock();
587          dev->Lock_dcrs();
588          foreach_dlist(dcr, dev->attached_dcrs) {
589             if (dcr->jcr->JobStatus == JS_WaitMount) {
590                len = Mmsg(msg, _("   Device is BLOCKED waiting for mount of volume \"%s\",\n"
591                                  "       Pool:        %s\n"
592                                  "       Media type:  %s\n"),
593                           dcr->VolumeName,
594                           dcr->pool_name,
595                           dcr->media_type);
596                sendit(msg, len, sp);
597                found_jcr = true;
598             } else if (dcr->jcr->JobStatus == JS_WaitMedia) {
599                len = Mmsg(msg, _("   Device is BLOCKED waiting to create a volume for:\n"
600                                  "       Pool:        %s\n"
601                                  "       Media type:  %s\n"),
602                           dcr->pool_name,
603                           dcr->media_type);
604                sendit(msg, len, sp);
605                found_jcr = true;
606             }
607          }
608          dev->Unlock_dcrs();
609          dev->Unlock();
610          if (!found_jcr) {
611             len = Mmsg(msg, _("   Device is BLOCKED waiting for media.\n"));
612             sendit(msg, len, sp);
613          }
614       }
615       break;
616    case BST_DOING_ACQUIRE:
617       len = Mmsg(msg, _("   Device is being initialized.\n"));
618       sendit(msg, len, sp);
619       break;
620    case BST_WRITING_LABEL:
621       len = Mmsg(msg, _("   Device is blocked labeling a Volume.\n"));
622       sendit(msg, len, sp);
623       break;
624    default:
625       break;
626    }
627    /* Send autochanger slot status */
628    if (dev->is_autochanger()) {
629       if (dev->get_slot() > 0) {
630          len = Mmsg(msg, _("   Slot %d %s loaded in drive %d.\n"),
631             dev->get_slot(), dev->is_open()?"is": "was last", dev->drive_index);
632          sendit(msg, len, sp);
633       } else if (dev->get_slot() <= 0) {
634          len = Mmsg(msg, _("   Drive %d is not loaded.\n"), dev->drive_index);
635          sendit(msg, len, sp);
636       }
637    }
638    if (chk_dbglvl(1)) {
639       send_device_status(dev, sp);
640    }
641 }
642
643 void send_device_status(DEVICE *dev, STATUS_PKT *sp)
644 {
645    POOL_MEM msg(PM_MESSAGE);
646    int len;
647    DCR *dcr = NULL;
648    bool found = false;
649    char b1[35];
650    
651
652    if (chk_dbglvl(5)) {
653       len = Mmsg(msg, _("Configured device capabilities:\n"));
654       sendit(msg, len, sp);
655       len = Mmsg(msg, "   %sEOF %sBSR %sBSF %sFSR %sFSF %sEOM %sREM %sRACCESS %sAUTOMOUNT %sLABEL %sANONVOLS %sALWAYSOPEN\n",
656          dev->capabilities & CAP_EOF ? "" : "!",
657          dev->capabilities & CAP_BSR ? "" : "!",
658          dev->capabilities & CAP_BSF ? "" : "!",
659          dev->capabilities & CAP_FSR ? "" : "!",
660          dev->capabilities & CAP_FSF ? "" : "!",
661          dev->capabilities & CAP_EOM ? "" : "!",
662          dev->capabilities & CAP_REM ? "" : "!",
663          dev->capabilities & CAP_RACCESS ? "" : "!",
664          dev->capabilities & CAP_AUTOMOUNT ? "" : "!",
665          dev->capabilities & CAP_LABEL ? "" : "!",
666          dev->capabilities & CAP_ANONVOLS ? "" : "!",
667          dev->capabilities & CAP_ALWAYSOPEN ? "" : "!");
668       sendit(msg, len, sp);
669    }
670
671    len = Mmsg(msg, _("Device state:\n"));
672    sendit(msg, len, sp);
673    len = Mmsg(msg, "   %sOPENED %sTAPE %sLABEL %sMALLOC %sAPPEND %sREAD %sEOT %sWEOT %sEOF %sNEXTVOL %sSHORT %sMOUNTED\n",
674       dev->is_open() ? "" : "!",
675       dev->is_tape() ? "" : "!",
676       dev->is_labeled() ? "" : "!",
677       dev->state & ST_MALLOC ? "" : "!",
678       dev->can_append() ? "" : "!",
679       dev->can_read() ? "" : "!",
680       dev->at_eot() ? "" : "!",
681       dev->state & ST_WEOT ? "" : "!",
682       dev->at_eof() ? "" : "!",
683       dev->state & ST_NEXTVOL ? "" : "!",
684       dev->state & ST_SHORT ? "" : "!",
685       dev->state & ST_MOUNTED ? "" : "!");
686    sendit(msg, len, sp);
687    len = Mmsg(msg, _("   Writers=%d reserves=%d blocked=%d enabled=%d usage=%s\n"), dev->num_writers,
688               dev->num_reserved(), dev->blocked(), dev->enabled,
689                edit_uint64_with_commas(dev->usage, b1));
690
691    sendit(msg, len, sp);
692
693    len = Mmsg(msg, _("Attached JobIds: "));
694    sendit(msg, len, sp);
695    dev->Lock();
696    dev->Lock_dcrs();
697    foreach_dlist(dcr, dev->attached_dcrs) {
698       if (dcr->jcr) {
699          if (found) {
700             sendit(",", 1, sp);
701          }
702          len = Mmsg(msg, "%d", (int)dcr->jcr->JobId);
703          sendit(msg, len, sp);
704          found = true;
705       }
706    }
707    dev->Unlock_dcrs();
708    dev->Unlock();
709    sendit("\n", 1, sp);
710
711    len = Mmsg(msg, _("Device parameters:\n"));
712    sendit(msg, len, sp);
713    len = Mmsg(msg, _("   Archive name: %s Device name: %s\n"), dev->archive_name(),
714       dev->name());
715    sendit(msg, len, sp);
716    len = Mmsg(msg, _("   File=%u block=%u\n"), dev->file, dev->block_num);
717    sendit(msg, len, sp);
718    len = Mmsg(msg, _("   Min block=%u Max block=%u\n"), dev->min_block_size, dev->max_block_size);
719    sendit(msg, len, sp);
720 }
721
722 static void api_list_running_jobs(STATUS_PKT *sp)
723 {
724    char *p1, *p2, *p3;
725    int i1, i2, i3;
726    OutputWriter ow(sp->api_opts);
727
728    uint64_t inst_bps, total_bps;
729    int inst_sec, total_sec;
730    JCR *jcr;
731    DCR *dcr, *rdcr;
732    time_t now = time(NULL);
733
734    foreach_jcr(jcr) {
735       if (jcr->getJobType() == JT_SYSTEM) {
736          continue;
737       }
738       ow.get_output(OT_CLEAR,
739                     OT_START_OBJ,
740                     OT_INT32,   "jobid",     jcr->JobId,
741                     OT_STRING,  "job",       jcr->Job,
742                     OT_JOBLEVEL,"level",     jcr->getJobLevel(),
743                     OT_JOBTYPE, "type",      jcr->getJobType(),
744                     OT_JOBSTATUS,"status",   jcr->JobStatus,
745                     OT_PINT64,  "jobbytes",  jcr->JobBytes,
746                     OT_INT32,   "jobfiles",  jcr->JobFiles,
747                     OT_UTIME,   "starttime", jcr->start_time,
748                     OT_INT32,   "errors",    jcr->JobErrors,
749                     OT_INT32,   "newbsr",    (int32_t)jcr->use_new_match_all,
750                     OT_END);
751
752       dcr = jcr->dcr;
753       rdcr = jcr->read_dcr;
754
755       p1 = p2 = p3 = NULL;
756       if (rdcr && rdcr->device) {
757          p1 = rdcr->VolumeName;
758          p2 = rdcr->pool_name;
759          p3 = rdcr->device->hdr.name;
760       }
761       ow.get_output(OT_STRING,  "read_volume",  NPRTB(p1),
762                     OT_STRING,  "read_pool",    NPRTB(p2),
763                     OT_STRING,  "read_device",  NPRTB(p3),
764                     OT_END);
765
766       p1 = p2 = p3 = NULL;
767       i1 = i2 = i3 = 0;
768       if (dcr && dcr->device) {
769          p1 = dcr->VolumeName;
770          p2 = dcr->pool_name;
771          p3 = dcr->device->hdr.name;
772          i1 = dcr->spooling;
773          i2 = dcr->despooling;
774          i3 = dcr->despool_wait;
775       }
776
777       ow.get_output(OT_STRING,  "write_volume",  NPRTB(p1),
778                     OT_STRING,  "write_pool",    NPRTB(p2),
779                     OT_STRING,  "write_device",  NPRTB(p3),
780                     OT_INT,     "spooling",      i1,
781                     OT_INT,     "despooling",    i2,
782                     OT_INT,     "despool_wait",  i3,
783                     OT_END);
784
785       if (jcr->last_time == 0) {
786          jcr->last_time = jcr->run_time;
787       }
788
789       total_sec = now - jcr->run_time;
790       inst_sec = now - jcr->last_time;
791
792       if (total_sec <= 0) {
793          total_sec = 1;
794       }
795       if (inst_sec <= 0) {
796          inst_sec = 1;
797       }
798
799       /* Instanteous bps not smoothed */
800       inst_bps = (jcr->JobBytes - jcr->LastJobBytes) / inst_sec;
801       if (jcr->LastRate == 0) {
802          jcr->LastRate = inst_bps;
803       }
804
805       /* Smooth the instantaneous bps a bit */
806       inst_bps = (2 * jcr->LastRate + inst_bps) / 3;
807       /* total bps (AveBytes/sec) since start of job */
808       total_bps = jcr->JobBytes / total_sec;
809
810       p1 = ow.get_output(OT_PINT64, "avebytes_sec",   total_bps,
811                          OT_PINT64, "lastbytes_sec",  inst_bps,
812                          OT_END_OBJ,
813                          OT_END);
814
815       sendit(p1, strlen(p1), sp);
816
817       /* Update only every 10 seconds */
818       if (now - jcr->last_time > 10) {
819          jcr->LastRate = inst_bps;
820          jcr->LastJobBytes = jcr->JobBytes;
821          jcr->last_time = now;
822       }
823    }
824    endeach_jcr(jcr);
825
826 }
827
828 static void list_running_jobs(STATUS_PKT *sp)
829 {
830    bool found = false;
831    uint64_t inst_bps, total_bps;
832    int inst_sec, total_sec;
833    JCR *jcr;
834    DCR *dcr, *rdcr;
835    char JobName[MAX_NAME_LENGTH];
836    char b1[50], b2[50], b3[50], b4[50];
837    int len;
838    POOL_MEM msg(PM_MESSAGE);
839    time_t now = time(NULL);
840
841    if (sp->api > 1) {
842       api_list_running_jobs(sp);
843       return;
844    }
845
846    len = Mmsg(msg, _("\nRunning Jobs:\n"));
847    if (!sp->api) sendit(msg, len, sp);
848
849    foreach_jcr(jcr) {
850       if (jcr->JobStatus == JS_WaitFD) {
851          len = Mmsg(msg, _("%s Job %s waiting for Client connection.\n"),
852             job_type_to_str(jcr->getJobType()), jcr->Job);
853          sendit(msg, len, sp);
854       }
855       dcr = jcr->dcr;
856       rdcr = jcr->read_dcr;
857       if ((dcr && dcr->device) || (rdcr && rdcr->device)) {
858          bstrncpy(JobName, jcr->Job, sizeof(JobName));
859          /* There are three periods after the Job name */
860          char *p;
861          for (int i=0; i<3; i++) {
862             if ((p=strrchr(JobName, '.')) != NULL) {
863                *p = 0;
864             }
865          }
866          if (rdcr && rdcr->device) {
867             len = Mmsg(msg, _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
868                             "    pool=\"%s\" device=%s newbsr=%d\n"),
869                    job_level_to_str(jcr->getJobLevel()),
870                    job_type_to_str(jcr->getJobType()),
871                    JobName,
872                    jcr->JobId,
873                    rdcr->VolumeName,
874                    rdcr->pool_name,
875                    rdcr->dev?rdcr->dev->print_name():
876                             rdcr->device->device_name,
877                    jcr->use_new_match_all
878                );
879             sendit(msg, len, sp);
880          } else if (dcr && dcr->device) {
881             len = Mmsg(msg, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
882                             "    pool=\"%s\" device=%s\n"),
883                    job_level_to_str(jcr->getJobLevel()),
884                    job_type_to_str(jcr->getJobType()),
885                    JobName,
886                    jcr->JobId,
887                    dcr->VolumeName,
888                    dcr->pool_name,
889                    dcr->dev?dcr->dev->print_name():
890                             dcr->device->device_name);
891             sendit(msg, len, sp);
892             len= Mmsg(msg, _("    spooling=%d despooling=%d despool_wait=%d\n"),
893                    dcr->spooling, dcr->despooling, dcr->despool_wait);
894             sendit(msg, len, sp);
895          }
896          if (jcr->last_time == 0) {
897             jcr->last_time = jcr->run_time;
898          }
899          total_sec = now - jcr->run_time;
900          inst_sec = now - jcr->last_time;
901          if (total_sec <= 0) {
902             total_sec = 1;
903          }
904          if (inst_sec <= 0) {
905             inst_sec = 1;
906          }
907          /* Instanteous bps not smoothed */
908          inst_bps = (jcr->JobBytes - jcr->LastJobBytes) / inst_sec;
909          if (jcr->LastRate == 0) {
910             jcr->LastRate = inst_bps;
911          }
912          /* Smooth the instantaneous bps a bit */
913          inst_bps = (2 * jcr->LastRate + inst_bps) / 3;
914          /* total bps (AveBytes/sec) since start of job */
915          total_bps = jcr->JobBytes / total_sec;
916          len = Mmsg(msg, _("    Files=%s Bytes=%s AveBytes/sec=%s LastBytes/sec=%s\n"),
917             edit_uint64_with_commas(jcr->JobFiles, b1),
918             edit_uint64_with_commas(jcr->JobBytes, b2),
919             edit_uint64_with_commas(total_bps, b3),
920             edit_uint64_with_commas(inst_bps, b4));
921          sendit(msg, len, sp);
922          /* Update only every 10 seconds */
923          if (now - jcr->last_time > 10) {
924             jcr->LastRate = inst_bps;
925             jcr->LastJobBytes = jcr->JobBytes;
926             jcr->last_time = now;
927          }
928          found = true;
929 #ifdef DEBUG
930          if (jcr->file_bsock) {
931             len = Mmsg(msg, _("    FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
932                edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
933                jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
934                jcr->file_bsock->m_fd);
935             sendit(msg, len, sp);
936          } else {
937             len = Mmsg(msg, _("    FDSocket closed\n"));
938             sendit(msg, len, sp);
939          }
940 #endif
941       }
942    }
943    endeach_jcr(jcr);
944
945    if (!found) {
946       len = Mmsg(msg, _("No Jobs running.\n"));
947       if (!sp->api) sendit(msg, len, sp);
948    }
949    if (!sp->api) sendit("====\n", 5, sp);
950 }
951
952 static void list_jobs_waiting_on_reservation(STATUS_PKT *sp)
953 {
954    JCR *jcr;
955    POOL_MEM msg(PM_MESSAGE);
956    int len;
957
958    len = Mmsg(msg, _("\nJobs waiting to reserve a drive:\n"));
959    if (!sp->api) sendit(msg, len, sp);
960
961    foreach_jcr(jcr) {
962       if (!jcr->reserve_msgs) {
963          continue;
964       }
965       send_drive_reserve_messages(jcr, sendit, sp);
966    }
967    endeach_jcr(jcr);
968
969    if (!sp->api) sendit("====\n", 5, sp);
970 }
971
972
973 static void sendit(const char *msg, int len, void *sp)
974 {
975    sendit(msg, len, (STATUS_PKT *)sp);
976 }
977
978 static void sendit(POOL_MEM &msg, int len, STATUS_PKT *sp)
979 {
980    BSOCK *bs = sp->bs;
981    if (bs) {
982       bs->msg = check_pool_memory_size(bs->msg, len+1);
983       memcpy(bs->msg, msg.c_str(), len+1);
984       bs->msglen = len+1;
985       bs->send();
986    } else {
987       sp->callback(msg.c_str(), len, sp->context);
988    }
989 }
990
991 static void dbg_sendit(const char *msg, int len, void *sp)
992 {
993    if (len > 0) {
994       Dmsg0(-1, msg);
995    }
996 }
997
998 /*
999  * Status command from Director
1000  */
1001 bool status_cmd(JCR *jcr)
1002 {
1003    BSOCK *dir = jcr->dir_bsock;
1004    STATUS_PKT sp;
1005
1006    dir->fsend("\n");
1007    sp.bs = dir;
1008    output_status(&sp);
1009    dir->signal(BNET_EOD);
1010    return true;
1011 }
1012
1013 /*
1014  * .status command from Director
1015  */
1016 bool qstatus_cmd(JCR *jcr)
1017 {
1018    BSOCK *dir = jcr->dir_bsock;
1019    JCR *njcr;
1020    s_last_job* job;
1021    STATUS_PKT sp;
1022    POOLMEM *args = get_pool_memory(PM_MESSAGE);
1023    char *argk[MAX_CMD_ARGS];          /* argument keywords */
1024    char *argv[MAX_CMD_ARGS];          /* argument values */
1025    int argc;                          /* number of arguments */
1026    bool ret=true;
1027    char *cmd;
1028    char *device=NULL;
1029    int api = true;
1030
1031    sp.bs = dir;
1032
1033    parse_args(dir->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
1034
1035    /* .status xxxx at the minimum */
1036    if (argc < 2 || strcmp(argk[0], ".status") != 0) {
1037       pm_strcpy(jcr->errmsg, dir->msg);
1038       dir->fsend(_("3900 No arg in .status command: %s\n"), jcr->errmsg);
1039       dir->signal(BNET_EOD);
1040       return false;
1041    }
1042
1043    cmd = argk[1];
1044    unbash_spaces(cmd);
1045
1046    /* The status command can contain some arguments
1047     * i=0 => .status
1048     * i=1 => [running | current | last | ... ]
1049     */
1050    for (int i=0 ; i < argc ; i++) {
1051       if (!strcmp(argk[i], "device") && argv[i]) {
1052          device = argv[i];
1053          unbash_spaces(device);
1054
1055       } else if (!strcmp(argk[i], "api") && argv[i]) {
1056          api = atoi(argv[i]);
1057
1058       } else if (!strcmp(argk[i], "api_opts") && argv[i]) {
1059          strncpy(sp.api_opts, argv[i], sizeof(sp.api_opts));;
1060       }
1061    }
1062
1063    Dmsg1(100, "cmd=%s\n", cmd);
1064
1065    if (strcasecmp(cmd, "current") == 0) {
1066       dir->fsend(OKqstatus, cmd);
1067       foreach_jcr(njcr) {
1068          if (njcr->JobId != 0) {
1069             dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
1070          }
1071       }
1072       endeach_jcr(njcr);
1073    } else if (strcasecmp(cmd, "last") == 0) {
1074       dir->fsend(OKqstatus, cmd);
1075       if ((last_jobs) && (last_jobs->size() > 0)) {
1076          job = (s_last_job*)last_jobs->last();
1077          dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
1078       }
1079    } else if (strcasecmp(cmd, "header") == 0) {
1080        sp.api = api;
1081        list_status_header(&sp);
1082    } else if (strcasecmp(cmd, "running") == 0) {
1083        sp.api = api;
1084        list_running_jobs(&sp);
1085    } else if (strcasecmp(cmd, "waitreservation") == 0) {
1086        sp.api = api;
1087        list_jobs_waiting_on_reservation(&sp);
1088    } else if (strcasecmp(cmd, "devices") == 0) {
1089        sp.api = api;
1090        list_devices(&sp, device);
1091    } else if (strcasecmp(cmd, "volumes") == 0) {
1092        sp.api = api;
1093        list_volumes(sendit, &sp);
1094    } else if (strcasecmp(cmd, "spooling") == 0) {
1095        sp.api = api;
1096        list_spool_stats(sendit, &sp);
1097    } else if (strcasecmp(cmd, "terminated") == 0) {
1098        sp.api = api;
1099        list_terminated_jobs(&sp); /* defined in lib/status.h */
1100    } else if (strcasecmp(cmd, "resources") == 0) {
1101        sp.api = api;
1102        list_resources(&sp);
1103    } else if (strcasecmp(cmd, "cloud") == 0) {
1104       list_cloud_transfers(&sp, true);
1105    } else {
1106       pm_strcpy(jcr->errmsg, dir->msg);
1107       dir->fsend(_("3900 Unknown arg in .status command: %s\n"), jcr->errmsg);
1108       dir->signal(BNET_EOD);
1109       ret = false;
1110    }
1111    dir->signal(BNET_EOD);
1112    free_pool_memory(args);
1113    return ret;
1114 }
1115
1116 /* List plugins and drivers */
1117 static void list_plugins(STATUS_PKT *sp)
1118 {
1119    POOL_MEM msg(PM_MESSAGE);
1120    alist drivers(10, not_owned_by_alist);
1121    int len;
1122
1123    if (b_plugin_list && b_plugin_list->size() > 0) {
1124       Plugin *plugin;
1125       pm_strcpy(msg, " Plugin: ");
1126       foreach_alist(plugin, b_plugin_list) {
1127          len = pm_strcat(msg, plugin->file);
1128          /* Print plugin version when debug activated */
1129          if (debug_level > 0 && plugin->pinfo) {
1130             pm_strcat(msg, "(");
1131             pm_strcat(msg, NPRT(sdplug_info(plugin)->plugin_version));
1132             len = pm_strcat(msg, ")");
1133          }
1134          if (len > 80) {
1135             pm_strcat(msg, "\n   ");
1136          } else {
1137             pm_strcat(msg, " ");
1138          }
1139       }
1140       len = pm_strcat(msg, "\n");
1141       sendit(msg.c_str(), len, sp);
1142    }
1143    sd_list_loaded_drivers(&drivers);
1144    if (drivers.size() > 0) {
1145       char *drv;
1146       pm_strcpy(msg, " Drivers: ");
1147       foreach_alist(drv, (&drivers)) {
1148          len = pm_strcat(msg, drv);
1149          if (len > 80) {
1150             pm_strcat(msg, "\n   ");
1151          } else {
1152             pm_strcat(msg, " ");
1153          }
1154       }
1155       len = pm_strcat(msg, "\n");
1156       sendit(msg.c_str(), len, sp);
1157    }
1158 }