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