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