339 lines
7.1 KiB
C
339 lines
7.1 KiB
C
/* $OpenBSD: ldattach.c,v 1.20 2023/04/19 12:58:15 jsg Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2007, 2008 Marc Balmer <mbalmer@openbsd.org>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
/*
|
|
* Attach a line disciplines to a tty(4) device either from the commandline
|
|
* or from init(8) (using entries in /etc/ttys). Optionally pass the data
|
|
* received on the tty(4) device to the master device of a pty(4) pair.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/limits.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <paths.h>
|
|
#include <poll.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <syslog.h>
|
|
#include <termios.h>
|
|
#include <unistd.h>
|
|
#include <util.h>
|
|
|
|
#include "atomicio.h"
|
|
|
|
__dead void usage(void);
|
|
void relay(int, int);
|
|
void coroner(int);
|
|
|
|
volatile sig_atomic_t dying = 0;
|
|
|
|
__dead void
|
|
usage(void)
|
|
{
|
|
extern char *__progname;
|
|
|
|
fprintf(stderr, "usage: %s [-27dehmop] [-s baudrate] "
|
|
"[-t cond] discipline device\n", __progname);
|
|
exit(1);
|
|
}
|
|
|
|
/* relay data between two file descriptors */
|
|
void
|
|
relay(int device, int pty)
|
|
{
|
|
struct pollfd pfd[2];
|
|
int nfds, n, nread;
|
|
char buf[128];
|
|
|
|
pfd[0].fd = device;
|
|
pfd[1].fd = pty;
|
|
|
|
while (!dying) {
|
|
pfd[0].events = POLLRDNORM;
|
|
pfd[1].events = POLLRDNORM;
|
|
nfds = poll(pfd, 2, INFTIM);
|
|
if (nfds == -1) {
|
|
syslog(LOG_ERR, "polling error");
|
|
exit(1);
|
|
}
|
|
if (nfds == 0) /* should not happen */
|
|
continue;
|
|
|
|
if (pfd[1].revents & POLLHUP) { /* slave device not connected */
|
|
sleep(1);
|
|
continue;
|
|
}
|
|
|
|
for (n = 0; n < 2; n++) {
|
|
if (!(pfd[n].revents & POLLRDNORM))
|
|
continue;
|
|
|
|
nread = read(pfd[n].fd, buf, sizeof(buf));
|
|
if (nread == -1) {
|
|
syslog(LOG_ERR, "error reading from %s: %m",
|
|
n ? "pty" : "device");
|
|
exit(1);
|
|
}
|
|
if (nread == 0) {
|
|
syslog(LOG_ERR, "eof during read from %s: %m",
|
|
n ? "pty" : "device");
|
|
exit(1);
|
|
}
|
|
atomicio(vwrite, pfd[1 - n].fd, buf, nread);
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
struct termios tty;
|
|
struct tstamps tstamps;
|
|
const char *errstr;
|
|
sigset_t sigset;
|
|
pid_t ppid;
|
|
int ch, fd, master = -1, slave, pty = 0, ldisc, nodaemon = 0;
|
|
int bits = 0, parity = 0, stop = 0, flowcl = 0, hupcl = 1;
|
|
speed_t speed = 0;
|
|
char devn[32], ptyn[32], *dev, *disc;
|
|
|
|
tstamps.ts_set = tstamps.ts_clr = 0;
|
|
|
|
if ((ppid = getppid()) == 1)
|
|
nodaemon = 1;
|
|
|
|
while ((ch = getopt(argc, argv, "27dehmops:t:")) != -1) {
|
|
switch (ch) {
|
|
case '2':
|
|
stop = 2;
|
|
break;
|
|
case '7':
|
|
bits = 7;
|
|
break;
|
|
case 'd':
|
|
nodaemon = 1;
|
|
break;
|
|
case 'e':
|
|
parity = 'e';
|
|
break;
|
|
case 'h':
|
|
flowcl = 1;
|
|
break;
|
|
case 'm':
|
|
hupcl = 0;
|
|
break;
|
|
case 'o':
|
|
parity = 'o';
|
|
break;
|
|
case 'p':
|
|
pty = 1;
|
|
break;
|
|
case 's':
|
|
speed = (speed_t)strtonum(optarg, 0, UINT_MAX, &errstr);
|
|
if (errstr) {
|
|
if (ppid != 1)
|
|
errx(1, "speed is %s: %s", errstr,
|
|
optarg);
|
|
else
|
|
goto bail_out;
|
|
}
|
|
break;
|
|
case 't':
|
|
if (!strcasecmp(optarg, "dcd"))
|
|
tstamps.ts_set |= TIOCM_CAR;
|
|
else if (!strcasecmp(optarg, "!dcd"))
|
|
tstamps.ts_clr |= TIOCM_CAR;
|
|
else if (!strcasecmp(optarg, "cts"))
|
|
tstamps.ts_set |= TIOCM_CTS;
|
|
else if (!strcasecmp(optarg, "!cts"))
|
|
tstamps.ts_clr |= TIOCM_CTS;
|
|
else {
|
|
if (ppid != 1)
|
|
errx(1, "'%s' not supported for "
|
|
"timestamping", optarg);
|
|
else
|
|
goto bail_out;
|
|
}
|
|
break;
|
|
default:
|
|
if (ppid != -1)
|
|
usage();
|
|
}
|
|
}
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
if (ppid != 1 && argc != 2)
|
|
usage();
|
|
|
|
disc = *argv++;
|
|
dev = *argv;
|
|
if (strncmp(_PATH_DEV, dev, sizeof(_PATH_DEV) - 1)) {
|
|
(void)snprintf(devn, sizeof(devn), "%s%s", _PATH_DEV, dev);
|
|
dev = devn;
|
|
}
|
|
|
|
if (!strcmp(disc, "nmea")) {
|
|
ldisc = NMEADISC;
|
|
if (speed == 0)
|
|
speed = B4800; /* default is 4800 baud for nmea */
|
|
} else if (!strcmp(disc, "msts")) {
|
|
ldisc = MSTSDISC;
|
|
} else if (!strcmp(disc, "endrun")) {
|
|
ldisc = ENDRUNDISC;
|
|
} else {
|
|
syslog(LOG_ERR, "unknown line discipline %s", disc);
|
|
goto bail_out;
|
|
}
|
|
|
|
if ((fd = open(dev, O_RDWR)) == -1) {
|
|
syslog(LOG_ERR, "can't open %s", dev);
|
|
goto bail_out;
|
|
}
|
|
|
|
/*
|
|
* Get the current line attributes, modify only values that are
|
|
* either requested on the command line or that are needed by
|
|
* the line discipline (e.g. nmea has a default baudrate of
|
|
* 4800 instead of 9600).
|
|
*/
|
|
if (tcgetattr(fd, &tty) == -1) {
|
|
if (ppid != 1)
|
|
warnx("tcgetattr");
|
|
goto bail_out;
|
|
}
|
|
|
|
|
|
if (bits == 7) {
|
|
tty.c_cflag &= ~CS8;
|
|
tty.c_cflag |= CS7;
|
|
} else if (bits == 8) {
|
|
tty.c_cflag &= ~CS7;
|
|
tty.c_cflag |= CS8;
|
|
}
|
|
|
|
if (parity != 0)
|
|
tty.c_cflag |= PARENB;
|
|
if (parity == 'o')
|
|
tty.c_cflag |= PARODD;
|
|
else
|
|
tty.c_cflag &= ~PARODD;
|
|
|
|
if (stop == 2)
|
|
tty.c_cflag |= CSTOPB;
|
|
else
|
|
tty.c_cflag &= ~CSTOPB;
|
|
|
|
if (flowcl)
|
|
tty.c_cflag |= CRTSCTS;
|
|
|
|
if (hupcl == 0)
|
|
tty.c_cflag &= ~HUPCL;
|
|
|
|
if (speed != 0)
|
|
cfsetspeed(&tty, speed);
|
|
|
|
/* setup common to all line disciplines */
|
|
if (ioctl(fd, TIOCSDTR, 0) == -1)
|
|
warn("TIOCSDTR");
|
|
if (ioctl(fd, TIOCSETD, &ldisc) == -1) {
|
|
syslog(LOG_ERR, "can't attach %s line discipline on %s", disc,
|
|
dev);
|
|
goto bail_out;
|
|
}
|
|
|
|
/* line discpline specific setup */
|
|
switch (ldisc) {
|
|
case NMEADISC:
|
|
case MSTSDISC:
|
|
case ENDRUNDISC:
|
|
if (ioctl(fd, TIOCSTSTAMP, &tstamps) == -1) {
|
|
warnx("TIOCSTSTAMP");
|
|
goto bail_out;
|
|
}
|
|
tty.c_cflag |= CLOCAL;
|
|
tty.c_iflag = 0;
|
|
tty.c_lflag = 0;
|
|
tty.c_oflag = 0;
|
|
tty.c_cc[VMIN] = 1;
|
|
tty.c_cc[VTIME] = 0;
|
|
break;
|
|
}
|
|
|
|
/* finally set the line attributes */
|
|
if (tcsetattr(fd, TCSADRAIN, &tty) == -1) {
|
|
if (ppid != 1)
|
|
warnx("tcsetattr");
|
|
goto bail_out;
|
|
}
|
|
|
|
/*
|
|
* open a pty(4) pair to pass the data if the -p option has been
|
|
* given on the commandline.
|
|
*/
|
|
if (pty) {
|
|
if (openpty(&master, &slave, ptyn, NULL, NULL))
|
|
errx(1, "can't open a pty");
|
|
close(slave);
|
|
printf("%s\n", ptyn);
|
|
fflush(stdout);
|
|
}
|
|
if (nodaemon)
|
|
openlog("ldattach", LOG_PID | LOG_CONS | LOG_PERROR,
|
|
LOG_DAEMON);
|
|
else {
|
|
openlog("ldattach", LOG_PID | LOG_CONS, LOG_DAEMON);
|
|
if (daemon(0, 0))
|
|
errx(1, "can't daemonize");
|
|
}
|
|
|
|
syslog(LOG_INFO, "attach %s on %s", disc, dev);
|
|
signal(SIGHUP, coroner);
|
|
signal(SIGTERM, coroner);
|
|
|
|
if (master != -1) {
|
|
syslog(LOG_INFO, "passing data to %s", ptyn);
|
|
relay(fd, master);
|
|
} else {
|
|
sigemptyset(&sigset);
|
|
|
|
while (!dying)
|
|
sigsuspend(&sigset);
|
|
}
|
|
|
|
bail_out:
|
|
if (ppid == 1)
|
|
sleep(30); /* delay restart when called from init */
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
coroner(int useless)
|
|
{
|
|
dying = 1;
|
|
}
|