blob: c86ac11e4045a6c12a05c5c9bf08409f82508932 [file] [log] [blame]
/*
* Copyright (C) 2002 - 2003 Ardis Technologies <roman@ardistech.com>
* Copyright (C) 2007 - 2018 Vladislav Bolkhovitin
* Copyright (C) 2007 - 2018 Western Digital Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, version 2
* of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <netdb.h>
#include <signal.h>
#include <poll.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/un.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include "iscsid.h"
#include "iscsi_adm.h"
static char *server_address;
uint16_t server_port = ISCSI_LISTEN_PORT;
struct pollfd poll_array[POLL_MAX];
static struct connection *incoming[INCOMING_MAX];
static int incoming_cnt;
int ctrl_fd, ipc_fd, nl_fd;
int conn_blocked;
struct iscsi_init_params iscsi_init_params;
static const char program_name[] = "iscsi-scstd";
static struct option const long_options[] = {
{"config", required_argument, 0, 'c'},
{"foreground", no_argument, 0, 'f'},
{"debug", required_argument, 0, 'd'},
{"uid", required_argument, 0, 'u'},
{"gid", required_argument, 0, 'g'},
{"address", required_argument, 0, 'a'},
{"port", required_argument, 0, 'p'},
{"version", no_argument, 0, 'v'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0},
};
int init_report_pipe[2];
static void usage(int status)
{
if (status != 0)
fprintf(stderr, "Try `%s --help' for more information.\n", program_name);
else {
printf("Usage: %s [OPTION]\n", program_name);
printf("\
iSCSI target daemon.\n\
-c, --config=[path] Execute in the config file.\n");
printf("\
-f, --foreground make the program run in the foreground\n\
-d, --debug debuglevel print debugging information\n\
-u, --uid=uid run as uid, default is current user\n\
-g, --gid=gid run as gid, default is current user group\n\
-a, --address=address listen on specified local address instead of all\n\
-p, --port=port listen on specified port instead of 3260\n\
-h, --help display this help and exit\n\
");
}
exit(1);
}
const char *get_error_str(int error)
{
if (error == EAI_SYSTEM)
return strerror(errno);
else
return gai_strerror(error);
}
static void create_listen_socket(struct pollfd *array)
{
struct addrinfo hints, *res, *res0;
char servname[64];
int i, sock, opt, rc;
memset(servname, 0, sizeof(servname));
snprintf(servname, sizeof(servname), "%d", server_port);
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
rc = getaddrinfo(server_address, servname, &hints, &res0);
if (rc != 0) {
log_error("Unable to get address info (%s)!",
get_error_str(rc));
exit(1);
}
i = 0;
for (res = res0; res && i < LISTEN_MAX; res = res->ai_next) {
sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sock < 0) {
log_error("Unable to create server socket (%s) %d %d %d!",
strerror(errno), res->ai_family,
res->ai_socktype, res->ai_protocol);
continue;
}
sock_set_keepalive(sock, 50);
opt = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)))
log_warning("Unable to set SO_REUSEADDR on server socket (%s)!",
strerror(errno));
opt = 1;
if (res->ai_family == AF_INET6 &&
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt))) {
log_error("Unable to restrict IPv6 socket (%s)", strerror(errno));
close(sock);
continue;
}
if (bind(sock, res->ai_addr, res->ai_addrlen)) {
log_error("Unable to bind server socket (%s)!", strerror(errno));
close(sock);
continue;
}
if (listen(sock, INCOMING_MAX)) {
log_error("Unable to listen to server socket (%s)!", strerror(errno));
close(sock);
continue;
}
set_non_blocking(sock);
array[i].fd = sock;
array[i].events = POLLIN;
i++;
}
freeaddrinfo(res0);
if (i == 0)
exit(1);
}
static struct connection *alloc_and_init_conn(int fd)
{
struct pollfd *pollfd;
struct connection *conn = NULL;
int i;
for (i = 0; i < INCOMING_MAX; i++) {
if (!incoming[i])
break;
}
if (i >= INCOMING_MAX) {
log_error("Unable to find incoming slot? %d\n", i);
goto out;
}
conn = conn_alloc();
if (!conn) {
log_error("Fail to allocate %s", "conn\n");
goto out;
}
conn->fd = fd;
incoming[i] = conn;
pollfd = &poll_array[POLL_INCOMING + i];
pollfd->fd = fd;
pollfd->events = POLLIN;
pollfd->revents = 0;
conn_read_pdu(conn);
set_non_blocking(fd);
out:
return conn;
}
static int transmit_iser(int fd, bool start)
{
int opt = start;
return ioctl(fd, RDMA_CORK, &opt, sizeof(opt));
}
static int cork_transmit_iser(int fd)
{
return transmit_iser(fd, true);
}
static int uncork_transmit_iser(int fd)
{
return transmit_iser(fd, false);
}
static void create_iser_listen_socket(struct pollfd *array)
{
struct addrinfo hints, *res, *res0;
char servname[64];
int rc, i;
int iser_fd;
struct isert_addr_info info;
iser_fd = create_and_open_dev("isert_scst", 1);
if (iser_fd >= 0) {
poll_array[POLL_ISER_LISTEN].fd = iser_fd;
poll_array[POLL_ISER_LISTEN].events = POLLIN;
/* RDMAExtensions */
session_keys[key_rdma_extensions].max = 1;
session_keys[key_rdma_extensions].local_def = 1;
} else {
poll_array[POLL_ISER_LISTEN].fd = -1;
poll_array[POLL_ISER_LISTEN].events = 0;
return;
}
memset(servname, 0, sizeof(servname));
snprintf(servname, sizeof(servname), "%d", server_port);
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
rc = getaddrinfo(server_address, servname, &hints, &res0);
if (rc != 0) {
log_error("Unable to get address info (%s)!",
get_error_str(rc));
exit(1);
}
i = 0;
for (res = res0; res && i < ISERT_MAX_PORTALS; res = res->ai_next) {
memcpy(&info.addr, res->ai_addr, res->ai_addrlen);
info.addr_len = res->ai_addrlen;
rc = ioctl(iser_fd, SET_LISTEN_ADDR, &info);
if (rc != 0) {
log_error("Unable to set listen address (%s)!",
strerror(errno));
}
++i;
}
freeaddrinfo(res0);
}
static int iser_getsockname(int fd, struct sockaddr *name, socklen_t *namelen)
{
struct isert_addr_info addr;
int ret;
ret = ioctl(fd, GET_PORTAL_ADDR, &addr, sizeof(addr));
if (ret)
return ret;
memcpy(name, &addr.addr, addr.addr_len);
*namelen = addr.addr_len;
return ret;
}
static int iser_is_discovery(int fd)
{
int val = 1;
return ioctl(fd, DISCOVERY_SESSION, &val, sizeof(val));
}
static void iser_accept(int fd)
{
char buff[256];
int ret, conn_fd;
struct connection *conn;
char target_portal[ISCSI_PORTAL_LEN], target_portal_port[NI_MAXSERV];
struct isert_addr_info addr;
ret = read(fd, buff, sizeof(buff));
if (ret == -1)
goto out;
conn_fd = open(buff, O_RDWR);
if (conn_fd == -1) {
log_error("open(iser_connection) %s failed: %s\n",
buff, strerror(errno));
goto out;
}
ret = ioctl(conn_fd, GET_PORTAL_ADDR, &addr, sizeof(addr));
if (ret) {
log_error("ioctl(GET_PORTAL_ADDR) failed: %s\n",
strerror(errno));
goto out_close;
}
ret = getnameinfo((struct sockaddr *)&addr, sizeof(addr), target_portal,
sizeof(target_portal), target_portal_port,
sizeof(target_portal_port),
NI_NUMERICHOST | NI_NUMERICSERV);
if (ret != 0) {
log_error("Target portal getnameinfo() failed: %s!",
get_error_str(ret));
goto out_close;
}
log_info("iSER Connect to %s:%s", target_portal, target_portal_port);
if (conn_blocked) {
log_warning("Connection refused due to blocking\n");
goto out_close;
}
conn = alloc_and_init_conn(conn_fd);
if (!conn)
goto out_close;
conn->target_portal = strdup(target_portal);
if (conn->target_portal == NULL) {
log_error("Unable to duplicate target portal %s", target_portal);
goto out_free;
}
conn->cork_transmit = cork_transmit_iser;
conn->uncork_transmit = uncork_transmit_iser;
conn->getsockname = iser_getsockname;
conn->is_discovery = iser_is_discovery;
conn->is_iser = true;
incoming_cnt++;
out:
return;
out_free:
conn_free(conn);
out_close:
close(conn_fd);
goto out;
}
static int transmit_sock(int fd, bool start)
{
int opt = start;
return setsockopt(fd, SOL_TCP, TCP_CORK, &opt, sizeof(opt));
}
static int cork_transmit_sock(int fd)
{
return transmit_sock(fd, true);
}
static int uncork_transmit_sock(int fd)
{
return transmit_sock(fd, false);
}
static int tcp_is_discovery(int fd)
{
return 0;
}
static void accept_connection(int listen)
{
union {
struct sockaddr sa;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
} from, to;
socklen_t namesize;
struct connection *conn;
int fd, rc;
char initiator_addr[ISCSI_PORTAL_LEN], initiator_port[NI_MAXSERV];
char target_portal[ISCSI_PORTAL_LEN], target_portal_port[NI_MAXSERV];
namesize = sizeof(from);
if ((fd = accept(listen, &from.sa, &namesize)) < 0) {
switch (errno) {
case EINTR:
case EAGAIN:
case ENETDOWN:
case EPROTO:
case ENOPROTOOPT:
case EHOSTDOWN:
case ENONET:
case EHOSTUNREACH:
case EOPNOTSUPP:
case ENETUNREACH:
break;
default:
log_error("accept(incoming_socket) failed: %s",
strerror(errno));
exit(1);
}
goto out;
}
namesize = sizeof(to);
rc = getsockname(fd, &to.sa, &namesize);
if (rc != 0) {
log_error("getsockname() failed: %s", strerror(errno));
goto out_close;
}
rc = getnameinfo(&to.sa, sizeof(to), target_portal, sizeof(target_portal),
target_portal_port, sizeof(target_portal_port),
NI_NUMERICHOST | NI_NUMERICSERV);
if (rc != 0) {
log_error("Target portal getnameinfo() failed: %s!",
get_error_str(rc));
goto out_close;
}
rc = getnameinfo(&from.sa, sizeof(from), initiator_addr,
sizeof(initiator_addr), initiator_port,
sizeof(initiator_port), NI_NUMERICHOST | NI_NUMERICSERV);
if (rc != 0) {
log_error("Initiator getnameinfo() failed: %s!",
get_error_str(rc));
goto out_close;
}
log_info("Connect from %s:%s to %s:%s", initiator_addr, initiator_port,
target_portal, target_portal_port);
if (conn_blocked) {
log_warning("Connection refused due to blocking\n");
goto out_close;
}
conn = alloc_and_init_conn(fd);
if (!conn)
goto out_close;
conn->target_portal = strdup(target_portal);
if (conn->target_portal == NULL) {
log_error("Unable to duplicate target portal %s", target_portal);
goto out_free;
}
conn->initiator_ip_address = strdup(initiator_addr);
if (conn->initiator_ip_address == NULL) {
log_error("Unable to duplicate initiator address %s", initiator_addr);
goto out_free;
}
conn->cork_transmit = cork_transmit_sock;
conn->uncork_transmit = uncork_transmit_sock;
conn->getsockname = getsockname;
conn->is_discovery = tcp_is_discovery;
conn_read_pdu(conn);
incoming_cnt++;
out:
return;
out_free:
conn_free(conn);
out_close:
close(fd);
goto out;
}
static void __set_fd(int idx, int fd)
{
poll_array[idx].fd = fd;
poll_array[idx].events = fd ? POLLIN : 0;
}
void isns_set_fd(int isns, int scn_listen, int scn)
{
__set_fd(POLL_ISNS, isns);
__set_fd(POLL_SCN_LISTEN, scn_listen);
__set_fd(POLL_SCN, scn);
}
static void event_conn(struct connection *conn, struct pollfd *pollfd)
{
int res;
again:
switch (conn->iostate) {
case IOSTATE_READ_BHS:
case IOSTATE_READ_AHS_DATA:
read_again:
errno = 0; /* for the log_debug() */
res = read(pollfd->fd, conn->buffer, conn->rwsize);
if (res <= 0) {
log_debug(1, "read(%u, %p, %u) returned %d, errno=%u",
pollfd->fd, conn->buffer, conn->rwsize, res, errno);
if (res == 0 || (errno != EINTR && errno != EAGAIN)) {
conn->state = STATE_DROP;
goto out;
} else if (errno == EINTR)
goto read_again;
break;
}
conn->rwsize -= res;
conn->buffer += res;
if (conn->rwsize)
break;
switch (conn->iostate) {
case IOSTATE_READ_BHS:
conn->iostate = IOSTATE_READ_AHS_DATA;
conn->req.ahssize = conn->req.bhs.ahslength * 4;
conn->req.datasize = ((conn->req.bhs.datalength[0] << 16) +
(conn->req.bhs.datalength[1] << 8) +
conn->req.bhs.datalength[2]);
conn->rwsize = (conn->req.ahssize + conn->req.datasize + 3) & -4;
if (conn->rwsize > INCOMING_BUFSIZE) {
log_warning("Recv PDU with invalid size %d "
"(max: %d)", conn->rwsize,
INCOMING_BUFSIZE);
conn->state = STATE_DROP;
goto out;
}
if (conn->rwsize) {
if (!conn->req_buffer) {
conn->req_buffer = malloc(INCOMING_BUFSIZE);
if (!conn->req_buffer) {
log_error("Failed to alloc recv buffer");
conn->state = STATE_DROP;
goto out;
}
}
conn->buffer = conn->req_buffer;
conn->req.ahs = conn->buffer;
conn->req.data = conn->buffer + conn->req.ahssize;
goto read_again;
}
/* fall-through */
case IOSTATE_READ_AHS_DATA:
conn_write_pdu(conn);
pollfd->events = POLLOUT;
log_pdu(2, &conn->req);
if (!cmnd_execute(conn))
conn->state = STATE_EXIT;
if (conn->state == STATE_EXIT) {
/* We need to send response */
goto again;
}
break;
}
break;
case IOSTATE_WRITE_BHS:
case IOSTATE_WRITE_AHS:
case IOSTATE_WRITE_DATA:
write_again:
conn->cork_transmit(pollfd->fd);
res = write(pollfd->fd, conn->buffer, conn->rwsize);
if (res < 0) {
log_debug(1, "write(%u, %p, %u) returned %d, errno=%u",
pollfd->fd, conn->buffer, conn->rwsize, res, errno);
if (errno != EINTR && errno != EAGAIN) {
conn->state = STATE_DROP;
goto out;
} else if (errno == EINTR)
goto write_again;
break;
}
conn->rwsize -= res;
conn->buffer += res;
if (conn->rwsize)
goto write_again;
switch (conn->iostate) {
case IOSTATE_WRITE_BHS:
if (conn->rsp.ahssize) {
conn->iostate = IOSTATE_WRITE_AHS;
conn->buffer = conn->rsp.ahs;
conn->rwsize = conn->rsp.ahssize;
goto write_again;
}
/* fall-through */
case IOSTATE_WRITE_AHS:
if (conn->rsp.datasize) {
int o;
conn->iostate = IOSTATE_WRITE_DATA;
conn->buffer = conn->rsp.data;
conn->rwsize = conn->rsp.datasize;
o = conn->rwsize & 3;
if (o) {
for (o = 4 - o; o; o--)
*((u8 *) conn->buffer + conn->rwsize++) =
0;
}
goto write_again;
}
/* fall-through */
case IOSTATE_WRITE_DATA:
conn->uncork_transmit(pollfd->fd);
cmnd_finish(conn);
switch (conn->state) {
case STATE_KERNEL:
conn_pass_to_kern(conn, pollfd->fd);
if (conn->passed_to_kern)
conn->state = STATE_CLOSE;
else
conn->state = STATE_EXIT;
break;
case STATE_EXIT:
case STATE_CLOSE:
case STATE_DROP:
break;
default:
conn_read_pdu(conn);
pollfd->events = POLLIN;
break;
}
break;
}
break;
default:
log_error("illegal iostate %d for port %d!\n", conn->iostate,
pollfd->fd);
exit(1);
}
out:
return;
}
static void event_loop(void)
{
int res, i;
create_listen_socket(poll_array + POLL_LISTEN);
create_iser_listen_socket(poll_array);
poll_array[POLL_IPC].fd = ipc_fd;
poll_array[POLL_IPC].events = POLLIN;
poll_array[POLL_NL].fd = nl_fd;
poll_array[POLL_NL].events = POLLIN;
for (i = 0; i < INCOMING_MAX; i++) {
poll_array[POLL_INCOMING + i].fd = -1;
poll_array[POLL_INCOMING + i].events = 0;
incoming[i] = NULL;
}
close(init_report_pipe[0]);
res = 0;
if (log_daemon)
res = write(init_report_pipe[1], &res, sizeof(res));
close(init_report_pipe[1]);
while (1) {
if (!iscsi_enabled) {
handle_iscsi_events(nl_fd, true);
continue;
}
res = poll(poll_array, POLL_MAX, isns_timeout);
if (res == 0) {
isns_handle(1);
continue;
} else if (res < 0) {
if (errno == EINTR)
continue;
else if (errno == EINVAL)
log_error("%s: poll() failed with EINVAL. Should "
"you increase RLIMIT_NOFILE (ulimit -n)? "
"Or upgrade your kernel? Kernels below 2.6.19 "
"have a bug, which doesn't allow poll() "
"to work with >256 file descriptors. See "
"http://sourceforge.net/mailarchive/forum.php?"
"thread_name=9392A06CB0FDC847B3A530B3DC174E7B0"
"55F1EF3%40mse10be1.mse10.exchange.ms&forum_"
"name=scst-devel for more details. Alternatively, "
"you can decrease iscsi_scstd.c::INCOMING_MAX "
"constant to a lower value, e.g. 128, then "
"recompile and reinstall the user space part "
"of iSCSI-SCST.", __func__);
else
log_error("%s: poll() failed: %s", __func__,
strerror(errno));
exit(1);
}
for (i = 0; i < LISTEN_MAX; i++) {
if (poll_array[POLL_LISTEN + i].revents
&& incoming_cnt < INCOMING_MAX)
accept_connection(poll_array[POLL_LISTEN + i].fd);
}
if (poll_array[POLL_NL].revents)
handle_iscsi_events(nl_fd, false);
if (poll_array[POLL_IPC].revents)
iscsi_adm_request_handle(ipc_fd);
if (poll_array[POLL_ISNS].revents)
isns_handle(0);
if (poll_array[POLL_SCN_LISTEN].revents)
isns_scn_handle(1);
if (poll_array[POLL_SCN].revents)
isns_scn_handle(0);
if (poll_array[POLL_ISER_LISTEN].revents)
iser_accept(poll_array[POLL_ISER_LISTEN].fd);
for (i = 0; i < INCOMING_MAX; i++) {
struct connection *conn = incoming[i];
struct pollfd *pollfd = &poll_array[POLL_INCOMING + i];
if (!conn || !pollfd->revents)
continue;
pollfd->revents = 0;
event_conn(conn, pollfd);
if ((conn->state == STATE_CLOSE) ||
(conn->state == STATE_EXIT) ||
(conn->state == STATE_DROP)) {
struct session *sess = conn->sess;
log_debug(1, "closing conn %p state=0x%x fd=%u",
conn, conn->state, pollfd->fd);
conn_free_pdu(conn);
close(pollfd->fd);
pollfd->fd = -1;
incoming[i] = NULL;
incoming_cnt--;
if (conn->state != STATE_CLOSE) {
if (conn->passed_to_kern) {
kernel_conn_destroy(conn->tid,
conn->sess->sid.id64,
conn->cid);
} else {
/*
* Check if session could not be established,
* but sessions count was already incremented
*/
if (!sess && conn->sessions_count_incremented)
conn->target->sessions_count--;
log_debug(1, "conn %p freed (sess %p, empty %d)",
conn, sess,
sess ? list_empty(&sess->conn_list) : -1);
conn_free(conn);
if (sess && list_empty(&sess->conn_list))
session_free(sess);
}
}
}
}
}
}
static void init_max_params(void)
{
if ((session_keys[key_max_recv_data_length].local_def != -1) ||
(session_keys[key_max_recv_data_length].max != -1) ||
(session_keys[key_max_xmit_data_length].local_def != -1) ||
(session_keys[key_max_xmit_data_length].max != -1) ||
(session_keys[key_max_burst_length].local_def != -1) ||
(session_keys[key_max_burst_length].max != -1) ||
(session_keys[key_first_burst_length].max != -1)) {
log_error("Wrong session_keys initialization");
exit(-1);
}
/* QueuedCommands */
target_keys[key_queued_cmnds].local_def = min((int)target_keys[key_queued_cmnds].local_def,
iscsi_init_params.max_queued_cmds);
target_keys[key_queued_cmnds].max = min((int)target_keys[key_queued_cmnds].max,
iscsi_init_params.max_queued_cmds);
target_keys[key_queued_cmnds].min = min((int)target_keys[key_queued_cmnds].min,
iscsi_init_params.max_queued_cmds);
/* MaxRecvDataSegmentLength */
session_keys[key_max_recv_data_length].local_def = iscsi_init_params.max_data_seg_len;
session_keys[key_max_recv_data_length].max = iscsi_init_params.max_data_seg_len;
/* MaxXmitDataSegmentLength */
session_keys[key_max_xmit_data_length].local_def = iscsi_init_params.max_data_seg_len;
session_keys[key_max_xmit_data_length].max = iscsi_init_params.max_data_seg_len;
/* MaxBurstLength */
session_keys[key_max_burst_length].local_def = iscsi_init_params.max_data_seg_len;
session_keys[key_max_burst_length].max = iscsi_init_params.max_data_seg_len;
/* FirstBurstLength */
session_keys[key_first_burst_length].local_def =
min((int)session_keys[key_first_burst_length].local_def,
iscsi_init_params.max_data_seg_len);
session_keys[key_first_burst_length].max = iscsi_init_params.max_data_seg_len;
return;
}
int main(int argc, char **argv)
{
int ch, longindex;
char *config = NULL;
uid_t uid = 0;
gid_t gid = 0;
int err;
if (pipe(init_report_pipe) == -1) {
perror("pipe failed");
exit(-1);
}
/*
* Otherwise we could die in some later write() during the event_loop()
* instead of getting EPIPE!
*
* The effects of signal(2) in a multithreaded process are unspecified,
* so use sigaction(2) instead.
*/
struct sigaction act = (struct sigaction) { .sa_handler = SIG_IGN };
int rc = sigaction(SIGPIPE, &act, NULL);
assert(rc == 0);
while ((ch = getopt_long(argc, argv, "c:fd:s:u:g:a:p:vh", long_options, &longindex)) >= 0) {
switch (ch) {
case 'c':
config = optarg;
break;
case 'f':
log_daemon = 0;
break;
case 'd':
log_level = strtol(optarg, NULL, 0);
break;
case 'u':
uid = strtoul(optarg, NULL, 0);
break;
case 'g':
gid = strtoul(optarg, NULL, 0);
break;
case 'a':
server_address = strdup(optarg);
if (server_address == NULL) {
perror("strdup failed");
exit(-1);
}
break;
case 'p':
server_port = (uint16_t)strtoul(optarg, NULL, 0);
break;
case 'v':
printf("%s version %s\n", program_name, ISCSI_VERSION_STRING);
exit(0);
break;
case 'h':
usage(0);
break;
default:
usage(1);
break;
}
}
if ((ctrl_fd = kernel_open()) < 0)
exit(-1);
init_max_params();
if ((nl_fd = nl_open()) < 0) {
perror("netlink open failed");
exit(-1);
};
err = kernel_attr_add(NULL, ISCSI_ISNS_SERVER_ATTR_NAME,
S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR, 0);
if (err != 0)
exit(err);
err = kernel_attr_add(NULL, ISCSI_ENABLED_ATTR_NAME,
S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR, 0);
if (err != 0)
exit(err);
err = kernel_attr_add(NULL, ISCSI_ISNS_ENTITY_ATTR_NAME,
S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR, 0);
if (err != 0)
exit(err);
if ((ipc_fd = iscsi_adm_request_listen()) < 0) {
perror("Opening AF_LOCAL socket failed");
exit(-1);
}
log_init();
if (log_daemon) {
char buf[64];
pid_t pid;
int fd;
fd = open("/var/run/iscsi-scstd.pid", O_WRONLY|O_CREAT, 0644);
if (fd < 0) {
log_error("unable to create pid file");
exit(1);
}
pid = fork();
if (pid < 0) {
log_error("starting daemon failed");
exit(1);
} else if (pid) {
int res = -1;
close(init_report_pipe[1]);
if (read(init_report_pipe[0], &res, sizeof(res)) < sizeof(res))
exit(-1);
else
exit(res);
}
if (chdir("/") < 0) {
log_error("failed to set working dir to /: %m");
exit(1);
}
if (lockf(fd, F_TLOCK, 0) < 0) {
log_error("unable to lock pid file");
exit(1);
}
if (ftruncate(fd, 0) < 0) {
log_error("failed to ftruncate the PID file: %m");
exit(1);
}
sprintf(buf, "%d\n", getpid());
if (write(fd, buf, strlen(buf)) < strlen(buf)) {
log_error("failed to write PID to PID file: %m");
exit(1);
}
close(0);
open("/dev/null", O_RDWR);
dup2(0, 1);
dup2(0, 2);
setsid();
}
err = config_load(config);
if (err != 0)
exit(1);
if (gid && setgid(gid) < 0)
log_error("setgid failed: %s", strerror(errno));
if (uid && setuid(uid) < 0)
log_error("setuid failed: %s", strerror(errno));
event_loop();
return 0;
}