Thanks for your comments. I tried to incorporate all of them. In the previous version of patch a lot of code was taken from the other agents, so it should be fixed there as well.
I also rewrote the Win & Lin exec funcs to be more consistent. Now it could be probably merged into one code, put into utilities.c and reused in other agents (e.g. services).
I also tried to fix some minor bugs (and hopefully didn't introduced new ones :) The functionality was re-tested again on Win & Lin
thanks & regards
Jaroslav --- CMakeLists.txt | 6 + matahari.spec | 45 ++++++ src/CMakeLists.txt | 1 + src/include/matahari/pm.h | 70 +++++++++ src/lib/CMakeLists.txt | 10 +- src/lib/pm.c | 45 ++++++ src/lib/pm_linux.c | 259 +++++++++++++++++++++++++++++++++ src/lib/pm_private.h | 39 +++++ src/lib/pm_windows.c | 241 ++++++++++++++++++++++++++++++ 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 | 142 ++++++++++++++++++ src/pm/pm-qmf.cpp | 107 ++++++++++++++ src/pm/schema.xml | 21 +++ src/schema.xml | 18 +++ src/windows/install.bat | 2 +- 18 files changed, 1112 insertions(+), 4 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..328b02e 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" ) @@ -105,6 +108,9 @@ if(WIN32) install(FILES ${SRVANY_EXE} DESTINATION sbin) install(FILES src/windows/install.bat DESTINATION sbin)
+ # gspawn-win32-helper-console + file(GLOB_RECURSE GSPAWN_HELPER_CONSOLE_EXE ${MINGW_ROOT}/*/gspawn-win32-helper-console.exe) + install(FILES ${GSPAWN_HELPER_CONSOLE_EXE} DESTINATION sbin) else(WIN32)
set (CPACK_GENERATOR "TGZ") 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 5dda02d..7013e1e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -132,6 +132,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..581dcda --- /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 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 ab21bba..2f71375 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}) @@ -36,8 +40,8 @@ else(WIN32) install(TARGETS ${lib} DESTINATION lib${LIB_SUFFIX}) endforeach (lib ${LIBS}) # libresolv - add_library(mcommon_resolv SHARED dnssrv_linux.c) - target_link_libraries(mcommon mcommon_resolv resolv) +# add_library(mcommon_resolv SHARED dnssrv_linux.c) +# target_link_libraries(mcommon mcommon_resolv resolv) endif(WIN32)
if(WITH-QMF) diff --git a/src/lib/pm.c b/src/lib/pm.c new file mode 100644 index 0000000..3d682cc --- /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); +} + +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..37841a9 --- /dev/null +++ b/src/lib/pm_linux.c @@ -0,0 +1,259 @@ +/* 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 <sigar.h> + +#include "matahari/logging.h" +#include "matahari/pm.h" +#include "host_private.h" + +#define BUFSIZE 4096 + +static void read_output(int fd, char **data) +{ + int bytes = 0, len = 0; + char buf[BUFSIZE]; + + while ((((bytes = read(fd, buf, BUFSIZE - 1)) > 0) && + (*data = realloc(*data, len + bytes + 1))) || + errno == EINTR) + if (bytes > 0) + { + buf[bytes] = 0; + mh_info("Read %d: %.*s", len, bytes, buf); + sprintf(*data + len, "%s", buf); + len += bytes; + } + + mh_info("Read %d bytes", len); +} + +static int exec_command(const char *apath, char *args[], int atimeout, + char **stdoutbuf, char **stderrbuf) +{ + int rc = EC_OK; + GPid pid = 0; + GError *gerr = NULL; + int status = 0; + int timeout = atimeout; + gint stdout_fd; + gint stderr_fd; + + if (!args || !args[0]) + return EC_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 EC_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); + + 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); + } + + mh_trace("Child done: %d", pid); + + read_output(stdout_fd, stdoutbuf); + read_output(stderr_fd, stderrbuf); + + if (*stdoutbuf) + mh_debug("stdout: %s", *stdoutbuf); + if (*stderrbuf) + mh_debug("stderr: %s", *stderrbuf); + + close(stdout_fd); + close(stderr_fd); + + g_spawn_close_pid(pid); + + 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; + + args[0] = TUNEDADM; + args[1] = TA_LISTPROFILES; + args[2] = NULL; + + if (exec_command(NULL, args, TIMEOUT, &stdoutbuf, &stderrbuf) == EC_OK && + 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); + free(stderrbuf); + + 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; + } + g_list_free_full(list, free); + + 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] = (char *)profile; + args[3] = NULL; + + rc = exec_command(NULL, args, TIMEOUT, &stdoutbuf, &stderrbuf) == EC_OK; + + free(stdoutbuf); + free(stderrbuf); + + return rc; +} + +char *pm_get_profile(void) +{ + char *stdoutbuf = NULL; + char *stderrbuf = NULL; + char *rbuf = NULL; + char *c1, *c2; + char *args[3] = {0}; + + args[0] = TUNEDADM; + args[1] = TA_GETPROFILE; + args[2] = NULL; + + if (exec_command(NULL, args, TIMEOUT, &stdoutbuf, &stderrbuf) == EC_OK) + { + 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); + free(stderrbuf); + + return rbuf; +} diff --git a/src/lib/pm_private.h b/src/lib/pm_private.h new file mode 100644 index 0000000..b9532bc --- /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 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..f251886 --- /dev/null +++ b/src/lib/pm_windows.c @@ -0,0 +1,241 @@ +/* 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 <io.h> + +#include "matahari/logging.h" +#include "matahari/pm.h" +#include "host_private.h" + +#define BUFSIZE 4096 + +static void read_output(gint h, char **data) +{ + int len = 0; + int bytes = 0; + char buf[BUFSIZE]; + + while ((bytes = _read(h, buf, BUFSIZE - 1)) > 0 && + (*data = realloc(*data, len + bytes + 1))) + { + buf[bytes] = 0; + mh_info("Read %d: %.*s", len, bytes, buf); + sprintf(*data + len, "%s", buf); + len += bytes; + } + + mh_info("Read %d bytes", len); +} + +static +int exec_command(const char *apath, char *args[], int atimeout, + char **stdoutbuf, char **stderrbuf) +{ + DWORD status = 0; + gint stdout_fd; + gint stderr_fd; + HANDLE phandle = NULL; + GError *gerr = NULL; + + if (!args || !args[0]) + return EC_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 EC_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()); + status = EC_PENDING; + } + + if (status == EC_PENDING) { + status = EC_TIMEOUT; + 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); + + read_output(stdout_fd, stdoutbuf); + read_output(stderr_fd, stderrbuf); + + if (*stdoutbuf) + mh_debug("stdout: %s", *stdoutbuf); + if (*stderrbuf) + mh_debug("stderr: %s", *stderrbuf); + + _close(stdout_fd); + _close(stderr_fd); + + g_spawn_close_pid((GPid *)phandle); + + return (int) status; +} + +static +gboolean get_profiles(GList **names, GList **guids) +{ + gboolean rc = FALSE; + int len; + char *stdoutbuf = NULL; + char *stderrbuf = 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, &stderrbuf) == 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); + free(stderrbuf); + + return rc; +} + +gboolean pm_set_profile(const char *profile) +{ + gboolean rc = FALSE; + char *guid = NULL; + char *stdoutbuf = NULL; + char *stderrbuf = NULL; + GList *names = NULL; + GList *guids = NULL; + GList *pnames = NULL; + GList *pguids = NULL; + char *args[4] = {0}; + + if (! get_profiles(&names, &guids)) + return FALSE; + + 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; + rc = exec_command(getenv("WINDIR"), args, TIMEOUT, + &stdoutbuf, &stderrbuf) == EC_OK; + free(guid); + free(stdoutbuf); + free(stderrbuf); + } + + return rc; +} + +char *pm_get_profile(void) +{ + char *rbuf = NULL; + char *stdoutbuf = NULL; + char *stderrbuf = NULL; + char *c1, *c2; + char *args[3] = {0}; + + args[0] = SYSTEM32 "\" POWERCFG; + args[1] = PC_GETPROFILE; + args[2] = NULL; + if (exec_command(getenv("WINDIR"), args, TIMEOUT, + &stdoutbuf, &stderrbuf) == 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); + free(stderrbuf); + + return rbuf; +} + +GList *pm_list_profiles(void) +{ + GList *names = NULL; + GList *guids = NULL; + + get_profiles(&names, &guids); + g_list_free_full(guids, free); + + 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..8fd325c --- /dev/null +++ b/src/pm/pm-dbus.c @@ -0,0 +1,142 @@ +/* + * 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 = 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); + profiles[i] = NULL; + dbus_g_method_return(context, profiles); + g_strfreev(profiles); + g_list_free_full(list, free); + 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) +{ + switch (property_id) { + case PROP_UUID: + g_value_set_string(value, mh_uuid()); + break; + case PROP_HOSTNAME: + g_value_set_string(value, mh_hostname()); + 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..40084aa --- /dev/null +++ b/src/pm/pm-qmf.cpp @@ -0,0 +1,107 @@ +/* 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) + 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 = 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); + g_list_free_full(profiles, free); + 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) +{ + _package.configure(session); + _instance = qmf::Data(_package.data_PM); + + _instance.setProperty("hostname", mh_hostname()); + _instance.setProperty("uuid", mh_uuid()); + + 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