addon-cpufreq.c revision de7d23d85e06f547e8cd4ed4bce494209d63612a
/***************************************************************************
*
* addon-cpufreq.c : Routines to support CPUFreq interface
*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* Licensed under the Academic Free License version 2.1
*
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <strings.h>
#include <stdarg.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <glib.h>
#include <dbus/dbus-glib-lowlevel.h>
#include <dbus/dbus-glib.h>
#include <priv.h>
#include <pwd.h>
#include <syslog.h>
#include <libhal.h>
#include "../../utils/adt_data.h"
#include <pwd.h>
#ifdef HAVE_POLKIT
#include <libpolkit.h>
#endif
#ifdef sun
#include <bsm/adt_event.h>
#endif
#define POWER_CONF_FILE "/etc/power.conf"
#define FILE_ARR_SIZE 256
#define EDIT_TYPE_SIZE 64
#define ERR_BUF_SIZE 256
#define WAIT_TIME 30
const char *sender;
unsigned long uid;
/*
* Specify different CPUFreq related HAL activities that can be done
*/
enum hal_type {
};
typedef enum hal_type power_conf_hal_type;
/*
* Various CPUFreq related editable parameters in the power.conf file
*/
typedef struct {
char cpu_gov[EDIT_TYPE_SIZE];
int cpu_th;
/*
* CPUFreq interospect XML that exports the various CPUFreq HAL interface
* supported methods
*/
const char *cpufreq_introspect_xml = \
" <method name= \"SetCPUFreqGovernor\">\n \
<arg type= \"s\" name= \"governor\" direction= \"in\"/>\n \
</method>\n \
<method name= \"GetCPUFreqGovernor\">\n \
<type= \"s\" direction= \"out\"/>\n \
</method>\n \
<method name= \"SetCPUFreqPerformance\">\n \
<arg type=\"i\" direction=\"in\"/>\n \
</method>\n \
<method name= \"GetCPUFreqPerformance\">\n \
<type=\"i\" direction=\"out\"/>\n \
</method>\n \
<method name= \"GetCPUFreqAvailableGovernors\">\n \
<type=\"s\" direction=\"out\"/>\n \
</method>\n";
/*
* List of governors that are currently supported
*/
char *const gov_list[] = {
"ondemand",
"performance",
};
static char current_gov[EDIT_TYPE_SIZE];
/*
* Free up the mem allocated to hold the DBusError
*/
static void
{
if (dbus_error_is_set (error)) {
}
}
/*
* Edit the /etc/power.conf file to update the cpupm and cpupm_threshold values
* Return 0 on success
* 1 if the governor is not available or supported
* -1 all other errors
* NOTE: Before modifying power.conf, it is first copied into a temp file, and
* pmconfig is executed on the temp file with -f option, which uses temp file
* to set the PM config and then replaces power.conf with the temp file.
*/
static int
{
char tstr[FILE_ARR_SIZE];
char temp_str[FILE_ARR_SIZE];
long fset = 0;
long next_fset = 0;
char *file_edit_type;
char *file_edit_value;
char file_edit_threshold[FILE_ARR_SIZE];
char file_update_str[FILE_ARR_SIZE];
int res = 0;
char cp_cmd_str[128];
int tmp_fd;
/*
* Copy /etc/power.conf to temp file
*/
HAL_INFO ((" Invalid temp file name"));
return (EINVAL);
}
if (system (cp_cmd_str) != 0) {
HAL_ERROR ((" Error in copying %s to %s, %s",
return (errno);
}
HAL_INFO (("Cannot open file %s: %s",
return (errno);
}
switch (pc_hal_type) {
case CPU_GOV:
" Should be either ondemand or performance"));
goto out;
}
file_edit_type = "cpupm";
file_edit_value = " enable";
} else {
file_edit_value = "disable";
}
break;
case CPU_PERFORMANCE:
HAL_INFO ((" CPU Threshold is not valid."));
goto out;
}
file_edit_type = "cpu-threshold";
break;
default:
HAL_DEBUG ((" Cannot recognize the type of change being"
" made to /etc/power.conf"));
goto out;
}
continue;
/*
* Look for line containing "cpupm" or "cpu-threshold"
*/
continue;
}
/*
* If the required value already present. Just
* return
*/
res = 0;
goto out;
}
HAL_ERROR (("\n Error in fseek %s: %s",
goto out;
}
/*
* Update the file with new values
*/
/*
* Check if the currrent line is the last one. If not,
* to avoid overwriting and wasting space, move remaining
* lines upwards and update at the end
*/
HAL_ERROR (("\n Error in fseek %s: %s",
goto out;
}
do {
"%s\n", tstr);
}
HAL_ERROR (("\n Error in writing to"
" %s: %s", POWER_CONF_FILE,
goto out;
}
HAL_ERROR (("\n Error in flushing to"
" %s: %s", POWER_CONF_FILE,
}
res = 0;
goto out;
}
/*
* If the pointer comes here, then the property is not already present.
* Have to append to the file
*/
HAL_DEBUG (("\n Passed value not found. Will append to the file"));
HAL_ERROR (("\n Error in fseek to %s: %s",
goto out;
}
/*
* Update the file with new values
*/
HAL_ERROR (("Error in writing to file %s: %s",
goto out;
}
HAL_ERROR (("\n Error in flushing to %s: %s",
}
res = 0;
out:
return (res);
}
/*
* Depending on the type(cpupm or cpu-threshold) to read, check if they are
* present. If present, return the corresponding value through pc_value arg
* and return 1 from the function. If there is no corresponding entry,return 0.
* Return -1 on error
*/
static int
{
char tstr[FILE_ARR_SIZE];
long fset = 0;
char *file_edit_type;
char *tpstr;
int res = 0;
HAL_INFO (("\n Cannot open the file %s: %s",
return (-1);
}
switch (pc_hal_type) {
case CPU_GOV:
file_edit_type = "cpupm";
break;
case CPU_PERFORMANCE:
file_edit_type = "cpu-threshold";
break;
default :
HAL_DEBUG (("Cannot recognize the HAL type to get value"));
res = -1;
goto out;
}
continue;
/*
* Look for line containing "cpupm" or "cpu-threshold"
*/
continue;
/*
* If the required value already present. Just
* get the value
*/
HAL_INFO (("Value of %s in %s is not valid",
res = -1;
goto out;
}
if (pc_hal_type == CPU_GOV) {
/*
* Copy the corresponding governor
*/
"%s", "ondemand");
} else {
"%s", "performance");
}
} else {
}
res = 1;
goto out;
}
/*
* Entry not found in the file
*/
res = 0;
out:
return (res);
}
/*
* Depending on the type(Governor or Perfromance) to read, get the current
* values through PM ioctls().
* For "Governor", return the cpupm state and for "Performance" return the
* current cpu threshold.
* Return the corresponding value through cur_value and return 1 from the
* function for success. Return -1 on error
*/
static int
{
int pm_fd;
int res = -1;
int pm_ret;
if (pm_fd == -1) {
return (res);
}
switch (pc_hal_type) {
case CPU_GOV:
/*
* First check the PM_GET_CPUPM_STATE. If it is not available
* then check PM_GET_PM_STATE
*/
if (pm_ret < 0) {
HAL_ERROR (("Error in ioctl PM_GET_CPUPM_STATE: %s \n",
goto out;
}
switch (pm_ret) {
case PM_CPU_PM_ENABLED:
res = 1;
goto out;
case PM_CPU_PM_DISABLED:
res = 1;
goto out;
case PM_CPU_PM_NOTSET:
/*
* Check for PM_GET_PM_STATE
*/
if (pm_ret < 0) {
HAL_ERROR (("Error in ioctl PM_GET_PM_STATE: "
goto out;
}
switch (pm_ret) {
case PM_SYSTEM_PM_ENABLED:
res = 1;
goto out;
case PM_SYSTEM_PM_DISABLED:
"performance");
res = 1;
goto out;
default:
HAL_ERROR (("PM Internal error during ioctl "
"PM_GET_PM_STATE"));
goto out;
}
default:
HAL_ERROR (("Unknown value ioctl PM_GET_CPUPM_STATE"));
goto out;
}
case CPU_PERFORMANCE:
/*
* First check the PM_GET_CPU_THRESHOLD. If it is not available
* then check PM_GET_SYSTEM_THRESHOLD
*/
if (pm_ret >= 0) {
res = 1;
goto out;
/*
* PM_GET_CPU_THRESHOLD is not available
*/
if (res >= 0) {
res = 1;
goto out;
} else {
HAL_ERROR (("Error in PM_GET_CPU_THRESHOLD: %s",
goto out;
}
} else {
HAL_ERROR ((" Error in ioctl PM_GET_CPU_THRESHOLD: %s",
goto out;
}
default :
HAL_DEBUG (("Cannot recognize the HAL type to get value"));
goto out;
}
out:
return (res);
}
/*
* Send an error message as a response to the pending call
*/
static void
const char *err_name,
char *fmt, ...)
{
char err_buf[ERR_BUF_SIZE];
HAL_ERROR (("No Memory for DBUS error msg"));
return;
}
HAL_ERROR ((" Out Of Memory!"));
}
}
static void
char *err_str)
{
msg,
"org.freedesktop.Hal.CPUFreq.UnknownGovernor",
"Unknown CPUFreq Governor: %s",
err_str);
}
static void
char *err_str)
{
msg,
"org.freedesktop.Hal.CPUFreq.NoSuitableGovernor",
"Could not find a suitable governor: %s",
err_str);
}
static void
char *err_str)
{
msg,
"org.freedesktop.Hal.CPUFreq.Error",
"%s: Syslog might give more information",
err_str);
}
/*
* Puts the required cpufreq audit data and calls adt_put_event()
* to generate auditing
*/
static void
{
return;
}
HAL_INFO(("adt_alloc_event audit_cpufreq failed: %s",
return;
}
switch (event_id) {
case ADT_cpu_ondemand:
break;
case ADT_cpu_performance:
break;
case ADT_cpu_threshold:
break;
default:
goto clean;
}
if (result == 0) {
HAL_INFO (("adt_put_event(%d, ADT_SUCCESS) failed",
event_id));
}
} else {
HAL_INFO (("adt_put_event(%d, ADT_FAILURE) failed",
event_id));
}
}
(void) adt_end_session (ah);
}
/*
* Check if the cpufreq related operations are authorized
*/
static int
{
int adt_res = 0;
#ifdef HAVE_POLKIT
char user_id[128];
char *udi;
char *privilege;
/*
* Check for authorization before proceeding
*/
privilege = "hal-power-cpu";
dbus_error_init (&error);
if (system_bus == NULL) {
HAL_INFO (("Cannot connect to the system bus"));
goto out;
}
HAL_INFO (("Could not get the sender of the message"));
"Could not get the sender of the message");
goto out;
}
dbus_error_init (&error);
if (dbus_error_is_set (&error)) {
HAL_INFO (("Could not get the user id of the message"));
"Could not get the user id of the message sender");
goto out;
}
HAL_INFO (("Cannot get libpolkit context"));
"Cannot get libpolkit context to check privileges");
goto out;
}
NULL,
udi,
NULL) != LIBPOLKIT_RESULT_OK) {
HAL_INFO (("Cannot lookup privilege from PolicyKit"));
"Error looking up privileges from Policykit");
goto out;
}
if (!is_priv_allowed) {
HAL_INFO (("Caller doesn't possess required privilege to"
" change the governor"));
"Caller doesn't possess required "
"privilege to change the governor");
goto out;
}
HAL_DEBUG ((" Privilege Succeed"));
#endif
out:
return (adt_res);
}
/*
* Sets the CPU Freq governor. It sets the gov name in the /etc/power.conf
* and executes pmconfig. If governor is "ondemand" then "cpupm" is enabled in
* and if governor is performance, then "cpupm" is disabled
*/
static void
{
char *arg_val;
int arg_type;
int pid;
int done_flag = 0;
int sleep_time = 0;
int status;
int adt_res = 0;
int tmp_fd;
char pmconfig_cmd[128];
#ifdef sun
#endif
HAL_DEBUG (("Incoming message has no arguments"));
goto out;
}
if (arg_type != DBUS_TYPE_STRING) {
HAL_DEBUG (("Incomming message arg type is not string"));
"Specified governor is not a string");
goto out;
}
} else {
HAL_DEBUG (("Could not get SetCPUFreqGov from message iter"));
goto out;
}
if (adt_res != 0) {
goto out;
}
/*
* Update the /etc/power.conf file.
*/
if (tmp_fd == -1) {
HAL_ERROR ((" Error in creating a temp conf file"));
goto out;
}
if (adt_res != 0) {
HAL_DEBUG (("Error in edit /etc/power.conf"));
"Internal Error while setting the governor");
goto out;
}
/*
* Execute pmconfig
*/
if (system (pmconfig_cmd) != 0) {
HAL_ERROR ((" Error in executing pmconfig: %s",
goto out;
}
HAL_DEBUG (("Executed pmconfig"));
/*
* Just return an empty response, so that if the client
* is waiting for any response will not keep waiting
*/
HAL_ERROR (("Out of memory to msg reply"));
"Out of memory to create a response");
goto out;
}
HAL_ERROR (("Out of memory to msg reply"));
"Out of memory to create a response");
goto out;
}
out:
#ifdef sun
/*
* Audit the new governor change
*/
dbus_error_init (&error);
if (system_bus == NULL) {
HAL_INFO (("Cannot connect to the system bus %s",
return;
}
"solaris.system.power.cpu", 0);
"solaris.system.power.cpu", 0);
}
} else {
HAL_INFO ((" Could not get audit export data"));
}
#endif /* sun */
}
/*
* Sets the CPU Freq performance. It sets the cpu-threshold in the
* /etc/power.conf and executes pmconfig. The performnace value should
* be between 1 to 100. The cpu-threshold = ((performance val) * 15) secs.
*/
static void
{
int arg_val;
int arg_type;
int pid;
int done_flag = 0;
int sleep_time = 0;
int adt_res = 0;
int tmp_fd;
char pmconfig_cmd[128];
#ifdef sun
#endif
if (adt_res != 0) {
goto out;
}
/*
* Performance can only be set to dynamic governors. Currently the
* only supported dynamic governor is ondemand.
*/
if (current_gov[0] == 0) {
/*
* Read the current governor from /etc/power.conf
*/
HAL_ERROR ((" Error in reading from /etc/power.conf"));
"getting the governor");
goto out;
}
}
HAL_DEBUG (("To set performance the current gov should be "
"dynamic like ondemand"));
"to the current governor");
goto out;
}
HAL_DEBUG (("Incoming message has no arguments"));
goto out;
}
if (arg_type != DBUS_TYPE_INT32) {
HAL_DEBUG (("Incomming message arg type is not Integer"));
"Specified performance is not a Integer");
goto out;
}
HAL_INFO (("SetCPUFreqPerformance should be between 1 to 100"
": %d", arg_val));
"Performance value should be between 1 and 100");
goto out;
}
/*
* Update the /etc/power.conf file
*/
if (tmp_fd == -1) {
HAL_ERROR ((" Error in creating a temp conf file"));
goto out;
}
if (adt_res != 0) {
HAL_DEBUG (("Error while editing /etc/power.conf"));
"Internal error while setting the performance");
goto out;
}
/*
* Execute pmconfig
*/
if (system (pmconfig_cmd) != 0) {
HAL_ERROR ((" Error in executing pmconfig: %s",
"Internal error while setting the performance");
goto out;
}
HAL_DEBUG (("Executed pmconfig"));
/*
* Just return an empty response, so that if the client
* is waiting for any response will not keep waiting
*/
HAL_ERROR (("Out of memory to msg reply"));
"Out of memory to create a response");
goto out;
}
HAL_ERROR (("Out of memory to msg reply"));
"Out of memory to create a response");
goto out;
}
out:
#ifdef sun
/*
* Audit the new performance change
*/
dbus_error_init (&error);
if (system_bus == NULL) {
HAL_INFO (("Cannot connect to the system bus %s",
return;
}
"solaris.system.power.cpu", arg_val);
} else {
HAL_INFO ((" Could not get audit export data"));
}
#endif /* sun */
}
/*
* Returns in the dbus message the current gov.
*/
static void
{
int res;
char *param;
/*
* Get the governor type from /etc/power.conf if it is present.
*/
if (res != 1) {
HAL_INFO ((" Error in getting the current governor"));
" the governor");
return;
}
HAL_ERROR (("Out of memory to msg reply"));
"Internal error while getting the governor");
return;
}
/*
* Append reply arguments
*/
HAL_ERROR (("\n Could not allocate mem to param"));
" the governor");
return;
}
¶m)) {
HAL_ERROR (("\n Out Of Memory!\n"));
" the governor");
return;
}
HAL_ERROR (("\n Out Of Memory!\n"));
" the governor");
return;
}
}
/*
* Returns in the dbus message the current performance value
*/
static void
{
int res;
int param_int;
/*
* Get the performance value
*/
if (res != 1) {
HAL_INFO ((" Error in getting current performance"));
" the performance value");
return;
}
HAL_ERROR (("Out of memory to msg reply"));
" the performance value");
return;
}
/*
* Append reply arguments.pc_type.cpu_th gives the current cputhreshold
* vlaue in seconds. Have to convert it into CPU HAL interface
* performance value
*/
param_int = 1;
else
¶m_int)) {
HAL_ERROR (("\n Out Of Memory!\n"));
" the performance value");
return;
}
HAL_ERROR (("\n Out Of Memory!\n"));
" the performance value");
return;
}
}
/*
* Returns list of available governors. Currently just two governors are
* supported. They are "ondemand" and "performance"
*/
static void
{
int ngov;
HAL_ERROR (("Out of memory to msg reply"));
" the list of governors");
return;
}
/*
* Append reply arguments
*/
&array_iter)) {
HAL_ERROR (("\n Out of memory to msg reply array"));
" the list of governors");
return;
}
}
HAL_ERROR (("\n Out Of Memory!\n"));
" the list of governors");
return;
}
}
static DBusHandlerResult
{
/*
* Check for method types
*/
if (!dbus_connection_get_is_connected (con))
HAL_DEBUG (("Connection disconnected in cpufreq addon"));
"org.freedesktop.Hal.Device.CPUFreq",
"SetCPUFreqGovernor")) {
HAL_DEBUG (("---- SetCPUFreqGovernor is called "));
} else if (dbus_message_is_method_call (msg,
"org.freedesktop.Hal.Device.CPUFreq",
"GetCPUFreqGovernor")) {
HAL_DEBUG (("---- GetCPUFreqGovernor is called "));
} else if (dbus_message_is_method_call (msg,
"org.freedesktop.Hal.Device.CPUFreq",
"GetCPUFreqAvailableGovernors")) {
HAL_DEBUG (("---- GetCPUFreqAvailableGovernors is called "));
} else if (dbus_message_is_method_call (msg,
"org.freedesktop.Hal.Device.CPUFreq",
"SetCPUFreqPerformance")) {
HAL_DEBUG (("---- SetCPUFreqPerformance is called "));
} else if (dbus_message_is_method_call (msg,
"org.freedesktop.Hal.Device.CPUFreq",
"GetCPUFreqPerformance")) {
HAL_DEBUG (("---- GetCPUFreqPerformance is called "));
} else {
}
return (DBUS_HANDLER_RESULT_HANDLED);
}
static void
{
/*
* Start with the 'basic' privilege set and then add any
* of the privileges that will be required.
*/
HAL_INFO (("Error in setting the priv"));
return;
}
HAL_INFO (("Could not set the privileges"));
return;
}
HAL_INFO (("Could not set the privileges"));
return;
}
}
int
{
char *udi;
drop_privileges ();
setup_logger ();
HAL_INFO (("\n Could not get the UDI in addon-cpufreq"));
return (0);
}
dbus_error_init (&error);
HAL_ERROR (("main(): init_direct failed\n"));
return (0);
}
dbus_error_init (&error);
return (0);
}
/*
* Claim the cpufreq interface
*/
if (!libhal_device_claim_interface (ctx,
udi,
"org.freedesktop.Hal.Device.CPUFreq",
&error)) {
HAL_DEBUG ((" Cannot claim the CPUFreq interface"));
return (0);
}
/*
* Add the cpufreq capability
*/
if (!libhal_device_add_capability (ctx,
udi,
"cpufreq_control",
&error)) {
HAL_DEBUG ((" Could not add cpufreq_control capability"));
return (0);
}
/*
* Watches and times incoming messages
*/
/*
* Add a filter function which gets called when a message comes in
* and processes the message
*/
if (!dbus_connection_add_filter (conn,
NULL,
NULL)) {
HAL_INFO ((" Cannot add the CPUFreq filter function"));
return (0);
}
}