⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tty.c

📁 经典的ppp程序
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * tty.c - code for handling serial ports in pppd. * * Copyright (C) 2000 Paul Mackerras. * All rights reserved. * * Portions Copyright (c) 1989 Carnegie Mellon University. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by Carnegie Mellon University.  The name of the * University may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */#define RCSID	"$Id: tty.c,v 1.6 2001/03/12 22:59:01 paulus Exp $"#include <stdio.h>#include <ctype.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <signal.h>#include <errno.h>#include <fcntl.h>#include <syslog.h>#include <netdb.h>#include <utmp.h>#include <pwd.h>#include <setjmp.h>#include <sys/param.h>#include <sys/types.h>#include <sys/wait.h>#include <sys/time.h>#include <sys/resource.h>#include <sys/stat.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include "pppd.h"#include "fsm.h"#include "lcp.h"void tty_process_extra_options __P((void));void tty_check_options __P((void));int  connect_tty __P((void));void disconnect_tty __P((void));void tty_close_fds __P((void));void cleanup_tty __P((void));void tty_do_send_config __P((int, u_int32_t, int, int));static int setdevname __P((char *, char **, int));static int setspeed __P((char *, char **, int));static int setxonxoff __P((char **));static int setescape __P((char **));static void printescape __P((option_t *, void (*)(void *, char *,...),void *));static void finish_tty __P((void));static int start_charshunt __P((int, int));static void stop_charshunt __P((void *, int));static void charshunt_done __P((void *));static void charshunt __P((int, int, char *));static int record_write __P((FILE *, int code, u_char *buf, int nb,			     struct timeval *));static int open_socket __P((char *));static void maybe_relock __P((void *, int));static int pty_master;		/* fd for master side of pty */static int pty_slave;		/* fd for slave side of pty */static int real_ttyfd;		/* fd for actual serial port (not pty) */static int ttyfd;		/* Serial port file descriptor */static char speed_str[16];	/* Serial port speed as string */mode_t tty_mode = (mode_t)-1;	/* Original access permissions to tty */int baud_rate;			/* Actual bits/second for serial device */char *callback_script;		/* script for doing callback */int charshunt_pid;		/* Process ID for charshunt */int locked;			/* lock() has succeeded */struct stat devstat;		/* result of stat() on devnam *//* option variables */int	crtscts = 0;		/* Use hardware flow control */bool	modem = 1;		/* Use modem control lines */int	inspeed = 0;		/* Input/Output speed requested */bool	lockflag = 0;		/* Create lock file to lock the serial dev */char	*initializer = NULL;	/* Script to initialize physical link */char	*connect_script = NULL;	/* Script to establish physical link */char	*disconnect_script = NULL; /* Script to disestablish physical link */char	*welcomer = NULL;	/* Script to run after phys link estab. */char	*ptycommand = NULL;	/* Command to run on other side of pty */bool	notty = 0;		/* Stdin/out is not a tty */char	*record_file = NULL;	/* File to record chars sent/received */int	max_data_rate;		/* max bytes/sec through charshunt */bool	sync_serial = 0;	/* Device is synchronous serial device */char	*pty_socket = NULL;	/* Socket to connect to pty */int	using_pty = 0;		/* we're allocating a pty as the device */extern uid_t uid;extern int kill_link;/* XXX */extern int privopen;		/* don't lock, open device as root */u_int32_t xmit_accm[8];		/* extended transmit ACCM *//* option descriptors */option_t tty_options[] = {    /* device name must be first, or change connect_tty() below! */    { "device name", o_wild, (void *) &setdevname,      "Serial port device name",      OPT_DEVNAM | OPT_PRIVFIX | OPT_NOARG  | OPT_A2STRVAL | OPT_STATIC,      devnam},    { "tty speed", o_wild, (void *) &setspeed,      "Baud rate for serial port",      OPT_PRIO | OPT_NOARG | OPT_A2STRVAL | OPT_STATIC, speed_str },    { "lock", o_bool, &lockflag,      "Lock serial device with UUCP-style lock file", OPT_PRIO | 1 },    { "nolock", o_bool, &lockflag,      "Don't lock serial device", OPT_PRIOSUB | OPT_PRIV },    { "init", o_string, &initializer,      "A program to initialize the device", OPT_PRIO | OPT_PRIVFIX },    { "connect", o_string, &connect_script,      "A program to set up a connection", OPT_PRIO | OPT_PRIVFIX },    { "disconnect", o_string, &disconnect_script,      "Program to disconnect serial device", OPT_PRIO | OPT_PRIVFIX },    { "welcome", o_string, &welcomer,      "Script to welcome client", OPT_PRIO | OPT_PRIVFIX },    { "pty", o_string, &ptycommand,      "Script to run on pseudo-tty master side",      OPT_PRIO | OPT_PRIVFIX | OPT_DEVNAM },    { "notty", o_bool, &notty,      "Input/output is not a tty", OPT_DEVNAM | 1 },    { "socket", o_string, &pty_socket,      "Send and receive over socket, arg is host:port",      OPT_PRIO | OPT_DEVNAM },    { "record", o_string, &record_file,      "Record characters sent/received to file", OPT_PRIO },    { "crtscts", o_int, &crtscts,      "Set hardware (RTS/CTS) flow control",      OPT_PRIO | OPT_NOARG | OPT_VAL(1) },    { "cdtrcts", o_int, &crtscts,      "Set alternate hardware (DTR/CTS) flow control",      OPT_PRIOSUB | OPT_NOARG | OPT_VAL(2) },    { "nocrtscts", o_int, &crtscts,      "Disable hardware flow control",      OPT_PRIOSUB | OPT_NOARG | OPT_VAL(-1) },    { "-crtscts", o_int, &crtscts,      "Disable hardware flow control",      OPT_PRIOSUB | OPT_ALIAS | OPT_NOARG | OPT_VAL(-1) },    { "nocdtrcts", o_int, &crtscts,      "Disable hardware flow control",      OPT_PRIOSUB | OPT_ALIAS | OPT_NOARG | OPT_VAL(-1) },    { "xonxoff", o_special_noarg, (void *)setxonxoff,      "Set software (XON/XOFF) flow control", OPT_PRIOSUB },    { "modem", o_bool, &modem,      "Use modem control lines", OPT_PRIO | 1 },    { "local", o_bool, &modem,      "Don't use modem control lines", OPT_PRIOSUB | 0 },    { "sync", o_bool, &sync_serial,      "Use synchronous HDLC serial encoding", 1 },    { "datarate", o_int, &max_data_rate,      "Maximum data rate in bytes/sec (with pty, notty or record option)",      OPT_PRIO },    { "escape", o_special, (void *)setescape,      "List of character codes to escape on transmission",      OPT_A2PRINTER, (void *)printescape },    { NULL }};struct channel tty_channel = {	tty_options,	&tty_process_extra_options,	&tty_check_options,	&connect_tty,	&disconnect_tty,	&tty_establish_ppp,	&tty_disestablish_ppp,	&tty_do_send_config,	&tty_recv_config,	&cleanup_tty,	&tty_close_fds};/* * setspeed - Set the serial port baud rate. * If doit is 0, the call is to check whether this option is * potentially a speed value. */static intsetspeed(arg, argv, doit)    char *arg;    char **argv;    int doit;{	char *ptr;	int spd;	spd = strtol(arg, &ptr, 0);	if (ptr == arg || *ptr != 0 || spd == 0)		return 0;	if (doit) {		inspeed = spd;		slprintf(speed_str, sizeof(speed_str), "%d", spd);	}	return 1;}/* * setdevname - Set the device name. * If doit is 0, the call is to check whether this option is * potentially a device name. */static intsetdevname(cp, argv, doit)    char *cp;    char **argv;    int doit;{	struct stat statbuf;	char dev[MAXPATHLEN];	if (*cp == 0)		return 0;	if (strncmp("/dev/", cp, 5) != 0) {		strlcpy(dev, "/dev/", sizeof(dev));		strlcat(dev, cp, sizeof(dev));		cp = dev;	}	/*	 * Check if there is a character device by this name.	 */	if (stat(cp, &statbuf) < 0) {		if (!doit)			return errno != ENOENT;		option_error("Couldn't stat %s: %m", cp);		return 0;	}	if (!S_ISCHR(statbuf.st_mode)) {		if (doit)			option_error("%s is not a character device", cp);		return 0;	}	if (doit) {		strlcpy(devnam, cp, sizeof(devnam));		devstat = statbuf;		default_device = 0;	}  	return 1;}static intsetxonxoff(argv)    char **argv;{	lcp_wantoptions[0].asyncmap |= 0x000A0000;	/* escape ^S and ^Q */	lcp_wantoptions[0].neg_asyncmap = 1;	crtscts = -2;	return 1;}/* * setescape - add chars to the set we escape on transmission. */static intsetescape(argv)    char **argv;{    int n, ret;    char *p, *endp;    p = *argv;    ret = 1;    while (*p) {	n = strtol(p, &endp, 16);	if (p == endp) {	    option_error("escape parameter contains invalid hex number '%s'",			 p);	    return 0;	}	p = endp;	if (n < 0 || n == 0x5E || n > 0xFF) {	    option_error("can't escape character 0x%x", n);	    ret = 0;	} else	    xmit_accm[n >> 5] |= 1 << (n & 0x1F);	while (*p == ',' || *p == ' ')	    ++p;    }    lcp_allowoptions[0].asyncmap = xmit_accm[0];    return ret;}static voidprintescape(opt, printer, arg)    option_t *opt;    void (*printer) __P((void *, char *, ...));    void *arg;{	int n;	int first = 1;	for (n = 0; n < 256; ++n) {		if (n == 0x7d)			n += 2;		/* skip 7d, 7e */		if (xmit_accm[n >> 5] & (1 << (n & 0x1f))) {			if (!first)				printer(arg, ",");			else				first = 0;			printer(arg, "%x", n);		}	}	if (first)		printer(arg, "oops # nothing escaped");}/* * tty_init - do various tty-related initializations. */void tty_init(){    add_notifier(&pidchange, maybe_relock, 0);    the_channel = &tty_channel;    xmit_accm[3] = 0x60000000;}/* * tty_process_extra_options - work out which tty device we are using * and read its options file. */void tty_process_extra_options(){	using_pty = notty || ptycommand != NULL || pty_socket != NULL;	if (using_pty)		return;	if (default_device) {		char *p;		if (!isatty(0) || (p = ttyname(0)) == NULL) {			option_error("no device specified and stdin is not a tty");			exit(EXIT_OPTION_ERROR);		}		strlcpy(devnam, p, sizeof(devnam));		if (stat(devnam, &devstat) < 0)			fatal("Couldn't stat default device %s: %m", devnam);	}	/*	 * Parse the tty options file.	 * The per-tty options file should not change	 * ptycommand, pty_socket, notty or devnam.	 * options_for_tty doesn't override options set on the command line,	 * except for some privileged options.	 */	if (!options_for_tty())		exit(EXIT_OPTION_ERROR);}/* * tty_check_options - do consistency checks on the options we were given. */voidtty_check_options(){	struct stat statbuf;	int fdflags;	if (demand && connect_script == 0) {		option_error("connect script is required for demand-dialling\n");		exit(EXIT_OPTION_ERROR);	}	/* default holdoff to 0 if no connect script has been given */	if (connect_script == 0 && !holdoff_specified)		holdoff = 0;	if (using_pty) {		if (!default_device) {			option_error("%s option precludes specifying device name",				     notty? "notty": "pty");			exit(EXIT_OPTION_ERROR);		}		if (ptycommand != NULL && notty) {			option_error("pty option is incompatible with notty option");			exit(EXIT_OPTION_ERROR);		}		if (pty_socket != NULL && (ptycommand != NULL || notty)) {			option_error("socket option is incompatible with pty and notty");			exit(EXIT_OPTION_ERROR);		}		default_device = notty;		lockflag = 0;		modem = 0;		if (notty && log_to_fd <= 1)			log_to_fd = -1;	} else {		/*		 * If the user has specified a device which is the same as		 * the one on stdin, pretend they didn't specify any.		 * If the device is already open read/write on stdin,		 * we assume we don't need to lock it, and we can open it		 * as root.		 */		if (fstat(0, &statbuf) >= 0 && S_ISCHR(statbuf.st_mode)		    && statbuf.st_rdev == devstat.st_rdev) {			default_device = 1;			fdflags = fcntl(0, F_GETFL);			if (fdflags != -1 && (fdflags & O_ACCMODE) == O_RDWR)				privopen = 1;		}	}	if (default_device)		nodetach = 1;	/*	 * Don't send log messages to the serial port, it tends to	 * confuse the peer. :-)	 */	if (log_to_fd >= 0 && fstat(log_to_fd, &statbuf) >= 0	    && S_ISCHR(statbuf.st_mode) && statbuf.st_rdev == devstat.st_rdev)		log_to_fd = -1;}/* * connect_tty - get the serial port ready to start doing PPP. * That is, open the serial port, set its speed and mode, and run * the connector and/or welcomer. */int connect_tty(){	char *connector;	int fdflags;	struct stat statbuf;	char numbuf[16];	/*	 * Get a pty master/slave pair if the pty, notty, socket,	 * or record options were specified.	 */	error("connect_tty");	strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam));	pty_master = -1;	pty_slave = -1;	real_ttyfd = -1;	if (using_pty || record_file != NULL) {		if (!get_pty(&pty_master, &pty_slave, ppp_devnam, uid)) {			error("Couldn't allocate pseudo-tty");			status = EXIT_FATAL_ERROR;			return -1;		}		set_up_tty(pty_slave, 1);	}	/*	 * Lock the device if we've been asked to.	 */	status = EXIT_LOCK_FAILED;	if (lockflag && !privopen) {		if (lock(devnam) < 0)			return -1;		locked = 1;	}	/*	 * Open the serial device and set it up to be the ppp interface.	 * First we open it in non-blocking mode so we can set the	 * various termios flags appropriately.  If we aren't dialling	 * out and we want to use the modem lines, we reopen it later	 * in order to wait for the carrier detect signal from the modem.	 */	hungup = 0;	kill_link = 0;	connector = doing_callback? callback_script: connect_script;	if (devnam[0] != 0) {		for (;;) {			/* If the user specified the device name, become the			   user before opening it. */			int err, prio;			prio = privopen? OPRIO_ROOT: tty_options[0].priority;			if (prio < OPRIO_ROOT)				seteuid(uid);			error("devname=%s",devnam);			ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0);			err = errno;			if (prio < OPRIO_ROOT)				seteuid(0);			if (ttyfd >= 0)				break;			errno = err;			if (err != EINTR) {				error("Failed to open %s: %m", devnam);				status = EXIT_OPEN_FAILED;			}			if (!persist || err != EINTR)				return -1;		}		real_ttyfd = ttyfd;		if ((fdflags = fcntl(ttyfd, F_GETFL)) == -1		    || fcntl(ttyfd, F_SETFL, fdflags & ~O_NONBLOCK) < 0)			warn("Couldn't reset non-blocking mode on device: %m");		/*		 * Do the equivalent of `mesg n' to stop broadcast messages.		 */		if (fstat(ttyfd, &statbuf) < 0		    || fchmod(ttyfd, statbuf.st_mode & ~(S_IWGRP | S_IWOTH)) < 0) {			warn("Couldn't restrict write permissions to %s: %m", devnam);		} else			tty_mode = statbuf.st_mode;		/*		 * Set line speed, flow control, etc.		 * If we have a non-null connection or initializer script,		 * on most systems we set CLOCAL for now so that we can talk		 * to the modem before carrier comes up.  But this has the		 * side effect that we might miss it if CD drops before we		 * get to clear CLOCAL below.  On systems where we can talk		 * successfully to the modem with CLOCAL clear and CD down,		 * we could clear CLOCAL at this point.		 */		set_up_tty(ttyfd, ((connector != NULL && connector[0] != 0)				   || initializer != NULL));	}	/*	 * If the pty, socket, notty and/or record option was specified,	 * start up the character shunt now.	 */	status = EXIT_PTYCMD_FAILED;	if (ptycommand != NULL) {		if (record_file != NULL) {			int ipipe[2], opipe[2], ok;			if (pipe(ipipe) < 0 || pipe(opipe) < 0)				fatal("Couldn't create pipes for record option: %m");			ok = device_script(ptycommand, opipe[0], ipipe[1], 1) == 0				&& start_charshunt(ipipe[0], opipe[1]);			close(ipipe[0]);			close(ipipe[1]);			close(opipe[0]);			close(opipe[1]);			if (!ok)				return -1;		} else {			if (device_script(ptycommand, pty_master, pty_master, 1) < 0)				return -1;			ttyfd = pty_slave;			close(pty_master);			pty_master = -1;		}	} else if (pty_socket != NULL) {		int fd = open_socket(pty_socket);		if (fd < 0)			return -1;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -