2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2017 Kern Sibbald
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.
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.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
20 * This file handles the status command
22 * Kern Sibbald, May MMIII
29 #include "lib/status.h"
30 #include "sd_plugins.h"
32 /* Imported functions */
33 extern void dbg_print_plugin(FILE *fp);
35 /* Imported variables */
36 extern BSOCK *filed_chan;
37 extern void *start_heap;
39 /* Static variables */
40 static char OKqstatus[] = "3000 OK .status\n";
41 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
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);
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)
61 STATUS_PKT *sp = (STATUS_PKT *)ctx;
62 const char *type = "Unknown";
63 POOL_MEM send_msg(PM_MESSAGE);
78 bstrftimes(edt, sizeof(edt), alert_time);
80 len = Mmsg(send_msg, _(" %s Alert: at %s Volume=\"%s\" flags=0x%x alert=%s\n"),
81 type, edt, Volume, flags, long_msg);
83 len = Mmsg(send_msg, _(" %s Alert: at %s Volume=\"%s\" alert=%s\n"),
84 type, edt, Volume, short_msg);
86 sendit(send_msg, len, sp);
91 * Status command from Director
93 void output_status(STATUS_PKT *sp)
95 POOL_MEM msg(PM_MESSAGE);
98 list_status_header(sp);
103 list_running_jobs(sp);
106 * List jobs stuck in reservation system
108 list_jobs_waiting_on_reservation(sp);
111 * List terminated jobs (defined in lib/status.h)
113 list_terminated_jobs(sp);
121 * List cloud transfers
123 list_cloud_transfers(sp, false);
126 len = Mmsg(msg, _("Used Volume status:\n"));
127 if (!sp->api) sendit(msg, len, sp);
129 list_volumes(sendit, (void *)sp);
130 if (!sp->api) sendit("====\n\n", 6, sp);
133 list_spool_stats(sendit, (void *)sp);
135 if (!sp->api) sendit("====\n\n", 6, sp);
137 if (chk_dbglvl(10)) {
138 dbg_print_plugin(stdout);
142 static void list_resources(STATUS_PKT *sp)
145 POOL_MEM msg(PM_MESSAGE);
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);
156 static find_device(char *devname)
158 foreach_res(device, R_DEVICE) {
159 if (strcasecmp(device->hdr.name, devname) == 0) {
165 foreach_res(changer, R_AUTOCHANGER) {
166 if (strcasecmp(changer->hdr.name, devname) == 0) {
174 static void api_list_one_device(char *name, DEVICE *dev, STATUS_PKT *sp)
176 OutputWriter ow(sp->api_opts);
186 dev->get_freespace(&f, &t);
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,
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),
213 ow.get_output(OT_INT, "mounted", zero,
214 OT_INT, "waiting", zero,
215 OT_STRING, "volume", "",
216 OT_STRING, "pool", "",
221 switch(dev->blocked()) {
223 p = "User unmounted";
225 case BST_UNMOUNTED_WAITING_FOR_SYSOP:
226 p = "User unmounted during wait for media/mount";
228 case BST_DOING_ACQUIRE:
229 p = "Device is being initialized";
231 case BST_WAITING_FOR_SYSOP:
232 p = "Waiting for mount or create a volume";
234 case BST_WRITING_LABEL:
235 p = "Labeling a Volume";
242 /* TODO: give more information about blocked status
243 * and the volume needed if WAITING for SYSOP
245 ow.get_output(OT_STRING, "blocked_desc", NPRTB(p),
246 OT_INT, "blocked", blocked,
249 ow.get_output(OT_INT, "append", (int)dev->can_append(),
252 if (dev->can_append()) {
253 ow.get_output(OT_INT64, "bytes", dev->VolCatInfo.VolCatBytes,
254 OT_INT32, "blocks", dev->VolCatInfo.VolCatBlocks,
257 } else { /* reading */
258 ow.get_output(OT_INT64, "bytes", dev->VolCatInfo.VolCatRBytes,
259 OT_INT32, "blocks", dev->VolCatInfo.VolCatReads, /* might not be blocks */
263 ow.get_output(OT_INT, "file", dev->file,
264 OT_INT, "block", dev->block_num,
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,
281 p = ow.get_output(OT_END_OBJ, OT_END);
282 sendit(p, strlen(p), sp);
286 static void list_one_device(char *name, DEVICE *dev, STATUS_PKT *sp)
288 char b1[35], b2[35], b3[35];
289 POOL_MEM msg(PM_MESSAGE);
294 api_list_one_device(name, dev, sp);
299 len = Mmsg(msg, _("\nDevice \"%s\" is not open or does not exist.\n"),
301 sendit(msg, len, sp);
302 if (!sp->api) sendit("==\n", 4, sp);
306 if (dev->is_open()) {
307 if (dev->is_labeled()) {
308 len = Mmsg(msg, _("\nDevice %s is %s %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);
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);
323 if (dev->can_append()) {
324 bpb = dev->VolCatInfo.VolCatBlocks;
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;
339 if (dev->VolCatInfo.VolCatRBytes > 0) {
340 bpb = dev->VolCatInfo.VolCatRBytes / bpb;
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);
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);
355 len = Mmsg(msg, _("\nDevice %s: %s is not open.\n"),
356 dev->print_type(), dev->print_name());
357 sendit(msg, len, sp);
359 send_blocked_status(dev, sp);
361 /* TODO: We need to check with Mount command, maybe we can
362 * display this number only when the device is open.
364 if (dev->is_file()) {
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);
376 dev->show_tape_alerts((DCR *)sp, list_short, list_all, status_alert_callback);
378 if (!sp->api) sendit("==\n", 4, sp);
381 void _dbg_list_one_device(char *name, DEVICE *dev, const char *file, int line)
385 sp.callback = dbg_sendit;
387 d_msg(file, line, 0, "Called dbg_list_one_device():");
388 list_one_device(name, dev, &sp);
389 send_device_status(dev, &sp);
392 static void list_one_autochanger(char *name, AUTOCHANGER *changer, STATUS_PKT *sp)
397 POOL_MEM msg(PM_MESSAGE);
398 OutputWriter ow(sp->api_opts);
401 ow.get_output(OT_START_OBJ,
402 OT_STRING, "autochanger", changer->hdr.name,
405 ow.start_group("devices");
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,
417 p = ow.get_output(OT_END_OBJ, OT_END);
418 sendit(p, strlen(p), sp);
422 len = Mmsg(msg, _("Autochanger \"%s\" with devices:\n"),
424 sendit(msg, len, sp);
426 foreach_alist(device, changer->device) {
428 len = Mmsg(msg, " %s\n", device->dev->print_name());
429 sendit(msg, len, sp);
431 len = Mmsg(msg, " %s\n", device->hdr.name);
432 sendit(msg, len, sp);
438 static void list_devices(STATUS_PKT *sp, char *name)
442 AUTOCHANGER *changer;
443 POOL_MEM msg(PM_MESSAGE);
446 len = Mmsg(msg, _("\nDevice status:\n"));
447 sendit(msg, len, sp);
450 foreach_res(changer, R_AUTOCHANGER) {
451 if (!name || strcmp(changer->hdr.name, name) == 0) {
452 list_one_autochanger(changer->hdr.name, changer, sp);
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);
461 if (!sp->api) sendit("====\n\n", 6, sp);
464 static void list_cloud_transfers(STATUS_PKT *sp, bool verbose)
469 POOL_MEM msg(PM_MESSAGE);
471 foreach_res(device, R_DEVICE) {
472 if (device->dev && device->dev->is_cloud()) {
476 len = Mmsg(msg, _("Cloud transfer status:\n"));
477 sendit(msg, len, sp);
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 */
491 if (!first && !sp->api) sendit("====\n\n", 6, sp);
494 static void api_list_sd_status_header(STATUS_PKT *sp)
497 alist drivers(10, not_owned_by_alist);
498 OutputWriter wt(sp->api_opts);
500 sd_list_loaded_drivers(&drivers);
501 wt.start_group("header");
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,
515 sendit(p, strlen(p), sp);
518 static void list_status_header(STATUS_PKT *sp)
520 char dt[MAX_TIME_LENGTH];
521 char b1[35], b2[35], b3[35], b4[35], b5[35];
522 POOL_MEM msg(PM_MESSAGE);
526 api_list_sd_status_header(sp);
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);
534 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
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);
559 static void send_blocked_status(DEVICE *dev, STATUS_PKT *sp)
561 POOL_MEM msg(PM_MESSAGE);
565 len = Mmsg(msg, _("No DEVICE structure.\n\n"));
566 sendit(msg, len, sp);
570 len = Mmsg(msg, _(" Device is disabled. User command.\n"));
571 sendit(msg, len, sp);
573 switch (dev->blocked()) {
575 len = Mmsg(msg, _(" Device is BLOCKED. User unmounted.\n"));
576 sendit(msg, len, sp);
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);
582 case BST_WAITING_FOR_SYSOP:
585 bool found_jcr = false;
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"
592 " Media type: %s\n"),
596 sendit(msg, len, sp);
598 } else if (dcr->jcr->JobStatus == JS_WaitMedia) {
599 len = Mmsg(msg, _(" Device is BLOCKED waiting to create a volume for:\n"
601 " Media type: %s\n"),
604 sendit(msg, len, sp);
611 len = Mmsg(msg, _(" Device is BLOCKED waiting for media.\n"));
612 sendit(msg, len, sp);
616 case BST_DOING_ACQUIRE:
617 len = Mmsg(msg, _(" Device is being initialized.\n"));
618 sendit(msg, len, sp);
620 case BST_WRITING_LABEL:
621 len = Mmsg(msg, _(" Device is blocked labeling a Volume.\n"));
622 sendit(msg, len, sp);
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);
639 send_device_status(dev, sp);
643 void send_device_status(DEVICE *dev, STATUS_PKT *sp)
645 POOL_MEM msg(PM_MESSAGE);
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);
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));
691 sendit(msg, len, sp);
693 len = Mmsg(msg, _("Attached JobIds: "));
694 sendit(msg, len, sp);
697 foreach_dlist(dcr, dev->attached_dcrs) {
702 len = Mmsg(msg, "%d", (int)dcr->jcr->JobId);
703 sendit(msg, len, sp);
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(),
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);
722 static void api_list_running_jobs(STATUS_PKT *sp)
726 OutputWriter ow(sp->api_opts);
728 uint64_t inst_bps, total_bps;
729 int inst_sec, total_sec;
732 time_t now = time(NULL);
735 if (jcr->getJobType() == JT_SYSTEM) {
738 ow.get_output(OT_CLEAR,
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,
753 rdcr = jcr->read_dcr;
756 if (rdcr && rdcr->device) {
757 p1 = rdcr->VolumeName;
758 p2 = rdcr->pool_name;
759 p3 = rdcr->device->hdr.name;
761 ow.get_output(OT_STRING, "read_volume", NPRTB(p1),
762 OT_STRING, "read_pool", NPRTB(p2),
763 OT_STRING, "read_device", NPRTB(p3),
768 if (dcr && dcr->device) {
769 p1 = dcr->VolumeName;
771 p3 = dcr->device->hdr.name;
773 i2 = dcr->despooling;
774 i3 = dcr->despool_wait;
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,
785 if (jcr->last_time == 0) {
786 jcr->last_time = jcr->run_time;
789 total_sec = now - jcr->run_time;
790 inst_sec = now - jcr->last_time;
792 if (total_sec <= 0) {
799 /* Instanteous bps not smoothed */
800 inst_bps = (jcr->JobBytes - jcr->LastJobBytes) / inst_sec;
801 if (jcr->LastRate == 0) {
802 jcr->LastRate = inst_bps;
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;
810 p1 = ow.get_output(OT_PINT64, "avebytes_sec", total_bps,
811 OT_PINT64, "lastbytes_sec", inst_bps,
815 sendit(p1, strlen(p1), sp);
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;
828 static void list_running_jobs(STATUS_PKT *sp)
831 uint64_t inst_bps, total_bps;
832 int inst_sec, total_sec;
835 char JobName[MAX_NAME_LENGTH];
836 char b1[50], b2[50], b3[50], b4[50];
838 POOL_MEM msg(PM_MESSAGE);
839 time_t now = time(NULL);
842 api_list_running_jobs(sp);
846 len = Mmsg(msg, _("\nRunning Jobs:\n"));
847 if (!sp->api) sendit(msg, len, sp);
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);
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 */
861 for (int i=0; i<3; i++) {
862 if ((p=strrchr(JobName, '.')) != NULL) {
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()),
875 rdcr->dev?rdcr->dev->print_name():
876 rdcr->device->device_name,
877 jcr->use_new_match_all
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()),
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);
896 if (jcr->last_time == 0) {
897 jcr->last_time = jcr->run_time;
899 total_sec = now - jcr->run_time;
900 inst_sec = now - jcr->last_time;
901 if (total_sec <= 0) {
907 /* Instanteous bps not smoothed */
908 inst_bps = (jcr->JobBytes - jcr->LastJobBytes) / inst_sec;
909 if (jcr->LastRate == 0) {
910 jcr->LastRate = inst_bps;
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;
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);
937 len = Mmsg(msg, _(" FDSocket closed\n"));
938 sendit(msg, len, sp);
946 len = Mmsg(msg, _("No Jobs running.\n"));
947 if (!sp->api) sendit(msg, len, sp);
949 if (!sp->api) sendit("====\n", 5, sp);
952 static void list_jobs_waiting_on_reservation(STATUS_PKT *sp)
955 POOL_MEM msg(PM_MESSAGE);
958 len = Mmsg(msg, _("\nJobs waiting to reserve a drive:\n"));
959 if (!sp->api) sendit(msg, len, sp);
962 if (!jcr->reserve_msgs) {
965 send_drive_reserve_messages(jcr, sendit, sp);
969 if (!sp->api) sendit("====\n", 5, sp);
973 static void sendit(const char *msg, int len, void *sp)
975 sendit(msg, len, (STATUS_PKT *)sp);
978 static void sendit(POOL_MEM &msg, int len, STATUS_PKT *sp)
982 bs->msg = check_pool_memory_size(bs->msg, len+1);
983 memcpy(bs->msg, msg.c_str(), len+1);
987 sp->callback(msg.c_str(), len, sp->context);
991 static void dbg_sendit(const char *msg, int len, void *sp)
999 * Status command from Director
1001 bool status_cmd(JCR *jcr)
1003 BSOCK *dir = jcr->dir_bsock;
1009 dir->signal(BNET_EOD);
1014 * .status command from Director
1016 bool qstatus_cmd(JCR *jcr)
1018 BSOCK *dir = jcr->dir_bsock;
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 */
1033 parse_args(dir->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
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);
1046 /* The status command can contain some arguments
1048 * i=1 => [running | current | last | ... ]
1050 for (int i=0 ; i < argc ; i++) {
1051 if (!strcmp(argk[i], "device") && argv[i]) {
1053 unbash_spaces(device);
1055 } else if (!strcmp(argk[i], "api") && argv[i]) {
1056 api = atoi(argv[i]);
1058 } else if (!strcmp(argk[i], "api_opts") && argv[i]) {
1059 strncpy(sp.api_opts, argv[i], sizeof(sp.api_opts));;
1063 Dmsg1(100, "cmd=%s\n", cmd);
1065 if (strcasecmp(cmd, "current") == 0) {
1066 dir->fsend(OKqstatus, cmd);
1068 if (njcr->JobId != 0) {
1069 dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
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);
1079 } else if (strcasecmp(cmd, "header") == 0) {
1081 list_status_header(&sp);
1082 } else if (strcasecmp(cmd, "running") == 0) {
1084 list_running_jobs(&sp);
1085 } else if (strcasecmp(cmd, "waitreservation") == 0) {
1087 list_jobs_waiting_on_reservation(&sp);
1088 } else if (strcasecmp(cmd, "devices") == 0) {
1090 list_devices(&sp, device);
1091 } else if (strcasecmp(cmd, "volumes") == 0) {
1093 list_volumes(sendit, &sp);
1094 } else if (strcasecmp(cmd, "spooling") == 0) {
1096 list_spool_stats(sendit, &sp);
1097 } else if (strcasecmp(cmd, "terminated") == 0) {
1099 list_terminated_jobs(&sp); /* defined in lib/status.h */
1100 } else if (strcasecmp(cmd, "resources") == 0) {
1102 list_resources(&sp);
1103 } else if (strcasecmp(cmd, "cloud") == 0) {
1104 list_cloud_transfers(&sp, true);
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);
1111 dir->signal(BNET_EOD);
1112 free_pool_memory(args);
1116 /* List plugins and drivers */
1117 static void list_plugins(STATUS_PKT *sp)
1119 POOL_MEM msg(PM_MESSAGE);
1120 alist drivers(10, not_owned_by_alist);
1123 if (b_plugin_list && b_plugin_list->size() > 0) {
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, ")");
1135 pm_strcat(msg, "\n ");
1137 pm_strcat(msg, " ");
1140 len = pm_strcat(msg, "\n");
1141 sendit(msg.c_str(), len, sp);
1143 sd_list_loaded_drivers(&drivers);
1144 if (drivers.size() > 0) {
1146 pm_strcpy(msg, " Drivers: ");
1147 foreach_alist(drv, (&drivers)) {
1148 len = pm_strcat(msg, drv);
1150 pm_strcat(msg, "\n ");
1152 pm_strcat(msg, " ");
1155 len = pm_strcat(msg, "\n");
1156 sendit(msg.c_str(), len, sp);