]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/status.c
Make DEVICE Slot private and access it via a method.
[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->get_slot() > 0) {
313          len = Mmsg(msg, _("    Slot %d is loaded in drive %d.\n"), 
314             dev->get_slot(), dev->drive_index);
315          sendit(msg, len, sp);
316       } else if (dev->get_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 reserved=%d block=%d\n\n"), dev->num_writers, 
371               dev->num_reserved(), dev->blocked());
372    sendit(msg, len, sp);
373
374    len = Mmsg(msg, _("Device parameters:\n"));
375    sendit(msg, len, sp);
376
377    len = Mmsg(msg, _("Archive name: %s Device name: %s\n"), dev->archive_name(),
378       dev->name());
379    sendit(msg, len, sp);
380
381    len = Mmsg(msg, _("File=%u block=%u\n"), dev->file, dev->block_num);
382    sendit(msg, len, sp);
383
384    len = Mmsg(msg, _("Min block=%u Max block=%u\n"), dev->min_block_size, dev->max_block_size);
385    sendit(msg, len, sp);
386 }
387
388 static void list_running_jobs(STATUS_PKT *sp)
389 {
390    bool found = false;
391    int bps, sec;
392    JCR *jcr;
393    DCR *dcr, *rdcr;
394    char JobName[MAX_NAME_LENGTH];
395    char b1[30], b2[30], b3[30];
396    int len;
397    POOL_MEM msg(PM_MESSAGE);
398
399    len = Mmsg(msg, _("\nRunning Jobs:\n"));
400    if (!sp->api) sendit(msg, len, sp);
401
402    foreach_jcr(jcr) {
403       if (jcr->JobStatus == JS_WaitFD) {
404          len = Mmsg(msg, _("%s Job %s waiting for Client connection.\n"),
405             job_type_to_str(jcr->JobType), jcr->Job);
406          sendit(msg, len, sp);
407       }
408       dcr = jcr->dcr;
409       rdcr = jcr->read_dcr;
410       if ((dcr && dcr->device) || rdcr && rdcr->device) {
411          bstrncpy(JobName, jcr->Job, sizeof(JobName));
412          /* There are three periods after the Job name */
413          char *p;
414          for (int i=0; i<3; i++) {
415             if ((p=strrchr(JobName, '.')) != NULL) {
416                *p = 0;
417             }
418          }
419          if (rdcr && rdcr->device) {
420             len = Mmsg(msg, _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
421                             "    pool=\"%s\" device=%s\n"),
422                    job_level_to_str(jcr->JobLevel),
423                    job_type_to_str(jcr->JobType),
424                    JobName,
425                    jcr->JobId,
426                    rdcr->VolumeName,
427                    rdcr->pool_name,
428                    rdcr->dev?rdcr->dev->print_name(): 
429                             rdcr->device->device_name);
430             sendit(msg, len, sp);
431          }
432          if (dcr && dcr->device) {
433             len = Mmsg(msg, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
434                             "    pool=\"%s\" device=%s\n"),
435                    job_level_to_str(jcr->JobLevel),
436                    job_type_to_str(jcr->JobType),
437                    JobName,
438                    jcr->JobId,
439                    dcr->VolumeName,
440                    dcr->pool_name,
441                    dcr->dev?dcr->dev->print_name(): 
442                             dcr->device->device_name);
443             sendit(msg, len, sp);
444             len= Mmsg(msg, _("    spooling=%d despooling=%d despool_wait=%d\n"),
445                    dcr->spooling, dcr->despooling, dcr->despool_wait);
446             sendit(msg, len, sp);
447          }
448          sec = time(NULL) - jcr->run_time;
449          if (sec <= 0) {
450             sec = 1;
451          }
452          bps = jcr->JobBytes / sec;
453          len = Mmsg(msg, _("    Files=%s Bytes=%s Bytes/sec=%s\n"),
454             edit_uint64_with_commas(jcr->JobFiles, b1),
455             edit_uint64_with_commas(jcr->JobBytes, b2),
456             edit_uint64_with_commas(bps, b3));
457          sendit(msg, len, sp);
458          found = true;
459 #ifdef DEBUG
460          if (jcr->file_bsock) {
461             len = Mmsg(msg, _("    FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
462                edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
463                jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
464                jcr->file_bsock->m_fd);
465             sendit(msg, len, sp);
466          } else {
467             len = Mmsg(msg, _("    FDSocket closed\n"));
468             sendit(msg, len, sp);
469          }
470 #endif
471       }
472    }
473    endeach_jcr(jcr);
474
475    if (!found) {
476       len = Mmsg(msg, _("No Jobs running.\n"));
477       if (!sp->api) sendit(msg, len, sp);
478    }
479    if (!sp->api) sendit("====\n", 5, sp);
480 }
481
482 static void list_jobs_waiting_on_reservation(STATUS_PKT *sp)
483
484    JCR *jcr;
485    POOL_MEM msg(PM_MESSAGE);
486    int len;
487
488    len = Mmsg(msg, _("\nJobs waiting to reserve a drive:\n"));
489    if (!sp->api) sendit(msg, len, sp);
490
491    foreach_jcr(jcr) {
492       if (!jcr->reserve_msgs) {
493          continue;
494       }
495       send_drive_reserve_messages(jcr, sendit, sp);
496    }
497    endeach_jcr(jcr);
498
499    if (!sp->api) sendit("====\n", 5, sp);
500 }
501
502
503 static void list_terminated_jobs(STATUS_PKT *sp)
504 {
505    char dt[MAX_TIME_LENGTH], b1[30], b2[30];
506    char level[10];
507    struct s_last_job *je;
508    const char *msg;
509
510    msg =  _("\nTerminated Jobs:\n");
511    if (!sp->api) sendit(msg, strlen(msg), sp);
512    if (last_jobs->size() == 0) {
513       if (!sp->api) sendit("====\n", 5, sp);
514       return;
515    }
516    lock_last_jobs_list();
517    msg =  _(" JobId  Level    Files      Bytes   Status   Finished        Name \n");
518    if (!sp->api) sendit(msg, strlen(msg), sp);
519    msg =  _("===================================================================\n");
520    if (!sp->api) sendit(msg, strlen(msg), sp);
521    foreach_dlist(je, last_jobs) {
522       char JobName[MAX_NAME_LENGTH];
523       const char *termstat;
524       char buf[1000];
525
526       bstrftime_nc(dt, sizeof(dt), je->end_time);
527       switch (je->JobType) {
528       case JT_ADMIN:
529       case JT_RESTORE:
530          bstrncpy(level, "    ", sizeof(level));
531          break;
532       default:
533          bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
534          level[4] = 0;
535          break;
536       }
537       switch (je->JobStatus) {
538       case JS_Created:
539          termstat = _("Created");
540          break;
541       case JS_FatalError:
542       case JS_ErrorTerminated:
543          termstat = _("Error");
544          break;
545       case JS_Differences:
546          termstat = _("Diffs");
547          break;
548       case JS_Canceled:
549          termstat = _("Cancel");
550          break;
551       case JS_Terminated:
552          termstat = _("OK");
553          break;
554       default:
555          termstat = _("Other");
556          break;
557       }
558       bstrncpy(JobName, je->Job, sizeof(JobName));
559       /* There are three periods after the Job name */
560       char *p;
561       for (int i=0; i<3; i++) {
562          if ((p=strrchr(JobName, '.')) != NULL) {
563             *p = 0;
564          }
565       }
566       if (sp->api) {
567          bsnprintf(buf, sizeof(buf), _("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"),
568             je->JobId,
569             level,
570             edit_uint64_with_commas(je->JobFiles, b1),
571             edit_uint64_with_suffix(je->JobBytes, b2),
572             termstat,
573             dt, JobName);
574       } else {
575          bsnprintf(buf, sizeof(buf), _("%6d  %-6s %8s %10s  %-7s  %-8s %s\n"),
576             je->JobId,
577             level,
578             edit_uint64_with_commas(je->JobFiles, b1),
579             edit_uint64_with_suffix(je->JobBytes, b2),
580             termstat,
581             dt, JobName);
582       }  
583       sendit(buf, strlen(buf), sp);
584    }
585    unlock_last_jobs_list();
586    if (!sp->api) sendit("====\n", 5, sp);
587 }
588
589 /*
590  * Convert Job Level into a string
591  */
592 static const char *level_to_str(int level)
593 {
594    const char *str;
595
596    switch (level) {
597    case L_BASE:
598       str = _("Base");
599    case L_FULL:
600       str = _("Full");
601       break;
602    case L_INCREMENTAL:
603       str = _("Incremental");
604       break;
605    case L_DIFFERENTIAL:
606       str = _("Differential");
607       break;
608    case L_SINCE:
609       str = _("Since");
610       break;
611    case L_VERIFY_CATALOG:
612       str = _("Verify Catalog");
613       break;
614    case L_VERIFY_INIT:
615       str = _("Init Catalog");
616       break;
617    case L_VERIFY_VOLUME_TO_CATALOG:
618       str = _("Volume to Catalog");
619       break;
620    case L_VERIFY_DISK_TO_CATALOG:
621       str = _("Disk to Catalog");
622       break;
623    case L_VERIFY_DATA:
624       str = _("Data");
625       break;
626    case L_NONE:
627       str = " ";
628       break;
629    default:
630       str = _("Unknown Job Level");
631       break;
632    }
633    return str;
634 }
635
636 /*
637  * Send to Director
638  */
639 static void sendit(const char *msg, int len, STATUS_PKT *sp)
640 {
641    BSOCK *bs = sp->bs;
642    if (bs) {
643       memcpy(bs->msg, msg, len+1);
644       bs->msglen = len+1;
645       bs->send();
646    } else {
647       sp->callback(msg, len, sp->context);
648    }
649 }
650
651 static void sendit(const char *msg, int len, void *sp)
652 {
653    sendit(msg, len, (STATUS_PKT *)sp);
654 }
655
656 static void sendit(POOL_MEM &msg, int len, STATUS_PKT *sp)
657 {
658    BSOCK *bs = sp->bs;
659    if (bs) {
660       memcpy(bs->msg, msg.c_str(), len+1);
661       bs->msglen = len+1;
662       bs->send();
663    } else {
664       sp->callback(msg.c_str(), len, sp->context);
665    }
666 }
667
668
669 /*
670  * Status command from Director
671  */
672 bool status_cmd(JCR *jcr)
673 {
674    BSOCK *dir = jcr->dir_bsock;
675    STATUS_PKT sp;
676
677    dir->fsend("\n");
678    sp.bs = dir;
679    output_status(&sp);
680    dir->signal(BNET_EOD);
681    return true;
682 }
683
684 /*
685  * .status command from Director
686  */
687 bool qstatus_cmd(JCR *jcr)
688 {
689    BSOCK *dir = jcr->dir_bsock;
690    POOL_MEM cmd;
691    JCR *njcr;
692    s_last_job* job;
693    STATUS_PKT sp;
694
695    sp.bs = dir;
696    if (sscanf(dir->msg, qstatus, cmd.c_str()) != 1) {
697       pm_strcpy(jcr->errmsg, dir->msg);
698       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
699       dir->fsend(_("3900 Bad .status command, missing argument.\n"));
700       dir->signal(BNET_EOD);
701       return false;
702    }
703    unbash_spaces(cmd);
704
705    Dmsg1(200, "cmd=%s\n", cmd.c_str());
706
707    if (strcmp(cmd.c_str(), "current") == 0) {
708       dir->fsend(OKqstatus, cmd.c_str());
709       foreach_jcr(njcr) {
710          if (njcr->JobId != 0) {
711             dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
712          }
713       }
714       endeach_jcr(njcr);
715    } else if (strcmp(cmd.c_str(), "last") == 0) {
716       dir->fsend(OKqstatus, cmd.c_str());
717       if ((last_jobs) && (last_jobs->size() > 0)) {
718          job = (s_last_job*)last_jobs->last();
719          dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
720       }
721    } else if (strcasecmp(cmd.c_str(), "header") == 0) {
722        sp.api = true;
723        list_status_header(&sp);
724    } else if (strcasecmp(cmd.c_str(), "running") == 0) {
725        sp.api = true;
726        list_running_jobs(&sp);
727    } else if (strcasecmp(cmd.c_str(), "waitreservation") == 0) {
728        sp.api = true;
729        list_jobs_waiting_on_reservation(&sp);
730    } else if (strcasecmp(cmd.c_str(), "devices") == 0) {
731        sp.api = true;
732        list_devices(&sp);
733    } else if (strcasecmp(cmd.c_str(), "volumes") == 0) {
734        sp.api = true;
735        list_volumes(sendit, &sp);
736    } else if (strcasecmp(cmd.c_str(), "spooling") == 0) {
737        sp.api = true;
738        list_spool_stats(sendit, &sp);
739    } else if (strcasecmp(cmd.c_str(), "terminated") == 0) {
740        sp.api = true;
741        list_terminated_jobs(&sp);
742    } else {
743       pm_strcpy(jcr->errmsg, dir->msg);
744       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
745       dir->fsend(_("3900 Bad .status command, wrong argument.\n"));
746       dir->signal(BNET_EOD);
747       return false;
748    }
749    dir->signal(BNET_EOD);
750    return true;
751 }
752
753 #if defined(HAVE_WIN32)
754 int bacstat = 0;
755
756 /* Return a one line status for the tray monitor */
757 char *bac_status(char *buf, int buf_len)
758 {
759    JCR *njcr;
760    const char *termstat = _("Bacula Storage: Idle");
761    struct s_last_job *job;
762    int stat = 0;                      /* Idle */
763
764    if (!last_jobs) {
765       goto done;
766    }
767    Dmsg0(1000, "Begin bac_status jcr loop.\n");
768    foreach_jcr(njcr) {
769       if (njcr->JobId != 0) {
770          stat = JS_Running;
771          termstat = _("Bacula Storage: Running");
772          break;
773       }
774    }
775    endeach_jcr(njcr);
776
777    if (stat != 0) {
778       goto done;
779    }
780    if (last_jobs->size() > 0) {
781       job = (struct s_last_job *)last_jobs->last();
782       stat = job->JobStatus;
783       switch (job->JobStatus) {
784       case JS_Canceled:
785          termstat = _("Bacula Storage: Last Job Canceled");
786          break;
787       case JS_ErrorTerminated:
788       case JS_FatalError:
789          termstat = _("Bacula Storage: Last Job Failed");
790          break;
791       default:
792          if (job->Errors) {
793             termstat = _("Bacula Storage: Last Job had Warnings");
794          }
795          break;
796       }
797    }
798    Dmsg0(1000, "End bac_status jcr loop.\n");
799 done:
800    bacstat = stat;
801    if (buf) {
802       bstrncpy(buf, termstat, buf_len);
803    }
804    return buf;
805 }
806
807 #endif /* HAVE_WIN32 */