~lightdm-team/lightdm/1.4

« back to all changes in this revision

Viewing changes to src/user-manager.c

  • Committer: Robert Ancell
  • Date: 2011-05-05 06:11:16 UTC
  • Revision ID: robert.ancell@canonical.com-20110505061116-4nhrdw6k0huve3ek
Move user manager into liblightdm

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Copyright (C) 2010 Robert Ancell.
3
 
 * Author: Robert Ancell <robert.ancell@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 <string.h>
13
 
#include <pwd.h>
14
 
#include <errno.h>
15
 
#include <gio/gio.h>
16
 
 
17
 
#include "user-manager.h"
18
 
 
19
 
enum {
20
 
    USER_ADDED,
21
 
    USER_CHANGED,
22
 
    USER_REMOVED,
23
 
    LAST_SIGNAL
24
 
};
25
 
static guint signals[LAST_SIGNAL] = { 0 };
26
 
 
27
 
struct UserManagerPrivate
28
 
{
29
 
    /* Configuration file */
30
 
    GKeyFile *config;
31
 
  
32
 
    /* File monitor for password file */
33
 
    GFileMonitor *passwd_monitor;
34
 
 
35
 
    /* TRUE if have scanned users */
36
 
    gboolean have_users;
37
 
 
38
 
    /* List of users */
39
 
    GList *users;
40
 
};
41
 
 
42
 
G_DEFINE_TYPE (UserManager, user_manager, G_TYPE_OBJECT);
43
 
 
44
 
UserManager *
45
 
user_manager_new (GKeyFile *config_file)
46
 
{
47
 
    UserManager *manager;
48
 
 
49
 
    manager = g_object_new (USER_MANAGER_TYPE, NULL);
50
 
    manager->priv->config = config_file;
51
 
 
52
 
    return manager;
53
 
}
54
 
 
55
 
static gint
56
 
compare_user (gconstpointer a, gconstpointer b)
57
 
{
58
 
    const UserInfo *user_a = a, *user_b = b;
59
 
    const gchar *name_a, *name_b;
60
 
    name_a = user_a->real_name ? user_a->real_name : user_a->name;
61
 
    name_b = user_b->real_name ? user_b->real_name : user_b->name;
62
 
    return strcmp (name_a, name_b);
63
 
}
64
 
 
65
 
static void
66
 
load_users (UserManager *manager)
67
 
{
68
 
    gchar **hidden_users, **hidden_shells;
69
 
    gchar *value;
70
 
    gint minimum_uid;
71
 
    GList *users = NULL, *old_users, *new_users = NULL, *changed_users = NULL, *link;
72
 
 
73
 
    if (g_key_file_has_key (manager->priv->config, "UserManager", "minimum-uid", NULL))
74
 
        minimum_uid = g_key_file_get_integer (manager->priv->config, "UserManager", "minimum-uid", NULL);
75
 
    else
76
 
        minimum_uid = 500;
77
 
 
78
 
    value = g_key_file_get_string (manager->priv->config, "UserManager", "hidden-users", NULL);
79
 
    if (!value)
80
 
        value = g_strdup ("nobody nobody4 noaccess");
81
 
    hidden_users = g_strsplit (value, " ", -1);
82
 
    g_free (value);
83
 
 
84
 
    value = g_key_file_get_string (manager->priv->config, "UserManager", "hidden-shells", NULL);
85
 
    if (!value)
86
 
        value = g_strdup ("/bin/false /usr/sbin/nologin");
87
 
    hidden_shells = g_strsplit (value, " ", -1);
88
 
    g_free (value);
89
 
 
90
 
    setpwent ();
91
 
 
92
 
    while (TRUE)
93
 
    {
94
 
        struct passwd *entry;
95
 
        UserInfo *user;
96
 
        char **tokens;
97
 
        gchar *image_path;
98
 
        int i;
99
 
 
100
 
        errno = 0;
101
 
        entry = getpwent ();
102
 
        if (!entry)
103
 
            break;
104
 
 
105
 
        /* Ignore system users */
106
 
        if (entry->pw_uid < minimum_uid)
107
 
            continue;
108
 
 
109
 
        /* Ignore users disabled by shell */
110
 
        if (entry->pw_shell)
111
 
        {
112
 
            for (i = 0; hidden_shells[i] && strcmp (entry->pw_shell, hidden_shells[i]) != 0; i++);
113
 
            if (hidden_shells[i])
114
 
                continue;
115
 
        }
116
 
 
117
 
        /* Ignore certain users */
118
 
        for (i = 0; hidden_users[i] && strcmp (entry->pw_name, hidden_users[i]) != 0; i++);
119
 
        if (hidden_users[i])
120
 
            continue;
121
 
 
122
 
        user = g_malloc0 (sizeof (UserInfo));
123
 
        user->name = g_strdup (entry->pw_name);
124
 
 
125
 
        tokens = g_strsplit (entry->pw_gecos, ",", -1);
126
 
        if (tokens[0] != NULL && tokens[0][0] != '\0')
127
 
            user->real_name = g_strdup (tokens[0]);
128
 
        else
129
 
            user->real_name = NULL;
130
 
        g_strfreev (tokens);
131
 
      
132
 
        user->home_dir = g_strdup (entry->pw_dir);
133
 
 
134
 
        image_path = g_build_filename (user->home_dir, ".face", NULL);
135
 
        if (!g_file_test (image_path, G_FILE_TEST_EXISTS))
136
 
        {
137
 
            g_free (image_path);
138
 
            image_path = g_build_filename (user->home_dir, ".face.icon", NULL);
139
 
            if (!g_file_test (image_path, G_FILE_TEST_EXISTS))
140
 
            {
141
 
                g_free (image_path);
142
 
                image_path = NULL;
143
 
            }
144
 
        }
145
 
        if (image_path)
146
 
            user->image = g_filename_to_uri (image_path, NULL, NULL);
147
 
        else
148
 
            user->image = g_strdup ("");
149
 
        g_free (image_path);
150
 
 
151
 
        /* Update existing users if have them */
152
 
        for (link = manager->priv->users; link; link = link->next)
153
 
        {
154
 
            UserInfo *info = link->data;
155
 
            if (strcmp (info->name, user->name) == 0)
156
 
            {
157
 
                if (strcmp (info->real_name, user->real_name) != 0 ||
158
 
                    strcmp (info->image, user->image) != 0 ||
159
 
                    strcmp (info->home_dir, user->home_dir) != 0 ||
160
 
                    info->logged_in != user->logged_in)
161
 
                {
162
 
                    g_free (info->real_name);
163
 
                    g_free (info->image);
164
 
                    g_free (info->home_dir);
165
 
                    info->real_name = user->real_name;
166
 
                    info->image = user->image;
167
 
                    info->home_dir = user->home_dir;
168
 
                    info->logged_in = user->logged_in;
169
 
                    g_free (user);
170
 
                    user = info;
171
 
                    changed_users = g_list_insert_sorted (changed_users, user, compare_user);
172
 
                }
173
 
                else
174
 
                {
175
 
                    g_free (user->real_name);
176
 
                    g_free (user->image);
177
 
                    g_free (user->home_dir);
178
 
                    g_free (user);
179
 
                    user = info;
180
 
                }
181
 
                break;
182
 
            }
183
 
        }
184
 
        if (!link)
185
 
        {
186
 
            /* Only notify once we have loaded the user list */
187
 
            if (manager->priv->have_users)
188
 
                new_users = g_list_insert_sorted (new_users, user, compare_user);
189
 
        }
190
 
        users = g_list_insert_sorted (users, user, compare_user);
191
 
    }
192
 
    g_strfreev (hidden_users);
193
 
    g_strfreev (hidden_shells);
194
 
 
195
 
    if (errno != 0)
196
 
        g_warning ("Failed to read password database: %s", strerror (errno));
197
 
 
198
 
    endpwent ();
199
 
 
200
 
    /* Use new user list */
201
 
    old_users = manager->priv->users;
202
 
    manager->priv->users = users;
203
 
 
204
 
    /* Notify of changes */
205
 
    for (link = new_users; link; link = link->next)
206
 
    {
207
 
        UserInfo *info = link->data;
208
 
        g_debug ("User %s added", info->name);
209
 
        g_signal_emit (manager, signals[USER_ADDED], 0, info);
210
 
    }
211
 
    g_list_free (new_users);
212
 
    for (link = changed_users; link; link = link->next)
213
 
    {
214
 
        UserInfo *info = link->data;
215
 
        g_debug ("User %s changed", info->name);
216
 
        g_signal_emit (manager, signals[USER_CHANGED], 0, info);
217
 
    }
218
 
    g_list_free (changed_users);
219
 
    for (link = old_users; link; link = link->next)
220
 
    {
221
 
        GList *new_link;
222
 
 
223
 
        /* See if this user is in the current list */
224
 
        for (new_link = manager->priv->users; new_link; new_link = new_link->next)
225
 
        {
226
 
            if (new_link->data == link->data)
227
 
                break;
228
 
        }
229
 
 
230
 
        if (!new_link)
231
 
        {
232
 
            UserInfo *info = link->data;
233
 
            g_debug ("User %s removed", info->name);
234
 
            g_signal_emit (manager, signals[USER_REMOVED], 0, info);
235
 
            g_free (info->name);
236
 
            g_free (info->real_name);
237
 
            g_free (info->image);
238
 
            g_free (info->home_dir);
239
 
            g_free (info);
240
 
        }
241
 
    }
242
 
    g_list_free (old_users);
243
 
}
244
 
 
245
 
static void
246
 
passwd_changed_cb (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, UserManager *manager)
247
 
{
248
 
    if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
249
 
    {
250
 
        g_debug ("%s changed, reloading user list", g_file_get_path (file));
251
 
        load_users (manager);
252
 
    }
253
 
}
254
 
 
255
 
static void
256
 
update_users (UserManager *manager)
257
 
{
258
 
    GFile *passwd_file;
259
 
    GError *error = NULL;
260
 
 
261
 
    if (manager->priv->have_users)
262
 
        return;
263
 
 
264
 
    /* User listing is disabled */
265
 
    if (g_key_file_has_key (manager->priv->config, "UserManager", "load-users", NULL) &&
266
 
        !g_key_file_get_boolean (manager->priv->config, "UserManager", "load-users", NULL))
267
 
    {
268
 
        manager->priv->have_users = TRUE;
269
 
        return;
270
 
    }
271
 
 
272
 
    load_users (manager);
273
 
 
274
 
    /* Watch for changes to user list */
275
 
    passwd_file = g_file_new_for_path ("/etc/passwd");
276
 
    manager->priv->passwd_monitor = g_file_monitor (passwd_file, G_FILE_MONITOR_NONE, NULL, &error);
277
 
    g_object_unref (passwd_file);
278
 
    if (!manager->priv->passwd_monitor)
279
 
        g_warning ("Error monitoring /etc/passwd: %s", error->message);
280
 
    else
281
 
        g_signal_connect (manager->priv->passwd_monitor, "changed", G_CALLBACK (passwd_changed_cb), manager);
282
 
    g_clear_error (&error);
283
 
 
284
 
    manager->priv->have_users = TRUE;
285
 
}
286
 
 
287
 
gint
288
 
user_manager_get_num_users (UserManager *manager)
289
 
{
290
 
    update_users (manager);
291
 
    return g_list_length (manager->priv->users);
292
 
}
293
 
 
294
 
const UserInfo *
295
 
user_manager_get_user (UserManager *manager, const gchar *username)
296
 
{
297
 
    GList *link;
298
 
 
299
 
    update_users (manager);
300
 
 
301
 
    for (link = manager->priv->users; link; link = link->next)
302
 
    {
303
 
        UserInfo *info = link->data;
304
 
        if (strcmp (info->name, username) == 0)
305
 
            return info;
306
 
    }
307
 
 
308
 
    return NULL;
309
 
}
310
 
 
311
 
GList *
312
 
user_manager_get_users (UserManager *manager)
313
 
{
314
 
    update_users (manager);
315
 
    return manager->priv->users;
316
 
}
317
 
 
318
 
gboolean
319
 
user_manager_get_user_defaults (UserManager *manager, gchar *username, gchar **language, gchar **layout, gchar **session)
320
 
{
321
 
    const UserInfo *info;
322
 
    GKeyFile *dmrc_file;
323
 
    gboolean have_dmrc;
324
 
    gchar *path;
325
 
 
326
 
    info = user_manager_get_user (manager, username);
327
 
    if (!info)
328
 
    {
329
 
        g_debug ("Unable to get user defaults, user %s does not exist", username);
330
 
        return FALSE;
331
 
    }
332
 
 
333
 
    dmrc_file = g_key_file_new ();
334
 
    g_key_file_set_string (dmrc_file, "Desktop", "Language", "");
335
 
    g_key_file_set_string (dmrc_file, "Desktop", "Layout", "");
336
 
    g_key_file_set_string (dmrc_file, "Desktop", "Session", "");
337
 
 
338
 
    /* Load the users login settings (~/.dmrc) */  
339
 
    path = g_build_filename (info->home_dir, ".dmrc", NULL);
340
 
    have_dmrc = g_key_file_load_from_file (dmrc_file, path, G_KEY_FILE_NONE, NULL);
341
 
    g_free (path);
342
 
 
343
 
    /* If no .dmrc, then load from the cache */
344
 
    if (!have_dmrc)
345
 
    {
346
 
        gchar *filename;
347
 
 
348
 
        filename = g_strdup_printf ("%s.dmrc", username);
349
 
        path = g_build_filename (CACHE_DIR, "dmrc", filename, NULL);
350
 
        g_free (filename);
351
 
        have_dmrc = g_key_file_load_from_file (dmrc_file, path, G_KEY_FILE_NONE, NULL);
352
 
        g_free (path);
353
 
    }
354
 
 
355
 
    *language = g_key_file_get_string (dmrc_file, "Desktop", "Language", NULL);
356
 
    *layout = g_key_file_get_string (dmrc_file, "Desktop", "Layout", NULL);
357
 
    *session = g_key_file_get_string (dmrc_file, "Desktop", "Session", NULL);
358
 
 
359
 
    g_key_file_free (dmrc_file);
360
 
 
361
 
    return TRUE;
362
 
}
363
 
 
364
 
 
365
 
static void
366
 
user_manager_init (UserManager *manager)
367
 
{
368
 
    manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager, USER_MANAGER_TYPE, UserManagerPrivate);
369
 
}
370
 
 
371
 
static void
372
 
user_manager_class_init (UserManagerClass *klass)
373
 
{
374
 
    g_type_class_add_private (klass, sizeof (UserManagerPrivate));
375
 
 
376
 
    signals[USER_ADDED] =
377
 
        g_signal_new ("user-added",
378
 
                      G_TYPE_FROM_CLASS (klass),
379
 
                      G_SIGNAL_RUN_LAST,
380
 
                      G_STRUCT_OFFSET (UserManagerClass, user_added),
381
 
                      NULL, NULL,
382
 
                      g_cclosure_marshal_VOID__POINTER,
383
 
                      G_TYPE_NONE, 1, G_TYPE_POINTER);
384
 
    signals[USER_CHANGED] =
385
 
        g_signal_new ("user-changed",
386
 
                      G_TYPE_FROM_CLASS (klass),
387
 
                      G_SIGNAL_RUN_LAST,
388
 
                      G_STRUCT_OFFSET (UserManagerClass, user_changed),
389
 
                      NULL, NULL,
390
 
                      g_cclosure_marshal_VOID__POINTER,
391
 
                      G_TYPE_NONE, 1, G_TYPE_POINTER);
392
 
    signals[USER_REMOVED] =
393
 
        g_signal_new ("user-removed",
394
 
                      G_TYPE_FROM_CLASS (klass),
395
 
                      G_SIGNAL_RUN_LAST,
396
 
                      G_STRUCT_OFFSET (UserManagerClass, user_removed),
397
 
                      NULL, NULL,
398
 
                      g_cclosure_marshal_VOID__POINTER,
399
 
                      G_TYPE_NONE, 1, G_TYPE_POINTER);
400
 
}