]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bls.c
First cut 1.23 -- kes07Jul02
[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 print_ls_output(char *fname, char *link, int type, struct stat *statp);
35 static void do_setup(char *infname);
36 static void do_close();
37
38 static DEVICE *dev;
39 static int default_tape = FALSE;
40 static int dump_label = FALSE;
41 static int list_blocks = FALSE;
42 static int list_jobs = FALSE;
43 static int verbose = 0;
44 static char Vol[2000];
45 static char *VolName;
46 static char *p;
47 static DEV_RECORD *rec;
48 static DEV_BLOCK *block;
49 static int NumVolumes, CurVolume;
50 static JCR *jcr;
51
52
53 extern char BaculaId[];
54
55 static FF_PKT ff;
56
57 static void usage()
58 {
59    fprintf(stderr,
60 "Usage: bls [-d debug_level] <physical-device-name>\n"
61 "       -b              list blocks\n"
62 "       -e <file>       exclude list\n"
63 "       -i <file>       include list\n"
64 "       -j              list jobs\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
74 int main (int argc, char *argv[])
75 {
76    int i, ch;
77    FILE *fd;
78    char line[1000];
79
80    my_name_is(argc, argv, "bls");
81    init_msg(NULL, NULL);              /* initialize message handler */
82
83    memset(&ff, 0, sizeof(ff));
84    init_include_exclude_files(&ff);
85
86    while ((ch = getopt(argc, argv, "bd:e:i:jLtv?")) != -1) {
87       switch (ch) {
88          case 'b':
89             list_blocks = TRUE;
90             break;
91          case 'd':                    /* debug level */
92             debug_level = atoi(optarg);
93             if (debug_level <= 0)
94                debug_level = 1; 
95             break;
96
97          case 'e':                    /* exclude list */
98             if ((fd = fopen(optarg, "r")) == NULL) {
99                Pmsg2(0, "Could not open exclude file: %s, ERR=%s\n",
100                   optarg, strerror(errno));
101                exit(1);
102             }
103             while (fgets(line, sizeof(line), fd) != NULL) {
104                strip_trailing_junk(line);
105                Dmsg1(000, "add_exclude %s\n", line);
106                add_fname_to_exclude_list(&ff, line);
107             }
108             fclose(fd);
109             break;
110
111          case 'i':                    /* include list */
112             if ((fd = fopen(optarg, "r")) == NULL) {
113                Pmsg2(0, "Could not open include file: %s, ERR=%s\n",
114                   optarg, strerror(errno));
115                exit(1);
116             }
117             while (fgets(line, sizeof(line), fd) != NULL) {
118                strip_trailing_junk(line);
119                Dmsg1(000, "add_include %s\n", line);
120                add_fname_to_include_list(&ff, 0, line);
121             }
122             fclose(fd);
123             break;
124
125          case 'j':
126             list_jobs = TRUE;
127             break;
128
129          case 'L':
130             dump_label = TRUE;
131             break;
132
133          case 't':
134             default_tape = TRUE;
135             break;
136
137          case 'v':
138             verbose++;
139             break;
140
141          case '?':
142          default:
143             usage();
144
145       }  
146    }
147    argc -= optind;
148    argv += optind;
149
150    if (!argc && !default_tape) {
151       Pmsg0(0, "No archive name specified\n");
152       usage();
153    }
154
155    if (ff.included_files_list == NULL) {
156       add_fname_to_include_list(&ff, 0, "/");
157    }
158
159    /* Try default device */
160    if (default_tape) {
161       do_ls(DEFAULT_TAPE_DRIVE);
162       return 0;
163    }
164
165    for (i=0; i < argc; i++) {
166       do_setup(argv[i]);
167       if (list_blocks) {
168          do_blocks(argv[i]);
169       } else if (list_jobs) {
170          do_jobs(argv[i]);
171       } else {
172          do_ls(argv[i]);
173       }
174       do_close();
175    }
176    return 0;
177 }
178
179 static void my_free_jcr(JCR *jcr)
180 {
181    return;
182 }
183
184 /*
185  * Setup device, jcr, and prepare to read
186  */
187 static void do_setup(char *infname) 
188 {
189    jcr = new_jcr(sizeof(JCR), my_free_jcr);
190    VolName = Vol;
191    VolName[0] = 0;
192    if (strncmp(infname, "/dev/", 5) != 0) {
193       /* Try stripping file part */
194       p = infname + strlen(infname);
195       while (p >= infname && *p != '/')
196          p--;
197       if (*p == '/') {
198          strcpy(VolName, p+1);
199          *p = 0;
200       }
201    }
202    Dmsg2(10, "Device=%s, Vol=%s.\n", infname, VolName);
203    dev = init_dev(NULL, infname);
204    if (!dev) {
205       Emsg1(M_FATAL, 0, "Cannot open %s\n", infname);
206       exit(1);
207    }
208    /* ***FIXME**** init capabilities */
209    if (!open_device(dev)) {
210       Emsg1(M_FATAL, 0, "Cannot open %s\n", infname);
211       exit(1);
212    }
213    Dmsg0(90, "Device opened for read.\n");
214
215    rec = new_record();
216    block = new_block(dev);
217
218    NumVolumes = 0;
219    CurVolume = 1;
220    for (p = VolName; p && *p; ) {
221       p = strchr(p, '^');
222       if (p) {
223          *p++ = 0;
224       }
225       NumVolumes++;
226    }
227
228    jcr->VolumeName = (char *)check_pool_memory_size(jcr->VolumeName, strlen(VolName)+1);
229    strcpy(jcr->VolumeName, VolName);
230    if (!acquire_device_for_read(jcr, dev, block)) {
231       Emsg0(M_ERROR, 0, dev->errmsg);
232       exit(1);
233    }
234 }
235
236 static void do_close()
237 {
238    term_dev(dev);
239    free_record(rec);
240    free_block(block);
241    free_jcr(jcr);
242 }
243
244 static int mount_next_volume(char *infname)
245 {
246    if (rec->remainder) {
247       Dmsg0(20, "Not end of record. Next volume has more data for current record.\n");
248    }
249    Dmsg2(20, "NumVolumes=%d CurVolume=%d\n", NumVolumes, CurVolume);
250    if (NumVolumes > 1 && CurVolume < NumVolumes) {
251       p = VolName;
252       while (*p++)  
253          { }
254       CurVolume++;
255       Dmsg1(20, "There is another volume %s.\n", p);
256       VolName = p;
257       jcr->VolumeName = check_pool_memory_size(jcr->VolumeName, 
258                           strlen(VolName)+1);
259       strcpy(jcr->VolumeName, VolName);
260
261       close_dev(dev);
262       dev->state &= ~ST_READ; 
263       if (!acquire_device_for_read(jcr, dev, block)) {
264          Emsg2(M_FATAL, 0, "Cannot open Dev=%s, Vol=%s\n", infname, VolName);
265          exit(1);
266       }
267       return 1;              /* Next volume mounted */
268    }
269    printf("End of Device reached.\n");
270    return 0;                 /* EOT */
271 }
272
273 /*
274  * Device got an error, attempt to analyse it
275  */
276 static void display_error_status()
277 {
278    uint32_t status;
279
280    Emsg0(M_ERROR, 0, dev->errmsg);
281    status_dev(dev, &status);
282    Dmsg1(20, "Device status: %x\n", status);
283    if (status & MT_EOD)
284       Emsg0(M_ERROR_TERM, 0, "Unexpected End of Data\n");
285    else if (status & MT_EOT)
286       Emsg0(M_ERROR_TERM, 0, "Unexpected End of Tape\n");
287    else if (status & MT_EOF)
288       Emsg0(M_ERROR_TERM, 0, "Unexpected End of File\n");
289    else if (status & MT_DR_OPEN)
290       Emsg0(M_ERROR_TERM, 0, "Tape Door is Open\n");
291    else if (!(status & MT_ONLINE))
292       Emsg0(M_ERROR_TERM, 0, "Unexpected Tape is Off-line\n");
293    else
294       Emsg2(M_ERROR_TERM, 0, "Read error on Record Header %s: %s\n", dev_name(dev), strerror(errno));
295 }
296
297
298 /* List just block information */
299 static void do_blocks(char *infname)
300 {
301
302    dump_volume_label(dev);
303
304    /* Assume that we have already read the volume label.
305     * If on second or subsequent volume, adjust buffer pointer 
306     */
307    if (dev->VolHdr.PrevVolName[0] != 0) { /* second volume */
308       Pmsg1(0, "\n\
309 Warning, this Volume is a continuation of Volume %s\n",
310                 dev->VolHdr.PrevVolName);
311    }
312  
313    for ( ;; ) {
314
315       if (!read_block_from_device(dev, block)) {
316          Dmsg0(20, "!read_record()\n");
317          if (dev->state & ST_EOT) {
318             if (!mount_next_volume(infname)) {
319                break;
320             }
321             continue;
322          }
323          if (dev->state & ST_EOF) {
324             Emsg1(M_INFO, 0, "Got EOF on device %s\n", dev_name(dev));
325             Dmsg0(20, "read_record got eof. try again\n");
326             continue;
327          }
328          if (dev->state & ST_SHORT) {
329             Emsg0(M_INFO, 0, dev->errmsg);
330             continue;
331          }
332          display_error_status();
333          break;
334       }
335
336       printf("Block: %d size=%d\n", block->BlockNumber, block->block_len);
337
338    }
339    return;
340 }
341
342 /* Do list job records */
343 static void do_jobs(char *infname)
344 {
345
346    /* Assume that we have already read the volume label.
347     * If on second or subsequent volume, adjust buffer pointer 
348     */
349    if (dev->VolHdr.PrevVolName[0] != 0) { /* second volume */
350       Pmsg1(0, "\n\
351 Warning, this Volume is a continuation of Volume %s\n",
352                 dev->VolHdr.PrevVolName);
353    }
354  
355    for ( ;; ) {
356       if (!read_record(dev, block, rec)) {
357          Dmsg0(20, "!read_record()\n");
358          if (dev->state & ST_EOT) {
359             if (!mount_next_volume(infname)) {
360                break;
361             }
362             continue;
363          }
364          if (dev->state & ST_EOF) {
365             Emsg1(M_INFO, 0, "Got EOF on device %s\n", dev_name(dev));
366             Dmsg0(20, "read_record got eof. try again\n");
367             continue;
368          }
369          if (dev->state & ST_SHORT) {
370             Emsg0(M_INFO, 0, dev->errmsg);
371             continue;
372          }
373          display_error_status();
374          break;
375       }
376
377       if (debug_level >= 30) {
378          Dmsg4(30, "VolSId=%ld FI=%s Strm=%s Size=%ld\n", rec->VolSessionId,
379                FI_to_ascii(rec->FileIndex), stream_to_ascii(rec->Stream), 
380                rec->data_len);
381       }
382
383
384       /*  
385        * Check for End of File record (all zeros)
386        *    NOTE: this no longer exists
387        */
388       if (rec->VolSessionId == 0 && rec->VolSessionTime == 0) {
389          Emsg0(M_ERROR_TERM, 0, "Zero VolSessionId and VolSessionTime. This shouldn't happen\n");
390       }
391
392       /* 
393        * Check for Start or End of Session Record 
394        *
395        */
396       if (rec->FileIndex < 0) {
397          dump_label_record(dev, rec, verbose);
398          continue;
399       }
400    }
401    return;
402 }
403
404 /* Do an ls type listing of an archive */
405 static void do_ls(char *infname)
406 {
407    char fname[2000];
408    struct stat statp;
409    int type;
410    long record_file_index;
411
412    if (dump_label) {
413       dump_volume_label(dev);
414       return;
415    }
416
417    /* Assume that we have already read the volume label.
418     * If on second or subsequent volume, adjust buffer pointer 
419     */
420    if (dev->VolHdr.PrevVolName[0] != 0) { /* second volume */
421       Pmsg1(0, "\n\
422 Warning, this Volume is a continuation of Volume %s\n",
423                 dev->VolHdr.PrevVolName);
424    }
425  
426    for ( ;; ) {
427       if (!read_record(dev, block, rec)) {
428          Dmsg0(20, "!read_record()\n");
429          if (dev->state & ST_EOT) {
430             if (!mount_next_volume(infname)) {
431                break;
432             }
433             continue;
434          }
435          if (dev->state & ST_EOF) {
436             Emsg1(M_INFO, 0, "Got EOF on device %s\n", dev_name(dev));
437             Dmsg0(20, "read_record got eof. try again\n");
438             continue;
439          }
440          if (dev->state & ST_SHORT) {
441             Emsg0(M_INFO, 0, dev->errmsg);
442             continue;
443          }
444          display_error_status();
445          break;
446       }
447
448       if (debug_level >= 30) {
449          Dmsg4(30, "VolSId=%ld FI=%s Strm=%s Size=%ld\n", rec->VolSessionId,
450                FI_to_ascii(rec->FileIndex), stream_to_ascii(rec->Stream), 
451                rec->data_len);
452       }
453
454
455       /*  
456        * Check for End of File record (all zeros)
457        *    NOTE: this no longer exists
458        */
459       if (rec->VolSessionId == 0 && rec->VolSessionTime == 0) {
460          Emsg0(M_ERROR_TERM, 0, "Zero VolSessionId and VolSessionTime. This shouldn't happen\n");
461       }
462
463       /* 
464        * Check for Start or End of Session Record 
465        *
466        */
467       if (rec->FileIndex < 0) {
468          dump_label_record(dev, rec, 0);
469          continue;
470       }
471
472       /* File Attributes stream */
473       if (rec->Stream == STREAM_UNIX_ATTRIBUTES) {
474          char *ap;
475          sscanf(rec->data, "%ld %d %s", &record_file_index, &type, fname);
476          if (record_file_index != rec->FileIndex) {
477             Emsg2(M_ERROR_TERM, 0, "Record header file index %ld not equal record index %ld\n",
478                rec->FileIndex, record_file_index);
479          }
480          ap = rec->data;
481          /* Skip to attributes */
482          while (*ap++ != 0)
483             ;
484          decode_stat(ap, &statp);
485          /* Skip to link name */  
486          while (*ap++ != 0)
487             ;
488          print_ls_output(fname, ap, type, &statp);
489       }
490    }
491    return;
492 }
493
494 extern char *getuser(uid_t uid);
495 extern char *getgroup(gid_t gid);
496
497 static void print_ls_output(char *fname, char *link, int type, struct stat *statp)
498 {
499    char buf[1000]; 
500    char ec1[30];
501    char *p, *f;
502    int n;
503
504    if (!file_is_included(&ff, fname) || file_is_excluded(&ff, fname)) {
505       return;
506    }
507    p = encode_mode(statp->st_mode, buf);
508    n = sprintf(p, "  %2d ", (uint32_t)statp->st_nlink);
509    p += n;
510    n = sprintf(p, "%-8.8s %-8.8s", getuser(statp->st_uid), getgroup(statp->st_gid));
511    p += n;
512    n = sprintf(p, "%8.8s ", edit_uint64(statp->st_size, ec1));
513    p += n;
514    p = encode_time(statp->st_ctime, p);
515    *p++ = ' ';
516    *p++ = ' ';
517    /* Copy file name */
518    for (f=fname; *f && (p-buf) < (int)sizeof(buf); )
519       *p++ = *f++;
520    if (type == FT_LNK) {
521       *p++ = ' ';
522       *p++ = '-';
523       *p++ = '>';
524       *p++ = ' ';
525       /* Copy link name */
526       for (f=link; *f && (p-buf) < (int)sizeof(buf); )
527          *p++ = *f++;
528    }
529    *p++ = '\n';
530    *p = 0;
531    fputs(buf, stdout);
532 }
533
534 /* Dummies to replace askdir.c */
535 int     dir_get_volume_info(JCR *jcr) { return 1;}
536 int     dir_find_next_appendable_volume(JCR *jcr) { return 1;}
537 int     dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; }
538 int     dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; }
539 int     dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;}
540 int     dir_send_job_status(JCR *jcr) {return 1;}
541
542
543 int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev)
544 {
545    fprintf(stderr, "Mount Volume %s on device %s and press return when ready: ",
546       jcr->VolumeName, dev_name(dev));
547    getchar();   
548    return 1;
549 }