2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
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.
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 Bacula® is a registered trademark of Kern Sibbald.
18 * Dumb program to do an "ls" of a Bacula 1.0 mortal file.
26 #include "findlib/find.h"
28 extern bool parse_sd_config(CONFIG *config, const char *configfile, int exit_code);
30 static void do_blocks(char *infname);
31 static void do_jobs(char *infname);
32 static void do_ls(char *fname);
33 static void do_close(JCR *jcr);
34 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec);
35 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
39 static bool dump_label = false;
40 static bool list_blocks = false;
41 static bool list_jobs = false;
42 static DEV_RECORD *rec;
44 static SESSION_LABEL sessrec;
45 static uint32_t num_files = 0;
47 static CONFIG *config;
50 #define CONFIG_FILE "bacula-sd.conf"
51 char *configfile = NULL;
52 STORES *me = NULL; /* our Global resource */
53 bool forge_on = false;
54 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
55 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
60 static BSR *bsr = NULL;
66 "\nVersion: %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"
80 " -V specify Volume names (separated by |)\n"
81 " -? print this message\n\n"), 2000, VERSION, BDATE);
86 int main (int argc, char *argv[])
91 char *VolumeName= NULL;
93 bool ignore_label_errors = false;
95 setlocale(LC_ALL, "");
96 bindtextdomain("bacula", LOCALEDIR);
101 working_directory = "/tmp";
102 my_name_is(argc, argv, "bls");
103 init_msg(NULL, NULL); /* initialize message handler */
107 ff = init_find_files();
109 while ((ch = getopt(argc, argv, "b:c:d:e:i:jkLpvV:?")) != -1) {
115 case 'c': /* specify config file */
116 if (configfile != NULL) {
119 configfile = bstrdup(optarg);
122 case 'd': /* debug level */
123 if (*optarg == 't') {
124 dbg_timestamp = true;
126 debug_level = atoi(optarg);
127 if (debug_level <= 0) {
133 case 'e': /* exclude list */
134 if ((fd = fopen(optarg, "rb")) == NULL) {
136 Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
137 optarg, be.bstrerror());
140 while (fgets(line, sizeof(line), fd) != NULL) {
141 strip_trailing_junk(line);
142 Dmsg1(100, "add_exclude %s\n", line);
143 add_fname_to_exclude_list(ff, line);
148 case 'i': /* include list */
149 if ((fd = fopen(optarg, "rb")) == NULL) {
151 Pmsg2(0, _("Could not open include 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_include %s\n", line);
158 add_fname_to_include_list(ff, 0, line);
176 ignore_label_errors = true;
184 case 'V': /* Volume name */
198 Pmsg0(0, _("No archive name specified\n"));
202 if (configfile == NULL) {
203 configfile = bstrdup(CONFIG_FILE);
206 config = new_config_parser();
207 parse_sd_config(config, configfile, M_ERROR_TERM);
209 load_sd_plugins(me->plugin_directory);
211 if (ff->included_files_list == NULL) {
212 add_fname_to_include_list(ff, 0, "/");
215 for (i=0; i < argc; i++) {
217 bsr = parse_bsr(NULL, bsrName);
219 jcr = setup_jcr("bls", argv[i], bsr, VolumeName, SD_READ);
223 jcr->ignore_label_errors = ignore_label_errors;
230 attr = new_attr(jcr);
232 * Assume that we have already read the volume label.
233 * If on second or subsequent volume, adjust buffer pointer
235 if (dev->VolHdr.PrevVolumeName[0] != 0) { /* second volume */
237 "Warning, this Volume is a continuation of Volume %s\n"),
238 dev->VolHdr.PrevVolumeName);
243 } else if (list_jobs) {
253 term_include_exclude_files(ff);
259 static void do_close(JCR *jcr)
261 release_device(jcr->dcr);
269 /* List just block information */
270 static void do_blocks(char *infname)
272 DEV_BLOCK *block = dcr->block;
273 char buf1[100], buf2[100];
275 if (!dcr->read_block_from_device(NO_BLOCK_NUMBER_CHECK)) {
276 Dmsg1(100, "!read_block(): ERR=%s\n", dev->print_errmsg());
278 if (!mount_next_read_volume(dcr)) {
279 Jmsg(jcr, M_INFO, 0, _("Got EOM at file %u on device %s, Volume \"%s\"\n"),
280 dev->file, dev->print_name(), dcr->VolumeName);
283 /* Read and discard Volume label */
285 record = new_record();
286 dcr->read_block_from_device(NO_BLOCK_NUMBER_CHECK);
287 read_record_from_block(dcr, record);
288 get_session_record(dev, record, &sessrec);
290 Jmsg(jcr, M_INFO, 0, _("Mounted Volume \"%s\".\n"), dcr->VolumeName);
291 } else if (dev->at_eof()) {
292 Jmsg(jcr, M_INFO, 0, _("End of file %u on device %s, Volume \"%s\"\n"),
293 dev->file, dev->print_name(), dcr->VolumeName);
294 Dmsg0(20, "read_record got eof. try again\n");
296 } else if (dev->is_short_block()) {
297 Jmsg(jcr, M_INFO, 0, "%s", dev->print_errmsg());
301 display_tape_error_status(jcr, dev);
305 if (!match_bsr_block(bsr, block)) {
306 Dmsg5(100, "reject Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
307 block->BlockNumber, block->block_len, block->BlockVer,
308 block->VolSessionId, block->VolSessionTime);
311 Dmsg5(100, "Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
312 block->BlockNumber, block->block_len, block->BlockVer,
313 block->VolSessionId, block->VolSessionTime);
315 read_record_from_block(dcr, rec);
316 Pmsg9(-1, _("File:blk=%u:%u blk_num=%u blen=%u First rec FI=%s SessId=%u SessTim=%u Strm=%s rlen=%d\n"),
317 dev->file, dev->block_num,
318 block->BlockNumber, block->block_len,
319 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId, rec->VolSessionTime,
320 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
322 } else if (verbose > 1) {
323 dump_block(block, "");
325 printf(_("Block: %d size=%d\n"), block->BlockNumber, block->block_len);
333 * We are only looking for labels or in particular Job Session records
335 static bool jobs_cb(DCR *dcr, DEV_RECORD *rec)
337 if (rec->FileIndex < 0) {
338 dump_label_record(dcr->dev, rec, verbose);
344 /* Do list job records */
345 static void do_jobs(char *infname)
347 read_records(dcr, jobs_cb, mount_next_read_volume);
350 /* Do an ls type listing of an archive */
351 static void do_ls(char *infname)
354 dump_volume_label(dev);
357 read_records(dcr, record_cb, mount_next_read_volume);
358 printf("%u files found.\n", num_files);
362 * Called here for each record from read_records()
364 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
366 if (verbose && rec->FileIndex < 0) {
367 dump_label_record(dcr->dev, rec, verbose);
371 char buf1[100], buf2[100];
372 Pmsg6(000, "Record: FI=%s SessId=%d Strm=%s len=%u remlen=%d data_len=%d\n",
373 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
374 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_bytes, rec->remlen,
378 /* File Attributes stream */
379 if (rec->maskedStream == STREAM_UNIX_ATTRIBUTES ||
380 rec->maskedStream == STREAM_UNIX_ATTRIBUTES_EX) {
381 if (!unpack_attributes_record(jcr, rec->Stream, rec->data, rec->data_len, attr)) {
383 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
385 Emsg0(M_ERROR, 0, _("Attrib unpack error!\n"));
391 attr->data_stream = decode_stat(attr->attr, &attr->statp, sizeof(attr->statp), &attr->LinkFI);
392 build_attr_output_fnames(jcr, attr);
394 if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
396 Pmsg5(000, _("FileIndex=%d VolSessionId=%d VolSessionTime=%d Stream=%d DataLen=%d\n"),
397 rec->FileIndex, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
399 print_ls_output(jcr, attr);
402 } else if (rec->Stream == STREAM_PLUGIN_NAME) {
404 int len = MIN(rec->data_len+1, sizeof(data));
405 bstrncpy(data, rec->data, len);
406 Dmsg1(100, "Plugin data: %s\n", data);
407 } else if (rec->Stream == STREAM_RESTORE_OBJECT) {
408 Dmsg0(100, "Restore Object record\n");
415 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
418 memset(sessrec, 0, sizeof(SESSION_LABEL));
420 switch (rec->FileIndex) {
422 rtype = _("Fresh Volume Label");
425 rtype = _("Volume Label");
426 unser_volume_label(dev, rec);
429 rtype = _("Begin Job Session");
430 unser_session_label(sessrec, rec);
431 jcr->JobId = sessrec->JobId;
434 rtype = _("End Job Session");
438 rtype = _("End of Medium");
441 rtype = _("End of Physical Medium");
444 rtype = _("Start of object");
447 rtype = _("End of object");
450 rtype = _("Unknown");
451 Dmsg1(10, "FI rtype=%d unknown\n", rec->FileIndex);
454 Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
455 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
457 Pmsg5(-1, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
458 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
463 /* Dummies to replace askdir.c */
464 bool dir_find_next_appendable_volume(DCR *dcr) { return 1;}
465 bool dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
466 bool dir_create_jobmedia_record(DCR *dcr, bool zero) { return 1; }
467 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
468 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
469 bool dir_send_job_status(JCR *jcr) {return 1;}
470 int generate_job_event(JCR *jcr, const char *event) { return 1; }
473 bool dir_ask_sysop_to_mount_volume(DCR *dcr, bool /*writing*/)
475 DEVICE *dev = dcr->dev;
476 fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
477 dcr->VolumeName, dev->print_name());
483 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
485 Dmsg0(100, "Fake dir_get_volume_info\n");
486 dcr->setVolCatName(dcr->VolumeName);
488 dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
490 Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->getVolCatName(), dcr->VolCatInfo.VolCatParts);