1/* 2 * tty.c - code for handling serial ports in pppd. 3 * 4 * Copyright (C) 2000 Paul Mackerras. 5 * All rights reserved. 6 * 7 * Portions Copyright (c) 1989 Carnegie Mellon University. 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms are permitted 11 * provided that the above copyright notice and this paragraph are 12 * duplicated in all such forms and that any documentation, 13 * advertising materials, and other materials related to such 14 * distribution and use acknowledge that the software was developed 15 * by Carnegie Mellon University. The name of the 16 * University may not be used to endorse or promote products derived 17 * from this software without specific prior written permission. 18 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 20 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 21 */ 22 23#define RCSID "$Id: tty.c,v 1.1.1.1 2008/10/15 03:30:13 james26_jang Exp $" 24 25#include <stdio.h> 26#include <ctype.h> 27#include <stdlib.h> 28#include <string.h> 29#include <unistd.h> 30#include <signal.h> 31#include <errno.h> 32#include <fcntl.h> 33#include <syslog.h> 34#include <netdb.h> 35#include <utmp.h> 36#include <pwd.h> 37#include <setjmp.h> 38#include <sys/param.h> 39#include <sys/types.h> 40#include <sys/wait.h> 41#include <sys/time.h> 42#include <sys/resource.h> 43#include <sys/stat.h> 44#include <sys/socket.h> 45#include <netinet/in.h> 46#include <arpa/inet.h> 47 48#include "pppd.h" 49#include "fsm.h" 50#include "lcp.h" 51 52void tty_process_extra_options __P((void)); 53void tty_check_options __P((void)); 54int connect_tty __P((void)); 55void disconnect_tty __P((void)); 56void tty_close_fds __P((void)); 57void cleanup_tty __P((void)); 58void tty_do_send_config __P((int, u_int32_t, int, int)); 59 60static int setdevname __P((char *, char **, int)); 61static int setspeed __P((char *, char **, int)); 62static int setxonxoff __P((char **)); 63static int setescape __P((char **)); 64static void printescape __P((option_t *, void (*)(void *, char *,...),void *)); 65static void finish_tty __P((void)); 66static int start_charshunt __P((int, int)); 67static void stop_charshunt __P((void *, int)); 68static void charshunt_done __P((void *)); 69static void charshunt __P((int, int, char *)); 70static int record_write __P((FILE *, int code, u_char *buf, int nb, 71 struct timeval *)); 72static int open_socket __P((char *)); 73static void maybe_relock __P((void *, int)); 74 75static int pty_master; /* fd for master side of pty */ 76static int pty_slave; /* fd for slave side of pty */ 77static int real_ttyfd; /* fd for actual serial port (not pty) */ 78static int ttyfd; /* Serial port file descriptor */ 79static char speed_str[16]; /* Serial port speed as string */ 80 81mode_t tty_mode = (mode_t)-1; /* Original access permissions to tty */ 82int baud_rate; /* Actual bits/second for serial device */ 83char *callback_script; /* script for doing callback */ 84int charshunt_pid; /* Process ID for charshunt */ 85int locked; /* lock() has succeeded */ 86struct stat devstat; /* result of stat() on devnam */ 87 88/* option variables */ 89int crtscts = 0; /* Use hardware flow control */ 90bool modem = 1; /* Use modem control lines */ 91int inspeed = 0; /* Input/Output speed requested */ 92bool lockflag = 0; /* Create lock file to lock the serial dev */ 93char *initializer = NULL; /* Script to initialize physical link */ 94char *connect_script = NULL; /* Script to establish physical link */ 95char *disconnect_script = NULL; /* Script to disestablish physical link */ 96char *welcomer = NULL; /* Script to run after phys link estab. */ 97char *ptycommand = NULL; /* Command to run on other side of pty */ 98bool notty = 0; /* Stdin/out is not a tty */ 99char *record_file = NULL; /* File to record chars sent/received */ 100int max_data_rate; /* max bytes/sec through charshunt */ 101bool sync_serial = 0; /* Device is synchronous serial device */ 102char *pty_socket = NULL; /* Socket to connect to pty */ 103int using_pty = 0; /* we're allocating a pty as the device */ 104 105extern uid_t uid; 106extern int kill_link; 107 108extern int privopen; /* don't lock, open device as root */ 109 110u_int32_t xmit_accm[8]; /* extended transmit ACCM */ 111 112/* option descriptors */ 113option_t tty_options[] = { 114 /* device name must be first, or change connect_tty() below! */ 115 { "device name", o_wild, (void *) &setdevname, 116 "Serial port device name", 117 OPT_DEVNAM | OPT_PRIVFIX | OPT_NOARG | OPT_A2STRVAL | OPT_STATIC, 118 devnam}, 119 120 { "tty speed", o_wild, (void *) &setspeed, 121 "Baud rate for serial port", 122 OPT_PRIO | OPT_NOARG | OPT_A2STRVAL | OPT_STATIC, speed_str }, 123 124 { "lock", o_bool, &lockflag, 125 "Lock serial device with UUCP-style lock file", OPT_PRIO | 1 }, 126 { "nolock", o_bool, &lockflag, 127 "Don't lock serial device", OPT_PRIOSUB | OPT_PRIV }, 128 129 { "init", o_string, &initializer, 130 "A program to initialize the device", OPT_PRIO | OPT_PRIVFIX }, 131 132 { "connect", o_string, &connect_script, 133 "A program to set up a connection", OPT_PRIO | OPT_PRIVFIX }, 134 135 { "disconnect", o_string, &disconnect_script, 136 "Program to disconnect serial device", OPT_PRIO | OPT_PRIVFIX }, 137 138 { "welcome", o_string, &welcomer, 139 "Script to welcome client", OPT_PRIO | OPT_PRIVFIX }, 140 141 { "pty", o_string, &ptycommand, 142 "Script to run on pseudo-tty master side", 143 OPT_PRIO | OPT_PRIVFIX | OPT_DEVNAM }, 144 145 { "notty", o_bool, ¬ty, 146 "Input/output is not a tty", OPT_DEVNAM | 1 }, 147 148 { "socket", o_string, &pty_socket, 149 "Send and receive over socket, arg is host:port", 150 OPT_PRIO | OPT_DEVNAM }, 151 152 { "record", o_string, &record_file, 153 "Record characters sent/received to file", OPT_PRIO }, 154 155 { "crtscts", o_int, &crtscts, 156 "Set hardware (RTS/CTS) flow control", 157 OPT_PRIO | OPT_NOARG | OPT_VAL(1) }, 158 { "cdtrcts", o_int, &crtscts, 159 "Set alternate hardware (DTR/CTS) flow control", 160 OPT_PRIOSUB | OPT_NOARG | OPT_VAL(2) }, 161 { "nocrtscts", o_int, &crtscts, 162 "Disable hardware flow control", 163 OPT_PRIOSUB | OPT_NOARG | OPT_VAL(-1) }, 164 { "-crtscts", o_int, &crtscts, 165 "Disable hardware flow control", 166 OPT_PRIOSUB | OPT_ALIAS | OPT_NOARG | OPT_VAL(-1) }, 167 { "nocdtrcts", o_int, &crtscts, 168 "Disable hardware flow control", 169 OPT_PRIOSUB | OPT_ALIAS | OPT_NOARG | OPT_VAL(-1) }, 170 { "xonxoff", o_special_noarg, (void *)setxonxoff, 171 "Set software (XON/XOFF) flow control", OPT_PRIOSUB }, 172 173 { "modem", o_bool, &modem, 174 "Use modem control lines", OPT_PRIO | 1 }, 175 { "local", o_bool, &modem, 176 "Don't use modem control lines", OPT_PRIOSUB | 0 }, 177 178 { "sync", o_bool, &sync_serial, 179 "Use synchronous HDLC serial encoding", 1 }, 180 181 { "datarate", o_int, &max_data_rate, 182 "Maximum data rate in bytes/sec (with pty, notty or record option)", 183 OPT_PRIO }, 184 185 { "escape", o_special, (void *)setescape, 186 "List of character codes to escape on transmission", 187 OPT_A2PRINTER, (void *)printescape }, 188 189 { NULL } 190}; 191 192 193struct channel tty_channel = { 194 tty_options, 195 &tty_process_extra_options, 196 &tty_check_options, 197 &connect_tty, 198 &disconnect_tty, 199 &tty_establish_ppp, 200 &tty_disestablish_ppp, 201 &tty_do_send_config, 202 &tty_recv_config, 203 &cleanup_tty, 204 &tty_close_fds 205}; 206 207/* 208 * setspeed - Set the serial port baud rate. 209 * If doit is 0, the call is to check whether this option is 210 * potentially a speed value. 211 */ 212static int 213setspeed(arg, argv, doit) 214 char *arg; 215 char **argv; 216 int doit; 217{ 218 char *ptr; 219 int spd; 220 221 spd = strtol(arg, &ptr, 0); 222 if (ptr == arg || *ptr != 0 || spd == 0) 223 return 0; 224 if (doit) { 225 inspeed = spd; 226 slprintf(speed_str, sizeof(speed_str), "%d", spd); 227 } 228 return 1; 229} 230 231 232/* 233 * setdevname - Set the device name. 234 * If doit is 0, the call is to check whether this option is 235 * potentially a device name. 236 */ 237static int 238setdevname(cp, argv, doit) 239 char *cp; 240 char **argv; 241 int doit; 242{ 243 struct stat statbuf; 244 char dev[MAXPATHLEN]; 245 246 if (*cp == 0) 247 return 0; 248 249 if (strncmp("/dev/", cp, 5) != 0) { 250 strlcpy(dev, "/dev/", sizeof(dev)); 251 strlcat(dev, cp, sizeof(dev)); 252 cp = dev; 253 } 254 255 /* 256 * Check if there is a character device by this name. 257 */ 258 if (stat(cp, &statbuf) < 0) { 259 if (!doit) 260 return errno != ENOENT; 261 option_error("Couldn't stat %s: %m", cp); 262 return 0; 263 } 264 if (!S_ISCHR(statbuf.st_mode)) { 265 if (doit) 266 option_error("%s is not a character device", cp); 267 return 0; 268 } 269 270 if (doit) { 271 strlcpy(devnam, cp, sizeof(devnam)); 272 devstat = statbuf; 273 default_device = 0; 274 } 275 276 return 1; 277} 278 279static int 280setxonxoff(argv) 281 char **argv; 282{ 283 lcp_wantoptions[0].asyncmap |= 0x000A0000; /* escape ^S and ^Q */ 284 lcp_wantoptions[0].neg_asyncmap = 1; 285 286 crtscts = -2; 287 return 1; 288} 289 290/* 291 * setescape - add chars to the set we escape on transmission. 292 */ 293static int 294setescape(argv) 295 char **argv; 296{ 297 int n, ret; 298 char *p, *endp; 299 300 p = *argv; 301 ret = 1; 302 while (*p) { 303 n = strtol(p, &endp, 16); 304 if (p == endp) { 305 option_error("escape parameter contains invalid hex number '%s'", 306 p); 307 return 0; 308 } 309 p = endp; 310 if (n < 0 || n == 0x5E || n > 0xFF) { 311 option_error("can't escape character 0x%x", n); 312 ret = 0; 313 } else 314 xmit_accm[n >> 5] |= 1 << (n & 0x1F); 315 while (*p == ',' || *p == ' ') 316 ++p; 317 } 318 lcp_allowoptions[0].asyncmap = xmit_accm[0]; 319 return ret; 320} 321 322static void 323printescape(opt, printer, arg) 324 option_t *opt; 325 void (*printer) __P((void *, char *, ...)); 326 void *arg; 327{ 328 int n; 329 int first = 1; 330 331 for (n = 0; n < 256; ++n) { 332 if (n == 0x7d) 333 n += 2; /* skip 7d, 7e */ 334 if (xmit_accm[n >> 5] & (1 << (n & 0x1f))) { 335 if (!first) 336 printer(arg, ","); 337 else 338 first = 0; 339 printer(arg, "%x", n); 340 } 341 } 342 if (first) 343 printer(arg, "oops # nothing escaped"); 344} 345 346/* 347 * tty_init - do various tty-related initializations. 348 */ 349void tty_init() 350{ 351 add_notifier(&pidchange, maybe_relock, 0); 352 the_channel = &tty_channel; 353 xmit_accm[3] = 0x60000000; 354} 355 356/* 357 * tty_process_extra_options - work out which tty device we are using 358 * and read its options file. 359 */ 360void tty_process_extra_options() 361{ 362 using_pty = notty || ptycommand != NULL || pty_socket != NULL; 363 if (using_pty) 364 return; 365 if (default_device) { 366 char *p; 367 if (!isatty(0) || (p = ttyname(0)) == NULL) { 368 option_error("no device specified and stdin is not a tty"); 369 exit(EXIT_OPTION_ERROR); 370 } 371 strlcpy(devnam, p, sizeof(devnam)); 372 if (stat(devnam, &devstat) < 0) 373 fatal("Couldn't stat default device %s: %m", devnam); 374 } 375 376 377 /* 378 * Parse the tty options file. 379 * The per-tty options file should not change 380 * ptycommand, pty_socket, notty or devnam. 381 * options_for_tty doesn't override options set on the command line, 382 * except for some privileged options. 383 */ 384 if (!options_for_tty()) 385 exit(EXIT_OPTION_ERROR); 386} 387 388/* 389 * tty_check_options - do consistency checks on the options we were given. 390 */ 391void 392tty_check_options() 393{ 394 struct stat statbuf; 395 int fdflags; 396 397 if (demand && connect_script == 0) { 398 option_error("connect script is required for demand-dialling\n"); 399 exit(EXIT_OPTION_ERROR); 400 } 401 /* default holdoff to 0 if no connect script has been given */ 402 if (connect_script == 0 && !holdoff_specified) 403 holdoff = 0; 404 405 if (using_pty) { 406 if (!default_device) { 407 option_error("%s option precludes specifying device name", 408 notty? "notty": "pty"); 409 exit(EXIT_OPTION_ERROR); 410 } 411 if (ptycommand != NULL && notty) { 412 option_error("pty option is incompatible with notty option"); 413 exit(EXIT_OPTION_ERROR); 414 } 415 if (pty_socket != NULL && (ptycommand != NULL || notty)) { 416 option_error("socket option is incompatible with pty and notty"); 417 exit(EXIT_OPTION_ERROR); 418 } 419 default_device = notty; 420 lockflag = 0; 421 modem = 0; 422 if (notty && log_to_fd <= 1) 423 log_to_fd = -1; 424 } else { 425 /* 426 * If the user has specified a device which is the same as 427 * the one on stdin, pretend they didn't specify any. 428 * If the device is already open read/write on stdin, 429 * we assume we don't need to lock it, and we can open it 430 * as root. 431 */ 432 if (fstat(0, &statbuf) >= 0 && S_ISCHR(statbuf.st_mode) 433 && statbuf.st_rdev == devstat.st_rdev) { 434 default_device = 1; 435 fdflags = fcntl(0, F_GETFL); 436 if (fdflags != -1 && (fdflags & O_ACCMODE) == O_RDWR) 437 privopen = 1; 438 } 439 } 440 if (default_device) 441 nodetach = 1; 442 443 /* 444 * Don't send log messages to the serial port, it tends to 445 * confuse the peer. :-) 446 */ 447 if (log_to_fd >= 0 && fstat(log_to_fd, &statbuf) >= 0 448 && S_ISCHR(statbuf.st_mode) && statbuf.st_rdev == devstat.st_rdev) 449 log_to_fd = -1; 450} 451 452/* 453 * connect_tty - get the serial port ready to start doing PPP. 454 * That is, open the serial port, set its speed and mode, and run 455 * the connector and/or welcomer. 456 */ 457int connect_tty() 458{ 459 char *connector; 460 int fdflags; 461 struct stat statbuf; 462 char numbuf[16]; 463 464 /* 465 * Get a pty master/slave pair if the pty, notty, socket, 466 * or record options were specified. 467 */ 468 strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam)); 469 pty_master = -1; 470 pty_slave = -1; 471 real_ttyfd = -1; 472 if (using_pty || record_file != NULL) { 473 if (!get_pty(&pty_master, &pty_slave, ppp_devnam, uid)) { 474 error("Couldn't allocate pseudo-tty"); 475 status = EXIT_FATAL_ERROR; 476 return -1; 477 } 478 set_up_tty(pty_slave, 1); 479 } 480 481 /* 482 * Lock the device if we've been asked to. 483 */ 484 status = EXIT_LOCK_FAILED; 485 if (lockflag && !privopen) { 486 if (lock(devnam) < 0) 487 return -1; 488 locked = 1; 489 } 490 491 /* 492 * Open the serial device and set it up to be the ppp interface. 493 * First we open it in non-blocking mode so we can set the 494 * various termios flags appropriately. If we aren't dialling 495 * out and we want to use the modem lines, we reopen it later 496 * in order to wait for the carrier detect signal from the modem. 497 */ 498 hungup = 0; 499 kill_link = 0; 500 connector = doing_callback? callback_script: connect_script; 501 if (devnam[0] != 0) { 502 for (;;) { 503 /* If the user specified the device name, become the 504 user before opening it. */ 505 int err, prio; 506 507 prio = privopen? OPRIO_ROOT: tty_options[0].priority; 508 if (prio < OPRIO_ROOT) 509 seteuid(uid); 510 ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0); 511 err = errno; 512 if (prio < OPRIO_ROOT) 513 seteuid(0); 514 if (ttyfd >= 0) 515 break; 516 errno = err; 517 if (err != EINTR) { 518 error("Failed to open %s: %m", devnam); 519 status = EXIT_OPEN_FAILED; 520 } 521 if (!persist || err != EINTR) 522 return -1; 523 } 524 real_ttyfd = ttyfd; 525 if ((fdflags = fcntl(ttyfd, F_GETFL)) == -1 526 || fcntl(ttyfd, F_SETFL, fdflags & ~O_NONBLOCK) < 0) 527 warn("Couldn't reset non-blocking mode on device: %m"); 528 529 /* 530 * Do the equivalent of `mesg n' to stop broadcast messages. 531 */ 532 if (fstat(ttyfd, &statbuf) < 0 533 || fchmod(ttyfd, statbuf.st_mode & ~(S_IWGRP | S_IWOTH)) < 0) { 534 warn("Couldn't restrict write permissions to %s: %m", devnam); 535 } else 536 tty_mode = statbuf.st_mode; 537 538 /* 539 * Set line speed, flow control, etc. 540 * If we have a non-null connection or initializer script, 541 * on most systems we set CLOCAL for now so that we can talk 542 * to the modem before carrier comes up. But this has the 543 * side effect that we might miss it if CD drops before we 544 * get to clear CLOCAL below. On systems where we can talk 545 * successfully to the modem with CLOCAL clear and CD down, 546 * we could clear CLOCAL at this point. 547 */ 548 set_up_tty(ttyfd, ((connector != NULL && connector[0] != 0) 549 || initializer != NULL)); 550 } 551 552 /* 553 * If the pty, socket, notty and/or record option was specified, 554 * start up the character shunt now. 555 */ 556 status = EXIT_PTYCMD_FAILED; 557 if (ptycommand != NULL) { 558 if (record_file != NULL) { 559 int ipipe[2], opipe[2], ok; 560 561 if (pipe(ipipe) < 0 || pipe(opipe) < 0) 562 fatal("Couldn't create pipes for record option: %m"); 563 ok = device_script(ptycommand, opipe[0], ipipe[1], 1) == 0 564 && start_charshunt(ipipe[0], opipe[1]); 565 close(ipipe[0]); 566 close(ipipe[1]); 567 close(opipe[0]); 568 close(opipe[1]); 569 if (!ok) 570 return -1; 571 } else { 572 if (device_script(ptycommand, pty_master, pty_master, 1) < 0) 573 return -1; 574 ttyfd = pty_slave; 575 close(pty_master); 576 pty_master = -1; 577 } 578 } else if (pty_socket != NULL) { 579 int fd = open_socket(pty_socket); 580 if (fd < 0) 581 return -1; 582 if (!start_charshunt(fd, fd)) 583 return -1; 584 } else if (notty) { 585 if (!start_charshunt(0, 1)) 586 return -1; 587 } else if (record_file != NULL) { 588 if (!start_charshunt(ttyfd, ttyfd)) 589 return -1; 590 } 591 592 /* run connection script */ 593 if ((connector && connector[0]) || initializer) { 594 if (real_ttyfd != -1) { 595 if (!default_device && modem) { 596 setdtr(real_ttyfd, 0); /* in case modem is off hook */ 597 sleep(1); 598 setdtr(real_ttyfd, 1); 599 } 600 } 601 602 if (initializer && initializer[0]) { 603 if (device_script(initializer, ttyfd, ttyfd, 0) < 0) { 604 error("Initializer script failed"); 605 status = EXIT_INIT_FAILED; 606 return -1; 607 } 608 if (kill_link) { 609 disconnect_tty(); 610 return -1; 611 } 612 info("Serial port initialized."); 613 } 614 615 if (connector && connector[0]) { 616 if (device_script(connector, ttyfd, ttyfd, 0) < 0) { 617 error("Connect script failed"); 618 status = EXIT_CONNECT_FAILED; 619 return -1; 620 } 621 if (kill_link) { 622 disconnect_tty(); 623 return -1; 624 } 625 info("Serial connection established."); 626 } 627 628 /* set line speed, flow control, etc.; 629 clear CLOCAL if modem option */ 630 if (real_ttyfd != -1) 631 set_up_tty(real_ttyfd, 0); 632 633 if (doing_callback == CALLBACK_DIALIN) 634 connector = NULL; 635 } 636 637 /* reopen tty if necessary to wait for carrier */ 638 if (connector == NULL && modem && devnam[0] != 0) { 639 int i; 640 for (;;) { 641 if ((i = open(devnam, O_RDWR)) >= 0) 642 break; 643 if (errno != EINTR) { 644 error("Failed to reopen %s: %m", devnam); 645 status = EXIT_OPEN_FAILED; 646 } 647 if (!persist || errno != EINTR || hungup || kill_link) 648 return -1; 649 } 650 close(i); 651 } 652 653 slprintf(numbuf, sizeof(numbuf), "%d", baud_rate); 654 script_setenv("SPEED", numbuf, 0); 655 656 /* run welcome script, if any */ 657 if (welcomer && welcomer[0]) { 658 if (device_script(welcomer, ttyfd, ttyfd, 0) < 0) 659 warn("Welcome script failed"); 660 } 661 662 /* 663 * If we are initiating this connection, wait for a short 664 * time for something from the peer. This can avoid bouncing 665 * our packets off his tty before he has it set up. 666 */ 667 if (connector != NULL || ptycommand != NULL) 668 listen_time = connect_delay; 669 670 return ttyfd; 671} 672 673 674void disconnect_tty() 675{ 676 if (disconnect_script == NULL || hungup) 677 return; 678 if (real_ttyfd >= 0) 679 set_up_tty(real_ttyfd, 1); 680 if (device_script(disconnect_script, ttyfd, ttyfd, 0) < 0) { 681 warn("disconnect script failed"); 682 } else { 683 info("Serial link disconnected."); 684 } 685} 686 687void tty_close_fds() 688{ 689 if (pty_master >= 0) 690 close(pty_master); 691 if (pty_slave >= 0) 692 close(pty_slave); 693 if (real_ttyfd >= 0) { 694 close(real_ttyfd); 695 real_ttyfd = -1; 696 } 697 /* N.B. ttyfd will == either pty_slave or real_ttyfd */ 698} 699 700void cleanup_tty() 701{ 702 if (real_ttyfd >= 0) 703 finish_tty(); 704 tty_close_fds(); 705 if (locked) { 706 unlock(); 707 locked = 0; 708 } 709} 710 711/* 712 * tty_do_send_config - set transmit-side PPP configuration. 713 * We set the extended transmit ACCM here as well. 714 */ 715void 716tty_do_send_config(mtu, accm, pcomp, accomp) 717 int mtu; 718 u_int32_t accm; 719 int pcomp, accomp; 720{ 721 tty_set_xaccm(xmit_accm); 722 tty_send_config(mtu, accm, pcomp, accomp); 723} 724 725/* 726 * finish_tty - restore the terminal device to its original settings 727 */ 728static void 729finish_tty() 730{ 731 /* drop dtr to hang up */ 732 if (!default_device && modem) { 733 setdtr(real_ttyfd, 0); 734 /* 735 * This sleep is in case the serial port has CLOCAL set by default, 736 * and consequently will reassert DTR when we close the device. 737 */ 738 sleep(1); 739 } 740 741 restore_tty(real_ttyfd); 742 743 if (tty_mode != (mode_t) -1) { 744 if (fchmod(real_ttyfd, tty_mode) != 0) { 745 chmod(devnam, tty_mode); 746 } 747 } 748 749 close(real_ttyfd); 750 real_ttyfd = -1; 751} 752 753/* 754 * maybe_relock - our PID has changed, maybe update the lock file. 755 */ 756static void 757maybe_relock(arg, pid) 758 void *arg; 759 int pid; 760{ 761 if (locked) 762 relock(pid); 763} 764 765/* 766 * open_socket - establish a stream socket connection to the nominated 767 * host and port. 768 */ 769static int 770open_socket(dest) 771 char *dest; 772{ 773 char *sep, *endp = NULL; 774 int sock, port = -1; 775 u_int32_t host; 776 struct hostent *hent; 777 struct sockaddr_in sad; 778 779 /* parse host:port and resolve host to an IP address */ 780 sep = strchr(dest, ':'); 781 if (sep != NULL) 782 port = strtol(sep+1, &endp, 10); 783 if (port < 0 || endp == sep+1 || sep == dest) { 784 error("Can't parse host:port for socket destination"); 785 return -1; 786 } 787 *sep = 0; 788 host = inet_addr(dest); 789 if (host == (u_int32_t) -1) { 790 hent = gethostbyname(dest); 791 if (hent == NULL) { 792 error("%s: unknown host in socket option", dest); 793 *sep = ':'; 794 return -1; 795 } 796 host = *(u_int32_t *)(hent->h_addr_list[0]); 797 } 798 *sep = ':'; 799 800 /* get a socket and connect it to the other end */ 801 sock = socket(PF_INET, SOCK_STREAM, 0); 802 if (sock < 0) { 803 error("Can't create socket: %m"); 804 return -1; 805 } 806 memset(&sad, 0, sizeof(sad)); 807 sad.sin_family = AF_INET; 808 sad.sin_port = htons(port); 809 sad.sin_addr.s_addr = host; 810 if (connect(sock, (struct sockaddr *)&sad, sizeof(sad)) < 0) { 811 error("Can't connect to %s: %m", dest); 812 close(sock); 813 return -1; 814 } 815 816 return sock; 817} 818 819 820/* 821 * start_charshunt - create a child process to run the character shunt. 822 */ 823static int 824start_charshunt(ifd, ofd) 825 int ifd, ofd; 826{ 827 int cpid; 828 829 cpid = fork(); 830 if (cpid == -1) { 831 error("Can't fork process for character shunt: %m"); 832 return 0; 833 } 834 if (cpid == 0) { 835 /* child */ 836 close(pty_slave); 837 setuid(uid); 838 if (getuid() != uid) 839 fatal("setuid failed"); 840 setgid(getgid()); 841 if (!nodetach) 842 log_to_fd = -1; 843 charshunt(ifd, ofd, record_file); 844 exit(0); 845 } 846 charshunt_pid = cpid; 847 add_notifier(&sigreceived, stop_charshunt, 0); 848 close(pty_master); 849 pty_master = -1; 850 ttyfd = pty_slave; 851 record_child(cpid, "pppd (charshunt)", charshunt_done, NULL); 852 return 1; 853} 854 855static void 856charshunt_done(arg) 857 void *arg; 858{ 859 charshunt_pid = 0; 860} 861 862static void 863stop_charshunt(arg, sig) 864 void *arg; 865 int sig; 866{ 867 if (charshunt_pid) 868 kill(charshunt_pid, (sig == SIGINT? sig: SIGTERM)); 869} 870 871/* 872 * charshunt - the character shunt, which passes characters between 873 * the pty master side and the serial port (or stdin/stdout). 874 * This runs as the user (not as root). 875 * (We assume ofd >= ifd which is true the way this gets called. :-). 876 */ 877static void 878charshunt(ifd, ofd, record_file) 879 int ifd, ofd; 880 char *record_file; 881{ 882 int n, nfds; 883 fd_set ready, writey; 884 u_char *ibufp, *obufp; 885 int nibuf, nobuf; 886 int flags; 887 int pty_readable, stdin_readable; 888 struct timeval lasttime; 889 FILE *recordf = NULL; 890 int ilevel, olevel, max_level; 891 struct timeval levelt, tout, *top; 892 extern u_char inpacket_buf[]; 893 894 /* 895 * Reset signal handlers. 896 */ 897 signal(SIGHUP, SIG_IGN); /* Hangup */ 898 signal(SIGINT, SIG_DFL); /* Interrupt */ 899 signal(SIGTERM, SIG_DFL); /* Terminate */ 900 signal(SIGCHLD, SIG_DFL); 901 signal(SIGUSR1, SIG_DFL); 902 signal(SIGUSR2, SIG_DFL); 903 signal(SIGABRT, SIG_DFL); 904 signal(SIGALRM, SIG_DFL); 905 signal(SIGFPE, SIG_DFL); 906 signal(SIGILL, SIG_DFL); 907 signal(SIGPIPE, SIG_DFL); 908 signal(SIGQUIT, SIG_DFL); 909 signal(SIGSEGV, SIG_DFL); 910#ifdef SIGBUS 911 signal(SIGBUS, SIG_DFL); 912#endif 913#ifdef SIGEMT 914 signal(SIGEMT, SIG_DFL); 915#endif 916#ifdef SIGPOLL 917 signal(SIGPOLL, SIG_DFL); 918#endif 919#ifdef SIGPROF 920 signal(SIGPROF, SIG_DFL); 921#endif 922#ifdef SIGSYS 923 signal(SIGSYS, SIG_DFL); 924#endif 925#ifdef SIGTRAP 926 signal(SIGTRAP, SIG_DFL); 927#endif 928#ifdef SIGVTALRM 929 signal(SIGVTALRM, SIG_DFL); 930#endif 931#ifdef SIGXCPU 932 signal(SIGXCPU, SIG_DFL); 933#endif 934#ifdef SIGXFSZ 935 signal(SIGXFSZ, SIG_DFL); 936#endif 937 938 /* 939 * Open the record file if required. 940 */ 941 if (record_file != NULL) { 942 recordf = fopen(record_file, "a"); 943 if (recordf == NULL) 944 error("Couldn't create record file %s: %m", record_file); 945 } 946 947 /* set all the fds to non-blocking mode */ 948 flags = fcntl(pty_master, F_GETFL); 949 if (flags == -1 950 || fcntl(pty_master, F_SETFL, flags | O_NONBLOCK) == -1) 951 warn("couldn't set pty master to nonblock: %m"); 952 flags = fcntl(ifd, F_GETFL); 953 if (flags == -1 954 || fcntl(ifd, F_SETFL, flags | O_NONBLOCK) == -1) 955 warn("couldn't set %s to nonblock: %m", (ifd==0? "stdin": "tty")); 956 if (ofd != ifd) { 957 flags = fcntl(ofd, F_GETFL); 958 if (flags == -1 959 || fcntl(ofd, F_SETFL, flags | O_NONBLOCK) == -1) 960 warn("couldn't set stdout to nonblock: %m"); 961 } 962 963 nibuf = nobuf = 0; 964 ibufp = obufp = NULL; 965 pty_readable = stdin_readable = 1; 966 967 ilevel = olevel = 0; 968 gettimeofday(&levelt, NULL); 969 if (max_data_rate) { 970 max_level = max_data_rate / 10; 971 if (max_level < 100) 972 max_level = 100; 973 } else 974 max_level = PPP_MRU + PPP_HDRLEN + 1; 975 976 nfds = (ofd > pty_master? ofd: pty_master) + 1; 977 if (recordf != NULL) { 978 gettimeofday(&lasttime, NULL); 979 putc(7, recordf); /* put start marker */ 980 putc(lasttime.tv_sec >> 24, recordf); 981 putc(lasttime.tv_sec >> 16, recordf); 982 putc(lasttime.tv_sec >> 8, recordf); 983 putc(lasttime.tv_sec, recordf); 984 lasttime.tv_usec = 0; 985 } 986 987 while (nibuf != 0 || nobuf != 0 || pty_readable || stdin_readable) { 988 top = 0; 989 tout.tv_sec = 0; 990 tout.tv_usec = 10000; 991 FD_ZERO(&ready); 992 FD_ZERO(&writey); 993 if (nibuf != 0) { 994 if (ilevel >= max_level) 995 top = &tout; 996 else 997 FD_SET(pty_master, &writey); 998 } else if (stdin_readable) 999 FD_SET(ifd, &ready); 1000 if (nobuf != 0) { 1001 if (olevel >= max_level) 1002 top = &tout; 1003 else 1004 FD_SET(ofd, &writey); 1005 } else if (pty_readable) 1006 FD_SET(pty_master, &ready); 1007 if (select(nfds, &ready, &writey, NULL, top) < 0) { 1008 if (errno != EINTR) 1009 fatal("select"); 1010 continue; 1011 } 1012 if (max_data_rate) { 1013 double dt; 1014 int nbt; 1015 struct timeval now; 1016 1017 gettimeofday(&now, NULL); 1018 dt = (now.tv_sec - levelt.tv_sec 1019 + (now.tv_usec - levelt.tv_usec) / 1e6); 1020 nbt = (int)(dt * max_data_rate); 1021 ilevel = (nbt < 0 || nbt > ilevel)? 0: ilevel - nbt; 1022 olevel = (nbt < 0 || nbt > olevel)? 0: olevel - nbt; 1023 levelt = now; 1024 } else 1025 ilevel = olevel = 0; 1026 if (FD_ISSET(ifd, &ready)) { 1027 ibufp = inpacket_buf; 1028 nibuf = read(ifd, ibufp, PPP_MRU + PPP_HDRLEN); 1029 if (nibuf < 0 && errno == EIO) 1030 nibuf = 0; 1031 if (nibuf < 0) { 1032 if (!(errno == EINTR || errno == EAGAIN)) { 1033 error("Error reading standard input: %m"); 1034 break; 1035 } 1036 nibuf = 0; 1037 } else if (nibuf == 0) { 1038 /* end of file from stdin */ 1039 stdin_readable = 0; 1040 /* do a 0-length write, hopefully this will generate 1041 an EOF (hangup) on the slave side. */ 1042 write(pty_master, inpacket_buf, 0); 1043 if (recordf) 1044 if (!record_write(recordf, 4, NULL, 0, &lasttime)) 1045 recordf = NULL; 1046 } else { 1047 FD_SET(pty_master, &writey); 1048 if (recordf) 1049 if (!record_write(recordf, 2, ibufp, nibuf, &lasttime)) 1050 recordf = NULL; 1051 } 1052 } 1053 if (FD_ISSET(pty_master, &ready)) { 1054 obufp = outpacket_buf; 1055 nobuf = read(pty_master, obufp, PPP_MRU + PPP_HDRLEN); 1056 if (nobuf < 0 && errno == EIO) 1057 nobuf = 0; 1058 if (nobuf < 0) { 1059 if (!(errno == EINTR || errno == EAGAIN)) { 1060 error("Error reading pseudo-tty master: %m"); 1061 break; 1062 } 1063 nobuf = 0; 1064 } else if (nobuf == 0) { 1065 /* end of file from the pty - slave side has closed */ 1066 pty_readable = 0; 1067 stdin_readable = 0; /* pty is not writable now */ 1068 nibuf = 0; 1069 close(ofd); 1070 if (recordf) 1071 if (!record_write(recordf, 3, NULL, 0, &lasttime)) 1072 recordf = NULL; 1073 } else { 1074 FD_SET(ofd, &writey); 1075 if (recordf) 1076 if (!record_write(recordf, 1, obufp, nobuf, &lasttime)) 1077 recordf = NULL; 1078 } 1079 } 1080 if (FD_ISSET(ofd, &writey)) { 1081 n = nobuf; 1082 if (olevel + n > max_level) 1083 n = max_level - olevel; 1084 n = write(ofd, obufp, n); 1085 if (n < 0) { 1086 if (errno == EIO) { 1087 pty_readable = 0; 1088 nobuf = 0; 1089 } else if (errno != EAGAIN && errno != EINTR) { 1090 error("Error writing standard output: %m"); 1091 break; 1092 } 1093 } else { 1094 obufp += n; 1095 nobuf -= n; 1096 olevel += n; 1097 } 1098 } 1099 if (FD_ISSET(pty_master, &writey)) { 1100 n = nibuf; 1101 if (ilevel + n > max_level) 1102 n = max_level - ilevel; 1103 n = write(pty_master, ibufp, n); 1104 if (n < 0) { 1105 if (errno == EIO) { 1106 stdin_readable = 0; 1107 nibuf = 0; 1108 } else if (errno != EAGAIN && errno != EINTR) { 1109 error("Error writing pseudo-tty master: %m"); 1110 break; 1111 } 1112 } else { 1113 ibufp += n; 1114 nibuf -= n; 1115 ilevel += n; 1116 } 1117 } 1118 } 1119 exit(0); 1120} 1121 1122static int 1123record_write(f, code, buf, nb, tp) 1124 FILE *f; 1125 int code; 1126 u_char *buf; 1127 int nb; 1128 struct timeval *tp; 1129{ 1130 struct timeval now; 1131 int diff; 1132 1133 gettimeofday(&now, NULL); 1134 now.tv_usec /= 100000; /* actually 1/10 s, not usec now */ 1135 diff = (now.tv_sec - tp->tv_sec) * 10 + (now.tv_usec - tp->tv_usec); 1136 if (diff > 0) { 1137 if (diff > 255) { 1138 putc(5, f); 1139 putc(diff >> 24, f); 1140 putc(diff >> 16, f); 1141 putc(diff >> 8, f); 1142 putc(diff, f); 1143 } else { 1144 putc(6, f); 1145 putc(diff, f); 1146 } 1147 *tp = now; 1148 } 1149 putc(code, f); 1150 if (buf != NULL) { 1151 putc(nb >> 8, f); 1152 putc(nb, f); 1153 fwrite(buf, nb, 1, f); 1154 } 1155 fflush(f); 1156 if (ferror(f)) { 1157 error("Error writing record file: %m"); 1158 return 0; 1159 } 1160 return 1; 1161} 1162