Add session manage mechanism

This commit is contained in:
DanyLE 2025-03-08 16:10:58 +01:00
parent c75274f3d2
commit b6a91b02db
2 changed files with 244 additions and 8 deletions

View File

@ -9,7 +9,7 @@
# The command to run to start a login session # The command to run to start a login session
# this command will handle the user input and send user # this command will handle the user input and send user
# credentials to the daemon via Dbus message # 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 # login session user
# The user that owns the login session, root by default # The user that owns the login session, root by default
@ -21,4 +21,4 @@ login_session_user = xdg
# login session is successful # login session is successful
# the logged in user will own this session # the logged in user will own this session
user_session_command = /usr/bin/diya-shell user_session_command = /usr/bin/diyac -x /usr/bin/diya-shell

View File

@ -4,10 +4,23 @@
#include <pwd.h> #include <pwd.h>
#include <grp.h> #include <grp.h>
#include <shadow.h> #include <shadow.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#define PROCESS_MOM_TO 200 // in ms
#define DBUS_SERVER_NAME "dev.iohub.diya.SessionManager" #define DBUS_SERVER_NAME "dev.iohub.diya.SessionManager"
#define DBUS_SERVER_PATH "/dev/iohub/diya/SessionManager" #define DBUS_SERVER_PATH "/dev/iohub/diya/SessionManager"
#define DBUS_SERVER_ERROR_NAME "dev.iohub.diya.SessionManager.Error" #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 * @brief Object properties enumeration
*/ */
@ -32,6 +45,8 @@ struct _SessionManager
GMainLoop *loop; GMainLoop *loop;
GDBusNodeInfo *introspection; GDBusNodeInfo *introspection;
guint bus_id; guint bus_id;
session_manage_state_t state;
gchar* current_session_user;
}; };
G_DEFINE_FINAL_TYPE(SessionManager, session_manager, BASE_TYPE_OBJECT); 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); 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); 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) static void session_manager_init(SessionManager *self)
{ {
self->configuration = NULL; self->configuration = NULL;
self->session_pid = 0; self->session_pid = -1;
self->login_session_pid = 0; self->login_session_pid = -1;
self->loop = g_main_loop_new(NULL, FALSE); self->loop = g_main_loop_new(NULL, FALSE);
self->introspection = NULL; self->introspection = NULL;
self->bus_id = 0; 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) 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 * @brief Method call handler on dbus interface
*/ */
@ -169,16 +290,21 @@ static void session_manager_method_call(GDBusConnection *connection, const gchar
(void)sender; (void)sender;
(void)object_path; (void)object_path;
(void)interface_name; (void)interface_name;
(void)parameters;
(void)user_data; (void)user_data;
SessionManager *self = SESSION_MANAGER(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) if (g_strcmp0(method_name, "login") == 0)
{ {
const gchar *username; const gchar *username;
const gchar *password; const gchar *password;
g_variant_get(parameters, "(&s&s)", &username, &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) 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)); 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 else
{ {
@ -201,6 +334,7 @@ static void session_manager_method_call(GDBusConnection *connection, const gchar
} }
else else
{ {
g_critical("Method not found: %s", method_name);
g_dbus_method_invocation_return_dbus_error(invocation, DBUS_SERVER_ERROR_NAME, "Method not found"); 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; 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 * @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_debug("Session manager event loop");
g_timeout_add(PROCESS_MOM_TO, processes_monitor, self);
g_main_loop_run(self->loop); g_main_loop_run(self->loop);
} }