mirror of https://github.com/fairyglade/ly.git
initial commit
This commit is contained in:
commit
7870f668f3
|
|
@ -0,0 +1,13 @@
|
||||||
|
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
Version 2, December 2004
|
||||||
|
|
||||||
|
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
copies of this license document, and changing it is allowed as long
|
||||||
|
as the name is changed.
|
||||||
|
|
||||||
|
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
### Ly - ncurses display manager - v0.0.1
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
This is a lightweight login/display manager for linux made with ncurses.
|
||||||
|
It is based on linux-pam and systemd and was developped in good old C99.
|
||||||
|
This program was designed to be simple, modular and highly configurable.
|
||||||
|
Ly was made for crazy people. If you're not insane, run away.
|
||||||
|
|
||||||
|
### Configuration and installation
|
||||||
|
Ly tries not to reinvent the wheel and uses linux-utils and xorg-xinit
|
||||||
|
instead of providing heavy, incomplete and outdated implementations.
|
||||||
|
Make sure all the following tools and libraries are available on your
|
||||||
|
distribution before going further:
|
||||||
|
- systemd
|
||||||
|
- linux-pam
|
||||||
|
- ncurses
|
||||||
|
- tput
|
||||||
|
- libX11 (required if you wish to use X)
|
||||||
|
- xorg-xinit (required if you wish to use X)
|
||||||
|
- xorg-xauth (required if you wish to use X)
|
||||||
|
- mcookie (required if you wish to use X)
|
||||||
|
|
||||||
|
You can now configure Ly by editing "src/config.h". Modify "ly.service"
|
||||||
|
as well if you changed the default tty, then build the executable:
|
||||||
|
```
|
||||||
|
make
|
||||||
|
```
|
||||||
|
Check if it works on the tty you configured (default is tty2). You can
|
||||||
|
also run it in terminal emulators, but desktop environments won't start.
|
||||||
|
```
|
||||||
|
sudo build/ly
|
||||||
|
```
|
||||||
|
Then, install Ly and the systemd service file:
|
||||||
|
```
|
||||||
|
sudo make install
|
||||||
|
```
|
||||||
|
Now enable the systemd service to make it spawn on startup:
|
||||||
|
```
|
||||||
|
sudo systemctl enable ly.service
|
||||||
|
```
|
||||||
|
If you need to switch between ttys after Ly's start you also have to
|
||||||
|
disable getty on Ly's tty to prevent "login" from spawning on top of it:
|
||||||
|
```
|
||||||
|
sudo systemctl disable getty@tty2.service
|
||||||
|
```
|
||||||
|
|
||||||
|
### Additionnal informations
|
||||||
|
The name "Ly" is a tribute to the fairy from the game Rayman.
|
||||||
|
Ly was tested by oxodao, who is some seriously awesome dude. (you rock!)
|
||||||
|
I wish to thank ncurses, linux-pam, X11 and systemd developers for not
|
||||||
|
providing anything close to a reference or documentation.
|
||||||
|
|
||||||
|
▌
|
||||||
|
▌▐
|
||||||
|
█▄▄ ▐ █ █▄▄ █▄▄ █▄▄ ▄▄█ ▄▄█▌ ▄▄▄▄
|
||||||
|
▐████▄█ █▌ ▐████▄ ▐████▄ ▐████▄ ▄████ ▄█████ █████▀
|
||||||
|
▐██████ ▐█ ▐█████▌ ▐█████▌ ▐█████▌ ▐█████▌ █████▌ █████
|
||||||
|
████▐██ █▌█████▀ ████ ▀ ████▐██ ██▐████ ▐█████ ▐████▌
|
||||||
|
███▌▐██ ▐█████▌ ███▌ ███▌▐██ ██ ████ ████▌▌██▐██▌
|
||||||
|
███▌ ██ ▐ █████▌ ▐███▌ ███▌▐██ ██ ████ ████ ▐█ ▐██▌
|
||||||
|
███▌ █ ▐████ ▐███▌ █ ███▌▄▄▄ ██ ████ ████ ▐▌ ███▌
|
||||||
|
▐██▌ ▄██▌▐████ ████▌ ██ ▐██▌▐██ ██ ███▌ ███▌ ▐ ▐████
|
||||||
|
▐███▄████▌ ████ ▐████████ ▐███▐██ ██▐███▌ ▐███▌ ▐████▌
|
||||||
|
█████▀ ▐▌ ████ █████████▄ ▐████▀ ▀████▌ ▐██▌ ████▌
|
||||||
|
▀▀▀ ▌ ████ ▐██▀▀ ▀▀ ▄█▀▀ ▀▀█▄ ███ ▀███
|
||||||
|
▐ ████ ▄█▀ ▄ ▐█ █▌▄██ ▀▀▄
|
||||||
|
▌ █████▀ █ ▀█▄▄ ▄▄█▀▄█▀
|
||||||
|
▄▄▄▀ ▐███▀ ▐██▄ ▀▀▀▀▀▀▀▀▀ ▄█▀
|
||||||
|
█▀ ▐█▀ ▀████▄▄▄▄▄▄▄▄▄▄▄▄▄█▀▀
|
||||||
|
█▄ ▀ ▀▀▀▀▀▀▀▀▀▀▀ ▄▄▄▄████████████▄▄
|
||||||
|
▀▀██▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄████████▀▀▀▀▀▀ ▀▀▀▄
|
||||||
|
▀▀▀▀▀▀▀▀▀▀▀▀ ▄▀
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
[Unit]
|
||||||
|
Description=ly ncurses display manager
|
||||||
|
After=systemd-user-sessions.service plymouth-quit-wait.service
|
||||||
|
After=getty@tty2.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/bin/ly
|
||||||
|
StandardInput=tty
|
||||||
|
TTYPath=/dev/tty2
|
||||||
|
TTYReset=yes
|
||||||
|
TTYVHangup=yes
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
Alias=display-manager.service
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
ly :
|
||||||
|
mkdir -p ./build
|
||||||
|
cc -std=c99 -pedantic -Wall -I src -L/usr/lib/security -lform -lncurses -lpam -lpam_misc -lX11 -l:pam_loginuid.so -o build/ly src/main.c src/utils.c src/login.c src/ncui.c src/desktop.c
|
||||||
|
|
||||||
|
install : ly
|
||||||
|
cp build/ly /bin/ly
|
||||||
|
mkdir -p /etc/ly
|
||||||
|
cp ly.service /lib/systemd/system/ly.service
|
||||||
|
ln -sf /usr/lib/security/pam_loginuid.so /lib/pam_loginuid.so
|
||||||
|
|
||||||
|
all : install
|
||||||
|
|
||||||
|
clean :
|
||||||
|
rm -rf build/ly
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
#ifndef _CONFIG_H_
|
||||||
|
#define _CONFIG_H_
|
||||||
|
|
||||||
|
/* UI */
|
||||||
|
#define LY_MARGIN_H 3
|
||||||
|
#define LY_MARGIN_V 2
|
||||||
|
|
||||||
|
/* array sizes */
|
||||||
|
#define LY_LIM_LINE_FILE 256
|
||||||
|
#define LY_LIM_LINE_CONSOLE 256
|
||||||
|
#define LY_LIM_PATH 256
|
||||||
|
#define LY_LIM_CMD 256
|
||||||
|
|
||||||
|
/* behaviour */
|
||||||
|
#define LY_CFG_SAVE "/etc/ly/ly.save"
|
||||||
|
#define LY_CFG_READ_SAVE 1
|
||||||
|
#define LY_CFG_WRITE_SAVE 1
|
||||||
|
#define LY_CFG_CLR_USR 0
|
||||||
|
/* 0-10 */
|
||||||
|
#define LY_CFG_FCHANCE 7
|
||||||
|
#define LY_CFG_AUTH_TRIG 10
|
||||||
|
#define LY_CFG_FPS 60
|
||||||
|
#define LY_CFG_FMAX 100
|
||||||
|
|
||||||
|
/* commands */
|
||||||
|
#define LY_CMD_X "/usr/bin/X"
|
||||||
|
#define LY_CMD_TPUT "/usr/bin/tput"
|
||||||
|
#define LY_CMD_HALT "/sbin/shutdown"
|
||||||
|
#define LY_CMD_XINITRC "exec /bin/bash --login ~/.xinitrc "
|
||||||
|
#define LY_CMD_MCOOKIE "/usr/bin/mcookie"
|
||||||
|
#define LY_XAUTHORITY ".lyxauth"
|
||||||
|
|
||||||
|
/* paths */
|
||||||
|
#define LY_PATH "/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/env"
|
||||||
|
#define LY_PATH_XSESSIONS "/usr/share/xsessions"
|
||||||
|
|
||||||
|
/* console */
|
||||||
|
#define LY_CONSOLE_DEV "/dev/console"
|
||||||
|
#define LY_CONSOLE_TERM "TERM=linux"
|
||||||
|
#define LY_CONSOLE_TTY 2
|
||||||
|
|
||||||
|
/* pam breaks if you don't set the service name at "login" */
|
||||||
|
#define LY_SERVICE_NAME "login"
|
||||||
|
|
||||||
|
#endif /* _CONFIG_H_ */
|
||||||
|
|
@ -0,0 +1,148 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "lang.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "desktop.h"
|
||||||
|
|
||||||
|
#define LY_XSESSION_EXEC "Exec="
|
||||||
|
#define LY_XSESSION_NAME "Name="
|
||||||
|
|
||||||
|
/* returns a list containing all the DE for all the display servers */
|
||||||
|
struct delist_t* list_de(void)
|
||||||
|
{
|
||||||
|
/* xsession */
|
||||||
|
FILE* file;
|
||||||
|
DIR* dir;
|
||||||
|
struct dirent* dir_info;
|
||||||
|
/* buffers */
|
||||||
|
char path[LY_LIM_PATH];
|
||||||
|
char* name;
|
||||||
|
char* command;
|
||||||
|
/* de list */
|
||||||
|
int count = 2;
|
||||||
|
struct delist_t* list = init_list(count);
|
||||||
|
/* reads xorg's desktop environments entries */
|
||||||
|
dir = opendir(LY_PATH_XSESSIONS);
|
||||||
|
|
||||||
|
/* exits if the folder can't be read */
|
||||||
|
if(!dir)
|
||||||
|
{
|
||||||
|
error_print(LY_ERR_DELIST);
|
||||||
|
end_list(list, count);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* cycles through the folder */
|
||||||
|
while((dir_info = readdir(dir)))
|
||||||
|
{
|
||||||
|
/* gets rid of ".", ".." and ".*" files */
|
||||||
|
if((dir_info->d_name)[0] == '.')
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* opens xsession file */
|
||||||
|
snprintf(path, sizeof(path), "%s/%s", LY_PATH_XSESSIONS,
|
||||||
|
dir_info->d_name);
|
||||||
|
file = fopen(path, "r");
|
||||||
|
|
||||||
|
/* stops the entire procedure if the file can't be read */
|
||||||
|
if(!file)
|
||||||
|
{
|
||||||
|
error_print(LY_ERR_DELIST);
|
||||||
|
closedir(dir);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* reads xsession file */
|
||||||
|
name = NULL;
|
||||||
|
command = NULL;
|
||||||
|
get_props(file, &name, &command);
|
||||||
|
|
||||||
|
/* frees memory when the entries are incomplete */
|
||||||
|
if((name && !command) || (!name && command))
|
||||||
|
{
|
||||||
|
free(name ? name : command);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* adds the new entry to the list */
|
||||||
|
list->names = realloc(list->names,
|
||||||
|
(count + 2) * (sizeof * (list->names)));
|
||||||
|
list->names[count] = name;
|
||||||
|
list->props = realloc(list->props,
|
||||||
|
(count + 1) * (sizeof * (list->props)));
|
||||||
|
list->props[count].cmd = command;
|
||||||
|
list->props[count].type = xorg;
|
||||||
|
++count;
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(dir);
|
||||||
|
end_list(list, count);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* writes default entries to the DE list */
|
||||||
|
struct delist_t* init_list(int count)
|
||||||
|
{
|
||||||
|
struct delist_t* list = malloc(sizeof * list);
|
||||||
|
list->names = malloc((count + 1) * (sizeof * (list->names)));
|
||||||
|
list->names[0] = strdup(LY_LANG_SHELL);
|
||||||
|
list->names[1] = strdup(LY_LANG_XINITRC);
|
||||||
|
list->props = malloc(count * (sizeof * (list->props)));
|
||||||
|
list->props[0].cmd = strdup("");
|
||||||
|
list->props[0].type = shell;
|
||||||
|
list->props[1].cmd = strdup(LY_CMD_XINITRC);
|
||||||
|
list->props[1].type = xorg;
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
void end_list(struct delist_t* list, int count)
|
||||||
|
{
|
||||||
|
list->names[count] = NULL;
|
||||||
|
list->count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* extracts the name and command of a DE from its .desktop file */
|
||||||
|
void get_props(FILE* file, char** name, char** command)
|
||||||
|
{
|
||||||
|
char line[LY_LIM_LINE_FILE];
|
||||||
|
|
||||||
|
while(fgets(line, sizeof(line), file))
|
||||||
|
{
|
||||||
|
if(!strncmp(LY_XSESSION_NAME, line, (sizeof(LY_XSESSION_NAME) - 1)))
|
||||||
|
{
|
||||||
|
*name = strdup(trim(line + (sizeof(LY_XSESSION_NAME) - 1)));
|
||||||
|
}
|
||||||
|
else if(!strncmp(LY_XSESSION_EXEC, line,
|
||||||
|
(sizeof(LY_XSESSION_EXEC) - 1)))
|
||||||
|
{
|
||||||
|
*command = strdup(trim(line + (sizeof(LY_XSESSION_EXEC) - 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(*name && *command)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_list(struct delist_t* list)
|
||||||
|
{
|
||||||
|
int count;
|
||||||
|
|
||||||
|
for(count = 0; count < list->count; ++count)
|
||||||
|
{
|
||||||
|
free(list->names[count]);
|
||||||
|
free(list->props[count].cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(list->names);
|
||||||
|
free(list->props);
|
||||||
|
free(list);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef _DESKTOP_H_
|
||||||
|
#define _DESKTOP_H_
|
||||||
|
|
||||||
|
enum deserv_t {shell, xorg, wayland};
|
||||||
|
|
||||||
|
struct deprops_t
|
||||||
|
{
|
||||||
|
char* cmd;
|
||||||
|
enum deserv_t type;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct delist_t
|
||||||
|
{
|
||||||
|
char** names;
|
||||||
|
struct deprops_t* props;
|
||||||
|
int count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct delist_t* init_list(int count);
|
||||||
|
void end_list(struct delist_t* list, int count);
|
||||||
|
void get_props(FILE* file, char** name, char** command);
|
||||||
|
struct delist_t* list_de(void);
|
||||||
|
void free_list(struct delist_t* list);
|
||||||
|
|
||||||
|
#endif /* _DESKTOP_H_ */
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
#ifndef _LANG_H_
|
||||||
|
#define _LANG_H_
|
||||||
|
|
||||||
|
/* UI strings */
|
||||||
|
#define LY_LANG_GREETING "Welcome to ly !"
|
||||||
|
#define LY_LANG_VALID_CREDS "Logged In"
|
||||||
|
#define LY_LANG_LOGOUT "Logged out"
|
||||||
|
#define LY_LANG_SHELL "shell"
|
||||||
|
#define LY_LANG_XINITRC "xinitrc"
|
||||||
|
#define LY_LANG_SHUTDOWN "shutdown"
|
||||||
|
#define LY_LANG_REBOOT "reboot"
|
||||||
|
#define LY_LANG_LOGIN "login : "
|
||||||
|
#define LY_LANG_PASSWORD "password : "
|
||||||
|
|
||||||
|
/* ioctl */
|
||||||
|
#define LY_ERR_FD "Failed to create the console file descriptor"
|
||||||
|
#define LY_ERR_FD_ADVICE "(ly probably wasn't run with enough privileges)"
|
||||||
|
|
||||||
|
/* pam */
|
||||||
|
#define LY_ERR_PAM_BUF "Memory buffer error"
|
||||||
|
#define LY_ERR_PAM_SYSTEM "System error"
|
||||||
|
#define LY_ERR_PAM_ABORT "Pam transaction aborted"
|
||||||
|
#define LY_ERR_PAM_AUTH "Authentication error"
|
||||||
|
#define LY_ERR_PAM_CRED_INSUFFICIENT "Insufficient credentials"
|
||||||
|
#define LY_ERR_PAM_AUTHINFO_UNAVAIL "Failed to get user info"
|
||||||
|
#define LY_ERR_PAM_MAXTRIES "Reached maximum tries limit"
|
||||||
|
#define LY_ERR_PAM_USER_UNKNOWN "Unknown user"
|
||||||
|
#define LY_ERR_PAM_ACCT_EXPIRED "Account expired"
|
||||||
|
#define LY_ERR_PAM_NEW_AUTHTOK_REQD "Token expired"
|
||||||
|
#define LY_ERR_PAM_PERM_DENIED "Permission denied"
|
||||||
|
#define LY_ERR_PAM_CRED "Failed to set credentials"
|
||||||
|
#define LY_ERR_PAM_CRED_EXPIRED "Credentials expired"
|
||||||
|
#define LY_ERR_PAM_CRED_UNAVAIL "Failed to get credentials"
|
||||||
|
#define LY_ERR_PAM_SESSION "Session error"
|
||||||
|
#define LY_ERR_PAM_SET_TTY "Failed to set tty for pam"
|
||||||
|
#define LY_ERR_PAM_SET_RUSER "Failed to set ruser for pam"
|
||||||
|
|
||||||
|
/* ncurses */
|
||||||
|
#define LY_ERR_NC_BUFFER "Failed to refresh ncurses buffer"
|
||||||
|
|
||||||
|
/* de listing */
|
||||||
|
#define LY_ERR_DELIST "Failed to open xsessions"
|
||||||
|
|
||||||
|
/* permissions */
|
||||||
|
#define LY_ERR_PERM_GROUP "Failed to downgrade group permissions"
|
||||||
|
#define LY_ERR_PERM_USER "Failed to downgrade user permissions"
|
||||||
|
#define LY_ERR_PERM_DIR "Failed to change current directory"
|
||||||
|
|
||||||
|
#endif /* _LANG_H_ */
|
||||||
|
|
@ -0,0 +1,588 @@
|
||||||
|
#define _XOPEN_SOURCE 700
|
||||||
|
#define _DEFAULT_SOURCE
|
||||||
|
|
||||||
|
/* std lib */
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
/* linux */
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <paths.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <linux/limits.h>
|
||||||
|
#include <grp.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
/* ncurses */
|
||||||
|
#include <form.h>
|
||||||
|
/* pam */
|
||||||
|
#include <security/pam_appl.h>
|
||||||
|
#include <security/pam_misc.h>
|
||||||
|
#define PAM_SYSTEMD
|
||||||
|
#include <security/pam_modules.h>
|
||||||
|
#include <security/pam_modutil.h>
|
||||||
|
/* ly */
|
||||||
|
#include "lang.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "login.h"
|
||||||
|
#include "desktop.h"
|
||||||
|
|
||||||
|
int login_conv(int num_msg, const struct pam_message** msg,
|
||||||
|
struct pam_response** resp, void* appdata_ptr)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int result = PAM_SUCCESS;
|
||||||
|
|
||||||
|
if(!(*resp = calloc(num_msg, sizeof(struct pam_response))))
|
||||||
|
{
|
||||||
|
return PAM_BUF_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 0; i < num_msg; i++)
|
||||||
|
{
|
||||||
|
char* username, *password;
|
||||||
|
|
||||||
|
switch(msg[i]->msg_style)
|
||||||
|
{
|
||||||
|
case PAM_PROMPT_ECHO_ON:
|
||||||
|
username = ((char**) appdata_ptr)[0];
|
||||||
|
(*resp)[i].resp = strdup(username);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAM_PROMPT_ECHO_OFF:
|
||||||
|
password = ((char**) appdata_ptr)[1];
|
||||||
|
(*resp)[i].resp = strdup(password);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAM_ERROR_MSG:
|
||||||
|
fprintf(stderr, "%s\n", msg[i]->msg);
|
||||||
|
result = PAM_CONV_ERR;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAM_TEXT_INFO:
|
||||||
|
printf("%s\n", msg[i]->msg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(result != PAM_SUCCESS)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(result != PAM_SUCCESS)
|
||||||
|
{
|
||||||
|
free(*resp);
|
||||||
|
*resp = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int start_env(const char* username, const char* password,
|
||||||
|
const char* de_command, enum deserv_t display_server)
|
||||||
|
{
|
||||||
|
pid_t pid_display;
|
||||||
|
int display_status;
|
||||||
|
/* login info */
|
||||||
|
int pam_result;
|
||||||
|
const char* creds[2] = {username, password};
|
||||||
|
struct pam_conv conv = {login_conv, creds};
|
||||||
|
struct passwd* pwd = NULL;
|
||||||
|
pam_handle_t* login_handle;
|
||||||
|
/* session info */
|
||||||
|
int display_id;
|
||||||
|
char display_name[3];
|
||||||
|
char tty_id[3];
|
||||||
|
char vt[5];
|
||||||
|
/* generates console and display id and updates the environment */
|
||||||
|
destroy_env();
|
||||||
|
display_id = get_free_display();
|
||||||
|
snprintf(display_name, sizeof(display_name), ":%d", display_id);
|
||||||
|
snprintf(tty_id, sizeof(tty_id), "%d", LY_CONSOLE_TTY);
|
||||||
|
snprintf(vt, sizeof(vt), "vt%d", LY_CONSOLE_TTY);
|
||||||
|
init_xdg(tty_id, display_name, display_server);
|
||||||
|
/* pam_start and error handling */
|
||||||
|
pam_result = pam_start(LY_SERVICE_NAME, username, &conv, &login_handle);
|
||||||
|
|
||||||
|
if(pam_result != PAM_SUCCESS)
|
||||||
|
{
|
||||||
|
switch(pam_result)
|
||||||
|
{
|
||||||
|
case PAM_BUF_ERR :
|
||||||
|
error_print(LY_ERR_PAM_BUF);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAM_SYSTEM_ERR :
|
||||||
|
error_print(LY_ERR_PAM_SYSTEM);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAM_ABORT :
|
||||||
|
default:
|
||||||
|
error_print(LY_ERR_PAM_ABORT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pam_end(login_handle, pam_result);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pam_authenticate and error handling */
|
||||||
|
pam_result = pam_authenticate(login_handle, 0);
|
||||||
|
|
||||||
|
if(pam_result != PAM_SUCCESS)
|
||||||
|
{
|
||||||
|
switch(pam_result)
|
||||||
|
{
|
||||||
|
case PAM_AUTH_ERR :
|
||||||
|
error_print(LY_ERR_PAM_AUTH);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAM_CRED_INSUFFICIENT :
|
||||||
|
error_print(LY_ERR_PAM_CRED_INSUFFICIENT);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAM_AUTHINFO_UNAVAIL :
|
||||||
|
error_print(LY_ERR_PAM_AUTHINFO_UNAVAIL);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAM_MAXTRIES :
|
||||||
|
error_print(LY_ERR_PAM_MAXTRIES);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAM_USER_UNKNOWN :
|
||||||
|
error_print(LY_ERR_PAM_USER_UNKNOWN);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAM_ABORT :
|
||||||
|
default:
|
||||||
|
error_print(LY_ERR_PAM_ABORT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pam_end(login_handle, pam_result);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pam_acct_mgmt and error handling */
|
||||||
|
pam_result = pam_acct_mgmt(login_handle, 0);
|
||||||
|
|
||||||
|
if(pam_result != PAM_SUCCESS)
|
||||||
|
{
|
||||||
|
switch(pam_result)
|
||||||
|
{
|
||||||
|
case PAM_ACCT_EXPIRED :
|
||||||
|
error_print(LY_ERR_PAM_ACCT_EXPIRED);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAM_AUTH_ERR :
|
||||||
|
error_print(LY_ERR_PAM_AUTH);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAM_NEW_AUTHTOK_REQD :
|
||||||
|
error_print(LY_ERR_PAM_NEW_AUTHTOK_REQD);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAM_PERM_DENIED :
|
||||||
|
error_print(LY_ERR_PAM_PERM_DENIED);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAM_USER_UNKNOWN :
|
||||||
|
error_print(LY_ERR_PAM_USER_UNKNOWN);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
error_print(LY_ERR_PAM_ABORT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pam_end(login_handle, pam_result);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pam_setcred and error handling */
|
||||||
|
pam_result = pam_setcred(login_handle, PAM_ESTABLISH_CRED);
|
||||||
|
|
||||||
|
if(pam_result != PAM_SUCCESS)
|
||||||
|
{
|
||||||
|
switch(pam_result)
|
||||||
|
{
|
||||||
|
case PAM_BUF_ERR :
|
||||||
|
error_print(LY_ERR_PAM_BUF);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAM_CRED_ERR :
|
||||||
|
error_print(LY_ERR_PAM_CRED);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAM_CRED_EXPIRED :
|
||||||
|
error_print(LY_ERR_PAM_CRED_EXPIRED);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAM_CRED_UNAVAIL :
|
||||||
|
error_print(LY_ERR_PAM_CRED_UNAVAIL);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAM_SYSTEM_ERR :
|
||||||
|
error_print(LY_ERR_PAM_SYSTEM);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAM_USER_UNKNOWN :
|
||||||
|
error_print(LY_ERR_PAM_USER_UNKNOWN);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
error_print(LY_ERR_PAM_ABORT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pam_end(login_handle, pam_result);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pam_open_session and error handling */
|
||||||
|
pam_result = pam_open_session(login_handle, 0);
|
||||||
|
|
||||||
|
if(pam_result != PAM_SUCCESS)
|
||||||
|
{
|
||||||
|
pam_setcred(login_handle, PAM_DELETE_CRED);
|
||||||
|
|
||||||
|
switch(pam_result)
|
||||||
|
{
|
||||||
|
case PAM_BUF_ERR :
|
||||||
|
error_print(LY_ERR_PAM_BUF);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAM_CRED_ERR :
|
||||||
|
error_print(LY_ERR_PAM_CRED);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAM_CRED_EXPIRED :
|
||||||
|
error_print(LY_ERR_PAM_CRED_EXPIRED);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAM_CRED_UNAVAIL :
|
||||||
|
error_print(LY_ERR_PAM_CRED_UNAVAIL);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAM_SYSTEM_ERR :
|
||||||
|
error_print(LY_ERR_PAM_SYSTEM);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAM_USER_UNKNOWN :
|
||||||
|
error_print(LY_ERR_PAM_USER_UNKNOWN);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
error_print(LY_ERR_PAM_ABORT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pam_end(login_handle, pam_result);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* login error */
|
||||||
|
if(login_handle == NULL)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* temporarily exits ncurses mode */
|
||||||
|
def_prog_mode();
|
||||||
|
endwin();
|
||||||
|
pwd = getpwnam(username);
|
||||||
|
/* launches the DE */
|
||||||
|
pid_display = fork();
|
||||||
|
|
||||||
|
if(pid_display == 0)
|
||||||
|
{
|
||||||
|
/* downgrades group permissions and checks for an error */
|
||||||
|
if(setgid(pwd->pw_gid) < 0)
|
||||||
|
{
|
||||||
|
error_print(LY_ERR_PERM_GROUP);
|
||||||
|
pam_end(login_handle, pam_result);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* initializes environment variables */
|
||||||
|
init_env(login_handle, pwd);
|
||||||
|
|
||||||
|
/* downgrades user permissions and checks for an error */
|
||||||
|
if(setuid(pwd->pw_uid) < 0)
|
||||||
|
{
|
||||||
|
error_print(LY_ERR_PERM_USER);
|
||||||
|
pam_end(login_handle, pam_result);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* changes directory and checks for an error */
|
||||||
|
if(chdir(pwd->pw_dir) < 0)
|
||||||
|
{
|
||||||
|
error_print(LY_ERR_PERM_DIR);
|
||||||
|
pam_end(login_handle, pam_result);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* starts the chosen environment */
|
||||||
|
switch(display_server)
|
||||||
|
{
|
||||||
|
case shell:
|
||||||
|
launch_shell(pwd, login_handle);
|
||||||
|
|
||||||
|
case wayland:
|
||||||
|
launch_wayland(pwd, login_handle, de_command);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case xorg:
|
||||||
|
default :
|
||||||
|
launch_xorg(pwd, login_handle, de_command, display_name, vt);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* waits for the de/shell to exit */
|
||||||
|
waitpid(pid_display, &display_status, 0);
|
||||||
|
/* pam_close_session and error handling */
|
||||||
|
pam_result = pam_close_session(login_handle, 0);
|
||||||
|
|
||||||
|
if(pam_result != PAM_SUCCESS)
|
||||||
|
{
|
||||||
|
switch(pam_result)
|
||||||
|
{
|
||||||
|
case PAM_BUF_ERR :
|
||||||
|
error_print(LY_ERR_PAM_BUF);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAM_SESSION_ERR :
|
||||||
|
error_print(LY_ERR_PAM_SESSION);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAM_ABORT :
|
||||||
|
default:
|
||||||
|
error_print(LY_ERR_PAM_ABORT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pam_end(login_handle, pam_result);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pam_setcred and error handling */
|
||||||
|
pam_result = pam_setcred(login_handle, PAM_DELETE_CRED);
|
||||||
|
|
||||||
|
if(pam_result != PAM_SUCCESS)
|
||||||
|
{
|
||||||
|
switch(pam_result)
|
||||||
|
{
|
||||||
|
case PAM_BUF_ERR :
|
||||||
|
error_print(LY_ERR_PAM_BUF);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAM_CRED_ERR :
|
||||||
|
error_print(LY_ERR_PAM_CRED);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAM_CRED_EXPIRED :
|
||||||
|
error_print(LY_ERR_PAM_CRED_EXPIRED);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAM_CRED_UNAVAIL :
|
||||||
|
error_print(LY_ERR_PAM_CRED_UNAVAIL);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAM_SYSTEM_ERR :
|
||||||
|
error_print(LY_ERR_PAM_SYSTEM);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAM_USER_UNKNOWN :
|
||||||
|
error_print(LY_ERR_PAM_USER_UNKNOWN);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
error_print(LY_ERR_PAM_ABORT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pam_end(login_handle, pam_result);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pam_end and error handling */
|
||||||
|
pam_result = pam_end(login_handle, pam_result);
|
||||||
|
|
||||||
|
if(pam_result != PAM_SUCCESS)
|
||||||
|
{
|
||||||
|
error_print(LY_ERR_PAM_SYSTEM);
|
||||||
|
refresh();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
error_print(LY_LANG_LOGOUT);
|
||||||
|
refresh();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void launch_xorg(struct passwd* pwd, pam_handle_t* pam_handle,
|
||||||
|
const char* de_command, const char* display_name, const char* vt)
|
||||||
|
{
|
||||||
|
FILE* file;
|
||||||
|
pid_t xauth_pid;
|
||||||
|
int xauth_status;
|
||||||
|
char cmd[LY_LIM_CMD];
|
||||||
|
/* updates cookie */
|
||||||
|
snprintf(cmd, sizeof(cmd), "exec xauth add %s . `%s`", display_name,
|
||||||
|
LY_CMD_MCOOKIE);
|
||||||
|
/* creates the file if it can't be found */
|
||||||
|
file = fopen(getenv("XAUTHORITY"), "ab+");
|
||||||
|
fclose(file);
|
||||||
|
/* generates the cookie */
|
||||||
|
xauth_pid = fork();
|
||||||
|
|
||||||
|
if(xauth_pid == 0)
|
||||||
|
{
|
||||||
|
execl(pwd->pw_shell, pwd->pw_shell, "-c", cmd, NULL);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
waitpid(xauth_pid, &xauth_status, 0);
|
||||||
|
reset_terminal(pwd);
|
||||||
|
/* starts session */
|
||||||
|
snprintf(cmd, sizeof(cmd),
|
||||||
|
"exec xinit /usr/bin/%s -- %s %s %s -auth %s", de_command, LY_CMD_X,
|
||||||
|
display_name, vt, getenv("XAUTHORITY"));
|
||||||
|
execl(pwd->pw_shell, pwd->pw_shell, "-c", cmd, NULL);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void launch_wayland(struct passwd* pwd, pam_handle_t* pam_handle,
|
||||||
|
const char* de_command)
|
||||||
|
{
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void launch_shell(struct passwd* pwd, pam_handle_t* pam_handle)
|
||||||
|
{
|
||||||
|
char* pos;
|
||||||
|
char args[PATH_MAX + 2];
|
||||||
|
reset_terminal(pwd);
|
||||||
|
args[0] = '-';
|
||||||
|
strncpy(args + 1, ((pos = strrchr(pwd->pw_shell,
|
||||||
|
'/')) ? pos + 1 : pwd->pw_shell), sizeof(args) - 1);
|
||||||
|
execl(pwd->pw_shell, args, NULL);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy_env(void)
|
||||||
|
{
|
||||||
|
/* our environment */
|
||||||
|
extern char** environ;
|
||||||
|
/* completely destroys environment */
|
||||||
|
environ = malloc(sizeof(char*));
|
||||||
|
memset(environ, 0, sizeof(char*));
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_xdg(const char* tty_id, const char* display_name,
|
||||||
|
enum deserv_t display_server)
|
||||||
|
{
|
||||||
|
setenv("XDG_SESSION_CLASS", "user", 0);
|
||||||
|
setenv("XDG_SEAT", "seat0", 0);
|
||||||
|
setenv("XDG_VTNR", tty_id, 0);
|
||||||
|
setenv("DISPLAY", display_name, 1);
|
||||||
|
|
||||||
|
switch(display_server)
|
||||||
|
{
|
||||||
|
case shell:
|
||||||
|
setenv("XDG_SESSION_TYPE", "tty", 0);
|
||||||
|
|
||||||
|
case wayland:
|
||||||
|
setenv("XDG_SESSION_TYPE", "wayland", 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case xorg:
|
||||||
|
default :
|
||||||
|
setenv("XDG_SESSION_TYPE", "x11", 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int init_env(pam_handle_t* pam_handle, struct passwd* pw)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int len;
|
||||||
|
/* buffers */
|
||||||
|
char tmp[PATH_MAX];
|
||||||
|
char* buf;
|
||||||
|
char** env;
|
||||||
|
char* termenv = getenv("TERM");
|
||||||
|
setenv("HOME", pw->pw_dir, 0);
|
||||||
|
setenv("USER", pw->pw_name, 1);
|
||||||
|
setenv("SHELL", pw->pw_shell, 1);
|
||||||
|
setenv("LOGNAME", pw->pw_name, 1);
|
||||||
|
snprintf(tmp, sizeof(tmp), "/home/%s/%s", pw->pw_name, LY_XAUTHORITY);
|
||||||
|
setenv("XAUTHORITY", tmp, 0);
|
||||||
|
buf = termenv ? strdup(termenv) : NULL;
|
||||||
|
setenv("TERM", buf ? buf : "linux", 1);
|
||||||
|
free(buf);
|
||||||
|
len = snprintf(tmp, sizeof(tmp), "%s/%s", _PATH_MAILDIR, pw->pw_name);
|
||||||
|
|
||||||
|
if((len > 0) && ((size_t) len < sizeof(tmp)))
|
||||||
|
{
|
||||||
|
setenv("MAIL", tmp, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(setenv("PATH", LY_PATH, 1))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
env = pam_getenvlist(pam_handle);
|
||||||
|
|
||||||
|
for(i = 0; env && env[i]; i++)
|
||||||
|
{
|
||||||
|
putenv(env[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset_terminal(struct passwd* pwd)
|
||||||
|
{
|
||||||
|
pid_t pid;
|
||||||
|
int status;
|
||||||
|
pid = fork();
|
||||||
|
char cmd[LY_LIM_CMD];
|
||||||
|
strncpy(cmd, "exec tput reset", sizeof(cmd));
|
||||||
|
|
||||||
|
if(pid == 0)
|
||||||
|
{
|
||||||
|
execl(pwd->pw_shell, pwd->pw_shell, "-c", cmd, NULL);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
waitpid(pid, &status, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_free_display(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
char xlock[LY_LIM_PATH];
|
||||||
|
|
||||||
|
for(i = 0; i < 200; ++i)
|
||||||
|
{
|
||||||
|
snprintf(xlock, sizeof(xlock), "/tmp/.X%d-lock", i);
|
||||||
|
|
||||||
|
if(access(xlock, F_OK) == -1)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef _LOGIN_H_
|
||||||
|
#define _LOGIN_H_
|
||||||
|
|
||||||
|
#include <security/pam_appl.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include "desktop.h"
|
||||||
|
|
||||||
|
int login_conv(int num_msg, const struct pam_message** msg,
|
||||||
|
struct pam_response** resp, void* appdata_ptr);
|
||||||
|
int start_env(const char* username, const char* password,
|
||||||
|
const char* de_command, enum deserv_t display_server);
|
||||||
|
void launch_xorg(struct passwd* pwd, pam_handle_t* pam_handle,
|
||||||
|
const char* de_command, const char* display_name, const char* vt);
|
||||||
|
void launch_wayland(struct passwd* pwd, pam_handle_t* pam_handle,
|
||||||
|
const char* de_command);
|
||||||
|
void launch_shell(struct passwd* pwd, pam_handle_t* pam_handle);
|
||||||
|
void destroy_env(void);
|
||||||
|
void init_xdg(const char* tty_id, const char* display_name,
|
||||||
|
enum deserv_t display_server);
|
||||||
|
int init_env(pam_handle_t* pam_handle, struct passwd* pw);
|
||||||
|
void reset_terminal(struct passwd* pwd);
|
||||||
|
int get_free_display(void);
|
||||||
|
|
||||||
|
#endif /* _LOGIN_H_ */
|
||||||
|
|
@ -0,0 +1,213 @@
|
||||||
|
#define _XOPEN_SOURCE
|
||||||
|
|
||||||
|
/* stdlib */
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
/* linux */
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <linux/vt.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
/* ncurses */
|
||||||
|
#include <form.h>
|
||||||
|
/* pam */
|
||||||
|
#include <security/pam_appl.h>
|
||||||
|
/* ly */
|
||||||
|
#include "lang.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "login.h"
|
||||||
|
#include "desktop.h"
|
||||||
|
#include "ncui.h"
|
||||||
|
|
||||||
|
#define KEY_ENTER_ASCII 10
|
||||||
|
#define KEY_BACKSPACE_ASCII 127
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
FILE* console;
|
||||||
|
/* user interface components */
|
||||||
|
struct ncform form;
|
||||||
|
struct ncwin win;
|
||||||
|
/* desktop environments list */
|
||||||
|
enum deserv_t type;
|
||||||
|
struct delist_t* de_list;
|
||||||
|
struct deprops_t* de_props;
|
||||||
|
char** de_names;
|
||||||
|
int de_count;
|
||||||
|
int de_id;
|
||||||
|
/* user input */
|
||||||
|
int input_key;
|
||||||
|
/* processing buffers */
|
||||||
|
int fail;
|
||||||
|
int auth_fails;
|
||||||
|
char* username;
|
||||||
|
char* password;
|
||||||
|
char* cmd;
|
||||||
|
/* prevents CTRL+C from killing the process */
|
||||||
|
signal(SIGINT, SIG_IGN);
|
||||||
|
signal(SIGHUP, SIG_IGN);
|
||||||
|
/* gets desktop entries */
|
||||||
|
de_list = list_de();
|
||||||
|
de_props = de_list->props;
|
||||||
|
de_names = de_list->names;
|
||||||
|
de_count = de_list->count;
|
||||||
|
de_id = 0;
|
||||||
|
auth_fails = 0;
|
||||||
|
/* verifies if we can access the console with enough privileges */
|
||||||
|
console = fopen(LY_CONSOLE_DEV, "w");
|
||||||
|
|
||||||
|
if(!console)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s\n", LY_ERR_FD);
|
||||||
|
fprintf(stderr, "%s\n", LY_ERR_FD_ADVICE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
kernel_log(0);
|
||||||
|
/* initializes ncurses UI */
|
||||||
|
init_ncurses(console);
|
||||||
|
init_form(&form, de_names, de_count, &de_id);
|
||||||
|
init_win(&win, &form);
|
||||||
|
init_scene(&win, &form);
|
||||||
|
init_draw(&win, &form);
|
||||||
|
close(fileno(console));
|
||||||
|
/* enables insertion mode */
|
||||||
|
form_driver(form.form, REQ_INS_MODE);
|
||||||
|
/* makes the password field active by default */
|
||||||
|
set_current_field(form.form, form.fields[4]);
|
||||||
|
form_driver(form.form, REQ_END_LINE);
|
||||||
|
|
||||||
|
while((input_key = wgetch(win.win)))
|
||||||
|
{
|
||||||
|
form.active = current_field(form.form);
|
||||||
|
|
||||||
|
switch(input_key)
|
||||||
|
{
|
||||||
|
case KEY_ENTER_ASCII:
|
||||||
|
if(form.active == form.fields[4])
|
||||||
|
{
|
||||||
|
/* checks for buffer errors */
|
||||||
|
if(form_driver(form.form, REQ_VALIDATION) != E_OK)
|
||||||
|
{
|
||||||
|
error_print(LY_ERR_NC_BUFFER);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* stores the user inputs in processing buffers */
|
||||||
|
username = trim(field_buffer(form.fields[2], 0));
|
||||||
|
password = trim(field_buffer(form.fields[4], 0));
|
||||||
|
cmd = de_props[de_id].cmd;
|
||||||
|
type = de_props[de_id].type;
|
||||||
|
|
||||||
|
/* saves the username and DE if enabled */
|
||||||
|
if(LY_CFG_WRITE_SAVE)
|
||||||
|
{
|
||||||
|
FILE* file = fopen(LY_CFG_SAVE, "ab+");
|
||||||
|
file = fopen(LY_CFG_SAVE, "wb");
|
||||||
|
fprintf(file, "%s\n%d", username, de_id);
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* logs in and suspends ncurses mode if successful */
|
||||||
|
fail = start_env(username, password, cmd, type);
|
||||||
|
/* clears the password */
|
||||||
|
set_field_buffer(form.fields[4], 0, "");
|
||||||
|
|
||||||
|
if(fail)
|
||||||
|
{
|
||||||
|
++auth_fails;
|
||||||
|
|
||||||
|
if(auth_fails > (LY_CFG_AUTH_TRIG - 1))
|
||||||
|
{
|
||||||
|
cascade();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(LY_CFG_CLR_USR)
|
||||||
|
{
|
||||||
|
/* clears the username */
|
||||||
|
set_field_buffer(form.fields[2], 0, "");
|
||||||
|
/* sets cursor to the login field */
|
||||||
|
set_current_field(form.form, form.fields[2]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* sets cursor to the password field */
|
||||||
|
set_current_field(form.form, form.fields[4]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case KEY_DOWN:
|
||||||
|
form_driver(form.form, REQ_NEXT_FIELD);
|
||||||
|
form_driver(form.form, REQ_END_LINE);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY_UP:
|
||||||
|
form_driver(form.form, REQ_PREV_FIELD);
|
||||||
|
form_driver(form.form, REQ_END_LINE);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY_RIGHT:
|
||||||
|
if(form.active == form.fields[0])
|
||||||
|
{
|
||||||
|
de_id = ((de_id + 1) == de_count) ? 0 : de_id + 1;
|
||||||
|
form_driver(form.form, REQ_NEXT_CHOICE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
form_driver(form.form, REQ_NEXT_CHAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY_LEFT:
|
||||||
|
if(form.active == form.fields[0])
|
||||||
|
{
|
||||||
|
de_id = (de_id == 0) ? (de_count - 1) : de_id - 1;
|
||||||
|
form_driver(form.form, REQ_PREV_CHOICE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
form_driver(form.form, REQ_PREV_CHAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY_BACKSPACE_ASCII:
|
||||||
|
case KEY_BACKSPACE:
|
||||||
|
form_driver(form.form, REQ_DEL_PREV);
|
||||||
|
form_driver(form.form, REQ_END_LINE);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY_DC:
|
||||||
|
form_driver(form.form, REQ_DEL_CHAR);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY_F(1):
|
||||||
|
end_form(&form);
|
||||||
|
endwin();
|
||||||
|
free_list(de_list);
|
||||||
|
execl(LY_CMD_HALT, LY_CMD_HALT, "-h", "now", NULL);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY_F(2):
|
||||||
|
end_form(&form);
|
||||||
|
endwin();
|
||||||
|
free_list(de_list);
|
||||||
|
execl(LY_CMD_HALT, LY_CMD_HALT, "-r", "now", NULL);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
form_driver(form.form, input_key);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kernel_log(1);
|
||||||
|
fclose(console);
|
||||||
|
free_list(de_list);
|
||||||
|
end_form(&form);
|
||||||
|
endwin();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,166 @@
|
||||||
|
#define _XOPEN_SOURCE
|
||||||
|
|
||||||
|
#include "ncui.h"
|
||||||
|
|
||||||
|
/* stdlib */
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
/* linux */
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <linux/vt.h>
|
||||||
|
/* ncurses */
|
||||||
|
#include <form.h>
|
||||||
|
/* ly */
|
||||||
|
#include "lang.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
size_t max(size_t a, size_t b)
|
||||||
|
{
|
||||||
|
return (a > b) ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_ncurses(FILE* desc)
|
||||||
|
{
|
||||||
|
int filedesc = fileno(desc);
|
||||||
|
/* required for ncurses */
|
||||||
|
putenv(LY_CONSOLE_TERM);
|
||||||
|
/* switches tty */
|
||||||
|
ioctl(filedesc, VT_ACTIVATE, LY_CONSOLE_TTY);
|
||||||
|
ioctl(filedesc, VT_WAITACTIVE, LY_CONSOLE_TTY);
|
||||||
|
/* ncurses startup */
|
||||||
|
initscr();
|
||||||
|
cbreak();
|
||||||
|
noecho();
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_form(struct ncform* form, char** list, int max_de, int* de_id)
|
||||||
|
{
|
||||||
|
FILE* file;
|
||||||
|
char line[LY_LIM_LINE_FILE];
|
||||||
|
char user[LY_LIM_LINE_FILE];
|
||||||
|
int de;
|
||||||
|
/* creates the file if it can't be found */
|
||||||
|
file = fopen(LY_CFG_SAVE, "ab+");
|
||||||
|
fclose(file);
|
||||||
|
/* opens the file */
|
||||||
|
file = fopen(LY_CFG_SAVE, "rb");
|
||||||
|
memset(user, '\0', LY_LIM_LINE_FILE);
|
||||||
|
de = max_de;
|
||||||
|
|
||||||
|
/* reads the username and DE from the save file if enabled */
|
||||||
|
if(LY_CFG_READ_SAVE)
|
||||||
|
{
|
||||||
|
if(fgets(line, sizeof(line), file))
|
||||||
|
{
|
||||||
|
snprintf(user, sizeof(file), "%s", line);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fgets(line, sizeof(line), file))
|
||||||
|
{
|
||||||
|
de = (unsigned int) strtol(line, NULL, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
/* computes input padding from labels text */
|
||||||
|
form->label_pad = max(strlen(LY_LANG_LOGIN), strlen(LY_LANG_PASSWORD));
|
||||||
|
/* DE list */
|
||||||
|
form->fields[0] = new_field(1, 32, 0, form->label_pad, 0, 0);
|
||||||
|
set_field_type(form->fields[0], TYPE_ENUM, list);
|
||||||
|
|
||||||
|
if(de < max_de)
|
||||||
|
{
|
||||||
|
set_field_buffer(form->fields[0], 0, list[de]);
|
||||||
|
*de_id = de;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
set_field_buffer(form->fields[0], 0, list[0]);
|
||||||
|
*de_id = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_field_opts(form->fields[0],
|
||||||
|
O_VISIBLE | O_PUBLIC | O_EDIT | O_ACTIVE);
|
||||||
|
/* login label */
|
||||||
|
form->fields[1] = new_field(1, form->label_pad, 2, 0, 0, 0);
|
||||||
|
set_field_buffer(form->fields[1], 0, LY_LANG_LOGIN);
|
||||||
|
set_field_opts(form->fields[1], O_VISIBLE | O_PUBLIC | O_AUTOSKIP);
|
||||||
|
/* login field */
|
||||||
|
form->fields[2] = new_field(1, 32, 2, form->label_pad, 0, 0);
|
||||||
|
|
||||||
|
if(*user)
|
||||||
|
{
|
||||||
|
set_field_buffer(form->fields[2], 0, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
set_field_opts(form->fields[2],
|
||||||
|
O_VISIBLE | O_PUBLIC | O_EDIT | O_ACTIVE);
|
||||||
|
/* password label */
|
||||||
|
form->fields[3] = new_field(1, form->label_pad, 4, 0, 0, 0);
|
||||||
|
set_field_buffer(form->fields[3], 0, LY_LANG_PASSWORD);
|
||||||
|
set_field_opts(form->fields[3], O_VISIBLE | O_PUBLIC | O_AUTOSKIP);
|
||||||
|
/* password field */
|
||||||
|
form->fields[4] = new_field(1, 32, 4, form->label_pad, 0, 0);
|
||||||
|
set_field_opts(form->fields[4], O_VISIBLE | O_EDIT | O_ACTIVE);
|
||||||
|
/* bound */
|
||||||
|
form->fields[5] = NULL;
|
||||||
|
/* generates the form */
|
||||||
|
form->form = new_form(form->fields);
|
||||||
|
scale_form(form->form, &(form->height), &(form->width));
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_win(struct ncwin* win, struct ncform* form)
|
||||||
|
{
|
||||||
|
int rows;
|
||||||
|
int cols;
|
||||||
|
/* fetches screen size */
|
||||||
|
getmaxyx(stdscr, rows, cols);
|
||||||
|
/* adds a margin */
|
||||||
|
win->width = LY_MARGIN_H * 2 + form->width;
|
||||||
|
win->height = LY_MARGIN_V * 2 + form->height + 2;
|
||||||
|
/* saves the position */
|
||||||
|
win->y = (rows - win->height) / 2;
|
||||||
|
win->x = (cols - win->width) / 2;
|
||||||
|
/* generates the window */
|
||||||
|
win->win = newwin(win->height, win->width, win->y, win->x);
|
||||||
|
/* enables advanced input (eg. "F1" key) */
|
||||||
|
keypad(win->win, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_scene(struct ncwin* win, struct ncform* form)
|
||||||
|
{
|
||||||
|
set_form_win(form->form, win->win);
|
||||||
|
set_form_sub(form->form, derwin(win->win, form->height, form->width,
|
||||||
|
LY_MARGIN_V + 2, LY_MARGIN_H));
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_draw(struct ncwin* win, struct ncform* form)
|
||||||
|
{
|
||||||
|
char line[LY_LIM_LINE_CONSOLE];
|
||||||
|
/* frame */
|
||||||
|
box(win->win, 0, 0);
|
||||||
|
/* initializes error output and prints greeting message */
|
||||||
|
error_init(win->win, win->width, LY_LANG_GREETING);
|
||||||
|
/* prints shutdown & reboot hints */
|
||||||
|
snprintf(line, sizeof(line), "F1 %s F2 %s", LY_LANG_SHUTDOWN,
|
||||||
|
LY_LANG_REBOOT);
|
||||||
|
mvprintw(0, 0, line);
|
||||||
|
/* dumps ncurses buffer */
|
||||||
|
refresh();
|
||||||
|
/* registers form */
|
||||||
|
post_form(form->form);
|
||||||
|
/* dumps window buffer */
|
||||||
|
wrefresh(win->win);
|
||||||
|
}
|
||||||
|
|
||||||
|
void end_form(struct ncform* form)
|
||||||
|
{
|
||||||
|
unpost_form(form->form);
|
||||||
|
free_form(form->form);
|
||||||
|
free_field(form->fields[0]);
|
||||||
|
free_field(form->fields[1]);
|
||||||
|
free_field(form->fields[2]);
|
||||||
|
free_field(form->fields[3]);
|
||||||
|
free_field(form->fields[4]);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef _NCUI_H_
|
||||||
|
#define _NCUI_H_
|
||||||
|
|
||||||
|
/* ncurses */
|
||||||
|
#include <form.h>
|
||||||
|
|
||||||
|
struct ncwin
|
||||||
|
{
|
||||||
|
WINDOW* win;
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ncform
|
||||||
|
{
|
||||||
|
FORM* form;
|
||||||
|
FIELD* fields[6];
|
||||||
|
FIELD* active;
|
||||||
|
int height;
|
||||||
|
int width;
|
||||||
|
int label_pad;
|
||||||
|
};
|
||||||
|
|
||||||
|
void init_ncurses(FILE* desc);
|
||||||
|
void init_form(struct ncform* form, char** list, int max_de,
|
||||||
|
int* de_id);
|
||||||
|
void init_win(struct ncwin* win, struct ncform* form);
|
||||||
|
void init_scene(struct ncwin* win, struct ncform* form);
|
||||||
|
void init_draw(struct ncwin* win, struct ncform* form);
|
||||||
|
void end_form(struct ncform* form);
|
||||||
|
|
||||||
|
#endif /* _NCUI_H_ */
|
||||||
|
|
@ -0,0 +1,164 @@
|
||||||
|
#define _XOPEN_SOURCE 500
|
||||||
|
|
||||||
|
/* std lib */
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
/* ncurses */
|
||||||
|
#include <form.h>
|
||||||
|
/* ly */
|
||||||
|
#include "config.h"
|
||||||
|
#include "utils.h"
|
||||||
|
/* important stuff */
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
void kernel_log(int mode)
|
||||||
|
{
|
||||||
|
pid_t pid;
|
||||||
|
int status;
|
||||||
|
pid = fork();
|
||||||
|
|
||||||
|
if(pid == 0)
|
||||||
|
{
|
||||||
|
if(mode)
|
||||||
|
{
|
||||||
|
execl("dmesg", "-E", NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
execl("dmesg", "-D", NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
waitpid(pid, &status, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* trim(char* s)
|
||||||
|
{
|
||||||
|
char* end = s + strlen(s) - 1;
|
||||||
|
|
||||||
|
while((end > s) && isspace((unsigned char) *end))
|
||||||
|
{
|
||||||
|
--end;
|
||||||
|
}
|
||||||
|
|
||||||
|
*(end + 1) = '\0';
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* strdup(const char* src)
|
||||||
|
{
|
||||||
|
size_t len = strlen(src) + 1;
|
||||||
|
char* s = malloc(len);
|
||||||
|
|
||||||
|
if(!s)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (char*)memcpy(s, src, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void error_init(WINDOW* win, int width, const char* s)
|
||||||
|
{
|
||||||
|
static WINDOW* win_stack = NULL;
|
||||||
|
static int width_stack = 0;
|
||||||
|
char* blank;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(win)
|
||||||
|
{
|
||||||
|
win_stack = win;
|
||||||
|
width_stack = width;
|
||||||
|
}
|
||||||
|
|
||||||
|
blank = malloc((width_stack - 1) * (sizeof(char)));
|
||||||
|
|
||||||
|
for(i = 0; i < width_stack - 2; ++i)
|
||||||
|
{
|
||||||
|
blank[i] = ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
blank[i] = '\0';
|
||||||
|
mvwprintw(win_stack, LY_MARGIN_V, 1, blank);
|
||||||
|
mvwprintw(win_stack, LY_MARGIN_V, (width_stack - strlen(s)) / 2, s);
|
||||||
|
free(blank);
|
||||||
|
}
|
||||||
|
|
||||||
|
void error_print(const char* s)
|
||||||
|
{
|
||||||
|
error_init(NULL, 0, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
chtype get_curses_char(int y, int x)
|
||||||
|
{
|
||||||
|
return mvwinch(newscr, y, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cascade(void)
|
||||||
|
{
|
||||||
|
int rows;
|
||||||
|
int cols;
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
chtype char_cur;
|
||||||
|
chtype char_under;
|
||||||
|
time_t time_start;
|
||||||
|
time_t time_end;
|
||||||
|
time_t time_rand;
|
||||||
|
int fps = LY_CFG_FPS;
|
||||||
|
int frame_target = LY_CFG_FMAX;
|
||||||
|
int frame_count;
|
||||||
|
float time_frame;
|
||||||
|
float time_delta = 1.0 / fps;
|
||||||
|
getmaxyx(stdscr, rows, cols);
|
||||||
|
time(&time_rand);
|
||||||
|
srand((unsigned) time_rand);
|
||||||
|
|
||||||
|
for(frame_count = 0; frame_count < frame_target; ++frame_count)
|
||||||
|
{
|
||||||
|
time_start = clock();
|
||||||
|
|
||||||
|
for(y = 0; y < rows; ++y)
|
||||||
|
{
|
||||||
|
for(x = 0; x < cols; ++x)
|
||||||
|
{
|
||||||
|
char_cur = get_curses_char(y, x);
|
||||||
|
|
||||||
|
if(isspace(char_cur & A_CHARTEXT))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
char_under = get_curses_char(y + 1, x);
|
||||||
|
|
||||||
|
if(!isspace(char_under & A_CHARTEXT))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(((rand() % 10) > LY_CFG_FCHANCE) && (frame_count > 0))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
mvaddch(y, x, ' ');
|
||||||
|
mvaddch(y + 1, x, char_cur);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh();
|
||||||
|
time_end = clock();
|
||||||
|
time_frame = (time_end - time_start) / CLOCKS_PER_SEC;
|
||||||
|
|
||||||
|
if(time_frame < time_delta)
|
||||||
|
{
|
||||||
|
usleep((time_delta - time_frame) * 1000000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
#ifndef _UTILS_H_
|
||||||
|
#define _UTILS_H_
|
||||||
|
|
||||||
|
/* ncurses */
|
||||||
|
#include <form.h>
|
||||||
|
|
||||||
|
void kernel_log(int mode);
|
||||||
|
char* trim(char* s);
|
||||||
|
char* strdup(const char* src);
|
||||||
|
void error_init(WINDOW* win, int width, const char* s);
|
||||||
|
void error_print(const char* s);
|
||||||
|
chtype get_curses_char(int y, int x);
|
||||||
|
void cascade(void);
|
||||||
|
|
||||||
|
#endif /* _UTILS_H_ */
|
||||||
Loading…
Reference in New Issue