1 /* $OpenBSD: cmd-if-shell.c,v 1.60 2018/08/27 11:03:34 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 * Executes a tmux command if a shell command returns true or false. 30 */ 31 32 static enum cmd_retval cmd_if_shell_exec(struct cmd *, struct cmdq_item *); 33 34 static void cmd_if_shell_callback(struct job *); 35 static void cmd_if_shell_free(void *); 36 37 const struct cmd_entry cmd_if_shell_entry = { 38 .name = "if-shell", 39 .alias = "if", 40 41 .args = { "bFt:", 2, 3 }, 42 .usage = "[-bF] " CMD_TARGET_PANE_USAGE " shell-command command " 43 "[command]", 44 45 .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, 46 47 .flags = 0, 48 .exec = cmd_if_shell_exec 49 }; 50 51 struct cmd_if_shell_data { 52 char *file; 53 u_int line; 54 55 char *cmd_if; 56 char *cmd_else; 57 58 struct client *client; 59 struct cmdq_item *item; 60 struct mouse_event mouse; 61 }; 62 63 static enum cmd_retval 64 cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) 65 { 66 struct args *args = self->args; 67 struct cmdq_shared *shared = item->shared; 68 struct cmd_if_shell_data *cdata; 69 char *shellcmd, *cmd, *cause; 70 struct cmd_list *cmdlist; 71 struct cmdq_item *new_item; 72 struct client *c = cmd_find_client(item, NULL, 1); 73 struct session *s = item->target.s; 74 struct winlink *wl = item->target.wl; 75 struct window_pane *wp = item->target.wp; 76 77 shellcmd = format_single(item, args->argv[0], c, s, wl, wp); 78 if (args_has(args, 'F')) { 79 cmd = NULL; 80 if (*shellcmd != '0' && *shellcmd != '\0') 81 cmd = args->argv[1]; 82 else if (args->argc == 3) 83 cmd = args->argv[2]; 84 free(shellcmd); 85 if (cmd == NULL) 86 return (CMD_RETURN_NORMAL); 87 cmdlist = cmd_string_parse(cmd, NULL, 0, &cause); 88 if (cmdlist == NULL) { 89 if (cause != NULL) { 90 cmdq_error(item, "%s", cause); 91 free(cause); 92 } 93 return (CMD_RETURN_ERROR); 94 } 95 new_item = cmdq_get_command(cmdlist, NULL, &shared->mouse, 0); 96 cmdq_insert_after(item, new_item); 97 cmd_list_free(cmdlist); 98 return (CMD_RETURN_NORMAL); 99 } 100 101 cdata = xcalloc(1, sizeof *cdata); 102 if (self->file != NULL) { 103 cdata->file = xstrdup(self->file); 104 cdata->line = self->line; 105 } 106 107 cdata->cmd_if = xstrdup(args->argv[1]); 108 if (args->argc == 3) 109 cdata->cmd_else = xstrdup(args->argv[2]); 110 else 111 cdata->cmd_else = NULL; 112 113 cdata->client = item->client; 114 if (cdata->client != NULL) 115 cdata->client->references++; 116 117 if (!args_has(args, 'b')) 118 cdata->item = item; 119 else 120 cdata->item = NULL; 121 memcpy(&cdata->mouse, &shared->mouse, sizeof cdata->mouse); 122 123 if (job_run(shellcmd, s, server_client_get_cwd(item->client, s), NULL, 124 cmd_if_shell_callback, cmd_if_shell_free, cdata, 0) == NULL) { 125 cmdq_error(item, "failed to run command: %s", shellcmd); 126 free(shellcmd); 127 free(cdata); 128 return (CMD_RETURN_ERROR); 129 } 130 free(shellcmd); 131 132 if (args_has(args, 'b')) 133 return (CMD_RETURN_NORMAL); 134 return (CMD_RETURN_WAIT); 135 } 136 137 static void 138 cmd_if_shell_callback(struct job *job) 139 { 140 struct cmd_if_shell_data *cdata = job_get_data(job); 141 struct client *c = cdata->client; 142 struct cmd_list *cmdlist; 143 struct cmdq_item *new_item; 144 char *cause, *cmd, *file = cdata->file; 145 u_int line = cdata->line; 146 int status; 147 148 status = job_get_status(job); 149 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) 150 cmd = cdata->cmd_else; 151 else 152 cmd = cdata->cmd_if; 153 if (cmd == NULL) 154 goto out; 155 156 cmdlist = cmd_string_parse(cmd, file, line, &cause); 157 if (cmdlist == NULL) { 158 if (cause != NULL && cdata->item != NULL) 159 cmdq_error(cdata->item, "%s", cause); 160 free(cause); 161 new_item = NULL; 162 } else { 163 new_item = cmdq_get_command(cmdlist, NULL, &cdata->mouse, 0); 164 cmd_list_free(cmdlist); 165 } 166 167 if (new_item != NULL) { 168 if (cdata->item == NULL) 169 cmdq_append(c, new_item); 170 else 171 cmdq_insert_after(cdata->item, new_item); 172 } 173 174 out: 175 if (cdata->item != NULL) 176 cdata->item->flags &= ~CMDQ_WAITING; 177 } 178 179 static void 180 cmd_if_shell_free(void *data) 181 { 182 struct cmd_if_shell_data *cdata = data; 183 184 if (cdata->client != NULL) 185 server_client_unref(cdata->client); 186 187 free(cdata->cmd_else); 188 free(cdata->cmd_if); 189 190 free(cdata->file); 191 free(cdata); 192 } 193