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