[Xfce4-commits] [xfce/xfce4-power-manager] 01/01: Add support for suspend/hibernate
noreply at xfce.org
noreply at xfce.org
Fri May 23 14:01:28 CEST 2014
This is an automated email from the git hooks/post-receive script.
eric pushed a commit to branch master
in repository xfce/xfce4-power-manager.
commit 8cc3a47c0265bcfb4627c1f503534dd20bfbffdc
Author: Eric Koegel <eric.koegel at gmail.com>
Date: Fri May 23 14:55:03 2014 +0300
Add support for suspend/hibernate
UPower dropped support for managing power. To continue to support
suspend and hibernate we'll add those functions into xfpm while
borrowing the helper code from xfce4-session.
---
configure.ac.in | 37 +++-
po/POTFILES.in | 2 +
src/Makefile.am | 18 +-
src/xfpm-pm-helper.c | 158 ++++++++++++++
src/xfpm-power.c | 113 +++++++++-
src/xfpm-suspend.c | 592 ++++++++++++++++++++++++++++++++++++++++++++++++++
src/xfpm-suspend.h | 85 ++++++++
7 files changed, 993 insertions(+), 12 deletions(-)
diff --git a/configure.ac.in b/configure.ac.in
index 527f399..2891d07 100644
--- a/configure.ac.in
+++ b/configure.ac.in
@@ -35,9 +35,11 @@ LT_INIT([disable-static])
# Check for headers needed for standard interfaces #
# ==================================================== #
AC_HEADER_STDC()
-AC_CHECK_HEADERS([errno.h signal.h stddef.h \
- string.h sys/stat.h sys/types.h sys/wait.h time.h \
- unistd.h])
+AC_CHECK_HEADERS([errno.h signal.h stddef.h sys/types.h memory.h stdlib.h \
+ string.h sys/stat.h sys/types.h sys/wait.h time.h \
+ unistd.h sys/resource.h sys/socket.h sys/sysctl.h unistd.h])
+
+AC_CHECK_FUNCS([getpwuid setsid sigaction])
# ===================================================== #
# Check for libm #
@@ -157,6 +159,34 @@ fi
AM_CONDITIONAL([BUILD_PANEL_PLUGINS], [test "x$build_panel_plugins" = "xyes"])
#=======================================================#
+# Compile time default choice of backend #
+#=======================================================#
+AC_ARG_WITH([backend],
+ AS_HELP_STRING([--with-backend=<option>],
+ [Default backend to use linux, freebsd, openbsd]))
+# default to a sane option
+AC_CANONICAL_HOST
+if test x$with_backend = x; then
+ AS_CASE([$host],
+ [*-linux*], [with_backend=linux],
+ [*-*freebsd*], [with_backend=freebsd],
+ [*-openbsd*], [with_backend=openbsd])
+fi
+AC_DEFINE_UNQUOTED(BACKEND, "$with_backend", [backend])
+AC_SUBST(BACKEND, "$with_backend")
+
+if test x$with_backend = xlinux; then
+ AC_DEFINE(BACKEND_TYPE_LINUX, 1, [Linux suspend/hibernate backend])
+fi
+if test x$with_backend = xfreebsd; then
+ AC_DEFINE(BACKEND_TYPE_FREEBSD, 1, [FreeBSD suspend/hibernate backend])
+fi
+if test x$with_backend = xopenbsd; then
+ AC_DEFINE(BACKEND_TYPE_OPENBSD, 1, [OpenBSD suspend/hibernate backend])
+fi
+AC_MSG_RESULT([$ac_network_manager])
+
+#=======================================================#
# Check for debugging support #
#=======================================================#
XDT_FEATURE_DEBUG
@@ -212,5 +242,6 @@ echo "POLKIT: ${polkit}"
echo "DPMS: ${enable_dpms}"
echo "Network manager: ${ac_network_manager}"
echo "Build panel plugins: ${build_panel_plugins}"
+echo "Backend: ${with_backend}"
echo "Debug: ${enable_debug}"
echo "Configuration finished, type make to compile"
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 4ac54bf..ea09e91 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -14,6 +14,8 @@ src/xfpm-network-manager.c
src/xfpm-dpms.c
src/xfpm-inhibit.c
src/xfpm-power-info.c
+src/xfpm-pm-helper.c
+src/xfpm-suspend.c
src/xfce4-power-manager.desktop.in
src/org.xfce.power.policy.in2
panel-plugins/brightness/brightness-button.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 9899733..1704c4d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,5 +1,6 @@
bin_PROGRAMS = xfce4-power-manager \
- xfce4-power-information
+ xfce4-power-information \
+ xfce4-pm-helper
xfce4_power_manager_SOURCES = \
$(BUILT_SOURCES) \
@@ -41,7 +42,9 @@ xfce4_power_manager_SOURCES = \
xfpm-polkit.c \
xfpm-polkit.h \
xfpm-errors.c \
- xfpm-errors.h
+ xfpm-errors.h \
+ xfpm-suspend.c \
+ xfpm-suspend.h
xfce4_power_manager_CFLAGS = \
-I$(top_srcdir) \
@@ -50,6 +53,7 @@ xfce4_power_manager_CFLAGS = \
-DLOCALEDIR=\"$(localedir)\" \
-DG_LOG_DOMAIN=\"xfce4-power-manager\" \
-DSYSCONFDIR=\"$(sysconfdir)\" \
+ -DXFPM_SUSPEND_HELPER_CMD=\"$(prefix)/bin/xfce4-pm-helper\" \
$(GOBJECT_CFLAGS) \
$(GTHREAD_CFLAGS) \
$(DBUS_GLIB_CFLAGS) \
@@ -108,6 +112,16 @@ xfce4_power_information_LDADD = \
$(LIBXFCE4UI_LIBS) \
$(top_builddir)/libdbus/libxfpmdbus.la
+xfce4_pm_helper_SOURCES = \
+ xfpm-pm-helper.c
+
+xfce4_pm_helper_CFLAGS = \
+ -I$(top_srcdir) \
+ $(LIBXFCE4UTIL_CFLAGS)
+
+xfce4_pm_helper_LDFLAGS = \
+ $(LIBXFCE4UTIL_LIBS)
+
if ENABLE_POLKIT
sbin_PROGRAMS = xfpm-power-backlight-helper
diff --git a/src/xfpm-pm-helper.c b/src/xfpm-pm-helper.c
new file mode 100644
index 0000000..bbef9a1
--- /dev/null
+++ b/src/xfpm-pm-helper.c
@@ -0,0 +1,158 @@
+/* $Id$ */
+/*-
+ * Copyright (c) 2003-2004 Benedikt Meurer <benny at xfce.org>
+ * All rights reserved.
+ *
+ * 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, 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.
+
+ *
+ * XXX - since this program is executed with root permissions, it may not
+ * be a good idea to trust glib!!
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <glib.h>
+
+/* XXX */
+#ifdef UP_BACKEND_SUSPEND_COMMAND
+#undef UP_BACKEND_SUSPEND_COMMAND
+#endif
+#ifdef UP_BACKEND_HIBERNATE_COMMAND
+#undef UP_BACKEND_HIBERNATE_COMMAND
+#endif
+
+
+#ifdef BACKEND_TYPE_FREEBSD
+#define UP_BACKEND_SUSPEND_COMMAND "/usr/sbin/zzz"
+#define UP_BACKEND_HIBERNATE_COMMAND "/usr/sbin/acpiconf -s 4"
+#endif
+#ifdef BACKEND_TYPE_LINUX
+#define UP_BACKEND_SUSPEND_COMMAND "/usr/sbin/pm-suspend"
+#define UP_BACKEND_HIBERNATE_COMMAND "/usr/sbin/pm-hibernate"
+#endif
+#ifdef BACKEND_TYPE_OPENBSD
+#define UP_BACKEND_SUSPEND_COMMAND "/usr/sbin/zzz"
+#define UP_BACKEND_HIBERNATE_COMMAND "/dev/null"
+#endif
+
+
+static gboolean
+run (const gchar *command)
+{
+#if defined(HAVE_SIGPROCMASK)
+ sigset_t sigset;
+#endif
+ gboolean result;
+ gchar **argv;
+ gchar **envp;
+ GError *err;
+ gint status;
+ gint argc;
+
+#if defined(HAVE_SETSID)
+ setsid ();
+#endif
+
+#if defined (HAVE_SIGPROCMASK)
+ sigemptyset (&sigset);
+ sigaddset (&sigset, SIGHUP);
+ sigaddset (&sigset, SIGINT);
+ sigprocmask (SIG_BLOCK, &sigset, NULL);
+#endif
+
+ result = g_shell_parse_argv (command, &argc, &argv, &err);
+
+ if (result)
+ {
+ envp = g_new0 (gchar *, 1);
+
+ result = g_spawn_sync (NULL, argv, envp,
+ G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL |
+ G_SPAWN_STDERR_TO_DEV_NULL,
+ NULL, NULL, NULL, NULL, &status, &err);
+
+ g_strfreev (envp);
+ g_strfreev (argv);
+ }
+
+ if (!result)
+ {
+ g_error_free (err);
+ return FALSE;
+ }
+
+ return (WIFEXITED (status) && WEXITSTATUS (status) == 0);
+}
+
+
+int
+main (int argc, char **argv)
+{
+ gboolean succeed = FALSE;
+ char action[1024];
+
+ /* display banner */
+ fprintf (stdout, "XFPM_SUDO_DONE ");
+ fflush (stdout);
+
+ if (fgets (action, 1024, stdin) == NULL)
+ {
+ fprintf (stdout, "FAILED\n");
+ return EXIT_FAILURE;
+ }
+
+ if (strncasecmp (action, "SUSPEND", 7) == 0)
+ {
+ succeed = run (UP_BACKEND_SUSPEND_COMMAND);
+ }
+ else if (strncasecmp (action, "HIBERNATE", 9) == 0)
+ {
+ succeed = run (UP_BACKEND_HIBERNATE_COMMAND);
+ }
+
+ if (succeed)
+ {
+ fprintf (stdout, "SUCCEED\n");
+ return EXIT_SUCCESS;
+ }
+
+ fprintf (stdout, "FAILED\n");
+ return EXIT_FAILURE;
+}
diff --git a/src/xfpm-power.c b/src/xfpm-power.c
index 3512ded..05036ad 100644
--- a/src/xfpm-power.c
+++ b/src/xfpm-power.c
@@ -26,6 +26,10 @@
#include <stdlib.h>
#include <string.h>
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>
#include <upower.h>
@@ -52,6 +56,7 @@
#include "xfpm-enum-types.h"
#include "egg-idletime.h"
#include "xfpm-systemd.h"
+#include "xfpm-suspend.h"
static void xfpm_power_finalize (GObject *object);
@@ -66,6 +71,8 @@ static void xfpm_power_dbus_init (XfpmPower *power);
static void xfpm_power_refresh_adaptor_visible (XfpmPower *power);
+static gboolean xfpm_power_prompt_password (XfpmPower *power);
+
#define XFPM_POWER_GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), XFPM_TYPE_POWER, XfpmPowerPrivate))
@@ -99,6 +106,8 @@ struct XfpmPowerPrivate
gboolean auth_suspend;
gboolean auth_hibernate;
+ XfpmSuspend *suspend;
+
/* Properties */
gboolean on_low_battery;
gboolean lid_is_present;
@@ -220,8 +229,7 @@ xfpm_power_get_properties (XfpmPower *power)
gboolean lid_is_closed;
gboolean lid_is_present;
- /* TODO: newer versions of upower don't have that => logind handles it */
-#if !UP_CHECK_VERSION(0, 9, 0)
+#if !UP_CHECK_VERSION(0, 99, 0)
power->priv->can_suspend = up_client_get_can_suspend(power->priv->upower);
power->priv->can_hibernate = up_client_get_can_hibernate(power->priv->upower);
#else
@@ -236,8 +244,8 @@ xfpm_power_get_properties (XfpmPower *power)
}
else
{
- g_warning("Error: using upower >= 0.9.0 but logind is not running");
- g_warning(" suspend / hibernate will not work!");
+ power->priv->can_suspend = xfpm_suspend_can_suspend ();
+ power->priv->can_hibernate = xfpm_suspend_can_hibernate ();
}
#endif
g_object_get (power->priv->upower,
@@ -303,6 +311,29 @@ xfpm_power_sleep (XfpmPower *power, const gchar *sleep_time, gboolean force)
return;
}
+/* Upower dropped support for doing anything power related */
+#if UP_CHECK_VERSION(0, 99, 0)
+ if ( !LOGIND_RUNNING () )
+ {
+ /* See if we require a password for sudo to call suspend */
+ if ( xfpm_suspend_password_required (power->priv->suspend) )
+ {
+ if ( !xfpm_power_prompt_password (power) )
+ {
+ const gchar *icon_name;
+ if ( !g_strcmp0 (sleep_time, "Hibernate") )
+ icon_name = XFPM_HIBERNATE_ICON;
+ else
+ icon_name = XFPM_SUSPEND_ICON;
+
+ xfpm_power_report_error (power, _("Incorrect password entered"), icon_name);
+
+ return;
+ }
+ }
+ }
+#endif
+
g_signal_emit (G_OBJECT (power), signals [SLEEPING], 0);
#ifdef WITH_NETWORK_MANAGER
@@ -352,7 +383,7 @@ xfpm_power_sleep (XfpmPower *power, const gchar *sleep_time, gboolean force)
}
else
{
-#if !UP_CHECK_VERSION(0, 9, 0)
+#if !UP_CHECK_VERSION(0, 99, 0)
if (!g_strcmp0 (sleep_time, "Hibernate"))
{
up_client_hibernate_sync(power->priv->upower, NULL, &error);
@@ -362,8 +393,16 @@ xfpm_power_sleep (XfpmPower *power, const gchar *sleep_time, gboolean force)
up_client_suspend_sync(power->priv->upower, NULL, &error);
}
#else
- g_warning("Error: using upower >= 0.9.0 but logind is not running");
- g_warning(" suspend / hibernate will not work!");
+ if (!g_strcmp0 (sleep_time, "Hibernate"))
+ {
+ if (xfpm_suspend_sudo_get_state (power->priv->suspend) == SUDO_AVAILABLE)
+ xfpm_suspend_sudo_try_action (power->priv->suspend, XFPM_HIBERNATE, &error);
+ }
+ else
+ {
+ if (xfpm_suspend_sudo_get_state (power->priv->suspend) == SUDO_AVAILABLE)
+ xfpm_suspend_sudo_try_action (power->priv->suspend, XFPM_SUSPEND, &error);
+ }
#endif
}
@@ -1361,6 +1400,7 @@ xfpm_power_init (XfpmPower *power)
power->priv->overall_state = XFPM_BATTERY_CHARGE_OK;
power->priv->critical_action_done = FALSE;
power->priv->power_mode = XFPM_POWER_MODE_NORMAL;
+ power->priv->suspend = xfpm_suspend_get ();
power->priv->inhibit = xfpm_inhibit_new ();
power->priv->notify = xfpm_notify_new ();
@@ -1546,6 +1586,65 @@ XfpmPowerMode xfpm_power_get_mode (XfpmPower *power)
return power->priv->power_mode;
}
+
+static gboolean
+xfpm_power_prompt_password (XfpmPower *power)
+{
+ GtkWidget *dialog = gtk_message_dialog_new (NULL,
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_OTHER,
+ GTK_BUTTONS_OK_CANCEL,
+ _("The requested operation requires elevated privileges.\t\n"
+ "Please enter your password."));
+ GtkWidget *content_area = gtk_dialog_get_content_area (GTK_DIALOG(dialog));
+ GtkWidget *password_entry = gtk_entry_new ();
+ GtkWidget *password_label, *hbox;
+ gint result;
+ XfpmPassState state = PASSWORD_FAILED;
+
+ /* Set the dialog's title */
+ gtk_window_set_title (GTK_WINDOW(dialog), _("xfce4-power-manager"));
+
+ /* setup password label */
+ password_label = gtk_label_new (_("Password:"));
+
+ /* Setup the password entry */
+ gtk_entry_set_visibility (GTK_ENTRY (password_entry), FALSE);
+ gtk_entry_set_max_length (GTK_ENTRY (password_entry), 1024);
+ gtk_entry_set_activates_default (GTK_ENTRY (password_entry), TRUE);
+
+ /* pack the password label and entry into an hbox */
+ hbox = gtk_hbox_new (FALSE, 4);
+ gtk_box_pack_start (GTK_BOX (hbox), password_label, FALSE, FALSE, 4);
+ gtk_box_pack_end (GTK_BOX (hbox), password_entry, TRUE, TRUE, 4);
+
+ /* Add it to the dialog */
+ gtk_box_pack_end (GTK_BOX (content_area), hbox, TRUE, TRUE, 8);
+
+ /* show it */
+ gtk_widget_show (password_entry);
+ gtk_widget_show (password_label);
+ gtk_widget_show (hbox);
+
+ /* make enter default to ok */
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+ /* Run the password prompt */
+ result = gtk_dialog_run (GTK_DIALOG(dialog));
+
+ if (result == GTK_RESPONSE_OK)
+ {
+ state = xfpm_suspend_sudo_send_password (power->priv->suspend, gtk_entry_get_text (GTK_ENTRY (password_entry)));
+ XFPM_DEBUG ("password state: %s", state == PASSWORD_FAILED ? "PASSWORD_FAILED" : "PASSWORD_SUCCEED");
+ }
+
+ gtk_widget_destroy (dialog);
+
+ return state == PASSWORD_FAILED ? FALSE : TRUE;
+}
+
+
+
/*
*
* DBus server implementation for org.freedesktop.PowerManagement
diff --git a/src/xfpm-suspend.c b/src/xfpm-suspend.c
new file mode 100644
index 0000000..ff053c6
--- /dev/null
+++ b/src/xfpm-suspend.c
@@ -0,0 +1,592 @@
+/*
+ * * Copyright (C) 2009-2011 Ali <aliov at xfce.org>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include <libxfce4util/libxfce4util.h>
+
+#include "xfpm-common.h"
+#include "xfpm-debug.h"
+#include "xfpm-suspend.h"
+
+
+static void xfpm_suspend_sudo_free (XfpmSuspend *suspend);
+
+static void xfpm_suspend_sudo_childwatch (GPid pid,
+ gint status,
+ gpointer data);
+
+static gboolean xfpm_suspend_sudo_init (XfpmSuspend *suspend,
+ GError **error);
+
+
+#define XFPM_SUSPEND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), XFPM_TYPE_SUSPEND, XfpmSuspendPrivate))
+
+struct XfpmSuspendPrivate
+{
+ /* sudo helper */
+ HelperState helper_state;
+ pid_t helper_pid;
+ FILE *helper_infile;
+ FILE *helper_outfile;
+ guint helper_watchid;
+ gboolean helper_require_password;
+};
+
+
+G_DEFINE_TYPE (XfpmSuspend, xfpm_suspend, G_TYPE_OBJECT)
+
+static void
+xfpm_suspend_init (XfpmSuspend *suspend)
+{
+ GError *error = NULL;
+
+ suspend->priv = XFPM_SUSPEND_GET_PRIVATE (suspend);
+ suspend->priv->helper_state = SUDO_NOT_INITIAZED;
+ suspend->priv->helper_require_password = FALSE;
+ suspend->priv->helper_infile = NULL;
+ suspend->priv->helper_outfile = NULL;
+ suspend->priv->helper_pid = 0;
+ suspend->priv->helper_watchid = 0;
+
+ if (!xfpm_suspend_sudo_init (suspend, &error))
+ {
+ g_warning ("xfpm_suspend_sudo_init failed : %s", error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+xfpm_suspend_finalize (GObject *object)
+{
+ XfpmSuspend *suspend = XFPM_SUSPEND (object);
+
+ /* close down helper */
+ xfpm_suspend_sudo_free (suspend);
+
+ G_OBJECT_CLASS (xfpm_suspend_parent_class)->finalize (object);
+}
+
+static void
+xfpm_suspend_class_init (XfpmSuspendClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = xfpm_suspend_finalize;
+
+ g_type_class_add_private (klass, sizeof (XfpmSuspendPrivate));
+}
+
+XfpmSuspend *
+xfpm_suspend_get (void)
+{
+ static gpointer xfpm_suspend_object = NULL;
+
+ if ( G_LIKELY (xfpm_suspend_object != NULL ) )
+ {
+ g_object_ref (xfpm_suspend_object);
+ }
+ else
+ {
+ xfpm_suspend_object = g_object_new (XFPM_TYPE_SUSPEND, NULL);
+ g_object_add_weak_pointer (xfpm_suspend_object, &xfpm_suspend_object);
+ }
+
+ return XFPM_SUSPEND (xfpm_suspend_object);
+}
+
+static void
+xfpm_suspend_sudo_free (XfpmSuspend *suspend)
+{
+ gint status;
+
+ /* close down helper */
+ if (suspend->priv->helper_infile != NULL)
+ {
+ fclose (suspend->priv->helper_infile);
+ suspend->priv->helper_infile = NULL;
+ }
+
+ if (suspend->priv->helper_outfile != NULL)
+ {
+ fclose (suspend->priv->helper_outfile);
+ suspend->priv->helper_outfile = NULL;
+ }
+
+ if (suspend->priv->helper_watchid > 0)
+ {
+ g_source_remove (suspend->priv->helper_watchid);
+ suspend->priv->helper_watchid = 0;
+ }
+
+ if (suspend->priv->helper_pid > 0)
+ {
+ waitpid (suspend->priv->helper_pid, &status, 0);
+ suspend->priv->helper_pid = 0;
+ }
+
+ /* reset state */
+ suspend->priv->helper_state = SUDO_NOT_INITIAZED;
+}
+
+static void
+xfpm_suspend_sudo_childwatch (GPid pid,
+ gint status,
+ gpointer data)
+{
+ /* close down sudo stuff */
+ xfpm_suspend_sudo_free (XFPM_SUSPEND (data));
+}
+
+static gboolean
+xfpm_suspend_sudo_init (XfpmSuspend *suspend,
+ GError **error)
+{
+ gchar *cmd;
+ struct rlimit rlp;
+ gchar buf[15];
+ gint parent_pipe[2];
+ gint child_pipe[2];
+ gint n;
+
+ /* return state if we succeeded */
+ if (suspend->priv->helper_state != SUDO_NOT_INITIAZED)
+ return suspend->priv->helper_state == SUDO_AVAILABLE;
+
+ g_return_val_if_fail (suspend->priv->helper_infile == NULL, FALSE);
+ g_return_val_if_fail (suspend->priv->helper_outfile == NULL, FALSE);
+ g_return_val_if_fail (suspend->priv->helper_watchid == 0, FALSE);
+ g_return_val_if_fail (suspend->priv->helper_pid == 0, FALSE);
+
+ /* assume it won't work for now */
+ suspend->priv->helper_state = SUDO_FAILED;
+
+ cmd = g_find_program_in_path ("sudo");
+ if (G_UNLIKELY (cmd == NULL))
+ {
+ g_set_error_literal (error, 1, 0,
+ "The program \"sudo\" was not found");
+ return FALSE;
+ }
+
+ if (pipe (parent_pipe) == -1)
+ {
+ g_set_error (error, 1, 0,
+ "Unable to create parent pipe: %s",
+ strerror (errno));
+ goto err0;
+ }
+
+ if (pipe (child_pipe) == -1)
+ {
+ g_set_error (error, 1, 0,
+ "Unable to create child pipe: %s",
+ strerror (errno));
+ goto err1;
+ }
+
+ suspend->priv->helper_pid = fork ();
+ if (suspend->priv->helper_pid < 0)
+ {
+ g_set_error (error, 1, 0,
+ "Unable to fork sudo helper: %s",
+ strerror (errno));
+ goto err2;
+ }
+ else if (suspend->priv->helper_pid == 0)
+ {
+ /* setup signals */
+ signal (SIGPIPE, SIG_IGN);
+
+ /* setup environment */
+ g_setenv ("LC_ALL", "C", TRUE);
+ g_setenv ("LANG", "C", TRUE);
+ g_setenv ("LANGUAGE", "C", TRUE);
+
+ /* setup the 3 standard file handles */
+ dup2 (child_pipe[0], STDIN_FILENO);
+ dup2 (parent_pipe[1], STDOUT_FILENO);
+ dup2 (parent_pipe[1], STDERR_FILENO);
+
+ /* close all other file handles */
+ getrlimit (RLIMIT_NOFILE, &rlp);
+ for (n = 0; n < (gint) rlp.rlim_cur; ++n)
+ {
+ if (n != STDIN_FILENO && n != STDOUT_FILENO && n != STDERR_FILENO)
+ close (n);
+ }
+
+ /* execute sudo with the helper */
+ execl (cmd, "sudo", "-H", "-S", "-p",
+ "XFPM_SUDO_PASS ", "--",
+ XFPM_SUSPEND_HELPER_CMD, NULL);
+
+ g_free (cmd);
+ cmd = NULL;
+
+ _exit (127);
+ }
+ else
+ {
+ /* watch the sudo helper */
+ suspend->priv->helper_watchid = g_child_watch_add (suspend->priv->helper_pid,
+ xfpm_suspend_sudo_childwatch,
+ suspend);
+ }
+
+ /* read sudo/helper answer */
+ n = read (parent_pipe[0], buf, sizeof (buf));
+ if (n < 15)
+ {
+ g_set_error (error, 1, 0,
+ "Unable to read response from sudo helper: %s",
+ n < 0 ? strerror (errno) : "Unknown error");
+ goto err2;
+ }
+
+ /* open pipe to receive replies from sudo */
+ suspend->priv->helper_infile = fdopen (parent_pipe[0], "r");
+ if (suspend->priv->helper_infile == NULL)
+ {
+ g_set_error (error, 1, 0,
+ "Unable to open parent pipe: %s",
+ strerror (errno));
+ goto err2;
+ }
+ close (parent_pipe[1]);
+
+ /* open pipe to send passwords to sudo */
+ suspend->priv->helper_outfile = fdopen (child_pipe[1], "w");
+ if (suspend->priv->helper_outfile == NULL)
+ {
+ g_set_error (error, 1, 0,
+ "Unable to open parent pipe: %s",
+ strerror (errno));
+ goto err2;
+ }
+ close (child_pipe[0]);
+
+ /* check if NOPASSWD is set in /etc/sudoers */
+ if (memcmp (buf, "XFPM_SUDO_PASS ", 15) == 0)
+ {
+ suspend->priv->helper_require_password = TRUE;
+ }
+ else if (memcmp (buf, "XFPM_SUDO_DONE ", 15) == 0)
+ {
+ suspend->priv->helper_require_password = FALSE;
+ }
+ else
+ {
+ g_set_error (error, 1, 0,
+ "Got unexpected reply from sudo pm helper");
+ goto err2;
+ }
+
+ XFPM_DEBUG ("suspend->priv->helper_require_password %s",
+ suspend->priv->helper_require_password ? "Required" : "Not required");
+
+ /* if we try again */
+ suspend->priv->helper_state = SUDO_AVAILABLE;
+
+ return TRUE;
+
+err2:
+ xfpm_suspend_sudo_free (suspend);
+
+ close (child_pipe[0]);
+ close (child_pipe[1]);
+
+err1:
+ close (parent_pipe[0]);
+ close (parent_pipe[1]);
+
+err0:
+ g_free (cmd);
+
+ suspend->priv->helper_pid = 0;
+
+ return FALSE;
+}
+
+gboolean
+xfpm_suspend_sudo_try_action (XfpmSuspend *suspend,
+ XfpmActionType type,
+ GError **error)
+{
+ const gchar *action;
+ gchar reply[256];
+
+ TRACE("entering");
+
+ g_return_val_if_fail (suspend->priv->helper_state == SUDO_AVAILABLE, FALSE);
+ g_return_val_if_fail (suspend->priv->helper_outfile != NULL, FALSE);
+ g_return_val_if_fail (suspend->priv->helper_infile != NULL, FALSE);
+
+ /* the command we send to sudo */
+ if (type == XFPM_SUSPEND)
+ action = "SUSPEND";
+ else if (type == XFPM_HIBERNATE)
+ action = "HIBERNATE";
+ else
+ return FALSE;
+
+ /* write action to sudo helper */
+ if (fprintf (suspend->priv->helper_outfile, "%s\n", action) > 0)
+ fflush (suspend->priv->helper_outfile);
+
+ /* check if the write succeeded */
+ if (ferror (suspend->priv->helper_outfile) != 0)
+ {
+ /* probably succeeded but the helper got killed */
+ if (errno == EINTR)
+ return TRUE;
+
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Error sending command to pm helper: %s"),
+ strerror (errno));
+ return FALSE;
+ }
+
+ /* get responce from sudo helper */
+ if (fgets (reply, sizeof (reply), suspend->priv->helper_infile) == NULL)
+ {
+ /* probably succeeded but the helper got killed */
+ if (errno == EINTR)
+ return TRUE;
+
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Error receiving response from pm helper: %s"),
+ strerror (errno));
+ return FALSE;
+ }
+
+ if (strncmp (reply, "SUCCEED", 7) != 0)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Sleep command failed"));
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+XfpmPassState
+xfpm_suspend_sudo_send_password (XfpmSuspend *suspend,
+ const gchar *password)
+{
+ gchar buf[1024];
+ gsize len_buf, len_send;
+ gint fd;
+ gsize len;
+ gssize result;
+ gint attempts;
+ const gchar *errmsg = NULL;
+
+ g_return_val_if_fail (suspend->priv->helper_state == SUDO_AVAILABLE, PASSWORD_FAILED);
+ g_return_val_if_fail (suspend->priv->helper_outfile != NULL, PASSWORD_FAILED);
+ g_return_val_if_fail (suspend->priv->helper_infile != NULL, PASSWORD_FAILED);
+ g_return_val_if_fail (suspend->priv->helper_require_password, PASSWORD_FAILED);
+ g_return_val_if_fail (password != NULL, PASSWORD_FAILED);
+
+ /* write password to sudo helper */
+ g_snprintf (buf, sizeof (buf), "%s\n", password);
+ len_buf = strlen (buf);
+ len_send = fwrite (buf, 1, len_buf, suspend->priv->helper_outfile);
+ fflush (suspend->priv->helper_outfile);
+ bzero (buf, len_buf);
+
+ if (len_send != len_buf || ferror (suspend->priv->helper_outfile) != 0)
+ {
+ errmsg = "Failed to send password to sudo";
+ goto err1;
+ }
+
+ fd = fileno (suspend->priv->helper_infile);
+
+ for (len = 0, attempts = 0;;)
+ {
+ result = read (fd, buf + len, 256 - len);
+
+ if (result < 0)
+ {
+ errmsg = "Failed to read data from sudo";
+ goto err1;
+ }
+ else if (result == 0)
+ {
+ /* don't try too often */
+ if (++attempts > 20)
+ {
+ errmsg = "Too many password attempts";
+ goto err1;
+ }
+
+ continue;
+ }
+ else if (result + len >= sizeof (buf))
+ {
+ errmsg = "Received too much data from sudo";
+ goto err1;
+ }
+
+ len += result;
+ buf[len] = '\0';
+
+ if (len >= 15)
+ {
+ if (g_str_has_suffix (buf, "XFPM_SUDO_PASS "))
+ {
+ return PASSWORD_RETRY;
+ }
+ else if (g_str_has_suffix (buf, "XFPM_SUDO_DONE "))
+ {
+ /* sudo is unlocked, no further passwords required */
+ suspend->priv->helper_require_password = FALSE;
+
+ return PASSWORD_SUCCEED;
+ }
+ }
+ }
+
+ return PASSWORD_FAILED;
+
+err1:
+ g_printerr (PACKAGE_NAME ": %s.\n\n", errmsg);
+ return PASSWORD_FAILED;
+}
+
+gboolean
+xfpm_suspend_password_required (XfpmSuspend *suspend)
+{
+ g_return_val_if_fail (XFPM_IS_SUSPEND (suspend), TRUE);
+
+ return suspend->priv->helper_require_password;
+}
+
+HelperState
+xfpm_suspend_sudo_get_state (XfpmSuspend *suspend)
+{
+ g_return_val_if_fail (XFPM_IS_SUSPEND (suspend), SUDO_NOT_INITIAZED);
+
+ return suspend->priv->helper_state;
+}
+
+
+
+
+#ifdef BACKEND_TYPE_FREEBSD
+static gboolean
+freebsd_supports_sleep_state (const gchar *state)
+{
+ gboolean ret = FALSE;
+ gchar *sleep_states;
+
+ XFPM_DEBUG("entering");
+
+ sleep_states = up_get_string_sysctl (NULL, "hw.acpi.supported_sleep_state");
+ if (sleep_states != NULL) {
+ if (strstr (sleep_states, state) != NULL)
+ ret = TRUE;
+ }
+
+ g_free (sleep_states);
+
+ return ret;
+}
+#endif
+
+#ifdef BACKEND_TYPE_LINUX
+static gboolean
+linux_supports_sleep_state (const gchar *state)
+{
+ gboolean ret = FALSE;
+ gchar *command;
+ GError *error = NULL;
+ gint exit_status;
+
+ XFPM_DEBUG("entering");
+
+ /* run script from pm-utils */
+ command = g_strdup_printf ("/usr/bin/pm-is-supported --%s", state);
+ g_debug ("excuting command: %s", command);
+ ret = g_spawn_command_line_sync (command, NULL, NULL, &exit_status, &error);
+ if (!ret) {
+ g_warning ("failed to run script: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+ ret = (WIFEXITED(exit_status) && (WEXITSTATUS(exit_status) == EXIT_SUCCESS));
+
+out:
+ g_free (command);
+
+ return ret;
+}
+#endif
+
+
+gboolean
+xfpm_suspend_can_suspend (void)
+{
+ XFPM_DEBUG("entering");
+#ifdef BACKEND_TYPE_FREEBSD
+ return freebsd_supports_sleep_state ("S3");
+#endif
+#ifdef BACKEND_TYPE_LINUX
+ return linux_supports_sleep_state ("suspend");
+#endif
+#ifdef BACKEND_TYPE_OPENBSD
+ return TRUE;
+#endif
+
+ return FALSE;
+}
+
+gboolean
+xfpm_suspend_can_hibernate (void)
+{
+ XFPM_DEBUG("entering");
+#ifdef BACKEND_TYPE_FREEBSD
+ return freebsd_supports_sleep_state ("S4");
+#endif
+#ifdef BACKEND_TYPE_LINUX
+ return linux_supports_sleep_state ("hibernate");
+#endif
+#ifdef BACKEND_TYPE_OPENBSD
+ return FALSE;
+#endif
+
+ return FALSE;
+}
diff --git a/src/xfpm-suspend.h b/src/xfpm-suspend.h
new file mode 100644
index 0000000..f012e69
--- /dev/null
+++ b/src/xfpm-suspend.h
@@ -0,0 +1,85 @@
+/*
+ * * Copyright (C) 2009-2011 Ali <aliov at xfce.org>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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
+ */
+#ifndef __XFPM_SUSPEND_H
+#define __XFPM_SUSPEND_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define XFPM_TYPE_SUSPEND (xfpm_suspend_get_type () )
+#define XFPM_SUSPEND(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), XFPM_TYPE_SUSPEND, XfpmSuspend))
+#define XFPM_IS_SUSPEND(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), XFPM_TYPE_SUSPEND))
+
+typedef struct XfpmSuspendPrivate XfpmSuspendPrivate;
+
+typedef struct
+{
+ GObject parent;
+ XfpmSuspendPrivate *priv;
+} XfpmSuspend;
+
+typedef struct
+{
+ GObjectClass parent_class;
+} XfpmSuspendClass;
+
+typedef enum
+{
+ SUDO_NOT_INITIAZED,
+ SUDO_AVAILABLE,
+ SUDO_FAILED
+} HelperState;
+
+typedef enum
+{
+ XFPM_ASK_0 = 0,
+ XFPM_SUSPEND,
+ XFPM_HIBERNATE,
+} XfpmActionType;
+
+typedef enum
+{
+ PASSWORD_RETRY,
+ PASSWORD_SUCCEED,
+ PASSWORD_FAILED
+} XfpmPassState;
+
+GType xfpm_suspend_get_type (void) G_GNUC_CONST;
+
+XfpmSuspend *xfpm_suspend_get (void);
+
+gboolean xfpm_suspend_can_suspend (void);
+gboolean xfpm_suspend_can_hibernate (void);
+
+gboolean xfpm_suspend_password_required (XfpmSuspend *suspend);
+
+gboolean xfpm_suspend_sudo_try_action (XfpmSuspend *suspend,
+ XfpmActionType type,
+ GError **error);
+
+XfpmPassState xfpm_suspend_sudo_send_password (XfpmSuspend *suspend,
+ const gchar *password);
+
+HelperState xfpm_suspend_sudo_get_state (XfpmSuspend *suspend);
+
+G_END_DECLS
+
+#endif /* __XFPM_SUSPEND_H */
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the Xfce4-commits
mailing list