1 /* $OpenBSD: cmd-if-shell.c,v 1.74 2020/04/13 20:51:57 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 struct cmd_parse_input input; 53 54 char *cmd_if; 55 char *cmd_else; 56 57 struct client *client; 58 struct cmdq_item *item; 59 }; 60 61 static enum cmd_retval 62 cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) 63 { 64 struct args *args = cmd_get_args(self); 65 struct cmd_find_state *target = cmdq_get_target(item); 66 struct cmdq_state *state = cmdq_get_state(item); 67 struct cmd_if_shell_data *cdata; 68 char *shellcmd, *cmd, *error; 69 const char *file; 70 struct client *tc = cmdq_get_target_client(item); 71 struct session *s = target->s; 72 struct cmd_parse_input pi; 73 enum cmd_parse_status status; 74 75 shellcmd = format_single_from_target(item, args->argv[0]); 76 if (args_has(args, 'F')) { 77 if (*shellcmd != '0' && *shellcmd != '\0') 78 cmd = args->argv[1]; 79 else if (args->argc == 3) 80 cmd = args->argv[2]; 81 else 82 cmd = NULL; 83 free(shellcmd); 84 if (cmd == NULL) 85 return (CMD_RETURN_NORMAL); 86 87 memset(&pi, 0, sizeof pi); 88 cmd_get_source(self, &pi.file, &pi.line); 89 pi.item = item; 90 pi.c = tc; 91 cmd_find_copy_state(&pi.fs, target); 92 93 status = cmd_parse_and_insert(cmd, &pi, item, state, &error); 94 if (status == CMD_PARSE_ERROR) { 95 cmdq_error(item, "%s", error); 96 free(error); 97 return (CMD_RETURN_ERROR); 98 } 99 return (CMD_RETURN_NORMAL); 100 } 101 102 cdata = xcalloc(1, sizeof *cdata); 103 104 cdata->cmd_if = xstrdup(args->argv[1]); 105 if (args->argc == 3) 106 cdata->cmd_else = xstrdup(args->argv[2]); 107 else 108 cdata->cmd_else = NULL; 109 110 if (!args_has(args, 'b')) 111 cdata->client = cmdq_get_client(item); 112 else 113 cdata->client = tc; 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 122 memset(&cdata->input, 0, sizeof cdata->input); 123 cmd_get_source(self, &file, &cdata->input.line); 124 if (file != NULL) 125 cdata->input.file = xstrdup(file); 126 cdata->input.c = tc; 127 if (cdata->input.c != NULL) 128 cdata->input.c->references++; 129 cmd_find_copy_state(&cdata->input.fs, target); 130 131 if (job_run(shellcmd, s, 132 server_client_get_cwd(cmdq_get_client(item), s), NULL, 133 cmd_if_shell_callback, cmd_if_shell_free, cdata, 0, -1, 134 -1) == NULL) { 135 cmdq_error(item, "failed to run command: %s", shellcmd); 136 free(shellcmd); 137 free(cdata); 138 return (CMD_RETURN_ERROR); 139 } 140 free(shellcmd); 141 142 if (args_has(args, 'b')) 143 return (CMD_RETURN_NORMAL); 144 return (CMD_RETURN_WAIT); 145 } 146 147 static void 148 cmd_if_shell_callback(struct job *job) 149 { 150 struct cmd_if_shell_data *cdata = job_get_data(job); 151 struct client *c = cdata->client; 152 struct cmdq_item *new_item = NULL; 153 struct cmdq_state *new_state = NULL; 154 char *cmd; 155 int status; 156 struct cmd_parse_result *pr; 157 158 status = job_get_status(job); 159 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) 160 cmd = cdata->cmd_else; 161 else 162 cmd = cdata->cmd_if; 163 if (cmd == NULL) 164 goto out; 165 166 pr = cmd_parse_from_string(cmd, &cdata->input); 167 switch (pr->status) { 168 case CMD_PARSE_EMPTY: 169 break; 170 case CMD_PARSE_ERROR: 171 if (cdata->item != NULL) 172 cmdq_error(cdata->item, "%s", pr->error); 173 free(pr->error); 174 break; 175 case CMD_PARSE_SUCCESS: 176 if (cdata->item == NULL) 177 new_state = cmdq_new_state(NULL, NULL, 0); 178 else 179 new_state = cmdq_get_state(cdata->item); 180 new_item = cmdq_get_command(pr->cmdlist, new_state); 181 if (cdata->item == NULL) 182 cmdq_free_state(new_state); 183 cmd_list_free(pr->cmdlist); 184 break; 185 } 186 if (new_item != NULL) { 187 if (cdata->item == NULL) 188 cmdq_append(c, new_item); 189 else 190 cmdq_insert_after(cdata->item, new_item); 191 } 192 193 out: 194 if (cdata->item != NULL) 195 cmdq_continue(cdata->item); 196 } 197 198 static void 199 cmd_if_shell_free(void *data) 200 { 201 struct cmd_if_shell_data *cdata = data; 202 203 if (cdata->client != NULL) 204 server_client_unref(cdata->client); 205 206 free(cdata->cmd_else); 207 free(cdata->cmd_if); 208 209 if (cdata->input.c != NULL) 210 server_client_unref(cdata->input.c); 211 free((void *)cdata->input.file); 212 213 free(cdata); 214 } 215