* exechelp.h, exechelp.c: New. Based on code from ../sm/import.c.
[gnupg.git] / common / exechelp.c
1 /* exechelp.c - fork and exec helpers
2  *      Copyright (C) 2004 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <assert.h>
28 #include <signal.h>
29 #include <unistd.h> 
30 #ifdef USE_GNU_PTH      
31 #include <pth.h>
32 #endif
33 #ifdef _WIN32
34 #else
35 #include <sys/wait.h>
36 #endif
37
38 #include "util.h"
39 #include "i18n.h"
40 #include "exechelp.h"
41
42
43 #ifdef _POSIX_OPEN_MAX
44 #define MAX_OPEN_FDS _POSIX_OPEN_MAX
45 #else
46 #define MAX_OPEN_FDS 20
47 #endif
48
49 /* We have the usual problem here: Some modules are linked against pth
50    and some are not.  However we want to use pth_fork and pth_waitpid
51    here. Using a weak symbol works but is not portable - we should
52    provide a an explicit dummy pth module instead of using the
53    pragma.  */ 
54 #ifndef _WIN32
55 #pragma weak pth_fork
56 #pragma weak pth_waitpid
57 #endif
58
59
60
61 /* Fork and exec the PGMNAME, connect the file descriptor of INFILE to
62    stdin, write the output to OUTFILE, return a new stream in
63    STATUSFILE for stderr and the pid of the process in PID. The
64    arguments for the process are expected in the NULL terminated array
65    ARGV.  The program name itself should not be included there.  if
66    PREEXEC is not NULL, that function will be called right before the
67    exec.
68
69    Returns 0 on success or an error code. */
70 gpg_error_t
71 gnupg_spawn_process (const char *pgmname, const char *argv[],
72                      FILE *infile, FILE *outfile,
73                      void (*preexec)(void),
74                      FILE **statusfile, pid_t *pid)
75 {
76 #ifdef _WIN32
77   return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
78
79 #else /* !_WIN32 */
80   gpg_error_t err;
81   int fd, fdout, rp[2];
82
83   *statusfile = NULL;
84   *pid = (pid_t)(-1);
85   fflush (infile);
86   rewind (infile);
87   fd = fileno (infile);
88   fdout = fileno (outfile);
89   if (fd == -1 || fdout == -1)
90     log_fatal ("no file descriptor for file passed"
91                " to gnupg_spawn_process: %s\n",  strerror (errno) );
92
93   if (pipe (rp) == -1)
94     {
95       err = gpg_error_from_errno (errno);
96       log_error (_("error creating a pipe: %s\n"), strerror (errno));
97       return err;
98     }
99
100 #ifdef USE_GNU_PTH      
101   *pid = pth_fork? pth_fork () : fork ();
102 #else
103   *pid = fork ();
104 #endif
105   if (*pid == (pid_t)(-1))
106     {
107       err = gpg_error_from_errno (errno);
108       log_error (_("error forking process: %s\n"), strerror (errno));
109       close (rp[0]);
110       close (rp[1]);
111       return err;
112     }
113
114   if (!*pid)
115     { 
116       /* Child. */
117       char **arg_list;
118       int n, i, j;
119
120       /* Create the command line argument array.  */
121       for (i=0; argv[i]; i++)
122         ;
123       arg_list = xcalloc (i+2, sizeof *arg_list);
124       arg_list[0] = strrchr (pgmname, '/');
125       if (arg_list[0])
126         arg_list[0]++;
127       else
128         arg_list[0] = xstrdup (pgmname);
129       for (i=0,j=1; argv[i]; i++, j++)
130         arg_list[j] = (char*)argv[i];
131
132       /* Connect the infile to stdin. */
133       if (fd != 0 && dup2 (fd, 0) == -1)
134         log_fatal ("dup2 stdin failed: %s\n", strerror (errno));
135
136       /* Connect the outfile to stdout. */
137       if (fdout != 1 && dup2 (fdout, 1) == -1)
138         log_fatal ("dup2 stdout failed: %s\n", strerror (errno));
139       
140       /* Connect stderr to our pipe. */
141       if (rp[1] != 2 && dup2 (rp[1], 2) == -1)
142         log_fatal ("dup2 stderr failed: %s\n", strerror (errno));
143
144       /* Close all other files. */
145       n = sysconf (_SC_OPEN_MAX);
146       if (n < 0)
147         n = MAX_OPEN_FDS;
148       for (i=3; i < n; i++)
149         close(i);
150       errno = 0;
151
152       if (preexec)
153         preexec ();
154       execv (pgmname, arg_list);
155       /* No way to print anything, as we have closed all streams. */
156       _exit (127);
157     }
158
159   /* Parent. */
160   close (rp[1]);
161
162   *statusfile = fdopen (rp[0], "r");
163   if (!*statusfile)
164     {
165       err = gpg_error_from_errno (errno);
166       log_error (_("can't fdopen pipe for reading: %s\n"), strerror (errno));
167       kill (*pid, SIGTERM);
168       *pid = (pid_t)(-1);
169       return err;
170     }
171
172   return 0;
173 #endif /* !_WIN32 */
174 }
175
176
177 /* Wait for the process identified by PID to terminate. PGMNAME should
178    be the same as suplieed to the spawn fucntion and is only used for
179    diagnostics. Returns 0 if the process succeded, GPG_ERR_GENERAL for
180    any failures of the spawned program or other error codes.*/
181 gpg_error_t
182 gnupg_wait_process (const char *pgmname, pid_t pid)
183 {
184   gpg_err_code_t ec;
185
186 #ifdef _WIN32
187   ec = GPG_ERR_NOT_IMPLEMENTED;
188
189 #else /* !_WIN32 */
190   int i, status;
191
192   if (pid == (pid_t)(-1))
193     return gpg_error (GPG_ERR_INV_VALUE);
194
195 #ifdef USE_GNU_PTH
196   i = pth_waitpid ? pth_waitpid (pid, &status, 0) : waitpid (pid, &status, 0);
197 #else
198   while ( (i=waitpid (pid, &status, 0)) == -1 && errno == EINTR)
199     ;
200 #endif
201   if (i == (pid_t)(-1))
202     {
203       log_error (_("waiting for process %d to terminate failed: %s\n"),
204                  (int)pid, strerror (errno));
205       ec = gpg_err_code_from_errno (errno);
206     }
207   else if (WIFEXITED (status) && WEXITSTATUS (status) == 127)
208     {
209       log_error (_("error running `%s': probably not installed\n"), pgmname);
210       ec = GPG_ERR_CONFIGURATION;
211     }
212   else if (WIFEXITED (status) && WEXITSTATUS (status))
213     {
214       log_error (_("error running `%s': exit status %d\n"), pgmname,
215                  WEXITSTATUS (status));
216       ec = GPG_ERR_GENERAL;
217     }
218   else if (!WIFEXITED (status))
219     {
220       log_error (_("error running `%s': terminated\n"), pgmname);
221       ec = GPG_ERR_GENERAL;
222     }
223   else 
224     ec = 0;
225 #endif /* !_WIN32 */
226
227   return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);
228
229 }
230