]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bls.c
Print plugin end records in bls
[bacula/bacula] / bacula / src / stored / bls.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2010 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *
30  *  Dumb program to do an "ls" of a Bacula 1.0 mortal file.
31  * 
32  *  Kern Sibbald, MM
33  *
34  */
35
36 #include "bacula.h"
37 #include "stored.h"
38 #include "findlib/find.h"
39
40 /* Dummy functions */
41 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
42 extern bool parse_sd_config(CONFIG *config, const char *configfile, int exit_code);
43
44 static void do_blocks(char *infname);
45 static void do_jobs(char *infname);
46 static void do_ls(char *fname);
47 static void do_close(JCR *jcr);
48 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec);
49 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
50
51 static DEVICE *dev;
52 static DCR *dcr;
53 static bool dump_label = false;
54 static bool list_blocks = false;
55 static bool list_jobs = false;
56 static DEV_RECORD *rec;
57 static JCR *jcr;
58 static SESSION_LABEL sessrec;
59 static uint32_t num_files = 0;
60 static ATTR *attr;
61 static CONFIG *config;
62
63 #define CONFIG_FILE "bacula-sd.conf"
64 char *configfile = NULL;
65 STORES *me = NULL;                    /* our Global resource */
66 bool forge_on = false;
67 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
68 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
69
70
71 static FF_PKT *ff;
72
73 static BSR *bsr = NULL;
74
75 static void usage()
76 {
77    fprintf(stderr, _(
78 PROG_COPYRIGHT
79 "\nVersion: %s (%s)\n\n"
80 "Usage: bls [options] <device-name>\n"
81 "       -b <file>       specify a bootstrap file\n"
82 "       -c <file>       specify a Storage configuration file\n"
83 "       -d <nn>         set debug level to <nn>\n"
84 "       -dt             print timestamp in debug output\n"
85 "       -e <file>       exclude list\n"
86 "       -i <file>       include list\n"
87 "       -j              list jobs\n"
88 "       -k              list blocks\n"
89 "    (no j or k option) list saved files\n"
90 "       -L              dump label\n"
91 "       -p              proceed inspite of errors\n"
92 "       -v              be verbose\n"
93 "       -V              specify Volume names (separated by |)\n"
94 "       -?              print this message\n\n"), 2000, VERSION, BDATE);
95    exit(1);
96 }
97
98
99 int main (int argc, char *argv[])
100 {
101    int i, ch;
102    FILE *fd;
103    char line[1000];
104    char *VolumeName= NULL;
105    char *bsrName = NULL;
106    bool ignore_label_errors = false;
107
108    setlocale(LC_ALL, "");
109    bindtextdomain("bacula", LOCALEDIR);
110    textdomain("bacula");
111    init_stack_dump();
112    lmgr_init_thread();
113
114    working_directory = "/tmp";
115    my_name_is(argc, argv, "bls");
116    init_msg(NULL, NULL);              /* initialize message handler */
117
118    OSDependentInit();
119
120    ff = init_find_files();
121
122    while ((ch = getopt(argc, argv, "b:c:d:e:i:jkLpvV:?")) != -1) {
123       switch (ch) {
124       case 'b':
125          bsrName = optarg;
126          break;
127
128       case 'c':                    /* specify config file */
129          if (configfile != NULL) {
130             free(configfile);
131          }
132          configfile = bstrdup(optarg);
133          break;
134
135       case 'd':                    /* debug level */
136          if (*optarg == 't') {
137             dbg_timestamp = true;
138          } else {
139             debug_level = atoi(optarg);
140             if (debug_level <= 0) {
141                debug_level = 1;
142             }
143          }
144          break;
145
146       case 'e':                    /* exclude list */
147          if ((fd = fopen(optarg, "rb")) == NULL) {
148             berrno be;
149             Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
150                optarg, be.bstrerror());
151             exit(1);
152          }
153          while (fgets(line, sizeof(line), fd) != NULL) {
154             strip_trailing_junk(line);
155             Dmsg1(100, "add_exclude %s\n", line);
156             add_fname_to_exclude_list(ff, line);
157          }
158          fclose(fd);
159          break;
160
161       case 'i':                    /* include list */
162          if ((fd = fopen(optarg, "rb")) == NULL) {
163             berrno be;
164             Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
165                optarg, be.bstrerror());
166             exit(1);
167          }
168          while (fgets(line, sizeof(line), fd) != NULL) {
169             strip_trailing_junk(line);
170             Dmsg1(100, "add_include %s\n", line);
171             add_fname_to_include_list(ff, 0, line);
172          }
173          fclose(fd);
174          break;
175
176       case 'j':
177          list_jobs = true;
178          break;
179
180       case 'k':
181          list_blocks = true;
182          break;
183
184       case 'L':
185          dump_label = true;
186          break;
187
188       case 'p':
189          ignore_label_errors = true;
190          forge_on = true;
191          break;
192
193       case 'v':
194          verbose++;
195          break;
196
197       case 'V':                    /* Volume name */
198          VolumeName = optarg;
199          break;
200
201       case '?':
202       default:
203          usage();
204
205       } /* end switch */
206    } /* end while */
207    argc -= optind;
208    argv += optind;
209
210    if (!argc) {
211       Pmsg0(0, _("No archive name specified\n"));
212       usage();
213    }
214
215    if (configfile == NULL) {
216       configfile = bstrdup(CONFIG_FILE);
217    }
218
219    config = new_config_parser();
220    parse_sd_config(config, configfile, M_ERROR_TERM);
221
222    if (ff->included_files_list == NULL) {
223       add_fname_to_include_list(ff, 0, "/");
224    }
225
226    for (i=0; i < argc; i++) {
227       if (bsrName) {
228          bsr = parse_bsr(NULL, bsrName);
229       }
230       jcr = setup_jcr("bls", argv[i], bsr, VolumeName, 1); /* acquire for read */
231       if (!jcr) {
232          exit(1);
233       }
234       jcr->ignore_label_errors = ignore_label_errors;
235       dev = jcr->dcr->dev;
236       if (!dev) {
237          exit(1);
238       }
239       dcr = jcr->dcr;
240       rec = new_record();
241       attr = new_attr(jcr);
242       /*
243        * Assume that we have already read the volume label.
244        * If on second or subsequent volume, adjust buffer pointer
245        */
246       if (dev->VolHdr.PrevVolumeName[0] != 0) { /* second volume */
247          Pmsg1(0, _("\n"
248                     "Warning, this Volume is a continuation of Volume %s\n"),
249                 dev->VolHdr.PrevVolumeName);
250       }
251
252       if (list_blocks) {
253          do_blocks(argv[i]);
254       } else if (list_jobs) {
255          do_jobs(argv[i]);
256       } else {
257          do_ls(argv[i]);
258       }
259       do_close(jcr);
260    }
261    if (bsr) {
262       free_bsr(bsr);
263    }
264    term_include_exclude_files(ff);
265    term_find_files(ff);
266    return 0;
267 }
268
269
270 static void do_close(JCR *jcr)
271 {
272    release_device(jcr->dcr);
273    free_attr(attr);
274    free_record(rec);
275    free_jcr(jcr);
276    dev->term();
277 }
278
279
280 /* List just block information */
281 static void do_blocks(char *infname)
282 {
283    DEV_BLOCK *block = dcr->block;
284    char buf1[100], buf2[100];
285    for ( ;; ) {
286       if (!read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK)) {
287          Dmsg1(100, "!read_block(): ERR=%s\n", dev->bstrerror());
288          if (dev->at_eot()) {
289             if (!mount_next_read_volume(dcr)) {
290                Jmsg(jcr, M_INFO, 0, _("Got EOM at file %u on device %s, Volume \"%s\"\n"),
291                   dev->file, dev->print_name(), dcr->VolumeName);
292                break;
293             }
294             /* Read and discard Volume label */
295             DEV_RECORD *record;
296             record = new_record();
297             read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK);
298             read_record_from_block(dcr, block, record);
299             get_session_record(dev, record, &sessrec);
300             free_record(record);
301             Jmsg(jcr, M_INFO, 0, _("Mounted Volume \"%s\".\n"), dcr->VolumeName);
302          } else if (dev->at_eof()) {
303             Jmsg(jcr, M_INFO, 0, _("End of file %u on device %s, Volume \"%s\"\n"),
304                dev->file, dev->print_name(), dcr->VolumeName);
305             Dmsg0(20, "read_record got eof. try again\n");
306             continue;
307          } else if (dev->is_short_block()) {
308             Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
309             continue;
310          } else {
311             /* I/O error */
312             display_tape_error_status(jcr, dev);
313             break;
314          }
315       }
316       if (!match_bsr_block(bsr, block)) {
317          Dmsg5(100, "reject Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
318             block->BlockNumber, block->block_len, block->BlockVer,
319             block->VolSessionId, block->VolSessionTime);
320          continue;
321       }
322       Dmsg5(100, "Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
323         block->BlockNumber, block->block_len, block->BlockVer,
324         block->VolSessionId, block->VolSessionTime);
325       if (verbose == 1) {
326          read_record_from_block(dcr, block, rec);
327          Pmsg9(-1, _("File:blk=%u:%u blk_num=%u blen=%u First rec FI=%s SessId=%u SessTim=%u Strm=%s rlen=%d\n"),
328               dev->file, dev->block_num,
329               block->BlockNumber, block->block_len,
330               FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId, rec->VolSessionTime,
331               stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
332          rec->remainder = 0;
333       } else if (verbose > 1) {
334          dump_block(block, "");
335       } else {
336          printf(_("Block: %d size=%d\n"), block->BlockNumber, block->block_len);
337       }
338
339    }
340    return;
341 }
342
343 /*
344  * We are only looking for labels or in particular Job Session records
345  */
346 static bool jobs_cb(DCR *dcr, DEV_RECORD *rec)
347 {
348    if (rec->FileIndex < 0) {
349       dump_label_record(dcr->dev, rec, verbose);
350    }
351    rec->remainder = 0;
352    return true;
353 }
354
355 /* Do list job records */
356 static void do_jobs(char *infname)
357 {
358    read_records(dcr, jobs_cb, mount_next_read_volume);
359 }
360
361 /* Do an ls type listing of an archive */
362 static void do_ls(char *infname)
363 {
364    if (dump_label) {
365       dump_volume_label(dev);
366       return;
367    }
368    read_records(dcr, record_cb, mount_next_read_volume);
369    printf("%u files found.\n", num_files);
370 }
371
372 /*
373  * Called here for each record from read_records()
374  */
375 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
376 {
377    if (rec->FileIndex < 0) {
378       get_session_record(dev, rec, &sessrec);
379       return true;
380    }
381    /* File Attributes stream */
382    if (rec->Stream == STREAM_UNIX_ATTRIBUTES ||
383        rec->Stream == STREAM_UNIX_ATTRIBUTES_EX) {
384
385       if (!unpack_attributes_record(jcr, rec->Stream, rec->data, attr)) {
386          if (!forge_on) {
387             Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
388          } else {
389             Emsg0(M_ERROR, 0, _("Attrib unpack error!\n"));
390          }
391          num_files++;
392          return true;
393       }
394
395       attr->data_stream = decode_stat(attr->attr, &attr->statp, &attr->LinkFI);
396       build_attr_output_fnames(jcr, attr);
397
398       if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
399          if (verbose) {
400             Pmsg5(-1, _("FileIndex=%d VolSessionId=%d VolSessionTime=%d Stream=%d DataLen=%d\n"),
401                   rec->FileIndex, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
402          }
403          print_ls_output(jcr, attr);
404          num_files++;
405       }
406    } else if (rec->Stream == STREAM_PLUGIN_NAME) {
407       char data[100];
408       int len = MIN(rec->data_len+1, sizeof(data));
409       bstrncpy(data, rec->data, len);
410       Pmsg1(000, "Plugin data: %s\n", data);
411    } else if (rec->Stream == STREAM_RESTORE_OBJECT) {
412       Pmsg0(000, "Restore Object record\n");
413    }
414       
415    return true;
416 }
417
418
419 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
420 {
421    const char *rtype;
422    memset(sessrec, 0, sizeof(sessrec));
423    jcr->JobId = 0;
424    switch (rec->FileIndex) {
425    case PRE_LABEL:
426       rtype = _("Fresh Volume Label");
427       break;
428    case VOL_LABEL:
429       rtype = _("Volume Label");
430       unser_volume_label(dev, rec);
431       break;
432    case SOS_LABEL:
433       rtype = _("Begin Job Session");
434       unser_session_label(sessrec, rec);
435       jcr->JobId = sessrec->JobId;
436       break;
437    case EOS_LABEL:
438       rtype = _("End Job Session");
439       break;
440    case 0:
441    case EOM_LABEL:
442       rtype = _("End of Medium");
443       break;
444    case EOT_LABEL:
445       rtype = _("End of Physical Medium");
446       break;
447    case SOB_LABEL:
448       rtype = _("Start of object");
449       break;
450    case EOB_LABEL:
451       rtype = _("End of object");
452       break;
453    default:
454       rtype = _("Unknown");
455       Dmsg1(10, "FI rtype=%d unknown\n", rec->FileIndex);     
456       break;
457    }
458    Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
459          rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
460    if (verbose) {
461       Pmsg5(-1, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
462             rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
463    }
464 }
465
466
467 /* Dummies to replace askdir.c */
468 bool    dir_find_next_appendable_volume(DCR *dcr) { return 1;}
469 bool    dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
470 bool    dir_create_jobmedia_record(DCR *dcr, bool zero) { return 1; }
471 bool    dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
472 bool    dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
473 bool    dir_send_job_status(JCR *jcr) {return 1;}
474 int     generate_job_event(JCR *jcr, const char *event) { return 1; }
475        
476
477 bool dir_ask_sysop_to_mount_volume(DCR *dcr, int /*mode*/)
478 {
479    DEVICE *dev = dcr->dev;
480    fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
481       dcr->VolumeName, dev->print_name());
482    dev->close();
483    getchar();
484    return true;
485 }
486
487 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw  writing)
488 {
489    Dmsg0(100, "Fake dir_get_volume_info\n");
490    dcr->setVolCatName(dcr->VolumeName);
491    dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
492    Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->getVolCatName(), dcr->VolCatInfo.VolCatParts);
493    return 1;
494 }