xref: /netbsd-src/sbin/iscsid/iscsid_main.c (revision b757af438b42b93f8c6571f026d8b8ef3eaf5fc9)
1 /*	$NetBSD: iscsid_main.c,v 1.3 2011/11/20 01:23:57 agc Exp $	*/
2 
3 /*-
4  * Copyright (c) 2005,2006,2011 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Wasabi Systems, Inc.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include "iscsid_globals.h"
33 
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <sys/un.h>
37 #include <sys/sysctl.h>
38 
39 #include <ctype.h>
40 #include <fcntl.h>
41 
42 #define DEVICE    "/dev/iscsi0"
43 
44 /* -------------------------------------------------------------------------- */
45 
46 list_head_t list[NUM_DAEMON_LISTS];	/* the lists this daemon keeps */
47 
48 #ifndef ISCSI_NOTHREAD
49 pthread_mutex_t sesslist_lock;	/* session list lock */
50 pthread_t event_thread;			/* event thread handle */
51 #endif
52 
53 int driver = -1;				/* the driver's file desc */
54 int client_sock;				/* the client communication socket */
55 
56 #ifdef ISCSI_DEBUG
57 int debug_level = ISCSI_DEBUG;	/* How much info to display */
58 #endif
59 
60 /*
61    To avoid memory fragmentation (and speed things up a bit), we use the
62    static bufs unless the request or response exceeds the buffer size
63    (which it normally shouldn't, assuming we don't have thousands
64    of list entries).
65 */
66 static uint8_t req_buf[REQ_BUFFER_SIZE];	/* default buffer for requests */
67 static uint8_t rsp_buf[RSP_BUFFER_SIZE];	/* default buffer for responses */
68 
69 /* -------------------------------------------------------------------------- */
70 
71 
72 /*
73  * create_node_name:
74  *    Create and set default node name.
75  *
76  *    Returns 0 on success, else an error code.
77  */
78 
79 static int
80 create_node_name(void)
81 {
82 	iscsi_set_node_name_parameters_t snp;
83 	uint32_t hid = 0;
84 	size_t siz;
85 	int mib[2];
86 	unsigned char *s;
87 
88 	(void) memset(&snp, 0x0, sizeof(snp));
89 	mib[0] = CTL_KERN;
90 	mib[1] = KERN_HOSTID;
91 	siz = sizeof(hid);
92 	sysctl(mib, 2, &hid, &siz, NULL, 0);
93 	mib[1] = KERN_HOSTNAME;
94 	siz = ISCSI_STRING_LENGTH - 45;
95 	sysctl(mib, 2, snp.InitiatorAlias, &siz, NULL, 0);
96 
97 	DEB(1, ("Host Name: <%s>, Host ID: %u\n", snp.InitiatorAlias, hid));
98 	if (!snp.InitiatorAlias[0]) {
99 		printf("Warning: iSCSI Node Name not set (No Host Name)!\n");
100 		return ISCSID_STATUS_NO_INITIATOR_NAME;
101 	}
102 	for (s = snp.InitiatorAlias; *s; s++)
103 		if (!isalnum((unsigned char) *s) && *s != '-' && *s != '.' && *s != ':')
104 			*s = '-';
105 	snprintf((char *)snp.InitiatorName, sizeof(snp.InitiatorName),
106 		"iqn.1994-04.org.netbsd:iscsi.%s:%u", snp.InitiatorAlias, hid);
107 
108 	ioctl(driver, ISCSI_SET_NODE_NAME, &snp);
109 	return snp.status;
110 }
111 
112 
113 /*
114  * init_daemon:
115  *    Open driver, create communication socket.
116  *
117  *    Returns:    <0 on error
118  */
119 
120 static int
121 init_daemon(void)
122 {
123 	int sock, i;
124 	struct sockaddr_un name;
125 	iscsid_request_t req;
126 
127 	if ((driver = open(DEVICE, O_RDONLY)) < 0) {
128 		perror("opening " DEVICE);
129 #ifndef ISCSI_DEBUG		/* DEBUG ONLY: Allow daemon to operate w/o driver */
130 		return -1;
131 #endif
132 	}
133 
134 	sock = socket(AF_UNIX, SOCK_DGRAM, 0);
135 	if (sock < 0) {
136 		perror("opening datagram socket");
137 		return -1;
138 	}
139 
140 	name.sun_family = AF_UNIX;
141 	strlcpy(name.sun_path, ISCSID_SOCK_NAME, sizeof(name.sun_path));
142 
143 	req.request = ISCSID_DAEMON_TEST;
144 	req.parameter_length = 0;
145 
146 	i = sendto(sock, &req, sizeof(req), 0, (struct sockaddr *)(void *)&name,
147 				(socklen_t)sizeof(struct sockaddr_un));
148 	if (i == sizeof(req)) {
149 		printf("Daemon already loaded!\n");
150 		close(sock);
151 		return -1;
152 	}
153 
154 	unlink(ISCSID_SOCK_NAME);
155 	if (bind(sock, (struct sockaddr *)(void *)&name, (socklen_t)sizeof(struct sockaddr_un))) {
156 		perror("binding name to socket");
157 		return -1;
158 	}
159 
160 	for (i = 0; i < NUM_DAEMON_LISTS; i++) {
161 		TAILQ_INIT(&list[i].list);
162 		list[i].num_entries = 0;
163 	}
164 
165 #ifndef ISCSI_NOTHREAD
166 	if ((i = pthread_mutex_init(&sesslist_lock, NULL)) != 0) {
167 		printf("Mutex init failed (%d)\n", i);
168 		close(sock);
169 		return -1;
170 	}
171 #endif
172 
173 	if (!register_event_handler()) {
174 		printf("Couldn't register event handler\n");
175 		close(sock);
176 		unlink(ISCSID_SOCK_NAME);
177 #ifndef ISCSI_NOTHREAD
178 		pthread_mutex_destroy(&sesslist_lock);
179 #endif
180 		return -1;
181 	}
182 
183 	create_node_name();
184 
185 	return sock;
186 }
187 
188 
189 /*
190  * make_rsp:
191  *    Allocate a response buffer if the static buffer is insufficient, set
192  *    the response parameter length.
193  *
194  *    Parameter:
195  *          len         Response parameter size (not counting header)
196  *          prsp        Pointer to address of response buffer
197  *          prsp_temp   Will be set to TRUE if buffer was allocated, FALSE
198  *                      for static buffer.
199  *
200  *    Returns:    Pointer to response buffer, NULL if allocation failed.
201  */
202 
203 iscsid_response_t *
204 make_rsp(size_t len, iscsid_response_t ** prsp, int *prsp_temp)
205 {
206 	iscsid_response_t *rsp;
207 
208 	if ((len + sizeof(iscsid_response_t)) > RSP_BUFFER_SIZE) {
209 		if ((rsp = calloc(1, len)) == NULL) {
210 			(*prsp)->status = ISCSID_STATUS_NO_RESOURCES;
211 			return NULL;
212 		}
213 		*prsp_temp = TRUE;
214 		*prsp = rsp;
215 	} else
216 		rsp = *prsp;
217 
218 	memset (rsp, 0, len + sizeof(iscsid_response_t));
219 	rsp->parameter_length = (uint32_t)len;
220 	return rsp;
221 }
222 
223 
224 /*
225  * process_message:
226  *    minimal parameter check and dispatch for the daemon functions.
227  *
228  *    Parameter:
229  *          req         The request
230  *          prsp        Pointer to address of response buffer
231  *          prsp_temp   Will be set to TRUE if buffer was allocated, FALSE
232  *                      for static buffer.
233  */
234 
235 static void
236 process_message(iscsid_request_t *req, iscsid_response_t **prsp, int *prsp_temp)
237 {
238 	iscsid_response_t *rsp;
239 	void *p = req->parameter;
240 
241 	*prsp_temp = FALSE;
242 	*prsp = rsp = (iscsid_response_t *)(void *)rsp_buf;
243 	rsp->parameter_length = 0;
244 	rsp->status = ISCSID_STATUS_SUCCESS;
245 
246 	switch (req->request) {
247 	case ISCSID_ADD_TARGET:
248 		if (req->parameter_length < sizeof(iscsid_add_target_req_t)) {
249 			rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
250 			break;
251 		}
252 		add_target((iscsid_add_target_req_t *)p, prsp, prsp_temp);
253 		break;
254 
255 	case ISCSID_ADD_PORTAL:
256 		if (req->parameter_length != sizeof(iscsid_add_portal_req_t)) {
257 			rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
258 			break;
259 		}
260 		add_portal((iscsid_add_portal_req_t *)p, prsp, prsp_temp);
261 		break;
262 
263 	case ISCSID_SET_TARGET_OPTIONS:
264 		if (req->parameter_length != sizeof(iscsid_get_set_target_options_t)) {
265 			rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
266 			break;
267 		}
268 		rsp->status = set_target_options((iscsid_get_set_target_options_t *)p);
269 		break;
270 
271 	case ISCSID_GET_TARGET_OPTIONS:
272 		if (req->parameter_length != sizeof(iscsid_sym_id_t)) {
273 			rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
274 			break;
275 		}
276 		rsp->status = ISCSID_STATUS_NOTIMPL;
277 		break;
278 
279 	case ISCSID_SET_TARGET_AUTHENTICATION:
280 		if (req->parameter_length !=
281 			sizeof(iscsid_set_target_authentication_req_t)) {
282 			rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
283 			break;
284 		}
285 		rsp->status = set_target_auth((iscsid_set_target_authentication_req_t *)p);
286 		break;
287 
288 	case ISCSID_SLP_FIND_TARGETS:
289 		rsp->status = ISCSID_STATUS_NOTIMPL;
290 		break;
291 
292 	case ISCSID_REFRESH_TARGETS:
293 		if (req->parameter_length < sizeof(iscsid_refresh_req_t)) {
294 			rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
295 			break;
296 		}
297 		rsp->status = refresh_targets((iscsid_refresh_req_t *)p);
298 		break;
299 
300 	case ISCSID_REMOVE_TARGET:
301 		if (req->parameter_length != sizeof(iscsid_list_id_t)) {
302 			rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
303 			break;
304 		}
305 		rsp->status = remove_target((iscsid_list_id_t *)p);
306 		break;
307 
308 	case ISCSID_SEARCH_LIST:
309 		if (req->parameter_length != sizeof(iscsid_search_list_req_t)) {
310 			rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
311 			break;
312 		}
313 		search_list((iscsid_search_list_req_t *)p, prsp, prsp_temp);
314 		break;
315 
316 	case ISCSID_GET_LIST:
317 		if (req->parameter_length != sizeof(iscsid_get_list_req_t)) {
318 			rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
319 			break;
320 		}
321 		get_list((iscsid_get_list_req_t *)p, prsp, prsp_temp);
322 		break;
323 
324 	case ISCSID_GET_TARGET_INFO:
325 		if (req->parameter_length != sizeof(iscsid_list_id_t)) {
326 			rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
327 			break;
328 		}
329 		get_target_info((iscsid_list_id_t *)p, prsp, prsp_temp);
330 		break;
331 
332 	case ISCSID_GET_PORTAL_INFO:
333 		if (req->parameter_length != sizeof(iscsid_list_id_t)) {
334 			rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
335 			break;
336 		}
337 		get_portal_info((iscsid_list_id_t *)p, prsp, prsp_temp);
338 		break;
339 
340 #ifndef ISCSI_MINIMAL
341 	case ISCSID_ADD_ISNS_SERVER:
342 		if (req->parameter_length != sizeof(iscsid_add_isns_server_req_t)) {
343 			rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
344 			break;
345 		}
346 		add_isns_server((iscsid_add_isns_server_req_t *)p,
347 						prsp, prsp_temp);
348 		break;
349 
350 	case ISCSID_GET_ISNS_SERVER:
351 		if (req->parameter_length != sizeof(iscsid_sym_id_t)) {
352 			rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
353 			break;
354 		}
355 		get_isns_server((iscsid_sym_id_t *)p, prsp, prsp_temp);
356 		break;
357 
358 	case ISCSID_SLP_FIND_ISNS_SERVERS:
359 		rsp->status = ISCSID_STATUS_NOTIMPL;
360 		break;
361 
362 	case ISCSID_REMOVE_ISNS_SERVER:
363 		if (req->parameter_length != sizeof(iscsid_sym_id_t)) {
364 			rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
365 			break;
366 		}
367 		rsp->status = remove_isns_server((iscsid_sym_id_t *)p);
368 		break;
369 #endif
370 
371 	case ISCSID_ADD_INITIATOR_PORTAL:
372 		if (req->parameter_length != sizeof(iscsid_add_initiator_req_t)) {
373 			rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
374 			break;
375 		}
376 		add_initiator_portal((iscsid_add_initiator_req_t *)p,
377 							prsp, prsp_temp);
378 		break;
379 
380 	case ISCSID_GET_INITIATOR_PORTAL:
381 		if (req->parameter_length != sizeof(iscsid_sym_id_t)) {
382 			rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
383 			break;
384 		}
385 		get_initiator_portal((iscsid_sym_id_t *)p, prsp, prsp_temp);
386 		break;
387 
388 	case ISCSID_REMOVE_INITIATOR_PORTAL:
389 		if (req->parameter_length != sizeof(iscsid_sym_id_t)) {
390 			rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
391 			break;
392 		}
393 		rsp->status = remove_initiator_portal((iscsid_sym_id_t *)p);
394 		break;
395 
396 	case ISCSID_LOGIN:
397 		if (req->parameter_length != sizeof(iscsid_login_req_t)) {
398 			rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
399 			break;
400 		}
401 		login((iscsid_login_req_t *)p, rsp);
402 		break;
403 
404 	case ISCSID_ADD_CONNECTION:
405 		if (req->parameter_length != sizeof(iscsid_login_req_t)) {
406 			rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
407 			break;
408 		}
409 		add_connection((iscsid_login_req_t *)p, rsp);
410 		break;
411 
412 	case ISCSID_LOGOUT:
413 		if (req->parameter_length != sizeof(iscsid_sym_id_t)) {
414 			rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
415 			break;
416 		}
417 		rsp->status = logout((iscsid_sym_id_t *)p);
418 		break;
419 
420 	case ISCSID_REMOVE_CONNECTION:
421 		if (req->parameter_length != sizeof(iscsid_remove_connection_req_t)) {
422 			rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
423 			break;
424 		}
425 		rsp->status = remove_connection((iscsid_remove_connection_req_t *)p);
426 		break;
427 
428 	case ISCSID_GET_SESSION_LIST:
429 		get_session_list(prsp, prsp_temp);
430 		break;
431 
432 	case ISCSID_GET_CONNECTION_LIST:
433 		if (req->parameter_length != sizeof(iscsid_sym_id_t)) {
434 			rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
435 			break;
436 		}
437 		get_connection_list((iscsid_sym_id_t *)p, prsp, prsp_temp);
438 		break;
439 
440 	case ISCSID_GET_CONNECTION_INFO:
441 		if (req->parameter_length != sizeof(iscsid_get_connection_info_req_t)) {
442 			rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
443 			break;
444 		}
445 		get_connection_info((iscsid_get_connection_info_req_t *)p,
446 							prsp, prsp_temp);
447 		break;
448 
449 	case ISCSID_SET_NODE_NAME:
450 		if (req->parameter_length != sizeof(iscsid_set_node_name_req_t)) {
451 			rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
452 			break;
453 		}
454 		rsp->status = set_node_name((iscsid_set_node_name_req_t *)p);
455 		break;
456 
457 	case ISCSID_GET_VERSION:
458 		get_version(prsp, prsp_temp);
459 		break;
460 
461 	default:
462 		rsp->status = ISCSID_STATUS_INVALID_REQUEST;
463 		break;
464 	}
465 }
466 
467 
468 /*
469  * exit_daemon:
470  *    Deregister the event handler, deregister isns servers, then exit program.
471  */
472 
473 void
474 exit_daemon(void)
475 {
476 #ifndef ISCSI_NOTHREAD
477 	LOCK_SESSIONS;
478 #endif
479 	deregister_event_handler();
480 
481 #ifndef ISCSI_MINIMAL
482 	dereg_all_isns_servers();
483 #endif
484 
485 	printf("iSCSI Daemon Exits\n");
486 	exit(0);
487 }
488 
489 
490 /*
491  * main:
492  *    init, go daemon, then loop reading requests, processing them,
493  *    and sending responses.
494  *    Stops on receiving a terminate message (no response to that one is sent),
495  *    or when an error occurs reading or writing the socket.
496  *
497  *    Parameter:  argc, argv currently ignored.
498  */
499 
500 int
501 /*ARGSUSED*/
502 main(int argc, char **argv)
503 {
504 	int req_temp, rsp_temp;
505 	ssize_t ret;
506 	size_t len;
507 	struct sockaddr_un from;
508 	socklen_t fromlen;
509 	iscsid_request_t *req;
510 	iscsid_response_t *rsp;
511 #ifdef ISCSI_NOTHREAD
512 	struct timeval seltout = { 2, 0 };	/* 2 second poll interval */
513 #endif
514 
515 	client_sock = init_daemon();
516 	if (client_sock < 0)
517 		exit(1);
518 
519 	printf("iSCSI Daemon loaded\n");
520 
521 	daemon(0, 1);
522 
523 #ifndef ISCSI_NOTHREAD
524 	ret = pthread_create(&event_thread, NULL, event_handler, NULL);
525 	if (ret) {
526 		printf("Thread creation failed (%zd)\n", ret);
527 		close(client_sock);
528 		unlink(ISCSID_SOCK_NAME);
529 		deregister_event_handler();
530 		pthread_mutex_destroy(&sesslist_lock);
531 		return -1;
532 	}
533 #else
534 	setsockopt(client_sock, SOL_SOCKET, SO_RCVTIMEO, &seltout, sizeof(seltout));
535 #endif
536 
537     /* ---------------------------------------------------------------------- */
538 
539 	for (;;) {
540 		/* First, get size of request */
541 		req = (iscsid_request_t *)(void *)req_buf;
542 		fromlen = sizeof(from);
543 		len = sizeof(iscsid_request_t);
544 
545 #ifdef ISCSI_NOTHREAD
546 		do {
547 			ret = recvfrom(client_sock, req, len, MSG_PEEK | MSG_WAITALL,
548 							(struct sockaddr *) &from, &fromlen);
549 			if (ret == -1)
550 				event_handler(NULL);
551 		} while (ret == -1 && errno == EAGAIN);
552 #else
553 		ret = recvfrom(client_sock, req, len, MSG_PEEK | MSG_WAITALL,
554 					 (struct sockaddr *)(void *)&from, &fromlen);
555 #endif
556 
557 		if ((size_t)ret != len) {
558 			perror("Receiving from socket");
559 			break;
560 		}
561 		DEB(99, ("Request %d, parlen %d\n",
562 				req->request, req->parameter_length));
563 
564 		len += req->parameter_length;
565 
566 		/* now that we know the size, get the buffer for it */
567 		req_temp = (len > REQ_BUFFER_SIZE);
568 
569 		if (req_temp) {
570 			req = malloc(len);
571 			if (!req) {
572 				printf("Can't alloc %zu bytes\n", len);
573 				break;
574 			}
575 		}
576 		/* read the complete request */
577 		fromlen = sizeof(from);
578 		ret = recvfrom(client_sock, req, len, MSG_WAITALL,
579 						(struct sockaddr *)(void *)&from, &fromlen);
580 		if ((size_t)ret != len) {
581 			DEBOUT(("Error receiving from socket!\n"));
582 			if (req_temp)
583 				free(req);
584 			continue;
585 		}
586 		/* terminate? then go die. */
587 		if (req->request == ISCSID_DAEMON_TERMINATE)
588 			break;
589 
590 		/* No reply required to test message */
591 		if (req->request == ISCSID_DAEMON_TEST) {
592 			if (req_temp)
593 				free(req);
594 			continue;
595 		}
596 		/* no return path? then we can't send a reply, */
597 		/* so don't process the command */
598 		if (!from.sun_path[0]) {
599 			DEBOUT(("No Return Address!\n"));
600 			continue;
601 		}
602 		/* process the request */
603 		process_message(req, &rsp, &rsp_temp);
604 		if (rsp == NULL)
605 			break;
606 
607 		DEB(99, ("Sending reply: status %d, len %d\n",
608 				rsp->status, rsp->parameter_length));
609 
610 		/* send the response */
611 		len = sizeof(iscsid_response_t) + rsp->parameter_length;
612 		ret = sendto(client_sock, rsp, len, 0,
613 					(struct sockaddr *)(void *)&from, fromlen);
614 		if (len != (size_t)ret) {
615 			DEBOUT(("Error sending reply!\n"));
616 		}
617 		/* free temp buffers if we needed them */
618 		if (req_temp)
619 			free(req);
620 		if (rsp_temp)
621 			free(rsp);
622 	}
623 
624 	exit_daemon();
625 
626 	/* we never get here */
627 	return 0;
628 }
629