diff options
Diffstat (limited to 'xfce4-session/xfsm-shutdown-fallback.c')
-rw-r--r-- | xfce4-session/xfsm-shutdown-fallback.c | 599 |
1 files changed, 599 insertions, 0 deletions
diff --git a/xfce4-session/xfsm-shutdown-fallback.c b/xfce4-session/xfsm-shutdown-fallback.c new file mode 100644 index 0000000..ff6b81b --- /dev/null +++ b/xfce4-session/xfsm-shutdown-fallback.c @@ -0,0 +1,599 @@ +/*- + * Copyright (c) 2003-2004 Benedikt Meurer <benny@xfce.org> + * Copyright (c) 2011 Nick Schermer <nick@xfce.org> + * Copyright (c) 2014 Xfce Development Team <xfce4-dev@xfce.org> + * Copyright (c) 2018 Ali Abdallah <ali@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. + * + * Parts of this file where taken from gnome-session/logout.c, which + * was written by Owen Taylor <otaylor@redhat.com>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_WAIT_H +#include <sys/wait.h> +#endif +#ifdef HAVE_SYS_RESOURCE_H +#include <sys/resource.h> +#endif + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_SIGNAL_H +#include <signal.h> +#endif + +#include <gio/gio.h> +#include <libxfce4util/libxfce4util.h> +#include <gtk/gtk.h> +#include <glib/gstdio.h> + +#ifdef HAVE_POLKIT +#include <polkit/polkit.h> +#endif + +#include <pwd.h> +#include <grp.h> + +#include <libxfsm/xfsm-util.h> +#include <libxfsm/xfsm-shutdown-common.h> +#include <xfce4-session/xfsm-shutdown-fallback.h> +#include <xfce4-session/xfce-screensaver.h> + +#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#define __BACKEND_TYPE_BSD__ 1 +#ifdef HAVE_SYS_SYSCTL_H +#include <sys/sysctl.h> +#endif +#endif + +#define POLKIT_AUTH_SHUTDOWN_XFSM "org.xfce.session.xfsm-shutdown-helper" +#define POLKIT_AUTH_RESTART_XFSM "org.xfce.session.xfsm-shutdown-helper" +#define POLKIT_AUTH_SUSPEND_XFSM "org.xfce.session.xfsm-shutdown-helper" +#define POLKIT_AUTH_HIBERNATE_XFSM "org.xfce.session.xfsm-shutdown-helper" +#define POLKIT_AUTH_HYBRID_SLEEP_XFSM "org.xfce.session.xfsm-shutdown-helper" + + + + +#ifdef BACKEND_TYPE_FREEBSD +static gchar * +get_string_sysctl (GError **err, const gchar *format, ...) +{ + va_list args; + gchar *name; + size_t value_len; + gchar *str = NULL; + + g_return_val_if_fail(format != NULL, FALSE); + + va_start (args, format); + name = g_strdup_vprintf (format, args); + va_end (args); + + if (sysctlbyname (name, NULL, &value_len, NULL, 0) == 0) { + str = g_new (char, value_len + 1); + if (sysctlbyname (name, str, &value_len, NULL, 0) == 0) + str[value_len] = 0; + else { + g_free (str); + str = NULL; + } + } + + if (!str) + g_set_error (err, 0, 0, "%s", g_strerror(errno)); + + g_free(name); + return str; +} + + + +static gboolean +freebsd_supports_sleep_state (const gchar *state) +{ + gboolean ret = FALSE; + gchar *sleep_states; + +#if defined(__FreeBSD__) + gboolean status; + gint v; + size_t value_len = sizeof(int); +#endif + + sleep_states = get_string_sysctl (NULL, "hw.acpi.supported_sleep_state"); + if (sleep_states != NULL) + { + if (strstr (sleep_states, state) != NULL) + ret = TRUE; + } + +#if defined(__FreeBSD__) + /* On FreeBSD, S4 is not supported unless S4BIOS is available. + * If S4 will ever be implemented on FreeBSD, we can disable S4bios + * check below, using #if __FreeBSD_version >= XXXXXX. + **/ + if (g_strcmp0(state, "S4") == 0) + { + status = sysctlbyname ("hw.acpi.s4bios", &v, &value_len, NULL, 0) == 0; + if (G_UNLIKELY(!status)) + { + g_warning ("sysctl failed on 'hw.acpi.s4bios'"); + } + else + { + /* No S4Bios support */ + if (v == 0) + ret = FALSE; + } + } +#endif /* __FreeBSD__ */ + + g_free (sleep_states); + + return ret; +} +#endif /* BACKEND_TYPE_FREEBSD */ + + + +#ifdef BACKEND_TYPE_LINUX +static gboolean +linux_supports_sleep_state (const gchar *state) +{ + gboolean ret = FALSE; + gchar *command; + GError *error = NULL; + gint exit_status; + + /* run script from pm-utils */ + command = g_strdup_printf ("/usr/bin/pm-is-supported --%s", state); + + 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 /* BACKEND_TYPE_LINUX */ + + + +#ifdef __BACKEND_TYPE_BSD__ +static gboolean +xfsm_shutdown_fallback_user_is_operator (void) +{ + struct passwd *pw; + int max_grp = sysconf(_SC_NGROUPS_MAX); + gid_t *groups; + int i = 0; + int ret; + static gboolean is_operator = FALSE; + static gboolean once = FALSE; + + /* Only check once */ + if (once == TRUE) + goto out; + + if (max_grp == -1) + { + fprintf (stderr, "sysconf(_SC_NGROUPS_MAX) failed"); + return FALSE; + } + + groups = malloc(sizeof(gid_t) * max_grp); + if (groups == NULL) + { + fprintf (stderr, "malloc(sizeof(gid_t) * max_grp) failed"); + return FALSE; + } + + pw = getpwuid (getuid()); + + ret = getgrouplist (pw->pw_name, pw->pw_gid, groups, &max_grp); + + if (ret < 0) + { + fprintf (stderr, + "Failed to get user group list, user belongs to more than %u groups?\n", + max_grp); + free(groups); + goto out; + } + + once = TRUE; + + for (i = 0; i < max_grp; i++) + { + struct group *gr; + + gr = getgrgid (groups[i]); + if (strncmp(gr->gr_name, "operator", 8) == 0) + { + is_operator = TRUE; + break; + } + } + free(groups); +out: + return is_operator; +} +#endif /* __BACKEND_TYPE_BSD__ */ + + + +#ifdef __BACKEND_TYPE_BSD__ +static gboolean +xfsm_shutdown_fallback_bsd_check_auth (XfsmShutdownType shutdown_type) +{ + gboolean auth_result = FALSE; + switch (shutdown_type) + { + /* On the BSDs users of the operator group are allowed to shutdown/restart the system */ + case XFSM_SHUTDOWN_SHUTDOWN: + case XFSM_SHUTDOWN_RESTART: + auth_result = xfsm_shutdown_fallback_user_is_operator (); + break; + case XFSM_SHUTDOWN_SUSPEND: + case XFSM_SHUTDOWN_HIBERNATE: + case XFSM_SHUTDOWN_HYBRID_SLEEP: + /* Check rw access on '/var/run/apmdev' on OpenBSD and to /dev/acpi' + * for the other BSDs */ + auth_result = g_access(BSD_SLEEP_ACCESS_NODE, R_OK|W_OK) == 0; + break; + default: + g_warning ("Unexpected shutdow id '%d'\n", shutdown_type); + break; + } + + return auth_result; +} +#endif /* __BACKEND_TYPE_BSD__ */ + + + +static gboolean +xfsm_shutdown_fallback_check_auth_polkit (const gchar *action_id) +{ + gboolean auth_result = FALSE; +#ifdef HAVE_POLKIT + GDBusConnection *bus; + PolkitAuthority *polkit; + PolkitAuthorizationResult *polkit_result; + PolkitSubject *polkit_subject; + + bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL); + polkit = polkit_authority_get_sync (NULL, NULL); + if (polkit != NULL && bus != NULL) + { + polkit_subject = polkit_system_bus_name_new (g_dbus_connection_get_unique_name (bus)); + polkit_result = polkit_authority_check_authorization_sync (polkit, + polkit_subject, + action_id, + NULL, + POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE, + NULL, + NULL); + if (polkit_result != NULL) + { + auth_result = polkit_authorization_result_get_is_authorized (polkit_result); + g_object_unref (polkit_result); + } + + g_object_unref (polkit); + g_object_unref (bus); + } +#endif /* HAVE_POLKIT */ + + return auth_result; +} + + + +static gboolean +lock_screen (GError **error) +{ + XfconfChannel *channel; + XfceScreenSaver *saver; + gboolean ret = TRUE; + + channel = xfsm_open_config (); + saver = xfce_screensaver_new (); + if (xfconf_channel_get_bool (channel, "/shutdown/LockScreen", FALSE)) + ret = xfce_screensaver_lock (saver); + + if (ret) + { + /* sleep 2 seconds so locking has time to startup */ + g_usleep (G_USEC_PER_SEC * 2); + } + else + g_set_error (error, 1, 0, "Failed to lock the screen."); + + g_object_unref (G_OBJECT (saver)); + + return ret; +} + + + +/** + * xfsm_shutdown_fallback_try_action: + * @type: The @XfsmShutdownType action to perform (shutdown, suspend, etc). + * @error: Returns a @GError if an error was encountered. + * + * Performs the @XfsmShutdownType action requested. + * + * Return value: Returns FALSE if an invalid @XfsmShutdownType action was + * requested. Otherwise returns the status of the pkexec + * call to the helper command. + **/ +gboolean +xfsm_shutdown_fallback_try_action (XfsmShutdownType type, + GError **error) +{ + const gchar *xfsm_helper_action; + const gchar *cmd __attribute__((unused)); + gboolean ret = FALSE; + gint exit_status = 0; +#ifdef HAVE_POLKIT + gchar *command = NULL; +#endif + + switch (type) + { + case XFSM_SHUTDOWN_SHUTDOWN: + xfsm_helper_action = "shutdown"; + cmd = POWEROFF_CMD; + break; + case XFSM_SHUTDOWN_RESTART: + xfsm_helper_action = "restart"; + cmd = REBOOT_CMD; + break; + case XFSM_SHUTDOWN_SUSPEND: + xfsm_helper_action = "suspend"; + cmd = UP_BACKEND_SUSPEND_COMMAND; + /* On suspend we try to lock the screen */ + if (!lock_screen (error)) + return FALSE; + break; + case XFSM_SHUTDOWN_HIBERNATE: + xfsm_helper_action = "hibernate"; + cmd = UP_BACKEND_HIBERNATE_COMMAND; + /* On hibernate we try to lock the screen */ + if (!lock_screen (error)) + return FALSE; + break; + case XFSM_SHUTDOWN_HYBRID_SLEEP: + xfsm_helper_action = "hybrid-sleep"; + cmd = UP_BACKEND_HIBERNATE_COMMAND; + /* On hybrid sleep we try to lock the screen */ + if (!lock_screen (error)) + return FALSE; + break; + default: + g_set_error (error, 1, 0, "Unknown shutdown type %d", type); + return FALSE; + } + +#ifdef __BACKEND_TYPE_BSD__ + /* Make sure we can use native shutdown commands */ + if (xfsm_shutdown_fallback_bsd_check_auth (type)) + { + return g_spawn_command_line_sync (cmd, NULL, NULL, &exit_status, error); + } +#endif + + /* xfsm-shutdown-helper requires polkit to run */ +#ifdef HAVE_POLKIT + command = g_strdup_printf ("pkexec " XFSM_SHUTDOWN_HELPER_CMD " --%s", xfsm_helper_action); + + ret = g_spawn_command_line_sync (command, NULL, NULL, &exit_status, error); + + g_free (command); +#endif + if (!ret) + { + g_set_error (error, 1, 0, "Failed to %s (%s)", xfsm_helper_action, cmd); + } + return ret; +} + + + +/** + * xfsm_shutdown_fallback_can_suspend: + * + * Return value: Returns whether the *system* is capable suspending. + **/ +gboolean +xfsm_shutdown_fallback_can_suspend (void) +{ +#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; +} + + + +/** + * xfsm_shutdown_fallback_can_hibernate: + * + * Return value: Returns whether the *system* is capable hibernating. + **/ +gboolean +xfsm_shutdown_fallback_can_hibernate (void) +{ +#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 TRUE; +#endif + + return FALSE; +} + + + +/** + * xfsm_shutdown_fallback_can_hybrid_sleep: + * + * Return value: Returns whether the *system* is capable of hybrid sleep. + **/ +gboolean +xfsm_shutdown_fallback_can_hybrid_sleep (void) +{ +#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; +} + + + +/** + * xfsm_shutdown_fallback_auth_shutdown: + * + * Return value: Returns whether the user is authorized to perform a shutdown. + **/ +gboolean +xfsm_shutdown_fallback_auth_shutdown (void) +{ +#ifdef __BACKEND_TYPE_BSD__ + gboolean ret_val = xfsm_shutdown_fallback_bsd_check_auth (XFSM_SHUTDOWN_SHUTDOWN); + if (ret_val) + return TRUE; +#endif + return xfsm_shutdown_fallback_check_auth_polkit (POLKIT_AUTH_SHUTDOWN_XFSM); +} + + + +/** + * xfsm_shutdown_fallback_auth_restart: + * + * Return value: Returns whether the user is authorized to perform a restart. + **/ +gboolean +xfsm_shutdown_fallback_auth_restart (void) +{ +#ifdef __BACKEND_TYPE_BSD__ + gboolean ret_val = xfsm_shutdown_fallback_bsd_check_auth (XFSM_SHUTDOWN_RESTART); + if (ret_val) + return TRUE; +#endif + return xfsm_shutdown_fallback_check_auth_polkit (POLKIT_AUTH_RESTART_XFSM); +} + + + +/** + * xfsm_shutdown_fallback_auth_suspend: + * + * Return value: Returns whether the user is authorized to perform a suspend. + **/ +gboolean +xfsm_shutdown_fallback_auth_suspend (void) +{ +#ifdef __BACKEND_TYPE_BSD__ + gboolean ret_val = xfsm_shutdown_fallback_bsd_check_auth (XFSM_SHUTDOWN_SUSPEND); + if (ret_val) + return TRUE; +#endif + return xfsm_shutdown_fallback_check_auth_polkit (POLKIT_AUTH_SUSPEND_XFSM); +} + + + +/** + * xfsm_shutdown_fallback_auth_hibernate: + * + * Return value: Returns whether the user is authorized to perform a hibernate. + **/ +gboolean +xfsm_shutdown_fallback_auth_hibernate (void) +{ +#ifdef __BACKEND_TYPE_BSD__ + gboolean ret_val = xfsm_shutdown_fallback_bsd_check_auth (XFSM_SHUTDOWN_HIBERNATE); + if (ret_val) + return TRUE; +#endif + return xfsm_shutdown_fallback_check_auth_polkit (POLKIT_AUTH_HIBERNATE_XFSM); +} + + + +/** + * xfsm_shutdown_fallback_auth_hybrid_sleep: + * + * Return value: Returns whether the user is authorized to perform a hybrid sleep. + **/ +gboolean +xfsm_shutdown_fallback_auth_hybrid_sleep (void) +{ +#ifdef __BACKEND_TYPE_BSD__ + gboolean ret_val = xfsm_shutdown_fallback_bsd_check_auth (XFSM_SHUTDOWN_HYBRID_SLEEP); + if (ret_val) + return TRUE; +#endif + return xfsm_shutdown_fallback_check_auth_polkit (POLKIT_AUTH_HYBRID_SLEEP_XFSM); +} |