Base code for session manager that supports user authentification via dbus
This commit is contained in:
parent
d82845ecf8
commit
c75274f3d2
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
|
Loading…
x
Reference in New Issue
Block a user