3 * Dumb program to do an "ls" of a Bacula 1.0 mortal file.
8 Copyright (C) 2000-2005 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"
26 #if defined(HAVE_CYGWIN) || defined(HAVE_WIN32)
33 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
35 static void do_blocks(char *infname);
36 static void do_jobs(char *infname);
37 static void do_ls(char *fname);
38 static void do_close(JCR *jcr);
39 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec);
40 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
44 static bool dump_label = false;
45 static bool list_blocks = false;
46 static bool list_jobs = false;
47 static DEV_RECORD *rec;
48 static DEV_BLOCK *block;
50 static SESSION_LABEL sessrec;
51 static uint32_t num_files = 0;
54 #define CONFIG_FILE "bacula-sd.conf"
55 char *configfile = NULL;
56 STORES *me = NULL; /* our Global resource */
57 bool forge_on = false;
58 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
59 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
64 static BSR *bsr = NULL;
69 "Copyright (C) 2000-2005 Kern Sibbald.\n"
70 "\nVersion: %s (%s)\n\n"
71 "Usage: bls [options] <device-name>\n"
72 " -b <file> specify a bootstrap file\n"
73 " -c <file> specify a config file\n"
74 " -d <level> specify debug level\n"
75 " -e <file> exclude list\n"
76 " -i <file> include list\n"
79 " (no j or k option) list saved files\n"
81 " -p proceed inspite of errors\n"
83 " -V specify Volume names (separated by |)\n"
84 " -? print this message\n\n"), VERSION, BDATE);
89 int main (int argc, char *argv[])
94 char *VolumeName= NULL;
96 bool ignore_label_errors = false;
98 setlocale(LC_ALL, "");
99 bindtextdomain("bacula", LOCALEDIR);
100 textdomain("bacula");
102 working_directory = "/tmp";
103 my_name_is(argc, argv, "bls");
104 init_msg(NULL, NULL); /* initialize message handler */
106 ff = init_find_files();
108 while ((ch = getopt(argc, argv, "b:c:d:e:i:jkLpvV:?")) != -1) {
114 case 'c': /* specify config file */
115 if (configfile != NULL) {
118 configfile = bstrdup(optarg);
121 case 'd': /* debug level */
122 debug_level = atoi(optarg);
123 if (debug_level <= 0)
127 case 'e': /* exclude list */
128 if ((fd = fopen(optarg, "r")) == NULL) {
129 Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
130 optarg, strerror(errno));
133 while (fgets(line, sizeof(line), fd) != NULL) {
134 strip_trailing_junk(line);
135 Dmsg1(100, "add_exclude %s\n", line);
136 add_fname_to_exclude_list(ff, line);
141 case 'i': /* include list */
142 if ((fd = fopen(optarg, "r")) == NULL) {
143 Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
144 optarg, strerror(errno));
147 while (fgets(line, sizeof(line), fd) != NULL) {
148 strip_trailing_junk(line);
149 Dmsg1(100, "add_include %s\n", line);
150 add_fname_to_include_list(ff, 0, line);
168 ignore_label_errors = true;
176 case 'V': /* Volume name */
190 Pmsg0(0, _("No archive name specified\n"));
194 if (configfile == NULL) {
195 configfile = bstrdup(CONFIG_FILE);
198 parse_config(configfile);
200 if (ff->included_files_list == NULL) {
201 add_fname_to_include_list(ff, 0, "/");
204 for (i=0; i < argc; i++) {
206 bsr = parse_bsr(NULL, bsrName);
208 jcr = setup_jcr("bls", argv[i], bsr, VolumeName, 1); /* acquire for read */
212 jcr->ignore_label_errors = ignore_label_errors;
219 block = new_block(dev);
222 * Assume that we have already read the volume label.
223 * If on second or subsequent volume, adjust buffer pointer
225 if (dev->VolHdr.PrevVolumeName[0] != 0) { /* second volume */
227 "Warning, this Volume is a continuation of Volume %s\n"),
228 dev->VolHdr.PrevVolumeName);
233 } else if (list_jobs) {
243 term_include_exclude_files(ff);
249 static void do_close(JCR *jcr)
251 release_device(jcr->dcr);
260 /* List just block information */
261 static void do_blocks(char *infname)
263 char buf1[100], buf2[100];
265 if (!read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK)) {
266 Dmsg1(100, "!read_block(): ERR=%s\n", dev->strerror());
268 if (!mount_next_read_volume(dcr)) {
269 Jmsg(jcr, M_INFO, 0, _("Got EOM at file %u on device %s, Volume \"%s\"\n"),
270 dev->file, dev->print_name(), dcr->VolumeName);
273 /* Read and discard Volume label */
275 record = new_record();
276 read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK);
277 read_record_from_block(block, record);
278 get_session_record(dev, record, &sessrec);
280 Jmsg(jcr, M_INFO, 0, _("Mounted Volume \"%s\".\n"), dcr->VolumeName);
281 } else if (dev->at_eof()) {
282 Jmsg(jcr, M_INFO, 0, _("Got EOF at file %u on device %s, Volume \"%s\"\n"),
283 dev->file, dev->print_name(), dcr->VolumeName);
284 Dmsg0(20, "read_record got eof. try again\n");
286 } else if (dev->is_short_block()) {
287 Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
291 display_tape_error_status(jcr, dev);
295 if (!match_bsr_block(bsr, block)) {
296 Dmsg5(100, "reject Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
297 block->BlockNumber, block->block_len, block->BlockVer,
298 block->VolSessionId, block->VolSessionTime);
301 Dmsg5(100, "Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
302 block->BlockNumber, block->block_len, block->BlockVer,
303 block->VolSessionId, block->VolSessionTime);
305 read_record_from_block(block, rec);
306 Pmsg9(-1, _("File:blk=%u:%u blk_num=%u blen=%u First rec FI=%s SessId=%u SessTim=%u Strm=%s rlen=%d\n"),
307 dev->file, dev->block_num,
308 block->BlockNumber, block->block_len,
309 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId, rec->VolSessionTime,
310 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
312 } else if (verbose > 1) {
313 dump_block(block, "");
315 printf(_("Block: %d size=%d\n"), block->BlockNumber, block->block_len);
323 * We are only looking for labels or in particular Job Session records
325 static bool jobs_cb(DCR *dcr, DEV_RECORD *rec)
327 if (rec->FileIndex < 0) {
328 dump_label_record(dcr->dev, rec, verbose);
334 /* Do list job records */
335 static void do_jobs(char *infname)
337 read_records(dcr, jobs_cb, mount_next_read_volume);
340 /* Do an ls type listing of an archive */
341 static void do_ls(char *infname)
344 dump_volume_label(dev);
347 read_records(dcr, record_cb, mount_next_read_volume);
348 printf("%u files found.\n", num_files);
352 * Called here for each record from read_records()
354 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
356 if (rec->FileIndex < 0) {
357 get_session_record(dev, rec, &sessrec);
360 /* File Attributes stream */
361 if (rec->Stream == STREAM_UNIX_ATTRIBUTES ||
362 rec->Stream == STREAM_UNIX_ATTRIBUTES_EX) {
364 if (!unpack_attributes_record(jcr, rec->Stream, rec->data, attr)) {
366 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
372 if (attr->file_index != rec->FileIndex) {
373 Emsg2(forge_on?M_WARNING:M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"),
374 rec->FileIndex, attr->file_index);
377 attr->data_stream = decode_stat(attr->attr, &attr->statp, &attr->LinkFI);
378 build_attr_output_fnames(jcr, attr);
380 if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
382 Pmsg5(-1, _("FileIndex=%d VolSessionId=%d VolSessionTime=%d Stream=%d DataLen=%d\n"),
383 rec->FileIndex, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
385 print_ls_output(jcr, attr);
393 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
396 memset(sessrec, 0, sizeof(sessrec));
397 switch (rec->FileIndex) {
399 rtype = _("Fresh Volume Label");
402 rtype = _("Volume Label");
403 unser_volume_label(dev, rec);
406 rtype = _("Begin Job Session");
407 unser_session_label(sessrec, rec);
410 rtype = _("End Job Session");
413 rtype = _("End of Medium");
416 rtype = _("Unknown");
419 Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
420 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
422 Pmsg5(-1, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
423 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
428 /* Dummies to replace askdir.c */
429 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing) { return 1;}
430 bool dir_find_next_appendable_volume(DCR *dcr) { return 1;}
431 bool dir_update_volume_info(DCR *dcr, bool relabel) { return 1; }
432 bool dir_create_jobmedia_record(DCR *dcr) { return 1; }
433 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
434 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
435 bool dir_send_job_status(JCR *jcr) {return 1;}
436 int generate_job_event(JCR *jcr, const char *event) { return 1; }
437 VOLRES *new_volume(DCR *dcr, const char *VolumeName) { return NULL; }
438 bool free_volume(DEVICE *dev) { return true; }
439 void free_unused_volume(DCR *dcr) { }
442 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
444 DEVICE *dev = dcr->dev;
445 fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
446 dcr->VolumeName, dev->print_name());