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);
47 static const char *level_to_str(int level);
50 * Status command from Director
52 void output_status(void sendit(const char *msg, int len, void *sarg), void *arg)
57 char dt[MAX_TIME_LENGTH];
58 char *msg, b1[35], b2[35], b3[35], b4[35];
62 msg = (char *)get_pool_memory(PM_MESSAGE);
64 len = Mmsg(msg, _("%s Version: %s (%s) %s %s %s\n"),
65 my_name, VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
66 sendit(msg, len, arg);
68 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
71 len = Mmsg(msg, _("Daemon started %s, %d Job%s run since started.\n"),
72 dt, num_jobs_run, num_jobs_run == 1 ? "" : "s");
73 sendit(msg, len, arg);
75 len = Mmsg(msg, _(" Heap: bytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
76 edit_uint64_with_commas(sm_bytes, b1),
77 edit_uint64_with_commas(sm_max_bytes, b2),
78 edit_uint64_with_commas(sm_buffers, b3),
79 edit_uint64_with_commas(sm_max_buffers, b4));
80 sendit(msg, len, arg);
85 list_running_jobs(sendit, arg);
88 * List jobs stuck in reservation system
90 list_jobs_waiting_on_reservation(sendit, arg);
93 * List terminated jobs
95 list_terminated_jobs(sendit, arg);
100 len = Mmsg(msg, _("\nDevice status:\n"));
101 sendit(msg, len, arg);
103 foreach_res(changer, R_AUTOCHANGER) {
104 len = Mmsg(msg, _("Autochanger \"%s\" with devices:\n"),
106 sendit(msg, len, arg);
108 foreach_alist(device, changer->device) {
110 len = Mmsg(msg, " %s\n", device->dev->print_name());
111 sendit(msg, len, arg);
113 len = Mmsg(msg, " %s\n", device->hdr.name);
114 sendit(msg, len, arg);
118 foreach_res(device, R_DEVICE) {
120 if (dev && dev->is_open()) {
121 if (dev->is_labeled()) {
122 len = Mmsg(msg, _("Device %s is mounted with Volume=\"%s\" Pool=\"%s\"\n"),
123 dev->print_name(), dev->VolHdr.VolumeName,
124 dev->pool_name[0]?dev->pool_name:"*unknown*");
125 sendit(msg, len, arg);
127 len = Mmsg(msg, _("Device %s open but no Bacula volume is mounted.\n"),
129 sendit(msg, len, arg);
131 send_blocked_status(dev, sendit, arg);
132 if (dev->can_append()) {
133 bpb = dev->VolCatInfo.VolCatBlocks;
137 bpb = dev->VolCatInfo.VolCatBytes / bpb;
138 len = Mmsg(msg, _(" Total Bytes=%s Blocks=%s Bytes/block=%s\n"),
139 edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, b1),
140 edit_uint64_with_commas(dev->VolCatInfo.VolCatBlocks, b2),
141 edit_uint64_with_commas(bpb, b3));
142 sendit(msg, len, arg);
143 } else { /* reading */
144 bpb = dev->VolCatInfo.VolCatReads;
148 if (dev->VolCatInfo.VolCatRBytes > 0) {
149 bpb = dev->VolCatInfo.VolCatRBytes / bpb;
153 len = Mmsg(msg, _(" Total Bytes Read=%s Blocks Read=%s Bytes/block=%s\n"),
154 edit_uint64_with_commas(dev->VolCatInfo.VolCatRBytes, b1),
155 edit_uint64_with_commas(dev->VolCatInfo.VolCatReads, b2),
156 edit_uint64_with_commas(bpb, b3));
157 sendit(msg, len, arg);
159 len = Mmsg(msg, _(" Positioned at File=%s Block=%s\n"),
160 edit_uint64_with_commas(dev->file, b1),
161 edit_uint64_with_commas(dev->block_num, b2));
162 sendit(msg, len, arg);
166 len = Mmsg(msg, _("Device %s is not open.\n"), dev->print_name());
167 sendit(msg, len, arg);
168 send_blocked_status(dev, sendit, arg);
170 len = Mmsg(msg, _("Device \"%s\" is not open or does not exist.\n"), device->hdr.name);
171 sendit(msg, len, arg);
175 sendit("====\n\n", 6, arg);
176 len = Mmsg(msg, _("In Use Volume status:\n"));
177 sendit(msg, len, arg);
178 list_volumes(sendit, arg);
179 sendit("====\n\n", 6, arg);
182 if (debug_level > 10) {
183 bnet_fsend(user, _("====\n\n"));
184 dump_resource(R_DEVICE, resources[R_DEVICE-r_first].res_head, sendit, user);
185 bnet_fsend(user, _("====\n\n"));
189 list_spool_stats(sendit, arg);
191 free_pool_memory(msg);
194 static void send_blocked_status(DEVICE *dev, void sendit(const char *msg, int len, void *sarg), void *arg)
199 msg = (char *)get_pool_memory(PM_MESSAGE);
202 len = Mmsg(msg, _("No DEVICE structure.\n\n"));
203 sendit(msg, len, arg);
204 free_pool_memory(msg);
207 switch (dev->dev_blocked) {
209 len = Mmsg(msg, _(" Device is BLOCKED. User unmounted.\n"));
210 sendit(msg, len, arg);
212 case BST_UNMOUNTED_WAITING_FOR_SYSOP:
213 len = Mmsg(msg, _(" Device is BLOCKED. User unmounted during wait for media/mount.\n"));
214 sendit(msg, len, arg);
216 case BST_WAITING_FOR_SYSOP:
218 dlist *dcrs = dev->attached_dcrs;
219 bool found_jcr = false;
224 for (dcr = (DCR *)dcrs->first(); dcr != NULL; dcr = (DCR *)dcrs->next(dcr)) {
225 if (dcr->jcr->JobStatus == JS_WaitMount) {
226 len = Mmsg(msg, _(" Device is BLOCKED waiting for mount of volume \"%s\".\n"),
228 sendit(msg, len, arg);
235 len = Mmsg(msg, _(" Device is BLOCKED waiting for media.\n"));
236 sendit(msg, len, arg);
240 case BST_DOING_ACQUIRE:
241 len = Mmsg(msg, _(" Device is being initialized.\n"));
242 sendit(msg, len, arg);
244 case BST_WRITING_LABEL:
245 len = Mmsg(msg, _(" Device is blocked labeling a Volume.\n"));
246 sendit(msg, len, arg);
251 /* Send autochanger slot status */
252 if (dev->is_autochanger()) {
254 len = Mmsg(msg, _(" Slot %d is loaded in drive %d.\n"),
255 dev->Slot, dev->drive_index);
256 sendit(msg, len, arg);
257 } else if (dev->Slot == 0) {
258 len = Mmsg(msg, _(" Drive %d is not loaded.\n"), dev->drive_index);
259 sendit(msg, len, arg);
261 len = Mmsg(msg, _(" Drive %d status unknown.\n"), dev->drive_index);
262 sendit(msg, len, arg);
265 if (debug_level > 1) {
266 len = Mmsg(msg, _("Configured device capabilities:\n"));
267 sendit(msg, len, arg);
269 len = Mmsg(msg, "%sEOF %sBSR %sBSF %sFSR %sFSF %sEOM %sREM %sRACCESS %sAUTOMOUNT %sLABEL %sANONVOLS %sALWAYSOPEN\n",
270 dev->capabilities & CAP_EOF ? "" : "!",
271 dev->capabilities & CAP_BSR ? "" : "!",
272 dev->capabilities & CAP_BSF ? "" : "!",
273 dev->capabilities & CAP_FSR ? "" : "!",
274 dev->capabilities & CAP_FSF ? "" : "!",
275 dev->capabilities & CAP_EOM ? "" : "!",
276 dev->capabilities & CAP_REM ? "" : "!",
277 dev->capabilities & CAP_RACCESS ? "" : "!",
278 dev->capabilities & CAP_AUTOMOUNT ? "" : "!",
279 dev->capabilities & CAP_LABEL ? "" : "!",
280 dev->capabilities & CAP_ANONVOLS ? "" : "!",
281 dev->capabilities & CAP_ALWAYSOPEN ? "" : "!");
282 sendit(msg, len, arg);
284 len = Mmsg(msg, _("Device state:\n"));
285 sendit(msg, len, arg);
287 len = Mmsg(msg, "%sOPENED %sTAPE %sLABEL %sMALLOC %sAPPEND %sREAD %sEOT %sWEOT %sEOF %sNEXTVOL %sSHORT %sMOUNTED\n",
288 dev->is_open() ? "" : "!",
289 dev->is_tape() ? "" : "!",
290 dev->is_labeled() ? "" : "!",
291 dev->state & ST_MALLOC ? "" : "!",
292 dev->can_append() ? "" : "!",
293 dev->can_read() ? "" : "!",
294 dev->at_eot() ? "" : "!",
295 dev->state & ST_WEOT ? "" : "!",
296 dev->at_eof() ? "" : "!",
297 dev->state & ST_NEXTVOL ? "" : "!",
298 dev->state & ST_SHORT ? "" : "!",
299 dev->state & ST_MOUNTED ? "" : "!");
300 sendit(msg, len, arg);
302 len = Mmsg(msg, _("num_writers=%d block=%d\n\n"), dev->num_writers, dev->dev_blocked);
303 sendit(msg, len, arg);
305 len = Mmsg(msg, _("Device parameters:\n"));
306 sendit(msg, len, arg);
308 len = Mmsg(msg, _("Archive name: %s Device name: %s\n"), dev->archive_name(),
310 sendit(msg, len, arg);
312 len = Mmsg(msg, _("File=%u block=%u\n"), dev->file, dev->block_num);
313 sendit(msg, len, arg);
315 len = Mmsg(msg, _("Min block=%u Max block=%u\n"), dev->min_block_size, dev->max_block_size);
316 sendit(msg, len, arg);
319 free_pool_memory(msg);
322 static void list_running_jobs(void sendit(const char *msg, int len, void *sarg), void *arg)
328 char JobName[MAX_NAME_LENGTH];
329 char *msg, b1[30], b2[30], b3[30];
332 msg = (char *)get_pool_memory(PM_MESSAGE);
334 len = Mmsg(msg, _("\nRunning Jobs:\n"));
335 sendit(msg, len, arg);
338 if (jcr->JobStatus == JS_WaitFD) {
339 len = Mmsg(msg, _("%s Job %s waiting for Client connection.\n"),
340 job_type_to_str(jcr->JobType), jcr->Job);
341 sendit(msg, len, arg);
344 rdcr = jcr->read_dcr;
345 if ((dcr && dcr->device) || rdcr && rdcr->device) {
346 bstrncpy(JobName, jcr->Job, sizeof(JobName));
347 /* There are three periods after the Job name */
349 for (int i=0; i<3; i++) {
350 if ((p=strrchr(JobName, '.')) != NULL) {
354 if (rdcr && rdcr->device) {
355 len = Mmsg(msg, _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
356 " pool=\"%s\" device=\"%s\"\n"),
357 job_level_to_str(jcr->JobLevel),
358 job_type_to_str(jcr->JobType),
363 rdcr->dev?rdcr->dev->print_name():
364 rdcr->device->device_name);
365 sendit(msg, len, arg);
367 if (dcr && dcr->device) {
368 len = Mmsg(msg, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
369 " pool=\"%s\" device=\"%s\"\n"),
370 job_level_to_str(jcr->JobLevel),
371 job_type_to_str(jcr->JobType),
376 dcr->dev?dcr->dev->print_name():
377 dcr->device->device_name);
378 sendit(msg, len, arg);
380 sec = time(NULL) - jcr->run_time;
384 bps = jcr->JobBytes / sec;
385 len = Mmsg(msg, _(" Files=%s Bytes=%s Bytes/sec=%s\n"),
386 edit_uint64_with_commas(jcr->JobFiles, b1),
387 edit_uint64_with_commas(jcr->JobBytes, b2),
388 edit_uint64_with_commas(bps, b3));
389 sendit(msg, len, arg);
392 if (jcr->file_bsock) {
393 len = Mmsg(msg, _(" FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
394 edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
395 jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
396 jcr->file_bsock->fd);
397 sendit(msg, len, arg);
399 len = Mmsg(msg, _(" FDSocket closed\n"));
400 sendit(msg, len, arg);
407 sendit("====\n", 5, arg);
409 free_pool_memory(msg);
412 static void list_jobs_waiting_on_reservation(void sendit(const char *msg, int len, void *sarg), void *arg)
417 msg = _("\nJobs waiting to reserve a drive:\n");
418 sendit(msg, strlen(msg), arg);
421 if (!jcr->reserve_msgs) {
424 send_drive_reserve_messages(jcr, sendit, arg);
428 sendit("====\n", 5, arg);
432 static void list_terminated_jobs(void sendit(const char *msg, int len, void *sarg), void *arg)
434 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
436 struct s_last_job *je;
439 msg = _("\nTerminated Jobs:\n");
440 sendit(msg, strlen(msg), arg);
441 if (last_jobs->size() == 0) {
442 sendit("====\n", 5, arg);
445 lock_last_jobs_list();
446 msg = _(" JobId Level Files Bytes Status Finished Name \n");
447 sendit(msg, strlen(msg), arg);
448 msg = _("===================================================================\n");
449 sendit(msg, strlen(msg), arg);
450 foreach_dlist(je, last_jobs) {
451 char JobName[MAX_NAME_LENGTH];
452 const char *termstat;
455 bstrftime_nc(dt, sizeof(dt), je->end_time);
456 switch (je->JobType) {
459 bstrncpy(level, " ", sizeof(level));
462 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
466 switch (je->JobStatus) {
468 termstat = _("Created");
471 case JS_ErrorTerminated:
472 termstat = _("Error");
475 termstat = _("Diffs");
478 termstat = _("Cancel");
484 termstat = _("Other");
487 bstrncpy(JobName, je->Job, sizeof(JobName));
488 /* There are three periods after the Job name */
490 for (int i=0; i<3; i++) {
491 if ((p=strrchr(JobName, '.')) != NULL) {
495 bsnprintf(buf, sizeof(buf), _("%6d %-6s %8s %10s %-7s %-8s %s\n"),
498 edit_uint64_with_commas(je->JobFiles, b1),
499 edit_uint64_with_suffix(je->JobBytes, b2),
502 sendit(buf, strlen(buf), arg);
504 unlock_last_jobs_list();
505 sendit("====\n", 5, arg);
509 * Convert Job Level into a string
511 static const char *level_to_str(int level)
522 str = _("Incremental");
525 str = _("Differential");
530 case L_VERIFY_CATALOG:
531 str = _("Verify Catalog");
534 str = _("Init Catalog");
536 case L_VERIFY_VOLUME_TO_CATALOG:
537 str = _("Volume to Catalog");
539 case L_VERIFY_DISK_TO_CATALOG:
540 str = _("Disk to Catalog");
549 str = _("Unknown Job Level");
558 static void bsock_sendit(const char *msg, int len, void *arg)
560 BSOCK *user = (BSOCK *)arg;
562 memcpy(user->msg, msg, len+1);
563 user->msglen = len+1;
568 * Status command from Director
570 bool status_cmd(JCR *jcr)
572 BSOCK *user = jcr->dir_bsock;
574 bnet_fsend(user, "\n");
575 output_status(bsock_sendit, (void *)user);
577 bnet_sig(user, BNET_EOD);
582 * .status command from Director
584 bool qstatus_cmd(JCR *jcr)
586 BSOCK *dir = jcr->dir_bsock;
591 if (sscanf(dir->msg, qstatus, time.c_str()) != 1) {
592 pm_strcpy(jcr->errmsg, dir->msg);
593 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
594 bnet_fsend(dir, _("3900 Bad .status command, missing argument.\n"));
595 bnet_sig(dir, BNET_EOD);
600 if (strcmp(time.c_str(), "current") == 0) {
601 bnet_fsend(dir, OKqstatus, time.c_str());
603 if (njcr->JobId != 0) {
604 bnet_fsend(dir, DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
608 } else if (strcmp(time.c_str(), "last") == 0) {
609 bnet_fsend(dir, OKqstatus, time.c_str());
610 if ((last_jobs) && (last_jobs->size() > 0)) {
611 job = (s_last_job*)last_jobs->last();
612 bnet_fsend(dir, DotStatusJob, job->JobId, job->JobStatus, job->Errors);
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, wrong argument.\n"));
618 bnet_sig(dir, BNET_EOD);
621 bnet_sig(dir, BNET_EOD);
625 #if defined(HAVE_WIN32)
628 char *bac_status(char *buf, int buf_len)
631 const char *termstat = _("Bacula Storage: Idle");
632 struct s_last_job *job;
633 int stat = 0; /* Idle */
638 Dmsg0(1000, "Begin bac_status jcr loop.\n");
640 if (njcr->JobId != 0) {
642 termstat = _("Bacula Storage: Running");
651 if (last_jobs->size() > 0) {
652 job = (struct s_last_job *)last_jobs->last();
653 stat = job->JobStatus;
654 switch (job->JobStatus) {
656 termstat = _("Bacula Storage: Last Job Canceled");
658 case JS_ErrorTerminated:
660 termstat = _("Bacula Storage: Last Job Failed");
664 termstat = _("Bacula Storage: Last Job had Warnings");
669 Dmsg0(1000, "End bac_status jcr loop.\n");
673 bstrncpy(buf, termstat, buf_len);
678 #endif /* HAVE_WIN32 */