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 ammended 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"));
95 foreach_res(changer, R_AUTOCHANGER) {
96 bnet_fsend(user, _("Autochanger \"%s\" with devices:\n"),
98 foreach_alist(device, changer->device) {
100 bnet_fsend(user, " %s\n", device->dev->print_name());
102 bnet_fsend(user, " %s\n", device->hdr.name);
106 foreach_res(device, R_DEVICE) {
108 if (dev && dev->is_open()) {
109 if (dev->is_labeled()) {
110 bnet_fsend(user, _("Device %s is mounted with Volume \"%s\"\n"),
111 dev->print_name(), dev->VolHdr.VolName);
113 bnet_fsend(user, _("Device %s open but no Bacula volume is mounted.\n"),
116 send_blocked_status(jcr, dev);
117 if (dev->can_append()) {
118 bpb = dev->VolCatInfo.VolCatBlocks;
122 bpb = dev->VolCatInfo.VolCatBytes / bpb;
123 bnet_fsend(user, _(" Total Bytes=%s Blocks=%s Bytes/block=%s\n"),
124 edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, b1),
125 edit_uint64_with_commas(dev->VolCatInfo.VolCatBlocks, b2),
126 edit_uint64_with_commas(bpb, b3));
127 } else { /* reading */
128 bpb = dev->VolCatInfo.VolCatReads;
132 if (dev->VolCatInfo.VolCatRBytes > 0) {
133 bpb = dev->VolCatInfo.VolCatRBytes / bpb;
137 bnet_fsend(user, _(" Total Bytes Read=%s Blocks Read=%s Bytes/block=%s\n"),
138 edit_uint64_with_commas(dev->VolCatInfo.VolCatRBytes, b1),
139 edit_uint64_with_commas(dev->VolCatInfo.VolCatReads, b2),
140 edit_uint64_with_commas(bpb, b3));
142 bnet_fsend(user, _(" Positioned at File=%s Block=%s\n"),
143 edit_uint64_with_commas(dev->file, b1),
144 edit_uint64_with_commas(dev->block_num, b2));
147 bnet_fsend(user, _("Archive \"%s\" is not open or does not exist.\n"), device->hdr.name);
148 send_blocked_status(jcr, dev);
152 bnet_fsend(user, "====\n\n");
153 bnet_fsend(user, "Volume status:\n");
158 if (debug_level > 0) {
159 bnet_fsend(user, "====\n\n");
160 dump_resource(R_DEVICE, resources[R_DEVICE-r_first].res_head, sendit, user);
162 bnet_fsend(user, "====\n\n");
165 list_spool_stats(user);
167 bnet_sig(user, BNET_EOD);
171 static void send_blocked_status(JCR *jcr, DEVICE *dev)
173 BSOCK *user = jcr->dir_bsock;
179 switch (dev->dev_blocked) {
181 bnet_fsend(user, _(" Device is BLOCKED. User unmounted.\n"));
183 case BST_UNMOUNTED_WAITING_FOR_SYSOP:
184 bnet_fsend(user, _(" Device is BLOCKED. User unmounted during wait for media/mount.\n"));
186 case BST_WAITING_FOR_SYSOP:
187 if (jcr->JobStatus == JS_WaitMount) {
188 bnet_fsend(user, _(" Device is BLOCKED waiting for mount of volume \"%s\".\n"),
191 bnet_fsend(user, _(" Device is BLOCKED waiting for media.\n"));
194 case BST_DOING_ACQUIRE:
195 bnet_fsend(user, _(" Device is being initialized.\n"));
197 case BST_WRITING_LABEL:
198 bnet_fsend(user, _(" Device is blocked labeling a Volume.\n"));
203 if (debug_level > 1) {
204 bnet_fsend(user, _("Configured device capabilities:\n"));
205 bnet_fsend(user, "%sEOF ", dev->capabilities & CAP_EOF ? "" : "!");
206 bnet_fsend(user, "%sBSR ", dev->capabilities & CAP_BSR ? "" : "!");
207 bnet_fsend(user, "%sBSF ", dev->capabilities & CAP_BSF ? "" : "!");
208 bnet_fsend(user, "%sFSR ", dev->capabilities & CAP_FSR ? "" : "!");
209 bnet_fsend(user, "%sFSF ", dev->capabilities & CAP_FSF ? "" : "!");
210 bnet_fsend(user, "%sEOM ", dev->capabilities & CAP_EOM ? "" : "!");
211 bnet_fsend(user, "%sREM ", dev->capabilities & CAP_REM ? "" : "!");
212 bnet_fsend(user, "%sRACCESS ", dev->capabilities & CAP_RACCESS ? "" : "!");
213 bnet_fsend(user, "%sAUTOMOUNT ", dev->capabilities & CAP_AUTOMOUNT ? "" : "!");
214 bnet_fsend(user, "%sLABEL ", dev->capabilities & CAP_LABEL ? "" : "!");
215 bnet_fsend(user, "%sANONVOLS ", dev->capabilities & CAP_ANONVOLS ? "" : "!");
216 bnet_fsend(user, "%sALWAYSOPEN ", dev->capabilities & CAP_ALWAYSOPEN ? "" : "!");
217 bnet_fsend(user, "\n");
219 bnet_fsend(user, _("Device status:\n"));
220 bnet_fsend(user, "%sOPENED ", dev->is_open() ? "" : "!");
221 bnet_fsend(user, "%sTAPE ", dev->is_tape() ? "" : "!");
222 bnet_fsend(user, "%sLABEL ", dev->is_labeled() ? "" : "!");
223 bnet_fsend(user, "%sMALLOC ", dev->state & ST_MALLOC ? "" : "!");
224 bnet_fsend(user, "%sAPPEND ", dev->can_append() ? "" : "!");
225 bnet_fsend(user, "%sREAD ", dev->can_read() ? "" : "!");
226 bnet_fsend(user, "%sEOT ", dev->at_eot() ? "" : "!");
227 bnet_fsend(user, "%sWEOT ", dev->state & ST_WEOT ? "" : "!");
228 bnet_fsend(user, "%sEOF ", dev->at_eof() ? "" : "!");
229 bnet_fsend(user, "%sNEXTVOL ", dev->state & ST_NEXTVOL ? "" : "!");
230 bnet_fsend(user, "%sSHORT ", dev->state & ST_SHORT ? "" : "!");
231 bnet_fsend(user, "%sMOUNTED ", dev->state & ST_MOUNTED ? "" : "!");
232 bnet_fsend(user, "\n");
233 bnet_fsend(user, "num_writers=%d JobStatus=%c block=%d\nn", dev->num_writers,
234 jcr->JobStatus, dev->dev_blocked);
236 bnet_fsend(user, _("Device parameters:\n"));
237 bnet_fsend(user, "Archive name: %s Device name: %s\n", dev->archive_name(),
239 bnet_fsend(user, "File=%u block=%u\n", dev->file, dev->block_num);
240 bnet_fsend(user, "Min block=%u Max block=%u\n", dev->min_block_size, dev->max_block_size);
245 static void list_running_jobs(BSOCK *user)
250 char JobName[MAX_NAME_LENGTH];
251 char b1[30], b2[30], b3[30];
253 bnet_fsend(user, _("\nRunning Jobs:\n"));
255 if (jcr->JobStatus == JS_WaitFD) {
256 bnet_fsend(user, _("%s Job %s waiting for Client connection.\n"),
257 job_type_to_str(jcr->JobType), jcr->Job);
259 if (jcr->dcr && jcr->dcr->device) {
260 bstrncpy(JobName, jcr->Job, sizeof(JobName));
261 /* There are three periods after the Job name */
263 for (int i=0; i<3; i++) {
264 if ((p=strrchr(JobName, '.')) != NULL) {
268 bnet_fsend(user, _("%s %s job %s JobId=%d Volume=\"%s\" device=\"%s\"\n"),
269 job_level_to_str(jcr->JobLevel),
270 job_type_to_str(jcr->JobType),
273 jcr->dcr->VolumeName,
274 jcr->dcr->device->device_name);
275 sec = time(NULL) - jcr->run_time;
279 bps = jcr->JobBytes / sec;
280 bnet_fsend(user, _(" Files=%s Bytes=%s Bytes/sec=%s\n"),
281 edit_uint64_with_commas(jcr->JobFiles, b1),
282 edit_uint64_with_commas(jcr->JobBytes, b2),
283 edit_uint64_with_commas(bps, b3));
286 if (jcr->file_bsock) {
287 bnet_fsend(user, " FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n",
288 edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
289 jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
290 jcr->file_bsock->fd);
292 bnet_fsend(user, " FDSocket closed\n");
299 bnet_fsend(user, _("No Jobs running.\n"));
301 bnet_fsend(user, "====\n");
304 static void list_terminated_jobs(void *arg)
306 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
308 struct s_last_job *je;
311 if (last_jobs->size() == 0) {
312 msg = _("No Terminated Jobs.\n");
313 sendit(msg, strlen(msg), arg);
316 lock_last_jobs_list();
317 msg = _("\nTerminated Jobs:\n");
318 sendit(msg, strlen(msg), arg);
319 msg = _(" JobId Level Files Bytes Status Finished Name \n");
320 sendit(msg, strlen(msg), arg);
321 msg = _("======================================================================\n");
322 sendit(msg, strlen(msg), arg);
323 foreach_dlist(je, last_jobs) {
324 char JobName[MAX_NAME_LENGTH];
325 const char *termstat;
328 bstrftime_nc(dt, sizeof(dt), je->end_time);
329 switch (je->JobType) {
332 bstrncpy(level, " ", sizeof(level));
335 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
339 switch (je->JobStatus) {
341 termstat = "Created";
344 case JS_ErrorTerminated:
360 bstrncpy(JobName, je->Job, sizeof(JobName));
361 /* There are three periods after the Job name */
363 for (int i=0; i<3; i++) {
364 if ((p=strrchr(JobName, '.')) != NULL) {
368 bsnprintf(buf, sizeof(buf), _("%6d %-6s %8s %14s %-7s %-8s %s\n"),
371 edit_uint64_with_commas(je->JobFiles, b1),
372 edit_uint64_with_commas(je->JobBytes, b2),
375 sendit(buf, strlen(buf), arg);
377 sendit("====\n", 5, arg);
378 unlock_last_jobs_list();
382 * Convert Job Level into a string
384 static const char *level_to_str(int level)
395 str = _("Incremental");
398 str = _("Differential");
403 case L_VERIFY_CATALOG:
404 str = _("Verify Catalog");
407 str = _("Init Catalog");
409 case L_VERIFY_VOLUME_TO_CATALOG:
410 str = _("Volume to Catalog");
412 case L_VERIFY_DISK_TO_CATALOG:
413 str = _("Disk to Catalog");
422 str = _("Unknown Job Level");
431 static void sendit(const char *msg, int len, void *arg)
433 BSOCK *user = (BSOCK *)arg;
435 memcpy(user->msg, msg, len+1);
436 user->msglen = len+1;
441 * .status command from Director
443 bool qstatus_cmd(JCR *jcr)
445 BSOCK *dir = jcr->dir_bsock;
450 if (sscanf(dir->msg, qstatus, time.c_str()) != 1) {
451 pm_strcpy(jcr->errmsg, dir->msg);
452 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
453 bnet_fsend(dir, "3900 Bad .status command, missing argument.\n");
454 bnet_sig(dir, BNET_EOD);
459 if (strcmp(time.c_str(), "current") == 0) {
460 bnet_fsend(dir, OKqstatus, time.c_str());
462 if (njcr->JobId != 0) {
463 bnet_fsend(dir, DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
468 else if (strcmp(time.c_str(), "last") == 0) {
469 bnet_fsend(dir, OKqstatus, time.c_str());
470 if ((last_jobs) && (last_jobs->size() > 0)) {
471 job = (s_last_job*)last_jobs->last();
472 bnet_fsend(dir, DotStatusJob, job->JobId, job->JobStatus, job->Errors);
476 pm_strcpy(jcr->errmsg, dir->msg);
477 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
478 bnet_fsend(dir, "3900 Bad .status command, wrong argument.\n");
479 bnet_sig(dir, BNET_EOD);
482 bnet_sig(dir, BNET_EOD);