/* Remote tape emulator subroutines.
Copyright (C) 1988 Free Software Foundation
This file is part of GNU Tar.
GNU Tar is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
GNU Tar is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Tar; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
/* JF: modified to make all rmtXXX calls into macros for speed */
static char *RCSid
= "$Header: /usr/src/local/usr.lib/librmt/RCS/rmtlib.c,v 1.7 89/03/23 14:09:51 root Exp Locker: root $";
* 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
* 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
* Redone as a library that can replace open, read, write, etc, by
* Fred Fish, with some additional work by Arnold Robbins.
/* Use -DUSE_REXEC for rexec code, courtesy of Dan Kegel, srs!dan */
#if defined(USG) && !defined(HAVE_MTIO)
#define BUFMAGIC 64 /* a magic number for buffer sizes */
* 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 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 };
* _rmt_panic --- close off a remote tape connection
static void _rmt_panic(fildes
)
* command --- attempt to perform a remote tape command
static int command(fildes
, buf
)
* save current pipe status and try to make the request
pstat
= signal(SIGPIPE
, SIG_IGN
);
if (write(WRITE(fildes
), buf
, blen
) == blen
)
* something went wrong. close down and go home
* status --- retrieve the status from the pipe
static int status(fildes
)
* read the reply command line
for (i
= 0, cp
= buffer
; i
< BUFMAGIC
; i
++, cp
++)
if (read(READ(fildes
), cp
, 1) != 1)
* check the return status
for (cp
= buffer
; *cp
; cp
++)
if (*cp
== 'E' || *cp
== 'F')
while (read(READ(fildes
), &c
, 1) == 1)
* check for mis-synced pipes
* 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.
char *user
; /* may be NULL */
struct servent
*rexecserv
;
int save_stdin
= dup(fileno(stdin
));
int save_stdout
= dup(fileno(stdout
));
int tape_fd
; /* Return value. */
* When using cpio -o < filename, stdin is no longer the tty.
* But the rexec subroutine reads the login and the passwd on stdin,
* to allow remote execution of the command.
* So, reopen stdin and stdout on /dev/tty before the rexec and
* give them back their original value after.
if (freopen("/dev/tty", "r", stdin
) == NULL
)
freopen("/dev/null", "r", stdin
);
if (freopen("/dev/tty", "w", stdout
) == NULL
)
freopen("/dev/null", "w", stdout
);
rexecserv
= getservbyname("exec", "tcp");
fprintf (stderr
, "? exec/tcp: service not available.");
if ((user
!= NULL
) && *user
== '\0')
tape_fd
= rexec (&host
, rexecserv
->s_port
, user
, NULL
,
"/etc/rmt", (int *)NULL
);
fdopen(save_stdout
, "w");
* _rmt_open --- open a magtape device on system specified, as given user
* file name has the form [user@]system:/dev/????
* file name has the form system[.user]:/dev/????
#define MAXHOSTLEN 257 /* BSD allows very long host names... */
int __rmt_open (path
, oflag
, mode
, bias
)
* first, find an open pair of file descriptors
for (i
= 0; i
< MAXUNIT
; i
++)
if (READ(i
) == -1 && WRITE(i
) == -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.
(void) strcpy (user
, system
); /* saw user part of user@host */
sys
= system
; /* start over */
else if (*(path
- 1) == '.')
* Execute the remote command using rexec
READ(i
) = WRITE(i
) = _rmt_rexec(system
, login
);
* setup the pipes for the 'rsh' command and fork
if (pipe(Ptc
[i
]) == -1 || pipe(Ctp
[i
]) == -1)
close(Ptc
[i
][0]); close(Ptc
[i
][1]);
close(Ctp
[i
][0]); close(Ctp
[i
][1]);
(void) setuid (getuid ());
(void) setgid (getgid ());
execl("/usr/ucb/rsh", "rsh", system
, "-l", login
,
execl("/usr/bin/remsh", "remsh", system
, "-l", login
,
execl("/usr/bin/rsh", "rsh", system
, "-l", login
,
execl("/usr/bsd/rsh", "rsh", system
, "-l", login
,
execl("/usr/bin/nsh", "nsh", system
, "-l", login
,
execl("/usr/ucb/rsh", "rsh", system
,
execl("/usr/bin/remsh", "remsh", system
,
execl("/usr/bin/rsh", "rsh", system
,
execl("/usr/bsd/rsh", "rsh", system
,
execl("/usr/bin/nsh", "nsh", system
,
* bad problems if we get here
perror("remote shell exec");
close(Ptc
[i
][0]); close(Ctp
[i
][1]);
* now attempt to open the tape device
sprintf(buffer
, "O%s\n%d\n", device
, oflag
);
if (command(i
, buffer
) == -1 || status(i
) == -1)
* _rmt_close --- close a remote magtape unit and shut down
if (command(fildes
, "C\n") != -1)
* _rmt_read --- read a buffer from a remote tape
int __rmt_read(fildes
, buf
, nbyte
)
sprintf(buffer
, "R%d\n", nbyte
);
if (command(fildes
, buffer
) == -1 || (rc
= status(fildes
)) == -1)
for (i
= 0; i
< rc
; i
+= nbyte
, buf
+= nbyte
)
nbyte
= read(READ(fildes
), buf
, rc
);
* _rmt_write --- write a buffer to the remote tape
int __rmt_write(fildes
, buf
, nbyte
)
sprintf(buffer
, "W%d\n", nbyte
);
if (command(fildes
, buffer
) == -1)
pstat
= signal(SIGPIPE
, SIG_IGN
);
if (write(WRITE(fildes
), buf
, nbyte
) == nbyte
)
* _rmt_lseek --- perform an imitation lseek operation remotely
long __rmt_lseek(fildes
, offset
, whence
)
sprintf(buffer
, "L%d\n%d\n", offset
, whence
);
if (command(fildes
, buffer
) == -1)
* _rmt_ioctl --- perform raw tape operations remotely
__rmt_ioctl(fildes
, op
, arg
)
* MTIOCOP is the easy one. nothing is transfered in binary
sprintf(buffer
, "I%d\n%d\n", ((struct mtop
*) arg
)->mt_op
,
((struct mtop
*) arg
)->mt_count
);
if (command(fildes
, buffer
) == -1)
* we can only handle 2 ops, if not the other one, punt
* 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)
for (; rc
> 0; rc
-= cnt
, arg
+= cnt
)
cnt
= read(READ(fildes
), arg
, rc
);
* 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)
for (cnt
= 0; cnt
< rc
; cnt
+= 2)