1
0
mirror of https://github.com/tmux/tmux.git synced 2025-03-25 07:18:48 +00:00

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

This commit is contained in:
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 .SUFFIXES: .c .o .y .h
.PHONY: clean .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 \ 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 \ 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 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> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@ -24,25 +24,25 @@
#include "tmux.h" #include "tmux.h"
void server_msg_fn_attach(struct client *, struct hdr *); int server_msg_fn_attach(struct hdr *, struct client *);
void server_msg_fn_create(struct client *, struct hdr *); int server_msg_fn_create(struct hdr *, struct client *);
void server_msg_fn_input(struct client *, struct hdr *); int server_msg_fn_input(struct hdr *, struct client *);
void server_msg_fn_last(struct client *, struct hdr *); int server_msg_fn_last(struct hdr *, struct client *);
void server_msg_fn_new(struct client *, struct hdr *); int server_msg_fn_new(struct hdr *, struct client *);
void server_msg_fn_next(struct client *, struct hdr *); int server_msg_fn_next(struct hdr *, struct client *);
void server_msg_fn_previous(struct client *, struct hdr *); int server_msg_fn_previous(struct hdr *, struct client *);
void server_msg_fn_refresh(struct client *, struct hdr *); int server_msg_fn_refresh(struct hdr *, struct client *);
void server_msg_fn_rename(struct client *, struct hdr *); int server_msg_fn_rename(struct hdr *, struct client *);
void server_msg_fn_select(struct client *, struct hdr *); int server_msg_fn_select(struct hdr *, struct client *);
void server_msg_fn_sessions(struct client *, struct hdr *); int server_msg_fn_sessions(struct hdr *, struct client *);
void server_msg_fn_size(struct client *, struct hdr *); int server_msg_fn_size(struct hdr *, struct client *);
void server_msg_fn_windowlist(struct client *, struct hdr *); int server_msg_fn_windowlist(struct hdr *, struct client *);
void server_msg_fn_windows(struct client *, struct hdr *); int server_msg_fn_windows(struct hdr *, struct client *);
struct server_msg { struct server_msg {
enum hdrtype type; enum hdrtype type;
void (*fn)(struct client *, struct hdr *); int (*fn)(struct hdr *, struct client *);
}; };
struct server_msg server_msg_table[] = { struct server_msg server_msg_table[] = {
{ MSG_ATTACH, server_msg_fn_attach }, { 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]) #define NSERVERMSG (sizeof server_msg_table / sizeof server_msg_table[0])
void int
server_msg_dispatch(struct client *c) server_msg_dispatch(struct client *c)
{ {
struct hdr hdr; struct hdr hdr;
struct server_msg *msg; struct server_msg *msg;
u_int i; u_int i;
int n;
if (BUFFER_USED(c->in) < sizeof hdr) for (;;) {
return; if (BUFFER_USED(c->in) < sizeof hdr)
memcpy(&hdr, BUFFER_OUT(c->in), sizeof hdr); return (0);
if (BUFFER_USED(c->in) < (sizeof hdr) + hdr.size) memcpy(&hdr, BUFFER_OUT(c->in), sizeof hdr);
return; if (BUFFER_USED(c->in) < (sizeof hdr) + hdr.size)
buffer_remove(c->in, sizeof hdr); return (0);
buffer_remove(c->in, sizeof hdr);
for (i = 0; i < NSERVERMSG; i++) {
msg = server_msg_table + i; for (i = 0; i < NSERVERMSG; i++) {
if (msg->type == hdr.type) { msg = server_msg_table + i;
msg->fn(c, &hdr); if (msg->type == hdr.type) {
return; if ((n = msg->fn(&hdr, c)) != 0)
} return (n);
break;
}
}
if (i == NSERVERMSG)
fatalx("unexpected message");
} }
fatalx("unexpected message");
} }
/* New message from client. */ /* New message from client. */
void int
server_msg_fn_new(struct client *c, struct hdr *hdr) server_msg_fn_new(struct hdr *hdr, struct client *c)
{ {
struct new_data data; struct new_data data;
const char *shell; const char *shell;
char *cmd, *msg; char *cmd, *msg;
if (c->session != NULL) if (c->session != NULL)
return; return (0);
if (hdr->size != sizeof data) if (hdr->size != sizeof data)
fatalx("bad MSG_NEW size"); fatalx("bad MSG_NEW size");
buffer_read(c->in, &data, hdr->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) { if (*data.name != '\0' && session_find(data.name) != NULL) {
xasprintf(&msg, "duplicate session: %s", data.name); 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); xfree(msg);
return; return (0);
} }
shell = getenv("SHELL"); shell = getenv("SHELL");
@ -124,19 +128,20 @@ server_msg_fn_new(struct client *c, struct hdr *hdr)
fatalx("session_create failed"); fatalx("session_create failed");
xfree(cmd); xfree(cmd);
write_client(c, MSG_READY, NULL, 0);
draw_client(c, 0, c->sy - 1); draw_client(c, 0, c->sy - 1);
return (0);
} }
/* Attach message from client. */ /* Attach message from client. */
void int
server_msg_fn_attach(struct client *c, struct hdr *hdr) server_msg_fn_attach(struct hdr *hdr, struct client *c)
{ {
struct attach_data data; struct attach_data data;
char *msg; char *msg;
if (c->session != NULL) if (c->session != NULL)
return; return (0);
if (hdr->size != sizeof data) if (hdr->size != sizeof data)
fatalx("bad MSG_ATTACH size"); fatalx("bad MSG_ATTACH size");
buffer_read(c->in, &data, hdr->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); c->session = session_find(data.name);
if (c->session == NULL) { if (c->session == NULL) {
xasprintf(&msg, "session not found: %s", data.name); 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); xfree(msg);
return; return (0);
} }
write_client(c, MSG_READY, NULL, 0);
draw_client(c, 0, c->sy - 1); draw_client(c, 0, c->sy - 1);
return (0);
} }
/* Create message from client. */ /* Create message from client. */
void int
server_msg_fn_create(struct client *c, struct hdr *hdr) server_msg_fn_create(struct hdr *hdr, struct client *c)
{ {
const char *shell; const char *shell;
char *cmd; char *cmd;
if (c->session == NULL) if (c->session == NULL)
return; return (0);
if (hdr->size != 0) if (hdr->size != 0)
fatalx("bad MSG_CREATE size"); fatalx("bad MSG_CREATE size");
@ -182,14 +188,16 @@ server_msg_fn_create(struct client *c, struct hdr *hdr)
xfree(cmd); xfree(cmd);
draw_client(c, 0, c->sy - 1); draw_client(c, 0, c->sy - 1);
return (0);
} }
/* Next message from client. */ /* Next message from client. */
void int
server_msg_fn_next(struct client *c, struct hdr *hdr) server_msg_fn_next(struct hdr *hdr, struct client *c)
{ {
if (c->session == NULL) if (c->session == NULL)
return; return (0);
if (hdr->size != 0) if (hdr->size != 0)
fatalx("bad MSG_NEXT size"); fatalx("bad MSG_NEXT size");
@ -197,14 +205,16 @@ server_msg_fn_next(struct client *c, struct hdr *hdr)
changed_window(c); changed_window(c);
else else
write_message(c, "No next window"); write_message(c, "No next window");
return (0);
} }
/* Previous message from client. */ /* Previous message from client. */
void int
server_msg_fn_previous(struct client *c, struct hdr *hdr) server_msg_fn_previous(struct hdr *hdr, struct client *c)
{ {
if (c->session == NULL) if (c->session == NULL)
return; return (0);
if (hdr->size != 0) if (hdr->size != 0)
fatalx("bad MSG_PREVIOUS size"); fatalx("bad MSG_PREVIOUS size");
@ -212,16 +222,18 @@ server_msg_fn_previous(struct client *c, struct hdr *hdr)
changed_window(c); changed_window(c);
else else
write_message(c, "No previous window"); write_message(c, "No previous window");
return (0);
} }
/* Size message from client. */ /* Size message from client. */
void int
server_msg_fn_size(struct client *c, struct hdr *hdr) server_msg_fn_size(struct hdr *hdr, struct client *c)
{ {
struct size_data data; struct size_data data;
if (c->session == NULL) if (c->session == NULL)
return; return (0);
if (hdr->size != sizeof data) if (hdr->size != sizeof data)
fatalx("bad MSG_SIZE size"); fatalx("bad MSG_SIZE size");
buffer_read(c->in, &data, hdr->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) if (window_resize(c->session->window, c->sx, c->sy) != 0)
draw_client(c, 0, c->sy - 1); draw_client(c, 0, c->sy - 1);
return (0);
} }
/* Input message from client. */ /* Input message from client. */
void int
server_msg_fn_input(struct client *c, struct hdr *hdr) server_msg_fn_input(struct hdr *hdr, struct client *c)
{ {
if (c->session == NULL) if (c->session == NULL)
return; return (0);
window_input(c->session->window, c->in, hdr->size); window_input(c->session->window, c->in, hdr->size);
return (0);
} }
/* Refresh message from client. */ /* Refresh message from client. */
void int
server_msg_fn_refresh(struct client *c, struct hdr *hdr) server_msg_fn_refresh(struct hdr *hdr, struct client *c)
{ {
struct refresh_data data; struct refresh_data data;
if (c->session == NULL) if (c->session == NULL)
return; return (0);
if (hdr->size != 0 && hdr->size != sizeof data) if (hdr->size != 0 && hdr->size != sizeof data)
fatalx("bad MSG_REFRESH size"); fatalx("bad MSG_REFRESH size");
draw_client(c, 0, c->sy - 1); draw_client(c, 0, c->sy - 1);
return (0);
} }
/* Select message from client. */ /* Select message from client. */
void int
server_msg_fn_select(struct client *c, struct hdr *hdr) server_msg_fn_select(struct hdr *hdr, struct client *c)
{ {
struct select_data data; struct select_data data;
if (c->session == NULL) if (c->session == NULL)
return; return (0);
if (hdr->size != sizeof data) if (hdr->size != sizeof data)
fatalx("bad MSG_SELECT size"); fatalx("bad MSG_SELECT size");
buffer_read(c->in, &data, hdr->size); buffer_read(c->in, &data, hdr->size);
if (c->session == NULL) if (c->session == NULL)
return; return (0);
if (session_select(c->session, data.idx) == 0) if (session_select(c->session, data.idx) == 0)
changed_window(c); changed_window(c);
else else
write_message(c, "Window %u not present", data.idx); write_message(c, "Window %u not present", data.idx);
return (0);
} }
/* Sessions message from client. */ /* Sessions message from client. */
void int
server_msg_fn_sessions(struct client *c, struct hdr *hdr) server_msg_fn_sessions(struct hdr *hdr, struct client *c)
{ {
struct sessions_data data; struct sessions_data data;
struct sessions_entry entry; 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); buffer_write(c->out, &entry, sizeof entry);
} }
return (0);
} }
/* Windows message from client. */ /* Windows message from client. */
void int
server_msg_fn_windows(struct client *c, struct hdr *hdr) server_msg_fn_windows(struct hdr *hdr, struct client *c)
{ {
struct windows_data data; struct windows_data data;
struct windows_entry entry; struct windows_entry entry;
@ -335,7 +357,7 @@ server_msg_fn_windows(struct client *c, struct hdr *hdr)
if (s == NULL) { if (s == NULL) {
data.windows = 0; data.windows = 0;
write_client(c, MSG_WINDOWS, &data, sizeof data); write_client(c, MSG_WINDOWS, &data, sizeof data);
return; return (0);
} }
data.windows = 0; data.windows = 0;
@ -357,14 +379,16 @@ server_msg_fn_windows(struct client *c, struct hdr *hdr)
*entry.tty = '\0'; *entry.tty = '\0';
buffer_write(c->out, &entry, sizeof entry); buffer_write(c->out, &entry, sizeof entry);
} }
return (0);
} }
/* Rename message from client. */ /* Rename message from client. */
void int
server_msg_fn_rename(struct client *c, struct hdr *hdr) server_msg_fn_rename(struct hdr *hdr, struct client *c)
{ {
if (c->session == NULL) if (c->session == NULL)
return; return (0);
if (hdr->size != 0) if (hdr->size != 0)
fatalx("bad MSG_RENAME size"); fatalx("bad MSG_RENAME size");
@ -372,11 +396,11 @@ server_msg_fn_rename(struct client *c, struct hdr *hdr)
} }
/* Last window message from client */ /* Last window message from client */
void int
server_msg_fn_last(struct client *c, struct hdr *hdr) server_msg_fn_last(struct hdr *hdr, struct client *c)
{ {
if (c->session == NULL) if (c->session == NULL)
return; return (0);
if (hdr->size != 0) if (hdr->size != 0)
fatalx("bad MSG_LAST size"); fatalx("bad MSG_LAST size");
@ -384,11 +408,13 @@ server_msg_fn_last(struct client *c, struct hdr *hdr)
changed_window(c); changed_window(c);
else else
write_message(c, "No last window"); write_message(c, "No last window");
return (0);
} }
/* Window list message from client */ /* Window list message from client */
void int
server_msg_fn_windowlist(struct client *c, struct hdr *hdr) server_msg_fn_windowlist(struct hdr *hdr, struct client *c)
{ {
struct window *w; struct window *w;
char *buf; char *buf;
@ -396,7 +422,7 @@ server_msg_fn_windowlist(struct client *c, struct hdr *hdr)
u_int i; u_int i;
if (c->session == NULL) if (c->session == NULL)
return; return (0);
if (hdr->size != 0) if (hdr->size != 0)
fatalx("bad MSG_WINDOWLIST size"); fatalx("bad MSG_WINDOWLIST size");
@ -417,4 +443,6 @@ server_msg_fn_windowlist(struct client *c, struct hdr *hdr)
write_message(c, "%s", buf); write_message(c, "%s", buf);
xfree(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> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@ -24,6 +24,7 @@
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <paths.h>
#include <poll.h> #include <poll.h>
#include <signal.h> #include <signal.h>
#include <stdio.h> #include <stdio.h>
@ -43,7 +44,7 @@
/* Client list. */ /* Client list. */
struct clients clients; struct clients clients;
int server_main(int); int server_main(char *, int);
void fill_windows(struct pollfd **); void fill_windows(struct pollfd **);
void handle_windows(struct pollfd **); void handle_windows(struct pollfd **);
void fill_clients(struct pollfd **); void fill_clients(struct pollfd **);
@ -52,21 +53,18 @@ struct client *accept_client(int);
void lost_client(struct client *); void lost_client(struct client *);
void lost_window(struct window *); void lost_window(struct window *);
/* Fork and start server process. */
int int
server_start(void) server_start(char *path)
{ {
mode_t mode;
int fd;
struct sockaddr_un sa; struct sockaddr_un sa;
size_t sz; size_t sz;
pid_t pid; pid_t pid;
FILE *f; mode_t mode;
char *path; int fd;
/* Fork the server process. */
switch (pid = fork()) { switch (pid = fork()) {
case -1: case -1:
log_warn("fork");
return (-1); return (-1);
case 0: case 0:
break; break;
@ -74,20 +72,15 @@ server_start(void)
return (0); return (0);
} }
/* Start logging to file. */ logfile("server");
if (debug_level > 0) { setproctitle("server (%s)", path);
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()); log_debug("server started, pid %ld", (long) getpid());
/* Create the socket. */ /* Create the socket. */
memset(&sa, 0, sizeof sa); memset(&sa, 0, sizeof sa);
sa.sun_family = AF_UNIX; 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) { if (sz >= sizeof sa.sun_path) {
errno = ENAMETOOLONG; errno = ENAMETOOLONG;
fatal("socket failed"); fatal("socket failed");
@ -113,32 +106,17 @@ server_start(void)
fatal("daemon failed"); fatal("daemon failed");
log_debug("server daemonised, pid now %ld", (long) getpid()); log_debug("server daemonised, pid now %ld", (long) getpid());
setproctitle("server (%s)", socket_path); exit(server_main(path, fd));
exit(server_main(fd));
} }
/* Main server loop. */ /* Main server loop. */
int int
server_main(int srv_fd) server_main(char *srv_path, int srv_fd)
{ {
struct pollfd *pfds, *pfd; struct pollfd *pfds, *pfd;
int nfds, mode; int nfds, mode;
struct sigaction act;
sigemptyset(&act.sa_mask); siginit();
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");
ARRAY_INIT(&windows); ARRAY_INIT(&windows);
ARRAY_INIT(&clients); ARRAY_INIT(&clients);
@ -192,7 +170,7 @@ server_main(int srv_fd)
} }
close(srv_fd); close(srv_fd);
unlink(socket_path); unlink(srv_path);
return (0); 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> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@ -17,17 +17,9 @@
*/ */
#include <sys/types.h> #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 <sys/wait.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <paths.h> #include <paths.h>
#include <getopt.h>
#include <signal.h> #include <signal.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -40,44 +32,46 @@
const char *malloc_options = "AFGJPX"; const char *malloc_options = "AFGJPX";
#endif #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; volatile sig_atomic_t sigwinch;
/* SIGTERM received flag. */
volatile sig_atomic_t sigterm; volatile sig_atomic_t sigterm;
/* Debug output level. */
int debug_level; int debug_level;
/* Path to server socket. */ void sighandler(int);
char socket_path[MAXPATHLEN];
/* Server socket and buffers. */ struct op {
int server_fd = -1; const char *cmd;
struct buffer *server_in; int (*fn)(char *, int, char **);
struct buffer *server_out; };
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
int local_fd = -1; usage(const char *s)
struct buffer *local_in;
struct buffer *local_out;
__dead void
usage(void)
{ {
fprintf(stderr, if (s == NULL)
"usage: %s [-v] [-n name] [-s path] command\n", __progname); s = "command ...";
exit(1); 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 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 int
main(int argc, char **argv) main(int argc, char **argv)
{ {
int opt, mode, n; struct op *op;
char *path, *error, name[MAXNAMELEN]; char *path;
FILE *f; int opt;
enum op op; u_int i;
struct pollfd pfds[2];
struct hdr hdr;
struct size_data sd;
struct winsize ws;
struct sigaction act;
struct stat sb;
*name = '\0';
path = NULL; path = NULL;
while ((opt = getopt(argc, argv, "s:v?")) != EOF) {
while ((opt = getopt(argc, argv, "n:s:v?")) != EOF) {
switch (opt) { switch (opt) {
case 'n':
if (strlcpy(name, optarg, sizeof name) >= sizeof name)
errx(1, "name too long");
break;
case 's': case 's':
path = xstrdup(optarg); path = xstrdup(optarg);
break; break;
@ -127,449 +170,24 @@ main(int argc, char **argv)
break; break;
case '?': case '?':
default: default:
usage(); exit(usage(NULL));
} }
} }
argc -= optind; argc -= optind;
argv += optind; argv += optind;
if (argc != 1) if (argc == 0)
usage(); exit(usage(NULL));
/* Determine command. */ log_open(stderr, LOG_USER, debug_level);
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();
/* Sort out socket path. */ for (i = 0; i < NOP; i++) {
if (path == NULL) { op = op_table + i;
xasprintf(&path, if (strncmp(argv[0], op->cmd, strlen(op->cmd)) == 0) {
"%s/%s-%lu", _PATH_TMP, __progname, (u_long) getuid()); argc--;
} argv++;
if (realpath(path, socket_path) == NULL) exit(op->fn(path, argc, argv));
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");
} }
} }
return (0); exit(usage(NULL));
}
/* 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");
}
}
} }

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> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@ -257,18 +257,11 @@ struct buffer {
#define CODE_KKEYPADON 25 #define CODE_KKEYPADON 25
#define CODE_TITLE 26 #define CODE_TITLE 26
/* Command-line commands. */
enum op {
OP_LIST = 0,
OP_NEW,
OP_ATTACH
};
/* Message codes. */ /* Message codes. */
enum hdrtype { enum hdrtype {
MSG_NEW = 0, MSG_NEW = 0,
MSG_ATTACH, MSG_ATTACH,
MSG_READY, MSG_ERROR,
MSG_CREATE, MSG_CREATE,
MSG_EXIT, MSG_EXIT,
MSG_SIZE, MSG_SIZE,
@ -423,17 +416,50 @@ struct client {
}; };
ARRAY_DECL(clients, 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 */ /* tmux.c */
volatile sig_atomic_t sigterm; extern volatile sig_atomic_t sigwinch;
extern int debug_level; extern volatile sig_atomic_t sigterm;
extern char socket_path[MAXPATHLEN]; 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 */ /* server.c */
extern struct clients clients; extern struct clients clients;
int server_start(void); int server_start(char *);
/* server-msg.c */ /* server-msg.c */
void server_msg_dispatch(struct client *); int server_msg_dispatch(struct client *);
/* server-fn.c */ /* server-fn.c */
void write_message(struct client *, const char *, ...); void write_message(struct client *, const char *, ...);
@ -468,10 +494,6 @@ void local_done(void);
int local_key(size_t *); int local_key(size_t *);
void local_output(struct buffer *, size_t); void local_output(struct buffer *, size_t);
/* command.c */
extern int cmd_prefix;
int cmd_execute(int, struct buffer *);
/* window.c */ /* window.c */
extern struct windows windows; extern struct windows windows;
struct window *window_create(const char *, u_int, u_int); struct window *window_create(const char *, u_int, u_int);