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