8 /* This code is based on popen() and pclose() from Darwin's
9 libc. The only functional difference is that sc_popen()
10 places the new process' pid at the pidp address. sc_pclose()
11 takes this pid which allows the code to be simplified
12 (maintaining a global linked list of fds to pids and locking
13 it is no longer necessary). */
15 #include <sys/cdefs.h>
16 #include <sys/param.h>
23 // allows for linking into a dylib on darwin
24 #if defined(__APPLE__) && !defined(SC_IPHONE)
25 #include <crt_externs.h>
26 #define environ (*_NSGetEnviron())
28 extern char **environ
;
32 sc_popen(const char *command
, pid_t
*pidp
, const char *type
)
35 int pdes
[2], pid
, twoway
;
39 * Lite2 introduced two-way popen() pipes using _socketpair().
40 * FreeBSD's pipe() is bidirectional, so we use that.
42 if (strchr(type
, '+')) {
47 if ((*type
!= 'r' && *type
!= 'w') || type
[1])
53 argv
[0] = (char *)"sh";
54 argv
[1] = (char *)"-c";
55 argv
[2] = (char *)command
;
58 switch (pid
= fork()) {
67 * The _dup2() to STDIN_FILENO is repeated to avoid
68 * writing to pdes[1], which might corrupt the
69 * parent's copy. This isn't good enough in
70 * general, since the _exit() is no return, so
71 * the compiler is free to corrupt all the local
75 if (pdes
[1] != STDOUT_FILENO
) {
76 (void)dup2(pdes
[1], STDOUT_FILENO
);
79 (void)dup2(STDOUT_FILENO
, STDIN_FILENO
);
80 } else if (twoway
&& (pdes
[1] != STDIN_FILENO
))
81 (void)dup2(pdes
[1], STDIN_FILENO
);
83 if (pdes
[0] != STDIN_FILENO
) {
84 (void)dup2(pdes
[0], STDIN_FILENO
);
90 execve("/bin/sh", argv
, environ
);
95 /* Parent; assume fdopen can't fail. */
97 iop
= fdopen(pdes
[0], type
);
100 iop
= fdopen(pdes
[1], type
);
101 (void)close(pdes
[0]);
110 * Pclose returns -1 if stream is not associated with a `popened' command,
111 * if already `pclosed', or waitpid returns an error.
114 sc_pclose(FILE *iop
, pid_t mPid
)
122 pid
= wait4(mPid
, &pstat
, 0, (struct rusage
*)0);
123 } while (pid
== -1 && errno
== EINTR
);
125 return (pid
== -1 ? -1 : pstat
);
135 /* The process handle allows us to get the exit code after
136 the process has died. It must be closed in sc_pclose;
137 just having the pid as in the unix version is not enough.
138 Thus this global linked list needs to be maintained and
139 access to it needs to be locked. */
141 static struct process
{
142 struct process
*next
;
146 static pthread_mutex_t processlist_mutex
= PTHREAD_MUTEX_INITIALIZER
;
148 #define THREAD_LOCK() pthread_mutex_lock(&processlist_mutex)
149 #define THREAD_UNLOCK() pthread_mutex_unlock(&processlist_mutex)
152 sc_popen(const char *cmd
, pid_t
*pid
, const char *mode
)
155 PROCESS_INFORMATION pi
;
158 HANDLE child_in
, child_out
;
159 HANDLE father_in
, father_out
;
160 HANDLE father_in_dup
, father_out_dup
;
163 char *new_cmd
= NULL
;
165 BOOL read_mode
, write_mode
;
171 new_cmd
= (char *)malloc(strlen(cmd
) + 10);
172 sprintf(new_cmd
, "cmd /c %s", cmd
);
174 current_pid
= GetCurrentProcess();
176 ZeroMemory( &si
, sizeof(STARTUPINFO
) );
177 si
.cb
= sizeof(STARTUPINFO
);
178 si
.dwFlags
= STARTF_USESTDHANDLES
| STARTF_USESHOWWINDOW
;
179 si
.wShowWindow
= SW_HIDE
;
180 si
.hStdInput
= GetStdHandle(STD_INPUT_HANDLE
);
181 si
.hStdOutput
= GetStdHandle(STD_OUTPUT_HANDLE
);
182 si
.hStdError
= GetStdHandle(STD_ERROR_HANDLE
);
184 binary_mode
= (strchr(mode
, 'b') ? _O_BINARY
: _O_TEXT
);
186 read_mode
= (strchr(mode
, 'r') != 0);
187 write_mode
= (strchr(mode
, 'w') != 0);
189 /* Opening the pipe for writing */
191 binary_mode
|= _O_WRONLY
;
192 if (CreatePipe(&child_in
, &father_out
, NULL
, 0) == FALSE
) {
193 fprintf(stderr
, "popen: error CreatePipe\n");
198 if (DuplicateHandle(current_pid
, child_in
,
199 current_pid
, &father_in_dup
,
200 0, TRUE
, DUPLICATE_SAME_ACCESS
) == FALSE
) {
201 fprintf(stderr
, "popen: error DuplicateHandle father_out\n");
205 si
.hStdInput
= father_in_dup
;
207 CloseHandle(child_in
);
208 fno
= _open_osfhandle((size_t)father_out
, binary_mode
);
209 f
= _fdopen(fno
, mode
);
211 /* Opening the pipe for reading */
212 else if (read_mode
) {
213 binary_mode
|= _O_RDONLY
;
214 if (CreatePipe(&father_in
, &child_out
, NULL
, 0) == FALSE
) {
215 fprintf(stderr
, "popen: error CreatePipe\n");
219 if (DuplicateHandle(current_pid
, child_out
,
220 current_pid
, &father_out_dup
,
221 0, TRUE
, DUPLICATE_SAME_ACCESS
) == FALSE
) {
222 fprintf(stderr
, "popen: error DuplicateHandle father_in\n");
226 CloseHandle(child_out
);
227 si
.hStdOutput
= father_out_dup
;
228 fno
= _open_osfhandle((size_t)father_in
, binary_mode
);
229 f
= _fdopen(fno
, mode
);
232 fprintf(stderr
, "popen: invalid mode %s\n", mode
);
237 /* creating child process */
238 if (CreateProcess(NULL
, /* pointer to name of executable module */
239 new_cmd
, /* pointer to command line string */
240 NULL
, /* pointer to process security attributes */
241 NULL
, /* pointer to thread security attributes */
242 TRUE
, /* handle inheritance flag */
243 0, /* creation flags */
244 NULL
, /* pointer to environment */
245 NULL
, /* pointer to current directory */
246 &si
, /* pointer to STARTUPINFO */
247 &pi
/* pointer to PROCESS_INFORMATION */
249 fprintf(stderr
, "popen: CreateProcess %x\n", GetLastError());
255 /* Only the process handle is needed, ignore errors */
256 CloseHandle(pi
.hThread
);
258 /* Closing the unnecessary part of the pipe */
260 CloseHandle(father_out_dup
);
262 CloseHandle(father_in_dup
);
264 if ((cur
= (struct process
*)malloc(sizeof(struct process
))) == NULL
) {
267 CloseHandle(pi
.hProcess
);
272 cur
->handle
= pi
.hProcess
;
274 cur
->next
= processlist
;
278 if (new_cmd
) free(new_cmd
);
280 *pid
= pi
.dwProcessId
;
286 sc_pclose(FILE *f
, pid_t pid
)
288 struct process
*cur
, *last
;
292 for (last
= NULL
, cur
= processlist
; cur
; last
= cur
, cur
= cur
->next
)
300 processlist
= cur
->next
;
302 last
->next
= cur
->next
;
305 if (WaitForSingleObject(cur
->handle
, INFINITE
) != WAIT_OBJECT_0
) {
306 fprintf(stderr
, "sc_pclose: error, process still active\n");
311 if (GetExitCodeProcess(cur
->handle
, (LPDWORD
)&exit_code
) == 0) {
312 fprintf(stderr
, "sc_pclose: can't get process exit code\n");
319 if (CloseHandle(cur
->handle
) == FALSE
) {
320 fprintf(stderr
, "sc_pclose: error closing process handle\n");