summaryrefslogtreecommitdiff
path: root/usr/src/cmd/hal/addons/cpufreq/addon-cpufreq.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/hal/addons/cpufreq/addon-cpufreq.c')
-rw-r--r--usr/src/cmd/hal/addons/cpufreq/addon-cpufreq.c1424
1 files changed, 1424 insertions, 0 deletions
diff --git a/usr/src/cmd/hal/addons/cpufreq/addon-cpufreq.c b/usr/src/cmd/hal/addons/cpufreq/addon-cpufreq.c
new file mode 100644
index 0000000000..770853f0f0
--- /dev/null
+++ b/usr/src/cmd/hal/addons/cpufreq/addon-cpufreq.c
@@ -0,0 +1,1424 @@
+/***************************************************************************
+ *
+ * addon-cpufreq.c : Routines to support CPUFreq interface
+ *
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ ***************************************************************************/
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#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 <sys/types.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/dkio.h>
+#include <sys/stat.h>
+#include <sys/types.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 "../../hald/logger.h"
+#include "../../utils/adt_data.h"
+
+#include <pwd.h>
+#ifdef HAVE_POLKIT
+#include <libpolkit.h>
+#endif
+
+#ifdef sun
+#include <bsm/adt.h>
+#include <bsm/adt_event.h>
+#include <sys/pm.h>
+#endif
+
+#define POWER_CONF_FILE "/etc/power.conf"
+#define PMCONFIG "/usr/sbin/pmconfig -f"
+#define PM "/dev/pm"
+
+#define FILE_ARR_SIZE 256
+#define EDIT_TYPE_SIZE 64
+#define ERR_BUF_SIZE 256
+
+#define WAIT_TIME 30
+
+char TMP_CONF_FILE[64] = "/tmp/power.conf.XXXXXX";
+const char *sender;
+unsigned long uid;
+
+/*
+ * Specify different CPUFreq related HAL activities that can be done
+ */
+enum hal_type {
+ CPU_GOV,
+ CPU_PERFORMANCE
+};
+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;
+} pconf_edit_type;
+
+/*
+ * 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",
+ NULL
+};
+
+static char current_gov[EDIT_TYPE_SIZE];
+
+/*
+ * Free up the mem allocated to hold the DBusError
+ */
+static void
+check_and_free_error(DBusError *error)
+{
+ if (dbus_error_is_set (error)) {
+ dbus_error_free (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
+edit_power_conf_file(pconf_edit_type pc_edit_type,
+ power_conf_hal_type pc_hal_type, char *tmp_file)
+{
+ FILE *pfile;
+ 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
+ */
+ if (tmp_file == NULL) {
+ HAL_INFO ((" Invalid temp file name"));
+ return (EINVAL);
+ }
+ sprintf (cp_cmd_str, "/usr/bin/cp %s %s", POWER_CONF_FILE, tmp_file);
+ if (system (cp_cmd_str) != 0) {
+ HAL_ERROR ((" Error in copying %s to %s, %s",
+ POWER_CONF_FILE, tmp_file, strerror (errno)));
+ return (errno);
+ }
+
+ pfile = fopen (tmp_file, "r+");
+ if (pfile == NULL) {
+ HAL_INFO (("Cannot open file %s: %s",
+ tmp_file, strerror (errno)));
+ return (errno);
+ }
+
+ switch (pc_hal_type) {
+ case CPU_GOV:
+ if ((pc_edit_type.cpu_gov == NULL) ||
+ ((strcmp (pc_edit_type.cpu_gov, "ondemand") != 0) &&
+ (strcmp (pc_edit_type.cpu_gov, "performance") != 0))) {
+ HAL_INFO ((" CPU governor is not available/valid."
+ " Should be either ondemand or performance"));
+ res = EINVAL;
+ goto out;
+ }
+ file_edit_type = "cpupm";
+ if (strcmp (pc_edit_type.cpu_gov, "ondemand") == 0) {
+ file_edit_value = " enable";
+ } else {
+ file_edit_value = "disable";
+ }
+ break;
+ case CPU_PERFORMANCE:
+ if (pc_edit_type.cpu_th == NULL) {
+ HAL_INFO ((" CPU Threshold is not valid."));
+ res = EINVAL;
+ goto out;
+ }
+ file_edit_type = "cpu-threshold";
+ sprintf (file_edit_threshold, "%d", pc_edit_type.cpu_th);
+ file_edit_value = file_edit_threshold;
+ break;
+ default:
+ HAL_DEBUG ((" Cannot recognize the type of change being"
+ " made to /etc/power.conf"));
+ res = EINVAL;
+ goto out;
+ }
+
+ while (fgets (tstr, FILE_ARR_SIZE, pfile) != NULL) {
+ if ((tstr == NULL) || (strlen (tstr) <= 0))
+ continue;
+ /*
+ * Look for line containing "cpupm" or "cpu-threshold"
+ */
+
+ if (strstr (tstr, file_edit_type) == NULL) {
+ fset = fset + strlen (tstr);
+ continue;
+ }
+ /*
+ * If the required value already present. Just
+ * return
+ */
+ if (strstr (tstr, file_edit_value) != NULL) {
+ res = 0;
+ goto out;
+ }
+
+ if (fseek (pfile, fset, SEEK_SET) != 0) {
+ HAL_ERROR (("\n Error in fseek %s: %s",
+ POWER_CONF_FILE, strerror (errno)));
+ res = errno;
+ goto out;
+ }
+ /*
+ * Update the file with new values
+ */
+ sprintf (file_update_str, "%s %s \n",
+ file_edit_type, file_edit_value);
+
+ /*
+ * 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
+ */
+ next_fset = fset + strlen(tstr);
+ if (fseek (pfile, next_fset, SEEK_SET) != 0) {
+ HAL_ERROR (("\n Error in fseek %s: %s",
+ tmp_file, strerror (errno)));
+ res = errno;
+ goto out;
+ }
+ if (fgets (tstr, FILE_ARR_SIZE, pfile) != NULL) {
+ do {
+ snprintf (temp_str, FILE_ARR_SIZE,
+ "%s\n", tstr);
+ fseek (pfile, fset, SEEK_SET);
+ fputs (temp_str, pfile);
+ fset = fset + strlen(tstr);
+ next_fset = next_fset + strlen(tstr);
+ fseek (pfile, next_fset, SEEK_SET);
+
+ } while (fgets (tstr, FILE_ARR_SIZE, pfile) != NULL);
+ }
+
+ fseek (pfile, fset, SEEK_SET);
+
+ if (fputs (file_update_str, pfile) == EOF) {
+ HAL_ERROR (("\n Error in writing to"
+ " %s: %s", POWER_CONF_FILE,
+ strerror (errno)));
+ res = errno;
+ goto out;
+ }
+
+ if (fflush (pfile) == EOF) {
+ HAL_ERROR (("\n Error in flushing to"
+ " %s: %s", POWER_CONF_FILE,
+ strerror (errno)));
+ }
+ 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"));
+ if (fseek (pfile, 0, SEEK_END) != 0) {
+ HAL_ERROR (("\n Error in fseek to %s: %s",
+ POWER_CONF_FILE, strerror (errno)));
+ res = errno;
+ goto out;
+ }
+
+ /*
+ * Update the file with new values
+ */
+ sprintf (file_update_str, "%s %s \n", file_edit_type, file_edit_value);
+
+ if (fputs (file_update_str, pfile) == EOF) {
+ HAL_ERROR (("Error in writing to file %s: %s",
+ POWER_CONF_FILE, strerror (errno)));
+ res = errno;
+ goto out;
+ }
+
+ if (fflush (pfile) == EOF) {
+ HAL_ERROR (("\n Error in flushing to %s: %s",
+ POWER_CONF_FILE, strerror (errno)));
+ }
+ res = 0;
+out:
+ fclose (pfile);
+ 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
+read_power_conf_file(pconf_edit_type *pc_value,
+ power_conf_hal_type pc_hal_type)
+{
+
+ FILE *pfile;
+ char tstr[FILE_ARR_SIZE];
+ long fset = 0;
+ char *file_edit_type;
+ char *tpstr;
+ int res = 0;
+
+ pfile = fopen (POWER_CONF_FILE, "r");
+ if (pfile == NULL) {
+ HAL_INFO (("\n Cannot open the file %s: %s",
+ POWER_CONF_FILE, strerror (errno)));
+ 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;
+ }
+
+ while (fgets (tstr, FILE_ARR_SIZE, pfile) != NULL) {
+ if ((tstr == NULL) || (strlen (tstr) <= 0))
+ continue;
+ /*
+ * Look for line containing "cpupm" or "cpu-threshold"
+ */
+ if (strstr (tstr, file_edit_type) == NULL)
+ continue;
+
+ /*
+ * If the required value already present. Just
+ * get the value
+ */
+ tpstr = strtok (tstr, " ");
+ tpstr = strtok (NULL, " ");
+ if (tpstr == NULL) {
+ HAL_INFO (("Value of %s in %s is not valid",
+ file_edit_type, POWER_CONF_FILE));
+ res = -1;
+ goto out;
+ }
+
+ if (pc_hal_type == CPU_GOV) {
+ /*
+ * Copy the corresponding governor
+ */
+ if (strcmp (tpstr, "enable") == 0) {
+ sprintf (pc_value->cpu_gov,
+ "%s", "ondemand");
+ } else {
+ sprintf (pc_value->cpu_gov,
+ "%s", "performance");
+ }
+ } else {
+ pc_value->cpu_th = atoi (tpstr);
+ }
+ res = 1;
+ goto out;
+ }
+ /*
+ * Entry not found in the file
+ */
+ HAL_DEBUG ((" No entry of %s in %s", file_edit_type, POWER_CONF_FILE));
+ res = 0;
+
+out:
+ fclose (pfile);
+ 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
+get_cur_val(pconf_edit_type *cur_value,
+ power_conf_hal_type pc_hal_type)
+{
+
+ int pm_fd;
+ int res = -1;
+ int pm_ret;
+
+ pm_fd = open (PM, O_RDONLY);
+ if (pm_fd == -1) {
+ HAL_ERROR (("Error opening %s: %s \n", PM, strerror (errno)));
+ 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
+ */
+ pm_ret = ioctl (pm_fd, PM_GET_CPUPM_STATE);
+ if (pm_ret < 0) {
+ HAL_ERROR (("Error in ioctl PM_GET_CPUPM_STATE: %s \n",
+ strerror (errno)));
+ goto out;
+ }
+ switch (pm_ret) {
+ case PM_CPU_PM_ENABLED:
+ sprintf (cur_value->cpu_gov, "%s", "ondemand");
+ res = 1;
+ goto out;
+ case PM_CPU_PM_DISABLED:
+ sprintf (cur_value->cpu_gov, "%s", "performance");
+ res = 1;
+ goto out;
+ case PM_CPU_PM_NOTSET:
+ /*
+ * Check for PM_GET_PM_STATE
+ */
+ pm_ret = ioctl (pm_fd, PM_GET_PM_STATE);
+ if (pm_ret < 0) {
+ HAL_ERROR (("Error in ioctl PM_GET_PM_STATE: "
+ "%s", strerror (errno)));
+ goto out;
+ }
+ switch (pm_ret) {
+ case PM_SYSTEM_PM_ENABLED:
+ sprintf (cur_value->cpu_gov, "%s", "ondemand");
+ res = 1;
+ goto out;
+ case PM_SYSTEM_PM_DISABLED:
+ sprintf (cur_value->cpu_gov, "%s",
+ "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
+ */
+ pm_ret = ioctl (pm_fd, PM_GET_CPU_THRESHOLD);
+ if (pm_ret >= 0) {
+ cur_value->cpu_th = pm_ret;
+ res = 1;
+ goto out;
+ } else if ((pm_ret == EINVAL) || (pm_ret == ENOTTY)) {
+ /*
+ * PM_GET_CPU_THRESHOLD is not available
+ */
+ pm_ret = ioctl (pm_fd, PM_GET_SYSTEM_THRESHOLD);
+ if (res >= 0) {
+ cur_value->cpu_th = pm_ret;
+ res = 1;
+ goto out;
+ } else {
+ HAL_ERROR (("Error in PM_GET_CPU_THRESHOLD: %s",
+ strerror (errno)));
+ goto out;
+ }
+ } else {
+ HAL_ERROR ((" Error in ioctl PM_GET_CPU_THRESHOLD: %s",
+ strerror (errno)));
+ goto out;
+ }
+ default :
+ HAL_DEBUG (("Cannot recognize the HAL type to get value"));
+ goto out;
+ }
+out:
+ close (pm_fd);
+ return (res);
+}
+/*
+ * Send an error message as a response to the pending call
+ */
+static void
+generate_err_msg(DBusConnection *con,
+ DBusMessage *msg,
+ const char *err_name,
+ char *fmt, ...)
+{
+
+ DBusMessage *err_msg;
+ char err_buf[ERR_BUF_SIZE];
+ va_list va_args;
+
+ va_start (va_args, fmt);
+ vsnprintf (err_buf, ERR_BUF_SIZE, fmt, va_args);
+ va_end (va_args);
+
+ HAL_DEBUG ((" Sending error message: %s", err_buf));
+
+ err_msg = dbus_message_new_error (msg, err_name, err_buf);
+ if (err_msg == NULL) {
+ HAL_ERROR (("No Memory for DBUS error msg"));
+ return;
+ }
+
+ if (!dbus_connection_send (con, err_msg, NULL)) {
+ HAL_ERROR ((" Out Of Memory!"));
+ }
+ dbus_connection_flush (con);
+
+}
+
+static void
+gen_unknown_gov_err(DBusConnection *con,
+ DBusMessage *msg,
+ char *err_str)
+{
+
+ generate_err_msg (con,
+ msg,
+ "org.freedesktop.Hal.CPUFreq.UnknownGovernor",
+ "Unknown CPUFreq Governor: %s",
+ err_str);
+}
+
+static void
+gen_no_suitable_gov_err(DBusConnection *con,
+ DBusMessage *msg,
+ char *err_str)
+{
+
+ generate_err_msg (con,
+ msg,
+ "org.freedesktop.Hal.CPUFreq.NoSuitableGovernor",
+ "Could not find a suitable governor: %s",
+ err_str);
+}
+
+static void
+gen_cpufreq_err(DBusConnection *con,
+ DBusMessage *msg,
+ char *err_str)
+{
+ generate_err_msg (con,
+ 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
+audit_cpufreq(const adt_export_data_t *imported_state, au_event_t event_id,
+ int result, const char *auth_used, const int cpu_thr_value)
+{
+ adt_session_data_t *ah;
+ adt_event_data_t *event;
+ struct passwd *msg_pwd;
+ uid_t gid;
+
+ if (adt_start_session (&ah, imported_state, 0) != 0) {
+ HAL_INFO (("adt_start_session failed: %s", strerror (errno)));
+ return;
+ }
+
+ if ((event = adt_alloc_event (ah, event_id)) == NULL) {
+ HAL_INFO(("adt_alloc_event audit_cpufreq failed: %s",
+ strerror (errno)));
+ return;
+ }
+
+ switch (event_id) {
+ case ADT_cpu_ondemand:
+ event->adt_cpu_ondemand.auth_used = (char *)auth_used;
+ break;
+ case ADT_cpu_performance:
+ event->adt_cpu_performance.auth_used = (char *)auth_used;
+ break;
+ case ADT_cpu_threshold:
+ event->adt_cpu_threshold.auth_used = (char *)auth_used;
+ event->adt_cpu_threshold.threshold = cpu_thr_value;
+ break;
+ default:
+ goto clean;
+ }
+
+ if (result == 0) {
+ if (adt_put_event (event, ADT_SUCCESS, ADT_SUCCESS) != 0) {
+ HAL_INFO (("adt_put_event(%d, ADT_SUCCESS) failed",
+ event_id));
+ }
+ } else {
+ if (adt_put_event (event, ADT_FAILURE, result) != 0) {
+ HAL_INFO (("adt_put_event(%d, ADT_FAILURE) failed",
+ event_id));
+ }
+ }
+
+clean:
+ adt_free_event (event);
+ (void) adt_end_session (ah);
+}
+
+/*
+ * Check if the cpufreq related operations are authorized
+ */
+
+static int
+check_authorization(DBusConnection *con, DBusMessage *msg)
+{
+ int adt_res = 0;
+#ifdef HAVE_POLKIT
+ char user_id[128];
+ char *udi;
+ char *privilege;
+ DBusError error;
+ gboolean is_priv_allowed;
+ gboolean is_priv_temporary;
+ DBusConnection *system_bus = NULL;
+ LibPolKitContext *pol_ctx = NULL;
+
+ /*
+ * Check for authorization before proceeding
+ */
+ udi = getenv ("HAL_PROP_INFO_UDI");
+ privilege = "hal-power-cpu";
+
+ dbus_error_init (&error);
+ system_bus = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
+ if (system_bus == NULL) {
+ HAL_INFO (("Cannot connect to the system bus"));
+ LIBHAL_FREE_DBUS_ERROR (&error);
+ gen_cpufreq_err (con, msg, "Cannot connect to the system bus");
+ adt_res = EINVAL;
+ goto out;
+ }
+
+ sender = dbus_message_get_sender (msg);
+ HAL_INFO (("Auth Sender: %s", sender));
+
+ if (sender == NULL) {
+ HAL_INFO (("Could not get the sender of the message"));
+ gen_cpufreq_err (con, msg,
+ "Could not get the sender of the message");
+ adt_res = ADT_FAIL_VALUE_AUTH;
+ goto out;
+ }
+
+ dbus_error_init (&error);
+ uid = dbus_bus_get_unix_user (system_bus, sender, &error);
+ if (dbus_error_is_set (&error)) {
+ HAL_INFO (("Could not get the user id of the message"));
+ LIBHAL_FREE_DBUS_ERROR (&error);
+ gen_cpufreq_err (con, msg,
+ "Could not get the user id of the message sender");
+ adt_res = ADT_FAIL_VALUE_AUTH;
+ goto out;
+ }
+
+ snprintf (user_id, sizeof (user_id), "%d", uid);
+ HAL_DEBUG ((" User id is : %d", uid));
+
+ pol_ctx = libpolkit_new_context (system_bus);
+ if (pol_ctx == NULL) {
+ HAL_INFO (("Cannot get libpolkit context"));
+ gen_cpufreq_err (con, msg,
+ "Cannot get libpolkit context to check privileges");
+ adt_res = ADT_FAIL_VALUE_AUTH;
+ goto out;
+ }
+
+ if (libpolkit_is_uid_allowed_for_privilege (pol_ctx,
+ NULL,
+ user_id,
+ privilege,
+ udi,
+ &is_priv_allowed,
+ &is_priv_temporary,
+ NULL) != LIBPOLKIT_RESULT_OK) {
+ HAL_INFO (("Cannot lookup privilege from PolicyKit"));
+ gen_cpufreq_err (con, msg,
+ "Error looking up privileges from Policykit");
+ adt_res = ADT_FAIL_VALUE_AUTH;
+ goto out;
+ }
+
+ if (!is_priv_allowed) {
+ HAL_INFO (("Caller doesn't possess required privilege to"
+ " change the governor"));
+ gen_cpufreq_err (con, msg,
+ "Caller doesn't possess required "
+ "privilege to change the governor");
+ adt_res = ADT_FAIL_VALUE_AUTH;
+ 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
+set_cpufreq_gov(DBusConnection *con, DBusMessage *msg, void *udata)
+{
+ DBusMessageIter arg_iter;
+ DBusMessage *msg_reply;
+ char *arg_val;
+ int arg_type;
+ int pid;
+ int done_flag = 0;
+ int sleep_time = 0;
+ int status;
+ int adt_res = 0;
+ char tmp_conf_file[64] = "/tmp/power.conf.XXXXXX";
+ int tmp_fd;
+ char pmconfig_cmd[128];
+ pconf_edit_type pc_edit_type;
+#ifdef sun
+ adt_export_data_t *adt_data;
+ size_t adt_data_size;
+ DBusConnection *system_bus = NULL;
+ DBusError error;
+#endif
+
+ if (! dbus_message_iter_init (msg, &arg_iter)) {
+ HAL_DEBUG (("Incoming message has no arguments"));
+ gen_unknown_gov_err (con, msg, "No governor specified");
+ adt_res = EINVAL;
+ goto out;
+ }
+ arg_type = dbus_message_iter_get_arg_type (&arg_iter);
+
+ if (arg_type != DBUS_TYPE_STRING) {
+ HAL_DEBUG (("Incomming message arg type is not string"));
+ gen_unknown_gov_err (con, msg,
+ "Specified governor is not a string");
+ adt_res = EINVAL;
+ goto out;
+ }
+ dbus_message_iter_get_basic (&arg_iter, &arg_val);
+ if (arg_val != NULL) {
+ HAL_DEBUG (("SetCPUFreqGov is: %s", arg_val));
+ } else {
+ HAL_DEBUG (("Could not get SetCPUFreqGov from message iter"));
+ adt_res = EINVAL;
+ goto out;
+ }
+
+ adt_res = check_authorization (con, msg);
+
+ if (adt_res != 0) {
+ goto out;
+ }
+
+ /*
+ * Update the /etc/power.conf file.
+ */
+ tmp_fd = mkstemp (tmp_conf_file);
+ if (tmp_fd == -1) {
+ HAL_ERROR ((" Error in creating a temp conf file"));
+ adt_res = EINVAL;
+ goto out;
+ }
+ strcpy (pc_edit_type.cpu_gov, arg_val);
+ adt_res = edit_power_conf_file (pc_edit_type, CPU_GOV, tmp_conf_file);
+ if (adt_res != 0) {
+ HAL_DEBUG (("Error in edit /etc/power.conf"));
+ gen_cpufreq_err (con, msg,
+ "Internal Error while setting the governor");
+ unlink (tmp_conf_file);
+ goto out;
+ }
+
+ /*
+ * Execute pmconfig
+ */
+ sprintf (pmconfig_cmd, "%s %s", PMCONFIG, tmp_conf_file);
+ if (system (pmconfig_cmd) != 0) {
+ HAL_ERROR ((" Error in executing pmconfig: %s",
+ strerror (errno)));
+ adt_res = errno;
+ gen_cpufreq_err (con, msg, "Error in executing pmconfig");
+ unlink (tmp_conf_file);
+ goto out;
+ }
+ unlink (tmp_conf_file);
+ HAL_DEBUG (("Executed pmconfig"));
+ sprintf (current_gov, "%s", arg_val);
+
+ /*
+ * Just return an empty response, so that if the client
+ * is waiting for any response will not keep waiting
+ */
+ msg_reply = dbus_message_new_method_return (msg);
+ if (msg_reply == NULL) {
+ HAL_ERROR (("Out of memory to msg reply"));
+ gen_cpufreq_err (con, msg,
+ "Out of memory to create a response");
+ adt_res = ENOMEM;
+ goto out;
+ }
+
+ if (!dbus_connection_send (con, msg_reply, NULL)) {
+ HAL_ERROR (("Out of memory to msg reply"));
+ gen_cpufreq_err (con, msg,
+ "Out of memory to create a response");
+ adt_res = ENOMEM;
+ goto out;
+ }
+
+ dbus_connection_flush (con);
+
+out:
+
+#ifdef sun
+ /*
+ * Audit the new governor change
+ */
+ dbus_error_init (&error);
+ system_bus = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
+ if (system_bus == NULL) {
+ HAL_INFO (("Cannot connect to the system bus %s",
+ error.message));
+ LIBHAL_FREE_DBUS_ERROR (&error);
+ return;
+ }
+
+ adt_data = get_audit_export_data (system_bus, sender, &adt_data_size);
+ if (adt_data != NULL) {
+ if (strcmp (arg_val, "ondemand") == 0) {
+ audit_cpufreq (adt_data, ADT_cpu_ondemand, adt_res,
+ "solaris.system.power.cpu", 0);
+ } else if (strcmp (arg_val, "performance") == 0) {
+ audit_cpufreq (adt_data, ADT_cpu_performance, adt_res,
+ "solaris.system.power.cpu", 0);
+ }
+ free (adt_data);
+ } 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
+set_cpufreq_performance(DBusConnection *con, DBusMessage *msg, void *udata)
+{
+
+ DBusMessageIter arg_iter;
+ DBusMessage *msg_reply;
+ int arg_val;
+ int arg_type;
+ int pid;
+ int done_flag = 0;
+ int sleep_time = 0;
+ int adt_res = 0;
+ char tmp_conf_file[64] = "/tmp/power.conf.XXXXXX";
+ int tmp_fd;
+ char pmconfig_cmd[128];
+ pconf_edit_type pc_edit_type;
+#ifdef sun
+ adt_export_data_t *adt_data;
+ size_t adt_data_size;
+ DBusConnection *system_bus = NULL;
+ DBusError error;
+#endif
+
+ adt_res = check_authorization (con, msg);
+
+ 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
+ */
+ if (read_power_conf_file (&pc_edit_type, CPU_GOV) != 1) {
+ HAL_ERROR ((" Error in reading from /etc/power.conf"));
+ gen_cpufreq_err (con, msg, "Internal error while "
+ "getting the governor");
+ adt_res = EINVAL;
+ goto out;
+ }
+ sprintf (current_gov, "%s", pc_edit_type.cpu_gov);
+ }
+
+ if (strcmp (current_gov, "ondemand") != 0) {
+ HAL_DEBUG (("To set performance the current gov should be "
+ "dynamic like ondemand"));
+ gen_no_suitable_gov_err (con, msg, "Cannot set performance "
+ "to the current governor");
+ adt_res = EINVAL;
+ goto out;
+ }
+
+ if (! dbus_message_iter_init (msg, &arg_iter)) {
+ HAL_DEBUG (("Incoming message has no arguments"));
+ gen_no_suitable_gov_err(con, msg, "No performance specified");
+ adt_res = EINVAL;
+ goto out;
+ }
+ arg_type = dbus_message_iter_get_arg_type (&arg_iter);
+
+ if (arg_type != DBUS_TYPE_INT32) {
+ HAL_DEBUG (("Incomming message arg type is not Integer"));
+ gen_no_suitable_gov_err (con, msg,
+ "Specified performance is not a Integer");
+ adt_res = EINVAL;
+ goto out;
+ }
+ dbus_message_iter_get_basic (&arg_iter, &arg_val);
+ if ((arg_val < 1) || (arg_val > 100)) {
+ HAL_INFO (("SetCPUFreqPerformance should be between 1 to 100"
+ ": %d", arg_val));
+ gen_no_suitable_gov_err (con, msg,
+ "Performance value should be between 1 and 100");
+ adt_res = EINVAL;
+ goto out;
+ }
+
+ HAL_DEBUG (("SetCPUFreqPerformance is: %d", arg_val));
+
+ /*
+ * Update the /etc/power.conf file
+ */
+ tmp_fd = mkstemp (tmp_conf_file);
+ if (tmp_fd == -1) {
+ HAL_ERROR ((" Error in creating a temp conf file"));
+ adt_res = EINVAL;
+ goto out;
+ }
+ pc_edit_type.cpu_th = arg_val * 15;
+ adt_res = edit_power_conf_file (pc_edit_type, CPU_PERFORMANCE,
+ tmp_conf_file);
+ if (adt_res != 0) {
+ HAL_DEBUG (("Error while editing /etc/power.conf"));
+ gen_cpufreq_err (con, msg,
+ "Internal error while setting the performance");
+ unlink (tmp_conf_file);
+ goto out;
+ }
+
+ /*
+ * Execute pmconfig
+ */
+ sprintf (pmconfig_cmd, "%s %s", PMCONFIG, tmp_conf_file);
+ if (system (pmconfig_cmd) != 0) {
+ HAL_ERROR ((" Error in executing pmconfig: %s",
+ strerror (errno)));
+ adt_res = errno;
+ gen_cpufreq_err (con, msg,
+ "Internal error while setting the performance");
+ unlink (tmp_conf_file);
+ goto out;
+ }
+ unlink (tmp_conf_file);
+ HAL_DEBUG (("Executed pmconfig"));
+
+ /*
+ * Just return an empty response, so that if the client
+ * is waiting for any response will not keep waiting
+ */
+
+ msg_reply = dbus_message_new_method_return (msg);
+ if (msg_reply == NULL) {
+ HAL_ERROR (("Out of memory to msg reply"));
+ gen_cpufreq_err (con, msg,
+ "Out of memory to create a response");
+ adt_res = ENOMEM;
+ goto out;
+ }
+
+ if (!dbus_connection_send (con, msg_reply, NULL)) {
+ HAL_ERROR (("Out of memory to msg reply"));
+ gen_cpufreq_err (con, msg,
+ "Out of memory to create a response");
+ adt_res = ENOMEM;
+ goto out;
+ }
+
+ dbus_connection_flush (con);
+out:
+#ifdef sun
+
+ /*
+ * Audit the new performance change
+ */
+ dbus_error_init (&error);
+ system_bus = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
+ if (system_bus == NULL) {
+ HAL_INFO (("Cannot connect to the system bus %s",
+ error.message));
+ LIBHAL_FREE_DBUS_ERROR (&error);
+ return;
+ }
+
+ adt_data = get_audit_export_data (system_bus, sender, &adt_data_size);
+ if (adt_data != NULL) {
+ audit_cpufreq (adt_data, ADT_cpu_threshold, adt_res,
+ "solaris.system.power.cpu", arg_val);
+ free (adt_data);
+ } else {
+ HAL_INFO ((" Could not get audit export data"));
+ }
+
+#endif /* sun */
+}
+
+/*
+ * Returns in the dbus message the current gov.
+ */
+static void
+get_cpufreq_gov(DBusConnection *con, DBusMessage *msg, void *udata)
+{
+
+ DBusMessageIter rep_iter;
+ DBusMessage *msg_reply;
+ int res;
+ pconf_edit_type pc_type;
+ char *param;
+
+ /*
+ * Get the governor type from /etc/power.conf if it is present.
+ */
+ res = get_cur_val (&pc_type, CPU_GOV);
+ if (res != 1) {
+ HAL_INFO ((" Error in getting the current governor"));
+ gen_cpufreq_err (con, msg, "Internal error while getting"
+ " the governor");
+ return;
+ }
+
+ HAL_DEBUG ((" Current governor is: %s", pc_type.cpu_gov));
+
+ msg_reply = dbus_message_new_method_return (msg);
+ if (msg_reply == NULL) {
+ HAL_ERROR (("Out of memory to msg reply"));
+ gen_cpufreq_err (con, msg,
+ "Internal error while getting the governor");
+ return;
+ }
+
+ /*
+ * Append reply arguments
+ */
+ param = (char *) malloc (sizeof (char) * 250);
+ if (param == NULL) {
+ HAL_ERROR (("\n Could not allocate mem to param"));
+ gen_cpufreq_err (con, msg, "Internal error while getting"
+ " the governor");
+ return;
+ }
+ sprintf (param, "%s", pc_type.cpu_gov);
+
+ dbus_message_iter_init_append (msg_reply, &rep_iter);
+ if (!dbus_message_iter_append_basic (&rep_iter, DBUS_TYPE_STRING,
+ &param)) {
+ HAL_ERROR (("\n Out Of Memory!\n"));
+ gen_cpufreq_err (con, msg, "Internal error while getting"
+ " the governor");
+ free (param);
+ return;
+ }
+
+ if (!dbus_connection_send (con, msg_reply, NULL)) {
+ HAL_ERROR (("\n Out Of Memory!\n"));
+ gen_cpufreq_err (con, msg, "Internal error while getting"
+ " the governor");
+ free (param);
+ return;
+ }
+ dbus_connection_flush (con);
+ free (param);
+}
+
+/*
+ * Returns in the dbus message the current performance value
+ */
+static void
+get_cpufreq_performance(DBusConnection *con, DBusMessage *msg, void *udata)
+{
+
+ DBusMessageIter rep_iter;
+ DBusMessage *msg_reply;
+ int res;
+ pconf_edit_type pc_type;
+ int param_int;
+
+ /*
+ * Get the performance value
+ */
+ res = get_cur_val (&pc_type, CPU_PERFORMANCE);
+ if (res != 1) {
+ HAL_INFO ((" Error in getting current performance"));
+ gen_cpufreq_err (con, msg, "Internal error while getting"
+ " the performance value");
+ return;
+ }
+
+ HAL_DEBUG ((" The current performance: %d", pc_type.cpu_th));
+
+ msg_reply = dbus_message_new_method_return (msg);
+ if (msg_reply == NULL) {
+ HAL_ERROR (("Out of memory to msg reply"));
+ gen_cpufreq_err (con, msg, "Internal error while getting"
+ " 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
+ */
+ if (pc_type.cpu_th < 15)
+ param_int = 1;
+ else
+ param_int = (pc_type.cpu_th / 15);
+
+ HAL_DEBUG (("Performance: %d \n", param_int));
+
+ dbus_message_iter_init_append (msg_reply, &rep_iter);
+ if (!dbus_message_iter_append_basic (&rep_iter, DBUS_TYPE_INT32,
+ &param_int)) {
+ HAL_ERROR (("\n Out Of Memory!\n"));
+ gen_cpufreq_err (con, msg, "Internal error while getting"
+ " the performance value");
+ return;
+ }
+
+ if (!dbus_connection_send (con, msg_reply, NULL)) {
+ HAL_ERROR (("\n Out Of Memory!\n"));
+ gen_cpufreq_err (con, msg, "Internal error while getting"
+ " the performance value");
+ return;
+ }
+ dbus_connection_flush (con);
+}
+
+/*
+ * Returns list of available governors. Currently just two governors are
+ * supported. They are "ondemand" and "performance"
+ */
+
+static void
+get_cpufreq_avail_gov(DBusConnection *con, DBusMessage *msg, void *udata)
+{
+
+ DBusMessageIter rep_iter;
+ DBusMessageIter array_iter;
+ DBusMessage *msg_reply;
+ int ngov;
+
+ msg_reply = dbus_message_new_method_return (msg);
+ if (msg_reply == NULL) {
+ HAL_ERROR (("Out of memory to msg reply"));
+ gen_cpufreq_err (con, msg, "Internal error while getting"
+ " the list of governors");
+ return;
+ }
+
+ /*
+ * Append reply arguments
+ */
+ dbus_message_iter_init_append (msg_reply, &rep_iter);
+
+ if (!dbus_message_iter_open_container (&rep_iter,
+ DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING,
+ &array_iter)) {
+ HAL_ERROR (("\n Out of memory to msg reply array"));
+ gen_cpufreq_err (con, msg, "Internal error while getting"
+ " the list of governors");
+ return;
+ }
+
+ for (ngov = 0; gov_list[ngov] != NULL; ngov++) {
+ if (gov_list[ngov])
+ HAL_DEBUG (("\n%d Gov Name: %s", ngov, gov_list[ngov]));
+ dbus_message_iter_append_basic (&array_iter,
+ DBUS_TYPE_STRING,
+ &gov_list[ngov]);
+ }
+ dbus_message_iter_close_container (&rep_iter, &array_iter);
+
+ if (!dbus_connection_send (con, msg_reply, NULL)) {
+ HAL_ERROR (("\n Out Of Memory!\n"));
+ gen_cpufreq_err (con, msg, "Internal error while getting"
+ " the list of governors");
+ return;
+ }
+ dbus_connection_flush (con);
+}
+
+static DBusHandlerResult
+hald_dbus_cpufreq_filter(DBusConnection *con, DBusMessage *msg, void *udata)
+{
+ HAL_DEBUG ((" Inside CPUFreq filter:%s", dbus_message_get_path(msg)));
+ /*
+ * Check for method types
+ */
+ if (!dbus_connection_get_is_connected (con))
+ HAL_DEBUG (("Connection disconnected in cpufreq addon"));
+
+ if (dbus_message_is_method_call (msg,
+ "org.freedesktop.Hal.Device.CPUFreq",
+ "SetCPUFreqGovernor")) {
+ HAL_DEBUG (("---- SetCPUFreqGovernor is called "));
+
+ set_cpufreq_gov (con, msg, udata);
+
+ } else if (dbus_message_is_method_call (msg,
+ "org.freedesktop.Hal.Device.CPUFreq",
+ "GetCPUFreqGovernor")) {
+ HAL_DEBUG (("---- GetCPUFreqGovernor is called "));
+
+ get_cpufreq_gov (con, msg, udata);
+ } else if (dbus_message_is_method_call (msg,
+ "org.freedesktop.Hal.Device.CPUFreq",
+ "GetCPUFreqAvailableGovernors")) {
+ HAL_DEBUG (("---- GetCPUFreqAvailableGovernors is called "));
+
+ get_cpufreq_avail_gov (con, msg, udata);
+ } else if (dbus_message_is_method_call (msg,
+ "org.freedesktop.Hal.Device.CPUFreq",
+ "SetCPUFreqPerformance")) {
+ HAL_DEBUG (("---- SetCPUFreqPerformance is called "));
+
+ set_cpufreq_performance (con, msg, udata);
+ } else if (dbus_message_is_method_call (msg,
+ "org.freedesktop.Hal.Device.CPUFreq",
+ "GetCPUFreqPerformance")) {
+ HAL_DEBUG (("---- GetCPUFreqPerformance is called "));
+
+ get_cpufreq_performance (con, msg, udata);
+ } else {
+ HAL_DEBUG (("---Not Set/Get cpufreq gov---"));
+ }
+
+ return (DBUS_HANDLER_RESULT_HANDLED);
+
+}
+
+static void
+drop_privileges()
+{
+ priv_set_t *pPrivSet = NULL;
+ priv_set_t *lPrivSet = NULL;
+
+ /*
+ * Start with the 'basic' privilege set and then add any
+ * of the privileges that will be required.
+ */
+ if ((pPrivSet = priv_str_to_set ("basic", ",", NULL)) == NULL) {
+ HAL_INFO (("Error in setting the priv"));
+ return;
+ }
+
+ (void) priv_addset (pPrivSet, PRIV_SYS_DEVICES);
+
+ if (setppriv (PRIV_SET, PRIV_INHERITABLE, pPrivSet) != 0) {
+ HAL_INFO (("Could not set the privileges"));
+ priv_freeset (pPrivSet);
+ return;
+ }
+
+ (void) priv_addset (pPrivSet, PRIV_PROC_AUDIT);
+ (void) priv_addset (pPrivSet, PRIV_SYS_CONFIG);
+
+ if (setppriv (PRIV_SET, PRIV_PERMITTED, pPrivSet) != 0) {
+ HAL_INFO (("Could not set the privileges"));
+ priv_freeset (pPrivSet);
+ return;
+ }
+
+ priv_freeset (pPrivSet);
+
+}
+
+int
+main(int argc, char **argv)
+{
+
+ LibHalContext *ctx = NULL;
+ char *udi;
+ DBusError error;
+ DBusConnection *conn;
+
+ GMainLoop *loop = g_main_loop_new (NULL, FALSE);
+
+ drop_privileges ();
+ openlog ("hald-addon-cpufreq", LOG_PID, LOG_DAEMON);
+ setup_logger ();
+
+ bzero (current_gov, EDIT_TYPE_SIZE-1);
+
+ if ((udi = getenv ("UDI")) == NULL) {
+ HAL_INFO (("\n Could not get the UDI in addon-cpufreq"));
+ return (0);
+ }
+
+ dbus_error_init (&error);
+ if ((ctx = libhal_ctx_init_direct (&error)) == NULL) {
+ HAL_ERROR (("main(): init_direct failed\n"));
+ return (0);
+ }
+ dbus_error_init (&error);
+ if (!libhal_device_addon_is_ready (ctx, getenv ("UDI"), &error)) {
+ check_and_free_error (&error);
+ return (0);
+ }
+
+ /*
+ * Claim the cpufreq interface
+ */
+
+ HAL_DEBUG (("cpufreq Introspect XML: %s", cpufreq_introspect_xml));
+
+ if (!libhal_device_claim_interface (ctx,
+ udi,
+ "org.freedesktop.Hal.Device.CPUFreq",
+ cpufreq_introspect_xml,
+ &error)) {
+ HAL_DEBUG ((" Cannot claim the CPUFreq interface"));
+ check_and_free_error (&error);
+ return (0);
+ }
+
+ conn = libhal_ctx_get_dbus_connection (ctx);
+
+ /*
+ * Add the cpufreq capability
+ */
+ if (!libhal_device_add_capability (ctx,
+ udi,
+ "cpufreq_control",
+ &error)) {
+ HAL_DEBUG ((" Could not add cpufreq_control capability"));
+ check_and_free_error (&error);
+ return (0);
+ }
+ /*
+ * Watches and times incoming messages
+ */
+
+ dbus_connection_setup_with_g_main (conn, NULL);
+
+ /*
+ * Add a filter function which gets called when a message comes in
+ * and processes the message
+ */
+
+ if (!dbus_connection_add_filter (conn,
+ hald_dbus_cpufreq_filter,
+ NULL,
+ NULL)) {
+ HAL_INFO ((" Cannot add the CPUFreq filter function"));
+ return (0);
+ }
+
+ dbus_connection_set_exit_on_disconnect (conn, 0);
+
+ g_main_loop_run (loop);
+}