Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 18 additions & 6 deletions Sources/CShim/exec_command.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ void exec_command_attrs_init(struct exec_command_attrs *attrs) {
attrs->uid = -1;
attrs->gid = -1;
attrs->pdeathSignal = 0;
attrs->setfgpgrp = 0;
}

static void child_handler(const int sync_pipes[2], const char *executable,
Expand All @@ -117,11 +118,27 @@ static void child_handler(const int sync_pipes[2], const char *executable,
int syncfd = sync_pipes[1];
struct sigaction action = {0};

// closing our parent's side of the pipe
// Closing our parent's side of the pipe
if (close(sync_pipes[0]) < 0) {
goto fail;
}

// Setup process group and foreground before clearing signal mask.
if (attrs.setpgid) {
if (setpgid(0, attrs.pgid) < 0) {
goto fail;
}
}

// Make the new process group the foreground process group so it can read from the TTY.
if (attrs.setfgpgrp) {
if (tcsetpgrp(STDIN_FILENO, getpgrp()) < 0) {
if (errno != ENOTTY && errno != ENXIO) {
goto fail;
}
}
}

// clear sighandlers
action.sa_flags = 0;
action.sa_handler = SIG_DFL;
Expand Down Expand Up @@ -245,11 +262,6 @@ static void child_handler(const int sync_pipes[2], const char *executable,
goto fail;
}
}
if (attrs.setpgid) {
if (setpgid(0, attrs.pgid) < 0) {
goto fail;
}
}

if (attrs.setctty) {
if (ioctl(attrs.ctty, TIOCSCTTY, 0)) {
Expand Down
2 changes: 2 additions & 0 deletions Sources/CShim/include/exec_command.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ struct exec_command_attrs {
int mask;
/// parent death signal (Linux only, 0 to disable)
int pdeathSignal;
/// make the new process group the foreground process group
int setfgpgrp;
};

void exec_command_attrs_init(struct exec_command_attrs *attrs);
Expand Down
29 changes: 27 additions & 2 deletions Sources/Containerization/LinuxContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ public final class LinuxContainer: Container, Sendable {
/// EXPERIMENTAL: Path in the root filesystem for the virtual
/// machine where the OCI runtime used to spawn the container lives.
public var ociRuntimePath: String?
/// Run the container with a minimal init process that handles signal
/// forwarding and zombie reaping.
public var useInit: Bool = false

public init() {}

Expand All @@ -81,7 +84,8 @@ public final class LinuxContainer: Container, Sendable {
hosts: Hosts? = nil,
virtualization: Bool = false,
bootLog: BootLog? = nil,
ociRuntimePath: String? = nil
ociRuntimePath: String? = nil,
useInit: Bool = false
) {
self.process = process
self.cpus = cpus
Expand All @@ -96,6 +100,7 @@ public final class LinuxContainer: Container, Sendable {
self.virtualization = virtualization
self.bootLog = bootLog
self.ociRuntimePath = ociRuntimePath
self.useInit = useInit
}
}

Expand Down Expand Up @@ -305,6 +310,12 @@ public final class LinuxContainer: Container, Sendable {
// Process toggles.
spec.process = config.process.toOCI()

// Wrap with init process if requested.
if config.useInit {
let originalArgs = spec.process?.args ?? []
spec.process?.args = ["/.cz-init", "--"] + originalArgs
}

// General toggles.
spec.hostname = config.hostname

Expand Down Expand Up @@ -522,12 +533,26 @@ extension LinuxContainer {
// Also filter out file mount holding directories. We'll mount those separately under /run.
let containerMounts = createdState.vm.mounts[self.id] ?? []
let holdingTags = createdState.fileMountContext.holdingDirectoryTags
spec.mounts =
var mounts: [ContainerizationOCI.Mount] =
containerMounts.dropFirst()
.filter { !holdingTags.contains($0.source) }
.map { $0.to }
+ createdState.fileMountContext.ociBindMounts()

// When useInit is enabled, bind mount vminitd from the VM's filesystem
// into the container so it can be executed.
if self.config.useInit {
mounts.append(
ContainerizationOCI.Mount(
type: "bind",
source: "/sbin/vminitd",
destination: "/.cz-init",
options: ["bind", "ro"]
))
}

spec.mounts = mounts

let stdio = IOUtil.setup(
portAllocator: self.hostVsockPorts,
stdin: self.config.process.stdin,
Expand Down
25 changes: 24 additions & 1 deletion Sources/Containerization/LinuxPod.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ public final class LinuxPod: Sendable {
public var dns: DNS?
/// The hosts file configuration for the container.
public var hosts: Hosts?
/// Run the container with a minimal init process that handles signal
/// forwarding and zombie reaping.
public var useInit: Bool = false

public init() {}
}
Expand Down Expand Up @@ -210,6 +213,12 @@ public final class LinuxPod: Sendable {
// Process configuration
spec.process = config.process.toOCI()

// Wrap with init process if requested.
if config.useInit {
let originalArgs = spec.process?.args ?? []
spec.process?.args = ["/.cz-init", "--"] + originalArgs
}

// General toggles
spec.hostname = config.hostname

Expand Down Expand Up @@ -508,12 +517,26 @@ extension LinuxPod {
// Also filter out file mount holding directories - we mount those separately under /run.
let containerMounts = createdState.vm.mounts[containerID] ?? []
let holdingTags = container.fileMountContext.holdingDirectoryTags
spec.mounts =
var mounts: [ContainerizationOCI.Mount] =
containerMounts.dropFirst()
.filter { !holdingTags.contains($0.source) }
.map { $0.to }
+ container.fileMountContext.ociBindMounts()

// When useInit is enabled, bind mount vminitd from the VM's filesystem
// into the container so it can be executed.
if container.config.useInit {
mounts.append(
ContainerizationOCI.Mount(
type: "bind",
source: "/sbin/vminitd",
destination: "/.cz-init",
options: ["bind", "ro"]
))
}

spec.mounts = mounts

// Configure namespaces for the container
var namespaces: [LinuxNamespace] = [
LinuxNamespace(type: .cgroup),
Expand Down
5 changes: 5 additions & 0 deletions Sources/ContainerizationOS/Command.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ public struct Command: Sendable {
public struct Attrs: Sendable {
/// Set pgroup for the new process.
public var setPGroup: Bool
/// Make the new process group the foreground process group (requires setPGroup).
public var setForegroundPGroup: Bool
/// Inherit the real uid/gid of the parent.
public var resetIDs: Bool
/// Reset the child's signal handlers to the default.
Expand All @@ -73,6 +75,7 @@ public struct Command: Sendable {

public init(
setPGroup: Bool = false,
setForegroundPGroup: Bool = false,
resetIDs: Bool = false,
setSignalDefault: Bool = true,
signalMask: UInt32 = 0,
Expand All @@ -83,6 +86,7 @@ public struct Command: Sendable {
pdeathSignal: Int32? = nil
) {
self.setPGroup = setPGroup
self.setForegroundPGroup = setForegroundPGroup
self.resetIDs = resetIDs
self.setSignalDefault = setSignalDefault
self.signalMask = signalMask
Expand Down Expand Up @@ -195,6 +199,7 @@ extension Command {
attrs.setsid = self.attrs.setsid ? 1 : 0
attrs.setctty = self.attrs.setctty ? 1 : 0
attrs.setpgid = self.attrs.setPGroup ? 1 : 0
attrs.setfgpgrp = self.attrs.setForegroundPGroup ? 1 : 0

var cwdPath: UnsafeMutablePointer<CChar>?
if let chdir = self.directory {
Expand Down
Loading