15494e770Schristos /* $OpenBSD$ */ 2698d5317Sjmmv 3698d5317Sjmmv /* 4698d5317Sjmmv * Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org> 5698d5317Sjmmv * Copyright (c) 2009 Nicholas Marriott <nicm@openbsd.org> 6698d5317Sjmmv * 7698d5317Sjmmv * Permission to use, copy, modify, and distribute this software for any 8698d5317Sjmmv * purpose with or without fee is hereby granted, provided that the above 9698d5317Sjmmv * copyright notice and this permission notice appear in all copies. 10698d5317Sjmmv * 11698d5317Sjmmv * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12698d5317Sjmmv * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13698d5317Sjmmv * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14698d5317Sjmmv * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15698d5317Sjmmv * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 16698d5317Sjmmv * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 17698d5317Sjmmv * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18698d5317Sjmmv */ 19698d5317Sjmmv 20698d5317Sjmmv #include <sys/types.h> 21698d5317Sjmmv #include <sys/wait.h> 22698d5317Sjmmv 239fb66d81Schristos #include <ctype.h> 24928fc495Schristos #include <stdlib.h> 25698d5317Sjmmv #include <string.h> 26698d5317Sjmmv 27698d5317Sjmmv #include "tmux.h" 28698d5317Sjmmv 29698d5317Sjmmv /* 30698d5317Sjmmv * Runs a command without a window. 31698d5317Sjmmv */ 32698d5317Sjmmv 336db26757Swiz static enum args_parse_type cmd_run_shell_args_parse(struct args *, u_int, 346db26757Swiz char **); 356db26757Swiz static enum cmd_retval cmd_run_shell_exec(struct cmd *, 366db26757Swiz struct cmdq_item *); 37698d5317Sjmmv 389fb66d81Schristos static void cmd_run_shell_timer(int, short, void *); 394e179ddaSchristos static void cmd_run_shell_callback(struct job *); 404e179ddaSchristos static void cmd_run_shell_free(void *); 414e179ddaSchristos static void cmd_run_shell_print(struct job *, const char *); 42698d5317Sjmmv 43698d5317Sjmmv const struct cmd_entry cmd_run_shell_entry = { 44ed4e6cd4Schristos .name = "run-shell", 45ed4e6cd4Schristos .alias = "run", 46ed4e6cd4Schristos 47c23f9150Swiz .args = { "bd:Ct:c:", 0, 2, cmd_run_shell_args_parse }, 48c23f9150Swiz .usage = "[-bC] [-c start-directory] [-d delay] " CMD_TARGET_PANE_USAGE 49c23f9150Swiz " [shell-command]", 50ed4e6cd4Schristos 51c9ad075bSchristos .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, 52ed4e6cd4Schristos 53ed4e6cd4Schristos .flags = 0, 54ed4e6cd4Schristos .exec = cmd_run_shell_exec 55698d5317Sjmmv }; 56698d5317Sjmmv 57698d5317Sjmmv struct cmd_run_shell_data { 589fb66d81Schristos struct client *client; 59698d5317Sjmmv char *cmd; 606db26757Swiz struct args_command_state *state; 619fb66d81Schristos char *cwd; 624e179ddaSchristos struct cmdq_item *item; 639fb66d81Schristos struct session *s; 64928fc495Schristos int wp_id; 659fb66d81Schristos struct event timer; 669fb66d81Schristos int flags; 67698d5317Sjmmv }; 68698d5317Sjmmv 696db26757Swiz static enum args_parse_type 706db26757Swiz cmd_run_shell_args_parse(struct args *args, __unused u_int idx, 716db26757Swiz __unused char **cause) 726db26757Swiz { 736db26757Swiz if (args_has(args, 'C')) 746db26757Swiz return (ARGS_PARSE_COMMANDS_OR_STRING); 756db26757Swiz return (ARGS_PARSE_STRING); 766db26757Swiz } 776db26757Swiz 784e179ddaSchristos static void 79928fc495Schristos cmd_run_shell_print(struct job *job, const char *msg) 80928fc495Schristos { 81ef36e747Schristos struct cmd_run_shell_data *cdata = job_get_data(job); 82928fc495Schristos struct window_pane *wp = NULL; 834e179ddaSchristos struct cmd_find_state fs; 84ef36e747Schristos struct window_mode_entry *wme; 85928fc495Schristos 86928fc495Schristos if (cdata->wp_id != -1) 87928fc495Schristos wp = window_pane_find_by_id(cdata->wp_id); 88*f8cf1a91Swiz if (wp == NULL) { 89*f8cf1a91Swiz if (cdata->item != NULL) { 90*f8cf1a91Swiz cmdq_print(cdata->item, "%s", msg); 91*f8cf1a91Swiz return; 92*f8cf1a91Swiz } 93*f8cf1a91Swiz if (cdata->item != NULL && cdata->client != NULL) 946db26757Swiz wp = server_client_get_pane(cdata->client); 956db26757Swiz if (wp == NULL && cmd_find_from_nothing(&fs, 0) == 0) 964e179ddaSchristos wp = fs.wp; 974e179ddaSchristos if (wp == NULL) 98928fc495Schristos return; 99*f8cf1a91Swiz } 100928fc495Schristos 101ef36e747Schristos wme = TAILQ_FIRST(&wp->modes); 102ef36e747Schristos if (wme == NULL || wme->mode != &window_view_mode) 1039fb66d81Schristos window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL); 1046db26757Swiz window_copy_add(wp, 1, "%s", msg); 105928fc495Schristos } 106928fc495Schristos 1074e179ddaSchristos static enum cmd_retval 1084e179ddaSchristos cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) 109698d5317Sjmmv { 1109fb66d81Schristos struct args *args = cmd_get_args(self); 1119fb66d81Schristos struct cmd_find_state *target = cmdq_get_target(item); 112698d5317Sjmmv struct cmd_run_shell_data *cdata; 113c23f9150Swiz struct client *c = cmdq_get_client(item); 1149fb66d81Schristos struct client *tc = cmdq_get_target_client(item); 1159fb66d81Schristos struct session *s = target->s; 1169fb66d81Schristos struct window_pane *wp = target->wp; 1176db26757Swiz const char *delay, *cmd; 1189fb66d81Schristos double d; 1199fb66d81Schristos struct timeval tv; 1209fb66d81Schristos char *end; 1219fb66d81Schristos int wait = !args_has(args, 'b'); 1229fb66d81Schristos 1239fb66d81Schristos if ((delay = args_get(args, 'd')) != NULL) { 1249fb66d81Schristos d = strtod(delay, &end); 1259fb66d81Schristos if (*end != '\0') { 1269fb66d81Schristos cmdq_error(item, "invalid delay time: %s", delay); 1279fb66d81Schristos return (CMD_RETURN_ERROR); 1289fb66d81Schristos } 1296db26757Swiz } else if (args_count(args) == 0) 1309fb66d81Schristos return (CMD_RETURN_NORMAL); 131698d5317Sjmmv 1324e179ddaSchristos cdata = xcalloc(1, sizeof *cdata); 1336db26757Swiz if (!args_has(args, 'C')) { 1346db26757Swiz cmd = args_string(args, 0); 1356db26757Swiz if (cmd != NULL) 1366db26757Swiz cdata->cmd = format_single_from_target(item, cmd); 1376db26757Swiz } else { 1386db26757Swiz cdata->state = args_make_commands_prepare(self, item, 0, NULL, 1396db26757Swiz wait, 1); 1409fb66d81Schristos } 141698d5317Sjmmv 1424e179ddaSchristos if (args_has(args, 't') && wp != NULL) 1434e179ddaSchristos cdata->wp_id = wp->id; 1444e179ddaSchristos else 1454e179ddaSchristos cdata->wp_id = -1; 146698d5317Sjmmv 1479fb66d81Schristos if (wait) { 148c23f9150Swiz cdata->client = c; 1494e179ddaSchristos cdata->item = item; 1509fb66d81Schristos } else { 1519fb66d81Schristos cdata->client = tc; 1529fb66d81Schristos cdata->flags |= JOB_NOWAIT; 153ef36e747Schristos } 1549fb66d81Schristos if (cdata->client != NULL) 1559fb66d81Schristos cdata->client->references++; 156c23f9150Swiz if (args_has(args, 'c')) 157c23f9150Swiz cdata->cwd = xstrdup(args_get(args, 'c')); 158c23f9150Swiz else 159c23f9150Swiz cdata->cwd = xstrdup(server_client_get_cwd(c, s)); 1609fb66d81Schristos 1619fb66d81Schristos cdata->s = s; 1629fb66d81Schristos if (s != NULL) 1639fb66d81Schristos session_add_ref(s, __func__); 1649fb66d81Schristos 1659fb66d81Schristos evtimer_set(&cdata->timer, cmd_run_shell_timer, cdata); 1669fb66d81Schristos if (delay != NULL) { 1679fb66d81Schristos timerclear(&tv); 1689fb66d81Schristos tv.tv_sec = (time_t)d; 1699fb66d81Schristos tv.tv_usec = (d - (double)tv.tv_sec) * 1000000U; 1709fb66d81Schristos evtimer_add(&cdata->timer, &tv); 1719fb66d81Schristos } else 1729fb66d81Schristos event_active(&cdata->timer, EV_TIMEOUT, 1); 1739fb66d81Schristos 1749fb66d81Schristos if (!wait) 175928fc495Schristos return (CMD_RETURN_NORMAL); 176928fc495Schristos return (CMD_RETURN_WAIT); 177698d5317Sjmmv } 178698d5317Sjmmv 1794e179ddaSchristos static void 1809fb66d81Schristos cmd_run_shell_timer(__unused int fd, __unused short events, void* arg) 1819fb66d81Schristos { 1829fb66d81Schristos struct cmd_run_shell_data *cdata = arg; 1839fb66d81Schristos struct client *c = cdata->client; 1849fb66d81Schristos const char *cmd = cdata->cmd; 1856db26757Swiz struct cmdq_item *item = cdata->item, *new_item; 1866db26757Swiz struct cmd_list *cmdlist; 1879fb66d81Schristos char *error; 1889fb66d81Schristos 1896db26757Swiz if (cdata->state == NULL) { 1906db26757Swiz if (cmd == NULL) { 1916db26757Swiz if (cdata->item != NULL) 1926db26757Swiz cmdq_continue(cdata->item); 1936db26757Swiz cmd_run_shell_free(cdata); 1946db26757Swiz return; 1956db26757Swiz } 1966db26757Swiz if (job_run(cmd, 0, NULL, NULL, cdata->s, cdata->cwd, NULL, 1979fb66d81Schristos cmd_run_shell_callback, cmd_run_shell_free, cdata, 1989fb66d81Schristos cdata->flags, -1, -1) == NULL) 1999fb66d81Schristos cmd_run_shell_free(cdata); 2009fb66d81Schristos return; 2019fb66d81Schristos } 2029fb66d81Schristos 2036db26757Swiz cmdlist = args_make_commands(cdata->state, 0, NULL, &error); 2046db26757Swiz if (cmdlist == NULL) { 2059fb66d81Schristos if (cdata->item == NULL) { 2069fb66d81Schristos *error = toupper((u_char)*error); 2079fb66d81Schristos status_message_set(c, -1, 1, 0, "%s", error); 2089fb66d81Schristos } else 2099fb66d81Schristos cmdq_error(cdata->item, "%s", error); 2109fb66d81Schristos free(error); 2116db26757Swiz } else if (item == NULL) { 2126db26757Swiz new_item = cmdq_get_command(cmdlist, NULL); 2136db26757Swiz cmdq_append(c, new_item); 2146db26757Swiz } else { 2156db26757Swiz new_item = cmdq_get_command(cmdlist, cmdq_get_state(item)); 2166db26757Swiz cmdq_insert_after(item, new_item); 2179fb66d81Schristos } 2189fb66d81Schristos 2199fb66d81Schristos if (cdata->item != NULL) 2209fb66d81Schristos cmdq_continue(cdata->item); 2219fb66d81Schristos cmd_run_shell_free(cdata); 2229fb66d81Schristos } 2239fb66d81Schristos 2249fb66d81Schristos static void 225698d5317Sjmmv cmd_run_shell_callback(struct job *job) 226698d5317Sjmmv { 227ef36e747Schristos struct cmd_run_shell_data *cdata = job_get_data(job); 228ef36e747Schristos struct bufferevent *event = job_get_event(job); 2299fb66d81Schristos struct cmdq_item *item = cdata->item; 230ef36e747Schristos char *cmd = cdata->cmd, *msg = NULL, *line; 231698d5317Sjmmv size_t size; 232ef36e747Schristos int retcode, status; 233698d5317Sjmmv 234698d5317Sjmmv do { 2356db26757Swiz line = evbuffer_readln(event->input, NULL, EVBUFFER_EOL_LF); 2366db26757Swiz if (line != NULL) { 237928fc495Schristos cmd_run_shell_print(job, line); 238928fc495Schristos free(line); 239698d5317Sjmmv } 240698d5317Sjmmv } while (line != NULL); 241698d5317Sjmmv 242ef36e747Schristos size = EVBUFFER_LENGTH(event->input); 243698d5317Sjmmv if (size != 0) { 244698d5317Sjmmv line = xmalloc(size + 1); 245ef36e747Schristos memcpy(line, EVBUFFER_DATA(event->input), size); 246698d5317Sjmmv line[size] = '\0'; 247698d5317Sjmmv 248928fc495Schristos cmd_run_shell_print(job, line); 249698d5317Sjmmv 250928fc495Schristos free(line); 251698d5317Sjmmv } 252698d5317Sjmmv 253ef36e747Schristos status = job_get_status(job); 254ef36e747Schristos if (WIFEXITED(status)) { 255ef36e747Schristos if ((retcode = WEXITSTATUS(status)) != 0) 256698d5317Sjmmv xasprintf(&msg, "'%s' returned %d", cmd, retcode); 257ef36e747Schristos } else if (WIFSIGNALED(status)) { 258ef36e747Schristos retcode = WTERMSIG(status); 259698d5317Sjmmv xasprintf(&msg, "'%s' terminated by signal %d", cmd, retcode); 2609fb66d81Schristos retcode += 128; 2619fb66d81Schristos } else 2629fb66d81Schristos retcode = 0; 2635494e770Schristos if (msg != NULL) 264928fc495Schristos cmd_run_shell_print(job, msg); 265928fc495Schristos free(msg); 2664e179ddaSchristos 2679fb66d81Schristos if (item != NULL) { 2689fb66d81Schristos if (cmdq_get_client(item) != NULL && 2699fb66d81Schristos cmdq_get_client(item)->session == NULL) 2709fb66d81Schristos cmdq_get_client(item)->retval = retcode; 2719fb66d81Schristos cmdq_continue(item); 2729fb66d81Schristos } 273698d5317Sjmmv } 274698d5317Sjmmv 2754e179ddaSchristos static void 276698d5317Sjmmv cmd_run_shell_free(void *data) 277698d5317Sjmmv { 278698d5317Sjmmv struct cmd_run_shell_data *cdata = data; 279698d5317Sjmmv 2809fb66d81Schristos evtimer_del(&cdata->timer); 2819fb66d81Schristos if (cdata->s != NULL) 2829fb66d81Schristos session_remove_ref(cdata->s, __func__); 2839fb66d81Schristos if (cdata->client != NULL) 2849fb66d81Schristos server_client_unref(cdata->client); 2856db26757Swiz if (cdata->state != NULL) 2866db26757Swiz args_make_commands_free(cdata->state); 2879fb66d81Schristos free(cdata->cwd); 288928fc495Schristos free(cdata->cmd); 289928fc495Schristos free(cdata); 290698d5317Sjmmv } 291