mirror of
				https://github.com/tmux/tmux.git
				synced 2025-11-04 00:56:10 +00:00 
			
		
		
		
	Cleanup part II: split up client stuff and ops. More to come.
This commit is contained in:
		
							
								
								
									
										251
									
								
								client.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								client.c
									
									
									
									
									
										Normal 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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user