river: send SIGTERM to init command process group
Run the init command in a new process group and send SIGTERM to the entire group on exit. Without doing this, only the sh invocation used for the `sh -c` would receive SIGTERM. This is particularly useful when starting a per-session server manager as the init command.
This commit is contained in:
parent
f72656b72e
commit
33fb7725c5
2 changed files with 27 additions and 30 deletions
|
@ -39,10 +39,9 @@ following locations, checked in the order listed:
|
||||||
- $HOME/.config/river/init
|
- $HOME/.config/river/init
|
||||||
- /etc/river/init
|
- /etc/river/init
|
||||||
|
|
||||||
This executable init file will be run after river's wayland server is
|
The executable init file will be run as a process group leader after river's
|
||||||
initialized but before entering the main loop. If the process started by
|
wayland server is initialized but before entering the main loop. On exit,
|
||||||
this flag is still running when river exits, river will send SIGTERM and
|
river will send SIGTERM to this process group.
|
||||||
and wait for it to terminate.
|
|
||||||
|
|
||||||
Usually this will be a shell script invoking *riverctl*(1) to create mappings,
|
Usually this will be a shell script invoking *riverctl*(1) to create mappings,
|
||||||
start programs such as a status bar, and preform other configuration.
|
start programs such as a status bar, and preform other configuration.
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const os = std.os;
|
||||||
const wlr = @import("wlroots");
|
const wlr = @import("wlroots");
|
||||||
|
|
||||||
const build_options = @import("build_options");
|
const build_options = @import("build_options");
|
||||||
|
@ -45,7 +46,7 @@ const usage: []const u8 =
|
||||||
|
|
||||||
fn testConfigPath(comptime fmt: []const u8, args: anytype) std.fmt.AllocPrintError!?[:0]const u8 {
|
fn testConfigPath(comptime fmt: []const u8, args: anytype) std.fmt.AllocPrintError!?[:0]const u8 {
|
||||||
const path = try std.fmt.allocPrintZ(util.gpa, fmt, args);
|
const path = try std.fmt.allocPrintZ(util.gpa, fmt, args);
|
||||||
std.os.access(path, std.os.X_OK) catch {
|
os.access(path, os.X_OK) catch {
|
||||||
util.gpa.free(path);
|
util.gpa.free(path);
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
@ -53,11 +54,11 @@ fn testConfigPath(comptime fmt: []const u8, args: anytype) std.fmt.AllocPrintErr
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getStartupCommand() std.fmt.AllocPrintError!?[:0]const u8 {
|
fn getStartupCommand() std.fmt.AllocPrintError!?[:0]const u8 {
|
||||||
if (std.os.getenv("XDG_CONFIG_HOME")) |xdg_config_home| {
|
if (os.getenv("XDG_CONFIG_HOME")) |xdg_config_home| {
|
||||||
if (try testConfigPath("{}/river/init", .{xdg_config_home})) |path| {
|
if (try testConfigPath("{}/river/init", .{xdg_config_home})) |path| {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
} else if (std.os.getenv("HOME")) |home| {
|
} else if (os.getenv("HOME")) |home| {
|
||||||
if (try testConfigPath("{}/.config/river/init", .{home})) |path| {
|
if (try testConfigPath("{}/.config/river/init", .{home})) |path| {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
@ -94,13 +95,13 @@ pub fn main() anyerror!void {
|
||||||
if (std.mem.eql(u8, arg, "-h")) {
|
if (std.mem.eql(u8, arg, "-h")) {
|
||||||
const stdout = std.io.getStdOut().outStream();
|
const stdout = std.io.getStdOut().outStream();
|
||||||
try stdout.print(usage, .{});
|
try stdout.print(usage, .{});
|
||||||
std.os.exit(0);
|
os.exit(0);
|
||||||
} else if (std.mem.eql(u8, arg, "-c")) {
|
} else if (std.mem.eql(u8, arg, "-c")) {
|
||||||
if (it.nextPosix()) |command| {
|
if (it.nextPosix()) |command| {
|
||||||
// If the user used '-c' multiple times the variable
|
// If the user used '-c' multiple times the variable
|
||||||
// already holds a path and needs to be freed.
|
// already holds a path and needs to be freed.
|
||||||
if (startup_command) |ptr| util.gpa.free(ptr);
|
if (startup_command) |cmd| util.gpa.free(cmd);
|
||||||
startup_command = try util.gpa.dupeZ(u8, std.mem.spanZ(command.ptr));
|
startup_command = try util.gpa.dupeZ(u8, command);
|
||||||
} else {
|
} else {
|
||||||
printErrorExit("Error: flag '-c' requires exactly one argument", .{});
|
printErrorExit("Error: flag '-c' requires exactly one argument", .{});
|
||||||
}
|
}
|
||||||
|
@ -115,7 +116,7 @@ pub fn main() anyerror!void {
|
||||||
} else {
|
} else {
|
||||||
const stderr = std.io.getStdErr().outStream();
|
const stderr = std.io.getStdErr().outStream();
|
||||||
try stderr.print(usage, .{});
|
try stderr.print(usage, .{});
|
||||||
std.os.exit(1);
|
os.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,39 +127,36 @@ pub fn main() anyerror!void {
|
||||||
.warn, .err, .crit, .alert, .emerg => .err,
|
.warn, .err, .crit, .alert, .emerg => .err,
|
||||||
});
|
});
|
||||||
|
|
||||||
log_server.info("initializing", .{});
|
|
||||||
|
|
||||||
if (startup_command == null) {
|
if (startup_command == null) {
|
||||||
if (try getStartupCommand()) |path| {
|
if (try getStartupCommand()) |path| startup_command = path;
|
||||||
startup_command = path;
|
|
||||||
log_server.info("Using default startup command path: {}", .{path});
|
|
||||||
} else {
|
|
||||||
log_server.info("Starting without startup command", .{});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log_server.info("Using custom startup command path: {}", .{startup_command});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log_server.info("initializing server", .{});
|
||||||
var server: Server = undefined;
|
var server: Server = undefined;
|
||||||
try server.init();
|
try server.init();
|
||||||
defer server.deinit();
|
defer server.deinit();
|
||||||
|
|
||||||
try server.start();
|
try server.start();
|
||||||
|
|
||||||
const child_pid = if (startup_command) |cmd| blk: {
|
// Run the child in a new process group so that we can send SIGTERM to all
|
||||||
|
// descendants on exit.
|
||||||
|
const child_pgid = if (startup_command) |cmd| blk: {
|
||||||
|
log_server.info("running startup command '{}'", .{cmd});
|
||||||
const child_args = [_:null]?[*:0]const u8{ "/bin/sh", "-c", cmd, null };
|
const child_args = [_:null]?[*:0]const u8{ "/bin/sh", "-c", cmd, null };
|
||||||
const pid = try std.os.fork();
|
const pid = try os.fork();
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
if (std.os.system.sigprocmask(std.os.SIG_SETMASK, &std.os.empty_sigset, null) < 0) unreachable;
|
if (c.setsid() < 0) unreachable;
|
||||||
std.os.execveZ("/bin/sh", &child_args, std.c.environ) catch c._exit(1);
|
if (os.system.sigprocmask(os.SIG_SETMASK, &os.empty_sigset, null) < 0) unreachable;
|
||||||
|
os.execveZ("/bin/sh", &child_args, std.c.environ) catch c._exit(1);
|
||||||
}
|
}
|
||||||
util.gpa.free(cmd);
|
util.gpa.free(cmd);
|
||||||
|
// Since the child has called setsid, the pid is the pgid
|
||||||
break :blk pid;
|
break :blk pid;
|
||||||
} else null;
|
} else null;
|
||||||
defer if (child_pid) |pid|
|
defer if (child_pgid) |pgid|
|
||||||
std.os.kill(pid, std.os.SIGTERM) catch |e| log_server.err("failed to kill startup process: {}", .{e});
|
os.kill(-pgid, os.SIGTERM) catch |e| log_server.err("failed to kill startup process: {}", .{e});
|
||||||
|
|
||||||
log_server.info("running...", .{});
|
log_server.info("running server", .{});
|
||||||
|
|
||||||
server.wl_server.run();
|
server.wl_server.run();
|
||||||
|
|
||||||
|
@ -167,6 +165,6 @@ pub fn main() anyerror!void {
|
||||||
|
|
||||||
fn printErrorExit(comptime format: []const u8, args: anytype) noreturn {
|
fn printErrorExit(comptime format: []const u8, args: anytype) noreturn {
|
||||||
const stderr = std.io.getStdErr().outStream();
|
const stderr = std.io.getStdErr().outStream();
|
||||||
stderr.print(format ++ "\n", args) catch std.os.exit(1);
|
stderr.print(format ++ "\n", args) catch os.exit(1);
|
||||||
std.os.exit(1);
|
os.exit(1);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue