]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bls.c
20Feb06
[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-2006 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    dev->term();
257 }
258
259
260 /* List just block information */
261 static void do_blocks(char *infname)
262 {
263    char buf1[100], buf2[100];
264    for ( ;; ) {
265       if (!read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK)) {
266          Dmsg1(100, "!read_block(): ERR=%s\n", dev->strerror());
267          if (dev->at_eot()) {
268             if (!mount_next_read_volume(dcr)) {
269                Jmsg(jcr, M_INFO, 0, _("Got EOM at file %u on device %s, Volume \"%s\"\n"),
270                   dev->file, dev->print_name(), dcr->VolumeName);
271                break;
272             }
273             /* Read and discard Volume label */
274             DEV_RECORD *record;
275             record = new_record();
276             read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK);
277             read_record_from_block(block, record);
278             get_session_record(dev, record, &sessrec);
279             free_record(record);
280             Jmsg(jcr, M_INFO, 0, _("Mounted Volume \"%s\".\n"), dcr->VolumeName);
281          } else if (dev->at_eof()) {
282             Jmsg(jcr, M_INFO, 0, _("Got EOF at file %u on device %s, Volume \"%s\"\n"),
283                dev->file, dev->print_name(), dcr->VolumeName);
284             Dmsg0(20, "read_record got eof. try again\n");
285             continue;
286          } else if (dev->is_short_block()) {
287             Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
288             continue;
289          } else {
290             /* I/O error */
291             display_tape_error_status(jcr, dev);
292             break;
293          }
294       }
295       if (!match_bsr_block(bsr, block)) {
296          Dmsg5(100, "reject Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
297             block->BlockNumber, block->block_len, block->BlockVer,
298             block->VolSessionId, block->VolSessionTime);
299          continue;
300       }
301       Dmsg5(100, "Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
302         block->BlockNumber, block->block_len, block->BlockVer,
303         block->VolSessionId, block->VolSessionTime);
304       if (verbose == 1) {
305          read_record_from_block(block, rec);
306          Pmsg9(-1, _("File:blk=%u:%u blk_num=%u blen=%u First rec FI=%s SessId=%u SessTim=%u Strm=%s rlen=%d\n"),
307               dev->file, dev->block_num,
308               block->BlockNumber, block->block_len,
309               FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId, rec->VolSessionTime,
310               stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
311          rec->remainder = 0;
312       } else if (verbose > 1) {
313          dump_block(block, "");
314       } else {
315          printf(_("Block: %d size=%d\n"), block->BlockNumber, block->block_len);
316       }
317
318    }
319    return;
320 }
321
322 /*
323  * We are only looking for labels or in particular Job Session records
324  */
325 static bool jobs_cb(DCR *dcr, DEV_RECORD *rec)
326 {
327    if (rec->FileIndex < 0) {
328       dump_label_record(dcr->dev, rec, verbose);
329    }
330    rec->remainder = 0;
331    return true;
332 }
333
334 /* Do list job records */
335 static void do_jobs(char *infname)
336 {
337    read_records(dcr, jobs_cb, mount_next_read_volume);
338 }
339
340 /* Do an ls type listing of an archive */
341 static void do_ls(char *infname)
342 {
343    if (dump_label) {
344       dump_volume_label(dev);
345       return;
346    }
347    read_records(dcr, record_cb, mount_next_read_volume);
348    printf("%u files found.\n", num_files);
349 }
350
351 /*
352  * Called here for each record from read_records()
353  */
354 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
355 {
356    if (rec->FileIndex < 0) {
357       get_session_record(dev, rec, &sessrec);
358       return true;
359    }
360    /* File Attributes stream */
361    if (rec->Stream == STREAM_UNIX_ATTRIBUTES ||
362        rec->Stream == STREAM_UNIX_ATTRIBUTES_EX) {
363
364       if (!unpack_attributes_record(jcr, rec->Stream, rec->data, attr)) {
365          if (!forge_on) {
366             Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
367          }
368          num_files++;
369          return true;
370       }
371
372       if (attr->file_index != rec->FileIndex) {
373          Emsg2(forge_on?M_WARNING:M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"),
374                rec->FileIndex, attr->file_index);
375       }
376
377       attr->data_stream = decode_stat(attr->attr, &attr->statp, &attr->LinkFI);
378       build_attr_output_fnames(jcr, attr);
379
380       if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
381          if (verbose) {
382             Pmsg5(-1, _("FileIndex=%d VolSessionId=%d VolSessionTime=%d Stream=%d DataLen=%d\n"),
383                   rec->FileIndex, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
384          }
385          print_ls_output(jcr, attr);
386          num_files++;
387       }
388    }
389    return true;
390 }
391
392
393 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
394 {
395    const char *rtype;
396    memset(sessrec, 0, sizeof(sessrec));
397    switch (rec->FileIndex) {
398    case PRE_LABEL:
399       rtype = _("Fresh Volume Label");
400       break;
401    case VOL_LABEL:
402       rtype = _("Volume Label");
403       unser_volume_label(dev, rec);
404       break;
405    case SOS_LABEL:
406       rtype = _("Begin Job Session");
407       unser_session_label(sessrec, rec);
408       break;
409    case EOS_LABEL:
410       rtype = _("End Job Session");
411       break;
412    case 0:
413    case EOM_LABEL:
414       rtype = _("End of Medium");
415       break;
416    default:
417       rtype = _("Unknown");
418       break;
419    }
420    Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
421          rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
422    if (verbose) {
423       Pmsg5(-1, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
424             rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
425    }
426 }
427
428
429 /* Dummies to replace askdir.c */
430 bool    dir_get_volume_info(DCR *dcr, enum get_vol_info_rw  writing) { return 1;}
431 bool    dir_find_next_appendable_volume(DCR *dcr) { return 1;}
432 bool    dir_update_volume_info(DCR *dcr, bool relabel) { return 1; }
433 bool    dir_create_jobmedia_record(DCR *dcr) { return 1; }
434 bool    dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
435 bool    dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
436 bool    dir_send_job_status(JCR *jcr) {return 1;}
437 int     generate_job_event(JCR *jcr, const char *event) { return 1; }
438        
439
440 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
441 {
442    DEVICE *dev = dcr->dev;
443    fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
444       dcr->VolumeName, dev->print_name());
445    getchar();
446    return true;
447 }