Base code for session manager that supports user authentification via dbus
This commit is contained in:
		
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -139,4 +139,5 @@ m4/lt~obsolete.m4 | ||||
| # can automatically generate from config.status script | ||||
| # (which is called by configure script)) | ||||
| Makefile | ||||
|  | ||||
| .vscode | ||||
| build | ||||
							
								
								
									
										29
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								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 | ||||
| ``` | ||||
							
								
								
									
										24
									
								
								conf/daemon.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								conf/daemon.conf
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
							
								
								
									
										29
									
								
								conf/diya-dbus.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								conf/diya-dbus.conf
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| <!DOCTYPE busconfig PUBLIC | ||||
|           "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" | ||||
|           "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> | ||||
| <busconfig> | ||||
|  | ||||
|   <!-- Only root can own the service --> | ||||
|   <policy user="root"> | ||||
|     <allow own="dev.iohub.diya.SessionManager"/> | ||||
|   </policy> | ||||
|    | ||||
|   <!--deny all access from another user--> | ||||
|     <policy context="default"> | ||||
|         <deny send_destination="dev.iohub.diya.SessionManager"/> | ||||
|         <deny receive_sender="dev.iohub.diya.SessionManager"/> | ||||
|   </policy> | ||||
|    | ||||
|   <!-- | ||||
|   <policy group="xdg"> | ||||
|     <allow send_destination="dev.iohub.diya.SessionManager"/> | ||||
|     <allow receive_sender="dev.iohub.diya.SessionManager"/> | ||||
|   </policy> | ||||
|   --> | ||||
|    | ||||
|   <policy user="root"> | ||||
|     <allow send_destination="dev.iohub.diya.SessionManager"/> | ||||
|     <allow receive_sender="dev.iohub.diya.SessionManager"/> | ||||
|   </policy> | ||||
|  | ||||
| </busconfig> | ||||
							
								
								
									
										38
									
								
								meson.build
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								meson.build
									
									
									
									
									
										Normal file
									
								
							| @@ -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]) | ||||
							
								
								
									
										6
									
								
								resources/gresource.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								resources/gresource.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <gresources> | ||||
|     <gresource prefix="/dev/iohub/diya/SessionManager"> | ||||
|         <file>resources/introspection.xml</file> | ||||
|     </gresource> | ||||
| </gresources> | ||||
							
								
								
									
										10
									
								
								resources/introspection.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								resources/introspection.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| <node name = "/dev/iohub/diya"> | ||||
|     <interface name='dev.iohub.diya.SessionManager'> | ||||
|         <method name='login'> | ||||
|             <!--annotation name='org.gtk.GDBus.Annotation' value='OnMethod' /--> | ||||
|             <arg type='s' name='user' direction='in' /> | ||||
|             <arg type='s' name='password' direction='in' /> | ||||
|             <arg type='b' name='result' direction='out' /> | ||||
|         </method> | ||||
|     </interface> | ||||
| </node> | ||||
							
								
								
									
										46
									
								
								src/base.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/base.c
									
									
									
									
									
										Normal file
									
								
							| @@ -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"; | ||||
| } | ||||
							
								
								
									
										23
									
								
								src/base.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/base.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| #ifndef BASE_H | ||||
| #define BASE_H | ||||
| #include <glib-object.h> | ||||
|  | ||||
| /** | ||||
|  * 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 | ||||
							
								
								
									
										268
									
								
								src/configuration.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										268
									
								
								src/configuration.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,268 @@ | ||||
| #include "configuration.h" | ||||
| #include <fcntl.h> | ||||
| #include <stddef.h> | ||||
| #include <stdio.h> | ||||
|  | ||||
| #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; | ||||
| } | ||||
							
								
								
									
										10
									
								
								src/configuration.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/configuration.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
							
								
								
									
										93
									
								
								src/main.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								src/main.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,93 @@ | ||||
| #include <unistd.h> | ||||
| #include <fcntl.h> | ||||
| #include <string.h> | ||||
| #include <errno.h> | ||||
| #include <stddef.h> | ||||
| #include <stdlib.h> | ||||
| #include <stdio.h> | ||||
| #include <glib-unix.h> | ||||
|  | ||||
| #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; | ||||
| } | ||||
							
								
								
									
										327
									
								
								src/manager.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										327
									
								
								src/manager.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,327 @@ | ||||
| #include "manager.h" | ||||
| #include "configuration.h" | ||||
| #include <gio/gio.h> | ||||
| #include <pwd.h> | ||||
| #include <grp.h> | ||||
| #include <shadow.h> | ||||
|  | ||||
| #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); | ||||
| } | ||||
							
								
								
									
										12
									
								
								src/manager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/manager.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
		Reference in New Issue
	
	Block a user