1 /* $OpenBSD: imsgev.c,v 1.6 2017/03/01 00:53:39 gsoares Exp $ */ 2 3 /* 4 * Copyright (c) 2009 Eric Faurot <eric@openbsd.org> 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 USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/queue.h> 20 #include <sys/socket.h> 21 #include <sys/uio.h> 22 23 #include <errno.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <unistd.h> 28 29 #include "imsgev.h" 30 31 void imsgev_add(struct imsgev *); 32 void imsgev_dispatch(int, short, void *); 33 void imsgev_disconnect(struct imsgev *, int); 34 35 void 36 imsgev_init(struct imsgev *iev, int fd, void *data, 37 void (*callback)(struct imsgev *, int, struct imsg *), 38 void (*needfd)(struct imsgev *)) 39 { 40 imsg_init(&iev->ibuf, fd); 41 iev->terminate = 0; 42 43 iev->data = data; 44 iev->handler = imsgev_dispatch; 45 iev->callback = callback; 46 iev->needfd = needfd; 47 48 iev->events = EV_READ; 49 event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev); 50 event_add(&iev->ev, NULL); 51 } 52 53 int 54 imsgev_compose(struct imsgev *iev, u_int16_t type, u_int32_t peerid, 55 uint32_t pid, int fd, void *data, u_int16_t datalen) 56 { 57 int r; 58 59 r = imsg_compose(&iev->ibuf, type, peerid, pid, fd, data, datalen); 60 if (r != -1) 61 imsgev_add(iev); 62 63 return (r); 64 } 65 66 void 67 imsgev_close(struct imsgev *iev) 68 { 69 iev->terminate = 1; 70 imsgev_add(iev); 71 } 72 73 void 74 imsgev_clear(struct imsgev *iev) 75 { 76 event_del(&iev->ev); 77 msgbuf_clear(&iev->ibuf.w); 78 close(iev->ibuf.fd); 79 } 80 81 void 82 imsgev_add(struct imsgev *iev) 83 { 84 short events = 0; 85 86 if (!iev->terminate) 87 events = EV_READ; 88 if (iev->ibuf.w.queued || iev->terminate) 89 events |= EV_WRITE; 90 91 /* optimization: skip event_{del/set/add} if already set */ 92 if (events == iev->events) 93 return; 94 95 iev->events = events; 96 event_del(&iev->ev); 97 event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev); 98 event_add(&iev->ev, NULL); 99 } 100 101 void 102 imsgev_dispatch(int fd, short ev, void *humppa) 103 { 104 struct imsgev *iev = humppa; 105 struct imsgbuf *ibuf = &iev->ibuf; 106 struct imsg imsg; 107 ssize_t n; 108 109 iev->events = 0; 110 111 if (ev & EV_READ) { 112 if ((n = imsg_read(ibuf)) == -1) { 113 /* if we don't have enough fds, free one up and retry */ 114 if (errno == EAGAIN) { 115 iev->needfd(iev); 116 n = imsg_read(ibuf); 117 } 118 119 if (n == -1) { 120 imsgev_disconnect(iev, IMSGEV_EREAD); 121 return; 122 } 123 } 124 if (n == 0) { 125 /* 126 * Connection is closed for reading, and we assume 127 * it is also closed for writing, so we error out 128 * if write data is pending. 129 */ 130 imsgev_disconnect(iev, 131 (iev->ibuf.w.queued) ? IMSGEV_EWRITE : IMSGEV_DONE); 132 return; 133 } 134 } 135 136 if (ev & EV_WRITE) { 137 /* 138 * We wanted to write data out but the connection is either 139 * closed, or some error occured. Both case are not recoverable 140 * from the imsg perspective, so we treat it as a WRITE error. 141 */ 142 if ((n = msgbuf_write(&ibuf->w)) <= 0 && errno != EAGAIN) { 143 imsgev_disconnect(iev, IMSGEV_EWRITE); 144 return; 145 } 146 } 147 148 while (iev->terminate == 0) { 149 if ((n = imsg_get(ibuf, &imsg)) == -1) { 150 imsgev_disconnect(iev, IMSGEV_EIMSG); 151 return; 152 } 153 if (n == 0) 154 break; 155 iev->callback(iev, IMSGEV_IMSG, &imsg); 156 imsg_free(&imsg); 157 } 158 159 if (iev->terminate && iev->ibuf.w.queued == 0) { 160 imsgev_disconnect(iev, IMSGEV_DONE); 161 return; 162 } 163 164 imsgev_add(iev); 165 } 166 167 void 168 imsgev_disconnect(struct imsgev *iev, int code) 169 { 170 iev->callback(iev, code, NULL); 171 } 172