/* * Copyright (c) 1983, 1990 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint char copyright[] = "@(#) Copyright (c) 1983, 1990 The Regents of the University of California.\n\ All rights reserved.\n"; #endif /* not lint */ #ifndef lint #ifdef ID static char sccsid[] = "@(#)rlogin.c 5.33 (Berkeley) 3/1/91"; #endif #endif /* not lint */ /* * $Source: /cvsup/minix/src/commands/simple/rlogin.c,v $ * $Header: mit/rlogin/RCS/rlogin.c,v 5.2 89/07/26 12:11:21 kfall * Exp Locker: kfall $ */ /* * rlogin - remote login */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef KERBEROS #include #include CREDENTIALS cred; Key_schedule schedule; int use_kerberos = 1, doencrypt; char dst_realm_buf[REALM_SZ], *dest_realm = NULL; extern char *krb_realmofhost(); #endif #ifndef TIOCPKT_WINDOW #define TIOCPKT_WINDOW 0x80 #endif /* concession to Sun */ #ifndef SIGUSR1 #define SIGUSR1 30 #endif extern int errno; int eight, litout, rem; int noescape; u_char escapechar = '~'; struct speed { speed_t speed; char *name; } speeds[] = { { B0, "0" }, { B50, "50" }, { B75, "75" }, { B110, "110" }, { B134, "134" }, { B150, "150" }, { B200, "200" }, { B300, "300" }, { B600, "600" }, { B1200, "1200" }, { B1800, "1800" }, { B2400, "2400" }, { B4800, "4800" }, { B9600, "9600" }, { B19200, "19200" }, { B38400, "38400" }, { B57600, "57600" }, { B115200, "115200" }, { -1, NULL }, }; #if __minix_vmd /* flow control variables */ int more2read_0; int inprogress_0; int more2write_1; int inprogress_1; int more2read_rem; int inprogress_rd_rem; int more2write_rem; int inprogress_wr_rem; /* write to remote */ size_t wr_rem_size; size_t wr_rem_offset; size_t extra_wr_rem_size; size_t extra_wr_rem_offset; char *extra_wr_rem; size_t extra_wr_rem_new_size; char *extra_wr_rem_new; #endif /* __minix_vmd */ struct winsize winsize; #define get_window_size(fd, wp) ioctl(fd, TIOCGWINSZ, wp) extern int main _ARGS(( int argc, char **argv )); static void usage _ARGS(( void )); static u_char getescape _ARGS(( char *p )); static char *speeds2str _ARGS(( speed_t speed )); static void lostpeer _ARGS(( int sig )); static void doit _ARGS(( void )); static void setsignal _ARGS(( int sig, void (*act)(int sig) )); static void msg _ARGS(( char *str )); static void done _ARGS(( int status )); #if !__minix_vmd static int reader _ARGS(( void )); #endif static void mode _ARGS(( int f )); #if __minix_vmd static void mark_async _ARGS(( int fd )); static void init_0 _ARGS(( void )); static void init_1 _ARGS(( void )); static void init_rd_rem _ARGS(( void )); static void init_wr_rem _ARGS(( void )); static void restart_0 _ARGS(( void )); static void restart_1 _ARGS(( void )); static void restart_rd_rem _ARGS(( void )); static void restart_wr_rem _ARGS(( void )); static void completed_0 _ARGS(( int result, int error )); static void completed_1 _ARGS(( int result, int error )); static void completed_rd_rem _ARGS(( int result, int error )); static void completed_wr_rem _ARGS(( int result, int error )); static void do_urg _ARGS(( int urg_byte )); #endif #if !__minix_vmd static void catch_child _ARGS(( int sig )); static void writer _ARGS(( void )); #endif static void echo _ARGS(( int c )); #if __minix_vmd static void finish _ARGS(( void )); static void sendwindow _ARGS(( void )); static void sigwinch _ARGS(( int sig )); static void subshell _ARGS(( void )); #endif int main(argc, argv) int argc; char **argv; { extern char *optarg; extern int optind; struct passwd *pw; struct servent *sp; struct termios ttyb; nwio_tcpopt_t tcpopt; int error; int argoff, ch, dflag, one, uid; char *host, *p, *user, term[1024]; argoff = dflag = 0; one = 1; host = user = NULL; if (p = rindex(argv[0], '/')) ++p; else p = argv[0]; if (strcmp(p, "rlogin")) host = p; /* handle "rlogin host flags" */ if (!host && argc > 2 && argv[1][0] != '-') { host = argv[1]; argoff = 1; } #ifdef KERBEROS #define OPTIONS "8EKLde:k:l:x" #else #define OPTIONS "8EKLde:l:" #endif while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != EOF) switch(ch) { case '8': eight = 1; break; case 'E': noescape = 1; break; case 'K': #ifdef KERBEROS use_kerberos = 0; #endif break; case 'L': litout = 1; break; case 'd': dflag = 1; break; case 'e': escapechar = getescape(optarg); break; #ifdef KERBEROS case 'k': dest_realm = dst_realm_buf; (void)strncpy(dest_realm, optarg, REALM_SZ); break; #endif case 'l': user = optarg; break; #ifdef CRYPT #ifdef KERBEROS case 'x': doencrypt = 1; des_set_key(cred.session, schedule); break; #endif #endif case '?': default: usage(); } optind += argoff; argc -= optind; argv += optind; /* if haven't gotten a host yet, do so */ if (!host && !(host = *argv++)) usage(); if (*argv) usage(); if (!(pw = getpwuid(uid = getuid()))) { (void)fprintf(stderr, "rlogin: unknown user id.\n"); exit(1); } if (!user) user = pw->pw_name; sp = NULL; #ifdef KERBEROS if (use_kerberos) { sp = getservbyname((doencrypt ? "eklogin" : "klogin"), "tcp"); if (sp == NULL) { use_kerberos = 0; warning("can't get entry for %s/tcp service", doencrypt ? "eklogin" : "klogin"); } } #endif if (sp == NULL) sp = getservbyname("login", "tcp"); if (sp == NULL) { (void)fprintf(stderr, "rlogin: login/tcp: unknown service.\n"); exit(1); } (void)strncpy(term, (p = getenv("TERM")) ? p : "network", sizeof(term)); term[sizeof(term)-1]= 0; if (tcgetattr(0, &ttyb) == 0) { (void)strcat(term, "/"); (void)strcat(term, speeds2str(cfgetospeed(&ttyb))); } (void)get_window_size(0, &winsize); (void)signal(SIGPIPE, lostpeer); #ifdef KERBEROS try_connect: if (use_kerberos) { rem = KSUCCESS; errno = 0; if (dest_realm == NULL) dest_realm = krb_realmofhost(host); #ifdef CRYPT if (doencrypt) rem = krcmd_mutual(&host, sp->s_port, user, term, 0, dest_realm, &cred, schedule); else #endif /* CRYPT */ rem = krcmd(&host, sp->s_port, user, term, 0, dest_realm); if (rem < 0) { use_kerberos = 0; sp = getservbyname("login", "tcp"); if (sp == NULL) { (void)fprintf(stderr, "rlogin: unknown service login/tcp.\n"); exit(1); } if (errno == ECONNREFUSED) warning("remote host doesn't support Kerberos"); if (errno == ENOENT) warning("can't provide Kerberos auth data"); goto try_connect; } } else { #ifdef CRYPT if (doencrypt) { (void)fprintf(stderr, "rlogin: the -x flag requires Kerberos authentication.\n"); exit(1); } #endif /* CRYPT */ rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0); } #else rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0); #endif /* KERBEROS */ if (rem < 0) exit(1); /* Enable BSD compatibility for urgent data. */ tcpopt.nwto_flags= NWTO_BSD_URG; error= ioctl(rem, NWIOSTCPOPT, &tcpopt); if (error == -1) { fprintf(stderr, "rlogin: NWIOSTCPOPT failed: %s\n", strerror(errno)); } (void)setuid(uid); doit(); /*NOTREACHED*/ } struct termios defattr, rawattr; #if __minix_vmd int mustsendwindow; #else int child; #endif static void doit() { struct termios sb; #if !__minix_vmd int r; #else asio_fd_set_t fd_set; struct fwait fw; int result; #endif (void)tcgetattr(0, &sb); defattr = sb; rawattr = sb; rawattr.c_iflag &= ~(ICRNL | IGNCR | INLCR | ISTRIP | IXOFF | IXON | PARMRK | IXANY); rawattr.c_oflag &= ~(OPOST); rawattr.c_lflag &= ~(ECHONL | ECHO | ICANON | IEXTEN | ISIG); (void)signal(SIGINT, SIG_IGN); setsignal(SIGHUP, exit); setsignal(SIGQUIT, exit); #if !__minix_vmd child = fork(); if (child == -1) { (void)fprintf(stderr, "rlogin: fork: %s.\n", strerror(errno)); done(1); } if (child == 0) { mode(1); r = reader(); if (r == 0) { msg("connection closed."); exit(0); } sleep(1); msg("\007connection closed."); exit(1); } (void)signal(SIGCHLD, catch_child); writer(); #else /* __minix_vmd */ mode(1); /* mark the file descriptors 0, 1, and rem as asynchronous. */ mark_async(0); mark_async(1); mark_async(rem); init_0(); init_1(); init_rd_rem(); init_wr_rem(); for (;;) { ASIO_FD_ZERO(&fd_set); fw.fw_flags= 0; fw.fw_bits= fd_set.afds_bits; fw.fw_maxfd= ASIO_FD_SETSIZE; if (more2read_0 && !inprogress_0) { restart_0(); fw.fw_flags |= FWF_NONBLOCK; } if (more2write_1 && !inprogress_1) { restart_1(); fw.fw_flags |= FWF_NONBLOCK; } if (more2read_rem && !inprogress_rd_rem) { restart_rd_rem(); fw.fw_flags |= FWF_NONBLOCK; } if (more2write_rem && !inprogress_wr_rem) { restart_wr_rem(); fw.fw_flags |= FWF_NONBLOCK; } if (more2read_0 && inprogress_0) ASIO_FD_SET(0, ASIO_READ, &fd_set); if (more2write_1 && inprogress_1) ASIO_FD_SET(1, ASIO_WRITE, &fd_set); if (more2read_rem && inprogress_rd_rem) ASIO_FD_SET(rem, ASIO_READ, &fd_set); if (more2write_rem && inprogress_wr_rem) ASIO_FD_SET(rem, ASIO_WRITE, &fd_set); for (;;) { result= fwait(&fw); if (result == -1 && (errno == EAGAIN || errno == EINTR)) { break; } if (result == -1) { fprintf(stderr, "fwait failed (%s)\n", strerror(errno)); exit(1); } assert(result == 0); #if 0 printf("fwait: fw_fw= %d, fw_operation= %d, fw_result= %d, fw.fw_errno= %d\n", fw.fw_fd, fw.fw_operation, fw.fw_result, fw.fw_errno); #endif if (fw.fw_fd == 0 && fw.fw_operation == ASIO_READ) { completed_0(fw.fw_result, fw.fw_errno); } else if (fw.fw_fd == 1 && fw.fw_operation == ASIO_WRITE) { completed_1(fw.fw_result, fw.fw_errno); } else if (fw.fw_fd == rem && fw.fw_operation == ASIO_READ) { completed_rd_rem(fw.fw_result, fw.fw_errno); } else if (fw.fw_fd == rem && fw.fw_operation == ASIO_WRITE) { completed_wr_rem(fw.fw_result, fw.fw_errno); } else { fprintf(stderr, "strange result from fwait: fd= %d, operation= %d\n", fw.fw_fd, fw.fw_operation); exit(1); } if (!(fw.fw_flags & FWF_MORE)) break; } if (mustsendwindow) { mustsendwindow= 0; sendwindow(); } } #endif /* __minix_vmd */ msg("connection closed."); done(0); } /* trap a signal, unless it is being ignored. */ static void setsignal(sig, act) int sig; void (*act) _ARGS(( int sig )); { if (signal(sig, act) == SIG_IGN) (void)signal(sig, SIG_IGN); } static void done(status) int status; { int w, wstatus; mode(0); #if !__minix_vmd if (child > 0) { /* make sure catch_child does not snap it up */ (void)signal(SIGCHLD, SIG_DFL); if (kill(child, SIGKILL) >= 0) while ((w = wait(&wstatus)) > 0 && w != child); } #endif exit(status); } int dosigwinch; #if !__minix void sigwinch(); #endif #if !__minix_vmd static void catch_child(sig) int sig; { int status; int pid; for (;;) { pid = waitpid(-1, &status, WNOHANG|WUNTRACED); if (pid == 0) return; /* if the child (reader) dies, just quit */ if (pid < 0 || pid == child && !WIFSTOPPED(status)) done(WTERMSIG(status) | WEXITSTATUS(status)); } /* NOTREACHED */ } #endif #if !__minix_vmd /* * writer: write to remote: 0 -> line. * ~. terminate * ~^Z suspend rlogin process. * ~ suspend rlogin process, but leave reader alone. */ static void writer() { register int bol, local, n; u_char ch; int c; bol = 1; /* beginning of line */ local = 0; for (;;) { n = read(STDIN_FILENO, &ch, 1); if (n <= 0) { if (n < 0 && errno == EINTR) continue; break; } c = ch; /* * If we're at the beginning of the line and recognize a * command character, then we echo locally. Otherwise, * characters are echo'd remotely. If the command character * is doubled, this acts as a force and local echo is * suppressed. */ if (bol) { bol = 0; if (!noescape && c == escapechar) { local = 1; continue; } } else if (local) { local = 0; if (c == '.' || c == defattr.c_cc[VEOF]) { echo(c); break; } #if !__minix if (c == defattr.c_cc[VSUSP]) { bol = 1; echo(c); stop(c); continue; } #endif if (c != escapechar) #ifdef CRYPT #ifdef KERBEROS if (doencrypt) (void)des_write(rem, &escapechar, 1); else #endif #endif (void)write(rem, &escapechar, 1); } ch = c; #ifdef CRYPT #ifdef KERBEROS if (doencrypt) { if (des_write(rem, &ch, 1) == 0) { msg("line gone"); break; } } else #endif #endif if (write(rem, &ch, 1) == 0) { msg("line gone"); break; } bol = c == defattr.c_cc[VKILL] || c == defattr.c_cc[VEOF] || c == defattr.c_cc[VINTR] || c == defattr.c_cc[VSUSP] || c == '\r' || c == '\n'; } } #endif #if !__minix_vmd static void echo(c) int c; { register char *p; char buf[8]; p = buf; c &= 0177; *p++ = escapechar; if (c < ' ') { *p++ = '^'; *p++ = c + '@'; } else if (c == 0177) { *p++ = '^'; *p++ = '?'; } else *p++ = c; *p++ = '\r'; *p++ = '\n'; (void)write(STDOUT_FILENO, buf, p - buf); } #endif #if !__minix stop(cmdc) char cmdc; { mode(0); (void)signal(SIGCHLD, SIG_IGN); (void)kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP); (void)signal(SIGCHLD, catch_child); mode(1); sigwinch(); /* check for size changes */ } #endif #if __minix_vmd #ifdef SIGWINCH static void sigwinch(sig) int sig; { struct winsize ws; #if __minix signal(SIGWINCH, sigwinch); #endif if (dosigwinch && get_window_size(0, &ws) == 0 && memcmp(&ws, &winsize, sizeof(ws))) { winsize = ws; mustsendwindow= 1; } } /* * Send the window size to the server via the magic escape */ static void sendwindow() { struct winsize *wp; char *obuf, *new_buf; new_buf= realloc(extra_wr_rem_new, extra_wr_rem_new_size+4+sizeof(*wp)); if (new_buf == 0) return; extra_wr_rem_new= new_buf; obuf= new_buf+extra_wr_rem_new_size; extra_wr_rem_new_size += 4+sizeof(*wp); more2read_0= 0; more2write_rem= 1; wp = (struct winsize *)(obuf+4); obuf[0] = 0377; obuf[1] = 0377; obuf[2] = 's'; obuf[3] = 's'; wp->ws_row = htons(winsize.ws_row); wp->ws_col = htons(winsize.ws_col); wp->ws_xpixel = htons(winsize.ws_xpixel); wp->ws_ypixel = htons(winsize.ws_ypixel); } #endif /* SIGWINCH */ #endif #if !__minix_vmd /* * reader: read from remote: line -> 1 */ #define READING 1 #define WRITING 2 int rcvcnt, rcvstate; char rcvbuf[8 * 1024]; static int reader() { int pid = -getpid(); int n, remaining; char *bufp = rcvbuf; for (;;) { while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) { rcvstate = WRITING; n = write(STDOUT_FILENO, bufp, remaining); if (n < 0) { if (errno != EINTR) return(-1); continue; } bufp += n; } bufp = rcvbuf; rcvcnt = 0; rcvstate = READING; #ifdef CRYPT #ifdef KERBEROS if (doencrypt) rcvcnt = des_read(rem, rcvbuf, sizeof(rcvbuf)); else #endif #endif rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf)); if (rcvcnt == 0) return (0); if (rcvcnt < 0) { if (errno == EINTR) continue; if (errno == EURG) { nwio_tcpopt_t tcpopt; #if DEBUG fprintf(stderr, "\n\rEURG\n\r"); #endif tcpopt.nwto_flags= NWTO_RCV_URG; if (ioctl(rem, NWIOSTCPOPT, &tcpopt) == -1) { fprintf(stderr, "rlogin: trouble with urgent data: %s\n", strerror(errno)); return(-1); } continue; } if (errno == ENOURG) { nwio_tcpopt_t tcpopt; #if DEBUG fprintf(stderr, "\n\rENOURG\n\r"); #endif tcpopt.nwto_flags= NWTO_RCV_NOTURG; if (ioctl(rem, NWIOSTCPOPT, &tcpopt) == -1) { fprintf(stderr, "rlogin: trouble with not-urgent data: %s\n", strerror(errno)); return(-1); } continue; } (void)fprintf(stderr, "rlogin: read: %s\n", strerror(errno)); return(-1); } } } #endif /* !__minix_vmd */ static void mode(f) int f; { struct termios *sb; switch(f) { case 0: sb= &defattr; break; case 1: sb= &rawattr; break; default: return; } (void)tcsetattr(0, TCSAFLUSH, sb); } static void lostpeer(sig) int sig; { (void)signal(SIGPIPE, SIG_IGN); msg("\007connection closed."); done(1); } static void msg(str) char *str; { (void)fprintf(stderr, "rlogin: %s\r\n", str); } #ifdef KERBEROS /* VARARGS */ warning(va_alist) va_dcl { va_list ap; char *fmt; (void)fprintf(stderr, "rlogin: warning, using standard rlogin: "); va_start(ap); fmt = va_arg(ap, char *); vfprintf(stderr, fmt, ap); va_end(ap); (void)fprintf(stderr, ".\n"); } #endif static void usage() { (void)fprintf(stderr, "Usage: rlogin [-%s]%s[-e char] [-l username] host\n", #ifdef KERBEROS #ifdef CRYPT "8ELx", " [-k realm] "); #else "8EL", " [-k realm] "); #endif #else "8EL", " "); #endif exit(1); } /* * The following routine provides compatibility (such as it is) between 4.2BSD * Suns and others. Suns have only a `ttysize', so we convert it to a winsize. */ #ifdef sun get_window_size(fd, wp) int fd; struct winsize *wp; { struct ttysize ts; int error; if ((error = ioctl(0, TIOCGSIZE, &ts)) != 0) return(error); wp->ws_row = ts.ts_lines; wp->ws_col = ts.ts_cols; wp->ws_xpixel = 0; wp->ws_ypixel = 0; return(0); } #endif static u_char getescape(p) register char *p; { long val; int len; if ((len = strlen(p)) == 1) /* use any single char, including '\' */ return((u_char)*p); /* otherwise, \nnn */ if (*p == '\\' && len >= 2 && len <= 4) { val = strtol(++p, (char **)NULL, 8); for (;;) { if (!*++p) return((u_char)val); if (*p < '0' || *p > '8') break; } } msg("illegal option value -- e"); usage(); /* NOTREACHED */ } static char * speeds2str(speed) speed_t speed; { int i; for (i= 0; speeds[i].name != NULL && speeds[i].speed != speed; i++) { if (speeds[i].speed == speed) return speeds[i].name; } return "unknown"; } #if __minix_vmd static void mark_async(fd) int fd; { int result; int v; result= fcntl(fd, F_GETFD); if (result == -1) { fprintf(stderr, "rlogin: mark_async: fcntl(%d, GETFD) failed (%s)\n", fd, strerror(errno)); exit(1); } v= result | FD_ASYNCHIO; result= fcntl(fd, F_SETFD, v); if (result == -1) { fprintf(stderr, "rlogin: mark_async: fcntl(%d, SETFD, %d) failed (%s)\n", fd, v, strerror(errno)); exit(1); } } #define RD_0_BUFSIZE 256 char rd_0_buf[RD_0_BUFSIZE]; size_t rd_0_offset; static void init_0() { more2read_0= 1; inprogress_0= 0; rd_0_offset= 0; } size_t wr_1_size; size_t wr_1_offset; char *urg_1; size_t urg_1_size; char *extra_1; size_t extra_1_size; size_t extra_1_offset; char *extra_1_new; size_t extra_1_new_size; #define MAX_EXTRA_1_NEW_SIZE (16*1024) static void init_1() { more2write_1= 0; inprogress_1= 0; wr_1_size= 0; wr_1_offset= 0; urg_1= NULL; urg_1_size= 0; extra_1= NULL; extra_1_size= 0; extra_1_offset= 0; extra_1_new= NULL; extra_1_new_size= 0; } #define RD_REM_BUFSIZE (8*1024) char rd_rem_buf[RD_REM_BUFSIZE]; size_t rd_rem_offset; int rd_rem_urg; static void init_rd_rem() { more2read_rem= 1; inprogress_rd_rem= 0; rd_rem_offset= 0; rd_rem_urg= 0; } static void init_wr_rem() { more2write_rem= 0; inprogress_wr_rem= 0; wr_rem_size= 0; wr_rem_offset= 0; extra_wr_rem_size= 0; extra_wr_rem_offset= 0; extra_wr_rem= NULL; extra_wr_rem_new_size= 0; extra_wr_rem_new= NULL; } static void restart_0() { size_t offset; int result, error; assert(!inprogress_0); rd_0_offset= 1; offset= 0; while (offset < RD_0_BUFSIZE) { result= read(0, rd_0_buf+rd_0_offset+offset, RD_0_BUFSIZE-rd_0_offset-offset); if (result > 0) { offset += result; assert(rd_0_offset+offset <= RD_0_BUFSIZE); continue; } error= errno; if (offset != 0) completed_0(offset, 0); rd_0_offset += offset; if (result == -1 && error == EINPROGRESS) { inprogress_0= 1; return; } completed_0(result, error); return; } completed_0(offset, 0); } static void restart_1() { size_t offset; int result, error; assert(!inprogress_1); while (extra_1 != NULL || extra_1_new != NULL) { if (extra_1 == NULL) { extra_1= extra_1_new; extra_1_new= NULL; extra_1_size= extra_1_new_size; extra_1_new_size= 0; extra_1_offset= 0; } offset= 0; #if DEBUG if (extra_1_size == 0) fprintf(stderr, "restart_1: extra_1_size= 0\n"); #endif while (offset < extra_1_size) { result= write(1, extra_1+extra_1_offset+offset, extra_1_size-offset); if (result > 0) { assert (result <= extra_1_size-offset); offset += result; continue; } error= errno; if (offset != 0) completed_1(offset, 0); if (result == -1 && errno == EINPROGRESS) { inprogress_1= 1; return; } completed_1(result, errno); return; } completed_1(offset, 0); } offset= 0; if (wr_1_size == 0) { more2write_1= 0; more2read_rem= 1; return; } while (offset < wr_1_size) { result= write(1, rd_rem_buf+wr_1_offset+offset, wr_1_size-offset); if (result > 0) { assert (result <= wr_1_size-offset); offset += result; continue; } error= errno; if (offset != 0) completed_1(offset, 0); if (result == -1 && errno == EINPROGRESS) { inprogress_1= 1; return; } completed_1(result, errno); return; } completed_1(offset, 0); } static void restart_rd_rem() { size_t offset; int result, error; assert(!inprogress_rd_rem); rd_rem_offset= 0; offset= 0; while (offset < RD_REM_BUFSIZE) { result= read(rem, rd_rem_buf+offset, RD_REM_BUFSIZE-offset); if (result > 0) { offset += result; assert(offset <= RD_REM_BUFSIZE); continue; } error= errno; if (offset != 0) completed_rd_rem(offset, 0); rd_rem_offset= offset; if (result == -1 && error == EINPROGRESS) { inprogress_rd_rem= 1; return; } completed_rd_rem(result, error); return; } completed_rd_rem(offset, 0); } static void restart_wr_rem() { size_t offset; int result, error; assert(!inprogress_wr_rem); if (extra_wr_rem_new != NULL && extra_wr_rem == NULL) { extra_wr_rem= extra_wr_rem_new; extra_wr_rem_size= extra_wr_rem_new_size; extra_wr_rem_offset= 0; extra_wr_rem_new= NULL; extra_wr_rem_new_size= 0; } if (extra_wr_rem != NULL) { offset= 0; while (offset < extra_wr_rem_size) { result= write(rem, extra_wr_rem+extra_wr_rem_offset+offset, extra_wr_rem_size-offset); if (result > 0) { assert (result <= extra_wr_rem_size-offset); offset += result; continue; } error= errno; if (offset != 0) completed_wr_rem(offset, 0); if (result == -1 && errno == EINPROGRESS) { inprogress_wr_rem= 1; return; } completed_wr_rem(result, errno); return; } completed_wr_rem(offset, 0); } if (wr_rem_size == 0) return; offset= 0; while (offset < wr_rem_size) { result= write(rem, rd_0_buf+wr_rem_offset+offset, wr_rem_size-offset); if (result > 0) { assert (result <= wr_rem_size-offset); offset += result; continue; } error= errno; if (offset != 0) completed_wr_rem(offset, 0); if (result == -1 && errno == EINPROGRESS) { inprogress_wr_rem= 1; return; } completed_wr_rem(result, errno); return; } completed_wr_rem(offset, 0); } static void completed_0(result, error) int result; int error; { static int bol= 0, local= 0; char *iptr, *optr; int i; u_char c; inprogress_0= 0; if (result > 0) { assert(rd_0_offset > 0); wr_rem_offset= 1; iptr= rd_0_buf+rd_0_offset; optr= rd_0_buf+wr_rem_offset; for (i= 0; i 0); wr_rem_offset--; optr[-1]= escapechar; } } } *(optr++)= c; bol= (c != _POSIX_VDISABLE) && (c == defattr.c_cc[VKILL] || c == defattr.c_cc[VEOF] || c == defattr.c_cc[VINTR] || c == defattr.c_cc[VSUSP] || c == '\r' || c == '\n'); } wr_rem_size += optr-rd_0_buf-wr_rem_offset; if (wr_rem_size != 0) { more2read_0= 0; more2write_rem= 1; } return; } else if (result < 0) { fprintf(stderr, "rlogin: %s\n", strerror(error)); } done(1); } static void completed_1(result, error) int result; int error; { inprogress_1= 0; if (result > 0) { if (extra_1 != NULL) { assert (result <= extra_1_size); extra_1_size -= result; extra_1_offset += result; if (extra_1_size == 0) { more2write_1= 0; more2read_rem= 1; free(extra_1); extra_1= NULL; } return; } assert (result <= wr_1_size); wr_1_size -= result; wr_1_offset += result; if (wr_1_size == 0) { more2write_1= 0; more2read_rem= 1; } return; } else if (result < 0) { fprintf(stderr, "rlogin: %s\n", strerror(error)); } done(1); } static void completed_rd_rem(result, error) int result; int error; { nwio_tcpopt_t tcpopt; char *new_buf; size_t keep_size; u_char urg_byte; int i; inprogress_rd_rem= 0; if (result > 0) { if (rd_rem_urg) { #if DEBUG fprintf(stderr, "\n\r%d urg bytes\n\r", result); #endif if (urg_1_size > MAX_EXTRA_1_NEW_SIZE) { keep_size= MAX_EXTRA_1_NEW_SIZE/2; memmove(urg_1, urg_1+urg_1_size-keep_size, keep_size); urg_1_size= keep_size; } new_buf= realloc(urg_1, urg_1_size+result); if (new_buf == NULL) { fprintf(stderr, "rlogin: warning realloc %d failed\n", urg_1_size+result); return; } memcpy(new_buf+urg_1_size, rd_rem_buf+rd_rem_offset, result); urg_1= new_buf; urg_1_size += result; return; } more2read_rem= 0; more2write_1= 1; wr_1_size= result; wr_1_offset= rd_rem_offset; return; } if (result == -1 && error == EURG) { #if DEBUG fprintf(stderr, "\n\rEURG\n\r"); #endif rd_rem_urg= 1; tcpopt.nwto_flags= NWTO_RCV_URG; result= ioctl(rem, NWIOSTCPOPT, &tcpopt); if (result == -1) { fprintf(stderr, "rlogin: NWIOSTCPOPT on %d failed (%s)\n", rem, strerror(errno)); exit(1); } return; } if (result == -1 && error == ENOURG) { #if DEBUG fprintf(stderr, "\n\rENOURG\n\r"); #endif rd_rem_urg= 0; tcpopt.nwto_flags= NWTO_RCV_NOTURG; result= ioctl(rem, NWIOSTCPOPT, &tcpopt); if (result == -1) { fprintf(stderr, "rlogin: NWIOSTCPOPT on %d failed (%s)\n", rem, strerror(errno)); exit(1); } if (urg_1_size != 0) { urg_byte= urg_1[urg_1_size-1]; urg_1_size--; do_urg(urg_byte); if (urg_1_size == 0) return; if (extra_1_new_size + urg_1_size > MAX_EXTRA_1_NEW_SIZE) { extra_1_new_size= 0; free(extra_1_new); extra_1_new= NULL; } if (extra_1_new_size != 0) { new_buf= realloc(extra_1_new, extra_1_new_size+urg_1_size); if (new_buf == 0) { extra_1_new_size= 0; free(extra_1_new); extra_1_new= NULL; } else { extra_1_new= new_buf; memcpy(extra_1_new+extra_1_new_size, urg_1, urg_1_size); extra_1_new_size += urg_1_size; urg_1_size= 0; free(urg_1); urg_1= NULL; } } if (extra_1_new_size == 0) { extra_1_new_size= urg_1_size; extra_1_new= urg_1; urg_1_size= 0; urg_1= NULL; } more2read_rem= 0; more2write_1= 1; } return; } if (result == -1 && error == EINTR) { /* Never mind. */ return; } if (result == 0) { msg("connection closed."); done(0); } if (result < 0) { fprintf(stderr, "rlogin: %s\n", strerror(error)); } done(1); } static void completed_wr_rem(result, error) int result; int error; { inprogress_wr_rem= 0; if (result > 0) { if (extra_wr_rem != NULL) { assert (result <= extra_wr_rem_size); extra_wr_rem_size -= result; extra_wr_rem_offset += result; if (extra_wr_rem_size == 0) { free(extra_wr_rem); extra_wr_rem= NULL; if (wr_rem_size == 0) { more2write_rem= 0; more2read_0= 1; } } return; } assert (result <= wr_rem_size); wr_rem_size -= result; wr_rem_offset += result; if (wr_rem_size == 0) { more2write_rem= 0; more2read_0= 1; } return; } if (result < 0) { fprintf(stderr, "rlogin: %s\n", strerror(error)); } done(1); } static void do_urg(urg_byte) int urg_byte; { #if DEBUG fprintf(stderr, "rlogin: warning got urg_byte 0x%x\r\n", urg_byte); #endif if (urg_byte & TIOCPKT_WINDOW) { if (dosigwinch == 0) { sendwindow(); signal(SIGWINCH, sigwinch); } dosigwinch= 1; } } static void echo(c) int c; { u_char c1; char *new_buf; new_buf= realloc(extra_1_new, extra_1_new_size+6); if (new_buf == NULL) return; extra_1_new= new_buf; new_buf= extra_1_new+extra_1_new_size; c1= escapechar; if (c1 < ' ') { *new_buf++= '^'; *new_buf++= c1 + '@'; } else if (c1 == 0x7f) { *new_buf++= '^'; *new_buf++= '?'; } else *new_buf++= c1; if (c < ' ') { *new_buf++= '^'; *new_buf++= c + '@'; } else if (c == 0x7f) { *new_buf++= '^'; *new_buf++= '?'; } else *new_buf++= c; *new_buf++= '\r'; *new_buf++= '\n'; extra_1_new_size= new_buf-extra_1_new; more2write_1= 1; } static void finish() { done(0); } static char cmdbuf[256]; static void subshell() { /* Start a subshell. Based on the first character of the command, * the tcp connection will be present at fd 3 ('+'), or at * fd 0 and fd 1 ('=') */ int r, pid, stat, len; char *shell, *cmd; /* cancel the reads and writes that are in progress. */ if (inprogress_0) { r= fcancel(0, ASIO_READ); if (r != 0) abort(); } if (inprogress_1) { r= fcancel(1, ASIO_WRITE); if (r != 0) abort(); } if (inprogress_rd_rem) { r= fcancel(rem, ASIO_READ); if (r != 0) abort(); } if (inprogress_wr_rem) { r= fcancel(rem, ASIO_WRITE); if (r != 0) abort(); } mode(0); pid= fork(); if (pid == -1) abort(); if (pid != 0) { r= waitpid(pid, &stat, 0); if (r != pid) abort(); #if DEBUG fprintf(stderr, "stat: 0x%x\n", stat); #endif mode(1); return; } (void)signal(SIGINT, SIG_DFL); shell= getenv("SHELL"); if (shell == NULL) shell= "/bin/sh"; printf("~!\ncommand [%s]: ", shell); cmd= fgets(cmdbuf, sizeof(cmdbuf), stdin); if (cmd == NULL) exit(0); #if DEBUG printf("got command '%s'\n", cmd); #endif /* Strip the trailing newline */ len= strlen(cmd); if (len > 0 && cmd[len-1] == '\n') cmd[len-1]= '\0'; else printf("\n"); /* Skip leading white space */ while (*cmd != '\0' && isspace(*cmd)) cmd++; if (*cmd == '+') { if (rem != 3) { dup2(rem, 3); close(rem); } cmd++; } else if (*cmd == '=') { dup2(rem, 0); dup2(rem, 1); close(rem); cmd++; } else close(rem); if (*cmd == '\0') { r= execl(shell, shell, NULL); } else { r= execl("/bin/sh", "sh", "-c", cmd, NULL); } printf("exec failed: %d, %d\n", r, errno); exit(0); } #endif /* __minix_vmd */