xref: /openbsd-src/usr.bin/tmux/proc.c (revision 0e59d0d19ca6a10a17663d531bcea1b99c1bfe09)
1*0e59d0d1Sclaudio /* $OpenBSD: proc.c,v 1.30 2024/11/21 13:35:20 claudio Exp $ */
25b8ac713Snicm 
35b8ac713Snicm /*
498ca8272Snicm  * Copyright (c) 2015 Nicholas Marriott <nicholas.marriott@gmail.com>
55b8ac713Snicm  *
65b8ac713Snicm  * Permission to use, copy, modify, and distribute this software for any
75b8ac713Snicm  * purpose with or without fee is hereby granted, provided that the above
85b8ac713Snicm  * copyright notice and this permission notice appear in all copies.
95b8ac713Snicm  *
105b8ac713Snicm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
115b8ac713Snicm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
125b8ac713Snicm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
135b8ac713Snicm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
145b8ac713Snicm  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
155b8ac713Snicm  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
165b8ac713Snicm  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
175b8ac713Snicm  */
185b8ac713Snicm 
195b8ac713Snicm #include <sys/types.h>
205b8ac713Snicm #include <sys/queue.h>
21ab26eabfSnicm #include <sys/socket.h>
225b8ac713Snicm #include <sys/uio.h>
23fca48d3eSnicm #include <sys/utsname.h>
245b8ac713Snicm 
255b8ac713Snicm #include <errno.h>
265b8ac713Snicm #include <event.h>
275b8ac713Snicm #include <imsg.h>
28c37a9299Snicm #include <signal.h>
295b8ac713Snicm #include <stdlib.h>
305b8ac713Snicm #include <string.h>
315b8ac713Snicm #include <unistd.h>
325b8ac713Snicm 
335b8ac713Snicm #include "tmux.h"
345b8ac713Snicm 
355b8ac713Snicm struct tmuxproc {
365b8ac713Snicm 	const char	 *name;
375b8ac713Snicm 	int		  exit;
385b8ac713Snicm 
395b8ac713Snicm 	void		(*signalcb)(int);
40c37a9299Snicm 
419e0637eaSnicm 	struct event	  ev_sigint;
42c37a9299Snicm 	struct event	  ev_sighup;
43c37a9299Snicm 	struct event	  ev_sigchld;
44c37a9299Snicm 	struct event	  ev_sigcont;
45c37a9299Snicm 	struct event	  ev_sigterm;
46c37a9299Snicm 	struct event	  ev_sigusr1;
47c37a9299Snicm 	struct event	  ev_sigusr2;
48c37a9299Snicm 	struct event	  ev_sigwinch;
49ab26eabfSnicm 
50ab26eabfSnicm 	TAILQ_HEAD(, tmuxpeer) peers;
515b8ac713Snicm };
525b8ac713Snicm 
535b8ac713Snicm struct tmuxpeer {
545b8ac713Snicm 	struct tmuxproc	*parent;
555b8ac713Snicm 
565b8ac713Snicm 	struct imsgbuf	 ibuf;
575b8ac713Snicm 	struct event	 event;
581c49b573Snicm 	uid_t		 uid;
595b8ac713Snicm 
605b8ac713Snicm 	int		 flags;
615b8ac713Snicm #define PEER_BAD 0x1
625b8ac713Snicm 
635b8ac713Snicm 	void		(*dispatchcb)(struct imsg *, void *);
645b8ac713Snicm 	void		 *arg;
65ab26eabfSnicm 
66ab26eabfSnicm 	TAILQ_ENTRY(tmuxpeer) entry;
675b8ac713Snicm };
685b8ac713Snicm 
69134fd25cSnicm static int	peer_check_version(struct tmuxpeer *, struct imsg *);
705b8ac713Snicm static void	proc_update_event(struct tmuxpeer *);
715b8ac713Snicm 
725b8ac713Snicm static void
73d0e2e7f1Snicm proc_event_cb(__unused int fd, short events, void *arg)
745b8ac713Snicm {
755b8ac713Snicm 	struct tmuxpeer	*peer = arg;
765b8ac713Snicm 	ssize_t		 n;
775b8ac713Snicm 	struct imsg	 imsg;
785b8ac713Snicm 
795b8ac713Snicm 	if (!(peer->flags & PEER_BAD) && (events & EV_READ)) {
80668e5ba9Sclaudio 		if (imsgbuf_read(&peer->ibuf) != 1) {
815b8ac713Snicm 			peer->dispatchcb(NULL, peer->arg);
825b8ac713Snicm 			return;
835b8ac713Snicm 		}
845b8ac713Snicm 		for (;;) {
855b8ac713Snicm 			if ((n = imsg_get(&peer->ibuf, &imsg)) == -1) {
865b8ac713Snicm 				peer->dispatchcb(NULL, peer->arg);
875b8ac713Snicm 				return;
885b8ac713Snicm 			}
895b8ac713Snicm 			if (n == 0)
905b8ac713Snicm 				break;
915b8ac713Snicm 			log_debug("peer %p message %d", peer, imsg.hdr.type);
925b8ac713Snicm 
93134fd25cSnicm 			if (peer_check_version(peer, &imsg) != 0) {
949461a728Snicm 				fd = imsg_get_fd(&imsg);
958a53274aSclaudio 				if (fd != -1)
968a53274aSclaudio 					close(fd);
975b8ac713Snicm 				imsg_free(&imsg);
985b8ac713Snicm 				break;
995b8ac713Snicm 			}
1005b8ac713Snicm 
1015b8ac713Snicm 			peer->dispatchcb(&imsg, peer->arg);
1025b8ac713Snicm 			imsg_free(&imsg);
1035b8ac713Snicm 		}
1045b8ac713Snicm 	}
1055b8ac713Snicm 
1065b8ac713Snicm 	if (events & EV_WRITE) {
107dd7efffeSclaudio 		if (imsgbuf_write(&peer->ibuf) == -1) {
1085b8ac713Snicm 			peer->dispatchcb(NULL, peer->arg);
1095b8ac713Snicm 			return;
1105b8ac713Snicm 		}
1115b8ac713Snicm 	}
1125b8ac713Snicm 
11331be28caSclaudio 	if ((peer->flags & PEER_BAD) && imsgbuf_queuelen(&peer->ibuf) == 0) {
1145b8ac713Snicm 		peer->dispatchcb(NULL, peer->arg);
1155b8ac713Snicm 		return;
1165b8ac713Snicm 	}
1175b8ac713Snicm 
1185b8ac713Snicm 	proc_update_event(peer);
1195b8ac713Snicm }
1205b8ac713Snicm 
1215b8ac713Snicm static void
122d0e2e7f1Snicm proc_signal_cb(int signo, __unused short events, void *arg)
1235b8ac713Snicm {
1245b8ac713Snicm 	struct tmuxproc	*tp = arg;
1255b8ac713Snicm 
1265b8ac713Snicm 	tp->signalcb(signo);
1275b8ac713Snicm }
1285b8ac713Snicm 
129134fd25cSnicm static int
130134fd25cSnicm peer_check_version(struct tmuxpeer *peer, struct imsg *imsg)
131134fd25cSnicm {
132134fd25cSnicm 	int	version;
133134fd25cSnicm 
134134fd25cSnicm 	version = imsg->hdr.peerid & 0xff;
135134fd25cSnicm 	if (imsg->hdr.type != MSG_VERSION && version != PROTOCOL_VERSION) {
136134fd25cSnicm 		log_debug("peer %p bad version %d", peer, version);
137134fd25cSnicm 
138134fd25cSnicm 		proc_send(peer, MSG_VERSION, -1, NULL, 0);
139134fd25cSnicm 		peer->flags |= PEER_BAD;
140134fd25cSnicm 
141134fd25cSnicm 		return (-1);
142134fd25cSnicm 	}
143134fd25cSnicm 	return (0);
144134fd25cSnicm }
145134fd25cSnicm 
1465b8ac713Snicm static void
1475b8ac713Snicm proc_update_event(struct tmuxpeer *peer)
1485b8ac713Snicm {
1495b8ac713Snicm 	short	events;
1505b8ac713Snicm 
1515b8ac713Snicm 	event_del(&peer->event);
1525b8ac713Snicm 
1535b8ac713Snicm 	events = EV_READ;
15431be28caSclaudio 	if (imsgbuf_queuelen(&peer->ibuf) > 0)
1555b8ac713Snicm 		events |= EV_WRITE;
1565b8ac713Snicm 	event_set(&peer->event, peer->ibuf.fd, events, proc_event_cb, peer);
1575b8ac713Snicm 
1585b8ac713Snicm 	event_add(&peer->event, NULL);
1595b8ac713Snicm }
1605b8ac713Snicm 
1615b8ac713Snicm int
1625b8ac713Snicm proc_send(struct tmuxpeer *peer, enum msgtype type, int fd, const void *buf,
1635b8ac713Snicm     size_t len)
1645b8ac713Snicm {
1655b8ac713Snicm 	struct imsgbuf	*ibuf = &peer->ibuf;
1665b8ac713Snicm 	void		*vp = (void *)buf;
1675b8ac713Snicm 	int		 retval;
1685b8ac713Snicm 
1695b8ac713Snicm 	if (peer->flags & PEER_BAD)
1705b8ac713Snicm 		return (-1);
1715b8ac713Snicm 	log_debug("sending message %d to peer %p (%zu bytes)", type, peer, len);
1725b8ac713Snicm 
1735b8ac713Snicm 	retval = imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, fd, vp, len);
1745b8ac713Snicm 	if (retval != 1)
1755b8ac713Snicm 		return (-1);
1765b8ac713Snicm 	proc_update_event(peer);
1775b8ac713Snicm 	return (0);
1785b8ac713Snicm }
1795b8ac713Snicm 
1805b8ac713Snicm struct tmuxproc *
181c37a9299Snicm proc_start(const char *name)
1825b8ac713Snicm {
1835b8ac713Snicm 	struct tmuxproc	*tp;
184fca48d3eSnicm 	struct utsname	 u;
1855b8ac713Snicm 
1860a607e68Snicm 	log_open(name);
1875b8ac713Snicm 	setproctitle("%s (%s)", name, socket_path);
1885b8ac713Snicm 
189fca48d3eSnicm 	if (uname(&u) < 0)
190fca48d3eSnicm 		memset(&u, 0, sizeof u);
191fca48d3eSnicm 
19255bde146Snicm 	log_debug("%s started (%ld): version %s, socket %s, protocol %d", name,
19355bde146Snicm 	    (long)getpid(), getversion(), socket_path, PROTOCOL_VERSION);
194fca48d3eSnicm 	log_debug("on %s %s %s; libevent %s (%s)", u.sysname, u.release,
195fca48d3eSnicm 	    u.version, event_get_version(), event_get_method());
1965b8ac713Snicm 
1975b8ac713Snicm 	tp = xcalloc(1, sizeof *tp);
1985b8ac713Snicm 	tp->name = xstrdup(name);
199ab26eabfSnicm 	TAILQ_INIT(&tp->peers);
2005b8ac713Snicm 
2015b8ac713Snicm 	return (tp);
2025b8ac713Snicm }
2035b8ac713Snicm 
2045b8ac713Snicm void
2055b8ac713Snicm proc_loop(struct tmuxproc *tp, int (*loopcb)(void))
2065b8ac713Snicm {
2075b8ac713Snicm 	log_debug("%s loop enter", tp->name);
2085b8ac713Snicm 	do
2095b8ac713Snicm 		event_loop(EVLOOP_ONCE);
2105b8ac713Snicm 	while (!tp->exit && (loopcb == NULL || !loopcb ()));
2115b8ac713Snicm 	log_debug("%s loop exit", tp->name);
2125b8ac713Snicm }
2135b8ac713Snicm 
2145b8ac713Snicm void
2155b8ac713Snicm proc_exit(struct tmuxproc *tp)
2165b8ac713Snicm {
217ab26eabfSnicm 	struct tmuxpeer	*peer;
218ab26eabfSnicm 
219ab26eabfSnicm 	TAILQ_FOREACH(peer, &tp->peers, entry)
220dd7efffeSclaudio 	    imsgbuf_flush(&peer->ibuf);
2215b8ac713Snicm 	tp->exit = 1;
2225b8ac713Snicm }
2235b8ac713Snicm 
224c37a9299Snicm void
225c37a9299Snicm proc_set_signals(struct tmuxproc *tp, void (*signalcb)(int))
226c37a9299Snicm {
227c37a9299Snicm 	struct sigaction	sa;
228c37a9299Snicm 
229c37a9299Snicm 	tp->signalcb = signalcb;
230c37a9299Snicm 
231c37a9299Snicm 	memset(&sa, 0, sizeof sa);
232c37a9299Snicm 	sigemptyset(&sa.sa_mask);
233c37a9299Snicm 	sa.sa_flags = SA_RESTART;
234c37a9299Snicm 	sa.sa_handler = SIG_IGN;
235c37a9299Snicm 
236c37a9299Snicm 	sigaction(SIGPIPE, &sa, NULL);
237c37a9299Snicm 	sigaction(SIGTSTP, &sa, NULL);
2389e0637eaSnicm 	sigaction(SIGTTIN, &sa, NULL);
2399e0637eaSnicm 	sigaction(SIGTTOU, &sa, NULL);
240eb5d7a07Snicm 	sigaction(SIGQUIT, &sa, NULL);
241c37a9299Snicm 
2429e0637eaSnicm 	signal_set(&tp->ev_sigint, SIGINT, proc_signal_cb, tp);
2439e0637eaSnicm 	signal_add(&tp->ev_sigint, NULL);
244c37a9299Snicm 	signal_set(&tp->ev_sighup, SIGHUP, proc_signal_cb, tp);
245c37a9299Snicm 	signal_add(&tp->ev_sighup, NULL);
246c37a9299Snicm 	signal_set(&tp->ev_sigchld, SIGCHLD, proc_signal_cb, tp);
247c37a9299Snicm 	signal_add(&tp->ev_sigchld, NULL);
248c37a9299Snicm 	signal_set(&tp->ev_sigcont, SIGCONT, proc_signal_cb, tp);
249c37a9299Snicm 	signal_add(&tp->ev_sigcont, NULL);
250c37a9299Snicm 	signal_set(&tp->ev_sigterm, SIGTERM, proc_signal_cb, tp);
251c37a9299Snicm 	signal_add(&tp->ev_sigterm, NULL);
252c37a9299Snicm 	signal_set(&tp->ev_sigusr1, SIGUSR1, proc_signal_cb, tp);
253c37a9299Snicm 	signal_add(&tp->ev_sigusr1, NULL);
254c37a9299Snicm 	signal_set(&tp->ev_sigusr2, SIGUSR2, proc_signal_cb, tp);
255c37a9299Snicm 	signal_add(&tp->ev_sigusr2, NULL);
256c37a9299Snicm 	signal_set(&tp->ev_sigwinch, SIGWINCH, proc_signal_cb, tp);
257c37a9299Snicm 	signal_add(&tp->ev_sigwinch, NULL);
258c37a9299Snicm }
259c37a9299Snicm 
260c37a9299Snicm void
26189b52a5bSnicm proc_clear_signals(struct tmuxproc *tp, int defaults)
262c37a9299Snicm {
263c37a9299Snicm 	struct sigaction	sa;
264c37a9299Snicm 
265c37a9299Snicm 	memset(&sa, 0, sizeof sa);
266c37a9299Snicm 	sigemptyset(&sa.sa_mask);
267c37a9299Snicm 	sa.sa_flags = SA_RESTART;
268c37a9299Snicm 	sa.sa_handler = SIG_DFL;
269c37a9299Snicm 
270c37a9299Snicm 	sigaction(SIGPIPE, &sa, NULL);
271c37a9299Snicm 	sigaction(SIGTSTP, &sa, NULL);
272c37a9299Snicm 
2739e0637eaSnicm 	signal_del(&tp->ev_sigint);
2743b700139Snicm 	signal_del(&tp->ev_sighup);
2753b700139Snicm 	signal_del(&tp->ev_sigchld);
2763b700139Snicm 	signal_del(&tp->ev_sigcont);
2773b700139Snicm 	signal_del(&tp->ev_sigterm);
2783b700139Snicm 	signal_del(&tp->ev_sigusr1);
2793b700139Snicm 	signal_del(&tp->ev_sigusr2);
2803b700139Snicm 	signal_del(&tp->ev_sigwinch);
28189b52a5bSnicm 
28289b52a5bSnicm 	if (defaults) {
2839e0637eaSnicm 		sigaction(SIGINT, &sa, NULL);
284b1f23571Snicm 		sigaction(SIGQUIT, &sa, NULL);
28589b52a5bSnicm 		sigaction(SIGHUP, &sa, NULL);
28689b52a5bSnicm 		sigaction(SIGCHLD, &sa, NULL);
28789b52a5bSnicm 		sigaction(SIGCONT, &sa, NULL);
28889b52a5bSnicm 		sigaction(SIGTERM, &sa, NULL);
28989b52a5bSnicm 		sigaction(SIGUSR1, &sa, NULL);
29089b52a5bSnicm 		sigaction(SIGUSR2, &sa, NULL);
29189b52a5bSnicm 		sigaction(SIGWINCH, &sa, NULL);
29289b52a5bSnicm 	}
293c37a9299Snicm }
294c37a9299Snicm 
2955b8ac713Snicm struct tmuxpeer *
2965b8ac713Snicm proc_add_peer(struct tmuxproc *tp, int fd,
2975b8ac713Snicm     void (*dispatchcb)(struct imsg *, void *), void *arg)
2985b8ac713Snicm {
2995b8ac713Snicm 	struct tmuxpeer	*peer;
3001c49b573Snicm 	gid_t		 gid;
3015b8ac713Snicm 
3025b8ac713Snicm 	peer = xcalloc(1, sizeof *peer);
3035b8ac713Snicm 	peer->parent = tp;
3045b8ac713Snicm 
3055b8ac713Snicm 	peer->dispatchcb = dispatchcb;
3065b8ac713Snicm 	peer->arg = arg;
3075b8ac713Snicm 
308*0e59d0d1Sclaudio 	if (imsgbuf_init(&peer->ibuf, fd) == -1)
309*0e59d0d1Sclaudio 		fatal("imsgbuf_init");
310*0e59d0d1Sclaudio 	imsgbuf_allow_fdpass(&peer->ibuf);
3115b8ac713Snicm 	event_set(&peer->event, fd, EV_READ, proc_event_cb, peer);
3125b8ac713Snicm 
3131c49b573Snicm 	if (getpeereid(fd, &peer->uid, &gid) != 0)
3141c49b573Snicm 		peer->uid = (uid_t)-1;
3151c49b573Snicm 
3165b8ac713Snicm 	log_debug("add peer %p: %d (%p)", peer, fd, arg);
317ab26eabfSnicm 	TAILQ_INSERT_TAIL(&tp->peers, peer, entry);
3185b8ac713Snicm 
3195b8ac713Snicm 	proc_update_event(peer);
3205b8ac713Snicm 	return (peer);
3215b8ac713Snicm }
3225b8ac713Snicm 
3235b8ac713Snicm void
3245b8ac713Snicm proc_remove_peer(struct tmuxpeer *peer)
3255b8ac713Snicm {
326ab26eabfSnicm 	TAILQ_REMOVE(&peer->parent->peers, peer, entry);
3275b8ac713Snicm 	log_debug("remove peer %p", peer);
3285b8ac713Snicm 
3295b8ac713Snicm 	event_del(&peer->event);
330dd7efffeSclaudio 	imsgbuf_clear(&peer->ibuf);
3315b8ac713Snicm 
3325b8ac713Snicm 	close(peer->ibuf.fd);
3335b8ac713Snicm 	free(peer);
3345b8ac713Snicm }
3355b8ac713Snicm 
3365b8ac713Snicm void
3375b8ac713Snicm proc_kill_peer(struct tmuxpeer *peer)
3385b8ac713Snicm {
3395b8ac713Snicm 	peer->flags |= PEER_BAD;
3405b8ac713Snicm }
34179c9b201Snicm 
34279c9b201Snicm void
3438ab000fcSnicm proc_flush_peer(struct tmuxpeer *peer)
3448ab000fcSnicm {
345dd7efffeSclaudio 	imsgbuf_flush(&peer->ibuf);
3468ab000fcSnicm }
3478ab000fcSnicm 
3488ab000fcSnicm void
34979c9b201Snicm proc_toggle_log(struct tmuxproc *tp)
35079c9b201Snicm {
35179c9b201Snicm 	log_toggle(tp->name);
35279c9b201Snicm }
353ab26eabfSnicm 
354ab26eabfSnicm pid_t
355ab26eabfSnicm proc_fork_and_daemon(int *fd)
356ab26eabfSnicm {
357ab26eabfSnicm 	pid_t	pid;
358ab26eabfSnicm 	int	pair[2];
359ab26eabfSnicm 
360ab26eabfSnicm 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
361ab26eabfSnicm 		fatal("socketpair failed");
362ab26eabfSnicm 	switch (pid = fork()) {
363ab26eabfSnicm 	case -1:
364ab26eabfSnicm 		fatal("fork failed");
365ab26eabfSnicm 	case 0:
366ab26eabfSnicm 		close(pair[0]);
367ab26eabfSnicm 		*fd = pair[1];
368ab26eabfSnicm 		if (daemon(1, 0) != 0)
369ab26eabfSnicm 			fatal("daemon failed");
370ab26eabfSnicm 		return (0);
371ab26eabfSnicm 	default:
372ab26eabfSnicm 		close(pair[1]);
373ab26eabfSnicm 		*fd = pair[0];
374ab26eabfSnicm 		return (pid);
375ab26eabfSnicm 	}
376ab26eabfSnicm }
3771c49b573Snicm 
3781c49b573Snicm uid_t
3791c49b573Snicm proc_get_peer_uid(struct tmuxpeer *peer)
3801c49b573Snicm {
3811c49b573Snicm 	return (peer->uid);
3821c49b573Snicm }
383