1 /* $OpenBSD: control.c,v 1.2 2010/09/24 10:32:57 claudio Exp $ */ 2 3 /* 4 * Copyright (c) 2010 Claudio Jeker <claudio@openbsd.org> 5 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/types.h> 21 #include <sys/queue.h> 22 #include <sys/stat.h> 23 #include <sys/socket.h> 24 #include <sys/uio.h> 25 #include <sys/un.h> 26 #include <errno.h> 27 #include <event.h> 28 #include <fcntl.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <unistd.h> 32 33 #include "iscsid.h" 34 #include "log.h" 35 36 struct control { 37 TAILQ_ENTRY(control) entry; 38 struct event ev; 39 struct pduq channel; 40 int fd; 41 } *control_state; 42 43 TAILQ_HEAD(, control) controls; 44 45 #define CONTROL_BACKLOG 5 46 47 void control_accept(int, short, void *); 48 void control_close(struct control *); 49 void control_dispatch(int, short, void *); 50 struct pdu *control_getpdu(char *, size_t); 51 52 int 53 control_init(char *path) 54 { 55 struct sockaddr_un sun; 56 int fd; 57 mode_t old_umask; 58 59 if ((control_state = calloc(1, sizeof(*control_state))) == NULL) { 60 log_warn("control_init: calloc"); 61 return (-1); 62 } 63 64 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { 65 log_warn("control_init: socket"); 66 return (-1); 67 } 68 69 bzero(&sun, sizeof(sun)); 70 sun.sun_family = AF_UNIX; 71 strlcpy(sun.sun_path, path, sizeof(sun.sun_path)); 72 73 if (unlink(path) == -1) 74 if (errno != ENOENT) { 75 log_warn("control_init: unlink %s", path); 76 close(fd); 77 return (-1); 78 } 79 80 old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH); 81 if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) { 82 log_warn("control_init: bind: %s", path); 83 close(fd); 84 umask(old_umask); 85 return (-1); 86 } 87 umask(old_umask); 88 89 if (chmod(path, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) { 90 log_warn("control_init: chmod"); 91 close(fd); 92 (void)unlink(path); 93 return (-1); 94 } 95 96 socket_setblockmode(fd, 1); 97 control_state->fd = fd; 98 TAILQ_INIT(&control_state->channel); 99 TAILQ_INIT(&controls); 100 101 return (0); 102 } 103 104 void 105 control_cleanup(char *path) 106 { 107 struct control *c; 108 109 if (path) 110 unlink(path); 111 112 while ((c = TAILQ_FIRST(&controls)) != NULL) { 113 TAILQ_REMOVE(&controls, c, entry); 114 control_close(c); 115 } 116 control_close(control_state); 117 } 118 119 int 120 control_listen(void) 121 { 122 if (listen(control_state->fd, CONTROL_BACKLOG) == -1) { 123 log_warn("control_listen: listen"); 124 return (-1); 125 } 126 127 event_set(&control_state->ev, control_state->fd, EV_READ | EV_PERSIST, 128 control_accept, NULL); 129 event_add(&control_state->ev, NULL); 130 131 return (0); 132 } 133 134 /* ARGSUSED */ 135 void 136 control_accept(int listenfd, short event, void *bula) 137 { 138 int connfd; 139 socklen_t len; 140 struct sockaddr_un sun; 141 struct control *c; 142 143 len = sizeof(sun); 144 if ((connfd = accept(listenfd, 145 (struct sockaddr *)&sun, &len)) == -1) { 146 if (errno != EWOULDBLOCK && errno != EINTR) 147 log_warn("control_accept"); 148 return; 149 } 150 151 if ((c = malloc(sizeof(struct control))) == NULL) { 152 log_warn("control_accept"); 153 close(connfd); 154 return; 155 } 156 157 TAILQ_INIT(&c->channel); 158 c->fd = connfd; 159 event_set(&c->ev, connfd, EV_READ, control_dispatch, c); 160 event_add(&c->ev, NULL); 161 } 162 163 void 164 control_close(struct control *c) 165 { 166 close(c->fd); 167 event_del(&c->ev); 168 pdu_free_queue(&c->channel); 169 free(c); 170 } 171 172 static char cbuf[CONTROL_READ_SIZE]; 173 174 /* ARGSUSED */ 175 void 176 control_dispatch(int fd, short event, void *bula) 177 { 178 struct iovec iov[PDU_MAXIOV]; 179 struct msghdr msg; 180 struct control *c = bula; 181 struct pdu *pdu; 182 ssize_t n; 183 unsigned int niov = 0; 184 short flags = EV_READ; 185 186 if (event & EV_TIMEOUT) { 187 log_debug("control connection (fd %d) timed out.", fd); 188 control_close(c); 189 return; 190 } 191 if (event & EV_READ) { 192 if ((n = recv(fd, cbuf, sizeof(cbuf), 0)) == -1 && 193 !(errno == EAGAIN || errno == EINTR)) { 194 control_close(c); 195 return; 196 } 197 if (n == 0) { 198 control_close(c); 199 return; 200 } 201 pdu = control_getpdu(cbuf, n); 202 if (!pdu) { 203 log_debug("control connection (fd %d) bad msg.", fd); 204 control_close(c); 205 return; 206 } 207 iscsid_ctrl_dispatch(c, pdu); 208 } 209 if (event & EV_WRITE) { 210 if ((pdu = TAILQ_FIRST(&c->channel)) != NULL) { 211 TAILQ_REMOVE(&c->channel, pdu, entry); 212 213 for (niov = 0; niov < PDU_MAXIOV; niov++) { 214 iov[niov].iov_base = pdu->iov[niov].iov_base; 215 iov[niov].iov_len = pdu->iov[niov].iov_len; 216 } 217 bzero(&msg, sizeof(msg)); 218 msg.msg_iov = iov; 219 msg.msg_iovlen = niov; 220 if (sendmsg(fd, &msg, 0) == -1 && 221 !(errno == EAGAIN || errno == ENOBUFS)) { 222 control_close(c); 223 return; 224 } 225 } 226 } 227 if (!TAILQ_EMPTY(&c->channel)) 228 flags |= EV_WRITE; 229 230 event_del(&c->ev); 231 event_set(&c->ev, fd, flags, control_dispatch, c); 232 event_add(&c->ev, NULL); 233 } 234 235 struct pdu * 236 control_getpdu(char *buf, size_t len) 237 { 238 struct pdu *p; 239 struct ctrlmsghdr *cmh; 240 void *data; 241 size_t n; 242 int i; 243 244 if (len < sizeof(*cmh)) 245 return NULL; 246 247 if (!(p = pdu_new())) 248 return NULL; 249 250 n = sizeof(*cmh); 251 cmh = pdu_alloc(n); 252 bcopy(buf, cmh, n); 253 buf += n; 254 len -= n; 255 256 if (pdu_addbuf(p, cmh, n, 0)) { 257 free(cmh); 258 fail: 259 pdu_free(p); 260 return NULL; 261 } 262 263 for (i = 0; i < 3; i++) { 264 n = cmh->len[i]; 265 if (n == 0) 266 continue; 267 if (PDU_LEN(n) > len) 268 goto fail; 269 if (!(data = pdu_alloc(n))) 270 goto fail; 271 bcopy(buf, data, n); 272 if (pdu_addbuf(p, data, n, i + 1)) { 273 free(data); 274 goto fail; 275 } 276 buf += PDU_LEN(n); 277 len -= PDU_LEN(n); 278 } 279 280 return p; 281 } 282 283 int 284 control_queue(void *ch, struct pdu *pdu) 285 { 286 struct control *c = ch; 287 288 TAILQ_INSERT_TAIL(&c->channel, pdu, entry); 289 290 event_del(&c->ev); 291 event_set(&c->ev, c->fd, EV_READ|EV_WRITE, control_dispatch, c); 292 event_add(&c->ev, NULL); 293 294 return 0; 295 } 296 297 int 298 control_compose(void *ch, u_int16_t type, void *buf, size_t len) 299 { 300 struct pdu *pdu; 301 struct ctrlmsghdr *cmh; 302 void *ptr; 303 304 if (PDU_LEN(len) > CONTROL_READ_SIZE - PDU_LEN(sizeof(*cmh))) 305 return -1; 306 if ((pdu = pdu_new()) == NULL) 307 return -1; 308 if ((cmh = pdu_alloc(sizeof(*cmh))) == NULL) 309 goto fail; 310 bzero(cmh, sizeof(*cmh)); 311 cmh->type = type; 312 cmh->len[0] = len; 313 pdu_addbuf(pdu, cmh, sizeof(*cmh), 0); 314 if (len > 0) { 315 if ((ptr = pdu_alloc(len)) == NULL) 316 goto fail; 317 bcopy(buf, ptr, len); 318 pdu_addbuf(pdu, ptr, len, 1); 319 } 320 321 return control_queue(ch, pdu); 322 fail: 323 pdu_free(pdu); 324 return -1; 325 } 326