8 Commits

Author SHA1 Message Date
64a1368717 feat: xorg sessions work
leaves a few TODOs:
* error handling
* properly passing VT around
* `wait_for_x_ready`
* allow compile-time overriding `DEFAULT_XORG_DISPLAY` and `XORG_COMMAND`
2025-09-03 14:50:39 +02:00
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
27 changed files with 709 additions and 461 deletions

View File

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

View File

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

View File

@@ -27,11 +27,10 @@ kmscon -l --vt /dev/tty7 --font-name "Cascadia Code" -- /usr/bin/lidm
## Features
- Simple as C, meant to depend only on standard libc and basic unix system headers.
- Fully customizable: ALL strings, colors (with its ANSI keys) and most behavior.
- Xorg[\*](https://github.com/javalsai/lidm/issues/79) and wayland sessions, while supporting the default user shell (if enabled in config)
- Init agnostinc (systemd, dinit, runit, openrc and s6).
- Supports [fido yubikeys](./docs/yubikey.md) (via pam_u2f).
- Simple as C, you only need a C compiler and standard unix libraries to build this.
- Fully customizable, from strings, including action keys, to colors (I hope you know ansi escape codes)
- Automatically detects xorg and wayland sessions, plus allowing to launch the default user shell (if enabled in config)
- Starts with many init systems (systemd, dinit, runit, openrc and s6).
# Table of Contents

View File

@@ -69,13 +69,13 @@ Characters for the corners of the box (ctl = corner top left, cbr = corner botto
.SS functions
All these are of type \fBKEY\fP.
.TP
\fBpoweroff, reboot, fido, refresh\fP
\fBpoweroff, reboot, refresh\fP
Function key to use for such action.
.SS strings
Display strings to use for some elements.
.TP
\fBf_poweroff, f_reboot, f_fido, f_refresh\fP
\fBf_poweroff, f_reboot, f_refresh\fP
Text displayed to name such functions at the bottom of the screen.
.TP
\fBe_user, e_passwd\fP

View File

@@ -1,30 +0,0 @@
# Yubikeys
Quick reference explaining how yubikeys work for now.
# Enable
Yubikeys are disabled by default, to enable them activate a keybinding for it (`[functions] fido`) in the config file.
Note that pressing this configured keybinding has no difference from trying to log in with an empty password, there's virtually no difference.
`pam_u2f` must be configured with a registered key (`pamu2fcfg`).
# Extra
All my yubikey knowledge comes from the [pr that implemented this](https://github.com/javalsai/lidm/pull/89), please refer to it for extra details. Contributions to this documentation are welcome (explaining more in detail, potential issues... really anything that improves this).
Allegedly this pam module configuration should work:
```pam
#%PAM-1.0
auth sufficient pam_u2f.so cue
auth requisite pam_nologin.so
auth include system-local-login
account include system-local-login
session include system-local-login
password include system-local-login
```
Also, I recommend giving the [arch wiki](https://wiki.archlinux.org/title/YubiKey) a read anyways.

View File

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

View File

@@ -8,7 +8,7 @@
#include "macros.h"
#include "util.h"
enum introspection_type {
enum IntrospectionType {
STRING,
BOOL,
NUMBER,
@@ -26,7 +26,7 @@ static const char* NNULLABLE const INTROS_TYS_NAMES[] = {
struct introspection_item {
char* NNULLABLE name;
size_t offset;
enum introspection_type typ;
enum IntrospectionType typ;
};
#define INTROS_ITEM(key, table, ty) \
@@ -90,17 +90,15 @@ BUILD(colors, COLORS, TABLE_COLORS);
BUILD(chars, CHARS, TABLE_CHARS);
#define TABLE_FUNCTIONS(F, name) \
F(enum keys, poweroff, KEY, F1, name) \
F(enum keys, reboot, KEY, F2, name) \
F(enum keys, fido, KEY, NONE, name) \
F(enum keys, refresh, KEY, F5, name)
F(enum Keys, poweroff, KEY, F1, name) \
F(enum Keys, reboot, KEY, F2, name) \
F(enum Keys, refresh, KEY, F5, name)
BUILD(functions, FUNCTIONS, TABLE_FUNCTIONS);
#define TABLE_STRINGS(F, name) \
F(char* NNULLABLE, f_poweroff, STRING, "poweroff", name) \
F(char* NNULLABLE, f_reboot, STRING, "reboot", name) \
F(char* NNULLABLE, f_fido, STRING, "fido", name) \
F(char* NNULLABLE, f_refresh, STRING, "refresh", name) \
F(char* NNULLABLE, e_user, STRING, "user", name) \
F(char* NNULLABLE, e_passwd, STRING, "password", name) \
@@ -152,15 +150,15 @@ struct introspection_table {
static const struct introspection_table CONFIG_INSTROSPECTION[] = {
{"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,
sizeof(INTROS_TABLE_CHARS) / sizeof(INTROS_TABLE_CHARS[0])},
LEN(INTROS_TABLE_CHARS)},
{"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,
sizeof(INTROS_TABLE_STRINGS) / sizeof(INTROS_TABLE_STRINGS[0])},
LEN(INTROS_TABLE_STRINGS)},
{"behavior", offsetof(struct config, behavior), INTROS_TABLE_BEHAVIOR,
sizeof(INTROS_TABLE_BEHAVIOR) / sizeof(INTROS_TABLE_BEHAVIOR[0])},
LEN(INTROS_TABLE_BEHAVIOR)},
};
//// FUNCTIONS

View File

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

View File

@@ -3,7 +3,7 @@
#include <stdlib.h>
enum keys {
enum Keys {
ESC,
F1,
F2,
@@ -32,11 +32,9 @@ enum keys {
END,
PAGE_UP,
PAGE_DOWN,
NONE,
};
static const char* const KEY_NAMES[] = {
[NONE] = "NONE",
[ESC] = "ESC",
[F1] = "F1",
[F2] = "F2",
@@ -68,7 +66,7 @@ static const char* const KEY_NAMES[] = {
};
struct key_mapping {
enum keys key;
enum Keys key;
const char* sequences[3];
};

View File

@@ -36,5 +36,6 @@
#endif
#define LEN(X) (sizeof(X) / sizeof((X)[0]))
#define UNUSED(x) ((void)(x))
#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,71 @@
#define SESSIONSH_
#include <sys/types.h>
#include <unistd.h>
#include "desktop_exec.h"
#include "macros.h"
#include "util.h"
enum session_type {
enum SessionType {
XORG,
WAYLAND,
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 execvpe_desktop(exec->desktop.args, envlist);
default:
__builtin_unreachable();
}
}
struct session {
char* NNULLABLE name;
char* NNULLABLE exec;
char* NULLABLE tryexec;
enum session_type type;
struct session_exec exec;
enum SessionType type;
};
struct Vector get_avaliable_sessions();

View File

@@ -39,17 +39,15 @@
#define VALUES_SEPR 3
#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;
void setup(struct config* config);
int load(struct Vector* users, struct Vector* sessions);
void print_err(const char* /*msg*/);
void print_errno(const char* /*descr*/);
void print_pam_msg(const char* msg, int msg_style);
void clear_pam_msg(void);
void ui_update_field(enum input focused_input);
void ui_update_field(enum Input focused_input);
void ui_update_ffield();
void ui_update_ofield(struct opts_field* self);
void ui_update_cursor_focus();

View File

@@ -6,7 +6,7 @@
#include "macros.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_user;
@@ -15,7 +15,7 @@ extern struct opts_field of_passwd;
extern struct Vector* UNULLABLE gusers;
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 user st_user();

View File

@@ -10,12 +10,18 @@
#include "keys.h"
int find_keyname(enum keys* at, const char* name);
enum keys find_ansi(const char* seq);
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);
@@ -24,12 +30,16 @@ 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);

View File

@@ -1,114 +1,41 @@
// TODO: handle `fork() == -1`s
#include <errno.h>
#include <grp.h>
#include <pwd.h>
#include <security/pam_misc.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include "auth.h"
#include "config.h"
#include "desktop_exec.h"
#include "log.h"
#include "macros.h"
#include "pam.h"
#include "sessions.h"
#include "ui.h"
#include "unistd.h"
#include "util.h"
struct pam_conv_data {
char* password;
void (*display_pam_msg)(const char* msg, int msg_style);
};
#define DEFAULT_XORG_DISPLAY 0
// no PATH search for now
#define XORG_COMMAND "/usr/bin/X"
int pam_conversation(int num_msg, const struct pam_message** msg,
struct pam_response** resp, void* appdata_ptr) {
struct pam_response* reply =
(struct pam_response*)malloc(sizeof(struct pam_response) * num_msg);
if (!reply) {
return PAM_BUF_ERR;
static void try_source_file(struct Vector* NNULLABLE vec_envlist,
char* NNULLABLE filepath) {
log_printf("sourcing %s\n", filepath);
FILE* file2source = fopen(filepath, "r");
if (file2source == NULL) {
log_printf("error sourcing %s\n", filepath);
return;
}
struct pam_conv_data* conv_data = (struct pam_conv_data*)appdata_ptr;
for (size_t i = 0; i < num_msg; i++) {
reply[i].resp = NULL;
reply[i].resp_retcode = 0;
switch (msg[i]->msg_style) {
case PAM_PROMPT_ECHO_OFF:
case PAM_PROMPT_ECHO_ON:
reply[i].resp = strdup(conv_data->password);
if (!reply[i].resp) {
for (size_t j = 0; j < i; j++)
free(reply[j].resp);
free(reply);
return PAM_BUF_ERR;
}
break;
case PAM_TEXT_INFO:
case PAM_ERROR_MSG:
if (conv_data->display_pam_msg && msg[i]->msg) {
conv_data->display_pam_msg(msg[i]->msg, msg[i]->msg_style);
}
break;
default:
break;
}
}
*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_data conv_data = {.password = passwd,
.display_pam_msg = print_pam_msg};
struct pam_conv pamc = {pam_conversation, (void*)&conv_data};
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;
size_t len = 0;
ssize_t read;
@@ -117,13 +44,10 @@ void sourceFileTry(char* file) {
if (read == 0 || (read > 0 && *line == '#')) continue;
if (line[read - 1] == '\n') line[read - 1] = '\0';
/* printf("Retrieved line of length %zu:\n", read); */
/* printf("%s\n", line); */
for (size_t i = 1; i < read; i++) {
for (ssize_t i = 1; i < read; i++) {
if (line[i] == '=') {
/* printf("FOUND '='!\n"); */
line[i] = '\0';
setenv(line, &line[i + 1], 1);
vec_push(vec_envlist, (void*)line);
line = NULL;
break;
}
}
@@ -133,53 +57,29 @@ void sourceFileTry(char* file) {
(void)fclose(file2source);
}
void moarEnv(char* user, struct session session, struct passwd* pw,
struct config* config) {
if (chdir(pw->pw_dir) == -1) print_errno("can't chdir to user home");
setenv("HOME", pw->pw_dir, true);
setenv("USER", pw->pw_name, true);
setenv("SHELL", pw->pw_shell, true);
// 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));
static void source_paths(struct Vector* NNULLABLE vec_envlist,
struct Vector* NNULLABLE abs_source,
const char* NULLABLE user_home,
struct Vector* NNULLABLE user_source) {
for (size_t i = 0; i < abs_source->length; i++) {
char* path = vec_get(abs_source, i);
try_source_file(vec_envlist, path);
}
/* printf("\n"); */
if (pw->pw_dir) {
const char* home = pw->pw_dir;
size_t home_len = strlen(home);
for (size_t i = 0; i < config->behavior.user_source.length; i++) {
const char* filename = (char*)vec_get(&config->behavior.user_source, i);
size_t filename_len = strlen(filename);
size_t path_len = home_len + 1 + filename_len + 1; // nullbyte and slash
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);
if (user_home)
for (size_t i = 0; i < user_source->length; i++) {
char* path = NULL;
asprintf(&path, "%s/%s", user_home, (char*)vec_get(user_source, i));
if (!path) {
log_puts("alloc failure on user source\n");
continue;
}
try_source_file(vec_envlist, path);
free(path);
}
else {
log_puts("user has no home\n");
}
}
/*char *buf;*/
@@ -191,27 +91,147 @@ void moarEnv(char* user, struct session session, struct passwd* pw,
/*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;
};
// TODO: OR check if xorg_pid fail exited
static int wait_for_x_ready(const int xorg_pipefd[1], __pid_t xorg_pid) {
// TODO
UNUSED(xorg_pipefd);
UNUSED(xorg_pid);
sleep(2);
return 0;
}
// TODO: properly pass this down
extern int vt;
// TODO: add error msgs
static void launch_with_xorg_server(struct session_exec* NNULLABLE exec,
struct passwd* pw,
char** NNULLABLE envlist) {
int xorg_pipefd[2];
pipe(xorg_pipefd);
(void)fflush(NULL);
__pid_t xorg_pid = fork();
if (xorg_pid == 0) {
if (!pw->pw_dir) _exit(EXIT_FAILURE);
// !!!!!!!!!! ATTENTION: this fails silently, of course add failure msgs but
// for now I can't so be careful
if (vt == -1) _exit(EXIT_FAILURE);
char* display_thing;
asprintf(&display_thing, ":%d", DEFAULT_XORG_DISPLAY);
if (!display_thing) _exit(EXIT_FAILURE);
char* vt_path;
asprintf(&vt_path, "vt%d", vt);
if (!vt_path) {
free(display_thing);
_exit(EXIT_FAILURE);
}
// dup2(xorg_pipefd[1], STDERR_FILENO);
// dup2(xorg_pipefd[1], STDOUT_FILENO);
// close(xorg_pipefd[0]);
// close(xorg_pipefd[1]);
int exit = execle(XORG_COMMAND, XORG_COMMAND, display_thing, vt_path, NULL,
envlist);
perror("exec");
// execle("X", "X", display_thing, vt_path, "-auth", xauth_path,
// "-nolisten", "tcp", "-background", "none", NULL, envlist);
printf("wtf3\n");
(void)fflush(stdout);
free(vt_path);
free(display_thing);
_exit(exit);
}
wait_for_x_ready(xorg_pipefd, xorg_pid);
__pid_t xorg_session_pid = fork();
if (xorg_session_pid == 0) {
int exit = session_exec_exec(exec, envlist);
perror("exec error");
(void)fputs("failure calling session\n", stderr);
_exit(exit);
}
// looks weird, waiting on -1 should wait on any child and then just check if
// its xorg server or the session and kill the other waiting on it
__pid_t pid;
int status; // not even read for now
while ((pid = waitpid(-1, &status, 0)) > 0) {
if (pid == xorg_pid || pid == xorg_session_pid) {
__pid_t pid_to_kill = pid ^ xorg_pid ^ xorg_session_pid;
if (pid == xorg_pid) printf("Xorg server died\n");
if (pid == xorg_session_pid) printf("Xorg session died\n");
kill(pid_to_kill, SIGTERM);
waitpid(pid_to_kill, &status, 0);
printf("wtf %d, x%d s%d - k%d\n", status, xorg_pid, xorg_session_pid,
pid_to_kill);
(void)fflush(stdout);
sleep(10);
break;
}
}
}
#define SEND_MSG(MSG) \
{ \
write(pipefd[1], &(MSG), sizeof(struct child_msg)); \
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* NNULLABLE session,
char** NNULLABLE envlist) {
if (chdir(pw->pw_dir) == -1) SEND_ERR("chdir");
if (setgid(pw->pw_gid) == -1) SEND_ERR("setgid");
if (initgroups(user, pw->pw_gid) == -1) SEND_ERR("initgroups");
if (setuid(pw->pw_uid) == -1) SEND_ERR("setuid");
SEND_MSG((struct child_msg){.err = false});
DUMMY_READ();
close(pipefd[0]);
if (session->type == XORG) {
launch_with_xorg_server(&session->exec, pw, envlist);
} else {
int exit = session_exec_exec(&session->exec, envlist);
perror("exec error");
(void)fputs("failure calling session\n", stderr);
_exit(exit);
}
}
#undef SEND_MSG
#undef SEND_ERR
#undef DUMMY_READ
// NOLINTBEGIN(readability-function-cognitive-complexity)
bool launch(char* user, char* passwd, struct session session, void (*cb)(void),
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);
if (pw == NULL) {
print_err("could not get user info");
@@ -220,96 +240,72 @@ bool launch(char* user, char* passwd, struct session session, void (*cb)(void),
pam_handle_t* pamh = get_pamh(user, passwd);
if (pamh == NULL) {
print_pam_msg("authentication failed", PAM_ERROR_MSG);
print_err("error on pam authentication");
return false;
}
clear_pam_msg();
bool* reach_session = shmalloc(sizeof(bool));
if (reach_session == NULL) {
perror("error allocating shared memory");
struct pamh_getenv_status env_ret =
pamh_get_complete_env(pamh, pw, session.type);
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;
}
*reach_session = false;
struct Vector envlist_vec = vec_from_raw((void**)env_ret.envlist);
if (session.type == XORG) {
char* display_env;
asprintf(&display_env, "DISPLAY=:%d", DEFAULT_XORG_DISPLAY);
if (!display_env) {
print_err("alloc error");
return false;
}
vec_push(&envlist_vec, display_env);
}
source_paths(&envlist_vec, &config->behavior.source, pw->pw_dir,
&config->behavior.user_source);
char** envlist = (char**)vec_as_raw(envlist_vec);
if (!envlist) {
print_err("vec alloc error");
return false;
}
int pipefd[2];
pipe(pipefd);
uint pid = fork();
if (pid == 0) { // child
char* term = NULL;
char* getterm = getenv("TERM");
// TODO: handle malloc error
if (getterm != NULL) term = strdup(getterm);
if (clearenv() != 0) {
print_errno("clearenv");
_exit(EXIT_FAILURE);
if (pid == 0)
forked(pipefd, pw, user, &session, envlist);
else {
struct child_msg msg;
read(pipefd[0], &msg, sizeof(struct child_msg));
close(pipefd[0]);
if (msg.err) {
errno = msg._errno;
print_errno(msg.msg);
return false;
}
char** envlist = pam_getenvlist(pamh);
if (envlist == NULL) {
print_errno("pam_getenvlist");
_exit(EXIT_FAILURE);
}
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);
moarEnv(user, session, pw, config);
// 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();
if (cb) cb();
printf("\x1b[0m\x1b[H\x1b[J");
(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);
close(pipefd[1]);
int exit_code;
waitpid((pid_t)pid, &exit_code, 0);
pam_setcred(pamh, PAM_DELETE_CRED);
pam_close_session(pamh, 0);
pam_end(pamh, PAM_SUCCESS);
if (*reach_session == false) return false;
if (exit_code != 0) return false;
exit(0);
}

View File

@@ -4,6 +4,7 @@
#include <stdlib.h>
#include "chvt.h"
#include "macros.h"
static char* vterms[] = {"/dev/tty", "/dev/tty0", "/dev/vc/0", "/dev/systty",
"/dev/console"};
@@ -23,16 +24,16 @@ int chvt_str(char* str) {
return chvt((int)i);
}
int vt = -1;
int chvt(int n) {
// NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
(void)fprintf(stderr, "activating vt %d\n", n);
vt = n;
// NOLINTNEXTLINE(readability-identifier-length)
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);
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) {
// NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
(void)fprintf(stderr, "Couldn't activate vt %d\n", n);
return -1;
}

View File

@@ -117,12 +117,12 @@ union typ_ptr {
char** string;
long long* number;
bool* boolean;
enum keys* key;
enum Keys* key;
struct Vector* vec;
uintptr_t ptr;
};
struct parser_error parse_key(enum introspection_type typ, union typ_ptr at,
char* key, size_t offset) {
struct parser_error parse_key(enum IntrospectionType typ, union typ_ptr at,
char* key) {
char* aux_str = NULL;
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,
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) {
log_printf("[E] cfg parser, failed to parse [%s.%s] (%s): %s\n", table, k,
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 <stdio.h>
#include <stdlib.h>
@@ -20,6 +19,7 @@ char* trim_str(char* str) {
return str;
}
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
int read_desktop(FILE* fd, void* ctx,
struct status (*cb)(void* ctx, char* table, char* key,
char* value)) {
@@ -82,4 +82,3 @@ int read_desktop(FILE* fd, void* ctx,
if (buf != NULL) free(buf);
return ret;
}
// NOLINTEND(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,readability-function-cognitive-complexity)

View File

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

View File

@@ -4,8 +4,6 @@
#include "ui.h"
#include "util.h"
// NOLINTBEGIN(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
struct editable_field efield_new(char* content) {
struct editable_field efield;
if (content != NULL) {
@@ -80,5 +78,3 @@ bool efield_seek(struct editable_field* self, char seek) {
self->pos = (u_char)(ptr - self->content);
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 "desktop.h"
#include "desktop_exec.h"
#include "log.h"
#include "macros.h"
#include "sessions.h"
#include "util.h"
struct source_dir {
enum session_type type;
enum SessionType type;
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
// desktop file to break all possible sessions
static enum session_type session_type;
static enum SessionType session_type;
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
static int fn(const char* fpath, const struct stat* sb, int typeflag) {
UNUSED(sb);
// guessing symlink behavior
// - FTW_PHYS if set doesn't follow symlinks, so ftw() has no flags and it
// 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);
if (ret < 0) { // any error
log_printf("[E] format error parsing %s", fpath);
return 0;
}
// any error
if (ret < 0) goto err_close;
(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
if (ctx.name != NULL && ctx.exec != NULL) {
struct session* this_session = malloc(sizeof(struct session));
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){
.name = ctx.name,
.exec = ctx.exec,
.tryexec = ctx.tryexec,
.exec = session_exec_desktop(arg_count, args),
.type = session_type,
};
@@ -118,6 +133,12 @@ static int fn(const char* fpath, const struct stat* sb, int typeflag) {
}
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
@@ -127,7 +148,7 @@ struct Vector get_avaliable_sessions() {
vec_reserve(&sessions, LIKELY_BOUND_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);
session_type = SOURCES[i].type;
ftw(SOURCES[i].dir, &fn, 1);

139
src/ui.c
View File

@@ -5,7 +5,6 @@
#include <errno.h>
#include <limits.h>
#include <pwd.h>
#include <security/pam_appl.h>
#include <signal.h>
#include <stdbool.h>
#include <stddef.h>
@@ -26,6 +25,7 @@
#include "keys.h"
#include "launch_state.h"
#include "log.h"
#include "macros.h"
#include "ofield.h"
#include "sessions.h"
#include "ui.h"
@@ -62,6 +62,7 @@ struct config* g_config = NULL;
static volatile sig_atomic_t need_resize = 0;
static void process_sigwinch(int signal) {
UNUSED(signal);
need_resize = 1;
}
@@ -110,8 +111,8 @@ static char* fmt_time(const char* fmt) {
}
}
char* trunc_gethostname(size_t maxlen, const char* const ELLIPSIS) {
if (utf8len(ELLIPSIS) > maxlen) return NULL;
char* trunc_gethostname(const size_t MAXLEN, const char* const ELLIPSIS) {
if (utf8len(ELLIPSIS) > MAXLEN) return NULL;
size_t alloc_size = HOST_NAME_MAX + strlen(ELLIPSIS) + 1;
char* buf = malloc(alloc_size);
if (!buf) return NULL;
@@ -121,8 +122,8 @@ char* trunc_gethostname(size_t maxlen, const char* const ELLIPSIS) {
return NULL;
}
if (utf8len(buf) > maxlen) {
size_t end = utf8trunc(buf, maxlen - utf8len(ELLIPSIS));
if (utf8len(buf) > MAXLEN) {
size_t end = utf8trunc(buf, MAXLEN - utf8len(ELLIPSIS));
strcpy(&buf[end], ELLIPSIS);
}
return buf;
@@ -152,7 +153,7 @@ void ui_update_cursor_focus() {
(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) {
print_passwd(utf8len(of_passwd.efield.content), false);
} else if (focused_input == SESSION) {
@@ -171,7 +172,7 @@ void ui_update_ffield() {
}
void ui_update_ofield(struct opts_field* NNULLABLE self) {
enum input input;
enum Input input;
if (self == &of_session)
input = SESSION;
else if (self == &of_user)
@@ -281,43 +282,29 @@ int load(struct Vector* users, struct Vector* sessions) {
(void)fflush(stdout);
if (!read_press_nb(&len, seq, &tv)) continue;
if (*seq == '\x1b') {
enum keys ansi_code = find_ansi(seq);
if (ansi_code != -1) {
if (ansi_code == ESC) {
struct option_keys ansi_code = find_ansi(seq);
if (ansi_code.is_some) {
enum Keys ansi_key = ansi_code.key;
if (ansi_key == ESC) {
esc = 2;
} else if (ansi_code == g_config->functions.refresh) {
} else if (ansi_key == g_config->functions.refresh) {
restore_all();
return 0;
} else if (ansi_code == g_config->functions.reboot) {
} else if (ansi_key == g_config->functions.reboot) {
restore_all();
reboot(RB_AUTOBOOT);
exit(0);
} else if (ansi_code == g_config->functions.poweroff) {
} else if (ansi_key == g_config->functions.poweroff) {
restore_all();
reboot(RB_POWER_OFF);
exit(0);
} else if (g_config->functions.fido != NONE &&
ansi_code == g_config->functions.fido) {
bool successful_write = write_launch_state((struct LaunchState){
.username = st_user().username,
.session_opt =
st_session(g_config->behavior.include_defshell).name,
});
if (!successful_write) log_puts("[E] failed to write launch state");
if (!launch(st_user().username, "",
st_session(g_config->behavior.include_defshell),
&restore_all, g_config)) {
print_passwd(utf8len(of_passwd.efield.content), true);
ui_update_cursor_focus();
}
} else if (ansi_code == A_UP || ansi_code == A_DOWN) {
st_ch_focus(ansi_code == A_DOWN ? 1 : -1);
} else if (ansi_code == A_RIGHT || ansi_code == A_LEFT) {
} else if (ansi_key == A_UP || ansi_key == A_DOWN) {
st_ch_focus(ansi_key == A_DOWN ? 1 : -1);
} else if (ansi_key == A_RIGHT || ansi_key == A_LEFT) {
if (esc)
st_ch_of_opts(ansi_code == A_RIGHT ? 1 : -1);
st_ch_of_opts(ansi_key == A_RIGHT ? 1 : -1);
else
st_ch_ef_col(ansi_code == A_RIGHT ? 1 : -1);
st_ch_ef_col(ansi_key == A_RIGHT ? 1 : -1);
}
}
} else {
@@ -357,40 +344,25 @@ u_char get_render_pos_offset(struct opts_field* self, u_char maxlen) {
return pos - ofield_display_cursor_col(self, maxlen);
}
#define HOSTNAME_SIZE (VALUES_COL - VALUES_SEPR - BOX_HMARGIN - 1)
void print_head() {
char* fmtd_time = fmt_time(g_config->behavior.timefmt);
size_t len_tm = utf8len(fmtd_time);
// calculate the space available for the host name
ssize_t hostname_size = BOX_WIDTH - (BOX_HMARGIN * 2) - len_tm - VALUES_SEPR;
if (hostname_size < 0) hostname_size = 0;
// hostname doesn't just change on runtime,
// but the length of the time string might
static char* NULLABLE hostname = NULL;
static size_t hostname_calcd_size;
// save the truncated hostname and the length it truncated to,
// if said length changes recalculate this (and free previous str)
if (!hostname || hostname_calcd_size != hostname_size) {
if (hostname) free(hostname);
hostname = trunc_gethostname(hostname_size, g_config->strings.ellipsis);
hostname_calcd_size = hostname_size;
}
// hostname doesn't just change on runtime
static char* hostname = NULL;
if (!hostname)
hostname = trunc_gethostname(HOSTNAME_SIZE, g_config->strings.ellipsis);
if (!hostname) hostname = "unknown";
clean_line(box_start, HEAD_ROW);
// put hostname
if (hostname_size)
printf("\x1b[%dG\x1b[%sm%s\x1b[%sm", box_start.x + 1 + BOX_HMARGIN,
g_config->colors.e_hostname, hostname ? hostname : "unknown",
g_config->colors.fg);
printf("\x1b[%dG\x1b[%sm%s\x1b[%sm",
box_start.x + VALUES_COL - VALUES_SEPR - (uint)utf8len(hostname),
g_config->colors.e_hostname, hostname, g_config->colors.fg);
// put date
char* fmtd_time = fmt_time(g_config->behavior.timefmt);
printf("\x1b[%dG\x1b[%sm%s\x1b[%sm",
box_start.x + BOX_WIDTH - 1 - BOX_HMARGIN - (uint)len_tm,
box_start.x + BOX_WIDTH - 1 - BOX_HMARGIN - (uint)utf8len(fmtd_time),
g_config->colors.e_date, fmtd_time, g_config->colors.fg);
free(fmtd_time);
}
@@ -525,8 +497,6 @@ static void print_box() {
}
static void print_footer() {
bool fido_enabled = g_config->functions.fido != NONE;
size_t bsize = utf8len(g_config->strings.f_poweroff) +
utf8len(KEY_NAMES[g_config->functions.poweroff]) +
utf8len(g_config->strings.f_reboot) +
@@ -534,31 +504,19 @@ static void print_footer() {
utf8len(g_config->strings.f_refresh) +
utf8len(KEY_NAMES[g_config->functions.refresh]);
bsize += 2 * 2 + 3 * 1;
if (fido_enabled) {
bsize += utf8len(g_config->strings.f_fido) +
utf8len(KEY_NAMES[g_config->functions.fido]) + 2 + 1;
}
bsize += 2 * 2 + // 2 wide separators between 3 items
3 * 1; // 3 thin separators inside every item
uint row = window.ws_row - 1;
uint col = window.ws_col - 2 - bsize;
printf("\x1b[%d;%dH%s \x1b[%sm%s\x1b[%sm %s \x1b[%sm%s\x1b[%sm ", row, col,
g_config->strings.f_poweroff, g_config->colors.e_key,
KEY_NAMES[g_config->functions.poweroff], g_config->colors.fg,
g_config->strings.f_reboot, g_config->colors.e_key,
KEY_NAMES[g_config->functions.reboot], g_config->colors.fg);
if (fido_enabled) {
printf("%s \x1b[%sm%s\x1b[%sm ", g_config->strings.f_fido,
g_config->colors.e_key, KEY_NAMES[g_config->functions.fido],
g_config->colors.fg);
}
printf("%s \x1b[%sm%s\x1b[%sm", g_config->strings.f_refresh,
g_config->colors.e_key, KEY_NAMES[g_config->functions.refresh],
g_config->colors.fg);
printf(
"\x1b[%3$d;%4$dH%8$s \x1b[%1$sm%5$s\x1b[%2$sm %9$s "
"\x1b[%1$sm%6$s\x1b[%2$sm %10$s \x1b[%1$sm%7$s\x1b[%2$sm",
g_config->colors.e_key, g_config->colors.fg, row, col,
KEY_NAMES[g_config->functions.poweroff],
KEY_NAMES[g_config->functions.reboot],
KEY_NAMES[g_config->functions.refresh], g_config->strings.f_poweroff,
g_config->strings.f_reboot, g_config->strings.f_refresh);
}
void print_err(const char* msg) {
@@ -566,21 +524,6 @@ void print_err(const char* msg) {
msg, errno, strerror(errno));
}
void print_pam_msg(const char* msg, int msg_style) {
uint row = box_start.y + BOX_HEIGHT + 1;
const char* color =
(msg_style == PAM_ERROR_MSG) ? g_config->colors.err : g_config->colors.fg;
printf("\x1b[%d;%dH\x1b[K\x1b[%sm%.*s\x1b[%sm", row, box_start.x, color,
BOX_WIDTH, msg, g_config->colors.fg);
(void)fflush(stdout);
}
void clear_pam_msg(void) {
uint row = box_start.y + BOX_HEIGHT + 1;
printf("\x1b[%d;%dH\x1b[K", row, box_start.x);
(void)fflush(stdout);
}
void print_errno(const char* descr) {
if (descr == NULL)
(void)fprintf(stderr, "\x1b[%d;%dH\x1b[%smunknown error(%d): %s",

View File

@@ -4,7 +4,7 @@
#include "ui.h"
#include "users.h"
enum input focused_input = PASSWD;
enum Input focused_input = PASSWD;
struct Vector* gusers;
struct Vector* gsessions;
@@ -13,7 +13,7 @@ struct opts_field of_session;
struct opts_field of_user;
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 == USER) return &of_user;
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) {
struct session shell_session;
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;
}
@@ -49,7 +50,8 @@ struct session st_session(bool include_defshell) {
struct session custom_session;
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;
}
@@ -86,7 +88,7 @@ void st_kbd_type(char* text, bool cfg_include_defshell) {
start = st_user().username;
if (focused_input == SESSION && of_session.current_opt != 0 &&
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);
ui_update_ffield();

View File

@@ -13,10 +13,10 @@
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++) {
if (strcmp(KEY_NAMES[i], name) == 0) {
*at = (enum keys)i;
*at = (enum Keys)i;
return 0;
}
}
@@ -24,16 +24,19 @@ int find_keyname(enum keys* at, const char* name) {
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++) {
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 (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) {
@@ -147,6 +150,23 @@ const struct Vector VEC_NEW = {
.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*));

View File

@@ -25,13 +25,11 @@
[functions]
# poweroff = F1
# reboot = F2
# fido = NONE
# refresh = F5
[strings]
# f_poweroff = "poweroff"
# f_reboot = "reboot"
# f_fido = "fido"
# f_refresh = "refresh"
# e_user = "user"
# e_passwd = "password"