]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/status.c
Recompile configure
[bacula/bacula] / bacula / src / stored / status.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2003-2010 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 three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    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  *
34  */
35
36 #include "bacula.h"
37 #include "stored.h"
38 #include "lib/status.h"
39
40 /* Exported variables */
41
42 /* Imported variables */
43 extern BSOCK *filed_chan;
44 extern void *start_heap;
45
46 /* Static variables */
47 static char qstatus[] = ".status %127s\n";
48
49 static char OKqstatus[]   = "3000 OK .status\n";
50 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
51
52
53 /* Forward referenced functions */
54 static void sendit(const char *msg, int len, STATUS_PKT *sp);
55 static void sendit(POOL_MEM &msg, int len, STATUS_PKT *sp);
56 static void sendit(const char *msg, int len, void *arg);
57
58 static void send_blocked_status(DEVICE *dev, STATUS_PKT *sp);
59 static void send_device_status(DEVICE *dev, STATUS_PKT *sp);
60 static void list_terminated_jobs(STATUS_PKT *sp);
61 static void list_running_jobs(STATUS_PKT *sp);
62 static void list_jobs_waiting_on_reservation(STATUS_PKT *sp);
63 static void list_status_header(STATUS_PKT *sp);
64 static void list_devices(STATUS_PKT *sp);
65
66 static const char *level_to_str(int level);
67
68 /*
69  * Status command from Director
70  */
71 void output_status(STATUS_PKT *sp)
72 {
73    POOL_MEM msg(PM_MESSAGE);
74    int len;
75
76    list_status_header(sp);
77
78    /*
79     * List running jobs
80     */
81    list_running_jobs(sp);
82
83    /*
84     * List jobs stuck in reservation system
85     */
86    list_jobs_waiting_on_reservation(sp);
87
88    /*
89     * List terminated jobs
90     */
91    list_terminated_jobs(sp);
92
93    /*
94     * List devices
95     */
96    list_devices(sp);
97
98
99    len = Mmsg(msg, _("Used Volume status:\n"));
100    if (!sp->api) sendit(msg, len, sp);
101
102    list_volumes(sendit, (void *)sp);
103    if (!sp->api) sendit("====\n\n", 6, sp);
104
105 #ifdef xxx
106    if (debug_level > 10) {
107       bs->fsend(_("====\n\n"));
108       dump_resource(R_DEVICE, resources[R_DEVICE-r_first].res_head, sendit, user);
109       bs->fsend(_("====\n\n"));
110    }
111 #endif
112
113    list_spool_stats(sendit, (void *)sp);
114    if (!sp->api) sendit("====\n\n", 6, sp);
115 }
116
117
118 static void list_devices(STATUS_PKT *sp)
119 {
120    DEVRES *device;
121    AUTOCHANGER *changer;
122    DEVICE *dev;
123    char b1[35], b2[35], b3[35];
124    POOL_MEM msg(PM_MESSAGE);
125    int len;
126    int bpb;
127
128    len = Mmsg(msg, _("\nDevice status:\n"));
129    if (!sp->api) sendit(msg, len, sp);
130
131    foreach_res(changer, R_AUTOCHANGER) {
132       len = Mmsg(msg, _("Autochanger \"%s\" with devices:\n"),
133          changer->hdr.name);
134       sendit(msg, len, sp);
135
136       foreach_alist(device, changer->device) {
137          if (device->dev) {
138             len = Mmsg(msg, "   %s\n", device->dev->print_name());
139             sendit(msg, len, sp);
140          } else {
141             len = Mmsg(msg, "   %s\n", device->hdr.name);
142             sendit(msg, len, sp);
143          }
144       }
145    }
146    foreach_res(device, R_DEVICE) {
147       dev = device->dev;
148       if (dev && dev->is_open()) {
149          if (dev->is_labeled()) {
150             len = Mmsg(msg, _("Device %s is mounted with:\n"
151                               "    Volume:      %s\n"
152                               "    Pool:        %s\n"
153                               "    Media type:  %s\n"),
154                dev->print_name(), 
155                dev->VolHdr.VolumeName, 
156                dev->pool_name[0]?dev->pool_name:"*unknown*",
157                dev->device->media_type);
158             sendit(msg, len, sp);
159          } else {
160             len = Mmsg(msg, _("Device %s open but no Bacula volume is currently mounted.\n"), 
161                dev->print_name());
162             sendit(msg, len, sp);
163          }
164          send_blocked_status(dev, sp);
165          if (dev->can_append()) {
166             bpb = dev->VolCatInfo.VolCatBlocks;
167             if (bpb <= 0) {
168                bpb = 1;
169             }
170             bpb = dev->VolCatInfo.VolCatBytes / bpb;
171             len = Mmsg(msg, _("    Total Bytes=%s Blocks=%s Bytes/block=%s\n"),
172                edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, b1),
173                edit_uint64_with_commas(dev->VolCatInfo.VolCatBlocks, b2),
174                edit_uint64_with_commas(bpb, b3));
175             sendit(msg, len, sp);
176          } else {  /* reading */
177             bpb = dev->VolCatInfo.VolCatReads;
178             if (bpb <= 0) {
179                bpb = 1;
180             }
181             if (dev->VolCatInfo.VolCatRBytes > 0) {
182                bpb = dev->VolCatInfo.VolCatRBytes / bpb;
183             } else {
184                bpb = 0;
185             }
186             len = Mmsg(msg, _("    Total Bytes Read=%s Blocks Read=%s Bytes/block=%s\n"),
187                edit_uint64_with_commas(dev->VolCatInfo.VolCatRBytes, b1),
188                edit_uint64_with_commas(dev->VolCatInfo.VolCatReads, b2),
189                edit_uint64_with_commas(bpb, b3));
190             sendit(msg, len, sp);
191          }
192          len = Mmsg(msg, _("    Positioned at File=%s Block=%s\n"),
193             edit_uint64_with_commas(dev->file, b1),
194             edit_uint64_with_commas(dev->block_num, b2));
195          sendit(msg, len, sp);
196
197       } else {
198          if (dev) {
199             len = Mmsg(msg, _("Device %s is not open.\n"), dev->print_name());
200             sendit(msg, len, sp);
201             send_blocked_status(dev, sp);
202         } else {
203             len = Mmsg(msg, _("Device \"%s\" is not open or does not exist.\n"), device->hdr.name);
204             sendit(msg, len, sp);
205          }
206       }
207    }
208    if (!sp->api) sendit("====\n\n", 6, sp);
209 }
210
211 static void list_status_header(STATUS_PKT *sp)
212 {
213    char dt[MAX_TIME_LENGTH];
214    char b1[35], b2[35], b3[35], b4[35], b5[35];
215    POOL_MEM msg(PM_MESSAGE);
216    int len;
217
218    len = Mmsg(msg, _("%s Version: %s (%s) %s %s %s\n"), 
219               my_name, VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
220    sendit(msg, len, sp);
221
222    bstrftime_nc(dt, sizeof(dt), daemon_start_time);
223
224
225    len = Mmsg(msg, _("Daemon started %s. Jobs: run=%d, running=%d.\n"),
226         dt, num_jobs_run, job_count());
227    sendit(msg, len, sp);
228    len = Mmsg(msg, _(" Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
229          edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
230          edit_uint64_with_commas(sm_bytes, b2),
231          edit_uint64_with_commas(sm_max_bytes, b3),
232          edit_uint64_with_commas(sm_buffers, b4),
233          edit_uint64_with_commas(sm_max_buffers, b5));
234    sendit(msg, len, sp);
235    len = Mmsg(msg, " Sizes: boffset_t=%d size_t=%d int32_t=%d int64_t=%d "
236               "mode=%d,%d\n", 
237               (int)sizeof(boffset_t), (int)sizeof(size_t), (int)sizeof(int32_t),
238               (int)sizeof(int64_t), (int)DEVELOPER_MODE, (int)BEEF);
239    sendit(msg, len, sp);
240 }
241
242 static void send_blocked_status(DEVICE *dev, STATUS_PKT *sp)
243 {
244    POOL_MEM msg(PM_MESSAGE);
245    int len;
246
247    if (!dev) {
248       len = Mmsg(msg, _("No DEVICE structure.\n\n"));
249       sendit(msg, len, sp);
250       return;
251    }
252    switch (dev->blocked()) {
253    case BST_UNMOUNTED:
254       len = Mmsg(msg, _("    Device is BLOCKED. User unmounted.\n"));
255       sendit(msg, len, sp);
256       break;
257    case BST_UNMOUNTED_WAITING_FOR_SYSOP:
258       len = Mmsg(msg, _("    Device is BLOCKED. User unmounted during wait for media/mount.\n"));
259       sendit(msg, len, sp);
260       break;
261    case BST_WAITING_FOR_SYSOP:
262       {
263          dlist *dcrs = dev->attached_dcrs;
264          bool found_jcr = false;
265
266          if (dcrs != NULL) {
267             DCR *dcr;
268             for (dcr = (DCR *)dcrs->first(); dcr != NULL; dcr = (DCR *)dcrs->next(dcr)) {
269                if (dcr->jcr->JobStatus == JS_WaitMount) {
270                   len = Mmsg(msg, _("    Device is BLOCKED waiting for mount of volume \"%s\",\n"
271                                     "       Pool:        %s\n"
272                                     "       Media type:  %s\n"),
273                              dcr->VolumeName,
274                              dcr->pool_name,
275                              dcr->media_type);
276                   sendit(msg, len, sp);
277                   found_jcr = true;
278                } else if (dcr->jcr->JobStatus == JS_WaitMedia) {
279                   len = Mmsg(msg, _("    Device is BLOCKED waiting to create a volume for:\n"
280                                     "       Pool:        %s\n"
281                                     "       Media type:  %s\n"),
282                              dcr->pool_name,
283                              dcr->media_type);
284                   sendit(msg, len, sp);
285                   found_jcr = true;
286                }
287             }
288          }
289
290          if (!found_jcr) {
291             len = Mmsg(msg, _("    Device is BLOCKED waiting for media.\n"));
292             sendit(msg, len, sp);
293          }
294       }
295       break;
296    case BST_DOING_ACQUIRE:
297       len = Mmsg(msg, _("    Device is being initialized.\n"));
298       sendit(msg, len, sp);
299       break;
300    case BST_WRITING_LABEL:
301       len = Mmsg(msg, _("    Device is blocked labeling a Volume.\n"));
302       sendit(msg, len, sp);
303       break;
304    default:
305       break;
306    }
307    /* Send autochanger slot status */
308    if (dev->is_autochanger()) {
309       if (dev->get_slot() > 0) {
310          len = Mmsg(msg, _("    Slot %d is loaded in drive %d.\n"), 
311             dev->get_slot(), dev->drive_index);
312          sendit(msg, len, sp);
313       } else if (dev->get_slot() == 0) {
314          len = Mmsg(msg, _("    Drive %d is not loaded.\n"), dev->drive_index);
315          sendit(msg, len, sp);
316       } else {
317          len = Mmsg(msg, _("    Drive %d status unknown.\n"), dev->drive_index);
318          sendit(msg, len, sp);
319       }
320    }
321    if (debug_level > 1) {
322       send_device_status(dev, sp);
323    }
324 }
325
326 static void send_device_status(DEVICE *dev, STATUS_PKT *sp)
327 {
328    POOL_MEM msg(PM_MESSAGE);
329    int len;
330    char *locked;
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->getJobType()), 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->getJobLevel()),
421                    job_type_to_str(jcr->getJobType()),
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->getJobLevel()),
434                    job_type_to_str(jcr->getJobType()),
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       case JS_Warnings:
553          termstat = _("OK -- with warnings");
554          break;
555       default:
556          termstat = _("Other");
557          break;
558       }
559       bstrncpy(JobName, je->Job, sizeof(JobName));
560       /* There are three periods after the Job name */
561       char *p;
562       for (int i=0; i<3; i++) {
563          if ((p=strrchr(JobName, '.')) != NULL) {
564             *p = 0;
565          }
566       }
567       if (sp->api) {
568          bsnprintf(buf, sizeof(buf), _("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"),
569             je->JobId,
570             level,
571             edit_uint64_with_commas(je->JobFiles, b1),
572             edit_uint64_with_suffix(je->JobBytes, b2),
573             termstat,
574             dt, JobName);
575       } else {
576          bsnprintf(buf, sizeof(buf), _("%6d  %-6s %8s %10s  %-7s  %-8s %s\n"),
577             je->JobId,
578             level,
579             edit_uint64_with_commas(je->JobFiles, b1),
580             edit_uint64_with_suffix(je->JobBytes, b2),
581             termstat,
582             dt, JobName);
583       }  
584       sendit(buf, strlen(buf), sp);
585    }
586    unlock_last_jobs_list();
587    if (!sp->api) sendit("====\n", 5, sp);
588 }
589
590 /*
591  * Convert Job Level into a string
592  */
593 static const char *level_to_str(int level)
594 {
595    const char *str;
596
597    switch (level) {
598    case L_BASE:
599       str = _("Base");
600    case L_FULL:
601       str = _("Full");
602       break;
603    case L_INCREMENTAL:
604       str = _("Incremental");
605       break;
606    case L_DIFFERENTIAL:
607       str = _("Differential");
608       break;
609    case L_SINCE:
610       str = _("Since");
611       break;
612    case L_VERIFY_CATALOG:
613       str = _("Verify Catalog");
614       break;
615    case L_VERIFY_INIT:
616       str = _("Init Catalog");
617       break;
618    case L_VERIFY_VOLUME_TO_CATALOG:
619       str = _("Volume to Catalog");
620       break;
621    case L_VERIFY_DISK_TO_CATALOG:
622       str = _("Disk to Catalog");
623       break;
624    case L_VERIFY_DATA:
625       str = _("Data");
626       break;
627    case L_NONE:
628       str = " ";
629       break;
630    default:
631       str = _("Unknown Job Level");
632       break;
633    }
634    return str;
635 }
636
637 /*
638  * Send to Director
639  */
640 static void sendit(const char *msg, int len, STATUS_PKT *sp)
641 {
642    BSOCK *bs = sp->bs;
643    if (bs) {
644       memcpy(bs->msg, msg, len+1);
645       bs->msglen = len+1;
646       bs->send();
647    } else {
648       sp->callback(msg, len, sp->context);
649    }
650 }
651
652 static void sendit(const char *msg, int len, void *sp)
653 {
654    sendit(msg, len, (STATUS_PKT *)sp);
655 }
656
657 static void sendit(POOL_MEM &msg, int len, STATUS_PKT *sp)
658 {
659    BSOCK *bs = sp->bs;
660    if (bs) {
661       memcpy(bs->msg, msg.c_str(), len+1);
662       bs->msglen = len+1;
663       bs->send();
664    } else {
665       sp->callback(msg.c_str(), len, sp->context);
666    }
667 }
668
669
670 /*
671  * Status command from Director
672  */
673 bool status_cmd(JCR *jcr)
674 {
675    BSOCK *dir = jcr->dir_bsock;
676    STATUS_PKT sp;
677
678    dir->fsend("\n");
679    sp.bs = dir;
680    output_status(&sp);
681    dir->signal(BNET_EOD);
682    return true;
683 }
684
685 /*
686  * .status command from Director
687  */
688 bool qstatus_cmd(JCR *jcr)
689 {
690    BSOCK *dir = jcr->dir_bsock;
691    POOL_MEM cmd;
692    JCR *njcr;
693    s_last_job* job;
694    STATUS_PKT sp;
695
696    sp.bs = dir;
697    if (sscanf(dir->msg, qstatus, cmd.c_str()) != 1) {
698       pm_strcpy(jcr->errmsg, dir->msg);
699       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
700       dir->fsend(_("3900 Bad .status command, missing argument.\n"));
701       dir->signal(BNET_EOD);
702       return false;
703    }
704    unbash_spaces(cmd);
705
706    Dmsg1(200, "cmd=%s\n", cmd.c_str());
707
708    if (strcmp(cmd.c_str(), "current") == 0) {
709       dir->fsend(OKqstatus, cmd.c_str());
710       foreach_jcr(njcr) {
711          if (njcr->JobId != 0) {
712             dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
713          }
714       }
715       endeach_jcr(njcr);
716    } else if (strcmp(cmd.c_str(), "last") == 0) {
717       dir->fsend(OKqstatus, cmd.c_str());
718       if ((last_jobs) && (last_jobs->size() > 0)) {
719          job = (s_last_job*)last_jobs->last();
720          dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
721       }
722    } else if (strcasecmp(cmd.c_str(), "header") == 0) {
723        sp.api = true;
724        list_status_header(&sp);
725    } else if (strcasecmp(cmd.c_str(), "running") == 0) {
726        sp.api = true;
727        list_running_jobs(&sp);
728    } else if (strcasecmp(cmd.c_str(), "waitreservation") == 0) {
729        sp.api = true;
730        list_jobs_waiting_on_reservation(&sp);
731    } else if (strcasecmp(cmd.c_str(), "devices") == 0) {
732        sp.api = true;
733        list_devices(&sp);
734    } else if (strcasecmp(cmd.c_str(), "volumes") == 0) {
735        sp.api = true;
736        list_volumes(sendit, &sp);
737    } else if (strcasecmp(cmd.c_str(), "spooling") == 0) {
738        sp.api = true;
739        list_spool_stats(sendit, &sp);
740    } else if (strcasecmp(cmd.c_str(), "terminated") == 0) {
741        sp.api = true;
742        list_terminated_jobs(&sp);
743    } else {
744       pm_strcpy(jcr->errmsg, dir->msg);
745       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
746       dir->fsend(_("3900 Bad .status command, wrong argument.\n"));
747       dir->signal(BNET_EOD);
748       return false;
749    }
750    dir->signal(BNET_EOD);
751    return true;
752 }
753
754 #if defined(HAVE_WIN32)
755 int bacstat = 0;
756
757 /* Return a one line status for the tray monitor */
758 char *bac_status(char *buf, int buf_len)
759 {
760    JCR *njcr;
761    const char *termstat = _("Bacula Storage: Idle");
762    struct s_last_job *job;
763    int stat = 0;                      /* Idle */
764
765    if (!last_jobs) {
766       goto done;
767    }
768    Dmsg0(1000, "Begin bac_status jcr loop.\n");
769    foreach_jcr(njcr) {
770       if (njcr->JobId != 0) {
771          stat = JS_Running;
772          termstat = _("Bacula Storage: Running");
773          break;
774       }
775    }
776    endeach_jcr(njcr);
777
778    if (stat != 0) {
779       goto done;
780    }
781    if (last_jobs->size() > 0) {
782       job = (struct s_last_job *)last_jobs->last();
783       stat = job->JobStatus;
784       switch (job->JobStatus) {
785       case JS_Canceled:
786          termstat = _("Bacula Storage: Last Job Canceled");
787          break;
788       case JS_ErrorTerminated:
789       case JS_FatalError:
790          termstat = _("Bacula Storage: Last Job Failed");
791          break;
792       default:
793          if (job->Errors) {
794             termstat = _("Bacula Storage: Last Job had Warnings");
795          }
796          break;
797       }
798    }
799    Dmsg0(1000, "End bac_status jcr loop.\n");
800 done:
801    bacstat = stat;
802    if (buf) {
803       bstrncpy(buf, termstat, buf_len);
804    }
805    return buf;
806 }
807
808 #endif /* HAVE_WIN32 */