From ae8dc4df287554af308b2b8537d4f40a5b75500b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jaroslav=20=C5=A0karvada?= <jskarvad@redhat.com>
Date: Mon, 30 May 2011 10:46:41 +0200
Subject: [PATCH] Add Power Management (PM) agent

Provides pm_set_profile, pm_get_profile, pm_list_profiles
for setting, getting current and listing all available PM profiles.
---
 CMakeLists.txt                        |    3 +
 matahari.spec                         |   45 +++++
 src/CMakeLists.txt                    |    1 +
 src/include/matahari/pm.h             |   70 +++++++
 src/lib/CMakeLists.txt                |    6 +-
 src/lib/pm.c                          |   45 +++++
 src/lib/pm_linux.c                    |  335 +++++++++++++++++++++++++++++++++
 src/lib/pm_private.h                  |   39 ++++
 src/lib/pm_windows.c                  |  301 +++++++++++++++++++++++++++++
 src/pm/CMakeLists.txt                 |   61 ++++++
 src/pm/org.matahariproject.PM.conf    |   18 ++
 src/pm/org.matahariproject.PM.policy  |   27 +++
 src/pm/org.matahariproject.PM.service |    4 +
 src/pm/pm-dbus.c                      |  148 +++++++++++++++
 src/pm/pm-qmf.cpp                     |  116 ++++++++++++
 src/pm/schema.xml                     |   21 ++
 src/schema.xml                        |   18 ++
 src/windows/install.bat               |    2 +-
 18 files changed, 1258 insertions(+), 2 deletions(-)
 create mode 100644 src/include/matahari/pm.h
 create mode 100644 src/lib/pm.c
 create mode 100644 src/lib/pm_linux.c
 create mode 100644 src/lib/pm_private.h
 create mode 100644 src/lib/pm_windows.c
 create mode 100644 src/pm/CMakeLists.txt
 create mode 100644 src/pm/org.matahariproject.PM.conf
 create mode 100644 src/pm/org.matahariproject.PM.policy
 create mode 100644 src/pm/org.matahariproject.PM.service
 create mode 100644 src/pm/pm-dbus.c
 create mode 100644 src/pm/pm-qmf.cpp
 create mode 100644 src/pm/schema.xml

diff --git a/CMakeLists.txt b/CMakeLists.txt
index e4daf05..a656085 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -79,15 +79,18 @@ if(WIN32)
   set( CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS ${CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS} 
   					   " Some comment\\n  DetailPrint \\\"Stopping Matahari services\\\"\\n"
 					   " Some comment\\n  ExecWait '\\\"$SYSDIR\\\\sc.exe stop mh_hostd\\\"'\\n"
+					   " Some comment\\n  ExecWait '\\\"$SYSDIR\\\\sc.exe stop mh_pmd\\\"'\\n"
 					   " Some comment\\n  ExecWait '\\\"$SYSDIR\\\\sc.exe stop mh_netd\\\"'\\n"
 					   " Some comment\\n  ExecWait '\\\"$SYSDIR\\\\sc.exe stop mh_serviced\\\"'\\n"
 					   " Give them time to stop\\n  Sleep 10000\\n"
   					   " Some comment\\n  DetailPrint \\\"Removing Matahari services\\\"\\n"
 					   " Some comment\\n  ExecWait '\\\"$SYSDIR\\\\sc.exe delete mh_hostd\\\"'\\n"
+					   " Some comment\\n  ExecWait '\\\"$SYSDIR\\\\sc.exe delete mh_pmd\\\"'\\n"
 					   " Some comment\\n  ExecWait '\\\"$SYSDIR\\\\sc.exe delete mh_netd\\\"'\\n"
 					   " Some comment\\n  ExecWait '\\\"$SYSDIR\\\\sc.exe delete mh_serviced\\\"'\\n"
   					   " Some comment\\n  DetailPrint \\\"Removing Matahari service binaries\\\"\\n"
 					   " Some comment\\n  ExecWait 'del \\\"$INSTDIR\\\\mh_hostd.exe\\\"'\\n"
+					   " Some comment\\n  ExecWait 'del \\\"$INSTDIR\\\\mh_pmd.exe\\\"'\\n"
 					   " Some comment\\n  ExecWait 'del \\\"$INSTDIR\\\\mh_netd.exe\\\"'\\n"
 					   " Some comment\\n  ExecWait 'del \\\"$INSTDIR\\\\mh_serviced.exe\\\"'\\n"
 					   )
diff --git a/matahari.spec b/matahari.spec
index a52b1d7..f0e8ffa 100644
--- a/matahari.spec
+++ b/matahari.spec
@@ -107,6 +107,17 @@ Requires:	%{name}-agent-lib = %{version}-%{release}
 %description host
 QMF agent for viewing and controlling remote hosts
 
+%package pm
+License:	GPLv2+
+Summary:	QMF agent for PM
+Group:		Applications/System
+Requires:	%{name}-lib = %{version}-%{release}
+Requires:	%{name}-agent-lib = %{version}-%{release}
+Requires:	tuned
+
+%description pm
+QMF agent for viewing and controlling power management settings
+
 %package network
 License:	GPLv2+
 Summary:	QMF agent for network devices  
@@ -202,6 +213,22 @@ if [ "$1" -ge "1" ]; then
     /sbin/service matahari-host condrestart >/dev/null 2>&1 || :
 fi
 
+#== PM
+
+%post pm
+/sbin/service matahari-pm condrestart
+
+%preun pm
+if [ $1 = 0 ]; then
+   /sbin/service matahari-pm stop >/dev/null 2>&1 || :
+   chkconfig --del matahari-pm
+fi
+
+%postun pm
+if [ "$1" -ge "1" ]; then
+    /sbin/service matahari-pm condrestart >/dev/null 2>&1 || :
+fi
+
 #== Network
 
 %post network
@@ -297,6 +324,7 @@ test "x%{buildroot}" != "x" && rm -rf %{buildroot}
 %defattr(644, root, root, 755)
 %{_libdir}/libmcommon.so.*
 %{_libdir}/libmhost.so.*
+%{_libdir}/libmpm.so.*
 %{_libdir}/libmnetwork.so.*
 %{_libdir}/libmservice.so.*
 %{_libdir}/libmconfig.so.*
@@ -328,6 +356,19 @@ test "x%{buildroot}" != "x" && rm -rf %{buildroot}
 %attr(755, root, root) %{_sbindir}/matahari-dbus-hostd
 %endif
 
+%files pm
+%defattr(644, root, root, 755)
+%doc AUTHORS COPYING
+
+%if %{with qmf}
+%attr(755, root, root) %{_initddir}/matahari-pm
+%attr(755, root, root) %{_sbindir}/matahari-qmf-pmd
+%endif
+
+%if %{with dbus}
+%attr(755, root, root) %{_sbindir}/matahari-dbus-pmd
+%endif
+
 %files service
 %defattr(644, root, root, 755)
 %doc AUTHORS COPYING
@@ -372,15 +413,19 @@ test "x%{buildroot}" != "x" && rm -rf %{buildroot}
 %if %{with dbus}
 %files dbus
 %{_sysconfdir}/dbus-1/system.d/org.matahariproject.Host.conf
+%{_sysconfdir}/dbus-1/system.d/org.matahariproject.PM.conf
 %{_sysconfdir}/dbus-1/system.d/org.matahariproject.Network.conf
 %{_sysconfdir}/dbus-1/system.d/org.matahariproject.Services.conf
 %{_datadir}/dbus-1/interfaces/org.matahariproject.Host.xml
+%{_datadir}/dbus-1/interfaces/org.matahariproject.PM.xml
 %{_datadir}/dbus-1/interfaces/org.matahariproject.Network.xml
 %{_datadir}/dbus-1/interfaces/org.matahariproject.Services.xml
 %{_datadir}/dbus-1/system-services/org.matahariproject.Host.service
+%{_datadir}/dbus-1/system-services/org.matahariproject.PM.service
 %{_datadir}/dbus-1/system-services/org.matahariproject.Network.service
 %{_datadir}/dbus-1/system-services/org.matahariproject.Services.service
 %{_datadir}/polkit-1/actions/org.matahariproject.Host.policy
+%{_datadir}/polkit-1/actions/org.matahariproject.PM.policy
 %{_datadir}/polkit-1/actions/org.matahariproject.Network.policy
 %{_datadir}/polkit-1/actions/org.matahariproject.Services.policy
 %endif
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index d3fd72f..e238ed1 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -131,6 +131,7 @@ endif(WITH-QMF)
 ### Subdirectories
 add_subdirectory(lib)
 add_subdirectory(host)
+add_subdirectory(pm)
 add_subdirectory(network)
 add_subdirectory(service)
 add_subdirectory(config)
diff --git a/src/include/matahari/pm.h b/src/include/matahari/pm.h
new file mode 100644
index 0000000..4857a47
--- /dev/null
+++ b/src/include/matahari/pm.h
@@ -0,0 +1,70 @@
+/* ph.h - Copyright (C) 2011 Red Hat, Inc.
+ * Written by Jaroslav Škarvada <jskarvad@redhat.com>
+ * Based on code from Arjun Roy <arroy@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/**
+ * \file
+ * \brief PM API
+ * \ingroup coreapi
+ */
+
+#ifndef __MH_PM_H__
+#define __MH_PM_H__
+
+#include <stdint.h>
+#include <stdlib.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 STR_UNK   "UNKNOWN"
+#define STR_ERR   "ERROR"
+
+enum exec_exitcode {
+    EC_OK = 0,
+    EC_UNKNOWN_ERROR = 1,
+    EC_INVALID_PARAM = 2,
+    EC_UNIMPLEMENT_FEATURE = 3,
+    EC_INSUFFICIENT_PRIV = 4,
+    EC_NOT_INSTALLED = 5,
+
+    /* 150-199<>reserved for application use */
+    EC_SIGNAL        = 194,
+    EC_NOT_SUPPORTED = 195,
+    EC_PENDING       = 196,
+    EC_CANCELLED     = 197,
+    EC_TIMEOUT       = 198,
+    EC_OTHER_ERROR   = 199, /* Keep the same codes as LSB */
+};
+
+extern gboolean mh_pm_set_profile(const char *profile);
+extern const char *mh_pm_get_profile(void);
+extern GList *mh_pm_list_profiles(void);
+
+#endif // __MH_PM_H__
diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt
index d33057b..fc48c74 100644
--- a/src/lib/CMakeLists.txt
+++ b/src/lib/CMakeLists.txt
@@ -1,6 +1,6 @@
 # Library
 
-set(LIBS mcommon mhost mnetwork mservice mconfig)
+set(LIBS mcommon mhost mnetwork mpm mservice mconfig)
 include_directories(${pcre_INCLUDE_DIRS})
 
 # See http://sourceware.org/autobook/autobook/autobook_91.html for
@@ -18,6 +18,10 @@ add_library (mnetwork SHARED network.c  network_${VARIANT}.c)
 set_target_properties(mnetwork PROPERTIES SOVERSION 1.0.0)
 target_link_libraries(mnetwork ${pcre_LIBRARIES} mcommon ${SIGAR} ${glib_LIBRARIES})
 
+add_library (mpm SHARED pm.c pm_${VARIANT}.c)
+set_target_properties(mpm PROPERTIES SOVERSION 1.0.0)
+target_link_libraries(mpm ${pcre_LIBRARIES} mcommon ${SIGAR} ${glib_LIBRARIES})
+
 add_library (mservice SHARED services.c services_${VARIANT}.c)
 set_target_properties(mservice PROPERTIES SOVERSION 1.0.0)
 target_link_libraries(mservice ${pcre_LIBRARIES} mcommon ${SIGAR} ${glib_LIBRARIES})
diff --git a/src/lib/pm.c b/src/lib/pm.c
new file mode 100644
index 0000000..a413c94
--- /dev/null
+++ b/src/lib/pm.c
@@ -0,0 +1,45 @@
+/* pm.c - Copyright (C) 2011 Red Hat, Inc.
+ * Written by Jaroslav Škarvada <andrew@beekhof.net>
+ * Based on code from Andrew Beekhof <andrew@beekhof.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef WIN32
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <glib.h>
+#include "matahari/pm.h"
+#include "matahari/logging.h"
+#include "pm_private.h"
+
+MH_TRACE_INIT_DATA(mh_pm);
+
+gboolean mh_pm_set_profile(const char* profile)
+{
+  return pm_set_profile(profile);
+}
+
+const char *mh_pm_get_profile(void)
+{
+  return pm_get_profile();
+}
+
+GList *mh_pm_list_profiles(void)
+{
+  return pm_list_profiles();
+}
diff --git a/src/lib/pm_linux.c b/src/lib/pm_linux.c
new file mode 100644
index 0000000..db8821e
--- /dev/null
+++ b/src/lib/pm_linux.c
@@ -0,0 +1,335 @@
+/* pm_linux.c - Copyright (C) 2011 Red Hat, Inc.
+ * Written by Jaroslav Škarvada <jskarvad@redhat.com>
+ * Based on code from Andrew Beekhof <andrew@beekhof.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/wait.h>
+
+#include "matahari/logging.h"
+#include "matahari/pm.h"
+#include "host_private.h"
+#include "sigar.h"
+
+static gboolean read_output(int fd, char **data)
+{
+    int rc = 0, len = 0;
+    char buf[500];
+    static const size_t bufsize = sizeof(buf);
+
+    if (*data)
+      len = strlen(*data);
+
+    do {
+        rc = read(fd, buf, bufsize - 1);
+        if (rc > 0) {
+            buf[rc] = 0;
+            *data = realloc(*data, len + rc + 1);
+            snprintf(*data + len, bufsize, "%s", buf);
+            len += rc;
+
+        } else if (errno != EINTR)
+            return FALSE;
+    } while (rc == bufsize - 1 || rc < 0);
+
+    return TRUE;
+}
+
+static int exec_command(const char *command, int atimeout, char **stdoutbuf,
+                        char **stderrbuf, char *const argv[])
+{
+    pid_t pid;
+    int fd, rc = 0;
+    int stdout_fd[2];
+    int stderr_fd[2];
+
+    if (! command)
+      return EC_OTHER_ERROR;
+
+    if (pipe(stdout_fd) < 0) {
+        mh_perror(LOG_ERR, "pipe() failed");
+    }
+    if (pipe(stderr_fd) < 0) {
+        mh_perror(LOG_ERR, "pipe() failed");
+    }
+    pid = fork();
+
+    switch (pid) {
+    case -1:
+        mh_perror(LOG_ERR, "fork() failed");
+        close(stdout_fd[0]);
+        close(stdout_fd[1]);
+        close(stderr_fd[0]);
+        close(stderr_fd[1]);
+        return EC_OTHER_ERROR;
+    /* Child. */
+    case 0:
+        setpgid(0, 0);
+        close(stdout_fd[0]);
+        close(stderr_fd[0]);
+        if (STDOUT_FILENO != stdout_fd[1]) {
+            if (dup2(stdout_fd[1], STDOUT_FILENO) != STDOUT_FILENO) {
+                mh_perror(LOG_ERR, "dup2() failed (stdout)");
+            }
+            close(stdout_fd[1]);
+        }
+        if (STDERR_FILENO != stderr_fd[1]) {
+            if (dup2(stderr_fd[1], STDERR_FILENO) != STDERR_FILENO) {
+                mh_perror(LOG_ERR, "dup2() failed (stderr)");
+            }
+            close(stderr_fd[1]);
+        }
+
+        /* close all descriptors except stdin/out/err and channels to logd */
+        for (fd = getdtablesize() - 1; fd > STDERR_FILENO; fd--)
+            close(fd);
+
+        /* execute the command */
+        execvp(command, argv);
+
+        switch (errno) { /* see execvp */
+        case ENOENT:  /* No such file or directory */
+        case EISDIR:   /* Is a directory */
+            rc = EC_NOT_INSTALLED;
+            break;
+        case EACCES:   /* permission denied (various errors) */
+            rc = EC_INSUFFICIENT_PRIV;
+            break;
+        default:
+            rc = EC_UNKNOWN_ERROR;
+            break;
+        }
+        exit(rc);
+    }
+    /* Only the parent reaches here */
+    close(stdout_fd[1]);
+    close(stderr_fd[1]);
+
+    int status = 0;
+    int timeout = atimeout;
+    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);
+
+        rc = EC_TIMEOUT;
+        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)) {
+        rc = WEXITSTATUS(status);
+        mh_err("Managed process %d exited with rc=%d", pid, rc);
+
+    } else if (WIFSIGNALED(status)) {
+        int signo = WTERMSIG(status);
+        rc = EC_SIGNAL;
+        mh_err("Managed process %d exited with signal=%d", pid, signo);
+    }
+#ifdef WCOREDUMP
+    if (WCOREDUMP(status)) {
+        rc = EC_OTHER_ERROR;
+        mh_err("Managed process %d dumped core", pid);
+    }
+#endif
+    mh_trace("Child done: %d", pid);
+
+    read_output(stdout_fd[0], stdoutbuf);
+    read_output(stderr_fd[0], stderrbuf);
+
+    close(stdout_fd[0]);
+    close(stdout_fd[0]);
+    return rc;
+}
+
+
+GList *pm_list_profiles(void)
+{
+    char *stdoutbuf = NULL;
+    char *stderrbuf = NULL;
+    char *c1, *c2;
+    char *args[3] = {0};
+    int cnt = 0;
+    GList *list = NULL;
+    gboolean rc;
+
+    args[0] = TUNEDADM;
+    args[1] = TA_LISTPROFILES;
+    args[2] = NULL;
+
+    rc = exec_command(args[0], TIMEOUT, &stdoutbuf, &stderrbuf, args) == EC_OK;
+
+    if (stdoutbuf) {
+        mh_trace(" >  stdout: %s", stdoutbuf);
+    }
+    if (stderrbuf) {
+        mh_trace(" >  stderr: %s", stderrbuf);
+        free(stderrbuf);
+    }
+
+    if (rc && stdoutbuf)
+    {
+        c1 = stdoutbuf;
+        do
+        {
+            if (*c1 == '-')
+            {
+                c2 = strchr(c1++, '\n');
+                if (c2 > c1)
+                {
+                    *c2 = 0;
+                    list = g_list_append(list, strdup(++c1));
+                    *c2 = '\n';
+                    cnt++;
+                }
+            }
+            c2 = strchr(c1, '\n');
+            if (c2)
+                c1 = ++c2;
+        }
+        while (c2 && c1 - stdoutbuf < strlen(stdoutbuf));
+
+        if (cnt)
+          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 = pm_list_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;
+        free(llist->data);
+    }
+    g_list_free(list);
+
+    return rc;
+}
+
+gboolean pm_set_profile(const char *profile)
+{
+    char *stdoutbuf = NULL;
+    char *stderrbuf = NULL;
+    char *args[4] = {0};
+    gboolean rc;
+
+    args[0] = TUNEDADM;
+    if (! profile)
+        return FALSE;
+
+    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 FALSE;
+        }
+        args[1] = TA_SETPROFILE;
+        mh_trace("setting profile: %s", profile);
+    }
+
+    args[2] = strdup(profile);
+    args[3] = NULL;
+
+    rc = exec_command(args[0], TIMEOUT, &stdoutbuf, &stderrbuf, args) == EC_OK;
+    free(args[2]);
+
+    if (stdoutbuf) {
+        mh_trace(" >  stdout: %s", stdoutbuf);
+        free(stdoutbuf);
+    }
+    if (stderrbuf) {
+        mh_trace(" >  stderr: %s", stderrbuf);
+        free(stderrbuf);
+    }
+
+    return rc;
+}
+
+const char *pm_get_profile(void)
+{
+    char *stdoutbuf = NULL;
+    char *stderrbuf = NULL;
+    char *rbuf = NULL;
+    char *c1, *c2;
+    char *args[3] = {0};
+    gboolean rc;
+
+    args[0] = TUNEDADM;
+    args[1] = TA_GETPROFILE;
+    args[2] = NULL;
+
+    rc = exec_command(args[0], TIMEOUT, &stdoutbuf, &stderrbuf, args) == EC_OK;
+
+    if (stdoutbuf) {
+        mh_trace(" >  stdout: %s", stdoutbuf);
+    }
+    if (stderrbuf) {
+        mh_trace(" >  stderr: %s", stderrbuf);
+        free(stderrbuf);
+    }
+
+    if (rc)
+    {
+        if (stdoutbuf && strlen(stdoutbuf) && (c1 = strchr(stdoutbuf, ':')) &&
+            (++c1 - stdoutbuf < strlen(stdoutbuf)))
+        {
+            if ((c2 = strchr(stdoutbuf, '\n')))
+                *c2 = 0;
+            rbuf = strdup(c1);
+        }
+        else
+            rbuf = strdup(STR_UNK);
+    }
+    else
+        rbuf = strdup(STR_ERR);
+
+    free(stdoutbuf);
+
+    return rbuf;
+}
diff --git a/src/lib/pm_private.h b/src/lib/pm_private.h
new file mode 100644
index 0000000..019f605
--- /dev/null
+++ b/src/lib/pm_private.h
@@ -0,0 +1,39 @@
+/* pm_private.h - Copyright (C) 2011 Red Hat, Inc.
+ * Written by Jaroslav Škarvada <jskarvad@redhat.com>
+ * Based on code from Darryl L. Pierce <dpierce@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/**
+ * \file
+ * \brief Platform specific function prototypes.
+ *
+ * The functions in this header must be implemented by the platform
+ * specific pm data code.
+ *  - pm_linux.c
+ *  - pm_windows.c
+ */
+
+#ifndef __MH_PM_PRIVATE_H__
+#define __MH_PM_PRIVATE_H__
+
+#include <glib.h>
+
+extern gboolean pm_set_profile(const char *profile);
+extern const char *pm_get_profile(void);
+extern GList *pm_list_profiles(void);
+
+#endif /* __MH_PM_PRIVATE_H__ */
diff --git a/src/lib/pm_windows.c b/src/lib/pm_windows.c
new file mode 100644
index 0000000..7725f4c
--- /dev/null
+++ b/src/lib/pm_windows.c
@@ -0,0 +1,301 @@
+/* pm_windows.c - Copyright (C) 2011 Red Hat, Inc.
+ * Written by Jaroslav Škarvada <jskarvad@redhat.com>
+ * Based on code from Andrew Beekhof <andrew@beekhof.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* Some code adapted from http://msdn.microsoft.com/en-us/library/ms682499(v=vs.85).aspx */
+
+#include <winsock.h>
+#include <windows.h>
+#include <winbase.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+
+#include "matahari/logging.h"
+#include "matahari/pm.h"
+#include "host_private.h"
+
+#define BUFSIZE 4096
+
+#define malloc_usable_size(size) _msize(size)
+
+static void
+read_output(HANDLE h, char **output, int *len)
+{
+    /* Read output from the child process's pipe for STDOUT
+     * Stop when there is no more data.
+     */
+    DWORD bytes = 0;
+    char buf[BUFSIZE];
+
+    *len = 0;
+    while (ReadFile(h, buf, BUFSIZE - 1, &bytes, NULL) && bytes > 0 &&
+           (*output = realloc(*output, *len + (int) bytes + 1)))
+    {
+        buf[bytes] = 0;
+        mh_info("Read %d: %.*s", *len, (int) bytes, buf);
+        sprintf(*output + *len, "%s", buf);
+        *len += (int)bytes;
+    }
+
+    mh_info("Read %d bytes", len);
+}
+
+static
+int exec_command(char *apath, char *acommand, int atimeout,
+                 char **stdoutbuf)
+{
+    DWORD status = 0;
+    int max = 0;
+    int len;
+    char *bcommand;
+    LPWSTR command;
+    STARTUPINFO siStartInfo;
+    SECURITY_ATTRIBUTES saAttr;
+    PROCESS_INFORMATION piProcInfo;
+    HANDLE child_pipe_rd = NULL;
+    HANDLE child_pipe_wr = NULL;
+
+    if (! acommand)
+        return EC_OTHER_ERROR;
+
+    /* Initialize all structures */
+    ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
+    ZeroMemory(&saAttr, sizeof(SECURITY_ATTRIBUTES));
+    ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
+
+    /* Set the bInheritHandle flag so pipe handles are inherited.  */
+    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+    saAttr.bInheritHandle = TRUE;
+    saAttr.lpSecurityDescriptor = NULL;
+
+    /* Copyrightreate a pipe for the child process's stdout  */
+    if (!CreatePipe(&child_pipe_rd, &child_pipe_wr, &saAttr, 0)) {
+        mh_err("Couldn't create child pipe");
+        return FALSE;
+    }
+
+    /* Ensure the read handle to the pipe for STDOUT is not inherited. */
+    if (!SetHandleInformation(child_pipe_rd, HANDLE_FLAG_INHERIT, 0)) {
+        mh_err("Couldn't set pipe handle information");
+        CloseHandle(child_pipe_rd);
+        CloseHandle(child_pipe_wr);
+        return FALSE;
+    }
+
+    if (apath)
+    {
+        bcommand = malloc((len = strlen(apath) + strlen(acommand) + 2));
+        len--;
+        if (! bcommand)
+        {
+            CloseHandle(child_pipe_rd);
+            CloseHandle(child_pipe_wr);
+            return EC_OTHER_ERROR;
+        }
+        bcommand[len] = 0;
+        strcpy(bcommand, apath);
+        strcat(bcommand, "\\");
+        strcat(bcommand, acommand);
+    }
+    else bcommand = strdup(acommand);
+
+    command = char2wide(bcommand);
+    free(bcommand);
+
+    siStartInfo.cb = sizeof(STARTUPINFO);
+    siStartInfo.hStdError = child_pipe_wr;
+    siStartInfo.hStdOutput = child_pipe_wr;
+    siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
+
+    if (!CreateProcess(NULL,
+                       command,        // command line
+                       NULL,           // process security attributes
+                       NULL,           // primary thread security attributes
+                       TRUE,           // handles are inherited
+                       0,              // creation flags
+                       NULL,           // use parent's environment
+                       NULL,           // use parent's current directory
+                       &siStartInfo,   // STARTUPINFO pointer
+                       &piProcInfo)) { // receives PROCESS_INFORMATION
+        mh_err("Could not create child process");
+        CloseHandle(child_pipe_rd);
+        CloseHandle(child_pipe_wr);
+        free(command);
+        return EC_OTHER_ERROR;
+    }
+
+    WaitForSingleObject(piProcInfo.hProcess, atimeout);
+
+    if (GetExitCodeProcess(piProcInfo.hProcess, &status) == 0) {
+        mh_err("Could not get exit code: %lu", GetLastError());
+        status = EC_PENDING;
+    }
+
+    if (status == EC_PENDING) {
+        status = EC_TIMEOUT;
+        TerminateProcess(piProcInfo.hProcess, 1);
+        mh_err("Operation timeout out after %dms", atimeout);
+
+    }
+    /* Nice to log it somewhere */
+    mh_debug("Result of '%ls' was %d", command, (int)status);
+
+    /* Close the write end of the pipe before reading from the
+     * read end of the pipe, to control child process execution.
+     * The pipe is assumed to have enough buffer space to hold the
+     * data the child process has already written to it.
+     */
+
+    if (!CloseHandle(child_pipe_wr))
+        mh_err("Couldn't close write end of child pipe");
+
+    read_output(child_pipe_rd, stdoutbuf, &max);
+
+    if (*stdoutbuf)
+        mh_debug("RAW: %s", *stdoutbuf);
+
+    CloseHandle(piProcInfo.hProcess);
+    CloseHandle(piProcInfo.hThread);
+    CloseHandle(child_pipe_rd);
+
+    free(command);
+
+    return (int) status;
+}
+
+static
+gboolean get_profiles(GList **names, GList **guids)
+{
+    gboolean rc = FALSE;
+    int len;
+    char *stdoutbuf = NULL;
+    char *c1, *c2;
+
+    if (exec_command(getenv("WINDIR"),
+        SYSTEM32 "\\" POWERCFG " " PC_LISTPROFILES, TIMEOUT * 1000,
+        &stdoutbuf) == EC_OK && stdoutbuf)
+    {
+        rc = TRUE;
+        len = strlen(stdoutbuf);
+        c1 = stdoutbuf;
+
+        while ((c1 = strstr(c1, "GUID: ")))
+        {
+            c1 += 6;
+            if (c1 - stdoutbuf < len && (c2 = strchr(c1, ' ')))
+            {
+                *c2++ = 0;
+                *guids = g_list_append(*guids, strdup(c1));
+                if ((c1 = strchr(c2, '(')) && ++c1 - stdoutbuf < len &&
+                    (c2 = strchr(c1, ')')))
+                {
+                    *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;
+}
+
+#define PMSP_COMMAND SYSTEM32 "\\" POWERCFG " " PC_SETPROFILE " "
+gboolean pm_set_profile(const char *profile)
+{
+    gboolean rc = FALSE;
+    char *guid = NULL;
+    char *stdoutbuf = NULL;
+    char *cmd = NULL;
+    GList *names = NULL;
+    GList *guids = NULL;
+    GList *pnames, *pguids;
+
+    if (! get_profiles(&names, &guids))
+      return FALSE;
+
+    for (pnames = g_list_first(names), pguids = g_list_first(guids);
+         pnames && pguids;
+         pnames = g_list_next(pnames), pguids = g_list_next(pguids))
+    {
+        if (!strcmp((char *)pnames->data, profile))
+            guid = strdup((char *)pguids->data);
+        free(pnames->data);
+        free(pguids->data);
+    }
+    g_list_free(names);
+    g_list_free(guids);
+
+    if (guid && (cmd = malloc(strlen(PMSP_COMMAND) + strlen(guid) + 1)))
+    {
+        strcpy(cmd, PMSP_COMMAND);
+        strcat(cmd, guid);
+        rc = exec_command(getenv("WINDIR"), cmd, TIMEOUT * 1000,
+             &stdoutbuf) == EC_OK;
+        free(cmd);
+        free(stdoutbuf);
+    }
+
+    return rc;
+}
+#undef PMSP_COMMAND
+
+const char *pm_get_profile(void)
+{
+    char *rbuf = NULL;
+    char *stdoutbuf = NULL;
+    char *c1, *c2;
+
+    if (exec_command(getenv("WINDIR"), SYSTEM32 "\\" POWERCFG " " PC_GETPROFILE,
+        TIMEOUT * 1000, &stdoutbuf) == EC_OK && stdoutbuf &&
+        (c1 = strchr(stdoutbuf, '(')) && ++c1 - stdoutbuf < strlen(stdoutbuf) &&
+        (c2 = strchr(c1, ')')))
+    {
+        *c2 = 0;
+        rbuf = strdup(c1);
+    }
+    else rbuf = strdup(STR_ERR);
+
+    free(stdoutbuf);
+
+    return rbuf;
+}
+
+GList *pm_list_profiles(void)
+{
+    GList *list;
+    GList *names = NULL;
+    GList *guids = NULL;
+
+    get_profiles(&names, &guids);
+
+    for (list = g_list_first(guids); list; list = g_list_next(list))
+      free((char *)list->data);
+
+    g_list_free(guids);
+
+    return names;
+}
diff --git a/src/pm/CMakeLists.txt b/src/pm/CMakeLists.txt
new file mode 100644
index 0000000..512f3f4
--- /dev/null
+++ b/src/pm/CMakeLists.txt
@@ -0,0 +1,61 @@
+set(BASE "pm")
+set(BASE_LIB "m${BASE}")
+set(QMF_AGENT "matahari-qmf-${BASE}d")
+set(DBUS_AGENT "matahari-dbus-${BASE}d")
+
+# QMF daemon
+if(WITH-QMF)
+    set(SCHEMA_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/qmf/org/matahariproject/QmfPackage.cpp)
+    generate_qmf_schemas(${CMAKE_CURRENT_SOURCE_DIR}/schema.xml ${SCHEMA_SOURCES})
+    include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+    add_executable(${QMF_AGENT} ${BASE}-qmf.cpp ${SCHEMA_SOURCES})
+    target_link_libraries(${QMF_AGENT} ${BASE_LIB} mcommon mcommon_qmf)
+
+    target_link_libraries(${QMF_AGENT} ${QPIDCOMMON_LIBRARY} ${QPIDCLIENT_LIBRARY} ${QPIDMESSAGING_LIBRARY} ${QMF2_LIBRARY})
+    if(QPIDTYPES_LIBRARY)
+        target_link_libraries(${QMF_AGENT} ${QPIDTYPES_LIBRARY})
+    endif(QPIDTYPES_LIBRARY)
+
+    if(WIN32)
+        target_link_libraries(${QMF_AGENT} wsock32)
+    endif(WIN32)
+
+    if(NOT WIN32)
+        configure_file(${CMAKE_SOURCE_DIR}/matahari.init.in ${CMAKE_CURRENT_BINARY_DIR}/matahari-${BASE})
+        install(FILES ${CMAKE_CURRENT_BINARY_DIR}/matahari-${BASE} DESTINATION ${initdir})
+    endif(NOT WIN32)
+
+    install(TARGETS ${QMF_AGENT} DESTINATION sbin)
+endif(WITH-QMF)
+
+
+# DBus daemon
+if(WITH-DBUS)
+    # Auto-generated stuff
+    generate_dbus_headers(${BASE} ${CMAKE_CURRENT_BINARY_DIR}/org.matahariproject.PM.xml)
+    # Create src/dbus directory and generate DBus XML definition files into it
+    generate_dbus_interface(${CMAKE_CURRENT_SOURCE_DIR}/schema.xml)
+
+    # Must be included to find matahari-API-dbus-properties.h
+    include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+    set(DBUS_AGENT_SOURCE
+        ${BASE}-dbus.c
+        ${BASE}-dbus-glue.h
+        ${BASE}-dbus-properties.h
+    )
+
+    add_executable(${DBUS_AGENT} ${DBUS_AGENT_SOURCE})
+
+    target_link_libraries(${DBUS_AGENT} ${BASE_LIB} mcommon_dbus ${dbus-glib_LIBRARIES})
+    include_directories(${dbus-glib_INCLUDE_DIRS})
+
+    # Install targets
+    # TODO: fix hardcoded paths, should go to libexec
+    install(TARGETS ${DBUS_AGENT} DESTINATION sbin)
+    install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.matahariproject.PM.xml DESTINATION share/dbus-1/interfaces)
+    install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/org.matahariproject.PM.service DESTINATION share/dbus-1/system-services)
+    install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/org.matahariproject.PM.conf DESTINATION /etc/dbus-1/system.d)
+    install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/org.matahariproject.PM.policy DESTINATION share/polkit-1/actions)
+endif(WITH-DBUS)
diff --git a/src/pm/org.matahariproject.PM.conf b/src/pm/org.matahariproject.PM.conf
new file mode 100644
index 0000000..1afe8a8
--- /dev/null
+++ b/src/pm/org.matahariproject.PM.conf
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE busconfig PUBLIC
+ "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+
+  <!-- Only root can own the service -->
+  <policy user="root">
+    <allow own="org.matahariproject.PM"/>
+  </policy>
+
+  <!-- Allow anyone to invoke methods on the interfaces -->
+  <policy context="default">
+    <allow send_destination="org.matahariproject.PM"/>
+  </policy>
+
+</busconfig>
diff --git a/src/pm/org.matahariproject.PM.policy b/src/pm/org.matahariproject.PM.policy
new file mode 100644
index 0000000..eb2c617
--- /dev/null
+++ b/src/pm/org.matahariproject.PM.policy
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN" "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
+<policyconfig>
+  <vendor>Matahari</vendor>
+  <vendor_url>https://fedorahosted.org/matahari/</vendor_url>
+  <action id="org.matahariproject.PM.set_profile">
+    <defaults>
+      <allow_any>no</allow_any>
+      <allow_inactive>no</allow_inactive>
+      <allow_active>auth_admin</allow_active>
+    </defaults>
+  </action>
+  <action id="org.matahariproject.PM.get_profile">
+    <defaults>
+      <allow_any>no</allow_any>
+      <allow_inactive>no</allow_inactive>
+      <allow_active>auth_admin</allow_active>
+    </defaults>
+  </action>
+  <action id="org.matahariproject.PM.list_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/pm/org.matahariproject.PM.service b/src/pm/org.matahariproject.PM.service
new file mode 100644
index 0000000..f293a71
--- /dev/null
+++ b/src/pm/org.matahariproject.PM.service
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=org.matahariproject.PM
+Exec=/usr/sbin/matahari-dbus-pmd
+User=root
diff --git a/src/pm/pm-dbus.c b/src/pm/pm-dbus.c
new file mode 100644
index 0000000..e7fbf10
--- /dev/null
+++ b/src/pm/pm-dbus.c
@@ -0,0 +1,148 @@
+/*
+ * matahari-pm-dbus.c
+ *
+ * Copyright (C) 2011 Red Hat, Inc.
+ * Written by Jaroslav Škarvada <jskarvad@redhat.com>
+ * Based on code from Roman Rakus <rrakus@redhat.com>,
+ *                    Radek Novacek <rnovacek@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#include <string.h>
+
+#include "matahari/mh_dbus_common.h"
+#include "matahari/utilities.h"
+
+/* PM methods */
+#include "matahari/pm.h"
+
+/* Generated properties list */
+#include "pm-dbus-properties.h"
+
+/* DBus names */
+#define PM_BUS_NAME "org.matahariproject.PM"
+#define PM_OBJECT_PATH "/org/matahariproject/PM"
+#define PM_INTERFACE_NAME "org.matahariproject.PM"
+#define DBUS_PROPERTY_INTERAFACE_NAME "org.freedesktop.DBus.Properties"
+
+/* Dbus methods */
+gboolean
+PM_set_profile(Matahari* matahari, const char *profile, DBusGMethodInvocation *context)
+{
+    GError* error = NULL;
+    if (!check_authorization(PM_BUS_NAME ".set_profile", &error, context)) {
+        dbus_g_method_return_error(context, error);
+        return FALSE;
+    }
+    dbus_g_method_return(context, mh_pm_set_profile(profile));
+    return TRUE;
+}
+
+gboolean
+PM_get_profile(Matahari* matahari, DBusGMethodInvocation *context)
+{
+    char *profile;
+    GError* error = NULL;
+    if (!check_authorization(PM_BUS_NAME ".get_profile", &error, context)) {
+        dbus_g_method_return_error(context, error);
+        return FALSE;
+    }
+    profile = (char *)mh_pm_get_profile();
+    dbus_g_method_return(context, g_strdup(profile));
+    free(profile);
+    return TRUE;
+}
+
+gboolean
+PM_list_profiles(Matahari* matahari, DBusGMethodInvocation *context)
+{
+    GError* error = NULL;
+    GList *list;
+    GList *plist;
+    char **profiles;
+    int i = 0;
+
+    if (!check_authorization(PM_BUS_NAME ".list_profiles", &error, context)) {
+        dbus_g_method_return_error(context, error);
+        return FALSE;
+    }
+    list = mh_pm_list_profiles();
+    profiles = g_new(char *, g_list_length(list) + 1);
+    for (plist = g_list_first(list); plist; plist = g_list_next(plist)) {
+        profiles[i++] = g_strdup((char *)plist->data);
+        free(plist->data);
+    }
+    profiles[i] = NULL;
+    dbus_g_method_return(context, profiles);
+    g_strfreev(profiles);
+    g_list_free(list);
+    return TRUE;
+}
+
+/* Generated dbus stuff for pm
+ * MUST be after declaration of user defined functions.
+ */
+#include "pm-dbus-glue.h"
+
+void
+matahari_set_property(GObject *object, guint property_id, const GValue *value,
+                      GParamSpec *pspec)
+{
+    switch (property_id) {
+    default:
+        /* We don't have any other property... */
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+        break;
+    }
+}
+
+void
+matahari_get_property(GObject *object, guint property_id, GValue *value,
+                      GParamSpec *pspec)
+{
+    char *str;
+
+    switch (property_id) {
+    case PROP_UUID:
+        g_value_set_string(value, str = (char *)mh_uuid());
+        free(str);
+        break;
+    case PROP_HOSTNAME:
+        g_value_set_string(value, str = (char *)mh_hostname());
+        free(str);
+        break;
+    default:
+        /* We don't have any other property... */
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+        break;
+    }
+}
+
+GType
+matahari_dict_type(int prop)
+{
+    g_printerr("Type of property %s is map of unknown types\n",
+               properties[prop].name);
+    return G_TYPE_VALUE;
+}
+
+int
+main(int argc, char** argv)
+{
+    g_type_init();
+    return run_dbus_server(PM_BUS_NAME, PM_OBJECT_PATH);
+}
diff --git a/src/pm/pm-qmf.cpp b/src/pm/pm-qmf.cpp
new file mode 100644
index 0000000..e070d41
--- /dev/null
+++ b/src/pm/pm-qmf.cpp
@@ -0,0 +1,116 @@
+/* matahari-pm.cpp - Copyright (C) 2011 Red Hat, Inc.
+ * Written by Jaroslav Škarvada <jskarvad@redhat.com>
+ * Based on code from Roman Rakus <rrakus@redhat.com>,
+ *                    Radek Novacek <rnovacek@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef WIN32
+#include "config.h"
+#endif
+
+#include "matahari/mh_agent.h"
+#include <qmf/Data.h>
+#include "qmf/org/matahariproject/QmfPackage.h"
+
+extern "C" {
+#include <string.h>
+#include "matahari/pm.h"
+#include "matahari/logging.h"
+#include "matahari/utilities.h"
+}
+
+class PMAgent : public MatahariAgent
+{
+private:
+    qmf::org::matahariproject::PackageDefinition _package;
+public:
+    virtual int setup(qmf::AgentSession session);
+    virtual gboolean invoke(qmf::AgentSession session, qmf::AgentEvent event,
+                            gpointer user_data);
+};
+
+int
+main(int argc, char **argv)
+{
+    PMAgent *agent = new PMAgent();
+    int rc = agent->init(argc, argv, "pm");
+    if (rc == 0) {
+        agent->run();
+    }
+
+    return rc;
+}
+
+gboolean
+PMAgent::invoke(qmf::AgentSession session, qmf::AgentEvent event,
+                  gpointer user_data)
+{
+    if (event.getType() != qmf::AGENT_METHOD) {
+        session.methodSuccess(event);
+        return TRUE;
+    }
+
+    const std::string& methodName(event.getMethodName());
+
+    if (methodName == "set_profile") {
+        event.addReturnArgument("status",
+            mh_pm_set_profile(event.getArguments()["profile"].asString().c_str()));
+    } else if (methodName == "get_profile") {
+        _qtype::Variant profile;
+        char *pprofile = (char *)mh_pm_get_profile();
+        profile = pprofile;
+        free(pprofile);
+        event.addReturnArgument("profile", profile);
+    } else if (methodName == "list_profiles") {
+        GList *plist = NULL;
+        GList *profiles = mh_pm_list_profiles();
+        _qtype::Variant::List s_list;
+        for (plist = g_list_first(profiles); plist;
+             plist = g_list_next(plist))
+        {
+            s_list.push_back((char *)plist->data);
+            free(plist->data);
+        }
+        g_list_free(profiles);
+        event.addReturnArgument("profiles", s_list);
+    } else {
+        session.raiseException(event, MH_NOT_IMPLEMENTED);
+        goto bail;
+    }
+
+    session.methodSuccess(event);
+
+bail:
+    return TRUE;
+}
+
+int
+PMAgent::setup(qmf::AgentSession session)
+{
+    char *str;
+
+    _package.configure(session);
+    _instance = qmf::Data(_package.data_PM);
+
+    _instance.setProperty("hostname", str = (char *)mh_hostname());
+    free(str);
+    _instance.setProperty("uuid", str = (char *)mh_uuid());
+    free(str);
+
+    session.addData(_instance);
+    return 0;
+}
diff --git a/src/pm/schema.xml b/src/pm/schema.xml
new file mode 100644
index 0000000..c2e580b
--- /dev/null
+++ b/src/pm/schema.xml
@@ -0,0 +1,21 @@
+<schema package="org.matahariproject">
+
+<!-- PM API -->
+  <class name="PM">
+   <property name="uuid"             type="sstr"  access="RO" desc="Host UUID" />
+   <property name="hostname"         type="sstr"  access="RO" desc="Hostname" index="y" />
+
+   <!-- APIs -->
+    <method name="set_profile"   desc="Set PM profile">
+      <arg name="profile" dir="I" type="sstr" />
+      <arg name="status"  dir="O" type="uint32" />
+    </method>
+    <method name="get_profile"   desc="Get current PM profile">
+      <arg name="profile" dir="O" type="sstr" />
+    </method>
+    <method name="list_profiles" desc="List available PM profiles">
+      <arg name="profiles" dir="O" type="list" />
+    </method>
+  </class>
+
+</schema>
diff --git a/src/schema.xml b/src/schema.xml
index d39c82d..1645bf4 100644
--- a/src/schema.xml
+++ b/src/schema.xml
@@ -58,6 +58,24 @@
 
  <event name="heartbeat" args="timestamp,sequence" />
 
+<!-- PM API -->
+  <class name="PM">
+   <property name="uuid"             type="sstr"  access="RO" desc="Host UUID" />
+   <property name="hostname"         type="sstr"  access="RO" desc="Hostname" index="y" />
+
+   <!-- APIs -->
+    <method name="set_profile"   desc="Set PM profile">
+      <arg name="profile" dir="I" type="sstr" />
+      <arg name="status"  dir="O" type="uint32" />
+    </method>
+    <method name="get_profile"   desc="Get current PM profile">
+      <arg name="profile" dir="O" type="sstr" />
+    </method>
+    <method name="list_profiles" desc="List available PM profiles">
+      <arg name="profiles" dir="O" type="list" />
+    </method>
+  </class>
+
  <!-- Network API -->
   <class name="Network">
    <property name="uuid"             type="sstr"  access="RO" desc="Host UUID" />
diff --git a/src/windows/install.bat b/src/windows/install.bat
index 5e6307e..5a47775 100644
--- a/src/windows/install.bat
+++ b/src/windows/install.bat
@@ -12,7 +12,7 @@ cd "%target%"
 
 rem Now install the agents as services and start them
 
-set agents=hostd networkd serviced
+set agents=hostd pmd networkd serviced
 for %%A in (%agents%) do sc delete mh_%%A
 for %%A in (%agents%) do del mh_%%A.exe
 for %%A in (%agents%) do copy srvany.exe mh_%%A.exe
-- 
1.7.5.2

