Home | History | Annotate | Line # | Download | only in librmt
rmtlib.c revision 1.24.2.1
      1  1.24.2.1    cherry /*	$NetBSD: rmtlib.c,v 1.24.2.1 2011/06/23 14:18:40 cherry Exp $	*/
      2       1.1       jtc 
      3       1.1       jtc /*
      4       1.1       jtc  *	rmt --- remote tape emulator subroutines
      5       1.1       jtc  *
      6       1.1       jtc  *	Originally written by Jeff Lee, modified some by Arnold Robbins
      7       1.1       jtc  *
      8       1.1       jtc  *	WARNING:  The man page rmt(8) for /etc/rmt documents the remote mag
      9       1.1       jtc  *	tape protocol which rdump and rrestore use.  Unfortunately, the man
     10       1.1       jtc  *	page is *WRONG*.  The author of the routines I'm including originally
     11       1.1       jtc  *	wrote his code just based on the man page, and it didn't work, so he
     12       1.1       jtc  *	went to the rdump source to figure out why.  The only thing he had to
     13       1.1       jtc  *	change was to check for the 'F' return code in addition to the 'E',
     14       1.1       jtc  *	and to separate the various arguments with \n instead of a space.  I
     15       1.1       jtc  *	personally don't think that this is much of a problem, but I wanted to
     16       1.1       jtc  *	point it out.
     17       1.1       jtc  *	-- Arnold Robbins
     18       1.1       jtc  *
     19       1.1       jtc  *	Redone as a library that can replace open, read, write, etc, by
     20       1.1       jtc  *	Fred Fish, with some additional work by Arnold Robbins.
     21       1.1       jtc  */
     22      1.11    simonb 
     23       1.1       jtc /*
     24       1.1       jtc  *	MAXUNIT --- Maximum number of remote tape file units
     25       1.1       jtc  *
     26       1.1       jtc  *	READ --- Return the number of the read side file descriptor
     27       1.1       jtc  *	WRITE --- Return the number of the write side file descriptor
     28       1.1       jtc  */
     29      1.19     lukem 
     30      1.19     lukem #include <sys/cdefs.h>
     31  1.24.2.1    cherry __RCSID("$NetBSD: rmtlib.c,v 1.24.2.1 2011/06/23 14:18:40 cherry Exp $");
     32       1.1       jtc 
     33       1.1       jtc #define RMTIOCTL	1
     34       1.6     mikel /* #define USE_REXEC	1 */	/* rexec code courtesy of Dan Kegel, srs!dan */
     35       1.1       jtc 
     36       1.1       jtc #include <sys/types.h>
     37      1.12     lukem #include <sys/stat.h>
     38       1.1       jtc 
     39       1.1       jtc #ifdef RMTIOCTL
     40       1.1       jtc #include <sys/ioctl.h>
     41       1.1       jtc #include <sys/mtio.h>
     42       1.1       jtc #endif
     43       1.1       jtc 
     44      1.12     lukem #include <assert.h>
     45      1.12     lukem #include <errno.h>
     46      1.12     lukem #include <fcntl.h>
     47      1.12     lukem #include <signal.h>
     48      1.14     lukem #include <stdarg.h>
     49      1.12     lukem #include <stdio.h>
     50      1.12     lukem #include <stdlib.h>
     51      1.12     lukem #include <string.h>
     52      1.12     lukem #include <unistd.h>
     53      1.24  christos #include <err.h>
     54      1.12     lukem 
     55       1.1       jtc #ifdef USE_REXEC
     56       1.1       jtc #include <netdb.h>
     57       1.1       jtc #endif
     58       1.1       jtc 
     59       1.9   thorpej #define __RMTLIB_PRIVATE
     60       1.9   thorpej #include <rmt.h>		/* get prototypes for remapped functions */
     61       1.9   thorpej 
     62      1.10       mrg #include "pathnames.h"
     63      1.10       mrg 
     64      1.14     lukem static	int	_rmt_close(int);
     65      1.14     lukem static	int	_rmt_ioctl(int, unsigned long, void *);
     66      1.14     lukem static	off_t	_rmt_lseek(int, off_t, int);
     67      1.14     lukem static	int	_rmt_open(const char *, int, int);
     68      1.14     lukem static	ssize_t	_rmt_read(int, void *, size_t);
     69      1.14     lukem static	ssize_t	_rmt_write(int, const void *, size_t);
     70      1.24  christos static	int	command(int, const char *);
     71      1.14     lukem static	int	remdev(const char *);
     72      1.14     lukem static	void	rmtabort(int);
     73      1.14     lukem static	int	status(int);
     74       1.7     lukem 
     75       1.7     lukem 
     76       1.1       jtc #define BUFMAGIC	64	/* a magic number for buffer sizes */
     77      1.15     enami #define MAXUNIT		4
     78       1.1       jtc 
     79       1.1       jtc #define READ(fd)	(Ctp[fd][0])
     80       1.1       jtc #define WRITE(fd)	(Ptc[fd][1])
     81       1.1       jtc 
     82       1.6     mikel static int Ctp[MAXUNIT][2] = { {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1} };
     83       1.6     mikel static int Ptc[MAXUNIT][2] = { {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1} };
     84       1.1       jtc 
     85       1.1       jtc 
     86       1.1       jtc /*
     87       1.2       jtc  *	rmtabort --- close off a remote tape connection
     88       1.1       jtc  */
     89       1.7     lukem static void
     90      1.14     lukem rmtabort(int fildes)
     91       1.1       jtc {
     92      1.14     lukem 
     93       1.1       jtc 	close(READ(fildes));
     94       1.1       jtc 	close(WRITE(fildes));
     95       1.1       jtc 	READ(fildes) = -1;
     96       1.1       jtc 	WRITE(fildes) = -1;
     97       1.1       jtc }
     98       1.1       jtc 
     99       1.1       jtc 
    100       1.1       jtc /*
    101       1.1       jtc  *	command --- attempt to perform a remote tape command
    102       1.1       jtc  */
    103       1.7     lukem static int
    104      1.24  christos command(int fildes, const char *buf)
    105       1.1       jtc {
    106      1.14     lukem 	size_t blen;
    107      1.24  christos 	sig_t pstat;
    108       1.1       jtc 
    109      1.12     lukem 	_DIAGASSERT(buf != NULL);
    110      1.12     lukem 
    111       1.1       jtc /*
    112       1.1       jtc  *	save current pipe status and try to make the request
    113       1.1       jtc  */
    114       1.1       jtc 
    115       1.1       jtc 	blen = strlen(buf);
    116       1.1       jtc 	pstat = signal(SIGPIPE, SIG_IGN);
    117      1.14     lukem 	if (write(WRITE(fildes), buf, blen) == blen) {
    118       1.1       jtc 		signal(SIGPIPE, pstat);
    119      1.24  christos 		return 0;
    120       1.1       jtc 	}
    121       1.1       jtc 
    122       1.1       jtc /*
    123       1.1       jtc  *	something went wrong. close down and go home
    124       1.1       jtc  */
    125       1.1       jtc 
    126       1.1       jtc 	signal(SIGPIPE, pstat);
    127       1.2       jtc 	rmtabort(fildes);
    128       1.1       jtc 
    129       1.1       jtc 	errno = EIO;
    130      1.24  christos 	return -1;
    131       1.1       jtc }
    132       1.1       jtc 
    133       1.1       jtc 
    134       1.1       jtc /*
    135       1.1       jtc  *	status --- retrieve the status from the pipe
    136       1.1       jtc  */
    137       1.7     lukem static int
    138      1.14     lukem status(int fildes)
    139       1.1       jtc {
    140       1.1       jtc 	int i;
    141       1.1       jtc 	char c, *cp;
    142       1.1       jtc 	char buffer[BUFMAGIC];
    143       1.1       jtc 
    144       1.1       jtc /*
    145       1.1       jtc  *	read the reply command line
    146       1.1       jtc  */
    147       1.1       jtc 
    148      1.14     lukem 	for (i = 0, cp = buffer; i < BUFMAGIC; i++, cp++) {
    149      1.14     lukem 		if (read(READ(fildes), cp, 1) != 1) {
    150       1.2       jtc 			rmtabort(fildes);
    151       1.1       jtc 			errno = EIO;
    152      1.24  christos 			return -1;
    153       1.1       jtc 		}
    154      1.14     lukem 		if (*cp == '\n') {
    155       1.1       jtc 			*cp = 0;
    156       1.1       jtc 			break;
    157       1.1       jtc 		}
    158       1.1       jtc 	}
    159       1.1       jtc 
    160      1.14     lukem 	if (i == BUFMAGIC) {
    161       1.2       jtc 		rmtabort(fildes);
    162       1.1       jtc 		errno = EIO;
    163      1.24  christos 		return -1;
    164       1.1       jtc 	}
    165       1.1       jtc 
    166       1.1       jtc /*
    167       1.1       jtc  *	check the return status
    168       1.1       jtc  */
    169       1.1       jtc 
    170       1.1       jtc 	for (cp = buffer; *cp; cp++)
    171       1.1       jtc 		if (*cp != ' ')
    172       1.1       jtc 			break;
    173       1.1       jtc 
    174      1.14     lukem 	if (*cp == 'E' || *cp == 'F') {
    175       1.1       jtc 		errno = atoi(cp + 1);
    176       1.1       jtc 		while (read(READ(fildes), &c, 1) == 1)
    177       1.1       jtc 			if (c == '\n')
    178       1.1       jtc 				break;
    179       1.1       jtc 
    180       1.1       jtc 		if (*cp == 'F')
    181       1.2       jtc 			rmtabort(fildes);
    182       1.1       jtc 
    183      1.24  christos 		return -1;
    184       1.1       jtc 	}
    185       1.1       jtc 
    186       1.1       jtc /*
    187       1.1       jtc  *	check for mis-synced pipes
    188       1.1       jtc  */
    189       1.1       jtc 
    190      1.14     lukem 	if (*cp != 'A') {
    191       1.2       jtc 		rmtabort(fildes);
    192       1.1       jtc 		errno = EIO;
    193      1.24  christos 		return -1;
    194       1.1       jtc 	}
    195       1.1       jtc 
    196      1.24  christos 	return atoi(cp + 1);
    197       1.1       jtc }
    198       1.1       jtc 
    199      1.14     lukem 
    200       1.1       jtc #ifdef USE_REXEC
    201       1.1       jtc /*
    202       1.1       jtc  * _rmt_rexec
    203       1.1       jtc  *
    204       1.1       jtc  * execute /etc/rmt on a remote system using rexec().
    205       1.1       jtc  * Return file descriptor of bidirectional socket for stdin and stdout
    206       1.1       jtc  * If username is NULL, or an empty string, uses current username.
    207       1.1       jtc  *
    208       1.1       jtc  * ADR: By default, this code is not used, since it requires that
    209       1.1       jtc  * the user have a .netrc file in his/her home directory, or that the
    210       1.1       jtc  * application designer be willing to have rexec prompt for login and
    211       1.1       jtc  * password info. This may be unacceptable, and .rhosts files for use
    212       1.1       jtc  * with rsh are much more common on BSD systems.
    213       1.1       jtc  */
    214       1.1       jtc 
    215      1.14     lukem static	int	_rmt_rexec(const char *, const char *);
    216       1.7     lukem 
    217       1.1       jtc static int
    218      1.14     lukem _rmt_rexec(const char *host, const char *user)
    219       1.1       jtc {
    220       1.1       jtc 	struct servent *rexecserv;
    221       1.1       jtc 
    222      1.12     lukem 	_DIAGASSERT(host != NULL);
    223      1.12     lukem 	/* user may be NULL */
    224      1.12     lukem 
    225       1.1       jtc 	rexecserv = getservbyname("exec", "tcp");
    226      1.24  christos 	if (rexecserv == NULL)
    227  1.24.2.1    cherry 		errx(1, "exec/tcp: service not available.");
    228       1.1       jtc 	if ((user != NULL) && *user == '\0')
    229      1.15     enami 		user = NULL;
    230      1.24  christos 	return rexec(&host, rexecserv->s_port, user, NULL,
    231      1.24  christos 	    "/etc/rmt", NULL);
    232       1.1       jtc }
    233       1.1       jtc #endif /* USE_REXEC */
    234       1.1       jtc 
    235      1.14     lukem 
    236       1.1       jtc /*
    237       1.1       jtc  *	_rmt_open --- open a magtape device on system specified, as given user
    238       1.1       jtc  *
    239       1.1       jtc  *	file name has the form [user@]system:/dev/????
    240       1.1       jtc #ifdef COMPAT
    241       1.1       jtc  *	file name has the form system[.user]:/dev/????
    242       1.1       jtc #endif
    243       1.1       jtc  */
    244       1.1       jtc 
    245       1.1       jtc #define MAXHOSTLEN	257	/* BSD allows very long host names... */
    246       1.1       jtc 
    247       1.7     lukem static int
    248      1.20  christos /*ARGSUSED*/
    249      1.14     lukem _rmt_open(const char *path, int oflag, int mode)
    250       1.1       jtc {
    251      1.24  christos 	int i;
    252       1.1       jtc 	char buffer[BUFMAGIC];
    253      1.18     lukem 	char host[MAXHOSTLEN];
    254       1.1       jtc 	char device[BUFMAGIC];
    255       1.1       jtc 	char login[BUFMAGIC];
    256       1.1       jtc 	char *sys, *dev, *user;
    257      1.24  christos 	char *rshpath, *rsh;
    258       1.1       jtc 
    259      1.12     lukem 	_DIAGASSERT(path != NULL);
    260      1.12     lukem 
    261      1.18     lukem 	sys = host;
    262       1.1       jtc 	dev = device;
    263       1.1       jtc 	user = login;
    264       1.1       jtc 
    265       1.1       jtc /*
    266       1.1       jtc  *	first, find an open pair of file descriptors
    267       1.1       jtc  */
    268       1.1       jtc 
    269       1.1       jtc 	for (i = 0; i < MAXUNIT; i++)
    270       1.1       jtc 		if (READ(i) == -1 && WRITE(i) == -1)
    271       1.1       jtc 			break;
    272       1.1       jtc 
    273      1.14     lukem 	if (i == MAXUNIT) {
    274       1.1       jtc 		errno = EMFILE;
    275      1.24  christos 		return -1;
    276       1.1       jtc 	}
    277       1.1       jtc 
    278       1.1       jtc /*
    279       1.1       jtc  *	pull apart system and device, and optional user
    280       1.1       jtc  *	don't munge original string
    281       1.1       jtc  *	if COMPAT is defined, also handle old (4.2) style person.site notation.
    282       1.1       jtc  */
    283       1.1       jtc 
    284       1.1       jtc 	while (*path != '@'
    285       1.1       jtc #ifdef COMPAT
    286       1.1       jtc 			&& *path != '.'
    287       1.1       jtc #endif
    288       1.1       jtc 			&& *path != ':') {
    289       1.1       jtc 		*sys++ = *path++;
    290       1.1       jtc 	}
    291       1.1       jtc 	*sys = '\0';
    292       1.1       jtc 	path++;
    293       1.1       jtc 
    294      1.14     lukem 	if (*(path - 1) == '@') {
    295      1.18     lukem 		(void)strncpy(user, host, sizeof(login) - 1);
    296       1.5       mrg 				/* saw user part of user@host */
    297      1.18     lukem 		sys = host;			/* start over */
    298       1.1       jtc 		while (*path != ':') {
    299       1.1       jtc 			*sys++ = *path++;
    300       1.1       jtc 		}
    301       1.1       jtc 		*sys = '\0';
    302       1.1       jtc 		path++;
    303       1.1       jtc 	}
    304       1.1       jtc #ifdef COMPAT
    305      1.14     lukem 	else if (*(path - 1) == '.') {
    306       1.1       jtc 		while (*path != ':') {
    307       1.1       jtc 			*user++ = *path++;
    308       1.1       jtc 		}
    309       1.1       jtc 		*user = '\0';
    310       1.1       jtc 		path++;
    311       1.1       jtc 	}
    312       1.1       jtc #endif
    313       1.1       jtc 	else
    314       1.1       jtc 		*user = '\0';
    315       1.1       jtc 
    316       1.1       jtc 	while (*path) {
    317       1.1       jtc 		*dev++ = *path++;
    318       1.1       jtc 	}
    319       1.1       jtc 	*dev = '\0';
    320       1.1       jtc 
    321       1.1       jtc #ifdef USE_REXEC
    322      1.11    simonb /*
    323      1.11    simonb  *	Execute the remote command using rexec
    324       1.1       jtc  */
    325      1.18     lukem 	READ(i) = WRITE(i) = _rmt_rexec(host, login);
    326       1.1       jtc 	if (READ(i) < 0)
    327      1.24  christos 		return -1;
    328       1.1       jtc #else
    329       1.1       jtc /*
    330       1.1       jtc  *	setup the pipes for the 'rsh' command and fork
    331       1.1       jtc  */
    332       1.1       jtc 
    333       1.1       jtc 	if (pipe(Ptc[i]) == -1 || pipe(Ctp[i]) == -1)
    334      1.24  christos 		return -1;
    335       1.1       jtc 
    336      1.24  christos 	switch (fork()) {
    337      1.24  christos 	case -1:
    338      1.24  christos 		return -1;
    339      1.10       mrg 
    340      1.24  christos 	case 0:
    341       1.1       jtc 		close(0);
    342       1.1       jtc 		dup(Ptc[i][0]);
    343       1.1       jtc 		close(Ptc[i][0]); close(Ptc[i][1]);
    344       1.1       jtc 		close(1);
    345       1.1       jtc 		dup(Ctp[i][1]);
    346       1.1       jtc 		close(Ctp[i][0]); close(Ctp[i][1]);
    347      1.14     lukem 		(void) setuid(getuid());
    348      1.14     lukem 		(void) setgid(getgid());
    349      1.11    simonb 
    350      1.10       mrg 		if ((rshpath = getenv("RCMD_CMD")) == NULL)
    351      1.10       mrg 			rshpath = _PATH_RSH;
    352      1.10       mrg 		if ((rsh = strrchr(rshpath, '/')) == NULL)
    353      1.10       mrg 			rsh = rshpath;
    354      1.10       mrg 		else
    355      1.10       mrg 			rsh++;
    356      1.11    simonb 
    357      1.14     lukem 		if (*login) {
    358      1.24  christos 			execl(rshpath, rsh, host, "-l", login, _PATH_RMT, NULL);
    359      1.14     lukem 		} else {
    360      1.24  christos 			execl(rshpath, rsh, host, _PATH_RMT, NULL);
    361       1.1       jtc 		}
    362       1.1       jtc 
    363       1.1       jtc /*
    364       1.1       jtc  *	bad problems if we get here
    365       1.1       jtc  */
    366       1.1       jtc 
    367      1.24  christos 		err(1, "Cannnot exec %s", rshpath);
    368      1.24  christos 		/*FALLTHROUGH*/
    369      1.24  christos 	default:
    370      1.24  christos 		break;
    371       1.1       jtc 	}
    372       1.1       jtc 
    373       1.1       jtc 	close(Ptc[i][0]); close(Ctp[i][1]);
    374       1.1       jtc #endif
    375       1.1       jtc 
    376       1.1       jtc /*
    377       1.1       jtc  *	now attempt to open the tape device
    378       1.1       jtc  */
    379       1.1       jtc 
    380       1.5       mrg 	(void)snprintf(buffer, sizeof(buffer), "O%s\n%d\n", device, oflag);
    381       1.1       jtc 	if (command(i, buffer) == -1 || status(i) == -1)
    382      1.24  christos 		return -1;
    383       1.1       jtc 
    384      1.24  christos 	return i;
    385       1.1       jtc }
    386       1.1       jtc 
    387       1.1       jtc 
    388       1.1       jtc /*
    389       1.1       jtc  *	_rmt_close --- close a remote magtape unit and shut down
    390       1.1       jtc  */
    391       1.7     lukem static int
    392      1.14     lukem _rmt_close(int fildes)
    393       1.1       jtc {
    394       1.1       jtc 	int rc;
    395       1.1       jtc 
    396      1.14     lukem 	if (command(fildes, "C\n") != -1) {
    397       1.1       jtc 		rc = status(fildes);
    398       1.1       jtc 
    399       1.2       jtc 		rmtabort(fildes);
    400      1.24  christos 		return rc;
    401       1.1       jtc 	}
    402       1.1       jtc 
    403      1.24  christos 	return -1;
    404       1.1       jtc }
    405       1.1       jtc 
    406       1.1       jtc 
    407       1.1       jtc /*
    408       1.1       jtc  *	_rmt_read --- read a buffer from a remote tape
    409       1.1       jtc  */
    410      1.14     lukem static ssize_t
    411      1.14     lukem _rmt_read(int fildes, void *buf, size_t nbyte)
    412       1.1       jtc {
    413      1.20  christos 	size_t rc;
    414      1.20  christos 	int rv;
    415      1.20  christos 	ssize_t nread;
    416      1.14     lukem 	char *p;
    417       1.1       jtc 	char buffer[BUFMAGIC];
    418       1.1       jtc 
    419      1.12     lukem 	_DIAGASSERT(buf != NULL);
    420      1.12     lukem 
    421      1.24  christos 	(void)snprintf(buffer, sizeof buffer, "R%zu\n", nbyte);
    422      1.20  christos 	if (command(fildes, buffer) == -1 || (rv = status(fildes)) == -1)
    423      1.24  christos 		return -1;
    424       1.1       jtc 
    425      1.20  christos 	if (rv > nbyte)
    426      1.24  christos 		rv = (int)nbyte;
    427      1.20  christos 
    428      1.20  christos 	for (rc = rv, p = buf; rc > 0; rc -= nread, p += nread) {
    429      1.20  christos 		if ((nread = read(READ(fildes), p, rc)) <= 0) {
    430       1.2       jtc 			rmtabort(fildes);
    431       1.1       jtc 			errno = EIO;
    432      1.24  christos 			return -1;
    433       1.1       jtc 		}
    434       1.1       jtc 	}
    435       1.1       jtc 
    436      1.20  christos 	return rv;
    437       1.1       jtc }
    438       1.1       jtc 
    439       1.1       jtc 
    440       1.1       jtc /*
    441       1.1       jtc  *	_rmt_write --- write a buffer to the remote tape
    442       1.1       jtc  */
    443      1.14     lukem static ssize_t
    444      1.14     lukem _rmt_write(int fildes, const void *buf, size_t nbyte)
    445       1.1       jtc {
    446       1.1       jtc 	char buffer[BUFMAGIC];
    447      1.24  christos 	sig_t pstat;
    448       1.1       jtc 
    449      1.12     lukem 	_DIAGASSERT(buf != NULL);
    450      1.12     lukem 
    451      1.24  christos 	(void)snprintf(buffer, sizeof buffer, "W%zu\n", nbyte);
    452       1.1       jtc 	if (command(fildes, buffer) == -1)
    453      1.24  christos 		return -1;
    454       1.1       jtc 
    455       1.1       jtc 	pstat = signal(SIGPIPE, SIG_IGN);
    456      1.14     lukem 	if (write(WRITE(fildes), buf, nbyte) == nbyte) {
    457      1.14     lukem 		signal(SIGPIPE, pstat);
    458      1.24  christos 		return status(fildes);
    459       1.1       jtc 	}
    460       1.1       jtc 
    461      1.14     lukem 	signal(SIGPIPE, pstat);
    462       1.2       jtc 	rmtabort(fildes);
    463       1.1       jtc 	errno = EIO;
    464      1.24  christos 	return -1;
    465       1.1       jtc }
    466       1.1       jtc 
    467       1.1       jtc 
    468       1.1       jtc /*
    469       1.1       jtc  *	_rmt_lseek --- perform an imitation lseek operation remotely
    470       1.1       jtc  */
    471       1.7     lukem static off_t
    472      1.14     lukem _rmt_lseek(int fildes, off_t offset, int whence)
    473       1.1       jtc {
    474       1.1       jtc 	char buffer[BUFMAGIC];
    475       1.1       jtc 
    476      1.20  christos 	/*LONGLONG*/
    477      1.14     lukem 	(void)snprintf(buffer, sizeof buffer, "L%lld\n%d\n", (long long)offset,
    478       1.8       mrg 	    whence);
    479       1.1       jtc 	if (command(fildes, buffer) == -1)
    480      1.24  christos 		return -1;
    481       1.1       jtc 
    482      1.24  christos 	return status(fildes);
    483       1.1       jtc }
    484       1.1       jtc 
    485       1.1       jtc 
    486       1.1       jtc /*
    487       1.1       jtc  *	_rmt_ioctl --- perform raw tape operations remotely
    488       1.1       jtc  */
    489       1.1       jtc #ifdef RMTIOCTL
    490       1.7     lukem static int
    491      1.14     lukem _rmt_ioctl(int fildes, unsigned long op, void *arg)
    492       1.1       jtc {
    493       1.1       jtc 	char c;
    494      1.20  christos 	int rv;
    495      1.20  christos 	size_t rc;
    496      1.20  christos 	ssize_t cnt;
    497      1.14     lukem 	char buffer[BUFMAGIC], *p;
    498      1.24  christos 	struct mtop *mtop = arg;
    499       1.1       jtc 
    500      1.12     lukem 	_DIAGASSERT(arg != NULL);
    501      1.12     lukem 
    502       1.1       jtc /*
    503       1.1       jtc  *	MTIOCOP is the easy one. nothing is transfered in binary
    504       1.1       jtc  */
    505       1.1       jtc 
    506      1.14     lukem 	if (op == MTIOCTOP) {
    507       1.5       mrg 		(void)snprintf(buffer, sizeof buffer, "I%d\n%d\n",
    508      1.24  christos 		    mtop->mt_op, mtop->mt_count);
    509       1.1       jtc 		if (command(fildes, buffer) == -1)
    510      1.24  christos 			return -1;
    511      1.24  christos 		return status(fildes);
    512       1.1       jtc 	}
    513       1.1       jtc 
    514       1.1       jtc /*
    515       1.1       jtc  *	we can only handle 2 ops, if not the other one, punt
    516       1.1       jtc  */
    517       1.1       jtc 
    518      1.14     lukem 	if (op != MTIOCGET) {
    519       1.1       jtc 		errno = EINVAL;
    520      1.24  christos 		return -1;
    521       1.1       jtc 	}
    522       1.1       jtc 
    523       1.1       jtc /*
    524       1.1       jtc  *	grab the status and read it directly into the structure
    525       1.1       jtc  *	this assumes that the status buffer is (hopefully) not
    526       1.1       jtc  *	padded and that 2 shorts fit in a long without any word
    527       1.1       jtc  *	alignment problems, ie - the whole struct is contiguous
    528       1.1       jtc  *	NOTE - this is probably NOT a good assumption.
    529       1.1       jtc  */
    530       1.1       jtc 
    531      1.20  christos 	if (command(fildes, "S") == -1 || (rv = status(fildes)) == -1)
    532      1.24  christos 		return -1;
    533       1.1       jtc 
    534      1.24  christos 	memset(arg, 0, sizeof(struct mtget));
    535      1.20  christos 	for (rc = rv, p = arg; rc > 0; rc -= cnt, p += cnt) {
    536      1.20  christos 		if ((cnt = read(READ(fildes), p, rc)) <= 0) {
    537       1.2       jtc 			rmtabort(fildes);
    538       1.1       jtc 			errno = EIO;
    539      1.24  christos 			return -1;
    540       1.1       jtc 		}
    541       1.1       jtc 	}
    542       1.1       jtc 
    543       1.1       jtc /*
    544       1.1       jtc  *	now we check for byte position. mt_type is a small integer field
    545       1.1       jtc  *	(normally) so we will check its magnitude. if it is larger than
    546       1.1       jtc  *	256, we will assume that the bytes are swapped and go through
    547       1.1       jtc  *	and reverse all the bytes
    548       1.1       jtc  */
    549       1.1       jtc 
    550      1.20  christos 	if (((struct mtget *)(void *)p)->mt_type < 256)
    551      1.24  christos 		return 0;
    552       1.1       jtc 
    553      1.21  christos 	for (cnt = 0; cnt < rv; cnt += 2) {
    554      1.14     lukem 		c = p[cnt];
    555      1.24  christos 		p[cnt] = p[cnt + 1];
    556      1.24  christos 		p[cnt + 1] = c;
    557       1.1       jtc 	}
    558       1.1       jtc 
    559      1.24  christos 	return 0;
    560      1.15     enami }
    561       1.1       jtc #endif /* RMTIOCTL */
    562       1.1       jtc 
    563      1.14     lukem 
    564       1.1       jtc /*
    565       1.1       jtc  *	Added routines to replace open(), close(), lseek(), ioctl(), etc.
    566       1.1       jtc  *	The preprocessor can be used to remap these the rmtopen(), etc
    567       1.1       jtc  *	thus minimizing source changes:
    568       1.1       jtc  *
    569       1.1       jtc  *		#ifdef <something>
    570       1.1       jtc  *		#  define access rmtaccess
    571       1.1       jtc  *		#  define close rmtclose
    572       1.1       jtc  *		#  define creat rmtcreat
    573       1.1       jtc  *		#  define dup rmtdup
    574       1.1       jtc  *		#  define fcntl rmtfcntl
    575       1.1       jtc  *		#  define fstat rmtfstat
    576       1.1       jtc  *		#  define ioctl rmtioctl
    577       1.1       jtc  *		#  define isatty rmtisatty
    578       1.1       jtc  *		#  define lseek rmtlseek
    579       1.1       jtc  *		#  define lstat rmtlstat
    580       1.1       jtc  *		#  define open rmtopen
    581       1.1       jtc  *		#  define read rmtread
    582       1.1       jtc  *		#  define stat rmtstat
    583       1.1       jtc  *		#  define write rmtwrite
    584       1.1       jtc  *		#endif
    585       1.1       jtc  *
    586       1.1       jtc  *	-- Fred Fish
    587       1.1       jtc  *
    588       1.1       jtc  *	ADR --- I set up a <rmt.h> include file for this
    589       1.1       jtc  *
    590       1.1       jtc  */
    591       1.1       jtc 
    592       1.1       jtc /*
    593       1.1       jtc  *	Note that local vs remote file descriptors are distinquished
    594       1.1       jtc  *	by adding a bias to the remote descriptors.  This is a quick
    595       1.1       jtc  *	and dirty trick that may not be portable to some systems.
    596       1.1       jtc  */
    597       1.1       jtc 
    598       1.1       jtc #define REM_BIAS 128
    599       1.1       jtc 
    600       1.1       jtc 
    601       1.1       jtc /*
    602       1.1       jtc  *	Test pathname to see if it is local or remote.  A remote device
    603       1.1       jtc  *	is any string that contains ":/dev/".  Returns 1 if remote,
    604       1.1       jtc  *	0 otherwise.
    605       1.1       jtc  */
    606      1.11    simonb 
    607       1.7     lukem static int
    608      1.14     lukem remdev(const char *path)
    609       1.1       jtc {
    610      1.12     lukem 
    611      1.12     lukem 	_DIAGASSERT(path != NULL);
    612      1.12     lukem 
    613      1.14     lukem 	if ((path = strchr(path, ':')) != NULL) {
    614      1.14     lukem 		if (strncmp(path + 1, "/dev/", 5) == 0) {
    615      1.24  christos 			return 1;
    616       1.1       jtc 		}
    617       1.1       jtc 	}
    618      1.24  christos 	return 0;
    619       1.1       jtc }
    620       1.1       jtc 
    621       1.1       jtc 
    622       1.1       jtc /*
    623       1.1       jtc  *	Open a local or remote file.  Looks just like open(2) to
    624       1.1       jtc  *	caller.
    625       1.1       jtc  */
    626       1.7     lukem int
    627       1.9   thorpej rmtopen(const char *path, int oflag, ...)
    628       1.9   thorpej {
    629       1.9   thorpej 	mode_t mode;
    630       1.9   thorpej 	int fd;
    631       1.9   thorpej 	va_list ap;
    632       1.9   thorpej 	va_start(ap, oflag);
    633       1.9   thorpej 
    634       1.9   thorpej 	mode = va_arg(ap, mode_t);
    635       1.9   thorpej 	va_end(ap);
    636       1.1       jtc 
    637      1.12     lukem 	_DIAGASSERT(path != NULL);
    638      1.12     lukem 
    639      1.14     lukem 	if (remdev(path)) {
    640      1.20  christos 		fd = _rmt_open(path, oflag, (int)mode);
    641       1.1       jtc 
    642      1.24  christos 		return (fd == -1) ? -1 : (fd + REM_BIAS);
    643      1.14     lukem 	} else {
    644      1.24  christos 		return open(path, oflag, mode);
    645       1.1       jtc 	}
    646       1.1       jtc }
    647       1.1       jtc 
    648       1.1       jtc /*
    649       1.1       jtc  *	Test pathname for specified access.  Looks just like access(2)
    650       1.1       jtc  *	to caller.
    651       1.1       jtc  */
    652      1.11    simonb 
    653       1.7     lukem int
    654      1.14     lukem rmtaccess(const char *path, int amode)
    655       1.1       jtc {
    656      1.12     lukem 
    657      1.12     lukem 	_DIAGASSERT(path != NULL);
    658      1.12     lukem 
    659      1.14     lukem 	if (remdev(path)) {
    660      1.24  christos 		return 0;		/* Let /etc/rmt find out */
    661      1.14     lukem 	} else {
    662      1.24  christos 		return access(path, amode);
    663       1.1       jtc 	}
    664       1.1       jtc }
    665       1.1       jtc 
    666       1.1       jtc 
    667       1.1       jtc /*
    668       1.6     mikel  *	Isrmt. Let a programmer know he has a remote device.
    669       1.6     mikel  */
    670       1.7     lukem int
    671      1.14     lukem isrmt(int fd)
    672       1.6     mikel {
    673      1.23     pooka 	int unbias = fd - REM_BIAS;
    674      1.14     lukem 
    675      1.23     pooka 	return (fd >= REM_BIAS) && unbias < MAXUNIT &&
    676      1.23     pooka 	    (WRITE(unbias) != -1 || READ(unbias) != -1);
    677       1.6     mikel }
    678       1.6     mikel 
    679       1.6     mikel 
    680       1.6     mikel /*
    681       1.1       jtc  *	Read from stream.  Looks just like read(2) to caller.
    682       1.1       jtc  */
    683       1.7     lukem ssize_t
    684      1.14     lukem rmtread(int fildes, void *buf, size_t nbyte)
    685       1.1       jtc {
    686      1.12     lukem 
    687      1.12     lukem 	_DIAGASSERT(buf != NULL);
    688      1.12     lukem 
    689      1.14     lukem 	if (isrmt(fildes)) {
    690      1.24  christos 		return _rmt_read(fildes - REM_BIAS, buf, nbyte);
    691      1.14     lukem 	} else {
    692      1.24  christos 		return read(fildes, buf, nbyte);
    693       1.1       jtc 	}
    694       1.1       jtc }
    695       1.1       jtc 
    696       1.1       jtc 
    697       1.1       jtc /*
    698       1.1       jtc  *	Write to stream.  Looks just like write(2) to caller.
    699       1.1       jtc  */
    700       1.7     lukem ssize_t
    701      1.14     lukem rmtwrite(int fildes, const void *buf, size_t nbyte)
    702       1.1       jtc {
    703      1.12     lukem 
    704      1.12     lukem 	_DIAGASSERT(buf != NULL);
    705      1.12     lukem 
    706      1.14     lukem 	if (isrmt(fildes)) {
    707      1.24  christos 		return _rmt_write(fildes - REM_BIAS, buf, nbyte);
    708      1.14     lukem 	} else {
    709      1.24  christos 		return write(fildes, buf, nbyte);
    710       1.1       jtc 	}
    711       1.1       jtc }
    712       1.1       jtc 
    713       1.1       jtc /*
    714       1.1       jtc  *	Perform lseek on file.  Looks just like lseek(2) to caller.
    715       1.1       jtc  */
    716       1.7     lukem off_t
    717      1.14     lukem rmtlseek(int fildes, off_t offset, int whence)
    718      1.14     lukem {
    719      1.15     enami 
    720      1.14     lukem 	if (isrmt(fildes)) {
    721      1.24  christos 		return _rmt_lseek(fildes - REM_BIAS, offset, whence);
    722      1.14     lukem 	} else {
    723      1.24  christos 		return lseek(fildes, offset, whence);
    724       1.1       jtc 	}
    725       1.1       jtc }
    726       1.1       jtc 
    727       1.1       jtc 
    728       1.1       jtc /*
    729       1.1       jtc  *	Close a file.  Looks just like close(2) to caller.
    730       1.1       jtc  */
    731       1.7     lukem int
    732      1.14     lukem rmtclose(int fildes)
    733       1.1       jtc {
    734      1.15     enami 
    735      1.14     lukem 	if (isrmt(fildes)) {
    736      1.24  christos 		return _rmt_close(fildes - REM_BIAS);
    737      1.14     lukem 	} else {
    738      1.24  christos 		return close(fildes);
    739       1.1       jtc 	}
    740       1.1       jtc }
    741       1.1       jtc 
    742      1.14     lukem 
    743       1.1       jtc /*
    744       1.1       jtc  *	Do ioctl on file.  Looks just like ioctl(2) to caller.
    745       1.1       jtc  */
    746       1.7     lukem int
    747       1.9   thorpej rmtioctl(int fildes, unsigned long request, ...)
    748       1.9   thorpej {
    749      1.24  christos 	void *arg;
    750       1.9   thorpej 	va_list ap;
    751       1.9   thorpej 	va_start(ap, request);
    752       1.9   thorpej 
    753      1.24  christos 	arg = va_arg(ap, void *);
    754       1.9   thorpej 	va_end(ap);
    755       1.9   thorpej 
    756      1.12     lukem 	/* XXX: arg may be NULL ? */
    757      1.12     lukem 
    758      1.14     lukem 	if (isrmt(fildes)) {
    759       1.1       jtc #ifdef RMTIOCTL
    760      1.24  christos 		return _rmt_ioctl(fildes - REM_BIAS, request, arg);
    761       1.1       jtc #else
    762       1.1       jtc 		errno = EOPNOTSUPP;
    763      1.24  christos 		return -1;		/* For now (fnf) */
    764       1.1       jtc #endif
    765      1.14     lukem 	} else {
    766      1.24  christos 		return ioctl(fildes, request, arg);
    767       1.1       jtc 	}
    768       1.1       jtc }
    769       1.1       jtc 
    770       1.1       jtc 
    771       1.1       jtc /*
    772       1.1       jtc  *	Duplicate an open file descriptor.  Looks just like dup(2)
    773       1.1       jtc  *	to caller.
    774       1.1       jtc  */
    775       1.7     lukem int
    776      1.14     lukem rmtdup(int fildes)
    777       1.1       jtc {
    778      1.15     enami 
    779      1.14     lukem 	if (isrmt(fildes)) {
    780       1.1       jtc 		errno = EOPNOTSUPP;
    781      1.24  christos 		return -1;		/* For now (fnf) */
    782      1.14     lukem 	} else {
    783      1.24  christos 		return dup(fildes);
    784       1.1       jtc 	}
    785       1.1       jtc }
    786       1.1       jtc 
    787      1.14     lukem 
    788       1.1       jtc /*
    789       1.1       jtc  *	Get file status.  Looks just like fstat(2) to caller.
    790       1.1       jtc  */
    791       1.7     lukem int
    792      1.14     lukem rmtfstat(int fildes, struct stat *buf)
    793       1.1       jtc {
    794      1.12     lukem 
    795      1.12     lukem 	_DIAGASSERT(buf != NULL);
    796      1.12     lukem 
    797      1.14     lukem 	if (isrmt(fildes)) {
    798       1.1       jtc 		errno = EOPNOTSUPP;
    799      1.24  christos 		return -1;		/* For now (fnf) */
    800      1.14     lukem 	} else {
    801      1.24  christos 		return fstat(fildes, buf);
    802       1.1       jtc 	}
    803       1.1       jtc }
    804       1.1       jtc 
    805       1.1       jtc 
    806       1.1       jtc /*
    807       1.1       jtc  *	Get file status.  Looks just like stat(2) to caller.
    808       1.1       jtc  */
    809       1.7     lukem int
    810      1.14     lukem rmtstat(const char *path, struct stat *buf)
    811       1.1       jtc {
    812      1.12     lukem 
    813      1.12     lukem 	_DIAGASSERT(path != NULL);
    814      1.12     lukem 	_DIAGASSERT(buf != NULL);
    815      1.12     lukem 
    816      1.14     lukem 	if (remdev(path)) {
    817       1.1       jtc 		errno = EOPNOTSUPP;
    818      1.24  christos 		return -1;		/* For now (fnf) */
    819      1.14     lukem 	} else {
    820      1.24  christos 		return stat(path, buf);
    821       1.1       jtc 	}
    822       1.1       jtc }
    823       1.1       jtc 
    824       1.1       jtc 
    825       1.1       jtc /*
    826       1.1       jtc  *	Create a file from scratch.  Looks just like creat(2) to the caller.
    827       1.1       jtc  */
    828       1.7     lukem int
    829      1.14     lukem rmtcreat(const char *path, mode_t mode)
    830       1.1       jtc {
    831      1.12     lukem 
    832      1.12     lukem 	_DIAGASSERT(path != NULL);
    833      1.12     lukem 
    834      1.14     lukem 	if (remdev(path)) {
    835      1.24  christos 		return rmtopen(path, O_WRONLY | O_CREAT, mode);
    836      1.14     lukem 	} else {
    837      1.24  christos 		return open(path, O_CREAT | O_TRUNC | O_WRONLY, mode);
    838       1.1       jtc 	}
    839       1.1       jtc }
    840       1.1       jtc 
    841      1.14     lukem 
    842       1.1       jtc /*
    843       1.1       jtc  *	Rmtfcntl. Do a remote fcntl operation.
    844       1.1       jtc  */
    845       1.7     lukem int
    846       1.9   thorpej rmtfcntl(int fd, int cmd, ...)
    847       1.1       jtc {
    848       1.9   thorpej 	void *arg;
    849       1.9   thorpej 	va_list ap;
    850       1.9   thorpej 	va_start(ap, cmd);
    851       1.9   thorpej 
    852       1.9   thorpej 	arg = va_arg(ap, void *);
    853       1.9   thorpej 	va_end(ap);
    854       1.9   thorpej 
    855      1.12     lukem 	/* XXX: arg may be NULL ? */
    856      1.12     lukem 
    857      1.14     lukem 	if (isrmt(fd)) {
    858       1.1       jtc 		errno = EOPNOTSUPP;
    859      1.24  christos 		return -1;
    860      1.14     lukem 	} else {
    861      1.24  christos 		return fcntl(fd, cmd, arg);
    862       1.1       jtc 	}
    863       1.1       jtc }
    864       1.1       jtc 
    865      1.14     lukem 
    866       1.1       jtc /*
    867       1.1       jtc  *	Rmtisatty.  Do the isatty function.
    868       1.1       jtc  */
    869       1.7     lukem int
    870      1.14     lukem rmtisatty(int fd)
    871       1.1       jtc {
    872      1.14     lukem 
    873      1.14     lukem 	if (isrmt(fd))
    874      1.24  christos 		return 0;
    875       1.1       jtc 	else
    876      1.24  christos 		return isatty(fd);
    877       1.1       jtc }
    878       1.1       jtc 
    879       1.1       jtc 
    880       1.1       jtc /*
    881       1.1       jtc  *	Get file status, even if symlink.  Looks just like lstat(2) to caller.
    882       1.1       jtc  */
    883       1.7     lukem int
    884      1.14     lukem rmtlstat(const char *path, struct stat *buf)
    885       1.1       jtc {
    886      1.12     lukem 
    887      1.12     lukem 	_DIAGASSERT(path != NULL);
    888      1.12     lukem 	_DIAGASSERT(buf != NULL);
    889      1.12     lukem 
    890      1.14     lukem 	if (remdev(path)) {
    891       1.1       jtc 		errno = EOPNOTSUPP;
    892      1.24  christos 		return -1;		/* For now (fnf) */
    893      1.14     lukem 	} else {
    894      1.24  christos 		return lstat(path, buf);
    895       1.1       jtc 	}
    896       1.1       jtc }
    897