diff options
Diffstat (limited to 'client.c')
-rw-r--r-- | client.c | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/client.c b/client.c new file mode 100644 index 00000000..8c120aa6 --- /dev/null +++ b/client.c @@ -0,0 +1,251 @@ +/* $Id: client.c,v 1.1 2007-09-26 13:43:15 nicm Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> + * + * 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 <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/un.h> + +#include <errno.h> +#include <fcntl.h> +#include <paths.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +#include "tmux.h" + +void client_handle_winch(struct client_ctx *); +int client_process_local(struct client_ctx *, const char **); + +int +client_init(char *path, struct client_ctx *cctx, int ws) +{ + struct sockaddr_un sa; + struct stat sb; + size_t sz; + int mode; + + if (path == NULL) { + xasprintf(&path, + "%s/%s-%lu", _PATH_TMP, __progname, (u_long) getuid()); + } + +retry: + if (stat(path, &sb) != 0) { + if (errno != ENOENT) { + log_warn("%s", path); + return (-1); + } + if (server_start(path) != 0) + return (-1); + sleep(1); /* XXX */ + goto retry; + } + if (!S_ISSOCK(sb.st_mode)) { + log_warnx("%s: %s", path, strerror(ENOTSOCK)); + return (-1); + } + + if (ws) { + if (!isatty(STDIN_FILENO)) { + log_warnx("stdin is not a tty"); + return (-1); + } + if (!isatty(STDOUT_FILENO)) { + log_warnx("stdout is not a tty"); + return (-1); + } + + if (ioctl(STDIN_FILENO, TIOCGWINSZ, &cctx->ws) == -1) { + log_warn("ioctl(TIOCGWINSZ)"); + return (-1); + } + } + + memset(&sa, 0, sizeof sa); + sa.sun_family = AF_UNIX; + sz = strlcpy(sa.sun_path, path, sizeof sa.sun_path); + if (sz >= sizeof sa.sun_path) { + log_warnx("%s: %s", path, strerror(ENAMETOOLONG)); + return (-1); + } + + if ((cctx->srv_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + log_warn("%s: socket", path); + return (-1); + } + if (connect( + cctx->srv_fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) { + log_warn("%s: connect", path); + return (-1); + } + + if ((mode = fcntl(cctx->srv_fd, F_GETFL)) == -1) { + log_warn("%s: fcntl", path); + return (-1); + } + if (fcntl(cctx->srv_fd, F_SETFL, mode|O_NONBLOCK) == -1) { + log_warn("%s: fcntl", path); + return (-1); + } + cctx->srv_in = buffer_create(BUFSIZ); + cctx->srv_out = buffer_create(BUFSIZ); + + return (0); +} + +int +client_main(struct client_ctx *cctx) +{ + struct pollfd pfds[2]; + const char *error; + int n; + + logfile("client"); + setproctitle("client"); + + siginit(); + if ((cctx->loc_fd = local_init(&cctx->loc_in, &cctx->loc_out)) == -1) + return (1); + + n = 0; + error = NULL; + while (!sigterm) { + if (sigwinch) + client_handle_winch(cctx); + + pfds[0].fd = cctx->srv_fd; + pfds[0].events = POLLIN; + if (BUFFER_USED(cctx->srv_out) > 0) + pfds[0].events |= POLLOUT; + pfds[1].fd = cctx->loc_fd; + pfds[1].events = POLLIN; + if (BUFFER_USED(cctx->loc_out) > 0) + pfds[1].events |= POLLOUT; + + if (poll(pfds, 2, INFTIM) == -1) { + if (errno == EAGAIN || errno == EINTR) + continue; + fatal("poll failed"); + } + + if (buffer_poll(&pfds[0], cctx->srv_in, cctx->srv_out) != 0) + goto server_dead; + if (buffer_poll(&pfds[1], cctx->loc_in, cctx->loc_out) != 0) + goto local_dead; + + /* XXX Output flushed; pause if required. */ + if (n) + usleep(750000); + /* XXX XXX special return code for pause */ + if ((n = client_process_local(cctx, &error)) == -1) + break; + if ((n = client_msg_dispatch(cctx, &error)) == -1) + break; + } + + local_done(); + + if (sigterm) + error = "received SIGTERM"; + if (error != NULL) { + printf("[terminated: %s]\n", error); + return (0); + } + printf("[detached]\n"); + return (0); + +server_dead: + local_done(); + + printf("[lost server]\n"); + return (1); + +local_dead: + /* Can't do much here. Log and die. */ + fatalx("local socket dead"); +} + +void +client_write_server( + struct client_ctx *cctx, enum hdrtype type, void *buf, size_t len) +{ + struct hdr hdr; + + hdr.type = type; + hdr.size = len; + buffer_write(cctx->srv_out, &hdr, sizeof hdr); + if (len > 0) + buffer_write(cctx->srv_out, buf, len); +} + +void +client_handle_winch(struct client_ctx *cctx) +{ + struct size_data data; + + if (ioctl(STDIN_FILENO, TIOCGWINSZ, &cctx->ws) == -1) + fatal("ioctl failed"); + + data.sx = cctx->ws.ws_col; + data.sy = cctx->ws.ws_row; + client_write_server(cctx, MSG_SIZE, &data, sizeof data); + + sigwinch = 0; +} + +int +client_process_local(struct client_ctx *cctx, const char **error) +{ + struct buffer *b; + size_t size; + int n, key; + + n = 0; + b = buffer_create(BUFSIZ); + + while ((key = local_key(&size)) != KEYC_NONE) { + log_debug("key code: %d", key); + + if (key == client_cmd_prefix) { + if ((key = local_key(NULL)) == KEYC_NONE) { + /* XXX sux */ + buffer_reverse_remove(cctx->loc_in, size); + break; + } + n = client_cmd_dispatch(key, cctx, error); + break; + } + + input_store8(b, '\e'); + input_store16(b, (uint16_t) key /*XXX*/); + } + + log_debug("transmitting %zu bytes of input", BUFFER_USED(b)); + if (BUFFER_USED(b) == 0) { + buffer_destroy(b); + return (n); + } + client_write_server(cctx, MSG_INPUT, BUFFER_OUT(b), BUFFER_USED(b)); + buffer_destroy(b); + return (n); +} + |