1 /* @(#)mt.c	1.32 21/08/20 Copyright 2000-2021 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)mt.c	1.32 21/08/20 Copyright 2000-2021 J. Schilling";
6 #endif
7 /*
8  *	Magnetic tape manipulation program
9  *
10  *	Copyright (c) 2000-2021 J. Schilling
11  */
12 /*
13  * The contents of this file are subject to the terms of the
14  * Common Development and Distribution License, Version 1.0 only
15  * (the "License").  You may not use this file except in compliance
16  * with the License.
17  *
18  * See the file CDDL.Schily.txt in this distribution for details.
19  * A copy of the CDDL is also available via the Internet at
20  * http://www.opensource.org/licenses/cddl1.txt
21  *
22  * When distributing Covered Code, include this CDDL HEADER in each
23  * file and include the License file CDDL.Schily.txt from this distribution.
24  */
25 
26 #include <schily/mconfig.h>
27 
28 /*
29  * XXX Until we find a better way, the next definitions must be in sync
30  * XXX with the definitions in librmt/remote.c
31  */
32 #if !defined(HAVE_FORK) || !defined(HAVE_SOCKETPAIR) || !defined(HAVE_DUP2)
33 #undef	USE_RCMD_RSH
34 #endif
35 #if !defined(HAVE_GETSERVBYNAME)
36 #undef	USE_REMOTE				/* Cannot get rcmd() port # */
37 #endif
38 #if (!defined(HAVE_NETDB_H) || !defined(HAVE_RCMD)) && !defined(USE_RCMD_RSH)
39 #undef	USE_REMOTE				/* There is no rcmd() */
40 #endif
41 
42 #include <schily/stdio.h>
43 #include <schily/stdlib.h>
44 #include <schily/unistd.h>
45 #include <schily/string.h>
46 #include <schily/utypes.h>
47 #include <schily/fcntl.h>
48 #include <schily/ioctl.h>
49 #include <schily/errno.h>
50 
51 #define	GT_COMERR		/* #define comerr gtcomerr */
52 #define	GT_ERROR		/* #define error gterror   */
53 #include <schily/schily.h>
54 #include <schily/standard.h>
55 /*#undef	HAVE_SYS_MTIO_H*/
56 #include <schily/mtio.h>
57 #include <schily/librmt.h>
58 #include <schily/nlsdefs.h>
59 
60 LOCAL BOOL	help;
61 LOCAL BOOL	prvers;
62 LOCAL BOOL	wready;
63 LOCAL int	debug;
64 
65 LOCAL struct mtop	mt_op;
66 LOCAL struct rmtget	mt_status;
67 
68 #ifndef	HAVE_MTGET_TYPE
69 #ifdef	HAVE_MTGET_MODEL
70 #define	HAVE_MTGET_TYPE
71 #define	mt_type	mt_model
72 #endif
73 #endif
74 
75 #define	NO_ASF		1000
76 #define	NO_NBSF		1001
77 #ifndef	MTASF
78 #	define	MTASF	NO_ASF
79 #endif
80 #ifndef	MTNBSF
81 #	define	MTNBSF	NO_NBSF
82 #endif
83 
84 #define	MTC_NONE	0	/* No flags defined			*/
85 #define	MTC_RW		0	/* This command writes to the tape	*/
86 #define	MTC_RDO		1	/* This command does not write		*/
87 #define	MTC_CNT		2	/* This command uses the count arg	*/
88 #define	MTC_NDEL	4	/* Open the tape drive with O_NDELAY	*/
89 
90 
91 LOCAL struct mt_cmds {
92 	char *mtc_name;		/* The name of the command		*/
93 	char *mtc_text;		/* Description of the command		*/
94 	int mtc_opcode;		/* The opcode for mtio			*/
95 	int mtc_flags;		/* Flags for this command		*/
96 } cmds[] = {
97 #ifdef	MTWEOF
98 	{ "weof",	"write EOF mark",		MTWEOF,		MTC_RW|MTC_CNT },
99 	{ "eof",	"write EOF mark",		MTWEOF,		MTC_RW|MTC_CNT },
100 #endif
101 #ifdef	MTFSF
102 	{ "fsf",	"forward skip FILE mark",	MTFSF,		MTC_RDO|MTC_CNT },
103 #endif
104 #ifdef	MTBSF
105 	{ "bsf",	"backward skip FILE mark",	MTBSF,		MTC_RDO|MTC_CNT },
106 #endif
107 	{ "asf",	"absolute FILE mark pos",	MTASF,		MTC_RDO|MTC_CNT },
108 #ifdef	MTFSR
109 	{ "fsr",	"forward skip record",		MTFSR,		MTC_RDO|MTC_CNT },
110 #endif
111 #ifdef	MTBSR
112 	{ "bsr",	"backward skip record",		MTBSR,		MTC_RDO|MTC_CNT },
113 #endif
114 #ifdef	MTREW
115 	{ "rewind",	"rewind tape",			MTREW,		MTC_RDO },
116 #endif
117 #ifdef	MTOFFL
118 	{ "offline",	"rewind and unload",		MTOFFL,		MTC_RDO },
119 	{ "rewoffl",	"rewind and unload",		MTOFFL,		MTC_RDO },
120 #endif
121 #ifdef	MTNOP
122 	{ "status",	"get tape status",		MTNOP,		MTC_RDO },
123 #endif
124 	{ "nop",	"no operation",			MTNOP,		MTC_RDO },
125 #ifdef	MTRETEN
126 	{ "retension",	"retension tape cartridge",	MTRETEN,	MTC_RDO },
127 #endif
128 #ifdef	MTERASE
129 	{ "erase",	"erase tape",			MTERASE,	MTC_RW },
130 #endif
131 #ifdef	MTEOM
132 	{ "eom",	"position to EOM",		MTEOM,		MTC_RDO },
133 #endif
134 
135 #if	MTNBSF != NO_NBSF
136 	{ "nbsf",	"backward skip FILE mark",	MTNBSF,		MTC_RDO|MTC_CNT },
137 #endif
138 
139 #ifdef	MTLOAD
140 	{ "load",	"load tape",			MTLOAD,		MTC_RDO|MTC_NDEL },
141 #endif
142 
143 	{ NULL, 	NULL,				0,		MTC_NONE }
144 };
145 
146 LOCAL	void	usage		__PR((int ex));
147 EXPORT	int	main		__PR((int ac, char *av[]));
148 LOCAL	void	mtstatus	__PR((struct rmtget *sp));
149 LOCAL	char 	*print_key	__PR((Llong key));
150 LOCAL	int	openremote	__PR((char *tape));
151 LOCAL	int	opentape	__PR((char *tape, struct mt_cmds *cp));
152 LOCAL	int	mtioctl		__PR((int cmd, caddr_t arg));
153 
154 LOCAL void
usage(ex)155 usage(ex)
156 	int	ex;
157 {
158 	struct mt_cmds	*cp;
159 	int		i;
160 
161 	error("Usage: mt [ -f device ] [options] command [ count ]\n");
162 	error("Options:\n");
163 	error("\t-help\t\tprint this online help\n");
164 	error("\t-version\tprint version number\n");
165 	error("\t-wready\t\twait for the tape to become ready before doing command\n");
166 	error("\n");
167 	error("Commands are:\n");
168 	for (cp = cmds; cp->mtc_name != NULL; cp++) {
169 		error("%s%n", cp->mtc_name, &i);
170 		error("%*s%s\n", 14-i, "", cp->mtc_text);
171 	}
172 	exit(ex);
173 }
174 
175 LOCAL char	opts[] = "f*,t*,version,help,h,debug,wready";
176 
177 int
main(ac,av)178 main(ac, av)
179 	int	ac;
180 	char	*av[];
181 {
182 	int	cac;
183 	char	* const *cav;
184 	char	*tape = NULL;
185 	char	*cmd = "BADCMD";
186 	int	count = 1;
187 	struct mt_cmds	*cp;
188 
189 	save_args(ac, av);
190 
191 	(void) setlocale(LC_ALL, "");
192 
193 #ifdef  USE_NLS
194 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
195 #define	TEXT_DOMAIN "mt"	/* Use this only if it weren't */
196 #endif
197 	{ char	*dir;
198 	dir = searchfileinpath("share/locale", F_OK,
199 					SIP_ANY_FILE|SIP_NO_PATH, NULL);
200 	if (dir)
201 		(void) bindtextdomain(TEXT_DOMAIN, dir);
202 	else
203 #if defined(PROTOTYPES) && defined(INS_BASE)
204 	(void) bindtextdomain(TEXT_DOMAIN, INS_BASE "/share/locale");
205 #else
206 	(void) bindtextdomain(TEXT_DOMAIN, "/usr/share/locale");
207 #endif
208 	(void) textdomain(TEXT_DOMAIN);
209 	}
210 #endif 	/* USE_NLS */
211 
212 	cac = --ac;
213 	cav = ++av;
214 
215 	if (getallargs(&cac, &cav, opts,
216 			&tape, &tape,
217 			&prvers,
218 			&help, &help,
219 			&debug,
220 			&wready) < 0) {
221 		errmsgno(EX_BAD, "Bad Option: '%s'.\n", cav[0]);
222 		usage(EX_BAD);
223 	}
224 	if (help) usage(0);
225 	if (prvers) {
226 		gtprintf("mt %s %s (%s-%s-%s)\n\n", "1.32", "2021/08/20", HOST_CPU, HOST_VENDOR, HOST_OS);
227 		gtprintf("Copyright (C) 2000-2021 %s\n", _("J�rg Schilling"));
228 		gtprintf("This is free software; see the source for copying conditions.  There is NO\n");
229 		gtprintf("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
230 		exit(0);
231 	}
232 
233 	if (tape == NULL && (tape = getenv("TAPE")) == NULL) {
234 #ifdef	DEFTAPE
235 		tape = DEFTAPE;
236 #else
237 		errmsgno(EX_BAD, "No default tape defined.\n");
238 		usage(EX_BAD);
239 		/* NOTREACHED */
240 #endif
241 	}
242 
243 	cac = ac;
244 	cav = av;
245 	if (getfiles(&cac, &cav, opts) == 0) {
246 		errmsgno(EX_BAD, "Missing args.\n");
247 		usage(EX_BAD);
248 	} else {
249 		cmd = cav[0];
250 		cav++;
251 		cac--;
252 	}
253 	if (getfiles(&cac, &cav, opts) > 0) {
254 		if (*astoi(cav[0], &count) != '\0') {
255 			errmsgno(EX_BAD, "Not a number: '%s'.\n", cav[0]);
256 			usage(EX_BAD);
257 		}
258 		if (count < 0) {
259 			comerrno(EX_BAD, "negative file number or repeat count\n");
260 			/* NOTREACHED */
261 		}
262 		cav++;
263 		cac--;
264 	}
265 	if (getfiles(&cac, &cav, opts) > 0) {
266 		errmsgno(EX_BAD, "Too many args.\n");
267 		usage(EX_BAD);
268 	}
269 
270 	for (cp = cmds; cp->mtc_name != NULL; cp++) {
271 		if (strncmp(cmd, cp->mtc_name, strlen(cmd)) == 0)
272 			break;
273 	}
274 	if (cp->mtc_name == NULL) {
275 		comerrno(EX_BAD, "Unknown command: %s\n", cmd);
276 		/* NOTREACHED */
277 	}
278 #ifdef	DEBUG
279 	error("cmd: %s opcode: %d %s %s\n",
280 		cp->mtc_name, cp->mtc_opcode,
281 		(cp->mtc_flags & MTC_RDO) != 0 ? "RO":"RW",
282 		(cp->mtc_flags & MTC_CNT) != 0 ? "usecount":"");
283 #endif
284 
285 	if ((cp->mtc_flags & MTC_CNT) == 0)
286 		count = 1;
287 
288 #ifdef	USE_REMOTE
289 	rmtdebug(debug);
290 	(void)openremote(tape);		/* This needs super user privilleges */
291 #endif
292 #ifdef	HAVE_SETREUID
293 	if (setreuid(-1, getuid()) < 0)
294 #else
295 #ifdef	HAVE_SETEUID
296 	if (seteuid(getuid()) < 0)
297 #else
298 	if (setuid(getuid()) < 0)
299 #endif
300 #endif
301 		comerr("Panic cannot set back effective uid.\n");
302 
303 	if (opentape(tape, cp) < 0) {
304 		if (geterrno() == EIO) {
305 			comerrno(EX_BAD, "'%s': no tape loaded or drive offline.\n",
306 				tape);
307 		} else if ((cp->mtc_flags & MTC_RDO) == 0 &&
308 			    geterrno() == EACCES) {
309 			comerr("Cannot open '%s': tape may be write protected.\n", tape);
310 		} else {
311 			comerr("Cannot open '%s'.\n", tape);
312 		}
313 		/* NOTREACHED */
314 	}
315 
316 #ifdef	DEBUG
317 	error("Tape: %s cmd : %s (%s) count: %d\n", tape, cmd, cp->mtc_name, count);
318 #endif
319 
320 	if (cp->mtc_opcode == MTNOP) {
321 		if (strcmp(cp->mtc_name, "nop")) {
322 			/*
323 			 * Status ioctl
324 			 */
325 			if (mtioctl(MTIOCGET, (caddr_t)&mt_status) < 0) {
326 				comerr("Cannot get mt status from '%s'.\n", tape);
327 				/* NOTREACHED */
328 			}
329 			mtstatus(&mt_status);
330 		}
331 #if	MTASF == NO_ASF
332 	} else if (cp->mtc_opcode == MTASF) {
333 		(void)mtioctl(MTIOCGET, (caddr_t)&mt_status);
334 		if (mtioctl(MTIOCGET, (caddr_t)&mt_status) < 0) {
335 			comerr("Cannot get mt status from '%s'.\n", tape);
336 			/* NOTREACHED */
337 		}
338 		/*
339 		 * If the device does not support to report the current file
340 		 * tape file position - rewind the tape, and space forward.
341 		 */
342 #ifndef	MTF_ASF
343 		if (1) {
344 #else
345 		if (!(mt_status.mt_flags & MTF_ASF) || MTNBSF == NO_NBSF) {
346 #endif
347 			mt_status.mt_fileno = 0;
348 			mt_op.mt_count = 1;
349 			mt_op.mt_op = MTREW;
350 			if (mtioctl(MTIOCTOP, (caddr_t)&mt_op) < 0) {
351 				comerr("%s %s %d failed\n", tape, cp->mtc_name,
352 							count);
353 				/* NOTREACHED */
354 			}
355 		}
356 		if (count < mt_status.mt_fileno) {
357 			mt_op.mt_op = MTNBSF;
358 			mt_op.mt_count =  mt_status.mt_fileno - count;
359 			/*printf("mt: bsf= %lld\n", (Llong)mt_op.mt_count);*/
360 		} else {
361 			mt_op.mt_op = MTFSF;
362 			mt_op.mt_count =  count - mt_status.mt_fileno;
363 			/*printf("mt: fsf= %lld\n", (Llong)mt_op.mt_count);*/
364 		}
365 		if (mtioctl(MTIOCTOP, (caddr_t)&mt_op) < 0) {
366 			if (mtioctl(MTIOCTOP, (caddr_t)&mt_op) < 0) {
367 				comerr("%s %s %d failed\n", tape, cp->mtc_name,
368 							count);
369 				/* NOTREACHED */
370 			}
371 		}
372 #endif
373 	} else {
374 		/*
375 		 * Regular magnetic tape ioctl
376 		 */
377 		mt_op.mt_op = cp->mtc_opcode;
378 		mt_op.mt_count = count;
379 		if (mtioctl(MTIOCTOP, (caddr_t)&mt_op) < 0) {
380 			comerr("%s %s %lld failed\n", tape, cp->mtc_name,
381 						(Llong)mt_op.mt_count);
382 			/* NOTREACHED */
383 		}
384 	}
385 	return (0);
386 }
387 
388 /*
389  * If we try to make this portable, we need a way to initialize it
390  * in an OS independant way.
391  * Don't use it for now.
392  */
393 /*LOCAL */
394 struct tape_info {
395 	short	t_type;		/* type of magnetic tape device	*/
396 	char	*t_name;	/* name for prining		*/
397 	char	*t_dsbits;	/* "drive status" register	*/
398 	char	*t_erbits;	/* "error" register		*/
399 };
400 #ifdef	XXX
401  tapes[] = {
402 	{ MT_ISTS,	"ts11",		0,		TSXS0_BITS },
403 	{ 0 }
404 };
405 #endif
406 
407 /*
408  * Interpret the status buffer returned
409  */
410 LOCAL void
mtstatus(sp)411 mtstatus(sp)
412 	register struct rmtget *sp;
413 {
414 	register struct tape_info *mt = NULL;
415 
416 #ifdef	XXX
417 #ifdef	HAVE_MTGET_TYPE
418 	for (mt = tapes; mt->t_type; mt++)
419 		if (mt->t_type == sp->mt_type)
420 			break;
421 #endif
422 #endif
423 
424 #if	defined(MTF_SCSI)
425 
426 	if ((sp->mt_xflags & RMT_FLAGS) && (sp->mt_flags & MTF_SCSI)) {
427 		/*
428 		 * Handle SCSI tape drives specially.
429 		 */
430 		if (sp->mt_xflags & RMT_TYPE) {
431 			if (mt != NULL && mt->t_type == sp->mt_type)
432 				gtprintf("%s tape drive:\n", mt->t_name);
433 			else
434 				gtprintf("%s tape drive:\n", "SCSI");
435 		} else {
436 			gtprintf("Unknown SCSI tape drive:\n");
437 		}
438 		printf("   sense key(0x%llx)= %s   residual= %lld   ",
439 			sp->mt_erreg, print_key(sp->mt_erreg), sp->mt_resid);
440 		gtprintf("retries= %lld\n", sp->mt_dsreg);
441 	} else
442 #endif	/* MTF_SCSI */
443 		{
444 		/*
445 		 * Handle other drives below.
446 		 */
447 		if (sp->mt_xflags & RMT_TYPE) {
448 			if (mt == NULL || mt->t_type == 0) {
449 				gtprintf("Unknown tape drive type (0x%llX):\n", (Ullong)sp->mt_type);
450 			} else {
451 				gtprintf("%s tape drive:\n", mt->t_name);
452 			}
453 		} else {
454 			gtprintf("Unknown tape drive:\n");
455 		}
456 		if (sp->mt_xflags & RMT_RESID)
457 			gtprintf("   residual= %lld", sp->mt_resid);
458 		/*
459 		 * If we implement better support for specific OS,
460 		 * then we may want to implement something like the
461 		 * *BSD kernel %b printf format (e.g. printreg).
462 		 */
463 		if (sp->mt_xflags & RMT_DSREG)
464 			printf  ("   ds = %llX", (Ullong)sp->mt_dsreg);
465 
466 		if (sp->mt_xflags & RMT_ERREG)
467 			printf  ("   er = %llX", sp->mt_erreg);
468 		putchar('\n');
469 	}
470 	gtprintf("   file no= %lld   block no= %lld\n",
471 			(sp->mt_xflags & RMT_FILENO)?
472 				sp->mt_fileno:
473 				(Llong)-1,
474 			(sp->mt_xflags & RMT_BLKNO)?
475 				sp->mt_blkno:
476 				(Llong)-1);
477 
478 	if (sp->mt_xflags & RMT_BF)
479 		gtprintf("   optimum blocking factor= %ld\n", sp->mt_bf);
480 
481 	if (sp->mt_xflags & RMT_FLAGS)
482 		gtprintf("   flags= 0x%llX\n", sp->mt_flags);
483 }
484 
485 static char *sense_keys[] = {
486 	"No Additional Sense",		/* 0x00 */
487 	"Recovered Error",		/* 0x01 */
488 	"Not Ready",			/* 0x02 */
489 	"Medium Error",			/* 0x03 */
490 	"Hardware Error",		/* 0x04 */
491 	"Illegal Request",		/* 0x05 */
492 	"Unit Attention",		/* 0x06 */
493 	"Data Protect",			/* 0x07 */
494 	"Blank Check",			/* 0x08 */
495 	"Vendor Unique",		/* 0x09 */
496 	"Copy Aborted",			/* 0x0a */
497 	"Aborted Command",		/* 0x0b */
498 	"Equal",			/* 0x0c */
499 	"Volume Overflow",		/* 0x0d */
500 	"Miscompare",			/* 0x0e */
501 	"Reserved"			/* 0x0f */
502 };
503 
504 LOCAL char *
print_key(key)505 print_key(key)
506 	Llong	key;
507 {
508 	static	char keys[32];
509 
510 	if (key >= 0 && key < (sizeof(sense_keys)/sizeof(sense_keys[0])))
511 		return (sense_keys[key]);
512 	js_snprintf(keys, sizeof(keys), "Unknown Key: %lld", key);
513 	return (keys);
514 }
515 
516 /*--------------------------------------------------------------------------*/
517 LOCAL int	isremote;
518 LOCAL int	remfd	= -1;
519 LOCAL int	mtfd;
520 LOCAL char	*remfn;
521 
522 #ifdef	USE_REMOTE
523 LOCAL int
openremote(tape)524 openremote(tape)
525 	char	*tape;
526 {
527 	char	host[128];
528 
529 	if ((remfn = rmtfilename(tape)) != NULL) {
530 		rmthostname(host, sizeof(host), tape);
531 		isremote++;
532 
533 		if (debug)
534 			errmsgno(EX_BAD, "Remote: %s Host: %s file: %s\n",
535 							tape, host, remfn);
536 
537 		if ((remfd = rmtgetconn(host, 4096, 0)) < 0)
538 			comerrno(EX_BAD, "Cannot get connection to '%s'.\n",
539 				/* errno not valid !! */		host);
540 	}
541 	return (isremote);
542 }
543 #endif
544 
545 LOCAL int
opentape(tape,cp)546 opentape(tape, cp)
547 		char		*tape;
548 	register struct mt_cmds *cp;
549 {
550 	int	ret;
551 	int	n = 0;
552 	int	oflag;
553 
554 	oflag = (cp->mtc_flags & MTC_RDO) ? O_RDONLY : O_RDWR;
555 #ifdef	O_NDELAY
556 	if (cp->mtc_flags & MTC_NDEL)
557 		oflag |= O_NDELAY;
558 #endif
559 
560 retry:
561 	ret = 0;
562 	if (isremote) {
563 #ifdef	USE_REMOTE
564 		if (rmtopen(remfd, remfn, oflag) < 0)
565 			ret = -1;
566 #else
567 		comerrno(EX_BAD, "Remote tape support not present.\n");
568 #endif
569 	} else if ((mtfd = open(tape, oflag)) < 0) {
570 			ret = -1;
571 	}
572 	if (wready && n++ < 120 && ret < 0 &&
573 	    (geterrno() == EIO || geterrno() == EBUSY)) {
574 		sleep(1);
575 		goto retry;
576 	}
577 	return (ret);
578 }
579 
580 LOCAL int
mtioctl(cmd,arg)581 mtioctl(cmd, arg)
582 	int	cmd;
583 	caddr_t	arg;
584 {
585 	int	ret = -1;
586 	struct rmtget *mtp;
587 	struct mtop *mop;
588 
589 	if (isremote) {
590 #ifdef	USE_REMOTE
591 		switch (cmd) {
592 
593 		case MTIOCGET:
594 			ret = rmtxstatus(remfd, (struct rmtget *)arg);
595 			if (ret < 0)
596 				return (ret);
597 
598 			mtp = (struct rmtget *)arg;
599 /*#define	DEBUG*/
600 #ifdef	DEBUG
601 error("type: %llX ds: %llX er: %llX resid: %lld fileno: %lld blkno: %lld flags: %llX bf: %ld\n",
602 mtp->mt_type, mtp->mt_dsreg, mtp->mt_erreg, mtp->mt_resid, mtp->mt_fileno,
603 mtp->mt_blkno, mtp->mt_flags, mtp->mt_bf);
604 #endif
605 			break;
606 		case MTIOCTOP:
607 			mop = (struct mtop *)arg;
608 			ret = rmtioctl(remfd, mop->mt_op, mop->mt_count);
609 			break;
610 		default:
611 			comerrno(ENOTTY, "Invalid mtioctl.\n");
612 			/* NOTREACHED */
613 		}
614 #else
615 		comerrno(EX_BAD, "Remote tape support not present.\n");
616 #endif
617 	} else {
618 #ifdef	HAVE_IOCTL
619 		if (cmd == MTIOCGET) {
620 			struct mtget mtget;
621 
622 			ret = ioctl(mtfd, cmd, &mtget);
623 			if (ret >= 0) {
624 				if (_mtg2rmtg((struct rmtget *)arg, &mtget) < 0)
625 					ret = -1;
626 			}
627 		} else
628 		ret = ioctl(mtfd, cmd, arg);
629 #else
630 		ret = -1;
631 #ifdef	ENOSYS
632 		seterrno(ENOSYS);
633 #else
634 		seterrno(EINVAL);
635 #endif
636 #endif
637 	}
638 	return (ret);
639 }
640