2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2016 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.
29 #include "findlib/find.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 STORES *me = NULL; /* our Global resource */
56 bool forge_on = false;
57 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
58 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
59 bool detect_errors = false;
64 static BSR *bsr = NULL;
70 "\n%sVersion: %s (%s)\n\n"
71 "Usage: bls [options] <device-name>\n"
72 " -b <file> specify a bootstrap file\n"
73 " -c <file> specify a Storage configuration file\n"
74 " -d <nn> set debug level to <nn>\n"
75 " -dt print timestamp in debug output\n"
76 " -e <file> exclude list\n"
77 " -i <file> include list\n"
80 " (no j or k option) list saved files\n"
82 " -p proceed inspite of errors\n"
84 " -V specify Volume names (separated by |)\n"
85 " -E Check records to detect errors\n"
86 " -? print this message\n\n"), 2000, "", VERSION, BDATE);
91 int main (int argc, char *argv[])
96 char *VolumeName= NULL;
98 bool ignore_label_errors = false;
100 setlocale(LC_ALL, "");
101 bindtextdomain("bacula", LOCALEDIR);
102 textdomain("bacula");
106 working_directory = "/tmp";
107 my_name_is(argc, argv, "bls");
108 init_msg(NULL, NULL); /* initialize message handler */
112 ff = init_find_files();
114 while ((ch = getopt(argc, argv, "b:c:d:e:i:jkLpvV:?E")) != -1) {
121 detect_errors = true;
124 case 'c': /* specify config file */
125 if (configfile != NULL) {
128 configfile = bstrdup(optarg);
131 case 'd': /* debug level */
132 if (*optarg == 't') {
133 dbg_timestamp = true;
135 debug_level = atoi(optarg);
136 if (debug_level <= 0) {
142 case 'e': /* exclude list */
143 if ((fd = fopen(optarg, "rb")) == NULL) {
145 Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
146 optarg, be.bstrerror());
149 while (fgets(line, sizeof(line), fd) != NULL) {
150 strip_trailing_junk(line);
151 Dmsg1(100, "add_exclude %s\n", line);
152 add_fname_to_exclude_list(ff, line);
157 case 'i': /* include list */
158 if ((fd = fopen(optarg, "rb")) == NULL) {
160 Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
161 optarg, be.bstrerror());
164 while (fgets(line, sizeof(line), fd) != NULL) {
165 strip_trailing_junk(line);
166 Dmsg1(100, "add_include %s\n", line);
167 add_fname_to_include_list(ff, 0, line);
185 ignore_label_errors = true;
193 case 'V': /* Volume name */
207 Pmsg0(0, _("No archive name specified\n"));
211 if (configfile == NULL) {
212 configfile = bstrdup(CONFIG_FILE);
215 config = new_config_parser();
216 parse_sd_config(config, configfile, M_ERROR_TERM);
218 load_sd_plugins(me->plugin_directory);
220 if (ff->included_files_list == NULL) {
221 add_fname_to_include_list(ff, 0, "/");
224 for (i=0; i < argc; i++) {
226 bsr = parse_bsr(NULL, bsrName);
228 jcr = setup_jcr("bls", argv[i], bsr, VolumeName, SD_READ);
232 jcr->ignore_label_errors = ignore_label_errors;
239 attr = new_attr(jcr);
241 * Assume that we have already read the volume label.
242 * If on second or subsequent volume, adjust buffer pointer
244 if (dev->VolHdr.PrevVolumeName[0] != 0) { /* second volume */
246 "Warning, this Volume is a continuation of Volume %s\n"),
247 dev->VolHdr.PrevVolumeName);
252 } else if (list_jobs) {
262 term_include_exclude_files(ff);
266 return (errors > 0)? 1 : 0;
272 static void do_close(JCR *jcr)
274 release_device(jcr->dcr);
282 /* List just block information */
283 static void do_blocks(char *infname)
285 DEV_BLOCK *block = dcr->block;
286 char buf1[100], buf2[100];
288 if (!dcr->read_block_from_device(NO_BLOCK_NUMBER_CHECK)) {
289 Dmsg1(100, "!read_block(): ERR=%s\n", dev->print_errmsg());
291 if (!mount_next_read_volume(dcr)) {
292 Jmsg(jcr, M_INFO, 0, _("Got EOM at file %u on device %s, Volume \"%s\"\n"),
293 dev->file, dev->print_name(), dcr->VolumeName);
296 /* Read and discard Volume label */
298 record = new_record();
299 dcr->read_block_from_device(NO_BLOCK_NUMBER_CHECK);
300 read_record_from_block(dcr, record);
301 get_session_record(dev, record, &sessrec);
303 Jmsg(jcr, M_INFO, 0, _("Mounted Volume \"%s\".\n"), dcr->VolumeName);
304 } else if (dev->at_eof()) {
305 Jmsg(jcr, M_INFO, 0, _("End of file %u on device %s, Volume \"%s\"\n"),
306 dev->file, dev->print_name(), dcr->VolumeName);
307 Dmsg0(20, "read_record got eof. try again\n");
309 } else if (dev->is_short_block()) {
310 Jmsg(jcr, M_INFO, 0, "%s", dev->print_errmsg());
315 display_tape_error_status(jcr, dev);
319 if (!match_bsr_block(bsr, block)) {
320 Dmsg5(100, "reject Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
321 block->BlockNumber, block->block_len, block->BlockVer,
322 block->VolSessionId, block->VolSessionTime);
325 Dmsg5(100, "Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
326 block->BlockNumber, block->block_len, block->BlockVer,
327 block->VolSessionId, block->VolSessionTime);
329 read_record_from_block(dcr, rec);
330 Pmsg9(-1, _("File:blk=%u:%u blk_num=%u blen=%u First rec FI=%s SessId=%u SessTim=%u Strm=%s rlen=%d\n"),
331 dev->file, dev->block_num,
332 block->BlockNumber, block->block_len,
333 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId, rec->VolSessionTime,
334 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
336 } else if (verbose > 1) {
337 dump_block(block, "");
339 printf(_("Block: %d size=%d\n"), block->BlockNumber, block->block_len);
347 * We are only looking for labels or in particular Job Session records
349 static bool jobs_cb(DCR *dcr, DEV_RECORD *rec)
351 if (rec->FileIndex < 0) {
352 dump_label_record(dcr->dev, rec, verbose, detect_errors);
358 /* Do list job records */
359 static void do_jobs(char *infname)
361 if (!read_records(dcr, jobs_cb, mount_next_read_volume)) {
366 /* Do an ls type listing of an archive */
367 static void do_ls(char *infname)
370 dump_volume_label(dev);
373 if (!read_records(dcr, record_cb, mount_next_read_volume)) {
376 printf("%u files found.\n", num_files);
380 * Called here for each record from read_records()
382 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
384 if (verbose && rec->FileIndex < 0) {
385 dump_label_record(dcr->dev, rec, verbose, false);
389 char buf1[100], buf2[100];
390 Pmsg6(000, "Record: FI=%s SessId=%d Strm=%s len=%u remlen=%d data_len=%d\n",
391 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
392 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_bytes, rec->remlen,
396 /* File Attributes stream */
397 if (rec->maskedStream == STREAM_UNIX_ATTRIBUTES ||
398 rec->maskedStream == STREAM_UNIX_ATTRIBUTES_EX) {
399 if (!unpack_attributes_record(jcr, rec->Stream, rec->data, rec->data_len, attr)) {
401 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
403 Emsg0(M_ERROR, 0, _("Attrib unpack error!\n"));
409 attr->data_stream = decode_stat(attr->attr, &attr->statp, sizeof(attr->statp), &attr->LinkFI);
410 build_attr_output_fnames(jcr, attr);
412 if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
414 Pmsg5(000, _("FileIndex=%d VolSessionId=%d VolSessionTime=%d Stream=%d DataLen=%d\n"),
415 rec->FileIndex, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
417 print_ls_output(jcr, attr);
420 } else if (rec->Stream == STREAM_PLUGIN_NAME) {
422 int len = MIN(rec->data_len+1, sizeof(data));
423 bstrncpy(data, rec->data, len);
424 Dmsg1(100, "Plugin data: %s\n", data);
425 } else if (rec->Stream == STREAM_RESTORE_OBJECT) {
426 Dmsg0(100, "Restore Object record\n");
433 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
436 memset(sessrec, 0, sizeof(SESSION_LABEL));
438 switch (rec->FileIndex) {
440 rtype = _("Fresh Volume Label");
443 rtype = _("Volume Label");
444 unser_volume_label(dev, rec);
447 rtype = _("Begin Job Session");
448 unser_session_label(sessrec, rec);
449 jcr->JobId = sessrec->JobId;
452 rtype = _("End Job Session");
456 rtype = _("End of Medium");
459 rtype = _("End of Physical Medium");
462 rtype = _("Start of object");
465 rtype = _("End of object");
468 rtype = _("Unknown");
469 Dmsg1(10, "FI rtype=%d unknown\n", rec->FileIndex);
472 Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
473 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
475 Pmsg5(-1, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
476 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
481 /* Dummies to replace askdir.c */
482 bool dir_find_next_appendable_volume(DCR *dcr) { return 1;}
483 bool dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
484 bool dir_create_jobmedia_record(DCR *dcr, bool zero) { return 1; }
485 bool flush_jobmedia_queue(JCR *jcr) { return true; }
486 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
487 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
488 bool dir_send_job_status(JCR *jcr) {return 1;}
489 int generate_job_event(JCR *jcr, const char *event) { return 1; }
492 bool dir_ask_sysop_to_mount_volume(DCR *dcr, bool /*writing*/)
494 DEVICE *dev = dcr->dev;
495 fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
496 dcr->VolumeName, dev->print_name());
502 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
504 Dmsg0(100, "Fake dir_get_volume_info\n");
505 dcr->setVolCatName(dcr->VolumeName);
506 Dmsg2(500, "Vol=%s VolType=%d\n", dcr->getVolCatName(), dcr->VolCatInfo.VolCatType);