diff options
Diffstat (limited to 'usr.bin/tmux/proc.c')
-rw-r--r-- | usr.bin/tmux/proc.c | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/usr.bin/tmux/proc.c b/usr.bin/tmux/proc.c new file mode 100644 index 00000000000..226c58d13bd --- /dev/null +++ b/usr.bin/tmux/proc.c @@ -0,0 +1,249 @@ +/* $OpenBSD: proc.c,v 1.1 2015/10/27 13:23:24 nicm Exp $ */ + +/* + * Copyright (c) 2015 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/queue.h> +#include <sys/uio.h> + +#include <errno.h> +#include <event.h> +#include <imsg.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "tmux.h" + +struct tmuxproc { + const char *name; + int exit; + + void (*signalcb)(int); +}; + +struct tmuxpeer { + struct tmuxproc *parent; + + struct imsgbuf ibuf; + struct event event; + + int flags; +#define PEER_BAD 0x1 + + void (*dispatchcb)(struct imsg *, void *); + void *arg; +}; + +static void proc_update_event(struct tmuxpeer *); + +static void +proc_event_cb(unused int fd, short events, void *arg) +{ + struct tmuxpeer *peer = arg; + ssize_t n; + struct imsg imsg; + int v; + + if (!(peer->flags & PEER_BAD) && (events & EV_READ)) { + if ((n = imsg_read(&peer->ibuf)) == -1 || n == 0) { + peer->dispatchcb(NULL, peer->arg); + return; + } + for (;;) { + if ((n = imsg_get(&peer->ibuf, &imsg)) == -1) { + peer->dispatchcb(NULL, peer->arg); + return; + } + if (n == 0) + break; + log_debug("peer %p message %d", peer, imsg.hdr.type); + + v = imsg.hdr.peerid; + if (imsg.hdr.type != MSG_VERSION && + v != PROTOCOL_VERSION) { + log_debug("peer %p bad version %d", peer, v); + + proc_send(peer, MSG_VERSION, -1, NULL, 0); + peer->flags |= PEER_BAD; + + if (imsg.fd != -1) + close(imsg.fd); + imsg_free(&imsg); + break; + } + + peer->dispatchcb(&imsg, peer->arg); + imsg_free(&imsg); + } + } + + if (events & EV_WRITE) { + if (msgbuf_write(&peer->ibuf.w) <= 0 && errno != EAGAIN) { + peer->dispatchcb(NULL, peer->arg); + return; + } + } + + if ((peer->flags & PEER_BAD) && peer->ibuf.w.queued == 0) { + peer->dispatchcb(NULL, peer->arg); + return; + } + + proc_update_event(peer); +} + +static void +proc_signal_cb(int signo, unused short events, void *arg) +{ + struct tmuxproc *tp = arg; + + tp->signalcb(signo); +} + +static void +proc_update_event(struct tmuxpeer *peer) +{ + short events; + + event_del(&peer->event); + + events = EV_READ; + if (peer->ibuf.w.queued > 0) + events |= EV_WRITE; + event_set(&peer->event, peer->ibuf.fd, events, proc_event_cb, peer); + + event_add(&peer->event, NULL); +} + +int +proc_send(struct tmuxpeer *peer, enum msgtype type, int fd, const void *buf, + size_t len) +{ + struct imsgbuf *ibuf = &peer->ibuf; + void *vp = (void *)buf; + int retval; + + if (peer->flags & PEER_BAD) + return (-1); + log_debug("sending message %d to peer %p (%zu bytes)", type, peer, len); + + retval = imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, fd, vp, len); + if (retval != 1) + return (-1); + proc_update_event(peer); + return (0); +} + +int +proc_send_s(struct tmuxpeer *peer, enum msgtype type, const char *s) +{ + return (proc_send(peer, type, -1, s, strlen(s) + 1)); +} + +struct tmuxproc * +proc_start(const char *name, struct event_base *base, int forkflag, + void (*signalcb)(int)) +{ + struct tmuxproc *tp; + + if (forkflag) { + switch (fork()) { + case -1: + fatal("fork failed"); + case 0: + break; + default: + return (NULL); + } + if (daemon(1, 0) != 0) + fatal("daemon failed"); + + clear_signals(0); + if (event_reinit(base) != 0) + fatalx("event_reinit failed"); + } + + logfile(name); + setproctitle("%s (%s)", name, socket_path); + + log_debug("%s started (%ld): socket %s, protocol %d", name, + (long)getpid(), socket_path, PROTOCOL_VERSION); + + tp = xcalloc(1, sizeof *tp); + tp->name = xstrdup(name); + + tp->signalcb = signalcb; + set_signals(proc_signal_cb, tp); + + return (tp); +} + +void +proc_loop(struct tmuxproc *tp, int (*loopcb)(void)) +{ + log_debug("%s loop enter", tp->name); + do + event_loop(EVLOOP_ONCE); + while (!tp->exit && (loopcb == NULL || !loopcb ())); + log_debug("%s loop exit", tp->name); +} + +void +proc_exit(struct tmuxproc *tp) +{ + tp->exit = 1; +} + +struct tmuxpeer * +proc_add_peer(struct tmuxproc *tp, int fd, + void (*dispatchcb)(struct imsg *, void *), void *arg) +{ + struct tmuxpeer *peer; + + peer = xcalloc(1, sizeof *peer); + peer->parent = tp; + + peer->dispatchcb = dispatchcb; + peer->arg = arg; + + imsg_init(&peer->ibuf, fd); + event_set(&peer->event, fd, EV_READ, proc_event_cb, peer); + + log_debug("add peer %p: %d (%p)", peer, fd, arg); + + proc_update_event(peer); + return (peer); +} + +void +proc_remove_peer(struct tmuxpeer *peer) +{ + log_debug("remove peer %p", peer); + + event_del(&peer->event); + imsg_clear(&peer->ibuf); + + close(peer->ibuf.fd); + free(peer); +} + +void +proc_kill_peer(struct tmuxpeer *peer) +{ + peer->flags |= PEER_BAD; +} |