New test.
[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 #define _GNU_SOURCE
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <errno.h>
32 #include <unistd.h>
33 #include <unistd.h>
34 #include <fcntl.h>
35 #include <sys/time.h>
36 #include <sys/wait.h>
37 #include <sys/types.h>
38 #include <glib.h>
39
40 #define set_error(msg...) do { if (error != NULL) *error = g_error_new (G_LOG_DOMAIN, 1, msg); } while (0)
41 #define set_error_cond(cond,msg...) do { if ((cond) && error != NULL) *error = g_error_new (G_LOG_DOMAIN, 1, msg); } while (0)
42 #define set_error_status(status,msg...) do { if (error != NULL) *error = g_error_new (G_LOG_DOMAIN, status, msg); } while (0)
43 #define NO_INTR(var,cmd) do { (var) = (cmd); } while ((var) == -1 && errno == EINTR)
44 #define CLOSE_PIPE(p) do { close (p [0]); close (p [1]); } while (0)
45
46 static int
47 safe_read (int fd, gchar *buffer, gint count, GError **error)
48 {
49         int res;
50
51         NO_INTR (res, read (fd, buffer, count));
52         set_error_cond (res == -1, "Error reading from pipe.");
53         return res;
54 }
55
56 static int
57 read_pipes (int outfd, gchar **out_str, int errfd, gchar **err_str, GError **error)
58 {
59         fd_set rfds;
60         int res;
61         gboolean out_closed;
62         gboolean err_closed;
63         GString *out = NULL;
64         GString *err = NULL;
65         gchar *buffer = NULL;
66         gint nread;
67
68         out_closed = (outfd < 0);
69         err_closed = (errfd < 0);
70         if (out_str) {
71                 *out_str = NULL;
72                 out = g_string_new ("");
73         }       
74
75         if (err_str) {
76                 *err_str = NULL;
77                 err = g_string_new ("");
78         }       
79
80         do {
81                 if (out_closed && err_closed)
82                         break;
83
84                 FD_ZERO (&rfds);
85                 if (!out_closed && outfd >= 0)
86                         FD_SET (outfd, &rfds);
87                 if (!err_closed && errfd >= 0)
88                         FD_SET (errfd, &rfds);
89
90                 res = select (MAX (outfd, errfd) + 1, &rfds, NULL, NULL, NULL);
91                 if (res > 0) {
92                         if (buffer == NULL)
93                                 buffer = g_malloc (1024);
94                         if (!out_closed && FD_ISSET (outfd, &rfds)) {
95                                 nread = safe_read (outfd, buffer, 1024, error);
96                                 if (nread < 0) {
97                                         close (errfd);
98                                         close (outfd);
99                                         return -1;
100                                 }
101                                 g_string_append_len (out, buffer, nread);
102                                 if (nread <= 0) {
103                                         out_closed = TRUE;
104                                         close (outfd);
105                                 }
106                         }
107
108                         if (!err_closed && FD_ISSET (errfd, &rfds)) {
109                                 nread = safe_read (errfd, buffer, 1024, error);
110                                 if (nread < 0) {
111                                         close (errfd);
112                                         close (outfd);
113                                         return -1;
114                                 }
115                                 g_string_append_len (err, buffer, nread);
116                                 if (nread <= 0) {
117                                         err_closed = TRUE;
118                                         close (errfd);
119                                 }
120                         }
121                 }
122         } while (res > 0 || (res == -1 && errno == EINTR));
123
124         g_free (buffer);
125         if (out_str)
126                 *out_str = g_string_free (out, FALSE);
127
128         if (err_str)
129                 *err_str = g_string_free (err, FALSE);
130
131         return 0;
132 }
133
134 static gboolean
135 create_pipe (int *fds, GError **error)
136 {
137         if (pipe (fds) == -1) {
138                 set_error ("Error creating pipe.");
139                 return FALSE;
140         }
141         return TRUE;
142 }
143
144 gboolean
145 g_spawn_command_line_sync (const gchar *command_line,
146                                 gchar **standard_output,
147                                 gchar **standard_error,
148                                 gint *exit_status,
149                                 GError **error)
150 {
151         pid_t pid;
152         gchar **argv;
153         gint argc;
154         int stdout_pipe [2] = { -1, -1 };
155         int stderr_pipe [2] = { -1, -1 };
156         int status;
157         int res;
158         
159         if (!g_shell_parse_argv (command_line, &argc, &argv, error))
160                 return FALSE;
161
162         if (standard_output && !create_pipe (stdout_pipe, error))
163                 return FALSE;
164
165         if (standard_error && !create_pipe (stderr_pipe, error)) {
166                 if (standard_output) {
167                         CLOSE_PIPE (stdout_pipe);
168                 }
169                 return FALSE;
170         }
171
172         pid = fork ();
173         if (pid == 0) {
174                 gint i;
175
176                 if (standard_output) {
177                         close (stdout_pipe [0]);
178                         dup2 (stdout_pipe [1], STDOUT_FILENO);
179                 }
180
181                 if (standard_error) {
182                         close (stderr_pipe [0]);
183                         dup2 (stderr_pipe [1], STDERR_FILENO);
184                 }
185                 for (i = getdtablesize () - 1; i >= 3; i--)
186                         close (i);
187
188                 execv (argv [0], argv);
189                 exit (1); /* TODO: What now? */
190         }
191
192         g_strfreev (argv);
193         if (standard_output)
194                 close (stdout_pipe [1]);
195
196         if (standard_error)
197                 close (stderr_pipe [1]);
198
199         if (standard_output || standard_error) {
200                 res = read_pipes (stdout_pipe [0], standard_output, stderr_pipe [0], standard_error, error);
201                 if (res) {
202                         waitpid (pid, &status, WNOHANG); /* avoid zombie */
203                         return FALSE;
204                 }
205         }
206
207         NO_INTR (res, waitpid (pid, &status, 0));
208
209         /* TODO: What if error? */
210         if (WIFEXITED (status) && exit_status) {
211                 *exit_status = WEXITSTATUS (status);
212         }
213
214         return TRUE;
215 }
216
217 /*
218  * This is the only use we have in mono/metadata
219 !g_spawn_async_with_pipes (NULL, (char**)addr_argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, &child_pid, &ch_in, &ch_out, NULL, NULL)
220 */
221 gboolean
222 g_spawn_async_with_pipes (const gchar *working_directory,
223                         gchar **argv,
224                         gchar **envp,
225                         GSpawnFlags flags,
226                         GSpawnChildSetupFunc child_setup,
227                         gpointer user_data,
228                         GPid *child_pid,
229                         gint *standard_input,
230                         gint *standard_output,
231                         gint *standard_error,
232                         GError **error)
233 {
234         pid_t pid;
235         int info_pipe [2];
236         int in_pipe [2] = { -1, -1 };
237         int out_pipe [2] = { -1, -1 };
238         int err_pipe [2] = { -1, -1 };
239         int status;
240
241         g_return_val_if_fail (argv != NULL, FALSE); /* Only mandatory arg */
242
243         if (!create_pipe (info_pipe, error))
244                 return FALSE;
245
246         if (standard_output && !create_pipe (out_pipe, error)) {
247                 CLOSE_PIPE (info_pipe);
248                 return FALSE;
249         }
250
251         if (standard_error && !create_pipe (err_pipe, error)) {
252                 CLOSE_PIPE (info_pipe);
253                 CLOSE_PIPE (out_pipe);
254                 return FALSE;
255         }
256
257         if (standard_input && !create_pipe (in_pipe, error)) {
258                 CLOSE_PIPE (info_pipe);
259                 CLOSE_PIPE (out_pipe);
260                 CLOSE_PIPE (err_pipe);
261                 return FALSE;
262         }
263
264         pid = fork ();
265         if (pid == -1) {
266                 CLOSE_PIPE (info_pipe);
267                 CLOSE_PIPE (out_pipe);
268                 CLOSE_PIPE (err_pipe);
269                 CLOSE_PIPE (in_pipe);
270                 set_error ("Error in fork ()");
271                 return FALSE;
272         }
273
274         if (pid == 0) {
275                 /* No zombie left behind */
276                 if ((flags & G_SPAWN_DO_NOT_REAP_CHILD) == 0) {
277                         pid = fork ();
278                 }
279
280                 if (pid != 0) {
281                         exit (pid == -1 ? 1 : 0);
282                 }  else {
283                         gint i;
284                         int fd;
285                         gchar *arg0;
286                         gchar **actual_args;
287                         gint unused;
288
289                         close (info_pipe [0]);
290                         close (in_pipe [1]);
291                         close (out_pipe [0]);
292                         close (err_pipe [0]);
293
294                         /* when exec* succeeds, we want to close this fd, which will return
295                          * a 0 read on the parent. We're not supposed to keep it open forever.
296                          * If exec fails, we still can write the error to it before closing.
297                          */
298                         fcntl (info_pipe [1], F_SETFD, FD_CLOEXEC);
299
300                         if ((flags & G_SPAWN_DO_NOT_REAP_CHILD) == 0) {
301                                 pid = getpid ();
302                                 NO_INTR (unused, write (info_pipe [1], &pid, sizeof (pid_t)));
303                         }
304
305                         if (working_directory && chdir (working_directory) == -1) {
306                                 int err = errno;
307                                 NO_INTR (unused, write (info_pipe [1], &err, sizeof (int)));
308                                 exit (0);
309                         }
310
311                         if (standard_output) {
312                                 dup2 (out_pipe [1], STDOUT_FILENO);
313                         } else if ((flags & G_SPAWN_STDOUT_TO_DEV_NULL) != 0) {
314                                 fd = open ("/dev/null", O_WRONLY);
315                                 dup2 (fd, STDOUT_FILENO);
316                         }
317
318                         if (standard_error) {
319                                 dup2 (err_pipe [1], STDERR_FILENO);
320                         } else if ((flags & G_SPAWN_STDERR_TO_DEV_NULL) != 0) {
321                                 fd = open ("/dev/null", O_WRONLY);
322                                 dup2 (fd, STDERR_FILENO);
323                         }
324
325                         if (standard_input) {
326                                 dup2 (in_pipe [0], STDERR_FILENO);
327                         } else if ((flags & G_SPAWN_CHILD_INHERITS_STDIN) == 0) {
328                                 fd = open ("/dev/null", O_RDONLY);
329                                 dup2 (fd, STDERR_FILENO);
330                         }
331
332                         if ((flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN) != 0) {
333                                 for (i = getdtablesize () - 1; i >= 3; i--)
334                                         close (i);
335                         }
336
337                         actual_args = ((flags & G_SPAWN_FILE_AND_ARGV_ZERO) == 0) ? argv : argv + 1;
338                         if (envp == NULL)
339                                 envp = environ;
340
341                         if (child_setup)
342                                 child_setup (user_data);
343
344                         arg0 = argv [0];
345                         if (!g_path_is_absolute (arg0) || (flags & G_SPAWN_SEARCH_PATH) != 0) {
346                                 arg0 = g_find_program_in_path (argv [0]);
347                                 if (arg0 == NULL) {
348                                         int err = ENOENT;
349                                         write (info_pipe [1], &err, sizeof (int));
350                                         exit (0);
351                                 }
352                         }
353
354                         execve (arg0, actual_args, envp);
355                         write (info_pipe [1], &errno, sizeof (int));
356                         exit (0);
357                 }
358         } else if ((flags & G_SPAWN_DO_NOT_REAP_CHILD) == 0) {
359                 int w;
360                 /* Wait for the first child if two are created */
361                 NO_INTR (w, waitpid (pid, &status, 0));
362                 if (status == 1 || w == -1) {
363                         CLOSE_PIPE (info_pipe);
364                         CLOSE_PIPE (out_pipe);
365                         CLOSE_PIPE (err_pipe);
366                         CLOSE_PIPE (in_pipe);
367                         set_error ("Error in fork (): %d", status);
368                         return FALSE;
369                 }
370         }
371         close (info_pipe [1]);
372         close (in_pipe [0]);
373         close (out_pipe [1]);
374         close (err_pipe [1]);
375
376         if ((flags & G_SPAWN_DO_NOT_REAP_CHILD) == 0) {
377                 int x;
378                 NO_INTR (x, read (info_pipe [0], &pid, sizeof (pid_t))); /* if we read < sizeof (pid_t)... */
379         }
380
381         if (child_pid) {
382                 *child_pid = pid;
383         }
384
385         if (read (info_pipe [0], &status, sizeof (int)) != 0) {
386                 close (info_pipe [0]);
387                 close (in_pipe [0]);
388                 close (out_pipe [1]);
389                 close (err_pipe [1]);
390                 set_error_status (status, "Error in exec (%d -> %s)", status, strerror (status));
391                 return FALSE;
392         }
393
394         close (info_pipe [0]);
395         if (standard_input)
396                 *standard_input = in_pipe [1];
397         if (standard_output)
398                 *standard_output = out_pipe [0];
399         if (standard_error)
400                 *standard_error = err_pipe [0];
401
402         return TRUE;
403 }
404