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