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[35], b2[35], b3[35], b4[35];
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 bnet_fsend(user, _(" Heap: bytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
77 edit_uint64_with_commas(sm_bytes, b1),
78 edit_uint64_with_commas(sm_max_bytes, b2),
79 edit_uint64_with_commas(sm_buffers, b3),
80 edit_uint64_with_commas(sm_max_buffers, b4));
85 list_running_jobs(user);
88 * List jobs stuck in reservation system
90 list_jobs_waiting_on_reservation(user);
93 * List terminated jobs
95 list_terminated_jobs(user);
100 bnet_fsend(user, _("\nDevice status:\n"));
101 foreach_res(changer, R_AUTOCHANGER) {
102 bnet_fsend(user, _("Autochanger \"%s\" with devices:\n"),
104 foreach_alist(device, changer->device) {
106 bnet_fsend(user, " %s\n", device->dev->print_name());
108 bnet_fsend(user, " %s\n", device->hdr.name);
112 foreach_res(device, R_DEVICE) {
114 if (dev && dev->is_open()) {
115 if (dev->is_labeled()) {
116 bnet_fsend(user, _("Device %s is mounted with Volume=\"%s\" Pool=\"%s\"\n"),
117 dev->print_name(), dev->VolHdr.VolumeName,
118 dev->pool_name[0]?dev->pool_name:"*unknown*");
120 bnet_fsend(user, _("Device %s open but no Bacula volume is mounted.\n"),
123 send_blocked_status(jcr, dev);
124 if (dev->can_append()) {
125 bpb = dev->VolCatInfo.VolCatBlocks;
129 bpb = dev->VolCatInfo.VolCatBytes / bpb;
130 bnet_fsend(user, _(" Total Bytes=%s Blocks=%s Bytes/block=%s\n"),
131 edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, b1),
132 edit_uint64_with_commas(dev->VolCatInfo.VolCatBlocks, b2),
133 edit_uint64_with_commas(bpb, b3));
134 } else { /* reading */
135 bpb = dev->VolCatInfo.VolCatReads;
139 if (dev->VolCatInfo.VolCatRBytes > 0) {
140 bpb = dev->VolCatInfo.VolCatRBytes / bpb;
144 bnet_fsend(user, _(" Total Bytes Read=%s Blocks Read=%s Bytes/block=%s\n"),
145 edit_uint64_with_commas(dev->VolCatInfo.VolCatRBytes, b1),
146 edit_uint64_with_commas(dev->VolCatInfo.VolCatReads, b2),
147 edit_uint64_with_commas(bpb, b3));
149 bnet_fsend(user, _(" Positioned at File=%s Block=%s\n"),
150 edit_uint64_with_commas(dev->file, b1),
151 edit_uint64_with_commas(dev->block_num, b2));
155 bnet_fsend(user, _("Device %s is not open.\n"), dev->print_name());
157 bnet_fsend(user, _("Device \"%s\" is not open or does not exist.\n"), device->hdr.name);
159 send_blocked_status(jcr, dev);
162 bnet_fsend(user, _("====\n\n"));
163 bnet_fsend(user, _("In Use Volume status:\n"));
165 bnet_fsend(user, _("====\n\n"));
168 if (debug_level > 10) {
169 bnet_fsend(user, _("====\n\n"));
170 dump_resource(R_DEVICE, resources[R_DEVICE-r_first].res_head, sendit, user);
171 bnet_fsend(user, _("====\n\n"));
175 list_spool_stats(user);
177 bnet_sig(user, BNET_EOD);
181 static void send_blocked_status(JCR *jcr, DEVICE *dev)
183 BSOCK *user = jcr->dir_bsock;
187 bnet_fsend(user, _("No DEVICE structure.\n\n"));
190 switch (dev->dev_blocked) {
192 bnet_fsend(user, _(" Device is BLOCKED. User unmounted.\n"));
194 case BST_UNMOUNTED_WAITING_FOR_SYSOP:
195 bnet_fsend(user, _(" Device is BLOCKED. User unmounted during wait for media/mount.\n"));
197 case BST_WAITING_FOR_SYSOP:
198 if (jcr->JobStatus == JS_WaitMount) {
199 bnet_fsend(user, _(" Device is BLOCKED waiting for mount of volume \"%s\".\n"),
202 bnet_fsend(user, _(" Device is BLOCKED waiting for media.\n"));
205 case BST_DOING_ACQUIRE:
206 bnet_fsend(user, _(" Device is being initialized.\n"));
208 case BST_WRITING_LABEL:
209 bnet_fsend(user, _(" Device is blocked labeling a Volume.\n"));
214 /* Send autochanger slot status */
215 if (dev->is_autochanger()) {
217 bnet_fsend(user, _(" Slot %d is loaded in drive %d.\n"),
218 dev->Slot, dev->drive_index);
220 bnet_fsend(user, _(" Drive %d is not loaded.\n"), dev->drive_index);
223 if (debug_level > 1) {
224 bnet_fsend(user, _("Configured device capabilities:\n"));
225 bnet_fsend(user, "%sEOF ", dev->capabilities & CAP_EOF ? "" : "!");
226 bnet_fsend(user, "%sBSR ", dev->capabilities & CAP_BSR ? "" : "!");
227 bnet_fsend(user, "%sBSF ", dev->capabilities & CAP_BSF ? "" : "!");
228 bnet_fsend(user, "%sFSR ", dev->capabilities & CAP_FSR ? "" : "!");
229 bnet_fsend(user, "%sFSF ", dev->capabilities & CAP_FSF ? "" : "!");
230 bnet_fsend(user, "%sEOM ", dev->capabilities & CAP_EOM ? "" : "!");
231 bnet_fsend(user, "%sREM ", dev->capabilities & CAP_REM ? "" : "!");
232 bnet_fsend(user, "%sRACCESS ", dev->capabilities & CAP_RACCESS ? "" : "!");
233 bnet_fsend(user, "%sAUTOMOUNT ", dev->capabilities & CAP_AUTOMOUNT ? "" : "!");
234 bnet_fsend(user, "%sLABEL ", dev->capabilities & CAP_LABEL ? "" : "!");
235 bnet_fsend(user, "%sANONVOLS ", dev->capabilities & CAP_ANONVOLS ? "" : "!");
236 bnet_fsend(user, "%sALWAYSOPEN ", dev->capabilities & CAP_ALWAYSOPEN ? "" : "!");
237 bnet_fsend(user, "\n");
239 bnet_fsend(user, _("Device state:\n"));
240 bnet_fsend(user, "%sOPENED ", dev->is_open() ? "" : "!");
241 bnet_fsend(user, "%sTAPE ", dev->is_tape() ? "" : "!");
242 bnet_fsend(user, "%sLABEL ", dev->is_labeled() ? "" : "!");
243 bnet_fsend(user, "%sMALLOC ", dev->state & ST_MALLOC ? "" : "!");
244 bnet_fsend(user, "%sAPPEND ", dev->can_append() ? "" : "!");
245 bnet_fsend(user, "%sREAD ", dev->can_read() ? "" : "!");
246 bnet_fsend(user, "%sEOT ", dev->at_eot() ? "" : "!");
247 bnet_fsend(user, "%sWEOT ", dev->state & ST_WEOT ? "" : "!");
248 bnet_fsend(user, "%sEOF ", dev->at_eof() ? "" : "!");
249 bnet_fsend(user, "%sNEXTVOL ", dev->state & ST_NEXTVOL ? "" : "!");
250 bnet_fsend(user, "%sSHORT ", dev->state & ST_SHORT ? "" : "!");
251 bnet_fsend(user, "%sMOUNTED ", dev->state & ST_MOUNTED ? "" : "!");
252 bnet_fsend(user, "\n");
253 bnet_fsend(user, _("num_writers=%d JobStatus=%c block=%d\n\n"), dev->num_writers,
254 jcr->JobStatus, dev->dev_blocked);
256 bnet_fsend(user, _("Device parameters:\n"));
257 bnet_fsend(user, _("Archive name: %s Device name: %s\n"), dev->archive_name(),
259 bnet_fsend(user, _("File=%u block=%u\n"), dev->file, dev->block_num);
260 bnet_fsend(user, _("Min block=%u Max block=%u\n"), dev->min_block_size, dev->max_block_size);
265 static void list_running_jobs(BSOCK *user)
271 char JobName[MAX_NAME_LENGTH];
272 char b1[30], b2[30], b3[30];
274 bnet_fsend(user, _("\nRunning Jobs:\n"));
276 if (jcr->JobStatus == JS_WaitFD) {
277 bnet_fsend(user, _("%s Job %s waiting for Client connection.\n"),
278 job_type_to_str(jcr->JobType), jcr->Job);
281 rdcr = jcr->read_dcr;
282 if ((dcr && dcr->device) || rdcr && rdcr->device) {
283 bstrncpy(JobName, jcr->Job, sizeof(JobName));
284 /* There are three periods after the Job name */
286 for (int i=0; i<3; i++) {
287 if ((p=strrchr(JobName, '.')) != NULL) {
291 if (rdcr && rdcr->device) {
292 bnet_fsend(user, _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
293 " pool=\"%s\" device=\"%s\"\n"),
294 job_level_to_str(jcr->JobLevel),
295 job_type_to_str(jcr->JobType),
300 rdcr->dev?rdcr->dev->print_name():
301 rdcr->device->device_name);
303 if (dcr && dcr->device) {
304 bnet_fsend(user, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
305 " pool=\"%s\" device=\"%s\"\n"),
306 job_level_to_str(jcr->JobLevel),
307 job_type_to_str(jcr->JobType),
312 dcr->dev?dcr->dev->print_name():
313 dcr->device->device_name);
315 sec = time(NULL) - jcr->run_time;
319 bps = jcr->JobBytes / sec;
320 bnet_fsend(user, _(" Files=%s Bytes=%s Bytes/sec=%s\n"),
321 edit_uint64_with_commas(jcr->JobFiles, b1),
322 edit_uint64_with_commas(jcr->JobBytes, b2),
323 edit_uint64_with_commas(bps, b3));
326 if (jcr->file_bsock) {
327 bnet_fsend(user, _(" FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
328 edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
329 jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
330 jcr->file_bsock->fd);
332 bnet_fsend(user, _(" FDSocket closed\n"));
340 bnet_fsend(user, _("No Jobs running.\n"));
342 bnet_fsend(user, _("====\n"));
345 static void list_jobs_waiting_on_reservation(BSOCK *user)
349 bnet_fsend(user, _("\nJobs waiting to reserve a drive:\n"));
351 if (!jcr->reserve_msgs) {
354 send_drive_reserve_messages(jcr, user);
358 bnet_fsend(user, _("====\n"));
362 static void list_terminated_jobs(void *arg)
364 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
366 struct s_last_job *je;
369 if (last_jobs->size() == 0) {
370 msg = _("No Terminated Jobs.\n");
371 sendit(msg, strlen(msg), arg);
374 lock_last_jobs_list();
375 msg = _("\nTerminated Jobs:\n");
376 sendit(msg, strlen(msg), arg);
377 msg = _(" JobId Level Files Bytes Status Finished Name \n");
378 sendit(msg, strlen(msg), arg);
379 msg = _("======================================================================\n");
380 sendit(msg, strlen(msg), arg);
381 foreach_dlist(je, last_jobs) {
382 char JobName[MAX_NAME_LENGTH];
383 const char *termstat;
386 bstrftime_nc(dt, sizeof(dt), je->end_time);
387 switch (je->JobType) {
390 bstrncpy(level, " ", sizeof(level));
393 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
397 switch (je->JobStatus) {
399 termstat = _("Created");
402 case JS_ErrorTerminated:
403 termstat = _("Error");
406 termstat = _("Diffs");
409 termstat = _("Cancel");
415 termstat = _("Other");
418 bstrncpy(JobName, je->Job, sizeof(JobName));
419 /* There are three periods after the Job name */
421 for (int i=0; i<3; i++) {
422 if ((p=strrchr(JobName, '.')) != NULL) {
426 bsnprintf(buf, sizeof(buf), _("%6d %-6s %8s %14s %-7s %-8s %s\n"),
429 edit_uint64_with_commas(je->JobFiles, b1),
430 edit_uint64_with_commas(je->JobBytes, b2),
433 sendit(buf, strlen(buf), arg);
435 sendit(_("====\n"), 5, arg);
436 unlock_last_jobs_list();
440 * Convert Job Level into a string
442 static const char *level_to_str(int level)
453 str = _("Incremental");
456 str = _("Differential");
461 case L_VERIFY_CATALOG:
462 str = _("Verify Catalog");
465 str = _("Init Catalog");
467 case L_VERIFY_VOLUME_TO_CATALOG:
468 str = _("Volume to Catalog");
470 case L_VERIFY_DISK_TO_CATALOG:
471 str = _("Disk to Catalog");
480 str = _("Unknown Job Level");
489 static void sendit(const char *msg, int len, void *arg)
491 BSOCK *user = (BSOCK *)arg;
493 memcpy(user->msg, msg, len+1);
494 user->msglen = len+1;
499 * .status command from Director
501 bool qstatus_cmd(JCR *jcr)
503 BSOCK *dir = jcr->dir_bsock;
508 if (sscanf(dir->msg, qstatus, time.c_str()) != 1) {
509 pm_strcpy(jcr->errmsg, dir->msg);
510 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
511 bnet_fsend(dir, _("3900 Bad .status command, missing argument.\n"));
512 bnet_sig(dir, BNET_EOD);
517 if (strcmp(time.c_str(), "current") == 0) {
518 bnet_fsend(dir, OKqstatus, time.c_str());
520 if (njcr->JobId != 0) {
521 bnet_fsend(dir, DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
525 } else if (strcmp(time.c_str(), "last") == 0) {
526 bnet_fsend(dir, OKqstatus, time.c_str());
527 if ((last_jobs) && (last_jobs->size() > 0)) {
528 job = (s_last_job*)last_jobs->last();
529 bnet_fsend(dir, DotStatusJob, job->JobId, job->JobStatus, job->Errors);
532 pm_strcpy(jcr->errmsg, dir->msg);
533 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
534 bnet_fsend(dir, _("3900 Bad .status command, wrong argument.\n"));
535 bnet_sig(dir, BNET_EOD);
538 bnet_sig(dir, BNET_EOD);