1 /* $OpenBSD: imsgev.c,v 1.13 2024/11/21 13:39:07 claudio 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 #include "log.h" 31 32 void imsgev_add(struct imsgev *); 33 void imsgev_dispatch(int, short, void *); 34 void imsgev_disconnect(struct imsgev *, int); 35 36 void 37 imsgev_init(struct imsgev *iev, int fd, void *data, 38 void (*callback)(struct imsgev *, int, struct imsg *), 39 void (*needfd)(struct imsgev *)) 40 { 41 if (imsgbuf_init(&iev->ibuf, fd) == -1) 42 fatal("imsgbuf_init"); 43 imsgbuf_allow_fdpass(&iev->ibuf); 44 iev->terminate = 0; 45 46 iev->data = data; 47 iev->handler = imsgev_dispatch; 48 iev->callback = callback; 49 iev->needfd = needfd; 50 51 iev->events = EV_READ; 52 event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev); 53 event_add(&iev->ev, NULL); 54 } 55 56 int 57 imsgev_compose(struct imsgev *iev, u_int16_t type, u_int32_t peerid, 58 uint32_t pid, int fd, void *data, u_int16_t datalen) 59 { 60 int r; 61 62 r = imsg_compose(&iev->ibuf, type, peerid, pid, fd, data, datalen); 63 if (r != -1) 64 imsgev_add(iev); 65 66 return (r); 67 } 68 69 void 70 imsgev_close(struct imsgev *iev) 71 { 72 iev->terminate = 1; 73 imsgev_add(iev); 74 } 75 76 void 77 imsgev_clear(struct imsgev *iev) 78 { 79 event_del(&iev->ev); 80 imsgbuf_clear(&iev->ibuf); 81 close(iev->ibuf.fd); 82 } 83 84 void 85 imsgev_add(struct imsgev *iev) 86 { 87 short events = 0; 88 89 if (!iev->terminate) 90 events = EV_READ; 91 if (imsgbuf_queuelen(&iev->ibuf) > 0 || iev->terminate) 92 events |= EV_WRITE; 93 94 /* optimization: skip event_{del/set/add} if already set */ 95 if (events == iev->events) 96 return; 97 98 iev->events = events; 99 event_del(&iev->ev); 100 event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev); 101 event_add(&iev->ev, NULL); 102 } 103 104 void 105 imsgev_dispatch(int fd, short ev, void *humppa) 106 { 107 struct imsgev *iev = humppa; 108 struct imsgbuf *ibuf = &iev->ibuf; 109 struct imsg imsg; 110 ssize_t n; 111 112 iev->events = 0; 113 114 if (ev & EV_READ) { 115 /* if we don't have enough fds, free one up and retry */ 116 if (getdtablesize() <= getdtablecount() + 117 (int)((CMSG_SPACE(sizeof(int))-CMSG_SPACE(0))/sizeof(int))) 118 iev->needfd(iev); 119 120 if ((n = imsgbuf_read(ibuf)) == -1) { 121 imsgev_disconnect(iev, IMSGEV_EREAD); 122 return; 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 imsgbuf_queuelen(&iev->ibuf) > 0 ? IMSGEV_EWRITE : 132 IMSGEV_DONE); 133 return; 134 } 135 } 136 137 if (ev & EV_WRITE) { 138 /* 139 * We wanted to write data out but the connection is either 140 * closed, or some error occured. Both case are not recoverable 141 * from the imsg perspective, so we treat it as a WRITE error. 142 */ 143 if (imsgbuf_write(ibuf) == -1) { 144 imsgev_disconnect(iev, IMSGEV_EWRITE); 145 return; 146 } 147 } 148 149 while (iev->terminate == 0) { 150 if ((n = imsg_get(ibuf, &imsg)) == -1) { 151 imsgev_disconnect(iev, IMSGEV_EIMSG); 152 return; 153 } 154 if (n == 0) 155 break; 156 iev->callback(iev, IMSGEV_IMSG, &imsg); 157 imsg_free(&imsg); 158 } 159 160 if (iev->terminate && imsgbuf_queuelen(&iev->ibuf) == 0) { 161 imsgev_disconnect(iev, IMSGEV_DONE); 162 return; 163 } 164 165 imsgev_add(iev); 166 } 167 168 void 169 imsgev_disconnect(struct imsgev *iev, int code) 170 { 171 iev->callback(iev, code, NULL); 172 } 173