1 /* $OpenBSD: cmd-run-shell.c,v 1.69 2020/06/12 10:31:12 nicm Exp $ */ 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 <stdlib.h> 24 #include <string.h> 25 26 #include "tmux.h" 27 28 /* 29 * Runs a command without a window. 30 */ 31 32 static enum cmd_retval cmd_run_shell_exec(struct cmd *, struct cmdq_item *); 33 34 static void cmd_run_shell_timer(int, short, void *); 35 static void cmd_run_shell_callback(struct job *); 36 static void cmd_run_shell_free(void *); 37 static void cmd_run_shell_print(struct job *, const char *); 38 39 const struct cmd_entry cmd_run_shell_entry = { 40 .name = "run-shell", 41 .alias = "run", 42 43 .args = { "bd:t:", 0, 1 }, 44 .usage = "[-b] [-d delay] " CMD_TARGET_PANE_USAGE " [shell-command]", 45 46 .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, 47 48 .flags = 0, 49 .exec = cmd_run_shell_exec 50 }; 51 52 struct cmd_run_shell_data { 53 char *cmd; 54 char *cwd; 55 struct cmdq_item *item; 56 struct session *s; 57 int wp_id; 58 struct event timer; 59 int flags; 60 }; 61 62 static void 63 cmd_run_shell_print(struct job *job, const char *msg) 64 { 65 struct cmd_run_shell_data *cdata = job_get_data(job); 66 struct window_pane *wp = NULL; 67 struct cmd_find_state fs; 68 struct window_mode_entry *wme; 69 70 if (cdata->wp_id != -1) 71 wp = window_pane_find_by_id(cdata->wp_id); 72 if (wp == NULL) { 73 if (cdata->item != NULL) { 74 cmdq_print(cdata->item, "%s", msg); 75 return; 76 } 77 if (cmd_find_from_nothing(&fs, 0) != 0) 78 return; 79 wp = fs.wp; 80 if (wp == NULL) 81 return; 82 } 83 84 wme = TAILQ_FIRST(&wp->modes); 85 if (wme == NULL || wme->mode != &window_view_mode) 86 window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL); 87 window_copy_add(wp, "%s", msg); 88 } 89 90 static enum cmd_retval 91 cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) 92 { 93 struct args *args = cmd_get_args(self); 94 struct cmd_find_state *target = cmdq_get_target(item); 95 struct cmd_run_shell_data *cdata; 96 struct session *s = target->s; 97 struct window_pane *wp = target->wp; 98 const char *delay; 99 double d; 100 struct timeval tv; 101 char *end; 102 103 cdata = xcalloc(1, sizeof *cdata); 104 if (args->argc != 0) 105 cdata->cmd = format_single_from_target(item, args->argv[0]); 106 107 if (args_has(args, 't') && wp != NULL) 108 cdata->wp_id = wp->id; 109 else 110 cdata->wp_id = -1; 111 112 if (!args_has(args, 'b')) 113 cdata->item = item; 114 else 115 cdata->flags |= JOB_NOWAIT; 116 117 cdata->cwd = xstrdup(server_client_get_cwd(cmdq_get_client(item), s)); 118 cdata->s = s; 119 if (s != NULL) 120 session_add_ref(s, __func__); 121 122 evtimer_set(&cdata->timer, cmd_run_shell_timer, cdata); 123 124 if ((delay = args_get(args, 'd')) != NULL) { 125 d = strtod(delay, &end); 126 if (*end != '\0') { 127 cmdq_error(item, "invalid delay time: %s", delay); 128 cmd_run_shell_free(cdata); 129 return (CMD_RETURN_ERROR); 130 } 131 timerclear(&tv); 132 tv.tv_sec = (time_t)d; 133 tv.tv_usec = (d - (double)tv.tv_sec) * 1000000U; 134 evtimer_add(&cdata->timer, &tv); 135 } else 136 cmd_run_shell_timer(-1, 0, cdata); 137 138 if (args_has(args, 'b')) 139 return (CMD_RETURN_NORMAL); 140 return (CMD_RETURN_WAIT); 141 } 142 143 static void 144 cmd_run_shell_timer(__unused int fd, __unused short events, void* arg) 145 { 146 struct cmd_run_shell_data *cdata = arg; 147 148 if (cdata->cmd != NULL) { 149 if (job_run(cdata->cmd, cdata->s, cdata->cwd, NULL, 150 cmd_run_shell_callback, cmd_run_shell_free, cdata, 151 cdata->flags, -1, -1) == NULL) 152 cmd_run_shell_free(cdata); 153 } else { 154 if (cdata->item != NULL) 155 cmdq_continue(cdata->item); 156 cmd_run_shell_free(cdata); 157 } 158 } 159 160 static void 161 cmd_run_shell_callback(struct job *job) 162 { 163 struct cmd_run_shell_data *cdata = job_get_data(job); 164 struct bufferevent *event = job_get_event(job); 165 struct cmdq_item *item = cdata->item; 166 char *cmd = cdata->cmd, *msg = NULL, *line; 167 size_t size; 168 int retcode, status; 169 170 do { 171 if ((line = evbuffer_readline(event->input)) != NULL) { 172 cmd_run_shell_print(job, line); 173 free(line); 174 } 175 } while (line != NULL); 176 177 size = EVBUFFER_LENGTH(event->input); 178 if (size != 0) { 179 line = xmalloc(size + 1); 180 memcpy(line, EVBUFFER_DATA(event->input), size); 181 line[size] = '\0'; 182 183 cmd_run_shell_print(job, line); 184 185 free(line); 186 } 187 188 status = job_get_status(job); 189 if (WIFEXITED(status)) { 190 if ((retcode = WEXITSTATUS(status)) != 0) 191 xasprintf(&msg, "'%s' returned %d", cmd, retcode); 192 } else if (WIFSIGNALED(status)) { 193 retcode = WTERMSIG(status); 194 xasprintf(&msg, "'%s' terminated by signal %d", cmd, retcode); 195 retcode += 128; 196 } else 197 retcode = 0; 198 if (msg != NULL) 199 cmd_run_shell_print(job, msg); 200 free(msg); 201 202 if (item != NULL) { 203 if (cmdq_get_client(item) != NULL && 204 cmdq_get_client(item)->session == NULL) 205 cmdq_get_client(item)->retval = retcode; 206 cmdq_continue(item); 207 } 208 } 209 210 static void 211 cmd_run_shell_free(void *data) 212 { 213 struct cmd_run_shell_data *cdata = data; 214 215 evtimer_del(&cdata->timer); 216 if (cdata->s != NULL) 217 session_remove_ref(cdata->s, __func__); 218 free(cdata->cwd); 219 free(cdata->cmd); 220 free(cdata); 221 } 222