2 Bacula® - The Network Backup Solution
4 Copyright (C) 2003-2010 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 three of the GNU Affero 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 Affero 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 Kern Sibbald.
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
38 #include "lib/status.h"
40 /* Exported variables */
42 /* Imported variables */
43 extern BSOCK *filed_chan;
44 extern void *start_heap;
46 /* Static variables */
47 static char qstatus[] = ".status %127s\n";
49 static char OKqstatus[] = "3000 OK .status\n";
50 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
53 /* Forward referenced functions */
54 static void sendit(const char *msg, int len, STATUS_PKT *sp);
55 static void sendit(POOL_MEM &msg, int len, STATUS_PKT *sp);
56 static void sendit(const char *msg, int len, void *arg);
58 static void send_blocked_status(DEVICE *dev, STATUS_PKT *sp);
59 static void send_device_status(DEVICE *dev, STATUS_PKT *sp);
60 static void list_terminated_jobs(STATUS_PKT *sp);
61 static void list_running_jobs(STATUS_PKT *sp);
62 static void list_jobs_waiting_on_reservation(STATUS_PKT *sp);
63 static void list_status_header(STATUS_PKT *sp);
64 static void list_devices(STATUS_PKT *sp);
66 static const char *level_to_str(int level);
69 * Status command from Director
71 void output_status(STATUS_PKT *sp)
73 POOL_MEM msg(PM_MESSAGE);
76 list_status_header(sp);
81 list_running_jobs(sp);
84 * List jobs stuck in reservation system
86 list_jobs_waiting_on_reservation(sp);
89 * List terminated jobs
91 list_terminated_jobs(sp);
99 len = Mmsg(msg, _("Used Volume status:\n"));
100 if (!sp->api) sendit(msg, len, sp);
102 list_volumes(sendit, (void *)sp);
103 if (!sp->api) sendit("====\n\n", 6, sp);
106 if (debug_level > 10) {
107 bs->fsend(_("====\n\n"));
108 dump_resource(R_DEVICE, resources[R_DEVICE-r_first].res_head, sendit, user);
109 bs->fsend(_("====\n\n"));
113 list_spool_stats(sendit, (void *)sp);
114 if (!sp->api) sendit("====\n\n", 6, sp);
118 static void list_devices(STATUS_PKT *sp)
121 AUTOCHANGER *changer;
123 char b1[35], b2[35], b3[35];
124 POOL_MEM msg(PM_MESSAGE);
128 len = Mmsg(msg, _("\nDevice status:\n"));
129 if (!sp->api) sendit(msg, len, sp);
131 foreach_res(changer, R_AUTOCHANGER) {
132 len = Mmsg(msg, _("Autochanger \"%s\" with devices:\n"),
134 sendit(msg, len, sp);
136 foreach_alist(device, changer->device) {
138 len = Mmsg(msg, " %s\n", device->dev->print_name());
139 sendit(msg, len, sp);
141 len = Mmsg(msg, " %s\n", device->hdr.name);
142 sendit(msg, len, sp);
146 foreach_res(device, R_DEVICE) {
148 if (dev && dev->is_open()) {
149 if (dev->is_labeled()) {
150 len = Mmsg(msg, _("Device %s is mounted with:\n"
153 " Media type: %s\n"),
155 dev->VolHdr.VolumeName,
156 dev->pool_name[0]?dev->pool_name:"*unknown*",
157 dev->device->media_type);
158 sendit(msg, len, sp);
160 len = Mmsg(msg, _("Device %s open but no Bacula volume is currently mounted.\n"),
162 sendit(msg, len, sp);
164 send_blocked_status(dev, sp);
165 if (dev->can_append()) {
166 bpb = dev->VolCatInfo.VolCatBlocks;
170 bpb = dev->VolCatInfo.VolCatBytes / bpb;
171 len = Mmsg(msg, _(" Total Bytes=%s Blocks=%s Bytes/block=%s\n"),
172 edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, b1),
173 edit_uint64_with_commas(dev->VolCatInfo.VolCatBlocks, b2),
174 edit_uint64_with_commas(bpb, b3));
175 sendit(msg, len, sp);
176 } else { /* reading */
177 bpb = dev->VolCatInfo.VolCatReads;
181 if (dev->VolCatInfo.VolCatRBytes > 0) {
182 bpb = dev->VolCatInfo.VolCatRBytes / bpb;
186 len = Mmsg(msg, _(" Total Bytes Read=%s Blocks Read=%s Bytes/block=%s\n"),
187 edit_uint64_with_commas(dev->VolCatInfo.VolCatRBytes, b1),
188 edit_uint64_with_commas(dev->VolCatInfo.VolCatReads, b2),
189 edit_uint64_with_commas(bpb, b3));
190 sendit(msg, len, sp);
192 len = Mmsg(msg, _(" Positioned at File=%s Block=%s\n"),
193 edit_uint64_with_commas(dev->file, b1),
194 edit_uint64_with_commas(dev->block_num, b2));
195 sendit(msg, len, sp);
199 len = Mmsg(msg, _("Device %s is not open.\n"), dev->print_name());
200 sendit(msg, len, sp);
201 send_blocked_status(dev, sp);
203 len = Mmsg(msg, _("Device \"%s\" is not open or does not exist.\n"), device->hdr.name);
204 sendit(msg, len, sp);
208 if (!sp->api) sendit("====\n\n", 6, sp);
211 static void list_status_header(STATUS_PKT *sp)
213 char dt[MAX_TIME_LENGTH];
214 char b1[35], b2[35], b3[35], b4[35], b5[35];
215 POOL_MEM msg(PM_MESSAGE);
218 len = Mmsg(msg, _("%s Version: %s (%s) %s %s %s\n"),
219 my_name, VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
220 sendit(msg, len, sp);
222 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
225 len = Mmsg(msg, _("Daemon started %s. Jobs: run=%d, running=%d.\n"),
226 dt, num_jobs_run, job_count());
227 sendit(msg, len, sp);
228 len = Mmsg(msg, _(" Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
229 edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
230 edit_uint64_with_commas(sm_bytes, b2),
231 edit_uint64_with_commas(sm_max_bytes, b3),
232 edit_uint64_with_commas(sm_buffers, b4),
233 edit_uint64_with_commas(sm_max_buffers, b5));
234 sendit(msg, len, sp);
235 len = Mmsg(msg, " Sizes: boffset_t=%d size_t=%d int32_t=%d int64_t=%d "
237 (int)sizeof(boffset_t), (int)sizeof(size_t), (int)sizeof(int32_t),
238 (int)sizeof(int64_t), (int)DEVELOPER_MODE, (int)BEEF);
239 sendit(msg, len, sp);
242 static void send_blocked_status(DEVICE *dev, STATUS_PKT *sp)
244 POOL_MEM msg(PM_MESSAGE);
248 len = Mmsg(msg, _("No DEVICE structure.\n\n"));
249 sendit(msg, len, sp);
252 switch (dev->blocked()) {
254 len = Mmsg(msg, _(" Device is BLOCKED. User unmounted.\n"));
255 sendit(msg, len, sp);
257 case BST_UNMOUNTED_WAITING_FOR_SYSOP:
258 len = Mmsg(msg, _(" Device is BLOCKED. User unmounted during wait for media/mount.\n"));
259 sendit(msg, len, sp);
261 case BST_WAITING_FOR_SYSOP:
263 dlist *dcrs = dev->attached_dcrs;
264 bool found_jcr = false;
268 for (dcr = (DCR *)dcrs->first(); dcr != NULL; dcr = (DCR *)dcrs->next(dcr)) {
269 if (dcr->jcr->JobStatus == JS_WaitMount) {
270 len = Mmsg(msg, _(" Device is BLOCKED waiting for mount of volume \"%s\",\n"
272 " Media type: %s\n"),
276 sendit(msg, len, sp);
278 } else if (dcr->jcr->JobStatus == JS_WaitMedia) {
279 len = Mmsg(msg, _(" Device is BLOCKED waiting to create a volume for:\n"
281 " Media type: %s\n"),
284 sendit(msg, len, sp);
291 len = Mmsg(msg, _(" Device is BLOCKED waiting for media.\n"));
292 sendit(msg, len, sp);
296 case BST_DOING_ACQUIRE:
297 len = Mmsg(msg, _(" Device is being initialized.\n"));
298 sendit(msg, len, sp);
300 case BST_WRITING_LABEL:
301 len = Mmsg(msg, _(" Device is blocked labeling a Volume.\n"));
302 sendit(msg, len, sp);
307 /* Send autochanger slot status */
308 if (dev->is_autochanger()) {
309 if (dev->get_slot() > 0) {
310 len = Mmsg(msg, _(" Slot %d is loaded in drive %d.\n"),
311 dev->get_slot(), dev->drive_index);
312 sendit(msg, len, sp);
313 } else if (dev->get_slot() == 0) {
314 len = Mmsg(msg, _(" Drive %d is not loaded.\n"), dev->drive_index);
315 sendit(msg, len, sp);
317 len = Mmsg(msg, _(" Drive %d status unknown.\n"), dev->drive_index);
318 sendit(msg, len, sp);
321 if (debug_level > 1) {
322 send_device_status(dev, sp);
326 static void send_device_status(DEVICE *dev, STATUS_PKT *sp)
328 POOL_MEM msg(PM_MESSAGE);
332 len = Mmsg(msg, _("Configured device capabilities:\n"));
333 sendit(msg, len, sp);
335 len = Mmsg(msg, "%sEOF %sBSR %sBSF %sFSR %sFSF %sEOM %sREM %sRACCESS %sAUTOMOUNT %sLABEL %sANONVOLS %sALWAYSOPEN\n",
336 dev->capabilities & CAP_EOF ? "" : "!",
337 dev->capabilities & CAP_BSR ? "" : "!",
338 dev->capabilities & CAP_BSF ? "" : "!",
339 dev->capabilities & CAP_FSR ? "" : "!",
340 dev->capabilities & CAP_FSF ? "" : "!",
341 dev->capabilities & CAP_EOM ? "" : "!",
342 dev->capabilities & CAP_REM ? "" : "!",
343 dev->capabilities & CAP_RACCESS ? "" : "!",
344 dev->capabilities & CAP_AUTOMOUNT ? "" : "!",
345 dev->capabilities & CAP_LABEL ? "" : "!",
346 dev->capabilities & CAP_ANONVOLS ? "" : "!",
347 dev->capabilities & CAP_ALWAYSOPEN ? "" : "!");
348 sendit(msg, len, sp);
350 len = Mmsg(msg, _("Device state:\n"));
351 sendit(msg, len, sp);
353 len = Mmsg(msg, "%sOPENED %sTAPE %sLABEL %sMALLOC %sAPPEND %sREAD %sEOT %sWEOT %sEOF %sNEXTVOL %sSHORT %sMOUNTED\n",
354 dev->is_open() ? "" : "!",
355 dev->is_tape() ? "" : "!",
356 dev->is_labeled() ? "" : "!",
357 dev->state & ST_MALLOC ? "" : "!",
358 dev->can_append() ? "" : "!",
359 dev->can_read() ? "" : "!",
360 dev->at_eot() ? "" : "!",
361 dev->state & ST_WEOT ? "" : "!",
362 dev->at_eof() ? "" : "!",
363 dev->state & ST_NEXTVOL ? "" : "!",
364 dev->state & ST_SHORT ? "" : "!",
365 dev->state & ST_MOUNTED ? "" : "!");
366 sendit(msg, len, sp);
368 len = Mmsg(msg, _("num_writers=%d reserved=%d block=%d\n\n"), dev->num_writers,
369 dev->num_reserved(), dev->blocked());
370 sendit(msg, len, sp);
372 locked = dev->lock_holder;
374 len = Mmsg(msg, _("Waiting for device locked by: %s\n"), locked);
375 sendit(msg, len, sp);
378 len = Mmsg(msg, _("Device parameters:\n"));
379 sendit(msg, len, sp);
381 len = Mmsg(msg, _("Archive name: %s Device name: %s\n"), dev->archive_name(),
383 sendit(msg, len, sp);
385 len = Mmsg(msg, _("File=%u block=%u\n"), dev->file, dev->block_num);
386 sendit(msg, len, sp);
388 len = Mmsg(msg, _("Min block=%u Max block=%u\n"), dev->min_block_size, dev->max_block_size);
389 sendit(msg, len, sp);
392 static void list_running_jobs(STATUS_PKT *sp)
398 char JobName[MAX_NAME_LENGTH];
399 char b1[30], b2[30], b3[30];
401 POOL_MEM msg(PM_MESSAGE);
403 len = Mmsg(msg, _("\nRunning Jobs:\n"));
404 if (!sp->api) sendit(msg, len, sp);
407 if (jcr->JobStatus == JS_WaitFD) {
408 len = Mmsg(msg, _("%s Job %s waiting for Client connection.\n"),
409 job_type_to_str(jcr->getJobType()), jcr->Job);
410 sendit(msg, len, sp);
413 rdcr = jcr->read_dcr;
414 if ((dcr && dcr->device) || (rdcr && rdcr->device)) {
415 bstrncpy(JobName, jcr->Job, sizeof(JobName));
416 /* There are three periods after the Job name */
418 for (int i=0; i<3; i++) {
419 if ((p=strrchr(JobName, '.')) != NULL) {
423 if (rdcr && rdcr->device) {
424 len = Mmsg(msg, _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
425 " pool=\"%s\" device=%s\n"),
426 job_level_to_str(jcr->getJobLevel()),
427 job_type_to_str(jcr->getJobType()),
432 rdcr->dev?rdcr->dev->print_name():
433 rdcr->device->device_name);
434 sendit(msg, len, sp);
436 if (dcr && dcr->device) {
437 len = Mmsg(msg, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
438 " pool=\"%s\" device=%s\n"),
439 job_level_to_str(jcr->getJobLevel()),
440 job_type_to_str(jcr->getJobType()),
445 dcr->dev?dcr->dev->print_name():
446 dcr->device->device_name);
447 sendit(msg, len, sp);
448 len= Mmsg(msg, _(" spooling=%d despooling=%d despool_wait=%d\n"),
449 dcr->spooling, dcr->despooling, dcr->despool_wait);
450 sendit(msg, len, sp);
452 sec = time(NULL) - jcr->run_time;
456 bps = jcr->JobBytes / sec;
457 len = Mmsg(msg, _(" Files=%s Bytes=%s Bytes/sec=%s\n"),
458 edit_uint64_with_commas(jcr->JobFiles, b1),
459 edit_uint64_with_commas(jcr->JobBytes, b2),
460 edit_uint64_with_commas(bps, b3));
461 sendit(msg, len, sp);
464 if (jcr->file_bsock) {
465 len = Mmsg(msg, _(" FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
466 edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
467 jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
468 jcr->file_bsock->m_fd);
469 sendit(msg, len, sp);
471 len = Mmsg(msg, _(" FDSocket closed\n"));
472 sendit(msg, len, sp);
480 len = Mmsg(msg, _("No Jobs running.\n"));
481 if (!sp->api) sendit(msg, len, sp);
483 if (!sp->api) sendit("====\n", 5, sp);
486 static void list_jobs_waiting_on_reservation(STATUS_PKT *sp)
489 POOL_MEM msg(PM_MESSAGE);
492 len = Mmsg(msg, _("\nJobs waiting to reserve a drive:\n"));
493 if (!sp->api) sendit(msg, len, sp);
496 if (!jcr->reserve_msgs) {
499 send_drive_reserve_messages(jcr, sendit, sp);
503 if (!sp->api) sendit("====\n", 5, sp);
507 static void list_terminated_jobs(STATUS_PKT *sp)
509 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
511 struct s_last_job *je;
514 msg = _("\nTerminated Jobs:\n");
515 if (!sp->api) sendit(msg, strlen(msg), sp);
516 if (last_jobs->size() == 0) {
517 if (!sp->api) sendit("====\n", 5, sp);
520 lock_last_jobs_list();
521 msg = _(" JobId Level Files Bytes Status Finished Name \n");
522 if (!sp->api) sendit(msg, strlen(msg), sp);
523 msg = _("===================================================================\n");
524 if (!sp->api) sendit(msg, strlen(msg), sp);
525 foreach_dlist(je, last_jobs) {
526 char JobName[MAX_NAME_LENGTH];
527 const char *termstat;
530 bstrftime_nc(dt, sizeof(dt), je->end_time);
531 switch (je->JobType) {
534 bstrncpy(level, " ", sizeof(level));
537 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
541 switch (je->JobStatus) {
543 termstat = _("Created");
546 case JS_ErrorTerminated:
547 termstat = _("Error");
550 termstat = _("Diffs");
553 termstat = _("Cancel");
559 termstat = _("OK -- with warnings");
562 termstat = _("Other");
565 bstrncpy(JobName, je->Job, sizeof(JobName));
566 /* There are three periods after the Job name */
568 for (int i=0; i<3; i++) {
569 if ((p=strrchr(JobName, '.')) != NULL) {
574 bsnprintf(buf, sizeof(buf), _("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"),
577 edit_uint64_with_commas(je->JobFiles, b1),
578 edit_uint64_with_suffix(je->JobBytes, b2),
582 bsnprintf(buf, sizeof(buf), _("%6d %-6s %8s %10s %-7s %-8s %s\n"),
585 edit_uint64_with_commas(je->JobFiles, b1),
586 edit_uint64_with_suffix(je->JobBytes, b2),
590 sendit(buf, strlen(buf), sp);
592 unlock_last_jobs_list();
593 if (!sp->api) sendit("====\n", 5, sp);
597 * Convert Job Level into a string
599 static const char *level_to_str(int level)
610 str = _("Incremental");
613 str = _("Differential");
618 case L_VERIFY_CATALOG:
619 str = _("Verify Catalog");
622 str = _("Init Catalog");
624 case L_VERIFY_VOLUME_TO_CATALOG:
625 str = _("Volume to Catalog");
627 case L_VERIFY_DISK_TO_CATALOG:
628 str = _("Disk to Catalog");
637 str = _("Unknown Job Level");
646 static void sendit(const char *msg, int len, STATUS_PKT *sp)
650 memcpy(bs->msg, msg, len+1);
654 sp->callback(msg, len, sp->context);
658 static void sendit(const char *msg, int len, void *sp)
660 sendit(msg, len, (STATUS_PKT *)sp);
663 static void sendit(POOL_MEM &msg, int len, STATUS_PKT *sp)
667 memcpy(bs->msg, msg.c_str(), len+1);
671 sp->callback(msg.c_str(), len, sp->context);
677 * Status command from Director
679 bool status_cmd(JCR *jcr)
681 BSOCK *dir = jcr->dir_bsock;
687 dir->signal(BNET_EOD);
692 * .status command from Director
694 bool qstatus_cmd(JCR *jcr)
696 BSOCK *dir = jcr->dir_bsock;
703 if (sscanf(dir->msg, qstatus, cmd.c_str()) != 1) {
704 pm_strcpy(jcr->errmsg, dir->msg);
705 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
706 dir->fsend(_("3900 Bad .status command, missing argument.\n"));
707 dir->signal(BNET_EOD);
712 Dmsg1(200, "cmd=%s\n", cmd.c_str());
714 if (strcmp(cmd.c_str(), "current") == 0) {
715 dir->fsend(OKqstatus, cmd.c_str());
717 if (njcr->JobId != 0) {
718 dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
722 } else if (strcmp(cmd.c_str(), "last") == 0) {
723 dir->fsend(OKqstatus, cmd.c_str());
724 if ((last_jobs) && (last_jobs->size() > 0)) {
725 job = (s_last_job*)last_jobs->last();
726 dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
728 } else if (strcasecmp(cmd.c_str(), "header") == 0) {
730 list_status_header(&sp);
731 } else if (strcasecmp(cmd.c_str(), "running") == 0) {
733 list_running_jobs(&sp);
734 } else if (strcasecmp(cmd.c_str(), "waitreservation") == 0) {
736 list_jobs_waiting_on_reservation(&sp);
737 } else if (strcasecmp(cmd.c_str(), "devices") == 0) {
740 } else if (strcasecmp(cmd.c_str(), "volumes") == 0) {
742 list_volumes(sendit, &sp);
743 } else if (strcasecmp(cmd.c_str(), "spooling") == 0) {
745 list_spool_stats(sendit, &sp);
746 } else if (strcasecmp(cmd.c_str(), "terminated") == 0) {
748 list_terminated_jobs(&sp);
750 pm_strcpy(jcr->errmsg, dir->msg);
751 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
752 dir->fsend(_("3900 Bad .status command, wrong argument.\n"));
753 dir->signal(BNET_EOD);
756 dir->signal(BNET_EOD);
760 #if defined(HAVE_WIN32)
763 /* Return a one line status for the tray monitor */
764 char *bac_status(char *buf, int buf_len)
767 const char *termstat = _("Bacula Storage: Idle");
768 struct s_last_job *job;
769 int stat = 0; /* Idle */
774 Dmsg0(1000, "Begin bac_status jcr loop.\n");
776 if (njcr->JobId != 0) {
778 termstat = _("Bacula Storage: Running");
787 if (last_jobs->size() > 0) {
788 job = (struct s_last_job *)last_jobs->last();
789 stat = job->JobStatus;
790 switch (job->JobStatus) {
792 termstat = _("Bacula Storage: Last Job Canceled");
794 case JS_ErrorTerminated:
796 termstat = _("Bacula Storage: Last Job Failed");
800 termstat = _("Bacula Storage: Last Job had Warnings");
805 Dmsg0(1000, "End bac_status jcr loop.\n");
809 bstrncpy(buf, termstat, buf_len);
814 #endif /* HAVE_WIN32 */