xref: /openbsd/usr.sbin/iscsid/control.c (revision 5b133f3f)
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