xref: /openbsd/usr.bin/cu/cu.c (revision f928e069)
1*f928e069Sderaadt /* $OpenBSD: cu.c,v 1.31 2024/02/10 15:29:04 deraadt Exp $ */
2f391cec5Snicm 
3f391cec5Snicm /*
4f391cec5Snicm  * Copyright (c) 2012 Nicholas Marriott <nicm@openbsd.org>
5f391cec5Snicm  *
6f391cec5Snicm  * Permission to use, copy, modify, and distribute this software for any
7f391cec5Snicm  * purpose with or without fee is hereby granted, provided that the above
8f391cec5Snicm  * copyright notice and this permission notice appear in all copies.
9f391cec5Snicm  *
10f391cec5Snicm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11f391cec5Snicm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12f391cec5Snicm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13f391cec5Snicm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14f391cec5Snicm  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15f391cec5Snicm  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16f391cec5Snicm  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17f391cec5Snicm  */
18f391cec5Snicm 
191835b44fSkrw #include <sys/types.h>
20f391cec5Snicm #include <sys/ioctl.h>
211835b44fSkrw #include <sys/sysctl.h>
22f391cec5Snicm 
23023045f1Shalex #include <ctype.h>
24f391cec5Snicm #include <err.h>
251835b44fSkrw #include <errno.h>
26f391cec5Snicm #include <event.h>
27f391cec5Snicm #include <fcntl.h>
28f391cec5Snicm #include <getopt.h>
29f391cec5Snicm #include <paths.h>
30f391cec5Snicm #include <pwd.h>
31f391cec5Snicm #include <signal.h>
32f391cec5Snicm #include <stdio.h>
33f391cec5Snicm #include <stdlib.h>
34f391cec5Snicm #include <string.h>
35f391cec5Snicm #include <termios.h>
36f391cec5Snicm #include <unistd.h>
37b9fc9a72Sderaadt #include <limits.h>
38f391cec5Snicm 
39f391cec5Snicm #include "cu.h"
40f391cec5Snicm 
41f391cec5Snicm extern char		*__progname;
42f391cec5Snicm 
4366d5e211Snicm FILE			*record_file;
44f391cec5Snicm struct termios		 saved_tio;
45f391cec5Snicm struct bufferevent	*input_ev;
46f391cec5Snicm struct bufferevent	*output_ev;
47f3767adaSnicm int			 escape_char = '~';
4850866e70Snicm int			 is_direct = -1;
49087a5e3bSderaadt int			 restricted = 0;
50439243a3Snicm const char		*line_path = NULL;
51439243a3Snicm int			 line_speed = -1;
52f391cec5Snicm int			 line_fd;
5389ee0e62Snicm struct termios		 line_tio;
54f391cec5Snicm struct bufferevent	*line_ev;
55f391cec5Snicm struct event		 sigterm_ev;
56c30d1c6aSnicm struct event		 sighup_ev;
57f391cec5Snicm enum {
58f391cec5Snicm 	STATE_NONE,
59f391cec5Snicm 	STATE_NEWLINE,
60f3767adaSnicm 	STATE_ESCAPE
61f391cec5Snicm } last_state = STATE_NEWLINE;
62f391cec5Snicm 
63f391cec5Snicm __dead void	usage(void);
64f391cec5Snicm void		signal_event(int, short, void *);
65f391cec5Snicm void		stream_read(struct bufferevent *, void *);
66f391cec5Snicm void		stream_error(struct bufferevent *, short, void *);
67f391cec5Snicm void		line_read(struct bufferevent *, void *);
68f391cec5Snicm void		line_error(struct bufferevent *, short, void *);
69a035d822Snicm void		try_remote(const char *, const char *, const char *);
701835b44fSkrw char		*get_ucomnames(void);
711835b44fSkrw char 		*find_ucom(const char *, char *);
72f391cec5Snicm 
73f391cec5Snicm __dead void
usage(void)74f391cec5Snicm usage(void)
75f391cec5Snicm {
76f3767adaSnicm 	fprintf(stderr, "usage: %s [-dr] [-E escape_char] [-l line] "
77f3767adaSnicm 	    "[-s speed | -speed]\n", __progname);
781a0917ceSnicm 	fprintf(stderr, "       %s [host]\n", __progname);
79f391cec5Snicm 	exit(1);
80f391cec5Snicm }
81f391cec5Snicm 
82f391cec5Snicm int
main(int argc,char ** argv)83f391cec5Snicm main(int argc, char **argv)
84f391cec5Snicm {
85439243a3Snicm 	const char	*errstr;
861835b44fSkrw 	char		*tmp, *s, *host, *ucomnames;
8750866e70Snicm 	int		 opt, i, flags;
88f391cec5Snicm 
891835b44fSkrw 	ucomnames = get_ucomnames();
901835b44fSkrw 
91758d4455Sderaadt 	if (pledge("stdio rpath wpath cpath getpw proc exec tty",
92758d4455Sderaadt 	    NULL) == -1)
93758d4455Sderaadt 		err(1, "pledge");
94758d4455Sderaadt 
95439243a3Snicm 	if (isatty(STDIN_FILENO) && tcgetattr(STDIN_FILENO, &saved_tio) != 0)
96439243a3Snicm 		err(1, "tcgetattr");
97f391cec5Snicm 
98f391cec5Snicm 	/*
99f391cec5Snicm 	 * Convert obsolescent -### speed to modern -s### syntax which getopt()
100f391cec5Snicm 	 * can handle.
101f391cec5Snicm 	 */
102f391cec5Snicm 	for (i = 1; i < argc; i++) {
1030630af0bSdlg 		if (strcmp("--", argv[i]) == 0)
104f391cec5Snicm 			break;
105f3767adaSnicm 		if (argv[i][0] != '-' || !isdigit((u_char)argv[i][1]))
1060630af0bSdlg 			continue;
1070630af0bSdlg 
1080630af0bSdlg 		if (asprintf(&argv[i], "-s%s", &argv[i][1]) == -1)
1090630af0bSdlg 			errx(1, "speed asprintf");
110f391cec5Snicm 	}
111f391cec5Snicm 
112f3767adaSnicm 	while ((opt = getopt(argc, argv, "drE:l:s:")) != -1) {
113f391cec5Snicm 		switch (opt) {
11450866e70Snicm 		case 'd':
11550866e70Snicm 			is_direct = 1;
11650866e70Snicm 			break;
117087a5e3bSderaadt 		case 'r':
118087a5e3bSderaadt 			if (pledge("stdio rpath wpath tty", NULL) == -1)
119087a5e3bSderaadt 				err(1, "pledge");
120087a5e3bSderaadt 			restricted = 1;
121087a5e3bSderaadt 			break;
122f3767adaSnicm 		case 'E':
123f3767adaSnicm 			if (optarg[0] == '^' && optarg[2] == '\0' &&
124f3767adaSnicm 			    (u_char)optarg[1] >= 64 && (u_char)optarg[1] < 128)
125f3767adaSnicm 				escape_char = (u_char)optarg[1] & 31;
126f3767adaSnicm 			else if (strlen(optarg) == 1)
127f3767adaSnicm 				escape_char = (u_char)optarg[0];
128f3767adaSnicm 			else
129f3767adaSnicm 				errx(1, "invalid escape character: %s", optarg);
130f3767adaSnicm 			break;
131f391cec5Snicm 		case 'l':
132439243a3Snicm 			line_path = optarg;
133f391cec5Snicm 			break;
134f391cec5Snicm 		case 's':
135439243a3Snicm 			line_speed = strtonum(optarg, 0, INT_MAX, &errstr);
136f391cec5Snicm 			if (errstr != NULL)
137f391cec5Snicm 				errx(1, "speed is %s: %s", errstr, optarg);
138f391cec5Snicm 			break;
139f391cec5Snicm 		default:
140f391cec5Snicm 			usage();
141f391cec5Snicm 		}
142f391cec5Snicm 	}
143f391cec5Snicm 	argc -= optind;
144f391cec5Snicm 	argv += optind;
145439243a3Snicm 	if (argc != 0 && argc != 1)
146f391cec5Snicm 		usage();
147f391cec5Snicm 
14850866e70Snicm 	if (line_path != NULL || line_speed != -1 || is_direct != -1) {
1491a0917ceSnicm 		if (argc != 0)
1501a0917ceSnicm 			usage();
1511a0917ceSnicm 	} else {
152a035d822Snicm 		if (argc == 1)
153a035d822Snicm 			host = argv[0];
154439243a3Snicm 		else
155a035d822Snicm 			host = getenv("HOST");
156a035d822Snicm 		if (host != NULL && *host != '\0') {
1571a0917ceSnicm 			if (*host == '/')
158290c3e61Snicm 				line_path = host;
1591a0917ceSnicm 			else {
160a035d822Snicm 				s = getenv("REMOTE");
161a035d822Snicm 				if (s != NULL && *s == '/')
162a035d822Snicm 					try_remote(host, s, NULL);
163a035d822Snicm 				else
164a035d822Snicm 					try_remote(host, NULL, s);
165a035d822Snicm 			}
166290c3e61Snicm 		}
1671a0917ceSnicm 	}
168439243a3Snicm 
169439243a3Snicm 	if (line_path == NULL)
170439243a3Snicm 		line_path = "/dev/cua00";
171439243a3Snicm 	if (line_speed == -1)
172439243a3Snicm 		line_speed = 9600;
17350866e70Snicm 	if (is_direct == -1)
17450866e70Snicm 		is_direct = 0;
175439243a3Snicm 
1761835b44fSkrw 	if (strncasecmp(line_path, "usb", 3) == 0) {
1771835b44fSkrw 		tmp = find_ucom(line_path, ucomnames);
1781835b44fSkrw 		if (tmp == NULL)
1791835b44fSkrw 			errx(1, "No ucom matched '%s'", line_path);
1801835b44fSkrw 		line_path = tmp;
1811835b44fSkrw 	}
182439243a3Snicm 	if (strchr(line_path, '/') == NULL) {
183439243a3Snicm 		if (asprintf(&tmp, "%s%s", _PATH_DEV, line_path) == -1)
184f391cec5Snicm 			err(1, "asprintf");
185439243a3Snicm 		line_path = tmp;
186f391cec5Snicm 	}
187f391cec5Snicm 
18850866e70Snicm 	flags = O_RDWR;
18950866e70Snicm 	if (is_direct)
19050866e70Snicm 		flags |= O_NONBLOCK;
19150866e70Snicm 	line_fd = open(line_path, flags);
1923aaa63ebSderaadt 	if (line_fd == -1)
193439243a3Snicm 		err(1, "open(\"%s\")", line_path);
194087a5e3bSderaadt 	if (restricted && pledge("stdio tty", NULL) == -1)
195087a5e3bSderaadt 		err(1, "pledge");
1962db3b77dSmestre 	if (!isatty(line_fd))
1972db3b77dSmestre 		err(1, "%s", line_path);
198f391cec5Snicm 	if (ioctl(line_fd, TIOCEXCL) != 0)
199f391cec5Snicm 		err(1, "ioctl(TIOCEXCL)");
20089ee0e62Snicm 	if (tcgetattr(line_fd, &line_tio) != 0)
20189ee0e62Snicm 		err(1, "tcgetattr");
202439243a3Snicm 	if (set_line(line_speed) != 0)
20311403328Snicm 		err(1, "tcsetattr");
204f391cec5Snicm 
205f391cec5Snicm 	event_init();
206ca5f2657Snicm 
207f391cec5Snicm 	signal_set(&sigterm_ev, SIGTERM, signal_event, NULL);
208f391cec5Snicm 	signal_add(&sigterm_ev, NULL);
209c30d1c6aSnicm 	signal_set(&sighup_ev, SIGHUP, signal_event, NULL);
210c30d1c6aSnicm 	signal_add(&sighup_ev, NULL);
211ca5f2657Snicm 	if (signal(SIGINT, SIG_IGN) == SIG_ERR)
212ca5f2657Snicm 		err(1, "signal");
213ca5f2657Snicm 	if (signal(SIGQUIT, SIG_IGN) == SIG_ERR)
214ca5f2657Snicm 		err(1, "signal");
215f391cec5Snicm 
21611403328Snicm 	set_termios(); /* after this use cu_err and friends */
217f391cec5Snicm 
218f391cec5Snicm 	/* stdin and stdout get separate events */
219f391cec5Snicm 	input_ev = bufferevent_new(STDIN_FILENO, stream_read, NULL,
220f391cec5Snicm 	    stream_error, NULL);
221f391cec5Snicm 	bufferevent_enable(input_ev, EV_READ);
222f391cec5Snicm 	output_ev = bufferevent_new(STDOUT_FILENO, NULL, NULL, stream_error,
223f391cec5Snicm 	    NULL);
224f391cec5Snicm 	bufferevent_enable(output_ev, EV_WRITE);
225f391cec5Snicm 
226668574b2Snicm 	set_blocking(line_fd, 0);
227f391cec5Snicm 	line_ev = bufferevent_new(line_fd, line_read, NULL, line_error,
228f391cec5Snicm 	    NULL);
229f391cec5Snicm 	bufferevent_enable(line_ev, EV_READ|EV_WRITE);
230f391cec5Snicm 
231439243a3Snicm 	printf("Connected to %s (speed %d)\r\n", line_path, line_speed);
232f391cec5Snicm 	event_dispatch();
233f391cec5Snicm 
234654f0ea4Snicm 	restore_termios();
235f391cec5Snicm 	printf("\r\n[EOT]\n");
236f391cec5Snicm 
237f391cec5Snicm 	exit(0);
238f391cec5Snicm }
239f391cec5Snicm 
240f391cec5Snicm void
signal_event(int fd,short events,void * data)241f391cec5Snicm signal_event(int fd, short events, void *data)
242f391cec5Snicm {
243654f0ea4Snicm 	restore_termios();
244f391cec5Snicm 	printf("\r\n[SIG%s]\n", sys_signame[fd]);
245f391cec5Snicm 
246f391cec5Snicm 	exit(0);
247f391cec5Snicm }
248f391cec5Snicm 
249f391cec5Snicm void
set_blocking(int fd,int state)250668574b2Snicm set_blocking(int fd, int state)
251668574b2Snicm {
252668574b2Snicm 	int mode;
253668574b2Snicm 
254668574b2Snicm 	state = state ? 0 : O_NONBLOCK;
255668574b2Snicm 	if ((mode = fcntl(fd, F_GETFL)) == -1)
256668574b2Snicm 		cu_err(1, "fcntl");
257668574b2Snicm 	if ((mode & O_NONBLOCK) != state) {
258668574b2Snicm 		mode = (mode & ~O_NONBLOCK) | state;
259668574b2Snicm 		if (fcntl(fd, F_SETFL, mode) == -1)
260668574b2Snicm 			cu_err(1, "fcntl");
261668574b2Snicm 	}
262668574b2Snicm }
263668574b2Snicm 
264668574b2Snicm void
set_termios(void)265f391cec5Snicm set_termios(void)
266f391cec5Snicm {
267f391cec5Snicm 	struct termios tio;
268f391cec5Snicm 
269f391cec5Snicm 	if (!isatty(STDIN_FILENO))
270f391cec5Snicm 		return;
271f391cec5Snicm 
272f391cec5Snicm 	memcpy(&tio, &saved_tio, sizeof(tio));
273f391cec5Snicm 	tio.c_lflag &= ~(ICANON|IEXTEN|ECHO);
274f391cec5Snicm 	tio.c_iflag &= ~(INPCK|ICRNL);
275f391cec5Snicm 	tio.c_oflag &= ~OPOST;
276f391cec5Snicm 	tio.c_cc[VMIN] = 1;
277f391cec5Snicm 	tio.c_cc[VTIME] = 0;
278f391cec5Snicm 	tio.c_cc[VDISCARD] = _POSIX_VDISABLE;
279f391cec5Snicm 	tio.c_cc[VDSUSP] = _POSIX_VDISABLE;
280f391cec5Snicm 	tio.c_cc[VINTR] = _POSIX_VDISABLE;
281f391cec5Snicm 	tio.c_cc[VLNEXT] = _POSIX_VDISABLE;
282f391cec5Snicm 	tio.c_cc[VQUIT] = _POSIX_VDISABLE;
283f391cec5Snicm 	tio.c_cc[VSUSP] = _POSIX_VDISABLE;
284f391cec5Snicm 	if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio) != 0)
28511403328Snicm 		cu_err(1, "tcsetattr");
286f391cec5Snicm }
287f391cec5Snicm 
288f391cec5Snicm void
restore_termios(void)289f391cec5Snicm restore_termios(void)
290f391cec5Snicm {
29111403328Snicm 	if (isatty(STDIN_FILENO))
29211403328Snicm 		tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_tio);
293f391cec5Snicm }
294f391cec5Snicm 
295909d4fcfSnicm int
set_line(int speed)296909d4fcfSnicm set_line(int speed)
297909d4fcfSnicm {
298909d4fcfSnicm 	struct termios	 tio;
299909d4fcfSnicm 
30089ee0e62Snicm 	memcpy(&tio, &line_tio, sizeof(tio));
30189ee0e62Snicm 	tio.c_iflag &= ~(ISTRIP|ICRNL);
30289ee0e62Snicm 	tio.c_oflag &= ~OPOST;
30389ee0e62Snicm 	tio.c_lflag &= ~(ICANON|ISIG|IEXTEN|ECHO);
30489ee0e62Snicm 	tio.c_cflag &= ~(CSIZE|PARENB);
30589ee0e62Snicm 	tio.c_cflag |= CREAD|CS8|CLOCAL;
306909d4fcfSnicm 	tio.c_cc[VMIN] = 1;
307909d4fcfSnicm 	tio.c_cc[VTIME] = 0;
308909d4fcfSnicm 	cfsetspeed(&tio, speed);
30911403328Snicm 	if (tcsetattr(line_fd, TCSAFLUSH, &tio) != 0)
310909d4fcfSnicm 		return (-1);
311909d4fcfSnicm 	return (0);
312909d4fcfSnicm }
313909d4fcfSnicm 
314f391cec5Snicm void
stream_read(struct bufferevent * bufev,void * data)315f391cec5Snicm stream_read(struct bufferevent *bufev, void *data)
316f391cec5Snicm {
317f391cec5Snicm 	char	*new_data, *ptr;
318f391cec5Snicm 	size_t	 new_size;
319f391cec5Snicm 	int	 state_change;
320f391cec5Snicm 
321f391cec5Snicm 	new_data = EVBUFFER_DATA(input_ev->input);
322f391cec5Snicm 	new_size = EVBUFFER_LENGTH(input_ev->input);
323f391cec5Snicm 	if (new_size == 0)
324f391cec5Snicm 		return;
325f391cec5Snicm 
326f391cec5Snicm 	state_change = isatty(STDIN_FILENO);
327f391cec5Snicm 	for (ptr = new_data; ptr < new_data + new_size; ptr++) {
328f391cec5Snicm 		switch (last_state) {
329f391cec5Snicm 		case STATE_NONE:
330f391cec5Snicm 			if (state_change && *ptr == '\r')
331f391cec5Snicm 				last_state = STATE_NEWLINE;
332f391cec5Snicm 			break;
333f391cec5Snicm 		case STATE_NEWLINE:
334f3767adaSnicm 			if (state_change && (u_char)*ptr == escape_char) {
335f3767adaSnicm 				last_state = STATE_ESCAPE;
336f391cec5Snicm 				continue;
337f391cec5Snicm 			}
338f391cec5Snicm 			if (*ptr != '\r')
339f391cec5Snicm 				last_state = STATE_NONE;
340f391cec5Snicm 			break;
341f3767adaSnicm 		case STATE_ESCAPE:
342f391cec5Snicm 			do_command(*ptr);
343f391cec5Snicm 			last_state = STATE_NEWLINE;
344f391cec5Snicm 			continue;
345f391cec5Snicm 		}
346f391cec5Snicm 
347f391cec5Snicm 		bufferevent_write(line_ev, ptr, 1);
348f391cec5Snicm 	}
349f391cec5Snicm 
350f391cec5Snicm 	evbuffer_drain(input_ev->input, new_size);
351f391cec5Snicm }
352f391cec5Snicm 
353f391cec5Snicm void
stream_error(struct bufferevent * bufev,short what,void * data)354f391cec5Snicm stream_error(struct bufferevent *bufev, short what, void *data)
355f391cec5Snicm {
356f391cec5Snicm 	event_loopexit(NULL);
357f391cec5Snicm }
358f391cec5Snicm 
359f391cec5Snicm void
line_read(struct bufferevent * bufev,void * data)360f391cec5Snicm line_read(struct bufferevent *bufev, void *data)
361f391cec5Snicm {
362f391cec5Snicm 	char	*new_data;
363f391cec5Snicm 	size_t	 new_size;
364f391cec5Snicm 
365f391cec5Snicm 	new_data = EVBUFFER_DATA(line_ev->input);
366f391cec5Snicm 	new_size = EVBUFFER_LENGTH(line_ev->input);
367f391cec5Snicm 	if (new_size == 0)
368f391cec5Snicm 		return;
369f391cec5Snicm 
37066d5e211Snicm 	if (record_file != NULL)
37166d5e211Snicm 		fwrite(new_data, 1, new_size, record_file);
372f391cec5Snicm 	bufferevent_write(output_ev, new_data, new_size);
373f391cec5Snicm 
374f391cec5Snicm 	evbuffer_drain(line_ev->input, new_size);
375f391cec5Snicm }
376f391cec5Snicm 
377f391cec5Snicm void
line_error(struct bufferevent * bufev,short what,void * data)378f391cec5Snicm line_error(struct bufferevent *bufev, short what, void *data)
379f391cec5Snicm {
380f391cec5Snicm 	event_loopexit(NULL);
381f391cec5Snicm }
382f391cec5Snicm 
383439243a3Snicm void
try_remote(const char * host,const char * path,const char * entry)384a035d822Snicm try_remote(const char *host, const char *path, const char *entry)
385439243a3Snicm {
386439243a3Snicm 	const char	*paths[] = { "/etc/remote", NULL, NULL };
387439243a3Snicm 	char		*cp, *s;
388439243a3Snicm 	long		 l;
389439243a3Snicm 	int		 error;
390439243a3Snicm 
391439243a3Snicm 	if (path != NULL) {
392439243a3Snicm 		paths[0] = path;
393439243a3Snicm 		paths[1] = "/etc/remote";
394439243a3Snicm 	}
395439243a3Snicm 
396a035d822Snicm 	if (entry != NULL && cgetset(entry) != 0)
397a035d822Snicm 		cu_errx(1, "cgetset failed");
398439243a3Snicm 	error = cgetent(&cp, (char **)paths, (char *)host);
399439243a3Snicm 	if (error < 0) {
400439243a3Snicm 		switch (error) {
401439243a3Snicm 		case -1:
402439243a3Snicm 			cu_errx(1, "unknown host %s", host);
403439243a3Snicm 		case -2:
404439243a3Snicm 			cu_errx(1, "can't open remote file");
405439243a3Snicm 		case -3:
406439243a3Snicm 			cu_errx(1, "loop in remote file");
407439243a3Snicm 		default:
408439243a3Snicm 			cu_errx(1, "unknown error in remote file");
409439243a3Snicm 		}
410439243a3Snicm 	}
411439243a3Snicm 
41250866e70Snicm 	if (is_direct == -1 && cgetcap(cp, "dc", ':') != NULL)
41350866e70Snicm 		is_direct = 1;
41450866e70Snicm 
415439243a3Snicm 	if (line_path == NULL && cgetstr(cp, "dv", &s) >= 0)
416439243a3Snicm 		line_path = s;
417439243a3Snicm 
418439243a3Snicm 	if (line_speed == -1 && cgetnum(cp, "br", &l) >= 0) {
419439243a3Snicm 		if (l < 0 || l > INT_MAX)
420439243a3Snicm 			cu_errx(1, "speed out of range");
421439243a3Snicm 		line_speed = l;
422439243a3Snicm 	}
423439243a3Snicm }
424439243a3Snicm 
425f391cec5Snicm /* Expands tildes in the file name. Based on code from ssh/misc.c. */
426f391cec5Snicm char *
tilde_expand(const char * filename1)427f391cec5Snicm tilde_expand(const char *filename1)
428f391cec5Snicm {
42934574ca2Stedu 	const char	*filename, *path, *sep;
43034574ca2Stedu 	char		 user[128], *out;
431f391cec5Snicm 	struct passwd	*pw;
432f391cec5Snicm 	u_int		 len, slash;
43334574ca2Stedu 	int		 rv;
434f391cec5Snicm 
435f391cec5Snicm 	if (*filename1 != '~')
436f391cec5Snicm 		goto no_change;
437f391cec5Snicm 	filename = filename1 + 1;
438f391cec5Snicm 
439f391cec5Snicm 	path = strchr(filename, '/');
440f391cec5Snicm 	if (path != NULL && path > filename) {		/* ~user/path */
441f391cec5Snicm 		slash = path - filename;
442f391cec5Snicm 		if (slash > sizeof(user) - 1)
443f391cec5Snicm 			goto no_change;
444f391cec5Snicm 		memcpy(user, filename, slash);
445f391cec5Snicm 		user[slash] = '\0';
446f391cec5Snicm 		if ((pw = getpwnam(user)) == NULL)
447f391cec5Snicm 			goto no_change;
448f391cec5Snicm 	} else if ((pw = getpwuid(getuid())) == NULL)	/* ~/path */
449f391cec5Snicm 		goto no_change;
450f391cec5Snicm 
451f391cec5Snicm 	/* Make sure directory has a trailing '/' */
452f391cec5Snicm 	len = strlen(pw->pw_dir);
45334574ca2Stedu 	if (len == 0 || pw->pw_dir[len - 1] != '/')
45434574ca2Stedu 		sep = "/";
45534574ca2Stedu 	else
45634574ca2Stedu 		sep = "";
457f391cec5Snicm 
458f391cec5Snicm 	/* Skip leading '/' from specified path */
459f391cec5Snicm 	if (path != NULL)
460f391cec5Snicm 		filename = path + 1;
461f391cec5Snicm 
46234574ca2Stedu 	if ((rv = asprintf(&out, "%s%s%s", pw->pw_dir, sep, filename)) == -1)
46334574ca2Stedu 		cu_err(1, "asprintf");
464b9fc9a72Sderaadt 	if (rv >= PATH_MAX) {
46534574ca2Stedu 		free(out);
46634574ca2Stedu 		goto no_change;
46734574ca2Stedu 	}
46834574ca2Stedu 
469f391cec5Snicm 	return (out);
470f391cec5Snicm 
471f391cec5Snicm no_change:
472f391cec5Snicm 	out = strdup(filename1);
473f391cec5Snicm 	if (out == NULL)
47411403328Snicm 		cu_err(1, "strdup");
475f391cec5Snicm 	return (out);
476f391cec5Snicm }
4771835b44fSkrw 
4781835b44fSkrw char *
get_ucomnames(void)4791835b44fSkrw get_ucomnames(void)
4801835b44fSkrw {
4811835b44fSkrw 	char *names;
4821835b44fSkrw 	int mib[2];
4831835b44fSkrw 	size_t size;
4841835b44fSkrw 
4851835b44fSkrw 	mib[0] = CTL_HW;
4861835b44fSkrw 	mib[1] = HW_UCOMNAMES;
4871835b44fSkrw 	names = NULL;
4881835b44fSkrw 	size = 0;
4891835b44fSkrw 	for (;;) {
4901835b44fSkrw 		if (sysctl(mib, 2, NULL, &size, NULL, 0) == -1 || size == 0)
491*f928e069Sderaadt 			return NULL;
4921835b44fSkrw 		if ((names = realloc(names, size)) == NULL)
4931835b44fSkrw 			err(1, NULL);
4941835b44fSkrw 		if (sysctl(mib, 2, names, &size, NULL, 0) != -1)
4951835b44fSkrw 			break;
4961835b44fSkrw 		if (errno != ENOMEM)
497*f928e069Sderaadt 			return NULL;
4981835b44fSkrw 	}
4991835b44fSkrw 	return names;
5001835b44fSkrw }
5011835b44fSkrw 
5021835b44fSkrw char *
find_ucom(const char * usbid,char * names)5031835b44fSkrw find_ucom(const char *usbid, char *names)
5041835b44fSkrw {
5051835b44fSkrw 	char *cua, *id, *ucom;
5061835b44fSkrw 
5071835b44fSkrw 	if (names == NULL)
5081835b44fSkrw 		return NULL;
5091835b44fSkrw 
5105cc7c8afSjca 	/* names is a comma separated list of "ucom<unit#>:<usb id>". */
5111835b44fSkrw 	cua = NULL;
5121835b44fSkrw 	for (ucom = strsep(&names, ","); ucom; ucom = strsep(&names, ",")) {
5131835b44fSkrw 		if (*ucom == '\0' || strncasecmp(ucom, "ucom", 4))
5141835b44fSkrw 			continue;
5151835b44fSkrw 		ucom += 4;
5161835b44fSkrw 		id = strchr(ucom, ':');
5171835b44fSkrw 		if (id == NULL)
5181835b44fSkrw 			continue;
5191835b44fSkrw 		*id++ = '\0';
5201835b44fSkrw 		if (strcasecmp(id, usbid) == 0) {
5211835b44fSkrw 			if (asprintf(&cua, "cuaU%s", ucom) == -1)
5221835b44fSkrw 				err(1, NULL);
5231835b44fSkrw 			break;
5241835b44fSkrw 		}
5251835b44fSkrw 	}
5261835b44fSkrw 	return cua;
5271835b44fSkrw }
528