0
0
mirror of https://github.com/ecki/net-tools.git synced 2024-09-22 04:10:55 +00:00
net-tools/slattach.c
2024-08-29 23:30:38 +02:00

806 lines
18 KiB
C

/*
* slattach A program for handling dialup IP connecions.
* This program forces a TTY line to go into a special
* terminal line discipline, so that it can be used for
* network traffic instead of the regular terminal I/O.
*
* Usage: slattach [-ehlmnqv] [ -k keepalive ] [ -o outfill ]
* [-c cmd] [-s speed] [-p protocol] tty | -
*
* Version: @(#)slattach.c 1.20 1999-05-29
*
* Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
* Copyright 1988-1993 MicroWalt Corporation
*
* Modified:
* Alan Cox, <A.Cox@swansea.ac.uk> , July 16 1994
* Miquel van Smoorenburg, <miquels@drinkel.ow.org>, October 1994
* George Shearer, <gshearer@one.net>, January 3, 1995
* Yossi Gottlieb, <yogo@math.tau.ac.il>, February 11, 1995
* Peter Tobias, <tobias@et-inf.fho-emden.de>, July 30 1995
* Bernd Eckenfels <net-tools@lina.inka.de>, May 29, 1999
* added some more printf's for debug and NOBLOCK to open
* this should be enough to support 2.2 ttyS-style locks
*
* This program is free software; you can redistribute it
* and/or modify it under the terms of the GNU General
* Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at
* your option) any later version.
*/
#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <pwd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <getopt.h>
#include <linux/if_slip.h>
#include "pathnames.h"
#include "net-support.h"
#include "version.h"
#include "config.h"
#include "intl.h"
#include "util.h"
#ifndef _PATH_LOCKD
#define _PATH_LOCKD "/var/lock" /* lock files */
#endif
#ifndef _UID_UUCP
#define _UID_UUCP "uucp" /* owns locks */
#endif
#ifndef _PATH_DEVPTMX
#define _PATH_DEVPTMX "/dev/ptmx" /* pseudo-terminal master */
#endif
#define DEF_PROTO "cslip"
static char *Release = RELEASE, *Signature = "Fred N. van Kempen et al.";
struct {
const char *speed;
int code;
} tty_speeds[] = { /* table of usable baud rates */
{ "50", B50 }, { "75", B75 },
{ "110", B110 }, { "300", B300 },
{ "600", B600 }, { "1200", B1200 },
{ "2400", B2400 }, { "4800", B4800 },
{ "9600", B9600 },
#ifdef B14400
{ "14400", B14400 },
#endif
#ifdef B19200
{ "19200", B19200 },
#endif
#ifdef B38400
{ "38400", B38400 },
#endif
#ifdef B57600
{ "57600", B57600 },
#endif
#ifdef B115200
{ "115200", B115200 },
#endif
#ifdef B230400
{ "230400", B230400 },
#endif
#ifdef B460800
{ "460800", B460800 },
#endif
#ifdef B500000
{ "500000", B500000 },
#endif
#ifdef B576000
{ "576000", B576000 },
#endif
#ifdef B921600
{ "921600", B921600 },
#endif
#ifdef B1000000
{ "1000000", B1000000 },
#endif
#ifdef B1152000
{ "1152000", B1152000 },
#endif
#ifdef B1500000
{ "1500000", B1500000 },
#endif
#ifdef B2000000
{ "2000000", B2000000 },
#endif
#ifdef B2500000
{ "2500000", B2500000 },
#endif
#ifdef B3000000
{ "3000000", B3000000 },
#endif
#ifdef B3500000
{ "3500000", B3500000 },
#endif
#ifdef B4000000
{ "4000000", B4000000 },
#endif
{ NULL, 0 }
};
struct termios tty_saved, /* saved TTY device state */
tty_current; /* current TTY device state */
int tty_sdisc, /* saved TTY line discipline */
tty_ldisc, /* current TTY line discipline */
tty_fd = -1; /* TTY file descriptor */
char * path_pts = NULL; /* slave pseudo-terminal device */
int opt_c = 0; /* "command" to run at exit */
int opt_e = 0; /* "activate only" flag */
int opt_h = 0; /* "hangup" on carrier loss */
#ifdef SIOCSKEEPALIVE
int opt_k = 0; /* "keepalive" value */
#endif
int opt_l = 0; /* "lock it" flag */
int opt_L = 0; /* 3-wire mode flag */
int opt_m = 0; /* "set RAW mode" flag */
int opt_n = 0; /* "set No Mesg" flag */
#ifdef SIOCSOUTFILL
int opt_o = 0; /* "outfill" value */
#endif
int opt_q = 0; /* "quiet" flag */
int opt_d = 0; /* debug flag */
int opt_v = 0; /* Verbose flag */
/* Disable any messages to the input channel of this process. */
static int
tty_nomesg(int fd)
{
if (opt_n == 0) return(0);
return(fchmod(fd, 0600));
}
/* Check for an existing lock file on our device */
static int
tty_already_locked(char *nam)
{
int i = 0, pid = 0;
FILE *fd = (FILE *)0;
/* Does the lock file on our device exist? */
if ((fd = fopen(nam, "r")) == (FILE *)0)
return(0); /* No, return perm to continue */
/* Yes, the lock is there. Now let's make sure */
/* at least there's no active process that owns */
/* that lock. */
i = fscanf(fd, "%d", &pid);
(void) fclose(fd);
if (i != 1) /* Lock file format's wrong! Kill't */
return(0);
/* We got the pid, check if the process's alive */
if (kill(pid, 0) == 0) /* it found process */
return(1); /* Yup, it's running... */
/* Dead, we can proceed locking this device... */
return(0);
}
/* Lock or unlock a terminal line. */
static int
tty_lock(char *path, int mode)
{
static char saved_path[PATH_MAX];
static int saved_lock = 0;
struct passwd *pw;
int fd;
char apid[16];
/* We do not lock standard input. */
if ((opt_l == 0) || ((path == NULL) && (saved_lock == 0))) return(0);
if (mode == 1) { /* lock */
sprintf(saved_path, "%s/LCK..%s", _PATH_LOCKD, path);
if (tty_already_locked(saved_path)) {
fprintf(stderr, _("slattach: /dev/%s already locked!\n"), path);
return(-1);
}
if ((fd = creat(saved_path, 0644)) < 0) {
if (errno != EEXIST)
if (opt_q == 0) fprintf(stderr,
_("slattach: tty_lock: (%s): %s\n"),
saved_path, strerror(errno));
return(-1);
}
sprintf(apid, "%10d\n", getpid());
if (write(fd, apid, strlen(apid)) != strlen(apid)) {
fprintf(stderr, _("slattach: cannot write PID file\n"));
close(fd);
unlink(saved_path);
return(-1);
}
/* Make sure UUCP owns the lockfile. Required by some packages. */
if ((pw = getpwnam(_UID_UUCP)) == NULL) {
if (opt_q == 0) fprintf(stderr, _("slattach: tty_lock: UUCP user %s unknown!\n"),
_UID_UUCP);
(void) close(fd);
return(0); /* keep the lock anyway */
}
if (fchown(fd, pw->pw_uid, pw->pw_gid))
/* keep the lock anyway */;
(void) close(fd);
saved_lock = 1;
} else { /* unlock */
if (saved_lock != 1) return(0);
if (unlink(saved_path) < 0) {
if (opt_q == 0) fprintf(stderr,
"slattach: tty_unlock: (%s): %s\n", saved_path,
strerror(errno));
return(-1);
}
saved_lock = 0;
}
return(0);
}
/* Find a serial speed code in the table. */
static int
tty_find_speed(const char *speed)
{
int i;
i = 0;
while (tty_speeds[i].speed != NULL) {
if (!strcmp(tty_speeds[i].speed, speed)) return(tty_speeds[i].code);
i++;
}
return(-EINVAL);
}
/* Set the number of stop bits. */
static int
tty_set_stopbits(struct termios *tty, char *stopbits)
{
if (opt_d) printf("slattach: tty_set_stopbits: %c\n", *stopbits);
switch(*stopbits) {
case '1':
tty->c_cflag &= ~CSTOPB;
break;
case '2':
tty->c_cflag |= CSTOPB;
break;
default:
return(-EINVAL);
}
return(0);
}
/* Set the number of data bits. */
static int
tty_set_databits(struct termios *tty, char *databits)
{
if (opt_d) printf("slattach: tty_set_databits: %c\n", *databits);
tty->c_cflag &= ~CSIZE;
switch(*databits) {
case '5':
tty->c_cflag |= CS5;
break;
case '6':
tty->c_cflag |= CS6;
break;
case '7':
tty->c_cflag |= CS7;
break;
case '8':
tty->c_cflag |= CS8;
break;
default:
return(-EINVAL);
}
return(0);
}
/* Set the type of parity encoding. */
static int
tty_set_parity(struct termios *tty, char *parity)
{
if (opt_d) printf("slattach: tty_set_parity: %c\n", *parity);
switch(toupper(*parity)) {
case 'N':
tty->c_cflag &= ~(PARENB | PARODD);
break;
case 'O':
tty->c_cflag &= ~(PARENB | PARODD);
tty->c_cflag |= (PARENB | PARODD);
break;
case 'E':
tty->c_cflag &= ~(PARENB | PARODD);
tty->c_cflag |= (PARENB);
break;
default:
return(-EINVAL);
}
return(0);
}
/* Set the line speed of a terminal line. */
static int
tty_set_speed(struct termios *tty, const char *speed)
{
int code;
if (opt_d) printf("slattach: tty_set_speed: %s\n", speed);
if ((code = tty_find_speed(speed)) < 0) return(code);
tty->c_cflag &= ~CBAUD;
tty->c_cflag |= code;
return(0);
}
/* Put a terminal line in a transparent state. */
static int
tty_set_raw(struct termios *tty)
{
int i;
int speed;
for(i = 0; i < NCCS; i++)
tty->c_cc[i] = '\0'; /* no spec chr */
tty->c_cc[VMIN] = 1;
tty->c_cc[VTIME] = 0;
tty->c_iflag = (IGNBRK | IGNPAR); /* input flags */
tty->c_oflag = (0); /* output flags */
tty->c_lflag = (0); /* local flags */
speed = (tty->c_cflag & CBAUD); /* save current speed */
tty->c_cflag = (HUPCL | CREAD); /* UART flags */
if (opt_L)
tty->c_cflag |= CLOCAL;
else
tty->c_cflag |= CRTSCTS;
tty->c_cflag |= speed; /* restore speed */
return(0);
}
/* Fetch the state of a terminal. */
static int
tty_get_state(struct termios *tty)
{
if (ioctl(tty_fd, TCGETS, tty) < 0) {
if (opt_q == 0) fprintf(stderr,
"slattach: tty_get_state: %s\n", strerror(errno));
return(-errno);
}
return(0);
}
/* Set the state of a terminal. */
static int
tty_set_state(struct termios *tty)
{
if (ioctl(tty_fd, TCSETS, tty) < 0) {
if (opt_q == 0) fprintf(stderr,
"slattach: tty_set_state: %s\n", strerror(errno));
return(-errno);
}
return(0);
}
/* Get the line discipline of a terminal line. */
static int
tty_get_disc(int *disc)
{
if (ioctl(tty_fd, TIOCGETD, disc) < 0) {
if (opt_q == 0) fprintf(stderr,
"slattach: tty_get_disc: %s\n", strerror(errno));
return(-errno);
}
return(0);
}
/* Set the line discipline of a terminal line. */
static int
tty_set_disc(int disc)
{
if (disc == -1) disc = tty_sdisc;
if (ioctl(tty_fd, TIOCSETD, &disc) < 0) {
if (opt_q == 0) fprintf(stderr,
"slattach: tty_set_disc(%d, %d): %s\n", tty_fd,
disc, strerror(errno));
return(-errno);
}
return(0);
}
/* Fetch the name of the network interface attached to this terminal. */
static int
tty_get_name(char *name)
{
if (ioctl(tty_fd, SIOCGIFNAME, name) < 0) {
if (opt_q == 0)
perror("tty_get_name");
return(-errno);
}
return(0);
}
/* Hangup the line. */
static int
tty_hangup(void)
{
struct termios tty;
tty = tty_current;
(void) tty_set_speed(&tty, "0");
if (tty_set_state(&tty) < 0) {
if (opt_q == 0) fprintf(stderr, _("slattach: tty_hangup(DROP): %s\n"), strerror(errno));
return(-errno);
}
(void) sleep(1);
if (tty_set_state(&tty_current) < 0) {
if (opt_q == 0) fprintf(stderr, _("slattach: tty_hangup(RAISE): %s\n"), strerror(errno));
return(-errno);
}
return(0);
}
/* Close down a terminal line. */
static int
tty_close(void)
{
(void) tty_set_disc(tty_sdisc);
(void) tty_hangup();
(void) tty_lock(NULL, 0);
return(0);
}
/* Open and initialize a terminal line. */
static int
tty_open(char *name, const char *speed)
{
char pathbuf[PATH_MAX];
char *path_open, *path_lock;
int fd;
/* Try opening the TTY device. */
if (name != NULL) {
if (name[0] != '/') {
if (strlen(name + 6) > sizeof(pathbuf)) {
if (opt_q == 0) fprintf(stderr,
_("slattach: tty name too long\n"));
return (-1);
}
sprintf(pathbuf, "/dev/%s", name);
path_open = pathbuf;
path_lock = name;
} else if (!strncmp(name, "/dev/", 5)) {
path_open = name;
path_lock = name + 5;
} else {
path_open = name;
path_lock = name;
}
if (opt_d) printf("slattach: tty_open: looking for lock\n");
if (tty_lock(path_lock, 1)) return(-1); /* can we lock the device? */
if (opt_d) printf("slattach: tty_open: trying to open %s\n", path_open);
if ((fd = open(path_open, O_RDWR|O_NDELAY)) < 0) {
if (opt_q == 0) fprintf(stderr,
"slattach: tty_open(%s, RW): %s\n",
path_open, strerror(errno));
return(-errno);
}
tty_fd = fd;
if (opt_d) printf("slattach: tty_open: %s (fd=%d)\n", path_open, fd);
if (!strcmp(path_open, _PATH_DEVPTMX)) {
if (opt_d) printf("slattach: tty_open: trying to grantpt and unlockpt\n");
if (grantpt(fd) < 0) {
if (opt_q == 0) fprintf(stderr,
"slattach: tty_open: grantpt: %s\n", strerror(errno));
return(-errno);
}
if (unlockpt(fd) < 0) {
if (opt_q == 0) fprintf(stderr,
"slattach: tty_open: unlockpt: %s\n", strerror(errno));
return(-errno);
}
path_pts = ptsname(fd);
if (path_pts == NULL) {
if (opt_q == 0) fprintf(stderr,
"slattach: tty_open: ptsname: %s\n", strerror(errno));
return(-errno);
}
if (opt_d) printf("slattach: tty_open: %s: slave pseudo-terminal is %s\n",
path_open, path_pts);
}
} else {
tty_fd = 0;
}
/* Fetch the current state of the terminal. */
if (tty_get_state(&tty_saved) < 0) {
if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot get current state!\n"));
return(-errno);
}
tty_current = tty_saved;
/* Fetch the current line discipline of this terminal. */
if (tty_get_disc(&tty_sdisc) < 0) {
if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot get current line disc!\n"));
return(-errno);
}
tty_ldisc = tty_sdisc;
/* Put this terminal line in a 8-bit transparent mode. */
if (opt_m == 0) {
if (tty_set_raw(&tty_current) < 0) {
if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot set RAW mode!\n"));
return(-errno);
}
/* Set the default speed if we need to. */
if (speed != NULL) {
if (tty_set_speed(&tty_current, speed) != 0) {
if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot set %s bps!\n"),
speed);
return(-errno);
}
}
/* Set up a completely 8-bit clean line. */
if (tty_set_databits(&tty_current, "8") ||
tty_set_stopbits(&tty_current, "1") ||
tty_set_parity(&tty_current, "N")) {
if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot set 8N1 mode!\n"));
return(-errno);
}
/* Set the new line mode. */
if ((fd = tty_set_state(&tty_current)) < 0) return(fd);
}
/* OK, line is open. Do we need to "silence" it? */
(void) tty_nomesg(tty_fd);
return(0);
}
/* Catch any signals. */
static void
sig_catch(int sig)
{
/* (void) signal(sig, sig_catch); */
tty_close();
exit(0);
}
static void
usage(int rc)
{
FILE *fp = rc ? stderr : stdout;
char *usage_msg = "Usage: slattach [-ehlLmnqv] "
#ifdef SIOCSKEEPALIVE
"[-k keepalive] "
#endif
#ifdef SIOCSOUTFILL
"[-o outfill] "
#endif
"[-c cmd] [-s speed] [-p protocol] tty | -\n"
" slattach -V | --version\n";
fputs(usage_msg, fp);
exit(rc);
}
static void
version(void)
{
printf("%s\n%s\n", Release, Signature);
exit(E_VERSION);
}
int
main(int argc, char *argv[])
{
char path_buf[128] = "";
char *path_dev;
char buff[128];
const char *speed = NULL;
const char *proto = DEF_PROTO;
const char *extcmd = NULL;
int s;
static struct option longopts[] = {
{ "version", 0, NULL, 'V' },
{ NULL, 0, NULL, 0 }
};
path_dev = path_buf;
/* Scan command line for any arguments. */
opterr = 0;
while ((s = getopt_long(argc, argv, "c:ehlLmnp:qs:vdVk:o:", longopts, NULL)) != EOF) switch(s) {
case 'c':
extcmd = optarg;
break;
case 'e':
opt_e = 1 - opt_e;
break;
case 'h':
opt_h = 1 - opt_h;
break;
#ifdef SIOCSKEEPALIVE
case 'k':
opt_k = atoi(optarg);
break;
#endif
case 'L':
opt_L = 1 - opt_L;
break;
case 'l':
opt_l = 1 - opt_l;
break;
case 'm':
opt_m = 1 - opt_m;
break;
case 'n':
opt_n = 1 - opt_n;
break;
#ifdef SIOCSOUTFILL
case 'o':
opt_o = atoi(optarg);
break;
#endif
case 'p':
proto = optarg;
break;
case 'q':
opt_q = 1 - opt_q;
break;
case 's':
speed = optarg;
break;
case 'd':
opt_d = 1 - opt_d;
break;
case 'v':
opt_v = 1 - opt_v;
break;
case 'V':
version();
/*NOTREACHED*/
default:
usage(E_OPTERR);
/*NOTREACHED*/
}
if (setvbuf(stdout,0,_IOLBF,0)) {
if (opt_q == 0) fprintf(stderr, _("slattach: setvbuf(stdout,0,_IOLBF,0) : %s\n"),
strerror(errno));
exit(1);
}
activate_init();
if (!strcmp(proto, "tty"))
opt_m++;
/* Is a terminal given? */
if (optind != (argc - 1)) usage(E_OPTERR);
safe_strncpy(path_buf, argv[optind], sizeof(path_buf));
if (!strcmp(path_buf, "-")) {
opt_e = 1;
path_dev = NULL;
if (tty_open(NULL, speed) < 0) { return(3); }
} else {
path_dev = path_buf;
if (tty_open(path_dev, speed) < 0) { return(3); }
}
/* Start the correct protocol. */
if (!strcmp(proto, "tty")) {
tty_sdisc = N_TTY;
tty_close();
return(0);
}
if (activate_ld(proto, tty_fd))
return(1);
if ((opt_v == 1) || (opt_d == 1)) {
if (tty_get_name(buff)) { return(3); }
printf(_("%s started"), proto);
if (path_dev != NULL) printf(_(" on %s"), path_dev);
if (path_pts != NULL) printf(_(" ptsname %s"), path_pts);
printf(_(" interface %s\n"), buff);
}
/* Configure keepalive and outfill. */
#ifdef SIOCSKEEPALIVE
if (opt_k && (ioctl(tty_fd, SIOCSKEEPALIVE, &opt_k) < 0))
fprintf(stderr, "slattach: ioctl(SIOCSKEEPALIVE): %s\n", strerror(errno));
#endif
#ifdef SIOCSOUTFILL
if (opt_o && (ioctl(tty_fd, SIOCSOUTFILL, &opt_o) < 0))
fprintf(stderr, "slattach: ioctl(SIOCSOUTFILL): %s\n", strerror(errno));
#endif
(void) signal(SIGHUP, sig_catch);
(void) signal(SIGINT, sig_catch);
(void) signal(SIGQUIT, sig_catch);
(void) signal(SIGTERM, sig_catch);
/* Wait until we get killed if hanging on a terminal. */
if (opt_e == 0) {
while(1) {
if(opt_h == 1) { /* hangup on carrier loss */
int n = 0;
ioctl(tty_fd, TIOCMGET, &n);
if(!(n & TIOCM_CAR))
break;
sleep(15);
}
else
sleep(60);
};
tty_close();
if(extcmd) /* external command on exit */
exit(system(extcmd));
}
exit(0);
}