d5f81336b7c0792c71b8acd431dd33eab9c17cc1
[mono.git] / eglib / src / gspawn.c
1 /*
2  * Spawning processes.
3  *
4  * Author:
5  *   Gonzalo Paniagua Javier (gonzalo@novell.com
6  *
7  * (C) 2006 Novell, Inc.
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining
10  * a copy of this software and associated documentation files (the
11  * "Software"), to deal in the Software without restriction, including
12  * without limitation the rights to use, copy, modify, merge, publish,
13  * distribute, sublicense, and/or sell copies of the Software, and to
14  * permit persons to whom the Software is furnished to do so, subject to
15  * the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be
18  * included in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27  */
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <sys/types.h>
33 #ifdef _MSC_VER
34 #include <winsock2.h>
35 #else
36 #include <unistd.h>
37 #include <sys/time.h>
38 #include <sys/wait.h>
39 #endif
40 #include <glib.h>
41
42 #define set_error(msg, ...) do { if (error != NULL) *error = g_error_new (G_LOG_DOMAIN, 1, msg, __VA_ARGS__); } while (0)
43 #define set_error_cond(cond,msg, ...) do { if ((cond) && error != NULL) *error = g_error_new (G_LOG_DOMAIN, 1, msg, __VA_ARGS__); } while (0)
44 #define set_error_status(status,msg, ...) do { if (error != NULL) *error = g_error_new (G_LOG_DOMAIN, status, msg, __VA_ARGS__); } while (0)
45 #define NO_INTR(var,cmd) do { (var) = (cmd); } while ((var) == -1 && errno == EINTR)
46 #define CLOSE_PIPE(p) do { close (p [0]); close (p [1]); } while (0)
47
48 static int
49 safe_read (int fd, gchar *buffer, gint count, GError **error)
50 {
51         int res;
52
53         NO_INTR (res, read (fd, buffer, count));
54         set_error_cond (res == -1, "%s", "Error reading from pipe.");
55         return res;
56 }
57
58 static int
59 read_pipes (int outfd, gchar **out_str, int errfd, gchar **err_str, GError **error)
60 {
61         fd_set rfds;
62         int res;
63         gboolean out_closed;
64         gboolean err_closed;
65         GString *out = NULL;
66         GString *err = NULL;
67         gchar *buffer = NULL;
68         gint nread;
69
70         out_closed = (outfd < 0);
71         err_closed = (errfd < 0);
72         if (out_str) {
73                 *out_str = NULL;
74                 out = g_string_new ("");
75         }       
76
77         if (err_str) {
78                 *err_str = NULL;
79                 err = g_string_new ("");
80         }       
81
82         do {
83                 if (out_closed && err_closed)
84                         break;
85
86                 FD_ZERO (&rfds);
87                 if (!out_closed && outfd >= 0)
88                         FD_SET (outfd, &rfds);
89                 if (!err_closed && errfd >= 0)
90                         FD_SET (errfd, &rfds);
91
92                 res = select (MAX (outfd, errfd) + 1, &rfds, NULL, NULL, NULL);
93                 if (res > 0) {
94                         if (buffer == NULL)
95                                 buffer = g_malloc (1024);
96                         if (!out_closed && FD_ISSET (outfd, &rfds)) {
97                                 nread = safe_read (outfd, buffer, 1024, error);
98                                 if (nread < 0) {
99                                         close (errfd);
100                                         close (outfd);
101                                         return -1;
102                                 }
103                                 g_string_append_len (out, buffer, nread);
104                                 if (nread <= 0) {
105                                         out_closed = TRUE;
106                                         close (outfd);
107                                 }
108                         }
109
110                         if (!err_closed && FD_ISSET (errfd, &rfds)) {
111                                 nread = safe_read (errfd, buffer, 1024, error);
112                                 if (nread < 0) {
113                                         close (errfd);
114                                         close (outfd);
115                                         return -1;
116                                 }
117                                 g_string_append_len (err, buffer, nread);
118                                 if (nread <= 0) {
119                                         err_closed = TRUE;
120                                         close (errfd);
121                                 }
122                         }
123                 }
124         } while (res > 0 || (res == -1 && errno == EINTR));
125
126         g_free (buffer);
127         if (out_str)
128                 *out_str = g_string_free (out, FALSE);
129
130         if (err_str)
131                 *err_str = g_string_free (err, FALSE);
132
133         return 0;
134 }
135
136 static gboolean
137 create_pipe (int *fds, GError **error)
138 {
139         if (pipe (fds) == -1) {
140                 set_error ("%s", "Error creating pipe.");
141                 return FALSE;
142         }
143         return TRUE;
144 }
145
146 gboolean
147 g_spawn_command_line_sync (const gchar *command_line,
148                                 gchar **standard_output,
149                                 gchar **standard_error,
150                                 gint *exit_status,
151                                 GError **error)
152 {
153         pid_t pid;
154         gchar **argv;
155         gint argc;
156         int stdout_pipe [2] = { -1, -1 };
157         int stderr_pipe [2] = { -1, -1 };
158         int status;
159         int res;
160         
161         if (!g_shell_parse_argv (command_line, &argc, &argv, error))
162                 return FALSE;
163
164         if (standard_output && !create_pipe (stdout_pipe, error))
165                 return FALSE;
166
167         if (standard_error && !create_pipe (stderr_pipe, error)) {
168                 if (standard_output) {
169                         CLOSE_PIPE (stdout_pipe);
170                 }
171                 return FALSE;
172         }
173
174         pid = fork ();
175         if (pid == 0) {
176                 gint i;
177
178                 if (standard_output) {
179                         close (stdout_pipe [0]);
180                         dup2 (stdout_pipe [1], STDOUT_FILENO);
181                 }
182
183                 if (standard_error) {
184                         close (stderr_pipe [0]);
185                         dup2 (stderr_pipe [1], STDERR_FILENO);
186                 }
187                 for (i = getdtablesize () - 1; i >= 3; i--)
188                         close (i);
189
190                 /* G_SPAWN_SEARCH_PATH is always enabled for g_spawn_command_line_sync */
191                 if (!g_path_is_absolute (argv [0])) {
192                         gchar *arg0;
193
194                         arg0 = g_find_program_in_path (argv [0]);
195                         if (arg0 == NULL) {
196                                 exit (1);
197                         }
198                         //g_free (argv [0]);
199                         argv [0] = arg0;
200                 }
201                 execv (argv [0], argv);
202                 exit (1); /* TODO: What now? */
203         }
204
205         g_strfreev (argv);
206         if (standard_output)
207                 close (stdout_pipe [1]);
208
209         if (standard_error)
210                 close (stderr_pipe [1]);
211
212         if (standard_output || standard_error) {
213                 res = read_pipes (stdout_pipe [0], standard_output, stderr_pipe [0], standard_error, error);
214                 if (res) {
215                         waitpid (pid, &status, WNOHANG); /* avoid zombie */
216                         return FALSE;
217                 }
218         }
219
220         NO_INTR (res, waitpid (pid, &status, 0));
221
222         /* TODO: What if error? */
223         if (WIFEXITED (status) && exit_status) {
224                 *exit_status = WEXITSTATUS (status);
225         }
226
227         return TRUE;
228 }
229
230 /*
231  * This is the only use we have in mono/metadata
232 !g_spawn_async_with_pipes (NULL, (char**)addr_argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, &child_pid, &ch_in, &ch_out, NULL, NULL)
233 */
234 gboolean
235 g_spawn_async_with_pipes (const gchar *working_directory,
236                         gchar **argv,
237                         gchar **envp,
238                         GSpawnFlags flags,
239                         GSpawnChildSetupFunc child_setup,
240                         gpointer user_data,
241                         GPid *child_pid,
242                         gint *standard_input,
243                         gint *standard_output,
244                         gint *standard_error,
245                         GError **error)
246 {
247         pid_t pid;
248         int info_pipe [2];
249         int in_pipe [2] = { -1, -1 };
250         int out_pipe [2] = { -1, -1 };
251         int err_pipe [2] = { -1, -1 };
252         int status;
253
254         g_return_val_if_fail (argv != NULL, FALSE); /* Only mandatory arg */
255
256         if (!create_pipe (info_pipe, error))
257                 return FALSE;
258
259         if (standard_output && !create_pipe (out_pipe, error)) {
260                 CLOSE_PIPE (info_pipe);
261                 return FALSE;
262         }
263
264         if (standard_error && !create_pipe (err_pipe, error)) {
265                 CLOSE_PIPE (info_pipe);
266                 CLOSE_PIPE (out_pipe);
267                 return FALSE;
268         }
269
270         if (standard_input && !create_pipe (in_pipe, error)) {
271                 CLOSE_PIPE (info_pipe);
272                 CLOSE_PIPE (out_pipe);
273                 CLOSE_PIPE (err_pipe);
274                 return FALSE;
275         }
276
277         pid = fork ();
278         if (pid == -1) {
279                 CLOSE_PIPE (info_pipe);
280                 CLOSE_PIPE (out_pipe);
281                 CLOSE_PIPE (err_pipe);
282                 CLOSE_PIPE (in_pipe);
283                 set_error ("%s", "Error in fork ()");
284                 return FALSE;
285         }
286
287         if (pid == 0) {
288                 /* No zombie left behind */
289                 if ((flags & G_SPAWN_DO_NOT_REAP_CHILD) == 0) {
290                         pid = fork ();
291                 }
292
293                 if (pid != 0) {
294                         exit (pid == -1 ? 1 : 0);
295                 }  else {
296                         gint i;
297                         int fd;
298                         gchar *arg0;
299                         gchar **actual_args;
300                         gint unused;
301
302                         close (info_pipe [0]);
303                         close (in_pipe [1]);
304                         close (out_pipe [0]);
305                         close (err_pipe [0]);
306
307                         /* when exec* succeeds, we want to close this fd, which will return
308                          * a 0 read on the parent. We're not supposed to keep it open forever.
309                          * If exec fails, we still can write the error to it before closing.
310                          */
311                         fcntl (info_pipe [1], F_SETFD, FD_CLOEXEC);
312
313                         if ((flags & G_SPAWN_DO_NOT_REAP_CHILD) == 0) {
314                                 pid = getpid ();
315                                 NO_INTR (unused, write (info_pipe [1], &pid, sizeof (pid_t)));
316                         }
317
318                         if (working_directory && chdir (working_directory) == -1) {
319                                 int err = errno;
320                                 NO_INTR (unused, write (info_pipe [1], &err, sizeof (int)));
321                                 exit (0);
322                         }
323
324                         if (standard_output) {
325                                 dup2 (out_pipe [1], STDOUT_FILENO);
326                         } else if ((flags & G_SPAWN_STDOUT_TO_DEV_NULL) != 0) {
327                                 fd = open ("/dev/null", O_WRONLY);
328                                 dup2 (fd, STDOUT_FILENO);
329                         }
330
331                         if (standard_error) {
332                                 dup2 (err_pipe [1], STDERR_FILENO);
333                         } else if ((flags & G_SPAWN_STDERR_TO_DEV_NULL) != 0) {
334                                 fd = open ("/dev/null", O_WRONLY);
335                                 dup2 (fd, STDERR_FILENO);
336                         }
337
338                         if (standard_input) {
339                                 dup2 (in_pipe [0], STDERR_FILENO);
340                         } else if ((flags & G_SPAWN_CHILD_INHERITS_STDIN) == 0) {
341                                 fd = open ("/dev/null", O_RDONLY);
342                                 dup2 (fd, STDERR_FILENO);
343                         }
344
345                         if ((flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN) != 0) {
346                                 for (i = getdtablesize () - 1; i >= 3; i--)
347                                         close (i);
348                         }
349
350                         actual_args = ((flags & G_SPAWN_FILE_AND_ARGV_ZERO) == 0) ? argv : argv + 1;
351                         if (envp == NULL)
352                                 envp = environ;
353
354                         if (child_setup)
355                                 child_setup (user_data);
356
357                         arg0 = argv [0];
358                         if (!g_path_is_absolute (arg0) || (flags & G_SPAWN_SEARCH_PATH) != 0) {
359                                 arg0 = g_find_program_in_path (argv [0]);
360                                 if (arg0 == NULL) {
361                                         int err = ENOENT;
362                                         write (info_pipe [1], &err, sizeof (int));
363                                         exit (0);
364                                 }
365                         }
366
367                         execve (arg0, actual_args, envp);
368                         write (info_pipe [1], &errno, sizeof (int));
369                         exit (0);
370                 }
371         } else if ((flags & G_SPAWN_DO_NOT_REAP_CHILD) == 0) {
372                 int w;
373                 /* Wait for the first child if two are created */
374                 NO_INTR (w, waitpid (pid, &status, 0));
375                 if (status == 1 || w == -1) {
376                         CLOSE_PIPE (info_pipe);
377                         CLOSE_PIPE (out_pipe);
378                         CLOSE_PIPE (err_pipe);
379                         CLOSE_PIPE (in_pipe);
380                         set_error ("Error in fork (): %d", status);
381                         return FALSE;
382                 }
383         }
384         close (info_pipe [1]);
385         close (in_pipe [0]);
386         close (out_pipe [1]);
387         close (err_pipe [1]);
388
389         if ((flags & G_SPAWN_DO_NOT_REAP_CHILD) == 0) {
390                 int x;
391                 NO_INTR (x, read (info_pipe [0], &pid, sizeof (pid_t))); /* if we read < sizeof (pid_t)... */
392         }
393
394         if (child_pid) {
395                 *child_pid = pid;
396         }
397
398         if (read (info_pipe [0], &status, sizeof (int)) != 0) {
399                 close (info_pipe [0]);
400                 close (in_pipe [0]);
401                 close (out_pipe [1]);
402                 close (err_pipe [1]);
403                 set_error_status (status, "Error in exec (%d -> %s)", status, strerror (status));
404                 return FALSE;
405         }
406
407         close (info_pipe [0]);
408         if (standard_input)
409                 *standard_input = in_pipe [1];
410         if (standard_output)
411                 *standard_output = out_pipe [0];
412         if (standard_error)
413                 *standard_error = err_pipe [0];
414
415         return TRUE;
416 }
417