1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2003-2012 Free Software Foundation Europe e.V.
5 Copyright (C) 2011-2012 Planets Communications B.V.
6 Copyright (C) 2013-2019 Bareos GmbH & Co. KG
7
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
11 in the file LICENSE.
12
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Affero General Public License for more details.
17
18 You should have received a copy of the GNU Affero General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 02110-1301, USA.
22 */
23 /*
24 * Kern Sibbald, May MMIII
25 */
26 /**
27 * @file
28 * This file handles the status command
29 */
30
31 #include "include/bareos.h"
32 #include "stored/stored.h"
33 #include "stored/stored_globals.h"
34 #include "stored/jcr_private.h"
35 #include "lib/status.h"
36 #include "stored/spool.h"
37 #include "lib/edit.h"
38 #include "include/jcr.h"
39 #include "lib/parse_conf.h"
40 #include "lib/bsock.h"
41 #include "lib/recent_job_results_list.h"
42 #include "lib/util.h"
43
44 /* Imported functions */
45 extern bool GetWindowsVersionString(char* buf, int maxsiz);
46
47 namespace storagedaemon {
48
49 /* Imported variables */
50 extern void* start_heap;
51
52 /* Static variables */
53 static char statuscmd[] = "status %s\n";
54 static char dotstatuscmd[] = ".status %127s\n";
55
56 static char OKdotstatus[] = "3000 OK .status\n";
57 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
58
59 /* Forward referenced functions */
60 static void sendit(const char* msg, int len, StatusPacket* sp);
61 static void sendit(PoolMem& msg, int len, StatusPacket* sp);
62 static void sendit(const char* msg, int len, void* arg);
63
64 static void SendBlockedStatus(Device* dev, StatusPacket* sp);
65 static void SendDeviceStatus(Device* dev, StatusPacket* sp);
66 static void ListTerminatedJobs(StatusPacket* sp);
67 static void ListRunningJobs(StatusPacket* sp);
68 static void ListJobsWaitingOnReservation(StatusPacket* sp);
69 static void ListStatusHeader(StatusPacket* sp);
70 static void ListDevices(JobControlRecord* jcr,
71 StatusPacket* sp,
72 const char* devicenames);
73 static void ListVolumes(StatusPacket* sp, const char* devicenames);
74
75 static const char* JobLevelToString(int level);
76
77 /**
78 * Status command from Director
79 */
OutputStatus(JobControlRecord * jcr,StatusPacket * sp,const char * devicenames)80 static void OutputStatus(JobControlRecord* jcr,
81 StatusPacket* sp,
82 const char* devicenames)
83 {
84 int len;
85 PoolMem msg(PM_MESSAGE);
86
87 ListStatusHeader(sp);
88
89 /*
90 * List running jobs
91 */
92 ListRunningJobs(sp);
93
94 /*
95 * List jobs stuck in reservation system
96 */
97 ListJobsWaitingOnReservation(sp);
98
99 /*
100 * List terminated jobs
101 */
102 ListTerminatedJobs(sp);
103
104 /*
105 * List devices
106 */
107 ListDevices(jcr, sp, devicenames);
108
109 if (!sp->api) {
110 len = Mmsg(msg, _("Used Volume status:\n"));
111 sendit(msg, len, sp);
112 }
113
114 ListVolumes(sp, devicenames);
115 if (!sp->api) {
116 len = PmStrcpy(msg, "====\n\n");
117 sendit(msg, len, sp);
118 }
119
120 ListSpoolStats(sendit, (void*)sp);
121 if (!sp->api) {
122 len = PmStrcpy(msg, "====\n\n");
123 sendit(msg, len, sp);
124 }
125 }
126
ListResources(StatusPacket * sp)127 static void ListResources(StatusPacket* sp)
128 {
129 /* this has not been implemented */
130 }
131
NeedToListDevice(const char * devicenames,const char * devicename)132 static bool NeedToListDevice(const char* devicenames, const char* devicename)
133 {
134 char *cur, *bp;
135 PoolMem namelist;
136
137 Dmsg2(200, "NeedToListDevice devicenames %s, devicename %s\n", devicenames,
138 devicename);
139
140 /*
141 * Make a local copy that we can split on ','
142 */
143 PmStrcpy(namelist, devicenames);
144
145 /*
146 * See if devicename is in the list.
147 */
148 cur = namelist.c_str();
149 while (cur) {
150 bp = strchr(cur, ',');
151 if (bp) { *bp++ = '\0'; }
152
153 if (Bstrcasecmp(cur, devicename)) { return true; }
154
155 cur = bp;
156 }
157
158 Dmsg0(200, "NeedToListDevice no listing needed\n");
159
160 return false;
161 }
162
NeedToListDevice(const char * devicenames,DeviceResource * device)163 static bool NeedToListDevice(const char* devicenames, DeviceResource* device)
164 {
165 /*
166 * See if we are requested to list an explicit device name.
167 * e.g. this happens when people address one particular device in
168 * a autochanger via its own storage definition or an non autochanger device.
169 */
170 if (!NeedToListDevice(devicenames, device->resource_name_)) {
171 /*
172 * See if this device is part of an autochanger.
173 */
174 if (device->changer_res) {
175 /*
176 * See if we need to list this particular device part of the given
177 * autochanger.
178 */
179 if (!NeedToListDevice(devicenames, device->changer_res->resource_name_)) {
180 return false;
181 }
182 } else {
183 return false;
184 }
185 }
186
187 return true;
188 }
189
190 /*
191 * Trigger the specific eventtype to get status information from any plugin that
192 * registered the event to return specific device information.
193 */
trigger_device_status_hook(JobControlRecord * jcr,DeviceResource * device,StatusPacket * sp,bsdEventType eventType)194 static void trigger_device_status_hook(JobControlRecord* jcr,
195 DeviceResource* device,
196 StatusPacket* sp,
197 bsdEventType eventType)
198 {
199 bsdDevStatTrig dst;
200
201 dst.device = device;
202 dst.status = GetPoolMemory(PM_MESSAGE);
203 dst.status_length = 0;
204
205 if (GeneratePluginEvent(jcr, eventType, &dst) == bRC_OK) {
206 if (dst.status_length > 0) { sendit(dst.status, dst.status_length, sp); }
207 }
208 FreePoolMemory(dst.status);
209 }
210
211 /*
212 * Ask the device if it want to log something specific in the status overview.
213 */
get_device_specific_status(DeviceResource * device,StatusPacket * sp)214 static void get_device_specific_status(DeviceResource* device, StatusPacket* sp)
215 {
216 bsdDevStatTrig dst;
217
218 dst.device = device;
219 dst.status = GetPoolMemory(PM_MESSAGE);
220 dst.status_length = 0;
221
222 if (device && device->dev && device->dev->DeviceStatus(&dst)) {
223 if (dst.status_length > 0) { sendit(dst.status, dst.status_length, sp); }
224 }
225 FreePoolMemory(dst.status);
226 }
227
ListDevices(JobControlRecord * jcr,StatusPacket * sp,const char * devicenames)228 static void ListDevices(JobControlRecord* jcr,
229 StatusPacket* sp,
230 const char* devicenames)
231 {
232 int len;
233 int bpb;
234 Device* dev;
235 DeviceResource* device;
236 AutochangerResource* changer;
237 PoolMem msg(PM_MESSAGE);
238 char b1[35], b2[35], b3[35];
239
240 if (!sp->api) {
241 len = Mmsg(msg, _("\nDevice status:\n"));
242 sendit(msg, len, sp);
243 }
244
245 foreach_res (changer, R_AUTOCHANGER) {
246 /*
247 * See if we need to list this autochanger.
248 */
249 if (devicenames &&
250 !NeedToListDevice(devicenames, changer->resource_name_)) {
251 continue;
252 }
253
254 len = Mmsg(msg, _("Autochanger \"%s\" with devices:\n"),
255 changer->resource_name_);
256 sendit(msg, len, sp);
257
258 foreach_alist (device, changer->device) {
259 if (device->dev) {
260 len = Mmsg(msg, " %s\n", device->dev->print_name());
261 sendit(msg, len, sp);
262 } else {
263 len = Mmsg(msg, " %s\n", device->resource_name_);
264 sendit(msg, len, sp);
265 }
266 }
267 }
268
269 foreach_res (device, R_DEVICE) {
270 if (devicenames && !NeedToListDevice(devicenames, device)) { continue; }
271
272 dev = device->dev;
273 if (dev && dev->IsOpen()) {
274 if (dev->IsLabeled()) {
275 const char* state;
276
277 switch (dev->blocked()) {
278 case BST_NOT_BLOCKED:
279 case BST_DESPOOLING:
280 case BST_RELEASING:
281 state = _("mounted with");
282 break;
283 case BST_MOUNT:
284 state = _("waiting for");
285 break;
286 case BST_WRITING_LABEL:
287 state = _("being labeled with");
288 break;
289 case BST_DOING_ACQUIRE:
290 state = _("being acquired with");
291 break;
292 case BST_UNMOUNTED:
293 case BST_WAITING_FOR_SYSOP:
294 case BST_UNMOUNTED_WAITING_FOR_SYSOP:
295 state = _("waiting for sysop intervention");
296 break;
297 default:
298 state = _("unknown state");
299 break;
300 }
301
302 len = Mmsg(msg,
303 _("\nDevice %s is %s:\n"
304 " Volume: %s\n"
305 " Pool: %s\n"
306 " Media type: %s\n"),
307 dev->print_name(), state, dev->VolHdr.VolumeName,
308 dev->pool_name[0] ? dev->pool_name : "*unknown*",
309 dev->device->media_type);
310 sendit(msg, len, sp);
311 } else {
312 len = Mmsg(
313 msg,
314 _("\nDevice %s open but no Bareos volume is currently mounted.\n"),
315 dev->print_name());
316 sendit(msg, len, sp);
317 }
318
319 get_device_specific_status(device, sp);
320 trigger_device_status_hook(jcr, device, sp, bsdEventDriveStatus);
321
322 SendBlockedStatus(dev, sp);
323
324 if (dev->CanAppend()) {
325 bpb = dev->VolCatInfo.VolCatBlocks;
326 if (bpb <= 0) { bpb = 1; }
327 bpb = dev->VolCatInfo.VolCatBytes / bpb;
328 len = Mmsg(msg, _(" Total Bytes=%s Blocks=%s Bytes/block=%s\n"),
329 edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, b1),
330 edit_uint64_with_commas(dev->VolCatInfo.VolCatBlocks, b2),
331 edit_uint64_with_commas(bpb, b3));
332 sendit(msg, len, sp);
333 } else { /* reading */
334 bpb = dev->VolCatInfo.VolCatReads;
335 if (bpb <= 0) { bpb = 1; }
336 if (dev->VolCatInfo.VolCatRBytes > 0) {
337 bpb = dev->VolCatInfo.VolCatRBytes / bpb;
338 } else {
339 bpb = 0;
340 }
341 len = Mmsg(msg,
342 _(" Total Bytes Read=%s Blocks Read=%s Bytes/block=%s\n"),
343 edit_uint64_with_commas(dev->VolCatInfo.VolCatRBytes, b1),
344 edit_uint64_with_commas(dev->VolCatInfo.VolCatReads, b2),
345 edit_uint64_with_commas(bpb, b3));
346 sendit(msg, len, sp);
347 }
348
349 len = Mmsg(msg, _(" Positioned at File=%s Block=%s\n"),
350 edit_uint64_with_commas(dev->file, b1),
351 edit_uint64_with_commas(dev->block_num, b2));
352 sendit(msg, len, sp);
353
354 trigger_device_status_hook(jcr, device, sp, bsdEventVolumeStatus);
355 } else {
356 if (dev) {
357 len = Mmsg(msg, _("\nDevice %s is not open.\n"), dev->print_name());
358 sendit(msg, len, sp);
359 SendBlockedStatus(dev, sp);
360 } else {
361 len = Mmsg(msg, _("\nDevice \"%s\" is not open or does not exist.\n"),
362 device->resource_name_);
363 sendit(msg, len, sp);
364 }
365
366 get_device_specific_status(device, sp);
367 }
368
369 if (!sp->api) {
370 len = PmStrcpy(msg, "==\n");
371 sendit(msg, len, sp);
372 }
373 }
374
375 if (!sp->api) {
376 len = PmStrcpy(msg, "====\n\n");
377 sendit(msg, len, sp);
378 }
379 }
380
381 /*
382 * List Volumes
383 */
ListVolumes(StatusPacket * sp,const char * devicenames)384 static void ListVolumes(StatusPacket* sp, const char* devicenames)
385 {
386 int len;
387 VolumeReservationItem* vol;
388 PoolMem msg(PM_MESSAGE);
389
390 foreach_vol (vol) {
391 Device* dev = vol->dev;
392
393 if (dev) {
394 if (devicenames && !NeedToListDevice(devicenames, dev->device)) {
395 continue;
396 }
397
398 len = Mmsg(msg, "%s on device %s\n", vol->vol_name, dev->print_name());
399 sendit(msg.c_str(), len, sp);
400 len = Mmsg(msg, " Reader=%d writers=%d reserves=%d volinuse=%d\n",
401 dev->CanRead() ? 1 : 0, dev->num_writers, dev->NumReserved(),
402 vol->IsInUse());
403 sendit(msg.c_str(), len, sp);
404 } else {
405 len = Mmsg(msg, "Volume %s no device. volinuse= %d\n", vol->vol_name,
406 vol->IsInUse());
407 sendit(msg.c_str(), len, sp);
408 }
409 }
410 endeach_vol(vol);
411
412 foreach_read_vol(vol)
413 {
414 Device* dev = vol->dev;
415
416 if (dev) {
417 if (devicenames && !NeedToListDevice(devicenames, dev->device)) {
418 continue;
419 }
420
421 len = Mmsg(msg, "Read volume: %s on device %s\n", vol->vol_name,
422 dev->print_name());
423 sendit(msg.c_str(), len, sp);
424 len = Mmsg(msg,
425 " Reader=%d writers=%d reserves=%d volinuse=%d JobId=%d\n",
426 dev->CanRead() ? 1 : 0, dev->num_writers, dev->NumReserved(),
427 vol->IsInUse(), vol->GetJobid());
428 sendit(msg.c_str(), len, sp);
429 } else {
430 len = Mmsg(msg, "Read Volume: %s no device. volinuse= %d\n",
431 vol->vol_name, vol->IsInUse());
432 sendit(msg.c_str(), len, sp);
433 }
434 }
435 endeach_read_vol(vol);
436 }
437
ListStatusHeader(StatusPacket * sp)438 static void ListStatusHeader(StatusPacket* sp)
439 {
440 int len;
441 PoolMem msg(PM_MESSAGE);
442 char dt[MAX_TIME_LENGTH];
443 char b1[35];
444 #if defined(HAVE_WIN32)
445 char buf[300];
446 #endif
447
448 len = Mmsg(msg, _("%s Version: %s (%s) %s %s %s\n"), my_name,
449 kBareosVersionStrings.Full, kBareosVersionStrings.Date, HOST_OS,
450 DISTNAME, DISTVER);
451 sendit(msg, len, sp);
452
453 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
454
455 len = Mmsg(msg, _("Daemon started %s. Jobs: run=%d, running=%d, %s binary\n"),
456 dt, num_jobs_run, JobCount(), kBareosVersionStrings.BinaryInfo);
457 sendit(msg, len, sp);
458
459 #if defined(HAVE_WIN32)
460 if (GetWindowsVersionString(buf, sizeof(buf))) {
461 len = Mmsg(msg, "%s\n", buf);
462 sendit(msg.c_str(), len, sp);
463 }
464
465 if (debug_level > 0) {
466 len =
467 Mmsg(msg, "APIs=%sOPT,%sATP,%sLPV,%sCFA,%sCFW,\n",
468 p_OpenProcessToken ? "" : "!", p_AdjustTokenPrivileges ? "" : "!",
469 p_LookupPrivilegeValue ? "" : "!", p_CreateFileA ? "" : "!",
470 p_CreateFileW ? "" : "!");
471 sendit(msg.c_str(), len, sp);
472 len = Mmsg(msg,
473 " %sWUL,%sWMKD,%sGFAA,%sGFAW,%sGFAEA,%sGFAEW,%sSFAA,%sSFAW,%sBR,"
474 "%sBW,%sSPSP,\n",
475 p_wunlink ? "" : "!", p_wmkdir ? "" : "!",
476 p_GetFileAttributesA ? "" : "!", p_GetFileAttributesW ? "" : "!",
477 p_GetFileAttributesExA ? "" : "!",
478 p_GetFileAttributesExW ? "" : "!",
479 p_SetFileAttributesA ? "" : "!", p_SetFileAttributesW ? "" : "!",
480 p_BackupRead ? "" : "!", p_BackupWrite ? "" : "!",
481 p_SetProcessShutdownParameters ? "" : "!");
482 sendit(msg.c_str(), len, sp);
483 len = Mmsg(
484 msg, " %sWC2MB,%sMB2WC,%sFFFA,%sFFFW,%sFNFA,%sFNFW,%sSCDA,%sSCDW,\n",
485 p_WideCharToMultiByte ? "" : "!", p_MultiByteToWideChar ? "" : "!",
486 p_FindFirstFileA ? "" : "!", p_FindFirstFileW ? "" : "!",
487 p_FindNextFileA ? "" : "!", p_FindNextFileW ? "" : "!",
488 p_SetCurrentDirectoryA ? "" : "!", p_SetCurrentDirectoryW ? "" : "!");
489 sendit(msg.c_str(), len, sp);
490 len =
491 Mmsg(msg, " %sGCDA,%sGCDW,%sGVPNW,%sGVNFVMPW\n",
492 p_GetCurrentDirectoryA ? "" : "!",
493 p_GetCurrentDirectoryW ? "" : "!", p_GetVolumePathNameW ? "" : "!",
494 p_GetVolumeNameForVolumeMountPointW ? "" : "!");
495 sendit(msg.c_str(), len, sp);
496 }
497 #endif
498
499 len = Mmsg(msg,
500 " Sizes: boffset_t=%d size_t=%d int32_t=%d int64_t=%d "
501 "bwlimit=%skB/s\n",
502 (int)sizeof(boffset_t), (int)sizeof(size_t), (int)sizeof(int32_t),
503 (int)sizeof(int64_t),
504 edit_uint64_with_commas(me->max_bandwidth_per_job / 1024, b1));
505 sendit(msg, len, sp);
506
507
508 if (me->secure_erase_cmdline) {
509 len =
510 Mmsg(msg, _(" secure erase command='%s'\n"), me->secure_erase_cmdline);
511 sendit(msg, len, sp);
512 }
513
514 len = ListSdPlugins(msg);
515 if (len > 0) { sendit(msg.c_str(), len, sp); }
516 }
517
SendBlockedStatus(Device * dev,StatusPacket * sp)518 static void SendBlockedStatus(Device* dev, StatusPacket* sp)
519 {
520 int len;
521 PoolMem msg(PM_MESSAGE);
522
523 if (!dev) {
524 len = Mmsg(msg, _("No Device structure.\n\n"));
525 sendit(msg, len, sp);
526 return;
527 }
528 switch (dev->blocked()) {
529 case BST_UNMOUNTED:
530 len = Mmsg(msg, _(" Device is BLOCKED. User unmounted.\n"));
531 sendit(msg, len, sp);
532 break;
533 case BST_UNMOUNTED_WAITING_FOR_SYSOP:
534 len = Mmsg(msg, _(" Device is BLOCKED. User unmounted during wait for "
535 "media/mount.\n"));
536 sendit(msg, len, sp);
537 break;
538 case BST_WAITING_FOR_SYSOP: {
539 DeviceControlRecord* dcr;
540 bool found_jcr = false;
541 dev->Lock();
542 foreach_dlist (dcr, dev->attached_dcrs) {
543 if (dcr->jcr->JobStatus == JS_WaitMount) {
544 len = Mmsg(
545 msg,
546 _(" Device is BLOCKED waiting for mount of volume \"%s\",\n"
547 " Pool: %s\n"
548 " Media type: %s\n"),
549 dcr->VolumeName, dcr->pool_name, dcr->media_type);
550 sendit(msg, len, sp);
551 found_jcr = true;
552 } else if (dcr->jcr->JobStatus == JS_WaitMedia) {
553 len = Mmsg(msg,
554 _(" Device is BLOCKED waiting to create a volume for:\n"
555 " Pool: %s\n"
556 " Media type: %s\n"),
557 dcr->pool_name, dcr->media_type);
558 sendit(msg, len, sp);
559 found_jcr = true;
560 }
561 }
562 dev->Unlock();
563 if (!found_jcr) {
564 len = Mmsg(msg, _(" Device is BLOCKED waiting for media.\n"));
565 sendit(msg, len, sp);
566 }
567 } break;
568 case BST_DOING_ACQUIRE:
569 len = Mmsg(msg, _(" Device is being initialized.\n"));
570 sendit(msg, len, sp);
571 break;
572 case BST_WRITING_LABEL:
573 len = Mmsg(msg, _(" Device is blocked labeling a Volume.\n"));
574 sendit(msg, len, sp);
575 break;
576 default:
577 break;
578 }
579
580 /*
581 * Send autochanger slot status
582 */
583 if (dev->IsAutochanger()) {
584 if (dev->GetSlot() > 0) {
585 len = Mmsg(msg, _(" Slot %hd %s loaded in drive %hd.\n"),
586 dev->GetSlot(), dev->IsOpen() ? "is" : "was last", dev->drive);
587 sendit(msg, len, sp);
588 } else if (dev->GetSlot() <= 0) {
589 len = Mmsg(msg, _(" Drive %hd is not loaded.\n"), dev->drive);
590 sendit(msg, len, sp);
591 }
592 }
593 if (debug_level > 1) { SendDeviceStatus(dev, sp); }
594 }
595
SendDeviceStatus(Device * dev,StatusPacket * sp)596 static void SendDeviceStatus(Device* dev, StatusPacket* sp)
597 {
598 int len;
599 DeviceControlRecord* dcr = NULL;
600 bool found = false;
601 PoolMem msg(PM_MESSAGE);
602
603 if (debug_level > 5) {
604 len = Mmsg(msg, _("Configured device capabilities:\n"));
605 sendit(msg, len, sp);
606
607 len = Mmsg(
608 msg,
609 " %sEOF %sBSR %sBSF %sFSR %sFSF %sEOM %sREM %sRACCESS %sAUTOMOUNT "
610 "%sLABEL %sANONVOLS %sALWAYSOPEN\n",
611 dev->HasCap(CAP_EOF) ? "" : "!", dev->HasCap(CAP_BSR) ? "" : "!",
612 dev->HasCap(CAP_BSF) ? "" : "!", dev->HasCap(CAP_FSR) ? "" : "!",
613 dev->HasCap(CAP_FSF) ? "" : "!", dev->HasCap(CAP_EOM) ? "" : "!",
614 dev->HasCap(CAP_REM) ? "" : "!", dev->HasCap(CAP_RACCESS) ? "" : "!",
615 dev->HasCap(CAP_AUTOMOUNT) ? "" : "!",
616 dev->HasCap(CAP_LABEL) ? "" : "!", dev->HasCap(CAP_ANONVOLS) ? "" : "!",
617 dev->HasCap(CAP_ALWAYSOPEN) ? "" : "!");
618 sendit(msg, len, sp);
619 }
620
621 len = Mmsg(msg, _("Device state:\n"));
622 sendit(msg, len, sp);
623
624 len = Mmsg(
625 msg,
626 " %sOPENED %sTAPE %sLABEL %sMALLOC %sAPPEND %sREAD %sEOT %sWEOT %sEOF "
627 "%sNEXTVOL %sSHORT %sMOUNTED\n",
628 dev->IsOpen() ? "" : "!", dev->IsTape() ? "" : "!",
629 dev->IsLabeled() ? "" : "!", BitIsSet(ST_MALLOC, dev->state) ? "" : "!",
630 dev->CanAppend() ? "" : "!", dev->CanRead() ? "" : "!",
631 dev->AtEot() ? "" : "!", BitIsSet(ST_WEOT, dev->state) ? "" : "!",
632 dev->AtEof() ? "" : "!", BitIsSet(ST_NEXTVOL, dev->state) ? "" : "!",
633 BitIsSet(ST_SHORT, dev->state) ? "" : "!",
634 BitIsSet(ST_MOUNTED, dev->state) ? "" : "!");
635 sendit(msg, len, sp);
636
637 len = Mmsg(msg, _(" num_writers=%d reserves=%d block=%d\n"),
638 dev->num_writers, dev->NumReserved(), dev->blocked());
639 sendit(msg, len, sp);
640
641 len = Mmsg(msg, _("Attached Jobs: "));
642 sendit(msg, len, sp);
643 dev->Lock();
644 foreach_dlist (dcr, dev->attached_dcrs) {
645 if (dcr->jcr) {
646 if (found) {
647 len = Mmsg(msg, ",%d", (int)dcr->jcr->JobId);
648 } else {
649 len = Mmsg(msg, "%d", (int)dcr->jcr->JobId);
650 }
651 sendit(msg, len, sp);
652 found = true;
653 }
654 }
655 dev->Unlock();
656 sendit("\n", 1, sp);
657
658 len = Mmsg(msg, _("Device parameters:\n"));
659 sendit(msg, len, sp);
660 len = Mmsg(msg, _(" Archive name: %s Device name: %s\n"),
661 dev->archive_name(), dev->name());
662 sendit(msg, len, sp);
663 len = Mmsg(msg, _(" File=%u block=%u\n"), dev->file, dev->block_num);
664 sendit(msg, len, sp);
665 len = Mmsg(msg, _(" Min block=%u Max block=%u\n"), dev->min_block_size,
666 dev->max_block_size);
667 sendit(msg, len, sp);
668 }
669
ListRunningJobs(StatusPacket * sp)670 static void ListRunningJobs(StatusPacket* sp)
671 {
672 JobControlRecord* jcr;
673 DeviceControlRecord *dcr, *rdcr;
674 bool found = false;
675 time_t now = time(NULL);
676 PoolMem msg(PM_MESSAGE);
677 int len, avebps, bps, sec;
678 char JobName[MAX_NAME_LENGTH];
679 char b1[50], b2[50], b3[50], b4[50];
680
681 if (!sp->api) {
682 len = Mmsg(msg, _("\nRunning Jobs:\n"));
683 sendit(msg, len, sp);
684 }
685
686 foreach_jcr (jcr) {
687 if (jcr->JobStatus == JS_WaitFD) {
688 len = Mmsg(msg, _("%s Job %s waiting for Client connection.\n"),
689 job_type_to_str(jcr->getJobType()), jcr->Job);
690 sendit(msg, len, sp);
691 }
692 dcr = jcr->impl->dcr;
693 rdcr = jcr->impl->read_dcr;
694 if ((dcr && dcr->device) || (rdcr && rdcr->device)) {
695 bstrncpy(JobName, jcr->Job, sizeof(JobName));
696 /* There are three periods after the Job name */
697 char* p;
698 for (int i = 0; i < 3; i++) {
699 if ((p = strrchr(JobName, '.')) != NULL) { *p = 0; }
700 }
701 if (rdcr && rdcr->device) {
702 len = Mmsg(
703 msg,
704 _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
705 " pool=\"%s\" device=%s\n"),
706 job_level_to_str(jcr->getJobLevel()),
707 job_type_to_str(jcr->getJobType()), JobName, jcr->JobId,
708 rdcr->VolumeName, rdcr->pool_name,
709 rdcr->dev ? rdcr->dev->print_name() : rdcr->device->device_name);
710 sendit(msg, len, sp);
711 }
712 if (dcr && dcr->device) {
713 len =
714 Mmsg(msg,
715 _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
716 " pool=\"%s\" device=%s\n"),
717 job_level_to_str(jcr->getJobLevel()),
718 job_type_to_str(jcr->getJobType()), JobName, jcr->JobId,
719 dcr->VolumeName, dcr->pool_name,
720 dcr->dev ? dcr->dev->print_name() : dcr->device->device_name);
721 sendit(msg, len, sp);
722 len = Mmsg(msg, _(" spooling=%d despooling=%d despool_wait=%d\n"),
723 dcr->spooling, dcr->despooling, dcr->despool_wait);
724 sendit(msg, len, sp);
725 }
726 if (jcr->last_time == 0) { jcr->last_time = jcr->run_time; }
727 sec = now - jcr->last_time;
728 if (sec <= 0) { sec = 1; }
729 bps = (jcr->JobBytes - jcr->LastJobBytes) / sec;
730 if (jcr->LastRate == 0) { jcr->LastRate = bps; }
731 avebps = (jcr->LastRate + bps) / 2;
732 len = Mmsg(msg,
733 _(" Files=%s Bytes=%s AveBytes/sec=%s LastBytes/sec=%s\n"),
734 edit_uint64_with_commas(jcr->JobFiles, b1),
735 edit_uint64_with_commas(jcr->JobBytes, b2),
736 edit_uint64_with_commas(avebps, b3),
737 edit_uint64_with_commas(bps, b4));
738 sendit(msg, len, sp);
739 jcr->LastRate = avebps;
740 jcr->LastJobBytes = jcr->JobBytes;
741 jcr->last_time = now;
742 found = true;
743 if (jcr->file_bsock) {
744 len = Mmsg(msg, _(" FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
745 edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
746 jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
747 jcr->file_bsock->fd_);
748 sendit(msg, len, sp);
749 } else {
750 len = Mmsg(msg, _(" FDSocket closed\n"));
751 sendit(msg, len, sp);
752 }
753 }
754 }
755 endeach_jcr(jcr);
756
757 if (!found) {
758 if (!sp->api) {
759 len = Mmsg(msg, _("No Jobs running.\n"));
760 sendit(msg, len, sp);
761 }
762 }
763
764 if (!sp->api) {
765 len = PmStrcpy(msg, "====\n");
766 sendit(msg, len, sp);
767 }
768 }
769
770 /*
771 * Send any reservation messages queued for this jcr
772 */
SendDriveReserveMessages(JobControlRecord * jcr,StatusPacket * sp)773 static inline void SendDriveReserveMessages(JobControlRecord* jcr,
774 StatusPacket* sp)
775 {
776 int i;
777 alist* msgs;
778 char* msg;
779
780 jcr->lock();
781 msgs = jcr->impl->reserve_msgs;
782 if (!msgs || msgs->size() == 0) { goto bail_out; }
783 for (i = msgs->size() - 1; i >= 0; i--) {
784 msg = (char*)msgs->get(i);
785 if (msg) {
786 sendit(" ", 3, sp);
787 sendit(msg, strlen(msg), sp);
788 } else {
789 break;
790 }
791 }
792
793 bail_out:
794 jcr->unlock();
795 }
796
ListJobsWaitingOnReservation(StatusPacket * sp)797 static void ListJobsWaitingOnReservation(StatusPacket* sp)
798 {
799 int len;
800 JobControlRecord* jcr;
801 PoolMem msg(PM_MESSAGE);
802
803 if (!sp->api) {
804 len = Mmsg(msg, _("\nJobs waiting to reserve a drive:\n"));
805 sendit(msg, len, sp);
806 }
807
808 foreach_jcr (jcr) {
809 if (!jcr->impl->reserve_msgs) { continue; }
810 SendDriveReserveMessages(jcr, sp);
811 }
812 endeach_jcr(jcr);
813
814 if (!sp->api) {
815 len = PmStrcpy(msg, "====\n");
816 sendit(msg, len, sp);
817 }
818 }
819
ListTerminatedJobs(StatusPacket * sp)820 static void ListTerminatedJobs(StatusPacket* sp)
821 {
822 int len;
823 char level[10];
824 PoolMem msg(PM_MESSAGE);
825 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
826
827 if (!sp->api) {
828 len = PmStrcpy(msg, _("\nTerminated Jobs:\n"));
829 sendit(msg, len, sp);
830 }
831
832 if (RecentJobResultsList::IsEmpty()) {
833 if (!sp->api) {
834 len = PmStrcpy(msg, "====\n");
835 sendit(msg, len, sp);
836 }
837 return;
838 }
839
840 if (!sp->api) {
841 len = PmStrcpy(msg, _(" JobId Level Files Bytes Status "
842 "Finished Name \n"));
843 sendit(msg, len, sp);
844 len = PmStrcpy(msg, _("===================================================="
845 "===============\n"));
846 sendit(msg, len, sp);
847 }
848
849 for (const RecentJobResultsList::JobResult& je :
850 RecentJobResultsList::Get()) {
851 char JobName[MAX_NAME_LENGTH];
852 const char* termstat;
853
854 bstrftime_nc(dt, sizeof(dt), je.end_time);
855 switch (je.JobType) {
856 case JT_ADMIN:
857 case JT_RESTORE:
858 bstrncpy(level, " ", sizeof(level));
859 break;
860 default:
861 bstrncpy(level, JobLevelToString(je.JobLevel), sizeof(level));
862 level[4] = 0;
863 break;
864 }
865 switch (je.JobStatus) {
866 case JS_Created:
867 termstat = _("Created");
868 break;
869 case JS_FatalError:
870 case JS_ErrorTerminated:
871 termstat = _("Error");
872 break;
873 case JS_Differences:
874 termstat = _("Diffs");
875 break;
876 case JS_Canceled:
877 termstat = _("Cancel");
878 break;
879 case JS_Terminated:
880 termstat = _("OK");
881 break;
882 case JS_Warnings:
883 termstat = _("OK -- with warnings");
884 break;
885 default:
886 termstat = _("Other");
887 break;
888 }
889 bstrncpy(JobName, je.Job, sizeof(JobName));
890 /* There are three periods after the Job name */
891 char* p;
892 for (int i = 0; i < 3; i++) {
893 if ((p = strrchr(JobName, '.')) != NULL) { *p = 0; }
894 }
895 if (sp->api) {
896 len =
897 Mmsg(msg, _("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"), je.JobId,
898 level, edit_uint64_with_commas(je.JobFiles, b1),
899 edit_uint64_with_suffix(je.JobBytes, b2), termstat, dt, JobName);
900 } else {
901 len =
902 Mmsg(msg, _("%6d %-6s %8s %10s %-7s %-8s %s\n"), je.JobId, level,
903 edit_uint64_with_commas(je.JobFiles, b1),
904 edit_uint64_with_suffix(je.JobBytes, b2), termstat, dt, JobName);
905 }
906 sendit(msg, len, sp);
907 }
908
909 if (!sp->api) {
910 len = PmStrcpy(msg, "====\n");
911 sendit(msg, len, sp);
912 }
913 }
914
915 /**
916 * Convert Job Level into a string
917 */
JobLevelToString(int level)918 static const char* JobLevelToString(int level)
919 {
920 const char* str;
921
922 switch (level) {
923 case L_BASE:
924 str = _("Base");
925 break;
926 case L_FULL:
927 str = _("Full");
928 break;
929 case L_INCREMENTAL:
930 str = _("Incremental");
931 break;
932 case L_DIFFERENTIAL:
933 str = _("Differential");
934 break;
935 case L_SINCE:
936 str = _("Since");
937 break;
938 case L_VERIFY_CATALOG:
939 str = _("Verify Catalog");
940 break;
941 case L_VERIFY_INIT:
942 str = _("Init Catalog");
943 break;
944 case L_VERIFY_VOLUME_TO_CATALOG:
945 str = _("Volume to Catalog");
946 break;
947 case L_VERIFY_DISK_TO_CATALOG:
948 str = _("Disk to Catalog");
949 break;
950 case L_VERIFY_DATA:
951 str = _("Data");
952 break;
953 case L_NONE:
954 str = " ";
955 break;
956 default:
957 str = _("Unknown Job Level");
958 break;
959 }
960 return str;
961 }
962
963 /**
964 * Send to Director
965 */
sendit(const char * msg,int len,StatusPacket * sp)966 static void sendit(const char* msg, int len, StatusPacket* sp)
967 {
968 BareosSocket* bs = sp->bs;
969
970 if (bs) {
971 memcpy(bs->msg, msg, len + 1);
972 bs->message_length = len + 1;
973 bs->send();
974 } else {
975 sp->callback(msg, len, sp->context);
976 }
977 }
978
sendit(const char * msg,int len,void * sp)979 static void sendit(const char* msg, int len, void* sp)
980 {
981 sendit(msg, len, (StatusPacket*)sp);
982 }
983
sendit(PoolMem & msg,int len,StatusPacket * sp)984 static void sendit(PoolMem& msg, int len, StatusPacket* sp)
985 {
986 BareosSocket* bs = sp->bs;
987
988 if (bs) {
989 memcpy(bs->msg, msg.c_str(), len + 1);
990 bs->message_length = len + 1;
991 bs->send();
992 } else {
993 sp->callback(msg.c_str(), len, sp->context);
994 }
995 }
996
997 /**
998 * Status command from Director
999 */
StatusCmd(JobControlRecord * jcr)1000 bool StatusCmd(JobControlRecord* jcr)
1001 {
1002 PoolMem devicenames;
1003 StatusPacket sp;
1004 BareosSocket* dir = jcr->dir_bsock;
1005
1006 sp.bs = dir;
1007 devicenames.check_size(dir->message_length);
1008 if (sscanf(dir->msg, statuscmd, devicenames.c_str()) != 1) {
1009 PmStrcpy(jcr->errmsg, dir->msg);
1010 dir->fsend(_("3900 No arg in status command: %s\n"), jcr->errmsg);
1011 dir->signal(BNET_EOD);
1012
1013 return false;
1014 }
1015 UnbashSpaces(devicenames);
1016
1017 dir->fsend("\n");
1018 OutputStatus(jcr, &sp, devicenames.c_str());
1019 dir->signal(BNET_EOD);
1020
1021 return true;
1022 }
1023
1024 /**
1025 * .status command from Director
1026 */
DotstatusCmd(JobControlRecord * jcr)1027 bool DotstatusCmd(JobControlRecord* jcr)
1028 {
1029 JobControlRecord* njcr;
1030 PoolMem cmd;
1031 StatusPacket sp;
1032 BareosSocket* dir = jcr->dir_bsock;
1033
1034 sp.bs = dir;
1035 cmd.check_size(dir->message_length);
1036 if (sscanf(dir->msg, dotstatuscmd, cmd.c_str()) != 1) {
1037 PmStrcpy(jcr->errmsg, dir->msg);
1038 dir->fsend(_("3900 No arg in .status command: %s\n"), jcr->errmsg);
1039 dir->signal(BNET_EOD);
1040
1041 return false;
1042 }
1043 UnbashSpaces(cmd);
1044
1045 Dmsg1(200, "cmd=%s\n", cmd.c_str());
1046
1047 if (Bstrcasecmp(cmd.c_str(), "current")) {
1048 dir->fsend(OKdotstatus, cmd.c_str());
1049 foreach_jcr (njcr) {
1050 if (njcr->JobId != 0) {
1051 dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
1052 }
1053 }
1054 endeach_jcr(njcr);
1055 } else if (Bstrcasecmp(cmd.c_str(), "last")) {
1056 dir->fsend(OKdotstatus, cmd.c_str());
1057 if (RecentJobResultsList::Count() > 0) {
1058 RecentJobResultsList::JobResult job =
1059 RecentJobResultsList::GetMostRecentJobResult();
1060 dir->fsend(DotStatusJob, job.JobId, job.JobStatus, job.Errors);
1061 }
1062 } else if (Bstrcasecmp(cmd.c_str(), "header")) {
1063 sp.api = true;
1064 ListStatusHeader(&sp);
1065 } else if (Bstrcasecmp(cmd.c_str(), "running")) {
1066 sp.api = true;
1067 ListRunningJobs(&sp);
1068 } else if (Bstrcasecmp(cmd.c_str(), "waitreservation")) {
1069 sp.api = true;
1070 ListJobsWaitingOnReservation(&sp);
1071 } else if (Bstrcasecmp(cmd.c_str(), "devices")) {
1072 sp.api = true;
1073 ListDevices(jcr, &sp, NULL);
1074 } else if (Bstrcasecmp(cmd.c_str(), "volumes")) {
1075 sp.api = true;
1076 ListVolumes(&sp, NULL);
1077 } else if (Bstrcasecmp(cmd.c_str(), "spooling")) {
1078 sp.api = true;
1079 ListSpoolStats(sendit, &sp);
1080 } else if (Bstrcasecmp(cmd.c_str(), "terminated")) {
1081 sp.api = true;
1082 ListTerminatedJobs(&sp);
1083 } else if (Bstrcasecmp(cmd.c_str(), "resources")) {
1084 sp.api = true;
1085 ListResources(&sp);
1086 } else {
1087 PmStrcpy(jcr->errmsg, dir->msg);
1088 dir->fsend(_("3900 Unknown arg in .status command: %s\n"), jcr->errmsg);
1089 dir->signal(BNET_EOD);
1090 return false;
1091 }
1092 dir->signal(BNET_EOD);
1093
1094 return true;
1095 }
1096
1097 } /* namespace storagedaemon */
1098