/*#define USE_REMOTE*/ /*#define USE_RCMD_RSH*/ /*#define NO_LIBSCHILY*/ /* @(#)remote.c 1.54 03/02/16 Copyright 1990-2003 J. Schilling */ #ifndef lint static char sccsid[] = "@(#)remote.c 1.54 03/02/16 Copyright 1990-2003 J. Schilling"; #endif /* * Remote tape client interface code * * Copyright (c) 1990-2003 J. Schilling * * TOTO: * Signal handler for SIGPIPE * check rmtaborted for exit() / clean abort of connection */ /* * This program 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 2, or (at your option) * any later version. * * This program 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 * this program; see the file COPYING. If not, write to the Free Software * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include /*#undef USE_REMOTE*/ /*#undef USE_RCMD_RSH*/ #if !defined(HAVE_FORK) || !defined(HAVE_SOCKETPAIR) || !defined(HAVE_DUP2) #undef USE_RCMD_RSH #endif /* * We may work without getservbyname() if we restructure the code not to * use the port number if we only use _rcmdrsh(). */ #if !defined(HAVE_GETSERVBYNAME) #undef USE_REMOTE /* Cannot get rcmd() port # */ #endif #if (!defined(HAVE_NETDB_H) || !defined(HAVE_RCMD)) && !defined(USE_RCMD_RSH) #undef USE_REMOTE /* There is no rcmd() */ #endif #include #include #include #include #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_SYS_SOCKET_H #include #endif #include #include #ifdef HAVE_NETDB_H #include #endif #ifdef HAVE_PWD_H #include #endif #include #include #include #include #include #include #include #if defined(SIGDEFER) || defined(SVR4) #define signal sigset #endif /* * On Cygwin, there are no privilleged ports. * On UNIX, rcmd() uses privilleged port that only work for root. */ #ifdef IS_CYGWIN #define privport_ok() (1) #else #define privport_ok() (geteuid() == 0) #endif #ifdef NO_LIBSCHILY /* >>>>>>>>>> Begin compatibility code for use without libschily <<<<<<<<<<< */ /* * This code is needed for applications that like to use librmt but don't * link against libschily. * * A working vfprintf(), snprintf(), strerror(), memmove() and atoll() is * needed. * For this reason, the portability in this compilation mode is limited. */ #include #ifndef HAVE_ERRNO_DEF extern int errno; #endif #define geterrno() (errno) #define seterrno(err) (errno = (err)) #define js_snprintf snprintf #define _niread _lniread #define _niwrite _lniwrite #define movebytes(f,t,n) memmove((t), (f), (n)) #define astoll(s,np) *np = atoll(s); #define comerrno(e,s) {_errmsgno(e, s); rmt_exit(e);} #define errmsgno _errmsgno #define errmsgstr strerror LOCAL int _niread __PR((int f, void *buf, int count)); LOCAL int _niwrite __PR((int f, void *buf, int count)); LOCAL int errmsgno __PR((int, const char *, ...)); LOCAL int _niread(f, buf, count) int f; void *buf; int count; { int ret; while ((ret = read(f, buf, count)) < 0 && geterrno() == EINTR) ; return (ret); } LOCAL int _niwrite(f, buf, count) int f; void *buf; int count; { int ret; while ((ret = write(f, buf, count)) < 0 && geterrno() == EINTR) ; return (ret); } /* * On UNIX errno is a small non-negative number, so we assume that * negative values cannot be a valid errno and don't print the error * string in this case. Note that this macro does not work on BeOS. */ #define silent_error(e) ((e) < 0) /* VARARGS2 */ LOCAL int #ifdef PROTOTYPES errmsgno(int err, const char *msg, ...) #else errmsgno(err, msg, va_alist) int err; char *msg; va_dcl #endif { va_list args; int ret; char errbuf[20]; char *errnam; ret = fprintf(stderr, "librmt: "); if (ret < 0) return (ret); if (!silent_error(err)) { errnam = errmsgstr(err); if (errnam == NULL) { (void) js_snprintf(errbuf, sizeof (errbuf), "Error %d", err); errnam = errbuf; } ret = fprintf(stderr, "%s. ", errnam); if (ret < 0) return (ret); } #ifdef PROTOTYPES va_start(args, msg); #else va_start(args); #endif ret = vfprintf(stderr, msg, args); va_end(args); return (ret); } /* >>>>>>>>>>> End compatibility code for use without libschily <<<<<<<<<<<< */ #endif /* NO_LIBSCHILY */ #define CMD_SIZE 80 LOCAL BOOL rmt_debug; LOCAL int (*rmt_errmsgno) __PR((int, const char *, ...)) = errmsgno; LOCAL void (*rmt_exit) __PR((int)) = exit; EXPORT void rmtinit __PR((int (*errmsgn)(int, const char *, ...), void (*eexit)(int))); EXPORT int rmtdebug __PR((int dlevel)); EXPORT char *rmtfilename __PR((char *name)); EXPORT char *rmthostname __PR((char *hostname, int hnsize, char *rmtspec)); EXPORT int rmtgetconn __PR((char* host, int trsize, int excode)); #ifdef USE_REMOTE LOCAL void rmtabrt __PR((int sig)); LOCAL BOOL okuser __PR((char *name)); LOCAL void rmtoflags __PR((int fmode, char *cmode)); EXPORT int rmtopen __PR((int fd, char* fname, int fmode)); EXPORT int rmtclose __PR((int fd)); EXPORT int rmtread __PR((int fd, char* buf, int count)); EXPORT int rmtwrite __PR((int fd, char* buf, int count)); EXPORT off_t rmtseek __PR((int fd, off_t offset, int whence)); EXPORT int rmtioctl __PR((int fd, int cmd, int count)); LOCAL int rmtmapold __PR((int cmd)); LOCAL int rmtmapnew __PR((int cmd)); LOCAL Llong rmtxgstatus __PR((int fd, int cmd)); LOCAL int rmt_v1_status __PR((int fd, struct rmtget* mtp)); LOCAL int rmt_v0_status __PR((int fd, struct mtget* mtp)); EXPORT int rmxtstatus __PR((int fd, struct rmtget* mtp)); EXPORT int rmtstatus __PR((int fd, struct mtget* mtp)); LOCAL Llong rmtcmd __PR((int fd, char* name, char* cbuf)); LOCAL void rmtsendcmd __PR((int fd, char* name, char* cbuf)); LOCAL int rmtfillrdbuf __PR((int fd)); LOCAL int rmtreadchar __PR((int fd, char *cp)); LOCAL int rmtreadbuf __PR((int fd, char *buf, int count)); LOCAL int rmtgetline __PR((int fd, char* line, int count)); LOCAL Llong rmtgetstatus __PR((int fd, char* name)); LOCAL int rmtaborted __PR((int fd)); EXPORT void _rmtg2mtg __PR((struct mtget *mtp, struct rmtget *rmtp)); EXPORT int _mtg2rmtg __PR((struct rmtget *rmtp, struct mtget *mtp)); #ifdef USE_RCMD_RSH LOCAL int _rcmdrsh __PR((char **ahost, int inport, const char *locuser, const char *remuser, const char *cmd, const char *rsh)); #endif #endif EXPORT void rmtinit(errmsgn, eexit) int (*errmsgn) __PR((int, const char *, ...)); void (*eexit) __PR((int)); { rmt_errmsgno = errmsgn; if (rmt_errmsgno == (int (*) __PR((int, const char *, ...)))0) rmt_errmsgno = errmsgno; rmt_exit = eexit; if (rmt_exit == (void (*) __PR((int)))0) rmt_exit = exit; } EXPORT int rmtdebug(dlevel) int dlevel; { int odebug = rmt_debug; rmt_debug = dlevel; return (odebug); } EXPORT char * rmtfilename(name) char *name; { char *ret; if (name[0] == '/') return (NULL); /* Absolut pathname cannot be remote */ if (name[0] == '.') { if (name[1] == '/' || (name[1] == '.' && name[2] == '/')) return (NULL); /* Relative pathname cannot be remote*/ } if ((ret = strchr(name, ':')) != NULL) { if (name[0] == ':') { /* * This cannot be a remote filename as the host part * has zero length. */ return (NULL); } ret++; /* Skip the colon. */ } return (ret); } EXPORT char * rmthostname(hostname, hnsize, rmtspec) char *hostname; register int hnsize; char *rmtspec; { register int i; register char *hp; register char *fp; register char *remfn; if ((remfn = rmtfilename(rmtspec)) == NULL) { hostname[0] = '\0'; return (NULL); } remfn--; for (fp = rmtspec, hp = hostname, i = 1; fp < remfn && i < hnsize; i++) { *hp++ = *fp++; } *hp = '\0'; return (hostname); } #ifdef USE_REMOTE EXPORT int rmtgetconn(host, trsize, excode) char *host; /* The host name to connect to */ int trsize; /* Max transfer size for SO_SNDBUF/SO_RCVBUF */ int excode; /* If != 0 use value to exit() in this func */ { static struct servent *sp = 0; static struct passwd *pw = 0; char *name = "root"; char *p; char *rmt; char *rsh; int rmtsock; char *rmtpeer; char rmtuser[128]; signal(SIGPIPE, rmtabrt); if (sp == 0) { sp = getservbyname("shell", "tcp"); if (sp == 0) { rmt_errmsgno(EX_BAD, "shell/tcp: unknown service\n"); if (excode) rmt_exit(excode); rmt_exit(EX_BAD); return (-2); /* exit function did not exit*/ } pw = getpwuid(getuid()); if (pw == 0) { rmt_errmsgno(EX_BAD, "who are you? No passwd entry found.\n"); if (excode) rmt_exit(excode); rmt_exit(EX_BAD); return (-2); /* exit function did not exit*/ } } if ((p = strchr(host, '@')) != NULL) { size_t d = p - host; if (d > sizeof (rmtuser)) d = sizeof (rmtuser); js_snprintf(rmtuser, sizeof (rmtuser), "%.*s", (int)d, host); if (! okuser(rmtuser)) { if (excode) rmt_exit(excode); rmt_exit(EX_BAD); return (-2); /* exit function did not exit*/ } name = rmtuser; host = &p[1]; } else { name = pw->pw_name; } if (rmt_debug) rmt_errmsgno(EX_BAD, "locuser: '%s' rmtuser: '%s' host: '%s'\n", pw->pw_name, name, host); rmtpeer = host; if ((rmt = getenv("RMT")) == NULL) rmt = "/etc/rmt"; rsh = getenv("RSH"); #ifdef USE_RCMD_RSH if (!privport_ok() || rsh != NULL) rmtsock = _rcmdrsh(&rmtpeer, (unsigned short)sp->s_port, pw->pw_name, name, rmt, rsh); else #endif #ifdef HAVE_RCMD rmtsock = rcmd(&rmtpeer, (unsigned short)sp->s_port, pw->pw_name, name, rmt, 0); #else rmtsock = _rcmdrsh(&rmtpeer, (unsigned short)sp->s_port, pw->pw_name, name, rmt, rsh); #endif if (rmtsock < 0) return (-1); #ifdef SO_SNDBUF while (trsize > 512 && setsockopt(rmtsock, SOL_SOCKET, SO_SNDBUF, (char *)&trsize, sizeof (trsize)) < 0) { trsize -= 512; } if (rmt_debug) rmt_errmsgno(EX_BAD, "sndsize: %d\n", trsize); #endif #ifdef SO_RCVBUF while (trsize > 512 && setsockopt(rmtsock, SOL_SOCKET, SO_RCVBUF, (char *)&trsize, sizeof (trsize)) < 0) { trsize -= 512; } if (rmt_debug) rmt_errmsgno(EX_BAD, "rcvsize: %d\n", trsize); #endif return (rmtsock); } LOCAL void rmtabrt(sig) int sig; { rmtaborted(-1); } /* * XXX Is such a function really needed? * XXX A similar function appeared on FreeBSD with a * XXX misterious change to dump(8) * XXX N.B. The FreeBSD function excludes '_' in addition. */ LOCAL BOOL okuser(name) char *name; { register char *p; register Uchar c; for (p = name; *p; ) { c = *p++; if (!isascii(c) || !(isalnum(c) || c == '-')) { rmt_errmsgno(EX_BAD, "invalid user name %s\n", name); return (FALSE); } } return (TRUE); } LOCAL void rmtoflags(fmode, cmode) int fmode; char *cmode; { register char *p; register int amt; register int maxcnt = CMD_SIZE; switch (fmode & O_ACCMODE) { case O_RDONLY: p = "O_RDONLY"; break; case O_RDWR: p = "O_RDWR"; break; case O_WRONLY: p = "O_WRONLY"; break; default: p = "Cannot Happen"; } amt = js_snprintf(cmode, maxcnt, p); if (amt < 0) return; p = cmode; p += amt; maxcnt -= amt; #ifdef O_TEXT if (fmode & O_TEXT) { amt = js_snprintf(p, maxcnt, "|O_TEXT"); if (amt < 0) return; p += amt; maxcnt -= amt; } #endif #ifdef O_NDELAY if (fmode & O_NDELAY) { amt = js_snprintf(p, maxcnt, "|O_NDELAY"); if (amt < 0) return; p += amt; maxcnt -= amt; } #endif #ifdef O_APPEND if (fmode & O_APPEND) { amt = js_snprintf(p, maxcnt, "|O_APPEND"); if (amt < 0) return; p += amt; maxcnt -= amt; } #endif #ifdef O_SYNC if (fmode & O_SYNC) { amt = js_snprintf(p, maxcnt, "|O_SYNC"); if (amt < 0) return; p += amt; maxcnt -= amt; } #endif #ifdef O_DSYNC if (fmode & O_DSYNC) { amt = js_snprintf(p, maxcnt, "|O_DSYNC"); if (amt < 0) return; p += amt; maxcnt -= amt; } #endif #ifdef O_RSYNC if (fmode & O_RSYNC) { amt = js_snprintf(p, maxcnt, "|O_RSYNC"); if (amt < 0) return; p += amt; maxcnt -= amt; } #endif #ifdef O_NONBLOCK if (fmode & O_NONBLOCK) { amt = js_snprintf(p, maxcnt, "|O_NONBLOCK"); if (amt < 0) return; p += amt; maxcnt -= amt; } #endif #ifdef O_PRIV if (fmode & O_PRIV) { amt = js_snprintf(p, maxcnt, "|O_PRIV"); if (amt < 0) return; p += amt; maxcnt -= amt; } #endif #ifdef O_LARGEFILE if (fmode & O_LARGEFILE) { amt = js_snprintf(p, maxcnt, "|O_LARGEFILE"); if (amt < 0) return; p += amt; maxcnt -= amt; } #endif #ifdef O_CREAT if (fmode & O_CREAT) { amt = js_snprintf(p, maxcnt, "|O_CREAT"); if (amt < 0) return; p += amt; maxcnt -= amt; } #endif #ifdef O_TRUNC if (fmode & O_TRUNC) { amt = js_snprintf(p, maxcnt, "|O_TRUNC"); if (amt < 0) return; p += amt; maxcnt -= amt; } #endif #ifdef O_EXCL if (fmode & O_EXCL) { amt = js_snprintf(p, maxcnt, "|O_EXCL"); if (amt < 0) return; p += amt; maxcnt -= amt; } #endif #ifdef O_NOCTTY if (fmode & O_NOCTTY) { amt = js_snprintf(p, maxcnt, "|O_NOCTTY"); if (amt < 0) return; p += amt; maxcnt -= amt; } #endif } EXPORT int rmtopen(fd, fname, fmode) int fd; char *fname; int fmode; { char cbuf[CMD_SIZE]; char cmode[CMD_SIZE]; int ret; /* * Convert all fmode bits into the symbolic fmode. * only send the lowest 2 bits in numeric mode as it would be too * dangerous because the apropriate bits differ between different * operating systems. */ rmtoflags(fmode, cmode); js_snprintf(cbuf, CMD_SIZE, "O%s\n%d %s\n", fname, fmode & O_ACCMODE, cmode); ret = rmtcmd(fd, "open", cbuf); if (ret < 0) return (ret); /* * Tell the rmt server that we are aware of Version 1 commands. */ (void) rmtioctl(fd, RMTIVERSION, 0); return (ret); } EXPORT int rmtclose(fd) int fd; { return (rmtcmd(fd, "close", "C\n")); } EXPORT int rmtread(fd, buf, count) int fd; char *buf; int count; { char cbuf[CMD_SIZE]; int n; int amt = 0; js_snprintf(cbuf, CMD_SIZE, "R%d\n", count); n = rmtcmd(fd, "read", cbuf); if (n < 0) return (-1); /* * Nice idea from disassembling Solaris ufsdump... */ if (n > count) { rmt_errmsgno(EX_BAD, "rmtread: expected response size %d, got %d\n", count, n); rmt_errmsgno(EX_BAD, "This means the remote rmt daemon is not compatible.\n"); return (rmtaborted(fd)); /* * XXX Should we better abort (exit) here? */ } amt = rmtreadbuf(fd, buf, n); return (amt); } EXPORT int rmtwrite(fd, buf, count) int fd; char *buf; int count; { char cbuf[CMD_SIZE]; js_snprintf(cbuf, CMD_SIZE, "W%d\n", count); rmtsendcmd(fd, "write", cbuf); if (_niwrite(fd, buf, count) != count) rmtaborted(fd); return (rmtgetstatus(fd, "write")); } EXPORT off_t rmtseek(fd, offset, whence) int fd; off_t offset; int whence; { char cbuf[CMD_SIZE]; switch (whence) { case 0: whence = SEEK_SET; break; case 1: whence = SEEK_CUR; break; case 2: whence = SEEK_END; break; default: seterrno(EINVAL); return (-1); } js_snprintf(cbuf, CMD_SIZE, "L%lld\n%d\n", (Llong)offset, whence); return ((off_t)rmtcmd(fd, "seek", cbuf)); } EXPORT int rmtioctl(fd, cmd, count) int fd; int cmd; int count; { char cbuf[CMD_SIZE]; char c = 'I'; int rmtversion = RMT_NOVERSION; int i; if (cmd != RMTIVERSION) rmtversion = rmtioctl(fd, RMTIVERSION, 0); if (cmd >= 0 && (rmtversion == RMT_VERSION)) { /* * Opcodes 0..7 are unique across different architectures. * But as in many cases Linux does not even follow this rule. * If we know that we are calling a VERSION 1 client, we may * safely assume that the client is not using Linux mapping * but the standard mapping. */ i = rmtmapold(cmd); if (cmd <= 7 && i < 0) { /* * We cannot map the current command but it's value is * within the range 0..7. Do not send it over the wire. */ seterrno(EINVAL); return (-1); } if (i >= 0) cmd = i; } if (cmd > 7 && (rmtversion == RMT_VERSION)) { i = rmtmapnew(cmd); if (i >= 0) { cmd = i; c = 'i'; } } js_snprintf(cbuf, CMD_SIZE, "%c%d\n%d\n", c, cmd, count); return (rmtcmd(fd, "ioctl", cbuf)); } /* * Map all old opcodes that should be in range 0..7 to numbers /etc/rmt expects * This is needed because Linux does not follow the UNIX conventions. */ LOCAL int rmtmapold(cmd) int cmd; { switch (cmd) { #ifdef MTWEOF case MTWEOF: return (0); #endif #ifdef MTFSF case MTFSF: return (1); #endif #ifdef MTBSF case MTBSF: return (2); #endif #ifdef MTFSR case MTFSR: return (3); #endif #ifdef MTBSR case MTBSR: return (4); #endif #ifdef MTREW case MTREW: return (5); #endif #ifdef MTOFFL case MTOFFL: return (6); #endif #ifdef MTNOP case MTNOP: return (7); #endif } return (-1); } /* * Map all new opcodes that should be in range above 7 to the * values expected by the 'i' command of /etc/rmt. */ LOCAL int rmtmapnew(cmd) int cmd; { switch (cmd) { #ifdef MTCACHE case MTCACHE: return (RMTICACHE); #endif #ifdef MTNOCACHE case MTNOCACHE: return (RMTINOCACHE); #endif #ifdef MTRETEN case MTRETEN: return (RMTIRETEN); #endif #ifdef MTERASE case MTERASE: return (RMTIERASE); #endif #ifdef MTEOM case MTEOM: return (RMTIEOM); #endif #ifdef MTNBSF case MTNBSF: return (RMTINBSF); #endif } return (-1); } /* * Get one (single) member of struct mtget from remote */ LOCAL Llong rmtxgstatus(fd, cmd) int fd; char cmd; { char cbuf[CMD_SIZE]; /* No newline */ js_snprintf(cbuf, CMD_SIZE, "s%c", cmd); seterrno(0); return (rmtcmd(fd, "extended status", cbuf)); } LOCAL int rmt_v1_status(fd, mtp) int fd; struct rmtget *mtp; { mtp->mt_xflags = 0; mtp->mt_erreg = rmtxgstatus(fd, MTS_ERREG); /* must be first */ if (geterrno() == 0) mtp->mt_xflags |= RMT_ERREG; mtp->mt_type = rmtxgstatus(fd, MTS_TYPE); if (geterrno() == 0) mtp->mt_xflags |= RMT_TYPE; mtp->mt_dsreg = rmtxgstatus(fd, MTS_DSREG); if (geterrno() == 0) mtp->mt_xflags |= RMT_DSREG; mtp->mt_resid = rmtxgstatus(fd, MTS_RESID); if (geterrno() == 0) mtp->mt_xflags |= RMT_RESID; mtp->mt_fileno = rmtxgstatus(fd, MTS_FILENO); if (geterrno() == 0) mtp->mt_xflags |= RMT_FILENO; mtp->mt_blkno = rmtxgstatus(fd, MTS_BLKNO); if (geterrno() == 0) mtp->mt_xflags |= RMT_BLKNO; mtp->mt_flags = rmtxgstatus(fd, MTS_FLAGS); if (geterrno() == 0) mtp->mt_xflags |= RMT_FLAGS; mtp->mt_bf = rmtxgstatus(fd, MTS_BF); if (geterrno() == 0) mtp->mt_xflags |= RMT_BF; if (mtp->mt_xflags == 0) return (-1); return (0); } LOCAL int rmt_v0_status(fd, mtp) int fd; struct mtget *mtp; { register int i; register char *cp; char c; int n; /* No newline */ if ((n = rmtcmd(fd, "status", "S")) < 0) return (-1); /* * From disassembling Solaris ufsdump, they seem to check * only if (n > sizeof (mts)). */ if (n != sizeof (struct mtget)) { rmt_errmsgno(EX_BAD, "rmtstatus: expected response size %d, got %d\n", (int)sizeof (struct mtget), n); rmt_errmsgno(EX_BAD, "This means the remote rmt daemon is not compatible.\n"); /* * XXX should we better abort here? */ } for (i = 0, cp = (char *)mtp; i < sizeof (struct mtget); i++) *cp++ = 0; for (i = 0, cp = (char *)mtp; i < n; i++) { /* * Make sure to read all bytes because we otherwise * would confuse the protocol. Do not copy more * than the size of our local struct mtget. */ if (rmtreadchar(fd, &c) != 1) return (rmtaborted(fd)); if (i < sizeof (struct mtget)) *cp++ = c; } /* * The GNU remote tape lib tries to swap the structure based on the * value of mt_type. While this makes sense for UNIX, it will not * work if one system is running Linux. The Linux mtget structure * is completely incompatible (mt_type is long instead of short). */ return (n); } EXPORT int rmtxstatus(fd, mtp) int fd; struct rmtget *mtp; { struct mtget mtget; if (rmtioctl(fd, RMTIVERSION, 0) == RMT_VERSION) return (rmt_v1_status(fd, mtp)); if (rmt_v0_status(fd, &mtget) < 0) return (-1); if (_mtg2rmtg(mtp, &mtget) < 0) return (-1); return (0); } EXPORT int rmtstatus(fd, mtp) int fd; struct mtget *mtp; { struct rmtget rmtget; int ret = -1; if (rmtioctl(fd, RMTIVERSION, 0) == RMT_VERSION) { ret = rmt_v1_status(fd, &rmtget); if (ret < 0) return (ret); } else { if (rmt_debug) rmt_errmsgno(EX_BAD, "Retrieving mt status from old server.\n"); return (rmt_v0_status(fd, mtp)); } _rmtg2mtg(mtp, &rmtget); return (ret); } LOCAL Llong rmtcmd(fd, name, cbuf) int fd; char *name; char *cbuf; { rmtsendcmd(fd, name, cbuf); return (rmtgetstatus(fd, name)); } LOCAL void rmtsendcmd(fd, name, cbuf) int fd; char *name; char *cbuf; { int buflen = strlen(cbuf); seterrno(0); if (_niwrite(fd, cbuf, buflen) != buflen) rmtaborted(fd); } #define READB_SIZE 128 LOCAL char readb[READB_SIZE]; LOCAL char *readbptr; LOCAL int readbcnt; LOCAL int rmtfillrdbuf(fd) int fd; { readbptr = readb; return (readbcnt = _niread(fd, readb, READB_SIZE)); } LOCAL int rmtreadchar(fd, cp) int fd; char *cp; { if (--readbcnt < 0) { if (rmtfillrdbuf(fd) <= 0) return (readbcnt); --readbcnt; } *cp = *readbptr++; return (1); } LOCAL int rmtreadbuf(fd, buf, count) register int fd; register char *buf; register int count; { register int amt = 0; register int cnt; if (readbcnt > 0) { cnt = readbcnt; if (cnt > count) cnt = count; movebytes(readbptr, buf, cnt); readbptr += cnt; readbcnt -= cnt; amt += cnt; } while (amt < count) { if ((cnt = _niread(fd, &buf[amt], count - amt)) <= 0) { return (rmtaborted(fd)); } amt += cnt; } return (amt); } LOCAL int rmtgetline(fd, line, count) int fd; char *line; int count; { register char *cp; for (cp = line; cp < &line[count]; cp++) { if (rmtreadchar(fd, cp) != 1) return (rmtaborted(fd)); if (*cp == '\n') { *cp = '\0'; return (cp - line); } } if (rmt_debug) rmt_errmsgno(EX_BAD, "Protocol error (in rmtgetline).\n"); return (rmtaborted(fd)); } LOCAL Llong rmtgetstatus(fd, name) int fd; char *name; { char cbuf[CMD_SIZE]; char code; Llong number; rmtgetline(fd, cbuf, sizeof (cbuf)); code = cbuf[0]; astoll(&cbuf[1], &number); if (code == 'E' || code == 'F') { rmtgetline(fd, cbuf, sizeof (cbuf)); if (code == 'F') /* should close file ??? */ rmtaborted(fd); if (rmt_debug) rmt_errmsgno(number, "Remote status(%s): %lld '%s'.\n", name, number, cbuf); seterrno(number); return ((Llong)-1); } if (code != 'A') { /* XXX Hier kommt evt Command not found ... */ if (rmt_debug) { rmt_errmsgno(EX_BAD, "Protocol error (got %s).\n", cbuf); } return (rmtaborted(fd)); } return (number); } LOCAL int rmtaborted(fd) int fd; { if (rmt_debug) rmt_errmsgno(EX_BAD, "Lost connection to remote host ??\n"); /* if fd >= 0 */ /* close file */ if (geterrno() == 0) { /* * BSD used EIO but EPIPE is better for something like * sdd -noerror */ seterrno(EPIPE); } /* * BSD uses exit(X_ABORT) == 3, we return(-1) and let the caller decide */ return (-1); } #else /* USE_REMOTE */ /* ARGSUSED */ EXPORT int rmtgetconn(host, trsize, excode) char *host; /* The host name to connect to */ int trsize; /* Max transfer size for SO_SNDBUF/SO_RCVBUF */ int excode; /* If != 0 use value to exit() in this func */ { rmt_errmsgno(EX_BAD, "Remote tape support not present.\n"); #ifdef ENOSYS seterrno(ENOSYS); #else seterrno(EINVAL); #endif return (-1); } /* ARGSUSED */ EXPORT int rmtopen(fd, fname, fmode) int fd; char *fname; int fmode; { #ifdef ENOSYS seterrno(ENOSYS); #else seterrno(EINVAL); #endif return (-1); } /* ARGSUSED */ EXPORT int rmtclose(fd) int fd; { #ifdef ENOSYS seterrno(ENOSYS); #else seterrno(EINVAL); #endif return (-1); } /* ARGSUSED */ EXPORT int rmtread(fd, buf, count) int fd; char *buf; int count; { #ifdef ENOSYS seterrno(ENOSYS); #else seterrno(EINVAL); #endif return (-1); } /* ARGSUSED */ EXPORT int rmtwrite(fd, buf, count) int fd; char *buf; int count; { #ifdef ENOSYS seterrno(ENOSYS); #else seterrno(EINVAL); #endif return (-1); } /* ARGSUSED */ EXPORT off_t rmtseek(fd, offset, whence) int fd; off_t offset; int whence; { #ifdef ENOSYS seterrno(ENOSYS); #else seterrno(EINVAL); #endif return (-1); } /* ARGSUSED */ EXPORT int rmtioctl(fd, cmd, count) int fd; int cmd; int count; { #ifdef ENOSYS seterrno(ENOSYS); #else seterrno(EINVAL); #endif return (-1); } /* ARGSUSED */ EXPORT int rmtxstatus(fd, mtp) int fd; struct rmtget *mtp; { #ifdef ENOSYS seterrno(ENOSYS); #else seterrno(EINVAL); #endif return (-1); } /* ARGSUSED */ EXPORT int rmtstatus(fd, mtp) int fd; struct mtget *mtp; { #ifdef ENOSYS seterrno(ENOSYS); #else seterrno(EINVAL); #endif return (-1); } #endif /* USE_REMOTE */ EXPORT void _rmtg2mtg(mtp, rmtp) struct mtget *mtp; struct rmtget *rmtp; { #ifdef HAVE_MTGET_TYPE if (rmtp->mt_xflags & RMT_TYPE) mtp->mt_type = rmtp->mt_type; else mtp->mt_type = 0; #endif #ifdef HAVE_MTGET_DSREG if (rmtp->mt_xflags & RMT_DSREG) mtp->mt_dsreg = rmtp->mt_dsreg; else mtp->mt_dsreg = 0; #endif #ifdef HAVE_MTGET_ERREG if (rmtp->mt_xflags & RMT_ERREG) mtp->mt_erreg = rmtp->mt_erreg; else mtp->mt_erreg = 0; #endif #ifdef HAVE_MTGET_RESID if (rmtp->mt_xflags & RMT_RESID) mtp->mt_resid = rmtp->mt_resid; else mtp->mt_resid = 0; #endif #ifdef HAVE_MTGET_FILENO if (rmtp->mt_xflags & RMT_FILENO) mtp->mt_fileno = rmtp->mt_fileno; else mtp->mt_fileno = -1; #endif #ifdef HAVE_MTGET_BLKNO if (rmtp->mt_xflags & RMT_BLKNO) mtp->mt_blkno = rmtp->mt_blkno; else mtp->mt_blkno = -1; #endif #ifdef HAVE_MTGET_FLAGS if (rmtp->mt_xflags & RMT_FLAGS) mtp->mt_flags = rmtp->mt_flags; else mtp->mt_flags = 0; #endif #ifdef HAVE_MTGET_BF if (rmtp->mt_xflags & RMT_BF) mtp->mt_bf = rmtp->mt_bf; else mtp->mt_bf = 0; #endif } EXPORT int _mtg2rmtg(rmtp, mtp) struct rmtget *rmtp; struct mtget *mtp; { rmtp->mt_xflags = 0; #ifdef HAVE_MTGET_TYPE rmtp->mt_xflags |= RMT_TYPE; rmtp->mt_type = mtp->mt_type; #else rmtp->mt_type = 0; #endif #ifdef HAVE_MTGET_DSREG rmtp->mt_xflags |= RMT_DSREG; rmtp->mt_dsreg = mtp->mt_dsreg; #else rmtp->mt_dsreg = 0; #endif #ifdef HAVE_MTGET_ERREG rmtp->mt_xflags |= RMT_ERREG; rmtp->mt_erreg = mtp->mt_erreg; #else rmtp->mt_erreg = 0; #endif #ifdef HAVE_MTGET_RESID rmtp->mt_xflags |= RMT_RESID; rmtp->mt_resid = mtp->mt_resid; #else rmtp->mt_resid = 0; #endif #ifdef HAVE_MTGET_FILENO rmtp->mt_xflags |= RMT_FILENO; rmtp->mt_fileno = mtp->mt_fileno; #else rmtp->mt_fileno = -1; #endif #ifdef HAVE_MTGET_BLKNO rmtp->mt_xflags |= RMT_BLKNO; rmtp->mt_blkno = mtp->mt_blkno; #else rmtp->mt_blkno = -1; #endif #ifdef HAVE_MTGET_FLAGS rmtp->mt_xflags |= RMT_FLAGS; rmtp->mt_flags = mtp->mt_flags; #else rmtp->mt_flags = 0; #endif #ifdef HAVE_MTGET_BF rmtp->mt_xflags |= RMT_BF; rmtp->mt_bf = mtp->mt_bf; #else rmtp->mt_bf = 0; #endif if (rmtp->mt_xflags == 0) return (-1); rmtp->mt_xflags |= RMT_COMPAT; return (0); } /*--------------------------------------------------------------------------*/ #ifdef USE_REMOTE #ifdef USE_RCMD_RSH /* * If we make a separate file for libschily, we would need these include files: * * socketpair(): sys/types.h + sys/socket.h * dup2(): unixstd.h (hat auch sys/types.h) * strrchr(): strdefs.h * * and make sure that we use sigset() instead of signal() if possible. */ #include LOCAL int _rcmdrsh(ahost, inport, locuser, remuser, cmd, rsh) char **ahost; int inport; /* port is ignored */ const char *locuser; const char *remuser; const char *cmd; const char *rsh; { struct passwd *pw; int pp[2]; int pid; if (rsh == 0) rsh = "rsh"; /* * Verify that 'locuser' is present on local host. */ if ((pw = getpwnam(locuser)) == NULL) { rmt_errmsgno(EX_BAD, "Unknown user: %s\n", locuser); return (-1); } /* XXX Check the existence for 'ahost' here? */ /* * rcmd(3) creates a single socket to be used for communication. * We need a bi-directional pipe to implement the same interface. * On newer OS that implement bi-directional we could use pipe(2) * but it makes no sense unless we find an OS that implements a * bi-directional pipe(2) but no socketpair(). */ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pp) == -1) { rmt_errmsgno(geterrno(), "Cannot create socketpair.\n"); return (-1); } pid = fork(); if (pid < 0) { return (-1); } else if (pid == 0) { const char *p; const char *av0; int xpid; (void) close(pp[0]); if (dup2(pp[1], 0) == -1 || /* Pipe becomes 'stdin' */ dup2(0, 1) == -1) { /* Pipe becomes 'stdout' */ rmt_errmsgno(geterrno(), "dup2 failed.\n"); _exit(EX_BAD); /* NOTREACHED */ } (void) close(pp[1]); /* We don't need this anymore*/ /* * Become 'locuser' to tell the rsh program the local user id. */ if (getuid() != pw->pw_uid && setuid(pw->pw_uid) == -1) { rmt_errmsgno(geterrno(), "setuid(%lld) failed.\n", (Llong)pw->pw_uid); _exit(EX_BAD); /* NOTREACHED */ } /* * Fork again to completely detach from parent * and avoid the need to wait(2). */ if ((xpid = fork()) == -1) { rmt_errmsgno(geterrno(), "rcmdsh: fork to lose parent failed.\n"); _exit(EX_BAD); /* NOTREACHED */ } if (xpid > 0) { _exit(0); /* NOTREACHED */ } /* * Always use remote shell programm (even for localhost). * The client command may call getpeername() for security * reasons and this would fail on a simple pipe. */ /* * By default, 'rsh' handles terminal created signals * but this is not what we like. * For this reason, we tell 'rsh' to ignore these signals. * Ignoring these signals is important to allow 'star' / 'sdd' * to e.g. implement SIGQUIT as signal to trigger intermediate * status printing. * * For now (late 2002), we know that the following programs * are broken and do not implement signal handling correctly: * * rsh on SunOS-5.0...SunOS-5.9 * ssh from ssh.com * ssh from openssh.org * * Sun already did accept a bug report for 'rsh'. For the ssh * commands we need to send out bug reports. Meanwhile it could * help to call setsid() if we are running under X so the ssh * X pop up for passwd reading will work. */ signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); #ifdef SIGTSTP signal(SIGTSTP, SIG_IGN); /* We would not be able to continue*/ #endif av0 = rsh; if ((p = strrchr(rsh, '/')) != NULL) av0 = ++p; execlp(rsh, av0, *ahost, "-l", remuser, cmd, NULL); rmt_errmsgno(geterrno(), "execlp '%s' failed.\n", rsh); _exit(EX_BAD); /* NOTREACHED */ } else { (void) close(pp[1]); /* * Wait for the intermediate child. * The real 'rsh' program is completely detached from us. */ wait(0); return (pp[0]); } return (-1); /* keep gcc happy */ } #endif /* USE_RCMD_RSH */ #endif /* USE_REMOTE */