]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/status.c
03Dec05
[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, *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       free_jcr(jcr);
334    }
335    if (!found) {
336       bnet_fsend(user, _("No Jobs running.\n"));
337    }
338    bnet_fsend(user, _("====\n"));
339 }
340
341 static void list_terminated_jobs(void *arg)
342 {
343    char dt[MAX_TIME_LENGTH], b1[30], b2[30];
344    char level[10];
345    struct s_last_job *je;
346    const char *msg;
347
348    if (last_jobs->size() == 0) {
349       msg = _("No Terminated Jobs.\n");
350       sendit(msg, strlen(msg), arg);
351       return;
352    }
353    lock_last_jobs_list();
354    msg =  _("\nTerminated Jobs:\n");
355    sendit(msg, strlen(msg), arg);
356    msg =  _(" JobId  Level   Files          Bytes Status   Finished        Name \n");
357    sendit(msg, strlen(msg), arg);
358    msg = _("======================================================================\n");
359    sendit(msg, strlen(msg), arg);
360    foreach_dlist(je, last_jobs) {
361       char JobName[MAX_NAME_LENGTH];
362       const char *termstat;
363       char buf[1000];
364
365       bstrftime_nc(dt, sizeof(dt), je->end_time);
366       switch (je->JobType) {
367       case JT_ADMIN:
368       case JT_RESTORE:
369          bstrncpy(level, "    ", sizeof(level));
370          break;
371       default:
372          bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
373          level[4] = 0;
374          break;
375       }
376       switch (je->JobStatus) {
377       case JS_Created:
378          termstat = _("Created");
379          break;
380       case JS_FatalError:
381       case JS_ErrorTerminated:
382          termstat = _("Error");
383          break;
384       case JS_Differences:
385          termstat = _("Diffs");
386          break;
387       case JS_Canceled:
388          termstat = _("Cancel");
389          break;
390       case JS_Terminated:
391          termstat = _("OK");
392          break;
393       default:
394          termstat = _("Other");
395          break;
396       }
397       bstrncpy(JobName, je->Job, sizeof(JobName));
398       /* There are three periods after the Job name */
399       char *p;
400       for (int i=0; i<3; i++) {
401          if ((p=strrchr(JobName, '.')) != NULL) {
402             *p = 0;
403          }
404       }
405       bsnprintf(buf, sizeof(buf), _("%6d  %-6s %8s %14s %-7s  %-8s %s\n"),
406          je->JobId,
407          level,
408          edit_uint64_with_commas(je->JobFiles, b1),
409          edit_uint64_with_commas(je->JobBytes, b2),
410          termstat,
411          dt, JobName);
412       sendit(buf, strlen(buf), arg);
413    }
414    sendit(_("====\n"), 5, arg);
415    unlock_last_jobs_list();
416 }
417
418 /*
419  * Convert Job Level into a string
420  */
421 static const char *level_to_str(int level)
422 {
423    const char *str;
424
425    switch (level) {
426    case L_BASE:
427       str = _("Base");
428    case L_FULL:
429       str = _("Full");
430       break;
431    case L_INCREMENTAL:
432       str = _("Incremental");
433       break;
434    case L_DIFFERENTIAL:
435       str = _("Differential");
436       break;
437    case L_SINCE:
438       str = _("Since");
439       break;
440    case L_VERIFY_CATALOG:
441       str = _("Verify Catalog");
442       break;
443    case L_VERIFY_INIT:
444       str = _("Init Catalog");
445       break;
446    case L_VERIFY_VOLUME_TO_CATALOG:
447       str = _("Volume to Catalog");
448       break;
449    case L_VERIFY_DISK_TO_CATALOG:
450       str = _("Disk to Catalog");
451       break;
452    case L_VERIFY_DATA:
453       str = _("Data");
454       break;
455    case L_NONE:
456       str = " ";
457       break;
458    default:
459       str = _("Unknown Job Level");
460       break;
461    }
462    return str;
463 }
464
465 /*
466  * Send to Director
467  */
468 static void sendit(const char *msg, int len, void *arg)
469 {
470    BSOCK *user = (BSOCK *)arg;
471
472    memcpy(user->msg, msg, len+1);
473    user->msglen = len+1;
474    bnet_send(user);
475 }
476
477 /*
478  * .status command from Director
479  */
480 bool qstatus_cmd(JCR *jcr)
481 {
482    BSOCK *dir = jcr->dir_bsock;
483    POOL_MEM time;
484    JCR *njcr;
485    s_last_job* job;
486
487    if (sscanf(dir->msg, qstatus, time.c_str()) != 1) {
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, missing argument.\n"));
491       bnet_sig(dir, BNET_EOD);
492       return false;
493    }
494    unbash_spaces(time);
495
496    if (strcmp(time.c_str(), "current") == 0) {
497       bnet_fsend(dir, OKqstatus, time.c_str());
498       foreach_jcr(njcr) {
499          if (njcr->JobId != 0) {
500             bnet_fsend(dir, DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
501          }
502          free_jcr(njcr);
503       }
504    } else if (strcmp(time.c_str(), "last") == 0) {
505       bnet_fsend(dir, OKqstatus, time.c_str());
506       if ((last_jobs) && (last_jobs->size() > 0)) {
507          job = (s_last_job*)last_jobs->last();
508          bnet_fsend(dir, DotStatusJob, job->JobId, job->JobStatus, job->Errors);
509       }
510    } else {
511       pm_strcpy(jcr->errmsg, dir->msg);
512       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
513       bnet_fsend(dir, _("3900 Bad .status command, wrong argument.\n"));
514       bnet_sig(dir, BNET_EOD);
515       return false;
516    }
517    bnet_sig(dir, BNET_EOD);
518    return true;
519 }