2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2007 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 <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"), 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 debug_level = atoi(optarg);
134 if (debug_level <= 0)
138 case 'e': /* exclude list */
139 if ((fd = fopen(optarg, "rb")) == NULL) {
141 Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
142 optarg, be.bstrerror());
145 while (fgets(line, sizeof(line), fd) != NULL) {
146 strip_trailing_junk(line);
147 Dmsg1(100, "add_exclude %s\n", line);
148 add_fname_to_exclude_list(ff, line);
153 case 'i': /* include list */
154 if ((fd = fopen(optarg, "rb")) == NULL) {
156 Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
157 optarg, be.bstrerror());
160 while (fgets(line, sizeof(line), fd) != NULL) {
161 strip_trailing_junk(line);
162 Dmsg1(100, "add_include %s\n", line);
163 add_fname_to_include_list(ff, 0, line);
181 ignore_label_errors = true;
189 case 'V': /* Volume name */
203 Pmsg0(0, _("No archive name specified\n"));
207 if (configfile == NULL) {
208 configfile = bstrdup(CONFIG_FILE);
211 parse_config(configfile);
213 if (ff->included_files_list == NULL) {
214 add_fname_to_include_list(ff, 0, "/");
217 for (i=0; i < argc; i++) {
219 bsr = parse_bsr(NULL, bsrName);
221 jcr = setup_jcr("bls", argv[i], bsr, VolumeName, 1); /* acquire for read */
225 jcr->ignore_label_errors = ignore_label_errors;
232 attr = new_attr(jcr);
234 * Assume that we have already read the volume label.
235 * If on second or subsequent volume, adjust buffer pointer
237 if (dev->VolHdr.PrevVolumeName[0] != 0) { /* second volume */
239 "Warning, this Volume is a continuation of Volume %s\n"),
240 dev->VolHdr.PrevVolumeName);
245 } else if (list_jobs) {
255 term_include_exclude_files(ff);
261 static void do_close(JCR *jcr)
263 release_device(jcr->dcr);
271 /* List just block information */
272 static void do_blocks(char *infname)
274 DEV_BLOCK *block = dcr->block;
275 char buf1[100], buf2[100];
277 if (!read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK)) {
278 Dmsg1(100, "!read_block(): ERR=%s\n", dev->bstrerror());
280 if (!mount_next_read_volume(dcr)) {
281 Jmsg(jcr, M_INFO, 0, _("Got EOM at file %u on device %s, Volume \"%s\"\n"),
282 dev->file, dev->print_name(), dcr->VolumeName);
285 /* Read and discard Volume label */
287 record = new_record();
288 read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK);
289 read_record_from_block(dcr, block, record);
290 get_session_record(dev, record, &sessrec);
292 Jmsg(jcr, M_INFO, 0, _("Mounted Volume \"%s\".\n"), dcr->VolumeName);
293 } else if (dev->at_eof()) {
294 Jmsg(jcr, M_INFO, 0, _("End of file %u on device %s, Volume \"%s\"\n"),
295 dev->file, dev->print_name(), dcr->VolumeName);
296 Dmsg0(20, "read_record got eof. try again\n");
298 } else if (dev->is_short_block()) {
299 Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
303 display_tape_error_status(jcr, dev);
307 if (!match_bsr_block(bsr, block)) {
308 Dmsg5(100, "reject Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
309 block->BlockNumber, block->block_len, block->BlockVer,
310 block->VolSessionId, block->VolSessionTime);
313 Dmsg5(100, "Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
314 block->BlockNumber, block->block_len, block->BlockVer,
315 block->VolSessionId, block->VolSessionTime);
317 read_record_from_block(dcr, block, rec);
318 Pmsg9(-1, _("File:blk=%u:%u blk_num=%u blen=%u First rec FI=%s SessId=%u SessTim=%u Strm=%s rlen=%d\n"),
319 dev->file, dev->block_num,
320 block->BlockNumber, block->block_len,
321 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId, rec->VolSessionTime,
322 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
324 } else if (verbose > 1) {
325 dump_block(block, "");
327 printf(_("Block: %d size=%d\n"), block->BlockNumber, block->block_len);
335 * We are only looking for labels or in particular Job Session records
337 static bool jobs_cb(DCR *dcr, DEV_RECORD *rec)
339 if (rec->FileIndex < 0) {
340 dump_label_record(dcr->dev, rec, verbose);
346 /* Do list job records */
347 static void do_jobs(char *infname)
349 read_records(dcr, jobs_cb, mount_next_read_volume);
352 /* Do an ls type listing of an archive */
353 static void do_ls(char *infname)
356 dump_volume_label(dev);
359 read_records(dcr, record_cb, mount_next_read_volume);
360 printf("%u files found.\n", num_files);
364 * Called here for each record from read_records()
366 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
368 if (rec->FileIndex < 0) {
369 get_session_record(dev, rec, &sessrec);
372 /* File Attributes stream */
373 if (rec->Stream == STREAM_UNIX_ATTRIBUTES ||
374 rec->Stream == STREAM_UNIX_ATTRIBUTES_EX) {
376 if (!unpack_attributes_record(jcr, rec->Stream, rec->data, attr)) {
378 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
384 if (attr->file_index != rec->FileIndex) {
385 Emsg2(forge_on?M_WARNING:M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"),
386 rec->FileIndex, attr->file_index);
389 attr->data_stream = decode_stat(attr->attr, &attr->statp, &attr->LinkFI);
390 build_attr_output_fnames(jcr, attr);
392 if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
394 Pmsg5(-1, _("FileIndex=%d VolSessionId=%d VolSessionTime=%d Stream=%d DataLen=%d\n"),
395 rec->FileIndex, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
397 print_ls_output(jcr, attr);
405 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
408 memset(sessrec, 0, sizeof(sessrec));
409 switch (rec->FileIndex) {
411 rtype = _("Fresh Volume Label");
414 rtype = _("Volume Label");
415 unser_volume_label(dev, rec);
418 rtype = _("Begin Job Session");
419 unser_session_label(sessrec, rec);
422 rtype = _("End Job Session");
426 rtype = _("End of Medium");
429 rtype = _("Unknown");
432 Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
433 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
435 Pmsg5(-1, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
436 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
441 /* Dummies to replace askdir.c */
442 bool dir_find_next_appendable_volume(DCR *dcr) { return 1;}
443 bool dir_update_volume_info(DCR *dcr, bool relabel) { return 1; }
444 bool dir_create_jobmedia_record(DCR *dcr) { return 1; }
445 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
446 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
447 bool dir_send_job_status(JCR *jcr) {return 1;}
448 int generate_job_event(JCR *jcr, const char *event) { return 1; }
451 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
453 DEVICE *dev = dcr->dev;
454 fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
455 dcr->VolumeName, dev->print_name());
461 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
463 Dmsg0(100, "Fake dir_get_volume_info\n");
464 bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
465 dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
466 Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->VolCatInfo.VolCatName, dcr->VolCatInfo.VolCatParts);