2 Bacula® - The Network Backup Solution
4 Copyright (C) 2003-2008 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of John Walker.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
29 * This file handles the status command
31 * Kern Sibbald, May MMIII
39 #include "lib/status.h"
41 /* Exported variables */
43 /* Imported variables */
44 extern BSOCK *filed_chan;
45 extern int r_first, r_last;
46 extern struct s_res resources[];
47 extern void *start_heap;
49 /* Static variables */
50 static char qstatus[] = ".status %127s\n";
52 static char OKqstatus[] = "3000 OK .status\n";
53 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
56 /* Forward referenced functions */
57 static void sendit(const char *msg, int len, STATUS_PKT *sp);
58 static void sendit(POOL_MEM &msg, int len, STATUS_PKT *sp);
59 static void sendit(const char *msg, int len, void *arg);
61 static void send_blocked_status(DEVICE *dev, STATUS_PKT *sp);
62 static void send_device_status(DEVICE *dev, STATUS_PKT *sp);
63 static void list_terminated_jobs(STATUS_PKT *sp);
64 static void list_running_jobs(STATUS_PKT *sp);
65 static void list_jobs_waiting_on_reservation(STATUS_PKT *sp);
66 static void list_status_header(STATUS_PKT *sp);
67 static void list_devices(STATUS_PKT *sp);
69 static const char *level_to_str(int level);
72 * Status command from Director
74 void output_status(STATUS_PKT *sp)
76 POOL_MEM msg(PM_MESSAGE);
79 list_status_header(sp);
84 list_running_jobs(sp);
87 * List jobs stuck in reservation system
89 list_jobs_waiting_on_reservation(sp);
92 * List terminated jobs
94 list_terminated_jobs(sp);
102 len = Mmsg(msg, _("In Use Volume status:\n"));
103 if (!sp->api) sendit(msg, len, sp);
105 list_volumes(sendit, (void *)sp);
106 if (!sp->api) sendit("====\n\n", 6, sp);
109 if (debug_level > 10) {
110 bs->fsend(_("====\n\n"));
111 dump_resource(R_DEVICE, resources[R_DEVICE-r_first].res_head, sendit, user);
112 bs->fsend(_("====\n\n"));
116 list_spool_stats(sendit, (void *)sp);
117 if (!sp->api) sendit("====\n\n", 6, sp);
121 static void list_devices(STATUS_PKT *sp)
124 AUTOCHANGER *changer;
126 char b1[35], b2[35], b3[35];
127 POOL_MEM msg(PM_MESSAGE);
131 len = Mmsg(msg, _("\nDevice status:\n"));
132 if (!sp->api) sendit(msg, len, sp);
134 foreach_res(changer, R_AUTOCHANGER) {
135 len = Mmsg(msg, _("Autochanger \"%s\" with devices:\n"),
137 sendit(msg, len, sp);
139 foreach_alist(device, changer->device) {
141 len = Mmsg(msg, " %s\n", device->dev->print_name());
142 sendit(msg, len, sp);
144 len = Mmsg(msg, " %s\n", device->hdr.name);
145 sendit(msg, len, sp);
149 foreach_res(device, R_DEVICE) {
151 if (dev && dev->is_open()) {
152 if (dev->is_labeled()) {
153 len = Mmsg(msg, _("Device %s is mounted with:\n"
156 " Media type: %s\n"),
158 dev->VolHdr.VolumeName,
159 dev->pool_name[0]?dev->pool_name:"*unknown*",
160 dev->device->media_type);
161 sendit(msg, len, sp);
163 len = Mmsg(msg, _("Device %s open but no Bacula volume is currently mounted.\n"),
165 sendit(msg, len, sp);
167 send_blocked_status(dev, sp);
168 if (dev->can_append()) {
169 bpb = dev->VolCatInfo.VolCatBlocks;
173 bpb = dev->VolCatInfo.VolCatBytes / bpb;
174 len = Mmsg(msg, _(" Total Bytes=%s Blocks=%s Bytes/block=%s\n"),
175 edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, b1),
176 edit_uint64_with_commas(dev->VolCatInfo.VolCatBlocks, b2),
177 edit_uint64_with_commas(bpb, b3));
178 sendit(msg, len, sp);
179 } else { /* reading */
180 bpb = dev->VolCatInfo.VolCatReads;
184 if (dev->VolCatInfo.VolCatRBytes > 0) {
185 bpb = dev->VolCatInfo.VolCatRBytes / bpb;
189 len = Mmsg(msg, _(" Total Bytes Read=%s Blocks Read=%s Bytes/block=%s\n"),
190 edit_uint64_with_commas(dev->VolCatInfo.VolCatRBytes, b1),
191 edit_uint64_with_commas(dev->VolCatInfo.VolCatReads, b2),
192 edit_uint64_with_commas(bpb, b3));
193 sendit(msg, len, sp);
195 len = Mmsg(msg, _(" Positioned at File=%s Block=%s\n"),
196 edit_uint64_with_commas(dev->file, b1),
197 edit_uint64_with_commas(dev->block_num, b2));
198 sendit(msg, len, sp);
202 len = Mmsg(msg, _("Device %s is not open.\n"), dev->print_name());
203 sendit(msg, len, sp);
204 send_blocked_status(dev, sp);
206 len = Mmsg(msg, _("Device \"%s\" is not open or does not exist.\n"), device->hdr.name);
207 sendit(msg, len, sp);
211 if (!sp->api) sendit("====\n\n", 6, sp);
214 static void list_status_header(STATUS_PKT *sp)
216 char dt[MAX_TIME_LENGTH];
217 char b1[35], b2[35], b3[35], b4[35], b5[35];
218 POOL_MEM msg(PM_MESSAGE);
221 len = Mmsg(msg, _("%s Version: %s (%s) %s %s %s\n"),
222 my_name, VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
223 sendit(msg, len, sp);
225 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
228 len = Mmsg(msg, _("Daemon started %s, %d Job%s run since started.\n"),
229 dt, num_jobs_run, num_jobs_run == 1 ? "" : "s");
230 sendit(msg, len, sp);
232 len = Mmsg(msg, _(" Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
233 edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
234 edit_uint64_with_commas(sm_bytes, b2),
235 edit_uint64_with_commas(sm_max_bytes, b3),
236 edit_uint64_with_commas(sm_buffers, b4),
237 edit_uint64_with_commas(sm_max_buffers, b5));
238 sendit(msg, len, sp);
239 len = Mmsg(msg, "Sizes: boffset_t=%d size_t=%d int32_t=%d int64_t=%d\n",
240 (int)sizeof(boffset_t), (int)sizeof(size_t), (int)sizeof(int32_t),
241 (int)sizeof(int64_t));
242 sendit(msg, len, sp);
245 static void send_blocked_status(DEVICE *dev, STATUS_PKT *sp)
247 POOL_MEM msg(PM_MESSAGE);
251 len = Mmsg(msg, _("No DEVICE structure.\n\n"));
252 sendit(msg, len, sp);
255 switch (dev->blocked()) {
257 len = Mmsg(msg, _(" Device is BLOCKED. User unmounted.\n"));
258 sendit(msg, len, sp);
260 case BST_UNMOUNTED_WAITING_FOR_SYSOP:
261 len = Mmsg(msg, _(" Device is BLOCKED. User unmounted during wait for media/mount.\n"));
262 sendit(msg, len, sp);
264 case BST_WAITING_FOR_SYSOP:
266 dlist *dcrs = dev->attached_dcrs;
267 bool found_jcr = false;
271 for (dcr = (DCR *)dcrs->first(); dcr != NULL; dcr = (DCR *)dcrs->next(dcr)) {
272 if (dcr->jcr->JobStatus == JS_WaitMount) {
273 len = Mmsg(msg, _(" Device is BLOCKED waiting for mount of volume \"%s\",\n"
275 " Media type: %s\n"),
279 sendit(msg, len, sp);
281 } else if (dcr->jcr->JobStatus == JS_WaitMedia) {
282 len = Mmsg(msg, _(" Device is BLOCKED waiting to create a volume for:\n"
284 " Media type: %s\n"),
287 sendit(msg, len, sp);
294 len = Mmsg(msg, _(" Device is BLOCKED waiting for media.\n"));
295 sendit(msg, len, sp);
299 case BST_DOING_ACQUIRE:
300 len = Mmsg(msg, _(" Device is being initialized.\n"));
301 sendit(msg, len, sp);
303 case BST_WRITING_LABEL:
304 len = Mmsg(msg, _(" Device is blocked labeling a Volume.\n"));
305 sendit(msg, len, sp);
310 /* Send autochanger slot status */
311 if (dev->is_autochanger()) {
313 len = Mmsg(msg, _(" Slot %d is loaded in drive %d.\n"),
314 dev->Slot, dev->drive_index);
315 sendit(msg, len, sp);
316 } else if (dev->Slot == 0) {
317 len = Mmsg(msg, _(" Drive %d is not loaded.\n"), dev->drive_index);
318 sendit(msg, len, sp);
320 len = Mmsg(msg, _(" Drive %d status unknown.\n"), dev->drive_index);
321 sendit(msg, len, sp);
324 if (debug_level > 1) {
325 send_device_status(dev, sp);
329 static void send_device_status(DEVICE *dev, STATUS_PKT *sp)
331 POOL_MEM msg(PM_MESSAGE);
334 len = Mmsg(msg, _("Configured device capabilities:\n"));
335 sendit(msg, len, sp);
337 len = Mmsg(msg, "%sEOF %sBSR %sBSF %sFSR %sFSF %sEOM %sREM %sRACCESS %sAUTOMOUNT %sLABEL %sANONVOLS %sALWAYSOPEN\n",
338 dev->capabilities & CAP_EOF ? "" : "!",
339 dev->capabilities & CAP_BSR ? "" : "!",
340 dev->capabilities & CAP_BSF ? "" : "!",
341 dev->capabilities & CAP_FSR ? "" : "!",
342 dev->capabilities & CAP_FSF ? "" : "!",
343 dev->capabilities & CAP_EOM ? "" : "!",
344 dev->capabilities & CAP_REM ? "" : "!",
345 dev->capabilities & CAP_RACCESS ? "" : "!",
346 dev->capabilities & CAP_AUTOMOUNT ? "" : "!",
347 dev->capabilities & CAP_LABEL ? "" : "!",
348 dev->capabilities & CAP_ANONVOLS ? "" : "!",
349 dev->capabilities & CAP_ALWAYSOPEN ? "" : "!");
350 sendit(msg, len, sp);
352 len = Mmsg(msg, _("Device state:\n"));
353 sendit(msg, len, sp);
355 len = Mmsg(msg, "%sOPENED %sTAPE %sLABEL %sMALLOC %sAPPEND %sREAD %sEOT %sWEOT %sEOF %sNEXTVOL %sSHORT %sMOUNTED\n",
356 dev->is_open() ? "" : "!",
357 dev->is_tape() ? "" : "!",
358 dev->is_labeled() ? "" : "!",
359 dev->state & ST_MALLOC ? "" : "!",
360 dev->can_append() ? "" : "!",
361 dev->can_read() ? "" : "!",
362 dev->at_eot() ? "" : "!",
363 dev->state & ST_WEOT ? "" : "!",
364 dev->at_eof() ? "" : "!",
365 dev->state & ST_NEXTVOL ? "" : "!",
366 dev->state & ST_SHORT ? "" : "!",
367 dev->state & ST_MOUNTED ? "" : "!");
368 sendit(msg, len, sp);
370 len = Mmsg(msg, _("num_writers=%d block=%d\n\n"), dev->num_writers, dev->blocked());
371 sendit(msg, len, sp);
373 len = Mmsg(msg, _("Device parameters:\n"));
374 sendit(msg, len, sp);
376 len = Mmsg(msg, _("Archive name: %s Device name: %s\n"), dev->archive_name(),
378 sendit(msg, len, sp);
380 len = Mmsg(msg, _("File=%u block=%u\n"), dev->file, dev->block_num);
381 sendit(msg, len, sp);
383 len = Mmsg(msg, _("Min block=%u Max block=%u\n"), dev->min_block_size, dev->max_block_size);
384 sendit(msg, len, sp);
387 static void list_running_jobs(STATUS_PKT *sp)
393 char JobName[MAX_NAME_LENGTH];
394 char b1[30], b2[30], b3[30];
396 POOL_MEM msg(PM_MESSAGE);
398 len = Mmsg(msg, _("\nRunning Jobs:\n"));
399 if (!sp->api) sendit(msg, len, sp);
402 if (jcr->JobStatus == JS_WaitFD) {
403 len = Mmsg(msg, _("%s Job %s waiting for Client connection.\n"),
404 job_type_to_str(jcr->JobType), jcr->Job);
405 sendit(msg, len, sp);
408 rdcr = jcr->read_dcr;
409 if ((dcr && dcr->device) || rdcr && rdcr->device) {
410 bstrncpy(JobName, jcr->Job, sizeof(JobName));
411 /* There are three periods after the Job name */
413 for (int i=0; i<3; i++) {
414 if ((p=strrchr(JobName, '.')) != NULL) {
418 if (rdcr && rdcr->device) {
419 len = Mmsg(msg, _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
420 " pool=\"%s\" device=%s\n"),
421 job_level_to_str(jcr->JobLevel),
422 job_type_to_str(jcr->JobType),
427 rdcr->dev?rdcr->dev->print_name():
428 rdcr->device->device_name);
429 sendit(msg, len, sp);
431 if (dcr && dcr->device) {
432 len = Mmsg(msg, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
433 " pool=\"%s\" device=%s\n"),
434 job_level_to_str(jcr->JobLevel),
435 job_type_to_str(jcr->JobType),
440 dcr->dev?dcr->dev->print_name():
441 dcr->device->device_name);
442 sendit(msg, len, sp);
443 len= Mmsg(msg, _(" spooling=%d despooling=%d despool_wait=%d\n"),
444 dcr->spooling, dcr->despooling, dcr->despool_wait);
445 sendit(msg, len, sp);
447 sec = time(NULL) - jcr->run_time;
451 bps = jcr->JobBytes / sec;
452 len = Mmsg(msg, _(" Files=%s Bytes=%s Bytes/sec=%s\n"),
453 edit_uint64_with_commas(jcr->JobFiles, b1),
454 edit_uint64_with_commas(jcr->JobBytes, b2),
455 edit_uint64_with_commas(bps, b3));
456 sendit(msg, len, sp);
459 if (jcr->file_bsock) {
460 len = Mmsg(msg, _(" FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
461 edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
462 jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
463 jcr->file_bsock->m_fd);
464 sendit(msg, len, sp);
466 len = Mmsg(msg, _(" FDSocket closed\n"));
467 sendit(msg, len, sp);
475 len = Mmsg(msg, _("No Jobs running.\n"));
476 if (!sp->api) sendit(msg, len, sp);
478 if (!sp->api) sendit("====\n", 5, sp);
481 static void list_jobs_waiting_on_reservation(STATUS_PKT *sp)
484 POOL_MEM msg(PM_MESSAGE);
487 len = Mmsg(msg, _("\nJobs waiting to reserve a drive:\n"));
488 if (!sp->api) sendit(msg, len, sp);
491 if (!jcr->reserve_msgs) {
494 send_drive_reserve_messages(jcr, sendit, sp);
498 if (!sp->api) sendit("====\n", 5, sp);
502 static void list_terminated_jobs(STATUS_PKT *sp)
504 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
506 struct s_last_job *je;
509 msg = _("\nTerminated Jobs:\n");
510 if (!sp->api) sendit(msg, strlen(msg), sp);
511 if (last_jobs->size() == 0) {
512 if (!sp->api) sendit("====\n", 5, sp);
515 lock_last_jobs_list();
516 msg = _(" JobId Level Files Bytes Status Finished Name \n");
517 if (!sp->api) sendit(msg, strlen(msg), sp);
518 msg = _("===================================================================\n");
519 if (!sp->api) sendit(msg, strlen(msg), sp);
520 foreach_dlist(je, last_jobs) {
521 char JobName[MAX_NAME_LENGTH];
522 const char *termstat;
525 bstrftime_nc(dt, sizeof(dt), je->end_time);
526 switch (je->JobType) {
529 bstrncpy(level, " ", sizeof(level));
532 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
536 switch (je->JobStatus) {
538 termstat = _("Created");
541 case JS_ErrorTerminated:
542 termstat = _("Error");
545 termstat = _("Diffs");
548 termstat = _("Cancel");
554 termstat = _("Other");
557 bstrncpy(JobName, je->Job, sizeof(JobName));
558 /* There are three periods after the Job name */
560 for (int i=0; i<3; i++) {
561 if ((p=strrchr(JobName, '.')) != NULL) {
566 bsnprintf(buf, sizeof(buf), _("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"),
569 edit_uint64_with_commas(je->JobFiles, b1),
570 edit_uint64_with_suffix(je->JobBytes, b2),
574 bsnprintf(buf, sizeof(buf), _("%6d %-6s %8s %10s %-7s %-8s %s\n"),
577 edit_uint64_with_commas(je->JobFiles, b1),
578 edit_uint64_with_suffix(je->JobBytes, b2),
582 sendit(buf, strlen(buf), sp);
584 unlock_last_jobs_list();
585 if (!sp->api) sendit("====\n", 5, sp);
589 * Convert Job Level into a string
591 static const char *level_to_str(int level)
602 str = _("Incremental");
605 str = _("Differential");
610 case L_VERIFY_CATALOG:
611 str = _("Verify Catalog");
614 str = _("Init Catalog");
616 case L_VERIFY_VOLUME_TO_CATALOG:
617 str = _("Volume to Catalog");
619 case L_VERIFY_DISK_TO_CATALOG:
620 str = _("Disk to Catalog");
629 str = _("Unknown Job Level");
638 static void sendit(const char *msg, int len, STATUS_PKT *sp)
642 memcpy(bs->msg, msg, len+1);
646 sp->callback(msg, len, sp->context);
650 static void sendit(const char *msg, int len, void *sp)
652 sendit(msg, len, (STATUS_PKT *)sp);
655 static void sendit(POOL_MEM &msg, int len, STATUS_PKT *sp)
659 memcpy(bs->msg, msg.c_str(), len+1);
663 sp->callback(msg.c_str(), len, sp->context);
669 * Status command from Director
671 bool status_cmd(JCR *jcr)
673 BSOCK *dir = jcr->dir_bsock;
679 dir->signal(BNET_EOD);
684 * .status command from Director
686 bool qstatus_cmd(JCR *jcr)
688 BSOCK *dir = jcr->dir_bsock;
695 if (sscanf(dir->msg, qstatus, cmd.c_str()) != 1) {
696 pm_strcpy(jcr->errmsg, dir->msg);
697 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
698 dir->fsend(_("3900 Bad .status command, missing argument.\n"));
699 dir->signal(BNET_EOD);
704 Dmsg1(200, "cmd=%s\n", cmd.c_str());
706 if (strcmp(cmd.c_str(), "current") == 0) {
707 dir->fsend(OKqstatus, cmd.c_str());
709 if (njcr->JobId != 0) {
710 dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
714 } else if (strcmp(cmd.c_str(), "last") == 0) {
715 dir->fsend(OKqstatus, cmd.c_str());
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.c_str(), "header") == 0) {
722 list_status_header(&sp);
723 } else if (strcasecmp(cmd.c_str(), "running") == 0) {
725 list_running_jobs(&sp);
726 } else if (strcasecmp(cmd.c_str(), "waitreservation") == 0) {
728 list_jobs_waiting_on_reservation(&sp);
729 } else if (strcasecmp(cmd.c_str(), "devices") == 0) {
732 } else if (strcasecmp(cmd.c_str(), "volumes") == 0) {
734 list_volumes(sendit, &sp);
735 } else if (strcasecmp(cmd.c_str(), "spooling") == 0) {
737 list_spool_stats(sendit, &sp);
738 } else if (strcasecmp(cmd.c_str(), "terminated") == 0) {
740 list_terminated_jobs(&sp);
742 pm_strcpy(jcr->errmsg, dir->msg);
743 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
744 dir->fsend(_("3900 Bad .status command, wrong argument.\n"));
745 dir->signal(BNET_EOD);
748 dir->signal(BNET_EOD);
752 #if defined(HAVE_WIN32)
755 /* Return a one line status for the tray monitor */
756 char *bac_status(char *buf, int buf_len)
759 const char *termstat = _("Bacula Storage: Idle");
760 struct s_last_job *job;
761 int stat = 0; /* Idle */
766 Dmsg0(1000, "Begin bac_status jcr loop.\n");
768 if (njcr->JobId != 0) {
770 termstat = _("Bacula Storage: Running");
779 if (last_jobs->size() > 0) {
780 job = (struct s_last_job *)last_jobs->last();
781 stat = job->JobStatus;
782 switch (job->JobStatus) {
784 termstat = _("Bacula Storage: Last Job Canceled");
786 case JS_ErrorTerminated:
788 termstat = _("Bacula Storage: Last Job Failed");
792 termstat = _("Bacula Storage: Last Job had Warnings");
797 Dmsg0(1000, "End bac_status jcr loop.\n");
801 bstrncpy(buf, termstat, buf_len);
806 #endif /* HAVE_WIN32 */