#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <syslog.h>
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#include <sys/open.h>
#include <ctype.h>
#include <string.h>
#include <alloca.h>
#include <libintl.h>
#include <sys/systeminfo.h>
#include <picl.h>
#include <picltree.h>
#include <picld_pluginutil.h>
#include <pthread.h>
#include <sys/utsname.h>
#include <sys/systeminfo.h>
#include "picldefs.h"
#include "envd.h"
typedef int ptree_vol_rdfunc_t(ptree_rarg_t *parg, void *buf);
typedef int ptree_vol_wrfunc_t(ptree_warg_t *parg, const void *buf);
extern int monitor_disk_temp;
extern sensor_ctrl_blk_t sensor_ctrl[];
extern fan_ctrl_blk_t fan_ctrl[];
extern env_tuneable_t tuneables[];
extern int errno;
extern int ntuneables;
#define PROP_FAN_SPEED_UNIT_VALUE "rpm"
typedef struct {
char *parent_path;
char *sensor_name;
env_sensor_t *sensorp;
picl_nodehdl_t nodeh;
picl_prophdl_t proph;
picl_prophdl_t target_proph;
} sensor_node_t;
static sensor_node_t sensor_nodes[] = {
{"/platform/pci@1e,600000/isa@7/i2c@0,320/hardware-monitor@0,58",
SENSOR_CPU0_DIE, NULL, 0, 0, 0},
{"/platform/pci@1e,600000/isa@7/i2c@0,320/hardware-monitor@0,58",
SENSOR_CPU1_DIE, NULL, 0, 0, 0},
{"/platform/pci@1e,600000/isa@7/i2c@0,320/hardware-monitor@0,58",
SENSOR_INT_AMB_0, NULL, 0, 0, 0},
{"/platform/pci@1e,600000/isa@7/i2c@0,320/hardware-monitor@0,5c",
SENSOR_SYS_OUT, NULL, 0, 0, 0},
{"/platform/pci@1e,600000/isa@7/i2c@0,320/hardware-monitor@0,5c",
SENSOR_SYS_IN, NULL, 0, 0, 0},
{"/platform/pci@1e,600000/isa@7/i2c@0,320/hardware-monitor@0,5c",
SENSOR_INT_AMB_1, NULL, 0, 0, 0},
};
#define N_SENSOR_NODES (sizeof (sensor_nodes)/sizeof (sensor_nodes[0]))
typedef struct {
char *parent_path;
char *fan_name;
env_fan_t *fanp;
char *speed_unit;
picl_nodehdl_t nodeh;
picl_prophdl_t proph;
} fan_node_t;
static fan_node_t fan_nodes[] = {
{"/platform/pci@1e,600000/isa@7/i2c@0,320/hardware-monitor@0,58",
ENV_CPU0_FAN, NULL, PROP_FAN_SPEED_UNIT_VALUE, 0, 0},
{"/platform/pci@1e,600000/isa@7/i2c@0,320/hardware-monitor@0,58",
ENV_CPU1_FAN, NULL, PROP_FAN_SPEED_UNIT_VALUE, 0, 0},
{"/platform/pci@1e,600000/isa@7/i2c@0,320/hardware-monitor@0,5c",
ENV_SYSTEM_OUT_FAN, NULL, PROP_FAN_SPEED_UNIT_VALUE, 0, 0},
{"/platform/pci@1e,600000/isa@7/i2c@0,320/hardware-monitor@0,5c",
ENV_SYSTEM_INTAKE_FAN, NULL, PROP_FAN_SPEED_UNIT_VALUE, 0, 0},
{"/platform/pci@1e,600000/isa@7/i2c@0,320/hardware-monitor@0,52",
ENV_DIMM_FAN, NULL, PROP_FAN_SPEED_UNIT_VALUE, 0, 0},
};
#define N_FAN_NODES (sizeof (fan_nodes)/sizeof (fan_nodes[0]))
typedef struct {
char *parent_path;
char *disk_name;
env_disk_t *diskp;
picl_nodehdl_t nodeh;
picl_prophdl_t proph;
} disk_node_t;
static disk_node_t disk_nodes[] = {
{DISK0_NODE_PATH, ENV_DISK0, NULL, 0, 0},
{DISK1_NODE_PATH, ENV_DISK1, NULL, 0, 0},
};
#define N_DISK_NODES (sizeof (disk_nodes)/sizeof (disk_nodes[0]))
static void delete_sensor_nodes_and_props(void);
static void delete_disk_nodes_and_props(void);
static void delete_fan_nodes_and_props(void);
static int
get_current_temp(ptree_rarg_t *parg, void *buf)
{
tempr_t temp;
picl_prophdl_t proph;
sensor_node_t *snodep;
int i;
proph = parg->proph;
for (i = 0; i < N_SENSOR_NODES; ++i) {
snodep = &sensor_nodes[i];
if (snodep->proph != proph)
continue;
if (get_temperature(snodep->sensorp, &temp) < 0)
break;
(void) memcpy(buf, (caddr_t)&temp, sizeof (tempr_t));
return (PICL_SUCCESS);
}
return (PICL_FAILURE);
}
static int
get_disk_temp(ptree_rarg_t *parg, void *buf)
{
tempr_t temp;
picl_prophdl_t proph;
disk_node_t *dnodep;
int i;
proph = parg->proph;
for (i = 0; i < N_DISK_NODES; ++i) {
dnodep = &disk_nodes[i];
if (dnodep->proph != proph)
continue;
if (disk_temperature(dnodep->diskp, &temp) < 0)
break;
(void) memcpy(buf, (caddr_t)&temp, sizeof (tempr_t));
return (PICL_SUCCESS);
}
return (PICL_FAILURE);
}
static int
set_current_speed(ptree_warg_t *parg, const void *buf)
{
fanspeed_t speed;
picl_prophdl_t proph;
fan_node_t *fnodep;
int i, ret;
proph = parg->proph;
for (i = 0; i < N_FAN_NODES; ++i) {
fnodep = &fan_nodes[i];
if (fnodep->proph != proph)
continue;
if (fnodep->fanp->fd == -1)
continue;
(void) memcpy((caddr_t)&speed, buf, sizeof (speed));
ret = set_fan_speed(fnodep->fanp, speed);
if (ret < 0) {
if (ret == -1 && errno == EBUSY)
return (PICL_NOTWRITABLE);
if (ret == -2)
return (PICL_INVALIDARG);
break;
}
return (PICL_SUCCESS);
}
return (PICL_FAILURE);
}
static int
get_current_speed(ptree_rarg_t *parg, void *buf)
{
fanspeed_t speed;
picl_prophdl_t proph;
fan_node_t *fnodep;
int i;
proph = parg->proph;
for (i = 0; i < N_FAN_NODES; ++i) {
fnodep = &fan_nodes[i];
if (fnodep->proph != proph)
continue;
if (fnodep->fanp->fd == -1)
continue;
if (get_fan_speed(fnodep->fanp, &speed) < 0)
break;
(void) memcpy(buf, (caddr_t)&speed, sizeof (speed));
return (PICL_SUCCESS);
}
return (PICL_FAILURE);
}
static int
add_regular_prop(picl_nodehdl_t nodeh, char *name, int type, int access,
int size, void *valbuf, picl_prophdl_t *prophp)
{
int err;
ptree_propinfo_t propinfo;
picl_prophdl_t proph;
err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
type, access, size, name, NULL, NULL);
if (err != PICL_SUCCESS)
return (err);
err = ptree_create_and_add_prop(nodeh, &propinfo, valbuf, &proph);
if (err == PICL_SUCCESS && prophp)
*prophp = proph;
return (err);
}
static int
add_volatile_prop(picl_nodehdl_t nodeh, char *name, int type, int access,
int size, ptree_vol_rdfunc_t *rdfunc, ptree_vol_wrfunc_t *wrfunc,
picl_prophdl_t *prophp)
{
int err;
ptree_propinfo_t propinfo;
picl_prophdl_t proph;
err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
type, (access|PICL_VOLATILE), size, name, rdfunc, wrfunc);
if (err != PICL_SUCCESS)
return (err);
err = ptree_create_and_add_prop(nodeh, &propinfo, NULL, &proph);
if (err == PICL_SUCCESS && prophp)
*prophp = proph;
return (err);
}
static void
add_sensor_thresh_props(picl_nodehdl_t nodeh, sensor_ctrl_blk_t *threshp)
{
picl_prophdl_t proph;
(void) add_regular_prop(nodeh, PICL_PROP_LOW_POWER_OFF,
PICL_PTYPE_INT, PICL_READ,
sizeof (threshp->low_power_off),
(void *)&(threshp->low_power_off), &proph);
(void) add_regular_prop(nodeh, PICL_PROP_LOW_SHUTDOWN,
PICL_PTYPE_INT, PICL_READ,
sizeof (threshp->low_shutdown),
(void *)&(threshp->low_shutdown), &proph);
(void) add_regular_prop(nodeh, PICL_PROP_LOW_WARNING,
PICL_PTYPE_INT, PICL_READ,
sizeof (threshp->low_warning),
(void *)&(threshp->low_warning), &proph);
(void) add_regular_prop(nodeh, PICL_PROP_HIGH_WARNING,
PICL_PTYPE_INT, PICL_READ,
sizeof (threshp->high_warning),
(void *)&(threshp->high_warning), &proph);
(void) add_regular_prop(nodeh, PICL_PROP_HIGH_SHUTDOWN,
PICL_PTYPE_INT, PICL_READ,
sizeof (threshp->high_shutdown),
(void *)&(threshp->high_shutdown), &proph);
(void) add_regular_prop(nodeh, PICL_PROP_HIGH_POWER_OFF,
PICL_PTYPE_INT, PICL_READ,
sizeof (threshp->high_power_off),
(void *)&(threshp->high_power_off), &proph);
}
static int
add_sensor_nodes_and_props()
{
int err;
char *pname, *nodename, *devfs_path;
sensor_node_t *snodep;
sensor_ctrl_blk_t *threshp;
picl_nodehdl_t nodeh, cnodeh;
picl_prophdl_t proph;
env_sensor_t *sensorp;
int i;
for (i = 0; i < N_SENSOR_NODES; ++i) {
snodep = &sensor_nodes[i];
err = ptree_get_node_by_path(snodep->parent_path, &nodeh);
if (err != PICL_SUCCESS)
continue;
sensorp = snodep->sensorp;
if (sensorp->present == B_FALSE)
continue;
nodename = snodep->sensor_name;
err = ptree_create_and_add_node(nodeh, nodename,
PICL_CLASS_TEMPERATURE_SENSOR, &cnodeh);
if (env_debug)
envd_log(LOG_INFO,
"Creating PICL sensor node '%s' err:%d\n",
nodename, err);
if (err != PICL_SUCCESS)
break;
snodep->nodeh = cnodeh;
devfs_path = sensorp->devfs_path;
pname = PICL_PROP_DEVFS_PATH;
err = add_regular_prop(cnodeh, pname,
PICL_PTYPE_CHARSTRING, PICL_READ,
strlen(devfs_path)+1, (void *)devfs_path, &proph);
if (err != PICL_SUCCESS)
break;
pname = PICL_PROP_TEMPERATURE;
err = add_volatile_prop(cnodeh, pname,
PICL_PTYPE_INT, PICL_READ, sizeof (tempr_t),
get_current_temp, NULL, &proph);
if (err != PICL_SUCCESS)
break;
snodep->proph = proph;
threshp = sensorp->es_ptr;
if (threshp != NULL)
add_sensor_thresh_props(cnodeh, threshp);
}
if (err != PICL_SUCCESS) {
delete_sensor_nodes_and_props();
if (env_debug)
envd_log(LOG_INFO,
"Can't create prop/node for sensor '%s'\n",
nodename);
return (err);
}
return (PICL_SUCCESS);
}
static void
delete_sensor_nodes_and_props(void)
{
sensor_node_t *snodep;
int i;
for (i = 0; i < N_SENSOR_NODES; ++i) {
snodep = &sensor_nodes[i];
if (snodep->nodeh != 0) {
(void) ptree_delete_node(snodep->nodeh);
(void) ptree_destroy_node(snodep->nodeh);
snodep->nodeh = 0;
snodep->proph = 0;
}
}
}
static int
add_disk_nodes_and_props()
{
int err;
char *pname, *nodename, *devfs_path;
disk_node_t *dnodep;
picl_nodehdl_t nodeh, cnodeh;
picl_prophdl_t proph;
env_disk_t *diskp;
int i;
for (i = 0; i < N_DISK_NODES; ++i) {
dnodep = &disk_nodes[i];
err = ptree_get_node_by_path(dnodep->parent_path, &nodeh);
if (err != PICL_SUCCESS) {
if (env_debug)
envd_log(LOG_ERR,
"failed to get node for path %s\n",
dnodep->parent_path);
err = PICL_SUCCESS;
continue;
}
diskp = dnodep->diskp;
if (diskp->present == B_FALSE)
continue;
nodename = dnodep->disk_name;
err = ptree_create_and_add_node(nodeh, nodename,
PICL_CLASS_TEMPERATURE_SENSOR, &cnodeh);
if (env_debug)
envd_log(LOG_ERR,
"Creating PICL disk node '%s' err:%d\n",
nodename, err);
if (err != PICL_SUCCESS)
break;
dnodep->nodeh = cnodeh;
devfs_path = diskp->devfs_path;
pname = PICL_PROP_DEVFS_PATH;
err = add_regular_prop(cnodeh, pname,
PICL_PTYPE_CHARSTRING, PICL_READ,
strlen(devfs_path)+1, (void *)devfs_path, &proph);
if (err != PICL_SUCCESS)
break;
pname = PICL_PROP_TEMPERATURE;
err = add_volatile_prop(cnodeh, pname,
PICL_PTYPE_INT, PICL_READ, sizeof (tempr_t),
get_disk_temp, NULL, &proph);
if (err != PICL_SUCCESS)
break;
dnodep->proph = proph;
(void) add_regular_prop(cnodeh, PICL_PROP_LOW_SHUTDOWN,
PICL_PTYPE_INT, PICL_READ,
sizeof (diskp->low_shutdown),
(void *)&(diskp->low_shutdown), &proph);
(void) add_regular_prop(cnodeh, PICL_PROP_LOW_WARNING,
PICL_PTYPE_INT, PICL_READ,
sizeof (diskp->low_warning),
(void *)&(diskp->low_warning), &proph);
(void) add_regular_prop(cnodeh, PICL_PROP_HIGH_WARNING,
PICL_PTYPE_INT, PICL_READ,
sizeof (diskp->high_warning),
(void *)&(diskp->high_warning), &proph);
(void) add_regular_prop(cnodeh, PICL_PROP_HIGH_SHUTDOWN,
PICL_PTYPE_INT, PICL_READ,
sizeof (diskp->high_shutdown),
(void *)&(diskp->high_shutdown), &proph);
}
if (err != PICL_SUCCESS) {
delete_disk_nodes_and_props();
if (env_debug)
envd_log(LOG_INFO,
"Can't create prop/node for disk '%s'\n",
nodename);
return (err);
}
return (PICL_SUCCESS);
}
static void
delete_disk_nodes_and_props(void)
{
disk_node_t *dnodep;
int i;
for (i = 0; i < N_DISK_NODES; ++i) {
dnodep = &disk_nodes[i];
if (dnodep->nodeh != 0) {
(void) ptree_delete_node(dnodep->nodeh);
(void) ptree_destroy_node(dnodep->nodeh);
dnodep->nodeh = 0;
dnodep->proph = 0;
}
}
}
static int
add_fan_nodes_and_props()
{
int err;
char *pname, *nodename, *devfs_path;
env_fan_t *fanp;
fan_node_t *fnodep;
picl_nodehdl_t nodeh, cnodeh;
picl_prophdl_t proph;
int i;
for (i = 0; i < N_FAN_NODES; ++i) {
fnodep = &fan_nodes[i];
if (fnodep->fanp->present == B_FALSE)
continue;
err = ptree_get_node_by_path(fnodep->parent_path, &nodeh);
if (err != PICL_SUCCESS) {
if (env_debug)
envd_log(LOG_ERR,
"node for %s NOT FOUND.\n", fnodep->parent_path);
err = PICL_SUCCESS;
continue;
}
nodename = fnodep->fan_name;
err = ptree_create_and_add_node(nodeh, nodename,
PICL_CLASS_FAN, &cnodeh);
if (env_debug)
envd_log(LOG_ERR,
"Creating PICL fan node '%s' err:%d\n",
nodename, err);
if (err != PICL_SUCCESS)
break;
fnodep->nodeh = cnodeh;
fanp = fnodep->fanp;
devfs_path = fanp->devfs_path;
pname = PICL_PROP_DEVFS_PATH;
err = add_regular_prop(cnodeh, pname,
PICL_PTYPE_CHARSTRING, PICL_READ,
strlen(devfs_path)+1, (void *)devfs_path, &proph);
if (err != PICL_SUCCESS)
break;
pname = PICL_PROP_FAN_SPEED;
if (fanp->id == DIMM_FAN_ID) {
err = add_volatile_prop(cnodeh, pname, PICL_PTYPE_INT,
PICL_READ, sizeof (fanspeed_t),
get_current_speed,
NULL, &proph);
} else {
err = add_volatile_prop(cnodeh, pname, PICL_PTYPE_INT,
PICL_READ|PICL_WRITE, sizeof (fanspeed_t),
get_current_speed,
set_current_speed, &proph);
}
if (err != PICL_SUCCESS)
break;
fnodep->proph = proph;
pname = PICL_PROP_FAN_SPEED_UNIT;
err = add_regular_prop(cnodeh, pname,
PICL_PTYPE_CHARSTRING, PICL_READ,
strlen(fnodep->speed_unit)+1,
(void *)fnodep->speed_unit, &proph);
if (err != PICL_SUCCESS)
break;
}
if (err != PICL_SUCCESS) {
delete_fan_nodes_and_props();
if (env_debug)
envd_log(LOG_WARNING,
"Can't create prop/node for fan '%s'\n",
nodename);
return (err);
}
return (PICL_SUCCESS);
}
static void
delete_fan_nodes_and_props(void)
{
fan_node_t *fnodep;
int i;
for (i = 0; i < N_FAN_NODES; ++i) {
fnodep = &fan_nodes[i];
if (fnodep->nodeh != 0) {
(void) ptree_delete_node(fnodep->nodeh);
(void) ptree_destroy_node(fnodep->nodeh);
fnodep->nodeh = 0;
}
}
}
static int
copy_persistent_tuneable(env_tuneable_t *tune, char *buf)
{
switch (tune->type) {
case PICL_PTYPE_INT : {
(void) memcpy((int *)tune->value,
buf, tune->nbytes);
break;
}
case PICL_PTYPE_CHARSTRING : {
(void) memcpy((caddr_t)tune->value,
buf, tune->nbytes);
break;
}
default : {
return (PICL_FAILURE);
}
}
return (PICL_SUCCESS);
}
static void
env_parse_tunables(picl_nodehdl_t rooth)
{
char nmbuf[SYS_NMLN];
char pname[PATH_MAX];
if (sysinfo(SI_PLATFORM, nmbuf, sizeof (nmbuf)) != -1) {
(void) snprintf(pname, PATH_MAX, PICLD_PLAT_PLUGIN_DIRF, nmbuf);
(void) strlcat(pname, TUNABLE_CONF_FILE, PATH_MAX);
if (access(pname, R_OK) == 0) {
(void) picld_pluginutil_parse_config_file(rooth, pname);
return;
}
}
}
int
env_picl_setup_tuneables(void)
{
int err;
int i;
picl_nodehdl_t nodeh;
picl_nodehdl_t rooth;
picl_prophdl_t proph;
env_tuneable_t *tuneablep;
char read_buf[BUFSIZ];
if (ptree_get_root(&rooth) != PICL_SUCCESS) {
return (PICL_FAILURE);
}
err = ptree_create_and_add_node(rooth, PICL_PLUGINS_NODE,
PICL_CLASS_PICL, &nodeh);
if (err != PICL_SUCCESS)
return (PICL_FAILURE);
err = ptree_create_and_add_node(nodeh, PICL_ENVIRONMENTAL_NODE,
PICL_CLASS_PICL, &nodeh);
if (err != PICL_SUCCESS) {
return (PICL_FAILURE);
}
env_parse_tunables(rooth);
for (i = 0; i < ntuneables; i++) {
tuneablep = &tuneables[i];
err = ptree_get_propval_by_name(nodeh, tuneablep->name,
read_buf, tuneablep->nbytes);
if (err != PICL_SUCCESS) {
err = add_volatile_prop(nodeh, tuneablep->name,
tuneablep->type,
PICL_READ|PICL_WRITE, tuneablep->nbytes,
tuneablep->rfunc,
tuneablep->wfunc, &proph);
tuneablep->proph = proph;
} else {
(void) copy_persistent_tuneable(tuneablep,
read_buf);
}
}
return (PICL_SUCCESS);
}
static int
get_envmodel_conf_file(char *outfilename)
{
char nmbuf[SYS_NMLN];
char pname[PATH_MAX];
if (sysinfo(SI_PLATFORM, nmbuf, sizeof (nmbuf)) != -1) {
(void) snprintf(pname, PATH_MAX, PICLD_PLAT_PLUGIN_DIRF, nmbuf);
(void) strlcat(pname, ENV_CONF_FILE, PATH_MAX);
if (access(pname, R_OK) == 0) {
(void) strlcpy(outfilename, pname, PATH_MAX);
return (0);
}
}
if (sysinfo(SI_MACHINE, nmbuf, sizeof (nmbuf)) != -1) {
(void) snprintf(pname, PATH_MAX, PICLD_PLAT_PLUGIN_DIRF, nmbuf);
(void) strlcat(pname, ENV_CONF_FILE, PATH_MAX);
if (access(pname, R_OK) == 0) {
(void) strlcpy(outfilename, pname, PATH_MAX);
return (0);
}
}
(void) snprintf(pname, PATH_MAX, "%s/%s", PICLD_COMMON_PLUGIN_DIR,
ENV_CONF_FILE);
if (access(pname, R_OK) == 0) {
(void) strlcpy(outfilename, pname, PATH_MAX);
return (0);
}
return (-1);
}
void
env_picl_destroy(void)
{
delete_fan_nodes_and_props();
delete_sensor_nodes_and_props();
delete_disk_nodes_and_props();
}
void
env_picl_setup(void)
{
int err;
sensor_node_t *snodep;
fan_node_t *fnodep;
disk_node_t *dnodep;
picl_nodehdl_t plath;
char fullfilename[PATH_MAX];
picl_nodehdl_t rooth;
int i;
for (i = 0; i < N_SENSOR_NODES; ++i) {
snodep = &sensor_nodes[i];
snodep->sensorp = sensor_lookup(snodep->sensor_name);
snodep->nodeh = 0;
snodep->proph = 0;
snodep->target_proph = 0;
}
for (i = 0; i < N_FAN_NODES; ++i) {
fnodep = &fan_nodes[i];
fnodep->fanp = fan_lookup(fnodep->fan_name);
fnodep->nodeh = 0;
fnodep->proph = 0;
}
for (i = 0; i < N_DISK_NODES; ++i) {
dnodep = &disk_nodes[i];
dnodep->diskp = disk_lookup(dnodep->disk_name);
dnodep->nodeh = 0;
dnodep->proph = 0;
}
err = ptree_get_node_by_path("/platform", &plath);
if (err == PICL_SUCCESS)
err = add_sensor_nodes_and_props();
if (err == PICL_SUCCESS)
err = add_fan_nodes_and_props();
if ((err == PICL_SUCCESS) && (monitor_disk_temp))
err = add_disk_nodes_and_props();
if (err != PICL_SUCCESS) {
delete_fan_nodes_and_props();
delete_disk_nodes_and_props();
delete_sensor_nodes_and_props();
envd_log(LOG_CRIT, ENVD_PICL_SETUP_FAILED);
return;
}
if (get_envmodel_conf_file(fullfilename) < 0)
envd_log(LOG_CRIT, ENVD_PICL_SETUP_FAILED);
if (ptree_get_root(&rooth) != PICL_SUCCESS)
envd_log(LOG_CRIT, ENVD_PICL_SETUP_FAILED);
err = picld_pluginutil_parse_config_file(rooth, fullfilename);
if (err != PICL_SUCCESS)
envd_log(LOG_CRIT, ENVD_PICL_SETUP_FAILED);
}