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 Volume=\"%s\" Pool=\"%s\"\n"),
139 dev->print_name(), dev->VolHdr.VolumeName,
140 dev->pool_name[0]?dev->pool_name:"*unknown*");
141 sendit(msg, len, arg);
143 len = Mmsg(msg, _("Device %s open but no Bacula volume is currently mounted.\n"),
145 sendit(msg, len, arg);
147 send_blocked_status(dev, sendit, arg);
148 if (dev->can_append()) {
149 bpb = dev->VolCatInfo.VolCatBlocks;
153 bpb = dev->VolCatInfo.VolCatBytes / bpb;
154 len = Mmsg(msg, _(" Total Bytes=%s Blocks=%s Bytes/block=%s\n"),
155 edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, b1),
156 edit_uint64_with_commas(dev->VolCatInfo.VolCatBlocks, b2),
157 edit_uint64_with_commas(bpb, b3));
158 sendit(msg, len, arg);
159 } else { /* reading */
160 bpb = dev->VolCatInfo.VolCatReads;
164 if (dev->VolCatInfo.VolCatRBytes > 0) {
165 bpb = dev->VolCatInfo.VolCatRBytes / bpb;
169 len = Mmsg(msg, _(" Total Bytes Read=%s Blocks Read=%s Bytes/block=%s\n"),
170 edit_uint64_with_commas(dev->VolCatInfo.VolCatRBytes, b1),
171 edit_uint64_with_commas(dev->VolCatInfo.VolCatReads, b2),
172 edit_uint64_with_commas(bpb, b3));
173 sendit(msg, len, arg);
175 len = Mmsg(msg, _(" Positioned at File=%s Block=%s\n"),
176 edit_uint64_with_commas(dev->file, b1),
177 edit_uint64_with_commas(dev->block_num, b2));
178 sendit(msg, len, arg);
182 len = Mmsg(msg, _("Device %s is not open.\n"), dev->print_name());
183 sendit(msg, len, arg);
184 send_blocked_status(dev, sendit, arg);
186 len = Mmsg(msg, _("Device \"%s\" is not open or does not exist.\n"), device->hdr.name);
187 sendit(msg, len, arg);
191 sendit("====\n\n", 6, arg);
192 len = Mmsg(msg, _("In Use Volume status:\n"));
193 sendit(msg, len, arg);
194 list_volumes(sendit, arg);
195 sendit("====\n\n", 6, arg);
198 if (debug_level > 10) {
199 bnet_fsend(user, _("====\n\n"));
200 dump_resource(R_DEVICE, resources[R_DEVICE-r_first].res_head, sendit, user);
201 bnet_fsend(user, _("====\n\n"));
205 list_spool_stats(sendit, arg);
207 free_pool_memory(msg);
210 static void send_blocked_status(DEVICE *dev, void sendit(const char *msg, int len, void *sarg), void *arg)
215 msg = (char *)get_pool_memory(PM_MESSAGE);
218 len = Mmsg(msg, _("No DEVICE structure.\n\n"));
219 sendit(msg, len, arg);
220 free_pool_memory(msg);
223 switch (dev->blocked()) {
225 len = Mmsg(msg, _(" Device is BLOCKED. User unmounted.\n"));
226 sendit(msg, len, arg);
228 case BST_UNMOUNTED_WAITING_FOR_SYSOP:
229 len = Mmsg(msg, _(" Device is BLOCKED. User unmounted during wait for media/mount.\n"));
230 sendit(msg, len, arg);
232 case BST_WAITING_FOR_SYSOP:
234 dlist *dcrs = dev->attached_dcrs;
235 bool found_jcr = false;
240 for (dcr = (DCR *)dcrs->first(); dcr != NULL; dcr = (DCR *)dcrs->next(dcr)) {
241 if (dcr->jcr->JobStatus == JS_WaitMount) {
242 len = Mmsg(msg, _(" Device is BLOCKED waiting for mount of volume \"%s\".\n"),
244 sendit(msg, len, arg);
251 len = Mmsg(msg, _(" Device is BLOCKED waiting for media.\n"));
252 sendit(msg, len, arg);
256 case BST_DOING_ACQUIRE:
257 len = Mmsg(msg, _(" Device is being initialized.\n"));
258 sendit(msg, len, arg);
260 case BST_WRITING_LABEL:
261 len = Mmsg(msg, _(" Device is blocked labeling a Volume.\n"));
262 sendit(msg, len, arg);
267 /* Send autochanger slot status */
268 if (dev->is_autochanger()) {
270 len = Mmsg(msg, _(" Slot %d is loaded in drive %d.\n"),
271 dev->Slot, dev->drive_index);
272 sendit(msg, len, arg);
273 } else if (dev->Slot == 0) {
274 len = Mmsg(msg, _(" Drive %d is not loaded.\n"), dev->drive_index);
275 sendit(msg, len, arg);
277 len = Mmsg(msg, _(" Drive %d status unknown.\n"), dev->drive_index);
278 sendit(msg, len, arg);
281 if (debug_level > 1) {
282 len = Mmsg(msg, _("Configured device capabilities:\n"));
283 sendit(msg, len, arg);
285 len = Mmsg(msg, "%sEOF %sBSR %sBSF %sFSR %sFSF %sEOM %sREM %sRACCESS %sAUTOMOUNT %sLABEL %sANONVOLS %sALWAYSOPEN\n",
286 dev->capabilities & CAP_EOF ? "" : "!",
287 dev->capabilities & CAP_BSR ? "" : "!",
288 dev->capabilities & CAP_BSF ? "" : "!",
289 dev->capabilities & CAP_FSR ? "" : "!",
290 dev->capabilities & CAP_FSF ? "" : "!",
291 dev->capabilities & CAP_EOM ? "" : "!",
292 dev->capabilities & CAP_REM ? "" : "!",
293 dev->capabilities & CAP_RACCESS ? "" : "!",
294 dev->capabilities & CAP_AUTOMOUNT ? "" : "!",
295 dev->capabilities & CAP_LABEL ? "" : "!",
296 dev->capabilities & CAP_ANONVOLS ? "" : "!",
297 dev->capabilities & CAP_ALWAYSOPEN ? "" : "!");
298 sendit(msg, len, arg);
300 len = Mmsg(msg, _("Device state:\n"));
301 sendit(msg, len, arg);
303 len = Mmsg(msg, "%sOPENED %sTAPE %sLABEL %sMALLOC %sAPPEND %sREAD %sEOT %sWEOT %sEOF %sNEXTVOL %sSHORT %sMOUNTED\n",
304 dev->is_open() ? "" : "!",
305 dev->is_tape() ? "" : "!",
306 dev->is_labeled() ? "" : "!",
307 dev->state & ST_MALLOC ? "" : "!",
308 dev->can_append() ? "" : "!",
309 dev->can_read() ? "" : "!",
310 dev->at_eot() ? "" : "!",
311 dev->state & ST_WEOT ? "" : "!",
312 dev->at_eof() ? "" : "!",
313 dev->state & ST_NEXTVOL ? "" : "!",
314 dev->state & ST_SHORT ? "" : "!",
315 dev->state & ST_MOUNTED ? "" : "!");
316 sendit(msg, len, arg);
318 len = Mmsg(msg, _("num_writers=%d block=%d\n\n"), dev->num_writers, dev->blocked());
319 sendit(msg, len, arg);
321 len = Mmsg(msg, _("Device parameters:\n"));
322 sendit(msg, len, arg);
324 len = Mmsg(msg, _("Archive name: %s Device name: %s\n"), dev->archive_name(),
326 sendit(msg, len, arg);
328 len = Mmsg(msg, _("File=%u block=%u\n"), dev->file, dev->block_num);
329 sendit(msg, len, arg);
331 len = Mmsg(msg, _("Min block=%u Max block=%u\n"), dev->min_block_size, dev->max_block_size);
332 sendit(msg, len, arg);
335 free_pool_memory(msg);
338 static void list_running_jobs(void sendit(const char *msg, int len, void *sarg), void *arg)
344 char JobName[MAX_NAME_LENGTH];
345 char *msg, b1[30], b2[30], b3[30];
348 msg = (char *)get_pool_memory(PM_MESSAGE);
350 len = Mmsg(msg, _("\nRunning Jobs:\n"));
351 sendit(msg, len, arg);
354 if (jcr->JobStatus == JS_WaitFD) {
355 len = Mmsg(msg, _("%s Job %s waiting for Client connection.\n"),
356 job_type_to_str(jcr->JobType), jcr->Job);
357 sendit(msg, len, arg);
360 rdcr = jcr->read_dcr;
361 if ((dcr && dcr->device) || rdcr && rdcr->device) {
362 bstrncpy(JobName, jcr->Job, sizeof(JobName));
363 /* There are three periods after the Job name */
365 for (int i=0; i<3; i++) {
366 if ((p=strrchr(JobName, '.')) != NULL) {
370 if (rdcr && rdcr->device) {
371 len = Mmsg(msg, _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
372 " pool=\"%s\" device=%s\n"),
373 job_level_to_str(jcr->JobLevel),
374 job_type_to_str(jcr->JobType),
379 rdcr->dev?rdcr->dev->print_name():
380 rdcr->device->device_name);
381 sendit(msg, len, arg);
383 if (dcr && dcr->device) {
384 len = Mmsg(msg, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
385 " pool=\"%s\" device=%s\n"),
386 job_level_to_str(jcr->JobLevel),
387 job_type_to_str(jcr->JobType),
392 dcr->dev?dcr->dev->print_name():
393 dcr->device->device_name);
394 sendit(msg, len, arg);
395 len= Mmsg(msg, _(" spooling=%d despooling=%d despool_wait=%d\n"),
396 dcr->spooling, dcr->despooling, dcr->despool_wait);
397 sendit(msg, len, arg);
399 sec = time(NULL) - jcr->run_time;
403 bps = jcr->JobBytes / sec;
404 len = Mmsg(msg, _(" Files=%s Bytes=%s Bytes/sec=%s\n"),
405 edit_uint64_with_commas(jcr->JobFiles, b1),
406 edit_uint64_with_commas(jcr->JobBytes, b2),
407 edit_uint64_with_commas(bps, b3));
408 sendit(msg, len, arg);
411 if (jcr->file_bsock) {
412 len = Mmsg(msg, _(" FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
413 edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
414 jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
415 jcr->file_bsock->fd);
416 sendit(msg, len, arg);
418 len = Mmsg(msg, _(" FDSocket closed\n"));
419 sendit(msg, len, arg);
427 len = Mmsg(msg, _("No Jobs running.\n"));
428 sendit(msg, len, arg);
430 sendit("====\n", 5, arg);
432 free_pool_memory(msg);
435 static void list_jobs_waiting_on_reservation(void sendit(const char *msg, int len, void *sarg), void *arg)
440 msg = _("\nJobs waiting to reserve a drive:\n");
441 sendit(msg, strlen(msg), arg);
444 if (!jcr->reserve_msgs) {
447 send_drive_reserve_messages(jcr, sendit, arg);
451 sendit("====\n", 5, arg);
455 static void list_terminated_jobs(void sendit(const char *msg, int len, void *sarg), void *arg)
457 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
459 struct s_last_job *je;
462 msg = _("\nTerminated Jobs:\n");
463 sendit(msg, strlen(msg), arg);
464 if (last_jobs->size() == 0) {
465 sendit("====\n", 5, arg);
468 lock_last_jobs_list();
469 msg = _(" JobId Level Files Bytes Status Finished Name \n");
470 sendit(msg, strlen(msg), arg);
471 msg = _("===================================================================\n");
472 sendit(msg, strlen(msg), arg);
473 foreach_dlist(je, last_jobs) {
474 char JobName[MAX_NAME_LENGTH];
475 const char *termstat;
478 bstrftime_nc(dt, sizeof(dt), je->end_time);
479 switch (je->JobType) {
482 bstrncpy(level, " ", sizeof(level));
485 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
489 switch (je->JobStatus) {
491 termstat = _("Created");
494 case JS_ErrorTerminated:
495 termstat = _("Error");
498 termstat = _("Diffs");
501 termstat = _("Cancel");
507 termstat = _("Other");
510 bstrncpy(JobName, je->Job, sizeof(JobName));
511 /* There are three periods after the Job name */
513 for (int i=0; i<3; i++) {
514 if ((p=strrchr(JobName, '.')) != NULL) {
518 bsnprintf(buf, sizeof(buf), _("%6d %-6s %8s %10s %-7s %-8s %s\n"),
521 edit_uint64_with_commas(je->JobFiles, b1),
522 edit_uint64_with_suffix(je->JobBytes, b2),
525 sendit(buf, strlen(buf), arg);
527 unlock_last_jobs_list();
528 sendit("====\n", 5, arg);
532 * Convert Job Level into a string
534 static const char *level_to_str(int level)
545 str = _("Incremental");
548 str = _("Differential");
553 case L_VERIFY_CATALOG:
554 str = _("Verify Catalog");
557 str = _("Init Catalog");
559 case L_VERIFY_VOLUME_TO_CATALOG:
560 str = _("Volume to Catalog");
562 case L_VERIFY_DISK_TO_CATALOG:
563 str = _("Disk to Catalog");
572 str = _("Unknown Job Level");
581 static void bsock_sendit(const char *msg, int len, void *arg)
583 BSOCK *user = (BSOCK *)arg;
585 memcpy(user->msg, msg, len+1);
586 user->msglen = len+1;
591 * Status command from Director
593 bool status_cmd(JCR *jcr)
595 BSOCK *user = jcr->dir_bsock;
597 bnet_fsend(user, "\n");
598 output_status(bsock_sendit, (void *)user);
600 bnet_sig(user, BNET_EOD);
605 * .status command from Director
607 bool qstatus_cmd(JCR *jcr)
609 BSOCK *dir = jcr->dir_bsock;
614 if (sscanf(dir->msg, qstatus, time.c_str()) != 1) {
615 pm_strcpy(jcr->errmsg, dir->msg);
616 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
617 bnet_fsend(dir, _("3900 Bad .status command, missing argument.\n"));
618 bnet_sig(dir, BNET_EOD);
623 if (strcmp(time.c_str(), "current") == 0) {
624 bnet_fsend(dir, OKqstatus, time.c_str());
626 if (njcr->JobId != 0) {
627 bnet_fsend(dir, DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
631 } else if (strcmp(time.c_str(), "last") == 0) {
632 bnet_fsend(dir, OKqstatus, time.c_str());
633 if ((last_jobs) && (last_jobs->size() > 0)) {
634 job = (s_last_job*)last_jobs->last();
635 bnet_fsend(dir, DotStatusJob, job->JobId, job->JobStatus, job->Errors);
638 pm_strcpy(jcr->errmsg, dir->msg);
639 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
640 bnet_fsend(dir, _("3900 Bad .status command, wrong argument.\n"));
641 bnet_sig(dir, BNET_EOD);
644 bnet_sig(dir, BNET_EOD);
648 #if defined(HAVE_WIN32)
651 char *bac_status(char *buf, int buf_len)
654 const char *termstat = _("Bacula Storage: Idle");
655 struct s_last_job *job;
656 int stat = 0; /* Idle */
661 Dmsg0(1000, "Begin bac_status jcr loop.\n");
663 if (njcr->JobId != 0) {
665 termstat = _("Bacula Storage: Running");
674 if (last_jobs->size() > 0) {
675 job = (struct s_last_job *)last_jobs->last();
676 stat = job->JobStatus;
677 switch (job->JobStatus) {
679 termstat = _("Bacula Storage: Last Job Canceled");
681 case JS_ErrorTerminated:
683 termstat = _("Bacula Storage: Last Job Failed");
687 termstat = _("Bacula Storage: Last Job had Warnings");
692 Dmsg0(1000, "End bac_status jcr loop.\n");
696 bstrncpy(buf, termstat, buf_len);
701 #endif /* HAVE_WIN32 */