--- /dev/null
+/* MIT License
+ *
+ * Copyright (c) 2025 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. */
+
+#include "fs_helpers.hpp"
+#include "main_context.hpp"
+#include "plugin.hpp"
+
+#undef LOG_TAG
+#define LOG_TAG "SESSIOND"
+#include <dlog.h>
+
+#include <system/syscommon-internal-sessiond.h>
+
+#ifndef EXPORT
+ #define EXPORT __attribute__((visibility("default")))
+#endif
+
+extern "C"
+{
+ /* In addition to listening, plugin can also push events. The interface is
+ * a bit poorly designed due to time constraints and should probably have
+ * "proper" errors from exceptions etc at some point.
+ *
+ * Some caveats:
+ *
+ * - the plugin receives OnSubsessionX from its own events.
+ *
+ * - if you call sessiond_internal_x_subsession from within
+ * OnSubsessionX, there will be an inversion in how D-Bus
+ * notifications to other processes are ordered
+ *
+ * - sessiond assumes single-threading and doing anything in
+ * a separate thread is a race condition. */
+
+ EXPORT int sessiond_internal_add_subsession(int uid, const char *subsession)
+ {
+ LOGD("Called sessiond_internal_add_subsession(%d, %s)", uid, subsession);
+ if (!check_uid_valid(uid) || !check_subsession_id_valid(subsession)) {
+ LOGE("Invalid arguments to sessiond_internal_add_subsession(%d, %s)", uid, subsession);
+ return -EINVAL;
+ }
+
+ try {
+ g_sessiond_context->do_add_user(uid, subsession, DirBackendAddRegularDir {});
+ } catch (const std::exception &ex) {
+ LOGE("Error in sessiond_internal_add_subsession(%d, %s): %s", uid, subsession, ex.what());
+ return -EIO;
+ }
+ return 0;
+ }
+
+ EXPORT int sessiond_internal_add_subsession_fixed_size(int uid, const char *subsession, unsigned size_kB)
+ {
+ LOGD("Called sessiond_internal_add_subsession_fixed_size(%d, %s, %u kB)", uid, subsession, size_kB);
+ if (!check_uid_valid(uid) || !check_subsession_id_valid(subsession) || !check_subsession_size_limit_valid(size_kB)) {
+ LOGE("Invalid arguments to sessiond_internal_add_subsession_fixed_size(%d, %s, %u kB)", uid, subsession, size_kB);
+ return -EINVAL;
+ }
+
+ try {
+ g_sessiond_context->do_add_user(uid, subsession, DirBackendAddFixedSize {size_kB});
+ } catch (const std::exception &ex) {
+ LOGE("Error in sessiond_internal_add_subsession_fixed_size(%d, %s, %u kB): %s", uid, subsession, size_kB, ex.what());
+ return -EIO;
+ }
+ return 0;
+ }
+
+ EXPORT int sessiond_internal_remove_subsession(int uid, const char *subsession)
+ {
+ LOGD("Called sessiond_internal_remove_subsession(%d, %s)", uid, subsession);
+ if (!check_uid_valid(uid) || !check_subsession_id_valid(subsession)) {
+ LOGE("Invalid arguments: sessiond_internal_add_subsession(%d, %s)", uid, subsession);
+ return -EINVAL;
+ }
+ if (g_sessiond_context->is_subsession_currently_active(uid, subsession)) {
+ LOGW("Cannot remove uid %d's currently active subsession %s", uid, subsession);
+ return -EBUSY;
+ }
+
+ try {
+ g_sessiond_context->do_remove_user(uid, subsession);
+ } catch (const std::exception &ex) {
+ LOGE("Error in sessiond_internal_remove_subsession(%d, %s): %s", uid, subsession, ex.what());
+ return -EIO;
+ }
+ return 0;
+ }
+
+ EXPORT int sessiond_internal_switch_subsession(int uid, const char *subsession)
+ {
+ LOGD("Called sessiond_internal_switch_subsession(%d, %s)", uid, subsession);
+
+ const bool isEmptySubsession = !strcmp(subsession, "");
+ if (!check_uid_valid(uid) || !(isEmptySubsession || check_subsession_id_valid(subsession))) {
+ LOGE("Invalid arguments: sessiond_internal_switch_subsession(%d, %s)", uid, subsession);
+ return -EINVAL;
+ }
+
+ if (!isEmptySubsession && !subsession_exists(uid, subsession)) {
+ LOGW("Cannot switch to nonexistent subsession %s (uid %d)", uid, subsession);
+ return -EEXIST;
+ }
+
+ try {
+ g_sessiond_context->do_switch_user(uid, subsession);
+ } catch (const std::exception &ex) {
+ LOGE("Error in sessiond_internal_switch_subsession(%d, %s): %s", uid, subsession, ex.what());
+ return -EIO;
+ }
+ return 0;
+ }
+};