2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998 University of Maryland at College Park
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of U.M. not be used in advertising or
11 * publicity pertaining to distribution of the software without specific,
12 * written prior permission. U.M. makes no representations about the
13 * suitability of this software for any purpose. It is provided "as is"
14 * without express or implied warranty.
16 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 * Author: James da Silva, Systems Design and Analysis Group
24 * Computer Science Department
25 * University of Maryland at College Park
29 * $Id: tapeio.c,v 1.57 2006/07/06 15:04:18 martinea Exp $
31 * implements generic tape I/O functions
36 #include "fileheader.h"
43 #include "output-tape.h"
44 #include "output-null.h"
45 #include "output-rait.h"
46 #include "output-file.h"
48 static struct virtualtape {
50 int (*xxx_tape_access)(char *, int);
51 int (*xxx_tape_open)(char *, int, mode_t);
52 int (*xxx_tape_stat)(char *, struct stat *);
53 int (*xxx_tapefd_close)(int);
54 int (*xxx_tapefd_fsf)(int, off_t);
55 ssize_t (*xxx_tapefd_read)(int, void *, size_t);
56 int (*xxx_tapefd_rewind)(int);
57 void (*xxx_tapefd_resetofs)(int);
58 int (*xxx_tapefd_unload)(int);
59 int (*xxx_tapefd_status)(int, struct am_mt_status *);
60 int (*xxx_tapefd_weof)(int, off_t);
61 ssize_t (*xxx_tapefd_write)(int, const void *, size_t);
62 int (*xxx_tapefd_can_fork)(int);
64 /* note: "tape" has to be the first entry because it is the
65 ** default if no prefix match is found.
67 {"tape", tape_tape_access, tape_tape_open, tape_tape_stat,
68 tape_tapefd_close, tape_tapefd_fsf,
69 tape_tapefd_read, tape_tapefd_rewind, tape_tapefd_resetofs,
70 tape_tapefd_unload, tape_tapefd_status, tape_tapefd_weof,
71 tape_tapefd_write, tape_tapefd_can_fork },
72 {"null", null_tape_access, null_tape_open, null_tape_stat,
73 null_tapefd_close, null_tapefd_fsf,
74 null_tapefd_read, null_tapefd_rewind, null_tapefd_resetofs,
75 null_tapefd_unload, null_tapefd_status, null_tapefd_weof,
76 null_tapefd_write, null_tapefd_can_fork },
77 {"rait", rait_access, rait_tape_open, rait_stat,
78 rait_close, rait_tapefd_fsf,
79 rait_read, rait_tapefd_rewind, rait_tapefd_resetofs,
80 rait_tapefd_unload, rait_tapefd_status, rait_tapefd_weof,
81 rait_write, rait_tapefd_can_fork },
82 {"file", file_tape_access, file_tape_open, file_tape_stat,
83 file_tapefd_close, file_tapefd_fsf,
84 file_tapefd_read, file_tapefd_rewind, file_tapefd_resetofs,
85 file_tapefd_unload, file_tapefd_status, file_tapefd_weof,
86 file_tapefd_write, file_tapefd_can_fork },
87 {NULL, NULL, NULL, NULL,
94 static struct tape_info {
106 static struct tape_info **tape_info_p = &tape_info;
108 static size_t tape_info_count = 0;
110 static char *errstr = NULL;
112 static void tape_info_init(void *ptr);
113 static int name2slot(char *name, char **ntrans);
116 * Additional initialization function for tape_info table.
123 struct tape_info *t = ptr;
132 * Convert the "name" part of a device to a vtape slot.
144 if(0 != (pc = strchr(name, ':'))) {
145 len = (size_t)(pc - name);
146 for( i = 0 ; vtable[i].prefix && vtable[i].prefix[0]; i++ ) {
147 if(0 == strncmp(vtable[i].prefix, name , len)
148 && '\0' == vtable[i].prefix[len]) {
159 * Routines for parsing a device name.
163 * Initialize parsing. The text in the "dev" parameter will be altered,
164 * so a copy should be passed to us.
178 *dev_left = *dev_right = *dev_next = NULL; /* defensive coding */
181 * See if there is a '{' and find the matching '}'.
183 if((*dev_next = p = strchr(dev, '{')) != NULL) {
188 while((ch != '\0') && (ch != '{') && (ch != '}'))
192 * Did not find a matching '}'.
197 } else if(ch == '{') {
199 } else if(ch == '}') {
203 if(strchr(p, '{') != NULL || strchr(p, '}') != NULL) {
206 return -1; /* only one list allowed */
208 *dev_left = dev; /* text before the '{' */
209 **dev_next = '\0'; /* zap the '{' */
210 (*dev_next)++; /* point to the first name */
211 p[-1] = '\0'; /* zap the '}' */
212 *dev_right = p; /* text after the '}' */
215 * Arrange to return just one name.
218 *dev_left = *dev_right = "";
224 * Return the next device name. A dynamic area is returned that the
225 * caller is responsible for freeing.
239 p = next = *dev_next; /* remember the start point */
243 while((ch != '\0') && (ch != '{') && (ch != '}') && (ch != ','))
247 * Found the end of a name.
251 return NULL; /* end of the list */
253 p--; /* point to the null byte */
255 } else if(ch == '{') {
257 } else if(ch == '}') {
261 } while(depth != 0 || ch != ',');
263 p[-1] = '\0'; /* zap the ',' */
265 *dev_next = p; /* set up for the next call */
266 return vstralloc(dev_left, next, dev_right, NULL);
270 * The following functions get/set fields in the tape_info structure.
271 * To allow them to be called (e.g. set) from lower level open functions
272 * started by tape_open, we check and allocate the tape_info structure
273 * here as well as in tape_open.
280 amtable_alloc((void **)tape_info_p,
286 if(tape_info[fd].master_fd != -1)
287 return tapefd_getinfo_host(tape_info[fd].master_fd);
288 return tape_info[fd].host;
296 amtable_alloc((void **)tape_info_p,
302 amfree(tape_info[fd].host);
304 tape_info[fd].host = stralloc(v);
312 amtable_alloc((void **)tape_info_p,
318 if(tape_info[fd].master_fd != -1)
319 return tapefd_getinfo_disk(tape_info[fd].master_fd);
320 return tape_info[fd].disk;
328 amtable_alloc((void **)tape_info_p,
334 amfree(tape_info[fd].disk);
336 tape_info[fd].disk = stralloc(v);
341 tapefd_getinfo_level(
344 amtable_alloc((void **)tape_info_p,
350 if(tape_info[fd].master_fd != -1)
351 return tapefd_getinfo_level(tape_info[fd].master_fd);
352 return tape_info[fd].level;
356 tapefd_setinfo_level(
360 amtable_alloc((void **)tape_info_p,
366 tape_info[fd].level = v;
370 tapefd_getinfo_datestamp(
373 amtable_alloc((void **)tape_info_p,
379 return tape_info[fd].datestamp;
383 tapefd_setinfo_datestamp(
387 amtable_alloc((void **)tape_info_p,
393 tape_info[fd].datestamp = newstralloc(tape_info[fd].datestamp, v);
397 tapefd_getinfo_length(
400 amtable_alloc((void **)tape_info_p,
406 return tape_info[fd].length;
410 tapefd_setinfo_length(
414 amtable_alloc((void **)tape_info_p,
420 tape_info[fd].length = v;
424 tapefd_getinfo_tapetype(
427 amtable_alloc((void **)tape_info_p,
433 return tape_info[fd].tapetype;
437 tapefd_setinfo_tapetype(
441 amtable_alloc((void **)tape_info_p,
447 tape_info[fd].tapetype = newstralloc(tape_info[fd].tapetype, v);
451 tapefd_getinfo_fake_label(
454 amtable_alloc((void **)tape_info_p,
460 return tape_info[fd].fake_label;
464 tapefd_setinfo_fake_label(
468 amtable_alloc((void **)tape_info_p,
474 tape_info[fd].fake_label = v;
478 tapefd_getinfo_ioctl_fork(
481 amtable_alloc((void **)tape_info_p,
487 return tape_info[fd].ioctl_fork;
491 tapefd_setinfo_ioctl_fork(
495 amtable_alloc((void **)tape_info_p,
501 tape_info[fd].ioctl_fork = v;
505 tapefd_set_master_fd(
509 amtable_alloc((void **)tape_info_p,
515 tape_info[fd].master_fd = master_fd;
520 * The normal tape operation functions.
531 vslot = name2slot(filename, &tname);
532 return vtable[vslot].xxx_tape_access(tname, mode);
543 vslot = name2slot(filename, &tname);
544 return vtable[vslot].xxx_tape_stat(tname, buf);
559 mask = (mode_t)va_arg(ap, int);
562 vslot = name2slot(filename, &tname);
563 if((fd = vtable[vslot].xxx_tape_open(tname, mode, mask)) >= 0) {
564 amtable_alloc((void **)tape_info_p,
571 * It is possible to recurse in the above open call and come
572 * back here twice for the same file descriptor. Set the vtape
573 * index only if it is not already set, i.e. the first call wins.
575 if(tape_info[fd].vtape_index < 0) {
576 tape_info[fd].vtape_index = vslot;
589 if ((fd < 0) || ((size_t)fd >= tape_info_count)
590 || ((vslot = tape_info[fd].vtape_index) < 0)) {
595 vslot = tape_info[fd].vtape_index;
596 if((res = vtable[vslot].xxx_tapefd_close(fd)) == 0) {
597 amfree(tape_info[fd].host);
598 amfree(tape_info[fd].disk);
599 amfree(tape_info[fd].datestamp);
600 amfree(tape_info[fd].tapetype);
601 memset(tape_info + fd, 0, SIZEOF(*tape_info));
602 tape_info_init((void *)(tape_info + fd));
613 if ((fd < 0) || ((size_t)fd >= tape_info_count)
614 || (tape_info[fd].vtape_index < 0)) {
619 vslot = tape_info[fd].vtape_index;
620 return vtable[vslot].xxx_tapefd_can_fork(fd);
630 if ((fd < 0) || ((size_t)fd >= tape_info_count)
631 || (tape_info[fd].vtape_index < 0)) {
636 vslot = tape_info[fd].vtape_index;
637 return vtable[vslot].xxx_tapefd_fsf(fd, count);
646 if ((fd < 0) || ((size_t)fd >= tape_info_count)
647 || (tape_info[fd].vtape_index < 0)) {
652 vslot = tape_info[fd].vtape_index;
653 return vtable[vslot].xxx_tapefd_rewind(fd);
662 if ((fd < 0) || ((size_t)fd >= tape_info_count)
663 || (tape_info[fd].vtape_index < 0)) {
664 errno = EBADF; /* not that it matters */
668 vslot = tape_info[fd].vtape_index;
669 vtable[vslot].xxx_tapefd_resetofs(fd);
678 if ((fd < 0) || ((size_t)fd >= tape_info_count)
679 || (tape_info[fd].vtape_index < 0)) {
684 vslot = tape_info[fd].vtape_index;
685 return vtable[vslot].xxx_tapefd_unload(fd);
691 struct am_mt_status *stat)
695 if ((fd < 0) || ((size_t)fd >= tape_info_count)
696 || (tape_info[fd].vtape_index < 0)) {
701 vslot = tape_info[fd].vtape_index;
702 return vtable[vslot].xxx_tapefd_status(fd, stat);
712 if ((fd < 0) || ((size_t)fd >= tape_info_count)
713 || (tape_info[fd].vtape_index < 0)) {
718 vslot = tape_info[fd].vtape_index;
719 return vtable[vslot].xxx_tapefd_weof(fd, count);
731 if ((fd < 0) || ((size_t)fd >= tape_info_count)
732 || (tape_info[fd].vtape_index < 0)) {
737 vslot = tape_info[fd].vtape_index;
738 return vtable[vslot].xxx_tapefd_read(fd, buffer, count);
749 if ((fd < 0) || ((size_t)fd >= tape_info_count)
750 || (tape_info[fd].vtape_index < 0)) {
755 vslot = tape_info[fd].vtape_index;
756 return vtable[vslot].xxx_tapefd_write(fd, buffer, count);
766 if((fd = tape_open(devname, O_RDONLY)) < 0) {
767 r = errstr = newvstralloc(errstr,
768 "tape_rewind: tape open: ",
773 } else if(tapefd_rewind(fd) == -1) {
774 r = errstr = newvstralloc(errstr,
775 "tape_rewind: rewinding tape: ",
794 if((fd = tape_open(devname, O_RDONLY)) < 0) {
795 r = errstr = newvstralloc(errstr,
796 "tape_unload: tape open: ",
801 } else if(tapefd_unload(fd) == -1) {
802 r = errstr = newvstralloc(errstr,
803 "tape_unload: unloading tape: ",
821 char count_str[NUM_STR_SIZE];
824 if((fd = tape_open(devname, O_RDONLY)) < 0) {
825 r = errstr = newvstralloc(errstr,
826 "tape_fsf: tape open: ",
831 } else if(tapefd_fsf(fd, count) == -1) {
832 snprintf(count_str, SIZEOF(count_str), OFF_T_FMT,
833 (OFF_T_FMT_TYPE)count);
834 r = errstr = newvstralloc(errstr,
837 "file", (count == 1) ? "" : "s",
848 /* Reads the tape label, like you expect. If failure, returns an error
849 string. If the tape might not be an Amanda tape, the returned
850 string will start with NOT_AMANDA_TAPE_MSG. */
866 buflen = MAX_TAPE_BLOCK_BYTES;
867 buffer = alloc(buflen + 1);
869 if(tapefd_getinfo_fake_label(fd)) {
870 *datestamp = stralloc("X");
871 *label = stralloc(FAKE_LABEL);
872 } else if(tapefd_rewind(fd) == -1) {
873 r = stralloc2("rewinding tape: ", strerror(errno));
874 } else if((rc = tapefd_read(fd, buffer, buflen)) == -1) {
875 r = vstralloc(NOT_AMANDA_TAPE_MSG, " (",
876 strerror(errno), ")", NULL);
877 } else if (rc == 0) {
878 r = stralloc2(NOT_AMANDA_TAPE_MSG, " (Read 0 bytes)");
880 /* make sure buffer is null-terminated */
883 parse_file_header(buffer, &file, (size_t)rc);
884 if(file.type != F_TAPESTART) {
885 r = stralloc(NOT_AMANDA_TAPE_MSG);
887 *datestamp = stralloc(file.datestamp);
888 *label = stralloc(file.name);
893 errstr = newvstralloc(errstr, r, NULL);
906 if((fd = tape_open(devname, O_RDONLY)) < 0) {
907 r = vstralloc("tape_rdlabel: tape open: ",
913 r = tapefd_rdlabel(fd, datestamp, label);
919 errstr = newvstralloc(errstr, r, NULL);
935 if(tapefd_rewind(fd) == -1) {
936 r = errstr = newstralloc2(errstr, "rewinding tape: ", strerror(errno));
939 file.type = F_TAPESTART;
940 strncpy(file.datestamp, datestamp, SIZEOF(file.datestamp) - 1);
941 file.datestamp[SIZEOF(file.datestamp) - 1] = '\0';
942 strncpy(file.name, label, SIZEOF(file.name) - 1);
943 file.name[SIZEOF(file.name) - 1] = '\0';
944 buffer = alloc(size);
945 file.blocksize = size;
946 build_header(buffer, &file, size);
947 tapefd_setinfo_host(fd, NULL);
948 tapefd_setinfo_disk(fd, label);
949 tapefd_setinfo_level(fd, -1);
950 if((rc = tapefd_write(fd, buffer, size)) != (ssize_t)size) {
951 r = errstr = newstralloc2(errstr,
953 (rc != -1) ? "short write"
971 if((fd = tape_open(devname, O_WRONLY)) < 0) {
972 r = errstr = newstralloc2(errstr,
974 (errno == EACCES) ? "tape is write-protected"
976 } else if(tapefd_wrlabel(fd, datestamp, label, size) != NULL) {
997 file.type = F_TAPEEND;
998 strncpy(file.datestamp, datestamp, SIZEOF(file.datestamp) - 1);
999 file.datestamp[SIZEOF(file.datestamp) - 1] = '\0';
1000 buffer = alloc(size);
1001 file.blocksize = size;
1002 build_header(buffer, &file, size);
1003 tapefd_setinfo_host(fd, NULL);
1004 tapefd_setinfo_disk(fd, "TAPEEND");
1005 tapefd_setinfo_level(fd, -1);
1007 if((rc = tapefd_write(fd, buffer, size)) != (ssize_t)size) {
1008 r = errstr = newstralloc2(errstr, "writing endmark: ",
1009 (rc != -1) ? "short write" : strerror(errno));
1025 if((fd = tape_open(devname, O_WRONLY)) < 0) {
1026 r = errstr = newstralloc2(errstr,
1027 "writing endmark: ",
1028 (errno == EACCES) ? "tape is write-protected"
1030 } else if(tapefd_wrendmark(fd, datestamp, size) != NULL) {
1046 /* first, make sure the file exists and the permissions are right */
1048 if(tape_access(devname, R_OK|W_OK) == -1) {
1049 r = errstr = newstralloc(errstr, strerror(errno));
1050 } else if((fd = tape_open(devname, O_WRONLY)) < 0) {
1051 r = errstr = newstralloc(errstr,
1052 (errno == EACCES) ? "tape write-protected"
1064 * The following test program may be used to exercise I/O patterns through
1065 * the tapeio interface. Commands may either be on the command line or
1066 * read from stdin (e.g. for a test suite).
1072 /* If the C library does not define random(), try to use rand() by
1073 defining USE_RAND, but then make sure you are not using hardware
1074 compression, because the low-order bits of rand() may not be that
1076 #define random() rand()
1077 #define srandom(seed) srand(seed)
1085 fprintf(stderr, " ?|help\n");
1086 fprintf(stderr, " open [\"file\"|$TAPE [\"mode\":O_RDONLY]]\n");
1087 fprintf(stderr, " read [\"records\":\"all\"]\n");
1088 fprintf(stderr, " write [\"records\":1] [\"file#\":\"+\"] [\"record#\":\"+\"] [\"host\"] [\"disk\"] [\"level\"]\n");
1089 fprintf(stderr, " eof|weof [\"count\":1]\n");
1090 fprintf(stderr, " fsf [\"count\":1]\n");
1091 fprintf(stderr, " rewind\n");
1092 fprintf(stderr, " unload\n");
1098 fprintf(stderr, "usage: %s [-c cmd [args] [%% cmd [args] ...]]\n", pgm);
1102 #define TEST_BLOCKSIZE (32 * 1024)
1104 #define MAX_TOKENS 10
1108 static char *token_area[MAX_TOKENS + 1];
1109 static char **token;
1110 static int token_count;
1113 static off_t current_file = (off_t)0;
1114 static off_t current_record = (off_t)0;
1116 static int have_length = 0;
1117 static int length = (off_t)0;
1119 static int show_timestamp = 0;
1121 char write_buf[TEST_BLOCKSIZE];
1130 || (token_count >= 2 && strcmp(token[1], "$TAPE") == 0)) {
1131 if((file = getenv("TAPE")) == NULL) {
1132 fprintf(stderr, "tape_open: no file name and $TAPE not set\n");
1138 if(token_count > 2) {
1139 mode = atoi(token[2]);
1144 fprintf(stderr, "tapefd_open(\"%s\", %d): ", file, mode);
1145 if((fd = tape_open(file, mode, 0644)) < 0) {
1148 fprintf(stderr, "%d (OK)\n", fd);
1150 tapefd_setinfo_length(fd, length);
1160 fprintf(stderr, "tapefd_close(): ");
1161 if((result = tapefd_close(fd)) < 0) {
1164 fprintf(stderr, "%d (OK)\n", result);
1172 off_t count = (off_t)0;
1174 char buf[SIZEOF(write_buf)];
1181 if(token_count > 1 && strcmp(token[1], "all") != 0) {
1182 count = OFF_T_ATOI(token[1]);
1187 for(i = 0; (! have_count) || (i < count); i++) {
1188 fprintf(stderr, "tapefd_read(" OFF_T_FMT "): ", (OFF_T_FMT_TYPE)i);
1189 if((result = tapefd_read(fd, buf, SIZEOF(buf))) < 0) {
1192 } else if(result == 0) {
1193 fprintf(stderr, SSIZE_T_FMT" (EOF)\n", result);
1195 * If we were not given a count, EOF breaks the loop, otherwise
1196 * we keep trying (to test read after EOF handling).
1202 if(result == (ssize_t)sizeof(buf)) {
1209 * If the amount read is really short, we may refer to junk
1210 * when displaying the record data, but things are pretty
1211 * well screwed up at this point anyway so it is not worth
1212 * the effort to deal with.
1215 SSIZE_T_FMT " (%s): file %d: record %d",
1220 if(show_timestamp) {
1222 tm = localtime(&then);
1224 ": %04d/%02d/%02d %02d:%02d:%02d\n",
1232 fputc('\n', stderr);
1248 if(token_count > 1) {
1249 count = OFF_T_ATOI(token[1]);
1254 if(token_count > 2 && strcmp(token[2], "+") != 0) {
1255 current_file = OFF_T_ATOI(token[2]);
1258 if(token_count > 3 && strcmp(token[3], "+") != 0) {
1259 current_record = OFF_T_ATOI(token[3]);
1262 if(token_count > 4 && token[4][0] != '\0') {
1263 tapefd_setinfo_host(fd, token[4]);
1266 if(token_count > 5 && token[5][0] != '\0') {
1267 tapefd_setinfo_disk(fd, token[5]);
1270 if(token_count > 6 && token[6][0] != '\0') {
1271 tapefd_setinfo_level(fd, atoi(token[6]));
1274 p = (off_t *)write_buf;
1277 tm = localtime(&now);
1278 for(i = 0; i < count; i++, (current_record += (off_t)1)) {
1279 p[0] = current_file;
1280 p[1] = current_record;
1281 fprintf(stderr, "tapefd_write(" OFF_T_FMT "): ", i);
1282 if((result = tapefd_write(fd, write_buf, SIZEOF(write_buf))) < 0) {
1286 if(result == (ssize_t)sizeof(write_buf)) {
1292 "%d (%s): file " OFF_T_FMT ": record " OFF_T_FMT,
1297 if(show_timestamp) {
1299 ": %04d/%02d/%02d %02d:%02d:%02d\n",
1307 fputc('\n', stderr);
1318 if(token_count > 1) {
1319 count = OFF_T_ATOI(token[1]);
1324 fprintf(stderr, "tapefd_fsf(" OFF_T_FMT "): ", (OFF_T_FMT_TYPE)count);
1325 if((result = tapefd_fsf(fd, count)) < 0) {
1328 fprintf(stderr, "%d (OK)\n", result);
1329 current_file += count;
1330 current_record = (off_t)0;
1340 if(token_count > 1) {
1341 count = OFF_T_ATOI(token[1]);
1346 fprintf(stderr, "tapefd_weof(" OFF_T_FMT "): ", count);
1347 if((result = tapefd_weof(fd, count)) < 0) {
1350 fprintf(stderr, "%d (OK)\n", result);
1351 current_file += count;
1352 current_record = (off_t)0;
1361 fprintf(stderr, "tapefd_rewind(): ");
1362 if((result = tapefd_rewind(fd)) < 0) {
1365 fprintf(stderr, "%d (OK)\n", result);
1366 current_file = (off_t)0;
1367 current_record = (off_t)0;
1376 fprintf(stderr, "tapefd_unload(): ");
1377 if((result = tapefd_unload(fd)) < 0) {
1380 fprintf(stderr, "%d (OK)\n", result);
1381 current_file = (off_t)-1;
1382 current_record = (off_t)-1;
1391 { "?", 0, do_help },
1392 { "help", 0, do_help },
1393 { "eof", 0, do_weof },
1394 { "weof", 0, do_weof },
1395 { "fsf", 0, do_fsf },
1396 { "rewind", 0, do_rewind },
1397 { "offline", 0, do_unload },
1398 { "open", 0, do_open },
1399 { "close", 0, do_close },
1400 { "read", 0, do_read },
1401 { "write", 0, do_write },
1418 /* Don't die when child closes pipe */
1419 signal(SIGPIPE, SIG_IGN);
1421 if((pgm = strrchr(argv[0], '/')) != NULL) {
1428 * Compute the minimum abbreviation for each command.
1430 for(i = 0; cmd[i].name; i++) {
1431 cmd[i].min_chars = 1;
1433 for(j = 0; cmd[j].name; j++) {
1437 if(0 == strncmp(cmd[i].name, cmd[j].name, cmd[i].min_chars)) {
1441 if(0 == cmd[j].name) {
1449 * Process the command line flags.
1451 while((ch = getopt(argc, argv, "hcl:t")) != EOF) {
1458 length = OFF_T_ATOI(optarg);
1461 switch(optarg[j-1] ) {
1463 case 'b': length /= (off_t)2; break;
1464 case 'M': length *= (off_t)1024; break;
1465 default: length /= (off_t)1024; break;
1468 length /= (off_t)1024;
1482 * Initialize the write buffer.
1486 for(j = 0; j < (int)SIZEOF(write_buf); j++) {
1487 write_buf[j] = (char)random();
1493 token = token_area + 1;
1494 token_area[0] = ""; /* if cmdline */
1497 for(token_count = 1;
1498 token_count < (int)(SIZEOF(token_area) / SIZEOF(token_area[0]))
1500 token_count++, optind++) {
1501 if(strcmp(argv[optind], "%") == 0) {
1505 token_area[token_count] = argv[optind];
1508 if(token_count == 0 && optind >= argc) {
1512 if((line = areads(0)) == NULL) {
1515 if((s = strchr(line, '#')) != NULL) {
1518 s = line + strlen(line) - 1;
1519 while(s >= line && isspace(*s)) {
1522 token_count = split(line,
1524 SIZEOF(token_area) / SIZEOF(token_area[0]),
1530 * Truncate tokens at first comment indicator, then test for
1533 for(i = 0; i < token_count; i++) {
1534 if(token[i][0] == '#') {
1539 if(token_count <= 0) {
1540 continue; /* blank/comment input line */
1544 * Find the command to run, the do it.
1546 j = strlen(token[0]);
1547 for(i = 0; cmd[i].name; i++) {
1548 if(strncmp(cmd[i].name, token[0], j) == 0
1549 && j >= cmd[i].min_chars) {
1553 if(cmd[i].name == NULL) {
1554 fprintf(stderr, "%s: unknown command: %s\n", pgm, token[0]);