1 |
|
|
/* $OpenBSD: ldattach.c,v 1.16 2015/01/15 00:48:10 deraadt Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright (c) 2007, 2008 Marc Balmer <mbalmer@openbsd.org> |
5 |
|
|
* |
6 |
|
|
* Permission to use, copy, modify, and distribute this software for any |
7 |
|
|
* purpose with or without fee is hereby granted, provided that the above |
8 |
|
|
* copyright notice and this permission notice appear in all copies. |
9 |
|
|
* |
10 |
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 |
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 |
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
13 |
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 |
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
15 |
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
16 |
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 |
|
|
*/ |
18 |
|
|
|
19 |
|
|
/* |
20 |
|
|
* Attach a line disciplines to a tty(4) device either from the commandline |
21 |
|
|
* or from init(8) (using entries in /etc/ttys). Optionally pass the data |
22 |
|
|
* received on the tty(4) device to the master device of a pty(4) pair. |
23 |
|
|
*/ |
24 |
|
|
|
25 |
|
|
#include <sys/types.h> |
26 |
|
|
#include <sys/ioctl.h> |
27 |
|
|
#include <sys/limits.h> |
28 |
|
|
#include <sys/socket.h> |
29 |
|
|
#include <sys/stat.h> |
30 |
|
|
#include <sys/ioctl.h> |
31 |
|
|
|
32 |
|
|
#include <err.h> |
33 |
|
|
#include <errno.h> |
34 |
|
|
#include <fcntl.h> |
35 |
|
|
#include <paths.h> |
36 |
|
|
#include <poll.h> |
37 |
|
|
#include <signal.h> |
38 |
|
|
#include <stdio.h> |
39 |
|
|
#include <stdlib.h> |
40 |
|
|
#include <string.h> |
41 |
|
|
#include <syslog.h> |
42 |
|
|
#include <termios.h> |
43 |
|
|
#include <unistd.h> |
44 |
|
|
#include <util.h> |
45 |
|
|
|
46 |
|
|
#include "atomicio.h" |
47 |
|
|
|
48 |
|
|
__dead void usage(void); |
49 |
|
|
void relay(int, int); |
50 |
|
|
void coroner(int); |
51 |
|
|
|
52 |
|
|
volatile sig_atomic_t dying = 0; |
53 |
|
|
|
54 |
|
|
__dead void |
55 |
|
|
usage(void) |
56 |
|
|
{ |
57 |
|
|
extern char *__progname; |
58 |
|
|
|
59 |
|
|
fprintf(stderr, "usage: %s [-27dehmop] [-s baudrate] " |
60 |
|
|
"[-t cond] discipline device\n", __progname); |
61 |
|
|
exit(1); |
62 |
|
|
} |
63 |
|
|
|
64 |
|
|
/* relay data between two file descriptors */ |
65 |
|
|
void |
66 |
|
|
relay(int device, int pty) |
67 |
|
|
{ |
68 |
|
|
struct pollfd pfd[2]; |
69 |
|
|
int nfds, n, nread; |
70 |
|
|
char buf[128]; |
71 |
|
|
|
72 |
|
|
pfd[0].fd = device; |
73 |
|
|
pfd[1].fd = pty; |
74 |
|
|
|
75 |
|
|
while (!dying) { |
76 |
|
|
pfd[0].events = POLLRDNORM; |
77 |
|
|
pfd[1].events = POLLRDNORM; |
78 |
|
|
nfds = poll(pfd, 2, INFTIM); |
79 |
|
|
if (nfds == -1) { |
80 |
|
|
syslog(LOG_ERR, "polling error"); |
81 |
|
|
exit(1); |
82 |
|
|
} |
83 |
|
|
if (nfds == 0) /* should not happen */ |
84 |
|
|
continue; |
85 |
|
|
|
86 |
|
|
if (pfd[1].revents & POLLHUP) { /* slave device not connected */ |
87 |
|
|
sleep(1); |
88 |
|
|
continue; |
89 |
|
|
} |
90 |
|
|
|
91 |
|
|
for (n = 0; n < 2; n++) { |
92 |
|
|
if (!(pfd[n].revents & POLLRDNORM)) |
93 |
|
|
continue; |
94 |
|
|
|
95 |
|
|
nread = read(pfd[n].fd, buf, sizeof(buf)); |
96 |
|
|
if (nread == -1) { |
97 |
|
|
syslog(LOG_ERR, "error reading from %s: %m", |
98 |
|
|
n ? "pty" : "device"); |
99 |
|
|
exit(1); |
100 |
|
|
} |
101 |
|
|
if (nread == 0) { |
102 |
|
|
syslog(LOG_ERR, "eof during read from %s: %m", |
103 |
|
|
n ? "pty" : "device"); |
104 |
|
|
exit(1); |
105 |
|
|
} |
106 |
|
|
atomicio(vwrite, pfd[1 - n].fd, buf, nread); |
107 |
|
|
} |
108 |
|
|
} |
109 |
|
|
} |
110 |
|
|
|
111 |
|
|
int |
112 |
|
|
main(int argc, char *argv[]) |
113 |
|
|
{ |
114 |
|
|
struct termios tty; |
115 |
|
|
struct tstamps tstamps; |
116 |
|
|
const char *errstr; |
117 |
|
|
sigset_t sigset; |
118 |
|
|
pid_t ppid; |
119 |
|
|
int ch, fd, master = -1, slave, pty = 0, ldisc, nodaemon = 0; |
120 |
|
|
int bits = 0, parity = 0, stop = 0, flowcl = 0, hupcl = 1; |
121 |
|
|
speed_t speed = 0; |
122 |
|
|
char devn[32], ptyn[32], *dev, *disc; |
123 |
|
|
|
124 |
|
|
tstamps.ts_set = tstamps.ts_clr = 0; |
125 |
|
|
|
126 |
|
|
if ((ppid = getppid()) == 1) |
127 |
|
|
nodaemon = 1; |
128 |
|
|
|
129 |
|
|
while ((ch = getopt(argc, argv, "27dehmops:t:")) != -1) { |
130 |
|
|
switch (ch) { |
131 |
|
|
case '2': |
132 |
|
|
stop = 2; |
133 |
|
|
break; |
134 |
|
|
case '7': |
135 |
|
|
bits = 7; |
136 |
|
|
break; |
137 |
|
|
case 'd': |
138 |
|
|
nodaemon = 1; |
139 |
|
|
break; |
140 |
|
|
case 'e': |
141 |
|
|
parity = 'e'; |
142 |
|
|
break; |
143 |
|
|
case 'h': |
144 |
|
|
flowcl = 1; |
145 |
|
|
break; |
146 |
|
|
case 'm': |
147 |
|
|
hupcl = 0; |
148 |
|
|
break; |
149 |
|
|
case 'o': |
150 |
|
|
parity = 'o'; |
151 |
|
|
break; |
152 |
|
|
case 'p': |
153 |
|
|
pty = 1; |
154 |
|
|
break; |
155 |
|
|
case 's': |
156 |
|
|
speed = (speed_t)strtonum(optarg, 0, UINT_MAX, &errstr); |
157 |
|
|
if (errstr) { |
158 |
|
|
if (ppid != 1) |
159 |
|
|
errx(1, "speed is %s: %s", errstr, |
160 |
|
|
optarg); |
161 |
|
|
else |
162 |
|
|
goto bail_out; |
163 |
|
|
} |
164 |
|
|
break; |
165 |
|
|
case 't': |
166 |
|
|
if (!strcasecmp(optarg, "dcd")) |
167 |
|
|
tstamps.ts_set |= TIOCM_CAR; |
168 |
|
|
else if (!strcasecmp(optarg, "!dcd")) |
169 |
|
|
tstamps.ts_clr |= TIOCM_CAR; |
170 |
|
|
else if (!strcasecmp(optarg, "cts")) |
171 |
|
|
tstamps.ts_set |= TIOCM_CTS; |
172 |
|
|
else if (!strcasecmp(optarg, "!cts")) |
173 |
|
|
tstamps.ts_clr |= TIOCM_CTS; |
174 |
|
|
else { |
175 |
|
|
if (ppid != 1) |
176 |
|
|
errx(1, "'%s' not supported for " |
177 |
|
|
"timestamping", optarg); |
178 |
|
|
else |
179 |
|
|
goto bail_out; |
180 |
|
|
} |
181 |
|
|
break; |
182 |
|
|
default: |
183 |
|
|
if (ppid != -1) |
184 |
|
|
usage(); |
185 |
|
|
} |
186 |
|
|
} |
187 |
|
|
argc -= optind; |
188 |
|
|
argv += optind; |
189 |
|
|
|
190 |
|
|
if (ppid != 1 && argc != 2) |
191 |
|
|
usage(); |
192 |
|
|
|
193 |
|
|
disc = *argv++; |
194 |
|
|
dev = *argv; |
195 |
|
|
if (strncmp(_PATH_DEV, dev, sizeof(_PATH_DEV) - 1)) { |
196 |
|
|
(void)snprintf(devn, sizeof(devn), "%s%s", _PATH_DEV, dev); |
197 |
|
|
dev = devn; |
198 |
|
|
} |
199 |
|
|
|
200 |
|
|
if (!strcmp(disc, "slip")) { |
201 |
|
|
bits = 8; /* make sure we use 8 databits */ |
202 |
|
|
ldisc = SLIPDISC; |
203 |
|
|
} else if (!strcmp(disc, "nmea")) { |
204 |
|
|
ldisc = NMEADISC; |
205 |
|
|
if (speed == 0) |
206 |
|
|
speed = B4800; /* default is 4800 baud for nmea */ |
207 |
|
|
} else if (!strcmp(disc, "msts")) { |
208 |
|
|
ldisc = MSTSDISC; |
209 |
|
|
} else if (!strcmp(disc, "endrun")) { |
210 |
|
|
ldisc = ENDRUNDISC; |
211 |
|
|
} else { |
212 |
|
|
syslog(LOG_ERR, "unknown line discipline %s", disc); |
213 |
|
|
goto bail_out; |
214 |
|
|
} |
215 |
|
|
|
216 |
|
|
if ((fd = open(dev, O_RDWR)) < 0) { |
217 |
|
|
syslog(LOG_ERR, "can't open %s", dev); |
218 |
|
|
goto bail_out; |
219 |
|
|
} |
220 |
|
|
|
221 |
|
|
/* |
222 |
|
|
* Get the current line attributes, modify only values that are |
223 |
|
|
* either requested on the command line or that are needed by |
224 |
|
|
* the line discipline (e.g. nmea has a default baudrate of |
225 |
|
|
* 4800 instead of 9600). |
226 |
|
|
*/ |
227 |
|
|
if (tcgetattr(fd, &tty) < 0) { |
228 |
|
|
if (ppid != 1) |
229 |
|
|
warnx("tcgetattr"); |
230 |
|
|
goto bail_out; |
231 |
|
|
} |
232 |
|
|
|
233 |
|
|
|
234 |
|
|
if (bits == 7) { |
235 |
|
|
tty.c_cflag &= ~CS8; |
236 |
|
|
tty.c_cflag |= CS7; |
237 |
|
|
} else if (bits == 8) { |
238 |
|
|
tty.c_cflag &= ~CS7; |
239 |
|
|
tty.c_cflag |= CS8; |
240 |
|
|
} |
241 |
|
|
|
242 |
|
|
if (parity != 0) |
243 |
|
|
tty.c_cflag |= PARENB; |
244 |
|
|
if (parity == 'o') |
245 |
|
|
tty.c_cflag |= PARODD; |
246 |
|
|
else |
247 |
|
|
tty.c_cflag &= ~PARODD; |
248 |
|
|
|
249 |
|
|
if (stop == 2) |
250 |
|
|
tty.c_cflag |= CSTOPB; |
251 |
|
|
else |
252 |
|
|
tty.c_cflag &= ~CSTOPB; |
253 |
|
|
|
254 |
|
|
if (flowcl) |
255 |
|
|
tty.c_cflag |= CRTSCTS; |
256 |
|
|
|
257 |
|
|
if (hupcl == 0) |
258 |
|
|
tty.c_cflag &= ~HUPCL; |
259 |
|
|
|
260 |
|
|
if (speed != 0) |
261 |
|
|
cfsetspeed(&tty, speed); |
262 |
|
|
|
263 |
|
|
/* setup common to all line disciplines */ |
264 |
|
|
if (ioctl(fd, TIOCSDTR, 0) < 0) |
265 |
|
|
warn("TIOCSDTR"); |
266 |
|
|
if (ioctl(fd, TIOCSETD, &ldisc) < 0) { |
267 |
|
|
syslog(LOG_ERR, "can't attach %s line discipline on %s", disc, |
268 |
|
|
dev); |
269 |
|
|
goto bail_out; |
270 |
|
|
} |
271 |
|
|
|
272 |
|
|
/* line discpline specific setup */ |
273 |
|
|
switch (ldisc) { |
274 |
|
|
case NMEADISC: |
275 |
|
|
case MSTSDISC: |
276 |
|
|
case ENDRUNDISC: |
277 |
|
|
if (ioctl(fd, TIOCSTSTAMP, &tstamps) < 0) { |
278 |
|
|
warnx("TIOCSTSTAMP"); |
279 |
|
|
goto bail_out; |
280 |
|
|
} |
281 |
|
|
tty.c_cflag |= CLOCAL; |
282 |
|
|
/* FALLTHROUGH */ |
283 |
|
|
case SLIPDISC: |
284 |
|
|
tty.c_iflag = 0; |
285 |
|
|
tty.c_lflag = 0; |
286 |
|
|
tty.c_oflag = 0; |
287 |
|
|
tty.c_cc[VMIN] = 1; |
288 |
|
|
tty.c_cc[VTIME] = 0; |
289 |
|
|
break; |
290 |
|
|
} |
291 |
|
|
|
292 |
|
|
/* finally set the line attributes */ |
293 |
|
|
if (tcsetattr(fd, TCSADRAIN, &tty) < 0) { |
294 |
|
|
if (ppid != 1) |
295 |
|
|
warnx("tcsetattr"); |
296 |
|
|
goto bail_out; |
297 |
|
|
} |
298 |
|
|
|
299 |
|
|
/* |
300 |
|
|
* open a pty(4) pair to pass the data if the -p option has been |
301 |
|
|
* given on the commandline. |
302 |
|
|
*/ |
303 |
|
|
if (pty) { |
304 |
|
|
if (openpty(&master, &slave, ptyn, NULL, NULL)) |
305 |
|
|
errx(1, "can't open a pty"); |
306 |
|
|
close(slave); |
307 |
|
|
printf("%s\n", ptyn); |
308 |
|
|
fflush(stdout); |
309 |
|
|
} |
310 |
|
|
if (nodaemon) |
311 |
|
|
openlog("ldattach", LOG_PID | LOG_CONS | LOG_PERROR, |
312 |
|
|
LOG_DAEMON); |
313 |
|
|
else { |
314 |
|
|
openlog("ldattach", LOG_PID | LOG_CONS, LOG_DAEMON); |
315 |
|
|
if (daemon(0, 0)) |
316 |
|
|
errx(1, "can't daemonize"); |
317 |
|
|
} |
318 |
|
|
|
319 |
|
|
syslog(LOG_INFO, "attach %s on %s", disc, dev); |
320 |
|
|
signal(SIGHUP, coroner); |
321 |
|
|
signal(SIGTERM, coroner); |
322 |
|
|
|
323 |
|
|
if (master != -1) { |
324 |
|
|
syslog(LOG_INFO, "passing data to %s", ptyn); |
325 |
|
|
relay(fd, master); |
326 |
|
|
} else { |
327 |
|
|
sigemptyset(&sigset); |
328 |
|
|
|
329 |
|
|
while (!dying) |
330 |
|
|
sigsuspend(&sigset); |
331 |
|
|
} |
332 |
|
|
|
333 |
|
|
bail_out: |
334 |
|
|
if (ppid == 1) |
335 |
|
|
sleep(30); /* delay restart when called from init */ |
336 |
|
|
|
337 |
|
|
return 0; |
338 |
|
|
} |
339 |
|
|
|
340 |
|
|
/* ARGSUSED */ |
341 |
|
|
void |
342 |
|
|
coroner(int useless) |
343 |
|
|
{ |
344 |
|
|
dying = 1; |
345 |
|
|
} |