xref: /netbsd-src/lib/librmt/rmtlib.c (revision 4a3e2a829545566f6a14044eb27b51011901769d)
1*4a3e2a82Sandvar /*	$NetBSD: rmtlib.c,v 1.29 2024/03/22 19:36:56 andvar Exp $	*/
267ead261Sjtc 
367ead261Sjtc /*
467ead261Sjtc  *	rmt --- remote tape emulator subroutines
567ead261Sjtc  *
667ead261Sjtc  *	Originally written by Jeff Lee, modified some by Arnold Robbins
767ead261Sjtc  *
867ead261Sjtc  *	WARNING:  The man page rmt(8) for /etc/rmt documents the remote mag
967ead261Sjtc  *	tape protocol which rdump and rrestore use.  Unfortunately, the man
1067ead261Sjtc  *	page is *WRONG*.  The author of the routines I'm including originally
1167ead261Sjtc  *	wrote his code just based on the man page, and it didn't work, so he
1267ead261Sjtc  *	went to the rdump source to figure out why.  The only thing he had to
1367ead261Sjtc  *	change was to check for the 'F' return code in addition to the 'E',
1467ead261Sjtc  *	and to separate the various arguments with \n instead of a space.  I
1567ead261Sjtc  *	personally don't think that this is much of a problem, but I wanted to
1667ead261Sjtc  *	point it out.
1767ead261Sjtc  *	-- Arnold Robbins
1867ead261Sjtc  *
1967ead261Sjtc  *	Redone as a library that can replace open, read, write, etc, by
2067ead261Sjtc  *	Fred Fish, with some additional work by Arnold Robbins.
2167ead261Sjtc  */
2267ead261Sjtc 
2367ead261Sjtc /*
2467ead261Sjtc  *	MAXUNIT --- Maximum number of remote tape file units
2567ead261Sjtc  *
2667ead261Sjtc  *	READ --- Return the number of the read side file descriptor
2767ead261Sjtc  *	WRITE --- Return the number of the write side file descriptor
2867ead261Sjtc  */
2967ead261Sjtc 
30e2d78706Slukem #include <sys/cdefs.h>
31*4a3e2a82Sandvar __RCSID("$NetBSD: rmtlib.c,v 1.29 2024/03/22 19:36:56 andvar Exp $");
32e2d78706Slukem 
3367ead261Sjtc #define RMTIOCTL	1
34e6077a76Smikel /* #define USE_REXEC	1 */	/* rexec code courtesy of Dan Kegel, srs!dan */
3567ead261Sjtc 
3667ead261Sjtc #include <sys/types.h>
37b48252f3Slukem #include <sys/stat.h>
3867ead261Sjtc 
3967ead261Sjtc #ifdef RMTIOCTL
4067ead261Sjtc #include <sys/ioctl.h>
4167ead261Sjtc #include <sys/mtio.h>
4267ead261Sjtc #endif
4367ead261Sjtc 
44b48252f3Slukem #include <assert.h>
45b48252f3Slukem #include <errno.h>
46b48252f3Slukem #include <fcntl.h>
47b48252f3Slukem #include <signal.h>
4832dd941fSlukem #include <stdarg.h>
49b48252f3Slukem #include <stdio.h>
50b48252f3Slukem #include <stdlib.h>
51b48252f3Slukem #include <string.h>
52b48252f3Slukem #include <unistd.h>
5388b197ecSchristos #include <err.h>
54b48252f3Slukem 
5567ead261Sjtc #ifdef USE_REXEC
5667ead261Sjtc #include <netdb.h>
5767ead261Sjtc #endif
5867ead261Sjtc 
596e190eedSthorpej #define __RMTLIB_PRIVATE
606e190eedSthorpej #include <rmt.h>		/* get prototypes for remapped functions */
616e190eedSthorpej 
625f2732abSmrg #include "pathnames.h"
635f2732abSmrg 
6432dd941fSlukem static	int	_rmt_close(int);
6532dd941fSlukem static	int	_rmt_ioctl(int, unsigned long, void *);
6632dd941fSlukem static	off_t	_rmt_lseek(int, off_t, int);
6732dd941fSlukem static	int	_rmt_open(const char *, int, int);
6832dd941fSlukem static	ssize_t	_rmt_read(int, void *, size_t);
6932dd941fSlukem static	ssize_t	_rmt_write(int, const void *, size_t);
7088b197ecSchristos static	int	command(int, const char *);
7132dd941fSlukem static	int	remdev(const char *);
7232dd941fSlukem static	void	rmtabort(int);
7332dd941fSlukem static	int	status(int);
74ae6b9c67Slukem 
75ae6b9c67Slukem 
7667ead261Sjtc #define BUFMAGIC	64	/* a magic number for buffer sizes */
7767ead261Sjtc #define MAXUNIT		4
7867ead261Sjtc 
7967ead261Sjtc #define READ(fd)	(Ctp[fd][0])
8067ead261Sjtc #define WRITE(fd)	(Ptc[fd][1])
8167ead261Sjtc 
82e6077a76Smikel static int Ctp[MAXUNIT][2] = { {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1} };
83e6077a76Smikel static int Ptc[MAXUNIT][2] = { {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1} };
8467ead261Sjtc 
8567ead261Sjtc 
8667ead261Sjtc /*
878ee4077eSjtc  *	rmtabort --- close off a remote tape connection
8867ead261Sjtc  */
89ae6b9c67Slukem static void
rmtabort(int fildes)9032dd941fSlukem rmtabort(int fildes)
9167ead261Sjtc {
9232dd941fSlukem 
9367ead261Sjtc 	close(READ(fildes));
9467ead261Sjtc 	close(WRITE(fildes));
9567ead261Sjtc 	READ(fildes) = -1;
9667ead261Sjtc 	WRITE(fildes) = -1;
9767ead261Sjtc }
9867ead261Sjtc 
9967ead261Sjtc 
10067ead261Sjtc /*
10167ead261Sjtc  *	command --- attempt to perform a remote tape command
10267ead261Sjtc  */
103ae6b9c67Slukem static int
command(int fildes,const char * buf)10488b197ecSchristos command(int fildes, const char *buf)
10567ead261Sjtc {
10632dd941fSlukem 	size_t blen;
10788b197ecSchristos 	sig_t pstat;
10867ead261Sjtc 
109b48252f3Slukem 	_DIAGASSERT(buf != NULL);
110b48252f3Slukem 
11167ead261Sjtc /*
11267ead261Sjtc  *	save current pipe status and try to make the request
11367ead261Sjtc  */
11467ead261Sjtc 
11567ead261Sjtc 	blen = strlen(buf);
11667ead261Sjtc 	pstat = signal(SIGPIPE, SIG_IGN);
117e1a2f47fSmatt 	if ((size_t)write(WRITE(fildes), buf, blen) == blen) {
11867ead261Sjtc 		signal(SIGPIPE, pstat);
11988b197ecSchristos 		return 0;
12067ead261Sjtc 	}
12167ead261Sjtc 
12267ead261Sjtc /*
12367ead261Sjtc  *	something went wrong. close down and go home
12467ead261Sjtc  */
12567ead261Sjtc 
12667ead261Sjtc 	signal(SIGPIPE, pstat);
1278ee4077eSjtc 	rmtabort(fildes);
12867ead261Sjtc 
12967ead261Sjtc 	errno = EIO;
13088b197ecSchristos 	return -1;
13167ead261Sjtc }
13267ead261Sjtc 
13367ead261Sjtc 
13467ead261Sjtc /*
13567ead261Sjtc  *	status --- retrieve the status from the pipe
13667ead261Sjtc  */
137ae6b9c67Slukem static int
status(int fildes)13832dd941fSlukem status(int fildes)
13967ead261Sjtc {
14067ead261Sjtc 	int i;
14167ead261Sjtc 	char c, *cp;
14267ead261Sjtc 	char buffer[BUFMAGIC];
14367ead261Sjtc 
14467ead261Sjtc /*
14567ead261Sjtc  *	read the reply command line
14667ead261Sjtc  */
14767ead261Sjtc 
14832dd941fSlukem 	for (i = 0, cp = buffer; i < BUFMAGIC; i++, cp++) {
14932dd941fSlukem 		if (read(READ(fildes), cp, 1) != 1) {
1508ee4077eSjtc 			rmtabort(fildes);
15167ead261Sjtc 			errno = EIO;
15288b197ecSchristos 			return -1;
15367ead261Sjtc 		}
15432dd941fSlukem 		if (*cp == '\n') {
15567ead261Sjtc 			*cp = 0;
15667ead261Sjtc 			break;
15767ead261Sjtc 		}
15867ead261Sjtc 	}
15967ead261Sjtc 
16032dd941fSlukem 	if (i == BUFMAGIC) {
1618ee4077eSjtc 		rmtabort(fildes);
16267ead261Sjtc 		errno = EIO;
16388b197ecSchristos 		return -1;
16467ead261Sjtc 	}
16567ead261Sjtc 
16667ead261Sjtc /*
16767ead261Sjtc  *	check the return status
16867ead261Sjtc  */
16967ead261Sjtc 
17067ead261Sjtc 	for (cp = buffer; *cp; cp++)
17167ead261Sjtc 		if (*cp != ' ')
17267ead261Sjtc 			break;
17367ead261Sjtc 
17432dd941fSlukem 	if (*cp == 'E' || *cp == 'F') {
17567ead261Sjtc 		errno = atoi(cp + 1);
17667ead261Sjtc 		while (read(READ(fildes), &c, 1) == 1)
17767ead261Sjtc 			if (c == '\n')
17867ead261Sjtc 				break;
17967ead261Sjtc 
18067ead261Sjtc 		if (*cp == 'F')
1818ee4077eSjtc 			rmtabort(fildes);
18267ead261Sjtc 
18388b197ecSchristos 		return -1;
18467ead261Sjtc 	}
18567ead261Sjtc 
18667ead261Sjtc /*
18767ead261Sjtc  *	check for mis-synced pipes
18867ead261Sjtc  */
18967ead261Sjtc 
19032dd941fSlukem 	if (*cp != 'A') {
1918ee4077eSjtc 		rmtabort(fildes);
19267ead261Sjtc 		errno = EIO;
19388b197ecSchristos 		return -1;
19467ead261Sjtc 	}
19567ead261Sjtc 
19688b197ecSchristos 	return atoi(cp + 1);
19767ead261Sjtc }
19867ead261Sjtc 
19967ead261Sjtc 
20032dd941fSlukem #ifdef USE_REXEC
20167ead261Sjtc /*
20267ead261Sjtc  * _rmt_rexec
20367ead261Sjtc  *
20467ead261Sjtc  * execute /etc/rmt on a remote system using rexec().
20567ead261Sjtc  * Return file descriptor of bidirectional socket for stdin and stdout
20667ead261Sjtc  * If username is NULL, or an empty string, uses current username.
20767ead261Sjtc  *
20867ead261Sjtc  * ADR: By default, this code is not used, since it requires that
20967ead261Sjtc  * the user have a .netrc file in his/her home directory, or that the
21067ead261Sjtc  * application designer be willing to have rexec prompt for login and
21167ead261Sjtc  * password info. This may be unacceptable, and .rhosts files for use
21267ead261Sjtc  * with rsh are much more common on BSD systems.
21367ead261Sjtc  */
21467ead261Sjtc 
21532dd941fSlukem static	int	_rmt_rexec(const char *, const char *);
216ae6b9c67Slukem 
21767ead261Sjtc static int
_rmt_rexec(const char * host,const char * user)21832dd941fSlukem _rmt_rexec(const char *host, const char *user)
21967ead261Sjtc {
22067ead261Sjtc 	struct servent *rexecserv;
22167ead261Sjtc 
222b48252f3Slukem 	_DIAGASSERT(host != NULL);
223b48252f3Slukem 	/* user may be NULL */
224b48252f3Slukem 
22567ead261Sjtc 	rexecserv = getservbyname("exec", "tcp");
22688b197ecSchristos 	if (rexecserv == NULL)
22777c15e81Smrg 		errx(1, "exec/tcp: service not available.");
22867ead261Sjtc 	if ((user != NULL) && *user == '\0')
22943ca618dSenami 		user = NULL;
23088b197ecSchristos 	return rexec(&host, rexecserv->s_port, user, NULL,
23188b197ecSchristos 	    "/etc/rmt", NULL);
23267ead261Sjtc }
23367ead261Sjtc #endif /* USE_REXEC */
23467ead261Sjtc 
23532dd941fSlukem 
23667ead261Sjtc /*
23767ead261Sjtc  *	_rmt_open --- open a magtape device on system specified, as given user
23867ead261Sjtc  *
23967ead261Sjtc  *	file name has the form [user@]system:/dev/????
24067ead261Sjtc #ifdef COMPAT
24167ead261Sjtc  *	file name has the form system[.user]:/dev/????
24267ead261Sjtc #endif
24367ead261Sjtc  */
24467ead261Sjtc 
24567ead261Sjtc #define MAXHOSTLEN	257	/* BSD allows very long host names... */
24667ead261Sjtc 
247ae6b9c67Slukem static int
24844fad74bSchristos /*ARGSUSED*/
_rmt_open(const char * path,int oflag,int mode)24932dd941fSlukem _rmt_open(const char *path, int oflag, int mode)
25067ead261Sjtc {
25188b197ecSchristos 	int i;
252d3574aa7Schristos 	char buffer[2 * BUFMAGIC];
2536a973ed8Slukem 	char host[MAXHOSTLEN];
25467ead261Sjtc 	char device[BUFMAGIC];
25567ead261Sjtc 	char login[BUFMAGIC];
25667ead261Sjtc 	char *sys, *dev, *user;
257e1a2f47fSmatt 	const char *rshpath, *rsh;
25867ead261Sjtc 
259b48252f3Slukem 	_DIAGASSERT(path != NULL);
260b48252f3Slukem 
2616a973ed8Slukem 	sys = host;
26267ead261Sjtc 	dev = device;
26367ead261Sjtc 	user = login;
26467ead261Sjtc 
26567ead261Sjtc /*
26667ead261Sjtc  *	first, find an open pair of file descriptors
26767ead261Sjtc  */
26867ead261Sjtc 
26967ead261Sjtc 	for (i = 0; i < MAXUNIT; i++)
27067ead261Sjtc 		if (READ(i) == -1 && WRITE(i) == -1)
27167ead261Sjtc 			break;
27267ead261Sjtc 
27332dd941fSlukem 	if (i == MAXUNIT) {
27467ead261Sjtc 		errno = EMFILE;
27588b197ecSchristos 		return -1;
27667ead261Sjtc 	}
27767ead261Sjtc 
27867ead261Sjtc /*
27967ead261Sjtc  *	pull apart system and device, and optional user
28067ead261Sjtc  *	don't munge original string
28167ead261Sjtc  *	if COMPAT is defined, also handle old (4.2) style person.site notation.
28267ead261Sjtc  */
28367ead261Sjtc 
28467ead261Sjtc 	while (*path != '@'
28567ead261Sjtc #ifdef COMPAT
28667ead261Sjtc 			&& *path != '.'
28767ead261Sjtc #endif
28867ead261Sjtc 			&& *path != ':') {
28967ead261Sjtc 		*sys++ = *path++;
29067ead261Sjtc 	}
29167ead261Sjtc 	*sys = '\0';
29267ead261Sjtc 	path++;
29367ead261Sjtc 
29432dd941fSlukem 	if (*(path - 1) == '@') {
295d3574aa7Schristos 		(void)strlcpy(user, host, sizeof(login));
2969cd5492cSmrg 				/* saw user part of user@host */
2976a973ed8Slukem 		sys = host;			/* start over */
29867ead261Sjtc 		while (*path != ':') {
29967ead261Sjtc 			*sys++ = *path++;
30067ead261Sjtc 		}
30167ead261Sjtc 		*sys = '\0';
30267ead261Sjtc 		path++;
30367ead261Sjtc 	}
30467ead261Sjtc #ifdef COMPAT
30532dd941fSlukem 	else if (*(path - 1) == '.') {
30667ead261Sjtc 		while (*path != ':') {
30767ead261Sjtc 			*user++ = *path++;
30867ead261Sjtc 		}
30967ead261Sjtc 		*user = '\0';
31067ead261Sjtc 		path++;
31167ead261Sjtc 	}
31267ead261Sjtc #endif
31367ead261Sjtc 	else
31467ead261Sjtc 		*user = '\0';
31567ead261Sjtc 
31667ead261Sjtc 	while (*path) {
31767ead261Sjtc 		*dev++ = *path++;
31867ead261Sjtc 	}
31967ead261Sjtc 	*dev = '\0';
32067ead261Sjtc 
32167ead261Sjtc #ifdef USE_REXEC
32267ead261Sjtc /*
32367ead261Sjtc  *	Execute the remote command using rexec
32467ead261Sjtc  */
3256a973ed8Slukem 	READ(i) = WRITE(i) = _rmt_rexec(host, login);
32667ead261Sjtc 	if (READ(i) < 0)
32788b197ecSchristos 		return -1;
32867ead261Sjtc #else
32967ead261Sjtc /*
33067ead261Sjtc  *	setup the pipes for the 'rsh' command and fork
33167ead261Sjtc  */
33267ead261Sjtc 
33367ead261Sjtc 	if (pipe(Ptc[i]) == -1 || pipe(Ctp[i]) == -1)
33488b197ecSchristos 		return -1;
33567ead261Sjtc 
33688b197ecSchristos 	switch (fork()) {
33788b197ecSchristos 	case -1:
33888b197ecSchristos 		return -1;
33967ead261Sjtc 
34088b197ecSchristos 	case 0:
34167ead261Sjtc 		close(0);
34267ead261Sjtc 		dup(Ptc[i][0]);
34367ead261Sjtc 		close(Ptc[i][0]); close(Ptc[i][1]);
34467ead261Sjtc 		close(1);
34567ead261Sjtc 		dup(Ctp[i][1]);
34667ead261Sjtc 		close(Ctp[i][0]); close(Ctp[i][1]);
34767ead261Sjtc 		(void) setuid(getuid());
34867ead261Sjtc 		(void) setgid(getgid());
3495f2732abSmrg 
3505f2732abSmrg 		if ((rshpath = getenv("RCMD_CMD")) == NULL)
3515f2732abSmrg 			rshpath = _PATH_RSH;
3525f2732abSmrg 		if ((rsh = strrchr(rshpath, '/')) == NULL)
3535f2732abSmrg 			rsh = rshpath;
3545f2732abSmrg 		else
3555f2732abSmrg 			rsh++;
3565f2732abSmrg 
35732dd941fSlukem 		if (*login) {
35888b197ecSchristos 			execl(rshpath, rsh, host, "-l", login, _PATH_RMT, NULL);
35932dd941fSlukem 		} else {
36088b197ecSchristos 			execl(rshpath, rsh, host, _PATH_RMT, NULL);
36167ead261Sjtc 		}
36267ead261Sjtc 
36367ead261Sjtc /*
36467ead261Sjtc  *	bad problems if we get here
36567ead261Sjtc  */
36667ead261Sjtc 
367*4a3e2a82Sandvar 		err(1, "Cannot exec %s", rshpath);
36888b197ecSchristos 		/*FALLTHROUGH*/
36988b197ecSchristos 	default:
37088b197ecSchristos 		break;
37167ead261Sjtc 	}
37267ead261Sjtc 
37367ead261Sjtc 	close(Ptc[i][0]); close(Ctp[i][1]);
37467ead261Sjtc #endif
37567ead261Sjtc 
37667ead261Sjtc /*
37767ead261Sjtc  *	now attempt to open the tape device
37867ead261Sjtc  */
37967ead261Sjtc 
3809cd5492cSmrg 	(void)snprintf(buffer, sizeof(buffer), "O%s\n%d\n", device, oflag);
38167ead261Sjtc 	if (command(i, buffer) == -1 || status(i) == -1)
38288b197ecSchristos 		return -1;
38367ead261Sjtc 
38488b197ecSchristos 	return i;
38567ead261Sjtc }
38667ead261Sjtc 
38767ead261Sjtc 
38867ead261Sjtc /*
38967ead261Sjtc  *	_rmt_close --- close a remote magtape unit and shut down
39067ead261Sjtc  */
391ae6b9c67Slukem static int
_rmt_close(int fildes)39232dd941fSlukem _rmt_close(int fildes)
39367ead261Sjtc {
39467ead261Sjtc 	int rc;
39567ead261Sjtc 
39632dd941fSlukem 	if (command(fildes, "C\n") != -1) {
39767ead261Sjtc 		rc = status(fildes);
39867ead261Sjtc 
3998ee4077eSjtc 		rmtabort(fildes);
40088b197ecSchristos 		return rc;
40167ead261Sjtc 	}
40267ead261Sjtc 
40388b197ecSchristos 	return -1;
40467ead261Sjtc }
40567ead261Sjtc 
40667ead261Sjtc 
40767ead261Sjtc /*
40867ead261Sjtc  *	_rmt_read --- read a buffer from a remote tape
40967ead261Sjtc  */
41032dd941fSlukem static ssize_t
_rmt_read(int fildes,void * buf,size_t nbyte)41132dd941fSlukem _rmt_read(int fildes, void *buf, size_t nbyte)
41267ead261Sjtc {
41344fad74bSchristos 	size_t rc;
41444fad74bSchristos 	int rv;
41544fad74bSchristos 	ssize_t nread;
41632dd941fSlukem 	char *p;
41767ead261Sjtc 	char buffer[BUFMAGIC];
41867ead261Sjtc 
419b48252f3Slukem 	_DIAGASSERT(buf != NULL);
420b48252f3Slukem 
42188b197ecSchristos 	(void)snprintf(buffer, sizeof buffer, "R%zu\n", nbyte);
42244fad74bSchristos 	if (command(fildes, buffer) == -1 || (rv = status(fildes)) == -1)
42388b197ecSchristos 		return -1;
42467ead261Sjtc 
425e1a2f47fSmatt 	if (rv > (int)nbyte)
42688b197ecSchristos 		rv = (int)nbyte;
42744fad74bSchristos 
42844fad74bSchristos 	for (rc = rv, p = buf; rc > 0; rc -= nread, p += nread) {
42944fad74bSchristos 		if ((nread = read(READ(fildes), p, rc)) <= 0) {
4308ee4077eSjtc 			rmtabort(fildes);
43167ead261Sjtc 			errno = EIO;
43288b197ecSchristos 			return -1;
43367ead261Sjtc 		}
43467ead261Sjtc 	}
43567ead261Sjtc 
43644fad74bSchristos 	return rv;
43767ead261Sjtc }
43867ead261Sjtc 
43967ead261Sjtc 
44067ead261Sjtc /*
44167ead261Sjtc  *	_rmt_write --- write a buffer to the remote tape
44267ead261Sjtc  */
44332dd941fSlukem static ssize_t
_rmt_write(int fildes,const void * buf,size_t nbyte)44432dd941fSlukem _rmt_write(int fildes, const void *buf, size_t nbyte)
44567ead261Sjtc {
44667ead261Sjtc 	char buffer[BUFMAGIC];
44788b197ecSchristos 	sig_t pstat;
44867ead261Sjtc 
449b48252f3Slukem 	_DIAGASSERT(buf != NULL);
450b48252f3Slukem 
45188b197ecSchristos 	(void)snprintf(buffer, sizeof buffer, "W%zu\n", nbyte);
45267ead261Sjtc 	if (command(fildes, buffer) == -1)
45388b197ecSchristos 		return -1;
45467ead261Sjtc 
45567ead261Sjtc 	pstat = signal(SIGPIPE, SIG_IGN);
456e1a2f47fSmatt 	if ((size_t)write(WRITE(fildes), buf, nbyte) == nbyte) {
45767ead261Sjtc 		signal(SIGPIPE, pstat);
45888b197ecSchristos 		return status(fildes);
45967ead261Sjtc 	}
46067ead261Sjtc 
46167ead261Sjtc 	signal(SIGPIPE, pstat);
4628ee4077eSjtc 	rmtabort(fildes);
46367ead261Sjtc 	errno = EIO;
46488b197ecSchristos 	return -1;
46567ead261Sjtc }
46667ead261Sjtc 
46767ead261Sjtc 
46867ead261Sjtc /*
46967ead261Sjtc  *	_rmt_lseek --- perform an imitation lseek operation remotely
47067ead261Sjtc  */
471ae6b9c67Slukem static off_t
_rmt_lseek(int fildes,off_t offset,int whence)47232dd941fSlukem _rmt_lseek(int fildes, off_t offset, int whence)
47367ead261Sjtc {
47467ead261Sjtc 	char buffer[BUFMAGIC];
47567ead261Sjtc 
47644fad74bSchristos 	/*LONGLONG*/
47732dd941fSlukem 	(void)snprintf(buffer, sizeof buffer, "L%lld\n%d\n", (long long)offset,
478395c20eaSmrg 	    whence);
47967ead261Sjtc 	if (command(fildes, buffer) == -1)
48088b197ecSchristos 		return -1;
48167ead261Sjtc 
48288b197ecSchristos 	return status(fildes);
48367ead261Sjtc }
48467ead261Sjtc 
48567ead261Sjtc 
48667ead261Sjtc /*
48767ead261Sjtc  *	_rmt_ioctl --- perform raw tape operations remotely
48867ead261Sjtc  */
48967ead261Sjtc #ifdef RMTIOCTL
490ae6b9c67Slukem static int
_rmt_ioctl(int fildes,unsigned long op,void * arg)49132dd941fSlukem _rmt_ioctl(int fildes, unsigned long op, void *arg)
49267ead261Sjtc {
49367ead261Sjtc 	char c;
49444fad74bSchristos 	int rv;
49544fad74bSchristos 	size_t rc;
49644fad74bSchristos 	ssize_t cnt;
49732dd941fSlukem 	char buffer[BUFMAGIC], *p;
49888b197ecSchristos 	struct mtop *mtop = arg;
49967ead261Sjtc 
500b48252f3Slukem 	_DIAGASSERT(arg != NULL);
501b48252f3Slukem 
50267ead261Sjtc /*
503a0403cdeSmsaitoh  *	MTIOCOP is the easy one. nothing is transferred in binary
50467ead261Sjtc  */
50567ead261Sjtc 
50632dd941fSlukem 	if (op == MTIOCTOP) {
5079cd5492cSmrg 		(void)snprintf(buffer, sizeof buffer, "I%d\n%d\n",
50888b197ecSchristos 		    mtop->mt_op, mtop->mt_count);
50967ead261Sjtc 		if (command(fildes, buffer) == -1)
51088b197ecSchristos 			return -1;
51188b197ecSchristos 		return status(fildes);
51267ead261Sjtc 	}
51367ead261Sjtc 
51467ead261Sjtc /*
51567ead261Sjtc  *	we can only handle 2 ops, if not the other one, punt
51667ead261Sjtc  */
51767ead261Sjtc 
51832dd941fSlukem 	if (op != MTIOCGET) {
51967ead261Sjtc 		errno = EINVAL;
52088b197ecSchristos 		return -1;
52167ead261Sjtc 	}
52267ead261Sjtc 
52367ead261Sjtc /*
52467ead261Sjtc  *	grab the status and read it directly into the structure
52567ead261Sjtc  *	this assumes that the status buffer is (hopefully) not
52667ead261Sjtc  *	padded and that 2 shorts fit in a long without any word
52767ead261Sjtc  *	alignment problems, ie - the whole struct is contiguous
52867ead261Sjtc  *	NOTE - this is probably NOT a good assumption.
52967ead261Sjtc  */
53067ead261Sjtc 
53144fad74bSchristos 	if (command(fildes, "S") == -1 || (rv = status(fildes)) == -1)
53288b197ecSchristos 		return -1;
53367ead261Sjtc 
53488b197ecSchristos 	memset(arg, 0, sizeof(struct mtget));
53544fad74bSchristos 	for (rc = rv, p = arg; rc > 0; rc -= cnt, p += cnt) {
53644fad74bSchristos 		if ((cnt = read(READ(fildes), p, rc)) <= 0) {
5378ee4077eSjtc 			rmtabort(fildes);
53867ead261Sjtc 			errno = EIO;
53988b197ecSchristos 			return -1;
54067ead261Sjtc 		}
54167ead261Sjtc 	}
54267ead261Sjtc 
54367ead261Sjtc /*
54467ead261Sjtc  *	now we check for byte position. mt_type is a small integer field
54567ead261Sjtc  *	(normally) so we will check its magnitude. if it is larger than
54667ead261Sjtc  *	256, we will assume that the bytes are swapped and go through
54767ead261Sjtc  *	and reverse all the bytes
54867ead261Sjtc  */
54967ead261Sjtc 
55044fad74bSchristos 	if (((struct mtget *)(void *)p)->mt_type < 256)
55188b197ecSchristos 		return 0;
55267ead261Sjtc 
5531acc9100Schristos 	for (cnt = 0; cnt < rv; cnt += 2) {
55432dd941fSlukem 		c = p[cnt];
55532dd941fSlukem 		p[cnt] = p[cnt + 1];
55632dd941fSlukem 		p[cnt + 1] = c;
55767ead261Sjtc 	}
55867ead261Sjtc 
55988b197ecSchristos 	return 0;
56067ead261Sjtc }
56167ead261Sjtc #endif /* RMTIOCTL */
56267ead261Sjtc 
56332dd941fSlukem 
56467ead261Sjtc /*
56567ead261Sjtc  *	Added routines to replace open(), close(), lseek(), ioctl(), etc.
56667ead261Sjtc  *	The preprocessor can be used to remap these the rmtopen(), etc
56767ead261Sjtc  *	thus minimizing source changes:
56867ead261Sjtc  *
56967ead261Sjtc  *		#ifdef <something>
57067ead261Sjtc  *		#  define access rmtaccess
57167ead261Sjtc  *		#  define close rmtclose
57267ead261Sjtc  *		#  define creat rmtcreat
57367ead261Sjtc  *		#  define dup rmtdup
57467ead261Sjtc  *		#  define fcntl rmtfcntl
57567ead261Sjtc  *		#  define fstat rmtfstat
57667ead261Sjtc  *		#  define ioctl rmtioctl
57767ead261Sjtc  *		#  define isatty rmtisatty
57867ead261Sjtc  *		#  define lseek rmtlseek
57967ead261Sjtc  *		#  define lstat rmtlstat
58067ead261Sjtc  *		#  define open rmtopen
58167ead261Sjtc  *		#  define read rmtread
58267ead261Sjtc  *		#  define stat rmtstat
58367ead261Sjtc  *		#  define write rmtwrite
58467ead261Sjtc  *		#endif
58567ead261Sjtc  *
58667ead261Sjtc  *	-- Fred Fish
58767ead261Sjtc  *
58867ead261Sjtc  *	ADR --- I set up a <rmt.h> include file for this
58967ead261Sjtc  *
59067ead261Sjtc  */
59167ead261Sjtc 
59267ead261Sjtc /*
59367ead261Sjtc  *	Note that local vs remote file descriptors are distinquished
59467ead261Sjtc  *	by adding a bias to the remote descriptors.  This is a quick
59567ead261Sjtc  *	and dirty trick that may not be portable to some systems.
59667ead261Sjtc  */
59767ead261Sjtc 
59867ead261Sjtc #define REM_BIAS 128
59967ead261Sjtc 
60067ead261Sjtc 
60167ead261Sjtc /*
60267ead261Sjtc  *	Test pathname to see if it is local or remote.  A remote device
60367ead261Sjtc  *	is any string that contains ":/dev/".  Returns 1 if remote,
60467ead261Sjtc  *	0 otherwise.
60567ead261Sjtc  */
60667ead261Sjtc 
607ae6b9c67Slukem static int
remdev(const char * path)60832dd941fSlukem remdev(const char *path)
60967ead261Sjtc {
610b48252f3Slukem 
611b48252f3Slukem 	_DIAGASSERT(path != NULL);
612b48252f3Slukem 
61332dd941fSlukem 	if ((path = strchr(path, ':')) != NULL) {
61432dd941fSlukem 		if (strncmp(path + 1, "/dev/", 5) == 0) {
61588b197ecSchristos 			return 1;
61667ead261Sjtc 		}
61767ead261Sjtc 	}
61888b197ecSchristos 	return 0;
61967ead261Sjtc }
62067ead261Sjtc 
62167ead261Sjtc 
62267ead261Sjtc /*
62367ead261Sjtc  *	Open a local or remote file.  Looks just like open(2) to
62467ead261Sjtc  *	caller.
62567ead261Sjtc  */
626ae6b9c67Slukem int
rmtopen(const char * path,int oflag,...)6276e190eedSthorpej rmtopen(const char *path, int oflag, ...)
6286e190eedSthorpej {
6296e190eedSthorpej 	mode_t mode;
6306e190eedSthorpej 	int fd;
6316e190eedSthorpej 	va_list ap;
6326e190eedSthorpej 	va_start(ap, oflag);
6336e190eedSthorpej 
6346e190eedSthorpej 	mode = va_arg(ap, mode_t);
6356e190eedSthorpej 	va_end(ap);
63667ead261Sjtc 
637b48252f3Slukem 	_DIAGASSERT(path != NULL);
638b48252f3Slukem 
63932dd941fSlukem 	if (remdev(path)) {
64044fad74bSchristos 		fd = _rmt_open(path, oflag, (int)mode);
64167ead261Sjtc 
64288b197ecSchristos 		return (fd == -1) ? -1 : (fd + REM_BIAS);
64332dd941fSlukem 	} else {
64488b197ecSchristos 		return open(path, oflag, mode);
64567ead261Sjtc 	}
64667ead261Sjtc }
64767ead261Sjtc 
64867ead261Sjtc /*
64967ead261Sjtc  *	Test pathname for specified access.  Looks just like access(2)
65067ead261Sjtc  *	to caller.
65167ead261Sjtc  */
65267ead261Sjtc 
653ae6b9c67Slukem int
rmtaccess(const char * path,int amode)65432dd941fSlukem rmtaccess(const char *path, int amode)
65567ead261Sjtc {
656b48252f3Slukem 
657b48252f3Slukem 	_DIAGASSERT(path != NULL);
658b48252f3Slukem 
65932dd941fSlukem 	if (remdev(path)) {
66088b197ecSchristos 		return 0;		/* Let /etc/rmt find out */
66132dd941fSlukem 	} else {
66288b197ecSchristos 		return access(path, amode);
66367ead261Sjtc 	}
66467ead261Sjtc }
66567ead261Sjtc 
66667ead261Sjtc 
66767ead261Sjtc /*
668e6077a76Smikel  *	Isrmt. Let a programmer know he has a remote device.
669e6077a76Smikel  */
670ae6b9c67Slukem int
isrmt(int fd)67132dd941fSlukem isrmt(int fd)
672e6077a76Smikel {
673f836d171Spooka 	int unbias = fd - REM_BIAS;
67432dd941fSlukem 
675f836d171Spooka 	return (fd >= REM_BIAS) && unbias < MAXUNIT &&
676f836d171Spooka 	    (WRITE(unbias) != -1 || READ(unbias) != -1);
677e6077a76Smikel }
678e6077a76Smikel 
679e6077a76Smikel 
680e6077a76Smikel /*
68167ead261Sjtc  *	Read from stream.  Looks just like read(2) to caller.
68267ead261Sjtc  */
683ae6b9c67Slukem ssize_t
rmtread(int fildes,void * buf,size_t nbyte)68432dd941fSlukem rmtread(int fildes, void *buf, size_t nbyte)
68567ead261Sjtc {
686b48252f3Slukem 
687b48252f3Slukem 	_DIAGASSERT(buf != NULL);
688b48252f3Slukem 
68932dd941fSlukem 	if (isrmt(fildes)) {
69088b197ecSchristos 		return _rmt_read(fildes - REM_BIAS, buf, nbyte);
69132dd941fSlukem 	} else {
69288b197ecSchristos 		return read(fildes, buf, nbyte);
69367ead261Sjtc 	}
69467ead261Sjtc }
69567ead261Sjtc 
69667ead261Sjtc 
69767ead261Sjtc /*
69867ead261Sjtc  *	Write to stream.  Looks just like write(2) to caller.
69967ead261Sjtc  */
700ae6b9c67Slukem ssize_t
rmtwrite(int fildes,const void * buf,size_t nbyte)70132dd941fSlukem rmtwrite(int fildes, const void *buf, size_t nbyte)
70267ead261Sjtc {
703b48252f3Slukem 
704b48252f3Slukem 	_DIAGASSERT(buf != NULL);
705b48252f3Slukem 
70632dd941fSlukem 	if (isrmt(fildes)) {
70788b197ecSchristos 		return _rmt_write(fildes - REM_BIAS, buf, nbyte);
70832dd941fSlukem 	} else {
70988b197ecSchristos 		return write(fildes, buf, nbyte);
71067ead261Sjtc 	}
71167ead261Sjtc }
71267ead261Sjtc 
71367ead261Sjtc /*
71467ead261Sjtc  *	Perform lseek on file.  Looks just like lseek(2) to caller.
71567ead261Sjtc  */
716ae6b9c67Slukem off_t
rmtlseek(int fildes,off_t offset,int whence)71732dd941fSlukem rmtlseek(int fildes, off_t offset, int whence)
71867ead261Sjtc {
71943ca618dSenami 
72032dd941fSlukem 	if (isrmt(fildes)) {
72188b197ecSchristos 		return _rmt_lseek(fildes - REM_BIAS, offset, whence);
72232dd941fSlukem 	} else {
72388b197ecSchristos 		return lseek(fildes, offset, whence);
72467ead261Sjtc 	}
72567ead261Sjtc }
72667ead261Sjtc 
72767ead261Sjtc 
72867ead261Sjtc /*
72967ead261Sjtc  *	Close a file.  Looks just like close(2) to caller.
73067ead261Sjtc  */
731ae6b9c67Slukem int
rmtclose(int fildes)73232dd941fSlukem rmtclose(int fildes)
73367ead261Sjtc {
73443ca618dSenami 
73532dd941fSlukem 	if (isrmt(fildes)) {
73688b197ecSchristos 		return _rmt_close(fildes - REM_BIAS);
73732dd941fSlukem 	} else {
73888b197ecSchristos 		return close(fildes);
73967ead261Sjtc 	}
74067ead261Sjtc }
74167ead261Sjtc 
74232dd941fSlukem 
74367ead261Sjtc /*
74467ead261Sjtc  *	Do ioctl on file.  Looks just like ioctl(2) to caller.
74567ead261Sjtc  */
746ae6b9c67Slukem int
rmtioctl(int fildes,unsigned long request,...)7476e190eedSthorpej rmtioctl(int fildes, unsigned long request, ...)
7486e190eedSthorpej {
74988b197ecSchristos 	void *arg;
7506e190eedSthorpej 	va_list ap;
7516e190eedSthorpej 	va_start(ap, request);
7526e190eedSthorpej 
75388b197ecSchristos 	arg = va_arg(ap, void *);
7546e190eedSthorpej 	va_end(ap);
7556e190eedSthorpej 
756b48252f3Slukem 	/* XXX: arg may be NULL ? */
757b48252f3Slukem 
75832dd941fSlukem 	if (isrmt(fildes)) {
75967ead261Sjtc #ifdef RMTIOCTL
76088b197ecSchristos 		return _rmt_ioctl(fildes - REM_BIAS, request, arg);
76167ead261Sjtc #else
76267ead261Sjtc 		errno = EOPNOTSUPP;
76388b197ecSchristos 		return -1;		/* For now (fnf) */
76467ead261Sjtc #endif
76532dd941fSlukem 	} else {
76688b197ecSchristos 		return ioctl(fildes, request, arg);
76767ead261Sjtc 	}
76867ead261Sjtc }
76967ead261Sjtc 
77067ead261Sjtc 
77167ead261Sjtc /*
77267ead261Sjtc  *	Duplicate an open file descriptor.  Looks just like dup(2)
77367ead261Sjtc  *	to caller.
77467ead261Sjtc  */
775ae6b9c67Slukem int
rmtdup(int fildes)77632dd941fSlukem rmtdup(int fildes)
77767ead261Sjtc {
77843ca618dSenami 
77932dd941fSlukem 	if (isrmt(fildes)) {
78067ead261Sjtc 		errno = EOPNOTSUPP;
78188b197ecSchristos 		return -1;		/* For now (fnf) */
78232dd941fSlukem 	} else {
78388b197ecSchristos 		return dup(fildes);
78467ead261Sjtc 	}
78567ead261Sjtc }
78667ead261Sjtc 
78732dd941fSlukem 
78867ead261Sjtc /*
78967ead261Sjtc  *	Get file status.  Looks just like fstat(2) to caller.
79067ead261Sjtc  */
791ae6b9c67Slukem int
rmtfstat(int fildes,struct stat * buf)79232dd941fSlukem rmtfstat(int fildes, struct stat *buf)
79367ead261Sjtc {
794b48252f3Slukem 
795b48252f3Slukem 	_DIAGASSERT(buf != NULL);
796b48252f3Slukem 
79732dd941fSlukem 	if (isrmt(fildes)) {
79867ead261Sjtc 		errno = EOPNOTSUPP;
79988b197ecSchristos 		return -1;		/* For now (fnf) */
80032dd941fSlukem 	} else {
80188b197ecSchristos 		return fstat(fildes, buf);
80267ead261Sjtc 	}
80367ead261Sjtc }
80467ead261Sjtc 
80567ead261Sjtc 
80667ead261Sjtc /*
80767ead261Sjtc  *	Get file status.  Looks just like stat(2) to caller.
80867ead261Sjtc  */
809ae6b9c67Slukem int
rmtstat(const char * path,struct stat * buf)81032dd941fSlukem rmtstat(const char *path, struct stat *buf)
81167ead261Sjtc {
812b48252f3Slukem 
813b48252f3Slukem 	_DIAGASSERT(path != NULL);
814b48252f3Slukem 	_DIAGASSERT(buf != NULL);
815b48252f3Slukem 
81632dd941fSlukem 	if (remdev(path)) {
81767ead261Sjtc 		errno = EOPNOTSUPP;
81888b197ecSchristos 		return -1;		/* For now (fnf) */
81932dd941fSlukem 	} else {
82088b197ecSchristos 		return stat(path, buf);
82167ead261Sjtc 	}
82267ead261Sjtc }
82367ead261Sjtc 
82467ead261Sjtc 
82567ead261Sjtc /*
82667ead261Sjtc  *	Create a file from scratch.  Looks just like creat(2) to the caller.
82767ead261Sjtc  */
828ae6b9c67Slukem int
rmtcreat(const char * path,mode_t mode)82932dd941fSlukem rmtcreat(const char *path, mode_t mode)
83067ead261Sjtc {
831b48252f3Slukem 
832b48252f3Slukem 	_DIAGASSERT(path != NULL);
833b48252f3Slukem 
83432dd941fSlukem 	if (remdev(path)) {
83588b197ecSchristos 		return rmtopen(path, O_WRONLY | O_CREAT, mode);
83632dd941fSlukem 	} else {
83788b197ecSchristos 		return open(path, O_CREAT | O_TRUNC | O_WRONLY, mode);
83867ead261Sjtc 	}
83967ead261Sjtc }
84067ead261Sjtc 
84132dd941fSlukem 
84267ead261Sjtc /*
84367ead261Sjtc  *	Rmtfcntl. Do a remote fcntl operation.
84467ead261Sjtc  */
845ae6b9c67Slukem int
rmtfcntl(int fd,int cmd,...)8466e190eedSthorpej rmtfcntl(int fd, int cmd, ...)
84767ead261Sjtc {
8486e190eedSthorpej 	void *arg;
8496e190eedSthorpej 	va_list ap;
8506e190eedSthorpej 	va_start(ap, cmd);
8516e190eedSthorpej 
8526e190eedSthorpej 	arg = va_arg(ap, void *);
8536e190eedSthorpej 	va_end(ap);
8546e190eedSthorpej 
855b48252f3Slukem 	/* XXX: arg may be NULL ? */
856b48252f3Slukem 
85732dd941fSlukem 	if (isrmt(fd)) {
85867ead261Sjtc 		errno = EOPNOTSUPP;
85988b197ecSchristos 		return -1;
86032dd941fSlukem 	} else {
86188b197ecSchristos 		return fcntl(fd, cmd, arg);
86267ead261Sjtc 	}
86367ead261Sjtc }
86467ead261Sjtc 
86532dd941fSlukem 
86667ead261Sjtc /*
86767ead261Sjtc  *	Rmtisatty.  Do the isatty function.
86867ead261Sjtc  */
869ae6b9c67Slukem int
rmtisatty(int fd)87032dd941fSlukem rmtisatty(int fd)
87167ead261Sjtc {
87232dd941fSlukem 
87367ead261Sjtc 	if (isrmt(fd))
87488b197ecSchristos 		return 0;
87567ead261Sjtc 	else
87688b197ecSchristos 		return isatty(fd);
87767ead261Sjtc }
87867ead261Sjtc 
87967ead261Sjtc 
88067ead261Sjtc /*
88167ead261Sjtc  *	Get file status, even if symlink.  Looks just like lstat(2) to caller.
88267ead261Sjtc  */
883ae6b9c67Slukem int
rmtlstat(const char * path,struct stat * buf)88432dd941fSlukem rmtlstat(const char *path, struct stat *buf)
88567ead261Sjtc {
886b48252f3Slukem 
887b48252f3Slukem 	_DIAGASSERT(path != NULL);
888b48252f3Slukem 	_DIAGASSERT(buf != NULL);
889b48252f3Slukem 
89032dd941fSlukem 	if (remdev(path)) {
89167ead261Sjtc 		errno = EOPNOTSUPP;
89288b197ecSchristos 		return -1;		/* For now (fnf) */
89332dd941fSlukem 	} else {
89488b197ecSchristos 		return lstat(path, buf);
89567ead261Sjtc 	}
89667ead261Sjtc }
897