7 Commits

Author SHA1 Message Date
570a68c586 fix: satify all clang code requirements 2025-08-27 22:21:21 +02:00
ff3e58ee5b fix: satify clang nullability requirements 2025-08-27 22:07:04 +02:00
109c9bd0be chore: organize code
add -Wextra by default
pipefd to communicate better with parent and forked process
rustify some stuff
add the desktop parser to the file read
2025-08-27 22:02:14 +02:00
056ec64bcb feat: better spawn error handling 2025-08-27 20:29:16 +02:00
b58bd50e64 perf: some compiler flag optimizations
https://stackoverflow.com/questions/6687630/how-to-remove-unused-c-c-symbols-with-gcc-and-ld
2025-08-27 19:54:24 +02:00
6d9107b360 lint: stricter casing rules 2025-08-27 19:47:19 +02:00
b56aa19671 chore: modularize PAM
also helps with sanitizing the code flow when spawning the child, more
code though
2025-08-27 19:31:46 +02:00
21 changed files with 526 additions and 303 deletions

View File

@@ -16,32 +16,34 @@ Checks: >
readability-*, readability-*,
-readability-braces-around-statements, -readability-braces-around-statements,
WarningsAsErrors: '' WarningsAsErrors: ""
HeaderFilterRegex: '.*' HeaderFilterRegex: ".*"
FormatStyle: file FormatStyle: file
CheckOptions: CheckOptions:
- key: readability-magic-numbers.IgnoredIntegerValues - key: readability-magic-numbers.IgnoredIntegerValues
value: '0;1;2;3;10;255' value: "0;1;2;3;10;255"
# - key: readability-magic-numbers.IgnoredValues - key: readability-identifier-naming.VariableCase
# value: '0;1;2;3;10;255' value: lower_case
- key: readability-identifier-naming.VariableCase - key: readability-identifier-naming.ConstantParameterCase
value: lower_case value: UPPER_CASE
- key: readability-identifier-naming.ConstantParameterCase
value: UPPER_CASE
- key: readability-identifier-naming.ConstantCase - key: readability-identifier-naming.ConstantCase
value: "UPPER_CASE" value: "UPPER_CASE"
- key: readability-identifier-naming.EnumCase
value: "CamelCase"
- key: readability-identifier-naming.FunctionCase
value: "lower_case"
- key: readability-identifier-length.VariableThreshold - key: readability-identifier-length.VariableThreshold
value: '2' value: "2"
- key: readability-identifier-length.ParameterThreshold - key: readability-identifier-length.ParameterThreshold
value: '2' value: "2"
- key: readability-identifier-length.LocalConstantThreshold - key: readability-identifier-length.LocalConstantThreshold
value: '2' value: "2"
- key: readability-identifier-length.MemberThreshold - key: readability-identifier-length.MemberThreshold
value: '2' value: "2"
- key: readability-identifier-length.MinimumParameterNameLength - key: readability-identifier-length.MinimumParameterNameLength
value: '2' value: "2"
- key: readability-identifier-length.MinimumVariableNameLength - key: readability-identifier-length.MinimumVariableNameLength
value: '2' value: "2"

View File

@@ -9,17 +9,18 @@ ODIR=dist
PREFIX=/usr PREFIX=/usr
CC?=gcc CC?=gcc
CFLAGS?=-O3 -Wall CFLAGS?=-O3 -Wall -Wextra -fdata-sections -ffunction-sections
# C PreProcessor flags, not C Plus Plus # C PreProcessor flags, not C Plus Plus
CPPFLAGS?= CPPFLAGS?=
ALLFLAGS=$(CFLAGS) $(CPPFLAGS) -I$(IDIR) ALLFLAGS=$(CFLAGS) $(CPPFLAGS) -I$(IDIR)
LDFLAGS?=-Wl,--gc-sections
LIBS=-lpam LIBS=-lpam
_DEPS = version.h log.h util.h ui.h ui_state.h config.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
DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS)) DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS))
_OBJ = main.o log.o util.o ui.o ui_state.o config.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
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 '?')
@@ -41,7 +42,7 @@ $(ODIR)/%.o: $(CDIR)/%.c $(DEPS)
$(CC) -c -o $@ $< $(ALLFLAGS) $(CC) -c -o $@ $< $(ALLFLAGS)
lidm: $(OBJ) lidm: $(OBJ)
$(CC) -o $@ $^ $(ALLFLAGS) $(LIBS) $(CC) -o $@ $^ $(ALLFLAGS) $(LIBS) $(LDFLAGS)
clean: clean:
rm -f $(ODIR)/*.o lidm rm -f $(ODIR)/*.o lidm
@@ -137,8 +138,8 @@ pre-commit:
prettier -c "**/*.md" prettier -c "**/*.md"
git ls-files "*.sh" "*/PKGBUILD" | xargs shellcheck --shell=bash git ls-files "*.sh" "*/PKGBUILD" | xargs shellcheck --shell=bash
clang-format -i $$(git ls-files "*.c" "*.h") clang-format -i $$(git ls-files "*.c" "*.h")
git ls-files -z "*.h" | \ git ls-files -z "*.c" "*.h" | \
parallel -j$$(nproc) -q0 --no-notice --will-cite --tty clang-tidy --quiet |& \ parallel -j$$(nproc) -q0 --no-notice --will-cite --tty clang-tidy -warnings-as-errors=\* --quiet |& \
grep -v "warnings generated." || true grep -v "warnings generated." || true
print-version: print-version:

View File

@@ -6,7 +6,8 @@
#include "config.h" #include "config.h"
#include "sessions.h" #include "sessions.h"
bool launch(char* user, char* passwd, struct session session, void (*cb)(void), bool launch(char* NNULLABLE user, char* NNULLABLE passwd,
struct config* config); struct session session, void (*NULLABLE cb)(void),
struct config* NNULLABLE config);
#endif #endif

View File

@@ -8,7 +8,7 @@
#include "macros.h" #include "macros.h"
#include "util.h" #include "util.h"
enum introspection_type { enum IntrospectionType {
STRING, STRING,
BOOL, BOOL,
NUMBER, NUMBER,
@@ -26,7 +26,7 @@ static const char* NNULLABLE const INTROS_TYS_NAMES[] = {
struct introspection_item { struct introspection_item {
char* NNULLABLE name; char* NNULLABLE name;
size_t offset; size_t offset;
enum introspection_type typ; enum IntrospectionType typ;
}; };
#define INTROS_ITEM(key, table, ty) \ #define INTROS_ITEM(key, table, ty) \
@@ -90,9 +90,9 @@ BUILD(colors, COLORS, TABLE_COLORS);
BUILD(chars, CHARS, TABLE_CHARS); BUILD(chars, CHARS, TABLE_CHARS);
#define TABLE_FUNCTIONS(F, name) \ #define TABLE_FUNCTIONS(F, name) \
F(enum keys, poweroff, KEY, F1, name) \ F(enum Keys, poweroff, KEY, F1, name) \
F(enum keys, reboot, KEY, F2, name) \ F(enum Keys, reboot, KEY, F2, name) \
F(enum keys, refresh, KEY, F5, name) F(enum Keys, refresh, KEY, F5, name)
BUILD(functions, FUNCTIONS, TABLE_FUNCTIONS); BUILD(functions, FUNCTIONS, TABLE_FUNCTIONS);
@@ -150,15 +150,15 @@ struct introspection_table {
static const struct introspection_table CONFIG_INSTROSPECTION[] = { static const struct introspection_table CONFIG_INSTROSPECTION[] = {
{"colors", offsetof(struct config, colors), INTROS_TABLE_COLORS, {"colors", offsetof(struct config, colors), INTROS_TABLE_COLORS,
sizeof(INTROS_TABLE_COLORS) / sizeof(INTROS_TABLE_COLORS[0])}, LEN(INTROS_TABLE_COLORS)},
{"chars", offsetof(struct config, chars), INTROS_TABLE_CHARS, {"chars", offsetof(struct config, chars), INTROS_TABLE_CHARS,
sizeof(INTROS_TABLE_CHARS) / sizeof(INTROS_TABLE_CHARS[0])}, LEN(INTROS_TABLE_CHARS)},
{"functions", offsetof(struct config, functions), INTROS_TABLE_FUNCTIONS, {"functions", offsetof(struct config, functions), INTROS_TABLE_FUNCTIONS,
sizeof(INTROS_TABLE_FUNCTIONS) / sizeof(INTROS_TABLE_FUNCTIONS[0])}, LEN(INTROS_TABLE_FUNCTIONS)},
{"strings", offsetof(struct config, strings), INTROS_TABLE_STRINGS, {"strings", offsetof(struct config, strings), INTROS_TABLE_STRINGS,
sizeof(INTROS_TABLE_STRINGS) / sizeof(INTROS_TABLE_STRINGS[0])}, LEN(INTROS_TABLE_STRINGS)},
{"behavior", offsetof(struct config, behavior), INTROS_TABLE_BEHAVIOR, {"behavior", offsetof(struct config, behavior), INTROS_TABLE_BEHAVIOR,
sizeof(INTROS_TABLE_BEHAVIOR) / sizeof(INTROS_TABLE_BEHAVIOR[0])}, LEN(INTROS_TABLE_BEHAVIOR)},
}; };
//// FUNCTIONS //// FUNCTIONS

View File

@@ -3,7 +3,7 @@
#include <stdlib.h> #include <stdlib.h>
enum keys { enum Keys {
ESC, ESC,
F1, F1,
F2, F2,
@@ -66,7 +66,7 @@ static const char* const KEY_NAMES[] = {
}; };
struct key_mapping { struct key_mapping {
enum keys key; enum Keys key;
const char* sequences[3]; const char* sequences[3];
}; };

View File

@@ -36,5 +36,6 @@
#endif #endif
#define LEN(X) (sizeof(X) / sizeof((X)[0])) #define LEN(X) (sizeof(X) / sizeof((X)[0]))
#define UNUSED(x) ((void)(x))
#endif #endif

33
include/pam.h Normal file
View File

@@ -0,0 +1,33 @@
#ifndef PAM_H
#define PAM_H
#include <pwd.h>
#include <security/_pam_types.h>
#include <security/pam_appl.h>
#include <stdbool.h>
#include "macros.h"
#include "sessions.h"
#define PAMH_ERR_NOERR 0
#define PAMH_ERR_ALLOC 1
#define PAMH_ERR_ERRNO 2
#define PAMH_ERR_NOERRNO 3
struct pamh_getenv_status {
char error_flag;
union {
char* NULLABLE* NNULLABLE envlist;
const char* NNULLABLE errfn;
};
};
// Doesn't include `source`s
struct pamh_getenv_status pamh_get_complete_env(pam_handle_t* NNULLABLE handle,
struct passwd* NNULLABLE pw,
enum SessionType session_typ);
void free_envlist(char* NULLABLE* NNULLABLE envlist);
pam_handle_t* NULLABLE get_pamh(char* NNULLABLE user, char* NNULLABLE passwd);
#endif /* PAM_H */

View File

@@ -2,21 +2,70 @@
#define SESSIONSH_ #define SESSIONSH_
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h>
#include "macros.h" #include "macros.h"
#include "util.h" #include "util.h"
enum session_type { enum SessionType {
XORG, XORG,
WAYLAND, WAYLAND,
SHELL, SHELL,
}; };
enum ExecType {
EXEC_SHELL,
EXEC_DESKTOP,
};
struct desktop_exec {
char* NULLABLE* NNULLABLE args;
int arg_count;
};
struct session_exec {
enum ExecType typ;
union {
char* NNULLABLE shell;
struct desktop_exec desktop;
};
};
static inline struct session_exec session_exec_shell(char* NNULLABLE shell) {
return (struct session_exec){
.typ = EXEC_SHELL,
.shell = shell,
};
}
static inline struct session_exec session_exec_desktop(
int arg_count, char* NULLABLE* NNULLABLE args) {
return (struct session_exec){
.typ = EXEC_DESKTOP,
.desktop =
{
.args = args,
.arg_count = arg_count,
},
};
}
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 execve(exec->desktop.args[0], exec->desktop.args, envlist);
default:
__builtin_unreachable();
}
}
struct session { struct session {
char* NNULLABLE name; char* NNULLABLE name;
char* NNULLABLE exec; struct session_exec exec;
char* NULLABLE tryexec; enum SessionType type;
enum session_type type;
}; };
struct Vector get_avaliable_sessions(); struct Vector get_avaliable_sessions();

View File

@@ -39,7 +39,7 @@
#define VALUES_SEPR 3 #define VALUES_SEPR 3
#define VALUE_MAXLEN (BOX_WIDTH - VALUES_COL + 1 - BOX_HMARGIN - 2) #define VALUE_MAXLEN (BOX_WIDTH - VALUES_COL + 1 - BOX_HMARGIN - 2)
enum input { SESSION, USER, PASSWD }; enum Input { SESSION, USER, PASSWD };
extern const u_char INPUTS_N; extern const u_char INPUTS_N;
void setup(struct config* config); void setup(struct config* config);
@@ -47,7 +47,7 @@ int load(struct Vector* users, struct Vector* sessions);
void print_err(const char* /*msg*/); void print_err(const char* /*msg*/);
void print_errno(const char* /*descr*/); void print_errno(const char* /*descr*/);
void ui_update_field(enum input focused_input); void ui_update_field(enum Input focused_input);
void ui_update_ffield(); void ui_update_ffield();
void ui_update_ofield(struct opts_field* self); void ui_update_ofield(struct opts_field* self);
void ui_update_cursor_focus(); void ui_update_cursor_focus();

View File

@@ -6,7 +6,7 @@
#include "macros.h" #include "macros.h"
#include "ui.h" #include "ui.h"
extern enum input focused_input; extern enum Input focused_input;
extern struct opts_field of_session; extern struct opts_field of_session;
extern struct opts_field of_user; extern struct opts_field of_user;
@@ -15,7 +15,7 @@ extern struct opts_field of_passwd;
extern struct Vector* UNULLABLE gusers; extern struct Vector* UNULLABLE gusers;
extern struct Vector* UNULLABLE gsessions; extern struct Vector* UNULLABLE gsessions;
struct opts_field* NNULLABLE get_opts_field(enum input from); struct opts_field* NNULLABLE get_opts_field(enum Input from);
struct opts_field* NNULLABLE get_opts_ffield(); struct opts_field* NNULLABLE get_opts_ffield();
struct user st_user(); struct user st_user();

View File

@@ -10,12 +10,18 @@
#include "keys.h" #include "keys.h"
int find_keyname(enum keys* at, const char* name); int find_keyname(enum Keys* at, const char* name);
enum keys find_ansi(const char* seq); 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); void read_press(u_char* length, char* out);
// non blocking, waits up to tv or interrupt, returns true if actually read // 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); bool read_press_nb(u_char* length, char* out, struct timeval* tv);
// UTF8
//
bool utf8_iscont(char byte); bool utf8_iscont(char byte);
size_t utf8len(const char* str); size_t utf8len(const char* str);
size_t utf8len_until(const char* str, const char* until); size_t utf8len_until(const char* str, const char* until);
@@ -24,12 +30,16 @@ const char* utf8back(const char* str);
const char* utf8seek(const char* str); const char* utf8seek(const char* str);
const char* utf8seekn(const char* str, size_t n); const char* utf8seekn(const char* str, size_t n);
// Vector
//
struct Vector { struct Vector {
uint32_t length; uint32_t length;
uint32_t capacity; uint32_t capacity;
void** pages; void** pages;
}; };
struct Vector vec_from_raw(void** raw);
void** vec_as_raw(struct Vector self);
extern const struct Vector VEC_NEW; extern const struct Vector VEC_NEW;
int vec_resize(struct Vector* self, size_t size); int vec_resize(struct Vector* self, size_t size);
int vec_reserve(struct Vector* self, size_t size); int vec_reserve(struct Vector* self, size_t size);

View File

@@ -1,3 +1,4 @@
#include <errno.h>
#include <grp.h> #include <grp.h>
#include <pwd.h> #include <pwd.h>
#include <security/pam_misc.h> #include <security/pam_misc.h>
@@ -7,79 +8,24 @@
#include <string.h> #include <string.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <unistd.h>
#include "auth.h" #include "auth.h"
#include "config.h" #include "config.h"
#include "desktop_exec.h"
#include "log.h" #include "log.h"
#include "pam.h"
#include "sessions.h" #include "sessions.h"
#include "ui.h" #include "ui.h"
#include "unistd.h"
#include "util.h" #include "util.h"
int pam_conversation(int num_msg, const struct pam_message** msg, static void try_source_file(struct Vector* NNULLABLE vec_envlist,
struct pam_response** resp, void* appdata_ptr) { char* NNULLABLE filepath) {
struct pam_response* reply = log_printf("sourcing %s\n", filepath);
(struct pam_response*)malloc(sizeof(struct pam_response) * num_msg); FILE* file2source = fopen(filepath, "r");
if (!reply) { if (file2source == NULL) {
return PAM_BUF_ERR; log_printf("error sourcing %s\n", filepath);
return;
} }
for (size_t i = 0; i < num_msg; i++) {
reply[i].resp = NULL;
reply[i].resp_retcode = 0;
if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF ||
msg[i]->msg_style == PAM_PROMPT_ECHO_ON) {
char* input = (char*)appdata_ptr;
reply[i].resp = strdup(input);
}
}
*resp = reply;
return PAM_SUCCESS;
}
#ifndef PAM_SERVICE_FALLBACK
#define PAM_SERVICE_FALLBACK "login"
#endif
#define CHECK_PAM_RET(call) \
ret = (call); \
if (ret != PAM_SUCCESS) { \
pam_end(pamh, ret); \
return NULL; \
}
void clear_screen() {
printf("\x1b[H\x1b[J");
}
pam_handle_t* get_pamh(char* user, char* passwd) {
pam_handle_t* pamh = NULL;
struct pam_conv pamc = {pam_conversation, (void*)passwd};
int ret;
char* pam_service_override = getenv("LIDM_PAM_SERVICE");
char* pam_service_name =
pam_service_override ? pam_service_override : PAM_SERVICE_FALLBACK;
CHECK_PAM_RET(pam_start(pam_service_name, user, &pamc, &pamh))
CHECK_PAM_RET(pam_authenticate(pamh, 0))
CHECK_PAM_RET(pam_acct_mgmt(pamh, 0))
CHECK_PAM_RET(pam_setcred(pamh, PAM_ESTABLISH_CRED))
CHECK_PAM_RET(pam_open_session(pamh, 0))
CHECK_PAM_RET(pam_setcred(pamh, PAM_REINITIALIZE_CRED))
return pamh;
}
#undef CHECK_PAM_RET
void* shmalloc(size_t size) {
return mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS,
-1, 0);
}
void sourceFileTry(char* file) {
FILE* file2source = fopen(file, "r");
if (file2source == NULL) return;
char* line = NULL; char* line = NULL;
size_t len = 0; size_t len = 0;
@@ -89,13 +35,10 @@ void sourceFileTry(char* file) {
if (read == 0 || (read > 0 && *line == '#')) continue; if (read == 0 || (read > 0 && *line == '#')) continue;
if (line[read - 1] == '\n') line[read - 1] = '\0'; if (line[read - 1] == '\n') line[read - 1] = '\0';
/* printf("Retrieved line of length %zu:\n", read); */ for (ssize_t i = 1; i < read; i++) {
/* printf("%s\n", line); */
for (size_t i = 1; i < read; i++) {
if (line[i] == '=') { if (line[i] == '=') {
/* printf("FOUND '='!\n"); */ vec_push(vec_envlist, (void*)line);
line[i] = '\0'; line = NULL;
setenv(line, &line[i + 1], 1);
break; break;
} }
} }
@@ -105,85 +48,92 @@ void sourceFileTry(char* file) {
(void)fclose(file2source); (void)fclose(file2source);
} }
void moarEnv(char* user, struct session session, struct passwd* pw, static void source_paths(struct Vector* NNULLABLE vec_envlist,
struct config* config) { struct Vector* NNULLABLE abs_source,
if (chdir(pw->pw_dir) == -1) print_errno("can't chdir to user home"); const char* NULLABLE user_home,
struct Vector* NNULLABLE user_source) {
setenv("HOME", pw->pw_dir, true); for (size_t i = 0; i < abs_source->length; i++) {
setenv("USER", pw->pw_name, true); char* path = vec_get(abs_source, i);
setenv("SHELL", pw->pw_shell, true); try_source_file(vec_envlist, path);
// TERM
setenv("LOGNAME", pw->pw_name, true);
// MAIL?
// PATH?
char* xdg_session_type = "unknown";
if (session.type == SHELL) xdg_session_type = "tty";
if (session.type == XORG) xdg_session_type = "x11";
if (session.type == WAYLAND) xdg_session_type = "wayland";
setenv("XDG_SESSION_TYPE", xdg_session_type, true);
printf("\n\n\n\n\x1b[1m");
for (size_t i = 0; i < config->behavior.source.length; i++) {
/* printf("DEBUG(source)!!!! %d %s\n", i, (char*)vec_get(&behavior->source,
* i)); */
sourceFileTry((char*)vec_get(&config->behavior.source, i));
} }
/* printf("\n"); */
if (pw->pw_dir) { if (user_home)
const char* home = pw->pw_dir; for (size_t i = 0; i < user_source->length; i++) {
size_t home_len = strlen(home); char* path = NULL;
asprintf(&path, "%s/%s", user_home, (char*)vec_get(user_source, i));
for (size_t i = 0; i < config->behavior.user_source.length; i++) { if (!path) {
const char* filename = (char*)vec_get(&config->behavior.user_source, i); log_puts("alloc failure on user source\n");
size_t filename_len = strlen(filename); continue;
}
size_t path_len = home_len + 1 + filename_len + 1; // nullbyte and slash try_source_file(vec_envlist, path);
char* path = malloc(path_len);
if (!path) continue; // can't bother
memcpy(path, home, home_len);
path[home_len] = '/'; // assume pw_dir doesn't start with '/' :P
memcpy(&path[home_len + 1], filename, filename_len);
path[path_len - 1] = '\0';
sourceFileTry(path);
free(path); free(path);
} }
else {
log_puts("user has no home\n");
} }
/*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);*/
} }
/*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;
bool err;
};
#define SEND_MSG(MSG) \
{ \
write(pipefd[1], &(MSG), sizeof(struct child_msg)); \
close(pipefd[1]); \
}
#define SEND_ERR(MSG) \
{ \
write(pipefd[1], \
&(struct child_msg){.msg = (MSG), ._errno = errno, .err = true}, \
sizeof(struct child_msg)); \
close(pipefd[1]); \
_exit(EXIT_FAILURE); \
}
#define DUMMY_READ() \
{ \
char _dummy; \
read(pipefd[0], &_dummy, sizeof(_dummy)); \
}
inline static void forked(int pipefd[2], struct passwd* pw,
char* NNULLABLE user,
struct session_exec* NNULLABLE exec,
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);
}
#undef SEND_MSG
#undef SEND_ERR
#undef DUMMY_READ
// NOLINTBEGIN(readability-function-cognitive-complexity) // NOLINTBEGIN(readability-function-cognitive-complexity)
bool launch(char* user, char* passwd, struct session session, void (*cb)(void), bool launch(char* user, char* passwd, struct session session, void (*cb)(void),
struct config* config) { struct config* config) {
char** desktop_exec;
int desktop_count;
if (session.type != SHELL) {
desktop_exec = NULL;
int parse_status =
parse_exec_string(session.exec, &desktop_count, &desktop_exec);
if (parse_status != 0 || desktop_count == 0 || !desktop_exec[0]) {
print_err("failure parsing exec string");
log_printf("failure parsing exec string '%s': %d\n",
session.exec ? session.exec : "NULL", parse_status);
free_parsed_args(desktop_count, desktop_exec);
return false;
}
}
struct passwd* pw = getpwnam(user); struct passwd* pw = getpwnam(user);
if (pw == NULL) { if (pw == NULL) {
print_err("could not get user info"); print_err("could not get user info");
@@ -196,91 +146,64 @@ bool launch(char* user, char* passwd, struct session session, void (*cb)(void),
return false; return false;
} }
bool* reach_session = shmalloc(sizeof(bool)); struct pamh_getenv_status env_ret =
if (reach_session == NULL) { pamh_get_complete_env(pamh, pw, session.type);
perror("error allocating shared memory"); if (env_ret.error_flag != PAMH_ERR_NOERR) {
if (env_ret.error_flag == PAMH_ERR_ALLOC) {
print_err("allocator error");
} else if (env_ret.error_flag == PAMH_ERR_ERRNO) {
print_errno(env_ret.errfn);
} else if (env_ret.error_flag == PAMH_ERR_NOERRNO) {
print_err(env_ret.errfn);
}
return false; return false;
} }
*reach_session = false;
struct Vector envlist_vec = vec_from_raw((void**)env_ret.envlist);
source_paths(&envlist_vec, &config->behavior.source, pw->pw_dir,
&config->behavior.user_source);
char** envlist = (char**)vec_as_raw(envlist_vec);
if (!envlist) {
print_err("vec alloc error");
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(); uint pid = fork();
if (pid == 0) { // child if (pid == 0)
char* term = NULL; forked(pipefd, pw, user, &session.exec, envlist);
char* getterm = getenv("TERM"); else {
// TODO: handle malloc error struct child_msg msg;
if (getterm != NULL) term = strdup(getterm); read(pipefd[0], &msg, sizeof(struct child_msg));
if (clearenv() != 0) { close(pipefd[0]);
print_errno("clearenv"); if (msg.err) {
_exit(EXIT_FAILURE); errno = msg._errno;
print_errno(msg.msg);
return false;
} }
char** envlist = pam_getenvlist(pamh); if (cb) cb();
if (envlist == NULL) { printf("\x1b[0m\x1b[H\x1b[J");
print_errno("pam_getenvlist"); (void)fflush(stdout);
_exit(EXIT_FAILURE); close(pipefd[1]);
}
for (size_t i = 0; envlist[i] != NULL; i++) {
putenv(envlist[i]);
}
// FIXME: path hotfix
putenv("PATH=/bin:/usr/bin");
if (term != NULL) {
setenv("TERM", term, true);
free(term);
}
free((void*)envlist); int exit_code;
moarEnv(user, session, pw, config); waitpid((pid_t)pid, &exit_code, 0);
// TODO: chown stdin to user
// does it inherit stdin from parent and
// does parent need to reclaim it after
// this dies?
if (setgid(pw->pw_gid) == -1) {
print_errno("setgid");
_exit(EXIT_FAILURE);
}
if (initgroups(user, pw->pw_gid) == -1) {
print_errno("initgroups");
_exit(EXIT_FAILURE);
}
if (setuid(pw->pw_uid) == -1) {
perror("setuid");
_exit(EXIT_FAILURE);
}
if (cb != NULL) cb();
*reach_session = true;
// TODO: test existence of executable with TryExec
printf("\x1b[0m");
// NOLINTNEXTLINE(bugprone-branch-clone)
if (session.type == SHELL) {
clear_screen();
(void)fflush(stdout);
execlp(session.exec, session.exec, NULL);
} else if (session.type == XORG || session.type == WAYLAND) {
clear_screen();
(void)fflush(stdout);
// NOLINTNEXTLINE
execvp(desktop_exec[0], desktop_exec);
// NOLINTNEXTLINE
free_parsed_args(desktop_count, desktop_exec);
}
perror("exec error");
(void)fputs("failure calling session\n", stderr);
} else {
pid_t child_pid = (pid_t)pid;
waitpid(child_pid, NULL, 0);
pam_setcred(pamh, PAM_DELETE_CRED); pam_setcred(pamh, PAM_DELETE_CRED);
pam_close_session(pamh, 0); pam_close_session(pamh, 0);
pam_end(pamh, PAM_SUCCESS); pam_end(pamh, PAM_SUCCESS);
if (*reach_session == false) return false; if (exit_code != 0) return false;
exit(0); exit(0);
} }

View File

@@ -4,6 +4,7 @@
#include <stdlib.h> #include <stdlib.h>
#include "chvt.h" #include "chvt.h"
#include "macros.h"
static char* vterms[] = {"/dev/tty", "/dev/tty0", "/dev/vc/0", "/dev/systty", static char* vterms[] = {"/dev/tty", "/dev/tty0", "/dev/vc/0", "/dev/systty",
"/dev/console"}; "/dev/console"};
@@ -24,15 +25,13 @@ int chvt_str(char* str) {
} }
int chvt(int n) { int chvt(int n) {
// NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
(void)fprintf(stderr, "activating vt %d\n", n); (void)fprintf(stderr, "activating vt %d\n", n);
// NOLINTNEXTLINE(readability-identifier-length) // NOLINTNEXTLINE(readability-identifier-length)
char c = 0; char c = 0;
for (size_t i = 0; i < sizeof(vterms) / sizeof(vterms[0]); i++) { for (size_t i = 0; i < LEN(vterms); i++) {
int fd = open(vterms[i], O_RDWR); int fd = open(vterms[i], O_RDWR);
if (fd >= 0 && isatty(fd) && ioctl(fd, KDGKBTYPE, &c) == 0 && c < 3) { if (fd >= 0 && isatty(fd) && ioctl(fd, KDGKBTYPE, &c) == 0 && c < 3) {
if (ioctl(fd, VT_ACTIVATE, n) < 0 || ioctl(fd, VT_WAITACTIVE, n) < 0) { if (ioctl(fd, VT_ACTIVATE, n) < 0 || ioctl(fd, VT_WAITACTIVE, n) < 0) {
// NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
(void)fprintf(stderr, "Couldn't activate vt %d\n", n); (void)fprintf(stderr, "Couldn't activate vt %d\n", n);
return -1; return -1;
} }

View File

@@ -117,12 +117,12 @@ union typ_ptr {
char** string; char** string;
long long* number; long long* number;
bool* boolean; bool* boolean;
enum keys* key; enum Keys* key;
struct Vector* vec; struct Vector* vec;
uintptr_t ptr; uintptr_t ptr;
}; };
struct parser_error parse_key(enum introspection_type typ, union typ_ptr at, struct parser_error parse_key(enum IntrospectionType typ, union typ_ptr at,
char* key, size_t offset) { char* key) {
char* aux_str = NULL; char* aux_str = NULL;
struct parser_error aux_err; struct parser_error aux_err;
@@ -219,7 +219,7 @@ struct status config_line_handler(void* _config, char* table, char* k,
log_printf("[I] parsing [%s.%s] as %s\n", table, k, log_printf("[I] parsing [%s.%s] as %s\n", table, k,
INTROS_TYS_NAMES[this_intros_key->typ]); INTROS_TYS_NAMES[this_intros_key->typ]);
struct parser_error err = parse_key(this_intros_key->typ, k_addr, v, offset); struct parser_error err = parse_key(this_intros_key->typ, k_addr, v);
if (err.msg != NULL) { if (err.msg != NULL) {
log_printf("[E] cfg parser, failed to parse [%s.%s] (%s): %s\n", table, k, log_printf("[E] cfg parser, failed to parse [%s.%s] (%s): %s\n", table, k,
INTROS_TYS_NAMES[this_intros_key->typ], err.msg); INTROS_TYS_NAMES[this_intros_key->typ], err.msg);

View File

@@ -1,4 +1,3 @@
// NOLINTBEGIN(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,readability-function-cognitive-complexity)
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -20,6 +19,7 @@ char* trim_str(char* str) {
return str; return str;
} }
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
int read_desktop(FILE* fd, void* ctx, int read_desktop(FILE* fd, void* ctx,
struct status (*cb)(void* ctx, char* table, char* key, struct status (*cb)(void* ctx, char* table, char* key,
char* value)) { char* value)) {
@@ -82,4 +82,3 @@ int read_desktop(FILE* fd, void* ctx,
if (buf != NULL) free(buf); if (buf != NULL) free(buf);
return ret; return ret;
} }
// NOLINTEND(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,readability-function-cognitive-complexity)

View File

@@ -4,8 +4,6 @@
#include "ui.h" #include "ui.h"
#include "util.h" #include "util.h"
// NOLINTBEGIN(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
struct editable_field efield_new(char* content) { struct editable_field efield_new(char* content) {
struct editable_field efield; struct editable_field efield;
if (content != NULL) { if (content != NULL) {
@@ -80,5 +78,3 @@ bool efield_seek(struct editable_field* self, char seek) {
self->pos = (u_char)(ptr - self->content); self->pos = (u_char)(ptr - self->content);
return ptr != start; return ptr != start;
} }
// NOLINTEND(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)

163
src/pam.c Normal file
View File

@@ -0,0 +1,163 @@
#include <pwd.h>
#include <security/_pam_types.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "macros.h"
#include "pam.h"
#include "sessions.h"
struct envpair {
const char* NNULLABLE name;
char* NULLABLE value;
};
char* NULLABLE make_env_kv(const char* NNULLABLE key, char* NNULLABLE value) {
char* buf = NULL;
asprintf(&buf, "%s=%s", key, value);
return buf;
}
void free_envlist(char** NNULLABLE envlist) {
for (char** ptr = envlist; *ptr; ptr++)
free(*ptr);
free((void*)envlist);
}
// NULL when allocation failure
// in any case, envlist would be freed after this function
char** NULLABLE merge_envlist(char** NNULLABLE envlist, struct envpair extra[],
size_t extra_len) {
size_t envlist_len = 0;
while (envlist[envlist_len])
envlist_len++;
size_t nonnullelems = 0;
for (size_t i = 0; i < extra_len; i++) {
if (extra[i].value) nonnullelems++;
}
size_t new_envlist_len = envlist_len + nonnullelems + 1;
char** new_envlist =
(char**)realloc((void*)envlist, sizeof(char*) * new_envlist_len);
if (!new_envlist) {
free_envlist(envlist);
return NULL;
}
// NOLINTNEXTLINE(readability-identifier-length)
size_t k = 0;
for (size_t i = 0; i < extra_len; i++) {
if (!extra[i].value) continue;
char* env_kv = make_env_kv(extra[i].name, extra[i].value);
if (!env_kv) goto free_new_envlist_extra;
new_envlist[envlist_len + k++] = env_kv;
}
new_envlist[envlist_len + nonnullelems] = NULL;
return new_envlist;
free_new_envlist_extra:
for (size_t j = 0; j < envlist_len + k; j++) {
free(new_envlist[envlist_len + j]);
}
free((void*)new_envlist);
return NULL;
}
char* NULLABLE xdg_ssession_type_str(enum SessionType typ) {
char* xdg_session_type = NULL;
if (typ == SHELL) xdg_session_type = "tty";
if (typ == XORG) xdg_session_type = "x11";
if (typ == WAYLAND) xdg_session_type = "wayland";
return xdg_session_type;
}
#define FAIL_ALLOC() \
{ \
return (struct pamh_getenv_status){.error_flag = PAMH_ERR_ALLOC}; \
}
#define FAIL(ERR, ERRFN) \
{ \
return (struct pamh_getenv_status){.error_flag = (ERR), .errfn = (ERRFN)}; \
}
struct pamh_getenv_status pamh_get_complete_env(pam_handle_t* handle,
struct passwd* NNULLABLE pw,
enum SessionType session_typ) {
char** raw_envlist = pam_getenvlist(handle);
if (!raw_envlist) FAIL(PAMH_ERR_ERRNO, "pam_getenvlist");
struct envpair extra_env[] = {
{"TERM", getenv("TERM")},
{"PATH", getenv("PATH")},
{"HOME", pw->pw_dir},
{"USER", pw->pw_name},
{"SHELL", pw->pw_shell},
{"LOGNAME", pw->pw_name},
{"XDG_SESSION_TYPE", xdg_ssession_type_str(session_typ)}};
char** envlist = merge_envlist(raw_envlist, extra_env, LEN(extra_env));
if (!envlist) FAIL_ALLOC();
return (struct pamh_getenv_status){
.error_flag = PAMH_ERR_NOERR,
.envlist = envlist,
};
}
#undef FAIL
#undef FAIL_ALLOC
///////////////
int pam_conversation(int num_msg, const struct pam_message** msg,
struct pam_response** resp, void* appdata_ptr) {
struct pam_response* reply = malloc(sizeof(struct pam_response) * num_msg);
if (!reply) {
return PAM_BUF_ERR;
}
for (int i = 0; i < num_msg; i++) {
reply[i].resp = NULL;
reply[i].resp_retcode = 0;
if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF ||
msg[i]->msg_style == PAM_PROMPT_ECHO_ON) {
char* input = (char*)appdata_ptr;
reply[i].resp = strdup(input);
}
}
*resp = reply;
return PAM_SUCCESS;
}
#ifndef PAM_SERVICE_FALLBACK
#define PAM_SERVICE_FALLBACK "login"
#endif
#define CHECK_PAM_RET(call) \
ret = (call); \
if (ret != PAM_SUCCESS) { \
pam_end(pamh, ret); \
return NULL; \
}
pam_handle_t* get_pamh(char* user, char* passwd) {
pam_handle_t* pamh = NULL;
struct pam_conv pamc = {pam_conversation, (void*)passwd};
int ret;
char* pam_service_override = getenv("LIDM_PAM_SERVICE");
char* pam_service_name =
pam_service_override ? pam_service_override : PAM_SERVICE_FALLBACK;
CHECK_PAM_RET(pam_start(pam_service_name, user, &pamc, &pamh))
CHECK_PAM_RET(pam_authenticate(pamh, 0))
CHECK_PAM_RET(pam_acct_mgmt(pamh, 0))
CHECK_PAM_RET(pam_setcred(pamh, PAM_ESTABLISH_CRED))
CHECK_PAM_RET(pam_open_session(pamh, 0))
CHECK_PAM_RET(pam_setcred(pamh, PAM_REINITIALIZE_CRED))
return pamh;
}
#undef CHECK_PAM_RET

View File

@@ -8,12 +8,14 @@
#include <sys/types.h> #include <sys/types.h>
#include "desktop.h" #include "desktop.h"
#include "desktop_exec.h"
#include "log.h" #include "log.h"
#include "macros.h"
#include "sessions.h" #include "sessions.h"
#include "util.h" #include "util.h"
struct source_dir { struct source_dir {
enum session_type type; enum SessionType type;
char* dir; char* dir;
}; };
@@ -72,9 +74,11 @@ struct status cb(void* _ctx, char* NULLABLE table, char* key, char* value) {
// also, always return 0 or we will break parsing and we don't want a bad // also, always return 0 or we will break parsing and we don't want a bad
// desktop file to break all possible sessions // desktop file to break all possible sessions
static enum session_type session_type; static enum SessionType session_type;
// NOLINTNEXTLINE(readability-function-cognitive-complexity) // NOLINTNEXTLINE(readability-function-cognitive-complexity)
static int fn(const char* fpath, const struct stat* sb, int typeflag) { static int fn(const char* fpath, const struct stat* sb, int typeflag) {
UNUSED(sb);
// guessing symlink behavior // guessing symlink behavior
// - FTW_PHYS if set doesn't follow symlinks, so ftw() has no flags and it // - FTW_PHYS if set doesn't follow symlinks, so ftw() has no flags and it
// follows symlinks, we should never get to handle that // follows symlinks, we should never get to handle that
@@ -95,22 +99,33 @@ static int fn(const char* fpath, const struct stat* sb, int typeflag) {
} }
int ret = read_desktop(fd, &ctx, &cb); int ret = read_desktop(fd, &ctx, &cb);
if (ret < 0) { // any error // any error
log_printf("[E] format error parsing %s", fpath); if (ret < 0) goto err_close;
return 0;
}
(void)fclose(fd); (void)fclose(fd);
// TODO: filter based on tryexec
// https://specifications.freedesktop.org/desktop-entry-spec/latest/recognized-keys.html
free(ctx.tryexec);
// just add this to the list // just add this to the list
if (ctx.name != NULL && ctx.exec != NULL) { if (ctx.name != NULL && ctx.exec != NULL) {
struct session* this_session = malloc(sizeof(struct session)); struct session* this_session = malloc(sizeof(struct session));
if (this_session == NULL) return 0; if (this_session == NULL) return 0;
int arg_count;
char** args;
int parse_status = parse_exec_string(ctx.exec, &arg_count, &args);
if (parse_status != 0 || arg_count == 0 || !args[0]) {
log_printf("[E] parsing exec string '%s': %d\n", ctx.exec, parse_status);
free(this_session);
goto err_parsing;
}
free(ctx.exec);
*this_session = (struct session){ *this_session = (struct session){
.name = ctx.name, .name = ctx.name,
.exec = ctx.exec, .exec = session_exec_desktop(arg_count, args),
.tryexec = ctx.tryexec,
.type = session_type, .type = session_type,
}; };
@@ -118,6 +133,12 @@ static int fn(const char* fpath, const struct stat* sb, int typeflag) {
} }
return 0; return 0;
err_close:
(void)fclose(fd);
err_parsing:
log_printf("[E] format error parsing %s", fpath);
return 0;
} }
// This code is designed to be run purely single threaded // This code is designed to be run purely single threaded
@@ -127,7 +148,7 @@ struct Vector get_avaliable_sessions() {
vec_reserve(&sessions, LIKELY_BOUND_SESSIONS); vec_reserve(&sessions, LIKELY_BOUND_SESSIONS);
cb_sessions = &sessions; cb_sessions = &sessions;
for (size_t i = 0; i < (sizeof(SOURCES) / sizeof(SOURCES[0])); i++) { for (size_t i = 0; i < LEN(SOURCES); i++) {
log_printf("[I] parsing into %s\n", SOURCES[i].dir); log_printf("[I] parsing into %s\n", SOURCES[i].dir);
session_type = SOURCES[i].type; session_type = SOURCES[i].type;
ftw(SOURCES[i].dir, &fn, 1); ftw(SOURCES[i].dir, &fn, 1);

View File

@@ -25,6 +25,7 @@
#include "keys.h" #include "keys.h"
#include "launch_state.h" #include "launch_state.h"
#include "log.h" #include "log.h"
#include "macros.h"
#include "ofield.h" #include "ofield.h"
#include "sessions.h" #include "sessions.h"
#include "ui.h" #include "ui.h"
@@ -61,6 +62,7 @@ struct config* g_config = NULL;
static volatile sig_atomic_t need_resize = 0; static volatile sig_atomic_t need_resize = 0;
static void process_sigwinch(int signal) { static void process_sigwinch(int signal) {
UNUSED(signal);
need_resize = 1; need_resize = 1;
} }
@@ -151,7 +153,7 @@ void ui_update_cursor_focus() {
(void)printf("\x1b[%d;%dH", line, col); (void)printf("\x1b[%d;%dH", line, col);
} }
void ui_update_field(enum input focused_input) { void ui_update_field(enum Input focused_input) {
if (focused_input == PASSWD) { if (focused_input == PASSWD) {
print_passwd(utf8len(of_passwd.efield.content), false); print_passwd(utf8len(of_passwd.efield.content), false);
} else if (focused_input == SESSION) { } else if (focused_input == SESSION) {
@@ -170,7 +172,7 @@ void ui_update_ffield() {
} }
void ui_update_ofield(struct opts_field* NNULLABLE self) { void ui_update_ofield(struct opts_field* NNULLABLE self) {
enum input input; enum Input input;
if (self == &of_session) if (self == &of_session)
input = SESSION; input = SESSION;
else if (self == &of_user) else if (self == &of_user)
@@ -280,28 +282,29 @@ int load(struct Vector* users, struct Vector* sessions) {
(void)fflush(stdout); (void)fflush(stdout);
if (!read_press_nb(&len, seq, &tv)) continue; if (!read_press_nb(&len, seq, &tv)) continue;
if (*seq == '\x1b') { if (*seq == '\x1b') {
enum keys ansi_code = find_ansi(seq); struct option_keys ansi_code = find_ansi(seq);
if (ansi_code != -1) { if (ansi_code.is_some) {
if (ansi_code == ESC) { enum Keys ansi_key = ansi_code.key;
if (ansi_key == ESC) {
esc = 2; esc = 2;
} else if (ansi_code == g_config->functions.refresh) { } else if (ansi_key == g_config->functions.refresh) {
restore_all(); restore_all();
return 0; return 0;
} else if (ansi_code == g_config->functions.reboot) { } else if (ansi_key == g_config->functions.reboot) {
restore_all(); restore_all();
reboot(RB_AUTOBOOT); reboot(RB_AUTOBOOT);
exit(0); exit(0);
} else if (ansi_code == g_config->functions.poweroff) { } else if (ansi_key == g_config->functions.poweroff) {
restore_all(); restore_all();
reboot(RB_POWER_OFF); reboot(RB_POWER_OFF);
exit(0); exit(0);
} else if (ansi_code == A_UP || ansi_code == A_DOWN) { } else if (ansi_key == A_UP || ansi_key == A_DOWN) {
st_ch_focus(ansi_code == A_DOWN ? 1 : -1); st_ch_focus(ansi_key == A_DOWN ? 1 : -1);
} else if (ansi_code == A_RIGHT || ansi_code == A_LEFT) { } else if (ansi_key == A_RIGHT || ansi_key == A_LEFT) {
if (esc) if (esc)
st_ch_of_opts(ansi_code == A_RIGHT ? 1 : -1); st_ch_of_opts(ansi_key == A_RIGHT ? 1 : -1);
else else
st_ch_ef_col(ansi_code == A_RIGHT ? 1 : -1); st_ch_ef_col(ansi_key == A_RIGHT ? 1 : -1);
} }
} }
} else { } else {

View File

@@ -4,7 +4,7 @@
#include "ui.h" #include "ui.h"
#include "users.h" #include "users.h"
enum input focused_input = PASSWD; enum Input focused_input = PASSWD;
struct Vector* gusers; struct Vector* gusers;
struct Vector* gsessions; struct Vector* gsessions;
@@ -13,7 +13,7 @@ struct opts_field of_session;
struct opts_field of_user; struct opts_field of_user;
struct opts_field of_passwd; struct opts_field of_passwd;
struct opts_field* NNULLABLE get_opts_field(enum input from) { struct opts_field* NNULLABLE get_opts_field(enum Input from) {
if (from == SESSION) return &of_session; if (from == SESSION) return &of_session;
if (from == USER) return &of_user; if (from == USER) return &of_user;
if (from == PASSWD) return &of_passwd; if (from == PASSWD) return &of_passwd;
@@ -40,7 +40,8 @@ struct session st_session(bool include_defshell) {
if (include_defshell && of_session.current_opt == gsessions->length + 1) { if (include_defshell && of_session.current_opt == gsessions->length + 1) {
struct session shell_session; struct session shell_session;
shell_session.type = SHELL; shell_session.type = SHELL;
shell_session.exec = shell_session.name = st_user().shell; shell_session.exec =
session_exec_shell(shell_session.name = st_user().shell);
return shell_session; return shell_session;
} }
@@ -49,7 +50,8 @@ struct session st_session(bool include_defshell) {
struct session custom_session; struct session custom_session;
custom_session.type = SHELL; custom_session.type = SHELL;
custom_session.name = custom_session.exec = of_session.efield.content; custom_session.exec =
session_exec_shell(custom_session.name = of_session.efield.content);
return custom_session; return custom_session;
} }
@@ -86,7 +88,7 @@ void st_kbd_type(char* text, bool cfg_include_defshell) {
start = st_user().username; start = st_user().username;
if (focused_input == SESSION && of_session.current_opt != 0 && if (focused_input == SESSION && of_session.current_opt != 0 &&
st_session(cfg_include_defshell).type == SHELL) st_session(cfg_include_defshell).type == SHELL)
start = st_session(cfg_include_defshell).exec; start = st_session(cfg_include_defshell).exec.shell;
ofield_kbd_type(field, text, start); ofield_kbd_type(field, text, start);
ui_update_ffield(); ui_update_ffield();

View File

@@ -13,10 +13,10 @@
static int selret_magic(); static int selret_magic();
int find_keyname(enum keys* at, const char* name) { int find_keyname(enum Keys* at, const char* name) {
for (size_t i = 0; i < LEN(KEY_MAPPINGS); i++) { for (size_t i = 0; i < LEN(KEY_MAPPINGS); i++) {
if (strcmp(KEY_NAMES[i], name) == 0) { if (strcmp(KEY_NAMES[i], name) == 0) {
*at = (enum keys)i; *at = (enum Keys)i;
return 0; return 0;
} }
} }
@@ -24,16 +24,19 @@ int find_keyname(enum keys* at, const char* name) {
return -1; return -1;
} }
enum keys find_ansi(const char* seq) { struct option_keys find_ansi(const char* seq) {
for (size_t i = 0; i < LEN(KEY_MAPPINGS); i++) { for (size_t i = 0; i < LEN(KEY_MAPPINGS); i++) {
struct key_mapping mapping = KEY_MAPPINGS[i]; struct key_mapping mapping = KEY_MAPPINGS[i];
for (size_t j = 0; mapping.sequences[j] != NULL; j++) { for (size_t j = 0; mapping.sequences[j] != NULL; j++) {
if (strcmp(mapping.sequences[j], seq) == 0) { if (strcmp(mapping.sequences[j], seq) == 0) {
return (enum keys)i; return (struct option_keys){
.is_some = true,
.key = (enum Keys)i,
};
} }
} }
} }
return -1; return (struct option_keys){.is_some = false};
} }
void read_press(u_char* length, char* out) { void read_press(u_char* length, char* out) {
@@ -147,6 +150,23 @@ const struct Vector VEC_NEW = {
.pages = NULL, .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) { int vec_resize(struct Vector* self, size_t size) {
void** new_location = void** new_location =
(void**)realloc((void*)self->pages, size * sizeof(void*)); (void**)realloc((void*)self->pages, size * sizeof(void*));