]> git.sur5r.net Git - bacula/bacula/blob - bacula/platforms/freebsd/tapetest.c
Add systemd files
[bacula/bacula] / bacula / platforms / freebsd / tapetest.c
1 /*
2  *
3  * Program to test loss of data at EOM on
4  *  FreeBSD systems.
5  *
6  *       Kern Sibbald, August 2003
7  *
8  *  If you build this program with:
9  *
10  *  c++ -g -O2 -Wall -c tapetest.c
11  *  c++ -g -O2 -Wall tapetest.o -o tapetest
12  *
13  *  Procedure for testing tape
14  *  ./tapetest /dev/your-tape-device
15  *  rewind
16  *  rawfill
17  *  rewind
18  *  scan
19  *  quit
20  *
21  *  The output will be:
22  * 
23  * ========
24  *  Rewound /dev/nsa0
25  *  *Begin writing blocks of 64512 bytes.
26  *  ++++++++++++++++++++ ...
27  *  Write failed.  Last block written=17294. stat=0 ERR=Unknown error: 0
28  *  weof_dev
29  *  Wrote EOF to /dev/nsa0
30  *  *Rewound /dev/nsa0
31  *  *Starting scan at file 0
32  *  17294 blocks of 64512 bytes in file 0
33  *  End of File mark.
34  *  End of File mark.
35  *  End of tape
36  *  Total files=1, blocks=17294, bytes = 1115670528
37  * ========
38  *
39  *  which is correct. Notice that the return status is
40  *  0, while in the example below, which fails, the return
41  *  status is -1.
42  *
43
44  *  If you build this program with:
45  *
46  *  c++ -g -O2 -Wall -pthread -c tapetest.c
47  *  c++ -g -O2 -Wall -pthread tapetest.o -o tapetest
48  *    Note, we simply added -pthread compared to the
49  *    previous example.
50  *                      
51  *  Procedure for testing tape
52  *  ./tapetest /dev/your-tape-device
53  *  rewind
54  *  rawfill
55  *  rewind
56  *  scan
57  *  quit
58  *
59  *  The output will be:
60  *
61  * ========
62  *    Rewound /dev/nsa0
63  *    *Begin writing blocks of 64512 bytes.
64  *    +++++++++++++++++++++++++++++ ...
65  *    Write failed.  Last block written=17926. stat=-1 ERR=No space left on device
66  *    weof_dev
67  *    Wrote EOF to /dev/nsa0
68  *    *Rewound /dev/nsa0
69  *    *Starting scan at file 0
70  *    17913 blocks of 64512 bytes in file 0
71  *    End of File mark.
72  *    End of File mark.
73  *    End of tape
74  *    Total files=1, blocks=17913, bytes = 1155603456
75  * ========
76  *
77  * which is incroorect because it wrote 17,926 blocks but read
78  * back only 17,913 blocks, AND because the return status on 
79  * the last block written was -1 when it should have been
80  * 0 (ie. stat=0 above).
81  *
82  *
83  */
84
85 #include <stdio.h>
86 #include <unistd.h>
87 #include <stdlib.h>
88 #include <stdarg.h>
89 #include <errno.h>
90 #include <sys/ioctl.h>
91 #include <string.h>
92 #include <strings.h>
93 #include <sys/mtio.h>
94 #include <sys/types.h>
95 #include <sys/stat.h>
96 #include <fcntl.h>
97 #include <ctype.h>
98
99 #define FALSE 0
100 #define TRUE  1
101
102 #define dev_state(dev, state) ((dev)->state & (state))
103
104 /* Device state bits */
105 #define ST_OPENED          (1<<0)     /* set when device opened */
106 #define ST_TAPE            (1<<1)     /* is a tape device */  
107 #define ST_FILE            (1<<2)     /* is a file device */
108 #define ST_FIFO            (1<<3)     /* is a fifo device */
109 #define ST_PROG            (1<<4)     /* is a program device */
110 #define ST_LABEL           (1<<5)     /* label found */
111 #define ST_MALLOC          (1<<6)     /* dev packet malloc'ed in init_dev() */
112 #define ST_APPEND          (1<<7)     /* ready for Bacula append */
113 #define ST_READ            (1<<8)     /* ready for Bacula read */
114 #define ST_EOT             (1<<9)     /* at end of tape */
115 #define ST_WEOT            (1<<10)    /* Got EOT on write */
116 #define ST_EOF             (1<<11)    /* Read EOF i.e. zero bytes */
117 #define ST_NEXTVOL         (1<<12)    /* Start writing on next volume */
118 #define ST_SHORT           (1<<13)    /* Short block read */
119
120 #define BLOCK_SIZE (512 * 126)
121
122
123 /* Exported variables */
124 int quit = 0;
125 char buf[100000];
126 int verbose = 0;
127 int debug_level = 0;
128 int fd = 0;
129
130 struct DEVICE {
131    int fd;
132    int dev_errno;
133    int file;
134    int block_num;
135    int state;
136    char *buf;
137    int buf_len;
138    char *dev_name;
139    int file_addr;
140 };
141
142 DEVICE *dev;
143
144 #define uint32_t unsigned long
145 #define uint64_t unsigned long long
146             
147 /* Forward referenced subroutines */
148 static void do_tape_cmds();
149 static void helpcmd();
150 static void scancmd();
151 static void rewindcmd();
152 static void rawfill_cmd();
153
154
155 /* Static variables */
156
157 static char cmd[1000];
158
159 static void usage();
160 int get_cmd(char *prompt);
161
162
163 /*********************************************************************
164  *
165  *         Main Bacula Pool Creation Program
166  *
167  */
168 int main(int argc, char *argv[])
169 {
170    int ch;
171
172    while ((ch = getopt(argc, argv, "d:v?")) != -1) {
173       switch (ch) {
174          case 'd':                    /* set debug level */
175             debug_level = atoi(optarg);
176             if (debug_level <= 0) {
177                debug_level = 1; 
178             }
179             break;
180
181          case 'v':
182             verbose++;
183             break;
184
185          case '?':
186          default:
187             helpcmd();
188             exit(0);
189
190       }  
191    }
192    argc -= optind;
193    argv += optind;
194
195
196    /* See if we can open a device */
197    if (argc == 0) {
198       printf("No archive name specified.\n");
199       usage();
200       exit(1);
201    } else if (argc != 1) {
202       printf("Improper number of arguments specified.\n");
203       usage();
204       exit(1);
205    }
206
207    fd = open(argv[0], O_RDWR);   
208    if (fd < 0) {
209       printf("Error opening %s ERR=%s\n", argv[0], strerror(errno));
210       exit(1);
211    }
212    dev = (DEVICE *)malloc(sizeof(DEVICE));
213    memset(dev, 0, sizeof(DEVICE));
214    dev->fd = fd;
215    dev->dev_name = strdup(argv[0]);
216    dev->buf_len = BLOCK_SIZE;
217    dev->buf = (char *)malloc(BLOCK_SIZE);
218
219    do_tape_cmds();
220    return 0;
221 }
222
223
224 int rewind_dev(DEVICE *dev)
225 {
226    struct mtop mt_com;
227
228    if (dev->fd < 0) {
229       dev->dev_errno = EBADF;
230       printf("Bad call to rewind_dev. Device %s not open\n",
231             dev->dev_name);
232       return 0;
233    }
234    dev->state &= ~(ST_APPEND|ST_READ|ST_EOT|ST_EOF|ST_WEOT);  /* remove EOF/EOT flags */
235    dev->block_num = dev->file = 0;
236    mt_com.mt_op = MTREW;
237    mt_com.mt_count = 1;
238    if (ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) {
239       dev->dev_errno = errno;
240       printf("Rewind error on %s. ERR=%s.\n",
241                dev->dev_name, strerror(dev->dev_errno));
242       return 0;
243    }
244    return 1;
245 }
246
247 /*
248  * Write an end of file on the device
249  *   Returns: 0 on success
250  *            non-zero on failure
251  */
252 int 
253 weof_dev(DEVICE *dev, int num)
254
255    struct mtop mt_com;
256    int stat;
257
258    if (dev->fd < 0) {
259       dev->dev_errno = EBADF;
260       printf("Bad call to fsf_dev. Archive not open\n");
261       return -1;
262    }
263
264    dev->state &= ~(ST_EOT | ST_EOF);  /* remove EOF/EOT flags */
265    dev->block_num = 0;
266    printf("weof_dev\n");
267    mt_com.mt_op = MTWEOF;
268    mt_com.mt_count = num;
269    stat = ioctl(dev->fd, MTIOCTOP, (char *)&mt_com);
270    if (stat == 0) {
271       dev->file++;
272       dev->file_addr = 0;
273    } else {
274       dev->dev_errno = errno;
275       printf("ioctl MTWEOF error on %s. ERR=%s.\n",
276          dev->dev_name, strerror(dev->dev_errno));
277    }
278    return stat;
279 }
280
281
282
283
284
285 void quitcmd()
286 {
287    quit = 1;
288 }
289
290
291 /*
292  * Rewind the tape.   
293  */
294 static void rewindcmd()
295 {
296    if (!rewind_dev(dev)) {
297       printf("Bad status from rewind. ERR=%s\n", strerror(dev->dev_errno));
298    } else {
299       printf("Rewound %s\n", dev->dev_name);
300    }
301 }
302
303 /*
304  * Write and end of file on the tape   
305  */
306 static void weofcmd()
307 {
308    int stat;
309
310    if ((stat = weof_dev(dev, 1)) < 0) {
311       printf("Bad status from weof %d. ERR=%s\n", stat, strerror(dev->dev_errno));
312       return;
313    } else {
314       printf("Wrote EOF to %s\n", dev->dev_name);
315    }
316 }
317
318
319 /* 
320  * Read a record from the tape
321  */
322 static void rrcmd()
323 {
324    char *buf;
325    int stat, len;
326
327    if (!get_cmd("Enter length to read: ")) {
328       return;
329    }
330    len = atoi(cmd);
331    if (len < 0 || len > 1000000) {
332       printf("Bad length entered, using default of 1024 bytes.\n");
333       len = 1024;
334    }
335    buf = (char *)malloc(len);
336    stat = read(fd, buf, len);
337    if (stat > 0 && stat <= len) {
338       errno = 0;
339    }
340    printf("Read of %d bytes gives stat=%d. ERR=%s\n",
341       len, stat, strerror(errno));
342    free(buf);
343 }
344
345 /* 
346  * Write a record to the tape
347  */
348 static void wrcmd()
349 {
350    int stat;
351    int rfd;
352
353    rfd = open("/dev/urandom", O_RDONLY);
354    if (rfd) {
355       read(rfd, dev->buf, dev->buf_len);
356    } else {
357       printf("Cannot open /dev/urandom.\n");
358       return;
359    }
360    printf("Write one block of %u bytes.\n", dev->buf_len);
361    stat = write(dev->fd, dev->buf, dev->buf_len);
362    if (stat != (int)dev->buf_len) {
363       if (stat == -1) {
364          printf("Bad status from write. ERR=%s\n", strerror(errno));
365       } else {
366          printf("Expected to write %d bytes but wrote only %d.\n",
367             dev->buf_len, stat);
368       }
369    }
370 }
371
372
373
374 /*
375  * Scan tape by reading block by block. Report what is
376  * on the tape.  Note, this command does raw reads, and as such
377  * will not work with fixed block size devices.
378  */
379 static void scancmd()
380 {
381    int stat;
382    int blocks, tot_blocks, tot_files;
383    int block_size;
384    uint64_t bytes;
385
386
387    blocks = block_size = tot_blocks = 0;
388    bytes = 0;
389    if (dev->state & ST_EOT) {
390       printf("End of tape\n");
391       return; 
392    }
393    tot_files = dev->file;
394    printf("Starting scan at file %u\n", dev->file);
395    for (;;) {
396       if ((stat = read(dev->fd, buf, sizeof(buf))) < 0) {
397          dev->dev_errno = errno;
398          printf("Bad status from read %d. ERR=%s\n", stat, strerror(dev->dev_errno));
399          if (blocks > 0)
400             printf("%d block%s of %d bytes in file %d\n",        
401                     blocks, blocks>1?"s":"", block_size, dev->file);
402          return;
403       }
404       if (stat != block_size) {
405          if (blocks > 0) {
406             printf("%d block%s of %d bytes in file %d\n", 
407                  blocks, blocks>1?"s":"", block_size, dev->file);
408             blocks = 0;
409          }
410          block_size = stat;
411       }
412       if (stat == 0) {                /* EOF */
413          printf("End of File mark.\n");
414          /* Two reads of zero means end of tape */
415          if (dev->state & ST_EOF)
416             dev->state |= ST_EOT;
417          else {
418             dev->state |= ST_EOF;
419             dev->file++;
420          }
421          if (dev->state & ST_EOT) {
422             printf("End of tape\n");
423             break;
424          }
425       } else {                        /* Got data */
426          dev->state &= ~ST_EOF;
427          blocks++;
428          tot_blocks++;
429          bytes += stat;
430       }
431    }
432    tot_files = dev->file - tot_files;
433    printf("Total files=%d, blocks=%d, bytes = %d\n", tot_files, tot_blocks, 
434       (int)bytes);                         
435 }
436
437
438 static void rawfill_cmd()
439 {
440    int stat;
441    int rfd;
442    uint32_t block_num = 0;
443    uint32_t *p;
444    int my_errno;
445
446    rfd = open("/dev/urandom", O_RDONLY);
447    if (rfd) {
448       read(rfd, dev->buf, dev->buf_len);
449    } else {
450       printf("Cannot open /dev/urandom.\n");
451       return;
452    }
453    p = (uint32_t *)dev->buf;
454    printf("Begin writing blocks of %u bytes.\n", dev->buf_len);
455    for ( ;; ) {
456       *p = block_num;
457       stat = write(dev->fd, dev->buf, dev->buf_len);
458       if (stat == (int)dev->buf_len) {
459          if ((block_num++ % 100) == 0) {
460             printf("+");
461             fflush(stdout);
462          }
463          continue;
464       }
465       break;
466    }
467    my_errno = errno;
468    printf("\n");
469    weofcmd();
470    printf("Write failed.  Last block written=%d. stat=%d ERR=%s\n", (int)block_num, stat,
471       strerror(my_errno));
472
473 }
474
475 /* Strip any trailing junk from the command */
476 void strip_trailing_junk(char *cmd)
477 {
478    char *p;
479    p = cmd + strlen(cmd) - 1;
480
481    /* strip trailing junk from command */
482    while ((p >= cmd) && (*p == '\n' || *p == '\r' || *p == ' '))
483       *p-- = 0;
484 }
485
486 /* folded search for string - case insensitive */
487 int
488 fstrsch(char *a, char *b)   /* folded case search */
489 {
490    register char *s1,*s2;
491    register char c1=0, c2=0;
492
493    s1=a;
494    s2=b;
495    while (*s1) {                      /* do it the fast way */
496       if ((*s1++ | 0x20) != (*s2++ | 0x20))
497          return 0;                    /* failed */
498    }
499    while (*a) {                       /* do it over the correct slow way */
500       if (isupper(c1 = *a)) {
501          c1 = tolower((int)c1);
502       }
503       if (isupper(c2 = *b)) {
504          c2 = tolower((int)c2);
505       }
506       if (c1 != c2) {
507          return 0;
508       }
509       a++;
510       b++;
511    }
512    return 1;
513 }
514    
515
516 struct cmdstruct { char *key; void (*func)(); char *help; }; 
517 static struct cmdstruct commands[] = {
518  {"help",       helpcmd,      "print this command"},
519  {"quit",       quitcmd,      "quit tapetest"},   
520  {"rawfill",    rawfill_cmd,  "use write() to fill tape"},
521  {"rewind",     rewindcmd,    "rewind the tape"},
522  {"rr",         rrcmd,        "raw read the tape"},
523  {"wr",         wrcmd,        "raw write one block to the tape"},
524  {"scan",       scancmd,      "read() tape block by block to EOT and report"}, 
525  {"weof",       weofcmd,      "write an EOF on the tape"},
526              };
527 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
528
529 static void
530 do_tape_cmds()
531 {
532    unsigned int i;
533    int found;
534
535    while (get_cmd("*")) {
536       found = 0;
537       for (i=0; i<comsize; i++)       /* search for command */
538          if (fstrsch(cmd,  commands[i].key)) {
539             (*commands[i].func)();    /* go execute command */
540             found = 1;
541             break;
542          }
543       if (!found) {
544          printf("%s is an illegal command\n", cmd);
545       }
546       if (quit) {
547          break;
548       }
549    }
550 }
551
552 static void helpcmd()
553 {
554    unsigned int i;
555    usage();
556    printf("  Command    Description\n  =======    ===========\n");
557    for (i=0; i<comsize; i++)
558       printf("  %-10s %s\n", commands[i].key, commands[i].help);
559    printf("\n");
560 }
561
562 static void usage()
563 {
564    fprintf(stderr,
565 "Usage: tapetest [-d debug_level] [device_name]\n"
566 "       -dnn        set debug level to nn\n"
567 "       -?          print this message.\n"  
568 "\n");
569
570 }
571
572
573
574 /*      
575  * Get next input command from terminal.  This
576  * routine is REALLY primitive, and should be enhanced
577  * to have correct backspacing, etc.
578  */
579 int 
580 get_cmd(char *prompt)
581 {
582    int i = 0;
583    int ch;
584    fprintf(stdout, prompt);
585
586    /* We really should turn off echoing and pretty this
587     * up a bit.
588     */
589    cmd[i] = 0;
590    while ((ch = fgetc(stdin)) != EOF) { 
591       if (ch == '\n') {
592          strip_trailing_junk(cmd);
593          return 1;
594       } else if (ch == 4 || ch == 0xd3 || ch == 0x8) {
595          if (i > 0)
596             cmd[--i] = 0;
597          continue;
598       } 
599          
600       cmd[i++] = ch;
601       cmd[i] = 0;
602    }
603    quit = 1;
604    return 0;
605 }