12 Commits

Author SHA1 Message Date
peterc-s
e6d8bea236 Remove redundant constant 2024-10-23 13:01:13 +01:00
peterc-s
7e69260511 Naming convention 2024-10-23 12:58:53 +01:00
peterc-s
1ddccf2683 Fix use after free and reintroduce code from earlier commit 2024-10-22 14:42:42 +01:00
peterc-s
798d9d934c Removed code give up TTY and take back TTY 2024-10-22 12:26:18 +01:00
peterc-s
c54ca6761d seems to fix segfault by not giving up control of the tty, shutting down when it's aready deferred could be the issue? 2024-10-22 12:17:22 +01:00
peterc-s
5e05d29e0c added some comments for doom animation 2024-10-22 04:00:46 +01:00
peterc-s
020f27b203 added refresh interval in ms for animations 2024-10-22 02:03:21 +01:00
Moritz
e125d8f1aa Add error when sleep command fails (#703) 2024-10-12 20:13:25 +02:00
Ch1llyB1lly
e885a5e776 Add Hyprland in README (#701)
Adds hyprland to the list of tested desktop environments
2024-10-09 07:17:38 +02:00
Moritz
aea95b7724 Prevent output of brightness/sleep command showing on screen (fixing #695) (#699)
* Prevent output of brightness cmds showing on screen (fixing #695)

* Prevent output of sleep cmd showing on screen
2024-10-01 20:32:30 +02:00
ttsenturk
215ca5edc7 Update readme.md (#698) 2024-10-01 20:29:58 +02:00
Claudi Lleyda Moltó
022d146f76 Update Catalan translation (#689) 2024-08-21 21:19:33 +02:00
11 changed files with 375 additions and 457 deletions

View File

@@ -25,7 +25,7 @@ Ly is a lightweight TUI (ncurses-like) display manager for Linux and BSD.
It is recommended to add a rule for Ly as it currently does not ship one. It is recommended to add a rule for Ly as it currently does not ship one.
``` ```
# dnf install kernel-devel pam-devel libxcb-devel # dnf install kernel-devel pam-devel libxcb-devel zig
``` ```
## Support ## Support
@@ -40,6 +40,7 @@ The following desktop environments were tested with success:
- dwm - dwm
- enlightenment - enlightenment
- gnome - gnome
- hyprland
- i3 - i3
- kde - kde
- labwc - labwc

View File

@@ -35,6 +35,14 @@
# matrix -> CMatrix # matrix -> CMatrix
animation = none animation = none
# The minimum time between animation frames
# 32 -> ~30fps
# 16 -> ~60fps
# 8 -> ~120fps
# 6 -> ~144fps
# 4 -> ~240fps
animation_refresh_ms = 16
# Stop the animation after some time # Stop the animation after some time
# 0 -> Run forever # 0 -> Run forever
# 1..2e12 -> Stop the animation after this many seconds # 1..2e12 -> Stop the animation after this many seconds

View File

@@ -1,45 +1,60 @@
authenticating = autenticant...
brightness_down = abaixar brillantor
brightness_up = apujar brillantor
capslock = Bloq Majús capslock = Bloq Majús
err_alloc = falla d'assignació de memòria err_alloc = assignació de memòria fallida
err_bounds = índex fora de límit err_bounds = índex fora de límits
err_chdir = error al obrir carpeta home err_brightness_change = error en canviar la brillantor
err_console_dev = error al accedir a la consola err_chdir = error en obrir la carpeta home
err_console_dev = error en accedir a la consola
err_dgn_oob = missatge de registre err_dgn_oob = missatge de registre
err_domain = domini invàlid err_domain = domini invàlid
err_hostname = error al obtenir el nom del host err_envlist = error en obtenir l'envlist
err_mlock = error al bloquejar la clau de memòria err_hostname = error en obtenir el nom de l'amfitrió
err_mlock = error en bloquejar la memòria de clau
err_null = punter nul err_null = punter nul
err_numlock = error en establir el Bloq num
err_pam = error en la transacció pam err_pam = error en la transacció pam
err_pam_abort = transacció pam avortada err_pam_abort = transacció pam avortada
err_pam_acct_expired = compte expirat err_pam_acct_expired = compte expirat
err_pam_auth = error d'autenticació err_pam_auth = error d'autenticació
err_pam_authinfo_unavail = error al obtenir informació de l'usuari err_pam_authinfo_unavail = error en obtenir la informació de l'usuari
err_pam_authok_reqd = token expirat err_pam_authok_reqd = token expirat
err_pam_buf = error de la memòria intermitja err_pam_buf = error en la memòria intermèdia
err_pam_cred_err = error al establir les credencials err_pam_cred_err = error en establir les credencials
err_pam_cred_expired = credencials expirades err_pam_cred_expired = credencials expirades
err_pam_cred_insufficient = credencials insuficients err_pam_cred_insufficient = credencials insuficients
err_pam_cred_unavail = error al obtenir credencials err_pam_cred_unavail = error en obtenir credencials
err_pam_maxtries = s'ha assolit al màxim nombre d'intents err_pam_maxtries = s'ha assolit al nombre màxim d'intents
err_pam_perm_denied = permís denegat err_pam_perm_denied = permís denegat
err_pam_session = error de sessió err_pam_session = error de sessió
err_pam_sys = error de sistema err_pam_sys = error de sistema
err_pam_user_unknown = usuari desconegut err_pam_user_unknown = usuari desconegut
err_path = error al establir la ruta err_path = error en establir la ruta
err_perm_dir = error al canviar de directori actual err_perm_dir = error en canviar el directori actual
err_perm_group = error al degradar els permisos de grup err_perm_group = error en degradar els permisos de grup
err_perm_user = error al degradar els permisos de l'usuari err_perm_user = error en degradar els permisos de l'usuari
err_pwnam = error al obtenir la informació de l'usuari err_pwnam = error en obtenir la informació de l'usuari
err_user_gid = error al establir el GID de l'usuari err_unknown = ha ocorregut un error desconegut
err_user_init = error al inicialitzar usuari err_user_gid = error en establir el GID de l'usuari
err_user_uid = error al establir el UID de l'usuari err_user_init = error en inicialitzar usuari
err_xsessions_dir = error al cercar la carpeta de sessions err_user_uid = error en establir l'UID de l'usuari
err_xsessions_open = error al obrir la carpeta de sessions err_xauth = error en la comanda xauth
err_xcb_conn = error en la connexió xcb
err_xsessions_dir = error en trobar la carpeta de sessions
err_xsessions_open = error en obrir la carpeta de sessions
insert = inserir
login = iniciar sessió login = iniciar sessió
logout = tancar sessió logout = sessió tancada
no_x11_support = x11 support disabled at compile-time
no_x11_support = el suport per x11 ha estat desactivat en la compilació
normal = normal
numlock = Bloq Num numlock = Bloq Num
password = Clau password = Clau
restart = reiniciar restart = reiniciar
shell = shell shell = shell
shutdown = aturar shutdown = aturar
sleep = suspendre
wayland = wayland wayland = wayland
x11 = x11
xinitrc = xinitrc xinitrc = xinitrc

View File

@@ -35,6 +35,7 @@ err_perm_dir = failed to change current directory
err_perm_group = failed to downgrade group permissions err_perm_group = failed to downgrade group permissions
err_perm_user = failed to downgrade user permissions err_perm_user = failed to downgrade user permissions
err_pwnam = failed to get user info err_pwnam = failed to get user info
err_sleep = failed to execute sleep command
err_unknown = an unknown error occurred err_unknown = an unknown error occurred
err_user_gid = failed to set user GID err_user_gid = failed to set user GID
err_user_init = failed to initialize user err_user_init = failed to initialize user

View File

@@ -1,7 +1,5 @@
#%PAM-1.0 #%PAM-1.0
auth sufficient pam_unix.so try_first_pass likeauth nullok # empty-password will not pass this, but will not fail causing the next line to get executed
-auth sufficient pam_fprintd.so # We do not want to get errors when pam_fprintd.so is not present
auth include login auth include login
-auth optional pam_gnome_keyring.so -auth optional pam_gnome_keyring.so
-auth optional pam_kwallet5.so -auth optional pam_kwallet5.so

View File

@@ -50,35 +50,57 @@ pub fn realloc(self: *Doom) !void {
self.buffer = buffer; self.buffer = buffer;
} }
pub fn draw(self: Doom) void { pub fn drawWithUpdate(self: Doom) void {
for (0..self.terminal_buffer.width) |x| { for (0..self.terminal_buffer.width) |x| {
for (1..self.terminal_buffer.height) |y| { for (1..self.terminal_buffer.height) |y| {
// get source index
const source = y * self.terminal_buffer.width + x; const source = y * self.terminal_buffer.width + x;
// random number between 0 and 3 inclusive
const random = (self.terminal_buffer.random.int(u16) % 7) & 3; const random = (self.terminal_buffer.random.int(u16) % 7) & 3;
// adjust destination index based on random value
var dest = (source - @min(source, random)) + 1; var dest = (source - @min(source, random)) + 1;
if (self.terminal_buffer.width > dest) dest = 0 else dest -= self.terminal_buffer.width; if (self.terminal_buffer.width > dest) dest = 0 else dest -= self.terminal_buffer.width;
// get source intensity and destination offset
const buffer_source = self.buffer[source]; const buffer_source = self.buffer[source];
const buffer_dest_offset = random & 1; const buffer_dest_offset = random & 1;
if (buffer_source < buffer_dest_offset) continue; if (buffer_source < buffer_dest_offset) continue;
// calculate the destination intensity
var buffer_dest = buffer_source - buffer_dest_offset; var buffer_dest = buffer_source - buffer_dest_offset;
if (buffer_dest > 12) buffer_dest = 0; if (buffer_dest > 12) buffer_dest = 0;
self.buffer[dest] = @intCast(buffer_dest); self.buffer[dest] = @intCast(buffer_dest);
// update terminal
self.terminal_buffer.buffer[dest] = toTermboxCell(FIRE[buffer_dest]); self.terminal_buffer.buffer[dest] = toTermboxCell(FIRE[buffer_dest]);
self.terminal_buffer.buffer[source] = toTermboxCell(FIRE[buffer_source]); self.terminal_buffer.buffer[source] = toTermboxCell(FIRE[buffer_source]);
} }
} }
} }
fn initBuffer(buffer: []u8, width: usize) void { pub fn draw(self: Doom) void {
const length = buffer.len - width; for (0..self.terminal_buffer.width) |x| {
const slice_start = buffer[0..length]; for (1..self.terminal_buffer.height) |y| {
const slice_end = buffer[length..]; // get source index
const source = y * self.terminal_buffer.width + x;
// get intensity from buffer
const buffer_source = self.buffer[source];
// set cell to correct fire char
self.terminal_buffer.buffer[source] = toTermboxCell(FIRE[buffer_source]);
}
}
}
fn initBuffer(buffer: []u8, width: usize) void {
const slice_start = buffer[0..buffer.len];
const slice_end = buffer[buffer.len - width .. buffer.len];
// set cell initial values to 0, set bottom row to be fire sources
@memset(slice_start, 0); @memset(slice_start, 0);
@memset(slice_end, STEPS - 1); @memset(slice_end, STEPS - 1);
} }

View File

@@ -68,80 +68,83 @@ pub fn realloc(self: *Matrix) !void {
self.lines = lines; self.lines = lines;
} }
pub fn draw(self: *Matrix) void { pub fn drawWithUpdate(self: *Matrix) void {
const buf_height = self.terminal_buffer.height; const buf_height = self.terminal_buffer.height;
const buf_width = self.terminal_buffer.width; const buf_width = self.terminal_buffer.width;
self.count += 1;
if (self.count > FRAME_DELAY) {
self.frame += 1;
if (self.frame > 4) self.frame = 1;
self.count = 0;
var x: usize = 0; self.frame += 1;
while (x < self.terminal_buffer.width) : (x += 2) { if (self.frame > 4) self.frame = 1;
var tail: usize = 0;
var line = &self.lines[x];
if (self.frame <= line.update) continue;
if (self.dots[x].value == -1 and self.dots[self.terminal_buffer.width + x].value == ' ') { var x: usize = 0;
if (line.space > 0) { while (x < self.terminal_buffer.width) : (x += 2) {
line.space -= 1; var tail: usize = 0;
} else { var line = &self.lines[x];
const randint = self.terminal_buffer.random.int(i16); if (self.frame <= line.update) continue;
const h: isize = @intCast(self.terminal_buffer.height);
line.length = @mod(randint, h - 3) + 3;
self.dots[x].value = @mod(randint, MAX_CODEPOINT) + MIN_CODEPOINT;
line.space = @mod(randint, h + 1);
}
}
var y: usize = 0;
var first_col = true;
var seg_len: u64 = 0;
height_it: while (y <= buf_height) : (y += 1) {
var dot = &self.dots[buf_width * y + x];
// Skip over spaces
while (y <= buf_height and (dot.value == ' ' or dot.value == -1)) {
y += 1;
if (y > buf_height) break :height_it;
dot = &self.dots[buf_width * y + x];
}
// Find the head of this column
tail = y;
seg_len = 0;
while (y <= buf_height and dot.value != ' ' and dot.value != -1) {
dot.is_head = false;
if (MID_SCROLL_CHANGE) {
const randint = self.terminal_buffer.random.int(i16);
if (@mod(randint, 8) == 0) {
dot.value = @mod(randint, MAX_CODEPOINT) + MIN_CODEPOINT;
}
}
y += 1;
seg_len += 1;
// Head's down offscreen
if (y > buf_height) {
self.dots[buf_width * tail + x].value = ' ';
break :height_it;
}
dot = &self.dots[buf_width * y + x];
}
if (self.dots[x].value == -1 and self.dots[self.terminal_buffer.width + x].value == ' ') {
if (line.space > 0) {
line.space -= 1;
} else {
const randint = self.terminal_buffer.random.int(i16); const randint = self.terminal_buffer.random.int(i16);
dot.value = @mod(randint, MAX_CODEPOINT) + MIN_CODEPOINT; const h: isize = @intCast(self.terminal_buffer.height);
dot.is_head = true; line.length = @mod(randint, h - 3) + 3;
self.dots[x].value = @mod(randint, MAX_CODEPOINT) + MIN_CODEPOINT;
if (seg_len > line.length or !first_col) { line.space = @mod(randint, h + 1);
self.dots[buf_width * tail + x].value = ' ';
self.dots[x].value = -1;
}
first_col = false;
} }
} }
var y: usize = 0;
var first_col = true;
var seg_len: u64 = 0;
height_it: while (y <= buf_height) : (y += 1) {
var dot = &self.dots[buf_width * y + x];
// Skip over spaces
while (y <= buf_height and (dot.value == ' ' or dot.value == -1)) {
y += 1;
if (y > buf_height) break :height_it;
dot = &self.dots[buf_width * y + x];
}
// Find the head of this column
tail = y;
seg_len = 0;
while (y <= buf_height and dot.value != ' ' and dot.value != -1) {
dot.is_head = false;
if (MID_SCROLL_CHANGE) {
const randint = self.terminal_buffer.random.int(i16);
if (@mod(randint, 8) == 0) {
dot.value = @mod(randint, MAX_CODEPOINT) + MIN_CODEPOINT;
}
}
y += 1;
seg_len += 1;
// Head's down offscreen
if (y > buf_height) {
self.dots[buf_width * tail + x].value = ' ';
break :height_it;
}
dot = &self.dots[buf_width * y + x];
}
const randint = self.terminal_buffer.random.int(i16);
dot.value = @mod(randint, MAX_CODEPOINT) + MIN_CODEPOINT;
dot.is_head = true;
if (seg_len > line.length or !first_col) {
self.dots[buf_width * tail + x].value = ' ';
self.dots[x].value = -1;
}
first_col = false;
}
} }
self.draw();
}
pub fn draw(self: *Matrix) void {
const buf_width = self.terminal_buffer.width;
var x: usize = 0; var x: usize = 0;
while (x < buf_width) : (x += 2) { while (x < buf_width) : (x += 2) {
var y: usize = 1; var y: usize = 1;

View File

@@ -13,66 +13,6 @@ const utmp = interop.utmp;
const Utmp = utmp.utmpx; const Utmp = utmp.utmpx;
const SharedError = @import("SharedError.zig"); const SharedError = @import("SharedError.zig");
// When setting the currentLogin you must deallocate the previous currentLogin first
pub var currentLogin: ?[:0]const u8 = null;
pub var asyncPamHandle: ?*interop.pam.pam_handle = null;
pub var current_environment: ?Session.Environment = null;
fn environment_equals(e0: Session.Environment, e1: Session.Environment) bool {
if (!std.mem.eql(u8, e0.cmd, e1.cmd)) {
return false;
}
if (!std.mem.eql(u8, e0.name, e1.name)) {
return false;
}
if (!std.mem.eql(u8, e0.specifier, e1.specifier)) {
return false;
}
if (!(e0.xdg_desktop_names == null and e1.xdg_desktop_names == null) or
(e0.xdg_desktop_names != null and e1.xdg_desktop_names != null and !std.mem.eql(u8, e0.xdg_desktop_names.?, e1.xdg_desktop_names.?)))
{
return false;
}
if (!(e0.xdg_session_desktop == null and e1.xdg_session_desktop == null) or
(e0.xdg_session_desktop != null and e1.xdg_session_desktop != null and !std.mem.eql(u8, e0.xdg_session_desktop.?, e1.xdg_session_desktop.?)))
{
return false;
}
if (e0.display_server != e1.display_server) {
return false;
}
return true;
}
pub fn automaticLogin(config: Config, login: [:0]const u8, environment: Session.Environment, wakesem: *std.Thread.Semaphore) !void {
while (asyncPamHandle == null and currentLogin != null and std.mem.eql(u8, currentLogin.?, login)) {
if (authenticate(config, login, "", environment)) |handle| {
if (currentLogin != null and !std.mem.eql(u8, currentLogin.?, login) and environment_equals(current_environment.?, environment)) {
return;
}
asyncPamHandle = handle;
wakesem.post();
return;
} else |_| {}
}
}
pub fn startAutomaticLogin(allocator: std.mem.Allocator, config: Config, login: Text, environment: Session.Environment, wakesem: *std.Thread.Semaphore) !void {
if (currentLogin) |clogin| {
allocator.free(clogin);
currentLogin = null;
}
const login_text = try allocator.dupeZ(u8, login.text.items);
currentLogin = login_text;
var handle = try std.Thread.spawn(.{}, automaticLogin, .{
config,
login_text,
environment,
wakesem,
});
handle.detach();
}
var xorg_pid: std.posix.pid_t = 0; var xorg_pid: std.posix.pid_t = 0;
pub fn xorgSignalHandler(i: c_int) callconv(.C) void { pub fn xorgSignalHandler(i: c_int) callconv(.C) void {
if (xorg_pid > 0) _ = std.c.kill(xorg_pid, i); if (xorg_pid > 0) _ = std.c.kill(xorg_pid, i);
@@ -83,20 +23,16 @@ pub fn sessionSignalHandler(i: c_int) callconv(.C) void {
if (child_pid > 0) _ = std.c.kill(child_pid, i); if (child_pid > 0) _ = std.c.kill(child_pid, i);
} }
pub fn authenticate( pub fn authenticate(config: Config, current_environment: Session.Environment, login: [:0]const u8, password: [:0]const u8) !void {
config: Config, var tty_buffer: [3]u8 = undefined;
login: [:0]const u8,
password: [:0]const u8,
environment: Session.Environment,
) !*interop.pam.pam_handle {
var pam_tty_buffer: [6]u8 = undefined;
const pam_tty_str = try std.fmt.bufPrintZ(&pam_tty_buffer, "tty{d}", .{config.tty});
var tty_buffer: [2]u8 = undefined;
const tty_str = try std.fmt.bufPrintZ(&tty_buffer, "{d}", .{config.tty}); const tty_str = try std.fmt.bufPrintZ(&tty_buffer, "{d}", .{config.tty});
var pam_tty_buffer: [6]u8 = undefined;
const pam_tty_str = try std.fmt.bufPrintZ(&pam_tty_buffer, "tty{d}", .{config.tty});
// Set the XDG environment variables // Set the XDG environment variables
setXdgSessionEnv(environment.display_server); setXdgSessionEnv(current_environment.display_server);
try setXdgEnv(tty_str, environment.xdg_session_desktop orelse "", environment.xdg_desktop_names orelse ""); try setXdgEnv(tty_str, current_environment.xdg_session_desktop orelse "", current_environment.xdg_desktop_names orelse "");
// Open the PAM session // Open the PAM session
var credentials = [_:null]?[*:0]const u8{ login, password }; var credentials = [_:null]?[*:0]const u8{ login, password };
@@ -107,15 +43,15 @@ pub fn authenticate(
}; };
var handle: ?*interop.pam.pam_handle = undefined; var handle: ?*interop.pam.pam_handle = undefined;
// Do the PAM routine
var status = interop.pam.pam_start(config.service_name, null, &conv, &handle); var status = interop.pam.pam_start(config.service_name, null, &conv, &handle);
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status); if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
errdefer _ = interop.pam.pam_end(handle, status); defer _ = interop.pam.pam_end(handle, status);
// Set PAM_TTY as the current TTY. This is required in case it isn't being set by another PAM module // Set PAM_TTY as the current TTY. This is required in case it isn't being set by another PAM module
status = interop.pam.pam_set_item(handle, interop.pam.PAM_TTY, pam_tty_str.ptr); status = interop.pam.pam_set_item(handle, interop.pam.PAM_TTY, pam_tty_str.ptr);
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status); if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
// Do the PAM routine
status = interop.pam.pam_authenticate(handle, 0); status = interop.pam.pam_authenticate(handle, 0);
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status); if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
@@ -123,21 +59,12 @@ pub fn authenticate(
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status); if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
status = interop.pam.pam_setcred(handle, interop.pam.PAM_ESTABLISH_CRED); status = interop.pam.pam_setcred(handle, interop.pam.PAM_ESTABLISH_CRED);
errdefer _ = interop.pam.pam_end(handle, status);
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status); if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
return handle.?;
}
pub fn finaliseAuth(config: Config, environment: Session.Environment, handle: ?*interop.pam.pam_handle, login: [:0]const u8) !void {
var status: c_int = undefined;
defer status = interop.pam.pam_end(handle, status);
defer status = interop.pam.pam_setcred(handle, interop.pam.PAM_DELETE_CRED); defer status = interop.pam.pam_setcred(handle, interop.pam.PAM_DELETE_CRED);
// Open the PAM session
status = interop.pam.pam_open_session(handle, 0); status = interop.pam.pam_open_session(handle, 0);
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status); if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
defer status = interop.pam.pam_close_session(handle, status); defer status = interop.pam.pam_close_session(handle, 0);
var pwd: *interop.pwd.passwd = undefined; var pwd: *interop.pwd.passwd = undefined;
{ {
@@ -159,7 +86,7 @@ pub fn finaliseAuth(config: Config, environment: Session.Environment, handle: ?*
child_pid = try std.posix.fork(); child_pid = try std.posix.fork();
if (child_pid == 0) { if (child_pid == 0) {
startSession(config, pwd, handle, environment) catch |e| { startSession(config, pwd, handle, current_environment) catch |e| {
shared_err.writeError(e); shared_err.writeError(e);
std.process.exit(1); std.process.exit(1);
}; };
@@ -197,7 +124,7 @@ fn startSession(
config: Config, config: Config,
pwd: *interop.pwd.passwd, pwd: *interop.pwd.passwd,
handle: ?*interop.pam.pam_handle, handle: ?*interop.pam.pam_handle,
environment: Session.Environment, current_environment: Session.Environment,
) !void { ) !void {
if (builtin.os.tag == .freebsd) { if (builtin.os.tag == .freebsd) {
// FreeBSD has initgroups() in unistd // FreeBSD has initgroups() in unistd
@@ -229,13 +156,13 @@ fn startSession(
std.posix.chdirZ(pwd.pw_dir.?) catch return error.ChangeDirectoryFailed; std.posix.chdirZ(pwd.pw_dir.?) catch return error.ChangeDirectoryFailed;
// Execute what the user requested // Execute what the user requested
switch (environment.display_server) { switch (current_environment.display_server) {
.wayland => try executeWaylandCmd(pwd.pw_shell.?, config, environment.cmd), .wayland => try executeWaylandCmd(pwd.pw_shell.?, config, current_environment.cmd),
.shell => try executeShellCmd(pwd.pw_shell.?, config), .shell => try executeShellCmd(pwd.pw_shell.?, config),
.xinitrc, .x11 => if (build_options.enable_x11_support) { .xinitrc, .x11 => if (build_options.enable_x11_support) {
var vt_buf: [5]u8 = undefined; var vt_buf: [5]u8 = undefined;
const vt = try std.fmt.bufPrint(&vt_buf, "vt{d}", .{config.tty}); const vt = try std.fmt.bufPrint(&vt_buf, "vt{d}", .{config.tty});
try executeX11Cmd(pwd.pw_shell.?, pwd.pw_dir.?, config, environment.cmd, vt); try executeX11Cmd(pwd.pw_shell.?, pwd.pw_dir.?, config, current_environment.cmd, vt);
}, },
} }
} }

View File

@@ -8,6 +8,7 @@ const Bigclock = enums.Bigclock;
animation: Animation = .none, animation: Animation = .none,
animation_timeout_sec: u12 = 0, animation_timeout_sec: u12 = 0,
animation_refresh_ms: u16 = 16,
asterisk: ?u8 = '*', asterisk: ?u8 = '*',
auth_fails: u64 = 10, auth_fails: u64 = 10,
bg: u16 = 0, bg: u16 = 0,

View File

@@ -36,6 +36,7 @@ err_perm_dir: []const u8 = "failed to change current directory",
err_perm_group: []const u8 = "failed to downgrade group permissions", err_perm_group: []const u8 = "failed to downgrade group permissions",
err_perm_user: []const u8 = "failed to downgrade user permissions", err_perm_user: []const u8 = "failed to downgrade user permissions",
err_pwnam: []const u8 = "failed to get user info", err_pwnam: []const u8 = "failed to get user info",
err_sleep: []const u8 = "failed to execute sleep command",
err_unknown: []const u8 = "an unknown error occurred", err_unknown: []const u8 = "an unknown error occurred",
err_user_gid: []const u8 = "failed to set user GID", err_user_gid: []const u8 = "failed to set user GID",
err_user_init: []const u8 = "failed to initialize user", err_user_init: []const u8 = "failed to initialize user",

View File

@@ -39,16 +39,6 @@ pub fn signalHandler(i: c_int) callconv(.C) void {
std.c.exit(i); std.c.exit(i);
} }
fn eventThreadMain(event: *?termbox.tb_event, event_error: *c_int, timeout: *c_int, wake: *std.Thread.Semaphore, cont: *std.Thread.Semaphore) void {
while (true) {
cont.wait();
var e: termbox.tb_event = undefined;
event_error.* = termbox.tb_peek_event(&e, timeout.*);
event.* = e;
wake.post();
}
}
pub fn main() !void { pub fn main() !void {
var shutdown = false; var shutdown = false;
var restart = false; var restart = false;
@@ -327,6 +317,11 @@ pub fn main() !void {
} }
} }
var animation_timer = switch (config.animation) {
.none => undefined,
else => try std.time.Timer.start(),
};
const animate = config.animation != .none; const animate = config.animation != .none;
const shutdown_key = try std.fmt.parseInt(u8, config.shutdown_key[1..], 10); const shutdown_key = try std.fmt.parseInt(u8, config.shutdown_key[1..], 10);
const shutdown_len = try utils.strWidth(lang.shutdown); const shutdown_len = try utils.strWidth(lang.shutdown);
@@ -339,11 +334,7 @@ pub fn main() !void {
const brightness_up_key = try std.fmt.parseInt(u8, config.brightness_up_key[1..], 10); const brightness_up_key = try std.fmt.parseInt(u8, config.brightness_up_key[1..], 10);
const brightness_up_len = try utils.strWidth(lang.brightness_up); const brightness_up_len = try utils.strWidth(lang.brightness_up);
var event_timeout: c_int = std.math.maxInt(c_int); var event: termbox.tb_event = undefined;
var event_error: c_int = undefined;
var event: ?termbox.tb_event = null;
var wake: std.Thread.Semaphore = .{};
var doneEvent: std.Thread.Semaphore = .{};
var run = true; var run = true;
var update = true; var update = true;
var resolution_changed = false; var resolution_changed = false;
@@ -354,16 +345,6 @@ pub fn main() !void {
try info_line.addMessage(lang.err_console_dev, config.error_bg, config.error_fg); try info_line.addMessage(lang.err_console_dev, config.error_bg, config.error_fg);
}; };
doneEvent.post();
const thandle = try std.Thread.spawn(.{}, eventThreadMain, .{ &event, &event_error, &event_timeout, &wake, &doneEvent });
thandle.detach();
{
const current_environment = session.label.list.items[session.label.current];
try auth.startAutomaticLogin(allocator, config, login, current_environment, &wake);
}
defer allocator.free(auth.currentLogin.?);
while (run) { while (run) {
// If there's no input or there's an animation, a resolution change needs to be checked // If there's no input or there's an animation, a resolution change needs to be checked
if (!update or config.animation != .none) { if (!update or config.animation != .none) {
@@ -404,8 +385,22 @@ pub fn main() !void {
if (!animation_timed_out) { if (!animation_timed_out) {
switch (config.animation) { switch (config.animation) {
.none => {}, .none => {},
.doom => doom.draw(), .doom => {
.matrix => matrix.draw(), if (animation_timer.read() / std.time.ns_per_ms > config.animation_refresh_ms) {
animation_timer.reset();
doom.drawWithUpdate();
} else {
doom.draw();
}
},
.matrix => {
if (animation_timer.read() / std.time.ns_per_ms > config.animation_refresh_ms) {
animation_timer.reset();
matrix.drawWithUpdate();
} else {
matrix.draw();
}
},
} }
} }
@@ -550,9 +545,9 @@ pub fn main() !void {
_ = termbox.tb_present(); _ = termbox.tb_present();
} }
var timeout: u64 = std.math.maxInt(u64); var timeout: i32 = -1;
// Calculate the maximum timeout based on current animations, or the (big) clock. If there's none, we wait for 100ms instead // Calculate the maximum timeout based on current animations, or the (big) clock. If there's none, we wait for the event indefinitely instead
if (animate and !animation_timed_out) { if (animate and !animation_timed_out) {
timeout = config.min_refresh_delta; timeout = config.min_refresh_delta;
@@ -580,280 +575,226 @@ pub fn main() !void {
timeout = @intCast(1000 - @divTrunc(tv.tv_usec, 1000) + 1); timeout = @intCast(1000 - @divTrunc(tv.tv_usec, 1000) + 1);
} }
var skipEvent: bool = false; const event_error = if (timeout == -1) termbox.tb_poll_event(&event) else termbox.tb_peek_event(&event, timeout);
_ = wake.timedWait(timeout) catch .{};
if (auth.asyncPamHandle) |handle| {
var shared_err = try SharedError.init();
defer shared_err.deinit();
{ update = timeout != -1;
const login_text = try allocator.dupeZ(u8, login.text.items);
defer allocator.free(login_text);
try info_line.addMessage(lang.authenticating, config.error_bg, config.error_fg); if (event_error < 0 or event.type != termbox.TB_EVENT_KEY) continue;
switch (event.key) {
termbox.TB_KEY_ESC => {
if (config.vi_mode and insert_mode) {
insert_mode = false;
update = true;
}
},
termbox.TB_KEY_F12...termbox.TB_KEY_F1 => {
const pressed_key = 0xFFFF - event.key + 1;
if (pressed_key == shutdown_key) {
shutdown = true;
run = false;
} else if (pressed_key == restart_key) {
restart = true;
run = false;
} else if (pressed_key == sleep_key) {
if (config.sleep_cmd) |sleep_cmd| {
var sleep = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", sleep_cmd }, allocator);
sleep.stdout_behavior = .Ignore;
sleep.stderr_behavior = .Ignore;
handle_sleep_cmd: {
const process_result = sleep.spawnAndWait() catch {
break :handle_sleep_cmd;
};
if (process_result.Exited != 0) {
try info_line.addMessage(lang.err_sleep, config.error_bg, config.error_fg);
}
}
}
} else if (pressed_key == brightness_down_key or pressed_key == brightness_up_key) {
const cmd = if (pressed_key == brightness_down_key) config.brightness_down_cmd else config.brightness_up_cmd;
var brightness = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", cmd }, allocator);
brightness.stdout_behavior = .Ignore;
brightness.stderr_behavior = .Ignore;
handle_brightness_cmd: {
const process_result = brightness.spawnAndWait() catch {
break :handle_brightness_cmd;
};
if (process_result.Exited != 0) {
try info_line.addMessage(lang.err_brightness_change, config.error_bg, config.error_fg);
}
}
}
},
termbox.TB_KEY_CTRL_C => run = false,
termbox.TB_KEY_CTRL_U => {
if (active_input == .login) {
login.clear();
update = true;
} else if (active_input == .password) {
password.clear();
update = true;
}
},
termbox.TB_KEY_CTRL_K, termbox.TB_KEY_ARROW_UP => {
active_input = switch (active_input) {
.session, .info_line => .info_line,
.login => .session,
.password => .login,
};
update = true;
},
termbox.TB_KEY_CTRL_J, termbox.TB_KEY_ARROW_DOWN => {
active_input = switch (active_input) {
.info_line => .session,
.session => .login,
.login, .password => .password,
};
update = true;
},
termbox.TB_KEY_TAB => {
active_input = switch (active_input) {
.info_line => .session,
.session => .login,
.login => .password,
.password => .info_line,
};
update = true;
},
termbox.TB_KEY_BACK_TAB => {
active_input = switch (active_input) {
.info_line => .password,
.session => .info_line,
.login => .session,
.password => .login,
};
update = true;
},
termbox.TB_KEY_ENTER => {
try info_line.addMessage(lang.authenticating, config.bg, config.fg);
InfoLine.clearRendered(allocator, buffer) catch { InfoLine.clearRendered(allocator, buffer) catch {
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg); try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
}; };
info_line.label.draw(); info_line.label.draw();
_ = termbox.tb_present(); _ = termbox.tb_present();
session_pid = try std.posix.fork(); if (config.save) save_last_settings: {
if (session_pid == 0) { var file = std.fs.cwd().createFile(save_path, .{}) catch break :save_last_settings;
const current_environment = session.label.list.items[session.label.current]; defer file.close();
auth.finaliseAuth(config, current_environment, handle, login_text) catch |err| {
shared_err.writeError(err);
std.process.exit(1);
};
std.process.exit(0); const save_data = Save{
.user = login.text.items,
.session_index = session.label.current,
};
ini.writeFromStruct(save_data, file.writer(), null, true, .{}) catch break :save_last_settings;
// Delete previous save file if it exists
if (migrator.maybe_save_file) |path| std.fs.cwd().deleteFile(path) catch {};
} }
_ = std.posix.waitpid(session_pid, 0);
session_pid = -1; var shared_err = try SharedError.init();
} defer shared_err.deinit();
auth.asyncPamHandle = null; {
const login_text = try allocator.dupeZ(u8, login.text.items);
defer allocator.free(login_text);
const password_text = try allocator.dupeZ(u8, password.text.items);
defer allocator.free(password_text);
const auth_err = shared_err.readError(); // Give up TTY
if (auth_err) |err| { _ = termbox.tb_shutdown();
try info_line.addMessage(getAuthErrorMsg(err, lang), config.error_bg, config.error_fg);
// We don't want to start login back in instantly. The user must first edit
// the login/desktop in order to login. Only in case of a failed login (so not a logout)
// should we automatically restart it.
const current_environment = session.label.list.items[session.label.current];
try auth.startAutomaticLogin(allocator, config, login, current_environment, &wake);
} else {
try info_line.addMessage(lang.logout, config.error_bg, config.error_fg);
}
try std.posix.tcsetattr(std.posix.STDIN_FILENO, .FLUSH, tb_termios); session_pid = try std.posix.fork();
_ = termbox.tb_clear(); if (session_pid == 0) {
_ = termbox.tb_present();
update = true;
_ = termbox.tb_set_cursor(0, 0);
_ = termbox.tb_present();
} else if (event) |e| {
defer doneEvent.post();
update = timeout != -1;
skipEvent = event_error < 0;
if (skipEvent or e.type != termbox.TB_EVENT_KEY) continue;
switch (e.key) {
termbox.TB_KEY_ESC => {
if (config.vi_mode and insert_mode) {
insert_mode = false;
update = true;
}
},
termbox.TB_KEY_F12...termbox.TB_KEY_F1 => {
const pressed_key = 0xFFFF - e.key + 1;
if (pressed_key == shutdown_key) {
shutdown = true;
run = false;
} else if (pressed_key == restart_key) {
restart = true;
run = false;
} else if (pressed_key == sleep_key) {
if (config.sleep_cmd) |sleep_cmd| {
var sleep = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", sleep_cmd }, allocator);
_ = sleep.spawnAndWait() catch .{};
}
} else if (pressed_key == brightness_down_key) {
var brightness = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", config.brightness_down_cmd }, allocator);
_ = brightness.spawnAndWait() catch .{};
} else if (pressed_key == brightness_up_key) {
var brightness = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", config.brightness_up_cmd }, allocator);
_ = brightness.spawnAndWait() catch .{};
}
},
termbox.TB_KEY_CTRL_C => run = false,
termbox.TB_KEY_CTRL_U => {
if (active_input == .login) {
login.clear();
update = true;
} else if (active_input == .password) {
password.clear();
update = true;
}
},
termbox.TB_KEY_CTRL_K, termbox.TB_KEY_ARROW_UP => {
active_input = switch (active_input) {
.session, .info_line => .info_line,
.login => .session,
.password => .login,
};
update = true;
},
termbox.TB_KEY_CTRL_J, termbox.TB_KEY_ARROW_DOWN => {
if (active_input == .login) {
const current_environment = session.label.list.items[session.label.current]; const current_environment = session.label.list.items[session.label.current];
try auth.startAutomaticLogin(allocator, config, login, current_environment, &wake); auth.authenticate(config, current_environment, login_text, password_text) catch |err| {
}
active_input = switch (active_input) {
.info_line => .session,
.session => .login,
.login, .password => .password,
};
update = true;
},
termbox.TB_KEY_TAB => {
if (active_input == .login) {
const current_environment = session.label.list.items[session.label.current];
try auth.startAutomaticLogin(allocator, config, login, current_environment, &wake);
}
active_input = switch (active_input) {
.info_line => .session,
.session => .login,
.login => .password,
.password => .info_line,
};
update = true;
},
termbox.TB_KEY_BACK_TAB => {
if (active_input == .info_line) {
const current_environment = session.label.list.items[session.label.current];
try auth.startAutomaticLogin(allocator, config, login, current_environment, &wake);
}
active_input = switch (active_input) {
.info_line => .password,
.session => .info_line,
.login => .session,
.password => .login,
};
update = true;
},
termbox.TB_KEY_ENTER => {
try info_line.addMessage(lang.authenticating, config.bg, config.fg);
InfoLine.clearRendered(allocator, buffer) catch {
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
};
info_line.label.draw();
_ = termbox.tb_present();
if (config.save) save_last_settings: {
var file = std.fs.cwd().createFile(save_path, .{}) catch break :save_last_settings;
defer file.close();
const save_data = Save{
.user = login.text.items,
.session_index = session.label.current,
};
ini.writeFromStruct(save_data, file.writer(), null, true, .{}) catch break :save_last_settings;
// Delete previous save file if it exists
if (migrator.maybe_save_file) |path| std.fs.cwd().deleteFile(path) catch {};
}
var shared_err = try SharedError.init();
defer shared_err.deinit();
{
const login_text = try allocator.dupeZ(u8, login.text.items);
defer allocator.free(login_text);
const password_text = try allocator.dupeZ(u8, password.text.items);
defer allocator.free(password_text);
// Give up control on the TTY
_ = termbox.tb_shutdown();
session_pid = try std.posix.fork();
const current_environment = session.label.list.items[session.label.current];
if (auth.authenticate(config, login_text, password_text, current_environment)) |handle| {
if (session_pid == 0) {
auth.finaliseAuth(config, current_environment, handle, login_text) catch |err| {
shared_err.writeError(err);
std.process.exit(1);
};
std.process.exit(0);
}
_ = std.posix.waitpid(session_pid, 0);
} else |err| {
shared_err.writeError(err); shared_err.writeError(err);
} std.process.exit(1);
};
session_pid = -1; std.process.exit(0);
} }
// Take back control of the TTY _ = std.posix.waitpid(session_pid, 0);
_ = termbox.tb_init(); session_pid = -1;
_ = termbox.tb_set_output_mode(termbox.TB_OUTPUT_NORMAL); }
const auth_err = shared_err.readError(); // Take back TTY
if (auth_err) |err| { _ = termbox.tb_init();
auth_fails += 1; _ = termbox.tb_set_output_mode(termbox.TB_OUTPUT_NORMAL);
active_input = .password;
try info_line.addMessage(getAuthErrorMsg(err, lang), config.error_bg, config.error_fg);
if (config.clear_password or err != error.PamAuthError) password.clear();
} else {
if (config.logout_cmd) |logout_cmd| {
var logout_process = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", logout_cmd }, allocator);
_ = logout_process.spawnAndWait() catch .{};
}
password.clear(); // Reinitialise buffer to avoid use after free
try info_line.addMessage(lang.logout, config.bg, config.fg); buffer = TerminalBuffer.init(config, labels_max_length, random);
const auth_err = shared_err.readError();
if (auth_err) |err| {
auth_fails += 1;
active_input = .password;
try info_line.addMessage(getAuthErrorMsg(err, lang), config.error_bg, config.error_fg);
if (config.clear_password or err != error.PamAuthError) password.clear();
} else {
if (config.logout_cmd) |logout_cmd| {
var logout_process = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", logout_cmd }, allocator);
_ = logout_process.spawnAndWait() catch .{};
} }
try std.posix.tcsetattr(std.posix.STDIN_FILENO, .FLUSH, tb_termios); password.clear();
if (auth_fails < config.auth_fails) _ = termbox.tb_clear(); try info_line.addMessage(lang.logout, config.bg, config.fg);
}
update = true; try std.posix.tcsetattr(std.posix.STDIN_FILENO, .FLUSH, tb_termios);
if (auth_fails < config.auth_fails) _ = termbox.tb_clear();
// Restore the cursor update = true;
_ = termbox.tb_set_cursor(0, 0);
_ = termbox.tb_present();
},
else => {
if (!insert_mode) {
switch (e.ch) {
'k' => {
active_input = switch (active_input) {
.session, .info_line => .info_line,
.login => .session,
.password => .login,
};
update = true;
continue;
},
'j' => {
if (active_input == .login) {
const current_environment = session.label.list.items[session.label.current];
try auth.startAutomaticLogin(allocator, config, login, current_environment, &wake);
}
active_input = switch (active_input) {
.info_line => .session,
.session => .login,
.login, .password => .password,
};
update = true;
continue;
},
'i' => {
insert_mode = true;
update = true;
continue;
},
else => {},
}
}
switch (active_input) { // Restore the cursor
.info_line => info_line.label.handle(&event.?, insert_mode), _ = termbox.tb_set_cursor(0, 0);
.session => session.label.handle(&event.?, insert_mode), _ = termbox.tb_present();
.login => login.handle(&event.?, insert_mode) catch { },
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg); else => {
if (!insert_mode) {
switch (event.ch) {
'k' => {
active_input = switch (active_input) {
.session, .info_line => .info_line,
.login => .session,
.password => .login,
};
update = true;
continue;
}, },
.password => password.handle(&event.?, insert_mode) catch { 'j' => {
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg); active_input = switch (active_input) {
.info_line => .session,
.session => .login,
.login, .password => .password,
};
update = true;
continue;
}, },
'i' => {
insert_mode = true;
update = true;
continue;
},
else => {},
} }
update = true; }
},
} switch (active_input) {
.info_line => info_line.label.handle(&event, insert_mode),
.session => session.label.handle(&event, insert_mode),
.login => login.handle(&event, insert_mode) catch {
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
},
.password => password.handle(&event, insert_mode) catch {
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
},
}
update = true;
},
} }
} }
} }