version 1.29, 2009/11/02 13:42:25 |
version 1.30, 2009/11/04 20:50:11 |
|
|
#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> |
|
|
#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) |
|
|
__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. */ |
if (client_exitmsg != NULL) { |
if (client_exitmsg != NULL) { |
|
|
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) |
|
|
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) |
|
|
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) |
|
|
|
|
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); |
|
} |
} |