]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/status.c
- Require 5 arguments to mtx-changer except list and slots
[bacula/bacula] / bacula / src / stored / status.c
1 /*
2  *  This file handles the status command
3  *
4  *     Kern Sibbald, May MMIII
5  *
6  *   Version $Id$
7  *
8  */
9 /*
10    Copyright (C) 2003-2005 Kern Sibbald
11
12    This program is free software; you can redistribute it and/or
13    modify it under the terms of the GNU General Public License
14    version 2 as amended with additional clauses defined in the
15    file LICENSE in the main source directory.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
20    the file LICENSE for additional details.
21
22  */
23
24 #include "bacula.h"
25 #include "stored.h"
26
27 /* Exported variables */
28
29 /* Imported variables */
30 extern BSOCK *filed_chan;
31 extern int r_first, r_last;
32 extern struct s_res resources[];
33 extern char my_name[];
34 extern time_t daemon_start_time;
35 extern int num_jobs_run;
36
37
38 /* Static variables */
39 static char qstatus[] = ".status %127s\n";
40
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 send_blocked_status(JCR *jcr, DEVICE *dev);
47 static void list_terminated_jobs(void *arg);
48 static void list_running_jobs(BSOCK *user);
49 static void sendit(const char *msg, int len, void *arg);
50 static const char *level_to_str(int level);
51
52
53 /*
54  * Status command from Director
55  */
56 bool status_cmd(JCR *jcr)
57 {
58    DEVRES *device;
59    AUTOCHANGER *changer;
60    DEVICE *dev;
61    BSOCK *user = jcr->dir_bsock;
62    char dt[MAX_TIME_LENGTH];
63    char b1[30], b2[30], b3[30];
64    int bpb;
65
66    bnet_fsend(user, "\n%s Version: " VERSION " (" BDATE ") %s %s %s\n", my_name,
67               HOST_OS, DISTNAME, DISTVER);
68    bstrftime_nc(dt, sizeof(dt), daemon_start_time);
69    bnet_fsend(user, _("Daemon started %s, %d Job%s run since started.\n"), dt, num_jobs_run,
70         num_jobs_run == 1 ? "" : "s");
71    if (debug_level > 0) {
72       char b1[35], b2[35], b3[35], b4[35];
73       bnet_fsend(user, _(" Heap: bytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
74             edit_uint64_with_commas(sm_bytes, b1),
75             edit_uint64_with_commas(sm_max_bytes, b2),
76             edit_uint64_with_commas(sm_buffers, b3),
77             edit_uint64_with_commas(sm_max_buffers, b4));
78    }
79
80    /*
81     * List running jobs
82     */
83    list_running_jobs(user);
84
85    /*
86     * List terminated jobs
87     */
88    list_terminated_jobs(user);
89
90    /*
91     * List devices
92     */
93    bnet_fsend(user, _("\nDevice status:\n"));
94    foreach_res(changer, R_AUTOCHANGER) {
95       bnet_fsend(user, _("Autochanger \"%s\" with devices:\n"),
96          changer->hdr.name);
97       foreach_alist(device, changer->device) {
98          if (device->dev) {
99             bnet_fsend(user, "   %s\n", device->dev->print_name());
100          } else {
101             bnet_fsend(user, "   %s\n", device->hdr.name);
102          }
103       }
104    }
105    foreach_res(device, R_DEVICE) {
106       dev = device->dev;
107       if (dev && dev->is_open()) {
108          if (dev->is_labeled()) {
109             bnet_fsend(user, _("Device %s is mounted with Volume \"%s\"\n"),
110                dev->print_name(), dev->VolHdr.VolumeName);
111          } else {
112             bnet_fsend(user, _("Device %s open but no Bacula volume is mounted.\n"), 
113                dev->print_name());
114          }
115          send_blocked_status(jcr, dev);
116          if (dev->can_append()) {
117             bpb = dev->VolCatInfo.VolCatBlocks;
118             if (bpb <= 0) {
119                bpb = 1;
120             }
121             bpb = dev->VolCatInfo.VolCatBytes / bpb;
122             bnet_fsend(user, _("    Total Bytes=%s Blocks=%s Bytes/block=%s\n"),
123                edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, b1),
124                edit_uint64_with_commas(dev->VolCatInfo.VolCatBlocks, b2),
125                edit_uint64_with_commas(bpb, b3));
126          } else {  /* reading */
127             bpb = dev->VolCatInfo.VolCatReads;
128             if (bpb <= 0) {
129                bpb = 1;
130             }
131             if (dev->VolCatInfo.VolCatRBytes > 0) {
132                bpb = dev->VolCatInfo.VolCatRBytes / bpb;
133             } else {
134                bpb = 0;
135             }
136             bnet_fsend(user, _("    Total Bytes Read=%s Blocks Read=%s Bytes/block=%s\n"),
137                edit_uint64_with_commas(dev->VolCatInfo.VolCatRBytes, b1),
138                edit_uint64_with_commas(dev->VolCatInfo.VolCatReads, b2),
139                edit_uint64_with_commas(bpb, b3));
140          }
141          bnet_fsend(user, _("    Positioned at File=%s Block=%s\n"),
142             edit_uint64_with_commas(dev->file, b1),
143             edit_uint64_with_commas(dev->block_num, b2));
144
145       } else {
146          if (dev) {
147             bnet_fsend(user, _("Device %s is not open or does not exist.\n"), dev->print_name());
148          } else {
149             bnet_fsend(user, _("Device \"%s\" is not open or does not exist.\n"), device->hdr.name);
150          }
151          send_blocked_status(jcr, dev);
152       }
153    }
154    bnet_fsend(user, "====\n\n");
155    bnet_fsend(user, "Volume status:\n");
156    list_volumes(user);
157        
158
159 #ifdef xxx
160    if (debug_level > 0) {
161       bnet_fsend(user, "====\n\n");
162       dump_resource(R_DEVICE, resources[R_DEVICE-r_first].res_head, sendit, user);
163    }
164    bnet_fsend(user, "====\n\n");
165 #endif
166
167    list_spool_stats(user);
168
169    bnet_sig(user, BNET_EOD);
170    return true;
171 }
172
173 static void send_blocked_status(JCR *jcr, DEVICE *dev)
174 {
175    BSOCK *user = jcr->dir_bsock;
176    DCR *dcr = jcr->dcr;
177
178    if (!dev) {
179       bnet_fsend(user, "No DEVICE structure.\n\n");
180       return;
181    }
182    switch (dev->dev_blocked) {
183    case BST_UNMOUNTED:
184       bnet_fsend(user, _("    Device is BLOCKED. User unmounted.\n"));
185       break;
186    case BST_UNMOUNTED_WAITING_FOR_SYSOP:
187       bnet_fsend(user, _("    Device is BLOCKED. User unmounted during wait for media/mount.\n"));
188       break;
189    case BST_WAITING_FOR_SYSOP:
190       if (jcr->JobStatus == JS_WaitMount) {
191          bnet_fsend(user, _("    Device is BLOCKED waiting for mount of volume \"%s\".\n"),
192             dcr->VolumeName);
193       } else {
194          bnet_fsend(user, _("    Device is BLOCKED waiting for media.\n"));
195       }
196       break;
197    case BST_DOING_ACQUIRE:
198       bnet_fsend(user, _("    Device is being initialized.\n"));
199       break;
200    case BST_WRITING_LABEL:
201       bnet_fsend(user, _("    Device is blocked labeling a Volume.\n"));
202       break;
203    default:
204       break;
205    }
206    /* Send autochanger slot status */
207    if (dev->is_autochanger()) {
208       if (dev->Slot) {
209          bnet_fsend(user, _("    Slot %d is loaded in drive %d.\n"), 
210             dev->Slot, dev->drive_index);
211       } else {
212          bnet_fsend(user, _("    Drive %d is not loaded.\n"), dev->drive_index);
213       }
214    }
215    if (debug_level > 1) {
216       bnet_fsend(user, _("Configured device capabilities:\n"));
217       bnet_fsend(user, "%sEOF ", dev->capabilities & CAP_EOF ? "" : "!");
218       bnet_fsend(user, "%sBSR ", dev->capabilities & CAP_BSR ? "" : "!");
219       bnet_fsend(user, "%sBSF ", dev->capabilities & CAP_BSF ? "" : "!");
220       bnet_fsend(user, "%sFSR ", dev->capabilities & CAP_FSR ? "" : "!");
221       bnet_fsend(user, "%sFSF ", dev->capabilities & CAP_FSF ? "" : "!");
222       bnet_fsend(user, "%sEOM ", dev->capabilities & CAP_EOM ? "" : "!");
223       bnet_fsend(user, "%sREM ", dev->capabilities & CAP_REM ? "" : "!");
224       bnet_fsend(user, "%sRACCESS ", dev->capabilities & CAP_RACCESS ? "" : "!");
225       bnet_fsend(user, "%sAUTOMOUNT ", dev->capabilities & CAP_AUTOMOUNT ? "" : "!");
226       bnet_fsend(user, "%sLABEL ", dev->capabilities & CAP_LABEL ? "" : "!");
227       bnet_fsend(user, "%sANONVOLS ", dev->capabilities & CAP_ANONVOLS ? "" : "!");
228       bnet_fsend(user, "%sALWAYSOPEN ", dev->capabilities & CAP_ALWAYSOPEN ? "" : "!");
229       bnet_fsend(user, "\n");
230
231       bnet_fsend(user, _("Device state:\n"));
232       bnet_fsend(user, "%sOPENED ", dev->is_open() ? "" : "!");
233       bnet_fsend(user, "%sTAPE ", dev->is_tape() ? "" : "!");
234       bnet_fsend(user, "%sLABEL ", dev->is_labeled() ? "" : "!");
235       bnet_fsend(user, "%sMALLOC ", dev->state & ST_MALLOC ? "" : "!");
236       bnet_fsend(user, "%sAPPEND ", dev->can_append() ? "" : "!");
237       bnet_fsend(user, "%sREAD ", dev->can_read() ? "" : "!");
238       bnet_fsend(user, "%sEOT ", dev->at_eot() ? "" : "!");
239       bnet_fsend(user, "%sWEOT ", dev->state & ST_WEOT ? "" : "!");
240       bnet_fsend(user, "%sEOF ", dev->at_eof() ? "" : "!");
241       bnet_fsend(user, "%sNEXTVOL ", dev->state & ST_NEXTVOL ? "" : "!");
242       bnet_fsend(user, "%sSHORT ", dev->state & ST_SHORT ? "" : "!");
243       bnet_fsend(user, "%sMOUNTED ", dev->state & ST_MOUNTED ? "" : "!");
244       bnet_fsend(user, "\n");
245       bnet_fsend(user, "num_writers=%d JobStatus=%c block=%d\n\n", dev->num_writers,
246          jcr->JobStatus, dev->dev_blocked);
247
248       bnet_fsend(user, _("Device parameters:\n"));
249       bnet_fsend(user, "Archive name: %s Device name: %s\n", dev->archive_name(),
250          dev->name());
251       bnet_fsend(user, "File=%u block=%u\n", dev->file, dev->block_num);
252       bnet_fsend(user, "Min block=%u Max block=%u\n", dev->min_block_size, dev->max_block_size);
253    }
254
255 }
256
257 static void list_running_jobs(BSOCK *user)
258 {
259    bool found = false;
260    int bps, sec;
261    JCR *jcr;
262    char JobName[MAX_NAME_LENGTH];
263    char b1[30], b2[30], b3[30];
264
265    bnet_fsend(user, _("\nRunning Jobs:\n"));
266    foreach_jcr(jcr) {
267       if (jcr->JobStatus == JS_WaitFD) {
268          bnet_fsend(user, _("%s Job %s waiting for Client connection.\n"),
269             job_type_to_str(jcr->JobType), jcr->Job);
270       }
271       if (jcr->dcr && jcr->dcr->device) {
272          bstrncpy(JobName, jcr->Job, sizeof(JobName));
273          /* There are three periods after the Job name */
274          char *p;
275          for (int i=0; i<3; i++) {
276             if ((p=strrchr(JobName, '.')) != NULL) {
277                *p = 0;
278             }
279          }
280          bnet_fsend(user, _("%s %s job %s JobId=%d Volume=\"%s\" device=\"%s\"\n"),
281                    job_level_to_str(jcr->JobLevel),
282                    job_type_to_str(jcr->JobType),
283                    JobName,
284                    jcr->JobId,
285                    jcr->dcr->VolumeName,
286                    jcr->dcr->device->device_name);
287          sec = time(NULL) - jcr->run_time;
288          if (sec <= 0) {
289             sec = 1;
290          }
291          bps = jcr->JobBytes / sec;
292          bnet_fsend(user, _("    Files=%s Bytes=%s Bytes/sec=%s\n"),
293             edit_uint64_with_commas(jcr->JobFiles, b1),
294             edit_uint64_with_commas(jcr->JobBytes, b2),
295             edit_uint64_with_commas(bps, b3));
296          found = true;
297 #ifdef DEBUG
298          if (jcr->file_bsock) {
299             bnet_fsend(user, "    FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n",
300                edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
301                jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
302                jcr->file_bsock->fd);
303          } else {
304             bnet_fsend(user, "    FDSocket closed\n");
305          }
306 #endif
307       }
308       free_jcr(jcr);
309    }
310    if (!found) {
311       bnet_fsend(user, _("No Jobs running.\n"));
312    }
313    bnet_fsend(user, "====\n");
314 }
315
316 static void list_terminated_jobs(void *arg)
317 {
318    char dt[MAX_TIME_LENGTH], b1[30], b2[30];
319    char level[10];
320    struct s_last_job *je;
321    const char *msg;
322
323    if (last_jobs->size() == 0) {
324       msg = _("No Terminated Jobs.\n");
325       sendit(msg, strlen(msg), arg);
326       return;
327    }
328    lock_last_jobs_list();
329    msg =  _("\nTerminated Jobs:\n");
330    sendit(msg, strlen(msg), arg);
331    msg =  _(" JobId  Level   Files          Bytes Status   Finished        Name \n");
332    sendit(msg, strlen(msg), arg);
333    msg = _("======================================================================\n");
334    sendit(msg, strlen(msg), arg);
335    foreach_dlist(je, last_jobs) {
336       char JobName[MAX_NAME_LENGTH];
337       const char *termstat;
338       char buf[1000];
339
340       bstrftime_nc(dt, sizeof(dt), je->end_time);
341       switch (je->JobType) {
342       case JT_ADMIN:
343       case JT_RESTORE:
344          bstrncpy(level, "    ", sizeof(level));
345          break;
346       default:
347          bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
348          level[4] = 0;
349          break;
350       }
351       switch (je->JobStatus) {
352       case JS_Created:
353          termstat = "Created";
354          break;
355       case JS_FatalError:
356       case JS_ErrorTerminated:
357          termstat = "Error";
358          break;
359       case JS_Differences:
360          termstat = "Diffs";
361          break;
362       case JS_Canceled:
363          termstat = "Cancel";
364          break;
365       case JS_Terminated:
366          termstat = "OK";
367          break;
368       default:
369          termstat = "Other";
370          break;
371       }
372       bstrncpy(JobName, je->Job, sizeof(JobName));
373       /* There are three periods after the Job name */
374       char *p;
375       for (int i=0; i<3; i++) {
376          if ((p=strrchr(JobName, '.')) != NULL) {
377             *p = 0;
378          }
379       }
380       bsnprintf(buf, sizeof(buf), _("%6d  %-6s %8s %14s %-7s  %-8s %s\n"),
381          je->JobId,
382          level,
383          edit_uint64_with_commas(je->JobFiles, b1),
384          edit_uint64_with_commas(je->JobBytes, b2),
385          termstat,
386          dt, JobName);
387       sendit(buf, strlen(buf), arg);
388    }
389    sendit("====\n", 5, arg);
390    unlock_last_jobs_list();
391 }
392
393 /*
394  * Convert Job Level into a string
395  */
396 static const char *level_to_str(int level)
397 {
398    const char *str;
399
400    switch (level) {
401    case L_BASE:
402       str = _("Base");
403    case L_FULL:
404       str = _("Full");
405       break;
406    case L_INCREMENTAL:
407       str = _("Incremental");
408       break;
409    case L_DIFFERENTIAL:
410       str = _("Differential");
411       break;
412    case L_SINCE:
413       str = _("Since");
414       break;
415    case L_VERIFY_CATALOG:
416       str = _("Verify Catalog");
417       break;
418    case L_VERIFY_INIT:
419       str = _("Init Catalog");
420       break;
421    case L_VERIFY_VOLUME_TO_CATALOG:
422       str = _("Volume to Catalog");
423       break;
424    case L_VERIFY_DISK_TO_CATALOG:
425       str = _("Disk to Catalog");
426       break;
427    case L_VERIFY_DATA:
428       str = _("Data");
429       break;
430    case L_NONE:
431       str = " ";
432       break;
433    default:
434       str = _("Unknown Job Level");
435       break;
436    }
437    return str;
438 }
439
440 /*
441  * Send to Director
442  */
443 static void sendit(const char *msg, int len, void *arg)
444 {
445    BSOCK *user = (BSOCK *)arg;
446
447    memcpy(user->msg, msg, len+1);
448    user->msglen = len+1;
449    bnet_send(user);
450 }
451
452 /*
453  * .status command from Director
454  */
455 bool qstatus_cmd(JCR *jcr)
456 {
457    BSOCK *dir = jcr->dir_bsock;
458    POOL_MEM time;
459    JCR *njcr;
460    s_last_job* job;
461
462    if (sscanf(dir->msg, qstatus, time.c_str()) != 1) {
463       pm_strcpy(jcr->errmsg, dir->msg);
464       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
465       bnet_fsend(dir, "3900 Bad .status command, missing argument.\n");
466       bnet_sig(dir, BNET_EOD);
467       return false;
468    }
469    unbash_spaces(time);
470
471    if (strcmp(time.c_str(), "current") == 0) {
472       bnet_fsend(dir, OKqstatus, time.c_str());
473       foreach_jcr(njcr) {
474          if (njcr->JobId != 0) {
475             bnet_fsend(dir, DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
476          }
477          free_jcr(njcr);
478       }
479    }
480    else if (strcmp(time.c_str(), "last") == 0) {
481       bnet_fsend(dir, OKqstatus, time.c_str());
482       if ((last_jobs) && (last_jobs->size() > 0)) {
483          job = (s_last_job*)last_jobs->last();
484          bnet_fsend(dir, DotStatusJob, job->JobId, job->JobStatus, job->Errors);
485       }
486    }
487    else {
488       pm_strcpy(jcr->errmsg, dir->msg);
489       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
490       bnet_fsend(dir, "3900 Bad .status command, wrong argument.\n");
491       bnet_sig(dir, BNET_EOD);
492       return false;
493    }
494    bnet_sig(dir, BNET_EOD);
495    return true;
496 }