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