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