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