From f86c93014e698d81d43fe1ebaf805fa794e5a984 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Tue, 22 Oct 2013 15:42:16 -0400 Subject: [PATCH] daemon: rip out extension interface It requires newer glib than we're shipping --- configure.ac | 2 +- src/Makefile.am | 1 - src/daemon.c | 11 --- src/daemon.h | 3 - src/user.c | 273 -------------------------------------------------------- 5 files changed, 1 insertion(+), 289 deletions(-) diff --git a/configure.ac b/configure.ac index cb1fcda..a7f4e20 100644 --- a/configure.ac +++ b/configure.ac @@ -1,58 +1,58 @@ AC_INIT([AccountsService],[0.6.35]) AM_INIT_AUTOMAKE(no-dist-gzip dist-xz tar-ustar foreign) GETTEXT_PACKAGE=accounts-service AC_SUBST(GETTEXT_PACKAGE) AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE", [the gettext translation domain]) # Support silent build rules, requires at least automake-1.11. Enable # by either passing --enable-silent-rules to configure or passing V=0 # to make m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) AC_USE_SYSTEM_EXTENSIONS AC_PROG_CC PKG_PROG_PKG_CONFIG AM_GLIB_GNU_GETTEXT IT_PROG_INTLTOOL([0.40.0]) LT_INIT LT_CURRENT=0 LT_REVISION=0 LT_AGE=0 AC_SUBST(LT_CURRENT) AC_SUBST(LT_REVISION) AC_SUBST(LT_AGE) -PKG_CHECK_MODULES(GIO, gio-2.0 >= 2.37.3 gio-unix-2.0) +PKG_CHECK_MODULES(GIO, gio-2.0 gio-unix-2.0) PKG_CHECK_MODULES(POLKIT, gio-unix-2.0 polkit-gobject-1) AM_MAINTAINER_MODE([enable]) # client library dependencies LIBACCOUNTSSERVICE_LIBS="$GIO_LIBS" AC_SUBST(LIBACCOUNTSSERVICE_LIBS) LIBACCOUNTSSERVICE_CFLAGS="$GIO_CFLAGS" AC_SUBST(LIBACCOUNTSSERVICE_CFLAGS) GOBJECT_INTROSPECTION_CHECK([0.9.12]) dnl --------------------------------------------------------------------------- dnl - Core configuration dnl --------------------------------------------------------------------------- AC_ARG_ENABLE(admin-group, [AS_HELP_STRING([--enable-admin-group],[Set group for administrative accounts @<:@default=auto@:>@])], ,enable_admin_group=auto) AS_IF([test x$enable_admin_group = xauto], [ AC_CHECK_FILE(/etc/redhat-release, enable_admin_group=wheel) AC_CHECK_FILE(/etc/debian_version, enable_admin_group=sudo) AS_IF([test x$enable_admin_group = xauto], [ enable_admin_group=wheel ]) ]) AC_DEFINE_UNQUOTED([ADMIN_GROUP], ["$enable_admin_group"], [Define to the group for administrator users]) AC_ARG_ENABLE(user-heuristics, [AS_HELP_STRING([--enable-user-heuristics],[Enable heuristics for guessing system vs. human users])], diff --git a/src/Makefile.am b/src/Makefile.am index 6940f2d..de57e7a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -7,52 +7,51 @@ INCLUDES = \ -DICONDIR=\"$(localstatedir)/lib/AccountsService/icons\" \ -DUSERDIR=\"$(localstatedir)/lib/AccountsService/users\" \ -I$(srcdir) \ -I$(builddir) \ $(POLKIT_CFLAGS) \ $(WARN_CFLAGS) noinst_LTLIBRARIES = libaccounts-generated.la libaccounts_generated_la_SOURCES = \ accounts-generated.c \ accounts-generated.h \ accounts-user-generated.c \ accounts-user-generated.h \ $(NULL) BUILT_SOURCES += $(libaccounts_generated_la_SOURCES) accounts-generated.c accounts-generated.h: $(top_srcdir)/data/org.freedesktop.Accounts.xml Makefile gdbus-codegen --generate-c-code accounts-generated --c-namespace Accounts --interface-prefix=org.freedesktop. $(top_srcdir)/data/org.freedesktop.Accounts.xml accounts-user-generated.c accounts-user-generated.h: $(top_srcdir)/data/org.freedesktop.Accounts.User.xml Makefile gdbus-codegen --generate-c-code accounts-user-generated --c-namespace Accounts --interface-prefix=org.freedesktop.Accounts. $(top_srcdir)/data/org.freedesktop.Accounts.User.xml libexec_PROGRAMS = accounts-daemon accounts_daemon_SOURCES = \ $(enums_h_sources) \ types.h \ daemon.h \ daemon.c \ - extensions.c \ user-classify.h \ user-classify.c \ user.h \ user.c \ util.h \ util.c \ main.c accounts_daemon_LDADD = \ libaccounts-generated.la \ $(POLKIT_LIBS) CLEANFILES = \ $(BUILT_SOURCES) \ *.gcda \ *.gcno \ $(NULL) install-data-hook: $(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/AccountsService/users" $(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/AccountsService/icons" diff --git a/src/daemon.c b/src/daemon.c index 9c9f617..ea75190 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -53,61 +53,60 @@ #define PATH_GDM_CUSTOM "/etc/gdm/custom.conf" #ifdef HAVE_UTMPX_H #define PATH_WTMP _PATH_WTMPX #endif enum { PROP_0, PROP_DAEMON_VERSION }; struct DaemonPrivate { GDBusConnection *bus_connection; GDBusProxy *bus_proxy; GHashTable *users; User *autologin; GFileMonitor *passwd_monitor; GFileMonitor *shadow_monitor; GFileMonitor *group_monitor; GFileMonitor *gdm_monitor; #ifdef HAVE_UTMPX_H GFileMonitor *wtmp_monitor; #endif guint reload_id; guint autologin_id; PolkitAuthority *authority; - GHashTable *extension_ifaces; }; typedef struct passwd * (* EntryGeneratorFunc) (GHashTable *, gpointer *); static void daemon_accounts_accounts_iface_init (AccountsAccountsIface *iface); G_DEFINE_TYPE_WITH_CODE (Daemon, daemon, ACCOUNTS_TYPE_ACCOUNTS_SKELETON, G_IMPLEMENT_INTERFACE (ACCOUNTS_TYPE_ACCOUNTS, daemon_accounts_accounts_iface_init)); #define DAEMON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TYPE_DAEMON, DaemonPrivate)) static const GDBusErrorEntry accounts_error_entries[] = { { ERROR_FAILED, "org.freedesktop.Accounts.Error.Failed" }, { ERROR_USER_EXISTS, "org.freedesktop.Accounts.Error.UserExists" }, { ERROR_USER_DOES_NOT_EXIST, "org.freedesktop.Accounts.Error.UserDoesNotExist" }, { ERROR_PERMISSION_DENIED, "org.freedesktop.Accounts.Error.PermissionDenied" }, { ERROR_NOT_SUPPORTED, "org.freedesktop.Accounts.Error.NotSupported" } }; GQuark error_quark (void) { static volatile gsize quark_volatile = 0; g_dbus_error_register_error_domain ("accounts_error", &quark_volatile, accounts_error_entries, G_N_ELEMENTS (accounts_error_entries)); return (GQuark) quark_volatile; @@ -656,107 +655,103 @@ setup_monitor (Daemon *daemon, FileChangeCallback *callback) { GError *error = NULL; GFile *file; GFileMonitor *monitor; file = g_file_new_for_path (path); monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &error); if (monitor != NULL) { g_signal_connect (monitor, "changed", G_CALLBACK (callback), daemon); } else { g_warning ("Unable to monitor %s: %s", path, error->message); g_error_free (error); } g_object_unref (file); return monitor; } static void daemon_init (Daemon *daemon) { daemon->priv = DAEMON_GET_PRIVATE (daemon); - daemon->priv->extension_ifaces = daemon_read_extension_ifaces (); - daemon->priv->users = create_users_hash_table (); daemon->priv->passwd_monitor = setup_monitor (daemon, PATH_PASSWD, on_users_monitor_changed); daemon->priv->shadow_monitor = setup_monitor (daemon, PATH_SHADOW, on_users_monitor_changed); daemon->priv->group_monitor = setup_monitor (daemon, PATH_GROUP, on_users_monitor_changed); #ifdef HAVE_UTMPX_H daemon->priv->wtmp_monitor = setup_monitor (daemon, PATH_WTMP, on_users_monitor_changed); #endif daemon->priv->gdm_monitor = setup_monitor (daemon, PATH_GDM_CUSTOM, on_gdm_monitor_changed); queue_reload_users (daemon); queue_reload_autologin (daemon); } static void daemon_finalize (GObject *object) { Daemon *daemon; g_return_if_fail (IS_DAEMON (object)); daemon = DAEMON (object); if (daemon->priv->bus_proxy != NULL) g_object_unref (daemon->priv->bus_proxy); if (daemon->priv->bus_connection != NULL) g_object_unref (daemon->priv->bus_connection); g_hash_table_destroy (daemon->priv->users); - g_hash_table_unref (daemon->priv->extension_ifaces); - G_OBJECT_CLASS (daemon_parent_class)->finalize (object); } static gboolean register_accounts_daemon (Daemon *daemon) { GError *error = NULL; daemon->priv->authority = polkit_authority_get_sync (NULL, &error); if (daemon->priv->authority == NULL) { if (error != NULL) { g_critical ("error getting polkit authority: %s", error->message); g_error_free (error); } goto error; } daemon->priv->bus_connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); if (daemon->priv->bus_connection == NULL) { if (error != NULL) { g_critical ("error getting system bus: %s", error->message); g_error_free (error); } goto error; } if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (daemon), daemon->priv->bus_connection, "/org/freedesktop/Accounts", @@ -1526,66 +1521,60 @@ daemon_local_set_automatic_login (Daemon *daemon, { if (daemon->priv->autologin == user && enabled) { return TRUE; } if (daemon->priv->autologin != user && !enabled) { return TRUE; } if (!save_autologin (daemon, user_get_user_name (user), enabled, error)) { return FALSE; } if (daemon->priv->autologin != NULL) { g_object_set (daemon->priv->autologin, "automatic-login", FALSE, NULL); g_signal_emit_by_name (daemon->priv->autologin, "changed", 0); g_object_unref (daemon->priv->autologin); daemon->priv->autologin = NULL; } if (enabled) { g_object_set (user, "automatic-login", TRUE, NULL); g_signal_emit_by_name (user, "changed", 0); g_object_ref (user); daemon->priv->autologin = user; } return TRUE; } -GHashTable * -daemon_get_extension_ifaces (Daemon *daemon) -{ - return daemon->priv->extension_ifaces; -} - static void get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { switch (prop_id) { case PROP_DAEMON_VERSION: g_value_set_string (value, VERSION); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { switch (prop_id) { case PROP_DAEMON_VERSION: g_assert_not_reached (); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); diff --git a/src/daemon.h b/src/daemon.h index b7e072e..e036407 100644 --- a/src/daemon.h +++ b/src/daemon.h @@ -69,36 +69,33 @@ GQuark error_quark (void); GType daemon_get_type (void) G_GNUC_CONST; Daemon *daemon_new (void); /* local methods */ User *daemon_local_find_user_by_id (Daemon *daemon, uid_t uid); User *daemon_local_find_user_by_name (Daemon *daemon, const gchar *name); User *daemon_local_get_automatic_login_user (Daemon *daemon); typedef void (*AuthorizedCallback) (Daemon *daemon, User *user, GDBusMethodInvocation *context, gpointer data); void daemon_local_check_auth (Daemon *daemon, User *user, const gchar *action_id, gboolean allow_interaction, AuthorizedCallback auth_cb, GDBusMethodInvocation *context, gpointer data, GDestroyNotify destroy_notify); gboolean daemon_local_set_automatic_login (Daemon *daemon, User *user, gboolean enabled, GError **error); -GHashTable * daemon_read_extension_ifaces (void); -GHashTable * daemon_get_extension_ifaces (Daemon *daemon); - G_END_DECLS #endif /* __DAEMON_H__ */ diff --git a/src/user.c b/src/user.c index 1698eeb..163d136 100644 --- a/src/user.c +++ b/src/user.c @@ -77,63 +77,60 @@ struct User { GDBusConnection *system_bus_connection; gchar *object_path; Daemon *daemon; GKeyFile *keyfile; uid_t uid; gid_t gid; gchar *user_name; gchar *real_name; AccountType account_type; PasswordMode password_mode; gchar *password_hint; gchar *home_dir; gchar *shell; gchar *email; gchar *language; gchar *x_session; gchar *location; guint64 login_frequency; gint64 login_time; GVariant *login_history; gchar *icon_file; gchar *default_icon_file; gboolean locked; gboolean automatic_login; gboolean system_account; gboolean local_account; - - guint *extension_ids; - guint n_extension_ids; }; typedef struct UserClass { AccountsUserSkeletonClass parent_class; } UserClass; static void user_accounts_user_iface_init (AccountsUserIface *iface); G_DEFINE_TYPE_WITH_CODE (User, user, ACCOUNTS_TYPE_USER_SKELETON, G_IMPLEMENT_INTERFACE (ACCOUNTS_TYPE_USER, user_accounts_user_iface_init)); static gint account_type_from_pwent (struct passwd *pwent) { struct group *grp; gid_t wheel; gid_t *groups; gint ngroups; gint i; if (pwent->pw_uid == 0) { g_debug ("user is root so account type is administrator"); return ACCOUNT_TYPE_ADMINISTRATOR; } grp = getgrnam (ADMIN_GROUP); if (grp == NULL) { g_debug (ADMIN_GROUP " group not found"); return ACCOUNT_TYPE_STANDARD; } @@ -436,379 +433,109 @@ save_extra_data (User *user) user->user_name, NULL); g_file_set_contents (filename, data, -1, &error); g_free (filename); } if (error) { g_warning ("Saving data for user %s failed: %s", user->user_name, error->message); g_error_free (error); } } static void move_extra_data (const gchar *old_name, const gchar *new_name) { gchar *old_filename; gchar *new_filename; old_filename = g_build_filename (USERDIR, old_name, NULL); new_filename = g_build_filename (USERDIR, new_name, NULL); g_rename (old_filename, new_filename); g_free (old_filename); g_free (new_filename); } -static GVariant * -user_extension_get_value (User *user, - GDBusInterfaceInfo *interface, - const GDBusPropertyInfo *property) -{ - const GVariantType *type = G_VARIANT_TYPE (property->signature); - GVariant *value; - gchar *printed; - gint i; - - /* First, try to get the value from the keyfile */ - printed = g_key_file_get_value (user->keyfile, interface->name, property->name, NULL); - if (printed) { - value = g_variant_parse (type, printed, NULL, NULL, NULL); - g_free (printed); - - if (value != NULL) - return value; - } - - /* If that didn't work, try for a default value annotation */ - for (i = 0; property->annotations && property->annotations[i]; i++) { - GDBusAnnotationInfo *annotation = property->annotations[i]; - - if (g_str_equal (annotation->key, "org.freedesktop.Accounts.DefaultValue.String")) { - if (g_str_equal (property->signature, "s")) - return g_variant_ref_sink (g_variant_new_string (annotation->value)); - } - else if (g_str_equal (annotation->key, "org.freedesktop.Accounts.DefaultValue")) { - value = g_variant_parse (type, annotation->value, NULL, NULL, NULL); - if (value != NULL) - return value; - } - } - - /* Nothing found... */ - return NULL; -} - -static void -user_extension_get_property (User *user, - Daemon *daemon, - GDBusInterfaceInfo *interface, - GDBusMethodInvocation *invocation) -{ - const GDBusPropertyInfo *property = g_dbus_method_invocation_get_property_info (invocation); - GVariant *value; - - value = user_extension_get_value (user, interface, property); - - if (value) { - g_dbus_method_invocation_return_value (invocation, g_variant_new ("(v)", value)); - g_variant_unref (value); - } - else { - g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, - "Key '%s' is not set and has no default value", - property->name); - } -} - -static void -user_extension_get_all_properties (User *user, - Daemon *daemon, - GDBusInterfaceInfo *interface, - GDBusMethodInvocation *invocation) -{ - GVariantBuilder builder; - gint i; - - g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); - for (i = 0; interface->properties && interface->properties[i]; i++) { - GDBusPropertyInfo *property = interface->properties[i]; - GVariant *value; - - value = user_extension_get_value (user, interface, property); - - if (value) { - g_variant_builder_add (&builder, "{sv}", property->name, value); - g_variant_unref (value); - } - } - - g_dbus_method_invocation_return_value (invocation, g_variant_new ("(a{sv})", &builder)); -} - -static void -user_extension_set_property (User *user, - Daemon *daemon, - GDBusInterfaceInfo *interface, - GDBusMethodInvocation *invocation) -{ - const GDBusPropertyInfo *property = g_dbus_method_invocation_get_property_info (invocation); - GVariant *value; - gchar *printed; - gchar *prev; - - g_variant_get_child (g_dbus_method_invocation_get_parameters (invocation), 2, "v", &value); - - /* We'll always have the type when we parse it back so - * we don't need it to be printed with annotations. - */ - printed = g_variant_print (value, FALSE); - - /* May as well try to avoid the thrashing... */ - prev = g_key_file_get_value (user->keyfile, interface->name, property->name, NULL); - - if (!prev || !g_str_equal (printed, prev)) { - g_key_file_set_value (user->keyfile, interface->name, property->name, printed); - - /* Emit a change signal. Use invalidation - * because the data may not be world-readable. - */ - g_dbus_connection_emit_signal (g_dbus_method_invocation_get_connection (invocation), - NULL, /* destination_bus_name */ - g_dbus_method_invocation_get_object_path (invocation), - "org.freedesktop.DBus.Properties", "PropertiesChanged", - g_variant_new_parsed ("( %s, %a{sv}, [ %s ] )", - interface->name, NULL, property->name), - NULL); - - accounts_user_emit_changed (ACCOUNTS_USER (user)); - save_extra_data (user); - } - - g_variant_unref (value); - g_free (printed); - g_free (prev); - - g_dbus_method_invocation_return_value (invocation, g_variant_new ("()")); -} - -static void -user_extension_authentication_done (Daemon *daemon, - User *user, - GDBusMethodInvocation *invocation, - gpointer user_data) -{ - GDBusInterfaceInfo *interface = user_data; - const gchar *method_name; - - method_name = g_dbus_method_invocation_get_method_name (invocation); - - if (g_str_equal (method_name, "Get")) - user_extension_get_property (user, daemon, interface, invocation); - else if (g_str_equal (method_name, "GetAll")) - user_extension_get_all_properties (user, daemon, interface, invocation); - else if (g_str_equal (method_name, "Set")) - user_extension_set_property (user, daemon, interface, invocation); - else - g_assert_not_reached (); -} - -static void -user_extension_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) -{ - User *user = user_data; - GDBusInterfaceInfo *iface_info; - const gchar *annotation_name; - const gchar *action_id; - gint uid; - gint i; - - /* We don't allow method calls on extension interfaces, so we - * should only ever see property calls here. - */ - g_assert_cmpstr (interface_name, ==, "org.freedesktop.DBus.Properties"); - - /* Now get the real interface name */ - g_variant_get_child (parameters, 0, "&s", &interface_name); - - if (get_caller_uid (invocation, &uid) && (uid_t) uid == user->uid) { - /* Operation on sender's own User object */ - if (g_str_equal (method_name, "Set")) { - annotation_name = "org.freedesktop.Accounts.Authentication.ChangeOwn"; - action_id = "org.freedesktop.accounts.change-own-user-data"; - } - else { - annotation_name = "org.freedesktop.Accounts.Authentication.ReadOwn"; - action_id = ""; /* reading allowed by default */ - } - } - else { - /* Operation on someone else's User object */ - if (g_str_equal (method_name, "Set")) { - annotation_name = "org.freedesktop.Accounts.Authentication.ChangeAny"; - action_id = "org.freedesktop.accounts.user-administration"; - } - else { - annotation_name = "org.freedesktop.Accounts.Authentication.ReadAny"; - action_id = ""; /* reading allowed by default */ - } - } - - iface_info = g_hash_table_lookup (daemon_get_extension_ifaces (user->daemon), interface_name); - g_assert (iface_info != NULL); - - for (i = 0; iface_info->annotations && iface_info->annotations[i]; i++) { - if (g_str_equal (iface_info->annotations[i]->key, annotation_name)) { - action_id = iface_info->annotations[i]->value; - break; - } - } - - if (action_id[0] == '\0') { - /* Should always allow this call, so just do it now */ - user_extension_authentication_done (user->daemon, user, invocation, iface_info); - } - else { - daemon_local_check_auth (user->daemon, user, action_id, TRUE, - user_extension_authentication_done, - invocation, iface_info, NULL); - } -} - -static void -user_register_extensions (User *user) -{ - static const GDBusInterfaceVTable vtable = { - user_extension_method_call, - NULL /* get_property */, - NULL /* set_property */ - }; - GHashTable *extensions; - GHashTableIter iter; - gpointer iface; - gint i = 0; - - g_assert (user->extension_ids == NULL); - g_assert (user->n_extension_ids == 0); - - extensions = daemon_get_extension_ifaces (user->daemon); - user->n_extension_ids = g_hash_table_size (extensions); - user->extension_ids = g_new (guint, user->n_extension_ids); - g_hash_table_iter_init (&iter, extensions); - - /* Ignore errors when registering more interfaces because (a) - * they won't happen and (b) even if they do, we still want to - * publish the main user interface. - */ - while (g_hash_table_iter_next (&iter, NULL, &iface)) - user->extension_ids[i++] = g_dbus_connection_register_object (user->system_bus_connection, - user->object_path, iface, - &vtable, user, NULL, NULL); -} - static gchar * compute_object_path (User *user) { gchar *object_path; object_path = g_strdup_printf ("/org/freedesktop/Accounts/User%ld", (long) user->uid); return object_path; } void user_register (User *user) { GError *error = NULL; user->system_bus_connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); if (user->system_bus_connection == NULL) { if (error != NULL) { g_critical ("error getting system bus: %s", error->message); g_error_free (error); } return; } user->object_path = compute_object_path (user); if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (user), user->system_bus_connection, user->object_path, &error)) { if (error != NULL) { g_critical ("error exporting user object: %s", error->message); g_error_free (error); } return; } - - user_register_extensions (user); } void user_save (User *user) { save_extra_data (user); } void user_unregister (User *user) { g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (user)); - - if (user->extension_ids) { - guint i; - - for (i = 0; i < user->n_extension_ids; i++) { - /* In theory, if an error happened during registration, we could have 0 here. */ - if (user->extension_ids[i] == 0) - continue; - - g_dbus_connection_unregister_object (user->system_bus_connection, user->extension_ids[i]); - } - - g_clear_pointer (&user->extension_ids, g_free); - user->n_extension_ids = 0; - } } void user_changed (User *user) { accounts_user_emit_changed (ACCOUNTS_USER (user)); } User * user_new (Daemon *daemon, uid_t uid) { User *user; user = g_object_new (TYPE_USER, NULL); user->daemon = daemon; user->uid = uid; return user; } const gchar * user_get_user_name (User *user) { return user->user_name; } gboolean user_get_system_account (User *user) { -- 1.8.3.1