1 /*
2 Bacula(R) - The Network Backup Solution
3
4 Copyright (C) 2000-2020 Kern Sibbald
5
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
8
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
13
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
16
17 Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20 * This file handles the status command
21 *
22 * Kern Sibbald, May MMIII
23 *
24 *
25 */
26
27 #include "bacula.h"
28 #include "stored.h"
29 #include "lib/status.h"
30 #include "sd_plugins.h"
31
32 /* Imported functions */
33 extern void dbg_print_plugin(FILE *fp);
34
35 /* Imported variables */
36 extern BSOCK *filed_chan;
37 extern void *start_heap;
38
39 /* Static variables */
40 static char OKqstatus[] = "3000 OK .status\n";
41 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
42
43
44 /* Forward referenced functions */
45 static void sendit(POOL_MEM &msg, int len, STATUS_PKT *sp);
46 static void sendit(const char *msg, int len, void *arg);
47 static void dbg_sendit(const char *msg, int len, void *arg);
48 static void send_blocked_status(DEVICE *dev, STATUS_PKT *sp);
49 static void send_device_status(DEVICE *dev, STATUS_PKT *sp);
50 static void list_running_jobs(STATUS_PKT *sp);
51 static void list_jobs_waiting_on_reservation(STATUS_PKT *sp);
52 static void list_status_header(STATUS_PKT *sp);
53 static void list_devices(STATUS_PKT *sp, char *name=NULL);
54 static void list_plugins(STATUS_PKT *sp);
55 static void list_cloud_transfers(STATUS_PKT *sp, bool verbose);
56 static void list_collectors_status(STATUS_PKT *sp, char *collname);
57 static void api_collectors_status(STATUS_PKT *sp, char *collname);
58
status_alert_callback(void * ctx,const char * short_msg,const char * long_msg,char * Volume,int severity,int flags,int alertno,utime_t alert_time)59 void status_alert_callback(void *ctx, const char *short_msg,
60 const char *long_msg, char *Volume, int severity,
61 int flags, int alertno, utime_t alert_time)
62 {
63 STATUS_PKT *sp = (STATUS_PKT *)ctx;
64 const char *type = "Unknown";
65 POOL_MEM send_msg(PM_MESSAGE);
66 char edt[50];
67 int len;
68
69 switch (severity) {
70 case 'C':
71 type = "Critical";
72 break;
73 case 'W':
74 type = "Warning";
75 break;
76 case 'I':
77 type = "Info";
78 break;
79 }
80 bstrftimes(edt, sizeof(edt), alert_time);
81 if (chk_dbglvl(10)) {
82 len = Mmsg(send_msg, _(" %s Alert: at %s Volume=\"%s\" flags=0x%x alert=%s\n"),
83 type, edt, Volume, flags, long_msg);
84 } else {
85 len = Mmsg(send_msg, _(" %s Alert: at %s Volume=\"%s\" alert=%s\n"),
86 type, edt, Volume, short_msg);
87 }
88 sendit(send_msg, len, sp);
89 }
90
91
92 /*
93 * Status command from Director
94 */
output_status(STATUS_PKT * sp)95 void output_status(STATUS_PKT *sp)
96 {
97 POOL_MEM msg(PM_MESSAGE);
98 int len;
99
100 list_status_header(sp);
101
102 /*
103 * List running jobs
104 */
105 list_running_jobs(sp);
106
107 /*
108 * List jobs stuck in reservation system
109 */
110 list_jobs_waiting_on_reservation(sp);
111
112 /*
113 * List terminated jobs (defined in lib/status.h)
114 */
115 list_terminated_jobs(sp);
116
117 /*
118 * List devices
119 */
120 list_devices(sp);
121
122 /*
123 * List cloud transfers
124 */
125 list_cloud_transfers(sp, false);
126
127
128 len = Mmsg(msg, _("Used Volume status:\n"));
129 if (!sp->api) sendit(msg, len, sp);
130
131 list_volumes(sendit, (void *)sp);
132 if (!sp->api) sendit("====\n\n", 6, sp);
133
134 list_spool_stats(sendit, (void *)sp);
135 if (!sp->api) sendit("====\n\n", 6, sp);
136
137 if (chk_dbglvl(10)) {
138 dbg_print_plugin(stdout);
139 }
140 }
141
list_resources(STATUS_PKT * sp)142 static void list_resources(STATUS_PKT *sp)
143 {
144 #ifdef when_working
145 POOL_MEM msg(PM_MESSAGE);
146 int len;
147
148 len = Mmsg(msg, _("\nSD Resources:\n"));
149 if (!sp->api) sendit(msg, len, sp);
150 dump_resource(R_DEVICE, resources[R_DEVICE-r_first], sp);
151 if (!sp->api) sendit("====\n\n", 6, sp);
152 #endif
153 }
154
155 #ifdef xxxx
find_device(char * devname)156 static find_device(char *devname)
157 {
158 foreach_res(device, R_DEVICE) {
159 if (strcasecmp(device->hdr.name, devname) == 0) {
160 found = true;
161 break;
162 }
163 }
164 if (!found) {
165 foreach_res(changer, R_AUTOCHANGER) {
166 if (strcasecmp(changer->hdr.name, devname) == 0) {
167 break;
168 }
169 }
170 }
171 }
172 #endif
173
api_list_one_device(char * name,DEVICE * dev,STATUS_PKT * sp)174 static void api_list_one_device(char *name, DEVICE *dev, STATUS_PKT *sp)
175 {
176 OutputWriter ow(sp->api_opts);
177 int zero=0;
178 int blocked=0;
179 uint64_t f, t;
180 const char *p=NULL;
181
182 if (!dev) {
183 return;
184 }
185
186 dev->get_freespace(&f, &t);
187
188 ow.get_output(OT_START_OBJ,
189 OT_STRING, "name", dev->device->hdr.name,
190 OT_STRING, "archive_device", dev->archive_name(),
191 OT_STRING, "type", dev->print_type(),
192 OT_STRING, "media_type", dev->device->media_type,
193 OT_INT, "open", (int)dev->is_open(),
194 OT_INT, "writers", dev->num_writers,
195 OT_INT32, "maximum_concurrent_jobs", dev->max_concurrent_jobs,
196 OT_INT64, "maximum_volume_size", dev->max_volume_size,
197 OT_INT, "read_only", dev->device->read_only,
198 OT_INT, "autoselect", dev->device->autoselect,
199 OT_INT, "enabled", dev->enabled,
200 OT_INT64, "free_space", f,
201 OT_INT64, "total_space", t,
202 OT_INT64, "devno", dev->devno,
203 OT_END);
204
205 if (dev->is_open()) {
206 if (dev->is_labeled()) {
207 ow.get_output(OT_STRING, "mounted", dev->blocked()?"0":"1",
208 OT_STRING, "waiting", dev->blocked()?"1":"0",
209 OT_STRING, "volume", dev->VolHdr.VolumeName,
210 OT_STRING, "pool", NPRTB(dev->pool_name),
211 OT_END);
212 } else {
213 ow.get_output(OT_INT, "mounted", zero,
214 OT_INT, "waiting", zero,
215 OT_STRING, "volume", "",
216 OT_STRING, "pool", "",
217 OT_END);
218 }
219
220 blocked = 1;
221 switch(dev->blocked()) {
222 case BST_UNMOUNTED:
223 p = "User unmounted";
224 break;
225 case BST_UNMOUNTED_WAITING_FOR_SYSOP:
226 p = "User unmounted during wait for media/mount";
227 break;
228 case BST_DOING_ACQUIRE:
229 p = "Device is being initialized";
230 break;
231 case BST_WAITING_FOR_SYSOP:
232 p = "Waiting for mount or create a volume";
233 break;
234 case BST_WRITING_LABEL:
235 p = "Labeling a Volume";
236 break;
237 default:
238 blocked=0;
239 p = NULL;
240 }
241
242 /* TODO: give more information about blocked status
243 * and the volume needed if WAITING for SYSOP
244 */
245 ow.get_output(OT_STRING, "blocked_desc", NPRTB(p),
246 OT_INT, "blocked", blocked,
247 OT_END);
248
249 ow.get_output(OT_INT, "append", (int)dev->can_append(),
250 OT_END);
251
252 if (dev->can_append()) {
253 ow.get_output(OT_INT64, "bytes", dev->VolCatInfo.VolCatBytes,
254 OT_INT32, "blocks", dev->VolCatInfo.VolCatBlocks,
255 OT_END);
256
257 } else { /* reading */
258 ow.get_output(OT_INT64, "bytes", dev->VolCatInfo.VolCatRBytes,
259 OT_INT32, "blocks", dev->VolCatInfo.VolCatReads, /* might not be blocks */
260 OT_END);
261
262 }
263 ow.get_output(OT_INT, "file", dev->file,
264 OT_INT, "block", dev->block_num,
265 OT_END);
266 } else {
267 ow.get_output(OT_INT, "mounted", zero,
268 OT_INT, "waiting", zero,
269 OT_STRING, "volume", "",
270 OT_STRING, "pool", "",
271 OT_STRING, "blocked_desc", "",
272 OT_INT, "blocked", zero,
273 OT_INT, "append", zero,
274 OT_INT, "bytes", zero,
275 OT_INT, "blocks", zero,
276 OT_INT, "file", zero,
277 OT_INT, "block", zero,
278 OT_END);
279 }
280
281 p = ow.get_output(OT_END_OBJ, OT_END);
282 sendit(p, strlen(p), sp);
283 }
284
285
list_one_device(char * name,DEVICE * dev,STATUS_PKT * sp)286 static void list_one_device(char *name, DEVICE *dev, STATUS_PKT *sp)
287 {
288 char b1[35], b2[35], b3[35];
289 POOL_MEM msg(PM_MESSAGE);
290 int len;
291 int bpb;
292
293 if (sp->api > 1) {
294 api_list_one_device(name, dev, sp);
295 return;
296 }
297
298 if (!dev) {
299 len = Mmsg(msg, _("\nDevice \"%s\" is not open or does not exist.\n"),
300 name);
301 sendit(msg, len, sp);
302 if (!sp->api) sendit("==\n", 4, sp);
303 return;
304 }
305
306 if (dev->is_open()) {
307 if (dev->is_labeled()) {
308 len = Mmsg(msg, _("\nDevice %s is %s %s:\n"
309 " Volume: %s\n"
310 " Pool: %s\n"
311 " Media type: %s\n"),
312 dev->print_type(), dev->print_name(),
313 dev->blocked()?_("waiting for"):_("mounted with"),
314 dev->VolHdr.VolumeName,
315 dev->pool_name[0]?dev->pool_name:_("*unknown*"),
316 dev->device->media_type);
317 sendit(msg, len, sp);
318 } else {
319 len = Mmsg(msg, _("\nDevice %s: %s open but no Bacula volume is currently mounted.\n"),
320 dev->print_type(), dev->print_name());
321 sendit(msg, len, sp);
322 }
323 if (dev->can_append()) {
324 bpb = dev->VolCatInfo.VolCatBlocks;
325 if (bpb <= 0) {
326 bpb = 1;
327 }
328 bpb = dev->VolCatInfo.VolCatBytes / bpb;
329 len = Mmsg(msg, _(" Total Bytes=%s Blocks=%s Bytes/block=%s\n"),
330 edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, b1),
331 edit_uint64_with_commas(dev->VolCatInfo.VolCatBlocks, b2),
332 edit_uint64_with_commas(bpb, b3));
333 sendit(msg, len, sp);
334 } else { /* reading */
335 bpb = dev->VolCatInfo.VolCatReads;
336 if (bpb <= 0) {
337 bpb = 1;
338 }
339 if (dev->VolCatInfo.VolCatRBytes > 0) {
340 bpb = dev->VolCatInfo.VolCatRBytes / bpb;
341 } else {
342 bpb = 0;
343 }
344 len = Mmsg(msg, _(" Total Bytes Read=%s Blocks Read=%s Bytes/block=%s\n"),
345 edit_uint64_with_commas(dev->VolCatInfo.VolCatRBytes, b1),
346 edit_uint64_with_commas(dev->VolCatInfo.VolCatReads, b2),
347 edit_uint64_with_commas(bpb, b3));
348 sendit(msg, len, sp);
349 }
350 len = Mmsg(msg, _(" Positioned at File=%s Block=%s\n"),
351 edit_uint64_with_commas(dev->file, b1),
352 edit_uint64_with_commas(dev->block_num, b2));
353 sendit(msg, len, sp);
354 } else {
355 len = Mmsg(msg, _("\nDevice %s: %s is not open.\n"),
356 dev->print_type(), dev->print_name());
357 sendit(msg, len, sp);
358 }
359 send_blocked_status(dev, sp);
360
361 /* TODO: We need to check with Mount command, maybe we can
362 * display this number only when the device is open.
363 */
364 if (dev->is_file()) {
365 char ed1[50];
366 uint64_t f, t;
367 dev->get_freespace(&f, &t);
368 if (t > 0) { /* We might not have access to numbers */
369 len = Mmsg(msg, _(" Available %sSpace=%sB\n"),
370 dev->is_cloud() ? _("Cache ") : "",
371 edit_uint64_with_suffix(f, ed1));
372 sendit(msg, len, sp);
373 }
374 }
375
376 dev->show_tape_alerts((DCR *)sp, list_short, list_all, status_alert_callback);
377
378 if (!sp->api) sendit("==\n", 4, sp);
379 }
380
_dbg_list_one_device(char * name,DEVICE * dev,const char * file,int line)381 void _dbg_list_one_device(char *name, DEVICE *dev, const char *file, int line)
382 {
383 STATUS_PKT sp;
384 sp.bs = NULL;
385 sp.callback = dbg_sendit;
386 sp.context = NULL;
387 d_msg(file, line, 0, "Called dbg_list_one_device():");
388 list_one_device(name, dev, &sp);
389 send_device_status(dev, &sp);
390 }
391
list_one_autochanger(char * name,AUTOCHANGER * changer,STATUS_PKT * sp)392 static void list_one_autochanger(char *name, AUTOCHANGER *changer, STATUS_PKT *sp)
393 {
394 int len;
395 char *p;
396 DEVRES *device;
397 POOL_MEM msg(PM_MESSAGE);
398 OutputWriter ow(sp->api_opts);
399
400 if (sp->api > 1) {
401 ow.get_output(OT_START_OBJ,
402 OT_STRING, "autochanger", changer->hdr.name,
403 OT_END);
404
405 ow.start_group("devices");
406
407 foreach_alist(device, changer->device) {
408 ow.get_output(OT_START_OBJ,
409 OT_STRING, "name", device->hdr.name,
410 OT_STRING, "device",device->device_name,
411 OT_END_OBJ,
412 OT_END);
413 }
414
415 ow.end_group();
416
417 p = ow.get_output(OT_END_OBJ, OT_END);
418 sendit(p, strlen(p), sp);
419
420 } else {
421
422 len = Mmsg(msg, _("Autochanger \"%s\" with devices:\n"),
423 changer->hdr.name);
424 sendit(msg, len, sp);
425
426 foreach_alist(device, changer->device) {
427 if (device->dev) {
428 len = Mmsg(msg, " %s\n", device->dev->print_name());
429 sendit(msg, len, sp);
430 } else {
431 len = Mmsg(msg, " %s\n", device->hdr.name);
432 sendit(msg, len, sp);
433 }
434 }
435 }
436 }
437
list_devices(STATUS_PKT * sp,char * name)438 static void list_devices(STATUS_PKT *sp, char *name)
439 {
440 int len;
441 DEVRES *device;
442 AUTOCHANGER *changer;
443 POOL_MEM msg(PM_MESSAGE);
444
445 if (!sp->api) {
446 len = Mmsg(msg, _("\nDevice status:\n"));
447 sendit(msg, len, sp);
448 }
449
450 foreach_res(changer, R_AUTOCHANGER) {
451 if (!name || strcmp(changer->hdr.name, name) == 0) {
452 list_one_autochanger(changer->hdr.name, changer, sp);
453 }
454 }
455
456 foreach_res(device, R_DEVICE) {
457 if (!name || strcmp(device->hdr.name, name) == 0) {
458 list_one_device(device->hdr.name, device->dev, sp);
459 }
460 }
461 if (!sp->api) sendit("====\n\n", 6, sp);
462 }
463
list_cloud_transfers(STATUS_PKT * sp,bool verbose)464 static void list_cloud_transfers(STATUS_PKT *sp, bool verbose)
465 {
466 bool first=true;
467 int len;
468 DEVRES *device;
469 POOL_MEM msg(PM_MESSAGE);
470
471 foreach_res(device, R_DEVICE) {
472 if (device->dev && device->dev->is_cloud()) {
473
474 if (first) {
475 if (!sp->api) {
476 len = Mmsg(msg, _("Cloud transfer status:\n"));
477 sendit(msg, len, sp);
478 }
479 first = false;
480 }
481
482 cloud_dev *cdev = (cloud_dev*)device->dev;
483 len = cdev->get_cloud_upload_transfer_status(msg, verbose);
484 sendit(msg, len, sp);
485 len = cdev->get_cloud_download_transfer_status(msg, verbose);
486 sendit(msg, len, sp);
487 break; /* only once, transfer mgr are shared */
488 }
489 }
490
491 if (!first && !sp->api) sendit("====\n\n", 6, sp);
492 }
493
api_list_sd_status_header(STATUS_PKT * sp)494 static void api_list_sd_status_header(STATUS_PKT *sp)
495 {
496 char *p;
497 alist drivers(10, not_owned_by_alist);
498 OutputWriter wt(sp->api_opts);
499
500 sd_list_loaded_drivers(&drivers);
501 wt.start_group("header");
502 wt.get_output(
503 OT_STRING, "name", my_name,
504 OT_STRING, "version", VERSION " (" BDATE ")",
505 OT_STRING, "uname", HOST_OS " " DISTNAME " " DISTVER,
506 OT_UTIME, "started", daemon_start_time,
507 OT_INT64, "pid", (int64_t)getpid(),
508 OT_INT, "jobs_run", num_jobs_run,
509 OT_INT, "jobs_running",job_count(),
510 OT_INT, "ndevices", ((rblist *)res_head[R_DEVICE-r_first]->res_list)->size(),
511 OT_INT, "nautochgr", ((rblist *)res_head[R_AUTOCHANGER-r_first]->res_list)->size(),
512 OT_PLUGINS,"plugins", b_plugin_list,
513 OT_ALIST_STR, "drivers", &drivers,
514 OT_END);
515 p = wt.end_group();
516 sendit(p, strlen(p), sp);
517 }
518
list_status_header(STATUS_PKT * sp)519 static void list_status_header(STATUS_PKT *sp)
520 {
521 char dt[MAX_TIME_LENGTH];
522 char b1[35], b2[35], b3[35], b4[35], b5[35];
523 POOL_MEM msg(PM_MESSAGE);
524 int len;
525
526 if (sp->api) {
527 api_list_sd_status_header(sp);
528 return;
529 }
530
531 len = Mmsg(msg, _("%s %sVersion: %s (%s) %s %s %s\n"),
532 my_name, "", VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
533 sendit(msg, len, sp);
534
535 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
536
537
538 len = Mmsg(msg, _("Daemon started %s. Jobs: run=%d, running=%d.\n"),
539 dt, num_jobs_run, job_count());
540 sendit(msg, len, sp);
541 len = Mmsg(msg, _(" Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
542 edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
543 edit_uint64_with_commas(sm_bytes, b2),
544 edit_uint64_with_commas(sm_max_bytes, b3),
545 edit_uint64_with_commas(sm_buffers, b4),
546 edit_uint64_with_commas(sm_max_buffers, b5));
547 sendit(msg, len, sp);
548 len = Mmsg(msg, " Sizes: boffset_t=%d size_t=%d int32_t=%d int64_t=%d "
549 "mode=%d,%d newbsr=%d\n",
550 (int)sizeof(boffset_t), (int)sizeof(size_t), (int)sizeof(int32_t),
551 (int)sizeof(int64_t), (int)DEVELOPER_MODE, 0, use_new_match_all);
552 sendit(msg, len, sp);
553 len = Mmsg(msg, _(" Res: ndevices=%d nautochgr=%d\n"),
554 ((rblist *)res_head[R_DEVICE-r_first]->res_list)->size(),
555 ((rblist *)res_head[R_AUTOCHANGER-r_first]->res_list)->size());
556 sendit(msg, len, sp);
557 list_plugins(sp);
558 }
559
send_blocked_status(DEVICE * dev,STATUS_PKT * sp)560 static void send_blocked_status(DEVICE *dev, STATUS_PKT *sp)
561 {
562 POOL_MEM msg(PM_MESSAGE);
563 int len;
564
565 if (!dev) {
566 len = Mmsg(msg, _("No DEVICE structure.\n\n"));
567 sendit(msg, len, sp);
568 return;
569 }
570 if (!dev->enabled) {
571 len = Mmsg(msg, _(" Device is disabled. User command.\n"));
572 sendit(msg, len, sp);
573 }
574 switch (dev->blocked()) {
575 case BST_UNMOUNTED:
576 len = Mmsg(msg, _(" Device is BLOCKED. User unmounted.\n"));
577 sendit(msg, len, sp);
578 break;
579 case BST_UNMOUNTED_WAITING_FOR_SYSOP:
580 len = Mmsg(msg, _(" Device is BLOCKED. User unmounted during wait for media/mount.\n"));
581 sendit(msg, len, sp);
582 break;
583 case BST_WAITING_FOR_SYSOP:
584 {
585 DCR *dcr;
586 bool found_jcr = false;
587 dev->Lock();
588 dev->Lock_dcrs();
589 foreach_dlist(dcr, dev->attached_dcrs) {
590 if (dcr->jcr->JobStatus == JS_WaitMount) {
591 len = Mmsg(msg, _(" Device is BLOCKED waiting for mount of volume \"%s\",\n"
592 " Pool: %s\n"
593 " Media type: %s\n"),
594 dcr->VolumeName,
595 dcr->pool_name,
596 dcr->media_type);
597 sendit(msg, len, sp);
598 found_jcr = true;
599 } else if (dcr->jcr->JobStatus == JS_WaitMedia) {
600 len = Mmsg(msg, _(" Device is BLOCKED waiting to create a volume for:\n"
601 " Pool: %s\n"
602 " Media type: %s\n"),
603 dcr->pool_name,
604 dcr->media_type);
605 sendit(msg, len, sp);
606 found_jcr = true;
607 }
608 }
609 dev->Unlock_dcrs();
610 dev->Unlock();
611 if (!found_jcr) {
612 len = Mmsg(msg, _(" Device is BLOCKED waiting for media.\n"));
613 sendit(msg, len, sp);
614 }
615 }
616 break;
617 case BST_DOING_ACQUIRE:
618 len = Mmsg(msg, _(" Device is being initialized.\n"));
619 sendit(msg, len, sp);
620 break;
621 case BST_WRITING_LABEL:
622 len = Mmsg(msg, _(" Device is blocked labeling a Volume.\n"));
623 sendit(msg, len, sp);
624 break;
625 default:
626 break;
627 }
628 /* Send autochanger slot status */
629 if (dev->is_autochanger()) {
630 if (dev->get_slot() > 0) {
631 len = Mmsg(msg, _(" Slot %d %s loaded in drive %d.\n"),
632 dev->get_slot(), dev->is_open()?"is": "was last", dev->drive_index);
633 sendit(msg, len, sp);
634 } else if (dev->get_slot() <= 0) {
635 len = Mmsg(msg, _(" Drive %d is not loaded.\n"), dev->drive_index);
636 sendit(msg, len, sp);
637 }
638 }
639 if (chk_dbglvl(1)) {
640 send_device_status(dev, sp);
641 }
642 }
643
send_device_status(DEVICE * dev,STATUS_PKT * sp)644 void send_device_status(DEVICE *dev, STATUS_PKT *sp)
645 {
646 POOL_MEM msg(PM_MESSAGE);
647 int len;
648 DCR *dcr = NULL;
649 bool found = false;
650 char b1[35];
651
652
653 if (chk_dbglvl(5)) {
654 len = Mmsg(msg, _("Configured device capabilities:\n"));
655 sendit(msg, len, sp);
656 len = Mmsg(msg, " %sEOF %sBSR %sBSF %sFSR %sFSF %sEOM %sREM %sRACCESS %sAUTOMOUNT %sLABEL %sANONVOLS %sALWAYSOPEN %sSYNCONCLOSE\n",
657 dev->capabilities & CAP_EOF ? "" : "!",
658 dev->capabilities & CAP_BSR ? "" : "!",
659 dev->capabilities & CAP_BSF ? "" : "!",
660 dev->capabilities & CAP_FSR ? "" : "!",
661 dev->capabilities & CAP_FSF ? "" : "!",
662 dev->capabilities & CAP_EOM ? "" : "!",
663 dev->capabilities & CAP_REM ? "" : "!",
664 dev->capabilities & CAP_RACCESS ? "" : "!",
665 dev->capabilities & CAP_AUTOMOUNT ? "" : "!",
666 dev->capabilities & CAP_LABEL ? "" : "!",
667 dev->capabilities & CAP_ANONVOLS ? "" : "!",
668 dev->capabilities & CAP_ALWAYSOPEN ? "" : "!",
669 dev->capabilities & CAP_SYNCONCLOSE ? "" : "!");
670 sendit(msg, len, sp);
671 }
672
673 len = Mmsg(msg, _("Device state:\n"));
674 sendit(msg, len, sp);
675 len = Mmsg(msg, " %sOPENED %sTAPE %sLABEL %sAPPEND %sREAD %sEOT %sWEOT %sEOF %sWORM %sNEXTVOL %sSHORT %sMOUNTED %sMALLOC\n",
676 dev->is_open() ? "" : "!",
677 dev->is_tape() ? "" : "!",
678 dev->is_labeled() ? "" : "!",
679 dev->can_append() ? "" : "!",
680 dev->can_read() ? "" : "!",
681 dev->at_eot() ? "" : "!",
682 dev->state & ST_WEOT ? "" : "!",
683 dev->at_eof() ? "" : "!",
684 dev->is_worm() ? "" : "!",
685 dev->state & ST_NEXTVOL ? "" : "!",
686 dev->state & ST_SHORT ? "" : "!",
687 dev->state & ST_MOUNTED ? "" : "!",
688 dev->state & ST_MALLOC ? "" : "!");
689 sendit(msg, len, sp);
690
691 len = Mmsg(msg, _(" Writers=%d reserves=%d blocked=%d enabled=%d usage=%s\n"), dev->num_writers,
692 dev->num_reserved(), dev->blocked(), dev->enabled,
693 edit_uint64_with_commas(dev->usage, b1));
694
695 sendit(msg, len, sp);
696
697 len = Mmsg(msg, _("Attached JobIds: "));
698 sendit(msg, len, sp);
699 dev->Lock();
700 dev->Lock_dcrs();
701 foreach_dlist(dcr, dev->attached_dcrs) {
702 if (dcr->jcr) {
703 if (found) {
704 sendit(",", 1, sp);
705 }
706 len = Mmsg(msg, "%d", (int)dcr->jcr->JobId);
707 sendit(msg, len, sp);
708 found = true;
709 }
710 }
711 dev->Unlock_dcrs();
712 dev->Unlock();
713 sendit("\n", 1, sp);
714
715 len = Mmsg(msg, _("Device parameters:\n"));
716 sendit(msg, len, sp);
717 len = Mmsg(msg, _(" Archive name: %s Device name: %s\n"), dev->archive_name(),
718 dev->name());
719 sendit(msg, len, sp);
720 len = Mmsg(msg, _(" File=%u block=%u\n"), dev->file, dev->block_num);
721 sendit(msg, len, sp);
722 len = Mmsg(msg, _(" Min block=%u Max block=%u\n"), dev->min_block_size, dev->max_block_size);
723 sendit(msg, len, sp);
724 }
725
api_list_running_jobs(STATUS_PKT * sp)726 static void api_list_running_jobs(STATUS_PKT *sp)
727 {
728 char *p1, *p2, *p3;
729 int i1, i2, i3;
730 OutputWriter ow(sp->api_opts);
731
732 uint64_t inst_bps, total_bps;
733 int inst_sec, total_sec;
734 JCR *jcr;
735 DCR *dcr, *rdcr;
736 time_t now = time(NULL);
737
738 foreach_jcr(jcr) {
739 if (jcr->getJobType() == JT_SYSTEM) {
740 continue;
741 }
742 ow.get_output(OT_CLEAR,
743 OT_START_OBJ,
744 OT_INT32, "jobid", jcr->JobId,
745 OT_STRING, "job", jcr->Job,
746 OT_JOBLEVEL,"level", jcr->getJobLevel(),
747 OT_JOBTYPE, "type", jcr->getJobType(),
748 OT_JOBSTATUS,"status", jcr->JobStatus,
749 OT_PINT64, "jobbytes", jcr->JobBytes,
750 OT_INT32, "jobfiles", jcr->JobFiles,
751 OT_UTIME, "starttime", jcr->start_time,
752 OT_INT32, "errors", jcr->JobErrors,
753 OT_INT32, "newbsr", (int32_t)jcr->use_new_match_all,
754 OT_END);
755
756 dcr = jcr->dcr;
757 rdcr = jcr->read_dcr;
758
759 p1 = p2 = p3 = NULL;
760 if (rdcr && rdcr->device) {
761 p1 = rdcr->VolumeName;
762 p2 = rdcr->pool_name;
763 p3 = rdcr->device->hdr.name;
764 }
765 ow.get_output(OT_STRING, "read_volume", NPRTB(p1),
766 OT_STRING, "read_pool", NPRTB(p2),
767 OT_STRING, "read_device", NPRTB(p3),
768 OT_END);
769
770 p1 = p2 = p3 = NULL;
771 i1 = i2 = i3 = 0;
772 if (dcr && dcr->device) {
773 p1 = dcr->VolumeName;
774 p2 = dcr->pool_name;
775 p3 = dcr->device->hdr.name;
776 i1 = dcr->spooling;
777 i2 = dcr->despooling;
778 i3 = dcr->despool_wait;
779 }
780
781 ow.get_output(OT_STRING, "write_volume", NPRTB(p1),
782 OT_STRING, "write_pool", NPRTB(p2),
783 OT_STRING, "write_device", NPRTB(p3),
784 OT_INT, "spooling", i1,
785 OT_INT, "despooling", i2,
786 OT_INT, "despool_wait", i3,
787 OT_END);
788
789 if (jcr->last_time == 0) {
790 jcr->last_time = jcr->run_time;
791 }
792
793 total_sec = now - jcr->run_time;
794 inst_sec = now - jcr->last_time;
795
796 if (total_sec <= 0) {
797 total_sec = 1;
798 }
799 if (inst_sec <= 0) {
800 inst_sec = 1;
801 }
802
803 /* Instanteous bps not smoothed */
804 inst_bps = (jcr->JobBytes - jcr->LastJobBytes) / inst_sec;
805 if (jcr->LastRate == 0) {
806 jcr->LastRate = inst_bps;
807 }
808
809 /* Smooth the instantaneous bps a bit */
810 inst_bps = (2 * jcr->LastRate + inst_bps) / 3;
811 /* total bps (AveBytes/sec) since start of job */
812 total_bps = jcr->JobBytes / total_sec;
813
814 p1 = ow.get_output(OT_PINT64, "avebytes_sec", total_bps,
815 OT_PINT64, "lastbytes_sec", inst_bps,
816 OT_END_OBJ,
817 OT_END);
818
819 sendit(p1, strlen(p1), sp);
820
821 /* Update only every 10 seconds */
822 if (now - jcr->last_time > 10) {
823 jcr->LastRate = inst_bps;
824 jcr->LastJobBytes = jcr->JobBytes;
825 jcr->last_time = now;
826 }
827 }
828 endeach_jcr(jcr);
829
830 }
831
list_running_jobs(STATUS_PKT * sp)832 static void list_running_jobs(STATUS_PKT *sp)
833 {
834 bool found = false;
835 uint64_t inst_bps, total_bps;
836 int inst_sec, total_sec;
837 JCR *jcr;
838 DCR *dcr, *rdcr;
839 char JobName[MAX_NAME_LENGTH];
840 char b1[50], b2[50], b3[50], b4[50];
841 int len;
842 POOL_MEM msg(PM_MESSAGE);
843 time_t now = time(NULL);
844
845 if (sp->api > 1) {
846 api_list_running_jobs(sp);
847 return;
848 }
849
850 len = Mmsg(msg, _("\nRunning Jobs:\n"));
851 if (!sp->api) sendit(msg, len, sp);
852
853 foreach_jcr(jcr) {
854 if (jcr->JobStatus == JS_WaitFD) {
855 len = Mmsg(msg, _("%s Job %s waiting for Client connection.\n"),
856 job_type_to_str(jcr->getJobType()), jcr->Job);
857 sendit(msg, len, sp);
858 }
859 dcr = jcr->dcr;
860 rdcr = jcr->read_dcr;
861 if ((dcr && dcr->device) || (rdcr && rdcr->device)) {
862 bstrncpy(JobName, jcr->Job, sizeof(JobName));
863 /* There are three periods after the Job name */
864 char *p;
865 for (int i=0; i<3; i++) {
866 if ((p=strrchr(JobName, '.')) != NULL) {
867 *p = 0;
868 }
869 }
870 if (rdcr && rdcr->device) {
871 len = Mmsg(msg, _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
872 " pool=\"%s\" device=%s newbsr=%d\n"),
873 job_level_to_str(jcr->getJobLevel()),
874 job_type_to_str(jcr->getJobType()),
875 JobName,
876 jcr->JobId,
877 rdcr->VolumeName,
878 rdcr->pool_name,
879 rdcr->dev?rdcr->dev->print_name():
880 rdcr->device->device_name,
881 jcr->use_new_match_all
882 );
883 sendit(msg, len, sp);
884 } else if (dcr && dcr->device) {
885 len = Mmsg(msg, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
886 " pool=\"%s\" device=%s\n"),
887 job_level_to_str(jcr->getJobLevel()),
888 job_type_to_str(jcr->getJobType()),
889 JobName,
890 jcr->JobId,
891 dcr->VolumeName,
892 dcr->pool_name,
893 dcr->dev?dcr->dev->print_name():
894 dcr->device->device_name);
895 sendit(msg, len, sp);
896 len= Mmsg(msg, _(" spooling=%d despooling=%d despool_wait=%d\n"),
897 dcr->spooling, dcr->despooling, dcr->despool_wait);
898 sendit(msg, len, sp);
899 }
900 if (jcr->last_time == 0) {
901 jcr->last_time = jcr->run_time;
902 }
903 total_sec = now - jcr->run_time;
904 inst_sec = now - jcr->last_time;
905 if (total_sec <= 0) {
906 total_sec = 1;
907 }
908 if (inst_sec <= 0) {
909 inst_sec = 1;
910 }
911 /* Instanteous bps not smoothed */
912 inst_bps = (jcr->JobBytes - jcr->LastJobBytes) / inst_sec;
913 if (jcr->LastRate == 0) {
914 jcr->LastRate = inst_bps;
915 }
916 /* Smooth the instantaneous bps a bit */
917 inst_bps = (2 * jcr->LastRate + inst_bps) / 3;
918 /* total bps (AveBytes/sec) since start of job */
919 total_bps = jcr->JobBytes / total_sec;
920 len = Mmsg(msg, _(" Files=%s Bytes=%s AveBytes/sec=%s LastBytes/sec=%s\n"),
921 edit_uint64_with_commas(jcr->JobFiles, b1),
922 edit_uint64_with_commas(jcr->JobBytes, b2),
923 edit_uint64_with_commas(total_bps, b3),
924 edit_uint64_with_commas(inst_bps, b4));
925 sendit(msg, len, sp);
926 /* Update only every 10 seconds */
927 if (now - jcr->last_time > 10) {
928 jcr->LastRate = inst_bps;
929 jcr->LastJobBytes = jcr->JobBytes;
930 jcr->last_time = now;
931 }
932 found = true;
933 #ifdef DEBUG
934 if (jcr->file_bsock) {
935 len = Mmsg(msg, _(" FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
936 edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
937 jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
938 jcr->file_bsock->m_fd);
939 sendit(msg, len, sp);
940 } else {
941 len = Mmsg(msg, _(" FDSocket closed\n"));
942 sendit(msg, len, sp);
943 }
944 #endif
945 }
946 }
947 endeach_jcr(jcr);
948
949 if (!found) {
950 len = Mmsg(msg, _("No Jobs running.\n"));
951 if (!sp->api) sendit(msg, len, sp);
952 }
953 if (!sp->api) sendit("====\n", 5, sp);
954 }
955
list_jobs_waiting_on_reservation(STATUS_PKT * sp)956 static void list_jobs_waiting_on_reservation(STATUS_PKT *sp)
957 {
958 JCR *jcr;
959 POOL_MEM msg(PM_MESSAGE);
960 int len;
961
962 len = Mmsg(msg, _("\nJobs waiting to reserve a drive:\n"));
963 if (!sp->api) sendit(msg, len, sp);
964
965 foreach_jcr(jcr) {
966 if (!jcr->reserve_msgs) {
967 continue;
968 }
969 send_drive_reserve_messages(jcr, sendit, sp);
970 }
971 endeach_jcr(jcr);
972
973 if (!sp->api) sendit("====\n", 5, sp);
974 }
975
976
sendit(const char * msg,int len,void * sp)977 static void sendit(const char *msg, int len, void *sp)
978 {
979 sendit(msg, len, (STATUS_PKT *)sp);
980 }
981
sendit(POOL_MEM & msg,int len,STATUS_PKT * sp)982 static void sendit(POOL_MEM &msg, int len, STATUS_PKT *sp)
983 {
984 BSOCK *bs = sp->bs;
985 if (bs) {
986 bs->msg = check_pool_memory_size(bs->msg, len+1);
987 memcpy(bs->msg, msg.c_str(), len+1);
988 bs->msglen = len+1;
989 bs->send();
990 } else {
991 sp->callback(msg.c_str(), len, sp->context);
992 }
993 }
994
dbg_sendit(const char * msg,int len,void * sp)995 static void dbg_sendit(const char *msg, int len, void *sp)
996 {
997 if (len > 0) {
998 Dmsg0(-1, msg);
999 }
1000 }
1001
1002 /*
1003 * Status command from Director
1004 */
status_cmd(JCR * jcr)1005 bool status_cmd(JCR *jcr)
1006 {
1007 BSOCK *dir = jcr->dir_bsock;
1008 STATUS_PKT sp;
1009
1010 dir->fsend("\n");
1011 sp.bs = dir;
1012 output_status(&sp);
1013 dir->signal(BNET_EOD);
1014 return true;
1015 }
1016
1017 /*
1018 * .status command from Director
1019 */
qstatus_cmd(JCR * jcr)1020 bool qstatus_cmd(JCR *jcr)
1021 {
1022 BSOCK *dir = jcr->dir_bsock;
1023 JCR *njcr;
1024 s_last_job* job;
1025 STATUS_PKT sp;
1026 POOLMEM *args = get_pool_memory(PM_MESSAGE);
1027 char *argk[MAX_CMD_ARGS]; /* argument keywords */
1028 char *argv[MAX_CMD_ARGS]; /* argument values */
1029 int argc; /* number of arguments */
1030 bool ret=true;
1031 char *cmd;
1032 char *device=NULL;
1033 char *collname=NULL;
1034 int api = true;
1035
1036 sp.bs = dir;
1037
1038 parse_args(dir->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
1039
1040 /* .status xxxx at the minimum */
1041 if (argc < 2 || strcmp(argk[0], ".status") != 0) {
1042 pm_strcpy(jcr->errmsg, dir->msg);
1043 dir->fsend(_("3900 No arg in .status command: %s\n"), jcr->errmsg);
1044 dir->signal(BNET_EOD);
1045 return false;
1046 }
1047
1048 cmd = argk[1];
1049 unbash_spaces(cmd);
1050
1051 /* The status command can contain some arguments
1052 * i=0 => .status
1053 * i=1 => [running | current | last | ... ]
1054 */
1055 for (int i=0 ; i < argc ; i++) {
1056 if (!strcmp(argk[i], "device") && argv[i]) {
1057 device = argv[i];
1058 unbash_spaces(device);
1059
1060 } else if (!strcmp(argk[i], "api") && argv[i]) {
1061 api = atoi(argv[i]);
1062
1063 } else if (!strcmp(argk[i], "statistics") && argv[i]) {
1064 collname = argv[i];
1065 unbash_spaces(collname);
1066
1067 } else if (!strcmp(argk[i], "api_opts") && argv[i]) {
1068 bstrncpy(sp.api_opts, argv[i], sizeof(sp.api_opts));
1069 }
1070 }
1071
1072 Dmsg1(100, "cmd=%s\n", cmd);
1073
1074 if (strcasecmp(cmd, "current") == 0) {
1075 dir->fsend(OKqstatus, cmd);
1076 foreach_jcr(njcr) {
1077 if (njcr->JobId != 0) {
1078 dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
1079 }
1080 }
1081 endeach_jcr(njcr);
1082 } else if (strcasecmp(cmd, "last") == 0) {
1083 dir->fsend(OKqstatus, cmd);
1084 if ((last_jobs) && (last_jobs->size() > 0)) {
1085 job = (s_last_job*)last_jobs->last();
1086 dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
1087 }
1088 } else if (strcasecmp(cmd, "header") == 0) {
1089 sp.api = api;
1090 list_status_header(&sp);
1091 } else if (strcasecmp(cmd, "running") == 0) {
1092 sp.api = api;
1093 list_running_jobs(&sp);
1094 } else if (strcasecmp(cmd, "waitreservation") == 0) {
1095 sp.api = api;
1096 list_jobs_waiting_on_reservation(&sp);
1097 } else if (strcasecmp(cmd, "devices") == 0) {
1098 sp.api = api;
1099 list_devices(&sp, device);
1100 } else if (strcasecmp(cmd, "volumes") == 0) {
1101 sp.api = api;
1102 list_volumes(sendit, &sp);
1103 } else if (strcasecmp(cmd, "spooling") == 0) {
1104 sp.api = api;
1105 list_spool_stats(sendit, &sp);
1106 } else if (strcasecmp(cmd, "terminated") == 0) {
1107 sp.api = api;
1108 list_terminated_jobs(&sp); /* defined in lib/status.h */
1109 } else if (strcasecmp(cmd, "resources") == 0) {
1110 sp.api = api;
1111 list_resources(&sp);
1112 } else if (strcasecmp(cmd, "cloud") == 0) {
1113 list_cloud_transfers(&sp, true);
1114 } else if (strcasecmp(cmd, "statistics") == 0) {
1115 sp.api = api;
1116 list_collectors_status(&sp, collname);
1117 } else {
1118 pm_strcpy(jcr->errmsg, dir->msg);
1119 dir->fsend(_("3900 Unknown arg in .status command: %s\n"), jcr->errmsg);
1120 dir->signal(BNET_EOD);
1121 ret = false;
1122 }
1123 dir->signal(BNET_EOD);
1124 free_pool_memory(args);
1125 return ret;
1126 }
1127
1128 /* List plugins and drivers */
list_plugins(STATUS_PKT * sp)1129 static void list_plugins(STATUS_PKT *sp)
1130 {
1131 POOL_MEM msg(PM_MESSAGE);
1132 alist drivers(10, not_owned_by_alist);
1133 int len;
1134
1135 if (b_plugin_list && b_plugin_list->size() > 0) {
1136 Plugin *plugin;
1137 pm_strcpy(msg, " Plugin: ");
1138 foreach_alist(plugin, b_plugin_list) {
1139 len = pm_strcat(msg, plugin->file);
1140 /* Print plugin version when debug activated */
1141 if (debug_level > 0 && plugin->pinfo) {
1142 pm_strcat(msg, "(");
1143 pm_strcat(msg, NPRT(sdplug_info(plugin)->plugin_version));
1144 len = pm_strcat(msg, ")");
1145 }
1146 if (len > 80) {
1147 pm_strcat(msg, "\n ");
1148 } else {
1149 pm_strcat(msg, " ");
1150 }
1151 }
1152 len = pm_strcat(msg, "\n");
1153 sendit(msg.c_str(), len, sp);
1154 }
1155 sd_list_loaded_drivers(&drivers);
1156 if (drivers.size() > 0) {
1157 char *drv;
1158 pm_strcpy(msg, " Drivers: ");
1159 foreach_alist(drv, (&drivers)) {
1160 len = pm_strcat(msg, drv);
1161 if (len > 80) {
1162 pm_strcat(msg, "\n ");
1163 } else {
1164 pm_strcat(msg, " ");
1165 }
1166 }
1167 len = pm_strcat(msg, "\n");
1168 sendit(msg.c_str(), len, sp);
1169 }
1170 }
1171
list_collectors_status(STATUS_PKT * sp,char * collname)1172 static void list_collectors_status(STATUS_PKT *sp, char *collname)
1173 {
1174 URES *res;
1175 int len;
1176 POOL_MEM buf(PM_MESSAGE);
1177
1178 Dmsg2(200, "enter list_collectors_status() api=%i coll=%s\n", sp->api, NPRTB(collname));
1179 if (sp->api > 1) {
1180 api_collectors_status(sp, collname);
1181 return;
1182 }
1183
1184 LockRes();
1185 foreach_res(res, R_COLLECTOR) {
1186 if (collname && !bstrcmp(collname, res->res_collector.hdr.name)){
1187 continue;
1188 }
1189 Dmsg1(500, "processing: %s\n", res->res_collector.hdr.name);
1190 len = render_collector_status(res->res_collector, buf);
1191 sendit(buf.c_str(), len, sp);
1192 };
1193 UnlockRes();
1194 if (!collname){
1195 len = render_updcollector_status(buf);
1196 sendit(buf.c_str(), len, sp);
1197 }
1198 Dmsg0(200, "leave list_collectors_status()\n");
1199 }
1200
api_collectors_status(STATUS_PKT * sp,char * collname)1201 static void api_collectors_status(STATUS_PKT *sp, char *collname)
1202 {
1203 URES *res;
1204 OutputWriter ow(sp->api_opts);
1205 POOLMEM *buf;
1206
1207 Dmsg1(200, "enter api_collectors_status() %s\n", NPRTB(collname));
1208 ow.start_group("collector_backends");
1209 LockRes();
1210 foreach_res(res, R_COLLECTOR) {
1211 if (collname && !bstrcmp(collname, res->res_collector.hdr.name)){
1212 continue;
1213 }
1214 Dmsg1(500, "processing: %s\n", res->res_collector.hdr.name);
1215 api_render_collector_status(res->res_collector, ow);
1216 };
1217 UnlockRes();
1218 buf = ow.end_group();
1219 if (!collname){
1220 ow.start_group("collector_update");
1221 api_render_updcollector_status(ow);
1222 buf = ow.end_group();
1223 }
1224 sendit(buf, strlen(buf), sp);
1225 Dmsg0(200, "leave api_collectors_status()\n");
1226 };
1227