~ubuntu-branches/debian/jessie/systemd/jessie

« back to all changes in this revision

Viewing changes to src/login/logind-user.c

  • Committer: Package Import Robot
  • Author(s): Tollef Fog Heen, Tollef Fog Heen, Michael Biebl
  • Date: 2012-04-03 19:59:17 UTC
  • mfrom: (1.1.10) (6.1.3 experimental)
  • Revision ID: package-import@ubuntu.com-20120403195917-l532urrbg4pkreas
Tags: 44-1
[ Tollef Fog Heen ]
* New upstream version.
  - Backport 3492207: journal: PAGE_SIZE is not known on ppc and other
    archs
  - Backport 5a2a2a1: journal: react with immediate rotation to a couple
    of more errors
  - Backport 693ce21: util: never follow symlinks in rm_rf_children()
    Fixes CVE-2012-1174, closes: #664364
* Drop output message from init-functions hook, it's pointless.
* Only rmdir /lib/init/rw if it exists.
* Explicitly order debian-fixup before sysinit.target to prevent a
  possible race condition with the creation of sockets.  Thanks to
  Michael Biebl for debugging this.
* Always restart the initctl socket on upgrades, to mask sysvinit
  removing it.

[ Michael Biebl ]
* Remove workaround for non-interactive sessions from pam config again.
* Create compat /dev/initctl symlink in case we are upgrading from a system
  running a newer version of sysvinit (using /run/initctl) and sysvinit is
  replaced with systemd-sysv during the upgrade. Closes: #663219
* Install new man pages.
* Build-Depend on valac (>= 0.12) instead of valac-0.12. Closes: #663323

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
 
2
 
 
3
/***
 
4
  This file is part of systemd.
 
5
 
 
6
  Copyright 2011 Lennart Poettering
 
7
 
 
8
  systemd is free software; you can redistribute it and/or modify it
 
9
  under the terms of the GNU General Public License as published by
 
10
  the Free Software Foundation; either version 2 of the License, or
 
11
  (at your option) any later version.
 
12
 
 
13
  systemd is distributed in the hope that it will be useful, but
 
14
  WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 
16
  General Public License for more details.
 
17
 
 
18
  You should have received a copy of the GNU General Public License
 
19
  along with systemd; If not, see <http://www.gnu.org/licenses/>.
 
20
***/
 
21
 
 
22
#include <string.h>
 
23
#include <unistd.h>
 
24
#include <errno.h>
 
25
 
 
26
#include "logind-user.h"
 
27
#include "util.h"
 
28
#include "cgroup-util.h"
 
29
#include "hashmap.h"
 
30
#include "strv.h"
 
31
 
 
32
User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
 
33
        User *u;
 
34
 
 
35
        assert(m);
 
36
        assert(name);
 
37
 
 
38
        u = new0(User, 1);
 
39
        if (!u)
 
40
                return NULL;
 
41
 
 
42
        u->name = strdup(name);
 
43
        if (!u->name) {
 
44
                free(u);
 
45
                return NULL;
 
46
        }
 
47
 
 
48
        if (asprintf(&u->state_file, "/run/systemd/users/%lu", (unsigned long) uid) < 0) {
 
49
                free(u->name);
 
50
                free(u);
 
51
                return NULL;
 
52
        }
 
53
 
 
54
        if (hashmap_put(m->users, ULONG_TO_PTR((unsigned long) uid), u) < 0) {
 
55
                free(u->state_file);
 
56
                free(u->name);
 
57
                free(u);
 
58
                return NULL;
 
59
        }
 
60
 
 
61
        u->manager = m;
 
62
        u->uid = uid;
 
63
        u->gid = gid;
 
64
 
 
65
        return u;
 
66
}
 
67
 
 
68
void user_free(User *u) {
 
69
        assert(u);
 
70
 
 
71
        if (u->in_gc_queue)
 
72
                LIST_REMOVE(User, gc_queue, u->manager->user_gc_queue, u);
 
73
 
 
74
        while (u->sessions)
 
75
                session_free(u->sessions);
 
76
 
 
77
        free(u->cgroup_path);
 
78
 
 
79
        free(u->service);
 
80
        free(u->runtime_path);
 
81
 
 
82
        hashmap_remove(u->manager->users, ULONG_TO_PTR((unsigned long) u->uid));
 
83
 
 
84
        free(u->name);
 
85
        free(u->state_file);
 
86
        free(u);
 
87
}
 
88
 
 
89
int user_save(User *u) {
 
90
        FILE *f;
 
91
        int r;
 
92
        char *temp_path;
 
93
 
 
94
        assert(u);
 
95
        assert(u->state_file);
 
96
 
 
97
        if (!u->started)
 
98
                return 0;
 
99
 
 
100
        r = safe_mkdir("/run/systemd/users", 0755, 0, 0);
 
101
        if (r < 0)
 
102
                goto finish;
 
103
 
 
104
        r = fopen_temporary(u->state_file, &f, &temp_path);
 
105
        if (r < 0)
 
106
                goto finish;
 
107
 
 
108
        fchmod(fileno(f), 0644);
 
109
 
 
110
        fprintf(f,
 
111
                "# This is private data. Do not parse.\n"
 
112
                "NAME=%s\n"
 
113
                "STATE=%s\n",
 
114
                u->name,
 
115
                user_state_to_string(user_get_state(u)));
 
116
 
 
117
        if (u->cgroup_path)
 
118
                fprintf(f,
 
119
                        "CGROUP=%s\n",
 
120
                        u->cgroup_path);
 
121
 
 
122
        if (u->runtime_path)
 
123
                fprintf(f,
 
124
                        "RUNTIME=%s\n",
 
125
                        u->runtime_path);
 
126
 
 
127
        if (u->service)
 
128
                fprintf(f,
 
129
                        "SERVICE=%s\n",
 
130
                        u->service);
 
131
 
 
132
        if (u->display)
 
133
                fprintf(f,
 
134
                        "DISPLAY=%s\n",
 
135
                        u->display->id);
 
136
 
 
137
        if (u->sessions) {
 
138
                Session *i;
 
139
 
 
140
                fputs("SESSIONS=", f);
 
141
                LIST_FOREACH(sessions_by_user, i, u->sessions) {
 
142
                        fprintf(f,
 
143
                                "%s%c",
 
144
                                i->id,
 
145
                                i->sessions_by_user_next ? ' ' : '\n');
 
146
                }
 
147
 
 
148
                fputs("SEATS=", f);
 
149
                LIST_FOREACH(sessions_by_user, i, u->sessions) {
 
150
                        if (i->seat)
 
151
                                fprintf(f,
 
152
                                        "%s%c",
 
153
                                        i->seat->id,
 
154
                                        i->sessions_by_user_next ? ' ' : '\n');
 
155
                }
 
156
 
 
157
                fputs("ACTIVE_SESSIONS=", f);
 
158
                LIST_FOREACH(sessions_by_user, i, u->sessions)
 
159
                        if (session_is_active(i))
 
160
                                fprintf(f,
 
161
                                        "%lu%c",
 
162
                                        (unsigned long) i->user->uid,
 
163
                                        i->sessions_by_user_next ? ' ' : '\n');
 
164
 
 
165
                fputs("ACTIVE_SEATS=", f);
 
166
                LIST_FOREACH(sessions_by_user, i, u->sessions) {
 
167
                        if (session_is_active(i) && i->seat)
 
168
                                fprintf(f,
 
169
                                        "%s%c",
 
170
                                        i->seat->id,
 
171
                                        i->sessions_by_user_next ? ' ' : '\n');
 
172
                }
 
173
        }
 
174
 
 
175
        fflush(f);
 
176
 
 
177
        if (ferror(f) || rename(temp_path, u->state_file) < 0) {
 
178
                r = -errno;
 
179
                unlink(u->state_file);
 
180
                unlink(temp_path);
 
181
        }
 
182
 
 
183
        fclose(f);
 
184
        free(temp_path);
 
185
 
 
186
finish:
 
187
        if (r < 0)
 
188
                log_error("Failed to save user data for %s: %s", u->name, strerror(-r));
 
189
 
 
190
        return r;
 
191
}
 
192
 
 
193
int user_load(User *u) {
 
194
        int r;
 
195
        char *display = NULL;
 
196
        Session *s = NULL;
 
197
 
 
198
        assert(u);
 
199
 
 
200
        r = parse_env_file(u->state_file, NEWLINE,
 
201
                           "CGROUP", &u->cgroup_path,
 
202
                           "RUNTIME", &u->runtime_path,
 
203
                           "SERVICE", &u->service,
 
204
                           "DISPLAY", &display,
 
205
                           NULL);
 
206
        if (r < 0) {
 
207
                free(display);
 
208
 
 
209
                if (r == -ENOENT)
 
210
                        return 0;
 
211
 
 
212
                log_error("Failed to read %s: %s", u->state_file, strerror(-r));
 
213
                return r;
 
214
        }
 
215
 
 
216
        if (display) {
 
217
                s = hashmap_get(u->manager->sessions, display);
 
218
                free(display);
 
219
        }
 
220
 
 
221
        if (s && s->display && display_is_local(s->display))
 
222
                u->display = s;
 
223
 
 
224
        return r;
 
225
}
 
226
 
 
227
static int user_mkdir_runtime_path(User *u) {
 
228
        char *p;
 
229
        int r;
 
230
 
 
231
        assert(u);
 
232
 
 
233
        r = safe_mkdir("/run/user", 0755, 0, 0);
 
234
        if (r < 0) {
 
235
                log_error("Failed to create /run/user: %s", strerror(-r));
 
236
                return r;
 
237
        }
 
238
 
 
239
        if (!u->runtime_path) {
 
240
                p = strappend("/run/user/", u->name);
 
241
 
 
242
                if (!p) {
 
243
                        log_error("Out of memory");
 
244
                        return -ENOMEM;
 
245
                }
 
246
        } else
 
247
                p = u->runtime_path;
 
248
 
 
249
        r = safe_mkdir(p, 0700, u->uid, u->gid);
 
250
        if (r < 0) {
 
251
                log_error("Failed to create runtime directory %s: %s", p, strerror(-r));
 
252
                free(p);
 
253
                u->runtime_path = NULL;
 
254
                return r;
 
255
        }
 
256
 
 
257
        u->runtime_path = p;
 
258
        return 0;
 
259
}
 
260
 
 
261
static int user_create_cgroup(User *u) {
 
262
        char **k;
 
263
        char *p;
 
264
        int r;
 
265
 
 
266
        assert(u);
 
267
 
 
268
        if (!u->cgroup_path) {
 
269
                if (asprintf(&p, "%s/%s", u->manager->cgroup_path, u->name) < 0) {
 
270
                        log_error("Out of memory");
 
271
                        return -ENOMEM;
 
272
                }
 
273
        } else
 
274
                p = u->cgroup_path;
 
275
 
 
276
        r = cg_create(SYSTEMD_CGROUP_CONTROLLER, p);
 
277
        if (r < 0) {
 
278
                log_error("Failed to create cgroup "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
 
279
                free(p);
 
280
                u->cgroup_path = NULL;
 
281
                return r;
 
282
        }
 
283
 
 
284
        u->cgroup_path = p;
 
285
 
 
286
        STRV_FOREACH(k, u->manager->controllers) {
 
287
 
 
288
                if (strv_contains(u->manager->reset_controllers, *k))
 
289
                        continue;
 
290
 
 
291
                r = cg_create(*k, p);
 
292
                if (r < 0)
 
293
                        log_warning("Failed to create cgroup %s:%s: %s", *k, p, strerror(-r));
 
294
        }
 
295
 
 
296
        return 0;
 
297
}
 
298
 
 
299
static int user_start_service(User *u) {
 
300
        assert(u);
 
301
 
 
302
        /* FIXME: Fill me in later ... */
 
303
 
 
304
        return 0;
 
305
}
 
306
 
 
307
int user_start(User *u) {
 
308
        int r;
 
309
 
 
310
        assert(u);
 
311
 
 
312
        if (u->started)
 
313
                return 0;
 
314
 
 
315
        log_debug("New user %s logged in.", u->name);
 
316
 
 
317
        /* Make XDG_RUNTIME_DIR */
 
318
        r = user_mkdir_runtime_path(u);
 
319
        if (r < 0)
 
320
                return r;
 
321
 
 
322
        /* Create cgroup */
 
323
        r = user_create_cgroup(u);
 
324
        if (r < 0)
 
325
                return r;
 
326
 
 
327
        /* Spawn user systemd */
 
328
        r = user_start_service(u);
 
329
        if (r < 0)
 
330
                return r;
 
331
 
 
332
        dual_timestamp_get(&u->timestamp);
 
333
 
 
334
        u->started = true;
 
335
 
 
336
        /* Save new user data */
 
337
        user_save(u);
 
338
 
 
339
        user_send_signal(u, true);
 
340
 
 
341
        return 0;
 
342
}
 
343
 
 
344
static int user_stop_service(User *u) {
 
345
        assert(u);
 
346
 
 
347
        if (!u->service)
 
348
                return 0;
 
349
 
 
350
        return 0;
 
351
}
 
352
 
 
353
static int user_shall_kill(User *u) {
 
354
        assert(u);
 
355
 
 
356
        if (!u->manager->kill_user_processes)
 
357
                return false;
 
358
 
 
359
        if (strv_contains(u->manager->kill_exclude_users, u->name))
 
360
                return false;
 
361
 
 
362
        if (strv_isempty(u->manager->kill_only_users))
 
363
                return true;
 
364
 
 
365
        return strv_contains(u->manager->kill_only_users, u->name);
 
366
}
 
367
 
 
368
static int user_terminate_cgroup(User *u) {
 
369
        int r;
 
370
        char **k;
 
371
 
 
372
        assert(u);
 
373
 
 
374
        if (!u->cgroup_path)
 
375
                return 0;
 
376
 
 
377
        cg_trim(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
 
378
 
 
379
        if (user_shall_kill(u)) {
 
380
 
 
381
                r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
 
382
                if (r < 0)
 
383
                        log_error("Failed to kill user cgroup: %s", strerror(-r));
 
384
        } else {
 
385
 
 
386
                r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
 
387
                if (r < 0)
 
388
                        log_error("Failed to check user cgroup: %s", strerror(-r));
 
389
                else if (r > 0) {
 
390
                        r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
 
391
                        if (r < 0)
 
392
                                log_error("Failed to delete user cgroup: %s", strerror(-r));
 
393
                } else
 
394
                        r = -EBUSY;
 
395
        }
 
396
 
 
397
        STRV_FOREACH(k, u->manager->controllers)
 
398
                cg_trim(*k, u->cgroup_path, true);
 
399
 
 
400
        free(u->cgroup_path);
 
401
        u->cgroup_path = NULL;
 
402
 
 
403
        return r;
 
404
}
 
405
 
 
406
static int user_remove_runtime_path(User *u) {
 
407
        int r;
 
408
 
 
409
        assert(u);
 
410
 
 
411
        if (!u->runtime_path)
 
412
                return 0;
 
413
 
 
414
        r = rm_rf(u->runtime_path, false, true, false);
 
415
        if (r < 0)
 
416
                log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r));
 
417
 
 
418
        free(u->runtime_path);
 
419
        u->runtime_path = NULL;
 
420
 
 
421
        return r;
 
422
}
 
423
 
 
424
int user_stop(User *u) {
 
425
        Session *s;
 
426
        int r = 0, k;
 
427
        assert(u);
 
428
 
 
429
        if (u->started)
 
430
                log_debug("User %s logged out.", u->name);
 
431
 
 
432
        LIST_FOREACH(sessions_by_user, s, u->sessions) {
 
433
                k = session_stop(s);
 
434
                if (k < 0)
 
435
                        r = k;
 
436
        }
 
437
 
 
438
        /* Kill systemd */
 
439
        k = user_stop_service(u);
 
440
        if (k < 0)
 
441
                r = k;
 
442
 
 
443
        /* Kill cgroup */
 
444
        k = user_terminate_cgroup(u);
 
445
        if (k < 0)
 
446
                r = k;
 
447
 
 
448
        /* Kill XDG_RUNTIME_DIR */
 
449
        k = user_remove_runtime_path(u);
 
450
        if (k < 0)
 
451
                r = k;
 
452
 
 
453
        unlink(u->state_file);
 
454
        user_add_to_gc_queue(u);
 
455
 
 
456
        if (u->started)
 
457
                user_send_signal(u, false);
 
458
 
 
459
        u->started = false;
 
460
 
 
461
        return r;
 
462
}
 
463
 
 
464
int user_get_idle_hint(User *u, dual_timestamp *t) {
 
465
        Session *s;
 
466
        bool idle_hint = true;
 
467
        dual_timestamp ts = { 0, 0 };
 
468
 
 
469
        assert(u);
 
470
 
 
471
        LIST_FOREACH(sessions_by_user, s, u->sessions) {
 
472
                dual_timestamp k;
 
473
                int ih;
 
474
 
 
475
                ih = session_get_idle_hint(s, &k);
 
476
                if (ih < 0)
 
477
                        return ih;
 
478
 
 
479
                if (!ih) {
 
480
                        if (!idle_hint) {
 
481
                                if (k.monotonic < ts.monotonic)
 
482
                                        ts = k;
 
483
                        } else {
 
484
                                idle_hint = false;
 
485
                                ts = k;
 
486
                        }
 
487
                } else if (idle_hint) {
 
488
 
 
489
                        if (k.monotonic > ts.monotonic)
 
490
                                ts = k;
 
491
                }
 
492
        }
 
493
 
 
494
        if (t)
 
495
                *t = ts;
 
496
 
 
497
        return idle_hint;
 
498
}
 
499
 
 
500
int user_check_gc(User *u, bool drop_not_started) {
 
501
        int r;
 
502
        char *p;
 
503
 
 
504
        assert(u);
 
505
 
 
506
        if (drop_not_started && !u->started)
 
507
                return 0;
 
508
 
 
509
        if (u->sessions)
 
510
                return 1;
 
511
 
 
512
        if (asprintf(&p, "/var/lib/systemd/linger/%s", u->name) < 0)
 
513
                return -ENOMEM;
 
514
 
 
515
        r = access(p, F_OK) >= 0;
 
516
        free(p);
 
517
 
 
518
        if (r > 0)
 
519
                return 1;
 
520
 
 
521
        if (u->cgroup_path) {
 
522
                r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
 
523
                if (r < 0)
 
524
                        return r;
 
525
 
 
526
                if (r <= 0)
 
527
                        return 1;
 
528
        }
 
529
 
 
530
        return 0;
 
531
}
 
532
 
 
533
void user_add_to_gc_queue(User *u) {
 
534
        assert(u);
 
535
 
 
536
        if (u->in_gc_queue)
 
537
                return;
 
538
 
 
539
        LIST_PREPEND(User, gc_queue, u->manager->user_gc_queue, u);
 
540
        u->in_gc_queue = true;
 
541
}
 
542
 
 
543
UserState user_get_state(User *u) {
 
544
        Session *i;
 
545
 
 
546
        assert(u);
 
547
 
 
548
        if (!u->sessions)
 
549
                return USER_LINGERING;
 
550
 
 
551
        LIST_FOREACH(sessions_by_user, i, u->sessions)
 
552
                if (session_is_active(i))
 
553
                        return USER_ACTIVE;
 
554
 
 
555
        return USER_ONLINE;
 
556
}
 
557
 
 
558
int user_kill(User *u, int signo) {
 
559
        int r = 0, q;
 
560
        Set *pid_set = NULL;
 
561
 
 
562
        assert(u);
 
563
 
 
564
        if (!u->cgroup_path)
 
565
                return -ESRCH;
 
566
 
 
567
        pid_set = set_new(trivial_hash_func, trivial_compare_func);
 
568
        if (!pid_set)
 
569
                return -ENOMEM;
 
570
 
 
571
        q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set);
 
572
        if (q < 0)
 
573
                if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
 
574
                        r = q;
 
575
 
 
576
        if (pid_set)
 
577
                set_free(pid_set);
 
578
 
 
579
        return r;
 
580
}
 
581
 
 
582
static const char* const user_state_table[_USER_STATE_MAX] = {
 
583
        [USER_OFFLINE] = "offline",
 
584
        [USER_LINGERING] = "lingering",
 
585
        [USER_ONLINE] = "online",
 
586
        [USER_ACTIVE] = "active"
 
587
};
 
588
 
 
589
DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);