summaryrefslogtreecommitdiff
path: root/usr/src/cmd/rcm_daemon/common/rcm_script.c
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/rcm_daemon/common/rcm_script.c
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/rcm_daemon/common/rcm_script.c')
-rw-r--r--usr/src/cmd/rcm_daemon/common/rcm_script.c2629
1 files changed, 2629 insertions, 0 deletions
diff --git a/usr/src/cmd/rcm_daemon/common/rcm_script.c b/usr/src/cmd/rcm_daemon/common/rcm_script.c
new file mode 100644
index 0000000000..094bc6288e
--- /dev/null
+++ b/usr/src/cmd/rcm_daemon/common/rcm_script.c
@@ -0,0 +1,2629 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * rcm scripting module:
+ *
+ * This module implements rcm scripting interfaces.
+ * It translates rcm module based interfaces to rcm script based
+ * interfaces.
+ *
+ * Entry points:
+ *
+ * int script_main_init()
+ * Initialize the rcm scripting framework.
+ * Called during the rcm daemon initialization
+ *
+ * int script_main_fini()
+ * Called at the time of the rcm daemon exit.
+ *
+ * struct rcm_mod_ops *script_init(module_t *module)
+ * Initialize the given script.
+ * module->name contains the name of the script.
+ * Called at the time of loading scripts.
+ * Semantics are similar to module init.
+ *
+ * char *script_info(module_t *module)
+ * Called when the rcm daemon wishes to get the script information.
+ * module->name contains the name of the script.
+ * Semantics are similar to module info.
+ *
+ * int script_fini(module_t *module)
+ * Called before removing the script.
+ * module->name contains the name of the script.
+ * Semantics are similar to module fini.
+ *
+ * In addition to the above entry points rcm_mod_ops structure contains
+ * the other entry points. A pointer to this structure is returned when
+ * script_init() is called.
+ */
+
+#include "rcm_impl.h"
+#include "rcm_script_impl.h"
+#include <sys/resource.h>
+#include <procfs.h>
+#include <sys/proc.h>
+#include <ctype.h>
+
+/*
+ * All rcm scripting commands are enumerated here.
+ * NOTE: command positions in script_cmd_id_t and script_cmd_name must match.
+ */
+typedef enum {
+ C_SCRIPTINFO,
+ C_RESOURCEINFO,
+ C_REGISTER,
+ C_QUERYREMOVE,
+ C_PREREMOVE,
+ C_POSTREMOVE,
+ C_UNDOREMOVE,
+ C_QUERYCAPACITY,
+ C_PRECAPACITY,
+ C_POSTCAPACITY,
+ C_QUERYSUSPEND,
+ C_PRESUSPEND,
+ C_POSTRESUME,
+ C_CANCELSUSPEND
+} script_cmd_id_t;
+
+/* NOTE: command positions in script_cmd_id_t and script_cmd_name must match */
+static char *script_cmd_name[] = {
+ "scriptinfo",
+ "resourceinfo",
+ "register",
+ "queryremove",
+ "preremove",
+ "postremove",
+ "undoremove",
+ "querycapacity",
+ "precapacity",
+ "postcapacity",
+ "querysuspend",
+ "presuspend",
+ "postresume",
+ "cancelsuspend",
+ NULL
+};
+
+/*
+ * All rcm scripting data items are enumerated here.
+ * NOTE: data item positions in script_data_item_id_t and
+ * script_data_item_name must match.
+ */
+typedef enum {
+ D_SCRIPT_VERSION,
+ D_SCRIPT_FUNC_INFO,
+ D_CMD_TIMEOUT,
+ D_RESOURCE_NAME,
+ D_RESOURCE_USAGE_INFO,
+ D_FAILURE_REASON,
+ D_LOG_ERR,
+ D_LOG_WARN,
+ D_LOG_INFO,
+ D_LOG_DEBUG
+} script_data_item_id_t;
+
+/*
+ * NOTE: data item positions in script_data_item_id_t and
+ * script_data_item_name must match.
+ */
+static const char *script_data_item_name[] = {
+ "rcm_script_version",
+ "rcm_script_func_info",
+ "rcm_cmd_timeout",
+ "rcm_resource_name",
+ "rcm_resource_usage_info",
+ "rcm_failure_reason",
+ "rcm_log_err",
+ "rcm_log_warn",
+ "rcm_log_info",
+ "rcm_log_debug",
+ NULL
+};
+
+/*
+ * Maximum number of rcm scripts that can run in parallel.
+ * RCM daemon has no limit on the number of scripts supported. But
+ * at most it runs script_max_parallelism number of scripts in parallel.
+ * For each running script rcm daemon consumes two file descriptors
+ * in order to communicate with the script via pipes.
+ * So maximum number of file descriptor entries consumed by rcm daemon
+ * on behalf of rcm scripts is "script_max_parallelism * 2"
+ */
+static const int script_max_parallelism = 64;
+
+/*
+ * semaphore to limit the number of rcm script processes running in
+ * parallel to script_max_parallelism.
+ */
+static sema_t script_process_sema;
+
+/* mutex to protect the any global data */
+static mutex_t script_lock;
+
+/* contains head to a queue of script_info structures */
+static rcm_queue_t script_info_q;
+
+/*
+ * This mmapped state file is used to store the process id and
+ * rcm script name of all currently running rcm scripts.
+ */
+static const char *script_ps_state_file = "/var/run/rcm_script_state";
+static state_file_descr_t script_ps_statefd;
+
+static char *script_env_noforce = "RCM_ENV_FORCE=FALSE";
+static char *script_env_force = "RCM_ENV_FORCE=TRUE";
+static char *script_env_interval = "RCM_ENV_INTERVAL=%ld";
+
+#define RSCR_TRACE RCM_TRACE1
+
+/* rcm script base environment */
+static char *script_env[MAX_ENV_PARAMS];
+
+struct rlimit file_limit;
+
+/* function prototypes */
+static void build_env(void);
+static void copy_env(char *[], char *[]);
+static void open_state_file(const char *, state_file_descr_t *, size_t, int,
+ uint32_t);
+static void truncate_state_file(state_file_descr_t *);
+static void close_state_file(const char *, state_file_descr_t *);
+static void grow_state_file(state_file_descr_t *);
+static void *get_state_element(state_file_descr_t *, int, int *);
+static void *allocate_state_element(state_file_descr_t *, int *);
+static void free_state_element(void *);
+static void script_ps_state_file_kill_pids(void);
+static void script_ps_state_file_add_entry(pid_t, char *);
+static void script_ps_state_file_remove_entry(pid_t);
+static int dname_to_id(char *);
+static void script_process_sema_wait(void);
+static int run_script(script_info_t *, char *[], char *[], char **);
+static int get_line(int fd, char *, char *, int, size_t *, time_t, int *);
+static void script_exited(script_info_t *);
+static int kill_pid(pid_t);
+static void kill_script(script_info_t *);
+static char *flags_to_name(int, char *, int);
+static void fill_argv(script_info_t *, char *[], char *);
+static void *read_stderr(script_info_t *);
+static int process_dataitem(script_info_t *, int, char *, char **);
+static int do_cmd(script_info_t *, char *[], char *[], char **);
+static int do_script_info(script_info_t *);
+static int do_dr(script_info_t *, char *[], char *[], char **);
+static int script_get_info(rcm_handle_t *, char *, pid_t, uint_t, char **,
+ char **, nvlist_t *, rcm_info_t **);
+static void add_for_unregister(script_info_t *);
+static void remove_from_unregister(script_info_t *, char *);
+static void complete_unregister(script_info_t *);
+static int script_register_interest(rcm_handle_t *);
+static void add_drreq(script_info_t *, char *);
+static void remove_drreq(script_info_t *, char *);
+static void remove_drreq_all(script_info_t *);
+static int script_request_offline(rcm_handle_t *, char *, pid_t, uint_t,
+ char **, rcm_info_t **);
+static int script_notify_online(rcm_handle_t *, char *, pid_t, uint_t,
+ char **, rcm_info_t **);
+static int script_notify_remove(rcm_handle_t *, char *, pid_t, uint_t,
+ char **, rcm_info_t **);
+static int script_request_suspend(rcm_handle_t *, char *, pid_t, timespec_t *,
+ uint_t, char **, rcm_info_t **);
+static int script_notify_resume(rcm_handle_t *, char *, pid_t, uint_t,
+ char **, rcm_info_t **);
+static capacity_descr_t *get_capacity_descr(char *);
+static int build_env_for_capacity(script_info_t *, char *, uint_t, nvlist_t *,
+ char *[], int *, char **);
+static int script_request_capacity_change(rcm_handle_t *, char *, pid_t,
+ uint_t, nvlist_t *, char **, rcm_info_t **);
+static int script_notify_capacity_change(rcm_handle_t *, char *, pid_t,
+ uint_t, nvlist_t *, char **, rcm_info_t **);
+static void log_msg(script_info_t *, int, char *);
+static char *dup_err(int, char *, ...);
+static void rcmscript_snprintf(char **, int *, char **, char *, ...);
+static char *rcmscript_strdup(char *);
+static void *rcmscript_malloc(size_t);
+static void *rcmscript_calloc(size_t, size_t);
+
+
+static struct rcm_mod_ops script_ops =
+{
+ RCM_MOD_OPS_VERSION,
+ script_register_interest, /* register */
+ script_register_interest, /* unregister */
+ script_get_info,
+ script_request_suspend,
+ script_notify_resume,
+ script_request_offline,
+ script_notify_online,
+ script_notify_remove,
+ script_request_capacity_change,
+ script_notify_capacity_change,
+ NULL
+};
+
+/*
+ * Messages fall into two categories:
+ * framework messages (MF_..)
+ * errors directly attributable to scripts (MS_..)
+ */
+#define MF_MEMORY_ALLOCATION_ERR \
+ gettext("rcm: failed to allocate memory: %1$s\n")
+#define MF_STATE_FILE_ERR \
+ gettext("rcm: state file error: %1$s: %2$s\n")
+#define MF_FUNC_CALL_ERR \
+ gettext("rcm: %1$s: %2$s\n")
+#define MF_NV_ERR \
+ gettext("rcm: required name-value parameters missing (%1$s)\n")
+#define MF_UNKNOWN_RSRC_ERR \
+ gettext("rcm: unknown resource name %1$s (%2$s)\n")
+#define MS_REGISTER_RSRC_ERR \
+ gettext("rcm script %1$s: failed to register %2$s\n")
+#define MS_REGISTER_ERR \
+ gettext("rcm script %1$s: register: %2$s\n")
+#define MS_SCRIPTINFO_ERR \
+ gettext("rcm script %1$s: scriptinfo: %2$s\n")
+#define MS_PROTOCOL_ERR \
+ gettext("rcm script %1$s: scripting protocol error\n")
+#define MS_TIMEOUT_ERR \
+ gettext("rcm script %1$s: timeout error\n")
+#define MS_UNSUPPORTED_VER \
+ gettext("rcm script %1$s: unsupported version %2$d\n")
+#define MS_SCRIPT_ERR \
+ gettext("rcm script %1$s: error: %2$s\n")
+#define MS_UNKNOWN_ERR \
+ gettext("rcm script %1$s: unknown error\n")
+#define MS_LOG_MSG \
+ gettext("rcm script %1$s: %2$s\n")
+
+
+/*
+ * Initialize rcm scripting framework.
+ * Called during initialization of rcm daemon.
+ */
+int
+script_main_init(void)
+{
+#define PS_STATE_FILE_CHUNK_SIZE 32
+
+ /* set base script environment */
+ build_env();
+
+ rcm_init_queue(&script_info_q);
+
+ /*
+ * Initialize the semaphore to limit the number of rcm script
+ * process running in parallel to script_max_parallelism.
+ */
+ (void) sema_init(&script_process_sema, script_max_parallelism,
+ USYNC_THREAD, NULL);
+
+ (void) mutex_init(&script_lock, USYNC_THREAD, NULL);
+
+ /* save original file limit */
+ (void) getrlimit(RLIMIT_NOFILE, &file_limit);
+
+ open_state_file(script_ps_state_file, &script_ps_statefd,
+ sizeof (ps_state_element_t),
+ PS_STATE_FILE_CHUNK_SIZE,
+ PS_STATE_FILE_VER);
+
+ /*
+ * If any pids exist in the ps state file since the last incarnation of
+ * the rcm daemon, kill the pids.
+ * On a normal daemon exit no pids should exist in the ps state file.
+ * But on an abnormal daemon exit pids may exist in the ps state file.
+ */
+ if (script_ps_statefd.state_file) {
+ script_ps_state_file_kill_pids();
+ truncate_state_file(&script_ps_statefd);
+ }
+
+ return (0);
+}
+
+/*
+ * Do any cleanup.
+ * Called at the time of normal rcm daemon exit.
+ */
+int
+script_main_fini(void)
+{
+ script_ps_state_file_kill_pids();
+ close_state_file(script_ps_state_file, &script_ps_statefd);
+ return (0);
+}
+
+/*
+ * Initialize the given rcm script.
+ * module->name contains the name of the rcm script.
+ */
+struct rcm_mod_ops *
+script_init(module_t *module)
+{
+ script_info_t *rsi;
+ size_t len;
+ char *script_path;
+
+ rcm_log_message(RSCR_TRACE, "script_init: script name = %s\n",
+ module->name);
+
+ module->rsi = NULL;
+
+ if ((script_path = rcm_get_script_dir(module->name)) == NULL)
+ return (NULL);
+
+ len = strlen(script_path) + strlen(module->name) + 2;
+
+ /* calloc also zeros the contents */
+ rsi = (script_info_t *)rcmscript_calloc(1, sizeof (script_info_t));
+ rsi->script_full_name = (char *)rcmscript_calloc(1, len);
+
+ rsi->module = module;
+ rcm_init_queue(&rsi->drreq_q);
+
+ (void) mutex_init(&rsi->channel_lock, USYNC_THREAD, NULL);
+
+ (void) snprintf(rsi->script_full_name, len, "%s%s", script_path,
+ module->name);
+ rsi->script_name = strrchr(rsi->script_full_name, '/') + 1;
+
+ (void) mutex_lock(&rsi->channel_lock);
+
+ rsi->cmd_timeout = -1; /* don't time scriptinfo command */
+ if (do_script_info(rsi) == RCM_SUCCESS) {
+ /*
+ * if the script hasn't specified a timeout value set it to
+ * default
+ */
+ if (rsi->cmd_timeout == -1)
+ rsi->cmd_timeout = SCRIPT_CMD_TIMEOUT;
+ (void) mutex_unlock(&rsi->channel_lock);
+
+ /* put rsi on script_info_q */
+ (void) mutex_lock(&script_lock);
+ rcm_enqueue_tail(&script_info_q, &rsi->queue);
+ (void) mutex_unlock(&script_lock);
+
+ module->rsi = rsi;
+ return (&script_ops);
+ }
+
+ (void) mutex_unlock(&rsi->channel_lock);
+
+ free(rsi->script_full_name);
+ free(rsi);
+ return (NULL);
+}
+
+/*
+ * Returns a string describing the script's functionality.
+ * module->name contains the name of the rcm script for which information
+ * is requested.
+ */
+char *
+script_info(module_t *module)
+{
+ script_info_t *rsi = module->rsi;
+
+ rcm_log_message(RSCR_TRACE, "script_info: script name = %s\n",
+ rsi->script_name);
+ return (rsi->func_info_buf);
+}
+
+/*
+ * Called before unloading the script.
+ * module->name contains the name of the rcm script which is being unloaded.
+ * Do any cleanup.
+ */
+int
+script_fini(module_t *module)
+{
+ script_info_t *rsi = module->rsi;
+
+ rcm_log_message(RSCR_TRACE, "script_fini: script name = %s\n",
+ rsi->script_name);
+
+ /* remove rsi from script_info_q */
+ (void) mutex_lock(&script_lock);
+ rcm_dequeue(&rsi->queue);
+ (void) mutex_unlock(&script_lock);
+
+ remove_drreq_all(rsi);
+
+ if (rsi->func_info_buf)
+ free(rsi->func_info_buf);
+
+ free(rsi->script_full_name);
+ free(rsi);
+
+ module->rsi = NULL;
+
+ return (RCM_SUCCESS);
+}
+
+/* build base environment for scripts */
+static void
+build_env(void)
+{
+ const char *env_list[] = { "LANG", "LC_COLLATE", "LC_CTYPE",
+ "LC_MESSAGES", "LC_MONETARY", "LC_NUMERIC", "LC_TIME",
+ "LC_ALL", "TZ", NULL };
+ char *x;
+ int len;
+ int i, j = 0;
+ int d;
+ extern int debug_level;
+
+ script_env[j++] = rcmscript_strdup("PATH=/usr/sbin:/usr/bin");
+
+ for (i = 0; env_list[i] != NULL; i++) {
+ x = getenv(env_list[i]);
+ if (x) {
+ len = strlen(env_list[i]) + strlen(x) + 2;
+ script_env[j] = (char *)rcmscript_malloc(len);
+
+ (void) snprintf(script_env[j++], len, "%s=%s",
+ env_list[i], x);
+ }
+ }
+
+ len = strlen("RCM_ENV_DEBUG_LEVEL") + 3;
+ script_env[j] = (char *)rcmscript_malloc(len);
+
+ if (debug_level < 0)
+ d = 0;
+ else if (debug_level > 9)
+ d = 9;
+ else
+ d = debug_level;
+
+ (void) snprintf(script_env[j++], len, "RCM_ENV_DEBUG_LEVEL=%d", d);
+
+ script_env[j] = NULL;
+}
+
+static void
+copy_env(char *src[], char *dst[])
+{
+ int i;
+
+ for (i = 0; src[i] != NULL; i++)
+ dst[i] = src[i];
+
+ dst[i] = NULL;
+}
+
+/*
+ * Open (or create if the file does not exist) the given state file
+ * and mmap it.
+ */
+static void
+open_state_file(const char *filename,
+ state_file_descr_t *statefd,
+ size_t element_size,
+ int chunk_size,
+ uint32_t version)
+{
+ struct stat stats;
+ int error_num;
+
+ if ((statefd->fd = open(filename, O_CREAT|O_RDWR, 0600)) ==
+ -1) {
+ error_num = errno;
+ rcm_log_message(RCM_ERROR, MF_STATE_FILE_ERR,
+ "open", strerror(error_num));
+ rcmd_exit(error_num);
+ /*NOTREACHED*/
+ }
+
+ if (fstat(statefd->fd, &stats) != 0) {
+ error_num = errno;
+ rcm_log_message(RCM_ERROR, MF_STATE_FILE_ERR,
+ "fstat", strerror(error_num));
+ rcmd_exit(error_num);
+ /*NOTREACHED*/
+ }
+
+ if (stats.st_size != 0) {
+ /* LINTED */
+ statefd->state_file = (state_file_t *)mmap(NULL,
+ stats.st_size, PROT_READ|PROT_WRITE, MAP_SHARED,
+ statefd->fd, 0);
+
+ if (statefd->state_file == MAP_FAILED) {
+ error_num = errno;
+ rcm_log_message(RCM_ERROR, MF_STATE_FILE_ERR,
+ "mmap", strerror(error_num));
+ rcmd_exit(error_num);
+ /*NOTREACHED*/
+ }
+
+ if (statefd->state_file->version != version) {
+ (void) munmap((void *)statefd->state_file,
+ stats.st_size);
+ statefd->state_file = NULL;
+ (void) ftruncate(statefd->fd, 0);
+ }
+ } else {
+ statefd->state_file = NULL;
+ }
+
+ statefd->version = version;
+ statefd->element_size = sizeof (state_element_t) +
+ RSCR_ROUNDUP(element_size, 8);
+ statefd->chunk_size = chunk_size;
+ statefd->index = 0;
+}
+
+static void
+truncate_state_file(state_file_descr_t *statefd)
+{
+ size_t size;
+
+ if (statefd->state_file) {
+ size = sizeof (state_file_t) + statefd->element_size *
+ statefd->state_file->max_elements;
+
+ (void) munmap((void *)statefd->state_file, size);
+ statefd->state_file = NULL;
+ }
+ (void) ftruncate(statefd->fd, 0);
+}
+
+static void
+close_state_file(const char *filename, state_file_descr_t *statefd)
+{
+ truncate_state_file(statefd);
+ (void) close(statefd->fd);
+ (void) unlink(filename);
+}
+
+/*
+ * Grow the state file by the chunk size specified in statefd
+ * and mmap it.
+ */
+static void
+grow_state_file(state_file_descr_t *statefd)
+{
+ size_t size;
+ int max_elements;
+ int error_num;
+
+ max_elements = statefd->chunk_size;
+ if (statefd->state_file)
+ max_elements += statefd->state_file->max_elements;
+
+ size = sizeof (state_file_t) +
+ statefd->element_size * max_elements;
+
+ if (ftruncate(statefd->fd, size) != 0) {
+ error_num = errno;
+ rcm_log_message(RCM_ERROR, MF_STATE_FILE_ERR,
+ "ftruncate", strerror(error_num));
+ rcmd_exit(error_num);
+ /*NOTREACHED*/
+ }
+
+ /* LINTED */
+ statefd->state_file = (state_file_t *)mmap(NULL, size,
+ PROT_READ|PROT_WRITE, MAP_SHARED, statefd->fd, 0);
+
+ if (statefd->state_file == MAP_FAILED) {
+ error_num = errno;
+ rcm_log_message(RCM_ERROR, MF_STATE_FILE_ERR,
+ "mmap", strerror(error_num));
+ rcmd_exit(error_num);
+ /*NOTREACHED*/
+ }
+
+ statefd->index = statefd->state_file->max_elements;
+ statefd->state_file->max_elements = max_elements;
+ statefd->state_file->version = statefd->version;
+}
+
+/*
+ * Given index into state element array, get the pointer to the actual
+ * state element.
+ * If flag is non-null set *flag to
+ * TRUE if the state element is currently is use.
+ * FALSE if the state element is free.
+ */
+static void *
+get_state_element(state_file_descr_t *statefd, int index, int *flag)
+{
+ char *ptr;
+
+ if (statefd->state_file &&
+ (index < statefd->state_file->max_elements)) {
+
+ ptr = (char *)(statefd->state_file);
+ ptr += sizeof (state_file_t) +
+ index * statefd->element_size;
+
+ if (flag) {
+ *flag = (((state_element_t *)((void *)ptr))->flags &
+ STATE_ELEMENT_IN_USE) ? 1 : 0;
+ }
+
+ ptr += sizeof (state_element_t);
+ } else
+ ptr = NULL;
+
+ return ((void *)ptr);
+}
+
+/*
+ * Allocate a state element entry in the state file and return a pointer
+ * to the allocated entry.
+ * If index is non-null set *index to index into the state element array
+ * of the allocated entry.
+ */
+static void *
+allocate_state_element(state_file_descr_t *statefd, int *index)
+{
+ void *x;
+ int i;
+ int flag;
+
+ if (statefd->state_file) {
+ /* find an empty slot */
+ for (i = 0; i < statefd->state_file->max_elements; i++) {
+ x = get_state_element(statefd, statefd->index,
+ &flag);
+ assert(x != NULL);
+
+ if (flag == 0)
+ /* entry is free */
+ break;
+
+ statefd->index++;
+ if (statefd->index >= statefd->state_file->max_elements)
+ statefd->index = 0;
+ }
+ }
+
+ if (statefd->state_file == NULL ||
+ i == statefd->state_file->max_elements) {
+
+ /* All entries are in use. Grow the list */
+ grow_state_file(statefd);
+ x = get_state_element(statefd, statefd->index, &flag);
+ assert(flag == 0);
+ }
+
+ if (index != NULL)
+ *index = statefd->index;
+
+ statefd->index++;
+ if (statefd->index >= statefd->state_file->max_elements)
+ statefd->index = 0;
+
+ ((state_element_t *)x - 1)->flags |= STATE_ELEMENT_IN_USE;
+ return (x);
+}
+
+static void
+free_state_element(void *x)
+{
+ ((state_element_t *)x - 1)->flags &= ~STATE_ELEMENT_IN_USE;
+}
+
+/*
+ * Kill the pids contained in ps state file.
+ */
+static void
+script_ps_state_file_kill_pids(void)
+{
+ ps_state_element_t *x;
+ char procfile[80];
+ psinfo_t psi;
+ int fd, i, flag;
+
+ /* LINTED */
+ for (i = 0; 1; i++) {
+ if ((x = (ps_state_element_t *)get_state_element(
+ &script_ps_statefd, i, &flag)) == NULL)
+ break;
+
+ if (flag == 1) { /* the entry is in use */
+ (void) snprintf(procfile, 80, "/proc/%ld/psinfo",
+ (long)x->pid);
+ if ((fd = open(procfile, O_RDONLY)) != -1 &&
+ read(fd, &psi, sizeof (psi)) == sizeof (psi) &&
+ strcmp(psi.pr_fname,
+ x->script_name) == 0) {
+
+ (void) close(fd);
+
+ /*
+ * just a safety check to not to blow up
+ * system processes if the file is ever corrupt
+ */
+ if (x->pid > 1) {
+ rcm_log_message(RCM_DEBUG,
+ "script_ps_state_file_kill_pids: "
+ "killing script_name = %s pid = %ld\n",
+ x->script_name, x->pid);
+
+ /* kill the process group */
+ (void) kill(-(x->pid), SIGKILL);
+ }
+ } else {
+ if (fd != -1)
+ (void) close(fd);
+ }
+ free_state_element((void *)x);
+ }
+ }
+}
+
+/*
+ * Add a state element entry to ps state file.
+ */
+static void
+script_ps_state_file_add_entry(pid_t pid, char *script_name)
+{
+ ps_state_element_t *x;
+
+ (void) mutex_lock(&script_lock);
+
+ x = (ps_state_element_t *)allocate_state_element(
+ &script_ps_statefd, NULL);
+
+ x->pid = pid;
+ (void) strlcpy(x->script_name, script_name, MAXNAMELEN);
+
+ (void) fsync(script_ps_statefd.fd);
+
+ (void) mutex_unlock(&script_lock);
+}
+
+/*
+ * Remove the state element entry corresponding to pid from the
+ * ps state file.
+ */
+static void
+script_ps_state_file_remove_entry(pid_t pid)
+{
+ ps_state_element_t *x;
+ int flag, i;
+
+ (void) mutex_lock(&script_lock);
+
+ /* LINTED */
+ for (i = 0; 1; i++) {
+ if ((x = (ps_state_element_t *)get_state_element(
+ &script_ps_statefd, i, &flag)) == NULL)
+ break;
+
+ /* if the state element entry is in use and pid matches */
+ if (flag == 1 && x->pid == pid) {
+ free_state_element((void *)x);
+ break;
+ }
+ }
+
+ (void) mutex_unlock(&script_lock);
+}
+
+/*
+ * Get data item id given data item name
+ */
+static int
+dname_to_id(char *dname)
+{
+ int i;
+
+ for (i = 0; script_data_item_name[i] != NULL; i++) {
+ if (strcmp(dname, script_data_item_name[i]) == 0)
+ return (i);
+ }
+
+ return (-1);
+}
+
+/*
+ * Called before running any script.
+ * This routine waits until the number of script processes running in
+ * parallel drops down below to script_max_parallelism.
+ */
+static void
+script_process_sema_wait(void)
+{
+ int error_num;
+
+ /* LINTED */
+ while (1) {
+ if (sema_wait(&script_process_sema) == 0)
+ return;
+
+ if (errno != EINTR && errno != EAGAIN) {
+ error_num = errno;
+ rcm_log_message(RCM_ERROR, MF_FUNC_CALL_ERR,
+ "sema_wait", strerror(error_num));
+ rcmd_exit(error_num);
+ /*NOTREACHED*/
+ }
+ }
+
+ /*NOTREACHED*/
+}
+
+/*
+ * Fork and execute the script.
+ */
+static int
+run_script(script_info_t *rsi, char *argv[], char *envp[], char **errmsg)
+{
+ int i, p1 = -1, p2 = -1;
+ struct rlimit rlp;
+ struct stat stats;
+
+ rcm_log_message(RSCR_TRACE, "run_script: script name = %s\n",
+ rsi->script_full_name);
+
+ for (i = 0; argv[i] != NULL; i++)
+ rcm_log_message(RSCR_TRACE, "run_script: argv[%d] = %s\n",
+ i, argv[i]);
+
+ *errmsg = NULL;
+
+ /* check that the script exists */
+ if (stat(rsi->script_full_name, &stats) != 0)
+ goto error;
+
+ /*
+ * If the syscall pipe fails because of reaching the max open file
+ * count per process then dynamically increase the limit on the max
+ * open file count.
+ *
+ * At present the rcm_daemon consumes file descriptor
+ * entries for the following files.
+ * RCM_STATE_FILE - /var/run/rcm_daemon_state
+ * DAEMON_LOCK_FILE - /var/run/rcm_daemon_lock
+ * RCM_SERVICE_DOOR - /var/run/rcm_daemon_door
+ * proc files in the format "/proc/pid/as" for each pid
+ * communicating with the rcm_daemon via doors
+ * dlopen for each rcm module
+ * When in daemon mode stdin, stdout and stderr are closed;
+ * /dev/null opened and duped to stdout, and stderr
+ * openlog
+ * Some files which are opened briefly and closed such as
+ * directory files.
+ * Two file descriptors for each script in running state.
+ * Note that the constant script_max_parallelism sets an
+ * upper cap on how many rcm scripts can run in
+ * parallel.
+ */
+ if ((p1 = pipe(rsi->pipe1)) == -1 || (p2 = pipe(rsi->pipe2)) == -1) {
+ if ((errno == EMFILE) &&
+ (getrlimit(RLIMIT_NOFILE, &rlp) == 0)) {
+
+ rlp.rlim_cur += 16;
+ if (rlp.rlim_max < rlp.rlim_cur)
+ rlp.rlim_max = rlp.rlim_cur;
+ (void) setrlimit(RLIMIT_NOFILE, &rlp);
+
+ if (p1 == -1) {
+ if ((p1 = pipe(rsi->pipe1)) == -1)
+ goto error;
+ }
+ if ((p2 = pipe(rsi->pipe2)) == -1)
+ goto error;
+ } else
+ goto error;
+ }
+
+forkagain:
+ if ((rsi->pid = fork1()) == (pid_t)-1) {
+ if (errno == EINTR || errno == EAGAIN)
+ goto forkagain;
+
+ goto error;
+ }
+
+ if (rsi->pid == 0) {
+ /* child process */
+
+ (void) setsid();
+
+ /* close stdin, stdout and stderr */
+ (void) close(0);
+ (void) close(1);
+ (void) close(2);
+
+ /* set stdin to /dev/null */
+ (void) open("/dev/null", O_RDWR, 0);
+
+ /* redirect stdout and stderr to pipe */
+ (void) dup2(rsi->pipe1[CHILD_END_OF_PIPE], 1);
+ (void) dup2(rsi->pipe2[CHILD_END_OF_PIPE], 2);
+
+ /* close all other file descriptors */
+ closefrom(3);
+
+ /* restore original file limit */
+ (void) setrlimit(RLIMIT_NOFILE, &file_limit);
+
+ /* set current working dir */
+ if (stats.st_uid == 0) {
+ /* root */
+ if (chdir("/var/run") == -1)
+ _exit(127);
+ } else {
+ if (chdir("/tmp") == -1)
+ _exit(127);
+ }
+
+ /*
+ * setuid sets real, effective and saved user ids to the
+ * given id.
+ * setgid sets real, effective and saved group ids to the
+ * given id.
+ */
+ (void) setgid(stats.st_gid);
+ (void) setuid(stats.st_uid);
+
+ (void) execve(rsi->script_full_name, argv, envp);
+ _exit(127);
+ /*NOTREACHED*/
+ }
+
+ (void) close(rsi->pipe1[CHILD_END_OF_PIPE]);
+ (void) close(rsi->pipe2[CHILD_END_OF_PIPE]);
+
+ script_ps_state_file_add_entry(rsi->pid, rsi->script_name);
+
+ return (0);
+
+error:
+ *errmsg = dup_err(RCM_ERROR, MS_SCRIPT_ERR,
+ rsi->script_name, strerror(errno));
+
+ if (p1 != -1) {
+ (void) close(rsi->pipe1[PARENT_END_OF_PIPE]);
+ (void) close(rsi->pipe1[CHILD_END_OF_PIPE]);
+ }
+
+ if (p2 != -1) {
+ (void) close(rsi->pipe2[PARENT_END_OF_PIPE]);
+ (void) close(rsi->pipe2[CHILD_END_OF_PIPE]);
+ }
+
+ return (-1);
+}
+
+/*
+ * Reads one line of input (including the newline character) from the
+ * given file descriptor "fd" to buf.
+ * maxbuflen specifies the size of memory allocated for buf.
+ * Timeoutval is the max timeout value in seconds for the script to supply
+ * input. A timeoutval of 0 implies no timeout.
+ *
+ * Upon return *buflen contains the number of bytes read.
+ *
+ * Return values:
+ * 0 success
+ * -1 an error occured
+ * -2 timeout occurred
+ * -3 script exited
+ */
+static int
+get_line(int fd,
+ char *fdname,
+ char *buf,
+ int maxbuflen,
+ size_t *buflen,
+ time_t timeoutval,
+ int *error_num)
+{
+ char c = '\0';
+ struct pollfd fds[1];
+ int x;
+ size_t len = 0;
+ char *ptr;
+ int timeit;
+ time_t deadline;
+ int rval = 0;
+
+ if (timeoutval) {
+ timeit = TRUE;
+ deadline = time(NULL) + timeoutval;
+ fds[0].fd = fd;
+ fds[0].events = POLLIN;
+ } else
+ timeit = FALSE;
+
+ ptr = buf;
+
+ while (c != '\n' && len < (maxbuflen -1)) {
+ if (timeit) {
+pollagain:
+ fds[0].revents = 0;
+ timeoutval = deadline - time(NULL);
+ if (timeoutval <= 0) {
+ rval = -2;
+ break;
+ }
+ x = poll(fds, 1, timeoutval*1000);
+ if (x <= 0) {
+ if (x == 0)
+ /* poll timedout */
+ rval = -2;
+ else {
+ if (errno == EINTR || errno == EAGAIN)
+ goto pollagain;
+ *error_num = errno;
+ rval = -1;
+ }
+ break;
+ }
+ }
+readagain:
+ if ((x = read(fd, &c, 1)) != 1) {
+ if (x == 0)
+ /*
+ * Script exited. Or more specifically the
+ * script has closed its end of the pipe.
+ */
+ rval = -3;
+ else {
+ if (errno == EINTR || errno == EAGAIN)
+ goto readagain;
+ *error_num = errno;
+ rval = -1;
+ }
+ break;
+ }
+
+ *ptr++ = c;
+ len++;
+ }
+
+ *ptr = '\0';
+ *buflen = len;
+
+ rcm_log_message(RSCR_TRACE,
+ "get_line(%s): rval = %d buflen = %d line = %s\n",
+ fdname, rval, *buflen, buf);
+ return (rval);
+}
+
+static void
+script_exited(script_info_t *rsi)
+{
+ if (rsi->flags & STDERR_THREAD_CREATED) {
+ rcm_log_message(RSCR_TRACE,
+ "script_exited: doing thr_join (%s)\n", rsi->script_name);
+ (void) thr_join(rsi->tid, NULL, NULL);
+ rsi->flags &= ~STDERR_THREAD_CREATED;
+ }
+
+ (void) close(rsi->pipe1[PARENT_END_OF_PIPE]);
+ (void) close(rsi->pipe2[PARENT_END_OF_PIPE]);
+ rsi->pipe1[PARENT_END_OF_PIPE] = -1;
+ rsi->pipe2[PARENT_END_OF_PIPE] = -1;
+
+ script_ps_state_file_remove_entry(rsi->pid);
+ rsi->pid = 0;
+ (void) sema_post(&script_process_sema);
+}
+
+/*
+ * Kill the specified process group
+ */
+static int
+kill_pid(pid_t pid)
+{
+ time_t deadline, timeleft;
+ int child_status;
+
+ /* kill the entire process group */
+ (void) kill(-(pid), SIGKILL);
+
+ /* give some time for the script to be killed */
+ deadline = time(NULL) + SCRIPT_KILL_TIMEOUT;
+ do {
+ if (waitpid(pid, &child_status, WNOHANG) == pid)
+ return (0);
+
+ /* wait for 100 ms */
+ (void) poll(NULL, 0, 100);
+
+ timeleft = deadline - time(NULL);
+ } while (timeleft > 0);
+
+ /* script process was not killed successfully */
+ return (-1);
+}
+
+/*
+ * Kill the specified script.
+ */
+static void
+kill_script(script_info_t *rsi)
+{
+ if (rsi->pid > 1) {
+ (void) kill_pid(rsi->pid);
+ script_exited(rsi);
+ remove_drreq_all(rsi);
+ }
+}
+
+/*
+ * Convert rcm flags parameter to a string.
+ * Used for debug prints.
+ */
+static char *
+flags_to_name(int flags, char *buf, int maxbuflen)
+{
+ (void) snprintf(buf, maxbuflen, "%s%s",
+ (flags & RCM_QUERY) ? "RCM_QUERY " : "",
+ (flags & RCM_FORCE) ? "RCM_FORCE" : "");
+
+ return (buf);
+}
+
+static void
+fill_argv(script_info_t *rsi, char *argv[], char *resource_name)
+{
+ argv[0] = rsi->script_full_name;
+ argv[1] = script_cmd_name[rsi->cmd];
+ if (resource_name) {
+ argv[2] = resource_name;
+ argv[3] = NULL;
+ } else
+ argv[2] = NULL;
+}
+
+/*
+ * stderr thread:
+ * Reads stderr and logs to syslog.
+ * Runs as a separate thread.
+ */
+static void *
+read_stderr(script_info_t *rsi)
+{
+ char buf[MAX_LINE_LEN];
+ size_t buflen;
+ int error_num;
+
+ while ((get_line(rsi->pipe2[PARENT_END_OF_PIPE], "stderr",
+ buf, MAX_LINE_LEN, &buflen, 0, &error_num)) == 0) {
+ log_msg(rsi, RCM_ERROR, buf);
+ }
+
+ if (buflen)
+ log_msg(rsi, RCM_ERROR, buf);
+
+ return (NULL);
+}
+
+/* process return data items passed by scripts to the framework */
+static int
+process_dataitem(script_info_t *rsi, int token, char *value, char **errmsg)
+{
+ char *ptr;
+ int status;
+
+ *errmsg = NULL;
+
+ if (*value == '\0')
+ goto error;
+
+ switch (token) {
+ case D_SCRIPT_VERSION:
+ if (rsi->cmd != C_SCRIPTINFO)
+ goto error;
+
+ /* check that value contains only digits */
+ for (ptr = value; *ptr != '\0'; ptr++)
+ if (isdigit((int)(*ptr)) == 0)
+ break;
+
+ if (*ptr == '\0')
+ rsi->ver = atoi(value);
+ else
+ goto error;
+
+ break;
+
+ case D_SCRIPT_FUNC_INFO:
+ if (rsi->cmd != C_SCRIPTINFO)
+ goto error;
+
+ rcmscript_snprintf(&rsi->func_info_buf,
+ &rsi->func_info_buf_len,
+ &rsi->func_info_buf_curptr,
+ "%s", value);
+ break;
+
+ case D_CMD_TIMEOUT:
+ if (rsi->cmd != C_SCRIPTINFO)
+ goto error;
+
+ /* check that value contains only digits */
+ for (ptr = value; *ptr != '\0'; ptr++)
+ if (isdigit((int)(*ptr)) == 0)
+ break;
+
+ if (*ptr == '\0')
+ rsi->cmd_timeout = atoi(value);
+ else
+ goto error;
+ break;
+
+ case D_RESOURCE_NAME:
+ if (rsi->cmd != C_REGISTER)
+ goto error;
+
+ if (get_capacity_descr(value) != NULL)
+ status = rcm_register_capacity(rsi->hdl, value,
+ 0, NULL);
+ else
+ status = rcm_register_interest(rsi->hdl, value, 0,
+ NULL);
+
+ if (status == RCM_FAILURE && errno == EALREADY)
+ status = RCM_SUCCESS;
+
+ if (status != RCM_SUCCESS) {
+ rcm_log_message(RCM_ERROR, MS_REGISTER_RSRC_ERR,
+ rsi->script_name, value);
+ }
+
+ remove_from_unregister(rsi, value);
+ break;
+
+ case D_RESOURCE_USAGE_INFO:
+ if (rsi->cmd != C_RESOURCEINFO)
+ goto error;
+
+ rcmscript_snprintf(&rsi->resource_usage_info_buf,
+ &rsi->resource_usage_info_buf_len,
+ &rsi->resource_usage_info_buf_curptr,
+ "%s", value);
+ break;
+
+ case D_FAILURE_REASON:
+ rcmscript_snprintf(&rsi->failure_reason_buf,
+ &rsi->failure_reason_buf_len,
+ &rsi->failure_reason_buf_curptr,
+ "%s", value);
+ break;
+
+ default:
+ goto error;
+ }
+
+ return (0);
+
+error:
+ *errmsg = dup_err(RCM_ERROR, MS_PROTOCOL_ERR, rsi->script_name);
+ return (-1);
+}
+
+/* Send the given command to the script and process return data */
+static int
+do_cmd(script_info_t *rsi, char *argv[], char *envp[], char **errmsg)
+{
+ char buf[MAX_LINE_LEN];
+ size_t buflen;
+ int loglevel = -1, continuelog = 0;
+ char *ptr, *dname, *value;
+ time_t maxsecs;
+ time_t deadline;
+ int sigaborted = 0;
+ int rval, child_status, token;
+ int error_num;
+ int cmd_timeout = rsi->cmd_timeout;
+
+ *errmsg = NULL;
+
+ script_process_sema_wait();
+
+ if (run_script(rsi, argv, envp, errmsg) == -1) {
+ (void) sema_post(&script_process_sema);
+ goto error2;
+ }
+
+ (void) time(&rsi->lastrun);
+ deadline = rsi->lastrun + cmd_timeout;
+
+ if (thr_create(NULL, 0, (void *(*)(void *))read_stderr, rsi,
+ 0, &rsi->tid) != 0) {
+ *errmsg = dup_err(RCM_ERROR, MF_FUNC_CALL_ERR,
+ "thr_create", strerror(errno));
+ goto error1;
+ }
+ rsi->flags |= STDERR_THREAD_CREATED;
+
+ /* LINTED */
+ while (1) {
+ if (cmd_timeout > 0) {
+ maxsecs = deadline - time(NULL);
+ if (maxsecs <= 0)
+ goto timedout;
+ } else
+ maxsecs = 0;
+
+ rval = get_line(rsi->pipe1[PARENT_END_OF_PIPE],
+ "stdout", buf, MAX_LINE_LEN, &buflen,
+ maxsecs, &error_num);
+
+ if (buflen) {
+ if (continuelog)
+ log_msg(rsi, loglevel, buf);
+ else {
+ if ((ptr = strchr(buf, '=')) == NULL)
+ goto error;
+
+ *ptr = '\0';
+ dname = buf;
+ value = ptr + 1;
+ if ((token = dname_to_id(dname)) == -1)
+ goto error;
+
+ switch (token) {
+ case D_LOG_ERR:
+ loglevel = RCM_ERROR;
+ break;
+
+ case D_LOG_WARN:
+ loglevel = RCM_WARNING;
+ break;
+
+ case D_LOG_INFO:
+ loglevel = RCM_INFO;
+ break;
+
+ case D_LOG_DEBUG:
+ loglevel = RCM_DEBUG;
+ break;
+
+ default:
+ loglevel = -1;
+ break;
+ }
+
+ if (loglevel != -1) {
+ log_msg(rsi, loglevel, value);
+ if (buf[buflen - 1] == '\n')
+ continuelog = 0;
+ else
+ continuelog = 1;
+ } else {
+ if (buf[buflen - 1] != '\n')
+ goto error;
+
+ buf[buflen - 1] = '\0';
+ if (process_dataitem(rsi, token,
+ value, errmsg) != 0)
+ goto error1;
+ }
+ }
+ }
+
+ if (rval == -3) {
+ /* script exited */
+waitagain:
+ if (waitpid(rsi->pid, &child_status, 0)
+ != rsi->pid) {
+ if (errno == EINTR || errno == EAGAIN)
+ goto waitagain;
+ *errmsg = dup_err(RCM_ERROR, MS_SCRIPT_ERR,
+ rsi->script_name, strerror(errno));
+ goto error1;
+ }
+
+ if (WIFEXITED(child_status)) {
+ script_exited(rsi);
+ rsi->exit_status = WEXITSTATUS(child_status);
+ } else {
+ if (sigaborted)
+ *errmsg = dup_err(RCM_ERROR,
+ MS_TIMEOUT_ERR, rsi->script_name);
+ else
+ *errmsg = dup_err(RCM_ERROR,
+ MS_UNKNOWN_ERR, rsi->script_name);
+
+ /* kill any remaining processes in the pgrp */
+ (void) kill(-(rsi->pid), SIGKILL);
+ script_exited(rsi);
+ goto error2;
+ }
+
+ break;
+ }
+
+ if (rval == -1) {
+ *errmsg = dup_err(RCM_ERROR, MS_SCRIPT_ERR,
+ rsi->script_name, strerror(errno));
+ goto error1;
+ }
+
+ if (rval == -2) {
+timedout:
+ /* timeout occurred */
+ if (sigaborted == 0) {
+ (void) kill(rsi->pid, SIGABRT);
+ sigaborted = 1;
+ /* extend deadline */
+ deadline += SCRIPT_ABORT_TIMEOUT;
+ } else {
+ *errmsg = dup_err(RCM_ERROR,
+ MS_TIMEOUT_ERR, rsi->script_name);
+ goto error1;
+ }
+ }
+ }
+
+ return (0);
+
+error:
+ *errmsg = dup_err(RCM_ERROR, MS_PROTOCOL_ERR, rsi->script_name);
+
+error1:
+ kill_script(rsi);
+
+error2:
+ return (-1);
+}
+
+static int
+do_script_info(script_info_t *rsi)
+{
+ char *argv[MAX_ARGS];
+ int status = RCM_FAILURE;
+ int err = 0;
+ char *errmsg = NULL;
+
+ rcm_log_message(RSCR_TRACE, "do_script_info: script name = %s\n",
+ rsi->script_name);
+
+ rsi->cmd = C_SCRIPTINFO;
+ rsi->func_info_buf = NULL;
+ rsi->failure_reason_buf = NULL;
+ fill_argv(rsi, argv, NULL);
+
+ if (do_cmd(rsi, argv, script_env, &errmsg) == 0) {
+ switch (rsi->exit_status) {
+ case E_SUCCESS:
+ if (rsi->func_info_buf != NULL &&
+ rsi->failure_reason_buf == NULL) {
+
+ if (rsi->ver >= SCRIPT_API_MIN_VER &&
+ rsi->ver <= SCRIPT_API_MAX_VER)
+ status = RCM_SUCCESS;
+ else
+ rcm_log_message(RCM_ERROR,
+ MS_UNSUPPORTED_VER, rsi->script_name,
+ rsi->ver);
+ } else
+ err = 1;
+ break;
+
+ case E_FAILURE:
+ if (rsi->failure_reason_buf != NULL) {
+ rcm_log_message(RCM_ERROR, MS_SCRIPTINFO_ERR,
+ rsi->script_name,
+ rsi->failure_reason_buf);
+ } else
+ err = 1;
+ break;
+
+ default:
+ err = 1;
+ break;
+ }
+ if (err)
+ rcm_log_message(RCM_ERROR, MS_PROTOCOL_ERR,
+ rsi->script_name);
+ } else if (errmsg)
+ (void) free(errmsg);
+
+ if (status != RCM_SUCCESS && rsi->func_info_buf != NULL)
+ free(rsi->func_info_buf);
+
+ if (rsi->failure_reason_buf)
+ free(rsi->failure_reason_buf);
+
+ return (status);
+}
+
+static int
+do_dr(script_info_t *rsi, char *argv[], char *envp[], char **info)
+{
+ int status = RCM_FAILURE;
+ int err = 0;
+
+ rsi->failure_reason_buf = NULL;
+
+ if (do_cmd(rsi, argv, envp, info) == 0) {
+ switch (rsi->exit_status) {
+ case E_SUCCESS:
+ case E_UNSUPPORTED_CMD:
+ if (rsi->failure_reason_buf == NULL)
+ status = RCM_SUCCESS;
+ else
+ err = 1;
+ break;
+
+ case E_FAILURE:
+ case E_REFUSE:
+ if (rsi->failure_reason_buf != NULL) {
+ *info = rsi->failure_reason_buf;
+ rsi->failure_reason_buf = NULL;
+ } else
+ err = 1;
+ break;
+
+ default:
+ err = 1;
+ break;
+ }
+
+ if (err)
+ *info = dup_err(RCM_ERROR, MS_PROTOCOL_ERR,
+ rsi->script_name);
+ }
+
+ if (rsi->failure_reason_buf)
+ free(rsi->failure_reason_buf);
+
+ return (status);
+}
+
+/*
+ * get_info entry point
+ */
+/* ARGSUSED */
+static int
+script_get_info(rcm_handle_t *hdl,
+ char *resource_name,
+ pid_t pid,
+ uint_t flag,
+ char **info,
+ char **error,
+ nvlist_t *props,
+ rcm_info_t **dependent_info)
+{
+ script_info_t *rsi = hdl->module->rsi;
+ char *argv[MAX_ARGS];
+ int status = RCM_FAILURE;
+ int err = 0;
+
+ rcm_log_message(RSCR_TRACE, "script_get_info: resource = %s\n",
+ resource_name);
+
+ *info = NULL;
+ *error = NULL;
+
+ (void) mutex_lock(&rsi->channel_lock);
+
+ rsi->hdl = hdl;
+ rsi->cmd = C_RESOURCEINFO;
+ rsi->resource_usage_info_buf = NULL;
+ rsi->failure_reason_buf = NULL;
+ fill_argv(rsi, argv, resource_name);
+
+ if (do_cmd(rsi, argv, script_env, error) == 0) {
+ switch (rsi->exit_status) {
+ case E_SUCCESS:
+ if (rsi->resource_usage_info_buf != NULL &&
+ rsi->failure_reason_buf == NULL) {
+
+ *info = rsi->resource_usage_info_buf;
+ rsi->resource_usage_info_buf = NULL;
+ status = RCM_SUCCESS;
+ } else
+ err = 1;
+ break;
+
+ case E_FAILURE:
+ if (rsi->failure_reason_buf != NULL) {
+ *error = rsi->failure_reason_buf;
+ rsi->failure_reason_buf = NULL;
+ } else
+ err = 1;
+ break;
+
+ default:
+ err = 1;
+ break;
+ }
+ if (err)
+ *error = dup_err(RCM_ERROR, MS_PROTOCOL_ERR,
+ rsi->script_name);
+ }
+
+ if (rsi->resource_usage_info_buf)
+ free(rsi->resource_usage_info_buf);
+
+ if (rsi->failure_reason_buf)
+ free(rsi->failure_reason_buf);
+
+ (void) mutex_unlock(&rsi->channel_lock);
+
+ return (status);
+}
+
+static void
+add_for_unregister(script_info_t *rsi)
+{
+ module_t *module = rsi->module;
+ client_t *client;
+ rcm_queue_t *head;
+ rcm_queue_t *q;
+
+ (void) mutex_lock(&rcm_req_lock);
+
+ head = &module->client_q;
+
+ for (q = head->next; q != head; q = q->next) {
+ client = RCM_STRUCT_BASE_ADDR(client_t, q, queue);
+ client->prv_flags |= RCM_NEED_TO_UNREGISTER;
+ }
+
+ (void) mutex_unlock(&rcm_req_lock);
+}
+
+static void
+remove_from_unregister(script_info_t *rsi, char *resource_name)
+{
+ module_t *module = rsi->module;
+ client_t *client;
+ rcm_queue_t *head;
+ rcm_queue_t *q;
+
+ (void) mutex_lock(&rcm_req_lock);
+
+ head = &module->client_q;
+
+ for (q = head->next; q != head; q = q->next) {
+ client = RCM_STRUCT_BASE_ADDR(client_t, q, queue);
+ if (strcmp(client->alias, resource_name) == 0) {
+ client->prv_flags &= ~RCM_NEED_TO_UNREGISTER;
+ break;
+ }
+ }
+
+ (void) mutex_unlock(&rcm_req_lock);
+}
+
+static void
+complete_unregister(script_info_t *rsi)
+{
+ module_t *module = rsi->module;
+ client_t *client;
+ rcm_queue_t *head;
+ rcm_queue_t *q;
+
+ (void) mutex_lock(&rcm_req_lock);
+
+ head = &module->client_q;
+
+ for (q = head->next; q != head; q = q->next) {
+ client = RCM_STRUCT_BASE_ADDR(client_t, q, queue);
+ if (client->prv_flags & RCM_NEED_TO_UNREGISTER) {
+ client->prv_flags &= ~RCM_NEED_TO_UNREGISTER;
+ client->state = RCM_STATE_REMOVE;
+ }
+ }
+
+ (void) mutex_unlock(&rcm_req_lock);
+}
+
+/*
+ * register_interest entry point
+ */
+static int
+script_register_interest(rcm_handle_t *hdl)
+{
+ script_info_t *rsi = hdl->module->rsi;
+ char *argv[MAX_ARGS];
+ int status = RCM_FAILURE;
+ int err = 0;
+ char *errmsg = NULL;
+
+ rcm_log_message(RSCR_TRACE,
+ "script_register_interest: script name = %s\n",
+ rsi->script_name);
+
+ (void) mutex_lock(&rsi->channel_lock);
+
+ if (rsi->drreq_q.next != &rsi->drreq_q) {
+ /* if DR is already in progress no need to register again */
+ (void) mutex_unlock(&rsi->channel_lock);
+ return (RCM_SUCCESS);
+ }
+
+ rsi->hdl = hdl;
+ rsi->cmd = C_REGISTER;
+ rsi->failure_reason_buf = NULL;
+ fill_argv(rsi, argv, NULL);
+
+ add_for_unregister(rsi);
+
+ if (do_cmd(rsi, argv, script_env, &errmsg) == 0) {
+ switch (rsi->exit_status) {
+ case E_SUCCESS:
+ status = RCM_SUCCESS;
+ break;
+
+ case E_FAILURE:
+ if (rsi->failure_reason_buf != NULL) {
+ rcm_log_message(RCM_ERROR, MS_REGISTER_ERR,
+ rsi->script_name,
+ rsi->failure_reason_buf);
+ } else
+ err = 1;
+ break;
+
+ default:
+ err = 1;
+ break;
+ }
+ if (err)
+ rcm_log_message(RCM_ERROR, MS_PROTOCOL_ERR,
+ rsi->script_name);
+ } else if (errmsg)
+ (void) free(errmsg);
+
+ complete_unregister(rsi);
+
+ if (rsi->failure_reason_buf)
+ free(rsi->failure_reason_buf);
+
+ (void) mutex_unlock(&rsi->channel_lock);
+
+ return (status);
+}
+
+/*
+ * Add the specified resource name to the drreq_q.
+ */
+static void
+add_drreq(script_info_t *rsi, char *resource_name)
+{
+ rcm_queue_t *head = &rsi->drreq_q;
+ rcm_queue_t *q;
+ drreq_t *drreq;
+
+ /* check if the dr req is already in the list */
+ for (q = head->next; q != head; q = q->next) {
+ drreq = RCM_STRUCT_BASE_ADDR(drreq_t, q, queue);
+ if (strcmp(drreq->resource_name, resource_name) == 0)
+ /* dr req is already present in the queue */
+ return;
+ }
+
+ drreq = (drreq_t *)rcmscript_calloc(1, sizeof (drreq_t));
+ drreq->resource_name = rcmscript_strdup(resource_name);
+
+ rcm_enqueue_tail(&rsi->drreq_q, &drreq->queue);
+}
+
+/*
+ * Remove the dr req for the specified resource name from the drreq_q.
+ */
+static void
+remove_drreq(script_info_t *rsi, char *resource_name)
+{
+ rcm_queue_t *head = &rsi->drreq_q;
+ rcm_queue_t *q;
+ drreq_t *drreq;
+
+ /* search for dr req and remove from the list */
+ for (q = head->next; q != head; q = q->next) {
+ drreq = RCM_STRUCT_BASE_ADDR(drreq_t, q, queue);
+ if (strcmp(drreq->resource_name, resource_name) == 0)
+ break;
+ }
+
+ if (q != head) {
+ /* found drreq on the queue */
+ rcm_dequeue(&drreq->queue);
+ free(drreq->resource_name);
+ free(drreq);
+ }
+}
+
+/*
+ * Remove all dr req's.
+ */
+static void
+remove_drreq_all(script_info_t *rsi)
+{
+ drreq_t *drreq;
+
+ while (rsi->drreq_q.next != &rsi->drreq_q) {
+ drreq = RCM_STRUCT_BASE_ADDR(drreq_t,
+ rsi->drreq_q.next, queue);
+ remove_drreq(rsi, drreq->resource_name);
+ }
+}
+
+/*
+ * request_offline entry point
+ */
+/* ARGSUSED */
+static int
+script_request_offline(rcm_handle_t *hdl,
+ char *resource_name,
+ pid_t pid,
+ uint_t flag,
+ char **info,
+ rcm_info_t **dependent_info)
+{
+ script_info_t *rsi = hdl->module->rsi;
+ char *argv[MAX_ARGS];
+ char *envp[MAX_ENV_PARAMS];
+ char flags_name[MAX_FLAGS_NAME_LEN];
+ int status;
+ int i;
+
+ rcm_log_message(RSCR_TRACE,
+ "script_request_offline: resource = %s flags = %s\n",
+ resource_name,
+ flags_to_name(flag, flags_name, MAX_FLAGS_NAME_LEN));
+
+ *info = NULL;
+
+ (void) mutex_lock(&rsi->channel_lock);
+
+ rsi->hdl = hdl;
+ rsi->cmd = (flag & RCM_QUERY) ? C_QUERYREMOVE : C_PREREMOVE;
+
+ if (rsi->cmd == C_PREREMOVE)
+ add_drreq(rsi, resource_name);
+
+ fill_argv(rsi, argv, resource_name);
+ copy_env(script_env, envp);
+ for (i = 0; envp[i] != NULL; i++)
+ ;
+ envp[i++] = (flag & RCM_FORCE) ? script_env_force : script_env_noforce;
+ envp[i] = NULL;
+
+ status = do_dr(rsi, argv, envp, info);
+
+ (void) mutex_unlock(&rsi->channel_lock);
+ return (status);
+}
+
+/*
+ * notify_online entry point
+ */
+/* ARGSUSED */
+static int
+script_notify_online(rcm_handle_t *hdl,
+ char *resource_name,
+ pid_t pid,
+ uint_t flag,
+ char **info,
+ rcm_info_t **dependent_info)
+{
+ script_info_t *rsi = hdl->module->rsi;
+ char *argv[MAX_ARGS];
+ int status;
+
+ rcm_log_message(RSCR_TRACE, "script_notify_online: resource = %s\n",
+ resource_name);
+
+ *info = NULL;
+
+ (void) mutex_lock(&rsi->channel_lock);
+
+ rsi->hdl = hdl;
+ rsi->cmd = C_UNDOREMOVE;
+ fill_argv(rsi, argv, resource_name);
+
+ status = do_dr(rsi, argv, script_env, info);
+
+ remove_drreq(rsi, resource_name);
+
+ (void) mutex_unlock(&rsi->channel_lock);
+ return (status);
+}
+
+/*
+ * notify_remove entry point
+ */
+/* ARGSUSED */
+static int
+script_notify_remove(rcm_handle_t *hdl,
+ char *resource_name,
+ pid_t pid,
+ uint_t flag,
+ char **info,
+ rcm_info_t **dependent_info)
+{
+ script_info_t *rsi = hdl->module->rsi;
+ char *argv[MAX_ARGS];
+ int status;
+
+ rcm_log_message(RSCR_TRACE, "script_notify_remove: resource = %s\n",
+ resource_name);
+
+ *info = NULL;
+
+ (void) mutex_lock(&rsi->channel_lock);
+
+ rsi->hdl = hdl;
+ rsi->cmd = C_POSTREMOVE;
+ fill_argv(rsi, argv, resource_name);
+
+ status = do_dr(rsi, argv, script_env, info);
+
+ remove_drreq(rsi, resource_name);
+
+ (void) mutex_unlock(&rsi->channel_lock);
+ return (status);
+}
+
+/*
+ * request_suspend entry point
+ */
+/* ARGSUSED */
+static int
+script_request_suspend(rcm_handle_t *hdl,
+ char *resource_name,
+ pid_t pid,
+ timespec_t *interval,
+ uint_t flag,
+ char **info,
+ rcm_info_t **dependent_info)
+{
+ script_info_t *rsi = hdl->module->rsi;
+ char *buf = NULL;
+ char *curptr = NULL;
+ char *argv[MAX_ARGS];
+ char *envp[MAX_ENV_PARAMS];
+ char flags_name[MAX_FLAGS_NAME_LEN];
+ int buflen = 0;
+ long seconds;
+ int status;
+ int i;
+
+ rcm_log_message(RSCR_TRACE,
+ "script_request_suspend: resource = %s flags = %s\n", resource_name,
+ flags_to_name(flag, flags_name, MAX_FLAGS_NAME_LEN));
+
+ *info = NULL;
+
+ (void) mutex_lock(&rsi->channel_lock);
+
+ rsi->hdl = hdl;
+ rsi->cmd = (flag & RCM_QUERY) ? C_QUERYSUSPEND : C_PRESUSPEND;
+
+ if (rsi->cmd == C_PRESUSPEND)
+ add_drreq(rsi, resource_name);
+
+ fill_argv(rsi, argv, resource_name);
+
+ copy_env(script_env, envp);
+ for (i = 0; envp[i] != NULL; i++);
+
+ envp[i++] = (flag & RCM_FORCE) ? script_env_force : script_env_noforce;
+
+ if (interval) {
+ /*
+ * Merge the seconds and nanoseconds, rounding up if there
+ * are any remainder nanoseconds.
+ */
+ seconds = interval->tv_sec + (interval->tv_nsec / 1000000000L);
+ if (interval->tv_nsec % 1000000000L)
+ seconds += (interval->tv_sec > 0) ? 1L : -1L;
+ rcmscript_snprintf(&buf, &buflen, &curptr, script_env_interval,
+ seconds);
+ envp[i++] = buf;
+ }
+
+ envp[i] = NULL;
+
+ status = do_dr(rsi, argv, envp, info);
+
+ (void) mutex_unlock(&rsi->channel_lock);
+ if (buf)
+ free(buf);
+ return (status);
+}
+
+/*
+ * notify_resume entry point
+ */
+/* ARGSUSED */
+static int
+script_notify_resume(rcm_handle_t *hdl,
+ char *resource_name,
+ pid_t pid,
+ uint_t flag,
+ char **info,
+ rcm_info_t **dependent_info)
+{
+ script_info_t *rsi = hdl->module->rsi;
+ char *argv[MAX_ARGS];
+ int status;
+
+ rcm_log_message(RSCR_TRACE, "script_notify_resume: resource = %s\n",
+ resource_name);
+
+ *info = NULL;
+
+ (void) mutex_lock(&rsi->channel_lock);
+
+ rsi->hdl = hdl;
+ rsi->cmd = (flag & RCM_SUSPENDED) ? C_POSTRESUME : C_CANCELSUSPEND;
+ fill_argv(rsi, argv, resource_name);
+
+ status = do_dr(rsi, argv, script_env, info);
+
+ remove_drreq(rsi, resource_name);
+
+ (void) mutex_unlock(&rsi->channel_lock);
+ return (status);
+}
+
+static capacity_descr_t capacity_type[] = {
+ { "SUNW_memory", MATCH_EXACT,
+ "new_pages", "RCM_ENV_CAPACITY",
+ "page_size", "RCM_ENV_UNIT_SIZE",
+ "", ""},
+ { "SUNW_cpu", MATCH_EXACT,
+ "new_total", "RCM_ENV_CAPACITY",
+ "new_cpu_list", "RCM_ENV_CPU_IDS",
+ "", ""},
+ { "SUNW_cpu/set", MATCH_PREFIX,
+ "new_total", "RCM_ENV_CAPACITY",
+ "new_cpu_list", "RCM_ENV_CPU_IDS",
+ "", ""},
+ { "", MATCH_INVALID, "", "" }
+};
+
+static capacity_descr_t *
+get_capacity_descr(char *resource_name)
+{
+ int i;
+
+ for (i = 0; *capacity_type[i].resource_name != '\0'; i++) {
+ if ((capacity_type[i].match_type == MATCH_EXACT &&
+ strcmp(capacity_type[i].resource_name,
+ resource_name) == 0) ||
+ (capacity_type[i].match_type == MATCH_PREFIX &&
+ strncmp(capacity_type[i].resource_name,
+ resource_name,
+ strlen(capacity_type[i].resource_name)) == 0))
+
+ return (&capacity_type[i]);
+ }
+
+ return (NULL);
+}
+
+static int
+build_env_for_capacity(script_info_t *rsi,
+ char *resource_name,
+ uint_t flag,
+ nvlist_t *capacity_info,
+ char *envp[],
+ int *dynamic_env_index,
+ char **errmsg)
+{
+ int p, i;
+ capacity_descr_t *capa = NULL;
+ nvpair_t *nvpair;
+ char *buf;
+ char *curptr;
+ int buflen;
+ int error;
+ uint_t n;
+
+ copy_env(script_env, envp);
+ for (p = 0; envp[p] != NULL; p++)
+ ;
+
+ if (rsi->cmd == C_QUERYCAPACITY || rsi->cmd == C_PRECAPACITY)
+ envp[p++] = (flag & RCM_FORCE) ? script_env_force :
+ script_env_noforce;
+
+ envp[p] = NULL;
+ *dynamic_env_index = p;
+
+ if ((capa = get_capacity_descr(resource_name)) == NULL) {
+ *errmsg = dup_err(RCM_ERROR, MF_UNKNOWN_RSRC_ERR,
+ resource_name, rsi->script_name);
+ return (-1);
+ }
+
+ for (i = 0; *capa->param[i].nvname != '\0'; i++) {
+ nvpair = NULL;
+ while ((nvpair = nvlist_next_nvpair(capacity_info, nvpair))
+ != NULL) {
+ if (strcmp(nvpair_name(nvpair),
+ capa->param[i].nvname) == 0)
+ break;
+ }
+
+ if (nvpair == NULL) {
+ *errmsg = dup_err(RCM_ERROR, MF_NV_ERR,
+ rsi->script_name);
+ return (-1);
+ }
+
+ error = 0;
+ buf = NULL;
+
+ rcmscript_snprintf(&buf, &buflen, &curptr, "%s=",
+ capa->param[i].envname);
+
+ switch (nvpair_type(nvpair)) {
+ case DATA_TYPE_INT16:
+ {
+ int16_t x;
+
+ if (nvpair_value_int16(nvpair, &x) == 0) {
+ rcmscript_snprintf(&buf, &buflen, &curptr,
+ "%hd", (short)x);
+ } else
+ error = 1;
+ break;
+ }
+
+ case DATA_TYPE_UINT16:
+ {
+ uint16_t x;
+
+ if (nvpair_value_uint16(nvpair, &x) == 0) {
+ rcmscript_snprintf(&buf, &buflen, &curptr,
+ "%hu", (unsigned short)x);
+ } else
+ error = 1;
+ break;
+ }
+
+ case DATA_TYPE_INT32:
+ {
+ int32_t x;
+
+ if (nvpair_value_int32(nvpair, &x) == 0) {
+ rcmscript_snprintf(&buf, &buflen, &curptr,
+ "%d", (int)x);
+ } else
+ error = 1;
+ break;
+ }
+
+ case DATA_TYPE_UINT32:
+ {
+ uint32_t x;
+
+ if (nvpair_value_uint32(nvpair, &x) == 0) {
+ rcmscript_snprintf(&buf, &buflen, &curptr,
+ "%u", (uint_t)x);
+ } else
+ error = 1;
+ break;
+ }
+
+ case DATA_TYPE_INT64:
+ {
+ int64_t x;
+
+ if (nvpair_value_int64(nvpair, &x) == 0) {
+ rcmscript_snprintf(&buf, &buflen, &curptr,
+ "%lld", (long long)x);
+ } else
+ error = 1;
+ break;
+ }
+
+ case DATA_TYPE_UINT64:
+ {
+ uint64_t x;
+
+ if (nvpair_value_uint64(nvpair, &x) == 0) {
+ rcmscript_snprintf(&buf, &buflen, &curptr,
+ "%llu", (unsigned long long)x);
+ } else
+ error = 1;
+ break;
+ }
+
+ case DATA_TYPE_INT16_ARRAY:
+ {
+ int16_t *x;
+
+ if (nvpair_value_int16_array(nvpair, &x, &n) == 0) {
+ while (n--) {
+ rcmscript_snprintf(&buf, &buflen,
+ &curptr, "%hd%s",
+ (short)(*x),
+ (n == 0) ? "" : " ");
+ x++;
+ }
+ } else
+ error = 1;
+ break;
+ }
+
+ case DATA_TYPE_UINT16_ARRAY:
+ {
+ uint16_t *x;
+
+ if (nvpair_value_uint16_array(nvpair, &x, &n) == 0) {
+ while (n--) {
+ rcmscript_snprintf(&buf, &buflen,
+ &curptr, "%hu%s",
+ (unsigned short)(*x),
+ (n == 0) ? "" : " ");
+ x++;
+ }
+ } else
+ error = 1;
+ break;
+ }
+
+ case DATA_TYPE_INT32_ARRAY:
+ {
+ int32_t *x;
+
+ if (nvpair_value_int32_array(nvpair, &x, &n) == 0) {
+ while (n--) {
+ rcmscript_snprintf(&buf, &buflen,
+ &curptr, "%d%s",
+ (int)(*x),
+ (n == 0) ? "" : " ");
+ x++;
+ }
+ } else
+ error = 1;
+ break;
+ }
+
+ case DATA_TYPE_UINT32_ARRAY:
+ {
+ uint32_t *x;
+
+ if (nvpair_value_uint32_array(nvpair, &x, &n) == 0) {
+ while (n--) {
+ rcmscript_snprintf(&buf, &buflen,
+ &curptr, "%u%s",
+ (uint_t)(*x),
+ (n == 0) ? "" : " ");
+ x++;
+ }
+ } else
+ error = 1;
+ break;
+ }
+
+ case DATA_TYPE_INT64_ARRAY:
+ {
+ int64_t *x;
+
+ if (nvpair_value_int64_array(nvpair, &x, &n) == 0) {
+ while (n--) {
+ rcmscript_snprintf(&buf, &buflen,
+ &curptr, "%lld%s",
+ (long long)(*x),
+ (n == 0) ? "" : " ");
+ x++;
+ }
+ } else
+ error = 1;
+ break;
+ }
+
+ case DATA_TYPE_UINT64_ARRAY:
+ {
+ uint64_t *x;
+
+ if (nvpair_value_uint64_array(nvpair, &x, &n) == 0) {
+ while (n--) {
+ rcmscript_snprintf(&buf, &buflen,
+ &curptr, "%llu%s",
+ (unsigned long long)(*x),
+ (n == 0) ? "" : " ");
+ x++;
+ }
+ } else
+ error = 1;
+ break;
+ }
+
+ case DATA_TYPE_STRING:
+ {
+ char *x;
+
+ if (nvpair_value_string(nvpair, &x) == 0) {
+ rcmscript_snprintf(&buf, &buflen, &curptr,
+ "%s", x);
+ } else
+ error = 1;
+ break;
+ }
+
+
+ default:
+ error = 1;
+ break;
+ }
+
+ envp[p++] = buf;
+
+ if (error) {
+ envp[p] = NULL;
+ for (p = *dynamic_env_index; envp[p] != NULL; p++)
+ free(envp[p]);
+ *errmsg = dup_err(RCM_ERROR, MF_NV_ERR,
+ rsi->script_name);
+ return (-1);
+ }
+ }
+
+ envp[p] = NULL;
+
+ return (0);
+}
+
+/*
+ * request_capacity_change entry point
+ */
+/* ARGSUSED */
+static int
+script_request_capacity_change(rcm_handle_t *hdl,
+ char *resource_name,
+ pid_t pid,
+ uint_t flag,
+ nvlist_t *capacity_info,
+ char **info,
+ rcm_info_t **dependent_info)
+{
+ script_info_t *rsi = hdl->module->rsi;
+ char *argv[MAX_ARGS];
+ char *envp[MAX_ENV_PARAMS];
+ char flags_name[MAX_FLAGS_NAME_LEN];
+ int status;
+ int dynamic_env_index;
+
+ rcm_log_message(RSCR_TRACE,
+ "script_request_capacity_change: resource = %s flags = %s\n",
+ resource_name,
+ flags_to_name(flag, flags_name, MAX_FLAGS_NAME_LEN));
+
+ *info = NULL;
+
+ (void) mutex_lock(&rsi->channel_lock);
+
+ rsi->hdl = hdl;
+ rsi->cmd = (flag & RCM_QUERY) ? C_QUERYCAPACITY : C_PRECAPACITY;
+ fill_argv(rsi, argv, resource_name);
+
+ if (build_env_for_capacity(rsi, resource_name, flag,
+ capacity_info, envp, &dynamic_env_index, info) == 0) {
+
+ status = do_dr(rsi, argv, envp, info);
+
+ while (envp[dynamic_env_index] != NULL) {
+ free(envp[dynamic_env_index]);
+ dynamic_env_index++;
+ }
+ } else
+ status = RCM_FAILURE;
+
+ (void) mutex_unlock(&rsi->channel_lock);
+ return (status);
+}
+
+/*
+ * notify_capacity_change entry point
+ */
+/* ARGSUSED */
+static int
+script_notify_capacity_change(rcm_handle_t *hdl,
+ char *resource_name,
+ pid_t pid,
+ uint_t flag,
+ nvlist_t *capacity_info,
+ char **info,
+ rcm_info_t **dependent_info)
+{
+ script_info_t *rsi = hdl->module->rsi;
+ char *argv[MAX_ARGS];
+ char *envp[MAX_ENV_PARAMS];
+ int status;
+ int dynamic_env_index;
+
+ rcm_log_message(RSCR_TRACE,
+ "script_notify_capacity_change: resource = %s\n", resource_name);
+
+ *info = NULL;
+
+ (void) mutex_lock(&rsi->channel_lock);
+
+ rsi->hdl = hdl;
+ rsi->cmd = C_POSTCAPACITY;
+ fill_argv(rsi, argv, resource_name);
+
+ if (build_env_for_capacity(rsi, resource_name, flag,
+ capacity_info, envp, &dynamic_env_index, info) == 0) {
+
+ status = do_dr(rsi, argv, envp, info);
+
+ while (envp[dynamic_env_index] != NULL) {
+ free(envp[dynamic_env_index]);
+ dynamic_env_index++;
+ }
+ } else
+ status = RCM_FAILURE;
+
+ (void) mutex_unlock(&rsi->channel_lock);
+ return (status);
+}
+
+/* Log the message to syslog */
+static void
+log_msg(script_info_t *rsi, int level, char *msg)
+{
+ rcm_log_msg(level, MS_LOG_MSG, rsi->script_name, msg);
+}
+
+/*PRINTFLIKE2*/
+static char *
+dup_err(int level, char *format, ...)
+{
+ va_list ap;
+ char buf1[1];
+ char *buf2;
+ int n;
+
+ va_start(ap, format);
+ n = vsnprintf(buf1, 1, format, ap);
+ va_end(ap);
+
+ if (n > 0) {
+ n++;
+ if (buf2 = (char *)malloc(n)) {
+ va_start(ap, format);
+ n = vsnprintf(buf2, n, format, ap);
+ va_end(ap);
+ if (n > 0) {
+ if (level != -1)
+ rcm_log_message(level, buf2);
+ return (buf2);
+ }
+ free(buf2);
+ }
+ }
+
+ return (NULL);
+}
+
+/*PRINTFLIKE4*/
+static void
+rcmscript_snprintf(char **buf, int *buflen, char **curptr, char *format, ...)
+{
+/* must be power of 2 otherwise RSCR_ROUNDUP would break */
+#define SPRINTF_CHUNK_LEN 512
+#define SPRINTF_MIN_CHUNK_LEN 64
+
+ va_list ap;
+ int offset, bytesneeded, bytesleft, error_num;
+
+ if (*buf == NULL) {
+ *buflen = 0;
+ *curptr = NULL;
+ }
+
+ offset = *curptr - *buf;
+ bytesneeded = SPRINTF_MIN_CHUNK_LEN;
+ bytesleft = *buflen - offset;
+
+ /* LINTED */
+ while (1) {
+ if (bytesneeded > bytesleft) {
+ *buflen += RSCR_ROUNDUP(bytesneeded - bytesleft,
+ SPRINTF_CHUNK_LEN);
+ if ((*buf = (char *)realloc(*buf, *buflen)) == NULL) {
+ error_num = errno;
+ rcm_log_message(RCM_ERROR,
+ MF_MEMORY_ALLOCATION_ERR,
+ strerror(error_num));
+ rcmd_exit(error_num);
+ /*NOTREACHED*/
+ }
+ *curptr = *buf + offset;
+ bytesleft = *buflen - offset;
+ }
+
+ va_start(ap, format);
+ bytesneeded = vsnprintf(*curptr, bytesleft, format, ap);
+ va_end(ap);
+
+ if (bytesneeded < 0) {
+ /* vsnprintf encountered an error */
+ error_num = errno;
+ rcm_log_message(RCM_ERROR, MF_FUNC_CALL_ERR,
+ "vsnprintf", strerror(error_num));
+ rcmd_exit(error_num);
+ /*NOTREACHED*/
+
+ } else if (bytesneeded < bytesleft) {
+ /* vsnprintf succeeded */
+ *curptr += bytesneeded;
+ return;
+
+ } else {
+ bytesneeded++; /* to account for storage for '\0' */
+ }
+ }
+}
+
+static char *
+rcmscript_strdup(char *str)
+{
+ char *dupstr;
+
+ if ((dupstr = strdup(str)) == NULL) {
+ rcm_log_message(RCM_ERROR, MF_MEMORY_ALLOCATION_ERR,
+ strerror(errno));
+ rcmd_exit(errno);
+ /*NOTREACHED*/
+ }
+
+ return (dupstr);
+}
+
+static void *
+rcmscript_malloc(size_t len)
+{
+ void *ptr;
+
+ if ((ptr = malloc(len)) == NULL) {
+ rcm_log_message(RCM_ERROR, MF_MEMORY_ALLOCATION_ERR,
+ strerror(errno));
+ rcmd_exit(errno);
+ /*NOTREACHED*/
+ }
+
+ return (ptr);
+}
+
+static void *
+rcmscript_calloc(size_t nelem, size_t elsize)
+{
+ void *ptr;
+
+ if ((ptr = calloc(nelem, elsize)) == NULL) {
+ rcm_log_message(RCM_ERROR, MF_MEMORY_ALLOCATION_ERR,
+ strerror(errno));
+ rcmd_exit(errno);
+ /*NOTREACHED*/
+ }
+
+ return (ptr);
+}