]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/status.c
Backport from BEE
[bacula/bacula] / bacula / src / stored / status.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2003-2014 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from many
7    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    Bacula® is a registered trademark of Kern Sibbald.
15 */
16 /*
17  *  This file handles the status command
18  *
19  *     Kern Sibbald, May MMIII
20  *
21  *
22  */
23
24 #include "bacula.h"
25 #include "stored.h"
26 #include "lib/status.h"
27
28 /* Imported functions */
29
30 /* Imported variables */
31 extern BSOCK *filed_chan;
32 extern void *start_heap;
33
34 /* Static variables */
35 static char qstatus[] = ".status %127s\n";
36
37 static char OKqstatus[]   = "3000 OK .status\n";
38 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
39
40
41 /* Forward referenced functions */
42 static void sendit(const char *msg, int len, STATUS_PKT *sp);
43 static void sendit(POOL_MEM &msg, int len, STATUS_PKT *sp);
44 static void sendit(const char *msg, int len, void *arg);
45
46 static void send_blocked_status(DEVICE *dev, STATUS_PKT *sp);
47 static void send_device_status(DEVICE *dev, STATUS_PKT *sp);
48 static void list_terminated_jobs(STATUS_PKT *sp);
49 static void list_running_jobs(STATUS_PKT *sp);
50 static void list_jobs_waiting_on_reservation(STATUS_PKT *sp);
51 static void list_status_header(STATUS_PKT *sp);
52 static void list_devices(STATUS_PKT *sp);
53
54 /*
55  * Status command from Director
56  */
57 void output_status(STATUS_PKT *sp)
58 {
59    POOL_MEM msg(PM_MESSAGE);
60    int len;
61
62    list_status_header(sp);
63
64    /*
65     * List running jobs
66     */
67    list_running_jobs(sp);
68
69    /*
70     * List jobs stuck in reservation system
71     */
72    list_jobs_waiting_on_reservation(sp);
73
74    /*
75     * List terminated jobs
76     */
77    list_terminated_jobs(sp);
78
79    /*
80     * List devices
81     */
82    list_devices(sp);
83
84
85    len = Mmsg(msg, _("Used Volume status:\n"));
86    if (!sp->api) sendit(msg, len, sp);
87
88    list_volumes(sendit, (void *)sp);
89    if (!sp->api) sendit("====\n\n", 6, sp);
90
91
92    list_spool_stats(sendit, (void *)sp);
93    if (!sp->api) sendit("====\n\n", 6, sp);
94
95 }
96
97 static void list_resources(STATUS_PKT *sp)
98 {
99 #ifdef when_working
100    POOL_MEM msg(PM_MESSAGE);
101    int len;
102
103    len = Mmsg(msg, _("\nSD Resources:\n"));
104    if (!sp->api) sendit(msg, len, sp);
105    dump_resource(R_DEVICE, resources[R_DEVICE-r_first], sp);
106    if (!sp->api) sendit("====\n\n", 6, sp);
107 #endif
108 }
109
110 #ifdef xxxx
111 static find_device(char *devname)
112 {
113    foreach_res(device, R_DEVICE) {
114       if (strcasecmp(device->hdr.name, devname) == 0) {
115          found = true;
116          break;
117       }
118    }
119    if (!found) {
120       foreach_res(changer, R_AUTOCHANGER) {
121          if (strcasecmp(changer->hdr.name, devname) == 0) {
122             break;
123          }
124       }
125    }
126 }
127 #endif
128
129 static void list_devices(STATUS_PKT *sp)
130 {
131    DEVRES *device;
132    AUTOCHANGER *changer;
133    DEVICE *dev;
134    char b1[35], b2[35], b3[35];
135    POOL_MEM msg(PM_MESSAGE);
136    int len;
137    int bpb;
138
139    len = Mmsg(msg, _("\nDevice status:\n"));
140    if (!sp->api) sendit(msg, len, sp);
141
142    foreach_res(changer, R_AUTOCHANGER) {
143       len = Mmsg(msg, _("Autochanger \"%s\" with devices:\n"),
144          changer->hdr.name);
145       sendit(msg, len, sp);
146
147       foreach_alist(device, changer->device) {
148          if (device->dev) {
149             len = Mmsg(msg, "   %s\n", device->dev->print_name());
150             sendit(msg, len, sp);
151          } else {
152             len = Mmsg(msg, "   %s\n", device->hdr.name);
153             sendit(msg, len, sp);
154          }
155       }
156    }
157
158
159    foreach_res(device, R_DEVICE) {
160       dev = device->dev;
161       if (dev && dev->is_open()) {
162          if (dev->is_labeled()) {
163             len = Mmsg(msg, _("\nDevice %s is %s:\n"
164                               "    Volume:      %s\n"
165                               "    Pool:        %s\n"
166                               "    Media type:  %s\n"),
167                dev->print_name(),
168                dev->blocked()?_("waiting for"):_("mounted with"),
169                dev->VolHdr.VolumeName,
170                dev->pool_name[0]?dev->pool_name:_("*unknown*"),
171                dev->device->media_type);
172             sendit(msg, len, sp);
173          } else {
174             len = Mmsg(msg, _("\nDevice %s open but no Bacula volume is currently mounted.\n"),
175                dev->print_name());
176             sendit(msg, len, sp);
177          }
178          send_blocked_status(dev, sp);
179          if (dev->can_append()) {
180             bpb = dev->VolCatInfo.VolCatBlocks;
181             if (bpb <= 0) {
182                bpb = 1;
183             }
184             bpb = dev->VolCatInfo.VolCatBytes / bpb;
185             len = Mmsg(msg, _("    Total Bytes=%s Blocks=%s Bytes/block=%s\n"),
186                edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, b1),
187                edit_uint64_with_commas(dev->VolCatInfo.VolCatBlocks, b2),
188                edit_uint64_with_commas(bpb, b3));
189             sendit(msg, len, sp);
190          } else {  /* reading */
191             bpb = dev->VolCatInfo.VolCatReads;
192             if (bpb <= 0) {
193                bpb = 1;
194             }
195             if (dev->VolCatInfo.VolCatRBytes > 0) {
196                bpb = dev->VolCatInfo.VolCatRBytes / bpb;
197             } else {
198                bpb = 0;
199             }
200             len = Mmsg(msg, _("    Total Bytes Read=%s Blocks Read=%s Bytes/block=%s\n"),
201                edit_uint64_with_commas(dev->VolCatInfo.VolCatRBytes, b1),
202                edit_uint64_with_commas(dev->VolCatInfo.VolCatReads, b2),
203                edit_uint64_with_commas(bpb, b3));
204             sendit(msg, len, sp);
205          }
206          len = Mmsg(msg, _("    Positioned at File=%s Block=%s\n"),
207             edit_uint64_with_commas(dev->file, b1),
208             edit_uint64_with_commas(dev->block_num, b2));
209          sendit(msg, len, sp);
210
211       } else {
212          if (dev) {
213             len = Mmsg(msg, _("\nDevice %s is not open.\n"), dev->print_name());
214             sendit(msg, len, sp);
215             send_blocked_status(dev, sp);
216         } else {
217             len = Mmsg(msg, _("\nDevice \"%s\" is not open or does not exist.\n"), device->hdr.name);
218             sendit(msg, len, sp);
219          }
220       }
221
222       if (!sp->api) sendit("==\n", 4, sp);
223    }
224    if (!sp->api) sendit("====\n\n", 6, sp);
225 }
226
227 static void list_status_header(STATUS_PKT *sp)
228 {
229    char dt[MAX_TIME_LENGTH];
230    char b1[35], b2[35], b3[35], b4[35], b5[35];
231    POOL_MEM msg(PM_MESSAGE);
232    int len;
233
234    len = Mmsg(msg, _("%s Version: %s (%s) %s %s %s\n"),
235               my_name, VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
236    sendit(msg, len, sp);
237
238    bstrftime_nc(dt, sizeof(dt), daemon_start_time);
239
240
241    len = Mmsg(msg, _("Daemon started %s. Jobs: run=%d, running=%d.\n"),
242         dt, num_jobs_run, job_count());
243    sendit(msg, len, sp);
244    len = Mmsg(msg, _(" Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
245          edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
246          edit_uint64_with_commas(sm_bytes, b2),
247          edit_uint64_with_commas(sm_max_bytes, b3),
248          edit_uint64_with_commas(sm_buffers, b4),
249          edit_uint64_with_commas(sm_max_buffers, b5));
250    sendit(msg, len, sp);
251    len = Mmsg(msg, " Sizes: boffset_t=%d size_t=%d int32_t=%d int64_t=%d "
252               "mode=%d,%d\n",
253               (int)sizeof(boffset_t), (int)sizeof(size_t), (int)sizeof(int32_t),
254               (int)sizeof(int64_t), (int)DEVELOPER_MODE, (int)BEEF);
255    sendit(msg, len, sp);
256    if (bplugin_list->size() > 0) {
257       Plugin *plugin;
258       int len;
259       pm_strcpy(msg, " Plugin: ");
260       foreach_alist(plugin, bplugin_list) {
261          len = pm_strcat(msg, plugin->file);
262          if (len > 80) {
263             pm_strcat(msg, "\n   ");
264          } else {
265             pm_strcat(msg, " ");
266          }
267       }
268       len = pm_strcat(msg, "\n");
269       sendit(msg.c_str(), len, sp);
270    }
271 }
272
273 static void send_blocked_status(DEVICE *dev, STATUS_PKT *sp)
274 {
275    POOL_MEM msg(PM_MESSAGE);
276    int len;
277
278    if (!dev) {
279       len = Mmsg(msg, _("No DEVICE structure.\n\n"));
280       sendit(msg, len, sp);
281       return;
282    }
283    switch (dev->blocked()) {
284    case BST_UNMOUNTED:
285       len = Mmsg(msg, _("    Device is BLOCKED. User unmounted.\n"));
286       sendit(msg, len, sp);
287       break;
288    case BST_UNMOUNTED_WAITING_FOR_SYSOP:
289       len = Mmsg(msg, _("    Device is BLOCKED. User unmounted during wait for media/mount.\n"));
290       sendit(msg, len, sp);
291       break;
292    case BST_WAITING_FOR_SYSOP:
293       {
294          DCR *dcr;
295          bool found_jcr = false;
296          dev->Lock();
297          dev->Lock_dcrs();
298          foreach_dlist(dcr, dev->attached_dcrs) {
299             if (dcr->jcr->JobStatus == JS_WaitMount) {
300                len = Mmsg(msg, _("    Device is BLOCKED waiting for mount of volume \"%s\",\n"
301                                  "       Pool:        %s\n"
302                                  "       Media type:  %s\n"),
303                           dcr->VolumeName,
304                           dcr->pool_name,
305                           dcr->media_type);
306                sendit(msg, len, sp);
307                found_jcr = true;
308             } else if (dcr->jcr->JobStatus == JS_WaitMedia) {
309                len = Mmsg(msg, _("    Device is BLOCKED waiting to create a volume for:\n"
310                                  "       Pool:        %s\n"
311                                  "       Media type:  %s\n"),
312                           dcr->pool_name,
313                           dcr->media_type);
314                sendit(msg, len, sp);
315                found_jcr = true;
316             }
317          }
318          dev->Unlock_dcrs();
319          dev->Unlock();
320          if (!found_jcr) {
321             len = Mmsg(msg, _("    Device is BLOCKED waiting for media.\n"));
322             sendit(msg, len, sp);
323          }
324       }
325       break;
326    case BST_DOING_ACQUIRE:
327       len = Mmsg(msg, _("    Device is being initialized.\n"));
328       sendit(msg, len, sp);
329       break;
330    case BST_WRITING_LABEL:
331       len = Mmsg(msg, _("    Device is blocked labeling a Volume.\n"));
332       sendit(msg, len, sp);
333       break;
334    default:
335       break;
336    }
337    /* Send autochanger slot status */
338    if (dev->is_autochanger()) {
339       if (dev->get_slot() > 0) {
340          len = Mmsg(msg, _("    Slot %d %s loaded in drive %d.\n"),
341             dev->get_slot(), dev->is_open()?"is": "was last", dev->drive_index);
342          sendit(msg, len, sp);
343       } else if (dev->get_slot() <= 0) {
344          len = Mmsg(msg, _("    Drive %d is not loaded.\n"), dev->drive_index);
345          sendit(msg, len, sp);
346       }
347    }
348    if (chk_dbglvl(1)) {
349       send_device_status(dev, sp);
350    }
351 }
352
353 static void send_device_status(DEVICE *dev, STATUS_PKT *sp)
354 {
355    POOL_MEM msg(PM_MESSAGE);
356    int len;
357    DCR *dcr = NULL;
358    bool found = false;
359
360    if (chk_dbglvl(5)) {
361       len = Mmsg(msg, _("Configured device capabilities:\n"));
362       sendit(msg, len, sp);
363       len = Mmsg(msg, "  %sEOF %sBSR %sBSF %sFSR %sFSF %sEOM %sREM %sRACCESS %sAUTOMOUNT %sLABEL %sANONVOLS %sALWAYSOPEN\n",
364          dev->capabilities & CAP_EOF ? "" : "!",
365          dev->capabilities & CAP_BSR ? "" : "!",
366          dev->capabilities & CAP_BSF ? "" : "!",
367          dev->capabilities & CAP_FSR ? "" : "!",
368          dev->capabilities & CAP_FSF ? "" : "!",
369          dev->capabilities & CAP_EOM ? "" : "!",
370          dev->capabilities & CAP_REM ? "" : "!",
371          dev->capabilities & CAP_RACCESS ? "" : "!",
372          dev->capabilities & CAP_AUTOMOUNT ? "" : "!",
373          dev->capabilities & CAP_LABEL ? "" : "!",
374          dev->capabilities & CAP_ANONVOLS ? "" : "!",
375          dev->capabilities & CAP_ALWAYSOPEN ? "" : "!");
376       sendit(msg, len, sp);
377    }
378
379    len = Mmsg(msg, _("Device state:\n"));
380    sendit(msg, len, sp);
381    len = Mmsg(msg, "  %sOPENED %sTAPE %sLABEL %sMALLOC %sAPPEND %sREAD %sEOT %sWEOT %sEOF %sNEXTVOL %sSHORT %sMOUNTED\n",
382       dev->is_open() ? "" : "!",
383       dev->is_tape() ? "" : "!",
384       dev->is_labeled() ? "" : "!",
385       dev->state & ST_MALLOC ? "" : "!",
386       dev->can_append() ? "" : "!",
387       dev->can_read() ? "" : "!",
388       dev->at_eot() ? "" : "!",
389       dev->state & ST_WEOT ? "" : "!",
390       dev->at_eof() ? "" : "!",
391       dev->state & ST_NEXTVOL ? "" : "!",
392       dev->state & ST_SHORT ? "" : "!",
393       dev->state & ST_MOUNTED ? "" : "!");
394    sendit(msg, len, sp);
395    len = Mmsg(msg, _("  num_writers=%d reserves=%d block=%d\n"), dev->num_writers,
396               dev->num_reserved(), dev->blocked());
397    sendit(msg, len, sp);
398
399    len = Mmsg(msg, _("Attached JobsIds: "));
400    sendit(msg, len, sp);
401    dev->Lock();
402    dev->Lock_dcrs();
403    foreach_dlist(dcr, dev->attached_dcrs) {
404       if (dcr->jcr) {
405          if (found) {
406             sendit(",", 1, sp);
407          }
408          len = Mmsg(msg, "%d", (int)dcr->jcr->JobId);
409          sendit(msg, len, sp);
410          found = true;
411       }
412    }
413    dev->Unlock_dcrs();
414    dev->Unlock();
415    sendit("\n", 1, sp);
416
417    len = Mmsg(msg, _("Device parameters:\n"));
418    sendit(msg, len, sp);
419    len = Mmsg(msg, _("  Archive name: %s Device name: %s\n"), dev->archive_name(),
420       dev->name());
421    sendit(msg, len, sp);
422    len = Mmsg(msg, _("  File=%u block=%u\n"), dev->file, dev->block_num);
423    sendit(msg, len, sp);
424    len = Mmsg(msg, _("  Min block=%u Max block=%u\n"), dev->min_block_size, dev->max_block_size);
425    sendit(msg, len, sp);
426 }
427
428 static void list_running_jobs(STATUS_PKT *sp)
429 {
430    bool found = false;
431    int avebps, bps, sec;
432    JCR *jcr;
433    DCR *dcr, *rdcr;
434    char JobName[MAX_NAME_LENGTH];
435    char b1[50], b2[50], b3[50], b4[50];
436    int len;
437    POOL_MEM msg(PM_MESSAGE);
438    time_t now = time(NULL);
439
440    len = Mmsg(msg, _("\nRunning Jobs:\n"));
441    if (!sp->api) sendit(msg, len, sp);
442
443    foreach_jcr(jcr) {
444       if (jcr->JobStatus == JS_WaitFD) {
445          len = Mmsg(msg, _("%s Job %s waiting for Client connection.\n"),
446             job_type_to_str(jcr->getJobType()), jcr->Job);
447          sendit(msg, len, sp);
448       }
449       dcr = jcr->dcr;
450       rdcr = jcr->read_dcr;
451       if ((dcr && dcr->device) || (rdcr && rdcr->device)) {
452          bstrncpy(JobName, jcr->Job, sizeof(JobName));
453          /* There are three periods after the Job name */
454          char *p;
455          for (int i=0; i<3; i++) {
456             if ((p=strrchr(JobName, '.')) != NULL) {
457                *p = 0;
458             }
459          }
460          if (rdcr && rdcr->device) {
461             len = Mmsg(msg, _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
462                             "    pool=\"%s\" device=%s\n"),
463                    job_level_to_str(jcr->getJobLevel()),
464                    job_type_to_str(jcr->getJobType()),
465                    JobName,
466                    jcr->JobId,
467                    rdcr->VolumeName,
468                    rdcr->pool_name,
469                    rdcr->dev?rdcr->dev->print_name():
470                             rdcr->device->device_name);
471             sendit(msg, len, sp);
472          }
473          if (dcr && dcr->device) {
474             len = Mmsg(msg, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
475                             "    pool=\"%s\" device=%s\n"),
476                    job_level_to_str(jcr->getJobLevel()),
477                    job_type_to_str(jcr->getJobType()),
478                    JobName,
479                    jcr->JobId,
480                    dcr->VolumeName,
481                    dcr->pool_name,
482                    dcr->dev?dcr->dev->print_name():
483                             dcr->device->device_name);
484             sendit(msg, len, sp);
485             len= Mmsg(msg, _("    spooling=%d despooling=%d despool_wait=%d\n"),
486                    dcr->spooling, dcr->despooling, dcr->despool_wait);
487             sendit(msg, len, sp);
488          }
489          if (jcr->last_time == 0) {
490             jcr->last_time = jcr->run_time;
491          }
492          sec = now - jcr->last_time;
493          if (sec <= 0) {
494             sec = 1;
495          }
496          bps = (jcr->JobBytes - jcr->LastJobBytes) / sec;
497          if (jcr->LastRate == 0) {
498             jcr->LastRate = bps;
499          }
500          avebps = (jcr->LastRate + bps) / 2;
501          len = Mmsg(msg, _("    Files=%s Bytes=%s AveBytes/sec=%s LastBytes/sec=%s\n"),
502             edit_uint64_with_commas(jcr->JobFiles, b1),
503             edit_uint64_with_commas(jcr->JobBytes, b2),
504             edit_uint64_with_commas(avebps, b3),
505             edit_uint64_with_commas(bps, b4));
506          sendit(msg, len, sp);
507          jcr->LastRate = avebps;
508          jcr->LastJobBytes = jcr->JobBytes;
509          jcr->last_time = now;
510          found = true;
511 #ifdef DEBUG
512          if (jcr->file_bsock) {
513             len = Mmsg(msg, _("    FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
514                edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
515                jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
516                jcr->file_bsock->m_fd);
517             sendit(msg, len, sp);
518          } else {
519             len = Mmsg(msg, _("    FDSocket closed\n"));
520             sendit(msg, len, sp);
521          }
522 #endif
523       }
524    }
525    endeach_jcr(jcr);
526
527    if (!found) {
528       len = Mmsg(msg, _("No Jobs running.\n"));
529       if (!sp->api) sendit(msg, len, sp);
530    }
531    if (!sp->api) sendit("====\n", 5, sp);
532 }
533
534 static void list_jobs_waiting_on_reservation(STATUS_PKT *sp)
535 {
536    JCR *jcr;
537    POOL_MEM msg(PM_MESSAGE);
538    int len;
539
540    len = Mmsg(msg, _("\nJobs waiting to reserve a drive:\n"));
541    if (!sp->api) sendit(msg, len, sp);
542
543    foreach_jcr(jcr) {
544       if (!jcr->reserve_msgs) {
545          continue;
546       }
547       send_drive_reserve_messages(jcr, sendit, sp);
548    }
549    endeach_jcr(jcr);
550
551    if (!sp->api) sendit("====\n", 5, sp);
552 }
553
554 /*
555  * Send to Director
556  */
557 static void sendit(const char *msg, int len, void *sp)
558 {
559    sendit(msg, len, (STATUS_PKT *)sp);
560 }
561
562 static void sendit(POOL_MEM &msg, int len, STATUS_PKT *sp)
563 {
564    BSOCK *bs = sp->bs;
565    if (bs) {
566       memcpy(bs->msg, msg.c_str(), len+1);
567       bs->msglen = len+1;
568       bs->send();
569    } else {
570       sp->callback(msg.c_str(), len, sp->context);
571    }
572 }
573
574
575 /*
576  * Status command from Director
577  */
578 bool status_cmd(JCR *jcr)
579 {
580    BSOCK *dir = jcr->dir_bsock;
581    STATUS_PKT sp;
582
583    dir->fsend("\n");
584    sp.bs = dir;
585    output_status(&sp);
586    dir->signal(BNET_EOD);
587    return true;
588 }
589
590 /*
591  * .status command from Director
592  */
593 bool qstatus_cmd(JCR *jcr)
594 {
595    BSOCK *dir = jcr->dir_bsock;
596    POOL_MEM cmd;
597    JCR *njcr;
598    s_last_job* job;
599    STATUS_PKT sp;
600
601    sp.bs = dir;
602    if (sscanf(dir->msg, qstatus, cmd.c_str()) != 1) {
603       pm_strcpy(jcr->errmsg, dir->msg);
604       dir->fsend(_("3900 No arg in .status command: %s\n"), jcr->errmsg);
605       dir->signal(BNET_EOD);
606       return false;
607    }
608    unbash_spaces(cmd);
609
610    Dmsg1(200, "cmd=%s\n", cmd.c_str());
611
612    if (strcasecmp(cmd.c_str(), "current") == 0) {
613       dir->fsend(OKqstatus, cmd.c_str());
614       foreach_jcr(njcr) {
615          if (njcr->JobId != 0) {
616             dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
617          }
618       }
619       endeach_jcr(njcr);
620    } else if (strcasecmp(cmd.c_str(), "last") == 0) {
621       dir->fsend(OKqstatus, cmd.c_str());
622       if ((last_jobs) && (last_jobs->size() > 0)) {
623          job = (s_last_job*)last_jobs->last();
624          dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
625       }
626    } else if (strcasecmp(cmd.c_str(), "header") == 0) {
627        sp.api = true;
628        list_status_header(&sp);
629    } else if (strcasecmp(cmd.c_str(), "running") == 0) {
630        sp.api = true;
631        list_running_jobs(&sp);
632    } else if (strcasecmp(cmd.c_str(), "waitreservation") == 0) {
633        sp.api = true;
634        list_jobs_waiting_on_reservation(&sp);
635    } else if (strcasecmp(cmd.c_str(), "devices") == 0) {
636        sp.api = true;
637        list_devices(&sp);
638    } else if (strcasecmp(cmd.c_str(), "volumes") == 0) {
639        sp.api = true;
640        list_volumes(sendit, &sp);
641    } else if (strcasecmp(cmd.c_str(), "spooling") == 0) {
642        sp.api = true;
643        list_spool_stats(sendit, &sp);
644    } else if (strcasecmp(cmd.c_str(), "terminated") == 0) {
645        sp.api = true;
646        list_terminated_jobs(&sp);
647    } else if (strcasecmp(cmd.c_str(), "resources") == 0) {
648        sp.api = true;
649        list_resources(&sp);
650    } else {
651       pm_strcpy(jcr->errmsg, dir->msg);
652       dir->fsend(_("3900 Unknown arg in .status command: %s\n"), jcr->errmsg);
653       dir->signal(BNET_EOD);
654       return false;
655    }
656    dir->signal(BNET_EOD);
657    return true;
658 }
659
660 #if defined(HAVE_WIN32)
661 int bacstat = 0;
662
663 /* Return a one line status for the tray monitor */
664 char *bac_status(char *buf, int buf_len)
665 {
666    JCR *njcr;
667    const char *termstat = _("Bacula Storage: Idle");
668    struct s_last_job *job;
669    int stat = 0;                      /* Idle */
670
671    if (!last_jobs) {
672       goto done;
673    }
674    Dmsg0(1000, "Begin bac_status jcr loop.\n");
675    foreach_jcr(njcr) {
676       if (njcr->JobId != 0) {
677          stat = JS_Running;
678          termstat = _("Bacula Storage: Running");
679          break;
680       }
681    }
682    endeach_jcr(njcr);
683
684    if (stat != 0) {
685       goto done;
686    }
687    if (last_jobs->size() > 0) {
688       job = (struct s_last_job *)last_jobs->last();
689       stat = job->JobStatus;
690       switch (job->JobStatus) {
691       case JS_Canceled:
692          termstat = _("Bacula Storage: Last Job Canceled");
693          break;
694       case JS_ErrorTerminated:
695       case JS_FatalError:
696          termstat = _("Bacula Storage: Last Job Failed");
697          break;
698       default:
699          if (job->Errors) {
700             termstat = _("Bacula Storage: Last Job had Warnings");
701          }
702          break;
703       }
704    }
705    Dmsg0(1000, "End bac_status jcr loop.\n");
706 done:
707    bacstat = stat;
708    if (buf) {
709       bstrncpy(buf, termstat, buf_len);
710    }
711    return buf;
712 }
713
714 #endif /* HAVE_WIN32 */