GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: sbin/ldattach/ldattach.c Lines: 0 160 0.0 %
Date: 2016-12-06 Branches: 0 114 0.0 %

Line Branch Exec Source
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
}