@@ -23,6 +23,7 @@ import Foundation
2323import LCShim
2424import Logging
2525import Musl
26+ import Glibc
2627
2728struct RunCommand : ParsableCommand {
2829 static let configuration = CommandConfiguration (
@@ -95,6 +96,31 @@ struct RunCommand: ParsableCommand {
9596
9697 try childRootSetup ( rootfs: root, mounts: spec. mounts, log: log, process: process)
9798
99+ var hostFd : Int32 = - 1
100+
101+ if process. terminal {
102+ var containerFd : Int32 = 0
103+ var ws = winsize ( ws_row: 40 , ws_col: 120 , ws_xpixel: 0 , ws_ypixel: 0 )
104+ guard openpty ( & hostFd, & containerFd, nil , nil , & ws) == 0 else {
105+ throw App . Errno ( stage: " openpty() " )
106+ }
107+
108+ guard dup3 ( containerFd, 0 , 0 ) != - 1 else {
109+ throw App . Errno ( stage: " dup3(slave->stdin) " )
110+ }
111+ guard dup3 ( containerFd, 1 , 0 ) != - 1 else {
112+ throw App . Errno ( stage: " dup3(slave->stdout) " )
113+ }
114+ guard dup3 ( containerFd, 2 , 0 ) != - 1 else {
115+ throw App . Errno ( stage: " dup3(slave->stderr) " )
116+ }
117+ _ = close ( containerFd)
118+
119+ guard ioctl ( 0 , UInt ( TIOCSCTTY) , 0 ) != - 1 else {
120+ throw App . Errno ( stage: " setctty() " )
121+ }
122+ }
123+
98124 if !spec. hostname. isEmpty {
99125 let errCode = spec. hostname. withCString { ptr in
100126 Musl . sethostname ( ptr, spec. hostname. count)
@@ -104,11 +130,17 @@ struct RunCommand: ParsableCommand {
104130 }
105131 }
106132
133+ var fdCopy = hostFd
134+ var fdData = Data ( bytes: & fdCopy, count: MemoryLayout . size ( ofValue: fdCopy) )
135+ try childPipe. fileHandleForWriting. write ( contentsOf: fdData)
136+ try childPipe. fileHandleForWriting. close ( )
137+
107138 // Apply O_CLOEXEC to all file descriptors except stdio.
108139 // This ensures that all unwanted fds we may have accidentally
109140 // inherited are marked close-on-exec so they stay out of the
110141 // container.
111- try App . applyCloseExecOnFDs ( )
142+ let preserve : Set < Int32 > = hostFd >= 0 ? [ hostFd] : [ ]
143+ try App . applyCloseExecOnFDs ( preserve: preserve)
112144
113145 try App . setRLimits ( rlimits: process. rlimits)
114146
@@ -129,9 +161,9 @@ struct RunCommand: ParsableCommand {
129161 _ = try childPipe. fileHandleForReading. readToEnd ( )
130162 try childPipe. fileHandleForReading. close ( )
131163
132- // send our child's pid to our parent before we exit.
133- var childPid = processID
134- let data = Data ( bytes: & childPid , count: MemoryLayout . size ( ofValue : childPid ) )
164+ // send our child's pid and the host fd to our parent before we exit.
165+ var payload = [ Int32 ( processID) , hostFd ]
166+ let data = Data ( bytes: & payload , count: MemoryLayout < Int32 > . size * 2 )
135167
136168 try syncfd. write ( contentsOf: data)
137169 try syncfd. close ( )
0 commit comments