diff options
Diffstat (limited to 'src/vdagentd/systemd-login.c')
-rw-r--r-- | src/vdagentd/systemd-login.c | 347 |
1 files changed, 347 insertions, 0 deletions
diff --git a/src/vdagentd/systemd-login.c b/src/vdagentd/systemd-login.c new file mode 100644 index 0000000..9719c0b --- /dev/null +++ b/src/vdagentd/systemd-login.c @@ -0,0 +1,347 @@ +/* systemd-login.c vdagentd libsystemd-login integration code + + Copyright 2012 Red Hat, Inc. + + Red Hat Authors: + Hans de Goede <hdegoede@redhat.com> + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "session-info.h" +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <systemd/sd-login.h> +#include <dbus/dbus.h> + +struct session_info { + int verbose; + sd_login_monitor *mon; + char *session; + struct { + DBusConnection *system_connection; + char *match_session_signals; + } dbus; + gboolean session_is_locked; + gboolean session_locked_hint; +}; + +#define LOGIND_INTERFACE "org.freedesktop.login1" + +#define LOGIND_SESSION_INTERFACE "org.freedesktop.login1.Session" +#define LOGIND_SESSION_OBJ_TEMPLATE "/org/freedesktop/login1/session/_3%s" + +#define DBUS_PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties" + +#define SESSION_SIGNAL_LOCK "Lock" +#define SESSION_SIGNAL_UNLOCK "Unlock" + +#define SESSION_PROP_LOCKED_HINT "LockedHint" + +/* dbus related */ +static DBusConnection *si_dbus_get_system_bus(void) +{ + DBusConnection *connection; + DBusError error; + + dbus_error_init(&error); + connection = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error); + if (connection == NULL || dbus_error_is_set(&error)) { + if (dbus_error_is_set(&error)) { + syslog(LOG_WARNING, "Unable to connect to system bus: %s", + error.message); + dbus_error_free(&error); + } else { + syslog(LOG_WARNING, "Unable to connect to system bus"); + } + return NULL; + } + return connection; +} + +static void si_dbus_match_remove(struct session_info *si) +{ + DBusError error; + if (si->dbus.match_session_signals == NULL) + return; + + dbus_error_init(&error); + dbus_bus_remove_match(si->dbus.system_connection, + si->dbus.match_session_signals, + &error); + + g_free(si->dbus.match_session_signals); + si->dbus.match_session_signals = NULL; +} + +static void si_dbus_match_rule_update(struct session_info *si) +{ + DBusError error; + + if (si->dbus.system_connection == NULL || + si->session == NULL) + return; + + si_dbus_match_remove(si); + + si->dbus.match_session_signals = + g_strdup_printf ("type='signal',interface='%s',path='" + LOGIND_SESSION_OBJ_TEMPLATE"'", + LOGIND_SESSION_INTERFACE, + si->session); + if (si->verbose) + syslog(LOG_DEBUG, "logind match: %s", si->dbus.match_session_signals); + + dbus_error_init(&error); + dbus_bus_add_match(si->dbus.system_connection, + si->dbus.match_session_signals, + &error); + if (dbus_error_is_set(&error)) { + syslog(LOG_WARNING, "Unable to add dbus rule match: %s", + error.message); + dbus_error_free(&error); + g_free(si->dbus.match_session_signals); + si->dbus.match_session_signals = NULL; + } +} + +static void +si_dbus_read_properties(struct session_info *si) +{ + dbus_bool_t locked_hint, ret; + DBusMessageIter iter, iter_variant; + gint type; + DBusError error; + DBusMessage *message = NULL; + DBusMessage *reply = NULL; + gchar *session_object; + const gchar *interface, *property; + + if (si->session == NULL) + return; + + session_object = g_strdup_printf(LOGIND_SESSION_OBJ_TEMPLATE, si->session); + message = dbus_message_new_method_call(LOGIND_INTERFACE, + session_object, + DBUS_PROPERTIES_INTERFACE, + "Get"); + g_free (session_object); + if (message == NULL) { + syslog(LOG_ERR, "Unable to create dbus message"); + goto exit; + } + + interface = LOGIND_SESSION_INTERFACE; + property = SESSION_PROP_LOCKED_HINT; + ret = dbus_message_append_args(message, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_STRING, &property, + DBUS_TYPE_INVALID); + if (!ret) { + syslog(LOG_ERR, "Unable to request locked-hint"); + goto exit; + } + + dbus_error_init(&error); + reply = dbus_connection_send_with_reply_and_block(si->dbus.system_connection, + message, + -1, + &error); + if (reply == NULL) { + if (dbus_error_is_set(&error)) { + syslog(LOG_ERR, "Properties.Get failed (locked-hint) due %s", error.message); + dbus_error_free(&error); + } else { + syslog(LOG_ERR, "Properties.Get failed (locked-hint)"); + } + goto exit; + } + + dbus_message_iter_init(reply, &iter); + type = dbus_message_iter_get_arg_type(&iter); + if (type != DBUS_TYPE_VARIANT) { + syslog(LOG_ERR, "expected a variant, got a '%c' instead", type); + goto exit; + } + + dbus_message_iter_recurse(&iter, &iter_variant); + type = dbus_message_iter_get_arg_type(&iter_variant); + if (type != DBUS_TYPE_BOOLEAN) { + syslog(LOG_ERR, "expected a boolean, got a '%c' instead", type); + goto exit; + } + dbus_message_iter_get_basic(&iter_variant, &locked_hint); + + si->session_locked_hint = (locked_hint) ? TRUE : FALSE; +exit: + if (reply != NULL) { + dbus_message_unref(reply); + } + + if (message != NULL) { + dbus_message_unref(message); + } +} + +static void +si_dbus_read_signals(struct session_info *si) +{ + DBusMessage *message = NULL; + + dbus_connection_read_write(si->dbus.system_connection, 0); + message = dbus_connection_pop_message(si->dbus.system_connection); + while (message != NULL) { + const char *member; + + member = dbus_message_get_member (message); + if (g_strcmp0(member, SESSION_SIGNAL_LOCK) == 0) { + si->session_is_locked = TRUE; + } else if (g_strcmp0(member, SESSION_SIGNAL_UNLOCK) == 0) { + si->session_is_locked = FALSE; + } else { + if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL) { + syslog(LOG_WARNING, "(systemd-login) received non signal message"); + } else if (si->verbose) { + syslog(LOG_DEBUG, "(systemd-login) Signal not handled: %s", member); + } + } + + dbus_message_unref(message); + dbus_connection_read_write(si->dbus.system_connection, 0); + message = dbus_connection_pop_message(si->dbus.system_connection); + } +} + +struct session_info *session_info_create(int verbose) +{ + struct session_info *si; + int r; + + si = calloc(1, sizeof(*si)); + if (!si) + return NULL; + + si->verbose = verbose; + si->session_is_locked = FALSE; + + r = sd_login_monitor_new("session", &si->mon); + if (r < 0) { + syslog(LOG_ERR, "Error creating login monitor: %s", strerror(-r)); + free(si); + return NULL; + } + + si->dbus.system_connection = si_dbus_get_system_bus(); + return si; +} + +void session_info_destroy(struct session_info *si) +{ + if (!si) + return; + + si_dbus_match_remove(si); + dbus_connection_close(si->dbus.system_connection); + sd_login_monitor_unref(si->mon); + free(si->session); + free(si); +} + +int session_info_get_fd(struct session_info *si) +{ + return sd_login_monitor_get_fd(si->mon); +} + +const char *session_info_get_active_session(struct session_info *si) +{ + int r; + char *old_session = si->session; + + si->session = NULL; + r = sd_seat_get_active("seat0", &si->session, NULL); + /* ENOENT happens when a seat is switching from one session to another */ + if (r < 0 && r != -ENOENT) + syslog(LOG_ERR, "Error getting active session: %s", + strerror(-r)); + + if (si->verbose && si->session && + (!old_session || strcmp(old_session, si->session))) + syslog(LOG_INFO, "Active session: %s", si->session); + + sd_login_monitor_flush(si->mon); + free(old_session); + + si_dbus_match_rule_update(si); + return si->session; +} + +char *session_info_session_for_pid(struct session_info *si, uint32_t pid) +{ + int r; + char *session = NULL; + + r = sd_pid_get_session(pid, &session); + if (r < 0) + syslog(LOG_ERR, "Error getting session for pid %u: %s", + pid, strerror(-r)); + else if (si->verbose) + syslog(LOG_INFO, "Session for pid %u: %s", pid, session); + + return session; +} + +gboolean session_info_session_is_locked(struct session_info *si) +{ + gboolean locked; + + g_return_val_if_fail (si != NULL, FALSE); + + si_dbus_read_signals(si); + si_dbus_read_properties(si); + + locked = (si->session_is_locked || si->session_locked_hint); + if (si->verbose) { + syslog(LOG_DEBUG, "(systemd-login) session is locked: %s", + locked ? "yes" : "no"); + } + return locked; +} + +/* This function should only be called after session_info_get_active_session + * in order to verify if active session belongs to user (non greeter) */ +gboolean session_info_is_user(struct session_info *si) +{ + gchar *class = NULL; + gboolean ret; + + g_return_val_if_fail (si != NULL, TRUE); + g_return_val_if_fail (si->session != NULL, TRUE); + + if (sd_session_get_class(si->session, &class) != 0) { + syslog(LOG_WARNING, "Unable to get class from session: %s", + si->session); + return TRUE; + } + + if (si->verbose) + syslog(LOG_DEBUG, "(systemd-login) class for %s is %s", + si->session, class); + + ret = (g_strcmp0(class, "user") == 0); + g_free(class); + + return ret; +} |