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);
264 len = Mmsg(msg, _(" Drive %d is not loaded.\n"), dev->drive_index);
265 sendit(msg, len, arg);
268 if (debug_level > 1) {
269 len = Mmsg(msg, _("Configured device capabilities:\n"));
270 sendit(msg, len, arg);
272 len = Mmsg(msg, "%sEOF %sBSR %sBSF %sFSR %sFSF %sEOM %sREM %sRACCESS %sAUTOMOUNT %sLABEL %sANONVOLS %sALWAYSOPEN\n",
273 dev->capabilities & CAP_EOF ? "" : "!",
274 dev->capabilities & CAP_BSR ? "" : "!",
275 dev->capabilities & CAP_BSF ? "" : "!",
276 dev->capabilities & CAP_FSR ? "" : "!",
277 dev->capabilities & CAP_FSF ? "" : "!",
278 dev->capabilities & CAP_EOM ? "" : "!",
279 dev->capabilities & CAP_REM ? "" : "!",
280 dev->capabilities & CAP_RACCESS ? "" : "!",
281 dev->capabilities & CAP_AUTOMOUNT ? "" : "!",
282 dev->capabilities & CAP_LABEL ? "" : "!",
283 dev->capabilities & CAP_ANONVOLS ? "" : "!",
284 dev->capabilities & CAP_ALWAYSOPEN ? "" : "!");
285 sendit(msg, len, arg);
287 len = Mmsg(msg, _("Device state:\n"));
288 sendit(msg, len, arg);
290 len = Mmsg(msg, "%sOPENED %sTAPE %sLABEL %sMALLOC %sAPPEND %sREAD %sEOT %sWEOT %sEOF %sNEXTVOL %sSHORT %sMOUNTED\n",
291 dev->is_open() ? "" : "!",
292 dev->is_tape() ? "" : "!",
293 dev->is_labeled() ? "" : "!",
294 dev->state & ST_MALLOC ? "" : "!",
295 dev->can_append() ? "" : "!",
296 dev->can_read() ? "" : "!",
297 dev->at_eot() ? "" : "!",
298 dev->state & ST_WEOT ? "" : "!",
299 dev->at_eof() ? "" : "!",
300 dev->state & ST_NEXTVOL ? "" : "!",
301 dev->state & ST_SHORT ? "" : "!",
302 dev->state & ST_MOUNTED ? "" : "!");
303 sendit(msg, len, arg);
305 len = Mmsg(msg, _("num_writers=%d block=%d\n\n"), dev->num_writers, dev->dev_blocked);
306 sendit(msg, len, arg);
308 len = Mmsg(msg, _("Device parameters:\n"));
309 sendit(msg, len, arg);
311 len = Mmsg(msg, _("Archive name: %s Device name: %s\n"), dev->archive_name(),
313 sendit(msg, len, arg);
315 len = Mmsg(msg, _("File=%u block=%u\n"), dev->file, dev->block_num);
316 sendit(msg, len, arg);
318 len = Mmsg(msg, _("Min block=%u Max block=%u\n"), dev->min_block_size, dev->max_block_size);
319 sendit(msg, len, arg);
322 free_pool_memory(msg);
325 static void list_running_jobs(void sendit(const char *msg, int len, void *sarg), void *arg)
331 char JobName[MAX_NAME_LENGTH];
332 char *msg, b1[30], b2[30], b3[30];
335 msg = (char *)get_pool_memory(PM_MESSAGE);
337 len = Mmsg(msg, _("\nRunning Jobs:\n"));
338 sendit(msg, len, arg);
341 if (jcr->JobStatus == JS_WaitFD) {
342 len = Mmsg(msg, _("%s Job %s waiting for Client connection.\n"),
343 job_type_to_str(jcr->JobType), jcr->Job);
344 sendit(msg, len, arg);
347 rdcr = jcr->read_dcr;
348 if ((dcr && dcr->device) || rdcr && rdcr->device) {
349 bstrncpy(JobName, jcr->Job, sizeof(JobName));
350 /* There are three periods after the Job name */
352 for (int i=0; i<3; i++) {
353 if ((p=strrchr(JobName, '.')) != NULL) {
357 if (rdcr && rdcr->device) {
358 len = Mmsg(msg, _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
359 " pool=\"%s\" device=\"%s\"\n"),
360 job_level_to_str(jcr->JobLevel),
361 job_type_to_str(jcr->JobType),
366 rdcr->dev?rdcr->dev->print_name():
367 rdcr->device->device_name);
368 sendit(msg, len, arg);
370 if (dcr && dcr->device) {
371 len = Mmsg(msg, _("Writing: %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 dcr->dev?dcr->dev->print_name():
380 dcr->device->device_name);
381 sendit(msg, len, arg);
383 sec = time(NULL) - jcr->run_time;
387 bps = jcr->JobBytes / sec;
388 len = Mmsg(msg, _(" Files=%s Bytes=%s Bytes/sec=%s\n"),
389 edit_uint64_with_commas(jcr->JobFiles, b1),
390 edit_uint64_with_commas(jcr->JobBytes, b2),
391 edit_uint64_with_commas(bps, b3));
392 sendit(msg, len, arg);
395 if (jcr->file_bsock) {
396 len = Mmsg(msg, _(" FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
397 edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
398 jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
399 jcr->file_bsock->fd);
400 sendit(msg, len, arg);
402 len = Mmsg(msg, _(" FDSocket closed\n"));
403 sendit(msg, len, arg);
411 len = Mmsg(msg, _("No Jobs running.\n"));
412 sendit(msg, len, arg);
414 len = Mmsg(msg, _("====\n"));
415 sendit(msg, len, arg);
417 free_pool_memory(msg);
420 static void list_jobs_waiting_on_reservation(void sendit(const char *msg, int len, void *sarg), void *arg)
425 msg = _("\nJobs waiting to reserve a drive:\n");
426 sendit(msg, strlen(msg), arg);
429 if (!jcr->reserve_msgs) {
432 send_drive_reserve_messages(jcr, sendit, arg);
437 sendit(msg, strlen(msg), arg);
441 static void list_terminated_jobs(void sendit(const char *msg, int len, void *sarg), void *arg)
443 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
445 struct s_last_job *je;
448 if (last_jobs->size() == 0) {
449 msg = _("No Terminated Jobs.\n");
450 sendit(msg, strlen(msg), arg);
453 lock_last_jobs_list();
454 msg = _("\nTerminated Jobs:\n");
455 sendit(msg, strlen(msg), arg);
456 msg = _(" JobId Level Files Bytes Status Finished Name \n");
457 sendit(msg, strlen(msg), arg);
458 msg = _("===================================================================\n");
459 sendit(msg, strlen(msg), arg);
460 foreach_dlist(je, last_jobs) {
461 char JobName[MAX_NAME_LENGTH];
462 const char *termstat;
465 bstrftime_nc(dt, sizeof(dt), je->end_time);
466 switch (je->JobType) {
469 bstrncpy(level, " ", sizeof(level));
472 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
476 switch (je->JobStatus) {
478 termstat = _("Created");
481 case JS_ErrorTerminated:
482 termstat = _("Error");
485 termstat = _("Diffs");
488 termstat = _("Cancel");
494 termstat = _("Other");
497 bstrncpy(JobName, je->Job, sizeof(JobName));
498 /* There are three periods after the Job name */
500 for (int i=0; i<3; i++) {
501 if ((p=strrchr(JobName, '.')) != NULL) {
505 bsnprintf(buf, sizeof(buf), _("%6d %-6s %8s %10s %-7s %-8s %s\n"),
508 edit_uint64_with_commas(je->JobFiles, b1),
509 edit_uint64_with_suffix(je->JobBytes, b2),
512 sendit(buf, strlen(buf), arg);
514 sendit(_("====\n"), 5, arg);
515 unlock_last_jobs_list();
519 * Convert Job Level into a string
521 static const char *level_to_str(int level)
532 str = _("Incremental");
535 str = _("Differential");
540 case L_VERIFY_CATALOG:
541 str = _("Verify Catalog");
544 str = _("Init Catalog");
546 case L_VERIFY_VOLUME_TO_CATALOG:
547 str = _("Volume to Catalog");
549 case L_VERIFY_DISK_TO_CATALOG:
550 str = _("Disk to Catalog");
559 str = _("Unknown Job Level");
568 static void bsock_sendit(const char *msg, int len, void *arg)
570 BSOCK *user = (BSOCK *)arg;
572 memcpy(user->msg, msg, len+1);
573 user->msglen = len+1;
578 * Status command from Director
580 bool status_cmd(JCR *jcr)
582 BSOCK *user = jcr->dir_bsock;
584 bnet_fsend(user, "\n");
585 do_status(bsock_sendit, (void *)user);
587 bnet_sig(user, BNET_EOD);
592 * .status command from Director
594 bool qstatus_cmd(JCR *jcr)
596 BSOCK *dir = jcr->dir_bsock;
601 if (sscanf(dir->msg, qstatus, time.c_str()) != 1) {
602 pm_strcpy(jcr->errmsg, dir->msg);
603 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
604 bnet_fsend(dir, _("3900 Bad .status command, missing argument.\n"));
605 bnet_sig(dir, BNET_EOD);
610 if (strcmp(time.c_str(), "current") == 0) {
611 bnet_fsend(dir, OKqstatus, time.c_str());
613 if (njcr->JobId != 0) {
614 bnet_fsend(dir, DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
618 } else if (strcmp(time.c_str(), "last") == 0) {
619 bnet_fsend(dir, OKqstatus, time.c_str());
620 if ((last_jobs) && (last_jobs->size() > 0)) {
621 job = (s_last_job*)last_jobs->last();
622 bnet_fsend(dir, DotStatusJob, job->JobId, job->JobStatus, job->Errors);
625 pm_strcpy(jcr->errmsg, dir->msg);
626 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
627 bnet_fsend(dir, _("3900 Bad .status command, wrong argument.\n"));
628 bnet_sig(dir, BNET_EOD);
631 bnet_sig(dir, BNET_EOD);
635 #if defined(HAVE_WIN32)
644 * Put message in Window List Box
646 static void win32_sendit(const char *msg, int len, void *marg)
648 struct s_win32_arg *arg = (struct s_win32_arg *)marg;
650 if (len > 0 && msg[len-1] == '\n') {
651 // when compiling with visual studio some strings are read-only
652 // and cause access violations. So we creat a tmp copy.
653 char *_msg = (char *)alloca(len);
654 bstrncpy(_msg, msg, len);
657 SendDlgItemMessage(arg->hwnd, arg->idlist, LB_ADDSTRING, 0, (LONG)msg);
661 void FillStatusBox(HWND hwnd, int idlist)
663 struct s_win32_arg arg;
669 for ( ; SendDlgItemMessage(hwnd, idlist, LB_DELETESTRING, 0, (LONG)0) > 0; )
671 do_status(win32_sendit, (void *)&arg);
674 char *bac_status(char *buf, int buf_len)
677 const char *termstat = _("Bacula Idle");
678 struct s_last_job *job;
679 int stat = 0; /* Idle */
684 Dmsg0(1000, "Begin bac_status jcr loop.\n");
686 if (njcr->JobId != 0) {
688 termstat = _("Bacula Running");
697 if (last_jobs->size() > 0) {
698 job = (struct s_last_job *)last_jobs->last();
699 stat = job->JobStatus;
700 switch (job->JobStatus) {
702 termstat = _("Last Job Canceled");
704 case JS_ErrorTerminated:
706 termstat = _("Last Job Failed");
710 termstat = _("Last Job had Warnings");
715 Dmsg0(1000, "End bac_status jcr loop.\n");
719 bstrncpy(buf, termstat, buf_len);
724 #endif /* HAVE_WIN32 */