1/* 2 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23/* 24 * tty.c - code for handling serial ports in pppd. 25 * 26 * Copyright (C) 2000-2002 Paul Mackerras. All rights reserved. 27 * 28 * Redistribution and use in source and binary forms, with or without 29 * modification, are permitted provided that the following conditions 30 * are met: 31 * 32 * 1. Redistributions of source code must retain the above copyright 33 * notice, this list of conditions and the following disclaimer. 34 * 35 * 2. Redistributions in binary form must reproduce the above copyright 36 * notice, this list of conditions and the following disclaimer in 37 * the documentation and/or other materials provided with the 38 * distribution. 39 * 40 * 3. The name(s) of the authors of this software must not be used to 41 * endorse or promote products derived from this software without 42 * prior written permission. 43 * 44 * 4. Redistributions of any form whatsoever must retain the following 45 * acknowledgment: 46 * "This product includes software developed by Paul Mackerras 47 * <paulus@samba.org>". 48 * 49 * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO 50 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 51 * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY 52 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 53 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 54 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 55 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 56 * 57 * Portions derived from main.c, which is: 58 * 59 * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. 60 * 61 * Redistribution and use in source and binary forms, with or without 62 * modification, are permitted provided that the following conditions 63 * are met: 64 * 65 * 1. Redistributions of source code must retain the above copyright 66 * notice, this list of conditions and the following disclaimer. 67 * 68 * 2. Redistributions in binary form must reproduce the above copyright 69 * notice, this list of conditions and the following disclaimer in 70 * the documentation and/or other materials provided with the 71 * distribution. 72 * 73 * 3. The name "Carnegie Mellon University" must not be used to 74 * endorse or promote products derived from this software without 75 * prior written permission. For permission or any legal 76 * details, please contact 77 * Office of Technology Transfer 78 * Carnegie Mellon University 79 * 5000 Forbes Avenue 80 * Pittsburgh, PA 15213-3890 81 * (412) 268-4387, fax: (412) 268-7395 82 * tech-transfer@andrew.cmu.edu 83 * 84 * 4. Redistributions of any form whatsoever must retain the following 85 * acknowledgment: 86 * "This product includes software developed by Computing Services 87 * at Carnegie Mellon University (http://www.cmu.edu/computing/)." 88 * 89 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO 90 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 91 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE 92 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 93 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 94 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 95 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 96 */ 97 98#define RCSID "$Id: tty.c,v 1.13 2005/03/11 05:48:32 lindak Exp $" 99 100#include <stdio.h> 101#include <ctype.h> 102#include <stdlib.h> 103#include <string.h> 104#include <unistd.h> 105#include <signal.h> 106#include <errno.h> 107#include <fcntl.h> 108#include <syslog.h> 109#include <netdb.h> 110#include <pwd.h> 111#include <setjmp.h> 112#include <sys/param.h> 113#include <sys/types.h> 114#include <sys/wait.h> 115#include <sys/time.h> 116#include <sys/resource.h> 117#include <sys/stat.h> 118#include <sys/socket.h> 119#include <netinet/in.h> 120#include <arpa/inet.h> 121#ifdef __APPLE__ 122#include <termios.h> 123#include <sys/ioctl.h> 124#endif 125 126#include "pppd.h" 127#include "fsm.h" 128#include "lcp.h" 129 130void tty_process_extra_options __P((void)); 131void tty_check_options __P((void)); 132#ifdef __APPLE__ 133int connect_tty __P((int *)); 134#else 135int connect_tty __P((void)); 136#endif 137void disconnect_tty __P((void)); 138void tty_close_fds __P((void)); 139void cleanup_tty __P((void)); 140void tty_do_send_config __P((int, u_int32_t, int, int)); 141 142#ifdef __APPLE__ 143static void sighup_tty(void *, uintptr_t); 144#endif 145static int setdevname __P((char *, char **, int)); 146static int setspeed __P((char *, char **, int)); 147static int setxonxoff __P((char **)); 148static int setescape __P((char **)); 149static void printescape __P((option_t *, void (*)(void *, char *,...),void *)); 150static void finish_tty __P((void)); 151static int start_charshunt __P((int, int)); 152static void stop_charshunt __P((void *, uintptr_t)); 153static void charshunt_done __P((void *)); 154static void charshunt __P((int, int, char *)); 155static int record_write __P((FILE *, int code, u_char *buf, int nb, 156 struct timeval *)); 157static int open_socket __P((char *)); 158static void maybe_relock __P((void *, uintptr_t)); 159 160static int pty_master; /* fd for master side of pty */ 161static int pty_slave; /* fd for slave side of pty */ 162static int real_ttyfd; /* fd for actual serial port (not pty) */ 163static int ttyfd; /* Serial port file descriptor */ 164static char speed_str[16]; /* Serial port speed as string */ 165 166mode_t tty_mode = (mode_t)-1; /* Original access permissions to tty */ 167int baud_rate; /* Actual bits/second for serial device */ 168char *callback_script; /* script for doing callback */ 169int charshunt_pid; /* Process ID for charshunt */ 170int locked; /* lock() has succeeded */ 171struct stat devstat; /* result of stat() on devnam */ 172 173/* option variables */ 174int crtscts = 0; /* Use hardware flow control */ 175bool modem = 1; /* Use modem control lines */ 176int inspeed = 0; /* Input/Output speed requested */ 177bool lockflag = 0; /* Create lock file to lock the serial dev */ 178char *initializer = NULL; /* Script to initialize physical link */ 179char *connect_script = NULL; /* Script to establish physical link */ 180#ifdef __APPLE__ 181int clocal = 0; /* is clocal flag set ? */ 182uid_t connector_uid = -1; /* uid for connect script */ 183uid_t disconnector_uid = -1; /*uid for disconnect script */ 184char *terminal_script = NULL;/* Script to etablish connection once modem is connected */ 185char *altconnect_script = NULL;/* alternate script to establish physical link */ 186char *altconnect_data = NULL;/* alternate connect data top pipe to the script */ 187int altconnect_data_len = 0;/* alternate connect data length */ 188char *connect_data = NULL;/* connect data top pipe to the script */ 189int connect_data_len = 0;/* connect data length */ 190char *disconnect_data = NULL;/* disconnect data top pipe to the script */ 191int disconnect_data_len = 0;/* disconnect data length */ 192char *terminal_data = NULL;/* terminal data top pipe to the script */ 193int terminal_data_len = 0;/* terminal data length */ 194int pty_delay = 0; /* timeout to wait for the pty command */ 195#endif 196char *disconnect_script = NULL; /* Script to disestablish physical link */ 197char *welcomer = NULL; /* Script to run after phys link estab. */ 198char *ptycommand = NULL; /* Command to run on other side of pty */ 199bool notty = 0; /* Stdin/out is not a tty */ 200char *record_file = NULL; /* File to record chars sent/received */ 201int max_data_rate; /* max bytes/sec through charshunt */ 202bool sync_serial = 0; /* Device is synchronous serial device */ 203char *pty_socket = NULL; /* Socket to connect to pty */ 204int using_pty = 0; /* we're allocating a pty as the device */ 205 206extern uid_t uid; 207extern int kill_link; 208 209#ifdef __APPLE__ 210struct notifier *initscript_started_notify = NULL; 211struct notifier *initscript_finished_notify = NULL; 212struct notifier *connectscript_started_notify = NULL; 213struct notifier *connectscript_finished_notify = NULL; 214struct notifier *terminalscript_started_notify = NULL; 215struct notifier *terminalscript_finished_notify = NULL; 216static int forcepty __P((char **)); 217#endif 218 219/* XXX */ 220extern int privopen; /* don't lock, open device as root */ 221 222u_int32_t xmit_accm[8]; /* extended transmit ACCM */ 223 224/* option descriptors */ 225option_t tty_options[] = { 226 /* device name must be first, or change connect_tty() below! */ 227 { "device name", o_wild, (void *) &setdevname, 228 "Serial port device name", 229 OPT_DEVNAM | OPT_PRIVFIX | OPT_NOARG | OPT_A2STRVAL | OPT_STATIC, 230 devnam}, 231 232 { "tty speed", o_wild, (void *) &setspeed, 233 "Baud rate for serial port", 234 OPT_PRIO | OPT_NOARG | OPT_A2STRVAL | OPT_STATIC, speed_str }, 235 236 { "lock", o_bool, &lockflag, 237 "Lock serial device with UUCP-style lock file", OPT_PRIO | 1 }, 238 { "nolock", o_bool, &lockflag, 239 "Don't lock serial device", OPT_PRIOSUB | OPT_PRIV }, 240 241 { "init", o_string, &initializer, 242 "A program to initialize the device", OPT_PRIO | OPT_PRIVFIX }, 243 244 { "connect", o_string, &connect_script, 245 "A program to set up a connection", OPT_PRIO | OPT_PRIVFIX }, 246 247 #ifdef __APPLE__ 248 { "altconnect", o_string, &altconnect_script, 249 "A an alternate program to set up a connection", OPT_PRIO | OPT_PRIVFIX }, 250 251 /* terminal needs to be hidden because it may contain the password */ 252 { "terminal", o_string, &terminal_script, 253 "A program to set up a terminal connection", OPT_PRIO | OPT_PRIVFIX | OPT_HIDE }, 254 255 { "pty-delay", o_int, &pty_delay, 256 "Timeout to wait for bytes on pty" }, 257 { "forcepty", o_special, (void *)forcepty, 258 "Force usage of pty, even if devname is set"}, 259#endif 260 261 { "disconnect", o_string, &disconnect_script, 262 "Program to disconnect serial device", OPT_PRIO | OPT_PRIVFIX }, 263 264 { "welcome", o_string, &welcomer, 265 "Script to welcome client", OPT_PRIO | OPT_PRIVFIX }, 266 267 { "pty", o_string, &ptycommand, 268 "Script to run on pseudo-tty master side", 269 OPT_PRIO | OPT_PRIVFIX | OPT_DEVNAM }, 270 271 { "notty", o_bool, ¬ty, 272 "Input/output is not a tty", OPT_DEVNAM | 1 }, 273 274 { "socket", o_string, &pty_socket, 275 "Send and receive over socket, arg is host:port", 276 OPT_PRIO | OPT_DEVNAM }, 277 278 { "record", o_string, &record_file, 279 "Record characters sent/received to file", OPT_PRIO }, 280 281 { "crtscts", o_int, &crtscts, 282 "Set hardware (RTS/CTS) flow control", 283 OPT_PRIO | OPT_NOARG | OPT_VAL(1) }, 284 { "cdtrcts", o_int, &crtscts, 285 "Set alternate hardware (DTR/CTS) flow control", 286 OPT_PRIOSUB | OPT_NOARG | OPT_VAL(2) }, 287 { "nocrtscts", o_int, &crtscts, 288 "Disable hardware flow control", 289 OPT_PRIOSUB | OPT_NOARG | OPT_VAL(-1) }, 290 { "-crtscts", o_int, &crtscts, 291 "Disable hardware flow control", 292 OPT_PRIOSUB | OPT_ALIAS | OPT_NOARG | OPT_VAL(-1) }, 293 { "nocdtrcts", o_int, &crtscts, 294 "Disable hardware flow control", 295 OPT_PRIOSUB | OPT_ALIAS | OPT_NOARG | OPT_VAL(-1) }, 296 { "xonxoff", o_special_noarg, (void *)setxonxoff, 297 "Set software (XON/XOFF) flow control", OPT_PRIOSUB }, 298 299 { "modem", o_bool, &modem, 300 "Use modem control lines", OPT_PRIO | 1 }, 301 { "local", o_bool, &modem, 302 "Don't use modem control lines", OPT_PRIOSUB | 0 }, 303 304 { "sync", o_bool, &sync_serial, 305 "Use synchronous HDLC serial encoding", 1 }, 306 307 { "datarate", o_int, &max_data_rate, 308 "Maximum data rate in bytes/sec (with pty, notty or record option)", 309 OPT_PRIO }, 310 311 { "escape", o_special, (void *)setescape, 312 "List of character codes to escape on transmission", 313 OPT_A2PRINTER, (void *)printescape }, 314 315 { NULL } 316}; 317 318 319struct channel tty_channel = { 320 tty_options, 321 &tty_process_extra_options, 322 &tty_check_options, 323 &connect_tty, 324 &disconnect_tty, 325 &tty_establish_ppp, 326 &tty_disestablish_ppp, 327 &tty_do_send_config, 328 &tty_recv_config, 329 &cleanup_tty, 330 &tty_close_fds 331#ifdef __APPLE__ 332 , 333 NULL 334#endif 335}; 336 337/* 338 * setspeed - Set the serial port baud rate. 339 * If doit is 0, the call is to check whether this option is 340 * potentially a speed value. 341 */ 342static int 343setspeed(arg, argv, doit) 344 char *arg; 345 char **argv; 346 int doit; 347{ 348 char *ptr; 349 int spd; 350 351 spd = strtol(arg, &ptr, 0); 352 if (ptr == arg || *ptr != 0 || spd == 0) 353 return 0; 354 if (doit) { 355 inspeed = spd; 356 slprintf(speed_str, sizeof(speed_str), "%d", spd); 357 } 358 return 1; 359} 360 361 362/* 363 * setdevname - Set the device name. 364 * If doit is 0, the call is to check whether this option is 365 * potentially a device name. 366 */ 367static int 368setdevname(cp, argv, doit) 369 char *cp; 370 char **argv; 371 int doit; 372{ 373 struct stat statbuf; 374 char dev[MAXPATHLEN]; 375 376 if (*cp == 0) 377 return 0; 378 379 if (strncmp("/dev/", cp, 5) != 0) { 380 strlcpy(dev, "/dev/", sizeof(dev)); 381 strlcat(dev, cp, sizeof(dev)); 382 cp = dev; 383 } 384 385 /* 386 * Check if there is a character device by this name. 387 */ 388 if (stat(cp, &statbuf) < 0) { 389 if (!doit) 390 return errno != ENOENT; 391 option_error("Couldn't stat %s: %m", cp); 392 return 0; 393 } 394 if (!S_ISCHR(statbuf.st_mode)) { 395 if (doit) 396 option_error("%s is not a character device", cp); 397 return 0; 398 } 399 400 if (doit) { 401 strlcpy(devnam, cp, sizeof(devnam)); 402 devstat = statbuf; 403 default_device = 0; 404 } 405 406 return 1; 407} 408 409static int 410setxonxoff(argv) 411 char **argv; 412{ 413 lcp_wantoptions[0].asyncmap |= 0x000A0000; /* escape ^S and ^Q */ 414 lcp_wantoptions[0].neg_asyncmap = 1; 415 416 crtscts = -2; 417 return 1; 418} 419 420/* 421 * setescape - add chars to the set we escape on transmission. 422 */ 423static int 424setescape(argv) 425 char **argv; 426{ 427 int n, ret; 428 char *p, *endp; 429 430 p = *argv; 431 ret = 1; 432 while (*p) { 433 n = strtol(p, &endp, 16); 434 if (p == endp) { 435 option_error("escape parameter contains invalid hex number '%s'", 436 p); 437 return 0; 438 } 439 p = endp; 440 if (n < 0 || n == 0x5E || n > 0xFF) { 441 option_error("can't escape character 0x%x", n); 442 ret = 0; 443 } else 444 xmit_accm[n >> 5] |= 1 << (n & 0x1F); 445 while (*p == ',' || *p == ' ') 446 ++p; 447 } 448 lcp_allowoptions[0].asyncmap = xmit_accm[0]; 449 return ret; 450} 451 452static void 453printescape(opt, printer, arg) 454 option_t *opt; 455 void (*printer) __P((void *, char *, ...)); 456 void *arg; 457{ 458 int n; 459 int first = 1; 460 461 for (n = 0; n < 256; ++n) { 462 if (n == 0x7d) 463 n += 2; /* skip 7d, 7e */ 464 if (xmit_accm[n >> 5] & (1 << (n & 0x1f))) { 465 if (!first) 466 printer(arg, ","); 467 else 468 first = 0; 469 printer(arg, "%x", n); 470 } 471 } 472 if (first) 473 printer(arg, "oops # nothing escaped"); 474} 475 476/* 477 * tty_init - do various tty-related initializations. 478 */ 479void tty_init() 480{ 481 add_notifier(&pidchange, maybe_relock, 0); 482#ifdef __APPLE__ 483 add_notifier(&sigreceived, sighup_tty, 0); 484 real_ttyfd = -1; 485#endif 486 the_channel = &tty_channel; 487 xmit_accm[3] = 0x60000000; 488} 489 490/* 491 * tty_process_extra_options - work out which tty device we are using 492 * and read its options file. 493 */ 494void tty_process_extra_options() 495{ 496 using_pty = notty || ptycommand != NULL || pty_socket != NULL; 497 if (using_pty) 498 return; 499 if (default_device) { 500 char *p; 501 if (!isatty(0) || (p = ttyname(0)) == NULL) { 502 option_error("no device specified and stdin is not a tty"); 503 exit(EXIT_OPTION_ERROR); 504 } 505 strlcpy(devnam, p, sizeof(devnam)); 506 if (stat(devnam, &devstat) < 0) 507 fatal("Couldn't stat default device %s: %m", devnam); 508 } 509 510 511 /* 512 * Parse the tty options file. 513 * The per-tty options file should not change 514 * ptycommand, pty_socket, notty or devnam. 515 * options_for_tty doesn't override options set on the command line, 516 * except for some privileged options. 517 */ 518 if (!options_for_tty()) 519 exit(EXIT_OPTION_ERROR); 520} 521 522/* 523 * tty_check_options - do consistency checks on the options we were given. 524 */ 525void 526tty_check_options() 527{ 528 struct stat statbuf; 529 int fdflags; 530 531 if (demand && (connect_script == 0) 532#ifdef __APPLE__ 533 && (ptycommand == 0) 534#endif 535 ) { 536 option_error("connect script is required for demand-dialling\n"); 537 exit(EXIT_OPTION_ERROR); 538 } 539 /* default holdoff to 0 if no connect script has been given */ 540 if (connect_script == 0 && !holdoff_specified) 541 holdoff = 0; 542 543 if (using_pty) { 544 if (!default_device) { 545 option_error("%s option precludes specifying device name", 546 notty? "notty": "pty"); 547 exit(EXIT_OPTION_ERROR); 548 } 549 if (ptycommand != NULL && notty) { 550 option_error("pty option is incompatible with notty option"); 551 exit(EXIT_OPTION_ERROR); 552 } 553 if (pty_socket != NULL && (ptycommand != NULL || notty)) { 554 option_error("socket option is incompatible with pty and notty"); 555 exit(EXIT_OPTION_ERROR); 556 } 557 default_device = notty; 558 lockflag = 0; 559 modem = 0; 560 if (notty && log_to_fd <= 1) 561 log_to_fd = -1; 562 } else { 563 /* 564 * If the user has specified a device which is the same as 565 * the one on stdin, pretend they didn't specify any. 566 * If the device is already open read/write on stdin, 567 * we assume we don't need to lock it, and we can open it 568 * as root. 569 */ 570 if (fstat(0, &statbuf) >= 0 && S_ISCHR(statbuf.st_mode) 571 && statbuf.st_rdev == devstat.st_rdev) { 572 default_device = 1; 573 fdflags = fcntl(0, F_GETFL); 574 if (fdflags != -1 && (fdflags & O_ACCMODE) == O_RDWR) 575 privopen = 1; 576 } 577 } 578 if (default_device) 579 nodetach = 1; 580 581 /* 582 * Don't send log messages to the serial port, it tends to 583 * confuse the peer. :-) 584 */ 585 if (log_to_fd >= 0 && fstat(log_to_fd, &statbuf) >= 0 586 && S_ISCHR(statbuf.st_mode) && statbuf.st_rdev == devstat.st_rdev) 587 log_to_fd = -1; 588} 589 590/* 591 * connect_tty - get the serial port ready to start doing PPP. 592 * That is, open the serial port, set its speed and mode, and run 593 * the connector and/or welcomer. 594 */ 595#ifdef __APPLE__ 596int connect_tty(int *errorcode) 597#else 598int connect_tty() 599#endif 600{ 601 char *connector, *connector_data = NULL; 602 int fdflags, connector_data_len = 0; 603 struct stat statbuf; 604 char numbuf[16]; 605 606#ifdef __APPLE__ 607 // so far, used only to return the busy error code 608 *errorcode = -1; 609#endif 610 /* 611 * Get a pty master/slave pair if the pty, notty, socket, 612 * or record options were specified. 613 */ 614 strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam)); 615 pty_master = -1; 616 pty_slave = -1; 617 real_ttyfd = -1; 618 if (using_pty || record_file != NULL) { 619 if (!get_pty(&pty_master, &pty_slave, ppp_devnam, uid)) { 620 error("Couldn't allocate pseudo-tty"); 621 status = EXIT_FATAL_ERROR; 622 return -1; 623 } 624#ifdef __APPLE__ 625 clocal = 1; 626#endif 627 set_up_tty(pty_slave, 1); 628 } 629 630 /* 631 * Lock the device if we've been asked to. 632 */ 633#ifndef __APPLE__ 634 status = EXIT_LOCK_FAILED; 635#endif 636 if (lockflag && !privopen) { 637 if (lock(devnam) < 0) { 638#ifdef __APPLE__ 639 status = EXIT_LOCK_FAILED; 640#endif 641 return -1; 642 } 643 locked = 1; 644 } 645 646 /* 647 * Open the serial device and set it up to be the ppp interface. 648 * First we open it in non-blocking mode so we can set the 649 * various termios flags appropriately. If we aren't dialling 650 * out and we want to use the modem lines, we reopen it later 651 * in order to wait for the carrier detect signal from the modem. 652 */ 653 hungup = 0; 654 do_modem_hungup = 0; 655 656#ifdef __APPLE__ 657 // race condition, we can get the SIGTERM before we had time to start the connector 658#else 659 kill_link = 0; 660#endif 661#ifdef __APPLE__ 662 // Fix me : alternate with call back 663 if (redialingalternate && altconnect_script) { 664 connector = altconnect_script; 665 connector_data = altconnect_data; 666 connector_data_len = altconnect_data_len; 667 } 668 else { 669 if (doing_callback) { 670 connector = callback_script; 671 connector_data = NULL; 672 connector_data_len = 0; 673 } 674 else { 675 connector = connect_script; 676 connector_data = connect_data; 677 connector_data_len = connect_data_len; 678 } 679 } 680#else 681 connector = doing_callback? callback_script: connect_script; 682#endif 683 if (devnam[0] != 0) { 684 for (;;) { 685 /* If the user specified the device name, become the 686 user before opening it. */ 687 int err, prio; 688 689 prio = privopen? OPRIO_ROOT: tty_options[0].priority; 690 if (prio < OPRIO_ROOT) 691 seteuid(uid); 692 ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0); 693 err = errno; 694 if (prio < OPRIO_ROOT) 695 seteuid(0); 696 if (ttyfd >= 0) { 697#ifdef __APPLE__ 698 // try to acquire the port exclusively 699 if (ioctl(ttyfd, TIOCEXCL, 0) < 0) 700 err = errno; 701 else 702#endif 703 break; 704 } 705 errno = err; 706 if (err != EINTR) { 707#ifdef __APPLE__ 708 if (ttyfd >= 0) { 709 error("Failed to acquire %s in exclusive mode : %m", devnam); 710 close(ttyfd); 711 ttyfd = -1; 712 } 713 else 714#endif 715 error("Failed to open %s: %m", devnam); 716 status = EXIT_OPEN_FAILED; 717 } 718 if (!persist || err != EINTR) 719 return -1; 720 } 721 real_ttyfd = ttyfd; 722 if ((fdflags = fcntl(ttyfd, F_GETFL)) == -1 723 || fcntl(ttyfd, F_SETFL, fdflags & ~O_NONBLOCK) < 0) 724 warning("Couldn't reset non-blocking mode on device: %m"); 725 726#ifndef __linux__ 727 /* 728 * Linux 2.4 and above blocks normal writes to the tty 729 * when it is in PPP line discipline, so this isn't needed. 730 */ 731 /* 732 * Do the equivalent of `mesg n' to stop broadcast messages. 733 */ 734 if (fstat(ttyfd, &statbuf) < 0 735 || fchmod(ttyfd, statbuf.st_mode & ~(S_IWGRP | S_IWOTH)) < 0) { 736 warning("Couldn't restrict write permissions to %s: %m", devnam); 737 } else 738 tty_mode = statbuf.st_mode; 739#endif /* __linux__ */ 740 741 /* 742 * Set line speed, flow control, etc. 743 * If we have a non-null connection or initializer script, 744 * on most systems we set CLOCAL for now so that we can talk 745 * to the modem before carrier comes up. But this has the 746 * side effect that we might miss it if CD drops before we 747 * get to clear CLOCAL below. On systems where we can talk 748 * successfully to the modem with CLOCAL clear and CD down, 749 * we could clear CLOCAL at this point. 750 */ 751#ifdef __APPLE__ 752 clocal = ((connector != NULL && connector[0] != 0) 753 || initializer != NULL); 754#endif 755 set_up_tty(ttyfd, ((connector != NULL && connector[0] != 0) 756 || initializer != NULL)); 757 } 758 759 /* 760 * If the pty, socket, notty and/or record option was specified, 761 * start up the character shunt now. 762 */ 763#ifdef __APPLE__ 764 if (kill_link) 765 return -1; 766#endif 767 status = EXIT_PTYCMD_FAILED; 768 if (ptycommand != NULL) { 769 if (record_file != NULL) { 770 int ipipe[2], opipe[2], ok; 771 772 if (pipe(ipipe) < 0 || pipe(opipe) < 0) 773 fatal("Couldn't create pipes for record option: %m"); 774 775 /* don't leak these to the ptycommand */ 776 (void) fcntl(ipipe[0], F_SETFD, FD_CLOEXEC); 777 (void) fcntl(opipe[1], F_SETFD, FD_CLOEXEC); 778 779 ok = device_script(ptycommand, opipe[0], ipipe[1], 1, -1, 0, 0) == 0 780 && start_charshunt(ipipe[0], opipe[1]); 781 close(ipipe[0]); 782 close(ipipe[1]); 783 close(opipe[0]); 784 close(opipe[1]); 785 if (!ok) 786 return -1; 787 } else { 788#ifdef __APPLE__ 789 notify(connectscript_started_notify, 0); 790#endif 791 if (device_script(ptycommand, pty_master, pty_master, 1, -1, 0, 0) < 0) 792 return -1; 793 ttyfd = pty_slave; 794 close(pty_master); 795 pty_master = -1; 796#ifdef __APPLE__ 797 if (pty_delay && wait_input_fd(ttyfd, pty_delay) <= 0) 798 return -1; 799 notify(connectscript_finished_notify, 0); 800#endif 801 } 802 } else if (pty_socket != NULL) { 803 int fd = open_socket(pty_socket); 804 if (fd < 0) 805 return -1; 806 if (!start_charshunt(fd, fd)) 807 return -1; 808 } else if (notty) { 809 if (!start_charshunt(0, 1)) 810 return -1; 811 } else if (record_file != NULL) { 812 if (!start_charshunt(ttyfd, ttyfd)) 813 return -1; 814 } 815 816 /* run connection script */ 817 if ((connector && connector[0]) || initializer) { 818 if (real_ttyfd != -1) { 819 /* XXX do this if doing_callback == CALLBACK_DIALIN? */ 820 if (!default_device && modem) { 821 setdtr(real_ttyfd, 0); /* in case modem is off hook */ 822 sleep(1); 823 setdtr(real_ttyfd, 1); 824 } 825 } 826 827 if (initializer && initializer[0]) { 828#ifdef __APPLE__ 829 if (kill_link) /* check if SIGTERM arrived before we had time to start the script */ 830 return -1; 831 notify(initscript_started_notify, 0); 832 if (device_script(initializer, ttyfd, ttyfd, 0, -1, 0, 0) != 0) { 833#else 834 if (device_script(initializer, ttyfd, ttyfd, 0) < 0) { 835#endif 836 error("Initializer script failed"); 837 status = EXIT_INIT_FAILED; 838 return -1; 839 } 840 if (kill_link) { 841 disconnect_tty(); 842 return -1; 843 } 844#ifdef __APPLE__ 845 notify(initscript_finished_notify, 0); 846#endif 847 info("Serial port initialized."); 848 } 849 850 if (connector && connector[0]) { 851#ifdef __APPLE__ 852 if (kill_link) /* check if SIGTERM arrived before we had time to start the script */ 853 return -1; 854 notify(connectscript_started_notify, 0); 855 if ((*errorcode = device_script(connector, ttyfd, ttyfd, 0, connector_uid, connector_data, connector_data_len)) != 0) { 856#else 857 if (device_script(connector, ttyfd, ttyfd, 0) < 0) { 858#endif 859#ifdef __APPLE__ 860 if (cancelcode != -1 && *errorcode == cancelcode) { 861 status = EXIT_USER_REQUEST; 862 return -2; 863 } 864#endif 865 error("Connect script failed"); 866 status = EXIT_CONNECT_FAILED; 867 return -1; 868 } 869 if (kill_link) { 870#ifdef __APPLE__ 871 return -2; 872#else 873 disconnect_tty(); 874 return -1; 875#endif 876 } 877#ifdef __APPLE__ 878 notify(connectscript_finished_notify, 0); 879#endif 880 info("Serial connection established."); 881 882#ifdef __APPLE__ 883 if (link_up_hook 884 && ((*link_up_hook)() == 0)) { 885 // cancelled 886 status = EXIT_USER_REQUEST; 887 return -2; 888 } 889 link_up_done = 1; 890#endif 891 } 892 893 /* set line speed, flow control, etc.; 894 clear CLOCAL if modem option */ 895 if (real_ttyfd != -1) 896#ifdef __APPLE__ 897 { 898 int state = 0; 899 900 /* 901 Check for CARRIER. Some devices don't report it. 902 If TIOCM_CD flag is not set, don't drop CLOCAL. 903 */ 904 if (ioctl(real_ttyfd, TIOCMGET, &state) != -1 905 && (state & TIOCM_CD)) { 906 clocal = 0; 907 set_up_tty_local(real_ttyfd, 0); 908 } 909 } 910 911#else 912 set_up_tty(real_ttyfd, 0); 913#endif 914 915 if (doing_callback == CALLBACK_DIALIN) { 916 connector = NULL; 917 connector_data = NULL; 918 connector_data_len = 0; 919 } 920 } 921 922 /* reopen tty if necessary to wait for carrier */ 923 if (connector == NULL && modem && devnam[0] != 0) { 924 int i; 925#ifdef __APPLE__ 926 // release the lock on the port 927 ioctl(ttyfd, TIOCNXCL, 0); 928#endif 929 for (;;) { 930 if ((i = open(devnam, O_RDWR)) >= 0) 931 break; 932 if (errno != EINTR) { 933 error("Failed to reopen %s: %m", devnam); 934 status = EXIT_OPEN_FAILED; 935 } 936 if (!persist || errno != EINTR || hungup || kill_link) 937 return -1; 938 } 939#ifdef __APPLE__ 940 // try to reacquire the port exclusively 941 if (ioctl(i, TIOCEXCL, 0) < 0) { 942 error("Failed to reacquire the port %s exclusively: %m", devnam); 943 status = EXIT_OPEN_FAILED; 944 close(i); 945 return -1; 946 } 947#endif 948 close(i); 949 } 950 951 slprintf(numbuf, sizeof(numbuf), "%d", baud_rate); 952 script_setenv("SPEED", numbuf, 0); 953 954#ifdef __APPLE__ 955 /* run terminal script */ 956 /* script to connect to the host are different than script to connect the modem */ 957 if (terminal_window_hook || (terminal_script && terminal_script[0])) { 958 if (kill_link) 959 return -2; 960 notify(terminalscript_started_notify, 0); 961 962 if (terminal_window_hook) 963 *errorcode = (*terminal_window_hook)(terminal_script, ttyfd, ttyfd); 964 else 965 *errorcode = device_script(terminal_script, ttyfd, ttyfd, 0, -1, terminal_data, terminal_data_len); 966 967 if (*errorcode) { 968 if (cancelcode != -1 && *errorcode == cancelcode) { 969 status = EXIT_USER_REQUEST; 970 } 971 else { 972 error("Terminal script failed"); 973 status = EXIT_TERMINAL_FAILED; 974 } 975 return -2; 976 } 977 if (kill_link) 978 return -2; 979 notify(terminalscript_finished_notify, 0); 980 info("Terminal connection established."); 981 } 982#endif 983 984 /* run welcome script, if any */ 985 if (welcomer && welcomer[0]) { 986#ifdef __APPLE__ 987 if (device_script(welcomer, ttyfd, ttyfd, 0, -1, 0, 0) != 0) 988#else 989 if (device_script(welcomer, ttyfd, ttyfd, 0) < 0) 990#endif 991 warning("Welcome script failed"); 992 } 993 994 /* 995 * If we are initiating this connection, wait for a short 996 * time for something from the peer. This can avoid bouncing 997 * our packets off his tty before he has it set up. 998 */ 999 if (connector != NULL || ptycommand != NULL) 1000 listen_time = connect_delay; 1001 1002 return ttyfd; 1003} 1004 1005 1006void disconnect_tty() 1007{ 1008 1009 if (disconnect_script == NULL || hungup) 1010 return; 1011 if (real_ttyfd >= 0) 1012#ifdef __APPLE__ 1013 clocal = 1; 1014 set_up_tty_local(real_ttyfd, 1); 1015#else 1016 set_up_tty(real_ttyfd, 1); 1017#endif 1018#ifdef __APPLE__ 1019 if (device_script(disconnect_script, ttyfd, ttyfd, 0, disconnector_uid, disconnect_data, disconnect_data_len) != 0) { 1020#else 1021 if (device_script(disconnect_script, ttyfd, ttyfd, 0) < 0) { 1022#endif 1023 warning("disconnect script failed"); 1024 } else { 1025 info("Serial link disconnected."); 1026 } 1027} 1028 1029void tty_close_fds() 1030{ 1031 if (pty_master >= 0) 1032 close(pty_master); 1033 if (pty_slave >= 0) 1034 close(pty_slave); 1035 if (real_ttyfd >= 0) { 1036 close(real_ttyfd); 1037 real_ttyfd = -1; 1038 } 1039 /* N.B. ttyfd will == either pty_slave or real_ttyfd */ 1040} 1041 1042void cleanup_tty() 1043{ 1044 if (real_ttyfd >= 0) 1045 finish_tty(); 1046 tty_close_fds(); 1047 if (locked) { 1048 unlock(); 1049 locked = 0; 1050 } 1051} 1052 1053/* 1054 * tty_do_send_config - set transmit-side PPP configuration. 1055 * We set the extended transmit ACCM here as well. 1056 */ 1057void 1058tty_do_send_config(mtu, accm, pcomp, accomp) 1059 int mtu; 1060 u_int32_t accm; 1061 int pcomp, accomp; 1062{ 1063 tty_set_xaccm(xmit_accm); 1064 tty_send_config(mtu, accm, pcomp, accomp); 1065} 1066 1067/* 1068 * finish_tty - restore the terminal device to its original settings 1069 */ 1070static void 1071finish_tty() 1072{ 1073 /* drop dtr to hang up */ 1074 if (!default_device && modem) { 1075 setdtr(real_ttyfd, 0); 1076 /* 1077 * This sleep is in case the serial port has CLOCAL set by default, 1078 * and consequently will reassert DTR when we close the device. 1079 */ 1080 sleep(1); 1081 } 1082 1083 restore_tty(real_ttyfd); 1084 1085#ifndef __linux__ 1086 if (tty_mode != (mode_t) -1) { 1087 if (fchmod(real_ttyfd, tty_mode) != 0) { 1088 /* XXX if devnam is a symlink, this will change the link */ 1089 chmod(devnam, tty_mode); 1090 } 1091 } 1092#endif /* __linux__ */ 1093 1094 close(real_ttyfd); 1095 real_ttyfd = -1; 1096} 1097 1098/* 1099 * maybe_relock - our PID has changed, maybe update the lock file. 1100 */ 1101static void 1102maybe_relock(arg, pid) 1103 void *arg; 1104 uintptr_t pid; 1105{ 1106 if (locked) 1107 relock(pid); 1108} 1109 1110/* 1111 * open_socket - establish a stream socket connection to the nominated 1112 * host and port. 1113 */ 1114static int 1115open_socket(dest) 1116 char *dest; 1117{ 1118 char *sep, *endp = NULL; 1119 int sock, port = -1; 1120 u_int32_t host; 1121 struct hostent *hent; 1122 struct sockaddr_in sad; 1123 1124 /* parse host:port and resolve host to an IP address */ 1125 sep = strchr(dest, ':'); 1126 if (sep != NULL) 1127 port = strtol(sep+1, &endp, 10); 1128 if (port < 0 || endp == sep+1 || sep == dest) { 1129 error("Can't parse host:port for socket destination"); 1130 return -1; 1131 } 1132 *sep = 0; 1133 host = inet_addr(dest); 1134 if (host == (u_int32_t) -1) { 1135 hent = gethostbyname(dest); 1136 if (hent == NULL) { 1137 error("%s: unknown host in socket option", dest); 1138 *sep = ':'; 1139 return -1; 1140 } 1141 memcpy(&host, hent->h_addr_list[0], sizeof(u_int32_t)); // Wcast-align fix - using memcpy for unknown alignment 1142 } 1143 *sep = ':'; 1144 1145 /* get a socket and connect it to the other end */ 1146 sock = socket(PF_INET, SOCK_STREAM, 0); 1147 if (sock < 0) { 1148 error("Can't create socket: %m"); 1149 return -1; 1150 } 1151 memset(&sad, 0, sizeof(sad)); 1152 sad.sin_family = AF_INET; 1153 sad.sin_port = htons(port); 1154 sad.sin_addr.s_addr = host; 1155 if (connect(sock, (struct sockaddr *)&sad, sizeof(sad)) < 0) { 1156 error("Can't connect to %s: %m", dest); 1157 close(sock); 1158 return -1; 1159 } 1160 1161 return sock; 1162} 1163 1164 1165/* 1166 * start_charshunt - create a child process to run the character shunt. 1167 */ 1168static int 1169start_charshunt(ifd, ofd) 1170 int ifd, ofd; 1171{ 1172 int cpid; 1173 1174 cpid = safe_fork(); 1175 if (cpid == -1) { 1176 error("Can't fork process for character shunt: %m"); 1177 return 0; 1178 } 1179 if (cpid == 0) { 1180 /* child */ 1181 close(pty_slave); 1182 setuid(uid); 1183 if (getuid() != uid) 1184 fatal("setuid failed"); 1185 setgid(getgid()); 1186#ifdef __APPLE__ 1187 sys_close(); 1188#endif 1189 if (!nodetach) 1190 log_to_fd = -1; 1191 charshunt(ifd, ofd, record_file); 1192 exit(0); 1193 } 1194 charshunt_pid = cpid; 1195 add_notifier(&sigreceived, stop_charshunt, 0); 1196 close(pty_master); 1197 pty_master = -1; 1198 ttyfd = pty_slave; 1199 record_child(cpid, "pppd (charshunt)", charshunt_done, NULL); 1200 return 1; 1201} 1202 1203static void 1204charshunt_done(arg) 1205 void *arg; 1206{ 1207 charshunt_pid = 0; 1208} 1209 1210static void 1211stop_charshunt(arg, sig) 1212 void *arg; 1213 uintptr_t sig; 1214{ 1215 if (charshunt_pid) 1216 kill(charshunt_pid, (sig == SIGINT? sig: SIGTERM)); 1217} 1218 1219/* 1220 * charshunt - the character shunt, which passes characters between 1221 * the pty master side and the serial port (or stdin/stdout). 1222 * This runs as the user (not as root). 1223 * (We assume ofd >= ifd which is true the way this gets called. :-). 1224 */ 1225static void 1226charshunt(ifd, ofd, record_file) 1227 int ifd, ofd; 1228 char *record_file; 1229{ 1230 int n, nfds; 1231 fd_set ready, writey; 1232 u_char *ibufp, *obufp; 1233 int nibuf, nobuf; 1234 int flags; 1235 int pty_readable, stdin_readable; 1236 struct timeval lasttime; 1237 FILE *recordf = NULL; 1238 int ilevel, olevel, max_level; 1239 struct timeval levelt, tout, *top; 1240 extern u_char inpacket_buf[]; 1241 1242 /* 1243 * Reset signal handlers. 1244 */ 1245 signal(SIGHUP, SIG_IGN); /* Hangup */ 1246 signal(SIGINT, SIG_DFL); /* Interrupt */ 1247 signal(SIGTERM, SIG_DFL); /* Terminate */ 1248 signal(SIGCHLD, SIG_DFL); 1249 signal(SIGUSR1, SIG_DFL); 1250 signal(SIGUSR2, SIG_DFL); 1251 signal(SIGABRT, SIG_DFL); 1252 signal(SIGALRM, SIG_DFL); 1253 signal(SIGFPE, SIG_DFL); 1254 signal(SIGILL, SIG_DFL); 1255 signal(SIGPIPE, SIG_DFL); 1256 signal(SIGQUIT, SIG_DFL); 1257 signal(SIGSEGV, SIG_DFL); 1258#ifdef SIGBUS 1259 signal(SIGBUS, SIG_DFL); 1260#endif 1261#ifdef SIGEMT 1262 signal(SIGEMT, SIG_DFL); 1263#endif 1264#ifdef SIGPOLL 1265 signal(SIGPOLL, SIG_DFL); 1266#endif 1267#ifdef SIGPROF 1268 signal(SIGPROF, SIG_DFL); 1269#endif 1270#ifdef SIGSYS 1271 signal(SIGSYS, SIG_DFL); 1272#endif 1273#ifdef SIGTRAP 1274 signal(SIGTRAP, SIG_DFL); 1275#endif 1276#ifdef SIGVTALRM 1277 signal(SIGVTALRM, SIG_DFL); 1278#endif 1279#ifdef SIGXCPU 1280 signal(SIGXCPU, SIG_DFL); 1281#endif 1282#ifdef SIGXFSZ 1283 signal(SIGXFSZ, SIG_DFL); 1284#endif 1285 1286 /* 1287 * Check that the fds won't overrun the fd_sets 1288 */ 1289 if (ifd >= FD_SETSIZE || ofd >= FD_SETSIZE || pty_master >= FD_SETSIZE) 1290 fatal("internal error: file descriptor too large (%d, %d, %d)", 1291 ifd, ofd, pty_master); 1292 1293 /* 1294 * Open the record file if required. 1295 */ 1296 if (record_file != NULL) { 1297 recordf = fopen(record_file, "a"); 1298 if (recordf == NULL) 1299 error("Couldn't create record file %s: %m", record_file); 1300 } 1301 1302 /* set all the fds to non-blocking mode */ 1303 flags = fcntl(pty_master, F_GETFL); 1304 if (flags == -1 1305 || fcntl(pty_master, F_SETFL, flags | O_NONBLOCK) == -1) 1306 warning("couldn't set pty master to nonblock: %m"); 1307 flags = fcntl(ifd, F_GETFL); 1308 if (flags == -1 1309 || fcntl(ifd, F_SETFL, flags | O_NONBLOCK) == -1) 1310 warning("couldn't set %s to nonblock: %m", (ifd==0? "stdin": "tty")); 1311 if (ofd != ifd) { 1312 flags = fcntl(ofd, F_GETFL); 1313 if (flags == -1 1314 || fcntl(ofd, F_SETFL, flags | O_NONBLOCK) == -1) 1315 warning("couldn't set stdout to nonblock: %m"); 1316 } 1317 1318 nibuf = nobuf = 0; 1319 ibufp = obufp = NULL; 1320 pty_readable = stdin_readable = 1; 1321 1322 ilevel = olevel = 0; 1323 gettimeofday(&levelt, NULL); 1324 if (max_data_rate) { 1325 max_level = max_data_rate / 10; 1326 if (max_level < 100) 1327 max_level = 100; 1328 } else 1329 max_level = PPP_MRU + PPP_HDRLEN + 1; 1330 1331 nfds = (ofd > pty_master? ofd: pty_master) + 1; 1332 if (recordf != NULL) { 1333 gettimeofday(&lasttime, NULL); 1334 putc(7, recordf); /* put start marker */ 1335 putc(lasttime.tv_sec >> 24, recordf); 1336 putc(lasttime.tv_sec >> 16, recordf); 1337 putc(lasttime.tv_sec >> 8, recordf); 1338 putc(lasttime.tv_sec, recordf); 1339 lasttime.tv_usec = 0; 1340 } 1341 1342 while (nibuf != 0 || nobuf != 0 || pty_readable || stdin_readable) { 1343 top = 0; 1344 tout.tv_sec = 0; 1345 tout.tv_usec = 10000; 1346 FD_ZERO(&ready); 1347 FD_ZERO(&writey); 1348 if (nibuf != 0) { 1349 if (ilevel >= max_level) 1350 top = &tout; 1351 else 1352 FD_SET(pty_master, &writey); 1353 } else if (stdin_readable) 1354 FD_SET(ifd, &ready); 1355 if (nobuf != 0) { 1356 if (olevel >= max_level) 1357 top = &tout; 1358 else 1359 FD_SET(ofd, &writey); 1360 } else if (pty_readable) 1361 FD_SET(pty_master, &ready); 1362 if (select(nfds, &ready, &writey, NULL, top) < 0) { 1363 if (errno != EINTR) 1364 fatal("select"); 1365 continue; 1366 } 1367 if (max_data_rate) { 1368 double dt; 1369 int nbt; 1370 struct timeval now; 1371 1372 gettimeofday(&now, NULL); 1373 dt = (now.tv_sec - levelt.tv_sec 1374 + (now.tv_usec - levelt.tv_usec) / 1e6); 1375 nbt = (int)(dt * max_data_rate); 1376 ilevel = (nbt < 0 || nbt > ilevel)? 0: ilevel - nbt; 1377 olevel = (nbt < 0 || nbt > olevel)? 0: olevel - nbt; 1378 levelt = now; 1379 } else 1380 ilevel = olevel = 0; 1381 if (FD_ISSET(ifd, &ready)) { 1382 ibufp = inpacket_buf; 1383 nibuf = read(ifd, ibufp, PPP_MRU + PPP_HDRLEN); 1384 if (nibuf < 0 && errno == EIO) 1385 nibuf = 0; 1386 if (nibuf < 0) { 1387 if (!(errno == EINTR || errno == EAGAIN)) { 1388 error("Error reading standard input: %m"); 1389 break; 1390 } 1391 nibuf = 0; 1392 } else if (nibuf == 0) { 1393 /* end of file from stdin */ 1394 stdin_readable = 0; 1395 /* do a 0-length write, hopefully this will generate 1396 an EOF (hangup) on the slave side. */ 1397 write(pty_master, inpacket_buf, 0); 1398 if (recordf) 1399 if (!record_write(recordf, 4, NULL, 0, &lasttime)) 1400 recordf = NULL; 1401 } else { 1402 FD_SET(pty_master, &writey); 1403 if (recordf) 1404 if (!record_write(recordf, 2, ibufp, nibuf, &lasttime)) 1405 recordf = NULL; 1406 } 1407 } 1408 if (FD_ISSET(pty_master, &ready)) { 1409 obufp = outpacket_buf; 1410 nobuf = read(pty_master, obufp, PPP_MRU + PPP_HDRLEN); 1411 if (nobuf < 0 && errno == EIO) 1412 nobuf = 0; 1413 if (nobuf < 0) { 1414 if (!(errno == EINTR || errno == EAGAIN)) { 1415 error("Error reading pseudo-tty master: %m"); 1416 break; 1417 } 1418 nobuf = 0; 1419 } else if (nobuf == 0) { 1420 /* end of file from the pty - slave side has closed */ 1421 pty_readable = 0; 1422 stdin_readable = 0; /* pty is not writable now */ 1423 nibuf = 0; 1424 close(ofd); 1425 if (recordf) 1426 if (!record_write(recordf, 3, NULL, 0, &lasttime)) 1427 recordf = NULL; 1428 } else { 1429 FD_SET(ofd, &writey); 1430 if (recordf) 1431 if (!record_write(recordf, 1, obufp, nobuf, &lasttime)) 1432 recordf = NULL; 1433 } 1434 } 1435 if (FD_ISSET(ofd, &writey)) { 1436 n = nobuf; 1437 if (olevel + n > max_level) 1438 n = max_level - olevel; 1439 n = write(ofd, obufp, n); 1440 if (n < 0) { 1441 if (errno == EIO) { 1442 pty_readable = 0; 1443 nobuf = 0; 1444 } else if (errno != EAGAIN && errno != EINTR) { 1445 error("Error writing standard output: %m"); 1446 break; 1447 } 1448 } else { 1449 obufp += n; 1450 nobuf -= n; 1451 olevel += n; 1452 } 1453 } 1454 if (FD_ISSET(pty_master, &writey)) { 1455 n = nibuf; 1456 if (ilevel + n > max_level) 1457 n = max_level - ilevel; 1458 n = write(pty_master, ibufp, n); 1459 if (n < 0) { 1460 if (errno == EIO) { 1461 stdin_readable = 0; 1462 nibuf = 0; 1463 } else if (errno != EAGAIN && errno != EINTR) { 1464 error("Error writing pseudo-tty master: %m"); 1465 break; 1466 } 1467 } else { 1468 ibufp += n; 1469 nibuf -= n; 1470 ilevel += n; 1471 } 1472 } 1473 } 1474 exit(0); 1475} 1476 1477static int 1478record_write(f, code, buf, nb, tp) 1479 FILE *f; 1480 int code; 1481 u_char *buf; 1482 int nb; 1483 struct timeval *tp; 1484{ 1485 struct timeval now; 1486 int diff; 1487 1488 gettimeofday(&now, NULL); 1489 now.tv_usec /= 100000; /* actually 1/10 s, not usec now */ 1490 diff = (now.tv_sec - tp->tv_sec) * 10 + (now.tv_usec - tp->tv_usec); 1491 if (diff > 0) { 1492 if (diff > 255) { 1493 putc(5, f); 1494 putc(diff >> 24, f); 1495 putc(diff >> 16, f); 1496 putc(diff >> 8, f); 1497 putc(diff, f); 1498 } else { 1499 putc(6, f); 1500 putc(diff, f); 1501 } 1502 *tp = now; 1503 } 1504 putc(code, f); 1505 if (buf != NULL) { 1506 putc(nb >> 8, f); 1507 putc(nb, f); 1508 fwrite(buf, nb, 1, f); 1509 } 1510 fflush(f); 1511 if (ferror(f)) { 1512 error("Error writing record file: %m"); 1513 return 0; 1514 } 1515 return 1; 1516} 1517 1518#ifdef __APPLE__ 1519/* 1520 special option to cancel the use of a device/speed/connect/disconnect script 1521 when pty option has been specified after the device parameters 1522 */ 1523static int 1524forcepty(argv) 1525 char **argv; 1526{ 1527 connect_script = 0; 1528 initializer = 0; 1529 disconnect_script = 0; 1530 default_device = 1; 1531 devnam[0] = 0; 1532 inspeed = 0; 1533 return 1; 1534} 1535 1536/* ----------------------------------------------------------------------------- 1537signal notifier 1538----------------------------------------------------------------------------- */ 1539static void 1540sighup_tty(arg, sig) 1541 void *arg; 1542 uintptr_t sig; 1543{ 1544 int state = 0; 1545 1546 if (sig != SIGHUP) 1547 return; 1548 1549 if (real_ttyfd == -1) 1550 return; 1551 1552 /* 1553 SIGHUP is received in the following cases : 1554 - USER DISCONNECT : does nothing here, let the main handler start 1555 the disconnection procedure 1556 - CARRIER goes down : calls to ioctl will succeed, then check line state, 1557 and handle the link disconnection case 1558 - HOT UNPLUG : any subsequent calls will return EIO 1559 handle the link disconnection case 1560 Note : signals are received only when CLOCAL is not set 1561 */ 1562 1563 // always "user disconnection" if connection is on-hold 1564 if (phase == PHASE_ONHOLD) 1565 return; 1566 1567 if (ioctl(real_ttyfd, TIOCMGET, &state) < 0) { 1568 // IOSerialFamily returns EIO after HotUnplug 1569 // but should probably disconnect for any error 1570 } 1571 1572 if (!clocal && (state & TIOCM_CD) == 0) { 1573 hungup = 1; 1574 do_modem_hungup = 1; 1575 // it's OK to get a hangup during terminate phase 1576 if (phase != PHASE_TERMINATE && phase != PHASE_DISCONNECT) { 1577 status = EXIT_HANGUP; 1578 } 1579 return; 1580 } 1581 1582 return; 1583} 1584 1585#endif 1586