lm.c revision cee0fb94c0d4227de0a00efc162fb2739844b641
/*
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <lm.h>
#include <lm_proto.h>
#include <net_cfg_service.h>
/* Globals */
/* exists per library manager. */
/* of which cmds are waiting on responses from MM */
/* This mutex allows only one cmd to be sent */
/* to MM at a time until MM responses with */
/* a accept or unaccept response */
/* occuring at one time from different */
/* threads */
/* be used when LM exits */
/* error was detected. LM will shutdown */
/* processing and exit */
/* Structure that is updated to contain the */
/* dynamic entry points to the commands that */
/* interface with a specific library */
lm_cmdData_t lm_cmdData[] = {
/* Commands that also need a general */
/* part as well as a library specific part */
/* This general part will get called frist */
/* and then the library specific part will be called */
static void lm_cmd_handler(lm_queue_ele_t *);
/*
* signal_waiter
*
* Parameters:
* None - arg exists only to match argument in pthread_create
*
* This routine is spawned as a separate thread created to wait for masked
* signals. Any masked signaled will be delivered to this thread.
*
* Return Values:
* None - Return value exists only to match return value in pthread_create
*/
static void*
/* LINTED arg in signal_waiter (E_FUNC_ARG_UNUSED) */
signal_waiter(void *arg)
{
int signum;
/* LINTED constant in conditional context */
while (1) {
switch (signum) {
case SIGHUP:
"signal, ignoring signal");
break;
case SIGINT:
"signal, restarting LM");
break;
case SIGPIPE:
"signal, shutting down LM");
break;
case SIGTERM:
"signal, shutting down LM");
break;
default:
"signal_waiter: Received a signal that "
"lm does not handle - %d", signum);
break;
}
}
/* LINTED Function has no return statement */
}
/*
* set_signal_handling()
*
* Paramters:
* None
*
* Mask signals to catch. All threads inherit the signal mask
* from their creator (this thread). The semantics of sigwait
* (see signal_waiter function) requires that all threads have
* the signal masked. Otherwise a signal that arrives while the
* signal_waiter is not blocked in sigwait might be delivered to
* another thread.
*
* Return Values:
* None
*
*/
static void
{
int rc;
(void) sigemptyset(&signalSet);
}
/*
* Create signal waiter thread.
*/
NULL)) != 0) {
"failed to create signal_waiter thread, rc - %d\n",
}
}
/*
* lm_initialize()
*
* Parameters:
* - cfg_name: Name of network configuration file. This file contains
* necessary configuration information to connect to the MM
* - lm_daemon_mode: Indicates if LM should be started in daemon
* mode or in a standalone execution mode.
* Default is daemon mode.
*
* Globals:
* - lm: The global library management structure. Only one exists
* per library manager. Using a global verses passing it
* into all the different routines that need it.
*
* This function will initialize LM.
* - Sets up for signal processing.
* - Sets up the work queue for processing LMPM cmds.
* - Initializes mutexes.
* - Establishes the initial connection to MM.
* - Sets up the correct set of commands to support the library type.
*
* Return Values:
* MMS_OK: Function completed sucessfully.
* LM_ERROR: Function had a non recoverable error.
*
* Errors are logged in the LM's mms_trace log or
* syslog file.
*/
static int
{
int err;
int rc; /* return code */
int i;
char ebuf[MMS_EBUF_LEN];
char *hello;
char *welcome;
char *corename;
/* Set up signal handling */
/* Obtain information from config file in order */
/* be able to connect to MM */
}
}
}
}
}
if (lm_daemon_mode) {
MMS_HERE);
if (getuid() != 0) {
"daemon by a process that is not root\n");
}
/* Close all open file descriptors and redirect stdin, */
/* becoming a standalone daemon */
for (i = 0; i < OPEN_MAX; i++)
(void) close(i);
if (setsid() < 0) {
"%s, make sure LM is started by root\n",
}
(void) umask(0);
/* Move to where core files will be placed */
}
/* Check to see how many core files exist */
}
/* Trace filename should indicate which library */
/* the mms_trace file is for */
"%s/%s.debug", LM_TRACE_DIR,
"mms_trace file %s, LM is unable to mms_trace "
"messages.\n",
}
} else {
(void) printf("%s:%d lm_init: "
"Unable to set mms_trace file to "
"stderr for non daemon mode\n", MMS_HERE);
}
}
#ifdef MMS_OPENSSL
}
#endif /* MMS_OPENSSL */
/* set tracing level */
#ifdef MMSDEBUG
#ifdef JDP
(void) mms_trace_filter(MMS_SEV_DEVP);
#else
(void) mms_trace_filter(MMS_SEV_DEBUG);
#endif /* JDP */
#else
(void) mms_trace_filter(MMS_SEV_WARN);
#endif
/* Initialize work queue */
!= 0) {
return (LM_ERROR);
}
/* Initialize mutex for cmd waiting on an accept response */
return (LM_ERROR);
}
/* Initialize mutex for controling writes over MM socket */
"lm_init: write_mutex_init failed, errno - %s",
return (LM_ERROR);
}
/* Initialize mutex for accessing response queue structure */
return (LM_ERROR);
}
/* XXX STILL NEED RESPONSE TO MMS_ERROR RETURNS AS TO WHICH */
/* ONES SHOULD CAUSE AN EXIT WITH A RESTART AND WHICH */
/* ONES SHOULD CAUSE A NON RESTART */
"lm_init: LM's mms_mmconnect failed, code - %s",
return (LM_ERROR);
}
return (LM_OK);
}
/*
*
* lm_cmd_intrp()
*
* Parameters:
* - cmd Parse tree of LMPM command sent to LM by MM
* - tid A return ptr to string containing task id from cmd
*
* Globals:
* - None.
*
* This function will determine which LMPM command to process. It pulls
* the command string from the cmd node of the parse tree.
*
* Return Values:
* - Enumeration of the command to process.
* - MMS_LM_E_DEVCMDILLEGAL If the cmd string was not a valid
* command. This should never occur
* unless lmpm_parse_buf() has a
* logic error.
*
*/
static int
{
int rc; /* Return code */
char *cmd_str; /* cmd to be processed */
/* We know at this point we have a syntaically correct cmd, and */
/* the cmd points to the cmd portion of the parse tree */
}
rc = LM_UNMOUNT;
}
}
}
}
}
rc = LM_C_ACTIVATE;
}
rc = LM_C_RESET;
}
}
rc = LM_BARRIER;
}
rc = LM_C_PRIVATE;
}
}
rc = LM_C_EVENT;
/* Return here for events, there is no taskid */
/* in an event */
return (rc);
}
else {
"lm_cmd_intrp: Command %s is not a supported "
"LMPM command", cmd_str);
/* Return unsupported command error response to MM */
}
"LMPM %s command", cmd_str);
/* Return an unacceptable to MM for the command */
return (rc);
}
"LMPM %s command", cmd_str);
/* Return an unacceptable to MM for this command */
return (rc);
}
return (rc);
}
/*
* lm_input_handler()
*
* Parameters:
* - None
*
* Globals:
* lm The global library management structure. Only one exists
* per library manager. Using a global verses passing it
* into all the different routines that need it.
*
* This function will handle all input for LM from MM.
* - Read input from MM.
* - Parse input.
* - Determine if a response cmd. If so give response to the thread that
* is waiting on the response. Response can be a accept or finial.
* - If new command place command on work queue for processing threads.
*
* Return Values:
* NONE: This routine does not exit unless LM is told to shutdown or
* an internal error is detected. The lm_internal_error and
* lm_state variables control when this routine will return.
*/
static void
{
int rc; /* return code */
int class;
int code;
char *tid; /* new cmd's task id */
char msg_str[256];
char rsp_str[512];
/* Continue processing input until we are told by MM through */
/* an exit command to shutdown. Signals may also shut us down */
/* but at this point, we don't know which signals we are to */
/* accept and which ones we are to ignore */
/* LINTED constant in conditional context */
while (1) {
if (getppid() == 1) {
"that it's parnet process mmswcr has gone away");
return;
}
/* Setup a timer on the select so that */
/* we will kick out periodically to see if */
/* a signal from the watcher occurred */
tvp);
if (rc < 0) {
continue;
"has determined that the MM socket is no "
return;
} else {
"on socket to MM has failed with a "
return;
}
/* Timer expired see if a something occurred */
/* to cause LM to shutdown */
} else if (rc == 0) {
/* Check to see if LM was told to stop */
/* See if LM can exit */
lm_cmdq.lmq_counter == 0) {
"lm_input_handler: "
"LM told to exit and there are no "
"outstanding LMPM commands left to "
"process, shutting down LM");
return;
}
}
/* Check to see if one of the command */
/* processing threads encountered an internal */
/* error */
if (lm_internal_error) {
"encountered an unrecoverable internal "
"error, shutting down LM");
return;
}
continue;
}
/* Check to see if one of the command processing */
/* threads encountered an internal error */
if (lm_internal_error) {
"lm_input_handler: LM has encountered "
"an unrecoverable internal error, shutting "
"down LM");
return;
}
/* Obtain input from MM */
"tripped on a file descriptor, but not the one "
"LM has opened with MM");
continue;
}
if (rc == 0) {
"routine mms_reader() has indicated that "
"the MM has disconnected");
} else
"routine mms_reader() failed with a "
"return code of %d", rc);
return;
}
"failed on MM input:\n%s", input);
== LM_NOMEM) {
/* NOMEM error, retry parsing again */
/* Second attempt at parsing had an */
/* error, return error */
"lmpm_parser() failed on second "
"attempt to parse input after a "
"first attempt failed due to lack "
"of memory");
} else {
"Able to obtain memory to parse "
"new input on second attempt of "
"parse");
goto parse_ok;
}
}
switch (rc) {
case LM_NOMEM:
sizeof (msg_str), LM_7025_MSG);
sizeof (rsp_str), LM_MSG_PARSE,
msg_str);
break;
case LM_SYNTAX_ERR:
sizeof (msg_str), LM_7024_MSG);
sizeof (rsp_str), LM_MSG_PARSE,
msg_str);
break;
case LM_SYNTAX_RSP:
sizeof (msg_str), LM_7023_MSG);
sizeof (rsp_str), LM_MSG_PARSE,
msg_str);
break;
case LM_SYNTAX_CMD:
MMS_PN_CMD, NULL);
sizeof (msg_str), LM_7005_MSG,
mms_pn_token(node));
sizeof (rsp_str), LM_MSG_PARSE,
msg_str);
break;
default:
sizeof (msg_str), LM_7026_MSG);
sizeof (rsp_str), LM_MSG_PARSE,
msg_str);
break;
}
/* Send message to MM indicating the type */
/* of parse error detected. Do no wait for */
/* a response to the message */
"parser error message failed");
return;
}
/* This should never occur unless parser generated */
/* a invalid parse tree or memory corruption */
"found in MM input:\n%s", input);
return;
}
/* Have received a response for a LMPL command */
/* that the LM sent to the MM, update the response */
/* command structure with the necessary response */
/* and wake up command processing thread waiting on */
/* the response */
/* NOTE: We do not destory the node "cmd" here. It is */
/* the job of function that issued the LMPL command */
/* to free the cmd memory when it is done processing */
/* the response */
"response:\n%s", input);
if (lm_handle_response(cmd)) {
"lm_handle_response was not able to "
"handle response cleanly");
return;
}
continue;
}
/* At this point we can assume that we have a new LMPM */
/* command Determine the command to process send an */
/* accept/unacceptable response and place command onto work */
/* queue for worker threads */
/* NOTE: We do not destory the node "cmd" here. It is the job */
/* of lm_cmd_handler() function to free the cmd memory when */
/* it is done processing the cmd */
"lm_input_handler: Process LMPM command:\n%s",
input);
/* Check to see if LMPM command is an event */
if (rc == LM_C_EVENT) {
if (lm_handle_event(cmd)) {
"handle_event failed with internal "
"error");
return;
}
continue;
}
/* If lm_cmd_intrp was unable to obtain a valid */
/* task id for the command we need to send an */
/* unaccept response for the command. There is */
/* an issue with lmpm_parse_buf since it did not */
/* issue an error for the command and yet we could */
/* not find the task id for the command */
mms_pn_token(cmd));
"lm_input_handler: Sending unacceptable "
"response for LMPM %s command:\n%s",
lm_write_mutex)) {
"unacceptable response failed");
return;
}
/* Obtain next command */
continue;
}
/* Send accept response for command */
"lm_input_handler: Sending accept response:\n%s",
rsp_str);
"response failed");
return;
}
/* If processing an exit cmd, */
/* SIGTERM, or SIGINT, the state of lm gets set */
/* to LM_STOP. Any commands that are received */
/* after one of the above, will be aborted with an */
/* error response */
mms_pn_token(cmd));
msg_str);
"lm_input_handler: LM in stop state "
"sending error final response for new command:"
"\n%s", rsp_str);
lm_write_mutex)) {
"error response failed");
return;
}
continue;
}
code = 0;
class = 0;
switch (rc) {
/* Commands that can be executed with LM */
/* in any state execpt stop. Stop state */
/* is detected above, thus not in stop state */
case LM_C_RESET:
case LM_C_EXIT:
case LM_C_PRIVATE:
break;
/* Commands that can be executed with LM */
/* in not-ready, broken, or disconnected */
case LM_BARRIER:
case LM_CANCEL:
"lm_input_handler: LM "
"is not in a valid state to "
"process %s command, state "
"- 0x%x",
sizeof (msg_str), LM_7001_MSG,
}
break;
/* Commands that can be executed with LM */
/* only in not-active, disconnected, or */
/* active states */
case LM_C_ACTIVATE:
"lm_input_handler: LM "
"is not in a valid state to "
"process %s command, state "
"- 0x%x",
sizeof (msg_str), LM_7001_MSG,
}
break;
/* Commands that can only be executed with */
/* LM in the active state */
case LM_MOUNT:
case LM_UNMOUNT:
case LM_MOVE:
case LM_INJECT:
case LM_SCAN:
case LM_EJECT:
/* Make sure LM is active */
"lm_input_handler: LM "
"is not in a valid state to "
"process %s command, state "
"- 0x%x",
sizeof (msg_str), LM_7001_MSG,
}
break;
case MMS_LM_E_DEVCMDILLEGAL:
"lm_input_handler: cmd_intrp "
"returned invalid command found, send "
"error final response");
break;
}
if (code) {
"final response to LMPM %s command:\n%s",
lm_write_mutex)) {
"error final response failed");
return;
}
continue;
}
/* Free command memory */
!= LM_OK) {
"command to LM's work queue failed",
mms_pn_token(cmd));
return;
}
}
}
/*
*
* lm_cmd_handler()
*
* Parameters:
* - ce Structure to a work queue element. The structure
* contains the necessary elements to process a command,
* one being the parse tree of the command itself. See
* lm.h for a complete description of the elements of
* the structure.
*
* This function is what process each of the commands issued to LM by
* MM. This function is actually executed as a spearate thread within LM.
* The queue_add function called by lm_input_handler() is what starts this
* function as a thread. The queue_add() function will start up to a
* max number of these threads to process individual commands. See the
* the queue functions for a better description of how the work queue is
* used.
*
* The final response is written out when the command processing returns.
*
* Return Values:
* - None. All error encountered need to be handled at a global
* level. Errors found in this routine are issues with
* system problems and most likely cause the LM to abort
* since recovery in most cases cannot be done correctly.
* The cmd was successfully parsed and thus should
* represent a valid command.
*
*/
static void
{
/* Parse tree of cmd being processed */
int rc;
/* Call cmd processing routine */
&ret_msg[0]);
/* Each command will return either a successful finial */
/* response or an error finial response message */
"lm_cmd_handler: Processing of LMPM %s command "
else
"lm_cmd_handler: Processing of LMPM %s command "
/* Free memory of parse tree of LMPM cmd */
/* Event or internal commands do not get a acknowledge or */
/* final response */
return;
/* write out final response message for LMPM command */
"%s command to MM failed",
}
}
/*
*
* Main routine for Library Manager.
*
* Arguments:
* Arg 1: Path to configuration file which this instance of LM is to
* use to obtain its configuration information from.
* Arg 2: Indicates if LM should be run in a non daemon mode. If set
* then LM can run as a standalone process. If run in daemon
* mode (default) LM must be run as root.
*
* Description:
* Each instance of an LM is used to control an actual physical instance
* of a library. Multiple LM instances for a library can exist at the
* same time, but only one can be active at a given time.
*/
int
{
char msg_str[256];
char msg_cmd[512];
/* If a second parameter is passed then this */
/* indicates that LM is being run in standalone mode */
if (argc == 3)
lm_daemon_mode = 0;
if (argc == 1) {
"Unable to start LM\n.", MMS_HERE);
}
"LM initialization failed, Unable to start LM.");
} else
if (lm_internal_error == LM_NO_MM) {
"Exiting LM because connection to MM is gone");
}
/* send a message to MM indicating LM is shutting down */
if (lm_internal_error) {
if (lm_internal_error == LM_NO_WCR)
else
if (exit_code == LM_SIG_NRESTART)
else
}
/* Need sleep in order for MM to complete processing of */
/* the LMPL message */
(void) sleep(5);
return (exit_code);
}