3 * Dumb program to do an "ls" of a Bacula 1.0 mortal file.
8 Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
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.
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.
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,
29 #include "findlib/find.h"
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();
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];
47 static DEV_RECORD *rec;
48 static DEV_BLOCK *block;
49 static int NumVolumes, CurVolume;
53 extern char BaculaId[];
60 "Usage: bls [-d debug_level] <physical-device-name>\n"
62 " -e <file> exclude list\n"
63 " -i <file> include list\n"
65 " -L list tape label\n"
66 " (none of above) list saved files\n"
67 " -t use default tape device\n"
69 " -? print this message\n\n");
74 int main (int argc, char *argv[])
80 my_name_is(argc, argv, "bls");
81 init_msg(NULL, NULL); /* initialize message handler */
83 memset(&ff, 0, sizeof(ff));
84 init_include_exclude_files(&ff);
86 while ((ch = getopt(argc, argv, "bd:e:i:jLtv?")) != -1) {
91 case 'd': /* debug level */
92 debug_level = atoi(optarg);
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));
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);
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));
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);
150 if (!argc && !default_tape) {
151 Pmsg0(0, "No archive name specified\n");
155 if (ff.included_files_list == NULL) {
156 add_fname_to_include_list(&ff, 0, "/");
159 /* Try default device */
161 do_ls(DEFAULT_TAPE_DRIVE);
165 for (i=0; i < argc; i++) {
169 } else if (list_jobs) {
179 static void my_free_jcr(JCR *jcr)
185 * Setup device, jcr, and prepare to read
187 static void do_setup(char *infname)
189 jcr = new_jcr(sizeof(JCR), my_free_jcr);
192 if (strncmp(infname, "/dev/", 5) != 0) {
193 /* Try stripping file part */
194 p = infname + strlen(infname);
195 while (p >= infname && *p != '/')
198 strcpy(VolName, p+1);
202 Dmsg2(10, "Device=%s, Vol=%s.\n", infname, VolName);
203 dev = init_dev(NULL, infname);
205 Emsg1(M_FATAL, 0, "Cannot open %s\n", infname);
208 /* ***FIXME**** init capabilities */
209 if (!open_device(dev)) {
210 Emsg1(M_FATAL, 0, "Cannot open %s\n", infname);
213 Dmsg0(90, "Device opened for read.\n");
216 block = new_block(dev);
220 for (p = VolName; p && *p; ) {
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);
236 static void do_close()
244 static int mount_next_volume(char *infname)
246 if (rec->remainder) {
247 Dmsg0(20, "Not end of record. Next volume has more data for current record.\n");
249 Dmsg2(20, "NumVolumes=%d CurVolume=%d\n", NumVolumes, CurVolume);
250 if (NumVolumes > 1 && CurVolume < NumVolumes) {
255 Dmsg1(20, "There is another volume %s.\n", p);
257 jcr->VolumeName = check_pool_memory_size(jcr->VolumeName,
259 strcpy(jcr->VolumeName, VolName);
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);
267 return 1; /* Next volume mounted */
269 printf("End of Device reached.\n");
274 * Device got an error, attempt to analyse it
276 static void display_error_status()
280 Emsg0(M_ERROR, 0, dev->errmsg);
281 status_dev(dev, &status);
282 Dmsg1(20, "Device status: %x\n", status);
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");
294 Emsg2(M_ERROR_TERM, 0, "Read error on Record Header %s: %s\n", dev_name(dev), strerror(errno));
298 /* List just block information */
299 static void do_blocks(char *infname)
302 dump_volume_label(dev);
304 /* Assume that we have already read the volume label.
305 * If on second or subsequent volume, adjust buffer pointer
307 if (dev->VolHdr.PrevVolName[0] != 0) { /* second volume */
309 Warning, this Volume is a continuation of Volume %s\n",
310 dev->VolHdr.PrevVolName);
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)) {
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");
328 if (dev->state & ST_SHORT) {
329 Emsg0(M_INFO, 0, dev->errmsg);
332 display_error_status();
336 printf("Block: %d size=%d\n", block->BlockNumber, block->block_len);
342 /* Do list job records */
343 static void do_jobs(char *infname)
346 /* Assume that we have already read the volume label.
347 * If on second or subsequent volume, adjust buffer pointer
349 if (dev->VolHdr.PrevVolName[0] != 0) { /* second volume */
351 Warning, this Volume is a continuation of Volume %s\n",
352 dev->VolHdr.PrevVolName);
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)) {
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");
369 if (dev->state & ST_SHORT) {
370 Emsg0(M_INFO, 0, dev->errmsg);
373 display_error_status();
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),
385 * Check for End of File record (all zeros)
386 * NOTE: this no longer exists
388 if (rec->VolSessionId == 0 && rec->VolSessionTime == 0) {
389 Emsg0(M_ERROR_TERM, 0, "Zero VolSessionId and VolSessionTime. This shouldn't happen\n");
393 * Check for Start or End of Session Record
396 if (rec->FileIndex < 0) {
397 dump_label_record(dev, rec, verbose);
404 /* Do an ls type listing of an archive */
405 static void do_ls(char *infname)
410 long record_file_index;
413 dump_volume_label(dev);
417 /* Assume that we have already read the volume label.
418 * If on second or subsequent volume, adjust buffer pointer
420 if (dev->VolHdr.PrevVolName[0] != 0) { /* second volume */
422 Warning, this Volume is a continuation of Volume %s\n",
423 dev->VolHdr.PrevVolName);
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)) {
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");
440 if (dev->state & ST_SHORT) {
441 Emsg0(M_INFO, 0, dev->errmsg);
444 display_error_status();
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),
456 * Check for End of File record (all zeros)
457 * NOTE: this no longer exists
459 if (rec->VolSessionId == 0 && rec->VolSessionTime == 0) {
460 Emsg0(M_ERROR_TERM, 0, "Zero VolSessionId and VolSessionTime. This shouldn't happen\n");
464 * Check for Start or End of Session Record
467 if (rec->FileIndex < 0) {
468 dump_label_record(dev, rec, 0);
472 /* File Attributes stream */
473 if (rec->Stream == STREAM_UNIX_ATTRIBUTES) {
475 sscanf(rec->data, "%ld %d", &record_file_index, &type);
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);
482 while (*ap++ != ' ') /* skip record file index */
484 while (*ap++ != ' ') /* skip type */
486 /* Save filename and position to attributes */
491 *fp = *ap++; /* terminate filename & point to attribs */
493 decode_stat(ap, &statp);
494 /* Skip to link name */
497 print_ls_output(fname, ap, type, &statp);
503 extern char *getuser(uid_t uid);
504 extern char *getgroup(gid_t gid);
506 static void print_ls_output(char *fname, char *link, int type, struct stat *statp)
513 if (!file_is_included(&ff, fname) || file_is_excluded(&ff, fname)) {
516 p = encode_mode(statp->st_mode, buf);
517 n = sprintf(p, " %2d ", (uint32_t)statp->st_nlink);
519 n = sprintf(p, "%-8.8s %-8.8s", getuser(statp->st_uid), getgroup(statp->st_gid));
521 n = sprintf(p, "%8.8s ", edit_uint64(statp->st_size, ec1));
523 p = encode_time(statp->st_ctime, p);
527 for (f=fname; *f && (p-buf) < (int)sizeof(buf); )
529 if (type == FT_LNK) {
535 for (f=link; *f && (p-buf) < (int)sizeof(buf); )
543 /* Dummies to replace askdir.c */
544 int dir_get_volume_info(JCR *jcr) { return 1;}
545 int dir_find_next_appendable_volume(JCR *jcr) { return 1;}
546 int dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; }
547 int dir_create_jobmedia_record(JCR *jcr) { return 1; }
548 int dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; }
549 int dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;}
550 int dir_send_job_status(JCR *jcr) {return 1;}
553 int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev)
555 fprintf(stderr, "Mount Volume %s on device %s and press return when ready: ",
556 jcr->VolumeName, dev_name(dev));