diff options
author | Eric Koegel <eric.koegel@gmail.com> | 2015-08-24 09:56:17 +0300 |
---|---|---|
committer | Eric Koegel <eric.koegel@gmail.com> | 2016-01-23 04:04:17 +0300 |
commit | 93d957b7dbe9841dd50bacb18cd36f5e737d81fb (patch) | |
tree | 824871ca835dde65b1f44388fd3c5b8bdb1e8e64 | |
parent | e0362195ecb5e584ab35d15c63b9fdcb2a48ee9d (diff) | |
download | ConsoleKit2-93d957b7dbe9841dd50bacb18cd36f5e737d81fb.tar.gz |
Implement XDG_RUNTIME_DIR
This adds support for implementing the runtime dir spec. ConsoleKit2
will create $RUNDIR/users on the first session creation and then
$RUNDIR/users/$uid for each user logging in that keeps an active
session. CK2 will clean up the $uid folder once all sessions of that
user have been closed. CK2 will also attempt to mount the user's
rundir as a tmpfs mount owned by the user. This way if CK2 crashes
the directory will get cleaned up on next system restart. CK2 will
not create a runtime dir for root.
http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
https://github.com/ConsoleKit2/ConsoleKit2/issues/41
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | configure.ac | 4 | ||||
-rw-r--r-- | data/ConsoleKit.conf | 3 | ||||
-rw-r--r-- | libck-connector/ck-connector.c | 188 | ||||
-rw-r--r-- | libck-connector/ck-connector.h | 2 | ||||
-rw-r--r-- | libck-connector/test-connector.c | 12 | ||||
-rw-r--r-- | src/ck-manager.c | 65 | ||||
-rw-r--r-- | src/ck-session-leader.c | 1 | ||||
-rw-r--r-- | src/ck-session.c | 57 | ||||
-rw-r--r-- | src/ck-session.h | 4 | ||||
-rw-r--r-- | src/ck-sysdeps-freebsd.c | 57 | ||||
-rw-r--r-- | src/ck-sysdeps-gnu.c | 12 | ||||
-rw-r--r-- | src/ck-sysdeps-linux.c | 52 | ||||
-rw-r--r-- | src/ck-sysdeps-openbsd.c | 56 | ||||
-rw-r--r-- | src/ck-sysdeps-solaris.c | 12 | ||||
-rw-r--r-- | src/ck-sysdeps-unix.c | 146 | ||||
-rw-r--r-- | src/ck-sysdeps.h | 10 | ||||
-rw-r--r-- | src/org.freedesktop.ConsoleKit.Session.xml | 13 | ||||
-rw-r--r-- | tools/Makefile.am | 10 | ||||
-rw-r--r-- | tools/ck-launch-session.c | 3 | ||||
-rw-r--r-- | tools/ck-remove-directory.c | 169 |
21 files changed, 874 insertions, 3 deletions
@@ -66,4 +66,5 @@ src/test-manager src/ck-collect-session-info tools/pam-foreground-compat.ck tools/70-udev-acl.rules +tools/ck-remove-directory doc/console-kit-daemon.1m diff --git a/configure.ac b/configure.ac index f8cd52f..0420f6b 100644 --- a/configure.ac +++ b/configure.ac @@ -61,11 +61,11 @@ POLKIT_REQUIRED_VERSION=0.92 AC_CHECK_HEADERS([unistd.h paths.h sys/vt.h sys/consio.h fcntl.h limits.h \ sys/ioctl.h sys/param.h sys/socket.h syslog.h kvm.h \ sys/sysctl.h sys/user.h poll.h libintl.h locale.h \ - sys/wait.h sys/resource.h]) + sys/wait.h sys/resource.h sys/mount.h sys/param.h ftw.h]) AC_CHECK_FUNCS([getpeerucred getpeereid memset setenv strchr strdup \ strerror strrchr strspn strstr strtol strtoul uname \ - setlocale]) + setlocale mount umount unmount]) AC_CHECK_MEMBERS([struct stat.st_rdev]) diff --git a/data/ConsoleKit.conf b/data/ConsoleKit.conf index 789da65..45b0917 100644 --- a/data/ConsoleKit.conf +++ b/data/ConsoleKit.conf @@ -145,6 +145,9 @@ send_member="GetUnixUser"/> <allow send_destination="org.freedesktop.ConsoleKit" send_interface="org.freedesktop.ConsoleKit.Session" + send_member="GetXDGRuntimeDir"/> + <allow send_destination="org.freedesktop.ConsoleKit" + send_interface="org.freedesktop.ConsoleKit.Session" send_member="GetX11Display"/> <allow send_destination="org.freedesktop.ConsoleKit" send_interface="org.freedesktop.ConsoleKit.Session" diff --git a/libck-connector/ck-connector.c b/libck-connector/ck-connector.c index 7f6f87f..416b28f 100644 --- a/libck-connector/ck-connector.c +++ b/libck-connector/ck-connector.c @@ -65,6 +65,8 @@ struct _CkConnector { int refcount; char *cookie; + char *ssid; + char *runtime_dir; dbus_bool_t session_created; DBusConnection *connection; }; @@ -181,6 +183,14 @@ _ck_connector_free (CkConnector *connector) free (connector->cookie); } + if (connector->ssid != NULL) { + free (connector->ssid); + } + + if (connector->runtime_dir != NULL) { + free (connector->runtime_dir); + } + free (connector); } @@ -242,6 +252,8 @@ ck_connector_new (void) connector->connection = NULL; connector->cookie = NULL; connector->session_created = FALSE; + connector->ssid = NULL; + connector->runtime_dir = NULL; oom: return connector; } @@ -589,6 +601,182 @@ ck_connector_get_cookie (CkConnector *connector) } } +static dbus_bool_t +ck_connector_get_ssid (CkConnector *connector, + DBusError *error) +{ + DBusError local_error; + DBusMessage *message; + DBusMessage *reply; + char *ssid; + dbus_bool_t ret; + + _ck_return_val_if_fail (connector != NULL, FALSE); + + reply = NULL; + message = NULL; + ssid = NULL; + ret = FALSE; + + if (!connector->session_created || connector->cookie == NULL) { + return ret; + } + + dbus_error_init (&local_error); + message = dbus_message_new_method_call ("org.freedesktop.ConsoleKit", + "/org/freedesktop/ConsoleKit/Manager", + "org.freedesktop.ConsoleKit.Manager", + "GetSessionForCookie"); + if (message == NULL) { + goto out; + } + + if (!dbus_message_append_args (message, + DBUS_TYPE_STRING, &(connector->cookie), + DBUS_TYPE_INVALID)) { + goto out; + } + + dbus_error_init (&local_error); + reply = dbus_connection_send_with_reply_and_block (connector->connection, + message, + -1, + &local_error); + if (reply == NULL) { + if (dbus_error_is_set (&local_error)) { + dbus_set_error (error, + CK_CONNECTOR_ERROR, + "Unable to get session for cookie: %s, no reply from dbus", + local_error.message); + dbus_error_free (&local_error); + goto out; + } + } + + if (!dbus_message_get_args (reply, error, + DBUS_TYPE_OBJECT_PATH, &ssid, + DBUS_TYPE_INVALID)) { + dbus_set_error (error, + CK_CONNECTOR_ERROR, + "Unable to get session for cookie: %s", + local_error.message); + dbus_error_free (&local_error); + goto out; + } + + connector->ssid = strdup (ssid); + if (connector->ssid == NULL) { + goto out; + } + + ret = TRUE; + +out: + if (reply != NULL) { + dbus_message_unref (reply); + } + + if (message != NULL) { + dbus_message_unref (message); + } + + return ret; +} + +/** + * Gets the XDG_RUNTIME_DIR for the current open session. + * Returns #NULL if no session is open. + * + * @returns a constant string with the XDG_RUNTIME_DIR. + */ +const char * +ck_connector_get_runtime_dir (CkConnector *connector, + DBusError *error) +{ + DBusError local_error; + DBusMessage *message; + char *runtime_dir; + DBusMessage *reply; + + _ck_return_val_if_fail (connector != NULL, NULL); + + if (!connector->session_created || connector->cookie == NULL) { + return NULL; + } + + /* If we already have the runtime dir, supply it again */ + if (connector->runtime_dir != NULL) { + return connector->runtime_dir; + } + + /* get the ssid if we don't already have it */ + if (connector->ssid == NULL) { + if (ck_connector_get_ssid (connector, error) == FALSE) { + return NULL; + } + } + + reply = NULL; + message = NULL; + runtime_dir = NULL; + + if (!connector->session_created || connector->cookie == NULL) { + return NULL; + } + + dbus_error_init (&local_error); + message = dbus_message_new_method_call ("org.freedesktop.ConsoleKit", + connector->ssid, + "org.freedesktop.ConsoleKit.Session", + "GetXDGRuntimeDir"); + if (message == NULL) { + goto out; + } + + dbus_error_init (&local_error); + reply = dbus_connection_send_with_reply_and_block (connector->connection, + message, + -1, + &local_error); + if (reply == NULL) { + if (dbus_error_is_set (&local_error)) { + dbus_set_error (error, + CK_CONNECTOR_ERROR, + "Unable to get runtime dir for session: %s", + local_error.message); + dbus_error_free (&local_error); + goto out; + } + } + + if (!dbus_message_get_args (reply, error, + DBUS_TYPE_STRING, &runtime_dir, + DBUS_TYPE_INVALID)) { + dbus_set_error (error, + CK_CONNECTOR_ERROR, + "Unable to get runtime dir for session: %s", + local_error.message); + dbus_error_free (&local_error); + goto out; + } + + connector->runtime_dir = strdup (runtime_dir); + if (connector->runtime_dir == NULL) { + goto out; + } + +out: + if (reply != NULL) { + dbus_message_unref (reply); + } + + if (message != NULL) { + dbus_message_unref (message); + } + + return connector->runtime_dir; +} + /** * Issues the CloseSession method call on the ConsoleKit manager * interface. diff --git a/libck-connector/ck-connector.h b/libck-connector/ck-connector.h index f692803..523b22b 100644 --- a/libck-connector/ck-connector.h +++ b/libck-connector/ck-connector.h @@ -56,6 +56,8 @@ dbus_bool_t ck_connector_open_session (CkConnector *ckc, DBusError *error); const char *ck_connector_get_cookie (CkConnector *ckc); +const char *ck_connector_get_runtime_dir (CkConnector *ckc, + DBusError *error); dbus_bool_t ck_connector_close_session (CkConnector *ckc, DBusError *error); diff --git a/libck-connector/test-connector.c b/libck-connector/test-connector.c index 66ddc61..3eb65c6 100644 --- a/libck-connector/test-connector.c +++ b/libck-connector/test-connector.c @@ -76,6 +76,18 @@ main (int argc, char *argv[]) } printf ("Session cookie is '%s'\n", ck_connector_get_cookie (connector)); + + if (ck_connector_get_runtime_dir (connector, &error) == NULL) { + if (dbus_error_is_set (&error)) { + printf ("Failed to get XDG_RUNTIME_DIR error is '%s'\n", error.message); + dbus_error_free (&error); + } else { + printf ("Cannot get XDG_RUNTIME_DIR, out of memory error\n"); + } + } else { + printf ("XDG_RUNTIME_DIR is '%s'\n", ck_connector_get_runtime_dir (connector, &error)); + } + sleep (20); dbus_error_init (&error); diff --git a/src/ck-manager.c b/src/ck-manager.c index 4a6e511..a72d5a1 100644 --- a/src/ck-manager.c +++ b/src/ck-manager.c @@ -1165,6 +1165,40 @@ get_system_num_users (CkManager *manager) return num_users; } +static gboolean +session_has_user (const char *ssid, + CkSession *session, + guint *unix_user) +{ + guint session_user; + + session_user = console_kit_session_get_unix_user (CONSOLE_KIT_SESSION (session)); + + if (session_user == *unix_user) { + g_debug ("Found session for user %d", *unix_user); + return TRUE; + } + + return FALSE; +} + +static const gchar * +get_runtime_dir_for_user (CkManager *manager, + guint unix_user) +{ + gpointer session; + + TRACE (); + + session = g_hash_table_find (manager->priv->sessions, (GHRFunc)session_has_user, &unix_user); + + if (session != NULL) { + return ck_session_get_runtime_dir (CK_SESSION (session)); + } + + return NULL; +} + #ifdef ENABLE_RBAC_SHUTDOWN static gboolean check_rbac_permissions (CkManager *manager, @@ -2584,6 +2618,8 @@ open_session_for_leader (CkManager *manager, CkSeat *seat; const char *ssid; const char *cookie; + char *runtime_dir; + guint unix_user; ssid = ck_session_leader_peek_session_id (leader); cookie = ck_session_leader_peek_cookie (leader); @@ -2598,6 +2634,20 @@ open_session_for_leader (CkManager *manager, return; } + unix_user = console_kit_session_get_unix_user (CONSOLE_KIT_SESSION (session)); + + /* If the user is already logged in, continue to use the same runtime dir. + * We need to do this before adding the session to the manager's table. */ + runtime_dir = g_strdup (get_runtime_dir_for_user (manager, unix_user)); + + /* otherwise generate a new one */ + if (runtime_dir == NULL) { + runtime_dir = ck_generate_runtime_dir_for_user (unix_user); + } + + g_debug ("XDG_RUNTIME_DIR is %s", runtime_dir); + ck_session_set_runtime_dir (session, runtime_dir); + /* If supported, add the session leader to a process group so we * can track it with something better than an environment variable */ pgroup = ck_process_group_get (); @@ -2631,6 +2681,7 @@ open_session_for_leader (CkManager *manager, manager); g_object_unref (session); + g_free (runtime_dir); g_dbus_method_invocation_return_value (context , g_variant_new ("(s)", cookie)); } @@ -3016,6 +3067,7 @@ remove_session_for_cookie (CkManager *manager, char *orig_ssid; CkSessionLeader *leader; char *sid; + guint unix_user; gboolean res; gboolean ret; @@ -3055,6 +3107,11 @@ remove_session_for_cookie (CkManager *manager, * for seat removals doesn't work. */ + /* Get the session's uid, we'll need this if we have to remove the + * runtime dir + */ + unix_user = console_kit_session_get_unix_user (CONSOLE_KIT_SESSION (orig_session)); + /* remove from seat */ sid = NULL; ck_session_get_seat_id (orig_session, &sid, NULL); @@ -3085,6 +3142,14 @@ remove_session_for_cookie (CkManager *manager, manager_update_system_idle_hint (manager); + if (get_runtime_dir_for_user (manager, unix_user) == NULL) { + /* We removed the session and now there's no runtime dir + * associated with that user. + * Remove the runtime dir from the system. + */ + ck_remove_runtime_dir_for_user (unix_user); + } + ret = TRUE; out: if (orig_session != NULL) { diff --git a/src/ck-session-leader.c b/src/ck-session-leader.c index 2b392b6..5fd4f96 100644 --- a/src/ck-session-leader.c +++ b/src/ck-session-leader.c @@ -62,6 +62,7 @@ struct CkSessionLeaderPrivate char *service_name; char *session_id; char *cookie; + char *runtime_dir; GList *pending_jobs; gboolean cancelled; GHashTable *override_parameters; diff --git a/src/ck-session.c b/src/ck-session.c index 1b58889..901422f 100644 --- a/src/ck-session.c +++ b/src/ck-session.c @@ -53,7 +53,7 @@ struct CkSessionPrivate char *id; char *cookie; char *seat_id; - + char *runtime_dir; char *login_session_id; GTimeVal creation_time; @@ -832,6 +832,58 @@ ck_session_set_seat_id (CkSession *session, return TRUE; } +/** + * ck_session_set_runtime_dir + * @session: CkSession object + * @runtime_dir: The XDG_RUNTIME_DIR for the user of this session. For details, see: + * http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + * returns: TRUE if successfully set, FALSE on failure. + **/ +gboolean +ck_session_set_runtime_dir (CkSession *session, + const char *runtime_dir) +{ + g_return_val_if_fail (CK_IS_SESSION (session), FALSE); + + g_free (session->priv->runtime_dir); + session->priv->runtime_dir = g_strdup (runtime_dir); + + return TRUE; +} + +/** + * ck_session_get_runtime_dir + * @session: CkSession object + * returns: The XDG_RUNTIME_DIR. For details, see: + * http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + **/ +const char * +ck_session_get_runtime_dir (CkSession *session) +{ + g_return_val_if_fail (CK_IS_SESSION (session), NULL); + return session->priv->runtime_dir; +} + +static gboolean +dbus_get_runtime_dir (ConsoleKitSession *cksession, + GDBusMethodInvocation *context) +{ + CkSession *session = CK_SESSION(cksession); + + TRACE (); + + g_return_val_if_fail (CK_IS_SESSION (cksession), FALSE); + + /* if no login session id is set return an empty string */ + if (session->priv->runtime_dir == NULL) { + throw_error (context, CK_SESSION_ERROR_FAILED, _("Failed to create the XDG_RUNTIME_DIR")); + return FALSE; + } + + console_kit_session_complete_get_xdgruntime_dir (cksession, context, session->priv->runtime_dir); + return TRUE; +} + static gboolean dbus_get_login_session_id (ConsoleKitSession *cksession, GDBusMethodInvocation *context) @@ -1087,6 +1139,7 @@ ck_session_iface_init (ConsoleKitSessionIface *iface) iface->handle_lock = dbus_lock; iface->handle_unlock = dbus_unlock; iface->handle_get_idle_hint = dbus_get_idle_hint; + iface->handle_get_xdgruntime_dir = dbus_get_runtime_dir; } static void @@ -1108,6 +1161,7 @@ ck_session_finalize (GObject *object) g_free (session->priv->id); g_free (session->priv->cookie); g_free (session->priv->login_session_id); + g_free (session->priv->runtime_dir); G_OBJECT_CLASS (ck_session_parent_class)->finalize (object); } @@ -1341,6 +1395,7 @@ ck_session_dump (CkSession *session, g_key_file_set_boolean (key_file, group_name, "is_active", console_kit_session_get_active (cksession)); g_key_file_set_boolean (key_file, group_name, "is_local", console_kit_session_get_is_local (cksession)); + g_key_file_set_string (key_file, group_name, "XDG_RUNTIME_DIR", NONULL_STRING (session->priv->runtime_dir)); s = g_time_val_to_iso8601 (&(session->priv->creation_time)); g_key_file_set_string (key_file, diff --git a/src/ck-session.h b/src/ck-session.h index d9b35cd..8750e2f 100644 --- a/src/ck-session.h +++ b/src/ck-session.h @@ -125,8 +125,12 @@ gboolean ck_session_get_login_session_id (CkSession *se gboolean ck_session_get_creation_time (CkSession *session, char **iso8601_datetime, GError **error); +const char * ck_session_get_runtime_dir (CkSession *session); +gboolean ck_session_set_runtime_dir (CkSession *session, + const char *runtime_dir); + gboolean ck_session_set_id (CkSession *session, const char *ssid, GError **error); diff --git a/src/ck-sysdeps-freebsd.c b/src/ck-sysdeps-freebsd.c index e7cbb04..b7a93c5 100644 --- a/src/ck-sysdeps-freebsd.c +++ b/src/ck-sysdeps-freebsd.c @@ -45,6 +45,15 @@ #endif #include <sys/consio.h> +#ifdef HAVE_SYS_MOUNT_H +#include <sys/mount.h> +#endif + +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + + #define DEV_ENCODE(M,m) ( \ ( (M&0xfff) << 8) | ( (m&0xfff00) << 12) | (m&0xff) \ ) @@ -598,3 +607,51 @@ ck_system_can_hybrid_sleep (void) /* TODO: not implemented */ return FALSE; } + +gboolean +ck_make_tmpfs (guint uid, guint gid, const gchar *dest) +{ +#ifdef HAVE_SYS_MOUNT_H + gchar *opts; + int result; + + TRACE (); + + opts = g_strdup_printf ("mode=0700,size=8M,uid=%d,guid=%d", uid, gid); + + result = mount("tmpfs", dest, 0, opts); + + g_free (opts); + + if (result == 0) { + return TRUE; + } else { + g_info ("Failed to create tmpfs mount, reason was: %s", strerror(errno)); + errno = 0; + return FALSE; + } +#endif + + return FALSE; +} + +gboolean +ck_remove_tmpfs (guint uid, const gchar *dest) +{ +#ifdef HAVE_SYS_MOUNT_H + int result; + + TRACE (); + + result = unmount(dest, 0); + + if (result == 0) { + return TRUE; + } + + g_info ("Failed to unmount tmpfs mount, reason was: %s", strerror(errno)); + errno = 0; +#endif + + return FALSE; +} diff --git a/src/ck-sysdeps-gnu.c b/src/ck-sysdeps-gnu.c index 9437559..2ff9b24 100644 --- a/src/ck-sysdeps-gnu.c +++ b/src/ck-sysdeps-gnu.c @@ -421,3 +421,15 @@ ck_system_can_hybrid_sleep (void) /* TODO: not implemented */ return FALSE; } + +gboolean +ck_make_tmpfs (guint uid, guint gid, const gchar *dest) +{ + return FALSE; +} + +gboolean +ck_remove_tmpfs (guint uid, const gchar *dest) +{ + return FALSE; +} diff --git a/src/ck-sysdeps-linux.c b/src/ck-sysdeps-linux.c index d3d1b5a..aea9ab1 100644 --- a/src/ck-sysdeps-linux.c +++ b/src/ck-sysdeps-linux.c @@ -39,6 +39,10 @@ #include <paths.h> #endif /* HAVE_PATHS_H */ +#ifdef HAVE_SYS_MOUNT_H +#include <sys/mount.h> +#endif + #include "ck-sysdeps.h" #ifndef ERROR @@ -900,6 +904,54 @@ ck_system_can_hybrid_sleep (void) return linux_supports_sleep_state ("suspend-hybrid"); } +gboolean +ck_make_tmpfs (guint uid, guint gid, const gchar *dest) +{ +#ifdef HAVE_SYS_MOUNT_H + gchar *opts; + int result; + + TRACE (); + + opts = g_strdup_printf ("mode=0700,size=8M,uid=%d,guid=%d", uid, gid); + + result = mount("none", dest, "tmpfs", 0, opts); + + g_free (opts); + + if (result == 0) { + return TRUE; + } else { + g_info ("Failed to create tmpfs mount, reason was: %s", strerror(errno)); + errno = 0; + return FALSE; + } +#endif + + return FALSE; +} + +gboolean +ck_remove_tmpfs (guint uid, const char* dest) +{ +#ifdef HAVE_SYS_MOUNT_H + int result; + + TRACE (); + + result = umount2(dest, MNT_DETACH); + + if (result == 0) { + return TRUE; + } + + g_info ("Failed to unmount tmpfs mount, reason was: %s", strerror(errno)); + errno = 0; +#endif + + return FALSE; +} + #ifdef HAVE_SYS_VT_SIGNAL /* For the moment this is Linux only. * Returns the vt file descriptor or < 0 on failure. diff --git a/src/ck-sysdeps-openbsd.c b/src/ck-sysdeps-openbsd.c index c924864..af1ee3a 100644 --- a/src/ck-sysdeps-openbsd.c +++ b/src/ck-sysdeps-openbsd.c @@ -43,6 +43,14 @@ #include <sys/stdint.h> #endif +#ifdef HAVE_SYS_MOUNT_H +#include <sys/mount.h> +#endif + +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + #include <dev/wscons/wsdisplay_usl_io.h> #define DEV_ENCODE(M,m) ( \ @@ -484,3 +492,51 @@ ck_system_can_hybrid_sleep (void) /* TODO: not implemented */ return FALSE; } + +gboolean +ck_make_tmpfs (guint uid, guint gid, const gchar *dest) +{ +#ifdef HAVE_SYS_MOUNT_H + gchar *opts; + int result; + + TRACE (); + + opts = g_strdup_printf ("mode=0700,size=8M,uid=%d,guid=%d", uid, gid); + + result = mount("tmpfs", dest, 0, opts); + + g_free (opts); + + if (result == 0) { + return TRUE; + } else { + g_info ("Failed to create tmpfs mount, reason was: %s", strerror(errno)); + errno = 0; + return FALSE; + } +#endif + + return FALSE; +} + +gboolean +ck_remove_tmpfs (guint uid, const gchar *dest) +{ +#ifdef HAVE_SYS_MOUNT_H + int result; + + TRACE (); + + result = unmount(dest, 0); + + if (result == 0) { + return TRUE; + } + + g_info ("Failed to unmount tmpfs mount, reason was: %s", strerror(errno)); + errno = 0; +#endif + + return FALSE; +} diff --git a/src/ck-sysdeps-solaris.c b/src/ck-sysdeps-solaris.c index 9d26380..0387fca 100644 --- a/src/ck-sysdeps-solaris.c +++ b/src/ck-sysdeps-solaris.c @@ -550,3 +550,15 @@ ck_system_can_hybrid_sleep (void) /* TODO: not implemented */ return FALSE; } + +gboolean +ck_make_tmpfs (guint uid, guint gid, const gchar *dest) +{ + return FALSE; +} + +gboolean +ck_remove_tmpfs (guint uid, const gchar *dest) +{ + return FALSE; +} diff --git a/src/ck-sysdeps-unix.c b/src/ck-sysdeps-unix.c index c24a0e1..a618388 100644 --- a/src/ck-sysdeps-unix.c +++ b/src/ck-sysdeps-unix.c @@ -31,6 +31,7 @@ #include <sys/stat.h> #include <sys/socket.h> #include <sys/ioctl.h> +#include <pwd.h> #ifdef __linux__ #include <linux/kd.h> @@ -56,6 +57,10 @@ #include <ucred.h> #endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + #include "ck-sysdeps.h" #ifndef ERROR @@ -328,6 +333,147 @@ ck_is_root_user (void) return FALSE; } +/* Call g_free on string when done using it. [transfer: full] */ +static gchar * +get_rundir (guint uid) +{ + const gchar *base; + + TRACE (); + + base = RUNDIR "/user"; + + return g_strdup_printf ("%s/%d", base, uid); +} + +static gboolean +create_rundir_base (guint uid) +{ + const gchar *base; + + TRACE (); + + base = RUNDIR "/user"; + + /* Create the base directory that we will own. */ + if (g_mkdir_with_parents (base, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0) { + g_warning ("Failed to create %s, reason was: %s", base, strerror(errno)); + errno = 0; + return FALSE; + } + + /* ensure we have ownership */ + if (chown (base, 0, 0) != 0) { + g_warning ("Failed to chown %s, reason was: %s", base, strerror(errno)); + errno = 0; + return FALSE; + } + + return TRUE; +} + +static gboolean +remove_rundir (guint uid, const gchar *dest) +{ + gchar *command; + GError *error = NULL; + gboolean res; + + TRACE (); + + g_return_val_if_fail (dest, FALSE); + + if (uid < 1) { + g_debug ("We didn't create a runtime dir for root, nothing to remove"); + return FALSE; + } + + command = g_strdup_printf (LIBEXECDIR "/ck-remove-directory --uid=%d --dest=%s", uid, dest); + + res = g_spawn_command_line_sync (command, NULL, NULL, NULL, &error); + + if (! res) { + g_warning ("Unable to remove user runtime dir '%s' error was: %s", dest, error->message); + g_clear_error (&error); + return FALSE; + } + + return TRUE; +} + +gchar * +ck_generate_runtime_dir_for_user (guint uid) +{ + gchar *dest; + struct passwd *pwent; + + TRACE (); + + if (uid < 1) { + g_debug ("We do not create runtime dirs for root"); + return NULL; + } + + errno = 0; + pwent = getpwuid (uid); + if (pwent == NULL) { + g_warning ("Unable to lookup UID: %s", g_strerror (errno)); + errno = 0; + return NULL; + } + + /* ensure we have created the base directory */ + if (create_rundir_base (uid) == FALSE) { + return NULL; + } + + dest = get_rundir (uid); + + /* Create the new directory */ + if (g_mkdir_with_parents (dest, S_IRWXU) != 0) { + g_warning ("Failed to create XDG_RUNTIME_DIR, reason was: %s", strerror(errno)); + errno = 0; + g_free (dest); + return NULL; + } + + g_debug ("setting uid %d, gid %d", uid, pwent->pw_gid); + + /* assign ownership to the user */ + if (chown (dest, uid, pwent->pw_gid) != 0) { + g_warning ("Failed to chown XDG_RUNTIME_DIR, reason was: %s", strerror(errno)); + errno = 0; + g_free (dest); + return NULL; + } + + /* attempt to make it a small tmpfs location */ + ck_make_tmpfs (uid, pwent->pw_gid, dest); + + return dest; +} + +gboolean +ck_remove_runtime_dir_for_user (guint uid) +{ + gchar *dest; + + TRACE (); + + dest = get_rundir (uid); + + /* attempt to remove the tmpfs */ + ck_remove_tmpfs (uid, dest); + + /* remove the user's runtime dir now that all user sessions + * are gone */ + remove_rundir (uid, dest); + + g_free (dest); + + return TRUE; +} + gboolean ck_wait_for_active_console_num (int console_fd, guint num) diff --git a/src/ck-sysdeps.h b/src/ck-sysdeps.h index bc32631..0390cb4 100644 --- a/src/ck-sysdeps.h +++ b/src/ck-sysdeps.h @@ -60,6 +60,16 @@ gboolean ck_fd_is_a_console (int fd); gboolean ck_is_root_user (void); +gchar * ck_generate_runtime_dir_for_user (guint uid); + +gboolean ck_remove_runtime_dir_for_user (guint uid); + +gboolean ck_make_tmpfs (guint uid, + guint gid, + const gchar *dest); +gboolean ck_remove_tmpfs (guint uid, + const gchar *dest); + gboolean ck_get_max_num_consoles (guint *num); gboolean ck_supports_activatable_consoles (void); diff --git a/src/org.freedesktop.ConsoleKit.Session.xml b/src/org.freedesktop.ConsoleKit.Session.xml index 4f5c268..2330de4 100644 --- a/src/org.freedesktop.ConsoleKit.Session.xml +++ b/src/org.freedesktop.ConsoleKit.Session.xml @@ -78,6 +78,19 @@ <doc:seealso><doc:ref type="property" to="Session:unix-user">unix-user</doc:ref></doc:seealso> </doc:doc> </method> + <method name="GetXDGRuntimeDir"> + <arg name="xdg_runtime_dir" direction="out" type="s"> + <doc:doc> + <doc:summary>XDG_RUNTIME_DIR</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description><doc:para>Returns the XDG_RUNTIME_DIR location of the session. + The XDG_RUNTIME_DIR is the same for all sessions of the same user and will be automatically + removed once the last session of the user is closed.</doc:para> + </doc:description> + </doc:doc> + </method> <method name="GetX11Display"> <arg name="display" direction="out" type="s"> <doc:doc> diff --git a/tools/Makefile.am b/tools/Makefile.am index 07d02e3..a181033 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -129,6 +129,7 @@ libexec_PROGRAMS = \ ck-collect-session-info \ ck-get-x11-server-pid \ ck-get-x11-display-device \ + ck-remove-directory \ $(NULL) ck_collect_session_info_SOURCES = \ @@ -159,6 +160,15 @@ ck_get_x11_display_device_LDADD = \ $(top_builddir)/src/libck.la \ $(NULL) +ck_remove_directory_SOURCES = \ + ck-remove-directory.c \ + $(NULL) + +ck_remove_directory_LDADD = \ + $(TOOLS_LIBS) \ + $(top_builddir)/src/libck.la \ + $(NULL) + if ENABLE_UDEV_ACL udevrulesdir = $(UDEVDIR)/rules.d diff --git a/tools/ck-launch-session.c b/tools/ck-launch-session.c index 4200da8..427e07b 100644 --- a/tools/ck-launch-session.c +++ b/tools/ck-launch-session.c @@ -76,6 +76,9 @@ main (int argc, char **argv) case 0: setenv ("XDG_SESSION_COOKIE", ck_connector_get_cookie (ckc), 1); + + setenv ("XDG_RUNTIME_DIR", + ck_connector_get_runtime_dir (ckc, &error), 1); break; default: waitpid (pid, &status, 0); diff --git a/tools/ck-remove-directory.c b/tools/ck-remove-directory.c new file mode 100644 index 0000000..cf44fd6 --- /dev/null +++ b/tools/ck-remove-directory.c @@ -0,0 +1,169 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (c) 2015, Eric Koegel <eric.koegel@gmail.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <unistd.h> +#include <pwd.h> +#include <errno.h> +#include <libintl.h> +#include <locale.h> +#include <ftw.h> + +#include <glib.h> +#include <glib/gi18n.h> + +#include "ck-sysdeps.h" + + +static void +become_user (uid_t uid, const gchar* dest) +{ + int res; + struct passwd *pwent; + + if (uid < 1) { + g_critical ("invalid UID"); + exit (1); + } + + if (dest == NULL) { + g_critical ("invalid dest"); + exit (1); + } + + errno = 0; + pwent = getpwuid (uid); + if (pwent == NULL) { + g_warning ("Unable to lookup UID: %s", g_strerror (errno)); + exit (1); + } + + /* set the group */ + errno = 0; + res = setgid (pwent->pw_gid); + if (res == -1) { + g_warning ("Error performing setgid: %s", g_strerror (errno)); + exit (1); + } + + /* become the user */ + errno = 0; + res = setuid (uid); + if (res == -1) { + g_warning ("Error performing setuid: %s", g_strerror (errno)); + exit (1); + } +} + +static int +unlink_cb (const char *fpath, + const struct stat *sb, + int typeflag, + struct FTW *ftwbuf) +{ + int ret = remove (fpath); + + if (ret) { + g_error ("Failed to remove %s, reason was: %s", fpath, strerror(errno)); + errno = 0; + } + + return ret; +} + +static int +remove_dest_dir (const gchar *dest) +{ + return nftw(dest, unlink_cb, 64, FTW_DEPTH | FTW_PHYS); +} + +int +main (int argc, + char **argv) +{ + GOptionContext *context; + gboolean ret; + GError *error; + static int user_id = -1; + static gchar *dest = NULL; + static GOptionEntry entries [] = { + { "uid", 0, 0, G_OPTION_ARG_INT, &user_id, N_("User ID"), NULL }, + { "dest", 0, 0, G_OPTION_ARG_STRING, &dest, N_("Destination to remove"), NULL }, + { NULL } + }; + + /* Setup for i18n */ + setlocale(LC_ALL, ""); + +#ifdef ENABLE_NLS + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); +#endif + + /* For now at least restrict this to root */ + if (getuid () != 0) { + g_warning (_("You must be root to run this program")); + exit (1); + } + + context = g_option_context_new (NULL); + g_option_context_add_main_entries (context, entries, NULL); + error = NULL; + ret = g_option_context_parse (context, &argc, &argv, &error); + g_option_context_free (context); + + if (! ret) { + g_warning ("%s", error->message); + g_error_free (error); + exit (1); + } + + if (user_id < 1) { + g_warning ("Invalid UID"); + exit (1); + } + + /* Ensure we have a dest and that it starts with the correct prefix + * so we don't remove something important. + */ + if (dest == NULL || !g_str_has_prefix (dest, RUNDIR "/user/")) { + g_warning ("Invalid Dest"); + exit (1); + } + + become_user (user_id, dest); + + ret = remove_dest_dir (dest); + + return ret != TRUE; +} |