]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bls.c
- Final tweaks to build Win32.
[bacula/bacula] / bacula / src / stored / bls.c
1 /*
2  *
3  *  Dumb program to do an "ls" of a Bacula 1.0 mortal file.
4  *
5  *   Version $Id$
6  */
7 /*
8    Copyright (C) 2000-2005 Kern Sibbald
9
10    This program is free software; you can redistribute it and/or
11    modify it under the terms of the GNU General Public License
12    version 2 as amended with additional clauses defined in the
13    file LICENSE in the main source directory.
14
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
18    the file LICENSE for additional details.
19
20  */
21
22 #include "bacula.h"
23 #include "stored.h"
24 #include "findlib/find.h"
25
26 #if defined(HAVE_CYGWIN) || defined(HAVE_WIN32)
27 int win32_client = 1;
28 #else
29 int win32_client = 0;
30 #endif
31
32 /* Dummy functions */
33 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
34
35 static void do_blocks(char *infname);
36 static void do_jobs(char *infname);
37 static void do_ls(char *fname);
38 static void do_close(JCR *jcr);
39 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec);
40 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
41
42 static DEVICE *dev;
43 static DCR *dcr;
44 static bool dump_label = false;
45 static bool list_blocks = false;
46 static bool list_jobs = false;
47 static DEV_RECORD *rec;
48 static DEV_BLOCK *block;
49 static JCR *jcr;
50 static SESSION_LABEL sessrec;
51 static uint32_t num_files = 0;
52 static ATTR *attr;
53
54 #define CONFIG_FILE "bacula-sd.conf"
55 char *configfile = NULL;
56 STORES *me = NULL;                    /* our Global resource */
57 bool forge_on = false;
58 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
59 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
60
61
62 static FF_PKT *ff;
63
64 static BSR *bsr = NULL;
65
66 static void usage()
67 {
68    fprintf(stderr, _(
69 "Copyright (C) 2000-2005 Kern Sibbald.\n"
70 "\nVersion: %s (%s)\n\n"
71 "Usage: bls [options] <device-name>\n"
72 "       -b <file>       specify a bootstrap file\n"
73 "       -c <file>       specify a config file\n"
74 "       -d <level>      specify debug level\n"
75 "       -e <file>       exclude list\n"
76 "       -i <file>       include list\n"
77 "       -j              list jobs\n"
78 "       -k              list blocks\n"
79 "    (no j or k option) list saved files\n"
80 "       -L              dump label\n"
81 "       -p              proceed inspite of errors\n"
82 "       -v              be verbose\n"
83 "       -V              specify Volume names (separated by |)\n"
84 "       -?              print this message\n\n"), VERSION, BDATE);
85    exit(1);
86 }
87
88
89 int main (int argc, char *argv[])
90 {
91    int i, ch;
92    FILE *fd;
93    char line[1000];
94    char *VolumeName= NULL;
95    char *bsrName = NULL;
96    bool ignore_label_errors = false;
97
98    setlocale(LC_ALL, "");
99    bindtextdomain("bacula", LOCALEDIR);
100    textdomain("bacula");
101
102    working_directory = "/tmp";
103    my_name_is(argc, argv, "bls");
104    init_msg(NULL, NULL);              /* initialize message handler */
105
106    ff = init_find_files();
107
108    while ((ch = getopt(argc, argv, "b:c:d:e:i:jkLpvV:?")) != -1) {
109       switch (ch) {
110       case 'b':
111          bsrName = optarg;
112          break;
113
114       case 'c':                    /* specify config file */
115          if (configfile != NULL) {
116             free(configfile);
117          }
118          configfile = bstrdup(optarg);
119          break;
120
121       case 'd':                    /* debug level */
122          debug_level = atoi(optarg);
123          if (debug_level <= 0)
124             debug_level = 1;
125          break;
126
127       case 'e':                    /* exclude list */
128          if ((fd = fopen(optarg, "r")) == NULL) {
129             Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
130                optarg, strerror(errno));
131             exit(1);
132          }
133          while (fgets(line, sizeof(line), fd) != NULL) {
134             strip_trailing_junk(line);
135             Dmsg1(100, "add_exclude %s\n", line);
136             add_fname_to_exclude_list(ff, line);
137          }
138          fclose(fd);
139          break;
140
141       case 'i':                    /* include list */
142          if ((fd = fopen(optarg, "r")) == NULL) {
143             Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
144                optarg, strerror(errno));
145             exit(1);
146          }
147          while (fgets(line, sizeof(line), fd) != NULL) {
148             strip_trailing_junk(line);
149             Dmsg1(100, "add_include %s\n", line);
150             add_fname_to_include_list(ff, 0, line);
151          }
152          fclose(fd);
153          break;
154
155       case 'j':
156          list_jobs = true;
157          break;
158
159       case 'k':
160          list_blocks = true;
161          break;
162
163       case 'L':
164          dump_label = true;
165          break;
166
167       case 'p':
168          ignore_label_errors = true;
169          forge_on = true;
170          break;
171
172       case 'v':
173          verbose++;
174          break;
175
176       case 'V':                    /* Volume name */
177          VolumeName = optarg;
178          break;
179
180       case '?':
181       default:
182          usage();
183
184       } /* end switch */
185    } /* end while */
186    argc -= optind;
187    argv += optind;
188
189    if (!argc) {
190       Pmsg0(0, _("No archive name specified\n"));
191       usage();
192    }
193
194    if (configfile == NULL) {
195       configfile = bstrdup(CONFIG_FILE);
196    }
197
198    parse_config(configfile);
199
200    if (ff->included_files_list == NULL) {
201       add_fname_to_include_list(ff, 0, "/");
202    }
203
204    for (i=0; i < argc; i++) {
205       if (bsrName) {
206          bsr = parse_bsr(NULL, bsrName);
207       }
208       jcr = setup_jcr("bls", argv[i], bsr, VolumeName, 1); /* acquire for read */
209       if (!jcr) {
210          exit(1);
211       }
212       jcr->ignore_label_errors = ignore_label_errors;
213       dev = jcr->dcr->dev;
214       if (!dev) {
215          exit(1);
216       }
217       dcr = jcr->dcr;
218       rec = new_record();
219       block = new_block(dev);
220       attr = new_attr();
221       /*
222        * Assume that we have already read the volume label.
223        * If on second or subsequent volume, adjust buffer pointer
224        */
225       if (dev->VolHdr.PrevVolumeName[0] != 0) { /* second volume */
226          Pmsg1(0, _("\n"
227                     "Warning, this Volume is a continuation of Volume %s\n"),
228                 dev->VolHdr.PrevVolumeName);
229       }
230
231       if (list_blocks) {
232          do_blocks(argv[i]);
233       } else if (list_jobs) {
234          do_jobs(argv[i]);
235       } else {
236          do_ls(argv[i]);
237       }
238       do_close(jcr);
239    }
240    if (bsr) {
241       free_bsr(bsr);
242    }
243    term_include_exclude_files(ff);
244    term_find_files(ff);
245    return 0;
246 }
247
248
249 static void do_close(JCR *jcr)
250 {
251    release_device(jcr->dcr);
252    free_attr(attr);
253    free_record(rec);
254    free_block(block);
255    free_jcr(jcr);
256    term_dev(dev);
257 }
258
259
260 /* List just block information */
261 static void do_blocks(char *infname)
262 {
263    for ( ;; ) {
264       if (!read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK)) {
265          Dmsg1(100, "!read_block(): ERR=%s\n", dev->strerror());
266          if (dev->at_eot()) {
267             if (!mount_next_read_volume(dcr)) {
268                Jmsg(jcr, M_INFO, 0, _("Got EOM at file %u on device %s, Volume \"%s\"\n"),
269                   dev->file, dev->print_name(), dcr->VolumeName);
270                break;
271             }
272             /* Read and discard Volume label */
273             DEV_RECORD *record;
274             record = new_record();
275             read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK);
276             read_record_from_block(block, record);
277             get_session_record(dev, record, &sessrec);
278             free_record(record);
279             Jmsg(jcr, M_INFO, 0, _("Mounted Volume \"%s\".\n"), dcr->VolumeName);
280          } else if (dev->at_eof()) {
281             Jmsg(jcr, M_INFO, 0, _("Got EOF at file %u on device %s, Volume \"%s\"\n"),
282                dev->file, dev->print_name(), dcr->VolumeName);
283             Dmsg0(20, "read_record got eof. try again\n");
284             continue;
285          } else if (dev->is_short_block()) {
286             Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
287             continue;
288          } else {
289             /* I/O error */
290             display_tape_error_status(jcr, dev);
291             break;
292          }
293       }
294       if (!match_bsr_block(bsr, block)) {
295          Dmsg5(100, "reject Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
296             block->BlockNumber, block->block_len, block->BlockVer,
297             block->VolSessionId, block->VolSessionTime);
298          continue;
299       }
300       Dmsg5(100, "Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
301         block->BlockNumber, block->block_len, block->BlockVer,
302         block->VolSessionId, block->VolSessionTime);
303       if (verbose == 1) {
304          read_record_from_block(block, rec);
305          Pmsg9(-1, _("File:blk=%u:%u blk_num=%u blen=%u First rec FI=%s SessId=%u SessTim=%u Strm=%s rlen=%d\n"),
306               dev->file, dev->block_num,
307               block->BlockNumber, block->block_len,
308               FI_to_ascii(rec->FileIndex), rec->VolSessionId, rec->VolSessionTime,
309               stream_to_ascii(rec->Stream, rec->FileIndex), rec->data_len);
310          rec->remainder = 0;
311       } else if (verbose > 1) {
312          dump_block(block, "");
313       } else {
314          printf(_("Block: %d size=%d\n"), block->BlockNumber, block->block_len);
315       }
316
317    }
318    return;
319 }
320
321 /*
322  * We are only looking for labels or in particular Job Session records
323  */
324 static bool jobs_cb(DCR *dcr, DEV_RECORD *rec)
325 {
326    if (rec->FileIndex < 0) {
327       dump_label_record(dcr->dev, rec, verbose);
328    }
329    rec->remainder = 0;
330    return true;
331 }
332
333 /* Do list job records */
334 static void do_jobs(char *infname)
335 {
336    read_records(dcr, jobs_cb, mount_next_read_volume);
337 }
338
339 /* Do an ls type listing of an archive */
340 static void do_ls(char *infname)
341 {
342    if (dump_label) {
343       dump_volume_label(dev);
344       return;
345    }
346    read_records(dcr, record_cb, mount_next_read_volume);
347    printf("%u files found.\n", num_files);
348 }
349
350 /*
351  * Called here for each record from read_records()
352  */
353 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
354 {
355    if (rec->FileIndex < 0) {
356       get_session_record(dev, rec, &sessrec);
357       return true;
358    }
359    /* File Attributes stream */
360    if (rec->Stream == STREAM_UNIX_ATTRIBUTES ||
361        rec->Stream == STREAM_UNIX_ATTRIBUTES_EX) {
362
363       if (!unpack_attributes_record(jcr, rec->Stream, rec->data, attr)) {
364          if (!forge_on) {
365             Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
366          }
367          num_files++;
368          return true;
369       }
370
371       if (attr->file_index != rec->FileIndex) {
372          Emsg2(forge_on?M_WARNING:M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"),
373                rec->FileIndex, attr->file_index);
374       }
375
376       attr->data_stream = decode_stat(attr->attr, &attr->statp, &attr->LinkFI);
377       build_attr_output_fnames(jcr, attr);
378
379       if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
380          if (verbose) {
381             Pmsg5(-1, _("FileIndex=%d VolSessionId=%d VolSessionTime=%d Stream=%d DataLen=%d\n"),
382                   rec->FileIndex, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
383          }
384          print_ls_output(jcr, attr);
385          num_files++;
386       }
387    }
388    return true;
389 }
390
391
392 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
393 {
394    const char *rtype;
395    memset(sessrec, 0, sizeof(sessrec));
396    switch (rec->FileIndex) {
397    case PRE_LABEL:
398       rtype = _("Fresh Volume Label");
399       break;
400    case VOL_LABEL:
401       rtype = _("Volume Label");
402       unser_volume_label(dev, rec);
403       break;
404    case SOS_LABEL:
405       rtype = _("Begin Job Session");
406       unser_session_label(sessrec, rec);
407       break;
408    case EOS_LABEL:
409       rtype = _("End Job Session");
410       break;
411    case EOM_LABEL:
412       rtype = _("End of Medium");
413       break;
414    default:
415       rtype = _("Unknown");
416       break;
417    }
418    Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
419          rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
420    if (verbose) {
421       Pmsg5(-1, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
422             rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
423    }
424 }
425
426
427 /* Dummies to replace askdir.c */
428 bool    dir_get_volume_info(DCR *dcr, enum get_vol_info_rw  writing) { return 1;}
429 bool    dir_find_next_appendable_volume(DCR *dcr) { return 1;}
430 bool    dir_update_volume_info(DCR *dcr, bool relabel) { return 1; }
431 bool    dir_create_jobmedia_record(DCR *dcr) { return 1; }
432 bool    dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
433 bool    dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
434 bool    dir_send_job_status(JCR *jcr) {return 1;}
435 int     generate_job_event(JCR *jcr, const char *event) { return 1; }
436 VOLRES *new_volume(DCR *dcr, const char *VolumeName) { return NULL; }
437 bool    free_volume(DEVICE *dev) { return true; }
438 void    free_unused_volume(DCR *dcr) { }
439        
440
441 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
442 {
443    DEVICE *dev = dcr->dev;
444    fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
445       dcr->VolumeName, dev->print_name());
446    getchar();
447    return true;
448 }