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, _("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 */