mv more tahoe stuff to machdep; clk_enable was dup;
[unix-history] / usr / src / usr.bin / rlogin / rlogin.c
CommitLineData
f42904bc
DF
1/*
2 * Copyright (c) 1983 Regents of the University of California.
3 * All rights reserved. The Berkeley software License Agreement
4 * specifies the terms and conditions for redistribution.
5 */
6
7#ifndef lint
8char copyright[] =
9"@(#) Copyright (c) 1983 Regents of the University of California.\n\
10 All rights reserved.\n";
11#endif not lint
12
5567b76f 13#ifndef lint
ba29c5a2 14static char sccsid[] = "@(#)rlogin.c 5.12 (Berkeley) %G%";
f42904bc 15#endif not lint
5567b76f 16
4ca10280
SL
17/*
18 * rlogin - remote login
19 */
545e8847 20#include <sys/param.h>
c3140eff 21#include <sys/errno.h>
da19e1ef 22#include <sys/file.h>
5567b76f 23#include <sys/socket.h>
a9b12987
KM
24#include <sys/time.h>
25#include <sys/resource.h>
840fc587 26#include <sys/wait.h>
86a16a64 27
c6c678f1 28#include <netinet/in.h>
86a16a64
SL
29
30#include <stdio.h>
31#include <sgtty.h>
5567b76f
BJ
32#include <errno.h>
33#include <pwd.h>
86a16a64 34#include <signal.h>
c3140eff 35#include <setjmp.h>
86a16a64 36#include <netdb.h>
e86d8e67
KM
37
38# ifndef TIOCPKT_WINDOW
39# define TIOCPKT_WINDOW 0x80
40# endif TIOCPKT_WINDOW
5567b76f 41
a9b12987
KM
42/* concession to sun */
43# ifndef SIGUSR1
44# define SIGUSR1 30
45# endif SIGUSR1
46
47char *index(), *rindex(), *malloc(), *getenv(), *strcat(), *strcpy();
5567b76f 48struct passwd *getpwuid();
86a16a64 49char *name;
5567b76f
BJ
50int rem;
51char cmdchar = '~';
5567b76f 52int eight;
39a44641 53int litout;
5567b76f
BJ
54char *speeds[] =
55 { "0", "50", "75", "110", "134", "150", "200", "300",
56 "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400" };
119312d1 57char term[256] = "network";
86a16a64
SL
58extern int errno;
59int lostpeer();
e86d8e67 60int dosigwinch = 0;
545e8847
MK
61#ifndef sigmask
62#define sigmask(m) (1 << ((m)-1))
63#endif
64#ifdef sun
545e8847
MK
65struct winsize {
66 unsigned short ws_row, ws_col;
67 unsigned short ws_xpixel, ws_ypixel;
68};
545e8847 69#endif sun
a9b12987 70struct winsize winsize;
e86d8e67 71int sigwinch(), oob();
86a16a64 72
a9b12987
KM
73/*
74 * The following routine provides compatibility (such as it is)
75 * between 4.2BSD Suns and others. Suns have only a `ttysize',
76 * so we convert it to a winsize.
77 */
78#ifdef sun
79int
80get_window_size(fd, wp)
81 int fd;
82 struct winsize *wp;
83{
84 struct ttysize ts;
85 int error;
86
87 if ((error = ioctl(0, TIOCGSIZE, &ts)) != 0)
88 return (error);
89 wp->ws_row = ts.ts_lines;
90 wp->ws_col = ts.ts_cols;
91 wp->ws_xpixel = 0;
92 wp->ws_ypixel = 0;
93 return (0);
94}
95#else sun
96#define get_window_size(fd, wp) ioctl(fd, TIOCGWINSZ, wp)
97#endif sun
98
5567b76f
BJ
99main(argc, argv)
100 int argc;
101 char **argv;
102{
86a16a64 103 char *host, *cp;
5567b76f
BJ
104 struct sgttyb ttyb;
105 struct passwd *pwd;
86a16a64 106 struct servent *sp;
e86d8e67 107 int uid, options = 0, oldmask;
0975b26d 108 int on = 1;
5567b76f
BJ
109
110 host = rindex(argv[0], '/');
111 if (host)
112 host++;
113 else
114 host = argv[0];
115 argv++, --argc;
116 if (!strcmp(host, "rlogin"))
117 host = *argv++, --argc;
118another:
4659bf8e 119 if (argc > 0 && !strcmp(*argv, "-d")) {
5567b76f 120 argv++, argc--;
b5f23952 121 options |= SO_DEBUG;
5567b76f
BJ
122 goto another;
123 }
4659bf8e 124 if (argc > 0 && !strcmp(*argv, "-l")) {
5567b76f
BJ
125 argv++, argc--;
126 if (argc == 0)
127 goto usage;
128 name = *argv++; argc--;
129 goto another;
130 }
4659bf8e 131 if (argc > 0 && !strncmp(*argv, "-e", 2)) {
5567b76f
BJ
132 cmdchar = argv[0][2];
133 argv++, argc--;
134 goto another;
135 }
4659bf8e 136 if (argc > 0 && !strcmp(*argv, "-8")) {
5567b76f
BJ
137 eight = 1;
138 argv++, argc--;
139 goto another;
140 }
39a44641
JB
141 if (argc > 0 && !strcmp(*argv, "-L")) {
142 litout = 1;
143 argv++, argc--;
144 goto another;
145 }
5567b76f
BJ
146 if (host == 0)
147 goto usage;
148 if (argc > 0)
149 goto usage;
150 pwd = getpwuid(getuid());
151 if (pwd == 0) {
152 fprintf(stderr, "Who are you?\n");
153 exit(1);
154 }
86a16a64
SL
155 sp = getservbyname("login", "tcp");
156 if (sp == 0) {
157 fprintf(stderr, "rlogin: login/tcp: unknown service\n");
158 exit(2);
159 }
f91500bc
SL
160 cp = getenv("TERM");
161 if (cp)
a9b12987 162 (void) strcpy(term, cp);
119312d1 163 if (ioctl(0, TIOCGETP, &ttyb) == 0) {
a9b12987
KM
164 (void) strcat(term, "/");
165 (void) strcat(term, speeds[ttyb.sg_ospeed]);
5567b76f 166 }
a9b12987
KM
167 (void) get_window_size(0, &winsize);
168 (void) signal(SIGPIPE, lostpeer);
169 /* will use SIGUSR1 for window size hack, so hold it off */
170 oldmask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1));
86a16a64 171 rem = rcmd(&host, sp->s_port, pwd->pw_name,
5567b76f
BJ
172 name ? name : pwd->pw_name, term, 0);
173 if (rem < 0)
174 exit(1);
b5f23952 175 if (options & SO_DEBUG &&
0975b26d 176 setsockopt(rem, SOL_SOCKET, SO_DEBUG, &on, sizeof (on)) < 0)
b5f23952 177 perror("rlogin: setsockopt (SO_DEBUG)");
86a16a64
SL
178 uid = getuid();
179 if (setuid(uid) < 0) {
180 perror("rlogin: setuid");
181 exit(1);
182 }
e86d8e67 183 doit(oldmask);
86a16a64 184 /*NOTREACHED*/
5567b76f
BJ
185usage:
186 fprintf(stderr,
ffc36ece 187 "usage: rlogin host [ -ex ] [ -l username ] [ -8 ] [ -L ]\n");
5567b76f
BJ
188 exit(1);
189}
190
5567b76f 191#define CRLF "\r\n"
5567b76f 192
86a16a64 193int child;
33953861 194int catchild();
a9b12987 195int copytochild(), writeroob();
5567b76f 196
9da647e3 197int defflags, tabflag;
39a44641 198int deflflags;
9da647e3
SL
199char deferase, defkill;
200struct tchars deftc;
201struct ltchars defltc;
202struct tchars notc = { -1, -1, -1, -1, -1, -1 };
203struct ltchars noltc = { -1, -1, -1, -1, -1, -1 };
86a16a64 204
e86d8e67 205doit(oldmask)
5567b76f 206{
5567b76f 207 int exit();
9da647e3 208 struct sgttyb sb;
5567b76f 209
a9b12987 210 (void) ioctl(0, TIOCGETP, (char *)&sb);
9da647e3 211 defflags = sb.sg_flags;
c887373b 212 tabflag = defflags & TBDELAY;
c64cdfb4 213 defflags &= ECHO | CRMOD;
9da647e3
SL
214 deferase = sb.sg_erase;
215 defkill = sb.sg_kill;
a9b12987
KM
216 (void) ioctl(0, TIOCLGET, (char *)&deflflags);
217 (void) ioctl(0, TIOCGETC, (char *)&deftc);
9da647e3
SL
218 notc.t_startc = deftc.t_startc;
219 notc.t_stopc = deftc.t_stopc;
a9b12987
KM
220 (void) ioctl(0, TIOCGLTC, (char *)&defltc);
221 (void) signal(SIGINT, SIG_IGN);
222 setsignal(SIGHUP, exit);
223 setsignal(SIGQUIT, exit);
86a16a64
SL
224 child = fork();
225 if (child == -1) {
226 perror("rlogin: fork");
c3140eff 227 done(1);
86a16a64 228 }
86a16a64 229 if (child == 0) {
e86d8e67 230 mode(1);
a9b12987 231 if (reader(oldmask) == 0) {
c3140eff
MK
232 prf("Connection closed.");
233 exit(0);
234 }
c887373b
SL
235 sleep(1);
236 prf("\007Connection closed.");
5567b76f
BJ
237 exit(3);
238 }
a9b12987
KM
239
240 /*
241 * We may still own the socket, and may have a pending SIGURG
242 * (or might receive one soon) that we really want to send to
243 * the reader. Set a trap that simply copies such signals to
244 * the child.
245 */
246 (void) signal(SIGURG, copytochild);
247 (void) signal(SIGUSR1, writeroob);
248 (void) sigsetmask(oldmask);
249 (void) signal(SIGCHLD, catchild);
86a16a64 250 writer();
c887373b 251 prf("Closed connection.");
c3140eff 252 done(0);
5567b76f
BJ
253}
254
a9b12987
KM
255/*
256 * Trap a signal, unless it is being ignored.
257 */
258setsignal(sig, act)
259 int sig, (*act)();
260{
261 int omask = sigblock(sigmask(sig));
262
263 if (signal(sig, act) == SIG_IGN)
264 (void) signal(sig, SIG_IGN);
265 (void) sigsetmask(omask);
266}
267
c3140eff
MK
268done(status)
269 int status;
5567b76f 270{
a9b12987 271 int w;
5567b76f
BJ
272
273 mode(0);
a9b12987
KM
274 if (child > 0) {
275 /* make sure catchild does not snap it up */
276 (void) signal(SIGCHLD, SIG_DFL);
277 if (kill(child, SIGKILL) >= 0)
278 while ((w = wait((union wait *)0)) > 0 && w != child)
279 /*void*/;
280 }
c3140eff 281 exit(status);
5567b76f
BJ
282}
283
a9b12987
KM
284/*
285 * Copy SIGURGs to the child process.
286 */
287copytochild()
288{
289
290 (void) kill(child, SIGURG);
291}
292
e86d8e67
KM
293/*
294 * This is called when the reader process gets the out-of-band (urgent)
295 * request to turn on the window-changing protocol.
296 */
297writeroob()
298{
299
ffc36ece 300 if (dosigwinch == 0) {
bedc18e4 301 sendwindow();
a9b12987 302 (void) signal(SIGWINCH, sigwinch);
ffc36ece 303 }
e86d8e67 304 dosigwinch = 1;
e86d8e67
KM
305}
306
33953861
EW
307catchild()
308{
309 union wait status;
310 int pid;
311
312again:
a9b12987 313 pid = wait3(&status, WNOHANG|WUNTRACED, (struct rusage *)0);
33953861
EW
314 if (pid == 0)
315 return;
316 /*
317 * if the child (reader) dies, just quit
318 */
319 if (pid < 0 || pid == child && !WIFSTOPPED(status))
a9b12987 320 done((int)(status.w_termsig | status.w_retcode));
33953861
EW
321 goto again;
322}
323
5567b76f 324/*
86a16a64
SL
325 * writer: write to remote: 0 -> line.
326 * ~. terminate
327 * ~^Z suspend rlogin process.
b5f23952 328 * ~^Y suspend rlogin process, but leave reader alone.
5567b76f 329 */
86a16a64 330writer()
5567b76f 331{
4d3ce186 332 char c;
33953861 333 register n;
4d3ce186
JB
334 register bol = 1; /* beginning of line */
335 register local = 0;
5567b76f 336
4d3ce186 337 for (;;) {
33953861 338 n = read(0, &c, 1);
119312d1
SL
339 if (n <= 0) {
340 if (n < 0 && errno == EINTR)
33953861 341 continue;
119312d1
SL
342 break;
343 }
86a16a64
SL
344 /*
345 * If we're at the beginning of the line
346 * and recognize a command character, then
347 * we echo locally. Otherwise, characters
348 * are echo'd remotely. If the command
349 * character is doubled, this acts as a
350 * force and local echo is suppressed.
351 */
4d3ce186
JB
352 if (bol) {
353 bol = 0;
354 if (c == cmdchar) {
355 bol = 0;
356 local = 1;
357 continue;
86a16a64 358 }
4d3ce186
JB
359 } else if (local) {
360 local = 0;
361 if (c == '.' || c == deftc.t_eofc) {
362 echo(c);
363 break;
364 }
365 if (c == defltc.t_suspc || c == defltc.t_dsuspc) {
366 bol = 1;
367 echo(c);
368 stop(c);
369 continue;
5567b76f 370 }
4d3ce186 371 if (c != cmdchar)
a9b12987 372 (void) write(rem, &cmdchar, 1);
5567b76f 373 }
4d3ce186
JB
374 if (write(rem, &c, 1) == 0) {
375 prf("line gone");
376 break;
5567b76f 377 }
4d3ce186 378 bol = c == defkill || c == deftc.t_eofc ||
c3140eff 379 c == deftc.t_intrc || c == defltc.t_suspc ||
4d3ce186 380 c == '\r' || c == '\n';
119312d1
SL
381 }
382}
383
4d3ce186
JB
384echo(c)
385register char c;
386{
387 char buf[8];
388 register char *p = buf;
389
390 c &= 0177;
391 *p++ = cmdchar;
392 if (c < ' ') {
393 *p++ = '^';
394 *p++ = c + '@';
395 } else if (c == 0177) {
396 *p++ = '^';
397 *p++ = '?';
398 } else
399 *p++ = c;
400 *p++ = '\r';
401 *p++ = '\n';
a9b12987 402 (void) write(1, buf, p - buf);
4d3ce186
JB
403}
404
119312d1
SL
405stop(cmdc)
406 char cmdc;
407{
119312d1 408 mode(0);
a9b12987
KM
409 (void) signal(SIGCHLD, SIG_IGN);
410 (void) kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP);
411 (void) signal(SIGCHLD, catchild);
119312d1
SL
412 mode(1);
413 sigwinch(); /* check for size changes */
414}
415
416sigwinch()
417{
418 struct winsize ws;
419
a9b12987 420 if (dosigwinch && get_window_size(0, &ws) == 0 &&
119312d1
SL
421 bcmp(&ws, &winsize, sizeof (ws))) {
422 winsize = ws;
e86d8e67 423 sendwindow();
5567b76f 424 }
5567b76f
BJ
425}
426
e86d8e67
KM
427/*
428 * Send the window size to the server via the magic escape
429 */
430sendwindow()
431{
432 char obuf[4 + sizeof (struct winsize)];
433 struct winsize *wp = (struct winsize *)(obuf+4);
434
435 obuf[0] = 0377;
436 obuf[1] = 0377;
437 obuf[2] = 's';
438 obuf[3] = 's';
439 wp->ws_row = htons(winsize.ws_row);
440 wp->ws_col = htons(winsize.ws_col);
441 wp->ws_xpixel = htons(winsize.ws_xpixel);
442 wp->ws_ypixel = htons(winsize.ws_ypixel);
443 (void) write(rem, obuf, sizeof(obuf));
444}
445
c3140eff
MK
446/*
447 * reader: read from remote: line -> 1
448 */
449#define READING 1
450#define WRITING 2
451
452char rcvbuf[8 * 1024];
453int rcvcnt;
454int rcvstate;
545e8847 455int ppid;
c3140eff
MK
456jmp_buf rcvtop;
457
5567b76f
BJ
458oob()
459{
c3140eff
MK
460 int out = FWRITE, atmark, n;
461 int rcvd = 0;
86a16a64 462 char waste[BUFSIZ], mark;
e86d8e67 463 struct sgttyb sb;
5567b76f 464
c3140eff
MK
465 while (recv(rem, &mark, 1, MSG_OOB) < 0)
466 switch (errno) {
467
468 case EWOULDBLOCK:
469 /*
470 * Urgent data not here yet.
471 * It may not be possible to send it yet
472 * if we are blocked for output
473 * and our input buffer is full.
474 */
475 if (rcvcnt < sizeof(rcvbuf)) {
476 n = read(rem, rcvbuf + rcvcnt,
477 sizeof(rcvbuf) - rcvcnt);
478 if (n <= 0)
479 return;
480 rcvd += n;
481 } else {
482 n = read(rem, waste, sizeof(waste));
483 if (n <= 0)
484 return;
485 }
486 continue;
487
488 default:
489 return;
5567b76f 490 }
c3140eff 491 if (mark & TIOCPKT_WINDOW) {
e86d8e67
KM
492 /*
493 * Let server know about window size changes
494 */
a9b12987 495 (void) kill(ppid, SIGUSR1);
e86d8e67 496 }
c3140eff 497 if (!eight && (mark & TIOCPKT_NOSTOP)) {
a9b12987 498 (void) ioctl(0, TIOCGETP, (char *)&sb);
e86d8e67
KM
499 sb.sg_flags &= ~CBREAK;
500 sb.sg_flags |= RAW;
a9b12987 501 (void) ioctl(0, TIOCSETN, (char *)&sb);
9da647e3
SL
502 notc.t_stopc = -1;
503 notc.t_startc = -1;
a9b12987 504 (void) ioctl(0, TIOCSETC, (char *)&notc);
5567b76f 505 }
c3140eff 506 if (!eight && (mark & TIOCPKT_DOSTOP)) {
a9b12987 507 (void) ioctl(0, TIOCGETP, (char *)&sb);
e86d8e67
KM
508 sb.sg_flags &= ~RAW;
509 sb.sg_flags |= CBREAK;
a9b12987 510 (void) ioctl(0, TIOCSETN, (char *)&sb);
9da647e3
SL
511 notc.t_stopc = deftc.t_stopc;
512 notc.t_startc = deftc.t_startc;
a9b12987 513 (void) ioctl(0, TIOCSETC, (char *)&notc);
5567b76f 514 }
c3140eff 515 if (mark & TIOCPKT_FLUSHWRITE) {
a9b12987 516 (void) ioctl(1, TIOCFLUSH, (char *)&out);
c3140eff
MK
517 for (;;) {
518 if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
519 perror("ioctl");
520 break;
521 }
522 if (atmark)
523 break;
524 n = read(rem, waste, sizeof (waste));
525 if (n <= 0)
526 break;
527 }
528 /*
529 * Don't want any pending data to be output,
530 * so clear the recv buffer.
531 * If we were hanging on a write when interrupted,
532 * don't want it to restart. If we were reading,
533 * restart anyway.
534 */
535 rcvcnt = 0;
536 longjmp(rcvtop, 1);
537 }
a9b12987
KM
538
539 /*
540 * oob does not do FLUSHREAD (alas!)
541 */
542
c3140eff
MK
543 /*
544 * If we filled the receive buffer while a read was pending,
545 * longjmp to the top to restart appropriately. Don't abort
546 * a pending write, however, or we won't know how much was written.
547 */
548 if (rcvd && rcvstate == READING)
549 longjmp(rcvtop, 1);
5567b76f
BJ
550}
551
86a16a64
SL
552/*
553 * reader: read from remote: line -> 1
554 */
a9b12987
KM
555reader(oldmask)
556 int oldmask;
5567b76f 557{
545e8847
MK
558#if !defined(BSD) || BSD < 43
559 int pid = -getpid();
560#else
c3140eff 561 int pid = getpid();
545e8847 562#endif
c3140eff
MK
563 int n, remaining;
564 char *bufp = rcvbuf;
5567b76f 565
a9b12987
KM
566 (void) signal(SIGTTOU, SIG_IGN);
567 (void) signal(SIGURG, oob);
545e8847 568 ppid = getppid();
a9b12987 569 (void) fcntl(rem, F_SETOWN, pid);
c3140eff 570 (void) setjmp(rcvtop);
a9b12987 571 (void) sigsetmask(oldmask);
5567b76f 572 for (;;) {
c3140eff
MK
573 while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
574 rcvstate = WRITING;
575 n = write(1, bufp, remaining);
576 if (n < 0) {
577 if (errno != EINTR)
83468856 578 return (-1);
c3140eff
MK
579 continue;
580 }
581 bufp += n;
582 }
583 bufp = rcvbuf;
584 rcvcnt = 0;
585 rcvstate = READING;
586 rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf));
587 if (rcvcnt == 0)
588 return (0);
589 if (rcvcnt < 0) {
86a16a64 590 if (errno == EINTR)
5567b76f 591 continue;
545e8847 592 perror("read");
c3140eff 593 return (-1);
5567b76f 594 }
5567b76f
BJ
595 }
596}
597
5567b76f
BJ
598mode(f)
599{
9da647e3
SL
600 struct tchars *tc;
601 struct ltchars *ltc;
602 struct sgttyb sb;
39a44641 603 int lflags;
86a16a64 604
a9b12987
KM
605 (void) ioctl(0, TIOCGETP, (char *)&sb);
606 (void) ioctl(0, TIOCLGET, (char *)&lflags);
c64cdfb4
SL
607 switch (f) {
608
609 case 0:
9da647e3
SL
610 sb.sg_flags &= ~(CBREAK|RAW|TBDELAY);
611 sb.sg_flags |= defflags|tabflag;
c64cdfb4 612 tc = &deftc;
9da647e3
SL
613 ltc = &defltc;
614 sb.sg_kill = defkill;
615 sb.sg_erase = deferase;
39a44641 616 lflags = deflflags;
c64cdfb4
SL
617 break;
618
619 case 1:
9da647e3
SL
620 sb.sg_flags |= (eight ? RAW : CBREAK);
621 sb.sg_flags &= ~defflags;
c887373b 622 /* preserve tab delays, but turn off XTABS */
9da647e3
SL
623 if ((sb.sg_flags & TBDELAY) == XTABS)
624 sb.sg_flags &= ~TBDELAY;
c64cdfb4 625 tc = &notc;
9da647e3
SL
626 ltc = &noltc;
627 sb.sg_kill = sb.sg_erase = -1;
39a44641
JB
628 if (litout)
629 lflags |= LLITOUT;
c64cdfb4
SL
630 break;
631
632 default:
633 return;
5567b76f 634 }
a9b12987
KM
635 (void) ioctl(0, TIOCSLTC, (char *)ltc);
636 (void) ioctl(0, TIOCSETC, (char *)tc);
637 (void) ioctl(0, TIOCSETN, (char *)&sb);
638 (void) ioctl(0, TIOCLSET, (char *)&lflags);
5567b76f
BJ
639}
640
86a16a64 641/*VARARGS*/
e86d8e67 642prf(f, a1, a2, a3, a4, a5)
86a16a64 643 char *f;
5567b76f 644{
a9b12987 645
e86d8e67 646 fprintf(stderr, f, a1, a2, a3, a4, a5);
5567b76f
BJ
647 fprintf(stderr, CRLF);
648}
649
86a16a64 650lostpeer()
5567b76f 651{
a9b12987
KM
652
653 (void) signal(SIGPIPE, SIG_IGN);
c887373b 654 prf("\007Connection closed.");
c3140eff 655 done(1);
5567b76f 656}
545e8847 657