1
0
Files
irix-657m-src/eoe/cmd/bsd/dump/dumprmt.c
2022-09-29 17:59:04 +03:00

493 lines
10 KiB
C

#ident "$Revision: 1.9 $"
/*
* Modified to use send()/recv() instead of write()/read(),
* to work around an IRIX 3.* bug which prevents sending more
* than 2 Gbytes of data over a single TCP connection.
*
* Also modified for significantly better error handling.
* This includes new routine rmtreset() called in dumptape.c
* This is so that if remote tape daemon dies, then current process will
* exit X_REWRITE, and the previous process will still have 'rmtape'
* indicating the dead connection. When it forks again, the new child
* will get SIGPIPE errors, and won't be able to recover, so it will
* exit X_REWRITE, and we get nowhere. By having the X_REWRITE case
* call rmtreset(), the problem can be immediately cleaned up.
* -Mike Muuss, BRL, 8-March-1991.
*/
/*
* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*
* static char sccsid[] = "@(#)dumprmt.c 5.5 (Berkeley) 10/22/87";
*/
#if _DUMP_
#include "dump.h"
#else
#include "restore.h"
#endif
#include <sys/mtio.h>
#include <sys/socket.h>
#include <netdb.h>
#include <pwd.h>
#include "dumprmt.h"
#define TS_CLOSED 0
#define TS_OPEN 1
static int doingvers;
static int rmtape = -1;
static char *rmtpeer;
static char *rmtpeer_orig;
static int rmtstate = TS_CLOSED;
static int server_version = -1;
static int okname(char *);
static int rmtcall(const char *, char *);
static void rmtconnaborted();
static void rmterror(char *);
static void rmtgetconn(void);
static int rmtgetb(void);
static int rmtgets(char *, int);
static int rmtreply(const char *);
int
rmthost(char **host)
{
rmtpeer = *host;
rmtpeer_orig = strdup(rmtpeer);
signal(SIGPIPE, rmtconnaborted);
rmtgetconn();
*host = rmtpeer;
if (rmtape < 0)
return (0);
return (1);
}
void
rmtreset(void)
{
if (rmtape != -1)
(void)close(rmtape);
rmtape = -1;
rmtpeer = rmtpeer_orig;
rmtpeer_orig = strdup(rmtpeer); /* rmtpeer clobbered by rmtgetconn */
rmtgetconn();
if( rmtape < 0 ) {
msg("Unable to reset connection to %s\n", rmtpeer);
exit(X_ABORT);
}
}
static void
rmtconnaborted()
{
fprintf(stderr, "dump: Lost connection to remote host (SIGPIPE).\n");
if (rmtape != -1)
(void) close(rmtape);
rmtape = -1;
exit(X_REWRITE);
}
/*
* Simply log an error message, and close link to remote machine.
* Propagating the error condition upwards is the duty of the caller.
*/
static void
rmterror(char *str)
{
msg("ERROR %s, disconnecting from %s.\n", str, rmtpeer);
if (rmtape != -1)
(void)close(rmtape);
rmtape = -1;
errno = 0;
}
static void
rmtgetconn(void)
{
static struct servent *sp = 0;
static struct passwd *pw = 0;
char *name;
char *tmphost;
int size;
if (sp == 0) {
sp = getservbyname("shell", "tcp");
if (sp == 0) {
fprintf(stderr, "rdump: shell/tcp: unknown service\n");
exit(1);
}
}
if (pw == 0) {
pw = getpwuid(getuid());
if (pw == 0) {
fprintf(stderr, "rdump: who are you?\n");
exit(1);
}
}
/*
* The format now is user@host:device (not host.user:device)
*/
tmphost = rindex(rmtpeer, '@');
if (tmphost) {
*tmphost++ = 0;
name = rmtpeer;
rmtpeer = tmphost;
if (!okname(name))
exit(1);
} else {
name = pw->pw_name;
}
rmtape = rcmd(&rmtpeer, sp->s_port, pw->pw_name, name, "/etc/rmt", 0);
if (rmtape < 0)
return;
size = ntrec * TP_BSIZE;
while (size > TP_BSIZE &&
setsockopt(rmtape, SOL_SOCKET, SO_SNDBUF, &size, sizeof (size)) < 0)
size -= TP_BSIZE;
}
static int
okname(char *cp0)
{
char *cp;
int c;
for (cp = cp0; *cp; cp++) {
c = *cp;
if (!isascii(c) || !(isalnum(c) || c == '_' || c == '-')) {
fprintf(stderr, "rdump: invalid user name %s\n", cp0);
return (0);
}
}
return (1);
}
int
rmtopen(const char *tape, int mode, ...)
{
char buf[256];
int rfd, rc, failedv = 0;
doingvers = 0; /* be paranoid */
if (rmtape < 0) {
rmtpeer = rmtpeer_orig;
rmtpeer_orig = strdup(rmtpeer);
rmtgetconn();
if( rmtape < 0 ) return (-1);
}
reopen:
(void)sprintf(buf, "O%s\n%d\n", tape, mode);
rmtstate = TS_OPEN;
rfd = rmtcall(tape, buf);
if(rfd == -1)
return rfd;
if(failedv == 0) {
/* determine which version for mtiocget; have to close
* and reopen, because most older rmt servers would exit
* on unrecognized commands. */
strcpy(buf, "V2\n");
doingvers++;
rc = rmtcall("ioctl", buf);
doingvers--;
if(rc == -1) {
failedv = 1;
rmtreset();
sleep(10); /* allow time for cleanup. hack, hack... */
goto reopen;
}
if(rc > 0)
server_version = rc;
}
return rfd;
}
/* ARGSUSED */
int
rmtclose(int fd)
{
int status;
if (rmtstate != TS_OPEN) {
return -1;
}
if (rmtape < 0 ) {
/* Special handling for dead remote machines:
* pretend it closed OK.
*/
rmtstate = TS_CLOSED;
return 0;
}
status = rmtcall("close", "C\n");
rmtstate = TS_CLOSED;
return status;
}
int
rmtread(char *buf, int count)
{
char line[30];
int n, i, cc;
if (rmtape < 0 ) return -1;
(void)sprintf(line, "R%d\n", count);
n = rmtcall("read", line);
if (n < 0) {
errno = n;
return (-1);
}
for (i = 0; i < n; i += cc) {
cc = recv(rmtape, buf+i, n - i, 0);
if (cc <= 0) {
rmterror("rmtread <= 0");
return (i);
}
}
return (n);
}
int
rmtwrite(char *buf, int count)
{
char line[30];
int len;
if (rmtape < 0 ) return -1;
(void)sprintf(line, "W%d\n", count);
len = strlen(line);
if (send(rmtape, line, len, 0) != len ||
send(rmtape, buf, count, 0) != count) {
perror("send");
return -1;
}
return (rmtreply("write"));
}
int
rmtseek(int offset, int pos)
{
char line[80];
(void)sprintf(line, "L%d\n%d\n", offset, pos);
return (rmtcall("seek", line));
}
int
rmtioctl(int cmd, void *arg)
{
char buf[256];
char c;
int rc, cnt, ssize;
char *p, *omtget;
short mt_type;
/* MTIOCOP is the easy one. nothing is transfered in binary */
if (cmd == MTIOCTOP) {
sprintf(buf, "I%d\n%d\n", ((struct mtop *) arg)->mt_op,
((struct mtop *) arg)->mt_count);
return (rmtcall("ioctl", buf));
}
else if(cmd == MTIOCGET) {
/*
* grab the status and read it directly into the structure
* Since the data is binary data, and the other machine might
* The original code could overwrite the space it malloced;
* must be careful to not do that!
* NOTE: the original /etc/rmt did NOT support a newline after
* the S command, and Sun still does not. Neither does the
* current bsd source, all the way through the tahoe release.
* So do NOT add the \n to this! The sgi rmt command will
* work either way. Olson, 4/91
*/
strcpy(buf, "S");
if((rc=rmtcall("ioctl", buf)) == -1)
return rc;
ssize = rc;
/*
* if server is of the old version, then the mtget struct
* that it returns is not the same as the one that we knows
* so lets convert it into something that we can use
* (note this assumption may no longer be true either, since
* sizeof old_mtget is 16, but some Sun machines return
* 20 bytes of data, at least one running 4.0.3 does...
*/
if (server_version == -1) {
p = omtget = (char *)malloc(sizeof(struct old_mtget));
if (p == (char *)0) {
errno = ENOMEM;
return(-1);
}
if(sizeof(struct old_mtget) < ssize)
ssize = sizeof(struct old_mtget);
} else {
if(sizeof(struct mtget) < ssize)
ssize = sizeof(struct mtget);
p = arg;
}
rc -= ssize;
for (; ssize > 0; ssize--)
{
cnt = rmtgetb();
if (cnt < 0)
{
abortit:
errno = EIO;
return(-1);
}
*p++ = cnt;
}
/* handle any bytes we didn't know what to do with */
while(rc-- > 0)
if(rmtgetb() == -1)
goto abortit;
/*
* 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 (server_version == -1)
p = omtget;
else
p = arg;
mt_type = ((struct mtget *) p)->mt_type;
if (mt_type >= 256) {
/* assume that we need to swap byte */
for (cnt = 0; cnt < rc; cnt += 2)
{
c = p[cnt];
p[cnt] = p[cnt+1];
p[cnt+1] = c;
}
}
/*
* now mtgetp has the correct (byte-swapped if needed)
* data, if server is of old version then lets convert
* the data into something that we can use
* else all done
*/
if (server_version == -1) {
struct mtget *newp = (struct mtget *)arg;
struct old_mtget *oldp = (struct old_mtget *)omtget;
newp->mt_type = oldp->mt_type;
newp->mt_dsreg = oldp->mt_dsreg;
newp->mt_erreg = oldp->mt_erreg;
newp->mt_resid = oldp->mt_resid;
newp->mt_fileno = oldp->mt_fileno;
newp->mt_blkno = oldp->mt_blkno;
/*
* dsreg has the HW specific bits set and it is
* different bet. tape driver, that is why
* dposn is invented in the newer version so that
* the code that deciphers dposn can be generic
* old version doesn't know about dposn, so just
* set it to 0 to get consistent result
*/
newp->mt_dposn = 0;
}
return(0);
}
else {
errno = EINVAL;
return(-1);
}
}
static int
rmtcall(const char *cmd, char *buf)
{
int len = strlen(buf);
if (rmtape < 0 ) return -1;
if (send(rmtape, buf, len, 0) != len) {
rmterror("rmtcall send()");
return (-1);
}
return (rmtreply(cmd));
}
static int
rmtreply(const char *cmd)
{
char code[30], emsg[BUFSIZ];
if( rmtgets(code, sizeof (code)) < 0 ) return (-1);
if (*code == 'E' || *code == 'F') {
if( rmtgets(emsg, sizeof (emsg)) < 0 ) return (-1);
msg("%s: %s %s\n", cmd, emsg, code + 1);
if (*code == 'F') {
rmtstate = TS_CLOSED;
return (-1);
}
return (-1);
}
if (*code != 'A') {
msg("Protocol to remote tape server botched (code='%s'?).\n",
code);
rmterror("rmtreply protocol");
return (-1);
}
return (atoi(code + 1));
}
static int
rmtgetb(void)
{
unsigned char c;
int n;
if ((n = recv(rmtape, &c, 1, 0)) != 1) {
if(!doingvers) { /* do not want complaint during this */
if (n == 0)
msg("Couldn't read from remote tape\n");
else
perror("recv");
rmterror("rmtgetb recv");
}
return (-1);
}
return (c);
}
static int
rmtgets(char *cp, int len)
{
int c;
while (len > 1) {
if( (c = rmtgetb()) < 0 ) return (-1);
*cp = c;
if ( c == '\n') {
cp[1] = 0;
return (0);
}
cp++;
len--;
}
rmterror("rmtgets: reply unterminated, or too long");
return (-1);
}