Cleanup part II: split up client stuff and ops. More to come.

pull/1/head
Nicholas Marriott 2007-09-26 13:43:15 +00:00
parent 671694ac30
commit fb39b22a2e
11 changed files with 924 additions and 743 deletions

View File

@ -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

108
client-cmd.c Normal file
View File

@ -0,0 +1,108 @@
/* $Id: client-cmd.c,v 1.1 2007-09-26 13:43:14 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 "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);
}

99
client-msg.c Normal file
View File

@ -0,0 +1,99 @@
/* $Id: client-msg.c,v 1.1 2007-09-26 13:43:14 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 <stdlib.h>
#include <string.h>
#include <unistd.h>
#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);
}

251
client.c Normal file
View File

@ -0,0 +1,251 @@
/* $Id: client.c,v 1.1 2007-09-26 13:43:15 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 <paths.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#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);
}

113
command.c
View File

@ -1,113 +0,0 @@
/* $Id: command.c,v 1.7 2007-09-22 11:50:33 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 "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);
}

126
op-list.c Normal file
View File

@ -0,0 +1,126 @@
/* $Id: op-list.c,v 1.1 2007-09-26 13:43:15 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.
*/
/* 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");
}
}
}

64
op.c Normal file
View File

@ -0,0 +1,64 @@
/* $Id: op.c,v 1.1 2007-09-26 13:43:15 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 <string.h>
#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));
}

View File

@ -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 <nicm@users.sourceforge.net>
@ -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);
}

View File

@ -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 <nicm@users.sourceforge.net>
@ -24,6 +24,7 @@
#include <errno.h>
#include <fcntl.h>
#include <paths.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
@ -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);
}

604
tmux.c
View File

@ -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 <nicm@users.sourceforge.net>
@ -17,17 +17,9 @@
*/
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <paths.h>
#include <getopt.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
@ -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));
}

58
tmux.h
View File

@ -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 <nicm@users.sourceforge.net>
@ -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);