]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/status.c
kes Fix logic error in handling error return from mtx-changer
[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 > 0) {
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 if (dev->Slot == 0) {
264          len = Mmsg(msg, _("    Drive %d is not loaded.\n"), dev->drive_index);
265          sendit(msg, len, arg);
266       } else {
267          len = Mmsg(msg, _("    Drive %d status unknown.\n"), dev->drive_index);
268          sendit(msg, len, arg);
269       }
270    }
271    if (debug_level > 1) {
272       len = Mmsg(msg, _("Configured device capabilities:\n"));
273       sendit(msg, len, arg);
274
275       len = Mmsg(msg, "%sEOF %sBSR %sBSF %sFSR %sFSF %sEOM %sREM %sRACCESS %sAUTOMOUNT %sLABEL %sANONVOLS %sALWAYSOPEN\n",
276          dev->capabilities & CAP_EOF ? "" : "!", 
277          dev->capabilities & CAP_BSR ? "" : "!", 
278          dev->capabilities & CAP_BSF ? "" : "!", 
279          dev->capabilities & CAP_FSR ? "" : "!", 
280          dev->capabilities & CAP_FSF ? "" : "!", 
281          dev->capabilities & CAP_EOM ? "" : "!", 
282          dev->capabilities & CAP_REM ? "" : "!", 
283          dev->capabilities & CAP_RACCESS ? "" : "!",
284          dev->capabilities & CAP_AUTOMOUNT ? "" : "!", 
285          dev->capabilities & CAP_LABEL ? "" : "!", 
286          dev->capabilities & CAP_ANONVOLS ? "" : "!", 
287          dev->capabilities & CAP_ALWAYSOPEN ? "" : "!");
288       sendit(msg, len, arg);
289
290       len = Mmsg(msg, _("Device state:\n"));
291       sendit(msg, len, arg);
292
293       len = Mmsg(msg, "%sOPENED %sTAPE %sLABEL %sMALLOC %sAPPEND %sREAD %sEOT %sWEOT %sEOF %sNEXTVOL %sSHORT %sMOUNTED\n", 
294          dev->is_open() ? "" : "!", 
295          dev->is_tape() ? "" : "!", 
296          dev->is_labeled() ? "" : "!", 
297          dev->state & ST_MALLOC ? "" : "!", 
298          dev->can_append() ? "" : "!", 
299          dev->can_read() ? "" : "!", 
300          dev->at_eot() ? "" : "!", 
301          dev->state & ST_WEOT ? "" : "!", 
302          dev->at_eof() ? "" : "!", 
303          dev->state & ST_NEXTVOL ? "" : "!", 
304          dev->state & ST_SHORT ? "" : "!", 
305          dev->state & ST_MOUNTED ? "" : "!");
306       sendit(msg, len, arg);
307
308       len = Mmsg(msg, _("num_writers=%d block=%d\n\n"), dev->num_writers, dev->dev_blocked);
309       sendit(msg, len, arg);
310
311       len = Mmsg(msg, _("Device parameters:\n"));
312       sendit(msg, len, arg);
313
314       len = Mmsg(msg, _("Archive name: %s Device name: %s\n"), dev->archive_name(),
315          dev->name());
316       sendit(msg, len, arg);
317
318       len = Mmsg(msg, _("File=%u block=%u\n"), dev->file, dev->block_num);
319       sendit(msg, len, arg);
320
321       len = Mmsg(msg, _("Min block=%u Max block=%u\n"), dev->min_block_size, dev->max_block_size);
322       sendit(msg, len, arg);
323    }
324
325    free_pool_memory(msg);
326 }
327
328 static void list_running_jobs(void sendit(const char *msg, int len, void *sarg), void *arg)
329 {
330    bool found = false;
331    int bps, sec;
332    JCR *jcr;
333    DCR *dcr, *rdcr;
334    char JobName[MAX_NAME_LENGTH];
335    char *msg, b1[30], b2[30], b3[30];
336    int len;
337
338    msg = (char *)get_pool_memory(PM_MESSAGE);
339
340    len = Mmsg(msg, _("\nRunning Jobs:\n"));
341    sendit(msg, len, arg);
342
343    foreach_jcr(jcr) {
344       if (jcr->JobStatus == JS_WaitFD) {
345          len = Mmsg(msg, _("%s Job %s waiting for Client connection.\n"),
346             job_type_to_str(jcr->JobType), jcr->Job);
347          sendit(msg, len, arg);
348       }
349       dcr = jcr->dcr;
350       rdcr = jcr->read_dcr;
351       if ((dcr && dcr->device) || rdcr && rdcr->device) {
352          bstrncpy(JobName, jcr->Job, sizeof(JobName));
353          /* There are three periods after the Job name */
354          char *p;
355          for (int i=0; i<3; i++) {
356             if ((p=strrchr(JobName, '.')) != NULL) {
357                *p = 0;
358             }
359          }
360          if (rdcr && rdcr->device) {
361             len = Mmsg(msg, _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
362                             "    pool=\"%s\" device=\"%s\"\n"),
363                    job_level_to_str(jcr->JobLevel),
364                    job_type_to_str(jcr->JobType),
365                    JobName,
366                    jcr->JobId,
367                    rdcr->VolumeName,
368                    rdcr->pool_name,
369                    rdcr->dev?rdcr->dev->print_name(): 
370                             rdcr->device->device_name);
371             sendit(msg, len, arg);
372          }
373          if (dcr && dcr->device) {
374             len = Mmsg(msg, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
375                             "    pool=\"%s\" device=\"%s\"\n"),
376                    job_level_to_str(jcr->JobLevel),
377                    job_type_to_str(jcr->JobType),
378                    JobName,
379                    jcr->JobId,
380                    dcr->VolumeName,
381                    dcr->pool_name,
382                    dcr->dev?dcr->dev->print_name(): 
383                             dcr->device->device_name);
384             sendit(msg, len, arg);
385          }
386          sec = time(NULL) - jcr->run_time;
387          if (sec <= 0) {
388             sec = 1;
389          }
390          bps = jcr->JobBytes / sec;
391          len = Mmsg(msg, _("    Files=%s Bytes=%s Bytes/sec=%s\n"),
392             edit_uint64_with_commas(jcr->JobFiles, b1),
393             edit_uint64_with_commas(jcr->JobBytes, b2),
394             edit_uint64_with_commas(bps, b3));
395          sendit(msg, len, arg);
396          found = true;
397 #ifdef DEBUG
398          if (jcr->file_bsock) {
399             len = Mmsg(msg, _("    FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
400                edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
401                jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
402                jcr->file_bsock->fd);
403             sendit(msg, len, arg);
404          } else {
405             len = Mmsg(msg, _("    FDSocket closed\n"));
406             sendit(msg, len, arg);
407          }
408 #endif
409       }
410    }
411    endeach_jcr(jcr);
412
413    if (!found) {
414       len = Mmsg(msg, _("No Jobs running.\n"));
415       sendit(msg, len, arg);
416    }
417    len = Mmsg(msg, _("====\n"));
418    sendit(msg, len, arg);
419
420    free_pool_memory(msg);
421 }
422
423 static void list_jobs_waiting_on_reservation(void sendit(const char *msg, int len, void *sarg), void *arg)
424
425    JCR *jcr;
426    char *msg;
427
428    msg = _("\nJobs waiting to reserve a drive:\n");
429    sendit(msg, strlen(msg), arg);
430
431    foreach_jcr(jcr) {
432       if (!jcr->reserve_msgs) {
433          continue;
434       }
435       send_drive_reserve_messages(jcr, sendit, arg);
436    }
437    endeach_jcr(jcr);
438
439    msg = _("====\n");
440    sendit(msg, strlen(msg), arg);
441 }
442
443
444 static void list_terminated_jobs(void sendit(const char *msg, int len, void *sarg), void *arg)
445 {
446    char dt[MAX_TIME_LENGTH], b1[30], b2[30];
447    char level[10];
448    struct s_last_job *je;
449    const char *msg;
450
451    if (last_jobs->size() == 0) {
452       msg = _("No Terminated Jobs.\n");
453       sendit(msg, strlen(msg), arg);
454       return;
455    }
456    lock_last_jobs_list();
457    msg =  _("\nTerminated Jobs:\n");
458    sendit(msg, strlen(msg), arg);
459    msg =  _(" JobId  Level    Files      Bytes   Status   Finished        Name \n");
460    sendit(msg, strlen(msg), arg);
461    msg =  _("===================================================================\n");
462    sendit(msg, strlen(msg), arg);
463    foreach_dlist(je, last_jobs) {
464       char JobName[MAX_NAME_LENGTH];
465       const char *termstat;
466       char buf[1000];
467
468       bstrftime_nc(dt, sizeof(dt), je->end_time);
469       switch (je->JobType) {
470       case JT_ADMIN:
471       case JT_RESTORE:
472          bstrncpy(level, "    ", sizeof(level));
473          break;
474       default:
475          bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
476          level[4] = 0;
477          break;
478       }
479       switch (je->JobStatus) {
480       case JS_Created:
481          termstat = _("Created");
482          break;
483       case JS_FatalError:
484       case JS_ErrorTerminated:
485          termstat = _("Error");
486          break;
487       case JS_Differences:
488          termstat = _("Diffs");
489          break;
490       case JS_Canceled:
491          termstat = _("Cancel");
492          break;
493       case JS_Terminated:
494          termstat = _("OK");
495          break;
496       default:
497          termstat = _("Other");
498          break;
499       }
500       bstrncpy(JobName, je->Job, sizeof(JobName));
501       /* There are three periods after the Job name */
502       char *p;
503       for (int i=0; i<3; i++) {
504          if ((p=strrchr(JobName, '.')) != NULL) {
505             *p = 0;
506          }
507       }
508       bsnprintf(buf, sizeof(buf), _("%6d  %-6s %8s %10s  %-7s  %-8s %s\n"),
509          je->JobId,
510          level,
511          edit_uint64_with_commas(je->JobFiles, b1),
512          edit_uint64_with_suffix(je->JobBytes, b2),
513          termstat,
514          dt, JobName);
515       sendit(buf, strlen(buf), arg);
516    }
517    sendit(_("====\n"), 5, arg);
518    unlock_last_jobs_list();
519 }
520
521 /*
522  * Convert Job Level into a string
523  */
524 static const char *level_to_str(int level)
525 {
526    const char *str;
527
528    switch (level) {
529    case L_BASE:
530       str = _("Base");
531    case L_FULL:
532       str = _("Full");
533       break;
534    case L_INCREMENTAL:
535       str = _("Incremental");
536       break;
537    case L_DIFFERENTIAL:
538       str = _("Differential");
539       break;
540    case L_SINCE:
541       str = _("Since");
542       break;
543    case L_VERIFY_CATALOG:
544       str = _("Verify Catalog");
545       break;
546    case L_VERIFY_INIT:
547       str = _("Init Catalog");
548       break;
549    case L_VERIFY_VOLUME_TO_CATALOG:
550       str = _("Volume to Catalog");
551       break;
552    case L_VERIFY_DISK_TO_CATALOG:
553       str = _("Disk to Catalog");
554       break;
555    case L_VERIFY_DATA:
556       str = _("Data");
557       break;
558    case L_NONE:
559       str = " ";
560       break;
561    default:
562       str = _("Unknown Job Level");
563       break;
564    }
565    return str;
566 }
567
568 /*
569  * Send to Director
570  */
571 static void bsock_sendit(const char *msg, int len, void *arg)
572 {
573    BSOCK *user = (BSOCK *)arg;
574
575    memcpy(user->msg, msg, len+1);
576    user->msglen = len+1;
577    bnet_send(user);
578 }
579
580 /*
581  * Status command from Director
582  */
583 bool status_cmd(JCR *jcr)
584 {
585    BSOCK *user = jcr->dir_bsock;
586
587    bnet_fsend(user, "\n");
588    do_status(bsock_sendit, (void *)user);
589
590    bnet_sig(user, BNET_EOD);
591    return 1;
592 }
593
594 /*
595  * .status command from Director
596  */
597 bool qstatus_cmd(JCR *jcr)
598 {
599    BSOCK *dir = jcr->dir_bsock;
600    POOL_MEM time;
601    JCR *njcr;
602    s_last_job* job;
603
604    if (sscanf(dir->msg, qstatus, time.c_str()) != 1) {
605       pm_strcpy(jcr->errmsg, dir->msg);
606       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
607       bnet_fsend(dir, _("3900 Bad .status command, missing argument.\n"));
608       bnet_sig(dir, BNET_EOD);
609       return false;
610    }
611    unbash_spaces(time);
612
613    if (strcmp(time.c_str(), "current") == 0) {
614       bnet_fsend(dir, OKqstatus, time.c_str());
615       foreach_jcr(njcr) {
616          if (njcr->JobId != 0) {
617             bnet_fsend(dir, DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
618          }
619       }
620       endeach_jcr(njcr);
621    } else if (strcmp(time.c_str(), "last") == 0) {
622       bnet_fsend(dir, OKqstatus, time.c_str());
623       if ((last_jobs) && (last_jobs->size() > 0)) {
624          job = (s_last_job*)last_jobs->last();
625          bnet_fsend(dir, DotStatusJob, job->JobId, job->JobStatus, job->Errors);
626       }
627    } else {
628       pm_strcpy(jcr->errmsg, dir->msg);
629       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
630       bnet_fsend(dir, _("3900 Bad .status command, wrong argument.\n"));
631       bnet_sig(dir, BNET_EOD);
632       return false;
633    }
634    bnet_sig(dir, BNET_EOD);
635    return true;
636 }
637
638 #if defined(HAVE_WIN32)
639 int bacstat = 0;
640
641 struct s_win32_arg {
642    HWND hwnd;
643    int idlist;
644 };
645
646 /*
647  * Put message in Window List Box
648  */
649 static void win32_sendit(const char *msg, int len, void *marg)
650 {
651    struct s_win32_arg *arg = (struct s_win32_arg *)marg;
652
653    if (len > 0 && msg[len-1] == '\n') {
654        // when compiling with visual studio some strings are read-only
655        // and cause access violations.  So we creat a tmp copy.
656        char *_msg = (char *)alloca(len);
657        bstrncpy(_msg, msg, len);
658        msg = _msg;
659    }
660    SendDlgItemMessage(arg->hwnd, arg->idlist, LB_ADDSTRING, 0, (LONG)msg);
661
662 }
663
664 void FillStatusBox(HWND hwnd, int idlist)
665 {
666    struct s_win32_arg arg;
667
668    arg.hwnd = hwnd;
669    arg.idlist = idlist;
670
671    /* Empty box */
672    for ( ; SendDlgItemMessage(hwnd, idlist, LB_DELETESTRING, 0, (LONG)0) > 0; )
673       { }
674    do_status(win32_sendit, (void *)&arg);
675 }
676
677 char *bac_status(char *buf, int buf_len)
678 {
679    JCR *njcr;
680    const char *termstat = _("Bacula Idle");
681    struct s_last_job *job;
682    int stat = 0;                      /* Idle */
683
684    if (!last_jobs) {
685       goto done;
686    }
687    Dmsg0(1000, "Begin bac_status jcr loop.\n");
688    foreach_jcr(njcr) {
689       if (njcr->JobId != 0) {
690          stat = JS_Running;
691          termstat = _("Bacula Running");
692          break;
693       }
694    }
695    endeach_jcr(njcr);
696
697    if (stat != 0) {
698       goto done;
699    }
700    if (last_jobs->size() > 0) {
701       job = (struct s_last_job *)last_jobs->last();
702       stat = job->JobStatus;
703       switch (job->JobStatus) {
704       case JS_Canceled:
705          termstat = _("Last Job Canceled");
706          break;
707       case JS_ErrorTerminated:
708       case JS_FatalError:
709          termstat = _("Last Job Failed");
710          break;
711       default:
712          if (job->Errors) {
713             termstat = _("Last Job had Warnings");
714          }
715          break;
716       }
717    }
718    Dmsg0(1000, "End bac_status jcr loop.\n");
719 done:
720    bacstat = stat;
721    if (buf) {
722       bstrncpy(buf, termstat, buf_len);
723    }
724    return buf;
725 }
726
727 #endif /* HAVE_WIN32 */