1 /* $OpenBSD: proc.c,v 1.10 2017/06/07 15:27:46 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2015 Nicholas Marriott <nicholas.marriott@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/queue.h> 21 #include <sys/uio.h> 22 #include <sys/utsname.h> 23 24 #include <errno.h> 25 #include <event.h> 26 #include <imsg.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <unistd.h> 30 31 #include "tmux.h" 32 33 struct tmuxproc { 34 const char *name; 35 int exit; 36 37 void (*signalcb)(int); 38 }; 39 40 struct tmuxpeer { 41 struct tmuxproc *parent; 42 43 struct imsgbuf ibuf; 44 struct event event; 45 46 int flags; 47 #define PEER_BAD 0x1 48 49 void (*dispatchcb)(struct imsg *, void *); 50 void *arg; 51 }; 52 53 static int peer_check_version(struct tmuxpeer *, struct imsg *); 54 static void proc_update_event(struct tmuxpeer *); 55 56 static void 57 proc_event_cb(__unused int fd, short events, void *arg) 58 { 59 struct tmuxpeer *peer = arg; 60 ssize_t n; 61 struct imsg imsg; 62 63 if (!(peer->flags & PEER_BAD) && (events & EV_READ)) { 64 if (((n = imsg_read(&peer->ibuf)) == -1 && errno != EAGAIN) || 65 n == 0) { 66 peer->dispatchcb(NULL, peer->arg); 67 return; 68 } 69 for (;;) { 70 if ((n = imsg_get(&peer->ibuf, &imsg)) == -1) { 71 peer->dispatchcb(NULL, peer->arg); 72 return; 73 } 74 if (n == 0) 75 break; 76 log_debug("peer %p message %d", peer, imsg.hdr.type); 77 78 if (peer_check_version(peer, &imsg) != 0) { 79 if (imsg.fd != -1) 80 close(imsg.fd); 81 imsg_free(&imsg); 82 break; 83 } 84 85 peer->dispatchcb(&imsg, peer->arg); 86 imsg_free(&imsg); 87 } 88 } 89 90 if (events & EV_WRITE) { 91 if (msgbuf_write(&peer->ibuf.w) <= 0 && errno != EAGAIN) { 92 peer->dispatchcb(NULL, peer->arg); 93 return; 94 } 95 } 96 97 if ((peer->flags & PEER_BAD) && peer->ibuf.w.queued == 0) { 98 peer->dispatchcb(NULL, peer->arg); 99 return; 100 } 101 102 proc_update_event(peer); 103 } 104 105 static void 106 proc_signal_cb(int signo, __unused short events, void *arg) 107 { 108 struct tmuxproc *tp = arg; 109 110 tp->signalcb(signo); 111 } 112 113 static int 114 peer_check_version(struct tmuxpeer *peer, struct imsg *imsg) 115 { 116 int version; 117 118 version = imsg->hdr.peerid & 0xff; 119 if (imsg->hdr.type != MSG_VERSION && version != PROTOCOL_VERSION) { 120 log_debug("peer %p bad version %d", peer, version); 121 122 proc_send(peer, MSG_VERSION, -1, NULL, 0); 123 peer->flags |= PEER_BAD; 124 125 return (-1); 126 } 127 return (0); 128 } 129 130 static void 131 proc_update_event(struct tmuxpeer *peer) 132 { 133 short events; 134 135 event_del(&peer->event); 136 137 events = EV_READ; 138 if (peer->ibuf.w.queued > 0) 139 events |= EV_WRITE; 140 event_set(&peer->event, peer->ibuf.fd, events, proc_event_cb, peer); 141 142 event_add(&peer->event, NULL); 143 } 144 145 int 146 proc_send(struct tmuxpeer *peer, enum msgtype type, int fd, const void *buf, 147 size_t len) 148 { 149 struct imsgbuf *ibuf = &peer->ibuf; 150 void *vp = (void *)buf; 151 int retval; 152 153 if (peer->flags & PEER_BAD) 154 return (-1); 155 log_debug("sending message %d to peer %p (%zu bytes)", type, peer, len); 156 157 retval = imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, fd, vp, len); 158 if (retval != 1) 159 return (-1); 160 proc_update_event(peer); 161 return (0); 162 } 163 164 int 165 proc_send_s(struct tmuxpeer *peer, enum msgtype type, const char *s) 166 { 167 return (proc_send(peer, type, -1, s, strlen(s) + 1)); 168 } 169 170 struct tmuxproc * 171 proc_start(const char *name, struct event_base *base, int forkflag, 172 void (*signalcb)(int)) 173 { 174 struct tmuxproc *tp; 175 struct utsname u; 176 177 if (forkflag) { 178 switch (fork()) { 179 case -1: 180 fatal("fork failed"); 181 case 0: 182 break; 183 default: 184 return (NULL); 185 } 186 if (daemon(1, 0) != 0) 187 fatal("daemon failed"); 188 189 clear_signals(0); 190 if (event_reinit(base) != 0) 191 fatalx("event_reinit failed"); 192 } 193 194 log_open(name); 195 setproctitle("%s (%s)", name, socket_path); 196 197 if (uname(&u) < 0) 198 memset(&u, 0, sizeof u); 199 200 log_debug("%s started (%ld): socket %s, protocol %d", name, 201 (long)getpid(), socket_path, PROTOCOL_VERSION); 202 log_debug("on %s %s %s; libevent %s (%s)", u.sysname, u.release, 203 u.version, event_get_version(), event_get_method()); 204 205 tp = xcalloc(1, sizeof *tp); 206 tp->name = xstrdup(name); 207 208 tp->signalcb = signalcb; 209 set_signals(proc_signal_cb, tp); 210 211 return (tp); 212 } 213 214 void 215 proc_loop(struct tmuxproc *tp, int (*loopcb)(void)) 216 { 217 log_debug("%s loop enter", tp->name); 218 do 219 event_loop(EVLOOP_ONCE); 220 while (!tp->exit && (loopcb == NULL || !loopcb ())); 221 log_debug("%s loop exit", tp->name); 222 } 223 224 void 225 proc_exit(struct tmuxproc *tp) 226 { 227 tp->exit = 1; 228 } 229 230 struct tmuxpeer * 231 proc_add_peer(struct tmuxproc *tp, int fd, 232 void (*dispatchcb)(struct imsg *, void *), void *arg) 233 { 234 struct tmuxpeer *peer; 235 236 peer = xcalloc(1, sizeof *peer); 237 peer->parent = tp; 238 239 peer->dispatchcb = dispatchcb; 240 peer->arg = arg; 241 242 imsg_init(&peer->ibuf, fd); 243 event_set(&peer->event, fd, EV_READ, proc_event_cb, peer); 244 245 log_debug("add peer %p: %d (%p)", peer, fd, arg); 246 247 proc_update_event(peer); 248 return (peer); 249 } 250 251 void 252 proc_remove_peer(struct tmuxpeer *peer) 253 { 254 log_debug("remove peer %p", peer); 255 256 event_del(&peer->event); 257 imsg_clear(&peer->ibuf); 258 259 close(peer->ibuf.fd); 260 free(peer); 261 } 262 263 void 264 proc_kill_peer(struct tmuxpeer *peer) 265 { 266 peer->flags |= PEER_BAD; 267 } 268 269 void 270 proc_toggle_log(struct tmuxproc *tp) 271 { 272 log_toggle(tp->name); 273 } 274