Home | History | Annotate | Line # | Download | only in dist
cmd-run-shell.c revision 1.1.1.8.4.3
      1 /* $OpenBSD$ */
      2 
      3 /*
      4  * Copyright (c) 2009 Tiago Cunha <me (at) tiagocunha.org>
      5  * Copyright (c) 2009 Nicholas Marriott <nicm (at) 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  * Runs a command without a window.
     30  */
     31 
     32 static enum cmd_retval	cmd_run_shell_exec(struct cmd *, struct cmdq_item *);
     33 
     34 static void	cmd_run_shell_callback(struct job *);
     35 static void	cmd_run_shell_free(void *);
     36 static void	cmd_run_shell_print(struct job *, const char *);
     37 
     38 const struct cmd_entry cmd_run_shell_entry = {
     39 	.name = "run-shell",
     40 	.alias = "run",
     41 
     42 	.args = { "bt:", 1, 1 },
     43 	.usage = "[-b] " CMD_TARGET_PANE_USAGE " shell-command",
     44 
     45 	.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
     46 
     47 	.flags = 0,
     48 	.exec = cmd_run_shell_exec
     49 };
     50 
     51 struct cmd_run_shell_data {
     52 	char			*cmd;
     53 	struct cmdq_item	*item;
     54 	int			 wp_id;
     55 };
     56 
     57 static void
     58 cmd_run_shell_print(struct job *job, const char *msg)
     59 {
     60 	struct cmd_run_shell_data	*cdata = job_get_data(job);
     61 	struct window_pane		*wp = NULL;
     62 	struct cmd_find_state		 fs;
     63 	struct window_mode_entry	*wme;
     64 
     65 	if (cdata->wp_id != -1)
     66 		wp = window_pane_find_by_id(cdata->wp_id);
     67 	if (wp == NULL) {
     68 		if (cdata->item != NULL) {
     69 			cmdq_print(cdata->item, "%s", msg);
     70 			return;
     71 		}
     72 		if (cmd_find_from_nothing(&fs, 0) != 0)
     73 			return;
     74 		wp = fs.wp;
     75 		if (wp == NULL)
     76 			return;
     77 	}
     78 
     79 	wme = TAILQ_FIRST(&wp->modes);
     80 	if (wme == NULL || wme->mode != &window_view_mode)
     81 		window_pane_set_mode(wp, &window_view_mode, NULL, NULL);
     82 	window_copy_add(wp, "%s", msg);
     83 }
     84 
     85 static enum cmd_retval
     86 cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
     87 {
     88 	struct args			*args = self->args;
     89 	struct cmd_run_shell_data	*cdata;
     90 	struct client			*c = cmd_find_client(item, NULL, 1);
     91 	struct session			*s = item->target.s;
     92 	struct winlink			*wl = item->target.wl;
     93 	struct window_pane		*wp = item->target.wp;
     94 
     95 	cdata = xcalloc(1, sizeof *cdata);
     96 	cdata->cmd = format_single(item, args->argv[0], c, s, wl, wp);
     97 
     98 	if (args_has(args, 't') && wp != NULL)
     99 		cdata->wp_id = wp->id;
    100 	else
    101 		cdata->wp_id = -1;
    102 
    103 	if (!args_has(args, 'b'))
    104 		cdata->item = item;
    105 
    106 	if (job_run(cdata->cmd, s, server_client_get_cwd(item->client, s), NULL,
    107 	    cmd_run_shell_callback, cmd_run_shell_free, cdata, 0) == NULL) {
    108 		cmdq_error(item, "failed to run command: %s", cdata->cmd);
    109 		free(cdata);
    110 		return (CMD_RETURN_ERROR);
    111 	}
    112 
    113 	if (args_has(args, 'b'))
    114 		return (CMD_RETURN_NORMAL);
    115 	return (CMD_RETURN_WAIT);
    116 }
    117 
    118 static void
    119 cmd_run_shell_callback(struct job *job)
    120 {
    121 	struct cmd_run_shell_data	*cdata = job_get_data(job);
    122 	struct bufferevent		*event = job_get_event(job);
    123 	char				*cmd = cdata->cmd, *msg = NULL, *line;
    124 	size_t				 size;
    125 	int				 retcode, status;
    126 
    127 	do {
    128 		if ((line = evbuffer_readline(event->input)) != NULL) {
    129 			cmd_run_shell_print(job, line);
    130 			free(line);
    131 		}
    132 	} while (line != NULL);
    133 
    134 	size = EVBUFFER_LENGTH(event->input);
    135 	if (size != 0) {
    136 		line = xmalloc(size + 1);
    137 		memcpy(line, EVBUFFER_DATA(event->input), size);
    138 		line[size] = '\0';
    139 
    140 		cmd_run_shell_print(job, line);
    141 
    142 		free(line);
    143 	}
    144 
    145 	status = job_get_status(job);
    146 	if (WIFEXITED(status)) {
    147 		if ((retcode = WEXITSTATUS(status)) != 0)
    148 			xasprintf(&msg, "'%s' returned %d", cmd, retcode);
    149 	} else if (WIFSIGNALED(status)) {
    150 		retcode = WTERMSIG(status);
    151 		xasprintf(&msg, "'%s' terminated by signal %d", cmd, retcode);
    152 	}
    153 	if (msg != NULL)
    154 		cmd_run_shell_print(job, msg);
    155 	free(msg);
    156 
    157 	if (cdata->item != NULL)
    158 		cmdq_continue(cdata->item);
    159 }
    160 
    161 static void
    162 cmd_run_shell_free(void *data)
    163 {
    164 	struct cmd_run_shell_data	*cdata = data;
    165 
    166 	free(cdata->cmd);
    167 	free(cdata);
    168 }
    169