2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
5 Copyright (C) 2003-2014 Free Software Foundation Europe e.V.
7 The original author of Bacula is Kern Sibbald, with contributions
8 from many others, a complete list can be found in the file AUTHORS.
10 You may use this file and others of this release according to the
11 license defined in the LICENSE file, which includes the Affero General
12 Public License, v3.0 ("AGPLv3") and some additional permissions and
13 terms pursuant to its AGPLv3 Section 7.
15 This notice must be preserved when any source code is
16 conveyed and/or propagated.
18 Bacula(R) is a registered trademark of Kern Sibbald.
21 * This file handles the status command
23 * Kern Sibbald, May MMIII
30 #include "lib/status.h"
31 #include "sd_plugins.h"
33 /* Imported functions */
34 extern void dbg_print_plugin(FILE *fp);
36 /* Imported variables */
37 extern BSOCK *filed_chan;
38 extern void *start_heap;
40 /* Static variables */
41 static char OKqstatus[] = "3000 OK .status\n";
42 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
45 /* Forward referenced functions */
46 static void sendit(POOL_MEM &msg, int len, STATUS_PKT *sp);
47 static void sendit(const char *msg, int len, void *arg);
48 static void dbg_sendit(const char *msg, int len, void *arg);
49 static void send_blocked_status(DEVICE *dev, STATUS_PKT *sp);
50 static void send_device_status(DEVICE *dev, STATUS_PKT *sp);
51 static void list_running_jobs(STATUS_PKT *sp);
52 static void list_jobs_waiting_on_reservation(STATUS_PKT *sp);
53 static void list_status_header(STATUS_PKT *sp);
54 static void list_devices(STATUS_PKT *sp, char *name=NULL);
55 static void list_plugins(STATUS_PKT *sp);
58 * Status command from Director
60 void output_status(STATUS_PKT *sp)
62 POOL_MEM msg(PM_MESSAGE);
65 list_status_header(sp);
70 list_running_jobs(sp);
73 * List jobs stuck in reservation system
75 list_jobs_waiting_on_reservation(sp);
78 * List terminated jobs (defined in lib/status.h)
80 list_terminated_jobs(sp);
88 len = Mmsg(msg, _("Used Volume status:\n"));
89 if (!sp->api) sendit(msg, len, sp);
91 list_volumes(sendit, (void *)sp);
92 if (!sp->api) sendit("====\n\n", 6, sp);
95 list_spool_stats(sendit, (void *)sp);
97 if (!sp->api) sendit("====\n\n", 6, sp);
100 dbg_print_plugin(stdout);
104 static void list_resources(STATUS_PKT *sp)
107 POOL_MEM msg(PM_MESSAGE);
110 len = Mmsg(msg, _("\nSD Resources:\n"));
111 if (!sp->api) sendit(msg, len, sp);
112 dump_resource(R_DEVICE, resources[R_DEVICE-r_first], sp);
113 if (!sp->api) sendit("====\n\n", 6, sp);
118 static find_device(char *devname)
120 foreach_res(device, R_DEVICE) {
121 if (strcasecmp(device->hdr.name, devname) == 0) {
127 foreach_res(changer, R_AUTOCHANGER) {
128 if (strcasecmp(changer->hdr.name, devname) == 0) {
137 static void list_one_device(char *name, DEVICE *dev, STATUS_PKT *sp)
139 char b1[35], b2[35], b3[35];
140 POOL_MEM msg(PM_MESSAGE);
145 len = Mmsg(msg, _("\nDevice \"%s\" is not open or does not exist.\n"),
147 sendit(msg, len, sp);
148 if (!sp->api) sendit("==\n", 4, sp);
152 if (dev->is_open()) {
153 if (dev->is_labeled()) {
154 len = Mmsg(msg, _("\nDevice %s is %s %s:\n"
157 " Media type: %s\n"),
158 dev->print_type(), dev->print_name(),
159 dev->blocked()?_("waiting for"):_("mounted with"),
160 dev->VolHdr.VolumeName,
161 dev->pool_name[0]?dev->pool_name:_("*unknown*"),
162 dev->device->media_type);
163 sendit(msg, len, sp);
165 len = Mmsg(msg, _("\nDevice %s: %s open but no Bacula volume is currently mounted.\n"),
166 dev->print_type(), dev->print_name());
167 sendit(msg, len, sp);
169 send_blocked_status(dev, sp);
170 if (dev->can_append()) {
171 bpb = dev->VolCatInfo.VolCatBlocks;
175 bpb = dev->VolCatInfo.VolCatBytes / bpb;
176 len = Mmsg(msg, _(" Total Bytes=%s Blocks=%s Bytes/block=%s\n"),
177 edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, b1),
178 edit_uint64_with_commas(dev->VolCatInfo.VolCatBlocks, b2),
179 edit_uint64_with_commas(bpb, b3));
180 sendit(msg, len, sp);
181 } else { /* reading */
182 bpb = dev->VolCatInfo.VolCatReads;
186 if (dev->VolCatInfo.VolCatRBytes > 0) {
187 bpb = dev->VolCatInfo.VolCatRBytes / bpb;
191 len = Mmsg(msg, _(" Total Bytes Read=%s Blocks Read=%s Bytes/block=%s\n"),
192 edit_uint64_with_commas(dev->VolCatInfo.VolCatRBytes, b1),
193 edit_uint64_with_commas(dev->VolCatInfo.VolCatReads, b2),
194 edit_uint64_with_commas(bpb, b3));
195 sendit(msg, len, sp);
197 len = Mmsg(msg, _(" Positioned at File=%s Block=%s\n"),
198 edit_uint64_with_commas(dev->file, b1),
199 edit_uint64_with_commas(dev->block_num, b2));
200 sendit(msg, len, sp);
203 len = Mmsg(msg, _("\nDevice %s: %s is not open.\n"),
204 dev->print_type(), dev->print_name());
205 sendit(msg, len, sp);
206 send_blocked_status(dev, sp);
209 /* TODO: We need to check with Mount command, maybe we can
210 * display this number only when the device is open.
212 if (dev->is_file()) {
215 dev->get_freespace(&f, &t);
216 if (t > 0) { /* We might not have access to numbers */
217 len = Mmsg(msg, _(" Available Space=%sB\n"),
218 edit_uint64_with_suffix(f, ed1));
219 sendit(msg, len, sp);
223 if (!sp->api) sendit("==\n", 4, sp);
226 void _dbg_list_one_device(char *name, DEVICE *dev, const char *file, int line)
230 sp.callback = dbg_sendit;
232 d_msg(file, line, 0, "Called dbg_list_one_device():");
233 list_one_device(name, dev, &sp);
234 send_device_status(dev, &sp);
237 static void list_one_autochanger(char *name, AUTOCHANGER *changer, STATUS_PKT *sp)
241 POOL_MEM msg(PM_MESSAGE);
243 len = Mmsg(msg, _("Autochanger \"%s\" with devices:\n"),
245 sendit(msg, len, sp);
247 foreach_alist(device, changer->device) {
249 len = Mmsg(msg, " %s\n", device->dev->print_name());
250 sendit(msg, len, sp);
252 len = Mmsg(msg, " %s\n", device->hdr.name);
253 sendit(msg, len, sp);
258 static void list_devices(STATUS_PKT *sp, char *name)
262 AUTOCHANGER *changer;
263 POOL_MEM msg(PM_MESSAGE);
266 len = Mmsg(msg, _("\nDevice status:\n"));
267 sendit(msg, len, sp);
270 foreach_res(changer, R_AUTOCHANGER) {
271 if (!name || strcmp(changer->hdr.name, name) == 0) {
272 list_one_autochanger(changer->hdr.name, changer, sp);
276 foreach_res(device, R_DEVICE) {
277 if (!name || strcmp(device->hdr.name, name) == 0) {
278 list_one_device(device->hdr.name, device->dev, sp);
281 if (!sp->api) sendit("====\n\n", 6, sp);
284 static void list_status_header(STATUS_PKT *sp)
286 char dt[MAX_TIME_LENGTH];
287 char b1[35], b2[35], b3[35], b4[35], b5[35];
288 POOL_MEM msg(PM_MESSAGE);
291 len = Mmsg(msg, _("%s %sVersion: %s (%s) %s %s %s\n"),
292 my_name, "", VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
293 sendit(msg, len, sp);
295 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
298 len = Mmsg(msg, _("Daemon started %s. Jobs: run=%d, running=%d.\n"),
299 dt, num_jobs_run, job_count());
300 sendit(msg, len, sp);
301 len = Mmsg(msg, _(" Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
302 edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
303 edit_uint64_with_commas(sm_bytes, b2),
304 edit_uint64_with_commas(sm_max_bytes, b3),
305 edit_uint64_with_commas(sm_buffers, b4),
306 edit_uint64_with_commas(sm_max_buffers, b5));
307 sendit(msg, len, sp);
308 len = Mmsg(msg, " Sizes: boffset_t=%d size_t=%d int32_t=%d int64_t=%d "
310 (int)sizeof(boffset_t), (int)sizeof(size_t), (int)sizeof(int32_t),
311 (int)sizeof(int64_t), (int)DEVELOPER_MODE, (int)0);
312 sendit(msg, len, sp);
316 static void send_blocked_status(DEVICE *dev, STATUS_PKT *sp)
318 POOL_MEM msg(PM_MESSAGE);
322 len = Mmsg(msg, _("No DEVICE structure.\n\n"));
323 sendit(msg, len, sp);
327 len = Mmsg(msg, _(" Device is disabled. User command.\n"));
328 sendit(msg, len, sp);
330 switch (dev->blocked()) {
332 len = Mmsg(msg, _(" Device is BLOCKED. User unmounted.\n"));
333 sendit(msg, len, sp);
335 case BST_UNMOUNTED_WAITING_FOR_SYSOP:
336 len = Mmsg(msg, _(" Device is BLOCKED. User unmounted during wait for media/mount.\n"));
337 sendit(msg, len, sp);
339 case BST_WAITING_FOR_SYSOP:
342 bool found_jcr = false;
345 foreach_dlist(dcr, dev->attached_dcrs) {
346 if (dcr->jcr->JobStatus == JS_WaitMount) {
347 len = Mmsg(msg, _(" Device is BLOCKED waiting for mount of volume \"%s\",\n"
349 " Media type: %s\n"),
353 sendit(msg, len, sp);
355 } else if (dcr->jcr->JobStatus == JS_WaitMedia) {
356 len = Mmsg(msg, _(" Device is BLOCKED waiting to create a volume for:\n"
358 " Media type: %s\n"),
361 sendit(msg, len, sp);
368 len = Mmsg(msg, _(" Device is BLOCKED waiting for media.\n"));
369 sendit(msg, len, sp);
373 case BST_DOING_ACQUIRE:
374 len = Mmsg(msg, _(" Device is being initialized.\n"));
375 sendit(msg, len, sp);
377 case BST_WRITING_LABEL:
378 len = Mmsg(msg, _(" Device is blocked labeling a Volume.\n"));
379 sendit(msg, len, sp);
384 /* Send autochanger slot status */
385 if (dev->is_autochanger()) {
386 if (dev->get_slot() > 0) {
387 len = Mmsg(msg, _(" Slot %d %s loaded in drive %d.\n"),
388 dev->get_slot(), dev->is_open()?"is": "was last", dev->drive_index);
389 sendit(msg, len, sp);
390 } else if (dev->get_slot() <= 0) {
391 len = Mmsg(msg, _(" Drive %d is not loaded.\n"), dev->drive_index);
392 sendit(msg, len, sp);
396 send_device_status(dev, sp);
400 void send_device_status(DEVICE *dev, STATUS_PKT *sp)
402 POOL_MEM msg(PM_MESSAGE);
408 len = Mmsg(msg, _("Configured device capabilities:\n"));
409 sendit(msg, len, sp);
410 len = Mmsg(msg, " %sEOF %sBSR %sBSF %sFSR %sFSF %sEOM %sREM %sRACCESS %sAUTOMOUNT %sLABEL %sANONVOLS %sALWAYSOPEN\n",
411 dev->capabilities & CAP_EOF ? "" : "!",
412 dev->capabilities & CAP_BSR ? "" : "!",
413 dev->capabilities & CAP_BSF ? "" : "!",
414 dev->capabilities & CAP_FSR ? "" : "!",
415 dev->capabilities & CAP_FSF ? "" : "!",
416 dev->capabilities & CAP_EOM ? "" : "!",
417 dev->capabilities & CAP_REM ? "" : "!",
418 dev->capabilities & CAP_RACCESS ? "" : "!",
419 dev->capabilities & CAP_AUTOMOUNT ? "" : "!",
420 dev->capabilities & CAP_LABEL ? "" : "!",
421 dev->capabilities & CAP_ANONVOLS ? "" : "!",
422 dev->capabilities & CAP_ALWAYSOPEN ? "" : "!");
423 sendit(msg, len, sp);
426 len = Mmsg(msg, _("Device state:\n"));
427 sendit(msg, len, sp);
428 len = Mmsg(msg, " %sOPENED %sTAPE %sLABEL %sMALLOC %sAPPEND %sREAD %sEOT %sWEOT %sEOF %sNEXTVOL %sSHORT %sMOUNTED\n",
429 dev->is_open() ? "" : "!",
430 dev->is_tape() ? "" : "!",
431 dev->is_labeled() ? "" : "!",
432 dev->state & ST_MALLOC ? "" : "!",
433 dev->can_append() ? "" : "!",
434 dev->can_read() ? "" : "!",
435 dev->at_eot() ? "" : "!",
436 dev->state & ST_WEOT ? "" : "!",
437 dev->at_eof() ? "" : "!",
438 dev->state & ST_NEXTVOL ? "" : "!",
439 dev->state & ST_SHORT ? "" : "!",
440 dev->state & ST_MOUNTED ? "" : "!");
441 sendit(msg, len, sp);
442 len = Mmsg(msg, _(" num_writers=%d reserves=%d block=%d enabled=%d\n"), dev->num_writers,
443 dev->num_reserved(), dev->blocked(), dev->enabled);
444 sendit(msg, len, sp);
446 len = Mmsg(msg, _("Attached JobIds: "));
447 sendit(msg, len, sp);
450 foreach_dlist(dcr, dev->attached_dcrs) {
455 len = Mmsg(msg, "%d", (int)dcr->jcr->JobId);
456 sendit(msg, len, sp);
464 len = Mmsg(msg, _("Device parameters:\n"));
465 sendit(msg, len, sp);
466 len = Mmsg(msg, _(" Archive name: %s Device name: %s\n"), dev->archive_name(),
468 sendit(msg, len, sp);
469 len = Mmsg(msg, _(" File=%u block=%u\n"), dev->file, dev->block_num);
470 sendit(msg, len, sp);
471 len = Mmsg(msg, _(" Min block=%u Max block=%u\n"), dev->min_block_size, dev->max_block_size);
472 sendit(msg, len, sp);
475 static void list_running_jobs(STATUS_PKT *sp)
478 uint64_t inst_bps, total_bps;
479 int inst_sec, total_sec;
482 char JobName[MAX_NAME_LENGTH];
483 char b1[50], b2[50], b3[50], b4[50];
485 POOL_MEM msg(PM_MESSAGE);
486 time_t now = time(NULL);
488 len = Mmsg(msg, _("\nRunning Jobs:\n"));
489 if (!sp->api) sendit(msg, len, sp);
492 if (jcr->JobStatus == JS_WaitFD) {
493 len = Mmsg(msg, _("%s Job %s waiting for Client connection.\n"),
494 job_type_to_str(jcr->getJobType()), jcr->Job);
495 sendit(msg, len, sp);
498 rdcr = jcr->read_dcr;
499 if ((dcr && dcr->device) || (rdcr && rdcr->device)) {
500 bstrncpy(JobName, jcr->Job, sizeof(JobName));
501 /* There are three periods after the Job name */
503 for (int i=0; i<3; i++) {
504 if ((p=strrchr(JobName, '.')) != NULL) {
508 if (rdcr && rdcr->device) {
509 len = Mmsg(msg, _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
510 " pool=\"%s\" device=%s\n"),
511 job_level_to_str(jcr->getJobLevel()),
512 job_type_to_str(jcr->getJobType()),
517 rdcr->dev?rdcr->dev->print_name():
518 rdcr->device->device_name);
519 sendit(msg, len, sp);
521 if (dcr && dcr->device && dcr != rdcr) {
522 len = Mmsg(msg, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
523 " pool=\"%s\" device=%s\n"),
524 job_level_to_str(jcr->getJobLevel()),
525 job_type_to_str(jcr->getJobType()),
530 dcr->dev?dcr->dev->print_name():
531 dcr->device->device_name);
532 sendit(msg, len, sp);
533 len= Mmsg(msg, _(" spooling=%d despooling=%d despool_wait=%d\n"),
534 dcr->spooling, dcr->despooling, dcr->despool_wait);
535 sendit(msg, len, sp);
537 if (jcr->last_time == 0) {
538 jcr->last_time = jcr->run_time;
540 total_sec = now - jcr->run_time;
541 inst_sec = now - jcr->last_time;
542 if (total_sec <= 0) {
548 /* Instanteous bps not smoothed */
549 inst_bps = (jcr->JobBytes - jcr->LastJobBytes) / inst_sec;
550 if (jcr->LastRate == 0) {
551 jcr->LastRate = inst_bps;
553 /* Smooth the instantaneous bps a bit */
554 inst_bps = (2 * jcr->LastRate + inst_bps) / 3;
555 /* total bps (AveBytes/sec) since start of job */
556 total_bps = jcr->JobBytes / total_sec;
557 len = Mmsg(msg, _(" Files=%s Bytes=%s AveBytes/sec=%s LastBytes/sec=%s\n"),
558 edit_uint64_with_commas(jcr->JobFiles, b1),
559 edit_uint64_with_commas(jcr->JobBytes, b2),
560 edit_uint64_with_commas(total_bps, b3),
561 edit_uint64_with_commas(inst_bps, b4));
562 sendit(msg, len, sp);
563 /* Update only every 10 seconds */
564 if (now - jcr->last_time > 10) {
565 jcr->LastRate = inst_bps;
566 jcr->LastJobBytes = jcr->JobBytes;
567 jcr->last_time = now;
571 if (jcr->file_bsock) {
572 len = Mmsg(msg, _(" FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
573 edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
574 jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
575 jcr->file_bsock->m_fd);
576 sendit(msg, len, sp);
578 len = Mmsg(msg, _(" FDSocket closed\n"));
579 sendit(msg, len, sp);
587 len = Mmsg(msg, _("No Jobs running.\n"));
588 if (!sp->api) sendit(msg, len, sp);
590 if (!sp->api) sendit("====\n", 5, sp);
593 static void list_jobs_waiting_on_reservation(STATUS_PKT *sp)
596 POOL_MEM msg(PM_MESSAGE);
599 len = Mmsg(msg, _("\nJobs waiting to reserve a drive:\n"));
600 if (!sp->api) sendit(msg, len, sp);
603 if (!jcr->reserve_msgs) {
606 send_drive_reserve_messages(jcr, sendit, sp);
610 if (!sp->api) sendit("====\n", 5, sp);
614 static void sendit(const char *msg, int len, void *sp)
616 sendit(msg, len, (STATUS_PKT *)sp);
619 static void sendit(POOL_MEM &msg, int len, STATUS_PKT *sp)
623 bs->msg = check_pool_memory_size(bs->msg, len+1);
624 memcpy(bs->msg, msg.c_str(), len+1);
628 sp->callback(msg.c_str(), len, sp->context);
632 static void dbg_sendit(const char *msg, int len, void *sp)
640 * Status command from Director
642 bool status_cmd(JCR *jcr)
644 BSOCK *dir = jcr->dir_bsock;
650 dir->signal(BNET_EOD);
655 * .status command from Director
657 bool qstatus_cmd(JCR *jcr)
659 BSOCK *dir = jcr->dir_bsock;
663 POOLMEM *args = get_pool_memory(PM_MESSAGE);
664 char *argk[MAX_CMD_ARGS]; /* argument keywords */
665 char *argv[MAX_CMD_ARGS]; /* argument values */
666 int argc; /* number of arguments */
674 parse_args(dir->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
676 /* .status xxxx at the minimum */
677 if (argc < 2 || strcmp(argk[0], ".status") != 0) {
678 pm_strcpy(jcr->errmsg, dir->msg);
679 dir->fsend(_("3900 No arg in .status command: %s\n"), jcr->errmsg);
680 dir->signal(BNET_EOD);
687 /* The status command can contain some arguments
689 * i=1 => [running | current | last | ... ]
691 for (int i=0 ; i < argc ; i++) {
692 if (!strcmp(argk[i], "device") && argv[i]) {
694 unbash_spaces(device);
696 } else if (!strcmp(argk[i], "api") && argv[i]) {
699 } else if (!strcmp(argk[i], "api_opts") && argv[i]) {
700 strncpy(sp.api_opts, argv[i], sizeof(sp.api_opts));;
704 Dmsg1(100, "cmd=%s\n", cmd);
706 if (strcasecmp(cmd, "current") == 0) {
707 dir->fsend(OKqstatus, cmd);
709 if (njcr->JobId != 0) {
710 dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
714 } else if (strcasecmp(cmd, "last") == 0) {
715 dir->fsend(OKqstatus, cmd);
716 if ((last_jobs) && (last_jobs->size() > 0)) {
717 job = (s_last_job*)last_jobs->last();
718 dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
720 } else if (strcasecmp(cmd, "header") == 0) {
722 list_status_header(&sp);
723 } else if (strcasecmp(cmd, "running") == 0) {
725 list_running_jobs(&sp);
726 } else if (strcasecmp(cmd, "waitreservation") == 0) {
728 list_jobs_waiting_on_reservation(&sp);
729 } else if (strcasecmp(cmd, "devices") == 0) {
731 list_devices(&sp, device);
732 } else if (strcasecmp(cmd, "volumes") == 0) {
734 list_volumes(sendit, &sp);
735 } else if (strcasecmp(cmd, "spooling") == 0) {
737 list_spool_stats(sendit, &sp);
738 } else if (strcasecmp(cmd, "terminated") == 0) {
740 list_terminated_jobs(&sp); /* defined in lib/status.h */
741 } else if (strcasecmp(cmd, "resources") == 0) {
745 pm_strcpy(jcr->errmsg, dir->msg);
746 dir->fsend(_("3900 Unknown arg in .status command: %s\n"), jcr->errmsg);
747 dir->signal(BNET_EOD);
750 dir->signal(BNET_EOD);
751 free_pool_memory(args);
755 static void list_plugins(STATUS_PKT *sp)
757 POOL_MEM msg(PM_MESSAGE);
758 if (b_plugin_list->size() > 0) {
761 pm_strcpy(msg, " Plugin: ");
762 foreach_alist(plugin, b_plugin_list) {
763 len = pm_strcat(msg, plugin->file);
764 /* Print plugin version when debug activated */
765 if (debug_level > 0 && plugin->pinfo) {
767 pm_strcat(msg, NPRT(sdplug_info(plugin)->plugin_version));
768 len = pm_strcat(msg, ")");
771 pm_strcat(msg, "\n ");
776 len = pm_strcat(msg, "\n");
777 sendit(msg.c_str(), len, sp);