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