]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bls.c
Create 2.2.10 by backporting trunk SD reservation changes
[bacula/bacula] / bacula / src / stored / bls.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2008 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 "       -e <file>       exclude list\n"
84 "       -i <file>       include list\n"
85 "       -j              list jobs\n"
86 "       -k              list blocks\n"
87 "    (no j or k option) list saved files\n"
88 "       -L              dump label\n"
89 "       -p              proceed inspite of errors\n"
90 "       -v              be verbose\n"
91 "       -V              specify Volume names (separated by |)\n"
92 "       -?              print this message\n\n"), 2000, VERSION, BDATE);
93    exit(1);
94 }
95
96
97 int main (int argc, char *argv[])
98 {
99    int i, ch;
100    FILE *fd;
101    char line[1000];
102    char *VolumeName= NULL;
103    char *bsrName = NULL;
104    bool ignore_label_errors = false;
105
106    setlocale(LC_ALL, "");
107    bindtextdomain("bacula", LOCALEDIR);
108    textdomain("bacula");
109    init_stack_dump();
110
111    working_directory = "/tmp";
112    my_name_is(argc, argv, "bls");
113    init_msg(NULL, NULL);              /* initialize message handler */
114
115    OSDependentInit();
116
117    ff = init_find_files();
118
119    while ((ch = getopt(argc, argv, "b:c:d:e:i:jkLpvV:?")) != -1) {
120       switch (ch) {
121       case 'b':
122          bsrName = optarg;
123          break;
124
125       case 'c':                    /* specify config file */
126          if (configfile != NULL) {
127             free(configfile);
128          }
129          configfile = bstrdup(optarg);
130          break;
131
132       case 'd':                    /* debug level */
133          if (*optarg == 't') {
134             dbg_timestamp = true;
135          } else {
136             debug_level = atoi(optarg);
137             if (debug_level <= 0) {
138                debug_level = 1;
139             }
140          }
141          break;
142
143       case 'e':                    /* exclude list */
144          if ((fd = fopen(optarg, "rb")) == NULL) {
145             berrno be;
146             Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
147                optarg, be.bstrerror());
148             exit(1);
149          }
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);
154          }
155          fclose(fd);
156          break;
157
158       case 'i':                    /* include list */
159          if ((fd = fopen(optarg, "rb")) == NULL) {
160             berrno be;
161             Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
162                optarg, be.bstrerror());
163             exit(1);
164          }
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);
169          }
170          fclose(fd);
171          break;
172
173       case 'j':
174          list_jobs = true;
175          break;
176
177       case 'k':
178          list_blocks = true;
179          break;
180
181       case 'L':
182          dump_label = true;
183          break;
184
185       case 'p':
186          ignore_label_errors = true;
187          forge_on = true;
188          break;
189
190       case 'v':
191          verbose++;
192          break;
193
194       case 'V':                    /* Volume name */
195          VolumeName = optarg;
196          break;
197
198       case '?':
199       default:
200          usage();
201
202       } /* end switch */
203    } /* end while */
204    argc -= optind;
205    argv += optind;
206
207    if (!argc) {
208       Pmsg0(0, _("No archive name specified\n"));
209       usage();
210    }
211
212    if (configfile == NULL) {
213       configfile = bstrdup(CONFIG_FILE);
214    }
215
216    parse_config(configfile);
217
218    if (ff->included_files_list == NULL) {
219       add_fname_to_include_list(ff, 0, "/");
220    }
221
222    for (i=0; i < argc; i++) {
223       if (bsrName) {
224          bsr = parse_bsr(NULL, bsrName);
225       }
226       jcr = setup_jcr("bls", argv[i], bsr, VolumeName, 1); /* acquire for read */
227       if (!jcr) {
228          exit(1);
229       }
230       jcr->ignore_label_errors = ignore_label_errors;
231       dev = jcr->dcr->dev;
232       if (!dev) {
233          exit(1);
234       }
235       dcr = jcr->dcr;
236       rec = new_record();
237       attr = new_attr(jcr);
238       /*
239        * Assume that we have already read the volume label.
240        * If on second or subsequent volume, adjust buffer pointer
241        */
242       if (dev->VolHdr.PrevVolumeName[0] != 0) { /* second volume */
243          Pmsg1(0, _("\n"
244                     "Warning, this Volume is a continuation of Volume %s\n"),
245                 dev->VolHdr.PrevVolumeName);
246       }
247
248       if (list_blocks) {
249          do_blocks(argv[i]);
250       } else if (list_jobs) {
251          do_jobs(argv[i]);
252       } else {
253          do_ls(argv[i]);
254       }
255       do_close(jcr);
256    }
257    if (bsr) {
258       free_bsr(bsr);
259    }
260    term_include_exclude_files(ff);
261    term_find_files(ff);
262    return 0;
263 }
264
265
266 static void do_close(JCR *jcr)
267 {
268    release_device(jcr->dcr);
269    free_attr(attr);
270    free_record(rec);
271    free_jcr(jcr);
272    dev->term();
273 }
274
275
276 /* List just block information */
277 static void do_blocks(char *infname)
278 {
279    DEV_BLOCK *block = dcr->block;
280    char buf1[100], buf2[100];
281    for ( ;; ) {
282       if (!read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK)) {
283          Dmsg1(100, "!read_block(): ERR=%s\n", dev->bstrerror());
284          if (dev->at_eot()) {
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);
288                break;
289             }
290             /* Read and discard Volume label */
291             DEV_RECORD *record;
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);
296             free_record(record);
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");
302             continue;
303          } else if (dev->is_short_block()) {
304             Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
305             continue;
306          } else {
307             /* I/O error */
308             display_tape_error_status(jcr, dev);
309             break;
310          }
311       }
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);
316          continue;
317       }
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);
321       if (verbose == 1) {
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);
328          rec->remainder = 0;
329       } else if (verbose > 1) {
330          dump_block(block, "");
331       } else {
332          printf(_("Block: %d size=%d\n"), block->BlockNumber, block->block_len);
333       }
334
335    }
336    return;
337 }
338
339 /*
340  * We are only looking for labels or in particular Job Session records
341  */
342 static bool jobs_cb(DCR *dcr, DEV_RECORD *rec)
343 {
344    if (rec->FileIndex < 0) {
345       dump_label_record(dcr->dev, rec, verbose);
346    }
347    rec->remainder = 0;
348    return true;
349 }
350
351 /* Do list job records */
352 static void do_jobs(char *infname)
353 {
354    read_records(dcr, jobs_cb, mount_next_read_volume);
355 }
356
357 /* Do an ls type listing of an archive */
358 static void do_ls(char *infname)
359 {
360    if (dump_label) {
361       dump_volume_label(dev);
362       return;
363    }
364    read_records(dcr, record_cb, mount_next_read_volume);
365    printf("%u files found.\n", num_files);
366 }
367
368 /*
369  * Called here for each record from read_records()
370  */
371 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
372 {
373    if (rec->FileIndex < 0) {
374       get_session_record(dev, rec, &sessrec);
375       return true;
376    }
377    /* File Attributes stream */
378    if (rec->Stream == STREAM_UNIX_ATTRIBUTES ||
379        rec->Stream == STREAM_UNIX_ATTRIBUTES_EX) {
380
381       if (!unpack_attributes_record(jcr, rec->Stream, rec->data, attr)) {
382          if (!forge_on) {
383             Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
384          }
385          num_files++;
386          return true;
387       }
388
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);
392       }
393
394       attr->data_stream = decode_stat(attr->attr, &attr->statp, &attr->LinkFI);
395       build_attr_output_fnames(jcr, attr);
396
397       if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
398          if (verbose) {
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);
401          }
402          print_ls_output(jcr, attr);
403          num_files++;
404       }
405    }
406    return true;
407 }
408
409
410 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
411 {
412    const char *rtype;
413    memset(sessrec, 0, sizeof(sessrec));
414    switch (rec->FileIndex) {
415    case PRE_LABEL:
416       rtype = _("Fresh Volume Label");
417       break;
418    case VOL_LABEL:
419       rtype = _("Volume Label");
420       unser_volume_label(dev, rec);
421       break;
422    case SOS_LABEL:
423       rtype = _("Begin Job Session");
424       unser_session_label(sessrec, rec);
425       break;
426    case EOS_LABEL:
427       rtype = _("End Job Session");
428       break;
429    case 0:
430    case EOM_LABEL:
431       rtype = _("End of Medium");
432       break;
433    default:
434       rtype = _("Unknown");
435       break;
436    }
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);
439    if (verbose) {
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);
442    }
443 }
444
445
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; }
454        
455
456 bool dir_ask_sysop_to_mount_volume(DCR *dcr, int /*mode*/)
457 {
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());
461    dev->close();
462    getchar();
463    return true;
464 }
465
466 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw  writing)
467 {
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);
472    return 1;
473 }