├── .gitignore ├── .gitmodules ├── install.sh ├── license.md ├── makefile ├── readme.md ├── res ├── config.ini ├── lang │ ├── en.ini │ ├── es.ini │ ├── fr.ini │ ├── it.ini │ ├── pt.ini │ ├── ro.ini │ ├── ru.ini │ └── tr.ini ├── ly-runit-service │ ├── conf │ ├── finish │ └── run ├── ly.service ├── pam.d │ └── ly ├── valgrind.supp ├── wsetup.sh └── xsetup.sh └── src ├── config.c ├── config.h ├── dragonfail_error.h ├── draw.c ├── draw.h ├── inputs.c ├── inputs.h ├── login.c ├── login.h ├── main.c ├── utils.c └── utils.h /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | obj 3 | valgrind.log 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "sub/argoat"] 2 | path = sub/argoat 3 | url = https://github.com/nullgemm/argoat.git 4 | [submodule "sub/configator"] 5 | path = sub/configator 6 | url = https://github.com/nullgemm/configator.git 7 | [submodule "sub/dragonfail"] 8 | path = sub/dragonfail 9 | url = https://github.com/nullgemm/dragonfail.git 10 | [submodule "sub/termbox_next"] 11 | path = sub/termbox_next 12 | url = https://github.com/nullgemm/termbox_next.git 13 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | install -dZ ${DESTDIR}/etc/ly 4 | install -DZ bin/ly -t ${DESTDIR}/usr/bin 5 | install -DZ res/xsetup.sh -t ${DESTDIR}/etc/ly 6 | install -DZ res/wsetup.sh -t ${DESTDIR}/etc/ly 7 | install -DZ res/config.ini -t ${DESTDIR}/etc/ly 8 | install -dZ ${DESTDIR}/etc/ly/lang 9 | install -DZ res/lang/* -t ${DESTDIR}/etc/ly/lang 10 | install -DZ res/ly.service -t ${DESTDIR}/usr/lib/systemd/system 11 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | NAME = ly 2 | CC = gcc 3 | FLAGS = -std=c99 -pedantic -g 4 | FLAGS+= -Wall -Wextra -Werror=vla -Wno-unused-parameter 5 | #FLAGS+= -DDEBUG 6 | FLAGS+= -DGIT_VERSION_STRING=\"$(shell git describe --long --tags | sed 's/\([^-]*-g\)/r\1/;s/-/./g')\" 7 | LINK = -lpam -lxcb 8 | VALGRIND = --show-leak-kinds=all --track-origins=yes --leak-check=full --suppressions=../res/valgrind.supp 9 | CMD = ./$(NAME) 10 | 11 | OS:= $(shell uname -s) 12 | ifeq ($(OS), Linux) 13 | FLAGS+= -D_DEFAULT_SOURCE 14 | endif 15 | 16 | BIND = bin 17 | OBJD = obj 18 | SRCD = src 19 | SUBD = sub 20 | RESD = res 21 | TESTD = tests 22 | 23 | DATADIR ?= ${DESTDIR}/etc/ly 24 | FLAGS+= -DDATADIR=\"$(DATADIR)\" 25 | 26 | INCL = -I$(SRCD) 27 | INCL+= -I$(SUBD)/argoat/src 28 | INCL+= -I$(SUBD)/configator/src 29 | INCL+= -I$(SUBD)/dragonfail/src 30 | INCL+= -I$(SUBD)/termbox_next/src 31 | 32 | SRCS = $(SRCD)/main.c 33 | SRCS += $(SRCD)/config.c 34 | SRCS += $(SRCD)/draw.c 35 | SRCS += $(SRCD)/inputs.c 36 | SRCS += $(SRCD)/login.c 37 | SRCS += $(SRCD)/utils.c 38 | SRCS += $(SUBD)/argoat/src/argoat.c 39 | SRCS += $(SUBD)/configator/src/configator.c 40 | SRCS += $(SUBD)/dragonfail/src/dragonfail.c 41 | 42 | SRCS_OBJS:= $(patsubst %.c,$(OBJD)/%.o,$(SRCS)) 43 | SRCS_OBJS+= $(SUBD)/termbox_next/bin/termbox.a 44 | 45 | .PHONY: final 46 | final: $(BIND)/$(NAME) 47 | 48 | $(OBJD)/%.o: %.c 49 | @echo "building object $@" 50 | @mkdir -p $(@D) 51 | @$(CC) $(INCL) $(FLAGS) -c -o $@ $< 52 | 53 | $(SUBD)/termbox_next/bin/termbox.a: 54 | @echo "building static object $@" 55 | @(cd $(SUBD)/termbox_next && $(MAKE)) 56 | 57 | $(BIND)/$(NAME): $(SRCS_OBJS) 58 | @echo "compiling executable $@" 59 | @mkdir -p $(@D) 60 | @$(CC) -o $@ $^ $(LINK) 61 | 62 | run: 63 | @cd $(BIND) && $(CMD) 64 | 65 | leak: leakgrind 66 | leakgrind: $(BIND)/$(NAME) 67 | @rm -f valgrind.log 68 | @cd $(BIND) && valgrind $(VALGRIND) 2> ../valgrind.log $(CMD) 69 | @less valgrind.log 70 | 71 | install: $(BIND)/$(NAME) 72 | @echo "installing" 73 | @install -dZ ${DESTDIR}/etc/ly 74 | @install -DZ $(BIND)/$(NAME) -t ${DESTDIR}/usr/bin 75 | @install -DZ $(RESD)/config.ini -t ${DESTDIR}/etc/ly 76 | @install -DZ $(RESD)/xsetup.sh -t $(DATADIR) 77 | @install -DZ $(RESD)/wsetup.sh -t $(DATADIR) 78 | @install -dZ $(DATADIR)/lang 79 | @install -DZ $(RESD)/lang/* -t $(DATADIR)/lang 80 | @install -DZ $(RESD)/ly.service -m 644 -t ${DESTDIR}/usr/lib/systemd/system 81 | @install -DZ $(RESD)/pam.d/ly -m 644 -t ${DESTDIR}/etc/pam.d 82 | 83 | installnoconf: $(BIND)/$(NAME) 84 | @echo "installing without the configuration file" 85 | @install -dZ ${DESTDIR}/etc/ly 86 | @install -DZ $(BIND)/$(NAME) -t ${DESTDIR}/usr/bin 87 | @install -DZ $(RESD)/xsetup.sh -t $(DATADIR) 88 | @install -DZ $(RESD)/wsetup.sh -t $(DATADIR) 89 | @install -dZ $(DATADIR)/lang 90 | @install -DZ $(RESD)/lang/* -t $(DATADIR)/lang 91 | @install -DZ $(RESD)/ly.service -m 644 -t ${DESTDIR}/usr/lib/systemd/system 92 | @install -DZ $(RESD)/pam.d/ly -m 644 -t ${DESTDIR}/etc/pam.d 93 | 94 | uninstall: 95 | @echo "uninstalling" 96 | @rm -rf ${DESTDIR}/etc/ly 97 | @rm -rf $(DATADIR) 98 | @rm -f ${DESTDIR}/usr/bin/ly 99 | @rm -f ${DESTDIR}/usr/lib/systemd/system/ly.service 100 | @rm -f ${DESTDIR}/etc/pam.d/ly 101 | 102 | clean: 103 | @echo "cleaning" 104 | @rm -rf $(BIND) $(OBJD) valgrind.log 105 | @(cd $(SUBD)/termbox_next && $(MAKE) clean) 106 | 107 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Ly Reloaded - a TUI display manager 2 | ![Ly screenshot](https://user-images.githubusercontent.com/5473047/88958888-65efbf80-d2a1-11ea-8ae5-3f263bce9cce.png "Ly screenshot") 3 | 4 | # Merging with Ly 5 | That's right, Ly Reloaded is merging into Ly! 6 | 7 | Ly Reloaded is a maintained fork of Ly with bug fixes and patches. It's a lightweight TUI (ncurses-like) display manager for Linux and BSD. 8 | 9 | ## Download 10 | An older package is available in the AUR as ``ly-plank-patched`` which only contains the XDG_SESSION_TYPE patch. 11 |
A newer package, which directly pulls from the latest git commits, is available in the AUR as ``ly-reloaded-git`` (thanks [ibrokemypie](https://github.com/ibrokemypie)!) 12 | 13 | However, to make Ly Reloaded launch at boot, you'll need to enable the ``ly`` systemd service after installing the package. 14 | 15 | ## Dependencies 16 | - a C99 compiler (tested with tcc and gcc) 17 | - a C standard library 18 | - GNU make 19 | - pam 20 | - xcb 21 | - xorg 22 | - xorg-xauth 23 | - mcookie 24 | - tput 25 | - shutdown 26 | 27 | On Debian-based distros running `apt install build-essential libpam0g-dev libxcb-xkb-dev` as root should install all the dependencies for you. 28 | 29 | ## Support 30 | The following desktop environments were tested with success 31 | - awesome 32 | - herbstluftwm 33 | - bspwm (might have a few problems) 34 | - budgie 35 | - cinnamon 36 | - deepin 37 | - dwm 38 | - enlightenment 39 | - gnome 40 | - i3 41 | - kde 42 | - lxde 43 | - lxqt 44 | - mate 45 | - qtile 46 | - sway 47 | - xfce 48 | - pantheon 49 | - maxx 50 | - windowmaker 51 | 52 | Ly Reloaded should work with any X desktop environment, and provides 53 | basic wayland support (sway works very well, for example). 54 | 55 | ## systemd? 56 | Unlike what you may have heard, Ly Reloaded does not require `systemd`, 57 | and was even specifically designed not to depend on `logind`. 58 | You should be able to make it work easily with a better init, 59 | changing the source code won't be necessary :) 60 | 61 | ## Cloning and Compiling 62 | Clone the repository 63 | ``` 64 | git clone --recurse-submodules https://github.com/SartoxSoftware/ly-reloaded 65 | ``` 66 | 67 | Go to its directory 68 | ``` 69 | cd ly-reloaded 70 | ``` 71 | 72 | Compile 73 | ``` 74 | make 75 | ``` 76 | 77 | Install Ly and the provided systemd service file 78 | ``` 79 | sudo make install 80 | ``` 81 | 82 | Enable the service 83 | ``` 84 | sudo systemctl enable ly.service 85 | ``` 86 | 87 | Test in the configured tty (tty2 by default) 88 | or a terminal emulator (but desktop environments won't start) 89 | ``` 90 | sudo make run 91 | ``` 92 | 93 | If you need to switch between ttys after Ly's start you also have to 94 | disable getty on Ly's tty to prevent "login" from spawning on top of it 95 | ``` 96 | sudo systemctl disable getty@tty2.service 97 | ``` 98 | 99 | ## Configuration 100 | You can find all the configuration in `/etc/ly/config.ini`. 101 | The file is commented, and includes the default values. 102 | 103 | ## Controls 104 | Use the up and down arrow keys to change the current field, and the 105 | left and right arrow keys to change the target desktop environment 106 | while on the desktop field (above the login field). 107 | 108 | ## .xinitrc 109 | If your .xinitrc doesn't work make sure it is executable and includes a shebang. 110 | This file is supposed to be a shell script! Quoting from xinit's man page: 111 | ``` 112 | If no specific client program is given on the command line, xinit will look for 113 | a file in the user's home directory called .xinitrc to run as a shell script to 114 | start up client programs. 115 | ``` 116 | On Arch Linux, the example .xinitrc (/etc/X11/xinit/xinitrc) starts like this: 117 | ``` 118 | #!/bin/sh 119 | ``` 120 | 121 | ## Tips 122 | The numlock and capslock state is printed in the top-right corner. 123 | Use the F1 and F2 keys to respectively shutdown and reboot. 124 | Take a look at your .xsession if X doesn't start, as it can interfere 125 | (this file is launched with X to configure the display properly). 126 | 127 | ## PSX DOOM fire animation 128 | To enable the famous PSX DOOM fire described by [Fabien Sanglard](http://fabiensanglard.net/doom_fire_psx/index.html), 129 | just uncomment `animate = true` in `/etc/ly/config.ini`. You may also 130 | disable the main box borders with `hide_borders = true`. 131 | 132 | ## CMatrix animation 133 | You can also enable a Matrix-like animation (which is based on the CMatrix project). To do that, simply uncomment `animate = true` but also uncomment `animation = 1`. 134 | 135 | ## Additional Information 136 | The name "Ly" is a tribute to the fairy from the game Rayman. 137 | Ly was tested by oxodao, who is some seriously awesome dude. 138 | -------------------------------------------------------------------------------- /res/config.ini: -------------------------------------------------------------------------------- 1 | # animation enabled 2 | #animate = false 3 | #animate = true 4 | 5 | # the active animation 6 | # 0 -> PSX DOOM fire (default) 7 | # 1 -> CMatrix 8 | #animation = 0 9 | 10 | # the char used to mask the password 11 | #asterisk = * 12 | #asterisk = o 13 | 14 | # print nor asterisk and password 15 | #asterisk_empty = true 16 | #asterisk_empty = false 17 | 18 | # background color id 19 | #bg = 0 20 | 21 | # blank main box 22 | #blank_box = true 23 | 24 | # erase password input on failure 25 | #blank_password = false 26 | #blank_password = true 27 | 28 | # console path 29 | #console_dev = /dev/console 30 | 31 | # input active by default on startup 32 | #default_input = 2 33 | 34 | # foreground color id 35 | #fg = 9 36 | 37 | # remove main box borders 38 | #hide_borders = false 39 | #hide_borders = true 40 | 41 | # remove f1 commands 42 | #hide_f1_commands = false 43 | #hide_f1_commands = true 44 | 45 | # remove date and time 46 | #hide_time = false 47 | #hide_time = true 48 | 49 | # date and time format 50 | #time_format = %m/%d/%Y %r 51 | #time_format = %d.%m.%Y %H:%M 52 | 53 | # number of visible chars on an input 54 | #input_len = 34 55 | 56 | # active language 57 | #lang = en 58 | #lang = fr 59 | 60 | # load the saved desktop and login 61 | #load = true 62 | 63 | # main box margins 64 | #margin_box_h = 2 65 | #margin_box_v = 1 66 | 67 | # total input sizes 68 | #max_desktop_len = 100 69 | #max_login_len = 255 70 | #max_password_len = 255 71 | 72 | # cookie generator 73 | #mcookie_cmd = /usr/bin/mcookie 74 | 75 | # event timeout in milliseconds 76 | #min_refresh_delta = 5 77 | 78 | # default path 79 | #path = /sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin 80 | 81 | # command executed when pressing F2 82 | #restart_cmd = /sbin/shutdown -r now 83 | 84 | # save the current desktop and login as defaults 85 | #save = true 86 | 87 | # file in which to save and load the default desktop and login 88 | #save_file = /etc/ly/save 89 | 90 | # service name (set to ly to use the provided pam config file) 91 | #service_name = ly 92 | 93 | # command executed when pressing F1 94 | #shutdown_cmd = /sbin/shutdown -a now 95 | 96 | # terminal reset command (tput is faster) 97 | #term_reset_cmd = /usr/bin/tput reset 98 | 99 | # tty in use 100 | #tty = 2 101 | 102 | # wayland setup command 103 | #wayland_cmd = /etc/ly/wsetup.sh 104 | 105 | # add wayland specifier to session names 106 | #wayland_specifier = false 107 | #wayland_specifier = true 108 | 109 | # wayland desktop environments 110 | #waylandsessions = /usr/share/wayland-sessions 111 | 112 | # xorg server command 113 | #x_cmd = /usr/bin/X 114 | 115 | # xorg setup command 116 | #x_cmd_setup = /etc/ly/xsetup.sh 117 | 118 | # xorg xauthority edition tool 119 | #xauth_cmd = /usr/bin/xauth 120 | 121 | # xorg desktop environments 122 | #xsessions = /usr/share/xsessions 123 | -------------------------------------------------------------------------------- /res/lang/en.ini: -------------------------------------------------------------------------------- 1 | capslock = capslock 2 | err_alloc = failed memory allocation 3 | err_bounds = out-of-bounds index 4 | err_chdir = failed to open home folder 5 | err_console_dev = failed to access console 6 | err_dgn_oob = log message 7 | err_domain = invalid domain 8 | err_hostname = failed to get hostname 9 | err_mlock = failed to lock password memory 10 | err_null = null pointer 11 | err_pam = pam transaction failed 12 | err_pam_abort = pam transaction aborted 13 | err_pam_acct_expired = account expired 14 | err_pam_auth = authentication error 15 | err_pam_authinfo_unavail = failed to get user info 16 | err_pam_authok_reqd = token expired 17 | err_pam_buf = memory buffer error 18 | err_pam_cred_err = failed to set credentials 19 | err_pam_cred_expired = credentials expired 20 | err_pam_cred_insufficient = insufficient credentials 21 | err_pam_cred_unavail = failed to get credentials 22 | err_pam_maxtries = reached maximum tries limit 23 | err_pam_perm_denied = permission denied 24 | err_pam_session = session error 25 | err_pam_sys = system error 26 | err_pam_user_unknown = unknown user 27 | err_path = failed to set path 28 | err_perm_dir = failed to change current directory 29 | err_perm_group = failed to downgrade group permissions 30 | err_perm_user = failed to downgrade user permissions 31 | err_pwnam = failed to get user info 32 | err_user_gid = failed to set user GID 33 | err_user_init = failed to initialize user 34 | err_user_uid = failed to set user UID 35 | err_xsessions_dir = failed to find sessions folder 36 | err_xsessions_open = failed to open sessions folder 37 | f1 = F1 shutdown 38 | f2 = F2 reboot 39 | login = login: 40 | logout = logged out 41 | numlock = numlock 42 | password = password: 43 | shell = shell 44 | wayland = wayland 45 | xinitrc = xinitrc 46 | -------------------------------------------------------------------------------- /res/lang/es.ini: -------------------------------------------------------------------------------- 1 | capslock = Bloq Mayús 2 | err_alloc = asignación de memoria fallida 3 | err_bounds = índice fuera de límites 4 | err_chdir = error al abrir la carpeta home 5 | err_console_dev = error al acceder a la consola 6 | err_dgn_oob = mensaje de registro 7 | err_domain = dominio inválido 8 | err_hostname = error al obtener el nombre de host 9 | err_mlock = error al bloquear la contraseña de memoria 10 | err_null = puntero nulo 11 | err_pam = error en la transacción pam 12 | err_pam_abort = transacción pam abortada 13 | err_pam_acct_expired = cuenta expirada 14 | err_pam_auth = error de autenticación 15 | err_pam_authinfo_unavail = error al obtener información del usuario 16 | err_pam_authok_reqd = token expirado 17 | err_pam_buf = error de la memoria intermedia 18 | err_pam_cred_err = error al establecer la credenciales 19 | err_pam_cred_expired = credenciales expiradas 20 | err_pam_cred_insufficient = credenciales insuficientes 21 | err_pam_cred_unavail = error al obtener credenciales 22 | err_pam_maxtries = se ha alcanzado el límite de intentos 23 | err_pam_perm_denied = permiso denegado 24 | err_pam_session = error de sesión 25 | err_pam_sys = error de sistema 26 | err_pam_user_unknown = usuario desconocido 27 | err_path = error al establecer la ruta 28 | err_perm_dir = error al cambiar el directorio actual 29 | err_perm_group = error al degradar los permisos del grupo 30 | err_perm_user = error al degradar los permisos del usuario 31 | err_pwnam = error al obtener la información del usuario 32 | err_user_gid = error al establecer el GID del usuario 33 | err_user_init = errpr al inicializar usuario 34 | err_user_uid = error al establecer el UID del usuario 35 | err_xsessions_dir = error al buscar la carpeta de sesiones 36 | err_xsessions_open = error al abrir la carpeta de sesiones 37 | f1 = F1 apagar 38 | f2 = F2 reiniciar 39 | login = iniciar sesion: 40 | logout = cerrar sesion 41 | numlock = Bloq Num 42 | password = contraseña: 43 | shell = shell 44 | wayland = wayland 45 | xinitrc = xinitrc 46 | -------------------------------------------------------------------------------- /res/lang/fr.ini: -------------------------------------------------------------------------------- 1 | capslock = verr.maj 2 | err_alloc = échec d'allocation mémoire 3 | err_bounds = indice hors-limite 4 | err_chdir = échec de l'ouverture du répertoire home 5 | err_console_dev = échec d'accès à la console 6 | err_dgn_oob = message 7 | err_domain = domaine invalide 8 | err_hostname = échec de captation du nom d'hôte 9 | err_mlock = échec du verrouillage mémoire 10 | err_null = pointeur null 11 | err_pam = échec de la transaction pam 12 | err_pam_abort = transaction pam avortée 13 | err_pam_acct_expired = compte expiré 14 | err_pam_auth = erreur d'authentification 15 | err_pam_authok_reqd = tiquet expiré 16 | err_pam_authinfo_unavail = échec de l'obtention des infos utilisateur 17 | err_pam_buf = erreur de mémoire tampon 18 | err_pam_cred_err = échec de la modification des identifiants 19 | err_pam_cred_expired = identifiants expirés 20 | err_pam_cred_insufficient = identifiants insuffisants 21 | err_pam_cred_unavail = échec de l'obtention des identifiants 22 | err_pam_maxtries = limite d'essais atteinte 23 | err_pam_perm_denied = permission refusée 24 | err_pam_session = erreur de session 25 | err_pam_sys = erreur système 26 | err_pam_user_unknown = utilisateur inconnu 27 | err_path = échec de la modification du path 28 | err_perm_dir = échec de changement de répertoire 29 | err_perm_group = échec du déclassement des permissions de groupe 30 | err_perm_user = échec du déclassement des permissions utilisateur 31 | err_pwnam = échec de captation des infos utilisateur 32 | err_user_gid = échec de modification du GID 33 | err_user_init = échec d'initialisation de l'utilisateur 34 | err_user_uid = échec de modification du UID 35 | err_xsessions_dir = échec de la recherche du dossier de sessions 36 | err_xsessions_open = échec de l'ouverture du dossier de sessions 37 | f1 = F1 éteindre 38 | f2 = F2 redémarrer 39 | login = identifiant : 40 | logout = déconnection 41 | numlock = verr.num 42 | password = mot de passe : 43 | shell = shell 44 | wayland = wayland 45 | xinitrc = xinitrc 46 | -------------------------------------------------------------------------------- /res/lang/it.ini: -------------------------------------------------------------------------------- 1 | capslock = capslock 2 | err_alloc = impossibile allocare memoria 3 | err_bounds = indice fuori limite 4 | err_chdir = impossibile aprire home directory 5 | err_console_dev = impossibile aprire console 6 | err_dgn_oob = messaggio log 7 | err_domain = dominio non valido 8 | err_hostname = impossibile ottenere hostname 9 | err_mlock = impossibile ottenere lock per la password in memoria 10 | err_null = puntatore nullo 11 | err_pam = transazione PAM fallita 12 | err_pam_abort = transazione PAM interrotta 13 | err_pam_acct_expired = account scaduto 14 | err_pam_auth = errore di autenticazione 15 | err_pam_authinfo_unavail = impossibile ottenere informazioni utente 16 | err_pam_authok_reqd = token scaduto 17 | err_pam_buf = errore buffer memoria 18 | err_pam_cred_err = impossibile impostare credenziali 19 | err_pam_cred_expired = credenziali scadute 20 | err_pam_cred_insufficient = credenziali insufficienti 21 | err_pam_cred_unavail = impossibile ottenere credenziali 22 | err_pam_maxtries = raggiunto limite tentativi 23 | err_pam_perm_denied = permesso negato 24 | err_pam_session = errore di sessione 25 | err_pam_sys = errore di sistema 26 | err_pam_user_unknown = utente sconosciuto 27 | err_path = impossibile impostare percorso 28 | err_perm_dir = impossibile cambiare directory corrente 29 | err_perm_group = impossibile ridurre permessi gruppo 30 | err_perm_user = impossibile ridurre permessi utente 31 | err_pwnam = impossibile ottenere dati utente 32 | err_user_gid = impossibile impostare GID utente 33 | err_user_init = impossibile inizializzare utente 34 | err_user_uid = impossible impostare UID utente 35 | err_xsessions_dir = impossibile localizzare cartella sessioni 36 | err_xsessions_open = impossibile aprire cartella sessioni 37 | f1 = F1 arresto 38 | f2 = F2 riavvio 39 | login = username: 40 | logout = scollegato 41 | numlock = numlock 42 | password = password: 43 | shell = shell 44 | wayland = wayland 45 | xinitrc = xinitrc 46 | -------------------------------------------------------------------------------- /res/lang/pt.ini: -------------------------------------------------------------------------------- 1 | capslock = caixa alta 2 | err_alloc = alocação de memória malsucedida 3 | err_bounds = índice fora de limites 4 | err_chdir = não foi possível abrir o diretório home 5 | err_console_dev = não foi possível acessar o console 6 | err_dgn_oob = mensagem de log 7 | err_domain = domínio inválido 8 | err_hostname = não foi possível obter o nome do host 9 | err_mlock = bloqueio da memória de senha malsucedido 10 | err_null = ponteiro nulo 11 | err_pam = transação pam malsucedida 12 | err_pam_abort = transação pam abortada 13 | err_pam_acct_expired = conta expirada 14 | err_pam_auth = erro de autenticação 15 | err_pam_authinfo_unavail = não foi possível obter informações do usuário 16 | err_pam_authok_reqd = token expirada 17 | err_pam_buf = erro de buffer de memória 18 | err_pam_cred_err = erro para definir credenciais 19 | err_pam_cred_expired = credenciais expiradas 20 | err_pam_cred_insufficient = credenciais insuficientes 21 | err_pam_cred_unavail = não foi possível obter credenciais 22 | err_pam_maxtries = limite máximo de tentativas atingido 23 | err_pam_perm_denied = permissão negada 24 | err_pam_session = erro de sessão 25 | err_pam_sys = erro de sistema 26 | err_pam_user_unknown = usuário desconhecido 27 | err_path = não foi possível definir o caminho 28 | err_perm_dir = não foi possível alterar o diretório atual 29 | err_perm_group = não foi possível reduzir as permissões de grupo 30 | err_perm_user = não foi possível reduzir as permissões de usuário 31 | err_pwnam = não foi possível obter informações do usuário 32 | err_user_gid = não foi possível definir o GID do usuário 33 | err_user_init = não foi possível iniciar o usuário 34 | err_user_uid = não foi possível definir o UID do usuário 35 | err_xsessions_dir = não foi possível encontrar a pasta das sessões 36 | err_xsessions_open = não foi possível abrir a pasta das sessões 37 | f1 = F1 desligar 38 | f2 = F2 reiniciar 39 | login = conectar: 40 | logout = desconectado 41 | numlock = numlock 42 | password = senha: 43 | shell = shell 44 | wayland = wayland 45 | xinitrc = xinitrc 46 | -------------------------------------------------------------------------------- /res/lang/ro.ini: -------------------------------------------------------------------------------- 1 | capslock = capslock 2 | 3 | 4 | 5 | err_console_dev = nu s-a putut accesa consola 6 | 7 | 8 | 9 | 10 | 11 | 12 | err_pam_abort = tranzacţie pam anulată 13 | err_pam_acct_expired = cont expirat 14 | err_pam_auth = eroare de autentificare 15 | err_pam_authinfo_unavail = nu s-au putut obţine informaţii despre utilizator 16 | err_pam_authok_reqd = token expirat 17 | err_pam_buf = eroare de memorie (buffer) 18 | err_pam_cred_err = nu s-au putut seta date de identificare (credentials) 19 | err_pam_cred_expired = datele de identificare (credentials) au expirat 20 | err_pam_cred_insufficient = date de identificare (credentials) insuficiente 21 | err_pam_cred_unavail = nu s-au putut obţine date de indentificare (credentials) 22 | err_pam_maxtries = s-a atins numărul maxim de încercări 23 | err_pam_perm_denied = acces interzis 24 | err_pam_session = eroare de sesiune 25 | err_pam_sys = eroare de sistem 26 | err_pam_user_unknown = utilizator necunoscut 27 | 28 | err_perm_dir = nu s-a putut schimba dosarul (folder-ul) curent 29 | err_perm_group = nu s-a putut face downgrade permisiunilor de grup 30 | err_perm_user = nu s-a putut face downgrade permisiunilor de utilizator 31 | 32 | 33 | 34 | 35 | 36 | 37 | f1 = F1 opreşte sistemul 38 | f2 = F2 resetează 39 | login = utilizator: 40 | logout = opreşte sesiunea 41 | numlock = numlock 42 | password = parolă: 43 | shell = shell 44 | wayland = wayland 45 | xinitrc = xinitrc 46 | -------------------------------------------------------------------------------- /res/lang/ru.ini: -------------------------------------------------------------------------------- 1 | capslock = capslock 2 | err_alloc = не удалось выделить память 3 | err_bounds = за пределами индекса 4 | err_chdir = не удалось открыть домашнюю папку 5 | err_console_dev = не удалось получить доступ к консоли 6 | err_dgn_oob = отладочное сообщение (log) 7 | err_domain = неверный домен 8 | err_hostname = не удалось получить имя хоста 9 | err_mlock = сбой блокировки памяти 10 | err_null = нулевой указатель 11 | err_pam = pam транзакция не удалась 12 | err_pam_abort = pam транзакция прервана 13 | err_pam_acct_expired = срок действия аккаунта истек 14 | err_pam_auth = ошибка аутентификации 15 | err_pam_authinfo_unavail = не удалось получить информацию о пользователе 16 | err_pam_authok_reqd = токен истек 17 | err_pam_buf = ошибка буфера памяти 18 | err_pam_cred_err = не удалось установить полномочия 19 | err_pam_cred_expired = полномочия истекли 20 | err_pam_cred_insufficient = недостаточо полномочий 21 | err_pam_cred_unavail = не удалось получить полномочия 22 | err_pam_maxtries = лимит попыток исчерпан 23 | err_pam_perm_denied = доступ запрещен 24 | err_pam_session = ошибка сессии 25 | err_pam_sys = системная ошибка 26 | err_pam_user_unknown = неизвестный пользователь 27 | err_path = не удалось установить путь 28 | err_perm_dir = не удалось изменить текущий каталог 29 | err_perm_group = не удалось понизить права доступа группы 30 | err_perm_user = не удалось понизить права доступа пользователя 31 | err_pwnam = не удалось получить информацию о пользователе 32 | err_user_gid = не удалось установить GID пользователя 33 | err_user_init = не удалось инициализировать пользователя 34 | err_user_uid = не удалось установить UID пользователя 35 | err_xsessions_dir = не удалось найти сессионную папку 36 | err_xsessions_open = не удалось открыть сессионную папку 37 | f1 = F1 выключить 38 | f2 = F2 перезагрузить 39 | login = логин: 40 | logout = logged out 41 | numlock = numlock 42 | password = пароль: 43 | shell = shell 44 | wayland = wayland 45 | xinitrc = xinitrc 46 | -------------------------------------------------------------------------------- /res/lang/tr.ini: -------------------------------------------------------------------------------- 1 | capslock = capslock 2 | err_alloc = başarısız bellek tahsisi 3 | err_bounds = sınır dışı indeksi 4 | err_chdir = ana klasör açılamadı 5 | err_console_dev = konsola erişilemedi 6 | err_dgn_oob = günlük mesajı 7 | err_domain = geçersiz alan 8 | err_hostname = ana bilgisayar adı alınamadı 9 | err_mlock = şifre hafızası kilitlenemedi 10 | err_null = boş işaretçisi 11 | err_pam = pam işlemi başarısız oldu 12 | err_pam_abort = pam işlemi iptal edildi 13 | err_pam_acct_expired = hesabın süresi dolmuş 14 | err_pam_auth = doğrulama hatası 15 | err_pam_authinfo_unavail = kullanıcı bilgisi alınamadı 16 | err_pam_authok_reqd = token süresi doldu 17 | err_pam_buf = bellek tampon hatası 18 | err_pam_cred_err = kimlik bilgileri ayarlanamadı 19 | err_pam_cred_expired = kimlik bilgilerinin süresi doldu 20 | err_pam_cred_insufficient = yetersiz kimlik bilgileri 21 | err_pam_cred_unavail = kimlik bilgileri alınamadı 22 | err_pam_maxtries = maksimum deneme sınırına ulaşıldı 23 | err_pam_perm_denied = izin reddedildi 24 | err_pam_session = oturum hatası 25 | err_pam_sys = Sistem hatası 26 | err_pam_user_unknown = bilinmeyen kullanıcı 27 | err_path = yol ayarlanamadı 28 | err_perm_dir = mevcut dizini değiştiremedi 29 | err_perm_group = grup izinleri eski sürüme geçirilemedi 30 | err_perm_user = kullanıcı izinlerini düşürme başarısız 31 | err_pwnam = kullanıcı bilgisi alınamadı 32 | err_user_gid = kullanıcı GID'si ayarlanamadı 33 | err_user_init = kullanıcı başlatılamadı 34 | err_user_uid = kullanıcı UID'si ayarlanamadı 35 | err_xsessions_dir = oturumlar klasörü bulunamadı 36 | err_xsessions_open = oturumlar klasörü açılamadı 37 | f1 = F1 Bilgisayarı Kapat 38 | f2 = F2 Yeniden Başlat 39 | login = Giriş: 40 | logout = Çıkış yapıldı 41 | numlock = numlock 42 | password = Parola: 43 | shell = shell 44 | wayland = wayland 45 | xinitrc = xinitrc 46 | -------------------------------------------------------------------------------- /res/ly-runit-service/conf: -------------------------------------------------------------------------------- 1 | if [ -x /sbin/agetty -o -x /bin/agetty ]; then 2 | # util-linux specific settings 3 | if [ "${tty}" = "tty1" ]; then 4 | GETTY_ARGS="--noclear" 5 | fi 6 | fi 7 | 8 | BAUD_RATE=38400 9 | TERM_NAME=linux 10 | 11 | StandardInput=tty 12 | TTYPath=/dev/tty2 13 | TTYReset=yes 14 | TTYVHangup=yes 15 | -------------------------------------------------------------------------------- /res/ly-runit-service/finish: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | tty=${PWD##*-} 3 | exec utmpset -w $tty 4 | -------------------------------------------------------------------------------- /res/ly-runit-service/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | tty=${PWD##*-} 3 | 4 | [ -r conf ] && . ./conf 5 | 6 | if [ -x /sbin/getty -o -x /bin/getty ]; then 7 | # busybox 8 | GETTY=getty 9 | elif [ -x /sbin/agetty -o -x /bin/agetty ]; then 10 | # util-linux 11 | GETTY=agetty 12 | fi 13 | 14 | exec setsid ${GETTY} ${GETTY_ARGS} -nl /usr/bin/ly tty2 "${BAUD_RATE}" "${TERM_NAME}" 15 | -------------------------------------------------------------------------------- /res/ly.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=TUI display manager 3 | After=systemd-user-sessions.service plymouth-quit-wait.service 4 | After=getty@tty2.service 5 | 6 | [Service] 7 | Type=idle 8 | ExecStart=/usr/bin/ly 9 | StandardInput=tty 10 | TTYPath=/dev/tty2 11 | TTYReset=yes 12 | TTYVHangup=yes 13 | 14 | [Install] 15 | Alias=display-manager.service 16 | -------------------------------------------------------------------------------- /res/pam.d/ly: -------------------------------------------------------------------------------- 1 | #%PAM-1.0 2 | 3 | auth include login 4 | account include login 5 | password include login 6 | session include login 7 | -------------------------------------------------------------------------------- /res/valgrind.supp: -------------------------------------------------------------------------------- 1 | { 2 | pam 3 | Memcheck:Leak 4 | ... 5 | obj:/usr/lib/libpam.so.0.84.2 6 | ... 7 | } 8 | 9 | { 10 | termbox 11 | Memcheck:Leak 12 | ... 13 | fun:tb_init 14 | ... 15 | } 16 | 17 | { 18 | libc/dynamic 19 | Memcheck:Leak 20 | ... 21 | fun:_dl_catch_exception 22 | ... 23 | } 24 | 25 | { 26 | libc/groups 27 | Memcheck:Leak 28 | ... 29 | fun:initgroups 30 | ... 31 | } 32 | -------------------------------------------------------------------------------- /res/wsetup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # wayland-session - run as user 3 | # Copyright (C) 2015-2016 Pier Luigi Fiorini 4 | 5 | # This file is extracted from kde-workspace (kdm/kfrontend/genkdmconf.c) 6 | # Copyright (C) 2001-2005 Oswald Buddenhagen 7 | 8 | # Note that the respective logout scripts are not sourced. 9 | case $SHELL in 10 | */bash) 11 | [ -z "$BASH" ] && exec "$SHELL" "$0" "$@" 12 | set +o posix 13 | [ -f /etc/profile ] && . /etc/profile 14 | if [ -f "$HOME"/.bash_profile ]; then 15 | . "$HOME"/.bash_profile 16 | elif [ -f "$HOME"/.bash_login ]; then 17 | . "$HOME"/.bash_login 18 | elif [ -f "$HOME"/.profile ]; then 19 | . "$HOME"/.profile 20 | fi 21 | ;; 22 | */zsh) 23 | [ -z "$ZSH_NAME" ] && exec "$SHELL" "$0" "$@" 24 | [ -d /etc/zsh ] && zdir=/etc/zsh || zdir=/etc 25 | zhome=${ZDOTDIR:-$HOME} 26 | # zshenv is always sourced automatically. 27 | [ -f $zdir/zprofile ] && . $zdir/zprofile 28 | [ -f "$zhome"/.zprofile ] && . "$zhome"/.zprofile 29 | [ -f $zdir/zlogin ] && . $zdir/zlogin 30 | [ -f "$zhome"/.zlogin ] && . "$zhome"/.zlogin 31 | emulate -R sh 32 | ;; 33 | */csh|*/tcsh) 34 | # [t]cshrc is always sourced automatically. 35 | # Note that sourcing csh.login after .cshrc is non-standard. 36 | wlsess_tmp=$(mktemp /tmp/wlsess-env-XXXXXX) 37 | $SHELL -c "if (-f /etc/csh.login) source /etc/csh.login; if (-f ~/.login) source ~/.login; /bin/sh -c 'export -p' >! $wlsess_tmp" 38 | # shellcheck source=/tmp/wlsess-env-XXXXXX 39 | . "$wlsess_tmp" 40 | rm -f "$wlsess_tmp" 41 | ;; 42 | */fish) 43 | [ -f /etc/profile ] && . /etc/profile 44 | wlsess_tmp=$(mktemp /tmp/wlsess-env-XXXXXX) 45 | $SHELL --login -c "/bin/sh -c 'export -p' > $wlsess_tmp" 46 | # shellcheck source=/tmp/wlsess-env-XXXXXX 47 | . "$wlsess_tmp" 48 | rm -f "$wlsess_tmp" 49 | ;; 50 | *) # Plain sh, ksh, and anything we do not know. 51 | [ -f /etc/profile ] && . /etc/profile 52 | [ -f "$HOME"/.profile ] && . "$HOME"/.profile 53 | ;; 54 | esac 55 | 56 | exec "$@" 57 | -------------------------------------------------------------------------------- /res/xsetup.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # Xsession - run as user 3 | # Copyright (C) 2016 Pier Luigi Fiorini 4 | 5 | # This file is extracted from kde-workspace (kdm/kfrontend/genkdmconf.c) 6 | # Copyright (C) 2001-2005 Oswald Buddenhagen 7 | 8 | # Note that the respective logout scripts are not sourced. 9 | case $SHELL in 10 | */bash) 11 | [ -z "$BASH" ] && exec "$SHELL" "$0" "$@" 12 | set +o posix 13 | [ -f /etc/profile ] && . /etc/profile 14 | if [ -f "$HOME"/.bash_profile ]; then 15 | . "$HOME"/.bash_profile 16 | elif [ -f "$HOME"/.bash_login ]; then 17 | . "$HOME"/.bash_login 18 | elif [ -f "$HOME"/.profile ]; then 19 | . "$HOME"/.profile 20 | fi 21 | ;; 22 | */zsh) 23 | [ -z "$ZSH_NAME" ] && exec "$SHELL" "$0" "$@" 24 | [ -d /etc/zsh ] && zdir=/etc/zsh || zdir=/etc 25 | zhome=${ZDOTDIR:-$HOME} 26 | # zshenv is always sourced automatically. 27 | [ -f $zdir/zprofile ] && . $zdir/zprofile 28 | [ -f "$zhome"/.zprofile ] && . "$zhome"/.zprofile 29 | [ -f $zdir/zlogin ] && . $zdir/zlogin 30 | [ -f "$zhome"/.zlogin ] && . "$zhome"/.zlogin 31 | emulate -R sh 32 | ;; 33 | */csh|*/tcsh) 34 | # [t]cshrc is always sourced automatically. 35 | # Note that sourcing csh.login after .cshrc is non-standard. 36 | xsess_tmp=$(mktemp /tmp/xsess-env-XXXXXX) 37 | $SHELL -c "if (-f /etc/csh.login) source /etc/csh.login; if (-f ~/.login) source ~/.login; /bin/sh -c 'export -p' >! $xsess_tmp" 38 | # shellcheck source=/tmp/xsess-env-XXXXXX 39 | . "$xsess_tmp" 40 | rm -f "$xsess_tmp" 41 | ;; 42 | */fish) 43 | [ -f /etc/profile ] && . /etc/profile 44 | xsess_tmp=$(mktemp /tmp/xsess-env-XXXXXX) 45 | $SHELL --login -c "/bin/sh -c 'export -p' > $xsess_tmp" 46 | # shellcheck source=/tmp/xsess-env-XXXXXX 47 | . "$xsess_tmp" 48 | rm -f "$xsess_tmp" 49 | ;; 50 | *) # Plain sh, ksh, and anything we do not know. 51 | [ -f /etc/profile ] && . /etc/profile 52 | [ -f "$HOME"/.profile ] && . "$HOME"/.profile 53 | ;; 54 | esac 55 | 56 | [ -f /etc/xprofile ] && . /etc/xprofile 57 | [ -f "$HOME"/.xprofile ] && . "$HOME"/.xprofile 58 | 59 | # run all system xinitrc shell scripts. 60 | if [ -d /etc/X11/xinit/xinitrc.d ]; then 61 | for i in /etc/X11/xinit/xinitrc.d/* ; do 62 | if [ -x "$i" ]; then 63 | # shellcheck source=/dev/null 64 | . "$i" 65 | fi 66 | done 67 | fi 68 | 69 | # Load Xsession scripts 70 | # OPTIONFILE, USERXSESSION, USERXSESSIONRC and ALTUSERXSESSION are required 71 | # by the scripts to work 72 | xsessionddir="/etc/X11/Xsession.d" 73 | OPTIONFILE=/etc/X11/Xsession.options 74 | USERXSESSION=$HOME/.xsession 75 | USERXSESSIONRC=$HOME/.xsessionrc 76 | ALTUSERXSESSION=$HOME/.Xsession 77 | 78 | if [ -d "$xsessionddir" ]; then 79 | for i in $xsessionddir; do 80 | script="$xsessionddir/$i" 81 | echo "Loading X session script $script" 82 | if [ -r "$script" ] && [ -f "$script" ] && expr "$i" : '^[[:alnum:]_-]\+$' > /dev/null; then 83 | # shellcheck source=/dev/null 84 | . "$script" 85 | fi 86 | done 87 | fi 88 | 89 | if [ -d /etc/X11/Xresources ]; then 90 | for i in /etc/X11/Xresources/*; do 91 | [ -f "$i" ] && xrdb -merge "$i" 92 | done 93 | elif [ -f /etc/X11/Xresources ]; then 94 | xrdb -merge /etc/X11/Xresources 95 | fi 96 | [ -f "$HOME"/.Xresources ] && xrdb -merge "$HOME"/.Xresources 97 | [ -f "$XDG_CONFIG_HOME"/X11/Xresources ] && xrdb -merge "$XDG_CONFIG_HOME"/X11/Xresources 98 | 99 | if [ -f "$USERXSESSION" ]; then 100 | # shellcheck source=$HOME/.xsession 101 | . "$USERXSESSION" 102 | fi 103 | 104 | if [ -z "$*" ]; then 105 | exec xmessage -center -buttons OK:0 -default OK "Sorry, $DESKTOP_SESSION is no valid session." 106 | else 107 | exec "$@" 108 | fi 109 | -------------------------------------------------------------------------------- /src/config.c: -------------------------------------------------------------------------------- 1 | #include "configator.h" 2 | 3 | #include "config.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | // 0.6.0 14 | //#ifndef DEBUG 15 | #define INI_LANG DATADIR "/etc/ly/lang/%s.ini" 16 | #define INI_CONFIG "/etc/ly/config.ini" 17 | /*#else 18 | #define INI_LANG "../res/lang/%s.ini" 19 | #define INI_CONFIG "../res/config.ini" 20 | #endif*/ 21 | 22 | static void lang_handle(void* data, char** pars, const int pars_count) 23 | { 24 | if (*((char**)data) != NULL) 25 | { 26 | free (*((char**)data)); 27 | } 28 | 29 | *((char**)data) = strdup(*pars); 30 | } 31 | 32 | static void config_handle_u8(void* data, char** pars, const int pars_count) 33 | { 34 | if (strcmp(*pars, "") == 0) 35 | { 36 | *((uint8_t*)data) = 0; 37 | } 38 | else 39 | { 40 | *((uint8_t*)data) = atoi(*pars); 41 | } 42 | } 43 | 44 | static void config_handle_u16(void* data, char** pars, const int pars_count) 45 | { 46 | if (strcmp(*pars, "") == 0) 47 | { 48 | *((uint16_t*)data) = 0; 49 | } 50 | else 51 | { 52 | *((uint16_t*)data) = atoi(*pars); 53 | } 54 | } 55 | 56 | void config_handle_str(void* data, char** pars, const int pars_count) 57 | { 58 | if (*((char**)data) != NULL) 59 | { 60 | free(*((char**)data)); 61 | } 62 | 63 | *((char**)data) = strdup(*pars); 64 | } 65 | 66 | static void config_handle_char(void* data, char** pars, const int pars_count) 67 | { 68 | *((char*)data) = **pars; 69 | } 70 | 71 | static void config_handle_bool(void* data, char** pars, const int pars_count) 72 | { 73 | *((bool*)data) = (strcmp("true", *pars) == 0); 74 | } 75 | 76 | void lang_load() 77 | { 78 | // must be alphabetically sorted 79 | struct configator_param map_no_section[] = 80 | { 81 | {"capslock", &lang.capslock, lang_handle}, 82 | {"err_alloc", &lang.err_alloc, lang_handle}, 83 | {"err_bounds", &lang.err_bounds, lang_handle}, 84 | {"err_chdir", &lang.err_chdir, lang_handle}, 85 | {"err_console_dev", &lang.err_console_dev, lang_handle}, 86 | {"err_dgn_oob", &lang.err_dgn_oob, lang_handle}, 87 | {"err_domain", &lang.err_domain, lang_handle}, 88 | {"err_hostname", &lang.err_hostname, lang_handle}, 89 | {"err_mlock", &lang.err_mlock, lang_handle}, 90 | {"err_null", &lang.err_null, lang_handle}, 91 | {"err_pam", &lang.err_pam, lang_handle}, 92 | {"err_pam_abort", &lang.err_pam_abort, lang_handle}, 93 | {"err_pam_acct_expired", &lang.err_pam_acct_expired, lang_handle}, 94 | {"err_pam_auth", &lang.err_pam_auth, lang_handle}, 95 | {"err_pam_authinfo_unavail", &lang.err_pam_authinfo_unavail, lang_handle}, 96 | {"err_pam_authok_reqd", &lang.err_pam_authok_reqd, lang_handle}, 97 | {"err_pam_buf", &lang.err_pam_buf, lang_handle}, 98 | {"err_pam_cred_err", &lang.err_pam_cred_err, lang_handle}, 99 | {"err_pam_cred_expired", &lang.err_pam_cred_expired, lang_handle}, 100 | {"err_pam_cred_insufficient", &lang.err_pam_cred_insufficient, lang_handle}, 101 | {"err_pam_cred_unavail", &lang.err_pam_cred_unavail, lang_handle}, 102 | {"err_pam_maxtries", &lang.err_pam_maxtries, lang_handle}, 103 | {"err_pam_perm_denied", &lang.err_pam_perm_denied, lang_handle}, 104 | {"err_pam_session", &lang.err_pam_session, lang_handle}, 105 | {"err_pam_sys", &lang.err_pam_sys, lang_handle}, 106 | {"err_pam_user_unknown", &lang.err_pam_user_unknown, lang_handle}, 107 | {"err_path", &lang.err_path, lang_handle}, 108 | {"err_perm_dir", &lang.err_perm_dir, lang_handle}, 109 | {"err_perm_group", &lang.err_perm_group, lang_handle}, 110 | {"err_perm_user", &lang.err_perm_user, lang_handle}, 111 | {"err_pwnam", &lang.err_pwnam, lang_handle}, 112 | {"err_user_gid", &lang.err_user_gid, lang_handle}, 113 | {"err_user_init", &lang.err_user_init, lang_handle}, 114 | {"err_user_uid", &lang.err_user_uid, lang_handle}, 115 | {"err_xsessions_dir", &lang.err_xsessions_dir, lang_handle}, 116 | {"err_xsessions_open", &lang.err_xsessions_open, lang_handle}, 117 | {"f1", &lang.f1, lang_handle}, 118 | {"f2", &lang.f2, lang_handle}, 119 | {"login", &lang.login, lang_handle}, 120 | {"logout", &lang.logout, lang_handle}, 121 | {"numlock", &lang.numlock, lang_handle}, 122 | {"password", &lang.password, lang_handle}, 123 | {"shell", &lang.shell, lang_handle}, 124 | {"wayland", &lang.wayland, lang_handle}, 125 | {"xinitrc", &lang.xinitrc, lang_handle}, 126 | }; 127 | 128 | uint16_t map_len[] = {45}; 129 | struct configator_param* map[] = 130 | { 131 | map_no_section, 132 | }; 133 | 134 | uint16_t sections_len = 0; 135 | struct configator_param* sections = NULL; 136 | 137 | struct configator lang; 138 | lang.map = map; 139 | lang.map_len = map_len; 140 | lang.sections = sections; 141 | lang.sections_len = sections_len; 142 | 143 | char file[256]; 144 | snprintf(file, 256, INI_LANG, config.lang); 145 | 146 | if (access(file, F_OK) != -1) 147 | { 148 | configator(&lang, file); 149 | } 150 | } 151 | 152 | void config_load(const char *cfg_path) 153 | { 154 | if (cfg_path == NULL) 155 | { 156 | cfg_path = INI_CONFIG; 157 | } 158 | // must be alphabetically sorted 159 | struct configator_param map_no_section[] = 160 | { 161 | {"animate", &config.animate, config_handle_bool}, 162 | {"animation", &config.animation, config_handle_u8}, 163 | {"asterisk", &config.asterisk, config_handle_char}, 164 | {"asterisk_empty", &config.asterisk_empty, config_handle_bool}, 165 | {"bg", &config.bg, config_handle_u8}, 166 | {"blank_box", &config.blank_box, config_handle_bool}, 167 | {"blank_password", &config.blank_password, config_handle_bool}, 168 | {"console_dev", &config.console_dev, config_handle_str}, 169 | {"default_input", &config.default_input, config_handle_u8}, 170 | {"fg", &config.fg, config_handle_u8}, 171 | {"hide_borders", &config.hide_borders, config_handle_bool}, 172 | {"hide_f1_commands", &config.hide_f1_commands, config_handle_bool}, 173 | {"hide_time", &config.hide_time, config_handle_bool}, 174 | {"input_len", &config.input_len, config_handle_u8}, 175 | {"lang", &config.lang, config_handle_str}, 176 | {"load", &config.load, config_handle_bool}, 177 | {"margin_box_h", &config.margin_box_h, config_handle_u8}, 178 | {"margin_box_v", &config.margin_box_v, config_handle_u8}, 179 | {"max_desktop_len", &config.max_desktop_len, config_handle_u8}, 180 | {"max_login_len", &config.max_login_len, config_handle_u8}, 181 | {"max_password_len", &config.max_password_len, config_handle_u8}, 182 | {"mcookie_cmd", &config.mcookie_cmd, config_handle_str}, 183 | {"min_refresh_delta", &config.min_refresh_delta, config_handle_u16}, 184 | {"path", &config.path, config_handle_str}, 185 | {"restart_cmd", &config.restart_cmd, config_handle_str}, 186 | {"save", &config.save, config_handle_bool}, 187 | {"save_file", &config.save_file, config_handle_str}, 188 | {"service_name", &config.service_name, config_handle_str}, 189 | {"shutdown_cmd", &config.shutdown_cmd, config_handle_str}, 190 | {"term_reset_cmd", &config.term_reset_cmd, config_handle_str}, 191 | {"time_format", &config.time_format, config_handle_str}, 192 | {"tty", &config.tty, config_handle_u8}, 193 | {"wayland_cmd", &config.wayland_cmd, config_handle_str}, 194 | {"wayland_specifier", &config.wayland_specifier, config_handle_bool}, 195 | {"waylandsessions", &config.waylandsessions, config_handle_str}, 196 | {"x_cmd", &config.x_cmd, config_handle_str}, 197 | {"x_cmd_setup", &config.x_cmd_setup, config_handle_str}, 198 | {"xauth_cmd", &config.xauth_cmd, config_handle_str}, 199 | {"xsessions", &config.xsessions, config_handle_str}, 200 | }; 201 | 202 | uint16_t map_len[] = {34}; 203 | struct configator_param* map[] = 204 | { 205 | map_no_section, 206 | }; 207 | 208 | uint16_t sections_len = 0; 209 | struct configator_param* sections = NULL; 210 | 211 | struct configator config; 212 | config.map = map; 213 | config.map_len = map_len; 214 | config.sections = sections; 215 | config.sections_len = sections_len; 216 | 217 | configator(&config, (char *) cfg_path); 218 | } 219 | 220 | void lang_defaults() 221 | { 222 | lang.capslock = strdup("capslock"); 223 | lang.err_alloc = strdup("failed memory allocation"); 224 | lang.err_bounds = strdup("out-of-bounds index"); 225 | lang.err_chdir = strdup("failed to open home folder"); 226 | lang.err_console_dev = strdup("failed to access console"); 227 | lang.err_dgn_oob = strdup("log message"); 228 | lang.err_domain = strdup("invalid domain"); 229 | lang.err_hostname = strdup("failed to get hostname"); 230 | lang.err_mlock = strdup("failed to lock password memory"); 231 | lang.err_null = strdup("null pointer"); 232 | lang.err_pam = strdup("pam transaction failed"); 233 | lang.err_pam_abort = strdup("pam transaction aborted"); 234 | lang.err_pam_acct_expired = strdup("account expired"); 235 | lang.err_pam_auth = strdup("authentication error"); 236 | lang.err_pam_authinfo_unavail = strdup("failed to get user info"); 237 | lang.err_pam_authok_reqd = strdup("token expired"); 238 | lang.err_pam_buf = strdup("memory buffer error"); 239 | lang.err_pam_cred_err = strdup("failed to set credentials"); 240 | lang.err_pam_cred_expired = strdup("credentials expired"); 241 | lang.err_pam_cred_insufficient = strdup("insufficient credentials"); 242 | lang.err_pam_cred_unavail = strdup("failed to get credentials"); 243 | lang.err_pam_maxtries = strdup("reached maximum tries limit"); 244 | lang.err_pam_perm_denied = strdup("permission denied"); 245 | lang.err_pam_session = strdup("session error"); 246 | lang.err_pam_sys = strdup("system error"); 247 | lang.err_pam_user_unknown = strdup("unknown user"); 248 | lang.err_path = strdup("failed to set path"); 249 | lang.err_perm_dir = strdup("failed to change current directory"); 250 | lang.err_perm_group = strdup("failed to downgrade group permissions"); 251 | lang.err_perm_user = strdup("failed to downgrade user permissions"); 252 | lang.err_pwnam = strdup("failed to get user info"); 253 | lang.err_user_gid = strdup("failed to set user GID"); 254 | lang.err_user_init = strdup("failed to initialize user"); 255 | lang.err_user_uid = strdup("failed to set user UID"); 256 | lang.err_xsessions_dir = strdup("failed to find sessions folder"); 257 | lang.err_xsessions_open = strdup("failed to open sessions folder"); 258 | lang.f1 = strdup("F1 shutdown"); 259 | lang.f2 = strdup("F2 reboot"); 260 | lang.login = strdup("login:"); 261 | lang.logout = strdup("logged out"); 262 | lang.numlock = strdup("numlock"); 263 | lang.password = strdup("password:"); 264 | lang.shell = strdup("shell"); 265 | lang.wayland = strdup("wayland"); 266 | lang.xinitrc = strdup("xinitrc"); 267 | } 268 | 269 | void config_defaults() 270 | { 271 | config.animate = false; 272 | config.animation = 0; 273 | config.asterisk = '*'; 274 | config.asterisk_empty = false; 275 | config.bg = 0; 276 | config.blank_box = true; 277 | config.blank_password = false; 278 | config.console_dev = strdup("/dev/console"); 279 | config.default_input = PASSWORD_INPUT; 280 | config.fg = 9; 281 | config.hide_borders = false; 282 | config.hide_f1_commands = false; 283 | config.hide_time = false; 284 | config.input_len = 34; 285 | config.lang = strdup("en"); 286 | config.load = true; 287 | config.margin_box_h = 2; 288 | config.margin_box_v = 1; 289 | config.max_desktop_len = 100; 290 | config.max_login_len = 255; 291 | config.max_password_len = 255; 292 | config.mcookie_cmd = strdup("/usr/bin/mcookie"); 293 | config.min_refresh_delta = 5; 294 | config.path = strdup("/sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin"); 295 | config.restart_cmd = strdup("/sbin/shutdown -r now"); 296 | config.save = true; 297 | config.save_file = strdup("/etc/ly/save"); 298 | config.service_name = strdup("ly"); 299 | config.shutdown_cmd = strdup("/sbin/shutdown -a now"); 300 | config.term_reset_cmd = strdup("/usr/bin/tput reset"); 301 | config.time_format = strdup("%m/%d/%Y %I:%M %p"); 302 | config.tty = 2; 303 | config.wayland_cmd = strdup(DATADIR "/wsetup.sh"); 304 | config.wayland_specifier = false; 305 | config.waylandsessions = strdup("/usr/share/wayland-sessions"); 306 | config.x_cmd = strdup("/usr/bin/X"); 307 | config.x_cmd_setup = strdup(DATADIR "/xsetup.sh"); 308 | config.xauth_cmd = strdup("/usr/bin/xauth"); 309 | config.xsessions = strdup("/usr/share/xsessions"); 310 | } 311 | 312 | void lang_free() 313 | { 314 | free(lang.capslock); 315 | free(lang.err_alloc); 316 | free(lang.err_bounds); 317 | free(lang.err_chdir); 318 | free(lang.err_console_dev); 319 | free(lang.err_dgn_oob); 320 | free(lang.err_domain); 321 | free(lang.err_hostname); 322 | free(lang.err_mlock); 323 | free(lang.err_null); 324 | free(lang.err_pam); 325 | free(lang.err_pam_abort); 326 | free(lang.err_pam_acct_expired); 327 | free(lang.err_pam_auth); 328 | free(lang.err_pam_authinfo_unavail); 329 | free(lang.err_pam_authok_reqd); 330 | free(lang.err_pam_buf); 331 | free(lang.err_pam_cred_err); 332 | free(lang.err_pam_cred_expired); 333 | free(lang.err_pam_cred_insufficient); 334 | free(lang.err_pam_cred_unavail); 335 | free(lang.err_pam_maxtries); 336 | free(lang.err_pam_perm_denied); 337 | free(lang.err_pam_session); 338 | free(lang.err_pam_sys); 339 | free(lang.err_pam_user_unknown); 340 | free(lang.err_path); 341 | free(lang.err_perm_dir); 342 | free(lang.err_perm_group); 343 | free(lang.err_perm_user); 344 | free(lang.err_pwnam); 345 | free(lang.err_user_gid); 346 | free(lang.err_user_init); 347 | free(lang.err_user_uid); 348 | free(lang.err_xsessions_dir); 349 | free(lang.err_xsessions_open); 350 | free(lang.f1); 351 | free(lang.f2); 352 | free(lang.login); 353 | free(lang.logout); 354 | free(lang.numlock); 355 | free(lang.password); 356 | free(lang.shell); 357 | free(lang.wayland); 358 | free(lang.xinitrc); 359 | } 360 | 361 | void config_free() 362 | { 363 | free(config.console_dev); 364 | free(config.lang); 365 | free(config.mcookie_cmd); 366 | free(config.path); 367 | free(config.restart_cmd); 368 | free(config.save_file); 369 | free(config.service_name); 370 | free(config.shutdown_cmd); 371 | free(config.term_reset_cmd); 372 | free(config.time_format); 373 | free(config.wayland_cmd); 374 | free(config.waylandsessions); 375 | free(config.x_cmd); 376 | free(config.x_cmd_setup); 377 | free(config.xauth_cmd); 378 | free(config.xsessions); 379 | } 380 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | #ifndef H_LY_CONFIG 2 | #define H_LY_CONFIG 3 | 4 | #include 5 | #include 6 | 7 | enum INPUTS { 8 | SESSION_SWITCH, 9 | LOGIN_INPUT, 10 | PASSWORD_INPUT, 11 | }; 12 | 13 | struct lang 14 | { 15 | char* capslock; 16 | char* err_alloc; 17 | char* err_bounds; 18 | char* err_chdir; 19 | char* err_console_dev; 20 | char* err_dgn_oob; 21 | char* err_domain; 22 | char* err_hostname; 23 | char* err_mlock; 24 | char* err_null; 25 | char* err_pam; 26 | char* err_pam_abort; 27 | char* err_pam_acct_expired; 28 | char* err_pam_auth; 29 | char* err_pam_authinfo_unavail; 30 | char* err_pam_authok_reqd; 31 | char* err_pam_buf; 32 | char* err_pam_cred_err; 33 | char* err_pam_cred_expired; 34 | char* err_pam_cred_insufficient; 35 | char* err_pam_cred_unavail; 36 | char* err_pam_maxtries; 37 | char* err_pam_perm_denied; 38 | char* err_pam_session; 39 | char* err_pam_sys; 40 | char* err_pam_user_unknown; 41 | char* err_path; 42 | char* err_perm_dir; 43 | char* err_perm_group; 44 | char* err_perm_user; 45 | char* err_pwnam; 46 | char* err_user_gid; 47 | char* err_user_init; 48 | char* err_user_uid; 49 | char* err_xsessions_dir; 50 | char* err_xsessions_open; 51 | char* f1; 52 | char* f2; 53 | char* login; 54 | char* logout; 55 | char* numlock; 56 | char* password; 57 | char* shell; 58 | char* wayland; 59 | char* xinitrc; 60 | }; 61 | 62 | struct config 63 | { 64 | bool animate; 65 | uint8_t animation; 66 | char asterisk; 67 | bool asterisk_empty; 68 | uint8_t bg; 69 | bool blank_box; 70 | bool blank_password; 71 | char* console_dev; 72 | uint8_t default_input; 73 | uint8_t fg; 74 | bool hide_borders; 75 | bool hide_f1_commands; 76 | bool hide_time; 77 | uint8_t input_len; 78 | char* lang; 79 | bool load; 80 | uint8_t margin_box_h; 81 | uint8_t margin_box_v; 82 | uint8_t max_desktop_len; 83 | uint8_t max_login_len; 84 | uint8_t max_password_len; 85 | char* mcookie_cmd; 86 | uint16_t min_refresh_delta; 87 | char* path; 88 | char* restart_cmd; 89 | bool save; 90 | char* save_file; 91 | char* service_name; 92 | char* shutdown_cmd; 93 | char* term_reset_cmd; 94 | char* time_format; 95 | uint8_t tty; 96 | char* wayland_cmd; 97 | bool wayland_specifier; 98 | char* waylandsessions; 99 | char* x_cmd; 100 | char* x_cmd_setup; 101 | char* xauth_cmd; 102 | char* xsessions; 103 | }; 104 | 105 | extern struct lang lang; 106 | extern struct config config; 107 | 108 | void config_handle_str(void* data, char** pars, const int pars_count); 109 | void lang_load(); 110 | void config_load(const char *cfg_path); 111 | void lang_defaults(); 112 | void config_defaults(); 113 | void lang_free(); 114 | void config_free(); 115 | 116 | #endif 117 | -------------------------------------------------------------------------------- /src/dragonfail_error.h: -------------------------------------------------------------------------------- 1 | #ifndef H_DRAGONFAIL_ERROR 2 | #define H_DRAGONFAIL_ERROR 3 | 4 | enum dgn_error 5 | { 6 | DGN_OK, // do not remove 7 | 8 | DGN_NULL, 9 | DGN_ALLOC, 10 | DGN_BOUNDS, 11 | DGN_DOMAIN, 12 | DGN_MLOCK, 13 | DGN_XSESSIONS_DIR, 14 | DGN_XSESSIONS_OPEN, 15 | DGN_PATH, 16 | DGN_CHDIR, 17 | DGN_PWNAM, 18 | DGN_USER_INIT, 19 | DGN_USER_GID, 20 | DGN_USER_UID, 21 | DGN_PAM, 22 | DGN_HOSTNAME, 23 | 24 | DGN_SIZE, // do not remove 25 | }; 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /src/draw.c: -------------------------------------------------------------------------------- 1 | #include "dragonfail.h" 2 | #include "termbox.h" 3 | 4 | #include "inputs.h" 5 | #include "utils.h" 6 | #include "config.h" 7 | #include "draw.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #if defined(__DragonFly__) || defined(__FreeBSD__) 21 | #include 22 | #else // linux 23 | #include 24 | #endif 25 | 26 | #define DOOM_STEPS 13 27 | 28 | void draw_init(struct term_buf* buf) 29 | { 30 | // 0.6.0, todo 31 | buf->width = tb_width(); 32 | buf->height = tb_height(); 33 | hostname(&buf->info_line); 34 | 35 | uint16_t len_login = strlen(lang.login); 36 | uint16_t len_password = strlen(lang.password); 37 | 38 | if (len_login > len_password) 39 | { 40 | buf->labels_max_len = len_login; 41 | } 42 | else 43 | { 44 | buf->labels_max_len = len_password; 45 | } 46 | 47 | buf->box_height = 7 + (2 * config.margin_box_v); 48 | buf->box_width = 49 | (2 * config.margin_box_h) 50 | + (config.input_len + 1) 51 | + buf->labels_max_len; 52 | 53 | #if defined(__linux__) 54 | buf->box_chars.left_up = 0x250c; 55 | buf->box_chars.left_down = 0x2514; 56 | buf->box_chars.right_up = 0x2510; 57 | buf->box_chars.right_down = 0x2518; 58 | buf->box_chars.top = 0x2500; 59 | buf->box_chars.bot = 0x2500; 60 | buf->box_chars.left = 0x2502; 61 | buf->box_chars.right = 0x2502; 62 | #else 63 | buf->box_chars.left_up = '+'; 64 | buf->box_chars.left_down = '+'; 65 | buf->box_chars.right_up = '+'; 66 | buf->box_chars.right_down= '+'; 67 | buf->box_chars.top = '-'; 68 | buf->box_chars.bot = '-'; 69 | buf->box_chars.left = '|'; 70 | buf->box_chars.right = '|'; 71 | #endif 72 | } 73 | 74 | static void doom_free(struct term_buf* buf); 75 | static void matrix_free(struct term_buf* buf); 76 | 77 | void draw_free(struct term_buf* buf) 78 | { 79 | if (config.animate) 80 | { 81 | switch (config.animation) 82 | { 83 | case 0: 84 | doom_free(buf); 85 | break; 86 | case 1: 87 | matrix_free(buf); 88 | break; 89 | } 90 | } 91 | } 92 | 93 | void draw_box(struct term_buf* buf) 94 | { 95 | uint16_t box_x = (buf->width - buf->box_width) / 2; 96 | uint16_t box_y = (buf->height - buf->box_height) / 2; 97 | uint16_t box_x2 = (buf->width + buf->box_width) / 2; 98 | uint16_t box_y2 = (buf->height + buf->box_height) / 2; 99 | buf->box_x = box_x; 100 | buf->box_y = box_y; 101 | 102 | if (!config.hide_borders) 103 | { 104 | // corners 105 | tb_change_cell( 106 | box_x - 1, 107 | box_y - 1, 108 | buf->box_chars.left_up, 109 | config.fg, 110 | config.bg); 111 | tb_change_cell( 112 | box_x2, 113 | box_y - 1, 114 | buf->box_chars.right_up, 115 | config.fg, 116 | config.bg); 117 | tb_change_cell( 118 | box_x - 1, 119 | box_y2, 120 | buf->box_chars.left_down, 121 | config.fg, 122 | config.bg); 123 | tb_change_cell( 124 | box_x2, 125 | box_y2, 126 | buf->box_chars.right_down, 127 | config.fg, 128 | config.bg); 129 | 130 | // top and bottom 131 | struct tb_cell c1 = {buf->box_chars.top, config.fg, config.bg}; 132 | struct tb_cell c2 = {buf->box_chars.bot, config.fg, config.bg}; 133 | 134 | for (uint8_t i = 0; i < buf->box_width; ++i) 135 | { 136 | tb_put_cell( 137 | box_x + i, 138 | box_y - 1, 139 | &c1); 140 | tb_put_cell( 141 | box_x + i, 142 | box_y2, 143 | &c2); 144 | } 145 | 146 | // left and right 147 | c1.ch = buf->box_chars.left; 148 | c2.ch = buf->box_chars.right; 149 | 150 | for (uint8_t i = 0; i < buf->box_height; ++i) 151 | { 152 | tb_put_cell( 153 | box_x - 1, 154 | box_y + i, 155 | &c1); 156 | 157 | tb_put_cell( 158 | box_x2, 159 | box_y + i, 160 | &c2); 161 | } 162 | } 163 | 164 | if (config.blank_box) 165 | { 166 | struct tb_cell blank = {' ', config.fg, config.bg}; 167 | 168 | for (uint8_t i = 0; i < buf->box_height; ++i) 169 | { 170 | for (uint8_t k = 0; k < buf->box_width; ++k) 171 | { 172 | tb_put_cell( 173 | box_x + k, 174 | box_y + i, 175 | &blank); 176 | } 177 | } 178 | } 179 | } 180 | 181 | struct tb_cell* strn_cell(char* s, uint16_t len) // throws 182 | { 183 | struct tb_cell* cells = malloc((sizeof (struct tb_cell)) * len); 184 | char* s2 = s; 185 | uint32_t c; 186 | 187 | if (cells != NULL) 188 | { 189 | for (uint16_t i = 0; i < len; ++i) 190 | { 191 | if ((s2 - s) >= len) 192 | { 193 | break; 194 | } 195 | 196 | s2 += utf8_char_to_unicode(&c, s2); 197 | 198 | cells[i].ch = c; 199 | cells[i].bg = config.bg; 200 | cells[i].fg = config.fg; 201 | } 202 | } 203 | else 204 | { 205 | dgn_throw(DGN_ALLOC); 206 | } 207 | 208 | return cells; 209 | } 210 | 211 | struct tb_cell* str_cell(char* s) // throws 212 | { 213 | return strn_cell(s, strlen(s)); 214 | } 215 | 216 | void draw_labels(struct term_buf* buf) // throws 217 | { 218 | // login text 219 | struct tb_cell* login = str_cell(lang.login); 220 | 221 | if (dgn_catch()) 222 | { 223 | dgn_reset(); 224 | } 225 | else 226 | { 227 | tb_blit( 228 | buf->box_x + config.margin_box_h, 229 | buf->box_y + config.margin_box_v + 4, 230 | strlen(lang.login), 231 | 1, 232 | login); 233 | free(login); 234 | } 235 | 236 | // password text 237 | struct tb_cell* password = str_cell(lang.password); 238 | 239 | if (dgn_catch()) 240 | { 241 | dgn_reset(); 242 | } 243 | else 244 | { 245 | tb_blit( 246 | buf->box_x + config.margin_box_h, 247 | buf->box_y + config.margin_box_v + 6, 248 | strlen(lang.password), 249 | 1, 250 | password); 251 | free(password); 252 | } 253 | 254 | if (buf->info_line != NULL) 255 | { 256 | uint16_t len = strlen(buf->info_line); 257 | struct tb_cell* info_cell = str_cell(buf->info_line); 258 | 259 | if (dgn_catch()) 260 | { 261 | dgn_reset(); 262 | } 263 | else 264 | { 265 | tb_blit( 266 | buf->box_x + ((buf->box_width - len) / 2), 267 | buf->box_y + config.margin_box_v, 268 | len, 269 | 1, 270 | info_cell); 271 | free(info_cell); 272 | } 273 | } 274 | } 275 | 276 | void draw_time(struct term_buf *buf) 277 | { 278 | time_t timer; 279 | char time_buf[53] = { 0 }; 280 | struct tm* tm_info; 281 | 282 | timer = time(NULL); 283 | tm_info = localtime(&timer); 284 | 285 | strftime(time_buf, 52, config.time_format, tm_info); 286 | 287 | struct tb_cell *time_cell = str_cell(time_buf); 288 | if(dgn_catch()) 289 | { 290 | dgn_reset(); 291 | } 292 | else 293 | { 294 | tb_blit(buf->width / 2 - strlen(time_buf) / 2 + 1, 0, strlen(time_buf), 1, time_cell); 295 | free(time_cell); 296 | } 297 | } 298 | 299 | void draw_f_commands() 300 | { 301 | struct tb_cell* f1 = str_cell(lang.f1); 302 | 303 | if (dgn_catch()) 304 | { 305 | dgn_reset(); 306 | } 307 | else 308 | { 309 | tb_blit(0, 0, strlen(lang.f1), 1, f1); 310 | free(f1); 311 | } 312 | 313 | struct tb_cell* f2 = str_cell(lang.f2); 314 | 315 | if (dgn_catch()) 316 | { 317 | dgn_reset(); 318 | } 319 | else 320 | { 321 | tb_blit(strlen(lang.f1) + 1, 0, strlen(lang.f2), 1, f2); 322 | free(f2); 323 | } 324 | } 325 | 326 | void draw_lock_state(struct term_buf* buf) 327 | { 328 | // get values 329 | int fd = open(config.console_dev, O_RDONLY); 330 | 331 | if (fd < 0) 332 | { 333 | buf->info_line = lang.err_console_dev; 334 | return; 335 | } 336 | 337 | bool numlock_on; 338 | bool capslock_on; 339 | 340 | #if defined(__DragonFly__) || defined(__FreeBSD__) 341 | int led; 342 | ioctl(fd, KDGETLED, &led); 343 | numlock_on = led & LED_NUM; 344 | capslock_on = led & LED_CAP; 345 | #else // linux 346 | char led; 347 | ioctl(fd, KDGKBLED, &led); 348 | numlock_on = led & K_NUMLOCK; 349 | capslock_on = led & K_CAPSLOCK; 350 | #endif 351 | 352 | close(fd); 353 | 354 | // print text 355 | uint16_t pos_x = buf->width - strlen(lang.numlock); 356 | 357 | if (numlock_on) 358 | { 359 | struct tb_cell* numlock = str_cell(lang.numlock); 360 | 361 | if (dgn_catch()) 362 | { 363 | dgn_reset(); 364 | } 365 | else 366 | { 367 | tb_blit(pos_x, 0, strlen(lang.numlock), 1, numlock); 368 | free(numlock); 369 | } 370 | } 371 | 372 | pos_x -= strlen(lang.capslock) + 1; 373 | 374 | if (capslock_on) 375 | { 376 | struct tb_cell* capslock = str_cell(lang.capslock); 377 | 378 | if (dgn_catch()) 379 | { 380 | dgn_reset(); 381 | } 382 | else 383 | { 384 | tb_blit(pos_x, 0, strlen(lang.capslock), 1, capslock); 385 | free(capslock); 386 | } 387 | } 388 | } 389 | 390 | void draw_desktop(struct desktop* target) 391 | { 392 | uint16_t len = strlen(target->list[target->cur]); 393 | 394 | if (len > (target->visible_len - 3)) 395 | { 396 | len = target->visible_len - 3; 397 | } 398 | 399 | tb_change_cell( 400 | target->x, 401 | target->y, 402 | '<', 403 | config.fg, 404 | config.bg); 405 | 406 | tb_change_cell( 407 | target->x + target->visible_len - 1, 408 | target->y, 409 | '>', 410 | config.fg, 411 | config.bg); 412 | 413 | for (uint16_t i = 0; i < len; ++ i) 414 | { 415 | tb_change_cell( 416 | target->x + i + 2, 417 | target->y, 418 | target->list[target->cur][i], 419 | config.fg, 420 | config.bg); 421 | } 422 | } 423 | 424 | void draw_input(struct text* input) 425 | { 426 | uint16_t len = strlen(input->text); 427 | uint16_t visible_len = input->visible_len; 428 | 429 | if (len > visible_len) 430 | { 431 | len = visible_len; 432 | } 433 | 434 | struct tb_cell* cells = strn_cell(input->visible_start, len); 435 | 436 | if (dgn_catch()) 437 | { 438 | dgn_reset(); 439 | } 440 | else 441 | { 442 | tb_blit(input->x, input->y, len, 1, cells); 443 | free(cells); 444 | 445 | struct tb_cell c1 = {' ', config.fg, config.bg}; 446 | 447 | for (uint16_t i = input->end - input->visible_start; i < visible_len; ++i) 448 | { 449 | tb_put_cell( 450 | input->x + i, 451 | input->y, 452 | &c1); 453 | } 454 | } 455 | } 456 | 457 | void draw_input_mask(struct text* input) 458 | { 459 | uint16_t len = strlen(input->text); 460 | uint16_t visible_len = input->visible_len; 461 | 462 | if (len > visible_len) 463 | { 464 | len = visible_len; 465 | } 466 | 467 | struct tb_cell c1 = {config.asterisk, config.fg, config.bg}; 468 | struct tb_cell c2 = {' ', config.fg, config.bg}; 469 | 470 | for (uint16_t i = 0; i < visible_len; ++i) 471 | { 472 | if (input->visible_start + i < input->end) 473 | { 474 | tb_put_cell( 475 | input->x + i, 476 | input->y, 477 | &c1); 478 | } 479 | else 480 | { 481 | tb_put_cell( 482 | input->x + i, 483 | input->y, 484 | &c2); 485 | } 486 | } 487 | } 488 | 489 | void position_input( 490 | struct term_buf* buf, 491 | struct desktop* desktop, 492 | struct text* login, 493 | struct text* password) 494 | { 495 | uint16_t x = buf->box_x + config.margin_box_h + buf->labels_max_len + 1; 496 | int32_t len = buf->box_x + buf->box_width - config.margin_box_h - x; 497 | int32_t len_password = config.asterisk_empty ? 0 : len; 498 | 499 | if (len < 0) 500 | { 501 | return; 502 | } 503 | 504 | desktop->x = x; 505 | desktop->y = buf->box_y + config.margin_box_v + 2; 506 | desktop->visible_len = len; 507 | 508 | login->x = x; 509 | login->y = buf->box_y + config.margin_box_v + 4; 510 | login->visible_len = len; 511 | 512 | password->x = x; 513 | password->y = buf->box_y + config.margin_box_v + 6; 514 | password->visible_len = len_password; 515 | } 516 | 517 | static void doom_init(struct term_buf* buf) 518 | { 519 | buf->init_width = buf->width; 520 | buf->init_height = buf->height; 521 | buf->astate.doom = malloc(sizeof(struct doom_state)); 522 | 523 | if (buf->astate.doom == NULL) 524 | { 525 | dgn_throw(DGN_ALLOC); 526 | } 527 | 528 | uint16_t tmp_len = buf->width * buf->height; 529 | buf->astate.doom->buf = malloc(tmp_len); 530 | tmp_len -= buf->width; 531 | 532 | if (buf->astate.doom->buf == NULL) 533 | { 534 | dgn_throw(DGN_ALLOC); 535 | } 536 | 537 | memset(buf->astate.doom->buf, 0, tmp_len); 538 | memset(buf->astate.doom->buf + tmp_len, DOOM_STEPS - 1, buf->width); 539 | } 540 | 541 | static void doom_free(struct term_buf* buf) 542 | { 543 | free(buf->astate.doom->buf); 544 | free(buf->astate.doom); 545 | } 546 | 547 | // Adapted from cmatrix 548 | static void matrix_init(struct term_buf* buf) 549 | { 550 | buf->init_width = buf->width; 551 | buf->init_height = buf->height; 552 | buf->astate.matrix = malloc(sizeof(struct matrix_state)); 553 | struct matrix_state* s = buf->astate.matrix; 554 | 555 | if (s == NULL) 556 | { 557 | dgn_throw(DGN_ALLOC); 558 | } 559 | 560 | uint16_t len = buf->height + 1; 561 | s->grid = malloc(sizeof(struct matrix_dot*) * len); 562 | 563 | if (s->grid == NULL) 564 | { 565 | dgn_throw(DGN_ALLOC); 566 | } 567 | 568 | len = (buf->height + 1) * buf->width; 569 | (s->grid)[0] = malloc(sizeof(struct matrix_dot) * len); 570 | 571 | if ((s->grid)[0] == NULL) 572 | { 573 | dgn_throw(DGN_ALLOC); 574 | } 575 | 576 | for (int i = 1; i <= buf->height; ++i) 577 | { 578 | s->grid[i] = s->grid[i - 1] + buf->width; 579 | 580 | if (s->grid[i] == NULL) 581 | { 582 | dgn_throw(DGN_ALLOC); 583 | } 584 | } 585 | 586 | s->length = malloc(buf->width * sizeof(int)); 587 | 588 | if (s->length == NULL) 589 | { 590 | dgn_throw(DGN_ALLOC); 591 | } 592 | 593 | s->spaces = malloc(buf->width * sizeof(int)); 594 | 595 | if (s->spaces == NULL) 596 | { 597 | dgn_throw(DGN_ALLOC); 598 | } 599 | 600 | s->updates = malloc(buf->width * sizeof(int)); 601 | 602 | if (s->updates == NULL) 603 | { 604 | dgn_throw(DGN_ALLOC); 605 | } 606 | 607 | // Initialize grid 608 | for (int i = 0; i <= buf->height; ++i) 609 | { 610 | for (int j = 0; j <= buf->width - 1; j += 2) 611 | { 612 | s->grid[i][j].val = -1; 613 | } 614 | } 615 | 616 | for (int j = 0; j < buf->width; j += 2) 617 | { 618 | s->spaces[j] = (int) rand() % buf->height + 1; 619 | s->length[j] = (int) rand() % (buf->height - 3) + 3; 620 | s->grid[1][j].val = ' '; 621 | s->updates[j] = (int) rand() % 3 + 1; 622 | } 623 | } 624 | 625 | static void matrix_free(struct term_buf* buf) 626 | { 627 | free(buf->astate.matrix->grid[0]); 628 | free(buf->astate.matrix->grid); 629 | free(buf->astate.matrix->length); 630 | free(buf->astate.matrix->spaces); 631 | free(buf->astate.matrix->updates); 632 | free(buf->astate.matrix); 633 | } 634 | 635 | void animate_init(struct term_buf* buf) 636 | { 637 | if (config.animate) 638 | { 639 | switch(config.animation) 640 | { 641 | case 0: 642 | { 643 | doom_init(buf); 644 | break; 645 | } 646 | case 1: 647 | { 648 | matrix_init(buf); 649 | break; 650 | } 651 | } 652 | } 653 | } 654 | 655 | static void doom(struct term_buf* term_buf) 656 | { 657 | static struct tb_cell fire[DOOM_STEPS] = 658 | { 659 | {' ', 9, 0}, // default 660 | {0x2591, 2, 0}, // red 661 | {0x2592, 2, 0}, // red 662 | {0x2593, 2, 0}, // red 663 | {0x2588, 2, 0}, // red 664 | {0x2591, 4, 2}, // yellow 665 | {0x2592, 4, 2}, // yellow 666 | {0x2593, 4, 2}, // yellow 667 | {0x2588, 4, 2}, // yellow 668 | {0x2591, 8, 4}, // white 669 | {0x2592, 8, 4}, // white 670 | {0x2593, 8, 4}, // white 671 | {0x2588, 8, 4}, // white 672 | }; 673 | 674 | uint16_t src; 675 | uint16_t random; 676 | uint16_t dst; 677 | 678 | uint16_t w = term_buf->init_width; 679 | uint8_t* tmp = term_buf->astate.doom->buf; 680 | 681 | if ((term_buf->width != term_buf->init_width) || (term_buf->height != term_buf->init_height)) 682 | { 683 | return; 684 | } 685 | 686 | struct tb_cell* buf = tb_cell_buffer(); 687 | 688 | for (uint16_t x = 0; x < w; ++x) 689 | { 690 | for (uint16_t y = 1; y < term_buf->init_height; ++y) 691 | { 692 | src = y * w + x; 693 | random = ((rand() % 7) & 3); 694 | dst = src - random + 1; 695 | 696 | if (w > dst) 697 | { 698 | dst = 0; 699 | } 700 | else 701 | { 702 | dst -= w; 703 | } 704 | 705 | tmp[dst] = tmp[src] - (random & 1); 706 | 707 | if (tmp[dst] > 12) 708 | { 709 | tmp[dst] = 0; 710 | } 711 | 712 | buf[dst] = fire[tmp[dst]]; 713 | buf[src] = fire[tmp[src]]; 714 | } 715 | } 716 | } 717 | 718 | // Adapted from cmatrix 719 | static void matrix(struct term_buf* buf) 720 | { 721 | static int frame = 3; 722 | const int frame_delay = 8; 723 | static int count = 0; 724 | bool first_col; 725 | struct matrix_state* s = buf->astate.matrix; 726 | 727 | // Allowed codepoints 728 | const int randmin = 33; 729 | const int randnum = 123 - randmin; 730 | // Chars change mid-scroll 731 | const bool changes = true; 732 | 733 | if ((buf->width != buf->init_width) || (buf->height != buf->init_height)) 734 | { 735 | return; 736 | } 737 | 738 | count += 1; 739 | if (count > frame_delay) { 740 | frame += 1; 741 | if (frame > 4) frame = 1; 742 | count = 0; 743 | 744 | for (int j = 0; j < buf->width; j += 2) 745 | { 746 | int tail; 747 | if (frame > s->updates[j]) 748 | { 749 | if (s->grid[0][j].val == -1 && s->grid[1][j].val == ' ') 750 | { 751 | if (s->spaces[j] > 0) 752 | { 753 | s->spaces[j]--; 754 | } else { 755 | s->length[j] = (int) rand() % (buf->height - 3) + 3; 756 | s->grid[0][j].val = (int) rand() % randnum + randmin; 757 | s->spaces[j] = (int) rand() % buf->height + 1; 758 | } 759 | } 760 | 761 | int i = 0, seg_len = 0; 762 | first_col = 1; 763 | while (i <= buf->height) 764 | { 765 | // Skip over spaces 766 | while (i <= buf->height 767 | && (s->grid[i][j].val == ' ' || s->grid[i][j].val == -1)) 768 | { 769 | i++; 770 | } 771 | 772 | if (i > buf->height) break; 773 | 774 | // Find the head of this col 775 | tail = i; 776 | seg_len = 0; 777 | while (i <= buf->height 778 | && (s->grid[i][j].val != ' ' && s->grid[i][j].val != -1)) 779 | { 780 | s->grid[i][j].is_head = false; 781 | if (changes) 782 | { 783 | if (rand() % 8 == 0) 784 | s->grid[i][j].val = (int) rand() % randnum + randmin; 785 | } 786 | i++; 787 | seg_len++; 788 | } 789 | 790 | // Head's down offscreen 791 | if (i > buf->height) 792 | { 793 | s->grid[tail][j].val = ' '; 794 | continue; 795 | } 796 | 797 | s->grid[i][j].val = (int) rand() % randnum + randmin; 798 | s->grid[i][j].is_head = true; 799 | 800 | if (seg_len > s->length[j] || !first_col) { 801 | s->grid[tail][j].val = ' '; 802 | s->grid[0][j].val = -1; 803 | } 804 | first_col = 0; 805 | i++; 806 | } 807 | } 808 | } 809 | } 810 | 811 | uint32_t blank; 812 | utf8_char_to_unicode(&blank, " "); 813 | 814 | for (int j = 0; j < buf->width; j += 2) { 815 | for (int i = 1; i <= buf->height; ++i) 816 | { 817 | uint32_t c; 818 | int fg = TB_GREEN; 819 | int bg = TB_DEFAULT; 820 | 821 | if (s->grid[i][j].val == -1 || s->grid[i][j].val == ' ') 822 | { 823 | tb_change_cell(j, i - 1, blank, fg, bg); 824 | continue; 825 | } 826 | 827 | char tmp[2]; 828 | tmp[0] = s->grid[i][j].val; 829 | tmp[1] = '\0'; 830 | if(utf8_char_to_unicode(&c, tmp)) 831 | { 832 | if (s->grid[i][j].is_head) 833 | { 834 | fg = TB_WHITE | TB_BOLD; 835 | } 836 | tb_change_cell(j, i - 1, c, fg, bg); 837 | } 838 | } 839 | } 840 | } 841 | 842 | void animate(struct term_buf* buf) 843 | { 844 | buf->width = tb_width(); 845 | buf->height = tb_height(); 846 | 847 | if (config.animate) 848 | { 849 | switch(config.animation) 850 | { 851 | case 0: 852 | { 853 | doom(buf); 854 | break; 855 | } 856 | case 1: 857 | { 858 | matrix(buf); 859 | break; 860 | } 861 | } 862 | } 863 | } 864 | 865 | bool cascade(struct term_buf* term_buf, uint8_t* fails) 866 | { 867 | uint16_t width = term_buf->width; 868 | uint16_t height = term_buf->height; 869 | 870 | struct tb_cell* buf = tb_cell_buffer(); 871 | bool changes = false; 872 | char c_under; 873 | char c; 874 | 875 | for (int i = height - 2; i >= 0; --i) 876 | { 877 | for (int k = 0; k < width; ++k) 878 | { 879 | c = buf[i * width + k].ch; 880 | 881 | if (isspace(c)) 882 | { 883 | continue; 884 | } 885 | 886 | c_under = buf[(i + 1) * width + k].ch; 887 | 888 | if (!isspace(c_under)) 889 | { 890 | continue; 891 | } 892 | 893 | if (!changes) 894 | { 895 | changes = true; 896 | } 897 | 898 | if ((rand() % 10) > 7) 899 | { 900 | continue; 901 | } 902 | 903 | buf[(i + 1) * width + k] = buf[i * width + k]; 904 | buf[i * width + k].ch = ' '; 905 | } 906 | } 907 | 908 | // stop force-updating 909 | if (!changes) 910 | { 911 | sleep(7); 912 | *fails = 0; 913 | 914 | return false; 915 | } 916 | 917 | // force-update 918 | return true; 919 | } 920 | -------------------------------------------------------------------------------- /src/draw.h: -------------------------------------------------------------------------------- 1 | #ifndef H_LY_DRAW 2 | #define H_LY_DRAW 3 | 4 | #include "termbox.h" 5 | 6 | #include "inputs.h" 7 | #include 8 | #include 9 | 10 | struct box 11 | { 12 | uint32_t left_up; 13 | uint32_t left_down; 14 | uint32_t right_up; 15 | uint32_t right_down; 16 | uint32_t top; 17 | uint32_t bot; 18 | uint32_t left; 19 | uint32_t right; 20 | }; 21 | 22 | struct matrix_dot 23 | { 24 | int val; 25 | bool is_head; 26 | }; 27 | 28 | struct matrix_state 29 | { 30 | struct matrix_dot** grid; 31 | int* length; 32 | int* spaces; 33 | int* updates; 34 | }; 35 | 36 | struct doom_state 37 | { 38 | uint8_t* buf; 39 | }; 40 | 41 | union anim_state 42 | { 43 | struct doom_state* doom; 44 | struct matrix_state* matrix; 45 | }; 46 | 47 | struct term_buf 48 | { 49 | uint16_t width; 50 | uint16_t height; 51 | uint16_t init_width; 52 | uint16_t init_height; 53 | 54 | struct box box_chars; 55 | char* info_line; 56 | uint16_t labels_max_len; 57 | uint16_t box_x; 58 | uint16_t box_y; 59 | uint16_t box_width; 60 | uint16_t box_height; 61 | 62 | union anim_state astate; 63 | }; 64 | 65 | void draw_init(struct term_buf* buf); 66 | void draw_free(struct term_buf* buf); 67 | void draw_box(struct term_buf* buf); 68 | 69 | struct tb_cell* strn_cell(char* s, uint16_t len); 70 | struct tb_cell* str_cell(char* s); 71 | 72 | void draw_labels(struct term_buf* buf); 73 | void draw_f_commands(); 74 | void draw_time(struct term_buf *buf); 75 | void draw_lock_state(struct term_buf* buf); 76 | void draw_desktop(struct desktop* target); 77 | void draw_input(struct text* input); 78 | void draw_input_mask(struct text* input); 79 | 80 | void position_input( 81 | struct term_buf* buf, 82 | struct desktop* desktop, 83 | struct text* login, 84 | struct text* password); 85 | 86 | void animate_init(struct term_buf* buf); 87 | void animate(struct term_buf* buf); 88 | bool cascade(struct term_buf* buf, uint8_t* fails); 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /src/inputs.c: -------------------------------------------------------------------------------- 1 | #include "dragonfail.h" 2 | #include "termbox.h" 3 | 4 | #include "inputs.h" 5 | #include "config.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | void handle_desktop(void* input_struct, struct tb_event* event) 13 | { 14 | struct desktop* target = (struct desktop*) input_struct; 15 | 16 | if ((event != NULL) && (event->type == TB_EVENT_KEY)) 17 | { 18 | if (event->key == TB_KEY_ARROW_LEFT) 19 | { 20 | input_desktop_right(target); 21 | } 22 | else if (event->key == TB_KEY_ARROW_RIGHT) 23 | { 24 | input_desktop_left(target); 25 | } 26 | } 27 | 28 | tb_set_cursor(target->x + 2, target->y); 29 | } 30 | 31 | void handle_text(void* input_struct, struct tb_event* event) 32 | { 33 | struct text* target = (struct text*) input_struct; 34 | 35 | if ((event != NULL) && (event->type == TB_EVENT_KEY)) 36 | { 37 | if (event->key == TB_KEY_ARROW_LEFT) 38 | { 39 | input_text_left(target); 40 | } 41 | else if (event->key == TB_KEY_ARROW_RIGHT) 42 | { 43 | input_text_right(target); 44 | } 45 | else if (event->key == TB_KEY_DELETE) 46 | { 47 | input_text_delete(target); 48 | } 49 | else if ((event->key == TB_KEY_BACKSPACE) 50 | || (event->key == TB_KEY_BACKSPACE2)) 51 | { 52 | input_text_backspace(target); 53 | } 54 | else if (((event->ch > 31) && (event->ch < 127)) 55 | || (event->key == TB_KEY_SPACE)) 56 | { 57 | char buf[7] = {0}; 58 | 59 | if (event->key == TB_KEY_SPACE) 60 | { 61 | buf[0] = ' '; 62 | } 63 | else 64 | { 65 | utf8_unicode_to_char(buf, event->ch); 66 | } 67 | 68 | input_text_write(target, buf[0]); 69 | } 70 | } 71 | 72 | tb_set_cursor( 73 | target->x + (target->cur - target->visible_start), 74 | target->y); 75 | } 76 | 77 | void input_desktop(struct desktop* target) 78 | { 79 | target->list = NULL; 80 | target->cmd = NULL; 81 | target->display_server = NULL; 82 | target->cur = 0; 83 | target->len = 0; 84 | 85 | input_desktop_add(target, strdup(lang.shell), strdup(""), DS_SHELL); 86 | input_desktop_add(target, strdup(lang.xinitrc), strdup("~/.xinitrc"), DS_XINITRC); 87 | #if 0 88 | input_desktop_add(target, strdup(lang.wayland), strdup(""), DS_WAYLAND); 89 | #endif 90 | } 91 | 92 | void input_text(struct text* target, uint64_t len) 93 | { 94 | target->text = malloc(len + 1); 95 | 96 | if (target->text == NULL) 97 | { 98 | dgn_throw(DGN_ALLOC); 99 | return; 100 | } 101 | else 102 | { 103 | int ok = mlock(target->text, len + 1); 104 | 105 | if (ok < 0) 106 | { 107 | dgn_throw(DGN_MLOCK); 108 | return; 109 | } 110 | 111 | memset(target->text, 0, len + 1); 112 | } 113 | 114 | target->cur = target->text; 115 | target->end = target->text; 116 | target->visible_start = target->text; 117 | target->len = len; 118 | target->x = 0; 119 | target->y = 0; 120 | } 121 | 122 | void input_desktop_free(struct desktop* target) 123 | { 124 | if (target != NULL) 125 | { 126 | for (uint16_t i = 0; i < target->len; ++i) 127 | { 128 | if (target->list[i] != NULL) 129 | { 130 | free(target->list[i]); 131 | } 132 | 133 | if (target->cmd[i] != NULL) 134 | { 135 | free(target->cmd[i]); 136 | } 137 | } 138 | 139 | free(target->list); 140 | free(target->cmd); 141 | free(target->display_server); 142 | } 143 | } 144 | 145 | void input_text_free(struct text* target) 146 | { 147 | memset(target->text, 0, target->len); 148 | munlock(target->text, target->len + 1); 149 | free(target->text); 150 | } 151 | 152 | void input_desktop_right(struct desktop* target) 153 | { 154 | ++(target->cur); 155 | 156 | if (target->cur >= target->len) 157 | { 158 | target->cur = 0; 159 | } 160 | } 161 | 162 | void input_desktop_left(struct desktop* target) 163 | { 164 | --(target->cur); 165 | 166 | if (target->cur >= target->len) 167 | { 168 | target->cur = target->len - 1; 169 | } 170 | } 171 | 172 | void input_desktop_add( 173 | struct desktop* target, 174 | char* name, 175 | char* cmd, 176 | enum display_server display_server) 177 | { 178 | ++(target->len); 179 | target->list = realloc(target->list, target->len * (sizeof (char*))); 180 | target->cmd = realloc(target->cmd, target->len * (sizeof (char*))); 181 | target->display_server = realloc( 182 | target->display_server, 183 | target->len * (sizeof (enum display_server))); 184 | target->cur = target->len - 1; 185 | 186 | if ((target->list == NULL) 187 | || (target->cmd == NULL) 188 | || (target->display_server == NULL)) 189 | { 190 | dgn_throw(DGN_ALLOC); 191 | return; 192 | } 193 | 194 | target->list[target->cur] = name; 195 | target->cmd[target->cur] = cmd; 196 | target->display_server[target->cur] = display_server; 197 | } 198 | 199 | void input_text_right(struct text* target) 200 | { 201 | if (target->cur < target->end) 202 | { 203 | ++(target->cur); 204 | 205 | if ((target->cur - target->visible_start) > target->visible_len) 206 | { 207 | ++(target->visible_start); 208 | } 209 | } 210 | } 211 | 212 | void input_text_left(struct text* target) 213 | { 214 | if (target->cur > target->text) 215 | { 216 | --(target->cur); 217 | 218 | if ((target->cur - target->visible_start) < 0) 219 | { 220 | --(target->visible_start); 221 | } 222 | } 223 | } 224 | 225 | void input_text_write(struct text* target, char ascii) 226 | { 227 | if (ascii <= 0) 228 | { 229 | return; // unices do not support usernames and passwords other than ascii 230 | } 231 | 232 | if ((target->end - target->text + 1) < target->len) 233 | { 234 | // moves the text to the right to add space for the new ascii char 235 | memcpy(target->cur + 1, target->cur, target->end - target->cur); 236 | ++(target->end); 237 | // adds the new char and moves the cursor to the right 238 | *(target->cur) = ascii; 239 | input_text_right(target); 240 | } 241 | } 242 | 243 | void input_text_delete(struct text* target) 244 | { 245 | if (target->cur < target->end) 246 | { 247 | // moves the text on the right to overwrite the currently pointed char 248 | memcpy(target->cur, target->cur + 1, target->end - target->cur + 1); 249 | --(target->end); 250 | } 251 | } 252 | 253 | void input_text_backspace(struct text* target) 254 | { 255 | if (target->cur > target->text) 256 | { 257 | input_text_left(target); 258 | input_text_delete(target); 259 | } 260 | } 261 | 262 | void input_text_clear(struct text* target) 263 | { 264 | memset(target->text, 0, target->len + 1); 265 | target->cur = target->text; 266 | target->end = target->text; 267 | target->visible_start = target->text; 268 | } 269 | -------------------------------------------------------------------------------- /src/inputs.h: -------------------------------------------------------------------------------- 1 | #ifndef H_LY_INPUTS 2 | #define H_LY_INPUTS 3 | 4 | #include "termbox.h" 5 | 6 | #include 7 | 8 | enum display_server {DS_WAYLAND, DS_SHELL, DS_XINITRC, DS_XORG}; 9 | 10 | struct text 11 | { 12 | char* text; 13 | char* end; 14 | int64_t len; 15 | char* cur; 16 | char* visible_start; 17 | uint16_t visible_len; 18 | 19 | uint16_t x; 20 | uint16_t y; 21 | }; 22 | 23 | struct desktop 24 | { 25 | char** list; 26 | char** cmd; 27 | enum display_server* display_server; 28 | 29 | uint16_t cur; 30 | uint16_t len; 31 | uint16_t visible_len; 32 | uint16_t x; 33 | uint16_t y; 34 | }; 35 | 36 | void handle_desktop(void* input_struct, struct tb_event* event); 37 | void handle_text(void* input_struct, struct tb_event* event); 38 | void input_desktop(struct desktop* target); 39 | void input_text(struct text* target, uint64_t len); 40 | void input_desktop_free(struct desktop* target); 41 | void input_text_free(struct text* target); 42 | void input_desktop_right(struct desktop* target); 43 | void input_desktop_left(struct desktop* target); 44 | void input_desktop_add( 45 | struct desktop* target, 46 | char* name, 47 | char* cmd, 48 | enum display_server display_server); 49 | void input_text_right(struct text* target); 50 | void input_text_left(struct text* target); 51 | void input_text_write(struct text* target, char ascii); 52 | void input_text_delete(struct text* target); 53 | void input_text_backspace(struct text* target); 54 | void input_text_clear(struct text* target); 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /src/login.c: -------------------------------------------------------------------------------- 1 | #include "dragonfail.h" 2 | #include "termbox.h" 3 | 4 | #include "inputs.h" 5 | #include "draw.h" 6 | #include "utils.h" 7 | #include "config.h" 8 | #include "login.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | int get_free_display() 26 | { 27 | char xlock[1024]; 28 | uint8_t i; 29 | 30 | for (i = 0; i < 200; ++i) 31 | { 32 | snprintf(xlock, 1024, "/tmp/.X%d-lock", i); 33 | 34 | if (access(xlock, F_OK) == -1) 35 | { 36 | break; 37 | } 38 | } 39 | 40 | return i; 41 | } 42 | 43 | void reset_terminal(struct passwd* pwd) 44 | { 45 | pid_t pid = fork(); 46 | 47 | if (pid == 0) 48 | { 49 | execl(pwd->pw_shell, pwd->pw_shell, "-c", config.term_reset_cmd, NULL); 50 | exit(EXIT_SUCCESS); 51 | } 52 | 53 | int status; 54 | waitpid(pid, &status, 0); 55 | } 56 | 57 | int login_conv( 58 | int num_msg, 59 | const struct pam_message** msg, 60 | struct pam_response** resp, 61 | void* appdata_ptr) 62 | { 63 | *resp = calloc(num_msg, sizeof (struct pam_response)); 64 | 65 | if (*resp == NULL) 66 | { 67 | return PAM_BUF_ERR; 68 | } 69 | 70 | char* username; 71 | char* password; 72 | int ok = PAM_SUCCESS; 73 | int i; 74 | 75 | for (i = 0; i < num_msg; ++i) 76 | { 77 | switch (msg[i]->msg_style) 78 | { 79 | case PAM_PROMPT_ECHO_ON: 80 | { 81 | username = ((char**) appdata_ptr)[0]; 82 | (*resp)[i].resp = strdup(username); 83 | break; 84 | } 85 | case PAM_PROMPT_ECHO_OFF: 86 | { 87 | password = ((char**) appdata_ptr)[1]; 88 | (*resp)[i].resp = strdup(password); 89 | break; 90 | } 91 | case PAM_ERROR_MSG: 92 | { 93 | ok = PAM_CONV_ERR; 94 | break; 95 | } 96 | } 97 | 98 | if (ok != PAM_SUCCESS) 99 | { 100 | break; 101 | } 102 | } 103 | 104 | if (ok != PAM_SUCCESS) 105 | { 106 | for (i = 0; i < num_msg; ++i) 107 | { 108 | if ((*resp)[i].resp == NULL) 109 | { 110 | continue; 111 | } 112 | 113 | free((*resp)[i].resp); 114 | (*resp)[i].resp = NULL; 115 | } 116 | 117 | free(*resp); 118 | *resp = NULL; 119 | } 120 | 121 | return ok; 122 | } 123 | 124 | void pam_diagnose(int error, struct term_buf* buf) 125 | { 126 | switch (error) 127 | { 128 | case PAM_ACCT_EXPIRED: 129 | { 130 | buf->info_line = lang.err_pam_acct_expired; 131 | break; 132 | } 133 | case PAM_AUTH_ERR: 134 | { 135 | buf->info_line = lang.err_pam_auth; 136 | break; 137 | } 138 | case PAM_AUTHINFO_UNAVAIL: 139 | { 140 | buf->info_line = lang.err_pam_authinfo_unavail; 141 | break; 142 | } 143 | case PAM_BUF_ERR: 144 | { 145 | buf->info_line = lang.err_pam_buf; 146 | break; 147 | } 148 | case PAM_CRED_ERR: 149 | { 150 | buf->info_line = lang.err_pam_cred_err; 151 | break; 152 | } 153 | case PAM_CRED_EXPIRED: 154 | { 155 | buf->info_line = lang.err_pam_cred_expired; 156 | break; 157 | } 158 | case PAM_CRED_INSUFFICIENT: 159 | { 160 | buf->info_line = lang.err_pam_cred_insufficient; 161 | break; 162 | } 163 | case PAM_CRED_UNAVAIL: 164 | { 165 | buf->info_line = lang.err_pam_cred_unavail; 166 | break; 167 | } 168 | case PAM_MAXTRIES: 169 | { 170 | buf->info_line = lang.err_pam_maxtries; 171 | break; 172 | } 173 | case PAM_NEW_AUTHTOK_REQD: 174 | { 175 | buf->info_line = lang.err_pam_authok_reqd; 176 | break; 177 | } 178 | case PAM_PERM_DENIED: 179 | { 180 | buf->info_line = lang.err_pam_perm_denied; 181 | break; 182 | } 183 | case PAM_SESSION_ERR: 184 | { 185 | buf->info_line = lang.err_pam_session; 186 | break; 187 | } 188 | case PAM_SYSTEM_ERR: 189 | { 190 | buf->info_line = lang.err_pam_sys; 191 | break; 192 | } 193 | case PAM_USER_UNKNOWN: 194 | { 195 | buf->info_line = lang.err_pam_user_unknown; 196 | break; 197 | } 198 | case PAM_ABORT: 199 | default: 200 | { 201 | buf->info_line = lang.err_pam_abort; 202 | break; 203 | } 204 | } 205 | 206 | dgn_throw(DGN_PAM); 207 | } 208 | 209 | void env_init(struct passwd* pwd) 210 | { 211 | extern char** environ; 212 | char* term = getenv("TERM"); 213 | //char* lang = getenv("LANG"); // 0.6.0 214 | // clean env 215 | environ[0] = NULL; 216 | 217 | if (term != NULL) 218 | { 219 | setenv("TERM", term, 1); 220 | } 221 | else 222 | { 223 | setenv("TERM", "linux", 1); 224 | } 225 | 226 | setenv("HOME", pwd->pw_dir, 1); 227 | setenv("PWD", pwd->pw_dir, 1); 228 | setenv("SHELL", pwd->pw_shell, 1); 229 | setenv("USER", pwd->pw_name, 1); 230 | setenv("LOGNAME", pwd->pw_name, 1); 231 | //setenv("LANG", lang, 1); // 0.6.0 232 | 233 | // Set PATH if specified in the configuration 234 | if (strlen(config.path)) 235 | { 236 | int ok = setenv("PATH", config.path, 1); 237 | 238 | if (ok != 0) 239 | { 240 | dgn_throw(DGN_PATH); 241 | } 242 | } 243 | } 244 | 245 | void env_xdg_session(const enum display_server display_server) 246 | { 247 | switch (display_server) 248 | { 249 | case DS_WAYLAND: 250 | { 251 | setenv("XDG_SESSION_TYPE", "wayland", 1); 252 | break; 253 | } 254 | case DS_SHELL: 255 | { 256 | setenv("XDG_SESSION_TYPE", "tty", 0); 257 | break; 258 | } 259 | case DS_XINITRC: 260 | case DS_XORG: 261 | { 262 | setenv("XDG_SESSION_TYPE", "x11", 0); 263 | break; 264 | } 265 | } 266 | } 267 | 268 | void env_xdg(const char* tty_id) 269 | { 270 | char user[15]; 271 | snprintf(user, 15, "/run/user/%d", getuid()); 272 | setenv("XDG_RUNTIME_DIR", user, 0); 273 | setenv("XDG_SESSION_CLASS", "user", 0); 274 | setenv("XDG_SEAT", "seat0", 0); 275 | setenv("XDG_VTNR", tty_id, 0); 276 | setenv("XDG_SESSION_ID", "1", 0); // 0.6.0 277 | } 278 | 279 | void add_utmp_entry( 280 | struct utmp *entry, 281 | char *username, 282 | pid_t display_pid 283 | ) { 284 | entry->ut_type = USER_PROCESS; 285 | entry->ut_pid = display_pid; 286 | strcpy(entry->ut_line, ttyname(STDIN_FILENO) + strlen("/dev/")); 287 | 288 | /* only correct for ptys named /dev/tty[pqr][0-9a-z] */ 289 | strcpy(entry->ut_id, ttyname(STDIN_FILENO) + strlen("/dev/tty")); 290 | 291 | time((long int *) &entry->ut_time); 292 | 293 | strncpy(entry->ut_user, username, UT_NAMESIZE); 294 | memset(entry->ut_host, 0, UT_HOSTSIZE); 295 | entry->ut_addr = 0; 296 | setutent(); 297 | 298 | pututline(entry); 299 | } 300 | 301 | void remove_utmp_entry(struct utmp *entry) { 302 | entry->ut_type = DEAD_PROCESS; 303 | memset(entry->ut_line, 0, UT_LINESIZE); 304 | entry->ut_time = 0; 305 | memset(entry->ut_user, 0, UT_NAMESIZE); 306 | setutent(); 307 | pututline(entry); 308 | endutent(); 309 | } 310 | 311 | void xauth(const char* display_name, const char* shell, const char* dir) 312 | { 313 | char xauthority[256]; 314 | snprintf(xauthority, 256, "%s/%s", dir, ".lyxauth"); 315 | setenv("XAUTHORITY", xauthority, 1); 316 | setenv("DISPLAY", display_name, 1); 317 | 318 | FILE* fp = fopen(xauthority, "ab+"); 319 | 320 | if (fp != NULL) 321 | { 322 | fclose(fp); 323 | } 324 | 325 | pid_t pid = fork(); 326 | 327 | if (pid == 0) 328 | { 329 | char cmd[1024]; 330 | snprintf( 331 | cmd, 332 | 1024, 333 | "%s add %s . `%s`", 334 | config.xauth_cmd, 335 | display_name, 336 | config.mcookie_cmd); 337 | execl(shell, shell, "-c", cmd, NULL); 338 | exit(EXIT_SUCCESS); 339 | } 340 | 341 | int status; 342 | waitpid(pid, &status, 0); 343 | } 344 | 345 | void xorg( 346 | struct passwd* pwd, 347 | const char* vt, 348 | const char* desktop_cmd) 349 | { 350 | // generate xauthority file 351 | const char* xauth_dir = getenv("XDG_CONFIG_HOME"); 352 | 353 | if ((xauth_dir == NULL) || (*xauth_dir == '\0')) 354 | { 355 | xauth_dir = pwd->pw_dir; 356 | } 357 | 358 | char display_name[4]; 359 | 360 | snprintf(display_name, 3, ":%d", get_free_display()); 361 | xauth(display_name, pwd->pw_shell, xauth_dir); 362 | 363 | // start xorg 364 | pid_t pid = fork(); 365 | 366 | if (pid == 0) 367 | { 368 | char x_cmd[1024]; 369 | snprintf( 370 | x_cmd, 371 | 1024, 372 | "%s %s %s", 373 | config.x_cmd, 374 | display_name, 375 | vt); 376 | execl(pwd->pw_shell, pwd->pw_shell, "-c", x_cmd, NULL); 377 | exit(EXIT_SUCCESS); 378 | } 379 | 380 | int ok; 381 | xcb_connection_t* xcb; 382 | 383 | do 384 | { 385 | xcb = xcb_connect(NULL, NULL); 386 | ok = xcb_connection_has_error(xcb); 387 | kill(pid, 0); 388 | } 389 | while((ok != 0) && (errno != ESRCH)); 390 | 391 | if (ok != 0) 392 | { 393 | return; 394 | } 395 | 396 | pid_t xorg_pid = fork(); 397 | 398 | if (xorg_pid == 0) 399 | { 400 | char de_cmd[1024]; 401 | snprintf( 402 | de_cmd, 403 | 1024, 404 | "%s %s", 405 | config.x_cmd_setup, 406 | desktop_cmd); 407 | execl(pwd->pw_shell, pwd->pw_shell, "-c", de_cmd, NULL); 408 | exit(EXIT_SUCCESS); 409 | } 410 | 411 | int status; 412 | waitpid(xorg_pid, &status, 0); 413 | xcb_disconnect(xcb); 414 | kill(pid, 0); 415 | 416 | if (errno != ESRCH) 417 | { 418 | kill(pid, SIGTERM); 419 | waitpid(pid, &status, 0); 420 | } 421 | } 422 | 423 | void wayland( 424 | struct passwd* pwd, 425 | const char* desktop_cmd) 426 | { 427 | 428 | char cmd[1024]; 429 | snprintf(cmd, 1024, "%s %s", config.wayland_cmd, desktop_cmd); 430 | execl(pwd->pw_shell, pwd->pw_shell, "-c", cmd, NULL); 431 | } 432 | 433 | void shell(struct passwd* pwd) 434 | { 435 | const char* pos = strrchr(pwd->pw_shell, '/'); 436 | char args[1024]; 437 | args[0] = '-'; 438 | 439 | if (pos != NULL) 440 | { 441 | pos = pos + 1; 442 | } 443 | else 444 | { 445 | pos = pwd->pw_shell; 446 | } 447 | 448 | strncpy(args + 1, pos, 1023); 449 | execl(pwd->pw_shell, args, NULL); 450 | } 451 | 452 | // pam_do performs the pam action specified in pam_action 453 | // on pam_action fail, call diagnose and end pam session 454 | int pam_do( 455 | int (pam_action)(struct pam_handle *, int), 456 | struct pam_handle *handle, 457 | int flags, 458 | struct term_buf *buf) 459 | { 460 | int status = pam_action(handle, flags); 461 | 462 | if (status != PAM_SUCCESS) { 463 | pam_diagnose(status, buf); 464 | pam_end(handle, status); 465 | } 466 | 467 | return status; 468 | } 469 | 470 | void auth( 471 | struct desktop* desktop, 472 | struct text* login, 473 | struct text* password, 474 | struct term_buf* buf) 475 | { 476 | int ok; 477 | 478 | // open pam session 479 | const char* creds[2] = {login->text, password->text}; 480 | struct pam_conv conv = {login_conv, creds}; 481 | struct pam_handle* handle; 482 | 483 | ok = pam_start(config.service_name, NULL, &conv, &handle); 484 | 485 | // fix plank not working without properly setting XDG_SESSION_TYPE 486 | env_xdg_session(desktop->display_server[desktop->cur]); 487 | 488 | if (ok != PAM_SUCCESS) 489 | { 490 | pam_diagnose(ok, buf); 491 | pam_end(handle, ok); 492 | return; 493 | } 494 | 495 | ok = pam_do(pam_authenticate, handle, 0, buf); 496 | 497 | if (ok != PAM_SUCCESS) 498 | { 499 | return; 500 | } 501 | 502 | ok = pam_do(pam_acct_mgmt, handle, 0, buf); 503 | 504 | if (ok != PAM_SUCCESS) 505 | { 506 | return; 507 | } 508 | 509 | ok = pam_do(pam_setcred, handle, PAM_ESTABLISH_CRED, buf); 510 | 511 | if (ok != PAM_SUCCESS) 512 | { 513 | return; 514 | } 515 | 516 | ok = pam_do(pam_open_session, handle, 0, buf); 517 | 518 | if (ok != PAM_SUCCESS) 519 | { 520 | return; 521 | } 522 | 523 | // clear the credentials 524 | input_text_clear(password); 525 | 526 | // get passwd structure 527 | struct passwd* pwd = getpwnam(login->text); 528 | endpwent(); 529 | 530 | if (pwd == NULL) 531 | { 532 | dgn_throw(DGN_PWNAM); 533 | pam_end(handle, ok); 534 | return; 535 | } 536 | 537 | // set user shell 538 | if (pwd->pw_shell[0] == '\0') 539 | { 540 | setusershell(); 541 | 542 | char* shell = getusershell(); 543 | 544 | if (shell != NULL) 545 | { 546 | strcpy(pwd->pw_shell, shell); 547 | } 548 | 549 | endusershell(); 550 | } 551 | 552 | // restore regular terminal mode 553 | tb_clear(); 554 | tb_present(); 555 | tb_shutdown(); 556 | 557 | // start desktop environment 558 | pid_t pid = fork(); 559 | 560 | if (pid == 0) 561 | { 562 | // set user info 563 | ok = initgroups(pwd->pw_name, pwd->pw_gid); 564 | 565 | if (ok != 0) 566 | { 567 | dgn_throw(DGN_USER_INIT); 568 | exit(EXIT_FAILURE); 569 | } 570 | 571 | ok = setgid(pwd->pw_gid); 572 | 573 | if (ok != 0) 574 | { 575 | dgn_throw(DGN_USER_GID); 576 | exit(EXIT_FAILURE); 577 | } 578 | 579 | ok = setuid(pwd->pw_uid); 580 | 581 | if (ok != 0) 582 | { 583 | dgn_throw(DGN_USER_UID); 584 | exit(EXIT_FAILURE); 585 | } 586 | 587 | // get a display 588 | char tty_id [3]; 589 | char vt[5]; 590 | 591 | snprintf(tty_id, 3, "%d", config.tty); 592 | snprintf(vt, 5, "vt%d", config.tty); 593 | 594 | // set env 595 | env_init(pwd); 596 | 597 | if (dgn_catch()) 598 | { 599 | exit(EXIT_FAILURE); 600 | } 601 | 602 | // add pam variables 603 | char** env = pam_getenvlist(handle); 604 | 605 | for (uint16_t i = 0; env && env[i]; ++i) 606 | { 607 | putenv(env[i]); 608 | } 609 | 610 | // add xdg variables 611 | env_xdg(tty_id); 612 | 613 | // execute 614 | int ok = chdir(pwd->pw_dir); 615 | 616 | if (ok != 0) 617 | { 618 | dgn_throw(DGN_CHDIR); 619 | exit(EXIT_FAILURE); 620 | } 621 | 622 | reset_terminal(pwd); 623 | switch (desktop->display_server[desktop->cur]) 624 | { 625 | case DS_WAYLAND: 626 | { 627 | wayland(pwd, desktop->cmd[desktop->cur]); 628 | break; 629 | } 630 | case DS_SHELL: 631 | { 632 | shell(pwd); 633 | break; 634 | } 635 | case DS_XINITRC: 636 | case DS_XORG: 637 | { 638 | xorg(pwd, vt, desktop->cmd[desktop->cur]); 639 | break; 640 | } 641 | } 642 | 643 | exit(EXIT_SUCCESS); 644 | } 645 | 646 | // add utmp audit 647 | struct utmp entry; 648 | add_utmp_entry(&entry, pwd->pw_name, pid); 649 | 650 | // wait for the session to stop 651 | int status; 652 | waitpid(pid, &status, 0); 653 | remove_utmp_entry(&entry); 654 | 655 | reset_terminal(pwd); 656 | 657 | // reinit termbox 658 | tb_init(); 659 | tb_select_output_mode(TB_OUTPUT_NORMAL); 660 | 661 | // reload the desktop environment list on logout 662 | input_desktop_free(desktop); 663 | input_desktop(desktop); 664 | desktop_load(desktop); 665 | 666 | // close pam session 667 | ok = pam_do(pam_close_session, handle, 0, buf); 668 | 669 | if (ok != PAM_SUCCESS) 670 | { 671 | return; 672 | } 673 | 674 | ok = pam_do(pam_setcred, handle, PAM_DELETE_CRED, buf); 675 | 676 | if (ok != PAM_SUCCESS) 677 | { 678 | return; 679 | } 680 | 681 | ok = pam_end(handle, 0); 682 | 683 | if (ok != PAM_SUCCESS) 684 | { 685 | pam_diagnose(ok, buf); 686 | } 687 | } 688 | 689 | -------------------------------------------------------------------------------- /src/login.h: -------------------------------------------------------------------------------- 1 | #ifndef H_LY_LOGIN 2 | #define H_LY_LOGIN 3 | 4 | #include "draw.h" 5 | #include "inputs.h" 6 | 7 | void auth( 8 | struct desktop* desktop, 9 | struct text* login, 10 | struct text* password, 11 | struct term_buf* buf); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include "argoat.h" 2 | #include "configator.h" 3 | #include "dragonfail.h" 4 | #include "termbox.h" 5 | 6 | #include "draw.h" 7 | #include "inputs.h" 8 | #include "login.h" 9 | #include "utils.h" 10 | #include "config.h" 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #define ARG_COUNT 7 21 | #define GIT_VERSION_STRING "0.6.0" 22 | 23 | // global 24 | struct lang lang; 25 | struct config config; 26 | 27 | // args handles 28 | void arg_help(void* data, char** pars, const int pars_count) 29 | { 30 | printf("--config|-c - Use a custom config file.\n"); 31 | printf("--help|-h - Show this message.\n"); 32 | printf("--version|-v - Show the installed version of Ly.\n"); 33 | exit(0); 34 | } 35 | 36 | void arg_version(void* data, char** pars, const int pars_count) 37 | { 38 | #ifdef GIT_VERSION_STRING 39 | printf("Ly version %s\n", GIT_VERSION_STRING); 40 | #else 41 | printf("Ly version unknown\n"); 42 | #endif 43 | exit(0); 44 | } 45 | 46 | // low-level error messages 47 | void log_init(char** log) 48 | { 49 | log[DGN_OK] = lang.err_dgn_oob; 50 | log[DGN_NULL] = lang.err_null; 51 | log[DGN_ALLOC] = lang.err_alloc; 52 | log[DGN_BOUNDS] = lang.err_bounds; 53 | log[DGN_DOMAIN] = lang.err_domain; 54 | log[DGN_MLOCK] = lang.err_mlock; 55 | log[DGN_XSESSIONS_DIR] = lang.err_xsessions_dir; 56 | log[DGN_XSESSIONS_OPEN] = lang.err_xsessions_open; 57 | log[DGN_PATH] = lang.err_path; 58 | log[DGN_CHDIR] = lang.err_chdir; 59 | log[DGN_PWNAM] = lang.err_pwnam; 60 | log[DGN_USER_INIT] = lang.err_user_init; 61 | log[DGN_USER_GID] = lang.err_user_gid; 62 | log[DGN_USER_UID] = lang.err_user_uid; 63 | log[DGN_PAM] = lang.err_pam; 64 | log[DGN_HOSTNAME] = lang.err_hostname; 65 | } 66 | 67 | void arg_config(void* data, char** pars, const int pars_count) 68 | { 69 | *((char **)data) = *pars; 70 | } 71 | 72 | // ly! 73 | int main(int argc, char** argv) 74 | { 75 | // init error lib 76 | log_init(dgn_init()); 77 | 78 | // load config 79 | config_defaults(); 80 | lang_defaults(); 81 | 82 | char *config_path = NULL; 83 | // parse args 84 | const struct argoat_sprig sprigs[ARG_COUNT] = 85 | { 86 | {NULL, 0, NULL, NULL}, 87 | {"config", 0, &config_path, arg_config}, 88 | {"c", 0, &config_path, arg_config}, 89 | {"help", 0, NULL, arg_help}, 90 | {"h", 0, NULL, arg_help}, 91 | {"version", 0, NULL, arg_version}, 92 | {"v", 0, NULL, arg_version}, 93 | }; 94 | 95 | struct argoat args = {sprigs, ARG_COUNT, NULL, 0, 0}; 96 | argoat_graze(&args, argc, argv); 97 | 98 | // init inputs 99 | struct desktop desktop; 100 | struct text login; 101 | struct text password; 102 | input_desktop(&desktop); 103 | input_text(&login, config.max_login_len); 104 | input_text(&password, config.max_password_len); 105 | 106 | if (dgn_catch()) 107 | { 108 | config_free(); 109 | lang_free(); 110 | return 1; 111 | } 112 | 113 | config_load(config_path); 114 | 115 | if (strcmp(config.lang, "en") != 0) 116 | { 117 | lang_load(); 118 | } 119 | 120 | void* input_structs[3] = 121 | { 122 | (void*) &desktop, 123 | (void*) &login, 124 | (void*) &password, 125 | }; 126 | 127 | void (*input_handles[3]) (void*, struct tb_event*) = 128 | { 129 | handle_desktop, 130 | handle_text, 131 | handle_text, 132 | }; 133 | 134 | desktop_load(&desktop); 135 | load(&desktop, &login); 136 | 137 | // start termbox 138 | tb_init(); 139 | tb_select_output_mode(TB_OUTPUT_NORMAL); 140 | tb_clear(); 141 | 142 | // init visible elements 143 | struct tb_event event; 144 | struct term_buf buf; 145 | uint8_t active_input = config.default_input; 146 | 147 | (*input_handles[active_input])(input_structs[active_input], NULL); 148 | 149 | // init drawing stuff 150 | draw_init(&buf); 151 | 152 | if (config.animate) 153 | { 154 | animate_init(&buf); 155 | 156 | if (dgn_catch()) 157 | { 158 | config.animate = false; 159 | dgn_reset(); 160 | } 161 | } 162 | 163 | // init state info 164 | int error; 165 | bool run = true; 166 | bool update = true; 167 | bool reboot = false; 168 | bool shutdown = false; 169 | uint8_t auth_fails = 0; 170 | 171 | switch_tty(&buf); 172 | 173 | // main loop 174 | while (run) 175 | { 176 | if (update) 177 | { 178 | if (auth_fails < 10) 179 | { 180 | tb_clear(); 181 | animate(&buf); 182 | draw_box(&buf); 183 | draw_labels(&buf); 184 | if (!config.hide_f1_commands) 185 | draw_f_commands(); 186 | if (!config.hide_time) 187 | draw_time(&buf); 188 | draw_lock_state(&buf); 189 | position_input(&buf, &desktop, &login, &password); 190 | draw_desktop(&desktop); 191 | draw_input(&login); 192 | draw_input_mask(&password); 193 | update = config.animate; 194 | } 195 | else 196 | { 197 | usleep(10000); 198 | update = cascade(&buf, &auth_fails); 199 | } 200 | 201 | tb_present(); 202 | } 203 | 204 | error = tb_peek_event(&event, config.min_refresh_delta); 205 | 206 | if (error < 0) 207 | { 208 | continue; 209 | } 210 | 211 | if (event.type == TB_EVENT_KEY) 212 | { 213 | switch (event.key) 214 | { 215 | case TB_KEY_F1: 216 | shutdown = true; 217 | run = false; 218 | break; 219 | case TB_KEY_F2: 220 | reboot = true; 221 | run = false; 222 | break; 223 | case TB_KEY_CTRL_C: 224 | run = false; 225 | break; 226 | case TB_KEY_CTRL_U: 227 | if (active_input > 0) 228 | { 229 | input_text_clear(input_structs[active_input]); 230 | } 231 | break; 232 | case TB_KEY_ARROW_UP: 233 | if (active_input > 0) 234 | { 235 | --active_input; 236 | update = true; 237 | } 238 | break; 239 | case TB_KEY_ARROW_DOWN: 240 | if (active_input < 2) 241 | { 242 | ++active_input; 243 | update = true; 244 | } 245 | break; 246 | case TB_KEY_TAB: 247 | ++active_input; 248 | 249 | if (active_input > 2) 250 | { 251 | active_input = PASSWORD_INPUT; 252 | } 253 | update = true; 254 | break; 255 | case TB_KEY_ENTER: 256 | save(&desktop, &login); 257 | auth(&desktop, &login, &password, &buf); 258 | update = true; 259 | 260 | if (dgn_catch()) 261 | { 262 | ++auth_fails; 263 | // move focus back to password input 264 | active_input = PASSWORD_INPUT; 265 | 266 | if (dgn_output_code() != DGN_PAM) 267 | { 268 | buf.info_line = dgn_output_log(); 269 | } 270 | 271 | if (config.blank_password) 272 | { 273 | input_text_clear(&password); 274 | } 275 | 276 | dgn_reset(); 277 | } 278 | else 279 | { 280 | buf.info_line = lang.logout; 281 | } 282 | 283 | load(&desktop, &login); 284 | system("tput cnorm"); 285 | break; 286 | default: 287 | (*input_handles[active_input])( 288 | input_structs[active_input], 289 | &event); 290 | update = true; 291 | break; 292 | } 293 | } 294 | } 295 | 296 | // stop termbox 297 | tb_shutdown(); 298 | 299 | // free inputs 300 | input_desktop_free(&desktop); 301 | input_text_free(&login); 302 | input_text_free(&password); 303 | free_hostname(); 304 | 305 | // unload config 306 | draw_free(&buf); 307 | lang_free(); 308 | 309 | if (shutdown) 310 | { 311 | execl("/bin/sh", "sh", "-c", config.shutdown_cmd, NULL); 312 | } 313 | 314 | if (reboot) 315 | { 316 | execl("/bin/sh", "sh", "-c", config.restart_cmd, NULL); 317 | } 318 | 319 | config_free(); 320 | 321 | return 0; 322 | } 323 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | #include "configator.h" 2 | #include "dragonfail.h" 3 | 4 | #include "inputs.h" 5 | #include "config.h" 6 | #include "utils.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #if defined(__DragonFly__) || defined(__FreeBSD__) 18 | #include 19 | #else // linux 20 | #include 21 | #endif 22 | 23 | void desktop_crawl( 24 | struct desktop* target, 25 | char* sessions, 26 | enum display_server server) 27 | { 28 | DIR* dir; 29 | struct dirent* dir_info; 30 | int ok; 31 | 32 | ok = access(sessions, F_OK); 33 | 34 | if (ok == -1) 35 | { 36 | dgn_throw(DGN_XSESSIONS_DIR); 37 | return; 38 | } 39 | 40 | dir = opendir(sessions); 41 | 42 | if (dir == NULL) 43 | { 44 | dgn_throw(DGN_XSESSIONS_OPEN); 45 | return; 46 | } 47 | 48 | char* name = NULL; 49 | char* exec = NULL; 50 | 51 | struct configator_param map_desktop[] = 52 | { 53 | {"Exec", &exec, config_handle_str}, 54 | {"Name", &name, config_handle_str}, 55 | }; 56 | 57 | struct configator_param* map[] = 58 | { 59 | NULL, 60 | map_desktop, 61 | }; 62 | 63 | struct configator_param sections[] = 64 | { 65 | {"Desktop Entry", NULL, NULL}, 66 | }; 67 | 68 | uint16_t map_len[] = {0, 2}; 69 | uint16_t sections_len = 1; 70 | 71 | struct configator desktop_config; 72 | desktop_config.map = map; 73 | desktop_config.map_len = map_len; 74 | desktop_config.sections = sections; 75 | desktop_config.sections_len = sections_len; 76 | 77 | #if defined(NAME_MAX) 78 | char path[NAME_MAX]; 79 | #elif defined(_POSIX_PATH_MAX) 80 | char path[_POSIX_PATH_MAX]; 81 | #else 82 | char path[1024]; 83 | #endif 84 | 85 | dir_info = readdir(dir); 86 | 87 | while (dir_info != NULL) 88 | { 89 | if ((dir_info->d_name)[0] == '.') 90 | { 91 | dir_info = readdir(dir); 92 | continue; 93 | } 94 | 95 | snprintf(path, (sizeof (path)) - 1, "%s/", sessions); 96 | strncat(path, dir_info->d_name, (sizeof (path)) - 1); 97 | configator(&desktop_config, path); 98 | 99 | // if these are wayland sessions, add " (Wayland)" to their names, 100 | // as long as their names don't already contain that string 101 | if (server == DS_WAYLAND && config.wayland_specifier) 102 | { 103 | const char wayland_specifier[] = " (Wayland)"; 104 | if (strstr(name, wayland_specifier) == NULL) 105 | { 106 | name = realloc(name, (strlen(name) + sizeof(wayland_specifier) + 1)); 107 | // using strcat is safe because the string is constant 108 | strcat(name, wayland_specifier); 109 | } 110 | } 111 | 112 | if ((name != NULL) && (exec != NULL)) 113 | { 114 | input_desktop_add(target, name, exec, server); 115 | } 116 | 117 | name = NULL; 118 | exec = NULL; 119 | dir_info = readdir(dir); 120 | } 121 | 122 | closedir(dir); 123 | } 124 | 125 | void desktop_load(struct desktop* target) 126 | { 127 | // we don't care about desktop environments presence 128 | // because the fallback shell is always available 129 | // so we just dismiss any "throw" for now 130 | int err = 0; 131 | 132 | desktop_crawl(target, config.waylandsessions, DS_WAYLAND); 133 | 134 | if (dgn_catch()) 135 | { 136 | ++err; 137 | dgn_reset(); 138 | } 139 | 140 | desktop_crawl(target, config.xsessions, DS_XORG); 141 | 142 | if (dgn_catch()) 143 | { 144 | ++err; 145 | dgn_reset(); 146 | } 147 | } 148 | 149 | static char* hostname_backup = NULL; 150 | 151 | void hostname(char** out) 152 | { 153 | if (hostname_backup != NULL) 154 | { 155 | *out = hostname_backup; 156 | return; 157 | } 158 | 159 | int maxlen = sysconf(_SC_HOST_NAME_MAX); 160 | 161 | if (maxlen < 0) 162 | { 163 | maxlen = _POSIX_HOST_NAME_MAX; 164 | } 165 | 166 | hostname_backup = malloc(maxlen + 1); 167 | 168 | if (hostname_backup == NULL) 169 | { 170 | dgn_throw(DGN_ALLOC); 171 | return; 172 | } 173 | 174 | if (gethostname(hostname_backup, maxlen) < 0) 175 | { 176 | dgn_throw(DGN_HOSTNAME); 177 | return; 178 | } 179 | 180 | hostname_backup[maxlen] = '\0'; 181 | *out = hostname_backup; 182 | } 183 | 184 | void free_hostname() 185 | { 186 | free(hostname_backup); 187 | } 188 | 189 | void switch_tty(struct term_buf* buf) 190 | { 191 | FILE* console = fopen(config.console_dev, "w"); 192 | 193 | if (console == NULL) 194 | { 195 | buf->info_line = lang.err_console_dev; 196 | return; 197 | } 198 | 199 | int fd = fileno(console); 200 | 201 | ioctl(fd, VT_ACTIVATE, config.tty); 202 | ioctl(fd, VT_WAITACTIVE, config.tty); 203 | 204 | fclose(console); 205 | } 206 | 207 | void save(struct desktop* desktop, struct text* login) 208 | { 209 | if (config.save) 210 | { 211 | FILE* fp = fopen(config.save_file, "wb+"); 212 | 213 | if (fp != NULL) 214 | { 215 | fprintf(fp, "%s\n%d", login->text, desktop->cur); 216 | fclose(fp); 217 | } 218 | } 219 | } 220 | 221 | void load(struct desktop* desktop, struct text* login) 222 | { 223 | if (!config.load) 224 | { 225 | return; 226 | } 227 | 228 | FILE* fp = fopen(config.save_file, "rb"); 229 | 230 | if (fp == NULL) 231 | { 232 | return; 233 | } 234 | 235 | char* line = malloc(config.max_login_len + 1); 236 | 237 | if (line == NULL) 238 | { 239 | fclose(fp); 240 | return; 241 | } 242 | 243 | if (fgets(line, config.max_login_len + 1, fp)) 244 | { 245 | int len = strlen(line); 246 | strncpy(login->text, line, login->len); 247 | 248 | if (len == 0) 249 | { 250 | login->end = login->text; 251 | } 252 | else 253 | { 254 | login->end = login->text + len - 1; 255 | login->text[len - 1] = '\0'; 256 | } 257 | } 258 | else 259 | { 260 | fclose(fp); 261 | free(line); 262 | return; 263 | } 264 | 265 | if (fgets(line, config.max_login_len + 1, fp)) 266 | { 267 | int saved_cur = abs(atoi(line)); 268 | 269 | if (saved_cur < desktop->len) 270 | { 271 | desktop->cur = saved_cur; 272 | } 273 | } 274 | 275 | fclose(fp); 276 | free(line); 277 | } 278 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef H_LY_UTILS 2 | #define H_LY_UTILS 3 | 4 | #include "draw.h" 5 | #include "inputs.h" 6 | #include "config.h" 7 | 8 | void desktop_load(struct desktop* target); 9 | void hostname(char** out); 10 | void free_hostname(); 11 | void switch_tty(struct term_buf* buf); 12 | void save(struct desktop* desktop, struct text* login); 13 | void load(struct desktop* desktop, struct text* login); 14 | 15 | #endif 16 | --------------------------------------------------------------------------------