]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bls.c
kes Change Bacula trademark owner from John Walker to Kern Sibbald
[bacula/bacula] / bacula / src / stored / bls.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2008 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
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          }
389          num_files++;
390          return true;
391       }
392
393       if (attr->file_index != rec->FileIndex) {
394          Emsg2(forge_on?M_WARNING:M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"),
395                rec->FileIndex, attr->file_index);
396       }
397
398       attr->data_stream = decode_stat(attr->attr, &attr->statp, &attr->LinkFI);
399       build_attr_output_fnames(jcr, attr);
400
401       if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
402          if (verbose) {
403             Pmsg5(-1, _("FileIndex=%d VolSessionId=%d VolSessionTime=%d Stream=%d DataLen=%d\n"),
404                   rec->FileIndex, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
405          }
406          print_ls_output(jcr, attr);
407          num_files++;
408       }
409    } else if (rec->Stream == STREAM_PLUGIN_NAME) {
410       Pmsg1(000, "Plugin name: %s\n", rec->data);
411    }
412       
413    return true;
414 }
415
416
417 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
418 {
419    const char *rtype;
420    memset(sessrec, 0, sizeof(sessrec));
421    switch (rec->FileIndex) {
422    case PRE_LABEL:
423       rtype = _("Fresh Volume Label");
424       break;
425    case VOL_LABEL:
426       rtype = _("Volume Label");
427       unser_volume_label(dev, rec);
428       break;
429    case SOS_LABEL:
430       rtype = _("Begin Job Session");
431       unser_session_label(sessrec, rec);
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    default:
441       rtype = _("Unknown");
442       break;
443    }
444    Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
445          rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
446    if (verbose) {
447       Pmsg5(-1, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
448             rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
449    }
450 }
451
452
453 /* Dummies to replace askdir.c */
454 bool    dir_find_next_appendable_volume(DCR *dcr) { return 1;}
455 bool    dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
456 bool    dir_create_jobmedia_record(DCR *dcr) { return 1; }
457 bool    dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
458 bool    dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
459 bool    dir_send_job_status(JCR *jcr) {return 1;}
460 int     generate_job_event(JCR *jcr, const char *event) { return 1; }
461        
462
463 bool dir_ask_sysop_to_mount_volume(DCR *dcr, int /*mode*/)
464 {
465    DEVICE *dev = dcr->dev;
466    fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
467       dcr->VolumeName, dev->print_name());
468    dev->close();
469    getchar();
470    return true;
471 }
472
473 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw  writing)
474 {
475    Dmsg0(100, "Fake dir_get_volume_info\n");
476    bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
477    dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
478    Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->VolCatInfo.VolCatName, dcr->VolCatInfo.VolCatParts);
479    return 1;
480 }