4 * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #include <sys/types.h>
20 #include <sys/socket.h>
34 * Open pipe to redirect pane output. If already open, close first.
37 static enum cmd_retval
cmd_pipe_pane_exec(struct cmd
*, struct cmdq_item
*);
39 static void cmd_pipe_pane_read_callback(struct bufferevent
*, void *);
40 static void cmd_pipe_pane_write_callback(struct bufferevent
*, void *);
41 static void cmd_pipe_pane_error_callback(struct bufferevent
*, short, void *);
43 const struct cmd_entry cmd_pipe_pane_entry
= {
47 .args
= { "IOot:", 0, 1, NULL
},
48 .usage
= "[-IOo] " CMD_TARGET_PANE_USAGE
" [shell-command]",
50 .target
= { 't', CMD_FIND_PANE
, 0 },
52 .flags
= CMD_AFTERHOOK
,
53 .exec
= cmd_pipe_pane_exec
56 static enum cmd_retval
57 cmd_pipe_pane_exec(struct cmd
*self
, struct cmdq_item
*item
)
59 struct args
*args
= cmd_get_args(self
);
60 struct cmd_find_state
*target
= cmdq_get_target(item
);
61 struct client
*tc
= cmdq_get_target_client(item
);
62 struct window_pane
*wp
= target
->wp
;
63 struct session
*s
= target
->s
;
64 struct winlink
*wl
= target
->wl
;
65 struct window_pane_offset
*wpo
= &wp
->pipe_offset
;
67 int old_fd
, pipe_fd
[2], null_fd
, in
, out
;
68 struct format_tree
*ft
;
71 /* Do nothing if pane is dead. */
72 if (wp
->fd
== -1 || (wp
->flags
& PANE_EXITED
)) {
73 cmdq_error(item
, "target pane has exited");
74 return (CMD_RETURN_ERROR
);
77 /* Destroy the old pipe. */
79 if (wp
->pipe_fd
!= -1) {
80 bufferevent_free(wp
->pipe_event
);
84 if (window_pane_destroy_ready(wp
)) {
85 server_destroy_pane(wp
, 1);
86 return (CMD_RETURN_NORMAL
);
90 /* If no pipe command, that is enough. */
91 if (args_count(args
) == 0 || *args_string(args
, 0) == '\0')
92 return (CMD_RETURN_NORMAL
);
95 * With -o, only open the new pipe if there was no previous one. This
96 * allows a pipe to be toggled with a single key, for example:
98 * bind ^p pipep -o 'cat >>~/output'
100 if (args_has(args
, 'o') && old_fd
!= -1)
101 return (CMD_RETURN_NORMAL
);
103 /* What do we want to do? Neither -I or -O is -O. */
104 if (args_has(args
, 'I')) {
106 out
= args_has(args
, 'O');
112 /* Open the new pipe. */
113 if (socketpair(AF_UNIX
, SOCK_STREAM
, PF_UNSPEC
, pipe_fd
) != 0) {
114 cmdq_error(item
, "socketpair error: %s", strerror(errno
));
115 return (CMD_RETURN_ERROR
);
118 /* Expand the command. */
119 ft
= format_create(cmdq_get_client(item
), item
, FORMAT_NONE
, 0);
120 format_defaults(ft
, tc
, s
, wl
, wp
);
121 cmd
= format_expand_time(ft
, args_string(args
, 0));
124 /* Fork the child. */
126 sigprocmask(SIG_BLOCK
, &set
, &oldset
);
129 sigprocmask(SIG_SETMASK
, &oldset
, NULL
);
130 cmdq_error(item
, "fork error: %s", strerror(errno
));
133 return (CMD_RETURN_ERROR
);
136 proc_clear_signals(server_proc
, 1);
137 sigprocmask(SIG_SETMASK
, &oldset
, NULL
);
140 null_fd
= open(_PATH_DEVNULL
, O_WRONLY
);
142 if (dup2(pipe_fd
[1], STDIN_FILENO
) == -1)
145 if (dup2(null_fd
, STDIN_FILENO
) == -1)
149 if (dup2(pipe_fd
[1], STDOUT_FILENO
) == -1)
151 if (pipe_fd
[1] != STDOUT_FILENO
)
154 if (dup2(null_fd
, STDOUT_FILENO
) == -1)
157 if (dup2(null_fd
, STDERR_FILENO
) == -1)
159 closefrom(STDERR_FILENO
+ 1);
161 execl(_PATH_BSHELL
, "sh", "-c", cmd
, (char *) NULL
);
164 /* Parent process. */
165 sigprocmask(SIG_SETMASK
, &oldset
, NULL
);
168 wp
->pipe_fd
= pipe_fd
[0];
169 memcpy(wpo
, &wp
->offset
, sizeof *wpo
);
171 setblocking(wp
->pipe_fd
, 0);
172 wp
->pipe_event
= bufferevent_new(wp
->pipe_fd
,
173 cmd_pipe_pane_read_callback
,
174 cmd_pipe_pane_write_callback
,
175 cmd_pipe_pane_error_callback
,
177 if (wp
->pipe_event
== NULL
)
178 fatalx("out of memory");
180 bufferevent_enable(wp
->pipe_event
, EV_WRITE
);
182 bufferevent_enable(wp
->pipe_event
, EV_READ
);
185 return (CMD_RETURN_NORMAL
);
190 cmd_pipe_pane_read_callback(__unused
struct bufferevent
*bufev
, void *data
)
192 struct window_pane
*wp
= data
;
193 struct evbuffer
*evb
= wp
->pipe_event
->input
;
196 available
= EVBUFFER_LENGTH(evb
);
197 log_debug("%%%u pipe read %zu", wp
->id
, available
);
199 bufferevent_write(wp
->event
, EVBUFFER_DATA(evb
), available
);
200 evbuffer_drain(evb
, available
);
202 if (window_pane_destroy_ready(wp
))
203 server_destroy_pane(wp
, 1);
207 cmd_pipe_pane_write_callback(__unused
struct bufferevent
*bufev
, void *data
)
209 struct window_pane
*wp
= data
;
211 log_debug("%%%u pipe empty", wp
->id
);
213 if (window_pane_destroy_ready(wp
))
214 server_destroy_pane(wp
, 1);
218 cmd_pipe_pane_error_callback(__unused
struct bufferevent
*bufev
,
219 __unused
short what
, void *data
)
221 struct window_pane
*wp
= data
;
223 log_debug("%%%u pipe error", wp
->id
);
225 bufferevent_free(wp
->pipe_event
);
229 if (window_pane_destroy_ready(wp
))
230 server_destroy_pane(wp
, 1);