]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bls.c
kes Implement code that should properly set that a job was migrated,
[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  *  Kern Sibbald, MM
6  *
7  *   Version $Id$
8  */
9 /*
10    Bacula® - The Network Backup Solution
11
12    Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
13
14    The main author of Bacula is Kern Sibbald, with contributions from
15    many others, a complete list can be found in the file AUTHORS.
16    This program is Free Software; you can redistribute it and/or
17    modify it under the terms of version two of the GNU General Public
18    License as published by the Free Software Foundation plus additions
19    that are listed in the file LICENSE.
20
21    This program is distributed in the hope that it will be useful, but
22    WITHOUT ANY WARRANTY; without even the implied warranty of
23    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24    General Public License for more details.
25
26    You should have received a copy of the GNU General Public License
27    along with this program; if not, write to the Free Software
28    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
29    02110-1301, USA.
30
31    Bacula® is a registered trademark of John Walker.
32    The licensor of Bacula is the Free Software Foundation Europe
33    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
34    Switzerland, email:ftf@fsfeurope.org.
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             Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
141                optarg, strerror(errno));
142             exit(1);
143          }
144          while (fgets(line, sizeof(line), fd) != NULL) {
145             strip_trailing_junk(line);
146             Dmsg1(100, "add_exclude %s\n", line);
147             add_fname_to_exclude_list(ff, line);
148          }
149          fclose(fd);
150          break;
151
152       case 'i':                    /* include list */
153          if ((fd = fopen(optarg, "rb")) == NULL) {
154             Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
155                optarg, strerror(errno));
156             exit(1);
157          }
158          while (fgets(line, sizeof(line), fd) != NULL) {
159             strip_trailing_junk(line);
160             Dmsg1(100, "add_include %s\n", line);
161             add_fname_to_include_list(ff, 0, line);
162          }
163          fclose(fd);
164          break;
165
166       case 'j':
167          list_jobs = true;
168          break;
169
170       case 'k':
171          list_blocks = true;
172          break;
173
174       case 'L':
175          dump_label = true;
176          break;
177
178       case 'p':
179          ignore_label_errors = true;
180          forge_on = true;
181          break;
182
183       case 'v':
184          verbose++;
185          break;
186
187       case 'V':                    /* Volume name */
188          VolumeName = optarg;
189          break;
190
191       case '?':
192       default:
193          usage();
194
195       } /* end switch */
196    } /* end while */
197    argc -= optind;
198    argv += optind;
199
200    if (!argc) {
201       Pmsg0(0, _("No archive name specified\n"));
202       usage();
203    }
204
205    if (configfile == NULL) {
206       configfile = bstrdup(CONFIG_FILE);
207    }
208
209    parse_config(configfile);
210
211    if (ff->included_files_list == NULL) {
212       add_fname_to_include_list(ff, 0, "/");
213    }
214
215    for (i=0; i < argc; i++) {
216       if (bsrName) {
217          bsr = parse_bsr(NULL, bsrName);
218       }
219       jcr = setup_jcr("bls", argv[i], bsr, VolumeName, 1); /* acquire for read */
220       if (!jcr) {
221          exit(1);
222       }
223       jcr->ignore_label_errors = ignore_label_errors;
224       dev = jcr->dcr->dev;
225       if (!dev) {
226          exit(1);
227       }
228       dcr = jcr->dcr;
229       rec = new_record();
230       attr = new_attr();
231       /*
232        * Assume that we have already read the volume label.
233        * If on second or subsequent volume, adjust buffer pointer
234        */
235       if (dev->VolHdr.PrevVolumeName[0] != 0) { /* second volume */
236          Pmsg1(0, _("\n"
237                     "Warning, this Volume is a continuation of Volume %s\n"),
238                 dev->VolHdr.PrevVolumeName);
239       }
240
241       if (list_blocks) {
242          do_blocks(argv[i]);
243       } else if (list_jobs) {
244          do_jobs(argv[i]);
245       } else {
246          do_ls(argv[i]);
247       }
248       do_close(jcr);
249    }
250    if (bsr) {
251       free_bsr(bsr);
252    }
253    term_include_exclude_files(ff);
254    term_find_files(ff);
255    return 0;
256 }
257
258
259 static void do_close(JCR *jcr)
260 {
261    release_device(jcr->dcr);
262    free_attr(attr);
263    free_record(rec);
264    free_jcr(jcr);
265    dev->term();
266 }
267
268
269 /* List just block information */
270 static void do_blocks(char *infname)
271 {
272    DEV_BLOCK *block = dcr->block;
273    char buf1[100], buf2[100];
274    for ( ;; ) {
275       if (!read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK)) {
276          Dmsg1(100, "!read_block(): ERR=%s\n", dev->strerror());
277          if (dev->at_eot()) {
278             if (!mount_next_read_volume(dcr)) {
279                Jmsg(jcr, M_INFO, 0, _("Got EOM at file %u on device %s, Volume \"%s\"\n"),
280                   dev->file, dev->print_name(), dcr->VolumeName);
281                break;
282             }
283             /* Read and discard Volume label */
284             DEV_RECORD *record;
285             record = new_record();
286             read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK);
287             read_record_from_block(dcr, block, record);
288             get_session_record(dev, record, &sessrec);
289             free_record(record);
290             Jmsg(jcr, M_INFO, 0, _("Mounted Volume \"%s\".\n"), dcr->VolumeName);
291          } else if (dev->at_eof()) {
292             Jmsg(jcr, M_INFO, 0, _("End of file %u on device %s, Volume \"%s\"\n"),
293                dev->file, dev->print_name(), dcr->VolumeName);
294             Dmsg0(20, "read_record got eof. try again\n");
295             continue;
296          } else if (dev->is_short_block()) {
297             Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
298             continue;
299          } else {
300             /* I/O error */
301             display_tape_error_status(jcr, dev);
302             break;
303          }
304       }
305       if (!match_bsr_block(bsr, block)) {
306          Dmsg5(100, "reject Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
307             block->BlockNumber, block->block_len, block->BlockVer,
308             block->VolSessionId, block->VolSessionTime);
309          continue;
310       }
311       Dmsg5(100, "Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
312         block->BlockNumber, block->block_len, block->BlockVer,
313         block->VolSessionId, block->VolSessionTime);
314       if (verbose == 1) {
315          read_record_from_block(dcr, block, rec);
316          Pmsg9(-1, _("File:blk=%u:%u blk_num=%u blen=%u First rec FI=%s SessId=%u SessTim=%u Strm=%s rlen=%d\n"),
317               dev->file, dev->block_num,
318               block->BlockNumber, block->block_len,
319               FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId, rec->VolSessionTime,
320               stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
321          rec->remainder = 0;
322       } else if (verbose > 1) {
323          dump_block(block, "");
324       } else {
325          printf(_("Block: %d size=%d\n"), block->BlockNumber, block->block_len);
326       }
327
328    }
329    return;
330 }
331
332 /*
333  * We are only looking for labels or in particular Job Session records
334  */
335 static bool jobs_cb(DCR *dcr, DEV_RECORD *rec)
336 {
337    if (rec->FileIndex < 0) {
338       dump_label_record(dcr->dev, rec, verbose);
339    }
340    rec->remainder = 0;
341    return true;
342 }
343
344 /* Do list job records */
345 static void do_jobs(char *infname)
346 {
347    read_records(dcr, jobs_cb, mount_next_read_volume);
348 }
349
350 /* Do an ls type listing of an archive */
351 static void do_ls(char *infname)
352 {
353    if (dump_label) {
354       dump_volume_label(dev);
355       return;
356    }
357    read_records(dcr, record_cb, mount_next_read_volume);
358    printf("%u files found.\n", num_files);
359 }
360
361 /*
362  * Called here for each record from read_records()
363  */
364 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
365 {
366    if (rec->FileIndex < 0) {
367       get_session_record(dev, rec, &sessrec);
368       return true;
369    }
370    /* File Attributes stream */
371    if (rec->Stream == STREAM_UNIX_ATTRIBUTES ||
372        rec->Stream == STREAM_UNIX_ATTRIBUTES_EX) {
373
374       if (!unpack_attributes_record(jcr, rec->Stream, rec->data, attr)) {
375          if (!forge_on) {
376             Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
377          }
378          num_files++;
379          return true;
380       }
381
382       if (attr->file_index != rec->FileIndex) {
383          Emsg2(forge_on?M_WARNING:M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"),
384                rec->FileIndex, attr->file_index);
385       }
386
387       attr->data_stream = decode_stat(attr->attr, &attr->statp, &attr->LinkFI);
388       build_attr_output_fnames(jcr, attr);
389
390       if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
391          if (verbose) {
392             Pmsg5(-1, _("FileIndex=%d VolSessionId=%d VolSessionTime=%d Stream=%d DataLen=%d\n"),
393                   rec->FileIndex, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
394          }
395          print_ls_output(jcr, attr);
396          num_files++;
397       }
398    }
399    return true;
400 }
401
402
403 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
404 {
405    const char *rtype;
406    memset(sessrec, 0, sizeof(sessrec));
407    switch (rec->FileIndex) {
408    case PRE_LABEL:
409       rtype = _("Fresh Volume Label");
410       break;
411    case VOL_LABEL:
412       rtype = _("Volume Label");
413       unser_volume_label(dev, rec);
414       break;
415    case SOS_LABEL:
416       rtype = _("Begin Job Session");
417       unser_session_label(sessrec, rec);
418       break;
419    case EOS_LABEL:
420       rtype = _("End Job Session");
421       break;
422    case 0:
423    case EOM_LABEL:
424       rtype = _("End of Medium");
425       break;
426    default:
427       rtype = _("Unknown");
428       break;
429    }
430    Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
431          rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
432    if (verbose) {
433       Pmsg5(-1, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
434             rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
435    }
436 }
437
438
439 /* Dummies to replace askdir.c */
440 bool    dir_find_next_appendable_volume(DCR *dcr) { return 1;}
441 bool    dir_update_volume_info(DCR *dcr, bool relabel) { return 1; }
442 bool    dir_create_jobmedia_record(DCR *dcr) { return 1; }
443 bool    dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
444 bool    dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
445 bool    dir_send_job_status(JCR *jcr) {return 1;}
446 int     generate_job_event(JCR *jcr, const char *event) { return 1; }
447        
448
449 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
450 {
451    DEVICE *dev = dcr->dev;
452    fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
453       dcr->VolumeName, dev->print_name());
454    dev->close();
455    getchar();
456    return true;
457 }
458
459 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw  writing)
460 {
461    Dmsg0(100, "Fake dir_get_volume_info\n");
462    bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
463    dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
464    Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->VolCatInfo.VolCatName, dcr->VolCatInfo.VolCatParts);
465    return 1;
466 }