unix exec: avoid atexit handlers when child exits
[grub.git] / grub-core / osdep / unix / exec.c
1 /*
2  *  GRUB  --  GRand Unified Bootloader
3  *  Copyright (C) 1999,2000,2001,2002,2003,2006,2007,2008,2009,2010,2011  Free Software Foundation, Inc.
4  *
5  *  GRUB is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  GRUB is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include <config-util.h>
20 #include <config.h>
21
22 #include <grub/misc.h>
23 #include <unistd.h>
24 #include <grub/emu/exec.h>
25 #include <grub/emu/hostdisk.h>
26 #include <grub/emu/getroot.h>
27 #include <grub/util/misc.h>
28 #include <grub/disk.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <sys/wait.h>
35
36 int
37 grub_util_exec_redirect_all (const char *const *argv, const char *stdin_file,
38                              const char *stdout_file, const char *stderr_file)
39 {
40   pid_t pid;
41   int status = -1;
42   char *str, *pstr;
43   const char *const *ptr;
44   grub_size_t strl = 0;
45   for (ptr = argv; *ptr; ptr++)
46     strl += grub_strlen (*ptr) + 1;
47   if (stdin_file)
48     strl += grub_strlen (stdin_file) + 2;
49   if (stdout_file)
50     strl += grub_strlen (stdout_file) + 2;
51   if (stderr_file)
52     strl += grub_strlen (stderr_file) + 3;
53
54   pstr = str = xmalloc (strl);
55   for (ptr = argv; *ptr; ptr++)
56     {
57       pstr = grub_stpcpy (pstr, *ptr);
58       *pstr++ = ' ';
59     }
60   if (stdin_file)
61     {
62       *pstr++ = '<';
63       pstr = grub_stpcpy (pstr, stdin_file);
64       *pstr++ = ' ';
65     }
66   if (stdout_file)
67     {
68       *pstr++ = '>';
69       pstr = grub_stpcpy (pstr, stdout_file);
70       *pstr++ = ' ';
71     }
72   if (stderr_file)
73     {
74       *pstr++ = '2';
75       *pstr++ = '>';
76       pstr = grub_stpcpy (pstr, stderr_file);
77       pstr++;
78     }
79   *--pstr = '\0';
80
81   grub_util_info ("executing %s", str);
82   grub_free (str);
83
84   pid = fork ();
85   if (pid < 0)
86     grub_util_error (_("Unable to fork: %s"), strerror (errno));
87   else if (pid == 0)
88     {
89       int fd;
90       /* Child.  */
91       
92       /* Close fd's.  */
93 #ifdef GRUB_UTIL
94       grub_util_devmapper_cleanup ();
95       grub_diskfilter_fini ();
96 #endif
97
98       if (stdin_file)
99         {
100           fd = open (stdin_file, O_RDONLY);
101           if (fd < 0)
102             _exit (127);
103           dup2 (fd, STDIN_FILENO);
104           close (fd);
105         }
106
107       if (stdout_file)
108         {
109           fd = open (stdout_file, O_WRONLY | O_CREAT, 0700);
110           if (fd < 0)
111             _exit (127);
112           dup2 (fd, STDOUT_FILENO);
113           close (fd);
114         }
115
116       if (stderr_file)
117         {
118           fd = open (stderr_file, O_WRONLY | O_CREAT, 0700);
119           if (fd < 0)
120             _exit (127);
121           dup2 (fd, STDERR_FILENO);
122           close (fd);
123         }
124
125       /* Ensure child is not localised.  */
126       setenv ("LC_ALL", "C", 1);
127
128       execvp ((char *) argv[0], (char **) argv);
129       _exit (127);
130     }
131   waitpid (pid, &status, 0);
132   if (!WIFEXITED (status))
133     return -1;
134   return WEXITSTATUS (status);
135 }
136
137 int
138 grub_util_exec (const char *const *argv)
139 {
140   return grub_util_exec_redirect_all (argv, NULL, NULL, NULL);
141 }
142
143 int
144 grub_util_exec_redirect (const char *const *argv, const char *stdin_file,
145                          const char *stdout_file)
146 {
147   return grub_util_exec_redirect_all (argv, stdin_file, stdout_file, NULL);
148 }
149
150 int
151 grub_util_exec_redirect_null (const char *const *argv)
152 {
153   return grub_util_exec_redirect_all (argv, "/dev/null", "/dev/null", NULL);
154 }
155
156 pid_t
157 grub_util_exec_pipe (const char *const *argv, int *fd)
158 {
159   int pipe_fd[2];
160   pid_t pid;
161
162   *fd = 0;
163
164   if (pipe (pipe_fd) < 0)
165     {
166       grub_util_warn (_("Unable to create pipe: %s"),
167                       strerror (errno));
168       return 0;
169     }
170   pid = fork ();
171   if (pid < 0)
172     grub_util_error (_("Unable to fork: %s"), strerror (errno));
173   else if (pid == 0)
174     {
175       /* Child.  */
176
177       /* Close fd's.  */
178 #ifdef GRUB_UTIL
179       grub_util_devmapper_cleanup ();
180       grub_diskfilter_fini ();
181 #endif
182
183       /* Ensure child is not localised.  */
184       setenv ("LC_ALL", "C", 1);
185
186       close (pipe_fd[0]);
187       dup2 (pipe_fd[1], STDOUT_FILENO);
188       close (pipe_fd[1]);
189
190       execvp ((char *) argv[0], (char **) argv);
191       exit (127);
192     }
193   else
194     {
195       close (pipe_fd[1]);
196       *fd = pipe_fd[0];
197       return pid;
198     }
199 }
200
201 pid_t
202 grub_util_exec_pipe_stderr (const char *const *argv, int *fd)
203 {
204   int pipe_fd[2];
205   pid_t pid;
206
207   *fd = 0;
208
209   if (pipe (pipe_fd) < 0)
210     {
211       grub_util_warn (_("Unable to create pipe: %s"),
212                       strerror (errno));
213       return 0;
214     }
215   pid = fork ();
216   if (pid < 0)
217     grub_util_error (_("Unable to fork: %s"), strerror (errno));
218   else if (pid == 0)
219     {
220       /* Child.  */
221
222       /* Close fd's.  */
223 #ifdef GRUB_UTIL
224       grub_util_devmapper_cleanup ();
225       grub_diskfilter_fini ();
226 #endif
227
228       /* Ensure child is not localised.  */
229       setenv ("LC_ALL", "C", 1);
230
231       close (pipe_fd[0]);
232       dup2 (pipe_fd[1], STDOUT_FILENO);
233       dup2 (pipe_fd[1], STDERR_FILENO);
234       close (pipe_fd[1]);
235
236       execvp ((char *) argv[0], (char **) argv);
237       exit (127);
238     }
239   else
240     {
241       close (pipe_fd[1]);
242       *fd = pipe_fd[0];
243       return pid;
244     }
245 }