1 | /* $OpenBSD$ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2009 Tiago Cunha <[email protected]> |
5 | * Copyright (c) 2009 Nicholas Marriott <[email protected]> |
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 | |