# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2013 Nexenta Systems, Inc. All rights reserved. # PROG= iscsid MANIFEST= iscsi-initiator.xml SVCMETHOD= iscsi-initiator iscsid include ../Makefile.cmd CFLAGS += $(CCVERBOSE) LDLIBS += -lnsl ROOTMANIFESTDIR= $(ROOTSVCNETWORKISCSI) $(ROOTSVCNETWORKISCSI)/iscsi-initiator.xml : FILEMODE = 0444 .KEEP_STATE: all: $(PROG) check: $(CHKMANIFEST) clean: install: all $(ROOTMANIFEST) $(ROOTSVCMETHOD) include ../Makefile.targ #!/sbin/sh # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. # # # Start/stop iscsi initiator service # . /lib/svc/share/smf_include.sh # checkmessage "fsck_device | mount_point" # # Simple auxilary routine to the shell function checkfs. Prints out # instructions for a manual file system check before entering the shell. # checkmessage() { echo "" > /dev/console if [ "$1" != "" ] ; then echo "WARNING - Unable to repair one or more \c" \ > /dev/console echo "of the following filesystem(s):" > /dev/console echo "\t$1" > /dev/console else echo "WARNING - Unable to repair one or more filesystems." \ > /dev/console fi echo "Run fsck manually (fsck filesystem...)." > /dev/console echo "" > /dev/console } # # checkfs raw_device fstype mountpoint # # Check the file system specified. The return codes from fsck have the # following meanings. # 0 - file system is unmounted and okay # 32 - file system is unmounted and needs checking (fsck -m only) # 33 - file system is already mounted # 34 - cannot stat device # 36 - uncorrectable errors detected - terminate normally (4.1 code 8) # 37 - a signal was caught during processing (4.1 exit 12) # 39 - uncorrectable errors detected - terminate rightaway (4.1 code 8) # 40 - for root, same as 0 (used by rcS to remount root) # checkfs() { /sbin/fsck -F $2 -m $1 >/dev/null 2>&1 if [ $? -ne 0 ] then # Determine fsck options by file system type case "$2" in ufs) foptions="-o p" ;; *) foptions="-y" ;; esac echo "The "$3" file system ("$1") is being checked." /sbin/fsck -F $2 ${foptions} $1 case $? in 0|40) # file system OK ;; *) # couldn't fix the file system echo "/sbin/fsck failed with exit code "$?"." checkmessage "$1" ;; esac fi } mount_iscsi() { err=0 iscsilist="" exec < /etc/vfstab while read special fsckdev mountp fstype fsckpass automnt mntopts; do case $special in '#'* | '' ) # Ignore comments, empty lines. continue ;; '-') # Ignore "no-action" lines. continue ;; esac if [ "$automnt" != "iscsi" ]; then continue fi if [ "$fstype" = "-" ]; then echo "iscsi-initiator: FSType of iscsi LUN \c" 1>&2 echo "$special cannot be identified" 1>&2 continue fi # # Ignore entries already mounted # /bin/grep " $mountp " /etc/mnttab >/dev/null \ 2>&1 && continue # # Can't fsck if no fsckdev is specified # if [ "$fsckdev" = "-" ]; then iscsilist="$iscsilist $mountp" continue fi # # fsck everything else: # # fsck -m simply returns true if the filesystem is # suitable for mounting. # /sbin/fsck -m -F $fstype $fsckdev >/dev/null 2>&1 case $? in 0|40) iscsilist="$iscsilist $mountp" continue ;; 32) checkfs $fsckdev $fstype $mountp iscsilist="$iscsilist $mountp" continue ;; 33) # already mounted echo "$special already mounted" ;; 34) # bogus special device echo "Cannot stat $fsckdev - ignoring" err=1 ;; *) # uncorrectable errors echo "$fsckdev uncorrectable error" err=1 ;; esac done [ -z "$iscsilist" ] || /sbin/mount -a $iscsilist for iscsilun in $iscsilist do /bin/grep " $iscsilun " /etc/mnttab >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Fail to mount $iscsilun" err=1 fi done return $err } umount_iscsi () { # # Generate iscsi mountp list from /etc/vfstab exec < /etc/vfstab while read special fsckdev mountp fstype fsckpass automnt mntopts; do case $special in '#'* | '') continue;; # Ignore comments, # empty lines. '-') continue;; # Ignore "no-action lines. esac if [ "$automnt" != "iscsi" ]; then continue fi /bin/grep " $mountp " /etc/mnttab >/dev/null 2>&1 if [ $? -ne 0 ]; then continue fi iscsilist="$iscsilist $mountp" done if [ -n "$iscsilist" ]; then umount -a $iscsilist 1>&2 rc=$? else rc=0; fi return $rc } case "$1" in 'start') /usr/bin/pgrep -P 1 -x iscsid if [ $? -ne 0 ]; then /lib/svc/method/iscsid fi if [ $? -eq 0 ]; then delay=60 while [ $delay -gt 0 ]; do delay=`expr $delay - 1` mount_iscsi if [ $? -eq 1 ]; then if [ $delay -gt 0 ]; then sleep 1 continue else echo "iscsi-initiator: mount iscsi \c" echo "lun in /etc/vfstab fail." umount_iscsi exit $SMF_EXIT_ERR_CONFIG fi else exit $SMF_EXIT_OK fi done else exit $? fi ;; 'stop') umount_iscsi /usr/bin/pkill -P 1 -x iscsid exit 0 ;; *) echo "Usage: $0 { start | stop }" exit 1 ;; esac exit $SMF_EXIT_OK /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2012 Milan Jurik. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Local Defines * ------------- */ #define ISCSI_DOOR_DAEMON_SYSLOG_PP "iscsid" #define ISCSI_DISCOVERY_POLL_DELAY1 1 /* Seconds */ #define ISCSI_DISCOVERY_POLL_DELAY2 60 /* Seconds */ #define ISCSI_SMF_OFFLINE_DELAY 10 /* Seconds */ #define ISCSI_SMF_OFFLINE_MAX_RETRY_TIMES 60 #if !defined(SMF_EXIT_ERR_OTHER) #define SMF_EXIT_ERR_OTHER -1 #endif /* * Global Variables related to the synchronization of the child process * -------------------------------------------------------------------- */ static pid_t iscsi_child_pid; static sem_t iscsi_child_sem; static int iscsi_child_door_handle; static int iscsi_child_smf_exit_code; /* * Global Variables related to the door accessed by the kernel * ----------------------------------------------------------- */ static int iscsi_dev_handle; static int iscsi_kernel_door_handle; /* * Prototypes of Functions the body of which is defined farther down * in this file. * ----------------------------------------------------------------- */ static void call_child_door(int value); static void sigchld_handler(int sig); static boolean_t discovery_event_wait(int did); static void signone(int, siginfo_t *, void *); static void iscsi_child_door( void *cookie, char *args, size_t alen, door_desc_t *ddp, uint_t ndid ); static void iscsi_kernel_door( void *cookie, char *args, size_t alen, door_desc_t *ddp, uint_t ndid ); static iscsi_door_cnf_t * _getipnodebyname_req( getipnodebyname_req_t *req, int req_len, size_t *pcnf_len ); /* * main -- Entry point of the iSCSI door server daemon * * This function forks, waits for the child process feedback and exits. */ /* ARGSUSED */ int main( int argc, char *argv[] ) { int i; int sig; int ret = -1; int retry = 0; sigset_t sigs, allsigs; struct sigaction act; uint32_t rval; /* * Get the locale set up before calling any other routines * with messages to ouput. */ (void) setlocale(LC_ALL, ""); openlog("ISCSI_DOOR_DAEMON_SYSLOG_PP", LOG_PID, LOG_DAEMON); /* The child semaphore is created. */ if (sem_init(&iscsi_child_sem, 0, 0) == -1) { exit(SMF_EXIT_ERR_OTHER); } /* The door for the child is created. */ iscsi_child_door_handle = door_create(iscsi_child_door, NULL, 0); if (iscsi_child_door_handle == -1) { (void) sem_destroy(&iscsi_child_sem); exit(SMF_EXIT_ERR_OTHER); } /* A signal handler is set for SIGCHLD. */ (void) signal(SIGCHLD, sigchld_handler); /* * Here begins the daemonizing code * -------------------------------- */ iscsi_child_pid = fork(); if (iscsi_child_pid < 0) { /* The fork failed. */ syslog(LOG_DAEMON | LOG_ERR, gettext("Cannot fork")); (void) sem_destroy(&iscsi_child_sem); exit(SMF_EXIT_ERR_OTHER); } if (iscsi_child_pid) { /* * The parent exits after the child has provided feedback. This * waiting phase is to meet one of greenline's requirements. * We shouldn't return till we are sure the service is ready to * be provided. */ (void) sem_wait(&iscsi_child_sem); (void) sem_destroy(&iscsi_child_sem); exit(iscsi_child_smf_exit_code); } /* * stdout and stderr are redirected to "/dev/null". */ i = open("/dev/null", O_RDWR); (void) dup2(i, 1); (void) dup2(i, 2); /* * Here ends the daemonizing code * ------------------------------ */ /* * Block out all signals */ (void) sigfillset(&allsigs); (void) pthread_sigmask(SIG_BLOCK, &allsigs, NULL); /* setup the door handle */ iscsi_kernel_door_handle = door_create(iscsi_kernel_door, NULL, 0); if (iscsi_kernel_door_handle == -1) { perror(gettext("door_create failed")); syslog(LOG_DAEMON | LOG_ERR, gettext("door_create failed")); exit(SMF_EXIT_ERR_OTHER); } /* * The iSCSI driver is opened. */ iscsi_dev_handle = open(ISCSI_DRIVER_DEVCTL, O_RDWR); if (iscsi_dev_handle == -1) { /* The driver couldn't be opened. */ perror(gettext("iscsi device open failed")); exit(SMF_EXIT_ERR_OTHER); } if (ioctl( iscsi_dev_handle, ISCSI_SMF_ONLINE, &iscsi_kernel_door_handle) == -1) { (void) close(iscsi_dev_handle); perror(gettext("ioctl: enable iscsi initiator")); exit(SMF_EXIT_ERR_OTHER); } /* * Keep the dev open, so to keep iscsi module from unloaded. * This is crutial to guarantee the consistency of the * door_handle and service state in kernel. */ /* We have to wait for the discovery process to finish. */ (void) discovery_event_wait(iscsi_dev_handle); /* We let the parent know that everything is ok. */ call_child_door(SMF_EXIT_OK); /* now set up signals we care about */ (void) sigemptyset(&sigs); (void) sigaddset(&sigs, SIGTERM); (void) sigaddset(&sigs, SIGINT); (void) sigaddset(&sigs, SIGQUIT); /* make sure signals to be enqueued */ act.sa_flags = SA_SIGINFO; act.sa_sigaction = signone; (void) sigaction(SIGTERM, &act, NULL); (void) sigaction(SIGINT, &act, NULL); (void) sigaction(SIGQUIT, &act, NULL); /* wait and process signals */ for (;;) { sig = sigwait(&sigs); if (sig < 0) continue; switch (sig) { case SIGQUIT: case SIGINT: case SIGTERM: do { ret = ioctl(iscsi_dev_handle, ISCSI_SMF_OFFLINE, &rval); if (ret == -1) { /* * Keep retrying if unable * to stop */ (void) sleep(ISCSI_SMF_OFFLINE_DELAY); retry++; } } while ((ret == -1) && (retry < ISCSI_SMF_OFFLINE_MAX_RETRY_TIMES)); (void) close(iscsi_dev_handle); if (rval == B_FALSE) { syslog(LOG_DAEMON, gettext("iSCSI initiator" " service exited with sessions left.")); } return (0); default: break; } } } /* * sigchld_handler -- SIGCHLD Handler * */ /* ARGSUSED */ static void sigchld_handler( int sig ) { int status; pid_t ret_pid; /* This is the default code. */ iscsi_child_smf_exit_code = SMF_EXIT_ERR_OTHER; ret_pid = waitpid(iscsi_child_pid, &status, WNOHANG); if (ret_pid == iscsi_child_pid) { if (WIFEXITED(status)) { iscsi_child_smf_exit_code = WEXITSTATUS(status); } } (void) sem_post(&iscsi_child_sem); } /* * iscsi_child_door -- Child process door entry point * * This function is executed when a driver calls door_ki_upcall(). */ /* ARGSUSED */ static void iscsi_child_door( void *cookie, char *args, size_t alen, door_desc_t *ddp, uint_t ndid ) { int *ptr = (int *)args; iscsi_child_smf_exit_code = SMF_EXIT_ERR_OTHER; if (alen >= sizeof (iscsi_child_smf_exit_code)) { iscsi_child_smf_exit_code = *ptr; } (void) sem_post(&iscsi_child_sem); (void) door_return(NULL, 0, NULL, 0); } /* * iscsi_kernel_door -- Kernel door entry point * * This function is executed when a driver calls door_ki_upcall(). */ /* ARGSUSED */ static void iscsi_kernel_door( void *cookie, char *args, size_t alen, door_desc_t *ddp, uint_t ndid ) { iscsi_door_msg_hdr_t err_ind; iscsi_door_req_t *req; iscsi_door_cnf_t *cnf; size_t cnf_len; char *err_txt; int err_code; /* Local variables pre-initialization */ err_ind.signature = ISCSI_DOOR_REQ_SIGNATURE; err_ind.version = ISCSI_DOOR_REQ_VERSION_1; err_ind.opcode = ISCSI_DOOR_ERROR_IND; req = (iscsi_door_req_t *)args; cnf = (iscsi_door_cnf_t *)&err_ind; cnf_len = sizeof (err_ind); /* * The validity of the request is checked before going any farther. */ if (req == NULL) { /* * A request has to be passed. */ err_ind.status = ISCSI_DOOR_STATUS_REQ_INVALID; } else if (alen < sizeof (iscsi_door_msg_hdr_t)) { /* * The buffer containing the request must be at least as big * as message header. */ err_ind.status = ISCSI_DOOR_STATUS_REQ_LENGTH; } else if (req->hdr.signature != ISCSI_DOOR_REQ_SIGNATURE) { /* * The request must be correctly signed. */ err_ind.status = ISCSI_DOOR_STATUS_REQ_INVALID; } else if (req->hdr.version != ISCSI_DOOR_REQ_VERSION_1) { /* * The version of the request must be supported by the server. */ err_ind.status = ISCSI_DOOR_STATUS_REQ_VERSION; } else { /* * The request is treated according to the opcode. */ switch (req->hdr.opcode) { case ISCSI_DOOR_GETIPNODEBYNAME_REQ: cnf = _getipnodebyname_req( &req->ginbn_req, alen, &cnf_len); break; default: err_ind.status = ISCSI_DOOR_STATUS_REQ_INVALID; break; } } err_code = door_return((char *)cnf, cnf_len, NULL, 0); switch (err_code) { case E2BIG: err_txt = "E2BIG"; break; case EFAULT: err_txt = "EFAULT"; break; case EINVAL: err_txt = "EINVAL"; break; case EMFILE: err_txt = "EMFILE"; break; default: err_txt = "?"; break; } (void) fprintf(stderr, "door_return error(%s,%d)", err_txt, err_code); syslog( LOG_DAEMON | LOG_ERR, gettext("!door_return error(%s,%d)"), err_txt, err_code); } /* * _getipnodebyname_req * * This function executes the request ISCSI_DOOR_GETIPNODEBYNAME_REQ. It * calls getipnodebyname() but doesn't return all the information. The * confirmation structure only contains one IP address of the list returned * by getipnodebyname(). */ static iscsi_door_cnf_t * _getipnodebyname_req( getipnodebyname_req_t *req, int req_len, size_t *pcnf_len ) { getipnodebyname_cnf_t *cnf = (getipnodebyname_cnf_t *)req; size_t cnf_len; struct hostent *hptr; char *name; /* The opcode is changed immediately. */ cnf->hdr.opcode = ISCSI_DOOR_GETIPNODEBYNAME_CNF; /* The size of the request is checked against the minimum required. */ if (req_len < sizeof (getipnodebyname_cnf_t)) { cnf->hdr.status = ISCSI_DOOR_STATUS_REQ_FORMAT; *pcnf_len = req_len; return ((iscsi_door_cnf_t *)cnf); } name = (char *)req + req->name_offset; /* * The pointer to the name has to stay inside the request but * after the header. */ if ((name < ((char *)req + sizeof (getipnodebyname_req_t))) || ((name + req->name_length) > ((char *)req + req_len))) { cnf->hdr.status = ISCSI_DOOR_STATUS_REQ_FORMAT; *pcnf_len = req_len; return ((iscsi_door_cnf_t *)cnf); } /* The library function is called. */ hptr = getipnodebyname( name, (int)req->af, (int)req->flags, (int *)&cnf->error_num); if (hptr) { /* * The call was successful. Now starts the painful work of * parsing the data. However, for version 1 we will only * return the first address. */ cnf_len = sizeof (getipnodebyname_cnf_t); cnf->h_size_needed = sizeof (getipnodebyname_cnf_t); cnf->h_alias_list_length = 0; cnf->h_alias_list_offset = 0; cnf->h_name_len = 0; cnf->h_name_offset = 0; cnf->h_addrlen = (uint32_t)hptr->h_length; cnf->h_addrtype = (uint32_t)hptr->h_addrtype; cnf->h_addr_list_offset = sizeof (getipnodebyname_cnf_t); if (*hptr->h_addr_list != NULL) { (void) memcpy( ((char *)cnf + sizeof (getipnodebyname_cnf_t)), *hptr->h_addr_list, hptr->h_length); cnf->h_addr_list_length = 1; cnf->h_size_needed += cnf->h_addrlen; cnf_len += hptr->h_length; } else { cnf->h_addr_list_length = 0; cnf->h_size_needed += hptr->h_length; } *pcnf_len = cnf_len; cnf->hdr.status = ISCSI_DOOR_STATUS_SUCCESS; freehostent(hptr); } else { cnf->hdr.status = ISCSI_DOOR_STATUS_SUCCESS; cnf->h_addrlen = 0; cnf->h_addrtype = 0; cnf->h_addr_list_offset = sizeof (getipnodebyname_cnf_t); cnf->h_addr_list_length = 0; cnf->h_name_offset = sizeof (getipnodebyname_cnf_t); cnf->h_name_len = 0; cnf->h_alias_list_offset = sizeof (getipnodebyname_cnf_t); cnf->h_alias_list_length = 0; cnf->h_size_needed = sizeof (getipnodebyname_cnf_t); *pcnf_len = sizeof (getipnodebyname_cnf_t); } return ((iscsi_door_cnf_t *)cnf); } /* * call_child_door -- This function calls the child door with the value * provided by the caller. * */ static void call_child_door( int value ) { door_arg_t door_arg; (void) memset(&door_arg, 0, sizeof (door_arg)); door_arg.data_ptr = (char *)&value; door_arg.data_size = sizeof (value); (void) door_call(iscsi_child_door_handle, &door_arg); } /* * get_luns_count -- */ static uint32_t get_luns_count( int did ) { iscsi_lun_list_t *lun_list; iscsi_lun_list_t *tmp; size_t len; uint32_t lun_count; lun_list = (iscsi_lun_list_t *)malloc(sizeof (*lun_list)); (void) memset(lun_list, 0, sizeof (*lun_list)); lun_list->ll_vers = ISCSI_INTERFACE_VERSION; lun_list->ll_in_cnt = 1; lun_list->ll_all_tgts = B_TRUE; for (;;) { if (ioctl( did, ISCSI_LUN_OID_LIST_GET, lun_list) == -1) { free(lun_list); /* The Ioctl didn't go well. */ return (0); } if (lun_list->ll_in_cnt >= lun_list->ll_out_cnt) { /* We got it all. */ break; } /* * We didn't get all the targets. Let's build a new Ioctl with * a new size. */ tmp = lun_list; len = tmp->ll_out_cnt * sizeof (tmp->ll_luns); len += sizeof (*tmp) - sizeof (tmp->ll_luns); lun_list = (iscsi_lun_list_t *)malloc(len); if (lun_list == NULL) { /* No resources. */ free(tmp); return (0); } (void) memset(lun_list, 0, len); lun_list->ll_vers = ISCSI_INTERFACE_VERSION; lun_list->ll_in_cnt = tmp->ll_out_cnt; lun_list->ll_all_tgts = B_TRUE; free(tmp); } lun_count = lun_list->ll_out_cnt; free(lun_list); return (lun_count); } /* * discovery_event_wait -- Waits for the discovery process to finish. * */ static boolean_t discovery_event_wait( int did ) { boolean_t rc; uint32_t lun_count; uint32_t lun_timer; uint32_t tmp; iSCSIDiscoveryMethod_t discovery_flags; iSCSIDiscoveryMethod_t discovery_all; rc = B_FALSE; lun_count = 0; lun_timer = 0; discovery_flags = 0; discovery_all = iSCSIDiscoveryMethodStatic | iSCSIDiscoveryMethodSLP | iSCSIDiscoveryMethodISNS | iSCSIDiscoveryMethodSendTargets; for (;;) { /* The status discovery flags are read. */ if (ioctl( did, ISCSI_DISCOVERY_EVENTS, &discovery_flags) == -1) { /* IO problem */ break; } if (discovery_flags == discovery_all) { /* Discovery over */ rc = B_TRUE; break; } if (lun_timer >= ISCSI_DISCOVERY_POLL_DELAY2) { /* Let's check if the driver is making progress. */ tmp = get_luns_count(did); if (tmp <= lun_count) { /* No progress */ break; } lun_count = tmp; lun_timer = 0; } (void) sleep(ISCSI_DISCOVERY_POLL_DELAY1); lun_timer += ISCSI_DISCOVERY_POLL_DELAY1; } return (rc); } /*ARGSUSED*/ static void signone(int sig, siginfo_t *sip, void *utp) { }