2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2011 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU Affero General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 * Dumb program to do an "ls" of a Bacula 1.0 mortal file.
38 #include "findlib/find.h"
41 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
42 extern bool parse_sd_config(CONFIG *config, const char *configfile, int exit_code);
44 static void do_blocks(char *infname);
45 static void do_jobs(char *infname);
46 static void do_ls(char *fname);
47 static void do_close(JCR *jcr);
48 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec);
49 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
53 static bool dump_label = false;
54 static bool list_blocks = false;
55 static bool list_jobs = false;
56 static DEV_RECORD *rec;
58 static SESSION_LABEL sessrec;
59 static uint32_t num_files = 0;
61 static CONFIG *config;
63 #define CONFIG_FILE "bacula-sd.conf"
64 char *configfile = NULL;
65 STORES *me = NULL; /* our Global resource */
66 bool forge_on = false;
67 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
68 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
73 static BSR *bsr = NULL;
79 "\nVersion: %s (%s)\n\n"
80 "Usage: bls [options] <device-name>\n"
81 " -b <file> specify a bootstrap file\n"
82 " -c <file> specify a Storage configuration file\n"
83 " -d <nn> set debug level to <nn>\n"
84 " -dt print timestamp in debug output\n"
85 " -e <file> exclude list\n"
86 " -i <file> include list\n"
89 " (no j or k option) list saved files\n"
91 " -p proceed inspite of errors\n"
93 " -V specify Volume names (separated by |)\n"
94 " -? print this message\n\n"), 2000, VERSION, BDATE);
99 int main (int argc, char *argv[])
104 char *VolumeName= NULL;
105 char *bsrName = NULL;
106 bool ignore_label_errors = false;
108 setlocale(LC_ALL, "");
109 bindtextdomain("bacula", LOCALEDIR);
110 textdomain("bacula");
114 working_directory = "/tmp";
115 my_name_is(argc, argv, "bls");
116 init_msg(NULL, NULL); /* initialize message handler */
120 ff = init_find_files();
122 while ((ch = getopt(argc, argv, "b:c:d:e:i:jkLpvV:?")) != -1) {
128 case 'c': /* specify config file */
129 if (configfile != NULL) {
132 configfile = bstrdup(optarg);
135 case 'd': /* debug level */
136 if (*optarg == 't') {
137 dbg_timestamp = true;
139 debug_level = atoi(optarg);
140 if (debug_level <= 0) {
146 case 'e': /* exclude list */
147 if ((fd = fopen(optarg, "rb")) == NULL) {
149 Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
150 optarg, be.bstrerror());
153 while (fgets(line, sizeof(line), fd) != NULL) {
154 strip_trailing_junk(line);
155 Dmsg1(100, "add_exclude %s\n", line);
156 add_fname_to_exclude_list(ff, line);
161 case 'i': /* include list */
162 if ((fd = fopen(optarg, "rb")) == NULL) {
164 Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
165 optarg, be.bstrerror());
168 while (fgets(line, sizeof(line), fd) != NULL) {
169 strip_trailing_junk(line);
170 Dmsg1(100, "add_include %s\n", line);
171 add_fname_to_include_list(ff, 0, line);
189 ignore_label_errors = true;
197 case 'V': /* Volume name */
211 Pmsg0(0, _("No archive name specified\n"));
215 if (configfile == NULL) {
216 configfile = bstrdup(CONFIG_FILE);
219 config = new_config_parser();
220 parse_sd_config(config, configfile, M_ERROR_TERM);
222 if (ff->included_files_list == NULL) {
223 add_fname_to_include_list(ff, 0, "/");
226 for (i=0; i < argc; i++) {
228 bsr = parse_bsr(NULL, bsrName);
230 jcr = setup_jcr("bls", argv[i], bsr, VolumeName, 1); /* acquire for read */
234 jcr->ignore_label_errors = ignore_label_errors;
241 attr = new_attr(jcr);
243 * Assume that we have already read the volume label.
244 * If on second or subsequent volume, adjust buffer pointer
246 if (dev->VolHdr.PrevVolumeName[0] != 0) { /* second volume */
248 "Warning, this Volume is a continuation of Volume %s\n"),
249 dev->VolHdr.PrevVolumeName);
254 } else if (list_jobs) {
264 term_include_exclude_files(ff);
270 static void do_close(JCR *jcr)
272 release_device(jcr->dcr);
280 /* List just block information */
281 static void do_blocks(char *infname)
283 DEV_BLOCK *block = dcr->block;
284 char buf1[100], buf2[100];
286 if (!read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK)) {
287 Dmsg1(100, "!read_block(): ERR=%s\n", dev->bstrerror());
289 if (!mount_next_read_volume(dcr)) {
290 Jmsg(jcr, M_INFO, 0, _("Got EOM at file %u on device %s, Volume \"%s\"\n"),
291 dev->file, dev->print_name(), dcr->VolumeName);
294 /* Read and discard Volume label */
296 record = new_record();
297 read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK);
298 read_record_from_block(dcr, block, record);
299 get_session_record(dev, record, &sessrec);
301 Jmsg(jcr, M_INFO, 0, _("Mounted Volume \"%s\".\n"), dcr->VolumeName);
302 } else if (dev->at_eof()) {
303 Jmsg(jcr, M_INFO, 0, _("End of file %u on device %s, Volume \"%s\"\n"),
304 dev->file, dev->print_name(), dcr->VolumeName);
305 Dmsg0(20, "read_record got eof. try again\n");
307 } else if (dev->is_short_block()) {
308 Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
312 display_tape_error_status(jcr, dev);
316 if (!match_bsr_block(bsr, block)) {
317 Dmsg5(100, "reject Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
318 block->BlockNumber, block->block_len, block->BlockVer,
319 block->VolSessionId, block->VolSessionTime);
322 Dmsg5(100, "Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
323 block->BlockNumber, block->block_len, block->BlockVer,
324 block->VolSessionId, block->VolSessionTime);
326 read_record_from_block(dcr, block, rec);
327 Pmsg9(-1, _("File:blk=%u:%u blk_num=%u blen=%u First rec FI=%s SessId=%u SessTim=%u Strm=%s rlen=%d\n"),
328 dev->file, dev->block_num,
329 block->BlockNumber, block->block_len,
330 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId, rec->VolSessionTime,
331 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
333 } else if (verbose > 1) {
334 dump_block(block, "");
336 printf(_("Block: %d size=%d\n"), block->BlockNumber, block->block_len);
344 * We are only looking for labels or in particular Job Session records
346 static bool jobs_cb(DCR *dcr, DEV_RECORD *rec)
348 if (rec->FileIndex < 0) {
349 dump_label_record(dcr->dev, rec, verbose);
355 /* Do list job records */
356 static void do_jobs(char *infname)
358 read_records(dcr, jobs_cb, mount_next_read_volume);
361 /* Do an ls type listing of an archive */
362 static void do_ls(char *infname)
365 dump_volume_label(dev);
368 read_records(dcr, record_cb, mount_next_read_volume);
369 printf("%u files found.\n", num_files);
373 * Called here for each record from read_records()
375 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
377 if (rec->FileIndex < 0) {
378 get_session_record(dev, rec, &sessrec);
381 /* File Attributes stream */
382 if (rec->maskedStream == STREAM_UNIX_ATTRIBUTES ||
383 rec->maskedStream == STREAM_UNIX_ATTRIBUTES_EX) {
384 if (!unpack_attributes_record(jcr, rec->Stream, rec->data, rec->data_len, attr)) {
386 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
388 Emsg0(M_ERROR, 0, _("Attrib unpack error!\n"));
394 attr->data_stream = decode_stat(attr->attr, &attr->statp, sizeof(attr->statp), &attr->LinkFI);
395 build_attr_output_fnames(jcr, attr);
397 if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
399 Pmsg5(-1, _("FileIndex=%d VolSessionId=%d VolSessionTime=%d Stream=%d DataLen=%d\n"),
400 rec->FileIndex, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
402 print_ls_output(jcr, attr);
405 } else if (rec->Stream == STREAM_PLUGIN_NAME) {
407 int len = MIN(rec->data_len+1, sizeof(data));
408 bstrncpy(data, rec->data, len);
409 Pmsg1(000, "Plugin data: %s\n", data);
410 } else if (rec->Stream == STREAM_RESTORE_OBJECT) {
411 Pmsg0(000, "Restore Object record\n");
418 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
421 memset(sessrec, 0, sizeof(sessrec));
423 switch (rec->FileIndex) {
425 rtype = _("Fresh Volume Label");
428 rtype = _("Volume Label");
429 unser_volume_label(dev, rec);
432 rtype = _("Begin Job Session");
433 unser_session_label(sessrec, rec);
434 jcr->JobId = sessrec->JobId;
437 rtype = _("End Job Session");
441 rtype = _("End of Medium");
444 rtype = _("End of Physical Medium");
447 rtype = _("Start of object");
450 rtype = _("End of object");
453 rtype = _("Unknown");
454 Dmsg1(10, "FI rtype=%d unknown\n", rec->FileIndex);
457 Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
458 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
460 Pmsg5(-1, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
461 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
466 /* Dummies to replace askdir.c */
467 bool dir_find_next_appendable_volume(DCR *dcr) { return 1;}
468 bool dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
469 bool dir_create_jobmedia_record(DCR *dcr, bool zero) { return 1; }
470 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
471 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
472 bool dir_send_job_status(JCR *jcr) {return 1;}
473 int generate_job_event(JCR *jcr, const char *event) { return 1; }
476 bool dir_ask_sysop_to_mount_volume(DCR *dcr, int /*mode*/)
478 DEVICE *dev = dcr->dev;
479 fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
480 dcr->VolumeName, dev->print_name());
486 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
488 Dmsg0(100, "Fake dir_get_volume_info\n");
489 dcr->setVolCatName(dcr->VolumeName);
490 dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
491 Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->getVolCatName(), dcr->VolCatInfo.VolCatParts);