1 /* $Id$ */
2
3 /*
4 *
5 * Copyright (C) 1998 David Mazieres (dm@uun.org)
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2, or (at
10 * your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 * USA
21 *
22 */
23
24 #include "amisc.h"
25 #include "rxx.h"
26 #include <dirent.h>
27
28 str execdir (EXECDIR);
29 #ifdef MAINTAINER
30 str builddir;
31 str buildtmpdir;
32 #endif /* MAINTAINER */
33
34 static void
nop(int)35 nop (int)
36 {
37 }
38
39 #ifdef MAINTAINER
40 bool afork_debug = safegetenv ("AFORK_DEBUG");
41 #endif /* MAINTAINER */
42
43 pid_t
afork()44 afork ()
45 {
46 if (pid_t pid = fork ())
47 return pid;
48
49 fatal_no_destruct = true;
50 err_reset ();
51
52 /* If we close/dup2 stderr before an exec, but the exec fails, we
53 * still want warn/fatal to work so as to report the error. */
54 if (errfd == 2) {
55 int fd = dup (errfd);
56 if (fd < 3)
57 close (fd);
58 else {
59 close_on_exec (fd);
60 errfd = fd;
61 }
62 }
63
64 /* If we exec, we want the child to get SIGPIPEs again. The signal
65 * mask and SIG_IGN signals are preserved by execve, but not
66 * handlers for specific functions. Thus we change the handler from
67 * the probably more efficient SIG_IGN to an empty function. */
68 struct sigaction sa;
69 bzero (&sa, sizeof (sa));
70 sa.sa_handler = nop;
71 sigaction (SIGPIPE, &sa, NULL);
72
73 #ifdef MAINTAINER
74 if (afork_debug) {
75 warn ("AFORK_DEBUG: child process pid %d\n", getpid ());
76 sleep (7);
77 }
78 #endif /* MAINTAINER */
79
80 return 0;
81 }
82
83 inline bool
execok(const char * path)84 execok (const char *path)
85 {
86 struct stat sb;
87 return !stat (path, &sb) && S_ISREG (sb.st_mode) && (sb.st_mode & 0111);
88 }
89
90 #ifdef MAINTAINER
91 static str
searchdir(str dir,str prog)92 searchdir (str dir, str prog)
93 {
94 DIR *dirp = opendir (dir);
95 if (!dirp)
96 return NULL;
97 while (dirent *dp = readdir (dirp)) {
98 struct stat sb;
99 str file = dir << "/" << dp->d_name;
100 str np;
101 if (!stat (file, &sb) && S_ISDIR (sb.st_mode)
102 && execok (np = file << prog)) {
103 closedir (dirp);
104 return np;
105 }
106 }
107 closedir (dirp);
108 return NULL;
109 }
110 #endif /* MAINTAINER */
111
112 str
fix_exec_path(str path,str dir)113 fix_exec_path (str path, str dir)
114 {
115 const char *prog = strrchr (path, '/');
116 if (prog)
117 return path;
118
119 if (!dir)
120 dir = execdir;
121 path = dir << "/" << path;
122 prog = strrchr (path, '/');
123 if (!prog)
124 panic ("No EXECDIR for unqualified path %s.\n", path.cstr ());
125
126 #ifdef MAINTAINER
127 if (builddir && dir == execdir) {
128 str np;
129 np = builddir << prog;
130 if (execok (np))
131 return np;
132 np = builddir << prog << prog;
133 if (execok (np))
134 return np;
135 if (np = searchdir (builddir, prog))
136 return np;
137 if (np = searchdir (builddir << "/lib", prog))
138 return np;
139 }
140 #endif /* MAINTAINER */
141
142 return path;
143 }
144
145 str
find_program(const char * program)146 find_program (const char *program)
147 {
148 static rxx colonplus (":+");
149 str r;
150 if (strchr (program, '/')) {
151 r = program;
152 if (execok (r))
153 return r;
154 return NULL;
155 }
156
157 #ifdef MAINTAINER
158 if (builddir) {
159 r = fix_exec_path (program);
160 if (execok (r))
161 return r;
162 }
163 #endif /* MAINTAINER */
164 if (progdir) {
165 r = progdir << program;
166 if (execok (r))
167 return r;
168 }
169
170 const char *p = getenv ("PATH");
171 if (!p)
172 return NULL;
173 vec<str> vs;
174 split (&vs, colonplus, p);
175 for (str *sp = vs.base (); sp < vs.lim (); sp++) {
176 if (!*sp || !sp->len ())
177 continue;
178 r = *sp << "/" << program;
179 if (execok (r))
180 return r;
181 }
182 return NULL;
183 }
184
185 str
find_program_plus_libsfs(const char * program)186 find_program_plus_libsfs (const char *program)
187 {
188 str s = fix_exec_path (program);
189 if (!s || !execok (s))
190 s = find_program (program);
191 return s;
192 }
193
194
195 void
setstdfds(int in,int out,int err)196 setstdfds (int in, int out, int err)
197 {
198 if (in != 0) {
199 dup2 (in, 0);
200 if (in > 2 && in != out && in != err)
201 close (in);
202 }
203 if (out != 1) {
204 dup2 (out, 1);
205 if (out > 2 && out != err)
206 close (out);
207 }
208 if (err != 2) {
209 dup2 (err, 2);
210 if (err > 2)
211 close (err);
212 }
213 }
214
215 extern bool amain_panic;
216 pid_t
spawn(const char * path,char * const * argv,int in,int out,int err,cbv::ptr postforkcb,char * const * env)217 spawn (const char *path, char *const *argv,
218 int in, int out, int err, cbv::ptr postforkcb, char *const *env)
219 {
220 int fds[2];
221
222 if (pipe (fds) < 0)
223 return -1;
224
225 close_on_exec (fds[0]);
226 close_on_exec (fds[1]);
227
228 pid_t pid = afork ();
229 if (pid < 0)
230 return pid;
231
232 if (!pid) {
233 amain_panic = true;
234 close (fds[0]);
235 setstdfds (in, out, err);
236 if (postforkcb)
237 (*postforkcb) ();
238 if (env)
239 execve (path, argv, env);
240 else
241 execv (path, argv);
242
243 int saved_err = errno;
244 use (write (fds[1], &saved_err, sizeof (saved_err)));
245 close (fds[1]);
246 // Since we return a useful errno, there is no need to print
247 // anything in the child process. Just exit.
248 _exit (1);
249 }
250
251 close (fds[1]);
252 int chld_err;
253 int n = read (fds[0], &chld_err, sizeof (chld_err));
254 close (fds[0]);
255
256 if (n == 0)
257 return pid;
258 errno = chld_err;
259 return -1;
260 }
261
262 pid_t
aspawn(const char * path,char * const * argv,int in,int out,int err,cbv::ptr postforkcb,char * const * env)263 aspawn (const char *path, char *const *argv,
264 int in, int out, int err, cbv::ptr postforkcb, char *const *env)
265 {
266 pid_t pid = afork ();
267 if (pid < 0)
268 return pid;
269
270 if (pid)
271 return pid;
272
273 setstdfds (in, out, err);
274
275 if (postforkcb)
276 (*postforkcb) ();
277 if (env)
278 execve (path, argv, env);
279 else
280 execv (path, argv);
281
282 warn ("%s: %m\n", path);
283 _exit (1);
284 }
285
286