SCDoc: Use proper static string constants instead of comparing string literals.
[supercollider.git] / common / sc_popen.cpp
blob05c457ba0f7114985c3f80f0754d3fc660c6da28
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include "sc_popen.h"
6 #ifndef _WIN32
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>
17 #include <sys/wait.h>
18 #include <signal.h>
19 #include <errno.h>
20 #include <unistd.h>
21 #include <paths.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())
27 #else
28 extern char **environ;
29 #endif
31 FILE *
32 sc_popen(const char *command, pid_t *pidp, const char *type)
34 FILE *iop;
35 int pdes[2], pid, twoway;
36 char *argv[4];
39 * Lite2 introduced two-way popen() pipes using _socketpair().
40 * FreeBSD's pipe() is bidirectional, so we use that.
42 if (strchr(type, '+')) {
43 twoway = 1;
44 type = "r+";
45 } else {
46 twoway = 0;
47 if ((*type != 'r' && *type != 'w') || type[1])
48 return (NULL);
50 if (pipe(pdes) < 0)
51 return (NULL);
53 argv[0] = (char *)"sh";
54 argv[1] = (char *)"-c";
55 argv[2] = (char *)command;
56 argv[3] = NULL;
58 switch (pid = fork()) {
59 case -1: /* Error. */
60 (void)close(pdes[0]);
61 (void)close(pdes[1]);
62 return (NULL);
63 /* NOTREACHED */
64 case 0: /* Child. */
65 if (*type == 'r') {
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
72 * variables.
74 (void)close(pdes[0]);
75 if (pdes[1] != STDOUT_FILENO) {
76 (void)dup2(pdes[1], STDOUT_FILENO);
77 (void)close(pdes[1]);
78 if (twoway)
79 (void)dup2(STDOUT_FILENO, STDIN_FILENO);
80 } else if (twoway && (pdes[1] != STDIN_FILENO))
81 (void)dup2(pdes[1], STDIN_FILENO);
82 } else {
83 if (pdes[0] != STDIN_FILENO) {
84 (void)dup2(pdes[0], STDIN_FILENO);
85 (void)close(pdes[0]);
87 (void)close(pdes[1]);
90 execve("/bin/sh", argv, environ);
91 exit(127);
92 /* NOTREACHED */
95 /* Parent; assume fdopen can't fail. */
96 if (*type == 'r') {
97 iop = fdopen(pdes[0], type);
98 (void)close(pdes[1]);
99 } else {
100 iop = fdopen(pdes[1], type);
101 (void)close(pdes[0]);
104 *pidp = pid;
105 return iop;
109 * pclose --
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)
116 int pstat = 0;
117 pid_t pid;
119 (void)fclose(iop);
121 do {
122 pid = wait4(mPid, &pstat, 0, (struct rusage *)0);
123 } while (pid == -1 && errno == EINTR);
125 return (pid == -1 ? -1 : pstat);
128 #else
130 #include <windows.h>
131 #include <fcntl.h>
132 #include <io.h>
133 #include <errno.h>
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;
143 FILE *fp;
144 HANDLE handle;
145 } *processlist;
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)
151 FILE *
152 sc_popen(const char *cmd, pid_t *pid, const char *mode)
154 STARTUPINFO si;
155 PROCESS_INFORMATION pi;
156 FILE *f = NULL;
157 int fno;
158 HANDLE child_in, child_out;
159 HANDLE father_in, father_out;
160 HANDLE father_in_dup, father_out_dup;
161 HANDLE current_pid;
162 int binary_mode;
163 char *new_cmd = NULL;
164 struct process *cur;
165 BOOL read_mode, write_mode;
167 if (cmd == NULL) {
168 return NULL;
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 */
190 if (write_mode) {
191 binary_mode |= _O_WRONLY;
192 if (CreatePipe(&child_in, &father_out, NULL, 0) == FALSE) {
193 fprintf(stderr, "popen: error CreatePipe\n");
194 free(new_cmd);
195 return NULL;
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");
202 free(new_cmd);
203 return NULL;
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");
216 free(new_cmd);
217 return NULL;
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");
223 free(new_cmd);
224 return NULL;
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);
231 else {
232 fprintf(stderr, "popen: invalid mode %s\n", mode);
233 free(new_cmd);
234 return NULL;
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 */
248 ) == FALSE) {
249 fprintf(stderr, "popen: CreateProcess %x\n", GetLastError());
250 free(new_cmd);
251 fclose(f);
252 return NULL;
255 /* Only the process handle is needed, ignore errors */
256 CloseHandle(pi.hThread);
258 /* Closing the unnecessary part of the pipe */
259 if (read_mode)
260 CloseHandle(father_out_dup);
261 else if (write_mode)
262 CloseHandle(father_in_dup);
264 if ((cur = (struct process *)malloc(sizeof(struct process))) == NULL) {
265 free(new_cmd);
266 fclose(f);
267 CloseHandle(pi.hProcess);
268 return NULL;
271 cur->fp = f;
272 cur->handle = pi.hProcess;
273 THREAD_LOCK();
274 cur->next = processlist;
275 processlist = cur;
276 THREAD_UNLOCK();
278 if (new_cmd) free(new_cmd);
280 *pid = pi.dwProcessId;
282 return f;
286 sc_pclose(FILE *f, pid_t pid)
288 struct process *cur, *last;
289 int exit_code;
291 THREAD_LOCK();
292 for (last = NULL, cur = processlist; cur; last = cur, cur = cur->next)
293 if (cur->fp == f)
294 break;
295 if (cur == NULL) {
296 THREAD_UNLOCK();
297 return (-1);
299 if (last == NULL)
300 processlist = cur->next;
301 else
302 last->next = cur->next;
303 THREAD_UNLOCK();
305 if (WaitForSingleObject(cur->handle, INFINITE) != WAIT_OBJECT_0) {
306 fprintf(stderr, "sc_pclose: error, process still active\n");
307 free(cur);
308 return -1;
311 if (GetExitCodeProcess(cur->handle, (LPDWORD)&exit_code) == 0) {
312 fprintf(stderr, "sc_pclose: can't get process exit code\n");
313 free(cur);
314 return -1;
317 fclose(cur->fp);
319 if (CloseHandle(cur->handle) == FALSE) {
320 fprintf(stderr, "sc_pclose: error closing process handle\n");
321 free(cur);
322 return -1;
325 free(cur);
327 return exit_code;
330 #endif