]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/status.c
07Jan06
[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-2006 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\" Pool=\"%s\"\n"),
114                dev->print_name(), dev->VolHdr.VolumeName, 
115                dev->pool_name[0]?dev->pool_name:"*unknown*");
116          } else {
117             bnet_fsend(user, _("Device %s open but no Bacula volume is mounted.\n"), 
118                dev->print_name());
119          }
120          send_blocked_status(jcr, dev);
121          if (dev->can_append()) {
122             bpb = dev->VolCatInfo.VolCatBlocks;
123             if (bpb <= 0) {
124                bpb = 1;
125             }
126             bpb = dev->VolCatInfo.VolCatBytes / bpb;
127             bnet_fsend(user, _("    Total Bytes=%s Blocks=%s Bytes/block=%s\n"),
128                edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, b1),
129                edit_uint64_with_commas(dev->VolCatInfo.VolCatBlocks, b2),
130                edit_uint64_with_commas(bpb, b3));
131          } else {  /* reading */
132             bpb = dev->VolCatInfo.VolCatReads;
133             if (bpb <= 0) {
134                bpb = 1;
135             }
136             if (dev->VolCatInfo.VolCatRBytes > 0) {
137                bpb = dev->VolCatInfo.VolCatRBytes / bpb;
138             } else {
139                bpb = 0;
140             }
141             bnet_fsend(user, _("    Total Bytes Read=%s Blocks Read=%s Bytes/block=%s\n"),
142                edit_uint64_with_commas(dev->VolCatInfo.VolCatRBytes, b1),
143                edit_uint64_with_commas(dev->VolCatInfo.VolCatReads, b2),
144                edit_uint64_with_commas(bpb, b3));
145          }
146          bnet_fsend(user, _("    Positioned at File=%s Block=%s\n"),
147             edit_uint64_with_commas(dev->file, b1),
148             edit_uint64_with_commas(dev->block_num, b2));
149
150       } else {
151          if (dev) {
152             bnet_fsend(user, _("Device %s is not open or does not exist.\n"), dev->print_name());
153          } else {
154             bnet_fsend(user, _("Device \"%s\" is not open or does not exist.\n"), device->hdr.name);
155          }
156          send_blocked_status(jcr, dev);
157       }
158    }
159    bnet_fsend(user, _("====\n\n"));
160    bnet_fsend(user, _("In Use Volume status:\n"));
161    list_volumes(user);
162    bnet_fsend(user, _("====\n\n"));
163        
164 #ifdef xxx
165    if (debug_level > 10) {
166       bnet_fsend(user, _("====\n\n"));
167       dump_resource(R_DEVICE, resources[R_DEVICE-r_first].res_head, sendit, user);
168       bnet_fsend(user, _("====\n\n"));
169    }
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, *rdcr;
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       rdcr = jcr->read_dcr;
279       if ((dcr && dcr->device) || rdcr && rdcr->device) {
280          bstrncpy(JobName, jcr->Job, sizeof(JobName));
281          /* There are three periods after the Job name */
282          char *p;
283          for (int i=0; i<3; i++) {
284             if ((p=strrchr(JobName, '.')) != NULL) {
285                *p = 0;
286             }
287          }
288          if (rdcr && rdcr->device) {
289             bnet_fsend(user, _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
290                             "    pool=\"%s\" device=\"%s\"\n"),
291                    job_level_to_str(jcr->JobLevel),
292                    job_type_to_str(jcr->JobType),
293                    JobName,
294                    jcr->JobId,
295                    rdcr->VolumeName,
296                    rdcr->pool_name,
297                    rdcr->dev?rdcr->dev->print_name(): 
298                             rdcr->device->device_name);
299          }
300          if (dcr && dcr->device) {
301             bnet_fsend(user, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
302                             "    pool=\"%s\" device=\"%s\"\n"),
303                    job_level_to_str(jcr->JobLevel),
304                    job_type_to_str(jcr->JobType),
305                    JobName,
306                    jcr->JobId,
307                    dcr->VolumeName,
308                    dcr->pool_name,
309                    dcr->dev?dcr->dev->print_name(): 
310                             dcr->device->device_name);
311          }
312          sec = time(NULL) - jcr->run_time;
313          if (sec <= 0) {
314             sec = 1;
315          }
316          bps = jcr->JobBytes / sec;
317          bnet_fsend(user, _("    Files=%s Bytes=%s Bytes/sec=%s\n"),
318             edit_uint64_with_commas(jcr->JobFiles, b1),
319             edit_uint64_with_commas(jcr->JobBytes, b2),
320             edit_uint64_with_commas(bps, b3));
321          found = true;
322 #ifdef DEBUG
323          if (jcr->file_bsock) {
324             bnet_fsend(user, _("    FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
325                edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
326                jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
327                jcr->file_bsock->fd);
328          } else {
329             bnet_fsend(user, _("    FDSocket closed\n"));
330          }
331 #endif
332       }
333    }
334    endeach_jcr(jcr);
335
336    if (!found) {
337       bnet_fsend(user, _("No Jobs running.\n"));
338    }
339    bnet_fsend(user, _("====\n"));
340 }
341
342 static void list_terminated_jobs(void *arg)
343 {
344    char dt[MAX_TIME_LENGTH], b1[30], b2[30];
345    char level[10];
346    struct s_last_job *je;
347    const char *msg;
348
349    if (last_jobs->size() == 0) {
350       msg = _("No Terminated Jobs.\n");
351       sendit(msg, strlen(msg), arg);
352       return;
353    }
354    lock_last_jobs_list();
355    msg =  _("\nTerminated Jobs:\n");
356    sendit(msg, strlen(msg), arg);
357    msg =  _(" JobId  Level   Files          Bytes Status   Finished        Name \n");
358    sendit(msg, strlen(msg), arg);
359    msg = _("======================================================================\n");
360    sendit(msg, strlen(msg), arg);
361    foreach_dlist(je, last_jobs) {
362       char JobName[MAX_NAME_LENGTH];
363       const char *termstat;
364       char buf[1000];
365
366       bstrftime_nc(dt, sizeof(dt), je->end_time);
367       switch (je->JobType) {
368       case JT_ADMIN:
369       case JT_RESTORE:
370          bstrncpy(level, "    ", sizeof(level));
371          break;
372       default:
373          bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
374          level[4] = 0;
375          break;
376       }
377       switch (je->JobStatus) {
378       case JS_Created:
379          termstat = _("Created");
380          break;
381       case JS_FatalError:
382       case JS_ErrorTerminated:
383          termstat = _("Error");
384          break;
385       case JS_Differences:
386          termstat = _("Diffs");
387          break;
388       case JS_Canceled:
389          termstat = _("Cancel");
390          break;
391       case JS_Terminated:
392          termstat = _("OK");
393          break;
394       default:
395          termstat = _("Other");
396          break;
397       }
398       bstrncpy(JobName, je->Job, sizeof(JobName));
399       /* There are three periods after the Job name */
400       char *p;
401       for (int i=0; i<3; i++) {
402          if ((p=strrchr(JobName, '.')) != NULL) {
403             *p = 0;
404          }
405       }
406       bsnprintf(buf, sizeof(buf), _("%6d  %-6s %8s %14s %-7s  %-8s %s\n"),
407          je->JobId,
408          level,
409          edit_uint64_with_commas(je->JobFiles, b1),
410          edit_uint64_with_commas(je->JobBytes, b2),
411          termstat,
412          dt, JobName);
413       sendit(buf, strlen(buf), arg);
414    }
415    sendit(_("====\n"), 5, arg);
416    unlock_last_jobs_list();
417 }
418
419 /*
420  * Convert Job Level into a string
421  */
422 static const char *level_to_str(int level)
423 {
424    const char *str;
425
426    switch (level) {
427    case L_BASE:
428       str = _("Base");
429    case L_FULL:
430       str = _("Full");
431       break;
432    case L_INCREMENTAL:
433       str = _("Incremental");
434       break;
435    case L_DIFFERENTIAL:
436       str = _("Differential");
437       break;
438    case L_SINCE:
439       str = _("Since");
440       break;
441    case L_VERIFY_CATALOG:
442       str = _("Verify Catalog");
443       break;
444    case L_VERIFY_INIT:
445       str = _("Init Catalog");
446       break;
447    case L_VERIFY_VOLUME_TO_CATALOG:
448       str = _("Volume to Catalog");
449       break;
450    case L_VERIFY_DISK_TO_CATALOG:
451       str = _("Disk to Catalog");
452       break;
453    case L_VERIFY_DATA:
454       str = _("Data");
455       break;
456    case L_NONE:
457       str = " ";
458       break;
459    default:
460       str = _("Unknown Job Level");
461       break;
462    }
463    return str;
464 }
465
466 /*
467  * Send to Director
468  */
469 static void sendit(const char *msg, int len, void *arg)
470 {
471    BSOCK *user = (BSOCK *)arg;
472
473    memcpy(user->msg, msg, len+1);
474    user->msglen = len+1;
475    bnet_send(user);
476 }
477
478 /*
479  * .status command from Director
480  */
481 bool qstatus_cmd(JCR *jcr)
482 {
483    BSOCK *dir = jcr->dir_bsock;
484    POOL_MEM time;
485    JCR *njcr;
486    s_last_job* job;
487
488    if (sscanf(dir->msg, qstatus, time.c_str()) != 1) {
489       pm_strcpy(jcr->errmsg, dir->msg);
490       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
491       bnet_fsend(dir, _("3900 Bad .status command, missing argument.\n"));
492       bnet_sig(dir, BNET_EOD);
493       return false;
494    }
495    unbash_spaces(time);
496
497    if (strcmp(time.c_str(), "current") == 0) {
498       bnet_fsend(dir, OKqstatus, time.c_str());
499       foreach_jcr(njcr) {
500          if (njcr->JobId != 0) {
501             bnet_fsend(dir, DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
502          }
503       }
504       endeach_jcr(njcr);
505    } else if (strcmp(time.c_str(), "last") == 0) {
506       bnet_fsend(dir, OKqstatus, time.c_str());
507       if ((last_jobs) && (last_jobs->size() > 0)) {
508          job = (s_last_job*)last_jobs->last();
509          bnet_fsend(dir, DotStatusJob, job->JobId, job->JobStatus, job->Errors);
510       }
511    } else {
512       pm_strcpy(jcr->errmsg, dir->msg);
513       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
514       bnet_fsend(dir, _("3900 Bad .status command, wrong argument.\n"));
515       bnet_sig(dir, BNET_EOD);
516       return false;
517    }
518    bnet_sig(dir, BNET_EOD);
519    return true;
520 }