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);
96 working_directory = "/tmp";
97 my_name_is(argc, argv, "bls");
98 init_msg(NULL, NULL); /* initialize message handler */
102 ff = init_find_files();
104 while ((ch = getopt(argc, argv, "b:c:d:e:i:jkLpvV:?")) != -1) {
110 case 'c': /* specify config file */
111 if (configfile != NULL) {
114 configfile = bstrdup(optarg);
117 case 'd': /* debug level */
118 debug_level = atoi(optarg);
119 if (debug_level <= 0)
123 case 'e': /* exclude list */
124 if ((fd = fopen(optarg, "rb")) == NULL) {
125 Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
126 optarg, strerror(errno));
129 while (fgets(line, sizeof(line), fd) != NULL) {
130 strip_trailing_junk(line);
131 Dmsg1(100, "add_exclude %s\n", line);
132 add_fname_to_exclude_list(ff, line);
137 case 'i': /* include list */
138 if ((fd = fopen(optarg, "rb")) == NULL) {
139 Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
140 optarg, strerror(errno));
143 while (fgets(line, sizeof(line), fd) != NULL) {
144 strip_trailing_junk(line);
145 Dmsg1(100, "add_include %s\n", line);
146 add_fname_to_include_list(ff, 0, line);
164 ignore_label_errors = true;
172 case 'V': /* Volume name */
186 Pmsg0(0, _("No archive name specified\n"));
190 if (configfile == NULL) {
191 configfile = bstrdup(CONFIG_FILE);
194 parse_config(configfile);
196 if (ff->included_files_list == NULL) {
197 add_fname_to_include_list(ff, 0, "/");
200 for (i=0; i < argc; i++) {
202 bsr = parse_bsr(NULL, bsrName);
204 jcr = setup_jcr("bls", argv[i], bsr, VolumeName, 1); /* acquire for read */
208 jcr->ignore_label_errors = ignore_label_errors;
217 * Assume that we have already read the volume label.
218 * If on second or subsequent volume, adjust buffer pointer
220 if (dev->VolHdr.PrevVolumeName[0] != 0) { /* second volume */
222 "Warning, this Volume is a continuation of Volume %s\n"),
223 dev->VolHdr.PrevVolumeName);
228 } else if (list_jobs) {
238 term_include_exclude_files(ff);
244 static void do_close(JCR *jcr)
246 release_device(jcr->dcr);
254 /* List just block information */
255 static void do_blocks(char *infname)
257 DEV_BLOCK *block = dcr->block;
258 char buf1[100], buf2[100];
260 if (!read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK)) {
261 Dmsg1(100, "!read_block(): ERR=%s\n", dev->strerror());
263 if (!mount_next_read_volume(dcr)) {
264 Jmsg(jcr, M_INFO, 0, _("Got EOM at file %u on device %s, Volume \"%s\"\n"),
265 dev->file, dev->print_name(), dcr->VolumeName);
268 /* Read and discard Volume label */
270 record = new_record();
271 read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK);
272 read_record_from_block(block, record);
273 get_session_record(dev, record, &sessrec);
275 Jmsg(jcr, M_INFO, 0, _("Mounted Volume \"%s\".\n"), dcr->VolumeName);
276 } else if (dev->at_eof()) {
277 Jmsg(jcr, M_INFO, 0, _("End of file %u on device %s, Volume \"%s\"\n"),
278 dev->file, dev->print_name(), dcr->VolumeName);
279 Dmsg0(20, "read_record got eof. try again\n");
281 } else if (dev->is_short_block()) {
282 Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
286 display_tape_error_status(jcr, dev);
290 if (!match_bsr_block(bsr, block)) {
291 Dmsg5(100, "reject Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
292 block->BlockNumber, block->block_len, block->BlockVer,
293 block->VolSessionId, block->VolSessionTime);
296 Dmsg5(100, "Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
297 block->BlockNumber, block->block_len, block->BlockVer,
298 block->VolSessionId, block->VolSessionTime);
300 read_record_from_block(block, rec);
301 Pmsg9(-1, _("File:blk=%u:%u blk_num=%u blen=%u First rec FI=%s SessId=%u SessTim=%u Strm=%s rlen=%d\n"),
302 dev->file, dev->block_num,
303 block->BlockNumber, block->block_len,
304 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId, rec->VolSessionTime,
305 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
307 } else if (verbose > 1) {
308 dump_block(block, "");
310 printf(_("Block: %d size=%d\n"), block->BlockNumber, block->block_len);
318 * We are only looking for labels or in particular Job Session records
320 static bool jobs_cb(DCR *dcr, DEV_RECORD *rec)
322 if (rec->FileIndex < 0) {
323 dump_label_record(dcr->dev, rec, verbose);
329 /* Do list job records */
330 static void do_jobs(char *infname)
332 read_records(dcr, jobs_cb, mount_next_read_volume);
335 /* Do an ls type listing of an archive */
336 static void do_ls(char *infname)
339 dump_volume_label(dev);
342 read_records(dcr, record_cb, mount_next_read_volume);
343 printf("%u files found.\n", num_files);
347 * Called here for each record from read_records()
349 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
351 if (rec->FileIndex < 0) {
352 get_session_record(dev, rec, &sessrec);
355 /* File Attributes stream */
356 if (rec->Stream == STREAM_UNIX_ATTRIBUTES ||
357 rec->Stream == STREAM_UNIX_ATTRIBUTES_EX) {
359 if (!unpack_attributes_record(jcr, rec->Stream, rec->data, attr)) {
361 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
367 if (attr->file_index != rec->FileIndex) {
368 Emsg2(forge_on?M_WARNING:M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"),
369 rec->FileIndex, attr->file_index);
372 attr->data_stream = decode_stat(attr->attr, &attr->statp, &attr->LinkFI);
373 build_attr_output_fnames(jcr, attr);
375 if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
377 Pmsg5(-1, _("FileIndex=%d VolSessionId=%d VolSessionTime=%d Stream=%d DataLen=%d\n"),
378 rec->FileIndex, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
380 print_ls_output(jcr, attr);
388 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
391 memset(sessrec, 0, sizeof(sessrec));
392 switch (rec->FileIndex) {
394 rtype = _("Fresh Volume Label");
397 rtype = _("Volume Label");
398 unser_volume_label(dev, rec);
401 rtype = _("Begin Job Session");
402 unser_session_label(sessrec, rec);
405 rtype = _("End Job Session");
409 rtype = _("End of Medium");
412 rtype = _("Unknown");
415 Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
416 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
418 Pmsg5(-1, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
419 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
424 /* Dummies to replace askdir.c */
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());
444 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
446 Dmsg0(100, "Fake dir_get_volume_info\n");
447 bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
448 dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
449 Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->VolCatInfo.VolCatName, dcr->VolCatInfo.VolCatParts);