1 /* $OpenBSD: control.c,v 1.11 2023/03/08 04:43:13 guenther 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 struct event ev;
38 struct pduq channel;
39 int fd;
40 };
41
42 struct control_state {
43 struct event ev;
44 struct event evt;
45 int fd;
46 } *control_state;
47
48 #define CONTROL_BACKLOG 5
49
50 void control_accept(int, short, void *);
51 void control_close(struct control *);
52 void control_dispatch(int, short, void *);
53 struct pdu *control_getpdu(char *, size_t);
54
55 int
control_init(char * path)56 control_init(char *path)
57 {
58 struct sockaddr_un sun;
59 int fd;
60 mode_t old_umask;
61
62 if ((control_state = calloc(1, sizeof(*control_state))) == NULL) {
63 log_warn("control_init: calloc");
64 return -1;
65 }
66
67 if ((fd = socket(AF_UNIX, SOCK_SEQPACKET, 0)) == -1) {
68 log_warn("control_init: socket");
69 return -1;
70 }
71
72 bzero(&sun, sizeof(sun));
73 sun.sun_family = AF_UNIX;
74 if (strlcpy(sun.sun_path, path, sizeof(sun.sun_path)) >=
75 sizeof(sun.sun_path)) {
76 log_warnx("control_init: path %s too long", path);
77 close(fd);
78 return -1;
79 }
80
81 if (unlink(path) == -1)
82 if (errno != ENOENT) {
83 log_warn("control_init: unlink %s", path);
84 close(fd);
85 return -1;
86 }
87
88 old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
89 if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
90 log_warn("control_init: bind: %s", path);
91 close(fd);
92 umask(old_umask);
93 return -1;
94 }
95 umask(old_umask);
96
97 if (chmod(path, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) {
98 log_warn("control_init: chmod");
99 close(fd);
100 (void)unlink(path);
101 return -1;
102 }
103
104 if (listen(fd, CONTROL_BACKLOG) == -1) {
105 log_warn("control_init: listen");
106 close(fd);
107 (void)unlink(path);
108 return -1;
109 }
110
111 socket_setblockmode(fd, 1);
112 control_state->fd = fd;
113
114 return 0;
115 }
116
117 void
control_cleanup(char * path)118 control_cleanup(char *path)
119 {
120 if (path)
121 unlink(path);
122
123 event_del(&control_state->ev);
124 event_del(&control_state->evt);
125 close(control_state->fd);
126 free(control_state);
127 }
128
129 void
control_event_init(void)130 control_event_init(void)
131 {
132 event_set(&control_state->ev, control_state->fd, EV_READ,
133 control_accept, NULL);
134 event_add(&control_state->ev, NULL);
135 evtimer_set(&control_state->evt, control_accept, NULL);
136 }
137
138 void
control_accept(int listenfd,short event,void * bula)139 control_accept(int listenfd, short event, void *bula)
140 {
141 int connfd;
142 socklen_t len;
143 struct sockaddr_un sun;
144 struct control *c;
145
146 event_add(&control_state->ev, NULL);
147 if ((event & EV_TIMEOUT))
148 return;
149
150 len = sizeof(sun);
151 if ((connfd = accept(listenfd,
152 (struct sockaddr *)&sun, &len)) == -1) {
153 /*
154 * Pause accept if we are out of file descriptors, or
155 * libevent will haunt us here too.
156 */
157 if (errno == ENFILE || errno == EMFILE) {
158 struct timeval evtpause = { 1, 0 };
159
160 event_del(&control_state->ev);
161 evtimer_add(&control_state->evt, &evtpause);
162 } else if (errno != EWOULDBLOCK && errno != EINTR &&
163 errno != ECONNABORTED)
164 log_warn("control_accept");
165 return;
166 }
167
168 if ((c = malloc(sizeof(struct control))) == NULL) {
169 log_warn("control_accept");
170 close(connfd);
171 return;
172 }
173
174 TAILQ_INIT(&c->channel);
175 c->fd = connfd;
176 event_set(&c->ev, connfd, EV_READ, control_dispatch, c);
177 event_add(&c->ev, NULL);
178 }
179
180 void
control_close(struct control * c)181 control_close(struct control *c)
182 {
183 event_del(&c->ev);
184 close(c->fd);
185
186 /* Some file descriptors are available again. */
187 if (evtimer_pending(&control_state->evt, NULL)) {
188 evtimer_del(&control_state->evt);
189 event_add(&control_state->ev, NULL);
190 }
191
192 pdu_free_queue(&c->channel);
193 free(c);
194 }
195
196 static char cbuf[CONTROL_READ_SIZE];
197
198 void
control_dispatch(int fd,short event,void * bula)199 control_dispatch(int fd, short event, void *bula)
200 {
201 struct iovec iov[PDU_MAXIOV];
202 struct msghdr msg;
203 struct control *c = bula;
204 struct pdu *pdu;
205 ssize_t n;
206 unsigned int niov = 0;
207 short flags = EV_READ;
208
209 if (event & EV_TIMEOUT) {
210 log_debug("control connection (fd %d) timed out.", fd);
211 control_close(c);
212 return;
213 }
214 if (event & EV_READ) {
215 if ((n = recv(fd, cbuf, sizeof(cbuf), 0)) == -1 &&
216 !(errno == EAGAIN || errno == EINTR)) {
217 control_close(c);
218 return;
219 }
220 if (n == 0) {
221 control_close(c);
222 return;
223 }
224 pdu = control_getpdu(cbuf, n);
225 if (!pdu) {
226 log_debug("control connection (fd %d) bad msg.", fd);
227 control_close(c);
228 return;
229 }
230 iscsid_ctrl_dispatch(c, pdu);
231 }
232 if (event & EV_WRITE) {
233 if ((pdu = TAILQ_FIRST(&c->channel)) != NULL) {
234 for (niov = 0; niov < PDU_MAXIOV; niov++) {
235 iov[niov].iov_base = pdu->iov[niov].iov_base;
236 iov[niov].iov_len = pdu->iov[niov].iov_len;
237 }
238 bzero(&msg, sizeof(msg));
239 msg.msg_iov = iov;
240 msg.msg_iovlen = niov;
241 if (sendmsg(fd, &msg, 0) == -1) {
242 if (errno == EAGAIN || errno == ENOBUFS)
243 goto requeue;
244 control_close(c);
245 return;
246 }
247 TAILQ_REMOVE(&c->channel, pdu, entry);
248 }
249 }
250 requeue:
251 if (!TAILQ_EMPTY(&c->channel))
252 flags |= EV_WRITE;
253
254 event_del(&c->ev);
255 event_set(&c->ev, fd, flags, control_dispatch, c);
256 event_add(&c->ev, NULL);
257 }
258
259 struct pdu *
control_getpdu(char * buf,size_t len)260 control_getpdu(char *buf, size_t len)
261 {
262 struct pdu *p;
263 struct ctrlmsghdr *cmh;
264 void *data;
265 size_t n;
266 int i;
267
268 if (len < sizeof(*cmh))
269 return NULL;
270
271 if (!(p = pdu_new()))
272 return NULL;
273
274 n = sizeof(*cmh);
275 cmh = pdu_alloc(n);
276 memcpy(cmh, buf, n);
277 buf += n;
278 len -= n;
279
280 if (pdu_addbuf(p, cmh, n, 0)) {
281 free(cmh);
282 fail:
283 pdu_free(p);
284 return NULL;
285 }
286
287 for (i = 0; i < 3; i++) {
288 n = cmh->len[i];
289 if (n == 0)
290 continue;
291 if (PDU_LEN(n) > len)
292 goto fail;
293 if (!(data = pdu_alloc(n)))
294 goto fail;
295 memcpy(data, buf, n);
296 if (pdu_addbuf(p, data, n, i + 1)) {
297 free(data);
298 goto fail;
299 }
300 buf += PDU_LEN(n);
301 len -= PDU_LEN(n);
302 }
303
304 return p;
305 }
306
307 void
control_queue(void * ch,struct pdu * pdu)308 control_queue(void *ch, struct pdu *pdu)
309 {
310 struct control *c = ch;
311
312 TAILQ_INSERT_TAIL(&c->channel, pdu, entry);
313
314 event_del(&c->ev);
315 event_set(&c->ev, c->fd, EV_READ|EV_WRITE, control_dispatch, c);
316 event_add(&c->ev, NULL);
317 }
318