diff options
Diffstat (limited to 'src/libaccountsservice/act-user-manager.c')
-rw-r--r-- | src/libaccountsservice/act-user-manager.c | 925 |
1 files changed, 909 insertions, 16 deletions
diff --git a/src/libaccountsservice/act-user-manager.c b/src/libaccountsservice/act-user-manager.c index 888070d..6438546 100644 --- a/src/libaccountsservice/act-user-manager.c +++ b/src/libaccountsservice/act-user-manager.c @@ -49,7 +49,9 @@ #endif #include "act-user-manager.h" +#include "act-user-manager-private.h" #include "act-user-private.h" +#include "act-group-private.h" #include "accounts-generated.h" #include "ck-manager-generated.h" #include "ck-seat-generated.h" @@ -86,6 +88,8 @@ * @ACT_USER_MANAGER_ERROR_USER_DOES_NOT_EXIST: The user does not exist * @ACT_USER_MANAGER_ERROR_PERMISSION_DENIED: Permission denied * @ACT_USER_MANAGER_ERROR_NOT_SUPPORTED: Operation not supported + * @ACT_USER_MANAGER_ERROR_GROUP_EXISTS: The group already exists + * @ACT_USER_MANAGER_ERROR_GROUP_DOES_NOT_EXIST: The group does not exist * * Various error codes returned by the accounts service. */ @@ -158,6 +162,8 @@ typedef enum { typedef enum { ACT_USER_MANAGER_FETCH_USER_FROM_USERNAME_REQUEST, ACT_USER_MANAGER_FETCH_USER_FROM_ID_REQUEST, + ACT_USER_MANAGER_FETCH_GROUP_FROM_GROUPNAME_REQUEST, + ACT_USER_MANAGER_FETCH_GROUP_FROM_ID_REQUEST, } ActUserManagerFetchUserRequestType; typedef struct @@ -165,10 +171,13 @@ typedef struct ActUserManager *manager; ActUserManagerGetUserState state; ActUser *user; + ActGroup *group; ActUserManagerFetchUserRequestType type; union { char *username; uid_t uid; + char *groupname; + gid_t gid; }; char *object_path; char *description; @@ -179,6 +188,8 @@ struct ActUserManagerPrivate GHashTable *normal_users_by_name; GHashTable *system_users_by_name; GHashTable *users_by_object_path; + GHashTable *groups_by_name; + GHashTable *groups_by_object_path; GHashTable *sessions; GDBusConnection *connection; AccountsAccounts *accounts_proxy; @@ -190,6 +201,8 @@ struct ActUserManagerPrivate GSList *new_users; GSList *new_users_inhibiting_load; GSList *fetch_user_requests; + GSList *new_groups; + GSList *new_groups_inhibiting_load; GSList *exclude_usernames; GSList *include_usernames; @@ -200,6 +213,7 @@ struct ActUserManagerPrivate gboolean has_multiple_users; gboolean getting_sessions; gboolean listing_cached_users; + gboolean listing_cached_groups; }; enum { @@ -215,6 +229,9 @@ enum { USER_REMOVED, USER_IS_LOGGED_IN_CHANGED, USER_CHANGED, + GROUP_ADDED, + GROUP_REMOVED, + GROUP_CHANGED, LAST_SIGNAL }; @@ -227,6 +244,7 @@ static void act_user_manager_finalize (GObject *object); static gboolean load_seat_incrementally (ActUserManager *manager); static void unload_seat (ActUserManager *manager); static void load_users (ActUserManager *manager); +static void load_groups (ActUserManager *manager); static void act_user_manager_queue_load (ActUserManager *manager); static void queue_load_seat_and_users (ActUserManager *manager); @@ -236,6 +254,9 @@ static void set_is_loaded (ActUserManager *manager, gboolean is_loaded); static void on_new_user_loaded (ActUser *user, GParamSpec *pspec, ActUserManager *manager); +static void on_new_group_loaded (ActGroup *group, + GParamSpec *pspec, + ActUserManager *manager); static void give_up (ActUserManager *manager, ActUserManagerFetchUserRequest *request); static void fetch_user_incrementally (ActUserManagerFetchUserRequest *request); @@ -243,6 +264,8 @@ static void fetch_user_incrementally (ActUserManagerFetchUserRequest * static void maybe_set_is_loaded (ActUserManager *manager); static void update_user (ActUserManager *manager, ActUser *user); +static void update_group (ActUserManager *manager, + ActGroup *group); static gpointer user_manager_object = NULL; G_DEFINE_TYPE (ActUserManager, act_user_manager, G_TYPE_OBJECT) @@ -252,7 +275,9 @@ static const GDBusErrorEntry error_entries[] = { { ACT_USER_MANAGER_ERROR_USER_EXISTS, "org.freedesktop.Accounts.Error.UserExists" }, { ACT_USER_MANAGER_ERROR_USER_DOES_NOT_EXIST, "org.freedesktop.Accounts.Error.UserDoesNotExist" }, { ACT_USER_MANAGER_ERROR_PERMISSION_DENIED, "org.freedesktop.Accounts.Error.PermissionDenied" }, - { ACT_USER_MANAGER_ERROR_NOT_SUPPORTED, "org.freedesktop.Accounts.Error.NotSupported" } + { ACT_USER_MANAGER_ERROR_NOT_SUPPORTED, "org.freedesktop.Accounts.Error.NotSupported" }, + { ACT_USER_MANAGER_ERROR_GROUP_EXISTS, "org.freedesktop.Accounts.Error.GroupExists" }, + { ACT_USER_MANAGER_ERROR_GROUP_DOES_NOT_EXIST, "org.freedesktop.Accounts.Error.GroupDoesNotExist" } }; GQuark @@ -638,6 +663,12 @@ describe_user (ActUser *user) { ActUserManagerFetchUserRequest *request; + request = g_object_get_data (G_OBJECT (user), "fetch-user-request"); + + if (request != NULL) { + return request->description; + } + if (act_user_is_loaded (user)) { static char *description = NULL; g_clear_pointer (&description, (GDestroyNotify) g_free); @@ -646,7 +677,23 @@ describe_user (ActUser *user) return description; } - request = g_object_get_data (G_OBJECT (user), "fetch-user-request"); + return "user"; +} + +static const char * +describe_group (ActGroup *group) +{ + ActUserManagerFetchUserRequest *request; + + if (act_group_is_loaded (group)) { + static char *description = NULL; + g_clear_pointer (&description, (GDestroyNotify) g_free); + + description = g_strdup_printf ("group %s", act_group_get_group_name (group)); + return description; + } + + request = g_object_get_data (G_OBJECT (group), "fetch-user-request"); if (request != NULL) { return request->description; @@ -694,6 +741,20 @@ on_user_changed (ActUser *user, } static void +on_group_changed (ActGroup *group, + ActUserManager *manager) +{ + if (manager->priv->is_loaded) { + g_debug ("ActUserManager: %s changed", + describe_group (group)); + + g_signal_emit (manager, signals[GROUP_CHANGED], 0, group); + + update_group (manager, group); + } +} + +static void queue_load_seat_incrementally (ActUserManager *manager) { if (manager->priv->seat.load_idle_id == 0) { @@ -846,12 +907,24 @@ create_new_user (ActUserManager *manager) return g_object_ref (user); } +static ActGroup * +create_new_group (ActUserManager *manager) +{ + ActGroup *group; + + group = g_object_new (ACT_TYPE_GROUP, NULL); + + manager->priv->new_groups = g_slist_prepend (manager->priv->new_groups, group); + + g_signal_connect_object (group, "notify::is-loaded", G_CALLBACK (on_new_group_loaded), manager, 0); + + return g_object_ref (group); +} + static void add_user (ActUserManager *manager, ActUser *user) { - const char *object_path; - g_debug ("ActUserManager: tracking user '%s'", act_user_get_user_name (user)); if (act_user_is_system_account (user)) { g_hash_table_insert (manager->priv->system_users_by_name, @@ -863,13 +936,6 @@ add_user (ActUserManager *manager, g_object_ref (user)); } - object_path = act_user_get_object_path (user); - if (object_path != NULL) { - g_hash_table_insert (manager->priv->users_by_object_path, - (gpointer) object_path, - g_object_ref (user)); - } - g_signal_connect_object (user, "sessions-changed", G_CALLBACK (on_user_sessions_changed), @@ -892,6 +958,29 @@ add_user (ActUserManager *manager, } static void +add_group (ActUserManager *manager, + ActGroup *group) +{ + g_debug ("ActUserManager: tracking group '%s'", act_group_get_group_name (group)); + + g_hash_table_insert (manager->priv->groups_by_name, + g_strdup (act_group_get_group_name (group)), + g_object_ref (group)); + + g_signal_connect_object (group, + "changed", + G_CALLBACK (on_group_changed), + manager, 0); + + if (manager->priv->is_loaded) { + g_debug ("ActUserManager: loaded, so emitting group-added signal"); + g_signal_emit (manager, signals[GROUP_ADDED], 0, group); + } else { + g_debug ("ActUserManager: not yet loaded, so not emitting group-added signal"); + } +} + +static void remove_user (ActUserManager *manager, ActUser *user) { @@ -927,6 +1016,34 @@ remove_user (ActUserManager *manager, } static void +remove_group (ActUserManager *manager, + ActGroup *group) +{ + g_debug ("ActUserManager: no longer tracking group '%s' (with object path %s)", + act_group_get_group_name (group), + act_group_get_object_path (group)); + + g_object_ref (group); + + g_signal_handlers_disconnect_by_func (group, on_group_changed, manager); + if (act_group_get_object_path (group) != NULL) { + g_hash_table_remove (manager->priv->groups_by_object_path, act_group_get_object_path (group)); + } + if (act_group_get_group_name (group) != NULL) { + g_hash_table_remove (manager->priv->groups_by_name, act_group_get_group_name (group)); + } + + if (manager->priv->is_loaded) { + g_debug ("ActUserManager: loaded, so emitting group-removed signal"); + g_signal_emit (manager, signals[GROUP_REMOVED], 0, group); + } else { + g_debug ("ActUserManager: not yet loaded, so not emitting group-removed signal"); + } + + g_object_unref (group); +} + +static void update_user (ActUserManager *manager, ActUser *user) { @@ -960,6 +1077,13 @@ update_user (ActUserManager *manager, } } +static void +update_group (ActUserManager *manager, + ActGroup *group) +{ + /* Nothing to do... */ +} + static ActUser * lookup_user_by_name (ActUserManager *manager, const char *username) @@ -975,6 +1099,13 @@ lookup_user_by_name (ActUserManager *manager, return user; } +static ActGroup * +lookup_group_by_name (ActUserManager *manager, + const char *groupname) +{ + return g_hash_table_lookup (manager->priv->groups_by_name, groupname); +} + static void on_new_user_loaded (ActUser *user, GParamSpec *pspec, @@ -1049,6 +1180,74 @@ out: } } +static void +on_new_group_loaded (ActGroup *group, + GParamSpec *pspec, + ActUserManager *manager) +{ + const char *groupname; + ActGroup *old_group; + + if (!act_group_is_loaded (group)) { + g_debug ("ActUserManager: %s loaded function called when not loaded", + describe_group (group)); + return; + } + g_signal_handlers_disconnect_by_func (group, on_new_group_loaded, manager); + + manager->priv->new_groups = g_slist_remove (manager->priv->new_groups, + group); + manager->priv->new_groups_inhibiting_load = g_slist_remove (manager->priv->new_groups_inhibiting_load, + group); + + groupname = act_group_get_group_name (group); + + if (groupname == NULL) { + if (!act_group_is_nonexistent (group)) { + const char *object_path; + + object_path = act_group_get_object_path (group); + + if (object_path != NULL) { + g_warning ("ActUserManager: %s has no groupname " + "(object path: %s, gid: %d)", + describe_group (group), + object_path, (int) act_group_get_gid (group)); + } else { + g_warning ("ActUserManager: %s has no groupname (gid: %d)", + describe_group (group), + (int) act_group_get_gid (group)); + } + } + g_object_unref (group); + goto out; + } + + g_debug ("ActUserManager: %s is now loaded", describe_group (group)); + + old_group = lookup_group_by_name (manager, groupname); + + /* If groupname hasn't been added, yet, add it now + */ + if (old_group == NULL) { + g_debug ("ActUserManager: %s was not yet known, adding it", + describe_group (group)); + add_group (manager, group); + } else { + _act_group_load_from_group (old_group, group); + } + + g_object_unref (group); + +out: + if (manager->priv->new_groups_inhibiting_load == NULL) { + g_debug ("ActUserManager: no pending groups, trying to set loaded property"); + maybe_set_is_loaded (manager); + } else { + g_debug ("ActUserManager: not all groups loaded yet"); + } +} + static ActUser * add_new_user_for_object_path (const char *object_path, ActUserManager *manager) @@ -1066,11 +1265,39 @@ add_new_user_for_object_path (const char *object_path, g_debug ("ActUserManager: tracking new user with object path %s", object_path); user = create_new_user (manager); - _act_user_update_from_object_path (user, object_path); + _act_user_update_from_object_path (manager, user, object_path); + g_hash_table_insert (manager->priv->users_by_object_path, + (gchar *)act_user_get_object_path (user), + g_object_ref (user)); return user; } +static ActGroup * +add_new_group_for_object_path (const char *object_path, + ActUserManager *manager) +{ + ActGroup *group; + + group = g_hash_table_lookup (manager->priv->groups_by_object_path, object_path); + + if (group != NULL) { + g_debug ("ActUserManager: tracking existing %s with object path %s", + describe_group (group), object_path); + return group; + } + + g_debug ("ActUserManager: tracking new group with object path %s", object_path); + + group = create_new_group (manager); + _act_group_update_from_object_path (manager, group, object_path); + g_hash_table_insert (manager->priv->groups_by_object_path, + (gchar *)act_group_get_object_path (group), + g_object_ref (group)); + + return group; +} + static void on_new_user_in_accounts_service (GDBusProxy *proxy, const char *object_path, @@ -1088,6 +1315,22 @@ on_new_user_in_accounts_service (GDBusProxy *proxy, } static void +on_new_group_in_accounts_service (GDBusProxy *proxy, + const char *object_path, + gpointer user_data) +{ + ActUserManager *manager = ACT_USER_MANAGER (user_data); + + if (!manager->priv->is_loaded) { + g_debug ("ActUserManager: ignoring new group in accounts service with object path %s since not loaded yet", object_path); + return; + } + + g_debug ("ActUserManager: new group in accounts service with object path %s", object_path); + add_new_group_for_object_path (object_path, manager); +} + +static void on_user_removed_in_accounts_service (GDBusProxy *proxy, const char *object_path, gpointer user_data) @@ -1110,6 +1353,28 @@ on_user_removed_in_accounts_service (GDBusProxy *proxy, } static void +on_group_removed_in_accounts_service (GDBusProxy *proxy, + const char *object_path, + gpointer user_data) +{ + ActUserManager *manager = ACT_USER_MANAGER (user_data); + ActGroup *group; + + group = g_hash_table_lookup (manager->priv->groups_by_object_path, object_path); + + if (group == NULL) { + g_debug ("ActUserManager: ignoring untracked group %s", object_path); + return; + } else { + g_debug ("ActUserManager: tracked group %s removed from accounts service", object_path); + } + + manager->priv->new_groups = g_slist_remove (manager->priv->new_groups, group); + + remove_group (manager, group); +} + +static void on_get_current_session_finished (GObject *object, GAsyncResult *result, gpointer data) @@ -1398,6 +1663,37 @@ on_find_user_by_name_finished (GObject *object, } static void +on_find_group_by_name_finished (GObject *object, + GAsyncResult *result, + gpointer data) +{ + AccountsAccounts *proxy = ACCOUNTS_ACCOUNTS (object); + ActUserManagerFetchUserRequest *request = data; + GError *error = NULL; + char *group; + + if (!accounts_accounts_call_find_group_by_name_finish (proxy, &group, result, &error)) { + if (error != NULL) { + g_debug ("ActUserManager: Failed to find %s: %s", + request->description, error->message); + g_error_free (error); + } else { + g_debug ("ActUserManager: Failed to find %s", + request->description); + } + give_up (request->manager, request); + return; + } + + g_debug ("ActUserManager: Found object path of %s: %s", + request->description, group); + request->object_path = group; + request->state++; + + fetch_user_incrementally (request); +} + +static void on_find_user_by_id_finished (GObject *object, GAsyncResult *result, gpointer data) @@ -1429,6 +1725,37 @@ on_find_user_by_id_finished (GObject *object, } static void +on_find_group_by_id_finished (GObject *object, + GAsyncResult *result, + gpointer data) +{ + AccountsAccounts *proxy = ACCOUNTS_ACCOUNTS (object); + ActUserManagerFetchUserRequest *request = data; + GError *error = NULL; + char *group; + + if (!accounts_accounts_call_find_group_by_id_finish (proxy, &group, result, &error)) { + if (error != NULL) { + g_debug ("ActUserManager: Failed to find user %lu: %s", + (gulong) request->uid, error->message); + g_error_free (error); + } else { + g_debug ("ActUserManager: Failed to find user with id %lu", + (gulong) request->uid); + } + give_up (request->manager, request); + return; + } + + g_debug ("ActUserManager: Found object path of %s: %s", + request->description, group); + request->object_path = group; + request->state++; + + fetch_user_incrementally (request); +} + +static void find_user_in_accounts_service (ActUserManager *manager, ActUserManagerFetchUserRequest *request) { @@ -1452,7 +1779,20 @@ find_user_in_accounts_service (ActUserManager *manager, on_find_user_by_id_finished, request); break; - + case ACT_USER_MANAGER_FETCH_GROUP_FROM_GROUPNAME_REQUEST: + accounts_accounts_call_find_group_by_name (manager->priv->accounts_proxy, + request->groupname, + NULL, + on_find_group_by_name_finished, + request); + break; + case ACT_USER_MANAGER_FETCH_GROUP_FROM_ID_REQUEST: + accounts_accounts_call_find_group_by_id (manager->priv->accounts_proxy, + request->gid, + NULL, + on_find_group_by_id_finished, + request); + break; } } @@ -1535,6 +1875,50 @@ on_list_cached_users_finished (GObject *object, } static void +on_list_cached_groups_finished (GObject *object, + GAsyncResult *result, + gpointer data) +{ + AccountsAccounts *proxy = ACCOUNTS_ACCOUNTS (object); + ActUserManager *manager = data; + gchar **group_paths; + GError *error = NULL; + + manager->priv->listing_cached_groups = FALSE; + if (!accounts_accounts_call_list_cached_groups_finish (proxy, &group_paths, result, &error)) { + g_debug ("ActUserManager: ListCachedGroups failed: %s", error->message); + g_error_free (error); + + g_object_unref (manager->priv->accounts_proxy); + manager->priv->accounts_proxy = NULL; + + g_object_unref (manager); + return; + } + + if (g_strv_length (group_paths) > 0) { + int i; + + g_debug ("ActUserManager: ListCachedGroups finished, will set loaded property after list is fully loaded"); + for (i = 0; group_paths[i] != NULL; i++) { + ActGroup *group; + + group = add_new_group_for_object_path (group_paths[i], manager); + if (!manager->priv->is_loaded) { + manager->priv->new_groups_inhibiting_load = g_slist_prepend (manager->priv->new_groups_inhibiting_load, group); + } + } + } else { + g_debug ("ActUserManager: ListCachedGroups finished with empty list, maybe setting loaded property now"); + maybe_set_is_loaded (manager); + } + + g_strfreev (group_paths); + + g_object_unref (manager); +} + +static void on_get_x11_display_finished (GObject *object, GAsyncResult *result, gpointer data) @@ -2150,12 +2534,18 @@ free_fetch_user_request (ActUserManagerFetchUserRequest *request) manager = request->manager; - g_object_set_data (G_OBJECT (request->user), "fetch-user-request", NULL); + if (request->user) + g_object_set_data (G_OBJECT (request->user), "fetch-user-request", NULL); + if (request->group) + g_object_set_data (G_OBJECT (request->group), "fetch-user-request", NULL); manager->priv->fetch_user_requests = g_slist_remove (manager->priv->fetch_user_requests, request); if (request->type == ACT_USER_MANAGER_FETCH_USER_FROM_USERNAME_REQUEST) { g_free (request->username); } + if (request->type == ACT_USER_MANAGER_FETCH_GROUP_FROM_GROUPNAME_REQUEST) { + g_free (request->groupname); + } g_free (request->object_path); g_free (request->description); @@ -2175,6 +2565,8 @@ give_up (ActUserManager *manager, if (request->user) _act_user_update_as_nonexistent (request->user); + if (request->group) + _act_group_update_as_nonexistent (request->group); } static void @@ -2226,7 +2618,10 @@ fetch_user_incrementally (ActUserManagerFetchUserRequest *request) break; case ACT_USER_MANAGER_GET_USER_STATE_FETCHED: g_debug ("ActUserManager: %s fetched", request->description); - _act_user_update_from_object_path (request->user, request->object_path); + if (request->user) + _act_user_update_from_object_path (request->manager, request->user, request->object_path); + if (request->group) + _act_group_update_from_object_path (request->manager, request->group, request->object_path); break; case ACT_USER_MANAGER_GET_USER_STATE_UNFETCHED: g_debug ("ActUserManager: %s was not fetched", request->description); @@ -2266,6 +2661,28 @@ fetch_user_with_username_from_accounts_service (ActUserManager *manager, } static void +fetch_group_with_groupname_from_accounts_service (ActUserManager *manager, + ActGroup *group, + const char *groupname) +{ + ActUserManagerFetchUserRequest *request; + + request = g_slice_new0 (ActUserManagerFetchUserRequest); + + request->manager = g_object_ref (manager); + request->type = ACT_USER_MANAGER_FETCH_GROUP_FROM_GROUPNAME_REQUEST; + request->groupname = g_strdup (groupname); + request->group = group; + request->state = ACT_USER_MANAGER_GET_USER_STATE_UNFETCHED + 1; + request->description = g_strdup_printf ("group '%s'", request->groupname); + + manager->priv->fetch_user_requests = g_slist_prepend (manager->priv->fetch_user_requests, + request); + g_object_set_data (G_OBJECT (group), "fetch-user-request", request); + fetch_user_incrementally (request); +} + +static void fetch_user_with_id_from_accounts_service (ActUserManager *manager, ActUser *user, uid_t id) @@ -2287,6 +2704,28 @@ fetch_user_with_id_from_accounts_service (ActUserManager *manager, fetch_user_incrementally (request); } +static void +fetch_group_with_id_from_accounts_service (ActUserManager *manager, + ActGroup *group, + gid_t id) +{ + ActUserManagerFetchUserRequest *request; + + request = g_slice_new0 (ActUserManagerFetchUserRequest); + + request->manager = g_object_ref (manager); + request->type = ACT_USER_MANAGER_FETCH_GROUP_FROM_ID_REQUEST; + request->gid = id; + request->group = group; + request->state = ACT_USER_MANAGER_GET_USER_STATE_UNFETCHED + 1; + request->description = g_strdup_printf ("group with id %lu", (gulong) request->gid); + + manager->priv->fetch_user_requests = g_slist_prepend (manager->priv->fetch_user_requests, + request); + g_object_set_data (G_OBJECT (group), "fetch-user-request", request); + fetch_user_incrementally (request); +} + /** * act_user_manager_get_user: * @manager: the manager to query. @@ -2324,6 +2763,44 @@ act_user_manager_get_user (ActUserManager *manager, } /** + * act_user_manager_get_group: + * @manager: the manager to query. + * @groupname: the login name of the group to get. + * + * Retrieves a pointer to the #ActGroup object for the login @groupname + * from @manager. Trying to use this object before its + * #ActGroup:is-loaded property is %TRUE will result in undefined + * behavior. + * + * Returns: (transfer none): #ActGroup object + * + * Since: 0.6.36 + **/ +ActGroup * +act_user_manager_get_group (ActUserManager *manager, + const char *groupname) +{ + ActGroup *group; + + g_return_val_if_fail (ACT_IS_USER_MANAGER (manager), NULL); + g_return_val_if_fail (groupname != NULL && groupname[0] != '\0', NULL); + + group = lookup_group_by_name (manager, groupname); + + /* if we don't have it loaded try to load it now */ + if (group == NULL) { + g_debug ("ActUserManager: trying to track new group with groupname %s", groupname); + group = create_new_group (manager); + + if (manager->priv->accounts_proxy != NULL) { + fetch_group_with_groupname_from_accounts_service (manager, group, groupname); + } + } + + return group; +} + +/** * act_user_manager_get_user_by_id: * @manager: the manager to query. * @id: the uid of the user to get. @@ -2362,6 +2839,45 @@ act_user_manager_get_user_by_id (ActUserManager *manager, return user; } +/** + * act_user_manager_get_group_by_id: + * @manager: the manager to query. + * @id: the uid of the group to get. + * + * Retrieves a pointer to the #ActGroup object for the group with the + * given uid from @manager. Trying to use this object before its + * #ActGroup:is-loaded property is %TRUE will result in undefined + * behavior. + * + * Returns: (transfer none): #ActGroup object + */ +ActGroup * +act_user_manager_get_group_by_id (ActUserManager *manager, + gid_t id) +{ + ActGroup *group; + gchar *object_path; + + g_return_val_if_fail (ACT_IS_USER_MANAGER (manager), NULL); + + object_path = g_strdup_printf ("/org/freedesktop/Accounts/Group%lu", (gulong) id); + group = g_hash_table_lookup (manager->priv->groups_by_object_path, object_path); + g_free (object_path); + + if (group != NULL) { + return g_object_ref (group); + } else { + g_debug ("ActUserManager: trying to track new group with uid %lu", (gulong) id); + group = create_new_group (manager); + + if (manager->priv->accounts_proxy != NULL) { + fetch_group_with_id_from_accounts_service (manager, group, id); + } + } + + return group; +} + static void listify_hash_values_hfunc (gpointer key, gpointer value, @@ -2393,6 +2909,29 @@ act_user_manager_list_users (ActUserManager *manager) return g_slist_sort (retval, (GCompareFunc) act_user_collate); } +/** + * act_user_manager_list_groups: + * @manager: a #ActUserManager + * + * Get a list of groups + * + * Returns: (element-type ActGroup) (transfer container): List of #ActGroup objects + * + * Since: 0.6.36 + */ +GSList * +act_user_manager_list_groups (ActUserManager *manager) +{ + GSList *retval; + + g_return_val_if_fail (ACT_IS_USER_MANAGER (manager), NULL); + + retval = NULL; + g_hash_table_foreach (manager->priv->groups_by_name, listify_hash_values_hfunc, &retval); + + return retval; +} + static void maybe_set_is_loaded (ActUserManager *manager) { @@ -2416,6 +2955,16 @@ maybe_set_is_loaded (ActUserManager *manager) return; } + if (manager->priv->listing_cached_groups) { + g_debug ("ActUserManager: Listing cached groups, so not setting loaded property"); + return; + } + + if (manager->priv->new_groups_inhibiting_load != NULL) { + g_debug ("ActUserManager: Loading new groups, so not setting loaded property"); + return; + } + /* Don't set is_loaded yet unless the seat is already loaded * or failed to load. */ @@ -2526,6 +3075,19 @@ load_users (ActUserManager *manager) manager->priv->listing_cached_users = TRUE; } +static void +load_groups (ActUserManager *manager) +{ + g_assert (manager->priv->accounts_proxy != NULL); + g_debug ("ActUserManager: calling 'ListCachedGroups'"); + + accounts_accounts_call_list_cached_groups (manager->priv->accounts_proxy, + NULL, + on_list_cached_groups_finished, + g_object_ref (manager)); + manager->priv->listing_cached_groups = TRUE; +} + static gboolean load_seat_incrementally (ActUserManager *manager) { @@ -2565,9 +3127,10 @@ load_idle (ActUserManager *manager) { /* The order below is important: load_seat_incrementally might set "is-loaded" immediately and we thus need to call - load_users before it. + load_users and load_groups before it. */ load_users (manager); + load_groups (manager); manager->priv->seat.state = ACT_USER_MANAGER_SEAT_STATE_UNLOADED + 1; load_seat_incrementally (manager); manager->priv->load_id = 0; @@ -2749,6 +3312,46 @@ act_user_manager_class_init (ActUserManagerClass *klass) g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, ACT_TYPE_USER); + /** + * ActUserManager::group-added: + * + * Emitted when a group is added to the user manager. + */ + signals [GROUP_ADDED] = + g_signal_new ("group-added", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ActUserManagerClass, group_added), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, ACT_TYPE_GROUP); + /** + * ActUserManager::group-removed: + * + * Emitted when a group is removed from the user manager. + */ + signals [GROUP_REMOVED] = + g_signal_new ("group-removed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ActUserManagerClass, group_removed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, ACT_TYPE_GROUP); + /** + * ActUserManager::group-changed: + * + * One of the groups has changed + */ + signals [GROUP_CHANGED] = + g_signal_new ("group-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ActUserManagerClass, group_changed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, ACT_TYPE_GROUP); + g_type_class_add_private (klass, sizeof (ActUserManagerPrivate)); } @@ -2798,6 +3401,14 @@ act_user_manager_init (ActUserManager *manager) g_str_equal, NULL, g_object_unref); + manager->priv->groups_by_name = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_object_unref); + manager->priv->groups_by_object_path = g_hash_table_new_full (g_str_hash, + g_str_equal, + NULL, + g_object_unref); error = NULL; manager->priv->connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); @@ -2837,6 +3448,15 @@ act_user_manager_init (ActUserManager *manager) G_CALLBACK (on_user_removed_in_accounts_service), manager); + g_signal_connect (manager->priv->accounts_proxy, + "group-added", + G_CALLBACK (on_new_group_in_accounts_service), + manager); + g_signal_connect (manager->priv->accounts_proxy, + "group-deleted", + G_CALLBACK (on_group_removed_in_accounts_service), + manager); + manager->priv->seat.state = ACT_USER_MANAGER_SEAT_STATE_UNLOADED; } @@ -2929,6 +3549,8 @@ act_user_manager_finalize (GObject *object) g_hash_table_destroy (manager->priv->normal_users_by_name); g_hash_table_destroy (manager->priv->system_users_by_name); g_hash_table_destroy (manager->priv->users_by_object_path); + g_hash_table_destroy (manager->priv->groups_by_name); + g_hash_table_destroy (manager->priv->groups_by_object_path); G_OBJECT_CLASS (act_user_manager_parent_class)->finalize (object); } @@ -3420,3 +4042,274 @@ act_user_manager_delete_user_finish (ActUserManager *manager, return success; } + +/** + * act_user_manager_create_group: + * @manager: a #ActUserManager + * @groupname: a unix group name + * @error: a #GError + * + * Creates a group on the system. + * + * Returns: (transfer full): group object + * + * Since: 0.6.36 + */ +ActGroup * +act_user_manager_create_group (ActUserManager *manager, + const char *groupname, + GError **error) +{ + GError *local_error = NULL; + gboolean res; + gchar *path; + ActGroup *group; + + g_debug ("ActUserManager: Creating group '%s'", + groupname); + + g_assert (manager->priv->accounts_proxy != NULL); + + local_error = NULL; + res = accounts_accounts_call_create_group_sync (manager->priv->accounts_proxy, + groupname, + &path, + NULL, + &local_error); + if (! res) { + g_propagate_error (error, local_error); + return NULL; + } + + group = add_new_group_for_object_path (path, manager); + + g_free (path); + + return group; +} + +/** + * act_user_manager_create_group_async: + * @manager: a #ActUserManager + * @groupname: a unix group name + * @cancellable: (allow-none): optional #GCancellable object, + * %NULL to ignore + * @callback: (scope async): a #GAsyncReadyCallback to call + * when the request is satisfied + * @user_data: (closure): the data to pass to @callback + * + * Asynchronously creates a group on the system. + * + * For more details, see act_user_manager_create_group(), which + * is the synchronous version of this call. + * + * Since: 0.6.36 + */ +void +act_user_manager_create_group_async (ActUserManager *manager, + const char *groupname, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *res; + + g_return_if_fail (ACT_IS_USER_MANAGER (manager)); + g_return_if_fail (manager->priv->accounts_proxy != NULL); + + g_debug ("ActUserManager: Creating group (async) '%s'", + groupname); + + g_assert (manager->priv->accounts_proxy != NULL); + + res = g_simple_async_result_new (G_OBJECT (manager), + callback, user_data, + act_user_manager_create_group_async); + g_simple_async_result_set_check_cancellable (res, cancellable); + + accounts_accounts_call_create_group (manager->priv->accounts_proxy, + groupname, + cancellable, + act_user_manager_async_complete_handler, res); +} + +/** + * act_user_manager_create_group_finish: + * @manager: a #ActUserManager + * @result: a #GAsyncResult + * @error: a #GError + * + * Finishes an asynchronous user creation. + * + * See act_user_manager_create_user_async(). + * + * Returns: (transfer full): group object + * + * Since: 0.6.37 + */ +ActGroup * +act_user_manager_create_group_finish (ActUserManager *manager, + GAsyncResult *result, + GError **error) +{ + GAsyncResult *inner_result; + ActGroup *group = NULL; + gchar *path; + GSimpleAsyncResult *res; + GError *remote_error = NULL; + + g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (manager), act_user_manager_create_group_async), FALSE); + + res = G_SIMPLE_ASYNC_RESULT (result); + inner_result = g_simple_async_result_get_op_res_gpointer (res); + g_assert (inner_result); + + if (accounts_accounts_call_create_group_finish (manager->priv->accounts_proxy, + &path, inner_result, &remote_error)) { + group = add_new_group_for_object_path (path, manager); + g_free (path); + } + + if (remote_error) { + g_dbus_error_strip_remote_error (remote_error); + g_propagate_error (error, remote_error); + } + + return group; +} + +/** + * act_user_manager_delete_group: + * @manager: a #ActUserManager + * @group: an #ActGroup object + * @error: a #GError + * + * Deletes a group account on the system. + * + * Returns: %TRUE if the group account was successfully deleted + * + * Since: 0.6.36 + */ +gboolean +act_user_manager_delete_group (ActUserManager *manager, + ActGroup *group, + GError **error) +{ + GError *local_error; + gboolean res = TRUE; + + g_return_val_if_fail (ACT_IS_USER_MANAGER (manager), FALSE); + g_return_val_if_fail (ACT_IS_GROUP (group), FALSE); + g_return_val_if_fail (manager->priv->accounts_proxy != NULL, FALSE); + + g_debug ("ActUserManager: Deleting group '%s' (gid %ld)", + act_group_get_group_name (group), (long) act_group_get_gid (group)); + + local_error = NULL; + if (!accounts_accounts_call_delete_group_sync (manager->priv->accounts_proxy, + act_group_get_gid (group), + NULL, + &local_error)) { + g_propagate_error (error, local_error); + res = FALSE; + } + + return res; +} + +/** + * act_user_manager_delete_group_async: + * @manager: a #ActUserManager + * @group: a #ActGroup object + * @cancellable: (allow-none): optional #GCancellable object, + * %NULL to ignore + * @callback: (scope async): a #GAsyncReadyCallback to call + * when the request is satisfied + * @user_data: (closure): the data to pass to @callback + * + * Asynchronously deletes a group account from the system. + * + * For more details, see act_user_manager_delete_group(), which + * is the synchronous version of this call. + * + * Since: 0.6.36 + */ +void +act_user_manager_delete_group_async (ActUserManager *manager, + ActGroup *group, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *res; + + g_return_if_fail (ACT_IS_USER_MANAGER (manager)); + g_return_if_fail (ACT_IS_GROUP (group)); + g_return_if_fail (manager->priv->accounts_proxy != NULL); + + res = g_simple_async_result_new (G_OBJECT (manager), + callback, user_data, + act_user_manager_delete_group_async); + g_simple_async_result_set_check_cancellable (res, cancellable); + + g_debug ("ActUserManager: Deleting (async) group '%s' (gid %ld)", + act_group_get_group_name (group), (long) act_group_get_gid (group)); + + accounts_accounts_call_delete_group (manager->priv->accounts_proxy, + act_group_get_gid (group), + cancellable, + act_user_manager_async_complete_handler, res); +} + +/** + * act_user_manager_delete_group_finish: + * @manager: a #ActUserManager + * @result: a #GAsyncResult + * @error: a #GError + * + * Finishes an asynchronous group account deletion. + * + * See act_user_manager_delete_group_async(). + * + * Returns: %TRUE if the group account was successfully deleted + * + * Since: 0.6.36 + */ +gboolean +act_user_manager_delete_group_finish (ActUserManager *manager, + GAsyncResult *result, + GError **error) +{ + GAsyncResult *inner_result; + gboolean success; + GSimpleAsyncResult *res; + GError *remote_error = NULL; + + g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (manager), act_user_manager_delete_group_async), FALSE); + res = G_SIMPLE_ASYNC_RESULT (result); + inner_result = g_simple_async_result_get_op_res_gpointer (res); + g_assert (inner_result); + + success = accounts_accounts_call_delete_group_finish (manager->priv->accounts_proxy, + inner_result, &remote_error); + if (remote_error) { + g_dbus_error_strip_remote_error (remote_error); + g_propagate_error (error, remote_error); + } + + return success; +} + +ActUser * +_act_user_manager_get_user (ActUserManager *manager, + const gchar *object_path) +{ + return add_new_user_for_object_path (object_path, manager); +} + +ActGroup * +_act_user_manager_get_group (ActUserManager *manager, + const gchar *object_path) +{ + return add_new_group_for_object_path (object_path, manager); +} |