]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/status.c
Backport new lock calls + debug for SD
[bacula/bacula] / bacula / src / stored / status.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2003-2012 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *  This file handles the status command
30  *
31  *     Kern Sibbald, May MMIII
32  *
33  *
34  */
35
36 #include "bacula.h"
37 #include "stored.h"
38 #include "lib/status.h"
39
40 /* Imported functions */
41
42 /* Imported variables */
43 extern BSOCK *filed_chan;
44 extern void *start_heap;
45
46 /* Static variables */
47 static char qstatus[] = ".status %127s\n";
48
49 static char OKqstatus[]   = "3000 OK .status\n";
50 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
51
52
53 /* Forward referenced functions */
54 static void sendit(const char *msg, int len, STATUS_PKT *sp);
55 static void sendit(POOL_MEM &msg, int len, STATUS_PKT *sp);
56 static void sendit(const char *msg, int len, void *arg);
57
58 static void send_blocked_status(DEVICE *dev, STATUS_PKT *sp);
59 static void send_device_status(DEVICE *dev, STATUS_PKT *sp);
60 static void list_terminated_jobs(STATUS_PKT *sp);
61 static void list_running_jobs(STATUS_PKT *sp);
62 static void list_jobs_waiting_on_reservation(STATUS_PKT *sp);
63 static void list_status_header(STATUS_PKT *sp);
64 static void list_devices(STATUS_PKT *sp);
65
66 static const char *level_to_str(int level);
67
68 /*
69  * Status command from Director
70  */
71 void output_status(STATUS_PKT *sp)
72 {
73    POOL_MEM msg(PM_MESSAGE);
74    int len;
75
76    list_status_header(sp);
77
78    /*
79     * List running jobs
80     */
81    list_running_jobs(sp);
82
83    /*
84     * List jobs stuck in reservation system
85     */
86    list_jobs_waiting_on_reservation(sp);
87
88    /*
89     * List terminated jobs
90     */
91    list_terminated_jobs(sp);
92
93    /*
94     * List devices
95     */
96    list_devices(sp);
97
98
99    len = Mmsg(msg, _("Used Volume status:\n"));
100    if (!sp->api) sendit(msg, len, sp);
101
102    list_volumes(sendit, (void *)sp);
103    if (!sp->api) sendit("====\n\n", 6, sp);
104
105
106    list_spool_stats(sendit, (void *)sp);
107    if (!sp->api) sendit("====\n\n", 6, sp);
108
109 }
110
111 static void list_resources(STATUS_PKT *sp)
112 {
113 #ifdef when_working
114    POOL_MEM msg(PM_MESSAGE);
115    int len;
116
117    len = Mmsg(msg, _("\nSD Resources:\n"));
118    if (!sp->api) sendit(msg, len, sp);
119    dump_resource(R_DEVICE, resources[R_DEVICE-r_first], sp);
120    if (!sp->api) sendit("====\n\n", 6, sp);
121 #endif
122 }
123
124 #ifdef xxxx
125 static find_device(char *devname)
126 {
127    foreach_res(device, R_DEVICE) {
128       if (strcasecmp(device->hdr.name, devname) == 0) {
129          found = true;
130          break;
131       }
132    }
133    if (!found) {
134       foreach_res(changer, R_AUTOCHANGER) {
135          if (strcasecmp(changer->hdr.name, devname) == 0) {
136             break;
137          }
138       }
139    }
140 }
141 #endif
142
143 static void list_devices(STATUS_PKT *sp)
144 {
145    DEVRES *device;
146    AUTOCHANGER *changer;
147    DEVICE *dev;
148    char b1[35], b2[35], b3[35];
149    POOL_MEM msg(PM_MESSAGE);
150    int len;
151    int bpb;
152
153    len = Mmsg(msg, _("\nDevice status:\n"));
154    if (!sp->api) sendit(msg, len, sp);
155
156    foreach_res(changer, R_AUTOCHANGER) {
157       len = Mmsg(msg, _("Autochanger \"%s\" with devices:\n"),
158          changer->hdr.name);
159       sendit(msg, len, sp);
160
161       foreach_alist(device, changer->device) {
162          if (device->dev) {
163             len = Mmsg(msg, "   %s\n", device->dev->print_name());
164             sendit(msg, len, sp);
165          } else {
166             len = Mmsg(msg, "   %s\n", device->hdr.name);
167             sendit(msg, len, sp);
168          }
169       }
170    }
171
172
173    foreach_res(device, R_DEVICE) {
174       dev = device->dev;
175       if (dev && dev->is_open()) {
176          if (dev->is_labeled()) {
177             len = Mmsg(msg, _("\nDevice %s is %s:\n"
178                               "    Volume:      %s\n"
179                               "    Pool:        %s\n"
180                               "    Media type:  %s\n"),
181                dev->print_name(), 
182                dev->blocked()?_("waiting for"):_("mounted with"),
183                dev->VolHdr.VolumeName, 
184                dev->pool_name[0]?dev->pool_name:_("*unknown*"),
185                dev->device->media_type);
186             sendit(msg, len, sp);
187          } else {
188             len = Mmsg(msg, _("\nDevice %s open but no Bacula volume is currently mounted.\n"), 
189                dev->print_name());
190             sendit(msg, len, sp);
191          }
192          send_blocked_status(dev, sp);
193          if (dev->can_append()) {
194             bpb = dev->VolCatInfo.VolCatBlocks;
195             if (bpb <= 0) {
196                bpb = 1;
197             }
198             bpb = dev->VolCatInfo.VolCatBytes / bpb;
199             len = Mmsg(msg, _("    Total Bytes=%s Blocks=%s Bytes/block=%s\n"),
200                edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, b1),
201                edit_uint64_with_commas(dev->VolCatInfo.VolCatBlocks, b2),
202                edit_uint64_with_commas(bpb, b3));
203             sendit(msg, len, sp);
204          } else {  /* reading */
205             bpb = dev->VolCatInfo.VolCatReads;
206             if (bpb <= 0) {
207                bpb = 1;
208             }
209             if (dev->VolCatInfo.VolCatRBytes > 0) {
210                bpb = dev->VolCatInfo.VolCatRBytes / bpb;
211             } else {
212                bpb = 0;
213             }
214             len = Mmsg(msg, _("    Total Bytes Read=%s Blocks Read=%s Bytes/block=%s\n"),
215                edit_uint64_with_commas(dev->VolCatInfo.VolCatRBytes, b1),
216                edit_uint64_with_commas(dev->VolCatInfo.VolCatReads, b2),
217                edit_uint64_with_commas(bpb, b3));
218             sendit(msg, len, sp);
219          }
220          len = Mmsg(msg, _("    Positioned at File=%s Block=%s\n"),
221             edit_uint64_with_commas(dev->file, b1),
222             edit_uint64_with_commas(dev->block_num, b2));
223          sendit(msg, len, sp);
224
225       } else {
226          if (dev) {
227             len = Mmsg(msg, _("\nDevice %s is not open.\n"), dev->print_name());
228             sendit(msg, len, sp);
229             send_blocked_status(dev, sp);
230         } else {
231             len = Mmsg(msg, _("\nDevice \"%s\" is not open or does not exist.\n"), device->hdr.name);
232             sendit(msg, len, sp);
233          }
234       }
235
236       if (!sp->api) sendit("==\n", 4, sp);
237    }
238    if (!sp->api) sendit("====\n\n", 6, sp);
239 }
240
241 static void list_status_header(STATUS_PKT *sp)
242 {
243    char dt[MAX_TIME_LENGTH];
244    char b1[35], b2[35], b3[35], b4[35], b5[35];
245    POOL_MEM msg(PM_MESSAGE);
246    int len;
247
248    len = Mmsg(msg, _("%s Version: %s (%s) %s %s %s\n"), 
249               my_name, VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
250    sendit(msg, len, sp);
251
252    bstrftime_nc(dt, sizeof(dt), daemon_start_time);
253
254
255    len = Mmsg(msg, _("Daemon started %s. Jobs: run=%d, running=%d.\n"),
256         dt, num_jobs_run, job_count());
257    sendit(msg, len, sp);
258    len = Mmsg(msg, _(" Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
259          edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
260          edit_uint64_with_commas(sm_bytes, b2),
261          edit_uint64_with_commas(sm_max_bytes, b3),
262          edit_uint64_with_commas(sm_buffers, b4),
263          edit_uint64_with_commas(sm_max_buffers, b5));
264    sendit(msg, len, sp);
265    len = Mmsg(msg, " Sizes: boffset_t=%d size_t=%d int32_t=%d int64_t=%d "
266               "mode=%d,%d\n", 
267               (int)sizeof(boffset_t), (int)sizeof(size_t), (int)sizeof(int32_t),
268               (int)sizeof(int64_t), (int)DEVELOPER_MODE, (int)BEEF);
269    sendit(msg, len, sp);
270    if (bplugin_list->size() > 0) {
271       Plugin *plugin;
272       int len;
273       pm_strcpy(msg, " Plugin: ");
274       foreach_alist(plugin, bplugin_list) {
275          len = pm_strcat(msg, plugin->file);
276          if (len > 80) {
277             pm_strcat(msg, "\n   ");
278          } else {
279             pm_strcat(msg, " ");
280          }
281       }
282       len = pm_strcat(msg, "\n");
283       sendit(msg.c_str(), len, sp);
284    }
285 }
286
287 static void send_blocked_status(DEVICE *dev, STATUS_PKT *sp)
288 {
289    POOL_MEM msg(PM_MESSAGE);
290    int len;
291
292    if (!dev) {
293       len = Mmsg(msg, _("No DEVICE structure.\n\n"));
294       sendit(msg, len, sp);
295       return;
296    }
297    switch (dev->blocked()) {
298    case BST_UNMOUNTED:
299       len = Mmsg(msg, _("    Device is BLOCKED. User unmounted.\n"));
300       sendit(msg, len, sp);
301       break;
302    case BST_UNMOUNTED_WAITING_FOR_SYSOP:
303       len = Mmsg(msg, _("    Device is BLOCKED. User unmounted during wait for media/mount.\n"));
304       sendit(msg, len, sp);
305       break;
306    case BST_WAITING_FOR_SYSOP:
307       {
308          DCR *dcr;
309          bool found_jcr = false;
310          dev->Lock();
311          foreach_dlist(dcr, dev->attached_dcrs) {
312             if (dcr->jcr->JobStatus == JS_WaitMount) {
313                len = Mmsg(msg, _("    Device is BLOCKED waiting for mount of volume \"%s\",\n"
314                                  "       Pool:        %s\n"
315                                  "       Media type:  %s\n"),
316                           dcr->VolumeName,
317                           dcr->pool_name,
318                           dcr->media_type);
319                sendit(msg, len, sp);
320                found_jcr = true;
321             } else if (dcr->jcr->JobStatus == JS_WaitMedia) {
322                len = Mmsg(msg, _("    Device is BLOCKED waiting to create a volume for:\n"
323                                  "       Pool:        %s\n"
324                                  "       Media type:  %s\n"),
325                           dcr->pool_name,
326                           dcr->media_type);
327                sendit(msg, len, sp);
328                found_jcr = true;
329             }
330          }
331          dev->Unlock();
332          if (!found_jcr) {
333             len = Mmsg(msg, _("    Device is BLOCKED waiting for media.\n"));
334             sendit(msg, len, sp);
335          }
336       }
337       break;
338    case BST_DOING_ACQUIRE:
339       len = Mmsg(msg, _("    Device is being initialized.\n"));
340       sendit(msg, len, sp);
341       break;
342    case BST_WRITING_LABEL:
343       len = Mmsg(msg, _("    Device is blocked labeling a Volume.\n"));
344       sendit(msg, len, sp);
345       break;
346    default:
347       break;
348    }
349    /* Send autochanger slot status */
350    if (dev->is_autochanger()) {
351       if (dev->get_slot() > 0) {
352          len = Mmsg(msg, _("    Slot %d %s loaded in drive %d.\n"), 
353             dev->get_slot(), dev->is_open()?"is": "was last", dev->drive_index);
354          sendit(msg, len, sp);
355       } else if (dev->get_slot() <= 0) {
356          len = Mmsg(msg, _("    Drive %d is not loaded.\n"), dev->drive_index);
357          sendit(msg, len, sp);
358       }
359    }
360    if (debug_level > 1) {
361       send_device_status(dev, sp);
362    }
363 }
364
365 static void send_device_status(DEVICE *dev, STATUS_PKT *sp)
366 {
367    POOL_MEM msg(PM_MESSAGE);
368    int len;
369
370    if (debug_level > 5) {
371       len = Mmsg(msg, _("Configured device capabilities:\n"));
372       sendit(msg, len, sp);
373       len = Mmsg(msg, "  %sEOF %sBSR %sBSF %sFSR %sFSF %sEOM %sREM %sRACCESS %sAUTOMOUNT %sLABEL %sANONVOLS %sALWAYSOPEN\n",
374          dev->capabilities & CAP_EOF ? "" : "!", 
375          dev->capabilities & CAP_BSR ? "" : "!", 
376          dev->capabilities & CAP_BSF ? "" : "!", 
377          dev->capabilities & CAP_FSR ? "" : "!", 
378          dev->capabilities & CAP_FSF ? "" : "!", 
379          dev->capabilities & CAP_EOM ? "" : "!", 
380          dev->capabilities & CAP_REM ? "" : "!", 
381          dev->capabilities & CAP_RACCESS ? "" : "!",
382          dev->capabilities & CAP_AUTOMOUNT ? "" : "!", 
383          dev->capabilities & CAP_LABEL ? "" : "!", 
384          dev->capabilities & CAP_ANONVOLS ? "" : "!", 
385          dev->capabilities & CAP_ALWAYSOPEN ? "" : "!");
386       sendit(msg, len, sp);
387    }
388
389    len = Mmsg(msg, _("Device state:\n"));
390    sendit(msg, len, sp);
391    len = Mmsg(msg, "  %sOPENED %sTAPE %sLABEL %sMALLOC %sAPPEND %sREAD %sEOT %sWEOT %sEOF %sNEXTVOL %sSHORT %sMOUNTED\n", 
392       dev->is_open() ? "" : "!", 
393       dev->is_tape() ? "" : "!", 
394       dev->is_labeled() ? "" : "!", 
395       dev->state & ST_MALLOC ? "" : "!", 
396       dev->can_append() ? "" : "!", 
397       dev->can_read() ? "" : "!", 
398       dev->at_eot() ? "" : "!", 
399       dev->state & ST_WEOT ? "" : "!", 
400       dev->at_eof() ? "" : "!", 
401       dev->state & ST_NEXTVOL ? "" : "!", 
402       dev->state & ST_SHORT ? "" : "!", 
403       dev->state & ST_MOUNTED ? "" : "!");
404    sendit(msg, len, sp);
405    len = Mmsg(msg, _("  num_writers=%d reserves=%d block=%d\n"), dev->num_writers,
406               dev->num_reserved(), dev->blocked());
407    sendit(msg, len, sp);
408
409    len = Mmsg(msg, _("Attached Jobs: "));
410    sendit(msg, len, sp);
411    DCR *dcr = NULL; 
412    bool found = false;
413    dev->Lock();
414    foreach_dlist(dcr, dev->attached_dcrs) {
415       if (dcr->jcr) {
416          if (found) {
417             sendit(",", 1, sp);
418          }
419          len = Mmsg(msg, "%d", (int)dcr->jcr->JobId);
420          sendit(msg, len, sp);
421          found = true;
422       }
423    }
424    dev->Unlock();
425    sendit("\n", 1, sp);
426
427    len = Mmsg(msg, _("Device parameters:\n"));
428    sendit(msg, len, sp);
429    len = Mmsg(msg, _("  Archive name: %s Device name: %s\n"), dev->archive_name(),
430       dev->name());
431    sendit(msg, len, sp);
432    len = Mmsg(msg, _("  File=%u block=%u\n"), dev->file, dev->block_num);
433    sendit(msg, len, sp);
434    len = Mmsg(msg, _("  Min block=%u Max block=%u\n"), dev->min_block_size, dev->max_block_size);
435    sendit(msg, len, sp);
436 }
437
438 static void list_running_jobs(STATUS_PKT *sp)
439 {
440    bool found = false;
441    int avebps, bps, sec;
442    JCR *jcr;
443    DCR *dcr, *rdcr;
444    char JobName[MAX_NAME_LENGTH];
445    char b1[50], b2[50], b3[50], b4[50];
446    int len;
447    POOL_MEM msg(PM_MESSAGE);
448    time_t now = time(NULL);
449
450    len = Mmsg(msg, _("\nRunning Jobs:\n"));
451    if (!sp->api) sendit(msg, len, sp);
452
453    foreach_jcr(jcr) {
454       if (jcr->JobStatus == JS_WaitFD) {
455          len = Mmsg(msg, _("%s Job %s waiting for Client connection.\n"),
456             job_type_to_str(jcr->getJobType()), jcr->Job);
457          sendit(msg, len, sp);
458       }
459       dcr = jcr->dcr;
460       rdcr = jcr->read_dcr;
461       if ((dcr && dcr->device) || (rdcr && rdcr->device)) {
462          bstrncpy(JobName, jcr->Job, sizeof(JobName));
463          /* There are three periods after the Job name */
464          char *p;
465          for (int i=0; i<3; i++) {
466             if ((p=strrchr(JobName, '.')) != NULL) {
467                *p = 0;
468             }
469          }
470          if (rdcr && rdcr->device) {
471             len = Mmsg(msg, _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
472                             "    pool=\"%s\" device=%s\n"),
473                    job_level_to_str(jcr->getJobLevel()),
474                    job_type_to_str(jcr->getJobType()),
475                    JobName,
476                    jcr->JobId,
477                    rdcr->VolumeName,
478                    rdcr->pool_name,
479                    rdcr->dev?rdcr->dev->print_name(): 
480                             rdcr->device->device_name);
481             sendit(msg, len, sp);
482          }
483          if (dcr && dcr->device) {
484             len = Mmsg(msg, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
485                             "    pool=\"%s\" device=%s\n"),
486                    job_level_to_str(jcr->getJobLevel()),
487                    job_type_to_str(jcr->getJobType()),
488                    JobName,
489                    jcr->JobId,
490                    dcr->VolumeName,
491                    dcr->pool_name,
492                    dcr->dev?dcr->dev->print_name(): 
493                             dcr->device->device_name);
494             sendit(msg, len, sp);
495             len= Mmsg(msg, _("    spooling=%d despooling=%d despool_wait=%d\n"),
496                    dcr->spooling, dcr->despooling, dcr->despool_wait);
497             sendit(msg, len, sp);
498          }
499          if (jcr->last_time == 0) {
500             jcr->last_time = jcr->run_time;
501          }
502          sec = now - jcr->last_time;
503          if (sec <= 0) {
504             sec = 1;
505          }
506          bps = (jcr->JobBytes - jcr->LastJobBytes) / sec;
507          if (jcr->LastRate == 0) {
508             jcr->LastRate = bps;
509          }
510          avebps = (jcr->LastRate + bps) / 2;
511          len = Mmsg(msg, _("    Files=%s Bytes=%s AveBytes/sec=%s LastBytes/sec=%s\n"),
512             edit_uint64_with_commas(jcr->JobFiles, b1),
513             edit_uint64_with_commas(jcr->JobBytes, b2),
514             edit_uint64_with_commas(avebps, b3),
515             edit_uint64_with_commas(bps, b4));
516          sendit(msg, len, sp);
517          jcr->LastRate = avebps;
518          jcr->LastJobBytes = jcr->JobBytes;
519          jcr->last_time = now;
520          found = true;
521 #ifdef DEBUG
522          if (jcr->file_bsock) {
523             len = Mmsg(msg, _("    FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
524                edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
525                jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
526                jcr->file_bsock->m_fd);
527             sendit(msg, len, sp);
528          } else {
529             len = Mmsg(msg, _("    FDSocket closed\n"));
530             sendit(msg, len, sp);
531          }
532 #endif
533       }
534    }
535    endeach_jcr(jcr);
536
537    if (!found) {
538       len = Mmsg(msg, _("No Jobs running.\n"));
539       if (!sp->api) sendit(msg, len, sp);
540    }
541    if (!sp->api) sendit("====\n", 5, sp);
542 }
543
544 static void list_jobs_waiting_on_reservation(STATUS_PKT *sp)
545
546    JCR *jcr;
547    POOL_MEM msg(PM_MESSAGE);
548    int len;
549
550    len = Mmsg(msg, _("\nJobs waiting to reserve a drive:\n"));
551    if (!sp->api) sendit(msg, len, sp);
552
553    foreach_jcr(jcr) {
554       if (!jcr->reserve_msgs) {
555          continue;
556       }
557       send_drive_reserve_messages(jcr, sendit, sp);
558    }
559    endeach_jcr(jcr);
560
561    if (!sp->api) sendit("====\n", 5, sp);
562 }
563
564
565 static void list_terminated_jobs(STATUS_PKT *sp)
566 {
567    char dt[MAX_TIME_LENGTH], b1[30], b2[30];
568    char level[10];
569    struct s_last_job *je;
570    const char *msg;
571
572    msg =  _("\nTerminated Jobs:\n");
573    if (!sp->api) sendit(msg, strlen(msg), sp);
574    if (last_jobs->size() == 0) {
575       if (!sp->api) sendit("====\n", 5, sp);
576       return;
577    }
578    lock_last_jobs_list();
579    msg =  _(" JobId  Level    Files      Bytes   Status   Finished        Name \n");
580    if (!sp->api) sendit(msg, strlen(msg), sp);
581    msg =  _("===================================================================\n");
582    if (!sp->api) sendit(msg, strlen(msg), sp);
583    foreach_dlist(je, last_jobs) {
584       char JobName[MAX_NAME_LENGTH];
585       const char *termstat;
586       char buf[1000];
587
588       bstrftime_nc(dt, sizeof(dt), je->end_time);
589       switch (je->JobType) {
590       case JT_ADMIN:
591       case JT_RESTORE:
592          bstrncpy(level, "    ", sizeof(level));
593          break;
594       default:
595          bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
596          level[4] = 0;
597          break;
598       }
599       switch (je->JobStatus) {
600       case JS_Created:
601          termstat = _("Created");
602          break;
603       case JS_FatalError:
604       case JS_ErrorTerminated:
605          termstat = _("Error");
606          break;
607       case JS_Differences:
608          termstat = _("Diffs");
609          break;
610       case JS_Canceled:
611          termstat = _("Cancel");
612          break;
613       case JS_Terminated:
614          termstat = _("OK");
615          break;
616       case JS_Warnings:
617          termstat = _("OK -- with warnings");
618          break;
619       default:
620          termstat = _("Other");
621          break;
622       }
623       bstrncpy(JobName, je->Job, sizeof(JobName));
624       /* There are three periods after the Job name */
625       char *p;
626       for (int i=0; i<3; i++) {
627          if ((p=strrchr(JobName, '.')) != NULL) {
628             *p = 0;
629          }
630       }
631       if (sp->api) {
632          bsnprintf(buf, sizeof(buf), _("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"),
633             je->JobId,
634             level,
635             edit_uint64_with_commas(je->JobFiles, b1),
636             edit_uint64_with_suffix(je->JobBytes, b2),
637             termstat,
638             dt, JobName);
639       } else {
640          bsnprintf(buf, sizeof(buf), _("%6d  %-6s %8s %10s  %-7s  %-8s %s\n"),
641             je->JobId,
642             level,
643             edit_uint64_with_commas(je->JobFiles, b1),
644             edit_uint64_with_suffix(je->JobBytes, b2),
645             termstat,
646             dt, JobName);
647       }  
648       sendit(buf, strlen(buf), sp);
649    }
650    unlock_last_jobs_list();
651    if (!sp->api) sendit("====\n", 5, sp);
652 }
653
654 /*
655  * Convert Job Level into a string
656  */
657 static const char *level_to_str(int level)
658 {
659    const char *str;
660
661    switch (level) {
662    case L_BASE:
663       str = _("Base");
664    case L_FULL:
665       str = _("Full");
666       break;
667    case L_INCREMENTAL:
668       str = _("Incremental");
669       break;
670    case L_DIFFERENTIAL:
671       str = _("Differential");
672       break;
673    case L_SINCE:
674       str = _("Since");
675       break;
676    case L_VERIFY_CATALOG:
677       str = _("Verify Catalog");
678       break;
679    case L_VERIFY_INIT:
680       str = _("Init Catalog");
681       break;
682    case L_VERIFY_VOLUME_TO_CATALOG:
683       str = _("Volume to Catalog");
684       break;
685    case L_VERIFY_DISK_TO_CATALOG:
686       str = _("Disk to Catalog");
687       break;
688    case L_VERIFY_DATA:
689       str = _("Data");
690       break;
691    case L_NONE:
692       str = " ";
693       break;
694    default:
695       str = _("Unknown Job Level");
696       break;
697    }
698    return str;
699 }
700
701 /*
702  * Send to Director
703  */
704 static void sendit(const char *msg, int len, STATUS_PKT *sp)
705 {
706    BSOCK *bs = sp->bs;
707    if (bs) {
708       memcpy(bs->msg, msg, len+1);
709       bs->msglen = len+1;
710       bs->send();
711    } else {
712       sp->callback(msg, len, sp->context);
713    }
714 }
715
716 static void sendit(const char *msg, int len, void *sp)
717 {
718    sendit(msg, len, (STATUS_PKT *)sp);
719 }
720
721 static void sendit(POOL_MEM &msg, int len, STATUS_PKT *sp)
722 {
723    BSOCK *bs = sp->bs;
724    if (bs) {
725       memcpy(bs->msg, msg.c_str(), len+1);
726       bs->msglen = len+1;
727       bs->send();
728    } else {
729       sp->callback(msg.c_str(), len, sp->context);
730    }
731 }
732
733
734 /*
735  * Status command from Director
736  */
737 bool status_cmd(JCR *jcr)
738 {
739    BSOCK *dir = jcr->dir_bsock;
740    STATUS_PKT sp;
741
742    dir->fsend("\n");
743    sp.bs = dir;
744    output_status(&sp);
745    dir->signal(BNET_EOD);
746    return true;
747 }
748
749 /*
750  * .status command from Director
751  */
752 bool qstatus_cmd(JCR *jcr)
753 {
754    BSOCK *dir = jcr->dir_bsock;
755    POOL_MEM cmd;
756    JCR *njcr;
757    s_last_job* job;
758    STATUS_PKT sp;
759
760    sp.bs = dir;
761    if (sscanf(dir->msg, qstatus, cmd.c_str()) != 1) {
762       pm_strcpy(jcr->errmsg, dir->msg);
763       dir->fsend(_("3900 No arg in .status command: %s\n"), jcr->errmsg);
764       dir->signal(BNET_EOD);
765       return false;
766    }
767    unbash_spaces(cmd);
768
769    Dmsg1(200, "cmd=%s\n", cmd.c_str());
770
771    if (strcasecmp(cmd.c_str(), "current") == 0) {
772       dir->fsend(OKqstatus, cmd.c_str());
773       foreach_jcr(njcr) {
774          if (njcr->JobId != 0) {
775             dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
776          }
777       }
778       endeach_jcr(njcr);
779    } else if (strcasecmp(cmd.c_str(), "last") == 0) {
780       dir->fsend(OKqstatus, cmd.c_str());
781       if ((last_jobs) && (last_jobs->size() > 0)) {
782          job = (s_last_job*)last_jobs->last();
783          dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
784       }
785    } else if (strcasecmp(cmd.c_str(), "header") == 0) {
786        sp.api = true;
787        list_status_header(&sp);
788    } else if (strcasecmp(cmd.c_str(), "running") == 0) {
789        sp.api = true;
790        list_running_jobs(&sp);
791    } else if (strcasecmp(cmd.c_str(), "waitreservation") == 0) {
792        sp.api = true;
793        list_jobs_waiting_on_reservation(&sp);
794    } else if (strcasecmp(cmd.c_str(), "devices") == 0) {
795        sp.api = true;
796        list_devices(&sp);
797    } else if (strcasecmp(cmd.c_str(), "volumes") == 0) {
798        sp.api = true;
799        list_volumes(sendit, &sp);
800    } else if (strcasecmp(cmd.c_str(), "spooling") == 0) {
801        sp.api = true;
802        list_spool_stats(sendit, &sp);
803    } else if (strcasecmp(cmd.c_str(), "terminated") == 0) {
804        sp.api = true;
805        list_terminated_jobs(&sp);
806    } else if (strcasecmp(cmd.c_str(), "resources") == 0) {
807        sp.api = true;
808        list_resources(&sp);
809    } else {
810       pm_strcpy(jcr->errmsg, dir->msg);
811       dir->fsend(_("3900 Unknown arg in .status command: %s\n"), jcr->errmsg);
812       dir->signal(BNET_EOD);
813       return false;
814    }
815    dir->signal(BNET_EOD);
816    return true;
817 }
818
819 #if defined(HAVE_WIN32)
820 int bacstat = 0;
821
822 /* Return a one line status for the tray monitor */
823 char *bac_status(char *buf, int buf_len)
824 {
825    JCR *njcr;
826    const char *termstat = _("Bacula Storage: Idle");
827    struct s_last_job *job;
828    int stat = 0;                      /* Idle */
829
830    if (!last_jobs) {
831       goto done;
832    }
833    Dmsg0(1000, "Begin bac_status jcr loop.\n");
834    foreach_jcr(njcr) {
835       if (njcr->JobId != 0) {
836          stat = JS_Running;
837          termstat = _("Bacula Storage: Running");
838          break;
839       }
840    }
841    endeach_jcr(njcr);
842
843    if (stat != 0) {
844       goto done;
845    }
846    if (last_jobs->size() > 0) {
847       job = (struct s_last_job *)last_jobs->last();
848       stat = job->JobStatus;
849       switch (job->JobStatus) {
850       case JS_Canceled:
851          termstat = _("Bacula Storage: Last Job Canceled");
852          break;
853       case JS_ErrorTerminated:
854       case JS_FatalError:
855          termstat = _("Bacula Storage: Last Job Failed");
856          break;
857       default:
858          if (job->Errors) {
859             termstat = _("Bacula Storage: Last Job had Warnings");
860          }
861          break;
862       }
863    }
864    Dmsg0(1000, "End bac_status jcr loop.\n");
865 done:
866    bacstat = stat;
867    if (buf) {
868       bstrncpy(buf, termstat, buf_len);
869    }
870    return buf;
871 }
872
873 #endif /* HAVE_WIN32 */