3 * Dumb program to do an "ls" of a Bacula 1.0 mortal file.
8 Copyright (C) 2000-2006 Kern Sibbald
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License
12 version 2 as amended with additional clauses defined in the
13 file LICENSE in the main source directory.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 the file LICENSE for additional details.
24 #include "findlib/find.h"
27 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
29 static void do_blocks(char *infname);
30 static void do_jobs(char *infname);
31 static void do_ls(char *fname);
32 static void do_close(JCR *jcr);
33 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec);
34 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
38 static bool dump_label = false;
39 static bool list_blocks = false;
40 static bool list_jobs = false;
41 static DEV_RECORD *rec;
43 static SESSION_LABEL sessrec;
44 static uint32_t num_files = 0;
47 #define CONFIG_FILE "bacula-sd.conf"
48 char *configfile = NULL;
49 STORES *me = NULL; /* our Global resource */
50 bool forge_on = false;
51 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
52 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
57 static BSR *bsr = NULL;
62 "Copyright (C) 2000-%s Kern Sibbald.\n"
63 "\nVersion: %s (%s)\n\n"
64 "Usage: bls [options] <device-name>\n"
65 " -b <file> specify a bootstrap file\n"
66 " -c <file> specify a config file\n"
67 " -d <level> specify debug level\n"
68 " -e <file> exclude list\n"
69 " -i <file> include list\n"
72 " (no j or k option) list saved files\n"
74 " -p proceed inspite of errors\n"
76 " -V specify Volume names (separated by |)\n"
77 " -? print this message\n\n"), BYEAR, VERSION, BDATE);
82 int main (int argc, char *argv[])
87 char *VolumeName= NULL;
89 bool ignore_label_errors = false;
91 setlocale(LC_ALL, "");
92 bindtextdomain("bacula", LOCALEDIR);
95 working_directory = "/tmp";
96 my_name_is(argc, argv, "bls");
97 init_msg(NULL, NULL); /* initialize message handler */
101 ff = init_find_files();
103 while ((ch = getopt(argc, argv, "b:c:d:e:i:jkLpvV:?")) != -1) {
109 case 'c': /* specify config file */
110 if (configfile != NULL) {
113 configfile = bstrdup(optarg);
116 case 'd': /* debug level */
117 debug_level = atoi(optarg);
118 if (debug_level <= 0)
122 case 'e': /* exclude list */
123 if ((fd = fopen(optarg, "rb")) == NULL) {
124 Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
125 optarg, strerror(errno));
128 while (fgets(line, sizeof(line), fd) != NULL) {
129 strip_trailing_junk(line);
130 Dmsg1(100, "add_exclude %s\n", line);
131 add_fname_to_exclude_list(ff, line);
136 case 'i': /* include list */
137 if ((fd = fopen(optarg, "rb")) == NULL) {
138 Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
139 optarg, strerror(errno));
142 while (fgets(line, sizeof(line), fd) != NULL) {
143 strip_trailing_junk(line);
144 Dmsg1(100, "add_include %s\n", line);
145 add_fname_to_include_list(ff, 0, line);
163 ignore_label_errors = true;
171 case 'V': /* Volume name */
185 Pmsg0(0, _("No archive name specified\n"));
189 if (configfile == NULL) {
190 configfile = bstrdup(CONFIG_FILE);
193 parse_config(configfile);
195 if (ff->included_files_list == NULL) {
196 add_fname_to_include_list(ff, 0, "/");
199 for (i=0; i < argc; i++) {
201 bsr = parse_bsr(NULL, bsrName);
203 jcr = setup_jcr("bls", argv[i], bsr, VolumeName, 1); /* acquire for read */
207 jcr->ignore_label_errors = ignore_label_errors;
216 * Assume that we have already read the volume label.
217 * If on second or subsequent volume, adjust buffer pointer
219 if (dev->VolHdr.PrevVolumeName[0] != 0) { /* second volume */
221 "Warning, this Volume is a continuation of Volume %s\n"),
222 dev->VolHdr.PrevVolumeName);
227 } else if (list_jobs) {
237 term_include_exclude_files(ff);
243 static void do_close(JCR *jcr)
245 release_device(jcr->dcr);
253 /* List just block information */
254 static void do_blocks(char *infname)
256 DEV_BLOCK *block = dcr->block;
257 char buf1[100], buf2[100];
259 if (!read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK)) {
260 Dmsg1(100, "!read_block(): ERR=%s\n", dev->strerror());
262 if (!mount_next_read_volume(dcr)) {
263 Jmsg(jcr, M_INFO, 0, _("Got EOM at file %u on device %s, Volume \"%s\"\n"),
264 dev->file, dev->print_name(), dcr->VolumeName);
267 /* Read and discard Volume label */
269 record = new_record();
270 read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK);
271 read_record_from_block(block, record);
272 get_session_record(dev, record, &sessrec);
274 Jmsg(jcr, M_INFO, 0, _("Mounted Volume \"%s\".\n"), dcr->VolumeName);
275 } else if (dev->at_eof()) {
276 Jmsg(jcr, M_INFO, 0, _("End of file %u on device %s, Volume \"%s\"\n"),
277 dev->file, dev->print_name(), dcr->VolumeName);
278 Dmsg0(20, "read_record got eof. try again\n");
280 } else if (dev->is_short_block()) {
281 Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
285 display_tape_error_status(jcr, dev);
289 if (!match_bsr_block(bsr, block)) {
290 Dmsg5(100, "reject Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
291 block->BlockNumber, block->block_len, block->BlockVer,
292 block->VolSessionId, block->VolSessionTime);
295 Dmsg5(100, "Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
296 block->BlockNumber, block->block_len, block->BlockVer,
297 block->VolSessionId, block->VolSessionTime);
299 read_record_from_block(block, rec);
300 Pmsg9(-1, _("File:blk=%u:%u blk_num=%u blen=%u First rec FI=%s SessId=%u SessTim=%u Strm=%s rlen=%d\n"),
301 dev->file, dev->block_num,
302 block->BlockNumber, block->block_len,
303 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId, rec->VolSessionTime,
304 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
306 } else if (verbose > 1) {
307 dump_block(block, "");
309 printf(_("Block: %d size=%d\n"), block->BlockNumber, block->block_len);
317 * We are only looking for labels or in particular Job Session records
319 static bool jobs_cb(DCR *dcr, DEV_RECORD *rec)
321 if (rec->FileIndex < 0) {
322 dump_label_record(dcr->dev, rec, verbose);
328 /* Do list job records */
329 static void do_jobs(char *infname)
331 read_records(dcr, jobs_cb, mount_next_read_volume);
334 /* Do an ls type listing of an archive */
335 static void do_ls(char *infname)
338 dump_volume_label(dev);
341 read_records(dcr, record_cb, mount_next_read_volume);
342 printf("%u files found.\n", num_files);
346 * Called here for each record from read_records()
348 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
350 if (rec->FileIndex < 0) {
351 get_session_record(dev, rec, &sessrec);
354 /* File Attributes stream */
355 if (rec->Stream == STREAM_UNIX_ATTRIBUTES ||
356 rec->Stream == STREAM_UNIX_ATTRIBUTES_EX) {
358 if (!unpack_attributes_record(jcr, rec->Stream, rec->data, attr)) {
360 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
366 if (attr->file_index != rec->FileIndex) {
367 Emsg2(forge_on?M_WARNING:M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"),
368 rec->FileIndex, attr->file_index);
371 attr->data_stream = decode_stat(attr->attr, &attr->statp, &attr->LinkFI);
372 build_attr_output_fnames(jcr, attr);
374 if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
376 Pmsg5(-1, _("FileIndex=%d VolSessionId=%d VolSessionTime=%d Stream=%d DataLen=%d\n"),
377 rec->FileIndex, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
379 print_ls_output(jcr, attr);
387 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
390 memset(sessrec, 0, sizeof(sessrec));
391 switch (rec->FileIndex) {
393 rtype = _("Fresh Volume Label");
396 rtype = _("Volume Label");
397 unser_volume_label(dev, rec);
400 rtype = _("Begin Job Session");
401 unser_session_label(sessrec, rec);
404 rtype = _("End Job Session");
408 rtype = _("End of Medium");
411 rtype = _("Unknown");
414 Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
415 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
417 Pmsg5(-1, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
418 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
423 /* Dummies to replace askdir.c */
424 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing) { return 1;}
425 bool dir_find_next_appendable_volume(DCR *dcr) { return 1;}
426 bool dir_update_volume_info(DCR *dcr, bool relabel) { return 1; }
427 bool dir_create_jobmedia_record(DCR *dcr) { return 1; }
428 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
429 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
430 bool dir_send_job_status(JCR *jcr) {return 1;}
431 int generate_job_event(JCR *jcr, const char *event) { return 1; }
434 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
436 DEVICE *dev = dcr->dev;
437 fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
438 dcr->VolumeName, dev->print_name());