* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. */
-#undef LOG_TAG
-#define LOG_TAG "SESSIOND"
-#include <dlog.h>
-
-#include <algorithm>
-#include <unordered_map>
-
-#include <gio/gio.h>
-
-#include "sessiond-internal.h"
-#include "globals.hpp"
-#include "fs_helpers.hpp"
-#include "tuple_g_variant_helpers.hpp"
-#include "tuple_hash.hpp"
-#include "wait_manager.hpp"
-#include "dir_backend_regular_dir.hpp"
-#include "dir_backend_fixed_size.hpp"
-
-static constexpr const char * get_dbus_error_mapping (subsession_error_e subsession_error)
-{
- /* I want this function to be evaluated at compile time
- * and `std::find_if` is not constexpr/consteval-friendly,
- * so caveman style iteration it is. */
-
- const char *ret = nullptr;
- for (const auto & x : error_mappings)
- if (x.second == subsession_error)
- ret = x.first;
-
- return ret ?: throw std::logic_error("Compile time get_dbus_error_mapping error");
-}
-
-using namespace std::string_view_literals;
-
-static bool is_subsession_size_limit_valid (unsigned size_kB)
-{
- static constexpr int MINIMUM_FOR_EXT2_OVERHEAD = 220; // determined empirically
- if (size_kB < MINIMUM_FOR_EXT2_OVERHEAD)
- return false;
-
- return true;
-}
-
-struct introspection_data {
- introspection_data(std::string_view xml)
- {
- inner = g_dbus_node_info_new_for_xml(xml.data(), nullptr);
- if (inner == nullptr)
- throw std::runtime_error("Couldn't parse introspection data");
- }
-
- ~introspection_data()
- {
- if (inner)
- g_dbus_node_info_unref(inner);
- }
-
- // Because of the rule of three, we also have to delete these.
- introspection_data(const introspection_data &) = delete;
- introspection_data &operator=(const introspection_data &) = delete;
-
- GDBusNodeInfo *inner;
-};
-
-struct owner_id {
- owner_id() : inner(0) {}
-
- void own_name_on_connection(GDBusConnection *connection, std::string_view name,
- GBusNameAcquiredCallback on_name_acquired, GBusNameLostCallback on_name_lost, gpointer user_data)
- {
- inner = g_bus_own_name_on_connection(connection, name.data(), G_BUS_NAME_OWNER_FLAGS_NONE,
- on_name_acquired, on_name_lost, user_data, nullptr);
- LOGD("Acquiring %s", name.data());
- }
-
- ~owner_id()
- {
- if (inner != 0)
- g_bus_unown_name(inner);
- }
-
- // Because of the rule of three, we also have to delete these.
- owner_id(const owner_id &) = delete;
- owner_id &operator=(const owner_id &) = delete;
-
- guint inner;
-};
-
-struct main_loop {
- main_loop()
- {
- inner = g_main_loop_new(nullptr, false);
- }
-
- ~main_loop()
- {
- g_main_loop_unref(inner);
- }
-
- // Because of the rule of three, we also have to delete these.
- main_loop(const main_loop &) = delete;
- main_loop &operator=(const main_loop &) = delete;
-
- GMainLoop *inner;
-};
-
-struct sessiond_context {
- sessiond_context() : data(xml), id(), loop()
- {
- connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL);
- if (!connection)
- throw std::runtime_error("Cannot connect to the system bus");
-
- guint registration_id = g_dbus_connection_register_object(
- connection, bus_object.data(), data.inner->interfaces[0], &table, this, nullptr, nullptr);
- if (registration_id <= 0)
- throw std::runtime_error("Couldn't register bus ID");
-
- id.own_name_on_connection(connection, bus_name, glib_name_acquired, glib_name_lost, this);
- }
-
- void run()
- {
- g_main_loop_run(loop.inner);
- }
-
- void on_name_acquired()
- {
- LOGD("Bus name acquired");
- }
-
- void on_name_lost()
- {
- throw std::runtime_error("Bus name lost");
- }
-
- bool check_parameters_invalid(GDBusMethodInvocation *invocation,
- const int session_uid, const std::string_view subsession_id)
- {
- if (session_uid <= 0) {
- g_dbus_method_invocation_return_dbus_error(invocation,
- get_dbus_error_mapping(SUBSESSION_ERROR_INVALID_PARAMETER), "Negative UID passed");
- return true;
- }
-
- if (!check_subsession_id_valid(subsession_id)) {
- g_dbus_method_invocation_return_dbus_error(invocation,
- get_dbus_error_mapping(SUBSESSION_ERROR_INVALID_PARAMETER), "Incorrect subsession_id passed");
- return true;
- }
-
- return false;
- }
-
- void do_add_user(int session_uid, const std::string& subsession_id, const DirBackendAdd& backend)
- {
- GError *err = nullptr;
- if (!g_dbus_connection_emit_signal(connection, nullptr, bus_object.data(), bus_iface.data(), "AddUserStarted",
- vals_to_g_variant(session_uid, subsession_id), &err))
- g_error_throw(err, "Failed to emit a signal: ");
-
- add_user_subsession(session_uid, subsession_id, backend);
-
- wait_add.try_emplace(session_uid, session_uid, connection, "AddUserCompleted");
- wait_add.at(session_uid).on_start(subsession_id, { });
- }
-
- void do_remove_user(int session_uid, const std::string& subsession_id)
- {
- GError *err = nullptr;
- if (!g_dbus_connection_emit_signal(connection, nullptr, bus_object.data(), bus_iface.data(), "RemoveUserStarted",
- vals_to_g_variant(session_uid, subsession_id), &err))
- g_error_throw(err, "Failed to emit a signal: ");
-
- remove_user_subsession(session_uid, subsession_id);
-
- wait_remove.try_emplace(session_uid, session_uid, connection, "RemoveUserCompleted");
- wait_remove.at(session_uid).on_start(subsession_id, { });
- }
-
- bool do_switch_user(int session_uid, const std::string& next_subsession_id)
- {
- switch_id += 1;
-
- std::string prev_subsession_id = SUBSESSION_INITIAL_SID;
- if (last_subsession_per_session.contains(session_uid))
- prev_subsession_id = last_subsession_per_session.at(session_uid);
-
- GError *err = nullptr;
- if (!g_dbus_connection_emit_signal(connection, nullptr, bus_object.data(), bus_iface.data(), "SwitchUserStarted",
- vals_to_g_variant(session_uid, switch_id, prev_subsession_id, next_subsession_id), &err))
- g_error_throw(err, "Failed to emit a signal: ");
-
- if (!switch_user_subsession(session_uid, prev_subsession_id, next_subsession_id))
- return false;
-
- last_subsession_per_session[session_uid] = next_subsession_id;
-
- wait_switch.try_emplace(session_uid, session_uid, connection, "SwitchUserCompleted");
- wait_switch.at(session_uid).on_start(switch_id, { std::string(prev_subsession_id), next_subsession_id });
-
- return true;
- }
-
- void on_add_user(GDBusMethodInvocation *invocation, std::string_view sender, GVariant *parameters)
- {
- auto [ session_uid, subsession_id ] = tuple_from_g_variant<int, std::string>(parameters);
-
- if (check_parameters_invalid(invocation, session_uid, subsession_id))
- return;
-
- do_add_user(session_uid, subsession_id, DirBackendAddRegularDir {});
-
- g_dbus_method_invocation_return_value(invocation, nullptr);
- }
-
- void on_add_user_fixed_size(GDBusMethodInvocation *invocation, std::string_view sender, GVariant *parameters)
- {
- auto [ session_uid, subsession_id, size_kB ] = tuple_from_g_variant<int, std::string, unsigned>(parameters);
-
- if (check_parameters_invalid(invocation, session_uid, subsession_id))
- return;
- if (!is_subsession_size_limit_valid(size_kB)) {
- g_dbus_method_invocation_return_dbus_error(invocation,
- get_dbus_error_mapping(SUBSESSION_ERROR_INVALID_PARAMETER), "Size too small");
- return;
- }
-
- do_add_user(session_uid, subsession_id, DirBackendAddFixedSize {size_kB});
-
- g_dbus_method_invocation_return_value(invocation, nullptr);
- }
-
- void on_remove_user(GDBusMethodInvocation *invocation, std::string_view sender, GVariant *parameters)
- {
- auto [ session_uid, subsession_id ] = tuple_from_g_variant<int, std::string>(parameters);
-
- if (check_parameters_invalid(invocation, session_uid, subsession_id))
- return;
-
- std::string_view current_subsession_id = SUBSESSION_INITIAL_SID;
- if (last_subsession_per_session.contains(session_uid))
- current_subsession_id = last_subsession_per_session.at(session_uid);
- if (subsession_id == current_subsession_id) {
- g_dbus_method_invocation_return_dbus_error(invocation,
- get_dbus_error_mapping(SUBSESSION_ERROR_RESOURCE_BUSY), "Cannot remove currently active user");
- return;
- }
-
- do_remove_user(session_uid, subsession_id);
-
- g_dbus_method_invocation_return_value(invocation, nullptr);
- }
-
- void on_switch_user(GDBusMethodInvocation *invocation, std::string_view sender, GVariant *parameters)
- {
- auto [ session_uid, next_subsession_id ] = tuple_from_g_variant<int, std::string>(parameters);
-
- if (session_uid <= 0) {
- g_dbus_method_invocation_return_dbus_error(invocation,
- get_dbus_error_mapping(SUBSESSION_ERROR_INVALID_PARAMETER), "Negative UID passed");
- return;
- }
-
- // N.B. Switch to user "" (empty string) is possible and it means no subsession is currently active
- if (next_subsession_id != SUBSESSION_INITIAL_SID && !check_subsession_id_valid(next_subsession_id)) {
- g_dbus_method_invocation_return_dbus_error(invocation,
- get_dbus_error_mapping(SUBSESSION_ERROR_INVALID_PARAMETER), "Incorrect subsession_id passed");
- return;
- }
- if (next_subsession_id != SUBSESSION_INITIAL_SID && !subsession_exists(session_uid, next_subsession_id)) {
- g_dbus_method_invocation_return_dbus_error(invocation,
- get_dbus_error_mapping(SUBSESSION_ERROR_NOT_AVAILABLE), "Subsession does not exist");
- return;
- }
-
- try {
- if (do_switch_user(session_uid, next_subsession_id))
- g_dbus_method_invocation_return_value(invocation, nullptr);
- else
- g_dbus_method_invocation_return_dbus_error(invocation,
- get_dbus_error_mapping(SUBSESSION_ERROR_IO_ERROR), "Failed to switch away from previous subsession");
- } catch (...) {
- g_dbus_method_invocation_return_dbus_error(invocation,
- get_dbus_error_mapping(SUBSESSION_ERROR_IO_ERROR), "Failed to switch away from previous subsession");
- throw;
- }
- }
-
- void on_add_user_wait(GDBusMethodInvocation *invocation, std::string_view sender, GVariant *parameters)
- {
- auto [ session_uid ] = tuple_from_g_variant<int>(parameters);
-
- if (session_uid <= 0) {
- g_dbus_method_invocation_return_dbus_error(invocation,
- get_dbus_error_mapping(SUBSESSION_ERROR_INVALID_PARAMETER), "Negative UID passed");
- return;
- }
-
- wait_add.try_emplace(session_uid, session_uid, connection, "AddUserCompleted");
- wait_add.at(session_uid).on_client_register(std::string(sender));
-
- g_dbus_method_invocation_return_value(invocation, nullptr);
- }
-
- void on_remove_user_wait(GDBusMethodInvocation *invocation, std::string_view sender, GVariant *parameters)
- {
- auto [ session_uid ] = tuple_from_g_variant<int>(parameters);
-
- if (session_uid <= 0) {
- g_dbus_method_invocation_return_dbus_error(invocation,
- get_dbus_error_mapping(SUBSESSION_ERROR_INVALID_PARAMETER), "Negative UID passed");
- return;
- }
-
- wait_remove.try_emplace(session_uid, session_uid, connection, "RemoveUserCompleted");
- wait_remove.at(session_uid).on_client_register(std::string(sender));
-
- g_dbus_method_invocation_return_value(invocation, nullptr);
- }
-
- void on_switch_user_wait(GDBusMethodInvocation *invocation, std::string_view sender, GVariant *parameters)
- {
- auto [ session_uid ] = tuple_from_g_variant<int>(parameters);
-
- if (session_uid <= 0) {
- g_dbus_method_invocation_return_dbus_error(invocation,
- get_dbus_error_mapping(SUBSESSION_ERROR_INVALID_PARAMETER), "Negative UID passed");
- return;
- }
-
- wait_switch.try_emplace(session_uid, session_uid, connection, "SwitchUserCompleted");
- wait_switch.at(session_uid).on_client_register(std::string(sender));
-
- g_dbus_method_invocation_return_value(invocation, nullptr);
- }
-
- void on_add_user_done(GDBusMethodInvocation *invocation, std::string_view sender, GVariant *parameters)
- {
- auto [ session_uid, subsession_id ] = tuple_from_g_variant<int, std::string>(parameters);
-
- if (check_parameters_invalid(invocation, session_uid, subsession_id))
- return;
-
- wait_add.try_emplace(session_uid, session_uid, connection, "AddUserCompleted");
- wait_add.at(session_uid).on_client_done(subsession_id, std::string(sender));
-
- g_dbus_method_invocation_return_value(invocation, nullptr);
- }
-
- void on_remove_user_done(GDBusMethodInvocation *invocation, std::string_view sender, GVariant *parameters)
- {
- auto [ session_uid, subsession_id ] = tuple_from_g_variant<int, std::string>(parameters);
-
- if (check_parameters_invalid(invocation, session_uid, subsession_id))
- return;
-
- wait_remove.try_emplace(session_uid, session_uid, connection, "RemoveUserCompleted");
- wait_remove.at(session_uid).on_client_done(subsession_id, std::string(sender));
-
- g_dbus_method_invocation_return_value(invocation, nullptr);
- }
-
- void on_switch_user_done(GDBusMethodInvocation *invocation, std::string_view sender, GVariant *parameters)
- {
- auto [ session_uid, switch_id ] = tuple_from_g_variant<int, uint64_t>(parameters);
-
- if (session_uid <= 0) {
- g_dbus_method_invocation_return_dbus_error(invocation,
- get_dbus_error_mapping(SUBSESSION_ERROR_INVALID_PARAMETER), "Negative UID passed");
- return;
- }
-
- wait_switch.try_emplace(session_uid, session_uid, connection, "SwitchUserCompleted");
- wait_switch.at(session_uid).on_client_done(switch_id, std::string(sender));
-
- g_dbus_method_invocation_return_value(invocation, nullptr);
- }
-
- void on_get_user_list(GDBusMethodInvocation *invocation, std::string_view sender, GVariant *parameters)
- {
- auto [ session_uid ] = tuple_from_g_variant<int>(parameters);
-
- if (session_uid <= 0) {
- g_dbus_method_invocation_return_dbus_error(invocation,
- get_dbus_error_mapping(SUBSESSION_ERROR_INVALID_PARAMETER), "Negative UID passed");
- return;
- }
-
- auto users = get_user_list(session_uid);
-
- // TODO: It would be cool to be able to use vals_to_g_variant here.
- g_autoptr(GVariantBuilder) builder = g_variant_builder_new(G_VARIANT_TYPE("as"));
- for (const auto &user : users)
- g_variant_builder_add(builder, "s", user.c_str());
- auto ret = g_variant_new("(as)", builder);
-
- g_dbus_method_invocation_return_value(invocation, ret);
- }
-
- void on_get_current_user(GDBusMethodInvocation *invocation, std::string_view sender, GVariant *parameters)
- {
- auto [ session_uid ] = tuple_from_g_variant<int>(parameters);
-
- if (session_uid <= 0) {
- g_dbus_method_invocation_return_dbus_error(invocation,
- get_dbus_error_mapping(SUBSESSION_ERROR_INVALID_PARAMETER), "Negative UID passed");
- return;
- }
-
- std::string subsession_id = SUBSESSION_INITIAL_SID;
- if (last_subsession_per_session.contains(session_uid))
- subsession_id = last_subsession_per_session.at(session_uid);
-
- auto ret = vals_to_g_variant(subsession_id);
- g_dbus_method_invocation_return_value(invocation, ret);
- }
-
- static void glib_name_acquired(GDBusConnection *conn, const gchar *name, gpointer user_data)
- {
- auto self = static_cast<sessiond_context *>(user_data);
- self->on_name_acquired();
- }
-
- static void glib_name_lost(GDBusConnection *conn, const gchar *name, gpointer user_data)
- {
- auto self = static_cast<sessiond_context *>(user_data);
- self->on_name_lost();
- }
- static void glib_method_call(GDBusConnection *conn, const gchar *sender, const gchar *object_path,
- const gchar *interface_name, const gchar *method_name, GVariant *parameters,
- GDBusMethodInvocation *invocation, gpointer user_data)
- try { // TODO: Also dispatch on interface_name (not needed at the moment)
- auto self = static_cast<sessiond_context *>(user_data);
- auto to_call = std::find_if(methods.begin(), methods.end(), [&](auto pair) {
- return pair.first == method_name;
- });
- if (to_call == methods.end())
- throw std::runtime_error(std::string("Unknown method ") + method_name + " called");
- LOGD("Handling %s call from %s", method_name, sender);
- (self->*(to_call->second))(invocation, std::string_view(sender), parameters);
- } catch (const std::invalid_argument &ex) {
- g_dbus_method_invocation_return_dbus_error(invocation,
- get_dbus_error_mapping(SUBSESSION_ERROR_INVALID_PARAMETER), ex.what());
- log_exception(ex, sender, method_name);
- } catch (const std::system_error &ex) {
- switch (ex.code().value()) {
- case EEXIST:
- g_dbus_method_invocation_return_dbus_error(invocation,
- get_dbus_error_mapping(SUBSESSION_ERROR_ALREADY_EXISTS), ex.what());
- break;
- case ENOENT:
- g_dbus_method_invocation_return_dbus_error(invocation,
- get_dbus_error_mapping(SUBSESSION_ERROR_NOT_AVAILABLE), ex.what());
- break;
- default:
- g_dbus_method_invocation_return_dbus_error(invocation,
- get_dbus_error_mapping(SUBSESSION_ERROR_IO_ERROR),
- (std::string("Unable to complete requested operation: ") + ex.what()).c_str());
- break;
- }
- log_exception(ex, sender, method_name);
- } catch (const std::runtime_error &ex) {
- g_dbus_method_invocation_return_dbus_error(invocation,
- get_dbus_error_mapping(SUBSESSION_ERROR_IO_ERROR),
- (std::string("Unable to complete requested operation: ") + ex.what()).c_str());
- log_exception(ex, sender, method_name);
- // Swallow the exception; the show must go on
- }
- static void log_exception(const std::exception &ex, std::string_view sender, std::string_view method_name)
- {
- LOGE("Exception %s\nwhile handling %s call from %s", ex.what(), method_name.data(), sender.data());
- }
-
- constexpr static GDBusInterfaceVTable table = {
- .method_call = sessiond_context::glib_method_call,
- .get_property = NULL,
- .set_property = NULL,
- .padding = {0,},
- };
-
- constexpr static std::string_view xml =
- "<node name=\"/org/tizen/sessiond\">"
- "<interface name=\"org.tizen.sessiond.subsession.Manager\">"
- "<method name=\"AddUser\">"
- "<arg name=\"session_uid\" type=\"i\" direction=\"in\"/>"
- "<arg name=\"subsession_id\" type=\"s\" direction=\"in\"/>"
- "</method>"
- "<method name=\"AddUserFixedSize\">"
- "<arg name=\"session_uid\" type=\"i\" direction=\"in\"/>"
- "<arg name=\"subsession_id\" type=\"s\" direction=\"in\"/>"
- "<arg name=\"size_kB\" type=\"u\" direction=\"in\"/>"
- "</method>"
- "<method name=\"RemoveUser\">"
- "<arg name=\"session_uid\" type=\"i\" direction=\"in\"/>"
- "<arg name=\"subsession_id\" type=\"s\" direction=\"in\"/>"
- "</method>"
- "<method name=\"SwitchUser\">"
- "<arg name=\"session_uid\" type=\"i\" direction=\"in\"/>"
- "<arg name=\"next_subsession_id\" type=\"s\" direction=\"in\"/>"
- "</method>"
- "<method name=\"AddUserWait\">"
- "<arg name=\"session_uid\" type=\"i\" direction=\"in\"/>"
- "</method>"
- "<method name=\"RemoveUserWait\">"
- "<arg name=\"session_uid\" type=\"i\" direction=\"in\"/>"
- "</method>"
- "<method name=\"SwitchUserWait\">"
- "<arg name=\"session_uid\" type=\"i\" direction=\"in\"/>"
- "</method>"
- "<method name=\"AddUserDone\">"
- "<arg name=\"session_uid\" type=\"i\" direction=\"in\"/>"
- "<arg name=\"subsession_id\" type=\"s\" direction=\"in\"/>"
- "</method>"
- "<method name=\"RemoveUserDone\">"
- "<arg name=\"session_uid\" type=\"i\" direction=\"in\"/>"
- "<arg name=\"subsession_id\" type=\"s\" direction=\"in\"/>"
- "</method>"
- "<method name=\"SwitchUserDone\">"
- "<arg name=\"session_uid\" type=\"i\" direction=\"in\"/>"
- "<arg name=\"switch_id\" type=\"x\" direction=\"in\"/>"
- "</method>"
- "<method name=\"GetUserList\">"
- "<arg name=\"session_uid\" type=\"i\" direction=\"in\"/>"
- "<arg name=\"subsession_id_list\" type=\"as\" direction=\"out\"/>"
- "</method>"
- "<method name=\"GetCurrentUser\">"
- "<arg name=\"session_uid\" type=\"i\" direction=\"in\"/>"
- "<arg name=\"subsession_id\" type=\"s\" direction=\"out\"/>"
- "</method>"
- "<signal name=\"AddUserStarted\">"
- "<arg name=\"session_uid\" type=\"i\" direction=\"out\"/>"
- "<arg name=\"subsession_id\" type=\"s\" direction=\"out\"/>"
- "</signal>"
- "<signal name=\"RemoveUserStarted\">"
- "<arg name=\"session_uid\" type=\"i\" direction=\"out\"/>"
- "<arg name=\"subsession_id\" type=\"s\" direction=\"out\"/>"
- "</signal>"
- "<signal name=\"SwitchUserStarted\">"
- "<arg name=\"session_uid\" type=\"i\" direction=\"out\"/>"
- "<arg name=\"switch_id\" type=\"x\" direction=\"out\"/>"
- "<arg name=\"prev_subsession_id\" type=\"s\" direction=\"out\"/>"
- "<arg name=\"next_subsession_id\" type=\"s\" direction=\"out\"/>"
- "</signal>"
- "<signal name=\"AddUserCompleted\">"
- "<arg name=\"session_uid\" type=\"i\" direction=\"out\"/>"
- "<arg name=\"subsession_id\" type=\"s\" direction=\"out\"/>"
- "</signal>"
- "<signal name=\"RemoveUserCompleted\">"
- "<arg name=\"session_uid\" type=\"i\" direction=\"out\"/>"
- "<arg name=\"subsession_id\" type=\"s\" direction=\"out\"/>"
- "</signal>"
- "<signal name=\"SwitchUserCompleted\">"
- "<arg name=\"session_uid\" type=\"i\" direction=\"out\"/>"
- "<arg name=\"switch_id\" type=\"x\" direction=\"out\"/>"
- "<arg name=\"prev_subsession_id\" type=\"s\" direction=\"out\"/>"
- "<arg name=\"next_subsession_id\" type=\"s\" direction=\"out\"/>"
- "</signal>"
- "</interface>"
- "</node>";
-
- constexpr static std::array methods = {
- std::make_pair( "AddUser"sv, &sessiond_context::on_add_user ),
- std::make_pair("AddUserFixedSize"sv, &sessiond_context::on_add_user_fixed_size),
- std::make_pair( "RemoveUser"sv, &sessiond_context::on_remove_user ),
- std::make_pair( "SwitchUser"sv, &sessiond_context::on_switch_user ),
- std::make_pair( "AddUserWait"sv, &sessiond_context::on_add_user_wait ),
- std::make_pair( "RemoveUserWait"sv, &sessiond_context::on_remove_user_wait ),
- std::make_pair( "SwitchUserWait"sv, &sessiond_context::on_switch_user_wait ),
- std::make_pair( "AddUserDone"sv, &sessiond_context::on_add_user_done ),
- std::make_pair( "RemoveUserDone"sv, &sessiond_context::on_remove_user_done ),
- std::make_pair( "SwitchUserDone"sv, &sessiond_context::on_switch_user_done ),
- std::make_pair( "GetUserList"sv, &sessiond_context::on_get_user_list ),
- std::make_pair( "GetCurrentUser"sv, &sessiond_context::on_get_current_user ),
- };
-
- // TODO: Currently, the first parameter is always a single-element tuple.
- // Consider simplifying wait_manager.
- // N.B. Although GLib is multi-threaded, the following data structures do not need
- // to be protected by mutexes as all of them are accessed from DBus handlers only.
- std::unordered_map<int, wait_manager<std::tuple<std::string>, std::tuple<>>> wait_add;
- std::unordered_map<int, wait_manager<std::tuple<std::string>, std::tuple<>>> wait_remove;
- std::unordered_map<int, wait_manager<std::tuple<uint64_t>, std::tuple<std::string, std::string>>> wait_switch;
-
- // used for tracking subsession switches for each `session_uid`
- std::unordered_map<int, std::string> last_subsession_per_session;
-
- uint64_t switch_id = 0;
-
- introspection_data data;
- owner_id id;
- main_loop loop;
- GDBusConnection *connection = nullptr;
-};
+#include "main_context.hpp"
+#include <stdexcept>
int main() try {
sessiond_context().run();
--- /dev/null
+/* MIT License
+ *
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is furnished
+ * to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE. */
+
+#pragma once
+
+#undef LOG_TAG
+#define LOG_TAG "SESSIOND"
+#include <dlog.h>
+
+#include <algorithm>
+#include <unordered_map>
+
+#include <gio/gio.h>
+
+#include "sessiond-internal.h"
+#include "globals.hpp"
+#include "fs_helpers.hpp"
+#include "tuple_g_variant_helpers.hpp"
+#include "tuple_hash.hpp"
+#include "wait_manager.hpp"
+#include "dir_backend_regular_dir.hpp"
+#include "dir_backend_fixed_size.hpp"
+
+static constexpr const char * get_dbus_error_mapping (subsession_error_e subsession_error)
+{
+ /* I want this function to be evaluated at compile time
+ * and `std::find_if` is not constexpr/consteval-friendly,
+ * so caveman style iteration it is. */
+
+ const char *ret = nullptr;
+ for (const auto & x : error_mappings)
+ if (x.second == subsession_error)
+ ret = x.first;
+
+ return ret ?: throw std::logic_error("Compile time get_dbus_error_mapping error");
+}
+
+using namespace std::string_view_literals;
+
+static bool is_subsession_size_limit_valid (unsigned size_kB)
+{
+ static constexpr int MINIMUM_FOR_EXT2_OVERHEAD = 220; // determined empirically
+ if (size_kB < MINIMUM_FOR_EXT2_OVERHEAD)
+ return false;
+
+ return true;
+}
+
+struct introspection_data {
+ introspection_data(std::string_view xml)
+ {
+ inner = g_dbus_node_info_new_for_xml(xml.data(), nullptr);
+ if (inner == nullptr)
+ throw std::runtime_error("Couldn't parse introspection data");
+ }
+
+ ~introspection_data()
+ {
+ if (inner)
+ g_dbus_node_info_unref(inner);
+ }
+
+ // Because of the rule of three, we also have to delete these.
+ introspection_data(const introspection_data &) = delete;
+ introspection_data &operator=(const introspection_data &) = delete;
+
+ GDBusNodeInfo *inner;
+};
+
+struct owner_id {
+ owner_id() : inner(0) {}
+
+ void own_name_on_connection(GDBusConnection *connection, std::string_view name,
+ GBusNameAcquiredCallback on_name_acquired, GBusNameLostCallback on_name_lost, gpointer user_data)
+ {
+ inner = g_bus_own_name_on_connection(connection, name.data(), G_BUS_NAME_OWNER_FLAGS_NONE,
+ on_name_acquired, on_name_lost, user_data, nullptr);
+ LOGD("Acquiring %s", name.data());
+ }
+
+ ~owner_id()
+ {
+ if (inner != 0)
+ g_bus_unown_name(inner);
+ }
+
+ // Because of the rule of three, we also have to delete these.
+ owner_id(const owner_id &) = delete;
+ owner_id &operator=(const owner_id &) = delete;
+
+ guint inner;
+};
+
+struct main_loop {
+ main_loop()
+ {
+ inner = g_main_loop_new(nullptr, false);
+ }
+
+ ~main_loop()
+ {
+ g_main_loop_unref(inner);
+ }
+
+ // Because of the rule of three, we also have to delete these.
+ main_loop(const main_loop &) = delete;
+ main_loop &operator=(const main_loop &) = delete;
+
+ GMainLoop *inner;
+};
+
+struct sessiond_context {
+ sessiond_context() : data(xml), id(), loop()
+ {
+ connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL);
+ if (!connection)
+ throw std::runtime_error("Cannot connect to the system bus");
+
+ guint registration_id = g_dbus_connection_register_object(
+ connection, bus_object.data(), data.inner->interfaces[0], &table, this, nullptr, nullptr);
+ if (registration_id <= 0)
+ throw std::runtime_error("Couldn't register bus ID");
+
+ id.own_name_on_connection(connection, bus_name, glib_name_acquired, glib_name_lost, this);
+ }
+
+ void run()
+ {
+ g_main_loop_run(loop.inner);
+ }
+
+ void on_name_acquired()
+ {
+ LOGD("Bus name acquired");
+ }
+
+ void on_name_lost()
+ {
+ throw std::runtime_error("Bus name lost");
+ }
+
+ bool check_parameters_invalid(GDBusMethodInvocation *invocation,
+ const int session_uid, const std::string_view subsession_id)
+ {
+ if (session_uid <= 0) {
+ g_dbus_method_invocation_return_dbus_error(invocation,
+ get_dbus_error_mapping(SUBSESSION_ERROR_INVALID_PARAMETER), "Negative UID passed");
+ return true;
+ }
+
+ if (!check_subsession_id_valid(subsession_id)) {
+ g_dbus_method_invocation_return_dbus_error(invocation,
+ get_dbus_error_mapping(SUBSESSION_ERROR_INVALID_PARAMETER), "Incorrect subsession_id passed");
+ return true;
+ }
+
+ return false;
+ }
+
+ void do_add_user(int session_uid, const std::string& subsession_id, const DirBackendAdd& backend)
+ {
+ GError *err = nullptr;
+ if (!g_dbus_connection_emit_signal(connection, nullptr, bus_object.data(), bus_iface.data(), "AddUserStarted",
+ vals_to_g_variant(session_uid, subsession_id), &err))
+ g_error_throw(err, "Failed to emit a signal: ");
+
+ add_user_subsession(session_uid, subsession_id, backend);
+
+ wait_add.try_emplace(session_uid, session_uid, connection, "AddUserCompleted");
+ wait_add.at(session_uid).on_start(subsession_id, { });
+ }
+
+ void do_remove_user(int session_uid, const std::string& subsession_id)
+ {
+ GError *err = nullptr;
+ if (!g_dbus_connection_emit_signal(connection, nullptr, bus_object.data(), bus_iface.data(), "RemoveUserStarted",
+ vals_to_g_variant(session_uid, subsession_id), &err))
+ g_error_throw(err, "Failed to emit a signal: ");
+
+ remove_user_subsession(session_uid, subsession_id);
+
+ wait_remove.try_emplace(session_uid, session_uid, connection, "RemoveUserCompleted");
+ wait_remove.at(session_uid).on_start(subsession_id, { });
+ }
+
+ bool do_switch_user(int session_uid, const std::string& next_subsession_id)
+ {
+ switch_id += 1;
+
+ std::string prev_subsession_id = SUBSESSION_INITIAL_SID;
+ if (last_subsession_per_session.contains(session_uid))
+ prev_subsession_id = last_subsession_per_session.at(session_uid);
+
+ GError *err = nullptr;
+ if (!g_dbus_connection_emit_signal(connection, nullptr, bus_object.data(), bus_iface.data(), "SwitchUserStarted",
+ vals_to_g_variant(session_uid, switch_id, prev_subsession_id, next_subsession_id), &err))
+ g_error_throw(err, "Failed to emit a signal: ");
+
+ if (!switch_user_subsession(session_uid, prev_subsession_id, next_subsession_id))
+ return false;
+
+ last_subsession_per_session[session_uid] = next_subsession_id;
+
+ wait_switch.try_emplace(session_uid, session_uid, connection, "SwitchUserCompleted");
+ wait_switch.at(session_uid).on_start(switch_id, { std::string(prev_subsession_id), next_subsession_id });
+
+ return true;
+ }
+
+ void on_add_user(GDBusMethodInvocation *invocation, std::string_view sender, GVariant *parameters)
+ {
+ auto [ session_uid, subsession_id ] = tuple_from_g_variant<int, std::string>(parameters);
+
+ if (check_parameters_invalid(invocation, session_uid, subsession_id))
+ return;
+
+ do_add_user(session_uid, subsession_id, DirBackendAddRegularDir {});
+
+ g_dbus_method_invocation_return_value(invocation, nullptr);
+ }
+
+ void on_add_user_fixed_size(GDBusMethodInvocation *invocation, std::string_view sender, GVariant *parameters)
+ {
+ auto [ session_uid, subsession_id, size_kB ] = tuple_from_g_variant<int, std::string, unsigned>(parameters);
+
+ if (check_parameters_invalid(invocation, session_uid, subsession_id))
+ return;
+ if (!is_subsession_size_limit_valid(size_kB)) {
+ g_dbus_method_invocation_return_dbus_error(invocation,
+ get_dbus_error_mapping(SUBSESSION_ERROR_INVALID_PARAMETER), "Size too small");
+ return;
+ }
+
+ do_add_user(session_uid, subsession_id, DirBackendAddFixedSize {size_kB});
+
+ g_dbus_method_invocation_return_value(invocation, nullptr);
+ }
+
+ void on_remove_user(GDBusMethodInvocation *invocation, std::string_view sender, GVariant *parameters)
+ {
+ auto [ session_uid, subsession_id ] = tuple_from_g_variant<int, std::string>(parameters);
+
+ if (check_parameters_invalid(invocation, session_uid, subsession_id))
+ return;
+
+ std::string_view current_subsession_id = SUBSESSION_INITIAL_SID;
+ if (last_subsession_per_session.contains(session_uid))
+ current_subsession_id = last_subsession_per_session.at(session_uid);
+ if (subsession_id == current_subsession_id) {
+ g_dbus_method_invocation_return_dbus_error(invocation,
+ get_dbus_error_mapping(SUBSESSION_ERROR_RESOURCE_BUSY), "Cannot remove currently active user");
+ return;
+ }
+
+ do_remove_user(session_uid, subsession_id);
+
+ g_dbus_method_invocation_return_value(invocation, nullptr);
+ }
+
+ void on_switch_user(GDBusMethodInvocation *invocation, std::string_view sender, GVariant *parameters)
+ {
+ auto [ session_uid, next_subsession_id ] = tuple_from_g_variant<int, std::string>(parameters);
+
+ if (session_uid <= 0) {
+ g_dbus_method_invocation_return_dbus_error(invocation,
+ get_dbus_error_mapping(SUBSESSION_ERROR_INVALID_PARAMETER), "Negative UID passed");
+ return;
+ }
+
+ // N.B. Switch to user "" (empty string) is possible and it means no subsession is currently active
+ if (next_subsession_id != SUBSESSION_INITIAL_SID && !check_subsession_id_valid(next_subsession_id)) {
+ g_dbus_method_invocation_return_dbus_error(invocation,
+ get_dbus_error_mapping(SUBSESSION_ERROR_INVALID_PARAMETER), "Incorrect subsession_id passed");
+ return;
+ }
+ if (next_subsession_id != SUBSESSION_INITIAL_SID && !subsession_exists(session_uid, next_subsession_id)) {
+ g_dbus_method_invocation_return_dbus_error(invocation,
+ get_dbus_error_mapping(SUBSESSION_ERROR_NOT_AVAILABLE), "Subsession does not exist");
+ return;
+ }
+
+ try {
+ if (do_switch_user(session_uid, next_subsession_id))
+ g_dbus_method_invocation_return_value(invocation, nullptr);
+ else
+ g_dbus_method_invocation_return_dbus_error(invocation,
+ get_dbus_error_mapping(SUBSESSION_ERROR_IO_ERROR), "Failed to switch away from previous subsession");
+ } catch (...) {
+ g_dbus_method_invocation_return_dbus_error(invocation,
+ get_dbus_error_mapping(SUBSESSION_ERROR_IO_ERROR), "Failed to switch away from previous subsession");
+ throw;
+ }
+ }
+
+ void on_add_user_wait(GDBusMethodInvocation *invocation, std::string_view sender, GVariant *parameters)
+ {
+ auto [ session_uid ] = tuple_from_g_variant<int>(parameters);
+
+ if (session_uid <= 0) {
+ g_dbus_method_invocation_return_dbus_error(invocation,
+ get_dbus_error_mapping(SUBSESSION_ERROR_INVALID_PARAMETER), "Negative UID passed");
+ return;
+ }
+
+ wait_add.try_emplace(session_uid, session_uid, connection, "AddUserCompleted");
+ wait_add.at(session_uid).on_client_register(std::string(sender));
+
+ g_dbus_method_invocation_return_value(invocation, nullptr);
+ }
+
+ void on_remove_user_wait(GDBusMethodInvocation *invocation, std::string_view sender, GVariant *parameters)
+ {
+ auto [ session_uid ] = tuple_from_g_variant<int>(parameters);
+
+ if (session_uid <= 0) {
+ g_dbus_method_invocation_return_dbus_error(invocation,
+ get_dbus_error_mapping(SUBSESSION_ERROR_INVALID_PARAMETER), "Negative UID passed");
+ return;
+ }
+
+ wait_remove.try_emplace(session_uid, session_uid, connection, "RemoveUserCompleted");
+ wait_remove.at(session_uid).on_client_register(std::string(sender));
+
+ g_dbus_method_invocation_return_value(invocation, nullptr);
+ }
+
+ void on_switch_user_wait(GDBusMethodInvocation *invocation, std::string_view sender, GVariant *parameters)
+ {
+ auto [ session_uid ] = tuple_from_g_variant<int>(parameters);
+
+ if (session_uid <= 0) {
+ g_dbus_method_invocation_return_dbus_error(invocation,
+ get_dbus_error_mapping(SUBSESSION_ERROR_INVALID_PARAMETER), "Negative UID passed");
+ return;
+ }
+
+ wait_switch.try_emplace(session_uid, session_uid, connection, "SwitchUserCompleted");
+ wait_switch.at(session_uid).on_client_register(std::string(sender));
+
+ g_dbus_method_invocation_return_value(invocation, nullptr);
+ }
+
+ void on_add_user_done(GDBusMethodInvocation *invocation, std::string_view sender, GVariant *parameters)
+ {
+ auto [ session_uid, subsession_id ] = tuple_from_g_variant<int, std::string>(parameters);
+
+ if (check_parameters_invalid(invocation, session_uid, subsession_id))
+ return;
+
+ wait_add.try_emplace(session_uid, session_uid, connection, "AddUserCompleted");
+ wait_add.at(session_uid).on_client_done(subsession_id, std::string(sender));
+
+ g_dbus_method_invocation_return_value(invocation, nullptr);
+ }
+
+ void on_remove_user_done(GDBusMethodInvocation *invocation, std::string_view sender, GVariant *parameters)
+ {
+ auto [ session_uid, subsession_id ] = tuple_from_g_variant<int, std::string>(parameters);
+
+ if (check_parameters_invalid(invocation, session_uid, subsession_id))
+ return;
+
+ wait_remove.try_emplace(session_uid, session_uid, connection, "RemoveUserCompleted");
+ wait_remove.at(session_uid).on_client_done(subsession_id, std::string(sender));
+
+ g_dbus_method_invocation_return_value(invocation, nullptr);
+ }
+
+ void on_switch_user_done(GDBusMethodInvocation *invocation, std::string_view sender, GVariant *parameters)
+ {
+ auto [ session_uid, switch_id ] = tuple_from_g_variant<int, uint64_t>(parameters);
+
+ if (session_uid <= 0) {
+ g_dbus_method_invocation_return_dbus_error(invocation,
+ get_dbus_error_mapping(SUBSESSION_ERROR_INVALID_PARAMETER), "Negative UID passed");
+ return;
+ }
+
+ wait_switch.try_emplace(session_uid, session_uid, connection, "SwitchUserCompleted");
+ wait_switch.at(session_uid).on_client_done(switch_id, std::string(sender));
+
+ g_dbus_method_invocation_return_value(invocation, nullptr);
+ }
+
+ void on_get_user_list(GDBusMethodInvocation *invocation, std::string_view sender, GVariant *parameters)
+ {
+ auto [ session_uid ] = tuple_from_g_variant<int>(parameters);
+
+ if (session_uid <= 0) {
+ g_dbus_method_invocation_return_dbus_error(invocation,
+ get_dbus_error_mapping(SUBSESSION_ERROR_INVALID_PARAMETER), "Negative UID passed");
+ return;
+ }
+
+ auto users = get_user_list(session_uid);
+
+ // TODO: It would be cool to be able to use vals_to_g_variant here.
+ g_autoptr(GVariantBuilder) builder = g_variant_builder_new(G_VARIANT_TYPE("as"));
+ for (const auto &user : users)
+ g_variant_builder_add(builder, "s", user.c_str());
+ auto ret = g_variant_new("(as)", builder);
+
+ g_dbus_method_invocation_return_value(invocation, ret);
+ }
+
+ void on_get_current_user(GDBusMethodInvocation *invocation, std::string_view sender, GVariant *parameters)
+ {
+ auto [ session_uid ] = tuple_from_g_variant<int>(parameters);
+
+ if (session_uid <= 0) {
+ g_dbus_method_invocation_return_dbus_error(invocation,
+ get_dbus_error_mapping(SUBSESSION_ERROR_INVALID_PARAMETER), "Negative UID passed");
+ return;
+ }
+
+ std::string subsession_id = SUBSESSION_INITIAL_SID;
+ if (last_subsession_per_session.contains(session_uid))
+ subsession_id = last_subsession_per_session.at(session_uid);
+
+ auto ret = vals_to_g_variant(subsession_id);
+ g_dbus_method_invocation_return_value(invocation, ret);
+ }
+
+ static void glib_name_acquired(GDBusConnection *conn, const gchar *name, gpointer user_data)
+ {
+ auto self = static_cast<sessiond_context *>(user_data);
+ self->on_name_acquired();
+ }
+
+ static void glib_name_lost(GDBusConnection *conn, const gchar *name, gpointer user_data)
+ {
+ auto self = static_cast<sessiond_context *>(user_data);
+ self->on_name_lost();
+ }
+ static void glib_method_call(GDBusConnection *conn, const gchar *sender, const gchar *object_path,
+ const gchar *interface_name, const gchar *method_name, GVariant *parameters,
+ GDBusMethodInvocation *invocation, gpointer user_data)
+ try { // TODO: Also dispatch on interface_name (not needed at the moment)
+ auto self = static_cast<sessiond_context *>(user_data);
+ auto to_call = std::find_if(methods.begin(), methods.end(), [&](auto pair) {
+ return pair.first == method_name;
+ });
+ if (to_call == methods.end())
+ throw std::runtime_error(std::string("Unknown method ") + method_name + " called");
+ LOGD("Handling %s call from %s", method_name, sender);
+ (self->*(to_call->second))(invocation, std::string_view(sender), parameters);
+ } catch (const std::invalid_argument &ex) {
+ g_dbus_method_invocation_return_dbus_error(invocation,
+ get_dbus_error_mapping(SUBSESSION_ERROR_INVALID_PARAMETER), ex.what());
+ log_exception(ex, sender, method_name);
+ } catch (const std::system_error &ex) {
+ switch (ex.code().value()) {
+ case EEXIST:
+ g_dbus_method_invocation_return_dbus_error(invocation,
+ get_dbus_error_mapping(SUBSESSION_ERROR_ALREADY_EXISTS), ex.what());
+ break;
+ case ENOENT:
+ g_dbus_method_invocation_return_dbus_error(invocation,
+ get_dbus_error_mapping(SUBSESSION_ERROR_NOT_AVAILABLE), ex.what());
+ break;
+ default:
+ g_dbus_method_invocation_return_dbus_error(invocation,
+ get_dbus_error_mapping(SUBSESSION_ERROR_IO_ERROR),
+ (std::string("Unable to complete requested operation: ") + ex.what()).c_str());
+ break;
+ }
+ log_exception(ex, sender, method_name);
+ } catch (const std::runtime_error &ex) {
+ g_dbus_method_invocation_return_dbus_error(invocation,
+ get_dbus_error_mapping(SUBSESSION_ERROR_IO_ERROR),
+ (std::string("Unable to complete requested operation: ") + ex.what()).c_str());
+ log_exception(ex, sender, method_name);
+ // Swallow the exception; the show must go on
+ }
+ static void log_exception(const std::exception &ex, std::string_view sender, std::string_view method_name)
+ {
+ LOGE("Exception %s\nwhile handling %s call from %s", ex.what(), method_name.data(), sender.data());
+ }
+
+ constexpr static GDBusInterfaceVTable table = {
+ .method_call = sessiond_context::glib_method_call,
+ .get_property = NULL,
+ .set_property = NULL,
+ .padding = {0,},
+ };
+
+ constexpr static std::string_view xml =
+ "<node name=\"/org/tizen/sessiond\">"
+ "<interface name=\"org.tizen.sessiond.subsession.Manager\">"
+ "<method name=\"AddUser\">"
+ "<arg name=\"session_uid\" type=\"i\" direction=\"in\"/>"
+ "<arg name=\"subsession_id\" type=\"s\" direction=\"in\"/>"
+ "</method>"
+ "<method name=\"AddUserFixedSize\">"
+ "<arg name=\"session_uid\" type=\"i\" direction=\"in\"/>"
+ "<arg name=\"subsession_id\" type=\"s\" direction=\"in\"/>"
+ "<arg name=\"size_kB\" type=\"u\" direction=\"in\"/>"
+ "</method>"
+ "<method name=\"RemoveUser\">"
+ "<arg name=\"session_uid\" type=\"i\" direction=\"in\"/>"
+ "<arg name=\"subsession_id\" type=\"s\" direction=\"in\"/>"
+ "</method>"
+ "<method name=\"SwitchUser\">"
+ "<arg name=\"session_uid\" type=\"i\" direction=\"in\"/>"
+ "<arg name=\"next_subsession_id\" type=\"s\" direction=\"in\"/>"
+ "</method>"
+ "<method name=\"AddUserWait\">"
+ "<arg name=\"session_uid\" type=\"i\" direction=\"in\"/>"
+ "</method>"
+ "<method name=\"RemoveUserWait\">"
+ "<arg name=\"session_uid\" type=\"i\" direction=\"in\"/>"
+ "</method>"
+ "<method name=\"SwitchUserWait\">"
+ "<arg name=\"session_uid\" type=\"i\" direction=\"in\"/>"
+ "</method>"
+ "<method name=\"AddUserDone\">"
+ "<arg name=\"session_uid\" type=\"i\" direction=\"in\"/>"
+ "<arg name=\"subsession_id\" type=\"s\" direction=\"in\"/>"
+ "</method>"
+ "<method name=\"RemoveUserDone\">"
+ "<arg name=\"session_uid\" type=\"i\" direction=\"in\"/>"
+ "<arg name=\"subsession_id\" type=\"s\" direction=\"in\"/>"
+ "</method>"
+ "<method name=\"SwitchUserDone\">"
+ "<arg name=\"session_uid\" type=\"i\" direction=\"in\"/>"
+ "<arg name=\"switch_id\" type=\"x\" direction=\"in\"/>"
+ "</method>"
+ "<method name=\"GetUserList\">"
+ "<arg name=\"session_uid\" type=\"i\" direction=\"in\"/>"
+ "<arg name=\"subsession_id_list\" type=\"as\" direction=\"out\"/>"
+ "</method>"
+ "<method name=\"GetCurrentUser\">"
+ "<arg name=\"session_uid\" type=\"i\" direction=\"in\"/>"
+ "<arg name=\"subsession_id\" type=\"s\" direction=\"out\"/>"
+ "</method>"
+ "<signal name=\"AddUserStarted\">"
+ "<arg name=\"session_uid\" type=\"i\" direction=\"out\"/>"
+ "<arg name=\"subsession_id\" type=\"s\" direction=\"out\"/>"
+ "</signal>"
+ "<signal name=\"RemoveUserStarted\">"
+ "<arg name=\"session_uid\" type=\"i\" direction=\"out\"/>"
+ "<arg name=\"subsession_id\" type=\"s\" direction=\"out\"/>"
+ "</signal>"
+ "<signal name=\"SwitchUserStarted\">"
+ "<arg name=\"session_uid\" type=\"i\" direction=\"out\"/>"
+ "<arg name=\"switch_id\" type=\"x\" direction=\"out\"/>"
+ "<arg name=\"prev_subsession_id\" type=\"s\" direction=\"out\"/>"
+ "<arg name=\"next_subsession_id\" type=\"s\" direction=\"out\"/>"
+ "</signal>"
+ "<signal name=\"AddUserCompleted\">"
+ "<arg name=\"session_uid\" type=\"i\" direction=\"out\"/>"
+ "<arg name=\"subsession_id\" type=\"s\" direction=\"out\"/>"
+ "</signal>"
+ "<signal name=\"RemoveUserCompleted\">"
+ "<arg name=\"session_uid\" type=\"i\" direction=\"out\"/>"
+ "<arg name=\"subsession_id\" type=\"s\" direction=\"out\"/>"
+ "</signal>"
+ "<signal name=\"SwitchUserCompleted\">"
+ "<arg name=\"session_uid\" type=\"i\" direction=\"out\"/>"
+ "<arg name=\"switch_id\" type=\"x\" direction=\"out\"/>"
+ "<arg name=\"prev_subsession_id\" type=\"s\" direction=\"out\"/>"
+ "<arg name=\"next_subsession_id\" type=\"s\" direction=\"out\"/>"
+ "</signal>"
+ "</interface>"
+ "</node>";
+
+ constexpr static std::array methods = {
+ std::make_pair( "AddUser"sv, &sessiond_context::on_add_user ),
+ std::make_pair("AddUserFixedSize"sv, &sessiond_context::on_add_user_fixed_size),
+ std::make_pair( "RemoveUser"sv, &sessiond_context::on_remove_user ),
+ std::make_pair( "SwitchUser"sv, &sessiond_context::on_switch_user ),
+ std::make_pair( "AddUserWait"sv, &sessiond_context::on_add_user_wait ),
+ std::make_pair( "RemoveUserWait"sv, &sessiond_context::on_remove_user_wait ),
+ std::make_pair( "SwitchUserWait"sv, &sessiond_context::on_switch_user_wait ),
+ std::make_pair( "AddUserDone"sv, &sessiond_context::on_add_user_done ),
+ std::make_pair( "RemoveUserDone"sv, &sessiond_context::on_remove_user_done ),
+ std::make_pair( "SwitchUserDone"sv, &sessiond_context::on_switch_user_done ),
+ std::make_pair( "GetUserList"sv, &sessiond_context::on_get_user_list ),
+ std::make_pair( "GetCurrentUser"sv, &sessiond_context::on_get_current_user ),
+ };
+
+ // TODO: Currently, the first parameter is always a single-element tuple.
+ // Consider simplifying wait_manager.
+ // N.B. Although GLib is multi-threaded, the following data structures do not need
+ // to be protected by mutexes as all of them are accessed from DBus handlers only.
+ std::unordered_map<int, wait_manager<std::tuple<std::string>, std::tuple<>>> wait_add;
+ std::unordered_map<int, wait_manager<std::tuple<std::string>, std::tuple<>>> wait_remove;
+ std::unordered_map<int, wait_manager<std::tuple<uint64_t>, std::tuple<std::string, std::string>>> wait_switch;
+
+ // used for tracking subsession switches for each `session_uid`
+ std::unordered_map<int, std::string> last_subsession_per_session;
+
+ uint64_t switch_id = 0;
+
+ introspection_data data;
+ owner_id id;
+ main_loop loop;
+ GDBusConnection *connection = nullptr;
+};
}
template <typename T>
-T value_from_g_variant_one_inner(GVariant *variant)
+inline T value_from_g_variant_one_inner(GVariant *variant)
{
// This case is disallowed due to lifetime mess potential.
// Implament at your own responsibility!
}
template<>
-std::string value_from_g_variant_one_inner<std::string>(GVariant *variant)
+inline std::string value_from_g_variant_one_inner<std::string>(GVariant *variant)
{
return std::string(g_variant_get_string(variant, NULL));
}
template <size_t I, typename T>
-std::tuple<T> tuple_from_g_variant_inner(GVariant *variant)
+inline std::tuple<T> tuple_from_g_variant_inner(GVariant *variant)
{
GVariant *i = g_variant_get_child_value(variant, I);
T output = value_from_g_variant_one_inner<T>(i);
}
template <size_t I, typename T, typename T1, typename... Ts>
-std::tuple<T, T1, Ts...> tuple_from_g_variant_inner(GVariant *variant)
+inline std::tuple<T, T1, Ts...> tuple_from_g_variant_inner(GVariant *variant)
{
GVariant *i = g_variant_get_child_value(variant, I);
T output = value_from_g_variant_one_inner<T>(i);
}
template <typename... Ts>
-std::tuple<Ts...> tuple_from_g_variant(GVariant *variant)
+inline std::tuple<Ts...> tuple_from_g_variant(GVariant *variant)
{
GVariantType *type = g_variant_type_new(type_of_g_variant<Ts...>().c_str());
if (!g_variant_is_of_type(variant, type)) {
}
template <typename T>
-GVariant *value_to_g_variant_one_inner(const T &value)
+inline GVariant *value_to_g_variant_one_inner(const T &value)
{
// Out of completeness:
static_assert(!std::is_same<T, std::string>::value, "Implemented elsewhere");
}
template <>
-GVariant *value_to_g_variant_one_inner<std::string>(const std::string &value)
+inline GVariant *value_to_g_variant_one_inner<std::string>(const std::string &value)
{
return g_variant_new_string(value.c_str());
}
template <>
-GVariant *value_to_g_variant_one_inner<std::string_view>(const std::string_view &value)
+inline GVariant *value_to_g_variant_one_inner<std::string_view>(const std::string_view &value)
{
return g_variant_new_string(value.data());
}
template <size_t I, typename T>
-void fill_g_variant_one_table_inner(GVariant **table, const T &value)
+inline void fill_g_variant_one_table_inner(GVariant **table, const T &value)
{
table[I] = value_to_g_variant_one_inner<T>(value);
}
template <size_t I, typename T, typename T1, typename... Ts>
-void fill_g_variant_one_table_inner(GVariant **table, const T &value, const T1 &next_value, const Ts &...vals)
+inline void fill_g_variant_one_table_inner(GVariant **table, const T &value, const T1 &next_value, const Ts &...vals)
{
table[I] = value_to_g_variant_one_inner<T>(value);
fill_g_variant_one_table_inner<I + 1, T1, Ts...>(table, next_value, vals...);
}
template <typename... Ts>
-GVariant *vals_to_g_variant(const Ts &...vals)
+inline GVariant *vals_to_g_variant(const Ts &...vals)
{
GVariant *table[sizeof...(vals)];
fill_g_variant_one_table_inner<0, Ts...>(table, vals...);
}
template <typename... Ts>
-GVariant *tuple_to_g_variant(const std::tuple<Ts...> &tuple)
+inline GVariant *tuple_to_g_variant(const std::tuple<Ts...> &tuple)
{
return std::apply(vals_to_g_variant<Ts...>, tuple);
}