From 3e306ecfbf3937522de993c745c702e810a96464 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Mon, 8 Apr 2024 16:24:51 +0200 Subject: [PATCH] river: raise the file descriptor limit --- river/command/spawn.zig | 3 +- river/main.zig | 17 ++++----- river/process.zig | 79 +++++++++++++++++++++++++++++++++++++++++ river/util.zig | 16 --------- 4 files changed, 87 insertions(+), 28 deletions(-) create mode 100644 river/process.zig diff --git a/river/command/spawn.zig b/river/command/spawn.zig index ced3922..e62bf2f 100644 --- a/river/command/spawn.zig +++ b/river/command/spawn.zig @@ -19,6 +19,7 @@ const os = std.os; const c = @import("../c.zig"); const util = @import("../util.zig"); +const process = @import("../process.zig"); const Error = @import("../command.zig").Error; const Seat = @import("../Seat.zig"); @@ -40,7 +41,7 @@ pub fn spawn( }; if (pid == 0) { - util.post_fork_pre_execve(); + process.cleanupChild(); const pid2 = os.fork() catch c._exit(1); if (pid2 == 0) os.execveZ("/bin/sh", &child_args, std.c.environ) catch c._exit(1); diff --git a/river/main.zig b/river/main.zig index da59953..74579bf 100644 --- a/river/main.zig +++ b/river/main.zig @@ -27,6 +27,7 @@ const flags = @import("flags"); const c = @import("c.zig"); const util = @import("util.zig"); +const process = @import("process.zig"); const Server = @import("Server.zig"); @@ -98,22 +99,16 @@ pub fn main() anyerror!void { } }; + log.info("river version {s}, initializing server", .{build_options.version}); + + process.setup(); + river_init_wlroots_log(switch (runtime_log_level) { .debug => .debug, .info => .info, .warn, .err => .err, }); - // Ignore SIGPIPE so we don't get killed when writing to a socket that - // has had its read end closed by another process. - const sig_ign = os.Sigaction{ - .handler = .{ .handler = os.SIG.IGN }, - .mask = os.empty_sigset, - .flags = 0, - }; - try os.sigaction(os.SIG.PIPE, &sig_ign, null); - - log.info("river version {s}, initializing server", .{build_options.version}); try server.init(enable_xwayland); defer server.deinit(); @@ -126,7 +121,7 @@ pub fn main() anyerror!void { const child_args = [_:null]?[*:0]const u8{ "/bin/sh", "-c", cmd, null }; const pid = try os.fork(); if (pid == 0) { - util.post_fork_pre_execve(); + process.cleanupChild(); os.execveZ("/bin/sh", &child_args, std.c.environ) catch c._exit(1); } util.gpa.free(cmd); diff --git a/river/process.zig b/river/process.zig new file mode 100644 index 0000000..27316c1 --- /dev/null +++ b/river/process.zig @@ -0,0 +1,79 @@ +// This file is part of river, a dynamic tiling wayland compositor. +// +// Copyright 2022-2024 The River Developers +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +const std = @import("std"); +const os = std.os; + +const c = @import("c.zig"); + +var original_rlimit: ?os.rlimit = null; + +pub fn setup() void { + // Ignore SIGPIPE so we don't get killed when writing to a socket that + // has had its read end closed by another process. + const sig_ign = os.Sigaction{ + .handler = .{ .handler = os.SIG.IGN }, + .mask = os.empty_sigset, + .flags = 0, + }; + os.sigaction(os.SIG.PIPE, &sig_ign, null) catch unreachable; + + // Most unix systems have a default limit of 1024 file descriptors and it + // seems unlikely for this default to be universally raised due to the + // broken behavior of select() on fds with value >1024. However, it is + // unreasonable to use such a low limit for a process such as river which + // uses many fds in its communication with wayland clients and the kernel. + // + // There is however an advantage to having a relatively low limit: it helps + // to catch any fd leaks. Therefore, don't use some crazy high limit that + // can never be reached before the system runs out of memory. This can be + // raised further if anyone reaches it in practice. + if (os.getrlimit(.NOFILE)) |original| { + original_rlimit = original; + const new: os.rlimit = .{ + .cur = @min(4096, original.max), + .max = original.max, + }; + if (os.setrlimit(.NOFILE, new)) { + std.log.info("raised file descriptor limit of the river process to {d}", .{new.cur}); + } else |_| { + std.log.err("setrlimit failed, using system default file descriptor limit of {d}", .{ + original.cur, + }); + } + } else |_| { + std.log.err("getrlimit failed, using system default file descriptor limit ", .{}); + } +} + +pub fn cleanupChild() void { + if (c.setsid() < 0) unreachable; + if (os.system.sigprocmask(os.SIG.SETMASK, &os.empty_sigset, null) < 0) unreachable; + + const sig_dfl = os.Sigaction{ + .handler = .{ .handler = os.SIG.DFL }, + .mask = os.empty_sigset, + .flags = 0, + }; + os.sigaction(os.SIG.PIPE, &sig_dfl, null) catch unreachable; + + if (original_rlimit) |original| { + os.setrlimit(.NOFILE, original) catch { + std.log.err("failed to restore original file descriptor limit for " ++ + "child process, setrlimit failed", .{}); + }; + } +} diff --git a/river/util.zig b/river/util.zig index 62e7e97..efbaf34 100644 --- a/river/util.zig +++ b/river/util.zig @@ -15,22 +15,6 @@ // along with this program. If not, see . const std = @import("std"); -const os = std.os; - -const xkb = @import("xkbcommon"); - -const c = @import("c.zig"); /// The global general-purpose allocator used throughout river's code pub const gpa = std.heap.c_allocator; - -pub fn post_fork_pre_execve() void { - if (c.setsid() < 0) unreachable; - if (os.system.sigprocmask(os.SIG.SETMASK, &os.empty_sigset, null) < 0) unreachable; - const sig_dfl = os.Sigaction{ - .handler = .{ .handler = os.SIG.DFL }, - .mask = os.empty_sigset, - .flags = 0, - }; - os.sigaction(os.SIG.PIPE, &sig_dfl, null) catch @panic("sigaction before fork failed"); -}