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[];
33 extern char my_name[];
34 extern time_t daemon_start_time;
35 extern int num_jobs_run;
38 /* Static variables */
39 static char qstatus[] = ".status %127s\n";
41 static char OKqstatus[] = "3000 OK .status\n";
42 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
45 /* Forward referenced functions */
46 static void send_blocked_status(JCR *jcr, DEVICE *dev);
47 static void list_terminated_jobs(void *arg);
48 static void list_running_jobs(BSOCK *user);
49 static void sendit(const char *msg, int len, void *arg);
50 static const char *level_to_str(int level);
54 * Status command from Director
56 bool status_cmd(JCR *jcr)
61 BSOCK *user = jcr->dir_bsock;
62 char dt[MAX_TIME_LENGTH];
63 char b1[30], b2[30], b3[30];
66 bnet_fsend(user, _("\n%s Version: %s (%s) %s %s %s\n"), my_name,
67 VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
68 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
69 if (num_jobs_run == 1) {
70 bnet_fsend(user, _("Daemon started %s, 1 Job run since started.\n"), dt);
73 bnet_fsend(user, _("Daemon started %s, %d Jobs run since started.\n"), dt, num_jobs_run);
75 if (debug_level > 0) {
76 char b1[35], b2[35], b3[35], b4[35];
77 bnet_fsend(user, _(" Heap: bytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
78 edit_uint64_with_commas(sm_bytes, b1),
79 edit_uint64_with_commas(sm_max_bytes, b2),
80 edit_uint64_with_commas(sm_buffers, b3),
81 edit_uint64_with_commas(sm_max_buffers, b4));
87 list_running_jobs(user);
90 * List terminated jobs
92 list_terminated_jobs(user);
97 bnet_fsend(user, _("\nDevice status:\n"));
98 foreach_res(changer, R_AUTOCHANGER) {
99 bnet_fsend(user, _("Autochanger \"%s\" with devices:\n"),
101 foreach_alist(device, changer->device) {
103 bnet_fsend(user, " %s\n", device->dev->print_name());
105 bnet_fsend(user, " %s\n", device->hdr.name);
109 foreach_res(device, R_DEVICE) {
111 if (dev && dev->is_open()) {
112 if (dev->is_labeled()) {
113 bnet_fsend(user, _("Device %s is mounted with Volume=\"%s\" Pool=\"%s\"\n"),
114 dev->print_name(), dev->VolHdr.VolumeName,
115 dev->pool_name[0]?dev->pool_name:"*unknown*");
117 bnet_fsend(user, _("Device %s open but no Bacula volume is mounted.\n"),
120 send_blocked_status(jcr, dev);
121 if (dev->can_append()) {
122 bpb = dev->VolCatInfo.VolCatBlocks;
126 bpb = dev->VolCatInfo.VolCatBytes / bpb;
127 bnet_fsend(user, _(" Total Bytes=%s Blocks=%s Bytes/block=%s\n"),
128 edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, b1),
129 edit_uint64_with_commas(dev->VolCatInfo.VolCatBlocks, b2),
130 edit_uint64_with_commas(bpb, b3));
131 } else { /* reading */
132 bpb = dev->VolCatInfo.VolCatReads;
136 if (dev->VolCatInfo.VolCatRBytes > 0) {
137 bpb = dev->VolCatInfo.VolCatRBytes / bpb;
141 bnet_fsend(user, _(" Total Bytes Read=%s Blocks Read=%s Bytes/block=%s\n"),
142 edit_uint64_with_commas(dev->VolCatInfo.VolCatRBytes, b1),
143 edit_uint64_with_commas(dev->VolCatInfo.VolCatReads, b2),
144 edit_uint64_with_commas(bpb, b3));
146 bnet_fsend(user, _(" Positioned at File=%s Block=%s\n"),
147 edit_uint64_with_commas(dev->file, b1),
148 edit_uint64_with_commas(dev->block_num, b2));
152 bnet_fsend(user, _("Device %s is not open or does not exist.\n"), dev->print_name());
154 bnet_fsend(user, _("Device \"%s\" is not open or does not exist.\n"), device->hdr.name);
156 send_blocked_status(jcr, dev);
159 bnet_fsend(user, _("====\n\n"));
160 bnet_fsend(user, _("In Use Volume status:\n"));
162 bnet_fsend(user, _("====\n\n"));
165 if (debug_level > 10) {
166 bnet_fsend(user, _("====\n\n"));
167 dump_resource(R_DEVICE, resources[R_DEVICE-r_first].res_head, sendit, user);
168 bnet_fsend(user, _("====\n\n"));
172 list_spool_stats(user);
174 bnet_sig(user, BNET_EOD);
178 static void send_blocked_status(JCR *jcr, DEVICE *dev)
180 BSOCK *user = jcr->dir_bsock;
184 bnet_fsend(user, _("No DEVICE structure.\n\n"));
187 switch (dev->dev_blocked) {
189 bnet_fsend(user, _(" Device is BLOCKED. User unmounted.\n"));
191 case BST_UNMOUNTED_WAITING_FOR_SYSOP:
192 bnet_fsend(user, _(" Device is BLOCKED. User unmounted during wait for media/mount.\n"));
194 case BST_WAITING_FOR_SYSOP:
195 if (jcr->JobStatus == JS_WaitMount) {
196 bnet_fsend(user, _(" Device is BLOCKED waiting for mount of volume \"%s\".\n"),
199 bnet_fsend(user, _(" Device is BLOCKED waiting for media.\n"));
202 case BST_DOING_ACQUIRE:
203 bnet_fsend(user, _(" Device is being initialized.\n"));
205 case BST_WRITING_LABEL:
206 bnet_fsend(user, _(" Device is blocked labeling a Volume.\n"));
211 /* Send autochanger slot status */
212 if (dev->is_autochanger()) {
214 bnet_fsend(user, _(" Slot %d is loaded in drive %d.\n"),
215 dev->Slot, dev->drive_index);
217 bnet_fsend(user, _(" Drive %d is not loaded.\n"), dev->drive_index);
220 if (debug_level > 1) {
221 bnet_fsend(user, _("Configured device capabilities:\n"));
222 bnet_fsend(user, "%sEOF ", dev->capabilities & CAP_EOF ? "" : "!");
223 bnet_fsend(user, "%sBSR ", dev->capabilities & CAP_BSR ? "" : "!");
224 bnet_fsend(user, "%sBSF ", dev->capabilities & CAP_BSF ? "" : "!");
225 bnet_fsend(user, "%sFSR ", dev->capabilities & CAP_FSR ? "" : "!");
226 bnet_fsend(user, "%sFSF ", dev->capabilities & CAP_FSF ? "" : "!");
227 bnet_fsend(user, "%sEOM ", dev->capabilities & CAP_EOM ? "" : "!");
228 bnet_fsend(user, "%sREM ", dev->capabilities & CAP_REM ? "" : "!");
229 bnet_fsend(user, "%sRACCESS ", dev->capabilities & CAP_RACCESS ? "" : "!");
230 bnet_fsend(user, "%sAUTOMOUNT ", dev->capabilities & CAP_AUTOMOUNT ? "" : "!");
231 bnet_fsend(user, "%sLABEL ", dev->capabilities & CAP_LABEL ? "" : "!");
232 bnet_fsend(user, "%sANONVOLS ", dev->capabilities & CAP_ANONVOLS ? "" : "!");
233 bnet_fsend(user, "%sALWAYSOPEN ", dev->capabilities & CAP_ALWAYSOPEN ? "" : "!");
234 bnet_fsend(user, "\n");
236 bnet_fsend(user, _("Device state:\n"));
237 bnet_fsend(user, "%sOPENED ", dev->is_open() ? "" : "!");
238 bnet_fsend(user, "%sTAPE ", dev->is_tape() ? "" : "!");
239 bnet_fsend(user, "%sLABEL ", dev->is_labeled() ? "" : "!");
240 bnet_fsend(user, "%sMALLOC ", dev->state & ST_MALLOC ? "" : "!");
241 bnet_fsend(user, "%sAPPEND ", dev->can_append() ? "" : "!");
242 bnet_fsend(user, "%sREAD ", dev->can_read() ? "" : "!");
243 bnet_fsend(user, "%sEOT ", dev->at_eot() ? "" : "!");
244 bnet_fsend(user, "%sWEOT ", dev->state & ST_WEOT ? "" : "!");
245 bnet_fsend(user, "%sEOF ", dev->at_eof() ? "" : "!");
246 bnet_fsend(user, "%sNEXTVOL ", dev->state & ST_NEXTVOL ? "" : "!");
247 bnet_fsend(user, "%sSHORT ", dev->state & ST_SHORT ? "" : "!");
248 bnet_fsend(user, "%sMOUNTED ", dev->state & ST_MOUNTED ? "" : "!");
249 bnet_fsend(user, "\n");
250 bnet_fsend(user, _("num_writers=%d JobStatus=%c block=%d\n\n"), dev->num_writers,
251 jcr->JobStatus, dev->dev_blocked);
253 bnet_fsend(user, _("Device parameters:\n"));
254 bnet_fsend(user, _("Archive name: %s Device name: %s\n"), dev->archive_name(),
256 bnet_fsend(user, _("File=%u block=%u\n"), dev->file, dev->block_num);
257 bnet_fsend(user, _("Min block=%u Max block=%u\n"), dev->min_block_size, dev->max_block_size);
262 static void list_running_jobs(BSOCK *user)
268 char JobName[MAX_NAME_LENGTH];
269 char b1[30], b2[30], b3[30];
271 bnet_fsend(user, _("\nRunning Jobs:\n"));
273 if (jcr->JobStatus == JS_WaitFD) {
274 bnet_fsend(user, _("%s Job %s waiting for Client connection.\n"),
275 job_type_to_str(jcr->JobType), jcr->Job);
278 rdcr = jcr->read_dcr;
279 if ((dcr && dcr->device) || rdcr && rdcr->device) {
280 bstrncpy(JobName, jcr->Job, sizeof(JobName));
281 /* There are three periods after the Job name */
283 for (int i=0; i<3; i++) {
284 if ((p=strrchr(JobName, '.')) != NULL) {
288 if (rdcr && rdcr->device) {
289 bnet_fsend(user, _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
290 " pool=\"%s\" device=\"%s\"\n"),
291 job_level_to_str(jcr->JobLevel),
292 job_type_to_str(jcr->JobType),
297 rdcr->dev?rdcr->dev->print_name():
298 rdcr->device->device_name);
300 if (dcr && dcr->device) {
301 bnet_fsend(user, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
302 " pool=\"%s\" device=\"%s\"\n"),
303 job_level_to_str(jcr->JobLevel),
304 job_type_to_str(jcr->JobType),
309 dcr->dev?dcr->dev->print_name():
310 dcr->device->device_name);
312 sec = time(NULL) - jcr->run_time;
316 bps = jcr->JobBytes / sec;
317 bnet_fsend(user, _(" Files=%s Bytes=%s Bytes/sec=%s\n"),
318 edit_uint64_with_commas(jcr->JobFiles, b1),
319 edit_uint64_with_commas(jcr->JobBytes, b2),
320 edit_uint64_with_commas(bps, b3));
323 if (jcr->file_bsock) {
324 bnet_fsend(user, _(" FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
325 edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
326 jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
327 jcr->file_bsock->fd);
329 bnet_fsend(user, _(" FDSocket closed\n"));
337 bnet_fsend(user, _("No Jobs running.\n"));
339 bnet_fsend(user, _("====\n"));
342 static void list_terminated_jobs(void *arg)
344 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
346 struct s_last_job *je;
349 if (last_jobs->size() == 0) {
350 msg = _("No Terminated Jobs.\n");
351 sendit(msg, strlen(msg), arg);
354 lock_last_jobs_list();
355 msg = _("\nTerminated Jobs:\n");
356 sendit(msg, strlen(msg), arg);
357 msg = _(" JobId Level Files Bytes Status Finished Name \n");
358 sendit(msg, strlen(msg), arg);
359 msg = _("======================================================================\n");
360 sendit(msg, strlen(msg), arg);
361 foreach_dlist(je, last_jobs) {
362 char JobName[MAX_NAME_LENGTH];
363 const char *termstat;
366 bstrftime_nc(dt, sizeof(dt), je->end_time);
367 switch (je->JobType) {
370 bstrncpy(level, " ", sizeof(level));
373 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
377 switch (je->JobStatus) {
379 termstat = _("Created");
382 case JS_ErrorTerminated:
383 termstat = _("Error");
386 termstat = _("Diffs");
389 termstat = _("Cancel");
395 termstat = _("Other");
398 bstrncpy(JobName, je->Job, sizeof(JobName));
399 /* There are three periods after the Job name */
401 for (int i=0; i<3; i++) {
402 if ((p=strrchr(JobName, '.')) != NULL) {
406 bsnprintf(buf, sizeof(buf), _("%6d %-6s %8s %14s %-7s %-8s %s\n"),
409 edit_uint64_with_commas(je->JobFiles, b1),
410 edit_uint64_with_commas(je->JobBytes, b2),
413 sendit(buf, strlen(buf), arg);
415 sendit(_("====\n"), 5, arg);
416 unlock_last_jobs_list();
420 * Convert Job Level into a string
422 static const char *level_to_str(int level)
433 str = _("Incremental");
436 str = _("Differential");
441 case L_VERIFY_CATALOG:
442 str = _("Verify Catalog");
445 str = _("Init Catalog");
447 case L_VERIFY_VOLUME_TO_CATALOG:
448 str = _("Volume to Catalog");
450 case L_VERIFY_DISK_TO_CATALOG:
451 str = _("Disk to Catalog");
460 str = _("Unknown Job Level");
469 static void sendit(const char *msg, int len, void *arg)
471 BSOCK *user = (BSOCK *)arg;
473 memcpy(user->msg, msg, len+1);
474 user->msglen = len+1;
479 * .status command from Director
481 bool qstatus_cmd(JCR *jcr)
483 BSOCK *dir = jcr->dir_bsock;
488 if (sscanf(dir->msg, qstatus, time.c_str()) != 1) {
489 pm_strcpy(jcr->errmsg, dir->msg);
490 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
491 bnet_fsend(dir, _("3900 Bad .status command, missing argument.\n"));
492 bnet_sig(dir, BNET_EOD);
497 if (strcmp(time.c_str(), "current") == 0) {
498 bnet_fsend(dir, OKqstatus, time.c_str());
500 if (njcr->JobId != 0) {
501 bnet_fsend(dir, DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
505 } else if (strcmp(time.c_str(), "last") == 0) {
506 bnet_fsend(dir, OKqstatus, time.c_str());
507 if ((last_jobs) && (last_jobs->size() > 0)) {
508 job = (s_last_job*)last_jobs->last();
509 bnet_fsend(dir, DotStatusJob, job->JobId, job->JobStatus, job->Errors);
512 pm_strcpy(jcr->errmsg, dir->msg);
513 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
514 bnet_fsend(dir, _("3900 Bad .status command, wrong argument.\n"));
515 bnet_sig(dir, BNET_EOD);
518 bnet_sig(dir, BNET_EOD);