Line data Source code
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 Lesser General Public License as published by
10 : the Free Software Foundation; either version 2.1 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 : Lesser General Public License for more details.
17 :
18 : You should have received a copy of the GNU Lesser General Public License
19 : along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 : ***/
21 :
22 : #include <sys/mount.h>
23 : #include <string.h>
24 : #include <unistd.h>
25 : #include <errno.h>
26 :
27 : #include "util.h"
28 : #include "mkdir.h"
29 : #include "rm-rf.h"
30 : #include "hashmap.h"
31 : #include "fileio.h"
32 : #include "path-util.h"
33 : #include "special.h"
34 : #include "unit-name.h"
35 : #include "bus-util.h"
36 : #include "bus-error.h"
37 : #include "conf-parser.h"
38 : #include "clean-ipc.h"
39 : #include "smack-util.h"
40 : #include "formats-util.h"
41 : #include "label.h"
42 : #include "logind-user.h"
43 :
44 0 : User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
45 : User *u;
46 :
47 0 : assert(m);
48 0 : assert(name);
49 :
50 0 : u = new0(User, 1);
51 0 : if (!u)
52 0 : return NULL;
53 :
54 0 : u->name = strdup(name);
55 0 : if (!u->name)
56 0 : goto fail;
57 :
58 0 : if (asprintf(&u->state_file, "/run/systemd/users/"UID_FMT, uid) < 0)
59 0 : goto fail;
60 :
61 0 : if (hashmap_put(m->users, UID_TO_PTR(uid), u) < 0)
62 0 : goto fail;
63 :
64 0 : u->manager = m;
65 0 : u->uid = uid;
66 0 : u->gid = gid;
67 :
68 0 : return u;
69 :
70 : fail:
71 0 : free(u->state_file);
72 0 : free(u->name);
73 0 : free(u);
74 :
75 0 : return NULL;
76 : }
77 :
78 0 : void user_free(User *u) {
79 0 : assert(u);
80 :
81 0 : if (u->in_gc_queue)
82 0 : LIST_REMOVE(gc_queue, u->manager->user_gc_queue, u);
83 :
84 0 : while (u->sessions)
85 0 : session_free(u->sessions);
86 :
87 0 : if (u->slice) {
88 0 : hashmap_remove(u->manager->user_units, u->slice);
89 0 : free(u->slice);
90 : }
91 :
92 0 : if (u->service) {
93 0 : hashmap_remove(u->manager->user_units, u->service);
94 0 : free(u->service);
95 : }
96 :
97 0 : free(u->slice_job);
98 0 : free(u->service_job);
99 :
100 0 : free(u->runtime_path);
101 :
102 0 : hashmap_remove(u->manager->users, UID_TO_PTR(u->uid));
103 :
104 0 : free(u->name);
105 0 : free(u->state_file);
106 0 : free(u);
107 0 : }
108 :
109 0 : static int user_save_internal(User *u) {
110 0 : _cleanup_free_ char *temp_path = NULL;
111 0 : _cleanup_fclose_ FILE *f = NULL;
112 : int r;
113 :
114 0 : assert(u);
115 0 : assert(u->state_file);
116 :
117 0 : r = mkdir_safe_label("/run/systemd/users", 0755, 0, 0);
118 0 : if (r < 0)
119 0 : goto finish;
120 :
121 0 : r = fopen_temporary(u->state_file, &f, &temp_path);
122 0 : if (r < 0)
123 0 : goto finish;
124 :
125 0 : fchmod(fileno(f), 0644);
126 :
127 0 : fprintf(f,
128 : "# This is private data. Do not parse.\n"
129 : "NAME=%s\n"
130 : "STATE=%s\n",
131 : u->name,
132 : user_state_to_string(user_get_state(u)));
133 :
134 0 : if (u->runtime_path)
135 0 : fprintf(f, "RUNTIME=%s\n", u->runtime_path);
136 :
137 0 : if (u->service)
138 0 : fprintf(f, "SERVICE=%s\n", u->service);
139 0 : if (u->service_job)
140 0 : fprintf(f, "SERVICE_JOB=%s\n", u->service_job);
141 :
142 0 : if (u->slice)
143 0 : fprintf(f, "SLICE=%s\n", u->slice);
144 0 : if (u->slice_job)
145 0 : fprintf(f, "SLICE_JOB=%s\n", u->slice_job);
146 :
147 0 : if (u->display)
148 0 : fprintf(f, "DISPLAY=%s\n", u->display->id);
149 :
150 0 : if (dual_timestamp_is_set(&u->timestamp))
151 0 : fprintf(f,
152 : "REALTIME="USEC_FMT"\n"
153 : "MONOTONIC="USEC_FMT"\n",
154 : u->timestamp.realtime,
155 : u->timestamp.monotonic);
156 :
157 0 : if (u->sessions) {
158 : Session *i;
159 : bool first;
160 :
161 0 : fputs("SESSIONS=", f);
162 0 : first = true;
163 0 : LIST_FOREACH(sessions_by_user, i, u->sessions) {
164 0 : if (first)
165 0 : first = false;
166 : else
167 0 : fputc(' ', f);
168 :
169 0 : fputs(i->id, f);
170 : }
171 :
172 0 : fputs("\nSEATS=", f);
173 0 : first = true;
174 0 : LIST_FOREACH(sessions_by_user, i, u->sessions) {
175 0 : if (!i->seat)
176 0 : continue;
177 :
178 0 : if (first)
179 0 : first = false;
180 : else
181 0 : fputc(' ', f);
182 :
183 0 : fputs(i->seat->id, f);
184 : }
185 :
186 0 : fputs("\nACTIVE_SESSIONS=", f);
187 0 : first = true;
188 0 : LIST_FOREACH(sessions_by_user, i, u->sessions) {
189 0 : if (!session_is_active(i))
190 0 : continue;
191 :
192 0 : if (first)
193 0 : first = false;
194 : else
195 0 : fputc(' ', f);
196 :
197 0 : fputs(i->id, f);
198 : }
199 :
200 0 : fputs("\nONLINE_SESSIONS=", f);
201 0 : first = true;
202 0 : LIST_FOREACH(sessions_by_user, i, u->sessions) {
203 0 : if (session_get_state(i) == SESSION_CLOSING)
204 0 : continue;
205 :
206 0 : if (first)
207 0 : first = false;
208 : else
209 0 : fputc(' ', f);
210 :
211 0 : fputs(i->id, f);
212 : }
213 :
214 0 : fputs("\nACTIVE_SEATS=", f);
215 0 : first = true;
216 0 : LIST_FOREACH(sessions_by_user, i, u->sessions) {
217 0 : if (!session_is_active(i) || !i->seat)
218 0 : continue;
219 :
220 0 : if (first)
221 0 : first = false;
222 : else
223 0 : fputc(' ', f);
224 :
225 0 : fputs(i->seat->id, f);
226 : }
227 :
228 0 : fputs("\nONLINE_SEATS=", f);
229 0 : first = true;
230 0 : LIST_FOREACH(sessions_by_user, i, u->sessions) {
231 0 : if (session_get_state(i) == SESSION_CLOSING || !i->seat)
232 0 : continue;
233 :
234 0 : if (first)
235 0 : first = false;
236 : else
237 0 : fputc(' ', f);
238 :
239 0 : fputs(i->seat->id, f);
240 : }
241 0 : fputc('\n', f);
242 : }
243 :
244 0 : fflush(f);
245 :
246 0 : if (ferror(f) || rename(temp_path, u->state_file) < 0) {
247 0 : r = -errno;
248 0 : unlink(u->state_file);
249 0 : unlink(temp_path);
250 : }
251 :
252 : finish:
253 0 : if (r < 0)
254 0 : log_error_errno(r, "Failed to save user data %s: %m", u->state_file);
255 :
256 0 : return r;
257 : }
258 :
259 0 : int user_save(User *u) {
260 0 : assert(u);
261 :
262 0 : if (!u->started)
263 0 : return 0;
264 :
265 0 : return user_save_internal (u);
266 : }
267 :
268 0 : int user_load(User *u) {
269 0 : _cleanup_free_ char *display = NULL, *realtime = NULL, *monotonic = NULL;
270 0 : Session *s = NULL;
271 : int r;
272 :
273 0 : assert(u);
274 :
275 0 : r = parse_env_file(u->state_file, NEWLINE,
276 : "RUNTIME", &u->runtime_path,
277 : "SERVICE", &u->service,
278 : "SERVICE_JOB", &u->service_job,
279 : "SLICE", &u->slice,
280 : "SLICE_JOB", &u->slice_job,
281 : "DISPLAY", &display,
282 : "REALTIME", &realtime,
283 : "MONOTONIC", &monotonic,
284 : NULL);
285 0 : if (r < 0) {
286 0 : if (r == -ENOENT)
287 0 : return 0;
288 :
289 0 : log_error_errno(r, "Failed to read %s: %m", u->state_file);
290 0 : return r;
291 : }
292 :
293 0 : if (display)
294 0 : s = hashmap_get(u->manager->sessions, display);
295 :
296 0 : if (s && s->display && display_is_local(s->display))
297 0 : u->display = s;
298 :
299 0 : if (realtime) {
300 : unsigned long long l;
301 0 : if (sscanf(realtime, "%llu", &l) > 0)
302 0 : u->timestamp.realtime = l;
303 : }
304 :
305 0 : if (monotonic) {
306 : unsigned long long l;
307 0 : if (sscanf(monotonic, "%llu", &l) > 0)
308 0 : u->timestamp.monotonic = l;
309 : }
310 :
311 0 : return r;
312 : }
313 :
314 0 : static int user_mkdir_runtime_path(User *u) {
315 : char *p;
316 : int r;
317 :
318 0 : assert(u);
319 :
320 0 : r = mkdir_safe_label("/run/user", 0755, 0, 0);
321 0 : if (r < 0)
322 0 : return log_error_errno(r, "Failed to create /run/user: %m");
323 :
324 0 : if (!u->runtime_path) {
325 0 : if (asprintf(&p, "/run/user/" UID_FMT, u->uid) < 0)
326 0 : return log_oom();
327 : } else
328 0 : p = u->runtime_path;
329 :
330 0 : if (path_is_mount_point(p, 0) <= 0) {
331 0 : _cleanup_free_ char *t = NULL;
332 :
333 0 : (void) mkdir_label(p, 0700);
334 :
335 0 : if (mac_smack_use())
336 0 : r = asprintf(&t, "mode=0700,smackfsroot=*,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
337 : else
338 0 : r = asprintf(&t, "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
339 0 : if (r < 0) {
340 0 : r = log_oom();
341 0 : goto fail;
342 : }
343 :
344 0 : r = mount("tmpfs", p, "tmpfs", MS_NODEV|MS_NOSUID, t);
345 0 : if (r < 0) {
346 0 : if (errno != EPERM) {
347 0 : r = log_error_errno(errno, "Failed to mount per-user tmpfs directory %s: %m", p);
348 0 : goto fail;
349 : }
350 :
351 : /* Lacking permissions, maybe
352 : * CAP_SYS_ADMIN-less container? In this case,
353 : * just use a normal directory. */
354 :
355 0 : r = chmod_and_chown(p, 0700, u->uid, u->gid);
356 0 : if (r < 0) {
357 0 : log_error_errno(r, "Failed to change runtime directory ownership and mode: %m");
358 0 : goto fail;
359 : }
360 : }
361 :
362 0 : r = label_fix(p, false, false);
363 0 : if (r < 0)
364 0 : log_warning_errno(r, "Failed to fix label of '%s', ignoring: %m", p);
365 : }
366 :
367 0 : u->runtime_path = p;
368 0 : return 0;
369 :
370 : fail:
371 0 : if (p) {
372 : /* Try to clean up, but ignore errors */
373 0 : (void) rmdir(p);
374 0 : free(p);
375 : }
376 :
377 0 : u->runtime_path = NULL;
378 0 : return r;
379 : }
380 :
381 0 : static int user_start_slice(User *u) {
382 : char *job;
383 : int r;
384 :
385 0 : assert(u);
386 :
387 0 : if (!u->slice) {
388 0 : _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
389 : char lu[DECIMAL_STR_MAX(uid_t) + 1], *slice;
390 0 : sprintf(lu, UID_FMT, u->uid);
391 :
392 0 : r = slice_build_subslice(SPECIAL_USER_SLICE, lu, &slice);
393 0 : if (r < 0)
394 0 : return r;
395 :
396 0 : r = manager_start_unit(u->manager, slice, &error, &job);
397 0 : if (r < 0) {
398 0 : log_error("Failed to start user slice: %s", bus_error_message(&error, r));
399 0 : free(slice);
400 : } else {
401 0 : u->slice = slice;
402 :
403 0 : free(u->slice_job);
404 0 : u->slice_job = job;
405 : }
406 : }
407 :
408 0 : if (u->slice)
409 0 : hashmap_put(u->manager->user_units, u->slice, u);
410 :
411 0 : return 0;
412 : }
413 :
414 0 : static int user_start_service(User *u) {
415 0 : _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
416 : char *job;
417 : int r;
418 :
419 0 : assert(u);
420 :
421 0 : if (!u->service) {
422 : char lu[DECIMAL_STR_MAX(uid_t) + 1], *service;
423 0 : sprintf(lu, UID_FMT, u->uid);
424 :
425 0 : r = unit_name_build("user", lu, ".service", &service);
426 0 : if (r < 0)
427 0 : return log_error_errno(r, "Failed to build service name: %m");
428 :
429 0 : r = manager_start_unit(u->manager, service, &error, &job);
430 0 : if (r < 0) {
431 0 : log_error("Failed to start user service: %s", bus_error_message(&error, r));
432 0 : free(service);
433 : } else {
434 0 : u->service = service;
435 :
436 0 : free(u->service_job);
437 0 : u->service_job = job;
438 : }
439 : }
440 :
441 0 : if (u->service)
442 0 : hashmap_put(u->manager->user_units, u->service, u);
443 :
444 0 : return 0;
445 : }
446 :
447 0 : int user_start(User *u) {
448 : int r;
449 :
450 0 : assert(u);
451 :
452 0 : if (u->started)
453 0 : return 0;
454 :
455 0 : log_debug("New user %s logged in.", u->name);
456 :
457 : /* Make XDG_RUNTIME_DIR */
458 0 : r = user_mkdir_runtime_path(u);
459 0 : if (r < 0)
460 0 : return r;
461 :
462 : /* Create cgroup */
463 0 : r = user_start_slice(u);
464 0 : if (r < 0)
465 0 : return r;
466 :
467 : /* Save the user data so far, because pam_systemd will read the
468 : * XDG_RUNTIME_DIR out of it while starting up systemd --user.
469 : * We need to do user_save_internal() because we have not
470 : * "officially" started yet. */
471 0 : user_save_internal(u);
472 :
473 : /* Spawn user systemd */
474 0 : r = user_start_service(u);
475 0 : if (r < 0)
476 0 : return r;
477 :
478 0 : if (!dual_timestamp_is_set(&u->timestamp))
479 0 : dual_timestamp_get(&u->timestamp);
480 :
481 0 : u->started = true;
482 :
483 : /* Save new user data */
484 0 : user_save(u);
485 :
486 0 : user_send_signal(u, true);
487 :
488 0 : return 0;
489 : }
490 :
491 0 : static int user_stop_slice(User *u) {
492 0 : _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
493 : char *job;
494 : int r;
495 :
496 0 : assert(u);
497 :
498 0 : if (!u->slice)
499 0 : return 0;
500 :
501 0 : r = manager_stop_unit(u->manager, u->slice, &error, &job);
502 0 : if (r < 0) {
503 0 : log_error("Failed to stop user slice: %s", bus_error_message(&error, r));
504 0 : return r;
505 : }
506 :
507 0 : free(u->slice_job);
508 0 : u->slice_job = job;
509 :
510 0 : return r;
511 : }
512 :
513 0 : static int user_stop_service(User *u) {
514 0 : _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
515 : char *job;
516 : int r;
517 :
518 0 : assert(u);
519 :
520 0 : if (!u->service)
521 0 : return 0;
522 :
523 0 : r = manager_stop_unit(u->manager, u->service, &error, &job);
524 0 : if (r < 0) {
525 0 : log_error("Failed to stop user service: %s", bus_error_message(&error, r));
526 0 : return r;
527 : }
528 :
529 0 : free(u->service_job);
530 0 : u->service_job = job;
531 :
532 0 : return r;
533 : }
534 :
535 0 : static int user_remove_runtime_path(User *u) {
536 : int r;
537 :
538 0 : assert(u);
539 :
540 0 : if (!u->runtime_path)
541 0 : return 0;
542 :
543 0 : r = rm_rf(u->runtime_path, 0);
544 0 : if (r < 0)
545 0 : log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
546 :
547 : /* Ignore cases where the directory isn't mounted, as that's
548 : * quite possible, if we lacked the permissions to mount
549 : * something */
550 0 : r = umount2(u->runtime_path, MNT_DETACH);
551 0 : if (r < 0 && errno != EINVAL && errno != ENOENT)
552 0 : log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", u->runtime_path);
553 :
554 0 : r = rm_rf(u->runtime_path, REMOVE_ROOT);
555 0 : if (r < 0)
556 0 : log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
557 :
558 0 : free(u->runtime_path);
559 0 : u->runtime_path = NULL;
560 :
561 0 : return r;
562 : }
563 :
564 0 : int user_stop(User *u, bool force) {
565 : Session *s;
566 0 : int r = 0, k;
567 0 : assert(u);
568 :
569 : /* Stop jobs have already been queued */
570 0 : if (u->stopping) {
571 0 : user_save(u);
572 0 : return r;
573 : }
574 :
575 0 : LIST_FOREACH(sessions_by_user, s, u->sessions) {
576 0 : k = session_stop(s, force);
577 0 : if (k < 0)
578 0 : r = k;
579 : }
580 :
581 : /* Kill systemd */
582 0 : k = user_stop_service(u);
583 0 : if (k < 0)
584 0 : r = k;
585 :
586 : /* Kill cgroup */
587 0 : k = user_stop_slice(u);
588 0 : if (k < 0)
589 0 : r = k;
590 :
591 0 : u->stopping = true;
592 :
593 0 : user_save(u);
594 :
595 0 : return r;
596 : }
597 :
598 0 : int user_finalize(User *u) {
599 : Session *s;
600 0 : int r = 0, k;
601 :
602 0 : assert(u);
603 :
604 0 : if (u->started)
605 0 : log_debug("User %s logged out.", u->name);
606 :
607 0 : LIST_FOREACH(sessions_by_user, s, u->sessions) {
608 0 : k = session_finalize(s);
609 0 : if (k < 0)
610 0 : r = k;
611 : }
612 :
613 : /* Kill XDG_RUNTIME_DIR */
614 0 : k = user_remove_runtime_path(u);
615 0 : if (k < 0)
616 0 : r = k;
617 :
618 : /* Clean SysV + POSIX IPC objects */
619 0 : if (u->manager->remove_ipc) {
620 0 : k = clean_ipc(u->uid);
621 0 : if (k < 0)
622 0 : r = k;
623 : }
624 :
625 0 : unlink(u->state_file);
626 0 : user_add_to_gc_queue(u);
627 :
628 0 : if (u->started) {
629 0 : user_send_signal(u, false);
630 0 : u->started = false;
631 : }
632 :
633 0 : return r;
634 : }
635 :
636 0 : int user_get_idle_hint(User *u, dual_timestamp *t) {
637 : Session *s;
638 0 : bool idle_hint = true;
639 0 : dual_timestamp ts = DUAL_TIMESTAMP_NULL;
640 :
641 0 : assert(u);
642 :
643 0 : LIST_FOREACH(sessions_by_user, s, u->sessions) {
644 : dual_timestamp k;
645 : int ih;
646 :
647 0 : ih = session_get_idle_hint(s, &k);
648 0 : if (ih < 0)
649 0 : return ih;
650 :
651 0 : if (!ih) {
652 0 : if (!idle_hint) {
653 0 : if (k.monotonic < ts.monotonic)
654 0 : ts = k;
655 : } else {
656 0 : idle_hint = false;
657 0 : ts = k;
658 : }
659 0 : } else if (idle_hint) {
660 :
661 0 : if (k.monotonic > ts.monotonic)
662 0 : ts = k;
663 : }
664 : }
665 :
666 0 : if (t)
667 0 : *t = ts;
668 :
669 0 : return idle_hint;
670 : }
671 :
672 0 : int user_check_linger_file(User *u) {
673 0 : _cleanup_free_ char *cc = NULL;
674 0 : char *p = NULL;
675 :
676 0 : cc = cescape(u->name);
677 0 : if (!cc)
678 0 : return -ENOMEM;
679 :
680 0 : p = strjoina("/var/lib/systemd/linger/", cc);
681 :
682 0 : return access(p, F_OK) >= 0;
683 : }
684 :
685 0 : bool user_check_gc(User *u, bool drop_not_started) {
686 0 : assert(u);
687 :
688 0 : if (drop_not_started && !u->started)
689 0 : return false;
690 :
691 0 : if (u->sessions)
692 0 : return true;
693 :
694 0 : if (user_check_linger_file(u) > 0)
695 0 : return true;
696 :
697 0 : if (u->slice_job && manager_job_is_active(u->manager, u->slice_job))
698 0 : return true;
699 :
700 0 : if (u->service_job && manager_job_is_active(u->manager, u->service_job))
701 0 : return true;
702 :
703 0 : return false;
704 : }
705 :
706 0 : void user_add_to_gc_queue(User *u) {
707 0 : assert(u);
708 :
709 0 : if (u->in_gc_queue)
710 0 : return;
711 :
712 0 : LIST_PREPEND(gc_queue, u->manager->user_gc_queue, u);
713 0 : u->in_gc_queue = true;
714 : }
715 :
716 0 : UserState user_get_state(User *u) {
717 : Session *i;
718 :
719 0 : assert(u);
720 :
721 0 : if (u->stopping)
722 0 : return USER_CLOSING;
723 :
724 0 : if (!u->started || u->slice_job || u->service_job)
725 0 : return USER_OPENING;
726 :
727 0 : if (u->sessions) {
728 0 : bool all_closing = true;
729 :
730 0 : LIST_FOREACH(sessions_by_user, i, u->sessions) {
731 : SessionState state;
732 :
733 0 : state = session_get_state(i);
734 0 : if (state == SESSION_ACTIVE)
735 0 : return USER_ACTIVE;
736 0 : if (state != SESSION_CLOSING)
737 0 : all_closing = false;
738 : }
739 :
740 0 : return all_closing ? USER_CLOSING : USER_ONLINE;
741 : }
742 :
743 0 : if (user_check_linger_file(u) > 0)
744 0 : return USER_LINGERING;
745 :
746 0 : return USER_CLOSING;
747 : }
748 :
749 0 : int user_kill(User *u, int signo) {
750 0 : assert(u);
751 :
752 0 : if (!u->slice)
753 0 : return -ESRCH;
754 :
755 0 : return manager_kill_unit(u->manager, u->slice, KILL_ALL, signo, NULL);
756 : }
757 :
758 0 : static bool elect_display_filter(Session *s) {
759 : /* Return true if the session is a candidate for the user’s ‘primary
760 : * session’ or ‘display’. */
761 0 : assert(s);
762 :
763 0 : return (s->class == SESSION_USER && !s->stopping);
764 : }
765 :
766 0 : static int elect_display_compare(Session *s1, Session *s2) {
767 : /* Indexed by SessionType. Lower numbers mean more preferred. */
768 0 : const int type_ranks[_SESSION_TYPE_MAX] = {
769 : [SESSION_UNSPECIFIED] = 0,
770 : [SESSION_TTY] = -2,
771 : [SESSION_X11] = -3,
772 : [SESSION_WAYLAND] = -3,
773 : [SESSION_MIR] = -3,
774 : [SESSION_WEB] = -1,
775 : };
776 :
777 : /* Calculate the partial order relationship between s1 and s2,
778 : * returning < 0 if s1 is preferred as the user’s ‘primary session’,
779 : * 0 if s1 and s2 are equally preferred or incomparable, or > 0 if s2
780 : * is preferred.
781 : *
782 : * s1 or s2 may be NULL. */
783 0 : if (!s1 && !s2)
784 0 : return 0;
785 :
786 0 : if ((s1 == NULL) != (s2 == NULL))
787 0 : return (s1 == NULL) - (s2 == NULL);
788 :
789 0 : if (s1->stopping != s2->stopping)
790 0 : return s1->stopping - s2->stopping;
791 :
792 0 : if ((s1->class != SESSION_USER) != (s2->class != SESSION_USER))
793 0 : return (s1->class != SESSION_USER) - (s2->class != SESSION_USER);
794 :
795 0 : if ((s1->type == _SESSION_TYPE_INVALID) != (s2->type == _SESSION_TYPE_INVALID))
796 0 : return (s1->type == _SESSION_TYPE_INVALID) - (s2->type == _SESSION_TYPE_INVALID);
797 :
798 0 : if (s1->type != s2->type)
799 0 : return type_ranks[s1->type] - type_ranks[s2->type];
800 :
801 0 : return 0;
802 : }
803 :
804 0 : void user_elect_display(User *u) {
805 : Session *s;
806 :
807 0 : assert(u);
808 :
809 : /* This elects a primary session for each user, which we call
810 : * the "display". We try to keep the assignment stable, but we
811 : * "upgrade" to better choices. */
812 0 : log_debug("Electing new display for user %s", u->name);
813 :
814 0 : LIST_FOREACH(sessions_by_user, s, u->sessions) {
815 0 : if (!elect_display_filter(s)) {
816 0 : log_debug("Ignoring session %s", s->id);
817 0 : continue;
818 : }
819 :
820 0 : if (elect_display_compare(s, u->display) < 0) {
821 0 : log_debug("Choosing session %s in preference to %s", s->id, u->display ? u->display->id : "-");
822 0 : u->display = s;
823 : }
824 : }
825 0 : }
826 :
827 : static const char* const user_state_table[_USER_STATE_MAX] = {
828 : [USER_OFFLINE] = "offline",
829 : [USER_OPENING] = "opening",
830 : [USER_LINGERING] = "lingering",
831 : [USER_ONLINE] = "online",
832 : [USER_ACTIVE] = "active",
833 : [USER_CLOSING] = "closing"
834 : };
835 :
836 16 : DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);
837 :
838 0 : int config_parse_tmpfs_size(
839 : const char* unit,
840 : const char *filename,
841 : unsigned line,
842 : const char *section,
843 : unsigned section_line,
844 : const char *lvalue,
845 : int ltype,
846 : const char *rvalue,
847 : void *data,
848 : void *userdata) {
849 :
850 0 : size_t *sz = data;
851 : const char *e;
852 : int r;
853 :
854 0 : assert(filename);
855 0 : assert(lvalue);
856 0 : assert(rvalue);
857 0 : assert(data);
858 :
859 0 : e = endswith(rvalue, "%");
860 0 : if (e) {
861 : unsigned long ul;
862 : char *f;
863 :
864 0 : errno = 0;
865 0 : ul = strtoul(rvalue, &f, 10);
866 0 : if (errno != 0 || f != e) {
867 0 : log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Failed to parse percentage value, ignoring: %s", rvalue);
868 0 : return 0;
869 : }
870 :
871 0 : if (ul <= 0 || ul >= 100) {
872 0 : log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Percentage value out of range, ignoring: %s", rvalue);
873 0 : return 0;
874 : }
875 :
876 0 : *sz = PAGE_ALIGN((size_t) ((physical_memory() * (uint64_t) ul) / (uint64_t) 100));
877 : } else {
878 : off_t o;
879 :
880 0 : r = parse_size(rvalue, 1024, &o);
881 0 : if (r < 0 || (off_t) (size_t) o != o) {
882 0 : log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
883 0 : return 0;
884 : }
885 :
886 0 : *sz = PAGE_ALIGN((size_t) o);
887 : }
888 :
889 0 : return 0;
890 : }
|