]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/status.c
kes Correct po warning message with datarootdir
[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
40 /* Exported variables */
41
42 /* Imported variables */
43 extern BSOCK *filed_chan;
44 extern int r_first, r_last;
45 extern struct s_res resources[];
46 extern void *start_heap;
47
48 /* Static variables */
49 static char qstatus[] = ".status %127s\n";
50
51 static char OKqstatus[]   = "3000 OK .status\n";
52 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
53
54
55 /* Forward referenced functions */
56 static void sendit(const char *msg, int len, BSOCK *bs);
57 static void sendit(POOL_MEM &msg, int len, BSOCK *bs);
58 static void sendit(const char *msg, int len, void *arg);
59
60 static void send_blocked_status(DEVICE *dev, BSOCK *bs);
61 static void list_terminated_jobs(BSOCK *bs);
62 static void list_running_jobs(BSOCK *bs);
63 static void list_jobs_waiting_on_reservation(BSOCK *bs);
64 static void list_status_header(BSOCK *bs);
65 static void list_devices(BSOCK *bs);
66
67 static const char *level_to_str(int level);
68
69 /*
70  * Status command from Director
71  */
72 void output_status(BSOCK *bs)
73 {
74    POOL_MEM msg(PM_MESSAGE);
75    int len;
76
77    list_status_header(bs);
78
79    /*
80     * List running jobs
81     */
82    list_running_jobs(bs);
83
84    /*
85     * List jobs stuck in reservation system
86     */
87    list_jobs_waiting_on_reservation(bs);
88
89    /*
90     * List terminated jobs
91     */
92    list_terminated_jobs(bs);
93
94    /*
95     * List devices
96     */
97    list_devices(bs);
98
99
100    len = Mmsg(msg, _("In Use Volume status:\n"));
101    sendit(msg, len, bs);
102
103    list_volumes(sendit, (void *)bs);
104    sendit("====\n\n", 6, bs);
105
106 #ifdef xxx
107    if (debug_level > 10) {
108       bs->fsend(_("====\n\n"));
109       dump_resource(R_DEVICE, resources[R_DEVICE-r_first].res_head, sendit, user);
110       bs->fsend(_("====\n\n"));
111    }
112 #endif
113
114    list_spool_stats(sendit, (void *)bs);
115 }
116
117
118 static void list_devices(BSOCK *bs)
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    sendit(msg, len, bs);
130
131    foreach_res(changer, R_AUTOCHANGER) {
132       len = Mmsg(msg, _("Autochanger \"%s\" with devices:\n"),
133          changer->hdr.name);
134       sendit(msg, len, bs);
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, bs);
140          } else {
141             len = Mmsg(msg, "   %s\n", device->hdr.name);
142             sendit(msg, len, bs);
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, bs);
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, bs);
163          }
164          send_blocked_status(dev, bs);
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, bs);
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, bs);
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, bs);
196
197       } else {
198          if (dev) {
199             len = Mmsg(msg, _("Device %s is not open.\n"), dev->print_name());
200             sendit(msg, len, bs);
201             send_blocked_status(dev, bs);
202         } else {
203             len = Mmsg(msg, _("Device \"%s\" is not open or does not exist.\n"), device->hdr.name);
204             sendit(msg, len, bs);
205          }
206       }
207    }
208    sendit("====\n\n", 6, bs);
209 }
210
211 static void list_status_header(BSOCK *bs)
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, bs);
221
222    bstrftime_nc(dt, sizeof(dt), daemon_start_time);
223
224
225    len = Mmsg(msg, _("Daemon started %s, %d Job%s run since started.\n"),
226         dt, num_jobs_run, num_jobs_run == 1 ? "" : "s");
227    sendit(msg, len, bs);
228
229    len = Mmsg(msg, _(" Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
230          edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
231          edit_uint64_with_commas(sm_bytes, b2),
232          edit_uint64_with_commas(sm_max_bytes, b3),
233          edit_uint64_with_commas(sm_buffers, b4),
234          edit_uint64_with_commas(sm_max_buffers, b5));
235    sendit(msg, len, bs);
236    len = Mmsg(msg, "Sizes: boffset_t=%d size_t=%d int32_t=%d int64_t=%d\n", 
237          (int)sizeof(boffset_t), (int)sizeof(size_t), (int)sizeof(int32_t),
238          (int)sizeof(int64_t));
239    sendit(msg, len, bs);
240 }
241
242 static void send_blocked_status(DEVICE *dev, BSOCK *bs)
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, bs);
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, bs);
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, bs);
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, bs);
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, bs);
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, bs);
293          }
294       }
295       break;
296    case BST_DOING_ACQUIRE:
297       len = Mmsg(msg, _("    Device is being initialized.\n"));
298       sendit(msg, len, bs);
299       break;
300    case BST_WRITING_LABEL:
301       len = Mmsg(msg, _("    Device is blocked labeling a Volume.\n"));
302       sendit(msg, len, bs);
303       break;
304    default:
305       break;
306    }
307    /* Send autochanger slot status */
308    if (dev->is_autochanger()) {
309       if (dev->Slot > 0) {
310          len = Mmsg(msg, _("    Slot %d is loaded in drive %d.\n"), 
311             dev->Slot, dev->drive_index);
312          sendit(msg, len, bs);
313       } else if (dev->Slot == 0) {
314          len = Mmsg(msg, _("    Drive %d is not loaded.\n"), dev->drive_index);
315          sendit(msg, len, bs);
316       } else {
317          len = Mmsg(msg, _("    Drive %d status unknown.\n"), dev->drive_index);
318          sendit(msg, len, bs);
319       }
320    }
321    if (debug_level > 1) {
322       len = Mmsg(msg, _("Configured device capabilities:\n"));
323       sendit(msg, len, bs);
324
325       len = Mmsg(msg, "%sEOF %sBSR %sBSF %sFSR %sFSF %sEOM %sREM %sRACCESS %sAUTOMOUNT %sLABEL %sANONVOLS %sALWAYSOPEN\n",
326          dev->capabilities & CAP_EOF ? "" : "!", 
327          dev->capabilities & CAP_BSR ? "" : "!", 
328          dev->capabilities & CAP_BSF ? "" : "!", 
329          dev->capabilities & CAP_FSR ? "" : "!", 
330          dev->capabilities & CAP_FSF ? "" : "!", 
331          dev->capabilities & CAP_EOM ? "" : "!", 
332          dev->capabilities & CAP_REM ? "" : "!", 
333          dev->capabilities & CAP_RACCESS ? "" : "!",
334          dev->capabilities & CAP_AUTOMOUNT ? "" : "!", 
335          dev->capabilities & CAP_LABEL ? "" : "!", 
336          dev->capabilities & CAP_ANONVOLS ? "" : "!", 
337          dev->capabilities & CAP_ALWAYSOPEN ? "" : "!");
338       sendit(msg, len, bs);
339
340       len = Mmsg(msg, _("Device state:\n"));
341       sendit(msg, len, bs);
342
343       len = Mmsg(msg, "%sOPENED %sTAPE %sLABEL %sMALLOC %sAPPEND %sREAD %sEOT %sWEOT %sEOF %sNEXTVOL %sSHORT %sMOUNTED\n", 
344          dev->is_open() ? "" : "!", 
345          dev->is_tape() ? "" : "!", 
346          dev->is_labeled() ? "" : "!", 
347          dev->state & ST_MALLOC ? "" : "!", 
348          dev->can_append() ? "" : "!", 
349          dev->can_read() ? "" : "!", 
350          dev->at_eot() ? "" : "!", 
351          dev->state & ST_WEOT ? "" : "!", 
352          dev->at_eof() ? "" : "!", 
353          dev->state & ST_NEXTVOL ? "" : "!", 
354          dev->state & ST_SHORT ? "" : "!", 
355          dev->state & ST_MOUNTED ? "" : "!");
356       sendit(msg, len, bs);
357
358       len = Mmsg(msg, _("num_writers=%d block=%d\n\n"), dev->num_writers, dev->blocked());
359       sendit(msg, len, bs);
360
361       len = Mmsg(msg, _("Device parameters:\n"));
362       sendit(msg, len, bs);
363
364       len = Mmsg(msg, _("Archive name: %s Device name: %s\n"), dev->archive_name(),
365          dev->name());
366       sendit(msg, len, bs);
367
368       len = Mmsg(msg, _("File=%u block=%u\n"), dev->file, dev->block_num);
369       sendit(msg, len, bs);
370
371       len = Mmsg(msg, _("Min block=%u Max block=%u\n"), dev->min_block_size, dev->max_block_size);
372       sendit(msg, len, bs);
373    }
374 }
375
376 static void list_running_jobs(BSOCK *bs)
377 {
378    bool found = false;
379    int bps, sec;
380    JCR *jcr;
381    DCR *dcr, *rdcr;
382    char JobName[MAX_NAME_LENGTH];
383    char b1[30], b2[30], b3[30];
384    int len;
385    POOL_MEM msg(PM_MESSAGE);
386
387    len = Mmsg(msg, _("\nRunning Jobs:\n"));
388    sendit(msg, len, bs);
389
390    foreach_jcr(jcr) {
391       if (jcr->JobStatus == JS_WaitFD) {
392          len = Mmsg(msg, _("%s Job %s waiting for Client connection.\n"),
393             job_type_to_str(jcr->JobType), jcr->Job);
394          sendit(msg, len, bs);
395       }
396       dcr = jcr->dcr;
397       rdcr = jcr->read_dcr;
398       if ((dcr && dcr->device) || rdcr && rdcr->device) {
399          bstrncpy(JobName, jcr->Job, sizeof(JobName));
400          /* There are three periods after the Job name */
401          char *p;
402          for (int i=0; i<3; i++) {
403             if ((p=strrchr(JobName, '.')) != NULL) {
404                *p = 0;
405             }
406          }
407          if (rdcr && rdcr->device) {
408             len = Mmsg(msg, _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
409                             "    pool=\"%s\" device=%s\n"),
410                    job_level_to_str(jcr->JobLevel),
411                    job_type_to_str(jcr->JobType),
412                    JobName,
413                    jcr->JobId,
414                    rdcr->VolumeName,
415                    rdcr->pool_name,
416                    rdcr->dev?rdcr->dev->print_name(): 
417                             rdcr->device->device_name);
418             sendit(msg, len, bs);
419          }
420          if (dcr && dcr->device) {
421             len = Mmsg(msg, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
422                             "    pool=\"%s\" device=%s\n"),
423                    job_level_to_str(jcr->JobLevel),
424                    job_type_to_str(jcr->JobType),
425                    JobName,
426                    jcr->JobId,
427                    dcr->VolumeName,
428                    dcr->pool_name,
429                    dcr->dev?dcr->dev->print_name(): 
430                             dcr->device->device_name);
431             sendit(msg, len, bs);
432             len= Mmsg(msg, _("    spooling=%d despooling=%d despool_wait=%d\n"),
433                    dcr->spooling, dcr->despooling, dcr->despool_wait);
434             sendit(msg, len, bs);
435          }
436          sec = time(NULL) - jcr->run_time;
437          if (sec <= 0) {
438             sec = 1;
439          }
440          bps = jcr->JobBytes / sec;
441          len = Mmsg(msg, _("    Files=%s Bytes=%s Bytes/sec=%s\n"),
442             edit_uint64_with_commas(jcr->JobFiles, b1),
443             edit_uint64_with_commas(jcr->JobBytes, b2),
444             edit_uint64_with_commas(bps, b3));
445          sendit(msg, len, bs);
446          found = true;
447 #ifdef DEBUG
448          if (jcr->file_bsock) {
449             len = Mmsg(msg, _("    FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
450                edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
451                jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
452                jcr->file_bsock->m_fd);
453             sendit(msg, len, bs);
454          } else {
455             len = Mmsg(msg, _("    FDSocket closed\n"));
456             sendit(msg, len, bs);
457          }
458 #endif
459       }
460    }
461    endeach_jcr(jcr);
462
463    if (!found) {
464       len = Mmsg(msg, _("No Jobs running.\n"));
465       sendit(msg, len, bs);
466    }
467    sendit("====\n", 5, bs);
468 }
469
470 static void list_jobs_waiting_on_reservation(BSOCK *bs)
471
472    JCR *jcr;
473    POOL_MEM msg(PM_MESSAGE);
474    int len;
475
476    len = Mmsg(msg, _("\nJobs waiting to reserve a drive:\n"));
477    sendit(msg, len, bs);
478
479    foreach_jcr(jcr) {
480       if (!jcr->reserve_msgs) {
481          continue;
482       }
483       send_drive_reserve_messages(jcr, sendit, bs);
484    }
485    endeach_jcr(jcr);
486
487    sendit("====\n", 5, bs);
488 }
489
490
491 static void list_terminated_jobs(BSOCK *bs)
492 {
493    char dt[MAX_TIME_LENGTH], b1[30], b2[30];
494    char level[10];
495    struct s_last_job *je;
496    const char *msg;
497
498    msg =  _("\nTerminated Jobs:\n");
499    sendit(msg, strlen(msg), bs);
500    if (last_jobs->size() == 0) {
501       sendit("====\n", 5, bs);
502       return;
503    }
504    lock_last_jobs_list();
505    msg =  _(" JobId  Level    Files      Bytes   Status   Finished        Name \n");
506    sendit(msg, strlen(msg), bs);
507    msg =  _("===================================================================\n");
508    sendit(msg, strlen(msg), bs);
509    foreach_dlist(je, last_jobs) {
510       char JobName[MAX_NAME_LENGTH];
511       const char *termstat;
512       char buf[1000];
513
514       bstrftime_nc(dt, sizeof(dt), je->end_time);
515       switch (je->JobType) {
516       case JT_ADMIN:
517       case JT_RESTORE:
518          bstrncpy(level, "    ", sizeof(level));
519          break;
520       default:
521          bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
522          level[4] = 0;
523          break;
524       }
525       switch (je->JobStatus) {
526       case JS_Created:
527          termstat = _("Created");
528          break;
529       case JS_FatalError:
530       case JS_ErrorTerminated:
531          termstat = _("Error");
532          break;
533       case JS_Differences:
534          termstat = _("Diffs");
535          break;
536       case JS_Canceled:
537          termstat = _("Cancel");
538          break;
539       case JS_Terminated:
540          termstat = _("OK");
541          break;
542       default:
543          termstat = _("Other");
544          break;
545       }
546       bstrncpy(JobName, je->Job, sizeof(JobName));
547       /* There are three periods after the Job name */
548       char *p;
549       for (int i=0; i<3; i++) {
550          if ((p=strrchr(JobName, '.')) != NULL) {
551             *p = 0;
552          }
553       }
554       bsnprintf(buf, sizeof(buf), _("%6d  %-6s %8s %10s  %-7s  %-8s %s\n"),
555          je->JobId,
556          level,
557          edit_uint64_with_commas(je->JobFiles, b1),
558          edit_uint64_with_suffix(je->JobBytes, b2),
559          termstat,
560          dt, JobName);
561       sendit(buf, strlen(buf), bs);
562    }
563    unlock_last_jobs_list();
564    sendit("====\n", 5, bs);
565 }
566
567 /*
568  * Convert Job Level into a string
569  */
570 static const char *level_to_str(int level)
571 {
572    const char *str;
573
574    switch (level) {
575    case L_BASE:
576       str = _("Base");
577    case L_FULL:
578       str = _("Full");
579       break;
580    case L_INCREMENTAL:
581       str = _("Incremental");
582       break;
583    case L_DIFFERENTIAL:
584       str = _("Differential");
585       break;
586    case L_SINCE:
587       str = _("Since");
588       break;
589    case L_VERIFY_CATALOG:
590       str = _("Verify Catalog");
591       break;
592    case L_VERIFY_INIT:
593       str = _("Init Catalog");
594       break;
595    case L_VERIFY_VOLUME_TO_CATALOG:
596       str = _("Volume to Catalog");
597       break;
598    case L_VERIFY_DISK_TO_CATALOG:
599       str = _("Disk to Catalog");
600       break;
601    case L_VERIFY_DATA:
602       str = _("Data");
603       break;
604    case L_NONE:
605       str = " ";
606       break;
607    default:
608       str = _("Unknown Job Level");
609       break;
610    }
611    return str;
612 }
613
614 /*
615  * Send to Director
616  */
617 static void sendit(const char *msg, int len, BSOCK *bs)
618 {
619    memcpy(bs->msg, msg, len+1);
620    bs->msglen = len+1;
621    bs->send();
622 }
623
624 static void sendit(const char *msg, int len, void *arg)
625 {
626    BSOCK *bs = (BSOCK *)arg;
627    memcpy(bs->msg, msg, len+1);
628    bs->msglen = len+1;
629    bs->send();
630 }
631
632 static void sendit(POOL_MEM &msg, int len, BSOCK *bs)
633 {
634    memcpy(bs->msg, msg.c_str(), len+1);
635    bs->msglen = len+1;
636    bs->send();
637 }
638
639
640 /*
641  * Status command from Director
642  */
643 bool status_cmd(JCR *jcr)
644 {
645    BSOCK *bs = jcr->dir_bsock;
646
647    bs->fsend("\n");
648    output_status(bs);
649    bs->signal(BNET_EOD);
650    return 1;
651 }
652
653 /*
654  * .status command from Director
655  */
656 bool qstatus_cmd(JCR *jcr)
657 {
658    BSOCK *dir = jcr->dir_bsock;
659    POOL_MEM time;
660    JCR *njcr;
661    s_last_job* job;
662
663    if (sscanf(dir->msg, qstatus, time.c_str()) != 1) {
664       pm_strcpy(jcr->errmsg, dir->msg);
665       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
666       dir->fsend(_("3900 Bad .status command, missing argument.\n"));
667       dir->signal(BNET_EOD);
668       return false;
669    }
670    unbash_spaces(time);
671
672    if (strcmp(time.c_str(), "current") == 0) {
673       dir->fsend(OKqstatus, time.c_str());
674       foreach_jcr(njcr) {
675          if (njcr->JobId != 0) {
676             dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
677          }
678       }
679       endeach_jcr(njcr);
680    } else if (strcmp(time.c_str(), "last") == 0) {
681       dir->fsend(OKqstatus, time.c_str());
682       if ((last_jobs) && (last_jobs->size() > 0)) {
683          job = (s_last_job*)last_jobs->last();
684          dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
685       }
686    } else {
687       pm_strcpy(jcr->errmsg, dir->msg);
688       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
689       dir->fsend(_("3900 Bad .status command, wrong argument.\n"));
690       dir->signal(BNET_EOD);
691       return false;
692    }
693    dir->signal(BNET_EOD);
694    return true;
695 }
696
697 #if defined(HAVE_WIN32)
698 int bacstat = 0;
699
700 /* Return a one line status for the tray monitor */
701 char *bac_status(char *buf, int buf_len)
702 {
703    JCR *njcr;
704    const char *termstat = _("Bacula Storage: Idle");
705    struct s_last_job *job;
706    int stat = 0;                      /* Idle */
707
708    if (!last_jobs) {
709       goto done;
710    }
711    Dmsg0(1000, "Begin bac_status jcr loop.\n");
712    foreach_jcr(njcr) {
713       if (njcr->JobId != 0) {
714          stat = JS_Running;
715          termstat = _("Bacula Storage: Running");
716          break;
717       }
718    }
719    endeach_jcr(njcr);
720
721    if (stat != 0) {
722       goto done;
723    }
724    if (last_jobs->size() > 0) {
725       job = (struct s_last_job *)last_jobs->last();
726       stat = job->JobStatus;
727       switch (job->JobStatus) {
728       case JS_Canceled:
729          termstat = _("Bacula Storage: Last Job Canceled");
730          break;
731       case JS_ErrorTerminated:
732       case JS_FatalError:
733          termstat = _("Bacula Storage: Last Job Failed");
734          break;
735       default:
736          if (job->Errors) {
737             termstat = _("Bacula Storage: Last Job had Warnings");
738          }
739          break;
740       }
741    }
742    Dmsg0(1000, "End bac_status jcr loop.\n");
743 done:
744    bacstat = stat;
745    if (buf) {
746       bstrncpy(buf, termstat, buf_len);
747    }
748    return buf;
749 }
750
751 #endif /* HAVE_WIN32 */