Nimbin[12]?Sway / seatd-user / seatd-user.c

seatd-user files

seatd launcher for a user

seatd wayland c · oldest file 2026-10-05 · last change 2026-10-05 (4 days ago) · synced 3 days ago

C 100%
wget https://git.christianimmanuel.de/sway/seatd-user/archive/seatd-user.tar.gz
seatd-user.c 2.7 KB · 107 lines raw
/*
 * seatd-user.c - minimal setuid-root wrapper to start/stop seatd for one user
 *
 * build:   u=n76310; gcc -O2 -Wall -Wextra -DSEATD_USER="$u" seatd-user.c -o seatd-$u
 * install: chown root:$u seatd-$u; chmod 4750 seatd-$u; mv seatd-$u /usr/bin/
 *
 * Only the group owner (the user) may execute it. seatd runs as root and
 * hands its socket to SEATD_USER.
 */

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#define STRINGIFY(x)  #x
#define STRINGIFY2(x) STRINGIFY(x)

#ifndef SEATD_USER
#error "SEATD_USER not defined, build with -DSEATD_USER=<user>"
#endif
#define SEATD_USER_STRING STRINGIFY2(SEATD_USER)

#define SEATD_BIN "/usr/bin/seatd"
#define PKILL_BIN "/usr/bin/pkill"

/* fixed environment, nothing from the caller leaks into root context */
static char *const clean_env[] = { "PATH=/usr/bin:/bin", NULL };

/* make sure 0/1/2 are open so nothing we open later lands on them */
static void secure_std_fds(void)
{
    for (int fd = 0; fd <= 2; fd++) {
        if (fcntl(fd, F_GETFD) == -1 && errno == EBADF) {
            int n = open("/dev/null", fd == 0 ? O_RDONLY : O_WRONLY);
            if (n != fd)
                _exit(1);
        }
    }
}

static int become_root(void)
{
    if (setgid(0) != 0 || setuid(0) != 0) {
        fprintf(stderr, "not running setuid root: %s\n", strerror(errno));
        return -1;
    }
    return 0;
}

static int start_seatd(void)
{
    char *const argv[] = { "seatd", "-u", SEATD_USER_STRING, NULL };

    execve(SEATD_BIN, argv, clean_env);
    fprintf(stderr, "exec %s: %s\n", SEATD_BIN, strerror(errno));
    return 1;
}

static int stop_seatd(void)
{
    char *const argv[] = { "pkill", "-x", "seatd", NULL };
    int status;
    pid_t pid = fork();

    if (pid < 0) {
        fprintf(stderr, "fork: %s\n", strerror(errno));
        return 1;
    }
    if (pid == 0) {
        execve(PKILL_BIN, argv, clean_env);
        fprintf(stderr, "exec %s: %s\n", PKILL_BIN, strerror(errno));
        _exit(127);
    }
    if (waitpid(pid, &status, 0) < 0) {
        fprintf(stderr, "waitpid: %s\n", strerror(errno));
        return 1;
    }
    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
        fprintf(stderr, "no running seatd or failed to stop it\n");
        return 1;
    }
    return 0;
}

int main(int argc, char *argv[])
{
    secure_std_fds();

    if (argc != 2) {
        fprintf(stderr, "Usage: %s start|stop\n", argv[0]);
        return 1;
    }

    if (strcmp(argv[1], "start") != 0 && strcmp(argv[1], "stop") != 0) {
        fprintf(stderr, "Invalid argument. Use 'start' or 'stop'\n");
        return 1;
    }

    if (become_root() != 0)
        return 1;

    return strcmp(argv[1], "start") == 0 ? start_seatd() : stop_seatd();
}