Skip to content

Commit d9395f0

Browse files
committed
removed posix_spawnp and used fork,chdir,execve
1 parent 45f2ebf commit d9395f0

File tree

2 files changed

+110
-71
lines changed

2 files changed

+110
-71
lines changed

io/native/src/main/scala/fs2/io/ioplatform.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ import cats.effect.kernel.Resource
3131
import cats.effect.kernel.Sync
3232
import cats.syntax.all._
3333
import fs2.io.internal.NativeUtil._
34-
3534
import java.io.OutputStream
3635
import java.nio.charset.Charset
3736
import java.nio.charset.StandardCharsets

io/native/src/main/scala/fs2/io/process/ProcessesPlatform.scala

Lines changed: 110 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@ import scala.scalanative.posix.sys.wait.*
3333
import scala.scalanative.posix.errno.*
3434
import scala.scalanative.meta.LinktimeInfo
3535
import scala.scalanative.posix.unistd.*
36-
import scala.scalanative.posix.spawn.*
3736
import scala.scalanative.posix.signal.*
3837
import java.io.IOException
3938
import scala.concurrent.duration.*
4039
import cats.effect.LiftIO
4140
import cats.effect.IO
41+
import cats.effect.implicits.*
4242
import org.typelevel.scalaccompat.annotation._
4343

4444
@extern
@@ -57,32 +57,44 @@ object PidFd {
5757
}
5858
}
5959

60+
final case class NativeProcess(
61+
pid: pid_t,
62+
stdinFd: Int,
63+
stdoutFd: Int,
64+
stderrFd: Int
65+
)
66+
6067
private[process] trait ProcessesCompanionPlatform {
6168
def forAsync[F[_]: LiftIO](implicit F: Async[F]): Processes[F] = new UnsealedProcesses[F] {
6269

6370
def spawn(process: ProcessBuilder): Resource[F, Process[F]] = {
6471

6572
def createProcess(): F[NativeProcess] = F.blocking {
6673
Zone { implicit z =>
67-
val allArgs = process.command +: process.args
68-
val argv = stackalloc[CString](allArgs.length.toULong + 1.toULong)
69-
allArgs.zipWithIndex.foreach { case (arg, i) =>
70-
argv(i.toULong) = toCString(arg)
74+
def findExecutable(command: String): Option[String] = {
75+
val pathEnv = sys.env.get("PATH").getOrElse("")
76+
val paths = pathEnv.split(":").toList
77+
78+
paths
79+
.find { dir =>
80+
val fullPath = s"$dir/$command"
81+
access(toCString(fullPath), X_OK) == 0
82+
}
83+
.map(dir => s"$dir/$command")
84+
7185
}
72-
argv(allArgs.length.toULong) = null
7386

7487
val envMap =
7588
if (process.inheritEnv)
7689
sys.env ++ process.extraEnv
77-
else
78-
process.extraEnv
79-
80-
val envp = stackalloc[CString]((envMap.size + 1).toULong)
81-
envMap.zipWithIndex.foreach { case ((k, v), i) =>
82-
envp(i.toULong) = toCString(s"$k=$v")
83-
}
84-
envp(envMap.size.toULong) = null
90+
else process.extraEnv
8591

92+
val executable =
93+
if (process.command.contains("/")) {
94+
process.command
95+
} else {
96+
findExecutable(process.command).getOrElse(process.command)
97+
}
8698
val stdinPipe = stackalloc[CInt](2)
8799
val stdoutPipe = stackalloc[CInt](2)
88100
val stderrPipe = stackalloc[CInt](2)
@@ -91,47 +103,62 @@ private[process] trait ProcessesCompanionPlatform {
91103
throw new RuntimeException("Failed to create pipes")
92104
}
93105

94-
val fileActions = stackalloc[posix_spawn_file_actions_t]()
95-
posix_spawn_file_actions_init(fileActions)
96-
97-
posix_spawn_file_actions_adddup2(fileActions, stdinPipe(0), STDIN_FILENO)
98-
posix_spawn_file_actions_addclose(fileActions, stdinPipe(1))
99-
100-
posix_spawn_file_actions_adddup2(fileActions, stdoutPipe(1), STDOUT_FILENO)
101-
posix_spawn_file_actions_addclose(fileActions, stdoutPipe(0))
102-
103-
posix_spawn_file_actions_adddup2(fileActions, stderrPipe(1), STDERR_FILENO)
104-
posix_spawn_file_actions_addclose(fileActions, stderrPipe(0))
105-
106-
val pid = stackalloc[pid_t]()
107-
val result = posix_spawnp(
108-
pid,
109-
argv(0),
110-
fileActions,
111-
null,
112-
argv,
113-
envp
114-
)
115-
116-
posix_spawn_file_actions_destroy(fileActions)
117-
118-
if (result != 0) {
106+
val pid = fork()
107+
if (pid < 0) {
119108
close(stdinPipe(0)); close(stdinPipe(1))
120109
close(stdoutPipe(0)); close(stdoutPipe(1))
121110
close(stderrPipe(0)); close(stderrPipe(1))
122-
throw new RuntimeException(s"posix_spawn failed: $result")
123-
}
111+
throw new RuntimeException("fork failed")
112+
} else if (pid == 0) {
113+
close(stdinPipe(1))
114+
close(stdoutPipe(0))
115+
close(stderrPipe(0))
116+
117+
if (
118+
dup2(stdinPipe(0), STDIN_FILENO) == -1 ||
119+
dup2(stdoutPipe(1), STDOUT_FILENO) == -1 ||
120+
dup2(stderrPipe(1), STDERR_FILENO) == -1
121+
) {
122+
_exit(1)
123+
}
124124

125-
close(stdinPipe(0))
126-
close(stdoutPipe(1))
127-
close(stderrPipe(1))
125+
close(stdinPipe(0))
126+
close(stdoutPipe(1))
127+
close(stderrPipe(1))
128128

129-
NativeProcess(
130-
pid = !pid,
131-
stdinFd = stdinPipe(1),
132-
stdoutFd = stdoutPipe(0),
133-
stderrFd = stderrPipe(0)
134-
)
129+
process.workingDirectory.foreach { dir =>
130+
if (chdir(toCString(dir.toString)) != 0) {
131+
_exit(1)
132+
}
133+
}
134+
135+
val allArgs = process.command +: process.args
136+
val argv = stackalloc[CString](allArgs.length.toULong + 1.toULong)
137+
allArgs.zipWithIndex.foreach { case (arg, i) =>
138+
argv(i.toULong) = toCString(arg)
139+
}
140+
argv(allArgs.length.toULong) = null
141+
142+
val envp = stackalloc[CString]((envMap.size + 1).toULong)
143+
envMap.zipWithIndex.foreach { case ((k, v), i) =>
144+
envp(i.toULong) = toCString(s"$k=$v")
145+
}
146+
envp(envMap.size.toULong) = null
147+
148+
execve(toCString(executable), argv, envp)
149+
_exit(1)
150+
throw new RuntimeException(s"execve failed")
151+
} else {
152+
close(stdinPipe(0))
153+
close(stdoutPipe(1))
154+
close(stderrPipe(1))
155+
NativeProcess(
156+
pid = pid,
157+
stdinFd = stdinPipe(1),
158+
stdoutFd = stdoutPipe(0),
159+
stderrFd = stderrPipe(0)
160+
)
161+
}
135162
}
136163
}
137164

@@ -144,7 +171,7 @@ private[process] trait ProcessesCompanionPlatform {
144171
val status = stackalloc[CInt]()
145172
val r = waitpid(proc.pid, status, WNOHANG)
146173
if (r < 0 && errno.errno != ECHILD)
147-
F.raiseError(new RuntimeException(s"waitpid failed: errno=${errno.errno}"))
174+
throw new RuntimeException(s"waitpid failed: errno=${errno.errno}")
148175
()
149176
}
150177

@@ -189,39 +216,59 @@ private[process] trait ProcessesCompanionPlatform {
189216
.to
190217
}
191218
} else {
192-
fallbackExitValue(nativeProcess.pid).to
219+
fallbackExitValue(nativeProcess.pid)
193220
}
194221
}
195222
} else {
196-
fallbackExitValue(nativeProcess.pid).to
223+
fallbackExitValue(nativeProcess.pid)
197224
}
198225

199-
def stdin: Pipe[F, Byte, Nothing] = writeFd(nativeProcess.stdinFd)
200-
226+
def stdin: Pipe[F, Byte, Nothing] = { in =>
227+
in
228+
.through(writeFd(nativeProcess.stdinFd))
229+
.onFinalize {
230+
F.blocking {
231+
close(nativeProcess.stdinFd)
232+
}.void
233+
}
234+
}
201235
def stdout: Stream[F, Byte] = readFd(nativeProcess.stdoutFd, 8192)
236+
.onFinalize {
237+
F.blocking {
238+
close(nativeProcess.stdoutFd)
239+
}.void
240+
}
202241

203242
def stderr: Stream[F, Byte] = readFd(nativeProcess.stderrFd, 8192)
243+
.onFinalize {
244+
F.blocking {
245+
close(nativeProcess.stderrFd)
246+
}.void
247+
}
204248
}
205249
}
206250
}
207251

208-
private def fallbackExitValue(pid: pid_t): IO[Int] = {
209-
def loop: IO[Int] =
210-
IO.blocking {
252+
private def fallbackExitValue(pid: pid_t): F[Int] = {
253+
def loop: F[Int] =
254+
F.delay {
211255
Zone { _ =>
212256
val status = stackalloc[CInt]()
213257
val result = waitpid(pid, status, WNOHANG)
214-
if (result == pid) Some(WEXITSTATUS(!status))
215-
else if (result == 0) None
258+
259+
if (result == pid) {
260+
println("bye bye process")
261+
Some(WEXITSTATUS(!status))
262+
} else if (result == 0) None
216263
else throw new IOException(s"waitpid failed with errno: ${errno.errno}")
217264
}
218265
}.flatMap {
219-
case Some(code) => IO.pure(code)
220-
case None => IO.sleep(100.millis) >> loop
266+
case Some(code) => F.pure(code)
267+
case None => F.sleep(100.millis) >> loop
221268
}
222269

223270
loop.onCancel {
224-
IO.blocking {
271+
F.blocking {
225272
kill(pid, SIGKILL)
226273
()
227274
}
@@ -230,10 +277,3 @@ private[process] trait ProcessesCompanionPlatform {
230277

231278
}
232279
}
233-
234-
case class NativeProcess(
235-
pid: pid_t,
236-
stdinFd: Int,
237-
stdoutFd: Int,
238-
stderrFd: Int
239-
)

0 commit comments

Comments
 (0)