diff --git a/lib/librmt/Makefile b/lib/librmt/Makefile new file mode 100644 index 000000000000..084b8ab31389 --- /dev/null +++ b/lib/librmt/Makefile @@ -0,0 +1,8 @@ +LIB= rmt +SRCS= rmtlib.c +MAN= rmtops.3 + +NOPROFILE= +NOPIC= + +.include diff --git a/lib/librmt/README b/lib/librmt/README new file mode 100644 index 000000000000..71b30724758d --- /dev/null +++ b/lib/librmt/README @@ -0,0 +1,18 @@ +README + +This is the remote mag tape library. It allows a program that uses +Unix system calls to transparently use a file (usually a tape drive) on +another system via /etc/rmt, simply by including . It is +particularly useful with tar and dd, and is supplied with GNU tar. + +This package has evolved somewhat over the years. My thanks to the +people who did most of the original work, and those who've contributed +bug fixes; appropriate credit is in the man page and source files. + +Enjoy, + +Arnold Robbins +Emory U. Computing Center +arnold@emoryu1.cc.emory.edu +gatech!emoryu1!arnold ++1 404 727 7636 diff --git a/lib/librmt/rmtlib.c b/lib/librmt/rmtlib.c new file mode 100644 index 000000000000..204c0971cc8b --- /dev/null +++ b/lib/librmt/rmtlib.c @@ -0,0 +1,932 @@ +#ifndef lint +static char *RCSid = "$Header: /cvsroot/src/lib/librmt/rmtlib.c,v 1.1 1996/08/09 03:35:19 jtc Exp $"; +#endif + +/* + * $Log: rmtlib.c,v $ + * Revision 1.1 1996/08/09 03:35:19 jtc + * Remote mag tape library from volume 18 of comp.sources.unix. + * + * Revision 1.7 89/03/23 14:09:51 root + * Fix from haynes@ucscc.ucsc.edu for use w/compat. ADR. + * + * Revision 1.6 88/10/25 17:04:29 root + * rexec code and a bug fix from srs!dan, miscellanious cleanup. ADR. + * + * Revision 1.5 88/10/25 16:30:17 root + * Fix from jeff@gatech.edu for getting user@host:dev right. ADR. + * + * Revision 1.4 87/10/30 10:36:12 root + * Made 4.2 syntax a compile time option. ADR. + * + * Revision 1.3 87/04/22 11:16:48 root + * Two fixes from parmelee@wayback.cs.cornell.edu to correctly + * do fd biasing and rmt protocol on 'S' command. ADR. + * + * Revision 1.2 86/10/09 16:38:53 root + * Changed to reflect 4.3BSD rcp syntax. ADR. + * + * Revision 1.1 86/10/09 16:17:35 root + * Initial revision + * + */ + +/* + * rmt --- remote tape emulator subroutines + * + * Originally written by Jeff Lee, modified some by Arnold Robbins + * + * WARNING: The man page rmt(8) for /etc/rmt documents the remote mag + * tape protocol which rdump and rrestore use. Unfortunately, the man + * page is *WRONG*. The author of the routines I'm including originally + * wrote his code just based on the man page, and it didn't work, so he + * went to the rdump source to figure out why. The only thing he had to + * change was to check for the 'F' return code in addition to the 'E', + * and to separate the various arguments with \n instead of a space. I + * personally don't think that this is much of a problem, but I wanted to + * point it out. + * -- Arnold Robbins + * + * Redone as a library that can replace open, read, write, etc, by + * Fred Fish, with some additional work by Arnold Robbins. + */ + +/* + * MAXUNIT --- Maximum number of remote tape file units + * + * READ --- Return the number of the read side file descriptor + * WRITE --- Return the number of the write side file descriptor + */ + +#define RMTIOCTL 1 +/* #define USE_REXEC 1 /* rexec code courtesy of Dan Kegel, srs!dan */ + +#include +#include +#include + +#ifdef RMTIOCTL +#include +#include +#endif + +#ifdef USE_REXEC +#include +#endif + +#include +#include +#include + +#define BUFMAGIC 64 /* a magic number for buffer sizes */ +#define MAXUNIT 4 + +#define READ(fd) (Ctp[fd][0]) +#define WRITE(fd) (Ptc[fd][1]) + +static int Ctp[MAXUNIT][2] = { -1, -1, -1, -1, -1, -1, -1, -1 }; +static int Ptc[MAXUNIT][2] = { -1, -1, -1, -1, -1, -1, -1, -1 }; + +static jmp_buf Jmpbuf; +extern int errno; + +/* + * abort --- close off a remote tape connection + */ + +static void abort(fildes) +int fildes; +{ + close(READ(fildes)); + close(WRITE(fildes)); + READ(fildes) = -1; + WRITE(fildes) = -1; +} + + + +/* + * command --- attempt to perform a remote tape command + */ + +static int command(fildes, buf) +int fildes; +char *buf; +{ + register int blen; + int (*pstat)(); + +/* + * save current pipe status and try to make the request + */ + + blen = strlen(buf); + pstat = signal(SIGPIPE, SIG_IGN); + if (write(WRITE(fildes), buf, blen) == blen) + { + signal(SIGPIPE, pstat); + return(0); + } + +/* + * something went wrong. close down and go home + */ + + signal(SIGPIPE, pstat); + abort(fildes); + + errno = EIO; + return(-1); +} + + + +/* + * status --- retrieve the status from the pipe + */ + +static int status(fildes) +int fildes; +{ + int i; + char c, *cp; + char buffer[BUFMAGIC]; + +/* + * read the reply command line + */ + + for (i = 0, cp = buffer; i < BUFMAGIC; i++, cp++) + { + if (read(READ(fildes), cp, 1) != 1) + { + abort(fildes); + errno = EIO; + return(-1); + } + if (*cp == '\n') + { + *cp = 0; + break; + } + } + + if (i == BUFMAGIC) + { + abort(fildes); + errno = EIO; + return(-1); + } + +/* + * check the return status + */ + + for (cp = buffer; *cp; cp++) + if (*cp != ' ') + break; + + if (*cp == 'E' || *cp == 'F') + { + errno = atoi(cp + 1); + while (read(READ(fildes), &c, 1) == 1) + if (c == '\n') + break; + + if (*cp == 'F') + abort(fildes); + + return(-1); + } + +/* + * check for mis-synced pipes + */ + + if (*cp != 'A') + { + abort(fildes); + errno = EIO; + return(-1); + } + + return(atoi(cp + 1)); +} + +#ifdef USE_REXEC + +/* + * _rmt_rexec + * + * execute /etc/rmt on a remote system using rexec(). + * Return file descriptor of bidirectional socket for stdin and stdout + * If username is NULL, or an empty string, uses current username. + * + * ADR: By default, this code is not used, since it requires that + * the user have a .netrc file in his/her home directory, or that the + * application designer be willing to have rexec prompt for login and + * password info. This may be unacceptable, and .rhosts files for use + * with rsh are much more common on BSD systems. + */ + +static int +_rmt_rexec(host, user) +char *host; +char *user; /* may be NULL */ +{ + struct servent *rexecserv; + + rexecserv = getservbyname("exec", "tcp"); + if (NULL == rexecserv) { + fprintf (stderr, "? exec/tcp: service not available."); + exit (-1); + } + if ((user != NULL) && *user == '\0') + user = (char *) NULL; + return rexec (&host, rexecserv->s_port, user, NULL, + "/etc/rmt", (int *)NULL); +} +#endif /* USE_REXEC */ + +/* + * _rmt_open --- open a magtape device on system specified, as given user + * + * file name has the form [user@]system:/dev/???? +#ifdef COMPAT + * file name has the form system[.user]:/dev/???? +#endif + */ + +#define MAXHOSTLEN 257 /* BSD allows very long host names... */ + +static int _rmt_open (path, oflag, mode) +char *path; +int oflag; +int mode; +{ + int i, rc; + char buffer[BUFMAGIC]; + char system[MAXHOSTLEN]; + char device[BUFMAGIC]; + char login[BUFMAGIC]; + char *sys, *dev, *user; + + sys = system; + dev = device; + user = login; + +/* + * first, find an open pair of file descriptors + */ + + for (i = 0; i < MAXUNIT; i++) + if (READ(i) == -1 && WRITE(i) == -1) + break; + + if (i == MAXUNIT) + { + errno = EMFILE; + return(-1); + } + +/* + * pull apart system and device, and optional user + * don't munge original string + * if COMPAT is defined, also handle old (4.2) style person.site notation. + */ + + while (*path != '@' +#ifdef COMPAT + && *path != '.' +#endif + && *path != ':') { + *sys++ = *path++; + } + *sys = '\0'; + path++; + + if (*(path - 1) == '@') + { + (void) strcpy (user, system); /* saw user part of user@host */ + sys = system; /* start over */ + while (*path != ':') { + *sys++ = *path++; + } + *sys = '\0'; + path++; + } +#ifdef COMPAT + else if (*(path - 1) == '.') + { + while (*path != ':') { + *user++ = *path++; + } + *user = '\0'; + path++; + } +#endif + else + *user = '\0'; + + while (*path) { + *dev++ = *path++; + } + *dev = '\0'; + +#ifdef USE_REXEC +/* + * Execute the remote command using rexec + */ + READ(i) = WRITE(i) = _rmt_rexec(system, login); + if (READ(i) < 0) + return -1; +#else +/* + * setup the pipes for the 'rsh' command and fork + */ + + if (pipe(Ptc[i]) == -1 || pipe(Ctp[i]) == -1) + return(-1); + + if ((rc = fork()) == -1) + return(-1); + + if (rc == 0) + { + close(0); + dup(Ptc[i][0]); + close(Ptc[i][0]); close(Ptc[i][1]); + close(1); + dup(Ctp[i][1]); + close(Ctp[i][0]); close(Ctp[i][1]); + (void) setuid (getuid ()); + (void) setgid (getgid ()); + if (*login) + { + execl("/usr/ucb/rsh", "rsh", system, "-l", login, + "/etc/rmt", (char *) 0); + execl("/usr/bin/remsh", "remsh", system, "-l", login, + "/etc/rmt", (char *) 0); + } + else + { + execl("/usr/ucb/rsh", "rsh", system, + "/etc/rmt", (char *) 0); + execl("/usr/bin/remsh", "remsh", system, + "/etc/rmt", (char *) 0); + } + +/* + * bad problems if we get here + */ + + perror("exec"); + exit(1); + } + + close(Ptc[i][0]); close(Ctp[i][1]); +#endif + +/* + * now attempt to open the tape device + */ + + sprintf(buffer, "O%s\n%d\n", device, oflag); + if (command(i, buffer) == -1 || status(i) == -1) + return(-1); + + return(i); +} + + + +/* + * _rmt_close --- close a remote magtape unit and shut down + */ + +static int _rmt_close(fildes) +int fildes; +{ + int rc; + + if (command(fildes, "C\n") != -1) + { + rc = status(fildes); + + abort(fildes); + return(rc); + } + + return(-1); +} + + + +/* + * _rmt_read --- read a buffer from a remote tape + */ + +static int _rmt_read(fildes, buf, nbyte) +int fildes; +char *buf; +unsigned int nbyte; +{ + int rc, i; + char buffer[BUFMAGIC]; + + sprintf(buffer, "R%d\n", nbyte); + if (command(fildes, buffer) == -1 || (rc = status(fildes)) == -1) + return(-1); + + for (i = 0; i < rc; i += nbyte, buf += nbyte) + { + nbyte = read(READ(fildes), buf, rc); + if (nbyte <= 0) + { + abort(fildes); + errno = EIO; + return(-1); + } + } + + return(rc); +} + + + +/* + * _rmt_write --- write a buffer to the remote tape + */ + +static int _rmt_write(fildes, buf, nbyte) +int fildes; +char *buf; +unsigned int nbyte; +{ + int rc; + char buffer[BUFMAGIC]; + int (*pstat)(); + + sprintf(buffer, "W%d\n", nbyte); + if (command(fildes, buffer) == -1) + return(-1); + + pstat = signal(SIGPIPE, SIG_IGN); + if (write(WRITE(fildes), buf, nbyte) == nbyte) + { + signal (SIGPIPE, pstat); + return(status(fildes)); + } + + signal (SIGPIPE, pstat); + abort(fildes); + errno = EIO; + return(-1); +} + + + +/* + * _rmt_lseek --- perform an imitation lseek operation remotely + */ + +static long _rmt_lseek(fildes, offset, whence) +int fildes; +long offset; +int whence; +{ + char buffer[BUFMAGIC]; + + sprintf(buffer, "L%d\n%d\n", offset, whence); + if (command(fildes, buffer) == -1) + return(-1); + + return(status(fildes)); +} + + +/* + * _rmt_ioctl --- perform raw tape operations remotely + */ + +#ifdef RMTIOCTL +static _rmt_ioctl(fildes, op, arg) +int fildes, op; +char *arg; +{ + char c; + int rc, cnt; + char buffer[BUFMAGIC]; + +/* + * MTIOCOP is the easy one. nothing is transfered in binary + */ + + if (op == MTIOCTOP) + { + sprintf(buffer, "I%d\n%d\n", ((struct mtop *) arg)->mt_op, + ((struct mtop *) arg)->mt_count); + if (command(fildes, buffer) == -1) + return(-1); + return(status(fildes)); + } + +/* + * we can only handle 2 ops, if not the other one, punt + */ + + if (op != MTIOCGET) + { + errno = EINVAL; + return(-1); + } + +/* + * grab the status and read it directly into the structure + * this assumes that the status buffer is (hopefully) not + * padded and that 2 shorts fit in a long without any word + * alignment problems, ie - the whole struct is contiguous + * NOTE - this is probably NOT a good assumption. + */ + + if (command(fildes, "S") == -1 || (rc = status(fildes)) == -1) + return(-1); + + for (; rc > 0; rc -= cnt, arg += cnt) + { + cnt = read(READ(fildes), arg, rc); + if (cnt <= 0) + { + abort(fildes); + errno = EIO; + return(-1); + } + } + +/* + * now we check for byte position. mt_type is a small integer field + * (normally) so we will check its magnitude. if it is larger than + * 256, we will assume that the bytes are swapped and go through + * and reverse all the bytes + */ + + if (((struct mtget *) arg)->mt_type < 256) + return(0); + + for (cnt = 0; cnt < rc; cnt += 2) + { + c = arg[cnt]; + arg[cnt] = arg[cnt+1]; + arg[cnt+1] = c; + } + + return(0); + } +#endif /* RMTIOCTL */ + +/* + * Added routines to replace open(), close(), lseek(), ioctl(), etc. + * The preprocessor can be used to remap these the rmtopen(), etc + * thus minimizing source changes: + * + * #ifdef + * # define access rmtaccess + * # define close rmtclose + * # define creat rmtcreat + * # define dup rmtdup + * # define fcntl rmtfcntl + * # define fstat rmtfstat + * # define ioctl rmtioctl + * # define isatty rmtisatty + * # define lseek rmtlseek + * # define lstat rmtlstat + * # define open rmtopen + * # define read rmtread + * # define stat rmtstat + * # define write rmtwrite + * #endif + * + * -- Fred Fish + * + * ADR --- I set up a include file for this + * + */ + +/* + * Note that local vs remote file descriptors are distinquished + * by adding a bias to the remote descriptors. This is a quick + * and dirty trick that may not be portable to some systems. + */ + +#define REM_BIAS 128 + + +/* + * Test pathname to see if it is local or remote. A remote device + * is any string that contains ":/dev/". Returns 1 if remote, + * 0 otherwise. + */ + +static int remdev (path) +register char *path; +{ +#define strchr index + extern char *strchr (); + + if ((path = strchr (path, ':')) != NULL) + { + if (strncmp (path + 1, "/dev/", 5) == 0) + { + return (1); + } + } + return (0); +} + + +/* + * Open a local or remote file. Looks just like open(2) to + * caller. + */ + +int rmtopen (path, oflag, mode) +char *path; +int oflag; +int mode; +{ + int fd; + + if (remdev (path)) + { + fd = _rmt_open (path, oflag, mode); + + return (fd == -1) ? -1 : (fd + REM_BIAS); + } + else + { + return (open (path, oflag, mode)); + } +} + +/* + * Test pathname for specified access. Looks just like access(2) + * to caller. + */ + +int rmtaccess (path, amode) +char *path; +int amode; +{ + if (remdev (path)) + { + return (0); /* Let /etc/rmt find out */ + } + else + { + return (access (path, amode)); + } +} + + +/* + * Read from stream. Looks just like read(2) to caller. + */ + +int rmtread (fildes, buf, nbyte) +int fildes; +char *buf; +unsigned int nbyte; +{ + if (isrmt (fildes)) + { + return (_rmt_read (fildes - REM_BIAS, buf, nbyte)); + } + else + { + return (read (fildes, buf, nbyte)); + } +} + + +/* + * Write to stream. Looks just like write(2) to caller. + */ + +int rmtwrite (fildes, buf, nbyte) +int fildes; +char *buf; +unsigned int nbyte; +{ + if (isrmt (fildes)) + { + return (_rmt_write (fildes - REM_BIAS, buf, nbyte)); + } + else + { + return (write (fildes, buf, nbyte)); + } +} + +/* + * Perform lseek on file. Looks just like lseek(2) to caller. + */ + +long rmtlseek (fildes, offset, whence) +int fildes; +long offset; +int whence; +{ + if (isrmt (fildes)) + { + return (_rmt_lseek (fildes - REM_BIAS, offset, whence)); + } + else + { + return (lseek (fildes, offset, whence)); + } +} + + +/* + * Close a file. Looks just like close(2) to caller. + */ + +int rmtclose (fildes) +int fildes; +{ + if (isrmt (fildes)) + { + return (_rmt_close (fildes - REM_BIAS)); + } + else + { + return (close (fildes)); + } +} + +/* + * Do ioctl on file. Looks just like ioctl(2) to caller. + */ + +int rmtioctl (fildes, request, arg) +int fildes; +unsigned long request; +char *arg; +{ + if (isrmt (fildes)) + { +#ifdef RMTIOCTL + return (_rmt_ioctl (fildes - REM_BIAS, request, arg)); +#else + errno = EOPNOTSUPP; + return (-1); /* For now (fnf) */ +#endif + } + else + { + return (ioctl (fildes, request, arg)); + } +} + + +/* + * Duplicate an open file descriptor. Looks just like dup(2) + * to caller. + */ + +int rmtdup (fildes) +int fildes; +{ + if (isrmt (fildes)) + { + errno = EOPNOTSUPP; + return (-1); /* For now (fnf) */ + } + else + { + return (dup (fildes)); + } +} + +/* + * Get file status. Looks just like fstat(2) to caller. + */ + +int rmtfstat (fildes, buf) +int fildes; +struct stat *buf; +{ + if (isrmt (fildes)) + { + errno = EOPNOTSUPP; + return (-1); /* For now (fnf) */ + } + else + { + return (fstat (fildes, buf)); + } +} + + +/* + * Get file status. Looks just like stat(2) to caller. + */ + +int rmtstat (path, buf) +char *path; +struct stat *buf; +{ + if (remdev (path)) + { + errno = EOPNOTSUPP; + return (-1); /* For now (fnf) */ + } + else + { + return (stat (path, buf)); + } +} + + + +/* + * Create a file from scratch. Looks just like creat(2) to the caller. + */ + +#include /* BSD DEPENDANT!!! */ +/* #include /* use this one for S5 with remote stuff */ + +int rmtcreat (path, mode) +char *path; +int mode; +{ + if (remdev (path)) + { + return (rmtopen (path, 1 | O_CREAT, mode)); + } + else + { + return (creat (path, mode)); + } +} + +/* + * Isrmt. Let a programmer know he has a remote device. + */ + +int isrmt (fd) +int fd; +{ + return (fd >= REM_BIAS); +} + +/* + * Rmtfcntl. Do a remote fcntl operation. + */ + +int rmtfcntl (fd, cmd, arg) +int fd, cmd, arg; +{ + if (isrmt (fd)) + { + errno = EOPNOTSUPP; + return (-1); + } + else + { + return (fcntl (fd, cmd, arg)); + } +} + +/* + * Rmtisatty. Do the isatty function. + */ + +int rmtisatty (fd) +int fd; +{ + if (isrmt (fd)) + return (0); + else + return (isatty (fd)); +} + + +/* + * Get file status, even if symlink. Looks just like lstat(2) to caller. + */ + +int rmtlstat (path, buf) +char *path; +struct stat *buf; +{ + if (remdev (path)) + { + errno = EOPNOTSUPP; + return (-1); /* For now (fnf) */ + } + else + { + return (lstat (path, buf)); + } +} diff --git a/lib/librmt/rmtops.3 b/lib/librmt/rmtops.3 new file mode 100644 index 000000000000..0c3ce33777d7 --- /dev/null +++ b/lib/librmt/rmtops.3 @@ -0,0 +1,230 @@ +... +... $Header: /cvsroot/src/lib/librmt/rmtops.3,v 1.1 1996/08/09 03:35:20 jtc Exp $ +... +... $Log: rmtops.3,v $ +... Revision 1.1 1996/08/09 03:35:20 jtc +... Remote mag tape library from volume 18 of comp.sources.unix. +... +... Revision 1.4 88/10/25 17:05:05 root +... Documented configuration options and added Dan Kegel to AUTHORS. ADR. +... +... Revision 1.3 87/10/30 10:36:38 root +... Some cleanup. 4.3 syntax is default, 4.2 is a compile time option. +... +... Revision 1.2 86/10/09 16:38:02 root +... Changed to reflect 4.3BSD rcp syntax and better rmt(8) capabilities. ADR. +... +... Revision 1.1 86/10/09 16:18:47 root +... Initial revision +... +... +.TH RMTOPS 3 local +.SH NAME +rmtops \- access tape drives on remote machines +.SH SYNOPSIS +.nf +.ft B +#include +#include /* MUST come after */ + +int isrmt (fd) +int fd; + +int rmtaccess (file, mode) +char *file; +int mode; + +int rmtclose (fd) +int fd; + +int rmtcreat (file, mode) +char *file; +int mode; + +int rmtdup (fd) +int fd; + +int rmtfcntl (fd, cmd, arg) +int fd, cmd, arg; + +int rmtfstat (fd, buf) +int fd; +struct stat *buf; + +int rmtioctl (fd, request, argp) +int fd, request; +char *argp; + +int rmtisatty (fd) +int fd; + +long rmtlseek (fd, offset, whence) +int fd, whence; +long offset; + +int rmtlstat (file, buf) +char *file; +struct stat *buf; + +int rmtopen (file, flags [, mode]) +char *file; +int flags, mode; + +int rmtread (fd, buf, nbytes) +int fd, nbytes; +char *buf; + +int rmtstat (file, buf) +char *file; +struct stat *buf; + +int rmtwrite (fd, buf, nbytes) +int fd, nbytes; +char *buf; +.fi +.ft R +.SH DESCRIPTION +.I Rmtops +provides a simple means of transparently accessing tape drives +on remote machines over the ethernet, via +.IR rsh (1) +and +.IR rmt (8). +These routines are used like their corresponding +system calls, but allow the user to open up a tape drive on a remote +system on which he or she has an account and the appropriate remote +permissions. +.PP +A remote tape drive file name has the form +.sp +.RS +.RI [ user @] system :/dev/??? +.RE +.sp +where +.I system +is the remote system, +.I /dev/??? +is the particular drive on the remote system (raw, blocked, rewinding, +non-rewinding, etc.), and the optional +.I user +is the login name to be used on the remote system, if different from +the current user's login name. +.PP +The library source code may be optionally compiled to recognize the +old, 4.2 BSD, remote syntax +.sp +.RS +.IR system [. user ]:/dev/??? +.RE +.sp +By default, only the first form (introduced in 4.3 BSD) is recognized. +.PP +For transparency, the user should include the file +.IR , +which has the following defines in it: +.PP +.nf +#define access rmtaccess +#define close rmtclose +#define creat rmtcreat +#define dup rmtdup +#define fcntl rmtfcntl +#define fstat rmtfstat +#define ioctl rmtioctl +#define isatty rmtisatty +#define lseek rmtlseek +#define lstat rmtlstat +#define open rmtopen +#define read rmtread +#define stat rmtstat +#define write rmtwrite +.fi +.PP +This allows the programmer to use +.IR open , +.IR close , +.IR read , +.IR write , +etc. in their normal fashion, with the +.I rmtops +routines taking care of differentiating between local and remote files. +This file should be included +.I before +including the file +.IR , +since it redefines the identifier ``stat,'' which is used to declare +objects of type +.BR "struct stat" . +.PP +The routines differentiate between local and remote file descriptors by +adding a bias (currently 128) to the file descriptor of the pipe. +The programmer, if he or she must know if a file is remote, should use the +.I isrmt +function. +.SH FILES +.TP +.B /usr/lib/librmt.a +Contains the remote tape library. To include the library with a program, +add the flag +.B \-lrmt +to the +.IR cc (1) +command line. +.SH SEE ALSO +.IR rcp (1), +.IR rsh (1), +.IR rmt (8), +and the appropriate system calls in section 2. +.SH DIAGNOSTICS +Several of these routines will return \-1 and set +.I errno +to EOPNOTSUPP, if they are given a remote file name or a file descriptor +on an open remote file (e.g., +.IR rmtdup ). +.SH BUGS +See \s-1DIAGNOSTICS\s+1 above. It is to be hoped that true remote file systems +will eventually appear, and eliminate the need for these routines. +.PP +There is no way to use remote tape drives with the +.IR stdio (3) +package, short of recompiling it entirely to use these routines. +.PP +The +.IR rmt (8) +protocol is not very capable. In particular, it relies on +TCP/IP sockets for error free transmission, and does no data validation +of its own. +.SH CONFIGURATION OPTIONS +The library may be compiled to allow the use of 4.2 BSD style remote +file names. This is not recommended. +.PP +By default, the library opens two pipes to +.IR rsh (1). +It may optionally be compiled to use +.IR rexec (3), +instead. Doing so requires the use of a +.I .netrc +file in the user's home directory, or that the application designer +be willing to have +.I rexec +prompt the user for a login name and password on the remote host. +.SH AUTHORS +Jeff Lee (gatech!jeff) wrote the original routines for accessing +tape drives via +.IR rmt (8). +.PP +Fred Fish (unisoft!fnf) redid them into a general purpose library. +.PP +Arnold Robbins +(formerly gatech!arnold, now emory!arnold) +added the ability to specify a user +name on the remote system, the +.B +include file, this man page, +cleaned up the library a little, +and made the appropriate changes for 4.3 BSD. +.PP +Dan Kegel (srs!dan) contributed the code to use +.IR rexec (3) +library routine.