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