X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Flogin%2Flogind-user.c;h=9ff1302663e0ebeaa31edafe904eb11df3ab8566;hb=13790add4bf648fed816361794d8277a75253410;hp=6ba8d9807bc16374284cd137bd3079251127d74e;hpb=3a9f7a30ce89b44097731310221802f6d72ea752;p=elogind.git
diff --git a/src/login/logind-user.c b/src/login/logind-user.c
index 6ba8d9807..9ff130266 100644
--- a/src/login/logind-user.c
+++ b/src/login/logind-user.c
@@ -19,6 +19,7 @@
along with systemd; If not, see .
***/
+#include
#include
#include
#include
@@ -28,11 +29,15 @@
#include "hashmap.h"
#include "strv.h"
#include "fileio.h"
+#include "path-util.h"
#include "special.h"
#include "unit-name.h"
#include "bus-util.h"
#include "bus-error.h"
+#include "conf-parser.h"
+#include "clean-ipc.h"
#include "logind-user.h"
+#include "smack-util.h"
User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
User *u;
@@ -48,7 +53,7 @@ User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
if (!u->name)
goto fail;
- if (asprintf(&u->state_file, "/run/systemd/users/%lu", (unsigned long) uid) < 0)
+ if (asprintf(&u->state_file, "/run/systemd/users/"UID_FMT, uid) < 0)
goto fail;
if (hashmap_put(m->users, ULONG_TO_PTR((unsigned long) uid), u) < 0)
@@ -145,10 +150,10 @@ int user_save(User *u) {
if (dual_timestamp_is_set(&u->timestamp))
fprintf(f,
- "REALTIME=%llu\n"
- "MONOTONIC=%llu\n",
- (unsigned long long) u->timestamp.realtime,
- (unsigned long long) u->timestamp.monotonic);
+ "REALTIME="USEC_FMT"\n"
+ "MONOTONIC="USEC_FMT"\n",
+ u->timestamp.realtime,
+ u->timestamp.monotonic);
if (u->sessions) {
Session *i;
@@ -247,7 +252,7 @@ int user_save(User *u) {
finish:
if (r < 0)
- log_error("Failed to save user data for %s: %s", u->name, strerror(-r));
+ log_error_errno(r, "Failed to save user data %s: %m", u->state_file);
return r;
}
@@ -273,7 +278,7 @@ int user_load(User *u) {
if (r == -ENOENT)
return 0;
- log_error("Failed to read %s: %s", u->state_file, strerror(-r));
+ log_error_errno(r, "Failed to read %s: %m", u->state_file);
return r;
}
@@ -305,27 +310,44 @@ static int user_mkdir_runtime_path(User *u) {
assert(u);
r = mkdir_safe_label("/run/user", 0755, 0, 0);
- if (r < 0) {
- log_error("Failed to create /run/user: %s", strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to create /run/user: %m");
if (!u->runtime_path) {
- if (asprintf(&p, "/run/user/%lu", (unsigned long) u->uid) < 0)
+ if (asprintf(&p, "/run/user/" UID_FMT, u->uid) < 0)
return log_oom();
} else
p = u->runtime_path;
- r = mkdir_safe_label(p, 0700, u->uid, u->gid);
- if (r < 0) {
- log_error("Failed to create runtime directory %s: %s", p, strerror(-r));
- free(p);
- u->runtime_path = NULL;
- return r;
+ if (path_is_mount_point(p, false) <= 0) {
+ _cleanup_free_ char *t = NULL;
+
+ mkdir(p, 0700);
+
+ if (mac_smack_use())
+ r = asprintf(&t, "mode=0700,smackfsroot=*,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
+ else
+ r = asprintf(&t, "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
+
+ if (r < 0) {
+ r = log_oom();
+ goto fail;
+ }
+
+ r = mount("tmpfs", p, "tmpfs", MS_NODEV|MS_NOSUID, t);
+ if (r < 0) {
+ log_error_errno(r, "Failed to mount per-user tmpfs directory %s: %m", p);
+ goto fail;
+ }
}
u->runtime_path = p;
return 0;
+
+fail:
+ free(p);
+ u->runtime_path = NULL;
+ return r;
}
static int user_start_slice(User *u) {
@@ -336,8 +358,8 @@ static int user_start_slice(User *u) {
if (!u->slice) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- char lu[DECIMAL_STR_MAX(unsigned long) + 1], *slice;
- sprintf(lu, "%lu", (unsigned long) u->uid);
+ char lu[DECIMAL_STR_MAX(uid_t) + 1], *slice;
+ sprintf(lu, UID_FMT, u->uid);
r = build_subslice(SPECIAL_USER_SLICE, lu, &slice);
if (r < 0)
@@ -369,8 +391,8 @@ static int user_start_service(User *u) {
assert(u);
if (!u->service) {
- char lu[DECIMAL_STR_MAX(unsigned long) + 1], *service;
- sprintf(lu, "%lu", (unsigned long) u->uid);
+ char lu[DECIMAL_STR_MAX(uid_t) + 1], *service;
+ sprintf(lu, UID_FMT, u->uid);
service = unit_name_build("user", lu, ".service");
if (!service)
@@ -484,9 +506,16 @@ static int user_remove_runtime_path(User *u) {
if (!u->runtime_path)
return 0;
+ r = rm_rf(u->runtime_path, false, false, false);
+ if (r < 0)
+ log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
+
+ if (umount2(u->runtime_path, MNT_DETACH) < 0)
+ log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", u->runtime_path);
+
r = rm_rf(u->runtime_path, false, true, false);
if (r < 0)
- log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r));
+ log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
free(u->runtime_path);
u->runtime_path = NULL;
@@ -494,13 +523,19 @@ static int user_remove_runtime_path(User *u) {
return r;
}
-int user_stop(User *u) {
+int user_stop(User *u, bool force) {
Session *s;
int r = 0, k;
assert(u);
+ /* Stop jobs have already been queued */
+ if (u->stopping) {
+ user_save(u);
+ return r;
+ }
+
LIST_FOREACH(sessions_by_user, s, u->sessions) {
- k = session_stop(s);
+ k = session_stop(s, force);
if (k < 0)
r = k;
}
@@ -515,6 +550,8 @@ int user_stop(User *u) {
if (k < 0)
r = k;
+ u->stopping = true;
+
user_save(u);
return r;
@@ -540,6 +577,13 @@ int user_finalize(User *u) {
if (k < 0)
r = k;
+ /* Clean SysV + POSIX IPC objects */
+ if (u->manager->remove_ipc) {
+ k = clean_ipc(u->uid);
+ if (k < 0)
+ r = k;
+ }
+
unlink(u->state_file);
user_add_to_gc_queue(u);
@@ -618,12 +662,6 @@ bool user_check_gc(User *u, bool drop_not_started) {
if (u->service_job && manager_job_is_active(u->manager, u->service_job))
return true;
- if (u->slice && manager_unit_is_active(u->manager, u->slice) != 0)
- return true;
-
- if (u->service && manager_unit_is_active(u->manager, u->service) != 0)
- return true;
-
return false;
}
@@ -639,25 +677,30 @@ void user_add_to_gc_queue(User *u) {
UserState user_get_state(User *u) {
Session *i;
- bool all_closing = true;
assert(u);
- if (u->closing)
+ if (u->stopping)
return USER_CLOSING;
if (u->slice_job || u->service_job)
return USER_OPENING;
- LIST_FOREACH(sessions_by_user, i, u->sessions) {
- if (session_is_active(i))
- return USER_ACTIVE;
- if (session_get_state(i) != SESSION_CLOSING)
- all_closing = false;
- }
+ if (u->sessions) {
+ bool all_closing = true;
+
+ LIST_FOREACH(sessions_by_user, i, u->sessions) {
+ SessionState state;
+
+ state = session_get_state(i);
+ if (state == SESSION_ACTIVE)
+ return USER_ACTIVE;
+ if (state != SESSION_CLOSING)
+ all_closing = false;
+ }
- if (u->sessions)
return all_closing ? USER_CLOSING : USER_ONLINE;
+ }
if (user_check_linger_file(u) > 0)
return USER_LINGERING;
@@ -674,6 +717,56 @@ int user_kill(User *u, int signo) {
return manager_kill_unit(u->manager, u->slice, KILL_ALL, signo, NULL);
}
+void user_elect_display(User *u) {
+ Session *graphical = NULL, *text = NULL, *other = NULL, *s;
+
+ assert(u);
+
+ /* This elects a primary session for each user, which we call
+ * the "display". We try to keep the assignment stable, but we
+ * "upgrade" to better choices. */
+
+ LIST_FOREACH(sessions_by_user, s, u->sessions) {
+
+ if (s->class != SESSION_USER)
+ continue;
+
+ if (s->stopping)
+ continue;
+
+ if (SESSION_TYPE_IS_GRAPHICAL(s->type))
+ graphical = s;
+ else if (s->type == SESSION_TTY)
+ text = s;
+ else
+ other = s;
+ }
+
+ if (graphical &&
+ (!u->display ||
+ u->display->class != SESSION_USER ||
+ u->display->stopping ||
+ !SESSION_TYPE_IS_GRAPHICAL(u->display->type))) {
+ u->display = graphical;
+ return;
+ }
+
+ if (text &&
+ (!u->display ||
+ u->display->class != SESSION_USER ||
+ u->display->stopping ||
+ u->display->type != SESSION_TTY)) {
+ u->display = text;
+ return;
+ }
+
+ if (other &&
+ (!u->display ||
+ u->display->class != SESSION_USER ||
+ u->display->stopping))
+ u->display = other;
+}
+
static const char* const user_state_table[_USER_STATE_MAX] = {
[USER_OFFLINE] = "offline",
[USER_OPENING] = "opening",
@@ -684,3 +777,57 @@ static const char* const user_state_table[_USER_STATE_MAX] = {
};
DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);
+
+int config_parse_tmpfs_size(
+ const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ size_t *sz = data;
+ const char *e;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ e = endswith(rvalue, "%");
+ if (e) {
+ unsigned long ul;
+ char *f;
+
+ errno = 0;
+ ul = strtoul(rvalue, &f, 10);
+ if (errno != 0 || f != e) {
+ log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Failed to parse percentage value, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ if (ul <= 0 || ul >= 100) {
+ log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Percentage value out of range, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ *sz = PAGE_ALIGN((size_t) ((physical_memory() * (uint64_t) ul) / (uint64_t) 100));
+ } else {
+ off_t o;
+
+ r = parse_size(rvalue, 1024, &o);
+ if (r < 0 || (off_t) (size_t) o != o) {
+ log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ *sz = PAGE_ALIGN((size_t) o);
+ }
+
+ return 0;
+}