/* * 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" /* * PICL plug-in to create environment tree nodes. * This plugin should only be installed in the platform directories * of supported systems, such as /usr/platform/picl/plugins/SUNW,<>. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int init_done; #define BUFSZ 512 typedef struct { char name[32]; } EName_t; typedef struct { int32_t (*funcp)(void *, char *); int32_t num_objects; EName_t *obj_list; char routine[64]; } ETask_t; typedef struct interval_info { int32_t interval; int32_t num_tasks; ETask_t *task_list; pthread_t thread; struct interval_info *next; } EInterval_t; static EInterval_t *first_interval; static psvc_opaque_t hdlp; sem_t timer_sem; pthread_mutex_t timer_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t timer_cond = PTHREAD_COND_INITIALIZER; pthread_t timer_thread_id; extern int ptree_get_node_by_path(const char *, picl_nodehdl_t *); /* Timer states */ #define NOT_READY 0 #define READY 1 #define HAVE_REQUEST 2 #define ACTIVE 3 int timer_state = NOT_READY; int app_timeout; /* Lock State Loop State Definitions */ #define STATE_CHANGED 1 #define STATE_NOT_CHANGED 0 #ifdef DEBUG static int32_t debug_flag = 1; #else static int32_t debug_flag = 0; #endif static char library[PATH_MAX]; #define PSVC_PLUGIN_VERSION PICLD_PLUGIN_VERSION_1 #pragma init(psvc_plugin_register) /* place in .init section */ typedef struct { char parent_path[256]; char child_name[32]; picl_nodehdl_t child_node; } psvc_name_t; psvc_name_t *psvc_paths; #define MUTEX_LOCK_FAILED_MSG gettext("platsvcd: pthread_mutex_lock %s\n") #define CV_WAIT_FAILED_MSG gettext("platsvcd: pthread_cond_wait %s\n") #define CV_TWAIT_FAILED_MSG gettext("platsvcd: pthread_cond_timed_wait %s\n") #define SEM_WAIT_FAILED_MSG gettext("platsvcd: sem_wait failed %s\n") #define PSVC_APP_DEATH_MSG gettext("PSVC application death detected\n") #define POLICY_FAILED_MSG gettext("ERROR running %s on %s (%d)") #define ID_NOT_FOUND_MSG gettext("%s: Can't determine id of %s\n") #define CLASS_NOT_FOUND_MSG gettext("%s: Can't determine class of %s\n") #define SUBCLASS_NOT_FOUND_MSG gettext("%s: Can't determine subclass of %s\n") #define NODE_NOT_FOUND_MSG gettext("%s: Can't determine node of %s\n") #define SIZE_NOT_FOUND_MSG gettext("%s: Couldn't determine size of %s\n") #define PTREE_CREATE_TABLE_FAILED_MSG \ gettext("%s: ptree_create_table failed, %s\n") #define PTREE_CREATE_PROP_FAILED_MSG \ gettext("%s: ptree_create_prop failed, %s\n") #define PTREE_CREATE_NODE_FAILED_MSG \ gettext("%s: ptree_create_node failed, %s\n") #define PTREE_ADD_ROW_FAILED_MSG gettext("%s: ptree_add_row_to_table: %s\n") #define PTREE_ADD_NODE_FAILED_MSG gettext("%s: ptree_add_node: %s\n") #define PTREE_ADD_PROP_FAILED_MSG gettext("%s: ptree_add_prop: %s\n") #define PTREE_GET_ROOT_FAILED_MSG gettext("%s: ptree_get_root: %s\n") #define CREATE_PROP_FAILED_MSG gettext("%s: Error creating property %s\n") #define INVALID_FILE_FORMAT_MSG gettext("%s: Invalid file format\n") #define INVALID_FILE_FORMAT1_MSG gettext("%s: Invalid file format %s\n") #define PSVC_INIT_ERR_MSG gettext("%s: Error in psvc_init(): %s\n") #define SYSINFO_FAILED_MSG gettext("%s: Can't determine platform type\n") #define FILE_OPEN_FAILED_MSG gettext("%s: Can't open file %s\n") #define MALLOC_FAILED_MSG gettext("%s: malloc failed, %s\n") #define UNKNOWN_CLASS_MSG gettext("%s: Unknown class\n") #define NODE_PROP_FAILED_MSG gettext("%s: node_property: %s\n") #define LOCK_STRING_MAX 32 picl_nodehdl_t system_node; static picl_nodehdl_t lock_node; static char env_lock_state[LOCK_STRING_MAX] = PSVC_LOCK_ENABLED; static pthread_mutex_t env_lock_mutex = PTHREAD_MUTEX_INITIALIZER; static char *class_name[] = { "temperature-sensor", "fan", "led", "picl", "digital-sensor", "digital-control", "gpio", "fan-tachometer", "switch", "keyswitch", "gpio", "i2c" }; #define NUM_CLASSES (sizeof (class_name) / sizeof (char *)) struct proj_prop { /* projected property */ picl_prophdl_t handle; picl_nodehdl_t dst_node; char name[32]; }; struct propinfo { char *name; uint32_t type; uint32_t size; uint32_t access; }; struct propinfo common[] = { {"State", PICL_PTYPE_CHARSTRING, 32, PICL_READ | PICL_WRITE | PICL_VOLATILE}, {"FaultInformation", PICL_PTYPE_CHARSTRING, 32, PICL_READ | PICL_VOLATILE} }; #define COMMON_COUNT (sizeof (common) / sizeof (struct propinfo)) struct propinfo led_properties[] = { {"Color", PICL_PTYPE_CHARSTRING, 32, PICL_READ | PICL_VOLATILE}, {"IsLocator", PICL_PTYPE_CHARSTRING, 32, PICL_READ | PICL_VOLATILE}, {"LocatorName", PICL_PTYPE_CHARSTRING, 32, PICL_READ | PICL_VOLATILE} }; /* * We define the amount of LED properties to 1 because not all LED's have * the two remainding properties. This number is augmented in psvc_plugin_init * when it sees an LED of subclass 2. */ #define LED_COUNT 1 struct propinfo temperature_sensor_properties[] = { {"Temperature", PICL_PTYPE_INT, 4, PICL_READ | PICL_VOLATILE}, {"LowWarningThreshold", PICL_PTYPE_INT, 4, PICL_READ | PICL_VOLATILE}, {"LowShutdownThreshold", PICL_PTYPE_INT, 4, PICL_READ | PICL_VOLATILE}, {"HighWarningThreshold", PICL_PTYPE_INT, 4, PICL_READ | PICL_VOLATILE}, {"HighShutdownThreshold", PICL_PTYPE_INT, 4, PICL_READ | PICL_VOLATILE} }; #define TEMP_SENSOR_COUNT \ (sizeof (temperature_sensor_properties) / sizeof (struct propinfo)) struct propinfo digi_sensor_properties[] = { {"AtoDSensorValue", PICL_PTYPE_INT, 4, PICL_READ | PICL_VOLATILE}, {"LowWarningThreshold", PICL_PTYPE_INT, 4, PICL_READ | PICL_VOLATILE}, {"LowShutdownThreshold", PICL_PTYPE_INT, 4, PICL_READ | PICL_VOLATILE}, {"HighWarningThreshold", PICL_PTYPE_INT, 4, PICL_READ | PICL_VOLATILE}, {"HighShutdownThreshold", PICL_PTYPE_INT, 4, PICL_READ | PICL_VOLATILE} }; #define DIGI_SENSOR_COUNT \ (sizeof (digi_sensor_properties) / sizeof (struct propinfo)) struct propinfo boolgpio_properties[] = { {"Gpio-value", PICL_PTYPE_UNSIGNED_INT, sizeof (boolean_t), PICL_READ | PICL_WRITE | PICL_VOLATILE}, {"#Bits", PICL_PTYPE_UNSIGNED_INT, 4, PICL_READ |PICL_VOLATILE} }; #define BOOLGPIO_COUNT (sizeof (boolgpio_properties) / sizeof (struct propinfo)) struct propinfo gpio8_properties[] = { {"Gpio-value", PICL_PTYPE_UNSIGNED_INT, 1, PICL_READ | PICL_WRITE | PICL_VOLATILE}, {"#Bits", PICL_PTYPE_UNSIGNED_INT, 4, PICL_READ |PICL_VOLATILE} }; #define GPIO8_COUNT (sizeof (gpio8_properties) / sizeof (struct propinfo)) struct propinfo digictrl_properties[] = { {"DtoAControlValue", PICL_PTYPE_INT, 4, PICL_READ | PICL_WRITE | PICL_VOLATILE}, }; #define DIGICTRL_COUNT (sizeof (digictrl_properties) / sizeof (struct propinfo)) struct classinfo { struct propinfo *props; int32_t count; } class_properties[] = { {temperature_sensor_properties, TEMP_SENSOR_COUNT}, /* temp sensor */ {0, 0}, /* fan, only has projected properties */ {led_properties, LED_COUNT}, {0, 0}, /* system class */ {digi_sensor_properties, DIGI_SENSOR_COUNT}, /* digital sensor */ {digictrl_properties, DIGICTRL_COUNT}, {boolgpio_properties, BOOLGPIO_COUNT}, {digi_sensor_properties, DIGI_SENSOR_COUNT}, /* fan tach */ {0, 0}, {0, 0}, {gpio8_properties, GPIO8_COUNT}, {0, 0}, }; struct prop_trans { char *picl_class; char *picl_prop; int32_t psvc_prop; } picl_prop_trans[] = { {"digital-sensor", "AtoDSensorValue", PSVC_SENSOR_VALUE_ATTR}, {"digital-sensor", "LowWarningThreshold", PSVC_LO_WARN_ATTR}, {"digital-sensor", "LowShutdownThreshold", PSVC_LO_SHUT_ATTR}, {"digital-sensor", "HighWarningThreshold", PSVC_HI_WARN_ATTR}, {"digital-sensor", "HighShutdownThreshold", PSVC_HI_SHUT_ATTR}, {"digital-control", "DtoAControlValue", PSVC_CONTROL_VALUE_ATTR}, {"fan-tachometer", "AtoDSensorValue", PSVC_SENSOR_VALUE_ATTR}, {"fan-tachometer", "LowWarningThreshold", PSVC_LO_WARN_ATTR}, {"fan-tachometer", "LowShutdownThreshold", PSVC_LO_SHUT_ATTR}, {"fan-tachometer", "HighWarningThreshold", PSVC_HI_WARN_ATTR}, {"fan-tachometer", "HighShutdownThreshold", PSVC_HI_SHUT_ATTR}, {"temperature-sensor", "Temperature", PSVC_SENSOR_VALUE_ATTR}, {"temperature-sensor", "LowWarningThreshold", PSVC_LO_WARN_ATTR}, {"temperature-sensor", "LowShutdownThreshold", PSVC_LO_SHUT_ATTR}, {"temperature-sensor", "HighWarningThreshold", PSVC_HI_WARN_ATTR}, {"temperature-sensor", "HighShutdownThreshold", PSVC_HI_SHUT_ATTR}, {"led", "State", PSVC_LED_STATE_ATTR}, {"led", "Color", PSVC_LED_COLOR_ATTR}, {"switch", "State", PSVC_SWITCH_STATE_ATTR}, {"keyswitch", "State", PSVC_SWITCH_STATE_ATTR}, {"i2c", "State", PSVC_PROBE_RESULT_ATTR} }; #define PICL_PROP_TRANS_COUNT \ (sizeof (picl_prop_trans) / sizeof (struct prop_trans)) typedef struct { char name[32]; picl_nodehdl_t node; } picl_psvc_t; struct assoc_pair { char antecedent[32]; char dependent[32]; }; struct handle { uint32_t obj_count; picl_psvc_t *objects; FILE *fp; } psvc_hdl; struct proj_prop *prop_list; uint32_t proj_prop_count; int psvc_picl_nodes; void psvc_plugin_init(void); void psvc_plugin_fini(void); picld_plugin_reg_t psvc_reg = { PSVC_PLUGIN_VERSION, PICLD_PLUGIN_CRITICAL, "PSVC", psvc_plugin_init, psvc_plugin_fini }; /* * psvcplugin_add_children was written so that devices which are hotplugable * will be able to add in all thier children and children's children. The * routine takes in the path of a parent and then searches the psvc_paths * array to find all of it's children. It in turns adds the child and then * recursively check to see if it had children and add them too. */ void psvcplugin_add_children(char *parent_path) { int i; picl_nodehdl_t parent_node; char next_path[256]; for (i = 0; i < psvc_picl_nodes; ++i) { if (strcmp(parent_path, psvc_paths[i].parent_path) == 0) { ptree_get_node_by_path(parent_path, &parent_node); ptree_add_node(parent_node, psvc_paths[i].child_node); snprintf(next_path, sizeof (next_path), "%s/%s", parent_path, psvc_paths[i].child_name); psvcplugin_add_children(next_path); } } } void psvcplugin_lookup(char *name, char *parent, picl_nodehdl_t *node) { int i; for (i = 0; i < psvc_picl_nodes; ++i) { if (strcmp(name, psvc_paths[i].child_name) == 0) { strcpy(parent, psvc_paths[i].parent_path); *node = psvc_paths[i].child_node; } } } void timer_thread() { struct timespec timeout; int status; status = pthread_mutex_lock(&timer_mutex); if (status != 0) { syslog(LOG_ERR, MUTEX_LOCK_FAILED_MSG, strerror(status)); } for (;;) { /* wait for thread to tell us to start timer */ timer_state = READY; do { status = pthread_cond_wait(&timer_cond, &timer_mutex); } while (timer_state == READY && status == 0); if (status != 0) { syslog(LOG_ERR, CV_WAIT_FAILED_MSG, strerror(status)); } /* * Will get signalled after semaphore acquired, * or when timeout occurs. */ clock_gettime(CLOCK_REALTIME, &timeout); timeout.tv_sec += app_timeout; if (timer_state == HAVE_REQUEST) { timer_state = ACTIVE; do { status = pthread_cond_timedwait(&timer_cond, &timer_mutex, &timeout); } while (timer_state == ACTIVE && status == 0); } if (status != 0) { if (status == ETIMEDOUT) { syslog(LOG_ERR, PSVC_APP_DEATH_MSG); pthread_mutex_lock(&env_lock_mutex); strlcpy(env_lock_state, PSVC_LOCK_ENABLED, LOCK_STRING_MAX); pthread_mutex_unlock(&env_lock_mutex); } else { syslog(LOG_ERR, CV_TWAIT_FAILED_MSG, strerror(status)); } } } } static int lock_state_loop(char *set_lock_state) { pthread_mutex_lock(&env_lock_mutex); if (strcmp(env_lock_state, PSVC_LOCK_ENABLED) == 0) { strlcpy(env_lock_state, set_lock_state, LOCK_STRING_MAX); pthread_mutex_unlock(&env_lock_mutex); return (STATE_NOT_CHANGED); } pthread_mutex_unlock(&env_lock_mutex); return (STATE_CHANGED); } static int timed_lock_wait(char *set_lock_state) { int32_t status; /* Only want one timer active at a time */ do { status = sem_wait(&timer_sem); } while (status == -1 && errno == EINTR); if (status == -1) return (status); while (timer_state != READY) sched_yield(); pthread_mutex_lock(&timer_mutex); timer_state = HAVE_REQUEST; pthread_cond_signal(&timer_cond); /* start timer */ pthread_mutex_unlock(&timer_mutex); /* * We now spin checking the state env_lock_state for it to change to * enabled. */ while (lock_state_loop(set_lock_state)) sleep(1); pthread_mutex_lock(&timer_mutex); if (timer_state == ACTIVE) { timer_state = NOT_READY; pthread_cond_signal(&timer_cond); /* stop timer */ } if (timer_state == HAVE_REQUEST) { /* cancel request */ timer_state = NOT_READY; } pthread_mutex_unlock(&timer_mutex); sem_post(&timer_sem); return (0); } static void lock_and_run(ETask_t *tp, int32_t obj_num) { int32_t status; /* Grab mutex to stop the env_lock from being changed. */ pthread_mutex_lock(&env_lock_mutex); /* * Check to see if the lock is anything but Enabled. If so, we then * goto our timer routine to wait for it to become enabled. * If not then set it to RUNNING and run policy. */ if (strcmp(env_lock_state, PSVC_LOCK_ENABLED) != 0) { /* drop mutex and goto timer */ pthread_mutex_unlock(&env_lock_mutex); status = timed_lock_wait(PSVC_LOCK_RUNNING); if (status == -1) { syslog(LOG_ERR, SEM_WAIT_FAILED_MSG); } } else { strlcpy(env_lock_state, PSVC_LOCK_RUNNING, LOCK_STRING_MAX); pthread_mutex_unlock(&env_lock_mutex); } status = (*tp->funcp)(hdlp, (tp->obj_list + obj_num)->name); if (status == PSVC_FAILURE && errno != ENODEV) { char dev_name[32]; psvc_get_attr(hdlp, (tp->obj_list + obj_num)->name, PSVC_LABEL_ATTR, dev_name); syslog(LOG_ERR, POLICY_FAILED_MSG, tp->routine, dev_name, (tp->obj_list + obj_num)->name); syslog(LOG_ERR, strerror(errno)); } /* The policy is done so set the lock back to ENABLED. */ pthread_mutex_lock(&env_lock_mutex); strlcpy(env_lock_state, PSVC_LOCK_ENABLED, LOCK_STRING_MAX); pthread_mutex_unlock(&env_lock_mutex); } static void run_policies(EInterval_t *ip) { ETask_t *tp; int32_t i, j; do { if (ip->interval) { int remaining = ip->interval; do { remaining = sleep(remaining); } while (remaining > 0); } for (i = 0; i < ip->num_tasks; ++i) { tp = &ip->task_list[i]; for (j = 0; j < tp->num_objects; ++j) { lock_and_run(tp, j); } } } while (ip->interval); } static void thread_setup(EInterval_t *ip) { int32_t status; status = pthread_create(&ip->thread, NULL, (void *(*)())run_policies, ip); if (status != 0) { if (debug_flag) syslog(LOG_ERR, strerror(errno)); exit(-1); } } static int32_t load_policy(const char *library, const char *policy, int32_t (**addr)(void *, char *)) { void *hp; hp = dlopen(library, RTLD_NODELETE | RTLD_NOW | RTLD_GLOBAL); if (hp == NULL) { if (debug_flag) { char *errstr = dlerror(); syslog(LOG_ERR, errstr); } exit(1); } *addr = (int32_t (*)(void *, char *))dlsym(hp, policy); if (*addr == NULL) { if (debug_flag) { char *errstr = dlerror(); syslog(LOG_ERR, errstr); } exit(1); } (void) dlclose(hp); return (0); } static int32_t get_timeout(FILE *fp, int *timeout) { char buf[BUFSZ]; char name[32]; char *cp; /* skip blank lines */ do { cp = fgets(buf, BUFSZ, fp); if (cp == NULL) return (1); while (isspace(*cp)) ++cp; sscanf(buf, "%s %d", name, timeout); } while (*cp == 0 || *cp == '\n' || strcmp(name, "TIMEOUT") != 0); if (strcmp(name, "TIMEOUT") != 0) { errno = EINVAL; return (-1); } return (0); } static int32_t load_interval(FILE *fp, EInterval_t **ipp) { char buf[BUFSZ]; int32_t found; EInterval_t *ip; ETask_t *tp; int32_t tasks; int32_t status, i, j; int32_t interval; char name[32]; char *cp; /* skip blank lines */ do { cp = fgets(buf, BUFSZ, fp); if (cp == NULL) return (1); while (isspace(*cp)) ++cp; } while (*cp == 0 || *cp == '\n'); found = sscanf(buf, "%s %d %d", name, &interval, &tasks); if (found != 3) { errno = EINVAL; return (-1); } if (strcmp(name, "INTERVAL") != 0) { errno = EINVAL; return (-1); } ip = (EInterval_t *)malloc(sizeof (EInterval_t)); if (ip == NULL) return (-1); ip->num_tasks = tasks; ip->interval = interval; ip->next = NULL; /* allocate and load table */ ip->task_list = (ETask_t *)malloc(ip->num_tasks * sizeof (ETask_t)); if (ip->task_list == NULL) return (-1); for (i = 0; i < ip->num_tasks; ++i) { tp = &ip->task_list[i]; (void) fgets(buf, BUFSZ, fp); found = sscanf(buf, "%s %s %s", name, library, tp->routine); if (found != 3) { errno = EINVAL; return (-1); } status = load_policy(library, tp->routine, &tp->funcp); if (status == -1) return (-1); found = fscanf(fp, "%d", &tp->num_objects); if (found != 1) { if (debug_flag) syslog(LOG_ERR, "No list of objects for task"); errno = EINVAL; return (-1); } tp->obj_list = (EName_t *)malloc(tp->num_objects * sizeof (EName_t)); if (tp->obj_list == NULL) return (-1); for (j = 0; j < tp->num_objects; ++j) { found = fscanf(fp, "%s", tp->obj_list + j); if (found != 1) { if (debug_flag) syslog(LOG_ERR, "Wrong number of objects for task"); errno = EINVAL; return (-1); } } (void) fgets(buf, BUFSZ, fp); /* reads newline on data line */ (void) fgets(buf, BUFSZ, fp); if (strncmp(buf, "TASK_END", 8) != 0) { if (debug_flag) syslog(LOG_ERR, "Expected TASK_END, task %s", tp->routine); errno = EINVAL; return (-1); } } (void) fgets(buf, BUFSZ, fp); if (strncmp(buf, "INTERVAL_END", 12) != 0) { if (debug_flag) syslog(LOG_ERR, "Expected INTERVAL_END"); errno = EINVAL; return (-1); } *ipp = ip; return (0); } void init_daemon(void) { int32_t intervals = 0; int32_t threads = 0; int32_t status; FILE *fp; char filename[PATH_MAX]; char platform[64]; EInterval_t *ip, *prev; if (sysinfo(SI_PLATFORM, platform, sizeof (platform)) == -1) { if (debug_flag) syslog(LOG_ERR, strerror(errno)); return; } (void) sprintf(filename, "/usr/platform/%s/lib/platsvcd.conf", platform); if ((fp = fopen(filename, "r")) == NULL) { if (debug_flag) syslog(LOG_ERR, strerror(errno)); return; } status = get_timeout(fp, &app_timeout); if (status != 0) { if (debug_flag) syslog(LOG_ERR, strerror(errno)); return; } status = sem_init(&timer_sem, 0, 1); if (status == -1) { if (debug_flag) syslog(LOG_ERR, strerror(errno)); return; } status = pthread_create(&timer_thread_id, NULL, (void *(*)())timer_thread, 0); if (status != 0) { if (debug_flag) syslog(LOG_ERR, strerror(errno)); return; } /* get timer thread running */ while (timer_state != READY) sched_yield(); for (;;) { status = load_interval(fp, &ip); if (status != 0) break; #ifdef lint prev = NULL; #endif if (first_interval == 0) first_interval = ip; else prev->next = ip; prev = ip; ++intervals; if (ip->interval == 0) { run_policies(ip); } else { thread_setup(ip); ++threads; } } if (intervals == 0) { if (debug_flag) syslog(LOG_ERR, "ERROR: No policies started"); return; } if (status == -1) { if (debug_flag) syslog(LOG_ERR, strerror(errno)); return; } return; } static int32_t count_records(FILE *fp, char *end, uint32_t *countp) { long first_record; char *ret; char buf[BUFSZ]; uint32_t count = 0; first_record = ftell(fp); while ((ret = fgets(buf, BUFSZ, fp)) != NULL) { if (strncmp(end, buf, strlen(end)) == 0) break; ++count; } if (ret == NULL) { errno = EINVAL; return (-1); } fseek(fp, first_record, SEEK_SET); *countp = count; return (0); } /* * Find start of a section within the config file, * Returns number of records in the section. * FILE *fd is set to first data record within section. */ static int32_t find_file_section(FILE *fd, char *start) { char *ret; char buf[BUFSZ]; char name[32]; int found; fseek(fd, 0, SEEK_SET); while ((ret = fgets(buf, BUFSZ, fd)) != NULL) { if (strncmp(start, buf, strlen(start)) == 0) break; } if (ret == NULL) { errno = EINVAL; return (-1); } found = sscanf(buf, "%s", name); if (found != 1) { errno = EINVAL; return (-1); } else { return (0); } } static int32_t name_compare_qsort(picl_psvc_t *s1, picl_psvc_t *s2) { return (strcmp(s1->name, s2->name)); } static int32_t name_compare_bsearch(char *s1, picl_psvc_t *s2) { return (strcmp(s1, s2->name)); } /* * Create a property and add it to the specified node. * PICL will take a segmentation violation if a volatile property * has a non-zero size. */ static int32_t node_property(picl_nodehdl_t node, int (*read)(ptree_rarg_t *, void *), int (*write)(ptree_warg_t *, const void *), picl_prop_type_t type, unsigned int size, unsigned int accessmode, char *name, void *value) { ptree_propinfo_t propinfo; picl_prophdl_t prophdl; int err; propinfo.version = PSVC_PLUGIN_VERSION; if (accessmode & PICL_VOLATILE) { propinfo.read = read; propinfo.write = write; } else { propinfo.read = NULL; propinfo.write = NULL; } propinfo.piclinfo.type = type; propinfo.piclinfo.accessmode = accessmode; propinfo.piclinfo.size = size; strcpy(propinfo.piclinfo.name, name); err = ptree_create_prop(&propinfo, value, &prophdl); if (err != 0) { return (err); } err = ptree_add_prop(node, prophdl); if (err != 0) return (err); return (0); } static void init_err(char *fmt, char *arg1, char *arg2) { char msg[256]; sprintf(msg, fmt, arg1, arg2); syslog(LOG_ERR, msg); } static int projected_lookup(picl_prophdl_t proph, struct proj_prop **dstp) { int i; for (i = 0; i < proj_prop_count; ++i) { if (prop_list[i].handle == proph) { *dstp = &prop_list[i]; return (PICL_SUCCESS); } } return (PICL_INVALIDHANDLE); } int projected_read(ptree_rarg_t *rarg, void *buf) { ptree_propinfo_t propinfo; struct proj_prop *dstinfo; int err; err = projected_lookup(rarg->proph, &dstinfo); if (err != 0) { return (PICL_FAILURE); } err = ptree_get_propinfo(rarg->proph, &propinfo); if (err != 0) return (err); err = ptree_get_propval_by_name(dstinfo->dst_node, dstinfo->name, buf, propinfo.piclinfo.size); if (err != 0) return (err); return (PICL_SUCCESS); } int projected_write(ptree_warg_t *warg, const void *buf) { ptree_propinfo_t propinfo; struct proj_prop *dstinfo; int err; err = projected_lookup(warg->proph, &dstinfo); if (err != 0) { return (PICL_FAILURE); } err = ptree_get_propinfo(warg->proph, &propinfo); if (err != 0) return (err); err = ptree_update_propval_by_name(dstinfo->dst_node, dstinfo->name, buf, propinfo.piclinfo.size); if (err != 0) return (err); return (PICL_SUCCESS); } int psvc_read_volatile(ptree_rarg_t *rarg, void *buf) { ptree_propinfo_t propinfo; char name[32], class[32]; int err, i; int32_t attr_num = -1; int32_t use_attr_num = 0; err = ptree_get_propval_by_name(rarg->nodeh, "name", name, sizeof (name)); if (err != 0) { return (err); } err = ptree_get_propval_by_name(rarg->nodeh, "_class", class, sizeof (class)); if (err != 0) { return (err); } err = ptree_get_propinfo(rarg->proph, &propinfo); if (err != 0) { return (err); } for (i = 0; i < PICL_PROP_TRANS_COUNT; i++) { if ((strcmp(class, picl_prop_trans[i].picl_class) == 0) && (strcmp(propinfo.piclinfo.name, picl_prop_trans[i].picl_prop) == 0)) { attr_num = i; break; } } if (attr_num == -1) for (i = 0; i < ATTR_STR_TAB_SIZE; i++) { if (strcmp(propinfo.piclinfo.name, attr_str_tab[i]) == 0) { attr_num = i; use_attr_num = 1; break; } } if (use_attr_num) err = psvc_get_attr(hdlp, name, attr_num, buf); else err = psvc_get_attr(hdlp, name, picl_prop_trans[attr_num].psvc_prop, buf); if (err != 0) { return (PICL_FAILURE); } return (PICL_SUCCESS); } int psvc_write_volatile(ptree_warg_t *warg, const void *buf) { ptree_propinfo_t propinfo; char name[32], class[32]; int err, i; int32_t attr_num = -1; int32_t use_attr_num = 0; if (warg->cred.dc_euid != 0) return (PICL_PERMDENIED); err = ptree_get_propval_by_name(warg->nodeh, "name", name, sizeof (name)); if (err != 0) { return (err); } err = ptree_get_propval_by_name(warg->nodeh, "_class", class, sizeof (class)); if (err != 0) { return (err); } err = ptree_get_propinfo(warg->proph, &propinfo); if (err != 0) { return (err); } for (i = 0; i < PICL_PROP_TRANS_COUNT; i++) { if ((strcmp(class, picl_prop_trans[i].picl_class) == 0) && (strcmp(propinfo.piclinfo.name, picl_prop_trans[i].picl_prop) == 0)) { attr_num = i; break; } } if (attr_num == -1) for (i = 0; i < ATTR_STR_TAB_SIZE; i++) { if (strcmp(propinfo.piclinfo.name, attr_str_tab[i]) == 0) { attr_num = i; use_attr_num = 1; break; } } if (use_attr_num) err = psvc_set_attr(hdlp, name, attr_num, (void *)buf); else err = psvc_set_attr(hdlp, name, picl_prop_trans[attr_num].psvc_prop, (void *)buf); if (err != 0) { return (PICL_FAILURE); } return (PICL_SUCCESS); } void create_reference_properties(struct assoc_pair *assoc_tbl, int32_t count, char *assoc_name) { picl_psvc_t *aobjp, *dobjp; picl_prophdl_t tbl_hdl; picl_nodehdl_t *dep_list; ptree_propinfo_t propinfo; char *funcname = "create_reference_properties"; char name[PICL_PROPNAMELEN_MAX]; int32_t i, j, offset; int32_t dependents; int32_t err; char class[PICL_CLASSNAMELEN_MAX]; for (i = 0; i < count; ++i) { /* antecedent */ aobjp = (picl_psvc_t *)bsearch(assoc_tbl[i].antecedent, psvc_hdl.objects, psvc_hdl.obj_count, sizeof (picl_psvc_t), (int (*)(const void *, const void *)) name_compare_bsearch); if (aobjp == NULL) { init_err(ID_NOT_FOUND_MSG, funcname, assoc_tbl[i].antecedent); return; } /* skip if table already created */ if (ptree_get_propval_by_name(aobjp->node, assoc_name, &tbl_hdl, sizeof (tbl_hdl)) == 0) { continue; } /* create a new table */ err = ptree_create_table(&tbl_hdl); if (err != 0) { init_err(PTREE_CREATE_TABLE_FAILED_MSG, funcname, picl_strerror(err)); return; } err = node_property(aobjp->node, NULL, NULL, PICL_PTYPE_TABLE, sizeof (tbl_hdl), PICL_READ, assoc_name, &tbl_hdl); if (err != 0) { init_err(CREATE_PROP_FAILED_MSG, funcname, picl_strerror(err)); return; } /* determine number of elements in the table */ dependents = 0; for (j = i; j < count; ++j) { if (strcmp(aobjp->name, assoc_tbl[j].antecedent) == 0) ++dependents; } dep_list = (picl_nodehdl_t *)malloc(sizeof (picl_nodehdl_t) * dependents); if (dep_list == NULL) { init_err(MALLOC_FAILED_MSG, funcname, strerror(errno)); return; } /* build row of reference properties */ offset = 0; for (j = i; j < count; ++j) { if (strcmp(aobjp->name, assoc_tbl[j].antecedent) != 0) continue; dobjp = (picl_psvc_t *)bsearch(assoc_tbl[j].dependent, psvc_hdl.objects, psvc_hdl.obj_count, sizeof (picl_psvc_t), (int (*)(const void *, const void *)) name_compare_bsearch); if (dobjp == NULL) { init_err(ID_NOT_FOUND_MSG, funcname, assoc_tbl[j].dependent); return; } /* * Reference property name must be * _classname_propertyname */ err = ptree_get_propval_by_name(dobjp->node, "_class", class, sizeof (class)); if (err != 0) { init_err(CLASS_NOT_FOUND_MSG, funcname, assoc_tbl[j].dependent); return; } sprintf(name, "_%s_%s", class, "subclass"); propinfo.version = PSVC_PLUGIN_VERSION; propinfo.read = NULL; propinfo.write = NULL; propinfo.piclinfo.type = PICL_PTYPE_REFERENCE; propinfo.piclinfo.accessmode = PICL_READ; propinfo.piclinfo.size = sizeof (picl_nodehdl_t); strcpy(propinfo.piclinfo.name, name); err = ptree_create_prop(&propinfo, &dobjp->node, dep_list + offset); if (err != 0) { init_err(PTREE_CREATE_PROP_FAILED_MSG, name, picl_strerror(err)); return; } ++offset; } /* add row to table */ err = ptree_add_row_to_table(tbl_hdl, dependents, dep_list); if (err != 0) { init_err(PTREE_ADD_ROW_FAILED_MSG, funcname, picl_strerror(err)); return; } } } /* Load projected properties */ static void load_projected_properties(FILE *fp) { int32_t found; ptree_propinfo_t propinfo; ptree_propinfo_t dstinfo; picl_prophdl_t src_prophdl, dst_prophdl; picl_nodehdl_t src_node, dst_node; int err, i; picl_psvc_t *srcobjp, *dstobjp; char src[32], dst[256]; char src_prop[32], dst_prop[32]; char buf[BUFSZ]; char *funcname = "load_projected_properties"; if (find_file_section(fp, "PROJECTED_PROPERTIES") != 0) return; if (count_records(fp, "PROJECTED_PROPERTIES_END", &proj_prop_count) != 0) { init_err(INVALID_FILE_FORMAT_MSG, funcname, 0); return; } prop_list = (struct proj_prop *)malloc(sizeof (struct proj_prop) * proj_prop_count); if (prop_list == NULL) { init_err(MALLOC_FAILED_MSG, funcname, strerror(errno)); return; } for (i = 0; i < proj_prop_count; ++i) { fgets(buf, BUFSZ, fp); found = sscanf(buf, "%s %s %s %s", src, src_prop, dst, dst_prop); if (found != 4) { init_err(INVALID_FILE_FORMAT_MSG, funcname, 0); return; } /* find src node */ if (src[0] == '/') { /* picl node name, outside psvc subtree */ err = ptree_get_node_by_path(src, &src_node); if (err != 0) { init_err(NODE_NOT_FOUND_MSG, funcname, src); return; } } else { srcobjp = (picl_psvc_t *)bsearch(src, psvc_hdl.objects, psvc_hdl.obj_count, sizeof (picl_psvc_t), (int (*)(const void *, const void *)) name_compare_bsearch); if (srcobjp == NULL) { init_err(ID_NOT_FOUND_MSG, funcname, src); return; } src_node = srcobjp->node; } /* find dest node */ if (dst[0] == '/') { /* picl node name, outside psvc subtree */ err = ptree_get_node_by_path(dst, &dst_node); if (err != 0) { init_err(NODE_NOT_FOUND_MSG, funcname, dst); return; } prop_list[i].dst_node = dst_node; } else { dstobjp = (picl_psvc_t *)bsearch(dst, psvc_hdl.objects, psvc_hdl.obj_count, sizeof (picl_psvc_t), (int (*)(const void *, const void *)) name_compare_bsearch); if (dstobjp == NULL) { init_err(ID_NOT_FOUND_MSG, funcname, dst); return; } dst_node = dstobjp->node; prop_list[i].dst_node = dst_node; } /* determine destination property size */ err = ptree_get_first_prop(dst_node, &dst_prophdl); while (err == 0) { err = ptree_get_propinfo(dst_prophdl, &dstinfo); if (err != 0) break; if (strcmp(dst_prop, dstinfo.piclinfo.name) == 0) break; err = ptree_get_next_prop(dst_prophdl, &dst_prophdl); } if (err != 0) { init_err(SIZE_NOT_FOUND_MSG, funcname, dst_prop); return; } propinfo.version = PSVC_PLUGIN_VERSION; propinfo.read = projected_read; propinfo.write = projected_write; propinfo.piclinfo.type = dstinfo.piclinfo.type; propinfo.piclinfo.accessmode = PICL_READ | PICL_WRITE | PICL_VOLATILE; propinfo.piclinfo.size = dstinfo.piclinfo.size; strcpy(propinfo.piclinfo.name, src_prop); err = ptree_create_prop(&propinfo, 0, &src_prophdl); if (err != 0) { init_err(PTREE_CREATE_PROP_FAILED_MSG, funcname, picl_strerror(err)); return; } err = ptree_add_prop(src_node, src_prophdl); if (err != 0) { init_err(PTREE_ADD_PROP_FAILED_MSG, funcname, picl_strerror(err)); return; } prop_list[i].handle = src_prophdl; strcpy(prop_list[i].name, dst_prop); } } /* Load the association table */ static void load_associations(FILE *fp) { char *funcname = "load_associations"; uint32_t count; int found; int j; char assoc_name[32]; struct assoc_pair *assoc_tbl; char name1[32]; char buf[BUFSZ]; /* * ignore count in the file, correct count is highest * association id + 1, now figured when loading ASSOC_STR * section. */ if (find_file_section(fp, "ASSOCIATIONS") != 0) return; fgets(buf, BUFSZ, fp); while (strncmp("ASSOCIATIONS_END", buf, 16) != 0) { found = sscanf(buf, "%s %s", name1, assoc_name); if (found != 2) { init_err(INVALID_FILE_FORMAT_MSG, funcname, 0); return; } if (count_records(fp, "ASSOCIATION_END", &count) != 0) { init_err(INVALID_FILE_FORMAT_MSG, funcname, 0); return; } assoc_tbl = (struct assoc_pair *)malloc( sizeof (struct assoc_pair) * count); if (assoc_tbl == NULL) { init_err(MALLOC_FAILED_MSG, funcname, strerror(errno)); return; } for (j = 0; j < count; ++j) { fgets(buf, BUFSZ, fp); found = sscanf(buf, "%s %s", assoc_tbl[j].antecedent, assoc_tbl[j].dependent); if (found != 2) { init_err(INVALID_FILE_FORMAT_MSG, funcname, 0); return; } } fgets(buf, BUFSZ, fp); if (strncmp(buf, "ASSOCIATION_END", 15) != 0) { init_err(INVALID_FILE_FORMAT_MSG, funcname, 0); return; } /* Create separate list of dependents for each antecedent */ if (strcmp(assoc_name, "PSVC_TABLE") != 0) { create_reference_properties(assoc_tbl, count, assoc_name); } free(assoc_tbl); fgets(buf, BUFSZ, fp); } } /* Enviornmental Lock Object's Read and Write routine */ static int env_lock_read(ptree_rarg_t *rarg, void *buf) { strlcpy((char *)buf, env_lock_state, LOCK_STRING_MAX); return (PSVC_SUCCESS); } static int env_lock_write(ptree_warg_t *warg, const void *buf) { int32_t status = PSVC_SUCCESS; char *var = (char *)buf; /* * Check to make sure that the value is either Disabled or Enabled * as these are the only 2 states that this object can be set to. */ if ((strcmp(var, PSVC_LOCK_DISABLED) != 0) && (strcmp(var, PSVC_LOCK_ENABLED) != 0)) { errno = EINVAL; return (PSVC_FAILURE); } pthread_mutex_lock(&env_lock_mutex); /* * If the state is already Enabled we can set the state to Disabled * to stop the policies. */ if (strcmp(env_lock_state, PSVC_LOCK_ENABLED) == 0) { pthread_mutex_unlock(&env_lock_mutex); status = timed_lock_wait(PSVC_LOCK_DISABLED); if (status == -1) { syslog(LOG_ERR, SEM_WAIT_FAILED_MSG); } return (status); } /* * If the state is Running we must go into timed_lock_wait to aquire * the env_lock. */ if (strcmp(env_lock_state, PSVC_LOCK_RUNNING) == 0) { pthread_mutex_unlock(&env_lock_mutex); status = timed_lock_wait(PSVC_LOCK_DISABLED); if (status == -1) { syslog(LOG_ERR, SEM_WAIT_FAILED_MSG); } return (status); } /* * If the state is already Disabled we need to first check to see if * we are resetting it to Disabled or changing it to Enabled. If we * are resetting it to Disabled then we need to stop the timer and * restart it. If we are changing it to Enabled we just set it to * enabled. */ if (strcmp(env_lock_state, PSVC_LOCK_DISABLED) == 0) { if (strcmp(var, PSVC_LOCK_DISABLED) == 0) { pthread_mutex_lock(&timer_mutex); if (timer_state == ACTIVE) { timer_state = NOT_READY; /* stop timer */ pthread_cond_signal(&timer_cond); pthread_mutex_unlock(&timer_mutex); /* wait for timer to reset */ while (timer_state != READY) sched_yield(); pthread_mutex_lock(&timer_mutex); timer_state = HAVE_REQUEST; /* restart timer */ pthread_cond_signal(&timer_cond); } pthread_mutex_unlock(&timer_mutex); } else { strlcpy(env_lock_state, var, LOCK_STRING_MAX); } } pthread_mutex_unlock(&env_lock_mutex); return (PSVC_SUCCESS); } static int init_env_lock_node(picl_nodehdl_t root_node) { int err; ptree_propinfo_t propinfo; char *funcname = "init_env_lock_node"; /* Here we are creating a Enviornmental Lock Node */ err = ptree_create_node("/plugins/environmental", "picl", &lock_node); if (err != PICL_SUCCESS) { init_err(PTREE_CREATE_NODE_FAILED_MSG, funcname, picl_strerror(err)); return (err); } err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION_1, PICL_PTYPE_CHARSTRING, PICL_READ | PICL_WRITE | PICL_VOLATILE, 32, "State", env_lock_read, env_lock_write); if (err != PICL_SUCCESS) { init_err(NODE_PROP_FAILED_MSG, funcname, picl_strerror(err)); return (err); } err = ptree_create_and_add_prop(lock_node, &propinfo, NULL, NULL); if (err != PICL_SUCCESS) { init_err(PTREE_ADD_PROP_FAILED_MSG, funcname, picl_strerror(err)); return (err); } err = ptree_add_node(root_node, lock_node); if (err != PICL_SUCCESS) { init_err(PTREE_ADD_NODE_FAILED_MSG, funcname, picl_strerror(err)); return (err); } return (PSVC_SUCCESS); } void psvc_plugin_init(void) { struct classinfo *cp; picl_nodehdl_t root_node; picl_nodehdl_t parent_node; char *funcname = "psvc_plugin_init"; char platform[32]; char filename[256]; char buf[BUFSZ]; int32_t i, j; int err, found; psvc_paths = NULL; psvc_hdl.obj_count = 0; psvc_hdl.objects = NULL; psvc_hdl.fp = NULL; first_interval = NULL; /* * So the volatile read/write routines can retrieve data from * psvc or picl */ err = psvc_init(&hdlp); if (err != 0) { init_err(PSVC_INIT_ERR_MSG, funcname, strerror(errno)); } if (sysinfo(SI_PLATFORM, platform, sizeof (platform)) == -1) { init_err(SYSINFO_FAILED_MSG, funcname, 0); return; } sprintf(filename, "/usr/platform/%s/lib/psvcobj.conf", platform); if ((psvc_hdl.fp = fopen(filename, "r")) == NULL) { init_err(FILE_OPEN_FAILED_MSG, funcname, filename); return; } /* Create all PICL nodes */ if (find_file_section(psvc_hdl.fp, "OBJECT_INFO") == -1) { init_err(INVALID_FILE_FORMAT1_MSG, funcname, filename); return; } if (count_records(psvc_hdl.fp, "OBJECT_INFO_END", &psvc_hdl.obj_count) == -1) { init_err(INVALID_FILE_FORMAT1_MSG, funcname, filename); return; } if ((psvc_hdl.objects = (picl_psvc_t *)malloc(sizeof (picl_psvc_t) * psvc_hdl.obj_count)) == NULL) { init_err(MALLOC_FAILED_MSG, funcname, strerror(errno)); return; } memset(psvc_hdl.objects, 0, sizeof (picl_psvc_t) * psvc_hdl.obj_count); err = ptree_get_root(&root_node); if (err != 0) { init_err(PTREE_GET_ROOT_FAILED_MSG, funcname, picl_strerror(err)); return; } /* Following array is accessed directly by the psvc policies. */ psvc_paths = (psvc_name_t *)malloc(sizeof (psvc_name_t) * psvc_hdl.obj_count); psvc_picl_nodes = psvc_hdl.obj_count; if (psvc_paths == NULL) { init_err(MALLOC_FAILED_MSG, funcname, strerror(errno)); return; } for (i = 0; i < psvc_hdl.obj_count; ++i) { char *start; int32_t class; int32_t subclass; int32_t cp_count; picl_psvc_t *objp = &psvc_hdl.objects[i]; fgets(buf, BUFSZ, psvc_hdl.fp); if (strncmp(buf, "OBJECT_INFO_END", 15) == 0) break; start = strrchr(buf, '/'); if (start == NULL) { init_err(INVALID_FILE_FORMAT1_MSG, funcname, filename); return; } found = sscanf(start + 1, "%s", objp->name); if (found != 1) { init_err(INVALID_FILE_FORMAT1_MSG, funcname, filename); return; } /* get class */ err = psvc_get_attr(hdlp, objp->name, PSVC_CLASS_ATTR, &class); if (err != PSVC_SUCCESS) { init_err(CLASS_NOT_FOUND_MSG, funcname, objp->name); return; } if (class > NUM_CLASSES) { init_err(UNKNOWN_CLASS_MSG, funcname, 0); return; } err = psvc_get_attr(hdlp, objp->name, PSVC_SUBCLASS_ATTR, &subclass); if (err != PSVC_SUCCESS) { init_err(SUBCLASS_NOT_FOUND_MSG, funcname, objp->name); return; } err = ptree_create_node(objp->name, class_name[class], &objp->node); if (err != 0) { init_err(PTREE_CREATE_NODE_FAILED_MSG, funcname, picl_strerror(err)); return; } if (strcmp(objp->name, PSVC_CHASSIS) == 0) system_node = objp->node; for (j = 0; j < COMMON_COUNT; ++j) { err = node_property(objp->node, common[j].access & PICL_READ ? psvc_read_volatile : 0, common[j].access & PICL_WRITE ? psvc_write_volatile : 0, common[j].type, common[j].size, common[j].access, common[j].name, 0); if (err != PSVC_SUCCESS) { init_err(NODE_PROP_FAILED_MSG, funcname, picl_strerror(err)); return; } } cp = &class_properties[class]; /* Locator LED Support */ if (class == 2 && subclass == 2) { cp_count = 3; } else { cp_count = cp->count; } for (j = 0; j < cp_count; ++j) { err = node_property(objp->node, psvc_read_volatile, psvc_write_volatile, cp->props[j].type, cp->props[j].size, cp->props[j].access, cp->props[j].name, 0); if (err != PSVC_SUCCESS) { init_err(NODE_PROP_FAILED_MSG, funcname, picl_strerror(err)); return; } } /* Link the nodes into the PICL tree */ *start = 0; if (start == buf) { /* no parent */ parent_node = root_node; } else { err = ptree_get_node_by_path(buf, &parent_node); if (err != PICL_SUCCESS) { init_err(NODE_NOT_FOUND_MSG, funcname, buf); return; } } err = ptree_add_node(parent_node, objp->node); if (err != PICL_SUCCESS) { init_err(PTREE_ADD_NODE_FAILED_MSG, funcname, picl_strerror(err)); return; } strcpy(psvc_paths[i].parent_path, buf); strcpy(psvc_paths[i].child_name, objp->name); psvc_paths[i].child_node = objp->node; } qsort(psvc_hdl.objects, psvc_hdl.obj_count, sizeof (picl_psvc_t), (int (*)(const void *, const void *))name_compare_qsort); load_associations(psvc_hdl.fp); load_projected_properties(psvc_hdl.fp); if (init_env_lock_node(root_node) != PSVC_SUCCESS) return; init_daemon(); init_done = 1; } void psvc_plugin_fini(void) { int32_t i; EInterval_t *ip, *next; /* Fix memory leaks */ free(prop_list); free(psvc_paths); for (ip = first_interval; ip != 0; ip = next) { /* * We don't check return types. * If this call fails, it's because we did not * run thread_setup() on this particular ip * * Please refer to init_daemon() for thread * assignments */ pthread_cancel(ip->thread); for (i = 0; i < ip->num_tasks; ++i) free(ip->task_list[i].obj_list); free(ip->task_list); next = ip->next; free(ip); } psvc_fini(hdlp); } void psvc_plugin_register(void) { picld_plugin_register(&psvc_reg); }