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