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);
212 switch (dev->dev_blocked) {
214 len = Mmsg(msg, _(" Device is BLOCKED. User unmounted.\n"));
215 sendit(msg, len, arg);
217 case BST_UNMOUNTED_WAITING_FOR_SYSOP:
218 len = Mmsg(msg, _(" Device is BLOCKED. User unmounted during wait for media/mount.\n"));
219 sendit(msg, len, arg);
221 case BST_WAITING_FOR_SYSOP:
223 dlist *dcrs = dev->attached_dcrs;
224 bool found_jcr = false;
229 for (dcr = (DCR *)dcrs->first(); dcr != NULL; dcr = (DCR *)dcrs->next(dcr)) {
230 if (dcr->jcr->JobStatus == JS_WaitMount) {
231 len = Mmsg(msg, _(" Device is BLOCKED waiting for mount of volume \"%s\".\n"),
233 sendit(msg, len, arg);
240 len = Mmsg(msg, _(" Device is BLOCKED waiting for media.\n"));
241 sendit(msg, len, arg);
245 case BST_DOING_ACQUIRE:
246 len = Mmsg(msg, _(" Device is being initialized.\n"));
247 sendit(msg, len, arg);
249 case BST_WRITING_LABEL:
250 len = Mmsg(msg, _(" Device is blocked labeling a Volume.\n"));
251 sendit(msg, len, arg);
256 /* Send autochanger slot status */
257 if (dev->is_autochanger()) {
259 len = Mmsg(msg, _(" Slot %d is loaded in drive %d.\n"),
260 dev->Slot, dev->drive_index);
261 sendit(msg, len, arg);
263 len = Mmsg(msg, _(" Drive %d is not loaded.\n"), dev->drive_index);
264 sendit(msg, len, arg);
267 if (debug_level > 1) {
268 len = Mmsg(msg, _("Configured device capabilities:\n"));
269 sendit(msg, len, arg);
271 len = Mmsg(msg, "%sEOF %sBSR %sBSF %sFSR %sFSF %sEOM %sREM %sRACCESS %sAUTOMOUNT %sLABEL %sANONVOLS %sALWAYSOPEN\n",
272 dev->capabilities & CAP_EOF ? "" : "!",
273 dev->capabilities & CAP_BSR ? "" : "!",
274 dev->capabilities & CAP_BSF ? "" : "!",
275 dev->capabilities & CAP_FSR ? "" : "!",
276 dev->capabilities & CAP_FSF ? "" : "!",
277 dev->capabilities & CAP_EOM ? "" : "!",
278 dev->capabilities & CAP_REM ? "" : "!",
279 dev->capabilities & CAP_RACCESS ? "" : "!",
280 dev->capabilities & CAP_AUTOMOUNT ? "" : "!",
281 dev->capabilities & CAP_LABEL ? "" : "!",
282 dev->capabilities & CAP_ANONVOLS ? "" : "!",
283 dev->capabilities & CAP_ALWAYSOPEN ? "" : "!");
284 sendit(msg, len, arg);
286 len = Mmsg(msg, _("Device state:\n"));
287 sendit(msg, len, arg);
289 len = Mmsg(msg, "%sOPENED %sTAPE %sLABEL %sMALLOC %sAPPEND %sREAD %sEOT %sWEOT %sEOF %sNEXTVOL %sSHORT %sMOUNTED\n",
290 dev->is_open() ? "" : "!",
291 dev->is_tape() ? "" : "!",
292 dev->is_labeled() ? "" : "!",
293 dev->state & ST_MALLOC ? "" : "!",
294 dev->can_append() ? "" : "!",
295 dev->can_read() ? "" : "!",
296 dev->at_eot() ? "" : "!",
297 dev->state & ST_WEOT ? "" : "!",
298 dev->at_eof() ? "" : "!",
299 dev->state & ST_NEXTVOL ? "" : "!",
300 dev->state & ST_SHORT ? "" : "!",
301 dev->state & ST_MOUNTED ? "" : "!");
302 sendit(msg, len, arg);
304 len = Mmsg(msg, _("num_writers=%d block=%d\n\n"), dev->num_writers, dev->dev_blocked);
305 sendit(msg, len, arg);
307 len = Mmsg(msg, _("Device parameters:\n"));
308 sendit(msg, len, arg);
310 len = Mmsg(msg, _("Archive name: %s Device name: %s\n"), dev->archive_name(),
312 sendit(msg, len, arg);
314 len = Mmsg(msg, _("File=%u block=%u\n"), dev->file, dev->block_num);
315 sendit(msg, len, arg);
317 len = Mmsg(msg, _("Min block=%u Max block=%u\n"), dev->min_block_size, dev->max_block_size);
318 sendit(msg, len, arg);
321 free_pool_memory(msg);
324 static void list_running_jobs(void sendit(const char *msg, int len, void *sarg), void *arg)
330 char JobName[MAX_NAME_LENGTH];
331 char *msg, b1[30], b2[30], b3[30];
334 msg = (char *)get_pool_memory(PM_MESSAGE);
336 len = Mmsg(msg, _("\nRunning Jobs:\n"));
337 sendit(msg, len, arg);
340 if (jcr->JobStatus == JS_WaitFD) {
341 len = Mmsg(msg, _("%s Job %s waiting for Client connection.\n"),
342 job_type_to_str(jcr->JobType), jcr->Job);
343 sendit(msg, len, arg);
346 rdcr = jcr->read_dcr;
347 if ((dcr && dcr->device) || rdcr && rdcr->device) {
348 bstrncpy(JobName, jcr->Job, sizeof(JobName));
349 /* There are three periods after the Job name */
351 for (int i=0; i<3; i++) {
352 if ((p=strrchr(JobName, '.')) != NULL) {
356 if (rdcr && rdcr->device) {
357 len = Mmsg(msg, _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
358 " pool=\"%s\" device=\"%s\"\n"),
359 job_level_to_str(jcr->JobLevel),
360 job_type_to_str(jcr->JobType),
365 rdcr->dev?rdcr->dev->print_name():
366 rdcr->device->device_name);
367 sendit(msg, len, arg);
369 if (dcr && dcr->device) {
370 len = Mmsg(msg, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
371 " pool=\"%s\" device=\"%s\"\n"),
372 job_level_to_str(jcr->JobLevel),
373 job_type_to_str(jcr->JobType),
378 dcr->dev?dcr->dev->print_name():
379 dcr->device->device_name);
380 sendit(msg, len, arg);
382 sec = time(NULL) - jcr->run_time;
386 bps = jcr->JobBytes / sec;
387 len = Mmsg(msg, _(" Files=%s Bytes=%s Bytes/sec=%s\n"),
388 edit_uint64_with_commas(jcr->JobFiles, b1),
389 edit_uint64_with_commas(jcr->JobBytes, b2),
390 edit_uint64_with_commas(bps, b3));
391 sendit(msg, len, arg);
394 if (jcr->file_bsock) {
395 len = Mmsg(msg, _(" FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
396 edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
397 jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
398 jcr->file_bsock->fd);
399 sendit(msg, len, arg);
401 len = Mmsg(msg, _(" FDSocket closed\n"));
402 sendit(msg, len, arg);
410 len = Mmsg(msg, _("No Jobs running.\n"));
411 sendit(msg, len, arg);
413 len = Mmsg(msg, _("====\n"));
414 sendit(msg, len, arg);
416 free_pool_memory(msg);
419 static void list_jobs_waiting_on_reservation(void sendit(const char *msg, int len, void *sarg), void *arg)
424 msg = _("\nJobs waiting to reserve a drive:\n");
425 sendit(msg, strlen(msg), arg);
428 if (!jcr->reserve_msgs) {
431 send_drive_reserve_messages(jcr, sendit, arg);
436 sendit(msg, strlen(msg), arg);
440 static void list_terminated_jobs(void sendit(const char *msg, int len, void *sarg), void *arg)
442 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
444 struct s_last_job *je;
447 if (last_jobs->size() == 0) {
448 msg = _("No Terminated Jobs.\n");
449 sendit(msg, strlen(msg), arg);
452 lock_last_jobs_list();
453 msg = _("\nTerminated Jobs:\n");
454 sendit(msg, strlen(msg), arg);
455 msg = _(" JobId Level Files Bytes Status Finished Name \n");
456 sendit(msg, strlen(msg), arg);
457 msg = _("======================================================================\n");
458 sendit(msg, strlen(msg), arg);
459 foreach_dlist(je, last_jobs) {
460 char JobName[MAX_NAME_LENGTH];
461 const char *termstat;
464 bstrftime_nc(dt, sizeof(dt), je->end_time);
465 switch (je->JobType) {
468 bstrncpy(level, " ", sizeof(level));
471 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
475 switch (je->JobStatus) {
477 termstat = _("Created");
480 case JS_ErrorTerminated:
481 termstat = _("Error");
484 termstat = _("Diffs");
487 termstat = _("Cancel");
493 termstat = _("Other");
496 bstrncpy(JobName, je->Job, sizeof(JobName));
497 /* There are three periods after the Job name */
499 for (int i=0; i<3; i++) {
500 if ((p=strrchr(JobName, '.')) != NULL) {
504 bsnprintf(buf, sizeof(buf), _("%6d %-6s %8s %14s %-7s %-8s %s\n"),
507 edit_uint64_with_commas(je->JobFiles, b1),
508 edit_uint64_with_commas(je->JobBytes, b2),
511 sendit(buf, strlen(buf), arg);
513 sendit(_("====\n"), 5, arg);
514 unlock_last_jobs_list();
518 * Convert Job Level into a string
520 static const char *level_to_str(int level)
531 str = _("Incremental");
534 str = _("Differential");
539 case L_VERIFY_CATALOG:
540 str = _("Verify Catalog");
543 str = _("Init Catalog");
545 case L_VERIFY_VOLUME_TO_CATALOG:
546 str = _("Volume to Catalog");
548 case L_VERIFY_DISK_TO_CATALOG:
549 str = _("Disk to Catalog");
558 str = _("Unknown Job Level");
567 static void bsock_sendit(const char *msg, int len, void *arg)
569 BSOCK *user = (BSOCK *)arg;
571 memcpy(user->msg, msg, len+1);
572 user->msglen = len+1;
577 * Status command from Director
579 bool status_cmd(JCR *jcr)
581 BSOCK *user = jcr->dir_bsock;
583 bnet_fsend(user, "\n");
584 do_status(bsock_sendit, (void *)user);
586 bnet_sig(user, BNET_EOD);
591 * .status command from Director
593 bool qstatus_cmd(JCR *jcr)
595 BSOCK *dir = jcr->dir_bsock;
600 if (sscanf(dir->msg, qstatus, time.c_str()) != 1) {
601 pm_strcpy(jcr->errmsg, dir->msg);
602 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
603 bnet_fsend(dir, _("3900 Bad .status command, missing argument.\n"));
604 bnet_sig(dir, BNET_EOD);
609 if (strcmp(time.c_str(), "current") == 0) {
610 bnet_fsend(dir, OKqstatus, time.c_str());
612 if (njcr->JobId != 0) {
613 bnet_fsend(dir, DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
617 } else if (strcmp(time.c_str(), "last") == 0) {
618 bnet_fsend(dir, OKqstatus, time.c_str());
619 if ((last_jobs) && (last_jobs->size() > 0)) {
620 job = (s_last_job*)last_jobs->last();
621 bnet_fsend(dir, DotStatusJob, job->JobId, job->JobStatus, job->Errors);
624 pm_strcpy(jcr->errmsg, dir->msg);
625 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
626 bnet_fsend(dir, _("3900 Bad .status command, wrong argument.\n"));
627 bnet_sig(dir, BNET_EOD);
630 bnet_sig(dir, BNET_EOD);
634 #if defined(HAVE_WIN32)
643 * Put message in Window List Box
645 static void win32_sendit(const char *msg, int len, void *marg)
647 struct s_win32_arg *arg = (struct s_win32_arg *)marg;
649 if (len > 0 && msg[len-1] == '\n') {
650 // when compiling with visual studio some strings are read-only
651 // and cause access violations. So we creat a tmp copy.
652 char *_msg = (char *)alloca(len);
653 bstrncpy(_msg, msg, len);
656 SendDlgItemMessage(arg->hwnd, arg->idlist, LB_ADDSTRING, 0, (LONG)msg);
660 void FillStatusBox(HWND hwnd, int idlist)
662 struct s_win32_arg arg;
668 for ( ; SendDlgItemMessage(hwnd, idlist, LB_DELETESTRING, 0, (LONG)0) > 0; )
670 do_status(win32_sendit, (void *)&arg);
673 char *bac_status(char *buf, int buf_len)
676 const char *termstat = _("Bacula Idle");
677 struct s_last_job *job;
678 int stat = 0; /* Idle */
683 Dmsg0(1000, "Begin bac_status jcr loop.\n");
685 if (njcr->JobId != 0) {
687 termstat = _("Bacula Running");
696 if (last_jobs->size() > 0) {
697 job = (struct s_last_job *)last_jobs->last();
698 stat = job->JobStatus;
699 switch (job->JobStatus) {
701 termstat = _("Last Job Canceled");
703 case JS_ErrorTerminated:
705 termstat = _("Last Job Failed");
709 termstat = _("Last Job had Warnings");
714 Dmsg0(1000, "End bac_status jcr loop.\n");
718 bstrncpy(buf, termstat, buf_len);
723 #endif /* HAVE_WIN32 */