1 /* mt 1.3 - magnetic tape control Author: Kees J. Bot 2 * 4 Apr 1993 3 */ 4 #define nil NULL 5 #ifndef _POSIX_SOURCE 6 #define _POSIX_SOURCE 1 7 #endif 8 #include <sys/types.h> 9 #include <stdio.h> 10 #include <stdlib.h> 11 #include <errno.h> 12 #include <unistd.h> 13 #include <fcntl.h> 14 #include <string.h> 15 #include <sys/ioctl.h> 16 #include <sys/mtio.h> 17 18 /* Device status. */ 19 #define DS_OK 0 20 #define DS_ERR 1 21 #define DS_EOF 2 22 23 /* SCSI Sense key bits. */ 24 #define SENSE_KEY 0x0F /* The key part. */ 25 #define SENSE_ILI 0x20 /* Illegal block size. */ 26 #define SENSE_EOM 0x40 /* End-of-media. */ 27 #define SENSE_EOF 0x80 /* Filemark reached. */ 28 29 /* Supported operations: */ 30 31 typedef struct tape_operation { 32 int op; /* Opcode for MTIOCTOP ioctl (if any). */ 33 char *cmd; /* Command name. */ 34 int lim; /* Limits on count. */ 35 } tape_operation_t; 36 37 #define SELF -1 /* Not a simple command, have to interpret. */ 38 #define IGN -1 /* Ignore count field (or accept anything.) */ 39 #define NNG 0 /* Nonnegative count field. */ 40 #define POS 1 /* Positive count field. */ 41 42 tape_operation_t tapeops[] = { 43 { MTWEOF, "eof", POS }, /* Write EOF mark */ 44 { MTWEOF, "weof", POS }, /* Same */ 45 { MTFSF, "fsf", POS }, /* Forward Space File */ 46 { MTFSR, "fsr", POS }, /* Forward Space Record */ 47 { MTBSF, "bsf", NNG }, /* Backward Space File */ 48 { MTBSR, "bsr", POS }, /* Backward Space Record */ 49 { MTEOM, "eom", IGN }, /* To End-Of-Media */ 50 { MTREW, "rewind", IGN }, /* Rewind */ 51 { MTOFFL, "offline", IGN }, /* Rewind and take offline */ 52 { MTOFFL, "rewoffl", IGN }, /* Same */ 53 { SELF, "status", IGN }, /* Tape Status */ 54 { MTRETEN, "retension",IGN }, /* Retension the tape */ 55 { MTERASE, "erase", IGN }, /* Erase the tape */ 56 { MTSETDNSTY, "density", NNG }, /* Select density */ 57 { MTSETBSIZ, "blksize", NNG }, /* Select block size */ 58 { MTSETBSIZ, "blocksize",NNG }, /* Same */ 59 }; 60 61 #define arraysize(a) (sizeof(a)/sizeof((a)[0])) 62 #define arraylimit(a) ((a) + arraysize(a)) 63 64 /* From aha_scsi.c: */ 65 char *dev_state[] = { 66 "OK", "ERR", "EOF" 67 }; 68 69 char *scsi_sense[] = { 70 "NO SENSE INFO", "RECOVERED ERROR", "NOT READY", "MEDIUM ERROR", 71 "HARDWARE ERROR", "ILLEGAL REQUEST", "UNIT ATTENTION", "DATA PROTECT", 72 "BLANK CHECK", "VENDOR UNIQUE ERROR", "COPY ABORTED", "ABORTED COMMAND", 73 "EQUAL", "VOLUME OVERFLOW", "MISCOMPARE", "SENSE RESERVED" 74 }; 75 76 void usage(void) 77 { 78 fprintf(stderr, "Usage: mt [-f device] command [count]\n"); 79 exit(1); 80 } 81 82 int main(int argc, char **argv) 83 { 84 char *tape; 85 char *cmd; 86 int count= 1; 87 int fd, r; 88 tape_operation_t *op, *found; 89 struct mtop mtop; 90 struct mtget mtget; 91 92 tape= getenv("TAPE"); 93 94 /* -f tape? */ 95 if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'f') { 96 tape= argv[1] + 2; 97 98 if (*tape == 0) { 99 if (--argc < 2) usage(); 100 argv++; 101 tape= argv[1]; 102 } 103 argc--; 104 argv++; 105 } 106 107 if (argc != 2 && argc != 3) usage(); 108 109 if (argc == 3) { 110 /* Check and convert the 'count' argument. */ 111 char *end; 112 113 errno= 0; 114 count= strtol(argv[2], &end, 0); 115 if (*end != 0) usage(); 116 if (errno == ERANGE || (mtop.mt_count= count) != count) { 117 fprintf(stderr, "mt: %s: count too large, overflow\n", 118 argv[2]); 119 exit(1); 120 } 121 } 122 123 if (tape == nil) { 124 fprintf(stderr, 125 "mt: tape device not specified by -f or $TAPE\n"); 126 exit(1); 127 } 128 129 cmd= argv[1]; 130 if (strcmp(cmd, "rew") == 0) cmd= "rewind"; /* aha! */ 131 found= nil; 132 133 /* Search for an operation that is unambiguously named. */ 134 for (op= tapeops; op < arraylimit(tapeops); op++) { 135 if (strncmp(op->cmd, cmd, strlen(cmd)) == 0) { 136 if (found != nil) { 137 fprintf(stderr, "mt: %s: ambiguous\n", cmd); 138 exit(1); 139 } 140 found= op; 141 } 142 } 143 144 if ((op= found) == nil) { 145 fprintf(stderr, "mt: unknown command '%s'\n", cmd); 146 exit(1); 147 } 148 149 /* Check count. */ 150 switch (op->lim) { 151 case NNG: 152 if (count < 0) { 153 fprintf(stderr, "mt %s: count may not be negative\n", 154 op->cmd); 155 exit(1); 156 } 157 break; 158 case POS: 159 if (count <= 0) { 160 fprintf(stderr, 161 "mt %s: count must be greater than zero\n", 162 op->cmd); 163 exit(1); 164 } 165 break; 166 } 167 168 if (strcmp(tape, "-") == 0) { 169 fd= 0; 170 } else 171 if ((fd= open(tape, O_RDONLY)) < 0) { 172 fprintf(stderr, "mt: %s: %s\n", tape, strerror(errno)); 173 exit(1); 174 } 175 176 if (op->op != SELF) { 177 /* A simple tape operation. */ 178 179 mtop.mt_op= op->op; 180 mtop.mt_count= count; 181 r= ioctl(fd, MTIOCTOP, &mtop); 182 } else 183 if (strcmp(op->cmd, "status") == 0) { 184 /* Get status information. */ 185 186 if ((r= ioctl(fd, MTIOCGET, &mtget)) == 0) { 187 printf("\ 188 SCSI tape drive %s:\n\ 189 drive status = 0x%02x (%s), sense key = 0x%02x (%s%s%s%s)\n\ 190 file no = %ld, block no = %ld, residual = %ld, block size = ", 191 tape, mtget.mt_dsreg, 192 mtget.mt_dsreg > 2 ? "?" : 193 dev_state[mtget.mt_dsreg], 194 mtget.mt_erreg, 195 mtget.mt_erreg & SENSE_EOF ? "EOF + " : "", 196 mtget.mt_erreg & SENSE_EOM ? "EOM + " : "", 197 mtget.mt_erreg & SENSE_ILI ? "ILI + " : "", 198 scsi_sense[mtget.mt_erreg & SENSE_KEY], 199 (long) mtget.mt_fileno, 200 (long) mtget.mt_blkno, 201 (long) mtget.mt_resid); 202 if (mtget.mt_blksiz == 0) printf("variable\n"); 203 else printf("%d\n", mtget.mt_blksiz); 204 } 205 } 206 if (r < 0) { 207 if (errno == ENOTTY) { 208 fprintf(stderr, "mt: %s: command '%s' not supported\n", 209 tape, op->cmd); 210 exit(2); 211 } 212 fprintf(stderr, "mt: %s: %s\n", tape, strerror(errno)); 213 exit(1); 214 } 215 exit(0); 216 } 217