diff options
author | Pavel Šimerda <psimerda@redhat.com> | 2013-07-30 23:09:52 +0200 |
---|---|---|
committer | Pavel Šimerda <psimerda@redhat.com> | 2015-01-05 18:39:46 +0100 |
commit | 0de60b300ed6cc0fc94c742396112b1274989371 (patch) | |
tree | df7d7f4c06ccb698f481246ff6389c5f01bc6b8b /src/nm-session-monitor.c | |
parent | eb2eda4408667395ffb73ec94fc5a02f311b66f5 (diff) | |
download | NetworkManager-0de60b300ed6cc0fc94c742396112b1274989371.tar.gz |
session: merge nm-session-monitor-* modules
Merged all session tracking modules into one source file and simplified
it substantially. Now systemd-logind and ConsoleKit support can be built
in at the same time and both are detected at runtime. This is useful on
source based as well as binary distributions.
Original patch written by Fabio Erculiani <lxnay@sabayon.org>, modified
by Pavel Šimerda <psimerda@redhat.com> and Thomas Haller <thaller@redhat.com>.
https://bugzilla.gnome.org/show_bug.cgi?id=686997
Acked-By: Thomas Haller <thaller@redhat.com>
Diffstat (limited to 'src/nm-session-monitor.c')
-rw-r--r-- | src/nm-session-monitor.c | 338 |
1 files changed, 333 insertions, 5 deletions
diff --git a/src/nm-session-monitor.c b/src/nm-session-monitor.c index 6ba0bb92f8..36d40095e2 100644 --- a/src/nm-session-monitor.c +++ b/src/nm-session-monitor.c @@ -14,13 +14,279 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * (C) Copyright 2008 - 2015 Red Hat, Inc. + * Author: David Zeuthen <davidz@redhat.com> * Author: Dan Williams <dcbw@redhat.com> + * Author: Matthias Clasen * Author: Pavel Šimerda <psimerda@redhat.com> */ +#include "config.h" + #include <pwd.h> -#include <sys/types.h> +#include <errno.h> +#include <string.h> +#include <sys/stat.h> +#include <gio/gio.h> #include "nm-session-monitor.h" +#include "nm-logging.h" + +#ifdef SESSION_TRACKING_SYSTEMD +#include <systemd/sd-login.h> +#endif + +/********************************************************************/ + +/* <internal> + * SECTION:nm-session-monitor + * @title: NMSessionMonitor + * @short_description: Monitor sessions + * + * The #NMSessionMonitor class is a utility class to track and monitor sessions. + */ +struct _NMSessionMonitor { + GObject parent_instance; + +#ifdef SESSION_TRACKING_SYSTEMD + struct { + sd_login_monitor *monitor; + guint watch; + } sd; +#endif + +#ifdef SESSION_TRACKING_CONSOLEKIT + struct { + GFileMonitor *monitor; + GHashTable *cache; + time_t timestamp; + } ck; +#endif +}; + +struct _NMSessionMonitorClass { + GObjectClass parent_class; + + void (*changed) (NMSessionMonitor *monitor); +}; + +G_DEFINE_TYPE (NMSessionMonitor, nm_session_monitor, G_TYPE_OBJECT); + +enum { + CHANGED, + LAST_SIGNAL, +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +/********************************************************************/ + +#ifdef SESSION_TRACKING_SYSTEMD +static gboolean +sd_session_exists (NMSessionMonitor *monitor, uid_t uid, gboolean active) +{ + int status; + + if (!monitor->sd.monitor) + return FALSE; + + status = sd_uid_get_sessions (uid, active, NULL); + + if (status < 0) + nm_log_err (LOGD_CORE, "Failed to get systemd sessions for uid %d: %d", + uid, status); + + return status > 0; +} + +static gboolean +sd_changed (GIOChannel *stream, GIOCondition condition, gpointer user_data) +{ + NMSessionMonitor *monitor = user_data; + + g_signal_emit (monitor, signals[CHANGED], 0); + + sd_login_monitor_flush (monitor->sd.monitor); + + return TRUE; +} + +static void +sd_init (NMSessionMonitor *monitor) +{ + int status; + GIOChannel *stream; + + if (!g_file_test ("/run/systemd/seats/", G_FILE_TEST_EXISTS)) + return; + + if ((status = sd_login_monitor_new (NULL, &monitor->sd.monitor)) < 0) { + nm_log_err (LOGD_CORE, "Failed to create systemd login monitor: %d", status); + return; + } + + stream = g_io_channel_unix_new (sd_login_monitor_get_fd (monitor->sd.monitor)); + monitor->sd.watch = g_io_add_watch (stream, G_IO_IN, sd_changed, monitor); + + g_io_channel_unref (stream); +} + +static void +sd_finalize (NMSessionMonitor *monitor) +{ + g_clear_pointer (&monitor->sd.monitor, sd_login_monitor_unref); + g_source_remove (monitor->sd.watch); +} +#endif /* SESSION_TRACKING_SYSTEMD */ + +/********************************************************************/ + +#ifdef SESSION_TRACKING_CONSOLEKIT +typedef struct { + gboolean active; +} CkSession; + +static gboolean +ck_load_cache (GHashTable *cache) +{ + GKeyFile *keyfile = g_key_file_new (); + char **groups = NULL; + GError *error = NULL; + gsize i, len; + gboolean finished = FALSE; + + if (!g_key_file_load_from_file (keyfile, CKDB_PATH, G_KEY_FILE_NONE, &error)) + goto out; + + if (!(groups = g_key_file_get_groups (keyfile, &len))) { + nm_log_err (LOGD_CORE, "Could not load groups from " CKDB_PATH); + goto out; + } + + g_hash_table_remove_all (cache); + + for (i = 0; i < len; i++) { + guint uid = G_MAXUINT; + CkSession session = { .active = FALSE }; + + if (!g_str_has_prefix (groups[i], "CkSession ")) + continue; + + uid = g_key_file_get_integer (keyfile, groups[i], "uid", &error); + if (error) + goto out; + + session.active = g_key_file_get_boolean (keyfile, groups[i], "is_active", &error); + if (error) + goto out; + + g_hash_table_insert (cache, GUINT_TO_POINTER (uid), g_memdup (&session, sizeof session)); + } + + finished = TRUE; +out: + if (error) + nm_log_err (LOGD_CORE, "ConsoleKit: Failed to load database: %s", error->message); + g_clear_error (&error); + g_clear_pointer (&groups, g_strfreev); + g_clear_pointer (&keyfile, g_key_file_free); + + return finished; +} + +static gboolean +ck_update_cache (NMSessionMonitor *monitor) +{ + struct stat statbuf; + + if (!monitor->ck.cache) + return FALSE; + + /* Check the database file */ + if (stat (CKDB_PATH, &statbuf) != 0) { + nm_log_err (LOGD_CORE, "Failed to check ConsoleKit timestamp: %s", strerror (errno)); + return FALSE; + } + if (statbuf.st_mtime == monitor->ck.timestamp) + return TRUE; + + /* Update the cache */ + if (!ck_load_cache (monitor->ck.cache)) + return FALSE; + + monitor->ck.timestamp = statbuf.st_mtime; + + return TRUE; +} + +static gboolean +ck_session_exists (NMSessionMonitor *monitor, uid_t uid, gboolean active) +{ + CkSession *session; + + if (!ck_update_cache (monitor)) + return FALSE; + + session = g_hash_table_lookup (monitor->ck.cache, GUINT_TO_POINTER (uid)); + + if (!session) + return FALSE; + if (active && !session->active) + return FALSE; + + return TRUE; +} + +static void +ck_changed (GFileMonitor * file_monitor, + GFile * file, + GFile * other_file, + GFileMonitorEvent event_type, + gpointer user_data) +{ + g_signal_emit (user_data, signals[CHANGED], 0); +} + +static void +ck_init (NMSessionMonitor *monitor) +{ + GFile *file = g_file_new_for_path (CKDB_PATH); + GError *error = NULL; + + if (g_file_query_exists (file, NULL)) { + if ((monitor->ck.monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &error))) { + monitor->ck.cache = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); + g_signal_connect (monitor->ck.monitor, + "changed", + G_CALLBACK (ck_changed), + monitor); + } else { + nm_log_err (LOGD_CORE, "Error monitoring " CKDB_PATH ": %s", error->message); + g_clear_error (&error); + } + } + + g_object_unref (file); +} + +static void +ck_finalize (NMSessionMonitor *monitor) +{ + g_clear_pointer (&monitor->ck.cache, g_hash_table_unref); + g_clear_object (&monitor->ck.monitor); +} +#endif /* SESSION_TRACKING_CONSOLEKIT */ + +/********************************************************************/ + +static NMSessionMonitor * +nm_session_monitor_get (void) +{ + static NMSessionMonitor *singleton = NULL; + + if (!singleton) + singleton = NM_SESSION_MONITOR (g_object_new (NM_TYPE_SESSION_MONITOR, NULL)); + + return singleton; +} /** * nm_session_monitor_connect: @@ -112,8 +378,70 @@ nm_session_monitor_user_to_uid (const char *user, uid_t *out_uid) gboolean nm_session_monitor_session_exists (uid_t uid, gboolean active) { - if (active) - return nm_session_monitor_uid_active (nm_session_monitor_get (), uid, NULL); - else - return nm_session_monitor_uid_has_session (nm_session_monitor_get (), uid, NULL, NULL); + NMSessionMonitor *monitor = nm_session_monitor_get (); + +#ifdef SESSION_TRACKING_SYSTEMD + if (sd_session_exists (monitor, uid, active)) + return TRUE; +#endif + +#ifdef SESSION_TRACKING_CONSOLEKIT + if (ck_session_exists (monitor, uid, active)) + return TRUE; +#endif + + return FALSE; +} + +/********************************************************************/ + +static void +nm_session_monitor_init (NMSessionMonitor *monitor) +{ +#ifdef SESSION_TRACKING_SYSTEMD + sd_init (monitor); +#endif + +#ifdef SESSION_TRACKING_CONSOLEKIT + ck_init (monitor); +#endif +} + +static void +nm_session_monitor_finalize (GObject *object) +{ +#ifdef SESSION_TRACKING_SYSTEMD + sd_finalize (NM_SESSION_MONITOR (object)); +#endif + +#ifdef SESSION_TRACKING_CONSOLEKIT + ck_finalize (NM_SESSION_MONITOR (object)); +#endif + + if (G_OBJECT_CLASS (nm_session_monitor_parent_class)->finalize != NULL) + G_OBJECT_CLASS (nm_session_monitor_parent_class)->finalize (object); +} + +static void +nm_session_monitor_class_init (NMSessionMonitorClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = nm_session_monitor_finalize; + + /** + * NMSessionMonitor::changed: + * @monitor: A #NMSessionMonitor + * + * Emitted when something changes. + */ + signals[CHANGED] = g_signal_new (NM_SESSION_MONITOR_CHANGED, + NM_TYPE_SESSION_MONITOR, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NMSessionMonitorClass, changed), + NULL, /* accumulator */ + NULL, /* accumulator data */ + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); } |