From b6a91b02db07c75796f00f9e734893e29f6b93cb Mon Sep 17 00:00:00 2001 From: DanyLE Date: Sat, 8 Mar 2025 16:10:58 +0100 Subject: [PATCH] Add session manage mechanism --- conf/daemon.conf | 4 +- src/manager.c | 248 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 244 insertions(+), 8 deletions(-) diff --git a/conf/daemon.conf b/conf/daemon.conf index dcda051..a177dd0 100644 --- a/conf/daemon.conf +++ b/conf/daemon.conf @@ -9,7 +9,7 @@ # The command to run to start a login session # this command will handle the user input and send user # credentials to the daemon via Dbus message -login_session_command = /usr/bin/diya-login-shell +login_session_command = /usr/bin/diyac -x /usr/bin/diya-login-shell # login session user # The user that owns the login session, root by default @@ -21,4 +21,4 @@ login_session_user = xdg # login session is successful # the logged in user will own this session -user_session_command = /usr/bin/diya-shell \ No newline at end of file +user_session_command = /usr/bin/diyac -x /usr/bin/diya-shell \ No newline at end of file diff --git a/src/manager.c b/src/manager.c index 18fd2c8..3771c99 100644 --- a/src/manager.c +++ b/src/manager.c @@ -4,10 +4,23 @@ #include #include #include +#include +#include +#include + +#define PROCESS_MOM_TO 200 // in ms #define DBUS_SERVER_NAME "dev.iohub.diya.SessionManager" #define DBUS_SERVER_PATH "/dev/iohub/diya/SessionManager" #define DBUS_SERVER_ERROR_NAME "dev.iohub.diya.SessionManager.Error" + +typedef enum +{ + SESSION_MANAGER_STATE_IDLE, + SESSION_MANAGER_STATE_LOGIN, + SESSION_MANAGER_STATE_SESSION +} session_manage_state_t; + /** * @brief Object properties enumeration */ @@ -32,6 +45,8 @@ struct _SessionManager GMainLoop *loop; GDBusNodeInfo *introspection; guint bus_id; + session_manage_state_t state; + gchar* current_session_user; }; G_DEFINE_FINAL_TYPE(SessionManager, session_manager, BASE_TYPE_OBJECT); @@ -59,6 +74,10 @@ static void session_manager_dispose(GObject *object) { g_bus_unown_name(self->bus_id); } + if (self->current_session_user) + { + g_free(self->current_session_user); + } G_OBJECT_CLASS(session_manager_parent_class)->dispose(object); } @@ -111,11 +130,13 @@ static void session_manager_class_init(SessionManagerClass *class) static void session_manager_init(SessionManager *self) { self->configuration = NULL; - self->session_pid = 0; - self->login_session_pid = 0; + self->session_pid = -1; + self->login_session_pid = -1; self->loop = g_main_loop_new(NULL, FALSE); self->introspection = NULL; self->bus_id = 0; + self->state = SESSION_MANAGER_STATE_IDLE; + self->current_session_user = NULL; } static gboolean session_user_auth(const char *username, const char *password) @@ -160,6 +181,106 @@ static gboolean session_user_auth(const char *username, const char *password) } } +static pid_t run_command_as(const gchar* command, const gchar* username) +{ + g_debug("run_command_as: start user session command [%s] for user %s", command, username); + int numgroups; + int numgroups_max = sysconf(_SC_NGROUPS_MAX) + 1; + gid_t g_list[numgroups_max]; + struct passwd *pwd = getpwnam(username); + if (pwd == NULL) + { + g_critical("run_command_as: Unable to call getpwnam: %s", strerror(errno)); + return -1; + } + if(initgroups(username, 0) != 0) + { + g_critical("run_command_as: Unable to call initgroups: %s", strerror(errno)); + return -1; + } + if (getgrouplist(username, pwd->pw_gid, g_list, &numgroups) == -1) + { + g_critical("run_command_as: Unable to call getgrouplist: %s", strerror(errno)); + return -1; + } + pid_t pid = fork(); + if (pid == 0) + { + if (setgid(pwd->pw_gid) == -1) + { + g_critical("run_command_as: Unable to call setgid: %s", strerror(errno)); + exit(1); + } + g_debug("run_command_as: setgroups: %d", numgroups); + for(int i = 0; i < numgroups; i++) + { + g_debug("run_command_as: group: %d \n", g_list[i]); + } + if (setgroups(numgroups, g_list) == -1) + { + g_critical("run_command_as: Unable to call setgroups: %s", strerror(errno)); + exit(1); + } + if (setuid(pwd->pw_uid) == -1) + { + g_critical("run_command_as: Unable to call setuid: %s", strerror(errno)); + exit(1); + } + char env_user[128]; + char* args[] = {"/bin/sh", "-c", (char*)command, NULL}; + char* env[1] = { NULL}; + g_snprintf(env_user, sizeof(env_user), "USER=%s", username); + env[0] = env_user; + execve(args[0], args, env); + g_critical("execve: %s", strerror(errno)); + exit(-1); + } + return pid; +} + +/** + * @brief Start the user session + */ +static void start_user_session(SessionManager *self) +{ + if(self->current_session_user == NULL) + { + g_critical("start_user_session: Current session user is not set. No user logged in"); + return; + } + /** + * wait for the login session to exit + */ + if (self->login_session_pid > 0) + { + int status; + pid_t pid = waitpid(self->login_session_pid, &status, WNOHANG); + if(pid < 0) + { + g_warning("start_user_session: login session is still running or waitpid error: %s. Force kill it", strerror(errno)); + // try to kill it + (void)kill(self->login_session_pid, SIGKILL); + } + g_debug("start_user_session: login session is exited"); + self->login_session_pid = 0; + } + if(self->session_pid > 0) + { + g_warning("start_user_session: Cannot start user session for user %s. A session is currently running", self->current_session_user); + return; + } + gchar *session_command = NULL; + g_object_get(self->configuration, "session-command", &session_command, NULL); + if (session_command == NULL) + { + g_free(session_command); + g_critical("start_user_session: session_command is not set in the configuration file, do nothing"); + return; + } + self->session_pid = run_command_as(session_command, self->current_session_user); + g_free(session_command); +} + /** * @brief Method call handler on dbus interface */ @@ -169,16 +290,21 @@ static void session_manager_method_call(GDBusConnection *connection, const gchar (void)sender; (void)object_path; (void)interface_name; - (void)parameters; (void)user_data; SessionManager *self = SESSION_MANAGER(user_data); - g_debug("session_manager_method_call: %s", method_name); + g_debug("session_manager_method_call: %s, object path: %s, interface name: %s", method_name, object_path, interface_name); + if (g_strcmp0(interface_name, DBUS_SERVER_NAME) != 0) + { + g_critical("Interface name mismatch: got %s, but expected %s", interface_name, DBUS_SERVER_NAME); + g_dbus_method_invocation_return_dbus_error(invocation, DBUS_SERVER_ERROR_NAME, "Method not found"); + return; + } if (g_strcmp0(method_name, "login") == 0) { const gchar *username; const gchar *password; g_variant_get(parameters, "(&s&s)", &username, &password); - g_debug("Login request for user: %s with password %s", username, password); + g_debug("Login request for user: %s", username); if (self->session_pid > 0) { @@ -190,8 +316,15 @@ static void session_manager_method_call(GDBusConnection *connection, const gchar { g_dbus_method_invocation_return_value(invocation, g_variant_new("(b)", TRUE)); /** - * TODO: exit login session and start a new user session + * wait for login session to exit and start user session */ + if(self->current_session_user) + { + g_free(self->current_session_user); + self->current_session_user = NULL; + } + self->current_session_user = g_strdup(username); + self->state = SESSION_MANAGER_STATE_SESSION; } else { @@ -201,6 +334,7 @@ static void session_manager_method_call(GDBusConnection *connection, const gchar } else { + g_critical("Method not found: %s", method_name); g_dbus_method_invocation_return_dbus_error(invocation, DBUS_SERVER_ERROR_NAME, "Method not found"); } } @@ -303,6 +437,106 @@ SessionManager *session_manager_new(const gchar *config_file) return manager; } +static void start_login_session(SessionManager *self) +{ + if(self->session_pid > 0) + { + g_warning("start_login_session: Cannot start login session. A session is currently running"); + return; + } + if(self->login_session_pid > 0) + { + g_warning("start_login_session: login session is already running"); + return; + } + if(self->current_session_user) + { + g_free(self->current_session_user); + self->current_session_user = NULL; + } + gchar *login_session_command = NULL; + g_object_get(self->configuration, "login-session-command", &login_session_command, NULL); + if (login_session_command == NULL) + { + g_critical("login_session_command is not set in the configuration file, do nothing"); + return; + } + + // get the login login_session_user from configuration + gchar *login_session_user = NULL; + g_object_get(self->configuration, "login-session-user", &login_session_user, NULL); + if (login_session_user == NULL) + { + g_critical("login_session_user is not set in the configuration file, start the login session as root"); + return; + } + + self->login_session_pid = run_command_as(login_session_command, login_session_user); + g_free(login_session_user); + g_free(login_session_command); +} + +/** + * @brief Process monitor + */ +static gboolean processes_monitor(gpointer user_data) +{ + int status; + pid_t pid; + SessionManager *self = SESSION_MANAGER(user_data); + + switch (self->state) + { + case SESSION_MANAGER_STATE_IDLE: + if(self->session_pid > 0) + { + + pid = waitpid(self->session_pid, &status, WNOHANG); + if(pid == -1) + { + g_critical("session waitpid error: %s", strerror(errno)); + // try to kill it + (void)kill(self->session_pid, SIGKILL); + self->session_pid = -1; + } + if(pid == 0) + { + return TRUE; + } + self->session_pid = -1; + } + if(self->login_session_pid > 0) + { + pid = waitpid(self->login_session_pid, &status, WNOHANG); + if(pid == -1) + { + g_critical("login session waitpid error: %s", strerror(errno)); + // try to kill it + (void)kill(self->login_session_pid, SIGKILL); + self->login_session_pid = -1; + } + if(pid == 0) + { + return TRUE; + } + self->login_session_pid = -1; + } + self->state = SESSION_MANAGER_STATE_LOGIN; + break; + case SESSION_MANAGER_STATE_LOGIN: + start_login_session(self); + self->state = SESSION_MANAGER_STATE_IDLE; + break; + case SESSION_MANAGER_STATE_SESSION: + start_user_session(self); + self->state = SESSION_MANAGER_STATE_IDLE; + break; + default: + break; + } + return TRUE; +} + /** * @brief Start the session manager event loop */ @@ -315,6 +549,8 @@ void session_manager_event_loop_start(SessionManager *self) } g_debug("Session manager event loop"); + g_timeout_add(PROCESS_MOM_TO, processes_monitor, self); + g_main_loop_run(self->loop); }