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
|
# 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
|
248
src/manager.c
248
src/manager.c
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user