2 * This file handles the status command
4 * Kern Sibbald, May MMIII
10 Copyright (C) 2003-2006 Kern Sibbald
12 This program is free software; you can redistribute it and/or
13 modify it under the terms of the GNU General Public License
14 version 2 as amended with additional clauses defined in the
15 file LICENSE in the main source directory.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 the file LICENSE for additional details.
27 /* Exported variables */
29 /* Imported variables */
30 extern BSOCK *filed_chan;
31 extern int r_first, r_last;
32 extern struct s_res resources[];
34 /* Static variables */
35 static char qstatus[] = ".status %127s\n";
37 static char OKqstatus[] = "3000 OK .status\n";
38 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
41 /* Forward referenced functions */
42 static void send_blocked_status(DEVICE *dev, void sendit(const char *msg, int len, void *sarg), void *arg);
43 static void list_terminated_jobs(void sendit(const char *msg, int len, void *sarg), void *arg);
44 static void list_running_jobs(void sendit(const char *msg, int len, void *sarg), void *arg);
45 static void list_jobs_waiting_on_reservation(void sendit(const char *msg, int len, void *sarg), void *arg);
46 #if defined(HAVE_WIN32)
47 static void win32_sendit(const char *msg, int len, void *arg);
50 static const char *level_to_str(int level);
53 * Status command from Director
55 bool do_status(void sendit(const char *msg, int len, void *sarg), void *arg)
60 char dt[MAX_TIME_LENGTH];
61 char *msg, b1[35], b2[35], b3[35], b4[35];
65 msg = (char *)get_pool_memory(PM_MESSAGE);
67 len = Mmsg(msg, _("%s Version: %s (%s) %s %s %s\n"),
68 my_name, VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
69 sendit(msg, len, arg);
71 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
74 len = Mmsg(msg, _("Daemon started %s, %d Job%s run since started.\n"),
75 dt, num_jobs_run, num_jobs_run == 1 ? "" : "s");
76 sendit(msg, len, arg);
78 len = Mmsg(msg, _(" Heap: bytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
79 edit_uint64_with_commas(sm_bytes, b1),
80 edit_uint64_with_commas(sm_max_bytes, b2),
81 edit_uint64_with_commas(sm_buffers, b3),
82 edit_uint64_with_commas(sm_max_buffers, b4));
83 sendit(msg, len, arg);
88 list_running_jobs(sendit, arg);
91 * List jobs stuck in reservation system
93 list_jobs_waiting_on_reservation(sendit, arg);
96 * List terminated jobs
98 list_terminated_jobs(sendit, arg);
103 len = Mmsg(msg, _("\nDevice status:\n"));
104 sendit(msg, len, arg);
106 foreach_res(changer, R_AUTOCHANGER) {
107 len = Mmsg(msg, _("Autochanger \"%s\" with devices:\n"),
109 sendit(msg, len, arg);
111 foreach_alist(device, changer->device) {
113 len = Mmsg(msg, " %s\n", device->dev->print_name());
114 sendit(msg, len, arg);
116 len = Mmsg(msg, " %s\n", device->hdr.name);
117 sendit(msg, len, arg);
121 foreach_res(device, R_DEVICE) {
123 if (dev && dev->is_open()) {
124 if (dev->is_labeled()) {
125 len = Mmsg(msg, _("Device %s is mounted with Volume=\"%s\" Pool=\"%s\"\n"),
126 dev->print_name(), dev->VolHdr.VolumeName,
127 dev->pool_name[0]?dev->pool_name:"*unknown*");
128 sendit(msg, len, arg);
130 len = Mmsg(msg, _("Device %s open but no Bacula volume is mounted.\n"),
132 sendit(msg, len, arg);
134 send_blocked_status(dev, sendit, arg);
135 if (dev->can_append()) {
136 bpb = dev->VolCatInfo.VolCatBlocks;
140 bpb = dev->VolCatInfo.VolCatBytes / bpb;
141 len = Mmsg(msg, _(" Total Bytes=%s Blocks=%s Bytes/block=%s\n"),
142 edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, b1),
143 edit_uint64_with_commas(dev->VolCatInfo.VolCatBlocks, b2),
144 edit_uint64_with_commas(bpb, b3));
145 sendit(msg, len, arg);
146 } else { /* reading */
147 bpb = dev->VolCatInfo.VolCatReads;
151 if (dev->VolCatInfo.VolCatRBytes > 0) {
152 bpb = dev->VolCatInfo.VolCatRBytes / bpb;
156 len = Mmsg(msg, _(" Total Bytes Read=%s Blocks Read=%s Bytes/block=%s\n"),
157 edit_uint64_with_commas(dev->VolCatInfo.VolCatRBytes, b1),
158 edit_uint64_with_commas(dev->VolCatInfo.VolCatReads, b2),
159 edit_uint64_with_commas(bpb, b3));
160 sendit(msg, len, arg);
162 len = Mmsg(msg, _(" Positioned at File=%s Block=%s\n"),
163 edit_uint64_with_commas(dev->file, b1),
164 edit_uint64_with_commas(dev->block_num, b2));
165 sendit(msg, len, arg);
169 len = Mmsg(msg, _("Device %s is not open.\n"), dev->print_name());
170 sendit(msg, len, arg);
172 len = Mmsg(msg, _("Device \"%s\" is not open or does not exist.\n"), device->hdr.name);
173 sendit(msg, len, arg);
175 send_blocked_status(dev, sendit, arg);
178 len = Mmsg(msg, _("====\n\n"));
179 sendit(msg, len, arg);
180 len = Mmsg(msg, _("In Use Volume status:\n"));
181 sendit(msg, len, arg);
182 list_volumes(sendit, arg);
183 len = Mmsg(msg, _("====\n\n"));
184 sendit(msg, len, arg);
187 if (debug_level > 10) {
188 bnet_fsend(user, _("====\n\n"));
189 dump_resource(R_DEVICE, resources[R_DEVICE-r_first].res_head, sendit, user);
190 bnet_fsend(user, _("====\n\n"));
194 list_spool_stats(sendit, arg);
196 free_pool_memory(msg);
200 static void send_blocked_status(DEVICE *dev, void sendit(const char *msg, int len, void *sarg), void *arg)
205 msg = (char *)get_pool_memory(PM_MESSAGE);
208 len = Mmsg(msg, _("No DEVICE structure.\n\n"));
209 sendit(msg, len, arg);
210 free_pool_memory(msg);
213 switch (dev->dev_blocked) {
215 len = Mmsg(msg, _(" Device is BLOCKED. User unmounted.\n"));
216 sendit(msg, len, arg);
218 case BST_UNMOUNTED_WAITING_FOR_SYSOP:
219 len = Mmsg(msg, _(" Device is BLOCKED. User unmounted during wait for media/mount.\n"));
220 sendit(msg, len, arg);
222 case BST_WAITING_FOR_SYSOP:
224 dlist *dcrs = dev->attached_dcrs;
225 bool found_jcr = false;
230 for (dcr = (DCR *)dcrs->first(); dcr != NULL; dcr = (DCR *)dcrs->next(dcr)) {
231 if (dcr->jcr->JobStatus == JS_WaitMount) {
232 len = Mmsg(msg, _(" Device is BLOCKED waiting for mount of volume \"%s\".\n"),
234 sendit(msg, len, arg);
241 len = Mmsg(msg, _(" Device is BLOCKED waiting for media.\n"));
242 sendit(msg, len, arg);
246 case BST_DOING_ACQUIRE:
247 len = Mmsg(msg, _(" Device is being initialized.\n"));
248 sendit(msg, len, arg);
250 case BST_WRITING_LABEL:
251 len = Mmsg(msg, _(" Device is blocked labeling a Volume.\n"));
252 sendit(msg, len, arg);
257 /* Send autochanger slot status */
258 if (dev->is_autochanger()) {
260 len = Mmsg(msg, _(" Slot %d is loaded in drive %d.\n"),
261 dev->Slot, dev->drive_index);
262 sendit(msg, len, arg);
263 } else if (dev->Slot == 0) {
264 len = Mmsg(msg, _(" Drive %d is not loaded.\n"), dev->drive_index);
265 sendit(msg, len, arg);
267 len = Mmsg(msg, _(" Drive %d status unknown.\n"), dev->drive_index);
268 sendit(msg, len, arg);
271 if (debug_level > 1) {
272 len = Mmsg(msg, _("Configured device capabilities:\n"));
273 sendit(msg, len, arg);
275 len = Mmsg(msg, "%sEOF %sBSR %sBSF %sFSR %sFSF %sEOM %sREM %sRACCESS %sAUTOMOUNT %sLABEL %sANONVOLS %sALWAYSOPEN\n",
276 dev->capabilities & CAP_EOF ? "" : "!",
277 dev->capabilities & CAP_BSR ? "" : "!",
278 dev->capabilities & CAP_BSF ? "" : "!",
279 dev->capabilities & CAP_FSR ? "" : "!",
280 dev->capabilities & CAP_FSF ? "" : "!",
281 dev->capabilities & CAP_EOM ? "" : "!",
282 dev->capabilities & CAP_REM ? "" : "!",
283 dev->capabilities & CAP_RACCESS ? "" : "!",
284 dev->capabilities & CAP_AUTOMOUNT ? "" : "!",
285 dev->capabilities & CAP_LABEL ? "" : "!",
286 dev->capabilities & CAP_ANONVOLS ? "" : "!",
287 dev->capabilities & CAP_ALWAYSOPEN ? "" : "!");
288 sendit(msg, len, arg);
290 len = Mmsg(msg, _("Device state:\n"));
291 sendit(msg, len, arg);
293 len = Mmsg(msg, "%sOPENED %sTAPE %sLABEL %sMALLOC %sAPPEND %sREAD %sEOT %sWEOT %sEOF %sNEXTVOL %sSHORT %sMOUNTED\n",
294 dev->is_open() ? "" : "!",
295 dev->is_tape() ? "" : "!",
296 dev->is_labeled() ? "" : "!",
297 dev->state & ST_MALLOC ? "" : "!",
298 dev->can_append() ? "" : "!",
299 dev->can_read() ? "" : "!",
300 dev->at_eot() ? "" : "!",
301 dev->state & ST_WEOT ? "" : "!",
302 dev->at_eof() ? "" : "!",
303 dev->state & ST_NEXTVOL ? "" : "!",
304 dev->state & ST_SHORT ? "" : "!",
305 dev->state & ST_MOUNTED ? "" : "!");
306 sendit(msg, len, arg);
308 len = Mmsg(msg, _("num_writers=%d block=%d\n\n"), dev->num_writers, dev->dev_blocked);
309 sendit(msg, len, arg);
311 len = Mmsg(msg, _("Device parameters:\n"));
312 sendit(msg, len, arg);
314 len = Mmsg(msg, _("Archive name: %s Device name: %s\n"), dev->archive_name(),
316 sendit(msg, len, arg);
318 len = Mmsg(msg, _("File=%u block=%u\n"), dev->file, dev->block_num);
319 sendit(msg, len, arg);
321 len = Mmsg(msg, _("Min block=%u Max block=%u\n"), dev->min_block_size, dev->max_block_size);
322 sendit(msg, len, arg);
325 free_pool_memory(msg);
328 static void list_running_jobs(void sendit(const char *msg, int len, void *sarg), void *arg)
334 char JobName[MAX_NAME_LENGTH];
335 char *msg, b1[30], b2[30], b3[30];
338 msg = (char *)get_pool_memory(PM_MESSAGE);
340 len = Mmsg(msg, _("\nRunning Jobs:\n"));
341 sendit(msg, len, arg);
344 if (jcr->JobStatus == JS_WaitFD) {
345 len = Mmsg(msg, _("%s Job %s waiting for Client connection.\n"),
346 job_type_to_str(jcr->JobType), jcr->Job);
347 sendit(msg, len, arg);
350 rdcr = jcr->read_dcr;
351 if ((dcr && dcr->device) || rdcr && rdcr->device) {
352 bstrncpy(JobName, jcr->Job, sizeof(JobName));
353 /* There are three periods after the Job name */
355 for (int i=0; i<3; i++) {
356 if ((p=strrchr(JobName, '.')) != NULL) {
360 if (rdcr && rdcr->device) {
361 len = Mmsg(msg, _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
362 " pool=\"%s\" device=\"%s\"\n"),
363 job_level_to_str(jcr->JobLevel),
364 job_type_to_str(jcr->JobType),
369 rdcr->dev?rdcr->dev->print_name():
370 rdcr->device->device_name);
371 sendit(msg, len, arg);
373 if (dcr && dcr->device) {
374 len = Mmsg(msg, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
375 " pool=\"%s\" device=\"%s\"\n"),
376 job_level_to_str(jcr->JobLevel),
377 job_type_to_str(jcr->JobType),
382 dcr->dev?dcr->dev->print_name():
383 dcr->device->device_name);
384 sendit(msg, len, arg);
386 sec = time(NULL) - jcr->run_time;
390 bps = jcr->JobBytes / sec;
391 len = Mmsg(msg, _(" Files=%s Bytes=%s Bytes/sec=%s\n"),
392 edit_uint64_with_commas(jcr->JobFiles, b1),
393 edit_uint64_with_commas(jcr->JobBytes, b2),
394 edit_uint64_with_commas(bps, b3));
395 sendit(msg, len, arg);
398 if (jcr->file_bsock) {
399 len = Mmsg(msg, _(" FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
400 edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
401 jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
402 jcr->file_bsock->fd);
403 sendit(msg, len, arg);
405 len = Mmsg(msg, _(" FDSocket closed\n"));
406 sendit(msg, len, arg);
414 len = Mmsg(msg, _("No Jobs running.\n"));
415 sendit(msg, len, arg);
417 len = Mmsg(msg, _("====\n"));
418 sendit(msg, len, arg);
420 free_pool_memory(msg);
423 static void list_jobs_waiting_on_reservation(void sendit(const char *msg, int len, void *sarg), void *arg)
428 msg = _("\nJobs waiting to reserve a drive:\n");
429 sendit(msg, strlen(msg), arg);
432 if (!jcr->reserve_msgs) {
435 send_drive_reserve_messages(jcr, sendit, arg);
440 sendit(msg, strlen(msg), arg);
444 static void list_terminated_jobs(void sendit(const char *msg, int len, void *sarg), void *arg)
446 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
448 struct s_last_job *je;
451 if (last_jobs->size() == 0) {
452 msg = _("No Terminated Jobs.\n");
453 sendit(msg, strlen(msg), arg);
456 lock_last_jobs_list();
457 msg = _("\nTerminated Jobs:\n");
458 sendit(msg, strlen(msg), arg);
459 msg = _(" JobId Level Files Bytes Status Finished Name \n");
460 sendit(msg, strlen(msg), arg);
461 msg = _("===================================================================\n");
462 sendit(msg, strlen(msg), arg);
463 foreach_dlist(je, last_jobs) {
464 char JobName[MAX_NAME_LENGTH];
465 const char *termstat;
468 bstrftime_nc(dt, sizeof(dt), je->end_time);
469 switch (je->JobType) {
472 bstrncpy(level, " ", sizeof(level));
475 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
479 switch (je->JobStatus) {
481 termstat = _("Created");
484 case JS_ErrorTerminated:
485 termstat = _("Error");
488 termstat = _("Diffs");
491 termstat = _("Cancel");
497 termstat = _("Other");
500 bstrncpy(JobName, je->Job, sizeof(JobName));
501 /* There are three periods after the Job name */
503 for (int i=0; i<3; i++) {
504 if ((p=strrchr(JobName, '.')) != NULL) {
508 bsnprintf(buf, sizeof(buf), _("%6d %-6s %8s %10s %-7s %-8s %s\n"),
511 edit_uint64_with_commas(je->JobFiles, b1),
512 edit_uint64_with_suffix(je->JobBytes, b2),
515 sendit(buf, strlen(buf), arg);
517 sendit(_("====\n"), 5, arg);
518 unlock_last_jobs_list();
522 * Convert Job Level into a string
524 static const char *level_to_str(int level)
535 str = _("Incremental");
538 str = _("Differential");
543 case L_VERIFY_CATALOG:
544 str = _("Verify Catalog");
547 str = _("Init Catalog");
549 case L_VERIFY_VOLUME_TO_CATALOG:
550 str = _("Volume to Catalog");
552 case L_VERIFY_DISK_TO_CATALOG:
553 str = _("Disk to Catalog");
562 str = _("Unknown Job Level");
571 static void bsock_sendit(const char *msg, int len, void *arg)
573 BSOCK *user = (BSOCK *)arg;
575 memcpy(user->msg, msg, len+1);
576 user->msglen = len+1;
581 * Status command from Director
583 bool status_cmd(JCR *jcr)
585 BSOCK *user = jcr->dir_bsock;
587 bnet_fsend(user, "\n");
588 do_status(bsock_sendit, (void *)user);
590 bnet_sig(user, BNET_EOD);
595 * .status command from Director
597 bool qstatus_cmd(JCR *jcr)
599 BSOCK *dir = jcr->dir_bsock;
604 if (sscanf(dir->msg, qstatus, time.c_str()) != 1) {
605 pm_strcpy(jcr->errmsg, dir->msg);
606 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
607 bnet_fsend(dir, _("3900 Bad .status command, missing argument.\n"));
608 bnet_sig(dir, BNET_EOD);
613 if (strcmp(time.c_str(), "current") == 0) {
614 bnet_fsend(dir, OKqstatus, time.c_str());
616 if (njcr->JobId != 0) {
617 bnet_fsend(dir, DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
621 } else if (strcmp(time.c_str(), "last") == 0) {
622 bnet_fsend(dir, OKqstatus, time.c_str());
623 if ((last_jobs) && (last_jobs->size() > 0)) {
624 job = (s_last_job*)last_jobs->last();
625 bnet_fsend(dir, DotStatusJob, job->JobId, job->JobStatus, job->Errors);
628 pm_strcpy(jcr->errmsg, dir->msg);
629 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
630 bnet_fsend(dir, _("3900 Bad .status command, wrong argument.\n"));
631 bnet_sig(dir, BNET_EOD);
634 bnet_sig(dir, BNET_EOD);
638 #if defined(HAVE_WIN32)
647 * Put message in Window List Box
649 static void win32_sendit(const char *msg, int len, void *marg)
651 struct s_win32_arg *arg = (struct s_win32_arg *)marg;
653 if (len > 0 && msg[len-1] == '\n') {
654 // when compiling with visual studio some strings are read-only
655 // and cause access violations. So we creat a tmp copy.
656 char *_msg = (char *)alloca(len);
657 bstrncpy(_msg, msg, len);
660 SendDlgItemMessage(arg->hwnd, arg->idlist, LB_ADDSTRING, 0, (LONG)msg);
664 void FillStatusBox(HWND hwnd, int idlist)
666 struct s_win32_arg arg;
672 for ( ; SendDlgItemMessage(hwnd, idlist, LB_DELETESTRING, 0, (LONG)0) > 0; )
674 do_status(win32_sendit, (void *)&arg);
677 char *bac_status(char *buf, int buf_len)
680 const char *termstat = _("Bacula Idle");
681 struct s_last_job *job;
682 int stat = 0; /* Idle */
687 Dmsg0(1000, "Begin bac_status jcr loop.\n");
689 if (njcr->JobId != 0) {
691 termstat = _("Bacula Running");
700 if (last_jobs->size() > 0) {
701 job = (struct s_last_job *)last_jobs->last();
702 stat = job->JobStatus;
703 switch (job->JobStatus) {
705 termstat = _("Last Job Canceled");
707 case JS_ErrorTerminated:
709 termstat = _("Last Job Failed");
713 termstat = _("Last Job had Warnings");
718 Dmsg0(1000, "End bac_status jcr loop.\n");
722 bstrncpy(buf, termstat, buf_len);
727 #endif /* HAVE_WIN32 */