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
37 #include "fileheader.h"
44 #include "output-tape.h"
45 #include "output-null.h"
46 #include "output-rait.h"
47 #include "output-file.h"
49 static struct virtualtape {
51 int (*xxx_tape_access)(char *, int);
52 int (*xxx_tape_open)(char *, int, mode_t);
53 int (*xxx_tape_stat)(char *, struct stat *);
54 int (*xxx_tapefd_close)(int);
55 int (*xxx_tapefd_fsf)(int, off_t);
56 ssize_t (*xxx_tapefd_read)(int, void *, size_t);
57 int (*xxx_tapefd_rewind)(int);
58 void (*xxx_tapefd_resetofs)(int);
59 int (*xxx_tapefd_unload)(int);
60 int (*xxx_tapefd_status)(int, struct am_mt_status *);
61 int (*xxx_tapefd_weof)(int, off_t);
62 ssize_t (*xxx_tapefd_write)(int, const void *, size_t);
63 int (*xxx_tapefd_can_fork)(int);
65 /* note: "tape" has to be the first entry because it is the
66 ** default if no prefix match is found.
68 {"tape", tape_tape_access, tape_tape_open, tape_tape_stat,
69 tape_tapefd_close, tape_tapefd_fsf,
70 tape_tapefd_read, tape_tapefd_rewind, tape_tapefd_resetofs,
71 tape_tapefd_unload, tape_tapefd_status, tape_tapefd_weof,
72 tape_tapefd_write, tape_tapefd_can_fork },
73 {"null", null_tape_access, null_tape_open, null_tape_stat,
74 null_tapefd_close, null_tapefd_fsf,
75 null_tapefd_read, null_tapefd_rewind, null_tapefd_resetofs,
76 null_tapefd_unload, null_tapefd_status, null_tapefd_weof,
77 null_tapefd_write, null_tapefd_can_fork },
78 {"rait", rait_access, rait_tape_open, rait_stat,
79 rait_close, rait_tapefd_fsf,
80 rait_read, rait_tapefd_rewind, rait_tapefd_resetofs,
81 rait_tapefd_unload, rait_tapefd_status, rait_tapefd_weof,
82 rait_write, rait_tapefd_can_fork },
83 {"file", file_tape_access, file_tape_open, file_tape_stat,
84 file_tapefd_close, file_tapefd_fsf,
85 file_tapefd_read, file_tapefd_rewind, file_tapefd_resetofs,
86 file_tapefd_unload, file_tapefd_status, file_tapefd_weof,
87 file_tapefd_write, file_tapefd_can_fork },
88 {NULL, NULL, NULL, NULL,
95 static struct tape_info {
107 static struct tape_info **tape_info_p = &tape_info;
109 static size_t tape_info_count = 0;
111 static char *errstr = NULL;
113 static void tape_info_init(void *ptr);
114 static int name2slot(char *name, char **ntrans);
117 * Additional initialization function for tape_info table.
124 struct tape_info *t = ptr;
133 * Convert the "name" part of a device to a vtape slot.
145 if(0 != (pc = strchr(name, ':'))) {
146 len = (size_t)(pc - name);
147 for( i = 0 ; vtable[i].prefix && vtable[i].prefix[0]; i++ ) {
148 if(0 == strncmp(vtable[i].prefix, name , len)
149 && '\0' == vtable[i].prefix[len]) {
160 * Routines for parsing a device name.
164 * Initialize parsing. The text in the "dev" parameter will be altered,
165 * so a copy should be passed to us.
179 *dev_left = *dev_right = *dev_next = NULL; /* defensive coding */
182 * See if there is a '{' and find the matching '}'.
184 if((*dev_next = p = strchr(dev, '{')) != NULL) {
189 while((ch != '\0') && (ch != '{') && (ch != '}'))
193 * Did not find a matching '}'.
198 } else if(ch == '{') {
200 } else if(ch == '}') {
204 if(strchr(p, '{') != NULL || strchr(p, '}') != NULL) {
207 return -1; /* only one list allowed */
209 *dev_left = dev; /* text before the '{' */
210 **dev_next = '\0'; /* zap the '{' */
211 (*dev_next)++; /* point to the first name */
212 p[-1] = '\0'; /* zap the '}' */
213 *dev_right = p; /* text after the '}' */
216 * Arrange to return just one name.
219 *dev_left = *dev_right = "";
225 * Return the next device name. A dynamic area is returned that the
226 * caller is responsible for freeing.
240 p = next = *dev_next; /* remember the start point */
244 while((ch != '\0') && (ch != '{') && (ch != '}') && (ch != ','))
248 * Found the end of a name.
252 return NULL; /* end of the list */
254 p--; /* point to the null byte */
256 } else if(ch == '{') {
258 } else if(ch == '}') {
262 } while(depth != 0 || ch != ',');
264 p[-1] = '\0'; /* zap the ',' */
266 *dev_next = p; /* set up for the next call */
267 return vstralloc(dev_left, next, dev_right, NULL);
271 * The following functions get/set fields in the tape_info structure.
272 * To allow them to be called (e.g. set) from lower level open functions
273 * started by tape_open, we check and allocate the tape_info structure
274 * here as well as in tape_open.
281 amtable_alloc((void **)tape_info_p,
287 if(tape_info[fd].master_fd != -1)
288 return tapefd_getinfo_host(tape_info[fd].master_fd);
289 return tape_info[fd].host;
297 amtable_alloc((void **)tape_info_p,
303 amfree(tape_info[fd].host);
305 tape_info[fd].host = stralloc(v);
313 amtable_alloc((void **)tape_info_p,
319 if(tape_info[fd].master_fd != -1)
320 return tapefd_getinfo_disk(tape_info[fd].master_fd);
321 return tape_info[fd].disk;
329 amtable_alloc((void **)tape_info_p,
335 amfree(tape_info[fd].disk);
337 tape_info[fd].disk = stralloc(v);
342 tapefd_getinfo_level(
345 amtable_alloc((void **)tape_info_p,
351 if(tape_info[fd].master_fd != -1)
352 return tapefd_getinfo_level(tape_info[fd].master_fd);
353 return tape_info[fd].level;
357 tapefd_setinfo_level(
361 amtable_alloc((void **)tape_info_p,
367 tape_info[fd].level = v;
371 tapefd_getinfo_datestamp(
374 amtable_alloc((void **)tape_info_p,
380 return tape_info[fd].datestamp;
384 tapefd_setinfo_datestamp(
388 amtable_alloc((void **)tape_info_p,
394 tape_info[fd].datestamp = newstralloc(tape_info[fd].datestamp, v);
398 tapefd_getinfo_length(
401 amtable_alloc((void **)tape_info_p,
407 return tape_info[fd].length;
411 tapefd_setinfo_length(
415 amtable_alloc((void **)tape_info_p,
421 tape_info[fd].length = v;
425 tapefd_getinfo_tapetype(
428 amtable_alloc((void **)tape_info_p,
434 return tape_info[fd].tapetype;
438 tapefd_setinfo_tapetype(
442 amtable_alloc((void **)tape_info_p,
448 tape_info[fd].tapetype = newstralloc(tape_info[fd].tapetype, v);
452 tapefd_getinfo_fake_label(
455 amtable_alloc((void **)tape_info_p,
461 return tape_info[fd].fake_label;
465 tapefd_setinfo_fake_label(
469 amtable_alloc((void **)tape_info_p,
475 tape_info[fd].fake_label = v;
479 tapefd_getinfo_ioctl_fork(
482 amtable_alloc((void **)tape_info_p,
488 return tape_info[fd].ioctl_fork;
492 tapefd_setinfo_ioctl_fork(
496 amtable_alloc((void **)tape_info_p,
502 tape_info[fd].ioctl_fork = v;
506 tapefd_set_master_fd(
510 amtable_alloc((void **)tape_info_p,
516 tape_info[fd].master_fd = master_fd;
521 * The normal tape operation functions.
532 vslot = name2slot(filename, &tname);
533 return vtable[vslot].xxx_tape_access(tname, mode);
544 vslot = name2slot(filename, &tname);
545 return vtable[vslot].xxx_tape_stat(tname, buf);
560 mask = (mode_t)va_arg(ap, int);
563 vslot = name2slot(filename, &tname);
564 if((fd = vtable[vslot].xxx_tape_open(tname, mode, mask)) >= 0) {
565 amtable_alloc((void **)tape_info_p,
572 * It is possible to recurse in the above open call and come
573 * back here twice for the same file descriptor. Set the vtape
574 * index only if it is not already set, i.e. the first call wins.
576 if(tape_info[fd].vtape_index < 0) {
577 tape_info[fd].vtape_index = vslot;
590 if ((fd < 0) || ((size_t)fd >= tape_info_count)
591 || ((vslot = tape_info[fd].vtape_index) < 0)) {
596 vslot = tape_info[fd].vtape_index;
597 if((res = vtable[vslot].xxx_tapefd_close(fd)) == 0) {
598 amfree(tape_info[fd].host);
599 amfree(tape_info[fd].disk);
600 amfree(tape_info[fd].datestamp);
601 amfree(tape_info[fd].tapetype);
602 memset(tape_info + fd, 0, SIZEOF(*tape_info));
603 tape_info_init((void *)(tape_info + fd));
614 if ((fd < 0) || ((size_t)fd >= tape_info_count)
615 || (tape_info[fd].vtape_index < 0)) {
620 vslot = tape_info[fd].vtape_index;
621 return vtable[vslot].xxx_tapefd_can_fork(fd);
631 if ((fd < 0) || ((size_t)fd >= tape_info_count)
632 || (tape_info[fd].vtape_index < 0)) {
637 vslot = tape_info[fd].vtape_index;
638 return vtable[vslot].xxx_tapefd_fsf(fd, count);
647 if ((fd < 0) || ((size_t)fd >= tape_info_count)
648 || (tape_info[fd].vtape_index < 0)) {
653 vslot = tape_info[fd].vtape_index;
654 return vtable[vslot].xxx_tapefd_rewind(fd);
663 if ((fd < 0) || ((size_t)fd >= tape_info_count)
664 || (tape_info[fd].vtape_index < 0)) {
665 errno = EBADF; /* not that it matters */
669 vslot = tape_info[fd].vtape_index;
670 vtable[vslot].xxx_tapefd_resetofs(fd);
679 if ((fd < 0) || ((size_t)fd >= tape_info_count)
680 || (tape_info[fd].vtape_index < 0)) {
685 vslot = tape_info[fd].vtape_index;
686 return vtable[vslot].xxx_tapefd_unload(fd);
692 struct am_mt_status *stat)
696 if ((fd < 0) || ((size_t)fd >= tape_info_count)
697 || (tape_info[fd].vtape_index < 0)) {
702 vslot = tape_info[fd].vtape_index;
703 return vtable[vslot].xxx_tapefd_status(fd, stat);
713 if ((fd < 0) || ((size_t)fd >= tape_info_count)
714 || (tape_info[fd].vtape_index < 0)) {
719 vslot = tape_info[fd].vtape_index;
720 return vtable[vslot].xxx_tapefd_weof(fd, count);
732 if ((fd < 0) || ((size_t)fd >= tape_info_count)
733 || (tape_info[fd].vtape_index < 0)) {
738 vslot = tape_info[fd].vtape_index;
739 return vtable[vslot].xxx_tapefd_read(fd, buffer, count);
750 if ((fd < 0) || ((size_t)fd >= tape_info_count)
751 || (tape_info[fd].vtape_index < 0)) {
756 vslot = tape_info[fd].vtape_index;
757 return vtable[vslot].xxx_tapefd_write(fd, buffer, count);
767 if((fd = tape_open(devname, O_RDONLY)) < 0) {
768 r = errstr = newvstrallocf(errstr,
769 _("tape_rewind: tape open: %s: %s"),
770 devname, strerror(errno));
771 } else if(tapefd_rewind(fd) == -1) {
772 r = errstr = newvstrallocf(errstr,
773 _("tape_rewind: rewinding tape: %s: %s"),
790 if((fd = tape_open(devname, O_RDONLY)) < 0) {
791 r = errstr = newvstrallocf(errstr,
792 _("tape_unload: tape open: %s: %s"),
795 } else if(tapefd_unload(fd) == -1) {
796 r = errstr = newvstrallocf(errstr,
797 _("tape_unload: unloading tape: %s: %s"),
815 if((fd = tape_open(devname, O_RDONLY)) < 0) {
816 r = errstr = newvstrallocf(errstr,
817 _("tape_fsf: tape open: %s: %s"),
820 } else if(tapefd_fsf(fd, count) == -1) {
821 r = errstr = newvstrallocf(errstr,
822 plural(_("tape_fsf: fsf %lld file: %s"),
823 _("tape_fsf: fsf %lld files: %s"),
825 (long long)count, strerror(errno));
833 /* Reads the tape label, like you expect. If failure, returns an error
834 string. If the tape might not be an Amanda tape, the returned
835 string will start with NOT_AMANDA_TAPE_MSG. */
851 buflen = getconf_readblocksize() * 1024;
852 buffer = alloc(buflen + 1);
854 if(tapefd_getinfo_fake_label(fd)) {
855 *datestamp = stralloc("X");
856 *label = stralloc(FAKE_LABEL);
857 } else if(tapefd_rewind(fd) == -1) {
858 r = vstrallocf(_("rewinding tape: %s"), strerror(errno));
859 } else if((rc = tapefd_read(fd, buffer, buflen)) == -1) {
860 r = vstrallocf(_(NOT_AMANDA_TAPE_MSG "(%s)"), strerror(errno));
861 } else if (rc == 0) {
862 r = vstrallocf(_(NOT_AMANDA_TAPE_MSG " (Read 0 bytes)"));
864 /* make sure buffer is null-terminated */
867 parse_file_header(buffer, &file, (size_t)rc);
868 if(file.type != F_TAPESTART) {
869 r = vstrallocf(NOT_AMANDA_TAPE_MSG);
871 *datestamp = stralloc(file.datestamp);
872 *label = stralloc(file.name);
877 errstr = newvstrallocf(errstr, "%s", r);
890 if((fd = tape_open(devname, O_RDONLY)) < 0) {
891 r = vstrallocf(_("tape_rdlabel: tape open: %s: %s"),
895 r = tapefd_rdlabel(fd, datestamp, label);
901 errstr = newvstrallocf(errstr, "%s", r);
917 if(tapefd_rewind(fd) == -1) {
918 r = errstr = newvstrallocf(errstr, _("rewinding tape: %s"),
922 file.type = F_TAPESTART;
923 strncpy(file.datestamp, datestamp, SIZEOF(file.datestamp) - 1);
924 file.datestamp[SIZEOF(file.datestamp) - 1] = '\0';
925 strncpy(file.name, label, SIZEOF(file.name) - 1);
926 file.name[SIZEOF(file.name) - 1] = '\0';
927 file.blocksize = size;
928 buffer = build_header(&file, size);
929 tapefd_setinfo_host(fd, NULL);
930 tapefd_setinfo_disk(fd, label);
931 tapefd_setinfo_level(fd, -1);
932 if((rc = tapefd_write(fd, buffer, size)) != (ssize_t)size) {
934 r = errstr = newvstrallocf(errstr,
935 _("writing label: short write"));
937 r = errstr = newvstrallocf(errstr,
938 _("writing label: %s"), strerror(errno));
956 if((fd = tape_open(devname, O_WRONLY)) < 0) {
957 if (errno == EACCES) {
958 r = errstr = newvstrallocf(errstr,
959 _("writing label: tape is write-protected"));
961 r = errstr = newvstrallocf(errstr,
962 _("writing label: %s"), strerror(errno));
964 } else if(tapefd_wrlabel(fd, datestamp, label, size) != NULL) {
985 file.type = F_TAPEEND;
986 strncpy(file.datestamp, datestamp, SIZEOF(file.datestamp) - 1);
987 file.datestamp[SIZEOF(file.datestamp) - 1] = '\0';
988 file.blocksize = size;
989 buffer = build_header(&file, size);
990 tapefd_setinfo_host(fd, NULL);
991 tapefd_setinfo_disk(fd, "TAPEEND");
992 tapefd_setinfo_level(fd, -1);
994 if((rc = tapefd_write(fd, buffer, size)) != (ssize_t)size) {
996 r = errstr = newvstrallocf(errstr,
997 _("writing endmark: short write"));
999 r = errstr = newvstrallocf(errstr,
1000 _("writing endmark: %s"), strerror(errno));
1017 if((fd = tape_open(devname, O_WRONLY)) < 0) {
1018 if (errno == EACCES) {
1019 r = errstr = newvstrallocf(errstr,
1020 _("writing endmark: tape is write-protected"));
1022 r = errstr = newvstrallocf(errstr,
1023 _("writing endmark: %s"), strerror(errno));
1025 } else if(tapefd_wrendmark(fd, datestamp, size) != NULL) {
1041 /* first, make sure the file exists and the permissions are right */
1043 if(tape_access(devname, R_OK|W_OK) == -1) {
1044 r = errstr = newvstrallocf(errstr, "%s", strerror(errno));
1045 } else if((fd = tape_open(devname, O_WRONLY)) < 0) {
1046 if (errno == EACCES) {
1047 r = errstr = newvstrallocf(errstr, _("tape is write-protected"));
1049 r = errstr = newvstrallocf(errstr, "%s", strerror(errno));
1059 getconf_readblocksize(void)
1062 char *conf_tapetype;
1064 conf_tapetype = getconf_str(CNF_TAPETYPE);
1066 if (!conf_tapetype || strlen(conf_tapetype) == 0)
1067 return MAX_TAPE_BLOCK_KB;
1069 tape = lookup_tapetype(conf_tapetype);
1071 return MAX_TAPE_BLOCK_KB;
1073 return tapetype_get_readblocksize(tape);
1079 * The following test program may be used to exercise I/O patterns through
1080 * the tapeio interface. Commands may either be on the command line or
1081 * read from stdin (e.g. for a test suite).
1087 /* If the C library does not define random(), try to use rand() by
1088 defining USE_RAND, but then make sure you are not using hardware
1089 compression, because the low-order bits of rand() may not be that
1091 #define random() rand()
1092 #define srandom(seed) srand(seed)
1100 g_fprintf(stderr, _(" ?|help\n"));
1101 g_fprintf(stderr, _(" open [\"file\"|$TAPE [\"mode\":O_RDONLY]]\n"));
1102 g_fprintf(stderr, _(" read [\"records\":\"all\"]\n"));
1103 g_fprintf(stderr, _(" write [\"records\":1] [\"file#\":\"+\"] [\"record#\":\"+\"] [\"host\"] [\"disk\"] [\"level\"]\n"));
1104 g_fprintf(stderr, _(" eof|weof [\"count\":1]\n"));
1105 g_fprintf(stderr, _(" fsf [\"count\":1]\n"));
1106 g_fprintf(stderr, _(" rewind\n"));
1107 g_fprintf(stderr, _(" unload\n"));
1113 g_fprintf(stderr, _("usage: %s [-c cmd [args] [%% cmd [args] ...]]\n"), pgm);
1117 #define TEST_BLOCKSIZE (32 * 1024)
1119 #define MAX_TOKENS 10
1123 static char *token_area[MAX_TOKENS + 1];
1124 static char **token;
1125 static int token_count;
1128 static off_t current_file = (off_t)0;
1129 static off_t current_record = (off_t)0;
1131 static int have_length = 0;
1132 static int length = (off_t)0;
1134 static int show_timestamp = 0;
1136 char write_buf[TEST_BLOCKSIZE];
1145 || (token_count >= 2 && strcmp(token[1], "$TAPE") == 0)) {
1146 if((file = getenv("TAPE")) == NULL) {
1147 g_fprintf(stderr, _("tape_open: no file name and $TAPE not set\n"));
1153 if(token_count > 2) {
1154 mode = atoi(token[2]);
1159 g_fprintf(stderr, _("tapefd_open(\"%s\", %d): "), file, mode);
1160 if((fd = tape_open(file, mode, 0644)) < 0) {
1163 g_fprintf(stderr, _("%d (OK)\n"), fd);
1165 tapefd_setinfo_length(fd, length);
1175 g_fprintf(stderr, _("tapefd_close(): "));
1176 if((result = tapefd_close(fd)) < 0) {
1179 g_fprintf(stderr, _("%d (OK)\n"), result);
1187 off_t count = (off_t)0;
1189 char buf[SIZEOF(write_buf)];
1196 if(token_count > 1 && strcmp(token[1], "all") != 0) {
1197 count = OFF_T_ATOI(token[1]);
1202 for(i = 0; (! have_count) || (i < count); i++) {
1203 g_fprintf(stderr, _("tapefd_read(%lld): "), (long long)i);
1204 if((result = tapefd_read(fd, buf, SIZEOF(buf))) < 0) {
1207 } else if(result == 0) {
1208 g_fprintf(stderr, _("%zd (EOF)\n"), result);
1210 * If we were not given a count, EOF breaks the loop, otherwise
1211 * we keep trying (to test read after EOF handling).
1217 if(result == (ssize_t)sizeof(buf)) {
1220 s = _("short read");
1224 * If the amount read is really short, we may refer to junk
1225 * when displaying the record data, but things are pretty
1226 * well screwed up at this point anyway so it is not worth
1227 * the effort to deal with.
1230 _("%zd (%s): file %d: record %d"),
1235 if(show_timestamp) {
1237 tm = localtime(&then);
1239 ": %04d/%02d/%02d %02d:%02d:%02d\n",
1247 fputc('\n', stderr);
1263 if(token_count > 1) {
1264 count = OFF_T_ATOI(token[1]);
1269 if(token_count > 2 && strcmp(token[2], "+") != 0) {
1270 current_file = OFF_T_ATOI(token[2]);
1273 if(token_count > 3 && strcmp(token[3], "+") != 0) {
1274 current_record = OFF_T_ATOI(token[3]);
1277 if(token_count > 4 && token[4][0] != '\0') {
1278 tapefd_setinfo_host(fd, token[4]);
1281 if(token_count > 5 && token[5][0] != '\0') {
1282 tapefd_setinfo_disk(fd, token[5]);
1285 if(token_count > 6 && token[6][0] != '\0') {
1286 tapefd_setinfo_level(fd, atoi(token[6]));
1289 p = (off_t *)write_buf;
1292 tm = localtime(&now);
1293 for(i = 0; i < count; i++, (current_record += (off_t)1)) {
1294 p[0] = current_file;
1295 p[1] = current_record;
1296 g_fprintf(stderr, _("tapefd_write(%lld): "), i);
1297 if((result = tapefd_write(fd, write_buf, SIZEOF(write_buf))) < 0) {
1301 if(result == (ssize_t)sizeof(write_buf)) {
1304 s = _("short write");
1307 _("%d (%s): file %lld: record %lld"),
1312 if(show_timestamp) {
1314 ": %04d/%02d/%02d %02d:%02d:%02d\n",
1322 fputc('\n', stderr);
1333 if(token_count > 1) {
1334 count = OFF_T_ATOI(token[1]);
1339 g_fprintf(stderr, _("tapefd_fsf(%lld): "), (long long)count);
1340 if((result = tapefd_fsf(fd, count)) < 0) {
1343 g_fprintf(stderr, _("%d (OK)\n"), result);
1344 current_file += count;
1345 current_record = (off_t)0;
1355 if(token_count > 1) {
1356 count = OFF_T_ATOI(token[1]);
1361 g_fprintf(stderr, _("tapefd_weof(%lld): "), count);
1362 if((result = tapefd_weof(fd, count)) < 0) {
1365 g_fprintf(stderr, _("%d (OK)\n"), result);
1366 current_file += count;
1367 current_record = (off_t)0;
1376 g_fprintf(stderr, _("tapefd_rewind(): "));
1377 if((result = tapefd_rewind(fd)) < 0) {
1380 g_fprintf(stderr, _("%d (OK)\n"), result);
1381 current_file = (off_t)0;
1382 current_record = (off_t)0;
1391 g_fprintf(stderr, _("tapefd_unload(): "));
1392 if((result = tapefd_unload(fd)) < 0) {
1395 g_fprintf(stderr, _("%d (OK)\n"), result);
1396 current_file = (off_t)-1;
1397 current_record = (off_t)-1;
1406 { "?", 0, do_help },
1407 { "help", 0, do_help },
1408 { "eof", 0, do_weof },
1409 { "weof", 0, do_weof },
1410 { "fsf", 0, do_fsf },
1411 { "rewind", 0, do_rewind },
1412 { "offline", 0, do_unload },
1413 { "open", 0, do_open },
1414 { "close", 0, do_close },
1415 { "read", 0, do_read },
1416 { "write", 0, do_write },
1434 * Configure program for internationalization:
1435 * 1) Only set the message locale for now.
1436 * 2) Set textdomain for all amanda related programs to "amanda"
1437 * We don't want to be forced to support dozens of message catalogs.
1439 setlocale(LC_MESSAGES, "C");
1440 textdomain("amanda");
1442 /* Don't die when child closes pipe */
1443 signal(SIGPIPE, SIG_IGN);
1445 if((pgm = strrchr(argv[0], '/')) != NULL) {
1452 * Compute the minimum abbreviation for each command.
1454 for(i = 0; cmd[i].name; i++) {
1455 cmd[i].min_chars = 1;
1457 for(j = 0; cmd[j].name; j++) {
1461 if(0 == strncmp(cmd[i].name, cmd[j].name, cmd[i].min_chars)) {
1465 if(0 == cmd[j].name) {
1473 * Process the command line flags.
1475 while((ch = getopt(argc, argv, "hcl:t")) != EOF) {
1482 length = OFF_T_ATOI(optarg);
1485 switch(optarg[j-1] ) {
1487 case 'b': length /= (off_t)2; break;
1488 case 'M': length *= (off_t)1024; break;
1489 default: length /= (off_t)1024; break;
1492 length /= (off_t)1024;
1506 * Initialize the write buffer.
1510 for(j = 0; j < (int)SIZEOF(write_buf); j++) {
1511 write_buf[j] = (char)random();
1517 token = token_area + 1;
1518 token_area[0] = ""; /* if cmdline */
1521 for(token_count = 1;
1522 token_count < (int)(SIZEOF(token_area) / SIZEOF(token_area[0]))
1524 token_count++, optind++) {
1525 if(strcmp(argv[optind], "%") == 0) {
1529 token_area[token_count] = argv[optind];
1532 if(token_count == 0 && optind >= argc) {
1536 if((line = areads(0)) == NULL) {
1539 if((s = strchr(line, '#')) != NULL) {
1542 s = line + strlen(line) - 1;
1543 while(s >= line && isspace(*s)) {
1546 token_count = split(line,
1548 SIZEOF(token_area) / SIZEOF(token_area[0]),
1554 * Truncate tokens at first comment indicator, then test for
1557 for(i = 0; i < token_count; i++) {
1558 if(token[i][0] == '#') {
1563 if(token_count <= 0) {
1564 continue; /* blank/comment input line */
1568 * Find the command to run, the do it.
1570 j = strlen(token[0]);
1571 for(i = 0; cmd[i].name; i++) {
1572 if(strncmp(cmd[i].name, token[0], j) == 0
1573 && j >= cmd[i].min_chars) {
1577 if(cmd[i].name == NULL) {
1578 g_fprintf(stderr, _("%s: unknown command: %s\n"), pgm, token[0]);