aboutsummaryrefslogtreecommitdiff
path: root/server.c
diff options
context:
space:
mode:
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);
+}