10 Commits

Author SHA1 Message Date
grialion
583ff1ae0c chore: clang-format 2026-01-17 17:18:38 +01:00
grialion
9253381d72 fix: fflush(NULL) before fork 2026-01-17 17:15:04 +01:00
grialion
424b3930a2 fix(launch-state): replace last newline with nullbyte 2026-01-17 17:12:16 +01:00
grialion
9ce7cd61e1 fix: search Xorg binary in PATH 2026-01-17 17:11:23 +01:00
grialion
b0d0a2e890 fix: use pid_t for compatibility
pid_t is supported on musl, unlike __pid_t
2026-01-17 13:09:37 +01:00
grialion
ded6a119b0 fix: clang warnings 2026-01-17 12:43:45 +01:00
grialion
d07395c325 fix: handle SIGTERM for graceful graphical shutdown
Sending SIGTERM to X.org is necessary to avoid graphical lock-up.
2026-01-17 12:34:05 +01:00
grialion
e615968a17 chore: clang-format 2026-01-17 11:45:33 +01:00
grialion
d65ed54578 fix: color reset after returning from session 2026-01-17 11:45:03 +01:00
grialion
b2019c7934 feat: wait for X DISPLAY before starting session
Launch X.org with a pipe fd where it will output the DISPLAY number,
once ready.
2026-01-17 11:40:49 +01:00
9 changed files with 155 additions and 75 deletions

View File

@@ -17,10 +17,10 @@ LDFLAGS?=-Wl,--gc-sections
LIBS=-lpam LIBS=-lpam
_DEPS = version.h log.h util.h ui.h ui_state.h config.h pam.h desktop.h desktop_exec.h auth.h ofield.h efield.h keys.h users.h sessions.h chvt.h macros.h launch_state.h _DEPS = version.h log.h util.h ui.h ui_state.h config.h pam.h desktop.h desktop_exec.h auth.h ofield.h efield.h keys.h users.h sessions.h chvt.h macros.h launch_state.h signal_handler.h
DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS)) DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS))
_OBJ = main.o log.o util.o ui.o ui_state.o config.o pam.o desktop.o desktop_exec.o auth.o ofield.o efield.o users.o sessions.o chvt.o launch_state.o _OBJ = main.o log.o util.o ui.o ui_state.o config.o pam.o desktop.o desktop_exec.o auth.o ofield.o efield.o users.o sessions.o chvt.o launch_state.o signal_handler.o
OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ)) OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ))
INFO_GIT_REV?=$$(git describe --long --tags --always || echo '?') INFO_GIT_REV?=$$(git describe --long --tags --always || echo '?')

View File

@@ -6,6 +6,7 @@
#include "macros.h" #include "macros.h"
char* NULLABLE search_path(const char* NNULLABLE for_binary);
int execvpe_desktop(char** args, char* NNULLABLE* NNULLABLE envlist); int execvpe_desktop(char** args, char* NNULLABLE* NNULLABLE envlist);
int parse_exec_string(const char* exec_s, int* arg_count, char*** args); int parse_exec_string(const char* exec_s, int* arg_count, char*** args);
void free_parsed_args(int arg_count, char** args); void free_parsed_args(int arg_count, char** args);

3
include/signal_handler.h Normal file
View File

@@ -0,0 +1,3 @@
// handle SIGTERM by sending SIGTERM to all children, resulting
// in a graceful graphical shutdown
void setup_sigterm();

View File

@@ -23,9 +23,7 @@
#include "ui.h" #include "ui.h"
#include "util.h" #include "util.h"
#define DEFAULT_XORG_DISPLAY 0 #define XORG_MESSAGE_LENGTH 16
// no PATH search for now
#define XORG_COMMAND "/usr/bin/X"
static void try_source_file(struct Vector* NNULLABLE vec_envlist, static void try_source_file(struct Vector* NNULLABLE vec_envlist,
char* NNULLABLE filepath) { char* NNULLABLE filepath) {
@@ -98,64 +96,121 @@ struct child_msg {
bool err; bool err;
}; };
// TODO: OR check if xorg_pid fail exited /// block until X returns the display number or an error occurs
static int wait_for_x_ready(const int xorg_pipefd[1], __pid_t xorg_pid) { static bool x_get_display(const int xorg_pipefd[2], int* display) {
// TODO char buffer[XORG_MESSAGE_LENGTH];
UNUSED(xorg_pipefd); bool status;
UNUSED(xorg_pid);
sleep(2); close(xorg_pipefd[1]);
return 0; ssize_t bytes_read = read(xorg_pipefd[0], buffer, sizeof(buffer) - 1);
buffer[bytes_read] = '\0';
if (bytes_read > 0) {
char* endptr;
int val = (int)strtol(buffer, &endptr, 10);
if (endptr == buffer) {
(void)fputs("failed to parse Xorg display response\n", stderr);
status = false;
} else {
*display = val;
status = true;
}
} else if (bytes_read == 0) {
(void)fputs("Xorg pipe closed\n", stderr);
status = false;
} else {
perror("read");
status = false;
}
close(xorg_pipefd[0]);
return status;
}
/// small helper to push dyn arr
static void push_dyn_arr(void*** arr, void* item) {
struct Vector vec = vec_from_raw(*arr);
vec_push(&vec, item);
*arr = vec_as_raw(vec);
} }
// TODO: properly pass this down // TODO: properly pass this down
extern int vt; extern int vt;
// TODO: add error msgs
static void launch_with_xorg_server(struct session_exec* NNULLABLE exec, static void start_xorg_server(struct passwd* pw, char** NNULLABLE envlist,
struct passwd* pw, int xorg_pipefd[2]) {
char** NNULLABLE envlist) { close(xorg_pipefd[0]);
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); if (!pw->pw_dir) _exit(EXIT_FAILURE);
// !!!!!!!!!! ATTENTION: this fails silently, of course add failure msgs but // !!!!!!!!!! ATTENTION: this fails silently, of course add failure msgs but
// for now I can't so be careful // for now I can't so be careful
if (vt == -1) _exit(EXIT_FAILURE); if (vt == -1) _exit(EXIT_FAILURE);
char* display_thing; // pass the pipe so Xorg can write the DISPLAY value in there
asprintf(&display_thing, ":%d", DEFAULT_XORG_DISPLAY); char* fd_str;
if (!display_thing) _exit(EXIT_FAILURE); asprintf(&fd_str, "%d", xorg_pipefd[1]);
if (!fd_str) _exit(EXIT_FAILURE);
char* vt_path; char* vt_path;
asprintf(&vt_path, "vt%d", vt); asprintf(&vt_path, "vt%d", vt);
if (!vt_path) { if (!vt_path) {
free(display_thing); free(fd_str);
_exit(EXIT_FAILURE); _exit(EXIT_FAILURE);
} }
// dup2(xorg_pipefd[1], STDERR_FILENO); char* xorg_path = search_path("Xorg");
// dup2(xorg_pipefd[1], STDOUT_FILENO); if (!xorg_path) {
// close(xorg_pipefd[0]); (void)fputs("couldn't find Xorg binary in PATH, sure it's installed?\n",
// close(xorg_pipefd[1]); stderr);
_exit(EXIT_FAILURE);
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); int exit = execle(xorg_path, xorg_path, "-displayfd", fd_str, vt_path, NULL,
envlist);
perror("exec");
__pid_t xorg_session_pid = fork(); free(vt_path);
free(fd_str);
free(xorg_path);
_exit(exit);
}
// TODO: add error msgs
/// returns on completion
static void launch_with_xorg_server(struct session_exec* NNULLABLE exec,
struct passwd* pw,
char** NNULLABLE envlist) {
int xorg_pipefd[2];
if (pipe(xorg_pipefd) == -1) _exit(EXIT_FAILURE);
(void)fflush(NULL);
pid_t xorg_pid = fork();
if (xorg_pid == 0) {
start_xorg_server(pw, envlist, xorg_pipefd);
}
int display = 0;
if (!x_get_display(xorg_pipefd, &display)) {
(void)fputs("failed to get X display, aborting\n", stderr);
int status;
waitpid(xorg_pid, &status, 0);
_exit(EXIT_FAILURE);
}
char* display_env;
asprintf(&display_env, "DISPLAY=:%d", display);
if (!display_env) {
(void)fputs("failure allocating memory for DISPLAY string\n", stderr);
_exit(EXIT_FAILURE);
}
// convert back for convenient push-ing
push_dyn_arr((void***)&envlist, display_env);
if (!envlist) {
(void)fputs("failure allocating memory for DISPLAY env\n", stderr);
_exit(EXIT_FAILURE);
}
(void)fflush(NULL);
pid_t xorg_session_pid = fork();
if (xorg_session_pid == 0) { if (xorg_session_pid == 0) {
int exit = session_exec_exec(exec, envlist); int exit = session_exec_exec(exec, envlist);
perror("exec error"); perror("exec error");
@@ -165,20 +220,16 @@ static void launch_with_xorg_server(struct session_exec* NNULLABLE exec,
// looks weird, waiting on -1 should wait on any child and then just check if // 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 // its xorg server or the session and kill the other waiting on it
__pid_t pid; pid_t pid;
int status; // not even read for now int status; // not even read for now
while ((pid = waitpid(-1, &status, 0)) > 0) { while ((pid = waitpid(-1, &status, 0)) > 0) {
if (pid == xorg_pid || pid == xorg_session_pid) { if (pid == xorg_pid || pid == xorg_session_pid) {
__pid_t pid_to_kill = pid ^ xorg_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_pid) printf("Xorg server died\n");
if (pid == xorg_session_pid) printf("Xorg session died\n"); if (pid == xorg_session_pid) printf("Xorg session died\n");
kill(pid_to_kill, SIGTERM); kill(pid_to_kill, SIGTERM);
waitpid(pid_to_kill, &status, 0); 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; break;
} }
@@ -218,6 +269,7 @@ inline static void forked(int pipefd[2], struct passwd* pw,
if (session->type == XORG) { if (session->type == XORG) {
launch_with_xorg_server(&session->exec, pw, envlist); launch_with_xorg_server(&session->exec, pw, envlist);
_exit(EXIT_SUCCESS);
} else { } else {
int exit = session_exec_exec(&session->exec, envlist); int exit = session_exec_exec(&session->exec, envlist);
perror("exec error"); perror("exec error");
@@ -260,16 +312,6 @@ bool launch(char* user, char* passwd, struct session session, void (*cb)(void),
struct Vector envlist_vec = vec_from_raw((void**)env_ret.envlist); 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, source_paths(&envlist_vec, &config->behavior.source, pw->pw_dir,
&config->behavior.user_source); &config->behavior.user_source);
char** envlist = (char**)vec_as_raw(envlist_vec); char** envlist = (char**)vec_as_raw(envlist_vec);

View File

@@ -17,7 +17,7 @@
// returns NULL on any error // returns NULL on any error
// otherwise it returns the absolute path of the program that MUST BE FREED // otherwise it returns the absolute path of the program that MUST BE FREED
static char* NULLABLE search_path(const char* NNULLABLE for_binary) { char* NULLABLE search_path(const char* NNULLABLE for_binary) {
if (strchr(for_binary, '/') != NULL) { if (strchr(for_binary, '/') != NULL) {
// skip absolute paths // skip absolute paths
return strdup(for_binary); return strdup(for_binary);

View File

@@ -21,19 +21,17 @@ int read_launch_state(struct LaunchState* NNULLABLE state) {
}; };
size_t num = 0; size_t num = 0;
if (getline(&state->username, &num, state_fd) < 0) goto fail; ssize_t chars = getline(&state->username, &num, state_fd);
// not sure I can actually prove it but ughh, getline < 0 will ensure there's if (chars < 0) goto fail;
// something in the string and then I think strcspn is bounded to the final if (state->username[chars] == '\n') state->username[chars] = 0;
// null byte, so it shouldn't go over
// NOLINTNEXTLINE(clang-analyzer-security.ArrayBound)
state->username[strcspn(state->username, "\n")] = 0;
num = 0; num = 0;
if (getline(&state->session_opt, &num, state_fd) < 0) { chars = getline(&state->session_opt, &num, state_fd);
if (chars < 0) {
free(state->session_opt); free(state->session_opt);
goto fail; goto fail;
} }
state->session_opt[strcspn(state->session_opt, "\n")] = 0; if (state->session_opt[chars] == '\n') state->session_opt[chars] = 0;
(void)fclose(state_fd); (void)fclose(state_fd);
return 0; return 0;

View File

@@ -12,12 +12,14 @@
#include "log.h" #include "log.h"
#include "macros.h" #include "macros.h"
#include "sessions.h" #include "sessions.h"
#include "signal_handler.h"
#include "ui.h" #include "ui.h"
#include "users.h" #include "users.h"
#include "util.h" #include "util.h"
#include "version.h" #include "version.h"
#define DATESTR_MAXBUFSIZE 0x20 #define DATESTR_MAXBUFSIZE 0x20
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
// Logger // Logger
char* log_output = getenv("LIDM_LOG"); char* log_output = getenv("LIDM_LOG");
@@ -85,6 +87,8 @@ int main(int argc, char* argv[]) {
struct Vector users = get_human_users(); struct Vector users = get_human_users();
struct Vector sessions = get_avaliable_sessions(); struct Vector sessions = get_avaliable_sessions();
setup_sigterm();
int ret = load(&users, &sessions); int ret = load(&users, &sessions);
if (ret == 0) execl(argv[0], argv[0], NULL); if (ret == 0) execl(argv[0], argv[0], NULL);
} }

27
src/signal_handler.c Normal file
View File

@@ -0,0 +1,27 @@
#include <errno.h>
#include <stddef.h>
#include <sys/wait.h>
#include <unistd.h>
static void handle_sigterm(int sig) {
(void)sig;
(void)signal(SIGTERM, SIG_IGN);
kill(-getpgrp(), SIGTERM);
int status;
while (waitpid(-1, &status, 0) > 0 || errno == EINTR) {
}
_exit(0);
}
void setup_sigterm() {
setpgid(0, 0);
struct sigaction sa;
sa.sa_handler = handle_sigterm;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGTERM, &sa, NULL);
}

View File

@@ -186,6 +186,7 @@ void ui_update_ofield(struct opts_field* NNULLABLE self) {
ui_update_field(input); ui_update_field(input);
} }
/// draw everything
void scratch_print_ui() { void scratch_print_ui() {
ioctl(STDOUT_FILENO, TIOCGWINSZ, &window); ioctl(STDOUT_FILENO, TIOCGWINSZ, &window);
box_start = (struct uint_point){ box_start = (struct uint_point){
@@ -201,7 +202,7 @@ void scratch_print_ui() {
return; return;
} }
printf("\033[2J\033[H"); // Clear screen printf("\033[2J\033[H\033c"); // Clear screen
/// PRINTING /// PRINTING
// printf box // printf box
@@ -313,6 +314,8 @@ int load(struct Vector* users, struct Vector* sessions) {
&restore_all, g_config)) { &restore_all, g_config)) {
print_passwd(utf8len(of_passwd.efield.content), true); print_passwd(utf8len(of_passwd.efield.content), true);
ui_update_cursor_focus(); ui_update_cursor_focus();
} else {
scratch_print_ui();
} }
} else if (ansi_key == A_UP || ansi_key == A_DOWN) { } else if (ansi_key == A_UP || ansi_key == A_DOWN) {
st_ch_focus(ansi_key == A_DOWN ? 1 : -1); st_ch_focus(ansi_key == A_DOWN ? 1 : -1);
@@ -336,6 +339,8 @@ int load(struct Vector* users, struct Vector* sessions) {
&restore_all, g_config)) { &restore_all, g_config)) {
print_passwd(utf8len(of_passwd.efield.content), true); print_passwd(utf8len(of_passwd.efield.content), true);
ui_update_cursor_focus(); ui_update_cursor_focus();
} else {
scratch_print_ui();
} }
} else } else
st_kbd_type(seq, g_config->behavior.include_defshell); st_kbd_type(seq, g_config->behavior.include_defshell);