2 Bacula® - The Network Backup Solution
4 Copyright (C) 2003-2014 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from many
7 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 Bacula® is a registered trademark of Kern Sibbald.
17 * This file handles the status command
19 * Kern Sibbald, May MMIII
26 #include "lib/status.h"
28 /* Imported functions */
30 /* Imported variables */
31 extern BSOCK *filed_chan;
32 extern void *start_heap;
34 /* Static variables */
35 static char qstatus[] = ".status %127s\n";
37 static char OKqstatus[] = "3000 OK .status\n";
38 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
41 /* Forward referenced functions */
42 static void sendit(const char *msg, int len, STATUS_PKT *sp);
43 static void sendit(POOL_MEM &msg, int len, STATUS_PKT *sp);
44 static void sendit(const char *msg, int len, void *arg);
46 static void send_blocked_status(DEVICE *dev, STATUS_PKT *sp);
47 static void send_device_status(DEVICE *dev, STATUS_PKT *sp);
48 static void list_terminated_jobs(STATUS_PKT *sp);
49 static void list_running_jobs(STATUS_PKT *sp);
50 static void list_jobs_waiting_on_reservation(STATUS_PKT *sp);
51 static void list_status_header(STATUS_PKT *sp);
52 static void list_devices(STATUS_PKT *sp);
55 * Status command from Director
57 void output_status(STATUS_PKT *sp)
59 POOL_MEM msg(PM_MESSAGE);
62 list_status_header(sp);
67 list_running_jobs(sp);
70 * List jobs stuck in reservation system
72 list_jobs_waiting_on_reservation(sp);
75 * List terminated jobs
77 list_terminated_jobs(sp);
85 len = Mmsg(msg, _("Used Volume status:\n"));
86 if (!sp->api) sendit(msg, len, sp);
88 list_volumes(sendit, (void *)sp);
89 if (!sp->api) sendit("====\n\n", 6, sp);
92 list_spool_stats(sendit, (void *)sp);
93 if (!sp->api) sendit("====\n\n", 6, sp);
97 static void list_resources(STATUS_PKT *sp)
100 POOL_MEM msg(PM_MESSAGE);
103 len = Mmsg(msg, _("\nSD Resources:\n"));
104 if (!sp->api) sendit(msg, len, sp);
105 dump_resource(R_DEVICE, resources[R_DEVICE-r_first], sp);
106 if (!sp->api) sendit("====\n\n", 6, sp);
111 static find_device(char *devname)
113 foreach_res(device, R_DEVICE) {
114 if (strcasecmp(device->hdr.name, devname) == 0) {
120 foreach_res(changer, R_AUTOCHANGER) {
121 if (strcasecmp(changer->hdr.name, devname) == 0) {
129 static void list_devices(STATUS_PKT *sp)
132 AUTOCHANGER *changer;
134 char b1[35], b2[35], b3[35];
135 POOL_MEM msg(PM_MESSAGE);
139 len = Mmsg(msg, _("\nDevice status:\n"));
140 if (!sp->api) sendit(msg, len, sp);
142 foreach_res(changer, R_AUTOCHANGER) {
143 len = Mmsg(msg, _("Autochanger \"%s\" with devices:\n"),
145 sendit(msg, len, sp);
147 foreach_alist(device, changer->device) {
149 len = Mmsg(msg, " %s\n", device->dev->print_name());
150 sendit(msg, len, sp);
152 len = Mmsg(msg, " %s\n", device->hdr.name);
153 sendit(msg, len, sp);
159 foreach_res(device, R_DEVICE) {
161 if (dev && dev->is_open()) {
162 if (dev->is_labeled()) {
163 len = Mmsg(msg, _("\nDevice %s is %s:\n"
166 " Media type: %s\n"),
168 dev->blocked()?_("waiting for"):_("mounted with"),
169 dev->VolHdr.VolumeName,
170 dev->pool_name[0]?dev->pool_name:_("*unknown*"),
171 dev->device->media_type);
172 sendit(msg, len, sp);
174 len = Mmsg(msg, _("\nDevice %s open but no Bacula volume is currently mounted.\n"),
176 sendit(msg, len, sp);
178 send_blocked_status(dev, sp);
179 if (dev->can_append()) {
180 bpb = dev->VolCatInfo.VolCatBlocks;
184 bpb = dev->VolCatInfo.VolCatBytes / bpb;
185 len = Mmsg(msg, _(" Total Bytes=%s Blocks=%s Bytes/block=%s\n"),
186 edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, b1),
187 edit_uint64_with_commas(dev->VolCatInfo.VolCatBlocks, b2),
188 edit_uint64_with_commas(bpb, b3));
189 sendit(msg, len, sp);
190 } else { /* reading */
191 bpb = dev->VolCatInfo.VolCatReads;
195 if (dev->VolCatInfo.VolCatRBytes > 0) {
196 bpb = dev->VolCatInfo.VolCatRBytes / bpb;
200 len = Mmsg(msg, _(" Total Bytes Read=%s Blocks Read=%s Bytes/block=%s\n"),
201 edit_uint64_with_commas(dev->VolCatInfo.VolCatRBytes, b1),
202 edit_uint64_with_commas(dev->VolCatInfo.VolCatReads, b2),
203 edit_uint64_with_commas(bpb, b3));
204 sendit(msg, len, sp);
206 len = Mmsg(msg, _(" Positioned at File=%s Block=%s\n"),
207 edit_uint64_with_commas(dev->file, b1),
208 edit_uint64_with_commas(dev->block_num, b2));
209 sendit(msg, len, sp);
213 len = Mmsg(msg, _("\nDevice %s is not open.\n"), dev->print_name());
214 sendit(msg, len, sp);
215 send_blocked_status(dev, sp);
217 len = Mmsg(msg, _("\nDevice \"%s\" is not open or does not exist.\n"), device->hdr.name);
218 sendit(msg, len, sp);
222 if (!sp->api) sendit("==\n", 4, sp);
224 if (!sp->api) sendit("====\n\n", 6, sp);
227 static void list_status_header(STATUS_PKT *sp)
229 char dt[MAX_TIME_LENGTH];
230 char b1[35], b2[35], b3[35], b4[35], b5[35];
231 POOL_MEM msg(PM_MESSAGE);
234 len = Mmsg(msg, _("%s Version: %s (%s) %s %s %s\n"),
235 my_name, VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
236 sendit(msg, len, sp);
238 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
241 len = Mmsg(msg, _("Daemon started %s. Jobs: run=%d, running=%d.\n"),
242 dt, num_jobs_run, job_count());
243 sendit(msg, len, sp);
244 len = Mmsg(msg, _(" Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
245 edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
246 edit_uint64_with_commas(sm_bytes, b2),
247 edit_uint64_with_commas(sm_max_bytes, b3),
248 edit_uint64_with_commas(sm_buffers, b4),
249 edit_uint64_with_commas(sm_max_buffers, b5));
250 sendit(msg, len, sp);
251 len = Mmsg(msg, " Sizes: boffset_t=%d size_t=%d int32_t=%d int64_t=%d "
253 (int)sizeof(boffset_t), (int)sizeof(size_t), (int)sizeof(int32_t),
254 (int)sizeof(int64_t), (int)DEVELOPER_MODE, (int)BEEF);
255 sendit(msg, len, sp);
256 if (bplugin_list->size() > 0) {
259 pm_strcpy(msg, " Plugin: ");
260 foreach_alist(plugin, bplugin_list) {
261 len = pm_strcat(msg, plugin->file);
263 pm_strcat(msg, "\n ");
268 len = pm_strcat(msg, "\n");
269 sendit(msg.c_str(), len, sp);
273 static void send_blocked_status(DEVICE *dev, STATUS_PKT *sp)
275 POOL_MEM msg(PM_MESSAGE);
279 len = Mmsg(msg, _("No DEVICE structure.\n\n"));
280 sendit(msg, len, sp);
283 switch (dev->blocked()) {
285 len = Mmsg(msg, _(" Device is BLOCKED. User unmounted.\n"));
286 sendit(msg, len, sp);
288 case BST_UNMOUNTED_WAITING_FOR_SYSOP:
289 len = Mmsg(msg, _(" Device is BLOCKED. User unmounted during wait for media/mount.\n"));
290 sendit(msg, len, sp);
292 case BST_WAITING_FOR_SYSOP:
295 bool found_jcr = false;
298 foreach_dlist(dcr, dev->attached_dcrs) {
299 if (dcr->jcr->JobStatus == JS_WaitMount) {
300 len = Mmsg(msg, _(" Device is BLOCKED waiting for mount of volume \"%s\",\n"
302 " Media type: %s\n"),
306 sendit(msg, len, sp);
308 } else if (dcr->jcr->JobStatus == JS_WaitMedia) {
309 len = Mmsg(msg, _(" Device is BLOCKED waiting to create a volume for:\n"
311 " Media type: %s\n"),
314 sendit(msg, len, sp);
321 len = Mmsg(msg, _(" Device is BLOCKED waiting for media.\n"));
322 sendit(msg, len, sp);
326 case BST_DOING_ACQUIRE:
327 len = Mmsg(msg, _(" Device is being initialized.\n"));
328 sendit(msg, len, sp);
330 case BST_WRITING_LABEL:
331 len = Mmsg(msg, _(" Device is blocked labeling a Volume.\n"));
332 sendit(msg, len, sp);
337 /* Send autochanger slot status */
338 if (dev->is_autochanger()) {
339 if (dev->get_slot() > 0) {
340 len = Mmsg(msg, _(" Slot %d %s loaded in drive %d.\n"),
341 dev->get_slot(), dev->is_open()?"is": "was last", dev->drive_index);
342 sendit(msg, len, sp);
343 } else if (dev->get_slot() <= 0) {
344 len = Mmsg(msg, _(" Drive %d is not loaded.\n"), dev->drive_index);
345 sendit(msg, len, sp);
349 send_device_status(dev, sp);
353 static void send_device_status(DEVICE *dev, STATUS_PKT *sp)
355 POOL_MEM msg(PM_MESSAGE);
361 len = Mmsg(msg, _("Configured device capabilities:\n"));
362 sendit(msg, len, sp);
363 len = Mmsg(msg, " %sEOF %sBSR %sBSF %sFSR %sFSF %sEOM %sREM %sRACCESS %sAUTOMOUNT %sLABEL %sANONVOLS %sALWAYSOPEN\n",
364 dev->capabilities & CAP_EOF ? "" : "!",
365 dev->capabilities & CAP_BSR ? "" : "!",
366 dev->capabilities & CAP_BSF ? "" : "!",
367 dev->capabilities & CAP_FSR ? "" : "!",
368 dev->capabilities & CAP_FSF ? "" : "!",
369 dev->capabilities & CAP_EOM ? "" : "!",
370 dev->capabilities & CAP_REM ? "" : "!",
371 dev->capabilities & CAP_RACCESS ? "" : "!",
372 dev->capabilities & CAP_AUTOMOUNT ? "" : "!",
373 dev->capabilities & CAP_LABEL ? "" : "!",
374 dev->capabilities & CAP_ANONVOLS ? "" : "!",
375 dev->capabilities & CAP_ALWAYSOPEN ? "" : "!");
376 sendit(msg, len, sp);
379 len = Mmsg(msg, _("Device state:\n"));
380 sendit(msg, len, sp);
381 len = Mmsg(msg, " %sOPENED %sTAPE %sLABEL %sMALLOC %sAPPEND %sREAD %sEOT %sWEOT %sEOF %sNEXTVOL %sSHORT %sMOUNTED\n",
382 dev->is_open() ? "" : "!",
383 dev->is_tape() ? "" : "!",
384 dev->is_labeled() ? "" : "!",
385 dev->state & ST_MALLOC ? "" : "!",
386 dev->can_append() ? "" : "!",
387 dev->can_read() ? "" : "!",
388 dev->at_eot() ? "" : "!",
389 dev->state & ST_WEOT ? "" : "!",
390 dev->at_eof() ? "" : "!",
391 dev->state & ST_NEXTVOL ? "" : "!",
392 dev->state & ST_SHORT ? "" : "!",
393 dev->state & ST_MOUNTED ? "" : "!");
394 sendit(msg, len, sp);
395 len = Mmsg(msg, _(" num_writers=%d reserves=%d block=%d\n"), dev->num_writers,
396 dev->num_reserved(), dev->blocked());
397 sendit(msg, len, sp);
399 len = Mmsg(msg, _("Attached JobsIds: "));
400 sendit(msg, len, sp);
403 foreach_dlist(dcr, dev->attached_dcrs) {
408 len = Mmsg(msg, "%d", (int)dcr->jcr->JobId);
409 sendit(msg, len, sp);
417 len = Mmsg(msg, _("Device parameters:\n"));
418 sendit(msg, len, sp);
419 len = Mmsg(msg, _(" Archive name: %s Device name: %s\n"), dev->archive_name(),
421 sendit(msg, len, sp);
422 len = Mmsg(msg, _(" File=%u block=%u\n"), dev->file, dev->block_num);
423 sendit(msg, len, sp);
424 len = Mmsg(msg, _(" Min block=%u Max block=%u\n"), dev->min_block_size, dev->max_block_size);
425 sendit(msg, len, sp);
428 static void list_running_jobs(STATUS_PKT *sp)
431 int avebps, bps, sec;
434 char JobName[MAX_NAME_LENGTH];
435 char b1[50], b2[50], b3[50], b4[50];
437 POOL_MEM msg(PM_MESSAGE);
438 time_t now = time(NULL);
440 len = Mmsg(msg, _("\nRunning Jobs:\n"));
441 if (!sp->api) sendit(msg, len, sp);
444 if (jcr->JobStatus == JS_WaitFD) {
445 len = Mmsg(msg, _("%s Job %s waiting for Client connection.\n"),
446 job_type_to_str(jcr->getJobType()), jcr->Job);
447 sendit(msg, len, sp);
450 rdcr = jcr->read_dcr;
451 if ((dcr && dcr->device) || (rdcr && rdcr->device)) {
452 bstrncpy(JobName, jcr->Job, sizeof(JobName));
453 /* There are three periods after the Job name */
455 for (int i=0; i<3; i++) {
456 if ((p=strrchr(JobName, '.')) != NULL) {
460 if (rdcr && rdcr->device) {
461 len = Mmsg(msg, _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
462 " pool=\"%s\" device=%s\n"),
463 job_level_to_str(jcr->getJobLevel()),
464 job_type_to_str(jcr->getJobType()),
469 rdcr->dev?rdcr->dev->print_name():
470 rdcr->device->device_name);
471 sendit(msg, len, sp);
473 if (dcr && dcr->device) {
474 len = Mmsg(msg, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
475 " pool=\"%s\" device=%s\n"),
476 job_level_to_str(jcr->getJobLevel()),
477 job_type_to_str(jcr->getJobType()),
482 dcr->dev?dcr->dev->print_name():
483 dcr->device->device_name);
484 sendit(msg, len, sp);
485 len= Mmsg(msg, _(" spooling=%d despooling=%d despool_wait=%d\n"),
486 dcr->spooling, dcr->despooling, dcr->despool_wait);
487 sendit(msg, len, sp);
489 if (jcr->last_time == 0) {
490 jcr->last_time = jcr->run_time;
492 sec = now - jcr->last_time;
496 bps = (jcr->JobBytes - jcr->LastJobBytes) / sec;
497 if (jcr->LastRate == 0) {
500 avebps = (jcr->LastRate + bps) / 2;
501 len = Mmsg(msg, _(" Files=%s Bytes=%s AveBytes/sec=%s LastBytes/sec=%s\n"),
502 edit_uint64_with_commas(jcr->JobFiles, b1),
503 edit_uint64_with_commas(jcr->JobBytes, b2),
504 edit_uint64_with_commas(avebps, b3),
505 edit_uint64_with_commas(bps, b4));
506 sendit(msg, len, sp);
507 jcr->LastRate = avebps;
508 jcr->LastJobBytes = jcr->JobBytes;
509 jcr->last_time = now;
512 if (jcr->file_bsock) {
513 len = Mmsg(msg, _(" FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
514 edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
515 jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
516 jcr->file_bsock->m_fd);
517 sendit(msg, len, sp);
519 len = Mmsg(msg, _(" FDSocket closed\n"));
520 sendit(msg, len, sp);
528 len = Mmsg(msg, _("No Jobs running.\n"));
529 if (!sp->api) sendit(msg, len, sp);
531 if (!sp->api) sendit("====\n", 5, sp);
534 static void list_jobs_waiting_on_reservation(STATUS_PKT *sp)
537 POOL_MEM msg(PM_MESSAGE);
540 len = Mmsg(msg, _("\nJobs waiting to reserve a drive:\n"));
541 if (!sp->api) sendit(msg, len, sp);
544 if (!jcr->reserve_msgs) {
547 send_drive_reserve_messages(jcr, sendit, sp);
551 if (!sp->api) sendit("====\n", 5, sp);
557 static void sendit(const char *msg, int len, void *sp)
559 sendit(msg, len, (STATUS_PKT *)sp);
562 static void sendit(POOL_MEM &msg, int len, STATUS_PKT *sp)
566 memcpy(bs->msg, msg.c_str(), len+1);
570 sp->callback(msg.c_str(), len, sp->context);
576 * Status command from Director
578 bool status_cmd(JCR *jcr)
580 BSOCK *dir = jcr->dir_bsock;
586 dir->signal(BNET_EOD);
591 * .status command from Director
593 bool qstatus_cmd(JCR *jcr)
595 BSOCK *dir = jcr->dir_bsock;
602 if (sscanf(dir->msg, qstatus, cmd.c_str()) != 1) {
603 pm_strcpy(jcr->errmsg, dir->msg);
604 dir->fsend(_("3900 No arg in .status command: %s\n"), jcr->errmsg);
605 dir->signal(BNET_EOD);
610 Dmsg1(200, "cmd=%s\n", cmd.c_str());
612 if (strcasecmp(cmd.c_str(), "current") == 0) {
613 dir->fsend(OKqstatus, cmd.c_str());
615 if (njcr->JobId != 0) {
616 dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
620 } else if (strcasecmp(cmd.c_str(), "last") == 0) {
621 dir->fsend(OKqstatus, cmd.c_str());
622 if ((last_jobs) && (last_jobs->size() > 0)) {
623 job = (s_last_job*)last_jobs->last();
624 dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
626 } else if (strcasecmp(cmd.c_str(), "header") == 0) {
628 list_status_header(&sp);
629 } else if (strcasecmp(cmd.c_str(), "running") == 0) {
631 list_running_jobs(&sp);
632 } else if (strcasecmp(cmd.c_str(), "waitreservation") == 0) {
634 list_jobs_waiting_on_reservation(&sp);
635 } else if (strcasecmp(cmd.c_str(), "devices") == 0) {
638 } else if (strcasecmp(cmd.c_str(), "volumes") == 0) {
640 list_volumes(sendit, &sp);
641 } else if (strcasecmp(cmd.c_str(), "spooling") == 0) {
643 list_spool_stats(sendit, &sp);
644 } else if (strcasecmp(cmd.c_str(), "terminated") == 0) {
646 list_terminated_jobs(&sp);
647 } else if (strcasecmp(cmd.c_str(), "resources") == 0) {
651 pm_strcpy(jcr->errmsg, dir->msg);
652 dir->fsend(_("3900 Unknown arg in .status command: %s\n"), jcr->errmsg);
653 dir->signal(BNET_EOD);
656 dir->signal(BNET_EOD);
660 #if defined(HAVE_WIN32)
663 /* Return a one line status for the tray monitor */
664 char *bac_status(char *buf, int buf_len)
667 const char *termstat = _("Bacula Storage: Idle");
668 struct s_last_job *job;
669 int stat = 0; /* Idle */
674 Dmsg0(1000, "Begin bac_status jcr loop.\n");
676 if (njcr->JobId != 0) {
678 termstat = _("Bacula Storage: Running");
687 if (last_jobs->size() > 0) {
688 job = (struct s_last_job *)last_jobs->last();
689 stat = job->JobStatus;
690 switch (job->JobStatus) {
692 termstat = _("Bacula Storage: Last Job Canceled");
694 case JS_ErrorTerminated:
696 termstat = _("Bacula Storage: Last Job Failed");
700 termstat = _("Bacula Storage: Last Job had Warnings");
705 Dmsg0(1000, "End bac_status jcr loop.\n");
709 bstrncpy(buf, termstat, buf_len);
714 #endif /* HAVE_WIN32 */