#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <signal.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 <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>
#define ISCSI_DOOR_DAEMON_SYSLOG_PP "iscsid"
#define ISCSI_DISCOVERY_POLL_DELAY1 1
#define ISCSI_DISCOVERY_POLL_DELAY2 60
#define ISCSI_SMF_OFFLINE_DELAY 10
#define ISCSI_SMF_OFFLINE_MAX_RETRY_TIMES 60
#if !defined(SMF_EXIT_ERR_OTHER)
#define SMF_EXIT_ERR_OTHER -1
#endif
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;
static int iscsi_dev_handle;
static int iscsi_kernel_door_handle;
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
);
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;
(void) setlocale(LC_ALL, "");
openlog("ISCSI_DOOR_DAEMON_SYSLOG_PP", LOG_PID, LOG_DAEMON);
if (sem_init(&iscsi_child_sem, 0, 0) == -1) {
exit(SMF_EXIT_ERR_OTHER);
}
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);
}
(void) signal(SIGCHLD, sigchld_handler);
iscsi_child_pid = fork();
if (iscsi_child_pid < 0) {
syslog(LOG_DAEMON | LOG_ERR, gettext("Cannot fork"));
(void) sem_destroy(&iscsi_child_sem);
exit(SMF_EXIT_ERR_OTHER);
}
if (iscsi_child_pid) {
(void) sem_wait(&iscsi_child_sem);
(void) sem_destroy(&iscsi_child_sem);
exit(iscsi_child_smf_exit_code);
}
i = open("/dev/null", O_RDWR);
(void) dup2(i, 1);
(void) dup2(i, 2);
(void) sigfillset(&allsigs);
(void) pthread_sigmask(SIG_BLOCK, &allsigs, NULL);
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);
}
iscsi_dev_handle = open(ISCSI_DRIVER_DEVCTL, O_RDWR);
if (iscsi_dev_handle == -1) {
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);
}
(void) discovery_event_wait(iscsi_dev_handle);
call_child_door(SMF_EXIT_OK);
(void) sigemptyset(&sigs);
(void) sigaddset(&sigs, SIGTERM);
(void) sigaddset(&sigs, SIGINT);
(void) sigaddset(&sigs, SIGQUIT);
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = signone;
(void) sigaction(SIGTERM, &act, NULL);
(void) sigaction(SIGINT, &act, NULL);
(void) sigaction(SIGQUIT, &act, NULL);
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) {
(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;
}
}
}
static
void
sigchld_handler(
int sig
)
{
int status;
pid_t ret_pid;
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);
}
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);
}
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;
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);
if (req == NULL) {
err_ind.status = ISCSI_DOOR_STATUS_REQ_INVALID;
} else if (alen < sizeof (iscsi_door_msg_hdr_t)) {
err_ind.status = ISCSI_DOOR_STATUS_REQ_LENGTH;
} else if (req->hdr.signature != ISCSI_DOOR_REQ_SIGNATURE) {
err_ind.status = ISCSI_DOOR_STATUS_REQ_INVALID;
} else if (req->hdr.version != ISCSI_DOOR_REQ_VERSION_1) {
err_ind.status = ISCSI_DOOR_STATUS_REQ_VERSION;
} else {
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);
}
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;
cnf->hdr.opcode = ISCSI_DOOR_GETIPNODEBYNAME_CNF;
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;
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);
}
hptr = getipnodebyname(
name,
(int)req->af,
(int)req->flags,
(int *)&cnf->error_num);
if (hptr) {
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);
}
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);
}
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);
return (0);
}
if (lun_list->ll_in_cnt >= lun_list->ll_out_cnt) {
break;
}
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) {
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);
}
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 (;;) {
if (ioctl(
did,
ISCSI_DISCOVERY_EVENTS,
&discovery_flags) == -1) {
break;
}
if (discovery_flags == discovery_all) {
rc = B_TRUE;
break;
}
if (lun_timer >= ISCSI_DISCOVERY_POLL_DELAY2) {
tmp = get_luns_count(did);
if (tmp <= lun_count) {
break;
}
lun_count = tmp;
lun_timer = 0;
}
(void) sleep(ISCSI_DISCOVERY_POLL_DELAY1);
lun_timer += ISCSI_DISCOVERY_POLL_DELAY1;
}
return (rc);
}
static void
signone(int sig, siginfo_t *sip, void *utp)
{
}