]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/status.c
- Pull in latest reservation system changes from 1.38
[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 #ifdef xxx
164    if (debug_level > 10) {
165       bnet_fsend(user, _("====\n\n"));
166       dump_resource(R_DEVICE, resources[R_DEVICE-r_first].res_head, sendit, user);
167       bnet_fsend(user, _("====\n\n"));
168    }
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    DCR *dcr, *rdcr;
267    char JobName[MAX_NAME_LENGTH];
268    char b1[30], b2[30], b3[30];
269
270    bnet_fsend(user, _("\nRunning Jobs:\n"));
271    foreach_jcr(jcr) {
272       if (jcr->JobStatus == JS_WaitFD) {
273          bnet_fsend(user, _("%s Job %s waiting for Client connection.\n"),
274             job_type_to_str(jcr->JobType), jcr->Job);
275       }
276       dcr = jcr->dcr;
277       rdcr = jcr->read_dcr;
278       if ((dcr && dcr->device) || rdcr && rdcr->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          if (rdcr && rdcr->device) {
288             bnet_fsend(user, _("Reading: %s %s job %s JobId=%d Volume=\"%s\"\n"
289                             "    pool=\"%s\" device=\"%s\"\n"),
290                    job_level_to_str(jcr->JobLevel),
291                    job_type_to_str(jcr->JobType),
292                    JobName,
293                    jcr->JobId,
294                    rdcr->VolumeName,
295                    rdcr->pool_name,
296                    rdcr->dev?rdcr->dev->print_name(): 
297                             rdcr->device->device_name);
298          }
299          if (dcr && dcr->device) {
300             bnet_fsend(user, _("Writing: %s %s job %s JobId=%d Volume=\"%s\"\n"
301                             "    pool=\"%s\" device=\"%s\"\n"),
302                    job_level_to_str(jcr->JobLevel),
303                    job_type_to_str(jcr->JobType),
304                    JobName,
305                    jcr->JobId,
306                    dcr->VolumeName,
307                    dcr->pool_name,
308                    dcr->dev?dcr->dev->print_name(): 
309                             dcr->device->device_name);
310          }
311          sec = time(NULL) - jcr->run_time;
312          if (sec <= 0) {
313             sec = 1;
314          }
315          bps = jcr->JobBytes / sec;
316          bnet_fsend(user, _("    Files=%s Bytes=%s Bytes/sec=%s\n"),
317             edit_uint64_with_commas(jcr->JobFiles, b1),
318             edit_uint64_with_commas(jcr->JobBytes, b2),
319             edit_uint64_with_commas(bps, b3));
320          found = true;
321 #ifdef DEBUG
322          if (jcr->file_bsock) {
323             bnet_fsend(user, _("    FDReadSeqNo=%s in_msg=%u out_msg=%d fd=%d\n"),
324                edit_uint64_with_commas(jcr->file_bsock->read_seqno, b1),
325                jcr->file_bsock->in_msg_no, jcr->file_bsock->out_msg_no,
326                jcr->file_bsock->fd);
327          } else {
328             bnet_fsend(user, _("    FDSocket closed\n"));
329          }
330 #endif
331       }
332       free_jcr(jcr);
333    }
334    if (!found) {
335       bnet_fsend(user, _("No Jobs running.\n"));
336    }
337    bnet_fsend(user, _("====\n"));
338 }
339
340 static void list_terminated_jobs(void *arg)
341 {
342    char dt[MAX_TIME_LENGTH], b1[30], b2[30];
343    char level[10];
344    struct s_last_job *je;
345    const char *msg;
346
347    if (last_jobs->size() == 0) {
348       msg = _("No Terminated Jobs.\n");
349       sendit(msg, strlen(msg), arg);
350       return;
351    }
352    lock_last_jobs_list();
353    msg =  _("\nTerminated Jobs:\n");
354    sendit(msg, strlen(msg), arg);
355    msg =  _(" JobId  Level   Files          Bytes Status   Finished        Name \n");
356    sendit(msg, strlen(msg), arg);
357    msg = _("======================================================================\n");
358    sendit(msg, strlen(msg), arg);
359    foreach_dlist(je, last_jobs) {
360       char JobName[MAX_NAME_LENGTH];
361       const char *termstat;
362       char buf[1000];
363
364       bstrftime_nc(dt, sizeof(dt), je->end_time);
365       switch (je->JobType) {
366       case JT_ADMIN:
367       case JT_RESTORE:
368          bstrncpy(level, "    ", sizeof(level));
369          break;
370       default:
371          bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
372          level[4] = 0;
373          break;
374       }
375       switch (je->JobStatus) {
376       case JS_Created:
377          termstat = _("Created");
378          break;
379       case JS_FatalError:
380       case JS_ErrorTerminated:
381          termstat = _("Error");
382          break;
383       case JS_Differences:
384          termstat = _("Diffs");
385          break;
386       case JS_Canceled:
387          termstat = _("Cancel");
388          break;
389       case JS_Terminated:
390          termstat = _("OK");
391          break;
392       default:
393          termstat = _("Other");
394          break;
395       }
396       bstrncpy(JobName, je->Job, sizeof(JobName));
397       /* There are three periods after the Job name */
398       char *p;
399       for (int i=0; i<3; i++) {
400          if ((p=strrchr(JobName, '.')) != NULL) {
401             *p = 0;
402          }
403       }
404       bsnprintf(buf, sizeof(buf), _("%6d  %-6s %8s %14s %-7s  %-8s %s\n"),
405          je->JobId,
406          level,
407          edit_uint64_with_commas(je->JobFiles, b1),
408          edit_uint64_with_commas(je->JobBytes, b2),
409          termstat,
410          dt, JobName);
411       sendit(buf, strlen(buf), arg);
412    }
413    sendit(_("====\n"), 5, arg);
414    unlock_last_jobs_list();
415 }
416
417 /*
418  * Convert Job Level into a string
419  */
420 static const char *level_to_str(int level)
421 {
422    const char *str;
423
424    switch (level) {
425    case L_BASE:
426       str = _("Base");
427    case L_FULL:
428       str = _("Full");
429       break;
430    case L_INCREMENTAL:
431       str = _("Incremental");
432       break;
433    case L_DIFFERENTIAL:
434       str = _("Differential");
435       break;
436    case L_SINCE:
437       str = _("Since");
438       break;
439    case L_VERIFY_CATALOG:
440       str = _("Verify Catalog");
441       break;
442    case L_VERIFY_INIT:
443       str = _("Init Catalog");
444       break;
445    case L_VERIFY_VOLUME_TO_CATALOG:
446       str = _("Volume to Catalog");
447       break;
448    case L_VERIFY_DISK_TO_CATALOG:
449       str = _("Disk to Catalog");
450       break;
451    case L_VERIFY_DATA:
452       str = _("Data");
453       break;
454    case L_NONE:
455       str = " ";
456       break;
457    default:
458       str = _("Unknown Job Level");
459       break;
460    }
461    return str;
462 }
463
464 /*
465  * Send to Director
466  */
467 static void sendit(const char *msg, int len, void *arg)
468 {
469    BSOCK *user = (BSOCK *)arg;
470
471    memcpy(user->msg, msg, len+1);
472    user->msglen = len+1;
473    bnet_send(user);
474 }
475
476 /*
477  * .status command from Director
478  */
479 bool qstatus_cmd(JCR *jcr)
480 {
481    BSOCK *dir = jcr->dir_bsock;
482    POOL_MEM time;
483    JCR *njcr;
484    s_last_job* job;
485
486    if (sscanf(dir->msg, qstatus, time.c_str()) != 1) {
487       pm_strcpy(jcr->errmsg, dir->msg);
488       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
489       bnet_fsend(dir, _("3900 Bad .status command, missing argument.\n"));
490       bnet_sig(dir, BNET_EOD);
491       return false;
492    }
493    unbash_spaces(time);
494
495    if (strcmp(time.c_str(), "current") == 0) {
496       bnet_fsend(dir, OKqstatus, time.c_str());
497       foreach_jcr(njcr) {
498          if (njcr->JobId != 0) {
499             bnet_fsend(dir, DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
500          }
501          free_jcr(njcr);
502       }
503    } else if (strcmp(time.c_str(), "last") == 0) {
504       bnet_fsend(dir, OKqstatus, time.c_str());
505       if ((last_jobs) && (last_jobs->size() > 0)) {
506          job = (s_last_job*)last_jobs->last();
507          bnet_fsend(dir, DotStatusJob, job->JobId, job->JobStatus, job->Errors);
508       }
509    } else {
510       pm_strcpy(jcr->errmsg, dir->msg);
511       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
512       bnet_fsend(dir, _("3900 Bad .status command, wrong argument.\n"));
513       bnet_sig(dir, BNET_EOD);
514       return false;
515    }
516    bnet_sig(dir, BNET_EOD);
517    return true;
518 }