feat: xorg sessions work

leaves a few TODOs:
* error handling
* properly passing VT around
* `wait_for_x_ready`
* allow compile-time overriding `DEFAULT_XORG_DISPLAY` and `XORG_COMMAND`
This commit is contained in:
2025-09-03 14:50:39 +02:00
parent 570a68c586
commit 64a1368717
5 changed files with 169 additions and 16 deletions

View File

@@ -1,6 +1,9 @@
#ifndef DESKTOP_EXEC_H_
#define DESKTOP_EXEC_H_
#include "macros.h"
int execvpe_desktop(char** args, char* NNULLABLE* NNULLABLE envlist);
int parse_exec_string(const char* exec_s, int* arg_count, char*** args);
void free_parsed_args(int arg_count, char** args);

View File

@@ -4,6 +4,7 @@
#include <sys/types.h>
#include <unistd.h>
#include "desktop_exec.h"
#include "macros.h"
#include "util.h"
@@ -56,7 +57,7 @@ static inline int session_exec_exec(struct session_exec* NNULLABLE exec,
case EXEC_SHELL:
return execle(exec->shell, exec->shell, NULL, envlist);
case EXEC_DESKTOP:
return execve(exec->desktop.args[0], exec->desktop.args, envlist);
return execvpe_desktop(exec->desktop.args, envlist);
default:
__builtin_unreachable();
}

View File

@@ -1,23 +1,32 @@
// TODO: handle `fork() == -1`s
#include <errno.h>
#include <grp.h>
#include <pwd.h>
#include <security/pam_misc.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include "auth.h"
#include "config.h"
#include "log.h"
#include "macros.h"
#include "pam.h"
#include "sessions.h"
#include "ui.h"
#include "util.h"
#define DEFAULT_XORG_DISPLAY 0
// no PATH search for now
#define XORG_COMMAND "/usr/bin/X"
static void try_source_file(struct Vector* NNULLABLE vec_envlist,
char* NNULLABLE filepath) {
log_printf("sourcing %s\n", filepath);
@@ -89,6 +98,93 @@ struct child_msg {
bool err;
};
// TODO: OR check if xorg_pid fail exited
static int wait_for_x_ready(const int xorg_pipefd[1], __pid_t xorg_pid) {
// TODO
UNUSED(xorg_pipefd);
UNUSED(xorg_pid);
sleep(2);
return 0;
}
// TODO: properly pass this down
extern int vt;
// TODO: add error msgs
static void launch_with_xorg_server(struct session_exec* NNULLABLE exec,
struct passwd* pw,
char** NNULLABLE envlist) {
int xorg_pipefd[2];
pipe(xorg_pipefd);
(void)fflush(NULL);
__pid_t xorg_pid = fork();
if (xorg_pid == 0) {
if (!pw->pw_dir) _exit(EXIT_FAILURE);
// !!!!!!!!!! ATTENTION: this fails silently, of course add failure msgs but
// for now I can't so be careful
if (vt == -1) _exit(EXIT_FAILURE);
char* display_thing;
asprintf(&display_thing, ":%d", DEFAULT_XORG_DISPLAY);
if (!display_thing) _exit(EXIT_FAILURE);
char* vt_path;
asprintf(&vt_path, "vt%d", vt);
if (!vt_path) {
free(display_thing);
_exit(EXIT_FAILURE);
}
// dup2(xorg_pipefd[1], STDERR_FILENO);
// dup2(xorg_pipefd[1], STDOUT_FILENO);
// close(xorg_pipefd[0]);
// close(xorg_pipefd[1]);
int exit = execle(XORG_COMMAND, XORG_COMMAND, display_thing, vt_path, NULL,
envlist);
perror("exec");
// execle("X", "X", display_thing, vt_path, "-auth", xauth_path,
// "-nolisten", "tcp", "-background", "none", NULL, envlist);
printf("wtf3\n");
(void)fflush(stdout);
free(vt_path);
free(display_thing);
_exit(exit);
}
wait_for_x_ready(xorg_pipefd, xorg_pid);
__pid_t xorg_session_pid = fork();
if (xorg_session_pid == 0) {
int exit = session_exec_exec(exec, envlist);
perror("exec error");
(void)fputs("failure calling session\n", stderr);
_exit(exit);
}
// looks weird, waiting on -1 should wait on any child and then just check if
// its xorg server or the session and kill the other waiting on it
__pid_t pid;
int status; // not even read for now
while ((pid = waitpid(-1, &status, 0)) > 0) {
if (pid == xorg_pid || pid == xorg_session_pid) {
__pid_t pid_to_kill = pid ^ xorg_pid ^ xorg_session_pid;
if (pid == xorg_pid) printf("Xorg server died\n");
if (pid == xorg_session_pid) printf("Xorg session died\n");
kill(pid_to_kill, SIGTERM);
waitpid(pid_to_kill, &status, 0);
printf("wtf %d, x%d s%d - k%d\n", status, xorg_pid, xorg_session_pid,
pid_to_kill);
(void)fflush(stdout);
sleep(10);
break;
}
}
}
#define SEND_MSG(MSG) \
{ \
write(pipefd[1], &(MSG), sizeof(struct child_msg)); \
@@ -109,23 +205,25 @@ struct child_msg {
}
inline static void forked(int pipefd[2], struct passwd* pw,
char* NNULLABLE user,
struct session_exec* NNULLABLE exec,
struct session* NNULLABLE session,
char** NNULLABLE envlist) {
if (chdir(pw->pw_dir) == -1) SEND_ERR("chdir");
if (setgid(pw->pw_gid) == -1) SEND_ERR("setgid");
if (initgroups(user, pw->pw_gid) == -1) SEND_ERR("initgroups");
if (setuid(pw->pw_uid) == -1) SEND_ERR("setuid");
// or maybe Xorg fork should happen here
SEND_MSG((struct child_msg){.err = false});
DUMMY_READ();
close(pipefd[0]);
int exit = session_exec_exec(exec, envlist);
perror("exec error");
(void)fputs("failure calling session\n", stderr);
_exit(exit);
if (session->type == XORG) {
launch_with_xorg_server(&session->exec, pw, envlist);
} else {
int exit = session_exec_exec(&session->exec, envlist);
perror("exec error");
(void)fputs("failure calling session\n", stderr);
_exit(exit);
}
}
#undef SEND_MSG
#undef SEND_ERR
@@ -160,6 +258,17 @@ bool launch(char* user, char* passwd, struct session session, void (*cb)(void),
}
struct Vector envlist_vec = vec_from_raw((void**)env_ret.envlist);
if (session.type == XORG) {
char* display_env;
asprintf(&display_env, "DISPLAY=:%d", DEFAULT_XORG_DISPLAY);
if (!display_env) {
print_err("alloc error");
return false;
}
vec_push(&envlist_vec, display_env);
}
source_paths(&envlist_vec, &config->behavior.source, pw->pw_dir,
&config->behavior.user_source);
char** envlist = (char**)vec_as_raw(envlist_vec);
@@ -168,19 +277,12 @@ bool launch(char* user, char* passwd, struct session session, void (*cb)(void),
return false;
}
// TODO: start X server here if needed
// e.g. spawn (also after downgrading privs):
//
// `X :0 tty<X> -auth <user-home>/.Xauthority -nolisten tcp -background none`
//
// Then `DISPLAY=:0 <xsession>`
int pipefd[2];
pipe(pipefd);
uint pid = fork();
if (pid == 0)
forked(pipefd, pw, user, &session.exec, envlist);
forked(pipefd, pw, user, &session, envlist);
else {
struct child_msg msg;
read(pipefd[0], &msg, sizeof(struct child_msg));

View File

@@ -24,8 +24,10 @@ int chvt_str(char* str) {
return chvt((int)i);
}
int vt = -1;
int chvt(int n) {
(void)fprintf(stderr, "activating vt %d\n", n);
vt = n;
// NOLINTNEXTLINE(readability-identifier-length)
char c = 0;
for (size_t i = 0; i < LEN(vterms); i++) {

View File

@@ -1,14 +1,59 @@
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include "desktop_exec.h"
#include "macros.h"
// constants for exec string parsing
#define MAX_ARGS 100
// ARG_LENGTH is the initial length of a parsed argument
#define ARG_LENGTH 64
// returns NULL on any error
// otherwise it returns the absolute path of the program that MUST BE FREED
static char* NULLABLE search_path(const char* NNULLABLE for_binary) {
char* path = strdup(getenv("PATH"));
if (!path) return NULL;
char* tok = strtok(path, ":");
while (tok) {
char* bin_path;
asprintf(&bin_path, "%s/%s", tok, for_binary);
if (!bin_path) {
free(path);
return NULL;
}
struct stat stat_buf;
if (stat(bin_path, &stat_buf) == 0) {
// TODO: check exec bit ig
// if(stat_buf.) {}
free(path);
return bin_path;
}
free(bin_path);
tok = strtok(NULL, ":");
}
return NULL;
}
// returns -1 on exec failure and -2 on search failure
int execvpe_desktop(char** args, char* NNULLABLE* NNULLABLE envlist) {
char* new_arg = search_path(args[0]);
if (!new_arg) return -2;
free(args[0]);
args[0] = new_arg;
return execve(args[0], args, envlist);
}
// parse Exec=/bin/prog arg1 arg2\ with\ spaces
void free_parsed_args(int arg_count, char** args) {
if (!args) return;