2 Bacula® - The Network Backup Solution
4 Copyright (C) 2003-2007 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 plus additions
11 that are listed in the file LICENSE.
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 send_blocked_status(DEVICE *dev, void sendit(const char *msg, int len, void *sarg), void *arg);
57 static void list_terminated_jobs(void sendit(const char *msg, int len, void *sarg), void *arg);
58 static void list_running_jobs(void sendit(const char *msg, int len, void *sarg), void *arg);
59 static void list_jobs_waiting_on_reservation(void sendit(const char *msg, int len, void *sarg), void *arg);
61 static const char *level_to_str(int level);
64 * Status command from Director
66 void output_status(void sendit(const char *msg, int len, void *sarg), void *arg)
71 char dt[MAX_TIME_LENGTH];
72 char b1[35], b2[35], b3[35], b4[35], b5[35];
77 msg = get_pool_memory(PM_MESSAGE);
79 len = Mmsg(msg, _("%s Version: %s (%s) %s %s %s\n"),
80 my_name, VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
81 sendit(msg, len, arg);
83 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
86 len = Mmsg(msg, _("Daemon started %s, %d Job%s run since started.\n"),
87 dt, num_jobs_run, num_jobs_run == 1 ? "" : "s");
88 sendit(msg, len, arg);
90 len = Mmsg(msg, _(" Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
91 edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
92 edit_uint64_with_commas(sm_bytes, b2),
93 edit_uint64_with_commas(sm_max_bytes, b3),
94 edit_uint64_with_commas(sm_buffers, b4),
95 edit_uint64_with_commas(sm_max_buffers, b5));
96 sendit(msg, len, arg);
101 list_running_jobs(sendit, arg);
104 * List jobs stuck in reservation system
106 list_jobs_waiting_on_reservation(sendit, arg);
109 * List terminated jobs
111 list_terminated_jobs(sendit, arg);
116 len = Mmsg(msg, _("\nDevice status:\n"));
117 sendit(msg, len, arg);
119 foreach_res(changer, R_AUTOCHANGER) {
120 len = Mmsg(msg, _("Autochanger \"%s\" with devices:\n"),
122 sendit(msg, len, arg);
124 foreach_alist(device, changer->device) {
126 len = Mmsg(msg, " %s\n", device->dev->print_name());
127 sendit(msg, len, arg);
129 len = Mmsg(msg, " %s\n", device->hdr.name);
130 sendit(msg, len, arg);
134 foreach_res(device, R_DEVICE) {
136 if (dev && dev->is_open()) {
137 if (dev->is_labeled()) {
138 len = Mmsg(msg, _("Device %s is mounted with:\n"
141 " Media type: %s\n"),
143 dev->VolHdr.VolumeName,
144 dev->pool_name[0]?dev->pool_name:"*unknown*",
145 dev->device->media_type);
146 sendit(msg, len, arg);
148 len = Mmsg(msg, _("Device %s open but no Bacula volume is currently mounted.\n"),
150 sendit(msg, len, arg);
152 send_blocked_status(dev, sendit, arg);
153 if (dev->can_append()) {
154 bpb = dev->VolCatInfo.VolCatBlocks;
158 bpb = dev->VolCatInfo.VolCatBytes / bpb;
159 len = Mmsg(msg, _(" Total Bytes=%s Blocks=%s Bytes/block=%s\n"),
160 edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, b1),
161 edit_uint64_with_commas(dev->VolCatInfo.VolCatBlocks, b2),
162 edit_uint64_with_commas(bpb, b3));
163 sendit(msg, len, arg);
164 } else { /* reading */
165 bpb = dev->VolCatInfo.VolCatReads;
169 if (dev->VolCatInfo.VolCatRBytes > 0) {
170 bpb = dev->VolCatInfo.VolCatRBytes / bpb;
174 len = Mmsg(msg, _(" Total Bytes Read=%s Blocks Read=%s Bytes/block=%s\n"),
175 edit_uint64_with_commas(dev->VolCatInfo.VolCatRBytes, b1),
176 edit_uint64_with_commas(dev->VolCatInfo.VolCatReads, b2),
177 edit_uint64_with_commas(bpb, b3));
178 sendit(msg, len, arg);
180 len = Mmsg(msg, _(" Positioned at File=%s Block=%s\n"),
181 edit_uint64_with_commas(dev->file, b1),
182 edit_uint64_with_commas(dev->block_num, b2));
183 sendit(msg, len, arg);
187 len = Mmsg(msg, _("Device %s is not open.\n"), dev->print_name());
188 sendit(msg, len, arg);
189 send_blocked_status(dev, sendit, arg);
191 len = Mmsg(msg, _("Device \"%s\" is not open or does not exist.\n"), device->hdr.name);
192 sendit(msg, len, arg);
196 sendit("====\n\n", 6, arg);
197 len = Mmsg(msg, _("In Use Volume status:\n"));
198 sendit(msg, len, arg);
199 list_volumes(sendit, arg);
200 sendit("====\n\n", 6, arg);
203 if (debug_level > 10) {
204 bnet_fsend(user, _("====\n\n"));
205 dump_resource(R_DEVICE, resources[R_DEVICE-r_first].res_head, sendit, user);
206 bnet_fsend(user, _("====\n\n"));
210 list_spool_stats(sendit, arg);
212 free_pool_memory(msg);
215 static void send_blocked_status(DEVICE *dev, void sendit(const char *msg, int len, void *sarg), void *arg)
220 msg = (char *)get_pool_memory(PM_MESSAGE);
223 len = Mmsg(msg, _("No DEVICE structure.\n\n"));
224 sendit(msg, len, arg);
225 free_pool_memory(msg);
228 switch (dev->blocked()) {
230 len = Mmsg(msg, _(" Device is BLOCKED. User unmounted.\n"));
231 sendit(msg, len, arg);
233 case BST_UNMOUNTED_WAITING_FOR_SYSOP:
234 len = Mmsg(msg, _(" Device is BLOCKED. User unmounted during wait for media/mount.\n"));
235 sendit(msg, len, arg);
237 case BST_WAITING_FOR_SYSOP:
239 dlist *dcrs = dev->attached_dcrs;
240 bool found_jcr = false;
244 for (dcr = (DCR *)dcrs->first(); dcr != NULL; dcr = (DCR *)dcrs->next(dcr)) {
245 if (dcr->jcr->JobStatus == JS_WaitMount) {
246 len = Mmsg(msg, _(" Device is BLOCKED waiting for mount of volume \"%s\",\n"
248 " Media type: %s\n"),
252 sendit(msg, len, arg);
254 } else if (dcr->jcr->JobStatus == JS_WaitMedia) {
255 len = Mmsg(msg, _(" Device is BLOCKED waiting to create a volume for:\n"
257 " Media type: %s\n"),
260 sendit(msg, len, arg);
267 len = Mmsg(msg, _(" Device is BLOCKED waiting for media.\n"));
268 sendit(msg, len, arg);
272 case BST_DOING_ACQUIRE:
273 len = Mmsg(msg, _(" Device is being initialized.\n"));
274 sendit(msg, len, arg);
276 case BST_WRITING_LABEL:
277 len = Mmsg(msg, _(" Device is blocked labeling a Volume.\n"));
278 sendit(msg, len, arg);
283 /* Send autochanger slot status */
284 if (dev->is_autochanger()) {
286 len = Mmsg(msg, _(" Slot %d is loaded in drive %d.\n"),
287 dev->Slot, dev->drive_index);
288 sendit(msg, len, arg);
289 } else if (dev->Slot == 0) {
290 len = Mmsg(msg, _(" Drive %d is not loaded.\n"), dev->drive_index);
291 sendit(msg, len, arg);
293 len = Mmsg(msg, _(" Drive %d status unknown.\n"), dev->drive_index);
294 sendit(msg, len, arg);
297 if (debug_level > 1) {
298 len = Mmsg(msg, _("Configured device capabilities:\n"));
299 sendit(msg, len, arg);
301 len = Mmsg(msg, "%sEOF %sBSR %sBSF %sFSR %sFSF %sEOM %sREM %sRACCESS %sAUTOMOUNT %sLABEL %sANONVOLS %sALWAYSOPEN\n",
302 dev->capabilities & CAP_EOF ? "" : "!",
303 dev->capabilities & CAP_BSR ? "" : "!",
304 dev->capabilities & CAP_BSF ? "" : "!",
305 dev->capabilities & CAP_FSR ? "" : "!",
306 dev->capabilities & CAP_FSF ? "" : "!",
307 dev->capabilities & CAP_EOM ? "" : "!",
308 dev->capabilities & CAP_REM ? "" : "!",
309 dev->capabilities & CAP_RACCESS ? "" : "!",
310 dev->capabilities & CAP_AUTOMOUNT ? "" : "!",
311 dev->capabilities & CAP_LABEL ? "" : "!",
312 dev->capabilities & CAP_ANONVOLS ? "" : "!",
313 dev->capabilities & CAP_ALWAYSOPEN ? "" : "!");
314 sendit(msg, len, arg);
316 len = Mmsg(msg, _("Device state:\n"));
317 sendit(msg, len, arg);
319 len = Mmsg(msg, "%sOPENED %sTAPE %sLABEL %sMALLOC %sAPPEND %sREAD %sEOT %sWEOT %sEOF %sNEXTVOL %sSHORT %sMOUNTED\n",
320 dev->is_open() ? "" : "!",
321 dev->is_tape() ? "" : "!",
322 dev->is_labeled() ? "" : "!",
323 dev->state & ST_MALLOC ? "" : "!",
324 dev->can_append() ? "" : "!",
325 dev->can_read() ? "" : "!",
326 dev->at_eot() ? "" : "!",
327 dev->state & ST_WEOT ? "" : "!",
328 dev->at_eof() ? "" : "!",
329 dev->state & ST_NEXTVOL ? "" : "!",
330 dev->state & ST_SHORT ? "" : "!",
331 dev->state & ST_MOUNTED ? "" : "!");
332 sendit(msg, len, arg);
334 len = Mmsg(msg, _("num_writers=%d block=%d\n\n"), dev->num_writers, dev->blocked());
335 sendit(msg, len, arg);
337 len = Mmsg(msg, _("Device parameters:\n"));
338 sendit(msg, len, arg);
340 len = Mmsg(msg, _("Archive name: %s Device name: %s\n"), dev->archive_name(),
342 sendit(msg, len, arg);
344 len = Mmsg(msg, _("File=%u block=%u\n"), dev->file, dev->block_num);
345 sendit(msg, len, arg);
347 len = Mmsg(msg, _("Min block=%u Max block=%u\n"), dev->min_block_size, dev->max_block_size);
348 sendit(msg, len, arg);
351 free_pool_memory(msg);
354 static void list_running_jobs(void sendit(const char *msg, int len, void *sarg), void *arg)
360 char JobName[MAX_NAME_LENGTH];
361 char *msg, b1[30], b2[30], b3[30];
364 msg = (char *)get_pool_memory(PM_MESSAGE);
366 len = Mmsg(msg, _("\nRunning Jobs:\n"));
367 sendit(msg, len, arg);
370 if (jcr->JobStatus == JS_WaitFD) {
371 len = Mmsg(msg, _("%s Job %s waiting for Client connection.\n"),
372 job_type_to_str(jcr->JobType), jcr->Job);
373 sendit(msg, len, arg);
376 rdcr = jcr->read_dcr;
377 if ((dcr && dcr->device) || rdcr && rdcr->device) {
378 bstrncpy(JobName, jcr->Job, sizeof(JobName));
379 /* There are three periods after the Job name */
381 for (int i=0; i<3; i++) {
382 if ((p=strrchr(JobName, '.')) != NULL) {
386 if (rdcr && rdcr->device) {
387 len = Mmsg(msg, _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
388 " pool=\"%s\" device=%s\n"),
389 job_level_to_str(jcr->JobLevel),
390 job_type_to_str(jcr->JobType),
395 rdcr->dev?rdcr->dev->print_name():
396 rdcr->device->device_name);
397 sendit(msg, len, arg);
399 if (dcr && dcr->device) {
400 len = Mmsg(msg, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
401 " pool=\"%s\" device=%s\n"),
402 job_level_to_str(jcr->JobLevel),
403 job_type_to_str(jcr->JobType),
408 dcr->dev?dcr->dev->print_name():
409 dcr->device->device_name);
410 sendit(msg, len, arg);
411 len= Mmsg(msg, _(" spooling=%d despooling=%d despool_wait=%d\n"),
412 dcr->spooling, dcr->despooling, dcr->despool_wait);
413 sendit(msg, len, arg);
415 sec = time(NULL) - jcr->run_time;
419 bps = jcr->JobBytes / sec;
420 len = Mmsg(msg, _(" Files=%s Bytes=%s Bytes/sec=%s\n"),
421 edit_uint64_with_commas(jcr->JobFiles, b1),
422 edit_uint64_with_commas(jcr->JobBytes, b2),
423 edit_uint64_with_commas(bps, b3));
424 sendit(msg, len, arg);
427 if (jcr->file_bsock) {
428 len = Mmsg(msg, _(" FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
429 edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
430 jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
431 jcr->file_bsock->m_fd);
432 sendit(msg, len, arg);
434 len = Mmsg(msg, _(" FDSocket closed\n"));
435 sendit(msg, len, arg);
443 len = Mmsg(msg, _("No Jobs running.\n"));
444 sendit(msg, len, arg);
446 sendit("====\n", 5, arg);
448 free_pool_memory(msg);
451 static void list_jobs_waiting_on_reservation(void sendit(const char *msg, int len, void *sarg), void *arg)
456 msg = _("\nJobs waiting to reserve a drive:\n");
457 sendit(msg, strlen(msg), arg);
460 if (!jcr->reserve_msgs) {
463 send_drive_reserve_messages(jcr, sendit, arg);
467 sendit("====\n", 5, arg);
471 static void list_terminated_jobs(void sendit(const char *msg, int len, void *sarg), void *arg)
473 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
475 struct s_last_job *je;
478 msg = _("\nTerminated Jobs:\n");
479 sendit(msg, strlen(msg), arg);
480 if (last_jobs->size() == 0) {
481 sendit("====\n", 5, arg);
484 lock_last_jobs_list();
485 msg = _(" JobId Level Files Bytes Status Finished Name \n");
486 sendit(msg, strlen(msg), arg);
487 msg = _("===================================================================\n");
488 sendit(msg, strlen(msg), arg);
489 foreach_dlist(je, last_jobs) {
490 char JobName[MAX_NAME_LENGTH];
491 const char *termstat;
494 bstrftime_nc(dt, sizeof(dt), je->end_time);
495 switch (je->JobType) {
498 bstrncpy(level, " ", sizeof(level));
501 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
505 switch (je->JobStatus) {
507 termstat = _("Created");
510 case JS_ErrorTerminated:
511 termstat = _("Error");
514 termstat = _("Diffs");
517 termstat = _("Cancel");
523 termstat = _("Other");
526 bstrncpy(JobName, je->Job, sizeof(JobName));
527 /* There are three periods after the Job name */
529 for (int i=0; i<3; i++) {
530 if ((p=strrchr(JobName, '.')) != NULL) {
534 bsnprintf(buf, sizeof(buf), _("%6d %-6s %8s %10s %-7s %-8s %s\n"),
537 edit_uint64_with_commas(je->JobFiles, b1),
538 edit_uint64_with_suffix(je->JobBytes, b2),
541 sendit(buf, strlen(buf), arg);
543 unlock_last_jobs_list();
544 sendit("====\n", 5, arg);
548 * Convert Job Level into a string
550 static const char *level_to_str(int level)
561 str = _("Incremental");
564 str = _("Differential");
569 case L_VERIFY_CATALOG:
570 str = _("Verify Catalog");
573 str = _("Init Catalog");
575 case L_VERIFY_VOLUME_TO_CATALOG:
576 str = _("Volume to Catalog");
578 case L_VERIFY_DISK_TO_CATALOG:
579 str = _("Disk to Catalog");
588 str = _("Unknown Job Level");
597 static void bsock_sendit(const char *msg, int len, void *arg)
599 BSOCK *user = (BSOCK *)arg;
601 memcpy(user->msg, msg, len+1);
602 user->msglen = len+1;
607 * Status command from Director
609 bool status_cmd(JCR *jcr)
611 BSOCK *user = jcr->dir_bsock;
613 bnet_fsend(user, "\n");
614 output_status(bsock_sendit, (void *)user);
616 bnet_sig(user, BNET_EOD);
621 * .status command from Director
623 bool qstatus_cmd(JCR *jcr)
625 BSOCK *dir = jcr->dir_bsock;
630 if (sscanf(dir->msg, qstatus, time.c_str()) != 1) {
631 pm_strcpy(jcr->errmsg, dir->msg);
632 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
633 bnet_fsend(dir, _("3900 Bad .status command, missing argument.\n"));
634 bnet_sig(dir, BNET_EOD);
639 if (strcmp(time.c_str(), "current") == 0) {
640 bnet_fsend(dir, OKqstatus, time.c_str());
642 if (njcr->JobId != 0) {
643 bnet_fsend(dir, DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
647 } else if (strcmp(time.c_str(), "last") == 0) {
648 bnet_fsend(dir, OKqstatus, time.c_str());
649 if ((last_jobs) && (last_jobs->size() > 0)) {
650 job = (s_last_job*)last_jobs->last();
651 bnet_fsend(dir, DotStatusJob, job->JobId, job->JobStatus, job->Errors);
654 pm_strcpy(jcr->errmsg, dir->msg);
655 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
656 bnet_fsend(dir, _("3900 Bad .status command, wrong argument.\n"));
657 bnet_sig(dir, BNET_EOD);
660 bnet_sig(dir, BNET_EOD);
664 #if defined(HAVE_WIN32)
667 char *bac_status(char *buf, int buf_len)
670 const char *termstat = _("Bacula Storage: Idle");
671 struct s_last_job *job;
672 int stat = 0; /* Idle */
677 Dmsg0(1000, "Begin bac_status jcr loop.\n");
679 if (njcr->JobId != 0) {
681 termstat = _("Bacula Storage: Running");
690 if (last_jobs->size() > 0) {
691 job = (struct s_last_job *)last_jobs->last();
692 stat = job->JobStatus;
693 switch (job->JobStatus) {
695 termstat = _("Bacula Storage: Last Job Canceled");
697 case JS_ErrorTerminated:
699 termstat = _("Bacula Storage: Last Job Failed");
703 termstat = _("Bacula Storage: Last Job had Warnings");
708 Dmsg0(1000, "End bac_status jcr loop.\n");
712 bstrncpy(buf, termstat, buf_len);
717 #endif /* HAVE_WIN32 */