mirror of
https://github.com/tmux/tmux.git
synced 2025-01-06 07:48:48 +00:00
Merge branch 'obsd-master'
This commit is contained in:
commit
7922f4ee7b
@ -132,6 +132,7 @@ dist_tmux_SOURCES = \
|
||||
control-notify.c \
|
||||
control.c \
|
||||
environ.c \
|
||||
file.c \
|
||||
format.c \
|
||||
format-draw.c \
|
||||
grid-view.c \
|
||||
|
46
cfg.c
46
cfg.c
@ -194,6 +194,52 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags,
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
load_cfg_from_buffer(const void *buf, size_t len, const char *path,
|
||||
struct client *c, struct cmdq_item *item, int flags,
|
||||
struct cmdq_item **new_item)
|
||||
{
|
||||
struct cmd_parse_input pi;
|
||||
struct cmd_parse_result *pr;
|
||||
struct cmdq_item *new_item0;
|
||||
|
||||
if (new_item != NULL)
|
||||
*new_item = NULL;
|
||||
|
||||
log_debug("loading %s", path);
|
||||
|
||||
memset(&pi, 0, sizeof pi);
|
||||
pi.flags = flags;
|
||||
pi.file = path;
|
||||
pi.line = 1;
|
||||
pi.item = item;
|
||||
pi.c = c;
|
||||
|
||||
pr = cmd_parse_from_buffer(buf, len, &pi);
|
||||
if (pr->status == CMD_PARSE_EMPTY)
|
||||
return (0);
|
||||
if (pr->status == CMD_PARSE_ERROR) {
|
||||
cfg_add_cause("%s", pr->error);
|
||||
free(pr->error);
|
||||
return (-1);
|
||||
}
|
||||
if (flags & CMD_PARSE_PARSEONLY) {
|
||||
cmd_list_free(pr->cmdlist);
|
||||
return (0);
|
||||
}
|
||||
|
||||
new_item0 = cmdq_get_command(pr->cmdlist, NULL, NULL, 0);
|
||||
if (item != NULL)
|
||||
cmdq_insert_after(item, new_item0);
|
||||
else
|
||||
cmdq_append(NULL, new_item0);
|
||||
cmd_list_free(pr->cmdlist);
|
||||
|
||||
if (new_item != NULL)
|
||||
*new_item = new_item0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
cfg_add_cause(const char *fmt, ...)
|
||||
{
|
||||
|
332
client.c
332
client.c
@ -35,7 +35,6 @@
|
||||
static struct tmuxproc *client_proc;
|
||||
static struct tmuxpeer *client_peer;
|
||||
static int client_flags;
|
||||
static struct event client_stdin;
|
||||
static enum {
|
||||
CLIENT_EXIT_NONE,
|
||||
CLIENT_EXIT_DETACHED,
|
||||
@ -52,13 +51,12 @@ static const char *client_exitsession;
|
||||
static const char *client_execshell;
|
||||
static const char *client_execcmd;
|
||||
static int client_attached;
|
||||
static struct client_files client_files = RB_INITIALIZER(&client_files);
|
||||
|
||||
static __dead void client_exec(const char *,const char *);
|
||||
static int client_get_lock(char *);
|
||||
static int client_connect(struct event_base *, const char *, int);
|
||||
static void client_send_identify(const char *, const char *);
|
||||
static void client_stdin_callback(int, short, void *);
|
||||
static void client_write(int, const char *, size_t);
|
||||
static void client_signal(int);
|
||||
static void client_dispatch(struct imsg *, void *);
|
||||
static void client_dispatch_attached(struct imsg *);
|
||||
@ -217,7 +215,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags)
|
||||
{
|
||||
struct cmd_parse_result *pr;
|
||||
struct cmd *cmd;
|
||||
struct msg_command_data *data;
|
||||
struct msg_command *data;
|
||||
int cmdflags, fd, i;
|
||||
const char *ttynam, *cwd;
|
||||
pid_t ppid;
|
||||
@ -291,7 +289,9 @@ client_main(struct event_base *base, int argc, char **argv, int flags)
|
||||
*
|
||||
* "sendfd" is dropped later in client_dispatch_wait().
|
||||
*/
|
||||
if (pledge("stdio unix sendfd proc exec tty", NULL) != 0)
|
||||
if (pledge(
|
||||
"stdio rpath wpath cpath unix sendfd proc exec tty",
|
||||
NULL) != 0)
|
||||
fatal("pledge failed");
|
||||
|
||||
/* Free stuff that is not used in the client. */
|
||||
@ -302,10 +302,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags)
|
||||
options_free(global_w_options);
|
||||
environ_free(global_environ);
|
||||
|
||||
/* Create stdin handler. */
|
||||
setblocking(STDIN_FILENO, 0);
|
||||
event_set(&client_stdin, STDIN_FILENO, EV_READ|EV_PERSIST,
|
||||
client_stdin_callback, NULL);
|
||||
/* Set up control mode. */
|
||||
if (client_flags & CLIENT_CONTROLCONTROL) {
|
||||
if (tcgetattr(STDIN_FILENO, &saved_tio) != 0) {
|
||||
fprintf(stderr, "tcgetattr failed: %s\n",
|
||||
@ -428,41 +425,236 @@ client_send_identify(const char *ttynam, const char *cwd)
|
||||
proc_send(client_peer, MSG_IDENTIFY_DONE, -1, NULL, 0);
|
||||
}
|
||||
|
||||
/* Callback for client stdin read events. */
|
||||
/* File write error callback. */
|
||||
static void
|
||||
client_stdin_callback(__unused int fd, __unused short events,
|
||||
__unused void *arg)
|
||||
client_write_error_callback(__unused struct bufferevent *bev,
|
||||
__unused short what, void *arg)
|
||||
{
|
||||
struct msg_stdin_data data;
|
||||
struct client_file *cf = arg;
|
||||
|
||||
data.size = read(STDIN_FILENO, data.data, sizeof data.data);
|
||||
if (data.size == -1 && (errno == EINTR || errno == EAGAIN))
|
||||
return;
|
||||
log_debug("write error file %d", cf->stream);
|
||||
|
||||
proc_send(client_peer, MSG_STDIN, -1, &data, sizeof data);
|
||||
if (data.size <= 0)
|
||||
event_del(&client_stdin);
|
||||
bufferevent_free(cf->event);
|
||||
cf->event = NULL;
|
||||
|
||||
close(cf->fd);
|
||||
cf->fd = -1;
|
||||
}
|
||||
|
||||
/* Force write to file descriptor. */
|
||||
/* File write callback. */
|
||||
static void
|
||||
client_write(int fd, const char *data, size_t size)
|
||||
client_write_callback(__unused struct bufferevent *bev, void *arg)
|
||||
{
|
||||
ssize_t used;
|
||||
struct client_file *cf = arg;
|
||||
|
||||
log_debug("%s: %.*s", __func__, (int)size, data);
|
||||
while (size != 0) {
|
||||
used = write(fd, data, size);
|
||||
if (used == -1) {
|
||||
if (errno == EINTR || errno == EAGAIN)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
data += used;
|
||||
size -= used;
|
||||
if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0) {
|
||||
bufferevent_free(cf->event);
|
||||
close(cf->fd);
|
||||
RB_REMOVE(client_files, &client_files, cf);
|
||||
file_free(cf);
|
||||
}
|
||||
}
|
||||
|
||||
/* Open write file. */
|
||||
static void
|
||||
client_write_open(void *data, size_t datalen)
|
||||
{
|
||||
struct msg_write_open *msg = data;
|
||||
struct msg_write_ready reply;
|
||||
struct client_file find, *cf;
|
||||
const int flags = O_NONBLOCK|O_WRONLY|O_CREAT;
|
||||
int error = 0;
|
||||
|
||||
if (datalen != sizeof *msg)
|
||||
fatalx("bad MSG_WRITE_OPEN size");
|
||||
log_debug("open write file %d %s", msg->stream, msg->path);
|
||||
|
||||
find.stream = msg->stream;
|
||||
if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) {
|
||||
cf = file_create(NULL, msg->stream, NULL, NULL);
|
||||
RB_INSERT(client_files, &client_files, cf);
|
||||
} else {
|
||||
error = EBADF;
|
||||
goto reply;
|
||||
}
|
||||
if (cf->closed) {
|
||||
error = EBADF;
|
||||
goto reply;
|
||||
}
|
||||
|
||||
cf->fd = -1;
|
||||
if (msg->fd == -1)
|
||||
cf->fd = open(msg->path, msg->flags|flags, 0644);
|
||||
else {
|
||||
if (msg->fd != STDOUT_FILENO && msg->fd != STDERR_FILENO)
|
||||
errno = EBADF;
|
||||
else {
|
||||
cf->fd = dup(msg->fd);
|
||||
if (client_flags & CLIENT_CONTROL)
|
||||
close(msg->fd); /* can only be used once */
|
||||
}
|
||||
}
|
||||
if (cf->fd == -1) {
|
||||
error = errno;
|
||||
goto reply;
|
||||
}
|
||||
|
||||
cf->event = bufferevent_new(cf->fd, NULL, client_write_callback,
|
||||
client_write_error_callback, cf);
|
||||
bufferevent_enable(cf->event, EV_WRITE);
|
||||
goto reply;
|
||||
|
||||
reply:
|
||||
reply.stream = msg->stream;
|
||||
reply.error = error;
|
||||
proc_send(client_peer, MSG_WRITE_READY, -1, &reply, sizeof reply);
|
||||
}
|
||||
|
||||
/* Write to client file. */
|
||||
static void
|
||||
client_write_data(void *data, size_t datalen)
|
||||
{
|
||||
struct msg_write_data *msg = data;
|
||||
struct client_file find, *cf;
|
||||
|
||||
if (datalen != sizeof *msg)
|
||||
fatalx("bad MSG_WRITE size");
|
||||
find.stream = msg->stream;
|
||||
if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL)
|
||||
fatalx("unknown stream number");
|
||||
log_debug("write %zu to file %d", msg->size, cf->stream);
|
||||
|
||||
if (cf->event != NULL)
|
||||
bufferevent_write(cf->event, msg->data, msg->size);
|
||||
}
|
||||
|
||||
/* Close client file. */
|
||||
static void
|
||||
client_write_close(void *data, size_t datalen)
|
||||
{
|
||||
struct msg_write_close *msg = data;
|
||||
struct client_file find, *cf;
|
||||
|
||||
if (datalen != sizeof *msg)
|
||||
fatalx("bad MSG_WRITE_CLOSE size");
|
||||
find.stream = msg->stream;
|
||||
if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL)
|
||||
fatalx("unknown stream number");
|
||||
log_debug("close file %d", cf->stream);
|
||||
|
||||
if (cf->event == NULL || EVBUFFER_LENGTH(cf->event->output) == 0) {
|
||||
if (cf->event != NULL)
|
||||
bufferevent_free(cf->event);
|
||||
if (cf->fd != -1)
|
||||
close(cf->fd);
|
||||
RB_REMOVE(client_files, &client_files, cf);
|
||||
file_free(cf);
|
||||
}
|
||||
}
|
||||
|
||||
/* File read callback. */
|
||||
static void
|
||||
client_read_callback(__unused struct bufferevent *bev, void *arg)
|
||||
{
|
||||
struct client_file *cf = arg;
|
||||
void *bdata;
|
||||
size_t bsize;
|
||||
struct msg_read_data msg;
|
||||
|
||||
for (;;) {
|
||||
bdata = EVBUFFER_DATA(cf->event->input);
|
||||
bsize = EVBUFFER_LENGTH(cf->event->input);
|
||||
|
||||
if (bsize == 0)
|
||||
break;
|
||||
if (bsize > sizeof msg.data)
|
||||
bsize = sizeof msg.data;
|
||||
log_debug("read %zu from file %d", bsize, cf->stream);
|
||||
|
||||
memcpy(msg.data, bdata, bsize);
|
||||
msg.size = bsize;
|
||||
|
||||
msg.stream = cf->stream;
|
||||
proc_send(client_peer, MSG_READ, -1, &msg, sizeof msg);
|
||||
|
||||
evbuffer_drain(cf->event->input, bsize);
|
||||
}
|
||||
}
|
||||
|
||||
/* File read error callback. */
|
||||
static void
|
||||
client_read_error_callback(__unused struct bufferevent *bev,
|
||||
__unused short what, void *arg)
|
||||
{
|
||||
struct client_file *cf = arg;
|
||||
struct msg_read_done msg;
|
||||
|
||||
log_debug("read error file %d", cf->stream);
|
||||
|
||||
msg.stream = cf->stream;
|
||||
msg.error = 0;
|
||||
proc_send(client_peer, MSG_READ_DONE, -1, &msg, sizeof msg);
|
||||
|
||||
bufferevent_free(cf->event);
|
||||
close(cf->fd);
|
||||
RB_REMOVE(client_files, &client_files, cf);
|
||||
file_free(cf);
|
||||
}
|
||||
|
||||
/* Open read file. */
|
||||
static void
|
||||
client_read_open(void *data, size_t datalen)
|
||||
{
|
||||
struct msg_read_open *msg = data;
|
||||
struct msg_read_done reply;
|
||||
struct client_file find, *cf;
|
||||
const int flags = O_NONBLOCK|O_RDONLY;
|
||||
int error = 0;
|
||||
|
||||
if (datalen != sizeof *msg)
|
||||
fatalx("bad MSG_READ_OPEN size");
|
||||
log_debug("open read file %d %s", msg->stream, msg->path);
|
||||
|
||||
find.stream = msg->stream;
|
||||
if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) {
|
||||
cf = file_create(NULL, msg->stream, NULL, NULL);
|
||||
RB_INSERT(client_files, &client_files, cf);
|
||||
} else {
|
||||
error = EBADF;
|
||||
goto reply;
|
||||
}
|
||||
if (cf->closed) {
|
||||
error = EBADF;
|
||||
goto reply;
|
||||
}
|
||||
|
||||
cf->fd = -1;
|
||||
if (msg->fd == -1)
|
||||
cf->fd = open(msg->path, flags);
|
||||
else {
|
||||
if (msg->fd != STDIN_FILENO)
|
||||
errno = EBADF;
|
||||
else {
|
||||
cf->fd = dup(msg->fd);
|
||||
close(msg->fd); /* can only be used once */
|
||||
}
|
||||
}
|
||||
if (cf->fd == -1) {
|
||||
error = errno;
|
||||
goto reply;
|
||||
}
|
||||
|
||||
cf->event = bufferevent_new(cf->fd, client_read_callback, NULL,
|
||||
client_read_error_callback, cf);
|
||||
bufferevent_enable(cf->event, EV_READ);
|
||||
return;
|
||||
|
||||
reply:
|
||||
reply.stream = msg->stream;
|
||||
reply.error = error;
|
||||
proc_send(client_peer, MSG_READ_DONE, -1, &reply, sizeof reply);
|
||||
}
|
||||
|
||||
/* Run command in shell; used for -c. */
|
||||
static __dead void
|
||||
client_exec(const char *shell, const char *shellcmd)
|
||||
@ -534,6 +726,29 @@ client_signal(int sig)
|
||||
}
|
||||
}
|
||||
|
||||
/* Exit if all streams flushed. */
|
||||
static void
|
||||
client_exit(__unused int fd, __unused short events, __unused void *arg)
|
||||
{
|
||||
struct client_file *cf;
|
||||
size_t left;
|
||||
int waiting = 0;
|
||||
|
||||
RB_FOREACH (cf, client_files, &client_files) {
|
||||
if (cf->event == NULL)
|
||||
continue;
|
||||
left = EVBUFFER_LENGTH(cf->event->output);
|
||||
if (left != 0) {
|
||||
waiting++;
|
||||
log_debug("file %u %zu bytes left", cf->stream, left);
|
||||
}
|
||||
}
|
||||
if (waiting == 0)
|
||||
proc_exit(client_proc);
|
||||
else
|
||||
event_once(-1, EV_TIMEOUT, client_exit, NULL, NULL);
|
||||
}
|
||||
|
||||
/* Callback for client read events. */
|
||||
static void
|
||||
client_dispatch(struct imsg *imsg, __unused void *arg)
|
||||
@ -555,12 +770,10 @@ client_dispatch(struct imsg *imsg, __unused void *arg)
|
||||
static void
|
||||
client_dispatch_wait(struct imsg *imsg)
|
||||
{
|
||||
char *data;
|
||||
ssize_t datalen;
|
||||
struct msg_stdout_data stdoutdata;
|
||||
struct msg_stderr_data stderrdata;
|
||||
int retval;
|
||||
static int pledge_applied;
|
||||
char *data;
|
||||
ssize_t datalen;
|
||||
int retval;
|
||||
static int pledge_applied;
|
||||
|
||||
/*
|
||||
* "sendfd" is no longer required once all of the identify messages
|
||||
@ -569,10 +782,12 @@ client_dispatch_wait(struct imsg *imsg)
|
||||
* get the first message from the server.
|
||||
*/
|
||||
if (!pledge_applied) {
|
||||
if (pledge("stdio unix proc exec tty", NULL) != 0)
|
||||
if (pledge(
|
||||
"stdio rpath wpath cpath unix proc exec tty",
|
||||
NULL) != 0)
|
||||
fatal("pledge failed");
|
||||
pledge_applied = 1;
|
||||
};
|
||||
}
|
||||
|
||||
data = imsg->data;
|
||||
datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
|
||||
@ -586,38 +801,15 @@ client_dispatch_wait(struct imsg *imsg)
|
||||
memcpy(&retval, data, sizeof retval);
|
||||
client_exitval = retval;
|
||||
}
|
||||
proc_exit(client_proc);
|
||||
event_once(-1, EV_TIMEOUT, client_exit, NULL, NULL);
|
||||
break;
|
||||
case MSG_READY:
|
||||
if (datalen != 0)
|
||||
fatalx("bad MSG_READY size");
|
||||
|
||||
event_del(&client_stdin);
|
||||
client_attached = 1;
|
||||
proc_send(client_peer, MSG_RESIZE, -1, NULL, 0);
|
||||
break;
|
||||
case MSG_STDIN:
|
||||
if (datalen != 0)
|
||||
fatalx("bad MSG_STDIN size");
|
||||
|
||||
event_add(&client_stdin, NULL);
|
||||
break;
|
||||
case MSG_STDOUT:
|
||||
if (datalen != sizeof stdoutdata)
|
||||
fatalx("bad MSG_STDOUT size");
|
||||
memcpy(&stdoutdata, data, sizeof stdoutdata);
|
||||
|
||||
client_write(STDOUT_FILENO, stdoutdata.data,
|
||||
stdoutdata.size);
|
||||
break;
|
||||
case MSG_STDERR:
|
||||
if (datalen != sizeof stderrdata)
|
||||
fatalx("bad MSG_STDERR size");
|
||||
memcpy(&stderrdata, data, sizeof stderrdata);
|
||||
|
||||
client_write(STDERR_FILENO, stderrdata.data,
|
||||
stderrdata.size);
|
||||
break;
|
||||
case MSG_VERSION:
|
||||
if (datalen != 0)
|
||||
fatalx("bad MSG_VERSION size");
|
||||
@ -641,6 +833,18 @@ client_dispatch_wait(struct imsg *imsg)
|
||||
case MSG_EXITED:
|
||||
proc_exit(client_proc);
|
||||
break;
|
||||
case MSG_READ_OPEN:
|
||||
client_read_open(data, datalen);
|
||||
break;
|
||||
case MSG_WRITE_OPEN:
|
||||
client_write_open(data, datalen);
|
||||
break;
|
||||
case MSG_WRITE:
|
||||
client_write_data(data, datalen);
|
||||
break;
|
||||
case MSG_WRITE_CLOSE:
|
||||
client_write_close(data, datalen);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -193,7 +193,7 @@ static enum cmd_retval
|
||||
cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
{
|
||||
struct args *args = self->args;
|
||||
struct client *c;
|
||||
struct client *c = item->client;
|
||||
struct window_pane *wp = item->target.wp;
|
||||
char *buf, *cause;
|
||||
const char *bufname;
|
||||
@ -214,18 +214,15 @@ cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
return (CMD_RETURN_ERROR);
|
||||
|
||||
if (args_has(args, 'p')) {
|
||||
c = item->client;
|
||||
if (c == NULL ||
|
||||
(c->session != NULL && !(c->flags & CLIENT_CONTROL))) {
|
||||
cmdq_error(item, "can't write to stdout");
|
||||
if (!file_can_print(c)) {
|
||||
cmdq_error(item, "can't write output to client");
|
||||
free(buf);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
evbuffer_add(c->stdout_data, buf, len);
|
||||
free(buf);
|
||||
file_print_buffer(c, buf, len);
|
||||
if (args_has(args, 'P') && len > 0)
|
||||
evbuffer_add(c->stdout_data, "\n", 1);
|
||||
server_client_push_stdout(c);
|
||||
file_print(c, "\n");
|
||||
free(buf);
|
||||
} else {
|
||||
bufname = NULL;
|
||||
if (args_has(args, 'b'))
|
||||
|
@ -33,8 +33,6 @@
|
||||
|
||||
static enum cmd_retval cmd_load_buffer_exec(struct cmd *, struct cmdq_item *);
|
||||
|
||||
static void cmd_load_buffer_callback(struct client *, int, void *);
|
||||
|
||||
const struct cmd_entry cmd_load_buffer_entry = {
|
||||
.name = "load-buffer",
|
||||
.alias = "loadb",
|
||||
@ -48,9 +46,40 @@ const struct cmd_entry cmd_load_buffer_entry = {
|
||||
|
||||
struct cmd_load_buffer_data {
|
||||
struct cmdq_item *item;
|
||||
char *bufname;
|
||||
char *name;
|
||||
};
|
||||
|
||||
static void
|
||||
cmd_load_buffer_done(__unused struct client *c, const char *path, int error,
|
||||
int closed, struct evbuffer *buffer, void *data)
|
||||
{
|
||||
struct cmd_load_buffer_data *cdata = data;
|
||||
struct cmdq_item *item = cdata->item;
|
||||
void *bdata = EVBUFFER_DATA(buffer);
|
||||
size_t bsize = EVBUFFER_LENGTH(buffer);
|
||||
void *copy;
|
||||
char *cause;
|
||||
|
||||
if (!closed)
|
||||
return;
|
||||
|
||||
if (error != 0)
|
||||
cmdq_error(item, "%s: %s", path, strerror(error));
|
||||
else if (bsize != 0) {
|
||||
copy = xmalloc(bsize);
|
||||
memcpy(copy, bdata, bsize);
|
||||
if (paste_set(copy, bsize, cdata->name, &cause) != 0) {
|
||||
cmdq_error(item, "%s", cause);
|
||||
free(cause);
|
||||
free(copy);
|
||||
}
|
||||
}
|
||||
cmdq_continue(item);
|
||||
|
||||
free(cdata->name);
|
||||
free(cdata);
|
||||
}
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item)
|
||||
{
|
||||
@ -60,124 +89,19 @@ cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item)
|
||||
struct session *s = item->target.s;
|
||||
struct winlink *wl = item->target.wl;
|
||||
struct window_pane *wp = item->target.wp;
|
||||
FILE *f;
|
||||
const char *bufname;
|
||||
char *pdata = NULL, *new_pdata, *cause;
|
||||
char *path, *file;
|
||||
size_t psize;
|
||||
int ch, error;
|
||||
const char *bufname = args_get(args, 'b');
|
||||
char *path;
|
||||
|
||||
bufname = NULL;
|
||||
if (args_has(args, 'b'))
|
||||
bufname = args_get(args, 'b');
|
||||
cdata = xmalloc(sizeof *cdata);
|
||||
cdata->item = item;
|
||||
if (bufname != NULL)
|
||||
cdata->name = xstrdup(bufname);
|
||||
else
|
||||
cdata->name = NULL;
|
||||
|
||||
path = format_single(item, args->argv[0], c, s, wl, wp);
|
||||
if (strcmp(path, "-") == 0) {
|
||||
free(path);
|
||||
c = item->client;
|
||||
|
||||
cdata = xcalloc(1, sizeof *cdata);
|
||||
cdata->item = item;
|
||||
|
||||
if (bufname != NULL)
|
||||
cdata->bufname = xstrdup(bufname);
|
||||
|
||||
error = server_set_stdin_callback(c, cmd_load_buffer_callback,
|
||||
cdata, &cause);
|
||||
if (error != 0) {
|
||||
cmdq_error(item, "-: %s", cause);
|
||||
free(cause);
|
||||
free(cdata);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
return (CMD_RETURN_WAIT);
|
||||
}
|
||||
|
||||
file = server_client_get_path(item->client, path);
|
||||
file_read(item->client, path, cmd_load_buffer_done, cdata);
|
||||
free(path);
|
||||
|
||||
f = fopen(file, "rb");
|
||||
if (f == NULL) {
|
||||
cmdq_error(item, "%s: %s", file, strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
|
||||
pdata = NULL;
|
||||
psize = 0;
|
||||
while ((ch = getc(f)) != EOF) {
|
||||
/* Do not let the server die due to memory exhaustion. */
|
||||
if ((new_pdata = realloc(pdata, psize + 2)) == NULL) {
|
||||
cmdq_error(item, "realloc error: %s", strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
pdata = new_pdata;
|
||||
pdata[psize++] = ch;
|
||||
}
|
||||
if (ferror(f)) {
|
||||
cmdq_error(item, "%s: read error", file);
|
||||
goto error;
|
||||
}
|
||||
if (pdata != NULL)
|
||||
pdata[psize] = '\0';
|
||||
|
||||
fclose(f);
|
||||
free(file);
|
||||
|
||||
if (paste_set(pdata, psize, bufname, &cause) != 0) {
|
||||
cmdq_error(item, "%s", cause);
|
||||
free(pdata);
|
||||
free(cause);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
return (CMD_RETURN_NORMAL);
|
||||
|
||||
error:
|
||||
free(pdata);
|
||||
if (f != NULL)
|
||||
fclose(f);
|
||||
free(file);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
static void
|
||||
cmd_load_buffer_callback(struct client *c, int closed, void *data)
|
||||
{
|
||||
struct cmd_load_buffer_data *cdata = data;
|
||||
char *pdata, *cause, *saved;
|
||||
size_t psize;
|
||||
|
||||
if (!closed)
|
||||
return;
|
||||
c->stdin_callback = NULL;
|
||||
|
||||
server_client_unref(c);
|
||||
if (c->flags & CLIENT_DEAD)
|
||||
goto out;
|
||||
|
||||
psize = EVBUFFER_LENGTH(c->stdin_data);
|
||||
if (psize == 0 || (pdata = malloc(psize + 1)) == NULL)
|
||||
goto out;
|
||||
|
||||
memcpy(pdata, EVBUFFER_DATA(c->stdin_data), psize);
|
||||
pdata[psize] = '\0';
|
||||
evbuffer_drain(c->stdin_data, psize);
|
||||
|
||||
if (paste_set(pdata, psize, cdata->bufname, &cause) != 0) {
|
||||
/* No context so can't use server_client_msg_error. */
|
||||
if (~c->flags & CLIENT_UTF8) {
|
||||
saved = cause;
|
||||
cause = utf8_sanitize(saved);
|
||||
free(saved);
|
||||
}
|
||||
evbuffer_add_printf(c->stderr_data, "%s", cause);
|
||||
server_client_push_stderr(c);
|
||||
free(pdata);
|
||||
free(cause);
|
||||
}
|
||||
out:
|
||||
cmdq_continue(cdata->item);
|
||||
|
||||
free(cdata->bufname);
|
||||
free(cdata);
|
||||
return (CMD_RETURN_WAIT);
|
||||
}
|
||||
|
34
cmd-queue.c
34
cmd-queue.c
@ -475,13 +475,11 @@ void
|
||||
cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
|
||||
{
|
||||
struct client *c = item->client;
|
||||
long t = item->time;
|
||||
u_int number = item->number;
|
||||
|
||||
if (c == NULL || !(c->flags & CLIENT_CONTROL))
|
||||
return;
|
||||
|
||||
evbuffer_add_printf(c->stdout_data, "%%%s %ld %u %d\n", guard,
|
||||
(long)item->time, item->number, flags);
|
||||
server_client_push_stdout(c);
|
||||
if (c != NULL && (c->flags & CLIENT_CONTROL))
|
||||
file_print(c, "%%%s %ld %u %d\n", guard, t, number, flags);
|
||||
}
|
||||
|
||||
/* Show message from command. */
|
||||
@ -495,20 +493,20 @@ cmdq_print(struct cmdq_item *item, const char *fmt, ...)
|
||||
char *tmp, *msg;
|
||||
|
||||
va_start(ap, fmt);
|
||||
xvasprintf(&msg, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
log_debug("%s: %s", __func__, msg);
|
||||
|
||||
if (c == NULL)
|
||||
/* nothing */;
|
||||
else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
|
||||
if (~c->flags & CLIENT_UTF8) {
|
||||
xvasprintf(&tmp, fmt, ap);
|
||||
tmp = msg;
|
||||
msg = utf8_sanitize(tmp);
|
||||
free(tmp);
|
||||
evbuffer_add(c->stdout_data, msg, strlen(msg));
|
||||
free(msg);
|
||||
} else
|
||||
evbuffer_add_vprintf(c->stdout_data, fmt, ap);
|
||||
evbuffer_add(c->stdout_data, "\n", 1);
|
||||
server_client_push_stdout(c);
|
||||
}
|
||||
file_print(c, "%s\n", msg);
|
||||
} else {
|
||||
wp = c->session->curw->window->active;
|
||||
wme = TAILQ_FIRST(&wp->modes);
|
||||
@ -517,7 +515,7 @@ cmdq_print(struct cmdq_item *item, const char *fmt, ...)
|
||||
window_copy_vadd(wp, fmt, ap);
|
||||
}
|
||||
|
||||
va_end(ap);
|
||||
free(msg);
|
||||
}
|
||||
|
||||
/* Show error from command. */
|
||||
@ -528,11 +526,10 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...)
|
||||
struct cmd *cmd = item->cmd;
|
||||
va_list ap;
|
||||
char *msg;
|
||||
size_t msglen;
|
||||
char *tmp;
|
||||
|
||||
va_start(ap, fmt);
|
||||
msglen = xvasprintf(&msg, fmt, ap);
|
||||
xvasprintf(&msg, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
log_debug("%s: %s", __func__, msg);
|
||||
@ -544,11 +541,8 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...)
|
||||
tmp = msg;
|
||||
msg = utf8_sanitize(tmp);
|
||||
free(tmp);
|
||||
msglen = strlen(msg);
|
||||
}
|
||||
evbuffer_add(c->stderr_data, msg, msglen);
|
||||
evbuffer_add(c->stderr_data, "\n", 1);
|
||||
server_client_push_stderr(c);
|
||||
file_error(c, "%s\n", msg);
|
||||
c->retval = 1;
|
||||
} else {
|
||||
*msg = toupper((u_char) *msg);
|
||||
|
@ -55,6 +55,20 @@ const struct cmd_entry cmd_show_buffer_entry = {
|
||||
.exec = cmd_save_buffer_exec
|
||||
};
|
||||
|
||||
static void
|
||||
cmd_save_buffer_done(__unused struct client *c, const char *path, int error,
|
||||
__unused int closed, __unused struct evbuffer *buffer, void *data)
|
||||
{
|
||||
struct cmdq_item *item = data;
|
||||
|
||||
if (!closed)
|
||||
return;
|
||||
|
||||
if (error != 0)
|
||||
cmdq_error(item, "%s: %s", path, strerror(error));
|
||||
cmdq_continue(item);
|
||||
}
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
|
||||
{
|
||||
@ -64,18 +78,17 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
|
||||
struct winlink *wl = item->target.wl;
|
||||
struct window_pane *wp = item->target.wp;
|
||||
struct paste_buffer *pb;
|
||||
const char *bufname, *bufdata, *start, *end, *flags;
|
||||
char *msg, *path, *file;
|
||||
size_t size, used, msglen, bufsize;
|
||||
FILE *f;
|
||||
int flags;
|
||||
const char *bufname = args_get(args, 'b'), *bufdata;
|
||||
size_t bufsize;
|
||||
char *path;
|
||||
|
||||
if (!args_has(args, 'b')) {
|
||||
if (bufname == NULL) {
|
||||
if ((pb = paste_get_top(NULL)) == NULL) {
|
||||
cmdq_error(item, "no buffers");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
} else {
|
||||
bufname = args_get(args, 'b');
|
||||
pb = paste_get_name(bufname);
|
||||
if (pb == NULL) {
|
||||
cmdq_error(item, "no buffer %s", bufname);
|
||||
@ -88,74 +101,13 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
|
||||
path = xstrdup("-");
|
||||
else
|
||||
path = format_single(item, args->argv[0], c, s, wl, wp);
|
||||
if (strcmp(path, "-") == 0) {
|
||||
free(path);
|
||||
c = item->client;
|
||||
if (c == NULL) {
|
||||
cmdq_error(item, "can't write to stdout");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
if (c->session == NULL || (c->flags & CLIENT_CONTROL))
|
||||
goto do_stdout;
|
||||
goto do_print;
|
||||
}
|
||||
|
||||
flags = "wb";
|
||||
if (args_has(self->args, 'a'))
|
||||
flags = "ab";
|
||||
|
||||
file = server_client_get_path(item->client, path);
|
||||
flags = O_APPEND;
|
||||
else
|
||||
flags = 0;
|
||||
file_write(item->client, path, flags, bufdata, bufsize,
|
||||
cmd_save_buffer_done, item);
|
||||
free(path);
|
||||
|
||||
f = fopen(file, flags);
|
||||
if (f == NULL) {
|
||||
cmdq_error(item, "%s: %s", file, strerror(errno));
|
||||
free(file);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
if (fwrite(bufdata, 1, bufsize, f) != bufsize) {
|
||||
cmdq_error(item, "%s: write error", file);
|
||||
fclose(f);
|
||||
free(file);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
free(file);
|
||||
|
||||
return (CMD_RETURN_NORMAL);
|
||||
|
||||
do_stdout:
|
||||
evbuffer_add(c->stdout_data, bufdata, bufsize);
|
||||
server_client_push_stdout(c);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
|
||||
do_print:
|
||||
if (bufsize > (INT_MAX / 4) - 1) {
|
||||
cmdq_error(item, "buffer too big");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
msg = NULL;
|
||||
|
||||
used = 0;
|
||||
while (used != bufsize) {
|
||||
start = bufdata + used;
|
||||
end = memchr(start, '\n', bufsize - used);
|
||||
if (end != NULL)
|
||||
size = end - start;
|
||||
else
|
||||
size = bufsize - used;
|
||||
|
||||
msglen = size * 4 + 1;
|
||||
msg = xrealloc(msg, msglen);
|
||||
|
||||
strvisx(msg, start, size, VIS_OCTAL|VIS_TAB);
|
||||
cmdq_print(item, "%s", msg);
|
||||
|
||||
used += size + (end != NULL);
|
||||
}
|
||||
|
||||
free(msg);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
return (CMD_RETURN_WAIT);
|
||||
}
|
||||
|
@ -31,8 +31,6 @@
|
||||
|
||||
static enum cmd_retval cmd_source_file_exec(struct cmd *, struct cmdq_item *);
|
||||
|
||||
static enum cmd_retval cmd_source_file_done(struct cmdq_item *, void *);
|
||||
|
||||
const struct cmd_entry cmd_source_file_entry = {
|
||||
.name = "source-file",
|
||||
.alias = "source",
|
||||
@ -44,31 +42,115 @@ const struct cmd_entry cmd_source_file_entry = {
|
||||
.exec = cmd_source_file_exec
|
||||
};
|
||||
|
||||
struct cmd_source_file_data {
|
||||
struct cmdq_item *item;
|
||||
int flags;
|
||||
|
||||
struct cmdq_item *after;
|
||||
enum cmd_retval retval;
|
||||
|
||||
u_int current;
|
||||
char **files;
|
||||
u_int nfiles;
|
||||
};
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_source_file_complete_cb(struct cmdq_item *item, __unused void *data)
|
||||
{
|
||||
cfg_print_causes(item);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
static void
|
||||
cmd_source_file_complete(struct client *c, struct cmd_source_file_data *cdata)
|
||||
{
|
||||
struct cmdq_item *new_item;
|
||||
|
||||
if (cfg_finished) {
|
||||
if (cdata->retval == CMD_RETURN_ERROR && c->session == NULL)
|
||||
c->retval = 1;
|
||||
new_item = cmdq_get_callback(cmd_source_file_complete_cb, NULL);
|
||||
cmdq_insert_after(cdata->after, new_item);
|
||||
}
|
||||
|
||||
free(cdata->files);
|
||||
free(cdata);
|
||||
}
|
||||
|
||||
static void
|
||||
cmd_source_file_done(struct client *c, const char *path, int error,
|
||||
int closed, struct evbuffer *buffer, void *data)
|
||||
{
|
||||
struct cmd_source_file_data *cdata = data;
|
||||
struct cmdq_item *item = cdata->item;
|
||||
void *bdata = EVBUFFER_DATA(buffer);
|
||||
size_t bsize = EVBUFFER_LENGTH(buffer);
|
||||
u_int n;
|
||||
struct cmdq_item *new_item;
|
||||
|
||||
if (!closed)
|
||||
return;
|
||||
|
||||
if (error != 0)
|
||||
cmdq_error(item, "%s: %s", path, strerror(error));
|
||||
else if (bsize != 0) {
|
||||
if (load_cfg_from_buffer(bdata, bsize, path, c, cdata->after,
|
||||
cdata->flags, &new_item) < 0)
|
||||
cdata->retval = CMD_RETURN_ERROR;
|
||||
else if (new_item != NULL)
|
||||
cdata->after = new_item;
|
||||
}
|
||||
|
||||
n = ++cdata->current;
|
||||
if (n < cdata->nfiles)
|
||||
file_read(c, cdata->files[n], cmd_source_file_done, cdata);
|
||||
else {
|
||||
cmd_source_file_complete(c, cdata);
|
||||
cmdq_continue(item);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cmd_source_file_add(struct cmd_source_file_data *cdata, const char *path)
|
||||
{
|
||||
cdata->files = xreallocarray(cdata->files, cdata->nfiles + 1,
|
||||
sizeof *cdata->files);
|
||||
cdata->files[cdata->nfiles++] = xstrdup(path);
|
||||
}
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
|
||||
{
|
||||
struct args *args = self->args;
|
||||
int flags = 0;
|
||||
struct client *c = item->client;
|
||||
struct cmdq_item *new_item, *after;
|
||||
enum cmd_retval retval;
|
||||
char *pattern, *cwd;
|
||||
const char *path, *error;
|
||||
glob_t g;
|
||||
int i;
|
||||
u_int j;
|
||||
struct args *args = self->args;
|
||||
struct cmd_source_file_data *cdata;
|
||||
int flags = 0;
|
||||
struct client *c = item->client;
|
||||
enum cmd_retval retval = CMD_RETURN_NORMAL;
|
||||
char *pattern, *cwd;
|
||||
const char *path, *error;
|
||||
glob_t g;
|
||||
int i;
|
||||
u_int j;
|
||||
|
||||
cdata = xcalloc(1, sizeof *cdata);
|
||||
cdata->item = item;
|
||||
|
||||
if (args_has(args, 'q'))
|
||||
flags |= CMD_PARSE_QUIET;
|
||||
cdata->flags |= CMD_PARSE_QUIET;
|
||||
if (args_has(args, 'n'))
|
||||
flags |= CMD_PARSE_PARSEONLY;
|
||||
cdata->flags |= CMD_PARSE_PARSEONLY;
|
||||
if (args_has(args, 'v'))
|
||||
flags |= CMD_PARSE_VERBOSE;
|
||||
cdata->flags |= CMD_PARSE_VERBOSE;
|
||||
|
||||
utf8_stravis(&cwd, server_client_get_cwd(c, NULL), VIS_GLOB);
|
||||
|
||||
retval = CMD_RETURN_NORMAL;
|
||||
for (i = 0; i < args->argc; i++) {
|
||||
path = args->argv[i];
|
||||
if (strcmp(path, "-") == 0) {
|
||||
cmd_source_file_add(cdata, "-");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*path == '/')
|
||||
pattern = xstrdup(path);
|
||||
else
|
||||
@ -86,30 +168,19 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
|
||||
}
|
||||
free(pattern);
|
||||
|
||||
after = item;
|
||||
for (j = 0; j < g.gl_pathc; j++) {
|
||||
path = g.gl_pathv[j];
|
||||
if (load_cfg(path, c, after, flags, &new_item) < 0)
|
||||
retval = CMD_RETURN_ERROR;
|
||||
else if (new_item != NULL)
|
||||
after = new_item;
|
||||
}
|
||||
globfree(&g);
|
||||
}
|
||||
if (cfg_finished) {
|
||||
if (retval == CMD_RETURN_ERROR && c->session == NULL)
|
||||
c->retval = 1;
|
||||
new_item = cmdq_get_callback(cmd_source_file_done, NULL);
|
||||
cmdq_insert_after(item, new_item);
|
||||
for (j = 0; j < g.gl_pathc; j++)
|
||||
cmd_source_file_add(cdata, g.gl_pathv[j]);
|
||||
}
|
||||
|
||||
cdata->after = item;
|
||||
cdata->retval = retval;
|
||||
|
||||
if (cdata->nfiles != 0) {
|
||||
file_read(c, cdata->files[0], cmd_source_file_done, cdata);
|
||||
retval = CMD_RETURN_WAIT;
|
||||
} else
|
||||
cmd_source_file_complete(c, cdata);
|
||||
|
||||
free(cwd);
|
||||
return (retval);
|
||||
}
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_source_file_done(struct cmdq_item *item, __unused void *data)
|
||||
{
|
||||
cfg_print_causes(item);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ control_notify_input(struct client *c, struct window_pane *wp,
|
||||
else
|
||||
evbuffer_add_printf(message, "%c", buf[i]);
|
||||
}
|
||||
control_write_buffer(c, message);
|
||||
control_write(c, "%s", EVBUFFER_DATA(message));
|
||||
evbuffer_free(message);
|
||||
}
|
||||
}
|
||||
|
36
control.c
36
control.c
@ -30,23 +30,12 @@
|
||||
void
|
||||
control_write(struct client *c, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
evbuffer_add_vprintf(c->stdout_data, fmt, ap);
|
||||
file_vprint(c, fmt, ap);
|
||||
file_print(c, "\n");
|
||||
va_end(ap);
|
||||
|
||||
evbuffer_add(c->stdout_data, "\n", 1);
|
||||
server_client_push_stdout(c);
|
||||
}
|
||||
|
||||
/* Write a buffer, adding a terminal newline. Empties buffer. */
|
||||
void
|
||||
control_write_buffer(struct client *c, struct evbuffer *buffer)
|
||||
{
|
||||
evbuffer_add_buffer(c->stdout_data, buffer);
|
||||
evbuffer_add(c->stdout_data, "\n", 1);
|
||||
server_client_push_stdout(c);
|
||||
}
|
||||
|
||||
/* Control error callback. */
|
||||
@ -65,20 +54,22 @@ control_error(struct cmdq_item *item, void *data)
|
||||
}
|
||||
|
||||
/* Control input callback. Read lines and fire commands. */
|
||||
void
|
||||
control_callback(struct client *c, int closed, __unused void *data)
|
||||
static void
|
||||
control_callback(__unused struct client *c, __unused const char *path,
|
||||
int error, int closed, struct evbuffer *buffer, __unused void *data)
|
||||
{
|
||||
char *line;
|
||||
struct cmdq_item *item;
|
||||
struct cmd_parse_result *pr;
|
||||
|
||||
if (closed)
|
||||
if (closed || error != 0)
|
||||
c->flags |= CLIENT_EXIT;
|
||||
|
||||
for (;;) {
|
||||
line = evbuffer_readln(c->stdin_data, NULL, EVBUFFER_EOL_LF);
|
||||
line = evbuffer_readln(buffer, NULL, EVBUFFER_EOL_LF);
|
||||
if (line == NULL)
|
||||
break;
|
||||
log_debug("%s: %s", __func__, line);
|
||||
if (*line == '\0') { /* empty line exit */
|
||||
free(line);
|
||||
c->flags |= CLIENT_EXIT;
|
||||
@ -104,3 +95,12 @@ control_callback(struct client *c, int closed, __unused void *data)
|
||||
free(line);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
control_start(struct client *c)
|
||||
{
|
||||
file_read(c, "-", control_callback, c);
|
||||
|
||||
if (c->flags & CLIENT_CONTROLCONTROL)
|
||||
file_print(c, "\033P1000p");
|
||||
}
|
||||
|
390
file.c
Normal file
390
file.c
Normal file
@ -0,0 +1,390 @@
|
||||
/* $OpenBSD$ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
static int file_next_stream = 3;
|
||||
|
||||
RB_GENERATE(client_files, client_file, entry, file_cmp);
|
||||
|
||||
int
|
||||
file_cmp(struct client_file *cf1, struct client_file *cf2)
|
||||
{
|
||||
if (cf1->stream < cf2->stream)
|
||||
return (-1);
|
||||
if (cf1->stream > cf2->stream)
|
||||
return (1);
|
||||
return (0);
|
||||
}
|
||||
|
||||
struct client_file *
|
||||
file_create(struct client *c, int stream, client_file_cb cb, void *cbdata)
|
||||
{
|
||||
struct client_file *cf;
|
||||
|
||||
cf = xcalloc(1, sizeof *cf);
|
||||
cf->c = c;
|
||||
cf->references = 1;
|
||||
cf->stream = stream;
|
||||
|
||||
cf->buffer = evbuffer_new();
|
||||
if (cf->buffer == NULL)
|
||||
fatalx("out of memory");
|
||||
|
||||
cf->cb = cb;
|
||||
cf->data = cbdata;
|
||||
|
||||
if (cf->c != NULL) {
|
||||
RB_INSERT(client_files, &cf->c->files, cf);
|
||||
cf->c->references++;
|
||||
}
|
||||
|
||||
return (cf);
|
||||
}
|
||||
|
||||
void
|
||||
file_free(struct client_file *cf)
|
||||
{
|
||||
if (--cf->references != 0)
|
||||
return;
|
||||
|
||||
evbuffer_free(cf->buffer);
|
||||
free(cf->path);
|
||||
|
||||
if (cf->c != NULL) {
|
||||
RB_REMOVE(client_files, &cf->c->files, cf);
|
||||
server_client_unref(cf->c);
|
||||
}
|
||||
free(cf);
|
||||
}
|
||||
|
||||
static void
|
||||
file_fire_done_cb(__unused int fd, __unused short events, void *arg)
|
||||
{
|
||||
struct client_file *cf = arg;
|
||||
struct client *c = cf->c;
|
||||
|
||||
if (cf->cb != NULL && (c == NULL || (~c->flags & CLIENT_DEAD)))
|
||||
cf->cb(c, cf->path, cf->error, 1, cf->buffer, cf->data);
|
||||
file_free(cf);
|
||||
}
|
||||
|
||||
void
|
||||
file_fire_done(struct client_file *cf)
|
||||
{
|
||||
event_once(-1, EV_TIMEOUT, file_fire_done_cb, cf, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
file_fire_read(struct client_file *cf)
|
||||
{
|
||||
struct client *c = cf->c;
|
||||
|
||||
if (cf->cb != NULL)
|
||||
cf->cb(c, cf->path, cf->error, 0, cf->buffer, cf->data);
|
||||
}
|
||||
|
||||
int
|
||||
file_can_print(struct client *c)
|
||||
{
|
||||
if (c == NULL)
|
||||
return (0);
|
||||
if (c->session != NULL && (~c->flags & CLIENT_CONTROL))
|
||||
return (0);
|
||||
return (1);
|
||||
}
|
||||
|
||||
void
|
||||
file_print(struct client *c, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
file_vprint(c, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
file_vprint(struct client *c, const char *fmt, va_list ap)
|
||||
{
|
||||
struct client_file find, *cf;
|
||||
struct msg_write_open msg;
|
||||
|
||||
if (!file_can_print(c))
|
||||
return;
|
||||
|
||||
find.stream = 1;
|
||||
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
|
||||
cf = file_create(c, 1, NULL, NULL);
|
||||
cf->path = xstrdup("-");
|
||||
|
||||
evbuffer_add_vprintf(cf->buffer, fmt, ap);
|
||||
|
||||
msg.stream = 1;
|
||||
msg.fd = STDOUT_FILENO;
|
||||
msg.flags = 0;
|
||||
strlcpy(msg.path, "-", sizeof msg.path);
|
||||
proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
|
||||
} else {
|
||||
evbuffer_add_vprintf(cf->buffer, fmt, ap);
|
||||
file_push(cf);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
file_print_buffer(struct client *c, void *data, size_t size)
|
||||
{
|
||||
struct client_file find, *cf;
|
||||
struct msg_write_open msg;
|
||||
|
||||
if (!file_can_print(c))
|
||||
return;
|
||||
|
||||
find.stream = 1;
|
||||
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
|
||||
cf = file_create(c, 1, NULL, NULL);
|
||||
cf->path = xstrdup("-");
|
||||
|
||||
evbuffer_add(cf->buffer, data, size);
|
||||
|
||||
msg.stream = 1;
|
||||
msg.fd = STDOUT_FILENO;
|
||||
msg.flags = 0;
|
||||
strlcpy(msg.path, "-", sizeof msg.path);
|
||||
proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
|
||||
} else {
|
||||
evbuffer_add(cf->buffer, data, size);
|
||||
file_push(cf);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
file_error(struct client *c, const char *fmt, ...)
|
||||
{
|
||||
struct client_file find, *cf;
|
||||
struct msg_write_open msg;
|
||||
va_list ap;
|
||||
|
||||
if (!file_can_print(c))
|
||||
return;
|
||||
|
||||
va_start(ap, fmt);
|
||||
|
||||
find.stream = 2;
|
||||
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
|
||||
cf = file_create(c, 2, NULL, NULL);
|
||||
cf->path = xstrdup("-");
|
||||
|
||||
evbuffer_add_vprintf(cf->buffer, fmt, ap);
|
||||
|
||||
msg.stream = 2;
|
||||
msg.fd = STDERR_FILENO;
|
||||
msg.flags = 0;
|
||||
strlcpy(msg.path, "-", sizeof msg.path);
|
||||
proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
|
||||
} else {
|
||||
evbuffer_add_vprintf(cf->buffer, fmt, ap);
|
||||
file_push(cf);
|
||||
}
|
||||
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
file_write(struct client *c, const char *path, int flags, const void *bdata,
|
||||
size_t bsize, client_file_cb cb, void *cbdata)
|
||||
{
|
||||
struct client_file *cf;
|
||||
FILE *f;
|
||||
struct msg_write_open msg;
|
||||
int fd = -1;
|
||||
const char *mode;
|
||||
|
||||
if (strcmp(path, "-") == 0) {
|
||||
cf = file_create(c, file_next_stream++, cb, cbdata);
|
||||
cf->path = xstrdup("-");
|
||||
|
||||
fd = STDOUT_FILENO;
|
||||
if (c == NULL || c->flags & CLIENT_ATTACHED) {
|
||||
cf->error = EBADF;
|
||||
goto done;
|
||||
}
|
||||
goto skip;
|
||||
}
|
||||
|
||||
cf = file_create(c, file_next_stream++, cb, cbdata);
|
||||
cf->path = server_client_get_path(c, path);
|
||||
|
||||
if (c == NULL || c->flags & CLIENT_ATTACHED) {
|
||||
if (flags & O_APPEND)
|
||||
mode = "ab";
|
||||
else
|
||||
mode = "wb";
|
||||
f = fopen(cf->path, mode);
|
||||
if (f == NULL) {
|
||||
cf->error = errno;
|
||||
goto done;
|
||||
}
|
||||
if (fwrite(bdata, 1, bsize, f) != bsize) {
|
||||
fclose(f);
|
||||
cf->error = EIO;
|
||||
goto done;
|
||||
}
|
||||
fclose(f);
|
||||
goto done;
|
||||
}
|
||||
|
||||
skip:
|
||||
evbuffer_add(cf->buffer, bdata, bsize);
|
||||
|
||||
msg.stream = cf->stream;
|
||||
msg.fd = fd;
|
||||
msg.flags = flags;
|
||||
if (strlcpy(msg.path, cf->path, sizeof msg.path) >= sizeof msg.path) {
|
||||
cf->error = E2BIG;
|
||||
goto done;
|
||||
}
|
||||
if (proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg) != 0) {
|
||||
cf->error = EINVAL;
|
||||
goto done;
|
||||
}
|
||||
return;
|
||||
|
||||
done:
|
||||
file_fire_done(cf);
|
||||
}
|
||||
|
||||
void
|
||||
file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata)
|
||||
{
|
||||
struct client_file *cf;
|
||||
FILE *f;
|
||||
struct msg_read_open msg;
|
||||
int fd = -1;
|
||||
char buffer[BUFSIZ];
|
||||
size_t size;
|
||||
|
||||
if (strcmp(path, "-") == 0) {
|
||||
cf = file_create(c, file_next_stream++, cb, cbdata);
|
||||
cf->path = xstrdup("-");
|
||||
|
||||
fd = STDIN_FILENO;
|
||||
if (c == NULL || c->flags & CLIENT_ATTACHED) {
|
||||
cf->error = EBADF;
|
||||
goto done;
|
||||
}
|
||||
goto skip;
|
||||
}
|
||||
|
||||
cf = file_create(c, file_next_stream++, cb, cbdata);
|
||||
cf->path = server_client_get_path(c, path);
|
||||
|
||||
if (c == NULL || c->flags & CLIENT_ATTACHED) {
|
||||
f = fopen(cf->path, "rb");
|
||||
if (f == NULL) {
|
||||
cf->error = errno;
|
||||
goto done;
|
||||
}
|
||||
for (;;) {
|
||||
size = fread(buffer, 1, sizeof buffer, f);
|
||||
if (evbuffer_add(cf->buffer, buffer, size) != 0) {
|
||||
cf->error = ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
if (size != sizeof buffer)
|
||||
break;
|
||||
}
|
||||
if (ferror(f)) {
|
||||
cf->error = EIO;
|
||||
goto done;
|
||||
}
|
||||
fclose(f);
|
||||
goto done;
|
||||
}
|
||||
|
||||
skip:
|
||||
msg.stream = cf->stream;
|
||||
msg.fd = fd;
|
||||
if (strlcpy(msg.path, cf->path, sizeof msg.path) >= sizeof msg.path) {
|
||||
cf->error = E2BIG;
|
||||
goto done;
|
||||
}
|
||||
if (proc_send(c->peer, MSG_READ_OPEN, -1, &msg, sizeof msg) != 0) {
|
||||
cf->error = EINVAL;
|
||||
goto done;
|
||||
}
|
||||
return;
|
||||
|
||||
done:
|
||||
file_fire_done(cf);
|
||||
}
|
||||
|
||||
static void
|
||||
file_push_cb(__unused int fd, __unused short events, void *arg)
|
||||
{
|
||||
struct client_file *cf = arg;
|
||||
struct client *c = cf->c;
|
||||
|
||||
if (~c->flags & CLIENT_DEAD)
|
||||
file_push(cf);
|
||||
file_free(cf);
|
||||
}
|
||||
|
||||
void
|
||||
file_push(struct client_file *cf)
|
||||
{
|
||||
struct client *c = cf->c;
|
||||
struct msg_write_data msg;
|
||||
struct msg_write_close close;
|
||||
size_t sent, left;
|
||||
|
||||
left = EVBUFFER_LENGTH(cf->buffer);
|
||||
while (left != 0) {
|
||||
sent = left;
|
||||
if (sent > sizeof msg.data)
|
||||
sent = sizeof msg.data;
|
||||
memcpy(msg.data, EVBUFFER_DATA(cf->buffer), sent);
|
||||
msg.size = sent;
|
||||
|
||||
msg.stream = cf->stream;
|
||||
if (proc_send(c->peer, MSG_WRITE, -1, &msg, sizeof msg) != 0)
|
||||
break;
|
||||
evbuffer_drain(cf->buffer, sent);
|
||||
|
||||
left = EVBUFFER_LENGTH(cf->buffer);
|
||||
log_debug("%s: file %d sent %zu, left %zu", c->name, cf->stream,
|
||||
sent, left);
|
||||
}
|
||||
if (left != 0) {
|
||||
cf->references++;
|
||||
event_once(-1, EV_TIMEOUT, file_push_cb, cf, NULL);
|
||||
} else if (cf->stream > 2) {
|
||||
close.stream = cf->stream;
|
||||
proc_send(c->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close);
|
||||
file_fire_done(cf);
|
||||
}
|
||||
}
|
223
server-client.c
223
server-client.c
@ -48,6 +48,12 @@ static void server_client_dispatch(struct imsg *, void *);
|
||||
static void server_client_dispatch_command(struct client *, struct imsg *);
|
||||
static void server_client_dispatch_identify(struct client *, struct imsg *);
|
||||
static void server_client_dispatch_shell(struct client *);
|
||||
static void server_client_dispatch_write_ready(struct client *,
|
||||
struct imsg *);
|
||||
static void server_client_dispatch_read_data(struct client *,
|
||||
struct imsg *);
|
||||
static void server_client_dispatch_read_done(struct client *,
|
||||
struct imsg *);
|
||||
|
||||
/* Number of attached clients. */
|
||||
u_int
|
||||
@ -195,16 +201,6 @@ server_client_create(int fd)
|
||||
|
||||
TAILQ_INIT(&c->queue);
|
||||
|
||||
c->stdin_data = evbuffer_new();
|
||||
if (c->stdin_data == NULL)
|
||||
fatalx("out of memory");
|
||||
c->stdout_data = evbuffer_new();
|
||||
if (c->stdout_data == NULL)
|
||||
fatalx("out of memory");
|
||||
c->stderr_data = evbuffer_new();
|
||||
if (c->stderr_data == NULL)
|
||||
fatalx("out of memory");
|
||||
|
||||
c->tty.fd = -1;
|
||||
c->title = NULL;
|
||||
|
||||
@ -223,6 +219,8 @@ server_client_create(int fd)
|
||||
c->prompt_buffer = NULL;
|
||||
c->prompt_index = 0;
|
||||
|
||||
RB_INIT(&c->files);
|
||||
|
||||
c->flags |= CLIENT_FOCUSED;
|
||||
|
||||
c->keytable = key_bindings_get_table("root", 1);
|
||||
@ -264,6 +262,7 @@ void
|
||||
server_client_lost(struct client *c)
|
||||
{
|
||||
struct message_entry *msg, *msg1;
|
||||
struct client_file *cf;
|
||||
|
||||
c->flags |= CLIENT_DEAD;
|
||||
|
||||
@ -271,8 +270,10 @@ server_client_lost(struct client *c)
|
||||
status_prompt_clear(c);
|
||||
status_message_clear(c);
|
||||
|
||||
if (c->stdin_callback != NULL)
|
||||
c->stdin_callback(c, 1, c->stdin_callback_data);
|
||||
RB_FOREACH(cf, client_files, &c->files) {
|
||||
cf->error = EINTR;
|
||||
file_fire_done(cf);
|
||||
}
|
||||
|
||||
TAILQ_REMOVE(&clients, c, entry);
|
||||
log_debug("lost client %p", c);
|
||||
@ -286,11 +287,6 @@ server_client_lost(struct client *c)
|
||||
free(c->ttyname);
|
||||
free(c->term);
|
||||
|
||||
evbuffer_free(c->stdin_data);
|
||||
evbuffer_free(c->stdout_data);
|
||||
if (c->stderr_data != c->stdout_data)
|
||||
evbuffer_free(c->stderr_data);
|
||||
|
||||
status_free(c);
|
||||
|
||||
free(c->title);
|
||||
@ -1558,17 +1554,17 @@ server_client_click_timer(__unused int fd, __unused short events, void *data)
|
||||
static void
|
||||
server_client_check_exit(struct client *c)
|
||||
{
|
||||
struct client_file *cf;
|
||||
|
||||
if (~c->flags & CLIENT_EXIT)
|
||||
return;
|
||||
if (c->flags & CLIENT_EXITED)
|
||||
return;
|
||||
|
||||
if (EVBUFFER_LENGTH(c->stdin_data) != 0)
|
||||
return;
|
||||
if (EVBUFFER_LENGTH(c->stdout_data) != 0)
|
||||
return;
|
||||
if (EVBUFFER_LENGTH(c->stderr_data) != 0)
|
||||
return;
|
||||
RB_FOREACH(cf, client_files, &c->files) {
|
||||
if (EVBUFFER_LENGTH(cf->buffer) != 0)
|
||||
return;
|
||||
}
|
||||
|
||||
if (c->flags & CLIENT_ATTACHED)
|
||||
notify_client("client-detached", c);
|
||||
@ -1707,11 +1703,10 @@ server_client_set_title(struct client *c)
|
||||
static void
|
||||
server_client_dispatch(struct imsg *imsg, void *arg)
|
||||
{
|
||||
struct client *c = arg;
|
||||
struct msg_stdin_data stdindata;
|
||||
const char *data;
|
||||
ssize_t datalen;
|
||||
struct session *s;
|
||||
struct client *c = arg;
|
||||
const char *data;
|
||||
ssize_t datalen;
|
||||
struct session *s;
|
||||
|
||||
if (c->flags & CLIENT_DEAD)
|
||||
return;
|
||||
@ -1738,21 +1733,6 @@ server_client_dispatch(struct imsg *imsg, void *arg)
|
||||
case MSG_COMMAND:
|
||||
server_client_dispatch_command(c, imsg);
|
||||
break;
|
||||
case MSG_STDIN:
|
||||
if (datalen != sizeof stdindata)
|
||||
fatalx("bad MSG_STDIN size");
|
||||
memcpy(&stdindata, data, sizeof stdindata);
|
||||
|
||||
if (c->stdin_callback == NULL)
|
||||
break;
|
||||
if (stdindata.size <= 0)
|
||||
c->stdin_closed = 1;
|
||||
else {
|
||||
evbuffer_add(c->stdin_data, stdindata.data,
|
||||
stdindata.size);
|
||||
}
|
||||
c->stdin_callback(c, c->stdin_closed, c->stdin_callback_data);
|
||||
break;
|
||||
case MSG_RESIZE:
|
||||
if (datalen != 0)
|
||||
fatalx("bad MSG_RESIZE size");
|
||||
@ -1804,6 +1784,15 @@ server_client_dispatch(struct imsg *imsg, void *arg)
|
||||
|
||||
server_client_dispatch_shell(c);
|
||||
break;
|
||||
case MSG_WRITE_READY:
|
||||
server_client_dispatch_write_ready(c, imsg);
|
||||
break;
|
||||
case MSG_READ:
|
||||
server_client_dispatch_read_data(c, imsg);
|
||||
break;
|
||||
case MSG_READ_DONE:
|
||||
server_client_dispatch_read_done(c, imsg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1822,7 +1811,7 @@ server_client_command_done(struct cmdq_item *item, __unused void *data)
|
||||
static void
|
||||
server_client_dispatch_command(struct client *c, struct imsg *imsg)
|
||||
{
|
||||
struct msg_command_data data;
|
||||
struct msg_command data;
|
||||
char *buf;
|
||||
size_t len;
|
||||
int argc;
|
||||
@ -1966,19 +1955,11 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg)
|
||||
#endif
|
||||
|
||||
if (c->flags & CLIENT_CONTROL) {
|
||||
c->stdin_callback = control_callback;
|
||||
|
||||
evbuffer_free(c->stderr_data);
|
||||
c->stderr_data = c->stdout_data;
|
||||
|
||||
if (c->flags & CLIENT_CONTROLCONTROL)
|
||||
evbuffer_add_printf(c->stdout_data, "\033P1000p");
|
||||
proc_send(c->peer, MSG_STDIN, -1, NULL, 0);
|
||||
|
||||
c->tty.fd = -1;
|
||||
|
||||
close(c->fd);
|
||||
c->fd = -1;
|
||||
|
||||
control_start(c);
|
||||
c->tty.fd = -1;
|
||||
} else if (c->fd != -1) {
|
||||
if (tty_init(&c->tty, c, c->fd, c->term) != 0) {
|
||||
close(c->fd);
|
||||
@ -2018,93 +1999,71 @@ server_client_dispatch_shell(struct client *c)
|
||||
proc_kill_peer(c->peer);
|
||||
}
|
||||
|
||||
/* Event callback to push more stdout data if any left. */
|
||||
/* Handle write ready message. */
|
||||
static void
|
||||
server_client_stdout_cb(__unused int fd, __unused short events, void *arg)
|
||||
server_client_dispatch_write_ready(struct client *c, struct imsg *imsg)
|
||||
{
|
||||
struct client *c = arg;
|
||||
struct msg_write_ready *msg = imsg->data;
|
||||
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
|
||||
struct client_file find, *cf;
|
||||
|
||||
if (~c->flags & CLIENT_DEAD)
|
||||
server_client_push_stdout(c);
|
||||
server_client_unref(c);
|
||||
}
|
||||
|
||||
/* Push stdout to client if possible. */
|
||||
void
|
||||
server_client_push_stdout(struct client *c)
|
||||
{
|
||||
struct msg_stdout_data data;
|
||||
size_t sent, left;
|
||||
|
||||
left = EVBUFFER_LENGTH(c->stdout_data);
|
||||
while (left != 0) {
|
||||
sent = left;
|
||||
if (sent > sizeof data.data)
|
||||
sent = sizeof data.data;
|
||||
memcpy(data.data, EVBUFFER_DATA(c->stdout_data), sent);
|
||||
data.size = sent;
|
||||
|
||||
if (proc_send(c->peer, MSG_STDOUT, -1, &data, sizeof data) != 0)
|
||||
break;
|
||||
evbuffer_drain(c->stdout_data, sent);
|
||||
|
||||
left = EVBUFFER_LENGTH(c->stdout_data);
|
||||
log_debug("%s: client %p, sent %zu, left %zu", __func__, c,
|
||||
sent, left);
|
||||
}
|
||||
if (left != 0) {
|
||||
c->references++;
|
||||
event_once(-1, EV_TIMEOUT, server_client_stdout_cb, c, NULL);
|
||||
log_debug("%s: client %p, queued", __func__, c);
|
||||
}
|
||||
}
|
||||
|
||||
/* Event callback to push more stderr data if any left. */
|
||||
static void
|
||||
server_client_stderr_cb(__unused int fd, __unused short events, void *arg)
|
||||
{
|
||||
struct client *c = arg;
|
||||
|
||||
if (~c->flags & CLIENT_DEAD)
|
||||
server_client_push_stderr(c);
|
||||
server_client_unref(c);
|
||||
}
|
||||
|
||||
/* Push stderr to client if possible. */
|
||||
void
|
||||
server_client_push_stderr(struct client *c)
|
||||
{
|
||||
struct msg_stderr_data data;
|
||||
size_t sent, left;
|
||||
|
||||
if (c->stderr_data == c->stdout_data) {
|
||||
server_client_push_stdout(c);
|
||||
if (msglen != sizeof *msg)
|
||||
fatalx("bad MSG_WRITE_READY size");
|
||||
find.stream = msg->stream;
|
||||
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL)
|
||||
return;
|
||||
}
|
||||
if (msg->error != 0) {
|
||||
cf->error = msg->error;
|
||||
file_fire_done(cf);
|
||||
} else
|
||||
file_push(cf);
|
||||
}
|
||||
|
||||
left = EVBUFFER_LENGTH(c->stderr_data);
|
||||
while (left != 0) {
|
||||
sent = left;
|
||||
if (sent > sizeof data.data)
|
||||
sent = sizeof data.data;
|
||||
memcpy(data.data, EVBUFFER_DATA(c->stderr_data), sent);
|
||||
data.size = sent;
|
||||
/* Handle read data message. */
|
||||
static void
|
||||
server_client_dispatch_read_data(struct client *c, struct imsg *imsg)
|
||||
{
|
||||
struct msg_read_data *msg = imsg->data;
|
||||
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
|
||||
struct client_file find, *cf;
|
||||
void *bdata = msg->data;
|
||||
size_t bsize = msg->size;
|
||||
|
||||
if (proc_send(c->peer, MSG_STDERR, -1, &data, sizeof data) != 0)
|
||||
break;
|
||||
evbuffer_drain(c->stderr_data, sent);
|
||||
if (msglen != sizeof *msg)
|
||||
fatalx("bad MSG_READ_DATA size");
|
||||
find.stream = msg->stream;
|
||||
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL)
|
||||
return;
|
||||
|
||||
left = EVBUFFER_LENGTH(c->stderr_data);
|
||||
log_debug("%s: client %p, sent %zu, left %zu", __func__, c,
|
||||
sent, left);
|
||||
}
|
||||
if (left != 0) {
|
||||
c->references++;
|
||||
event_once(-1, EV_TIMEOUT, server_client_stderr_cb, c, NULL);
|
||||
log_debug("%s: client %p, queued", __func__, c);
|
||||
log_debug("%s: file %d read %zu bytes", c->name, cf->stream, bsize);
|
||||
if (cf->error == 0) {
|
||||
if (evbuffer_add(cf->buffer, bdata, bsize) != 0) {
|
||||
cf->error = ENOMEM;
|
||||
file_fire_done(cf);
|
||||
} else
|
||||
file_fire_read(cf);
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle read done message. */
|
||||
static void
|
||||
server_client_dispatch_read_done(struct client *c, struct imsg *imsg)
|
||||
{
|
||||
struct msg_read_done *msg = imsg->data;
|
||||
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
|
||||
struct client_file find, *cf;
|
||||
|
||||
if (msglen != sizeof *msg)
|
||||
fatalx("bad MSG_READ_DONE size");
|
||||
find.stream = msg->stream;
|
||||
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL)
|
||||
return;
|
||||
|
||||
log_debug("%s: file %d read done", c->name, cf->stream);
|
||||
cf->error = msg->error;
|
||||
file_fire_done(cf);
|
||||
}
|
||||
|
||||
/* Add to client message log. */
|
||||
void
|
||||
server_client_add_message(struct client *c, const char *fmt, ...)
|
||||
|
30
server-fn.c
30
server-fn.c
@ -440,36 +440,6 @@ server_check_unattached(void)
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int,
|
||||
void *), void *cb_data, char **cause)
|
||||
{
|
||||
if (c == NULL || c->session != NULL) {
|
||||
*cause = xstrdup("no client with stdin");
|
||||
return (-1);
|
||||
}
|
||||
if (c->flags & CLIENT_TERMINAL) {
|
||||
*cause = xstrdup("stdin is a tty");
|
||||
return (-1);
|
||||
}
|
||||
if (c->stdin_callback != NULL) {
|
||||
*cause = xstrdup("stdin is in use");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
c->stdin_callback_data = cb_data;
|
||||
c->stdin_callback = cb;
|
||||
|
||||
c->references++;
|
||||
|
||||
if (c->stdin_closed)
|
||||
c->stdin_callback(c, 1, c->stdin_callback_data);
|
||||
|
||||
proc_send(c->peer, MSG_STDIN, -1, NULL, 0);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
server_unzoom_window(struct window *w)
|
||||
{
|
||||
|
112
tmux.h
112
tmux.h
@ -480,13 +480,21 @@ enum msgtype {
|
||||
MSG_RESIZE,
|
||||
MSG_SHELL,
|
||||
MSG_SHUTDOWN,
|
||||
MSG_STDERR,
|
||||
MSG_STDIN,
|
||||
MSG_STDOUT,
|
||||
MSG_OLDSTDERR, /* unused */
|
||||
MSG_OLDSTDIN, /* unused */
|
||||
MSG_OLDSTDOUT, /* unused */
|
||||
MSG_SUSPEND,
|
||||
MSG_UNLOCK,
|
||||
MSG_WAKEUP,
|
||||
MSG_EXEC,
|
||||
|
||||
MSG_READ_OPEN = 300,
|
||||
MSG_READ,
|
||||
MSG_READ_DONE,
|
||||
MSG_WRITE_OPEN,
|
||||
MSG_WRITE,
|
||||
MSG_WRITE_READY,
|
||||
MSG_WRITE_CLOSE
|
||||
};
|
||||
|
||||
/*
|
||||
@ -494,23 +502,47 @@ enum msgtype {
|
||||
*
|
||||
* Don't forget to bump PROTOCOL_VERSION if any of these change!
|
||||
*/
|
||||
struct msg_command_data {
|
||||
struct msg_command {
|
||||
int argc;
|
||||
}; /* followed by packed argv */
|
||||
|
||||
struct msg_stdin_data {
|
||||
ssize_t size;
|
||||
struct msg_read_open {
|
||||
int stream;
|
||||
int fd;
|
||||
char path[PATH_MAX];
|
||||
};
|
||||
|
||||
struct msg_read_data {
|
||||
int stream;
|
||||
size_t size;
|
||||
char data[BUFSIZ];
|
||||
};
|
||||
|
||||
struct msg_stdout_data {
|
||||
ssize_t size;
|
||||
struct msg_read_done {
|
||||
int stream;
|
||||
int error;
|
||||
};
|
||||
|
||||
struct msg_write_open {
|
||||
int stream;
|
||||
int fd;
|
||||
char path[PATH_MAX];
|
||||
int flags;
|
||||
};
|
||||
|
||||
struct msg_write_data {
|
||||
int stream;
|
||||
size_t size;
|
||||
char data[BUFSIZ];
|
||||
};
|
||||
|
||||
struct msg_stderr_data {
|
||||
ssize_t size;
|
||||
char data[BUFSIZ];
|
||||
struct msg_write_ready {
|
||||
int stream;
|
||||
int error;
|
||||
};
|
||||
|
||||
struct msg_write_close {
|
||||
int stream;
|
||||
};
|
||||
|
||||
/* Mode keys. */
|
||||
@ -1476,6 +1508,29 @@ struct status_line {
|
||||
struct status_line_entry entries[STATUS_LINES_LIMIT];
|
||||
};
|
||||
|
||||
/* File in client. */
|
||||
typedef void (*client_file_cb) (struct client *, const char *, int, int,
|
||||
struct evbuffer *, void *);
|
||||
struct client_file {
|
||||
struct client *c;
|
||||
int references;
|
||||
int stream;
|
||||
|
||||
char *path;
|
||||
struct evbuffer *buffer;
|
||||
struct bufferevent *event;
|
||||
|
||||
int fd;
|
||||
int error;
|
||||
int closed;
|
||||
|
||||
client_file_cb cb;
|
||||
void *data;
|
||||
|
||||
RB_ENTRY (client_file) entry;
|
||||
};
|
||||
RB_HEAD(client_files, client_file);
|
||||
|
||||
/* Client connection. */
|
||||
typedef int (*prompt_input_cb)(struct client *, void *, const char *, int);
|
||||
typedef void (*prompt_free_cb)(void *);
|
||||
@ -1509,13 +1564,6 @@ struct client {
|
||||
size_t discarded;
|
||||
size_t redraw;
|
||||
|
||||
void (*stdin_callback)(struct client *, int, void *);
|
||||
void *stdin_callback_data;
|
||||
struct evbuffer *stdin_data;
|
||||
int stdin_closed;
|
||||
struct evbuffer *stdout_data;
|
||||
struct evbuffer *stderr_data;
|
||||
|
||||
struct event repeat_timer;
|
||||
|
||||
struct event click_timer;
|
||||
@ -1599,6 +1647,8 @@ struct client {
|
||||
void *overlay_data;
|
||||
struct event overlay_timer;
|
||||
|
||||
struct client_files files;
|
||||
|
||||
TAILQ_ENTRY(client) entry;
|
||||
};
|
||||
TAILQ_HEAD(clients, client);
|
||||
@ -1755,6 +1805,8 @@ extern struct client *cfg_client;
|
||||
void start_cfg(void);
|
||||
int load_cfg(const char *, struct client *, struct cmdq_item *, int,
|
||||
struct cmdq_item **);
|
||||
int load_cfg_from_buffer(const void *, size_t, const char *,
|
||||
struct client *, struct cmdq_item *, int, struct cmdq_item **);
|
||||
void set_cfg_file(const char *);
|
||||
void printflike(1, 2) cfg_add_cause(const char *, ...);
|
||||
void cfg_print_causes(struct cmdq_item *);
|
||||
@ -2072,6 +2124,8 @@ void cmd_parse_empty(struct cmd_parse_input *);
|
||||
struct cmd_parse_result *cmd_parse_from_file(FILE *, struct cmd_parse_input *);
|
||||
struct cmd_parse_result *cmd_parse_from_string(const char *,
|
||||
struct cmd_parse_input *);
|
||||
struct cmd_parse_result *cmd_parse_from_buffer(const void *, size_t,
|
||||
struct cmd_parse_input *);
|
||||
struct cmd_parse_result *cmd_parse_from_arguments(int, char **,
|
||||
struct cmd_parse_input *);
|
||||
|
||||
@ -2131,6 +2185,23 @@ void alerts_reset_all(void);
|
||||
void alerts_queue(struct window *, int);
|
||||
void alerts_check_session(struct session *);
|
||||
|
||||
/* file.c */
|
||||
int file_cmp(struct client_file *, struct client_file *);
|
||||
RB_PROTOTYPE(client_files, client_file, entry, file_cmp);
|
||||
struct client_file *file_create(struct client *, int, client_file_cb, void *);
|
||||
void file_free(struct client_file *);
|
||||
void file_fire_done(struct client_file *);
|
||||
void file_fire_read(struct client_file *);
|
||||
int file_can_print(struct client *);
|
||||
void printflike(2, 3) file_print(struct client *, const char *, ...);
|
||||
void file_vprint(struct client *, const char *, va_list);
|
||||
void file_print_buffer(struct client *, void *, size_t);
|
||||
void printflike(2, 3) file_error(struct client *, const char *, ...);
|
||||
void file_write(struct client *, const char *, int, const void *, size_t,
|
||||
client_file_cb, void *);
|
||||
void file_read(struct client *, const char *, client_file_cb, void *);
|
||||
void file_push(struct client_file *);
|
||||
|
||||
/* server.c */
|
||||
extern struct tmuxproc *server_proc;
|
||||
extern struct clients clients;
|
||||
@ -2189,8 +2260,6 @@ void server_unlink_window(struct session *, struct winlink *);
|
||||
void server_destroy_pane(struct window_pane *, int);
|
||||
void server_destroy_session(struct session *);
|
||||
void server_check_unattached(void);
|
||||
int server_set_stdin_callback(struct client *, void (*)(struct client *,
|
||||
int, void *), void *, char **);
|
||||
void server_unzoom_window(struct window *);
|
||||
|
||||
/* status.c */
|
||||
@ -2575,9 +2644,8 @@ char *default_window_name(struct window *);
|
||||
char *parse_window_name(const char *);
|
||||
|
||||
/* control.c */
|
||||
void control_callback(struct client *, int, void *);
|
||||
void control_start(struct client *);
|
||||
void printflike(2, 3) control_write(struct client *, const char *, ...);
|
||||
void control_write_buffer(struct client *, struct evbuffer *);
|
||||
|
||||
/* control-notify.c */
|
||||
void control_notify_input(struct client *, struct window_pane *,
|
||||
|
27
window.c
27
window.c
@ -1608,31 +1608,28 @@ winlink_shuffle_up(struct session *s, struct winlink *wl)
|
||||
}
|
||||
|
||||
static void
|
||||
window_pane_input_callback(struct client *c, int closed, void *data)
|
||||
window_pane_input_callback(struct client *c, __unused const char *path,
|
||||
int error, int closed, struct evbuffer *buffer, void *data)
|
||||
{
|
||||
struct window_pane_input_data *cdata = data;
|
||||
struct window_pane *wp;
|
||||
struct evbuffer *evb = c->stdin_data;
|
||||
u_char *buf = EVBUFFER_DATA(evb);
|
||||
size_t len = EVBUFFER_LENGTH(evb);
|
||||
u_char *buf = EVBUFFER_DATA(buffer);
|
||||
size_t len = EVBUFFER_LENGTH(buffer);
|
||||
|
||||
wp = window_pane_find_by_id(cdata->wp);
|
||||
if (wp == NULL || closed || c->flags & CLIENT_DEAD) {
|
||||
if (wp == NULL || closed || error != 0 || c->flags & CLIENT_DEAD) {
|
||||
if (wp == NULL)
|
||||
c->flags |= CLIENT_EXIT;
|
||||
evbuffer_drain(evb, len);
|
||||
|
||||
c->stdin_callback = NULL;
|
||||
server_client_unref(c);
|
||||
|
||||
evbuffer_drain(buffer, len);
|
||||
cmdq_continue(cdata->item);
|
||||
free(cdata);
|
||||
|
||||
server_client_unref(c);
|
||||
free(cdata);
|
||||
return;
|
||||
}
|
||||
|
||||
input_parse_buffer(wp, buf, len);
|
||||
evbuffer_drain(evb, len);
|
||||
evbuffer_drain(buffer, len);
|
||||
}
|
||||
|
||||
int
|
||||
@ -1651,6 +1648,8 @@ window_pane_start_input(struct window_pane *wp, struct cmdq_item *item,
|
||||
cdata->item = item;
|
||||
cdata->wp = wp->id;
|
||||
|
||||
return (server_set_stdin_callback(c, window_pane_input_callback, cdata,
|
||||
cause));
|
||||
c->references++;
|
||||
file_read(c, "-", window_pane_input_callback, cdata);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user