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