1 /* $Id: cmd-pipe-pane.c,v 1.1.1.1 2011/03/10 09:15:37 jmmv Exp $ */ 2 3 /* 4 * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net> 5 * 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. 9 * 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. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/socket.h> 21 22 #include <errno.h> 23 #include <fcntl.h> 24 #include <string.h> 25 #include <time.h> 26 #include <unistd.h> 27 28 #include "tmux.h" 29 30 /* 31 * Open pipe to redirect pane output. If already open, close first. 32 */ 33 34 int cmd_pipe_pane_exec(struct cmd *, struct cmd_ctx *); 35 36 void cmd_pipe_pane_error_callback(struct bufferevent *, short, void *); 37 38 const struct cmd_entry cmd_pipe_pane_entry = { 39 "pipe-pane", "pipep", 40 CMD_TARGET_PANE_USAGE "[-o] [command]", 41 CMD_ARG01, "o", 42 cmd_target_init, 43 cmd_target_parse, 44 cmd_pipe_pane_exec, 45 cmd_target_free, 46 cmd_target_print 47 }; 48 49 int 50 cmd_pipe_pane_exec(struct cmd *self, struct cmd_ctx *ctx) 51 { 52 struct cmd_target_data *data = self->data; 53 struct client *c; 54 struct window_pane *wp; 55 char *command; 56 int old_fd, pipe_fd[2], null_fd, mode; 57 58 if ((c = cmd_find_client(ctx, NULL)) == NULL) 59 return (-1); 60 61 if (cmd_find_pane(ctx, data->target, NULL, &wp) == NULL) 62 return (-1); 63 64 /* Destroy the old pipe. */ 65 old_fd = wp->pipe_fd; 66 if (wp->pipe_fd != -1) { 67 bufferevent_free(wp->pipe_event); 68 close(wp->pipe_fd); 69 wp->pipe_fd = -1; 70 } 71 72 /* If no pipe command, that is enough. */ 73 if (data->arg == NULL || *data->arg == '\0') 74 return (0); 75 76 /* 77 * With -o, only open the new pipe if there was no previous one. This 78 * allows a pipe to be toggled with a single key, for example: 79 * 80 * bind ^p pipep -o 'cat >>~/output' 81 */ 82 if (cmd_check_flag(data->chflags, 'o') && old_fd != -1) 83 return (0); 84 85 /* Open the new pipe. */ 86 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_fd) != 0) { 87 ctx->error(ctx, "socketpair error: %s", strerror(errno)); 88 return (-1); 89 } 90 91 /* Fork the child. */ 92 switch (fork()) { 93 case -1: 94 ctx->error(ctx, "fork error: %s", strerror(errno)); 95 return (-1); 96 case 0: 97 /* Child process. */ 98 close(pipe_fd[0]); 99 clear_signals(1); 100 101 if (dup2(pipe_fd[1], STDIN_FILENO) == -1) 102 _exit(1); 103 if (pipe_fd[1] != STDIN_FILENO) 104 close(pipe_fd[1]); 105 106 null_fd = open(_PATH_DEVNULL, O_WRONLY, 0); 107 if (dup2(null_fd, STDOUT_FILENO) == -1) 108 _exit(1); 109 if (dup2(null_fd, STDERR_FILENO) == -1) 110 _exit(1); 111 if (null_fd != STDOUT_FILENO && null_fd != STDERR_FILENO) 112 close(null_fd); 113 114 closefrom(STDERR_FILENO + 1); 115 116 command = status_replace(c, NULL, data->arg, time(NULL), 0); 117 execl(_PATH_BSHELL, "sh", "-c", command, (char *) NULL); 118 _exit(1); 119 default: 120 /* Parent process. */ 121 close(pipe_fd[1]); 122 123 wp->pipe_fd = pipe_fd[0]; 124 wp->pipe_off = EVBUFFER_LENGTH(wp->event->input); 125 126 wp->pipe_event = bufferevent_new(wp->pipe_fd, 127 NULL, NULL, cmd_pipe_pane_error_callback, wp); 128 bufferevent_enable(wp->pipe_event, EV_WRITE); 129 130 if ((mode = fcntl(wp->pipe_fd, F_GETFL)) == -1) 131 fatal("fcntl failed"); 132 if (fcntl(wp->pipe_fd, F_SETFL, mode|O_NONBLOCK) == -1) 133 fatal("fcntl failed"); 134 return (0); 135 } 136 } 137 138 /* ARGSUSED */ 139 void 140 cmd_pipe_pane_error_callback( 141 unused struct bufferevent *bufev, unused short what, void *data) 142 { 143 struct window_pane *wp = data; 144 145 bufferevent_free(wp->pipe_event); 146 close(wp->pipe_fd); 147 wp->pipe_fd = -1; 148 } 149