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 len = Mmsg(msg, _("Device parameters:\n"));
373 sendit(msg, len, sp);
375 len = Mmsg(msg, _("Archive name: %s Device name: %s\n"), dev->archive_name(),
377 sendit(msg, len, sp);
379 len = Mmsg(msg, _("File=%u block=%u\n"), dev->file, dev->block_num);
380 sendit(msg, len, sp);
382 len = Mmsg(msg, _("Min block=%u Max block=%u\n"), dev->min_block_size, dev->max_block_size);
383 sendit(msg, len, sp);
386 static void list_running_jobs(STATUS_PKT *sp)
392 char JobName[MAX_NAME_LENGTH];
393 char b1[30], b2[30], b3[30];
395 POOL_MEM msg(PM_MESSAGE);
397 len = Mmsg(msg, _("\nRunning Jobs:\n"));
398 if (!sp->api) sendit(msg, len, sp);
401 if (jcr->JobStatus == JS_WaitFD) {
402 len = Mmsg(msg, _("%s Job %s waiting for Client connection.\n"),
403 job_type_to_str(jcr->getJobType()), jcr->Job);
404 sendit(msg, len, sp);
407 rdcr = jcr->read_dcr;
408 if ((dcr && dcr->device) || (rdcr && rdcr->device)) {
409 bstrncpy(JobName, jcr->Job, sizeof(JobName));
410 /* There are three periods after the Job name */
412 for (int i=0; i<3; i++) {
413 if ((p=strrchr(JobName, '.')) != NULL) {
417 if (rdcr && rdcr->device) {
418 len = Mmsg(msg, _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
419 " pool=\"%s\" device=%s\n"),
420 job_level_to_str(jcr->getJobLevel()),
421 job_type_to_str(jcr->getJobType()),
426 rdcr->dev?rdcr->dev->print_name():
427 rdcr->device->device_name);
428 sendit(msg, len, sp);
430 if (dcr && dcr->device) {
431 len = Mmsg(msg, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
432 " pool=\"%s\" device=%s\n"),
433 job_level_to_str(jcr->getJobLevel()),
434 job_type_to_str(jcr->getJobType()),
439 dcr->dev?dcr->dev->print_name():
440 dcr->device->device_name);
441 sendit(msg, len, sp);
442 len= Mmsg(msg, _(" spooling=%d despooling=%d despool_wait=%d\n"),
443 dcr->spooling, dcr->despooling, dcr->despool_wait);
444 sendit(msg, len, sp);
446 sec = time(NULL) - jcr->run_time;
450 bps = jcr->JobBytes / sec;
451 len = Mmsg(msg, _(" Files=%s Bytes=%s Bytes/sec=%s\n"),
452 edit_uint64_with_commas(jcr->JobFiles, b1),
453 edit_uint64_with_commas(jcr->JobBytes, b2),
454 edit_uint64_with_commas(bps, b3));
455 sendit(msg, len, sp);
458 if (jcr->file_bsock) {
459 len = Mmsg(msg, _(" FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
460 edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
461 jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
462 jcr->file_bsock->m_fd);
463 sendit(msg, len, sp);
465 len = Mmsg(msg, _(" FDSocket closed\n"));
466 sendit(msg, len, sp);
474 len = Mmsg(msg, _("No Jobs running.\n"));
475 if (!sp->api) sendit(msg, len, sp);
477 if (!sp->api) sendit("====\n", 5, sp);
480 static void list_jobs_waiting_on_reservation(STATUS_PKT *sp)
483 POOL_MEM msg(PM_MESSAGE);
486 len = Mmsg(msg, _("\nJobs waiting to reserve a drive:\n"));
487 if (!sp->api) sendit(msg, len, sp);
490 if (!jcr->reserve_msgs) {
493 send_drive_reserve_messages(jcr, sendit, sp);
497 if (!sp->api) sendit("====\n", 5, sp);
501 static void list_terminated_jobs(STATUS_PKT *sp)
503 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
505 struct s_last_job *je;
508 msg = _("\nTerminated Jobs:\n");
509 if (!sp->api) sendit(msg, strlen(msg), sp);
510 if (last_jobs->size() == 0) {
511 if (!sp->api) sendit("====\n", 5, sp);
514 lock_last_jobs_list();
515 msg = _(" JobId Level Files Bytes Status Finished Name \n");
516 if (!sp->api) sendit(msg, strlen(msg), sp);
517 msg = _("===================================================================\n");
518 if (!sp->api) sendit(msg, strlen(msg), sp);
519 foreach_dlist(je, last_jobs) {
520 char JobName[MAX_NAME_LENGTH];
521 const char *termstat;
524 bstrftime_nc(dt, sizeof(dt), je->end_time);
525 switch (je->JobType) {
528 bstrncpy(level, " ", sizeof(level));
531 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
535 switch (je->JobStatus) {
537 termstat = _("Created");
540 case JS_ErrorTerminated:
541 termstat = _("Error");
544 termstat = _("Diffs");
547 termstat = _("Cancel");
553 termstat = _("OK -- with warnings");
556 termstat = _("Other");
559 bstrncpy(JobName, je->Job, sizeof(JobName));
560 /* There are three periods after the Job name */
562 for (int i=0; i<3; i++) {
563 if ((p=strrchr(JobName, '.')) != NULL) {
568 bsnprintf(buf, sizeof(buf), _("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"),
571 edit_uint64_with_commas(je->JobFiles, b1),
572 edit_uint64_with_suffix(je->JobBytes, b2),
576 bsnprintf(buf, sizeof(buf), _("%6d %-6s %8s %10s %-7s %-8s %s\n"),
579 edit_uint64_with_commas(je->JobFiles, b1),
580 edit_uint64_with_suffix(je->JobBytes, b2),
584 sendit(buf, strlen(buf), sp);
586 unlock_last_jobs_list();
587 if (!sp->api) sendit("====\n", 5, sp);
591 * Convert Job Level into a string
593 static const char *level_to_str(int level)
604 str = _("Incremental");
607 str = _("Differential");
612 case L_VERIFY_CATALOG:
613 str = _("Verify Catalog");
616 str = _("Init Catalog");
618 case L_VERIFY_VOLUME_TO_CATALOG:
619 str = _("Volume to Catalog");
621 case L_VERIFY_DISK_TO_CATALOG:
622 str = _("Disk to Catalog");
631 str = _("Unknown Job Level");
640 static void sendit(const char *msg, int len, STATUS_PKT *sp)
644 memcpy(bs->msg, msg, len+1);
648 sp->callback(msg, len, sp->context);
652 static void sendit(const char *msg, int len, void *sp)
654 sendit(msg, len, (STATUS_PKT *)sp);
657 static void sendit(POOL_MEM &msg, int len, STATUS_PKT *sp)
661 memcpy(bs->msg, msg.c_str(), len+1);
665 sp->callback(msg.c_str(), len, sp->context);
671 * Status command from Director
673 bool status_cmd(JCR *jcr)
675 BSOCK *dir = jcr->dir_bsock;
681 dir->signal(BNET_EOD);
686 * .status command from Director
688 bool qstatus_cmd(JCR *jcr)
690 BSOCK *dir = jcr->dir_bsock;
697 if (sscanf(dir->msg, qstatus, cmd.c_str()) != 1) {
698 pm_strcpy(jcr->errmsg, dir->msg);
699 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
700 dir->fsend(_("3900 Bad .status command, missing argument.\n"));
701 dir->signal(BNET_EOD);
706 Dmsg1(200, "cmd=%s\n", cmd.c_str());
708 if (strcmp(cmd.c_str(), "current") == 0) {
709 dir->fsend(OKqstatus, cmd.c_str());
711 if (njcr->JobId != 0) {
712 dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
716 } else if (strcmp(cmd.c_str(), "last") == 0) {
717 dir->fsend(OKqstatus, cmd.c_str());
718 if ((last_jobs) && (last_jobs->size() > 0)) {
719 job = (s_last_job*)last_jobs->last();
720 dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
722 } else if (strcasecmp(cmd.c_str(), "header") == 0) {
724 list_status_header(&sp);
725 } else if (strcasecmp(cmd.c_str(), "running") == 0) {
727 list_running_jobs(&sp);
728 } else if (strcasecmp(cmd.c_str(), "waitreservation") == 0) {
730 list_jobs_waiting_on_reservation(&sp);
731 } else if (strcasecmp(cmd.c_str(), "devices") == 0) {
734 } else if (strcasecmp(cmd.c_str(), "volumes") == 0) {
736 list_volumes(sendit, &sp);
737 } else if (strcasecmp(cmd.c_str(), "spooling") == 0) {
739 list_spool_stats(sendit, &sp);
740 } else if (strcasecmp(cmd.c_str(), "terminated") == 0) {
742 list_terminated_jobs(&sp);
744 pm_strcpy(jcr->errmsg, dir->msg);
745 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
746 dir->fsend(_("3900 Bad .status command, wrong argument.\n"));
747 dir->signal(BNET_EOD);
750 dir->signal(BNET_EOD);
754 #if defined(HAVE_WIN32)
757 /* Return a one line status for the tray monitor */
758 char *bac_status(char *buf, int buf_len)
761 const char *termstat = _("Bacula Storage: Idle");
762 struct s_last_job *job;
763 int stat = 0; /* Idle */
768 Dmsg0(1000, "Begin bac_status jcr loop.\n");
770 if (njcr->JobId != 0) {
772 termstat = _("Bacula Storage: Running");
781 if (last_jobs->size() > 0) {
782 job = (struct s_last_job *)last_jobs->last();
783 stat = job->JobStatus;
784 switch (job->JobStatus) {
786 termstat = _("Bacula Storage: Last Job Canceled");
788 case JS_ErrorTerminated:
790 termstat = _("Bacula Storage: Last Job Failed");
794 termstat = _("Bacula Storage: Last Job had Warnings");
799 Dmsg0(1000, "End bac_status jcr loop.\n");
803 bstrncpy(buf, termstat, buf_len);
808 #endif /* HAVE_WIN32 */