2 Bacula® - The Network Backup Solution
4 Copyright (C) 2003-2009 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 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
39 #include "lib/status.h"
41 /* Exported variables */
43 /* Imported variables */
44 extern BSOCK *filed_chan;
45 extern void *start_heap;
47 /* Static variables */
48 static char qstatus[] = ".status %127s\n";
50 static char OKqstatus[] = "3000 OK .status\n";
51 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
54 /* Forward referenced functions */
55 static void sendit(const char *msg, int len, STATUS_PKT *sp);
56 static void sendit(POOL_MEM &msg, int len, STATUS_PKT *sp);
57 static void sendit(const char *msg, int len, void *arg);
59 static void send_blocked_status(DEVICE *dev, STATUS_PKT *sp);
60 static void send_device_status(DEVICE *dev, STATUS_PKT *sp);
61 static void list_terminated_jobs(STATUS_PKT *sp);
62 static void list_running_jobs(STATUS_PKT *sp);
63 static void list_jobs_waiting_on_reservation(STATUS_PKT *sp);
64 static void list_status_header(STATUS_PKT *sp);
65 static void list_devices(STATUS_PKT *sp);
67 static const char *level_to_str(int level);
70 * Status command from Director
72 void output_status(STATUS_PKT *sp)
74 POOL_MEM msg(PM_MESSAGE);
77 list_status_header(sp);
82 list_running_jobs(sp);
85 * List jobs stuck in reservation system
87 list_jobs_waiting_on_reservation(sp);
90 * List terminated jobs
92 list_terminated_jobs(sp);
100 len = Mmsg(msg, _("Used Volume status:\n"));
101 if (!sp->api) sendit(msg, len, sp);
103 list_volumes(sendit, (void *)sp);
104 if (!sp->api) sendit("====\n\n", 6, sp);
107 if (debug_level > 10) {
108 bs->fsend(_("====\n\n"));
109 dump_resource(R_DEVICE, resources[R_DEVICE-r_first].res_head, sendit, user);
110 bs->fsend(_("====\n\n"));
114 list_spool_stats(sendit, (void *)sp);
115 if (!sp->api) sendit("====\n\n", 6, sp);
119 static void list_devices(STATUS_PKT *sp)
122 AUTOCHANGER *changer;
124 char b1[35], b2[35], b3[35];
125 POOL_MEM msg(PM_MESSAGE);
129 len = Mmsg(msg, _("\nDevice status:\n"));
130 if (!sp->api) sendit(msg, len, sp);
132 foreach_res(changer, R_AUTOCHANGER) {
133 len = Mmsg(msg, _("Autochanger \"%s\" with devices:\n"),
135 sendit(msg, len, sp);
137 foreach_alist(device, changer->device) {
139 len = Mmsg(msg, " %s\n", device->dev->print_name());
140 sendit(msg, len, sp);
142 len = Mmsg(msg, " %s\n", device->hdr.name);
143 sendit(msg, len, sp);
147 foreach_res(device, R_DEVICE) {
149 if (dev && dev->is_open()) {
150 if (dev->is_labeled()) {
151 len = Mmsg(msg, _("Device %s is mounted with:\n"
154 " Media type: %s\n"),
156 dev->VolHdr.VolumeName,
157 dev->pool_name[0]?dev->pool_name:"*unknown*",
158 dev->device->media_type);
159 sendit(msg, len, sp);
161 len = Mmsg(msg, _("Device %s open but no Bacula volume is currently mounted.\n"),
163 sendit(msg, len, sp);
165 send_blocked_status(dev, sp);
166 if (dev->can_append()) {
167 bpb = dev->VolCatInfo.VolCatBlocks;
171 bpb = dev->VolCatInfo.VolCatBytes / bpb;
172 len = Mmsg(msg, _(" Total Bytes=%s Blocks=%s Bytes/block=%s\n"),
173 edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, b1),
174 edit_uint64_with_commas(dev->VolCatInfo.VolCatBlocks, b2),
175 edit_uint64_with_commas(bpb, b3));
176 sendit(msg, len, sp);
177 } else { /* reading */
178 bpb = dev->VolCatInfo.VolCatReads;
182 if (dev->VolCatInfo.VolCatRBytes > 0) {
183 bpb = dev->VolCatInfo.VolCatRBytes / bpb;
187 len = Mmsg(msg, _(" Total Bytes Read=%s Blocks Read=%s Bytes/block=%s\n"),
188 edit_uint64_with_commas(dev->VolCatInfo.VolCatRBytes, b1),
189 edit_uint64_with_commas(dev->VolCatInfo.VolCatReads, b2),
190 edit_uint64_with_commas(bpb, b3));
191 sendit(msg, len, sp);
193 len = Mmsg(msg, _(" Positioned at File=%s Block=%s\n"),
194 edit_uint64_with_commas(dev->file, b1),
195 edit_uint64_with_commas(dev->block_num, b2));
196 sendit(msg, len, sp);
200 len = Mmsg(msg, _("Device %s is not open.\n"), dev->print_name());
201 sendit(msg, len, sp);
202 send_blocked_status(dev, sp);
204 len = Mmsg(msg, _("Device \"%s\" is not open or does not exist.\n"), device->hdr.name);
205 sendit(msg, len, sp);
209 if (!sp->api) sendit("====\n\n", 6, sp);
212 static void list_status_header(STATUS_PKT *sp)
214 char dt[MAX_TIME_LENGTH];
215 char b1[35], b2[35], b3[35], b4[35], b5[35];
216 POOL_MEM msg(PM_MESSAGE);
219 len = Mmsg(msg, _("%s Version: %s (%s) %s %s %s\n"),
220 my_name, VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
221 sendit(msg, len, sp);
223 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
226 len = Mmsg(msg, _("Daemon started %s, %d Job%s run since started.\n"),
227 dt, num_jobs_run, num_jobs_run == 1 ? "" : "s");
228 sendit(msg, len, sp);
230 len = Mmsg(msg, _(" Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
231 edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
232 edit_uint64_with_commas(sm_bytes, b2),
233 edit_uint64_with_commas(sm_max_bytes, b3),
234 edit_uint64_with_commas(sm_buffers, b4),
235 edit_uint64_with_commas(sm_max_buffers, b5));
236 sendit(msg, len, sp);
237 len = Mmsg(msg, "Sizes: boffset_t=%d size_t=%d int32_t=%d int64_t=%d\n",
238 (int)sizeof(boffset_t), (int)sizeof(size_t), (int)sizeof(int32_t),
239 (int)sizeof(int64_t));
240 sendit(msg, len, sp);
243 static void send_blocked_status(DEVICE *dev, STATUS_PKT *sp)
245 POOL_MEM msg(PM_MESSAGE);
249 len = Mmsg(msg, _("No DEVICE structure.\n\n"));
250 sendit(msg, len, sp);
253 switch (dev->blocked()) {
255 len = Mmsg(msg, _(" Device is BLOCKED. User unmounted.\n"));
256 sendit(msg, len, sp);
258 case BST_UNMOUNTED_WAITING_FOR_SYSOP:
259 len = Mmsg(msg, _(" Device is BLOCKED. User unmounted during wait for media/mount.\n"));
260 sendit(msg, len, sp);
262 case BST_WAITING_FOR_SYSOP:
264 dlist *dcrs = dev->attached_dcrs;
265 bool found_jcr = false;
269 for (dcr = (DCR *)dcrs->first(); dcr != NULL; dcr = (DCR *)dcrs->next(dcr)) {
270 if (dcr->jcr->JobStatus == JS_WaitMount) {
271 len = Mmsg(msg, _(" Device is BLOCKED waiting for mount of volume \"%s\",\n"
273 " Media type: %s\n"),
277 sendit(msg, len, sp);
279 } else if (dcr->jcr->JobStatus == JS_WaitMedia) {
280 len = Mmsg(msg, _(" Device is BLOCKED waiting to create a volume for:\n"
282 " Media type: %s\n"),
285 sendit(msg, len, sp);
292 len = Mmsg(msg, _(" Device is BLOCKED waiting for media.\n"));
293 sendit(msg, len, sp);
297 case BST_DOING_ACQUIRE:
298 len = Mmsg(msg, _(" Device is being initialized.\n"));
299 sendit(msg, len, sp);
301 case BST_WRITING_LABEL:
302 len = Mmsg(msg, _(" Device is blocked labeling a Volume.\n"));
303 sendit(msg, len, sp);
308 /* Send autochanger slot status */
309 if (dev->is_autochanger()) {
310 if (dev->get_slot() > 0) {
311 len = Mmsg(msg, _(" Slot %d is loaded in drive %d.\n"),
312 dev->get_slot(), dev->drive_index);
313 sendit(msg, len, sp);
314 } else if (dev->get_slot() == 0) {
315 len = Mmsg(msg, _(" Drive %d is not loaded.\n"), dev->drive_index);
316 sendit(msg, len, sp);
318 len = Mmsg(msg, _(" Drive %d status unknown.\n"), dev->drive_index);
319 sendit(msg, len, sp);
322 if (debug_level > 1) {
323 send_device_status(dev, sp);
327 static void send_device_status(DEVICE *dev, STATUS_PKT *sp)
329 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 len = Mmsg(msg, _("Device parameters:\n"));
373 sendit(msg, len, sp);
375 len = Mmsg(msg, _("truncate on purge: %d\n"), dev->device->truncate_on_purge);
376 sendit(msg, len, sp);
378 len = Mmsg(msg, _("Archive name: %s Device name: %s\n"), dev->archive_name(),
380 sendit(msg, len, sp);
382 len = Mmsg(msg, _("File=%u block=%u\n"), dev->file, dev->block_num);
383 sendit(msg, len, sp);
385 len = Mmsg(msg, _("Min block=%u Max block=%u\n"), dev->min_block_size, dev->max_block_size);
386 sendit(msg, len, sp);
389 static void list_running_jobs(STATUS_PKT *sp)
395 char JobName[MAX_NAME_LENGTH];
396 char b1[30], b2[30], b3[30];
398 POOL_MEM msg(PM_MESSAGE);
400 len = Mmsg(msg, _("\nRunning Jobs:\n"));
401 if (!sp->api) sendit(msg, len, sp);
404 if (jcr->JobStatus == JS_WaitFD) {
405 len = Mmsg(msg, _("%s Job %s waiting for Client connection.\n"),
406 job_type_to_str(jcr->get_JobType()), jcr->Job);
407 sendit(msg, len, sp);
410 rdcr = jcr->read_dcr;
411 if ((dcr && dcr->device) || (rdcr && rdcr->device)) {
412 bstrncpy(JobName, jcr->Job, sizeof(JobName));
413 /* There are three periods after the Job name */
415 for (int i=0; i<3; i++) {
416 if ((p=strrchr(JobName, '.')) != NULL) {
420 if (rdcr && rdcr->device) {
421 len = Mmsg(msg, _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
422 " pool=\"%s\" device=%s\n"),
423 job_level_to_str(jcr->get_JobLevel()),
424 job_type_to_str(jcr->get_JobType()),
429 rdcr->dev?rdcr->dev->print_name():
430 rdcr->device->device_name);
431 sendit(msg, len, sp);
433 if (dcr && dcr->device) {
434 len = Mmsg(msg, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
435 " pool=\"%s\" device=%s\n"),
436 job_level_to_str(jcr->get_JobLevel()),
437 job_type_to_str(jcr->get_JobType()),
442 dcr->dev?dcr->dev->print_name():
443 dcr->device->device_name);
444 sendit(msg, len, sp);
445 len= Mmsg(msg, _(" spooling=%d despooling=%d despool_wait=%d\n"),
446 dcr->spooling, dcr->despooling, dcr->despool_wait);
447 sendit(msg, len, sp);
449 sec = time(NULL) - jcr->run_time;
453 bps = jcr->JobBytes / sec;
454 len = Mmsg(msg, _(" Files=%s Bytes=%s Bytes/sec=%s\n"),
455 edit_uint64_with_commas(jcr->JobFiles, b1),
456 edit_uint64_with_commas(jcr->JobBytes, b2),
457 edit_uint64_with_commas(bps, b3));
458 sendit(msg, len, sp);
461 if (jcr->file_bsock) {
462 len = Mmsg(msg, _(" FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
463 edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
464 jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
465 jcr->file_bsock->m_fd);
466 sendit(msg, len, sp);
468 len = Mmsg(msg, _(" FDSocket closed\n"));
469 sendit(msg, len, sp);
477 len = Mmsg(msg, _("No Jobs running.\n"));
478 if (!sp->api) sendit(msg, len, sp);
480 if (!sp->api) sendit("====\n", 5, sp);
483 static void list_jobs_waiting_on_reservation(STATUS_PKT *sp)
486 POOL_MEM msg(PM_MESSAGE);
489 len = Mmsg(msg, _("\nJobs waiting to reserve a drive:\n"));
490 if (!sp->api) sendit(msg, len, sp);
493 if (!jcr->reserve_msgs) {
496 send_drive_reserve_messages(jcr, sendit, sp);
500 if (!sp->api) sendit("====\n", 5, sp);
504 static void list_terminated_jobs(STATUS_PKT *sp)
506 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
508 struct s_last_job *je;
511 msg = _("\nTerminated Jobs:\n");
512 if (!sp->api) sendit(msg, strlen(msg), sp);
513 if (last_jobs->size() == 0) {
514 if (!sp->api) sendit("====\n", 5, sp);
517 lock_last_jobs_list();
518 msg = _(" JobId Level Files Bytes Status Finished Name \n");
519 if (!sp->api) sendit(msg, strlen(msg), sp);
520 msg = _("===================================================================\n");
521 if (!sp->api) sendit(msg, strlen(msg), sp);
522 foreach_dlist(je, last_jobs) {
523 char JobName[MAX_NAME_LENGTH];
524 const char *termstat;
527 bstrftime_nc(dt, sizeof(dt), je->end_time);
528 switch (je->JobType) {
531 bstrncpy(level, " ", sizeof(level));
534 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
538 switch (je->JobStatus) {
540 termstat = _("Created");
543 case JS_ErrorTerminated:
544 termstat = _("Error");
547 termstat = _("Diffs");
550 termstat = _("Cancel");
556 termstat = _("OK -- with warnings");
559 termstat = _("Other");
562 bstrncpy(JobName, je->Job, sizeof(JobName));
563 /* There are three periods after the Job name */
565 for (int i=0; i<3; i++) {
566 if ((p=strrchr(JobName, '.')) != NULL) {
571 bsnprintf(buf, sizeof(buf), _("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"),
574 edit_uint64_with_commas(je->JobFiles, b1),
575 edit_uint64_with_suffix(je->JobBytes, b2),
579 bsnprintf(buf, sizeof(buf), _("%6d %-6s %8s %10s %-7s %-8s %s\n"),
582 edit_uint64_with_commas(je->JobFiles, b1),
583 edit_uint64_with_suffix(je->JobBytes, b2),
587 sendit(buf, strlen(buf), sp);
589 unlock_last_jobs_list();
590 if (!sp->api) sendit("====\n", 5, sp);
594 * Convert Job Level into a string
596 static const char *level_to_str(int level)
607 str = _("Incremental");
610 str = _("Differential");
615 case L_VERIFY_CATALOG:
616 str = _("Verify Catalog");
619 str = _("Init Catalog");
621 case L_VERIFY_VOLUME_TO_CATALOG:
622 str = _("Volume to Catalog");
624 case L_VERIFY_DISK_TO_CATALOG:
625 str = _("Disk to Catalog");
634 str = _("Unknown Job Level");
643 static void sendit(const char *msg, int len, STATUS_PKT *sp)
647 memcpy(bs->msg, msg, len+1);
651 sp->callback(msg, len, sp->context);
655 static void sendit(const char *msg, int len, void *sp)
657 sendit(msg, len, (STATUS_PKT *)sp);
660 static void sendit(POOL_MEM &msg, int len, STATUS_PKT *sp)
664 memcpy(bs->msg, msg.c_str(), len+1);
668 sp->callback(msg.c_str(), len, sp->context);
674 * Status command from Director
676 bool status_cmd(JCR *jcr)
678 BSOCK *dir = jcr->dir_bsock;
684 dir->signal(BNET_EOD);
689 * .status command from Director
691 bool qstatus_cmd(JCR *jcr)
693 BSOCK *dir = jcr->dir_bsock;
700 if (sscanf(dir->msg, qstatus, cmd.c_str()) != 1) {
701 pm_strcpy(jcr->errmsg, dir->msg);
702 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
703 dir->fsend(_("3900 Bad .status command, missing argument.\n"));
704 dir->signal(BNET_EOD);
709 Dmsg1(200, "cmd=%s\n", cmd.c_str());
711 if (strcmp(cmd.c_str(), "current") == 0) {
712 dir->fsend(OKqstatus, cmd.c_str());
714 if (njcr->JobId != 0) {
715 dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
719 } else if (strcmp(cmd.c_str(), "last") == 0) {
720 dir->fsend(OKqstatus, cmd.c_str());
721 if ((last_jobs) && (last_jobs->size() > 0)) {
722 job = (s_last_job*)last_jobs->last();
723 dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
725 } else if (strcasecmp(cmd.c_str(), "header") == 0) {
727 list_status_header(&sp);
728 } else if (strcasecmp(cmd.c_str(), "running") == 0) {
730 list_running_jobs(&sp);
731 } else if (strcasecmp(cmd.c_str(), "waitreservation") == 0) {
733 list_jobs_waiting_on_reservation(&sp);
734 } else if (strcasecmp(cmd.c_str(), "devices") == 0) {
737 } else if (strcasecmp(cmd.c_str(), "volumes") == 0) {
739 list_volumes(sendit, &sp);
740 } else if (strcasecmp(cmd.c_str(), "spooling") == 0) {
742 list_spool_stats(sendit, &sp);
743 } else if (strcasecmp(cmd.c_str(), "terminated") == 0) {
745 list_terminated_jobs(&sp);
747 pm_strcpy(jcr->errmsg, dir->msg);
748 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
749 dir->fsend(_("3900 Bad .status command, wrong argument.\n"));
750 dir->signal(BNET_EOD);
753 dir->signal(BNET_EOD);
757 #if defined(HAVE_WIN32)
760 /* Return a one line status for the tray monitor */
761 char *bac_status(char *buf, int buf_len)
764 const char *termstat = _("Bacula Storage: Idle");
765 struct s_last_job *job;
766 int stat = 0; /* Idle */
771 Dmsg0(1000, "Begin bac_status jcr loop.\n");
773 if (njcr->JobId != 0) {
775 termstat = _("Bacula Storage: Running");
784 if (last_jobs->size() > 0) {
785 job = (struct s_last_job *)last_jobs->last();
786 stat = job->JobStatus;
787 switch (job->JobStatus) {
789 termstat = _("Bacula Storage: Last Job Canceled");
791 case JS_ErrorTerminated:
793 termstat = _("Bacula Storage: Last Job Failed");
797 termstat = _("Bacula Storage: Last Job had Warnings");
802 Dmsg0(1000, "End bac_status jcr loop.\n");
806 bstrncpy(buf, termstat, buf_len);
811 #endif /* HAVE_WIN32 */