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));
146 bnet_fsend(user, _("Archive \"%s\" is not open or does not exist.\n"), device->hdr.name);
147 send_blocked_status(jcr, dev);
150 bnet_fsend(user, "====\n\n");
151 bnet_fsend(user, "Volume status:\n");
156 if (debug_level > 0) {
157 bnet_fsend(user, "====\n\n");
158 dump_resource(R_DEVICE, resources[R_DEVICE-r_first].res_head, sendit, user);
160 bnet_fsend(user, "====\n\n");
163 list_spool_stats(user);
165 bnet_sig(user, BNET_EOD);
169 static void send_blocked_status(JCR *jcr, DEVICE *dev)
171 BSOCK *user = jcr->dir_bsock;
175 bnet_fsend(user, "No DEVICE structure.\n\n");
178 switch (dev->dev_blocked) {
180 bnet_fsend(user, _(" Device is BLOCKED. User unmounted.\n"));
182 case BST_UNMOUNTED_WAITING_FOR_SYSOP:
183 bnet_fsend(user, _(" Device is BLOCKED. User unmounted during wait for media/mount.\n"));
185 case BST_WAITING_FOR_SYSOP:
186 if (jcr->JobStatus == JS_WaitMount) {
187 bnet_fsend(user, _(" Device is BLOCKED waiting for mount of volume \"%s\".\n"),
190 bnet_fsend(user, _(" Device is BLOCKED waiting for media.\n"));
193 case BST_DOING_ACQUIRE:
194 bnet_fsend(user, _(" Device is being initialized.\n"));
196 case BST_WRITING_LABEL:
197 bnet_fsend(user, _(" Device is blocked labeling a Volume.\n"));
202 if (debug_level > 1) {
203 bnet_fsend(user, _("Configured device capabilities:\n"));
204 bnet_fsend(user, "%sEOF ", dev->capabilities & CAP_EOF ? "" : "!");
205 bnet_fsend(user, "%sBSR ", dev->capabilities & CAP_BSR ? "" : "!");
206 bnet_fsend(user, "%sBSF ", dev->capabilities & CAP_BSF ? "" : "!");
207 bnet_fsend(user, "%sFSR ", dev->capabilities & CAP_FSR ? "" : "!");
208 bnet_fsend(user, "%sFSF ", dev->capabilities & CAP_FSF ? "" : "!");
209 bnet_fsend(user, "%sEOM ", dev->capabilities & CAP_EOM ? "" : "!");
210 bnet_fsend(user, "%sREM ", dev->capabilities & CAP_REM ? "" : "!");
211 bnet_fsend(user, "%sRACCESS ", dev->capabilities & CAP_RACCESS ? "" : "!");
212 bnet_fsend(user, "%sAUTOMOUNT ", dev->capabilities & CAP_AUTOMOUNT ? "" : "!");
213 bnet_fsend(user, "%sLABEL ", dev->capabilities & CAP_LABEL ? "" : "!");
214 bnet_fsend(user, "%sANONVOLS ", dev->capabilities & CAP_ANONVOLS ? "" : "!");
215 bnet_fsend(user, "%sALWAYSOPEN ", dev->capabilities & CAP_ALWAYSOPEN ? "" : "!");
216 bnet_fsend(user, "\n");
218 bnet_fsend(user, _("Device state:\n"));
219 bnet_fsend(user, "%sOPENED ", dev->is_open() ? "" : "!");
220 bnet_fsend(user, "%sTAPE ", dev->is_tape() ? "" : "!");
221 bnet_fsend(user, "%sLABEL ", dev->is_labeled() ? "" : "!");
222 bnet_fsend(user, "%sMALLOC ", dev->state & ST_MALLOC ? "" : "!");
223 bnet_fsend(user, "%sAPPEND ", dev->can_append() ? "" : "!");
224 bnet_fsend(user, "%sREAD ", dev->can_read() ? "" : "!");
225 bnet_fsend(user, "%sEOT ", dev->at_eot() ? "" : "!");
226 bnet_fsend(user, "%sWEOT ", dev->state & ST_WEOT ? "" : "!");
227 bnet_fsend(user, "%sEOF ", dev->at_eof() ? "" : "!");
228 bnet_fsend(user, "%sNEXTVOL ", dev->state & ST_NEXTVOL ? "" : "!");
229 bnet_fsend(user, "%sSHORT ", dev->state & ST_SHORT ? "" : "!");
230 bnet_fsend(user, "%sMOUNTED ", dev->state & ST_MOUNTED ? "" : "!");
231 bnet_fsend(user, "\n");
232 bnet_fsend(user, "num_writers=%d JobStatus=%c block=%d\n\n", dev->num_writers,
233 jcr->JobStatus, dev->dev_blocked);
235 bnet_fsend(user, _("Device parameters:\n"));
236 bnet_fsend(user, "Archive name: %s Device name: %s\n", dev->archive_name(),
238 bnet_fsend(user, "File=%u block=%u\n", dev->file, dev->block_num);
239 bnet_fsend(user, "Min block=%u Max block=%u\n", dev->min_block_size, dev->max_block_size);
244 static void list_running_jobs(BSOCK *user)
249 char JobName[MAX_NAME_LENGTH];
250 char b1[30], b2[30], b3[30];
252 bnet_fsend(user, _("\nRunning Jobs:\n"));
254 if (jcr->JobStatus == JS_WaitFD) {
255 bnet_fsend(user, _("%s Job %s waiting for Client connection.\n"),
256 job_type_to_str(jcr->JobType), jcr->Job);
258 if (jcr->dcr && jcr->dcr->device) {
259 bstrncpy(JobName, jcr->Job, sizeof(JobName));
260 /* There are three periods after the Job name */
262 for (int i=0; i<3; i++) {
263 if ((p=strrchr(JobName, '.')) != NULL) {
267 bnet_fsend(user, _("%s %s job %s JobId=%d Volume=\"%s\" device=\"%s\"\n"),
268 job_level_to_str(jcr->JobLevel),
269 job_type_to_str(jcr->JobType),
272 jcr->dcr->VolumeName,
273 jcr->dcr->device->device_name);
274 sec = time(NULL) - jcr->run_time;
278 bps = jcr->JobBytes / sec;
279 bnet_fsend(user, _(" Files=%s Bytes=%s Bytes/sec=%s\n"),
280 edit_uint64_with_commas(jcr->JobFiles, b1),
281 edit_uint64_with_commas(jcr->JobBytes, b2),
282 edit_uint64_with_commas(bps, b3));
285 if (jcr->file_bsock) {
286 bnet_fsend(user, " FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n",
287 edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
288 jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
289 jcr->file_bsock->fd);
291 bnet_fsend(user, " FDSocket closed\n");
298 bnet_fsend(user, _("No Jobs running.\n"));
300 bnet_fsend(user, "====\n");
303 static void list_terminated_jobs(void *arg)
305 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
307 struct s_last_job *je;
310 if (last_jobs->size() == 0) {
311 msg = _("No Terminated Jobs.\n");
312 sendit(msg, strlen(msg), arg);
315 lock_last_jobs_list();
316 msg = _("\nTerminated Jobs:\n");
317 sendit(msg, strlen(msg), arg);
318 msg = _(" JobId Level Files Bytes Status Finished Name \n");
319 sendit(msg, strlen(msg), arg);
320 msg = _("======================================================================\n");
321 sendit(msg, strlen(msg), arg);
322 foreach_dlist(je, last_jobs) {
323 char JobName[MAX_NAME_LENGTH];
324 const char *termstat;
327 bstrftime_nc(dt, sizeof(dt), je->end_time);
328 switch (je->JobType) {
331 bstrncpy(level, " ", sizeof(level));
334 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
338 switch (je->JobStatus) {
340 termstat = "Created";
343 case JS_ErrorTerminated:
359 bstrncpy(JobName, je->Job, sizeof(JobName));
360 /* There are three periods after the Job name */
362 for (int i=0; i<3; i++) {
363 if ((p=strrchr(JobName, '.')) != NULL) {
367 bsnprintf(buf, sizeof(buf), _("%6d %-6s %8s %14s %-7s %-8s %s\n"),
370 edit_uint64_with_commas(je->JobFiles, b1),
371 edit_uint64_with_commas(je->JobBytes, b2),
374 sendit(buf, strlen(buf), arg);
376 sendit("====\n", 5, arg);
377 unlock_last_jobs_list();
381 * Convert Job Level into a string
383 static const char *level_to_str(int level)
394 str = _("Incremental");
397 str = _("Differential");
402 case L_VERIFY_CATALOG:
403 str = _("Verify Catalog");
406 str = _("Init Catalog");
408 case L_VERIFY_VOLUME_TO_CATALOG:
409 str = _("Volume to Catalog");
411 case L_VERIFY_DISK_TO_CATALOG:
412 str = _("Disk to Catalog");
421 str = _("Unknown Job Level");
430 static void sendit(const char *msg, int len, void *arg)
432 BSOCK *user = (BSOCK *)arg;
434 memcpy(user->msg, msg, len+1);
435 user->msglen = len+1;
440 * .status command from Director
442 bool qstatus_cmd(JCR *jcr)
444 BSOCK *dir = jcr->dir_bsock;
449 if (sscanf(dir->msg, qstatus, time.c_str()) != 1) {
450 pm_strcpy(jcr->errmsg, dir->msg);
451 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
452 bnet_fsend(dir, "3900 Bad .status command, missing argument.\n");
453 bnet_sig(dir, BNET_EOD);
458 if (strcmp(time.c_str(), "current") == 0) {
459 bnet_fsend(dir, OKqstatus, time.c_str());
461 if (njcr->JobId != 0) {
462 bnet_fsend(dir, DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
467 else if (strcmp(time.c_str(), "last") == 0) {
468 bnet_fsend(dir, OKqstatus, time.c_str());
469 if ((last_jobs) && (last_jobs->size() > 0)) {
470 job = (s_last_job*)last_jobs->last();
471 bnet_fsend(dir, DotStatusJob, job->JobId, job->JobStatus, job->Errors);
475 pm_strcpy(jcr->errmsg, dir->msg);
476 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
477 bnet_fsend(dir, "3900 Bad .status command, wrong argument.\n");
478 bnet_sig(dir, BNET_EOD);
481 bnet_sig(dir, BNET_EOD);