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