3 * Dumb program to do an "ls" of a Bacula 1.0 mortal file.
10 Bacula® - The Network Backup Solution
12 Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
14 The main author of Bacula is Kern Sibbald, with contributions from
15 many others, a complete list can be found in the file AUTHORS.
16 This program is Free Software; you can redistribute it and/or
17 modify it under the terms of version two of the GNU General Public
18 License as published by the Free Software Foundation plus additions
19 that are listed in the file LICENSE.
21 This program is distributed in the hope that it will be useful, but
22 WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 General Public License for more details.
26 You should have received a copy of the GNU General Public License
27 along with this program; if not, write to the Free Software
28 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
31 Bacula® is a registered trademark of John Walker.
32 The licensor of Bacula is the Free Software Foundation Europe
33 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
34 Switzerland, email:ftf@fsfeurope.org.
39 #include "findlib/find.h"
42 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
44 static void do_blocks(char *infname);
45 static void do_jobs(char *infname);
46 static void do_ls(char *fname);
47 static void do_close(JCR *jcr);
48 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec);
49 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
53 static bool dump_label = false;
54 static bool list_blocks = false;
55 static bool list_jobs = false;
56 static DEV_RECORD *rec;
58 static SESSION_LABEL sessrec;
59 static uint32_t num_files = 0;
62 #define CONFIG_FILE "bacula-sd.conf"
63 char *configfile = NULL;
64 STORES *me = NULL; /* our Global resource */
65 bool forge_on = false;
66 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
67 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
72 static BSR *bsr = NULL;
78 "\nVersion: %s (%s)\n\n"
79 "Usage: bls [options] <device-name>\n"
80 " -b <file> specify a bootstrap file\n"
81 " -c <file> specify a config file\n"
82 " -d <level> specify debug level\n"
83 " -e <file> exclude list\n"
84 " -i <file> include list\n"
87 " (no j or k option) list saved files\n"
89 " -p proceed inspite of errors\n"
91 " -V specify Volume names (separated by |)\n"
92 " -? print this message\n\n"), BYEAR, VERSION, BDATE);
97 int main (int argc, char *argv[])
102 char *VolumeName= NULL;
103 char *bsrName = NULL;
104 bool ignore_label_errors = false;
106 setlocale(LC_ALL, "");
107 bindtextdomain("bacula", LOCALEDIR);
108 textdomain("bacula");
111 working_directory = "/tmp";
112 my_name_is(argc, argv, "bls");
113 init_msg(NULL, NULL); /* initialize message handler */
117 ff = init_find_files();
119 while ((ch = getopt(argc, argv, "b:c:d:e:i:jkLpvV:?")) != -1) {
125 case 'c': /* specify config file */
126 if (configfile != NULL) {
129 configfile = bstrdup(optarg);
132 case 'd': /* debug level */
133 debug_level = atoi(optarg);
134 if (debug_level <= 0)
138 case 'e': /* exclude list */
139 if ((fd = fopen(optarg, "rb")) == NULL) {
140 Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
141 optarg, strerror(errno));
144 while (fgets(line, sizeof(line), fd) != NULL) {
145 strip_trailing_junk(line);
146 Dmsg1(100, "add_exclude %s\n", line);
147 add_fname_to_exclude_list(ff, line);
152 case 'i': /* include list */
153 if ((fd = fopen(optarg, "rb")) == NULL) {
154 Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
155 optarg, strerror(errno));
158 while (fgets(line, sizeof(line), fd) != NULL) {
159 strip_trailing_junk(line);
160 Dmsg1(100, "add_include %s\n", line);
161 add_fname_to_include_list(ff, 0, line);
179 ignore_label_errors = true;
187 case 'V': /* Volume name */
201 Pmsg0(0, _("No archive name specified\n"));
205 if (configfile == NULL) {
206 configfile = bstrdup(CONFIG_FILE);
209 parse_config(configfile);
211 if (ff->included_files_list == NULL) {
212 add_fname_to_include_list(ff, 0, "/");
215 for (i=0; i < argc; i++) {
217 bsr = parse_bsr(NULL, bsrName);
219 jcr = setup_jcr("bls", argv[i], bsr, VolumeName, 1); /* acquire for read */
223 jcr->ignore_label_errors = ignore_label_errors;
232 * Assume that we have already read the volume label.
233 * If on second or subsequent volume, adjust buffer pointer
235 if (dev->VolHdr.PrevVolumeName[0] != 0) { /* second volume */
237 "Warning, this Volume is a continuation of Volume %s\n"),
238 dev->VolHdr.PrevVolumeName);
243 } else if (list_jobs) {
253 term_include_exclude_files(ff);
259 static void do_close(JCR *jcr)
261 release_device(jcr->dcr);
269 /* List just block information */
270 static void do_blocks(char *infname)
272 DEV_BLOCK *block = dcr->block;
273 char buf1[100], buf2[100];
275 if (!read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK)) {
276 Dmsg1(100, "!read_block(): ERR=%s\n", dev->strerror());
278 if (!mount_next_read_volume(dcr)) {
279 Jmsg(jcr, M_INFO, 0, _("Got EOM at file %u on device %s, Volume \"%s\"\n"),
280 dev->file, dev->print_name(), dcr->VolumeName);
283 /* Read and discard Volume label */
285 record = new_record();
286 read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK);
287 read_record_from_block(block, record);
288 get_session_record(dev, record, &sessrec);
290 Jmsg(jcr, M_INFO, 0, _("Mounted Volume \"%s\".\n"), dcr->VolumeName);
291 } else if (dev->at_eof()) {
292 Jmsg(jcr, M_INFO, 0, _("End of file %u on device %s, Volume \"%s\"\n"),
293 dev->file, dev->print_name(), dcr->VolumeName);
294 Dmsg0(20, "read_record got eof. try again\n");
296 } else if (dev->is_short_block()) {
297 Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
301 display_tape_error_status(jcr, dev);
305 if (!match_bsr_block(bsr, block)) {
306 Dmsg5(100, "reject Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
307 block->BlockNumber, block->block_len, block->BlockVer,
308 block->VolSessionId, block->VolSessionTime);
311 Dmsg5(100, "Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
312 block->BlockNumber, block->block_len, block->BlockVer,
313 block->VolSessionId, block->VolSessionTime);
315 read_record_from_block(block, rec);
316 Pmsg9(-1, _("File:blk=%u:%u blk_num=%u blen=%u First rec FI=%s SessId=%u SessTim=%u Strm=%s rlen=%d\n"),
317 dev->file, dev->block_num,
318 block->BlockNumber, block->block_len,
319 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId, rec->VolSessionTime,
320 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
322 } else if (verbose > 1) {
323 dump_block(block, "");
325 printf(_("Block: %d size=%d\n"), block->BlockNumber, block->block_len);
333 * We are only looking for labels or in particular Job Session records
335 static bool jobs_cb(DCR *dcr, DEV_RECORD *rec)
337 if (rec->FileIndex < 0) {
338 dump_label_record(dcr->dev, rec, verbose);
344 /* Do list job records */
345 static void do_jobs(char *infname)
347 read_records(dcr, jobs_cb, mount_next_read_volume);
350 /* Do an ls type listing of an archive */
351 static void do_ls(char *infname)
354 dump_volume_label(dev);
357 read_records(dcr, record_cb, mount_next_read_volume);
358 printf("%u files found.\n", num_files);
362 * Called here for each record from read_records()
364 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
366 if (rec->FileIndex < 0) {
367 get_session_record(dev, rec, &sessrec);
370 /* File Attributes stream */
371 if (rec->Stream == STREAM_UNIX_ATTRIBUTES ||
372 rec->Stream == STREAM_UNIX_ATTRIBUTES_EX) {
374 if (!unpack_attributes_record(jcr, rec->Stream, rec->data, attr)) {
376 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
382 if (attr->file_index != rec->FileIndex) {
383 Emsg2(forge_on?M_WARNING:M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"),
384 rec->FileIndex, attr->file_index);
387 attr->data_stream = decode_stat(attr->attr, &attr->statp, &attr->LinkFI);
388 build_attr_output_fnames(jcr, attr);
390 if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
392 Pmsg5(-1, _("FileIndex=%d VolSessionId=%d VolSessionTime=%d Stream=%d DataLen=%d\n"),
393 rec->FileIndex, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
395 print_ls_output(jcr, attr);
403 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
406 memset(sessrec, 0, sizeof(sessrec));
407 switch (rec->FileIndex) {
409 rtype = _("Fresh Volume Label");
412 rtype = _("Volume Label");
413 unser_volume_label(dev, rec);
416 rtype = _("Begin Job Session");
417 unser_session_label(sessrec, rec);
420 rtype = _("End Job Session");
424 rtype = _("End of Medium");
427 rtype = _("Unknown");
430 Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
431 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
433 Pmsg5(-1, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
434 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
439 /* Dummies to replace askdir.c */
440 bool dir_find_next_appendable_volume(DCR *dcr) { return 1;}
441 bool dir_update_volume_info(DCR *dcr, bool relabel) { return 1; }
442 bool dir_create_jobmedia_record(DCR *dcr) { return 1; }
443 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
444 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
445 bool dir_send_job_status(JCR *jcr) {return 1;}
446 int generate_job_event(JCR *jcr, const char *event) { return 1; }
449 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
451 DEVICE *dev = dcr->dev;
452 fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
453 dcr->VolumeName, dev->print_name());
459 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
461 Dmsg0(100, "Fake dir_get_volume_info\n");
462 bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
463 dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
464 Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->VolCatInfo.VolCatName, dcr->VolCatInfo.VolCatParts);