LCOV - code coverage report
Current view: top level - login - logind-user.c (source / functions) Hit Total Coverage
Test: systemd test coverage Lines: 1 454 0.2 %
Date: 2015-07-29 18:47:03 Functions: 2 26 7.7 %

          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             : }

Generated by: LCOV version 1.11