main.c revision 36c5fee33fa8b822175d410202aebcf592c8d342
/*
* 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
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#define FD_SETSIZE 65536
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
#include <stdint.h>
#include <dirent.h>
#include <fcntl.h>
#include <assert.h>
#include <errno.h>
#include <door.h>
#include <iscsi_door.h>
#include <signal.h>
#include <siginfo.h>
#include <sys/ethernet.h>
#include <libscf.h>
#include <syslog.h>
#include <synch.h>
#include <libxml/xmlreader.h>
#include <sys/resource.h>
#include <syslog.h>
#include "queue.h"
#include "port.h"
#include "iscsi_conn.h"
#include "target.h"
#include "xml.h"
#include "utility.h"
#include "iscsi_ffp.h"
#include "errcode.h"
#include "t10.h"
#define EMPTY_CONFIG "<config version='1.0'>\n</config>\n"
/* ---- Forward declarations ---- */
/* ---- Global configuration data. ---- */
char *target_basedir = NULL;
char *target_log = DEFAULT_TARGET_LOG;
char *config_file = DEFAULT_CONFIG_LOCATION;
int targets_vers_maj,
int dbg_lvl = 0;
/*
* door_return doesn't have a means to free the memory that's passed in
* which means the program must either use the buffer space provided as
* the argument to the service routine or allocate it's own and create
* a leak. Since the argument to the service routine is likely to be a
* fairly small buffer it may not be able to hold the results. So, the
* daemon which already has the results supplies that value to the door_return
* and sets up a request to collect that memory later. This structure
* empty_garbage(), and delayed_free() are used to recoupe the memory.
*/
typedef struct garbage_can {
char *g_buf;
int g_timo;
typedef struct var_table {
char *v_name;
int *v_value;
} var_table_t;
typedef struct cmd_table {
char *c_name;
} cmd_table_t;
admin_table_t admin_prop_list[] = {
{XML_ELEMENT_CHAPSECRET, 0},
{XML_ELEMENT_CHAPNAME, 0},
{XML_ELEMENT_RAD_ACCESS, 0},
{XML_ELEMENT_RAD_SECRET, 0},
{XML_ELEMENT_ISNS_ACCESS, 0},
{XML_ELEMENT_FAST, 0},
{0, 0}
};
/*
* Global variables which can be set via the management XML interface
* with the syntax of "<variable><dbg_lvl>0x033</dbg_lvl></variable>"
*/
var_table_t var_table[] = {
{ "dbg_lvl", &dbg_lvl },
{ "qlog_lvl", &qlog_lvl },
/* ---- End of Table marker ---- */
{ NULL, 0 }
};
/*
* Commands which are run via the management XML interface
*/
cmd_table_t cmd_table[] = {
{ "variable", variable_handler },
{ "create", create_func },
{ "modify", modify_func },
{ "delete", remove_func },
{ "list", list_func },
/* ---- End of Table marker ---- */
};
/*
* []----
* | scan_for_luns -- scan target for missing luns
* |
* | Checks to see if there are any LUs not in the target node which
* | exist in the target directory and adds them. Also checks to see
* | if there are LUs in the target node which are not present in the
* | directory and removes them.
* |
* | NOTE: This routine expects that the targ_dir has already been
* | verified to exist and is a directory
* []----
*/
static void
{
int l;
NULL) {
/*
* Most likely have an older configuration file that
* didn't list the LUs assocated with each target. No problem.
* Create a node and add it to the target.
*/
changes_made = True;
} else {
/*
* Build up a bit mask of the current LU set. This will be
* used to see if a LU already exists or not.
* When a LU is found the bit will be checked. If it's
* set then we know the current tree is correct for that
* LU and the bit will be cleared. If it's not set we're
* missing that LU and it will be added.
* Once we've got through the list any bits that are still
* set means that LU node in the tree should be removed.
*/
NULL) {
}
}
continue;
/* ---- Look for the LU backing store files ---- */
continue;
changes_made = True;
} else
}
for (l = 0; l < FD_SETSIZE; l++) {
changes_made = True;
}
}
if (changes_made == True)
(void) update_config_targets(NULL);
}
/*
* []----
* | process_target_config -- Load up the targets into memory
* []----
*/
{
xmlTextReaderPtr r = NULL;
char path[MAXPATHLEN],
int xml_fd = -1;
if (target_basedir != NULL) {
/*
* The target base directory has been set, but no
* longer exists which means someone has removed it
* behind our back. Obviously something bad has
* occurred, but we should attempt to start anyway and
* do so with an empty configuration.
*/
"Previous target directory (%s) has been removed",
} else {
target_basedir, "config.xml");
/*
* No existing configuration, but we have
* the target directory so attempt to create
* an empty configuration file.
* If the open or write fail there's a
* serious problem which needs attention.
*/
"Can not create empty "
"configuration file in %s",
return (False);
}
strlen(EMPTY_CONFIG)) !=
strlen(EMPTY_CONFIG)) {
"configuration file in %s", path);
return (False);
}
}
}
} else {
}
if (r != NULL) {
while (xmlTextReaderRead(r) == 1) {
break;
}
/*
* Validate the configuration file has the appropriate
* version number.
*/
&targets_vers_min) == False) {
return (False);
}
continue;
}
continue;
}
continue;
}
}
if (xml_fd != -1)
return (True);
} else {
if (xml_fd != -1)
/*
* NOTE: Look at sending a syslog message or maybe
* something to FMA.
*/
return (False);
}
}
/*
* []----
* | process_config -- parse the main configuration file
* |
* | Everything in the configuratin file is optional. That's because
* | the management CLI can set the value to everything and update
* | the configuration.
* []----
*/
static Boolean_t
process_config(char *file)
{
int ret,
xml_fd = -1;
#ifndef lint
#endif
return (False);
return (False);
}
if (r != NULL) {
ret = xmlTextReaderRead(r);
while (ret == 1) {
break;
}
ret = xmlTextReaderRead(r);
}
/*
* Validate the configuration file has the appropriate
* version number.
*/
False) {
return (False);
}
/*
* The base directory is optional in the sense that the daemon
* can start without it, but the daemon can't really do
* anything until the administrator sets the value.
*/
/*
* These are optional settings for the target. Each of
* these has a default value which can be overwritten in
* the configuration file.
*/
&target_log);
&iscsi_port);
&disable_tpgs);
main_config = node;
if (xml_fd != -1)
return (True);
} else {
if (xml_fd != -1)
return (False);
}
}
/*
* []----
* | logout_cleanup -- see if the initiator did what was requested
* |
* | When a target issues an asynchrouns event with the code set to
* | "logout requested" the initiator is supposed to respond with
* | a LogoutRequested PDU within a certain amount of time. If it
* | fails to do so, it's the targets responsibility to clean up.
* | After ASYNC_LOGOUT_TIMEOUT seconds (currently 10) we look to
* | see if any connections are still in the S7_LOGOUT_REQUESTED
* | state. If so, reissue the management request to logout which
* | will cause the connections to close.
* []----
*/
static void *
logout_cleanup(void *v)
{
int msg_sent,
i;
char *targ = (char *)v;
extern pthread_mutex_t port_mutex;
bzero(&m, sizeof (m));
m.m_request = mgmt_logout;
m.m_q = queue_alloc();
msg_sent = 0;
(void) sleep(ASYNC_LOGOUT_TIMEOUT);
(void) pthread_mutex_lock(&port_mutex);
msg_mgmt_rqst, &m);
msg_sent++;
}
}
(void) pthread_mutex_unlock(&port_mutex);
/*
* Wait to see if they received the message.
*/
for (i = 0; i < msg_sent; i++)
return ((void *)0);
}
void
logout_targ(char *targ)
{
int i,
extern pthread_mutex_t port_mutex;
/*
* Now we look for connections to this target and issue
* a request to asynchronously logout.
*/
bzero(&m, sizeof (m));
m.m_request = mgmt_logout;
m.m_q = queue_alloc();
msg_sent = 0;
(void) pthread_mutex_lock(&port_mutex);
msg_sent++;
}
}
(void) pthread_mutex_unlock(&port_mutex);
/* ---- Wait to see if they received the message. ---- */
for (i = 0; i < msg_sent; i++)
/* ---- Start housecleaning thread ---- */
}
/*
* [] ---- XML Management Handlers ---- []
*/
/*
* []----
* | variable_handler -- used to set a couple of internal global variables
* []----
*/
/*ARGSUSED*/
void
{
var_table_t *v;
xml_node_t *c;
break;
}
}
}
}
/*
* []----
* | parse_xml -- incoming management requests are sent here for processing
* []----
*/
static void
{
cmd_table_t *c;
return;
}
break;
} else {
}
}
/*
* []----
* | empty_garbage -- sleep for the requested time and then release the memory
* []----
*/
static void *
empty_garbage(void *v)
{
garbage_can_t *g = (garbage_can_t *)v;
free(g);
return (NULL);
}
/*
* []----
* | delayed_free -- set things up to free a chunk of memory later
* []----
*/
static void
{
garbage_can_t *g;
/*
* if we can't get memory we're pretty much sunk.
*/
return;
}
/*ARGSUSED*/
static void
{
bzero(&m, sizeof (m));
while (xmlTextReaderRead(r)) {
break;
}
m.m_q = queue_alloc();
m.m_request = mgmt_parse_xml;
m.m_targ_name = NULL;
}
/*
* Check to see if the response can fit into the
* incoming argument buffer. If so, copy the response
* to that buffer so that we can free the data.
* If it's not big enough we'll return the pointer to
* the message response and have to free the data
* at another time.
*/
} else {
}
} else {
}
} else {
}
}
}
/*
* []----
* | setup_door -- Create a door portal for management requests
* |
* | First check to see if another daemon is already running by attempting
* | to send an empty request to the door. If successful it means this
* | daemon should exit.
* []----
*/
static void
{
int did,
fd;
struct stat s;
door_arg_t d;
/*
* There's at least a file with the same name as our
* door. Let's see if someone is currently answering
* by sending an empty XML request.
*/
d.data_ptr = "<config></config>";
d.desc_num = 0;
d.rsize = 0;
/*
* If the door_call succeeds that means another
* daemon is already running so let's just exit.
*/
exit(0);
}
}
exit(1);
}
int newfd;
exit(1);
}
}
exit(2);
}
}
int
{
char c,
*p,
target_queue_t *q;
switch (c) {
case 'c':
break;
case 'd':
break;
}
}
/*
* If the initiator closes the socket because of a protocol error
* or bad digest on the header packet we'll receive a SIGPIPE if we're
* in the middle of a write operation. There's no need to receive
* a signal when a -1 from the write will handle things correctly.
* So, ignore SIGPIPE's.
*/
/*
* Look at the function lu_buserr_handler() above to see the details
* of why we need to handle segmentation violations.
*/
perror("sigaction");
}
if (process_target_config() == False)
&daemonize);
switch (fork()) {
case 0:
/* ---- I'm the daemon, continue running ---- */
break;
case -1:
/* ---- Failed to fork!. Trouble ---- */
default:
}
closefrom(0);
}
q = queue_alloc();
/*
* Initialize the various subsystems. In most cases these are
* just initializing mutexs.
*/
session_init();
t10_init(q);
port_init();
queue_init();
util_init();
/*
* If there's no MAC address currently available don't worry about
* it. The first time an initiator connects the SAM-3 layer will
* attempt to create a GUID and force another look for a MAC address.
*/
if (if_find_mac(q) == False)
/*
* At a minimum we need two file descriptors for each target, one for
* the socket and one for the backing store. If there's more than one
* initiator attached to a given target than that number goes up by 1.
* Once we have multiple sessions per connection that to will cause
* an increase.
*/
"Can't set new limit for open files");
}
port1.port_mgmtq = q;
setup_door(q, door_name);
port2.port_mgmtq = q;
}
do {
msg = queue_message_get(q);
case msg_log:
*p = '\0';
break;
msg_log, p);
if (console_output == True)
(void) printf("%s\n", p);
break;
case msg_mgmt_rqst:
break;
case msg_status:
/*
* NOTE:
* These are real error conditons being sent from
* the other threads and should be logged in
* some manner, either syslog() or using a FMA
* interface.
*/
(void) printf("STATUS: %s\n", p);
break;
default:
break;
}
/*CONSTANTCONDITION*/
} while (1);
return (0);
}