Everything

This commit is contained in:
Kinzie 2024-03-23 14:09:23 +00:00
parent d6fc781192
commit b0ad7a611a
14 changed files with 872 additions and 482 deletions

View File

@ -3,12 +3,12 @@
.version = "1.0.0", .version = "1.0.0",
.dependencies = .{ .dependencies = .{
.ini = .{ .ini = .{
.url = "https://github.com/AnErrupTion/zig-ini/archive/7ab77196a4dc63d1ede50e0a1af1a8325c152f2f.tar.gz", .url = "https://github.com/ziglibs/ini/archive/2b11e8fef86d0eefb225156e695be1c1d5c35cbc.tar.gz",
.hash = "12204b1d133060dd0c4304d5e0fbdb33a70038118e7c112b14b5f8d176ea15cd5939", .hash = "1220ed24f7dda09121a175601ddd5c86c1cc937d061a97d38aef049ee4af76e2f594",
}, },
.clap = .{ .clap = .{
.url = "https://github.com/Hejsil/zig-clap/archive/f49b94700e0761b7514abdca0e4f0e7f3f938a93.tar.gz", .url = "https://github.com/Hejsil/zig-clap/archive/f49b94700e0761b7514abdca0e4f0e7f3f938a93.tar.gz",
.hash = "1220f48518ce22882e102255ed3bcdb7aeeb4891f50b2cdd3bd74b5b2e24d3149ba2", .hash = "1220f48518ce22882e102255ed3bcdb7aeeb4891f50b2cdd3bd74b5b2e24d3149ba2",
}, },
} },
} }

View File

@ -1,22 +1,20 @@
[ly]
# The active animation # The active animation
# none -> Nothing (default) # none -> Nothing (default)
# doom -> PSX DOOM fire # doom -> PSX DOOM fire
# matrix -> CMatrix # matrix -> CMatrix
animation = doom animation = none
# Format string for clock in top right corner (see strftime specification) # Format string for clock in top right corner (see strftime specification). Example: %c
clock = %c clock = null
# Enable/disable big clock # Enable/disable big clock
bigclock = true bigclock = false
# The character used to mask the password # The character used to mask the password
asterisk = * asterisk = *
# Erase password input on failure # Erase password input on failure
blank_password = false clear_password = false
# The `fg` and `bg` color settings take a digit 0-8 corresponding to: # The `fg` and `bg` color settings take a digit 0-8 corresponding to:
#define TB_DEFAULT 0x00 #define TB_DEFAULT 0x00
@ -43,6 +41,9 @@ bg = 0
# Foreground color id # Foreground color id
fg = 8 fg = 8
# Border color
border_fg = 8
# Blank main box background # Blank main box background
# Setting to false will make it transparent # Setting to false will make it transparent
blank_box = true blank_box = true
@ -62,7 +63,6 @@ max_desktop_len = 100
max_login_len = 255 max_login_len = 255
max_password_len = 255 max_password_len = 255
# Input box active by default on startup # Input box active by default on startup
# Available inputs: session, login, password # Available inputs: session, login, password
default_input = login default_input = login
@ -76,7 +76,6 @@ save = true
# File in which to save and load the default desktop and login # File in which to save and load the default desktop and login
save_file = /etc/ly/save save_file = /etc/ly/save
# Remove power management command hints # Remove power management command hints
hide_key_hints = false hide_key_hints = false
@ -86,28 +85,30 @@ shutdown_key = F1
# Specifies the key used for restart (F1-F12) # Specifies the key used for restart (F1-F12)
restart_key = F2 restart_key = F2
sleep_key = F3
# Command executed when pressing shutdown_key # Command executed when pressing shutdown_key
shutdown_cmd = /sbin/shutdown -a now shutdown_cmd = /sbin/shutdown -a now
# Command executed when pressing restart_key # Command executed when pressing restart_key
restart_cmd = /sbin/shutdown -r now restart_cmd = /sbin/shutdown -r now
# Command executed when pressing sleep key (can be null)
sleep_cmd = null
# Active language # Active language
# Available languages are found in /etc/ly/lang/ # Available languages are found in /etc/ly/lang/
lang = en lang = en
# TTY in use # TTY in use
tty = 2 tty = 2
# Console path # Console path
console_dev = /dev/console console_dev = /dev/console
# Default path # Default path. If null, ly doesn't set a path.
path = /sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin path = /sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin
# Event timeout in milliseconds # Event timeout in milliseconds
min_refresh_delta = 5 min_refresh_delta = 5
@ -123,15 +124,13 @@ term_restore_cursor_cmd = /usr/bin/tput cnorm
# Cookie generator # Cookie generator
mcookie_cmd = /usr/bin/mcookie mcookie_cmd = /usr/bin/mcookie
# Wayland setup command # Wayland setup command
wayland_cmd = /etc/ly/wsetup.sh wayland_cmd = /etc/ly/wsetup.sh
# Wayland desktop environments # Wayland desktop environments
waylandsessions = /usr/share/wayland-sessions waylandsessions = /usr/share/wayland-sessions
# xinitrc (hidden if null)
# xinitrc
xinitrc = ~/.xinitrc xinitrc = ~/.xinitrc
# Xorg server command # Xorg server command

View File

@ -2,6 +2,7 @@
Description=TUI display manager Description=TUI display manager
After=systemd-user-sessions.service plymouth-quit-wait.service After=systemd-user-sessions.service plymouth-quit-wait.service
After=getty@tty2.service After=getty@tty2.service
Conflicts=getty@tty2.service
[Service] [Service]
Type=idle Type=idle

View File

@ -66,102 +66,92 @@ pub fn realloc(self: *Matrix) !void {
self.lines = lines; self.lines = lines;
} }
// TODO: Fix!!
pub fn draw(self: *Matrix) void { pub fn draw(self: *Matrix) void {
var first_column = false; const buf_height = self.terminal_buffer.height;
const buf_width = self.terminal_buffer.width;
self.count += 1; self.count += 1;
if (self.count > FRAME_DELAY) { if (self.count > FRAME_DELAY) {
self.frame += 1; self.frame += 1;
if (self.frame > 4) self.frame = 1; if (self.frame > 4) self.frame = 1;
self.count = 0; self.count = 0;
var x: u64 = 0; var j: u64 = 0;
while (x < self.terminal_buffer.width) : (x += 2) { while (j < self.terminal_buffer.width) : (j += 2) {
var line = self.lines[x];
if (self.frame <= line.update) continue;
var tail: u64 = 0; var tail: u64 = 0;
if (self.dots[x].value == -1 and self.dots[self.terminal_buffer.width + x].value == ' ') { if (self.frame > self.lines[j].update) {
if (line.space <= 0) { if (self.dots[j].value == -1 and self.dots[self.terminal_buffer.width + j].value == ' ') {
const random = self.terminal_buffer.random.int(i16); if (self.lines[j].space > 0) {
const h: isize = @intCast(self.terminal_buffer.height); self.lines[j].space -= 1;
line.length = @mod(random, h - 3) + 3;
line.space = @mod(random, h) + 1;
self.dots[x].value = @mod(random, MAX_CODEPOINT) + MIN_CODEPOINT;
} else { } else {
line.space -= 1; const randint = self.terminal_buffer.random.int(i16);
const h: isize = @intCast(self.terminal_buffer.height);
self.lines[j].length = @mod(randint, h - 3) + 3;
self.dots[j].value = @mod(randint, MAX_CODEPOINT) + MIN_CODEPOINT;
self.lines[j].space = @mod(randint, h + 1);
}
} }
self.lines[x] = line; var i: u64 = 0;
first_column = true; var first_col = true;
var seg_len: u64 = 0;
var y: u64 = 0; while (i <= buf_height) {
var seg_length: u64 = 0;
while (y <= self.terminal_buffer.height) : (y += 1) {
// TODO: Are all these y/height checks required?
var dot = self.dots[y * self.terminal_buffer.width + x];
// Skip over spaces // Skip over spaces
while (dot.value == ' ' or dot.value == -1) { while (i <= buf_height and (self.dots[buf_width * i + j].value == ' ' or self.dots[buf_width * i + j].value == -1)) {
y += 1; i += 1;
if (y > self.terminal_buffer.height) break;
dot = self.dots[y * self.terminal_buffer.width + x];
} }
if (y > self.terminal_buffer.height) break;
// Find the head of this column if (i > buf_height) break;
tail = y;
seg_length = 0; // Find the head of this col
while (y <= self.terminal_buffer.height and dot.value != ' ' and dot.value != -1) { tail = i;
dot.is_head = false; seg_len = 0;
while (i <= buf_height and (self.dots[buf_width * i + j].value != ' ' and self.dots[buf_width * i + j].value != -1)) {
self.dots[buf_width * i + j].is_head = false;
if (MID_SCROLL_CHANGE) { if (MID_SCROLL_CHANGE) {
const random = self.terminal_buffer.random.int(i16); const randint = self.terminal_buffer.random.int(i16);
if (@mod(random, 8) == 0) dot.value = @mod(random, MAX_CODEPOINT) + MIN_CODEPOINT; if (@mod(randint, 8) == 0)
self.dots[buf_width * i + j].value = @mod(randint, MAX_CODEPOINT) + MIN_CODEPOINT;
} }
self.dots[y * self.terminal_buffer.width + x] = dot; i += 1;
seg_len += 1;
y += 1;
seg_length += 1;
dot = self.dots[y * self.terminal_buffer.width + x];
} }
// The head is down offscreen // Head's down offscreen
if (y > self.terminal_buffer.height) { if (i > buf_height) {
self.dots[tail * self.terminal_buffer.width + x].value = ' '; self.dots[buf_width * tail + j].value = ' ';
continue; // TODO: Shouldn't this be break? continue;
} }
const random = self.terminal_buffer.random.int(i16); const randint = self.terminal_buffer.random.int(i16);
self.dots[y * self.terminal_buffer.width + x].value = @mod(random, MAX_CODEPOINT) + MIN_CODEPOINT; self.dots[buf_width * i + j].value = @mod(randint, MAX_CODEPOINT) + MIN_CODEPOINT;
self.dots[y * self.terminal_buffer.width + x].is_head = true; self.dots[buf_width * i + j].is_head = true;
if (seg_length > line.length or !first_column) { if (seg_len > self.lines[j].length or !first_col) {
self.dots[tail * self.terminal_buffer.width + x].value = ' '; self.dots[buf_width * tail + j].value = ' ';
self.dots[x].value = -1; self.dots[j].value = -1;
} }
first_column = false; first_col = false;
i += 1;
} }
} }
} }
} }
var x: u64 = 0; // Fine
while (x < self.terminal_buffer.width) : (x += 2) { var j: u64 = 0;
var y: u64 = 1; while (j < buf_width) : (j += 2) {
while (y <= self.terminal_buffer.height) : (y += 1) { var i: u64 = 1;
const dot = self.dots[y * self.terminal_buffer.width + x]; while (i <= self.terminal_buffer.height) : (i += 1) {
const dot = self.dots[buf_width * i + j];
var fg: u32 = @intCast(termbox.TB_GREEN); var fg: u32 = @intCast(termbox.TB_GREEN);
if (dot.value == -1 or dot.value == ' ') { if (dot.value == -1 or dot.value == ' ') {
termbox.tb_change_cell(@intCast(x), @intCast(y - 1), ' ', fg, termbox.TB_DEFAULT); termbox.tb_change_cell(@intCast(j), @intCast(i - 1), ' ', fg, termbox.TB_DEFAULT);
continue; continue;
} }
if (dot.is_head) fg = @intCast(termbox.TB_WHITE | termbox.TB_BOLD); if (dot.is_head) fg = @intCast(termbox.TB_WHITE | termbox.TB_BOLD);
termbox.tb_change_cell(@intCast(x), @intCast(y - 1), @intCast(dot.value), fg, termbox.TB_DEFAULT); termbox.tb_change_cell(@intCast(j), @intCast(i - 1), @intCast(dot.value), fg, termbox.TB_DEFAULT);
} }
} }
} }

View File

@ -4,35 +4,26 @@ const interop = @import("interop.zig");
const TerminalBuffer = @import("tui/TerminalBuffer.zig"); const TerminalBuffer = @import("tui/TerminalBuffer.zig");
const Desktop = @import("tui/components/Desktop.zig"); const Desktop = @import("tui/components/Desktop.zig");
const Text = @import("tui/components/Text.zig"); const Text = @import("tui/components/Text.zig");
const Config = @import("config/Config.zig");
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const utmp = interop.utmp;
var login_conv_allocator: Allocator = undefined; const Utmp = utmp.utmp;
//const LogFile = @import("logger/LogFile.zig");
pub fn authenticate( pub fn authenticate(
allocator: Allocator, allocator: Allocator,
tty: u8, config: Config,
desktop: Desktop, desktop: Desktop,
login: Text, login: Text,
password: *Text, password: *Text,
service_name: []const u8,
path: []const u8,
term_reset_cmd: []const u8,
wayland_cmd: []const u8,
) !void { ) !void {
login_conv_allocator = allocator;
const uid = interop.getuid();
var tty_buffer = std.mem.zeroes([@sizeOf(u8) + 1]u8); var tty_buffer = std.mem.zeroes([@sizeOf(u8) + 1]u8);
var uid_buffer = std.mem.zeroes([10 + @sizeOf(u32) + 1]u8); const tty_str = try std.fmt.bufPrintZ(&tty_buffer, "{d}", .{config.tty});
const tty_str = try std.fmt.bufPrintZ(&tty_buffer, "{d}", .{tty});
const uid_str = try std.fmt.bufPrintZ(&uid_buffer, "/run/user/{d}", .{uid});
const current_environment = desktop.environments.items[desktop.current]; const current_environment = desktop.environments.items[desktop.current];
// Set the XDG environment variables // Set the XDG environment variables
setXdgSessionEnv(current_environment.display_server); setXdgSessionEnv(current_environment.display_server);
try setXdgEnv(allocator, tty_str, uid_str, current_environment.xdg_name); try setXdgEnv(allocator, tty_str, current_environment.xdg_name);
// Open the PAM session // Open the PAM session
const login_text_z = try allocator.dupeZ(u8, login.text.items); const login_text_z = try allocator.dupeZ(u8, login.text.items);
@ -53,7 +44,7 @@ pub fn authenticate(
}; };
var handle: ?*interop.pam.pam_handle = undefined; var handle: ?*interop.pam.pam_handle = undefined;
const service_name_z = try allocator.dupeZ(u8, service_name); const service_name_z = try allocator.dupeZ(u8, config.service_name);
defer allocator.free(service_name_z); defer allocator.free(service_name_z);
var status = interop.pam.pam_start(service_name_z.ptr, null, &conv, &handle); var status = interop.pam.pam_start(service_name_z.ptr, null, &conv, &handle);
@ -122,11 +113,11 @@ pub fn authenticate(
if (status != 0) return error.SetUserUidFailed; if (status != 0) return error.SetUserUidFailed;
// Set up the environment (this clears the currently set one) // Set up the environment (this clears the currently set one)
try initEnv(allocator, pwd, path); try initEnv(allocator, pwd, config.path);
// Reset the XDG environment variables from before // Reset the XDG environment variables from before
setXdgSessionEnv(current_environment.display_server); setXdgSessionEnv(current_environment.display_server);
try setXdgEnv(allocator, tty_str, uid_str, current_environment.xdg_name); try setXdgEnv(allocator, tty_str, current_environment.xdg_name);
// Set the PAM variables // Set the PAM variables
const pam_env_vars = interop.pam.pam_getenvlist(handle); const pam_env_vars = interop.pam.pam_getenvlist(handle);
@ -143,33 +134,34 @@ pub fn authenticate(
status = interop.chdir(pwd.pw_dir); status = interop.chdir(pwd.pw_dir);
if (status != 0) return error.ChangeDirectoryFailed; if (status != 0) return error.ChangeDirectoryFailed;
try resetTerminal(allocator, pwd.pw_shell, term_reset_cmd); try resetTerminal(allocator, pwd.pw_shell, config.term_reset_cmd);
switch (current_environment.display_server) { switch (current_environment.display_server) {
.wayland => try executeWaylandCmd(pwd.pw_shell, wayland_cmd, current_environment.cmd), .wayland => try executeWaylandCmd(pwd.pw_shell, config.wayland_cmd, current_environment.cmd),
.shell => executeShellCmd(pwd.pw_shell), .shell => executeShellCmd(pwd.pw_shell),
.xinitrc, .x11 => { .xinitrc, .x11 => {
// TODO var vt_buf: [5]u8 = undefined;
const vt = try std.fmt.bufPrint(&vt_buf, "vt{d}", .{config.tty});
try executeX11Cmd(pwd.pw_shell, pwd.pw_dir, config.x_cmd, config.x_cmd_setup, current_environment.cmd, config.xauth_cmd, config.mcookie_cmd, vt);
}, },
} }
std.os.exit(0); std.os.exit(0);
} }
var entry: Utmp = std.mem.zeroes(Utmp);
// TODO: Add UTMP entry try addUtmpEntry(&entry, pwd.pw_name, pid);
// Wait for the session to stop // Wait for the session to stop
_ = std.c.waitpid(pid, &status, 0); _ = std.c.waitpid(pid, &status, 0);
// TODO: Remove UTMP entry
try resetTerminal(allocator, pwd.pw_shell, term_reset_cmd); removeUtmpEntry(&entry);
try resetTerminal(allocator, pwd.pw_shell, config.term_reset_cmd);
// Re-initialize termbox // Re-initialize termbox
_ = interop.termbox.tb_init(); _ = interop.termbox.tb_init();
_ = interop.termbox.tb_select_output_mode(interop.termbox.TB_OUTPUT_NORMAL); _ = interop.termbox.tb_select_output_mode(interop.termbox.TB_OUTPUT_NORMAL);
// TODO: Reload the DE list on log out
// Close the PAM session // Close the PAM session
status = interop.pam.pam_close_session(handle, 0); status = interop.pam.pam_close_session(handle, 0);
if (status != 0) return pamDiagnose(status); if (status != 0) return pamDiagnose(status);
@ -178,23 +170,27 @@ pub fn authenticate(
if (status != 0) return pamDiagnose(status); if (status != 0) return pamDiagnose(status);
} }
fn initEnv(allocator: Allocator, pwd: *interop.passwd, path: []const u8) !void { fn initEnv(allocator: Allocator, pwd: *interop.passwd, path: ?[]const u8) !void {
const term = interop.getenv("TERM"); const term = interop.getenv("TERM");
const lang = interop.getenv("LANG"); //const lang = interop.getenv("LANG");
if (term[0] == 0) _ = interop.setenv("TERM", "linux", 1); _ = interop.clearenv();
if (lang[0] == 0) _ = interop.setenv("LANG", "C", 1);
if (term[0] != 0) _ = interop.setenv("TERM", term, 1);
//if (lang[0] != 0) _ = interop.setenv("LANG", lang, 1);
_ = interop.setenv("HOME", pwd.pw_dir, 1); _ = interop.setenv("HOME", pwd.pw_dir, 1);
_ = interop.setenv("PWD", pwd.pw_dir, 1); _ = interop.setenv("PWD", pwd.pw_dir, 1);
_ = interop.setenv("SHELL", pwd.pw_shell, 1); _ = interop.setenv("SHELL", pwd.pw_shell, 1);
_ = interop.setenv("USER", pwd.pw_name, 1); _ = interop.setenv("USER", pwd.pw_name, 1);
_ = interop.setenv("LOGNAME", pwd.pw_name, 1); _ = interop.setenv("LOGNAME", pwd.pw_name, 1);
const path_z = try allocator.dupeZ(u8, path); if (path != null) {
const path_z = try allocator.dupeZ(u8, path.?);
defer allocator.free(path_z); defer allocator.free(path_z);
const status = interop.setenv("PATH", path_z, 1); const status = interop.setenv("PATH", path_z, 1);
if (status != 0) return error.SetPathFailed; if (status != 0) return error.SetPathFailed;
}
} }
fn setXdgSessionEnv(display_server: enums.DisplayServer) void { fn setXdgSessionEnv(display_server: enums.DisplayServer) void {
@ -205,10 +201,15 @@ fn setXdgSessionEnv(display_server: enums.DisplayServer) void {
}, 0); }, 0);
} }
fn setXdgEnv(allocator: Allocator, tty_str: [:0]u8, uid_str: [:0]u8, desktop_name: []const u8) !void { fn setXdgEnv(allocator: Allocator, tty_str: [:0]u8, desktop_name: []const u8) !void {
const desktop_name_z = try allocator.dupeZ(u8, desktop_name); const desktop_name_z = try allocator.dupeZ(u8, desktop_name);
defer allocator.free(desktop_name_z); defer allocator.free(desktop_name_z);
const uid = interop.getuid();
var uid_buffer = std.mem.zeroes([10 + @sizeOf(u32) + 1]u8);
const uid_str = try std.fmt.bufPrintZ(&uid_buffer, "/run/user/{d}", .{uid});
_ = interop.setenv("XDG_CURRENT_DESKTOP", desktop_name_z.ptr, 0);
_ = interop.setenv("XDG_RUNTIME_DIR", uid_str.ptr, 0); _ = interop.setenv("XDG_RUNTIME_DIR", uid_str.ptr, 0);
_ = interop.setenv("XDG_SESSION_CLASS", "user", 0); _ = interop.setenv("XDG_SESSION_CLASS", "user", 0);
_ = interop.setenv("XDG_SESSION_ID", "1", 0); _ = interop.setenv("XDG_SESSION_ID", "1", 0);
@ -226,28 +227,24 @@ fn loginConv(
const message_count: u32 = @intCast(num_msg); const message_count: u32 = @intCast(num_msg);
const messages = msg.?; const messages = msg.?;
const response = login_conv_allocator.alloc(interop.pam.pam_response, message_count) catch return interop.pam.PAM_BUF_ERR; var allocator = std.heap.raw_c_allocator;
defer login_conv_allocator.free(response); const response = allocator.alloc(interop.pam.pam_response, message_count) catch return interop.pam.PAM_BUF_ERR;
var status: c_int = undefined; var username: ?[:0]u8 = null;
var password: ?[:0]u8 = null;
var status: c_int = interop.pam.PAM_SUCCESS;
for (0..message_count) |i| set_credentials: { for (0..message_count) |i| set_credentials: {
switch (messages[i].?.msg_style) { switch (messages[i].?.msg_style) {
// TODO: Potentially cast appdata pointer before so we only do it once
// TODO: Verify if we need to do string duplication here
interop.pam.PAM_PROMPT_ECHO_ON => { interop.pam.PAM_PROMPT_ECHO_ON => {
const appdata: ?*align(8) anyopaque = @alignCast(appdata_ptr); const data: [*][*:0]u8 = @ptrCast(@alignCast(appdata_ptr));
const data: [*][:0]u8 = @ptrCast(appdata.?); username = allocator.dupeZ(u8, std.mem.span(data[0])) catch return interop.pam.PAM_BUF_ERR;
const username = data[0]; response[i].resp = username.?.ptr;
response[i].resp = username;
}, },
interop.pam.PAM_PROMPT_ECHO_OFF => { interop.pam.PAM_PROMPT_ECHO_OFF => {
const appdata: ?*align(8) anyopaque = @alignCast(appdata_ptr); const data: [*][*:0]u8 = @ptrCast(@alignCast(appdata_ptr));
const data: [*][:0]u8 = @ptrCast(appdata.?); password = allocator.dupeZ(u8, std.mem.span(data[1])) catch return interop.pam.PAM_BUF_ERR;
const password = data[1]; response[i].resp = password.?.ptr;
response[i].resp = password;
}, },
interop.pam.PAM_ERROR_MSG => { interop.pam.PAM_ERROR_MSG => {
status = interop.pam.PAM_CONV_ERR; status = interop.pam.PAM_CONV_ERR;
@ -257,7 +254,14 @@ fn loginConv(
} }
} }
if (status == interop.pam.PAM_SUCCESS) resp.?.* = response.ptr; if (status != interop.pam.PAM_SUCCESS) {
// Memory is freed by pam otherwise
allocator.free(response);
if (username != null) allocator.free(username.?);
if (password != null) allocator.free(password.?);
} else {
resp.?.* = response.ptr;
}
return status; return status;
} }
@ -269,7 +273,7 @@ fn resetTerminal(allocator: Allocator, shell: [*:0]const u8, term_reset_cmd: []c
const pid = std.c.fork(); const pid = std.c.fork();
if (pid == 0) { if (pid == 0) {
_ = interop.execl(shell, shell, "-c\x00".ptr, term_reset_cmd_z.ptr, @as([*c]const u8, 0)); _ = interop.execl(shell, shell, "-c", term_reset_cmd_z.ptr, @as([*c]const u8, 0));
std.os.exit(0); std.os.exit(0);
} }
@ -277,6 +281,93 @@ fn resetTerminal(allocator: Allocator, shell: [*:0]const u8, term_reset_cmd: []c
_ = std.c.waitpid(pid, &status, 0); _ = std.c.waitpid(pid, &status, 0);
} }
fn getFreeDisplay() !u8 {
var buf: [15]u8 = undefined;
var i: u8 = 0;
while (i < 200) : (i += 1) {
const xlock = try std.fmt.bufPrint(&buf, "/tmp/.X{d}-lock", .{i});
std.os.access(xlock, std.os.F_OK) catch break;
}
return i;
}
fn getXPid(display_num: u8) !i32 {
var buf: [15]u8 = undefined;
const file_name = try std.fmt.bufPrint(&buf, "/tmp/.X{d}-lock", .{display_num});
const file = try std.fs.openFileAbsolute(file_name, std.fs.File.OpenFlags{});
defer file.close();
var file_buf: [20]u8 = undefined;
var fbs = std.io.fixedBufferStream(&file_buf);
_ = try file.reader().streamUntilDelimiter(fbs.writer(), '\n', null);
const line = std.mem.sliceTo(&file_buf, 170);
return std.fmt.parseInt(i32, std.mem.trim(u8, line, " "), 10);
}
fn xauth(display_name: [:0]u8, shell: [*:0]const u8, pw_dir: [*:0]const u8, xauth_cmd: []const u8, mcookie_cmd: []const u8) !void {
var pwd_buf: [100]u8 = undefined;
var pwd: [:0]u8 = try std.fmt.bufPrintZ(&pwd_buf, "{s}", .{pw_dir});
var xauth_buf: [100]u8 = undefined;
var xauth_dir: [:0]u8 = std.mem.span(interop.getenv("XDG_RUNTIME_DIR"));
var xauth_file: []const u8 = "lyxauth";
if (xauth_dir[0] == 0) {
xauth_dir = std.mem.span(interop.getenv("XDG_CONFIG_HOME"));
var sb: std.c.Stat = std.mem.zeroes(std.c.Stat);
if (xauth_dir[0] == 0) {
xauth_dir = try std.fmt.bufPrintZ(&xauth_buf, "{s}/.config", .{xauth_dir});
_ = std.c.stat(xauth_dir, &sb);
const mode = sb.mode & std.os.S.IFMT;
if (mode == std.os.S.IFDIR) {
xauth_dir = try std.fmt.bufPrintZ(&xauth_buf, "{s}/ly", .{xauth_dir});
} else {
xauth_dir = pwd;
xauth_file = ".lyxauth";
}
} else {
xauth_dir = try std.fmt.bufPrintZ(&xauth_buf, "{s}/ly", .{xauth_dir});
}
_ = std.c.stat(xauth_dir, &sb);
const mode = sb.mode & std.os.S.IFMT;
if (mode != std.os.S.IFDIR) {
std.os.mkdir(xauth_dir, 777) catch {
xauth_dir = pwd;
xauth_file = ".lyxauth";
};
}
}
// Trim trailing slashes
var i = xauth_dir.len - 1;
while (xauth_dir[i] == '/') : (i -= 1) {}
xauth_dir[i + 1] = 0;
xauth_dir = xauth_dir[0 .. i + 1 :0];
var buf: [256]u8 = undefined;
const xauthority: [:0]u8 = try std.fmt.bufPrintZ(&buf, "{s}/{s}", .{ xauth_dir, xauth_file });
_ = interop.setenv("XAUTHORITY", xauthority, 1);
_ = interop.setenv("DISPLAY", display_name, 1);
const createFlags = std.fs.File.CreateFlags{};
const file = try std.fs.createFileAbsolute(xauthority, createFlags);
file.close();
const pid = std.c.fork();
if (pid == 0) {
var cmd_buffer = std.mem.zeroes([1024]u8);
const cmd_str = try std.fmt.bufPrintZ(&cmd_buffer, "{s} add {s} . $({s})", .{ xauth_cmd, display_name, mcookie_cmd });
_ = interop.execl(shell, shell, "-c", cmd_str.ptr, @as([*c]const u8, 0));
std.os.exit(0);
}
var status: c_int = 0;
_ = std.c.waitpid(pid, &status, 0);
}
fn executeWaylandCmd(shell: [*:0]const u8, wayland_cmd: []const u8, desktop_cmd: []const u8) !void { fn executeWaylandCmd(shell: [*:0]const u8, wayland_cmd: []const u8, desktop_cmd: []const u8) !void {
var cmd_buffer = std.mem.zeroes([1024]u8); var cmd_buffer = std.mem.zeroes([1024]u8);
@ -284,10 +375,98 @@ fn executeWaylandCmd(shell: [*:0]const u8, wayland_cmd: []const u8, desktop_cmd:
_ = interop.execl(shell, shell, "-c", cmd_str.ptr, @as([*c]const u8, 0)); _ = interop.execl(shell, shell, "-c", cmd_str.ptr, @as([*c]const u8, 0));
} }
fn executeX11Cmd(shell: [*:0]const u8, pw_dir: [*:0]const u8, x_cmd: []const u8, x_cmd_setup: []const u8, desktop_cmd: []const u8, xauth_cmd: []const u8, mcookie_cmd: []const u8, vt: []const u8) !void {
const display_num = try getFreeDisplay();
var buf: [5]u8 = undefined;
var display_name: [:0]u8 = try std.fmt.bufPrintZ(&buf, ":{d}", .{display_num});
try xauth(display_name, shell, pw_dir, xauth_cmd, mcookie_cmd);
const pid = std.c.fork();
if (pid == 0) {
var cmd_buffer = std.mem.zeroes([1024]u8);
const cmd_str = try std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s}", .{ x_cmd, display_name, vt });
_ = interop.execl(shell, shell, "-c", cmd_str.ptr, @as([*c]const u8, 0));
std.os.exit(0);
}
var status: c_int = 0;
var ok: c_int = undefined;
var xcb: ?*interop.xcb.xcb_connection_t = null;
while (ok != 0) {
xcb = interop.xcb.xcb_connect(null, null);
ok = interop.xcb.xcb_connection_has_error(xcb);
_ = std.c.kill(pid, 0);
if (std.c._errno().* == interop.ESRCH and ok != 0) {
return;
}
}
// X Server detaches from the process.
// Pid can be fetched from /tmp/X{d}.lock
const x_pid = try getXPid(display_num);
//logger.debug("Found X Server PID: {d}", .{x_pid});
const xorg_pid = std.c.fork();
if (xorg_pid == 0) {
var cmd_buffer = std.mem.zeroes([1024]u8);
const cmd_str = try std.fmt.bufPrintZ(&cmd_buffer, "{s} {s}", .{ x_cmd_setup, desktop_cmd });
_ = interop.execl(shell, shell, "-c", cmd_str.ptr, @as([*c]const u8, 0));
std.os.exit(0);
}
_ = std.c.waitpid(xorg_pid, &status, 0);
interop.xcb.xcb_disconnect(xcb);
_ = std.c.kill(x_pid, 0);
if (std.c._errno().* != interop.ESRCH) {
//logger.debug("X Server is alive. Sending SIGTERM to process. PID: {d}", .{pid});
_ = std.c.kill(x_pid, interop.SIGTERM);
_ = std.c.waitpid(x_pid, &status, 0);
}
}
fn executeShellCmd(shell: [*:0]const u8) void { fn executeShellCmd(shell: [*:0]const u8) void {
_ = interop.execl(shell, shell, @as([*c]const u8, 0)); _ = interop.execl(shell, shell, @as([*c]const u8, 0));
} }
fn addUtmpEntry(entry: *Utmp, username: [*:0]const u8, pid: c_int) !void {
entry.ut_type = utmp.USER_PROCESS;
entry.ut_pid = pid;
var buf: [4096]u8 = undefined;
const ttyname = try std.os.getFdPath(0, &buf);
var ttyname_buf: [32]u8 = undefined;
_ = try std.fmt.bufPrint(&ttyname_buf, "{s}", .{ttyname["/dev/".len..]});
entry.ut_line = ttyname_buf;
entry.ut_id = ttyname_buf["tty".len..7].*;
var username_buf: [32]u8 = undefined;
_ = try std.fmt.bufPrint(&username_buf, "{s}", .{username});
entry.ut_user = username_buf;
entry.ut_host = std.mem.zeroes([256]u8);
entry.ut_tv.tv_sec = @truncate(std.time.timestamp());
entry.ut_addr_v6[0] = 0;
utmp.setutent();
_ = utmp.pututline(entry);
}
fn removeUtmpEntry(entry: *Utmp) void {
entry.ut_type = utmp.DEAD_PROCESS;
entry.ut_line = std.mem.zeroes([32]u8);
entry.ut_user = std.mem.zeroes([32]u8);
utmp.setutent();
_ = utmp.pututline(entry);
utmp.endutent();
}
fn pamDiagnose(status: c_int) anyerror { fn pamDiagnose(status: c_int) anyerror {
return switch (status) { return switch (status) {
interop.pam.PAM_ACCT_EXPIRED => return error.PamAccountExpired, interop.pam.PAM_ACCT_EXPIRED => return error.PamAccountExpired,

View File

@ -6,92 +6,48 @@ const Input = enums.Input;
const Config = @This(); const Config = @This();
ly: struct { animation: Animation = .none,
animation: Animation, asterisk: u8 = '*',
asterisk: u8, bg: u8 = 0,
bg: u8, bigclock: bool = false,
bigclock: bool, blank_box: bool = true,
blank_box: bool, border_fg: u8 = 8,
blank_password: bool, clear_password: bool = false,
clock: []const u8, clock: ?[]const u8 = null,
console_dev: []const u8, console_dev: []const u8 = "/dev/console",
default_input: Input, default_input: Input = .login,
fg: u8, fg: u8 = 8,
hide_borders: bool, hide_borders: bool = false,
hide_key_hints: bool, hide_key_hints: bool = false,
input_len: u8, input_len: u8 = 34,
lang: []const u8, lang: []const u8 = "en",
load: bool, load: bool = true,
margin_box_h: u8, log_path: ?[]const u8 = null,
margin_box_v: u8, margin_box_h: u8 = 2,
max_desktop_len: u8, margin_box_v: u8 = 1,
max_login_len: u8, max_desktop_len: u8 = 100,
max_password_len: u8, max_login_len: u8 = 255,
mcookie_cmd: []const u8, max_password_len: u8 = 255,
min_refresh_delta: u16, mcookie_cmd: []const u8 = "/usr/bin/mcookie",
path: []const u8, min_refresh_delta: u16 = 5,
restart_cmd: []const u8, path: ?[]const u8 = "/sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin",
restart_key: []const u8, restart_cmd: []const u8 = "/sbin/shutdown -r now",
save: bool, restart_key: []const u8 = "F2",
save_file: []const u8, save: bool = true,
service_name: []const u8, save_file: []const u8 = "/etc/ly/save",
shutdown_cmd: []const u8, service_name: []const u8 = "ly",
shutdown_key: []const u8, shutdown_cmd: []const u8 = "/sbin/shutdown -a now",
term_reset_cmd: []const u8, shutdown_key: []const u8 = "F1",
term_restore_cursor_cmd: []const u8, sleep_cmd: ?[]const u8 = null,
tty: u8, sleep_key: []const u8 = "F3",
wayland_cmd: []const u8, term_reset_cmd: []const u8 = "/usr/bin/tput reset",
wayland_specifier: bool, term_restore_cursor_cmd: []const u8 = "/usr/bin/tput cnorm",
waylandsessions: []const u8, tty: u8 = 2,
x_cmd: []const u8, wayland_cmd: []const u8 = build_options.data_directory ++ "/wsetup.sh",
xinitrc: []const u8, wayland_specifier: bool = false,
x_cmd_setup: []const u8, waylandsessions: []const u8 = "/usr/share/wayland-sessions",
xauth_cmd: []const u8, x_cmd: []const u8 = "/usr/bin/X",
xsessions: []const u8, xinitrc: ?[]const u8 = "~/.xinitrc",
}, x_cmd_setup: []const u8 = build_options.data_directory ++ "/xsetup.sh",
xauth_cmd: []const u8 = "/usr/bin/xauth",
pub fn init() Config { xsessions: []const u8 = "/usr/share/xsessions",
return .{ .ly = .{
.animation = .none,
.asterisk = '*',
.bg = 0,
.bigclock = false,
.blank_box = true,
.blank_password = false,
.clock = "",
.console_dev = "/dev/console",
.default_input = .login,
.fg = 9,
.hide_borders = false,
.hide_key_hints = false,
.input_len = 34,
.lang = "en",
.load = true,
.margin_box_h = 2,
.margin_box_v = 1,
.max_desktop_len = 100,
.max_login_len = 255,
.max_password_len = 255,
.mcookie_cmd = "/usr/bin/mcookie",
.min_refresh_delta = 5,
.path = "/sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin",
.restart_cmd = "/sbin/shutdown -r now",
.restart_key = "F2",
.save = true,
.save_file = "/etc/ly/save",
.service_name = "ly",
.shutdown_cmd = "/sbin/shutdown -a now",
.shutdown_key = "F1",
.term_reset_cmd = "/usr/bin/tput reset",
.term_restore_cursor_cmd = "/usr/bin/tput cnorm",
.tty = 2,
.wayland_cmd = build_options.data_directory ++ "/wsetup.sh",
.wayland_specifier = false,
.waylandsessions = "/usr/share/wayland-sessions",
.x_cmd = "/usr/bin/X",
.xinitrc = "~/.xinitrc",
.x_cmd_setup = build_options.data_directory ++ "/xsetup.sh",
.xauth_cmd = "/usr/bin/xauth",
.xsessions = "/usr/share/xsessions",
} };
}

View File

@ -1,99 +1,48 @@
const Lang = @This(); const Lang = @This();
ly: struct { capslock: []const u8 = "capslock",
capslock: []const u8, err_alloc: []const u8 = "failed memory allocation",
err_alloc: []const u8, err_bounds: []const u8 = "out-of-bounds index",
err_bounds: []const u8, err_chdir: []const u8 = "failed to open home folder",
err_chdir: []const u8, err_console_dev: []const u8 = "failed to access console",
err_console_dev: []const u8, err_dgn_oob: []const u8 = "log message",
err_dgn_oob: []const u8, err_domain: []const u8 = "invalid domain",
err_domain: []const u8, err_hostname: []const u8 = "failed to get hostname",
err_hostname: []const u8, err_mlock: []const u8 = "failed to lock password memory",
err_mlock: []const u8, err_null: []const u8 = "null pointer",
err_null: []const u8, err_pam: []const u8 = "pam transaction failed",
err_pam: []const u8, err_pam_abort: []const u8 = "pam transaction aborted",
err_pam_abort: []const u8, err_pam_acct_expired: []const u8 = "account expired",
err_pam_acct_expired: []const u8, err_pam_auth: []const u8 = "authentication error",
err_pam_auth: []const u8, err_pam_authinfo_unavail: []const u8 = "failed to get user info",
err_pam_authinfo_unavail: []const u8, err_pam_authok_reqd: []const u8 = "token expired",
err_pam_authok_reqd: []const u8, err_pam_buf: []const u8 = "memory buffer error",
err_pam_buf: []const u8, err_pam_cred_err: []const u8 = "failed to set credentials",
err_pam_cred_err: []const u8, err_pam_cred_expired: []const u8 = "credentials expired",
err_pam_cred_expired: []const u8, err_pam_cred_insufficient: []const u8 = "insufficient credentials",
err_pam_cred_insufficient: []const u8, err_pam_cred_unavail: []const u8 = "failed to get credentials",
err_pam_cred_unavail: []const u8, err_pam_maxtries: []const u8 = "reached maximum tries limit",
err_pam_maxtries: []const u8, err_pam_perm_denied: []const u8 = "permission denied",
err_pam_perm_denied: []const u8, err_pam_session: []const u8 = "session error",
err_pam_session: []const u8, err_pam_sys: []const u8 = "system error",
err_pam_sys: []const u8, err_pam_user_unknown: []const u8 = "unknown user",
err_pam_user_unknown: []const u8, err_path: []const u8 = "failed to set path",
err_path: []const u8, err_perm_dir: []const u8 = "failed to change current directory",
err_perm_dir: []const u8, err_perm_group: []const u8 = "failed to downgrade group permissions",
err_perm_group: []const u8, err_perm_user: []const u8 = "failed to downgrade user permissions",
err_perm_user: []const u8, err_pwnam: []const u8 = "failed to get user info",
err_pwnam: []const u8, err_user_gid: []const u8 = "failed to set user GID",
err_user_gid: []const u8, err_user_init: []const u8 = "failed to initialize user",
err_user_init: []const u8, err_user_uid: []const u8 = "failed to set user UID",
err_user_uid: []const u8, err_xsessions_dir: []const u8 = "failed to find sessions folder",
err_xsessions_dir: []const u8, err_xsessions_open: []const u8 = "failed to open sessions folder",
err_xsessions_open: []const u8, login: []const u8 = "login:",
login: []const u8, logout: []const u8 = "logged out",
logout: []const u8, numlock: []const u8 = "numlock",
numlock: []const u8, password: []const u8 = "password:",
password: []const u8, restart: []const u8 = "reboot",
restart: []const u8, shell: []const u8 = "shell",
shell: []const u8, shutdown: []const u8 = "shutdown",
shutdown: []const u8, sleep: []const u8 = "sleep",
wayland: []const u8, wayland: []const u8 = "wayland",
xinitrc: []const u8, xinitrc: []const u8 = "xinitrc"
},
pub fn init() Lang {
return .{ .ly = .{
.capslock = "capslock",
.err_alloc = "failed memory allocation",
.err_bounds = "out-of-bounds index",
.err_chdir = "failed to open home folder",
.err_console_dev = "failed to access console",
.err_dgn_oob = "log message",
.err_domain = "invalid domain",
.err_hostname = "failed to get hostname",
.err_mlock = "failed to lock password memory",
.err_null = "null pointer",
.err_pam = "pam transaction failed",
.err_pam_abort = "pam transaction aborted",
.err_pam_acct_expired = "account expired",
.err_pam_auth = "authentication error",
.err_pam_authinfo_unavail = "failed to get user info",
.err_pam_authok_reqd = "token expired",
.err_pam_buf = "memory buffer error",
.err_pam_cred_err = "failed to set credentials",
.err_pam_cred_expired = "credentials expired",
.err_pam_cred_insufficient = "insufficient credentials",
.err_pam_cred_unavail = "failed to get credentials",
.err_pam_maxtries = "reached maximum tries limit",
.err_pam_perm_denied = "permission denied",
.err_pam_session = "session error",
.err_pam_sys = "system error",
.err_pam_user_unknown = "unknown user",
.err_path = "failed to set path",
.err_perm_dir = "failed to change current directory",
.err_perm_group = "failed to downgrade group permissions",
.err_perm_user = "failed to downgrade user permissions",
.err_pwnam = "failed to get user info",
.err_user_gid = "failed to set user GID",
.err_user_init = "failed to initialize user",
.err_user_uid = "failed to set user UID",
.err_xsessions_dir = "failed to find sessions folder",
.err_xsessions_open = "failed to open sessions folder",
.login = "login:",
.logout = "logged out",
.numlock = "numlock",
.password = "password:",
.restart = "reboot",
.shell = "shell",
.shutdown = "shutdown",
.wayland = "wayland",
.xinitrc = "xinitrc",
} };
}

4
src/config/Save.zig Normal file
View File

@ -0,0 +1,4 @@
const Save = @This();
user: ?[]const u8 = null,
session_index: ?u64 = null,

194
src/config/ini.zig Normal file
View File

@ -0,0 +1,194 @@
const std = @import("std");
const ini = @import("ini");
const Allocator = std.mem.Allocator;
const trueOrFalse = std.ComptimeStringMap(bool, .{ .{ "true", true }, .{ "false", false }, .{ "1", true }, .{ "0", false } });
pub fn Ini(comptime T: type) type {
return struct {
const Self = @This();
data: T,
allocator: std.mem.Allocator,
list: std.ArrayList([]u8),
pub fn init(allocator: std.mem.Allocator) Self {
return .{
.data = T{},
.allocator = allocator,
.list = std.ArrayList([]u8).init(allocator),
};
}
pub fn deinit(self: *Self) void {
for (self.list.items) |item| {
self.allocator.free(item);
}
self.list.deinit();
}
pub fn readToStruct(self: *Self, path: []const u8) !T {
const file = try std.fs.cwd().openFile(path, .{});
defer file.close();
var parser = ini.parse(self.allocator, file.reader());
defer parser.deinit();
var ns: []u8 = &.{};
defer self.allocator.free(ns);
while (try parser.next()) |record| {
switch (record) {
.section => |heading| {
ns = try self.allocator.realloc(ns, heading.len);
@memcpy(ns, heading);
std.mem.replaceScalar(u8, ns, ' ', '_');
},
.property => |kv| {
inline for (std.meta.fields(T)) |field| {
const field_info = @typeInfo(field.type);
if (field_info == .Struct or (field_info == .Optional and @typeInfo(field_info.Optional.child) == .Struct)) {
if (ns.len != 0 and std.mem.eql(u8, field.name, ns)) {
inline for (std.meta.fields(@TypeOf(@field(self.data, field.name)))) |inner_field| {
if (std.mem.eql(u8, inner_field.name, kv.key)) {
@field(@field(self.data, field.name), inner_field.name) = try self.convert(inner_field.type, kv.value);
}
}
}
} else if (ns.len == 0 and std.mem.eql(u8, field.name, kv.key)) {
@field(self.data, field.name) = try self.convert(field.type, kv.value);
}
}
},
.enumeration => {},
}
}
return self.data;
}
fn convert(self: *Self, comptime T1: type, val: []const u8) !T1 {
return switch (@typeInfo(T1)) {
.Int, .ComptimeInt => try std.fmt.parseInt(T1, val, 0),
.Float, .ComptimeFloat => try std.fmt.parseFloat(T1, val),
.Bool => trueOrFalse.get(val).?,
.Enum => std.meta.stringToEnum(T1, val).?,
.Optional => |opt| {
if (val.len == 0 or std.mem.eql(u8, val, "null")) return null;
return try self.convert(opt.child, val);
},
else => {
const a_val = try self.allocator.alloc(u8, val.len);
@memcpy(a_val, val);
try self.list.append(a_val);
return @as(T1, a_val);
},
};
}
};
}
fn writeProperty(writer: anytype, field_name: []const u8, val: anytype) !void {
switch (@typeInfo(@TypeOf(val))) {
.Bool => {
try writer.print("{s}={d}\n", .{ field_name, @intFromBool(val) });
},
.Int, .ComptimeInt, .Float, .ComptimeFloat => {
try writer.print("{s}={d}\n", .{ field_name, val });
},
.Enum => {
try writer.print("{s}={s}\n", .{ field_name, @tagName(val) });
},
else => {
try writer.print("{s}={s}\n", .{ field_name, val });
},
}
}
fn isDefaultValue(field: anytype, field_value: field.type) bool {
if (field.default_value) |default_value_ao| {
const def_val: *align(field.alignment) const anyopaque = @alignCast(default_value_ao);
const default_value = @as(*const field.type, @ptrCast(def_val)).*;
const field_t_info = @typeInfo(field.type);
if (field_t_info == .Optional) {
if (default_value != null) {
if (field_value != null) {
if (field_t_info == .Pointer) {
return std.mem.eql(field_t_info.Pointer.child, default_value.?, field_value.?);
} else {
return default_value.? == field_value.?;
}
}
return false;
}
return field_value == null;
}
if (field_t_info == .Pointer) {
return std.mem.eql(field_t_info.Pointer.child, default_value, field_value);
} else {
return default_value == field_value;
}
}
return false;
}
pub fn writeFromStruct(data: anytype, writer: anytype, ns: ?[]const u8) !void {
if (@typeInfo(@TypeOf(data)) != .Struct) @compileError("writeFromStruct() requires a struct");
var should_write_ns = ns != null;
inline for (std.meta.fields(@TypeOf(data))) |field| {
switch (@typeInfo(field.type)) {
.Struct => continue,
.Optional => |opt| {
if (@typeInfo(opt.child) != .Struct) {
const val = @field(data, field.name);
if (val) |field_val| {
if (!isDefaultValue(field, @field(data, field.name))) {
if (should_write_ns) {
try writer.print("[{s}]\n", .{ns.?});
should_write_ns = false;
}
try writeProperty(writer, field.name, field_val);
}
} else if (!isDefaultValue(field, @field(data, field.name))) {
if (should_write_ns) {
try writer.print("[{s}]\n", .{ns.?});
should_write_ns = false;
}
try writeProperty(writer, field.name, "");
}
} else continue;
},
else => {
if (!isDefaultValue(field, @field(data, field.name))) {
if (should_write_ns) {
try writer.print("[{s}]\n", .{ns.?});
should_write_ns = false;
}
try writeProperty(writer, field.name, @field(data, field.name));
}
},
}
}
if (ns == null) {
inline for (std.meta.fields(@TypeOf(data))) |field| {
switch (@typeInfo(field.type)) {
.Struct => {
try writeFromStruct(@field(data, field.name), writer, field.name);
},
.Optional => |opt| {
if (@typeInfo(opt.child) == .Struct) {
if (@field(data, field.name)) |inner_data| {
try writeFromStruct(inner_data, writer, field.name);
}
} else continue;
},
else => continue,
}
}
}
}

View File

@ -10,6 +10,14 @@ pub const pam = @cImport({
@cInclude("security/pam_appl.h"); @cInclude("security/pam_appl.h");
}); });
pub const utmp = @cImport({
@cInclude("utmp.h");
});
pub const xcb = @cImport({
@cInclude("xcb/xcb.h");
});
pub const c_size = u64; pub const c_size = u64;
pub const c_uid = u32; pub const c_uid = u32;
pub const c_gid = u32; pub const c_gid = u32;
@ -36,6 +44,9 @@ pub const passwd = extern struct {
pw_shell: [*:0]u8, pw_shell: [*:0]u8,
}; };
pub const SIGTERM: c_int = 15;
pub const ESRCH: c_int = 3;
pub const _POSIX_HOST_NAME_MAX: c_int = 0xFF; pub const _POSIX_HOST_NAME_MAX: c_int = 0xFF;
pub const _SC_HOST_NAME_MAX: c_int = 0xB4; pub const _SC_HOST_NAME_MAX: c_int = 0xB4;
@ -63,6 +74,7 @@ pub extern "c" fn strftime(str: [*:0]u8, maxsize: c_size, format: [*:0]const u8,
pub extern "c" fn setenv(name: [*:0]const u8, value: [*:0]const u8, overwrite: c_int) c_int; pub extern "c" fn setenv(name: [*:0]const u8, value: [*:0]const u8, overwrite: c_int) c_int;
pub extern "c" fn getenv(name: [*:0]const u8) [*:0]u8; pub extern "c" fn getenv(name: [*:0]const u8) [*:0]u8;
pub extern "c" fn putenv(name: [*:0]u8) c_int; pub extern "c" fn putenv(name: [*:0]u8) c_int;
pub extern "c" fn clearenv() c_int;
pub extern "c" fn getuid() c_uid; pub extern "c" fn getuid() c_uid;
pub extern "c" fn getpwnam(name: [*:0]const u8) ?*passwd; pub extern "c" fn getpwnam(name: [*:0]const u8) ?*passwd;
pub extern "c" fn endpwent() void; pub extern "c" fn endpwent() void;

56
src/logger/LogFile.zig Normal file
View File

@ -0,0 +1,56 @@
const std = @import("std");
const LogFile = @This();
file: ?std.fs.File = null,
writer: ?std.fs.File.Writer = null,
start_time: i64 = 0,
pub fn init(log_path: ?[]const u8) LogFile {
if (log_path == null or log_path.?.len == 0) return LogFile{};
const directory = std.fs.openDirAbsolute(log_path.?, std.fs.Dir.OpenDirOptions{}) catch return LogFile{};
directory.rename("ly.log", "ly.log.old") catch |e| {
if (e != error.FileNotFound) return LogFile{};
};
const createFlags = std.fs.File.CreateFlags{};
const file = directory.createFile("ly.log", createFlags) catch return LogFile{};
return LogFile{
.file = file,
.writer = file.writer(),
.start_time = std.time.milliTimestamp(),
};
}
pub fn deinit(self: LogFile) void {
if (self.file) |file| file.close();
}
fn prettyPrint(self: LogFile, comptime log_level: std.log.Level, comptime format: []const u8, args: anytype) void {
//if (comptime !std.log.logEnabled(log_level, .log_file)) return;
const ms_since_start: f80 = @floatFromInt(std.time.milliTimestamp() - self.start_time);
const log_time: f64 = @floatCast(ms_since_start / 1000);
if (self.writer) |writer| {
writer.print("[{d:.2}s] " ++ "{s}: " ++ format ++ "\n", .{ log_time, log_level.asText() } ++ args) catch return;
}
}
pub fn info(self: LogFile, comptime format: []const u8, args: anytype) void {
self.prettyPrint(.info, format, args);
}
pub fn debug(self: LogFile, comptime format: []const u8, args: anytype) void {
self.prettyPrint(.debug, format, args);
}
pub fn warn(self: LogFile, comptime format: []const u8, args: anytype) void {
self.prettyPrint(.warn, format, args);
}
pub fn err(self: LogFile, comptime format: []const u8, args: anytype) void {
self.prettyPrint(.err, format, args);
}

View File

@ -11,9 +11,12 @@ const TerminalBuffer = @import("tui/TerminalBuffer.zig");
const Desktop = @import("tui/components/Desktop.zig"); const Desktop = @import("tui/components/Desktop.zig");
const Text = @import("tui/components/Text.zig"); const Text = @import("tui/components/Text.zig");
const Config = @import("config/Config.zig"); const Config = @import("config/Config.zig");
const ConfigReader = @import("config/ConfigReader.zig"); const ini = @import("config/ini.zig");
const Lang = @import("config/Lang.zig"); const Lang = @import("config/Lang.zig");
const LogFile = @import("logger/LogFile.zig");
const Save = @import("config/Save.zig");
const Ini = ini.Ini;
const termbox = interop.termbox; const termbox = interop.termbox;
const LY_VERSION = "1.0.0"; const LY_VERSION = "1.0.0";
@ -55,8 +58,10 @@ pub fn main() !void {
} }
// Load configuration file // Load configuration file
var config_reader = ConfigReader.init(allocator); var config_ini = Ini(Config).init(allocator);
defer config_reader.deinit(); defer config_ini.deinit();
var lang_ini = Ini(Lang).init(allocator);
defer lang_ini.deinit();
if (res.args.config) |s| { if (res.args.config) |s| {
const trailing_slash = if (s[s.len - 1] != '/') "/" else ""; const trailing_slash = if (s[s.len - 1] != '/') "/" else "";
@ -64,21 +69,26 @@ pub fn main() !void {
const config_path = try std.fmt.allocPrint(allocator, "{s}{s}config.ini", .{ s, trailing_slash }); const config_path = try std.fmt.allocPrint(allocator, "{s}{s}config.ini", .{ s, trailing_slash });
defer allocator.free(config_path); defer allocator.free(config_path);
config = try config_reader.readConfig(config_path); config = config_ini.readToStruct(config_path) catch Config{};
const lang_path = try std.fmt.allocPrint(allocator, "{s}{s}lang/{s}.ini", .{ s, trailing_slash, config.ly.lang }); const lang_path = try std.fmt.allocPrint(allocator, "{s}{s}lang/{s}.ini", .{ s, trailing_slash, config.lang });
defer allocator.free(lang_path); defer allocator.free(lang_path);
lang = try config_reader.readLang(lang_path); lang = lang_ini.readToStruct(lang_path) catch Lang{};
} else { } else {
config = try config_reader.readConfig(build_options.data_directory ++ "/config.ini"); config = config_ini.readToStruct(build_options.data_directory ++ "/config.ini") catch Config{};
const lang_path = try std.fmt.allocPrint(allocator, "{s}/lang/{s}.ini", .{ build_options.data_directory, config.ly.lang }); const lang_path = try std.fmt.allocPrint(allocator, "{s}/lang/{s}.ini", .{ build_options.data_directory, config.lang });
defer allocator.free(lang_path); defer allocator.free(lang_path);
lang = try config_reader.readLang(lang_path); lang = lang_ini.readToStruct(lang_path) catch Lang{};
} }
const logger = LogFile.init(config.log_path);
defer logger.deinit();
logger.debug("Ly Started", .{});
// Initialize information line with host name // Initialize information line with host name
var got_host_name = false; var got_host_name = false;
var host_name_buffer: []u8 = undefined; var host_name_buffer: []u8 = undefined;
@ -86,9 +96,9 @@ pub fn main() !void {
get_host_name: { get_host_name: {
const host_name_struct = interop.getHostName(allocator) catch |err| { const host_name_struct = interop.getHostName(allocator) catch |err| {
if (err == error.CannotGetHostName) { if (err == error.CannotGetHostName) {
info_line = lang.ly.err_hostname; info_line = lang.err_hostname;
} else { } else {
info_line = lang.ly.err_alloc; info_line = lang.err_alloc;
} }
break :get_host_name; break :get_host_name;
}; };
@ -98,66 +108,69 @@ pub fn main() !void {
info_line = host_name_struct.slice; info_line = host_name_struct.slice;
} }
defer {
if (got_host_name) allocator.free(host_name_buffer);
}
// Initialize termbox // Initialize termbox
_ = termbox.tb_init(); _ = termbox.tb_init();
defer termbox.tb_shutdown(); defer {
termbox.tb_clear();
termbox.tb_shutdown();
}
_ = termbox.tb_select_output_mode(termbox.TB_OUTPUT_NORMAL); _ = termbox.tb_select_output_mode(termbox.TB_OUTPUT_NORMAL);
termbox.tb_clear(); termbox.tb_clear();
// Initialize terminal buffer // Initialize terminal buffer
const labels_max_length = @max(lang.ly.login.len, lang.ly.password.len); const labels_max_length = @max(lang.login.len, lang.password.len);
var buffer = TerminalBuffer.init(config.ly.margin_box_v, config.ly.margin_box_h, config.ly.input_len, labels_max_length, config.ly.fg, config.ly.bg); var buffer = TerminalBuffer.init(config.margin_box_v, config.margin_box_h, config.input_len, labels_max_length, config.fg, config.bg, config.border_fg);
// Initialize components // Initialize components
var desktop = try Desktop.init(allocator, &buffer, config.ly.max_desktop_len); var desktop = try Desktop.init(allocator, &buffer, config.max_desktop_len);
defer desktop.deinit(); defer desktop.deinit();
desktop.addEnvironment(lang.ly.shell, "", .shell) catch { desktop.addEnvironment(lang.shell, "", .shell) catch {
info_line = lang.ly.err_alloc; info_line = lang.err_alloc;
}; };
desktop.addEnvironment(lang.ly.xinitrc, config.ly.xinitrc, .xinitrc) catch { if (config.xinitrc) |xinitrc| {
info_line = lang.ly.err_alloc; desktop.addEnvironment(lang.xinitrc, xinitrc, .xinitrc) catch {
info_line = lang.err_alloc;
}; };
}
try desktop.crawl(config.ly.waylandsessions, .wayland); try desktop.crawl(config.waylandsessions, .wayland);
try desktop.crawl(config.ly.xsessions, .x11); try desktop.crawl(config.xsessions, .x11);
var login = try Text.init(allocator, &buffer, config.ly.max_login_len); var login = try Text.init(allocator, &buffer, config.max_login_len);
defer login.deinit(); defer login.deinit();
var password = try Text.init(allocator, &buffer, config.ly.max_password_len); var password = try Text.init(allocator, &buffer, config.max_password_len);
defer password.deinit(); defer password.deinit();
var active_input = config.default_input;
// Load last saved username and desktop selection, if any // Load last saved username and desktop selection, if any
if (config.ly.load) load_last_saved: { if (config.load) {
var file = std.fs.openFileAbsolute(config.ly.save_file, .{}) catch break :load_last_saved; var save_ini = Ini(Save).init(allocator);
defer file.close(); defer save_ini.deinit();
const save = save_ini.readToStruct(config.save_file) catch Save{};
const reader = file.reader(); if (save.user) |user| {
const username_length = try reader.readIntLittle(u64); try login.text.appendSlice(user);
login.end = user.len;
const username_buffer = try allocator.alloc(u8, username_length); active_input = .password;
defer allocator.free(username_buffer);
_ = try reader.read(username_buffer);
const current_desktop = try reader.readIntLittle(u64);
if (username_buffer.len > 0) {
try login.text.appendSlice(username_buffer);
login.end = username_buffer.len;
} }
if (current_desktop < desktop.environments.items.len) desktop.current = current_desktop; if (save.session_index) |session_index| {
if (session_index < desktop.environments.items.len) desktop.current = session_index;
}
} }
var active_input = if (config.ly.default_input == .login and login.text.items.len != login.end) .password else config.ly.default_input;
// Place components on the screen // Place components on the screen
{ {
buffer.drawBoxCenter(!config.ly.hide_borders, config.ly.blank_box); buffer.drawBoxCenter(!config.hide_borders, config.blank_box);
const coordinates = buffer.calculateComponentCoordinates(); const coordinates = buffer.calculateComponentCoordinates();
desktop.position(coordinates.x, coordinates.y + 2, coordinates.visible_length); desktop.position(coordinates.x, coordinates.y + 2, coordinates.visible_length);
@ -167,10 +180,10 @@ pub fn main() !void {
switch (active_input) { switch (active_input) {
.session => desktop.handle(null), .session => desktop.handle(null),
.login => login.handle(null) catch { .login => login.handle(null) catch {
info_line = lang.ly.err_alloc; info_line = lang.err_alloc;
}, },
.password => password.handle(null) catch { .password => password.handle(null) catch {
info_line = lang.ly.err_alloc; info_line = lang.err_alloc;
}, },
} }
} }
@ -179,23 +192,23 @@ pub fn main() !void {
var doom: Doom = undefined; var doom: Doom = undefined;
var matrix: Matrix = undefined; var matrix: Matrix = undefined;
switch (config.ly.animation) { switch (config.animation) {
.none => {}, .none => {},
.doom => doom = try Doom.init(allocator, &buffer), .doom => doom = try Doom.init(allocator, &buffer),
.matrix => matrix = try Matrix.init(allocator, &buffer), .matrix => matrix = try Matrix.init(allocator, &buffer),
} }
defer { defer {
switch (config.ly.animation) { switch (config.animation) {
.none => {}, .none => {},
.doom => doom.deinit(), .doom => doom.deinit(),
.matrix => matrix.deinit(), .matrix => matrix.deinit(),
} }
} }
const animate = config.ly.animation != .none; const animate = config.animation != .none;
const has_clock = config.ly.clock.len > 0; const shutdown_key = try std.fmt.parseInt(u8, config.shutdown_key[1..], 10);
const shutdown_key = try std.fmt.parseInt(u8, config.ly.shutdown_key[1..], 10); const restart_key = try std.fmt.parseInt(u8, config.restart_key[1..], 10);
const restart_key = try std.fmt.parseInt(u8, config.ly.restart_key[1..], 10); const sleep_key = try std.fmt.parseInt(u8, config.sleep_key[1..], 10);
var event = std.mem.zeroes(termbox.tb_event); var event = std.mem.zeroes(termbox.tb_event);
var run = true; var run = true;
@ -207,8 +220,8 @@ pub fn main() !void {
// Switch to selected TTY if possible // Switch to selected TTY if possible
open_console_dev: { open_console_dev: {
const console_dev_z = allocator.dupeZ(u8, config.ly.console_dev) catch { const console_dev_z = allocator.dupeZ(u8, config.console_dev) catch {
info_line = lang.ly.err_alloc; info_line = lang.err_alloc;
break :open_console_dev; break :open_console_dev;
}; };
defer allocator.free(console_dev_z); defer allocator.free(console_dev_z);
@ -217,17 +230,17 @@ pub fn main() !void {
defer _ = std.c.close(fd); defer _ = std.c.close(fd);
if (fd < 0) { if (fd < 0) {
info_line = lang.ly.err_console_dev; info_line = lang.err_console_dev;
break :open_console_dev; break :open_console_dev;
} }
_ = std.c.ioctl(fd, interop.VT_ACTIVATE, config.ly.tty); _ = std.c.ioctl(fd, interop.VT_ACTIVATE, config.tty);
_ = std.c.ioctl(fd, interop.VT_WAITACTIVE, config.ly.tty); _ = std.c.ioctl(fd, interop.VT_WAITACTIVE, config.tty);
} }
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.ly.animation != .none) { if (!update or config.animation != .none) {
if (!update) std.time.sleep(100_000_000); if (!update) std.time.sleep(100_000_000);
termbox.tb_present(); // Required to update tb_width(), tb_height() and tb_cell_buffer() termbox.tb_present(); // Required to update tb_width(), tb_height() and tb_cell_buffer()
@ -249,13 +262,13 @@ pub fn main() !void {
if (resolution_changed) { if (resolution_changed) {
buffer.buffer = termbox.tb_cell_buffer(); buffer.buffer = termbox.tb_cell_buffer();
switch (config.ly.animation) { switch (config.animation) {
.none => {}, .none => {},
.doom => doom.realloc() catch { .doom => doom.realloc() catch {
info_line = lang.ly.err_alloc; info_line = lang.err_alloc;
}, },
.matrix => matrix.realloc() catch { .matrix => matrix.realloc() catch {
info_line = lang.ly.err_alloc; info_line = lang.err_alloc;
}, },
} }
@ -269,28 +282,28 @@ pub fn main() !void {
switch (active_input) { switch (active_input) {
.session => desktop.handle(null), .session => desktop.handle(null),
.login => login.handle(null) catch { .login => login.handle(null) catch {
info_line = lang.ly.err_alloc; info_line = lang.err_alloc;
}, },
.password => password.handle(null) catch { .password => password.handle(null) catch {
info_line = lang.ly.err_alloc; info_line = lang.err_alloc;
}, },
} }
termbox.tb_clear(); termbox.tb_clear();
switch (config.ly.animation) { switch (config.animation) {
.none => {}, .none => {},
.doom => doom.draw(), .doom => doom.draw(),
.matrix => matrix.draw(), .matrix => matrix.draw(),
} }
if (config.ly.bigclock and buffer.box_height + (bigclock.HEIGHT + 2) * 2 < buffer.height) draw_big_clock: { if (config.bigclock and buffer.box_height + (bigclock.HEIGHT + 2) * 2 < buffer.height) draw_big_clock: {
const format = "%H:%M"; const format = "%H:%M";
const xo = buffer.width / 2 - (format.len * (bigclock.WIDTH + 1)) / 2; const xo = buffer.width / 2 - (format.len * (bigclock.WIDTH + 1)) / 2;
const yo = (buffer.height - buffer.box_height) / 2 - bigclock.HEIGHT - 2; const yo = (buffer.height - buffer.box_height) / 2 - bigclock.HEIGHT - 2;
const clock_str = interop.timeAsString(allocator, format, format.len + 1) catch { const clock_str = interop.timeAsString(allocator, format, format.len + 1) catch {
info_line = lang.ly.err_alloc; info_line = lang.err_alloc;
break :draw_big_clock; break :draw_big_clock;
}; };
defer allocator.free(clock_str); defer allocator.free(clock_str);
@ -301,11 +314,11 @@ pub fn main() !void {
} }
} }
buffer.drawBoxCenter(!config.ly.hide_borders, config.ly.blank_box); buffer.drawBoxCenter(!config.hide_borders, config.blank_box);
if (has_clock) draw_clock: { if (config.clock) |clock| draw_clock: {
const clock_buffer = interop.timeAsString(allocator, config.ly.clock, 32) catch { const clock_buffer = interop.timeAsString(allocator, clock, 32) catch {
info_line = lang.ly.err_alloc; info_line = lang.err_alloc;
break :draw_clock; break :draw_clock;
}; };
defer allocator.free(clock_buffer); defer allocator.free(clock_buffer);
@ -326,46 +339,57 @@ pub fn main() !void {
const label_x = buffer.box_x + buffer.margin_box_h; const label_x = buffer.box_x + buffer.margin_box_h;
const label_y = buffer.box_y + buffer.margin_box_v; const label_y = buffer.box_y + buffer.margin_box_v;
buffer.drawLabel(lang.ly.login, label_x, label_y + 4); buffer.drawLabel(lang.login, label_x, label_y + 4);
buffer.drawLabel(lang.ly.password, label_x, label_y + 6); buffer.drawLabel(lang.password, label_x, label_y + 6);
if (info_line.len > 0) { if (info_line.len > 0) {
const x = buffer.box_x + ((buffer.box_width - info_line.len) / 2); const x = buffer.box_x + ((buffer.box_width - info_line.len) / 2);
buffer.drawLabel(info_line, x, label_y); buffer.drawLabel(info_line, x, label_y);
} }
if (!config.ly.hide_key_hints) { if (!config.hide_key_hints) {
var length: u64 = 0; var length: u64 = 0;
buffer.drawLabel(config.ly.shutdown_key, length, 0); buffer.drawLabel(config.shutdown_key, length, 0);
length += config.ly.shutdown_key.len + 1; length += config.shutdown_key.len + 1;
buffer.drawLabel(" ", length - 1, 0);
buffer.drawLabel(lang.ly.shutdown, length, 0); buffer.drawLabel(lang.shutdown, length, 0);
length += lang.ly.shutdown.len + 1; length += lang.shutdown.len + 1;
buffer.drawLabel(config.ly.restart_key, length, 0); buffer.drawLabel(config.restart_key, length, 0);
length += config.ly.restart_key.len + 1; length += config.restart_key.len + 1;
buffer.drawLabel(" ", length - 1, 0);
buffer.drawLabel(lang.ly.restart, length, 0); buffer.drawLabel(lang.restart, length, 0);
length += lang.ly.restart.len + 1; length += lang.restart.len + 1;
if (config.sleep_cmd != null) {
buffer.drawLabel(config.sleep_key, length, 0);
length += config.sleep_key.len + 1;
buffer.drawLabel(" ", length - 1, 0);
buffer.drawLabel(lang.sleep, length, 0);
}
// length += lang.sleep.len + 1;
} }
draw_lock_state: { draw_lock_state: {
const lock_state = interop.getLockState(allocator, config.ly.console_dev) catch |err| { const lock_state = interop.getLockState(allocator, config.console_dev) catch |err| {
if (err == error.CannotOpenConsoleDev) { if (err == error.CannotOpenConsoleDev) {
info_line = lang.ly.err_console_dev; info_line = lang.err_console_dev;
} else { } else {
info_line = lang.ly.err_alloc; info_line = lang.err_alloc;
} }
break :draw_lock_state; break :draw_lock_state;
}; };
var lock_state_x = buffer.width - lang.ly.numlock.len; var lock_state_x = buffer.width - lang.numlock.len;
const lock_state_y: u64 = if (has_clock) 1 else 0; const lock_state_y: u64 = if (config.clock != null) 1 else 0;
if (lock_state.numlock) buffer.drawLabel(lang.ly.numlock, lock_state_x, lock_state_y); if (lock_state.numlock) buffer.drawLabel(lang.numlock, lock_state_x, lock_state_y);
lock_state_x -= lang.ly.capslock.len + 1; lock_state_x -= lang.capslock.len + 1;
if (lock_state.capslock) buffer.drawLabel(lang.ly.capslock, lock_state_x, lock_state_y); if (lock_state.capslock) buffer.drawLabel(lang.capslock, lock_state_x, lock_state_y);
} }
if (resolution_changed) { if (resolution_changed) {
@ -379,7 +403,7 @@ pub fn main() !void {
desktop.draw(); desktop.draw();
login.draw(); login.draw();
password.drawMasked(config.ly.asterisk); password.drawMasked(config.asterisk);
update = animate; update = animate;
} else { } else {
@ -399,13 +423,13 @@ pub fn main() !void {
// Calculate the maximum timeout based on current animations, or the (big) clock. If there's none, we wait for the event indefinitely 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) { if (animate) {
timeout = config.ly.min_refresh_delta; timeout = config.min_refresh_delta;
} else if (config.ly.bigclock and config.ly.clock.len == 0) { } else if (config.bigclock and config.clock == null) {
var tv = std.mem.zeroes(std.c.timeval); var tv = std.mem.zeroes(std.c.timeval);
_ = std.c.gettimeofday(&tv, null); _ = std.c.gettimeofday(&tv, null);
timeout = @intCast((60 - @rem(tv.tv_sec, 60)) * 1000 - @divTrunc(tv.tv_usec, 1000) + 1); timeout = @intCast((60 - @rem(tv.tv_sec, 60)) * 1000 - @divTrunc(tv.tv_usec, 1000) + 1);
} else if (config.ly.clock.len > 0 or auth_fails >= 10) { } else if (config.clock != null or auth_fails >= 10) {
var tv = std.mem.zeroes(std.c.timeval); var tv = std.mem.zeroes(std.c.timeval);
_ = std.c.gettimeofday(&tv, null); _ = std.c.gettimeofday(&tv, null);
@ -424,6 +448,11 @@ pub fn main() !void {
} else if (0xFFFF - event.key + 1 == restart_key) { } else if (0xFFFF - event.key + 1 == restart_key) {
restart = true; restart = true;
run = false; run = false;
} else if (0xFFFF - event.key + 1 == sleep_key and config.sleep_cmd != null) {
const pid = std.c.fork();
if (pid == 0) {
std.process.execv(allocator, &[_][]const u8{ "/bin/sh", "-c", config.sleep_cmd.? }) catch {};
}
} }
}, },
termbox.TB_KEY_CTRL_C => run = false, termbox.TB_KEY_CTRL_C => run = false,
@ -459,50 +488,50 @@ pub fn main() !void {
update = true; update = true;
}, },
termbox.TB_KEY_ENTER => authenticate: { termbox.TB_KEY_ENTER => authenticate: {
if (config.ly.save) save_last_settings: { if (config.save) save_last_settings: {
var file = std.fs.createFileAbsolute(config.ly.save_file, .{}) catch break :save_last_settings; var file = std.fs.createFileAbsolute(config.save_file, .{}) catch break :save_last_settings;
defer file.close(); defer file.close();
const writer = file.writer(); const save_data = Save{
try writer.writeIntLittle(u64, login.end); .user = login.text.items,
_ = try writer.write(login.text.items); .session_index = desktop.current,
try writer.writeIntLittle(u64, desktop.current); };
ini.writeFromStruct(save_data, file.writer(), null) catch break :save_last_settings;
} }
var has_error = false; var has_error = false;
auth.authenticate( auth.authenticate(
allocator, allocator,
config.ly.tty, config,
desktop, desktop,
login, login,
&password, &password,
config.ly.service_name, ) catch |err| {
config.ly.path,
config.ly.term_reset_cmd,
config.ly.wayland_cmd,
) catch {
has_error = true; has_error = true;
auth_fails += 1; auth_fails += 1;
active_input = .password; active_input = .password;
// TODO: Errors in info_line info_line = getAuthErrorMsg(err, lang);
if (config.ly.blank_password) password.clear(); if (config.clear_password) password.clear();
}; };
update = true; update = true;
if (!has_error) info_line = lang.ly.logout; if (!has_error) info_line = lang.logout;
std.process.execv(allocator, &[_][]const u8{ "/bin/sh", "-c", config.ly.term_restore_cursor_cmd }) catch break :authenticate; const pid = try std.os.fork();
if (pid == 0) {
std.process.execv(allocator, &[_][]const u8{ "/bin/sh", "-c", config.term_restore_cursor_cmd }) catch break :authenticate;
}
}, },
else => { else => {
switch (active_input) { switch (active_input) {
.session => desktop.handle(&event), .session => desktop.handle(&event),
.login => login.handle(&event) catch { .login => login.handle(&event) catch {
info_line = lang.ly.err_alloc; info_line = lang.err_alloc;
}, },
.password => password.handle(&event) catch { .password => password.handle(&event) catch {
info_line = lang.ly.err_alloc; info_line = lang.err_alloc;
}, },
} }
update = true; update = true;
@ -510,11 +539,36 @@ pub fn main() !void {
} }
} }
if (got_host_name) allocator.free(host_name_buffer);
if (shutdown) { if (shutdown) {
return std.process.execv(allocator, &[_][]const u8{ "/bin/sh", "-c", config.ly.shutdown_cmd }); return std.process.execv(allocator, &[_][]const u8{ "/bin/sh", "-c", config.shutdown_cmd });
} else if (restart) { } else if (restart) {
return std.process.execv(allocator, &[_][]const u8{ "/bin/sh", "-c", config.ly.restart_cmd }); return std.process.execv(allocator, &[_][]const u8{ "/bin/sh", "-c", config.restart_cmd });
} }
} }
fn getAuthErrorMsg(err: anyerror, lang: Lang) []const u8 {
return switch (err) {
error.GetPasswordNameFailed => lang.err_pwnam,
error.GroupInitializationFailed => lang.err_user_init,
error.SetUserGidFailed => lang.err_user_gid,
error.SetUserUidFailed => lang.err_user_uid,
error.ChangeDirectoryFailed => lang.err_perm_dir,
error.SetPathFailed => lang.err_path,
error.PamAccountExpired => lang.err_pam_acct_expired,
error.PamAuthError => lang.err_pam_auth,
error.PamAuthInfoUnavailable => lang.err_pam_authinfo_unavail,
error.PamBufferError => lang.err_pam_buf,
error.PamCredentialsError => lang.err_pam_cred_err,
error.PamCredentialsExpired => lang.err_pam_cred_expired,
error.PamCredentialsInsufficient => lang.err_pam_cred_insufficient,
error.PamCredentialsUnavailable => lang.err_pam_cred_unavail,
error.PamMaximumTries => lang.err_pam_maxtries,
error.PamNewAuthTokenRequired => lang.err_pam_authok_reqd,
error.PamPermissionDenied => lang.err_pam_perm_denied,
error.PamSessionError => lang.err_pam_session,
error.PamSystemError => lang.err_pam_sys,
error.PamUserUnknown => lang.err_pam_user_unknown,
error.PamAbort => lang.err_pam_abort,
else => "An unknown error occurred",
};
}

View File

@ -16,6 +16,7 @@ height: u64,
buffer: [*]termbox.tb_cell, buffer: [*]termbox.tb_cell,
fg: u8, fg: u8,
bg: u8, bg: u8,
border_fg: u8,
box_chars: struct { box_chars: struct {
left_up: u32, left_up: u32,
left_down: u32, left_down: u32,
@ -34,7 +35,7 @@ box_height: u64,
margin_box_v: u8, margin_box_v: u8,
margin_box_h: u8, margin_box_h: u8,
pub fn init(margin_box_v: u8, margin_box_h: u8, input_length: u8, labels_max_length: u64, fg: u8, bg: u8) TerminalBuffer { pub fn init(margin_box_v: u8, margin_box_h: u8, input_length: u8, labels_max_length: u64, fg: u8, bg: u8, border_fg: u8) TerminalBuffer {
var prng = std.rand.Isaac64.init(@intCast(std.time.timestamp())); var prng = std.rand.Isaac64.init(@intCast(std.time.timestamp()));
return .{ return .{
@ -44,6 +45,7 @@ pub fn init(margin_box_v: u8, margin_box_h: u8, input_length: u8, labels_max_len
.buffer = termbox.tb_cell_buffer(), .buffer = termbox.tb_cell_buffer(),
.fg = fg, .fg = fg,
.bg = bg, .bg = bg,
.border_fg = border_fg,
.box_chars = if (builtin.os.tag == .linux or builtin.os.tag.isBSD()) .{ .box_chars = if (builtin.os.tag == .linux or builtin.os.tag.isBSD()) .{
.left_up = 0x250C, .left_up = 0x250C,
.left_down = 0x2514, .left_down = 0x2514,
@ -107,13 +109,13 @@ pub fn drawBoxCenter(self: *TerminalBuffer, show_borders: bool, blank_box: bool)
self.box_y = y1; self.box_y = y1;
if (show_borders) { if (show_borders) {
termbox.tb_change_cell(@intCast(x1 - 1), @intCast(y1 - 1), self.box_chars.left_up, self.fg, self.bg); termbox.tb_change_cell(@intCast(x1 - 1), @intCast(y1 - 1), self.box_chars.left_up, self.border_fg, self.bg);
termbox.tb_change_cell(@intCast(x2), @intCast(y1 - 1), self.box_chars.right_up, self.fg, self.bg); termbox.tb_change_cell(@intCast(x2), @intCast(y1 - 1), self.box_chars.right_up, self.border_fg, self.bg);
termbox.tb_change_cell(@intCast(x1 - 1), @intCast(y2), self.box_chars.left_down, self.fg, self.bg); termbox.tb_change_cell(@intCast(x1 - 1), @intCast(y2), self.box_chars.left_down, self.border_fg, self.bg);
termbox.tb_change_cell(@intCast(x2), @intCast(y2), self.box_chars.right_down, self.fg, self.bg); termbox.tb_change_cell(@intCast(x2), @intCast(y2), self.box_chars.right_down, self.border_fg, self.bg);
var c1 = utils.initCell(self.box_chars.top, self.fg, self.bg); var c1 = utils.initCell(self.box_chars.top, self.border_fg, self.bg);
var c2 = utils.initCell(self.box_chars.bottom, self.fg, self.bg); var c2 = utils.initCell(self.box_chars.bottom, self.border_fg, self.bg);
for (0..self.box_width) |i| { for (0..self.box_width) |i| {
termbox.tb_put_cell(@intCast(x1 + i), @intCast(y1 - 1), &c1); termbox.tb_put_cell(@intCast(x1 + i), @intCast(y1 - 1), &c1);

View File

@ -3,6 +3,7 @@ const ini = @import("ini");
const enums = @import("../../enums.zig"); const enums = @import("../../enums.zig");
const interop = @import("../../interop.zig"); const interop = @import("../../interop.zig");
const TerminalBuffer = @import("../TerminalBuffer.zig"); const TerminalBuffer = @import("../TerminalBuffer.zig");
const Ini = @import("../../config/ini.zig").Ini;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const EnvironmentList = std.ArrayList(Environment); const EnvironmentList = std.ArrayList(Environment);
@ -11,27 +12,24 @@ const DisplayServer = enums.DisplayServer;
const termbox = interop.termbox; const termbox = interop.termbox;
pub const DESKTOP_ENTRY_MAX_SIZE = 8 * 1024;
const Desktop = @This(); const Desktop = @This();
pub const Environment = struct { pub const Environment = struct {
has_entry_buffer: bool, entry_ini: ?Ini(Entry) = null,
entry_buffer: []u8, name: []const u8 = "",
name: []const u8, xdg_name: []const u8 = "",
xdg_name: []const u8, cmd: []const u8 = "",
cmd: []const u8, specifier: []const u8 = "",
specifier: []const u8, display_server: DisplayServer = .wayland,
display_server: DisplayServer,
}; };
pub const Entry = struct { const DesktopEntry = struct {
Desktop_Entry: struct { Exec: []const u8 = "",
Exec: []const u8, Name: []const u8 = "",
Name: []const u8,
},
}; };
pub const Entry = struct { Desktop_Entry: DesktopEntry = DesktopEntry{} };
allocator: Allocator, allocator: Allocator,
buffer: *TerminalBuffer, buffer: *TerminalBuffer,
environments: EnvironmentList, environments: EnvironmentList,
@ -53,8 +51,8 @@ pub fn init(allocator: Allocator, buffer: *TerminalBuffer, max_length: u64) !Des
} }
pub fn deinit(self: Desktop) void { pub fn deinit(self: Desktop) void {
for (self.environments.items) |environment| { for (self.environments.items) |*environment| {
if (environment.has_entry_buffer) self.allocator.free(environment.entry_buffer); if (environment.entry_ini) |*entry_ini| entry_ini.deinit();
} }
self.environments.deinit(); self.environments.deinit();
@ -68,8 +66,7 @@ pub fn position(self: *Desktop, x: u64, y: u64, visible_length: u64) void {
pub fn addEnvironment(self: *Desktop, name: []const u8, cmd: []const u8, display_server: DisplayServer) !void { pub fn addEnvironment(self: *Desktop, name: []const u8, cmd: []const u8, display_server: DisplayServer) !void {
try self.environments.append(.{ try self.environments.append(.{
.has_entry_buffer = false, .entry_ini = null,
.entry_buffer = undefined,
.name = name, .name = name,
.xdg_name = name, // TODO .xdg_name = name, // TODO
.cmd = cmd, .cmd = cmd,
@ -84,10 +81,9 @@ pub fn addEnvironment(self: *Desktop, name: []const u8, cmd: []const u8, display
self.current = self.environments.items.len - 1; self.current = self.environments.items.len - 1;
} }
pub fn addEnvironmentWithBuffer(self: *Desktop, entry_buffer: []u8, name: []const u8, cmd: []const u8, display_server: DisplayServer) !void { pub fn addEnvironmentWithIni(self: *Desktop, entry_ini: Ini(Entry), name: []const u8, cmd: []const u8, display_server: DisplayServer) !void {
try self.environments.append(.{ try self.environments.append(.{
.has_entry_buffer = true, .entry_ini = entry_ini,
.entry_buffer = entry_buffer,
.name = name, .name = name,
.xdg_name = name, // TODO .xdg_name = name, // TODO
.cmd = cmd, .cmd = cmd,
@ -103,21 +99,19 @@ pub fn addEnvironmentWithBuffer(self: *Desktop, entry_buffer: []u8, name: []cons
} }
pub fn crawl(self: *Desktop, path: []const u8, display_server: DisplayServer) !void { pub fn crawl(self: *Desktop, path: []const u8, display_server: DisplayServer) !void {
var directory = std.fs.openDirAbsolute(path, .{}) catch return;
defer directory.close();
var iterable_directory = try std.fs.openIterableDirAbsolute(path, .{}); var iterable_directory = try std.fs.openIterableDirAbsolute(path, .{});
defer iterable_directory.close(); defer iterable_directory.close();
var iterator = iterable_directory.iterate(); var iterator = iterable_directory.iterate();
while (try iterator.next()) |item| { while (try iterator.next()) |item| {
var file = try directory.openFile(item.name, .{}); if (std.mem.eql(u8, std.fs.path.extension(item.name), ".desktop")) {
defer file.close(); const entry_path = try std.fmt.allocPrint(self.allocator, "{s}/{s}", .{ path, item.name });
defer self.allocator.free(entry_path);
var entry_ini = Ini(Entry).init(self.allocator);
var entry = try entry_ini.readToStruct(entry_path);
const buffer = try file.readToEndAlloc(self.allocator, DESKTOP_ENTRY_MAX_SIZE); try self.addEnvironmentWithIni(entry_ini, entry.Desktop_Entry.Name, entry.Desktop_Entry.Exec, display_server);
const entry = try ini.readToStruct(Entry, buffer); }
try self.addEnvironmentWithBuffer(buffer, entry.Desktop_Entry.Name, entry.Desktop_Entry.Exec, display_server);
} }
} }