mirror of
https://github.com/javalsai/lidm.git
synced 2026-02-27 12:00:44 +01:00
Compare commits
6 Commits
64a1368717
...
9cfe00c197
| Author | SHA1 | Date | |
|---|---|---|---|
|
9cfe00c197
|
|||
|
5749263605
|
|||
|
e5c3c619c3
|
|||
|
|
4eca2b056f | ||
|
|
a7a1f42f0a | ||
|
|
feeba5c41b |
4
Makefile
4
Makefile
@@ -58,7 +58,7 @@ install: lidm
|
||||
uninstall:
|
||||
rm -rf ${DESTDIR}${PREFIX}/bin/lidm ${DESTDIR}/etc/lidm.ini
|
||||
rm -rf ${DESTDIR}/usr/share/man/man{1/lidm.1,5/lidm-config.5}.gz
|
||||
rm -rf ${DESTDIR}/etc/systemd/system/lidm.service ${DESTDIR}/etc/dinit.d/lidm ${DESTDIR}/etc/runit/sv/lidm
|
||||
rm -rf ${DESTDIR}/usr/local/lib/systemd/system/lidm.service ${DESTDIR}/etc/dinit.d/lidm ${DESTDIR}/etc/runit/sv/lidm
|
||||
|
||||
install-service:
|
||||
@if command -v systemctl &> /dev/null; then \
|
||||
@@ -88,7 +88,7 @@ install-service:
|
||||
fi
|
||||
|
||||
install-service-systemd:
|
||||
install -m644 ./assets/services/systemd.service ${DESTDIR}/etc/systemd/system/lidm.service
|
||||
install -m644 ./assets/services/systemd.service ${DESTDIR}/usr/local/lib/systemd/system/lidm.service
|
||||
@printf '\033[1m%s\033[0m\n\n' " don't forget to run 'systemctl enable lidm'"
|
||||
install-service-dinit:
|
||||
install -m644 ./assets/services/dinit ${DESTDIR}/etc/dinit.d/lidm
|
||||
|
||||
@@ -27,10 +27,11 @@ kmscon -l --vt /dev/tty7 --font-name "Cascadia Code" -- /usr/bin/lidm
|
||||
|
||||
## Features
|
||||
|
||||
- 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).
|
||||
- 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).
|
||||
|
||||
# Table of Contents
|
||||
|
||||
|
||||
@@ -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, refresh\fP
|
||||
\fBpoweroff, reboot, fido, refresh\fP
|
||||
Function key to use for such action.
|
||||
|
||||
.SS strings
|
||||
Display strings to use for some elements.
|
||||
.TP
|
||||
\fBf_poweroff, f_reboot, f_refresh\fP
|
||||
\fBf_poweroff, f_reboot, f_fido, f_refresh\fP
|
||||
Text displayed to name such functions at the bottom of the screen.
|
||||
.TP
|
||||
\fBe_user, e_passwd\fP
|
||||
|
||||
@@ -13,7 +13,7 @@ The manual steps for installation are:
|
||||
|
||||
## Systemd
|
||||
|
||||
- Copy `systemd.service` to `/etc/systemd/system/lidm.service`
|
||||
- Copy `systemd.service` to `/usr/local/lib/systemd/system/lidm.service` (if the directory doesn't exist, create it first)
|
||||
- To enable it you can run `systemctl enable lidm`
|
||||
|
||||
## Dinit
|
||||
|
||||
30
docs/yubikey.md
Normal file
30
docs/yubikey.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# 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.
|
||||
@@ -92,6 +92,7 @@ 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)
|
||||
|
||||
BUILD(functions, FUNCTIONS, TABLE_FUNCTIONS);
|
||||
@@ -99,6 +100,7 @@ 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) \
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
#ifndef DESKTOP_EXEC_H_
|
||||
#define DESKTOP_EXEC_H_
|
||||
// TODO: rewrite properly
|
||||
// NOLINTBEGIN(clang-diagnostic-nullability-completeness)
|
||||
|
||||
#include "macros.h"
|
||||
#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);
|
||||
|
||||
#endif
|
||||
// NOLINTEND(clang-diagnostic-nullability-completeness)
|
||||
|
||||
@@ -32,9 +32,11 @@ enum Keys {
|
||||
END,
|
||||
PAGE_UP,
|
||||
PAGE_DOWN,
|
||||
NONE,
|
||||
};
|
||||
|
||||
static const char* const KEY_NAMES[] = {
|
||||
[NONE] = "NONE",
|
||||
[ESC] = "ESC",
|
||||
[F1] = "F1",
|
||||
[F2] = "F2",
|
||||
|
||||
@@ -7,25 +7,25 @@
|
||||
#endif
|
||||
|
||||
// Do we just replace the compiler with clang??
|
||||
#if defined(__clang__)
|
||||
#ifdef __clang__
|
||||
#define NULLABLE _Nullable
|
||||
#else
|
||||
#define NULLABLE
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
#ifdef __clang__
|
||||
#define NNULLABLE _Nonnull
|
||||
#else
|
||||
#define NNULLABLE
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
#ifdef __clang__
|
||||
#define UNULLABLE _Null_unspecified
|
||||
#else
|
||||
#define UNULLABLE
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
#ifdef __clang__
|
||||
#define COMPILER_VERSION __VERSION__
|
||||
#elif defined(__GNUC__)
|
||||
#define xstr(s) str(s)
|
||||
|
||||
@@ -46,6 +46,8 @@ 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_ffield();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// TODO: handle `fork() == -1`s
|
||||
// TODO: handle `fork() == -1`// TODO: handle `fork() == -1`s
|
||||
|
||||
#include <errno.h>
|
||||
#include <grp.h>
|
||||
@@ -240,9 +240,10 @@ bool launch(char* user, char* passwd, struct session session, void (*cb)(void),
|
||||
|
||||
pam_handle_t* pamh = get_pamh(user, passwd);
|
||||
if (pamh == NULL) {
|
||||
print_err("error on pam authentication");
|
||||
print_pam_msg("authentication failed", PAM_ERROR_MSG);
|
||||
return false;
|
||||
}
|
||||
clear_pam_msg();
|
||||
|
||||
struct pamh_getenv_status env_ret =
|
||||
pamh_get_complete_env(pamh, pw, session.type);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// TODO: rewrite properly
|
||||
// NOLINTBEGIN(clang-diagnostic-nullability-completeness)
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -16,7 +18,9 @@
|
||||
// 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"));
|
||||
char* path_env = getenv("PATH");
|
||||
if (!path_env) return NULL;
|
||||
char* path = strdup(path_env);
|
||||
if (!path) return NULL;
|
||||
|
||||
char* tok = strtok(path, ":");
|
||||
@@ -40,6 +44,7 @@ static char* NULLABLE search_path(const char* NNULLABLE for_binary) {
|
||||
tok = strtok(NULL, ":");
|
||||
}
|
||||
|
||||
free(path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -133,6 +138,7 @@ int push_arg(struct ctx* st) {
|
||||
3 = syntax
|
||||
Important: call free_parsed_args afterwards to free the passed ***args
|
||||
*/
|
||||
|
||||
// NOLINTBEGIN(readability-function-cognitive-complexity)
|
||||
int parse_exec_string(const char* exec_s, int* arg_count, char*** args) {
|
||||
if (!exec_s || !args || !arg_count) return 1;
|
||||
@@ -226,3 +232,4 @@ syntax_err:
|
||||
return 3;
|
||||
}
|
||||
// NOLINTEND(readability-function-cognitive-complexity)
|
||||
// NOLINTEND(clang-diagnostic-nullability-completeness)
|
||||
|
||||
@@ -22,6 +22,10 @@ int read_launch_state(struct LaunchState* NNULLABLE state) {
|
||||
|
||||
size_t num = 0;
|
||||
if (getline(&state->username, &num, state_fd) < 0) goto fail;
|
||||
// not sure I can actually prove it but ughh, getline < 0 will ensure there's
|
||||
// something in the string and then I think strcspn is bounded to the final
|
||||
// null byte, so it shouldn't go over
|
||||
// NOLINTNEXTLINE(clang-analyzer-security.ArrayBound)
|
||||
state->username[strcspn(state->username, "\n")] = 0;
|
||||
|
||||
num = 0;
|
||||
|
||||
39
src/pam.c
39
src/pam.c
@@ -9,6 +9,7 @@
|
||||
#include "macros.h"
|
||||
#include "pam.h"
|
||||
#include "sessions.h"
|
||||
#include "ui.h"
|
||||
|
||||
struct envpair {
|
||||
const char* NNULLABLE name;
|
||||
@@ -113,19 +114,45 @@ struct pamh_getenv_status pamh_get_complete_env(pam_handle_t* handle,
|
||||
|
||||
///////////////
|
||||
|
||||
struct pam_conv_data {
|
||||
char* password;
|
||||
void (*display_pam_msg)(const char* msg, int msg_style);
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
struct pam_conv_data* conv_data = (struct pam_conv_data*)appdata_ptr;
|
||||
|
||||
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);
|
||||
|
||||
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 (int 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;
|
||||
@@ -144,7 +171,9 @@ int pam_conversation(int num_msg, const struct pam_message** msg,
|
||||
}
|
||||
pam_handle_t* get_pamh(char* user, char* passwd) {
|
||||
pam_handle_t* pamh = NULL;
|
||||
struct pam_conv pamc = {pam_conversation, (void*)passwd};
|
||||
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");
|
||||
|
||||
114
src/ui.c
114
src/ui.c
@@ -5,6 +5,7 @@
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <pwd.h>
|
||||
#include <security/pam_appl.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
@@ -111,8 +112,8 @@ static char* fmt_time(const char* fmt) {
|
||||
}
|
||||
}
|
||||
|
||||
char* trunc_gethostname(const size_t MAXLEN, const char* const ELLIPSIS) {
|
||||
if (utf8len(ELLIPSIS) > MAXLEN) return NULL;
|
||||
char* trunc_gethostname(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;
|
||||
@@ -122,8 +123,8 @@ char* trunc_gethostname(const 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;
|
||||
@@ -192,8 +193,8 @@ void scratch_print_ui() {
|
||||
.y = ((window.ws_row - BOX_HEIGHT) / 2), // leave more space under
|
||||
};
|
||||
|
||||
if (window.ws_row < BOX_HEIGHT + INNER_BOX_OUT_MARGIN * 2 ||
|
||||
window.ws_col < BOX_WIDTH + INNER_BOX_OUT_MARGIN * 2) {
|
||||
if (window.ws_row < BOX_HEIGHT + (INNER_BOX_OUT_MARGIN * 2) ||
|
||||
window.ws_col < BOX_WIDTH + (INNER_BOX_OUT_MARGIN * 2)) {
|
||||
printf("\033[2J\033[H"); // Clear screen
|
||||
printf("\x1b[1;31mScreen too small\x1b[0m\n");
|
||||
printf("\x1b[%s;%sm\x1b[2J", g_config->colors.bg, g_config->colors.fg);
|
||||
@@ -298,6 +299,21 @@ int load(struct Vector* users, struct Vector* sessions) {
|
||||
restore_all();
|
||||
reboot(RB_POWER_OFF);
|
||||
exit(0);
|
||||
} else if (g_config->functions.fido != NONE &&
|
||||
ansi_key == 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_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) {
|
||||
@@ -344,25 +360,40 @@ 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() {
|
||||
// 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";
|
||||
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 ssize_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;
|
||||
}
|
||||
|
||||
clean_line(box_start, HEAD_ROW);
|
||||
|
||||
// put hostname
|
||||
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);
|
||||
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);
|
||||
|
||||
// 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)utf8len(fmtd_time),
|
||||
box_start.x + BOX_WIDTH - 1 - BOX_HMARGIN - (uint)len_tm,
|
||||
g_config->colors.e_date, fmtd_time, g_config->colors.fg);
|
||||
|
||||
free(fmtd_time);
|
||||
}
|
||||
|
||||
@@ -497,6 +528,8 @@ 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) +
|
||||
@@ -504,19 +537,31 @@ static void print_footer() {
|
||||
utf8len(g_config->strings.f_refresh) +
|
||||
utf8len(KEY_NAMES[g_config->functions.refresh]);
|
||||
|
||||
bsize += 2 * 2 + // 2 wide separators between 3 items
|
||||
3 * 1; // 3 thin separators inside every item
|
||||
bsize += (2 * 2) + (3 * 1);
|
||||
|
||||
if (fido_enabled) {
|
||||
bsize += utf8len(g_config->strings.f_fido) +
|
||||
utf8len(KEY_NAMES[g_config->functions.fido]) + 2 + 1;
|
||||
}
|
||||
|
||||
uint row = window.ws_row - 1;
|
||||
uint col = window.ws_col - 2 - bsize;
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void print_err(const char* msg) {
|
||||
@@ -524,6 +569,21 @@ 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",
|
||||
|
||||
@@ -25,11 +25,13 @@
|
||||
[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"
|
||||
|
||||
Reference in New Issue
Block a user