8349 thrd_equal implements the wrong specification
[unleashed.git] / usr / src / cmd / iscsid / iscsid.c
blob463e3cae96719f34a187fb08b280e2bdbe3cddd8
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright 2012 Milan Jurik. All rights reserved.
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <sys/socket.h>
30 #include <signal.h>
31 #include <locale.h>
32 #include <syslog.h>
33 #include <netdb.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <fcntl.h>
37 #include <unistd.h>
38 #include <stdio.h>
39 #include <errno.h>
40 #include <door.h>
41 #include <libsysevent.h>
42 #include <wait.h>
43 #include <semaphore.h>
44 #include <libscf.h>
46 #include <sys/scsi/adapters/iscsi_door.h>
47 #include <sys/scsi/adapters/iscsi_if.h>
50 * Local Defines
51 * -------------
53 #define ISCSI_DOOR_DAEMON_SYSLOG_PP "iscsid"
54 #define ISCSI_DISCOVERY_POLL_DELAY1 1 /* Seconds */
55 #define ISCSI_DISCOVERY_POLL_DELAY2 60 /* Seconds */
56 #define ISCSI_SMF_OFFLINE_DELAY 10 /* Seconds */
57 #define ISCSI_SMF_OFFLINE_MAX_RETRY_TIMES 60
59 #if !defined(SMF_EXIT_ERR_OTHER)
60 #define SMF_EXIT_ERR_OTHER -1
61 #endif
64 * Global Variables related to the synchronization of the child process
65 * --------------------------------------------------------------------
67 static pid_t iscsi_child_pid;
68 static sem_t iscsi_child_sem;
69 static int iscsi_child_door_handle;
70 static int iscsi_child_smf_exit_code;
73 * Global Variables related to the door accessed by the kernel
74 * -----------------------------------------------------------
76 static int iscsi_dev_handle;
77 static int iscsi_kernel_door_handle;
80 * Prototypes of Functions the body of which is defined farther down
81 * in this file.
82 * -----------------------------------------------------------------
84 static void call_child_door(int value);
85 static void sigchld_handler(int sig);
86 static boolean_t discovery_event_wait(int did);
87 static void signone(int, siginfo_t *, void *);
89 static
90 void
91 iscsi_child_door(
92 void *cookie,
93 char *args,
94 size_t alen,
95 door_desc_t *ddp,
96 uint_t ndid
99 static
100 void
101 iscsi_kernel_door(
102 void *cookie,
103 char *args,
104 size_t alen,
105 door_desc_t *ddp,
106 uint_t ndid
109 static
110 iscsi_door_cnf_t *
111 _getipnodebyname_req(
112 getipnodebyname_req_t *req,
113 int req_len,
114 size_t *pcnf_len
118 * main -- Entry point of the iSCSI door server daemon
120 * This function forks, waits for the child process feedback and exits.
122 /* ARGSUSED */
124 main(
125 int argc,
126 char *argv[]
129 int i;
130 int sig;
131 int ret = -1;
132 int retry = 0;
133 sigset_t sigs, allsigs;
134 struct sigaction act;
135 uint32_t rval;
138 * Get the locale set up before calling any other routines
139 * with messages to ouput.
141 (void) setlocale(LC_ALL, "");
142 openlog("ISCSI_DOOR_DAEMON_SYSLOG_PP", LOG_PID, LOG_DAEMON);
144 /* The child semaphore is created. */
145 if (sem_init(&iscsi_child_sem, 0, 0) == -1) {
146 exit(SMF_EXIT_ERR_OTHER);
149 /* The door for the child is created. */
150 iscsi_child_door_handle = door_create(iscsi_child_door, NULL, 0);
151 if (iscsi_child_door_handle == -1) {
152 (void) sem_destroy(&iscsi_child_sem);
153 exit(SMF_EXIT_ERR_OTHER);
156 /* A signal handler is set for SIGCHLD. */
157 (void) signal(SIGCHLD, sigchld_handler);
160 * Here begins the daemonizing code
161 * --------------------------------
163 iscsi_child_pid = fork();
164 if (iscsi_child_pid < 0) {
165 /* The fork failed. */
166 syslog(LOG_DAEMON | LOG_ERR, gettext("Cannot fork"));
167 (void) sem_destroy(&iscsi_child_sem);
168 exit(SMF_EXIT_ERR_OTHER);
171 if (iscsi_child_pid) {
173 * The parent exits after the child has provided feedback. This
174 * waiting phase is to meet one of greenline's requirements.
175 * We shouldn't return till we are sure the service is ready to
176 * be provided.
178 (void) sem_wait(&iscsi_child_sem);
179 (void) sem_destroy(&iscsi_child_sem);
180 exit(iscsi_child_smf_exit_code);
184 * stdout and stderr are redirected to "/dev/null".
186 i = open("/dev/null", O_RDWR);
187 (void) dup2(i, 1);
188 (void) dup2(i, 2);
191 * Here ends the daemonizing code
192 * ------------------------------
196 * Block out all signals
198 (void) sigfillset(&allsigs);
199 (void) pthread_sigmask(SIG_BLOCK, &allsigs, NULL);
201 /* setup the door handle */
202 iscsi_kernel_door_handle = door_create(iscsi_kernel_door, NULL, 0);
203 if (iscsi_kernel_door_handle == -1) {
204 perror(gettext("door_create failed"));
205 syslog(LOG_DAEMON | LOG_ERR, gettext("door_create failed"));
206 exit(SMF_EXIT_ERR_OTHER);
210 * The iSCSI driver is opened.
212 iscsi_dev_handle = open(ISCSI_DRIVER_DEVCTL, O_RDWR);
213 if (iscsi_dev_handle == -1) {
214 /* The driver couldn't be opened. */
215 perror(gettext("iscsi device open failed"));
216 exit(SMF_EXIT_ERR_OTHER);
219 if (ioctl(
220 iscsi_dev_handle,
221 ISCSI_SMF_ONLINE,
222 &iscsi_kernel_door_handle) == -1) {
223 (void) close(iscsi_dev_handle);
224 perror(gettext("ioctl: enable iscsi initiator"));
225 exit(SMF_EXIT_ERR_OTHER);
229 * Keep the dev open, so to keep iscsi module from unloaded.
230 * This is crutial to guarantee the consistency of the
231 * door_handle and service state in kernel.
234 /* We have to wait for the discovery process to finish. */
235 (void) discovery_event_wait(iscsi_dev_handle);
237 /* We let the parent know that everything is ok. */
238 call_child_door(SMF_EXIT_OK);
240 /* now set up signals we care about */
242 (void) sigemptyset(&sigs);
243 (void) sigaddset(&sigs, SIGTERM);
244 (void) sigaddset(&sigs, SIGINT);
245 (void) sigaddset(&sigs, SIGQUIT);
247 /* make sure signals to be enqueued */
248 act.sa_flags = SA_SIGINFO;
249 act.sa_sigaction = signone;
251 (void) sigaction(SIGTERM, &act, NULL);
252 (void) sigaction(SIGINT, &act, NULL);
253 (void) sigaction(SIGQUIT, &act, NULL);
255 /* wait and process signals */
256 for (;;) {
257 sig = sigwait(&sigs);
258 if (sig < 0)
259 continue;
260 switch (sig) {
261 case SIGQUIT:
262 case SIGINT:
263 case SIGTERM:
264 do {
265 ret = ioctl(iscsi_dev_handle,
266 ISCSI_SMF_OFFLINE, &rval);
267 if (ret == -1) {
269 * Keep retrying if unable
270 * to stop
272 (void) sleep(ISCSI_SMF_OFFLINE_DELAY);
273 retry++;
275 } while ((ret == -1) &&
276 (retry < ISCSI_SMF_OFFLINE_MAX_RETRY_TIMES));
277 (void) close(iscsi_dev_handle);
278 if (rval == B_FALSE) {
279 syslog(LOG_DAEMON, gettext("iSCSI initiator"
280 " service exited with sessions left."));
282 return (0);
283 default:
284 break;
290 * sigchld_handler -- SIGCHLD Handler
293 /* ARGSUSED */
294 static
295 void
296 sigchld_handler(
297 int sig
300 int status;
301 pid_t ret_pid;
303 /* This is the default code. */
304 iscsi_child_smf_exit_code = SMF_EXIT_ERR_OTHER;
306 ret_pid = waitpid(iscsi_child_pid, &status, WNOHANG);
308 if (ret_pid == iscsi_child_pid) {
309 if (WIFEXITED(status)) {
310 iscsi_child_smf_exit_code = WEXITSTATUS(status);
313 (void) sem_post(&iscsi_child_sem);
317 * iscsi_child_door -- Child process door entry point
319 * This function is executed when a driver calls door_ki_upcall().
321 /* ARGSUSED */
322 static
323 void
324 iscsi_child_door(
325 void *cookie,
326 char *args,
327 size_t alen,
328 door_desc_t *ddp,
329 uint_t ndid
332 int *ptr = (int *)args;
334 iscsi_child_smf_exit_code = SMF_EXIT_ERR_OTHER;
336 if (alen >= sizeof (iscsi_child_smf_exit_code)) {
337 iscsi_child_smf_exit_code = *ptr;
339 (void) sem_post(&iscsi_child_sem);
340 (void) door_return(NULL, 0, NULL, 0);
344 * iscsi_kernel_door -- Kernel door entry point
346 * This function is executed when a driver calls door_ki_upcall().
348 /* ARGSUSED */
349 static
350 void
351 iscsi_kernel_door(
352 void *cookie,
353 char *args,
354 size_t alen,
355 door_desc_t *ddp,
356 uint_t ndid
359 iscsi_door_msg_hdr_t err_ind;
360 iscsi_door_req_t *req;
361 iscsi_door_cnf_t *cnf;
362 size_t cnf_len;
363 char *err_txt;
364 int err_code;
366 /* Local variables pre-initialization */
367 err_ind.signature = ISCSI_DOOR_REQ_SIGNATURE;
368 err_ind.version = ISCSI_DOOR_REQ_VERSION_1;
369 err_ind.opcode = ISCSI_DOOR_ERROR_IND;
371 req = (iscsi_door_req_t *)args;
372 cnf = (iscsi_door_cnf_t *)&err_ind;
373 cnf_len = sizeof (err_ind);
376 * The validity of the request is checked before going any farther.
378 if (req == NULL) {
380 * A request has to be passed.
382 err_ind.status = ISCSI_DOOR_STATUS_REQ_INVALID;
383 } else if (alen < sizeof (iscsi_door_msg_hdr_t)) {
385 * The buffer containing the request must be at least as big
386 * as message header.
388 err_ind.status = ISCSI_DOOR_STATUS_REQ_LENGTH;
389 } else if (req->hdr.signature != ISCSI_DOOR_REQ_SIGNATURE) {
391 * The request must be correctly signed.
393 err_ind.status = ISCSI_DOOR_STATUS_REQ_INVALID;
394 } else if (req->hdr.version != ISCSI_DOOR_REQ_VERSION_1) {
396 * The version of the request must be supported by the server.
398 err_ind.status = ISCSI_DOOR_STATUS_REQ_VERSION;
399 } else {
401 * The request is treated according to the opcode.
403 switch (req->hdr.opcode) {
405 case ISCSI_DOOR_GETIPNODEBYNAME_REQ:
406 cnf = _getipnodebyname_req(
407 &req->ginbn_req,
408 alen,
409 &cnf_len);
410 break;
411 default:
412 err_ind.status = ISCSI_DOOR_STATUS_REQ_INVALID;
413 break;
416 err_code = door_return((char *)cnf, cnf_len, NULL, 0);
418 switch (err_code) {
419 case E2BIG:
420 err_txt = "E2BIG";
421 break;
422 case EFAULT:
423 err_txt = "EFAULT";
424 break;
425 case EINVAL:
426 err_txt = "EINVAL";
427 break;
428 case EMFILE:
429 err_txt = "EMFILE";
430 break;
431 default:
432 err_txt = "?";
433 break;
435 (void) fprintf(stderr, "door_return error(%s,%d)", err_txt, err_code);
436 syslog(
437 LOG_DAEMON | LOG_ERR,
438 gettext("!door_return error(%s,%d)"),
439 err_txt,
440 err_code);
444 * _getipnodebyname_req
446 * This function executes the request ISCSI_DOOR_GETIPNODEBYNAME_REQ. It
447 * calls getipnodebyname() but doesn't return all the information. The
448 * confirmation structure only contains one IP address of the list returned
449 * by getipnodebyname().
451 static
452 iscsi_door_cnf_t *
453 _getipnodebyname_req(
454 getipnodebyname_req_t *req,
455 int req_len,
456 size_t *pcnf_len
458 getipnodebyname_cnf_t *cnf = (getipnodebyname_cnf_t *)req;
459 size_t cnf_len;
460 struct hostent *hptr;
461 char *name;
463 /* The opcode is changed immediately. */
464 cnf->hdr.opcode = ISCSI_DOOR_GETIPNODEBYNAME_CNF;
466 /* The size of the request is checked against the minimum required. */
467 if (req_len < sizeof (getipnodebyname_cnf_t)) {
468 cnf->hdr.status = ISCSI_DOOR_STATUS_REQ_FORMAT;
469 *pcnf_len = req_len;
470 return ((iscsi_door_cnf_t *)cnf);
473 name = (char *)req + req->name_offset;
476 * The pointer to the name has to stay inside the request but
477 * after the header.
479 if ((name < ((char *)req + sizeof (getipnodebyname_req_t))) ||
480 ((name + req->name_length) > ((char *)req + req_len))) {
481 cnf->hdr.status = ISCSI_DOOR_STATUS_REQ_FORMAT;
482 *pcnf_len = req_len;
483 return ((iscsi_door_cnf_t *)cnf);
486 /* The library function is called. */
487 hptr = getipnodebyname(
488 name,
489 (int)req->af,
490 (int)req->flags,
491 (int *)&cnf->error_num);
493 if (hptr) {
495 * The call was successful. Now starts the painful work of
496 * parsing the data. However, for version 1 we will only
497 * return the first address.
499 cnf_len = sizeof (getipnodebyname_cnf_t);
500 cnf->h_size_needed = sizeof (getipnodebyname_cnf_t);
501 cnf->h_alias_list_length = 0;
502 cnf->h_alias_list_offset = 0;
503 cnf->h_name_len = 0;
504 cnf->h_name_offset = 0;
506 cnf->h_addrlen = (uint32_t)hptr->h_length;
507 cnf->h_addrtype = (uint32_t)hptr->h_addrtype;
508 cnf->h_addr_list_offset = sizeof (getipnodebyname_cnf_t);
510 if (*hptr->h_addr_list != NULL) {
511 (void) memcpy(
512 ((char *)cnf + sizeof (getipnodebyname_cnf_t)),
513 *hptr->h_addr_list,
514 hptr->h_length);
515 cnf->h_addr_list_length = 1;
516 cnf->h_size_needed += cnf->h_addrlen;
517 cnf_len += hptr->h_length;
518 } else {
519 cnf->h_addr_list_length = 0;
520 cnf->h_size_needed += hptr->h_length;
522 *pcnf_len = cnf_len;
523 cnf->hdr.status = ISCSI_DOOR_STATUS_SUCCESS;
524 freehostent(hptr);
525 } else {
526 cnf->hdr.status = ISCSI_DOOR_STATUS_SUCCESS;
527 cnf->h_addrlen = 0;
528 cnf->h_addrtype = 0;
529 cnf->h_addr_list_offset = sizeof (getipnodebyname_cnf_t);
530 cnf->h_addr_list_length = 0;
531 cnf->h_name_offset = sizeof (getipnodebyname_cnf_t);
532 cnf->h_name_len = 0;
533 cnf->h_alias_list_offset = sizeof (getipnodebyname_cnf_t);
534 cnf->h_alias_list_length = 0;
535 cnf->h_size_needed = sizeof (getipnodebyname_cnf_t);
536 *pcnf_len = sizeof (getipnodebyname_cnf_t);
538 return ((iscsi_door_cnf_t *)cnf);
542 * call_child_door -- This function calls the child door with the value
543 * provided by the caller.
546 static
547 void
548 call_child_door(
549 int value
552 door_arg_t door_arg;
554 (void) memset(&door_arg, 0, sizeof (door_arg));
555 door_arg.data_ptr = (char *)&value;
556 door_arg.data_size = sizeof (value);
557 (void) door_call(iscsi_child_door_handle, &door_arg);
561 * get_luns_count --
563 static
564 uint32_t
565 get_luns_count(
566 int did
569 iscsi_lun_list_t *lun_list;
570 iscsi_lun_list_t *tmp;
571 size_t len;
572 uint32_t lun_count;
574 lun_list = (iscsi_lun_list_t *)malloc(sizeof (*lun_list));
576 (void) memset(lun_list, 0, sizeof (*lun_list));
577 lun_list->ll_vers = ISCSI_INTERFACE_VERSION;
578 lun_list->ll_in_cnt = 1;
579 lun_list->ll_all_tgts = B_TRUE;
581 for (;;) {
583 if (ioctl(
584 did,
585 ISCSI_LUN_OID_LIST_GET,
586 lun_list) == -1) {
587 free(lun_list);
588 /* The Ioctl didn't go well. */
589 return (0);
591 if (lun_list->ll_in_cnt >= lun_list->ll_out_cnt) {
592 /* We got it all. */
593 break;
596 * We didn't get all the targets. Let's build a new Ioctl with
597 * a new size.
599 tmp = lun_list;
600 len = tmp->ll_out_cnt * sizeof (tmp->ll_luns);
601 len += sizeof (*tmp) - sizeof (tmp->ll_luns);
602 lun_list = (iscsi_lun_list_t *)malloc(len);
603 if (lun_list == NULL) {
604 /* No resources. */
605 free(tmp);
606 return (0);
608 (void) memset(lun_list, 0, len);
609 lun_list->ll_vers = ISCSI_INTERFACE_VERSION;
610 lun_list->ll_in_cnt = tmp->ll_out_cnt;
611 lun_list->ll_all_tgts = B_TRUE;
612 free(tmp);
614 lun_count = lun_list->ll_out_cnt;
615 free(lun_list);
616 return (lun_count);
620 * discovery_event_wait -- Waits for the discovery process to finish.
623 static
624 boolean_t
625 discovery_event_wait(
626 int did
629 boolean_t rc;
630 uint32_t lun_count;
631 uint32_t lun_timer;
632 uint32_t tmp;
633 iSCSIDiscoveryMethod_t discovery_flags;
634 iSCSIDiscoveryMethod_t discovery_all;
636 rc = B_FALSE;
637 lun_count = 0;
638 lun_timer = 0;
639 discovery_flags = 0;
640 discovery_all = iSCSIDiscoveryMethodStatic |
641 iSCSIDiscoveryMethodSLP |
642 iSCSIDiscoveryMethodISNS |
643 iSCSIDiscoveryMethodSendTargets;
645 for (;;) {
647 /* The status discovery flags are read. */
648 if (ioctl(
649 did,
650 ISCSI_DISCOVERY_EVENTS,
651 &discovery_flags) == -1) {
652 /* IO problem */
653 break;
656 if (discovery_flags == discovery_all) {
657 /* Discovery over */
658 rc = B_TRUE;
659 break;
662 if (lun_timer >= ISCSI_DISCOVERY_POLL_DELAY2) {
663 /* Let's check if the driver is making progress. */
664 tmp = get_luns_count(did);
665 if (tmp <= lun_count) {
666 /* No progress */
667 break;
669 lun_count = tmp;
670 lun_timer = 0;
672 (void) sleep(ISCSI_DISCOVERY_POLL_DELAY1);
673 lun_timer += ISCSI_DISCOVERY_POLL_DELAY1;
675 return (rc);
678 /*ARGSUSED*/
679 static void
680 signone(int sig, siginfo_t *sip, void *utp)