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