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