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