Add session manage mechanism
This commit is contained in:
parent
c75274f3d2
commit
b6a91b02db
@ -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
|
||||
user_session_command = /usr/bin/diyac -x /usr/bin/diya-shell
|
248
src/manager.c
248
src/manager.c
@ -4,10 +4,23 @@
|
||||
#include <pwd.h>
|
||||
#include <grp.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_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);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user