xref: /netbsd-src/external/bsd/tmux/dist/cmd-run-shell.c (revision f8cf1a9151c7af1cb0bd8b09c13c66bca599c027)
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