]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bls.c
fc5eb8b8e1df7c2be7b9bf201c72351c6b42ba0e
[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, 2001, 2002 Kern Sibbald and John Walker
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 as
12    published by the Free Software Foundation; either version 2 of
13    the License, or (at your option) any later version.
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 GNU
18    General Public License for more details.
19
20    You should have received a copy of the GNU General Public
21    License along with this program; if not, write to the Free
22    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
23    MA 02111-1307, USA.
24
25  */
26
27 #include "bacula.h"
28 #include "stored.h"
29 #include "findlib/find.h"
30
31 static void do_blocks(char *infname);
32 static void do_jobs(char *infname);
33 static void do_ls(char *fname);
34 static void do_close();
35 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec);
36
37 static DEVICE *dev;
38 static int default_tape = FALSE;
39 static int dump_label = FALSE;
40 static int list_blocks = FALSE;
41 static int list_jobs = FALSE;
42 static int verbose = 0;
43 static DEV_RECORD *rec;
44 static DEV_BLOCK *block;
45 static JCR *jcr;
46 static SESSION_LABEL sessrec;
47
48
49 extern char BaculaId[];
50
51 static FF_PKT ff;
52
53 static BSR *bsr = NULL;
54
55 static void usage()
56 {
57    fprintf(stderr,
58 "\nVersion: " VERSION " (" DATE ")\n\n"
59 "Usage: bls [-d debug_level] <physical-device-name>\n"
60 "       -b <file>       specify a bootstrap file\n"
61 "       -e <file>       exclude list\n"
62 "       -i <file>       include list\n"
63 "       -j              list jobs\n"
64 "       -k              list blocks\n"
65 "       -L              list tape label\n"
66 "    (none of above)    list saved files\n"
67 "       -t              use default tape device\n"
68 "       -v              be verbose\n"
69 "       -?              print this message\n\n");
70    exit(1);
71 }
72
73 static char *rec_state_to_str(DEV_RECORD *rec)
74 {
75    static char buf[200]; 
76    buf[0] = 0;
77    if (rec->state & REC_NO_HEADER) {
78       strcat(buf, "Nohdr,");
79    }
80    if (is_partial_record(rec)) {
81       strcat(buf, "partial,");
82    }
83    if (rec->state & REC_BLOCK_EMPTY) {
84       strcat(buf, "empty,");
85    }
86    if (rec->state & REC_NO_MATCH) {
87       strcat(buf, "Nomatch,");
88    }
89    if (rec->state & REC_CONTINUATION) {
90       strcat(buf, "cont,");
91    }
92    if (buf[0]) {
93       buf[strlen(buf)-1] = 0;
94    }
95    return buf;
96 }
97
98
99
100 int main (int argc, char *argv[])
101 {
102    int i, ch;
103    FILE *fd;
104    char line[1000];
105
106    my_name_is(argc, argv, "bls");
107    init_msg(NULL, NULL);              /* initialize message handler */
108
109    memset(&ff, 0, sizeof(ff));
110    init_include_exclude_files(&ff);
111
112    while ((ch = getopt(argc, argv, "b:d:e:i:jkLtv?")) != -1) {
113       switch (ch) {
114          case 'b':
115             bsr = parse_bsr(NULL, optarg);
116             break;
117
118          case 'd':                    /* debug level */
119             debug_level = atoi(optarg);
120             if (debug_level <= 0)
121                debug_level = 1; 
122             break;
123
124          case 'e':                    /* exclude list */
125             if ((fd = fopen(optarg, "r")) == NULL) {
126                Pmsg2(0, "Could not open exclude file: %s, ERR=%s\n",
127                   optarg, strerror(errno));
128                exit(1);
129             }
130             while (fgets(line, sizeof(line), fd) != NULL) {
131                strip_trailing_junk(line);
132                Dmsg1(100, "add_exclude %s\n", line);
133                add_fname_to_exclude_list(&ff, line);
134             }
135             fclose(fd);
136             break;
137
138          case 'i':                    /* include list */
139             if ((fd = fopen(optarg, "r")) == NULL) {
140                Pmsg2(0, "Could not open include 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_include %s\n", line);
147                add_fname_to_include_list(&ff, 0, line);
148             }
149             fclose(fd);
150             break;
151
152          case 'j':
153             list_jobs = TRUE;
154             break;
155
156          case 'k':
157             list_blocks = TRUE;
158             break;
159
160          case 'L':
161             dump_label = TRUE;
162             break;
163
164          case 't':
165             default_tape = TRUE;
166             break;
167
168          case 'v':
169             verbose++;
170             break;
171
172          case '?':
173          default:
174             usage();
175
176       }  
177    }
178    argc -= optind;
179    argv += optind;
180
181    if (!argc && !default_tape) {
182       Pmsg0(0, "No archive name specified\n");
183       usage();
184    }
185
186    if (ff.included_files_list == NULL) {
187       add_fname_to_include_list(&ff, 0, "/");
188    }
189
190    /* Try default device */
191    if (default_tape) {
192       do_ls(DEFAULT_TAPE_DRIVE);
193       return 0;
194    }
195
196    for (i=0; i < argc; i++) {
197       jcr = setup_jcr("bls", argv[i], bsr);
198       dev = setup_to_read_device(jcr);
199       if (!dev) {
200          exit(1);
201       }
202       rec = new_record();
203       block = new_block(dev);
204       if (list_blocks) {
205          do_blocks(argv[i]);
206       } else if (list_jobs) {
207          do_jobs(argv[i]);
208       } else {
209          do_ls(argv[i]);
210       }
211       do_close();
212    }
213    if (bsr) {
214       free_bsr(bsr);
215    }
216    return 0;
217 }
218
219
220 static void do_close()
221 {
222    term_dev(dev);
223    free_record(rec);
224    free_block(block);
225    free_jcr(jcr);
226 }
227
228
229 /*
230  * Device got an error, attempt to analyse it
231  */
232 static void display_error_status()
233 {
234    uint32_t status;
235
236    Emsg0(M_ERROR, 0, dev->errmsg);
237    status_dev(dev, &status);
238    Dmsg1(20, "Device status: %x\n", status);
239    if (status & MT_EOD)
240       Emsg0(M_ERROR_TERM, 0, "Unexpected End of Data\n");
241    else if (status & MT_EOT)
242       Emsg0(M_ERROR_TERM, 0, "Unexpected End of Tape\n");
243    else if (status & MT_EOF)
244       Emsg0(M_ERROR_TERM, 0, "Unexpected End of File\n");
245    else if (status & MT_DR_OPEN)
246       Emsg0(M_ERROR_TERM, 0, "Tape Door is Open\n");
247    else if (!(status & MT_ONLINE))
248       Emsg0(M_ERROR_TERM, 0, "Unexpected Tape is Off-line\n");
249    else
250       Emsg2(M_ERROR_TERM, 0, "Read error on Record Header %s: %s\n", dev_name(dev), strerror(errno));
251 }
252
253
254 /* List just block information */
255 static void do_blocks(char *infname)
256 {
257
258    dump_volume_label(dev);
259
260    /* Assume that we have already read the volume label.
261     * If on second or subsequent volume, adjust buffer pointer 
262     */
263    if (dev->VolHdr.PrevVolName[0] != 0) { /* second volume */
264       Pmsg1(0, "\n\
265 Warning, this Volume is a continuation of Volume %s\n",
266                 dev->VolHdr.PrevVolName);
267    }
268  
269    if (verbose) {
270       rec = new_record();
271    }
272    for ( ;; ) {
273       if (!read_block_from_device(dev, block)) {
274          Dmsg0(20, "!read_block()\n");
275          if (dev->state & ST_EOT) {
276             if (!mount_next_read_volume(jcr, dev, block)) {
277                printf("End of File on device\n");
278                break;
279             }
280             DEV_RECORD *record;
281             record = new_record();
282             read_block_from_device(dev, block);
283             read_record_from_block(block, record);
284             get_session_record(dev, record, &sessrec);
285             free_record(record);
286             printf("Volume %s mounted.\n", jcr->VolumeName);
287             continue;
288          }
289          if (dev->state & ST_EOF) {
290             Emsg1(M_INFO, 0, "Got EOF on device %s\n", dev_name(dev));
291             Dmsg0(20, "read_record got eof. try again\n");
292             continue;
293          }
294          if (dev->state & ST_SHORT) {
295             Emsg0(M_INFO, 0, dev->errmsg);
296             continue;
297          }
298          display_error_status();
299          break;
300       }
301
302       if (verbose) {
303          read_record_from_block(block, rec);
304          Pmsg6(-1, "Block: %d blen=%d First rec FI=%s SessId=%d Strm=%s rlen=%d\n",
305               block->BlockNumber, block->block_len,
306               FI_to_ascii(rec->FileIndex), rec->VolSessionId, 
307               stream_to_ascii(rec->Stream), rec->data_len);
308          rec->remainder = 0;
309       } else {
310          printf("Block: %d size=%d\n", block->BlockNumber, block->block_len);
311       }
312
313    }
314    return;
315 }
316
317 /* Do list job records */
318 static void do_jobs(char *infname)
319 {
320
321    /* Assume that we have already read the volume label.
322     * If on second or subsequent volume, adjust buffer pointer 
323     */
324    if (dev->VolHdr.PrevVolName[0] != 0) { /* second volume */
325       Pmsg1(0, "\n\
326 Warning, this Volume is a continuation of Volume %s\n",
327                 dev->VolHdr.PrevVolName);
328    }
329  
330    for ( ;; ) {
331       if (!read_block_from_device(dev, block)) {
332          Dmsg0(20, "!read_block()\n");
333          if (dev->state & ST_EOT) {
334             DEV_RECORD *record;
335             if (!mount_next_read_volume(jcr, dev, block)) {
336                printf("Got EOF on device %s\n", dev_name(dev));
337                break;
338             }
339             record = new_record();
340             read_block_from_device(dev, block);
341             read_record_from_block(block, record);
342             get_session_record(dev, record, &sessrec);
343             free_record(record);
344             printf("Volume %s mounted.\n", jcr->VolumeName);
345             continue;
346          }
347          if (dev->state & ST_EOF) {
348             Emsg1(M_INFO, 0, "Got EOF on device %s\n", dev_name(dev));
349             Dmsg0(20, "read_record got eof. try again\n");
350             continue;
351          }
352          if (dev->state & ST_SHORT) {
353             Pmsg0(000, "Got short block.\n");
354             Emsg0(M_INFO, 0, dev->errmsg);
355             continue;
356          }
357          display_error_status();
358          break;
359       }
360       while (read_record_from_block(block, rec)) {
361          if (debug_level >= 30) {
362             Dmsg4(30, "VolSId=%ld FI=%s Strm=%s Size=%ld\n", rec->VolSessionId,
363                   FI_to_ascii(rec->FileIndex), stream_to_ascii(rec->Stream), 
364                   rec->data_len);
365          }
366
367
368          /*  
369           * Check for End of File record (all zeros)
370           *    NOTE: this no longer exists
371           */
372          if (rec->VolSessionId == 0 && rec->VolSessionTime == 0) {
373             Emsg0(M_ERROR_TERM, 0, "Zero VolSessionId and VolSessionTime. This shouldn't happen\n");
374          }
375
376          /* 
377           * Check for Start or End of Session Record 
378           *
379           */
380          if (rec->FileIndex < 0) {
381             dump_label_record(dev, rec, verbose);
382             continue;
383          }
384       }
385       rec->remainder = 0;
386    }
387    return;
388 }
389
390 /* Do an ls type listing of an archive */
391 static void do_ls(char *infname)
392 {
393    char fname[2000];
394    struct stat statp;
395    int type;
396    long record_file_index;
397    uint32_t num_files = 0;
398    int record;
399
400    if (dump_label) {
401       dump_volume_label(dev);
402       return;
403    }
404
405    /* Assume that we have already read the volume label.
406     * If on second or subsequent volume, adjust buffer pointer 
407     */
408    if (dev->VolHdr.PrevVolName[0] != 0) { /* second volume */
409       Pmsg1(0, "\n\
410 Warning, this Volume is a continuation of Volume %s\n",
411                 dev->VolHdr.PrevVolName);
412    }
413  
414    for ( ;; ) {
415
416       if (!read_block_from_device(dev, block)) {
417          Dmsg0(20, "!read_record()\n");
418          if (dev->state & ST_EOT) {
419             DEV_RECORD *record;
420             Dmsg3(100, "EOT. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec), 
421                   block->BlockNumber, rec->remainder);
422             if (!mount_next_read_volume(jcr, dev, block)) {
423                Dmsg3(100, "After mount next vol. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec), 
424                   block->BlockNumber, rec->remainder);
425                break;
426             }
427             Dmsg3(100, "After mount next vol. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec), 
428                   block->BlockNumber, rec->remainder);
429             record = new_record();
430             read_block_from_device(dev, block);
431             read_record_from_block(block, record);
432             get_session_record(dev, record, &sessrec);
433             free_record(record);
434             goto next_record;
435          }
436          if (dev->state & ST_EOF) {
437             Emsg1(M_INFO, 0, "Got EOF on device %s\n", dev_name(dev));
438             Dmsg0(20, "read_record got eof. try again\n");
439             continue;
440          }
441          if (dev->state & ST_SHORT) {
442             Emsg0(M_INFO, 0, dev->errmsg);
443             continue;
444          }
445          display_error_status();
446          break;
447       }
448       if (verbose) {
449          Dmsg2(10, "Block: %d blen=%d\n", block->BlockNumber, block->block_len);
450       }
451
452 next_record:
453       record = 0;
454       for (rec->state=0; !is_block_empty(rec); ) {
455          if (!read_record_from_block(block, rec)) {
456             Dmsg3(10, "!read-break. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec), 
457                   block->BlockNumber, rec->remainder);
458             break;
459          }
460          Dmsg3(10, "read-OK. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec), 
461                   block->BlockNumber, rec->remainder);
462          /*
463           * At this point, we have at least a record header.
464           *  Now decide if we want this record or not, but remember
465           *  before accessing the record, we may need to read again to
466           *  get all the data.
467           */
468          record++;
469          if (verbose) {
470             Dmsg6(30, "recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
471                rec_state_to_str(rec), block->BlockNumber,
472                rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
473          }
474          if (debug_level >= 30) {
475             Dmsg4(30, "VolSId=%ld FI=%s Strm=%s Size=%ld\n", rec->VolSessionId,
476                   FI_to_ascii(rec->FileIndex), stream_to_ascii(rec->Stream), 
477                   rec->data_len);
478          }
479
480          if (rec->FileIndex == EOM_LABEL) { /* end of tape? */
481             Dmsg0(40, "Get EOM LABEL\n");
482             rec->remainder = 0;
483             break;                         /* yes, get out */
484          }
485
486          /* Some sort of label? */ 
487          if (rec->FileIndex < 0) {
488             get_session_record(dev, rec, &sessrec);
489             continue;
490          } /* end if label record */
491
492          /* 
493           * Apply BSR filter
494           */
495          if (bsr && !match_bsr(bsr, rec, &dev->VolHdr, &sessrec)) {
496             if (verbose) {
497                Dmsg5(10, "BSR no match rec=%d block=%d SessId=%d SessTime=%d FI=%d\n",
498                   record, block->BlockNumber, rec->VolSessionId, rec->VolSessionTime, 
499                   rec->FileIndex);
500             }
501             rec->remainder = 0;
502             continue;              /* we don't want record, read next one */
503          }
504          if (is_partial_record(rec)) {
505             Dmsg6(10, "Partial, break. recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
506                rec_state_to_str(rec), block->BlockNumber,
507                rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
508             break;                    /* read second part of record */
509          }
510
511          /* File Attributes stream */
512          if (rec->Stream == STREAM_UNIX_ATTRIBUTES) {
513             char *ap, *fp;
514             sscanf(rec->data, "%ld %d", &record_file_index, &type);
515             if (record_file_index != rec->FileIndex) {
516                Emsg2(M_ERROR_TERM, 0, "Record header file index %ld not equal record index %ld\n",
517                   rec->FileIndex, record_file_index);
518             }
519             ap = rec->data;
520
521             while (*ap++ != ' ')         /* skip record file index */
522                ;
523             while (*ap++ != ' ')         /* skip type */
524                ;
525             /* Save filename and position to attributes */
526             fp = fname;
527             while (*ap != 0) {
528                *fp++  = *ap++;
529             }
530             *fp = *ap++;                 /* terminate filename & point to attribs */
531
532             decode_stat(ap, &statp);
533             /* Skip to link name */  
534             while (*ap++ != 0)
535                ;
536             if (file_is_included(&ff, fname) && !file_is_excluded(&ff, fname)) {
537                print_ls_output(fname, ap, type, &statp);
538                num_files++;
539             }
540          }
541       }
542    }
543    if (verbose) {
544       printf("%u files found.\n", num_files);
545    }
546    return;
547 }
548
549
550 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
551 {
552    char *rtype;
553    memset(sessrec, 0, sizeof(sessrec));
554    switch (rec->FileIndex) {
555       case PRE_LABEL:
556          rtype = "Fresh Volume Label";   
557          break;
558       case VOL_LABEL:
559          rtype = "Volume Label";
560          unser_volume_label(dev, rec);
561          break;
562       case SOS_LABEL:
563          rtype = "Begin Session";
564          unser_session_label(sessrec, rec);
565          break;
566       case EOS_LABEL:
567          rtype = "End Session";
568          break;
569       case EOM_LABEL:
570          rtype = "End of Media";
571          break;
572       default:
573          rtype = "Unknown";
574          break;
575    }
576    Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
577          rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
578 }
579
580
581 /* Dummies to replace askdir.c */
582 int     dir_get_volume_info(JCR *jcr) { return 1;}
583 int     dir_find_next_appendable_volume(JCR *jcr) { return 1;}
584 int     dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; }
585 int     dir_create_jobmedia_record(JCR *jcr) { return 1; }
586 int     dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; }
587 int     dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;}
588 int     dir_send_job_status(JCR *jcr) {return 1;}
589
590
591 int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev)
592 {
593    fprintf(stderr, "Mount Volume %s on device %s and press return when ready: ",
594       jcr->VolumeName, dev_name(dev));
595    getchar();   
596    return 1;
597 }