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"
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);
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);
41 static bool dump_label = false;
42 static bool list_blocks = false;
43 static bool list_jobs = false;
44 static DEV_RECORD *rec;
46 static SESSION_LABEL sessrec;
47 static uint32_t num_files = 0;
49 static CONFIG *config;
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;
62 static BSR *bsr = NULL;
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"
78 " (no j or k option) list saved files\n"
80 " -p proceed inspite of errors\n"
82 " -V specify Volume names (separated by |)\n"
83 " -? print this message\n\n"), 2000, VERSION, BDATE);
88 int main (int argc, char *argv[])
93 char *VolumeName= NULL;
95 bool ignore_label_errors = false;
97 setlocale(LC_ALL, "");
98 bindtextdomain("bacula", LOCALEDIR);
103 working_directory = "/tmp";
104 my_name_is(argc, argv, "bls");
105 init_msg(NULL, NULL); /* initialize message handler */
109 ff = init_find_files();
111 while ((ch = getopt(argc, argv, "b:c:d:e:i:jkLpvV:?")) != -1) {
117 case 'c': /* specify config file */
118 if (configfile != NULL) {
121 configfile = bstrdup(optarg);
124 case 'd': /* debug level */
125 if (*optarg == 't') {
126 dbg_timestamp = true;
128 debug_level = atoi(optarg);
129 if (debug_level <= 0) {
135 case 'e': /* exclude list */
136 if ((fd = fopen(optarg, "rb")) == NULL) {
138 Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
139 optarg, be.bstrerror());
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);
150 case 'i': /* include list */
151 if ((fd = fopen(optarg, "rb")) == NULL) {
153 Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
154 optarg, be.bstrerror());
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);
178 ignore_label_errors = true;
186 case 'V': /* Volume name */
200 Pmsg0(0, _("No archive name specified\n"));
204 if (configfile == NULL) {
205 configfile = bstrdup(CONFIG_FILE);
208 config = new_config_parser();
209 parse_sd_config(config, configfile, M_ERROR_TERM);
211 load_sd_plugins(me->plugin_directory);
213 if (ff->included_files_list == NULL) {
214 add_fname_to_include_list(ff, 0, "/");
217 for (i=0; i < argc; i++) {
219 bsr = parse_bsr(NULL, bsrName);
221 jcr = setup_jcr("bls", argv[i], bsr, VolumeName, SD_READ);
225 jcr->ignore_label_errors = ignore_label_errors;
232 attr = new_attr(jcr);
234 * Assume that we have already read the volume label.
235 * If on second or subsequent volume, adjust buffer pointer
237 if (dev->VolHdr.PrevVolumeName[0] != 0) { /* second volume */
239 "Warning, this Volume is a continuation of Volume %s\n"),
240 dev->VolHdr.PrevVolumeName);
245 } else if (list_jobs) {
255 term_include_exclude_files(ff);
261 static void do_close(JCR *jcr)
263 release_device(jcr->dcr);
271 /* List just block information */
272 static void do_blocks(char *infname)
274 DEV_BLOCK *block = dcr->block;
275 char buf1[100], buf2[100];
277 if (!dcr->read_block_from_device(NO_BLOCK_NUMBER_CHECK)) {
278 Dmsg1(100, "!read_block(): ERR=%s\n", dev->bstrerror());
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);
285 /* Read and discard Volume label */
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);
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");
298 } else if (dev->is_short_block()) {
299 Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
303 display_tape_error_status(jcr, dev);
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);
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);
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);
324 } else if (verbose > 1) {
325 dump_block(block, "");
327 printf(_("Block: %d size=%d\n"), block->BlockNumber, block->block_len);
335 * We are only looking for labels or in particular Job Session records
337 static bool jobs_cb(DCR *dcr, DEV_RECORD *rec)
339 if (rec->FileIndex < 0) {
340 dump_label_record(dcr->dev, rec, verbose);
346 /* Do list job records */
347 static void do_jobs(char *infname)
349 read_records(dcr, jobs_cb, mount_next_read_volume);
352 /* Do an ls type listing of an archive */
353 static void do_ls(char *infname)
356 dump_volume_label(dev);
359 read_records(dcr, record_cb, mount_next_read_volume);
360 printf("%u files found.\n", num_files);
364 * Called here for each record from read_records()
366 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
368 if (verbose && rec->FileIndex < 0) {
369 dump_label_record(dcr->dev, rec, 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,
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)) {
385 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
387 Emsg0(M_ERROR, 0, _("Attrib unpack error!\n"));
393 attr->data_stream = decode_stat(attr->attr, &attr->statp, sizeof(attr->statp), &attr->LinkFI);
394 build_attr_output_fnames(jcr, attr);
396 if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
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);
401 print_ls_output(jcr, attr);
404 } else if (rec->Stream == STREAM_PLUGIN_NAME) {
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");
417 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
420 memset(sessrec, 0, sizeof(SESSION_LABEL));
422 switch (rec->FileIndex) {
424 rtype = _("Fresh Volume Label");
427 rtype = _("Volume Label");
428 unser_volume_label(dev, rec);
431 rtype = _("Begin Job Session");
432 unser_session_label(sessrec, rec);
433 jcr->JobId = sessrec->JobId;
436 rtype = _("End Job Session");
440 rtype = _("End of Medium");
443 rtype = _("End of Physical Medium");
446 rtype = _("Start of object");
449 rtype = _("End of object");
452 rtype = _("Unknown");
453 Dmsg1(10, "FI rtype=%d unknown\n", rec->FileIndex);
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);
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);
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; }
475 bool dir_ask_sysop_to_mount_volume(DCR *dcr, bool /*writing*/)
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());
485 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
487 Dmsg0(100, "Fake dir_get_volume_info\n");
488 dcr->setVolCatName(dcr->VolumeName);
490 dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
492 Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->getVolCatName(), dcr->VolCatInfo.VolCatParts);