From 7c8c250d676695e42d02972d01e1ab7abeab0d4b Mon Sep 17 00:00:00 2001 From: Marius Vollmer Date: Mon, 21 Oct 2013 16:30:15 +0300 Subject: [PATCH 1/2] Daemon: Support for local groups. --- data/org.freedesktop.Accounts.Group.xml | 149 ++++++++ data/org.freedesktop.Accounts.User.xml | 50 +++ data/org.freedesktop.Accounts.xml | 134 +++++++ src/Makefile.am | 7 + src/daemon.c | 652 ++++++++++++++++++++++++++++++-- src/daemon.h | 11 + src/group.c | 487 ++++++++++++++++++++++++ src/group.h | 55 +++ src/types.h | 1 + src/user.c | 115 +++--- src/user.h | 8 +- src/util.c | 32 ++ src/util.h | 3 + 13 files changed, 1621 insertions(+), 83 deletions(-) create mode 100644 data/org.freedesktop.Accounts.Group.xml create mode 100644 src/group.c create mode 100644 src/group.h diff --git a/data/org.freedesktop.Accounts.Group.xml b/data/org.freedesktop.Accounts.Group.xml new file mode 100644 index 0000000..50b871f --- /dev/null +++ b/data/org.freedesktop.Accounts.Group.xml @@ -0,0 +1,149 @@ + + + + + + + + + + The new groupname. + + + + + + + Sets the groups groupname. Note that it is usually not allowed + to have multiple groups with the same groupname. + + + + The caller needs the following PolicyKit authorizations: + + + org.freedesktop.accounts.user-administration + + + + + if the caller lacks the appropriate PolicyKit authorization + if the operation failed + + + + + + + + + + The object path of the user to add to the group + + + + + + + Adds a user to the group as a direct member. + + + + The caller needs the following PolicyKit authorizations: + + + org.freedesktop.accounts.user-administration + + + + + if the caller lacks the appropriate PolicyKit authorization + if the operation failed + + + + + + + + + + The object path of the user to remove from the group. + + + + + + + Removes a user from the direct members of a group. + + + + The caller needs the following PolicyKit authorizations: + + + org.freedesktop.accounts.user-administration + + + + + if the caller lacks the appropriate PolicyKit authorization + if the operation failed + + + + + + + + + The gid of the group. + + + + + + + + + + The groupname of the group. + + + + + + + + + + Whether the group is a local group or not. + + + + + + + + + + The users that are direct members of this group. + + + + + + + + + + Emitted when the group has changed. + + + + + + + diff --git a/data/org.freedesktop.Accounts.User.xml b/data/org.freedesktop.Accounts.User.xml index 88198d8..6e7871f 100644 --- a/data/org.freedesktop.Accounts.User.xml +++ b/data/org.freedesktop.Accounts.User.xml @@ -533,6 +533,42 @@ + + + + + + Whether to also look for groups that this user is only an + indirect member of. + + + + + + + The groups that include this user. + + + + + + + Finds all groups that this user is a member of. This will + search all groups, not only the cached ones, and might thus + be slow. + + + A user is a direct member of a group when that group + explicitly lists the user. A user is an indirect member of + a group G if it is a member of a group that is listed by G. + + + + if the operation failed + + + + @@ -766,6 +802,20 @@ + + + + + The cached groups that the user is a member of (directly or + indirectly). This is not an exhaustive list of all groups + that the user is a member of; the user might be a member of + groups that are not currently cached. To find all such + groups, use the FindGroups method. + + + + + diff --git a/data/org.freedesktop.Accounts.xml b/data/org.freedesktop.Accounts.xml index 692540a..5ea60c6 100644 --- a/data/org.freedesktop.Accounts.xml +++ b/data/org.freedesktop.Accounts.xml @@ -214,6 +214,140 @@ + + + + Object paths of cached groups + + + + + + Lists the groups that are currently known to the system. + Groups that are defined locally are always known, but + remotely defined groups might only become known after they + have been explicitly mentioned in a call to + FindGroupByName, or similar operations. + + + + + + + + + The gid to look up + + + Object path of group + + + + + + Finds a group by gid. + + + + if no group with the given gid exists + + + + + + + + The groupname to look up + + + Object path of group + + + + + + Finds a group by its groupname. + + + + if no group with the given groupname exists + + + + + + + + The groupname for the new group + + + + Object path of the new group + + + + + Creates a new group. + + + + The caller needs the org.freedesktop.accounts.user-administration PolicyKit authorization. + + + if the caller lacks the appropriate PolicyKit authorization + if the operation failed + + + + + + + + The gid to delete + + + + + + Deletes a user account. + + + + The caller needs the org.freedesktop.accounts.user-administration PolicyKit authorization. + + + if the caller lacks the appropriate PolicyKit authorization + if the operation failed + + + + + + + Object path of the group that was added. + + + + + Emitted when a group becomes known to the local system. + + + + + + + + Object path of the group that was deleted. + + + + + Emitted when a group is no longer known to the local system. + + + + + diff --git a/src/Makefile.am b/src/Makefile.am index a053976..4a8643f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,6 +18,8 @@ libaccounts_generated_la_SOURCES = \ accounts-generated.h \ accounts-user-generated.c \ accounts-user-generated.h \ + accounts-group-generated.c \ + accounts-group-generated.h \ $(NULL) BUILT_SOURCES += $(libaccounts_generated_la_SOURCES) @@ -27,6 +29,9 @@ accounts-generated.c accounts-generated.h: $(top_srcdir)/data/org.freedesktop.Ac 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 +accounts-group-generated.c accounts-group-generated.h: $(top_srcdir)/data/org.freedesktop.Accounts.Group.xml Makefile + gdbus-codegen --generate-c-code accounts-group-generated --c-namespace Accounts --interface-prefix=org.freedesktop.Accounts. $(top_srcdir)/data/org.freedesktop.Accounts.Group.xml + libexec_PROGRAMS = accounts-daemon accounts_daemon_SOURCES = \ @@ -36,6 +41,8 @@ accounts_daemon_SOURCES = \ daemon.c \ user.h \ user.c \ + group.h \ + group.c \ util.h \ util.c \ main.c diff --git a/src/daemon.c b/src/daemon.c index d79c6f1..d44f194 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -22,7 +22,6 @@ #include "config.h" #include -#include #include #include #include @@ -96,8 +95,11 @@ struct DaemonPrivate { GDBusProxy *bus_proxy; GHashTable *users; + GHashTable *groups; GHashTable *exclusions; + GHashTable *objpath_to_users; + User *autologin; GFileMonitor *passwd_monitor; @@ -114,7 +116,8 @@ struct DaemonPrivate { PolkitAuthority *authority; }; -typedef struct passwd * (* EntryGeneratorFunc) (GHashTable *, gpointer *); +typedef struct passwd * (* UserEntryGeneratorFunc) (GHashTable *, gpointer *); +typedef struct group * (* GroupEntryGeneratorFunc) (GHashTable *, gpointer *); static void daemon_accounts_accounts_iface_init (AccountsAccountsIface *iface); @@ -128,7 +131,9 @@ static const GDBusErrorEntry accounts_error_entries[] = { 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" } + { ERROR_NOT_SUPPORTED, "org.freedesktop.Accounts.Error.NotSupported" }, + { ERROR_GROUP_EXISTS, "org.freedesktop.Accounts.Error.GroupExists" }, + { ERROR_GROUP_DOES_NOT_EXIST, "org.freedesktop.Accounts.Error.GroupDoesNotExist" } }; GQuark @@ -159,6 +164,7 @@ error_get_type (void) ENUM_ENTRY (ERROR_USER_DOES_NOT_EXIST, "UserDoesntExist"), ENUM_ENTRY (ERROR_PERMISSION_DENIED, "PermissionDenied"), ENUM_ENTRY (ERROR_NOT_SUPPORTED, "NotSupported"), + ENUM_ENTRY (ERROR_GROUP_EXISTS, "GroupExists"), { 0, 0, 0 } }; g_assert (NUM_ERRORS == G_N_ELEMENTS (values) - 1); @@ -239,6 +245,79 @@ daemon_local_user_is_excluded (Daemon *daemon, return ret; } +static gboolean +daemon_local_group_is_excluded (Daemon *daemon, struct group *grent) +{ + // We ignore groups that are the primary group of a same-named + // user. + + struct passwd *pwent = getpwnam (grent->gr_name); + if (pwent && pwent->pw_gid == grent->gr_gid) + return TRUE; + + return FALSE; +} + +static void +register_user (Daemon *daemon, + User *user) +{ + GError *error = NULL; + + if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (user), + daemon->priv->bus_connection, + user_get_object_path (user), + &error)) { + if (error != NULL) { + g_critical ("error exporting user object: %s", error->message); + g_error_free (error); + } + return; + } + + g_hash_table_insert (daemon->priv->objpath_to_users, (gchar *)user_get_object_path (user), user); +} + +static void +unregister_user (Daemon *daemon, + User *user) +{ + g_hash_table_remove (daemon->priv->objpath_to_users, user_get_object_path (user)); + g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (user)); +} + +User * +daemon_local_get_user (Daemon *daemon, + const gchar *object_path) +{ + return g_hash_table_lookup (daemon->priv->objpath_to_users, object_path); +} + +static void +register_group (Daemon *daemon, + Group *group) +{ + GError *error = NULL; + + if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (group), + daemon->priv->bus_connection, + group_get_object_path (group), + &error)) { + if (error != NULL) { + g_critical ("error exporting group object: %s", error->message); + g_error_free (error); + } + return; + } +} + +static void +unregister_group (Daemon *daemon, + Group *group) +{ + g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (group)); +} + #ifdef HAVE_UTMPX_H typedef struct { @@ -501,10 +580,39 @@ entry_generator_cachedir (GHashTable *users, return NULL; } +static struct group * +entry_generator_fgetgrent (GHashTable *users, + gpointer *state) +{ + struct group *grent; + FILE *fp; + + /* First iteration */ + if (*state == NULL) { + *state = fp = fopen (PATH_GROUP, "r"); + if (fp == NULL) { + g_warning ("Unable to open %s: %s", PATH_GROUP, g_strerror (errno)); + return NULL; + } + } + + /* Every iteration */ + fp = *state; + grent = fgetgrent (fp); + if (grent != NULL) { + return grent; + } + + /* Last iteration */ + fclose (fp); + *state = NULL; + return NULL; +} + static void -load_entries (Daemon *daemon, - GHashTable *users, - EntryGeneratorFunc entry_generator) +load_user_entries (Daemon *daemon, + GHashTable *users, + UserEntryGeneratorFunc entry_generator) { gpointer generator_state = NULL; struct passwd *pwent; @@ -547,6 +655,53 @@ load_entries (Daemon *daemon, g_assert (generator_state == NULL); } +static void +load_group_entries (Daemon *daemon, + GHashTable *users, + GHashTable *groups, + GroupEntryGeneratorFunc entry_generator) +{ + gpointer generator_state = NULL; + struct group *grent; + Group *group = NULL; + + g_assert (entry_generator != NULL); + + for (;;) { + grent = entry_generator (groups, &generator_state); + if (grent == NULL) + break; + + /* Skip excluded groups... */ + if (daemon_local_group_is_excluded (daemon, grent)) { + g_debug ("skipping group: %s", grent->gr_name); + continue; + } + + /* ignore duplicate entries */ + if (g_hash_table_lookup (groups, grent->gr_name)) { + continue; + } + + group = g_hash_table_lookup (daemon->priv->groups, grent->gr_name); + if (group == NULL) { + group = group_new (daemon, grent->gr_gid); + } else { + g_object_ref (group); + } + + /* freeze & update group */ + g_object_freeze_notify (G_OBJECT (group)); + group_update_from_grent (group, grent, users); + + g_hash_table_insert (groups, g_strdup (group_get_group_name (group)), group); + g_debug ("loaded group: %s", group_get_group_name (group)); + } + + /* Generator should have cleaned up */ + g_assert (generator_state == NULL); +} + static GHashTable * create_users_hash_table (void) { @@ -556,18 +711,67 @@ create_users_hash_table (void) g_object_unref); } +static GHashTable * +create_groups_hash_table (void) +{ + return g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_object_unref); +} + static void -reload_users (Daemon *daemon) +update_user_cached_groups (GHashTable *users, GHashTable *groups) +{ + GHashTable *objpath_to_user; + GHashTableIter iter; + gpointer name; + User *user; + Group *group; + + objpath_to_user = g_hash_table_new (g_str_hash, g_str_equal); + + g_hash_table_iter_init (&iter, users); + while (g_hash_table_iter_next (&iter, &name, (gpointer *)&user)) { + g_hash_table_insert (objpath_to_user, (gchar *)user_get_object_path (user), user); + user_reset_cached_groups (user); + } + + g_hash_table_iter_init (&iter, groups); + while (g_hash_table_iter_next (&iter, &name, (gpointer *)&group)) { + GStrv members = group_get_users (group); + int i; + for (i = 0; members[i]; i++) { + User *user = g_hash_table_lookup (objpath_to_user, members[i]); + if (user) + user_add_cached_group (user, group); + } + } + + g_hash_table_iter_init (&iter, users); + while (g_hash_table_iter_next (&iter, &name, (gpointer *)&user)) { + user_finish_cached_groups (user); + } + + g_hash_table_destroy (objpath_to_user); +} + +static void +reload_users_and_groups (Daemon *daemon) { GHashTable *users; GHashTable *old_users; + GHashTable *groups; + GHashTable *old_groups; GHashTable *local; GHashTableIter iter; gpointer name; User *user; + Group *group; - /* Track the users that we saw during our (re)load */ + /* Track the users and groups that we saw during our (re)load */ users = create_users_hash_table (); + groups = create_groups_hash_table (); /* * NOTE: As we load data from all the sources, notifies are @@ -576,7 +780,7 @@ reload_users (Daemon *daemon) */ /* Load the local users into our hash table */ - load_entries (daemon, users, entry_generator_fgetpwent); + load_user_entries (daemon, users, entry_generator_fgetpwent); local = g_hash_table_new (g_str_hash, g_str_equal); g_hash_table_iter_init (&iter, users); while (g_hash_table_iter_next (&iter, &name, NULL)) @@ -584,9 +788,9 @@ reload_users (Daemon *daemon) /* Now add/update users from other sources, possibly non-local */ #ifdef HAVE_UTMPX_H - load_entries (daemon, users, entry_generator_wtmp); + load_user_entries (daemon, users, entry_generator_wtmp); #endif - load_entries (daemon, users, entry_generator_cachedir); + load_user_entries (daemon, users, entry_generator_cachedir); /* Mark which users are local, which are not */ g_hash_table_iter_init (&iter, users); @@ -595,6 +799,12 @@ reload_users (Daemon *daemon) g_hash_table_destroy (local); + /* Now the groups. + */ + load_group_entries (daemon, users, groups, entry_generator_fgetgrent); + + update_user_cached_groups (users, groups); + /* Swap out the users */ old_users = daemon->priv->users; daemon->priv->users = users; @@ -603,7 +813,7 @@ reload_users (Daemon *daemon) g_hash_table_iter_init (&iter, old_users); while (g_hash_table_iter_next (&iter, &name, (gpointer *)&user)) { if (!g_hash_table_lookup (users, name)) { - user_unregister (user); + unregister_user (daemon, user); accounts_accounts_emit_user_deleted (ACCOUNTS_ACCOUNTS (daemon), user_get_object_path (user)); } @@ -613,7 +823,7 @@ reload_users (Daemon *daemon) g_hash_table_iter_init (&iter, users); while (g_hash_table_iter_next (&iter, &name, (gpointer *)&user)) { if (!g_hash_table_lookup (old_users, name)) { - user_register (user); + register_user (daemon, user); accounts_accounts_emit_user_added (ACCOUNTS_ACCOUNTS (daemon), user_get_object_path (user)); } @@ -621,12 +831,40 @@ reload_users (Daemon *daemon) } g_hash_table_destroy (old_users); + + /* Swap out the groups. + */ + old_groups = daemon->priv->groups; + daemon->priv->groups = groups; + + /* Remove all the old groups */ + g_hash_table_iter_init (&iter, old_groups); + while (g_hash_table_iter_next (&iter, &name, (gpointer *)&group)) { + if (!g_hash_table_lookup (groups, name)) { + unregister_group (daemon, group); + accounts_accounts_emit_group_deleted (ACCOUNTS_ACCOUNTS (daemon), + group_get_object_path (group)); + } + } + + /* Register all the new groups */ + g_hash_table_iter_init (&iter, groups); + while (g_hash_table_iter_next (&iter, &name, (gpointer *)&group)) { + if (!g_hash_table_lookup (old_groups, name)) { + register_group (daemon, group); + accounts_accounts_emit_group_added (ACCOUNTS_ACCOUNTS (daemon), + group_get_object_path (group)); + } + g_object_thaw_notify (G_OBJECT (group)); + } + + g_hash_table_destroy (old_groups); } static gboolean -reload_users_timeout (Daemon *daemon) +reload_users_and_groups_timeout (Daemon *daemon) { - reload_users (daemon); + reload_users_and_groups (daemon); daemon->priv->reload_id = 0; return FALSE; @@ -683,7 +921,7 @@ reload_autologin_timeout (Daemon *daemon) } static void -queue_reload_users_soon (Daemon *daemon) +queue_reload_users_and_groups_soon (Daemon *daemon) { if (daemon->priv->reload_id > 0) { return; @@ -692,17 +930,17 @@ queue_reload_users_soon (Daemon *daemon) /* we wait half a second or so in case /etc/passwd and * /etc/shadow are changed at the same time, or repeatedly. */ - daemon->priv->reload_id = g_timeout_add (500, (GSourceFunc)reload_users_timeout, daemon); + daemon->priv->reload_id = g_timeout_add (500, (GSourceFunc)reload_users_and_groups_timeout, daemon); } static void -queue_reload_users (Daemon *daemon) +queue_reload_users_and_groups (Daemon *daemon) { if (daemon->priv->reload_id > 0) { return; } - daemon->priv->reload_id = g_idle_add ((GSourceFunc)reload_users_timeout, daemon); + daemon->priv->reload_id = g_idle_add ((GSourceFunc)reload_users_and_groups_timeout, daemon); } static void @@ -716,18 +954,18 @@ queue_reload_autologin (Daemon *daemon) } static void -on_users_monitor_changed (GFileMonitor *monitor, - GFile *file, - GFile *other_file, - GFileMonitorEvent event_type, - Daemon *daemon) +on_users_and_groups_monitor_changed (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + Daemon *daemon) { if (event_type != G_FILE_MONITOR_EVENT_CHANGED && event_type != G_FILE_MONITOR_EVENT_CREATED) { return; } - queue_reload_users_soon (daemon); + queue_reload_users_and_groups_soon (daemon); } static void @@ -798,28 +1036,30 @@ daemon_init (Daemon *daemon) } daemon->priv->users = create_users_hash_table (); + daemon->priv->groups = create_groups_hash_table (); + daemon->priv->objpath_to_users = g_hash_table_new (g_str_hash, g_str_equal); daemon->priv->passwd_monitor = setup_monitor (daemon, PATH_PASSWD, - on_users_monitor_changed); + on_users_and_groups_monitor_changed); daemon->priv->shadow_monitor = setup_monitor (daemon, PATH_SHADOW, - on_users_monitor_changed); + on_users_and_groups_monitor_changed); daemon->priv->group_monitor = setup_monitor (daemon, PATH_GROUP, - on_users_monitor_changed); + on_users_and_groups_monitor_changed); #ifdef HAVE_UTMPX_H daemon->priv->wtmp_monitor = setup_monitor (daemon, PATH_WTMP, - on_users_monitor_changed); + on_users_and_groups_monitor_changed); #endif daemon->priv->gdm_monitor = setup_monitor (daemon, PATH_GDM_CUSTOM, on_gdm_monitor_changed); - queue_reload_users (daemon); + queue_reload_users_and_groups (daemon); queue_reload_autologin (daemon); } @@ -839,6 +1079,8 @@ daemon_finalize (GObject *object) g_object_unref (daemon->priv->bus_connection); g_hash_table_destroy (daemon->priv->users); + g_hash_table_destroy (daemon->priv->groups); + g_hash_table_destroy (daemon->priv->objpath_to_users); G_OBJECT_CLASS (daemon_parent_class)->finalize (object); } @@ -928,7 +1170,7 @@ add_new_user_for_pwent (Daemon *daemon, user = user_new (daemon, pwent->pw_uid); user_update_from_pwent (user, pwent); - user_register (user); + register_user (daemon, user); g_hash_table_insert (daemon->priv->users, g_strdup (user_get_user_name (user)), @@ -952,6 +1194,11 @@ daemon_local_find_user_by_id (Daemon *daemon, return NULL; } + if (daemon_local_user_is_excluded (daemon, pwent->pw_name, pwent->pw_shell, NULL)) { + g_debug ("rejecting excluded user %d", (int)uid); + return NULL; + } + user = g_hash_table_lookup (daemon->priv->users, pwent->pw_name); if (user == NULL) @@ -973,6 +1220,11 @@ daemon_local_find_user_by_name (Daemon *daemon, return NULL; } + if (daemon_local_user_is_excluded (daemon, pwent->pw_name, pwent->pw_shell, NULL)) { + g_debug ("rejecting excluded user %s", name); + return NULL; + } + user = g_hash_table_lookup (daemon->priv->users, pwent->pw_name); if (user == NULL) @@ -981,6 +1233,76 @@ daemon_local_find_user_by_name (Daemon *daemon, return user; } +static Group * +add_new_group_for_grent (Daemon *daemon, + struct group *grent) +{ + Group *group; + + group = group_new (daemon, grent->gr_gid); + group_update_from_grent (group, grent, daemon->priv->users); + register_group (daemon, group); + + g_hash_table_insert (daemon->priv->groups, + g_strdup (group_get_group_name (group)), + group); + + update_user_cached_groups (daemon->priv->users, daemon->priv->groups); + + accounts_accounts_emit_group_added (ACCOUNTS_ACCOUNTS (daemon), group_get_object_path (group)); + + return group; +} + +static Group * +find_group_by_grent (Daemon *daemon, + struct group *grent) +{ + Group *group; + + if (daemon_local_group_is_excluded (daemon, grent)) { + g_debug ("rejecting excluded group %s/%d", grent->gr_name, grent->gr_gid); + return NULL; + } + + group = g_hash_table_lookup (daemon->priv->groups, grent->gr_name); + + if (group == NULL) + group = add_new_group_for_grent (daemon, grent); + + return group; +} + +Group * +daemon_local_find_group_by_id (Daemon *daemon, + gid_t gid) +{ + struct group *grent; + + grent = getgrgid (gid); + if (grent == NULL) { + g_debug ("unable to lookup gid %d", (int)gid); + return NULL; + } + + return find_group_by_grent (daemon, grent); +} + +Group * +daemon_local_find_group_by_name (Daemon *daemon, + const gchar *name) +{ + struct group *grent; + + grent = getgrnam (name); + if (grent == NULL) { + g_debug ("unable to lookup name %s: %s", name, g_strerror (errno)); + return NULL; + } + + return find_group_by_grent (daemon, grent); +} + User * daemon_local_get_automatic_login_user (Daemon *daemon) { @@ -1111,6 +1433,79 @@ daemon_list_cached_users (AccountsAccounts *accounts, return TRUE; } +typedef struct { + Daemon *daemon; + GDBusMethodInvocation *context; +} ListGroupData; + + +static ListGroupData * +list_group_data_new (Daemon *daemon, + GDBusMethodInvocation *context) +{ + ListGroupData *data; + + data = g_new0 (ListGroupData, 1); + + data->daemon = g_object_ref (daemon); + data->context = context; + + return data; +} + +static void +list_group_data_free (ListGroupData *data) +{ + g_object_unref (data->daemon); + g_free (data); +} + +static gboolean +finish_list_cached_groups (gpointer user_data) +{ + ListGroupData *data = user_data; + GPtrArray *object_paths; + GHashTableIter iter; + const gchar *name; + Group *group; + + object_paths = g_ptr_array_new (); + + g_hash_table_iter_init (&iter, data->daemon->priv->groups); + while (g_hash_table_iter_next (&iter, (gpointer *)&name, (gpointer *)&group)) { + g_ptr_array_add (object_paths, (gpointer) group_get_object_path (group)); + } + g_ptr_array_add (object_paths, NULL); + + accounts_accounts_complete_list_cached_groups (NULL, data->context, (const gchar * const *) object_paths->pdata); + + g_ptr_array_free (object_paths, TRUE); + + list_group_data_free (data); + + return FALSE; +} + +static gboolean +daemon_list_cached_groups (AccountsAccounts *accounts, + GDBusMethodInvocation *context) +{ + Daemon *daemon = (Daemon*)accounts; + ListGroupData *data; + + data = list_group_data_new (daemon, context); + + if (daemon->priv->reload_id > 0) { + /* reload in progress, wait a bit */ + g_idle_add (finish_list_cached_groups, data); + } + else { + finish_list_cached_groups (data); + } + + return TRUE; +} + static const gchar * daemon_get_daemon_version (AccountsAccounts *object) { @@ -1142,7 +1537,7 @@ typedef struct { } CreateUserData; static void -create_data_free (gpointer data) +create_user_data_free (gpointer data) { CreateUserData *cd = data; @@ -1230,7 +1625,7 @@ daemon_create_user (AccountsAccounts *accounts, daemon_create_user_authorized_cb, context, data, - (GDestroyNotify)create_data_free); + (GDestroyNotify)create_user_data_free); return TRUE; } @@ -1319,7 +1714,7 @@ daemon_uncache_user_authorized_cb (Daemon *daemon, accounts_accounts_complete_uncache_user (NULL, context); - queue_reload_users (daemon); + queue_reload_users_and_groups (daemon); } static gboolean @@ -1445,6 +1840,186 @@ daemon_delete_user (AccountsAccounts *accounts, return TRUE; } +/** Groups */ + +static gboolean +daemon_find_group_by_id (AccountsAccounts *accounts, + GDBusMethodInvocation *context, + gint64 gid) +{ + Daemon *daemon = (Daemon*)accounts; + Group *group; + + group = daemon_local_find_group_by_id (daemon, gid); + + if (group) { + accounts_accounts_complete_find_group_by_id (NULL, context, group_get_object_path (group)); + } + else { + throw_error (context, ERROR_FAILED, "Failed to look up group with gid %d.", (int)gid); + } + + return TRUE; +} + +static gboolean +daemon_find_group_by_name (AccountsAccounts *accounts, + GDBusMethodInvocation *context, + const gchar *name) +{ + Daemon *daemon = (Daemon*)accounts; + Group *group; + + group = daemon_local_find_group_by_name (daemon, name); + + if (group) { + accounts_accounts_complete_find_group_by_name (NULL, context, group_get_object_path (group)); + } + else { + throw_error (context, ERROR_FAILED, "Failed to look up group with name %s.", name); + } + + return TRUE; +} + +typedef struct { + gchar *group_name; +} CreateGroupData; + +static void +create_group_data_free (gpointer data) +{ + CreateGroupData *cd = data; + + g_free (cd->group_name); + g_free (cd); +} + +static void +daemon_create_group_authorized_cb (Daemon *daemon, + User *dummy, + GDBusMethodInvocation *context, + gpointer data) + +{ + CreateGroupData *cd = data; + Group *group; + GError *error; + const gchar *argv[4]; + + if (getgrnam (cd->group_name) != NULL) { + throw_error (context, ERROR_GROUP_EXISTS, "A group with name '%s' already exists", cd->group_name); + return; + } + + sys_log (context, "create group '%s'", cd->group_name); + + argv[0] = "/usr/sbin/groupadd"; + argv[1] = "--"; + argv[2] = cd->group_name; + argv[3] = NULL; + + error = NULL; + if (!spawn_with_login_uid (context, argv, &error)) { + throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message); + g_error_free (error); + return; + } + + group = daemon_local_find_group_by_name (daemon, cd->group_name); + + accounts_accounts_complete_create_group (NULL, context, group_get_object_path (group)); +} + +static gboolean +daemon_create_group (AccountsAccounts *accounts, + GDBusMethodInvocation *context, + const gchar *group_name) +{ + Daemon *daemon = (Daemon*)accounts; + CreateGroupData *data; + + data = g_new0 (CreateGroupData, 1); + data->group_name = g_strdup (group_name); + + daemon_local_check_auth (daemon, + NULL, + "org.freedesktop.accounts.user-administration", + TRUE, + daemon_create_group_authorized_cb, + context, + data, + (GDestroyNotify)create_group_data_free); + + return TRUE; +} + +typedef struct { + gint64 gid; +} DeleteGroupData; + +static void +daemon_delete_group_authorized_cb (Daemon *daemon, + User *dummy, + GDBusMethodInvocation *context, + gpointer data) + +{ + DeleteGroupData *gd = data; + GError *error; + struct group *grent; + const gchar *argv[4]; + + grent = getgrgid (gd->gid); + + if (grent == NULL) { + throw_error (context, ERROR_GROUP_DOES_NOT_EXIST, "No group with gid %d found", gd->gid); + return; + } + + sys_log (context, "delete group '%s' (%d)", grent->gr_name, gd->gid); + + argv[0] = "/usr/sbin/groupdel"; + argv[1] = "--"; + argv[2] = grent->gr_name; + argv[3] = NULL; + + error = NULL; + if (!spawn_with_login_uid (context, argv, &error)) { + throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message); + g_error_free (error); + return; + } + + accounts_accounts_complete_delete_group (NULL, context); +} + + +static gboolean +daemon_delete_group (AccountsAccounts *accounts, + GDBusMethodInvocation *context, + gint64 gid) +{ + Daemon *daemon = (Daemon*)accounts; + DeleteGroupData *data; + + data = g_new0 (DeleteGroupData, 1); + data->gid = gid; + + daemon_local_check_auth (daemon, + NULL, + "org.freedesktop.accounts.user-administration", + TRUE, + daemon_delete_group_authorized_cb, + context, + data, + (GDestroyNotify)g_free); + + return TRUE; +} + +/** Auth helpers */ + typedef struct { Daemon *daemon; User *user; @@ -1721,7 +2296,14 @@ daemon_accounts_accounts_iface_init (AccountsAccountsIface *iface) iface->handle_find_user_by_id = daemon_find_user_by_id; iface->handle_find_user_by_name = daemon_find_user_by_name; iface->handle_list_cached_users = daemon_list_cached_users; - iface->get_daemon_version = daemon_get_daemon_version; iface->handle_cache_user = daemon_cache_user; iface->handle_uncache_user = daemon_uncache_user; + + iface->handle_list_cached_groups = daemon_list_cached_groups; + iface->handle_create_group = daemon_create_group; + iface->handle_delete_group = daemon_delete_group; + iface->handle_find_group_by_id = daemon_find_group_by_id; + iface->handle_find_group_by_name = daemon_find_group_by_name; + + iface->get_daemon_version = daemon_get_daemon_version; } diff --git a/src/daemon.h b/src/daemon.h index 7d89ef5..0d6dfbc 100644 --- a/src/daemon.h +++ b/src/daemon.h @@ -28,6 +28,7 @@ #include "types.h" #include "user.h" +#include "group.h" #include "accounts-generated.h" G_BEGIN_DECLS @@ -57,6 +58,8 @@ typedef enum { ERROR_USER_DOES_NOT_EXIST, ERROR_PERMISSION_DENIED, ERROR_NOT_SUPPORTED, + ERROR_GROUP_EXISTS, + ERROR_GROUP_DOES_NOT_EXIST, NUM_ERRORS } Error; @@ -75,7 +78,13 @@ User *daemon_local_find_user_by_id (Daemon *daemon, uid_t uid); User *daemon_local_find_user_by_name (Daemon *daemon, const gchar *name); +Group *daemon_local_find_group_by_id (Daemon *daemon, + gid_t gid); +Group *daemon_local_find_group_by_name (Daemon *daemon, + const gchar *name); + User *daemon_local_get_automatic_login_user (Daemon *daemon); + gboolean daemon_local_user_is_excluded (Daemon *daemon, const gchar *name, const gchar *shell, @@ -100,6 +109,8 @@ gboolean daemon_local_set_automatic_login (Daemon *daemon, gboolean enabled, GError **error); +User *daemon_local_get_user (Daemon *daemon, const gchar *object_path); + G_END_DECLS #endif /* __DAEMON_H__ */ diff --git a/src/group.c b/src/group.c new file mode 100644 index 0000000..18aaeaf --- /dev/null +++ b/src/group.c @@ -0,0 +1,487 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2004-2005 James M. Cape . + * Copyright (C) 2007-2008 William Jon McCann + * Copyright (C) 2009-2010 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define _BSD_SOURCE + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SHADOW_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "daemon.h" +#include "group.h" +#include "accounts-group-generated.h" +#include "util.h" + +enum { + // XXX - is this dependent on the order of the property + // definitions in data/foo.xml? That would be insane, + // no? + + PROP_0, + PROP_GID, + PROP_GROUP_NAME, + PROP_LOCAL_GROUP, + PROP_USERS, +}; + +struct Group { + AccountsGroupSkeleton parent; + + gchar *object_path; + + Daemon *daemon; + + gid_t gid; + gchar *group_name; + gboolean local_group; + GStrv users; +}; + +typedef struct GroupClass +{ + AccountsGroupSkeletonClass parent_class; +} GroupClass; + +static void group_accounts_group_iface_init (AccountsGroupIface *iface); + +G_DEFINE_TYPE_WITH_CODE (Group, group, ACCOUNTS_TYPE_GROUP_SKELETON, G_IMPLEMENT_INTERFACE (ACCOUNTS_TYPE_GROUP, group_accounts_group_iface_init)); + +static GStrv +find_user_paths (Group *group, GHashTable *users, gchar **members) +{ + int i, j, n; + GStrv paths; + + n = 0; + for (i = 0; members[i]; i++) { + if (g_hash_table_lookup (users, members[i])) + n++; + } + + paths = g_new0 (gchar *, n+1); + for (i = 0, j = 0; members[i] && j < n; i++) { + User *user = g_hash_table_lookup (users, members[i]); + if (user) + paths[j++] = g_strdup (user_get_object_path (user)); + } + + strv_sort (paths); + + return paths; +} + +void +group_update_from_grent (Group *group, + struct group *grent, + GHashTable *users) +{ + gboolean changed = FALSE; + GStrv new_members; + + g_object_freeze_notify (G_OBJECT (group)); + + if (grent->gr_gid != group->gid) { + group->gid = grent->gr_gid; + changed = TRUE; + g_object_notify (G_OBJECT (group), "gid"); + } + + if (g_strcmp0 (group->group_name, grent->gr_name) != 0) { + g_free (group->group_name); + group->group_name = g_strdup (grent->gr_name); + changed = TRUE; + g_object_notify (G_OBJECT (group), "group-name"); + } + + new_members = find_user_paths (group, users, grent->gr_mem); + if (!strv_equal (group->users, new_members)) { + g_strfreev (group->users); + group->users = new_members; + changed = TRUE; + g_object_notify (G_OBJECT (group), "users"); + } else + g_strfreev (new_members); + + g_object_thaw_notify (G_OBJECT (group)); + + if (changed) + accounts_group_emit_changed (ACCOUNTS_GROUP (group)); +} + +void +group_changed (Group *group) +{ + accounts_group_emit_changed (ACCOUNTS_GROUP (group)); +} + +static gchar * +compute_object_path (Group *group) +{ + gchar *object_path; + + object_path = g_strdup_printf ("/org/freedesktop/Accounts/Group%ld", + (long) group->gid); + return object_path; +} + +Group * +group_new (Daemon *daemon, + gid_t gid) +{ + Group *group; + + group = g_object_new (TYPE_GROUP, NULL); + group->daemon = daemon; + group->gid = gid; + group->local_group = TRUE; + + group->object_path = compute_object_path (group); + + return group; +} + +const gchar * +group_get_object_path (Group *group) +{ + return group->object_path; +} + +gid_t +group_get_gid (Group *group) +{ + return group->gid; +} + +const gchar * +group_get_group_name (Group *group) +{ + return group->group_name; +} + +gboolean +group_get_local_group (Group *group) +{ + return group->local_group; +} + +GStrv +group_get_users (Group *group) +{ + return group->users; +} + +static void +throw_error (GDBusMethodInvocation *context, + gint error_code, + const gchar *format, + ...) +{ + va_list args; + gchar *message; + + va_start (args, format); + message = g_strdup_vprintf (format, args); + va_end (args); + + g_dbus_method_invocation_return_error (context, ERROR, error_code, "%s", message); + + g_free (message); +} + +static void +group_finalize (GObject *object) +{ + Group *group; + + group = GROUP (object); + + g_free (group->object_path); + g_free (group->group_name); + + if (G_OBJECT_CLASS (group_parent_class)->finalize) + (*G_OBJECT_CLASS (group_parent_class)->finalize) (object); +} + +static void +group_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + Group *group = GROUP (object); + + switch (param_id) { + case PROP_GID: + group->gid = g_value_get_uint64 (value); + break; + case PROP_GROUP_NAME: + group->group_name = g_value_dup_string (value); + break; + case PROP_LOCAL_GROUP: + group->local_group = g_value_get_boolean (value); + break; + case PROP_USERS: + group->users = g_value_dup_boxed (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +group_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + Group *group = GROUP (object); + + switch (param_id) { + case PROP_GID: + g_value_set_uint64 (value, group_get_gid (group)); + break; + case PROP_GROUP_NAME: + g_value_set_string (value, group_get_group_name (group)); + break; + case PROP_LOCAL_GROUP: + g_value_set_boolean (value, group_get_local_group (group)); + break; + case PROP_USERS: + g_value_set_boxed (value, group_get_users (group)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +group_class_init (GroupClass *class) +{ + GObjectClass *gobject_class; + + gobject_class = G_OBJECT_CLASS (class); + + gobject_class->get_property = group_get_property; + gobject_class->set_property = group_set_property; + gobject_class->finalize = group_finalize; + + accounts_group_override_properties (gobject_class, 1); +} + +typedef struct { + Group *group; + gchar *new_group_name; +} SetGroupNameData; + +static void +free_set_group_data (SetGroupNameData *data) +{ + g_object_unref (data->group); + g_free (data->new_group_name); + g_free (data); +} + +static void +group_set_group_name_authorized_cb (Daemon *daemon, + User *dummy, + GDBusMethodInvocation *context, + gpointer user_data) + +{ + SetGroupNameData *data = user_data; + GError *error; + const gchar *argv[6]; + + sys_log (context, "changing name of group '%s' to '%s'", + group_get_group_name (data->group), + data->new_group_name); + + argv[0] = "/usr/sbin/groupmod"; + argv[1] = "-n"; + argv[2] = data->new_group_name; + argv[3] = "--"; + argv[4] = group_get_group_name (data->group); + argv[5] = NULL; + + error = NULL; + if (!spawn_with_login_uid (context, argv, &error)) { + throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message); + g_error_free (error); + return; + } + + accounts_group_complete_set_group_name (NULL, context); +} + +static gboolean +group_set_group_name (AccountsGroup *group, + GDBusMethodInvocation *context, + const gchar *new_group_name) +{ + SetGroupNameData *data; + + data = g_new0 (SetGroupNameData, 1); + data->group = g_object_ref (GROUP (group)); + data->new_group_name = g_strdup (new_group_name); + + daemon_local_check_auth (data->group->daemon, + NULL, + "org.freedesktop.accounts.user-administration", + TRUE, + group_set_group_name_authorized_cb, + context, + data, + (GDestroyNotify)free_set_group_data); + + return TRUE; + +} + +typedef struct { + Group *group; + User *user; + gboolean add; +} AddRemoveUserData; + +static void +free_add_remove_user_data (AddRemoveUserData *data) +{ + g_object_unref (data->group); + g_object_unref (data->user); + g_free (data); +} + +static void +group_add_remove_user_authorized_cb (Daemon *daemon, + User *dummy, + GDBusMethodInvocation *context, + gpointer user_data) + +{ + AddRemoveUserData *data = user_data; + GError *error; + const gchar *argv[6]; + + sys_log (context, "%s user '%s' %s group '%s'", + data->add? "add" : "remove", + user_get_user_name (data->user), + data->add? "to" : "from", + group_get_group_name (data->group)); + + argv[0] = "/usr/sbin/groupmems"; + argv[1] = "-g"; + argv[2] = group_get_group_name (data->group); + argv[3] = data->add? "-a" : "-d"; + argv[4] = user_get_user_name (data->user); + argv[5] = NULL; + + error = NULL; + if (!spawn_with_login_uid (context, argv, &error)) { + throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message); + g_error_free (error); + return; + } + + if (data->add) + accounts_group_complete_add_user (NULL, context); + else + accounts_group_complete_remove_user (NULL, context); +} + +static gboolean +group_add_remove_user (AccountsGroup *group, + GDBusMethodInvocation *context, + const gchar *user_path, + gboolean add) +{ + AddRemoveUserData *data; + User *user = daemon_local_get_user (GROUP(group)->daemon, user_path); + + if (user == NULL) { + throw_error (context, ERROR_FAILED, "object '%s' does not exist", user_path); + return TRUE; + } + + data = g_new0 (AddRemoveUserData, 1); + data->group = g_object_ref (GROUP (group)); + data->user = g_object_ref (user); + data->add = add; + + daemon_local_check_auth (data->group->daemon, + NULL, + "org.freedesktop.accounts.user-administration", + TRUE, + group_add_remove_user_authorized_cb, + context, + data, + (GDestroyNotify)free_add_remove_user_data); + + return TRUE; +} + +static gboolean +group_add_user (AccountsGroup *group, + GDBusMethodInvocation *context, + const gchar *user_path) +{ + return group_add_remove_user (group, context, user_path, TRUE); +} + +static gboolean +group_remove_user (AccountsGroup *group, + GDBusMethodInvocation *context, + const gchar *user_path) +{ + return group_add_remove_user (group, context, user_path, FALSE); +} + +static void +group_accounts_group_iface_init (AccountsGroupIface *iface) +{ + iface->handle_set_group_name = group_set_group_name; + iface->handle_add_user = group_add_user; + iface->handle_remove_user = group_remove_user; +} + +static void +group_init (Group *group) +{ + group->object_path = NULL; + group->group_name = NULL; +} diff --git a/src/group.h b/src/group.h new file mode 100644 index 0000000..0d76301 --- /dev/null +++ b/src/group.h @@ -0,0 +1,55 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2009-2010 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __GROUP__ +#define __GROUP__ + +#include +#include + +#include +#include + +#include "types.h" + +G_BEGIN_DECLS + +#define TYPE_GROUP (group_get_type ()) +#define GROUP(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), TYPE_GROUP, Group)) +#define IS_GROUP(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), TYPE_GROUP)) + +GType group_get_type (void) G_GNUC_CONST; +Group * group_new (Daemon *daemon, + gid_t gid); + +void group_update_from_grent (Group *group, + struct group *grent, + GHashTable *users); + +void group_changed (Group *group); + +const gchar * group_get_object_path (Group *group); +gid_t group_get_gid (Group *group); +const gchar * group_get_group_name (Group *group); +gboolean group_get_local_group (Group *group); +GStrv group_get_users (Group *group); + +G_END_DECLS + +#endif diff --git a/src/types.h b/src/types.h index ff19943..77ee755 100644 --- a/src/types.h +++ b/src/types.h @@ -24,5 +24,6 @@ typedef struct Daemon Daemon; typedef struct User User; +typedef struct Group Group; #endif diff --git a/src/user.c b/src/user.c index d99453d..ff10f63 100644 --- a/src/user.c +++ b/src/user.c @@ -68,12 +68,12 @@ enum { PROP_AUTOMATIC_LOGIN, PROP_SYSTEM_ACCOUNT, PROP_LOCAL_ACCOUNT, + PROP_CACHED_GROUPS }; struct User { AccountsUserSkeleton parent; - GDBusConnection *system_bus_connection; gchar *object_path; Daemon *daemon; @@ -100,6 +100,9 @@ struct User { gboolean automatic_login; gboolean system_account; gboolean local_account; + GStrv cached_groups; + + GStrv cached_groups_stage; }; typedef struct UserClass @@ -389,6 +392,38 @@ user_update_system_account_property (User *user, g_object_notify (G_OBJECT (user), "system-account"); } +void +user_reset_cached_groups (User *user) +{ + user->cached_groups_stage = g_malloc_n (1, sizeof(gchar*)); + user->cached_groups_stage[0] = NULL; +} + +void +user_add_cached_group (User *user, + Group *group) +{ + int n = g_strv_length (user->cached_groups_stage); + n += 1; + user->cached_groups_stage = g_realloc_n (user->cached_groups_stage, + n+1, sizeof(gchar*)); + user->cached_groups_stage[n-1] = g_strdup (group_get_object_path (group)); + user->cached_groups_stage[n] = NULL; +} + +void +user_finish_cached_groups (User *user) +{ + strv_sort (user->cached_groups_stage); + if (!strv_equal (user->cached_groups, user->cached_groups_stage)) { + g_strfreev (user->cached_groups); + user->cached_groups = user->cached_groups_stage; + g_object_notify (G_OBJECT (user), "cached-groups"); + } else + g_strfreev (user->cached_groups_stage); + user->cached_groups_stage = NULL; +} + static void user_save_to_keyfile (User *user, GKeyFile *keyfile) @@ -460,45 +495,6 @@ move_extra_data (const gchar *old_name, g_free (new_filename); } -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; - } -} - void user_save (User *user) { @@ -506,15 +502,20 @@ user_save (User *user) } void -user_unregister (User *user) +user_changed (User *user) { - g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (user)); + accounts_user_emit_changed (ACCOUNTS_USER (user)); } -void -user_changed (User *user) +static gchar * +compute_object_path (User *user) { - accounts_user_emit_changed (ACCOUNTS_USER (user)); + gchar *object_path; + + object_path = g_strdup_printf ("/org/freedesktop/Accounts/User%ld", + (long) user->uid); + + return object_path; } User * @@ -527,6 +528,8 @@ user_new (Daemon *daemon, user->daemon = daemon; user->uid = uid; + user->object_path = compute_object_path (user); + return user; } @@ -560,6 +563,12 @@ user_get_uid (User *user) return user->uid; } +uid_t +user_get_gid (User *user) +{ + return user->gid; +} + const gchar * user_get_shell(User *user) { @@ -886,6 +895,17 @@ user_set_x_session (AccountsUser *auser, return TRUE; } +static gboolean +user_find_groups (AccountsUser *auser, + GDBusMethodInvocation *context, + gboolean indirect) +{ + User *user = (User*)auser; + + accounts_user_complete_find_groups (auser, context, (const gchar *const *)user->cached_groups); + return TRUE; +} + static void user_change_location_authorized_cb (Daemon *daemon, User *user, @@ -1960,6 +1980,9 @@ user_get_property (GObject *object, case PROP_LOCAL_ACCOUNT: g_value_set_boolean (value, user->local_account); break; + case PROP_CACHED_GROUPS: + g_value_set_boxed (value, user->cached_groups); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; @@ -1997,6 +2020,7 @@ user_accounts_user_iface_init (AccountsUserIface *iface) iface->handle_set_shell = user_set_shell; iface->handle_set_user_name = user_set_user_name; iface->handle_set_xsession = user_set_x_session; + iface->handle_find_groups = user_find_groups; iface->get_uid = user_real_get_uid; iface->get_user_name = user_real_get_user_name; iface->get_real_name = user_real_get_real_name; @@ -2021,7 +2045,6 @@ user_accounts_user_iface_init (AccountsUserIface *iface) static void user_init (User *user) { - user->system_bus_connection = NULL; user->object_path = NULL; user->user_name = NULL; user->real_name = NULL; diff --git a/src/user.h b/src/user.h index 0848b50..6d746e8 100644 --- a/src/user.h +++ b/src/user.h @@ -62,8 +62,11 @@ void user_update_local_account_property (User *user, void user_update_system_account_property (User *user, gboolean system); -void user_register (User *user); -void user_unregister (User *user); +void user_reset_cached_groups (User *user); +void user_add_cached_group (User *user, + Group *group); +void user_finish_cached_groups (User *user); + void user_changed (User *user); void user_save (User *user); @@ -73,6 +76,7 @@ gboolean user_get_system_account (User *user); gboolean user_get_local_account (User *user); const gchar * user_get_object_path (User *user); uid_t user_get_uid (User *user); +gid_t user_get_gid (User *user); const gchar * user_get_shell (User *user); G_END_DECLS diff --git a/src/util.c b/src/util.c index 84e7b78..4605623 100644 --- a/src/util.c +++ b/src/util.c @@ -21,6 +21,7 @@ #include "config.h" +#include #include #include #include @@ -313,3 +314,34 @@ get_caller_uid (GDBusMethodInvocation *context, return TRUE; } + +gboolean +strv_equal (GStrv a, GStrv b) +{ + int i; + + if (a == NULL && b == NULL) + return TRUE; + + if (a == NULL || b == NULL) + return FALSE; + + for (i = 0; a[i] && b[i]; i++) + if (strcmp (a[i], b[i]) != 0) + return FALSE; + + return a[i] == NULL && b[i] == NULL; +} + +static int +cmpstringp(const void *p1, const void *p2) +{ + return strcmp(* (char * const *) p1, * (char * const *) p2); +} + +void +strv_sort (GStrv a) +{ + int n = g_strv_length (a); + qsort (a, n, sizeof (gchar *), cmpstringp); +} diff --git a/src/util.h b/src/util.h index 41ba545..2b97089 100644 --- a/src/util.h +++ b/src/util.h @@ -40,6 +40,9 @@ gint get_user_groups (const gchar *username, gid_t group, gid_t **groups); +void strv_sort (GStrv strv); +gboolean strv_equal (GStrv a, GStrv b); + G_END_DECLS #endif /* __UTIL_H__ */ -- 1.8.3.1