restarted from scratch

ly_0_1_0
nullgemm 6 years ago
parent 244f072d11
commit d285d558cd

@ -1,13 +0,0 @@
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.

@ -1,67 +0,0 @@
### Ly - ncurses display manager
![ly screenshot](https://user-images.githubusercontent.com/5473047/42466218-8cb53d3c-83ae-11e8-8e53-bae3669f959c.png "ly on st")
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:
- make
- a c99 compiler
- 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
```
If messages from other services pops-up over the login prompt, you have to edit the service in order to run it last. In order to do this, simply edit the following file:
```
/usr/lib/systemd/system/ly.service
```
Just under the "After getty", add another one with your last-launching service. Example on my computer:
```
After=tlp.service
```
### Controls
Use the up and down arrow keys to change the current field, and the
left and right arrow keys to change the target desktop environment
while on the desktop field (above the username field).
### 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.

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

@ -1,67 +0,0 @@
rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d))
######## CONFIG ########
NAME := ly
CXX := cc
INSTALL = ln -sf /usr/lib/security/pam_loginuid.so ${DESTDIR}/usr/lib/pam_loginuid.so
SRCD := src
INCD := src
BIND := build
OBJD := obj
DEPD := dep
LIBS := -lform -lncurses -lpam -lpam_misc -lX11
LIBSUSR = -L/usr/lib/security -l:pam_loginuid.so
VPATH = $(SRCD) $(INCD) $(OBJD) $(DEPD)
######## STOP ########
SRCS := $(call rwildcard,$(SRCD)/,*.c)
OBJS := $(patsubst $(SRCD)/%.c,$(OBJD)/%.o,$(SRCS))
DEPS := $(patsubst $(SRCD)/%.c,$(DEPD)/%.d,$(SRCS))
CXXFLAGS := -Wall -g -I$(INCD)
LDDFLAGS := $(LIBS)
.PHONY: all install uninstall clean distclean
.PRECIOUS: $(DEPD)/%.d
all: $(BIND)/$(NAME)
$(DEPD)/%.d : $(SRCD)/%.c
@echo "listing dependencies for source file $<"
@mkdir -p $(@D)
@$(CXX) $(CXXFLAGS) -M -c $< -o $@
$(OBJD)/%.o : $(SRCD)/%.c $(DEPD)/%.d
@echo "building object $@"
@mkdir -p $(@D)
@$(CXX) $(CXXFLAGS) -c $< -o $@
$(BIND)/$(NAME): $(OBJS)
@echo "compiling $@"
@mkdir -p $(BIND)
@$(CXX) $(CXXFLAGS) $(LDDFLAGS) $(OBJS) -o $(BIND)/$(NAME)
install : $(BIND)/$(NAME)
install -dZ ${DESTDIR}/etc/ly
install -DZ $(BIND)/$(NAME) -t ${DESTDIR}/usr/bin
install -DZ xsetup.sh -t ${DESTDIR}/etc/ly
install -DZ ly.service -t ${DESTDIR}/usr/lib/systemd/system
$(INSTALL)
uninstall:
rm -rf ${DESTDIR}/etc/ly
rm -f ${DESTDIR}/usr/bin/ly
rm -f ${DESTDIR}/usr/lib/systemd/system/ly.service
clean:
@echo "cleaning workspace"
@rm -rf $(BIND)
@rm -rf $(OBJD)
@rm -rf $(DEPD)
distclean: clean

@ -1,47 +0,0 @@
#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 512
#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 "~/.xinitrc"
#define LY_CMD_MCOOKIE "/usr/bin/mcookie"
#define LY_CMD_XSETUP "/etc/ly/xsetup.sh"
#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"
#define LY_PATH_WSESSIONS "/usr/share/wayland-sessions"
/* 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_ */

@ -1,180 +0,0 @@
#include <stdlib.h>
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <unistd.h>
#include "lang.h"
#include "config.h"
#include "utils.h"
#include "desktop.h"
#define LY_XSESSION_EXEC "Exec="
#define LY_XSESSION_NAME "Name="
// searches a folder
short get_desktops(char* sessions_dir, struct delist_t* list, int* remote_count, short x)
{
/* xsession */
FILE* file;
DIR* dir;
struct dirent* dir_info;
/* buffers */
char path[LY_LIM_PATH];
char* name;
char* command;
int count = *remote_count;
if(access(sessions_dir, F_OK) == -1 )
{
return -1;
}
/* reads xorg's desktop environments entries */
dir = opendir(sessions_dir);
/* exits if the folder can't be read */
if(!dir)
{
error_print(LY_ERR_DELIST);
end_list(list, count);
return -2;
}
/* 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", sessions_dir,
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 = x ? xorg : wayland;
++count;
fclose(file);
}
closedir(dir);
*remote_count = count;
return 0;
}
/* returns a list containing all the DE for all the display servers */
struct delist_t* list_de(void)
{
/* de list */
int count = 2;
short errors = 0;
struct delist_t* list = init_list(count);
if(get_desktops(LY_PATH_XSESSIONS, list, &count, true) != 0)
{
++errors;
}
if(get_desktops(LY_PATH_WSESSIONS, list, &count, false))
{
++errors;
}
if(errors > 1) {
return NULL;
}
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 = xinitrc;
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);
}

@ -1,25 +0,0 @@
#ifndef _DESKTOP_H_
#define _DESKTOP_H_
enum deserv_t {shell, xorg, xinitrc, 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_ */

@ -1,49 +0,0 @@
#ifndef _LANG_H_
#define _LANG_H_
/* UI strings */
#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_CONSOLE "Failed to create the console file descriptor"
#define LY_ERR_FD_CONSOLE_ADVICE "(ly probably wasn't run with enough privileges)"
#define LY_ERR_FD_CFG_SAVE "Failed to create the config file"
/* 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_ */

@ -1,625 +0,0 @@
#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 <pwd.h>
#include <signal.h>
#include <X11/Xlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.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;
}
/* Initialise user groups */
/* Get pwd structure for the user to get his group id */
struct passwd* pw = getpwnam(username);
if(!pw)
{
error_print(strerror(errno));
pam_end(login_handle, pam_result);
return 1;
}
int grp_result = initgroups(username, pw->pw_gid);
if(grp_result == -1)
{
error_print(strerror(errno));
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:
launch_xorg(pwd, login_handle, de_command, display_name, vt, 0);
break;
case xinitrc:
default :
launch_xorg(pwd, login_handle, de_command, display_name, vt, 1);
break;
}
exit(EXIT_SUCCESS);
}
/* 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,
int xinitrc)
{
FILE* file;
pid_t child;
int status;
char cmd[LY_LIM_CMD];
char* argv[] = {pwd->pw_shell, "-l", "-c", cmd, NULL};
extern char** environ;
/* 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 */
child = fork();
if(child == 0)
{
execl(pwd->pw_shell, pwd->pw_shell, "-c", cmd, NULL);
exit(EXIT_SUCCESS);
}
waitpid(child, &status, 0);
reset_terminal(pwd);
snprintf(cmd, sizeof(cmd),
"exec xinit %s %s%s -- %s %s %s -auth %s",
LY_CMD_XSETUP,
xinitrc ? "" : "/usr/bin/",
de_command, LY_CMD_X,
display_name, vt, getenv("XAUTHORITY"));
execve(pwd->pw_shell, argv, environ);
exit(EXIT_SUCCESS);
}
void launch_wayland(struct passwd* pwd, pam_handle_t* pam_handle,
const char* de_command)
{
char cmd[32];
extern char** environ;
char* argv[] = {pwd->pw_shell, "-l", "-c", cmd, NULL};
snprintf(cmd, 32, "exec %s", de_command);
reset_terminal(pwd);
execve(pwd->pw_shell, argv, environ);
exit(EXIT_SUCCESS);
}
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(EXIT_SUCCESS);
}
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), "%s/%s", pw->pw_dir, 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(EXIT_SUCCESS);
}
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;
}

@ -1,25 +0,0 @@
#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,
int xinitrc);
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_ */

@ -1,226 +0,0 @@
#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_TAB_ASCII 8
#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;
/* gets desktop entries */
de_list = list_de();
if(de_list == NULL) {
return EXIT_FAILURE;
}
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_CONSOLE);
fprintf(stderr, "%s\n", LY_ERR_FD_CONSOLE_ADVICE);
return EXIT_FAILURE;
}
/* create LY_CFG_SAVE if it doesn't exist yet */
FILE* cfg_save = fopen(LY_CFG_SAVE, "ab+");
if (!cfg_save)
{
fprintf(stderr, "%s: %s\n", LY_ERR_FD_CFG_SAVE, LY_CFG_SAVE);
return EXIT_FAILURE;
}
fclose(cfg_save);
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[6]);
form_driver(form.form, REQ_END_LINE);
while((input_key = wgetch(win.win)) != ERR)
{
form.active = current_field(form.form);
switch(input_key)
{
case KEY_ENTER_ASCII:
if(form.active == form.fields[6])
{
/* 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[4], 0));
password = trim(field_buffer(form.fields[6], 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, "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[6], 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[4], 0, "");
/* sets cursor to the login field */
set_current_field(form.form, form.fields[4]);
break;
}
/* sets cursor to the password field */
set_current_field(form.form, form.fields[6]);
break;
}
case KEY_TAB_ASCII:
case KEY_DOWN:
form_driver(form.form, REQ_NEXT_FIELD);
form_driver(form.form, REQ_END_LINE);
break;
case KEY_BTAB:
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[1])
{
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[1])
{
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 EXIT_SUCCESS;
}

@ -1,193 +0,0 @@
#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();
raw();
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];
char* arrow_left;
char arrow_right[3] = " >";
int de;
int i;
/* 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))
{
strcpy(user, 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));
arrow_left = malloc((form->label_pad + 1) * (sizeof (char)));
/* adds the left arrow */
i = 0;
form->fields[i] = new_field(1, form->label_pad, 0, 0, 0, 0);
memset(arrow_left, ' ', form->label_pad);
arrow_left[form->label_pad - 2] = '<';
arrow_left[form->label_pad] = '\0';
set_field_buffer(form->fields[i], 0, arrow_left);
set_field_opts(form->fields[i], O_VISIBLE | O_PUBLIC | O_AUTOSKIP);
/* DE list */
++i;
form->fields[i] = new_field(1, 32, 0, form->label_pad, 0, 0);
set_field_type(form->fields[i], TYPE_ENUM, list);
if(de < max_de)
{
set_field_buffer(form->fields[i], 0, list[de]);
*de_id = de;
}
else
{
set_field_buffer(form->fields[i], 0, list[0]);
*de_id = 0;
}
set_field_opts(form->fields[i],
O_VISIBLE | O_PUBLIC | O_ACTIVE);
/* adds the right arrow */
++i;
form->fields[i] = new_field(1, 2, 0, form->label_pad + 32, 0, 0);
set_field_buffer(form->fields[i], 0, arrow_right);
set_field_opts(form->fields[i], O_VISIBLE | O_PUBLIC | O_AUTOSKIP);
/* login label */
++i;
form->fields[i] = new_field(1, form->label_pad, 2, 0, 0, 0);
set_field_buffer(form->fields[i], 0, LY_LANG_LOGIN);
set_field_opts(form->fields[i], O_VISIBLE | O_PUBLIC | O_AUTOSKIP);
/* login field */
++i;
form->fields[i] = new_field(1, 32, 2, form->label_pad, 0, 0);
if(*user)
{
set_field_buffer(form->fields[i], 0, user);
}
set_field_opts(form->fields[i],
O_VISIBLE | O_PUBLIC | O_EDIT | O_ACTIVE);
/* password label */
++i;
form->fields[i] = new_field(1, form->label_pad, 4, 0, 0, 0);
set_field_buffer(form->fields[i], 0, LY_LANG_PASSWORD);
set_field_opts(form->fields[i], O_VISIBLE | O_PUBLIC | O_AUTOSKIP);
/* password field */
++i;
form->fields[i] = new_field(1, 32, 4, form->label_pad, 0, 0);
set_field_opts(form->fields[i], O_VISIBLE | O_EDIT | O_ACTIVE);
/* bound */
++i;
form->fields[i] = NULL;
/* generates the form */
form->form = new_form(form->fields);
form_opts_off(form->form, O_BS_OVERLOAD);
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];
char *greeting;
/* frame */
box(win->win, 0, 0);
/* initializes error output and prints greeting message */
hostname(&greeting);
error_init(win->win, win->width, 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);
free(greeting);
}
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]);
free_field(form->fields[5]);
free_field(form->fields[6]);
}

@ -1,34 +0,0 @@
#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[8];
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_ */

@ -1,194 +0,0 @@
#define _XOPEN_SOURCE 500
#define _DEFAULT_SOURCE
#define _POSIX_C_SOURCE 200809L
/* 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>
/* sockets */
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
void kernel_log(int mode)
{
pid_t pid;
int status;
pid = fork();
if(pid < 0) {
perror("fork");
exit(EXIT_FAILURE);
}
if(pid == 0)
{
if(mode)
{
execl("/bin/dmesg", "/bin/dmesg", "-E", NULL);
}
else
{
execl("/bin/dmesg", "/bin/dmesg", "-D", NULL);
}
/* execl should not return */
perror("execl");
exit(EXIT_FAILURE);
}
waitpid(pid, &status, 0);
if(!WIFEXITED(status) || WEXITSTATUS(status))
exit(EXIT_FAILURE);
}
char* trim(char* s)
{
char* end = s + strlen(s) - 1;
while((end > s) && isspace((unsigned char) *end))
{
--end;
}
*(end + 1) = '\0';
return s;
}
void hostname(char** out) {
struct addrinfo hints;
struct addrinfo *info;
char hostname[1024];
char* dot;
int result;
hostname[1023] = '\0';
gethostname(hostname, 1023);
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_CANONNAME;
result = getaddrinfo(hostname, "http", &hints, &info);
if(result == 0 && info != NULL)
{
dot = strchr(info->ai_canonname, '.');
*out = strndup(info->ai_canonname, dot - info->ai_canonname);
}
else
{
*out = strdup("");
}
freeaddrinfo(info);
}
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);
}
}
}

@ -1,16 +0,0 @@
#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 hostname(char** out);
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_ */

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