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 guint num_setup_users;
29 GHashTable *starting_dirs;
34 SharedDataManager *manager;
38 G_DEFINE_TYPE (SharedDataManager, shared_data_manager, G_TYPE_OBJECT);
41 shared_data_manager_new (void)
43 return g_object_new (SHARED_DATA_MANAGER_TYPE, NULL);
47 delete_unused_user (gpointer key, gpointer value, gpointer user_data)
49 const gchar *user = (const gchar *)key;
52 /* Listen, the rest of this file is nice async glib code and all, but
53 for this operation, we just need a fire and forget rm -rf. Since
54 recursively deleting in GIO is a huge pain in the butt, we'll just drop
57 gchar *path = g_build_filename (USERS_DIR, user, NULL);
58 gchar *quoted_path = g_shell_quote (path);
59 gchar *cmd = g_strdup_printf ("/bin/rm -rf %s", quoted_path);
61 if (!g_spawn_command_line_async (cmd, &error))
63 g_warning ("Could not delete unused user data directory %s: %s", path, error->message);
73 chown_user_dir_cb (GObject *object, GAsyncResult *res, gpointer user_data)
75 GFile *file = G_FILE (object);
76 GFileInfo *info = NULL;
79 if (!g_file_set_attributes_finish (file, res, &info, &error))
81 gchar *path = g_file_get_path (file);
82 g_warning ("Could not chown user data directory %s: %s",
83 path, error->message);
89 g_object_unref (info);
93 make_user_dir_cb (GObject *object, GAsyncResult *res, gpointer user_data)
95 GFile *file = G_FILE (object);
96 struct OwnerInfo *owner = (struct OwnerInfo *)user_data;
99 if (!g_file_make_directory_finish (file, res, &error))
101 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
103 gchar *path = g_file_get_path (file);
104 g_warning ("Could not create user data directory %s: %s",
105 path, error->message);
107 g_error_free (error);
108 owner->manager->priv->num_setup_users--;
109 g_object_unref (owner->manager);
113 g_error_free (error);
116 /* Even if the directory already exists, we want to re-affirm the owners
117 because the greeter gid is configuration based and may change between
119 GFileInfo *info = g_file_info_new ();
120 g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_UID,
122 g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_GID,
123 owner->manager->priv->greeter_gid);
124 g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE, 0770);
125 g_file_set_attributes_async (file, info, G_FILE_QUERY_INFO_NONE,
126 G_PRIORITY_DEFAULT, NULL,
127 chown_user_dir_cb, NULL);
129 /* If we're the last user dir to be set up, delete unused user dirs */
130 owner->manager->priv->num_setup_users--;
131 if (owner->manager->priv->starting_dirs != NULL &&
132 owner->manager->priv->num_setup_users == 0)
134 g_hash_table_foreach (owner->manager->priv->starting_dirs,
135 delete_unused_user, owner->manager);
136 g_hash_table_destroy (owner->manager->priv->starting_dirs);
137 owner->manager->priv->starting_dirs = NULL;
140 g_object_unref (owner->manager);
145 setup_user_dir (SharedDataManager *manager, guint32 uid)
147 struct OwnerInfo *owner = g_malloc (sizeof (struct OwnerInfo));
148 owner->manager = g_object_ref (manager);
151 gchar *uidstr = g_strdup_printf ("%u", uid);
152 gchar *path = g_build_filename (USERS_DIR, uidstr, NULL);
153 GFile *file = g_file_new_for_path (path);
156 manager->priv->num_setup_users++;
157 if (manager->priv->starting_dirs != NULL)
158 g_hash_table_remove (manager->priv->starting_dirs, uidstr);
161 g_file_make_directory_async (file, G_PRIORITY_DEFAULT, NULL,
162 make_user_dir_cb, owner);
164 g_object_unref (file);
168 next_user_dirs_cb (GObject *object, GAsyncResult *res, gpointer user_data)
170 GFileEnumerator *enumerator = G_FILE_ENUMERATOR (object);
171 SharedDataManager *manager = SHARED_DATA_MANAGER (user_data);
173 GError *error = NULL;
175 GList *files = g_file_enumerator_next_files_finish (enumerator, res,
179 g_warning ("Could not enumerate user data directory %s: %s",
180 USERS_DIR, error->message);
181 g_error_free (error);
182 g_object_unref (manager);
186 for (link = files; link; link = link->next)
188 GFileInfo *info = link->data;
189 g_hash_table_insert (manager->priv->starting_dirs,
190 g_strdup (g_file_info_get_name (info)), NULL);
195 g_list_free_full (files, g_object_unref);
196 g_file_enumerator_next_files_async (enumerator, NUM_ENUMERATION_FILES,
197 G_PRIORITY_DEFAULT, NULL,
198 next_user_dirs_cb, manager);
202 // We've finally assembled all the initial directories. Now let's
203 // iterate the current users and set them each up. As we go, we'll
204 // remove the users from the starting_dirs hash and thus see which
205 // users are obsolete.
206 GList *users = common_user_list_get_users (common_user_list_get_instance ());
207 for (link = users; link; link = link->next)
209 CommonUser *user = link->data;
210 setup_user_dir (manager, common_user_get_uid (user));
212 // Also set up our own greeter dir, so it has a place to dump its own files
213 // (imagine it holding some large files temporarily before shunting them
214 // to the next user to log in's specific directory).
215 setup_user_dir (manager, manager->priv->greeter_uid);
216 g_object_unref (manager);
221 list_user_dirs_cb (GObject *object, GAsyncResult *res, gpointer user_data)
223 GFile *file = G_FILE (object);
224 SharedDataManager *manager = SHARED_DATA_MANAGER (user_data);
225 GFileEnumerator *enumerator;
226 GError *error = NULL;
228 enumerator = g_file_enumerate_children_finish (file, res, &error);
229 if (enumerator == NULL)
231 g_warning ("Could not enumerate user data directory %s: %s",
232 USERS_DIR, error->message);
233 g_error_free (error);
234 g_object_unref (manager);
238 manager->priv->starting_dirs = g_hash_table_new_full (g_str_hash,
241 g_file_enumerator_next_files_async (enumerator, NUM_ENUMERATION_FILES,
242 G_PRIORITY_DEFAULT, NULL,
243 next_user_dirs_cb, manager);
247 user_added_cb (CommonUserList *list, CommonUser *user,
248 SharedDataManager *manager)
250 setup_user_dir (manager, common_user_get_uid (user));
254 user_removed_cb (CommonUserList *list, CommonUser *user,
255 SharedDataManager *manager)
257 gchar *uid = g_strdup_printf ("%u", common_user_get_uid (user));
258 delete_unused_user (uid, NULL, manager);
263 shared_data_manager_init (SharedDataManager *manager)
265 manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager, SHARED_DATA_MANAGER_TYPE, SharedDataManagerPrivate);
267 // Grab current greeter-user gid
269 struct passwd *greeter_entry;
270 greeter_user = config_get_string (config_get_instance (), "LightDM", "greeter-user");
271 greeter_entry = getpwnam (greeter_user);
274 manager->priv->greeter_uid = greeter_entry->pw_uid;
275 manager->priv->greeter_gid = greeter_entry->pw_gid;
277 g_free (greeter_user);
279 /* Grab list of all current directories, so we know if any exist that we
281 GFile *file = g_file_new_for_path (USERS_DIR);
282 g_file_enumerate_children_async (file, G_FILE_ATTRIBUTE_STANDARD_NAME,
283 G_FILE_QUERY_INFO_NONE,
284 G_PRIORITY_DEFAULT, NULL,
285 list_user_dirs_cb, g_object_ref (manager));
286 g_object_unref (file);
288 /* And listen for user changes. The chance of a race with the above
289 initial setup is so tiny, it's not worth worrying about. */
290 g_signal_connect (common_user_list_get_instance (), "user-added",
291 G_CALLBACK (user_added_cb), manager);
292 g_signal_connect (common_user_list_get_instance (), "user-removed",
293 G_CALLBACK (user_removed_cb), manager);
297 shared_data_manager_dispose (GObject *object)
299 SharedDataManager *self = SHARED_DATA_MANAGER (object);
301 /* Should also cancel outstanding GIO operations, but whatever, let them
304 g_signal_handlers_disconnect_by_data (common_user_list_get_instance (),
307 G_OBJECT_CLASS (shared_data_manager_parent_class)->dispose (object);
311 shared_data_manager_finalize (GObject *object)
313 SharedDataManager *self = SHARED_DATA_MANAGER (object);
315 if (self->priv->starting_dirs)
316 g_hash_table_destroy (self->priv->starting_dirs);
318 G_OBJECT_CLASS (shared_data_manager_parent_class)->finalize (object);
322 shared_data_manager_class_init (SharedDataManagerClass *klass)
324 GObjectClass *object_class = G_OBJECT_CLASS (klass);
326 object_class->dispose = shared_data_manager_dispose;
327 object_class->finalize = shared_data_manager_finalize;
329 g_type_class_add_private (klass, sizeof (SharedDataManagerPrivate));