Reduce libc usage & move more stuff to interop

Signed-off-by: AnErrupTion <anerruption@disroot.org>
This commit is contained in:
AnErrupTion 2025-08-28 01:39:19 +02:00
parent 336847d418
commit 7cfb947187
No known key found for this signature in database
10 changed files with 256 additions and 165 deletions

View File

@ -6,17 +6,17 @@ const Ini = ini.Ini;
pub const DesktopEntry = struct {
Exec: []const u8 = "",
Name: [:0]const u8 = "",
DesktopNames: ?[:0]u8 = null,
Name: []const u8 = "",
DesktopNames: ?[]u8 = null,
Terminal: ?bool = null,
};
pub const Entry = struct { @"Desktop Entry": DesktopEntry = .{} };
entry_ini: ?Ini(Entry) = null,
name: [:0]const u8 = "",
xdg_session_desktop: ?[:0]const u8 = null,
xdg_desktop_names: ?[:0]const u8 = null,
name: []const u8 = "",
xdg_session_desktop: ?[]const u8 = null,
xdg_desktop_names: ?[]const u8 = null,
cmd: []const u8 = "",
specifier: []const u8 = "",
display_server: DisplayServer = .wayland,

View File

@ -2,5 +2,5 @@ const std = @import("std");
// We set both values to 0 by default so that, in case they aren't present in
// the login.defs for some reason, then only the root username will be shown
uid_min: std.c.uid_t = 0,
uid_max: std.c.uid_t = 0,
uid_min: std.posix.uid_t = 0,
uid_max: std.posix.uid_t = 0,

View File

@ -14,7 +14,7 @@ const Utmp = utmp.utmpx;
pub const AuthOptions = struct {
tty: u8,
service_name: [:0]const u8,
path: ?[:0]const u8,
path: ?[]const u8,
session_log: ?[]const u8,
xauth_cmd: []const u8,
setup_cmd: []const u8,
@ -33,15 +33,15 @@ pub fn sessionSignalHandler(i: c_int) callconv(.c) void {
if (child_pid > 0) _ = std.c.kill(child_pid, i);
}
pub fn authenticate(options: AuthOptions, current_environment: Environment, login: [:0]const u8, password: [:0]const u8) !void {
pub fn authenticate(allocator: std.mem.Allocator, options: AuthOptions, current_environment: Environment, login: [:0]const u8, password: [:0]const u8) !void {
var tty_buffer: [3]u8 = undefined;
const tty_str = try std.fmt.bufPrintZ(&tty_buffer, "{d}", .{options.tty});
const tty_str = try std.fmt.bufPrint(&tty_buffer, "{d}", .{options.tty});
var pam_tty_buffer: [6]u8 = undefined;
const pam_tty_str = try std.fmt.bufPrintZ(&pam_tty_buffer, "tty{d}", .{options.tty});
// Set the XDG environment variables
try setXdgEnv(tty_str, current_environment);
try setXdgEnv(allocator, tty_str, current_environment);
// Open the PAM session
var credentials = [_:null]?[*:0]const u8{ login, password };
@ -75,27 +75,23 @@ pub fn authenticate(options: AuthOptions, current_environment: Environment, logi
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
defer status = interop.pam.pam_close_session(handle, 0);
var pwd: *interop.pwd.passwd = undefined;
var user_entry: interop.UsernameEntry = undefined;
{
defer interop.pwd.endpwent();
defer interop.closePasswordDatabase();
// Get password structure from username
pwd = interop.pwd.getpwnam(login) orelse return error.GetPasswordNameFailed;
user_entry = interop.getUsernameEntry(login) orelse return error.GetPasswordNameFailed;
}
// Set user shell if it hasn't already been set
if (pwd.pw_shell == null) {
interop.unistd.setusershell();
pwd.pw_shell = interop.unistd.getusershell();
interop.unistd.endusershell();
}
if (user_entry.shell == null) interop.setUserShell(&user_entry);
var shared_err = try SharedError.init();
defer shared_err.deinit();
child_pid = try std.posix.fork();
if (child_pid == 0) {
startSession(options, tty_str, pwd, handle, current_environment) catch |e| {
startSession(allocator, options, tty_str, user_entry, handle, current_environment) catch |e| {
shared_err.writeError(e);
std.process.exit(1);
};
@ -119,7 +115,7 @@ pub fn authenticate(options: AuthOptions, current_environment: Environment, logi
};
std.posix.sigaction(std.posix.SIG.TERM, &act, null);
try addUtmpEntry(&entry, pwd.pw_name.?, child_pid);
try addUtmpEntry(&entry, user_entry.username.?, child_pid);
}
// Wait for the session to stop
_ = std.posix.waitpid(child_pid, 0);
@ -130,99 +126,89 @@ pub fn authenticate(options: AuthOptions, current_environment: Environment, logi
}
fn startSession(
allocator: std.mem.Allocator,
options: AuthOptions,
tty_str: [:0]u8,
pwd: *interop.pwd.passwd,
tty_str: []u8,
user_entry: interop.UsernameEntry,
handle: ?*interop.pam.pam_handle,
current_environment: Environment,
) !void {
if (builtin.os.tag == .freebsd) {
// FreeBSD has initgroups() in unistd
const status = interop.unistd.initgroups(pwd.pw_name, pwd.pw_gid);
if (status != 0) return error.GroupInitializationFailed;
// FreeBSD sets the GID and UID with setusercontext()
const result = interop.pwd.setusercontext(null, pwd, pwd.pw_uid, interop.pwd.LOGIN_SETALL);
if (result != 0) return error.SetUserUidFailed;
} else {
const status = interop.grp.initgroups(pwd.pw_name, pwd.pw_gid);
if (status != 0) return error.GroupInitializationFailed;
std.posix.setgid(pwd.pw_gid) catch return error.SetUserGidFailed;
std.posix.setuid(pwd.pw_uid) catch return error.SetUserUidFailed;
}
// Set the user's GID & PID
try interop.setUserContext(allocator, user_entry);
// Set up the environment
try initEnv(pwd, options.path);
try initEnv(allocator, user_entry, options.path);
// Reset the XDG environment variables
try setXdgEnv(tty_str, current_environment);
try setXdgEnv(allocator, tty_str, current_environment);
// Set the PAM variables
const pam_env_vars: ?[*:null]?[*:0]u8 = interop.pam.pam_getenvlist(handle);
if (pam_env_vars == null) return error.GetEnvListFailed;
const env_list = std.mem.span(pam_env_vars.?);
for (env_list) |env_var| _ = interop.stdlib.putenv(env_var);
for (env_list) |env_var| try interop.putEnvironmentVariable(env_var);
// Change to the user's home directory
std.posix.chdirZ(pwd.pw_dir.?) catch return error.ChangeDirectoryFailed;
std.posix.chdir(user_entry.home.?) catch return error.ChangeDirectoryFailed;
// Signal to the session process to give up control on the TTY
std.posix.kill(options.session_pid, std.posix.SIG.CHLD) catch return error.TtyControlTransferFailed;
// Execute what the user requested
switch (current_environment.display_server) {
.wayland => try executeWaylandCmd(pwd.pw_shell.?, options, current_environment.cmd),
.shell => try executeShellCmd(pwd.pw_shell.?, options),
.wayland => try executeWaylandCmd(allocator, user_entry.shell.?, options, current_environment.cmd),
.shell => try executeShellCmd(allocator, user_entry.shell.?, options),
.xinitrc, .x11 => if (build_options.enable_x11_support) {
var vt_buf: [5]u8 = undefined;
const vt = try std.fmt.bufPrint(&vt_buf, "vt{d}", .{options.tty});
try executeX11Cmd(pwd.pw_shell.?, pwd.pw_dir.?, options, current_environment.cmd, vt);
try executeX11Cmd(allocator, user_entry.shell.?, user_entry.home.?, options, current_environment.cmd, vt);
},
.custom => try executeCustomCmd(pwd.pw_shell.?, options, current_environment.is_terminal, current_environment.cmd),
.custom => try executeCustomCmd(allocator, user_entry.shell.?, options, current_environment.is_terminal, current_environment.cmd),
}
}
fn initEnv(pwd: *interop.pwd.passwd, path_env: ?[:0]const u8) !void {
_ = interop.stdlib.setenv("HOME", pwd.pw_dir, 1);
_ = interop.stdlib.setenv("PWD", pwd.pw_dir, 1);
_ = interop.stdlib.setenv("SHELL", pwd.pw_shell, 1);
_ = interop.stdlib.setenv("USER", pwd.pw_name, 1);
_ = interop.stdlib.setenv("LOGNAME", pwd.pw_name, 1);
fn initEnv(allocator: std.mem.Allocator, entry: interop.UsernameEntry, path_env: ?[]const u8) !void {
if (entry.home) |home| {
try interop.setEnvironmentVariable(allocator, "HOME", home, true);
try interop.setEnvironmentVariable(allocator, "PWD", home, true);
} else return error.NoHomeDirectory;
try interop.setEnvironmentVariable(allocator, "SHELL", entry.shell.?, true);
try interop.setEnvironmentVariable(allocator, "USER", entry.username.?, true);
try interop.setEnvironmentVariable(allocator, "LOGNAME", entry.username.?, true);
if (path_env) |path| {
const status = interop.stdlib.setenv("PATH", path, 1);
if (status != 0) return error.SetPathFailed;
interop.setEnvironmentVariable(allocator, "PATH", path, true) catch return error.SetPathFailed;
}
}
fn setXdgEnv(tty_str: [:0]u8, environment: Environment) !void {
_ = interop.stdlib.setenv("XDG_SESSION_TYPE", switch (environment.display_server) {
fn setXdgEnv(allocator: std.mem.Allocator, tty_str: []u8, environment: Environment) !void {
try interop.setEnvironmentVariable(allocator, "XDG_SESSION_TYPE", switch (environment.display_server) {
.wayland => "wayland",
.shell => "tty",
.xinitrc, .x11 => "x11",
.custom => "unspecified",
}, 0);
}, false);
// The "/run/user/%d" directory is not available on FreeBSD. It is much
// better to stick to the defaults and let applications using
// XDG_RUNTIME_DIR to fall back to directories inside user's home
// directory.
if (builtin.os.tag != .freebsd) {
const uid = interop.unistd.getuid();
const uid = std.posix.getuid();
var uid_buffer: [32]u8 = undefined; // No UID can be larger than this
const uid_str = try std.fmt.bufPrintZ(&uid_buffer, "/run/user/{d}", .{uid});
const uid_str = try std.fmt.bufPrint(&uid_buffer, "/run/user/{d}", .{uid});
_ = interop.stdlib.setenv("XDG_RUNTIME_DIR", uid_str, 0);
try interop.setEnvironmentVariable(allocator, "XDG_RUNTIME_DIR", uid_str, false);
}
if (environment.xdg_desktop_names) |xdg_desktop_names| _ = interop.stdlib.setenv("XDG_CURRENT_DESKTOP", xdg_desktop_names, 0);
_ = interop.stdlib.setenv("XDG_SESSION_CLASS", "user", 0);
_ = interop.stdlib.setenv("XDG_SESSION_ID", "1", 0);
if (environment.xdg_session_desktop) |desktop_name| _ = interop.stdlib.setenv("XDG_SESSION_DESKTOP", desktop_name, 0);
_ = interop.stdlib.setenv("XDG_SEAT", "seat0", 0);
_ = interop.stdlib.setenv("XDG_VTNR", tty_str, 0);
if (environment.xdg_desktop_names) |xdg_desktop_names| try interop.setEnvironmentVariable(allocator, "XDG_CURRENT_DESKTOP", xdg_desktop_names, false);
try interop.setEnvironmentVariable(allocator, "XDG_SESSION_CLASS", "user", false);
try interop.setEnvironmentVariable(allocator, "XDG_SESSION_ID", "1", false);
if (environment.xdg_session_desktop) |desktop_name| try interop.setEnvironmentVariable(allocator, "XDG_SESSION_DESKTOP", desktop_name, false);
try interop.setEnvironmentVariable(allocator, "XDG_SEAT", "seat0", false);
try interop.setEnvironmentVariable(allocator, "XDG_VTNR", tty_str, false);
}
fn loginConv(
@ -274,8 +260,8 @@ fn loginConv(
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.?);
if (username) |str| allocator.free(str);
if (password) |str| allocator.free(str);
} else {
resp.?.* = response.ptr;
}
@ -310,7 +296,7 @@ fn getXPid(display_num: u8) !i32 {
return std.fmt.parseInt(i32, std.mem.trim(u8, buffer[0..written], " "), 10);
}
fn createXauthFile(pwd: [:0]const u8) ![:0]const u8 {
fn createXauthFile(pwd: [:0]const u8) ![]const u8 {
var xauth_buf: [100]u8 = undefined;
var xauth_dir: [:0]const u8 = undefined;
const xdg_rt_dir = std.posix.getenv("XDG_RUNTIME_DIR");
@ -351,8 +337,8 @@ fn createXauthFile(pwd: [:0]const u8) ![:0]const u8 {
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.createFileAbsoluteZ(xauthority, .{});
const xauthority: []u8 = try std.fmt.bufPrint(&buf, "{s}/{s}", .{ trimmed_xauth_dir, xauth_file });
const file = try std.fs.createFileAbsolute(xauthority, .{});
file.close();
return xauthority;
@ -368,13 +354,13 @@ fn mcookie() [Md5.digest_length * 2]u8 {
return std.fmt.bytesToHex(&out, .lower);
}
fn xauth(display_name: [:0]u8, shell: [*:0]const u8, pw_dir: [*:0]const u8, options: AuthOptions) !void {
fn xauth(allocator: std.mem.Allocator, display_name: []u8, shell: [*:0]const u8, pw_dir: [*:0]const u8, options: AuthOptions) !void {
var pwd_buf: [100]u8 = undefined;
const pwd = try std.fmt.bufPrintZ(&pwd_buf, "{s}", .{pw_dir});
const xauthority = try createXauthFile(pwd);
_ = interop.stdlib.setenv("XAUTHORITY", xauthority, 1);
_ = interop.stdlib.setenv("DISPLAY", display_name, 1);
try interop.setEnvironmentVariable(allocator, "XAUTHORITY", xauthority, true);
try interop.setEnvironmentVariable(allocator, "DISPLAY", display_name, true);
const magic_cookie = mcookie();
@ -391,40 +377,53 @@ fn xauth(display_name: [:0]u8, shell: [*:0]const u8, pw_dir: [*:0]const u8, opti
if (status.status != 0) return error.XauthFailed;
}
fn executeShellCmd(shell: [*:0]const u8, options: AuthOptions) !void {
fn executeShellCmd(allocator: std.mem.Allocator, shell: []const u8, options: AuthOptions) !void {
// We don't want to redirect stdout and stderr in a shell session
const shell_z = try allocator.dupeZ(u8, shell);
defer allocator.free(shell_z);
var cmd_buffer: [1024]u8 = undefined;
const cmd_str = try std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s}", .{ options.setup_cmd, options.login_cmd orelse "", shell });
const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str };
return std.posix.execveZ(shell, &args, std.c.environ);
const args = [_:null]?[*:0]const u8{ shell_z, "-c", cmd_str };
return std.posix.execveZ(shell_z, &args, std.c.environ);
}
fn executeWaylandCmd(shell: [*:0]const u8, options: AuthOptions, desktop_cmd: []const u8) !void {
fn executeWaylandCmd(allocator: std.mem.Allocator, shell: []const u8, options: AuthOptions, desktop_cmd: []const u8) !void {
var maybe_log_file: ?std.fs.File = null;
if (options.session_log) |log_path| {
maybe_log_file = try redirectStandardStreams(log_path, true);
}
defer if (maybe_log_file) |log_file| log_file.close();
const shell_z = try allocator.dupeZ(u8, shell);
defer allocator.free(shell_z);
var cmd_buffer: [1024]u8 = undefined;
const cmd_str = try std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s}", .{ options.setup_cmd, options.login_cmd orelse "", desktop_cmd });
const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str };
return std.posix.execveZ(shell, &args, std.c.environ);
const args = [_:null]?[*:0]const u8{ shell_z, "-c", cmd_str };
return std.posix.execveZ(shell_z, &args, std.c.environ);
}
fn executeX11Cmd(shell: [*:0]const u8, pw_dir: [*:0]const u8, options: AuthOptions, desktop_cmd: []const u8, vt: []const u8) !void {
fn executeX11Cmd(allocator: std.mem.Allocator, shell: []const u8, home: []const u8, options: AuthOptions, desktop_cmd: []const u8, vt: []const u8) !void {
const display_num = try getFreeDisplay();
var buf: [5]u8 = undefined;
const display_name = try std.fmt.bufPrintZ(&buf, ":{d}", .{display_num});
try xauth(display_name, shell, pw_dir, options);
const display_name = try std.fmt.bufPrint(&buf, ":{d}", .{display_num});
const shell_z = try allocator.dupeZ(u8, shell);
defer allocator.free(shell_z);
const home_z = try allocator.dupeZ(u8, home);
defer allocator.free(home_z);
try xauth(allocator, display_name, shell_z, home_z, options);
const pid = try std.posix.fork();
if (pid == 0) {
var cmd_buffer: [1024]u8 = undefined;
const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s}", .{ options.x_cmd, display_name, vt }) catch std.process.exit(1);
const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str };
std.posix.execveZ(shell, &args, std.c.environ) catch {};
const args = [_:null]?[*:0]const u8{ shell_z, "-c", cmd_str };
std.posix.execveZ(shell_z, &args, std.c.environ) catch {};
std.process.exit(1);
}
@ -446,8 +445,8 @@ fn executeX11Cmd(shell: [*:0]const u8, pw_dir: [*:0]const u8, options: AuthOptio
if (xorg_pid == 0) {
var cmd_buffer: [1024]u8 = undefined;
const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s}", .{ options.setup_cmd, options.login_cmd orelse "", desktop_cmd }) catch std.process.exit(1);
const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str };
std.posix.execveZ(shell, &args, std.c.environ) catch {};
const args = [_:null]?[*:0]const u8{ shell_z, "-c", cmd_str };
std.posix.execveZ(shell_z, &args, std.c.environ) catch {};
std.process.exit(1);
}
@ -467,11 +466,10 @@ fn executeX11Cmd(shell: [*:0]const u8, pw_dir: [*:0]const u8, options: AuthOptio
std.Thread.sleep(std.time.ns_per_s * 1); // Wait 1 second before sending SIGKILL
std.posix.kill(x_pid, std.posix.SIG.KILL) catch return;
var status: c_int = 0;
_ = std.c.waitpid(x_pid, &status, 0);
_ = std.posix.waitpid(x_pid, 0);
}
fn executeCustomCmd(shell: [*:0]const u8, options: AuthOptions, is_terminal: bool, exec_cmd: []const u8) !void {
fn executeCustomCmd(allocator: std.mem.Allocator, shell: []const u8, options: AuthOptions, is_terminal: bool, exec_cmd: []const u8) !void {
var maybe_log_file: ?std.fs.File = null;
if (!is_terminal) {
// For custom desktop entries, the "Terminal" value here determines if
@ -483,10 +481,13 @@ fn executeCustomCmd(shell: [*:0]const u8, options: AuthOptions, is_terminal: boo
}
defer if (maybe_log_file) |log_file| log_file.close();
const shell_z = try allocator.dupeZ(u8, shell);
defer allocator.free(shell_z);
var cmd_buffer: [1024]u8 = undefined;
const cmd_str = try std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s}", .{ options.setup_cmd, options.login_cmd orelse "", exec_cmd });
const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str };
return std.posix.execveZ(shell, &args, std.c.environ);
const args = [_:null]?[*:0]const u8{ shell_z, "-c", cmd_str };
return std.posix.execveZ(shell_z, &args, std.c.environ);
}
fn redirectStandardStreams(session_log: []const u8, create: bool) !std.fs.File {
@ -498,7 +499,7 @@ fn redirectStandardStreams(session_log: []const u8, create: bool) !std.fs.File {
return log_file;
}
fn addUtmpEntry(entry: *Utmp, username: [*:0]const u8, pid: c_int) !void {
fn addUtmpEntry(entry: *Utmp, username: []const u8, pid: c_int) !void {
entry.ut_type = utmp.USER_PROCESS;
entry.ut_pid = pid;
@ -520,12 +521,11 @@ fn addUtmpEntry(entry: *Utmp, username: [*:0]const u8, pid: c_int) !void {
host[0] = 0;
entry.ut_host = host;
var tv: interop.system_time.timeval = undefined;
_ = interop.system_time.gettimeofday(&tv, null);
const time = try interop.getTimeOfDay();
entry.ut_tv = .{
.tv_sec = @intCast(tv.tv_sec),
.tv_usec = @intCast(tv.tv_usec),
.tv_sec = @intCast(time.seconds),
.tv_usec = @intCast(time.microseconds),
};
entry.ut_addr_v6[0] = 0;

View File

@ -11,13 +11,11 @@ pub const WIDTH = Lang.WIDTH;
pub const HEIGHT = Lang.HEIGHT;
pub const SIZE = Lang.SIZE;
pub fn clockCell(animate: bool, char: u8, fg: u32, bg: u32, bigclock: Bigclock) [SIZE]Cell {
pub fn clockCell(animate: bool, char: u8, fg: u32, bg: u32, bigclock: Bigclock) ![SIZE]Cell {
var cells: [SIZE]Cell = undefined;
var tv: interop.system_time.timeval = undefined;
_ = interop.system_time.gettimeofday(&tv, null);
const clock_chars = toBigNumber(if (animate and char == ':' and @divTrunc(tv.tv_usec, 500000) != 0) ' ' else char, bigclock);
const time = try interop.getTimeOfDay();
const clock_chars = toBigNumber(if (animate and char == ':' and @divTrunc(time.microseconds, 500000) != 0) ' ' else char, bigclock);
for (0..cells.len) |i| cells[i] = Cell.init(clock_chars[i], fg, bg);
return cells;

View File

@ -1,10 +1,10 @@
const builtin = @import("builtin");
const interop = @import("../interop.zig");
pub const WIDTH = 5;
pub const HEIGHT = 5;
pub const SIZE = WIDTH * HEIGHT;
pub const X: u32 = if (builtin.os.tag == .linux or builtin.os.tag.isBSD()) 0x2593 else '#';
pub const X: u32 = if (interop.supportsUnicode()) 0x2593 else '#';
pub const O: u32 = 0;
// zig fmt: off

View File

@ -61,7 +61,7 @@ margin_box_h: u8 = 2,
margin_box_v: u8 = 1,
min_refresh_delta: u16 = 5,
numlock: bool = false,
path: ?[:0]const u8 = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
path: ?[]const u8 = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
restart_cmd: []const u8 = "/sbin/shutdown -r now",
restart_key: []const u8 = "F2",
save: bool = true,

View File

@ -2,6 +2,19 @@ const std = @import("std");
const builtin = @import("builtin");
const Allocator = std.mem.Allocator;
pub const TimeOfDay = struct {
seconds: i64,
microseconds: i64,
};
pub const UsernameEntry = struct {
username: ?[]const u8,
uid: std.posix.uid_t,
gid: std.posix.gid_t,
home: ?[]const u8,
shell: ?[]const u8,
};
pub const termbox = @import("termbox2");
pub const pam = @cImport({
@ -17,44 +30,44 @@ pub const xcb = @cImport({
@cInclude("xcb/xcb.h");
});
pub const unistd = @cImport({
@cInclude("unistd.h");
});
pub const time = @cImport({
@cInclude("time.h");
});
pub const system_time = @cImport({
@cInclude("sys/time.h");
});
pub const stdlib = @cImport({
@cInclude("stdlib.h");
});
pub const pwd = @cImport({
const pwd = @cImport({
@cInclude("pwd.h");
// We include a FreeBSD-specific header here since login_cap.h references
// the passwd struct directly, so we can't import it separately
if (builtin.os.tag == .freebsd) @cInclude("login_cap.h");
});
pub const grp = @cImport({
const stdlib = @cImport({
@cInclude("stdlib.h");
});
const unistd = @cImport({
@cInclude("unistd.h");
});
const grp = @cImport({
@cInclude("grp.h");
});
const system_time = @cImport({
@cInclude("sys/time.h");
});
const time = @cImport({
@cInclude("time.h");
});
// BSD-specific headers
pub const kbio = @cImport({
const kbio = @cImport({
@cInclude("sys/kbio.h");
});
// Linux-specific headers
pub const kd = @cImport({
const kd = @cImport({
@cInclude("sys/kd.h");
});
pub const vt = @cImport({
const vt = @cImport({
@cInclude("sys/vt.h");
});
@ -65,6 +78,10 @@ const set_led_state = if (builtin.os.tag.isBSD()) kbio.KDSETLED else kd.KDSKBLED
const numlock_led = if (builtin.os.tag.isBSD()) kbio.LED_NUM else kd.K_NUMLOCK;
const capslock_led = if (builtin.os.tag.isBSD()) kbio.LED_CAP else kd.K_CAPSLOCK;
pub fn supportsUnicode() bool {
return builtin.os.tag == .linux or builtin.os.tag.isBSD();
}
pub fn timeAsString(buf: [:0]u8, format: [:0]const u8) []u8 {
const timer = std.time.timestamp();
const tm_info = time.localtime(&timer);
@ -73,11 +90,23 @@ pub fn timeAsString(buf: [:0]u8, format: [:0]const u8) []u8 {
return buf[0..len];
}
pub fn getTimeOfDay() !TimeOfDay {
var tv: system_time.timeval = undefined;
const status = system_time.gettimeofday(&tv, null);
if (status != 0) return error.FailedToGetTimeOfDay;
return .{
.seconds = @intCast(tv.tv_sec),
.microseconds = @intCast(tv.tv_usec),
};
}
pub fn switchTty(tty: u8) !void {
var status = std.c.ioctl(std.c.STDIN_FILENO, vt.VT_ACTIVATE, tty);
var status = std.c.ioctl(std.posix.STDIN_FILENO, vt.VT_ACTIVATE, tty);
if (status != 0) return error.FailedToActivateTty;
status = std.c.ioctl(std.c.STDIN_FILENO, vt.VT_WAITACTIVE, tty);
status = std.c.ioctl(std.posix.STDIN_FILENO, vt.VT_WAITACTIVE, tty);
if (status != 0) return error.FailedToWaitForActiveTty;
}
@ -86,7 +115,7 @@ pub fn getLockState() !struct {
capslock: bool,
} {
var led: LedState = undefined;
const status = std.c.ioctl(std.c.STDIN_FILENO, get_led_state, &led);
const status = std.c.ioctl(std.posix.STDIN_FILENO, get_led_state, &led);
if (status != 0) return error.FailedToGetLockState;
return .{
@ -97,7 +126,7 @@ pub fn getLockState() !struct {
pub fn setNumlock(val: bool) !void {
var led: LedState = undefined;
var status = std.c.ioctl(std.c.STDIN_FILENO, get_led_state, &led);
var status = std.c.ioctl(std.posix.STDIN_FILENO, get_led_state, &led);
if (status != 0) return error.FailedToGetNumlock;
const numlock = (led & numlock_led) != 0;
@ -106,3 +135,80 @@ pub fn setNumlock(val: bool) !void {
if (status != 0) return error.FailedToSetNumlock;
}
}
pub fn setUserContext(allocator: std.mem.Allocator, entry: UsernameEntry) !void {
const username_z = try allocator.dupeZ(u8, entry.username.?);
defer allocator.free(username_z);
if (builtin.os.tag == .freebsd) {
// FreeBSD has initgroups() in unistd
const status = unistd.initgroups(username_z.ptr, @intCast(entry.gid));
if (status != 0) return error.GroupInitializationFailed;
// FreeBSD sets the GID and UID with setusercontext()
// TODO
const result = pwd.setusercontext(null, entry, @intCast(entry.uid), pwd.LOGIN_SETALL);
if (result != 0) return error.SetUserUidFailed;
} else {
const status = grp.initgroups(username_z.ptr, @intCast(entry.gid));
if (status != 0) return error.GroupInitializationFailed;
std.posix.setgid(@intCast(entry.gid)) catch return error.SetUserGidFailed;
std.posix.setuid(@intCast(entry.uid)) catch return error.SetUserUidFailed;
}
}
pub fn setUserShell(entry: *UsernameEntry) void {
unistd.setusershell();
const shell = unistd.getusershell();
entry.shell = shell[0..std.mem.len(shell)];
unistd.endusershell();
}
pub fn setEnvironmentVariable(allocator: std.mem.Allocator, name: []const u8, value: []const u8, replace: bool) !void {
const name_z = try allocator.dupeZ(u8, name);
defer allocator.free(name_z);
const value_z = try allocator.dupeZ(u8, value);
defer allocator.free(value_z);
const status = stdlib.setenv(name_z.ptr, value_z.ptr, @intFromBool(replace));
if (status != 0) return error.SetEnvironmentVariableFailed;
}
pub fn putEnvironmentVariable(name_and_value: [*c]u8) !void {
const status = stdlib.putenv(name_and_value);
if (status != 0) return error.PutEnvironmentVariableFailed;
}
pub fn getNextUsernameEntry() ?UsernameEntry {
const entry = pwd.getpwent();
if (entry == null) return null;
return .{
.username = if (entry.*.pw_name) |name| name[0..std.mem.len(name)] else null,
.uid = @intCast(entry.*.pw_uid),
.gid = @intCast(entry.*.pw_gid),
.home = if (entry.*.pw_dir) |dir| dir[0..std.mem.len(dir)] else null,
.shell = if (entry.*.pw_shell) |shell| shell[0..std.mem.len(shell)] else null,
};
}
pub fn getUsernameEntry(username: [:0]const u8) ?UsernameEntry {
const entry = pwd.getpwnam(username);
if (entry == null) return null;
return .{
.username = if (entry.*.pw_name) |name| name[0..std.mem.len(name)] else null,
.uid = @intCast(entry.*.pw_uid),
.gid = @intCast(entry.*.pw_gid),
.home = if (entry.*.pw_dir) |dir| dir[0..std.mem.len(dir)] else null,
.shell = if (entry.*.pw_shell) |shell| shell[0..std.mem.len(shell)] else null,
};
}
pub fn closePasswordDatabase() void {
pwd.endpwent();
}

View File

@ -31,7 +31,6 @@ const Ini = ini.Ini;
const DisplayServer = enums.DisplayServer;
const Entry = Environment.Entry;
const termbox = interop.termbox;
const unistd = interop.unistd;
const temporary_allocator = std.heap.page_allocator;
const ly_top_str = "Ly version " ++ build_options.version;
@ -85,8 +84,7 @@ pub fn main() !void {
defer _ = gpa.deinit();
// Allows stopping an animation after some time
var tv_zero: interop.system_time.timeval = undefined;
_ = interop.system_time.gettimeofday(&tv_zero, null);
const time_start = try interop.getTimeOfDay();
var animation_timed_out: bool = false;
const allocator = gpa.allocator();
@ -547,7 +545,8 @@ pub fn main() !void {
const clock_str = interop.timeAsString(&clock_buf, format);
for (clock_str, 0..) |c, i| {
const clock_cell = bigclock.clockCell(animate, c, buffer.fg, buffer.bg, config.bigclock);
// TODO: Show error
const clock_cell = try bigclock.clockCell(animate, c, buffer.fg, buffer.bg, config.bigclock);
bigclock.alphaBlit(xo + i * (bigclock.WIDTH + 1), yo, buffer.width, buffer.height, clock_cell);
}
}
@ -682,24 +681,21 @@ pub fn main() !void {
if (animate and !animation_timed_out) {
timeout = config.min_refresh_delta;
// check how long we have been running so we can turn off the animation
var tv: interop.system_time.timeval = undefined;
_ = interop.system_time.gettimeofday(&tv, null);
// Check how long we've been running so we can turn off the animation
const time = try interop.getTimeOfDay();
if (config.animation_timeout_sec > 0 and tv.tv_sec - tv_zero.tv_sec > config.animation_timeout_sec) {
if (config.animation_timeout_sec > 0 and time.seconds - time_start.seconds > config.animation_timeout_sec) {
animation_timed_out = true;
animation.deinit();
}
} else if (config.bigclock != .none and config.clock == null) {
var tv: interop.system_time.timeval = undefined;
_ = interop.system_time.gettimeofday(&tv, null);
const time = try interop.getTimeOfDay();
timeout = @intCast((60 - @rem(tv.tv_sec, 60)) * 1000 - @divTrunc(tv.tv_usec, 1000) + 1);
timeout = @intCast((60 - @rem(time.seconds, 60)) * 1000 - @divTrunc(time.microseconds, 1000) + 1);
} else if (config.clock != null or auth_fails >= config.auth_fails) {
var tv: interop.system_time.timeval = undefined;
_ = interop.system_time.gettimeofday(&tv, null);
const time = try interop.getTimeOfDay();
timeout = @intCast(1000 - @divTrunc(tv.tv_usec, 1000) + 1);
timeout = @intCast(1000 - @divTrunc(time.microseconds, 1000) + 1);
}
const event_error = if (timeout == -1) termbox.tb_poll_event(&event) else termbox.tb_peek_event(&event, timeout);
@ -864,7 +860,7 @@ pub fn main() !void {
};
std.posix.sigaction(std.posix.SIG.CHLD, &tty_control_transfer_act, null);
auth.authenticate(auth_options, current_environment, login_text, password_text) catch |err| {
auth.authenticate(allocator, auth_options, current_environment, login_text, password_text) catch |err| {
shared_err.writeError(err);
std.process.exit(1);
};
@ -1016,7 +1012,7 @@ fn crawl(session: *Session, lang: Lang, path: []const u8, display_server: Displa
// Prepare the XDG_CURRENT_DESKTOP environment variable here
const entry = entry_ini.data.@"Desktop Entry";
var maybe_xdg_desktop_names: ?[:0]const u8 = null;
var maybe_xdg_desktop_names: ?[]const u8 = null;
if (entry.DesktopNames) |desktop_names| {
for (desktop_names) |*c| {
if (c.* == ';') c.* = ':';
@ -1024,13 +1020,10 @@ fn crawl(session: *Session, lang: Lang, path: []const u8, display_server: Displa
maybe_xdg_desktop_names = desktop_names;
}
const maybe_session_desktop = if (maybe_xdg_session_desktop) |xdg_session_desktop| try session.label.allocator.dupeZ(u8, xdg_session_desktop) else null;
errdefer if (maybe_session_desktop) |session_desktop| session.label.allocator.free(session_desktop);
try session.addEnvironment(.{
.entry_ini = entry_ini,
.name = entry.Name,
.xdg_session_desktop = maybe_session_desktop,
.xdg_session_desktop = maybe_xdg_session_desktop,
.xdg_desktop_names = maybe_xdg_desktop_names,
.cmd = entry.Exec,
.specifier = switch (display_server) {
@ -1049,24 +1042,20 @@ fn getAllUsernames(allocator: std.mem.Allocator, login_defs_path: []const u8) !S
const uid_range = try getUserIdRange(allocator, login_defs_path);
var usernames: StringList = .empty;
var maybe_entry = interop.pwd.getpwent();
while (maybe_entry != null) {
const entry = maybe_entry.*;
var maybe_entry = interop.getNextUsernameEntry();
while (maybe_entry) |entry| {
// We check if the UID is equal to 0 because we always want to add root
// as a username (even if you can't log into it)
if (entry.pw_uid >= uid_range.uid_min and entry.pw_uid <= uid_range.uid_max or entry.pw_uid == 0) {
const pw_name_slice = entry.pw_name[0..std.mem.len(entry.pw_name)];
const username = try allocator.dupe(u8, pw_name_slice);
if (entry.uid >= uid_range.uid_min and entry.uid <= uid_range.uid_max or entry.uid == 0 and entry.username != null) {
const username = try allocator.dupe(u8, entry.username.?);
try usernames.append(allocator, username);
}
maybe_entry = interop.pwd.getpwent();
maybe_entry = interop.getNextUsernameEntry();
}
interop.pwd.endpwent();
interop.closePasswordDatabase();
return usernames;
}
@ -1086,9 +1075,9 @@ fn getUserIdRange(allocator: std.mem.Allocator, login_defs_path: []const u8) !Ui
const trimmed_line = std.mem.trim(u8, line, " \n\r\t");
if (std.mem.startsWith(u8, trimmed_line, "UID_MIN")) {
uid_range.uid_min = try parseValue(std.c.uid_t, "UID_MIN", trimmed_line);
uid_range.uid_min = try parseValue(std.posix.uid_t, "UID_MIN", trimmed_line);
} else if (std.mem.startsWith(u8, trimmed_line, "UID_MAX")) {
uid_range.uid_max = try parseValue(std.c.uid_t, "UID_MAX", trimmed_line);
uid_range.uid_max = try parseValue(std.posix.uid_t, "UID_MAX", trimmed_line);
}
}

View File

@ -1,5 +1,4 @@
const std = @import("std");
const builtin = @import("builtin");
const interop = @import("../interop.zig");
const Cell = @import("Cell.zig");
@ -82,7 +81,7 @@ pub fn init(options: InitOptions, labels_max_length: usize, random: Random) Term
.fg = options.fg,
.bg = options.bg,
.border_fg = options.border_fg,
.box_chars = if (builtin.os.tag == .linux or builtin.os.tag.isBSD()) .{
.box_chars = if (interop.supportsUnicode()) .{
.left_up = 0x250C,
.left_down = 0x2514,
.right_up = 0x2510,

View File

@ -23,7 +23,6 @@ pub fn init(allocator: Allocator, buffer: *TerminalBuffer) Session {
pub fn deinit(self: *Session) void {
for (self.label.list.items) |*environment| {
if (environment.entry_ini) |*entry_ini| entry_ini.deinit();
if (environment.xdg_session_desktop) |session_desktop| self.label.allocator.free(session_desktop);
}
self.label.deinit();