1 /* $OpenBSD$ */ 2 3 /* 4 * Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org> 5 * Copyright (c) 2009 Nicholas Marriott <nicm@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 16 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 17 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/types.h> 21 #include <sys/wait.h> 22 23 #include <ctype.h> 24 #include <stdlib.h> 25 #include <string.h> 26 27 #include "tmux.h" 28 29 /* 30 * Runs a command without a window. 31 */ 32 33 static enum args_parse_type cmd_run_shell_args_parse(struct args *, u_int, 34 char **); 35 static enum cmd_retval cmd_run_shell_exec(struct cmd *, 36 struct cmdq_item *); 37 38 static void cmd_run_shell_timer(int, short, void *); 39 static void cmd_run_shell_callback(struct job *); 40 static void cmd_run_shell_free(void *); 41 static void cmd_run_shell_print(struct job *, const char *); 42 43 const struct cmd_entry cmd_run_shell_entry = { 44 .name = "run-shell", 45 .alias = "run", 46 47 .args = { "bd:Ct:c:", 0, 2, cmd_run_shell_args_parse }, 48 .usage = "[-bC] [-c start-directory] [-d delay] " CMD_TARGET_PANE_USAGE 49 " [shell-command]", 50 51 .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, 52 53 .flags = 0, 54 .exec = cmd_run_shell_exec 55 }; 56 57 struct cmd_run_shell_data { 58 struct client *client; 59 char *cmd; 60 struct args_command_state *state; 61 char *cwd; 62 struct cmdq_item *item; 63 struct session *s; 64 int wp_id; 65 struct event timer; 66 int flags; 67 }; 68 69 static enum args_parse_type 70 cmd_run_shell_args_parse(struct args *args, __unused u_int idx, 71 __unused char **cause) 72 { 73 if (args_has(args, 'C')) 74 return (ARGS_PARSE_COMMANDS_OR_STRING); 75 return (ARGS_PARSE_STRING); 76 } 77 78 static void 79 cmd_run_shell_print(struct job *job, const char *msg) 80 { 81 struct cmd_run_shell_data *cdata = job_get_data(job); 82 struct window_pane *wp = NULL; 83 struct cmd_find_state fs; 84 struct window_mode_entry *wme; 85 86 if (cdata->wp_id != -1) 87 wp = window_pane_find_by_id(cdata->wp_id); 88 if (wp == NULL) { 89 if (cdata->item != NULL) { 90 cmdq_print(cdata->item, "%s", msg); 91 return; 92 } 93 if (cdata->item != NULL && cdata->client != NULL) 94 wp = server_client_get_pane(cdata->client); 95 if (wp == NULL && cmd_find_from_nothing(&fs, 0) == 0) 96 wp = fs.wp; 97 if (wp == NULL) 98 return; 99 } 100 101 wme = TAILQ_FIRST(&wp->modes); 102 if (wme == NULL || wme->mode != &window_view_mode) 103 window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL); 104 window_copy_add(wp, 1, "%s", msg); 105 } 106 107 static enum cmd_retval 108 cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) 109 { 110 struct args *args = cmd_get_args(self); 111 struct cmd_find_state *target = cmdq_get_target(item); 112 struct cmd_run_shell_data *cdata; 113 struct client *c = cmdq_get_client(item); 114 struct client *tc = cmdq_get_target_client(item); 115 struct session *s = target->s; 116 struct window_pane *wp = target->wp; 117 const char *delay, *cmd; 118 double d; 119 struct timeval tv; 120 char *end; 121 int wait = !args_has(args, 'b'); 122 123 if ((delay = args_get(args, 'd')) != NULL) { 124 d = strtod(delay, &end); 125 if (*end != '\0') { 126 cmdq_error(item, "invalid delay time: %s", delay); 127 return (CMD_RETURN_ERROR); 128 } 129 } else if (args_count(args) == 0) 130 return (CMD_RETURN_NORMAL); 131 132 cdata = xcalloc(1, sizeof *cdata); 133 if (!args_has(args, 'C')) { 134 cmd = args_string(args, 0); 135 if (cmd != NULL) 136 cdata->cmd = format_single_from_target(item, cmd); 137 } else { 138 cdata->state = args_make_commands_prepare(self, item, 0, NULL, 139 wait, 1); 140 } 141 142 if (args_has(args, 't') && wp != NULL) 143 cdata->wp_id = wp->id; 144 else 145 cdata->wp_id = -1; 146 147 if (wait) { 148 cdata->client = c; 149 cdata->item = item; 150 } else { 151 cdata->client = tc; 152 cdata->flags |= JOB_NOWAIT; 153 } 154 if (cdata->client != NULL) 155 cdata->client->references++; 156 if (args_has(args, 'c')) 157 cdata->cwd = xstrdup(args_get(args, 'c')); 158 else 159 cdata->cwd = xstrdup(server_client_get_cwd(c, s)); 160 161 cdata->s = s; 162 if (s != NULL) 163 session_add_ref(s, __func__); 164 165 evtimer_set(&cdata->timer, cmd_run_shell_timer, cdata); 166 if (delay != NULL) { 167 timerclear(&tv); 168 tv.tv_sec = (time_t)d; 169 tv.tv_usec = (d - (double)tv.tv_sec) * 1000000U; 170 evtimer_add(&cdata->timer, &tv); 171 } else 172 event_active(&cdata->timer, EV_TIMEOUT, 1); 173 174 if (!wait) 175 return (CMD_RETURN_NORMAL); 176 return (CMD_RETURN_WAIT); 177 } 178 179 static void 180 cmd_run_shell_timer(__unused int fd, __unused short events, void* arg) 181 { 182 struct cmd_run_shell_data *cdata = arg; 183 struct client *c = cdata->client; 184 const char *cmd = cdata->cmd; 185 struct cmdq_item *item = cdata->item, *new_item; 186 struct cmd_list *cmdlist; 187 char *error; 188 189 if (cdata->state == NULL) { 190 if (cmd == NULL) { 191 if (cdata->item != NULL) 192 cmdq_continue(cdata->item); 193 cmd_run_shell_free(cdata); 194 return; 195 } 196 if (job_run(cmd, 0, NULL, NULL, cdata->s, cdata->cwd, NULL, 197 cmd_run_shell_callback, cmd_run_shell_free, cdata, 198 cdata->flags, -1, -1) == NULL) 199 cmd_run_shell_free(cdata); 200 return; 201 } 202 203 cmdlist = args_make_commands(cdata->state, 0, NULL, &error); 204 if (cmdlist == NULL) { 205 if (cdata->item == NULL) { 206 *error = toupper((u_char)*error); 207 status_message_set(c, -1, 1, 0, "%s", error); 208 } else 209 cmdq_error(cdata->item, "%s", error); 210 free(error); 211 } else if (item == NULL) { 212 new_item = cmdq_get_command(cmdlist, NULL); 213 cmdq_append(c, new_item); 214 } else { 215 new_item = cmdq_get_command(cmdlist, cmdq_get_state(item)); 216 cmdq_insert_after(item, new_item); 217 } 218 219 if (cdata->item != NULL) 220 cmdq_continue(cdata->item); 221 cmd_run_shell_free(cdata); 222 } 223 224 static void 225 cmd_run_shell_callback(struct job *job) 226 { 227 struct cmd_run_shell_data *cdata = job_get_data(job); 228 struct bufferevent *event = job_get_event(job); 229 struct cmdq_item *item = cdata->item; 230 char *cmd = cdata->cmd, *msg = NULL, *line; 231 size_t size; 232 int retcode, status; 233 234 do { 235 line = evbuffer_readln(event->input, NULL, EVBUFFER_EOL_LF); 236 if (line != NULL) { 237 cmd_run_shell_print(job, line); 238 free(line); 239 } 240 } while (line != NULL); 241 242 size = EVBUFFER_LENGTH(event->input); 243 if (size != 0) { 244 line = xmalloc(size + 1); 245 memcpy(line, EVBUFFER_DATA(event->input), size); 246 line[size] = '\0'; 247 248 cmd_run_shell_print(job, line); 249 250 free(line); 251 } 252 253 status = job_get_status(job); 254 if (WIFEXITED(status)) { 255 if ((retcode = WEXITSTATUS(status)) != 0) 256 xasprintf(&msg, "'%s' returned %d", cmd, retcode); 257 } else if (WIFSIGNALED(status)) { 258 retcode = WTERMSIG(status); 259 xasprintf(&msg, "'%s' terminated by signal %d", cmd, retcode); 260 retcode += 128; 261 } else 262 retcode = 0; 263 if (msg != NULL) 264 cmd_run_shell_print(job, msg); 265 free(msg); 266 267 if (item != NULL) { 268 if (cmdq_get_client(item) != NULL && 269 cmdq_get_client(item)->session == NULL) 270 cmdq_get_client(item)->retval = retcode; 271 cmdq_continue(item); 272 } 273 } 274 275 static void 276 cmd_run_shell_free(void *data) 277 { 278 struct cmd_run_shell_data *cdata = data; 279 280 evtimer_del(&cdata->timer); 281 if (cdata->s != NULL) 282 session_remove_ref(cdata->s, __func__); 283 if (cdata->client != NULL) 284 server_client_unref(cdata->client); 285 if (cdata->state != NULL) 286 args_make_commands_free(cdata->state); 287 free(cdata->cwd); 288 free(cdata->cmd); 289 free(cdata); 290 } 291