/* $Id: server-msg.c,v 1.12 2007-09-30 13:02: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 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_windowinfo(struct hdr *, struct client *); int server_msg_fn_windows(struct hdr *, struct client *); struct server_msg { enum hdrtype type; int (*fn)(struct hdr *, struct client *); }; struct server_msg server_msg_table[] = { { MSG_ATTACH, server_msg_fn_attach }, { MSG_CREATE, server_msg_fn_create }, { MSG_INPUT, server_msg_fn_input }, { MSG_LAST, server_msg_fn_last }, { MSG_NEW, server_msg_fn_new }, { MSG_NEXT, server_msg_fn_next }, { MSG_PREVIOUS, server_msg_fn_previous }, { MSG_REFRESH, server_msg_fn_refresh }, { MSG_RENAME, server_msg_fn_rename }, { MSG_SELECT, server_msg_fn_select }, { MSG_SESSIONS, server_msg_fn_sessions }, { MSG_SIZE, server_msg_fn_size }, { MSG_WINDOWLIST, server_msg_fn_windowlist }, { MSG_WINDOWINFO, server_msg_fn_windowinfo }, { MSG_WINDOWS, server_msg_fn_windows }, }; #define NSERVERMSG (sizeof server_msg_table / sizeof server_msg_table[0]) int server_msg_dispatch(struct client *c) { struct hdr hdr; struct server_msg *msg; u_int i; int n; 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"); } } /* New message from client. */ 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 (0); if (hdr->size != sizeof data) fatalx("bad MSG_NEW 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; data.name[(sizeof data.name) - 1] = '\0'; if (*data.name != '\0' && session_find(data.name) != NULL) { xasprintf(&msg, "duplicate session: %s", data.name); server_write_client(c, MSG_ERROR, msg, strlen(msg)); xfree(msg); return (0); } shell = getenv("SHELL"); if (shell == NULL) shell = "/bin/ksh"; xasprintf(&cmd, "%s -l", shell); c->session = session_create(data.name, cmd, c->sx, c->sy); if (c->session == NULL) fatalx("session_create failed"); xfree(cmd); server_write_client(c, MSG_DONE, NULL, 0); server_draw_client(c, 0, c->sy - 1); return (0); } /* Attach message from client. */ int server_msg_fn_attach(struct hdr *hdr, struct client *c) { struct attach_data data; char *cause; if (c->session != NULL) return (0); if (hdr->size != sizeof data) fatalx("bad MSG_ATTACH 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 ((c->session = server_find_sessid(&data.sid, &cause)) == NULL) { server_write_error(c, "%s", cause); xfree(cause); return (0); } server_draw_client(c, 0, c->sy - 1); return (0); } /* Create message from client. */ int server_msg_fn_create(struct hdr *hdr, struct client *c) { const char *shell; char *cmd; if (c->session == NULL) return (0); if (hdr->size != 0) fatalx("bad MSG_CREATE size"); shell = getenv("SHELL"); if (shell == NULL) shell = "/bin/ksh"; xasprintf(&cmd, "%s -l", shell); if (session_new(c->session, cmd, c->sx, c->sy) != 0) fatalx("session_new failed"); xfree(cmd); server_draw_client(c, 0, c->sy - 1); return (0); } /* Next message from client. */ int server_msg_fn_next(struct hdr *hdr, struct client *c) { if (c->session == NULL) return (0); if (hdr->size != 0) fatalx("bad MSG_NEXT size"); if (session_next(c->session) == 0) server_window_changed(c); else server_write_message(c, "No next window"); return (0); } /* Previous message from client. */ int server_msg_fn_previous(struct hdr *hdr, struct client *c) { if (c->session == NULL) return (0); if (hdr->size != 0) fatalx("bad MSG_PREVIOUS size"); if (session_previous(c->session) == 0) server_window_changed(c); else server_write_message(c, "No previous window"); return (0); } /* Size message from client. */ int server_msg_fn_size(struct hdr *hdr, struct client *c) { struct size_data data; if (c->session == NULL) return (0); if (hdr->size != sizeof data) 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) server_draw_client(c, 0, c->sy - 1); return (0); } /* Input message from client. */ int server_msg_fn_input(struct hdr *hdr, struct client *c) { if (c->session == NULL) return (0); window_input(c->session->window, c->in, hdr->size); return (0); } /* Refresh message from client. */ int server_msg_fn_refresh(struct hdr *hdr, struct client *c) { struct refresh_data data; if (c->session == NULL) return (0); if (hdr->size != 0 && hdr->size != sizeof data) fatalx("bad MSG_REFRESH size"); server_draw_client(c, 0, c->sy - 1); return (0); } /* Select message from client. */ int server_msg_fn_select(struct hdr *hdr, struct client *c) { struct select_data data; if (c->session == NULL) return (0); if (hdr->size != sizeof data) fatalx("bad MSG_SELECT size"); buffer_read(c->in, &data, hdr->size); if (c->session == NULL) return (0); if (session_select(c->session, data.idx) == 0) server_window_changed(c); else server_write_message(c, "Window %u not present", data.idx); return (0); } /* Sessions message from client. */ int server_msg_fn_sessions(struct hdr *hdr, struct client *c) { struct sessions_data data; struct sessions_entry entry; struct session *s; u_int i, j; if (hdr->size != sizeof data) 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++; } server_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); } return (0); } /* Windows message from client. */ int server_msg_fn_windows(struct hdr *hdr, struct client *c) { struct windows_data data; struct windows_entry entry; struct session *s; struct window *w; u_int i; char *cause; if (hdr->size != sizeof data) fatalx("bad MSG_WINDOWS size"); buffer_read(c->in, &data, hdr->size); if ((s = server_find_sessid(&data.sid, &cause)) == NULL) { server_write_error(c, "%s", cause); xfree(cause); return (0); } data.windows = 0; for (i = 0; i < ARRAY_LENGTH(&s->windows); i++) { if (ARRAY_ITEM(&s->windows, i) != NULL) data.windows++; } server_write_client2(c, MSG_WINDOWS, &data, sizeof data, NULL, data.windows * sizeof entry); for (i = 0; i < ARRAY_LENGTH(&s->windows); i++) { w = ARRAY_ITEM(&s->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); } return (0); } /* Rename message from client. */ int server_msg_fn_rename(struct hdr *hdr, struct client *c) { struct rename_data data; char *cause; struct window *w; struct session *s; if (hdr->size != sizeof data) fatalx("bad MSG_RENAME size"); buffer_read(c->in, &data, hdr->size); data.newname[(sizeof data.newname) - 1] = '\0'; if ((s = server_find_sessid(&data.sid, &cause)) == NULL) { server_write_error(c, "%s", cause); xfree(cause); return (0); } if (data.idx == -1) w = s->window; else { if (data.idx < 0) fatalx("bad window index"); w = window_at(&s->windows, data.idx); if (w == NULL) { server_write_error(c, "window not found: %d", data.idx); return (0); } } strlcpy(w->name, data.newname, sizeof w->name); server_write_client(c, MSG_DONE, NULL, 0); return (0); } /* Last window message from client */ int server_msg_fn_last(struct hdr *hdr, struct client *c) { if (c->session == NULL) return (0); if (hdr->size != 0) fatalx("bad MSG_LAST size"); if (session_last(c->session) == 0) server_window_changed(c); else server_write_message(c, "No last window"); return (0); } /* Window list message from client */ int server_msg_fn_windowlist(struct hdr *hdr, struct client *c) { struct window *w; char *buf; size_t len, off; u_int i; if (c->session == NULL) return (0); if (hdr->size != 0) fatalx("bad MSG_WINDOWLIST size"); len = c->sx + 1; buf = xmalloc(len); off = 0; *buf = '\0'; for (i = 0; i < ARRAY_LENGTH(&c->session->windows); i++) { w = ARRAY_ITEM(&c->session->windows, i); if (w == NULL) continue; off += xsnprintf(buf + off, len - off, "%u:%s%s ", i, w->name, w == c->session->window ? "*" : ""); if (off >= len) break; } server_write_message(c, "%s", buf); xfree(buf); return (0); } /* Window info message from client */ int server_msg_fn_windowinfo(struct hdr *hdr, struct client *c) { struct window *w; char *buf; size_t len; u_int i; if (c->session == NULL) return (0); if (hdr->size != 0) fatalx("bad MSG_WINDOWINFO size"); len = c->sx + 1; buf = xmalloc(len); w = c->session->window; window_index(&c->session->windows, w, &i); xsnprintf(buf, len, "%u:%s \"%s\" (size %u,%u) (cursor %u,%u) " "(region %u,%u)", i, w->name, w->screen.title, w->screen.sx, w->screen.sy, w->screen.cx, w->screen.cy, w->screen.ry_upper, w->screen.ry_lower); server_write_message(c, "%s", buf); xfree(buf); return (0); }