diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e55b19..d94d31b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ +- lidm now calls bash for logging in and sourcing profile files, other shells can be configured +- a good PATH default is fetched from `confstr` +- debug logs are now unbuffered + +# 2.0.0 + - Most stuff (most of `/etc` being a notable exception) now installs to `/usr/local` by default, check [`docs/PACKAGERS.md`](./docs/PACKAGERS.md). - Added a changelog. - Finally add proper (experimental) xorg support. diff --git a/Makefile b/Makefile index 30cc9b1..9606dad 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -VERSION = 2.0.0 +VERSION := 2.0.0 .DEFAULT_GOAL := lidm CDIR = src @@ -9,7 +9,9 @@ ODIR = dist PREFIX = /usr/local INFO_GIT_REV ?= $(shell git describe --long --tags --always || echo '?') +INFO_GIT_REV := $(INFO_GIT_REV) INFO_BUILD_TS ?= $(shell date +%s) +INFO_BUILD_TS := $(INFO_BUILD_TS) CFLAGS ?= -O3 -Wall -Wextra -fdata-sections -ffunction-sections # C PreProcessor flags, not C Plus Plus @@ -24,21 +26,19 @@ LDFLAGS ?= -Wl,--gc-sections LIBS = -lpam # includes all headers in `$(IDIR)` and compiles everything in `$(CDIR)` -DEPS = $(wildcard $(IDIR)/*.h) -_SOURCES = $(notdir $(wildcard $(CDIR)/*.c)) -OBJ = $(patsubst %.c,$(ODIR)/%.o,$(_SOURCES)) +DEPS = $(wildcard $(IDIR)/*.h $(IDIR)/**/*.h) +_SOURCES = $(wildcard $(CDIR)/*.c) $(wildcard $(CDIR)/**/*.c) +OBJ = $(patsubst $(CDIR)/%.c,$(ODIR)/%.o,$(_SOURCES)) $(ODIR)/%.o: $(CDIR)/%.c $(DEPS) - @mkdir -p $(ODIR) + @mkdir -p $(dir $@) $(CC) -c -o $@ $< $(ALLFLAGS) lidm: $(OBJ) $(CC) -o $@ $^ $(ALLFLAGS) $(LIBS) $(LDFLAGS) clean: - rm -f \ - $(ODIR)/* \ - lidm + rm -rf $(ODIR) lidm install: lidm mkdir -p ${DESTDIR}${PREFIX}/bin ${DESTDIR}${PREFIX}/share/man/man{1,5} diff --git a/assets/man/lidm-config.5 b/assets/man/lidm-config.5 index 8e4668e..e52f174 100644 --- a/assets/man/lidm-config.5 +++ b/assets/man/lidm-config.5 @@ -104,6 +104,9 @@ Specify the time format string to be displayed. Check \fBstrftime (3)\fP to know .TP \fBrefresh_rate\fP Rate (in milliseconds) at which the UI should refresh, affects clock and resize behavior. +.TP +\fBbypass_shell_login\fP +Type \fBBOOL\fP, lidm logins through a login shell (e.g. `bash`) in login mode (e.g. `arg0` being `-bash`) to load `/etc/profile/, `/etc/profile.d/` and more. If this option is enabled that's bypassed entirely. .SH "SEE ALSO" diff --git a/debug/lidm-debug b/debug/lidm-debug new file mode 100644 index 0000000..4a8018a --- /dev/null +++ b/debug/lidm-debug @@ -0,0 +1,9 @@ +type = process +command = /bin/sh -c "LIDM_LOG=/tmp/lidm.debug /sbin/agetty tty8 linux-c -n -l /usr/local/bin/lidm -o 8" +restart = true +depends-on = login.target +termsignal = HUP +smooth-recovery = true +inittab-id = 8 +inittab-line = tty8 +log-type = buffer diff --git a/docs/INSTALL.md b/docs/INSTALL.md index d958464..a2e2017 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -50,7 +50,6 @@ It is recommended to disable any other display managers that are still enabled: touch /etc/sv/lightdm/down # replace lightdm with your previous display manager (e.g., sddm, gdm) ``` - ## Fedora Thanks to @KernelFreeze there's a COPR repo available at to install lidm. () @@ -59,9 +58,9 @@ Thanks to @KernelFreeze there's a COPR repo available at +#include #include // holds also the max string buffer in itself, not dynamic struct editable_field { - u_char pos; + uint8_t pos; char content[255]; }; struct editable_field efield_new(char* content); -void efield_trim(struct editable_field* self, u_char pos); +void efield_trim(struct editable_field* self, uint8_t pos); void efield_update(struct editable_field* self, char* update); bool efield_seek(struct editable_field* self, char seek); diff --git a/include/keys.h b/include/keys.h index 6bb1338..3d753c6 100644 --- a/include/keys.h +++ b/include/keys.h @@ -1,7 +1,10 @@ #ifndef KEYSH_ #define KEYSH_ +#include +#include #include +#include enum Keys { ESC, @@ -103,4 +106,15 @@ static const struct key_mapping KEY_MAPPINGS[] = { {PAGE_DOWN, {"\x1b[6~", NULL}}, }; +struct option_keys { + bool is_some; + enum Keys key; +}; + +int find_keyname(enum Keys* at, const char* name); +struct option_keys find_ansi(const char* seq); +void read_press(uint8_t* length, char* out); +// non blocking, waits up to tv or interrupt, returns true if actually read +bool read_press_nb(uint8_t* length, char* out, struct timeval* tv); + #endif diff --git a/include/ofield.h b/include/ofield.h index 1d2c608..2d1e7a9 100644 --- a/include/ofield.h +++ b/include/ofield.h @@ -18,6 +18,6 @@ void ofield_kbd_type(struct opts_field* self, char* typed, char* empty_default); bool ofield_opts_seek(struct opts_field* self, char seek); bool ofield_seek(struct opts_field* self, char seek); -u_char ofield_display_cursor_col(struct opts_field* self, u_char maxlen); +uint8_t ofield_display_cursor_col(struct opts_field* self, uint8_t maxlen); #endif diff --git a/include/sessions.h b/include/sessions.h index cd4153b..882068a 100644 --- a/include/sessions.h +++ b/include/sessions.h @@ -4,9 +4,8 @@ #include #include -#include "desktop_exec.h" #include "macros.h" -#include "util.h" +#include "util/vec.h" enum SessionType { XORG, @@ -51,18 +50,6 @@ static inline struct session_exec session_exec_desktop( }; } -static inline int session_exec_exec(struct session_exec* NNULLABLE exec, - char* NULLABLE* NNULLABLE envlist) { - switch (exec->typ) { - case EXEC_SHELL: - return execle(exec->shell, exec->shell, NULL, envlist); - case EXEC_DESKTOP: - return execvpe_desktop(exec->desktop.args, envlist); - default: - __builtin_unreachable(); - } -} - struct session { char* NNULLABLE name; struct session_exec exec; @@ -70,5 +57,9 @@ struct session { }; struct Vector get_avaliable_sessions(); +int session_exec_exec(struct session_exec* NNULLABLE exec, + char* NULLABLE* NNULLABLE envp); +int session_exec_login_through_shell(struct session_exec* NNULLABLE exec, + char* NULLABLE* NNULLABLE envp); #endif diff --git a/include/ui.h b/include/ui.h index e41e84a..5119c32 100644 --- a/include/ui.h +++ b/include/ui.h @@ -3,7 +3,7 @@ #include "config.h" #include "ofield.h" -#include "util.h" +#include "util/vec.h" // [box_start] // ↓ @@ -40,7 +40,7 @@ #define VALUE_MAXLEN (BOX_WIDTH - VALUES_COL + 1 - BOX_HMARGIN - 2) enum Input { SESSION, USER, PASSWD }; -extern const u_char INPUTS_N; +extern const uint8_t INPUTS_N; void setup(struct config* config); int load(struct Vector* users, struct Vector* sessions); diff --git a/include/users.h b/include/users.h index 7e818fb..91c5196 100644 --- a/include/users.h +++ b/include/users.h @@ -3,7 +3,7 @@ #include -#include "util.h" +#include "util/vec.h" struct user { char* shell; diff --git a/include/util.h b/include/util.h deleted file mode 100644 index f88130c..0000000 --- a/include/util.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef UTILH_ -#define UTILH_ - -#include -#include -#include -#include -#include -#include - -#include "keys.h" - -int find_keyname(enum Keys* at, const char* name); -struct option_keys { - bool is_some; - enum Keys key; -}; -struct option_keys find_ansi(const char* seq); -void read_press(u_char* length, char* out); -// non blocking, waits up to tv or interrupt, returns true if actually read -bool read_press_nb(u_char* length, char* out, struct timeval* tv); - -// UTF8 -// -bool utf8_iscont(char byte); -size_t utf8len(const char* str); -size_t utf8len_until(const char* str, const char* until); -size_t utf8trunc(char* str, size_t n); -const char* utf8back(const char* str); -const char* utf8seek(const char* str); -const char* utf8seekn(const char* str, size_t n); - -// Vector -// -struct Vector { - uint32_t length; - uint32_t capacity; - void** pages; -}; - -struct Vector vec_from_raw(void** raw); -void** vec_as_raw(struct Vector self); -extern const struct Vector VEC_NEW; -int vec_resize(struct Vector* self, size_t size); -int vec_reserve(struct Vector* self, size_t size); -int vec_reserve_exact(struct Vector* self, size_t size); -int vec_push(struct Vector* self, void* item); -void vec_free(struct Vector* self); -void vec_clear(struct Vector* self); -void vec_reset(struct Vector* self); -void* vec_pop(struct Vector* self); // won't free it, nor shrink vec list space -void* vec_get(struct Vector* self, size_t index); - -#endif diff --git a/include/util/path.h b/include/util/path.h new file mode 100644 index 0000000..c3efeb9 --- /dev/null +++ b/include/util/path.h @@ -0,0 +1,10 @@ +#ifndef UTIL_PATH_H +#define UTIL_PATH_H + +#include "macros.h" + +char* NULLABLE search_path(const char* NNULLABLE for_binary); +int execvpe(const char* NNULLABLE file, char* NULLABLE const argv[NNULLABLE], + char* NULLABLE const envp[NNULLABLE]); + +#endif /* UTIL_PATH_H */ diff --git a/include/util/utf8.h b/include/util/utf8.h new file mode 100644 index 0000000..40c911b --- /dev/null +++ b/include/util/utf8.h @@ -0,0 +1,15 @@ +#ifndef UTIL_UTF8_H +#define UTIL_UTF8_H + +#include +#include + +bool utf8_iscont(char byte); +size_t utf8len(const char* str); +size_t utf8len_until(const char* str, const char* until); +size_t utf8trunc(char* str, size_t n); +const char* utf8back(const char* str); +const char* utf8seek(const char* str); +const char* utf8seekn(const char* str, size_t n); + +#endif /* UTIL_UTF8_H */ diff --git a/include/util/vec.h b/include/util/vec.h new file mode 100644 index 0000000..b8f6da7 --- /dev/null +++ b/include/util/vec.h @@ -0,0 +1,26 @@ +#ifndef UTIL_VEC_H +#define UTIL_VEC_H + +#include +#include + +struct Vector { + uint32_t length; + uint32_t capacity; + void** pages; +}; + +struct Vector vec_from_raw(void** raw); +void** vec_as_raw(struct Vector self); +extern const struct Vector VEC_NEW; +int vec_resize(struct Vector* self, size_t size); +int vec_reserve(struct Vector* self, size_t size); +int vec_reserve_exact(struct Vector* self, size_t size); +int vec_push(struct Vector* self, void* item); +void vec_free(struct Vector* self); +void vec_clear(struct Vector* self); +void vec_reset(struct Vector* self); +void* vec_pop(struct Vector* self); // won't free it, nor shrink vec list space +void* vec_get(struct Vector* self, size_t index); + +#endif /* UTIL_VEC_H */ diff --git a/src/auth.c b/src/auth.c index 0be732b..3c5888d 100644 --- a/src/auth.c +++ b/src/auth.c @@ -1,10 +1,11 @@ -// TODO: handle `fork() == -1`// TODO: handle `fork() == -1`s +// TODO: handle `fork() == -1` #include #include #include #include #include +#include #include #include #include @@ -21,7 +22,8 @@ #include "pam.h" #include "sessions.h" #include "ui.h" -#include "util.h" +#include "util/path.h" +#include "util/vec.h" #define XORG_MESSAGE_LENGTH 16 @@ -80,16 +82,6 @@ static void source_paths(struct Vector* NNULLABLE vec_envlist, } } -/*char *buf;*/ -/*size_t bsize = snprintf(NULL, 0, "/run/user/%d", pw->pw_uid) + 1;*/ -/*buf = malloc(bsize);*/ -/*snprintf(buf, bsize, "/run/user/%d", pw->pw_uid);*/ -/*setenv("XDG_RUNTIME_DIR", buf, true);*/ -/*setenv("XDG_SESSION_CLASS", "user", true);*/ -/*setenv("XDG_SESSION_ID", "1", true);*/ -/*setenv("XDG_SESSION_DESKTOP", , true);*/ -/*setenv("XDG_SEAT", "seat0", true);*/ - struct child_msg { char* msg; int _errno; @@ -176,7 +168,8 @@ static void start_xorg_server(struct passwd* pw, char** NNULLABLE envlist, // TODO: add error msgs /// returns on completion -static void launch_with_xorg_server(struct session_exec* NNULLABLE exec, +static void launch_with_xorg_server(struct config* config, + struct session_exec* NNULLABLE exec, struct passwd* pw, char** NNULLABLE envlist) { int xorg_pipefd[2]; @@ -212,10 +205,14 @@ static void launch_with_xorg_server(struct session_exec* NNULLABLE exec, (void)fflush(NULL); pid_t xorg_session_pid = fork(); if (xorg_session_pid == 0) { - int exit = session_exec_exec(exec, envlist); - perror("exec error"); + if (config->behavior.bypass_shell_login) + session_exec_exec(exec, envlist); + else + session_exec_login_through_shell(exec, envlist); + + perror("session exec error"); (void)fputs("failure calling session\n", stderr); - _exit(exit); + _exit(EXIT_FAILURE); } // looks weird, waiting on -1 should wait on any child and then just check if @@ -254,8 +251,8 @@ static void launch_with_xorg_server(struct session_exec* NNULLABLE exec, char _dummy; \ read(pipefd[0], &_dummy, sizeof(_dummy)); \ } -inline static void forked(int pipefd[2], struct passwd* pw, - char* NNULLABLE user, +inline static void forked(struct config* config, int pipefd[2], + struct passwd* pw, char* NNULLABLE user, struct session* NNULLABLE session, char** NNULLABLE envlist) { if (chdir(pw->pw_dir) == -1) SEND_ERR("chdir"); @@ -267,14 +264,26 @@ inline static void forked(int pipefd[2], struct passwd* pw, DUMMY_READ(); close(pipefd[0]); + if (!getenv("PATH")) { + size_t path_size = confstr(_CS_PATH, NULL, 0); + char* path_env = malloc(path_size); + confstr(_CS_PATH, path_env, path_size); + setenv("PATH", path_env, 1); + free(path_env); + } + + log_printf(" [I] using shell login?: %s", + config->behavior.bypass_shell_login ? "true" : "false"); if (session->type == XORG) { - launch_with_xorg_server(&session->exec, pw, envlist); + launch_with_xorg_server(config, &session->exec, pw, envlist); _exit(EXIT_SUCCESS); } else { - int exit = session_exec_exec(&session->exec, envlist); - perror("exec error"); - (void)fputs("failure calling session\n", stderr); - _exit(exit); + if (config->behavior.bypass_shell_login) + session_exec_exec(&session->exec, envlist); + else + session_exec_login_through_shell(&session->exec, envlist); + perror("session exec error"); + _exit(EXIT_FAILURE); } } #undef SEND_MSG @@ -325,7 +334,7 @@ bool launch(char* user, char* passwd, struct session session, void (*cb)(void), uint pid = fork(); if (pid == 0) - forked(pipefd, pw, user, &session, envlist); + forked(config, pipefd, pw, user, &session, envlist); else { struct child_msg msg; read(pipefd[0], &msg, sizeof(struct child_msg)); diff --git a/src/config.c b/src/config.c index e058bd4..e55f19f 100644 --- a/src/config.c +++ b/src/config.c @@ -11,14 +11,15 @@ #include "config.h" #include "desktop.h" +#include "keys.h" #include "log.h" #include "macros.h" -#include "util.h" +#include "util/vec.h" #define UPPER_HALF_BYTE 4 int parse_hex(char* _at, char x1, char x2) { // make linter happy - u_char* at = (u_char*)_at; + uint8_t* at = (uint8_t*)_at; *at = 0; diff --git a/src/desktop_exec.c b/src/desktop_exec.c index c457305..e7cd8b0 100644 --- a/src/desktop_exec.c +++ b/src/desktop_exec.c @@ -9,61 +9,39 @@ #include "desktop_exec.h" #include "macros.h" +#include "util/path.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 -char* NULLABLE search_path(const char* NNULLABLE for_binary) { - if (strchr(for_binary, '/') != NULL) { - // skip absolute paths - return strdup(for_binary); +char* NULLABLE desktop_as_cmdline(char** args) { + if (args[0] == NULL) return NULL; + size_t fmtd_len = 0; + + char** ptr = args; + while (*ptr) { + fmtd_len += strlen(*ptr) + 1; + ptr++; } - char* path_env = getenv("PATH"); - if (!path_env) return NULL; - char* path = strdup(path_env); - if (!path) return NULL; + fmtd_len -= 1; - char* tok = strtok(path, ":"); - while (tok) { - char* bin_path; - asprintf(&bin_path, "%s/%s", tok, for_binary); - if (!bin_path) { - free(path); - return NULL; - } + char* fmt_cmdline = malloc(fmtd_len + 1); + if (!fmt_cmdline) 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; - } + size_t fmting_len = 0; + ptr = args; + while (*ptr) { + char* nbyte = stpcpy(&fmt_cmdline[fmting_len], *ptr); + *nbyte = ' '; - free(bin_path); - tok = strtok(NULL, ":"); + fmting_len += nbyte - &fmt_cmdline[fmting_len] + 1; + ptr++; } + fmt_cmdline[fmting_len - 1] = '\0'; - free(path); - 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; - - int status = execve(args[0], args, envlist); - free(new_arg); - - return status; + return fmt_cmdline; } // parse Exec=/bin/prog arg1 arg2\ with\ spaces diff --git a/src/efield.c b/src/efield.c index 3deab03..00d50eb 100644 --- a/src/efield.c +++ b/src/efield.c @@ -2,7 +2,7 @@ #include "efield.h" #include "ui.h" -#include "util.h" +#include "util/utf8.h" struct editable_field efield_new(char* content) { struct editable_field efield; @@ -16,14 +16,14 @@ struct editable_field efield_new(char* content) { return efield; } -void efield_trim(struct editable_field* self, u_char pos) { +void efield_trim(struct editable_field* self, uint8_t pos) { self->pos = pos; self->content[pos + 1] = '\0'; } #define BACKSPACE_CODE 127 void efield_update(struct editable_field* self, char* update) { - u_char insert_len = strlen(update); + uint8_t insert_len = strlen(update); if (insert_len == 0) return; if (self->pos > strlen(self->content)) @@ -61,7 +61,7 @@ bool efield_seek(struct editable_field* self, char seek) { if (*self->content == '\0') return false; if (seek == 0) return false; - u_char count = seek < 0 ? -seek : seek; + uint8_t count = seek < 0 ? -seek : seek; char* ptr = &self->content[self->pos]; char* start = ptr; @@ -75,6 +75,6 @@ bool efield_seek(struct editable_field* self, char seek) { } } - self->pos = (u_char)(ptr - self->content); + self->pos = (uint8_t)(ptr - self->content); return ptr != start; } diff --git a/src/keys.c b/src/keys.c new file mode 100644 index 0000000..76d4fe9 --- /dev/null +++ b/src/keys.c @@ -0,0 +1,77 @@ +#include +#include +#include + +#include "keys.h" +#include "macros.h" +#include "ui.h" + +static int selret_magic(); + +int find_keyname(enum Keys* at, const char* name) { + for (size_t i = 0; i < LEN(KEY_MAPPINGS); i++) { + if (strcmp(KEY_NAMES[i], name) == 0) { + *at = (enum Keys)i; + return 0; + } + } + + return -1; +} + +struct option_keys find_ansi(const char* seq) { + for (size_t i = 0; i < LEN(KEY_MAPPINGS); i++) { + struct key_mapping mapping = KEY_MAPPINGS[i]; + for (size_t j = 0; mapping.sequences[j] != NULL; j++) { + if (strcmp(mapping.sequences[j], seq) == 0) { + return (struct option_keys){ + .is_some = true, + .key = (enum Keys)i, + }; + } + } + } + return (struct option_keys){.is_some = false}; +} + +void read_press(uint8_t* length, char* out) { + *length = 0; + + while (true) { + if (read(STDIN_FILENO, &out[(*length)++], 1) != 1) { + print_errno("read error"); + sleep(3); + exit(1); + } + int selret = selret_magic(); + if (selret == -1) { + print_errno("selret error"); + } else if (selret != 1) { + out[*length] = '\0'; + return; + } + } +} + +bool read_press_nb(uint8_t* length, char* out, struct timeval* tv) { + fd_set fds; + FD_ZERO(&fds); + FD_SET(STDIN_FILENO, &fds); + errno = 0; + int ret = select(STDIN_FILENO + 1, &fds, NULL, NULL, tv); + if (errno || ret <= 0) return false; + + read_press(length, out); + return true; +} + +// https://stackoverflow.com/a/48040042 +static int selret_magic() { + fd_set set; + struct timeval timeout; + FD_ZERO(&set); + FD_SET(STDIN_FILENO, &set); + timeout.tv_sec = 0; + timeout.tv_usec = 0; + return select(1, &set, NULL, NULL, &timeout); +} diff --git a/src/log.c b/src/log.c index 34ddc36..c00d001 100644 --- a/src/log.c +++ b/src/log.c @@ -8,6 +8,7 @@ static FILE* logger_out = NULL; void log_init(FILE* fd) { if (logger_out) (void)fclose(logger_out); + (void)setvbuf(fd, NULL, _IONBF, 0); logger_out = fd; } diff --git a/src/main.c b/src/main.c index 1b0a43c..8bbfe9a 100644 --- a/src/main.c +++ b/src/main.c @@ -15,7 +15,7 @@ #include "signal_handler.h" #include "ui.h" #include "users.h" -#include "util.h" +#include "util/vec.h" #define DATESTR_MAXBUFSIZE 0x20 @@ -31,6 +31,7 @@ int main(int argc, char* argv[]) { } log_init(log_fd); + log_puts("Starting log:"); } if (argc == 2) { diff --git a/src/ofield.c b/src/ofield.c index 0914412..e7320d2 100644 --- a/src/ofield.c +++ b/src/ofield.c @@ -1,10 +1,9 @@ #include -#include "config.h" #include "efield.h" #include "ofield.h" #include "ui.h" -#include "util.h" +#include "util/utf8.h" struct opts_field ofield_new(size_t opts) { if (opts == 0) { @@ -55,11 +54,11 @@ bool ofield_seek(struct opts_field* self, char seek) { return true; } -u_char ofield_display_cursor_col(struct opts_field* self, u_char maxlen) { +uint8_t ofield_display_cursor_col(struct opts_field* self, uint8_t maxlen) { if (self->current_opt == 0) { - u_char display_len = utf8len(self->efield.content); - u_char pos = utf8len_until(self->efield.content, - &self->efield.content[self->efield.pos]); + uint8_t display_len = utf8len(self->efield.content); + uint8_t pos = utf8len_until(self->efield.content, + &self->efield.content[self->efield.pos]); if (display_len > maxlen) { if (pos < maxlen / 2) { diff --git a/src/pam.c b/src/pam.c index 21b7568..bf98d66 100644 --- a/src/pam.c +++ b/src/pam.c @@ -6,6 +6,7 @@ #include #include +#include "log.h" #include "macros.h" #include "pam.h" #include "sessions.h" @@ -92,6 +93,10 @@ struct pamh_getenv_status pamh_get_complete_env(pam_handle_t* handle, char** raw_envlist = pam_getenvlist(handle); if (!raw_envlist) FAIL(PAMH_ERR_ERRNO, "pam_getenvlist"); + char** ptr = raw_envlist; + while (*ptr) + log_printf("[I] got pam env %s\n", *(ptr++)); + struct envpair extra_env[] = { {"TERM", getenv("TERM")}, {"PATH", getenv("PATH")}, diff --git a/src/sessions.c b/src/sessions.c index a15f248..6533167 100644 --- a/src/sessions.c +++ b/src/sessions.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -6,13 +7,16 @@ #include #include #include +#include +#include "config.h" #include "desktop.h" #include "desktop_exec.h" #include "log.h" #include "macros.h" #include "sessions.h" -#include "util.h" +#include "util/path.h" +#include "util/vec.h" struct source_dir { enum SessionType type; @@ -163,3 +167,54 @@ struct Vector get_avaliable_sessions() { return sessions; } + +int session_exec_exec(struct session_exec* NNULLABLE exec, + char* NULLABLE* NNULLABLE envp) { + switch (exec->typ) { + case EXEC_SHELL: { + char* argv[] = {exec->shell, NULL}; + return execvpe(exec->shell, argv, envp); + } + case EXEC_DESKTOP: + return execvpe(exec->desktop.args[0], exec->desktop.args, envp); + default: + __builtin_unreachable(); + } +} + +/// So the reason this doesn't use the user shell is because that can really be +/// anything, also assuming it were fish for example, it can't execute posix +/// shell files and that leaves it out of most `/etc/profile.d/` scripts. +/// +/// I'll just default to bash for now as it's able to source almost everything +/// and takes most flags. Maybe will try to make this more sophisticated in the +/// future. +/// +/// This respects errno. Even also works as any exec family function. +#ifndef LOGIN_SHELL + #define LOGIN_SHELL "bash" +#endif +// This triggers login behavior +#define LOGIN_SHELL_ARG0 "-" LOGIN_SHELL +int session_exec_login_through_shell(struct session_exec* NNULLABLE exec, + char* NULLABLE* NNULLABLE envp) { + switch (exec->typ) { + case EXEC_SHELL: { + char* argv[] = {LOGIN_SHELL_ARG0, "-c", exec->shell, NULL}; + return execvpe(LOGIN_SHELL, argv, envp); + } + case EXEC_DESKTOP: { + char* name = desktop_as_cmdline(exec->desktop.args); + if (!name) { + errno = EINVAL; + return -1; + } + char* argv[] = {LOGIN_SHELL_ARG0, "-c", name, NULL}; + int ret = execvpe(LOGIN_SHELL, argv, envp); + free(name); + return ret; + } + default: + __builtin_unreachable(); + } +} diff --git a/src/ui.c b/src/ui.c index 53c6dba..2d46fac 100644 --- a/src/ui.c +++ b/src/ui.c @@ -32,9 +32,9 @@ #include "ui.h" #include "ui_state.h" #include "users.h" -#include "util.h" +#include "util/utf8.h" -const u_char INPUTS_N = 3; +const uint8_t INPUTS_N = 3; struct uint_point { uint x; @@ -135,11 +135,11 @@ char* trunc_gethostname(size_t maxlen, const char* const ELLIPSIS) { } void ui_update_cursor_focus() { - u_char line = box_start.y; - u_char col = box_start.x + VALUES_COL; + uint8_t line = box_start.y; + uint8_t col = box_start.x + VALUES_COL; struct opts_field* ofield = get_opts_ffield(); - u_char maxlen = VALUE_MAXLEN; + uint8_t maxlen = VALUE_MAXLEN; if (ofield->opts > 1) { maxlen -= utf8len(g_config->strings.opts_pre) + utf8len(g_config->strings.opts_post); @@ -269,7 +269,7 @@ int load(struct Vector* users, struct Vector* sessions) { scratch_print_ui(); /// INTERACTIVE - u_char len; + uint8_t len; char seq[0xff]; uint esc = 0; while (true) { @@ -362,11 +362,11 @@ void clean_line(struct uint_point origin, uint line) { printf("\x1b[%d;%dH%s", origin.y + line, origin.x + 1, line_cleaner); } -u_char get_render_pos_offset(struct opts_field* self, u_char maxlen) { +uint8_t get_render_pos_offset(struct opts_field* self, uint8_t maxlen) { if (self->current_opt != 0) return 0; - u_char pos = utf8len_until(self->efield.content, - &self->efield.content[self->efield.pos]); + uint8_t pos = utf8len_until(self->efield.content, + &self->efield.content[self->efield.pos]); return pos - ofield_display_cursor_col(self, maxlen); } @@ -439,8 +439,8 @@ void print_session(struct session session, bool multiple) { char* toprint = session.name; if (multiple) { - u_char maxlen = VALUE_MAXLEN - utf8len(g_config->strings.opts_pre) - - utf8len(g_config->strings.opts_post); + uint8_t maxlen = VALUE_MAXLEN - utf8len(g_config->strings.opts_pre) - + utf8len(g_config->strings.opts_post); toprint += get_render_pos_offset(&of_session, maxlen); size_t printlen = utf8seekn(toprint, maxlen) - toprint; @@ -467,8 +467,8 @@ void print_user(struct user user, bool multiple) { char* toprint = user.display_name; if (multiple) { - u_char maxlen = VALUE_MAXLEN - utf8len(g_config->strings.opts_pre) - - utf8len(g_config->strings.opts_post); + uint8_t maxlen = VALUE_MAXLEN - utf8len(g_config->strings.opts_pre) - + utf8len(g_config->strings.opts_post); toprint += get_render_pos_offset(&of_session, maxlen); size_t printlen = utf8seekn(toprint, maxlen) - toprint; diff --git a/src/users.c b/src/users.c index f3724ba..95c43a5 100644 --- a/src/users.c +++ b/src/users.c @@ -7,7 +7,7 @@ #include "log.h" #include "macros.h" #include "users.h" -#include "util.h" +#include "util/vec.h" // NOLINTNEXTLINE(readability-identifier-length) int build_user(struct user* NNULLABLE user, struct passwd* p) { diff --git a/src/util.c b/src/util.c deleted file mode 100644 index 1b1c53d..0000000 --- a/src/util.c +++ /dev/null @@ -1,238 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "keys.h" -#include "ui.h" -#include "util.h" - -static int selret_magic(); - -int find_keyname(enum Keys* at, const char* name) { - for (size_t i = 0; i < LEN(KEY_MAPPINGS); i++) { - if (strcmp(KEY_NAMES[i], name) == 0) { - *at = (enum Keys)i; - return 0; - } - } - - return -1; -} - -struct option_keys find_ansi(const char* seq) { - for (size_t i = 0; i < LEN(KEY_MAPPINGS); i++) { - struct key_mapping mapping = KEY_MAPPINGS[i]; - for (size_t j = 0; mapping.sequences[j] != NULL; j++) { - if (strcmp(mapping.sequences[j], seq) == 0) { - return (struct option_keys){ - .is_some = true, - .key = (enum Keys)i, - }; - } - } - } - return (struct option_keys){.is_some = false}; -} - -void read_press(u_char* length, char* out) { - *length = 0; - - while (true) { - if (read(STDIN_FILENO, &out[(*length)++], 1) != 1) { - print_errno("read error"); - sleep(3); - exit(1); - } - int selret = selret_magic(); - if (selret == -1) { - print_errno("selret error"); - } else if (selret != 1) { - out[*length] = '\0'; - return; - } - } -} - -bool read_press_nb(u_char* length, char* out, struct timeval* tv) { - fd_set fds; - FD_ZERO(&fds); - FD_SET(STDIN_FILENO, &fds); - errno = 0; - int ret = select(STDIN_FILENO + 1, &fds, NULL, NULL, tv); - if (errno || ret <= 0) return false; - - read_press(length, out); - return true; -} - -// https://stackoverflow.com/a/48040042 -static int selret_magic() { - fd_set set; - struct timeval timeout; - FD_ZERO(&set); - FD_SET(STDIN_FILENO, &set); - timeout.tv_sec = 0; - timeout.tv_usec = 0; - return select(1, &set, NULL, NULL, &timeout); -} - -// UTF-8 shii -#define UTF8_CONT_MSK 0b11000000 -#define UTF8_CONT_VAL 0b10000000 -bool utf8_iscont(char byte) { - return (byte & UTF8_CONT_MSK) == UTF8_CONT_VAL; -} - -size_t utf8len(const char* str) { - size_t len = 0; - while (*str != '\0') { - if (!utf8_iscont(*(str++))) len++; - } - - return len; -} - -size_t utf8len_until(const char* str, const char* until) { - size_t len = 0; - while (str < until) { - if (!utf8_iscont(*(str++))) len++; - } - - return len; -} - -size_t utf8trunc(char* str, size_t n) { - size_t bytes = 0; - while (true) { - if (str[bytes] == '\0') break; - if (utf8_iscont(str[bytes])) { - bytes++; - continue; - } - if (n == 0) { - str[bytes] = '\0'; - break; - } - bytes++; - n--; - } - return bytes; -} - -const char* utf8back(const char* str) { - while (utf8_iscont(*(--str))) { - } - return str; -} -const char* utf8seek(const char* str) { - while (utf8_iscont(*(++str))) { - } - return str; -} - -const char* utf8seekn(const char* str, size_t n) { - while (n > 0 && *str != '\0') { - str = utf8seek(str); - n--; - } - return str; -} - -// Vector shii -const struct Vector VEC_NEW = { - .length = 0, - .capacity = 0, - .pages = NULL, -}; - -struct Vector vec_from_raw(void** raw) { - size_t len = 0; - while (raw[len]) - len++; - - return (struct Vector){ - .length = len, - .capacity = len, - .pages = raw, - }; -} - -void** vec_as_raw(struct Vector self) { - if (vec_push(&self, NULL) != 0) return NULL; - return self.pages; -} - -int vec_resize(struct Vector* self, size_t size) { - void** new_location = - (void**)realloc((void*)self->pages, size * sizeof(void*)); - if (new_location != NULL) { - if (self->length > size) self->length = size; - self->capacity = size; - self->pages = new_location; - } else { - return -1; - } - return 0; -} - -int vec_reserve(struct Vector* self, size_t size) { - uint32_t new_capacity = self->capacity; - while (self->length + size > new_capacity) { - new_capacity = new_capacity + (new_capacity >> 1) + - 1; // cap * 1.5 + 1; 0 1 2 4 7 11... - } - return vec_resize(self, new_capacity); -} - -int vec_reserve_exact(struct Vector* self, size_t size) { - uint32_t needed_capacity = self->length + size; - if (self->capacity < needed_capacity) { - return vec_resize(self, needed_capacity); - } - return 0; -} - -int vec_push(struct Vector* self, void* item) { - int res_ret = vec_reserve(self, 1); - if (res_ret != 0) return res_ret; - - self->pages[self->length++] = item; - return 0; -} - -void vec_free(struct Vector* self) { - while (self->length > 0) - free(self->pages[--self->length]); - - vec_clear(self); -} - -void vec_clear(struct Vector* self) { - free((void*)self->pages); - vec_reset(self); -} - -void vec_reset(struct Vector* self) { - *self = (struct Vector){ - .length = 0, - .capacity = 0, - .pages = NULL, - }; -} - -void* vec_pop(struct Vector* self) { - if (self->length == 0) return NULL; - - return self->pages[--self->length]; -} - -void* vec_get(struct Vector* self, size_t index) { - if (index >= self->length) return NULL; - - return self->pages[index]; -} diff --git a/src/util/path.c b/src/util/path.c new file mode 100644 index 0000000..571c6aa --- /dev/null +++ b/src/util/path.c @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util/path.h" + +// returns NULL on any error +// otherwise it returns the absolute path of the program that MUST BE FREED +// +// Should be almost completely posix compliant, except it won't resolve empty +// PATH entries relative to the cwd +char* NULLABLE search_path(const char* NNULLABLE for_binary) { + if (strchr(for_binary, '/') != NULL) { + // skip absolute paths + return strdup(for_binary); + } + char* path_env = getenv("PATH"); + if (!path_env) return NULL; + char* path = strdup(path_env); + 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 && access(bin_path, X_OK) == 0) { + free(path); + return bin_path; + } + + free(bin_path); + tok = strtok(NULL, ":"); + } + + free(path); + return NULL; +} + +// This is present in glibc ONLY with GNU extensions, this aims to provide a +// musl compatible variant. +// +// Respects errno of exec functions family. +int execvpe(const char* NNULLABLE file, char* NULLABLE const argv[NNULLABLE], + char* NULLABLE const envp[NNULLABLE]) { + char* path = search_path(file); + if (!path) { + errno = ENOENT; + return -1; + } + int ret = execve(path, argv, envp); + free(path); + return ret; +} diff --git a/src/util/utf8.c b/src/util/utf8.c new file mode 100644 index 0000000..7c21f3c --- /dev/null +++ b/src/util/utf8.c @@ -0,0 +1,62 @@ +#include "util/utf8.h" + +#define UTF8_CONT_MSK 0b11000000 +#define UTF8_CONT_VAL 0b10000000 +bool utf8_iscont(char byte) { + return (byte & UTF8_CONT_MSK) == UTF8_CONT_VAL; +} + +size_t utf8len(const char* str) { + size_t len = 0; + while (*str != '\0') { + if (!utf8_iscont(*(str++))) len++; + } + + return len; +} + +size_t utf8len_until(const char* str, const char* until) { + size_t len = 0; + while (str < until) { + if (!utf8_iscont(*(str++))) len++; + } + + return len; +} + +size_t utf8trunc(char* str, size_t n) { + size_t bytes = 0; + while (true) { + if (str[bytes] == '\0') break; + if (utf8_iscont(str[bytes])) { + bytes++; + continue; + } + if (n == 0) { + str[bytes] = '\0'; + break; + } + bytes++; + n--; + } + return bytes; +} + +const char* utf8back(const char* str) { + while (utf8_iscont(*(--str))) { + } + return str; +} +const char* utf8seek(const char* str) { + while (utf8_iscont(*(++str))) { + } + return str; +} + +const char* utf8seekn(const char* str, size_t n) { + while (n > 0 && *str != '\0') { + str = utf8seek(str); + n--; + } + return str; +} diff --git a/src/util/vec.c b/src/util/vec.c new file mode 100644 index 0000000..244ce0f --- /dev/null +++ b/src/util/vec.c @@ -0,0 +1,96 @@ +#include + +#include "util/vec.h" + +const struct Vector VEC_NEW = { + .length = 0, + .capacity = 0, + .pages = NULL, +}; + +struct Vector vec_from_raw(void** raw) { + size_t len = 0; + while (raw[len]) + len++; + + return (struct Vector){ + .length = len, + .capacity = len, + .pages = raw, + }; +} + +void** vec_as_raw(struct Vector self) { + if (vec_push(&self, NULL) != 0) return NULL; + return self.pages; +} + +int vec_resize(struct Vector* self, size_t size) { + void** new_location = + (void**)realloc((void*)self->pages, size * sizeof(void*)); + if (new_location != NULL) { + if (self->length > size) self->length = size; + self->capacity = size; + self->pages = new_location; + } else { + return -1; + } + return 0; +} + +int vec_reserve(struct Vector* self, size_t size) { + uint32_t new_capacity = self->capacity; + while (self->length + size > new_capacity) { + new_capacity = new_capacity + (new_capacity >> 1) + + 1; // cap * 1.5 + 1; 0 1 2 4 7 11... + } + return vec_resize(self, new_capacity); +} + +int vec_reserve_exact(struct Vector* self, size_t size) { + uint32_t needed_capacity = self->length + size; + if (self->capacity < needed_capacity) { + return vec_resize(self, needed_capacity); + } + return 0; +} + +int vec_push(struct Vector* self, void* item) { + int res_ret = vec_reserve(self, 1); + if (res_ret != 0) return res_ret; + + self->pages[self->length++] = item; + return 0; +} + +void vec_free(struct Vector* self) { + while (self->length > 0) + free(self->pages[--self->length]); + + vec_clear(self); +} + +void vec_clear(struct Vector* self) { + free((void*)self->pages); + vec_reset(self); +} + +void vec_reset(struct Vector* self) { + *self = (struct Vector){ + .length = 0, + .capacity = 0, + .pages = NULL, + }; +} + +void* vec_pop(struct Vector* self) { + if (self->length == 0) return NULL; + + return self->pages[--self->length]; +} + +void* vec_get(struct Vector* self, size_t index) { + if (index >= self->length) return NULL; + + return self->pages[index]; +}