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[];
34 /* Static variables */
35 static char qstatus[] = ".status %127s\n";
37 static char OKqstatus[] = "3000 OK .status\n";
38 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
41 /* Forward referenced functions */
42 static void send_blocked_status(JCR *jcr, DEVICE *dev);
43 static void list_terminated_jobs(void *arg);
44 static void list_running_jobs(BSOCK *user);
45 static void list_jobs_waiting_on_reservation(BSOCK *user);
46 static void sendit(const char *msg, int len, void *arg);
47 static const char *level_to_str(int level);
51 * Status command from Director
53 bool status_cmd(JCR *jcr)
58 BSOCK *user = jcr->dir_bsock;
59 char dt[MAX_TIME_LENGTH];
60 char b1[35], b2[35], b3[35], b4[35];
63 bnet_fsend(user, _("\n%s Version: %s (%s) %s %s %s\n"), my_name,
64 VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
65 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
66 if (num_jobs_run == 1) {
67 bnet_fsend(user, _("Daemon started %s, 1 Job run since started.\n"), dt);
70 bnet_fsend(user, _("Daemon started %s, %d Jobs run since started.\n"), dt, num_jobs_run);
72 bnet_fsend(user, _(" Heap: bytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
73 edit_uint64_with_commas(sm_bytes, b1),
74 edit_uint64_with_commas(sm_max_bytes, b2),
75 edit_uint64_with_commas(sm_buffers, b3),
76 edit_uint64_with_commas(sm_max_buffers, b4));
81 list_running_jobs(user);
84 * List jobs stuck in reservation system
86 list_jobs_waiting_on_reservation(user);
89 * List terminated jobs
91 list_terminated_jobs(user);
96 bnet_fsend(user, _("\nDevice status:\n"));
97 foreach_res(changer, R_AUTOCHANGER) {
98 bnet_fsend(user, _("Autochanger \"%s\" with devices:\n"),
100 foreach_alist(device, changer->device) {
102 bnet_fsend(user, " %s\n", device->dev->print_name());
104 bnet_fsend(user, " %s\n", device->hdr.name);
108 foreach_res(device, R_DEVICE) {
110 if (dev && dev->is_open()) {
111 if (dev->is_labeled()) {
112 bnet_fsend(user, _("Device %s is mounted with Volume=\"%s\" Pool=\"%s\"\n"),
113 dev->print_name(), dev->VolHdr.VolumeName,
114 dev->pool_name[0]?dev->pool_name:"*unknown*");
116 bnet_fsend(user, _("Device %s open but no Bacula volume is mounted.\n"),
119 send_blocked_status(jcr, dev);
120 if (dev->can_append()) {
121 bpb = dev->VolCatInfo.VolCatBlocks;
125 bpb = dev->VolCatInfo.VolCatBytes / bpb;
126 bnet_fsend(user, _(" Total Bytes=%s Blocks=%s Bytes/block=%s\n"),
127 edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, b1),
128 edit_uint64_with_commas(dev->VolCatInfo.VolCatBlocks, b2),
129 edit_uint64_with_commas(bpb, b3));
130 } else { /* reading */
131 bpb = dev->VolCatInfo.VolCatReads;
135 if (dev->VolCatInfo.VolCatRBytes > 0) {
136 bpb = dev->VolCatInfo.VolCatRBytes / bpb;
140 bnet_fsend(user, _(" Total Bytes Read=%s Blocks Read=%s Bytes/block=%s\n"),
141 edit_uint64_with_commas(dev->VolCatInfo.VolCatRBytes, b1),
142 edit_uint64_with_commas(dev->VolCatInfo.VolCatReads, b2),
143 edit_uint64_with_commas(bpb, b3));
145 bnet_fsend(user, _(" Positioned at File=%s Block=%s\n"),
146 edit_uint64_with_commas(dev->file, b1),
147 edit_uint64_with_commas(dev->block_num, b2));
151 bnet_fsend(user, _("Device %s is not open.\n"), dev->print_name());
153 bnet_fsend(user, _("Device \"%s\" is not open or does not exist.\n"), device->hdr.name);
155 send_blocked_status(jcr, dev);
158 bnet_fsend(user, _("====\n\n"));
159 bnet_fsend(user, _("In Use Volume status:\n"));
161 bnet_fsend(user, _("====\n\n"));
164 if (debug_level > 10) {
165 bnet_fsend(user, _("====\n\n"));
166 dump_resource(R_DEVICE, resources[R_DEVICE-r_first].res_head, sendit, user);
167 bnet_fsend(user, _("====\n\n"));
171 list_spool_stats(user);
173 bnet_sig(user, BNET_EOD);
177 static void send_blocked_status(JCR *jcr, DEVICE *dev)
179 BSOCK *user = jcr->dir_bsock;
183 bnet_fsend(user, _("No DEVICE structure.\n\n"));
186 switch (dev->dev_blocked) {
188 bnet_fsend(user, _(" Device is BLOCKED. User unmounted.\n"));
190 case BST_UNMOUNTED_WAITING_FOR_SYSOP:
191 bnet_fsend(user, _(" Device is BLOCKED. User unmounted during wait for media/mount.\n"));
193 case BST_WAITING_FOR_SYSOP:
194 if (jcr->JobStatus == JS_WaitMount) {
195 bnet_fsend(user, _(" Device is BLOCKED waiting for mount of volume \"%s\".\n"),
198 bnet_fsend(user, _(" Device is BLOCKED waiting for media.\n"));
201 case BST_DOING_ACQUIRE:
202 bnet_fsend(user, _(" Device is being initialized.\n"));
204 case BST_WRITING_LABEL:
205 bnet_fsend(user, _(" Device is blocked labeling a Volume.\n"));
210 /* Send autochanger slot status */
211 if (dev->is_autochanger()) {
213 bnet_fsend(user, _(" Slot %d is loaded in drive %d.\n"),
214 dev->Slot, dev->drive_index);
216 bnet_fsend(user, _(" Drive %d is not loaded.\n"), dev->drive_index);
219 if (debug_level > 1) {
220 bnet_fsend(user, _("Configured device capabilities:\n"));
221 bnet_fsend(user, "%sEOF ", dev->capabilities & CAP_EOF ? "" : "!");
222 bnet_fsend(user, "%sBSR ", dev->capabilities & CAP_BSR ? "" : "!");
223 bnet_fsend(user, "%sBSF ", dev->capabilities & CAP_BSF ? "" : "!");
224 bnet_fsend(user, "%sFSR ", dev->capabilities & CAP_FSR ? "" : "!");
225 bnet_fsend(user, "%sFSF ", dev->capabilities & CAP_FSF ? "" : "!");
226 bnet_fsend(user, "%sEOM ", dev->capabilities & CAP_EOM ? "" : "!");
227 bnet_fsend(user, "%sREM ", dev->capabilities & CAP_REM ? "" : "!");
228 bnet_fsend(user, "%sRACCESS ", dev->capabilities & CAP_RACCESS ? "" : "!");
229 bnet_fsend(user, "%sAUTOMOUNT ", dev->capabilities & CAP_AUTOMOUNT ? "" : "!");
230 bnet_fsend(user, "%sLABEL ", dev->capabilities & CAP_LABEL ? "" : "!");
231 bnet_fsend(user, "%sANONVOLS ", dev->capabilities & CAP_ANONVOLS ? "" : "!");
232 bnet_fsend(user, "%sALWAYSOPEN ", dev->capabilities & CAP_ALWAYSOPEN ? "" : "!");
233 bnet_fsend(user, "\n");
235 bnet_fsend(user, _("Device state:\n"));
236 bnet_fsend(user, "%sOPENED ", dev->is_open() ? "" : "!");
237 bnet_fsend(user, "%sTAPE ", dev->is_tape() ? "" : "!");
238 bnet_fsend(user, "%sLABEL ", dev->is_labeled() ? "" : "!");
239 bnet_fsend(user, "%sMALLOC ", dev->state & ST_MALLOC ? "" : "!");
240 bnet_fsend(user, "%sAPPEND ", dev->can_append() ? "" : "!");
241 bnet_fsend(user, "%sREAD ", dev->can_read() ? "" : "!");
242 bnet_fsend(user, "%sEOT ", dev->at_eot() ? "" : "!");
243 bnet_fsend(user, "%sWEOT ", dev->state & ST_WEOT ? "" : "!");
244 bnet_fsend(user, "%sEOF ", dev->at_eof() ? "" : "!");
245 bnet_fsend(user, "%sNEXTVOL ", dev->state & ST_NEXTVOL ? "" : "!");
246 bnet_fsend(user, "%sSHORT ", dev->state & ST_SHORT ? "" : "!");
247 bnet_fsend(user, "%sMOUNTED ", dev->state & ST_MOUNTED ? "" : "!");
248 bnet_fsend(user, "\n");
249 bnet_fsend(user, _("num_writers=%d JobStatus=%c block=%d\n\n"), dev->num_writers,
250 jcr->JobStatus, dev->dev_blocked);
252 bnet_fsend(user, _("Device parameters:\n"));
253 bnet_fsend(user, _("Archive name: %s Device name: %s\n"), dev->archive_name(),
255 bnet_fsend(user, _("File=%u block=%u\n"), dev->file, dev->block_num);
256 bnet_fsend(user, _("Min block=%u Max block=%u\n"), dev->min_block_size, dev->max_block_size);
261 static void list_running_jobs(BSOCK *user)
267 char JobName[MAX_NAME_LENGTH];
268 char b1[30], b2[30], b3[30];
270 bnet_fsend(user, _("\nRunning Jobs:\n"));
272 if (jcr->JobStatus == JS_WaitFD) {
273 bnet_fsend(user, _("%s Job %s waiting for Client connection.\n"),
274 job_type_to_str(jcr->JobType), jcr->Job);
277 rdcr = jcr->read_dcr;
278 if ((dcr && dcr->device) || rdcr && rdcr->device) {
279 bstrncpy(JobName, jcr->Job, sizeof(JobName));
280 /* There are three periods after the Job name */
282 for (int i=0; i<3; i++) {
283 if ((p=strrchr(JobName, '.')) != NULL) {
287 if (rdcr && rdcr->device) {
288 bnet_fsend(user, _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
289 " pool=\"%s\" device=\"%s\"\n"),
290 job_level_to_str(jcr->JobLevel),
291 job_type_to_str(jcr->JobType),
296 rdcr->dev?rdcr->dev->print_name():
297 rdcr->device->device_name);
299 if (dcr && dcr->device) {
300 bnet_fsend(user, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
301 " pool=\"%s\" device=\"%s\"\n"),
302 job_level_to_str(jcr->JobLevel),
303 job_type_to_str(jcr->JobType),
308 dcr->dev?dcr->dev->print_name():
309 dcr->device->device_name);
310 bnet_fsend(user, _(" spooling=%d despooling=%d devblocked=%d\n"),
311 dcr->spooling, dcr->despooling, dcr->dev->dev_blocked);
313 sec = time(NULL) - jcr->run_time;
317 bps = jcr->JobBytes / sec;
318 bnet_fsend(user, _(" Files=%s Bytes=%s Bytes/sec=%s\n"),
319 edit_uint64_with_commas(jcr->JobFiles, b1),
320 edit_uint64_with_commas(jcr->JobBytes, b2),
321 edit_uint64_with_commas(bps, b3));
324 if (jcr->file_bsock) {
325 bnet_fsend(user, _(" FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
326 edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
327 jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
328 jcr->file_bsock->fd);
330 bnet_fsend(user, _(" FDSocket closed\n"));
338 bnet_fsend(user, _("No Jobs running.\n"));
340 bnet_fsend(user, _("====\n"));
343 static void list_jobs_waiting_on_reservation(BSOCK *user)
347 bnet_fsend(user, _("\nJobs waiting to reserve a drive:\n"));
349 if (!jcr->reserve_msgs) {
352 send_drive_reserve_messages(jcr, user);
356 bnet_fsend(user, _("====\n"));
360 static void list_terminated_jobs(void *arg)
362 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
364 struct s_last_job *je;
367 if (last_jobs->size() == 0) {
368 msg = _("No Terminated Jobs.\n");
369 sendit(msg, strlen(msg), arg);
372 lock_last_jobs_list();
373 msg = _("\nTerminated Jobs:\n");
374 sendit(msg, strlen(msg), arg);
375 msg = _(" JobId Level Files Bytes Status Finished Name \n");
376 sendit(msg, strlen(msg), arg);
377 msg = _("======================================================================\n");
378 sendit(msg, strlen(msg), arg);
379 foreach_dlist(je, last_jobs) {
380 char JobName[MAX_NAME_LENGTH];
381 const char *termstat;
384 bstrftime_nc(dt, sizeof(dt), je->end_time);
385 switch (je->JobType) {
388 bstrncpy(level, " ", sizeof(level));
391 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
395 switch (je->JobStatus) {
397 termstat = _("Created");
400 case JS_ErrorTerminated:
401 termstat = _("Error");
404 termstat = _("Diffs");
407 termstat = _("Cancel");
413 termstat = _("Other");
416 bstrncpy(JobName, je->Job, sizeof(JobName));
417 /* There are three periods after the Job name */
419 for (int i=0; i<3; i++) {
420 if ((p=strrchr(JobName, '.')) != NULL) {
424 bsnprintf(buf, sizeof(buf), _("%6d %-6s %8s %14s %-7s %-8s %s\n"),
427 edit_uint64_with_commas(je->JobFiles, b1),
428 edit_uint64_with_commas(je->JobBytes, b2),
431 sendit(buf, strlen(buf), arg);
433 sendit(_("====\n"), 5, arg);
434 unlock_last_jobs_list();
438 * Convert Job Level into a string
440 static const char *level_to_str(int level)
451 str = _("Incremental");
454 str = _("Differential");
459 case L_VERIFY_CATALOG:
460 str = _("Verify Catalog");
463 str = _("Init Catalog");
465 case L_VERIFY_VOLUME_TO_CATALOG:
466 str = _("Volume to Catalog");
468 case L_VERIFY_DISK_TO_CATALOG:
469 str = _("Disk to Catalog");
478 str = _("Unknown Job Level");
487 static void sendit(const char *msg, int len, void *arg)
489 BSOCK *user = (BSOCK *)arg;
491 memcpy(user->msg, msg, len+1);
492 user->msglen = len+1;
497 * .status command from Director
499 bool qstatus_cmd(JCR *jcr)
501 BSOCK *dir = jcr->dir_bsock;
506 if (sscanf(dir->msg, qstatus, time.c_str()) != 1) {
507 pm_strcpy(jcr->errmsg, dir->msg);
508 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
509 bnet_fsend(dir, _("3900 Bad .status command, missing argument.\n"));
510 bnet_sig(dir, BNET_EOD);
515 if (strcmp(time.c_str(), "current") == 0) {
516 bnet_fsend(dir, OKqstatus, time.c_str());
518 if (njcr->JobId != 0) {
519 bnet_fsend(dir, DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
523 } else if (strcmp(time.c_str(), "last") == 0) {
524 bnet_fsend(dir, OKqstatus, time.c_str());
525 if ((last_jobs) && (last_jobs->size() > 0)) {
526 job = (s_last_job*)last_jobs->last();
527 bnet_fsend(dir, DotStatusJob, job->JobId, job->JobStatus, job->Errors);
530 pm_strcpy(jcr->errmsg, dir->msg);
531 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
532 bnet_fsend(dir, _("3900 Bad .status command, wrong argument.\n"));
533 bnet_sig(dir, BNET_EOD);
536 bnet_sig(dir, BNET_EOD);