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