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