xref: /openbsd-src/usr.bin/cu/command.c (revision 3aaa63eb46949490a39db9c6d82aacc8ee5d8551)
1*3aaa63ebSderaadt /* $OpenBSD: command.c,v 1.18 2019/06/28 13:35:00 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 
19f391cec5Snicm #include <sys/types.h>
20f391cec5Snicm #include <sys/ioctl.h>
21f391cec5Snicm #include <sys/wait.h>
22f391cec5Snicm 
23f391cec5Snicm #include <event.h>
24f391cec5Snicm #include <errno.h>
25f391cec5Snicm #include <fcntl.h>
26a3cadbf3Snicm #include <limits.h>
27f391cec5Snicm #include <paths.h>
28f391cec5Snicm #include <signal.h>
29f391cec5Snicm #include <stdlib.h>
30f391cec5Snicm #include <stdio.h>
31087a5e3bSderaadt #include <string.h>
32f391cec5Snicm #include <unistd.h>
33f3767adaSnicm #include <vis.h>
34f391cec5Snicm 
35f391cec5Snicm #include "cu.h"
36f391cec5Snicm 
37f391cec5Snicm void	pipe_command(void);
382001eabaSnicm void	connect_command(void);
39f391cec5Snicm void	send_file(void);
40649fc061Snicm void	send_xmodem(void);
4143a6699fSderaadt void	set_speed(void);
4243a6699fSderaadt void	start_record(void);
43f391cec5Snicm 
44f391cec5Snicm void
pipe_command(void)45f391cec5Snicm pipe_command(void)
46f391cec5Snicm {
47f391cec5Snicm 	const char	*cmd;
48f391cec5Snicm 	pid_t		 pid;
49f391cec5Snicm 	int		 fd;
50f391cec5Snicm 
51f391cec5Snicm 	cmd = get_input("Local command?");
52f391cec5Snicm 	if (cmd == NULL || *cmd == '\0')
53f391cec5Snicm 		return;
54f391cec5Snicm 
55ca5f2657Snicm 	restore_termios();
56668574b2Snicm 	set_blocking(line_fd, 1);
57ca5f2657Snicm 
58f391cec5Snicm 	switch (pid = fork()) {
59f391cec5Snicm 	case -1:
6011403328Snicm 		cu_err(1, "fork");
61f391cec5Snicm 	case 0:
62f391cec5Snicm 		fd = open(_PATH_DEVNULL, O_RDWR);
63*3aaa63ebSderaadt 		if (fd == -1 || dup2(fd, STDIN_FILENO) == -1)
64f391cec5Snicm 			_exit(1);
65f391cec5Snicm 		close(fd);
66f391cec5Snicm 
67ca5f2657Snicm 		if (signal(SIGINT, SIG_DFL) == SIG_ERR)
68ca5f2657Snicm 			_exit(1);
69ca5f2657Snicm 		if (signal(SIGQUIT, SIG_DFL) == SIG_ERR)
70ca5f2657Snicm 			_exit(1);
71ca5f2657Snicm 
72f391cec5Snicm 		/* attach stdout to line */
73f391cec5Snicm 		if (dup2(line_fd, STDOUT_FILENO) == -1)
74f391cec5Snicm 			_exit(1);
75f391cec5Snicm 
76d8a7f187Snicm 		if (closefrom(STDERR_FILENO + 1) != 0)
77f391cec5Snicm 			_exit(1);
78f391cec5Snicm 
79b6d7cb74Snicm 		execl(_PATH_BSHELL, "sh", "-c", cmd, (char *)NULL);
80f391cec5Snicm 		_exit(1);
81f391cec5Snicm 	default:
82f391cec5Snicm 		while (waitpid(pid, NULL, 0) == -1 && errno == EINTR)
83f391cec5Snicm 			/* nothing */;
84f391cec5Snicm 		break;
85f391cec5Snicm 	}
86ca5f2657Snicm 
87668574b2Snicm 	set_blocking(line_fd, 0);
88ca5f2657Snicm 	set_termios();
89f391cec5Snicm }
90f391cec5Snicm 
91f391cec5Snicm void
connect_command(void)922001eabaSnicm connect_command(void)
932001eabaSnicm {
942001eabaSnicm 	const char	*cmd;
952001eabaSnicm 	pid_t		 pid;
962001eabaSnicm 
972001eabaSnicm 	/*
982001eabaSnicm 	 * Fork a program with:
992001eabaSnicm 	 *  0 <-> remote tty in
1002001eabaSnicm 	 *  1 <-> remote tty out
1012001eabaSnicm 	 *  2 <-> local tty stderr
1022001eabaSnicm 	 */
1032001eabaSnicm 
1042001eabaSnicm 	cmd = get_input("Local command?");
1052001eabaSnicm 	if (cmd == NULL || *cmd == '\0')
1062001eabaSnicm 		return;
1072001eabaSnicm 
1082001eabaSnicm 	restore_termios();
109668574b2Snicm 	set_blocking(line_fd, 1);
1102001eabaSnicm 
1112001eabaSnicm 	switch (pid = fork()) {
1122001eabaSnicm 	case -1:
11311403328Snicm 		cu_err(1, "fork");
1142001eabaSnicm 	case 0:
1152001eabaSnicm 		if (signal(SIGINT, SIG_DFL) == SIG_ERR)
1162001eabaSnicm 			_exit(1);
1172001eabaSnicm 		if (signal(SIGQUIT, SIG_DFL) == SIG_ERR)
1182001eabaSnicm 			_exit(1);
1192001eabaSnicm 
1202001eabaSnicm 		/* attach stdout and stdin to line */
1212001eabaSnicm 		if (dup2(line_fd, STDOUT_FILENO) == -1)
1222001eabaSnicm 			_exit(1);
1232001eabaSnicm 		if (dup2(line_fd, STDIN_FILENO) == -1)
1242001eabaSnicm 			_exit(1);
1252001eabaSnicm 
126d8a7f187Snicm 		if (closefrom(STDERR_FILENO + 1) != 0)
1272001eabaSnicm 			_exit(1);
1282001eabaSnicm 
129b6d7cb74Snicm 		execl(_PATH_BSHELL, "sh", "-c", cmd, (char *)NULL);
1302001eabaSnicm 		_exit(1);
1312001eabaSnicm 	default:
1322001eabaSnicm 		while (waitpid(pid, NULL, 0) == -1 && errno == EINTR)
1332001eabaSnicm 			/* nothing */;
1342001eabaSnicm 		break;
1352001eabaSnicm 	}
1362001eabaSnicm 
137668574b2Snicm 	set_blocking(line_fd, 0);
1382001eabaSnicm 	set_termios();
1392001eabaSnicm }
1402001eabaSnicm 
1412001eabaSnicm void
send_file(void)142f391cec5Snicm send_file(void)
143f391cec5Snicm {
144f391cec5Snicm 	const char	*file;
145f391cec5Snicm 	FILE		*f;
146f391cec5Snicm 	char		 buf[BUFSIZ], *expanded;
147f391cec5Snicm 	size_t		 len;
148f391cec5Snicm 
149f391cec5Snicm 	file = get_input("Local file?");
150f391cec5Snicm 	if (file == NULL || *file == '\0')
151f391cec5Snicm 		return;
152f391cec5Snicm 
153f391cec5Snicm 	expanded = tilde_expand(file);
154f391cec5Snicm 	f = fopen(expanded, "r");
155f391cec5Snicm 	if (f == NULL) {
15611403328Snicm 		cu_warn("%s", file);
157f391cec5Snicm 		return;
158f391cec5Snicm 	}
159f391cec5Snicm 
160f391cec5Snicm 	while (!feof(f) && !ferror(f)) {
161f391cec5Snicm 		len = fread(buf, 1, sizeof(buf), f);
162f391cec5Snicm 		if (len != 0)
163f391cec5Snicm 			bufferevent_write(line_ev, buf, len);
164f391cec5Snicm 	}
165f391cec5Snicm 
166f391cec5Snicm 	fclose(f);
167f391cec5Snicm 	free(expanded);
168f391cec5Snicm }
169f391cec5Snicm 
170f391cec5Snicm void
send_xmodem(void)171649fc061Snicm send_xmodem(void)
172649fc061Snicm {
173649fc061Snicm 	const char	*file;
174649fc061Snicm 	char		*expanded;
175649fc061Snicm 
176649fc061Snicm 	file = get_input("Local file?");
177649fc061Snicm 	if (file == NULL || *file == '\0')
178649fc061Snicm 		return;
179649fc061Snicm 
180649fc061Snicm 	expanded = tilde_expand(file);
181649fc061Snicm 	xmodem_send(expanded);
182649fc061Snicm 	free(expanded);
183649fc061Snicm }
184649fc061Snicm 
185649fc061Snicm void
set_speed(void)186909d4fcfSnicm set_speed(void)
187909d4fcfSnicm {
188909d4fcfSnicm 	const char	*s, *errstr;
189909d4fcfSnicm 	int		 speed;
190909d4fcfSnicm 
191909d4fcfSnicm 	s = get_input("New speed?");
192909d4fcfSnicm 	if (s == NULL || *s == '\0')
193909d4fcfSnicm 		return;
194909d4fcfSnicm 
195909d4fcfSnicm 	speed = strtonum(s, 0, UINT_MAX, &errstr);
196909d4fcfSnicm 	if (errstr != NULL) {
19711403328Snicm 		cu_warnx("speed is %s: %s", errstr, s);
198909d4fcfSnicm 		return;
199909d4fcfSnicm 	}
200909d4fcfSnicm 
20111403328Snicm 	if (set_line(speed) != 0)
20211403328Snicm 		cu_warn("tcsetattr");
203909d4fcfSnicm }
204909d4fcfSnicm 
205909d4fcfSnicm void
start_record(void)20666d5e211Snicm start_record(void)
20766d5e211Snicm {
20866d5e211Snicm 	const char	*file;
20966d5e211Snicm 
21066d5e211Snicm 	if (record_file != NULL) {
21166d5e211Snicm 		fclose(record_file);
21266d5e211Snicm 		record_file = NULL;
21366d5e211Snicm 	}
21466d5e211Snicm 
21566d5e211Snicm 	file = get_input("Record file?");
21666d5e211Snicm 	if (file == NULL || *file == '\0')
21766d5e211Snicm 		return;
21866d5e211Snicm 
21966d5e211Snicm 	record_file = fopen(file, "a");
22066d5e211Snicm 	if (record_file == NULL)
22166d5e211Snicm 		cu_warnx("%s", file);
22266d5e211Snicm }
22366d5e211Snicm 
22466d5e211Snicm void
do_command(char c)225f391cec5Snicm do_command(char c)
226f391cec5Snicm {
227f3767adaSnicm 	char esc[4 + 1];
228f3767adaSnicm 
229087a5e3bSderaadt 	if (restricted && strchr("CRX$>", c) != NULL) {
230087a5e3bSderaadt 		cu_warnx("~%c command is not allowed in restricted mode", c);
231087a5e3bSderaadt 		return;
232087a5e3bSderaadt 	}
233087a5e3bSderaadt 
234f391cec5Snicm 	switch (c) {
235f391cec5Snicm 	case '.':
236f391cec5Snicm 	case '\004': /* ^D */
237f391cec5Snicm 		event_loopexit(NULL);
238f391cec5Snicm 		break;
239f391cec5Snicm 	case '\032': /* ^Z */
240f391cec5Snicm 		restore_termios();
241f391cec5Snicm 		kill(getpid(), SIGTSTP);
242f391cec5Snicm 		set_termios();
243f391cec5Snicm 		break;
2442001eabaSnicm 	case 'C':
2452001eabaSnicm 		connect_command();
2462001eabaSnicm 		break;
247beec8f30Snicm 	case 'D':
248beec8f30Snicm 		ioctl(line_fd, TIOCCDTR, NULL);
249beec8f30Snicm 		sleep(1);
250beec8f30Snicm 		ioctl(line_fd, TIOCSDTR, NULL);
251beec8f30Snicm 		break;
25266d5e211Snicm 	case 'R':
25366d5e211Snicm 		start_record();
25466d5e211Snicm 		break;
255909d4fcfSnicm 	case 'S':
256909d4fcfSnicm 		set_speed();
257909d4fcfSnicm 		break;
258649fc061Snicm 	case 'X':
259649fc061Snicm 		send_xmodem();
260649fc061Snicm 		break;
261f391cec5Snicm 	case '$':
262f391cec5Snicm 		pipe_command();
263f391cec5Snicm 		break;
264f391cec5Snicm 	case '>':
265f391cec5Snicm 		send_file();
266f391cec5Snicm 		break;
267f391cec5Snicm 	case '#':
268f391cec5Snicm 		ioctl(line_fd, TIOCSBRK, NULL);
269f391cec5Snicm 		sleep(1);
270f391cec5Snicm 		ioctl(line_fd, TIOCCBRK, NULL);
271f391cec5Snicm 		break;
272f3767adaSnicm 	default:
273f3767adaSnicm 		if ((u_char)c == escape_char)
274f3767adaSnicm 			bufferevent_write(line_ev, &c, 1);
27555e67042Snicm 		break;
276f391cec5Snicm 	case '?':
277f3767adaSnicm 		vis(esc, escape_char, VIS_WHITE | VIS_NOSLASH, 0);
278f391cec5Snicm 		printf("\r\n"
279f3767adaSnicm 		    "%s#      send break\r\n"
280f3767adaSnicm 		    "%s$      pipe local command to remote host\r\n"
281f3767adaSnicm 		    "%s>      send file to remote host\r\n"
282f3767adaSnicm 		    "%sC      connect program to remote host\r\n"
283f3767adaSnicm 		    "%sD      de-assert DTR line briefly\r\n"
284f3767adaSnicm 		    "%sR      start recording to file\r\n"
285f3767adaSnicm 		    "%sS      set speed\r\n"
286f3767adaSnicm 		    "%sX      send file with XMODEM\r\n"
287f3767adaSnicm 		    "%s?      get this summary\r\n",
288f3767adaSnicm 		    esc, esc, esc, esc, esc, esc, esc, esc, esc
289909d4fcfSnicm 		);
290f391cec5Snicm 		break;
291f391cec5Snicm 	}
292f391cec5Snicm }
293