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 " -dt print timestamp in debug output\n"
84 " -e <file> exclude list\n"
85 " -i <file> include list\n"
88 " (no j or k option) list saved files\n"
90 " -p proceed inspite of errors\n"
92 " -V specify Volume names (separated by |)\n"
93 " -? print this message\n\n"), 2000, VERSION, BDATE);
98 int main (int argc, char *argv[])
103 char *VolumeName= NULL;
104 char *bsrName = NULL;
105 bool ignore_label_errors = false;
107 setlocale(LC_ALL, "");
108 bindtextdomain("bacula", LOCALEDIR);
109 textdomain("bacula");
112 working_directory = "/tmp";
113 my_name_is(argc, argv, "bls");
114 init_msg(NULL, NULL); /* initialize message handler */
118 ff = init_find_files();
120 while ((ch = getopt(argc, argv, "b:c:d:e:i:jkLpvV:?")) != -1) {
126 case 'c': /* specify config file */
127 if (configfile != NULL) {
130 configfile = bstrdup(optarg);
133 case 'd': /* debug level */
134 if (*optarg == 't') {
135 dbg_timestamp = true;
137 debug_level = atoi(optarg);
138 if (debug_level <= 0) {
144 case 'e': /* exclude list */
145 if ((fd = fopen(optarg, "rb")) == NULL) {
147 Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
148 optarg, be.bstrerror());
151 while (fgets(line, sizeof(line), fd) != NULL) {
152 strip_trailing_junk(line);
153 Dmsg1(100, "add_exclude %s\n", line);
154 add_fname_to_exclude_list(ff, line);
159 case 'i': /* include list */
160 if ((fd = fopen(optarg, "rb")) == NULL) {
162 Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
163 optarg, be.bstrerror());
166 while (fgets(line, sizeof(line), fd) != NULL) {
167 strip_trailing_junk(line);
168 Dmsg1(100, "add_include %s\n", line);
169 add_fname_to_include_list(ff, 0, line);
187 ignore_label_errors = true;
195 case 'V': /* Volume name */
209 Pmsg0(0, _("No archive name specified\n"));
213 if (configfile == NULL) {
214 configfile = bstrdup(CONFIG_FILE);
217 parse_config(configfile);
219 if (ff->included_files_list == NULL) {
220 add_fname_to_include_list(ff, 0, "/");
223 for (i=0; i < argc; i++) {
225 bsr = parse_bsr(NULL, bsrName);
227 jcr = setup_jcr("bls", argv[i], bsr, VolumeName, 1); /* acquire for read */
231 jcr->ignore_label_errors = ignore_label_errors;
238 attr = new_attr(jcr);
240 * Assume that we have already read the volume label.
241 * If on second or subsequent volume, adjust buffer pointer
243 if (dev->VolHdr.PrevVolumeName[0] != 0) { /* second volume */
245 "Warning, this Volume is a continuation of Volume %s\n"),
246 dev->VolHdr.PrevVolumeName);
251 } else if (list_jobs) {
261 term_include_exclude_files(ff);
267 static void do_close(JCR *jcr)
269 release_device(jcr->dcr);
277 /* List just block information */
278 static void do_blocks(char *infname)
280 DEV_BLOCK *block = dcr->block;
281 char buf1[100], buf2[100];
283 if (!read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK)) {
284 Dmsg1(100, "!read_block(): ERR=%s\n", dev->bstrerror());
286 if (!mount_next_read_volume(dcr)) {
287 Jmsg(jcr, M_INFO, 0, _("Got EOM at file %u on device %s, Volume \"%s\"\n"),
288 dev->file, dev->print_name(), dcr->VolumeName);
291 /* Read and discard Volume label */
293 record = new_record();
294 read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK);
295 read_record_from_block(dcr, block, record);
296 get_session_record(dev, record, &sessrec);
298 Jmsg(jcr, M_INFO, 0, _("Mounted Volume \"%s\".\n"), dcr->VolumeName);
299 } else if (dev->at_eof()) {
300 Jmsg(jcr, M_INFO, 0, _("End of file %u on device %s, Volume \"%s\"\n"),
301 dev->file, dev->print_name(), dcr->VolumeName);
302 Dmsg0(20, "read_record got eof. try again\n");
304 } else if (dev->is_short_block()) {
305 Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
309 display_tape_error_status(jcr, dev);
313 if (!match_bsr_block(bsr, block)) {
314 Dmsg5(100, "reject Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
315 block->BlockNumber, block->block_len, block->BlockVer,
316 block->VolSessionId, block->VolSessionTime);
319 Dmsg5(100, "Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
320 block->BlockNumber, block->block_len, block->BlockVer,
321 block->VolSessionId, block->VolSessionTime);
323 read_record_from_block(dcr, block, rec);
324 Pmsg9(-1, _("File:blk=%u:%u blk_num=%u blen=%u First rec FI=%s SessId=%u SessTim=%u Strm=%s rlen=%d\n"),
325 dev->file, dev->block_num,
326 block->BlockNumber, block->block_len,
327 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId, rec->VolSessionTime,
328 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
330 } else if (verbose > 1) {
331 dump_block(block, "");
333 printf(_("Block: %d size=%d\n"), block->BlockNumber, block->block_len);
341 * We are only looking for labels or in particular Job Session records
343 static bool jobs_cb(DCR *dcr, DEV_RECORD *rec)
345 if (rec->FileIndex < 0) {
346 dump_label_record(dcr->dev, rec, verbose);
352 /* Do list job records */
353 static void do_jobs(char *infname)
355 read_records(dcr, jobs_cb, mount_next_read_volume);
358 /* Do an ls type listing of an archive */
359 static void do_ls(char *infname)
362 dump_volume_label(dev);
365 read_records(dcr, record_cb, mount_next_read_volume);
366 printf("%u files found.\n", num_files);
370 * Called here for each record from read_records()
372 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
374 if (rec->FileIndex < 0) {
375 get_session_record(dev, rec, &sessrec);
378 /* File Attributes stream */
379 if (rec->Stream == STREAM_UNIX_ATTRIBUTES ||
380 rec->Stream == STREAM_UNIX_ATTRIBUTES_EX) {
382 if (!unpack_attributes_record(jcr, rec->Stream, rec->data, attr)) {
384 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
390 if (attr->file_index != rec->FileIndex) {
391 Emsg2(forge_on?M_WARNING:M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"),
392 rec->FileIndex, attr->file_index);
395 attr->data_stream = decode_stat(attr->attr, &attr->statp, &attr->LinkFI);
396 build_attr_output_fnames(jcr, attr);
398 if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
400 Pmsg5(-1, _("FileIndex=%d VolSessionId=%d VolSessionTime=%d Stream=%d DataLen=%d\n"),
401 rec->FileIndex, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
403 print_ls_output(jcr, attr);
406 } else if (rec->Stream == STREAM_PLUGIN_NAME) {
407 Pmsg1(000, "Plugin name: %s\n", rec->data);
414 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
417 memset(sessrec, 0, sizeof(sessrec));
418 switch (rec->FileIndex) {
420 rtype = _("Fresh Volume Label");
423 rtype = _("Volume Label");
424 unser_volume_label(dev, rec);
427 rtype = _("Begin Job Session");
428 unser_session_label(sessrec, rec);
431 rtype = _("End Job Session");
435 rtype = _("End of Medium");
438 rtype = _("Unknown");
441 Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
442 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
444 Pmsg5(-1, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
445 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
450 /* Dummies to replace askdir.c */
451 bool dir_find_next_appendable_volume(DCR *dcr) { return 1;}
452 bool dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
453 bool dir_create_jobmedia_record(DCR *dcr) { return 1; }
454 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
455 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
456 bool dir_send_job_status(JCR *jcr) {return 1;}
457 int generate_job_event(JCR *jcr, const char *event) { return 1; }
460 bool dir_ask_sysop_to_mount_volume(DCR *dcr, int /*mode*/)
462 DEVICE *dev = dcr->dev;
463 fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
464 dcr->VolumeName, dev->print_name());
470 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
472 Dmsg0(100, "Fake dir_get_volume_info\n");
473 bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
474 dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
475 Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->VolCatInfo.VolCatName, dcr->VolCatInfo.VolCatParts);