2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2016 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);
57 * Status command from Director
59 void output_status(STATUS_PKT *sp)
61 POOL_MEM msg(PM_MESSAGE);
64 list_status_header(sp);
69 list_running_jobs(sp);
72 * List jobs stuck in reservation system
74 list_jobs_waiting_on_reservation(sp);
77 * List terminated jobs (defined in lib/status.h)
79 list_terminated_jobs(sp);
87 len = Mmsg(msg, _("Used Volume status:\n"));
88 if (!sp->api) sendit(msg, len, sp);
90 list_volumes(sendit, (void *)sp);
91 if (!sp->api) sendit("====\n\n", 6, sp);
94 list_spool_stats(sendit, (void *)sp);
96 if (!sp->api) sendit("====\n\n", 6, sp);
99 dbg_print_plugin(stdout);
103 static void list_resources(STATUS_PKT *sp)
106 POOL_MEM msg(PM_MESSAGE);
109 len = Mmsg(msg, _("\nSD Resources:\n"));
110 if (!sp->api) sendit(msg, len, sp);
111 dump_resource(R_DEVICE, resources[R_DEVICE-r_first], sp);
112 if (!sp->api) sendit("====\n\n", 6, sp);
117 static find_device(char *devname)
119 foreach_res(device, R_DEVICE) {
120 if (strcasecmp(device->hdr.name, devname) == 0) {
126 foreach_res(changer, R_AUTOCHANGER) {
127 if (strcasecmp(changer->hdr.name, devname) == 0) {
136 static void list_one_device(char *name, DEVICE *dev, STATUS_PKT *sp)
138 char b1[35], b2[35], b3[35];
139 POOL_MEM msg(PM_MESSAGE);
144 len = Mmsg(msg, _("\nDevice \"%s\" is not open or does not exist.\n"),
146 sendit(msg, len, sp);
147 if (!sp->api) sendit("==\n", 4, sp);
151 if (dev->is_open()) {
152 if (dev->is_labeled()) {
153 len = Mmsg(msg, _("\nDevice %s is %s %s:\n"
156 " Media type: %s\n"),
157 dev->print_type(), dev->print_name(),
158 dev->blocked()?_("waiting for"):_("mounted with"),
159 dev->VolHdr.VolumeName,
160 dev->pool_name[0]?dev->pool_name:_("*unknown*"),
161 dev->device->media_type);
162 sendit(msg, len, sp);
164 len = Mmsg(msg, _("\nDevice %s: %s open but no Bacula volume is currently mounted.\n"),
165 dev->print_type(), dev->print_name());
166 sendit(msg, len, sp);
168 send_blocked_status(dev, sp);
169 if (dev->can_append()) {
170 bpb = dev->VolCatInfo.VolCatBlocks;
174 bpb = dev->VolCatInfo.VolCatBytes / bpb;
175 len = Mmsg(msg, _(" Total Bytes=%s Blocks=%s Bytes/block=%s\n"),
176 edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, b1),
177 edit_uint64_with_commas(dev->VolCatInfo.VolCatBlocks, b2),
178 edit_uint64_with_commas(bpb, b3));
179 sendit(msg, len, sp);
180 } else { /* reading */
181 bpb = dev->VolCatInfo.VolCatReads;
185 if (dev->VolCatInfo.VolCatRBytes > 0) {
186 bpb = dev->VolCatInfo.VolCatRBytes / bpb;
190 len = Mmsg(msg, _(" Total Bytes Read=%s Blocks Read=%s Bytes/block=%s\n"),
191 edit_uint64_with_commas(dev->VolCatInfo.VolCatRBytes, b1),
192 edit_uint64_with_commas(dev->VolCatInfo.VolCatReads, b2),
193 edit_uint64_with_commas(bpb, b3));
194 sendit(msg, len, sp);
196 len = Mmsg(msg, _(" Positioned at File=%s Block=%s\n"),
197 edit_uint64_with_commas(dev->file, b1),
198 edit_uint64_with_commas(dev->block_num, b2));
199 sendit(msg, len, sp);
202 len = Mmsg(msg, _("\nDevice %s: %s is not open.\n"),
203 dev->print_type(), dev->print_name());
204 sendit(msg, len, sp);
205 send_blocked_status(dev, sp);
208 /* TODO: We need to check with Mount command, maybe we can
209 * display this number only when the device is open.
211 if (dev->is_file()) {
214 dev->get_freespace(&f, &t);
215 if (t > 0) { /* We might not have access to numbers */
216 len = Mmsg(msg, _(" Available Space=%sB\n"),
217 edit_uint64_with_suffix(f, ed1));
218 sendit(msg, len, sp);
222 if (!sp->api) sendit("==\n", 4, sp);
225 void _dbg_list_one_device(char *name, DEVICE *dev, const char *file, int line)
229 sp.callback = dbg_sendit;
231 d_msg(file, line, 0, "Called dbg_list_one_device():");
232 list_one_device(name, dev, &sp);
233 send_device_status(dev, &sp);
236 static void list_one_autochanger(char *name, AUTOCHANGER *changer, STATUS_PKT *sp)
240 POOL_MEM msg(PM_MESSAGE);
242 len = Mmsg(msg, _("Autochanger \"%s\" with devices:\n"),
244 sendit(msg, len, sp);
246 foreach_alist(device, changer->device) {
248 len = Mmsg(msg, " %s\n", device->dev->print_name());
249 sendit(msg, len, sp);
251 len = Mmsg(msg, " %s\n", device->hdr.name);
252 sendit(msg, len, sp);
257 static void list_devices(STATUS_PKT *sp, char *name)
261 AUTOCHANGER *changer;
262 POOL_MEM msg(PM_MESSAGE);
265 len = Mmsg(msg, _("\nDevice status:\n"));
266 sendit(msg, len, sp);
269 foreach_res(changer, R_AUTOCHANGER) {
270 if (!name || strcmp(changer->hdr.name, name) == 0) {
271 list_one_autochanger(changer->hdr.name, changer, sp);
275 foreach_res(device, R_DEVICE) {
276 if (!name || strcmp(device->hdr.name, name) == 0) {
277 list_one_device(device->hdr.name, device->dev, sp);
280 if (!sp->api) sendit("====\n\n", 6, sp);
283 static void list_status_header(STATUS_PKT *sp)
285 char dt[MAX_TIME_LENGTH];
286 char b1[35], b2[35], b3[35], b4[35], b5[35];
287 POOL_MEM msg(PM_MESSAGE);
290 len = Mmsg(msg, _("%s %sVersion: %s (%s) %s %s %s\n"),
291 my_name, "", VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
292 sendit(msg, len, sp);
294 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
297 len = Mmsg(msg, _("Daemon started %s. Jobs: run=%d, running=%d.\n"),
298 dt, num_jobs_run, job_count());
299 sendit(msg, len, sp);
300 len = Mmsg(msg, _(" Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
301 edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
302 edit_uint64_with_commas(sm_bytes, b2),
303 edit_uint64_with_commas(sm_max_bytes, b3),
304 edit_uint64_with_commas(sm_buffers, b4),
305 edit_uint64_with_commas(sm_max_buffers, b5));
306 sendit(msg, len, sp);
307 len = Mmsg(msg, " Sizes: boffset_t=%d size_t=%d int32_t=%d int64_t=%d "
309 (int)sizeof(boffset_t), (int)sizeof(size_t), (int)sizeof(int32_t),
310 (int)sizeof(int64_t), (int)DEVELOPER_MODE, (int)0);
311 sendit(msg, len, sp);
315 static void send_blocked_status(DEVICE *dev, STATUS_PKT *sp)
317 POOL_MEM msg(PM_MESSAGE);
321 len = Mmsg(msg, _("No DEVICE structure.\n\n"));
322 sendit(msg, len, sp);
326 len = Mmsg(msg, _(" Device is disabled. User command.\n"));
327 sendit(msg, len, sp);
329 switch (dev->blocked()) {
331 len = Mmsg(msg, _(" Device is BLOCKED. User unmounted.\n"));
332 sendit(msg, len, sp);
334 case BST_UNMOUNTED_WAITING_FOR_SYSOP:
335 len = Mmsg(msg, _(" Device is BLOCKED. User unmounted during wait for media/mount.\n"));
336 sendit(msg, len, sp);
338 case BST_WAITING_FOR_SYSOP:
341 bool found_jcr = false;
344 foreach_dlist(dcr, dev->attached_dcrs) {
345 if (dcr->jcr->JobStatus == JS_WaitMount) {
346 len = Mmsg(msg, _(" Device is BLOCKED waiting for mount of volume \"%s\",\n"
348 " Media type: %s\n"),
352 sendit(msg, len, sp);
354 } else if (dcr->jcr->JobStatus == JS_WaitMedia) {
355 len = Mmsg(msg, _(" Device is BLOCKED waiting to create a volume for:\n"
357 " Media type: %s\n"),
360 sendit(msg, len, sp);
367 len = Mmsg(msg, _(" Device is BLOCKED waiting for media.\n"));
368 sendit(msg, len, sp);
372 case BST_DOING_ACQUIRE:
373 len = Mmsg(msg, _(" Device is being initialized.\n"));
374 sendit(msg, len, sp);
376 case BST_WRITING_LABEL:
377 len = Mmsg(msg, _(" Device is blocked labeling a Volume.\n"));
378 sendit(msg, len, sp);
383 /* Send autochanger slot status */
384 if (dev->is_autochanger()) {
385 if (dev->get_slot() > 0) {
386 len = Mmsg(msg, _(" Slot %d %s loaded in drive %d.\n"),
387 dev->get_slot(), dev->is_open()?"is": "was last", dev->drive_index);
388 sendit(msg, len, sp);
389 } else if (dev->get_slot() <= 0) {
390 len = Mmsg(msg, _(" Drive %d is not loaded.\n"), dev->drive_index);
391 sendit(msg, len, sp);
395 send_device_status(dev, sp);
399 void send_device_status(DEVICE *dev, STATUS_PKT *sp)
401 POOL_MEM msg(PM_MESSAGE);
407 len = Mmsg(msg, _("Configured device capabilities:\n"));
408 sendit(msg, len, sp);
409 len = Mmsg(msg, " %sEOF %sBSR %sBSF %sFSR %sFSF %sEOM %sREM %sRACCESS %sAUTOMOUNT %sLABEL %sANONVOLS %sALWAYSOPEN\n",
410 dev->capabilities & CAP_EOF ? "" : "!",
411 dev->capabilities & CAP_BSR ? "" : "!",
412 dev->capabilities & CAP_BSF ? "" : "!",
413 dev->capabilities & CAP_FSR ? "" : "!",
414 dev->capabilities & CAP_FSF ? "" : "!",
415 dev->capabilities & CAP_EOM ? "" : "!",
416 dev->capabilities & CAP_REM ? "" : "!",
417 dev->capabilities & CAP_RACCESS ? "" : "!",
418 dev->capabilities & CAP_AUTOMOUNT ? "" : "!",
419 dev->capabilities & CAP_LABEL ? "" : "!",
420 dev->capabilities & CAP_ANONVOLS ? "" : "!",
421 dev->capabilities & CAP_ALWAYSOPEN ? "" : "!");
422 sendit(msg, len, sp);
425 len = Mmsg(msg, _("Device state:\n"));
426 sendit(msg, len, sp);
427 len = Mmsg(msg, " %sOPENED %sTAPE %sLABEL %sMALLOC %sAPPEND %sREAD %sEOT %sWEOT %sEOF %sNEXTVOL %sSHORT %sMOUNTED\n",
428 dev->is_open() ? "" : "!",
429 dev->is_tape() ? "" : "!",
430 dev->is_labeled() ? "" : "!",
431 dev->state & ST_MALLOC ? "" : "!",
432 dev->can_append() ? "" : "!",
433 dev->can_read() ? "" : "!",
434 dev->at_eot() ? "" : "!",
435 dev->state & ST_WEOT ? "" : "!",
436 dev->at_eof() ? "" : "!",
437 dev->state & ST_NEXTVOL ? "" : "!",
438 dev->state & ST_SHORT ? "" : "!",
439 dev->state & ST_MOUNTED ? "" : "!");
440 sendit(msg, len, sp);
441 len = Mmsg(msg, _(" num_writers=%d reserves=%d block=%d enabled=%d\n"), dev->num_writers,
442 dev->num_reserved(), dev->blocked(), dev->enabled);
443 sendit(msg, len, sp);
445 len = Mmsg(msg, _("Attached JobIds: "));
446 sendit(msg, len, sp);
449 foreach_dlist(dcr, dev->attached_dcrs) {
454 len = Mmsg(msg, "%d", (int)dcr->jcr->JobId);
455 sendit(msg, len, sp);
463 len = Mmsg(msg, _("Device parameters:\n"));
464 sendit(msg, len, sp);
465 len = Mmsg(msg, _(" Archive name: %s Device name: %s\n"), dev->archive_name(),
467 sendit(msg, len, sp);
468 len = Mmsg(msg, _(" File=%u block=%u\n"), dev->file, dev->block_num);
469 sendit(msg, len, sp);
470 len = Mmsg(msg, _(" Min block=%u Max block=%u\n"), dev->min_block_size, dev->max_block_size);
471 sendit(msg, len, sp);
474 static void list_running_jobs(STATUS_PKT *sp)
477 uint64_t inst_bps, total_bps;
478 int inst_sec, total_sec;
481 char JobName[MAX_NAME_LENGTH];
482 char b1[50], b2[50], b3[50], b4[50];
484 POOL_MEM msg(PM_MESSAGE);
485 time_t now = time(NULL);
487 len = Mmsg(msg, _("\nRunning Jobs:\n"));
488 if (!sp->api) sendit(msg, len, sp);
491 if (jcr->JobStatus == JS_WaitFD) {
492 len = Mmsg(msg, _("%s Job %s waiting for Client connection.\n"),
493 job_type_to_str(jcr->getJobType()), jcr->Job);
494 sendit(msg, len, sp);
497 rdcr = jcr->read_dcr;
498 if ((dcr && dcr->device) || (rdcr && rdcr->device)) {
499 bstrncpy(JobName, jcr->Job, sizeof(JobName));
500 /* There are three periods after the Job name */
502 for (int i=0; i<3; i++) {
503 if ((p=strrchr(JobName, '.')) != NULL) {
507 if (rdcr && rdcr->device) {
508 len = Mmsg(msg, _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
509 " pool=\"%s\" device=%s\n"),
510 job_level_to_str(jcr->getJobLevel()),
511 job_type_to_str(jcr->getJobType()),
516 rdcr->dev?rdcr->dev->print_name():
517 rdcr->device->device_name);
518 sendit(msg, len, sp);
519 } else if (dcr && dcr->device) {
520 len = Mmsg(msg, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
521 " pool=\"%s\" device=%s\n"),
522 job_level_to_str(jcr->getJobLevel()),
523 job_type_to_str(jcr->getJobType()),
528 dcr->dev?dcr->dev->print_name():
529 dcr->device->device_name);
530 sendit(msg, len, sp);
531 len= Mmsg(msg, _(" spooling=%d despooling=%d despool_wait=%d\n"),
532 dcr->spooling, dcr->despooling, dcr->despool_wait);
533 sendit(msg, len, sp);
535 if (jcr->last_time == 0) {
536 jcr->last_time = jcr->run_time;
538 total_sec = now - jcr->run_time;
539 inst_sec = now - jcr->last_time;
540 if (total_sec <= 0) {
546 /* Instanteous bps not smoothed */
547 inst_bps = (jcr->JobBytes - jcr->LastJobBytes) / inst_sec;
548 if (jcr->LastRate == 0) {
549 jcr->LastRate = inst_bps;
551 /* Smooth the instantaneous bps a bit */
552 inst_bps = (2 * jcr->LastRate + inst_bps) / 3;
553 /* total bps (AveBytes/sec) since start of job */
554 total_bps = jcr->JobBytes / total_sec;
555 len = Mmsg(msg, _(" Files=%s Bytes=%s AveBytes/sec=%s LastBytes/sec=%s\n"),
556 edit_uint64_with_commas(jcr->JobFiles, b1),
557 edit_uint64_with_commas(jcr->JobBytes, b2),
558 edit_uint64_with_commas(total_bps, b3),
559 edit_uint64_with_commas(inst_bps, b4));
560 sendit(msg, len, sp);
561 /* Update only every 10 seconds */
562 if (now - jcr->last_time > 10) {
563 jcr->LastRate = inst_bps;
564 jcr->LastJobBytes = jcr->JobBytes;
565 jcr->last_time = now;
569 if (jcr->file_bsock) {
570 len = Mmsg(msg, _(" FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
571 edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
572 jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
573 jcr->file_bsock->m_fd);
574 sendit(msg, len, sp);
576 len = Mmsg(msg, _(" FDSocket closed\n"));
577 sendit(msg, len, sp);
585 len = Mmsg(msg, _("No Jobs running.\n"));
586 if (!sp->api) sendit(msg, len, sp);
588 if (!sp->api) sendit("====\n", 5, sp);
591 static void list_jobs_waiting_on_reservation(STATUS_PKT *sp)
594 POOL_MEM msg(PM_MESSAGE);
597 len = Mmsg(msg, _("\nJobs waiting to reserve a drive:\n"));
598 if (!sp->api) sendit(msg, len, sp);
601 if (!jcr->reserve_msgs) {
604 send_drive_reserve_messages(jcr, sendit, sp);
608 if (!sp->api) sendit("====\n", 5, sp);
612 static void sendit(const char *msg, int len, void *sp)
614 sendit(msg, len, (STATUS_PKT *)sp);
617 static void sendit(POOL_MEM &msg, int len, STATUS_PKT *sp)
621 bs->msg = check_pool_memory_size(bs->msg, len+1);
622 memcpy(bs->msg, msg.c_str(), len+1);
626 sp->callback(msg.c_str(), len, sp->context);
630 static void dbg_sendit(const char *msg, int len, void *sp)
638 * Status command from Director
640 bool status_cmd(JCR *jcr)
642 BSOCK *dir = jcr->dir_bsock;
648 dir->signal(BNET_EOD);
653 * .status command from Director
655 bool qstatus_cmd(JCR *jcr)
657 BSOCK *dir = jcr->dir_bsock;
661 POOLMEM *args = get_pool_memory(PM_MESSAGE);
662 char *argk[MAX_CMD_ARGS]; /* argument keywords */
663 char *argv[MAX_CMD_ARGS]; /* argument values */
664 int argc; /* number of arguments */
672 parse_args(dir->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
674 /* .status xxxx at the minimum */
675 if (argc < 2 || strcmp(argk[0], ".status") != 0) {
676 pm_strcpy(jcr->errmsg, dir->msg);
677 dir->fsend(_("3900 No arg in .status command: %s\n"), jcr->errmsg);
678 dir->signal(BNET_EOD);
685 /* The status command can contain some arguments
687 * i=1 => [running | current | last | ... ]
689 for (int i=0 ; i < argc ; i++) {
690 if (!strcmp(argk[i], "device") && argv[i]) {
692 unbash_spaces(device);
694 } else if (!strcmp(argk[i], "api") && argv[i]) {
697 } else if (!strcmp(argk[i], "api_opts") && argv[i]) {
698 strncpy(sp.api_opts, argv[i], sizeof(sp.api_opts));;
702 Dmsg1(100, "cmd=%s\n", cmd);
704 if (strcasecmp(cmd, "current") == 0) {
705 dir->fsend(OKqstatus, cmd);
707 if (njcr->JobId != 0) {
708 dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
712 } else if (strcasecmp(cmd, "last") == 0) {
713 dir->fsend(OKqstatus, cmd);
714 if ((last_jobs) && (last_jobs->size() > 0)) {
715 job = (s_last_job*)last_jobs->last();
716 dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
718 } else if (strcasecmp(cmd, "header") == 0) {
720 list_status_header(&sp);
721 } else if (strcasecmp(cmd, "running") == 0) {
723 list_running_jobs(&sp);
724 } else if (strcasecmp(cmd, "waitreservation") == 0) {
726 list_jobs_waiting_on_reservation(&sp);
727 } else if (strcasecmp(cmd, "devices") == 0) {
729 list_devices(&sp, device);
730 } else if (strcasecmp(cmd, "volumes") == 0) {
732 list_volumes(sendit, &sp);
733 } else if (strcasecmp(cmd, "spooling") == 0) {
735 list_spool_stats(sendit, &sp);
736 } else if (strcasecmp(cmd, "terminated") == 0) {
738 list_terminated_jobs(&sp); /* defined in lib/status.h */
739 } else if (strcasecmp(cmd, "resources") == 0) {
743 pm_strcpy(jcr->errmsg, dir->msg);
744 dir->fsend(_("3900 Unknown arg in .status command: %s\n"), jcr->errmsg);
745 dir->signal(BNET_EOD);
748 dir->signal(BNET_EOD);
749 free_pool_memory(args);
753 static void list_plugins(STATUS_PKT *sp)
755 POOL_MEM msg(PM_MESSAGE);
756 if (b_plugin_list->size() > 0) {
759 pm_strcpy(msg, " Plugin: ");
760 foreach_alist(plugin, b_plugin_list) {
761 len = pm_strcat(msg, plugin->file);
762 /* Print plugin version when debug activated */
763 if (debug_level > 0 && plugin->pinfo) {
765 pm_strcat(msg, NPRT(sdplug_info(plugin)->plugin_version));
766 len = pm_strcat(msg, ")");
769 pm_strcat(msg, "\n ");
774 len = pm_strcat(msg, "\n");
775 sendit(msg.c_str(), len, sp);