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