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"
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;
49 static SESSION_LABEL sessrec;
50 static uint32_t num_files = 0;
53 #define CONFIG_FILE "bacula-sd.conf"
54 char *configfile = NULL;
55 STORES *me = NULL; /* our Global resource */
56 bool forge_on = false;
57 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
58 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
63 static BSR *bsr = NULL;
68 "Copyright (C) 2000-2005 Kern Sibbald.\n"
69 "\nVersion: %s (%s)\n\n"
70 "Usage: bls [options] <device-name>\n"
71 " -b <file> specify a bootstrap file\n"
72 " -c <file> specify a config file\n"
73 " -d <level> specify debug level\n"
74 " -e <file> exclude list\n"
75 " -i <file> include list\n"
78 " (no j or k option) list saved files\n"
80 " -p proceed inspite of errors\n"
82 " -V specify Volume names (separated by |)\n"
83 " -? print this message\n\n"), VERSION, BDATE);
88 int main (int argc, char *argv[])
93 char *VolumeName= NULL;
95 bool ignore_label_errors = false;
97 setlocale(LC_ALL, "");
98 bindtextdomain("bacula", LOCALEDIR);
101 working_directory = "/tmp";
102 my_name_is(argc, argv, "bls");
103 init_msg(NULL, NULL); /* initialize message handler */
105 ff = init_find_files();
107 while ((ch = getopt(argc, argv, "b:c:d:e:i:jkLpvV:?")) != -1) {
113 case 'c': /* specify config file */
114 if (configfile != NULL) {
117 configfile = bstrdup(optarg);
120 case 'd': /* debug level */
121 debug_level = atoi(optarg);
122 if (debug_level <= 0)
126 case 'e': /* exclude list */
127 if ((fd = fopen(optarg, "r")) == NULL) {
128 Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
129 optarg, strerror(errno));
132 while (fgets(line, sizeof(line), fd) != NULL) {
133 strip_trailing_junk(line);
134 Dmsg1(100, "add_exclude %s\n", line);
135 add_fname_to_exclude_list(ff, line);
140 case 'i': /* include list */
141 if ((fd = fopen(optarg, "r")) == NULL) {
142 Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
143 optarg, strerror(errno));
146 while (fgets(line, sizeof(line), fd) != NULL) {
147 strip_trailing_junk(line);
148 Dmsg1(100, "add_include %s\n", line);
149 add_fname_to_include_list(ff, 0, line);
167 ignore_label_errors = true;
175 case 'V': /* Volume name */
189 Pmsg0(0, _("No archive name specified\n"));
193 if (configfile == NULL) {
194 configfile = bstrdup(CONFIG_FILE);
197 parse_config(configfile);
199 if (ff->included_files_list == NULL) {
200 add_fname_to_include_list(ff, 0, "/");
203 for (i=0; i < argc; i++) {
205 bsr = parse_bsr(NULL, bsrName);
207 jcr = setup_jcr("bls", argv[i], bsr, VolumeName, 1); /* acquire for read */
211 jcr->ignore_label_errors = ignore_label_errors;
220 * Assume that we have already read the volume label.
221 * If on second or subsequent volume, adjust buffer pointer
223 if (dev->VolHdr.PrevVolumeName[0] != 0) { /* second volume */
225 "Warning, this Volume is a continuation of Volume %s\n"),
226 dev->VolHdr.PrevVolumeName);
231 } else if (list_jobs) {
241 term_include_exclude_files(ff);
247 static void do_close(JCR *jcr)
249 release_device(jcr->dcr);
257 /* List just block information */
258 static void do_blocks(char *infname)
260 char buf1[100], buf2[100];
261 DEV_BLOCK *block = dcr->block;
263 if (!read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK)) {
264 Dmsg1(100, "!read_block(): ERR=%s\n", dev->strerror());
266 if (!mount_next_read_volume(dcr)) {
267 Jmsg(jcr, M_INFO, 0, _("Got EOM at file %u on device %s, Volume \"%s\"\n"),
268 dev->file, dev->print_name(), dcr->VolumeName);
271 /* Read and discard Volume label */
273 record = new_record();
274 read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK);
275 read_record_from_block(block, record);
276 get_session_record(dev, record, &sessrec);
278 Jmsg(jcr, M_INFO, 0, _("Mounted Volume \"%s\".\n"), dcr->VolumeName);
279 } else if (dev->at_eof()) {
280 Jmsg(jcr, M_INFO, 0, _("End of file %u on device %s, Volume \"%s\"\n"),
281 dev->file, dev->print_name(), dcr->VolumeName);
282 Dmsg0(20, "read_record got eof. try again\n");
284 } else if (dev->is_short_block()) {
285 Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
289 display_tape_error_status(jcr, dev);
293 if (!match_bsr_block(bsr, block)) {
294 Dmsg5(100, "reject Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
295 block->BlockNumber, block->block_len, block->BlockVer,
296 block->VolSessionId, block->VolSessionTime);
299 Dmsg5(100, "Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
300 block->BlockNumber, block->block_len, block->BlockVer,
301 block->VolSessionId, block->VolSessionTime);
303 read_record_from_block(block, rec);
304 Pmsg9(-1, _("File:blk=%u:%u blk_num=%u blen=%u First rec FI=%s SessId=%u SessTim=%u Strm=%s rlen=%d\n"),
305 dev->file, dev->block_num,
306 block->BlockNumber, block->block_len,
307 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId, rec->VolSessionTime,
308 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
310 } else if (verbose > 1) {
311 dump_block(block, "");
313 printf(_("Block: %d size=%d\n"), block->BlockNumber, block->block_len);
321 * We are only looking for labels or in particular Job Session records
323 static bool jobs_cb(DCR *dcr, DEV_RECORD *rec)
325 if (rec->FileIndex < 0) {
326 dump_label_record(dcr->dev, rec, verbose);
332 /* Do list job records */
333 static void do_jobs(char *infname)
335 read_records(dcr, jobs_cb, mount_next_read_volume);
338 /* Do an ls type listing of an archive */
339 static void do_ls(char *infname)
342 dump_volume_label(dev);
345 read_records(dcr, record_cb, mount_next_read_volume);
346 printf("%u files found.\n", num_files);
350 * Called here for each record from read_records()
352 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
354 if (rec->FileIndex < 0) {
355 get_session_record(dev, rec, &sessrec);
358 /* File Attributes stream */
359 if (rec->Stream == STREAM_UNIX_ATTRIBUTES ||
360 rec->Stream == STREAM_UNIX_ATTRIBUTES_EX) {
362 if (!unpack_attributes_record(jcr, rec->Stream, rec->data, attr)) {
364 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
370 if (attr->file_index != rec->FileIndex) {
371 Emsg2(forge_on?M_WARNING:M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"),
372 rec->FileIndex, attr->file_index);
375 attr->data_stream = decode_stat(attr->attr, &attr->statp, &attr->LinkFI);
376 build_attr_output_fnames(jcr, attr);
378 if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
380 Pmsg5(-1, _("FileIndex=%d VolSessionId=%d VolSessionTime=%d Stream=%d DataLen=%d\n"),
381 rec->FileIndex, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
383 print_ls_output(jcr, attr);
391 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
394 memset(sessrec, 0, sizeof(sessrec));
395 switch (rec->FileIndex) {
397 rtype = _("Fresh Volume Label");
400 rtype = _("Volume Label");
401 unser_volume_label(dev, rec);
404 rtype = _("Begin Job Session");
405 unser_session_label(sessrec, rec);
408 rtype = _("End Job Session");
411 rtype = _("End of Medium");
414 rtype = _("Unknown");
417 Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
418 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
420 Pmsg5(-1, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
421 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
426 /* Dummies to replace askdir.c */
427 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing) { return 1;}
428 bool dir_find_next_appendable_volume(DCR *dcr) { return 1;}
429 bool dir_update_volume_info(DCR *dcr, bool relabel) { return 1; }
430 bool dir_create_jobmedia_record(DCR *dcr) { return 1; }
431 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
432 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
433 bool dir_send_job_status(JCR *jcr) {return 1;}
434 int generate_job_event(JCR *jcr, const char *event) { return 1; }
437 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
439 DEVICE *dev = dcr->dev;
440 fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
441 dcr->VolumeName, dev->print_name());