xref: /openbsd/sbin/ldattach/ldattach.c (revision a6445c1d)
1 /*	$OpenBSD: ldattach.c,v 1.15 2014/08/10 02:09:35 guenther 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/ttycom.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 }
346