Ack - excellent work, thanks Radek. It's great to have those comments in
there; I expect that will be a big help when maintaining this code in
future.
cheers,
Zane.
On 27/10/11 12:03, Radek Novacek wrote:
Functions for listing, setting and getting power management profiles
for
the host agent, including unittest. Based on patch by
Jaroslav Skarvada<jskarvad(a)redhat.com>
Signed-off-by: Radek Novacek<rnovacek(a)redhat.com>
---
matahari.spec.in | 2 +
src/host/host-dbus.c | 79 +++++++++++
src/host/host-qmf.cpp | 31 ++++
src/host/org.matahariproject.Host.policy | 21 +++
src/host/schema.xml | 13 ++
src/include/matahari/host.h | 35 +++++
src/include/matahari/utilities.h | 12 ++
src/lib/host.c | 18 +++
src/lib/host_linux.c | 223 ++++++++++++++++++++++++++++++
src/lib/host_private.h | 28 ++++
src/lib/host_windows.c | 205 +++++++++++++++++++++++++++
src/lib/utilities.c | 25 ++++
src/unittests/mh_api_host.h | 37 +++++
13 files changed, 729 insertions(+), 0 deletions(-)
diff --git a/matahari.spec.in b/matahari.spec.in
index 8f105f4..83301c7 100644
--- a/matahari.spec.in
+++ b/matahari.spec.in
@@ -168,6 +168,8 @@ Requires: dmidecode
Requires(post): chkconfig
Requires(preun):chkconfig
Requires(preun):initscripts
+# Power management functions in host agent require tuned
+Requires: tuned
%description host
QMF agent for viewing and controlling remote hosts
diff --git a/src/host/host-dbus.c b/src/host/host-dbus.c
index 05b25f3..9ef8b6c 100644
--- a/src/host/host-dbus.c
+++ b/src/host/host-dbus.c
@@ -119,6 +119,85 @@ Host_set_uuid(Matahari* matahari, const char *lifetime, const char
*uuid, DBusGM
return TRUE;
}
+gboolean
+Host_set_power_profile(Matahari* matahari, const char *profile, DBusGMethodInvocation
*context)
+{
+ GError *error = NULL;
+ enum mh_result res;
+
+ if (!check_authorization(HOST_BUS_NAME ".set_power_profile",&error,
context)) {
+ dbus_g_method_return_error(context, error);
+ g_error_free(error);
+ return FALSE;
+ }
+ res = mh_host_set_power_profile(profile);
+ if (res != MH_RES_SUCCESS) {
+ error = g_error_new(MATAHARI_ERROR, res, mh_result_to_str(res));
+ dbus_g_method_return_error(context, error);
+ g_error_free(error);
+ return FALSE;
+ }
+
+ dbus_g_method_return(context, 0);
+ return TRUE;
+}
+
+gboolean
+Host_get_power_profile(Matahari* matahari, DBusGMethodInvocation *context)
+{
+ char *profile = NULL;
+ GError *error = NULL;
+ enum mh_result res;
+
+ if (!check_authorization(HOST_BUS_NAME ".get_power_profile",&error,
context)) {
+ dbus_g_method_return_error(context, error);
+ g_error_free(error);
+ return FALSE;
+ }
+ res = mh_host_get_power_profile(&profile);
+ if (res != MH_RES_SUCCESS) {
+ error = g_error_new(MATAHARI_ERROR, res, mh_result_to_str(res));
+ dbus_g_method_return_error(context, error);
+ g_error_free(error);
+ return FALSE;
+ }
+ dbus_g_method_return(context, profile);
+ free(profile);
+ return TRUE;
+}
+
+gboolean
+Host_list_power_profiles(Matahari* matahari, DBusGMethodInvocation *context)
+{
+ GError *error = NULL;
+ GList *list, *plist;
+ char **profiles;
+ int i = 0;
+
+ if (!check_authorization(HOST_BUS_NAME ".list_power_profiles",&error,
context)) {
+ dbus_g_method_return_error(context, error);
+ g_error_free(error);
+ return FALSE;
+ }
+ // Get the list of profiles
+ list = mh_host_list_power_profiles();
+
+ // Convert GList * with profiles to array (char **)
+ profiles = g_new(char *, g_list_length(list) + 1);
+ for (plist = g_list_first(list);
+ plist;
+ plist = g_list_next(plist)) {
+ profiles[i++] = plist->data;
+ }
+ profiles[i] = NULL; // Sentinel
+
+ dbus_g_method_return(context, profiles);
+ g_list_free_full(list, free);
+ g_free(profiles);
+ return TRUE;
+}
+
+
/* Generated dbus stuff for host
* MUST be after declaration of user defined functions.
*/
diff --git a/src/host/host-qmf.cpp b/src/host/host-qmf.cpp
index 618cf9c..4b72d00 100644
--- a/src/host/host-qmf.cpp
+++ b/src/host/host-qmf.cpp
@@ -112,6 +112,7 @@ HostAgent::invoke(qmf::AgentSession session, qmf::AgentEvent event,
return TRUE;
}
+ enum mh_result res = MH_RES_SUCCESS;
const std::string& methodName(event.getMethodName());
qpid::types::Variant::Map& args = event.getArguments();
@@ -153,7 +154,37 @@ HostAgent::invoke(qmf::AgentSession session, qmf::AgentEvent event,
if (uuid) {
event.addReturnArgument("uuid", uuid);
}
+ } else if (methodName == "set_power_profile") {
+ res = mh_host_set_power_profile(args["profile"].asString().c_str());
+ if (res != MH_RES_SUCCESS) {
+ session.raiseException(event, mh_result_to_str(res));
+ goto bail;
+ } else {
+ event.addReturnArgument("status", 0);
+ }
+ } else if (methodName == "get_power_profile") {
+ char *profile = NULL;
+ res = mh_host_get_power_profile(&profile);
+ if (res != MH_RES_SUCCESS) {
+ session.raiseException(event, mh_result_to_str(res));
+ goto bail;
+ } else {
+ event.addReturnArgument("profile", profile);
+ }
+ free(profile);
+ } else if (methodName == "list_power_profiles") {
+ GList *plist = NULL;
+ GList *profile_list = NULL;
+ _qtype::Variant::List s_list;
+
+ profile_list = mh_host_list_power_profiles();
+ for (plist = g_list_first(profile_list); plist;
+ plist = g_list_next(plist)) {
+ s_list.push_back((const char *) plist->data);
+ }
+ event.addReturnArgument("profiles", s_list);
+ g_list_free_full(profile_list, free);
} else {
session.raiseException(event, mh_result_to_str(MH_RES_NOT_IMPLEMENTED));
goto bail;
diff --git a/src/host/org.matahariproject.Host.policy
b/src/host/org.matahariproject.Host.policy
index ed04a25..90636a8 100644
--- a/src/host/org.matahariproject.Host.policy
+++ b/src/host/org.matahariproject.Host.policy
@@ -200,4 +200,25 @@
<allow_active>auth_admin</allow_active>
</defaults>
</action>
+<action id="org.matahariproject.Host.get_power_profile">
+<defaults>
+<allow_any>no</allow_any>
+<allow_inactive>no</allow_inactive>
+<allow_active>auth_admin</allow_active>
+</defaults>
+</action>
+<action id="org.matahariproject.Host.set_power_profile">
+<defaults>
+<allow_any>no</allow_any>
+<allow_inactive>no</allow_inactive>
+<allow_active>auth_admin</allow_active>
+</defaults>
+</action>
+<action id="org.matahariproject.Host.list_power_profiles">
+<defaults>
+<allow_any>no</allow_any>
+<allow_inactive>no</allow_inactive>
+<allow_active>auth_admin</allow_active>
+</defaults>
+</action>
</policyconfig>
diff --git a/src/host/schema.xml b/src/host/schema.xml
index 1841907..f5b2092 100644
--- a/src/host/schema.xml
+++ b/src/host/schema.xml
@@ -90,6 +90,19 @@
<arg name="uuid" dir="I"
type="sstr" />
<arg name="rc" dir="O"
type="int32" />
</method>
+
+<method name="set_power_profile" desc="Set power management
profile">
+<arg name="profile" dir="I"
type="sstr" />
+<arg name="status" dir="O"
type="uint32" />
+</method>
+
+<method name="get_power_profile" desc="Get current power
management profile">
+<arg name="profile" dir="O"
type="sstr" />
+</method>
+
+<method name="list_power_profiles" desc="List available power
management profiles">
+<arg name="profiles" dir="O"
type="list" />
+</method>
</class>
<event name="heartbeat"
args="timestamp,sequence,hostname,uuid" />
diff --git a/src/include/matahari/host.h b/src/include/matahari/host.h
index 1da756a..31a5a98 100644
--- a/src/include/matahari/host.h
+++ b/src/include/matahari/host.h
@@ -28,6 +28,9 @@
#include<stdint.h>
#include<stdlib.h>
#include<sigar.h>
+#include<glib.h>
+
+#include "matahari/errors.h"
/**
* Get a UUID for a host.
@@ -118,4 +121,36 @@ mh_host_get_load_averages(sigar_loadavg_t *avg);
void
mh_host_get_processes(sigar_proc_stat_t *procs);
+/**
+ * Set power management profile.
+ *
+ * \param[in] profile One of profile returned by mh_host_list_power_profiles
+ * function
+ * \return see enum mh_result
+ */
+enum mh_result
+mh_host_set_power_profile(const char *profile);
+
+/**
+ * Get current power management profile.
+ *
+ * \param[out] profile Variable where current profile will be written to.
+ * Variable must be freed with free().
+ *
+ * \return see enum mh_result
+ */
+enum mh_result
+mh_host_get_power_profile(char **profile);
+
+/**
+ * Get list of all available power management profiles.
+ *
+ * \note The return of this routine must be freed with
+ * g_list_free_full(returned_list, free);
+ *
+ * \return list of all profiles.
+ */
+GList *
+mh_host_list_power_profiles(void);
+
#endif // __MH_HOST_H__
diff --git a/src/include/matahari/utilities.h b/src/include/matahari/utilities.h
index 893d068..5084261 100644
--- a/src/include/matahari/utilities.h
+++ b/src/include/matahari/utilities.h
@@ -128,4 +128,16 @@ mh_strlen_zero(const char *s)
char *
mh_string_copy(char *dst, const char *src, size_t dst_len);
+/**
+ * Read the whole content of file from file descriptor
+ *
+ * \param[in] fd File descriptor to read from. Must be readable.
+ * \param[out] data Data read from the file. Must be freed with free().
+ *
+ * \retval 0 File is empty
+ * \retval>0 Number of characters read
+ * \retval<0 Error occured
+ */
+gsize
+mh_read_from_fd(int fd, char **data);
#endif
diff --git a/src/lib/host.c b/src/lib/host.c
index eb712c9..c6fb4c8 100644
--- a/src/lib/host.c
+++ b/src/lib/host.c
@@ -310,3 +310,21 @@ mh_host_set_uuid(const char *lifetime, const char *uuid)
return G_FILE_ERROR_NOSYS;
}
+
+enum mh_result
+mh_host_set_power_profile(const char* profile)
+{
+ return host_os_set_power_profile(profile);
+}
+
+enum mh_result
+mh_host_get_power_profile(char **profile)
+{
+ return host_os_get_power_profile(profile);
+}
+
+GList *
+mh_host_list_power_profiles(void)
+{
+ return host_os_list_power_profiles();
+}
diff --git a/src/lib/host_linux.c b/src/lib/host_linux.c
index 5b9a4f9..ac9c305 100644
--- a/src/lib/host_linux.c
+++ b/src/lib/host_linux.c
@@ -24,10 +24,12 @@
#include<limits.h>
#include<string.h>
#include<fcntl.h>
+#include<errno.h>
#include<sys/reboot.h>
#include<sys/sysinfo.h>
#include<sys/utsname.h>
#include<sys/ioctl.h>
+#include<sys/wait.h>
#include<linux/reboot.h>
#include<linux/kd.h>
@@ -45,6 +47,7 @@
#define UUID_STR_BUF_LEN 37
+#define BUFSIZE 4096
const char *
host_os_get_cpu_flags(void)
@@ -436,3 +439,223 @@ host_os_set_custom_uuid(const char *uuid)
return rc;
}
+static enum mh_result
+exec_command(const char *apath, char *args[], int atimeout, char **stdoutbuf,
+ char **stderrbuf)
+{
+ enum mh_result res = MH_RES_SUCCESS;
+ GPid pid = 0;
+ GError *gerr = NULL;
+ int status = 0;
+ int timeout = atimeout;
+ gint stdout_fd;
+ gint stderr_fd;
+
+ if (!args || !args[0])
+ return MH_RES_OTHER_ERROR;
+
+ mh_trace("Spawning '%s'\n", args[0]);
+ if (!g_spawn_async_with_pipes(apath, args, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
+ NULL, NULL,&pid, NULL,&stdout_fd,
+&stderr_fd,&gerr)) {
+ mh_perror(LOG_ERR, "Spawn_process failed with code %d, message:
%s\n",
+ gerr->code, gerr->message);
+ return MH_RES_OTHER_ERROR;
+ }
+
+ mh_trace("Waiting for %d", pid);
+ while (timeout> 0&& waitpid(pid,&status, WNOHANG)<= 0) {
+ sleep(1);
+ timeout--;
+ }
+
+ if (timeout == 0) {
+ int killrc = sigar_proc_kill(pid, SIGKILL);
+
+ res = MH_RES_BACKEND_ERROR;
+ mh_warn("%d - timed out after %dms", pid, atimeout);
+
+ if (killrc != SIGAR_OK&& killrc != ESRCH) {
+ mh_err("kill(%d, KILL) failed: %d", pid, killrc);
+ }
+
+ } else if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status)> 0)
+ res = MH_RES_BACKEND_ERROR;
+ mh_err("Managed process %d exited with rc=%d", pid,
WEXITSTATUS(status));
+
+ } else if (WIFSIGNALED(status)) {
+ int signo = WTERMSIG(status);
+ res = MH_RES_BACKEND_ERROR;
+ mh_err("Managed process %d exited with signal=%d", pid, signo);
+ }
+
+ mh_trace("Child done: %d", pid);
+
+ if (stdoutbuf) {
+ if (mh_read_from_fd(stdout_fd, stdoutbuf)< 0) {
+ mh_err("Unable to read standard output from command %s.", apath);
+ stdoutbuf = NULL;
+ } else {
+ mh_debug("stdout: %s", *stdoutbuf);
+ }
+ }
+
+ if (stderrbuf) {
+ if (mh_read_from_fd(stderr_fd, stderrbuf)< 0) {
+ mh_err("Unable to read standard error output from command %s.",
apath);
+ stderrbuf = NULL;
+ } else {
+ mh_debug("stderr: %s", *stderrbuf);
+ }
+ }
+
+ close(stdout_fd);
+ close(stderr_fd);
+
+ g_spawn_close_pid(pid);
+
+ return res;
+}
+
+GList *
+host_os_list_power_profiles(void)
+{
+ char *stdoutbuf = NULL;
+ char *c1, *c2;
+ char *args[3] = {0};
+ GList *list = NULL;
+ enum mh_result res;
+ int len;
+
+ args[0] = TUNEDADM;
+ args[1] = TA_LISTPROFILES;
+ args[2] = NULL;
+
+ res = exec_command(NULL, args, TIMEOUT,&stdoutbuf, NULL);
+ len = strlen(stdoutbuf);
+
+ if (res == MH_RES_SUCCESS&& stdoutbuf) {
+ c1 = stdoutbuf;
+ do {
+ // Each line with profile in "tuned-adm list" starts with
"-"
+ if (*c1 == '-'&& c1 - stdoutbuf + 2< len) {
+ // Skip the dash and the space after it
+ c1 += 2;
+ // Profile name is the rest of the line
+ c2 = strchr(c1, '\n');
+ if (c2&& c2> c1) {
+ // Replace \n with \0 and append it to output
+ *c2 = '\0';
+ list = g_list_append(list, strdup(c1));
+ // Replace it back with \n, so free() will free whole string
+ *c2 = '\n';
+ }
+ } else {
+ // Line doesn't contain the profile -> skip the line
+ c2 = strchr(c1, '\n');
+ }
+ // Move c1 to beggining of the next line
+ if (c2) {
+ c1 = c2 + 1;
+ }
+ } while (c2&& c1 - stdoutbuf< len);
+
+ if (g_list_length(list) == 0) {
+ // Return at least "off" profile, if no other profile found
+ list = g_list_append(list, strdup(TA_OFF));
+ }
+ }
+ free(stdoutbuf);
+
+ return list;
+}
+
+static gboolean
+check_profile(const char *profile)
+{
+ GList *list = NULL;
+ GList *llist;
+ gboolean rc = FALSE;
+
+ if (!(list = host_os_list_power_profiles()))
+ return FALSE;
+
+ if (!g_list_length(list))
+ return FALSE;
+
+ for (llist = g_list_first(list); llist&& !rc; llist = g_list_next(llist))
{
+ mh_trace("comparing '%s' with '%s'", (char *)
llist->data, profile);
+ if (!strcmp(profile, (char *) llist->data)) {
+ rc = TRUE;
+ break;
+ }
+ }
+ g_list_free_full(list, free);
+
+ return rc;
+}
+
+enum mh_result
+host_os_set_power_profile(const char *profile)
+{
+ char *args[4] = {0};
+
+ args[0] = TUNEDADM;
+ if (!profile)
+ return MH_RES_INVALID_ARGS;
+
+ if (!strcmp(profile, TA_OFF)) {
+ args[1] = TA_OFF;
+ mh_trace("switching tuning off");
+ } else {
+ if (!check_profile(profile)) {
+ mh_err("invalid profile: %s", profile);
+ return MH_RES_INVALID_ARGS;
+ }
+ args[1] = TA_SETPROFILE;
+ mh_trace("setting profile: %s", profile);
+ }
+
+ args[2] = (char *) profile;
+ args[3] = NULL;
+
+ return exec_command(NULL, args, TIMEOUT, NULL, NULL);
+}
+
+enum mh_result
+host_os_get_power_profile(char **profile)
+{
+ char *stdoutbuf = NULL;
+ char *c1, *c2 = NULL;
+ char *args[3] = {0};
+ enum mh_result res;
+
+ args[0] = TUNEDADM;
+ args[1] = TA_GETPROFILE;
+ args[2] = NULL;
+
+ res = exec_command(NULL, args, TIMEOUT,&stdoutbuf, NULL);
+ if (res == MH_RES_SUCCESS) {
+ // Parse first line of "tuned-adm active", that is something like
+ // "Current active profile: profile_name", so take what is after
+ // semicolon and space to the end of the line
+ if (stdoutbuf&& (c1 = strchr(stdoutbuf, ':'))&&
+ (c1 - stdoutbuf + 2< strlen(stdoutbuf))) {
+ if ((c2 = strchr(c1, '\n'))) {
+ *c2 = '\0';
+ }
+ // + 2 because we need to skip semicolon and space
+ *profile = strdup(c1 + 2);
+ if (c2)
+ *c2 = '\n';
+ } else {
+ *profile = strdup(STR_UNK);
+ }
+ } else {
+ res = MH_RES_BACKEND_ERROR;
+ }
+ free(stdoutbuf);
+
+ return res;
+}
diff --git a/src/lib/host_private.h b/src/lib/host_private.h
index c2f7a1b..3a985d5 100644
--- a/src/lib/host_private.h
+++ b/src/lib/host_private.h
@@ -31,6 +31,25 @@
#include<glib.h>
+#define TIMEOUT 10
+#define TA_PATH "/usr/bin/"
+#define TA_SETPROFILE "profile"
+#define TA_GETPROFILE "active"
+#define TA_LISTPROFILES "list"
+#define TA_OFF "off"
+#define TUNEDADM TA_PATH "tuned-adm"
+
+#define SYSTEM32 "system32"
+#define POWERCFG "powercfg"
+#define PC_SETPROFILE "-SETACTIVE"
+#define PC_GETPROFILE "-GETACTIVESCHEME"
+#define PC_LISTPROFILES "-LIST"
+#define GUID "GUID: "
+
+#define STR_UNK "UNKNOWN"
+#define STR_ERR "ERROR"
+
+
const char *
host_os_get_cpu_flags(void);
@@ -67,4 +86,13 @@ host_os_agent_uuid(void);
int
host_os_set_custom_uuid(const char *uuid);
+enum mh_result
+host_os_set_power_profile(const char *profile);
+
+enum mh_result
+host_os_get_power_profile(char **profile);
+
+GList *
+host_os_list_power_profiles(void);
+
#endif /* __MH_HOST_PRIVATE_H__ */
diff --git a/src/lib/host_windows.c b/src/lib/host_windows.c
index f53567c..fb8af0b 100644
--- a/src/lib/host_windows.c
+++ b/src/lib/host_windows.c
@@ -33,12 +33,15 @@
#include "matahari/logging.h"
#include "matahari/host.h"
+#include "matahari/errors.h"
#include "host_private.h"
static const char CUSTOM_UUID_KEY[] = "CustomUUID";
static const char REBOOT_UUID_KEY[] = "RebootUUID";
+#define BUFSIZE 4096
+
const char *
host_os_get_cpu_flags(void)
{
@@ -394,3 +397,205 @@ host_os_set_custom_uuid(const char *uuid)
return ret;
}
+
+static enum mh_result
+exec_command(const char *apath, char *args[], int atimeout, char **stdoutbuf,
+ char **stderrbuf)
+{
+ enum mh_result res = MH_RES_SUCCESS;
+ DWORD status = 0;
+ gint stdout_fd;
+ gint stderr_fd;
+ HANDLE phandle = NULL;
+ GError *gerr = NULL;
+
+ if (!args || !args[0])
+ return MH_RES_OTHER_ERROR;
+
+ /* convert to ms */
+ atimeout = atimeout * 1000;
+
+ mh_trace("Spawning '%s'\n", args[0]);
+ if (!g_spawn_async_with_pipes(apath, args, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
+ NULL, NULL, (GPid *)&phandle, NULL,
+&stdout_fd,&stderr_fd,&gerr)) {
+ mh_perror(LOG_ERR, "Spawn_process failed with code %d, message:
%s\n",
+ gerr->code, gerr->message);
+ return MH_RES_OTHER_ERROR;
+ }
+
+ mh_trace("Waiting for %p", (void *)phandle);
+ WaitForSingleObject(phandle, atimeout);
+
+ if (GetExitCodeProcess(phandle,&status) == 0) {
+ mh_err("Could not get exit code: %lu", GetLastError());
+ res = MH_RES_BACKEND_ERROR;
+ }
+
+ if (res == MH_RES_BACKEND_ERROR) {
+ TerminateProcess(phandle, 1);
+ mh_err("%p - timed out after %dms", (void *)phandle, atimeout);
+ }
+
+ /* Nice to log it somewhere */
+ mh_debug("Result of '%s' was %d", args[0], (int)status);
+
+ mh_trace("Child done: %p", (void *)phandle);
+
+ if (stdoutbuf) {
+ if (mh_read_from_fd(stdout_fd, stdoutbuf)< 0) {
+ mh_err("Unable to read standard output from command %s.", apath);
+ stdoutbuf = NULL;
+ } else {
+ mh_debug("stdout: %s", *stdoutbuf);
+ }
+ }
+
+ if (stderrbuf) {
+ if (mh_read_from_fd(stderr_fd, stderrbuf)< 0) {
+ mh_err("Unable to read standard error output from command %s.",
apath);
+ stderrbuf = NULL;
+ } else {
+ mh_debug("stderr: %s", *stderrbuf);
+ }
+ }
+
+ _close(stdout_fd);
+ _close(stderr_fd);
+
+ g_spawn_close_pid((GPid *) phandle);
+
+ return res;
+}
+
+static gboolean
+get_profiles(GList **names, GList **guids)
+{
+ gboolean rc = FALSE;
+ int len;
+ char *stdoutbuf = NULL;
+ char *c1, *c2;
+ char *args[3] = {0};
+
+ args[0] = SYSTEM32 "\\" POWERCFG;
+ args[1] = PC_LISTPROFILES;
+ args[2] = NULL;
+
+ if (exec_command(getenv("WINDIR"), args , TIMEOUT,&stdoutbuf, NULL)
+ == MH_RES_SUCCESS&& stdoutbuf) {
+ rc = TRUE;
+ len = strlen(stdoutbuf);
+ c1 = stdoutbuf;
+
+ // powercfg command returns lines with profiles, that looks like:
+ // "Power Scheme GUID: 381b4222-f694-41f0-9685-ff5bb260df2e
(Balanced)"
+ // So we will take word after "GUID: " as GUID and word in brackets
+ // as name of the profile
+ while ((c1 = strstr(c1, GUID))) {
+ c1 += strlen(GUID);
+ // c1 is now after "GUID: "
+ if (c1 - stdoutbuf< len&& (c2 = strchr(c1, ' '))) {
+ // c2 points to end of uuid, put \0 there
+ *c2 = '\0';
+ // Add guid to the list
+ *guids = g_list_append(*guids, strdup(c1));
+ // Put \n back, so the string can be freed
+ *c2 = '\n';
+ // Now find name in brackets
+ if ((c1 = strchr(c2, '('))&& ++c1 - stdoutbuf<
len&&
+ (c2 = strchr(c1, ')'))) {
+ // Append profile name to the list
+ *c2 = '\0';
+ *names = g_list_append(*names, strdup(c1));
+ *c2 = ')';
+ }
+ }
+ }
+ }
+
+ if (!*names || !*guids || g_list_length(*names) != g_list_length(*guids))
+ rc = FALSE;
+
+ free(stdoutbuf);
+ return rc;
+}
+
+enum mh_result
+host_os_set_power_profile(const char *profile)
+{
+ enum mh_result res = MH_RES_SUCCESS;
+ char *guid = NULL;
+ GList *names = NULL;
+ GList *guids = NULL;
+ GList *pnames = NULL;
+ GList *pguids = NULL;
+ char *args[4] = {0};
+
+ if (!get_profiles(&names,&guids)) {
+ return MH_RES_BACKEND_ERROR;
+ }
+
+ for (pnames = g_list_first(names), pguids = g_list_first(guids);
+ pnames&& pguids&& !guid;
+ pnames = g_list_next(pnames), pguids = g_list_next(pguids)) {
+ if (!strcmp((char *) pnames->data, profile)) {
+ guid = strdup((char *) pguids->data);
+ }
+ }
+
+ g_list_free_full(names, free);
+ g_list_free_full(guids, free);
+
+ if (guid) {
+ args[0] = SYSTEM32 "\\" POWERCFG;
+ args[1] = PC_SETPROFILE;
+ args[2] = guid;
+ args[3] = NULL;
+ res = exec_command(getenv("WINDIR"), args, TIMEOUT, NULL, NULL);
+ free(guid);
+ } else {
+ res = MH_RES_INVALID_ARGS;
+ }
+ return res;
+}
+
+enum mh_result
+host_os_get_power_profile(char **profile)
+{
+ enum mh_result res;
+ char *stdoutbuf = NULL;
+ char *c1, *c2;
+ char *args[3] = {0};
+
+ args[0] = SYSTEM32 "\\" POWERCFG;
+ args[1] = PC_GETPROFILE;
+ args[2] = NULL;
+
+ res = exec_command(getenv("WINDIR"), args, TIMEOUT,&stdoutbuf, NULL);
+ if (res == MH_RES_SUCCESS) {
+ // Profile name is string between brackets
+ if (stdoutbuf&& (c1 = strchr(stdoutbuf, '('))&&
+ ++c1 - stdoutbuf< strlen(stdoutbuf)&& (c2 = strchr(c1,
')'))) {
+ *c2 = '\0';
+ *profile = strdup(c1);
+ *c2 = ')';
+ } else {
+ res = MH_RES_BACKEND_ERROR;
+ }
+ }
+ free(stdoutbuf);
+
+ return res;
+}
+
+GList *
+host_os_list_power_profiles(void)
+{
+ GList *names = NULL;
+ GList *guids = NULL;
+
+ get_profiles(&names,&guids);
+ g_list_free_full(guids, free);
+
+ return names;
+}
diff --git a/src/lib/utilities.c b/src/lib/utilities.c
index 8da6b44..d62f76e 100644
--- a/src/lib/utilities.c
+++ b/src/lib/utilities.c
@@ -545,3 +545,28 @@ mh_result_to_str(enum mh_result res)
}
return "FIXME: result code doesn't have message assigned!";
}
+
+gsize
+mh_read_from_fd(int fd, char **data)
+{
+ gsize length;
+ GError *err = NULL;
+ GIOChannel *ch;
+
+#ifdef WIN32
+ ch = g_io_channel_win32_new_fd(fd);
+#else
+ ch = g_io_channel_unix_new(fd);
+#endif
+
+ if (g_io_channel_read_to_end(ch, data,&length,&err) != G_IO_STATUS_NORMAL)
{
+ mh_err("Unable to read from filedescriptor %d: %s", fd,
err->message);
+ free(*data);
+ data = NULL;
+ return -err->code;
+ }
+ g_io_channel_close(ch);
+ g_io_channel_unref(ch);
+ return length;
+}
+
diff --git a/src/unittests/mh_api_host.h b/src/unittests/mh_api_host.h
index 00287ca..56b3feb 100644
--- a/src/unittests/mh_api_host.h
+++ b/src/unittests/mh_api_host.h
@@ -163,6 +163,43 @@ class MhApiHostSuite : public CxxTest::TestSuite
infomsg.str("");
}
+ void testPowerManagement(void)
+ {
+ char *original, *newProfile;
+ const char *profile;
+ GList *profiles;
+ guint len;
+
+ srand(time(NULL));
+
+ // Save the original profile
+ TS_ASSERT(mh_host_get_power_profile(&original) == MH_RES_SUCCESS);
+
+ // List all profiles
+ profiles = mh_host_list_power_profiles();
+ len = g_list_length(profiles);
+
+ // Check if at least one profile is present
+ TS_ASSERT(len> 0);
+
+ // Choose random profile
+ profile = (const char *) g_list_nth(profiles, rand() % len)->data;
+
+ // Set it
+ TS_ASSERT(mh_host_set_power_profile(profile) == MH_RES_SUCCESS);
+
+ // Check if the profile is set
+ TS_ASSERT(mh_host_get_power_profile(&newProfile) == MH_RES_SUCCESS);
+
+ TS_ASSERT(strcmp(newProfile, profile) == 0);
+
+ // Restore original profile
+ TS_ASSERT(mh_host_set_power_profile(original) == MH_RES_SUCCESS);
+
+ free(original);
+ free(newProfile);
+ g_list_free_full(profiles, free);
+ }
};
#endif