summaryrefslogtreecommitdiff
path: root/usr/src/cmd/iscsid/iscsid.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/iscsid/iscsid.c')
-rw-r--r--usr/src/cmd/iscsid/iscsid.c619
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);
+}