diff --git a/.gitignore b/.gitignore
index e7c2d04..c8550a3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -139,4 +139,5 @@ m4/lt~obsolete.m4
# can automatically generate from config.status script
# (which is called by configure script))
Makefile
-
+.vscode
+build
\ No newline at end of file
diff --git a/README.md b/README.md
index ae5c3d7..cd8672a 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,31 @@
# diya-session-manager
+## Running the daemon
+
+By default, the daemon will look for this file in the following locations `/etc/diya/daemon.conf`
+or it can be specified using the -c option:
+
+```
+diya-session-manager -c /path/to/daemon.conf
+```
+Example of configuration file can be found in `conf/daemon.conf`
+
+As the session manager daemon listens on Dbus system bus,to run the daemon:
+- the variable environment `DBUS_SYSTEM_BUS_ADDRESS` shall be set and known by the daemon
+ e.g. `DBUS_SYSTEM_BUS_ADDRESS=unix:path=/run/dbus/system_bus_socket`
+- the bus policy shall be configured correctly in `/etc/dbus-1/system.d/` to allow or deny user
+ accessing tothe bus. See `conf/diya-dbus.conf` as an example of bus policy configuration
+
+## test with dbus-send
+
+```sh
+#introspect
+dbus-send --system --print-reply --reply-timeout=2000 --type=method_call --dest=dev.iohub.diya.SessionManager /dev/iohub/diya/SessionManager org.freedesktop.DBus.Introspectable.Introspect
+
+# monitor the bus
+dbus-monitor --system "path=/dev/iohub/diya/SessionManager"
+
+
+# request user login
+dbus-send --system --type=method_call --print-reply --dest=dev.iohub.diya.SessionManager /dev/iohub/diya/SessionManager dev.iohub.diya.SessionManager.login string:toto string:tata
+```
\ No newline at end of file
diff --git a/conf/daemon.conf b/conf/daemon.conf
new file mode 100644
index 0000000..dcda051
--- /dev/null
+++ b/conf/daemon.conf
@@ -0,0 +1,24 @@
+# this file is the configuration file for the SessionMaganer daemon
+# daemon
+# By default, the daemon will look for this file in the following locations
+# /etc/diya/daemon.conf
+# or it can be specified using the -c option
+# example: diya-session-manager -c /path/to/daemon.conf
+
+# Login session command
+# 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 user
+# The user that owns the login session, root by default
+# if this setting is not set
+login_session_user = xdg
+
+# User session command
+# The command to run to start a user session after the
+# 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
diff --git a/conf/diya-dbus.conf b/conf/diya-dbus.conf
new file mode 100644
index 0000000..a118ce7
--- /dev/null
+++ b/conf/diya-dbus.conf
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/meson.build b/meson.build
new file mode 100644
index 0000000..a5d0f08
--- /dev/null
+++ b/meson.build
@@ -0,0 +1,38 @@
+project('diya-session-manager',
+ ['c'],
+ version: '0.1.0',
+ license: 'MIT',
+ meson_version: '>=0.58.0',
+ default_options: ['c_std=gnu11', 'warning_level=3'])
+
+lib_so_version = '0'
+
+glib = dependency('glib-2.0')
+gobject = dependency('gobject-2.0')
+gio = dependency('gio-2.0')
+crypt = dependency('libcrypt')
+add_project_arguments(
+ [
+ '-Wno-pedantic',
+ '-Werror=implicit-function-declaration',
+ '-Werror=return-type',
+ ],
+ language: 'c')
+
+
+gnome=import('gnome')
+resources = gnome.compile_resources('resources','resources/gresource.xml')
+
+
+src = [
+ 'src/main.c',
+ 'src/configuration.c',
+ 'src/manager.c',
+ 'src/base.c',
+ resources
+]
+
+executable(
+ 'diya-session-manager',
+ src,
+ dependencies: [glib, gobject, gio, crypt])
\ No newline at end of file
diff --git a/resources/gresource.xml b/resources/gresource.xml
new file mode 100644
index 0000000..e97407b
--- /dev/null
+++ b/resources/gresource.xml
@@ -0,0 +1,6 @@
+
+
+
+ resources/introspection.xml
+
+
\ No newline at end of file
diff --git a/resources/introspection.xml b/resources/introspection.xml
new file mode 100644
index 0000000..977b9be
--- /dev/null
+++ b/resources/introspection.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/base.c b/src/base.c
new file mode 100644
index 0000000..e92c8f3
--- /dev/null
+++ b/src/base.c
@@ -0,0 +1,46 @@
+#include "base.h"
+
+G_DEFINE_ABSTRACT_TYPE(BaseObject, base_object, G_TYPE_OBJECT)
+
+/**
+ * @brief Dispose the object
+ */
+static void base_object_dispose(GObject* object)
+{
+ g_debug("base_object_dispose");
+ G_OBJECT_CLASS (base_object_parent_class)->dispose(object);
+}
+
+/**
+ * @brief Class initialization
+ */
+static void base_object_class_init(BaseObjectClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS(class);
+ class->to_string = NULL;
+ gobject_class->dispose = base_object_dispose;
+}
+
+/**
+ * @brief Object initialization
+ */
+static void base_object_init(BaseObject *self)
+{
+ (void) self;
+}
+
+/**
+ * @brief Get the string representation of the object
+ */
+const gchar* base_object_to_string(gpointer object)
+{
+ g_return_val_if_fail(BASE_IS_OBJECT(object), NULL);
+ BaseObject *self = BASE_OBJECT(object);
+ BaseObjectClass *class = BASE_OBJECT_GET_CLASS(self);
+ if (class->to_string)
+ {
+ g_debug("calling to_string");
+ return class->to_string(self);
+ }
+ return "BaseObject";
+}
\ No newline at end of file
diff --git a/src/base.h b/src/base.h
new file mode 100644
index 0000000..7827343
--- /dev/null
+++ b/src/base.h
@@ -0,0 +1,23 @@
+#ifndef BASE_H
+#define BASE_H
+#include
+
+/**
+ * Base class object
+ *
+ */
+#define BASE_TYPE_OBJECT (base_object_get_type())
+G_DECLARE_DERIVABLE_TYPE(BaseObject, base_object, BASE, OBJECT, GObject)
+
+struct _BaseObjectClass
+{
+ GObjectClass parent_class;
+ const gchar *(*to_string)(BaseObject *self);
+ /**
+ * @brief reserve for future use to
+ * define common API for descendants
+ */
+};
+
+const gchar *base_object_to_string(gpointer object);
+#endif
\ No newline at end of file
diff --git a/src/configuration.c b/src/configuration.c
new file mode 100644
index 0000000..6a9cca1
--- /dev/null
+++ b/src/configuration.c
@@ -0,0 +1,268 @@
+#include "configuration.h"
+#include
+#include
+#include
+
+#define DEFAULT_LOGIN_SESSION_USER "root"
+
+/**
+ * @brief Object properties enumeration
+ */
+enum
+{
+ NO_PROP,
+ DAEMON_LOGIN_SESSION_COMMAND,
+ DAEMON_LOGIN_SESSION_USER,
+ DAEMON_SESSION_COMMAND,
+ N_PROPERTIES
+};
+static GParamSpec *conf_properties[N_PROPERTIES] = {0};
+
+/**
+ * @brief Object private structure
+ */
+struct _DaemonConfiguration
+{
+ BaseObject parent_object;
+ gchar* login_session_command;
+ gchar* login_session_user;
+ gchar* session_command;
+ gchar* string;
+};
+
+G_DEFINE_FINAL_TYPE(DaemonConfiguration, daemon_configuration, BASE_TYPE_OBJECT)
+
+/**
+ * @brief Dispose the object
+ */
+static void daemon_configuration_dispose(GObject* object)
+{
+ DaemonConfiguration * self = DAEMON_CONFIGURATION(object);
+ g_debug("daemon_configuration_dispose: %s", base_object_to_string(self));
+ if(self->login_session_command)
+ {
+ g_free(self->login_session_command);
+ }
+ if(self->login_session_user)
+ {
+ g_free(self->login_session_user);
+ }
+ if(self->session_command)
+ {
+ g_free(self->session_command);
+ }
+ if(self->string)
+ {
+ g_free(self->string);
+ }
+ self->string = NULL;
+ G_OBJECT_CLASS(daemon_configuration_parent_class)->dispose(object);
+}
+/**
+ * @brief Set the object property
+ */
+static void daemon_configuration_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+ (void)object;
+ (void)value;
+ (void)pspec;
+ // DaemonConfiguration * self = DAEMON_CONFIGURATION(object);
+ switch (property_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+/**
+ * @brief Get the object property
+ */
+static void daemon_configuration_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+ DaemonConfiguration * self = DAEMON_CONFIGURATION(object);
+ switch (property_id)
+ {
+ case DAEMON_LOGIN_SESSION_COMMAND:
+ g_value_set_string(value, self->login_session_command);
+ break;
+ case DAEMON_LOGIN_SESSION_USER:
+ g_value_set_string(value, self->login_session_user);
+ break;
+ case DAEMON_SESSION_COMMAND:
+ g_value_set_string(value, self->session_command);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+/**
+ * @brief Get the string representation of the object
+ */
+static const gchar* daemon_configuration_to_string(BaseObject* object)
+{
+ g_debug("Call daemon_configuration_to_string");
+ DaemonConfiguration* self = DAEMON_CONFIGURATION(object);
+ return self->string;
+}
+
+/**
+ * @brief Class initialization
+ */
+static void daemon_configuration_class_init(DaemonConfigurationClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS(class);
+ BaseObjectClass *base_class = BASE_OBJECT_CLASS(class);
+
+ gobject_class->dispose = daemon_configuration_dispose;
+ gobject_class->set_property = daemon_configuration_set_property;
+ gobject_class->get_property = daemon_configuration_get_property;
+
+ base_class->to_string = daemon_configuration_to_string;
+
+ conf_properties[DAEMON_LOGIN_SESSION_COMMAND] = g_param_spec_string("login-session-command", NULL, "Login session command", NULL, G_PARAM_READABLE);
+ conf_properties[DAEMON_LOGIN_SESSION_USER] = g_param_spec_string("login-session-user", NULL, "Login session user", NULL, G_PARAM_READABLE);
+ conf_properties[DAEMON_SESSION_COMMAND] = g_param_spec_string("session-command", NULL, "Session command", NULL, G_PARAM_READABLE);
+ g_object_class_install_properties (gobject_class, N_PROPERTIES, conf_properties);
+}
+
+/**
+ * @brief Object initialization
+ */
+static void daemon_configuration_init(DaemonConfiguration *self)
+{
+ self->login_session_command = NULL;
+ self->login_session_user = NULL;
+ self->session_command = NULL;
+ self->string = NULL;
+}
+/**
+ * @brief Create a new DaemonConfiguration object
+ *
+ * read and parse the config_file
+ * The format of the config file is as follows:
+ * - line starting with # are comments and should be ignored
+ * - line starting with login_session_command= is the command to start the login session
+ * - line starting with login_session_user= is the user to start the login session
+ * - line starting with session_command= is the command to start the session
+ *
+ * @param config_file The path to the configuration file
+ * @return The DaemonConfiguration object
+ */
+DaemonConfiguration* daemon_configuration_new(const gchar* config_file)
+{
+ FILE* file = fopen(config_file, "r");
+ if(!file)
+ {
+ g_critical("Error opening the configuration file %s: %s", config_file, strerror(errno));
+ return NULL;
+ }
+ /**
+ * read file line by line
+ */
+ DaemonConfiguration* conf = g_object_new(DAEMON_TYPE_CONFIGURATION, NULL);
+ char* line = NULL;
+ ssize_t read;
+ gchar* ptr = NULL;
+ int conf_valid = 0;
+ size_t len = 0;
+ conf->string = g_strconcat("DaemonConfiguration from ",
+ config_file,
+ ":\n",
+ NULL);
+ ptr = conf->string;
+ while ((read = getline(&line, &len, file)) != -1)
+ {
+ if (line[0] == '#')
+ {
+ free(line);
+ line = NULL;
+ continue;
+ }
+ /**
+ * strip newline at the end
+ */
+ if (line[read - 1] == '\n')
+ {
+ line[read - 1] = '\0';
+ }
+ /**
+ * parse line using strok
+ */
+ char* value = NULL;
+ char* key = strtok_r(line, "=", &value);
+ if(!key || !value)
+ {
+ free(line);
+ line = NULL;
+ continue;
+ }
+ /**
+ * strim key and value at the begin and the end
+ */
+ while(*key == ' ')
+ {
+ key++;
+ }
+ char* end = key + strlen(key) - 1;
+ while(*end == ' ')
+ {
+ *end = '\0';
+ end--;
+ }
+ while(*value == ' ')
+ {
+ value++;
+ }
+ end = value + strlen(value) - 1;
+ while(*end == ' ')
+ {
+ *end = '\0';
+ end--;
+ }
+ conf_valid = 1;
+ if (strncmp(key, "login_session_command", 22) == 0)
+ {
+ conf->login_session_command = g_strdup(value);
+ }
+ else if (strncmp(key, "login_session_user", 19) == 0)
+ {
+ conf->login_session_user = g_strdup(value);
+ }
+ else if (strncmp(key, "user_session_command", 21) == 0)
+ {
+ conf->session_command = g_strdup(value);
+ }
+ else
+ {
+ g_warning("Ignore unknown configuration: [%s]:[%s]", key, value);
+ conf_valid = 0;
+ }
+ if(conf_valid)
+ {
+ conf->string = g_strconcat(ptr, " - ", key, ": ", value, "\n", NULL);
+ g_free(ptr);
+ ptr = conf->string;
+ }
+ free(line);
+ line = NULL;
+ }
+ if(line)
+ {
+ free(line);
+ }
+ fclose(file);
+ if(conf->login_session_user == NULL)
+ {
+ conf->login_session_user = g_strdup("root");
+ }
+ if(conf->login_session_command == NULL || conf->session_command == NULL)
+ {
+ g_critical("login_session_command or session_command is not set in the configuration file");
+ g_object_unref(conf);
+ return NULL;
+ }
+ return conf;
+}
\ No newline at end of file
diff --git a/src/configuration.h b/src/configuration.h
new file mode 100644
index 0000000..bc27015
--- /dev/null
+++ b/src/configuration.h
@@ -0,0 +1,10 @@
+#ifndef CONFIGURATION_H
+#define CONFIGURATION_H
+#include "base.h"
+
+#define DAEMON_TYPE_CONFIGURATION (daemon_configuration_get_type())
+G_DECLARE_FINAL_TYPE(DaemonConfiguration, daemon_configuration, DAEMON, CONFIGURATION, BaseObject)
+
+DaemonConfiguration* daemon_configuration_new(const gchar* config_file);
+
+#endif
\ No newline at end of file
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..a0aeadc
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,93 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "manager.h"
+#include "configuration.h"
+
+#define DEFALT_CONF_FILE "/etc/diya/daemon.conf"
+
+static gchar **g_shell_argv;
+
+static void help(const char* name)
+{
+ printf("Usage: %s [-c config_file]\n" ,name);
+}
+
+static gboolean restart(gpointer d)
+{
+ (void)d;
+ gint i, fdlimit;
+
+ fdlimit = (int)sysconf(_SC_OPEN_MAX);
+ g_debug("reload: closing fd's %d to %d", STDERR_FILENO + 1, fdlimit);
+ for (i = STDERR_FILENO + 1; i < fdlimit; i++)
+ {
+ fcntl(i, F_SETFD, FD_CLOEXEC);
+ }
+ g_debug("reload: exec: %s", g_shell_argv[0]);
+ execvp(g_shell_argv[0], g_shell_argv);
+ exit(1);
+ return FALSE;
+}
+
+static gboolean quit(gpointer d)
+{
+ g_debug("Quit the daemon");
+ session_manager_event_loop_stop((SessionManager*)d);
+ return TRUE;
+}
+
+int main(int argc, char* const *argv)
+{
+ /**
+ * Read command line arguments using getopt:
+ * - if the argument is -h or --help print the help message
+ * - if the argument is -c or --config set the configuration file
+ */
+ const gchar* config_file = DEFALT_CONF_FILE;
+ int opt;
+ while ((opt = getopt(argc, argv, "hc:")) != -1)
+ {
+ switch (opt)
+ {
+ case 'h':
+ help(argv[0]);
+ return 0;
+ case 'c':
+ config_file = optarg;
+ break;
+ default:
+ help(argv[0]);
+ return 1;
+ }
+ }
+ g_shell_argv = g_malloc0(sizeof(gchar *) * (argc + 1));
+ for (int i = 0; i < argc; i++)
+ {
+ g_shell_argv[i] = argv[i];
+ }
+ SessionManager* manager = session_manager_new(config_file);
+ if (!manager)
+ {
+ g_error("Error creating the session manager");
+ return -1;
+ }
+ g_unix_signal_add(SIGHUP, (GSourceFunc)restart, NULL);
+ g_unix_signal_add(SIGINT,(GSourceFunc)quit,(void*)manager);
+ DaemonConfiguration* conf = NULL;
+ g_object_get(manager, "configuration", &conf, NULL);
+ if(conf)
+ {
+ g_debug("Run daemon with: %s", base_object_to_string(conf));
+ }
+ session_manager_event_loop_start(manager);
+ g_debug("Release the manager");
+ g_object_unref(manager);
+ return 0;
+}
diff --git a/src/manager.c b/src/manager.c
new file mode 100644
index 0000000..18fd2c8
--- /dev/null
+++ b/src/manager.c
@@ -0,0 +1,327 @@
+#include "manager.h"
+#include "configuration.h"
+#include
+#include
+#include
+#include
+
+#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"
+/**
+ * @brief Object properties enumeration
+ */
+enum
+{
+ SESSION_MANAGER_NO_PROP,
+ SESSION_MANAGER_CONFIGURATION,
+ N_PROPERTIES
+};
+
+static GParamSpec *manager_properties[N_PROPERTIES] = {0};
+
+/**
+ * @brief Object private structure
+ */
+struct _SessionManager
+{
+ BaseObject parent_object;
+ DaemonConfiguration *configuration;
+ pid_t session_pid;
+ pid_t login_session_pid;
+ GMainLoop *loop;
+ GDBusNodeInfo *introspection;
+ guint bus_id;
+};
+
+G_DEFINE_FINAL_TYPE(SessionManager, session_manager, BASE_TYPE_OBJECT);
+
+/**
+ * @brief Dispose the object
+ */
+static void session_manager_dispose(GObject *object)
+{
+ SessionManager *self = SESSION_MANAGER(object);
+ g_debug("session_manager_dispose: %s", base_object_to_string(self));
+ if (self->configuration)
+ {
+ g_object_unref(self->configuration);
+ }
+ if (self->loop)
+ {
+ g_main_loop_unref(self->loop);
+ }
+ if (self->introspection)
+ {
+ g_dbus_node_info_unref(self->introspection);
+ }
+ if (self->bus_id > 0)
+ {
+ g_bus_unown_name(self->bus_id);
+ }
+ G_OBJECT_CLASS(session_manager_parent_class)->dispose(object);
+}
+
+/**
+ * @brief Get the string representation of the object
+ */
+static const gchar *session_manager_to_string(BaseObject *object)
+{
+ (void)object;
+ // SessionManager* self = SESSION_MANAGER(object);
+ return "SessionManager";
+}
+
+/**
+ * @brief Get the object property
+ */
+static void session_manager_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+ SessionManager *self = SESSION_MANAGER(object);
+ switch (property_id)
+ {
+ case SESSION_MANAGER_CONFIGURATION:
+ g_value_set_pointer(value, self->configuration);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ break;
+ }
+}
+
+/**
+ * @brief Class initialization
+ */
+static void session_manager_class_init(SessionManagerClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS(class);
+ BaseObjectClass *base_class = BASE_OBJECT_CLASS(class);
+ gobject_class->dispose = session_manager_dispose;
+ gobject_class->get_property = session_manager_get_property;
+
+ base_class->to_string = session_manager_to_string;
+
+ manager_properties[SESSION_MANAGER_CONFIGURATION] = g_param_spec_pointer("configuration", NULL, "Configuration", G_PARAM_READABLE);
+ g_object_class_install_properties(gobject_class, N_PROPERTIES, manager_properties);
+}
+
+/**
+ * @brief Object initialization
+ */
+static void session_manager_init(SessionManager *self)
+{
+ self->configuration = NULL;
+ self->session_pid = 0;
+ self->login_session_pid = 0;
+ self->loop = g_main_loop_new(NULL, FALSE);
+ self->introspection = NULL;
+ self->bus_id = 0;
+}
+
+static gboolean session_user_auth(const char *username, const char *password)
+{
+ char *encrypted;
+ struct passwd *pwd;
+ struct spwd *spwd;
+ /* Look up password and shadow password records for username */
+ pwd = getpwnam(username);
+ if (pwd == NULL)
+ {
+ g_critical("getpwnam: %s", strerror(errno));
+ return FALSE;
+ }
+ spwd = getspnam(username);
+ /*
+ if (spwd == NULL)
+ {
+ g_critical("getspnam: %s", strerror(errno));
+ return FALSE;
+ }
+ */
+
+ if (spwd != NULL) /* If there is a shadow password record */
+ {
+ pwd->pw_passwd = spwd->sp_pwdp; /* Use the shadow password */
+ }
+ /* Encrypt password and erase cleartext version immediately */
+ encrypted = crypt(password, pwd->pw_passwd);
+ if (encrypted == NULL)
+ {
+ g_critical("crypt: %s", strerror(errno));
+ return FALSE;
+ }
+ if (strcmp(encrypted, pwd->pw_passwd) == 0)
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+/**
+ * @brief Method call handler on dbus interface
+ */
+static void session_manager_method_call(GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data)
+{
+ (void)connection;
+ (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);
+ 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);
+
+ if (self->session_pid > 0)
+ {
+ g_warning("Cannot process login request for user %s. A session is currently running", username);
+ g_dbus_method_invocation_return_dbus_error(invocation, DBUS_SERVER_ERROR_NAME, "A session is currently running");
+ return;
+ }
+ if(session_user_auth(username, password))
+ {
+ g_dbus_method_invocation_return_value(invocation, g_variant_new("(b)", TRUE));
+ /**
+ * TODO: exit login session and start a new user session
+ */
+ }
+ else
+ {
+ g_dbus_method_invocation_return_value(invocation, g_variant_new("(b)", FALSE));
+ }
+ // g_variant_unref(result);
+ }
+ else
+ {
+ g_dbus_method_invocation_return_dbus_error(invocation, DBUS_SERVER_ERROR_NAME, "Method not found");
+ }
+}
+
+/**
+ * @brief Bus listener callback
+ */
+static const GDBusInterfaceVTable interface_vtable =
+ {
+ .method_call = session_manager_method_call,
+ .get_property = NULL,
+ .set_property = NULL,
+};
+
+/**
+ * @brief Bus acquired callback
+ */
+static void on_bus_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data)
+{
+ SessionManager *self = SESSION_MANAGER(user_data);
+ g_debug("on_bus_acquired: %s", name);
+ GError *error = NULL;
+ if (!g_dbus_connection_register_object(
+ connection,
+ DBUS_SERVER_PATH,
+ self->introspection->interfaces[0],
+ &interface_vtable,
+ user_data,
+ NULL,
+ &error))
+ {
+ g_critical("Failed to register object: %s", error->message);
+ g_error_free(error);
+ session_manager_event_loop_stop(self);
+ }
+}
+
+/**
+ * @brief Name acquired callback
+ */
+static void on_name_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data)
+{
+ (void)connection;
+ (void)user_data;
+ g_debug("on_name_acquired: %s", name);
+}
+
+/**
+ * @brief Name lost callback
+ */
+static void on_name_lost(GDBusConnection *connection, const gchar *name, gpointer user_data)
+{
+ (void)connection;
+ g_critical("DBus session manager name lost, probably because of user not having access rights on the bus: %s", name);
+ SessionManager *self = SESSION_MANAGER(user_data);
+ session_manager_event_loop_stop(self);
+}
+
+/**
+ * @brief Create a new SessionManager object
+ */
+SessionManager *session_manager_new(const gchar *config_file)
+{
+ SessionManager *manager = g_object_new(SESSION_TYPE_MANAGER, NULL);
+ manager->configuration = daemon_configuration_new(config_file);
+ if (!manager->configuration)
+ {
+ return NULL;
+ }
+ // load xml content from gresource
+ GError *err = NULL;
+ GBytes *bytes = g_resources_lookup_data("/dev/iohub/diya/SessionManager/resources/introspection.xml", 0, &err);
+ if (err != NULL)
+ {
+ g_critical("Unable to load introspection resource: %s", err->message);
+ g_error_free(err);
+ g_object_unref(manager);
+ return NULL;
+ }
+ const char *data = g_bytes_get_data(bytes, NULL);
+ g_debug("introspection xml: \n %s", data);
+ manager->introspection = g_dbus_node_info_new_for_xml(data, &err);
+ g_bytes_unref(bytes);
+ if (err != NULL)
+ {
+ g_critical("Unable to parse introspection xml: %s", err->message);
+ g_error_free(err);
+ g_object_unref(manager);
+ return NULL;
+ }
+ manager->bus_id = g_bus_own_name(G_BUS_TYPE_SYSTEM,
+ DBUS_SERVER_NAME,
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ on_bus_acquired,
+ on_name_acquired,
+ on_name_lost,
+ manager,
+ NULL);
+
+ return manager;
+}
+
+/**
+ * @brief Start the session manager event loop
+ */
+void session_manager_event_loop_start(SessionManager *self)
+{
+ if (g_main_loop_is_running(self->loop))
+ {
+ g_warning("Session manager already started. do nothing");
+ return;
+ }
+ g_debug("Session manager event loop");
+
+ g_main_loop_run(self->loop);
+}
+
+/**
+ * @brief Stop the session manager event loop
+ */
+void session_manager_event_loop_stop(SessionManager *self)
+{
+ g_main_loop_quit(self->loop);
+}
\ No newline at end of file
diff --git a/src/manager.h b/src/manager.h
new file mode 100644
index 0000000..b490cec
--- /dev/null
+++ b/src/manager.h
@@ -0,0 +1,12 @@
+#ifndef MANAGER_H
+#define MANAGER_H
+
+#include "base.h"
+
+#define SESSION_TYPE_MANAGER (session_manager_get_type())
+G_DECLARE_FINAL_TYPE(SessionManager, session_manager, SESSION, MANAGER, BaseObject)
+
+SessionManager* session_manager_new(const gchar* config_file);
+void session_manager_event_loop_start(SessionManager* manager);
+void session_manager_event_loop_stop(SessionManager* manager);
+#endif
\ No newline at end of file