2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2017 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
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.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
21 * Dumb program to do an "ls" of a Bacula 1.0 mortal file.
28 #include "findlib/find.h"
29 #include "lib/cmd_parser.h"
31 extern bool parse_sd_config(CONFIG *config, const char *configfile, int exit_code);
33 static void do_blocks(char *infname);
34 static void do_jobs(char *infname);
35 static void do_ls(char *fname);
36 static void do_close(JCR *jcr);
37 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec);
38 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
42 static bool dump_label = false;
43 static bool list_blocks = false;
44 static bool list_jobs = false;
45 static DEV_RECORD *rec;
47 static SESSION_LABEL sessrec;
48 static uint32_t num_files = 0;
50 static CONFIG *config;
53 #define CONFIG_FILE "bacula-sd.conf"
54 char *configfile = NULL;
55 bool detect_errors = false;
60 static BSR *bsr = NULL;
66 "\n%sVersion: %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"
76 " (no j or k option) list saved files\n"
78 " -p proceed inspite of errors\n"
79 " -V specify Volume names (separated by |)\n"
80 " -E Check records to detect errors\n"
82 " -? print this message\n\n"), 2000, "", VERSION, BDATE);
87 int main (int argc, char *argv[])
92 char *VolumeName= NULL;
94 bool ignore_label_errors = false;
95 BtoolsAskDirHandler askdir_handler;
97 init_askdir_handler(&askdir_handler);
98 setlocale(LC_ALL, "");
99 bindtextdomain("bacula", LOCALEDIR);
100 textdomain("bacula");
104 working_directory = "/tmp";
105 my_name_is(argc, argv, "bls");
106 init_msg(NULL, NULL); /* initialize message handler */
110 ff = init_find_files();
112 while ((ch = getopt(argc, argv, "b:c:d:e:i:jkLpvV:?EDF:")) != -1) {
119 detect_errors = true;
122 case 'c': /* specify config file */
123 if (configfile != NULL) {
126 configfile = bstrdup(optarg);
129 case 'd': /* debug level */
130 if (*optarg == 't') {
131 dbg_timestamp = true;
134 /* We probably find a tag list -d 10,sql,bvfs */
135 if ((p = strchr(optarg, ',')) != NULL) {
138 debug_level = atoi(optarg);
139 if (debug_level <= 0) {
143 debug_parse_tags(p+1, &debug_level_tags);
148 case 'e': /* exclude list */
149 if ((fd = fopen(optarg, "rb")) == NULL) {
151 Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
152 optarg, be.bstrerror());
155 while (fgets(line, sizeof(line), fd) != NULL) {
156 strip_trailing_junk(line);
157 Dmsg1(100, "add_exclude %s\n", line);
158 add_fname_to_exclude_list(ff, line);
163 case 'i': /* include list */
164 if ((fd = fopen(optarg, "rb")) == NULL) {
166 Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
167 optarg, be.bstrerror());
170 while (fgets(line, sizeof(line), fd) != NULL) {
171 strip_trailing_junk(line);
172 Dmsg1(100, "add_include %s\n", line);
173 add_fname_to_include_list(ff, 0, line);
191 ignore_label_errors = true;
199 case 'V': /* Volume name */
213 Pmsg0(0, _("No archive name specified\n"));
217 if (configfile == NULL) {
218 configfile = bstrdup(CONFIG_FILE);
221 config = New(CONFIG());
222 parse_sd_config(config, configfile, M_ERROR_TERM);
224 load_sd_plugins(me->plugin_directory);
226 if (ff->included_files_list == NULL) {
227 add_fname_to_include_list(ff, 0, "/");
230 for (i=0; i < argc; i++) {
232 bsr = parse_bsr(NULL, bsrName);
234 jcr = setup_jcr("bls", argv[i], bsr, VolumeName, SD_READ);
238 jcr->ignore_label_errors = ignore_label_errors;
245 attr = new_attr(jcr);
247 * Assume that we have already read the volume label.
248 * If on second or subsequent volume, adjust buffer pointer
250 if (dev->VolHdr.PrevVolumeName[0] != 0) { /* second volume */
252 "Warning, this Volume is a continuation of Volume %s\n"),
253 dev->VolHdr.PrevVolumeName);
258 } else if (list_jobs) {
268 term_include_exclude_files(ff);
272 return (errors > 0)? 1 : 0;
277 static void do_close(JCR *jcr)
279 release_device(jcr->dcr);
287 /* List just block information */
288 static void do_blocks(char *infname)
290 DEV_BLOCK *block = dcr->block;
291 char buf1[100], buf2[100];
293 if (!dcr->read_block_from_device(NO_BLOCK_NUMBER_CHECK)) {
294 Dmsg1(100, "!read_block(): ERR=%s\n", dev->print_errmsg());
296 if (!mount_next_read_volume(dcr)) {
297 Jmsg(jcr, M_INFO, 0, _("Got EOM at file %u on device %s, Volume \"%s\"\n"),
298 dev->file, dev->print_name(), dcr->VolumeName);
301 /* Read and discard Volume label */
303 record = new_record();
304 dcr->read_block_from_device(NO_BLOCK_NUMBER_CHECK);
305 read_record_from_block(dcr, record);
306 get_session_record(dev, record, &sessrec);
308 Jmsg(jcr, M_INFO, 0, _("Mounted Volume \"%s\".\n"), dcr->VolumeName);
309 } else if (dev->at_eof()) {
310 Jmsg(jcr, M_INFO, 0, _("End of file %u on device %s, Volume \"%s\"\n"),
311 dev->file, dev->print_name(), dcr->VolumeName);
312 Dmsg0(20, "read_record got eof. try again\n");
314 } else if (dev->is_short_block()) {
315 Jmsg(jcr, M_INFO, 0, "%s", dev->print_errmsg());
320 display_tape_error_status(jcr, dev);
324 if (!match_bsr_block(bsr, block)) {
325 Dmsg5(100, "reject Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
326 block->BlockNumber, block->block_len, block->BlockVer,
327 block->VolSessionId, block->VolSessionTime);
330 Dmsg5(100, "Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
331 block->BlockNumber, block->block_len, block->BlockVer,
332 block->VolSessionId, block->VolSessionTime);
334 read_record_from_block(dcr, rec);
335 Pmsg8(-1, "Addr=%llu blk_num=%u blen=%u First rec FI=%s SessId=%u SessTim=%u Strm=%s rlen=%d\n",
336 dev->get_full_addr(),
337 block->BlockNumber, block->block_len,
338 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId, rec->VolSessionTime,
339 stream_to_ascii_ex(buf2, rec->Stream, rec->FileIndex), rec->data_len);
341 } else if (verbose > 1) { /* detailed block dump */
342 Pmsg5(-1, "Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
343 block->BlockNumber, block->block_len, block->BlockVer,
344 block->VolSessionId, block->VolSessionTime);
345 dump_block(dcr->dev, block, "", true);
347 printf("Block: %d size=%d\n", block->BlockNumber, block->block_len);
355 * We are only looking for labels or in particular Job Session records
357 static bool jobs_cb(DCR *dcr, DEV_RECORD *rec)
359 if (rec->FileIndex < 0) {
360 dump_label_record(dcr->dev, rec, verbose, detect_errors);
366 /* Do list job records */
367 static void do_jobs(char *infname)
369 if (!read_records(dcr, jobs_cb, mount_next_read_volume)) {
374 /* Do an ls type listing of an archive */
375 static void do_ls(char *infname)
378 dev->dump_volume_label();
381 if (!read_records(dcr, record_cb, mount_next_read_volume)) {
384 printf("%u files found.\n", num_files);
389 * Called here for each record from read_records()
391 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
393 if (verbose && rec->FileIndex < 0) {
394 dump_label_record(dcr->dev, rec, verbose, false);
398 /* File Attributes stream */
399 if (rec->maskedStream == STREAM_UNIX_ATTRIBUTES ||
400 rec->maskedStream == STREAM_UNIX_ATTRIBUTES_EX) {
401 if (!unpack_attributes_record(jcr, rec->Stream, rec->data, rec->data_len, attr)) {
403 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
405 Emsg0(M_ERROR, 0, _("Attrib unpack error!\n"));
411 attr->data_stream = decode_stat(attr->attr, &attr->statp, sizeof(attr->statp), &attr->LinkFI);
412 build_attr_output_fnames(jcr, attr);
414 if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
416 Pmsg5(000, _("FileIndex=%d VolSessionId=%d VolSessionTime=%d Stream=%d DataLen=%d\n"),
417 rec->FileIndex, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
419 print_ls_output(jcr, attr);
422 } else if (rec->maskedStream == STREAM_PLUGIN_NAME) {
424 int len = MIN(rec->data_len+1, sizeof(data));
425 bstrncpy(data, rec->data, len);
426 Dmsg1(100, "Plugin data: %s\n", data);
427 } else if (rec->maskedStream == STREAM_RESTORE_OBJECT) {
428 Dmsg0(100, "Restore Object record\n");
429 } else if (rec->maskedStream == STREAM_ADATA_BLOCK_HEADER) {
430 Dmsg0(000, "Adata block header\n");
431 } else if (rec->maskedStream == STREAM_ADATA_RECORD_HEADER) {
432 Dmsg0(000, "Adata record header\n");
439 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
442 memset(sessrec, 0, sizeof(SESSION_LABEL));
444 switch (rec->FileIndex) {
446 rtype = _("Fresh Volume Label");
449 rtype = _("Volume Label");
450 unser_volume_label(dev, rec);
453 rtype = _("Begin Job Session");
454 unser_session_label(sessrec, rec);
455 jcr->JobId = sessrec->JobId;
458 rtype = _("End Job Session");
462 rtype = _("End of Medium");
465 rtype = _("End of Physical Medium");
468 rtype = _("Start of object");
471 rtype = _("End of object");
474 rtype = _("Unknown");
475 Dmsg1(10, "FI rtype=%d unknown\n", rec->FileIndex);
478 Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
479 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
481 Pmsg5(-1, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
482 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);