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