diff --git a/Makefile b/Makefile index 80a3b003..a300e0c4 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.4 2007-09-26 10:35:24 nicm Exp $ +# $Id: Makefile,v 1.5 2007-09-26 13:43:14 nicm Exp $ .SUFFIXES: .c .o .y .h .PHONY: clean @@ -18,7 +18,7 @@ META?= \002 # C-b SRCS= tmux.c server.c server-msg.c server-fn.c buffer.c buffer-poll.c \ xmalloc.c xmalloc-debug.c input.c screen.c window.c session.c local.c \ - log.c command.c + log.c client.c client-msg.c client-cmd.c op.c YACC= yacc -d diff --git a/client-cmd.c b/client-cmd.c new file mode 100644 index 00000000..998fee2c --- /dev/null +++ b/client-cmd.c @@ -0,0 +1,108 @@ +/* $Id: client-cmd.c,v 1.1 2007-09-26 13:43:14 nicm Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * 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 + +#include "tmux.h" + +int client_cmd_prefix = META; + +int client_cmd_fn_select(int, struct client_ctx *, const char **); +int client_cmd_fn_detach(int, struct client_ctx *, const char **); +int client_cmd_fn_msg(int, struct client_ctx *, const char **); + +struct cmd { + int key; + int (*fn)(int, struct client_ctx *, const char **); + int arg; +}; + +struct cmd client_cmd_table[] = { + { '0', client_cmd_fn_select, 0 }, + { '1', client_cmd_fn_select, 1 }, + { '2', client_cmd_fn_select, 2 }, + { '3', client_cmd_fn_select, 3 }, + { '4', client_cmd_fn_select, 4 }, + { '5', client_cmd_fn_select, 5 }, + { '6', client_cmd_fn_select, 6 }, + { '7', client_cmd_fn_select, 7 }, + { '8', client_cmd_fn_select, 8 }, + { '9', client_cmd_fn_select, 9 }, + { 'C', client_cmd_fn_msg, MSG_CREATE }, + { 'c', client_cmd_fn_msg, MSG_CREATE }, + { 'D', client_cmd_fn_detach, 0 }, + { 'd', client_cmd_fn_detach, 0 }, + { 'N', client_cmd_fn_msg, MSG_NEXT }, + { 'n', client_cmd_fn_msg, MSG_NEXT }, + { 'P', client_cmd_fn_msg, MSG_PREVIOUS }, + { 'p', client_cmd_fn_msg, MSG_PREVIOUS }, + { 'R', client_cmd_fn_msg, MSG_REFRESH }, + { 'r', client_cmd_fn_msg, MSG_REFRESH }, + { 'T', client_cmd_fn_msg, MSG_RENAME }, + { 't', client_cmd_fn_msg, MSG_RENAME }, + { 'L', client_cmd_fn_msg, MSG_LAST }, + { 'l', client_cmd_fn_msg, MSG_LAST }, + { 'W', client_cmd_fn_msg, MSG_WINDOWLIST }, + { 'w', client_cmd_fn_msg, MSG_WINDOWLIST } +}; +#define NCLIENTCMD (sizeof client_cmd_table / sizeof client_cmd_table[0]) + +/* Dispatch to a command. */ +int +client_cmd_dispatch(int key, struct client_ctx *cctx, const char **error) +{ + struct cmd *cmd; + u_int i; + + for (i = 0; i < NCLIENTCMD; i++) { + cmd = client_cmd_table + i; + if (cmd->key == key) + return (cmd->fn(cmd->arg, cctx, error)); + } + return (0); +} + +/* Handle generic command. */ +int +client_cmd_fn_msg(int arg, struct client_ctx *cctx, unused const char **error) +{ + client_write_server(cctx, arg, NULL, 0); + + return (0); +} + +/* Handle select command. */ +int +client_cmd_fn_select( + int arg, struct client_ctx *cctx, unused const char **error) +{ + struct select_data data; + + data.idx = arg; + client_write_server(cctx, MSG_SELECT, &data, sizeof data); + + return (0); +} + +/* Handle detach command. */ +int +client_cmd_fn_detach( + unused int arg, unused struct client_ctx *cctx, unused const char **error) +{ + return (-1); +} diff --git a/client-msg.c b/client-msg.c new file mode 100644 index 00000000..6639d915 --- /dev/null +++ b/client-msg.c @@ -0,0 +1,99 @@ +/* $Id: client-msg.c,v 1.1 2007-09-26 13:43:14 nicm Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * 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 + +#include +#include +#include + +#include "tmux.h" + +int client_msg_fn_output(struct hdr *, struct client_ctx *, const char **); +int client_msg_fn_pause(struct hdr *, struct client_ctx *, const char **); +int client_msg_fn_exit(struct hdr *, struct client_ctx *, const char **); + +struct client_msg { + enum hdrtype type; + + int (*fn)(struct hdr *, struct client_ctx *, const char **); +}; +struct client_msg client_msg_table[] = { + { MSG_OUTPUT, client_msg_fn_output }, + { MSG_PAUSE, client_msg_fn_pause }, + { MSG_EXIT, client_msg_fn_exit }, +}; +#define NCLIENTMSG (sizeof client_msg_table / sizeof client_msg_table[0]) + +int +client_msg_dispatch(struct client_ctx *cctx, const char **error) +{ + struct hdr hdr; + struct client_msg *msg; + u_int i; + int n; + + for (;;) { + if (BUFFER_USED(cctx->srv_in) < sizeof hdr) + return (0); + memcpy(&hdr, BUFFER_OUT(cctx->srv_in), sizeof hdr); + if (BUFFER_USED(cctx->srv_in) < (sizeof hdr) + hdr.size) + return (0); + buffer_remove(cctx->srv_in, sizeof hdr); + + for (i = 0; i < NCLIENTMSG; i++) { + msg = client_msg_table + i; + if (msg->type == hdr.type) { + if ((n = msg->fn(&hdr, cctx, error)) != 0) + return (n); + break; + } + } + if (i == NCLIENTMSG) + fatalx("unexpected message"); + } +} + +/* Output message from client. */ +int +client_msg_fn_output( + struct hdr *hdr, struct client_ctx *cctx, unused const char **error) +{ + local_output(cctx->srv_in, hdr->size); + return (0); +} + +/* Pause message from server. */ +int +client_msg_fn_pause( + struct hdr *hdr, unused struct client_ctx *cctx, unused const char **error) +{ + if (hdr->size != 0) + fatalx("bad MSG_PAUSE size"); + return (1); +} + +/* Exit message from server. */ +int +client_msg_fn_exit( + struct hdr *hdr, unused struct client_ctx *cctx, unused const char **error) +{ + if (hdr->size != 0) + fatalx("bad MSG_EXIT size"); + return (-1); +} 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 + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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); +} + diff --git a/command.c b/command.c deleted file mode 100644 index d07bd35f..00000000 --- a/command.c +++ /dev/null @@ -1,113 +0,0 @@ -/* $Id: command.c,v 1.7 2007-09-22 11:50:33 nicm Exp $ */ - -/* - * Copyright (c) 2007 Nicholas Marriott - * - * 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 - -#include "tmux.h" - -int cmd_prefix = META; - -int cmd_fn_select(struct buffer *, int); -int cmd_fn_detach(struct buffer *, int); -int cmd_fn_msg(struct buffer *, int); - -struct cmd { - int key; - int (*fn)(struct buffer *, int); - int arg; -}; - -struct cmd cmd_table[] = { - { '0', cmd_fn_select, 0 }, - { '1', cmd_fn_select, 1 }, - { '2', cmd_fn_select, 2 }, - { '3', cmd_fn_select, 3 }, - { '4', cmd_fn_select, 4 }, - { '5', cmd_fn_select, 5 }, - { '6', cmd_fn_select, 6 }, - { '7', cmd_fn_select, 7 }, - { '8', cmd_fn_select, 8 }, - { '9', cmd_fn_select, 9 }, - { 'C', cmd_fn_msg, MSG_CREATE }, - { 'c', cmd_fn_msg, MSG_CREATE }, - { 'D', cmd_fn_detach, 0 }, - { 'd', cmd_fn_detach, 0 }, - { 'N', cmd_fn_msg, MSG_NEXT }, - { 'n', cmd_fn_msg, MSG_NEXT }, - { 'P', cmd_fn_msg, MSG_PREVIOUS }, - { 'p', cmd_fn_msg, MSG_PREVIOUS }, - { 'R', cmd_fn_msg, MSG_REFRESH }, - { 'r', cmd_fn_msg, MSG_REFRESH }, - { 'T', cmd_fn_msg, MSG_RENAME }, - { 't', cmd_fn_msg, MSG_RENAME }, - { 'L', cmd_fn_msg, MSG_LAST }, - { 'l', cmd_fn_msg, MSG_LAST }, - { 'W', cmd_fn_msg, MSG_WINDOWLIST }, - { 'w', cmd_fn_msg, MSG_WINDOWLIST } -}; - -/* Dispatch to a command. */ -int -cmd_execute(int key, struct buffer *srv_out) -{ - struct cmd *cmd; - u_int i; - - for (i = 0; i < (sizeof cmd_table / sizeof cmd_table[0]); i++) { - cmd = cmd_table + i; - if (cmd->key == key) - return (cmd->fn(srv_out, cmd->arg)); - } - return (0); -} - -/* Handle generic command. */ -int -cmd_fn_msg(struct buffer *srv_out, int type) -{ - struct hdr hdr; - - hdr.type = type; - hdr.size = 0; - buffer_write(srv_out, &hdr, sizeof hdr); - - return (0); -} - -/* Handle select command. */ -int -cmd_fn_select(struct buffer *srv_out, int arg) -{ - struct hdr hdr; - struct select_data data; - - hdr.type = MSG_SELECT; - hdr.size = sizeof data; - buffer_write(srv_out, &hdr, sizeof hdr); - data.idx = arg; - buffer_write(srv_out, &data, sizeof data); - - return (0); -} - -/* Handle detach command. */ -int -cmd_fn_detach(unused struct buffer *srv_out, unused int arg) -{ - return (-1); -} diff --git a/op-list.c b/op-list.c new file mode 100644 index 00000000..e2f78efe --- /dev/null +++ b/op-list.c @@ -0,0 +1,126 @@ +/* $Id: op-list.c,v 1.1 2007-09-26 13:43:15 nicm Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * 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. + */ + +/* List sessions or windows. */ +void +op_list(char *name) +{ + struct sessions_data sd; + struct windows_data wd; + struct pollfd pfd; + struct hdr hdr; + + /* Send query data. */ + if (*name == '\0') { + hdr.type = MSG_SESSIONS; + hdr.size = sizeof sd; + buffer_write(server_out, &hdr, sizeof hdr); + buffer_write(server_out, &sd, hdr.size); + } else { + hdr.type = MSG_WINDOWS; + hdr.size = sizeof wd; + buffer_write(server_out, &hdr, sizeof hdr); + strlcpy(wd.name, name, sizeof wd.name); + buffer_write(server_out, &wd, hdr.size); + } + + /* Main loop. */ + for (;;) { + /* Set up pollfd. */ + pfd.fd = server_fd; + pfd.events = POLLIN; + if (BUFFER_USED(server_out) > 0) + pfd.events |= POLLOUT; + + /* Do the poll. */ + if (poll(&pfd, 1, INFTIM) == -1) { + if (errno == EAGAIN || errno == EINTR) + continue; + err(1, "poll"); + } + + /* Read/write from sockets. */ + if (buffer_poll(&pfd, server_in, server_out) != 0) + errx(1, "lost server"); + + /* Process data. */ + process_list(name); + } +} + +void +op_list_process(const char *name) +{ + struct sessions_data sd; + struct sessions_entry se; + struct windows_data wd; + struct windows_entry we; + struct hdr hdr; + char *tim; + + for (;;) { + if (BUFFER_USED(server_in) < sizeof hdr) + break; + memcpy(&hdr, BUFFER_OUT(server_in), sizeof hdr); + if (BUFFER_USED(server_in) < (sizeof hdr) + hdr.size) + break; + buffer_remove(server_in, sizeof hdr); + + switch (hdr.type) { + case MSG_SESSIONS: + if (hdr.size < sizeof sd) + errx(1, "bad MSG_SESSIONS size"); + buffer_read(server_in, &sd, sizeof sd); + hdr.size -= sizeof sd; + if (sd.sessions == 0 && hdr.size == 0) + exit(0); + if (hdr.size < sd.sessions * sizeof se) + errx(1, "bad MSG_SESSIONS size"); + while (sd.sessions-- > 0) { + buffer_read(server_in, &se, sizeof se); + tim = ctime(&se.tim); + *strchr(tim, '\n') = '\0'; + printf("%s: %u windows (created %s)\n", + se.name, se.windows, tim); + } + exit(0); + case MSG_WINDOWS: + if (hdr.size < sizeof wd) + errx(1, "bad MSG_WINDOWS size"); + buffer_read(server_in, &wd, sizeof wd); + hdr.size -= sizeof wd; + if (wd.windows == 0 && hdr.size == 0) + errx(1, "session not found: %s", name); + if (hdr.size < wd.windows * sizeof we) + errx(1, "bad MSG_WINDOWS size"); + while (wd.windows-- > 0) { + buffer_read(server_in, &we, sizeof we); + if (*we.title != '\0') { + printf("%u: %s \"%s\" (%s)\n", + we.idx, we.name, we.title, we.tty); + } else { + printf("%u: %s (%s)\n", + we.idx, we.name, we.tty); + } + } + exit(0); + default: + fatalx("unexpected message"); + } + } +} diff --git a/op.c b/op.c new file mode 100644 index 00000000..393ca69f --- /dev/null +++ b/op.c @@ -0,0 +1,64 @@ +/* $Id: op.c,v 1.1 2007-09-26 13:43:15 nicm Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * 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 + +#include + +#include "tmux.h" + +int +op_new(char *path, int argc, unused char **argv) +{ + struct new_data data; + struct client_ctx cctx; + + if (argc != 0) /* XXX -n */ + return (usage("new")); + + if (client_init(path, &cctx, 1) != 0) + return (1); + + strlcpy(data.name, "XXX"/*XXX*/, sizeof data.name); + data.sx = cctx.ws.ws_col; + data.sy = cctx.ws.ws_row; + client_write_server(&cctx, MSG_NEW, &data, sizeof data); + + return (client_main(&cctx)); +} + +int +op_attach(char *path, int argc, unused char **argv) +{ + struct attach_data data; + struct client_ctx cctx; + + if (argc != 0) /* XXX -n */ + return (usage("attach")); + + if (client_init(path, &cctx, 1) != 0) + return (1); + + strlcpy(data.name, "XXX"/*XXX*/, sizeof data.name); + data.sx = cctx.ws.ws_col; + data.sy = cctx.ws.ws_row; + client_write_server(&cctx, MSG_ATTACH, &data, sizeof data); + + return (client_main(&cctx)); +} + diff --git a/server-msg.c b/server-msg.c index 8bf5af2a..1c0de755 100644 --- a/server-msg.c +++ b/server-msg.c @@ -1,4 +1,4 @@ -/* $Id: server-msg.c,v 1.1 2007-09-26 10:35:24 nicm Exp $ */ +/* $Id: server-msg.c,v 1.2 2007-09-26 13:43:15 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -24,25 +24,25 @@ #include "tmux.h" -void server_msg_fn_attach(struct client *, struct hdr *); -void server_msg_fn_create(struct client *, struct hdr *); -void server_msg_fn_input(struct client *, struct hdr *); -void server_msg_fn_last(struct client *, struct hdr *); -void server_msg_fn_new(struct client *, struct hdr *); -void server_msg_fn_next(struct client *, struct hdr *); -void server_msg_fn_previous(struct client *, struct hdr *); -void server_msg_fn_refresh(struct client *, struct hdr *); -void server_msg_fn_rename(struct client *, struct hdr *); -void server_msg_fn_select(struct client *, struct hdr *); -void server_msg_fn_sessions(struct client *, struct hdr *); -void server_msg_fn_size(struct client *, struct hdr *); -void server_msg_fn_windowlist(struct client *, struct hdr *); -void server_msg_fn_windows(struct client *, struct hdr *); +int server_msg_fn_attach(struct hdr *, struct client *); +int server_msg_fn_create(struct hdr *, struct client *); +int server_msg_fn_input(struct hdr *, struct client *); +int server_msg_fn_last(struct hdr *, struct client *); +int server_msg_fn_new(struct hdr *, struct client *); +int server_msg_fn_next(struct hdr *, struct client *); +int server_msg_fn_previous(struct hdr *, struct client *); +int server_msg_fn_refresh(struct hdr *, struct client *); +int server_msg_fn_rename(struct hdr *, struct client *); +int server_msg_fn_select(struct hdr *, struct client *); +int server_msg_fn_sessions(struct hdr *, struct client *); +int server_msg_fn_size(struct hdr *, struct client *); +int server_msg_fn_windowlist(struct hdr *, struct client *); +int server_msg_fn_windows(struct hdr *, struct client *); struct server_msg { enum hdrtype type; - void (*fn)(struct client *, struct hdr *); + int (*fn)(struct hdr *, struct client *); }; struct server_msg server_msg_table[] = { { MSG_ATTACH, server_msg_fn_attach }, @@ -62,41 +62,45 @@ struct server_msg server_msg_table[] = { }; #define NSERVERMSG (sizeof server_msg_table / sizeof server_msg_table[0]) -void +int server_msg_dispatch(struct client *c) { struct hdr hdr; struct server_msg *msg; u_int i; + int n; - if (BUFFER_USED(c->in) < sizeof hdr) - return; - memcpy(&hdr, BUFFER_OUT(c->in), sizeof hdr); - if (BUFFER_USED(c->in) < (sizeof hdr) + hdr.size) - return; - buffer_remove(c->in, sizeof hdr); - - for (i = 0; i < NSERVERMSG; i++) { - msg = server_msg_table + i; - if (msg->type == hdr.type) { - msg->fn(c, &hdr); - return; - } + for (;;) { + if (BUFFER_USED(c->in) < sizeof hdr) + return (0); + memcpy(&hdr, BUFFER_OUT(c->in), sizeof hdr); + if (BUFFER_USED(c->in) < (sizeof hdr) + hdr.size) + return (0); + buffer_remove(c->in, sizeof hdr); + + for (i = 0; i < NSERVERMSG; i++) { + msg = server_msg_table + i; + if (msg->type == hdr.type) { + if ((n = msg->fn(&hdr, c)) != 0) + return (n); + break; + } + } + if (i == NSERVERMSG) + fatalx("unexpected message"); } - - fatalx("unexpected message"); } /* New message from client. */ -void -server_msg_fn_new(struct client *c, struct hdr *hdr) +int +server_msg_fn_new(struct hdr *hdr, struct client *c) { struct new_data data; const char *shell; char *cmd, *msg; if (c->session != NULL) - return; + return (0); if (hdr->size != sizeof data) fatalx("bad MSG_NEW size"); buffer_read(c->in, &data, hdr->size); @@ -110,9 +114,9 @@ server_msg_fn_new(struct client *c, struct hdr *hdr) if (*data.name != '\0' && session_find(data.name) != NULL) { xasprintf(&msg, "duplicate session: %s", data.name); - write_client(c, MSG_READY, msg, strlen(msg)); + write_client(c, MSG_ERROR, msg, strlen(msg)); xfree(msg); - return; + return (0); } shell = getenv("SHELL"); @@ -124,19 +128,20 @@ server_msg_fn_new(struct client *c, struct hdr *hdr) fatalx("session_create failed"); xfree(cmd); - write_client(c, MSG_READY, NULL, 0); draw_client(c, 0, c->sy - 1); + + return (0); } /* Attach message from client. */ -void -server_msg_fn_attach(struct client *c, struct hdr *hdr) +int +server_msg_fn_attach(struct hdr *hdr, struct client *c) { struct attach_data data; char *msg; if (c->session != NULL) - return; + return (0); if (hdr->size != sizeof data) fatalx("bad MSG_ATTACH size"); buffer_read(c->in, &data, hdr->size); @@ -152,24 +157,25 @@ server_msg_fn_attach(struct client *c, struct hdr *hdr) c->session = session_find(data.name); if (c->session == NULL) { xasprintf(&msg, "session not found: %s", data.name); - write_client(c, MSG_READY, msg, strlen(msg)); + write_client(c, MSG_ERROR, msg, strlen(msg)); xfree(msg); - return; + return (0); } - write_client(c, MSG_READY, NULL, 0); draw_client(c, 0, c->sy - 1); + + return (0); } /* Create message from client. */ -void -server_msg_fn_create(struct client *c, struct hdr *hdr) +int +server_msg_fn_create(struct hdr *hdr, struct client *c) { const char *shell; char *cmd; if (c->session == NULL) - return; + return (0); if (hdr->size != 0) fatalx("bad MSG_CREATE size"); @@ -182,14 +188,16 @@ server_msg_fn_create(struct client *c, struct hdr *hdr) xfree(cmd); draw_client(c, 0, c->sy - 1); + + return (0); } /* Next message from client. */ -void -server_msg_fn_next(struct client *c, struct hdr *hdr) +int +server_msg_fn_next(struct hdr *hdr, struct client *c) { if (c->session == NULL) - return; + return (0); if (hdr->size != 0) fatalx("bad MSG_NEXT size"); @@ -197,14 +205,16 @@ server_msg_fn_next(struct client *c, struct hdr *hdr) changed_window(c); else write_message(c, "No next window"); + + return (0); } /* Previous message from client. */ -void -server_msg_fn_previous(struct client *c, struct hdr *hdr) +int +server_msg_fn_previous(struct hdr *hdr, struct client *c) { if (c->session == NULL) - return; + return (0); if (hdr->size != 0) fatalx("bad MSG_PREVIOUS size"); @@ -212,16 +222,18 @@ server_msg_fn_previous(struct client *c, struct hdr *hdr) changed_window(c); else write_message(c, "No previous window"); + + return (0); } /* Size message from client. */ -void -server_msg_fn_size(struct client *c, struct hdr *hdr) +int +server_msg_fn_size(struct hdr *hdr, struct client *c) { struct size_data data; if (c->session == NULL) - return; + return (0); if (hdr->size != sizeof data) fatalx("bad MSG_SIZE size"); buffer_read(c->in, &data, hdr->size); @@ -235,55 +247,63 @@ server_msg_fn_size(struct client *c, struct hdr *hdr) if (window_resize(c->session->window, c->sx, c->sy) != 0) draw_client(c, 0, c->sy - 1); + + return (0); } /* Input message from client. */ -void -server_msg_fn_input(struct client *c, struct hdr *hdr) +int +server_msg_fn_input(struct hdr *hdr, struct client *c) { if (c->session == NULL) - return; + return (0); window_input(c->session->window, c->in, hdr->size); + + return (0); } /* Refresh message from client. */ -void -server_msg_fn_refresh(struct client *c, struct hdr *hdr) +int +server_msg_fn_refresh(struct hdr *hdr, struct client *c) { struct refresh_data data; if (c->session == NULL) - return; + return (0); if (hdr->size != 0 && hdr->size != sizeof data) fatalx("bad MSG_REFRESH size"); draw_client(c, 0, c->sy - 1); + + return (0); } /* Select message from client. */ -void -server_msg_fn_select(struct client *c, struct hdr *hdr) +int +server_msg_fn_select(struct hdr *hdr, struct client *c) { struct select_data data; if (c->session == NULL) - return; + return (0); if (hdr->size != sizeof data) fatalx("bad MSG_SELECT size"); buffer_read(c->in, &data, hdr->size); if (c->session == NULL) - return; + return (0); if (session_select(c->session, data.idx) == 0) changed_window(c); else write_message(c, "Window %u not present", data.idx); + + return (0); } /* Sessions message from client. */ -void -server_msg_fn_sessions(struct client *c, struct hdr *hdr) +int +server_msg_fn_sessions(struct hdr *hdr, struct client *c) { struct sessions_data data; struct sessions_entry entry; @@ -315,11 +335,13 @@ server_msg_fn_sessions(struct client *c, struct hdr *hdr) } buffer_write(c->out, &entry, sizeof entry); } + + return (0); } /* Windows message from client. */ -void -server_msg_fn_windows(struct client *c, struct hdr *hdr) +int +server_msg_fn_windows(struct hdr *hdr, struct client *c) { struct windows_data data; struct windows_entry entry; @@ -335,7 +357,7 @@ server_msg_fn_windows(struct client *c, struct hdr *hdr) if (s == NULL) { data.windows = 0; write_client(c, MSG_WINDOWS, &data, sizeof data); - return; + return (0); } data.windows = 0; @@ -357,14 +379,16 @@ server_msg_fn_windows(struct client *c, struct hdr *hdr) *entry.tty = '\0'; buffer_write(c->out, &entry, sizeof entry); } + + return (0); } /* Rename message from client. */ -void -server_msg_fn_rename(struct client *c, struct hdr *hdr) +int +server_msg_fn_rename(struct hdr *hdr, struct client *c) { if (c->session == NULL) - return; + return (0); if (hdr->size != 0) fatalx("bad MSG_RENAME size"); @@ -372,11 +396,11 @@ server_msg_fn_rename(struct client *c, struct hdr *hdr) } /* Last window message from client */ -void -server_msg_fn_last(struct client *c, struct hdr *hdr) +int +server_msg_fn_last(struct hdr *hdr, struct client *c) { if (c->session == NULL) - return; + return (0); if (hdr->size != 0) fatalx("bad MSG_LAST size"); @@ -384,11 +408,13 @@ server_msg_fn_last(struct client *c, struct hdr *hdr) changed_window(c); else write_message(c, "No last window"); + + return (0); } /* Window list message from client */ -void -server_msg_fn_windowlist(struct client *c, struct hdr *hdr) +int +server_msg_fn_windowlist(struct hdr *hdr, struct client *c) { struct window *w; char *buf; @@ -396,7 +422,7 @@ server_msg_fn_windowlist(struct client *c, struct hdr *hdr) u_int i; if (c->session == NULL) - return; + return (0); if (hdr->size != 0) fatalx("bad MSG_WINDOWLIST size"); @@ -417,4 +443,6 @@ server_msg_fn_windowlist(struct client *c, struct hdr *hdr) write_message(c, "%s", buf); xfree(buf); + + return (0); } diff --git a/server.c b/server.c index 21a35f4f..3c12543d 100644 --- a/server.c +++ b/server.c @@ -1,4 +1,4 @@ -/* $Id: server.c,v 1.12 2007-09-26 10:35:24 nicm Exp $ */ +/* $Id: server.c,v 1.13 2007-09-26 13:43:15 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -43,7 +44,7 @@ /* Client list. */ struct clients clients; -int server_main(int); +int server_main(char *, int); void fill_windows(struct pollfd **); void handle_windows(struct pollfd **); void fill_clients(struct pollfd **); @@ -52,21 +53,18 @@ struct client *accept_client(int); void lost_client(struct client *); void lost_window(struct window *); -/* Fork and start server process. */ int -server_start(void) +server_start(char *path) { - mode_t mode; - int fd; struct sockaddr_un sa; size_t sz; pid_t pid; - FILE *f; - char *path; + mode_t mode; + int fd; - /* Fork the server process. */ switch (pid = fork()) { case -1: + log_warn("fork"); return (-1); case 0: break; @@ -74,20 +72,15 @@ server_start(void) return (0); } - /* Start logging to file. */ - if (debug_level > 0) { - xasprintf(&path, - "%s-server-%ld.log", __progname, (long) getpid()); - f = fopen(path, "w"); - log_open(f, LOG_DAEMON, debug_level); - xfree(path); - } + logfile("server"); + setproctitle("server (%s)", path); + log_debug("server started, pid %ld", (long) getpid()); /* Create the socket. */ memset(&sa, 0, sizeof sa); sa.sun_family = AF_UNIX; - sz = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path); + sz = strlcpy(sa.sun_path, path, sizeof sa.sun_path); if (sz >= sizeof sa.sun_path) { errno = ENAMETOOLONG; fatal("socket failed"); @@ -113,32 +106,17 @@ server_start(void) fatal("daemon failed"); log_debug("server daemonised, pid now %ld", (long) getpid()); - setproctitle("server (%s)", socket_path); - exit(server_main(fd)); + exit(server_main(path, fd)); } /* Main server loop. */ int -server_main(int srv_fd) +server_main(char *srv_path, int srv_fd) { struct pollfd *pfds, *pfd; int nfds, mode; - struct sigaction 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(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(SIGQUIT, &act, NULL) != 0) - fatal("sigaction failed"); + siginit(); ARRAY_INIT(&windows); ARRAY_INIT(&clients); @@ -192,7 +170,7 @@ server_main(int srv_fd) } close(srv_fd); - unlink(socket_path); + unlink(srv_path); return (0); } diff --git a/tmux.c b/tmux.c index 881e7900..6ae982eb 100644 --- a/tmux.c +++ b/tmux.c @@ -1,4 +1,4 @@ -/* $Id: tmux.c,v 1.8 2007-09-21 18:00:58 nicm Exp $ */ +/* $Id: tmux.c,v 1.9 2007-09-26 13:43:15 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -17,17 +17,9 @@ */ #include -#include -#include -#include -#include #include -#include -#include -#include #include -#include #include #include #include @@ -40,44 +32,46 @@ const char *malloc_options = "AFGJPX"; #endif -void op_new(char *, struct winsize *); -void op_attach(char *, struct winsize *); -int connect_server(void); -int process_server(char **); -int process_local(void); -void sighandler(int); -__dead void usage(void); -__dead void main_list(char *); -void process_list(const char *); - -/* SIGWINCH received flag. */ volatile sig_atomic_t sigwinch; - -/* SIGTERM received flag. */ volatile sig_atomic_t sigterm; - -/* Debug output level. */ int debug_level; -/* Path to server socket. */ -char socket_path[MAXPATHLEN]; +void sighandler(int); -/* Server socket and buffers. */ -int server_fd = -1; -struct buffer *server_in; -struct buffer *server_out; +struct op { + const char *cmd; + int (*fn)(char *, int, char **); +}; +struct op op_table[] = { +// { "list", op_list }, + { "new", op_new }, + { "attach", op_attach } +}; +#define NOP (sizeof op_table / sizeof op_table[0]) -/* Local socket and buffers. */ -int local_fd = -1; -struct buffer *local_in; -struct buffer *local_out; - -__dead void -usage(void) +int +usage(const char *s) { - fprintf(stderr, - "usage: %s [-v] [-n name] [-s path] command\n", __progname); - exit(1); + if (s == NULL) + s = "command ..."; + fprintf(stderr, "usage: %s [-v] [-s path] %s\n", __progname, s); + return (1); +} + +void +logfile(const char *name) +{ + FILE *f; + char *path; + + log_close(); + if (debug_level > 0) { + xasprintf( + &path, "%s-%s-%ld.log", __progname, name, (long) getpid()); + f = fopen(path, "w"); + log_open(f, LOG_DAEMON, debug_level); + xfree(path); + } } void @@ -96,29 +90,78 @@ sighandler(int sig) } } +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(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"); + + 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"); +} + +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"); +} + int main(int argc, char **argv) { - int opt, mode, n; - char *path, *error, name[MAXNAMELEN]; - FILE *f; - enum op op; - struct pollfd pfds[2]; - struct hdr hdr; - struct size_data sd; - struct winsize ws; - struct sigaction act; - struct stat sb; + struct op *op; + char *path; + int opt; + u_int i; - *name = '\0'; path = NULL; - - while ((opt = getopt(argc, argv, "n:s:v?")) != EOF) { + while ((opt = getopt(argc, argv, "s:v?")) != EOF) { switch (opt) { - case 'n': - if (strlcpy(name, optarg, sizeof name) >= sizeof name) - errx(1, "name too long"); - break; case 's': path = xstrdup(optarg); break; @@ -127,449 +170,24 @@ main(int argc, char **argv) break; case '?': default: - usage(); + exit(usage(NULL)); } } argc -= optind; argv += optind; - if (argc != 1) - usage(); + if (argc == 0) + exit(usage(NULL)); - /* Determine command. */ - if (strncmp(argv[0], "list", strlen(argv[0])) == 0) - op = OP_LIST; - else if (strncmp(argv[0], "new", strlen(argv[0])) == 0) - op = OP_NEW; - else if (strncmp(argv[0], "attach", strlen(argv[0])) == 0) - op = OP_ATTACH; - else - usage(); + log_open(stderr, LOG_USER, debug_level); - /* Sort out socket path. */ - if (path == NULL) { - xasprintf(&path, - "%s/%s-%lu", _PATH_TMP, __progname, (u_long) getuid()); - } - if (realpath(path, socket_path) == NULL) - err(1, "realpath"); - xfree(path); - - /* Set up signal handlers. */ - 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) - err(1, "sigaction"); - if (sigaction(SIGUSR1, &act, NULL) != 0) - err(1, "sigaction"); - if (sigaction(SIGUSR2, &act, NULL) != 0) - err(1, "sigaction"); - if (sigaction(SIGINT, &act, NULL) != 0) - err(1, "sigaction"); - if (sigaction(SIGTSTP, &act, NULL) != 0) - err(1, "sigaction"); - if (sigaction(SIGQUIT, &act, NULL) != 0) - err(1, "sigaction"); - - act.sa_handler = sighandler; - if (sigaction(SIGWINCH, &act, NULL) != 0) - err(1, "sigaction"); - if (sigaction(SIGTERM, &act, NULL) != 0) - err(1, "sigaction"); - if (sigaction(SIGCHLD, &act, NULL) != 0) - err(1, "sigaction"); - - /* Start server if necessary. */ - n = 0; -restart: - if (stat(socket_path, &sb) != 0) { - if (errno != ENOENT) - err(1, "%s", socket_path); - else if (op != OP_LIST) { - if (server_start() != 0) - errx(1, "couldn't start server"); - sleep(1); /* XXX this sucks */ - } - } else { - if (!S_ISSOCK(sb.st_mode)) - errx(1, "%s: not a socket", socket_path); - } - - /* Connect to server. */ - if ((server_fd = connect_server()) == -1) { - if (errno == ECONNREFUSED && n++ < 5) { - unlink(socket_path); - goto restart; - } - errx(1, "couldn't find server"); - } - if ((mode = fcntl(server_fd, F_GETFL)) == -1) - err(1, "fcntl"); - if (fcntl(server_fd, F_SETFL, mode|O_NONBLOCK) == -1) - err(1, "fcntl"); - server_in = buffer_create(BUFSIZ); - server_out = buffer_create(BUFSIZ); - - /* Skip to list function if listing. */ - if (op == OP_LIST) - main_list(name); - - /* Check stdin/stdout. */ - if (!isatty(STDIN_FILENO)) - errx(1, "stdin is not a tty"); - if (!isatty(STDOUT_FILENO)) - errx(1, "stdout is not a tty"); - - /* Find window size. */ - if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) - err(1, "ioctl(TIOCGWINSZ)"); - - /* Send initial data. */ - switch (op) { - case OP_NEW: - op_new(name, &ws); - break; - case OP_ATTACH: - op_attach(name, &ws); - break; - default: - fatalx("unknown op"); - } - - /* Start logging to file. */ - if (debug_level > 0) { - xasprintf(&path, - "%s-client-%ld.log", __progname, (long) getpid()); - f = fopen(path, "w"); - log_open(f, LOG_USER, debug_level); - xfree(path); - } - setproctitle("client (%s)", name); - - /* Main loop. */ - n = 0; - while (!sigterm) { - /* Handle SIGWINCH if necessary. */ - if (local_fd != -1 && sigwinch) { - if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) - fatal("ioctl failed"); - - hdr.type = MSG_SIZE; - hdr.size = sizeof sd; - buffer_write(server_out, &hdr, sizeof hdr); - sd.sx = ws.ws_col; - sd.sy = ws.ws_row; - buffer_write(server_out, &sd, hdr.size); - - sigwinch = 0; - } - - /* Set up pollfds. */ - pfds[0].fd = server_fd; - pfds[0].events = POLLIN; - if (BUFFER_USED(server_out) > 0) - pfds[0].events |= POLLOUT; - pfds[1].fd = local_fd; - pfds[1].events = POLLIN; - if (local_fd != -1 && BUFFER_USED(local_out) > 0) - pfds[1].events |= POLLOUT; - - /* Do the poll. */ - if (poll(pfds, 2, INFTIM) == -1) { - if (errno == EAGAIN || errno == EINTR) - continue; - fatal("poll failed"); - } - - /* Read/write from sockets. */ - if (buffer_poll(&pfds[0], server_in, server_out) != 0) - goto server_dead; - if (local_fd != -1 && - buffer_poll(&pfds[1], local_in, local_out) != 0) - fatalx("lost local socket"); - - /* Output flushed; pause if requested. */ - if (n) - usleep(750000); - - /* Process any data. */ - if ((n = process_server(&error)) == -1) - break; - if (process_local() == -1) - break; - } - - if (local_fd != -1) - local_done(); - - if (error != NULL) - errx(1, "%s", error); - - if (!sigterm) - printf("[detached]\n"); - else - printf("[terminated]\n"); - exit(0); - -server_dead: - if (local_fd != -1) - local_done(); - - printf("[lost server]\n"); - exit(1); -} - -/* New command. */ -void -op_new(char *name, struct winsize *ws) -{ - struct new_data data; - struct hdr hdr; - - hdr.type = MSG_NEW; - hdr.size = sizeof data; - buffer_write(server_out, &hdr, sizeof hdr); - - strlcpy(data.name, name, sizeof data.name); - data.sx = ws->ws_col; - data.sy = ws->ws_row; - buffer_write(server_out, &data, hdr.size); -} - -/* Attach command. */ -void -op_attach(char *name, struct winsize *ws) -{ - struct attach_data data; - struct hdr hdr; - - hdr.type = MSG_ATTACH; - hdr.size = sizeof data; - buffer_write(server_out, &hdr, sizeof hdr); - - strlcpy(data.name, name, sizeof data.name); - data.sx = ws->ws_col; - data.sy = ws->ws_row; - buffer_write(server_out, &data, hdr.size); -} - -/* Connect to server socket from PID. */ -int -connect_server(void) -{ - int fd; - struct sockaddr_un sa; - size_t sz; - - memset(&sa, 0, sizeof sa); - sa.sun_family = AF_UNIX; - sz = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path); - if (sz >= sizeof sa.sun_path) { - errno = ENAMETOOLONG; - return (-1); - } - - if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) - return (-1); - if (connect(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) - return (-1); - return (fd); -} - -/* Handle data from server. */ -int -process_server(char **error) -{ - struct hdr hdr; - - *error = NULL; - for (;;) { - if (BUFFER_USED(server_in) < sizeof hdr) - break; - memcpy(&hdr, BUFFER_OUT(server_in), sizeof hdr); - if (BUFFER_USED(server_in) < (sizeof hdr) + hdr.size) - break; - buffer_remove(server_in, sizeof hdr); - - switch (hdr.type) { - case MSG_READY: - if (hdr.size != 0) { - xasprintf(error, "%.*s", - (int) hdr.size, BUFFER_OUT(server_in)); - return (-1); - } - local_fd = local_init(&local_in, &local_out); - break; - case MSG_OUTPUT: - local_output(server_in, hdr.size); - break; - case MSG_PAUSE: - if (hdr.size != 0) - fatalx("bad MSG_PAUSE size"); - return (1); - case MSG_EXIT: - return (-1); - default: - fatalx("unexpected message"); + for (i = 0; i < NOP; i++) { + op = op_table + i; + if (strncmp(argv[0], op->cmd, strlen(op->cmd)) == 0) { + argc--; + argv++; + exit(op->fn(path, argc, argv)); } } - return (0); -} - -/* Handle data from local terminal. */ -int -process_local(void) -{ - struct buffer *b; - struct hdr hdr; - size_t size; - int n, key; - - if (local_fd == -1) - return (0); - - n = 0; - b = buffer_create(BUFSIZ); - - while ((key = local_key(&size)) != KEYC_NONE) { - log_debug("key code: %d", key); - - if (key == cmd_prefix) { - if ((key = local_key(NULL)) == KEYC_NONE) { - buffer_reverse_remove(local_in, size); - break; - } - n = cmd_execute(key, server_out); - break; - } - - input_store8(b, '\e'); - input_store16(b, (uint16_t) key); - } - - if (BUFFER_USED(b) == 0) { - buffer_destroy(b); - return (n); - } - log_debug("transmitting %zu bytes of input", BUFFER_USED(b)); - - hdr.type = MSG_INPUT; - hdr.size = BUFFER_USED(b); - buffer_write(server_out, &hdr, sizeof hdr); - buffer_write(server_out, BUFFER_OUT(b), BUFFER_USED(b)); - - buffer_destroy(b); - return (n); -} - -/* List sessions or windows. */ -__dead void -main_list(char *name) -{ - struct sessions_data sd; - struct windows_data wd; - struct pollfd pfd; - struct hdr hdr; - - /* Send query data. */ - if (*name == '\0') { - hdr.type = MSG_SESSIONS; - hdr.size = sizeof sd; - buffer_write(server_out, &hdr, sizeof hdr); - buffer_write(server_out, &sd, hdr.size); - } else { - hdr.type = MSG_WINDOWS; - hdr.size = sizeof wd; - buffer_write(server_out, &hdr, sizeof hdr); - strlcpy(wd.name, name, sizeof wd.name); - buffer_write(server_out, &wd, hdr.size); - } - - /* Main loop. */ - for (;;) { - /* Set up pollfd. */ - pfd.fd = server_fd; - pfd.events = POLLIN; - if (BUFFER_USED(server_out) > 0) - pfd.events |= POLLOUT; - - /* Do the poll. */ - if (poll(&pfd, 1, INFTIM) == -1) { - if (errno == EAGAIN || errno == EINTR) - continue; - err(1, "poll"); - } - - /* Read/write from sockets. */ - if (buffer_poll(&pfd, server_in, server_out) != 0) - errx(1, "lost server"); - - /* Process data. */ - process_list(name); - } -} - -void -process_list(const char *name) -{ - struct sessions_data sd; - struct sessions_entry se; - struct windows_data wd; - struct windows_entry we; - struct hdr hdr; - char *tim; - - for (;;) { - if (BUFFER_USED(server_in) < sizeof hdr) - break; - memcpy(&hdr, BUFFER_OUT(server_in), sizeof hdr); - if (BUFFER_USED(server_in) < (sizeof hdr) + hdr.size) - break; - buffer_remove(server_in, sizeof hdr); - - switch (hdr.type) { - case MSG_SESSIONS: - if (hdr.size < sizeof sd) - errx(1, "bad MSG_SESSIONS size"); - buffer_read(server_in, &sd, sizeof sd); - hdr.size -= sizeof sd; - if (sd.sessions == 0 && hdr.size == 0) - exit(0); - if (hdr.size < sd.sessions * sizeof se) - errx(1, "bad MSG_SESSIONS size"); - while (sd.sessions-- > 0) { - buffer_read(server_in, &se, sizeof se); - tim = ctime(&se.tim); - *strchr(tim, '\n') = '\0'; - printf("%s: %u windows (created %s)\n", - se.name, se.windows, tim); - } - exit(0); - case MSG_WINDOWS: - if (hdr.size < sizeof wd) - errx(1, "bad MSG_WINDOWS size"); - buffer_read(server_in, &wd, sizeof wd); - hdr.size -= sizeof wd; - if (wd.windows == 0 && hdr.size == 0) - errx(1, "session not found: %s", name); - if (hdr.size < wd.windows * sizeof we) - errx(1, "bad MSG_WINDOWS size"); - while (wd.windows-- > 0) { - buffer_read(server_in, &we, sizeof we); - if (*we.title != '\0') { - printf("%u: %s \"%s\" (%s)\n", - we.idx, we.name, we.title, we.tty); - } else { - printf("%u: %s (%s)\n", - we.idx, we.name, we.tty); - } - } - exit(0); - default: - fatalx("unexpected message"); - } - } + exit(usage(NULL)); } diff --git a/tmux.h b/tmux.h index cb10a3cf..c35a951f 100644 --- a/tmux.h +++ b/tmux.h @@ -1,4 +1,4 @@ -/* $Id: tmux.h,v 1.11 2007-09-26 10:35:24 nicm Exp $ */ +/* $Id: tmux.h,v 1.12 2007-09-26 13:43:15 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -257,18 +257,11 @@ struct buffer { #define CODE_KKEYPADON 25 #define CODE_TITLE 26 -/* Command-line commands. */ -enum op { - OP_LIST = 0, - OP_NEW, - OP_ATTACH -}; - /* Message codes. */ enum hdrtype { MSG_NEW = 0, MSG_ATTACH, - MSG_READY, + MSG_ERROR, MSG_CREATE, MSG_EXIT, MSG_SIZE, @@ -423,17 +416,50 @@ struct client { }; ARRAY_DECL(clients, struct client *); +/* Client context. */ +struct client_ctx { + int srv_fd; + struct buffer *srv_in; + struct buffer *srv_out; + + int loc_fd; + struct buffer *loc_in; + struct buffer *loc_out; + + struct winsize ws; +}; + /* tmux.c */ -volatile sig_atomic_t sigterm; -extern int debug_level; -extern char socket_path[MAXPATHLEN]; +extern volatile sig_atomic_t sigwinch; +extern volatile sig_atomic_t sigterm; +extern int debug_level; +int usage(const char *); +void logfile(const char *); +void siginit(void); +void sigreset(void); + +/* op.c */ +int op_new(char *, int, char **); +int op_attach(char *, int, char **); + +/* client.c */ +int client_init(char *, struct client_ctx *, int); +int client_main(struct client_ctx *); +void client_write_server(struct client_ctx *, enum hdrtype, void *, size_t); + +/* client-msg.c */ +int client_msg_dispatch(struct client_ctx *, const char **); + +/* command.c */ +extern int client_cmd_prefix; +int client_cmd_dispatch(int, struct client_ctx *, const char **); /* server.c */ extern struct clients clients; -int server_start(void); +int server_start(char *); /* server-msg.c */ -void server_msg_dispatch(struct client *); +int server_msg_dispatch(struct client *); /* server-fn.c */ void write_message(struct client *, const char *, ...); @@ -468,10 +494,6 @@ void local_done(void); int local_key(size_t *); void local_output(struct buffer *, size_t); -/* command.c */ -extern int cmd_prefix; -int cmd_execute(int, struct buffer *); - /* window.c */ extern struct windows windows; struct window *window_create(const char *, u_int, u_int);