| /* $OpenBSD$ */ |
| |
| /* |
| * 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 <errno.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <time.h> |
| #include <unistd.h> |
| |
| #include "tmux.h" |
| |
| void server_msg_command(struct client *, struct msg_command_data *); |
| void server_msg_identify(struct client *, struct msg_identify_data *, int); |
| void server_msg_resize(struct client *, struct msg_resize_data *); |
| |
| void printflike2 server_msg_command_error(struct cmd_ctx *, const char *, ...); |
| void printflike2 server_msg_command_print(struct cmd_ctx *, const char *, ...); |
| void printflike2 server_msg_command_info(struct cmd_ctx *, const char *, ...); |
| |
| int |
| server_msg_dispatch(struct client *c) |
| { |
| struct imsg imsg; |
| struct msg_command_data commanddata; |
| struct msg_identify_data identifydata; |
| struct msg_resize_data resizedata; |
| struct msg_unlock_data unlockdata; |
| struct msg_environ_data environdata; |
| ssize_t n, datalen; |
| |
| if ((n = imsg_read(&c->ibuf)) == -1 || n == 0) |
| return (-1); |
| |
| for (;;) { |
| if ((n = imsg_get(&c->ibuf, &imsg)) == -1) |
| return (-1); |
| if (n == 0) |
| return (0); |
| datalen = imsg.hdr.len - IMSG_HEADER_SIZE; |
| |
| if (imsg.hdr.peerid != PROTOCOL_VERSION) { |
| server_write_client(c, MSG_VERSION, NULL, 0); |
| c->flags |= CLIENT_BAD; |
| imsg_free(&imsg); |
| continue; |
| } |
| |
| log_debug("got %d from client %d", imsg.hdr.type, c->ibuf.fd); |
| switch (imsg.hdr.type) { |
| case MSG_COMMAND: |
| if (datalen != sizeof commanddata) |
| fatalx("bad MSG_COMMAND size"); |
| memcpy(&commanddata, imsg.data, sizeof commanddata); |
| |
| server_msg_command(c, &commanddata); |
| break; |
| case MSG_IDENTIFY: |
| if (datalen != sizeof identifydata) |
| fatalx("bad MSG_IDENTIFY size"); |
| memcpy(&identifydata, imsg.data, sizeof identifydata); |
| |
| server_msg_identify(c, &identifydata, imsg.fd); |
| break; |
| case MSG_RESIZE: |
| if (datalen != sizeof resizedata) |
| fatalx("bad MSG_RESIZE size"); |
| memcpy(&resizedata, imsg.data, sizeof resizedata); |
| |
| server_msg_resize(c, &resizedata); |
| break; |
| case MSG_EXITING: |
| if (datalen != 0) |
| fatalx("bad MSG_EXITING size"); |
| |
| c->session = NULL; |
| tty_close(&c->tty); |
| server_write_client(c, MSG_EXITED, NULL, 0); |
| break; |
| case MSG_UNLOCK: |
| if (datalen != sizeof unlockdata) |
| fatalx("bad MSG_UNLOCK size"); |
| memcpy(&unlockdata, imsg.data, sizeof unlockdata); |
| |
| unlockdata.pass[(sizeof unlockdata.pass) - 1] = '\0'; |
| if (server_unlock(unlockdata.pass) != 0) |
| server_write_error(c, "bad password"); |
| memset(&unlockdata, 0, sizeof unlockdata); |
| server_write_client(c, MSG_EXIT, NULL, 0); |
| break; |
| case MSG_WAKEUP: |
| if (datalen != 0) |
| fatalx("bad MSG_WAKEUP size"); |
| |
| c->flags &= ~CLIENT_SUSPENDED; |
| tty_start_tty(&c->tty); |
| server_redraw_client(c); |
| break; |
| case MSG_ENVIRON: |
| if (datalen != sizeof environdata) |
| fatalx("bad MSG_ENVIRON size"); |
| memcpy(&environdata, imsg.data, sizeof environdata); |
| |
| environdata.var[(sizeof environdata.var) - 1] = '\0'; |
| if (strchr(environdata.var, '=') != NULL) |
| environ_put(&c->environ, environdata.var); |
| break; |
| default: |
| fatalx("unexpected message"); |
| } |
| |
| imsg_free(&imsg); |
| } |
| } |
| |
| void printflike2 |
| server_msg_command_error(struct cmd_ctx *ctx, const char *fmt, ...) |
| { |
| struct msg_print_data data; |
| va_list ap; |
| |
| va_start(ap, fmt); |
| xvsnprintf(data.msg, sizeof data.msg, fmt, ap); |
| va_end(ap); |
| |
| server_write_client(ctx->cmdclient, MSG_ERROR, &data, sizeof data); |
| } |
| |
| void printflike2 |
| server_msg_command_print(struct cmd_ctx *ctx, const char *fmt, ...) |
| { |
| struct msg_print_data data; |
| va_list ap; |
| |
| va_start(ap, fmt); |
| xvsnprintf(data.msg, sizeof data.msg, fmt, ap); |
| va_end(ap); |
| |
| server_write_client(ctx->cmdclient, MSG_PRINT, &data, sizeof data); |
| } |
| |
| void printflike2 |
| server_msg_command_info(struct cmd_ctx *ctx, const char *fmt, ...) |
| { |
| struct msg_print_data data; |
| va_list ap; |
| |
| if (be_quiet) |
| return; |
| |
| va_start(ap, fmt); |
| xvsnprintf(data.msg, sizeof data.msg, fmt, ap); |
| va_end(ap); |
| |
| server_write_client(ctx->cmdclient, MSG_PRINT, &data, sizeof data); |
| } |
| |
| void |
| server_msg_command(struct client *c, struct msg_command_data *data) |
| { |
| struct cmd_ctx ctx; |
| struct cmd_list *cmdlist = NULL; |
| struct cmd *cmd; |
| int argc; |
| char **argv, *cause; |
| |
| server_activity = time(NULL); |
| |
| ctx.error = server_msg_command_error; |
| ctx.print = server_msg_command_print; |
| ctx.info = server_msg_command_info; |
| |
| ctx.msgdata = data; |
| ctx.curclient = NULL; |
| ctx.cursession = NULL; |
| |
| ctx.cmdclient = c; |
| |
| argc = data->argc; |
| data->argv[(sizeof data->argv) - 1] = '\0'; |
| if (cmd_unpack_argv(data->argv, sizeof data->argv, argc, &argv) != 0) { |
| server_msg_command_error(&ctx, "command too long"); |
| goto error; |
| } |
| |
| if (argc == 0) { |
| argc = 1; |
| argv = xcalloc(1, sizeof *argv); |
| *argv = xstrdup("new-session"); |
| } |
| |
| if ((cmdlist = cmd_list_parse(argc, argv, &cause)) == NULL) { |
| server_msg_command_error(&ctx, "%s", cause); |
| cmd_free_argv(argc, argv); |
| goto error; |
| } |
| cmd_free_argv(argc, argv); |
| |
| if (data->pid != -1) { |
| TAILQ_FOREACH(cmd, cmdlist, qentry) { |
| if (cmd->entry->flags & CMD_CANTNEST) { |
| server_msg_command_error(&ctx, |
| "sessions should be nested with care. " |
| "unset $TMUX to force"); |
| goto error; |
| } |
| } |
| } |
| |
| if (cmd_list_exec(cmdlist, &ctx) != 1) |
| server_write_client(c, MSG_EXIT, NULL, 0); |
| cmd_list_free(cmdlist); |
| return; |
| |
| error: |
| if (cmdlist != NULL) |
| cmd_list_free(cmdlist); |
| server_write_client(c, MSG_EXIT, NULL, 0); |
| } |
| |
| void |
| server_msg_identify(struct client *c, struct msg_identify_data *data, int fd) |
| { |
| c->tty.sx = data->sx; |
| c->tty.sy = data->sy; |
| |
| c->cwd = NULL; |
| data->cwd[(sizeof data->cwd) - 1] = '\0'; |
| if (*data->cwd != '\0') |
| c->cwd = xstrdup(data->cwd); |
| |
| data->tty[(sizeof data->tty) - 1] = '\0'; |
| data->term[(sizeof data->term) - 1] = '\0'; |
| tty_init(&c->tty, fd, data->tty, data->term); |
| if (data->flags & IDENTIFY_UTF8) |
| c->tty.flags |= TTY_UTF8; |
| if (data->flags & IDENTIFY_256COLOURS) |
| c->tty.term_flags |= TERM_256COLOURS; |
| else if (data->flags & IDENTIFY_88COLOURS) |
| c->tty.term_flags |= TERM_88COLOURS; |
| if (data->flags & IDENTIFY_HASDEFAULTS) |
| c->tty.term_flags |= TERM_HASDEFAULTS; |
| |
| c->flags |= CLIENT_TERMINAL; |
| } |
| |
| void |
| server_msg_resize(struct client *c, struct msg_resize_data *data) |
| { |
| c->tty.sx = data->sx; |
| if (c->tty.sx == 0) |
| c->tty.sx = 80; |
| c->tty.sy = data->sy; |
| if (c->tty.sy == 0) |
| c->tty.sy = 25; |
| |
| c->tty.cx = UINT_MAX; |
| c->tty.cy = UINT_MAX; |
| c->tty.rupper = UINT_MAX; |
| c->tty.rlower = UINT_MAX; |
| |
| recalculate_sizes(); |
| |
| /* Always redraw this client. */ |
| server_redraw_client(c); |
| } |