xref: /titanic_44/usr/src/cmd/iscsid/iscsid.c (revision 95efa359b507db290a2484b8f615822b1e06096f)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <sys/socket.h>
29 #include <locale.h>
30 #include <syslog.h>
31 #include <netdb.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <fcntl.h>
35 #include <unistd.h>
36 #include <stdio.h>
37 #include <errno.h>
38 #include <door.h>
39 #include <meta.h>
40 #include <libsysevent.h>
41 #include <wait.h>
42 #include <semaphore.h>
43 #include <libscf.h>
44 
45 #include <sys/scsi/adapters/iscsi_door.h>
46 #include <sys/scsi/adapters/iscsi_if.h>
47 
48 /*
49  * Local Defines
50  * -------------
51  */
52 #define	ISCSI_DOOR_DAEMON_SYSLOG_PP		"iscsid"
53 #define	ISCSI_DISCOVERY_POLL_DELAY1		1	/* Seconds */
54 #define	ISCSI_DISCOVERY_POLL_DELAY2		60	/* Seconds */
55 
56 #if !defined(SMF_EXIT_ERR_OTHER)
57 #define	SMF_EXIT_ERR_OTHER	-1
58 #endif
59 
60 /*
61  * Global Variables related to the synchronization of the child process
62  * --------------------------------------------------------------------
63  */
64 static	pid_t		iscsi_child_pid;
65 static	sem_t		iscsi_child_sem;
66 static	int		iscsi_child_door_handle;
67 static	int		iscsi_child_smf_exit_code;
68 
69 /*
70  * Global Variables related to the door accessed by the kernel
71  * -----------------------------------------------------------
72  */
73 static	int		iscsi_dev_handle;
74 static	int		iscsi_kernel_door_handle;
75 
76 /*
77  * Prototypes of Functions the body of which is defined farther down
78  * in this file.
79  * -----------------------------------------------------------------
80  */
81 static	void		call_child_door(int value);
82 static	void		sigchld_handler(int sig);
83 static	boolean_t	discovery_event_wait(int did);
84 
85 static
86 void
87 iscsi_child_door(
88 	void			*cookie,
89 	char			*args,
90 	size_t			alen,
91 	door_desc_t		*ddp,
92 	uint_t			ndid
93 );
94 
95 static
96 void
97 iscsi_kernel_door(
98 	void			*cookie,
99 	char			*args,
100 	size_t			alen,
101 	door_desc_t		*ddp,
102 	uint_t			ndid
103 );
104 
105 static
106 iscsi_door_cnf_t *
107 _getipnodebyname_req(
108 	getipnodebyname_req_t	*req,
109 	int			req_len,
110 	size_t			*pcnf_len
111 );
112 
113 /*
114  * main -- Entry point of the iSCSI door server daemon
115  *
116  * This function forks, waits for the child process feedback and exits.
117  */
118 /* ARGSUSED */
119 int
120 main(
121 	int	argc,
122 	char	*argv[]
123 )
124 {
125 	int		i;
126 
127 	/*
128 	 * Get the locale set up before calling any other routines
129 	 * with messages to ouput.
130 	 */
131 	(void) setlocale(LC_ALL, "");
132 	openlog("ISCSI_DOOR_DAEMON_SYSLOG_PP", LOG_PID, LOG_DAEMON);
133 
134 	/* The child semaphore is created. */
135 	if (sem_init(&iscsi_child_sem, 0, 0) == -1) {
136 		exit(SMF_EXIT_ERR_OTHER);
137 	}
138 
139 	/* The door for the child is created. */
140 	iscsi_child_door_handle = door_create(iscsi_child_door, NULL, 0);
141 	if (iscsi_child_door_handle == -1) {
142 		(void) sem_destroy(&iscsi_child_sem);
143 		exit(SMF_EXIT_ERR_OTHER);
144 	}
145 
146 	/* A signal handler is set for SIGCHLD. */
147 	(void) signal(SIGCHLD, sigchld_handler);
148 
149 	/*
150 	 * Here begins the daemonizing code
151 	 * --------------------------------
152 	 */
153 	iscsi_child_pid = fork();
154 	if (iscsi_child_pid < 0) {
155 		/* The fork failed. */
156 		syslog(LOG_DAEMON | LOG_ERR, gettext("Cannot fork"));
157 		(void) sem_destroy(&iscsi_child_sem);
158 		exit(SMF_EXIT_ERR_OTHER);
159 	}
160 
161 	if (iscsi_child_pid) {
162 		/*
163 		 * The parent exits after the child has provided feedback. This
164 		 * waiting phase is to meet one of greenline's requirements.
165 		 * We shouldn't return till we are sure the service is ready to
166 		 * be provided.
167 		 */
168 		(void) sem_wait(&iscsi_child_sem);
169 		(void) sem_destroy(&iscsi_child_sem);
170 		exit(iscsi_child_smf_exit_code);
171 	}
172 
173 	/*
174 	 * stdout and stderr are redirected to "/dev/null".
175 	 */
176 	i = open("/dev/null", O_RDWR);
177 	(void) dup2(i, 1);
178 	(void) dup2(i, 2);
179 
180 	/*
181 	 * Here ends the daemonizing code
182 	 * ------------------------------
183 	 */
184 
185 	/*
186 	 * Block out the usual signals so we don't get killed unintentionally.
187 	 */
188 	(void) signal(SIGHUP, SIG_IGN);
189 	(void) signal(SIGINT, SIG_IGN);
190 	(void) signal(SIGQUIT, SIG_IGN);
191 
192 	/* setup the door handle */
193 	iscsi_kernel_door_handle = door_create(iscsi_kernel_door, NULL, 0);
194 	if (iscsi_kernel_door_handle == -1) {
195 		perror(gettext("door_create failed"));
196 		syslog(LOG_DAEMON | LOG_ERR, gettext("door_create failed"));
197 		exit(SMF_EXIT_ERR_OTHER);
198 	}
199 
200 	/*
201 	 * The iSCSI driver is opened.
202 	 */
203 	iscsi_dev_handle = open(ISCSI_DRIVER_DEVCTL, O_RDWR);
204 	if (iscsi_dev_handle == -1) {
205 		/* The driver couldn't be opened. */
206 		perror(gettext("iscsi device open failed"));
207 		exit(SMF_EXIT_ERR_OTHER);
208 	}
209 
210 	if (ioctl(
211 	    iscsi_dev_handle,
212 	    ISCSI_DOOR_HANDLE_SET,
213 	    &iscsi_kernel_door_handle) == -1) {
214 		(void) close(iscsi_dev_handle);
215 		perror(gettext("ioctl: set door handle"));
216 		exit(SMF_EXIT_ERR_OTHER);
217 	}
218 
219 	/* We have to wait for the discovery process to finish. */
220 	(void) discovery_event_wait(iscsi_dev_handle);
221 
222 	/* We don't need to keep the device opened. */
223 	(void) close(iscsi_dev_handle);
224 
225 	/* We let know the parent that everything is ok. */
226 	call_child_door(SMF_EXIT_OK);
227 	for (;;) {
228 		(void) pause();
229 	}
230 }
231 
232 /*
233  * sigchld_handler -- SIGCHLD Handler
234  *
235  */
236 /* ARGSUSED */
237 static
238 void
239 sigchld_handler(
240 	int	sig
241 )
242 {
243 	int	status;
244 	pid_t	ret_pid;
245 
246 	/* This is the default code. */
247 	iscsi_child_smf_exit_code = SMF_EXIT_ERR_OTHER;
248 
249 	ret_pid = waitpid(iscsi_child_pid, &status, WNOHANG);
250 
251 	if (ret_pid == iscsi_child_pid) {
252 		if (WIFEXITED(status)) {
253 			iscsi_child_smf_exit_code = WEXITSTATUS(status);
254 		}
255 	}
256 	(void) sem_post(&iscsi_child_sem);
257 }
258 
259 /*
260  * iscsi_child_door -- Child process door entry point
261  *
262  * This function is executed when a driver calls door_ki_upcall().
263  */
264 /* ARGSUSED */
265 static
266 void
267 iscsi_child_door(
268 	void		*cookie,
269 	char		*args,
270 	size_t		alen,
271 	door_desc_t	*ddp,
272 	uint_t		ndid
273 )
274 {
275 	int		*ptr = (int *)args;
276 
277 	iscsi_child_smf_exit_code = SMF_EXIT_ERR_OTHER;
278 
279 	if (alen >= sizeof (iscsi_child_smf_exit_code)) {
280 		iscsi_child_smf_exit_code = *ptr;
281 	}
282 	(void) sem_post(&iscsi_child_sem);
283 	(void) door_return(NULL, 0, NULL, 0);
284 }
285 
286 /*
287  * iscsi_kernel_door -- Kernel door entry point
288  *
289  * This function is executed when a driver calls door_ki_upcall().
290  */
291 /* ARGSUSED */
292 static
293 void
294 iscsi_kernel_door(
295 	void		*cookie,
296 	char		*args,
297 	size_t		alen,
298 	door_desc_t	*ddp,
299 	uint_t		ndid
300 )
301 {
302 	iscsi_door_msg_hdr_t	err_ind;
303 	iscsi_door_req_t	*req;
304 	iscsi_door_cnf_t	*cnf;
305 	size_t			cnf_len;
306 	char			*err_txt;
307 	int			err_code;
308 
309 	/* Local variables pre-initialization */
310 	err_ind.signature = ISCSI_DOOR_REQ_SIGNATURE;
311 	err_ind.version	  = ISCSI_DOOR_REQ_VERSION_1;
312 	err_ind.opcode	  = ISCSI_DOOR_ERROR_IND;
313 
314 	req = (iscsi_door_req_t *)args;
315 	cnf = (iscsi_door_cnf_t *)&err_ind;
316 	cnf_len = sizeof (err_ind);
317 
318 	/*
319 	 * The validity of the request is checked before going any farther.
320 	 */
321 	if (req == NULL) {
322 		/*
323 		 * A request has to be passed.
324 		 */
325 		err_ind.status = ISCSI_DOOR_STATUS_REQ_INVALID;
326 	} else if (alen < sizeof (iscsi_door_msg_hdr_t)) {
327 		/*
328 		 * The buffer containing the request must be at least as big
329 		 * as message header.
330 		 */
331 		err_ind.status = ISCSI_DOOR_STATUS_REQ_LENGTH;
332 	} else if (req->hdr.signature != ISCSI_DOOR_REQ_SIGNATURE) {
333 		/*
334 		 * The request must be correctly signed.
335 		 */
336 		err_ind.status = ISCSI_DOOR_STATUS_REQ_INVALID;
337 	} else if (req->hdr.version != ISCSI_DOOR_REQ_VERSION_1) {
338 		/*
339 		 * The version of the request must be supported by the server.
340 		 */
341 		err_ind.status = ISCSI_DOOR_STATUS_REQ_VERSION;
342 	} else {
343 		/*
344 		 * The request is treated according to the opcode.
345 		 */
346 		switch (req->hdr.opcode) {
347 
348 		case ISCSI_DOOR_GETIPNODEBYNAME_REQ:
349 			cnf = _getipnodebyname_req(
350 			    &req->ginbn_req,
351 			    alen,
352 			    &cnf_len);
353 			break;
354 		default:
355 			err_ind.status = ISCSI_DOOR_STATUS_REQ_INVALID;
356 			break;
357 		}
358 	}
359 	err_code = door_return((char *)cnf, cnf_len, NULL, 0);
360 
361 	switch (err_code) {
362 	case E2BIG:
363 		err_txt = "E2BIG";
364 		break;
365 	case EFAULT:
366 		err_txt = "EFAULT";
367 		break;
368 	case EINVAL:
369 		err_txt = "EINVAL";
370 		break;
371 	case EMFILE:
372 		err_txt = "EMFILE";
373 		break;
374 	default:
375 		err_txt = "?";
376 		break;
377 	}
378 	(void) fprintf(stderr, "door_return error(%s,%d)", err_txt, err_code);
379 	syslog(
380 	    LOG_DAEMON | LOG_ERR,
381 	    gettext("!door_return error(%s,%d)"),
382 	    err_txt,
383 	    err_code);
384 }
385 
386 /*
387  * _getipnodebyname_req
388  *
389  * This function executes the request ISCSI_DOOR_GETIPNODEBYNAME_REQ.  It
390  * calls getipnodebyname() but doesn't return all the information.  The
391  * confirmation structure only contains one IP address of the list returned
392  * by getipnodebyname().
393  */
394 static
395 iscsi_door_cnf_t *
396 _getipnodebyname_req(
397 	getipnodebyname_req_t	*req,
398 	int			req_len,
399 	size_t			*pcnf_len
400 ) {
401 	getipnodebyname_cnf_t	*cnf = (getipnodebyname_cnf_t *)req;
402 	size_t			cnf_len;
403 	struct hostent		*hptr;
404 	char			*name;
405 
406 	/* The opcode is changed immediately. */
407 	cnf->hdr.opcode = ISCSI_DOOR_GETIPNODEBYNAME_CNF;
408 
409 	/* The size of the request is checked against the minimum required. */
410 	if (req_len < sizeof (getipnodebyname_cnf_t)) {
411 		cnf->hdr.status = ISCSI_DOOR_STATUS_REQ_FORMAT;
412 		*pcnf_len = req_len;
413 		return ((iscsi_door_cnf_t *)cnf);
414 	}
415 
416 	name = (char *)req + req->name_offset;
417 
418 	/*
419 	 * The pointer to the name has to stay inside the request but
420 	 * after the header.
421 	 */
422 	if ((name < ((char *)req + sizeof (getipnodebyname_req_t))) ||
423 	    ((name + req->name_length) > ((char *)req + req_len))) {
424 		cnf->hdr.status = ISCSI_DOOR_STATUS_REQ_FORMAT;
425 		*pcnf_len = req_len;
426 		return ((iscsi_door_cnf_t *)cnf);
427 	}
428 
429 	/* The library function is called. */
430 	hptr = getipnodebyname(
431 			name,
432 			(int)req->af,
433 			(int)req->flags,
434 			(int *)&cnf->error_num);
435 
436 	if (hptr) {
437 		/*
438 		 * The call was successful. Now starts the painful work of
439 		 * parsing the data.  However, for version 1 we will only
440 		 * return the first address.
441 		 */
442 		cnf_len = sizeof (getipnodebyname_cnf_t);
443 		cnf->h_size_needed = sizeof (getipnodebyname_cnf_t);
444 		cnf->h_alias_list_length = 0;
445 		cnf->h_alias_list_offset = 0;
446 		cnf->h_name_len = 0;
447 		cnf->h_name_offset = 0;
448 
449 		cnf->h_addrlen = (uint32_t)hptr->h_length;
450 		cnf->h_addrtype = (uint32_t)hptr->h_addrtype;
451 		cnf->h_addr_list_offset = sizeof (getipnodebyname_cnf_t);
452 
453 		if (*hptr->h_addr_list != NULL) {
454 			(void) memcpy(
455 				((char *)cnf + sizeof (getipnodebyname_cnf_t)),
456 				*hptr->h_addr_list,
457 				hptr->h_length);
458 			cnf->h_addr_list_length = 1;
459 			cnf->h_size_needed += cnf->h_addrlen;
460 			cnf_len += hptr->h_length;
461 		} else {
462 			cnf->h_addr_list_length = 0;
463 			cnf->h_size_needed += hptr->h_length;
464 		}
465 		*pcnf_len = cnf_len;
466 		cnf->hdr.status = ISCSI_DOOR_STATUS_SUCCESS;
467 		freehostent(hptr);
468 	} else {
469 		cnf->hdr.status = ISCSI_DOOR_STATUS_SUCCESS;
470 		cnf->h_addrlen = 0;
471 		cnf->h_addrtype = 0;
472 		cnf->h_addr_list_offset = sizeof (getipnodebyname_cnf_t);
473 		cnf->h_addr_list_length = 0;
474 		cnf->h_name_offset = sizeof (getipnodebyname_cnf_t);
475 		cnf->h_name_len = 0;
476 		cnf->h_alias_list_offset = sizeof (getipnodebyname_cnf_t);
477 		cnf->h_alias_list_length = 0;
478 		cnf->h_size_needed = sizeof (getipnodebyname_cnf_t);
479 		*pcnf_len = sizeof (getipnodebyname_cnf_t);
480 	}
481 	return ((iscsi_door_cnf_t *)cnf);
482 }
483 
484 /*
485  * call_child_door -- This function calls the child door with the value
486  *		      provided by the caller.
487  *
488  */
489 static
490 void
491 call_child_door(
492 	int		value
493 )
494 {
495 	door_arg_t	door_arg;
496 
497 	(void) memset(&door_arg, 0, sizeof (door_arg));
498 	door_arg.data_ptr = (char *)&value;
499 	door_arg.data_size = sizeof (value);
500 	(void) door_call(iscsi_child_door_handle, &door_arg);
501 }
502 
503 /*
504  * get_luns_count --
505  */
506 static
507 uint32_t
508 get_luns_count(
509 	int		did
510 )
511 {
512 	iscsi_lun_list_t	*lun_list;
513 	iscsi_lun_list_t	*tmp;
514 	size_t			len;
515 	uint32_t		lun_count;
516 
517 	lun_list = (iscsi_lun_list_t *)malloc(sizeof (*lun_list));
518 
519 	(void) memset(lun_list, 0, sizeof (*lun_list));
520 	lun_list->ll_vers = ISCSI_INTERFACE_VERSION;
521 	lun_list->ll_in_cnt = 1;
522 	lun_list->ll_all_tgts = B_TRUE;
523 
524 	for (;;) {
525 
526 		if (ioctl(
527 		    did,
528 		    ISCSI_LUN_OID_LIST_GET,
529 		    lun_list) == -1) {
530 			free(lun_list);
531 			/* The Ioctl didn't go well. */
532 			return (0);
533 		}
534 		if (lun_list->ll_in_cnt >= lun_list->ll_out_cnt) {
535 			/* We got it all. */
536 			break;
537 		}
538 		/*
539 		 * We didn't get all the targets. Let's build a new Ioctl with
540 		 * a new size.
541 		 */
542 		tmp  = lun_list;
543 		len  = tmp->ll_out_cnt * sizeof (tmp->ll_luns);
544 		len += sizeof (*tmp) - sizeof (tmp->ll_luns);
545 		lun_list = (iscsi_lun_list_t *)malloc(len);
546 		if (lun_list == NULL) {
547 			/* No resources. */
548 			free(tmp);
549 			return (0);
550 		}
551 		(void) memset(lun_list, 0, len);
552 		lun_list->ll_vers = ISCSI_INTERFACE_VERSION;
553 		lun_list->ll_in_cnt = tmp->ll_out_cnt;
554 		lun_list->ll_all_tgts = B_TRUE;
555 		free(tmp);
556 	}
557 	lun_count = lun_list->ll_out_cnt;
558 	free(lun_list);
559 	return (lun_count);
560 }
561 
562 /*
563  * discovery_event_wait -- Waits for the discovery process to finish.
564  *
565  */
566 static
567 boolean_t
568 discovery_event_wait(
569 	int		did
570 )
571 {
572 	boolean_t		rc;
573 	uint32_t		lun_count;
574 	uint32_t		lun_timer;
575 	uint32_t		tmp;
576 	iSCSIDiscoveryMethod_t  discovery_flags;
577 	iSCSIDiscoveryMethod_t  discovery_all;
578 
579 	rc = B_FALSE;
580 	lun_count = 0;
581 	lun_timer = 0;
582 	discovery_flags = 0;
583 	discovery_all = iSCSIDiscoveryMethodStatic |
584 	    iSCSIDiscoveryMethodSLP |
585 	    iSCSIDiscoveryMethodISNS |
586 	    iSCSIDiscoveryMethodSendTargets;
587 
588 	for (;;) {
589 
590 		/* The status discovery flags are read. */
591 		if (ioctl(
592 		    did,
593 		    ISCSI_DISCOVERY_EVENTS,
594 		    &discovery_flags) == -1) {
595 			/* IO problem */
596 			break;
597 		}
598 
599 		if (discovery_flags == discovery_all) {
600 			/* Discovery over */
601 			rc = B_TRUE;
602 			break;
603 		}
604 
605 		if (lun_timer >= ISCSI_DISCOVERY_POLL_DELAY2) {
606 			/* Let's check if the driver is making progress. */
607 			tmp = get_luns_count(did);
608 			if (tmp <= lun_count) {
609 				/* No progress */
610 				break;
611 			}
612 			lun_count = tmp;
613 			lun_timer = 0;
614 		}
615 		(void) sleep(ISCSI_DISCOVERY_POLL_DELAY1);
616 		lun_timer += ISCSI_DISCOVERY_POLL_DELAY1;
617 	}
618 	return (rc);
619 }
620