summaryrefslogtreecommitdiffstats
path: root/server.c
diff options
context:
space:
mode:
authorNicholas Marriott <nicholas.marriott@gmail.com>2007-07-09 19:04:12 +0000
committerNicholas Marriott <nicholas.marriott@gmail.com>2007-07-09 19:04:12 +0000
commita41ece5ff0d3ce7a0b7d987baa9759f8a012b48b (patch)
tree95adfffa5b036fe4c593254efd2b32aa03696b34 /server.c
parent2905e0ef10926ab6f538598228a71e40844a8be6 (diff)
Initial import to CVS. Basic functions are working, albeit with a couple of showstopper memory bugs and many missing features. Detaching, reattaching, creating new sessions, listing sessions work acceptably for using with shells. Simple curses programs (top, systat, tetris) and more complicated ones (mutt, emacs) that don't require scrolling regions (ESC[r) mostly work fine (including mutt, emacs). No status bar yet and no key remapping or other customisation.
Diffstat (limited to 'server.c')
-rw-r--r--server.c999
1 files changed, 999 insertions, 0 deletions
diff --git a/server.c b/server.c
new file mode 100644
index 00000000..5d6204dd
--- /dev/null
+++ b/server.c
@@ -0,0 +1,999 @@
+/* $Id: server.c,v 1.1.1.1 2007-07-09 19:03:42 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 <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <termios.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "tmux.h"
+
+/*
+ * Main server functions.
+ */
+
+/* Client list. */
+struct clients clients;
+
+int server_main(int);
+void fill_windows(struct pollfd **);
+void handle_windows(struct pollfd **);
+void fill_clients(struct pollfd **);
+void handle_clients(struct pollfd **);
+struct client *accept_client(int);
+void lost_client(struct client *);
+void user_start(struct client *, const char *, const char *,
+ size_t, void (*)(struct client *, const char *));
+void user_input(struct client *, size_t);
+void write_message(struct client *, const char *, ...);
+void write_client(struct client *, u_int, void *, size_t);
+void write_client2(
+ struct client *, u_int, void *, size_t, void *, size_t);
+void write_clients(struct window *, u_int, void *, size_t);
+void new_window(struct window *);
+void lost_window(struct window *);
+void changed_window(struct client *);
+void draw_client(struct client *, u_int, u_int);
+void process_client(struct client *);
+void process_identify_msg(struct client *, struct hdr *);
+void process_create_msg(struct client *, struct hdr *);
+void process_next_msg(struct client *, struct hdr *);
+void process_previous_msg(struct client *, struct hdr *);
+void process_select_msg(struct client *, struct hdr *);
+void process_size_msg(struct client *, struct hdr *);
+void process_input_msg(struct client *, struct hdr *);
+void process_refresh_msg(struct client *, struct hdr *);
+void process_sessions_msg(struct client *, struct hdr *);
+void process_windows_msg(struct client *, struct hdr *);
+void process_rename_msg(struct client *, struct hdr *);
+void rename_callback(struct client *, const char *);
+
+/* Fork and start server process. */
+int
+server_start(void)
+{
+ mode_t mode;
+ int fd;
+ struct sockaddr_un sa;
+ size_t sz;
+ pid_t pid;
+ FILE *f;
+ char *path;
+
+ /* Fork the server process. */
+ switch (pid = fork()) {
+ case -1:
+ return (-1);
+ case 0:
+ break;
+ default:
+ 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);
+ }
+ 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);
+ if (sz >= sizeof sa.sun_path) {
+ errno = ENAMETOOLONG;
+ log_fatal("socket");
+ }
+ unlink(sa.sun_path);
+
+ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
+ log_fatal("socket");
+
+ mode = umask(S_IXUSR|S_IRWXG|S_IRWXO);
+ if (bind(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1)
+ log_fatal("bind");
+ umask(mode);
+
+ if (listen(fd, 16) == -1)
+ log_fatal("listen");
+
+ /*
+ * Detach into the background. This means the PID changes which will
+ * have to be fixed in some way at some point... XXX
+ */
+ if (daemon(1, 1) != 0)
+ log_fatal("daemon");
+ log_debug("server daemonised, pid now %ld", (long) getpid());
+
+ setproctitle("server (%s)", socket_path);
+ exit(server_main(fd));
+}
+
+/* Main server loop. */
+int
+server_main(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)
+ log_fatal("sigaction");
+ if (sigaction(SIGUSR1, &act, NULL) != 0)
+ log_fatal("sigaction");
+ if (sigaction(SIGUSR2, &act, NULL) != 0)
+ log_fatal("sigaction");
+ if (sigaction(SIGINT, &act, NULL) != 0)
+ log_fatal("sigaction");
+ if (sigaction(SIGQUIT, &act, NULL) != 0)
+ log_fatal("sigaction");
+
+ ARRAY_INIT(&windows);
+ ARRAY_INIT(&clients);
+ ARRAY_INIT(&sessions);
+
+ if ((mode = fcntl(srv_fd, F_GETFL)) == -1)
+ log_fatal("fcntl");
+ if (fcntl(srv_fd, F_SETFL, mode|O_NONBLOCK) == -1)
+ log_fatal("fcntl");
+
+ pfds = NULL;
+ while (!sigterm) {
+ /* Initialise pollfd array. */
+ nfds = 1 + ARRAY_LENGTH(&windows) + ARRAY_LENGTH(&clients);
+ pfds = xrealloc(pfds, nfds, sizeof *pfds);
+ pfd = pfds;
+
+ /* Fill server socket. */
+ pfd->fd = srv_fd;
+ pfd->events = POLLIN;
+ pfd++;
+
+ /* Fill window and client sockets. */
+ fill_windows(&pfd);
+ fill_clients(&pfd);
+
+ /* Do the poll. */
+ if (poll(pfds, nfds, INFTIM) == -1) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ log_fatal("poll");
+ }
+ pfd = pfds;
+
+ /* Handle server socket. */
+ if (pfd->revents & (POLLERR|POLLNVAL|POLLHUP))
+ log_fatalx("lost server socket");
+ if (pfd->revents & POLLIN) {
+ accept_client(srv_fd);
+ continue;
+ }
+ pfd++;
+
+ /*
+ * Handle window and client sockets. Clients can create
+ * windows, so windows must come first to avoid messing up by
+ * increasing the array size.
+ */
+ handle_windows(&pfd);
+ handle_clients(&pfd);
+ }
+
+ close(srv_fd);
+ unlink(socket_path);
+
+ return (0);
+}
+
+/* Fill window pollfds. */
+void
+fill_windows(struct pollfd **pfd)
+{
+ struct window *w;
+ u_int i;
+
+ for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
+ if ((w = ARRAY_ITEM(&windows, i)) == NULL)
+ (*pfd)->fd = -1;
+ else {
+ (*pfd)->fd = w->fd;
+ (*pfd)->events = POLLIN;
+ if (BUFFER_USED(w->out) > 0)
+ (*pfd)->events |= POLLOUT;
+ }
+ (*pfd)++;
+ }
+}
+
+/* Handle window pollfds. */
+void
+handle_windows(struct pollfd **pfd)
+{
+ struct window *w;
+ u_int i;
+ struct buffer *b;
+
+ for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
+ if ((w = ARRAY_ITEM(&windows, i)) != NULL) {
+ if (window_poll(w, *pfd) != 0)
+ lost_window(w);
+ else {
+ b = buffer_create(BUFSIZ);
+ window_output(w, b);
+ if (BUFFER_USED(b) != 0) {
+ write_clients(w, MSG_OUTPUT,
+ BUFFER_OUT(b), BUFFER_USED(b));
+ }
+ buffer_destroy(b);
+ }
+ }
+ (*pfd)++;
+ }
+}
+
+/* Fill client pollfds. */
+void
+fill_clients(struct pollfd **pfd)
+{
+ struct client *c;
+ u_int i;
+
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ if ((c = ARRAY_ITEM(&clients, i)) == NULL)
+ (*pfd)->fd = -1;
+ else {
+ (*pfd)->fd = c->fd;
+ (*pfd)->events = POLLIN;
+ if (BUFFER_USED(c->out) > 0)
+ (*pfd)->events |= POLLOUT;
+ }
+ (*pfd)++;
+ }
+}
+
+/* Handle client pollfds. */
+void
+handle_clients(struct pollfd *(*pfd))
+{
+ struct client *c;
+ u_int i;
+
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ if ((c = ARRAY_ITEM(&clients, i)) != NULL) {
+ if (buffer_poll((*pfd), c->in, c->out) != 0)
+ lost_client(c);
+ else
+ process_client(c);
+ }
+ (*pfd)++;
+ }
+}
+
+/* accept(2) and create new client. */
+struct client *
+accept_client(int srv_fd)
+{
+ struct client *c;
+ struct sockaddr_storage sa;
+ socklen_t slen = sizeof sa;
+ int client_fd, mode;
+ u_int i;
+
+ client_fd = accept(srv_fd, (struct sockaddr *) &sa, &slen);
+ if (client_fd == -1) {
+ if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED)
+ return (NULL);
+ log_fatal("accept");
+ }
+ if ((mode = fcntl(client_fd, F_GETFL)) == -1)
+ log_fatal("fcntl");
+ if (fcntl(client_fd, F_SETFL, mode|O_NONBLOCK) == -1)
+ log_fatal("fcntl");
+
+ c = xmalloc(sizeof *c);
+ c->fd = client_fd;
+ c->in = buffer_create(BUFSIZ);
+ c->out = buffer_create(BUFSIZ);
+ c->session = NULL;
+ c->prompt = NULL;
+
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ if (ARRAY_ITEM(&clients, i) == NULL) {
+ ARRAY_SET(&clients, i, c);
+ return (c);
+ }
+ }
+ ARRAY_ADD(&clients, c);
+ return (c);
+}
+
+/* Lost a client. */
+void
+lost_client(struct client *c)
+{
+ u_int i;
+
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ if (ARRAY_ITEM(&clients, i) == c)
+ ARRAY_SET(&clients, i, NULL);
+ }
+
+ close(c->fd);
+ buffer_destroy(c->in);
+ buffer_destroy(c->out);
+ xfree(c);
+}
+
+/* Write message command to a client. */
+void
+write_message(struct client *c, const char *fmt, ...)
+{
+ struct hdr hdr;
+ va_list ap;
+ char *msg;
+ size_t size;
+ u_int i;
+
+ buffer_ensure(c->out, sizeof hdr);
+ buffer_add(c->out, sizeof hdr);
+ size = BUFFER_USED(c->out);
+
+ input_store_zero(c->out, CODE_CURSOROFF);
+ input_store_two(c->out, CODE_CURSORMOVE, c->sy, 1);
+ input_store_one(c->out, CODE_ATTRIBUTES, 2);
+ input_store16(c->out, 0);
+ input_store16(c->out, 7);
+ va_start(ap, fmt);
+ xvasprintf(&msg, fmt, ap);
+ buffer_write(c->out, msg, strlen(msg));
+ xfree(msg);
+ va_end(ap);
+ for (i = strlen(msg); i < c->sx; i++)
+ input_store8(c->out, ' ');
+
+ size = BUFFER_USED(c->out) - size;
+ hdr.code = MSG_OUTPUT;
+ hdr.size = size;
+ memcpy(BUFFER_IN(c->out) - size - sizeof hdr, &hdr, sizeof hdr);
+
+ hdr.code = MSG_PAUSE;
+ hdr.size = 0;
+ buffer_write(c->out, &hdr, sizeof hdr);
+
+ buffer_ensure(c->out, sizeof hdr);
+ buffer_add(c->out, sizeof hdr);
+ size = BUFFER_USED(c->out);
+
+ screen_draw(&c->session->window->screen, c->out, c->sy - 1, c->sy - 1);
+
+ size = BUFFER_USED(c->out) - size;
+ hdr.code = MSG_OUTPUT;
+ hdr.size = size;
+ memcpy(BUFFER_IN(c->out) - size - sizeof hdr, &hdr, sizeof hdr);
+}
+
+/* Start user input. */
+void
+user_start(struct client *c, const char *prompt, const char *now,
+ size_t len, void (*callback)(struct client *, const char *))
+{
+ struct hdr hdr;
+ size_t size;
+ u_int i;
+
+ c->callback = callback;
+ c->prompt = prompt;
+
+ c->len = len;
+ if (c->len > c->sx - strlen(c->prompt))
+ c->len = c->sx - strlen(c->prompt);
+ c->buf = xmalloc(c->len + 1);
+ strlcpy(c->buf, now, c->len + 1);
+ c->idx = strlen(c->buf);
+
+ buffer_ensure(c->out, sizeof hdr);
+ buffer_add(c->out, sizeof hdr);
+ size = BUFFER_USED(c->out);
+
+ input_store_zero(c->out, CODE_CURSOROFF);
+ input_store_two(c->out, CODE_CURSORMOVE, c->sy, 1);
+ input_store_one(c->out, CODE_ATTRIBUTES, 2);
+ input_store16(c->out, 0);
+ input_store16(c->out, 7);
+
+ i = 0;
+ buffer_write(c->out, c->prompt, strlen(c->prompt));
+ i += strlen(c->prompt);
+ if (*c->buf != '\0') {
+ buffer_write(c->out, c->buf, strlen(c->buf));
+ i += strlen(c->buf);
+ }
+ for (; i < c->sx; i++)
+ input_store8(c->out, ' ');
+
+ input_store_two(c->out,
+ CODE_CURSORMOVE, c->sy, 1 + strlen(c->prompt) + c->idx);
+ input_store_zero(c->out, CODE_CURSORON);
+
+ size = BUFFER_USED(c->out) - size;
+ hdr.code = MSG_OUTPUT;
+ hdr.size = size;
+ memcpy(BUFFER_IN(c->out) - size - sizeof hdr, &hdr, sizeof hdr);
+}
+
+/* Handle user input. */
+void
+user_input(struct client *c, size_t in)
+{
+ struct hdr hdr;
+ size_t size;
+ int key;
+ u_int i;
+
+ buffer_ensure(c->out, sizeof hdr);
+ buffer_add(c->out, sizeof hdr);
+ size = BUFFER_USED(c->out);
+
+ while (in != 0) {
+ if (in < 1)
+ break;
+ in--;
+ key = input_extract8(c->in);
+ if (key == '\e') {
+ if (in < 2)
+ log_fatalx("user_input: underflow");
+ in -= 2;
+ key = (int16_t) input_extract16(c->in);
+ }
+
+ again:
+ if (key == '\r') {
+ screen_draw(&c->session->window->screen,
+ c->out, c->sy - 1, c->sy - 1);
+
+ c->callback(c, c->buf);
+ c->prompt = NULL;
+ xfree(c->buf);
+ break;
+ }
+
+ switch (key) {
+ case KEYC_LEFT:
+ if (c->idx > 0)
+ c->idx--;
+ input_store_two(c->out, CODE_CURSORMOVE,
+ c->sy, 1 + strlen(c->prompt) + c->idx);
+ break;
+ case KEYC_RIGHT:
+ if (c->idx < strlen(c->buf))
+ c->idx++;
+ input_store_two(c->out, CODE_CURSORMOVE,
+ c->sy, 1 + strlen(c->prompt) + c->idx);
+ break;
+ case KEYC_HOME:
+ c->idx = 0;
+ input_store_two(c->out, CODE_CURSORMOVE,
+ c->sy, 1 + strlen(c->prompt) + c->idx);
+ break;
+ case KEYC_LL:
+ c->idx = strlen(c->buf);
+ input_store_two(c->out, CODE_CURSORMOVE,
+ c->sy, 1 + strlen(c->prompt) + c->idx);
+ break;
+ case KEYC_BACKSPACE:
+ if (c->idx == 0)
+ break;
+ if (strlen(c->buf) == 0)
+ break;
+ if (c->idx == strlen(c->buf))
+ c->buf[c->idx - 1] = '\0';
+ else {
+ memmove(c->buf + c->idx - 1,
+ c->buf + c->idx, c->len - c->idx);
+ }
+ c->idx--;
+ input_store_one(c->out, CODE_CURSORLEFT, 1);
+ input_store_one(c->out, CODE_DELETECHARACTER, 1);
+ input_store_zero(c->out, CODE_CURSOROFF);
+ input_store_two(c->out, CODE_CURSORMOVE, c->sy, c->sx);
+ input_store8(c->out, ' ');
+ input_store_two(c->out, CODE_CURSORMOVE,
+ c->sy, 1 + strlen(c->prompt) + c->idx);
+ input_store_zero(c->out, CODE_CURSORON);
+ break;
+ case KEYC_DC:
+ if (strlen(c->buf) == 0)
+ break;
+ if (c->idx == strlen(c->buf))
+ break;
+ memmove(c->buf + c->idx,
+ c->buf + c->idx + 1, c->len - c->idx - 1);
+ input_store_one(c->out, CODE_DELETECHARACTER, 1);
+ input_store_zero(c->out, CODE_CURSOROFF);
+ input_store_two(c->out,CODE_CURSORMOVE, c->sy, c->sx);
+ input_store8(c->out, ' ');
+ input_store_two(c->out, CODE_CURSORMOVE,
+ c->sy, 1 + strlen(c->prompt) + c->idx);
+ input_store_zero(c->out, CODE_CURSORON);
+ break;
+ default:
+ if (key >= ' ' && key != '\177') {
+ if (c->idx == c->len)
+ break;
+ if (strlen(c->buf) == c->len)
+ break;
+ memmove(c->buf + c->idx + 1,
+ c->buf + c->idx, c->len - c->idx);
+ c->buf[c->idx++] = key;
+ input_store_one(
+ c->out, CODE_INSERTCHARACTER, 1);
+ input_store8(c->out, key);
+ break;
+ }
+ switch (key) {
+ case '\001':
+ key = KEYC_HOME;
+ goto again;
+ case '\005':
+ key = KEYC_LL;
+ goto again;
+ case '\013':
+ c->buf[c->idx + 1] = '\0';
+ input_store_zero(c->out, CODE_CURSOROFF);
+ i = 1 + strlen(c->prompt) + c->idx;
+ for (; i < c->sx; i++)
+ input_store8(c->out, ' ');
+ input_store_two(c->out, CODE_CURSORMOVE,
+ c->sy, 1 + strlen(c->prompt) + c->idx);
+ input_store_zero(c->out, CODE_CURSORON);
+ break;
+ }
+ }
+ }
+
+ size = BUFFER_USED(c->out) - size;
+ if (size != 0) {
+ hdr.code = MSG_OUTPUT;
+ hdr.size = size;
+ memcpy(BUFFER_IN(c->out) - size - sizeof hdr, &hdr, sizeof hdr);
+ } else
+ buffer_reverse_add(c->out, sizeof hdr);
+}
+
+/* Write command to a client. */
+void
+write_client(struct client *c, u_int cmd, void *buf, size_t len)
+{
+ struct hdr hdr;
+
+ hdr.code = cmd;
+ hdr.size = len;
+
+ buffer_write(c->out, &hdr, sizeof hdr);
+ if (buf != NULL)
+ buffer_write(c->out, buf, len);
+}
+
+/* Write command to a client with two buffers. */
+void
+write_client2(struct client *c,
+ u_int cmd, void *buf1, size_t len1, void *buf2, size_t len2)
+{
+ struct hdr hdr;
+
+ hdr.code = cmd;
+ hdr.size = len1 + len2;
+
+ buffer_write(c->out, &hdr, sizeof hdr);
+ if (buf1 != NULL)
+ buffer_write(c->out, buf1, len1);
+ if (buf2 != NULL)
+ buffer_write(c->out, buf2, len2);
+}
+
+/* Write command to all clients attached to a specific window. */
+void
+write_clients(struct window *w, u_int cmd, void *buf, size_t len)
+{
+ struct client *c;
+ struct hdr hdr;
+ u_int i;
+
+ hdr.code = cmd;
+ hdr.size = len;
+
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ c = ARRAY_ITEM(&clients, i);
+ if (c != NULL && c->session != NULL) {
+ if (c->session->window == w) {
+ buffer_write(c->out, &hdr, sizeof hdr);
+ if (buf != NULL)
+ buffer_write(c->out, buf, len);
+ }
+ }
+ }
+}
+
+/* Lost window: move clients on to next window. */
+void
+lost_window(struct window *w)
+{
+ struct client *c;
+ u_int i;
+
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ c = ARRAY_ITEM(&clients, i);
+ if (c != NULL && c->session != NULL) {
+ if (session_has(c->session, w)) {
+ if (session_detach(c->session, w) != 0)
+ write_client(c, MSG_EXIT, NULL, 0);
+ changed_window(c);
+ }
+ }
+ }
+}
+
+/* Changed client window. */
+void
+changed_window(struct client *c)
+{
+ struct window *w;
+
+ w = c->session->window;
+ if (c->sx != w->screen.sx || c->sy != w->screen.sy)
+ window_resize(w, c->sx, c->sy);
+ draw_client(c, 0, c->sy - 1);
+}
+
+/* Draw window on client. */
+void
+draw_client(struct client *c, u_int py_upper, u_int py_lower)
+{
+ struct hdr hdr;
+ size_t size;
+
+ buffer_ensure(c->out, sizeof hdr);
+ buffer_add(c->out, sizeof hdr);
+ size = BUFFER_USED(c->out);
+
+ screen_draw(&c->session->window->screen, c->out, py_upper, py_lower);
+
+ size = BUFFER_USED(c->out) - size;
+ log_debug("redrawing screen, %zu bytes", size);
+ if (size != 0) {
+ hdr.code = MSG_OUTPUT;
+ hdr.size = size;
+ memcpy(
+ BUFFER_IN(c->out) - size - sizeof hdr, &hdr, sizeof hdr);
+ } else
+ buffer_reverse_add(c->out, sizeof hdr);
+}
+
+/* Process a command from the client. */
+void
+process_client(struct client *c)
+{
+ struct hdr hdr;
+
+ 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);
+
+ switch (hdr.code) {
+ case MSG_IDENTIFY:
+ process_identify_msg(c, &hdr);
+ break;
+ case MSG_CREATE:
+ process_create_msg(c, &hdr);
+ break;
+ case MSG_NEXT:
+ process_next_msg(c, &hdr);
+ break;
+ case MSG_PREVIOUS:
+ process_previous_msg(c, &hdr);
+ break;
+ case MSG_SIZE:
+ process_size_msg(c, &hdr);
+ break;
+ case MSG_INPUT:
+ process_input_msg(c, &hdr);
+ break;
+ case MSG_REFRESH:
+ process_refresh_msg(c, &hdr);
+ break;
+ case MSG_SELECT:
+ process_select_msg(c, &hdr);
+ break;
+ case MSG_SESSIONS:
+ process_sessions_msg(c, &hdr);
+ break;
+ case MSG_WINDOWS:
+ process_windows_msg(c, &hdr);
+ break;
+ case MSG_RENAME:
+ process_rename_msg(c, &hdr);
+ break;
+ }
+}
+
+/* Identify message from client. */
+void
+process_identify_msg(struct client *c, struct hdr *hdr)
+{
+ struct identify_data data;
+
+ if (hdr->size != sizeof data)
+ log_fatalx("bad MSG_IDENTIFY size");
+ buffer_read(c->in, &data, hdr->size);
+
+ c->sx = data.sx;
+ if (c->sx == 0)
+ c->sx = 80;
+ c->sy = data.sy;
+ if (c->sy == 0)
+ c->sy = 25;
+
+ /* Try and find session or create if not found. */
+ c->session = session_find(data.name);
+ if (c->session == NULL) {
+ c->session =
+ session_create(data.name, "/bin/ksh -l", c->sx, c->sy);
+ }
+ if (c->session == NULL)
+ log_fatalx("process_identify_msg: session_create failed");
+
+ draw_client(c, 0, c->sy - 1);
+}
+
+/* Create message from client. */
+void
+process_create_msg(struct client *c, struct hdr *hdr)
+{
+ if (c->session == NULL)
+ log_fatalx("MSG_CREATE before identified");
+ if (hdr->size != 0)
+ log_fatalx("bad MSG_CREATE size");
+
+ if (session_new(c->session, "/bin/ksh -l", c->sx, c->sy) != 0)
+ log_fatalx("process_create_msg: session_new failed");
+
+ draw_client(c, 0, c->sy - 1);
+}
+
+/* Next message from client. */
+void
+process_next_msg(struct client *c, struct hdr *hdr)
+{
+ if (c->session == NULL)
+ log_fatalx("MSG_NEXT before identified");
+ if (hdr->size != 0)
+ log_fatalx("bad MSG_NEXT size");
+
+ if (session_next(c->session) == 0)
+ changed_window(c);
+ else
+ write_message(c, "No next window");
+}
+
+/* Previous message from client. */
+void
+process_previous_msg(struct client *c, struct hdr *hdr)
+{
+ if (c->session == NULL)
+ log_fatalx("MSG_PREVIOUS before identified");
+ if (hdr->size != 0)
+ log_fatalx("bad MSG_PREVIOUS size");
+
+ if (session_previous(c->session) == 0)
+ changed_window(c);
+ else
+ write_message(c, "No previous window");
+}
+
+/* Size message from client. */
+void
+process_size_msg(struct client *c, struct hdr *hdr)
+{
+ struct size_data data;
+
+ if (c->session == NULL)
+ log_fatalx("MSG_SIZE before identified");
+ if (hdr->size != sizeof data)
+ log_fatalx("bad MSG_SIZE size");
+ buffer_read(c->in, &data, hdr->size);
+
+ c->sx = data.sx;
+ if (c->sx == 0)
+ c->sx = 80;
+ c->sy = data.sy;
+ if (c->sy == 0)
+ c->sy = 25;
+
+ if (window_resize(c->session->window, c->sx, c->sy) != 0)
+ draw_client(c, 0, c->sy - 1);
+}
+
+/* Input message from client. */
+void
+process_input_msg(struct client *c, struct hdr *hdr)
+{
+ if (c->session == NULL)
+ log_fatalx("MSG_INPUT before identified");
+
+ if (c->prompt == NULL)
+ window_input(c->session->window, c->in, hdr->size);
+ else
+ user_input(c, hdr->size);
+}
+
+/* Refresh message from client. */
+void
+process_refresh_msg(struct client *c, struct hdr *hdr)
+{
+ struct refresh_data data;
+
+ if (c->session == NULL)
+ log_fatalx("MSG_REFRESH before identified");
+ if (hdr->size != 0 && hdr->size != sizeof data)
+ log_fatalx("bad MSG_REFRESH size");
+
+ draw_client(c, 0, c->sy - 1);
+}
+
+/* Select message from client. */
+void
+process_select_msg(struct client *c, struct hdr *hdr)
+{
+ struct select_data data;
+
+ if (c->session == NULL)
+ log_fatalx("MSG_SELECT before identified");
+ if (hdr->size != sizeof data)
+ log_fatalx("bad MSG_SELECT size");
+ buffer_read(c->in, &data, hdr->size);
+
+ if (c->session == NULL)
+ return;
+ if (session_select(c->session, data.idx) == 0)
+ changed_window(c);
+ else
+ write_message(c, "Window %u not present", data.idx);
+}
+
+/* Sessions message from client. */
+void
+process_sessions_msg(struct client *c, struct hdr *hdr)
+{
+ struct sessions_data data;
+ struct sessions_entry entry;
+ struct session *s;
+ u_int i, j;
+
+ if (hdr->size != sizeof data)
+ log_fatalx("bad MSG_SESSIONS size");
+ buffer_read(c->in, &data, hdr->size);
+
+ data.sessions = 0;
+ for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
+ if (ARRAY_ITEM(&sessions, i) != NULL)
+ data.sessions++;
+ }
+ write_client2(c, MSG_SESSIONS,
+ &data, sizeof data, NULL, data.sessions * sizeof entry);
+
+ for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
+ s = ARRAY_ITEM(&sessions, i);
+ if (s == NULL)
+ continue;
+ strlcpy(entry.name, s->name, sizeof entry.name);
+ entry.tim = s->tim;
+ entry.windows = 0;
+ for (j = 0; j < ARRAY_LENGTH(&s->windows); j++) {
+ if (ARRAY_ITEM(&s->windows, j) != NULL)
+ entry.windows++;
+ }
+ buffer_write(c->out, &entry, sizeof entry);
+ }
+}
+
+/* Windows message from client. */
+void
+process_windows_msg(struct client *c, struct hdr *hdr)
+{
+ struct windows_data data;
+ struct windows_entry entry;
+ struct session *s;
+ struct window *w;
+ u_int i;
+
+ if (hdr->size != sizeof data)
+ log_fatalx("bad MSG_WINDOWS size");
+ buffer_read(c->in, &data, hdr->size);
+
+ s = session_find(data.name);
+ if (s == NULL) {
+ data.windows = 0;
+ write_client(c, MSG_WINDOWS, &data, sizeof data);
+ return;
+ }
+
+ data.windows = 0;
+ for (i = 0; i < ARRAY_LENGTH(&s->windows); i++) {
+ if (ARRAY_ITEM(&windows, i) != NULL)
+ data.windows++;
+ }
+ write_client2(c, MSG_WINDOWS,
+ &data, sizeof data, NULL, data.windows * sizeof entry);
+
+ for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
+ w = ARRAY_ITEM(&windows, i);
+ if (w == NULL)
+ continue;
+ entry.idx = i;
+ strlcpy(entry.name, w->name, sizeof entry.name);
+ strlcpy(entry.title, w->screen.title, sizeof entry.title);
+ if (ttyname_r(w->fd, entry.tty, sizeof entry.tty) != 0)
+ *entry.tty = '\0';
+ buffer_write(c->out, &entry, sizeof entry);
+ }
+}
+
+/* Rename message from client. */
+void
+process_rename_msg(struct client *c, struct hdr *hdr)
+{
+ if (c->session == NULL)
+ log_fatalx("MSG_RENAME before identified");
+ if (hdr->size != 0)
+ log_fatalx("bad MSG_RENAME size");
+
+ user_start(c, "Window name: ",
+ c->session->window->name, MAXNAMELEN, rename_callback);
+}
+
+/* Callback for rename. */
+void
+rename_callback(struct client *c, const char *string)
+{
+ strlcpy(
+ c->session->window->name, string, sizeof c->session->window->name);
+}