2 Bacula® - The Network Backup Solution
4 Copyright (C) 2003-2007 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation plus additions
11 that are listed in the file LICENSE.
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of John Walker.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
29 * This file handles the status command
31 * Kern Sibbald, May MMIII
40 /* Exported variables */
42 /* Imported variables */
43 extern BSOCK *filed_chan;
44 extern int r_first, r_last;
45 extern struct s_res resources[];
46 extern void *start_heap;
48 /* Static variables */
49 static char qstatus[] = ".status %127s\n";
51 static char OKqstatus[] = "3000 OK .status\n";
52 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
55 /* Forward referenced functions */
56 static void send_blocked_status(DEVICE *dev, void sendit(const char *msg, int len, void *sarg), void *arg);
57 static void list_terminated_jobs(void sendit(const char *msg, int len, void *sarg), void *arg);
58 static void list_running_jobs(void sendit(const char *msg, int len, void *sarg), void *arg);
59 static void list_jobs_waiting_on_reservation(void sendit(const char *msg, int len, void *sarg), void *arg);
61 static const char *level_to_str(int level);
64 * Status command from Director
66 void output_status(void sendit(const char *msg, int len, void *sarg), void *arg)
71 char dt[MAX_TIME_LENGTH];
72 char *msg, b1[35], b2[35], b3[35], b4[35], b5[35];
76 msg = (char *)get_pool_memory(PM_MESSAGE);
78 len = Mmsg(msg, _("%s Version: %s (%s) %s %s %s\n"),
79 my_name, VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
80 sendit(msg, len, arg);
82 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
85 len = Mmsg(msg, _("Daemon started %s, %d Job%s run since started.\n"),
86 dt, num_jobs_run, num_jobs_run == 1 ? "" : "s");
87 sendit(msg, len, arg);
89 len = Mmsg(msg, _(" Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
90 edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
91 edit_uint64_with_commas(sm_bytes, b2),
92 edit_uint64_with_commas(sm_max_bytes, b3),
93 edit_uint64_with_commas(sm_buffers, b4),
94 edit_uint64_with_commas(sm_max_buffers, b5));
95 sendit(msg, len, arg);
100 list_running_jobs(sendit, arg);
103 * List jobs stuck in reservation system
105 list_jobs_waiting_on_reservation(sendit, arg);
108 * List terminated jobs
110 list_terminated_jobs(sendit, arg);
115 len = Mmsg(msg, _("\nDevice status:\n"));
116 sendit(msg, len, arg);
118 foreach_res(changer, R_AUTOCHANGER) {
119 len = Mmsg(msg, _("Autochanger \"%s\" with devices:\n"),
121 sendit(msg, len, arg);
123 foreach_alist(device, changer->device) {
125 len = Mmsg(msg, " %s\n", device->dev->print_name());
126 sendit(msg, len, arg);
128 len = Mmsg(msg, " %s\n", device->hdr.name);
129 sendit(msg, len, arg);
133 foreach_res(device, R_DEVICE) {
135 if (dev && dev->is_open()) {
136 if (dev->is_labeled()) {
137 len = Mmsg(msg, _("Device %s is mounted with Volume=\"%s\" Pool=\"%s\"\n"),
138 dev->print_name(), dev->VolHdr.VolumeName,
139 dev->pool_name[0]?dev->pool_name:"*unknown*");
140 sendit(msg, len, arg);
142 len = Mmsg(msg, _("Device %s open but no Bacula volume is currently mounted.\n"),
144 sendit(msg, len, arg);
146 send_blocked_status(dev, sendit, arg);
147 if (dev->can_append()) {
148 bpb = dev->VolCatInfo.VolCatBlocks;
152 bpb = dev->VolCatInfo.VolCatBytes / bpb;
153 len = Mmsg(msg, _(" Total Bytes=%s Blocks=%s Bytes/block=%s\n"),
154 edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, b1),
155 edit_uint64_with_commas(dev->VolCatInfo.VolCatBlocks, b2),
156 edit_uint64_with_commas(bpb, b3));
157 sendit(msg, len, arg);
158 } else { /* reading */
159 bpb = dev->VolCatInfo.VolCatReads;
163 if (dev->VolCatInfo.VolCatRBytes > 0) {
164 bpb = dev->VolCatInfo.VolCatRBytes / bpb;
168 len = Mmsg(msg, _(" Total Bytes Read=%s Blocks Read=%s Bytes/block=%s\n"),
169 edit_uint64_with_commas(dev->VolCatInfo.VolCatRBytes, b1),
170 edit_uint64_with_commas(dev->VolCatInfo.VolCatReads, b2),
171 edit_uint64_with_commas(bpb, b3));
172 sendit(msg, len, arg);
174 len = Mmsg(msg, _(" Positioned at File=%s Block=%s\n"),
175 edit_uint64_with_commas(dev->file, b1),
176 edit_uint64_with_commas(dev->block_num, b2));
177 sendit(msg, len, arg);
181 len = Mmsg(msg, _("Device %s is not open.\n"), dev->print_name());
182 sendit(msg, len, arg);
183 send_blocked_status(dev, sendit, arg);
185 len = Mmsg(msg, _("Device \"%s\" is not open or does not exist.\n"), device->hdr.name);
186 sendit(msg, len, arg);
190 sendit("====\n\n", 6, arg);
191 len = Mmsg(msg, _("In Use Volume status:\n"));
192 sendit(msg, len, arg);
193 list_volumes(sendit, arg);
194 sendit("====\n\n", 6, arg);
197 if (debug_level > 10) {
198 bnet_fsend(user, _("====\n\n"));
199 dump_resource(R_DEVICE, resources[R_DEVICE-r_first].res_head, sendit, user);
200 bnet_fsend(user, _("====\n\n"));
204 list_spool_stats(sendit, arg);
206 free_pool_memory(msg);
209 static void send_blocked_status(DEVICE *dev, void sendit(const char *msg, int len, void *sarg), void *arg)
214 msg = (char *)get_pool_memory(PM_MESSAGE);
217 len = Mmsg(msg, _("No DEVICE structure.\n\n"));
218 sendit(msg, len, arg);
219 free_pool_memory(msg);
222 switch (dev->blocked()) {
224 len = Mmsg(msg, _(" Device is BLOCKED. User unmounted.\n"));
225 sendit(msg, len, arg);
227 case BST_UNMOUNTED_WAITING_FOR_SYSOP:
228 len = Mmsg(msg, _(" Device is BLOCKED. User unmounted during wait for media/mount.\n"));
229 sendit(msg, len, arg);
231 case BST_WAITING_FOR_SYSOP:
233 dlist *dcrs = dev->attached_dcrs;
234 bool found_jcr = false;
239 for (dcr = (DCR *)dcrs->first(); dcr != NULL; dcr = (DCR *)dcrs->next(dcr)) {
240 if (dcr->jcr->JobStatus == JS_WaitMount) {
241 len = Mmsg(msg, _(" Device is BLOCKED waiting for mount of volume \"%s\".\n"),
243 sendit(msg, len, arg);
250 len = Mmsg(msg, _(" Device is BLOCKED waiting for media.\n"));
251 sendit(msg, len, arg);
255 case BST_DOING_ACQUIRE:
256 len = Mmsg(msg, _(" Device is being initialized.\n"));
257 sendit(msg, len, arg);
259 case BST_WRITING_LABEL:
260 len = Mmsg(msg, _(" Device is blocked labeling a Volume.\n"));
261 sendit(msg, len, arg);
266 /* Send autochanger slot status */
267 if (dev->is_autochanger()) {
269 len = Mmsg(msg, _(" Slot %d is loaded in drive %d.\n"),
270 dev->Slot, dev->drive_index);
271 sendit(msg, len, arg);
272 } else if (dev->Slot == 0) {
273 len = Mmsg(msg, _(" Drive %d is not loaded.\n"), dev->drive_index);
274 sendit(msg, len, arg);
276 len = Mmsg(msg, _(" Drive %d status unknown.\n"), dev->drive_index);
277 sendit(msg, len, arg);
280 if (debug_level > 1) {
281 len = Mmsg(msg, _("Configured device capabilities:\n"));
282 sendit(msg, len, arg);
284 len = Mmsg(msg, "%sEOF %sBSR %sBSF %sFSR %sFSF %sEOM %sREM %sRACCESS %sAUTOMOUNT %sLABEL %sANONVOLS %sALWAYSOPEN\n",
285 dev->capabilities & CAP_EOF ? "" : "!",
286 dev->capabilities & CAP_BSR ? "" : "!",
287 dev->capabilities & CAP_BSF ? "" : "!",
288 dev->capabilities & CAP_FSR ? "" : "!",
289 dev->capabilities & CAP_FSF ? "" : "!",
290 dev->capabilities & CAP_EOM ? "" : "!",
291 dev->capabilities & CAP_REM ? "" : "!",
292 dev->capabilities & CAP_RACCESS ? "" : "!",
293 dev->capabilities & CAP_AUTOMOUNT ? "" : "!",
294 dev->capabilities & CAP_LABEL ? "" : "!",
295 dev->capabilities & CAP_ANONVOLS ? "" : "!",
296 dev->capabilities & CAP_ALWAYSOPEN ? "" : "!");
297 sendit(msg, len, arg);
299 len = Mmsg(msg, _("Device state:\n"));
300 sendit(msg, len, arg);
302 len = Mmsg(msg, "%sOPENED %sTAPE %sLABEL %sMALLOC %sAPPEND %sREAD %sEOT %sWEOT %sEOF %sNEXTVOL %sSHORT %sMOUNTED\n",
303 dev->is_open() ? "" : "!",
304 dev->is_tape() ? "" : "!",
305 dev->is_labeled() ? "" : "!",
306 dev->state & ST_MALLOC ? "" : "!",
307 dev->can_append() ? "" : "!",
308 dev->can_read() ? "" : "!",
309 dev->at_eot() ? "" : "!",
310 dev->state & ST_WEOT ? "" : "!",
311 dev->at_eof() ? "" : "!",
312 dev->state & ST_NEXTVOL ? "" : "!",
313 dev->state & ST_SHORT ? "" : "!",
314 dev->state & ST_MOUNTED ? "" : "!");
315 sendit(msg, len, arg);
317 len = Mmsg(msg, _("num_writers=%d block=%d\n\n"), dev->num_writers, dev->blocked());
318 sendit(msg, len, arg);
320 len = Mmsg(msg, _("Device parameters:\n"));
321 sendit(msg, len, arg);
323 len = Mmsg(msg, _("Archive name: %s Device name: %s\n"), dev->archive_name(),
325 sendit(msg, len, arg);
327 len = Mmsg(msg, _("File=%u block=%u\n"), dev->file, dev->block_num);
328 sendit(msg, len, arg);
330 len = Mmsg(msg, _("Min block=%u Max block=%u\n"), dev->min_block_size, dev->max_block_size);
331 sendit(msg, len, arg);
334 free_pool_memory(msg);
337 static void list_running_jobs(void sendit(const char *msg, int len, void *sarg), void *arg)
343 char JobName[MAX_NAME_LENGTH];
344 char *msg, b1[30], b2[30], b3[30];
347 msg = (char *)get_pool_memory(PM_MESSAGE);
349 len = Mmsg(msg, _("\nRunning Jobs:\n"));
350 sendit(msg, len, arg);
353 if (jcr->JobStatus == JS_WaitFD) {
354 len = Mmsg(msg, _("%s Job %s waiting for Client connection.\n"),
355 job_type_to_str(jcr->JobType), jcr->Job);
356 sendit(msg, len, arg);
359 rdcr = jcr->read_dcr;
360 if ((dcr && dcr->device) || rdcr && rdcr->device) {
361 bstrncpy(JobName, jcr->Job, sizeof(JobName));
362 /* There are three periods after the Job name */
364 for (int i=0; i<3; i++) {
365 if ((p=strrchr(JobName, '.')) != NULL) {
369 if (rdcr && rdcr->device) {
370 len = Mmsg(msg, _("Reading: %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 rdcr->dev?rdcr->dev->print_name():
379 rdcr->device->device_name);
380 sendit(msg, len, arg);
382 if (dcr && dcr->device) {
383 len = Mmsg(msg, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
384 " pool=\"%s\" device=%s\n"),
385 job_level_to_str(jcr->JobLevel),
386 job_type_to_str(jcr->JobType),
391 dcr->dev?dcr->dev->print_name():
392 dcr->device->device_name);
393 sendit(msg, len, arg);
394 len= Mmsg(msg, _(" spooling=%d despooling=%d despool_wait=%d\n"),
395 dcr->spooling, dcr->despooling, dcr->despool_wait);
396 sendit(msg, len, arg);
398 sec = time(NULL) - jcr->run_time;
402 bps = jcr->JobBytes / sec;
403 len = Mmsg(msg, _(" Files=%s Bytes=%s Bytes/sec=%s\n"),
404 edit_uint64_with_commas(jcr->JobFiles, b1),
405 edit_uint64_with_commas(jcr->JobBytes, b2),
406 edit_uint64_with_commas(bps, b3));
407 sendit(msg, len, arg);
410 if (jcr->file_bsock) {
411 len = Mmsg(msg, _(" FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
412 edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
413 jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
414 jcr->file_bsock->fd);
415 sendit(msg, len, arg);
417 len = Mmsg(msg, _(" FDSocket closed\n"));
418 sendit(msg, len, arg);
426 len = Mmsg(msg, _("No Jobs running.\n"));
427 sendit(msg, len, arg);
429 sendit("====\n", 5, arg);
431 free_pool_memory(msg);
434 static void list_jobs_waiting_on_reservation(void sendit(const char *msg, int len, void *sarg), void *arg)
439 msg = _("\nJobs waiting to reserve a drive:\n");
440 sendit(msg, strlen(msg), arg);
443 if (!jcr->reserve_msgs) {
446 send_drive_reserve_messages(jcr, sendit, arg);
450 sendit("====\n", 5, arg);
454 static void list_terminated_jobs(void sendit(const char *msg, int len, void *sarg), void *arg)
456 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
458 struct s_last_job *je;
461 msg = _("\nTerminated Jobs:\n");
462 sendit(msg, strlen(msg), arg);
463 if (last_jobs->size() == 0) {
464 sendit("====\n", 5, arg);
467 lock_last_jobs_list();
468 msg = _(" JobId Level Files Bytes Status Finished Name \n");
469 sendit(msg, strlen(msg), arg);
470 msg = _("===================================================================\n");
471 sendit(msg, strlen(msg), arg);
472 foreach_dlist(je, last_jobs) {
473 char JobName[MAX_NAME_LENGTH];
474 const char *termstat;
477 bstrftime_nc(dt, sizeof(dt), je->end_time);
478 switch (je->JobType) {
481 bstrncpy(level, " ", sizeof(level));
484 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
488 switch (je->JobStatus) {
490 termstat = _("Created");
493 case JS_ErrorTerminated:
494 termstat = _("Error");
497 termstat = _("Diffs");
500 termstat = _("Cancel");
506 termstat = _("Other");
509 bstrncpy(JobName, je->Job, sizeof(JobName));
510 /* There are three periods after the Job name */
512 for (int i=0; i<3; i++) {
513 if ((p=strrchr(JobName, '.')) != NULL) {
517 bsnprintf(buf, sizeof(buf), _("%6d %-6s %8s %10s %-7s %-8s %s\n"),
520 edit_uint64_with_commas(je->JobFiles, b1),
521 edit_uint64_with_suffix(je->JobBytes, b2),
524 sendit(buf, strlen(buf), arg);
526 unlock_last_jobs_list();
527 sendit("====\n", 5, arg);
531 * Convert Job Level into a string
533 static const char *level_to_str(int level)
544 str = _("Incremental");
547 str = _("Differential");
552 case L_VERIFY_CATALOG:
553 str = _("Verify Catalog");
556 str = _("Init Catalog");
558 case L_VERIFY_VOLUME_TO_CATALOG:
559 str = _("Volume to Catalog");
561 case L_VERIFY_DISK_TO_CATALOG:
562 str = _("Disk to Catalog");
571 str = _("Unknown Job Level");
580 static void bsock_sendit(const char *msg, int len, void *arg)
582 BSOCK *user = (BSOCK *)arg;
584 memcpy(user->msg, msg, len+1);
585 user->msglen = len+1;
590 * Status command from Director
592 bool status_cmd(JCR *jcr)
594 BSOCK *user = jcr->dir_bsock;
596 bnet_fsend(user, "\n");
597 output_status(bsock_sendit, (void *)user);
599 bnet_sig(user, BNET_EOD);
604 * .status command from Director
606 bool qstatus_cmd(JCR *jcr)
608 BSOCK *dir = jcr->dir_bsock;
613 if (sscanf(dir->msg, qstatus, time.c_str()) != 1) {
614 pm_strcpy(jcr->errmsg, dir->msg);
615 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
616 bnet_fsend(dir, _("3900 Bad .status command, missing argument.\n"));
617 bnet_sig(dir, BNET_EOD);
622 if (strcmp(time.c_str(), "current") == 0) {
623 bnet_fsend(dir, OKqstatus, time.c_str());
625 if (njcr->JobId != 0) {
626 bnet_fsend(dir, DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
630 } else if (strcmp(time.c_str(), "last") == 0) {
631 bnet_fsend(dir, OKqstatus, time.c_str());
632 if ((last_jobs) && (last_jobs->size() > 0)) {
633 job = (s_last_job*)last_jobs->last();
634 bnet_fsend(dir, DotStatusJob, job->JobId, job->JobStatus, job->Errors);
637 pm_strcpy(jcr->errmsg, dir->msg);
638 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
639 bnet_fsend(dir, _("3900 Bad .status command, wrong argument.\n"));
640 bnet_sig(dir, BNET_EOD);
643 bnet_sig(dir, BNET_EOD);
647 #if defined(HAVE_WIN32)
650 char *bac_status(char *buf, int buf_len)
653 const char *termstat = _("Bacula Storage: Idle");
654 struct s_last_job *job;
655 int stat = 0; /* Idle */
660 Dmsg0(1000, "Begin bac_status jcr loop.\n");
662 if (njcr->JobId != 0) {
664 termstat = _("Bacula Storage: Running");
673 if (last_jobs->size() > 0) {
674 job = (struct s_last_job *)last_jobs->last();
675 stat = job->JobStatus;
676 switch (job->JobStatus) {
678 termstat = _("Bacula Storage: Last Job Canceled");
680 case JS_ErrorTerminated:
682 termstat = _("Bacula Storage: Last Job Failed");
686 termstat = _("Bacula Storage: Last Job had Warnings");
691 Dmsg0(1000, "End bac_status jcr loop.\n");
695 bstrncpy(buf, termstat, buf_len);
700 #endif /* HAVE_WIN32 */