/*
- * Copyright (c) 1983 Regents of the University of California.
- * All rights reserved. The Berkeley software License Agreement
- * specifies the terms and conditions for redistribution.
+ * Copyright (c) 1983 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef lint
char copyright[] =
-"@(#) Copyright (c) 1983 Regents of the University of California.\n\
+"@(#) Copyright (c) 1983 The Regents of the University of California.\n\
All rights reserved.\n";
-#endif not lint
+#endif /* not lint */
#ifndef lint
-static char sccsid[] = "@(#)rlogin.c 5.3 (Berkeley) %G%";
-#endif not lint
+static char sccsid[] = "@(#)rlogin.c 5.12 (Berkeley) %G%";
+#endif /* not lint */
/*
* rlogin - remote login
*/
-#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/file.h>
#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/resource.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <errno.h>
#include <pwd.h>
#include <signal.h>
-#include <netdb.h>
#include <setjmp.h>
+#include <netdb.h>
+
+# ifndef TIOCPKT_WINDOW
+# define TIOCPKT_WINDOW 0x80
+# endif TIOCPKT_WINDOW
-char *index(), *rindex(), *malloc(), *getenv();
+/* concession to sun */
+# ifndef SIGUSR1
+# define SIGUSR1 30
+# endif SIGUSR1
+
+char *index(), *rindex(), *malloc(), *getenv(), *strcat(), *strcpy();
struct passwd *getpwuid();
char *name;
int rem;
char term[256] = "network";
extern int errno;
int lostpeer();
-int nosigwin;
-jmp_buf winsizechanged;
+int dosigwinch = 0;
+#ifndef sigmask
+#define sigmask(m) (1 << ((m)-1))
+#endif
+#ifdef sun
+struct winsize {
+ unsigned short ws_row, ws_col;
+ unsigned short ws_xpixel, ws_ypixel;
+};
+#endif sun
struct winsize winsize;
-int sigwinch();
+int sigwinch(), oob();
+
+/*
+ * 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
+int
+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);
+}
+#else sun
+#define get_window_size(fd, wp) ioctl(fd, TIOCGWINSZ, wp)
+#endif sun
main(argc, argv)
int argc;
struct sgttyb ttyb;
struct passwd *pwd;
struct servent *sp;
- int uid, options = 0;
+ int uid, options = 0, oldmask;
int on = 1;
host = rindex(argv[0], '/');
argv++, argc--;
goto another;
}
- if (argc > 0 && !strcmp(*argv, "-w")) {
- nosigwin++;
- argv++, argc--;
- goto another;
- }
if (host == 0)
goto usage;
if (argc > 0)
}
cp = getenv("TERM");
if (cp)
- strcpy(term, cp);
+ (void) strcpy(term, cp);
if (ioctl(0, TIOCGETP, &ttyb) == 0) {
- strcat(term, "/");
- strcat(term, speeds[ttyb.sg_ospeed]);
- }
- if (!nosigwin && ioctl(0, TIOCGWINSZ, &winsize) == 0) {
- cp = index(term, '\0');
- sprintf(cp, "/%u,%u,%u,%u", winsize.ws_row, winsize.ws_col,
- winsize.ws_xpixel, winsize.ws_ypixel);
+ (void) strcat(term, "/");
+ (void) strcat(term, speeds[ttyb.sg_ospeed]);
}
- signal(SIGPIPE, lostpeer);
+ (void) get_window_size(0, &winsize);
+ (void) signal(SIGPIPE, lostpeer);
+ /* will use SIGUSR1 for window size hack, so hold it off */
+ oldmask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1));
rem = rcmd(&host, sp->s_port, pwd->pw_name,
name ? name : pwd->pw_name, term, 0);
if (rem < 0)
perror("rlogin: setuid");
exit(1);
}
- doit();
+ doit(oldmask);
/*NOTREACHED*/
usage:
fprintf(stderr,
- "usage: rlogin host [ -ex ] [ -l username ] [ -8 ] [ -L ] [ -w ]\n");
+ "usage: rlogin host [ -ex ] [ -l username ] [ -8 ] [ -L ]\n");
exit(1);
}
int child;
int catchild();
+int copytochild(), writeroob();
int defflags, tabflag;
int deflflags;
struct tchars notc = { -1, -1, -1, -1, -1, -1 };
struct ltchars noltc = { -1, -1, -1, -1, -1, -1 };
-doit()
+doit(oldmask)
{
int exit();
struct sgttyb sb;
- ioctl(0, TIOCGETP, (char *)&sb);
+ (void) ioctl(0, TIOCGETP, (char *)&sb);
defflags = sb.sg_flags;
tabflag = defflags & TBDELAY;
defflags &= ECHO | CRMOD;
deferase = sb.sg_erase;
defkill = sb.sg_kill;
- ioctl(0, TIOCLGET, (char *)&deflflags);
- ioctl(0, TIOCGETC, (char *)&deftc);
+ (void) ioctl(0, TIOCLGET, (char *)&deflflags);
+ (void) ioctl(0, TIOCGETC, (char *)&deftc);
notc.t_startc = deftc.t_startc;
notc.t_stopc = deftc.t_stopc;
- ioctl(0, TIOCGLTC, (char *)&defltc);
- signal(SIGINT, exit);
- signal(SIGHUP, exit);
- signal(SIGQUIT, exit);
+ (void) ioctl(0, TIOCGLTC, (char *)&defltc);
+ (void) signal(SIGINT, SIG_IGN);
+ setsignal(SIGHUP, exit);
+ setsignal(SIGQUIT, exit);
child = fork();
if (child == -1) {
perror("rlogin: fork");
- done();
+ done(1);
}
- signal(SIGINT, SIG_IGN);
- mode(1);
if (child == 0) {
- reader();
+ mode(1);
+ if (reader(oldmask) == 0) {
+ prf("Connection closed.");
+ exit(0);
+ }
sleep(1);
prf("\007Connection closed.");
exit(3);
}
- signal(SIGCHLD, catchild);
- if (!nosigwin)
- signal(SIGWINCH, sigwinch);
+
+ /*
+ * We may still own the socket, and may have a pending SIGURG
+ * (or might receive one soon) that we really want to send to
+ * the reader. Set a trap that simply copies such signals to
+ * the child.
+ */
+ (void) signal(SIGURG, copytochild);
+ (void) signal(SIGUSR1, writeroob);
+ (void) sigsetmask(oldmask);
+ (void) signal(SIGCHLD, catchild);
writer();
prf("Closed connection.");
- done();
+ done(0);
}
-done()
+/*
+ * Trap a signal, unless it is being ignored.
+ */
+setsignal(sig, act)
+ int sig, (*act)();
{
+ int omask = sigblock(sigmask(sig));
+
+ if (signal(sig, act) == SIG_IGN)
+ (void) signal(sig, SIG_IGN);
+ (void) sigsetmask(omask);
+}
+
+done(status)
+ int status;
+{
+ int w;
mode(0);
- if (child > 0 && kill(child, SIGKILL) >= 0)
- wait((int *)0);
- exit(0);
+ if (child > 0) {
+ /* make sure catchild does not snap it up */
+ (void) signal(SIGCHLD, SIG_DFL);
+ if (kill(child, SIGKILL) >= 0)
+ while ((w = wait((union wait *)0)) > 0 && w != child)
+ /*void*/;
+ }
+ exit(status);
+}
+
+/*
+ * Copy SIGURGs to the child process.
+ */
+copytochild()
+{
+
+ (void) kill(child, SIGURG);
+}
+
+/*
+ * This is called when the reader process gets the out-of-band (urgent)
+ * request to turn on the window-changing protocol.
+ */
+writeroob()
+{
+
+ if (dosigwinch == 0) {
+ sendwindow();
+ (void) signal(SIGWINCH, sigwinch);
+ }
+ dosigwinch = 1;
}
catchild()
int pid;
again:
- pid = wait3(&status, WNOHANG|WUNTRACED, 0);
+ pid = wait3(&status, WNOHANG|WUNTRACED, (struct rusage *)0);
if (pid == 0)
return;
/*
* if the child (reader) dies, just quit
*/
if (pid < 0 || pid == child && !WIFSTOPPED(status))
- done();
+ done((int)(status.w_termsig | status.w_retcode));
goto again;
}
register bol = 1; /* beginning of line */
register local = 0;
- /*
- * Handle SIGWINCH's with in-band signaling of new
- * window size. It seems reasonable that we flush
- * pending input and not force out of band signal
- * as this most likely will occur from an input device
- * other than the keyboard (e.g. a mouse).
- *
- * The hack of using 0377 to signal an in-band signal
- * is pretty bad, but otherwise we'd be forced to
- * either get complicated (use MSG_OOB) or go to a
- * serious (telnet-style) protocol.
- */
- if (setjmp(winsizechanged)) {
- char obuf[4 + sizeof (struct winsize)];
- struct winsize *wp = (struct winsize *)(obuf+4);
-
- obuf[0] = 0377; /* XXX */
- obuf[1] = 0377; /* XXX */
- obuf[2] = 's'; /* XXX */
- obuf[3] = 's'; /* XXX */
- 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);
- (void) write(rem, obuf, 4+sizeof (*wp));
- }
-
for (;;) {
n = read(0, &c, 1);
if (n <= 0) {
continue;
break;
}
- if (!eight)
- c &= 0177;
/*
* If we're at the beginning of the line
* and recognize a command character, then
continue;
}
if (c != cmdchar)
- write(rem, &cmdchar, 1);
+ (void) write(rem, &cmdchar, 1);
}
if (write(rem, &c, 1) == 0) {
prf("line gone");
break;
}
bol = c == defkill || c == deftc.t_eofc ||
+ c == deftc.t_intrc || c == defltc.t_suspc ||
c == '\r' || c == '\n';
}
}
*p++ = c;
*p++ = '\r';
*p++ = '\n';
- write(1, buf, p - buf);
+ (void) write(1, buf, p - buf);
}
stop(cmdc)
char cmdc;
{
- struct winsize ws;
-
mode(0);
- signal(SIGCHLD, SIG_IGN);
- kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP);
- signal(SIGCHLD, catchild);
+ (void) signal(SIGCHLD, SIG_IGN);
+ (void) kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP);
+ (void) signal(SIGCHLD, catchild);
mode(1);
sigwinch(); /* check for size changes */
}
{
struct winsize ws;
- if (!nosigwin && ioctl(0, TIOCGWINSZ, &ws) == 0 &&
+ if (dosigwinch && get_window_size(0, &ws) == 0 &&
bcmp(&ws, &winsize, sizeof (ws))) {
winsize = ws;
- longjmp(winsizechanged, 1);
+ sendwindow();
}
}
+/*
+ * Send the window size to the server via the magic escape
+ */
+sendwindow()
+{
+ char obuf[4 + sizeof (struct winsize)];
+ struct winsize *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);
+ (void) write(rem, obuf, sizeof(obuf));
+}
+
+/*
+ * reader: read from remote: line -> 1
+ */
+#define READING 1
+#define WRITING 2
+
+char rcvbuf[8 * 1024];
+int rcvcnt;
+int rcvstate;
+int ppid;
+jmp_buf rcvtop;
+
oob()
{
- int out = 1+1, atmark;
+ int out = FWRITE, atmark, n;
+ int rcvd = 0;
char waste[BUFSIZ], mark;
+ struct sgttyb sb;
- ioctl(1, TIOCFLUSH, (char *)&out);
- for (;;) {
- if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
- perror("ioctl");
- break;
- }
- if (atmark)
- break;
- if (read(rem, waste, sizeof (waste)) <= 0)
- break;
+ while (recv(rem, &mark, 1, MSG_OOB) < 0)
+ switch (errno) {
+
+ case EWOULDBLOCK:
+ /*
+ * Urgent data not here yet.
+ * It may not be possible to send it yet
+ * if we are blocked for output
+ * and our input buffer is full.
+ */
+ if (rcvcnt < sizeof(rcvbuf)) {
+ n = read(rem, rcvbuf + rcvcnt,
+ sizeof(rcvbuf) - rcvcnt);
+ if (n <= 0)
+ return;
+ rcvd += n;
+ } else {
+ n = read(rem, waste, sizeof(waste));
+ if (n <= 0)
+ return;
+ }
+ continue;
+
+ default:
+ return;
}
- recv(rem, &mark, 1, MSG_OOB);
- if (mark & TIOCPKT_NOSTOP) {
+ if (mark & TIOCPKT_WINDOW) {
+ /*
+ * Let server know about window size changes
+ */
+ (void) kill(ppid, SIGUSR1);
+ }
+ if (!eight && (mark & TIOCPKT_NOSTOP)) {
+ (void) ioctl(0, TIOCGETP, (char *)&sb);
+ sb.sg_flags &= ~CBREAK;
+ sb.sg_flags |= RAW;
+ (void) ioctl(0, TIOCSETN, (char *)&sb);
notc.t_stopc = -1;
notc.t_startc = -1;
- ioctl(0, TIOCSETC, (char *)¬c);
+ (void) ioctl(0, TIOCSETC, (char *)¬c);
}
- if (mark & TIOCPKT_DOSTOP) {
+ if (!eight && (mark & TIOCPKT_DOSTOP)) {
+ (void) ioctl(0, TIOCGETP, (char *)&sb);
+ sb.sg_flags &= ~RAW;
+ sb.sg_flags |= CBREAK;
+ (void) ioctl(0, TIOCSETN, (char *)&sb);
notc.t_stopc = deftc.t_stopc;
notc.t_startc = deftc.t_startc;
- ioctl(0, TIOCSETC, (char *)¬c);
+ (void) ioctl(0, TIOCSETC, (char *)¬c);
}
+ if (mark & TIOCPKT_FLUSHWRITE) {
+ (void) ioctl(1, TIOCFLUSH, (char *)&out);
+ for (;;) {
+ if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
+ perror("ioctl");
+ break;
+ }
+ if (atmark)
+ break;
+ n = read(rem, waste, sizeof (waste));
+ if (n <= 0)
+ break;
+ }
+ /*
+ * Don't want any pending data to be output,
+ * so clear the recv buffer.
+ * If we were hanging on a write when interrupted,
+ * don't want it to restart. If we were reading,
+ * restart anyway.
+ */
+ rcvcnt = 0;
+ longjmp(rcvtop, 1);
+ }
+
+ /*
+ * oob does not do FLUSHREAD (alas!)
+ */
+
+ /*
+ * If we filled the receive buffer while a read was pending,
+ * longjmp to the top to restart appropriately. Don't abort
+ * a pending write, however, or we won't know how much was written.
+ */
+ if (rcvd && rcvstate == READING)
+ longjmp(rcvtop, 1);
}
/*
* reader: read from remote: line -> 1
*/
-reader()
+reader(oldmask)
+ int oldmask;
{
- char rb[BUFSIZ];
- register int cnt;
-
- signal(SIGURG, oob);
- signal(SIGTTOU, SIG_IGN);
- { int pid = -getpid();
- ioctl(rem, SIOCSPGRP, (char *)&pid); }
+#if !defined(BSD) || BSD < 43
+ int pid = -getpid();
+#else
+ int pid = getpid();
+#endif
+ int n, remaining;
+ char *bufp = rcvbuf;
+
+ (void) signal(SIGTTOU, SIG_IGN);
+ (void) signal(SIGURG, oob);
+ ppid = getppid();
+ (void) fcntl(rem, F_SETOWN, pid);
+ (void) setjmp(rcvtop);
+ (void) sigsetmask(oldmask);
for (;;) {
- cnt = read(rem, rb, sizeof (rb));
- if (cnt == 0)
- break;
- if (cnt < 0) {
+ while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
+ rcvstate = WRITING;
+ n = write(1, bufp, remaining);
+ if (n < 0) {
+ if (errno != EINTR)
+ return (-1);
+ continue;
+ }
+ bufp += n;
+ }
+ bufp = rcvbuf;
+ rcvcnt = 0;
+ rcvstate = READING;
+ rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf));
+ if (rcvcnt == 0)
+ return (0);
+ if (rcvcnt < 0) {
if (errno == EINTR)
continue;
- break;
+ perror("read");
+ return (-1);
}
- write(1, rb, cnt);
}
}
struct sgttyb sb;
int lflags;
- ioctl(0, TIOCGETP, (char *)&sb);
- ioctl(0, TIOCLGET, (char *)&lflags);
+ (void) ioctl(0, TIOCGETP, (char *)&sb);
+ (void) ioctl(0, TIOCLGET, (char *)&lflags);
switch (f) {
case 0:
default:
return;
}
- ioctl(0, TIOCSLTC, (char *)ltc);
- ioctl(0, TIOCSETC, (char *)tc);
- ioctl(0, TIOCSETN, (char *)&sb);
- ioctl(0, TIOCLSET, (char *)&lflags);
+ (void) ioctl(0, TIOCSLTC, (char *)ltc);
+ (void) ioctl(0, TIOCSETC, (char *)tc);
+ (void) ioctl(0, TIOCSETN, (char *)&sb);
+ (void) ioctl(0, TIOCLSET, (char *)&lflags);
}
/*VARARGS*/
-prf(f, a1, a2, a3)
+prf(f, a1, a2, a3, a4, a5)
char *f;
{
- fprintf(stderr, f, a1, a2, a3);
+
+ fprintf(stderr, f, a1, a2, a3, a4, a5);
fprintf(stderr, CRLF);
}
lostpeer()
{
- signal(SIGPIPE, SIG_IGN);
+
+ (void) signal(SIGPIPE, SIG_IGN);
prf("\007Connection closed.");
- done();
+ done(1);
}