]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/rlogin/rlogin.c
This commit was generated by cvs2svn to compensate for changes in r57416,
[FreeBSD/FreeBSD.git] / usr.bin / rlogin / rlogin.c
1 /*
2  * Copyright (c) 1983, 1990, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #ifndef lint
35 static const char copyright[] =
36 "@(#) Copyright (c) 1983, 1990, 1993\n\
37         The Regents of the University of California.  All rights reserved.\n";
38 #endif /* not lint */
39
40 #ifndef lint
41 static const char sccsid[] = "@(#)rlogin.c      8.1 (Berkeley) 6/6/93";
42 static const char rcsid[] =
43   "$FreeBSD$";
44 #endif /* not lint */
45
46 /*
47  * rlogin - remote login
48  */
49 #include <sys/param.h>
50 #include <sys/socket.h>
51 #include <sys/time.h>
52 #include <sys/resource.h>
53 #include <sys/wait.h>
54
55 #include <netinet/in.h>
56 #include <netinet/in_systm.h>
57 #include <netinet/ip.h>
58 #include <netinet/tcp.h>
59
60 #include <err.h>
61 #include <errno.h>
62 #include <fcntl.h>
63 #include <libutil.h>
64 #include <netdb.h>
65 #include <pwd.h>
66 #include <setjmp.h>
67 #include <sgtty.h>
68 #include <signal.h>
69 #include <stdio.h>
70 #include <stdlib.h>
71 #include <string.h>
72 #include <unistd.h>
73 #include <err.h>
74
75 #ifdef KERBEROS
76 #include <des.h>
77 #include <krb.h>
78
79 #include "../../bin/rcp/pathnames.h"
80 #include "krb.h"
81
82 CREDENTIALS cred;
83 Key_schedule schedule;
84 int use_kerberos = 1, doencrypt;
85 char dst_realm_buf[REALM_SZ], *dest_realm = NULL;
86 #endif
87
88 #ifndef TIOCPKT_WINDOW
89 #define TIOCPKT_WINDOW  0x80
90 #endif
91
92 /* concession to Sun */
93 #ifndef SIGUSR1
94 #define SIGUSR1 30
95 #endif
96
97 int eight, litout, rem;
98 int family = PF_UNSPEC;
99
100 int noescape;
101 u_char escapechar = '~';
102
103 char *speeds[] = {
104         "0", "50", "75", "110", "134", "150", "200", "300", "600", "1200",
105         "1800", "2400", "4800", "9600", "19200", "38400", "57600", "115200"
106 #define MAX_SPEED_LENGTH        (sizeof("115200") - 1)
107 };
108
109 #ifdef OLDSUN
110 struct winsize {
111         unsigned short ws_row, ws_col;
112         unsigned short ws_xpixel, ws_ypixel;
113 };
114 #else
115 #define get_window_size(fd, wp) ioctl(fd, TIOCGWINSZ, wp)
116 #endif
117 struct  winsize winsize;
118
119 void            catch_child __P((int));
120 void            copytochild __P((int));
121 void            doit __P((long)) __dead2;
122 void            done __P((int)) __dead2;
123 void            echo __P((char));
124 u_int           getescape __P((char *));
125 void            lostpeer __P((int));
126 void            mode __P((int));
127 void            msg __P((char *));
128 void            oob __P((int));
129 int             reader __P((int));
130 void            sendwindow __P((void));
131 void            setsignal __P((int));
132 void            sigwinch __P((int));
133 void            stop __P((char));
134 void            usage __P((void)) __dead2;
135 void            writer __P((void));
136 void            writeroob __P((int));
137
138 #ifdef OLDSUN
139 int             get_window_size __P((int, struct winsize *));
140 #endif
141
142 int
143 main(argc, argv)
144         int argc;
145         char *argv[];
146 {
147         extern char *optarg;
148         extern int optind;
149         struct passwd *pw;
150         struct servent *sp;
151         struct sgttyb ttyb;
152         long omask;
153         int argoff, ch, dflag, Dflag, one, uid;
154         char *host, *localname, *p, *user, term[1024];
155 #ifdef KERBEROS
156         char *k;
157 #endif
158         struct sockaddr_storage ss;
159         int sslen;
160
161         argoff = dflag = Dflag = 0;
162         one = 1;
163         host = localname = user = NULL;
164
165         if ((p = rindex(argv[0], '/')))
166                 ++p;
167         else
168                 p = argv[0];
169
170         if (strcmp(p, "rlogin"))
171                 host = p;
172
173         /* handle "rlogin host flags" */
174         if (!host && argc > 2 && argv[1][0] != '-') {
175                 host = argv[1];
176                 argoff = 1;
177         }
178
179 #ifdef KERBEROS
180 #define OPTIONS "468DEKLde:i:k:l:x"
181 #else
182 #define OPTIONS "468DEKLde:i:l:"
183 #endif
184         while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != -1)
185                 switch(ch) {
186                 case '4':
187                         family = PF_INET;
188                         break;
189
190                 case '6':
191                         family = PF_INET6;
192                         break;
193
194                 case '8':
195                         eight = 1;
196                         break;
197                 case 'D':
198                         Dflag = 1;
199                         break;
200                 case 'E':
201                         noescape = 1;
202                         break;
203                 case 'K':
204 #ifdef KERBEROS
205                         use_kerberos = 0;
206 #endif
207                         break;
208                 case 'L':
209                         litout = 1;
210                         break;
211                 case 'd':
212                         dflag = 1;
213                         break;
214                 case 'e':
215                         noescape = 0;
216                         escapechar = getescape(optarg);
217                         break;
218                 case 'i':
219                         if (getuid() != 0)
220                                 errx(1, "-i user: permission denied");
221                         localname = optarg;
222                         break;
223 #ifdef KERBEROS
224                 case 'k':
225                         dest_realm = dst_realm_buf;
226                         (void)strncpy(dest_realm, optarg, REALM_SZ);
227                         break;
228 #endif
229                 case 'l':
230                         user = optarg;
231                         break;
232 #ifdef CRYPT
233 #ifdef KERBEROS
234                 case 'x':
235                         doencrypt = 1;
236                         break;
237 #endif
238 #endif
239                 case '?':
240                 default:
241                         usage();
242                 }
243         optind += argoff;
244
245         /* if haven't gotten a host yet, do so */
246         if (!host && !(host = argv[optind++]))
247                 usage();
248
249         if (argv[optind])
250                 usage();
251
252         if (!(pw = getpwuid(uid = getuid())))
253                 errx(1, "unknown user id");
254         if (!user)
255                 user = pw->pw_name;
256         if (!localname)
257                 localname = pw->pw_name;
258
259         sp = NULL;
260 #ifdef KERBEROS
261         k = auth_getval("auth_list");
262         if (k && !strstr(k, "kerberos"))
263             use_kerberos = 0;
264         if (use_kerberos) {
265                 sp = getservbyname((doencrypt ? "eklogin" : "klogin"), "tcp");
266                 if (sp == NULL) {
267                         use_kerberos = 0;
268                         warn("can't get entry for %s/tcp service",
269                             doencrypt ? "eklogin" : "klogin");
270                 }
271         }
272 #endif
273         if (sp == NULL)
274                 sp = getservbyname("login", "tcp");
275         if (sp == NULL)
276                 errx(1, "login/tcp: unknown service");
277
278 #define MAX_TERM_LENGTH (sizeof(term) - 1 - MAX_SPEED_LENGTH - 1)
279
280         (void)strncpy(term, (p = getenv("TERM")) ? p : "network",
281                       MAX_TERM_LENGTH);
282         term[MAX_TERM_LENGTH] = '\0';
283         if (ioctl(0, TIOCGETP, &ttyb) == 0) {
284                 (void)strcat(term, "/");
285                 (void)strcat(term, speeds[(int)ttyb.sg_ospeed]);
286         }
287
288         (void)get_window_size(0, &winsize);
289
290         (void)signal(SIGPIPE, lostpeer);
291         /* will use SIGUSR1 for window size hack, so hold it off */
292         omask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1));
293         /*
294          * We set SIGURG and SIGUSR1 below so that an
295          * incoming signal will be held pending rather than being
296          * discarded. Note that these routines will be ready to get
297          * a signal by the time that they are unblocked below.
298          */
299         (void)signal(SIGURG, copytochild);
300         (void)signal(SIGUSR1, writeroob);
301
302 #ifdef KERBEROS
303         if (use_kerberos) {
304                 setuid(getuid());
305                 rem = KSUCCESS;
306                 errno = 0;
307                 if (dest_realm == NULL)
308                         dest_realm = krb_realmofhost(host);
309
310 #ifdef CRYPT
311                 if (doencrypt) {
312                         rem = krcmd_mutual(&host, sp->s_port, user, term, 0,
313                             dest_realm, &cred, schedule);
314                         des_set_key(&cred.session, schedule);
315                 } else
316 #endif /* CRYPT */
317                         rem = krcmd(&host, sp->s_port, user, term, 0,
318                             dest_realm);
319                 if (rem < 0) {
320                         int i;
321                         char **newargv;
322
323                         sp = getservbyname("login", "tcp");
324                         if (sp == NULL)
325                                 errx(1, "unknown service login/tcp");
326                         if (errno == ECONNREFUSED)
327                                 warn("remote host doesn't support Kerberos");
328                         if (errno == ENOENT)
329                                 warn("can't provide Kerberos auth data");
330                         newargv = malloc((argc + 2) * sizeof(*newargv));
331                         if (newargv == NULL)
332                                 err(1, "malloc");
333                         newargv[0] = argv[0];
334                         newargv[1] = "-K";
335                         for(i = 1; i < argc; ++i)
336                         newargv[i + 1] = argv[i];
337                         newargv[argc + 1] = NULL;
338                         execv(_PATH_RLOGIN, newargv);
339                 }
340         } else {
341 #ifdef CRYPT
342                 if (doencrypt)
343                         errx(1, "the -x flag requires Kerberos authentication");
344 #endif /* CRYPT */
345                 rem = rcmd_af(&host, sp->s_port, localname, user, term, 0,
346                               family);
347         }
348 #else
349         rem = rcmd_af(&host, sp->s_port, localname, user, term, 0, family);
350 #endif /* KERBEROS */
351
352         if (rem < 0)
353                 exit(1);
354
355         if (dflag &&
356             setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0)
357                 warn("setsockopt");
358         if (Dflag &&
359             setsockopt(rem, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) < 0)
360                 warn("setsockopt NODELAY (ignored)");
361
362         sslen = sizeof(ss);
363         one = IPTOS_LOWDELAY;
364         if (getsockname(rem, (struct sockaddr *)&ss, &sslen) == 0 &&
365             ss.ss_family == AF_INET) {
366                 if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one,
367                                sizeof(int)) < 0)
368                         warn("setsockopt TOS (ignored)");
369         } else
370                 if (ss.ss_family == AF_INET)
371                         warn("setsockopt getsockname failed");
372
373         (void)setuid(uid);
374         doit(omask);
375         /*NOTREACHED*/
376 }
377
378 int child, defflags, deflflags, tabflag;
379 char deferase, defkill;
380 struct tchars deftc;
381 struct ltchars defltc;
382 struct tchars notc = { -1, -1, -1, -1, -1, -1 };
383 struct ltchars noltc = { -1, -1, -1, -1, -1, -1 };
384
385 void
386 doit(omask)
387         long omask;
388 {
389         struct sgttyb sb;
390
391         (void)ioctl(0, TIOCGETP, (char *)&sb);
392         defflags = sb.sg_flags;
393         tabflag = defflags & TBDELAY;
394         defflags &= ECHO | CRMOD;
395         deferase = sb.sg_erase;
396         defkill = sb.sg_kill;
397         (void)ioctl(0, TIOCLGET, &deflflags);
398         (void)ioctl(0, TIOCGETC, &deftc);
399         notc.t_startc = deftc.t_startc;
400         notc.t_stopc = deftc.t_stopc;
401         (void)ioctl(0, TIOCGLTC, &defltc);
402         (void)signal(SIGINT, SIG_IGN);
403         setsignal(SIGHUP);
404         setsignal(SIGQUIT);
405         child = fork();
406         if (child == -1) {
407                 warn("fork");
408                 done(1);
409         }
410         if (child == 0) {
411                 mode(1);
412                 if (reader(omask) == 0) {
413                         msg("connection closed.");
414                         exit(0);
415                 }
416                 sleep(1);
417                 msg("\007connection closed.");
418                 exit(1);
419         }
420
421         /*
422          * We may still own the socket, and may have a pending SIGURG (or might
423          * receive one soon) that we really want to send to the reader.  When
424          * one of these comes in, the trap copytochild simply copies such
425          * signals to the child. We can now unblock SIGURG and SIGUSR1
426          * that were set above.
427          */
428         (void)sigsetmask(omask);
429         (void)signal(SIGCHLD, catch_child);
430         writer();
431         msg("closed connection.");
432         done(0);
433 }
434
435 /* trap a signal, unless it is being ignored. */
436 void
437 setsignal(sig)
438         int sig;
439 {
440         int omask = sigblock(sigmask(sig));
441
442         if (signal(sig, exit) == SIG_IGN)
443                 (void)signal(sig, SIG_IGN);
444         (void)sigsetmask(omask);
445 }
446
447 void
448 done(status)
449         int status;
450 {
451         int w, wstatus;
452
453         mode(0);
454         if (child > 0) {
455                 /* make sure catch_child does not snap it up */
456                 (void)signal(SIGCHLD, SIG_DFL);
457                 if (kill(child, SIGKILL) >= 0)
458                         while ((w = wait(&wstatus)) > 0 && w != child);
459         }
460         exit(status);
461 }
462
463 int dosigwinch;
464
465 /*
466  * This is called when the reader process gets the out-of-band (urgent)
467  * request to turn on the window-changing protocol.
468  */
469 void
470 writeroob(signo)
471         int signo;
472 {
473         if (dosigwinch == 0) {
474                 sendwindow();
475                 (void)signal(SIGWINCH, sigwinch);
476         }
477         dosigwinch = 1;
478 }
479
480 void
481 catch_child(signo)
482         int signo;
483 {
484         union wait status;
485         int pid;
486
487         for (;;) {
488                 pid = wait3((int *)&status, WNOHANG|WUNTRACED, NULL);
489                 if (pid == 0)
490                         return;
491                 /* if the child (reader) dies, just quit */
492                 if (pid < 0 || (pid == child && !WIFSTOPPED(status)))
493                         done((int)(status.w_termsig | status.w_retcode));
494         }
495         /* NOTREACHED */
496 }
497
498 /*
499  * writer: write to remote: 0 -> line.
500  * ~.                           terminate
501  * ~^Z                          suspend rlogin process.
502  * ~<delayed-suspend char>      suspend rlogin process, but leave reader alone.
503  */
504 void
505 writer()
506 {
507         register int bol, local, n;
508         char c;
509
510         bol = 1;                        /* beginning of line */
511         local = 0;
512         for (;;) {
513                 n = read(STDIN_FILENO, &c, 1);
514                 if (n <= 0) {
515                         if (n < 0 && errno == EINTR)
516                                 continue;
517                         break;
518                 }
519                 /*
520                  * If we're at the beginning of the line and recognize a
521                  * command character, then we echo locally.  Otherwise,
522                  * characters are echo'd remotely.  If the command character
523                  * is doubled, this acts as a force and local echo is
524                  * suppressed.
525                  */
526                 if (bol) {
527                         bol = 0;
528                         if (!noescape && c == escapechar) {
529                                 local = 1;
530                                 continue;
531                         }
532                 } else if (local) {
533                         local = 0;
534                         if (c == '.' || c == deftc.t_eofc) {
535                                 echo(c);
536                                 break;
537                         }
538                         if (c == defltc.t_suspc || c == defltc.t_dsuspc) {
539                                 bol = 1;
540                                 echo(c);
541                                 stop(c);
542                                 continue;
543                         }
544                         if (c != escapechar)
545 #ifdef CRYPT
546 #ifdef KERBEROS
547                                 if (doencrypt)
548                                         (void)des_enc_write(rem,
549                                             (char *)&escapechar, 1,
550                                                 schedule, &cred.session);
551                                 else
552 #endif
553 #endif
554                                         (void)write(rem, &escapechar, 1);
555                 }
556
557 #ifdef CRYPT
558 #ifdef KERBEROS
559                 if (doencrypt) {
560                         if (des_enc_write(rem, &c, 1, schedule, &cred.session) == 0) {
561                                 msg("line gone");
562                                 break;
563                         }
564                 } else
565 #endif
566 #endif
567                         if (write(rem, &c, 1) == 0) {
568                                 msg("line gone");
569                                 break;
570                         }
571                 bol = c == defkill || c == deftc.t_eofc ||
572                     c == deftc.t_intrc || c == defltc.t_suspc ||
573                     c == '\r' || c == '\n';
574         }
575 }
576
577 void
578 #if __STDC__
579 echo(register char c)
580 #else
581 echo(c)
582         register char c;
583 #endif
584 {
585         register char *p;
586         char buf[8];
587
588         p = buf;
589         c &= 0177;
590         *p++ = escapechar;
591         if (c < ' ') {
592                 *p++ = '^';
593                 *p++ = c + '@';
594         } else if (c == 0177) {
595                 *p++ = '^';
596                 *p++ = '?';
597         } else
598                 *p++ = c;
599         *p++ = '\r';
600         *p++ = '\n';
601         (void)write(STDOUT_FILENO, buf, p - buf);
602 }
603
604 void
605 #if __STDC__
606 stop(char cmdc)
607 #else
608 stop(cmdc)
609         char cmdc;
610 #endif
611 {
612         mode(0);
613         (void)signal(SIGCHLD, SIG_IGN);
614         (void)kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP);
615         (void)signal(SIGCHLD, catch_child);
616         mode(1);
617         sigwinch(0);                    /* check for size changes */
618 }
619
620 void
621 sigwinch(signo)
622         int signo;
623 {
624         struct winsize ws;
625
626         if (dosigwinch && get_window_size(0, &ws) == 0 &&
627             bcmp(&ws, &winsize, sizeof(ws))) {
628                 winsize = ws;
629                 sendwindow();
630         }
631 }
632
633 /*
634  * Send the window size to the server via the magic escape
635  */
636 void
637 sendwindow()
638 {
639         struct winsize *wp;
640         char obuf[4 + sizeof (struct winsize)];
641
642         wp = (struct winsize *)(obuf+4);
643         obuf[0] = 0377;
644         obuf[1] = 0377;
645         obuf[2] = 's';
646         obuf[3] = 's';
647         wp->ws_row = htons(winsize.ws_row);
648         wp->ws_col = htons(winsize.ws_col);
649         wp->ws_xpixel = htons(winsize.ws_xpixel);
650         wp->ws_ypixel = htons(winsize.ws_ypixel);
651
652 #ifdef CRYPT
653 #ifdef KERBEROS
654         if(doencrypt)
655                 (void)des_enc_write(rem, obuf, sizeof(obuf),
656                         schedule, &cred.session);
657         else
658 #endif
659 #endif
660                 (void)write(rem, obuf, sizeof(obuf));
661 }
662
663 /*
664  * reader: read from remote: line -> 1
665  */
666 #define READING 1
667 #define WRITING 2
668
669 jmp_buf rcvtop;
670 int ppid, rcvcnt, rcvstate;
671 char rcvbuf[8 * 1024];
672
673 void
674 oob(signo)
675         int signo;
676 {
677         struct sgttyb sb;
678         int atmark, n, out, rcvd;
679         char waste[BUFSIZ], mark;
680
681         out = O_RDWR;
682         rcvd = 0;
683         while (recv(rem, &mark, 1, MSG_OOB) < 0) {
684                 switch (errno) {
685                 case EWOULDBLOCK:
686                         /*
687                          * Urgent data not here yet.  It may not be possible
688                          * to send it yet if we are blocked for output and
689                          * our input buffer is full.
690                          */
691                         if (rcvcnt < sizeof(rcvbuf)) {
692                                 n = read(rem, rcvbuf + rcvcnt,
693                                     sizeof(rcvbuf) - rcvcnt);
694                                 if (n <= 0)
695                                         return;
696                                 rcvd += n;
697                         } else {
698                                 n = read(rem, waste, sizeof(waste));
699                                 if (n <= 0)
700                                         return;
701                         }
702                         continue;
703                 default:
704                         return;
705                 }
706         }
707         if (mark & TIOCPKT_WINDOW) {
708                 /* Let server know about window size changes */
709                 (void)kill(ppid, SIGUSR1);
710         }
711         if (!eight && (mark & TIOCPKT_NOSTOP)) {
712                 (void)ioctl(0, TIOCGETP, (char *)&sb);
713                 sb.sg_flags &= ~CBREAK;
714                 sb.sg_flags |= RAW;
715                 (void)ioctl(0, TIOCSETN, (char *)&sb);
716                 notc.t_stopc = -1;
717                 notc.t_startc = -1;
718                 (void)ioctl(0, TIOCSETC, (char *)&notc);
719         }
720         if (!eight && (mark & TIOCPKT_DOSTOP)) {
721                 (void)ioctl(0, TIOCGETP, (char *)&sb);
722                 sb.sg_flags &= ~RAW;
723                 sb.sg_flags |= CBREAK;
724                 (void)ioctl(0, TIOCSETN, (char *)&sb);
725                 notc.t_stopc = deftc.t_stopc;
726                 notc.t_startc = deftc.t_startc;
727                 (void)ioctl(0, TIOCSETC, (char *)&notc);
728         }
729         if (mark & TIOCPKT_FLUSHWRITE) {
730                 (void)ioctl(1, TIOCFLUSH, (char *)&out);
731                 for (;;) {
732                         if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
733                                 warn("ioctl");
734                                 break;
735                         }
736                         if (atmark)
737                                 break;
738                         n = read(rem, waste, sizeof (waste));
739                         if (n <= 0)
740                                 break;
741                 }
742                 /*
743                  * Don't want any pending data to be output, so clear the recv
744                  * buffer.  If we were hanging on a write when interrupted,
745                  * don't want it to restart.  If we were reading, restart
746                  * anyway.
747                  */
748                 rcvcnt = 0;
749                 longjmp(rcvtop, 1);
750         }
751
752         /* oob does not do FLUSHREAD (alas!) */
753
754         /*
755          * If we filled the receive buffer while a read was pending, longjmp
756          * to the top to restart appropriately.  Don't abort a pending write,
757          * however, or we won't know how much was written.
758          */
759         if (rcvd && rcvstate == READING)
760                 longjmp(rcvtop, 1);
761 }
762
763 /* reader: read from remote: line -> 1 */
764 int
765 reader(omask)
766         int omask;
767 {
768         int pid, n, remaining;
769         char *bufp;
770
771 #if BSD >= 43 || defined(SUNOS4)
772         pid = getpid();         /* modern systems use positives for pid */
773 #else
774         pid = -getpid();        /* old broken systems use negatives */
775 #endif
776         (void)signal(SIGTTOU, SIG_IGN);
777         (void)signal(SIGURG, oob);
778         ppid = getppid();
779         (void)fcntl(rem, F_SETOWN, pid);
780         (void)setjmp(rcvtop);
781         (void)sigsetmask(omask);
782         bufp = rcvbuf;
783         for (;;) {
784                 while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
785                         rcvstate = WRITING;
786                         n = write(STDOUT_FILENO, bufp, remaining);
787                         if (n < 0) {
788                                 if (errno != EINTR)
789                                         return (-1);
790                                 continue;
791                         }
792                         bufp += n;
793                 }
794                 bufp = rcvbuf;
795                 rcvcnt = 0;
796                 rcvstate = READING;
797
798 #ifdef CRYPT
799 #ifdef KERBEROS
800                 if (doencrypt)
801                         rcvcnt = des_enc_read(rem, rcvbuf, sizeof(rcvbuf),
802                                 schedule, &cred.session);
803                 else
804 #endif
805 #endif
806                         rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf));
807                 if (rcvcnt == 0)
808                         return (0);
809                 if (rcvcnt < 0) {
810                         if (errno == EINTR)
811                                 continue;
812                         warn("read");
813                         return (-1);
814                 }
815         }
816 }
817
818 void
819 mode(f)
820         int f;
821 {
822         struct ltchars *ltc;
823         struct sgttyb sb;
824         struct tchars *tc;
825         int lflags;
826
827         (void)ioctl(0, TIOCGETP, (char *)&sb);
828         (void)ioctl(0, TIOCLGET, (char *)&lflags);
829         switch(f) {
830         case 0:
831                 sb.sg_flags &= ~(CBREAK|RAW|TBDELAY);
832                 sb.sg_flags |= defflags|tabflag;
833                 tc = &deftc;
834                 ltc = &defltc;
835                 sb.sg_kill = defkill;
836                 sb.sg_erase = deferase;
837                 lflags = deflflags;
838                 break;
839         case 1:
840                 sb.sg_flags |= (eight ? RAW : CBREAK);
841                 sb.sg_flags &= ~defflags;
842                 /* preserve tab delays, but turn off XTABS */
843                 if ((sb.sg_flags & TBDELAY) == XTABS)
844                         sb.sg_flags &= ~TBDELAY;
845                 tc = &notc;
846                 ltc = &noltc;
847                 sb.sg_kill = sb.sg_erase = -1;
848                 if (litout)
849                         lflags |= LLITOUT;
850                 break;
851         default:
852                 return;
853         }
854         (void)ioctl(0, TIOCSLTC, (char *)ltc);
855         (void)ioctl(0, TIOCSETC, (char *)tc);
856         (void)ioctl(0, TIOCSETN, (char *)&sb);
857         (void)ioctl(0, TIOCLSET, (char *)&lflags);
858 }
859
860 void
861 lostpeer(signo)
862         int signo;
863 {
864         (void)signal(SIGPIPE, SIG_IGN);
865         msg("\007connection closed.");
866         done(1);
867 }
868
869 /* copy SIGURGs to the child process. */
870 void
871 copytochild(signo)
872         int signo;
873 {
874         (void)kill(child, SIGURG);
875 }
876
877 void
878 msg(str)
879         char *str;
880 {
881         (void)fprintf(stderr, "rlogin: %s\r\n", str);
882 }
883
884 void
885 usage()
886 {
887         (void)fprintf(stderr,
888         "usage: rlogin [-46%s]%s[-e char] [-i localname] [-l username] host\n",
889 #ifdef KERBEROS
890 #ifdef CRYPT
891             "8DEKLdx", " [-k realm] ");
892 #else
893             "8DEKLd", " [-k realm] ");
894 #endif
895 #else
896             "8DELd", " ");
897 #endif
898         exit(1);
899 }
900
901 /*
902  * The following routine provides compatibility (such as it is) between older
903  * Suns and others.  Suns have only a `ttysize', so we convert it to a winsize.
904  */
905 #ifdef OLDSUN
906 int
907 get_window_size(fd, wp)
908         int fd;
909         struct winsize *wp;
910 {
911         struct ttysize ts;
912         int error;
913
914         if ((error = ioctl(0, TIOCGSIZE, &ts)) != 0)
915                 return (error);
916         wp->ws_row = ts.ts_lines;
917         wp->ws_col = ts.ts_cols;
918         wp->ws_xpixel = 0;
919         wp->ws_ypixel = 0;
920         return (0);
921 }
922 #endif
923
924 u_int
925 getescape(p)
926         register char *p;
927 {
928         long val;
929         int len;
930
931         if ((len = strlen(p)) == 1)     /* use any single char, including '\' */
932                 return ((u_int)*p);
933                                         /* otherwise, \nnn */
934         if (*p == '\\' && len >= 2 && len <= 4) {
935                 val = strtol(++p, NULL, 8);
936                 for (;;) {
937                         if (!*++p)
938                                 return ((u_int)val);
939                         if (*p < '0' || *p > '8')
940                                 break;
941                 }
942         }
943         msg("illegal option value -- e");
944         usage();
945         /* NOTREACHED */
946 }