]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bls.c
kes Make explicit exception to GPL in LICENSE to permit linking
[bacula/bacula] / bacula / src / stored / bls.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2007 Free Software Foundation Europe e.V.
5
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
11    in the file LICENSE.
12
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.
17
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
21    02110-1301, USA.
22
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.
27 */
28 /*
29  *
30  *  Dumb program to do an "ls" of a Bacula 1.0 mortal file.
31  * 
32  *  Kern Sibbald, MM
33  *
34  *   Version $Id$
35  */
36
37 #include "bacula.h"
38 #include "stored.h"
39 #include "findlib/find.h"
40
41 /* Dummy functions */
42 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
43
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);
50
51 static DEVICE *dev;
52 static DCR *dcr;
53 static bool dump_label = false;
54 static bool list_blocks = false;
55 static bool list_jobs = false;
56 static DEV_RECORD *rec;
57 static JCR *jcr;
58 static SESSION_LABEL sessrec;
59 static uint32_t num_files = 0;
60 static ATTR *attr;
61
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;
68
69
70 static FF_PKT *ff;
71
72 static BSR *bsr = NULL;
73
74 static void usage()
75 {
76    fprintf(stderr, _(
77 PROG_COPYRIGHT
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"
86 "       -j              list jobs\n"
87 "       -k              list blocks\n"
88 "    (no j or k option) list saved files\n"
89 "       -L              dump label\n"
90 "       -p              proceed inspite of errors\n"
91 "       -v              be verbose\n"
92 "       -V              specify Volume names (separated by |)\n"
93 "       -?              print this message\n\n"), 2000, VERSION, BDATE);
94    exit(1);
95 }
96
97
98 int main (int argc, char *argv[])
99 {
100    int i, ch;
101    FILE *fd;
102    char line[1000];
103    char *VolumeName= NULL;
104    char *bsrName = NULL;
105    bool ignore_label_errors = false;
106
107    setlocale(LC_ALL, "");
108    bindtextdomain("bacula", LOCALEDIR);
109    textdomain("bacula");
110    init_stack_dump();
111
112    working_directory = "/tmp";
113    my_name_is(argc, argv, "bls");
114    init_msg(NULL, NULL);              /* initialize message handler */
115
116    OSDependentInit();
117
118    ff = init_find_files();
119
120    while ((ch = getopt(argc, argv, "b:c:d:e:i:jkLpvV:?")) != -1) {
121       switch (ch) {
122       case 'b':
123          bsrName = optarg;
124          break;
125
126       case 'c':                    /* specify config file */
127          if (configfile != NULL) {
128             free(configfile);
129          }
130          configfile = bstrdup(optarg);
131          break;
132
133       case 'd':                    /* debug level */
134          if (*optarg == 't') {
135             dbg_timestamp = true;
136          } else {
137             debug_level = atoi(optarg);
138             if (debug_level <= 0) {
139                debug_level = 1;
140             }
141          }
142          break;
143
144       case 'e':                    /* exclude list */
145          if ((fd = fopen(optarg, "rb")) == NULL) {
146             berrno be;
147             Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
148                optarg, be.bstrerror());
149             exit(1);
150          }
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);
155          }
156          fclose(fd);
157          break;
158
159       case 'i':                    /* include list */
160          if ((fd = fopen(optarg, "rb")) == NULL) {
161             berrno be;
162             Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
163                optarg, be.bstrerror());
164             exit(1);
165          }
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);
170          }
171          fclose(fd);
172          break;
173
174       case 'j':
175          list_jobs = true;
176          break;
177
178       case 'k':
179          list_blocks = true;
180          break;
181
182       case 'L':
183          dump_label = true;
184          break;
185
186       case 'p':
187          ignore_label_errors = true;
188          forge_on = true;
189          break;
190
191       case 'v':
192          verbose++;
193          break;
194
195       case 'V':                    /* Volume name */
196          VolumeName = optarg;
197          break;
198
199       case '?':
200       default:
201          usage();
202
203       } /* end switch */
204    } /* end while */
205    argc -= optind;
206    argv += optind;
207
208    if (!argc) {
209       Pmsg0(0, _("No archive name specified\n"));
210       usage();
211    }
212
213    if (configfile == NULL) {
214       configfile = bstrdup(CONFIG_FILE);
215    }
216
217    parse_config(configfile);
218
219    if (ff->included_files_list == NULL) {
220       add_fname_to_include_list(ff, 0, "/");
221    }
222
223    for (i=0; i < argc; i++) {
224       if (bsrName) {
225          bsr = parse_bsr(NULL, bsrName);
226       }
227       jcr = setup_jcr("bls", argv[i], bsr, VolumeName, 1); /* acquire for read */
228       if (!jcr) {
229          exit(1);
230       }
231       jcr->ignore_label_errors = ignore_label_errors;
232       dev = jcr->dcr->dev;
233       if (!dev) {
234          exit(1);
235       }
236       dcr = jcr->dcr;
237       rec = new_record();
238       attr = new_attr(jcr);
239       /*
240        * Assume that we have already read the volume label.
241        * If on second or subsequent volume, adjust buffer pointer
242        */
243       if (dev->VolHdr.PrevVolumeName[0] != 0) { /* second volume */
244          Pmsg1(0, _("\n"
245                     "Warning, this Volume is a continuation of Volume %s\n"),
246                 dev->VolHdr.PrevVolumeName);
247       }
248
249       if (list_blocks) {
250          do_blocks(argv[i]);
251       } else if (list_jobs) {
252          do_jobs(argv[i]);
253       } else {
254          do_ls(argv[i]);
255       }
256       do_close(jcr);
257    }
258    if (bsr) {
259       free_bsr(bsr);
260    }
261    term_include_exclude_files(ff);
262    term_find_files(ff);
263    return 0;
264 }
265
266
267 static void do_close(JCR *jcr)
268 {
269    release_device(jcr->dcr);
270    free_attr(attr);
271    free_record(rec);
272    free_jcr(jcr);
273    dev->term();
274 }
275
276
277 /* List just block information */
278 static void do_blocks(char *infname)
279 {
280    DEV_BLOCK *block = dcr->block;
281    char buf1[100], buf2[100];
282    for ( ;; ) {
283       if (!read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK)) {
284          Dmsg1(100, "!read_block(): ERR=%s\n", dev->bstrerror());
285          if (dev->at_eot()) {
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);
289                break;
290             }
291             /* Read and discard Volume label */
292             DEV_RECORD *record;
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);
297             free_record(record);
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");
303             continue;
304          } else if (dev->is_short_block()) {
305             Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
306             continue;
307          } else {
308             /* I/O error */
309             display_tape_error_status(jcr, dev);
310             break;
311          }
312       }
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);
317          continue;
318       }
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);
322       if (verbose == 1) {
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);
329          rec->remainder = 0;
330       } else if (verbose > 1) {
331          dump_block(block, "");
332       } else {
333          printf(_("Block: %d size=%d\n"), block->BlockNumber, block->block_len);
334       }
335
336    }
337    return;
338 }
339
340 /*
341  * We are only looking for labels or in particular Job Session records
342  */
343 static bool jobs_cb(DCR *dcr, DEV_RECORD *rec)
344 {
345    if (rec->FileIndex < 0) {
346       dump_label_record(dcr->dev, rec, verbose);
347    }
348    rec->remainder = 0;
349    return true;
350 }
351
352 /* Do list job records */
353 static void do_jobs(char *infname)
354 {
355    read_records(dcr, jobs_cb, mount_next_read_volume);
356 }
357
358 /* Do an ls type listing of an archive */
359 static void do_ls(char *infname)
360 {
361    if (dump_label) {
362       dump_volume_label(dev);
363       return;
364    }
365    read_records(dcr, record_cb, mount_next_read_volume);
366    printf("%u files found.\n", num_files);
367 }
368
369 /*
370  * Called here for each record from read_records()
371  */
372 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
373 {
374    if (rec->FileIndex < 0) {
375       get_session_record(dev, rec, &sessrec);
376       return true;
377    }
378    /* File Attributes stream */
379    if (rec->Stream == STREAM_UNIX_ATTRIBUTES ||
380        rec->Stream == STREAM_UNIX_ATTRIBUTES_EX) {
381
382       if (!unpack_attributes_record(jcr, rec->Stream, rec->data, attr)) {
383          if (!forge_on) {
384             Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
385          }
386          num_files++;
387          return true;
388       }
389
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);
393       }
394
395       attr->data_stream = decode_stat(attr->attr, &attr->statp, &attr->LinkFI);
396       build_attr_output_fnames(jcr, attr);
397
398       if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
399          if (verbose) {
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);
402          }
403          print_ls_output(jcr, attr);
404          num_files++;
405       }
406    }
407    return true;
408 }
409
410
411 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
412 {
413    const char *rtype;
414    memset(sessrec, 0, sizeof(sessrec));
415    switch (rec->FileIndex) {
416    case PRE_LABEL:
417       rtype = _("Fresh Volume Label");
418       break;
419    case VOL_LABEL:
420       rtype = _("Volume Label");
421       unser_volume_label(dev, rec);
422       break;
423    case SOS_LABEL:
424       rtype = _("Begin Job Session");
425       unser_session_label(sessrec, rec);
426       break;
427    case EOS_LABEL:
428       rtype = _("End Job Session");
429       break;
430    case 0:
431    case EOM_LABEL:
432       rtype = _("End of Medium");
433       break;
434    default:
435       rtype = _("Unknown");
436       break;
437    }
438    Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
439          rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
440    if (verbose) {
441       Pmsg5(-1, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
442             rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
443    }
444 }
445
446
447 /* Dummies to replace askdir.c */
448 bool    dir_find_next_appendable_volume(DCR *dcr) { return 1;}
449 bool    dir_update_volume_info(DCR *dcr, bool relabel) { return 1; }
450 bool    dir_create_jobmedia_record(DCR *dcr) { return 1; }
451 bool    dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
452 bool    dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
453 bool    dir_send_job_status(JCR *jcr) {return 1;}
454 int     generate_job_event(JCR *jcr, const char *event) { return 1; }
455        
456
457 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
458 {
459    DEVICE *dev = dcr->dev;
460    fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
461       dcr->VolumeName, dev->print_name());
462    dev->close();
463    getchar();
464    return true;
465 }
466
467 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw  writing)
468 {
469    Dmsg0(100, "Fake dir_get_volume_info\n");
470    bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
471    dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
472    Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->VolCatInfo.VolCatName, dcr->VolCatInfo.VolCatParts);
473    return 1;
474 }