GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: sbin/ldattach/ldattach.c Lines: 0 151 0.0 %
Date: 2017-11-07 Branches: 0 103 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: ldattach.c,v 1.17 2016/11/26 11:18:43 mpi 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, "nmea")) {
201
		ldisc = NMEADISC;
202
		if (speed == 0)
203
			speed = B4800;	/* default is 4800 baud for nmea */
204
	} else if (!strcmp(disc, "msts")) {
205
		ldisc = MSTSDISC;
206
	} else if (!strcmp(disc, "endrun")) {
207
		ldisc = ENDRUNDISC;
208
	} else {
209
		syslog(LOG_ERR, "unknown line discipline %s", disc);
210
		goto bail_out;
211
	}
212
213
	if ((fd = open(dev, O_RDWR)) < 0) {
214
		syslog(LOG_ERR, "can't open %s", dev);
215
		goto bail_out;
216
	}
217
218
	/*
219
	 * Get the current line attributes, modify only values that are
220
	 * either requested on the command line or that are needed by
221
	 * the line discipline (e.g. nmea has a default baudrate of
222
	 * 4800 instead of 9600).
223
	 */
224
	if (tcgetattr(fd, &tty) < 0) {
225
		if (ppid != 1)
226
			warnx("tcgetattr");
227
		goto bail_out;
228
	}
229
230
231
	if (bits == 7) {
232
		tty.c_cflag &= ~CS8;
233
		tty.c_cflag |= CS7;
234
	} else if (bits == 8) {
235
		tty.c_cflag &= ~CS7;
236
		tty.c_cflag |= CS8;
237
	}
238
239
	if (parity != 0)
240
		tty.c_cflag |= PARENB;
241
	if (parity == 'o')
242
		tty.c_cflag |= PARODD;
243
	else
244
		tty.c_cflag &= ~PARODD;
245
246
	if (stop == 2)
247
		tty.c_cflag |= CSTOPB;
248
	else
249
		tty.c_cflag &= ~CSTOPB;
250
251
	if (flowcl)
252
		tty.c_cflag |= CRTSCTS;
253
254
	if (hupcl == 0)
255
		tty.c_cflag &= ~HUPCL;
256
257
	if (speed != 0)
258
		cfsetspeed(&tty, speed);
259
260
	/* setup common to all line disciplines */
261
	if (ioctl(fd, TIOCSDTR, 0) < 0)
262
		warn("TIOCSDTR");
263
	if (ioctl(fd, TIOCSETD, &ldisc) < 0) {
264
		syslog(LOG_ERR, "can't attach %s line discipline on %s", disc,
265
		    dev);
266
		goto bail_out;
267
	}
268
269
	/* line discpline specific setup */
270
	switch (ldisc) {
271
	case NMEADISC:
272
	case MSTSDISC:
273
	case ENDRUNDISC:
274
		if (ioctl(fd, TIOCSTSTAMP, &tstamps) < 0) {
275
			warnx("TIOCSTSTAMP");
276
			goto bail_out;
277
		}
278
		tty.c_cflag |= CLOCAL;
279
		tty.c_iflag = 0;
280
		tty.c_lflag = 0;
281
		tty.c_oflag = 0;
282
		tty.c_cc[VMIN] = 1;
283
		tty.c_cc[VTIME] = 0;
284
		break;
285
	}
286
287
	/* finally set the line attributes */
288
	if (tcsetattr(fd, TCSADRAIN, &tty) < 0) {
289
		if (ppid != 1)
290
			warnx("tcsetattr");
291
		goto bail_out;
292
	}
293
294
	/*
295
	 * open a pty(4) pair to pass the data if the -p option has been
296
	 * given on the commandline.
297
	 */
298
	if (pty) {
299
		if (openpty(&master, &slave, ptyn, NULL, NULL))
300
			errx(1, "can't open a pty");
301
		close(slave);
302
		printf("%s\n", ptyn);
303
		fflush(stdout);
304
	}
305
	if (nodaemon)
306
		openlog("ldattach", LOG_PID | LOG_CONS | LOG_PERROR,
307
		    LOG_DAEMON);
308
	else {
309
		openlog("ldattach", LOG_PID | LOG_CONS, LOG_DAEMON);
310
		if (daemon(0, 0))
311
			errx(1, "can't daemonize");
312
	}
313
314
	syslog(LOG_INFO, "attach %s on %s", disc, dev);
315
	signal(SIGHUP, coroner);
316
	signal(SIGTERM, coroner);
317
318
	if (master != -1) {
319
		syslog(LOG_INFO, "passing data to %s", ptyn);
320
		relay(fd, master);
321
	} else {
322
		sigemptyset(&sigset);
323
324
		while (!dying)
325
			sigsuspend(&sigset);
326
	}
327
328
bail_out:
329
	if (ppid == 1)
330
		sleep(30);	/* delay restart when called from init */
331
332
	return 0;
333
}
334
335
/* ARGSUSED */
336
void
337
coroner(int useless)
338
{
339
	dying = 1;
340
}