xref: /openbsd/usr.sbin/iscsid/control.c (revision 09467b48)
1 /*	$OpenBSD: control.c,v 1.10 2016/09/10 05:42:12 jsg 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
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
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
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 /* ARGSUSED */
139 void
140 control_accept(int listenfd, short event, void *bula)
141 {
142 	int			 connfd;
143 	socklen_t		 len;
144 	struct sockaddr_un	 sun;
145 	struct control		*c;
146 
147 	event_add(&control_state->ev, NULL);
148 	if ((event & EV_TIMEOUT))
149 		return;
150 
151 	len = sizeof(sun);
152 	if ((connfd = accept(listenfd,
153 	    (struct sockaddr *)&sun, &len)) == -1) {
154 		/*
155 		 * Pause accept if we are out of file descriptors, or
156 		 * libevent will haunt us here too.
157 		 */
158 		if (errno == ENFILE || errno == EMFILE) {
159 			struct timeval evtpause = { 1, 0 };
160 
161 			event_del(&control_state->ev);
162 			evtimer_add(&control_state->evt, &evtpause);
163 		} else if (errno != EWOULDBLOCK && errno != EINTR &&
164 		    errno != ECONNABORTED)
165 			log_warn("control_accept");
166 		return;
167 	}
168 
169 	if ((c = malloc(sizeof(struct control))) == NULL) {
170 		log_warn("control_accept");
171 		close(connfd);
172 		return;
173 	}
174 
175 	TAILQ_INIT(&c->channel);
176 	c->fd = connfd;
177 	event_set(&c->ev, connfd, EV_READ, control_dispatch, c);
178 	event_add(&c->ev, NULL);
179 }
180 
181 void
182 control_close(struct control *c)
183 {
184 	event_del(&c->ev);
185 	close(c->fd);
186 
187 	/* Some file descriptors are available again. */
188 	if (evtimer_pending(&control_state->evt, NULL)) {
189 		evtimer_del(&control_state->evt);
190 		event_add(&control_state->ev, NULL);
191 	}
192 
193 	pdu_free_queue(&c->channel);
194 	free(c);
195 }
196 
197 static char	cbuf[CONTROL_READ_SIZE];
198 
199 /* ARGSUSED */
200 void
201 control_dispatch(int fd, short event, void *bula)
202 {
203 	struct iovec iov[PDU_MAXIOV];
204 	struct msghdr msg;
205 	struct control *c = bula;
206 	struct pdu *pdu;
207 	ssize_t	 n;
208 	unsigned int niov = 0;
209 	short flags = EV_READ;
210 
211 	if (event & EV_TIMEOUT) {
212 		log_debug("control connection (fd %d) timed out.", fd);
213 		control_close(c);
214 		return;
215 	}
216 	if (event & EV_READ) {
217 		if ((n = recv(fd, cbuf, sizeof(cbuf), 0)) == -1 &&
218 		    !(errno == EAGAIN || errno == EINTR)) {
219 			control_close(c);
220 			return;
221 		}
222 		if (n == 0) {
223 			control_close(c);
224 			return;
225 		}
226 		pdu = control_getpdu(cbuf, n);
227 		if (!pdu) {
228 			log_debug("control connection (fd %d) bad msg.", fd);
229 			control_close(c);
230 			return;
231 		}
232 		iscsid_ctrl_dispatch(c, pdu);
233 	}
234 	if (event & EV_WRITE) {
235 		if ((pdu = TAILQ_FIRST(&c->channel)) != NULL) {
236 			for (niov = 0; niov < PDU_MAXIOV; niov++) {
237 				iov[niov].iov_base = pdu->iov[niov].iov_base;
238 				iov[niov].iov_len = pdu->iov[niov].iov_len;
239 			}
240 			bzero(&msg, sizeof(msg));
241 			msg.msg_iov = iov;
242 			msg.msg_iovlen = niov;
243 			if (sendmsg(fd, &msg, 0) == -1) {
244 				if (errno == EAGAIN || errno == ENOBUFS)
245 					goto requeue;
246 				control_close(c);
247 				return;
248 			}
249 			TAILQ_REMOVE(&c->channel, pdu, entry);
250 		}
251 	}
252 requeue:
253 	if (!TAILQ_EMPTY(&c->channel))
254 		flags |= EV_WRITE;
255 
256 	event_del(&c->ev);
257 	event_set(&c->ev, fd, flags, control_dispatch, c);
258 	event_add(&c->ev, NULL);
259 }
260 
261 struct pdu *
262 control_getpdu(char *buf, size_t len)
263 {
264 	struct pdu *p;
265 	struct ctrlmsghdr *cmh;
266 	void *data;
267 	size_t n;
268 	int i;
269 
270 	if (len < sizeof(*cmh))
271 		return NULL;
272 
273 	if (!(p = pdu_new()))
274 		return NULL;
275 
276 	n = sizeof(*cmh);
277 	cmh = pdu_alloc(n);
278 	memcpy(cmh, buf, n);
279 	buf += n;
280 	len -= n;
281 
282 	if (pdu_addbuf(p, cmh, n, 0)) {
283 		free(cmh);
284 fail:
285 		pdu_free(p);
286 		return NULL;
287 	}
288 
289 	for (i = 0; i < 3; i++) {
290 		n = cmh->len[i];
291 		if (n == 0)
292 			continue;
293 		if (PDU_LEN(n) > len)
294 			goto fail;
295 		if (!(data = pdu_alloc(n)))
296 			goto fail;
297 		memcpy(data, buf, n);
298 		if (pdu_addbuf(p, data, n, i + 1)) {
299 			free(data);
300 			goto fail;
301 		}
302 		buf += PDU_LEN(n);
303 		len -= PDU_LEN(n);
304 	}
305 
306 	return p;
307 }
308 
309 void
310 control_queue(void *ch, struct pdu *pdu)
311 {
312 	struct control *c = ch;
313 
314 	TAILQ_INSERT_TAIL(&c->channel, pdu, entry);
315 
316 	event_del(&c->ev);
317 	event_set(&c->ev, c->fd, EV_READ|EV_WRITE, control_dispatch, c);
318 	event_add(&c->ev, NULL);
319 }
320