2 * This file handles the status command
4 * Kern Sibbald, May MMIII
10 Bacula® - The Network Backup Solution
12 Copyright (C) 2003-2006 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);
393 sec = time(NULL) - jcr->run_time;
397 bps = jcr->JobBytes / sec;
398 len = Mmsg(msg, _(" Files=%s Bytes=%s Bytes/sec=%s\n"),
399 edit_uint64_with_commas(jcr->JobFiles, b1),
400 edit_uint64_with_commas(jcr->JobBytes, b2),
401 edit_uint64_with_commas(bps, b3));
402 sendit(msg, len, arg);
405 if (jcr->file_bsock) {
406 len = Mmsg(msg, _(" FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
407 edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
408 jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
409 jcr->file_bsock->fd);
410 sendit(msg, len, arg);
412 len = Mmsg(msg, _(" FDSocket closed\n"));
413 sendit(msg, len, arg);
421 len = Mmsg(msg, _("No Jobs running.\n"));
422 sendit(msg, len, arg);
424 sendit("====\n", 5, arg);
426 free_pool_memory(msg);
429 static void list_jobs_waiting_on_reservation(void sendit(const char *msg, int len, void *sarg), void *arg)
434 msg = _("\nJobs waiting to reserve a drive:\n");
435 sendit(msg, strlen(msg), arg);
438 if (!jcr->reserve_msgs) {
441 send_drive_reserve_messages(jcr, sendit, arg);
445 sendit("====\n", 5, arg);
449 static void list_terminated_jobs(void sendit(const char *msg, int len, void *sarg), void *arg)
451 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
453 struct s_last_job *je;
456 msg = _("\nTerminated Jobs:\n");
457 sendit(msg, strlen(msg), arg);
458 if (last_jobs->size() == 0) {
459 sendit("====\n", 5, arg);
462 lock_last_jobs_list();
463 msg = _(" JobId Level Files Bytes Status Finished Name \n");
464 sendit(msg, strlen(msg), arg);
465 msg = _("===================================================================\n");
466 sendit(msg, strlen(msg), arg);
467 foreach_dlist(je, last_jobs) {
468 char JobName[MAX_NAME_LENGTH];
469 const char *termstat;
472 bstrftime_nc(dt, sizeof(dt), je->end_time);
473 switch (je->JobType) {
476 bstrncpy(level, " ", sizeof(level));
479 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
483 switch (je->JobStatus) {
485 termstat = _("Created");
488 case JS_ErrorTerminated:
489 termstat = _("Error");
492 termstat = _("Diffs");
495 termstat = _("Cancel");
501 termstat = _("Other");
504 bstrncpy(JobName, je->Job, sizeof(JobName));
505 /* There are three periods after the Job name */
507 for (int i=0; i<3; i++) {
508 if ((p=strrchr(JobName, '.')) != NULL) {
512 bsnprintf(buf, sizeof(buf), _("%6d %-6s %8s %10s %-7s %-8s %s\n"),
515 edit_uint64_with_commas(je->JobFiles, b1),
516 edit_uint64_with_suffix(je->JobBytes, b2),
519 sendit(buf, strlen(buf), arg);
521 unlock_last_jobs_list();
522 sendit("====\n", 5, arg);
526 * Convert Job Level into a string
528 static const char *level_to_str(int level)
539 str = _("Incremental");
542 str = _("Differential");
547 case L_VERIFY_CATALOG:
548 str = _("Verify Catalog");
551 str = _("Init Catalog");
553 case L_VERIFY_VOLUME_TO_CATALOG:
554 str = _("Volume to Catalog");
556 case L_VERIFY_DISK_TO_CATALOG:
557 str = _("Disk to Catalog");
566 str = _("Unknown Job Level");
575 static void bsock_sendit(const char *msg, int len, void *arg)
577 BSOCK *user = (BSOCK *)arg;
579 memcpy(user->msg, msg, len+1);
580 user->msglen = len+1;
585 * Status command from Director
587 bool status_cmd(JCR *jcr)
589 BSOCK *user = jcr->dir_bsock;
591 bnet_fsend(user, "\n");
592 output_status(bsock_sendit, (void *)user);
594 bnet_sig(user, BNET_EOD);
599 * .status command from Director
601 bool qstatus_cmd(JCR *jcr)
603 BSOCK *dir = jcr->dir_bsock;
608 if (sscanf(dir->msg, qstatus, time.c_str()) != 1) {
609 pm_strcpy(jcr->errmsg, dir->msg);
610 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
611 bnet_fsend(dir, _("3900 Bad .status command, missing argument.\n"));
612 bnet_sig(dir, BNET_EOD);
617 if (strcmp(time.c_str(), "current") == 0) {
618 bnet_fsend(dir, OKqstatus, time.c_str());
620 if (njcr->JobId != 0) {
621 bnet_fsend(dir, DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
625 } else if (strcmp(time.c_str(), "last") == 0) {
626 bnet_fsend(dir, OKqstatus, time.c_str());
627 if ((last_jobs) && (last_jobs->size() > 0)) {
628 job = (s_last_job*)last_jobs->last();
629 bnet_fsend(dir, DotStatusJob, job->JobId, job->JobStatus, job->Errors);
632 pm_strcpy(jcr->errmsg, dir->msg);
633 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
634 bnet_fsend(dir, _("3900 Bad .status command, wrong argument.\n"));
635 bnet_sig(dir, BNET_EOD);
638 bnet_sig(dir, BNET_EOD);
642 #if defined(HAVE_WIN32)
645 char *bac_status(char *buf, int buf_len)
648 const char *termstat = _("Bacula Storage: Idle");
649 struct s_last_job *job;
650 int stat = 0; /* Idle */
655 Dmsg0(1000, "Begin bac_status jcr loop.\n");
657 if (njcr->JobId != 0) {
659 termstat = _("Bacula Storage: Running");
668 if (last_jobs->size() > 0) {
669 job = (struct s_last_job *)last_jobs->last();
670 stat = job->JobStatus;
671 switch (job->JobStatus) {
673 termstat = _("Bacula Storage: Last Job Canceled");
675 case JS_ErrorTerminated:
677 termstat = _("Bacula Storage: Last Job Failed");
681 termstat = _("Bacula Storage: Last Job had Warnings");
686 Dmsg0(1000, "End bac_status jcr loop.\n");
690 bstrncpy(buf, termstat, buf_len);
695 #endif /* HAVE_WIN32 */