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 list_terminated_jobs(STATUS_PKT *sp);
63 static void list_running_jobs(STATUS_PKT *sp);
64 static void list_jobs_waiting_on_reservation(STATUS_PKT *sp);
65 static void list_status_header(STATUS_PKT *sp);
66 static void list_devices(STATUS_PKT *sp);
68 static const char *level_to_str(int level);
71 * Status command from Director
73 void output_status(STATUS_PKT *sp)
75 POOL_MEM msg(PM_MESSAGE);
78 list_status_header(sp);
83 list_running_jobs(sp);
86 * List jobs stuck in reservation system
88 list_jobs_waiting_on_reservation(sp);
91 * List terminated jobs
93 list_terminated_jobs(sp);
101 len = Mmsg(msg, _("In Use Volume status:\n"));
102 sendit(msg, len, sp);
104 list_volumes(sendit, (void *)sp);
105 sendit("====\n\n", 6, sp);
108 if (debug_level > 10) {
109 bs->fsend(_("====\n\n"));
110 dump_resource(R_DEVICE, resources[R_DEVICE-r_first].res_head, sendit, user);
111 bs->fsend(_("====\n\n"));
115 list_spool_stats(sendit, (void *)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 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 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()) {
311 len = Mmsg(msg, _(" Slot %d is loaded in drive %d.\n"),
312 dev->Slot, dev->drive_index);
313 sendit(msg, len, sp);
314 } else if (dev->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 len = Mmsg(msg, _("Configured device capabilities:\n"));
324 sendit(msg, len, sp);
326 len = Mmsg(msg, "%sEOF %sBSR %sBSF %sFSR %sFSF %sEOM %sREM %sRACCESS %sAUTOMOUNT %sLABEL %sANONVOLS %sALWAYSOPEN\n",
327 dev->capabilities & CAP_EOF ? "" : "!",
328 dev->capabilities & CAP_BSR ? "" : "!",
329 dev->capabilities & CAP_BSF ? "" : "!",
330 dev->capabilities & CAP_FSR ? "" : "!",
331 dev->capabilities & CAP_FSF ? "" : "!",
332 dev->capabilities & CAP_EOM ? "" : "!",
333 dev->capabilities & CAP_REM ? "" : "!",
334 dev->capabilities & CAP_RACCESS ? "" : "!",
335 dev->capabilities & CAP_AUTOMOUNT ? "" : "!",
336 dev->capabilities & CAP_LABEL ? "" : "!",
337 dev->capabilities & CAP_ANONVOLS ? "" : "!",
338 dev->capabilities & CAP_ALWAYSOPEN ? "" : "!");
339 sendit(msg, len, sp);
341 len = Mmsg(msg, _("Device state:\n"));
342 sendit(msg, len, sp);
344 len = Mmsg(msg, "%sOPENED %sTAPE %sLABEL %sMALLOC %sAPPEND %sREAD %sEOT %sWEOT %sEOF %sNEXTVOL %sSHORT %sMOUNTED\n",
345 dev->is_open() ? "" : "!",
346 dev->is_tape() ? "" : "!",
347 dev->is_labeled() ? "" : "!",
348 dev->state & ST_MALLOC ? "" : "!",
349 dev->can_append() ? "" : "!",
350 dev->can_read() ? "" : "!",
351 dev->at_eot() ? "" : "!",
352 dev->state & ST_WEOT ? "" : "!",
353 dev->at_eof() ? "" : "!",
354 dev->state & ST_NEXTVOL ? "" : "!",
355 dev->state & ST_SHORT ? "" : "!",
356 dev->state & ST_MOUNTED ? "" : "!");
357 sendit(msg, len, sp);
359 len = Mmsg(msg, _("num_writers=%d block=%d\n\n"), dev->num_writers, dev->blocked());
360 sendit(msg, len, sp);
362 len = Mmsg(msg, _("Device parameters:\n"));
363 sendit(msg, len, sp);
365 len = Mmsg(msg, _("Archive name: %s Device name: %s\n"), dev->archive_name(),
367 sendit(msg, len, sp);
369 len = Mmsg(msg, _("File=%u block=%u\n"), dev->file, dev->block_num);
370 sendit(msg, len, sp);
372 len = Mmsg(msg, _("Min block=%u Max block=%u\n"), dev->min_block_size, dev->max_block_size);
373 sendit(msg, len, sp);
377 static void list_running_jobs(STATUS_PKT *sp)
383 char JobName[MAX_NAME_LENGTH];
384 char b1[30], b2[30], b3[30];
386 POOL_MEM msg(PM_MESSAGE);
388 len = Mmsg(msg, _("\nRunning Jobs:\n"));
389 sendit(msg, len, sp);
392 if (jcr->JobStatus == JS_WaitFD) {
393 len = Mmsg(msg, _("%s Job %s waiting for Client connection.\n"),
394 job_type_to_str(jcr->JobType), jcr->Job);
395 sendit(msg, len, sp);
398 rdcr = jcr->read_dcr;
399 if ((dcr && dcr->device) || rdcr && rdcr->device) {
400 bstrncpy(JobName, jcr->Job, sizeof(JobName));
401 /* There are three periods after the Job name */
403 for (int i=0; i<3; i++) {
404 if ((p=strrchr(JobName, '.')) != NULL) {
408 if (rdcr && rdcr->device) {
409 len = Mmsg(msg, _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
410 " pool=\"%s\" device=%s\n"),
411 job_level_to_str(jcr->JobLevel),
412 job_type_to_str(jcr->JobType),
417 rdcr->dev?rdcr->dev->print_name():
418 rdcr->device->device_name);
419 sendit(msg, len, sp);
421 if (dcr && dcr->device) {
422 len = Mmsg(msg, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
423 " pool=\"%s\" device=%s\n"),
424 job_level_to_str(jcr->JobLevel),
425 job_type_to_str(jcr->JobType),
430 dcr->dev?dcr->dev->print_name():
431 dcr->device->device_name);
432 sendit(msg, len, sp);
433 len= Mmsg(msg, _(" spooling=%d despooling=%d despool_wait=%d\n"),
434 dcr->spooling, dcr->despooling, dcr->despool_wait);
435 sendit(msg, len, sp);
437 sec = time(NULL) - jcr->run_time;
441 bps = jcr->JobBytes / sec;
442 len = Mmsg(msg, _(" Files=%s Bytes=%s Bytes/sec=%s\n"),
443 edit_uint64_with_commas(jcr->JobFiles, b1),
444 edit_uint64_with_commas(jcr->JobBytes, b2),
445 edit_uint64_with_commas(bps, b3));
446 sendit(msg, len, sp);
449 if (jcr->file_bsock) {
450 len = Mmsg(msg, _(" FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
451 edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
452 jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
453 jcr->file_bsock->m_fd);
454 sendit(msg, len, sp);
456 len = Mmsg(msg, _(" FDSocket closed\n"));
457 sendit(msg, len, sp);
465 len = Mmsg(msg, _("No Jobs running.\n"));
466 sendit(msg, len, sp);
468 sendit("====\n", 5, sp);
471 static void list_jobs_waiting_on_reservation(STATUS_PKT *sp)
474 POOL_MEM msg(PM_MESSAGE);
477 len = Mmsg(msg, _("\nJobs waiting to reserve a drive:\n"));
478 sendit(msg, len, sp);
481 if (!jcr->reserve_msgs) {
484 send_drive_reserve_messages(jcr, sendit, sp);
488 sendit("====\n", 5, sp);
492 static void list_terminated_jobs(STATUS_PKT *sp)
494 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
496 struct s_last_job *je;
499 msg = _("\nTerminated Jobs:\n");
500 sendit(msg, strlen(msg), sp);
501 if (last_jobs->size() == 0) {
502 sendit("====\n", 5, sp);
505 lock_last_jobs_list();
506 msg = _(" JobId Level Files Bytes Status Finished Name \n");
507 sendit(msg, strlen(msg), sp);
508 msg = _("===================================================================\n");
509 sendit(msg, strlen(msg), sp);
510 foreach_dlist(je, last_jobs) {
511 char JobName[MAX_NAME_LENGTH];
512 const char *termstat;
515 bstrftime_nc(dt, sizeof(dt), je->end_time);
516 switch (je->JobType) {
519 bstrncpy(level, " ", sizeof(level));
522 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
526 switch (je->JobStatus) {
528 termstat = _("Created");
531 case JS_ErrorTerminated:
532 termstat = _("Error");
535 termstat = _("Diffs");
538 termstat = _("Cancel");
544 termstat = _("Other");
547 bstrncpy(JobName, je->Job, sizeof(JobName));
548 /* There are three periods after the Job name */
550 for (int i=0; i<3; i++) {
551 if ((p=strrchr(JobName, '.')) != NULL) {
555 bsnprintf(buf, sizeof(buf), _("%6d %-6s %8s %10s %-7s %-8s %s\n"),
558 edit_uint64_with_commas(je->JobFiles, b1),
559 edit_uint64_with_suffix(je->JobBytes, b2),
562 sendit(buf, strlen(buf), sp);
564 unlock_last_jobs_list();
565 sendit("====\n", 5, sp);
569 * Convert Job Level into a string
571 static const char *level_to_str(int level)
582 str = _("Incremental");
585 str = _("Differential");
590 case L_VERIFY_CATALOG:
591 str = _("Verify Catalog");
594 str = _("Init Catalog");
596 case L_VERIFY_VOLUME_TO_CATALOG:
597 str = _("Volume to Catalog");
599 case L_VERIFY_DISK_TO_CATALOG:
600 str = _("Disk to Catalog");
609 str = _("Unknown Job Level");
618 static void sendit(const char *msg, int len, STATUS_PKT *sp)
622 memcpy(bs->msg, msg, len+1);
626 sp->callback(msg, len, sp->context);
630 static void sendit(const char *msg, int len, void *sp)
632 sendit(msg, len, (STATUS_PKT *)sp);
635 static void sendit(POOL_MEM &msg, int len, STATUS_PKT *sp)
639 memcpy(bs->msg, msg.c_str(), len+1);
643 sp->callback(msg.c_str(), len, sp->context);
649 * Status command from Director
651 bool status_cmd(JCR *jcr)
653 BSOCK *bs = jcr->dir_bsock;
659 bs->signal(BNET_EOD);
664 * .status command from Director
666 bool qstatus_cmd(JCR *jcr)
668 BSOCK *dir = jcr->dir_bsock;
673 if (sscanf(dir->msg, qstatus, time.c_str()) != 1) {
674 pm_strcpy(jcr->errmsg, dir->msg);
675 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
676 dir->fsend(_("3900 Bad .status command, missing argument.\n"));
677 dir->signal(BNET_EOD);
682 if (strcmp(time.c_str(), "current") == 0) {
683 dir->fsend(OKqstatus, time.c_str());
685 if (njcr->JobId != 0) {
686 dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
690 } else if (strcmp(time.c_str(), "last") == 0) {
691 dir->fsend(OKqstatus, time.c_str());
692 if ((last_jobs) && (last_jobs->size() > 0)) {
693 job = (s_last_job*)last_jobs->last();
694 dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
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, wrong argument.\n"));
700 dir->signal(BNET_EOD);
703 dir->signal(BNET_EOD);
707 #if defined(HAVE_WIN32)
710 /* Return a one line status for the tray monitor */
711 char *bac_status(char *buf, int buf_len)
714 const char *termstat = _("Bacula Storage: Idle");
715 struct s_last_job *job;
716 int stat = 0; /* Idle */
721 Dmsg0(1000, "Begin bac_status jcr loop.\n");
723 if (njcr->JobId != 0) {
725 termstat = _("Bacula Storage: Running");
734 if (last_jobs->size() > 0) {
735 job = (struct s_last_job *)last_jobs->last();
736 stat = job->JobStatus;
737 switch (job->JobStatus) {
739 termstat = _("Bacula Storage: Last Job Canceled");
741 case JS_ErrorTerminated:
743 termstat = _("Bacula Storage: Last Job Failed");
747 termstat = _("Bacula Storage: Last Job had Warnings");
752 Dmsg0(1000, "End bac_status jcr loop.\n");
756 bstrncpy(buf, termstat, buf_len);
761 #endif /* HAVE_WIN32 */