xref: /openbsd-src/usr.bin/cu/command.c (revision aa997e528a848ca5596493c2a801bdd6fb26ae61)
1 /* $OpenBSD: command.c,v 1.16 2017/12/10 01:03:46 deraadt Exp $ */
2 
3 /*
4  * Copyright (c) 2012 Nicholas Marriott <nicm@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 MIND, USE, DATA OR PROFITS, WHETHER
15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/ioctl.h>
21 #include <sys/wait.h>
22 
23 #include <event.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <limits.h>
27 #include <paths.h>
28 #include <signal.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <unistd.h>
33 
34 #include "cu.h"
35 
36 void	pipe_command(void);
37 void	connect_command(void);
38 void	send_file(void);
39 void	send_xmodem(void);
40 void	set_speed(void);
41 void	start_record(void);
42 
43 void
44 pipe_command(void)
45 {
46 	const char	*cmd;
47 	pid_t		 pid;
48 	int		 fd;
49 
50 	cmd = get_input("Local command?");
51 	if (cmd == NULL || *cmd == '\0')
52 		return;
53 
54 	restore_termios();
55 	set_blocking(line_fd, 1);
56 
57 	switch (pid = fork()) {
58 	case -1:
59 		cu_err(1, "fork");
60 	case 0:
61 		fd = open(_PATH_DEVNULL, O_RDWR);
62 		if (fd < 0 || dup2(fd, STDIN_FILENO) == -1)
63 			_exit(1);
64 		close(fd);
65 
66 		if (signal(SIGINT, SIG_DFL) == SIG_ERR)
67 			_exit(1);
68 		if (signal(SIGQUIT, SIG_DFL) == SIG_ERR)
69 			_exit(1);
70 
71 		/* attach stdout to line */
72 		if (dup2(line_fd, STDOUT_FILENO) == -1)
73 			_exit(1);
74 
75 		if (closefrom(STDERR_FILENO + 1) != 0)
76 			_exit(1);
77 
78 		execl(_PATH_BSHELL, "sh", "-c", cmd, (char *)NULL);
79 		_exit(1);
80 	default:
81 		while (waitpid(pid, NULL, 0) == -1 && errno == EINTR)
82 			/* nothing */;
83 		break;
84 	}
85 
86 	set_blocking(line_fd, 0);
87 	set_termios();
88 }
89 
90 void
91 connect_command(void)
92 {
93 	const char	*cmd;
94 	pid_t		 pid;
95 
96 	/*
97 	 * Fork a program with:
98 	 *  0 <-> remote tty in
99 	 *  1 <-> remote tty out
100 	 *  2 <-> local tty stderr
101 	 */
102 
103 	cmd = get_input("Local command?");
104 	if (cmd == NULL || *cmd == '\0')
105 		return;
106 
107 	restore_termios();
108 	set_blocking(line_fd, 1);
109 
110 	switch (pid = fork()) {
111 	case -1:
112 		cu_err(1, "fork");
113 	case 0:
114 		if (signal(SIGINT, SIG_DFL) == SIG_ERR)
115 			_exit(1);
116 		if (signal(SIGQUIT, SIG_DFL) == SIG_ERR)
117 			_exit(1);
118 
119 		/* attach stdout and stdin to line */
120 		if (dup2(line_fd, STDOUT_FILENO) == -1)
121 			_exit(1);
122 		if (dup2(line_fd, STDIN_FILENO) == -1)
123 			_exit(1);
124 
125 		if (closefrom(STDERR_FILENO + 1) != 0)
126 			_exit(1);
127 
128 		execl(_PATH_BSHELL, "sh", "-c", cmd, (char *)NULL);
129 		_exit(1);
130 	default:
131 		while (waitpid(pid, NULL, 0) == -1 && errno == EINTR)
132 			/* nothing */;
133 		break;
134 	}
135 
136 	set_blocking(line_fd, 0);
137 	set_termios();
138 }
139 
140 void
141 send_file(void)
142 {
143 	const char	*file;
144 	FILE		*f;
145 	char		 buf[BUFSIZ], *expanded;
146 	size_t		 len;
147 
148 	file = get_input("Local file?");
149 	if (file == NULL || *file == '\0')
150 		return;
151 
152 	expanded = tilde_expand(file);
153 	f = fopen(expanded, "r");
154 	if (f == NULL) {
155 		cu_warn("%s", file);
156 		return;
157 	}
158 
159 	while (!feof(f) && !ferror(f)) {
160 		len = fread(buf, 1, sizeof(buf), f);
161 		if (len != 0)
162 			bufferevent_write(line_ev, buf, len);
163 	}
164 
165 	fclose(f);
166 	free(expanded);
167 }
168 
169 void
170 send_xmodem(void)
171 {
172 	const char	*file;
173 	char		*expanded;
174 
175 	file = get_input("Local file?");
176 	if (file == NULL || *file == '\0')
177 		return;
178 
179 	expanded = tilde_expand(file);
180 	xmodem_send(expanded);
181 	free(expanded);
182 }
183 
184 void
185 set_speed(void)
186 {
187 	const char	*s, *errstr;
188 	int		 speed;
189 
190 	s = get_input("New speed?");
191 	if (s == NULL || *s == '\0')
192 		return;
193 
194 	speed = strtonum(s, 0, UINT_MAX, &errstr);
195 	if (errstr != NULL) {
196 		cu_warnx("speed is %s: %s", errstr, s);
197 		return;
198 	}
199 
200 	if (set_line(speed) != 0)
201 		cu_warn("tcsetattr");
202 }
203 
204 void
205 start_record(void)
206 {
207 	const char	*file;
208 
209 	if (record_file != NULL) {
210 		fclose(record_file);
211 		record_file = NULL;
212 	}
213 
214 	file = get_input("Record file?");
215 	if (file == NULL || *file == '\0')
216 		return;
217 
218 	record_file = fopen(file, "a");
219 	if (record_file == NULL)
220 		cu_warnx("%s", file);
221 }
222 
223 void
224 do_command(char c)
225 {
226 	if (restricted && strchr("CRX$>", c) != NULL) {
227 		cu_warnx("~%c command is not allowed in restricted mode", c);
228 		return;
229 	}
230 
231 	switch (c) {
232 	case '.':
233 	case '\004': /* ^D */
234 		event_loopexit(NULL);
235 		break;
236 	case '\032': /* ^Z */
237 		restore_termios();
238 		kill(getpid(), SIGTSTP);
239 		set_termios();
240 		break;
241 	case 'C':
242 		connect_command();
243 		break;
244 	case 'D':
245 		ioctl(line_fd, TIOCCDTR, NULL);
246 		sleep(1);
247 		ioctl(line_fd, TIOCSDTR, NULL);
248 		break;
249 	case 'R':
250 		start_record();
251 		break;
252 	case 'S':
253 		set_speed();
254 		break;
255 	case 'X':
256 		send_xmodem();
257 		break;
258 	case '$':
259 		pipe_command();
260 		break;
261 	case '>':
262 		send_file();
263 		break;
264 	case '#':
265 		ioctl(line_fd, TIOCSBRK, NULL);
266 		sleep(1);
267 		ioctl(line_fd, TIOCCBRK, NULL);
268 		break;
269 	case '~':
270 		bufferevent_write(line_ev, "~", 1);
271 		break;
272 	case '?':
273 		printf("\r\n"
274 		    "~#      send break\r\n"
275 		    "~$      pipe local command to remote host\r\n"
276 		    "~>      send file to remote host\r\n"
277 		    "~C      connect program to remote host\r\n"
278 		    "~D      de-assert DTR line briefly\r\n"
279 		    "~R      start recording to file\r\n"
280 		    "~S      set speed\r\n"
281 		    "~X      send file with XMODEM\r\n"
282 		    "~?      get this summary\r\n"
283 		);
284 		break;
285 	}
286 }
287