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