* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
-#define RCSID "$Id: tty.c,v 1.1 2000/06/30 04:54:23 paulus Exp $"
+#define RCSID "$Id: tty.c,v 1.4 2001/02/22 03:15:21 paulus Exp $"
#include <stdio.h>
#include <ctype.h>
#include "fsm.h"
#include "lcp.h"
+static int setdevname __P((char *, char **, int));
+static int setspeed __P((char *, char **, int));
static int setxonxoff __P((char **));
static void finish_tty __P((void));
static int start_charshunt __P((int, int));
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 */
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;
+int using_pty = 0; /* we're allocating a pty as the device */
extern uid_t uid;
extern int kill_link;
/* 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 },
+ { "tty speed", o_wild, (void *) &setspeed,
+ "Baud rate for serial port", OPT_NOARG },
{ "lock", o_bool, &lockflag,
"Lock serial device with UUCP-style lock file", 1 },
{ "nolock", o_bool, &lockflag,
"Don't lock serial device", OPT_PRIV },
{ "init", o_string, &initializer,
- "A program to initialize the device",
- OPT_A2INFO | OPT_PRIVFIX, &initializer_info },
+ "A program to initialize the device", OPT_PRIVFIX },
{ "connect", o_string, &connect_script,
- "A program to set up a connection",
- OPT_A2INFO | OPT_PRIVFIX, &connect_script_info },
+ "A program to set up a connection", OPT_PRIVFIX },
{ "disconnect", o_string, &disconnect_script,
- "Program to disconnect serial device",
- OPT_A2INFO | OPT_PRIVFIX, &disconnect_script_info },
+ "Program to disconnect serial device", OPT_PRIVFIX },
{ "welcome", o_string, &welcomer,
- "Script to welcome client",
- OPT_A2INFO | OPT_PRIVFIX, &welcomer_info },
+ "Script to welcome client", OPT_PRIVFIX },
{ "pty", o_string, &ptycommand,
- "Script to run on pseudo-tty master side",
- OPT_A2INFO | OPT_PRIVFIX | OPT_DEVNAM, &ptycommand_info },
+ "Script to run on pseudo-tty master side", OPT_PRIVFIX | OPT_DEVNAM },
{ "notty", o_bool, ¬ty,
"Input/output is not a tty", OPT_DEVNAM | 1 },
{ "socket", o_string, &pty_socket,
{ NULL }
};
+
+/*
+ * 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 int
+setspeed(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;
+ 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 int
+setdevname(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 int
setxonxoff(argv)
char **argv;
add_options(tty_options);
}
+/*
+ * tty_device_check - work out which tty device we are using
+ * and read its options file.
+ */
+void tty_device_check()
+{
+ 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.
+ */
+void
+tty_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
for (;;) {
/* If the user specified the device name, become the
user before opening it. */
- int err;
- if (!devnam_info.priv && !privopen)
+ int err, prio;
+
+ prio = privopen? OPRIO_ROOT: tty_options[0].priority;
+ if (prio < OPRIO_ROOT)
seteuid(uid);
ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0);
err = errno;
- if (!devnam_info.priv && !privopen)
+ if (prio < OPRIO_ROOT)
seteuid(0);
if (ttyfd >= 0)
break;
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");
*/
set_up_tty(ttyfd, ((connector != NULL && connector[0] != 0)
|| initializer != NULL));
- real_ttyfd = ttyfd;
}
/*
warn("Welcome script failed");
}
+ /*
+ * If we are initiating this connection, wait for a short
+ * time for something from the peer. This can avoid bouncing
+ * our packets off his tty before he has it set up.
+ */
+ if (connector != NULL || ptycommand != NULL)
+ listen_time = connect_delay;
+
return ttyfd;
}