2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of John Walker.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 * Dumb program to do an "ls" of a Bacula 1.0 mortal file.
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 <nn> set debug level to <nn>\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"), 2000, 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 if (*optarg == 't') {
134 dbg_timestamp = true;
136 debug_level = atoi(optarg);
137 if (debug_level <= 0) {
143 case 'e': /* exclude list */
144 if ((fd = fopen(optarg, "rb")) == NULL) {
146 Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
147 optarg, be.bstrerror());
150 while (fgets(line, sizeof(line), fd) != NULL) {
151 strip_trailing_junk(line);
152 Dmsg1(100, "add_exclude %s\n", line);
153 add_fname_to_exclude_list(ff, line);
158 case 'i': /* include list */
159 if ((fd = fopen(optarg, "rb")) == NULL) {
161 Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
162 optarg, be.bstrerror());
165 while (fgets(line, sizeof(line), fd) != NULL) {
166 strip_trailing_junk(line);
167 Dmsg1(100, "add_include %s\n", line);
168 add_fname_to_include_list(ff, 0, line);
186 ignore_label_errors = true;
194 case 'V': /* Volume name */
208 Pmsg0(0, _("No archive name specified\n"));
212 if (configfile == NULL) {
213 configfile = bstrdup(CONFIG_FILE);
216 parse_config(configfile);
218 if (ff->included_files_list == NULL) {
219 add_fname_to_include_list(ff, 0, "/");
222 for (i=0; i < argc; i++) {
224 bsr = parse_bsr(NULL, bsrName);
226 jcr = setup_jcr("bls", argv[i], bsr, VolumeName, 1); /* acquire for read */
230 jcr->ignore_label_errors = ignore_label_errors;
237 attr = new_attr(jcr);
239 * Assume that we have already read the volume label.
240 * If on second or subsequent volume, adjust buffer pointer
242 if (dev->VolHdr.PrevVolumeName[0] != 0) { /* second volume */
244 "Warning, this Volume is a continuation of Volume %s\n"),
245 dev->VolHdr.PrevVolumeName);
250 } else if (list_jobs) {
260 term_include_exclude_files(ff);
266 static void do_close(JCR *jcr)
268 release_device(jcr->dcr);
276 /* List just block information */
277 static void do_blocks(char *infname)
279 DEV_BLOCK *block = dcr->block;
280 char buf1[100], buf2[100];
282 if (!read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK)) {
283 Dmsg1(100, "!read_block(): ERR=%s\n", dev->bstrerror());
285 if (!mount_next_read_volume(dcr)) {
286 Jmsg(jcr, M_INFO, 0, _("Got EOM at file %u on device %s, Volume \"%s\"\n"),
287 dev->file, dev->print_name(), dcr->VolumeName);
290 /* Read and discard Volume label */
292 record = new_record();
293 read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK);
294 read_record_from_block(dcr, block, record);
295 get_session_record(dev, record, &sessrec);
297 Jmsg(jcr, M_INFO, 0, _("Mounted Volume \"%s\".\n"), dcr->VolumeName);
298 } else if (dev->at_eof()) {
299 Jmsg(jcr, M_INFO, 0, _("End of file %u on device %s, Volume \"%s\"\n"),
300 dev->file, dev->print_name(), dcr->VolumeName);
301 Dmsg0(20, "read_record got eof. try again\n");
303 } else if (dev->is_short_block()) {
304 Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
308 display_tape_error_status(jcr, dev);
312 if (!match_bsr_block(bsr, block)) {
313 Dmsg5(100, "reject Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
314 block->BlockNumber, block->block_len, block->BlockVer,
315 block->VolSessionId, block->VolSessionTime);
318 Dmsg5(100, "Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
319 block->BlockNumber, block->block_len, block->BlockVer,
320 block->VolSessionId, block->VolSessionTime);
322 read_record_from_block(dcr, block, rec);
323 Pmsg9(-1, _("File:blk=%u:%u blk_num=%u blen=%u First rec FI=%s SessId=%u SessTim=%u Strm=%s rlen=%d\n"),
324 dev->file, dev->block_num,
325 block->BlockNumber, block->block_len,
326 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId, rec->VolSessionTime,
327 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
329 } else if (verbose > 1) {
330 dump_block(block, "");
332 printf(_("Block: %d size=%d\n"), block->BlockNumber, block->block_len);
340 * We are only looking for labels or in particular Job Session records
342 static bool jobs_cb(DCR *dcr, DEV_RECORD *rec)
344 if (rec->FileIndex < 0) {
345 dump_label_record(dcr->dev, rec, verbose);
351 /* Do list job records */
352 static void do_jobs(char *infname)
354 read_records(dcr, jobs_cb, mount_next_read_volume);
357 /* Do an ls type listing of an archive */
358 static void do_ls(char *infname)
361 dump_volume_label(dev);
364 read_records(dcr, record_cb, mount_next_read_volume);
365 printf("%u files found.\n", num_files);
369 * Called here for each record from read_records()
371 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
373 if (rec->FileIndex < 0) {
374 get_session_record(dev, rec, &sessrec);
377 /* File Attributes stream */
378 if (rec->Stream == STREAM_UNIX_ATTRIBUTES ||
379 rec->Stream == STREAM_UNIX_ATTRIBUTES_EX) {
381 if (!unpack_attributes_record(jcr, rec->Stream, rec->data, attr)) {
383 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
389 if (attr->file_index != rec->FileIndex) {
390 Emsg2(forge_on?M_WARNING:M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"),
391 rec->FileIndex, attr->file_index);
394 attr->data_stream = decode_stat(attr->attr, &attr->statp, &attr->LinkFI);
395 build_attr_output_fnames(jcr, attr);
397 if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
399 Pmsg5(-1, _("FileIndex=%d VolSessionId=%d VolSessionTime=%d Stream=%d DataLen=%d\n"),
400 rec->FileIndex, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
402 print_ls_output(jcr, attr);
410 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
413 memset(sessrec, 0, sizeof(sessrec));
414 switch (rec->FileIndex) {
416 rtype = _("Fresh Volume Label");
419 rtype = _("Volume Label");
420 unser_volume_label(dev, rec);
423 rtype = _("Begin Job Session");
424 unser_session_label(sessrec, rec);
427 rtype = _("End Job Session");
431 rtype = _("End of Medium");
434 rtype = _("Unknown");
437 Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
438 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
440 Pmsg5(-1, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
441 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
446 /* Dummies to replace askdir.c */
447 bool dir_find_next_appendable_volume(DCR *dcr) { return 1;}
448 bool dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
449 bool dir_create_jobmedia_record(DCR *dcr) { return 1; }
450 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
451 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
452 bool dir_send_job_status(JCR *jcr) {return 1;}
453 int generate_job_event(JCR *jcr, const char *event) { return 1; }
456 bool dir_ask_sysop_to_mount_volume(DCR *dcr, int /*mode*/)
458 DEVICE *dev = dcr->dev;
459 fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
460 dcr->VolumeName, dev->print_name());
466 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
468 Dmsg0(100, "Fake dir_get_volume_info\n");
469 bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
470 dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
471 Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->VolCatInfo.VolCatName, dcr->VolCatInfo.VolCatParts);