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 list_jobs_waiting_on_reservation(BSOCK *user);
50 static void sendit(const char *msg, int len, void *arg);
51 static const char *level_to_str(int level);
55 * Status command from Director
57 bool status_cmd(JCR *jcr)
62 BSOCK *user = jcr->dir_bsock;
63 char dt[MAX_TIME_LENGTH];
64 char b1[30], b2[30], b3[30];
67 bnet_fsend(user, _("\n%s Version: %s (%s) %s %s %s\n"), my_name,
68 VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
69 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
70 if (num_jobs_run == 1) {
71 bnet_fsend(user, _("Daemon started %s, 1 Job run since started.\n"), dt);
74 bnet_fsend(user, _("Daemon started %s, %d Jobs run since started.\n"), dt, num_jobs_run);
76 if (debug_level > 0) {
77 char b1[35], b2[35], b3[35], b4[35];
78 bnet_fsend(user, _(" Heap: bytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
79 edit_uint64_with_commas(sm_bytes, b1),
80 edit_uint64_with_commas(sm_max_bytes, b2),
81 edit_uint64_with_commas(sm_buffers, b3),
82 edit_uint64_with_commas(sm_max_buffers, b4));
88 list_running_jobs(user);
91 * List jobs stuck in reservation system
93 list_jobs_waiting_on_reservation(user);
96 * List terminated jobs
98 list_terminated_jobs(user);
103 bnet_fsend(user, _("\nDevice status:\n"));
104 foreach_res(changer, R_AUTOCHANGER) {
105 bnet_fsend(user, _("Autochanger \"%s\" with devices:\n"),
107 foreach_alist(device, changer->device) {
109 bnet_fsend(user, " %s\n", device->dev->print_name());
111 bnet_fsend(user, " %s\n", device->hdr.name);
115 foreach_res(device, R_DEVICE) {
117 if (dev && dev->is_open()) {
118 if (dev->is_labeled()) {
119 bnet_fsend(user, _("Device %s is mounted with Volume=\"%s\" Pool=\"%s\"\n"),
120 dev->print_name(), dev->VolHdr.VolumeName,
121 dev->pool_name[0]?dev->pool_name:"*unknown*");
123 bnet_fsend(user, _("Device %s open but no Bacula volume is mounted.\n"),
126 send_blocked_status(jcr, dev);
127 if (dev->can_append()) {
128 bpb = dev->VolCatInfo.VolCatBlocks;
132 bpb = dev->VolCatInfo.VolCatBytes / bpb;
133 bnet_fsend(user, _(" Total Bytes=%s Blocks=%s Bytes/block=%s\n"),
134 edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, b1),
135 edit_uint64_with_commas(dev->VolCatInfo.VolCatBlocks, b2),
136 edit_uint64_with_commas(bpb, b3));
137 } else { /* reading */
138 bpb = dev->VolCatInfo.VolCatReads;
142 if (dev->VolCatInfo.VolCatRBytes > 0) {
143 bpb = dev->VolCatInfo.VolCatRBytes / bpb;
147 bnet_fsend(user, _(" Total Bytes Read=%s Blocks Read=%s Bytes/block=%s\n"),
148 edit_uint64_with_commas(dev->VolCatInfo.VolCatRBytes, b1),
149 edit_uint64_with_commas(dev->VolCatInfo.VolCatReads, b2),
150 edit_uint64_with_commas(bpb, b3));
152 bnet_fsend(user, _(" Positioned at File=%s Block=%s\n"),
153 edit_uint64_with_commas(dev->file, b1),
154 edit_uint64_with_commas(dev->block_num, b2));
158 bnet_fsend(user, _("Device %s is not open or does not exist.\n"), dev->print_name());
160 bnet_fsend(user, _("Device \"%s\" is not open or does not exist.\n"), device->hdr.name);
162 send_blocked_status(jcr, dev);
165 bnet_fsend(user, _("====\n\n"));
166 bnet_fsend(user, _("In Use Volume status:\n"));
168 bnet_fsend(user, _("====\n\n"));
171 if (debug_level > 10) {
172 bnet_fsend(user, _("====\n\n"));
173 dump_resource(R_DEVICE, resources[R_DEVICE-r_first].res_head, sendit, user);
174 bnet_fsend(user, _("====\n\n"));
178 list_spool_stats(user);
180 bnet_sig(user, BNET_EOD);
184 static void send_blocked_status(JCR *jcr, DEVICE *dev)
186 BSOCK *user = jcr->dir_bsock;
190 bnet_fsend(user, _("No DEVICE structure.\n\n"));
193 switch (dev->dev_blocked) {
195 bnet_fsend(user, _(" Device is BLOCKED. User unmounted.\n"));
197 case BST_UNMOUNTED_WAITING_FOR_SYSOP:
198 bnet_fsend(user, _(" Device is BLOCKED. User unmounted during wait for media/mount.\n"));
200 case BST_WAITING_FOR_SYSOP:
201 if (jcr->JobStatus == JS_WaitMount) {
202 bnet_fsend(user, _(" Device is BLOCKED waiting for mount of volume \"%s\".\n"),
205 bnet_fsend(user, _(" Device is BLOCKED waiting for media.\n"));
208 case BST_DOING_ACQUIRE:
209 bnet_fsend(user, _(" Device is being initialized.\n"));
211 case BST_WRITING_LABEL:
212 bnet_fsend(user, _(" Device is blocked labeling a Volume.\n"));
217 /* Send autochanger slot status */
218 if (dev->is_autochanger()) {
220 bnet_fsend(user, _(" Slot %d is loaded in drive %d.\n"),
221 dev->Slot, dev->drive_index);
223 bnet_fsend(user, _(" Drive %d is not loaded.\n"), dev->drive_index);
226 if (debug_level > 1) {
227 bnet_fsend(user, _("Configured device capabilities:\n"));
228 bnet_fsend(user, "%sEOF ", dev->capabilities & CAP_EOF ? "" : "!");
229 bnet_fsend(user, "%sBSR ", dev->capabilities & CAP_BSR ? "" : "!");
230 bnet_fsend(user, "%sBSF ", dev->capabilities & CAP_BSF ? "" : "!");
231 bnet_fsend(user, "%sFSR ", dev->capabilities & CAP_FSR ? "" : "!");
232 bnet_fsend(user, "%sFSF ", dev->capabilities & CAP_FSF ? "" : "!");
233 bnet_fsend(user, "%sEOM ", dev->capabilities & CAP_EOM ? "" : "!");
234 bnet_fsend(user, "%sREM ", dev->capabilities & CAP_REM ? "" : "!");
235 bnet_fsend(user, "%sRACCESS ", dev->capabilities & CAP_RACCESS ? "" : "!");
236 bnet_fsend(user, "%sAUTOMOUNT ", dev->capabilities & CAP_AUTOMOUNT ? "" : "!");
237 bnet_fsend(user, "%sLABEL ", dev->capabilities & CAP_LABEL ? "" : "!");
238 bnet_fsend(user, "%sANONVOLS ", dev->capabilities & CAP_ANONVOLS ? "" : "!");
239 bnet_fsend(user, "%sALWAYSOPEN ", dev->capabilities & CAP_ALWAYSOPEN ? "" : "!");
240 bnet_fsend(user, "\n");
242 bnet_fsend(user, _("Device state:\n"));
243 bnet_fsend(user, "%sOPENED ", dev->is_open() ? "" : "!");
244 bnet_fsend(user, "%sTAPE ", dev->is_tape() ? "" : "!");
245 bnet_fsend(user, "%sLABEL ", dev->is_labeled() ? "" : "!");
246 bnet_fsend(user, "%sMALLOC ", dev->state & ST_MALLOC ? "" : "!");
247 bnet_fsend(user, "%sAPPEND ", dev->can_append() ? "" : "!");
248 bnet_fsend(user, "%sREAD ", dev->can_read() ? "" : "!");
249 bnet_fsend(user, "%sEOT ", dev->at_eot() ? "" : "!");
250 bnet_fsend(user, "%sWEOT ", dev->state & ST_WEOT ? "" : "!");
251 bnet_fsend(user, "%sEOF ", dev->at_eof() ? "" : "!");
252 bnet_fsend(user, "%sNEXTVOL ", dev->state & ST_NEXTVOL ? "" : "!");
253 bnet_fsend(user, "%sSHORT ", dev->state & ST_SHORT ? "" : "!");
254 bnet_fsend(user, "%sMOUNTED ", dev->state & ST_MOUNTED ? "" : "!");
255 bnet_fsend(user, "\n");
256 bnet_fsend(user, _("num_writers=%d JobStatus=%c block=%d\n\n"), dev->num_writers,
257 jcr->JobStatus, dev->dev_blocked);
259 bnet_fsend(user, _("Device parameters:\n"));
260 bnet_fsend(user, _("Archive name: %s Device name: %s\n"), dev->archive_name(),
262 bnet_fsend(user, _("File=%u block=%u\n"), dev->file, dev->block_num);
263 bnet_fsend(user, _("Min block=%u Max block=%u\n"), dev->min_block_size, dev->max_block_size);
268 static void list_running_jobs(BSOCK *user)
274 char JobName[MAX_NAME_LENGTH];
275 char b1[30], b2[30], b3[30];
277 bnet_fsend(user, _("\nRunning Jobs:\n"));
279 if (jcr->JobStatus == JS_WaitFD) {
280 bnet_fsend(user, _("%s Job %s waiting for Client connection.\n"),
281 job_type_to_str(jcr->JobType), jcr->Job);
284 rdcr = jcr->read_dcr;
285 if ((dcr && dcr->device) || rdcr && rdcr->device) {
286 bstrncpy(JobName, jcr->Job, sizeof(JobName));
287 /* There are three periods after the Job name */
289 for (int i=0; i<3; i++) {
290 if ((p=strrchr(JobName, '.')) != NULL) {
294 if (rdcr && rdcr->device) {
295 bnet_fsend(user, _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
296 " pool=\"%s\" device=\"%s\"\n"),
297 job_level_to_str(jcr->JobLevel),
298 job_type_to_str(jcr->JobType),
303 rdcr->dev?rdcr->dev->print_name():
304 rdcr->device->device_name);
306 if (dcr && dcr->device) {
307 bnet_fsend(user, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
308 " pool=\"%s\" device=\"%s\"\n"),
309 job_level_to_str(jcr->JobLevel),
310 job_type_to_str(jcr->JobType),
315 dcr->dev?dcr->dev->print_name():
316 dcr->device->device_name);
318 sec = time(NULL) - jcr->run_time;
322 bps = jcr->JobBytes / sec;
323 bnet_fsend(user, _(" Files=%s Bytes=%s Bytes/sec=%s\n"),
324 edit_uint64_with_commas(jcr->JobFiles, b1),
325 edit_uint64_with_commas(jcr->JobBytes, b2),
326 edit_uint64_with_commas(bps, b3));
329 if (jcr->file_bsock) {
330 bnet_fsend(user, _(" FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
331 edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
332 jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
333 jcr->file_bsock->fd);
335 bnet_fsend(user, _(" FDSocket closed\n"));
343 bnet_fsend(user, _("No Jobs running.\n"));
345 bnet_fsend(user, _("====\n"));
348 static void list_jobs_waiting_on_reservation(BSOCK *user)
352 bnet_fsend(user, _("\nJobs waiting to reserve a drive:\n"));
354 if (!jcr->reserve_msgs) {
357 send_drive_reserve_messages(jcr, user);
361 bnet_fsend(user, _("====\n"));
365 static void list_terminated_jobs(void *arg)
367 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
369 struct s_last_job *je;
372 if (last_jobs->size() == 0) {
373 msg = _("No Terminated Jobs.\n");
374 sendit(msg, strlen(msg), arg);
377 lock_last_jobs_list();
378 msg = _("\nTerminated Jobs:\n");
379 sendit(msg, strlen(msg), arg);
380 msg = _(" JobId Level Files Bytes Status Finished Name \n");
381 sendit(msg, strlen(msg), arg);
382 msg = _("======================================================================\n");
383 sendit(msg, strlen(msg), arg);
384 foreach_dlist(je, last_jobs) {
385 char JobName[MAX_NAME_LENGTH];
386 const char *termstat;
389 bstrftime_nc(dt, sizeof(dt), je->end_time);
390 switch (je->JobType) {
393 bstrncpy(level, " ", sizeof(level));
396 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
400 switch (je->JobStatus) {
402 termstat = _("Created");
405 case JS_ErrorTerminated:
406 termstat = _("Error");
409 termstat = _("Diffs");
412 termstat = _("Cancel");
418 termstat = _("Other");
421 bstrncpy(JobName, je->Job, sizeof(JobName));
422 /* There are three periods after the Job name */
424 for (int i=0; i<3; i++) {
425 if ((p=strrchr(JobName, '.')) != NULL) {
429 bsnprintf(buf, sizeof(buf), _("%6d %-6s %8s %14s %-7s %-8s %s\n"),
432 edit_uint64_with_commas(je->JobFiles, b1),
433 edit_uint64_with_commas(je->JobBytes, b2),
436 sendit(buf, strlen(buf), arg);
438 sendit(_("====\n"), 5, arg);
439 unlock_last_jobs_list();
443 * Convert Job Level into a string
445 static const char *level_to_str(int level)
456 str = _("Incremental");
459 str = _("Differential");
464 case L_VERIFY_CATALOG:
465 str = _("Verify Catalog");
468 str = _("Init Catalog");
470 case L_VERIFY_VOLUME_TO_CATALOG:
471 str = _("Volume to Catalog");
473 case L_VERIFY_DISK_TO_CATALOG:
474 str = _("Disk to Catalog");
483 str = _("Unknown Job Level");
492 static void sendit(const char *msg, int len, void *arg)
494 BSOCK *user = (BSOCK *)arg;
496 memcpy(user->msg, msg, len+1);
497 user->msglen = len+1;
502 * .status command from Director
504 bool qstatus_cmd(JCR *jcr)
506 BSOCK *dir = jcr->dir_bsock;
511 if (sscanf(dir->msg, qstatus, time.c_str()) != 1) {
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, missing argument.\n"));
515 bnet_sig(dir, BNET_EOD);
520 if (strcmp(time.c_str(), "current") == 0) {
521 bnet_fsend(dir, OKqstatus, time.c_str());
523 if (njcr->JobId != 0) {
524 bnet_fsend(dir, DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
528 } else if (strcmp(time.c_str(), "last") == 0) {
529 bnet_fsend(dir, OKqstatus, time.c_str());
530 if ((last_jobs) && (last_jobs->size() > 0)) {
531 job = (s_last_job*)last_jobs->last();
532 bnet_fsend(dir, DotStatusJob, job->JobId, job->JobStatus, job->Errors);
535 pm_strcpy(jcr->errmsg, dir->msg);
536 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
537 bnet_fsend(dir, _("3900 Bad .status command, wrong argument.\n"));
538 bnet_sig(dir, BNET_EOD);
541 bnet_sig(dir, BNET_EOD);