2 * This file handles the status command
4 * Kern Sibbald, May MMIII
10 Copyright (C) 2003-2005 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: " VERSION " (" BDATE ") %s %s %s\n", my_name,
67 HOST_OS, DISTNAME, DISTVER);
68 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
69 bnet_fsend(user, _("Daemon started %s, %d Job%s run since started.\n"), dt, num_jobs_run,
70 num_jobs_run == 1 ? "" : "s");
71 if (debug_level > 0) {
72 char b1[35], b2[35], b3[35], b4[35];
73 bnet_fsend(user, _(" Heap: bytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
74 edit_uint64_with_commas(sm_bytes, b1),
75 edit_uint64_with_commas(sm_max_bytes, b2),
76 edit_uint64_with_commas(sm_buffers, b3),
77 edit_uint64_with_commas(sm_max_buffers, b4));
83 list_running_jobs(user);
86 * List terminated jobs
88 list_terminated_jobs(user);
93 bnet_fsend(user, _("\nDevice status:\n"));
94 foreach_res(changer, R_AUTOCHANGER) {
95 bnet_fsend(user, _("Autochanger \"%s\" with devices:\n"),
97 foreach_alist(device, changer->device) {
99 bnet_fsend(user, " %s\n", device->dev->print_name());
101 bnet_fsend(user, " %s\n", device->hdr.name);
105 foreach_res(device, R_DEVICE) {
107 if (dev && dev->is_open()) {
108 if (dev->is_labeled()) {
109 bnet_fsend(user, _("Device %s is mounted with Volume \"%s\"\n"),
110 dev->print_name(), dev->VolHdr.VolumeName);
112 bnet_fsend(user, _("Device %s open but no Bacula volume is mounted.\n"),
115 send_blocked_status(jcr, dev);
116 if (dev->can_append()) {
117 bpb = dev->VolCatInfo.VolCatBlocks;
121 bpb = dev->VolCatInfo.VolCatBytes / bpb;
122 bnet_fsend(user, _(" Total Bytes=%s Blocks=%s Bytes/block=%s\n"),
123 edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, b1),
124 edit_uint64_with_commas(dev->VolCatInfo.VolCatBlocks, b2),
125 edit_uint64_with_commas(bpb, b3));
126 } else { /* reading */
127 bpb = dev->VolCatInfo.VolCatReads;
131 if (dev->VolCatInfo.VolCatRBytes > 0) {
132 bpb = dev->VolCatInfo.VolCatRBytes / bpb;
136 bnet_fsend(user, _(" Total Bytes Read=%s Blocks Read=%s Bytes/block=%s\n"),
137 edit_uint64_with_commas(dev->VolCatInfo.VolCatRBytes, b1),
138 edit_uint64_with_commas(dev->VolCatInfo.VolCatReads, b2),
139 edit_uint64_with_commas(bpb, b3));
141 bnet_fsend(user, _(" Positioned at File=%s Block=%s\n"),
142 edit_uint64_with_commas(dev->file, b1),
143 edit_uint64_with_commas(dev->block_num, b2));
147 bnet_fsend(user, _("Device %s is not open or does not exist.\n"), dev->print_name());
149 bnet_fsend(user, _("Device \"%s\" is not open or does not exist.\n"), device->hdr.name);
151 send_blocked_status(jcr, dev);
154 bnet_fsend(user, "====\n\n");
155 bnet_fsend(user, "Volume status:\n");
160 if (debug_level > 0) {
161 bnet_fsend(user, "====\n\n");
162 dump_resource(R_DEVICE, resources[R_DEVICE-r_first].res_head, sendit, user);
164 bnet_fsend(user, "====\n\n");
167 list_spool_stats(user);
169 bnet_sig(user, BNET_EOD);
173 static void send_blocked_status(JCR *jcr, DEVICE *dev)
175 BSOCK *user = jcr->dir_bsock;
179 bnet_fsend(user, "No DEVICE structure.\n\n");
182 switch (dev->dev_blocked) {
184 bnet_fsend(user, _(" Device is BLOCKED. User unmounted.\n"));
186 case BST_UNMOUNTED_WAITING_FOR_SYSOP:
187 bnet_fsend(user, _(" Device is BLOCKED. User unmounted during wait for media/mount.\n"));
189 case BST_WAITING_FOR_SYSOP:
190 if (jcr->JobStatus == JS_WaitMount) {
191 bnet_fsend(user, _(" Device is BLOCKED waiting for mount of volume \"%s\".\n"),
194 bnet_fsend(user, _(" Device is BLOCKED waiting for media.\n"));
197 case BST_DOING_ACQUIRE:
198 bnet_fsend(user, _(" Device is being initialized.\n"));
200 case BST_WRITING_LABEL:
201 bnet_fsend(user, _(" Device is blocked labeling a Volume.\n"));
206 /* Send autochanger slot status */
207 if (dev->is_autochanger()) {
209 bnet_fsend(user, _(" Slot %d is loaded in drive %d.\n"),
210 dev->Slot, dev->drive_index);
212 bnet_fsend(user, _(" Drive %d is not loaded.\n"), dev->drive_index);
215 if (debug_level > 1) {
216 bnet_fsend(user, _("Configured device capabilities:\n"));
217 bnet_fsend(user, "%sEOF ", dev->capabilities & CAP_EOF ? "" : "!");
218 bnet_fsend(user, "%sBSR ", dev->capabilities & CAP_BSR ? "" : "!");
219 bnet_fsend(user, "%sBSF ", dev->capabilities & CAP_BSF ? "" : "!");
220 bnet_fsend(user, "%sFSR ", dev->capabilities & CAP_FSR ? "" : "!");
221 bnet_fsend(user, "%sFSF ", dev->capabilities & CAP_FSF ? "" : "!");
222 bnet_fsend(user, "%sEOM ", dev->capabilities & CAP_EOM ? "" : "!");
223 bnet_fsend(user, "%sREM ", dev->capabilities & CAP_REM ? "" : "!");
224 bnet_fsend(user, "%sRACCESS ", dev->capabilities & CAP_RACCESS ? "" : "!");
225 bnet_fsend(user, "%sAUTOMOUNT ", dev->capabilities & CAP_AUTOMOUNT ? "" : "!");
226 bnet_fsend(user, "%sLABEL ", dev->capabilities & CAP_LABEL ? "" : "!");
227 bnet_fsend(user, "%sANONVOLS ", dev->capabilities & CAP_ANONVOLS ? "" : "!");
228 bnet_fsend(user, "%sALWAYSOPEN ", dev->capabilities & CAP_ALWAYSOPEN ? "" : "!");
229 bnet_fsend(user, "\n");
231 bnet_fsend(user, _("Device state:\n"));
232 bnet_fsend(user, "%sOPENED ", dev->is_open() ? "" : "!");
233 bnet_fsend(user, "%sTAPE ", dev->is_tape() ? "" : "!");
234 bnet_fsend(user, "%sLABEL ", dev->is_labeled() ? "" : "!");
235 bnet_fsend(user, "%sMALLOC ", dev->state & ST_MALLOC ? "" : "!");
236 bnet_fsend(user, "%sAPPEND ", dev->can_append() ? "" : "!");
237 bnet_fsend(user, "%sREAD ", dev->can_read() ? "" : "!");
238 bnet_fsend(user, "%sEOT ", dev->at_eot() ? "" : "!");
239 bnet_fsend(user, "%sWEOT ", dev->state & ST_WEOT ? "" : "!");
240 bnet_fsend(user, "%sEOF ", dev->at_eof() ? "" : "!");
241 bnet_fsend(user, "%sNEXTVOL ", dev->state & ST_NEXTVOL ? "" : "!");
242 bnet_fsend(user, "%sSHORT ", dev->state & ST_SHORT ? "" : "!");
243 bnet_fsend(user, "%sMOUNTED ", dev->state & ST_MOUNTED ? "" : "!");
244 bnet_fsend(user, "\n");
245 bnet_fsend(user, "num_writers=%d JobStatus=%c block=%d\n\n", dev->num_writers,
246 jcr->JobStatus, dev->dev_blocked);
248 bnet_fsend(user, _("Device parameters:\n"));
249 bnet_fsend(user, "Archive name: %s Device name: %s\n", dev->archive_name(),
251 bnet_fsend(user, "File=%u block=%u\n", dev->file, dev->block_num);
252 bnet_fsend(user, "Min block=%u Max block=%u\n", dev->min_block_size, dev->max_block_size);
257 static void list_running_jobs(BSOCK *user)
262 char JobName[MAX_NAME_LENGTH];
263 char b1[30], b2[30], b3[30];
265 bnet_fsend(user, _("\nRunning Jobs:\n"));
267 if (jcr->JobStatus == JS_WaitFD) {
268 bnet_fsend(user, _("%s Job %s waiting for Client connection.\n"),
269 job_type_to_str(jcr->JobType), jcr->Job);
271 if (jcr->dcr && jcr->dcr->device) {
272 bstrncpy(JobName, jcr->Job, sizeof(JobName));
273 /* There are three periods after the Job name */
275 for (int i=0; i<3; i++) {
276 if ((p=strrchr(JobName, '.')) != NULL) {
280 bnet_fsend(user, _("%s %s job %s JobId=%d Volume=\"%s\" device=\"%s\"\n"),
281 job_level_to_str(jcr->JobLevel),
282 job_type_to_str(jcr->JobType),
285 jcr->dcr->VolumeName,
286 jcr->dcr->device->device_name);
287 sec = time(NULL) - jcr->run_time;
291 bps = jcr->JobBytes / sec;
292 bnet_fsend(user, _(" Files=%s Bytes=%s Bytes/sec=%s\n"),
293 edit_uint64_with_commas(jcr->JobFiles, b1),
294 edit_uint64_with_commas(jcr->JobBytes, b2),
295 edit_uint64_with_commas(bps, b3));
298 if (jcr->file_bsock) {
299 bnet_fsend(user, " FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n",
300 edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
301 jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
302 jcr->file_bsock->fd);
304 bnet_fsend(user, " FDSocket closed\n");
311 bnet_fsend(user, _("No Jobs running.\n"));
313 bnet_fsend(user, "====\n");
316 static void list_terminated_jobs(void *arg)
318 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
320 struct s_last_job *je;
323 if (last_jobs->size() == 0) {
324 msg = _("No Terminated Jobs.\n");
325 sendit(msg, strlen(msg), arg);
328 lock_last_jobs_list();
329 msg = _("\nTerminated Jobs:\n");
330 sendit(msg, strlen(msg), arg);
331 msg = _(" JobId Level Files Bytes Status Finished Name \n");
332 sendit(msg, strlen(msg), arg);
333 msg = _("======================================================================\n");
334 sendit(msg, strlen(msg), arg);
335 foreach_dlist(je, last_jobs) {
336 char JobName[MAX_NAME_LENGTH];
337 const char *termstat;
340 bstrftime_nc(dt, sizeof(dt), je->end_time);
341 switch (je->JobType) {
344 bstrncpy(level, " ", sizeof(level));
347 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
351 switch (je->JobStatus) {
353 termstat = "Created";
356 case JS_ErrorTerminated:
372 bstrncpy(JobName, je->Job, sizeof(JobName));
373 /* There are three periods after the Job name */
375 for (int i=0; i<3; i++) {
376 if ((p=strrchr(JobName, '.')) != NULL) {
380 bsnprintf(buf, sizeof(buf), _("%6d %-6s %8s %14s %-7s %-8s %s\n"),
383 edit_uint64_with_commas(je->JobFiles, b1),
384 edit_uint64_with_commas(je->JobBytes, b2),
387 sendit(buf, strlen(buf), arg);
389 sendit("====\n", 5, arg);
390 unlock_last_jobs_list();
394 * Convert Job Level into a string
396 static const char *level_to_str(int level)
407 str = _("Incremental");
410 str = _("Differential");
415 case L_VERIFY_CATALOG:
416 str = _("Verify Catalog");
419 str = _("Init Catalog");
421 case L_VERIFY_VOLUME_TO_CATALOG:
422 str = _("Volume to Catalog");
424 case L_VERIFY_DISK_TO_CATALOG:
425 str = _("Disk to Catalog");
434 str = _("Unknown Job Level");
443 static void sendit(const char *msg, int len, void *arg)
445 BSOCK *user = (BSOCK *)arg;
447 memcpy(user->msg, msg, len+1);
448 user->msglen = len+1;
453 * .status command from Director
455 bool qstatus_cmd(JCR *jcr)
457 BSOCK *dir = jcr->dir_bsock;
462 if (sscanf(dir->msg, qstatus, time.c_str()) != 1) {
463 pm_strcpy(jcr->errmsg, dir->msg);
464 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
465 bnet_fsend(dir, "3900 Bad .status command, missing argument.\n");
466 bnet_sig(dir, BNET_EOD);
471 if (strcmp(time.c_str(), "current") == 0) {
472 bnet_fsend(dir, OKqstatus, time.c_str());
474 if (njcr->JobId != 0) {
475 bnet_fsend(dir, DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
480 else if (strcmp(time.c_str(), "last") == 0) {
481 bnet_fsend(dir, OKqstatus, time.c_str());
482 if ((last_jobs) && (last_jobs->size() > 0)) {
483 job = (s_last_job*)last_jobs->last();
484 bnet_fsend(dir, DotStatusJob, job->JobId, job->JobStatus, job->Errors);
488 pm_strcpy(jcr->errmsg, dir->msg);
489 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
490 bnet_fsend(dir, "3900 Bad .status command, wrong argument.\n");
491 bnet_sig(dir, BNET_EOD);
494 bnet_sig(dir, BNET_EOD);