completed

ly_0_1_0
nullgemm 6 years ago
parent d285d558cd
commit ce745bf9f4

2
.gitignore vendored

@ -0,0 +1,2 @@
bin
obj

6
.gitmodules vendored

@ -0,0 +1,6 @@
[submodule "sub/inih"]
path = sub/inih
url = https://github.com/benhoyt/inih.git
[submodule "sub/termbox-next"]
path = sub/termbox-next
url = https://github.com/cylgom/termbox-next.git

@ -0,0 +1,13 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.

@ -0,0 +1,78 @@
### Ly - a TUI display manager
![ly screenshot](https://user-images.githubusercontent.com/5473047/42466218-8cb53d3c-83ae-11e8-8e53-bae3669f959c.png "ly on st")
Ly is a lightweight, TUI (ncurses-like) display manager for linux.
### Dependencies
Make sure all the following packages are properly installed and configured
on your linux distribution before going further:
- a c99 compiler (tested with gcc and tcc)
- a c standard library
- make
- linux-pam
- xorg
- xorg-xinit
- xorg-xauth
- mcookie
- tput
- shutdown
### Cloning and Compiling
This repository uses submodules, so you must clone it like so
```
git clone --recurse-submodules https://github.com/cylgom/ly.git
```
To compile you just need to launch make in the created folder
```
make
```
Check if it works on the tty you configured (default is tty2). You can
also run it in terminal emulators, but desktop environments won't start
```
sudo make run
```
Then, install Ly and the systemd service file
```
sudo make install
```
Now enable the systemd service to make it spawn on startup
```
sudo systemctl enable ly.service
```
If you need to switch between ttys after Ly's start you also have to
disable getty on Ly's tty to prevent "login" from spawning on top of it
```
sudo systemctl disable getty@tty2.service
```
If messages from other services pop over the login prompt,
edit open the configuration and make sure `force_update` is enabled
```
[box_main]
force_update=1
```
### Configuration
All the configuration takes place in `/etc/ly/config.ini`.
A complete reference is available on the wiki.
### Controls
Use the up and down arrow keys to change the current field, and the
left and right arrow keys to change the target desktop environment
while on the desktop field (above the login field).
### Tips
The numlock and capslock state is printed in the top-right corner.
Use the F1 and F2 keys to respectively shutdown and reboot.
### Additionnal informations
The name "Ly" is a tribute to the fairy from the game Rayman.
Ly was tested by oxodao, who is some seriously awesome dude.
I wish to thank linux-pam, X11 and systemd developers for not
providing anything close to a reference or documentation.

@ -0,0 +1,15 @@
[Unit]
Description=TUI display manager
After=systemd-user-sessions.service plymouth-quit-wait.service
After=getty@tty2.service
[Service]
Type=simple
ExecStart=/usr/bin/ly
StandardInput=tty
TTYPath=/dev/tty2
TTYReset=yes
TTYVHangup=yes
[Install]
Alias=display-manager.service

@ -0,0 +1,71 @@
NAME=ly
CC=gcc
#CC=gcc -O3
#CC=tcc
FLAGS=-std=c99 -pedantic -Wall -Werror=vla -Werror -g
BIND=bin
SRCD=src
SUBD=sub
OBJD=obj
RESD=res
LANG=$(RESD)/lang
INCL=-I$(SRCD) -I$(SUBD)/termbox-next/src -I$(SUBD)/inih
LINK=-lm -lpam -lpam_misc
SRCS=$(SRCD)/main.c
SRCS+=$(SRCD)/draw.c
SRCS+=$(SRCD)/util.c
SRCS+=$(SRCD)/config.c
SRCS+=$(SRCD)/widgets.c
SRCS+=$(SRCD)/desktop.c
SRCS+=$(SRCD)/inputs.c
SRCS+=$(SRCD)/login.c
SRCS+=$(SUBD)/inih/ini.c
OBJS:=$(patsubst $(SRCD)/%.c,$(OBJD)/$(SRCD)/%.o,$(SRCS))
OBJS+=$(SUBD)/termbox-next/bin/termbox.a
.PHONY:all
all:$(BIND)/$(NAME)
$(OBJD)/%.o:%.c
@echo "building source object $@"
@mkdir -p $(@D)
@$(CC) $(INCL) $(FLAGS) -c -o $@ $<
$(SUBD)/termbox-next/bin/termbox.a:
@echo "building static object $@"
@cd $(SUBD)/termbox-next && make
$(BIND)/$(NAME):$(OBJS)
@echo "compiling $@"
@mkdir -p $(BIND)
@$(CC) $(INCL) $(FLAGS) $(LINK) -o $(BIND)/$(NAME) $(OBJS)
@cp -r $(LANG) $(BIND)/lang
@cp $(RESD)/config.ini $(BIND)
run:$(BIND)/$(NAME)
@cd ./$(BIND) && ./$(NAME)
valgrind:$(BIND)/$(NAME)
@cd ./$(BIND) && valgrind --show-leak-kinds=all --track-origins=yes --leak-check=full --suppressions=../res/valgrind.supp 2> ../valgrind.log ./ly
install:$(BIND)/$(NAME)
install -dZ ${DESTDIR}/etc/ly
install -DZ $(BIND)/$(NAME) -t ${DESTDIR}/usr/bin
install -DZ xsetup.sh -t ${DESTDIR}/etc/ly
install -DZ $(RESD)/config.ini -t ${DESTDIR}/etc/ly
install -dZ ${DESTDIR}/etc/ly/lang
install -DZ $(RESD)/lang/* -t ${DESTDIR}/etc/ly/lang
install -DZ ly.service -t ${DESTDIR}/usr/lib/systemd/system
uninstall:
rm -rf ${DESTDIR}/etc/ly
rm -f ${DESTDIR}/usr/bin/ly
rm -f ${DESTDIR}/usr/lib/systemd/system/ly.service
clean:
@echo "cleaning workspace"
@rm -rf $(BIND)
@rm -rf $(OBJD)

@ -0,0 +1,13 @@
[box_main]
bg=0x000000
fg=0x00ff00
margin_box_main_h=2
margin_box_main_v=1
input_len=34
blank_box=1
min_refresh_delta=10
force_update=1
animate=1
save=1
load=1
hide_x=1

@ -0,0 +1,29 @@
[box_main]
login=login:
password=password:
f1=F1 shutdown
f2=F2 reboot
shell=shell
xinitrc=xinitrc
logout=logout
capslock=capslock
numlock=numlock
err_pam_buf=memory buffer error
err_pam_sys=system error
err_pam_auth=authentication error
err_pam_cred_insufficient=insufficient credentials
err_pam_authinfo_unavail=failed to get user info
err_pam_maxtries=reached maximum tries limit
err_pam_user_unknown=unknown user
err_pam_acct_expired=account expired
err_pam_authok_reqd=token expired
err_pam_perm_denied=permission denied
err_pam_cred_err=failed to set credentials
err_pam_cred_expired=credentials expired
err_pam_cred_unavail=failed to get credentials
err_pam_session=session error
err_pam_abort=pam transaction aborted
err_perm_group=failed to downgrade group permissions
err_perm_user=failed to downgrade user permissions
err_perm_dir=failed to change current directory
err_console_dev=failed to access console

@ -0,0 +1,29 @@
[box_main]
login=identifiant :
password=mot de passe :
f1=F1 éteindre
f2=F2 redémarrer
shell=shell
xinitrc=xinitrc
logout=déconnection
capslock=verr.maj
numlock=verr.num
err_pam_buf=erreur de mémoire tampon
err_pam_sys=erreur système
err_pam_auth=erreur d'authentification
err_pam_cred_insufficient=identifiants insuffisants
err_pam_authinfo_unavail=échec de l'obtention des infos utilisateur
err_pam_maxtries=limite d'essais atteinte
err_pam_user_unknown=utilisateur inconnu
err_pam_acct_expired=compte expiré
err_pam_authok_reqd=tiquet expiré
err_pam_perm_denied=permission refusée
err_pam_cred_err=échec de la modification des identifiants
err_pam_cred_expired=identifiants expirés
err_pam_cred_unavail=échec de l'obtention des identifiants
err_pam_session=erreur de session
err_pam_abort=transaction pam avortée
err_perm_group=échec du déclassement des permissions de groupe
err_perm_user=échec du déclassement des permissions utilisateur
err_perm_dir=échec de changement de répertoire
err_console_dev=échec d'accès à la console

@ -0,0 +1,31 @@
{
pam
Memcheck:Leak
...
obj:/usr/lib/libpam.so.0.84.2
...
}
{
termbox
Memcheck:Leak
...
fun:tb_init
...
}
{
libc/dynamic
Memcheck:Leak
...
fun:_dl_catch_exception
...
}
{
libc/groups
Memcheck:Leak
...
fun:initgroups
...
}

@ -0,0 +1,691 @@
#define _XOPEN_SOURCE 700
#include "config.h"
#include "cylgom.h"
#include "ini.h"
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h> // login max length
#include <security/pam_appl.h>
struct lang lang = {0};
struct config config = {0};
char* info_line = NULL;
u16 compute_box_main_width()
{
u16 login_len = strlen(lang.login);
u16 password_len = strlen(lang.password);
u16 label_len = login_len > password_len ? login_len : password_len;
return 2 * config.margin_box_main_h + config.input_len + 1 + label_len;
}
// smart-dup for config loaders
void cfg_dup(char** name, const char* value)
{
// this is not a mistake, struct was initialized with zeros
// empty fields are zero-valued pointers because of that
// we probably don't care about that, but let's pretend systems
// where NULL != 0 actually exist and are used.
if (*name != 0)
{
free(*name);
}
*name = strdup(value);
}
int config_lang_handler(void* user, const char* section, const char* name, const char* value)
{
(void)(user);
if (strcmp(section, "box_main") == 0)
{
if (strcmp(name, "login") == 0)
{
cfg_dup(&lang.login, value);
}
else if (strcmp(name, "password") == 0)
{
cfg_dup(&lang.password, value);
}
else if (strcmp(name, "f1") == 0)
{
cfg_dup(&lang.f1, value);
}
else if (strcmp(name, "f2") == 0)
{
cfg_dup(&lang.f2, value);
}
else if (strcmp(name, "shell") == 0)
{
cfg_dup(&lang.shell, value);
}
else if (strcmp(name, "xinitrc") == 0)
{
cfg_dup(&lang.xinitrc, value);
}
else if (strcmp(name, "logout") == 0)
{
cfg_dup(&lang.logout, value);
}
else if (strcmp(name, "capslock") == 0)
{
cfg_dup(&lang.capslock, value);
}
else if (strcmp(name, "numlock") == 0)
{
cfg_dup(&lang.numlock, value);
}
else if (strcmp(name, "err_pam_buf") == 0)
{
cfg_dup(&lang.err_pam_buf, value);
}
else if (strcmp(name, "err_pam_sys") == 0)
{
cfg_dup(&lang.err_pam_sys, value);
}
else if (strcmp(name, "err_pam_auth") == 0)
{
cfg_dup(&lang.err_pam_auth, value);
}
else if (strcmp(name, "err_pam_cred_insufficient") == 0)
{
cfg_dup(&lang.err_pam_cred_insufficient, value);
}
else if (strcmp(name, "err_pam_authinfo_unavail") == 0)
{
cfg_dup(&lang.err_pam_authinfo_unavail, value);
}
else if (strcmp(name, "err_pam_maxtries") == 0)
{
cfg_dup(&lang.err_pam_maxtries, value);
}
else if (strcmp(name, "err_pam_user_unknown") == 0)
{
cfg_dup(&lang.err_pam_user_unknown, value);
}
else if (strcmp(name, "err_pam_acct_expired") == 0)
{
cfg_dup(&lang.err_pam_acct_expired, value);
}
else if (strcmp(name, "err_pam_authok_reqd") == 0)
{
cfg_dup(&lang.err_pam_authok_reqd, value);
}
else if (strcmp(name, "err_pam_perm_denied") == 0)
{
cfg_dup(&lang.err_pam_perm_denied, value);
}
else if (strcmp(name, "err_pam_cred_err") == 0)
{
cfg_dup(&lang.err_pam_cred_err, value);
}
else if (strcmp(name, "err_pam_cred_expired") == 0)
{
cfg_dup(&lang.err_pam_cred_expired, value);
}
else if (strcmp(name, "err_pam_cred_unavail") == 0)
{
cfg_dup(&lang.err_pam_cred_unavail, value);
}
else if (strcmp(name, "err_pam_session") == 0)
{
cfg_dup(&lang.err_pam_session, value);
}
else if (strcmp(name, "err_pam_abort") == 0)
{
cfg_dup(&lang.err_pam_abort, value);
}
else if (strcmp(name, "err_perm_group") == 0)
{
cfg_dup(&lang.err_perm_group, value);
}
else if (strcmp(name, "err_perm_user") == 0)
{
cfg_dup(&lang.err_perm_user, value);
}
else if (strcmp(name, "err_perm_dir") == 0)
{
cfg_dup(&lang.err_perm_dir, value);
}
else if (strcmp(name, "err_console_dev") == 0)
{
cfg_dup(&lang.err_console_dev, value);
}
}
return 1;
}
void config_lang_patch()
{
if (lang.login == 0)
{
lang.login = strdup("login:");
}
if (lang.password == 0)
{
lang.password = strdup("password:");
}
if (lang.f1 == 0)
{
lang.f1 = strdup("F1 shutdown");
}
if (lang.f2 == 0)
{
lang.f2 = strdup("F2 reboot");
}
if (lang.shell == 0)
{
lang.shell = strdup("shell");
}
if (lang.xinitrc == 0)
{
lang.xinitrc = strdup("xinitrc");
}
if (lang.logout == 0)
{
lang.logout = strdup("logout");
}
if (lang.capslock == 0)
{
lang.capslock = strdup("capslock");
}
if (lang.numlock == 0)
{
lang.numlock = strdup("numlock");
}
if (lang.err_pam_buf == 0)
{
lang.err_pam_buf = strdup("memory buffer error");
}
if (lang.err_pam_sys == 0)
{
lang.err_pam_sys = strdup("system error");
}
if (lang.err_pam_auth == 0)
{
lang.err_pam_auth = strdup("authentication error");
}
if (lang.err_pam_cred_insufficient == 0)
{
lang.err_pam_cred_insufficient = strdup("insufficient credentials");
}
if (lang.err_pam_authinfo_unavail == 0)
{
lang.err_pam_authinfo_unavail = strdup("failed to get user info");
}
if (lang.err_pam_maxtries == 0)
{
lang.err_pam_maxtries = strdup("reached maximum tries limit");
}
if (lang.err_pam_user_unknown == 0)
{
lang.err_pam_user_unknown = strdup("unknown user");
}
if (lang.err_pam_acct_expired == 0)
{
lang.err_pam_acct_expired = strdup("account expired");
}
if (lang.err_pam_authok_reqd == 0)
{
lang.err_pam_authok_reqd = strdup("token expired");
}
if (lang.err_pam_perm_denied == 0)
{
lang.err_pam_perm_denied = strdup("permission denied");
}
if (lang.err_pam_cred_err == 0)
{
lang.err_pam_cred_err = strdup("failed to set credentials");
}
if (lang.err_pam_cred_expired == 0)
{
lang.err_pam_cred_expired = strdup("credentials expired");
}
if (lang.err_pam_cred_unavail == 0)
{
lang.err_pam_cred_unavail = strdup("failed to get credentials");
}
if (lang.err_pam_session == 0)
{
lang.err_pam_session = strdup("session error");
}
if (lang.err_pam_abort == 0)
{
lang.err_pam_abort = strdup("pam transaction aborted");
}
if (lang.err_perm_group == 0)
{
lang.err_perm_group = strdup("failed to downgrade group permissions");
}
if (lang.err_perm_user == 0)
{
lang.err_perm_user = strdup("failed to downgrade user permissions");
}
if (lang.err_perm_dir == 0)
{
lang.err_perm_dir = strdup("failed to change current directory");
}
if (lang.err_console_dev == 0)
{
lang.err_console_dev = strdup("failed to access console");
}
}
void config_lang_free()
{
free(lang.login);
free(lang.password);
free(lang.f1);
free(lang.f2);
free(lang.shell);
free(lang.xinitrc);
free(lang.logout);
free(lang.capslock);
free(lang.numlock);
free(lang.err_pam_buf);
free(lang.err_pam_sys);
free(lang.err_pam_auth);
free(lang.err_pam_cred_insufficient);
free(lang.err_pam_authinfo_unavail);
free(lang.err_pam_maxtries);
free(lang.err_pam_user_unknown);
free(lang.err_pam_acct_expired);
free(lang.err_pam_authok_reqd);
free(lang.err_pam_perm_denied);
free(lang.err_pam_cred_err);
free(lang.err_pam_cred_expired);
free(lang.err_pam_cred_unavail);
free(lang.err_pam_session);
free(lang.err_pam_abort);
free(lang.err_perm_group);
free(lang.err_perm_user);
free(lang.err_perm_dir);
free(lang.err_console_dev);
}
int config_config_handler(void* user, const char* section, const char* name, const char* value)
{
(void)(user);
if (strcmp(section, "box_main") == 0)
{
if (strcmp(name, "margin_box_main_h") == 0)
{
config.margin_box_main_h = abs(atoi(value));
}
else if (strcmp(name, "margin_box_main_v") == 0)
{
config.margin_box_main_v = abs(atoi(value));
}
else if (strcmp(name, "input_len") == 0)
{
config.input_len = abs(atoi(value));
}
else if (strcmp(name, "bg") == 0)
{
config.bg = strtoul(value, NULL, 16);
}
else if (strcmp(name, "fg") == 0)
{
config.fg = strtoul(value, NULL, 16);
}
else if (strcmp(name, "max_desktop_len") == 0)
{
config.max_desktop_len = abs(atoi(value));
}
else if (strcmp(name, "max_login_len") == 0)
{
config.max_login_len = abs(atoi(value));
}
else if (strcmp(name, "max_password_len") == 0)
{
config.max_password_len = abs(atoi(value));
}
else if (strcmp(name, "min_refresh_delta") == 0)
{
config.min_refresh_delta = abs(atoi(value));
}
else if (strcmp(name, "blank_box") == 0)
{
config.blank_box = (atoi(value) > 0) ? true : false;
}
else if (strcmp(name, "force_update") == 0)
{
config.force_update = (atoi(value) > 0) ? true : false;
}
else if (strcmp(name, "animate") == 0)
{
config.animate = abs(atoi(value));
}
else if (strcmp(name, "xsessions") == 0)
{
cfg_dup(&config.xsessions, value);
}
else if (strcmp(name, "service_name") == 0)
{
cfg_dup(&config.service_name, value);
}
else if (strcmp(name, "tty_id") == 0)
{
config.tty_id = abs(atoi(value));
}
else if (strcmp(name, "x_cmd") == 0)
{
cfg_dup(&config.x_cmd, value);
}
else if (strcmp(name, "x_cmd_setup") == 0)
{
cfg_dup(&config.x_cmd_setup, value);
}
else if (strcmp(name, "mcookie_cmd") == 0)
{
cfg_dup(&config.mcookie_cmd, value);
}
else if (strcmp(name, "xauthority") == 0)
{
cfg_dup(&config.xauthority, value);
}
else if (strcmp(name, "path") == 0)
{
cfg_dup(&config.path, value);
}
else if (strcmp(name, "shutdown_cmd") == 0)
{
cfg_dup(&config.shutdown_cmd, value);
}
else if (strcmp(name, "console_dev") == 0)
{
cfg_dup(&config.console_dev, value);
}
else if (strcmp(name, "tty") == 0)
{
config.tty = abs(atoi(value));
}
else if (strcmp(name, "save") == 0)
{
config.save = (atoi(value) > 0) ? true : false;
}
else if (strcmp(name, "load") == 0)
{
config.load = (atoi(value) > 0) ? true : false;
}
else if (strcmp(name, "save_file") == 0)
{
cfg_dup(&config.save_file, value);
}
else if (strcmp(name, "custom_res") == 0)
{
config.custom_res = (atoi(value) > 0) ? true : false;
}
else if (strcmp(name, "res_width") == 0)
{
config.res_width = abs(atoi(value));
}
else if (strcmp(name, "res_height") == 0)
{
config.res_height = abs(atoi(value));
}
else if (strcmp(name, "hide_x") == 0)
{
config.hide_x = (atoi(value) > 0) ? true : false;
}
else if (strcmp(name, "hide_x_save_log") == 0)
{
cfg_dup(&config.hide_x_save_log, value);
}
else if (strcmp(name, "lang") == 0)
{
cfg_dup(&config.lang, value);
}
}
return 1;
}
void config_config_patch()
{
if (config.margin_box_main_h == 0)
{
config.margin_box_main_h = 2;
}
if (config.margin_box_main_v == 0)
{
config.margin_box_main_v = 1;
}
if (config.input_len == 0)
{
config.input_len = 34;
}
if (config.bg == 0)
{
config.bg = 0x000000;
}
if (config.fg == 0)
{
config.fg = 0xffffff;
}
if (config.max_desktop_len == 0)
{
// arbitrary one
config.max_desktop_len = 100;
}
if (config.max_login_len == 0)
{
// for "useradd" the max is 32
config.max_login_len = 32;
#ifdef LOGIN_NAME_MAX
if (config.max_login_len < LOGIN_NAME_MAX)
{
// the posix standard specifies it includes the terminating NULL
// http://pubs.opengroup.org/onlinepubs/007908799/xsh/limits.h.html
config.max_login_len = LOGIN_NAME_MAX - 1;
}
#endif
#ifdef _POSIX_LOGIN_NAME_MAX
if (config.max_login_len < _POSIX_LOGIN_NAME_MAX)
{
config.max_login_len = _POSIX_LOGIN_NAME_MAX - 1;
}
#endif
}
if (config.max_password_len == 0)
{
// for "passwd" the max is 200
// https://github.com/shadow-maint/shadow/blob/master/src/passwd.c#L217
// for "sudo" it is 255
// https://www.sudo.ws/repos/sudo/file/tip/include/sudo_plugin.h
// https://www.sudo.ws/repos/sudo/file/tip/src/sudo.c
// "su" and "login" user linux-pam and do not seem to have a limit
config.max_password_len = 255;
}
if (config.min_refresh_delta == 0)
{
config.min_refresh_delta = 1000;
}
// commenting theses because the defaults are 0
//#if 0
if (config.blank_box == 0)
{
config.blank_box = false;
}
if (config.force_update == 0)
{
config.force_update = false;
}
if (config.animate == 0)
{
config.animate = 0;
}
//#endif
if (config.xsessions == 0)
{
config.xsessions = strdup("/usr/share/xsessions");
}
if (config.service_name == 0)
{
config.service_name = strdup("login");
}
if (config.tty_id == 0)
{
config.tty_id = 2;
}
if (config.x_cmd == 0)
{
config.x_cmd = strdup("/usr/bin/X");
}
if (config.x_cmd_setup == 0)
{
config.x_cmd_setup = strdup("/etc/ly/xsetup.sh");
}
if (config.mcookie_cmd == 0)
{
config.mcookie_cmd = strdup("/usr/bin/mcookie");
}
if (config.xauthority == 0)
{
config.xauthority = strdup(".lyxauth");
}
if (config.path == 0)
{
config.path = strdup("/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/env");
}
if (config.shutdown_cmd == 0)
{
config.shutdown_cmd = strdup("/sbin/shutdown");
}
if (config.console_dev == 0)
{
config.console_dev = strdup("/dev/console");
}
if (config.tty == 0)
{
config.tty = 2;
}
if (config.save_file == 0)
{
config.save_file = strdup("/etc/ly/ly.save");
}
if ((config.res_width == 0) || (config.res_height == 0))
{
config.custom_res = false;
}
if (config.hide_x_save_log == 0)
{
config.hide_x_save_log = strdup("/dev/null");
}
if (config.lang == 0)
{
config.lang = strdup("/etc/ly/lang/en.ini");
}
// fill secret parameters
config.box_main_w = compute_box_main_width();
config.box_main_h = 9;
}
void config_config_free()
{
free(config.xsessions);
free(config.service_name);
free(config.x_cmd);
free(config.x_cmd_setup);
free(config.mcookie_cmd);
free(config.xauthority);
free(config.path);
free(config.shutdown_cmd);
free(config.console_dev);
free(config.save_file);
free(config.hide_x_save_log);
free(config.lang);
}
// loads the ini configs
void config_load(const char* file_config)
{
// we don't care about this function's success
ini_parse(file_config, config_config_handler, NULL);
ini_parse(config.lang, config_lang_handler, NULL);
// because we check for missing strings anyway
config_lang_patch(); // config patch depends on lang
config_config_patch(); // so we call them in this order
}
void set_error(enum err error)
{
switch (error)
{
case ERR_PERM_GROUP:
info_line = lang.err_perm_group;
break;
case ERR_PERM_USER:
info_line = lang.err_perm_user;
break;
case ERR_PERM_DIR:
info_line = lang.err_perm_dir;
break;
default:
info_line = lang.err_pam_abort;
break;
}
}
void pam_diagnose(int error)
{
switch (error)
{
case PAM_BUF_ERR:
info_line = lang.err_pam_buf;
break;
case PAM_SYSTEM_ERR:
info_line = lang.err_pam_sys;
break;
case PAM_AUTH_ERR:
info_line = lang.err_pam_auth;
break;
case PAM_CRED_INSUFFICIENT:
info_line = lang.err_pam_cred_insufficient;
break;
case PAM_AUTHINFO_UNAVAIL:
info_line = lang.err_pam_authinfo_unavail;
break;
case PAM_MAXTRIES:
info_line = lang.err_pam_maxtries;
break;
case PAM_USER_UNKNOWN:
info_line = lang.err_pam_user_unknown;
break;
case PAM_ACCT_EXPIRED:
info_line = lang.err_pam_acct_expired;
break;
case PAM_NEW_AUTHTOK_REQD:
info_line = lang.err_pam_authok_reqd;
break;
case PAM_PERM_DENIED:
info_line = lang.err_pam_perm_denied;
break;
case PAM_CRED_ERR:
info_line = lang.err_pam_cred_err;
break;
case PAM_CRED_EXPIRED:
info_line = lang.err_pam_cred_expired;
break;
case PAM_CRED_UNAVAIL:
info_line = lang.err_pam_cred_unavail;
break;
case PAM_SESSION_ERR:
info_line = lang.err_pam_session;
break;
case PAM_ABORT:
default:
info_line = lang.err_pam_abort;
break;
}
}

@ -0,0 +1,96 @@
#ifndef H_CONFIG
#define H_CONFIG
#include "cylgom.h"
extern char* info_line;
#define LY_SYSTEMD
enum err {OK, ERR, SECURE_RAM, XSESSIONS_MISSING, XSESSIONS_READ, ERR_PERM_GROUP, ERR_PERM_USER, ERR_PERM_DIR};
enum display_server {DS_WAYLAND, DS_SHELL, DS_XINITRC, DS_XORG};
struct lang
{
char* login;
char* password;
char* f1;
char* f2;
char* shell;
char* xinitrc;
char* logout;
char* capslock;
char* numlock;
// errors
char* err_pam_buf;
char* err_pam_sys;
char* err_pam_auth;
char* err_pam_cred_insufficient;
char* err_pam_authinfo_unavail;
char* err_pam_maxtries;
char* err_pam_user_unknown;
char* err_pam_acct_expired;
char* err_pam_authok_reqd;
char* err_pam_perm_denied;
char* err_pam_cred_err;
char* err_pam_cred_expired;
char* err_pam_cred_unavail;
char* err_pam_session;
char* err_pam_abort;
char* err_perm_group;
char* err_perm_user;
char* err_perm_dir;
char* err_console_dev;
};
struct config
{
u32 bg;
u32 fg;
u16 box_main_w;
u16 box_main_h;
u16 margin_box_main_h;
u16 margin_box_main_v;
u16 input_len;
u16 max_desktop_len;
u16 max_login_len;
u16 max_password_len;
u16 min_refresh_delta;
u16 old_min_refresh_delta;
bool blank_box;
bool force_update;
bool old_force_update;
u16 animate;
char* xsessions;
char* service_name;
u16 tty_id;
char* x_cmd;
char* x_cmd_setup;
char* mcookie_cmd;
char* xauthority;
char* path;
char* shutdown_cmd;
char* console_dev;
u8 tty;
bool save;
bool load;
char* save_file;
bool custom_res;
u16 res_width;
u16 res_height;
bool hide_x;
char* hide_x_save_log;
u8 auth_fails;
char* lang;
};
extern struct lang lang;
extern struct config config;
void config_load(const char* file_config);
void config_config_free();
void config_lang_free();
void set_error(enum err error);
void pam_diagnose(int error);
#endif

@ -0,0 +1,73 @@
#ifndef H_CYLGOM
#define H_CYLGOM
#include <stdbool.h>
#include <stdint.h>
// typedefs for convenience and optimizations
// 0 to save ram and optimize for embedded systems
// 1 to gain extra speed by replacing all floats by doubles
// 2 to gain extra speed by using bigger integers depending on arch
// level 2 includes *heavy* optimizations that will definitely eat your ram
#define SPEED 0
///////////////////
// regular stuff //
///////////////////
// 100% standard
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef int8_t i8;
typedef int16_t i16;
typedef int32_t i32;
typedef int64_t i64;
// float and double are not fixed-size by the C standard
// however, the C standard strongly suggests using IEEE 754
// in IEEE 754, float is 32 bits and double 64 bits
// howevevr, long double is whatever size the compiler prefers
// this is why we redefine float and double but not long double
typedef float f32;
typedef double f64;
///////////////////////////////
// black magic optimizations //
///////////////////////////////
// the best optimization out there
// doubles are usually slower than floats for various reasons
// on embedded systems though, it is usually the opposite
#if SPEED > 0
typedef f64 f32;
#endif
// the following block tries to optimize speed at the cost of ram
// we are testing the architecturee in the most portable way possible
// the following macro is not mandatory, obscure systems might not provide it
// on 16 bits systems, 16-bit integer operations can be the fastest
// on 32 bits systems, 32-bit integer operations can be the fastest
// on 64 bits systems, 64-bit integer operations can be the fastest
#if SPEED > 1
#if UINTPTR_MAX == 0xffff
typedef uint16_t u8;
typedef int16_t i8;
#elif UINTPTR_MAX == 0xffffffff
typedef uint32_t u8;
typedef int32_t i8;
typedef uint32_t u16;
typedef int32_t i16;
#elif UINTPTR_MAX == 0xffffffffffffffff
typedef uint64_t u8;
typedef int64_t i8;
typedef uint64_t u16;
typedef int64_t i16;
typedef uint64_t u32;
typedef int64_t i32;
#endif
#endif
#endif

@ -0,0 +1,91 @@
#define _XOPEN_SOURCE 700
#include "desktop.h"
#include "cylgom.h"
#include "ini.h"
#include "widgets.h"
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stddef.h>
#include <dirent.h>
#include <limits.h>
char* value_name = NULL;
char* value_exec = NULL;
int desktop_handler(void* user, const char* section, const char* name, const char* value)
{
(void)(user);
if (strcmp(section, "Desktop Entry") == 0)
{
if ((strcmp(name, "Name") == 0) && (value_name == NULL))
{
value_name = strdup(value);
}
if ((strcmp(name, "Exec") == 0) && (value_exec == NULL))
{
value_exec = strdup(value);
}
}
return 1;
}
enum err desktop_load(struct desktop* target)
{
DIR* dir;
struct dirent* dir_info;
#if defined(NAME_MAX)
char path[NAME_MAX];
#elif defined(_POSIX_PATH_MAX)
char path[_POSIX_PATH_MAX];
#else
char path[1024];
#endif
// checks dir existence
if (access(config.xsessions, F_OK) == -1)
{
return XSESSIONS_MISSING;
}
// requests read access
dir = opendir(config.xsessions);
if (dir == NULL)
{
return XSESSIONS_READ;
}
// reads the content
dir_info = readdir(dir);
while (dir_info != NULL)
{
// skips the files starting with "."
if ((dir_info->d_name)[0] == '.')
{
dir_info = readdir(dir);
continue;
}
snprintf(path, (sizeof (path)) - 1, "%s/", config.xsessions);
strncat(path, dir_info->d_name, (sizeof (path)) - 1);
ini_parse(path, desktop_handler, NULL);
if ((value_name != NULL) && (value_exec != NULL))
{
widget_desktop_add(target, value_name, value_exec, DS_XORG);
value_name = NULL;
value_exec = NULL;
}
dir_info = readdir(dir);
}
closedir(dir);
return OK;
}

@ -0,0 +1,8 @@
#ifndef H_DESKTOP
#include "config.h"
#include "widgets.h"
enum err desktop_load(struct desktop* target);
#endif

@ -0,0 +1,460 @@
#define _XOPEN_SOURCE 700
#include "draw.h"
#include "cylgom.h"
#include "termbox.h"
#include "util.h"
#include "config.h"
#include "widgets.h"
#include <math.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <linux/kd.h>
#include <linux/vt.h>
#include <stdio.h>
#include <unistd.h>
// border chars: ┌ └ ┐ ┘ ─ ─ │ │
struct box box_main = {0x250c, 0x2514, 0x2510, 0x2518, 0x2500, 0x2500 ,0x2502, 0x2502};
// alternative border chars:
// struct box box_main = {'+', '+', '+', '+', '-', '-', '|', '|'};
u16 width = 0;
u16 height = 0;
u16 box_main_x = 0;
u16 box_main_y = 0;
u16 labels_max_len = 0;
void draw_init()
{
if (config.custom_res)
{
width = config.res_width;
height = config.res_height;
}
else
{
width = tb_width();
height = tb_height();
}
if (info_line == NULL)
{
hostname(&info_line);
}
}
// box outline
void draw_box()
{
box_main_x = ((width - config.box_main_w) / 2);
box_main_y = ((height - config.box_main_h) / 2);
u16 box_main_x2 = ((width + config.box_main_w) / 2);
u16 box_main_y2 = ((height + config.box_main_h) / 2);
// corners
tb_change_cell(box_main_x - 1,
box_main_y - 1,
box_main.left_up,
config.fg,
config.bg);
tb_change_cell(box_main_x2 + 1,
box_main_y - 1,
box_main.right_up,
config.fg,
config.bg);
tb_change_cell(box_main_x - 1,
box_main_y2 + 1,
box_main.left_down,
config.fg,
config.bg);
tb_change_cell(box_main_x2 + 1,
box_main_y2 + 1,
box_main.right_down,
config.fg,
config.bg);
// top and bottom
struct tb_cell c1 = {box_main.top, config.fg, config.bg};
struct tb_cell c2 = {box_main.bot, config.fg, config.bg};
for(u8 i = 0; i <= config.box_main_w; ++i)
{
tb_put_cell(box_main_x + i,
box_main_y - 1,
&c1);
tb_put_cell(box_main_x + i,
box_main_y2 + 1,
&c2);
}
// left and right
c1.ch = box_main.left;
c2.ch = box_main.right;
// blank
struct tb_cell blank = {' ', config.fg, config.bg};
for(u8 i = 0; i <= config.box_main_h; ++i)
{
// testing in the height loop takes less cycles
// (I know this is placebo optimization :D)
if (config.blank_box)
{
for (u8 k = 0; k <= config.box_main_w; ++k)
{
tb_put_cell(box_main_x + k,
box_main_y + i,
&blank);
}
}
tb_put_cell(box_main_x - 1,
box_main_y + i,
&c1);
tb_put_cell(box_main_x2 + 1,
box_main_y + i,
&c2);
}
}
struct tb_cell* strn_cell(char* s, u16 len)
{
struct tb_cell* cells = malloc((sizeof (struct tb_cell)) * len);
char* s2 = s;
u32 c;
if (cells != NULL)
{
for (u16 i = 0; i < len; ++i)
{
if ((s2 - s) >= len)
{
break;
}
s2 += utf8_char_to_unicode(&c, s2);
cells[i].ch = c;
cells[i].bg = config.bg;
cells[i].fg = config.fg;
}
}
return cells;
}
struct tb_cell* str_cell(char* s)
{
return strn_cell(s, strlen(s));
}
// input labels
void draw_labels()
{
struct tb_cell* login = str_cell(lang.login);
tb_blit(box_main_x + config.margin_box_main_h,
box_main_y + config.margin_box_main_v + 5,
strlen(lang.login),
1,
login);
free(login);
struct tb_cell* password = str_cell(lang.password);
tb_blit(box_main_x + config.margin_box_main_h,
box_main_y + config.margin_box_main_v + 7,
strlen(lang.password),
1,
password);
free(password);
labels_max_len = strlen(lang.login);
if (labels_max_len < strlen(lang.password))
{
labels_max_len = strlen(lang.password);
}
if (info_line != NULL)
{
u16 hostname_len = strlen(info_line);
struct tb_cell* info_cell = str_cell(info_line);
tb_blit(box_main_x + ((config.box_main_w - hostname_len) / 2),
box_main_y + config.margin_box_main_v,
hostname_len,
1,
info_cell);
free(info_cell);
}
}
// F1 and F2 labels
void draw_f_commands()
{
struct tb_cell* f1 = str_cell(lang.f1);
tb_blit(0, 0, strlen(lang.f1), 1, f1);
free(f1);
struct tb_cell* f2 = str_cell(lang.f2);
tb_blit(strlen(lang.f1) + 1, 0, strlen(lang.f2), 1, f2);
free(f2);
}
// numlock and capslock info
void draw_lock_state()
{
FILE* console = fopen(config.console_dev, "w");
if (console == NULL)
{
info_line = lang.err_console_dev;
return;
}
int fd = fileno(console);
char ret;
ioctl(fd, KDGKBLED, &ret);
fclose(console);
u16 pos_x = width - strlen(lang.numlock);
if (((ret >> 1) & 0x01) == 1)
{
struct tb_cell* numlock = str_cell(lang.numlock);
tb_blit(pos_x, 0, strlen(lang.numlock), 1, numlock);
free(numlock);
}
pos_x -= strlen(lang.capslock) + 1;
if (((ret >> 2) & 0x01) == 1)
{
struct tb_cell* capslock = str_cell(lang.capslock);
tb_blit(pos_x, 0, strlen(lang.capslock), 1, capslock);
free(capslock);
}
}
// main box
void draw_desktop(struct desktop* target)
{
u16 len = strlen(target->list[target->cur]);
if (len > (target->visible_len - 3))
{
len = target->visible_len - 3;
}
tb_change_cell(target->x,
target->y,
'<',
config.fg,
config.bg);
tb_change_cell(target->x + target->visible_len - 1,
target->y,
'>',
config.fg,
config.bg);
for (u16 i = 0; i < len; ++i)
{
tb_change_cell(target->x + i + 2,
target->y,
target->list[target->cur][i],
config.fg,
config.bg);
}
}
// classic input
void draw_input(struct input* input)
{
u16 len = strlen(input->text);
u16 visible_len = input->visible_len;
struct tb_cell* cells;
if (len > visible_len)
{
len = visible_len;
}
cells = strn_cell(input->visible_start, len);
if (cells != NULL)
{
tb_blit(input->x, input->y, len, 1, cells);
free(cells);
for (u16 i = input->end - input->visible_start; i < visible_len; ++i)
{
tb_change_cell(input->x + i,
input->y,
' ',
config.fg,
config.bg);
}
}
}
// password input (hidden text)
void draw_input_mask(struct input* input)
{
u16 len = strlen(input->text);
u16 visible_len = input->visible_len;
if (len > visible_len)
{
len = visible_len;
}
for (u16 i = 0; i < visible_len; ++i)
{
if (input->visible_start + i < input->end)
{
tb_change_cell(input->x + i,
input->y,
'o',
config.fg,
config.bg);
}
else
{
tb_change_cell(input->x + i,
input->y,
' ',
config.fg,
config.bg);
}
}
}
// configures the inputs accroding to the size of the screen
void position_input(struct desktop* desktop, struct input* login, struct input* password)
{
i32 len;
u16 x;
x = box_main_x + config.margin_box_main_h + labels_max_len + 1;
len = box_main_x + config.box_main_w - config.margin_box_main_h - x;
if (len < 0)
{
return;
}
desktop->x = x;
desktop->y = box_main_y + config.margin_box_main_v + 3;
desktop->visible_len = len;
login->x = x;
login->y = box_main_y + config.margin_box_main_v + 5;
login->visible_len = len;
password->x = x;
password->y = box_main_y + config.margin_box_main_v + 7;
password->visible_len = len;
}
// background animations
// example implementation
void spiral()
{
static struct timeval time;
static uint64_t time_present = 0;
static uint64_t time_past = 0;
const struct tb_cell c1 = {'o', config.fg, config.bg};
static f64 ini = 0;
gettimeofday(&time, NULL);
time_present = time.tv_usec + ((uint64_t) 1000000) * time.tv_sec;
ini += 2 * M_PI * ((time_present - time_past) / 2000000.0);
if (ini > (5 * 2 * M_PI))
{
ini = 0;
}
for (f64 t = 0; t < (5 * 2 * M_PI); t += 0.01)
{
f64 y = sin(t + ini) * (height / 2) * 0.3 * (t / (2 * M_PI)) * 1.2;
f64 x = cos(t + ini) * (width / 2) * 0.2 * (t / (2 * M_PI)) * 1.2;
tb_put_cell((width / 2) + x, (height / 2) + y, &c1);
}
time_past = time_present;
}
void animate()
{
switch(config.animate)
{
case 1:
spiral();
break;
case 0:
default:
break;
}
}
// very important ;)
void cascade(u8* fails)
{
u16 width = tb_width();
u16 height = tb_height();
struct tb_cell* buf = tb_cell_buffer();
char c;
char c_under;
bool changes = false;
for (int i = height - 2; i >= 0; --i)
{
for (int k = 0; k < width; ++k)
{
c = buf[i * width + k].ch;
if (isspace(c))
{
continue;
}
c_under = buf[(i + 1) * width + k].ch;
if (!isspace(c_under))
{
continue;
}
if (!changes)
{
changes = true;
}
// omg this is not cryptographically secure
if (((rand() % 10)) > 7)
{
continue;
}
buf[(i + 1) * width + k] = buf[i * width + k];
buf[i * width + k].ch = ' ';
}
}
if (!changes)
{
sleep(7);
config.auth_fails = 0;
config.min_refresh_delta = config.old_min_refresh_delta;
config.force_update = config.old_force_update;
}
}

@ -0,0 +1,31 @@
#ifndef H_DRAW
#define H_DRAW
#include "widgets.h"
#include "cylgom.h"
struct box
{
u32 left_up;
u32 left_down;
u32 right_up;
u32 right_down;
u32 top;
u32 bot;
u32 left;
u32 right;
};
void draw_init();
void draw_box();
void draw_labels();
void draw_f_commands();
void draw_lock_state();
void draw_desktop(struct desktop* target);
void draw_input(struct input* input);
void draw_input_mask(struct input* input);
void position_input(struct desktop* desktop, struct input* login, struct input* password);
void animate();
void cascade();
#endif

@ -0,0 +1,66 @@
#include "inputs.h"
#include "termbox.h"
#include "widgets.h"
#include "cylgom.h"
#include <stdlib.h>
void handle_desktop(void* input_struct, struct tb_event* event)
{
struct desktop* target = (struct desktop*) input_struct;
if ((event != NULL) && (event->type == TB_EVENT_KEY))
{
if (event->key == TB_KEY_ARROW_LEFT)
{
widget_desktop_move_cur(target, LEFT);
}
else if (event->key == TB_KEY_ARROW_RIGHT)
{
widget_desktop_move_cur(target, RIGHT);
}
}
tb_set_cursor(target->x + 2, target->y);
}
void handle_text(void* input_struct, struct tb_event* event)
{
struct input* target = (struct input*) input_struct;
if ((event != NULL) && (event->type == TB_EVENT_KEY))
{
if (event->key == TB_KEY_ARROW_LEFT)
{
widget_input_move_cur(target, LEFT);
}
else if (event->key == TB_KEY_ARROW_RIGHT)
{
widget_input_move_cur(target, RIGHT);
}
else if (event->key == TB_KEY_DELETE)
{
widget_input_delete(target);
}
else if ((event->key == TB_KEY_BACKSPACE) || (event->key == TB_KEY_BACKSPACE2))
{
widget_input_backspace(target);
}
else if (((event->ch > 31) && (event->ch < 127)) || (event->key == TB_KEY_SPACE))
{
char buf[7] = {0};
if (event->key == TB_KEY_SPACE)
{
buf[0] = ' ';
}
else
{
utf8_unicode_to_char(buf, event->ch);
}
widget_input_write(target, buf[0]);
}
}
tb_set_cursor(target->x + (target->cur - target->visible_start), target->y);
}

@ -0,0 +1,9 @@
#ifndef H_INPUTS
#define H_INPUTS
#include "termbox.h"
void handle_text(void* input_struct, struct tb_event* event);
void handle_desktop(void* input_struct, struct tb_event* event);
#endif

@ -0,0 +1,456 @@
#define _XOPEN_SOURCE 700
#define _DEFAULT_SOURCE
#include "config.h"
#include "login.h"
#include "widgets.h"
#include "desktop.h"
#include "termbox.h"
#include <sys/types.h>
#include <grp.h>
#include <pwd.h>
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <security/pam_appl.h>
#include <security/pam_misc.h>
#ifdef LY_SYSTEMD
#define PAM_SYSTEMD
#endif
#include <security/pam_modules.h>
#include <security/pam_modutil.h>
int login_conv(int num_msg, const struct pam_message** msg,
struct pam_response** resp, void* appdata_ptr)
{
int i;
int result = PAM_SUCCESS;
if(!(*resp = calloc(num_msg, sizeof(struct pam_response))))
{
return PAM_BUF_ERR;
}
for(i = 0; i < num_msg; i++)
{
char* username, *password;
switch(msg[i]->msg_style)
{
case PAM_PROMPT_ECHO_ON:
username = ((char**) appdata_ptr)[0];
(*resp)[i].resp = strdup(username);
break;
case PAM_PROMPT_ECHO_OFF:
password = ((char**) appdata_ptr)[1];
(*resp)[i].resp = strdup(password);
break;
case PAM_ERROR_MSG:
fprintf(stderr, "%s\n", msg[i]->msg);
result = PAM_CONV_ERR;
break;
case PAM_TEXT_INFO:
printf("%s\n", msg[i]->msg);
break;
}
if(result != PAM_SUCCESS)
{
break;
}
}
if(result != PAM_SUCCESS)
{
free(*resp);
*resp = 0;
}
return result;
}
int get_free_display()
{
int i;
char xlock[256];
for(i = 0; i < 200; ++i)
{
snprintf(xlock, sizeof(xlock), "/tmp/.X%d-lock", i);
if(access(xlock, F_OK) == -1)
{
break;
}
}
return i;
}
enum err init_env(pam_handle_t* handle, struct passwd* pw)
{
u16 i;
u16 len;
char tmp[256];
char** env;
char* termenv;
termenv = getenv("TERM");
setenv("HOME", pw->pw_dir, 0);
setenv("USER", pw->pw_name, 1);
setenv("SHELL", pw->pw_shell, 1);
setenv("LOGNAME", pw->pw_name, 1);
if (termenv)
{
setenv("TERM", termenv, 1);
}
else
{
setenv("TERM", "linux", 1);
}
snprintf(tmp, sizeof(tmp), "%s/%s", pw->pw_dir, config.xauthority);
setenv("XAUTHORITY", tmp, 0);
len = snprintf(tmp, sizeof(tmp), "%s/%s", _PATH_MAILDIR, pw->pw_name);
if ((len > 0) && ((size_t) len < sizeof(tmp)))
{
setenv("MAIL", tmp, 0);
}
if (setenv("PATH", config.path, 1))
{
return ERR;
}
env = pam_getenvlist(handle);
for (i = 0; env && env[i]; i++)
{
putenv(env[i]);
}
return OK;
}
void init_xdg(const char* tty_id, const char* display_name,
enum display_server display_server)
{
setenv("XDG_SESSION_CLASS", "user", 0);
setenv("XDG_SEAT", "seat0", 0);
setenv("XDG_VTNR", tty_id, 0);
setenv("DISPLAY", display_name, 1);
switch(display_server)
{
case DS_SHELL:
setenv("XDG_SESSION_TYPE", "tty", 0);
break;
case DS_WAYLAND:
setenv("XDG_SESSION_TYPE", "wayland", 0);
break;
case DS_XINITRC:
case DS_XORG:
setenv("XDG_SESSION_TYPE", "x11", 0);
break;
}
}
void reset_terminal(struct passwd* pwd)
{
pid_t pid;
int status;
char cmd[256];
pid = fork();
strncpy(cmd, "exec tput reset", sizeof(cmd));
if (pid == 0)
{
execl(pwd->pw_shell, pwd->pw_shell, "-c", cmd, NULL);
exit(EXIT_SUCCESS);
}
waitpid(pid, &status, 0);
}
void launch_wayland(struct passwd* pwd, pam_handle_t* pam_handle,
const char* de_command)
{
char cmd[32];
snprintf(cmd, 32, "exec %s", de_command);
reset_terminal(pwd);
execl(pwd->pw_shell, pwd->pw_shell, "-l", "-c", cmd, NULL);
exit(EXIT_SUCCESS);
}
void launch_shell(struct passwd* pwd, pam_handle_t* pam_handle)
{
char* pos;
char args[256 + 2]; // arbitrary
args[0] = '-';
strncpy(args + 1, ((pos = strrchr(pwd->pw_shell,
'/')) ? pos + 1 : pwd->pw_shell), sizeof(args) - 1);
reset_terminal(pwd);
execl(pwd->pw_shell, args, NULL);
exit(EXIT_SUCCESS);
}
void launch_xorg(struct passwd* pwd, pam_handle_t* pam_handle,
const char* de_command, const char* display_name, const char* vt,
int xinitrc)
{
FILE* file;
pid_t child;
int status;
char cmd[256];
// updates cookie
snprintf(cmd,
sizeof(cmd),
"exec xauth add %s . `%s`",
display_name,
config.mcookie_cmd);
file = fopen(getenv("XAUTHORITY"), "ab+");
fclose(file);
// generates the cookie
child = fork();
if(child == 0)
{
execl(pwd->pw_shell, pwd->pw_shell, "-c", cmd, NULL);
exit(EXIT_SUCCESS);
}
waitpid(child, &status, 0);
// starts x
snprintf(cmd, sizeof(cmd),
"exec xinit %s %s%s -- %s %s %s %s %s %s -auth %s",
config.x_cmd_setup,
xinitrc ? "" : "/usr/bin/",
de_command, config.x_cmd,
config.hide_x ? "-keeptty >" : "",
config.hide_x ? config.hide_x_save_log : "",
config.hide_x ? "2>&1" : "",
display_name, vt, getenv("XAUTHORITY"));
reset_terminal(pwd);
execl(pwd->pw_shell, pwd->pw_shell, "-l", "-c", cmd, NULL);
exit(EXIT_SUCCESS);
}
enum err login_desktop(struct desktop* desktop,
struct input* login,
struct input* password)
{
int display_id;
char display_name[3];
pid_t display_pid;
int display_status;
const char* creds[2] = {login->text, password->text};
struct pam_conv conv = {login_conv, creds};
struct passwd* pwd = NULL;
pam_handle_t* handle;
int pam_result;
enum display_server display_server = desktop->display_server[desktop->cur];
char tty_id [3];
char vt[5];
display_id = get_free_display();
snprintf(display_name, sizeof(display_name), ":%d", display_id);
snprintf(tty_id, sizeof(tty_id), "%d", config.tty_id);
snprintf(vt, sizeof(vt), "vt%d", config.tty_id);
// starting pam transations
pam_result = pam_start(config.service_name, login->text, &conv, &handle);
if (pam_result != PAM_SUCCESS)
{
pam_diagnose(pam_result);
pam_end(handle, pam_result);
return ERR;
}
pam_result = pam_authenticate(handle, 0);
if (pam_result != PAM_SUCCESS)
{
pam_diagnose(pam_result);
pam_end(handle, pam_result);
return ERR;
}
pam_result = pam_acct_mgmt(handle, 0);
if (pam_result != PAM_SUCCESS)
{
pam_diagnose(pam_result);
pam_end(handle, pam_result);
return ERR;
}
// initializes user groups
struct passwd* pw = getpwnam(login->text);
if (!pw)
{
pam_end(handle, pam_result);
return ERR;
}
int grp_result = initgroups(login->text, pw->pw_gid);
if (grp_result == -1)
{
pam_end(handle, pam_result);
return ERR;
}
// back to pam transactions
pam_result = pam_setcred(handle, PAM_ESTABLISH_CRED);
if (pam_result != PAM_SUCCESS)
{
pam_diagnose(pam_result);
pam_end(handle, pam_result);
return ERR;
}
pam_result = pam_open_session(handle, 0);
if (pam_result != PAM_SUCCESS)
{
pam_diagnose(pam_result);
pam_end(handle, pam_result);
return ERR;
}
// login error
if (handle == NULL)
{
return ERR;
}
pwd = getpwnam(login->text);
// clears the password in memory
widget_input_free(password);
widget_input(password, config.max_password_len);
// launches the DE
display_pid = fork();
if (display_pid == 0)
{
// restores regular terminal mode
// doing this here to enable post-return cleanup
tb_clear();
tb_present();
tb_shutdown();
// initialization
clearenv();
init_xdg(tty_id, display_name, display_server);
// downgrades group permissions
if ((pwd == NULL) || (setgid(pwd->pw_gid) < 0))
{
set_error(ERR_PERM_GROUP);
pam_end(handle, pam_result);
exit(EXIT_FAILURE);
}
init_env(handle, pwd);
// downgrades user permissions
if (setuid(pwd->pw_uid) < 0)
{
set_error(ERR_PERM_USER);
pam_end(handle, pam_result);
exit(EXIT_FAILURE);
}
if (chdir(pwd->pw_dir) < 0)
{
set_error(ERR_PERM_DIR);
pam_end(handle, pam_result);
exit(EXIT_FAILURE);
}
switch (display_server)
{
case DS_SHELL:
launch_shell(pwd, handle);
break;
case DS_WAYLAND:
launch_wayland(pwd, handle, desktop->cmd[desktop->cur]);
break;
case DS_XORG:
launch_xorg(pwd, handle, desktop->cmd[desktop->cur],
display_name, vt, 0);
break;
case DS_XINITRC:
launch_xorg(pwd, handle, desktop->cmd[desktop->cur],
display_name, vt, 1);
break;
}
exit(EXIT_SUCCESS);
}
// waits for the de/shell to exit
waitpid(display_pid, &display_status, 0);
tb_init();
tb_select_output_mode(TB_OUTPUT_TRUECOLOR);
// reloads the desktop environment list on logout
widget_desktop_free(desktop);
widget_desktop(desktop);
desktop_load(desktop);
info_line = lang.logout;
pam_result = pam_close_session(handle, 0);
if (pam_result != PAM_SUCCESS)
{
pam_diagnose(pam_result);
pam_end(handle, pam_result);
return ERR;
}
pam_result = pam_setcred(handle, PAM_DELETE_CRED);
if (pam_result != PAM_SUCCESS)
{
pam_diagnose(pam_result);
pam_end(handle, pam_result);
return ERR;
}
pam_result = pam_end(handle, pam_result);
if (pam_result != PAM_SUCCESS)
{
pam_diagnose(pam_result);
pam_end(handle, pam_result);
return ERR;
}
return OK;
}

@ -0,0 +1,9 @@
#ifndef H_LOGIN
#define H_LOGIN
#include "config.h"
#include "widgets.h"
enum err login_desktop(struct desktop* desktop, struct input* login, struct input* password);
#endif

@ -0,0 +1,179 @@
#include "cylgom.h"
#include "termbox.h"
#include "draw.h"
#include "desktop.h"
#include "config.h"
#include "inputs.h"
#include "login.h"
#include "util.h"
#include <unistd.h>
#include <stdio.h>
enum active_input {INPUT_DESKTOP, INPUT_LOGIN, INPUT_PASSWORD};
enum shutdown {SHUTDOWN_NO, SHUTDOWN_YES, SHUTDOWN_REBOOT};
int main (int argc, char **argv)
{
struct desktop desktop;
struct input login;
struct input password;
struct tb_event event;
enum shutdown shutdown = SHUTDOWN_NO;
enum active_input active_input;
enum err error = OK;
enum err status;
void* input_structs[3] =
{
(void*) &desktop,
(void*) &login,
(void*) &password
};
void (*input_handles[3]) (void*, struct tb_event*) =
{
handle_desktop,
handle_text,
handle_text
};
active_input = INPUT_PASSWORD;
config_load("/etc/ly/config.ini");
widget_desktop(&desktop);
widget_input(&login, config.max_login_len);
widget_input(&password, config.max_password_len);
desktop_load(&desktop);
tb_init();
tb_select_output_mode(TB_OUTPUT_TRUECOLOR);
tb_clear();
draw_init();
draw_box();
draw_labels();
draw_f_commands();
draw_lock_state();
position_input(&desktop, &login, &password);
load(&desktop, &login);
draw_desktop(&desktop);
draw_input(&login);
draw_input_mask(&password);
(*input_handles[active_input])(input_structs[active_input], NULL);
tb_present();
switch_tty();
bool run = true;
while (run)
{
error = tb_peek_event(&event, config.min_refresh_delta);
if (error < 0)
{
continue;
}
if (event.type == TB_EVENT_KEY)
{
if (event.key == TB_KEY_F1)
{
shutdown = SHUTDOWN_YES;
break;
}
else if (event.key == TB_KEY_F2)
{
shutdown = SHUTDOWN_REBOOT;
break;
}
else if (event.key == TB_KEY_CTRL_C)
{
break;
}
else if ((event.key == TB_KEY_ARROW_UP) && (active_input > 0))
{
--active_input;
}
else if (((event.key == TB_KEY_ARROW_DOWN) || (event.key == TB_KEY_ENTER))
&& (active_input < 2))
{
++active_input;
}
else if (event.key == TB_KEY_ENTER)
{
save(&desktop, &login);
status = login_desktop(&desktop, &login, &password);
load(&desktop, &login);
error = 1; // triggers cursor and screen update
if (status != OK)
{
++config.auth_fails;
config.old_min_refresh_delta = config.min_refresh_delta;
config.old_force_update = config.force_update;
config.min_refresh_delta = 10;
config.force_update = true;
}
}
}
if (error > 0)
{
// calls the apropriate function depending on the active input
(*input_handles[active_input])(input_structs[active_input], &event);
}
if (config.force_update || (error > 0))
{
if (config.auth_fails < 10)
{
tb_clear();
draw_init();
animate();
draw_box();
draw_labels();
draw_f_commands();
draw_lock_state();
position_input(&desktop, &login, &password);
draw_desktop(&desktop);
draw_input(&login);
draw_input_mask(&password);
}
else
{
cascade();
}
}
tb_present();
}
// TODO error
tb_shutdown();
widget_desktop_free(&desktop);
widget_input_free(&login);
widget_input_free(&password);
config_lang_free();
free_hostname();
if (shutdown == SHUTDOWN_YES)
{
execl(config.shutdown_cmd, config.shutdown_cmd, "-h", "now", NULL);
}
if (shutdown == SHUTDOWN_REBOOT)
{
execl(config.shutdown_cmd, config.shutdown_cmd, "-r", "now", NULL);
}
config_config_free();
return 0;
}

@ -0,0 +1,147 @@
#define _XOPEN_SOURCE 700
#include "util.h"
#include "config.h"
#include "widgets.h"
#include "cylgom.h"
#include <string.h>
#include <unistd.h>
// hostname
#include <limits.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include <linux/vt.h>
char* hostname_backup;
void hostname(char** out)
{
struct addrinfo hints;
struct addrinfo* info;
char hostname[HOST_NAME_MAX + 1];
char* dot;
int result;
hostname[HOST_NAME_MAX] = '\0';
gethostname(hostname, HOST_NAME_MAX);
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_CANONNAME;
result = getaddrinfo(hostname, "http", &hints, &info);
if ((result == 0) && (info != NULL))
{
dot = strchr(info->ai_canonname, '.');
*out = strndup(info->ai_canonname, dot - info->ai_canonname);
}
else
{
*out = strdup("");
}
hostname_backup = *out;
freeaddrinfo(info);
}
void free_hostname()
{
free(hostname_backup);
}
void switch_tty()
{
FILE* console = fopen(config.console_dev, "w");
if (console == NULL)
{
info_line = lang.err_console_dev;
return;
}
int fd = fileno(console);
ioctl(fd, VT_ACTIVATE, config.tty);
ioctl(fd, VT_WAITACTIVE, config.tty);
fclose(console);
}
void save(struct desktop* desktop, struct input* login)
{
if (config.save)
{
FILE* file = fopen(config.save_file, "wb+");
if (file != NULL)
{
fprintf(file, "%s\n%d", login->text, desktop->cur);
fclose(file);
}
}
}
void load(struct desktop* desktop, struct input* login)
{
if (config.load == 0)
{
return;
}
FILE* file = fopen(config.save_file, "rb");
if (file == NULL)
{
return;
}
char* line = malloc((config.max_login_len * (sizeof (char))) + 1);
if (line == NULL)
{
fclose(file);
return;
}
if (fgets(line, (config.max_login_len * (sizeof (char))) + 1, file))
{
strncpy(login->text, line, login->len);
int len = strlen(line);
if (len == 0)
{
login->end = login->text;
}
else
{
login->end = login->text + len - 1;
login->text[len - 1] = '\0';
}
}
else
{
fclose(file);
free(line);
return;
}
if (fgets(line, (config.max_login_len * (sizeof (char))) + 1, file))
{
int saved_cur = abs(atoi(line));
if (saved_cur < desktop->len)
{
desktop->cur = saved_cur;
}
}
fclose(file);
free(line);
}

@ -0,0 +1,12 @@
#ifndef H_UTIL
#define H_UTIL
#include "widgets.h"
void hostname(char** out);
void free_hostname();
void switch_tty();
void save(struct desktop* desktop, struct input* login);
void load(struct desktop* desktop, struct input* login);
#endif

@ -0,0 +1,184 @@
#define _XOPEN_SOURCE 700
#include "widgets.h"
#include "cylgom.h"
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/mman.h>
enum err widget_desktop(struct desktop* target)
{
enum err error = OK;
// one default slot for the shell
target->list = NULL;
target->cmd = NULL;
target->display_server = NULL;
target->cur = 0;
target->len = 0;
error |= widget_desktop_add(target, strdup(lang.shell), strdup(""), DS_SHELL);
error |= widget_desktop_add(target, strdup(lang.xinitrc), strdup(""), DS_XINITRC);
return error;
}
enum err widget_input(struct input* target, u64 len)
{
enum err error = OK;
int ret;
target->text = malloc(len + 1);
if (target->text == NULL)
{
error = ERR;
}
else
{
// lock inputs memory so it won't swap and leak the password
// probably not relevant as most software is insecure as hell,
// but hey are we trying to write good code or not?
ret = mlock(target->text, len + 1);
if (ret < 0)
{
error = SECURE_RAM;
}
memset(target->text, 0, len + 1);
}
target->cur = target->text;
target->end = target->text;
target->visible_start = target->text;
target->len = len;
return error;
}
void widget_desktop_free(struct desktop* target)
{
if (target != NULL)
{
for (u16 i = 0; i < target->len; ++i)
{
if (target->list[i] != NULL)
{
free(target->list[i]);
}
if (target->cmd[i] != NULL)
{
free(target->cmd[i]);
}
}
free(target->list);
free(target->cmd);
free(target->display_server);
}
}
void widget_input_free(struct input* target)
{
// wipes the passord from memory and
// restores the buffer's address as swappable
memset(target->text, 0, target->len);
munlock(target->text, target->len + 1);
free(target->text);
}
void widget_desktop_move_cur(struct desktop* target, enum direction dest)
{
if ((dest == RIGHT) && (target->cur < (target->len - 1)))
{
++(target->cur);
}
if ((dest == LEFT) && (target->cur > 0))
{
--(target->cur);
}
}
enum err widget_desktop_add(struct desktop* target, char* name, char* cmd, enum display_server display_server)
{
++(target->len);
target->list = realloc(target->list, target->len * (sizeof (char*)));
target->cmd = realloc(target->cmd, target->len * (sizeof (char*)));
target->display_server = realloc(target->display_server, target->len * (sizeof (enum display_server)));
target->cur = target->len - 1;
if ((target->list == NULL)
|| (target->cmd == NULL)
|| (target->display_server == NULL))
{
return ERR;
}
target->list[target->cur] = name;
target->cmd[target->cur] = cmd;
target->display_server[target->cur] = display_server;
return OK;
}
void widget_input_move_cur(struct input* target, enum direction dest)
{
if ((dest == RIGHT) && (target->cur < target->end))
{
++(target->cur);
if ((target->cur - target->visible_start) > target->visible_len)
{
++(target->visible_start);
}
}
if ((dest == LEFT) && (target->cur > target->text))
{
--(target->cur);
if ((target->cur - target->visible_start) < 0)
{
--(target->visible_start);
}
}
}
void widget_input_write(struct input* target, char ascii)
{
if (ascii <= 0)
{
return; // unices do not support usernames and passwords other than ascii
}
if ((target->end - target->text + 1) < target->len)
{
// moves the text on the right to add space for the new ascii char
memcpy(target->cur + 1, target->cur, target->end - target->cur);
++(target->end);
// adds the new char and moves the cursor to the right
*(target->cur) = ascii;
widget_input_move_cur(target, RIGHT);
}
}
void widget_input_delete(struct input* target)
{
if (target->cur < target->end)
{
// moves the text on the right to overwrite the currently pointed char
memcpy(target->cur, target->cur + 1, target->end - target->cur + 1);
--(target->end);
}
}
void widget_input_backspace(struct input* target)
{
if (target->cur > target->text)
{
widget_input_move_cur(target, LEFT);
widget_input_delete(target);
}
}

@ -0,0 +1,49 @@
#ifndef H_WIDGETS
#define H_WIDGETS
#include "cylgom.h"
#include "config.h"
#include <stdbool.h>
enum direction {LEFT, RIGHT};
struct input
{
char* text;
char* end;
u64 len;
char* cur;
char* visible_start;
u16 visible_len;
u16 x;
u16 y;
};
struct desktop
{
char** list;
char** cmd;
enum display_server* display_server;
u16 cur;
u16 len;
u16 visible_len;
u16 x;
u16 y;
};
enum err widget_desktop(struct desktop* target);
enum err widget_input(struct input* target, u64 len);
void widget_desktop_free(struct desktop* target);
void widget_input_free(struct input* target);
void widget_desktop_move_cur(struct desktop* target, enum direction dest);
enum err widget_desktop_add(struct desktop* target, char* name, char* cmd, enum display_server display_server);
void widget_input_move_cur(struct input* target, enum direction dest);
void widget_input_write(struct input* target, char ascii);
void widget_input_delete(struct input* target);
void widget_input_backspace(struct input* target);
#endif

@ -0,0 +1 @@
Subproject commit 0ee2bf26abccc63ee0a5a416ed9cdf4d113d8c25

@ -0,0 +1 @@
Subproject commit 467501d0fc3c4bd6889af54a62d2ff7580325444

@ -0,0 +1,4 @@
#!/bin/bash
~/.xsession
exec $@
Loading…
Cancel
Save