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