[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