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