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