mirror of https://github.com/tmux/tmux.git
Initial changes to move tmux to libevent.
This moves the client-side loops are pretty much fully over to event-based only (tmux.c and client.c) but server-side (server.c and friends) treats libevent as a sort of clever poll, waking up after every event to run various things. Moving the server stuff over to bufferevents and timers and so on will come later.pull/1/head
parent
f575e39b0a
commit
abf3a5d50e
2
Makefile
2
Makefile
|
@ -45,7 +45,7 @@ CDIAGFLAGS+= -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations
|
||||||
CDIAGFLAGS+= -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare
|
CDIAGFLAGS+= -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare
|
||||||
CDIAGFLAGS+= -Wundef -Wbad-function-cast -Winline -Wcast-align
|
CDIAGFLAGS+= -Wundef -Wbad-function-cast -Winline -Wcast-align
|
||||||
|
|
||||||
LDADD= -lutil -lcurses
|
LDADD= -lutil -lcurses -levent
|
||||||
DPADD= ${LIBUTIL}
|
DPADD= ${LIBUTIL}
|
||||||
|
|
||||||
.include <bsd.prog.mk>
|
.include <bsd.prog.mk>
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <event.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "tmux.h"
|
#include "tmux.h"
|
||||||
|
@ -29,9 +30,7 @@ buffer_poll(int fd, int events, struct buffer *in, struct buffer *out)
|
||||||
{
|
{
|
||||||
ssize_t n;
|
ssize_t n;
|
||||||
|
|
||||||
if (events & (POLLERR|POLLNVAL))
|
if (in != NULL && events & EV_READ) {
|
||||||
return (-1);
|
|
||||||
if (in != NULL && events & POLLIN) {
|
|
||||||
buffer_ensure(in, BUFSIZ);
|
buffer_ensure(in, BUFSIZ);
|
||||||
n = read(fd, BUFFER_IN(in), BUFFER_FREE(in));
|
n = read(fd, BUFFER_IN(in), BUFFER_FREE(in));
|
||||||
if (n == 0)
|
if (n == 0)
|
||||||
|
@ -41,9 +40,8 @@ buffer_poll(int fd, int events, struct buffer *in, struct buffer *out)
|
||||||
return (-1);
|
return (-1);
|
||||||
} else
|
} else
|
||||||
buffer_add(in, n);
|
buffer_add(in, n);
|
||||||
} else if (events & POLLHUP)
|
}
|
||||||
return (-1);
|
if (out != NULL && BUFFER_USED(out) > 0 && events & EV_WRITE) {
|
||||||
if (out != NULL && BUFFER_USED(out) > 0 && events & POLLOUT) {
|
|
||||||
n = write(fd, BUFFER_OUT(out), BUFFER_USED(out));
|
n = write(fd, BUFFER_OUT(out), BUFFER_USED(out));
|
||||||
if (n == -1) {
|
if (n == -1) {
|
||||||
if (errno != EINTR && errno != EAGAIN)
|
if (errno != EINTR && errno != EAGAIN)
|
||||||
|
|
214
client.c
214
client.c
|
@ -24,6 +24,7 @@
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <event.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
@ -34,13 +35,15 @@
|
||||||
#include "tmux.h"
|
#include "tmux.h"
|
||||||
|
|
||||||
struct imsgbuf client_ibuf;
|
struct imsgbuf client_ibuf;
|
||||||
|
struct event client_event;
|
||||||
const char *client_exitmsg;
|
const char *client_exitmsg;
|
||||||
|
|
||||||
void client_send_identify(int);
|
void client_send_identify(int);
|
||||||
void client_send_environ(void);
|
void client_send_environ(void);
|
||||||
void client_write_server(enum msgtype, void *, size_t);
|
void client_write_server(enum msgtype, void *, size_t);
|
||||||
int client_dispatch(void);
|
void client_signal(int, short, void *);
|
||||||
void client_suspend(void);
|
void client_callback(int, short, void *);
|
||||||
|
int client_dispatch(void);
|
||||||
|
|
||||||
struct imsgbuf *
|
struct imsgbuf *
|
||||||
client_init(char *path, int cmdflags, int flags)
|
client_init(char *path, int cmdflags, int flags)
|
||||||
|
@ -154,76 +157,54 @@ client_write_server(enum msgtype type, void *buf, size_t len)
|
||||||
__dead void
|
__dead void
|
||||||
client_main(void)
|
client_main(void)
|
||||||
{
|
{
|
||||||
struct pollfd pfd;
|
struct event ev_sigcont, ev_sigterm, ev_sigwinch;
|
||||||
int n, nfds;
|
struct sigaction sigact;
|
||||||
|
short events;
|
||||||
siginit();
|
|
||||||
|
|
||||||
logfile("client");
|
logfile("client");
|
||||||
|
|
||||||
|
/* Note: event_init() has already been called. */
|
||||||
|
|
||||||
|
/* Set up signals. */
|
||||||
|
memset(&sigact, 0, sizeof sigact);
|
||||||
|
sigemptyset(&sigact.sa_mask);
|
||||||
|
sigact.sa_flags = SA_RESTART;
|
||||||
|
sigact.sa_handler = SIG_IGN;
|
||||||
|
if (sigaction(SIGINT, &sigact, NULL) != 0)
|
||||||
|
fatal("sigaction failed");
|
||||||
|
if (sigaction(SIGPIPE, &sigact, NULL) != 0)
|
||||||
|
fatal("sigaction failed");
|
||||||
|
if (sigaction(SIGUSR1, &sigact, NULL) != 0)
|
||||||
|
fatal("sigaction failed");
|
||||||
|
if (sigaction(SIGUSR2, &sigact, NULL) != 0)
|
||||||
|
fatal("sigaction failed");
|
||||||
|
if (sigaction(SIGTSTP, &sigact, NULL) != 0)
|
||||||
|
fatal("sigaction failed");
|
||||||
|
|
||||||
|
signal_set(&ev_sigcont, SIGCONT, client_signal, NULL);
|
||||||
|
signal_add(&ev_sigcont, NULL);
|
||||||
|
signal_set(&ev_sigterm, SIGTERM, client_signal, NULL);
|
||||||
|
signal_add(&ev_sigterm, NULL);
|
||||||
|
signal_set(&ev_sigwinch, SIGWINCH, client_signal, NULL);
|
||||||
|
signal_add(&ev_sigwinch, NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* imsg_read in the first client poll loop (before the terminal has
|
* imsg_read in the first client poll loop (before the terminal has
|
||||||
* been initialiased) may have read messages into the buffer after the
|
* been initialised) may have read messages into the buffer after the
|
||||||
* MSG_READY switched to here. Process anything outstanding now so poll
|
* MSG_READY switched to here. Process anything outstanding now to
|
||||||
* doesn't hang waiting for messages that have already arrived.
|
* avoid hanging waiting for messages that have already arrived.
|
||||||
*/
|
*/
|
||||||
if (client_dispatch() != 0)
|
if (client_dispatch() != 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
for (;;) {
|
/* Set up the client-server socket event. */
|
||||||
if (sigterm) {
|
events = EV_READ;
|
||||||
client_exitmsg = "terminated";
|
if (client_ibuf.w.queued > 0)
|
||||||
client_write_server(MSG_EXITING, NULL, 0);
|
events |= EV_WRITE;
|
||||||
}
|
event_set(&client_event, client_ibuf.fd, events, client_callback, NULL);
|
||||||
if (sigchld) {
|
event_add(&client_event, NULL);
|
||||||
sigchld = 0;
|
|
||||||
waitpid(WAIT_ANY, NULL, WNOHANG);
|
event_dispatch();
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (sigwinch) {
|
|
||||||
sigwinch = 0;
|
|
||||||
client_write_server(MSG_RESIZE, NULL, 0);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (sigcont) {
|
|
||||||
sigcont = 0;
|
|
||||||
siginit();
|
|
||||||
client_write_server(MSG_WAKEUP, NULL, 0);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
pfd.fd = client_ibuf.fd;
|
|
||||||
pfd.events = POLLIN;
|
|
||||||
if (client_ibuf.w.queued > 0)
|
|
||||||
pfd.events |= POLLOUT;
|
|
||||||
|
|
||||||
if ((nfds = poll(&pfd, 1, INFTIM)) == -1) {
|
|
||||||
if (errno == EAGAIN || errno == EINTR)
|
|
||||||
continue;
|
|
||||||
fatal("poll failed");
|
|
||||||
}
|
|
||||||
if (nfds == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (pfd.revents & (POLLERR|POLLHUP|POLLNVAL))
|
|
||||||
fatalx("socket error");
|
|
||||||
|
|
||||||
if (pfd.revents & POLLIN) {
|
|
||||||
if ((n = imsg_read(&client_ibuf)) == -1 || n == 0) {
|
|
||||||
client_exitmsg = "lost server";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (client_dispatch() != 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pfd.revents & POLLOUT) {
|
|
||||||
if (msgbuf_write(&client_ibuf.w) < 0) {
|
|
||||||
client_exitmsg = "lost server";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
out:
|
||||||
/* Print the exit message, if any, and exit. */
|
/* Print the exit message, if any, and exit. */
|
||||||
|
@ -235,12 +216,78 @@ out:
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
client_signal(int sig, short events, unused void *data)
|
||||||
|
{
|
||||||
|
struct sigaction sigact;
|
||||||
|
|
||||||
|
switch (sig) {
|
||||||
|
case SIGTERM:
|
||||||
|
client_exitmsg = "terminated";
|
||||||
|
client_write_server(MSG_EXITING, NULL, 0);
|
||||||
|
break;
|
||||||
|
case SIGWINCH:
|
||||||
|
client_write_server(MSG_RESIZE, NULL, 0);
|
||||||
|
break;
|
||||||
|
case SIGCONT:
|
||||||
|
memset(&sigact, 0, sizeof sigact);
|
||||||
|
sigemptyset(&sigact.sa_mask);
|
||||||
|
sigact.sa_flags = SA_RESTART;
|
||||||
|
sigact.sa_handler = SIG_IGN;
|
||||||
|
if (sigaction(SIGTSTP, &sigact, NULL) != 0)
|
||||||
|
fatal("sigaction failed");
|
||||||
|
client_write_server(MSG_WAKEUP, NULL, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
event_del(&client_event);
|
||||||
|
events = EV_READ;
|
||||||
|
if (client_ibuf.w.queued > 0)
|
||||||
|
events |= EV_WRITE;
|
||||||
|
event_set(&client_event, client_ibuf.fd, events, client_callback, NULL);
|
||||||
|
event_add(&client_event, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
client_callback(unused int fd, short events, unused void *data)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
|
||||||
|
if (events & EV_READ) {
|
||||||
|
if ((n = imsg_read(&client_ibuf)) == -1 || n == 0)
|
||||||
|
goto lost_server;
|
||||||
|
if (client_dispatch() != 0) {
|
||||||
|
event_loopexit(NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (events & EV_WRITE) {
|
||||||
|
if (msgbuf_write(&client_ibuf.w) < 0)
|
||||||
|
goto lost_server;
|
||||||
|
}
|
||||||
|
|
||||||
|
event_del(&client_event);
|
||||||
|
events = EV_READ;
|
||||||
|
if (client_ibuf.w.queued > 0)
|
||||||
|
events |= EV_WRITE;
|
||||||
|
event_set(&client_event, client_ibuf.fd, events, client_callback, NULL);
|
||||||
|
event_add(&client_event, NULL);
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
lost_server:
|
||||||
|
client_exitmsg = "lost server";
|
||||||
|
event_loopexit(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
client_dispatch(void)
|
client_dispatch(void)
|
||||||
{
|
{
|
||||||
struct imsg imsg;
|
struct imsg imsg;
|
||||||
struct msg_lock_data lockdata;
|
struct msg_lock_data lockdata;
|
||||||
ssize_t n, datalen;
|
struct sigaction sigact;
|
||||||
|
ssize_t n, datalen;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if ((n = imsg_get(&client_ibuf, &imsg)) == -1)
|
if ((n = imsg_get(&client_ibuf, &imsg)) == -1)
|
||||||
|
@ -249,6 +296,7 @@ client_dispatch(void)
|
||||||
return (0);
|
return (0);
|
||||||
datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
|
datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
|
||||||
|
|
||||||
|
log_debug("client got %d", imsg.hdr.type);
|
||||||
switch (imsg.hdr.type) {
|
switch (imsg.hdr.type) {
|
||||||
case MSG_DETACH:
|
case MSG_DETACH:
|
||||||
if (datalen != 0)
|
if (datalen != 0)
|
||||||
|
@ -281,7 +329,13 @@ client_dispatch(void)
|
||||||
if (datalen != 0)
|
if (datalen != 0)
|
||||||
fatalx("bad MSG_SUSPEND size");
|
fatalx("bad MSG_SUSPEND size");
|
||||||
|
|
||||||
client_suspend();
|
memset(&sigact, 0, sizeof sigact);
|
||||||
|
sigemptyset(&sigact.sa_mask);
|
||||||
|
sigact.sa_flags = SA_RESTART;
|
||||||
|
sigact.sa_handler = SIG_DFL;
|
||||||
|
if (sigaction(SIGTSTP, &sigact, NULL) != 0)
|
||||||
|
fatal("sigaction failed");
|
||||||
|
kill(getpid(), SIGTSTP);
|
||||||
break;
|
break;
|
||||||
case MSG_LOCK:
|
case MSG_LOCK:
|
||||||
if (datalen != sizeof lockdata)
|
if (datalen != sizeof lockdata)
|
||||||
|
@ -299,23 +353,3 @@ client_dispatch(void)
|
||||||
imsg_free(&imsg);
|
imsg_free(&imsg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
client_suspend(void)
|
|
||||||
{
|
|
||||||
struct sigaction act;
|
|
||||||
|
|
||||||
memset(&act, 0, sizeof act);
|
|
||||||
sigemptyset(&act.sa_mask);
|
|
||||||
act.sa_flags = SA_RESTART;
|
|
||||||
|
|
||||||
act.sa_handler = SIG_DFL;
|
|
||||||
if (sigaction(SIGTSTP, &act, NULL) != 0)
|
|
||||||
fatal("sigaction failed");
|
|
||||||
|
|
||||||
act.sa_handler = sighandler;
|
|
||||||
if (sigaction(SIGCONT, &act, NULL) != 0)
|
|
||||||
fatal("sigaction failed");
|
|
||||||
|
|
||||||
kill(getpid(), SIGTSTP);
|
|
||||||
}
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ const struct cmd_entry cmd_kill_server_entry = {
|
||||||
int
|
int
|
||||||
cmd_kill_server_exec(unused struct cmd *self, unused struct cmd_ctx *ctx)
|
cmd_kill_server_exec(unused struct cmd *self, unused struct cmd_ctx *ctx)
|
||||||
{
|
{
|
||||||
sigterm = 1;
|
kill(getpid(), SIGTERM);
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,7 +88,7 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
|
||||||
case 0:
|
case 0:
|
||||||
/* Child process. */
|
/* Child process. */
|
||||||
close(pipe_fd[0]);
|
close(pipe_fd[0]);
|
||||||
sigreset();
|
server_signal_clear();
|
||||||
|
|
||||||
if (dup2(pipe_fd[1], STDIN_FILENO) == -1)
|
if (dup2(pipe_fd[1], STDIN_FILENO) == -1)
|
||||||
_exit(1);
|
_exit(1);
|
||||||
|
|
4
job.c
4
job.c
|
@ -87,6 +87,7 @@ job_add(struct jobs *jobs, int flags, struct client *c, const char *cmd,
|
||||||
|
|
||||||
job->fd = -1;
|
job->fd = -1;
|
||||||
job->out = buffer_create(BUFSIZ);
|
job->out = buffer_create(BUFSIZ);
|
||||||
|
memset(&job->event, 0, sizeof job->event);
|
||||||
|
|
||||||
job->callbackfn = callbackfn;
|
job->callbackfn = callbackfn;
|
||||||
job->freefn = freefn;
|
job->freefn = freefn;
|
||||||
|
@ -126,6 +127,7 @@ job_free(struct job *job)
|
||||||
close(job->fd);
|
close(job->fd);
|
||||||
if (job->out != NULL)
|
if (job->out != NULL)
|
||||||
buffer_destroy(job->out);
|
buffer_destroy(job->out);
|
||||||
|
event_del(&job->event);
|
||||||
|
|
||||||
xfree(job);
|
xfree(job);
|
||||||
}
|
}
|
||||||
|
@ -147,7 +149,7 @@ job_run(struct job *job)
|
||||||
case -1:
|
case -1:
|
||||||
return (-1);
|
return (-1);
|
||||||
case 0: /* child */
|
case 0: /* child */
|
||||||
sigreset();
|
server_signal_clear();
|
||||||
/* XXX environ? */
|
/* XXX environ? */
|
||||||
|
|
||||||
if (dup2(out[1], STDOUT_FILENO) == -1)
|
if (dup2(out[1], STDOUT_FILENO) == -1)
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <event.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
@ -134,7 +135,9 @@ server_client_lost(struct client *c)
|
||||||
|
|
||||||
close(c->ibuf.fd);
|
close(c->ibuf.fd);
|
||||||
imsg_clear(&c->ibuf);
|
imsg_clear(&c->ibuf);
|
||||||
|
event_del(&c->event);
|
||||||
|
event_del(&c->tty.event);
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) {
|
for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) {
|
||||||
if (ARRAY_ITEM(&dead_clients, i) == NULL) {
|
if (ARRAY_ITEM(&dead_clients, i) == NULL) {
|
||||||
ARRAY_SET(&dead_clients, i, c);
|
ARRAY_SET(&dead_clients, i, c);
|
||||||
|
@ -162,25 +165,31 @@ server_client_prepare(void)
|
||||||
|
|
||||||
events = 0;
|
events = 0;
|
||||||
if (!(c->flags & CLIENT_BAD))
|
if (!(c->flags & CLIENT_BAD))
|
||||||
events |= POLLIN;
|
events |= EV_READ;
|
||||||
if (c->ibuf.w.queued > 0)
|
if (c->ibuf.w.queued > 0)
|
||||||
events |= POLLOUT;
|
events |= EV_WRITE;
|
||||||
server_poll_add(c->ibuf.fd, events, server_client_callback, c);
|
event_del(&c->event);
|
||||||
|
event_set(&c->event,
|
||||||
|
c->ibuf.fd, events, server_client_callback, c);
|
||||||
|
event_add(&c->event, NULL);
|
||||||
|
|
||||||
if (c->tty.fd == -1)
|
if (c->tty.fd == -1)
|
||||||
continue;
|
continue;
|
||||||
if (c->flags & CLIENT_SUSPENDED || c->session == NULL)
|
if (c->flags & CLIENT_SUSPENDED || c->session == NULL)
|
||||||
continue;
|
continue;
|
||||||
events = POLLIN;
|
events = EV_READ;
|
||||||
if (BUFFER_USED(c->tty.out) > 0)
|
if (BUFFER_USED(c->tty.out) > 0)
|
||||||
events |= POLLOUT;
|
events |= EV_WRITE;
|
||||||
server_poll_add(c->tty.fd, events, server_client_callback, c);
|
event_del(&c->tty.event);
|
||||||
|
event_set(&c->tty.event,
|
||||||
|
c->tty.fd, events, server_client_callback, c);
|
||||||
|
event_add(&c->tty.event, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Process a single client event. */
|
/* Process a single client event. */
|
||||||
void
|
void
|
||||||
server_client_callback(int fd, int events, void *data)
|
server_client_callback(int fd, short events, void *data)
|
||||||
{
|
{
|
||||||
struct client *c = data;
|
struct client *c = data;
|
||||||
|
|
||||||
|
@ -188,10 +197,7 @@ server_client_callback(int fd, int events, void *data)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (fd == c->ibuf.fd) {
|
if (fd == c->ibuf.fd) {
|
||||||
if (events & (POLLERR|POLLNVAL|POLLHUP))
|
if (events & EV_WRITE && msgbuf_write(&c->ibuf.w) < 0)
|
||||||
goto client_lost;
|
|
||||||
|
|
||||||
if (events & POLLOUT && msgbuf_write(&c->ibuf.w) < 0)
|
|
||||||
goto client_lost;
|
goto client_lost;
|
||||||
|
|
||||||
if (c->flags & CLIENT_BAD) {
|
if (c->flags & CLIENT_BAD) {
|
||||||
|
@ -200,7 +206,7 @@ server_client_callback(int fd, int events, void *data)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (events & POLLIN && server_client_msg_dispatch(c) != 0)
|
if (events & EV_READ && server_client_msg_dispatch(c) != 0)
|
||||||
goto client_lost;
|
goto client_lost;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,7 +217,7 @@ server_client_callback(int fd, int events, void *data)
|
||||||
if (buffer_poll(fd, events, c->tty.in, c->tty.out) != 0)
|
if (buffer_poll(fd, events, c->tty.in, c->tty.out) != 0)
|
||||||
goto client_lost;
|
goto client_lost;
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
client_lost:
|
client_lost:
|
||||||
|
@ -424,7 +430,7 @@ server_client_check_redraw(struct client *c)
|
||||||
if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) {
|
if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) {
|
||||||
if (options_get_number(&s->options, "set-titles"))
|
if (options_get_number(&s->options, "set-titles"))
|
||||||
server_client_set_title(c);
|
server_client_set_title(c);
|
||||||
|
|
||||||
if (c->message_string != NULL)
|
if (c->message_string != NULL)
|
||||||
redraw = status_message_redraw(c);
|
redraw = status_message_redraw(c);
|
||||||
else if (c->prompt_string != NULL)
|
else if (c->prompt_string != NULL)
|
||||||
|
|
10
server-job.c
10
server-job.c
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <event.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "tmux.h"
|
#include "tmux.h"
|
||||||
|
@ -31,13 +32,16 @@ server_job_prepare(void)
|
||||||
SLIST_FOREACH(job, &all_jobs, lentry) {
|
SLIST_FOREACH(job, &all_jobs, lentry) {
|
||||||
if (job->fd == -1)
|
if (job->fd == -1)
|
||||||
continue;
|
continue;
|
||||||
server_poll_add(job->fd, POLLIN, server_job_callback, job);
|
event_del(&job->event);
|
||||||
|
event_set(
|
||||||
|
&job->event, job->fd, EV_READ, server_job_callback, job);
|
||||||
|
event_add(&job->event, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Process a single job event. */
|
/* Process a single job event. */
|
||||||
void
|
void
|
||||||
server_job_callback(int fd, int events, void *data)
|
server_job_callback(int fd, short events, void *data)
|
||||||
{
|
{
|
||||||
struct job *job = data;
|
struct job *job = data;
|
||||||
|
|
||||||
|
@ -55,7 +59,7 @@ void
|
||||||
server_job_loop(void)
|
server_job_loop(void)
|
||||||
{
|
{
|
||||||
struct job *job;
|
struct job *job;
|
||||||
|
|
||||||
restart:
|
restart:
|
||||||
SLIST_FOREACH(job, &all_jobs, lentry) {
|
SLIST_FOREACH(job, &all_jobs, lentry) {
|
||||||
if (job->flags & JOB_DONE || job->fd != -1 || job->pid != -1)
|
if (job->flags & JOB_DONE || job->fd != -1 || job->pid != -1)
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <event.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "tmux.h"
|
#include "tmux.h"
|
||||||
|
@ -47,19 +48,23 @@ server_window_prepare(void)
|
||||||
continue;
|
continue;
|
||||||
events = 0;
|
events = 0;
|
||||||
if (!server_window_backoff(wp))
|
if (!server_window_backoff(wp))
|
||||||
events |= POLLIN;
|
events = EV_READ;
|
||||||
if (BUFFER_USED(wp->out) > 0)
|
if (BUFFER_USED(wp->out) > 0)
|
||||||
events |= POLLOUT;
|
events |= EV_WRITE;
|
||||||
server_poll_add(
|
event_del(&wp->event);
|
||||||
|
event_set(&wp->event,
|
||||||
wp->fd, events, server_window_callback, wp);
|
wp->fd, events, server_window_callback, wp);
|
||||||
|
event_add(&wp->event, NULL);
|
||||||
|
|
||||||
if (wp->pipe_fd == -1)
|
if (wp->pipe_fd == -1)
|
||||||
continue;
|
continue;
|
||||||
events = 0;
|
events = 0;
|
||||||
if (BUFFER_USED(wp->pipe_buf) > 0)
|
if (BUFFER_USED(wp->pipe_buf) > 0)
|
||||||
events |= POLLOUT;
|
events |= EV_WRITE;
|
||||||
server_poll_add(
|
event_del(&wp->pipe_event);
|
||||||
|
event_set(&wp->pipe_event,
|
||||||
wp->pipe_fd, events, server_window_callback, wp);
|
wp->pipe_fd, events, server_window_callback, wp);
|
||||||
|
event_add(&wp->pipe_event, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,7 +95,7 @@ server_window_backoff(struct window_pane *wp)
|
||||||
|
|
||||||
/* Process a single window pane event. */
|
/* Process a single window pane event. */
|
||||||
void
|
void
|
||||||
server_window_callback(int fd, int events, void *data)
|
server_window_callback(int fd, short events, void *data)
|
||||||
{
|
{
|
||||||
struct window_pane *wp = data;
|
struct window_pane *wp = data;
|
||||||
|
|
||||||
|
|
601
server.c
601
server.c
|
@ -24,6 +24,7 @@
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <event.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <paths.h>
|
#include <paths.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
@ -45,112 +46,28 @@
|
||||||
struct clients clients;
|
struct clients clients;
|
||||||
struct clients dead_clients;
|
struct clients dead_clients;
|
||||||
|
|
||||||
/* Mapping of a pollfd to an fd independent of its position in the array. */
|
int server_fd;
|
||||||
struct poll_item {
|
int server_shutdown;
|
||||||
int fd;
|
struct event server_ev_accept;
|
||||||
int events;
|
struct event server_ev_sigterm;
|
||||||
|
struct event server_ev_sigusr1;
|
||||||
void (*fn)(int, int, void *);
|
struct event server_ev_sigchld;
|
||||||
void *data;
|
struct event server_ev_second;
|
||||||
|
|
||||||
RB_ENTRY(poll_item) entry;
|
|
||||||
};
|
|
||||||
RB_HEAD(poll_items, poll_item) poll_items;
|
|
||||||
|
|
||||||
int server_poll_cmp(struct poll_item *, struct poll_item *);
|
|
||||||
struct poll_item*server_poll_lookup(int);
|
|
||||||
struct pollfd *server_poll_flatten(int *);
|
|
||||||
void server_poll_dispatch(struct pollfd *, int);
|
|
||||||
void server_poll_reset(void);
|
|
||||||
RB_PROTOTYPE(poll_items, poll_item, entry, server_poll_cmp);
|
|
||||||
RB_GENERATE(poll_items, poll_item, entry, server_poll_cmp);
|
|
||||||
|
|
||||||
int server_create_socket(void);
|
int server_create_socket(void);
|
||||||
void server_callback(int, int, void *);
|
void server_loop(void);
|
||||||
int server_main(int);
|
|
||||||
void server_shutdown(void);
|
|
||||||
int server_should_shutdown(void);
|
int server_should_shutdown(void);
|
||||||
void server_child_signal(void);
|
void server_send_shutdown(void);
|
||||||
void server_clean_dead(void);
|
void server_clean_dead(void);
|
||||||
void server_second_timers(void);
|
int server_update_socket(void);
|
||||||
|
void server_accept_callback(int, short, void *);
|
||||||
|
void server_signal_callback(int, short, void *);
|
||||||
|
void server_child_signal(void);
|
||||||
|
void server_child_exited(pid_t, int);
|
||||||
|
void server_child_stopped(pid_t, int);
|
||||||
|
void server_second_callback(int, short, void *);
|
||||||
void server_lock_server(void);
|
void server_lock_server(void);
|
||||||
void server_lock_sessions(void);
|
void server_lock_sessions(void);
|
||||||
int server_update_socket(void);
|
|
||||||
|
|
||||||
int
|
|
||||||
server_poll_cmp(struct poll_item *pitem1, struct poll_item *pitem2)
|
|
||||||
{
|
|
||||||
return (pitem1->fd - pitem2->fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
server_poll_add(int fd, int events, void (*fn)(int, int, void *), void *data)
|
|
||||||
{
|
|
||||||
struct poll_item *pitem;
|
|
||||||
|
|
||||||
pitem = xmalloc(sizeof *pitem);
|
|
||||||
pitem->fd = fd;
|
|
||||||
pitem->events = events;
|
|
||||||
|
|
||||||
pitem->fn = fn;
|
|
||||||
pitem->data = data;
|
|
||||||
|
|
||||||
RB_INSERT(poll_items, &poll_items, pitem);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct poll_item *
|
|
||||||
server_poll_lookup(int fd)
|
|
||||||
{
|
|
||||||
struct poll_item pitem;
|
|
||||||
|
|
||||||
pitem.fd = fd;
|
|
||||||
return (RB_FIND(poll_items, &poll_items, &pitem));
|
|
||||||
}
|
|
||||||
|
|
||||||
struct pollfd *
|
|
||||||
server_poll_flatten(int *nfds)
|
|
||||||
{
|
|
||||||
struct poll_item *pitem;
|
|
||||||
struct pollfd *pfds;
|
|
||||||
|
|
||||||
pfds = NULL;
|
|
||||||
*nfds = 0;
|
|
||||||
RB_FOREACH(pitem, poll_items, &poll_items) {
|
|
||||||
pfds = xrealloc(pfds, (*nfds) + 1, sizeof *pfds);
|
|
||||||
pfds[*nfds].fd = pitem->fd;
|
|
||||||
pfds[*nfds].events = pitem->events;
|
|
||||||
(*nfds)++;
|
|
||||||
}
|
|
||||||
return (pfds);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
server_poll_dispatch(struct pollfd *pfds, int nfds)
|
|
||||||
{
|
|
||||||
struct poll_item *pitem;
|
|
||||||
struct pollfd *pfd;
|
|
||||||
|
|
||||||
while (nfds > 0) {
|
|
||||||
pfd = &pfds[--nfds];
|
|
||||||
if (pfd->revents != 0) {
|
|
||||||
pitem = server_poll_lookup(pfd->fd);
|
|
||||||
pitem->fn(pitem->fd, pfd->revents, pitem->data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xfree(pfds);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
server_poll_reset(void)
|
|
||||||
{
|
|
||||||
struct poll_item *pitem;
|
|
||||||
|
|
||||||
while (!RB_EMPTY(&poll_items)) {
|
|
||||||
pitem = RB_ROOT(&poll_items);
|
|
||||||
RB_REMOVE(poll_items, &poll_items, pitem);
|
|
||||||
xfree(pitem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create server socket. */
|
/* Create server socket. */
|
||||||
int
|
int
|
||||||
|
@ -191,40 +108,14 @@ server_create_socket(void)
|
||||||
return (fd);
|
return (fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Callback for server socket. */
|
|
||||||
void
|
|
||||||
server_callback(int fd, int events, unused void *data)
|
|
||||||
{
|
|
||||||
struct sockaddr_storage sa;
|
|
||||||
socklen_t slen = sizeof sa;
|
|
||||||
int newfd;
|
|
||||||
|
|
||||||
if (events & (POLLERR|POLLNVAL|POLLHUP))
|
|
||||||
fatalx("lost server socket");
|
|
||||||
if (!(events & POLLIN))
|
|
||||||
return;
|
|
||||||
|
|
||||||
newfd = accept(fd, (struct sockaddr *) &sa, &slen);
|
|
||||||
if (newfd == -1) {
|
|
||||||
if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED)
|
|
||||||
return;
|
|
||||||
fatal("accept failed");
|
|
||||||
}
|
|
||||||
if (sigterm) {
|
|
||||||
close(newfd);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
server_client_create(newfd);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fork new server. */
|
/* Fork new server. */
|
||||||
int
|
int
|
||||||
server_start(char *path)
|
server_start(char *path)
|
||||||
{
|
{
|
||||||
struct client *c;
|
struct client *c;
|
||||||
int pair[2], srv_fd;
|
int pair[2];
|
||||||
char *cause;
|
char *cause, rpathbuf[MAXPATHLEN];
|
||||||
char rpathbuf[MAXPATHLEN];
|
struct timeval tv;
|
||||||
|
|
||||||
/* The first client is special and gets a socketpair; create it. */
|
/* The first client is special and gets a socketpair; create it. */
|
||||||
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
|
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
|
||||||
|
@ -269,7 +160,7 @@ server_start(char *path)
|
||||||
log_debug("socket path %s", socket_path);
|
log_debug("socket path %s", socket_path);
|
||||||
setproctitle("server (%s)", rpathbuf);
|
setproctitle("server (%s)", rpathbuf);
|
||||||
|
|
||||||
srv_fd = server_create_socket();
|
server_fd = server_create_socket();
|
||||||
server_client_create(pair[1]);
|
server_client_create(pair[1]);
|
||||||
|
|
||||||
if (access(SYSTEM_CFG, R_OK) == 0) {
|
if (access(SYSTEM_CFG, R_OK) == 0) {
|
||||||
|
@ -282,7 +173,20 @@ server_start(char *path)
|
||||||
if (cfg_file != NULL && load_cfg(cfg_file, NULL, &cause) != 0)
|
if (cfg_file != NULL && load_cfg(cfg_file, NULL, &cause) != 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
exit(server_main(srv_fd));
|
event_init();
|
||||||
|
|
||||||
|
event_set(&server_ev_accept,
|
||||||
|
server_fd, EV_READ|EV_PERSIST, server_accept_callback, NULL);
|
||||||
|
event_add(&server_ev_accept, NULL);
|
||||||
|
|
||||||
|
memset(&tv, 0, sizeof tv);
|
||||||
|
tv.tv_sec = 1;
|
||||||
|
evtimer_set(&server_ev_second, server_second_callback, NULL);
|
||||||
|
evtimer_add(&server_ev_second, &tv);
|
||||||
|
|
||||||
|
server_signal_set();
|
||||||
|
server_loop();
|
||||||
|
exit(0);
|
||||||
|
|
||||||
error:
|
error:
|
||||||
/* Write the error and shutdown the server. */
|
/* Write the error and shutdown the server. */
|
||||||
|
@ -291,150 +195,39 @@ error:
|
||||||
server_write_error(c, cause);
|
server_write_error(c, cause);
|
||||||
xfree(cause);
|
xfree(cause);
|
||||||
|
|
||||||
sigterm = 1;
|
server_shutdown = 1;
|
||||||
server_shutdown();
|
|
||||||
|
|
||||||
exit(server_main(srv_fd));
|
server_signal_set();
|
||||||
|
server_loop();
|
||||||
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Main server loop. */
|
/* Main server loop. */
|
||||||
int
|
void
|
||||||
server_main(int srv_fd)
|
server_loop(void)
|
||||||
{
|
{
|
||||||
struct pollfd *pfds;
|
struct timeval tv;
|
||||||
int nfds, xtimeout;
|
|
||||||
u_int i;
|
|
||||||
time_t now, last;
|
|
||||||
|
|
||||||
siginit();
|
memset(&tv, 0, sizeof tv);
|
||||||
log_debug("server socket is %d", srv_fd);
|
tv.tv_usec = POLL_TIMEOUT * 1000;
|
||||||
|
|
||||||
last = time(NULL);
|
while (!server_should_shutdown()) {
|
||||||
|
server_update_socket();
|
||||||
|
|
||||||
pfds = NULL;
|
|
||||||
for (;;) {
|
|
||||||
/* If sigterm, kill all windows and clients. */
|
|
||||||
if (sigterm)
|
|
||||||
server_shutdown();
|
|
||||||
|
|
||||||
/* Stop if no sessions or clients left. */
|
|
||||||
if (server_should_shutdown())
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* Handle child exit. */
|
|
||||||
if (sigchld) {
|
|
||||||
sigchld = 0;
|
|
||||||
server_child_signal();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Recreate socket on SIGUSR1. */
|
|
||||||
if (sigusr1) {
|
|
||||||
sigusr1 = 0;
|
|
||||||
close(srv_fd);
|
|
||||||
srv_fd = server_create_socket();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initialise pollfd array and add server socket. */
|
|
||||||
server_poll_reset();
|
|
||||||
server_poll_add(srv_fd, POLLIN, server_callback, NULL);
|
|
||||||
|
|
||||||
/* Fill window and client sockets. */
|
|
||||||
server_job_prepare();
|
server_job_prepare();
|
||||||
server_window_prepare();
|
server_window_prepare();
|
||||||
server_client_prepare();
|
server_client_prepare();
|
||||||
|
|
||||||
/* Update socket permissions. */
|
|
||||||
xtimeout = INFTIM;
|
|
||||||
if (server_update_socket() != 0)
|
|
||||||
xtimeout = POLL_TIMEOUT;
|
|
||||||
|
|
||||||
/* Do the poll. */
|
event_loopexit(&tv);
|
||||||
pfds = server_poll_flatten(&nfds);
|
event_loop(EVLOOP_ONCE);
|
||||||
if (poll(pfds, nfds, xtimeout) == -1) {
|
|
||||||
if (errno == EAGAIN || errno == EINTR)
|
|
||||||
continue;
|
|
||||||
fatal("poll failed");
|
|
||||||
}
|
|
||||||
server_poll_dispatch(pfds, nfds);
|
|
||||||
|
|
||||||
/* Call second-based timers. */
|
|
||||||
now = time(NULL);
|
|
||||||
if (now != last) {
|
|
||||||
last = now;
|
|
||||||
server_second_timers();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Run once-per-loop events. */
|
|
||||||
server_job_loop();
|
server_job_loop();
|
||||||
server_window_loop();
|
server_window_loop();
|
||||||
server_client_loop();
|
server_client_loop();
|
||||||
|
|
||||||
/* Collect any unset key bindings. */
|
|
||||||
key_bindings_clean();
|
key_bindings_clean();
|
||||||
|
|
||||||
/* Collect dead clients and sessions. */
|
|
||||||
server_clean_dead();
|
server_clean_dead();
|
||||||
}
|
}
|
||||||
server_poll_reset();
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
|
|
||||||
if (ARRAY_ITEM(&sessions, i) != NULL)
|
|
||||||
session_destroy(ARRAY_ITEM(&sessions, i));
|
|
||||||
}
|
|
||||||
ARRAY_FREE(&sessions);
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
|
|
||||||
if (ARRAY_ITEM(&clients, i) != NULL)
|
|
||||||
server_client_lost(ARRAY_ITEM(&clients, i));
|
|
||||||
}
|
|
||||||
ARRAY_FREE(&clients);
|
|
||||||
|
|
||||||
mode_key_free_trees();
|
|
||||||
key_bindings_free();
|
|
||||||
|
|
||||||
close(srv_fd);
|
|
||||||
|
|
||||||
unlink(socket_path);
|
|
||||||
xfree(socket_path);
|
|
||||||
|
|
||||||
options_free(&global_s_options);
|
|
||||||
options_free(&global_w_options);
|
|
||||||
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Kill all clients. */
|
|
||||||
void
|
|
||||||
server_shutdown(void)
|
|
||||||
{
|
|
||||||
struct session *s;
|
|
||||||
struct client *c;
|
|
||||||
u_int i, j;
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
|
|
||||||
c = ARRAY_ITEM(&clients, i);
|
|
||||||
if (c != NULL) {
|
|
||||||
if (c->flags & (CLIENT_BAD|CLIENT_SUSPENDED))
|
|
||||||
server_client_lost(c);
|
|
||||||
else
|
|
||||||
server_write_client(c, MSG_SHUTDOWN, NULL, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
|
|
||||||
s = ARRAY_ITEM(&sessions, i);
|
|
||||||
for (j = 0; j < ARRAY_LENGTH(&clients); j++) {
|
|
||||||
c = ARRAY_ITEM(&clients, j);
|
|
||||||
if (c != NULL && c->session == s) {
|
|
||||||
s = NULL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (s != NULL)
|
|
||||||
session_destroy(s);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if the server should be shutting down (no more clients or windows). */
|
/* Check if the server should be shutting down (no more clients or windows). */
|
||||||
|
@ -454,49 +247,28 @@ server_should_shutdown(void)
|
||||||
return (1);
|
return (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle SIGCHLD. */
|
/* Shutdown the server by killing all clients and windows. */
|
||||||
void
|
void
|
||||||
server_child_signal(void)
|
server_send_shutdown(void)
|
||||||
{
|
{
|
||||||
struct window *w;
|
struct client *c;
|
||||||
struct window_pane *wp;
|
struct session *s;
|
||||||
struct job *job;
|
u_int i;
|
||||||
int status;
|
|
||||||
pid_t pid;
|
|
||||||
u_int i;
|
|
||||||
|
|
||||||
for (;;) {
|
for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
|
||||||
switch (pid = waitpid(WAIT_ANY, &status, WNOHANG|WUNTRACED)) {
|
c = ARRAY_ITEM(&clients, i);
|
||||||
case -1:
|
if (c != NULL) {
|
||||||
if (errno == ECHILD)
|
if (c->flags & (CLIENT_BAD|CLIENT_SUSPENDED))
|
||||||
return;
|
server_client_lost(c);
|
||||||
fatal("waitpid failed");
|
else
|
||||||
case 0:
|
server_write_client(c, MSG_SHUTDOWN, NULL, 0);
|
||||||
return;
|
c->session = NULL;
|
||||||
}
|
}
|
||||||
if (!WIFSTOPPED(status)) {
|
}
|
||||||
SLIST_FOREACH(job, &all_jobs, lentry) {
|
|
||||||
if (pid == job->pid) {
|
|
||||||
job->pid = -1;
|
|
||||||
job->status = status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
|
for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
|
||||||
w = ARRAY_ITEM(&windows, i);
|
if ((s = ARRAY_ITEM(&sessions, i)) != NULL)
|
||||||
if (w == NULL)
|
session_destroy(s);
|
||||||
continue;
|
|
||||||
TAILQ_FOREACH(wp, &w->panes, entry) {
|
|
||||||
if (wp->pid == pid) {
|
|
||||||
if (killpg(pid, SIGCONT) != 0)
|
|
||||||
kill(pid, SIGCONT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -525,12 +297,215 @@ server_clean_dead(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Call any once-per-second timers. */
|
/* Update socket execute permissions based on whether sessions are attached. */
|
||||||
|
int
|
||||||
|
server_update_socket(void)
|
||||||
|
{
|
||||||
|
struct session *s;
|
||||||
|
u_int i;
|
||||||
|
static int last = -1;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
n = 0;
|
||||||
|
for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
|
||||||
|
s = ARRAY_ITEM(&sessions, i);
|
||||||
|
if (s != NULL && !(s->flags & SESSION_UNATTACHED)) {
|
||||||
|
n++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n != last) {
|
||||||
|
last = n;
|
||||||
|
if (n != 0)
|
||||||
|
chmod(socket_path, S_IRWXU);
|
||||||
|
else
|
||||||
|
chmod(socket_path, S_IRUSR|S_IWUSR);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Callback for server socket. */
|
||||||
void
|
void
|
||||||
server_second_timers(void)
|
server_accept_callback(int fd, short events, unused void *data)
|
||||||
|
{
|
||||||
|
struct sockaddr_storage sa;
|
||||||
|
socklen_t slen = sizeof sa;
|
||||||
|
int newfd;
|
||||||
|
|
||||||
|
if (!(events & EV_READ))
|
||||||
|
return;
|
||||||
|
|
||||||
|
newfd = accept(fd, (struct sockaddr *) &sa, &slen);
|
||||||
|
if (newfd == -1) {
|
||||||
|
if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED)
|
||||||
|
return;
|
||||||
|
fatal("accept failed");
|
||||||
|
}
|
||||||
|
if (server_shutdown) {
|
||||||
|
close(newfd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
server_client_create(newfd);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set up server signal handling. */
|
||||||
|
void
|
||||||
|
server_signal_set(void)
|
||||||
|
{
|
||||||
|
struct sigaction sigact;
|
||||||
|
|
||||||
|
memset(&sigact, 0, sizeof sigact);
|
||||||
|
sigemptyset(&sigact.sa_mask);
|
||||||
|
sigact.sa_flags = SA_RESTART;
|
||||||
|
sigact.sa_handler = SIG_IGN;
|
||||||
|
if (sigaction(SIGINT, &sigact, NULL) != 0)
|
||||||
|
fatal("sigaction failed");
|
||||||
|
if (sigaction(SIGPIPE, &sigact, NULL) != 0)
|
||||||
|
fatal("sigaction failed");
|
||||||
|
if (sigaction(SIGUSR2, &sigact, NULL) != 0)
|
||||||
|
fatal("sigaction failed");
|
||||||
|
if (sigaction(SIGTSTP, &sigact, NULL) != 0)
|
||||||
|
fatal("sigaction failed");
|
||||||
|
|
||||||
|
signal_set(&server_ev_sigchld, SIGCHLD, server_signal_callback, NULL);
|
||||||
|
signal_add(&server_ev_sigchld, NULL);
|
||||||
|
signal_set(&server_ev_sigterm, SIGTERM, server_signal_callback, NULL);
|
||||||
|
signal_add(&server_ev_sigterm, NULL);
|
||||||
|
signal_set(&server_ev_sigusr1, SIGUSR1, server_signal_callback, NULL);
|
||||||
|
signal_add(&server_ev_sigusr1, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Destroy server signal events. */
|
||||||
|
void
|
||||||
|
server_signal_clear(void)
|
||||||
|
{
|
||||||
|
struct sigaction sigact;
|
||||||
|
|
||||||
|
memset(&sigact, 0, sizeof sigact);
|
||||||
|
sigemptyset(&sigact.sa_mask);
|
||||||
|
sigact.sa_flags = SA_RESTART;
|
||||||
|
sigact.sa_handler = SIG_DFL;
|
||||||
|
if (sigaction(SIGINT, &sigact, NULL) != 0)
|
||||||
|
fatal("sigaction failed");
|
||||||
|
if (sigaction(SIGPIPE, &sigact, NULL) != 0)
|
||||||
|
fatal("sigaction failed");
|
||||||
|
if (sigaction(SIGUSR2, &sigact, NULL) != 0)
|
||||||
|
fatal("sigaction failed");
|
||||||
|
if (sigaction(SIGTSTP, &sigact, NULL) != 0)
|
||||||
|
fatal("sigaction failed");
|
||||||
|
|
||||||
|
signal_del(&server_ev_sigchld);
|
||||||
|
signal_del(&server_ev_sigterm);
|
||||||
|
signal_del(&server_ev_sigusr1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Signal handler. */
|
||||||
|
void
|
||||||
|
server_signal_callback(int sig, unused short events, unused void *data)
|
||||||
|
{
|
||||||
|
switch (sig) {
|
||||||
|
case SIGTERM:
|
||||||
|
server_shutdown = 1;
|
||||||
|
server_send_shutdown();
|
||||||
|
break;
|
||||||
|
case SIGCHLD:
|
||||||
|
server_child_signal();
|
||||||
|
break;
|
||||||
|
case SIGUSR1:
|
||||||
|
event_del(&server_ev_accept);
|
||||||
|
close(server_fd);
|
||||||
|
server_fd = server_create_socket();
|
||||||
|
event_set(&server_ev_accept, server_fd,
|
||||||
|
EV_READ|EV_PERSIST, server_accept_callback, NULL);
|
||||||
|
event_add(&server_ev_accept, NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle SIGCHLD. */
|
||||||
|
void
|
||||||
|
server_child_signal(void)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
switch (pid = waitpid(WAIT_ANY, &status, WNOHANG|WUNTRACED)) {
|
||||||
|
case -1:
|
||||||
|
if (errno == ECHILD)
|
||||||
|
return;
|
||||||
|
fatal("waitpid failed");
|
||||||
|
case 0:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (WIFSTOPPED(status))
|
||||||
|
server_child_stopped(pid, status);
|
||||||
|
else if (WIFEXITED(status))
|
||||||
|
server_child_exited(pid, status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle exited children. */
|
||||||
|
void
|
||||||
|
server_child_exited(pid_t pid, int status)
|
||||||
{
|
{
|
||||||
struct window *w;
|
struct window *w;
|
||||||
struct window_pane *wp;
|
struct window_pane *wp;
|
||||||
|
struct job *job;
|
||||||
|
u_int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
|
||||||
|
if ((w = ARRAY_ITEM(&windows, i)) == NULL)
|
||||||
|
continue;
|
||||||
|
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||||
|
if (wp->pid == pid) {
|
||||||
|
close(wp->fd);
|
||||||
|
wp->fd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SLIST_FOREACH(job, &all_jobs, lentry) {
|
||||||
|
if (pid == job->pid) {
|
||||||
|
job->pid = -1;
|
||||||
|
job->status = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle stopped children. */
|
||||||
|
void
|
||||||
|
server_child_stopped(pid_t pid, int status)
|
||||||
|
{
|
||||||
|
struct window *w;
|
||||||
|
struct window_pane *wp;
|
||||||
|
u_int i;
|
||||||
|
|
||||||
|
if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
|
||||||
|
if ((w = ARRAY_ITEM(&windows, i)) == NULL)
|
||||||
|
continue;
|
||||||
|
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||||
|
if (wp->pid == pid) {
|
||||||
|
if (killpg(pid, SIGCONT) != 0)
|
||||||
|
kill(pid, SIGCONT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle once-per-second timer events. */
|
||||||
|
void
|
||||||
|
server_second_callback(unused int fd, unused short events, unused void *arg)
|
||||||
|
{
|
||||||
|
struct window *w;
|
||||||
|
struct window_pane *wp;
|
||||||
|
struct timeval tv;
|
||||||
u_int i;
|
u_int i;
|
||||||
|
|
||||||
if (options_get_number(&global_s_options, "lock-server"))
|
if (options_get_number(&global_s_options, "lock-server"))
|
||||||
|
@ -548,6 +523,11 @@ server_second_timers(void)
|
||||||
wp->mode->timer(wp);
|
wp->mode->timer(wp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
evtimer_del(&server_ev_second);
|
||||||
|
memset(&tv, 0, sizeof tv);
|
||||||
|
tv.tv_sec = 1;
|
||||||
|
evtimer_add(&server_ev_second, &tv);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Lock the server if ALL sessions have hit the time limit. */
|
/* Lock the server if ALL sessions have hit the time limit. */
|
||||||
|
@ -606,32 +586,3 @@ server_lock_sessions(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update socket execute permissions based on whether sessions are attached. */
|
|
||||||
int
|
|
||||||
server_update_socket(void)
|
|
||||||
{
|
|
||||||
struct session *s;
|
|
||||||
u_int i;
|
|
||||||
static int last = -1;
|
|
||||||
int n;
|
|
||||||
|
|
||||||
n = 0;
|
|
||||||
for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
|
|
||||||
s = ARRAY_ITEM(&sessions, i);
|
|
||||||
if (s != NULL && !(s->flags & SESSION_UNATTACHED)) {
|
|
||||||
n++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n != last) {
|
|
||||||
last = n;
|
|
||||||
if (n != 0)
|
|
||||||
chmod(socket_path, S_IRWXU);
|
|
||||||
else
|
|
||||||
chmod(socket_path, S_IRUSR|S_IWUSR);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (n);
|
|
||||||
}
|
|
||||||
|
|
303
tmux.c
303
tmux.c
|
@ -20,6 +20,7 @@
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <event.h>
|
||||||
#include <paths.h>
|
#include <paths.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
@ -34,13 +35,6 @@
|
||||||
extern char *malloc_options;
|
extern char *malloc_options;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
volatile sig_atomic_t sigwinch;
|
|
||||||
volatile sig_atomic_t sigterm;
|
|
||||||
volatile sig_atomic_t sigcont;
|
|
||||||
volatile sig_atomic_t sigchld;
|
|
||||||
volatile sig_atomic_t sigusr1;
|
|
||||||
volatile sig_atomic_t sigusr2;
|
|
||||||
|
|
||||||
char *cfg_file;
|
char *cfg_file;
|
||||||
struct options global_s_options; /* session options */
|
struct options global_s_options; /* session options */
|
||||||
struct options global_w_options; /* window options */
|
struct options global_w_options; /* window options */
|
||||||
|
@ -55,9 +49,18 @@ int login_shell;
|
||||||
__dead void usage(void);
|
__dead void usage(void);
|
||||||
void fill_session(struct msg_command_data *);
|
void fill_session(struct msg_command_data *);
|
||||||
char *makesockpath(const char *);
|
char *makesockpath(const char *);
|
||||||
int dispatch_imsg(struct imsgbuf *, const char *, int *);
|
|
||||||
__dead void shell_exec(const char *, const char *);
|
__dead void shell_exec(const char *, const char *);
|
||||||
|
|
||||||
|
struct imsgbuf *main_ibuf;
|
||||||
|
struct event main_ev_sigterm;
|
||||||
|
int main_exitval;
|
||||||
|
|
||||||
|
void main_set_signals(void);
|
||||||
|
void main_clear_signals(void);
|
||||||
|
void main_signal(int, short, unused void *);
|
||||||
|
void main_callback(int, short, void *);
|
||||||
|
void main_dispatch(const char *);
|
||||||
|
|
||||||
__dead void
|
__dead void
|
||||||
usage(void)
|
usage(void)
|
||||||
{
|
{
|
||||||
|
@ -81,96 +84,6 @@ logfile(const char *name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
sighandler(int sig)
|
|
||||||
{
|
|
||||||
int saved_errno;
|
|
||||||
|
|
||||||
saved_errno = errno;
|
|
||||||
switch (sig) {
|
|
||||||
case SIGWINCH:
|
|
||||||
sigwinch = 1;
|
|
||||||
break;
|
|
||||||
case SIGTERM:
|
|
||||||
sigterm = 1;
|
|
||||||
break;
|
|
||||||
case SIGCHLD:
|
|
||||||
sigchld = 1;
|
|
||||||
break;
|
|
||||||
case SIGCONT:
|
|
||||||
sigcont = 1;
|
|
||||||
break;
|
|
||||||
case SIGUSR1:
|
|
||||||
sigusr1 = 1;
|
|
||||||
break;
|
|
||||||
case SIGUSR2:
|
|
||||||
sigusr2 = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
errno = saved_errno;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
siginit(void)
|
|
||||||
{
|
|
||||||
struct sigaction act;
|
|
||||||
|
|
||||||
memset(&act, 0, sizeof act);
|
|
||||||
sigemptyset(&act.sa_mask);
|
|
||||||
act.sa_flags = SA_RESTART;
|
|
||||||
|
|
||||||
act.sa_handler = SIG_IGN;
|
|
||||||
if (sigaction(SIGPIPE, &act, NULL) != 0)
|
|
||||||
fatal("sigaction failed");
|
|
||||||
if (sigaction(SIGINT, &act, NULL) != 0)
|
|
||||||
fatal("sigaction failed");
|
|
||||||
if (sigaction(SIGTSTP, &act, NULL) != 0)
|
|
||||||
fatal("sigaction failed");
|
|
||||||
if (sigaction(SIGQUIT, &act, NULL) != 0)
|
|
||||||
fatal("sigaction failed");
|
|
||||||
|
|
||||||
act.sa_handler = sighandler;
|
|
||||||
if (sigaction(SIGWINCH, &act, NULL) != 0)
|
|
||||||
fatal("sigaction failed");
|
|
||||||
if (sigaction(SIGTERM, &act, NULL) != 0)
|
|
||||||
fatal("sigaction failed");
|
|
||||||
if (sigaction(SIGCHLD, &act, NULL) != 0)
|
|
||||||
fatal("sigaction failed");
|
|
||||||
if (sigaction(SIGUSR1, &act, NULL) != 0)
|
|
||||||
fatal("sigaction failed");
|
|
||||||
if (sigaction(SIGUSR2, &act, NULL) != 0)
|
|
||||||
fatal("sigaction failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
sigreset(void)
|
|
||||||
{
|
|
||||||
struct sigaction act;
|
|
||||||
|
|
||||||
memset(&act, 0, sizeof act);
|
|
||||||
sigemptyset(&act.sa_mask);
|
|
||||||
|
|
||||||
act.sa_handler = SIG_DFL;
|
|
||||||
if (sigaction(SIGPIPE, &act, NULL) != 0)
|
|
||||||
fatal("sigaction failed");
|
|
||||||
if (sigaction(SIGUSR1, &act, NULL) != 0)
|
|
||||||
fatal("sigaction failed");
|
|
||||||
if (sigaction(SIGUSR2, &act, NULL) != 0)
|
|
||||||
fatal("sigaction failed");
|
|
||||||
if (sigaction(SIGINT, &act, NULL) != 0)
|
|
||||||
fatal("sigaction failed");
|
|
||||||
if (sigaction(SIGTSTP, &act, NULL) != 0)
|
|
||||||
fatal("sigaction failed");
|
|
||||||
if (sigaction(SIGQUIT, &act, NULL) != 0)
|
|
||||||
fatal("sigaction failed");
|
|
||||||
if (sigaction(SIGWINCH, &act, NULL) != 0)
|
|
||||||
fatal("sigaction failed");
|
|
||||||
if (sigaction(SIGTERM, &act, NULL) != 0)
|
|
||||||
fatal("sigaction failed");
|
|
||||||
if (sigaction(SIGCHLD, &act, NULL) != 0)
|
|
||||||
fatal("sigaction failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *
|
const char *
|
||||||
getshell(void)
|
getshell(void)
|
||||||
{
|
{
|
||||||
|
@ -281,23 +194,43 @@ makesockpath(const char *label)
|
||||||
return (path);
|
return (path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__dead void
|
||||||
|
shell_exec(const char *shell, const char *shellcmd)
|
||||||
|
{
|
||||||
|
const char *shellname, *ptr;
|
||||||
|
char *argv0;
|
||||||
|
|
||||||
|
ptr = strrchr(shell, '/');
|
||||||
|
if (ptr != NULL && *(ptr + 1) != '\0')
|
||||||
|
shellname = ptr + 1;
|
||||||
|
else
|
||||||
|
shellname = shell;
|
||||||
|
if (login_shell)
|
||||||
|
xasprintf(&argv0, "-%s", shellname);
|
||||||
|
else
|
||||||
|
xasprintf(&argv0, "%s", shellname);
|
||||||
|
setenv("SHELL", shell, 1);
|
||||||
|
|
||||||
|
execl(shell, argv0, "-c", shellcmd, (char *) NULL);
|
||||||
|
fatal("execl failed");
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, char **argv)
|
main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
struct cmd_list *cmdlist;
|
struct cmd_list *cmdlist;
|
||||||
struct cmd *cmd;
|
struct cmd *cmd;
|
||||||
struct pollfd pfd;
|
|
||||||
enum msgtype msg;
|
enum msgtype msg;
|
||||||
struct passwd *pw;
|
struct passwd *pw;
|
||||||
struct options *so, *wo;
|
struct options *so, *wo;
|
||||||
struct keylist *keylist;
|
struct keylist *keylist;
|
||||||
struct imsgbuf *ibuf;
|
|
||||||
struct msg_command_data cmddata;
|
struct msg_command_data cmddata;
|
||||||
char *s, *shellcmd, *path, *label, *home, *cause;
|
char *s, *shellcmd, *path, *label, *home, *cause;
|
||||||
char cwd[MAXPATHLEN], **var;
|
char cwd[MAXPATHLEN], **var;
|
||||||
void *buf;
|
void *buf;
|
||||||
size_t len;
|
size_t len;
|
||||||
int nfds, retcode, opt, flags, cmdflags = 0;
|
int opt, flags, cmdflags = 0;
|
||||||
|
short events;
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
malloc_options = (char *) "AFGJPX";
|
malloc_options = (char *) "AFGJPX";
|
||||||
|
@ -359,7 +292,6 @@ main(int argc, char **argv)
|
||||||
usage();
|
usage();
|
||||||
|
|
||||||
log_open_tty(debug_level);
|
log_open_tty(debug_level);
|
||||||
siginit();
|
|
||||||
|
|
||||||
if (!(flags & IDENTIFY_UTF8)) {
|
if (!(flags & IDENTIFY_UTF8)) {
|
||||||
/*
|
/*
|
||||||
|
@ -549,63 +481,121 @@ main(int argc, char **argv)
|
||||||
cmd_list_free(cmdlist);
|
cmd_list_free(cmdlist);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ibuf = client_init(path, cmdflags, flags)) == NULL)
|
if ((main_ibuf = client_init(path, cmdflags, flags)) == NULL)
|
||||||
exit(1);
|
exit(1);
|
||||||
xfree(path);
|
xfree(path);
|
||||||
|
|
||||||
imsg_compose(ibuf, msg, PROTOCOL_VERSION, -1, -1, buf, len);
|
event_init();
|
||||||
|
|
||||||
retcode = 0;
|
imsg_compose(main_ibuf, msg, PROTOCOL_VERSION, -1, -1, buf, len);
|
||||||
for (;;) {
|
|
||||||
pfd.fd = ibuf->fd;
|
|
||||||
pfd.events = POLLIN;
|
|
||||||
if (ibuf->w.queued != 0)
|
|
||||||
pfd.events |= POLLOUT;
|
|
||||||
|
|
||||||
if ((nfds = poll(&pfd, 1, INFTIM)) == -1) {
|
main_set_signals();
|
||||||
if (errno == EAGAIN || errno == EINTR)
|
|
||||||
continue;
|
|
||||||
fatal("poll failed");
|
|
||||||
}
|
|
||||||
if (nfds == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (pfd.revents & (POLLERR|POLLHUP|POLLNVAL))
|
events = EV_READ;
|
||||||
fatalx("socket error");
|
if (main_ibuf->w.queued > 0)
|
||||||
|
events |= EV_WRITE;
|
||||||
|
event_once(main_ibuf->fd, events, main_callback, shellcmd, NULL);
|
||||||
|
|
||||||
if (pfd.revents & POLLIN) {
|
main_exitval = 0;
|
||||||
if (dispatch_imsg(ibuf, shellcmd, &retcode) != 0)
|
event_dispatch();
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pfd.revents & POLLOUT) {
|
main_clear_signals();
|
||||||
if (msgbuf_write(&ibuf->w) < 0)
|
|
||||||
fatalx("msgbuf_write failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
options_free(&global_s_options);
|
client_main(); /* doesn't return */
|
||||||
options_free(&global_w_options);
|
|
||||||
|
|
||||||
return (retcode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
|
||||||
dispatch_imsg(struct imsgbuf *ibuf, const char *shellcmd, int *retcode)
|
void
|
||||||
|
main_set_signals(void)
|
||||||
|
{
|
||||||
|
struct sigaction sigact;
|
||||||
|
|
||||||
|
memset(&sigact, 0, sizeof sigact);
|
||||||
|
sigemptyset(&sigact.sa_mask);
|
||||||
|
sigact.sa_flags = SA_RESTART;
|
||||||
|
sigact.sa_handler = SIG_IGN;
|
||||||
|
if (sigaction(SIGINT, &sigact, NULL) != 0)
|
||||||
|
fatal("sigaction failed");
|
||||||
|
if (sigaction(SIGPIPE, &sigact, NULL) != 0)
|
||||||
|
fatal("sigaction failed");
|
||||||
|
if (sigaction(SIGUSR1, &sigact, NULL) != 0)
|
||||||
|
fatal("sigaction failed");
|
||||||
|
if (sigaction(SIGUSR2, &sigact, NULL) != 0)
|
||||||
|
fatal("sigaction failed");
|
||||||
|
if (sigaction(SIGTSTP, &sigact, NULL) != 0)
|
||||||
|
fatal("sigaction failed");
|
||||||
|
|
||||||
|
signal_set(&main_ev_sigterm, SIGTERM, main_signal, NULL);
|
||||||
|
signal_add(&main_ev_sigterm, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
main_clear_signals(void)
|
||||||
|
{
|
||||||
|
struct sigaction sigact;
|
||||||
|
|
||||||
|
memset(&sigact, 0, sizeof sigact);
|
||||||
|
sigemptyset(&sigact.sa_mask);
|
||||||
|
sigact.sa_flags = SA_RESTART;
|
||||||
|
sigact.sa_handler = SIG_DFL;
|
||||||
|
if (sigaction(SIGINT, &sigact, NULL) != 0)
|
||||||
|
fatal("sigaction failed");
|
||||||
|
if (sigaction(SIGPIPE, &sigact, NULL) != 0)
|
||||||
|
fatal("sigaction failed");
|
||||||
|
if (sigaction(SIGUSR1, &sigact, NULL) != 0)
|
||||||
|
fatal("sigaction failed");
|
||||||
|
if (sigaction(SIGUSR2, &sigact, NULL) != 0)
|
||||||
|
fatal("sigaction failed");
|
||||||
|
if (sigaction(SIGTSTP, &sigact, NULL) != 0)
|
||||||
|
fatal("sigaction failed");
|
||||||
|
|
||||||
|
event_del(&main_ev_sigterm);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
main_signal(int sig, unused short events, unused void *data)
|
||||||
|
{
|
||||||
|
switch (sig) {
|
||||||
|
case SIGTERM:
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
main_callback(unused int fd, short events, void *data)
|
||||||
|
{
|
||||||
|
char *shellcmd = data;
|
||||||
|
|
||||||
|
if (events & EV_READ)
|
||||||
|
main_dispatch(shellcmd);
|
||||||
|
|
||||||
|
if (events & EV_WRITE) {
|
||||||
|
if (msgbuf_write(&main_ibuf->w) < 0)
|
||||||
|
fatalx("msgbuf_write failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
events = EV_READ;
|
||||||
|
if (main_ibuf->w.queued > 0)
|
||||||
|
events |= EV_WRITE;
|
||||||
|
event_once(main_ibuf->fd, events, main_callback, shellcmd, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
main_dispatch(const char *shellcmd)
|
||||||
{
|
{
|
||||||
struct imsg imsg;
|
struct imsg imsg;
|
||||||
ssize_t n, datalen;
|
ssize_t n, datalen;
|
||||||
struct msg_print_data printdata;
|
struct msg_print_data printdata;
|
||||||
struct msg_shell_data shelldata;
|
struct msg_shell_data shelldata;
|
||||||
|
|
||||||
if ((n = imsg_read(ibuf)) == -1 || n == 0)
|
if ((n = imsg_read(main_ibuf)) == -1 || n == 0)
|
||||||
fatalx("imsg_read failed");
|
fatalx("imsg_read failed");
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if ((n = imsg_get(ibuf, &imsg)) == -1)
|
if ((n = imsg_get(main_ibuf, &imsg)) == -1)
|
||||||
fatalx("imsg_get failed");
|
fatalx("imsg_get failed");
|
||||||
if (n == 0)
|
if (n == 0)
|
||||||
return (0);
|
return;
|
||||||
datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
|
datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
|
||||||
|
|
||||||
switch (imsg.hdr.type) {
|
switch (imsg.hdr.type) {
|
||||||
|
@ -614,10 +604,8 @@ dispatch_imsg(struct imsgbuf *ibuf, const char *shellcmd, int *retcode)
|
||||||
if (datalen != 0)
|
if (datalen != 0)
|
||||||
fatalx("bad MSG_EXIT size");
|
fatalx("bad MSG_EXIT size");
|
||||||
|
|
||||||
return (-1);
|
exit(main_exitval);
|
||||||
case MSG_ERROR:
|
case MSG_ERROR:
|
||||||
*retcode = 1;
|
|
||||||
/* FALLTHROUGH */
|
|
||||||
case MSG_PRINT:
|
case MSG_PRINT:
|
||||||
if (datalen != sizeof printdata)
|
if (datalen != sizeof printdata)
|
||||||
fatalx("bad MSG_PRINT size");
|
fatalx("bad MSG_PRINT size");
|
||||||
|
@ -625,26 +613,30 @@ dispatch_imsg(struct imsgbuf *ibuf, const char *shellcmd, int *retcode)
|
||||||
printdata.msg[(sizeof printdata.msg) - 1] = '\0';
|
printdata.msg[(sizeof printdata.msg) - 1] = '\0';
|
||||||
|
|
||||||
log_info("%s", printdata.msg);
|
log_info("%s", printdata.msg);
|
||||||
|
if (imsg.hdr.type == MSG_ERROR)
|
||||||
|
main_exitval = 1;
|
||||||
break;
|
break;
|
||||||
case MSG_READY:
|
case MSG_READY:
|
||||||
if (datalen != 0)
|
if (datalen != 0)
|
||||||
fatalx("bad MSG_READY size");
|
fatalx("bad MSG_READY size");
|
||||||
|
|
||||||
client_main(); /* doesn't return */
|
event_loopexit(NULL); /* move to client_main() */
|
||||||
|
break;
|
||||||
case MSG_VERSION:
|
case MSG_VERSION:
|
||||||
if (datalen != 0)
|
if (datalen != 0)
|
||||||
fatalx("bad MSG_VERSION size");
|
fatalx("bad MSG_VERSION size");
|
||||||
|
|
||||||
log_warnx("protocol version mismatch (client %u, "
|
log_warnx("protocol version mismatch (client %u, "
|
||||||
"server %u)", PROTOCOL_VERSION, imsg.hdr.peerid);
|
"server %u)", PROTOCOL_VERSION, imsg.hdr.peerid);
|
||||||
*retcode = 1;
|
exit(1);
|
||||||
return (-1);
|
|
||||||
case MSG_SHELL:
|
case MSG_SHELL:
|
||||||
if (datalen != sizeof shelldata)
|
if (datalen != sizeof shelldata)
|
||||||
fatalx("bad MSG_SHELL size");
|
fatalx("bad MSG_SHELL size");
|
||||||
memcpy(&shelldata, imsg.data, sizeof shelldata);
|
memcpy(&shelldata, imsg.data, sizeof shelldata);
|
||||||
shelldata.shell[(sizeof shelldata.shell) - 1] = '\0';
|
shelldata.shell[(sizeof shelldata.shell) - 1] = '\0';
|
||||||
|
|
||||||
|
main_clear_signals();
|
||||||
|
|
||||||
shell_exec(shelldata.shell, shellcmd);
|
shell_exec(shelldata.shell, shellcmd);
|
||||||
default:
|
default:
|
||||||
fatalx("unexpected message");
|
fatalx("unexpected message");
|
||||||
|
@ -653,26 +645,3 @@ dispatch_imsg(struct imsgbuf *ibuf, const char *shellcmd, int *retcode)
|
||||||
imsg_free(&imsg);
|
imsg_free(&imsg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
__dead void
|
|
||||||
shell_exec(const char *shell, const char *shellcmd)
|
|
||||||
{
|
|
||||||
const char *shellname, *ptr;
|
|
||||||
char *argv0;
|
|
||||||
|
|
||||||
sigreset();
|
|
||||||
|
|
||||||
ptr = strrchr(shell, '/');
|
|
||||||
if (ptr != NULL && *(ptr + 1) != '\0')
|
|
||||||
shellname = ptr + 1;
|
|
||||||
else
|
|
||||||
shellname = shell;
|
|
||||||
if (login_shell)
|
|
||||||
xasprintf(&argv0, "-%s", shellname);
|
|
||||||
else
|
|
||||||
xasprintf(&argv0, "%s", shellname);
|
|
||||||
setenv("SHELL", shell, 1);
|
|
||||||
|
|
||||||
execl(shell, argv0, "-c", shellcmd, (char *) NULL);
|
|
||||||
fatal("execl failed");
|
|
||||||
}
|
|
||||||
|
|
28
tmux.h
28
tmux.h
|
@ -28,9 +28,9 @@
|
||||||
#include <sys/uio.h>
|
#include <sys/uio.h>
|
||||||
|
|
||||||
#include <bitstring.h>
|
#include <bitstring.h>
|
||||||
|
#include <event.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <poll.h>
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
@ -661,6 +661,7 @@ struct job {
|
||||||
struct client *client;
|
struct client *client;
|
||||||
|
|
||||||
int fd;
|
int fd;
|
||||||
|
struct event event;
|
||||||
struct buffer *out;
|
struct buffer *out;
|
||||||
|
|
||||||
void (*callbackfn)(struct job *);
|
void (*callbackfn)(struct job *);
|
||||||
|
@ -796,14 +797,17 @@ struct window_pane {
|
||||||
char *cwd;
|
char *cwd;
|
||||||
|
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
int fd;
|
|
||||||
char tty[TTY_NAME_MAX];
|
char tty[TTY_NAME_MAX];
|
||||||
|
|
||||||
|
int fd;
|
||||||
|
struct event event;
|
||||||
struct buffer *in;
|
struct buffer *in;
|
||||||
struct buffer *out;
|
struct buffer *out;
|
||||||
|
|
||||||
struct input_ctx ictx;
|
struct input_ctx ictx;
|
||||||
|
|
||||||
int pipe_fd;
|
int pipe_fd;
|
||||||
|
struct event pipe_event;
|
||||||
struct buffer *pipe_buf;
|
struct buffer *pipe_buf;
|
||||||
size_t pipe_off;
|
size_t pipe_off;
|
||||||
|
|
||||||
|
@ -998,6 +1002,7 @@ struct tty {
|
||||||
struct tty_term *term;
|
struct tty_term *term;
|
||||||
|
|
||||||
int fd;
|
int fd;
|
||||||
|
struct event event;
|
||||||
struct buffer *in;
|
struct buffer *in;
|
||||||
struct buffer *out;
|
struct buffer *out;
|
||||||
|
|
||||||
|
@ -1062,6 +1067,7 @@ struct mouse_event {
|
||||||
/* Client connection. */
|
/* Client connection. */
|
||||||
struct client {
|
struct client {
|
||||||
struct imsgbuf ibuf;
|
struct imsgbuf ibuf;
|
||||||
|
struct event event;
|
||||||
|
|
||||||
struct timeval creation_time;
|
struct timeval creation_time;
|
||||||
struct timeval activity_time;
|
struct timeval activity_time;
|
||||||
|
@ -1235,12 +1241,6 @@ extern const struct set_option_entry set_option_table[];
|
||||||
extern const struct set_option_entry set_window_option_table[];
|
extern const struct set_option_entry set_window_option_table[];
|
||||||
|
|
||||||
/* tmux.c */
|
/* tmux.c */
|
||||||
extern volatile sig_atomic_t sigwinch;
|
|
||||||
extern volatile sig_atomic_t sigterm;
|
|
||||||
extern volatile sig_atomic_t sigcont;
|
|
||||||
extern volatile sig_atomic_t sigchld;
|
|
||||||
extern volatile sig_atomic_t sigusr1;
|
|
||||||
extern volatile sig_atomic_t sigusr2;
|
|
||||||
extern struct options global_s_options;
|
extern struct options global_s_options;
|
||||||
extern struct options global_w_options;
|
extern struct options global_w_options;
|
||||||
extern struct environ global_environ;
|
extern struct environ global_environ;
|
||||||
|
@ -1251,9 +1251,6 @@ extern time_t start_time;
|
||||||
extern char *socket_path;
|
extern char *socket_path;
|
||||||
extern int login_shell;
|
extern int login_shell;
|
||||||
void logfile(const char *);
|
void logfile(const char *);
|
||||||
void siginit(void);
|
|
||||||
void sigreset(void);
|
|
||||||
void sighandler(int);
|
|
||||||
const char *getshell(void);
|
const char *getshell(void);
|
||||||
int checkshell(const char *);
|
int checkshell(const char *);
|
||||||
int areshell(const char *);
|
int areshell(const char *);
|
||||||
|
@ -1582,23 +1579,24 @@ const char *key_string_lookup_key(int);
|
||||||
extern struct clients clients;
|
extern struct clients clients;
|
||||||
extern struct clients dead_clients;
|
extern struct clients dead_clients;
|
||||||
int server_start(char *);
|
int server_start(char *);
|
||||||
void server_poll_add(int, int, void (*)(int, int, void *), void *);
|
void server_signal_set(void);
|
||||||
|
void server_signal_clear(void);
|
||||||
|
|
||||||
/* server-client.c */
|
/* server-client.c */
|
||||||
void server_client_create(int);
|
void server_client_create(int);
|
||||||
void server_client_lost(struct client *);
|
void server_client_lost(struct client *);
|
||||||
void server_client_prepare(void);
|
void server_client_prepare(void);
|
||||||
void server_client_callback(int, int, void *);
|
void server_client_callback(int, short, void *);
|
||||||
void server_client_loop(void);
|
void server_client_loop(void);
|
||||||
|
|
||||||
/* server-job.c */
|
/* server-job.c */
|
||||||
void server_job_prepare(void);
|
void server_job_prepare(void);
|
||||||
void server_job_callback(int, int, void *);
|
void server_job_callback(int, short, void *);
|
||||||
void server_job_loop(void);
|
void server_job_loop(void);
|
||||||
|
|
||||||
/* server-window.c */
|
/* server-window.c */
|
||||||
void server_window_prepare(void);
|
void server_window_prepare(void);
|
||||||
void server_window_callback(int, int, void *);
|
void server_window_callback(int, short, void *);
|
||||||
void server_window_loop(void);
|
void server_window_loop(void);
|
||||||
|
|
||||||
/* server-fn.c */
|
/* server-fn.c */
|
||||||
|
|
4
window.c
4
window.c
|
@ -455,10 +455,12 @@ window_pane_destroy(struct window_pane *wp)
|
||||||
if (wp->pipe_fd != -1) {
|
if (wp->pipe_fd != -1) {
|
||||||
buffer_destroy(wp->pipe_buf);
|
buffer_destroy(wp->pipe_buf);
|
||||||
close(wp->pipe_fd);
|
close(wp->pipe_fd);
|
||||||
|
event_del(&wp->pipe_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer_destroy(wp->in);
|
buffer_destroy(wp->in);
|
||||||
buffer_destroy(wp->out);
|
buffer_destroy(wp->out);
|
||||||
|
event_del(&wp->event);
|
||||||
|
|
||||||
if (wp->cwd != NULL)
|
if (wp->cwd != NULL)
|
||||||
xfree(wp->cwd);
|
xfree(wp->cwd);
|
||||||
|
@ -543,7 +545,7 @@ window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell,
|
||||||
setenv(envent->name, envent->value, 1);
|
setenv(envent->name, envent->value, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
sigreset();
|
server_signal_clear();
|
||||||
log_close();
|
log_close();
|
||||||
|
|
||||||
if (*wp->cmd != '\0') {
|
if (*wp->cmd != '\0') {
|
||||||
|
|
Loading…
Reference in New Issue