2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2009 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 two of the GNU 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 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.
39 #include "findlib/find.h"
42 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
43 extern bool parse_sd_config(CONFIG *config, const char *configfile, int exit_code);
45 static void do_blocks(char *infname);
46 static void do_jobs(char *infname);
47 static void do_ls(char *fname);
48 static void do_close(JCR *jcr);
49 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec);
50 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
54 static bool dump_label = false;
55 static bool list_blocks = false;
56 static bool list_jobs = false;
57 static DEV_RECORD *rec;
59 static SESSION_LABEL sessrec;
60 static uint32_t num_files = 0;
62 static CONFIG *config;
64 #define CONFIG_FILE "bacula-sd.conf"
65 char *configfile = NULL;
66 STORES *me = NULL; /* our Global resource */
67 bool forge_on = false;
68 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
69 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
74 static BSR *bsr = NULL;
80 "\nVersion: %s (%s)\n\n"
81 "Usage: bls [options] <device-name>\n"
82 " -b <file> specify a bootstrap file\n"
83 " -c <file> specify a Storage configuration file\n"
84 " -d <nn> set debug level to <nn>\n"
85 " -dt print timestamp in debug output\n"
86 " -e <file> exclude list\n"
87 " -i <file> include list\n"
90 " (no j or k option) list saved files\n"
92 " -p proceed inspite of errors\n"
94 " -V specify Volume names (separated by |)\n"
95 " -? print this message\n\n"), 2000, VERSION, BDATE);
100 int main (int argc, char *argv[])
105 char *VolumeName= NULL;
106 char *bsrName = NULL;
107 bool ignore_label_errors = false;
109 setlocale(LC_ALL, "");
110 bindtextdomain("bacula", LOCALEDIR);
111 textdomain("bacula");
115 working_directory = "/tmp";
116 my_name_is(argc, argv, "bls");
117 init_msg(NULL, NULL); /* initialize message handler */
121 ff = init_find_files();
123 while ((ch = getopt(argc, argv, "b:c:d:e:i:jkLpvV:?")) != -1) {
129 case 'c': /* specify config file */
130 if (configfile != NULL) {
133 configfile = bstrdup(optarg);
136 case 'd': /* debug level */
137 if (*optarg == 't') {
138 dbg_timestamp = true;
140 debug_level = atoi(optarg);
141 if (debug_level <= 0) {
147 case 'e': /* exclude list */
148 if ((fd = fopen(optarg, "rb")) == NULL) {
150 Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
151 optarg, be.bstrerror());
154 while (fgets(line, sizeof(line), fd) != NULL) {
155 strip_trailing_junk(line);
156 Dmsg1(100, "add_exclude %s\n", line);
157 add_fname_to_exclude_list(ff, line);
162 case 'i': /* include list */
163 if ((fd = fopen(optarg, "rb")) == NULL) {
165 Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
166 optarg, be.bstrerror());
169 while (fgets(line, sizeof(line), fd) != NULL) {
170 strip_trailing_junk(line);
171 Dmsg1(100, "add_include %s\n", line);
172 add_fname_to_include_list(ff, 0, line);
190 ignore_label_errors = true;
198 case 'V': /* Volume name */
212 Pmsg0(0, _("No archive name specified\n"));
216 if (configfile == NULL) {
217 configfile = bstrdup(CONFIG_FILE);
220 config = new_config_parser();
221 parse_sd_config(config, configfile, M_ERROR_TERM);
223 if (ff->included_files_list == NULL) {
224 add_fname_to_include_list(ff, 0, "/");
227 for (i=0; i < argc; i++) {
229 bsr = parse_bsr(NULL, bsrName);
231 jcr = setup_jcr("bls", argv[i], bsr, VolumeName, 1); /* acquire for read */
235 jcr->ignore_label_errors = ignore_label_errors;
242 attr = new_attr(jcr);
244 * Assume that we have already read the volume label.
245 * If on second or subsequent volume, adjust buffer pointer
247 if (dev->VolHdr.PrevVolumeName[0] != 0) { /* second volume */
249 "Warning, this Volume is a continuation of Volume %s\n"),
250 dev->VolHdr.PrevVolumeName);
255 } else if (list_jobs) {
265 term_include_exclude_files(ff);
271 static void do_close(JCR *jcr)
273 release_device(jcr->dcr);
281 /* List just block information */
282 static void do_blocks(char *infname)
284 DEV_BLOCK *block = dcr->block;
285 char buf1[100], buf2[100];
287 if (!read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK)) {
288 Dmsg1(100, "!read_block(): ERR=%s\n", dev->bstrerror());
290 if (!mount_next_read_volume(dcr)) {
291 Jmsg(jcr, M_INFO, 0, _("Got EOM at file %u on device %s, Volume \"%s\"\n"),
292 dev->file, dev->print_name(), dcr->VolumeName);
295 /* Read and discard Volume label */
297 record = new_record();
298 read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK);
299 read_record_from_block(dcr, block, record);
300 get_session_record(dev, record, &sessrec);
302 Jmsg(jcr, M_INFO, 0, _("Mounted Volume \"%s\".\n"), dcr->VolumeName);
303 } else if (dev->at_eof()) {
304 Jmsg(jcr, M_INFO, 0, _("End of file %u on device %s, Volume \"%s\"\n"),
305 dev->file, dev->print_name(), dcr->VolumeName);
306 Dmsg0(20, "read_record got eof. try again\n");
308 } else if (dev->is_short_block()) {
309 Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
313 display_tape_error_status(jcr, dev);
317 if (!match_bsr_block(bsr, block)) {
318 Dmsg5(100, "reject Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
319 block->BlockNumber, block->block_len, block->BlockVer,
320 block->VolSessionId, block->VolSessionTime);
323 Dmsg5(100, "Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
324 block->BlockNumber, block->block_len, block->BlockVer,
325 block->VolSessionId, block->VolSessionTime);
327 read_record_from_block(dcr, block, rec);
328 Pmsg9(-1, _("File:blk=%u:%u blk_num=%u blen=%u First rec FI=%s SessId=%u SessTim=%u Strm=%s rlen=%d\n"),
329 dev->file, dev->block_num,
330 block->BlockNumber, block->block_len,
331 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId, rec->VolSessionTime,
332 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
334 } else if (verbose > 1) {
335 dump_block(block, "");
337 printf(_("Block: %d size=%d\n"), block->BlockNumber, block->block_len);
345 * We are only looking for labels or in particular Job Session records
347 static bool jobs_cb(DCR *dcr, DEV_RECORD *rec)
349 if (rec->FileIndex < 0) {
350 dump_label_record(dcr->dev, rec, verbose);
356 /* Do list job records */
357 static void do_jobs(char *infname)
359 read_records(dcr, jobs_cb, mount_next_read_volume);
362 /* Do an ls type listing of an archive */
363 static void do_ls(char *infname)
366 dump_volume_label(dev);
369 read_records(dcr, record_cb, mount_next_read_volume);
370 printf("%u files found.\n", num_files);
374 * Called here for each record from read_records()
376 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
378 if (rec->FileIndex < 0) {
379 get_session_record(dev, rec, &sessrec);
382 /* File Attributes stream */
383 if (rec->Stream == STREAM_UNIX_ATTRIBUTES ||
384 rec->Stream == STREAM_UNIX_ATTRIBUTES_EX) {
386 if (!unpack_attributes_record(jcr, rec->Stream, rec->data, attr)) {
388 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
394 if (attr->file_index != rec->FileIndex) {
395 Emsg2(forge_on?M_WARNING:M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"),
396 rec->FileIndex, attr->file_index);
399 attr->data_stream = decode_stat(attr->attr, &attr->statp, &attr->LinkFI);
400 build_attr_output_fnames(jcr, attr);
402 if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
404 Pmsg5(-1, _("FileIndex=%d VolSessionId=%d VolSessionTime=%d Stream=%d DataLen=%d\n"),
405 rec->FileIndex, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
407 print_ls_output(jcr, attr);
410 } else if (rec->Stream == STREAM_PLUGIN_NAME) {
411 if (strncmp("0 0", rec->data, 3) != 0) {
412 Pmsg1(000, "Plugin data: %s\n", rec->data);
420 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
423 memset(sessrec, 0, sizeof(sessrec));
425 switch (rec->FileIndex) {
427 rtype = _("Fresh Volume Label");
430 rtype = _("Volume Label");
431 unser_volume_label(dev, rec);
434 rtype = _("Begin Job Session");
435 unser_session_label(sessrec, rec);
436 jcr->JobId = sessrec->JobId;
439 rtype = _("End Job Session");
443 rtype = _("End of Medium");
446 rtype = _("Unknown");
449 Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
450 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
452 Pmsg5(-1, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
453 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
458 /* Dummies to replace askdir.c */
459 bool dir_find_next_appendable_volume(DCR *dcr) { return 1;}
460 bool dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
461 bool dir_create_jobmedia_record(DCR *dcr, bool zero) { return 1; }
462 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
463 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
464 bool dir_send_job_status(JCR *jcr) {return 1;}
465 int generate_job_event(JCR *jcr, const char *event) { return 1; }
468 bool dir_ask_sysop_to_mount_volume(DCR *dcr, int /*mode*/)
470 DEVICE *dev = dcr->dev;
471 fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
472 dcr->VolumeName, dev->print_name());
478 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
480 Dmsg0(100, "Fake dir_get_volume_info\n");
481 bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
482 dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
483 Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->VolCatInfo.VolCatName, dcr->VolCatInfo.VolCatParts);