2 * This file handles the status command
4 * Kern Sibbald, May MMIII
10 Bacula® - The Network Backup Solution
12 Copyright (C) 2003-2007 Free Software Foundation Europe e.V.
14 The main author of Bacula is Kern Sibbald, with contributions from
15 many others, a complete list can be found in the file AUTHORS.
16 This program is Free Software; you can redistribute it and/or
17 modify it under the terms of version two of the GNU General Public
18 License as published by the Free Software Foundation plus additions
19 that are listed in the file LICENSE.
21 This program is distributed in the hope that it will be useful, but
22 WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 General Public License for more details.
26 You should have received a copy of the GNU General Public License
27 along with this program; if not, write to the Free Software
28 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
31 Bacula® is a registered trademark of John Walker.
32 The licensor of Bacula is the Free Software Foundation Europe
33 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
34 Switzerland, email:ftf@fsfeurope.org.
40 /* Exported variables */
42 /* Imported variables */
43 extern BSOCK *filed_chan;
44 extern int r_first, r_last;
45 extern struct s_res resources[];
47 /* Static variables */
48 static char qstatus[] = ".status %127s\n";
50 static char OKqstatus[] = "3000 OK .status\n";
51 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
54 /* Forward referenced functions */
55 static void send_blocked_status(DEVICE *dev, void sendit(const char *msg, int len, void *sarg), void *arg);
56 static void list_terminated_jobs(void sendit(const char *msg, int len, void *sarg), void *arg);
57 static void list_running_jobs(void sendit(const char *msg, int len, void *sarg), void *arg);
58 static void list_jobs_waiting_on_reservation(void sendit(const char *msg, int len, void *sarg), void *arg);
60 static const char *level_to_str(int level);
63 * Status command from Director
65 void output_status(void sendit(const char *msg, int len, void *sarg), void *arg)
70 char dt[MAX_TIME_LENGTH];
71 char *msg, b1[35], b2[35], b3[35], b4[35];
75 msg = (char *)get_pool_memory(PM_MESSAGE);
77 len = Mmsg(msg, _("%s Version: %s (%s) %s %s %s\n"),
78 my_name, VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
79 sendit(msg, len, arg);
81 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
84 len = Mmsg(msg, _("Daemon started %s, %d Job%s run since started.\n"),
85 dt, num_jobs_run, num_jobs_run == 1 ? "" : "s");
86 sendit(msg, len, arg);
88 len = Mmsg(msg, _(" Heap: bytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
89 edit_uint64_with_commas(sm_bytes, b1),
90 edit_uint64_with_commas(sm_max_bytes, b2),
91 edit_uint64_with_commas(sm_buffers, b3),
92 edit_uint64_with_commas(sm_max_buffers, b4));
93 sendit(msg, len, arg);
98 list_running_jobs(sendit, arg);
101 * List jobs stuck in reservation system
103 list_jobs_waiting_on_reservation(sendit, arg);
106 * List terminated jobs
108 list_terminated_jobs(sendit, arg);
113 len = Mmsg(msg, _("\nDevice status:\n"));
114 sendit(msg, len, arg);
116 foreach_res(changer, R_AUTOCHANGER) {
117 len = Mmsg(msg, _("Autochanger \"%s\" with devices:\n"),
119 sendit(msg, len, arg);
121 foreach_alist(device, changer->device) {
123 len = Mmsg(msg, " %s\n", device->dev->print_name());
124 sendit(msg, len, arg);
126 len = Mmsg(msg, " %s\n", device->hdr.name);
127 sendit(msg, len, arg);
131 foreach_res(device, R_DEVICE) {
133 if (dev && dev->is_open()) {
134 if (dev->is_labeled()) {
135 len = Mmsg(msg, _("Device %s is mounted with Volume=\"%s\" Pool=\"%s\"\n"),
136 dev->print_name(), dev->VolHdr.VolumeName,
137 dev->pool_name[0]?dev->pool_name:"*unknown*");
138 sendit(msg, len, arg);
140 len = Mmsg(msg, _("Device %s open but no Bacula volume is currently mounted.\n"),
142 sendit(msg, len, arg);
144 send_blocked_status(dev, sendit, arg);
145 if (dev->can_append()) {
146 bpb = dev->VolCatInfo.VolCatBlocks;
150 bpb = dev->VolCatInfo.VolCatBytes / bpb;
151 len = Mmsg(msg, _(" Total Bytes=%s Blocks=%s Bytes/block=%s\n"),
152 edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, b1),
153 edit_uint64_with_commas(dev->VolCatInfo.VolCatBlocks, b2),
154 edit_uint64_with_commas(bpb, b3));
155 sendit(msg, len, arg);
156 } else { /* reading */
157 bpb = dev->VolCatInfo.VolCatReads;
161 if (dev->VolCatInfo.VolCatRBytes > 0) {
162 bpb = dev->VolCatInfo.VolCatRBytes / bpb;
166 len = Mmsg(msg, _(" Total Bytes Read=%s Blocks Read=%s Bytes/block=%s\n"),
167 edit_uint64_with_commas(dev->VolCatInfo.VolCatRBytes, b1),
168 edit_uint64_with_commas(dev->VolCatInfo.VolCatReads, b2),
169 edit_uint64_with_commas(bpb, b3));
170 sendit(msg, len, arg);
172 len = Mmsg(msg, _(" Positioned at File=%s Block=%s\n"),
173 edit_uint64_with_commas(dev->file, b1),
174 edit_uint64_with_commas(dev->block_num, b2));
175 sendit(msg, len, arg);
179 len = Mmsg(msg, _("Device %s is not open.\n"), dev->print_name());
180 sendit(msg, len, arg);
181 send_blocked_status(dev, sendit, arg);
183 len = Mmsg(msg, _("Device \"%s\" is not open or does not exist.\n"), device->hdr.name);
184 sendit(msg, len, arg);
188 sendit("====\n\n", 6, arg);
189 len = Mmsg(msg, _("In Use Volume status:\n"));
190 sendit(msg, len, arg);
191 list_volumes(sendit, arg);
192 sendit("====\n\n", 6, arg);
195 if (debug_level > 10) {
196 bnet_fsend(user, _("====\n\n"));
197 dump_resource(R_DEVICE, resources[R_DEVICE-r_first].res_head, sendit, user);
198 bnet_fsend(user, _("====\n\n"));
202 list_spool_stats(sendit, arg);
204 free_pool_memory(msg);
207 static void send_blocked_status(DEVICE *dev, void sendit(const char *msg, int len, void *sarg), void *arg)
212 msg = (char *)get_pool_memory(PM_MESSAGE);
215 len = Mmsg(msg, _("No DEVICE structure.\n\n"));
216 sendit(msg, len, arg);
217 free_pool_memory(msg);
220 switch (dev->dev_blocked) {
222 len = Mmsg(msg, _(" Device is BLOCKED. User unmounted.\n"));
223 sendit(msg, len, arg);
225 case BST_UNMOUNTED_WAITING_FOR_SYSOP:
226 len = Mmsg(msg, _(" Device is BLOCKED. User unmounted during wait for media/mount.\n"));
227 sendit(msg, len, arg);
229 case BST_WAITING_FOR_SYSOP:
231 dlist *dcrs = dev->attached_dcrs;
232 bool found_jcr = false;
237 for (dcr = (DCR *)dcrs->first(); dcr != NULL; dcr = (DCR *)dcrs->next(dcr)) {
238 if (dcr->jcr->JobStatus == JS_WaitMount) {
239 len = Mmsg(msg, _(" Device is BLOCKED waiting for mount of volume \"%s\".\n"),
241 sendit(msg, len, arg);
248 len = Mmsg(msg, _(" Device is BLOCKED waiting for media.\n"));
249 sendit(msg, len, arg);
253 case BST_DOING_ACQUIRE:
254 len = Mmsg(msg, _(" Device is being initialized.\n"));
255 sendit(msg, len, arg);
257 case BST_WRITING_LABEL:
258 len = Mmsg(msg, _(" Device is blocked labeling a Volume.\n"));
259 sendit(msg, len, arg);
264 /* Send autochanger slot status */
265 if (dev->is_autochanger()) {
267 len = Mmsg(msg, _(" Slot %d is loaded in drive %d.\n"),
268 dev->Slot, dev->drive_index);
269 sendit(msg, len, arg);
270 } else if (dev->Slot == 0) {
271 len = Mmsg(msg, _(" Drive %d is not loaded.\n"), dev->drive_index);
272 sendit(msg, len, arg);
274 len = Mmsg(msg, _(" Drive %d status unknown.\n"), dev->drive_index);
275 sendit(msg, len, arg);
278 if (debug_level > 1) {
279 len = Mmsg(msg, _("Configured device capabilities:\n"));
280 sendit(msg, len, arg);
282 len = Mmsg(msg, "%sEOF %sBSR %sBSF %sFSR %sFSF %sEOM %sREM %sRACCESS %sAUTOMOUNT %sLABEL %sANONVOLS %sALWAYSOPEN\n",
283 dev->capabilities & CAP_EOF ? "" : "!",
284 dev->capabilities & CAP_BSR ? "" : "!",
285 dev->capabilities & CAP_BSF ? "" : "!",
286 dev->capabilities & CAP_FSR ? "" : "!",
287 dev->capabilities & CAP_FSF ? "" : "!",
288 dev->capabilities & CAP_EOM ? "" : "!",
289 dev->capabilities & CAP_REM ? "" : "!",
290 dev->capabilities & CAP_RACCESS ? "" : "!",
291 dev->capabilities & CAP_AUTOMOUNT ? "" : "!",
292 dev->capabilities & CAP_LABEL ? "" : "!",
293 dev->capabilities & CAP_ANONVOLS ? "" : "!",
294 dev->capabilities & CAP_ALWAYSOPEN ? "" : "!");
295 sendit(msg, len, arg);
297 len = Mmsg(msg, _("Device state:\n"));
298 sendit(msg, len, arg);
300 len = Mmsg(msg, "%sOPENED %sTAPE %sLABEL %sMALLOC %sAPPEND %sREAD %sEOT %sWEOT %sEOF %sNEXTVOL %sSHORT %sMOUNTED\n",
301 dev->is_open() ? "" : "!",
302 dev->is_tape() ? "" : "!",
303 dev->is_labeled() ? "" : "!",
304 dev->state & ST_MALLOC ? "" : "!",
305 dev->can_append() ? "" : "!",
306 dev->can_read() ? "" : "!",
307 dev->at_eot() ? "" : "!",
308 dev->state & ST_WEOT ? "" : "!",
309 dev->at_eof() ? "" : "!",
310 dev->state & ST_NEXTVOL ? "" : "!",
311 dev->state & ST_SHORT ? "" : "!",
312 dev->state & ST_MOUNTED ? "" : "!");
313 sendit(msg, len, arg);
315 len = Mmsg(msg, _("num_writers=%d block=%d\n\n"), dev->num_writers, dev->dev_blocked);
316 sendit(msg, len, arg);
318 len = Mmsg(msg, _("Device parameters:\n"));
319 sendit(msg, len, arg);
321 len = Mmsg(msg, _("Archive name: %s Device name: %s\n"), dev->archive_name(),
323 sendit(msg, len, arg);
325 len = Mmsg(msg, _("File=%u block=%u\n"), dev->file, dev->block_num);
326 sendit(msg, len, arg);
328 len = Mmsg(msg, _("Min block=%u Max block=%u\n"), dev->min_block_size, dev->max_block_size);
329 sendit(msg, len, arg);
332 free_pool_memory(msg);
335 static void list_running_jobs(void sendit(const char *msg, int len, void *sarg), void *arg)
341 char JobName[MAX_NAME_LENGTH];
342 char *msg, b1[30], b2[30], b3[30];
345 msg = (char *)get_pool_memory(PM_MESSAGE);
347 len = Mmsg(msg, _("\nRunning Jobs:\n"));
348 sendit(msg, len, arg);
351 if (jcr->JobStatus == JS_WaitFD) {
352 len = Mmsg(msg, _("%s Job %s waiting for Client connection.\n"),
353 job_type_to_str(jcr->JobType), jcr->Job);
354 sendit(msg, len, arg);
357 rdcr = jcr->read_dcr;
358 if ((dcr && dcr->device) || rdcr && rdcr->device) {
359 bstrncpy(JobName, jcr->Job, sizeof(JobName));
360 /* There are three periods after the Job name */
362 for (int i=0; i<3; i++) {
363 if ((p=strrchr(JobName, '.')) != NULL) {
367 if (rdcr && rdcr->device) {
368 len = Mmsg(msg, _("Reading: %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 rdcr->dev?rdcr->dev->print_name():
377 rdcr->device->device_name);
378 sendit(msg, len, arg);
380 if (dcr && dcr->device) {
381 len = Mmsg(msg, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
382 " pool=\"%s\" device=\"%s\"\n"),
383 job_level_to_str(jcr->JobLevel),
384 job_type_to_str(jcr->JobType),
389 dcr->dev?dcr->dev->print_name():
390 dcr->device->device_name);
391 sendit(msg, len, arg);
392 len= Mmsg(msg, _(" spooling=%d despooling=%d despool_wait=%d\n"),
393 dcr->spooling, dcr->despooling, dcr->despool_wait);
394 sendit(msg, len, arg);
396 sec = time(NULL) - jcr->run_time;
400 bps = jcr->JobBytes / sec;
401 len = Mmsg(msg, _(" Files=%s Bytes=%s Bytes/sec=%s\n"),
402 edit_uint64_with_commas(jcr->JobFiles, b1),
403 edit_uint64_with_commas(jcr->JobBytes, b2),
404 edit_uint64_with_commas(bps, b3));
405 sendit(msg, len, arg);
408 if (jcr->file_bsock) {
409 len = Mmsg(msg, _(" FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
410 edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
411 jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
412 jcr->file_bsock->fd);
413 sendit(msg, len, arg);
415 len = Mmsg(msg, _(" FDSocket closed\n"));
416 sendit(msg, len, arg);
424 len = Mmsg(msg, _("No Jobs running.\n"));
425 sendit(msg, len, arg);
427 sendit("====\n", 5, arg);
429 free_pool_memory(msg);
432 static void list_jobs_waiting_on_reservation(void sendit(const char *msg, int len, void *sarg), void *arg)
437 msg = _("\nJobs waiting to reserve a drive:\n");
438 sendit(msg, strlen(msg), arg);
441 if (!jcr->reserve_msgs) {
444 send_drive_reserve_messages(jcr, sendit, arg);
448 sendit("====\n", 5, arg);
452 static void list_terminated_jobs(void sendit(const char *msg, int len, void *sarg), void *arg)
454 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
456 struct s_last_job *je;
459 msg = _("\nTerminated Jobs:\n");
460 sendit(msg, strlen(msg), arg);
461 if (last_jobs->size() == 0) {
462 sendit("====\n", 5, arg);
465 lock_last_jobs_list();
466 msg = _(" JobId Level Files Bytes Status Finished Name \n");
467 sendit(msg, strlen(msg), arg);
468 msg = _("===================================================================\n");
469 sendit(msg, strlen(msg), arg);
470 foreach_dlist(je, last_jobs) {
471 char JobName[MAX_NAME_LENGTH];
472 const char *termstat;
475 bstrftime_nc(dt, sizeof(dt), je->end_time);
476 switch (je->JobType) {
479 bstrncpy(level, " ", sizeof(level));
482 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
486 switch (je->JobStatus) {
488 termstat = _("Created");
491 case JS_ErrorTerminated:
492 termstat = _("Error");
495 termstat = _("Diffs");
498 termstat = _("Cancel");
504 termstat = _("Other");
507 bstrncpy(JobName, je->Job, sizeof(JobName));
508 /* There are three periods after the Job name */
510 for (int i=0; i<3; i++) {
511 if ((p=strrchr(JobName, '.')) != NULL) {
515 bsnprintf(buf, sizeof(buf), _("%6d %-6s %8s %10s %-7s %-8s %s\n"),
518 edit_uint64_with_commas(je->JobFiles, b1),
519 edit_uint64_with_suffix(je->JobBytes, b2),
522 sendit(buf, strlen(buf), arg);
524 unlock_last_jobs_list();
525 sendit("====\n", 5, arg);
529 * Convert Job Level into a string
531 static const char *level_to_str(int level)
542 str = _("Incremental");
545 str = _("Differential");
550 case L_VERIFY_CATALOG:
551 str = _("Verify Catalog");
554 str = _("Init Catalog");
556 case L_VERIFY_VOLUME_TO_CATALOG:
557 str = _("Volume to Catalog");
559 case L_VERIFY_DISK_TO_CATALOG:
560 str = _("Disk to Catalog");
569 str = _("Unknown Job Level");
578 static void bsock_sendit(const char *msg, int len, void *arg)
580 BSOCK *user = (BSOCK *)arg;
582 memcpy(user->msg, msg, len+1);
583 user->msglen = len+1;
588 * Status command from Director
590 bool status_cmd(JCR *jcr)
592 BSOCK *user = jcr->dir_bsock;
594 bnet_fsend(user, "\n");
595 output_status(bsock_sendit, (void *)user);
597 bnet_sig(user, BNET_EOD);
602 * .status command from Director
604 bool qstatus_cmd(JCR *jcr)
606 BSOCK *dir = jcr->dir_bsock;
611 if (sscanf(dir->msg, qstatus, time.c_str()) != 1) {
612 pm_strcpy(jcr->errmsg, dir->msg);
613 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
614 bnet_fsend(dir, _("3900 Bad .status command, missing argument.\n"));
615 bnet_sig(dir, BNET_EOD);
620 if (strcmp(time.c_str(), "current") == 0) {
621 bnet_fsend(dir, OKqstatus, time.c_str());
623 if (njcr->JobId != 0) {
624 bnet_fsend(dir, DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
628 } else if (strcmp(time.c_str(), "last") == 0) {
629 bnet_fsend(dir, OKqstatus, time.c_str());
630 if ((last_jobs) && (last_jobs->size() > 0)) {
631 job = (s_last_job*)last_jobs->last();
632 bnet_fsend(dir, DotStatusJob, job->JobId, job->JobStatus, job->Errors);
635 pm_strcpy(jcr->errmsg, dir->msg);
636 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
637 bnet_fsend(dir, _("3900 Bad .status command, wrong argument.\n"));
638 bnet_sig(dir, BNET_EOD);
641 bnet_sig(dir, BNET_EOD);
645 #if defined(HAVE_WIN32)
648 char *bac_status(char *buf, int buf_len)
651 const char *termstat = _("Bacula Storage: Idle");
652 struct s_last_job *job;
653 int stat = 0; /* Idle */
658 Dmsg0(1000, "Begin bac_status jcr loop.\n");
660 if (njcr->JobId != 0) {
662 termstat = _("Bacula Storage: Running");
671 if (last_jobs->size() > 0) {
672 job = (struct s_last_job *)last_jobs->last();
673 stat = job->JobStatus;
674 switch (job->JobStatus) {
676 termstat = _("Bacula Storage: Last Job Canceled");
678 case JS_ErrorTerminated:
680 termstat = _("Bacula Storage: Last Job Failed");
684 termstat = _("Bacula Storage: Last Job had Warnings");
689 Dmsg0(1000, "End bac_status jcr loop.\n");
693 bstrncpy(buf, termstat, buf_len);
698 #endif /* HAVE_WIN32 */