#include <picl.h>
#include <picltree.h>
#include <picldefs.h>
#include <stdio.h>
#include <umem.h>
#include <unistd.h>
#include <libnvpair.h>
#include <strings.h>
#include <syslog.h>
#include <dlfcn.h>
#include <link.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/param.h>
#include <sys/raidioctl.h>
#include <sys/utsname.h>
#include <sys/systeminfo.h>
#include <libpcp.h>
#include "piclsbl.h"
#include "errno.h"
#pragma init(piclsbl_register)
static void *pcp_handle;
static char hba_devctl[MAXPATHLEN];
static int (* pcp_init_ptr)();
static int (* pcp_send_recv_ptr)();
static int (* pcp_close_ptr)();
static int load_pcp_libs(void);
static void piclsbl_init(void);
static void piclsbl_fini(void);
static void piclsbl_register(void);
static void piclsbl_handler(const char *ename, const void *earg,
size_t size, void *cookie);
static picld_plugin_reg_t piclsbl_reg = {
PICLD_PLUGIN_VERSION_1,
PICLD_PLUGIN_CRITICAL,
"piclsbl",
piclsbl_init,
piclsbl_fini
};
static int
load_pcp_libs()
{
char pcp_dl_lib[80];
(void) snprintf(pcp_dl_lib, sizeof (pcp_dl_lib), "%s%s",
LIB_PCP_PATH, PCPLIB);
if ((pcp_handle = dlopen(pcp_dl_lib, RTLD_NOW)) == (void *) NULL)
return (1);
pcp_init_ptr = (int(*)())dlsym(pcp_handle, "pcp_init");
pcp_close_ptr = (int(*)())dlsym(pcp_handle, "pcp_close");
pcp_send_recv_ptr = (int(*)())dlsym(pcp_handle, "pcp_send_recv");
if (pcp_init_ptr == NULL || pcp_send_recv_ptr == NULL ||
pcp_close_ptr == NULL)
return (1);
return (0);
}
static int
cb_find_disk(picl_nodehdl_t node, void *args)
{
disk_lookup_t *lookup = (disk_lookup_t *)args;
int status = -1;
char *n;
char path[PICL_PROPNAMELEN_MAX];
status = ptree_get_propval_by_name(node, "Path", (void *)&path,
PICL_PROPNAMELEN_MAX);
if (status != PICL_SUCCESS) {
return (PICL_WALK_CONTINUE);
}
if (strcmp(path, lookup->path) == 0) {
lookup->disk = node;
lookup->result = DISK_FOUND;
n = strstr(path, "/sd");
strncpy(n, "\0", 1);
(void) snprintf(hba_devctl, MAXPATHLEN, "/devices%s:devctl",
path);
return (PICL_WALK_TERMINATE);
}
return (PICL_WALK_CONTINUE);
}
static int
check_raid(int target)
{
raid_config_t config;
int fd;
int numvols;
int i;
int j;
if ((fd = open(hba_devctl, O_RDONLY)) < 0) {
syslog(LOG_ERR, "%s", strerror(errno));
return (0);
}
if (ioctl(fd, RAID_NUMVOLUMES, &numvols)) {
syslog(LOG_ERR, "%s", strerror(errno));
(void) close(fd);
return (0);
}
for (i = 0; i < numvols; i++) {
config.unitid = i;
if (ioctl(fd, RAID_GETCONFIG, &config)) {
syslog(LOG_ERR, "%s", strerror(errno));
(void) close(fd);
return (0);
}
for (j = 0; j < config.ndisks; j++) {
if (config.disk[j] == target) {
(void) close(fd);
return (1);
}
}
}
(void) close(fd);
return (0);
}
static void
piclsbl_handler(const char *ename, const void *earg, size_t size,
void *cookie)
{
char *devfs_path;
char hdd_location[PICL_PROPNAMELEN_MAX];
nvlist_t *nvlp = NULL;
pcp_msg_t send_msg;
pcp_msg_t recv_msg;
pcp_sbl_req_t *req_ptr = NULL;
pcp_sbl_resp_t *resp_ptr = NULL;
int status = -1;
int target;
disk_lookup_t lookup;
int channel_fd;
if ((req_ptr = (pcp_sbl_req_t *)umem_zalloc(sizeof (pcp_sbl_req_t),
UMEM_DEFAULT)) == NULL)
goto sbl_return;
if (strcmp(ename, PICLEVENT_SYSEVENT_DEVICE_ADDED) == 0)
req_ptr->sbl_action = PCP_SBL_DISABLE;
else if (strcmp(ename, PICLEVENT_SYSEVENT_DEVICE_REMOVED) == 0)
req_ptr->sbl_action = PCP_SBL_ENABLE;
else
goto sbl_return;
if (nvlist_unpack((char *)earg, size, &nvlp, 0))
goto sbl_return;
if (nvlist_lookup_string(nvlp, "devfs-path", &devfs_path))
goto sbl_return;
lookup.path = strdup(devfs_path);
lookup.disk = 0;
lookup.result = DISK_NOT_FOUND;
status = ptree_walk_tree_by_class(root_node, "disk", (void *)&lookup,
cb_find_disk);
if (status != PICL_SUCCESS)
goto sbl_return;
if (lookup.result == DISK_FOUND) {
status = ptree_get_propval_by_name(lookup.disk, "Location",
(void *)&hdd_location, PICL_PROPNAMELEN_MAX);
if (status != PICL_SUCCESS) {
syslog(LOG_ERR, "piclsbl: failed hdd discovery");
goto sbl_return;
}
}
if (strncmp(hdd_location, NAC_DISK_PREFIX,
strlen(NAC_DISK_PREFIX)) == 0) {
(void) sscanf(hdd_location, "%*3s%d", &req_ptr->sbl_id);
target = (int)req_ptr->sbl_id;
} else {
goto sbl_return;
}
if (check_raid(target))
goto sbl_return;
if ((channel_fd = (*pcp_init_ptr)(LED_CHANNEL)) < 0) {
int s = PCPINIT_TIMEOUT;
int retries = 0;
while (++retries) {
(void) sleep(s);
if ((channel_fd = (*pcp_init_ptr)(LED_CHANNEL)) >= 0)
break;
else if (retries == 3) {
syslog(LOG_ERR, "piclsbl: ",
"SC channel initialization failed");
goto sbl_return;
}
}
}
send_msg.msg_type = PCP_SBL_CONTROL;
send_msg.sub_type = 0;
send_msg.msg_len = sizeof (pcp_sbl_req_t);
send_msg.msg_data = (uint8_t *)req_ptr;
if ((*pcp_send_recv_ptr)(channel_fd, &send_msg, &recv_msg,
PCPCOMM_TIMEOUT) < 0) {
int s = PCPCOMM_TIMEOUT;
(void) sleep(s);
if ((*pcp_send_recv_ptr)(channel_fd, &send_msg, &recv_msg,
PCPCOMM_TIMEOUT) < 0) {
syslog(LOG_ERR, "piclsbl: communication failure");
goto sbl_return;
}
}
if (recv_msg.msg_type != PCP_SBL_CONTROL_R) {
syslog(LOG_ERR, "piclsbl: unbound packet received");
goto sbl_return;
}
resp_ptr = (pcp_sbl_resp_t *)recv_msg.msg_data;
if (resp_ptr->status == PCP_SBL_ERROR) {
syslog(LOG_ERR, "piclsbl: OK2RM LED action error");
goto sbl_return;
}
if ((req_ptr->sbl_action == PCP_SBL_DISABLE) &&
(resp_ptr->sbl_state != SBL_STATE_OFF))
syslog(LOG_ERR, "piclsbl: OK2RM LED not OFF after disk "
"configuration");
else if ((req_ptr->sbl_action == PCP_SBL_ENABLE) &&
(resp_ptr->sbl_state != SBL_STATE_ON))
syslog(LOG_ERR, "piclsbl: OK2RM LED not ON after disk "
"unconfiguration");
else if (resp_ptr->sbl_state == SBL_STATE_UNKNOWN)
syslog(LOG_ERR, "piclsbl: OK2RM LED set to unknown state");
sbl_return:
(*pcp_close_ptr)(channel_fd);
if (req_ptr != NULL)
umem_free(req_ptr, sizeof (pcp_sbl_req_t));
if (resp_ptr != NULL)
free(resp_ptr);
nvlist_free(nvlp);
}
static void
piclsbl_init(void)
{
char platbuf[SYS_NMLN];
if ((sysinfo(SI_PLATFORM, platbuf, SYS_NMLN) != -1) &&
((strcmp(platbuf, ERIE_PLATFORM) == 0) ||
(strcmp(platbuf, ERIE_PLATFORM2) == 0)))
return;
if ((ptree_get_root(&root_node)) != 0)
return;
if (load_pcp_libs()) {
syslog(LOG_ERR, "piclsbl: failed to load libpcp");
syslog(LOG_ERR, "piclsbl: aborting");
return;
}
(void) ptree_register_handler(PICLEVENT_SYSEVENT_DEVICE_ADDED,
piclsbl_handler, NULL);
(void) ptree_register_handler(PICLEVENT_SYSEVENT_DEVICE_REMOVED,
piclsbl_handler, NULL);
}
static void
piclsbl_fini(void)
{
(void) ptree_unregister_handler(PICLEVENT_SYSEVENT_DEVICE_ADDED,
piclsbl_handler, NULL);
(void) ptree_unregister_handler(PICLEVENT_SYSEVENT_DEVICE_REMOVED,
piclsbl_handler, NULL);
}
static void
piclsbl_register(void)
{
picld_plugin_register(&piclsbl_reg);
}