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