Home | History | Annotate | Line # | Download | only in dist
      1 /* $OpenBSD$ */
      2 
      3 /*
      4  * Copyright (c) 2015 Nicholas Marriott <nicholas.marriott (at) gmail.com>
      5  *
      6  * Permission to use, copy, modify, and distribute this software for any
      7  * purpose with or without fee is hereby granted, provided that the above
      8  * copyright notice and this permission notice appear in all copies.
      9  *
     10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
     15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
     16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     17  */
     18 
     19 #include <sys/types.h>
     20 #include <sys/socket.h>
     21 #include <sys/uio.h>
     22 #include <sys/utsname.h>
     23 
     24 #include <errno.h>
     25 #include <signal.h>
     26 #include <stdlib.h>
     27 #include <string.h>
     28 #include <unistd.h>
     29 
     30 #if defined(HAVE_NCURSES_H)
     31 #include <ncurses.h>
     32 #endif
     33 
     34 #include "tmux.h"
     35 
     36 struct tmuxproc {
     37 	const char	 *name;
     38 	int		  exit;
     39 
     40 	void		(*signalcb)(int);
     41 
     42 	struct event	  ev_sigint;
     43 	struct event	  ev_sighup;
     44 	struct event	  ev_sigchld;
     45 	struct event	  ev_sigcont;
     46 	struct event	  ev_sigterm;
     47 	struct event	  ev_sigusr1;
     48 	struct event	  ev_sigusr2;
     49 	struct event	  ev_sigwinch;
     50 
     51 	TAILQ_HEAD(, tmuxpeer) peers;
     52 };
     53 
     54 struct tmuxpeer {
     55 	struct tmuxproc	*parent;
     56 
     57 	struct imsgbuf	 ibuf;
     58 	struct event	 event;
     59 	uid_t		 uid;
     60 
     61 	int		 flags;
     62 #define PEER_BAD 0x1
     63 
     64 	void		(*dispatchcb)(struct imsg *, void *);
     65 	void		 *arg;
     66 
     67 	TAILQ_ENTRY(tmuxpeer) entry;
     68 };
     69 
     70 static int	peer_check_version(struct tmuxpeer *, struct imsg *);
     71 static void	proc_update_event(struct tmuxpeer *);
     72 
     73 static void
     74 proc_event_cb(__unused int fd, short events, void *arg)
     75 {
     76 	struct tmuxpeer	*peer = arg;
     77 	ssize_t		 n;
     78 	struct imsg	 imsg;
     79 
     80 	if (!(peer->flags & PEER_BAD) && (events & EV_READ)) {
     81 		if (imsgbuf_read(&peer->ibuf) != 1) {
     82 			peer->dispatchcb(NULL, peer->arg);
     83 			return;
     84 		}
     85 		for (;;) {
     86 			if ((n = imsg_get(&peer->ibuf, &imsg)) == -1) {
     87 				peer->dispatchcb(NULL, peer->arg);
     88 				return;
     89 			}
     90 			if (n == 0)
     91 				break;
     92 			log_debug("peer %p message %d", peer, imsg.hdr.type);
     93 
     94 			if (peer_check_version(peer, &imsg) != 0) {
     95 				imsg_free(&imsg);
     96 				break;
     97 			}
     98 
     99 			peer->dispatchcb(&imsg, peer->arg);
    100 			imsg_free(&imsg);
    101 		}
    102 	}
    103 
    104 	if (events & EV_WRITE) {
    105 		if (imsgbuf_write(&peer->ibuf) == -1) {
    106 			peer->dispatchcb(NULL, peer->arg);
    107 			return;
    108 		}
    109 	}
    110 
    111 	if ((peer->flags & PEER_BAD) && imsgbuf_queuelen(&peer->ibuf) == 0) {
    112 		peer->dispatchcb(NULL, peer->arg);
    113 		return;
    114 	}
    115 
    116 	proc_update_event(peer);
    117 }
    118 
    119 static void
    120 proc_signal_cb(int signo, __unused short events, void *arg)
    121 {
    122 	struct tmuxproc	*tp = arg;
    123 
    124 	tp->signalcb(signo);
    125 }
    126 
    127 static int
    128 peer_check_version(struct tmuxpeer *peer, struct imsg *imsg)
    129 {
    130 	int	version;
    131 
    132 	version = imsg->hdr.peerid & 0xff;
    133 	if (imsg->hdr.type != MSG_VERSION && version != PROTOCOL_VERSION) {
    134 		log_debug("peer %p bad version %d", peer, version);
    135 
    136 		proc_send(peer, MSG_VERSION, -1, NULL, 0);
    137 		peer->flags |= PEER_BAD;
    138 
    139 		return (-1);
    140 	}
    141 	return (0);
    142 }
    143 
    144 static void
    145 proc_update_event(struct tmuxpeer *peer)
    146 {
    147 	short	events;
    148 
    149 	event_del(&peer->event);
    150 
    151 	events = EV_READ;
    152 	if (imsgbuf_queuelen(&peer->ibuf) > 0)
    153 		events |= EV_WRITE;
    154 	event_set(&peer->event, peer->ibuf.fd, events, proc_event_cb, peer);
    155 
    156 	event_add(&peer->event, NULL);
    157 }
    158 
    159 int
    160 proc_send(struct tmuxpeer *peer, enum msgtype type, int fd, const void *buf,
    161     size_t len)
    162 {
    163 	struct imsgbuf	*ibuf = &peer->ibuf;
    164 	void		*vp = __UNCONST(buf);
    165 	int		 retval;
    166 
    167 	if (peer->flags & PEER_BAD)
    168 		return (-1);
    169 	log_debug("sending message %d to peer %p (%zu bytes)", type, peer, len);
    170 
    171 	retval = imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, fd, vp, len);
    172 	if (retval != 1)
    173 		return (-1);
    174 	proc_update_event(peer);
    175 	return (0);
    176 }
    177 
    178 struct tmuxproc *
    179 proc_start(const char *name)
    180 {
    181 	struct tmuxproc	*tp;
    182 	struct utsname	 u;
    183 
    184 	log_open(name);
    185 	setproctitle("%s (%s)", name, socket_path);
    186 
    187 	if (uname(&u) < 0)
    188 		memset(&u, 0, sizeof u);
    189 
    190 	log_debug("%s started (%ld): version %s, socket %s, protocol %d", name,
    191 	    (long)getpid(), getversion(), socket_path, PROTOCOL_VERSION);
    192 	log_debug("on %s %s %s", u.sysname, u.release, u.version);
    193 	log_debug("using libevent %s %s", event_get_version(), event_get_method());
    194 #ifdef HAVE_UTF8PROC
    195 	log_debug("using utf8proc %s", utf8proc_version());
    196 #endif
    197 #ifdef NCURSES_VERSION
    198 	log_debug("using ncurses %s %06u", NCURSES_VERSION, NCURSES_VERSION_PATCH);
    199 #endif
    200 
    201 	tp = xcalloc(1, sizeof *tp);
    202 	tp->name = xstrdup(name);
    203 	TAILQ_INIT(&tp->peers);
    204 
    205 	return (tp);
    206 }
    207 
    208 void
    209 proc_loop(struct tmuxproc *tp, int (*loopcb)(void))
    210 {
    211 	log_debug("%s loop enter", tp->name);
    212 	do
    213 		event_loop(EVLOOP_ONCE);
    214 	while (!tp->exit && (loopcb == NULL || !loopcb ()));
    215 	log_debug("%s loop exit", tp->name);
    216 }
    217 
    218 void
    219 proc_exit(struct tmuxproc *tp)
    220 {
    221 	struct tmuxpeer	*peer;
    222 
    223 	TAILQ_FOREACH(peer, &tp->peers, entry)
    224 	    imsgbuf_flush(&peer->ibuf);
    225 	tp->exit = 1;
    226 }
    227 
    228 void
    229 proc_set_signals(struct tmuxproc *tp, void (*signalcb)(int))
    230 {
    231 	struct sigaction	sa;
    232 
    233 	tp->signalcb = signalcb;
    234 
    235 	memset(&sa, 0, sizeof sa);
    236 	sigemptyset(&sa.sa_mask);
    237 	sa.sa_flags = SA_RESTART;
    238 	sa.sa_handler = SIG_IGN;
    239 
    240 	sigaction(SIGPIPE, &sa, NULL);
    241 	sigaction(SIGTSTP, &sa, NULL);
    242 	sigaction(SIGTTIN, &sa, NULL);
    243 	sigaction(SIGTTOU, &sa, NULL);
    244 	sigaction(SIGQUIT, &sa, NULL);
    245 
    246 	signal_set(&tp->ev_sigint, SIGINT, proc_signal_cb, tp);
    247 	signal_add(&tp->ev_sigint, NULL);
    248 	signal_set(&tp->ev_sighup, SIGHUP, proc_signal_cb, tp);
    249 	signal_add(&tp->ev_sighup, NULL);
    250 	signal_set(&tp->ev_sigchld, SIGCHLD, proc_signal_cb, tp);
    251 	signal_add(&tp->ev_sigchld, NULL);
    252 	signal_set(&tp->ev_sigcont, SIGCONT, proc_signal_cb, tp);
    253 	signal_add(&tp->ev_sigcont, NULL);
    254 	signal_set(&tp->ev_sigterm, SIGTERM, proc_signal_cb, tp);
    255 	signal_add(&tp->ev_sigterm, NULL);
    256 	signal_set(&tp->ev_sigusr1, SIGUSR1, proc_signal_cb, tp);
    257 	signal_add(&tp->ev_sigusr1, NULL);
    258 	signal_set(&tp->ev_sigusr2, SIGUSR2, proc_signal_cb, tp);
    259 	signal_add(&tp->ev_sigusr2, NULL);
    260 	signal_set(&tp->ev_sigwinch, SIGWINCH, proc_signal_cb, tp);
    261 	signal_add(&tp->ev_sigwinch, NULL);
    262 }
    263 
    264 void
    265 proc_clear_signals(struct tmuxproc *tp, int defaults)
    266 {
    267 	struct sigaction	sa;
    268 
    269 	memset(&sa, 0, sizeof sa);
    270 	sigemptyset(&sa.sa_mask);
    271 	sa.sa_flags = SA_RESTART;
    272 	sa.sa_handler = SIG_DFL;
    273 
    274 	sigaction(SIGPIPE, &sa, NULL);
    275 	sigaction(SIGTSTP, &sa, NULL);
    276 
    277 	signal_del(&tp->ev_sigint);
    278 	signal_del(&tp->ev_sighup);
    279 	signal_del(&tp->ev_sigchld);
    280 	signal_del(&tp->ev_sigcont);
    281 	signal_del(&tp->ev_sigterm);
    282 	signal_del(&tp->ev_sigusr1);
    283 	signal_del(&tp->ev_sigusr2);
    284 	signal_del(&tp->ev_sigwinch);
    285 
    286 	if (defaults) {
    287 		sigaction(SIGINT, &sa, NULL);
    288 		sigaction(SIGQUIT, &sa, NULL);
    289 		sigaction(SIGHUP, &sa, NULL);
    290 		sigaction(SIGCHLD, &sa, NULL);
    291 		sigaction(SIGCONT, &sa, NULL);
    292 		sigaction(SIGTERM, &sa, NULL);
    293 		sigaction(SIGUSR1, &sa, NULL);
    294 		sigaction(SIGUSR2, &sa, NULL);
    295 		sigaction(SIGWINCH, &sa, NULL);
    296 	}
    297 }
    298 
    299 struct tmuxpeer *
    300 proc_add_peer(struct tmuxproc *tp, int fd,
    301     void (*dispatchcb)(struct imsg *, void *), void *arg)
    302 {
    303 	struct tmuxpeer	*peer;
    304 	gid_t		 gid;
    305 
    306 	peer = xcalloc(1, sizeof *peer);
    307 	peer->parent = tp;
    308 
    309 	peer->dispatchcb = dispatchcb;
    310 	peer->arg = arg;
    311 
    312 	if (imsgbuf_init(&peer->ibuf, fd) == -1)
    313 		fatal("imsgbuf_init");
    314 	imsgbuf_allow_fdpass(&peer->ibuf);
    315 	event_set(&peer->event, fd, EV_READ, proc_event_cb, peer);
    316 
    317 	if (getpeereid(fd, &peer->uid, &gid) != 0)
    318 		peer->uid = (uid_t)-1;
    319 
    320 	log_debug("add peer %p: %d (%p)", peer, fd, arg);
    321 	TAILQ_INSERT_TAIL(&tp->peers, peer, entry);
    322 
    323 	proc_update_event(peer);
    324 	return (peer);
    325 }
    326 
    327 void
    328 proc_remove_peer(struct tmuxpeer *peer)
    329 {
    330 	TAILQ_REMOVE(&peer->parent->peers, peer, entry);
    331 	log_debug("remove peer %p", peer);
    332 
    333 	event_del(&peer->event);
    334 	imsgbuf_clear(&peer->ibuf);
    335 
    336 	close(peer->ibuf.fd);
    337 	free(peer);
    338 }
    339 
    340 void
    341 proc_kill_peer(struct tmuxpeer *peer)
    342 {
    343 	peer->flags |= PEER_BAD;
    344 }
    345 
    346 void
    347 proc_flush_peer(struct tmuxpeer *peer)
    348 {
    349 	imsgbuf_flush(&peer->ibuf);
    350 }
    351 
    352 void
    353 proc_toggle_log(struct tmuxproc *tp)
    354 {
    355 	log_toggle(tp->name);
    356 }
    357 
    358 pid_t
    359 proc_fork_and_daemon(int *fd)
    360 {
    361 	pid_t	pid;
    362 	int	pair[2];
    363 
    364 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
    365 		fatal("socketpair failed");
    366 	switch (pid = fork()) {
    367 	case -1:
    368 		fatal("fork failed");
    369 	case 0:
    370 		close(pair[0]);
    371 		*fd = pair[1];
    372 		if (daemon(1, 0) != 0)
    373 			fatal("daemon failed");
    374 		return (0);
    375 	default:
    376 		close(pair[1]);
    377 		*fd = pair[0];
    378 		return (pid);
    379 	}
    380 }
    381 
    382 uid_t
    383 proc_get_peer_uid(struct tmuxpeer *peer)
    384 {
    385 	return (peer->uid);
    386 }
    387