]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/status.c
Backport from Bacula Enterprise
[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          }
521          if (dcr && dcr->device && dcr != rdcr) {
522             len = Mmsg(msg, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
523                             "    pool=\"%s\" device=%s\n"),
524                    job_level_to_str(jcr->getJobLevel()),
525                    job_type_to_str(jcr->getJobType()),
526                    JobName,
527                    jcr->JobId,
528                    dcr->VolumeName,
529                    dcr->pool_name,
530                    dcr->dev?dcr->dev->print_name():
531                             dcr->device->device_name);
532             sendit(msg, len, sp);
533             len= Mmsg(msg, _("    spooling=%d despooling=%d despool_wait=%d\n"),
534                    dcr->spooling, dcr->despooling, dcr->despool_wait);
535             sendit(msg, len, sp);
536          }
537          if (jcr->last_time == 0) {
538             jcr->last_time = jcr->run_time;
539          }
540          total_sec = now - jcr->run_time;
541          inst_sec = now - jcr->last_time;
542          if (total_sec <= 0) {
543             total_sec = 1;
544          }
545          if (inst_sec <= 0) {
546             inst_sec = 1;
547          }
548          /* Instanteous bps not smoothed */
549          inst_bps = (jcr->JobBytes - jcr->LastJobBytes) / inst_sec;
550          if (jcr->LastRate == 0) {
551             jcr->LastRate = inst_bps;
552          }
553          /* Smooth the instantaneous bps a bit */
554          inst_bps = (2 * jcr->LastRate + inst_bps) / 3;
555          /* total bps (AveBytes/sec) since start of job */
556          total_bps = jcr->JobBytes / total_sec;
557          len = Mmsg(msg, _("    Files=%s Bytes=%s AveBytes/sec=%s LastBytes/sec=%s\n"),
558             edit_uint64_with_commas(jcr->JobFiles, b1),
559             edit_uint64_with_commas(jcr->JobBytes, b2),
560             edit_uint64_with_commas(total_bps, b3),
561             edit_uint64_with_commas(inst_bps, b4));
562          sendit(msg, len, sp);
563          /* Update only every 10 seconds */
564          if (now - jcr->last_time > 10) {
565             jcr->LastRate = inst_bps;
566             jcr->LastJobBytes = jcr->JobBytes;
567             jcr->last_time = now;
568          }
569          found = true;
570 #ifdef DEBUG
571          if (jcr->file_bsock) {
572             len = Mmsg(msg, _("    FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
573                edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
574                jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
575                jcr->file_bsock->m_fd);
576             sendit(msg, len, sp);
577          } else {
578             len = Mmsg(msg, _("    FDSocket closed\n"));
579             sendit(msg, len, sp);
580          }
581 #endif
582       }
583    }
584    endeach_jcr(jcr);
585
586    if (!found) {
587       len = Mmsg(msg, _("No Jobs running.\n"));
588       if (!sp->api) sendit(msg, len, sp);
589    }
590    if (!sp->api) sendit("====\n", 5, sp);
591 }
592
593 static void list_jobs_waiting_on_reservation(STATUS_PKT *sp)
594 {
595    JCR *jcr;
596    POOL_MEM msg(PM_MESSAGE);
597    int len;
598
599    len = Mmsg(msg, _("\nJobs waiting to reserve a drive:\n"));
600    if (!sp->api) sendit(msg, len, sp);
601
602    foreach_jcr(jcr) {
603       if (!jcr->reserve_msgs) {
604          continue;
605       }
606       send_drive_reserve_messages(jcr, sendit, sp);
607    }
608    endeach_jcr(jcr);
609
610    if (!sp->api) sendit("====\n", 5, sp);
611 }
612
613
614 static void sendit(const char *msg, int len, void *sp)
615 {
616    sendit(msg, len, (STATUS_PKT *)sp);
617 }
618
619 static void sendit(POOL_MEM &msg, int len, STATUS_PKT *sp)
620 {
621    BSOCK *bs = sp->bs;
622    if (bs) {
623       bs->msg = check_pool_memory_size(bs->msg, len+1);
624       memcpy(bs->msg, msg.c_str(), len+1);
625       bs->msglen = len+1;
626       bs->send();
627    } else {
628       sp->callback(msg.c_str(), len, sp->context);
629    }
630 }
631
632 static void dbg_sendit(const char *msg, int len, void *sp)
633 {
634    if (len > 0) {
635       Dmsg0(-1, msg);
636    }
637 }
638
639 /*
640  * Status command from Director
641  */
642 bool status_cmd(JCR *jcr)
643 {
644    BSOCK *dir = jcr->dir_bsock;
645    STATUS_PKT sp;
646
647    dir->fsend("\n");
648    sp.bs = dir;
649    output_status(&sp);
650    dir->signal(BNET_EOD);
651    return true;
652 }
653
654 /*
655  * .status command from Director
656  */
657 bool qstatus_cmd(JCR *jcr)
658 {
659    BSOCK *dir = jcr->dir_bsock;
660    JCR *njcr;
661    s_last_job* job;
662    STATUS_PKT sp;
663    POOLMEM *args = get_pool_memory(PM_MESSAGE);
664    char *argk[MAX_CMD_ARGS];          /* argument keywords */
665    char *argv[MAX_CMD_ARGS];          /* argument values */
666    int argc;                          /* number of arguments */
667    bool ret=true;
668    char *cmd;
669    char *device=NULL;
670    int api = true;
671
672    sp.bs = dir;
673
674    parse_args(dir->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
675
676    /* .status xxxx at the minimum */
677    if (argc < 2 || strcmp(argk[0], ".status") != 0) {
678       pm_strcpy(jcr->errmsg, dir->msg);
679       dir->fsend(_("3900 No arg in .status command: %s\n"), jcr->errmsg);
680       dir->signal(BNET_EOD);
681       return false;
682    }
683
684    cmd = argk[1];
685    unbash_spaces(cmd);
686
687    /* The status command can contain some arguments
688     * i=0 => .status
689     * i=1 => [running | current | last | ... ]
690     */
691    for (int i=0 ; i < argc ; i++) {
692       if (!strcmp(argk[i], "device") && argv[i]) {
693          device = argv[i];
694          unbash_spaces(device);
695
696       } else if (!strcmp(argk[i], "api") && argv[i]) {
697          api = atoi(argv[i]);
698
699       } else if (!strcmp(argk[i], "api_opts") && argv[i]) {
700          strncpy(sp.api_opts, argv[i], sizeof(sp.api_opts));;
701       }
702    }
703
704    Dmsg1(100, "cmd=%s\n", cmd);
705
706    if (strcasecmp(cmd, "current") == 0) {
707       dir->fsend(OKqstatus, cmd);
708       foreach_jcr(njcr) {
709          if (njcr->JobId != 0) {
710             dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
711          }
712       }
713       endeach_jcr(njcr);
714    } else if (strcasecmp(cmd, "last") == 0) {
715       dir->fsend(OKqstatus, cmd);
716       if ((last_jobs) && (last_jobs->size() > 0)) {
717          job = (s_last_job*)last_jobs->last();
718          dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
719       }
720    } else if (strcasecmp(cmd, "header") == 0) {
721        sp.api = api;
722        list_status_header(&sp);
723    } else if (strcasecmp(cmd, "running") == 0) {
724        sp.api = api;
725        list_running_jobs(&sp);
726    } else if (strcasecmp(cmd, "waitreservation") == 0) {
727        sp.api = api;
728        list_jobs_waiting_on_reservation(&sp);
729    } else if (strcasecmp(cmd, "devices") == 0) {
730        sp.api = api;
731        list_devices(&sp, device);
732    } else if (strcasecmp(cmd, "volumes") == 0) {
733        sp.api = api;
734        list_volumes(sendit, &sp);
735    } else if (strcasecmp(cmd, "spooling") == 0) {
736        sp.api = api;
737        list_spool_stats(sendit, &sp);
738    } else if (strcasecmp(cmd, "terminated") == 0) {
739        sp.api = api;
740        list_terminated_jobs(&sp); /* defined in lib/status.h */
741    } else if (strcasecmp(cmd, "resources") == 0) {
742        sp.api = api;
743        list_resources(&sp);
744    } else {
745       pm_strcpy(jcr->errmsg, dir->msg);
746       dir->fsend(_("3900 Unknown arg in .status command: %s\n"), jcr->errmsg);
747       dir->signal(BNET_EOD);
748       ret = false;
749    }
750    dir->signal(BNET_EOD);
751    free_pool_memory(args);
752    return ret;
753 }
754
755 static void list_plugins(STATUS_PKT *sp)
756 {
757    POOL_MEM msg(PM_MESSAGE);
758    if (b_plugin_list->size() > 0) {
759       Plugin *plugin;
760       int len;
761       pm_strcpy(msg, " Plugin: ");
762       foreach_alist(plugin, b_plugin_list) {
763          len = pm_strcat(msg, plugin->file);
764          /* Print plugin version when debug activated */
765          if (debug_level > 0 && plugin->pinfo) {
766             pm_strcat(msg, "(");
767             pm_strcat(msg, NPRT(sdplug_info(plugin)->plugin_version));
768             len = pm_strcat(msg, ")");
769          }
770          if (len > 80) {
771             pm_strcat(msg, "\n   ");
772          } else {
773             pm_strcat(msg, " ");
774          }
775       }
776       len = pm_strcat(msg, "\n");
777       sendit(msg.c_str(), len, sp);
778    }
779 }