diff options
Diffstat (limited to 'usr/src/cmd/iscsid/iscsid.c')
-rw-r--r-- | usr/src/cmd/iscsid/iscsid.c | 619 |
1 files changed, 619 insertions, 0 deletions
diff --git a/usr/src/cmd/iscsid/iscsid.c b/usr/src/cmd/iscsid/iscsid.c new file mode 100644 index 0000000000..b2acf5cbf4 --- /dev/null +++ b/usr/src/cmd/iscsid/iscsid.c @@ -0,0 +1,619 @@ +/* + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <locale.h> +#include <syslog.h> +#include <netdb.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> +#include <errno.h> +#include <door.h> +#include <meta.h> +#include <libsysevent.h> +#include <wait.h> +#include <semaphore.h> +#include <libscf.h> + +#include <sys/scsi/adapters/iscsi_door.h> +#include <sys/scsi/adapters/iscsi_if.h> + +/* + * Local Defines + * ------------- + */ +#define ISCSI_DOOR_DAEMON_SYSLOG_PP "iscsid" +#define ISCSI_DISCOVERY_POLL_DELAY1 1 /* Seconds */ +#define ISCSI_DISCOVERY_POLL_DELAY2 60 /* Seconds */ + +#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 +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; + + /* + * 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 the usual signals so we don't get killed unintentionally. + */ + (void) signal(SIGHUP, SIG_IGN); + (void) signal(SIGINT, SIG_IGN); + (void) signal(SIGQUIT, SIG_IGN); + + /* 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_DOOR_HANDLE_SET, + &iscsi_kernel_door_handle) == -1) { + (void) close(iscsi_dev_handle); + perror(gettext("ioctl: set door handle")); + exit(SMF_EXIT_ERR_OTHER); + } + + /* We have to wait for the discovery process to finish. */ + (void) discovery_event_wait(iscsi_dev_handle); + + /* We don't need to keep the device opened. */ + (void) close(iscsi_dev_handle); + + /* We let know the parent that everything is ok. */ + call_child_door(SMF_EXIT_OK); + for (;;) { + (void) pause(); + } +} + +/* + * 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); +} |