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