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()) {
312 if (dev->get_slot() > 0) {
313 len = Mmsg(msg, _(" Slot %d is loaded in drive %d.\n"),
314 dev->get_slot(), dev->drive_index);
315 sendit(msg, len, sp);
316 } else if (dev->get_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 reserved=%d block=%d\n\n"), dev->num_writers,
371 dev->num_reserved(), dev->blocked());
372 sendit(msg, len, sp);
374 len = Mmsg(msg, _("Device parameters:\n"));
375 sendit(msg, len, sp);
377 len = Mmsg(msg, _("Archive name: %s Device name: %s\n"), dev->archive_name(),
379 sendit(msg, len, sp);
381 len = Mmsg(msg, _("File=%u block=%u\n"), dev->file, dev->block_num);
382 sendit(msg, len, sp);
384 len = Mmsg(msg, _("Min block=%u Max block=%u\n"), dev->min_block_size, dev->max_block_size);
385 sendit(msg, len, sp);
388 static void list_running_jobs(STATUS_PKT *sp)
394 char JobName[MAX_NAME_LENGTH];
395 char b1[30], b2[30], b3[30];
397 POOL_MEM msg(PM_MESSAGE);
399 len = Mmsg(msg, _("\nRunning Jobs:\n"));
400 if (!sp->api) sendit(msg, len, sp);
403 if (jcr->JobStatus == JS_WaitFD) {
404 len = Mmsg(msg, _("%s Job %s waiting for Client connection.\n"),
405 job_type_to_str(jcr->JobType), jcr->Job);
406 sendit(msg, len, sp);
409 rdcr = jcr->read_dcr;
410 if ((dcr && dcr->device) || rdcr && rdcr->device) {
411 bstrncpy(JobName, jcr->Job, sizeof(JobName));
412 /* There are three periods after the Job name */
414 for (int i=0; i<3; i++) {
415 if ((p=strrchr(JobName, '.')) != NULL) {
419 if (rdcr && rdcr->device) {
420 len = Mmsg(msg, _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
421 " pool=\"%s\" device=%s\n"),
422 job_level_to_str(jcr->JobLevel),
423 job_type_to_str(jcr->JobType),
428 rdcr->dev?rdcr->dev->print_name():
429 rdcr->device->device_name);
430 sendit(msg, len, sp);
432 if (dcr && dcr->device) {
433 len = Mmsg(msg, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
434 " pool=\"%s\" device=%s\n"),
435 job_level_to_str(jcr->JobLevel),
436 job_type_to_str(jcr->JobType),
441 dcr->dev?dcr->dev->print_name():
442 dcr->device->device_name);
443 sendit(msg, len, sp);
444 len= Mmsg(msg, _(" spooling=%d despooling=%d despool_wait=%d\n"),
445 dcr->spooling, dcr->despooling, dcr->despool_wait);
446 sendit(msg, len, sp);
448 sec = time(NULL) - jcr->run_time;
452 bps = jcr->JobBytes / sec;
453 len = Mmsg(msg, _(" Files=%s Bytes=%s Bytes/sec=%s\n"),
454 edit_uint64_with_commas(jcr->JobFiles, b1),
455 edit_uint64_with_commas(jcr->JobBytes, b2),
456 edit_uint64_with_commas(bps, b3));
457 sendit(msg, len, sp);
460 if (jcr->file_bsock) {
461 len = Mmsg(msg, _(" FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
462 edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
463 jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
464 jcr->file_bsock->m_fd);
465 sendit(msg, len, sp);
467 len = Mmsg(msg, _(" FDSocket closed\n"));
468 sendit(msg, len, sp);
476 len = Mmsg(msg, _("No Jobs running.\n"));
477 if (!sp->api) sendit(msg, len, sp);
479 if (!sp->api) sendit("====\n", 5, sp);
482 static void list_jobs_waiting_on_reservation(STATUS_PKT *sp)
485 POOL_MEM msg(PM_MESSAGE);
488 len = Mmsg(msg, _("\nJobs waiting to reserve a drive:\n"));
489 if (!sp->api) sendit(msg, len, sp);
492 if (!jcr->reserve_msgs) {
495 send_drive_reserve_messages(jcr, sendit, sp);
499 if (!sp->api) sendit("====\n", 5, sp);
503 static void list_terminated_jobs(STATUS_PKT *sp)
505 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
507 struct s_last_job *je;
510 msg = _("\nTerminated Jobs:\n");
511 if (!sp->api) sendit(msg, strlen(msg), sp);
512 if (last_jobs->size() == 0) {
513 if (!sp->api) sendit("====\n", 5, sp);
516 lock_last_jobs_list();
517 msg = _(" JobId Level Files Bytes Status Finished Name \n");
518 if (!sp->api) sendit(msg, strlen(msg), sp);
519 msg = _("===================================================================\n");
520 if (!sp->api) sendit(msg, strlen(msg), sp);
521 foreach_dlist(je, last_jobs) {
522 char JobName[MAX_NAME_LENGTH];
523 const char *termstat;
526 bstrftime_nc(dt, sizeof(dt), je->end_time);
527 switch (je->JobType) {
530 bstrncpy(level, " ", sizeof(level));
533 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
537 switch (je->JobStatus) {
539 termstat = _("Created");
542 case JS_ErrorTerminated:
543 termstat = _("Error");
546 termstat = _("Diffs");
549 termstat = _("Cancel");
555 termstat = _("Other");
558 bstrncpy(JobName, je->Job, sizeof(JobName));
559 /* There are three periods after the Job name */
561 for (int i=0; i<3; i++) {
562 if ((p=strrchr(JobName, '.')) != NULL) {
567 bsnprintf(buf, sizeof(buf), _("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"),
570 edit_uint64_with_commas(je->JobFiles, b1),
571 edit_uint64_with_suffix(je->JobBytes, b2),
575 bsnprintf(buf, sizeof(buf), _("%6d %-6s %8s %10s %-7s %-8s %s\n"),
578 edit_uint64_with_commas(je->JobFiles, b1),
579 edit_uint64_with_suffix(je->JobBytes, b2),
583 sendit(buf, strlen(buf), sp);
585 unlock_last_jobs_list();
586 if (!sp->api) sendit("====\n", 5, sp);
590 * Convert Job Level into a string
592 static const char *level_to_str(int level)
603 str = _("Incremental");
606 str = _("Differential");
611 case L_VERIFY_CATALOG:
612 str = _("Verify Catalog");
615 str = _("Init Catalog");
617 case L_VERIFY_VOLUME_TO_CATALOG:
618 str = _("Volume to Catalog");
620 case L_VERIFY_DISK_TO_CATALOG:
621 str = _("Disk to Catalog");
630 str = _("Unknown Job Level");
639 static void sendit(const char *msg, int len, STATUS_PKT *sp)
643 memcpy(bs->msg, msg, len+1);
647 sp->callback(msg, len, sp->context);
651 static void sendit(const char *msg, int len, void *sp)
653 sendit(msg, len, (STATUS_PKT *)sp);
656 static void sendit(POOL_MEM &msg, int len, STATUS_PKT *sp)
660 memcpy(bs->msg, msg.c_str(), len+1);
664 sp->callback(msg.c_str(), len, sp->context);
670 * Status command from Director
672 bool status_cmd(JCR *jcr)
674 BSOCK *dir = jcr->dir_bsock;
680 dir->signal(BNET_EOD);
685 * .status command from Director
687 bool qstatus_cmd(JCR *jcr)
689 BSOCK *dir = jcr->dir_bsock;
696 if (sscanf(dir->msg, qstatus, cmd.c_str()) != 1) {
697 pm_strcpy(jcr->errmsg, dir->msg);
698 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
699 dir->fsend(_("3900 Bad .status command, missing argument.\n"));
700 dir->signal(BNET_EOD);
705 Dmsg1(200, "cmd=%s\n", cmd.c_str());
707 if (strcmp(cmd.c_str(), "current") == 0) {
708 dir->fsend(OKqstatus, cmd.c_str());
710 if (njcr->JobId != 0) {
711 dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
715 } else if (strcmp(cmd.c_str(), "last") == 0) {
716 dir->fsend(OKqstatus, cmd.c_str());
717 if ((last_jobs) && (last_jobs->size() > 0)) {
718 job = (s_last_job*)last_jobs->last();
719 dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
721 } else if (strcasecmp(cmd.c_str(), "header") == 0) {
723 list_status_header(&sp);
724 } else if (strcasecmp(cmd.c_str(), "running") == 0) {
726 list_running_jobs(&sp);
727 } else if (strcasecmp(cmd.c_str(), "waitreservation") == 0) {
729 list_jobs_waiting_on_reservation(&sp);
730 } else if (strcasecmp(cmd.c_str(), "devices") == 0) {
733 } else if (strcasecmp(cmd.c_str(), "volumes") == 0) {
735 list_volumes(sendit, &sp);
736 } else if (strcasecmp(cmd.c_str(), "spooling") == 0) {
738 list_spool_stats(sendit, &sp);
739 } else if (strcasecmp(cmd.c_str(), "terminated") == 0) {
741 list_terminated_jobs(&sp);
743 pm_strcpy(jcr->errmsg, dir->msg);
744 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
745 dir->fsend(_("3900 Bad .status command, wrong argument.\n"));
746 dir->signal(BNET_EOD);
749 dir->signal(BNET_EOD);
753 #if defined(HAVE_WIN32)
756 /* Return a one line status for the tray monitor */
757 char *bac_status(char *buf, int buf_len)
760 const char *termstat = _("Bacula Storage: Idle");
761 struct s_last_job *job;
762 int stat = 0; /* Idle */
767 Dmsg0(1000, "Begin bac_status jcr loop.\n");
769 if (njcr->JobId != 0) {
771 termstat = _("Bacula Storage: Running");
780 if (last_jobs->size() > 0) {
781 job = (struct s_last_job *)last_jobs->last();
782 stat = job->JobStatus;
783 switch (job->JobStatus) {
785 termstat = _("Bacula Storage: Last Job Canceled");
787 case JS_ErrorTerminated:
789 termstat = _("Bacula Storage: Last Job Failed");
793 termstat = _("Bacula Storage: Last Job had Warnings");
798 Dmsg0(1000, "End bac_status jcr loop.\n");
802 bstrncpy(buf, termstat, buf_len);
807 #endif /* HAVE_WIN32 */