xref: /openbsd-src/usr.bin/tmux/cmd-if-shell.c (revision a2d98599e3c69439489c0bfafa67f0ebef23661b)
1 /* $OpenBSD: cmd-if-shell.c,v 1.82 2021/09/09 13:38:32 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 args_parse_type	cmd_if_shell_args_parse(struct args *, u_int,
33 				    char **);
34 static enum cmd_retval		cmd_if_shell_exec(struct cmd *,
35 				    struct cmdq_item *);
36 
37 static void	cmd_if_shell_callback(struct job *);
38 static void	cmd_if_shell_free(void *);
39 
40 const struct cmd_entry cmd_if_shell_entry = {
41 	.name = "if-shell",
42 	.alias = "if",
43 
44 	.args = { "bFt:", 2, 3, cmd_if_shell_args_parse },
45 	.usage = "[-bF] " CMD_TARGET_PANE_USAGE " shell-command command "
46 		 "[command]",
47 
48 	.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
49 
50 	.flags = 0,
51 	.exec = cmd_if_shell_exec
52 };
53 
54 struct cmd_if_shell_data {
55 	struct cmd_list		*cmd_if;
56 	struct cmd_list		*cmd_else;
57 
58 	struct client		*client;
59 	struct cmdq_item	*item;
60 };
61 
62 static enum args_parse_type
63 cmd_if_shell_args_parse(__unused struct args *args, u_int idx,
64     __unused char **cause)
65 {
66 	if (idx == 1 || idx == 2)
67 		return (ARGS_PARSE_COMMANDS_OR_STRING);
68 	return (ARGS_PARSE_STRING);
69 }
70 
71 static enum cmd_retval
72 cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
73 {
74 	struct args			*args = cmd_get_args(self);
75 	struct cmd_find_state		*target = cmdq_get_target(item);
76 	struct cmd_if_shell_data	*cdata;
77 	struct cmdq_item		*new_item;
78 	char				*shellcmd;
79 	struct client			*tc = cmdq_get_target_client(item);
80 	struct session			*s = target->s;
81 	struct cmd_list			*cmdlist = NULL;
82 	u_int				 count = args_count(args);
83 
84 	shellcmd = format_single_from_target(item, args_string(args, 0));
85 	if (args_has(args, 'F')) {
86 		if (*shellcmd != '0' && *shellcmd != '\0')
87 			cmdlist = args_make_commands_now(self, item, 1, 0);
88 		else if (count == 3)
89 			cmdlist = args_make_commands_now(self, item, 2, 0);
90 		else {
91 			free(shellcmd);
92 			return (CMD_RETURN_NORMAL);
93 		}
94 		free(shellcmd);
95 		if (cmdlist == NULL)
96 			return (CMD_RETURN_ERROR);
97 		new_item = cmdq_get_command(cmdlist, cmdq_get_state(item));
98 		cmdq_insert_after(item, new_item);
99 		return (CMD_RETURN_NORMAL);
100 	}
101 
102 	cdata = xcalloc(1, sizeof *cdata);
103 
104 	cdata->cmd_if = args_make_commands_now(self, item, 1, 0);
105 	if (cdata->cmd_if == NULL)
106 	    return (CMD_RETURN_ERROR);
107 	if (count == 3) {
108 		cdata->cmd_else = args_make_commands_now(self, item, 2, 0);
109 		if (cdata->cmd_else == NULL)
110 		    return (CMD_RETURN_ERROR);
111 	}
112 
113 	if (!args_has(args, 'b'))
114 		cdata->client = cmdq_get_client(item);
115 	else
116 		cdata->client = tc;
117 	if (cdata->client != NULL)
118 		cdata->client->references++;
119 
120 	if (!args_has(args, 'b'))
121 		cdata->item = item;
122 
123 	if (job_run(shellcmd, 0, NULL, s,
124 	    server_client_get_cwd(cmdq_get_client(item), s), NULL,
125 	    cmd_if_shell_callback, cmd_if_shell_free, cdata, 0, -1,
126 	    -1) == NULL) {
127 		cmdq_error(item, "failed to run command: %s", shellcmd);
128 		free(shellcmd);
129 		free(cdata);
130 		return (CMD_RETURN_ERROR);
131 	}
132 	free(shellcmd);
133 
134 	if (args_has(args, 'b'))
135 		return (CMD_RETURN_NORMAL);
136 	return (CMD_RETURN_WAIT);
137 }
138 
139 static void
140 cmd_if_shell_callback(struct job *job)
141 {
142 	struct cmd_if_shell_data	*cdata = job_get_data(job);
143 	struct client			*c = cdata->client;
144 	struct cmdq_item		*item = cdata->item, *new_item;
145 	struct cmd_list			*cmdlist;
146 	int				 status;
147 
148 	status = job_get_status(job);
149 	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
150 		cmdlist = cdata->cmd_else;
151 	else
152 		cmdlist = cdata->cmd_if;
153 	if (cmdlist == NULL)
154 		goto out;
155 
156 	if (item == NULL) {
157 		new_item = cmdq_get_command(cmdlist, NULL);
158 		cmdq_append(c, new_item);
159 	} else {
160 		new_item = cmdq_get_command(cmdlist, cmdq_get_state(item));
161 		cmdq_insert_after(item, new_item);
162 	}
163 
164 out:
165 	if (cdata->item != NULL)
166 		cmdq_continue(cdata->item);
167 }
168 
169 static void
170 cmd_if_shell_free(void *data)
171 {
172 	struct cmd_if_shell_data	*cdata = data;
173 
174 	if (cdata->client != NULL)
175 		server_client_unref(cdata->client);
176 
177 	if (cdata->cmd_else != NULL)
178 		cmd_list_free(cdata->cmd_else);
179 	cmd_list_free(cdata->cmd_if);
180 
181 	free(cdata);
182 }
183