]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/status.c
2a8b7e6bf81627bab13884030c7edb346187300e
[bacula/bacula] / bacula / src / stored / status.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2015 Kern Sibbald
5    Copyright (C) 2003-2014 Free Software Foundation Europe e.V.
6
7    The original author of Bacula is Kern Sibbald, with contributions
8    from many others, a complete list can be found in the file AUTHORS.
9
10    You may use this file and others of this release according to the
11    license defined in the LICENSE file, which includes the Affero General
12    Public License, v3.0 ("AGPLv3") and some additional permissions and
13    terms pursuant to its AGPLv3 Section 7.
14
15    This notice must be preserved when any source code is 
16    conveyed and/or propagated.
17
18    Bacula(R) is a registered trademark of Kern Sibbald.
19 */
20 /*
21  *  This file handles the status command
22  *
23  *     Kern Sibbald, May MMIII
24  *
25  *
26  */
27
28 #include "bacula.h"
29 #include "stored.h"
30 #include "lib/status.h"
31 #include "sd_plugins.h"
32
33 /* Imported functions */
34 extern void dbg_print_plugin(FILE *fp);
35
36 /* Imported variables */
37 extern BSOCK *filed_chan;
38 extern void *start_heap;
39
40 /* Static variables */
41 static char OKqstatus[]   = "3000 OK .status\n";
42 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
43
44
45 /* Forward referenced functions */
46 static void sendit(POOL_MEM &msg, int len, STATUS_PKT *sp);
47 static void sendit(const char *msg, int len, void *arg);
48 static void dbg_sendit(const char *msg, int len, void *arg);
49 static void send_blocked_status(DEVICE *dev, STATUS_PKT *sp);
50 static void send_device_status(DEVICE *dev, STATUS_PKT *sp);
51 static void list_running_jobs(STATUS_PKT *sp);
52 static void list_jobs_waiting_on_reservation(STATUS_PKT *sp);
53 static void list_status_header(STATUS_PKT *sp);
54 static void list_devices(STATUS_PKT *sp, char *name=NULL);
55 static void list_plugins(STATUS_PKT *sp);
56
57 /*
58  * Status command from Director
59  */
60 void output_status(STATUS_PKT *sp)
61 {
62    POOL_MEM msg(PM_MESSAGE);
63    int len;
64
65    list_status_header(sp);
66
67    /*
68     * List running jobs
69     */
70    list_running_jobs(sp);
71
72    /*
73     * List jobs stuck in reservation system
74     */
75    list_jobs_waiting_on_reservation(sp);
76
77    /*
78     * List terminated jobs (defined in lib/status.h)
79     */
80    list_terminated_jobs(sp);
81
82    /*
83     * List devices
84     */
85    list_devices(sp);
86
87
88    len = Mmsg(msg, _("Used Volume status:\n"));
89    if (!sp->api) sendit(msg, len, sp);
90
91    list_volumes(sendit, (void *)sp);
92    if (!sp->api) sendit("====\n\n", 6, sp);
93
94
95    list_spool_stats(sendit, (void *)sp);
96
97    if (!sp->api) sendit("====\n\n", 6, sp);
98
99    if (chk_dbglvl(10)) {
100       dbg_print_plugin(stdout);
101    }
102 }
103
104 static void list_resources(STATUS_PKT *sp)
105 {
106 #ifdef when_working
107    POOL_MEM msg(PM_MESSAGE);
108    int len;
109
110    len = Mmsg(msg, _("\nSD Resources:\n"));
111    if (!sp->api) sendit(msg, len, sp);
112    dump_resource(R_DEVICE, resources[R_DEVICE-r_first], sp);
113    if (!sp->api) sendit("====\n\n", 6, sp);
114 #endif
115 }
116
117 #ifdef xxxx
118 static find_device(char *devname)
119 {
120    foreach_res(device, R_DEVICE) {
121       if (strcasecmp(device->hdr.name, devname) == 0) {
122          found = true;
123          break;
124       }
125    }
126    if (!found) {
127       foreach_res(changer, R_AUTOCHANGER) {
128          if (strcasecmp(changer->hdr.name, devname) == 0) {
129             break;
130          }
131       }
132    }
133 }
134 #endif
135
136
137 static void list_one_device(char *name, DEVICE *dev, STATUS_PKT *sp)
138 {
139    char b1[35], b2[35], b3[35];
140    POOL_MEM msg(PM_MESSAGE);
141    int len;
142    int bpb;
143
144    if (!dev) {
145       len = Mmsg(msg, _("\nDevice \"%s\" is not open or does not exist.\n"),
146                  name);
147       sendit(msg, len, sp);
148       if (!sp->api) sendit("==\n", 4, sp);
149       return;
150    }
151
152    if (dev->is_open()) {
153       if (dev->is_labeled()) {
154          len = Mmsg(msg, _("\nDevice %s is %s %s:\n"
155                            "    Volume:      %s\n"
156                            "    Pool:        %s\n"
157                            "    Media type:  %s\n"),
158             dev->print_type(), dev->print_name(),
159             dev->blocked()?_("waiting for"):_("mounted with"),
160             dev->VolHdr.VolumeName,
161             dev->pool_name[0]?dev->pool_name:_("*unknown*"),
162             dev->device->media_type);
163          sendit(msg, len, sp);
164       } else {
165          len = Mmsg(msg, _("\nDevice %s: %s open but no Bacula volume is currently mounted.\n"),
166             dev->print_type(), dev->print_name());
167          sendit(msg, len, sp);
168       }
169       send_blocked_status(dev, sp);
170       if (dev->can_append()) {
171          bpb = dev->VolCatInfo.VolCatBlocks;
172          if (bpb <= 0) {
173             bpb = 1;
174          }
175          bpb = dev->VolCatInfo.VolCatBytes / bpb;
176          len = Mmsg(msg, _("    Total Bytes=%s Blocks=%s Bytes/block=%s\n"),
177             edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, b1),
178             edit_uint64_with_commas(dev->VolCatInfo.VolCatBlocks, b2),
179             edit_uint64_with_commas(bpb, b3));
180          sendit(msg, len, sp);
181       } else {  /* reading */
182          bpb = dev->VolCatInfo.VolCatReads;
183          if (bpb <= 0) {
184             bpb = 1;
185          }
186          if (dev->VolCatInfo.VolCatRBytes > 0) {
187             bpb = dev->VolCatInfo.VolCatRBytes / bpb;
188          } else {
189             bpb = 0;
190          }
191          len = Mmsg(msg, _("    Total Bytes Read=%s Blocks Read=%s Bytes/block=%s\n"),
192             edit_uint64_with_commas(dev->VolCatInfo.VolCatRBytes, b1),
193             edit_uint64_with_commas(dev->VolCatInfo.VolCatReads, b2),
194             edit_uint64_with_commas(bpb, b3));
195          sendit(msg, len, sp);
196       }
197       len = Mmsg(msg, _("    Positioned at File=%s Block=%s\n"),
198          edit_uint64_with_commas(dev->file, b1),
199          edit_uint64_with_commas(dev->block_num, b2));
200       sendit(msg, len, sp);
201
202    } else {
203       len = Mmsg(msg, _("\nDevice %s: %s is not open.\n"),
204                  dev->print_type(), dev->print_name());
205       sendit(msg, len, sp);
206       send_blocked_status(dev, sp);
207    }
208
209    /* TODO: We need to check with Mount command, maybe we can
210     * display this number only when the device is open.
211     */
212    if (dev->is_file()) {
213       char ed1[50];
214       uint64_t f, t;
215       dev->get_freespace(&f, &t);
216       if (t > 0) {              /* We might not have access to numbers */
217          len = Mmsg(msg, _("    Available Space=%sB\n"),
218                     edit_uint64_with_suffix(f, ed1));
219          sendit(msg, len, sp);
220       }
221    }
222
223    if (!sp->api) sendit("==\n", 4, sp);
224 }
225
226 void _dbg_list_one_device(char *name, DEVICE *dev, const char *file, int line)
227 {
228    STATUS_PKT sp;
229    sp.bs = NULL;
230    sp.callback = dbg_sendit;
231    sp.context = NULL;
232    d_msg(file, line, 0, "Called dbg_list_one_device():");
233    list_one_device(name, dev, &sp);
234    send_device_status(dev, &sp);
235 }
236
237 static void list_one_autochanger(char *name, AUTOCHANGER *changer, STATUS_PKT *sp)
238 {
239    int     len;
240    DEVRES *device;
241    POOL_MEM msg(PM_MESSAGE);
242
243    len = Mmsg(msg, _("Autochanger \"%s\" with devices:\n"),
244               changer->hdr.name);
245    sendit(msg, len, sp);
246
247    foreach_alist(device, changer->device) {
248       if (device->dev) {
249          len = Mmsg(msg, "   %s\n", device->dev->print_name());
250          sendit(msg, len, sp);
251       } else {
252          len = Mmsg(msg, "   %s\n", device->hdr.name);
253          sendit(msg, len, sp);
254       }
255    }
256 }
257
258 static void list_devices(STATUS_PKT *sp, char *name)
259 {
260    int len;
261    DEVRES *device;
262    AUTOCHANGER *changer;
263    POOL_MEM msg(PM_MESSAGE);
264
265    if (!sp->api) {
266       len = Mmsg(msg, _("\nDevice status:\n"));
267       sendit(msg, len, sp);
268    }
269
270    foreach_res(changer, R_AUTOCHANGER) {
271       if (!name || strcmp(changer->hdr.name, name) == 0) {
272          list_one_autochanger(changer->hdr.name, changer, sp);
273       }
274    }
275
276    foreach_res(device, R_DEVICE) {
277       if (!name || strcmp(device->hdr.name, name) == 0) {
278          list_one_device(device->hdr.name, device->dev, sp);
279       }
280    }
281    if (!sp->api) sendit("====\n\n", 6, sp);
282 }
283
284 static void list_status_header(STATUS_PKT *sp)
285 {
286    char dt[MAX_TIME_LENGTH];
287    char b1[35], b2[35], b3[35], b4[35], b5[35];
288    POOL_MEM msg(PM_MESSAGE);
289    int len;
290
291    len = Mmsg(msg, _("%s %sVersion: %s (%s) %s %s %s\n"),
292               my_name, "", VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
293    sendit(msg, len, sp);
294
295    bstrftime_nc(dt, sizeof(dt), daemon_start_time);
296
297
298    len = Mmsg(msg, _("Daemon started %s. Jobs: run=%d, running=%d.\n"),
299         dt, num_jobs_run, job_count());
300    sendit(msg, len, sp);
301    len = Mmsg(msg, _(" Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
302          edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
303          edit_uint64_with_commas(sm_bytes, b2),
304          edit_uint64_with_commas(sm_max_bytes, b3),
305          edit_uint64_with_commas(sm_buffers, b4),
306          edit_uint64_with_commas(sm_max_buffers, b5));
307    sendit(msg, len, sp);
308    len = Mmsg(msg, " Sizes: boffset_t=%d size_t=%d int32_t=%d int64_t=%d "
309               "mode=%d,%d\n",
310               (int)sizeof(boffset_t), (int)sizeof(size_t), (int)sizeof(int32_t),
311               (int)sizeof(int64_t), (int)DEVELOPER_MODE, (int)0);
312    sendit(msg, len, sp);
313    list_plugins(sp);
314 }
315
316 static void send_blocked_status(DEVICE *dev, STATUS_PKT *sp)
317 {
318    POOL_MEM msg(PM_MESSAGE);
319    int len;
320
321    if (!dev) {
322       len = Mmsg(msg, _("No DEVICE structure.\n\n"));
323       sendit(msg, len, sp);
324       return;
325    }
326    if (!dev->enabled) {
327       len = Mmsg(msg, _("    Device is disabled. User command.\n"));
328       sendit(msg, len, sp);
329    }
330    switch (dev->blocked()) {
331    case BST_UNMOUNTED:
332       len = Mmsg(msg, _("    Device is BLOCKED. User unmounted.\n"));
333       sendit(msg, len, sp);
334       break;
335    case BST_UNMOUNTED_WAITING_FOR_SYSOP:
336       len = Mmsg(msg, _("    Device is BLOCKED. User unmounted during wait for media/mount.\n"));
337       sendit(msg, len, sp);
338       break;
339    case BST_WAITING_FOR_SYSOP:
340       {
341          DCR *dcr;
342          bool found_jcr = false;
343          dev->Lock();
344          dev->Lock_dcrs();
345          foreach_dlist(dcr, dev->attached_dcrs) {
346             if (dcr->jcr->JobStatus == JS_WaitMount) {
347                len = Mmsg(msg, _("    Device is BLOCKED waiting for mount of volume \"%s\",\n"
348                                  "       Pool:        %s\n"
349                                  "       Media type:  %s\n"),
350                           dcr->VolumeName,
351                           dcr->pool_name,
352                           dcr->media_type);
353                sendit(msg, len, sp);
354                found_jcr = true;
355             } else if (dcr->jcr->JobStatus == JS_WaitMedia) {
356                len = Mmsg(msg, _("    Device is BLOCKED waiting to create a volume for:\n"
357                                  "       Pool:        %s\n"
358                                  "       Media type:  %s\n"),
359                           dcr->pool_name,
360                           dcr->media_type);
361                sendit(msg, len, sp);
362                found_jcr = true;
363             }
364          }
365          dev->Unlock_dcrs();
366          dev->Unlock();
367          if (!found_jcr) {
368             len = Mmsg(msg, _("    Device is BLOCKED waiting for media.\n"));
369             sendit(msg, len, sp);
370          }
371       }
372       break;
373    case BST_DOING_ACQUIRE:
374       len = Mmsg(msg, _("    Device is being initialized.\n"));
375       sendit(msg, len, sp);
376       break;
377    case BST_WRITING_LABEL:
378       len = Mmsg(msg, _("    Device is blocked labeling a Volume.\n"));
379       sendit(msg, len, sp);
380       break;
381    default:
382       break;
383    }
384    /* Send autochanger slot status */
385    if (dev->is_autochanger()) {
386       if (dev->get_slot() > 0) {
387          len = Mmsg(msg, _("    Slot %d %s loaded in drive %d.\n"),
388             dev->get_slot(), dev->is_open()?"is": "was last", dev->drive_index);
389          sendit(msg, len, sp);
390       } else if (dev->get_slot() <= 0) {
391          len = Mmsg(msg, _("    Drive %d is not loaded.\n"), dev->drive_index);
392          sendit(msg, len, sp);
393       }
394    }
395    if (chk_dbglvl(1)) {
396       send_device_status(dev, sp);
397    }
398 }
399
400 void send_device_status(DEVICE *dev, STATUS_PKT *sp)
401 {
402    POOL_MEM msg(PM_MESSAGE);
403    int len;
404    DCR *dcr = NULL;
405    bool found = false;
406
407    if (chk_dbglvl(5)) {
408       len = Mmsg(msg, _("Configured device capabilities:\n"));
409       sendit(msg, len, sp);
410       len = Mmsg(msg, "  %sEOF %sBSR %sBSF %sFSR %sFSF %sEOM %sREM %sRACCESS %sAUTOMOUNT %sLABEL %sANONVOLS %sALWAYSOPEN\n",
411          dev->capabilities & CAP_EOF ? "" : "!",
412          dev->capabilities & CAP_BSR ? "" : "!",
413          dev->capabilities & CAP_BSF ? "" : "!",
414          dev->capabilities & CAP_FSR ? "" : "!",
415          dev->capabilities & CAP_FSF ? "" : "!",
416          dev->capabilities & CAP_EOM ? "" : "!",
417          dev->capabilities & CAP_REM ? "" : "!",
418          dev->capabilities & CAP_RACCESS ? "" : "!",
419          dev->capabilities & CAP_AUTOMOUNT ? "" : "!",
420          dev->capabilities & CAP_LABEL ? "" : "!",
421          dev->capabilities & CAP_ANONVOLS ? "" : "!",
422          dev->capabilities & CAP_ALWAYSOPEN ? "" : "!");
423       sendit(msg, len, sp);
424    }
425
426    len = Mmsg(msg, _("Device state:\n"));
427    sendit(msg, len, sp);
428    len = Mmsg(msg, "  %sOPENED %sTAPE %sLABEL %sMALLOC %sAPPEND %sREAD %sEOT %sWEOT %sEOF %sNEXTVOL %sSHORT %sMOUNTED\n",
429       dev->is_open() ? "" : "!",
430       dev->is_tape() ? "" : "!",
431       dev->is_labeled() ? "" : "!",
432       dev->state & ST_MALLOC ? "" : "!",
433       dev->can_append() ? "" : "!",
434       dev->can_read() ? "" : "!",
435       dev->at_eot() ? "" : "!",
436       dev->state & ST_WEOT ? "" : "!",
437       dev->at_eof() ? "" : "!",
438       dev->state & ST_NEXTVOL ? "" : "!",
439       dev->state & ST_SHORT ? "" : "!",
440       dev->state & ST_MOUNTED ? "" : "!");
441    sendit(msg, len, sp);
442    len = Mmsg(msg, _("  num_writers=%d reserves=%d block=%d enabled=%d\n"), dev->num_writers,
443               dev->num_reserved(), dev->blocked(), dev->enabled);
444    sendit(msg, len, sp);
445
446    len = Mmsg(msg, _("Attached JobIds: "));
447    sendit(msg, len, sp);
448    dev->Lock();
449    dev->Lock_dcrs();
450    foreach_dlist(dcr, dev->attached_dcrs) {
451       if (dcr->jcr) {
452          if (found) {
453             sendit(",", 1, sp);
454          }
455          len = Mmsg(msg, "%d", (int)dcr->jcr->JobId);
456          sendit(msg, len, sp);
457          found = true;
458       }
459    }
460    dev->Unlock_dcrs();
461    dev->Unlock();
462    sendit("\n", 1, sp);
463
464    len = Mmsg(msg, _("Device parameters:\n"));
465    sendit(msg, len, sp);
466    len = Mmsg(msg, _("  Archive name: %s Device name: %s\n"), dev->archive_name(),
467       dev->name());
468    sendit(msg, len, sp);
469    len = Mmsg(msg, _("  File=%u block=%u\n"), dev->file, dev->block_num);
470    sendit(msg, len, sp);
471    len = Mmsg(msg, _("  Min block=%u Max block=%u\n"), dev->min_block_size, dev->max_block_size);
472    sendit(msg, len, sp);
473 }
474
475 static void list_running_jobs(STATUS_PKT *sp)
476 {
477    bool found = false;
478    uint64_t inst_bps, total_bps;
479    int inst_sec, total_sec;
480    JCR *jcr;
481    DCR *dcr, *rdcr;
482    char JobName[MAX_NAME_LENGTH];
483    char b1[50], b2[50], b3[50], b4[50];
484    int len;
485    POOL_MEM msg(PM_MESSAGE);
486    time_t now = time(NULL);
487
488    len = Mmsg(msg, _("\nRunning Jobs:\n"));
489    if (!sp->api) sendit(msg, len, sp);
490
491    foreach_jcr(jcr) {
492       if (jcr->JobStatus == JS_WaitFD) {
493          len = Mmsg(msg, _("%s Job %s waiting for Client connection.\n"),
494             job_type_to_str(jcr->getJobType()), jcr->Job);
495          sendit(msg, len, sp);
496       }
497       dcr = jcr->dcr;
498       rdcr = jcr->read_dcr;
499       if ((dcr && dcr->device) || (rdcr && rdcr->device)) {
500          bstrncpy(JobName, jcr->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          if (rdcr && rdcr->device) {
509             len = Mmsg(msg, _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
510                             "    pool=\"%s\" device=%s\n"),
511                    job_level_to_str(jcr->getJobLevel()),
512                    job_type_to_str(jcr->getJobType()),
513                    JobName,
514                    jcr->JobId,
515                    rdcr->VolumeName,
516                    rdcr->pool_name,
517                    rdcr->dev?rdcr->dev->print_name():
518                             rdcr->device->device_name);
519             sendit(msg, len, sp);
520          } else if (dcr && dcr->device) {
521             len = Mmsg(msg, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
522                             "    pool=\"%s\" device=%s\n"),
523                    job_level_to_str(jcr->getJobLevel()),
524                    job_type_to_str(jcr->getJobType()),
525                    JobName,
526                    jcr->JobId,
527                    dcr->VolumeName,
528                    dcr->pool_name,
529                    dcr->dev?dcr->dev->print_name():
530                             dcr->device->device_name);
531             sendit(msg, len, sp);
532             len= Mmsg(msg, _("    spooling=%d despooling=%d despool_wait=%d\n"),
533                    dcr->spooling, dcr->despooling, dcr->despool_wait);
534             sendit(msg, len, sp);
535          }
536          if (jcr->last_time == 0) {
537             jcr->last_time = jcr->run_time;
538          }
539          total_sec = now - jcr->run_time;
540          inst_sec = now - jcr->last_time;
541          if (total_sec <= 0) {
542             total_sec = 1;
543          }
544          if (inst_sec <= 0) {
545             inst_sec = 1;
546          }
547          /* Instanteous bps not smoothed */
548          inst_bps = (jcr->JobBytes - jcr->LastJobBytes) / inst_sec;
549          if (jcr->LastRate == 0) {
550             jcr->LastRate = inst_bps;
551          }
552          /* Smooth the instantaneous bps a bit */
553          inst_bps = (2 * jcr->LastRate + inst_bps) / 3;
554          /* total bps (AveBytes/sec) since start of job */
555          total_bps = jcr->JobBytes / total_sec;
556          len = Mmsg(msg, _("    Files=%s Bytes=%s AveBytes/sec=%s LastBytes/sec=%s\n"),
557             edit_uint64_with_commas(jcr->JobFiles, b1),
558             edit_uint64_with_commas(jcr->JobBytes, b2),
559             edit_uint64_with_commas(total_bps, b3),
560             edit_uint64_with_commas(inst_bps, b4));
561          sendit(msg, len, sp);
562          /* Update only every 10 seconds */
563          if (now - jcr->last_time > 10) {
564             jcr->LastRate = inst_bps;
565             jcr->LastJobBytes = jcr->JobBytes;
566             jcr->last_time = now;
567          }
568          found = true;
569 #ifdef DEBUG
570          if (jcr->file_bsock) {
571             len = Mmsg(msg, _("    FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
572                edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
573                jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
574                jcr->file_bsock->m_fd);
575             sendit(msg, len, sp);
576          } else {
577             len = Mmsg(msg, _("    FDSocket closed\n"));
578             sendit(msg, len, sp);
579          }
580 #endif
581       }
582    }
583    endeach_jcr(jcr);
584
585    if (!found) {
586       len = Mmsg(msg, _("No Jobs running.\n"));
587       if (!sp->api) sendit(msg, len, sp);
588    }
589    if (!sp->api) sendit("====\n", 5, sp);
590 }
591
592 static void list_jobs_waiting_on_reservation(STATUS_PKT *sp)
593 {
594    JCR *jcr;
595    POOL_MEM msg(PM_MESSAGE);
596    int len;
597
598    len = Mmsg(msg, _("\nJobs waiting to reserve a drive:\n"));
599    if (!sp->api) sendit(msg, len, sp);
600
601    foreach_jcr(jcr) {
602       if (!jcr->reserve_msgs) {
603          continue;
604       }
605       send_drive_reserve_messages(jcr, sendit, sp);
606    }
607    endeach_jcr(jcr);
608
609    if (!sp->api) sendit("====\n", 5, sp);
610 }
611
612
613 static void sendit(const char *msg, int len, void *sp)
614 {
615    sendit(msg, len, (STATUS_PKT *)sp);
616 }
617
618 static void sendit(POOL_MEM &msg, int len, STATUS_PKT *sp)
619 {
620    BSOCK *bs = sp->bs;
621    if (bs) {
622       bs->msg = check_pool_memory_size(bs->msg, len+1);
623       memcpy(bs->msg, msg.c_str(), len+1);
624       bs->msglen = len+1;
625       bs->send();
626    } else {
627       sp->callback(msg.c_str(), len, sp->context);
628    }
629 }
630
631 static void dbg_sendit(const char *msg, int len, void *sp)
632 {
633    if (len > 0) {
634       Dmsg0(-1, msg);
635    }
636 }
637
638 /*
639  * Status command from Director
640  */
641 bool status_cmd(JCR *jcr)
642 {
643    BSOCK *dir = jcr->dir_bsock;
644    STATUS_PKT sp;
645
646    dir->fsend("\n");
647    sp.bs = dir;
648    output_status(&sp);
649    dir->signal(BNET_EOD);
650    return true;
651 }
652
653 /*
654  * .status command from Director
655  */
656 bool qstatus_cmd(JCR *jcr)
657 {
658    BSOCK *dir = jcr->dir_bsock;
659    JCR *njcr;
660    s_last_job* job;
661    STATUS_PKT sp;
662    POOLMEM *args = get_pool_memory(PM_MESSAGE);
663    char *argk[MAX_CMD_ARGS];          /* argument keywords */
664    char *argv[MAX_CMD_ARGS];          /* argument values */
665    int argc;                          /* number of arguments */
666    bool ret=true;
667    char *cmd;
668    char *device=NULL;
669    int api = true;
670
671    sp.bs = dir;
672
673    parse_args(dir->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
674
675    /* .status xxxx at the minimum */
676    if (argc < 2 || strcmp(argk[0], ".status") != 0) {
677       pm_strcpy(jcr->errmsg, dir->msg);
678       dir->fsend(_("3900 No arg in .status command: %s\n"), jcr->errmsg);
679       dir->signal(BNET_EOD);
680       return false;
681    }
682
683    cmd = argk[1];
684    unbash_spaces(cmd);
685
686    /* The status command can contain some arguments
687     * i=0 => .status
688     * i=1 => [running | current | last | ... ]
689     */
690    for (int i=0 ; i < argc ; i++) {
691       if (!strcmp(argk[i], "device") && argv[i]) {
692          device = argv[i];
693          unbash_spaces(device);
694
695       } else if (!strcmp(argk[i], "api") && argv[i]) {
696          api = atoi(argv[i]);
697
698       } else if (!strcmp(argk[i], "api_opts") && argv[i]) {
699          strncpy(sp.api_opts, argv[i], sizeof(sp.api_opts));;
700       }
701    }
702
703    Dmsg1(100, "cmd=%s\n", cmd);
704
705    if (strcasecmp(cmd, "current") == 0) {
706       dir->fsend(OKqstatus, cmd);
707       foreach_jcr(njcr) {
708          if (njcr->JobId != 0) {
709             dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
710          }
711       }
712       endeach_jcr(njcr);
713    } else if (strcasecmp(cmd, "last") == 0) {
714       dir->fsend(OKqstatus, cmd);
715       if ((last_jobs) && (last_jobs->size() > 0)) {
716          job = (s_last_job*)last_jobs->last();
717          dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
718       }
719    } else if (strcasecmp(cmd, "header") == 0) {
720        sp.api = api;
721        list_status_header(&sp);
722    } else if (strcasecmp(cmd, "running") == 0) {
723        sp.api = api;
724        list_running_jobs(&sp);
725    } else if (strcasecmp(cmd, "waitreservation") == 0) {
726        sp.api = api;
727        list_jobs_waiting_on_reservation(&sp);
728    } else if (strcasecmp(cmd, "devices") == 0) {
729        sp.api = api;
730        list_devices(&sp, device);
731    } else if (strcasecmp(cmd, "volumes") == 0) {
732        sp.api = api;
733        list_volumes(sendit, &sp);
734    } else if (strcasecmp(cmd, "spooling") == 0) {
735        sp.api = api;
736        list_spool_stats(sendit, &sp);
737    } else if (strcasecmp(cmd, "terminated") == 0) {
738        sp.api = api;
739        list_terminated_jobs(&sp); /* defined in lib/status.h */
740    } else if (strcasecmp(cmd, "resources") == 0) {
741        sp.api = api;
742        list_resources(&sp);
743    } else {
744       pm_strcpy(jcr->errmsg, dir->msg);
745       dir->fsend(_("3900 Unknown arg in .status command: %s\n"), jcr->errmsg);
746       dir->signal(BNET_EOD);
747       ret = false;
748    }
749    dir->signal(BNET_EOD);
750    free_pool_memory(args);
751    return ret;
752 }
753
754 static void list_plugins(STATUS_PKT *sp)
755 {
756    POOL_MEM msg(PM_MESSAGE);
757    if (b_plugin_list->size() > 0) {
758       Plugin *plugin;
759       int len;
760       pm_strcpy(msg, " Plugin: ");
761       foreach_alist(plugin, b_plugin_list) {
762          len = pm_strcat(msg, plugin->file);
763          /* Print plugin version when debug activated */
764          if (debug_level > 0 && plugin->pinfo) {
765             pm_strcat(msg, "(");
766             pm_strcat(msg, NPRT(sdplug_info(plugin)->plugin_version));
767             len = pm_strcat(msg, ")");
768          }
769          if (len > 80) {
770             pm_strcat(msg, "\n   ");
771          } else {
772             pm_strcat(msg, " ");
773          }
774       }
775       len = pm_strcat(msg, "\n");
776       sendit(msg.c_str(), len, sp);
777    }
778 }