]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bls.c
Eliminate more strerror() and replace with bstrerror().
[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 <level>      specify debug level\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          debug_level = atoi(optarg);
134          if (debug_level <= 0)
135             debug_level = 1;
136          break;
137
138       case 'e':                    /* exclude list */
139          if ((fd = fopen(optarg, "rb")) == NULL) {
140             berrno be;
141             Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
142                optarg, be.bstrerror());
143             exit(1);
144          }
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);
149          }
150          fclose(fd);
151          break;
152
153       case 'i':                    /* include list */
154          if ((fd = fopen(optarg, "rb")) == NULL) {
155             berrno be;
156             Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
157                optarg, be.bstrerror());
158             exit(1);
159          }
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);
164          }
165          fclose(fd);
166          break;
167
168       case 'j':
169          list_jobs = true;
170          break;
171
172       case 'k':
173          list_blocks = true;
174          break;
175
176       case 'L':
177          dump_label = true;
178          break;
179
180       case 'p':
181          ignore_label_errors = true;
182          forge_on = true;
183          break;
184
185       case 'v':
186          verbose++;
187          break;
188
189       case 'V':                    /* Volume name */
190          VolumeName = optarg;
191          break;
192
193       case '?':
194       default:
195          usage();
196
197       } /* end switch */
198    } /* end while */
199    argc -= optind;
200    argv += optind;
201
202    if (!argc) {
203       Pmsg0(0, _("No archive name specified\n"));
204       usage();
205    }
206
207    if (configfile == NULL) {
208       configfile = bstrdup(CONFIG_FILE);
209    }
210
211    parse_config(configfile);
212
213    if (ff->included_files_list == NULL) {
214       add_fname_to_include_list(ff, 0, "/");
215    }
216
217    for (i=0; i < argc; i++) {
218       if (bsrName) {
219          bsr = parse_bsr(NULL, bsrName);
220       }
221       jcr = setup_jcr("bls", argv[i], bsr, VolumeName, 1); /* acquire for read */
222       if (!jcr) {
223          exit(1);
224       }
225       jcr->ignore_label_errors = ignore_label_errors;
226       dev = jcr->dcr->dev;
227       if (!dev) {
228          exit(1);
229       }
230       dcr = jcr->dcr;
231       rec = new_record();
232       attr = new_attr(jcr);
233       /*
234        * Assume that we have already read the volume label.
235        * If on second or subsequent volume, adjust buffer pointer
236        */
237       if (dev->VolHdr.PrevVolumeName[0] != 0) { /* second volume */
238          Pmsg1(0, _("\n"
239                     "Warning, this Volume is a continuation of Volume %s\n"),
240                 dev->VolHdr.PrevVolumeName);
241       }
242
243       if (list_blocks) {
244          do_blocks(argv[i]);
245       } else if (list_jobs) {
246          do_jobs(argv[i]);
247       } else {
248          do_ls(argv[i]);
249       }
250       do_close(jcr);
251    }
252    if (bsr) {
253       free_bsr(bsr);
254    }
255    term_include_exclude_files(ff);
256    term_find_files(ff);
257    return 0;
258 }
259
260
261 static void do_close(JCR *jcr)
262 {
263    release_device(jcr->dcr);
264    free_attr(attr);
265    free_record(rec);
266    free_jcr(jcr);
267    dev->term();
268 }
269
270
271 /* List just block information */
272 static void do_blocks(char *infname)
273 {
274    DEV_BLOCK *block = dcr->block;
275    char buf1[100], buf2[100];
276    for ( ;; ) {
277       if (!read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK)) {
278          Dmsg1(100, "!read_block(): ERR=%s\n", dev->bstrerror());
279          if (dev->at_eot()) {
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);
283                break;
284             }
285             /* Read and discard Volume label */
286             DEV_RECORD *record;
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);
291             free_record(record);
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");
297             continue;
298          } else if (dev->is_short_block()) {
299             Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
300             continue;
301          } else {
302             /* I/O error */
303             display_tape_error_status(jcr, dev);
304             break;
305          }
306       }
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);
311          continue;
312       }
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);
316       if (verbose == 1) {
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);
323          rec->remainder = 0;
324       } else if (verbose > 1) {
325          dump_block(block, "");
326       } else {
327          printf(_("Block: %d size=%d\n"), block->BlockNumber, block->block_len);
328       }
329
330    }
331    return;
332 }
333
334 /*
335  * We are only looking for labels or in particular Job Session records
336  */
337 static bool jobs_cb(DCR *dcr, DEV_RECORD *rec)
338 {
339    if (rec->FileIndex < 0) {
340       dump_label_record(dcr->dev, rec, verbose);
341    }
342    rec->remainder = 0;
343    return true;
344 }
345
346 /* Do list job records */
347 static void do_jobs(char *infname)
348 {
349    read_records(dcr, jobs_cb, mount_next_read_volume);
350 }
351
352 /* Do an ls type listing of an archive */
353 static void do_ls(char *infname)
354 {
355    if (dump_label) {
356       dump_volume_label(dev);
357       return;
358    }
359    read_records(dcr, record_cb, mount_next_read_volume);
360    printf("%u files found.\n", num_files);
361 }
362
363 /*
364  * Called here for each record from read_records()
365  */
366 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
367 {
368    if (rec->FileIndex < 0) {
369       get_session_record(dev, rec, &sessrec);
370       return true;
371    }
372    /* File Attributes stream */
373    if (rec->Stream == STREAM_UNIX_ATTRIBUTES ||
374        rec->Stream == STREAM_UNIX_ATTRIBUTES_EX) {
375
376       if (!unpack_attributes_record(jcr, rec->Stream, rec->data, attr)) {
377          if (!forge_on) {
378             Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
379          }
380          num_files++;
381          return true;
382       }
383
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);
387       }
388
389       attr->data_stream = decode_stat(attr->attr, &attr->statp, &attr->LinkFI);
390       build_attr_output_fnames(jcr, attr);
391
392       if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
393          if (verbose) {
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);
396          }
397          print_ls_output(jcr, attr);
398          num_files++;
399       }
400    }
401    return true;
402 }
403
404
405 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
406 {
407    const char *rtype;
408    memset(sessrec, 0, sizeof(sessrec));
409    switch (rec->FileIndex) {
410    case PRE_LABEL:
411       rtype = _("Fresh Volume Label");
412       break;
413    case VOL_LABEL:
414       rtype = _("Volume Label");
415       unser_volume_label(dev, rec);
416       break;
417    case SOS_LABEL:
418       rtype = _("Begin Job Session");
419       unser_session_label(sessrec, rec);
420       break;
421    case EOS_LABEL:
422       rtype = _("End Job Session");
423       break;
424    case 0:
425    case EOM_LABEL:
426       rtype = _("End of Medium");
427       break;
428    default:
429       rtype = _("Unknown");
430       break;
431    }
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);
434    if (verbose) {
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);
437    }
438 }
439
440
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; }
449        
450
451 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
452 {
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());
456    dev->close();
457    getchar();
458    return true;
459 }
460
461 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw  writing)
462 {
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);
467    return 1;
468 }