#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/vt.h>
+#include <string.h>
#include "logind-seat.h"
+#include "logind-acl.h"
#include "util.h"
Seat *seat_new(Manager *m, const char *id) {
}
s->id = file_name_from_path(s->state_file);
+ s->manager = m;
if (hashmap_put(m->seats, s->id, s) < 0) {
- free(s->id);
+ free(s->state_file);
free(s);
return NULL;
}
- s->manager = m;
-
return s;
}
void seat_free(Seat *s) {
assert(s);
+ if (s->in_gc_queue)
+ LIST_REMOVE(Seat, gc_queue, s->manager->seat_gc_queue, s);
+
while (s->sessions)
session_free(s->sessions);
hashmap_remove(s->manager->seats, s->id);
- free(s->state_file);
+ if (s->state_file) {
+ unlink(s->state_file);
+ free(s->state_file);
+ }
+
free(s);
}
int seat_save(Seat *s) {
- FILE *f;
int r;
+ FILE *f;
+ char *temp_path;
assert(s);
r = safe_mkdir("/run/systemd/seat", 0755, 0, 0);
if (r < 0)
- return r;
+ goto finish;
- f = fopen(s->state_file, "we");
- if (!f)
- return -errno;
+ r = fopen_temporary(s->state_file, &f, &temp_path);
+ if (r < 0)
+ goto finish;
+
+ fchmod(fileno(f), 0644);
fprintf(f,
+ "# This is private data. Do not parse.\n"
"IS_VTCONSOLE=%i\n",
s->manager->vtconsole == s);
if (s->sessions) {
Session *i;
- fputs("OTHER_UIDS=", f);
+ fputs("OTHER=", f);
LIST_FOREACH(sessions_by_seat, i, s->sessions) {
- assert(i->user);
+ if (i == s->active)
+ continue;
+ fprintf(f,
+ "%s%c",
+ i->id,
+ i->sessions_by_seat_next ? ' ' : '\n');
+ }
+
+ fputs("OTHER_UIDS=", f);
+ LIST_FOREACH(sessions_by_seat, i, s->sessions) {
if (i == s->active)
continue;
fprintf(f,
- "%s%lu",
- i == s->sessions ? "" : " ",
- (unsigned long) i->user->uid);
+ "%lu%c",
+ (unsigned long) i->user->uid,
+ i->sessions_by_seat_next ? ' ' : '\n');
}
}
fflush(f);
- if (ferror(f)) {
+
+ if (ferror(f) || rename(temp_path, s->state_file) < 0) {
r = -errno;
unlink(s->state_file);
+ unlink(temp_path);
}
fclose(f);
+ free(temp_path);
+
+finish:
+ if (r < 0)
+ log_error("Failed to save seat data for %s: %s", s->id, strerror(-r));
+
return r;
}
return r;
}
-int seat_preallocate_vts(Seat *s) {
+static int seat_preallocate_vts(Seat *s) {
int i, r = 0;
assert(s);
int q;
q = vt_allocate(i);
- if (r >= 0 && q < 0)
+ if (q < 0) {
+ log_error("Failed to preallocate VT %i: %s", i, strerror(-q));
r = q;
+ }
}
return r;
}
-int seat_apply_acls(Seat *s) {
+int seat_apply_acls(Seat *s, Session *old_active) {
+ int r;
+
assert(s);
+ r = devnode_acl_all(s->manager->udev,
+ s->id,
+ false,
+ !!old_active, old_active ? old_active->user->uid : 0,
+ !!s->active, s->active ? s->active->user->uid : 0);
- return 0;
+ if (r < 0)
+ log_error("Failed to apply ACLs: %s", strerror(-r));
+
+ return r;
}
-static int vt_is_busy(int vtnr) {
- struct vt_stat vt_stat;
- int r = 0, fd;
+int seat_active_vt_changed(Seat *s, int vtnr) {
+ Session *i, *new_active = NULL, *old_active;
+ assert(s);
assert(vtnr >= 1);
- fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return -errno;
+ if (s->manager->vtconsole != s)
+ return -EINVAL;
- if (ioctl(fd, VT_GETSTATE, &vt_stat) < 0)
- r = -errno;
- else
- r = !!(vt_stat.v_state & (1 << vtnr));
+ log_debug("VT changed to %i", vtnr);
- close_nointr_nofail(fd);
+ LIST_FOREACH(sessions_by_seat, i, s->sessions)
+ if (i->vtnr == vtnr) {
+ new_active = i;
+ break;
+ }
- return r;
+ if (new_active == s->active)
+ return 0;
+
+ old_active = s->active;
+ s->active = new_active;
+
+ seat_apply_acls(s, old_active);
+ manager_spawn_autovt(s->manager, vtnr);
+
+ return 0;
}
-void seat_active_vt_changed(Seat *s, int vtnr) {
- Session *i;
+int seat_read_active_vt(Seat *s) {
+ char t[64];
+ ssize_t k;
+ int r, vtnr;
assert(s);
- assert(vtnr >= 1);
- assert(s->manager->vtconsole == s);
- s->active = NULL;
+ if (s->manager->vtconsole != s)
+ return 0;
- LIST_FOREACH(sessions_by_seat, i, s->sessions)
- if (i->vtnr == vtnr) {
- s->active = i;
- break;
- }
+ lseek(s->manager->console_active_fd, SEEK_SET, 0);
+
+ k = read(s->manager->console_active_fd, t, sizeof(t)-1);
+ if (k <= 0) {
+ log_error("Failed to read current console: %s", k < 0 ? strerror(-errno) : "EOF");
+ return k < 0 ? -errno : -EIO;
+ }
+
+ t[k] = 0;
+ truncate_nl(t);
+
+ if (!startswith(t, "tty")) {
+ log_error("Hm, /sys/class/tty/tty0/active is badly formatted.");
+ return -EIO;
+ }
+
+ r = safe_atoi(t+3, &vtnr);
+ if (r < 0) {
+ log_error("Failed to parse VT number %s", t+3);
+ return r;
+ }
+
+ if (vtnr <= 0) {
+ log_error("VT number invalid: %s", t+3);
+ return -EIO;
+ }
+
+ return seat_active_vt_changed(s, vtnr);
+}
+
+int seat_start(Seat *s) {
+ assert(s);
- seat_apply_acls(s);
+ /* Initialize VT magic stuff */
+ seat_preallocate_vts(s);
- if (vt_is_busy(vtnr) == 0)
- manager_spawn_autovt(s->manager, vtnr);
+ /* Read current VT */
+ seat_read_active_vt(s);
+
+ /* Save seat data */
+ seat_save(s);
+
+ return 0;
}
int seat_stop(Seat *s) {
Session *session;
- int r = 0;
+ int r = 0, k;
assert(s);
LIST_FOREACH(sessions_by_seat, session, s->sessions) {
- int k;
-
k = session_stop(session);
if (k < 0)
r = k;
}
+ seat_save(s);
+ seat_add_to_gc_queue(s);
+
return r;
}
+
+int seat_check_gc(Seat *s) {
+ assert(s);
+
+ if (s->manager->vtconsole == s)
+ return 1;
+
+ return !!s->devices;
+}
+
+void seat_add_to_gc_queue(Seat *s) {
+ assert(s);
+
+ if (s->in_gc_queue)
+ return;
+
+ LIST_PREPEND(Seat, gc_queue, s->manager->seat_gc_queue, s);
+ s->in_gc_queue = true;
+}