]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/shared-data-manager.c
Remove trailing whitespace
[sojka/lightdm.git] / src / shared-data-manager.c
1 /*
2  * Copyright (C) 2014 Canonical, Ltd
3  * Author: Michael Terry <michael.terry@canonical.com>
4  *
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
9  * license.
10  */
11
12 #include <config.h>
13 #include <gio/gio.h>
14 #include <pwd.h>
15 #include <sys/types.h>
16 #include <unistd.h>
17
18 #include "configuration.h"
19 #include "shared-data-manager.h"
20 #include "user-list.h"
21
22 #define NUM_ENUMERATION_FILES 100
23
24 struct SharedDataManagerPrivate
25 {
26     gchar *greeter_user;
27     guint32 greeter_gid;
28     GHashTable *starting_dirs;
29 };
30
31 struct OwnerInfo
32 {
33     SharedDataManager *manager;
34     guint32 uid;
35 };
36
37 G_DEFINE_TYPE (SharedDataManager, shared_data_manager, G_TYPE_OBJECT);
38
39 static SharedDataManager *singleton = NULL;
40
41 SharedDataManager *
42 shared_data_manager_get_instance (void)
43 {
44     if (!singleton)
45         singleton = g_object_new (SHARED_DATA_MANAGER_TYPE, NULL);
46     return singleton;
47 }
48
49 void
50 shared_data_manager_cleanup (void)
51 {
52     if (singleton)
53     {
54         g_object_unref (singleton);
55         singleton = NULL;
56     }
57 }
58
59 static void
60 delete_unused_user (gpointer key, gpointer value, gpointer user_data)
61 {
62     const gchar *user = (const gchar *)key;
63     GError *error = NULL;
64
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
67        to shell for this. */
68
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);
72
73     g_spawn_command_line_async (cmd, &error);
74     if (error)
75         g_warning ("Could not delete unused user data directory %s: %s", path, error->message);
76     g_clear_error (&error);
77
78     g_free (cmd);
79     g_free (quoted_path);
80     g_free (path);
81 }
82
83 gchar *
84 shared_data_manager_ensure_user_dir (SharedDataManager *manager, const gchar *user)
85 {
86     struct passwd *entry;
87     gchar *path;
88     GFile *file;
89     gboolean result;
90     GFileInfo *info;
91     GError *error = NULL;
92
93     entry = getpwnam (user);
94     if (!entry)
95         return NULL;
96
97     path = g_build_filename (USERS_DIR, user, NULL);
98     file = g_file_new_for_path (path);
99
100     g_debug ("Creating shared data directory %s", path);
101
102     result = g_file_make_directory (file, NULL, &error);
103     if (error)
104     {
105         if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
106             result = TRUE;
107         else
108             g_warning ("Could not create user data directory %s: %s", path, error->message);
109     }
110     g_clear_error (&error);
111     if (!result)
112     {
113         g_object_unref (file);
114         g_free (path);
115         return NULL;
116     }
117
118     /* Even if the directory already exists, we want to re-affirm the owners
119        because the greeter gid is configuration based and may change between
120        runs. */
121     info = g_file_info_new ();
122     g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_UID, entry->pw_uid);
123     g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_GID, manager->priv->greeter_gid);
124     g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE, 0770);
125     result = g_file_set_attributes_from_info (file, info, G_FILE_QUERY_INFO_NONE, NULL, &error);
126     g_object_unref (info);
127     g_object_unref (file);
128     if (error)
129         g_warning ("Could not chown user data directory %s: %s", path, error->message);
130     if (!result)
131     {
132         g_free (path);
133         return NULL;
134     }
135
136     return path;
137 }
138
139 static void
140 next_user_dirs_cb (GObject *object, GAsyncResult *res, gpointer user_data)
141 {
142     GFileEnumerator *enumerator = G_FILE_ENUMERATOR (object);
143     SharedDataManager *manager = SHARED_DATA_MANAGER (user_data);
144     GList *link;
145     GError *error = NULL;
146
147     GList *files = g_file_enumerator_next_files_finish (enumerator, res, &error);
148     if (error)
149     {
150         g_warning ("Could not enumerate user data directory %s: %s", USERS_DIR, error->message);
151         g_clear_error (&error);
152         g_object_unref (manager);
153         return;
154     }
155
156     for (link = files; link; link = link->next)
157     {
158         GFileInfo *info = link->data;
159         g_hash_table_insert (manager->priv->starting_dirs, g_strdup (g_file_info_get_name (info)), NULL);
160     }
161
162     if (files != NULL)
163     {
164         g_list_free_full (files, g_object_unref);
165         g_file_enumerator_next_files_async (enumerator, NUM_ENUMERATION_FILES, G_PRIORITY_DEFAULT, NULL, next_user_dirs_cb, manager);
166     }
167     else
168     {
169         // We've finally assembled all the initial directories.  Now let's
170         // iterate the current users and as we go, remove the users from the
171         // starting_dirs hash and thus see which users are obsolete.
172         GList *users = common_user_list_get_users (common_user_list_get_instance ());
173         for (link = users; link; link = link->next)
174         {
175             CommonUser *user = link->data;
176             g_hash_table_remove (manager->priv->starting_dirs, common_user_get_name (user));
177         }
178         g_hash_table_foreach (manager->priv->starting_dirs, delete_unused_user, manager);
179         g_hash_table_destroy (manager->priv->starting_dirs);
180         manager->priv->starting_dirs = NULL;
181
182         g_object_unref (manager);
183     }
184 }
185
186 static void
187 list_user_dirs_cb (GObject *object, GAsyncResult *res, gpointer user_data)
188 {
189     GFile *file = G_FILE (object);
190     SharedDataManager *manager = SHARED_DATA_MANAGER (user_data);
191     GFileEnumerator *enumerator;
192     GError *error = NULL;
193
194     enumerator = g_file_enumerate_children_finish (file, res, &error);
195     if (error)
196         g_warning ("Could not enumerate user data directory %s: %s", USERS_DIR, error->message);
197     g_clear_error (&error);
198     if (!enumerator)
199     {
200         g_object_unref (manager);
201         return;
202     }
203
204     manager->priv->starting_dirs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
205     g_file_enumerator_next_files_async (enumerator, NUM_ENUMERATION_FILES,
206                                         G_PRIORITY_DEFAULT, NULL,
207                                         next_user_dirs_cb, manager);
208 }
209
210 static void
211 user_removed_cb (CommonUserList *list, CommonUser *user, SharedDataManager *manager)
212 {
213     delete_unused_user ((gpointer) common_user_get_name (user), NULL, manager);
214 }
215
216 void
217 shared_data_manager_start (SharedDataManager *manager)
218 {
219     GFile *file;
220
221     /* Grab list of all current directories, so we know if any exist that we no longer need. */
222     file = g_file_new_for_path (USERS_DIR);
223     g_file_enumerate_children_async (file, G_FILE_ATTRIBUTE_STANDARD_NAME,
224                                      G_FILE_QUERY_INFO_NONE,
225                                      G_PRIORITY_DEFAULT, NULL,
226                                      list_user_dirs_cb, g_object_ref (manager));
227     g_object_unref (file);
228
229     /* And listen for user removals. */
230     g_signal_connect (common_user_list_get_instance (), "user-removed", G_CALLBACK (user_removed_cb), manager);
231 }
232
233 static void
234 shared_data_manager_init (SharedDataManager *manager)
235 {
236     struct passwd *greeter_entry;
237
238     manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager, SHARED_DATA_MANAGER_TYPE, SharedDataManagerPrivate);
239
240     /* Grab current greeter-user gid */
241     manager->priv->greeter_user = config_get_string (config_get_instance (), "LightDM", "greeter-user");
242     greeter_entry = getpwnam (manager->priv->greeter_user);
243     if (greeter_entry)
244         manager->priv->greeter_gid = greeter_entry->pw_gid;
245 }
246
247 static void
248 shared_data_manager_dispose (GObject *object)
249 {
250     SharedDataManager *self = SHARED_DATA_MANAGER (object);
251
252     /* Should also cancel outstanding GIO operations, but whatever, let them do their thing. */
253
254     g_signal_handlers_disconnect_by_data (common_user_list_get_instance (), self);
255
256     G_OBJECT_CLASS (shared_data_manager_parent_class)->dispose (object);
257 }
258
259 static void
260 shared_data_manager_finalize (GObject *object)
261 {
262     SharedDataManager *self = SHARED_DATA_MANAGER (object);
263
264     if (self->priv->starting_dirs)
265         g_hash_table_destroy (self->priv->starting_dirs);
266
267     g_free (self->priv->greeter_user);
268
269     G_OBJECT_CLASS (shared_data_manager_parent_class)->finalize (object);
270 }
271
272 static void
273 shared_data_manager_class_init (SharedDataManagerClass *klass)
274 {
275     GObjectClass *object_class = G_OBJECT_CLASS (klass);
276
277     object_class->dispose = shared_data_manager_dispose;
278     object_class->finalize = shared_data_manager_finalize;
279
280     g_type_class_add_private (klass, sizeof (SharedDataManagerPrivate));
281 }