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);
331 len = Mmsg(msg, _("Configured device capabilities:\n"));
332 sendit(msg, len, sp);
334 len = Mmsg(msg, "%sEOF %sBSR %sBSF %sFSR %sFSF %sEOM %sREM %sRACCESS %sAUTOMOUNT %sLABEL %sANONVOLS %sALWAYSOPEN\n",
335 dev->capabilities & CAP_EOF ? "" : "!",
336 dev->capabilities & CAP_BSR ? "" : "!",
337 dev->capabilities & CAP_BSF ? "" : "!",
338 dev->capabilities & CAP_FSR ? "" : "!",
339 dev->capabilities & CAP_FSF ? "" : "!",
340 dev->capabilities & CAP_EOM ? "" : "!",
341 dev->capabilities & CAP_REM ? "" : "!",
342 dev->capabilities & CAP_RACCESS ? "" : "!",
343 dev->capabilities & CAP_AUTOMOUNT ? "" : "!",
344 dev->capabilities & CAP_LABEL ? "" : "!",
345 dev->capabilities & CAP_ANONVOLS ? "" : "!",
346 dev->capabilities & CAP_ALWAYSOPEN ? "" : "!");
347 sendit(msg, len, sp);
349 len = Mmsg(msg, _("Device state:\n"));
350 sendit(msg, len, sp);
352 len = Mmsg(msg, "%sOPENED %sTAPE %sLABEL %sMALLOC %sAPPEND %sREAD %sEOT %sWEOT %sEOF %sNEXTVOL %sSHORT %sMOUNTED\n",
353 dev->is_open() ? "" : "!",
354 dev->is_tape() ? "" : "!",
355 dev->is_labeled() ? "" : "!",
356 dev->state & ST_MALLOC ? "" : "!",
357 dev->can_append() ? "" : "!",
358 dev->can_read() ? "" : "!",
359 dev->at_eot() ? "" : "!",
360 dev->state & ST_WEOT ? "" : "!",
361 dev->at_eof() ? "" : "!",
362 dev->state & ST_NEXTVOL ? "" : "!",
363 dev->state & ST_SHORT ? "" : "!",
364 dev->state & ST_MOUNTED ? "" : "!");
365 sendit(msg, len, sp);
367 len = Mmsg(msg, _("num_writers=%d reserved=%d block=%d\n\n"), dev->num_writers,
368 dev->num_reserved(), dev->blocked());
369 sendit(msg, len, sp);
371 len = Mmsg(msg, _("Device parameters:\n"));
372 sendit(msg, len, sp);
374 len = Mmsg(msg, _("Archive name: %s Device name: %s\n"), dev->archive_name(),
376 sendit(msg, len, sp);
378 len = Mmsg(msg, _("File=%u block=%u\n"), dev->file, dev->block_num);
379 sendit(msg, len, sp);
381 len = Mmsg(msg, _("Min block=%u Max block=%u\n"), dev->min_block_size, dev->max_block_size);
382 sendit(msg, len, sp);
385 static void list_running_jobs(STATUS_PKT *sp)
391 char JobName[MAX_NAME_LENGTH];
392 char b1[30], b2[30], b3[30];
394 POOL_MEM msg(PM_MESSAGE);
396 len = Mmsg(msg, _("\nRunning Jobs:\n"));
397 if (!sp->api) sendit(msg, len, sp);
400 if (jcr->JobStatus == JS_WaitFD) {
401 len = Mmsg(msg, _("%s Job %s waiting for Client connection.\n"),
402 job_type_to_str(jcr->getJobType()), jcr->Job);
403 sendit(msg, len, sp);
406 rdcr = jcr->read_dcr;
407 if ((dcr && dcr->device) || (rdcr && rdcr->device)) {
408 bstrncpy(JobName, jcr->Job, sizeof(JobName));
409 /* There are three periods after the Job name */
411 for (int i=0; i<3; i++) {
412 if ((p=strrchr(JobName, '.')) != NULL) {
416 if (rdcr && rdcr->device) {
417 len = Mmsg(msg, _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
418 " pool=\"%s\" device=%s\n"),
419 job_level_to_str(jcr->getJobLevel()),
420 job_type_to_str(jcr->getJobType()),
425 rdcr->dev?rdcr->dev->print_name():
426 rdcr->device->device_name);
427 sendit(msg, len, sp);
429 if (dcr && dcr->device) {
430 len = Mmsg(msg, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
431 " pool=\"%s\" device=%s\n"),
432 job_level_to_str(jcr->getJobLevel()),
433 job_type_to_str(jcr->getJobType()),
438 dcr->dev?dcr->dev->print_name():
439 dcr->device->device_name);
440 sendit(msg, len, sp);
441 len= Mmsg(msg, _(" spooling=%d despooling=%d despool_wait=%d\n"),
442 dcr->spooling, dcr->despooling, dcr->despool_wait);
443 sendit(msg, len, sp);
445 sec = time(NULL) - jcr->run_time;
449 bps = jcr->JobBytes / sec;
450 len = Mmsg(msg, _(" Files=%s Bytes=%s Bytes/sec=%s\n"),
451 edit_uint64_with_commas(jcr->JobFiles, b1),
452 edit_uint64_with_commas(jcr->JobBytes, b2),
453 edit_uint64_with_commas(bps, b3));
454 sendit(msg, len, sp);
457 if (jcr->file_bsock) {
458 len = Mmsg(msg, _(" FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
459 edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
460 jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
461 jcr->file_bsock->m_fd);
462 sendit(msg, len, sp);
464 len = Mmsg(msg, _(" FDSocket closed\n"));
465 sendit(msg, len, sp);
473 len = Mmsg(msg, _("No Jobs running.\n"));
474 if (!sp->api) sendit(msg, len, sp);
476 if (!sp->api) sendit("====\n", 5, sp);
479 static void list_jobs_waiting_on_reservation(STATUS_PKT *sp)
482 POOL_MEM msg(PM_MESSAGE);
485 len = Mmsg(msg, _("\nJobs waiting to reserve a drive:\n"));
486 if (!sp->api) sendit(msg, len, sp);
489 if (!jcr->reserve_msgs) {
492 send_drive_reserve_messages(jcr, sendit, sp);
496 if (!sp->api) sendit("====\n", 5, sp);
500 static void list_terminated_jobs(STATUS_PKT *sp)
502 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
504 struct s_last_job *je;
507 msg = _("\nTerminated Jobs:\n");
508 if (!sp->api) sendit(msg, strlen(msg), sp);
509 if (last_jobs->size() == 0) {
510 if (!sp->api) sendit("====\n", 5, sp);
513 lock_last_jobs_list();
514 msg = _(" JobId Level Files Bytes Status Finished Name \n");
515 if (!sp->api) sendit(msg, strlen(msg), sp);
516 msg = _("===================================================================\n");
517 if (!sp->api) sendit(msg, strlen(msg), sp);
518 foreach_dlist(je, last_jobs) {
519 char JobName[MAX_NAME_LENGTH];
520 const char *termstat;
523 bstrftime_nc(dt, sizeof(dt), je->end_time);
524 switch (je->JobType) {
527 bstrncpy(level, " ", sizeof(level));
530 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
534 switch (je->JobStatus) {
536 termstat = _("Created");
539 case JS_ErrorTerminated:
540 termstat = _("Error");
543 termstat = _("Diffs");
546 termstat = _("Cancel");
552 termstat = _("OK -- with warnings");
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 */