• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===-- Linux implementation of posix_spawn -------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "src/spawn/posix_spawn.h"
10 
11 #include "src/__support/CPP/optional.h"
12 #include "src/__support/OSUtil/syscall.h" // For internal syscall function.
13 #include "src/__support/common.h"
14 #include "src/spawn/file_actions.h"
15 
16 #include <fcntl.h>
17 #include <signal.h> // For SIGCHLD
18 #include <spawn.h>
19 #include <sys/syscall.h> // For syscall numbers.
20 
21 namespace LIBC_NAMESPACE {
22 
23 namespace {
24 
fork()25 pid_t fork() {
26   // TODO: Use only the clone syscall and use a sperate small stack in the child
27   // to avoid duplicating the complete stack from the parent. A new stack will
28   // be created on exec anyway so duplicating the full stack is unnecessary.
29 #ifdef SYS_fork
30   return LIBC_NAMESPACE::syscall_impl<pid_t>(SYS_fork);
31 #elif defined(SYS_clone)
32   return LIBC_NAMESPACE::syscall_impl<pid_t>(SYS_clone, SIGCHLD, 0);
33 #else
34 #error "fork or clone syscalls not available."
35 #endif
36 }
37 
open(const char * path,int oflags,mode_t mode)38 cpp::optional<int> open(const char *path, int oflags, mode_t mode) {
39 #ifdef SYS_open
40   int fd = LIBC_NAMESPACE::syscall_impl<int>(SYS_open, path, oflags, mode);
41 #else
42   int fd = LIBC_NAMESPACE::syscall_impl<int>(SYS_openat, AT_FDCWD, path, oflags,
43                                              mode);
44 #endif
45   if (fd > 0)
46     return fd;
47   // The open function is called as part of the child process' preparatory
48   // steps. If an open fails, the child process just exits. So, unlike
49   // the public open function, we do not need to set errno here.
50   return cpp::nullopt;
51 }
52 
close(int fd)53 void close(int fd) { LIBC_NAMESPACE::syscall_impl<long>(SYS_close, fd); }
54 
55 // We use dup3 if dup2 is not available, similar to our implementation of dup2
dup2(int fd,int newfd)56 bool dup2(int fd, int newfd) {
57 #ifdef SYS_dup2
58   int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_dup2, fd, newfd);
59 #elif defined(SYS_dup3)
60   int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_dup3, fd, newfd, 0);
61 #else
62 #error "dup2 and dup3 syscalls not available."
63 #endif
64   return ret < 0 ? false : true;
65 }
66 
67 // All exits from child_process are error exits. So, we use a simple
68 // exit implementation which exits with code 127.
exit()69 void exit() {
70   for (;;) {
71     LIBC_NAMESPACE::syscall_impl<long>(SYS_exit_group, 127);
72     LIBC_NAMESPACE::syscall_impl<long>(SYS_exit, 127);
73   }
74 }
75 
child_process(const char * __restrict path,const posix_spawn_file_actions_t * file_actions,const posix_spawnattr_t * __restrict,char * const * __restrict argv,char * const * __restrict envp)76 void child_process(const char *__restrict path,
77                    const posix_spawn_file_actions_t *file_actions,
78                    const posix_spawnattr_t *__restrict, // For now unused
79                    char *const *__restrict argv, char *const *__restrict envp) {
80   // TODO: In the code below, the child_process just exits on error during
81   // processing |file_actions| and |attr|. The correct way would be to exit
82   // after conveying the information about the failure to the parent process
83   // (via a pipe for example).
84   // TODO: Handle |attr|.
85 
86   if (file_actions != nullptr) {
87     auto *act = reinterpret_cast<BaseSpawnFileAction *>(file_actions->__front);
88     while (act != nullptr) {
89       switch (act->type) {
90       case BaseSpawnFileAction::OPEN: {
91         auto *open_act = reinterpret_cast<SpawnFileOpenAction *>(act);
92         auto fd = open(open_act->path, open_act->oflag, open_act->mode);
93         if (!fd)
94           exit();
95         int actual_fd = *fd;
96         if (actual_fd != open_act->fd) {
97           bool dup2_result = dup2(actual_fd, open_act->fd);
98           close(actual_fd); // The old fd is not needed anymore.
99           if (!dup2_result)
100             exit();
101         }
102         break;
103       }
104       case BaseSpawnFileAction::CLOSE: {
105         auto *close_act = reinterpret_cast<SpawnFileCloseAction *>(act);
106         close(close_act->fd);
107         break;
108       }
109       case BaseSpawnFileAction::DUP2: {
110         auto *dup2_act = reinterpret_cast<SpawnFileDup2Action *>(act);
111         if (!dup2(dup2_act->fd, dup2_act->newfd))
112           exit();
113         break;
114       }
115       }
116       act = act->next;
117     }
118   }
119 
120   if (LIBC_NAMESPACE::syscall_impl<long>(SYS_execve, path, argv, envp) < 0)
121     exit();
122 }
123 
124 } // anonymous namespace
125 
126 LLVM_LIBC_FUNCTION(int, posix_spawn,
127                    (pid_t *__restrict pid, const char *__restrict path,
128                     const posix_spawn_file_actions_t *file_actions,
129                     const posix_spawnattr_t *__restrict attr,
130                     char *const *__restrict argv,
131                     char *const *__restrict envp)) {
132   pid_t cpid = fork();
133   if (cpid == 0)
134     child_process(path, file_actions, attr, argv, envp);
135   else if (cpid < 0)
136     return -cpid;
137 
138   if (pid != nullptr)
139     *pid = cpid;
140 
141   // TODO: Before returning, one should wait for the child_process to startup
142   // successfully. For now, we will just return. Future changes will add proper
143   // wait (using pipes for example).
144 
145   return 0;
146 }
147 
148 } // namespace LIBC_NAMESPACE
149