To contribute to elogind, fork the current source code from github:
- https://github.com/andywingo/elogind
+ https://github.com/elogind/elogind
Send a pull request for the changes you like.
Finally, bug reports:
- https://github.com/andywingo/elogind/issues
+ https://github.com/elogind/elogind/issues
Why bother?
-----------
Libelogind just implements login-related functionality. It also
provides the sd-bus API.
-Unlike systemd, whose logind arranges to run user sessions in cgroups
-via RPC calls to systemd, in elogind there is no systemd so there are
-no cgroups. This has a few implications:
+Unlike systemd, whose logind arranges to manage resources for user
+sessions via RPC calls to systemd, in elogind there is no systemd so
+there is no global cgroup-based resource management. This has a few
+implications:
* Elogind does not create "slices" for users. Elogind will not
record that users are associated with slices.
- * Systemd's logind waits for all user jobs to stop before recording
- that a user's session has gone away. Since we have no cgroups,
- elogind just removes the session directly when pam_elogind.so
- indicates the user has logged out.
-
* The /run/systemd/slices directory will always be empty.
- * Support for lingering is not so great.
+ * Elogind does not have the concept of a "scope", internally, as
+ it's the same as a session. Any API that refers to scopes will
+ always return an error code.
+
+On the other hand, elogind does use a similar strategy to systemd in
+that it places processes in a private cgroup for organizational
+purposes, without installing any controllers (see
+http://0pointer.de/blog/projects/cgroups-vs-cgroups.html). This
+allows elogind to map arbitrary processes to sessions, even if the
+process does the usual double-fork to be reparented to PID 1.
Elogind does not manage virtual terminals.
#include "formats-util.h"
#include "process-util.h"
#include "path-util.h"
-#include "unit-name.h"
+// #include "unit-name.h"
#include "fileio.h"
// #include "special.h"
#include "mkdir.h"
}
int cg_get_root_path(char **path) {
- char *p; //, *e;
+/// elogind does not support systemd scopes and slices
+#if 0
+ char *p, *e;
int r;
assert(path);
if (r < 0)
return r;
-/// elogind does not support systemd scopes and slices
-#if 0
e = endswith(p, "/" SPECIAL_INIT_SCOPE);
if (!e)
e = endswith(p, "/" SPECIAL_SYSTEM_SLICE); /* legacy */
e = endswith(p, "/system"); /* even more legacy */
if (e)
*e = 0;
-#endif // 0
*path = p;
return 0;
+#else
+ assert(path);
+ return cg_pid_get_path(ELOGIND_CGROUP_CONTROLLER, 1, path);
+#endif // 0
}
int cg_shift_path(const char *cgroup, const char *root, const char **shifted) {
return 0;
}
+/// UNNEEDED by elogind
+#if 0
int cg_path_decode_unit(const char *cgroup, char **unit){
char *c, *s;
size_t n;
return cg_path_get_machine_name(cgroup, machine);
}
+#endif // 0
int cg_path_get_session(const char *path, char **session) {
+ /* Elogind uses a flat hierarchy, just "/SESSION". The only
+ wrinkle is that SESSION might be escaped. */
+#if 0
_cleanup_free_ char *unit = NULL;
char *start, *end;
int r;
*end = 0;
if (!session_id_valid(start))
return -ENXIO;
+#else
+ const char *e, *n, *start;
+
+ assert(path);
+ assert(path[0] == '/');
+
+ e = path + 1;
+ n = strchrnul(e, '/');
+ if (e == n)
+ return -ENOENT;
+
+ start = strndupa(e, n - e);
+ start = cg_unescape(start);
+
+ if (!start[0])
+ return -ENOENT;
+#endif // 0
if (session) {
char *rr;
return cg_path_get_session(cgroup, session);
}
+/// UNNEEDED by elogind
+#if 0
int cg_path_get_owner_uid(const char *path, uid_t *uid) {
_cleanup_free_ char *slice = NULL;
char *start, *end;
return cg_path_get_user_slice(cgroup, slice);
}
+#endif // 0
char *cg_escape(const char *p) {
bool need_prefix = false;
int cg_get_root_path(char **path);
int cg_path_get_session(const char *path, char **session);
-int cg_path_get_owner_uid(const char *path, uid_t *uid);
-int cg_path_get_unit(const char *path, char **unit);
-int cg_path_get_user_unit(const char *path, char **unit);
-int cg_path_get_machine_name(const char *path, char **machine);
-int cg_path_get_slice(const char *path, char **slice);
-int cg_path_get_user_slice(const char *path, char **slice);
+// UNNEEDED int cg_path_get_owner_uid(const char *path, uid_t *uid);
+// UNNEEDED int cg_path_get_unit(const char *path, char **unit);
+// UNNEEDED int cg_path_get_user_unit(const char *path, char **unit);
+// UNNEEDED int cg_path_get_machine_name(const char *path, char **machine);
+// UNNEEDED int cg_path_get_slice(const char *path, char **slice);
+// UNNEEDED int cg_path_get_user_slice(const char *path, char **slice);
int cg_shift_path(const char *cgroup, const char *cached_root, const char **shifted);
int cg_pid_get_path_shifted(pid_t pid, const char *cached_root, char **cgroup);
int cg_pid_get_session(pid_t pid, char **session);
-int cg_pid_get_owner_uid(pid_t pid, uid_t *uid);
-int cg_pid_get_unit(pid_t pid, char **unit);
-int cg_pid_get_user_unit(pid_t pid, char **unit);
-int cg_pid_get_machine_name(pid_t pid, char **machine);
-int cg_pid_get_slice(pid_t pid, char **slice);
-int cg_pid_get_user_slice(pid_t pid, char **slice);
-
-int cg_path_decode_unit(const char *cgroup, char **unit);
+// UNNEEDED int cg_pid_get_owner_uid(pid_t pid, uid_t *uid);
+// UNNEEDED int cg_pid_get_unit(pid_t pid, char **unit);
+// UNNEEDED int cg_pid_get_user_unit(pid_t pid, char **unit);
+// UNNEEDED int cg_pid_get_machine_name(pid_t pid, char **machine);
+// UNNEEDED int cg_pid_get_slice(pid_t pid, char **slice);
+// UNNEEDED int cg_pid_get_user_slice(pid_t pid, char **slice);
+
+// UNNEEDED int cg_path_decode_unit(const char *cgroup, char **unit);
char *cg_escape(const char *p);
char *cg_unescape(const char *p) _pure_;
log_parse_environment();
log_open();
+#if 0
/* We send this event to the private D-Bus socket and then the
* system instance will forward this to the system bus. We do
* this to avoid an activation loop when we start dbus when we
* are called when the dbus service is shut down. */
r = bus_open_system_systemd(&bus);
+#else
+ /* Unlike in systemd where this has to use a private socket,
+ since elogind doesn't associate control groups with services
+ and doesn't manage the dbus service, we can just use the
+ system bus. */
+ r = sd_bus_open_system(&bus);
+#endif // 0
+
if (r < 0) {
+#if 0
/* If we couldn't connect we assume this was triggered
* while systemd got restarted/transitioned from
* initrd to the system, so let's ignore this */
log_debug_errno(r, "Failed to get D-Bus connection: %m");
+#else
+ /* If dbus isn't running or responding, there is nothing
+ * we can do about it. */
+ log_debug_errno(r, "Failed to open system bus: %m");
+#endif
return EXIT_FAILURE;
}
"Released",
"s", argv[1]);
if (r < 0) {
+#if 0
log_debug_errno(r, "Failed to send signal message on private connection: %m");
+#else
+ log_debug_errno(r, "Failed to send signal message: %m");
+#endif
return EXIT_FAILURE;
}
if (r < 0)
return r;
+/// elogind does not support systemd slices
+#if 0
return cg_path_get_owner_uid(shifted, uid);
+#else
+ *uid = c->uid;
+ return 0;
+#endif // 0
}
_public_ int sd_bus_creds_get_cmdline(sd_bus_creds *c, char ***cmdline) {
return 0;
}
-/// UNNEEDED by elogind
-#if 0
_public_ int sd_bus_creds_get_audit_session_id(sd_bus_creds *c, uint32_t *sessionid) {
assert_return(c, -EINVAL);
assert_return(sessionid, -EINVAL);
*sessionid = c->audit_session_id;
return 0;
}
-#endif // 0
_public_ int sd_bus_creds_get_audit_login_uid(sd_bus_creds *c, uid_t *uid) {
assert_return(c, -EINVAL);
_cleanup_bus_unref_ _unused_ sd_bus *_dont_destroy_##bus = sd_bus_ref(bus)
int bus_set_address_system(sd_bus *bus);
-int bus_set_address_user(sd_bus *bus);
+// UNNEEDED int bus_set_address_user(sd_bus *bus);
int bus_set_address_system_remote(sd_bus *b, const char *host);
int bus_set_address_system_machine(sd_bus *b, const char *machine);
if (e) {
if (streq(e, "system"))
return sd_bus_open_system(ret);
+/// elogind does not support systemd units
+#if 0
else if (STR_IN_SET(e, "session", "user"))
return sd_bus_open_user(ret);
+#endif // 0
}
e = secure_getenv("DBUS_STARTER_ADDRESS");
if (!e) {
+/// elogind does not support systemd units
+#if 0
if (cg_pid_get_owner_uid(0, NULL) >= 0)
return sd_bus_open_user(ret);
else
+#endif // 0
return sd_bus_open_system(ret);
}
return r;
}
+/// elogind can not open/use a user bus
+#if 0
int bus_set_address_user(sd_bus *b) {
const char *e;
uid_t uid;
return 0;
}
+#endif // 0
_public_ int sd_bus_open_user(sd_bus **ret) {
+/// elogind does not support user buses
+#if 0
sd_bus *b;
int r;
fail:
bus_free(b);
return r;
+#else
+ return sd_bus_open_system(ret);
+#endif // 0
}
int bus_set_address_system_remote(sd_bus *b, const char *host) {
}
_public_ int sd_bus_default_user(sd_bus **ret) {
+/// elogind does not support user buses
+#if 0
static thread_local sd_bus *default_user_bus = NULL;
return bus_default(sd_bus_open_user, &default_user_bus, ret);
+#else
+ return sd_bus_default_system(ret);
+#endif // 0
}
_public_ int sd_bus_default(sd_bus **ret) {
if (e) {
if (streq(e, "system"))
return sd_bus_default_system(ret);
+/// elogind does not support systemd units
+#if 0
else if (STR_IN_SET(e, "user", "session"))
return sd_bus_default_user(ret);
+#endif // 0
}
/* No type is specified, so we have not other option than to
/* Finally, if nothing is set use the cached connection for
* the right scope */
-
+/// elogind does not support systemd units
+#if 0
if (cg_pid_get_owner_uid(0, NULL) >= 0)
return sd_bus_default_user(ret);
else
+#endif // 0
return sd_bus_default_system(ret);
}
assert_return(pid >= 0, -EINVAL);
assert_return(unit, -EINVAL);
+/// elogind does not support systemd units
+#if 0
return cg_pid_get_unit(pid, unit);
+#else
+ return -ESRCH;
+#endif // 0
}
_public_ int sd_pid_get_user_unit(pid_t pid, char **unit) {
assert_return(pid >= 0, -EINVAL);
assert_return(unit, -EINVAL);
+/// elogind does not support systemd units
+#if 0
return cg_pid_get_user_unit(pid, unit);
+#else
+ return -ESRCH;
+#endif // 0
}
_public_ int sd_pid_get_machine_name(pid_t pid, char **name) {
assert_return(pid >= 0, -EINVAL);
assert_return(name, -EINVAL);
+/// elogind does not support systemd units
+#if 0
return cg_pid_get_machine_name(pid, name);
+#else
+ return -ESRCH;
+#endif // 0
}
_public_ int sd_pid_get_slice(pid_t pid, char **slice) {
assert_return(pid >= 0, -EINVAL);
assert_return(slice, -EINVAL);
+/// elogind does not support systemd slices
+#if 0
return cg_pid_get_slice(pid, slice);
+#else
+ return -ESRCH;
+#endif // 0
}
_public_ int sd_pid_get_user_slice(pid_t pid, char **slice) {
assert_return(pid >= 0, -EINVAL);
assert_return(slice, -EINVAL);
+/// elogind does not support systemd slices
+#if 0
return cg_pid_get_user_slice(pid, slice);
+#else
+ return -ESRCH;
+#endif // 0
}
_public_ int sd_pid_get_owner_uid(pid_t pid, uid_t *uid) {
assert_return(pid >= 0, -EINVAL);
assert_return(uid, -EINVAL);
+/// elogind does not support systemd slices
+#if 0
return cg_pid_get_owner_uid(pid, uid);
+#else
+ return -ESRCH;
+#endif // 0
}
_public_ int sd_pid_get_cgroup(pid_t pid, char **cgroup) {
if (r < 0)
return r;
+/// elogind does not support systemd scopes
+#if 0
return cg_pid_get_session(ucred.pid, session);
+#else
+ return -ESRCH;
+#endif // 0
}
_public_ int sd_peer_get_owner_uid(int fd, uid_t *uid) {
if (r < 0)
return r;
+/// elogind does not support systemd units
+#if 0
return cg_pid_get_owner_uid(ucred.pid, uid);
+#else
+ return -ESRCH;
+#endif // 0
}
_public_ int sd_peer_get_unit(int fd, char **unit) {
if (r < 0)
return r;
+/// elogind does not support systemd units
+#if 0
return cg_pid_get_unit(ucred.pid, unit);
+#else
+ return -ESRCH;
+#endif // 0
}
_public_ int sd_peer_get_user_unit(int fd, char **unit) {
if (r < 0)
return r;
+/// elogind does not support systemd units
+#if 0
return cg_pid_get_user_unit(ucred.pid, unit);
+#else
+ return -ESRCH;
+#endif // 0
}
_public_ int sd_peer_get_machine_name(int fd, char **machine) {
if (r < 0)
return r;
+/// elogind does not support systemd units
+#if 0
return cg_pid_get_machine_name(ucred.pid, machine);
+#else
+ return -ESRCH;
+#endif // 0
}
_public_ int sd_peer_get_slice(int fd, char **slice) {
if (r < 0)
return r;
+/// elogind does not support systemd slices
+#if 0
return cg_pid_get_slice(ucred.pid, slice);
+#else
+ return -ESRCH;
+#endif // 0
}
_public_ int sd_peer_get_user_slice(int fd, char **slice) {
if (r < 0)
return r;
+/// elogind does not support systemd slices
+#if 0
return cg_pid_get_user_slice(ucred.pid, slice);
+#else
+ return -ESRCH;
+#endif // 0
}
_public_ int sd_peer_get_cgroup(int fd, char **cgroup) {
}
int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session) {
+/// elogind does not support systemd units, but its own session system
+#if 0
_cleanup_free_ char *unit = NULL;
+#else
+ _cleanup_free_ char *session_name = NULL;
+#endif
Session *s;
int r;
if (pid < 1)
return -EINVAL;
+/// elogind does not support systemd units, but its own session system
+#if 0
r = cg_pid_get_unit(pid, &unit);
if (r < 0)
return 0;
s = hashmap_get(m->session_units, unit);
+#else
+ r = cg_pid_get_session(pid, &session_name);
+ if (r < 0)
+ return 0;
+
+ s = hashmap_get(m->sessions, session_name);
+#endif
if (!s)
return 0;
}
int manager_get_user_by_pid(Manager *m, pid_t pid, User **user) {
+/// elogind does not support systemd units, but its own session system
+#if 0
_cleanup_free_ char *unit = NULL;
User *u;
+#else
+ Session *s;
+#endif
int r;
assert(m);
if (pid < 1)
return -EINVAL;
+/// elogind does not support systemd units, but its own session system
+#if 0
r = cg_pid_get_slice(pid, &unit);
if (r < 0)
return 0;
return 0;
*user = u;
+#else
+ r = manager_get_session_by_pid (m, pid, &s);
+ if (r <= 0)
+ return r;
+
+ *user = s->user;
+#endif // 0
+
return 1;
}
return 0;
}
+/// UNNEEDED by elogind
+#if 0
static int session_start_scope(Session *s) {
int r = 0;
if (!scope)
return log_oom();
-/// elogind : Do not try to use dbus to call systemd
-#if 0
r = manager_start_scope(s->manager, scope, s->leader, s->user->slice, description, "logind.service", "systemd-user-sessions.service", &error, &job);
-#endif // 0
if (r < 0) {
log_error("Failed to start session scope %s: %s %s",
scope, bus_error_message(&error, r), error.name);
return r;
} else {
s->scope = scope;
-/// elogind does not support scope jobs
-#if 0
free(s->scope_job);
s->scope_job = job;
-#endif // 0
}
}
return 0;
}
+#endif // 0
+
+static int session_start_cgroup(Session *s) {
+ int r;
+
+ assert(s);
+ assert(s->user);
+ assert(s->leader > 0);
+
+ /* First, create our own group */
+ r = cg_create(ELOGIND_CGROUP_CONTROLLER, s->id);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create cgroup %s: %m", s->id);
+
+ r = cg_attach(ELOGIND_CGROUP_CONTROLLER, s->id, s->leader);
+ if (r < 0)
+ log_warning_errno(r, "Failed to attach PID %d to cgroup %s: %m", s->leader, s->id);
+
+ return 0;
+}
+
int session_start(Session *s) {
int r;
return r;
/* Create cgroup */
+/// elogind does its own session management without systemd units,
+/// slices and scopes
+#if 0
r = session_start_scope(s);
+#else
+ r = session_start_cgroup(s);
+#endif // 0
if (r < 0)
return r;
}
#endif // 0
+static int session_stop_cgroup(Session *s, bool force) {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ int r;
+
+ assert(s);
+
+ if (force || manager_shall_kill(s->manager, s->user->name)) {
+ r = session_kill(s, KILL_ALL, SIGTERM);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
int session_stop(Session *s, bool force) {
- int r = 0;
+ int r;
assert(s);
session_remove_fifo(s);
/* Kill cgroup */
-/// @todo : Currently elogind does not start scopes. It remains to be seen
-/// whether this is really not needed, but then, elogind is not a
-/// systemd cgroups manager.
+/// elogind does not start scopes, but sessions
#if 0
r = session_stop_scope(s, force);
+#else
+ r = session_stop_cgroup(s, force);
#endif // 0
s->stopping = true;
return true;
#endif // 0
+ if ( s->user->manager
+ && (cg_is_empty_recursive (ELOGIND_CGROUP_CONTROLLER, s->user->manager->cgroup_root) > 0) )
+ return true;
+
return false;
}
int session_kill(Session *s, KillWho who, int signo) {
assert(s);
-/// FIXME: Without direct cgroup support, elogind can not kill sessions
+/// Without direct cgroup support, elogind can not kill sessions
#if 0
if (!s->scope)
return -ESRCH;
return manager_kill_unit(s->manager, s->scope, who, signo, NULL);
#else
- return -ESRCH;
+ if (who == KILL_LEADER) {
+ if (s->leader <= 0)
+ return -ESRCH;
+
+ /* FIXME: verify that leader is in cgroup? */
+
+ if (kill(s->leader, signo) < 0) {
+ return log_error_errno(errno, "Failed to kill process leader %d for session %s: %m", s->leader, s->id);
+ }
+ return 0;
+ } else {
+ bool sigcont = false;
+ bool ignore_self = true;
+ bool rem = true;
+ return cg_kill_recursive (ELOGIND_CGROUP_CONTROLLER, s->id, signo,
+ sigcont, ignore_self, rem, NULL);
+ }
#endif // 0
}
}
int user_kill(User *u, int signo) {
+/// Without systemd unit support, elogind has to rely on its session system
+#if 0
assert(u);
-/// FIXME: Without direct cgroup support, elogind can not kill users
-#if 0
if (!u->slice)
return -ESRCH;
return manager_kill_unit(u->manager, u->slice, KILL_ALL, signo, NULL);
#else
- return -ESRCH;
+ Session *s;
+ int res = 0;
+
+ assert(u);
+
+ LIST_FOREACH(sessions_by_user, s, u->sessions) {
+ int r = session_kill(s, KILL_ALL, signo);
+ if (res == 0 && r < 0)
+ res = r;
+ }
+
+ return res;
#endif // 0
}
}
#endif // 0
+static int signal_agent_released(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ Session *s;
+ const char *cgroup;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "s", &cgroup);
+ if (r < 0) {
+ bus_log_parse_error(r);
+ return 0;
+ }
+
+ s = hashmap_get(m->sessions, cgroup);
+
+ if (!s) {
+ log_warning("Session not found: %s", cgroup);
+ return 0;
+ }
+
+ session_finalize(s);
+ session_free(s);
+
+ return 0;
+}
+
static int manager_connect_bus(Manager *m) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
if (r < 0)
return log_error_errno(r, "Failed to add manager object vtable: %m");
+ /* elogind relies on signals from its release agent */
+ r = sd_bus_add_match(m->bus, NULL,
+ "type='signal',"
+ "interface='org.freedesktop.systemd1.Agent',"
+ "member='Released',"
+ "path='/org/freedesktop/systemd1/agent'",
+ signal_agent_released, m);
+
r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/login1/seat", "org.freedesktop.login1.Seat", seat_vtable, seat_object_find, m);
if (r < 0)
return log_error_errno(r, "Failed to add seat object vtable: %m");
if (r < 0)
return log_error_errno(r, "Failed to add user enumerator: %m");
-/// elogind does not support systemd action jobs
+/// elogind does not support systemd as PID 1
#if 0
r = sd_bus_add_match(m->bus,
NULL,
match_job_removed, m);
if (r < 0)
log_warning_errno(r, "Failed to add match for JobRemoved: %m");
-#endif // 0
r = sd_bus_add_match(m->bus,
NULL,
NULL, NULL);
if (r < 0)
log_notice("Failed to enable subscription: %s", bus_error_message(&error, r));
+#endif // 0
r = sd_bus_request_name(m->bus, "org.freedesktop.login1", 0);
if (r < 0)
return 0;
}
+/// UNNEEDED by elogind
+#if 0
int bus_open_user_systemd(sd_bus **_bus) {
_cleanup_bus_unref_ sd_bus *bus = NULL;
_cleanup_free_ char *ee = NULL;
return 0;
}
+#endif // 0
int bus_print_property(const char *name, sd_bus_message *property, bool all) {
char type;
switch (transport) {
case BUS_TRANSPORT_LOCAL:
+/// elogind does not support a user bus
+#if 0
if (user)
r = sd_bus_default_user(bus);
else
+#endif // 0
r = sd_bus_default_system(bus);
break;
void bus_verify_polkit_async_registry_free(Hashmap *registry);
int bus_open_system_systemd(sd_bus **_bus);
-int bus_open_user_systemd(sd_bus **_bus);
+// UNNEEDED int bus_open_user_systemd(sd_bus **_bus);
int bus_open_transport(BusTransport transport, const char *host, bool user, sd_bus **bus);
// UNNEEDED int bus_open_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus);
// UNNEEDED int sd_bus_creds_has_inheritable_cap(sd_bus_creds *c, int capability);
// UNNEEDED int sd_bus_creds_has_bounding_cap(sd_bus_creds *c, int capability);
int sd_bus_creds_get_selinux_context(sd_bus_creds *c, const char **context);
-// UNNEEDED int sd_bus_creds_get_audit_session_id(sd_bus_creds *c, uint32_t *sessionid);
+int sd_bus_creds_get_audit_session_id(sd_bus_creds *c, uint32_t *sessionid);
int sd_bus_creds_get_audit_login_uid(sd_bus_creds *c, uid_t *loginuid);
int sd_bus_creds_get_tty(sd_bus_creds *c, const char **tty);
// UNNEEDED int sd_bus_creds_get_unique_name(sd_bus_creds *c, const char **name);