src/os/server_os_linux.zig
Ref: Size: 3.3 KiB History
//! Linux arm of `server_os`. Spellings only; the contract is in the root.
const std = @import("std");
const root = @import("server_os.zig");
const c = @cImport({
@cInclude("pty.h");
@cInclude("sys/ioctl.h");
});
pub fn getpid() std.posix.pid_t {
return std.os.linux.getpid();
}
pub fn peerCred(fd: std.posix.socket_t) ?root.PeerCred {
const Ucred = extern struct { pid: std.posix.pid_t, uid: std.posix.uid_t, gid: std.posix.gid_t };
var cred: Ucred = undefined;
std.posix.getsockopt(fd, std.posix.SOL.SOCKET, std.posix.SO.PEERCRED, std.mem.asBytes(&cred)) catch return null;
return .{ .uid = cred.uid, .pid = cred.pid };
}
pub fn sendNoSigNoWait(fd: std.posix.socket_t, bytes: []const u8) std.posix.SendError!usize {
return std.posix.send(fd, bytes, std.posix.MSG.DONTWAIT | std.posix.MSG.NOSIGNAL);
}
pub fn sockType(fd: std.posix.fd_t) error{NotASocket}!u32 {
var t: i32 = undefined;
var len: std.posix.socklen_t = @sizeOf(@TypeOf(t));
const rc = std.os.linux.getsockopt(fd, std.os.linux.SOL.SOCKET, std.os.linux.SO.TYPE, @ptrCast(&t), &len);
if (std.os.linux.E.init(rc) != .SUCCESS) return error.NotASocket;
return @intCast(t);
}
pub fn forkPty(ws: root.Winsize) error{ForkPtyFailed}!root.ForkedPty {
var master: c_int = undefined;
var cws: c.struct_winsize = .{ .ws_row = ws.row, .ws_col = ws.col, .ws_xpixel = 0, .ws_ypixel = 0 };
const pid = c.forkpty(&master, null, null, &cws);
if (pid < 0) return error.ForkPtyFailed;
return .{ .pid = pid, .master = master };
}
pub fn exitNow(code: u8) noreturn {
std.os.linux.exit_group(code);
}
pub fn forkDetached(
exe: [*:0]const u8,
argv: [*:null]const ?[*:0]const u8,
stdin_fd: std.posix.fd_t,
out_fd: std.posix.fd_t,
) error{ForkFailed}!std.posix.pid_t {
const pid = std.posix.fork() catch return error.ForkFailed;
if (pid != 0) return pid;
_ = std.os.linux.setsid();
std.posix.dup2(stdin_fd, std.posix.STDIN_FILENO) catch exitNow(127);
std.posix.dup2(out_fd, std.posix.STDOUT_FILENO) catch exitNow(127);
std.posix.dup2(out_fd, std.posix.STDERR_FILENO) catch exitNow(127);
std.posix.execveZ(exe, argv, std.c.environ) catch exitNow(127);
unreachable;
}
pub fn closeFrom(first: std.posix.fd_t) void {
// ENOSYS (pre-5.9 kernel) leaves the CLOEXEC flags to do the work alone.
_ = std.os.linux.syscall3(.close_range, @intCast(first), std.math.maxInt(u32), 0);
}
pub fn ptyMode(master: std.posix.fd_t) std.posix.TermiosGetError!root.PtyMode {
// On Linux the master shares one termios with the slave, so what the
// session did with tcsetattr is one syscall away.
const t = try std.posix.tcgetattr(master);
return .{ .icanon = t.lflag.ICANON, .echo = t.lflag.ECHO };
}
pub fn ptyFgPgid(master: std.posix.fd_t) error{IoctlFailed}!std.posix.pid_t {
var pgid: c.pid_t = 0;
if (c.ioctl(master, c.TIOCGPGRP, &pgid) < 0) return error.IoctlFailed;
return @intCast(pgid);
}
pub fn setWinsize(master: std.posix.fd_t, ws: root.Winsize) error{IoctlFailed}!void {
var cws: c.struct_winsize = .{ .ws_row = ws.row, .ws_col = ws.col, .ws_xpixel = 0, .ws_ypixel = 0 };
if (c.ioctl(master, c.TIOCSWINSZ, &cws) < 0) return error.IoctlFailed;
}
pub fn anonFd(name: [*:0]const u8) error{CarrierFailed}!std.posix.fd_t {
return std.posix.memfd_create(std.mem.span(name), 0) catch error.CarrierFailed;
}