mirror of https://github.com/fairyglade/ly.git
Changes in the Zig rewrite (#596)
* Everything * make matrix.zig a bit cleaner * make long lines shorter and add changelog * vi mode * update changelog * get errors from child process and (hopefully) fix some other things * fix utmp entry * run authentication in a child process * update changelog * small code improvements * change that * clear terminal on SIGTERM * Remove LogFile * moved ini to a lib, fixed alternative langs * fix logging out * oops * code improvements * consistency * clearing the env isn't needed anymore (afaik) * replace vi_mode with a bool * type aliases, avoiding zeroes(), breaking a long line * lowercase insert/normal, merge conditionals, code improvements
This commit is contained in:
parent
d6fc781192
commit
002630995d
11
build.zig
11
build.zig
|
@ -1,10 +1,15 @@
|
|||
const std = @import("std");
|
||||
|
||||
const ly_version = std.SemanticVersion{ .major = 1, .minor = 0, .patch = 0 };
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const data_directory = b.option([]const u8, "data_directory", "Specify a default data directory (default is /etc/ly)");
|
||||
|
||||
const build_options = b.addOptions();
|
||||
build_options.addOption([]const u8, "data_directory", data_directory orelse "/etc/ly");
|
||||
const version_str = b.fmt("{d}.{d}.{d}", .{ ly_version.major, ly_version.minor, ly_version.patch });
|
||||
|
||||
build_options.addOption([]const u8, "version", version_str);
|
||||
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
@ -29,10 +34,10 @@ pub fn build(b: *std.Build) void {
|
|||
.optimize = optimize,
|
||||
});
|
||||
|
||||
exe.addOptions("build_options", build_options);
|
||||
const zigini = b.dependency("zigini", .{ .target = target, .optimize = optimize });
|
||||
exe.addModule("zigini", zigini.module("zigini"));
|
||||
|
||||
const ini = b.dependency("ini", .{});
|
||||
exe.addModule("ini", ini.module("ini"));
|
||||
exe.addOptions("build_options", build_options);
|
||||
|
||||
const clap = b.dependency("clap", .{ .target = target, .optimize = optimize });
|
||||
exe.addModule("clap", clap.module("clap"));
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
.name = "ly",
|
||||
.version = "1.0.0",
|
||||
.dependencies = .{
|
||||
.ini = .{
|
||||
.url = "https://github.com/AnErrupTion/zig-ini/archive/7ab77196a4dc63d1ede50e0a1af1a8325c152f2f.tar.gz",
|
||||
.hash = "12204b1d133060dd0c4304d5e0fbdb33a70038118e7c112b14b5f8d176ea15cd5939",
|
||||
},
|
||||
.clap = .{
|
||||
.url = "https://github.com/Hejsil/zig-clap/archive/f49b94700e0761b7514abdca0e4f0e7f3f938a93.tar.gz",
|
||||
.hash = "1220f48518ce22882e102255ed3bcdb7aeeb4891f50b2cdd3bd74b5b2e24d3149ba2",
|
||||
},
|
||||
}
|
||||
.zigini = .{
|
||||
.url = "https://github.com/Kawaii-Ash/zigini/archive/91f47e46591982fc559afa3248749c1d29a0fa2a.tar.gz",
|
||||
.hash = "12209908f2773f730fbca024c80dc7f48dce15a6527b2387f3768968f5bae0d3931e",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
# Zig Rewrite (Version 1.0.0)
|
||||
|
||||
## Config Options
|
||||
|
||||
res/config.ini contains all of the available config options and their default values.
|
||||
|
||||
### Additions
|
||||
|
||||
+ `border_fg` has been introduced to change the color of the borders.
|
||||
+ `term_restore_cursor_cmd` should restore the cursor to it's usual state.
|
||||
+ `vi_mode` to enable vi keybindings.
|
||||
+ `sleep_key` and `sleep_cmd`.
|
||||
|
||||
Note: `sleep_cmd` is unset by default, meaning it's hidden and has no effect.
|
||||
|
||||
### Changes
|
||||
|
||||
+ xinitrc can be set to null to hide it.
|
||||
+ `blank_password` has been renamed to `clear_password`.
|
||||
|
||||
### Removals
|
||||
|
||||
+ `wayland_specifier` has been removed.
|
||||
|
||||
## Save File
|
||||
|
||||
The save file is now in .ini format.
|
||||
|
||||
Example:
|
||||
|
||||
```ini
|
||||
user = ash
|
||||
session_index = 0
|
||||
```
|
||||
|
||||
## Misc
|
||||
|
||||
+ Display server name added next to selected session.
|
||||
+ getty@tty2 has been added as a conflict in res/ly.service, so if it is running, ly should still be able to start.
|
||||
+ `XDG_CURRENT_DESKTOP` is now set by ly.
|
||||
+ LANG is no longer set by ly.
|
||||
+ X Server PID is fetched from /tmp/X{d}.lock to be able to kill the process since it detaches.
|
||||
+ Non .desktop files are now ignored in sessions directory.
|
||||
+ PAM auth is now done in a child process. (Fixes some issues with logging out and back in).
|
||||
+ When ly receives SIGTERM, the terminal is now cleared.
|
|
@ -1,22 +1,23 @@
|
|||
[ly]
|
||||
|
||||
# The active animation
|
||||
# none -> Nothing (default)
|
||||
# doom -> PSX DOOM fire
|
||||
# matrix -> CMatrix
|
||||
animation = doom
|
||||
animation = none
|
||||
|
||||
# Format string for clock in top right corner (see strftime specification)
|
||||
clock = %c
|
||||
# Format string for clock in top right corner (see strftime specification). Example: %c
|
||||
clock = null
|
||||
|
||||
# Enable/disable big clock
|
||||
bigclock = true
|
||||
bigclock = false
|
||||
|
||||
# The character used to mask the password
|
||||
asterisk = *
|
||||
|
||||
# Erase password input on failure
|
||||
blank_password = false
|
||||
clear_password = false
|
||||
|
||||
# Enable vi keybindings
|
||||
vi_mode = false
|
||||
|
||||
# The `fg` and `bg` color settings take a digit 0-8 corresponding to:
|
||||
#define TB_DEFAULT 0x00
|
||||
|
@ -43,6 +44,9 @@ bg = 0
|
|||
# Foreground color id
|
||||
fg = 8
|
||||
|
||||
# Border color
|
||||
border_fg = 8
|
||||
|
||||
# Blank main box background
|
||||
# Setting to false will make it transparent
|
||||
blank_box = true
|
||||
|
@ -62,7 +66,6 @@ max_desktop_len = 100
|
|||
max_login_len = 255
|
||||
max_password_len = 255
|
||||
|
||||
|
||||
# Input box active by default on startup
|
||||
# Available inputs: session, login, password
|
||||
default_input = login
|
||||
|
@ -76,7 +79,6 @@ save = true
|
|||
# File in which to save and load the default desktop and login
|
||||
save_file = /etc/ly/save
|
||||
|
||||
|
||||
# Remove power management command hints
|
||||
hide_key_hints = false
|
||||
|
||||
|
@ -86,28 +88,31 @@ shutdown_key = F1
|
|||
# Specifies the key used for restart (F1-F12)
|
||||
restart_key = F2
|
||||
|
||||
# Specifies the key used for sleep (F1-F12)
|
||||
sleep_key = F3
|
||||
|
||||
# Command executed when pressing shutdown_key
|
||||
shutdown_cmd = /sbin/shutdown -a now
|
||||
|
||||
# Command executed when pressing restart_key
|
||||
restart_cmd = /sbin/shutdown -r now
|
||||
|
||||
# Command executed when pressing sleep key (can be null)
|
||||
sleep_cmd = null
|
||||
|
||||
# Active language
|
||||
# Available languages are found in /etc/ly/lang/
|
||||
lang = en
|
||||
|
||||
|
||||
# TTY in use
|
||||
tty = 2
|
||||
|
||||
# Console path
|
||||
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
|
||||
|
||||
|
||||
# Event timeout in milliseconds
|
||||
min_refresh_delta = 5
|
||||
|
||||
|
@ -123,15 +128,13 @@ term_restore_cursor_cmd = /usr/bin/tput cnorm
|
|||
# Cookie generator
|
||||
mcookie_cmd = /usr/bin/mcookie
|
||||
|
||||
|
||||
# Wayland setup command
|
||||
wayland_cmd = /etc/ly/wsetup.sh
|
||||
|
||||
# Wayland desktop environments
|
||||
waylandsessions = /usr/share/wayland-sessions
|
||||
|
||||
|
||||
# xinitrc
|
||||
# xinitrc (hidden if null)
|
||||
xinitrc = ~/.xinitrc
|
||||
|
||||
# Xorg server command
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
[ly]
|
||||
capslock = Bloq Majús
|
||||
err_alloc = falla d'assignació de memòria
|
||||
err_bounds = índex fora de límit
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
[ly]
|
||||
capslock = capslock
|
||||
err_alloc = alokace paměti selhala
|
||||
err_bounds = index je mimo hranice pole
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
[ly]
|
||||
capslock = Feststelltaste
|
||||
err_alloc = Speicherzuweisung fehlgeschlagen
|
||||
err_bounds = Listenindex ist außerhalb des Bereichs
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
[ly]
|
||||
capslock = capslock
|
||||
err_alloc = failed memory allocation
|
||||
err_bounds = out-of-bounds index
|
||||
|
@ -35,12 +34,16 @@ 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
|
||||
insert = insert
|
||||
login = login
|
||||
logout = logged out
|
||||
normal = normal
|
||||
numlock = numlock
|
||||
password = password
|
||||
restart = reboot
|
||||
shell = shell
|
||||
shutdown = shutdown
|
||||
sleep = sleep
|
||||
wayland = wayland
|
||||
xinitrc = xinitrc
|
||||
x11 = x11
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
[ly]
|
||||
capslock = Bloq Mayús
|
||||
err_alloc = asignación de memoria fallida
|
||||
err_bounds = índice fuera de límites
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
[ly]
|
||||
capslock = verr.maj
|
||||
capslock = verr.maj
|
||||
err_alloc = échec d'allocation mémoire
|
||||
err_bounds = indice hors-limite
|
||||
err_chdir = échec de l'ouverture du répertoire home
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
[ly]
|
||||
capslock = capslock
|
||||
err_alloc = impossibile allocare memoria
|
||||
err_bounds = indice fuori limite
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
[ly]
|
||||
capslock = capslock
|
||||
err_alloc = nieudana alokacja pamięci
|
||||
err_bounds = indeks poza granicami
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
[ly]
|
||||
capslock = capslock
|
||||
err_alloc = erro na atribuição de memória
|
||||
err_bounds = índice fora de limites
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
[ly]
|
||||
capslock = caixa alta
|
||||
err_alloc = alocação de memória malsucedida
|
||||
err_bounds = índice fora de limites
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
[ly]
|
||||
capslock = capslock
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
[ly]
|
||||
capslock = capslock
|
||||
err_alloc = не удалось выделить память
|
||||
err_bounds = за пределами индекса
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
[ly]
|
||||
capslock = capslock
|
||||
err_alloc = neuspijesna alokacija memorije
|
||||
err_bounds = izvan granica indeksa
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
[ly]
|
||||
capslock = capslock
|
||||
err_alloc = misslyckad minnesallokering
|
||||
err_bounds = utanför banan index
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
[ly]
|
||||
capslock = capslock
|
||||
err_alloc = basarisiz bellek ayirma
|
||||
err_bounds = sinirlarin disinda dizin
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
[ly]
|
||||
capslock = capslock
|
||||
err_alloc = невдале виділення пам'яті
|
||||
err_bounds = поза межами індексу
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
Description=TUI display manager
|
||||
After=systemd-user-sessions.service plymouth-quit-wait.service
|
||||
After=getty@tty2.service
|
||||
Conflicts=getty@tty2.service
|
||||
|
||||
[Service]
|
||||
Type=idle
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
const std = @import("std");
|
||||
|
||||
const ErrInt = std.meta.Int(.unsigned, @bitSizeOf(anyerror));
|
||||
|
||||
const ErrorHandler = packed struct {
|
||||
has_error: bool = false,
|
||||
err_int: ErrInt = 0,
|
||||
};
|
||||
|
||||
const SharedError = @This();
|
||||
|
||||
data: []align(std.mem.page_size) u8,
|
||||
|
||||
pub fn init() !SharedError {
|
||||
const data = try std.os.mmap(null, @sizeOf(ErrorHandler), std.os.PROT.READ | std.os.PROT.WRITE, std.os.MAP.SHARED | std.os.MAP.ANONYMOUS, -1, 0);
|
||||
|
||||
return .{ .data = data };
|
||||
}
|
||||
|
||||
pub fn deinit(self: *SharedError) void {
|
||||
defer std.os.munmap(self.data);
|
||||
}
|
||||
|
||||
pub fn writeError(self: SharedError, err: anyerror) void {
|
||||
var buf_stream = std.io.fixedBufferStream(self.data);
|
||||
const writer = buf_stream.writer();
|
||||
writer.writeStruct(ErrorHandler{ .has_error = true, .err_int = @intFromError(err) }) catch {};
|
||||
}
|
||||
|
||||
pub fn readError(self: SharedError) ?anyerror {
|
||||
var buf_stream = std.io.fixedBufferStream(self.data);
|
||||
const reader = buf_stream.reader();
|
||||
const err_handler = try reader.readStruct(ErrorHandler);
|
||||
|
||||
if (err_handler.has_error)
|
||||
return @errorFromInt(err_handler.err_int);
|
||||
|
||||
return null;
|
||||
}
|
|
@ -66,10 +66,9 @@ pub fn realloc(self: *Matrix) !void {
|
|||
self.lines = lines;
|
||||
}
|
||||
|
||||
// TODO: Fix!!
|
||||
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;
|
||||
if (self.count > FRAME_DELAY) {
|
||||
self.frame += 1;
|
||||
|
@ -78,81 +77,74 @@ pub fn draw(self: *Matrix) void {
|
|||
|
||||
var x: u64 = 0;
|
||||
while (x < self.terminal_buffer.width) : (x += 2) {
|
||||
var line = self.lines[x];
|
||||
var tail: u64 = 0;
|
||||
var line = &self.lines[x];
|
||||
if (self.frame <= line.update) continue;
|
||||
|
||||
var tail: u64 = 0;
|
||||
if (self.dots[x].value == -1 and self.dots[self.terminal_buffer.width + x].value == ' ') {
|
||||
if (line.space <= 0) {
|
||||
const random = self.terminal_buffer.random.int(i16);
|
||||
const h: isize = @intCast(self.terminal_buffer.height);
|
||||
line.length = @mod(random, h - 3) + 3;
|
||||
line.space = @mod(random, h) + 1;
|
||||
self.dots[x].value = @mod(random, MAX_CODEPOINT) + MIN_CODEPOINT;
|
||||
} else {
|
||||
if (line.space > 0) {
|
||||
line.space -= 1;
|
||||
} else {
|
||||
const randint = self.terminal_buffer.random.int(i16);
|
||||
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: u64 = 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];
|
||||
}
|
||||
|
||||
self.lines[x] = line;
|
||||
first_column = true;
|
||||
|
||||
var y: u64 = 0;
|
||||
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
|
||||
while (dot.value == ' ' or dot.value == -1) {
|
||||
y += 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
|
||||
tail = y;
|
||||
seg_length = 0;
|
||||
while (y <= self.terminal_buffer.height and dot.value != ' ' and dot.value != -1) {
|
||||
dot.is_head = false;
|
||||
if (MID_SCROLL_CHANGE) {
|
||||
const random = self.terminal_buffer.random.int(i16);
|
||||
if (@mod(random, 8) == 0) dot.value = @mod(random, MAX_CODEPOINT) + MIN_CODEPOINT;
|
||||
// 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;
|
||||
}
|
||||
self.dots[y * self.terminal_buffer.width + x] = dot;
|
||||
|
||||
y += 1;
|
||||
seg_length += 1;
|
||||
dot = self.dots[y * self.terminal_buffer.width + x];
|
||||
}
|
||||
|
||||
// The head is down offscreen
|
||||
if (y > self.terminal_buffer.height) {
|
||||
self.dots[tail * self.terminal_buffer.width + x].value = ' ';
|
||||
continue; // TODO: Shouldn't this be break?
|
||||
y += 1;
|
||||
seg_len += 1;
|
||||
// Head's down offscreen
|
||||
if (y > buf_height) {
|
||||
self.dots[buf_width * tail + x].value = ' ';
|
||||
break :height_it;
|
||||
}
|
||||
|
||||
const random = self.terminal_buffer.random.int(i16);
|
||||
self.dots[y * self.terminal_buffer.width + x].value = @mod(random, MAX_CODEPOINT) + MIN_CODEPOINT;
|
||||
self.dots[y * self.terminal_buffer.width + x].is_head = true;
|
||||
|
||||
if (seg_length > line.length or !first_column) {
|
||||
self.dots[tail * self.terminal_buffer.width + x].value = ' ';
|
||||
self.dots[x].value = -1;
|
||||
}
|
||||
first_column = false;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var x: u64 = 0;
|
||||
while (x < self.terminal_buffer.width) : (x += 2) {
|
||||
while (x < buf_width) : (x += 2) {
|
||||
var y: u64 = 1;
|
||||
while (y <= self.terminal_buffer.height) : (y += 1) {
|
||||
const dot = self.dots[y * self.terminal_buffer.width + x];
|
||||
const dot = self.dots[buf_width * y + x];
|
||||
var fg: u32 = @intCast(termbox.TB_GREEN);
|
||||
|
||||
if (dot.value == -1 or dot.value == ' ') {
|
||||
|
|
369
src/auth.zig
369
src/auth.zig
|
@ -4,35 +4,20 @@ const interop = @import("interop.zig");
|
|||
const TerminalBuffer = @import("tui/TerminalBuffer.zig");
|
||||
const Desktop = @import("tui/components/Desktop.zig");
|
||||
const Text = @import("tui/components/Text.zig");
|
||||
const Config = @import("config/Config.zig");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const utmp = interop.utmp;
|
||||
const Utmp = utmp.utmp;
|
||||
const SharedError = @import("SharedError.zig");
|
||||
|
||||
var login_conv_allocator: Allocator = undefined;
|
||||
|
||||
pub fn authenticate(
|
||||
allocator: Allocator,
|
||||
tty: u8,
|
||||
desktop: Desktop,
|
||||
login: Text,
|
||||
password: *Text,
|
||||
service_name: []const u8,
|
||||
path: []const u8,
|
||||
term_reset_cmd: []const u8,
|
||||
wayland_cmd: []const u8,
|
||||
) !void {
|
||||
login_conv_allocator = allocator;
|
||||
|
||||
const uid = interop.getuid();
|
||||
|
||||
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}", .{tty});
|
||||
const uid_str = try std.fmt.bufPrintZ(&uid_buffer, "/run/user/{d}", .{uid});
|
||||
pub fn authenticate(allocator: Allocator, config: Config, desktop: Desktop, login: Text, password: *Text) !void {
|
||||
var tty_buffer: [2]u8 = undefined;
|
||||
const tty_str = try std.fmt.bufPrintZ(&tty_buffer, "{d}", .{config.tty});
|
||||
const current_environment = desktop.environments.items[desktop.current];
|
||||
|
||||
// Set the XDG environment variables
|
||||
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
|
||||
const login_text_z = try allocator.dupeZ(u8, login.text.items);
|
||||
|
@ -53,12 +38,10 @@ pub fn authenticate(
|
|||
};
|
||||
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);
|
||||
|
||||
var status = interop.pam.pam_start(service_name_z.ptr, null, &conv, &handle);
|
||||
defer status = interop.pam.pam_end(handle, status);
|
||||
|
||||
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
|
||||
|
||||
// Do the PAM routine
|
||||
|
@ -74,9 +57,6 @@ pub fn authenticate(
|
|||
status = interop.pam.pam_open_session(handle, 0);
|
||||
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
|
||||
|
||||
// Clear the password
|
||||
password.clear();
|
||||
|
||||
// Get password structure from username
|
||||
const maybe_pwd = interop.getpwnam(login_text_z.ptr);
|
||||
interop.endpwent();
|
||||
|
@ -103,30 +83,35 @@ pub fn authenticate(
|
|||
}
|
||||
}
|
||||
|
||||
// Restore the previous terminal mode
|
||||
interop.termbox.tb_clear();
|
||||
interop.termbox.tb_present();
|
||||
interop.termbox.tb_shutdown();
|
||||
|
||||
const pid = std.c.fork();
|
||||
var shared_err = try SharedError.init();
|
||||
defer shared_err.deinit();
|
||||
|
||||
const pid = try std.os.fork();
|
||||
if (pid == 0) {
|
||||
// Set the user information
|
||||
status = interop.initgroups(pwd.pw_name, pwd.pw_gid);
|
||||
if (status != 0) return error.GroupInitializationFailed;
|
||||
if (status != 0) {
|
||||
shared_err.writeError(error.GroupInitializationFailed);
|
||||
std.os.exit(1);
|
||||
}
|
||||
|
||||
status = std.c.setgid(pwd.pw_gid);
|
||||
if (status != 0) return error.SetUserGidFailed;
|
||||
if (status != 0) {
|
||||
shared_err.writeError(error.SetUserGidFailed);
|
||||
std.os.exit(1);
|
||||
}
|
||||
|
||||
status = std.c.setuid(pwd.pw_uid);
|
||||
if (status != 0) return error.SetUserUidFailed;
|
||||
if (status != 0) {
|
||||
shared_err.writeError(error.SetUserUidFailed);
|
||||
std.os.exit(1);
|
||||
}
|
||||
|
||||
// Set up the environment (this clears the currently set one)
|
||||
try initEnv(allocator, pwd, path);
|
||||
|
||||
// Reset the XDG environment variables from before
|
||||
setXdgSessionEnv(current_environment.display_server);
|
||||
try setXdgEnv(allocator, tty_str, uid_str, current_environment.xdg_name);
|
||||
// Set up the environment
|
||||
initEnv(allocator, pwd, config.path) catch |e| {
|
||||
shared_err.writeError(e);
|
||||
std.os.exit(1);
|
||||
};
|
||||
|
||||
// Set the PAM variables
|
||||
const pam_env_vars = interop.pam.pam_getenvlist(handle);
|
||||
|
@ -141,34 +126,47 @@ pub fn authenticate(
|
|||
|
||||
// Execute what the user requested
|
||||
status = interop.chdir(pwd.pw_dir);
|
||||
if (status != 0) return error.ChangeDirectoryFailed;
|
||||
if (status != 0) {
|
||||
shared_err.writeError(error.ChangeDirectoryFailed);
|
||||
std.os.exit(1);
|
||||
}
|
||||
|
||||
try resetTerminal(allocator, pwd.pw_shell, term_reset_cmd);
|
||||
resetTerminal(allocator, pwd.pw_shell, config.term_reset_cmd) catch |e| {
|
||||
shared_err.writeError(e);
|
||||
std.os.exit(1);
|
||||
};
|
||||
|
||||
switch (current_environment.display_server) {
|
||||
.wayland => try executeWaylandCmd(pwd.pw_shell, wayland_cmd, current_environment.cmd),
|
||||
.wayland => executeWaylandCmd(pwd.pw_shell, config.wayland_cmd, current_environment.cmd) catch |e| {
|
||||
shared_err.writeError(e);
|
||||
std.os.exit(1);
|
||||
},
|
||||
.shell => executeShellCmd(pwd.pw_shell),
|
||||
.xinitrc, .x11 => {
|
||||
// TODO
|
||||
var vt_buf: [5]u8 = undefined;
|
||||
const vt = std.fmt.bufPrint(&vt_buf, "vt{d}", .{config.tty}) catch |e| {
|
||||
shared_err.writeError(e);
|
||||
std.os.exit(1);
|
||||
};
|
||||
executeX11Cmd(pwd.pw_shell, pwd.pw_dir, config, current_environment.cmd, vt) catch |e| {
|
||||
shared_err.writeError(e);
|
||||
std.os.exit(1);
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
std.os.exit(0);
|
||||
}
|
||||
|
||||
// TODO: Add UTMP entry
|
||||
var entry: Utmp = std.mem.zeroes(Utmp);
|
||||
addUtmpEntry(&entry, pwd.pw_name, pid) catch {};
|
||||
|
||||
// Wait for the session to stop
|
||||
_ = std.c.waitpid(pid, &status, 0);
|
||||
// TODO: Remove UTMP entry
|
||||
_ = std.os.waitpid(pid, 0);
|
||||
|
||||
try resetTerminal(allocator, pwd.pw_shell, term_reset_cmd);
|
||||
removeUtmpEntry(&entry);
|
||||
|
||||
// Re-initialize termbox
|
||||
_ = interop.termbox.tb_init();
|
||||
_ = interop.termbox.tb_select_output_mode(interop.termbox.TB_OUTPUT_NORMAL);
|
||||
|
||||
// TODO: Reload the DE list on log out
|
||||
try resetTerminal(allocator, pwd.pw_shell, config.term_reset_cmd);
|
||||
|
||||
// Close the PAM session
|
||||
status = interop.pam.pam_close_session(handle, 0);
|
||||
|
@ -176,25 +174,30 @@ pub fn authenticate(
|
|||
|
||||
status = interop.pam.pam_setcred(handle, 0);
|
||||
if (status != 0) return pamDiagnose(status);
|
||||
|
||||
status = interop.pam.pam_end(handle, status);
|
||||
if (status != 0) return pamDiagnose(status);
|
||||
|
||||
if (shared_err.readError()) |err| return err;
|
||||
}
|
||||
|
||||
fn initEnv(allocator: Allocator, pwd: *interop.passwd, path: []const u8) !void {
|
||||
const term = interop.getenv("TERM");
|
||||
const lang = interop.getenv("LANG");
|
||||
fn initEnv(allocator: Allocator, pwd: *interop.passwd, path: ?[]const u8) !void {
|
||||
const term_env = std.os.getenv("TERM");
|
||||
|
||||
if (term[0] == 0) _ = interop.setenv("TERM", "linux", 1);
|
||||
if (lang[0] == 0) _ = interop.setenv("LANG", "C", 1);
|
||||
if (term_env) |term| _ = interop.setenv("TERM", term, 1);
|
||||
_ = interop.setenv("HOME", pwd.pw_dir, 1);
|
||||
_ = interop.setenv("PWD", pwd.pw_dir, 1);
|
||||
_ = interop.setenv("SHELL", pwd.pw_shell, 1);
|
||||
_ = interop.setenv("USER", pwd.pw_name, 1);
|
||||
_ = interop.setenv("LOGNAME", pwd.pw_name, 1);
|
||||
|
||||
const path_z = try allocator.dupeZ(u8, path);
|
||||
defer allocator.free(path_z);
|
||||
if (path != null) {
|
||||
const path_z = try allocator.dupeZ(u8, path.?);
|
||||
defer allocator.free(path_z);
|
||||
|
||||
const status = interop.setenv("PATH", path_z, 1);
|
||||
if (status != 0) return error.SetPathFailed;
|
||||
const status = interop.setenv("PATH", path_z, 1);
|
||||
if (status != 0) return error.SetPathFailed;
|
||||
}
|
||||
}
|
||||
|
||||
fn setXdgSessionEnv(display_server: enums.DisplayServer) void {
|
||||
|
@ -205,10 +208,15 @@ fn setXdgSessionEnv(display_server: enums.DisplayServer) void {
|
|||
}, 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);
|
||||
defer allocator.free(desktop_name_z);
|
||||
|
||||
const uid = interop.getuid();
|
||||
var uid_buffer: [10 + @sizeOf(u32) + 1]u8 = undefined;
|
||||
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_SESSION_CLASS", "user", 0);
|
||||
_ = interop.setenv("XDG_SESSION_ID", "1", 0);
|
||||
|
@ -226,28 +234,24 @@ fn loginConv(
|
|||
const message_count: u32 = @intCast(num_msg);
|
||||
const messages = msg.?;
|
||||
|
||||
const response = login_conv_allocator.alloc(interop.pam.pam_response, message_count) catch return interop.pam.PAM_BUF_ERR;
|
||||
defer login_conv_allocator.free(response);
|
||||
const allocator = std.heap.c_allocator;
|
||||
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: {
|
||||
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 => {
|
||||
const appdata: ?*align(8) anyopaque = @alignCast(appdata_ptr);
|
||||
const data: [*][:0]u8 = @ptrCast(appdata.?);
|
||||
const username = data[0];
|
||||
|
||||
response[i].resp = username;
|
||||
const data: [*][*:0]u8 = @ptrCast(@alignCast(appdata_ptr));
|
||||
username = allocator.dupeZ(u8, std.mem.span(data[0])) catch return interop.pam.PAM_BUF_ERR;
|
||||
response[i].resp = username.?.ptr;
|
||||
},
|
||||
interop.pam.PAM_PROMPT_ECHO_OFF => {
|
||||
const appdata: ?*align(8) anyopaque = @alignCast(appdata_ptr);
|
||||
const data: [*][:0]u8 = @ptrCast(appdata.?);
|
||||
const password = data[1];
|
||||
|
||||
response[i].resp = password;
|
||||
const data: [*][*:0]u8 = @ptrCast(@alignCast(appdata_ptr));
|
||||
password = allocator.dupeZ(u8, std.mem.span(data[1])) catch return interop.pam.PAM_BUF_ERR;
|
||||
response[i].resp = password.?.ptr;
|
||||
},
|
||||
interop.pam.PAM_ERROR_MSG => {
|
||||
status = interop.pam.PAM_CONV_ERR;
|
||||
|
@ -257,7 +261,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;
|
||||
}
|
||||
|
@ -266,28 +277,210 @@ fn resetTerminal(allocator: Allocator, shell: [*:0]const u8, term_reset_cmd: []c
|
|||
const term_reset_cmd_z = try allocator.dupeZ(u8, term_reset_cmd);
|
||||
defer allocator.free(term_reset_cmd_z);
|
||||
|
||||
const pid = std.c.fork();
|
||||
const pid = try std.os.fork();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
var status: c_int = undefined;
|
||||
_ = std.c.waitpid(pid, &status, 0);
|
||||
_ = std.os.waitpid(pid, 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, .{});
|
||||
defer file.close();
|
||||
|
||||
var file_buf: [20]u8 = undefined;
|
||||
var fbs = std.io.fixedBufferStream(&file_buf);
|
||||
|
||||
_ = try file.reader().streamUntilDelimiter(fbs.writer(), '\n', 20);
|
||||
const line = fbs.getWritten();
|
||||
|
||||
return std.fmt.parseInt(i32, std.mem.trim(u8, line, " "), 10);
|
||||
}
|
||||
|
||||
fn createXauthFile(pwd: [:0]const u8) ![:0]const u8 {
|
||||
var xauth_buf: [100]u8 = undefined;
|
||||
var xauth_dir: [:0]const u8 = undefined;
|
||||
var xdg_rt_dir = std.os.getenv("XDG_RUNTIME_DIR");
|
||||
var xauth_file: []const u8 = "lyxauth";
|
||||
|
||||
if (xdg_rt_dir == null) {
|
||||
const xdg_cfg_home = std.os.getenv("XDG_CONFIG_HOME");
|
||||
var sb: std.c.Stat = undefined;
|
||||
if (xdg_cfg_home == null) {
|
||||
xauth_dir = try std.fmt.bufPrintZ(&xauth_buf, "{s}/.config", .{pwd});
|
||||
_ = 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", .{xdg_cfg_home.?});
|
||||
}
|
||||
|
||||
_ = 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";
|
||||
};
|
||||
}
|
||||
} else {
|
||||
xauth_dir = xdg_rt_dir.?;
|
||||
}
|
||||
|
||||
// Trim trailing slashes
|
||||
var i = xauth_dir.len - 1;
|
||||
while (xauth_dir[i] == '/') i -= 1;
|
||||
const trimmed_xauth_dir = xauth_dir[0 .. i + 1];
|
||||
|
||||
var buf: [256]u8 = undefined;
|
||||
const xauthority: [:0]u8 = try std.fmt.bufPrintZ(&buf, "{s}/{s}", .{ trimmed_xauth_dir, xauth_file });
|
||||
const file = try std.fs.createFileAbsolute(xauthority, .{});
|
||||
file.close();
|
||||
|
||||
return xauthority;
|
||||
}
|
||||
|
||||
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});
|
||||
|
||||
const xauthority = try createXauthFile(pwd);
|
||||
_ = interop.setenv("XAUTHORITY", xauthority, 1);
|
||||
_ = interop.setenv("DISPLAY", display_name, 1);
|
||||
|
||||
const pid = try std.os.fork();
|
||||
if (pid == 0) {
|
||||
var cmd_buffer: [1024]u8 = undefined;
|
||||
const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} add {s} . $({s})", .{ xauth_cmd, display_name, mcookie_cmd }) catch std.os.exit(1);
|
||||
_ = interop.execl(shell, shell, "-c", cmd_str.ptr, @as([*c]const u8, 0));
|
||||
std.os.exit(0);
|
||||
}
|
||||
|
||||
_ = std.os.waitpid(pid, 0);
|
||||
}
|
||||
|
||||
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: [1024]u8 = undefined;
|
||||
|
||||
const cmd_str = try std.fmt.bufPrintZ(&cmd_buffer, "{s} {s}", .{ wayland_cmd, desktop_cmd });
|
||||
_ = interop.execl(shell, shell, "-c", cmd_str.ptr, @as([*c]const u8, 0));
|
||||
}
|
||||
|
||||
fn executeX11Cmd(shell: [*:0]const u8, pw_dir: [*:0]const u8, config: Config, desktop_cmd: []const u8, vt: []const u8) !void {
|
||||
const display_num = try getFreeDisplay();
|
||||
var buf: [5]u8 = undefined;
|
||||
var display_name = try std.fmt.bufPrintZ(&buf, ":{d}", .{display_num});
|
||||
try xauth(display_name, shell, pw_dir, config.xauth_cmd, config.mcookie_cmd);
|
||||
|
||||
const pid = try std.os.fork();
|
||||
if (pid == 0) {
|
||||
var cmd_buffer: [1024]u8 = undefined;
|
||||
const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s}", .{ config.x_cmd, display_name, vt }) catch std.os.exit(1);
|
||||
_ = interop.execl(shell, shell, "-c", cmd_str.ptr, @as([*c]const u8, 0));
|
||||
std.os.exit(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);
|
||||
|
||||
const xorg_pid = try std.os.fork();
|
||||
if (xorg_pid == 0) {
|
||||
var cmd_buffer: [1024]u8 = undefined;
|
||||
const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} {s}", .{ config.x_cmd_setup, desktop_cmd }) catch std.os.exit(1);
|
||||
_ = interop.execl(shell, shell, "-c", cmd_str.ptr, @as([*c]const u8, 0));
|
||||
std.os.exit(0);
|
||||
}
|
||||
|
||||
var status: c_int = 0;
|
||||
_ = std.os.waitpid(xorg_pid, 0);
|
||||
interop.xcb.xcb_disconnect(xcb);
|
||||
|
||||
_ = std.c.kill(x_pid, 0);
|
||||
if (std.c._errno().* != interop.ESRCH) {
|
||||
_ = std.c.kill(x_pid, interop.SIGTERM);
|
||||
_ = std.c.waitpid(x_pid, &status, 0);
|
||||
}
|
||||
}
|
||||
|
||||
fn executeShellCmd(shell: [*:0]const u8) void {
|
||||
_ = 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.bufPrintZ(&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.bufPrintZ(&username_buf, "{s}", .{username});
|
||||
|
||||
entry.ut_user = username_buf;
|
||||
|
||||
var host: [256]u8 = undefined;
|
||||
host[0] = 0;
|
||||
entry.ut_host = host;
|
||||
|
||||
var tv: std.c.timeval = undefined;
|
||||
_ = std.c.gettimeofday(&tv, null);
|
||||
|
||||
entry.ut_tv = .{
|
||||
.tv_sec = @intCast(tv.tv_sec),
|
||||
.tv_usec = @intCast(tv.tv_usec),
|
||||
};
|
||||
entry.ut_addr_v6[0] = 0;
|
||||
|
||||
utmp.setutent();
|
||||
_ = utmp.pututline(entry);
|
||||
utmp.endutent();
|
||||
}
|
||||
|
||||
fn removeUtmpEntry(entry: *Utmp) void {
|
||||
entry.ut_type = utmp.DEAD_PROCESS;
|
||||
entry.ut_line[0] = 0;
|
||||
entry.ut_user[0] = 0;
|
||||
utmp.setutent();
|
||||
_ = utmp.pututline(entry);
|
||||
utmp.endutent();
|
||||
}
|
||||
|
||||
fn pamDiagnose(status: c_int) anyerror {
|
||||
return switch (status) {
|
||||
interop.pam.PAM_ACCT_EXPIRED => return error.PamAccountExpired,
|
||||
|
|
|
@ -100,9 +100,9 @@ const E = [_]u32{
|
|||
// zig fmt: on
|
||||
|
||||
pub fn clockCell(animate: bool, char: u8, fg: u8, bg: u8) [SIZE]termbox.tb_cell {
|
||||
var cells = std.mem.zeroes([SIZE]termbox.tb_cell);
|
||||
var cells: [SIZE]termbox.tb_cell = undefined;
|
||||
|
||||
var tv = std.mem.zeroes(std.c.timeval);
|
||||
var tv: std.c.timeval = undefined;
|
||||
_ = std.c.gettimeofday(&tv, null);
|
||||
|
||||
const clock_chars = toBigNumber(if (animate and char == ':' and @divTrunc(tv.tv_usec, 500000) != 0) ' ' else char);
|
||||
|
|
|
@ -6,92 +6,47 @@ const Input = enums.Input;
|
|||
|
||||
const Config = @This();
|
||||
|
||||
ly: struct {
|
||||
animation: Animation,
|
||||
asterisk: u8,
|
||||
bg: u8,
|
||||
bigclock: bool,
|
||||
blank_box: bool,
|
||||
blank_password: bool,
|
||||
clock: []const u8,
|
||||
console_dev: []const u8,
|
||||
default_input: Input,
|
||||
fg: u8,
|
||||
hide_borders: bool,
|
||||
hide_key_hints: bool,
|
||||
input_len: u8,
|
||||
lang: []const u8,
|
||||
load: bool,
|
||||
margin_box_h: u8,
|
||||
margin_box_v: u8,
|
||||
max_desktop_len: u8,
|
||||
max_login_len: u8,
|
||||
max_password_len: u8,
|
||||
mcookie_cmd: []const u8,
|
||||
min_refresh_delta: u16,
|
||||
path: []const u8,
|
||||
restart_cmd: []const u8,
|
||||
restart_key: []const u8,
|
||||
save: bool,
|
||||
save_file: []const u8,
|
||||
service_name: []const u8,
|
||||
shutdown_cmd: []const u8,
|
||||
shutdown_key: []const u8,
|
||||
term_reset_cmd: []const u8,
|
||||
term_restore_cursor_cmd: []const u8,
|
||||
tty: u8,
|
||||
wayland_cmd: []const u8,
|
||||
wayland_specifier: bool,
|
||||
waylandsessions: []const u8,
|
||||
x_cmd: []const u8,
|
||||
xinitrc: []const u8,
|
||||
x_cmd_setup: []const u8,
|
||||
xauth_cmd: []const u8,
|
||||
xsessions: []const u8,
|
||||
},
|
||||
|
||||
pub fn init() Config {
|
||||
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",
|
||||
} };
|
||||
}
|
||||
animation: Animation = .none,
|
||||
asterisk: u8 = '*',
|
||||
bg: u8 = 0,
|
||||
bigclock: bool = false,
|
||||
blank_box: bool = true,
|
||||
border_fg: u8 = 8,
|
||||
clear_password: bool = false,
|
||||
clock: ?[:0]const u8 = null,
|
||||
console_dev: [:0]const u8 = "/dev/console",
|
||||
default_input: Input = .login,
|
||||
fg: u8 = 8,
|
||||
hide_borders: bool = false,
|
||||
hide_key_hints: bool = false,
|
||||
input_len: u8 = 34,
|
||||
lang: []const u8 = "en",
|
||||
load: bool = true,
|
||||
margin_box_h: u8 = 2,
|
||||
margin_box_v: u8 = 1,
|
||||
max_desktop_len: u8 = 100,
|
||||
max_login_len: u8 = 255,
|
||||
max_password_len: u8 = 255,
|
||||
mcookie_cmd: []const u8 = "/usr/bin/mcookie",
|
||||
min_refresh_delta: u16 = 5,
|
||||
path: ?[]const u8 = "/sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin",
|
||||
restart_cmd: []const u8 = "/sbin/shutdown -r now",
|
||||
restart_key: []const u8 = "F2",
|
||||
save: bool = true,
|
||||
save_file: []const u8 = "/etc/ly/save",
|
||||
service_name: []const u8 = "ly",
|
||||
shutdown_cmd: []const u8 = "/sbin/shutdown -a now",
|
||||
shutdown_key: []const u8 = "F1",
|
||||
sleep_cmd: ?[]const u8 = null,
|
||||
sleep_key: []const u8 = "F3",
|
||||
term_reset_cmd: []const u8 = "/usr/bin/tput reset",
|
||||
term_restore_cursor_cmd: []const u8 = "/usr/bin/tput cnorm",
|
||||
tty: u8 = 2,
|
||||
vi_mode: bool = false,
|
||||
wayland_cmd: []const u8 = build_options.data_directory ++ "/wsetup.sh",
|
||||
waylandsessions: []const u8 = "/usr/share/wayland-sessions",
|
||||
x_cmd: []const u8 = "/usr/bin/X",
|
||||
xinitrc: ?[]const u8 = "~/.xinitrc",
|
||||
x_cmd_setup: []const u8 = build_options.data_directory ++ "/xsetup.sh",
|
||||
xauth_cmd: []const u8 = "/usr/bin/xauth",
|
||||
xsessions: []const u8 = "/usr/share/xsessions",
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
const std = @import("std");
|
||||
const ini = @import("ini");
|
||||
const Config = @import("Config.zig");
|
||||
const Lang = @import("Lang.zig");
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
pub const CONFIG_MAX_SIZE: u64 = 8 * 1024;
|
||||
|
||||
const ConfigReader = @This();
|
||||
|
||||
allocator: Allocator,
|
||||
config_allocated: bool = false,
|
||||
lang_allocated: bool = false,
|
||||
config: []u8 = undefined,
|
||||
lang: []u8 = undefined,
|
||||
|
||||
pub fn init(config_allocator: Allocator) ConfigReader {
|
||||
return .{
|
||||
.allocator = config_allocator,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: ConfigReader) void {
|
||||
if (self.config_allocated) self.allocator.free(self.config);
|
||||
if (self.lang_allocated) self.allocator.free(self.lang);
|
||||
}
|
||||
|
||||
pub fn readConfig(self: *ConfigReader, path: []const u8) !Config {
|
||||
var file = std.fs.cwd().openFile(path, .{}) catch return Config.init();
|
||||
defer file.close();
|
||||
|
||||
self.config = try file.readToEndAlloc(self.allocator, CONFIG_MAX_SIZE);
|
||||
self.config_allocated = true;
|
||||
|
||||
return try ini.readToStruct(Config, self.config);
|
||||
}
|
||||
|
||||
pub fn readLang(self: *ConfigReader, path: []const u8) !Lang {
|
||||
var file = std.fs.cwd().openFile(path, .{}) catch return Lang.init();
|
||||
defer file.close();
|
||||
|
||||
self.lang = try file.readToEndAlloc(self.allocator, CONFIG_MAX_SIZE);
|
||||
self.lang_allocated = true;
|
||||
|
||||
return try ini.readToStruct(Lang, self.lang);
|
||||
}
|
|
@ -1,99 +1,52 @@
|
|||
const Lang = @This();
|
||||
|
||||
ly: struct {
|
||||
capslock: []const u8,
|
||||
err_alloc: []const u8,
|
||||
err_bounds: []const u8,
|
||||
err_chdir: []const u8,
|
||||
err_console_dev: []const u8,
|
||||
err_dgn_oob: []const u8,
|
||||
err_domain: []const u8,
|
||||
err_hostname: []const u8,
|
||||
err_mlock: []const u8,
|
||||
err_null: []const u8,
|
||||
err_pam: []const u8,
|
||||
err_pam_abort: []const u8,
|
||||
err_pam_acct_expired: []const u8,
|
||||
err_pam_auth: []const u8,
|
||||
err_pam_authinfo_unavail: []const u8,
|
||||
err_pam_authok_reqd: []const u8,
|
||||
err_pam_buf: []const u8,
|
||||
err_pam_cred_err: []const u8,
|
||||
err_pam_cred_expired: []const u8,
|
||||
err_pam_cred_insufficient: []const u8,
|
||||
err_pam_cred_unavail: []const u8,
|
||||
err_pam_maxtries: []const u8,
|
||||
err_pam_perm_denied: []const u8,
|
||||
err_pam_session: []const u8,
|
||||
err_pam_sys: []const u8,
|
||||
err_pam_user_unknown: []const u8,
|
||||
err_path: []const u8,
|
||||
err_perm_dir: []const u8,
|
||||
err_perm_group: []const u8,
|
||||
err_perm_user: []const u8,
|
||||
err_pwnam: []const u8,
|
||||
err_user_gid: []const u8,
|
||||
err_user_init: []const u8,
|
||||
err_user_uid: []const u8,
|
||||
err_xsessions_dir: []const u8,
|
||||
err_xsessions_open: []const u8,
|
||||
login: []const u8,
|
||||
logout: []const u8,
|
||||
numlock: []const u8,
|
||||
password: []const u8,
|
||||
restart: []const u8,
|
||||
shell: []const u8,
|
||||
shutdown: []const u8,
|
||||
wayland: []const u8,
|
||||
xinitrc: []const u8,
|
||||
},
|
||||
|
||||
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",
|
||||
} };
|
||||
}
|
||||
capslock: []const u8 = "capslock",
|
||||
err_alloc: []const u8 = "failed memory allocation",
|
||||
err_bounds: []const u8 = "out-of-bounds index",
|
||||
err_chdir: []const u8 = "failed to open home folder",
|
||||
err_console_dev: []const u8 = "failed to access console",
|
||||
err_dgn_oob: []const u8 = "log message",
|
||||
err_domain: []const u8 = "invalid domain",
|
||||
err_hostname: []const u8 = "failed to get hostname",
|
||||
err_mlock: []const u8 = "failed to lock password memory",
|
||||
err_null: []const u8 = "null pointer",
|
||||
err_pam: []const u8 = "pam transaction failed",
|
||||
err_pam_abort: []const u8 = "pam transaction aborted",
|
||||
err_pam_acct_expired: []const u8 = "account expired",
|
||||
err_pam_auth: []const u8 = "authentication error",
|
||||
err_pam_authinfo_unavail: []const u8 = "failed to get user info",
|
||||
err_pam_authok_reqd: []const u8 = "token expired",
|
||||
err_pam_buf: []const u8 = "memory buffer error",
|
||||
err_pam_cred_err: []const u8 = "failed to set credentials",
|
||||
err_pam_cred_expired: []const u8 = "credentials expired",
|
||||
err_pam_cred_insufficient: []const u8 = "insufficient credentials",
|
||||
err_pam_cred_unavail: []const u8 = "failed to get credentials",
|
||||
err_pam_maxtries: []const u8 = "reached maximum tries limit",
|
||||
err_pam_perm_denied: []const u8 = "permission denied",
|
||||
err_pam_session: []const u8 = "session error",
|
||||
err_pam_sys: []const u8 = "system error",
|
||||
err_pam_user_unknown: []const u8 = "unknown user",
|
||||
err_path: []const u8 = "failed to set path",
|
||||
err_perm_dir: []const u8 = "failed to change current directory",
|
||||
err_perm_group: []const u8 = "failed to downgrade group permissions",
|
||||
err_perm_user: []const u8 = "failed to downgrade user permissions",
|
||||
err_pwnam: []const u8 = "failed to get user info",
|
||||
err_user_gid: []const u8 = "failed to set user GID",
|
||||
err_user_init: []const u8 = "failed to initialize user",
|
||||
err_user_uid: []const u8 = "failed to set user UID",
|
||||
err_xsessions_dir: []const u8 = "failed to find sessions folder",
|
||||
err_xsessions_open: []const u8 = "failed to open sessions folder",
|
||||
insert: []const u8 = "insert",
|
||||
login: []const u8 = "login:",
|
||||
logout: []const u8 = "logged out",
|
||||
normal: []const u8 = "normal",
|
||||
numlock: []const u8 = "numlock",
|
||||
other: []const u8 = "other",
|
||||
password: []const u8 = "password:",
|
||||
restart: []const u8 = "reboot",
|
||||
shell: []const u8 = "shell",
|
||||
shutdown: []const u8 = "shutdown",
|
||||
sleep: []const u8 = "sleep",
|
||||
wayland: []const u8 = "wayland",
|
||||
xinitrc: []const u8 = "xinitrc",
|
||||
x11: []const u8 = "x11",
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
const Save = @This();
|
||||
|
||||
user: ?[]const u8 = null,
|
||||
session_index: ?u64 = null,
|
|
@ -10,6 +10,14 @@ pub const pam = @cImport({
|
|||
@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_uid = u32;
|
||||
pub const c_gid = u32;
|
||||
|
@ -36,6 +44,9 @@ pub const passwd = extern struct {
|
|||
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 _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 getenv(name: [*:0]const u8) [*:0]u8;
|
||||
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 getpwnam(name: [*:0]const u8) ?*passwd;
|
||||
pub extern "c" fn endpwent() void;
|
||||
|
@ -81,6 +93,7 @@ pub fn getHostName(allocator: Allocator) !struct {
|
|||
const hostname_max_length: u64 = if (hostname_sysconf < 0) @intCast(_POSIX_HOST_NAME_MAX) else @intCast(hostname_sysconf);
|
||||
|
||||
const buffer = try allocator.alloc(u8, hostname_max_length);
|
||||
errdefer allocator.free(buffer);
|
||||
|
||||
const error_code = std.c.gethostname(buffer.ptr, hostname_max_length);
|
||||
if (error_code < 0) return error.CannotGetHostName;
|
||||
|
@ -99,27 +112,22 @@ pub fn getHostName(allocator: Allocator) !struct {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn timeAsString(allocator: Allocator, format: []const u8, max_length: u64) ![:0]u8 {
|
||||
pub fn timeAsString(allocator: Allocator, format: [:0]const u8, max_length: u64) ![:0]u8 {
|
||||
const timer = time(null);
|
||||
const tm_info = localtime(&timer);
|
||||
const buffer = try allocator.allocSentinel(u8, max_length, 0);
|
||||
errdefer allocator.free(buffer);
|
||||
|
||||
const format_z = try allocator.dupeZ(u8, format);
|
||||
defer allocator.free(format_z);
|
||||
|
||||
if (strftime(buffer, max_length, format_z, tm_info) < 0) return error.CannotGetFormattedTime;
|
||||
if (strftime(buffer, max_length, format, tm_info) < 0) return error.CannotGetFormattedTime;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
pub fn getLockState(allocator: Allocator, console_dev: []const u8) !struct {
|
||||
pub fn getLockState(console_dev: [:0]const u8) !struct {
|
||||
numlock: bool,
|
||||
capslock: bool,
|
||||
} {
|
||||
const console_dev_z = try allocator.dupeZ(u8, console_dev);
|
||||
defer allocator.free(console_dev_z);
|
||||
|
||||
const fd = std.c.open(console_dev_z, O_RDONLY);
|
||||
const fd = std.c.open(console_dev, O_RDONLY);
|
||||
if (fd < 0) return error.CannotOpenConsoleDev;
|
||||
|
||||
var numlock = false;
|
||||
|
|
428
src/main.zig
428
src/main.zig
|
@ -10,13 +10,22 @@ const Matrix = @import("animations/Matrix.zig");
|
|||
const TerminalBuffer = @import("tui/TerminalBuffer.zig");
|
||||
const Desktop = @import("tui/components/Desktop.zig");
|
||||
const Text = @import("tui/components/Text.zig");
|
||||
const InfoLine = @import("tui/components/InfoLine.zig");
|
||||
const Config = @import("config/Config.zig");
|
||||
const ConfigReader = @import("config/ConfigReader.zig");
|
||||
const ini = @import("zigini");
|
||||
const Lang = @import("config/Lang.zig");
|
||||
const Save = @import("config/Save.zig");
|
||||
const ViMode = @import("enums.zig").ViMode;
|
||||
const SharedError = @import("SharedError.zig");
|
||||
const utils = @import("tui/utils.zig");
|
||||
|
||||
const Ini = ini.Ini;
|
||||
const termbox = interop.termbox;
|
||||
|
||||
const LY_VERSION = "1.0.0";
|
||||
pub fn signalHandler(i: c_int) callconv(.C) void {
|
||||
termbox.tb_shutdown();
|
||||
std.c.exit(i);
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
|
@ -41,7 +50,7 @@ pub fn main() !void {
|
|||
|
||||
var config: Config = undefined;
|
||||
var lang: Lang = undefined;
|
||||
var info_line: []const u8 = undefined;
|
||||
var info_line = InfoLine{};
|
||||
|
||||
if (res.args.help != 0) {
|
||||
try clap.help(stderr, clap.Help, ¶ms, .{});
|
||||
|
@ -50,13 +59,15 @@ pub fn main() !void {
|
|||
std.os.exit(0);
|
||||
}
|
||||
if (res.args.version != 0) {
|
||||
_ = try stderr.write("Ly version " ++ LY_VERSION ++ "\n");
|
||||
_ = try stderr.write("Ly version " ++ build_options.version ++ "\n");
|
||||
std.os.exit(0);
|
||||
}
|
||||
|
||||
// Load configuration file
|
||||
var config_reader = ConfigReader.init(allocator);
|
||||
defer config_reader.deinit();
|
||||
var config_ini = Ini(Config).init(allocator);
|
||||
defer config_ini.deinit();
|
||||
var lang_ini = Ini(Lang).init(allocator);
|
||||
defer lang_ini.deinit();
|
||||
|
||||
if (res.args.config) |s| {
|
||||
const trailing_slash = if (s[s.len - 1] != '/') "/" else "";
|
||||
|
@ -64,19 +75,19 @@ pub fn main() !void {
|
|||
const config_path = try std.fmt.allocPrint(allocator, "{s}{s}config.ini", .{ s, trailing_slash });
|
||||
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);
|
||||
|
||||
lang = try config_reader.readLang(lang_path);
|
||||
lang = lang_ini.readToStruct(lang_path) catch Lang{};
|
||||
} 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);
|
||||
|
||||
lang = try config_reader.readLang(lang_path);
|
||||
lang = lang_ini.readToStruct(lang_path) catch Lang{};
|
||||
}
|
||||
|
||||
// Initialize information line with host name
|
||||
|
@ -86,78 +97,89 @@ pub fn main() !void {
|
|||
get_host_name: {
|
||||
const host_name_struct = interop.getHostName(allocator) catch |err| {
|
||||
if (err == error.CannotGetHostName) {
|
||||
info_line = lang.ly.err_hostname;
|
||||
try info_line.setText(lang.err_hostname);
|
||||
} else {
|
||||
info_line = lang.ly.err_alloc;
|
||||
try info_line.setText(lang.err_alloc);
|
||||
}
|
||||
break :get_host_name;
|
||||
};
|
||||
|
||||
got_host_name = true;
|
||||
host_name_buffer = host_name_struct.buffer;
|
||||
info_line = host_name_struct.slice;
|
||||
try info_line.setText(host_name_struct.slice);
|
||||
}
|
||||
|
||||
defer {
|
||||
if (got_host_name) allocator.free(host_name_buffer);
|
||||
}
|
||||
|
||||
// Initialize termbox
|
||||
_ = termbox.tb_init();
|
||||
defer termbox.tb_shutdown();
|
||||
|
||||
const act = std.os.Sigaction{
|
||||
.handler = .{ .handler = &signalHandler },
|
||||
.mask = std.os.empty_sigset,
|
||||
.flags = 0,
|
||||
};
|
||||
try std.os.sigaction(std.os.SIG.TERM, &act, null);
|
||||
|
||||
_ = termbox.tb_select_output_mode(termbox.TB_OUTPUT_NORMAL);
|
||||
termbox.tb_clear();
|
||||
|
||||
// Initialize terminal buffer
|
||||
const labels_max_length = @max(lang.ly.login.len, lang.ly.password.len);
|
||||
// we need this to reset it after auth.
|
||||
const tb_termios = try std.os.tcgetattr(std.os.STDIN_FILENO);
|
||||
|
||||
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);
|
||||
// Initialize terminal buffer
|
||||
const labels_max_length = @max(lang.login.len, lang.password.len);
|
||||
|
||||
var buffer = TerminalBuffer.init(config, labels_max_length);
|
||||
|
||||
// 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, lang);
|
||||
defer desktop.deinit();
|
||||
|
||||
desktop.addEnvironment(lang.ly.shell, "", .shell) catch {
|
||||
info_line = lang.ly.err_alloc;
|
||||
desktop.addEnvironment(lang.shell, "", .shell) catch {
|
||||
try info_line.setText(lang.err_alloc);
|
||||
};
|
||||
desktop.addEnvironment(lang.ly.xinitrc, config.ly.xinitrc, .xinitrc) catch {
|
||||
info_line = lang.ly.err_alloc;
|
||||
};
|
||||
|
||||
try desktop.crawl(config.ly.waylandsessions, .wayland);
|
||||
try desktop.crawl(config.ly.xsessions, .x11);
|
||||
|
||||
var login = try Text.init(allocator, &buffer, config.ly.max_login_len);
|
||||
defer login.deinit();
|
||||
|
||||
var password = try Text.init(allocator, &buffer, config.ly.max_password_len);
|
||||
defer password.deinit();
|
||||
|
||||
// Load last saved username and desktop selection, if any
|
||||
if (config.ly.load) load_last_saved: {
|
||||
var file = std.fs.openFileAbsolute(config.ly.save_file, .{}) catch break :load_last_saved;
|
||||
defer file.close();
|
||||
|
||||
const reader = file.reader();
|
||||
const username_length = try reader.readIntLittle(u64);
|
||||
|
||||
const username_buffer = try allocator.alloc(u8, username_length);
|
||||
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 (config.xinitrc) |xinitrc| {
|
||||
desktop.addEnvironment(lang.xinitrc, xinitrc, .xinitrc) catch {
|
||||
try info_line.setText(lang.err_alloc);
|
||||
};
|
||||
}
|
||||
|
||||
var active_input = if (config.ly.default_input == .login and login.text.items.len != login.end) .password else config.ly.default_input;
|
||||
try desktop.crawl(config.waylandsessions, .wayland);
|
||||
try desktop.crawl(config.xsessions, .x11);
|
||||
|
||||
var login = try Text.init(allocator, &buffer, config.max_login_len);
|
||||
defer login.deinit();
|
||||
|
||||
var password = try Text.init(allocator, &buffer, config.max_password_len);
|
||||
defer password.deinit();
|
||||
|
||||
var active_input = config.default_input;
|
||||
var insert_mode = !config.vi_mode;
|
||||
|
||||
// Load last saved username and desktop selection, if any
|
||||
if (config.load) {
|
||||
var save_ini = Ini(Save).init(allocator);
|
||||
defer save_ini.deinit();
|
||||
const save = save_ini.readToStruct(config.save_file) catch Save{};
|
||||
|
||||
if (save.user) |user| {
|
||||
try login.text.appendSlice(user);
|
||||
login.end = user.len;
|
||||
active_input = .password;
|
||||
}
|
||||
|
||||
if (save.session_index) |session_index| {
|
||||
if (session_index < desktop.environments.items.len) desktop.current = session_index;
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
desktop.position(coordinates.x, coordinates.y + 2, coordinates.visible_length);
|
||||
|
@ -165,12 +187,12 @@ pub fn main() !void {
|
|||
password.position(coordinates.x, coordinates.y + 6, coordinates.visible_length);
|
||||
|
||||
switch (active_input) {
|
||||
.session => desktop.handle(null),
|
||||
.login => login.handle(null) catch {
|
||||
info_line = lang.ly.err_alloc;
|
||||
.session => desktop.handle(null, insert_mode),
|
||||
.login => login.handle(null, insert_mode) catch {
|
||||
try info_line.setText(lang.err_alloc);
|
||||
},
|
||||
.password => password.handle(null) catch {
|
||||
info_line = lang.ly.err_alloc;
|
||||
.password => password.handle(null, insert_mode) catch {
|
||||
try info_line.setText(lang.err_alloc);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -179,25 +201,27 @@ pub fn main() !void {
|
|||
var doom: Doom = undefined;
|
||||
var matrix: Matrix = undefined;
|
||||
|
||||
switch (config.ly.animation) {
|
||||
switch (config.animation) {
|
||||
.none => {},
|
||||
.doom => doom = try Doom.init(allocator, &buffer),
|
||||
.matrix => matrix = try Matrix.init(allocator, &buffer),
|
||||
}
|
||||
defer {
|
||||
switch (config.ly.animation) {
|
||||
switch (config.animation) {
|
||||
.none => {},
|
||||
.doom => doom.deinit(),
|
||||
.matrix => matrix.deinit(),
|
||||
}
|
||||
}
|
||||
|
||||
const animate = config.ly.animation != .none;
|
||||
const has_clock = config.ly.clock.len > 0;
|
||||
const shutdown_key = try std.fmt.parseInt(u8, config.ly.shutdown_key[1..], 10);
|
||||
const restart_key = try std.fmt.parseInt(u8, config.ly.restart_key[1..], 10);
|
||||
const animate = config.animation != .none;
|
||||
const shutdown_key = try std.fmt.parseInt(u8, config.shutdown_key[1..], 10);
|
||||
const shutdown_len = try utils.strWidth(lang.shutdown);
|
||||
const restart_key = try std.fmt.parseInt(u8, config.restart_key[1..], 10);
|
||||
const restart_len = try utils.strWidth(lang.restart);
|
||||
const sleep_key = try std.fmt.parseInt(u8, config.sleep_key[1..], 10);
|
||||
|
||||
var event = std.mem.zeroes(termbox.tb_event);
|
||||
var event: termbox.tb_event = undefined;
|
||||
var run = true;
|
||||
var update = true;
|
||||
var resolution_changed = false;
|
||||
|
@ -207,8 +231,8 @@ pub fn main() !void {
|
|||
|
||||
// Switch to selected TTY if possible
|
||||
open_console_dev: {
|
||||
const console_dev_z = allocator.dupeZ(u8, config.ly.console_dev) catch {
|
||||
info_line = lang.ly.err_alloc;
|
||||
const console_dev_z = allocator.dupeZ(u8, config.console_dev) catch {
|
||||
try info_line.setText(lang.err_alloc);
|
||||
break :open_console_dev;
|
||||
};
|
||||
defer allocator.free(console_dev_z);
|
||||
|
@ -217,17 +241,17 @@ pub fn main() !void {
|
|||
defer _ = std.c.close(fd);
|
||||
|
||||
if (fd < 0) {
|
||||
info_line = lang.ly.err_console_dev;
|
||||
try info_line.setText(lang.err_console_dev);
|
||||
break :open_console_dev;
|
||||
}
|
||||
|
||||
_ = std.c.ioctl(fd, interop.VT_ACTIVATE, config.ly.tty);
|
||||
_ = std.c.ioctl(fd, interop.VT_WAITACTIVE, config.ly.tty);
|
||||
_ = std.c.ioctl(fd, interop.VT_ACTIVATE, config.tty);
|
||||
_ = std.c.ioctl(fd, interop.VT_WAITACTIVE, config.tty);
|
||||
}
|
||||
|
||||
while (run) {
|
||||
// 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);
|
||||
|
||||
termbox.tb_present(); // Required to update tb_width(), tb_height() and tb_cell_buffer()
|
||||
|
@ -249,13 +273,13 @@ pub fn main() !void {
|
|||
if (resolution_changed) {
|
||||
buffer.buffer = termbox.tb_cell_buffer();
|
||||
|
||||
switch (config.ly.animation) {
|
||||
switch (config.animation) {
|
||||
.none => {},
|
||||
.doom => doom.realloc() catch {
|
||||
info_line = lang.ly.err_alloc;
|
||||
try info_line.setText(lang.err_alloc);
|
||||
},
|
||||
.matrix => matrix.realloc() catch {
|
||||
info_line = lang.ly.err_alloc;
|
||||
try info_line.setText(lang.err_alloc);
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -267,30 +291,30 @@ pub fn main() !void {
|
|||
// If the user entered a wrong password 10 times in a row, play a cascade animation, else update normally
|
||||
if (auth_fails < 10) {
|
||||
switch (active_input) {
|
||||
.session => desktop.handle(null),
|
||||
.login => login.handle(null) catch {
|
||||
info_line = lang.ly.err_alloc;
|
||||
.session => desktop.handle(null, insert_mode),
|
||||
.login => login.handle(null, insert_mode) catch {
|
||||
try info_line.setText(lang.err_alloc);
|
||||
},
|
||||
.password => password.handle(null) catch {
|
||||
info_line = lang.ly.err_alloc;
|
||||
.password => password.handle(null, insert_mode) catch {
|
||||
try info_line.setText(lang.err_alloc);
|
||||
},
|
||||
}
|
||||
|
||||
termbox.tb_clear();
|
||||
|
||||
switch (config.ly.animation) {
|
||||
switch (config.animation) {
|
||||
.none => {},
|
||||
.doom => doom.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 xo = buffer.width / 2 - (format.len * (bigclock.WIDTH + 1)) / 2;
|
||||
const yo = (buffer.height - buffer.box_height) / 2 - bigclock.HEIGHT - 2;
|
||||
|
||||
const clock_str = interop.timeAsString(allocator, format, format.len + 1) catch {
|
||||
info_line = lang.ly.err_alloc;
|
||||
try info_line.setText(lang.err_alloc);
|
||||
break :draw_big_clock;
|
||||
};
|
||||
defer allocator.free(clock_str);
|
||||
|
@ -301,11 +325,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: {
|
||||
const clock_buffer = interop.timeAsString(allocator, config.ly.clock, 32) catch {
|
||||
info_line = lang.ly.err_alloc;
|
||||
if (config.clock) |clock| draw_clock: {
|
||||
const clock_buffer = interop.timeAsString(allocator, clock, 32) catch {
|
||||
try info_line.setText(lang.err_alloc);
|
||||
break :draw_clock;
|
||||
};
|
||||
defer allocator.free(clock_buffer);
|
||||
|
@ -326,46 +350,61 @@ pub fn main() !void {
|
|||
const label_x = buffer.box_x + buffer.margin_box_h;
|
||||
const label_y = buffer.box_y + buffer.margin_box_v;
|
||||
|
||||
buffer.drawLabel(lang.ly.login, label_x, label_y + 4);
|
||||
buffer.drawLabel(lang.ly.password, label_x, label_y + 6);
|
||||
buffer.drawLabel(lang.login, label_x, label_y + 4);
|
||||
buffer.drawLabel(lang.password, label_x, label_y + 6);
|
||||
|
||||
if (info_line.len > 0) {
|
||||
const x = buffer.box_x + ((buffer.box_width - info_line.len) / 2);
|
||||
buffer.drawLabel(info_line, x, label_y);
|
||||
if (info_line.width > 0 and buffer.box_width > info_line.width) {
|
||||
const x = buffer.box_x + ((buffer.box_width - info_line.width) / 2);
|
||||
buffer.drawLabel(info_line.text, x, label_y);
|
||||
}
|
||||
|
||||
if (!config.ly.hide_key_hints) {
|
||||
if (!config.hide_key_hints) {
|
||||
var length: u64 = 0;
|
||||
|
||||
buffer.drawLabel(config.ly.shutdown_key, length, 0);
|
||||
length += config.ly.shutdown_key.len + 1;
|
||||
buffer.drawLabel(config.shutdown_key, length, 0);
|
||||
length += config.shutdown_key.len + 1;
|
||||
buffer.drawLabel(" ", length - 1, 0);
|
||||
|
||||
buffer.drawLabel(lang.ly.shutdown, length, 0);
|
||||
length += lang.ly.shutdown.len + 1;
|
||||
buffer.drawLabel(lang.shutdown, length, 0);
|
||||
length += shutdown_len + 1;
|
||||
|
||||
buffer.drawLabel(config.ly.restart_key, length, 0);
|
||||
length += config.ly.restart_key.len + 1;
|
||||
buffer.drawLabel(config.restart_key, length, 0);
|
||||
length += config.restart_key.len + 1;
|
||||
buffer.drawLabel(" ", length - 1, 0);
|
||||
|
||||
buffer.drawLabel(lang.ly.restart, length, 0);
|
||||
length += lang.ly.restart.len + 1;
|
||||
buffer.drawLabel(lang.restart, length, 0);
|
||||
length += 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);
|
||||
}
|
||||
}
|
||||
|
||||
if (config.vi_mode) {
|
||||
const label_txt = if (insert_mode) lang.insert else lang.normal;
|
||||
buffer.drawLabel(label_txt, buffer.box_x, buffer.box_y - 1);
|
||||
}
|
||||
|
||||
draw_lock_state: {
|
||||
const lock_state = interop.getLockState(allocator, config.ly.console_dev) catch |err| {
|
||||
const lock_state = interop.getLockState(config.console_dev) catch |err| {
|
||||
if (err == error.CannotOpenConsoleDev) {
|
||||
info_line = lang.ly.err_console_dev;
|
||||
try info_line.setText(lang.err_console_dev);
|
||||
} else {
|
||||
info_line = lang.ly.err_alloc;
|
||||
try info_line.setText(lang.err_alloc);
|
||||
}
|
||||
break :draw_lock_state;
|
||||
};
|
||||
|
||||
var lock_state_x = buffer.width - lang.ly.numlock.len;
|
||||
const lock_state_y: u64 = if (has_clock) 1 else 0;
|
||||
var lock_state_x = buffer.width - lang.numlock.len;
|
||||
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);
|
||||
lock_state_x -= lang.ly.capslock.len + 1;
|
||||
if (lock_state.capslock) buffer.drawLabel(lang.ly.capslock, lock_state_x, lock_state_y);
|
||||
if (lock_state.numlock) buffer.drawLabel(lang.numlock, lock_state_x, lock_state_y);
|
||||
lock_state_x -= lang.capslock.len + 1;
|
||||
if (lock_state.capslock) buffer.drawLabel(lang.capslock, lock_state_x, lock_state_y);
|
||||
}
|
||||
|
||||
if (resolution_changed) {
|
||||
|
@ -379,7 +418,7 @@ pub fn main() !void {
|
|||
|
||||
desktop.draw();
|
||||
login.draw();
|
||||
password.drawMasked(config.ly.asterisk);
|
||||
password.drawMasked(config.asterisk);
|
||||
|
||||
update = animate;
|
||||
} else {
|
||||
|
@ -399,14 +438,14 @@ 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
|
||||
if (animate) {
|
||||
timeout = config.ly.min_refresh_delta;
|
||||
} else if (config.ly.bigclock and config.ly.clock.len == 0) {
|
||||
var tv = std.mem.zeroes(std.c.timeval);
|
||||
timeout = config.min_refresh_delta;
|
||||
} else if (config.bigclock and config.clock == null) {
|
||||
var tv: std.c.timeval = undefined;
|
||||
_ = std.c.gettimeofday(&tv, null);
|
||||
|
||||
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) {
|
||||
var tv = std.mem.zeroes(std.c.timeval);
|
||||
} else if (config.clock != null or auth_fails >= 10) {
|
||||
var tv: std.c.timeval = undefined;
|
||||
_ = std.c.gettimeofday(&tv, null);
|
||||
|
||||
timeout = @intCast(1000 - @divTrunc(tv.tv_usec, 1000) + 1);
|
||||
|
@ -417,13 +456,28 @@ pub fn main() !void {
|
|||
if (event_error < 0 or event.type != termbox.TB_EVENT_KEY) continue;
|
||||
|
||||
switch (event.key) {
|
||||
termbox.TB_KEY_F1, termbox.TB_KEY_F2, termbox.TB_KEY_F3, termbox.TB_KEY_F4, termbox.TB_KEY_F5, termbox.TB_KEY_F6, termbox.TB_KEY_F7, termbox.TB_KEY_F8, termbox.TB_KEY_F9, termbox.TB_KEY_F10, termbox.TB_KEY_F11, termbox.TB_KEY_F12 => {
|
||||
if (0xFFFF - event.key + 1 == shutdown_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 (0xFFFF - event.key + 1 == restart_key) {
|
||||
} else if (pressed_key == restart_key) {
|
||||
restart = true;
|
||||
run = false;
|
||||
} else if (pressed_key == sleep_key) {
|
||||
if (config.sleep_cmd) |sleep_cmd| {
|
||||
const pid = try std.os.fork();
|
||||
if (pid == 0) {
|
||||
std.process.execv(allocator, &[_][]const u8{ "/bin/sh", "-c", sleep_cmd }) catch std.os.exit(1);
|
||||
std.os.exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
termbox.TB_KEY_CTRL_C => run = false,
|
||||
|
@ -458,51 +512,90 @@ pub fn main() !void {
|
|||
};
|
||||
update = true;
|
||||
},
|
||||
termbox.TB_KEY_ENTER => authenticate: {
|
||||
if (config.ly.save) save_last_settings: {
|
||||
var file = std.fs.createFileAbsolute(config.ly.save_file, .{}) catch break :save_last_settings;
|
||||
termbox.TB_KEY_ENTER => {
|
||||
if (config.save) save_last_settings: {
|
||||
var file = std.fs.createFileAbsolute(config.save_file, .{}) catch break :save_last_settings;
|
||||
defer file.close();
|
||||
|
||||
const writer = file.writer();
|
||||
try writer.writeIntLittle(u64, login.end);
|
||||
_ = try writer.write(login.text.items);
|
||||
try writer.writeIntLittle(u64, desktop.current);
|
||||
const save_data = Save{
|
||||
.user = login.text.items,
|
||||
.session_index = desktop.current,
|
||||
};
|
||||
ini.writeFromStruct(save_data, file.writer(), null) catch break :save_last_settings;
|
||||
}
|
||||
|
||||
var has_error = false;
|
||||
var shared_err = try SharedError.init();
|
||||
defer shared_err.deinit();
|
||||
|
||||
auth.authenticate(
|
||||
allocator,
|
||||
config.ly.tty,
|
||||
desktop,
|
||||
login,
|
||||
&password,
|
||||
config.ly.service_name,
|
||||
config.ly.path,
|
||||
config.ly.term_reset_cmd,
|
||||
config.ly.wayland_cmd,
|
||||
) catch {
|
||||
has_error = true;
|
||||
const session_pid = try std.os.fork();
|
||||
if (session_pid == 0) {
|
||||
auth.authenticate(allocator, config, desktop, login, &password) catch |err| {
|
||||
shared_err.writeError(err);
|
||||
std.os.exit(1);
|
||||
};
|
||||
std.os.exit(0);
|
||||
}
|
||||
|
||||
_ = std.os.waitpid(session_pid, 0);
|
||||
|
||||
var auth_err = shared_err.readError();
|
||||
if (auth_err) |err| {
|
||||
auth_fails += 1;
|
||||
active_input = .password;
|
||||
try info_line.setText(getAuthErrorMsg(err, lang));
|
||||
if (config.clear_password or err != error.PamAuthError) password.clear();
|
||||
} else {
|
||||
password.clear();
|
||||
try info_line.setText(lang.logout);
|
||||
}
|
||||
|
||||
// TODO: Errors in info_line
|
||||
try std.os.tcsetattr(std.os.STDIN_FILENO, .FLUSH, tb_termios);
|
||||
termbox.tb_clear();
|
||||
termbox.tb_present();
|
||||
|
||||
if (config.ly.blank_password) password.clear();
|
||||
};
|
||||
update = true;
|
||||
|
||||
if (!has_error) info_line = lang.ly.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 std.os.exit(1);
|
||||
std.os.exit(0);
|
||||
}
|
||||
},
|
||||
else => {
|
||||
if (!insert_mode) {
|
||||
switch (event.ch) {
|
||||
'k' => {
|
||||
active_input = switch (active_input) {
|
||||
.session, .login => .session,
|
||||
.password => .login,
|
||||
};
|
||||
update = true;
|
||||
continue;
|
||||
},
|
||||
'j' => {
|
||||
active_input = switch (active_input) {
|
||||
.session => .login,
|
||||
.login, .password => .password,
|
||||
};
|
||||
update = true;
|
||||
continue;
|
||||
},
|
||||
'i' => {
|
||||
insert_mode = true;
|
||||
update = true;
|
||||
continue;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
switch (active_input) {
|
||||
.session => desktop.handle(&event),
|
||||
.login => login.handle(&event) catch {
|
||||
info_line = lang.ly.err_alloc;
|
||||
.session => desktop.handle(&event, insert_mode),
|
||||
.login => login.handle(&event, insert_mode) catch {
|
||||
try info_line.setText(lang.err_alloc);
|
||||
},
|
||||
.password => password.handle(&event) catch {
|
||||
info_line = lang.ly.err_alloc;
|
||||
.password => password.handle(&event, insert_mode) catch {
|
||||
try info_line.setText(lang.err_alloc);
|
||||
},
|
||||
}
|
||||
update = true;
|
||||
|
@ -510,11 +603,36 @@ pub fn main() !void {
|
|||
}
|
||||
}
|
||||
|
||||
if (got_host_name) allocator.free(host_name_buffer);
|
||||
|
||||
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) {
|
||||
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",
|
||||
};
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ const std = @import("std");
|
|||
const builtin = @import("builtin");
|
||||
const interop = @import("../interop.zig");
|
||||
const utils = @import("utils.zig");
|
||||
const Config = @import("../config/Config.zig");
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
const Random = std.rand.Random;
|
||||
|
@ -16,6 +17,7 @@ height: u64,
|
|||
buffer: [*]termbox.tb_cell,
|
||||
fg: u8,
|
||||
bg: u8,
|
||||
border_fg: u8,
|
||||
box_chars: struct {
|
||||
left_up: u32,
|
||||
left_down: u32,
|
||||
|
@ -34,7 +36,7 @@ box_height: u64,
|
|||
margin_box_v: 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(config: Config, labels_max_length: u64) TerminalBuffer {
|
||||
var prng = std.rand.Isaac64.init(@intCast(std.time.timestamp()));
|
||||
|
||||
return .{
|
||||
|
@ -42,8 +44,9 @@ pub fn init(margin_box_v: u8, margin_box_h: u8, input_length: u8, labels_max_len
|
|||
.width = @intCast(termbox.tb_width()),
|
||||
.height = @intCast(termbox.tb_height()),
|
||||
.buffer = termbox.tb_cell_buffer(),
|
||||
.fg = fg,
|
||||
.bg = bg,
|
||||
.fg = config.fg,
|
||||
.bg = config.bg,
|
||||
.border_fg = config.border_fg,
|
||||
.box_chars = if (builtin.os.tag == .linux or builtin.os.tag.isBSD()) .{
|
||||
.left_up = 0x250C,
|
||||
.left_down = 0x2514,
|
||||
|
@ -66,10 +69,10 @@ pub fn init(margin_box_v: u8, margin_box_h: u8, input_length: u8, labels_max_len
|
|||
.labels_max_length = labels_max_length,
|
||||
.box_x = 0,
|
||||
.box_y = 0,
|
||||
.box_width = (2 * margin_box_h) + input_length + 1 + labels_max_length,
|
||||
.box_height = 7 + (2 * margin_box_v),
|
||||
.margin_box_v = margin_box_v,
|
||||
.margin_box_h = margin_box_h,
|
||||
.box_width = (2 * config.margin_box_h) + config.input_len + 1 + labels_max_length,
|
||||
.box_height = 7 + (2 * config.margin_box_v),
|
||||
.margin_box_v = config.margin_box_v,
|
||||
.margin_box_h = config.margin_box_h,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -107,13 +110,13 @@ pub fn drawBoxCenter(self: *TerminalBuffer, show_borders: bool, blank_box: bool)
|
|||
self.box_y = y1;
|
||||
|
||||
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(x2), @intCast(y1 - 1), self.box_chars.right_up, self.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(x2), @intCast(y2), self.box_chars.right_down, 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.border_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.border_fg, self.bg);
|
||||
|
||||
var c1 = utils.initCell(self.box_chars.top, self.fg, self.bg);
|
||||
var c2 = utils.initCell(self.box_chars.bottom, 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.border_fg, self.bg);
|
||||
|
||||
for (0..self.box_width) |i| {
|
||||
termbox.tb_put_cell(@intCast(x1 + i), @intCast(y1 - 1), &c1);
|
||||
|
@ -158,7 +161,13 @@ pub fn calculateComponentCoordinates(self: TerminalBuffer) struct {
|
|||
|
||||
pub fn drawLabel(self: TerminalBuffer, text: []const u8, x: u64, y: u64) void {
|
||||
const yc: c_int = @intCast(y);
|
||||
for (0..text.len) |xx| termbox.tb_change_cell(@intCast(x + xx), yc, text[xx], self.fg, self.bg);
|
||||
const utf8view = std.unicode.Utf8View.init(text) catch return;
|
||||
var utf8 = utf8view.iterator();
|
||||
|
||||
var i = x;
|
||||
while (utf8.nextCodepoint()) |codepoint| : (i += 1) {
|
||||
termbox.tb_change_cell(@intCast(i), yc, codepoint, self.fg, self.bg);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn drawCharMultiple(self: TerminalBuffer, char: u8, x: u64, y: u64, length: u64) void {
|
||||
|
|
|
@ -1,37 +1,36 @@
|
|||
const std = @import("std");
|
||||
const ini = @import("ini");
|
||||
const enums = @import("../../enums.zig");
|
||||
const interop = @import("../../interop.zig");
|
||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||
const Ini = @import("zigini").Ini;
|
||||
const Lang = @import("../../config/Lang.zig");
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
const EnvironmentList = std.ArrayList(Environment);
|
||||
|
||||
const DisplayServer = enums.DisplayServer;
|
||||
const ViMode = enums.ViMode;
|
||||
|
||||
const termbox = interop.termbox;
|
||||
|
||||
pub const DESKTOP_ENTRY_MAX_SIZE = 8 * 1024;
|
||||
|
||||
const Desktop = @This();
|
||||
|
||||
pub const Environment = struct {
|
||||
has_entry_buffer: bool,
|
||||
entry_buffer: []u8,
|
||||
name: []const u8,
|
||||
xdg_name: []const u8,
|
||||
cmd: []const u8,
|
||||
specifier: []const u8,
|
||||
display_server: DisplayServer,
|
||||
entry_ini: ?Ini(Entry) = null,
|
||||
name: []const u8 = "",
|
||||
xdg_name: []const u8 = "",
|
||||
cmd: []const u8 = "",
|
||||
specifier: []const u8 = "",
|
||||
display_server: DisplayServer = .wayland,
|
||||
};
|
||||
|
||||
pub const Entry = struct {
|
||||
Desktop_Entry: struct {
|
||||
Exec: []const u8,
|
||||
Name: []const u8,
|
||||
},
|
||||
const DesktopEntry = struct {
|
||||
Exec: []const u8 = "",
|
||||
Name: []const u8 = "",
|
||||
};
|
||||
|
||||
pub const Entry = struct { Desktop_Entry: DesktopEntry = DesktopEntry{} };
|
||||
|
||||
allocator: Allocator,
|
||||
buffer: *TerminalBuffer,
|
||||
environments: EnvironmentList,
|
||||
|
@ -39,8 +38,9 @@ current: u64,
|
|||
visible_length: u64,
|
||||
x: u64,
|
||||
y: u64,
|
||||
lang: Lang,
|
||||
|
||||
pub fn init(allocator: Allocator, buffer: *TerminalBuffer, max_length: u64) !Desktop {
|
||||
pub fn init(allocator: Allocator, buffer: *TerminalBuffer, max_length: u64, lang: Lang) !Desktop {
|
||||
return .{
|
||||
.allocator = allocator,
|
||||
.buffer = buffer,
|
||||
|
@ -49,12 +49,13 @@ pub fn init(allocator: Allocator, buffer: *TerminalBuffer, max_length: u64) !Des
|
|||
.visible_length = 0,
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.lang = lang,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: Desktop) void {
|
||||
for (self.environments.items) |environment| {
|
||||
if (environment.has_entry_buffer) self.allocator.free(environment.entry_buffer);
|
||||
for (self.environments.items) |*environment| {
|
||||
if (environment.entry_ini) |*entry_ini| entry_ini.deinit();
|
||||
}
|
||||
|
||||
self.environments.deinit();
|
||||
|
@ -68,15 +69,14 @@ 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 {
|
||||
try self.environments.append(.{
|
||||
.has_entry_buffer = false,
|
||||
.entry_buffer = undefined,
|
||||
.entry_ini = null,
|
||||
.name = name,
|
||||
.xdg_name = name, // TODO
|
||||
.cmd = cmd,
|
||||
.specifier = switch (display_server) {
|
||||
.wayland => "wayland",
|
||||
.x11 => "x11",
|
||||
else => "other",
|
||||
.wayland => self.lang.wayland,
|
||||
.x11 => self.lang.x11,
|
||||
else => self.lang.other,
|
||||
},
|
||||
.display_server = display_server,
|
||||
});
|
||||
|
@ -84,17 +84,16 @@ pub fn addEnvironment(self: *Desktop, name: []const u8, cmd: []const u8, display
|
|||
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(.{
|
||||
.has_entry_buffer = true,
|
||||
.entry_buffer = entry_buffer,
|
||||
.entry_ini = entry_ini,
|
||||
.name = name,
|
||||
.xdg_name = name, // TODO
|
||||
.cmd = cmd,
|
||||
.specifier = switch (display_server) {
|
||||
.wayland => "wayland",
|
||||
.x11 => "x11",
|
||||
else => "other",
|
||||
.wayland => self.lang.wayland,
|
||||
.x11 => self.lang.x11,
|
||||
else => self.lang.other,
|
||||
},
|
||||
.display_server = display_server,
|
||||
});
|
||||
|
@ -103,32 +102,38 @@ pub fn addEnvironmentWithBuffer(self: *Desktop, entry_buffer: []u8, name: []cons
|
|||
}
|
||||
|
||||
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, .{});
|
||||
defer iterable_directory.close();
|
||||
|
||||
var iterator = iterable_directory.iterate();
|
||||
while (try iterator.next()) |item| {
|
||||
var file = try directory.openFile(item.name, .{});
|
||||
defer file.close();
|
||||
if (!std.mem.eql(u8, std.fs.path.extension(item.name), ".desktop")) continue;
|
||||
|
||||
const buffer = try file.readToEndAlloc(self.allocator, DESKTOP_ENTRY_MAX_SIZE);
|
||||
const entry = try ini.readToStruct(Entry, buffer);
|
||||
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);
|
||||
|
||||
try self.addEnvironmentWithBuffer(buffer, entry.Desktop_Entry.Name, entry.Desktop_Entry.Exec, display_server);
|
||||
try self.addEnvironmentWithIni(entry_ini, entry.Desktop_Entry.Name, entry.Desktop_Entry.Exec, display_server);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle(self: *Desktop, maybe_event: ?*termbox.tb_event) void {
|
||||
pub fn handle(self: *Desktop, maybe_event: ?*termbox.tb_event, insert_mode: bool) void {
|
||||
if (maybe_event) |event| blk: {
|
||||
if (event.type != termbox.TB_EVENT_KEY) break :blk;
|
||||
|
||||
switch (event.key) {
|
||||
termbox.TB_KEY_ARROW_LEFT, termbox.TB_KEY_CTRL_H => self.goLeft(),
|
||||
termbox.TB_KEY_ARROW_RIGHT, termbox.TB_KEY_CTRL_L => self.goRight(),
|
||||
else => {},
|
||||
else => {
|
||||
if (!insert_mode) {
|
||||
switch (event.ch) {
|
||||
'h' => self.goLeft(),
|
||||
'l' => self.goRight(),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
const utils = @import("../utils.zig");
|
||||
|
||||
const InfoLine = @This();
|
||||
|
||||
text: []const u8 = "",
|
||||
width: u8 = 0,
|
||||
|
||||
pub fn setText(self: *InfoLine, text: []const u8) !void {
|
||||
self.width = if (text.len > 0) try utils.strWidth(text) else 0;
|
||||
self.text = text;
|
||||
}
|
|
@ -2,6 +2,7 @@ const std = @import("std");
|
|||
const interop = @import("../../interop.zig");
|
||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||
const utils = @import("../utils.zig");
|
||||
const ViMode = @import("../../enums.zig").ViMode;
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
const DynamicString = std.ArrayList(u8);
|
||||
|
@ -46,7 +47,7 @@ pub fn position(self: *Text, x: u64, y: u64, visible_length: u64) void {
|
|||
self.visible_length = visible_length;
|
||||
}
|
||||
|
||||
pub fn handle(self: *Text, maybe_event: ?*termbox.tb_event) !void {
|
||||
pub fn handle(self: *Text, maybe_event: ?*termbox.tb_event, insert_mode: bool) !void {
|
||||
if (maybe_event) |event| blk: {
|
||||
if (event.type != termbox.TB_EVENT_KEY) break :blk;
|
||||
|
||||
|
@ -54,9 +55,27 @@ pub fn handle(self: *Text, maybe_event: ?*termbox.tb_event) !void {
|
|||
termbox.TB_KEY_ARROW_LEFT => self.goLeft(),
|
||||
termbox.TB_KEY_ARROW_RIGHT => self.goRight(),
|
||||
termbox.TB_KEY_DELETE => self.delete(),
|
||||
termbox.TB_KEY_BACKSPACE, termbox.TB_KEY_BACKSPACE2 => self.backspace(),
|
||||
termbox.TB_KEY_BACKSPACE, termbox.TB_KEY_BACKSPACE2 => {
|
||||
if (insert_mode) {
|
||||
self.backspace();
|
||||
} else {
|
||||
self.goLeft();
|
||||
}
|
||||
},
|
||||
termbox.TB_KEY_SPACE => try self.write(' '),
|
||||
else => if (event.ch > 31 and event.ch < 127) try self.write(@intCast(event.ch)),
|
||||
else => {
|
||||
if (event.ch > 31 and event.ch < 127) {
|
||||
if (insert_mode) {
|
||||
try self.write(@intCast(event.ch));
|
||||
} else {
|
||||
switch (event.ch) {
|
||||
'h' => self.goLeft(),
|
||||
'l' => self.goRight(),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,9 +4,19 @@ const interop = @import("../interop.zig");
|
|||
const termbox = interop.termbox;
|
||||
|
||||
pub fn initCell(ch: u32, fg: u32, bg: u32) termbox.tb_cell {
|
||||
var cell = std.mem.zeroes(termbox.tb_cell);
|
||||
cell.ch = ch;
|
||||
cell.fg = fg;
|
||||
cell.bg = bg;
|
||||
return cell;
|
||||
return .{
|
||||
.ch = ch,
|
||||
.fg = fg,
|
||||
.bg = bg,
|
||||
};
|
||||
}
|
||||
|
||||
// Every codepoint is assumed to have a width of 1.
|
||||
// Since ly should be running in a tty, this should be fine.
|
||||
pub fn strWidth(str: []const u8) !u8 {
|
||||
const utf8view = try std.unicode.Utf8View.init(str);
|
||||
var utf8 = utf8view.iterator();
|
||||
var i: u8 = 0;
|
||||
while (utf8.nextCodepoint()) |_| i += 1;
|
||||
return i;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue