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