2 * Copyright (C) 2014 Canonical, Ltd
3 * Author: Michael Terry <michael.terry@canonical.com>
5 * This program is free software: you can redistribute it and/or modify it under
6 * the terms of the GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option) any later
8 * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
15 #include <sys/types.h>
18 #include "configuration.h"
19 #include "shared-data-manager.h"
20 #include "user-list.h"
22 #define NUM_ENUMERATION_FILES 100
24 struct SharedDataManagerPrivate
28 GHashTable *starting_dirs;
33 SharedDataManager *manager;
37 G_DEFINE_TYPE (SharedDataManager, shared_data_manager, G_TYPE_OBJECT);
39 static SharedDataManager *singleton = NULL;
42 shared_data_manager_get_instance (void)
45 singleton = g_object_new (SHARED_DATA_MANAGER_TYPE, NULL);
50 shared_data_manager_cleanup (void)
54 g_object_unref (singleton);
60 delete_unused_user (gpointer key, gpointer value, gpointer user_data)
62 const gchar *user = (const gchar *)key;
65 /* For this operation, we just need a fire and forget rm -rf. Since
66 recursively deleting in GIO is a huge pain in the butt, we'll just drop
69 gchar *path = g_build_filename (USERS_DIR, user, NULL);
70 gchar *quoted_path = g_shell_quote (path);
71 gchar *cmd = g_strdup_printf ("/bin/rm -rf %s", quoted_path);
73 g_spawn_command_line_async (cmd, &error);
75 g_warning ("Could not delete unused user data directory %s: %s", path, error->message);
76 g_clear_error (&error);
84 shared_data_manager_ensure_user_dir (SharedDataManager *manager, const gchar *user)
93 entry = getpwnam (user);
97 path = g_build_filename (USERS_DIR, user, NULL);
98 file = g_file_new_for_path (path);
100 g_debug ("Creating shared data directory %s", path);
102 result = g_file_make_directory (file, NULL, &error);
103 if (error && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
104 g_warning ("Could not create user data directory %s: %s", path, error->message);
105 g_clear_error (&error);
108 g_object_unref (file);
113 /* Even if the directory already exists, we want to re-affirm the owners
114 because the greeter gid is configuration based and may change between
116 info = g_file_info_new ();
117 g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_UID, entry->pw_uid);
118 g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_GID, manager->priv->greeter_gid);
119 g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE, 0770);
120 result = g_file_set_attributes_from_info (file, info, G_FILE_QUERY_INFO_NONE, NULL, &error);
121 g_object_unref (info);
122 g_object_unref (file);
124 g_warning ("Could not chown user data directory %s: %s", path, error->message);
135 next_user_dirs_cb (GObject *object, GAsyncResult *res, gpointer user_data)
137 GFileEnumerator *enumerator = G_FILE_ENUMERATOR (object);
138 SharedDataManager *manager = SHARED_DATA_MANAGER (user_data);
140 GError *error = NULL;
142 GList *files = g_file_enumerator_next_files_finish (enumerator, res, &error);
145 g_warning ("Could not enumerate user data directory %s: %s", USERS_DIR, error->message);
146 g_clear_error (&error);
147 g_object_unref (manager);
151 for (link = files; link; link = link->next)
153 GFileInfo *info = link->data;
154 g_hash_table_insert (manager->priv->starting_dirs, g_strdup (g_file_info_get_name (info)), NULL);
159 g_list_free_full (files, g_object_unref);
160 g_file_enumerator_next_files_async (enumerator, NUM_ENUMERATION_FILES, G_PRIORITY_DEFAULT, NULL, next_user_dirs_cb, manager);
164 // We've finally assembled all the initial directories. Now let's
165 // iterate the current users and as we go, remove the users from the
166 // starting_dirs hash and thus see which users are obsolete.
167 GList *users = common_user_list_get_users (common_user_list_get_instance ());
168 for (link = users; link; link = link->next)
170 CommonUser *user = link->data;
171 g_hash_table_remove (manager->priv->starting_dirs, common_user_get_name (user));
173 g_hash_table_foreach (manager->priv->starting_dirs, delete_unused_user, manager);
174 g_hash_table_destroy (manager->priv->starting_dirs);
175 manager->priv->starting_dirs = NULL;
177 g_object_unref (manager);
182 list_user_dirs_cb (GObject *object, GAsyncResult *res, gpointer user_data)
184 GFile *file = G_FILE (object);
185 SharedDataManager *manager = SHARED_DATA_MANAGER (user_data);
186 GFileEnumerator *enumerator;
187 GError *error = NULL;
189 enumerator = g_file_enumerate_children_finish (file, res, &error);
191 g_warning ("Could not enumerate user data directory %s: %s", USERS_DIR, error->message);
192 g_clear_error (&error);
195 g_object_unref (manager);
199 manager->priv->starting_dirs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
200 g_file_enumerator_next_files_async (enumerator, NUM_ENUMERATION_FILES,
201 G_PRIORITY_DEFAULT, NULL,
202 next_user_dirs_cb, manager);
206 user_removed_cb (CommonUserList *list, CommonUser *user, SharedDataManager *manager)
208 delete_unused_user ((gpointer) common_user_get_name (user), NULL, manager);
212 shared_data_manager_start (SharedDataManager *manager)
216 /* Grab list of all current directories, so we know if any exist that we no longer need. */
217 file = g_file_new_for_path (USERS_DIR);
218 g_file_enumerate_children_async (file, G_FILE_ATTRIBUTE_STANDARD_NAME,
219 G_FILE_QUERY_INFO_NONE,
220 G_PRIORITY_DEFAULT, NULL,
221 list_user_dirs_cb, g_object_ref (manager));
222 g_object_unref (file);
224 /* And listen for user removals. */
225 g_signal_connect (common_user_list_get_instance (), "user-removed", G_CALLBACK (user_removed_cb), manager);
229 shared_data_manager_init (SharedDataManager *manager)
231 struct passwd *greeter_entry;
233 manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager, SHARED_DATA_MANAGER_TYPE, SharedDataManagerPrivate);
235 /* Grab current greeter-user gid */
236 manager->priv->greeter_user = config_get_string (config_get_instance (), "LightDM", "greeter-user");
237 greeter_entry = getpwnam (manager->priv->greeter_user);
239 manager->priv->greeter_gid = greeter_entry->pw_gid;
243 shared_data_manager_dispose (GObject *object)
245 SharedDataManager *self = SHARED_DATA_MANAGER (object);
247 /* Should also cancel outstanding GIO operations, but whatever, let them do their thing. */
249 g_signal_handlers_disconnect_by_data (common_user_list_get_instance (), self);
251 G_OBJECT_CLASS (shared_data_manager_parent_class)->dispose (object);
255 shared_data_manager_finalize (GObject *object)
257 SharedDataManager *self = SHARED_DATA_MANAGER (object);
259 if (self->priv->starting_dirs)
260 g_hash_table_destroy (self->priv->starting_dirs);
262 g_free (self->priv->greeter_user);
264 G_OBJECT_CLASS (shared_data_manager_parent_class)->finalize (object);
268 shared_data_manager_class_init (SharedDataManagerClass *klass)
270 GObjectClass *object_class = G_OBJECT_CLASS (klass);
272 object_class->dispose = shared_data_manager_dispose;
273 object_class->finalize = shared_data_manager_finalize;
275 g_type_class_add_private (klass, sizeof (SharedDataManagerPrivate));