1 /* $NetBSD: mt.c,v 1.49 2022/01/24 09:14:36 andvar Exp $ */
2 
3 /*
4  * Copyright (c) 1980, 1993
5  *        The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT("@(#) Copyright (c) 1980, 1993\
35  The Regents of the University of California.  All rights reserved.");
36 #endif /* not lint */
37 
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)mt.c        8.2 (Berkeley) 6/6/93";
41 #else
42 __RCSID("$NetBSD: mt.c,v 1.49 2022/01/24 09:14:36 andvar Exp $");
43 #endif
44 #endif /* not lint */
45 
46 /*
47  * mt --
48  *   magnetic tape manipulation program
49  */
50 #include <sys/types.h>
51 #include <sys/param.h>
52 #include <sys/ioctl.h>
53 #include <sys/mtio.h>
54 #include <sys/stat.h>
55 
56 #include <ctype.h>
57 #include <err.h>
58 #include <fcntl.h>
59 #include <paths.h>
60 #include <rmt.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <unistd.h>
65 
66 /* pseudo ioctl constants */
67 #define MTASF       100
68 
69 struct commands {
70           const char *c_name;           /* command */
71           size_t c_namelen;             /* command len */
72           u_long c_spcl;                          /* ioctl request */
73           int c_code;                             /* ioctl code for MTIOCTOP command */
74           int c_ronly;                            /* open tape read-only */
75           int c_mincount;                         /* min allowed count value */
76 };
77 
78 #define CMD(a)      a, sizeof(a) - 1
79 static const struct commands com[] = {
80           { CMD("asf"),                 MTIOCTOP,     MTASF,      1,  0 },
81           { CMD("blocksize"), MTIOCTOP,     MTSETBSIZ,  1,  0 },
82           { CMD("bsf"),                 MTIOCTOP,     MTBSF,      1,  1 },
83           { CMD("bsr"),                 MTIOCTOP,     MTBSR,      1,  1 },
84           { CMD("compress"),  MTIOCTOP,     MTCMPRESS,  1,  0 },
85           { CMD("density"),   MTIOCTOP,     MTSETDNSTY, 1,  0 },
86           { CMD("eof"),                 MTIOCTOP,     MTWEOF,     0,  1 },
87           { CMD("eom"),                 MTIOCTOP,     MTEOM,      1,  0 },
88           { CMD("erase"),               MTIOCTOP,     MTERASE,    0,  0 },
89           { CMD("fsf"),                 MTIOCTOP,     MTFSF,      1,  1 },
90           { CMD("fsr"),                 MTIOCTOP,     MTFSR,      1,  1 },
91           { CMD("offline"),   MTIOCTOP,     MTOFFL,     1,  0 },
92           { CMD("rdhpos"),    MTIOCRDHPOS,  0,          1,  0 },
93           { CMD("rdspos"),    MTIOCRDSPOS,  0,          1,  0 },
94           { CMD("retension"), MTIOCTOP,     MTRETEN,    1,  0 },
95           { CMD("rewind"),    MTIOCTOP,     MTREW,      1,  0 },
96           { CMD("rewoffl"),   MTIOCTOP,     MTOFFL,     1,  0 },
97           { CMD("setblk"),    MTIOCTOP,     MTSETBSIZ,  1,  0 },
98           { CMD("setdensity"),          MTIOCTOP,     MTSETDNSTY, 1,  0 },
99           { CMD("sethpos"),   MTIOCHLOCATE, 0,          1,  0 },
100           { CMD("setspos"),   MTIOCSLOCATE, 0,          1,  0 },
101           { CMD("status"),    MTIOCGET,     MTNOP,      1,  0 },
102           { CMD("weof"),                MTIOCTOP,     MTWEOF,     0,  1 },
103           { CMD("eew"),                 MTIOCTOP,     MTEWARN,    1,  0 },
104           { CMD("cache"),               MTIOCTOP,     MTCACHE,    1,  0 },
105           { CMD("nocache"),   MTIOCTOP,     MTNOCACHE,  1,  0 },
106           { .c_name = NULL }
107 };
108 
109 static void printreg(const char *, u_int, const char *);
110 static void status(struct mtget *);
111 __dead static void usage(void);
112 
113 int
main(int argc,char * argv[])114 main(int argc, char *argv[])
115 {
116           const struct commands *cp, *comp;
117           struct mtget mt_status;
118           struct mtop mt_com;
119           int ch, mtfd, flags;
120           char *p;
121           const char *tape;
122           int count;
123           size_t len;
124 
125           setprogname(argv[0]);
126           if ((tape = getenv("TAPE")) == NULL)
127                     tape = _PATH_DEFTAPE;
128 
129           while ((ch = getopt(argc, argv, "f:t:")) != -1)
130                     switch (ch) {
131                     case 'f':
132                     case 't':
133                               tape = optarg;
134                               break;
135                     case '?':
136                     default:
137                               usage();
138                     }
139           argc -= optind;
140           argv += optind;
141 
142           if (argc < 1 || argc > 2)
143                     usage();
144 
145           len = strlen(p = *argv++);
146           for (comp = NULL, cp = com; cp->c_name != NULL; cp++) {
147                     size_t clen = MIN(len, cp->c_namelen);
148                     if (strncmp(p, cp->c_name, clen) == 0) {
149                               if (comp != NULL)
150                                         errx(1, "%s: Ambiguous command `%s' or `%s'?",
151                                             p, cp->c_name, comp->c_name);
152                               else
153                                         comp = cp;
154                     }
155           }
156           if (comp == NULL)
157                     errx(1, "%s: unknown command", p);
158 
159           if (*argv) {
160                     count = strtol(*argv, &p, 10);
161                     if (count < comp->c_mincount || *p)
162                               errx(2, "%s: illegal count", *argv);
163           } else
164                     count = 1;
165 
166           flags = comp->c_ronly ? O_RDONLY : O_WRONLY;
167 
168           if ((mtfd = open(tape, flags)) < 0)
169                     err(2, "%s", tape);
170 
171           switch (comp->c_spcl) {
172           case MTIOCTOP:
173                     if (comp->c_code == MTASF) {
174 
175                               /* If mtget.mt_fileno was implemented, We could
176                                  compute the minimal seek needed to position
177                                  the tape.  Until then, rewind and seek from
178                                  beginning-of-tape */
179 
180                               mt_com.mt_op = MTREW;
181                               mt_com.mt_count = 1;
182                               if (ioctl(mtfd, MTIOCTOP, &mt_com) < 0)
183                                         err(2, "%s", tape);
184 
185                               if (count > 0) {
186                                         mt_com.mt_op = MTFSF;
187                                             mt_com.mt_count = count;
188                                         if (ioctl(mtfd, MTIOCTOP, &mt_com) < 0)
189                                             err(2, "%s", tape);
190                               }
191 
192                     } else {
193                               mt_com.mt_op = comp->c_code;
194                               mt_com.mt_count = count;
195 
196                               if (ioctl(mtfd, MTIOCTOP, &mt_com) < 0)
197                                         err(2, "%s: %s", tape, comp->c_name);
198 
199                     }
200                     break;
201 
202           case MTIOCGET:
203                     if (ioctl(mtfd, MTIOCGET, &mt_status) < 0)
204                               err(2, "%s: %s", tape, comp->c_name);
205                     status(&mt_status);
206                     break;
207 
208           case MTIOCRDSPOS:
209           case MTIOCRDHPOS:
210                     if (ioctl(mtfd, comp->c_spcl, (caddr_t) &count) < 0)
211                               err(2, "%s", tape);
212                     printf("%s: block location %u\n", tape, (unsigned int) count);
213                     break;
214 
215           case MTIOCSLOCATE:
216           case MTIOCHLOCATE:
217                     if (ioctl(mtfd, comp->c_spcl, (caddr_t) &count) < 0)
218                               err(2, "%s", tape);
219                     break;
220 
221           default:
222                     errx(1, "internal error: unknown request %ld", comp->c_spcl);
223           }
224 
225           exit(0);
226           /* NOTREACHED */
227 }
228 
229 #if defined(sun) && !defined(__SVR4)
230 #include <sundev/tmreg.h>
231 #include <sundev/arreg.h>
232 #endif
233 
234 #ifdef tahoe
235 #include <tahoe/vba/cyreg.h>
236 #endif
237 
238 static const struct tape_desc {
239           short     t_type;             /* type of magtape device */
240           const     char *t_name;       /* printing name */
241           const     char *t_dsbits;     /* "drive status" register */
242           const     char *t_erbits;     /* "error" register */
243 } tapes[] = {
244 #if defined(sun) && !defined(__SVR4)
245           { MT_ISCPC,         "TapeMaster",       TMS_BITS, 0 },
246           { MT_ISAR,          "Archive",          ARCH_CTRL_BITS,     ARCH_BITS },
247 #endif
248 #ifdef tahoe
249           { MT_ISCY,          "cipher", CYS_BITS, CYCW_BITS },
250 #endif
251 #define SCSI_DS_BITS          "\20\5WriteProtect\2Mounted"
252           { 0x7,              "SCSI",             SCSI_DS_BITS,       "76543210" },
253           { .t_type = 0 }
254 };
255 
256 
257 /*
258  * Interpret the status buffer returned
259  */
260 static void
status(struct mtget * bp)261 status(struct mtget *bp)
262 {
263           const struct tape_desc *mt;
264 
265           for (mt = tapes;; mt++) {
266                     if (mt->t_type == 0) {
267                               (void)printf("%d: unknown tape drive type\n",
268                                   bp->mt_type);
269                               return;
270                     }
271                     if (mt->t_type == bp->mt_type)
272                               break;
273           }
274           (void)printf("%s tape drive, residual=%d\n", mt->t_name, bp->mt_resid);
275           printreg("ds", bp->mt_dsreg, mt->t_dsbits);
276           printreg("\ner", bp->mt_erreg, mt->t_erbits);
277           (void)putchar('\n');
278           (void)printf("blocksize: %d (%d, %d, %d, %d)\n",
279                     bp->mt_blksiz, bp->mt_mblksiz[0], bp->mt_mblksiz[1],
280                     bp->mt_mblksiz[2], bp->mt_mblksiz[3]);
281           (void)printf("density: %d (%d, %d, %d, %d)\n",
282                     bp->mt_density, bp->mt_mdensity[0], bp->mt_mdensity[1],
283                     bp->mt_mdensity[2], bp->mt_mdensity[3]);
284           (void)printf("current file number: %d\n", bp->mt_fileno);
285           (void)printf("current block number: %d\n", bp->mt_blkno);
286 }
287 
288 /*
289  * Print a register a la the %b format of the kernel's printf.
290  */
291 static void
printreg(const char * s,u_int v,const char * bits)292 printreg(const char *s, u_int v, const char *bits)
293 {
294           int any, i;
295           char c;
296 
297           any = 0;
298           if (bits && *bits == 8)
299                     printf("%s=%o", s, v);
300           else
301                     printf("%s=%x", s, v);
302           if (v && bits && *++bits) {
303                     putchar('<');
304                     while ((i = *bits++)) {
305                               if (v & (1 << (i-1))) {
306                                         if (any)
307                                                   putchar(',');
308                                         any = 1;
309                                         for (; (c = *bits) > 32; bits++)
310                                                   putchar(c);
311                               } else
312                                         for (; *bits > 32; bits++)
313                                                   ;
314                     }
315                     putchar('>');
316           }
317 }
318 
319 static void
usage(void)320 usage(void)
321 {
322           (void)fprintf(stderr, "usage: %s [-f device] command [count]\n",
323               getprogname());
324           exit(1);
325           /* NOTREACHED */
326 }
327