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
40 /* Exported variables */
42 /* Imported variables */
43 extern BSOCK *filed_chan;
44 extern int r_first, r_last;
45 extern struct s_res resources[];
46 extern void *start_heap;
48 /* Static variables */
49 static char qstatus[] = ".status %127s\n";
51 static char OKqstatus[] = "3000 OK .status\n";
52 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
55 /* Forward referenced functions */
56 static void sendit(const char *msg, int len, BSOCK *bs);
57 static void sendit(POOL_MEM &msg, int len, BSOCK *bs);
58 static void sendit(const char *msg, int len, void *arg);
60 static void send_blocked_status(DEVICE *dev, BSOCK *bs);
61 static void list_terminated_jobs(BSOCK *bs);
62 static void list_running_jobs(BSOCK *bs);
63 static void list_jobs_waiting_on_reservation(BSOCK *bs);
64 static void list_status_header(BSOCK *bs);
65 static void list_devices(BSOCK *bs);
67 static const char *level_to_str(int level);
70 * Status command from Director
72 void output_status(BSOCK *bs)
74 POOL_MEM msg(PM_MESSAGE);
77 list_status_header(bs);
82 list_running_jobs(bs);
85 * List jobs stuck in reservation system
87 list_jobs_waiting_on_reservation(bs);
90 * List terminated jobs
92 list_terminated_jobs(bs);
100 len = Mmsg(msg, _("In Use Volume status:\n"));
101 sendit(msg, len, bs);
103 list_volumes(sendit, (void *)bs);
104 sendit("====\n\n", 6, bs);
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 *)bs);
118 static void list_devices(BSOCK *bs)
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 sendit(msg, len, bs);
131 foreach_res(changer, R_AUTOCHANGER) {
132 len = Mmsg(msg, _("Autochanger \"%s\" with devices:\n"),
134 sendit(msg, len, bs);
136 foreach_alist(device, changer->device) {
138 len = Mmsg(msg, " %s\n", device->dev->print_name());
139 sendit(msg, len, bs);
141 len = Mmsg(msg, " %s\n", device->hdr.name);
142 sendit(msg, len, bs);
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, bs);
160 len = Mmsg(msg, _("Device %s open but no Bacula volume is currently mounted.\n"),
162 sendit(msg, len, bs);
164 send_blocked_status(dev, bs);
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, bs);
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, bs);
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, bs);
199 len = Mmsg(msg, _("Device %s is not open.\n"), dev->print_name());
200 sendit(msg, len, bs);
201 send_blocked_status(dev, bs);
203 len = Mmsg(msg, _("Device \"%s\" is not open or does not exist.\n"), device->hdr.name);
204 sendit(msg, len, bs);
208 sendit("====\n\n", 6, bs);
211 static void list_status_header(BSOCK *bs)
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, bs);
222 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
225 len = Mmsg(msg, _("Daemon started %s, %d Job%s run since started.\n"),
226 dt, num_jobs_run, num_jobs_run == 1 ? "" : "s");
227 sendit(msg, len, bs);
229 len = Mmsg(msg, _(" Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
230 edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
231 edit_uint64_with_commas(sm_bytes, b2),
232 edit_uint64_with_commas(sm_max_bytes, b3),
233 edit_uint64_with_commas(sm_buffers, b4),
234 edit_uint64_with_commas(sm_max_buffers, b5));
235 sendit(msg, len, bs);
236 len = Mmsg(msg, "Sizes: boffset_t=%d size_t=%d int32_t=%d int64_t=%d\n",
237 (int)sizeof(boffset_t), (int)sizeof(size_t), (int)sizeof(int32_t),
238 (int)sizeof(int64_t));
239 sendit(msg, len, bs);
242 static void send_blocked_status(DEVICE *dev, BSOCK *bs)
244 POOL_MEM msg(PM_MESSAGE);
248 len = Mmsg(msg, _("No DEVICE structure.\n\n"));
249 sendit(msg, len, bs);
252 switch (dev->blocked()) {
254 len = Mmsg(msg, _(" Device is BLOCKED. User unmounted.\n"));
255 sendit(msg, len, bs);
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, bs);
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, bs);
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, bs);
291 len = Mmsg(msg, _(" Device is BLOCKED waiting for media.\n"));
292 sendit(msg, len, bs);
296 case BST_DOING_ACQUIRE:
297 len = Mmsg(msg, _(" Device is being initialized.\n"));
298 sendit(msg, len, bs);
300 case BST_WRITING_LABEL:
301 len = Mmsg(msg, _(" Device is blocked labeling a Volume.\n"));
302 sendit(msg, len, bs);
307 /* Send autochanger slot status */
308 if (dev->is_autochanger()) {
310 len = Mmsg(msg, _(" Slot %d is loaded in drive %d.\n"),
311 dev->Slot, dev->drive_index);
312 sendit(msg, len, bs);
313 } else if (dev->Slot == 0) {
314 len = Mmsg(msg, _(" Drive %d is not loaded.\n"), dev->drive_index);
315 sendit(msg, len, bs);
317 len = Mmsg(msg, _(" Drive %d status unknown.\n"), dev->drive_index);
318 sendit(msg, len, bs);
321 if (debug_level > 1) {
322 len = Mmsg(msg, _("Configured device capabilities:\n"));
323 sendit(msg, len, bs);
325 len = Mmsg(msg, "%sEOF %sBSR %sBSF %sFSR %sFSF %sEOM %sREM %sRACCESS %sAUTOMOUNT %sLABEL %sANONVOLS %sALWAYSOPEN\n",
326 dev->capabilities & CAP_EOF ? "" : "!",
327 dev->capabilities & CAP_BSR ? "" : "!",
328 dev->capabilities & CAP_BSF ? "" : "!",
329 dev->capabilities & CAP_FSR ? "" : "!",
330 dev->capabilities & CAP_FSF ? "" : "!",
331 dev->capabilities & CAP_EOM ? "" : "!",
332 dev->capabilities & CAP_REM ? "" : "!",
333 dev->capabilities & CAP_RACCESS ? "" : "!",
334 dev->capabilities & CAP_AUTOMOUNT ? "" : "!",
335 dev->capabilities & CAP_LABEL ? "" : "!",
336 dev->capabilities & CAP_ANONVOLS ? "" : "!",
337 dev->capabilities & CAP_ALWAYSOPEN ? "" : "!");
338 sendit(msg, len, bs);
340 len = Mmsg(msg, _("Device state:\n"));
341 sendit(msg, len, bs);
343 len = Mmsg(msg, "%sOPENED %sTAPE %sLABEL %sMALLOC %sAPPEND %sREAD %sEOT %sWEOT %sEOF %sNEXTVOL %sSHORT %sMOUNTED\n",
344 dev->is_open() ? "" : "!",
345 dev->is_tape() ? "" : "!",
346 dev->is_labeled() ? "" : "!",
347 dev->state & ST_MALLOC ? "" : "!",
348 dev->can_append() ? "" : "!",
349 dev->can_read() ? "" : "!",
350 dev->at_eot() ? "" : "!",
351 dev->state & ST_WEOT ? "" : "!",
352 dev->at_eof() ? "" : "!",
353 dev->state & ST_NEXTVOL ? "" : "!",
354 dev->state & ST_SHORT ? "" : "!",
355 dev->state & ST_MOUNTED ? "" : "!");
356 sendit(msg, len, bs);
358 len = Mmsg(msg, _("num_writers=%d block=%d\n\n"), dev->num_writers, dev->blocked());
359 sendit(msg, len, bs);
361 len = Mmsg(msg, _("Device parameters:\n"));
362 sendit(msg, len, bs);
364 len = Mmsg(msg, _("Archive name: %s Device name: %s\n"), dev->archive_name(),
366 sendit(msg, len, bs);
368 len = Mmsg(msg, _("File=%u block=%u\n"), dev->file, dev->block_num);
369 sendit(msg, len, bs);
371 len = Mmsg(msg, _("Min block=%u Max block=%u\n"), dev->min_block_size, dev->max_block_size);
372 sendit(msg, len, bs);
376 static void list_running_jobs(BSOCK *bs)
382 char JobName[MAX_NAME_LENGTH];
383 char b1[30], b2[30], b3[30];
385 POOL_MEM msg(PM_MESSAGE);
387 len = Mmsg(msg, _("\nRunning Jobs:\n"));
388 sendit(msg, len, bs);
391 if (jcr->JobStatus == JS_WaitFD) {
392 len = Mmsg(msg, _("%s Job %s waiting for Client connection.\n"),
393 job_type_to_str(jcr->JobType), jcr->Job);
394 sendit(msg, len, bs);
397 rdcr = jcr->read_dcr;
398 if ((dcr && dcr->device) || rdcr && rdcr->device) {
399 bstrncpy(JobName, jcr->Job, sizeof(JobName));
400 /* There are three periods after the Job name */
402 for (int i=0; i<3; i++) {
403 if ((p=strrchr(JobName, '.')) != NULL) {
407 if (rdcr && rdcr->device) {
408 len = Mmsg(msg, _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
409 " pool=\"%s\" device=%s\n"),
410 job_level_to_str(jcr->JobLevel),
411 job_type_to_str(jcr->JobType),
416 rdcr->dev?rdcr->dev->print_name():
417 rdcr->device->device_name);
418 sendit(msg, len, bs);
420 if (dcr && dcr->device) {
421 len = Mmsg(msg, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
422 " pool=\"%s\" device=%s\n"),
423 job_level_to_str(jcr->JobLevel),
424 job_type_to_str(jcr->JobType),
429 dcr->dev?dcr->dev->print_name():
430 dcr->device->device_name);
431 sendit(msg, len, bs);
432 len= Mmsg(msg, _(" spooling=%d despooling=%d despool_wait=%d\n"),
433 dcr->spooling, dcr->despooling, dcr->despool_wait);
434 sendit(msg, len, bs);
436 sec = time(NULL) - jcr->run_time;
440 bps = jcr->JobBytes / sec;
441 len = Mmsg(msg, _(" Files=%s Bytes=%s Bytes/sec=%s\n"),
442 edit_uint64_with_commas(jcr->JobFiles, b1),
443 edit_uint64_with_commas(jcr->JobBytes, b2),
444 edit_uint64_with_commas(bps, b3));
445 sendit(msg, len, bs);
448 if (jcr->file_bsock) {
449 len = Mmsg(msg, _(" FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
450 edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
451 jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
452 jcr->file_bsock->m_fd);
453 sendit(msg, len, bs);
455 len = Mmsg(msg, _(" FDSocket closed\n"));
456 sendit(msg, len, bs);
464 len = Mmsg(msg, _("No Jobs running.\n"));
465 sendit(msg, len, bs);
467 sendit("====\n", 5, bs);
470 static void list_jobs_waiting_on_reservation(BSOCK *bs)
473 POOL_MEM msg(PM_MESSAGE);
476 len = Mmsg(msg, _("\nJobs waiting to reserve a drive:\n"));
477 sendit(msg, len, bs);
480 if (!jcr->reserve_msgs) {
483 send_drive_reserve_messages(jcr, sendit, bs);
487 sendit("====\n", 5, bs);
491 static void list_terminated_jobs(BSOCK *bs)
493 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
495 struct s_last_job *je;
498 msg = _("\nTerminated Jobs:\n");
499 sendit(msg, strlen(msg), bs);
500 if (last_jobs->size() == 0) {
501 sendit("====\n", 5, bs);
504 lock_last_jobs_list();
505 msg = _(" JobId Level Files Bytes Status Finished Name \n");
506 sendit(msg, strlen(msg), bs);
507 msg = _("===================================================================\n");
508 sendit(msg, strlen(msg), bs);
509 foreach_dlist(je, last_jobs) {
510 char JobName[MAX_NAME_LENGTH];
511 const char *termstat;
514 bstrftime_nc(dt, sizeof(dt), je->end_time);
515 switch (je->JobType) {
518 bstrncpy(level, " ", sizeof(level));
521 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
525 switch (je->JobStatus) {
527 termstat = _("Created");
530 case JS_ErrorTerminated:
531 termstat = _("Error");
534 termstat = _("Diffs");
537 termstat = _("Cancel");
543 termstat = _("Other");
546 bstrncpy(JobName, je->Job, sizeof(JobName));
547 /* There are three periods after the Job name */
549 for (int i=0; i<3; i++) {
550 if ((p=strrchr(JobName, '.')) != NULL) {
554 bsnprintf(buf, sizeof(buf), _("%6d %-6s %8s %10s %-7s %-8s %s\n"),
557 edit_uint64_with_commas(je->JobFiles, b1),
558 edit_uint64_with_suffix(je->JobBytes, b2),
561 sendit(buf, strlen(buf), bs);
563 unlock_last_jobs_list();
564 sendit("====\n", 5, bs);
568 * Convert Job Level into a string
570 static const char *level_to_str(int level)
581 str = _("Incremental");
584 str = _("Differential");
589 case L_VERIFY_CATALOG:
590 str = _("Verify Catalog");
593 str = _("Init Catalog");
595 case L_VERIFY_VOLUME_TO_CATALOG:
596 str = _("Volume to Catalog");
598 case L_VERIFY_DISK_TO_CATALOG:
599 str = _("Disk to Catalog");
608 str = _("Unknown Job Level");
617 static void sendit(const char *msg, int len, BSOCK *bs)
619 memcpy(bs->msg, msg, len+1);
624 static void sendit(const char *msg, int len, void *bs)
626 sendit(msg, len, (BSOCK *)bs);
629 static void sendit(POOL_MEM &msg, int len, BSOCK *bs)
631 memcpy(bs->msg, msg.c_str(), len+1);
638 * Status command from Director
640 bool status_cmd(JCR *jcr)
642 BSOCK *bs = jcr->dir_bsock;
646 bs->signal(BNET_EOD);
651 * .status command from Director
653 bool qstatus_cmd(JCR *jcr)
655 BSOCK *dir = jcr->dir_bsock;
660 if (sscanf(dir->msg, qstatus, time.c_str()) != 1) {
661 pm_strcpy(jcr->errmsg, dir->msg);
662 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
663 dir->fsend(_("3900 Bad .status command, missing argument.\n"));
664 dir->signal(BNET_EOD);
669 if (strcmp(time.c_str(), "current") == 0) {
670 dir->fsend(OKqstatus, time.c_str());
672 if (njcr->JobId != 0) {
673 dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
677 } else if (strcmp(time.c_str(), "last") == 0) {
678 dir->fsend(OKqstatus, time.c_str());
679 if ((last_jobs) && (last_jobs->size() > 0)) {
680 job = (s_last_job*)last_jobs->last();
681 dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
684 pm_strcpy(jcr->errmsg, dir->msg);
685 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
686 dir->fsend(_("3900 Bad .status command, wrong argument.\n"));
687 dir->signal(BNET_EOD);
690 dir->signal(BNET_EOD);
694 #if defined(HAVE_WIN32)
697 /* Return a one line status for the tray monitor */
698 char *bac_status(char *buf, int buf_len)
701 const char *termstat = _("Bacula Storage: Idle");
702 struct s_last_job *job;
703 int stat = 0; /* Idle */
708 Dmsg0(1000, "Begin bac_status jcr loop.\n");
710 if (njcr->JobId != 0) {
712 termstat = _("Bacula Storage: Running");
721 if (last_jobs->size() > 0) {
722 job = (struct s_last_job *)last_jobs->last();
723 stat = job->JobStatus;
724 switch (job->JobStatus) {
726 termstat = _("Bacula Storage: Last Job Canceled");
728 case JS_ErrorTerminated:
730 termstat = _("Bacula Storage: Last Job Failed");
734 termstat = _("Bacula Storage: Last Job had Warnings");
739 Dmsg0(1000, "End bac_status jcr loop.\n");
743 bstrncpy(buf, termstat, buf_len);
748 #endif /* HAVE_WIN32 */