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 currently 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);
408 len = Mmsg(msg, _("No Jobs running.\n"));
409 sendit(msg, len, arg);
411 sendit("====\n", 5, arg);
413 free_pool_memory(msg);
416 static void list_jobs_waiting_on_reservation(void sendit(const char *msg, int len, void *sarg), void *arg)
421 msg = _("\nJobs waiting to reserve a drive:\n");
422 sendit(msg, strlen(msg), arg);
425 if (!jcr->reserve_msgs) {
428 send_drive_reserve_messages(jcr, sendit, arg);
432 sendit("====\n", 5, arg);
436 static void list_terminated_jobs(void sendit(const char *msg, int len, void *sarg), void *arg)
438 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
440 struct s_last_job *je;
443 msg = _("\nTerminated Jobs:\n");
444 sendit(msg, strlen(msg), arg);
445 if (last_jobs->size() == 0) {
446 sendit("====\n", 5, arg);
449 lock_last_jobs_list();
450 msg = _(" JobId Level Files Bytes Status Finished Name \n");
451 sendit(msg, strlen(msg), arg);
452 msg = _("===================================================================\n");
453 sendit(msg, strlen(msg), arg);
454 foreach_dlist(je, last_jobs) {
455 char JobName[MAX_NAME_LENGTH];
456 const char *termstat;
459 bstrftime_nc(dt, sizeof(dt), je->end_time);
460 switch (je->JobType) {
463 bstrncpy(level, " ", sizeof(level));
466 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
470 switch (je->JobStatus) {
472 termstat = _("Created");
475 case JS_ErrorTerminated:
476 termstat = _("Error");
479 termstat = _("Diffs");
482 termstat = _("Cancel");
488 termstat = _("Other");
491 bstrncpy(JobName, je->Job, sizeof(JobName));
492 /* There are three periods after the Job name */
494 for (int i=0; i<3; i++) {
495 if ((p=strrchr(JobName, '.')) != NULL) {
499 bsnprintf(buf, sizeof(buf), _("%6d %-6s %8s %10s %-7s %-8s %s\n"),
502 edit_uint64_with_commas(je->JobFiles, b1),
503 edit_uint64_with_suffix(je->JobBytes, b2),
506 sendit(buf, strlen(buf), arg);
508 unlock_last_jobs_list();
509 sendit("====\n", 5, arg);
513 * Convert Job Level into a string
515 static const char *level_to_str(int level)
526 str = _("Incremental");
529 str = _("Differential");
534 case L_VERIFY_CATALOG:
535 str = _("Verify Catalog");
538 str = _("Init Catalog");
540 case L_VERIFY_VOLUME_TO_CATALOG:
541 str = _("Volume to Catalog");
543 case L_VERIFY_DISK_TO_CATALOG:
544 str = _("Disk to Catalog");
553 str = _("Unknown Job Level");
562 static void bsock_sendit(const char *msg, int len, void *arg)
564 BSOCK *user = (BSOCK *)arg;
566 memcpy(user->msg, msg, len+1);
567 user->msglen = len+1;
572 * Status command from Director
574 bool status_cmd(JCR *jcr)
576 BSOCK *user = jcr->dir_bsock;
578 bnet_fsend(user, "\n");
579 output_status(bsock_sendit, (void *)user);
581 bnet_sig(user, BNET_EOD);
586 * .status command from Director
588 bool qstatus_cmd(JCR *jcr)
590 BSOCK *dir = jcr->dir_bsock;
595 if (sscanf(dir->msg, qstatus, time.c_str()) != 1) {
596 pm_strcpy(jcr->errmsg, dir->msg);
597 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
598 bnet_fsend(dir, _("3900 Bad .status command, missing argument.\n"));
599 bnet_sig(dir, BNET_EOD);
604 if (strcmp(time.c_str(), "current") == 0) {
605 bnet_fsend(dir, OKqstatus, time.c_str());
607 if (njcr->JobId != 0) {
608 bnet_fsend(dir, DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
612 } else if (strcmp(time.c_str(), "last") == 0) {
613 bnet_fsend(dir, OKqstatus, time.c_str());
614 if ((last_jobs) && (last_jobs->size() > 0)) {
615 job = (s_last_job*)last_jobs->last();
616 bnet_fsend(dir, DotStatusJob, job->JobId, job->JobStatus, job->Errors);
619 pm_strcpy(jcr->errmsg, dir->msg);
620 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
621 bnet_fsend(dir, _("3900 Bad .status command, wrong argument.\n"));
622 bnet_sig(dir, BNET_EOD);
625 bnet_sig(dir, BNET_EOD);
629 #if defined(HAVE_WIN32)
632 char *bac_status(char *buf, int buf_len)
635 const char *termstat = _("Bacula Storage: Idle");
636 struct s_last_job *job;
637 int stat = 0; /* Idle */
642 Dmsg0(1000, "Begin bac_status jcr loop.\n");
644 if (njcr->JobId != 0) {
646 termstat = _("Bacula Storage: Running");
655 if (last_jobs->size() > 0) {
656 job = (struct s_last_job *)last_jobs->last();
657 stat = job->JobStatus;
658 switch (job->JobStatus) {
660 termstat = _("Bacula Storage: Last Job Canceled");
662 case JS_ErrorTerminated:
664 termstat = _("Bacula Storage: Last Job Failed");
668 termstat = _("Bacula Storage: Last Job had Warnings");
673 Dmsg0(1000, "End bac_status jcr loop.\n");
677 bstrncpy(buf, termstat, buf_len);
682 #endif /* HAVE_WIN32 */