/*- * Copyright (c) 2003-2004 Benedikt Meurer * Copyright (c) 2011 Nick Schermer * Copyright (c) 2014 Xfce Development Team * Copyright (c) 2018 Ali Abdallah * 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 . */ #ifdef HAVE_CONFIG_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_WAIT_H #include #endif #ifdef HAVE_SYS_RESOURCE_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_SIGNAL_H #include #endif #include #include #include #include #ifdef HAVE_POLKIT #include #endif #include #include #include #include #include #include #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) #define __BACKEND_TYPE_BSD__ 1 #ifdef HAVE_SYS_SYSCTL_H #include #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); } 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: 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 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); }