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