rlogin.c revision 152397
1292932Sdim/* 2292932Sdim * Copyright (c) 1983, 1990, 1993 3353358Sdim * The Regents of the University of California. All rights reserved. 4353358Sdim * Copyright (c) 2002 Networks Associates Technology, Inc. 5353358Sdim * All rights reserved. 6292932Sdim * 7292932Sdim * Portions of this software were developed for the FreeBSD Project by 8292932Sdim * ThinkSec AS and NAI Labs, the Security Research Division of Network 9292932Sdim * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 10292932Sdim * ("CBOSS"), as part of the DARPA CHATS research program. 11292932Sdim * 12292932Sdim * Redistribution and use in source and binary forms, with or without 13292932Sdim * modification, are permitted provided that the following conditions 14292932Sdim * are met: 15292932Sdim * 1. Redistributions of source code must retain the above copyright 16292932Sdim * notice, this list of conditions and the following disclaimer. 17292932Sdim * 2. Redistributions in binary form must reproduce the above copyright 18292932Sdim * notice, this list of conditions and the following disclaimer in the 19292932Sdim * documentation and/or other materials provided with the distribution. 20314564Sdim * 3. All advertising materials mentioning features or use of this software 21292932Sdim * must display the following acknowledgement: 22314564Sdim * This product includes software developed by the University of 23314564Sdim * California, Berkeley and its contributors. 24292932Sdim * 4. Neither the name of the University nor the names of its contributors 25292932Sdim * may be used to endorse or promote products derived from this software 26292932Sdim * without specific prior written permission. 27309124Sdim * 28292932Sdim * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 29292932Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30292932Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31309124Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 32292932Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33321369Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 34321369Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35321369Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 36321369Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 37344779Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 38321369Sdim * SUCH DAMAGE. 39292932Sdim */ 40292932Sdim 41292932Sdim#ifndef lint 42292932Sdimstatic const char copyright[] = 43292932Sdim"@(#) Copyright (c) 1983, 1990, 1993\n\ 44292932Sdim The Regents of the University of California. All rights reserved.\n"; 45292932Sdim#endif /* not lint */ 46360784Sdim 47360784Sdim#if 0 48314564Sdim#ifndef lint 49314564Sdimstatic const char sccsid[] = "@(#)rlogin.c 8.1 (Berkeley) 6/6/93"; 50292932Sdim#endif /* not lint */ 51314564Sdim#endif 52292932Sdim 53292932Sdim#include <sys/cdefs.h> 54314564Sdim__FBSDID("$FreeBSD: head/usr.bin/rlogin/rlogin.c 152397 2005-11-13 21:03:56Z dwmalone $"); 55314564Sdim 56314564Sdim/* 57314564Sdim * rlogin - remote login 58314564Sdim */ 59314564Sdim 60314564Sdim#include <sys/param.h> 61292932Sdim#include <sys/ioctl.h> 62292932Sdim#include <sys/socket.h> 63314564Sdim#include <sys/time.h> 64314564Sdim#include <sys/resource.h> 65353358Sdim#include <sys/wait.h> 66292932Sdim 67314564Sdim#include <netinet/in.h> 68292932Sdim#include <netinet/in_systm.h> 69292932Sdim#include <netinet/ip.h> 70314564Sdim#include <netinet/tcp.h> 71314564Sdim 72314564Sdim#include <err.h> 73314564Sdim#include <errno.h> 74314564Sdim#include <fcntl.h> 75314564Sdim#include <libutil.h> 76353358Sdim#include <netdb.h> 77353358Sdim#include <paths.h> 78353358Sdim#include <pwd.h> 79353358Sdim#include <setjmp.h> 80353358Sdim#include <termios.h> 81353358Sdim#include <signal.h> 82314564Sdim#include <stdio.h> 83292932Sdim#include <stdlib.h> 84292932Sdim#include <string.h> 85292932Sdim#include <unistd.h> 86314564Sdim 87314564Sdim#ifndef TIOCPKT_WINDOW 88314564Sdim#define TIOCPKT_WINDOW 0x80 89292932Sdim#endif 90314564Sdim 91292932Sdim/* concession to Sun */ 92292932Sdim#ifndef SIGUSR1 93314564Sdim#define SIGUSR1 30 94314564Sdim#endif 95314564Sdim 96292932Sdimint eight, rem; 97292932Sdimstruct termios deftty; 98314564Sdim 99314564Sdimint family = PF_UNSPEC; 100314564Sdim 101314564Sdimint noescape; 102314564Sdimu_char escapechar = '~'; 103314564Sdim 104314564Sdim#define get_window_size(fd, wp) ioctl(fd, TIOCGWINSZ, wp) 105314564Sdimstruct winsize winsize; 106292932Sdim 107292932Sdimvoid catch_child(int); 108314564Sdimvoid copytochild(int); 109292932Sdimvoid doit(long) __dead2; 110314564Sdimvoid done(int) __dead2; 111314564Sdimvoid echo(char); 112314564Sdimu_int getescape(const char *); 113314564Sdimvoid lostpeer(int); 114292932Sdimvoid mode(int); 115292932Sdimvoid msg(const char *); 116314564Sdimvoid oob(int); 117314564Sdimint reader(int); 118314564Sdimvoid sendwindow(void); 119314564Sdimvoid setsignal(int); 120292932Sdimvoid sigwinch(int); 121314564Sdimvoid stop(char); 122353358Sdimvoid usage(void) __dead2; 123292932Sdimvoid writer(void); 124314564Sdimvoid writeroob(int); 125314564Sdim 126292932Sdimint 127314564Sdimmain(int argc, char *argv[]) 128314564Sdim{ 129314564Sdim struct passwd *pw; 130314564Sdim struct servent *sp; 131353358Sdim struct termios tty; 132292932Sdim long omask; 133314564Sdim int argoff, ch, dflag, Dflag, one; 134314564Sdim uid_t uid; 135292932Sdim char *host, *localname, *p, *user, term[1024]; 136314564Sdim speed_t ospeed; 137314564Sdim struct sockaddr_storage ss; 138314564Sdim socklen_t sslen; 139292932Sdim size_t len, len2; 140314564Sdim int i; 141314564Sdim 142292932Sdim argoff = dflag = Dflag = 0; 143314564Sdim one = 1; 144353358Sdim host = localname = user = NULL; 145292932Sdim 146314564Sdim if ((p = rindex(argv[0], '/'))) 147292932Sdim ++p; 148314564Sdim else 149314564Sdim p = argv[0]; 150292932Sdim 151353358Sdim if (strcmp(p, "rlogin")) 152292932Sdim host = p; 153292932Sdim 154314564Sdim /* handle "rlogin host flags" */ 155314564Sdim if (!host && argc > 2 && argv[1][0] != '-') { 156292932Sdim host = argv[1]; 157292932Sdim argoff = 1; 158360784Sdim } 159360784Sdim 160360784Sdim#define OPTIONS "468DEde:i:l:" 161360784Sdim while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != -1) 162360784Sdim switch(ch) { 163360784Sdim case '4': 164360784Sdim family = PF_INET; 165360784Sdim break; 166360784Sdim 167314564Sdim case '6': 168314564Sdim family = PF_INET6; 169314564Sdim break; 170292932Sdim 171314564Sdim case '8': 172314564Sdim eight = 1; 173292932Sdim break; 174314564Sdim case 'D': 175292932Sdim Dflag = 1; 176314564Sdim break; 177292932Sdim case 'E': 178360784Sdim noescape = 1; 179360784Sdim break; 180292932Sdim case 'd': 181360784Sdim dflag = 1; 182360784Sdim break; 183292932Sdim case 'e': 184360784Sdim noescape = 0; 185360784Sdim escapechar = getescape(optarg); 186360784Sdim break; 187360784Sdim case 'i': 188360784Sdim if (getuid() != 0) 189360784Sdim errx(1, "-i user: permission denied"); 190314564Sdim localname = optarg; 191314564Sdim break; 192314564Sdim case 'l': 193292932Sdim user = optarg; 194360784Sdim break; 195360784Sdim case '?': 196314564Sdim default: 197314564Sdim usage(); 198292932Sdim } 199314564Sdim optind += argoff; 200292932Sdim 201360784Sdim /* if haven't gotten a host yet, do so */ 202360784Sdim if (!host && !(host = argv[optind++])) 203292932Sdim usage(); 204314564Sdim 205314564Sdim if (argv[optind]) 206292932Sdim usage(); 207360784Sdim 208314564Sdim if (!(pw = getpwuid(uid = getuid()))) 209314564Sdim errx(1, "unknown user id"); 210314564Sdim if (!user) 211314564Sdim user = pw->pw_name; 212360784Sdim if (!localname) 213292932Sdim localname = pw->pw_name; 214360784Sdim 215360784Sdim sp = NULL; 216314564Sdim sp = getservbyname("login", "tcp"); 217292932Sdim if (sp == NULL) 218314564Sdim errx(1, "login/tcp: unknown service"); 219314564Sdim 220292932Sdim if ((p = getenv("TERM")) != NULL) 221360784Sdim (void)strlcpy(term, p, sizeof(term)); 222360784Sdim len = strlen(term); 223292932Sdim if (len < (sizeof(term) - 1) && tcgetattr(0, &tty) == 0) { 224314564Sdim /* start at 2 to include the / */ 225292932Sdim for (ospeed = i = cfgetospeed(&tty), len2 = 2; i > 9; len2++) 226314564Sdim i /= 10; 227360784Sdim if (len + len2 < sizeof(term)) 228292932Sdim (void)snprintf(term + len, len2 + 1, "/%d", ospeed); 229360784Sdim } 230314564Sdim 231314564Sdim (void)get_window_size(0, &winsize); 232292932Sdim 233314564Sdim (void)signal(SIGPIPE, lostpeer); 234314564Sdim /* will use SIGUSR1 for window size hack, so hold it off */ 235292932Sdim omask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1)); 236314564Sdim /* 237314564Sdim * We set SIGURG and SIGUSR1 below so that an 238360784Sdim * incoming signal will be held pending rather than being 239292932Sdim * discarded. Note that these routines will be ready to get 240360784Sdim * a signal by the time that they are unblocked below. 241314564Sdim */ 242314564Sdim (void)signal(SIGURG, copytochild); 243292932Sdim (void)signal(SIGUSR1, writeroob); 244314564Sdim 245314564Sdim rem = rcmd_af(&host, sp->s_port, localname, user, term, 0, family); 246292932Sdim 247314564Sdim if (rem < 0) 248314564Sdim exit(1); 249314564Sdim 250314564Sdim if (dflag && 251314564Sdim setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0) 252292932Sdim warn("setsockopt"); 253360784Sdim if (Dflag && 254314564Sdim setsockopt(rem, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) < 0) 255292932Sdim warn("setsockopt NODELAY (ignored)"); 256314564Sdim 257314564Sdim sslen = sizeof(ss); 258360784Sdim one = IPTOS_LOWDELAY; 259292932Sdim if (getsockname(rem, (struct sockaddr *)&ss, &sslen) == 0 && 260360784Sdim ss.ss_family == AF_INET) { 261360784Sdim if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one, 262314564Sdim sizeof(int)) < 0) 263314564Sdim warn("setsockopt TOS (ignored)"); 264292932Sdim } else 265314564Sdim if (ss.ss_family == AF_INET) 266314564Sdim warn("setsockopt getsockname failed"); 267292932Sdim 268314564Sdim (void)setuid(uid); 269341825Sdim doit(omask); 270292932Sdim /*NOTREACHED*/ 271314564Sdim} 272314564Sdim 273314564Sdimint child; 274314564Sdim 275314564Sdimvoid 276314564Sdimdoit(long omask) 277314564Sdim{ 278314564Sdim 279314564Sdim (void)signal(SIGINT, SIG_IGN); 280314564Sdim setsignal(SIGHUP); 281314564Sdim setsignal(SIGQUIT); 282314564Sdim mode(1); 283314564Sdim child = fork(); 284314564Sdim if (child == -1) { 285314564Sdim warn("fork"); 286314564Sdim done(1); 287314564Sdim } 288314564Sdim if (child == 0) { 289314564Sdim if (reader(omask) == 0) { 290314564Sdim msg("connection closed"); 291314564Sdim exit(0); 292314564Sdim } 293314564Sdim sleep(1); 294314564Sdim msg("\007connection closed"); 295314564Sdim exit(1); 296314564Sdim } 297314564Sdim 298360784Sdim /* 299314564Sdim * We may still own the socket, and may have a pending SIGURG (or might 300360784Sdim * receive one soon) that we really want to send to the reader. When 301314564Sdim * one of these comes in, the trap copytochild simply copies such 302314564Sdim * signals to the child. We can now unblock SIGURG and SIGUSR1 303314564Sdim * that were set above. 304314564Sdim */ 305292932Sdim (void)sigsetmask(omask); 306314564Sdim (void)signal(SIGCHLD, catch_child); 307314564Sdim writer(); 308314564Sdim msg("closed connection"); 309314564Sdim done(0); 310314564Sdim} 311314564Sdim 312292932Sdim/* trap a signal, unless it is being ignored. */ 313314564Sdimvoid 314314564Sdimsetsignal(int sig) 315344779Sdim{ 316344779Sdim int omask = sigblock(sigmask(sig)); 317344779Sdim 318314564Sdim if (signal(sig, exit) == SIG_IGN) 319314564Sdim (void)signal(sig, SIG_IGN); 320292932Sdim (void)sigsetmask(omask); 321360784Sdim} 322292932Sdim 323314564Sdimvoid 324314564Sdimdone(int status) 325314564Sdim{ 326314564Sdim int w, wstatus; 327314564Sdim 328292932Sdim mode(0); 329314564Sdim if (child > 0) { 330314564Sdim /* make sure catch_child does not snap it up */ 331314564Sdim (void)signal(SIGCHLD, SIG_DFL); 332292932Sdim if (kill(child, SIGKILL) >= 0) 333360784Sdim while ((w = wait(&wstatus)) > 0 && w != child); 334314564Sdim } 335292932Sdim exit(status); 336314564Sdim} 337292932Sdim 338360784Sdimint dosigwinch; 339360784Sdim 340292932Sdim/* 341314564Sdim * This is called when the reader process gets the out-of-band (urgent) 342292932Sdim * request to turn on the window-changing protocol. 343314564Sdim */ 344314564Sdim/* ARGSUSED */ 345353358Sdimvoid 346353358Sdimwriteroob(int signo __unused) 347314564Sdim{ 348292932Sdim if (dosigwinch == 0) { 349341825Sdim sendwindow(); 350341825Sdim (void)signal(SIGWINCH, sigwinch); 351341825Sdim } 352314564Sdim dosigwinch = 1; 353314564Sdim} 354314564Sdim 355292932Sdim/* ARGSUSED */ 356314564Sdimvoid 357314564Sdimcatch_child(int signo __unused) 358360784Sdim{ 359292932Sdim pid_t pid; 360314564Sdim int status; 361314564Sdim 362314564Sdim for (;;) { 363292932Sdim pid = wait3(&status, WNOHANG|WUNTRACED, NULL); 364314564Sdim if (pid == 0) 365292932Sdim return; 366314564Sdim /* if the child (reader) dies, just quit */ 367314564Sdim if (pid < 0 || (pid == child && !WIFSTOPPED(status))) 368314564Sdim done(WTERMSIG(status) | WEXITSTATUS(status)); 369314564Sdim } 370314564Sdim /* NOTREACHED */ 371360784Sdim} 372360784Sdim 373314564Sdim/* 374314564Sdim * writer: write to remote: 0 -> line. 375314564Sdim * ~. terminate 376314564Sdim * ~^Z suspend rlogin process. 377314564Sdim * ~<delayed-suspend char> suspend rlogin process, but leave reader alone. 378314564Sdim */ 379314564Sdimvoid 380314564Sdimwriter(void) 381314564Sdim{ 382314564Sdim int bol, local, n; 383314564Sdim char c; 384314564Sdim 385360784Sdim bol = 1; /* beginning of line */ 386314564Sdim local = 0; 387360784Sdim for (;;) { 388360784Sdim n = read(STDIN_FILENO, &c, 1); 389314564Sdim if (n <= 0) { 390314564Sdim if (n < 0 && errno == EINTR) 391314564Sdim continue; 392292932Sdim break; 393292932Sdim } 394314564Sdim /* 395292932Sdim * If we're at the beginning of the line and recognize a 396314564Sdim * command character, then we echo locally. Otherwise, 397314564Sdim * characters are echo'd remotely. If the command character 398314564Sdim * is doubled, this acts as a force and local echo is 399360784Sdim * suppressed. 400360784Sdim */ 401314564Sdim if (bol) { 402314564Sdim bol = 0; 403314564Sdim if (!noescape && c == escapechar) { 404292932Sdim local = 1; 405314564Sdim continue; 406314564Sdim } 407314564Sdim } else if (local) { 408292932Sdim local = 0; 409314564Sdim if (c == '.' || CCEQ(deftty.c_cc[VEOF], c)) { 410292932Sdim echo(c); 411314564Sdim break; 412314564Sdim } 413292932Sdim if (CCEQ(deftty.c_cc[VSUSP], c) || 414314564Sdim CCEQ(deftty.c_cc[VDSUSP], c)) { 415314564Sdim bol = 1; 416314564Sdim echo(c); 417314564Sdim stop(c); 418292932Sdim continue; 419314564Sdim } 420292932Sdim if (c != escapechar) 421314564Sdim (void)write(rem, &escapechar, 1); 422314564Sdim } 423314564Sdim 424292932Sdim if (write(rem, &c, 1) == 0) { 425314564Sdim msg("line gone"); 426314564Sdim break; 427292932Sdim } 428314564Sdim bol = CCEQ(deftty.c_cc[VKILL], c) || 429314564Sdim CCEQ(deftty.c_cc[VEOF], c) || 430314564Sdim CCEQ(deftty.c_cc[VINTR], c) || 431353358Sdim CCEQ(deftty.c_cc[VSUSP], c) || 432314564Sdim c == '\r' || c == '\n'; 433353358Sdim } 434353358Sdim} 435353358Sdim 436314564Sdimvoid 437314564Sdimecho(char c) 438314564Sdim{ 439314564Sdim char *p; 440314564Sdim char buf[8]; 441314564Sdim 442314564Sdim p = buf; 443314564Sdim c &= 0177; 444314564Sdim *p++ = escapechar; 445360784Sdim if (c < ' ') { 446360784Sdim *p++ = '^'; 447292932Sdim *p++ = c + '@'; 448314564Sdim } else if (c == 0177) { 449314564Sdim *p++ = '^'; 450314564Sdim *p++ = '?'; 451314564Sdim } else 452314564Sdim *p++ = c; 453314564Sdim *p++ = '\r'; 454314564Sdim *p++ = '\n'; 455314564Sdim (void)write(STDOUT_FILENO, buf, p - buf); 456314564Sdim} 457314564Sdim 458314564Sdimvoid 459314564Sdimstop(char cmdc) 460314564Sdim{ 461314564Sdim mode(0); 462314564Sdim (void)signal(SIGCHLD, SIG_IGN); 463314564Sdim (void)kill(CCEQ(deftty.c_cc[VSUSP], cmdc) ? 0 : getpid(), SIGTSTP); 464341825Sdim (void)signal(SIGCHLD, catch_child); 465292932Sdim mode(1); 466314564Sdim sigwinch(0); /* check for size changes */ 467292932Sdim} 468314564Sdim 469314564Sdim/* ARGSUSED */ 470314564Sdimvoid 471314564Sdimsigwinch(int signo __unused) 472314564Sdim{ 473292932Sdim struct winsize ws; 474314564Sdim 475292932Sdim if (dosigwinch && get_window_size(0, &ws) == 0 && 476353358Sdim bcmp(&ws, &winsize, sizeof(ws))) { 477314564Sdim winsize = ws; 478292932Sdim sendwindow(); 479314564Sdim } 480314564Sdim} 481314564Sdim 482314564Sdim/* 483353358Sdim * Send the window size to the server via the magic escape 484353358Sdim */ 485314564Sdimvoid 486292932Sdimsendwindow(void) 487353358Sdim{ 488292932Sdim struct winsize *wp; 489314564Sdim char obuf[4 + sizeof (struct winsize)]; 490314564Sdim 491292932Sdim wp = (struct winsize *)(obuf+4); 492314564Sdim obuf[0] = 0377; 493314564Sdim obuf[1] = 0377; 494314564Sdim obuf[2] = 's'; 495314564Sdim obuf[3] = 's'; 496314564Sdim wp->ws_row = htons(winsize.ws_row); 497314564Sdim wp->ws_col = htons(winsize.ws_col); 498314564Sdim wp->ws_xpixel = htons(winsize.ws_xpixel); 499314564Sdim wp->ws_ypixel = htons(winsize.ws_ypixel); 500314564Sdim 501314564Sdim (void)write(rem, obuf, sizeof(obuf)); 502314564Sdim} 503314564Sdim 504314564Sdim/* 505314564Sdim * reader: read from remote: line -> 1 506314564Sdim */ 507314564Sdim#define READING 1 508314564Sdim#define WRITING 2 509314564Sdim 510321369Sdimjmp_buf rcvtop; 511314564Sdimint rcvcnt, rcvstate; 512321369Sdimpid_t ppid; 513314564Sdimchar rcvbuf[8 * 1024]; 514314564Sdim 515314564Sdim/* ARGSUSED */ 516314564Sdimvoid 517314564Sdimoob(int signo __unused) 518314564Sdim{ 519314564Sdim struct termios tty; 520314564Sdim int atmark, n, rcvd; 521314564Sdim char waste[BUFSIZ], mark; 522314564Sdim 523314564Sdim rcvd = 0; 524314564Sdim while (recv(rem, &mark, 1, MSG_OOB) < 0) { 525314564Sdim switch (errno) { 526314564Sdim case EWOULDBLOCK: 527314564Sdim /* 528314564Sdim * Urgent data not here yet. It may not be possible 529314564Sdim * to send it yet if we are blocked for output and 530314564Sdim * our input buffer is full. 531314564Sdim */ 532314564Sdim if (rcvcnt < (int)sizeof(rcvbuf)) { 533314564Sdim n = read(rem, rcvbuf + rcvcnt, 534314564Sdim sizeof(rcvbuf) - rcvcnt); 535314564Sdim if (n <= 0) 536314564Sdim return; 537314564Sdim rcvd += n; 538314564Sdim } else { 539360784Sdim n = read(rem, waste, sizeof(waste)); 540314564Sdim if (n <= 0) 541314564Sdim return; 542314564Sdim } 543314564Sdim continue; 544314564Sdim default: 545314564Sdim return; 546314564Sdim } 547314564Sdim } 548314564Sdim if (mark & TIOCPKT_WINDOW) { 549314564Sdim /* Let server know about window size changes */ 550314564Sdim (void)kill(ppid, SIGUSR1); 551314564Sdim } 552314564Sdim if (!eight && (mark & TIOCPKT_NOSTOP)) { 553314564Sdim (void)tcgetattr(0, &tty); 554314564Sdim tty.c_iflag &= ~IXON; 555314564Sdim (void)tcsetattr(0, TCSANOW, &tty); 556314564Sdim } 557314564Sdim if (!eight && (mark & TIOCPKT_DOSTOP)) { 558314564Sdim (void)tcgetattr(0, &tty); 559360784Sdim tty.c_iflag |= (deftty.c_iflag & IXON); 560360784Sdim (void)tcsetattr(0, TCSANOW, &tty); 561314564Sdim } 562360784Sdim if (mark & TIOCPKT_FLUSHWRITE) { 563360784Sdim (void)tcflush(1, TCIOFLUSH); 564314564Sdim for (;;) { 565314564Sdim if (ioctl(rem, SIOCATMARK, &atmark) < 0) { 566314564Sdim warn("ioctl"); 567314564Sdim break; 568314564Sdim } 569360784Sdim if (atmark) 570292932Sdim break; 571314564Sdim n = read(rem, waste, sizeof (waste)); 572314564Sdim if (n <= 0) 573314564Sdim break; 574292932Sdim } 575314564Sdim /* 576292932Sdim * Don't want any pending data to be output, so clear the recv 577314564Sdim * buffer. If we were hanging on a write when interrupted, 578360784Sdim * don't want it to restart. If we were reading, restart 579292932Sdim * anyway. 580314564Sdim */ 581314564Sdim rcvcnt = 0; 582292932Sdim longjmp(rcvtop, 1); 583314564Sdim } 584314564Sdim 585292932Sdim /* oob does not do FLUSHREAD (alas!) */ 586314564Sdim 587314564Sdim /* 588292932Sdim * If we filled the receive buffer while a read was pending, longjmp 589314564Sdim * to the top to restart appropriately. Don't abort a pending write, 590360784Sdim * however, or we won't know how much was written. 591360784Sdim */ 592292932Sdim if (rcvd && rcvstate == READING) 593314564Sdim longjmp(rcvtop, 1); 594314564Sdim} 595292932Sdim 596314564Sdim/* reader: read from remote: line -> 1 */ 597314564Sdimint 598292932Sdimreader(int omask) 599314564Sdim{ 600314564Sdim int n, remaining; 601314564Sdim char *bufp; 602314564Sdim pid_t pid; 603314564Sdim 604314564Sdim pid = getpid(); 605314564Sdim (void)signal(SIGTTOU, SIG_IGN); 606314564Sdim (void)signal(SIGURG, oob); 607292932Sdim (void)signal(SIGUSR1, oob); /* When propogating SIGURG from parent */ 608314564Sdim ppid = getppid(); 609292932Sdim (void)fcntl(rem, F_SETOWN, pid); 610360784Sdim (void)setjmp(rcvtop); 611360784Sdim (void)sigsetmask(omask); 612360784Sdim bufp = rcvbuf; 613360784Sdim for (;;) { 614360784Sdim while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) { 615314564Sdim rcvstate = WRITING; 616314564Sdim n = write(STDOUT_FILENO, bufp, remaining); 617314564Sdim if (n < 0) { 618292932Sdim if (errno != EINTR) 619314564Sdim return (-1); 620314564Sdim continue; 621314564Sdim } 622314564Sdim bufp += n; 623314564Sdim } 624314564Sdim bufp = rcvbuf; 625360784Sdim rcvcnt = 0; 626292932Sdim rcvstate = READING; 627314564Sdim 628314564Sdim rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf)); 629314564Sdim if (rcvcnt == 0) 630292932Sdim return (0); 631314564Sdim if (rcvcnt < 0) { 632314564Sdim if (errno == EINTR) 633292932Sdim continue; 634314564Sdim warn("read"); 635292932Sdim return (-1); 636314564Sdim } 637360784Sdim } 638360784Sdim} 639292932Sdim 640314564Sdimvoid 641314564Sdimmode(int f) 642314564Sdim{ 643292932Sdim struct termios tty; 644314564Sdim 645314564Sdim switch (f) { 646292932Sdim case 0: 647314564Sdim (void)tcsetattr(0, TCSANOW, &deftty); 648292932Sdim break; 649314564Sdim case 1: 650314564Sdim (void)tcgetattr(0, &deftty); 651314564Sdim tty = deftty; 652314564Sdim /* This is loosely derived from sys/kern/tty_compat.c. */ 653314564Sdim tty.c_lflag &= ~(ECHO|ICANON|ISIG|IEXTEN); 654314564Sdim tty.c_iflag &= ~ICRNL; 655314564Sdim tty.c_oflag &= ~OPOST; 656292932Sdim tty.c_cc[VMIN] = 1; 657314564Sdim tty.c_cc[VTIME] = 0; 658360784Sdim if (eight) { 659360784Sdim tty.c_iflag &= IXOFF; 660292932Sdim tty.c_cflag &= ~(CSIZE|PARENB); 661314564Sdim tty.c_cflag |= CS8; 662314564Sdim } 663292932Sdim (void)tcsetattr(0, TCSANOW, &tty); 664314564Sdim break; 665314564Sdim default: 666292932Sdim return; 667314564Sdim } 668360784Sdim} 669360784Sdim 670292932Sdim/* ARGSUSED */ 671314564Sdimvoid 672314564Sdimlostpeer(int signo __unused) 673314564Sdim{ 674292932Sdim (void)signal(SIGPIPE, SIG_IGN); 675314564Sdim msg("\007connection closed"); 676314564Sdim done(1); 677314564Sdim} 678314564Sdim 679314564Sdim/* copy SIGURGs to the child process via SIGUSR1. */ 680314564Sdim/* ARGSUSED */ 681314564Sdimvoid 682314564Sdimcopytochild(int signo __unused) 683314564Sdim{ 684314564Sdim (void)kill(child, SIGUSR1); 685314564Sdim} 686314564Sdim 687314564Sdimvoid 688314564Sdimmsg(const char *str) 689314564Sdim{ 690314564Sdim (void)fprintf(stderr, "rlogin: %s\r\n", str); 691314564Sdim} 692314564Sdim 693314564Sdimvoid 694314564Sdimusage(void) 695314564Sdim{ 696314564Sdim (void)fprintf(stderr, 697314564Sdim "usage: rlogin [-46%s]%s[-e char] [-i localname] [-l username] host\n", 698314564Sdim "8DEd", " "); 699314564Sdim exit(1); 700314564Sdim} 701314564Sdim 702314564Sdimu_int 703314564Sdimgetescape(const char *p) 704314564Sdim{ 705314564Sdim long val; 706314564Sdim size_t len; 707314564Sdim 708314564Sdim if ((len = strlen(p)) == 1) /* use any single char, including '\' */ 709314564Sdim return ((u_int)*p); 710314564Sdim /* otherwise, \nnn */ 711360784Sdim if (*p == '\\' && len >= 2 && len <= 4) { 712360784Sdim val = strtol(++p, NULL, 8); 713360784Sdim for (;;) { 714360784Sdim if (!*++p) 715360784Sdim return ((u_int)val); 716360784Sdim if (*p < '0' || *p > '8') 717314564Sdim break; 718314564Sdim } 719353358Sdim } 720314564Sdim msg("illegal option value -- e"); 721314564Sdim usage(); 722360784Sdim /* NOTREACHED */ 723292932Sdim} 724314564Sdim