struct DirBackend {
virtual void RemoveSubsession (const std::filesystem::path& subsession_path) const = 0;
+ virtual void SwitchSubsessionAway (const std::filesystem::path& subsession_path) const = 0;
+ virtual void SwitchSubsessionInto (const std::filesystem::path& subsession_path) const = 0;
};
fs::rename(subsession_path, tmp_subsession_path);
fs::remove_all(tmp_subsession_path);
}
+
+void DirBackendRegularDir::SwitchSubsessionAway (const std::filesystem::path& subsession_path) const
+{
+}
+
+void DirBackendRegularDir::SwitchSubsessionInto (const std::filesystem::path& subsession_path) const
+{
+}
struct DirBackendRegularDir : public DirBackend {
void RemoveSubsession (const std::filesystem::path& subsession_path) const override;
+ void SwitchSubsessionAway (const std::filesystem::path& subsession_path) const override;
+ void SwitchSubsessionInto (const std::filesystem::path& subsession_path) const override;
};
return dbrd;
}
+bool switch_user_subsession(const int session_uid, const std::string_view prev_subsession, const std::string_view next_subsession)
+{
+ /* We switch into next first, and only then switch away from prev. This is so
+ * that we are never in an intermediate "no session" state which is important
+ * for robustness (consider the case where Away succeeds and Into fails). */
+
+ try {
+ const auto next_subsession_path = fs::path(get_main_dir_by_user_id(session_uid)) / next_subsession;
+ const DirBackend& next_backend = GetBackendOfSubsession(next_subsession_path);
+ next_backend.SwitchSubsessionInto(next_subsession_path);
+ } catch (const std::exception &ex) {
+ LOGE("Logic exception %s\nwhile switching into subsession [session_uid=%d subsession_id=%s]", ex.what(), session_uid, next_subsession.data());
+ return false;
+ }
+
+ try {
+ const auto prev_subsession_path = fs::path(get_main_dir_by_user_id(session_uid)) / prev_subsession;
+ const DirBackend& prev_backend = GetBackendOfSubsession(prev_subsession_path);
+ prev_backend.SwitchSubsessionAway(prev_subsession_path);
+ } catch (const std::exception &ex) {
+ LOGE("Logic exception %s\nwhile switching away from user subsession [session_uid=%d subsession_id=%s]", ex.what(), session_uid, prev_subsession.data());
+ // fallthrough and return true, the new subsession was mounted after all
+ }
+
+ return true;
+}
+
void remove_user_subsession(const int session_uid, const std::string_view subsession_id)
{
try {
bool subsession_exists(const int session_uid, const std::string_view subsession_id);
void add_user_subsession(const int session_uid, const std::string_view subsession_id);
void remove_user_subsession(const int session_uid, const std::string_view subsession_id);
+bool switch_user_subsession(const int session_uid, const std::string_view prev_subsession, const std::string_view next_subsession);
std::vector<std::string> get_user_list(const int session_uid);
constexpr static std::string_view main_dir_name = "subsession";
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)) {
+ g_dbus_method_invocation_return_dbus_error(invocation,
+ get_dbus_error_mapping(SUBSESSION_ERROR_IO_ERROR), "Failed to switch away from previous subsession");
+ return;
+ }
+
last_subsession_per_session[session_uid] = next_subsession_id;
wait_switch.try_emplace(session_uid, session_uid, connection, "SwitchUserCompleted");