xref: /freebsd/contrib/pam-krb5/tests/tap/process.c (revision bf6873c5786e333d679a7838d28812febf479a8a)
1*bf6873c5SCy Schubert /*
2*bf6873c5SCy Schubert  * Utility functions for tests that use subprocesses.
3*bf6873c5SCy Schubert  *
4*bf6873c5SCy Schubert  * Provides utility functions for subprocess manipulation.  Specifically,
5*bf6873c5SCy Schubert  * provides a function, run_setup, which runs a command and bails if it fails,
6*bf6873c5SCy Schubert  * using its error message as the bail output, and is_function_output, which
7*bf6873c5SCy Schubert  * runs a function in a subprocess and checks its output and exit status
8*bf6873c5SCy Schubert  * against expected values.
9*bf6873c5SCy Schubert  *
10*bf6873c5SCy Schubert  * Requires an Autoconf probe for sys/select.h and a replacement for a missing
11*bf6873c5SCy Schubert  * mkstemp.
12*bf6873c5SCy Schubert  *
13*bf6873c5SCy Schubert  * The canonical version of this file is maintained in the rra-c-util package,
14*bf6873c5SCy Schubert  * which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
15*bf6873c5SCy Schubert  *
16*bf6873c5SCy Schubert  * Written by Russ Allbery <eagle@eyrie.org>
17*bf6873c5SCy Schubert  * Copyright 2002, 2004-2005, 2013, 2016-2017 Russ Allbery <eagle@eyrie.org>
18*bf6873c5SCy Schubert  * Copyright 2009-2011, 2013-2014
19*bf6873c5SCy Schubert  *     The Board of Trustees of the Leland Stanford Junior University
20*bf6873c5SCy Schubert  *
21*bf6873c5SCy Schubert  * Permission is hereby granted, free of charge, to any person obtaining a
22*bf6873c5SCy Schubert  * copy of this software and associated documentation files (the "Software"),
23*bf6873c5SCy Schubert  * to deal in the Software without restriction, including without limitation
24*bf6873c5SCy Schubert  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
25*bf6873c5SCy Schubert  * and/or sell copies of the Software, and to permit persons to whom the
26*bf6873c5SCy Schubert  * Software is furnished to do so, subject to the following conditions:
27*bf6873c5SCy Schubert  *
28*bf6873c5SCy Schubert  * The above copyright notice and this permission notice shall be included in
29*bf6873c5SCy Schubert  * all copies or substantial portions of the Software.
30*bf6873c5SCy Schubert  *
31*bf6873c5SCy Schubert  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
32*bf6873c5SCy Schubert  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33*bf6873c5SCy Schubert  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
34*bf6873c5SCy Schubert  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
35*bf6873c5SCy Schubert  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
36*bf6873c5SCy Schubert  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
37*bf6873c5SCy Schubert  * DEALINGS IN THE SOFTWARE.
38*bf6873c5SCy Schubert  *
39*bf6873c5SCy Schubert  * SPDX-License-Identifier: MIT
40*bf6873c5SCy Schubert  */
41*bf6873c5SCy Schubert 
42*bf6873c5SCy Schubert #include <config.h>
43*bf6873c5SCy Schubert #include <portable/system.h>
44*bf6873c5SCy Schubert 
45*bf6873c5SCy Schubert #include <errno.h>
46*bf6873c5SCy Schubert #include <fcntl.h>
47*bf6873c5SCy Schubert #include <signal.h>
48*bf6873c5SCy Schubert #ifdef HAVE_SYS_SELECT_H
49*bf6873c5SCy Schubert #    include <sys/select.h>
50*bf6873c5SCy Schubert #endif
51*bf6873c5SCy Schubert #include <sys/stat.h>
52*bf6873c5SCy Schubert #ifdef HAVE_SYS_TIME_H
53*bf6873c5SCy Schubert #    include <sys/time.h>
54*bf6873c5SCy Schubert #endif
55*bf6873c5SCy Schubert #include <sys/wait.h>
56*bf6873c5SCy Schubert #include <time.h>
57*bf6873c5SCy Schubert 
58*bf6873c5SCy Schubert #include <tests/tap/basic.h>
59*bf6873c5SCy Schubert #include <tests/tap/process.h>
60*bf6873c5SCy Schubert #include <tests/tap/string.h>
61*bf6873c5SCy Schubert 
62*bf6873c5SCy Schubert /* May be defined by the build system. */
63*bf6873c5SCy Schubert #ifndef PATH_FAKEROOT
64*bf6873c5SCy Schubert #    define PATH_FAKEROOT ""
65*bf6873c5SCy Schubert #endif
66*bf6873c5SCy Schubert 
67*bf6873c5SCy Schubert /* How long to wait for the process to start in seconds. */
68*bf6873c5SCy Schubert #define PROCESS_WAIT 10
69*bf6873c5SCy Schubert 
70*bf6873c5SCy Schubert /*
71*bf6873c5SCy Schubert  * Used to store information about a background process.  This contains
72*bf6873c5SCy Schubert  * everything required to stop the process and clean up after it.
73*bf6873c5SCy Schubert  */
74*bf6873c5SCy Schubert struct process {
75*bf6873c5SCy Schubert     pid_t pid;            /* PID of child process */
76*bf6873c5SCy Schubert     char *pidfile;        /* PID file to delete on process stop */
77*bf6873c5SCy Schubert     char *tmpdir;         /* Temporary directory for log file */
78*bf6873c5SCy Schubert     char *logfile;        /* Log file of process output */
79*bf6873c5SCy Schubert     bool is_child;        /* Whether we can waitpid for process */
80*bf6873c5SCy Schubert     struct process *next; /* Next process in global list */
81*bf6873c5SCy Schubert };
82*bf6873c5SCy Schubert 
83*bf6873c5SCy Schubert /*
84*bf6873c5SCy Schubert  * Global list of started processes, which will be cleaned up automatically on
85*bf6873c5SCy Schubert  * program exit if they haven't been explicitly stopped with process_stop
86*bf6873c5SCy Schubert  * prior to that point.
87*bf6873c5SCy Schubert  */
88*bf6873c5SCy Schubert static struct process *processes = NULL;
89*bf6873c5SCy Schubert 
90*bf6873c5SCy Schubert 
91*bf6873c5SCy Schubert /*
92*bf6873c5SCy Schubert  * Given a function, an expected exit status, and expected output, runs that
93*bf6873c5SCy Schubert  * function in a subprocess, capturing stdout and stderr via a pipe, and
94*bf6873c5SCy Schubert  * returns the function output in newly allocated memory.  Also captures the
95*bf6873c5SCy Schubert  * process exit status.
96*bf6873c5SCy Schubert  */
97*bf6873c5SCy Schubert static void
run_child_function(test_function_type function,void * data,int * status,char ** output)98*bf6873c5SCy Schubert run_child_function(test_function_type function, void *data, int *status,
99*bf6873c5SCy Schubert                    char **output)
100*bf6873c5SCy Schubert {
101*bf6873c5SCy Schubert     int fds[2];
102*bf6873c5SCy Schubert     pid_t child;
103*bf6873c5SCy Schubert     char *buf;
104*bf6873c5SCy Schubert     ssize_t count, ret, buflen;
105*bf6873c5SCy Schubert     int rval;
106*bf6873c5SCy Schubert 
107*bf6873c5SCy Schubert     /* Flush stdout before we start to avoid odd forking issues. */
108*bf6873c5SCy Schubert     fflush(stdout);
109*bf6873c5SCy Schubert 
110*bf6873c5SCy Schubert     /* Set up the pipe and call the function, collecting its output. */
111*bf6873c5SCy Schubert     if (pipe(fds) == -1)
112*bf6873c5SCy Schubert         sysbail("can't create pipe");
113*bf6873c5SCy Schubert     child = fork();
114*bf6873c5SCy Schubert     if (child == (pid_t) -1) {
115*bf6873c5SCy Schubert         sysbail("can't fork");
116*bf6873c5SCy Schubert     } else if (child == 0) {
117*bf6873c5SCy Schubert         /* In child.  Set up our stdout and stderr. */
118*bf6873c5SCy Schubert         close(fds[0]);
119*bf6873c5SCy Schubert         if (dup2(fds[1], 1) == -1)
120*bf6873c5SCy Schubert             _exit(255);
121*bf6873c5SCy Schubert         if (dup2(fds[1], 2) == -1)
122*bf6873c5SCy Schubert             _exit(255);
123*bf6873c5SCy Schubert 
124*bf6873c5SCy Schubert         /* Now, run the function and exit successfully if it returns. */
125*bf6873c5SCy Schubert         (*function)(data);
126*bf6873c5SCy Schubert         fflush(stdout);
127*bf6873c5SCy Schubert         _exit(0);
128*bf6873c5SCy Schubert     } else {
129*bf6873c5SCy Schubert         /*
130*bf6873c5SCy Schubert          * In the parent; close the extra file descriptor, read the output if
131*bf6873c5SCy Schubert          * any, and then collect the exit status.
132*bf6873c5SCy Schubert          */
133*bf6873c5SCy Schubert         close(fds[1]);
134*bf6873c5SCy Schubert         buflen = BUFSIZ;
135*bf6873c5SCy Schubert         buf = bmalloc(buflen);
136*bf6873c5SCy Schubert         count = 0;
137*bf6873c5SCy Schubert         do {
138*bf6873c5SCy Schubert             ret = read(fds[0], buf + count, buflen - count - 1);
139*bf6873c5SCy Schubert             if (SSIZE_MAX - count <= ret)
140*bf6873c5SCy Schubert                 bail("maximum output size exceeded in run_child_function");
141*bf6873c5SCy Schubert             if (ret > 0)
142*bf6873c5SCy Schubert                 count += ret;
143*bf6873c5SCy Schubert             if (count >= buflen - 1) {
144*bf6873c5SCy Schubert                 buflen += BUFSIZ;
145*bf6873c5SCy Schubert                 buf = brealloc(buf, buflen);
146*bf6873c5SCy Schubert             }
147*bf6873c5SCy Schubert         } while (ret > 0);
148*bf6873c5SCy Schubert         buf[count] = '\0';
149*bf6873c5SCy Schubert         if (waitpid(child, &rval, 0) == (pid_t) -1)
150*bf6873c5SCy Schubert             sysbail("waitpid failed");
151*bf6873c5SCy Schubert         close(fds[0]);
152*bf6873c5SCy Schubert     }
153*bf6873c5SCy Schubert 
154*bf6873c5SCy Schubert     /* Store the output and return. */
155*bf6873c5SCy Schubert     *status = rval;
156*bf6873c5SCy Schubert     *output = buf;
157*bf6873c5SCy Schubert }
158*bf6873c5SCy Schubert 
159*bf6873c5SCy Schubert 
160*bf6873c5SCy Schubert /*
161*bf6873c5SCy Schubert  * Given a function, data to pass to that function, an expected exit status,
162*bf6873c5SCy Schubert  * and expected output, runs that function in a subprocess, capturing stdout
163*bf6873c5SCy Schubert  * and stderr via a pipe, and compare the combination of stdout and stderr
164*bf6873c5SCy Schubert  * with the expected output and the exit status with the expected status.
165*bf6873c5SCy Schubert  * Expects the function to always exit (not die from a signal).
166*bf6873c5SCy Schubert  */
167*bf6873c5SCy Schubert void
is_function_output(test_function_type function,void * data,int status,const char * output,const char * format,...)168*bf6873c5SCy Schubert is_function_output(test_function_type function, void *data, int status,
169*bf6873c5SCy Schubert                    const char *output, const char *format, ...)
170*bf6873c5SCy Schubert {
171*bf6873c5SCy Schubert     char *buf, *msg;
172*bf6873c5SCy Schubert     int rval;
173*bf6873c5SCy Schubert     va_list args;
174*bf6873c5SCy Schubert 
175*bf6873c5SCy Schubert     run_child_function(function, data, &rval, &buf);
176*bf6873c5SCy Schubert 
177*bf6873c5SCy Schubert     /* Now, check the results against what we expected. */
178*bf6873c5SCy Schubert     va_start(args, format);
179*bf6873c5SCy Schubert     bvasprintf(&msg, format, args);
180*bf6873c5SCy Schubert     va_end(args);
181*bf6873c5SCy Schubert     ok(WIFEXITED(rval), "%s (exited)", msg);
182*bf6873c5SCy Schubert     is_int(status, WEXITSTATUS(rval), "%s (status)", msg);
183*bf6873c5SCy Schubert     is_string(output, buf, "%s (output)", msg);
184*bf6873c5SCy Schubert     free(buf);
185*bf6873c5SCy Schubert     free(msg);
186*bf6873c5SCy Schubert }
187*bf6873c5SCy Schubert 
188*bf6873c5SCy Schubert 
189*bf6873c5SCy Schubert /*
190*bf6873c5SCy Schubert  * A helper function for run_setup.  This is a function to run an external
191*bf6873c5SCy Schubert  * command, suitable for passing into run_child_function.  The expected
192*bf6873c5SCy Schubert  * argument must be an argv array, with argv[0] being the command to run.
193*bf6873c5SCy Schubert  */
194*bf6873c5SCy Schubert static void
exec_command(void * data)195*bf6873c5SCy Schubert exec_command(void *data)
196*bf6873c5SCy Schubert {
197*bf6873c5SCy Schubert     char *const *argv = data;
198*bf6873c5SCy Schubert 
199*bf6873c5SCy Schubert     execvp(argv[0], argv);
200*bf6873c5SCy Schubert }
201*bf6873c5SCy Schubert 
202*bf6873c5SCy Schubert 
203*bf6873c5SCy Schubert /*
204*bf6873c5SCy Schubert  * Given a command expressed as an argv struct, with argv[0] the name or path
205*bf6873c5SCy Schubert  * to the command, run that command.  If it exits with a non-zero status, use
206*bf6873c5SCy Schubert  * the part of its output up to the first newline as the error message when
207*bf6873c5SCy Schubert  * calling bail.
208*bf6873c5SCy Schubert  */
209*bf6873c5SCy Schubert void
run_setup(const char * const argv[])210*bf6873c5SCy Schubert run_setup(const char *const argv[])
211*bf6873c5SCy Schubert {
212*bf6873c5SCy Schubert     char *output, *p;
213*bf6873c5SCy Schubert     int status;
214*bf6873c5SCy Schubert 
215*bf6873c5SCy Schubert     run_child_function(exec_command, (void *) argv, &status, &output);
216*bf6873c5SCy Schubert     if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
217*bf6873c5SCy Schubert         p = strchr(output, '\n');
218*bf6873c5SCy Schubert         if (p != NULL)
219*bf6873c5SCy Schubert             *p = '\0';
220*bf6873c5SCy Schubert         if (output[0] != '\0')
221*bf6873c5SCy Schubert             bail("%s", output);
222*bf6873c5SCy Schubert         else
223*bf6873c5SCy Schubert             bail("setup command failed with no output");
224*bf6873c5SCy Schubert     }
225*bf6873c5SCy Schubert     free(output);
226*bf6873c5SCy Schubert }
227*bf6873c5SCy Schubert 
228*bf6873c5SCy Schubert 
229*bf6873c5SCy Schubert /*
230*bf6873c5SCy Schubert  * Free the resources associated with tracking a process, without doing
231*bf6873c5SCy Schubert  * anything to the process.  This is kept separate so that we can free
232*bf6873c5SCy Schubert  * resources during shutdown in a non-primary process.
233*bf6873c5SCy Schubert  */
234*bf6873c5SCy Schubert static void
process_free(struct process * process)235*bf6873c5SCy Schubert process_free(struct process *process)
236*bf6873c5SCy Schubert {
237*bf6873c5SCy Schubert     struct process **prev;
238*bf6873c5SCy Schubert 
239*bf6873c5SCy Schubert     /* Do nothing if called with a NULL argument. */
240*bf6873c5SCy Schubert     if (process == NULL)
241*bf6873c5SCy Schubert         return;
242*bf6873c5SCy Schubert 
243*bf6873c5SCy Schubert     /* Remove the process from the global list. */
244*bf6873c5SCy Schubert     prev = &processes;
245*bf6873c5SCy Schubert     while (*prev != NULL && *prev != process)
246*bf6873c5SCy Schubert         prev = &(*prev)->next;
247*bf6873c5SCy Schubert     if (*prev == process)
248*bf6873c5SCy Schubert         *prev = process->next;
249*bf6873c5SCy Schubert 
250*bf6873c5SCy Schubert     /* Free resources. */
251*bf6873c5SCy Schubert     free(process->pidfile);
252*bf6873c5SCy Schubert     free(process->logfile);
253*bf6873c5SCy Schubert     test_tmpdir_free(process->tmpdir);
254*bf6873c5SCy Schubert     free(process);
255*bf6873c5SCy Schubert }
256*bf6873c5SCy Schubert 
257*bf6873c5SCy Schubert 
258*bf6873c5SCy Schubert /*
259*bf6873c5SCy Schubert  * Kill a process and wait for it to exit.  Returns the status of the process.
260*bf6873c5SCy Schubert  * Calls bail on a system failure or a failure of the process to exit.
261*bf6873c5SCy Schubert  *
262*bf6873c5SCy Schubert  * We are quite aggressive with error reporting here because child processes
263*bf6873c5SCy Schubert  * that don't exit or that don't exist often indicate some form of test
264*bf6873c5SCy Schubert  * failure.
265*bf6873c5SCy Schubert  */
266*bf6873c5SCy Schubert static int
process_kill(struct process * process)267*bf6873c5SCy Schubert process_kill(struct process *process)
268*bf6873c5SCy Schubert {
269*bf6873c5SCy Schubert     int result, i;
270*bf6873c5SCy Schubert     int status = -1;
271*bf6873c5SCy Schubert     struct timeval tv;
272*bf6873c5SCy Schubert     unsigned long pid = process->pid;
273*bf6873c5SCy Schubert 
274*bf6873c5SCy Schubert     /* If the process is not a child, just kill it and hope. */
275*bf6873c5SCy Schubert     if (!process->is_child) {
276*bf6873c5SCy Schubert         if (kill(process->pid, SIGTERM) < 0 && errno != ESRCH)
277*bf6873c5SCy Schubert             sysbail("cannot send SIGTERM to process %lu", pid);
278*bf6873c5SCy Schubert         return 0;
279*bf6873c5SCy Schubert     }
280*bf6873c5SCy Schubert 
281*bf6873c5SCy Schubert     /* Check if the process has already exited. */
282*bf6873c5SCy Schubert     result = waitpid(process->pid, &status, WNOHANG);
283*bf6873c5SCy Schubert     if (result < 0)
284*bf6873c5SCy Schubert         sysbail("cannot wait for child process %lu", pid);
285*bf6873c5SCy Schubert     else if (result > 0)
286*bf6873c5SCy Schubert         return status;
287*bf6873c5SCy Schubert 
288*bf6873c5SCy Schubert     /*
289*bf6873c5SCy Schubert      * Kill the process and wait for it to exit.  I don't want to go to the
290*bf6873c5SCy Schubert      * work of setting up a SIGCHLD handler or a full event loop here, so we
291*bf6873c5SCy Schubert      * effectively poll every tenth of a second for process exit (and
292*bf6873c5SCy Schubert      * hopefully faster when it does since the SIGCHLD may interrupt our
293*bf6873c5SCy Schubert      * select, although we're racing with it.
294*bf6873c5SCy Schubert      */
295*bf6873c5SCy Schubert     if (kill(process->pid, SIGTERM) < 0 && errno != ESRCH)
296*bf6873c5SCy Schubert         sysbail("cannot send SIGTERM to child process %lu", pid);
297*bf6873c5SCy Schubert     for (i = 0; i < PROCESS_WAIT * 10; i++) {
298*bf6873c5SCy Schubert         tv.tv_sec = 0;
299*bf6873c5SCy Schubert         tv.tv_usec = 100000;
300*bf6873c5SCy Schubert         select(0, NULL, NULL, NULL, &tv);
301*bf6873c5SCy Schubert         result = waitpid(process->pid, &status, WNOHANG);
302*bf6873c5SCy Schubert         if (result < 0)
303*bf6873c5SCy Schubert             sysbail("cannot wait for child process %lu", pid);
304*bf6873c5SCy Schubert         else if (result > 0)
305*bf6873c5SCy Schubert             return status;
306*bf6873c5SCy Schubert     }
307*bf6873c5SCy Schubert 
308*bf6873c5SCy Schubert     /* The process still hasn't exited.  Bail. */
309*bf6873c5SCy Schubert     bail("child process %lu did not exit on SIGTERM", pid);
310*bf6873c5SCy Schubert 
311*bf6873c5SCy Schubert     /* Not reached, but some compilers may get confused. */
312*bf6873c5SCy Schubert     return status;
313*bf6873c5SCy Schubert }
314*bf6873c5SCy Schubert 
315*bf6873c5SCy Schubert 
316*bf6873c5SCy Schubert /*
317*bf6873c5SCy Schubert  * Stop a particular process given its process struct.  This kills the
318*bf6873c5SCy Schubert  * process, waits for it to exit if possible (giving it at most five seconds),
319*bf6873c5SCy Schubert  * and then removes it from the global processes struct so that it isn't
320*bf6873c5SCy Schubert  * stopped again during global shutdown.
321*bf6873c5SCy Schubert  */
322*bf6873c5SCy Schubert void
process_stop(struct process * process)323*bf6873c5SCy Schubert process_stop(struct process *process)
324*bf6873c5SCy Schubert {
325*bf6873c5SCy Schubert     int status;
326*bf6873c5SCy Schubert     unsigned long pid = process->pid;
327*bf6873c5SCy Schubert 
328*bf6873c5SCy Schubert     /* Stop the process. */
329*bf6873c5SCy Schubert     status = process_kill(process);
330*bf6873c5SCy Schubert 
331*bf6873c5SCy Schubert     /* Call diag to flush logs as well as provide exit status. */
332*bf6873c5SCy Schubert     if (process->is_child)
333*bf6873c5SCy Schubert         diag("stopped process %lu (exit status %d)", pid, status);
334*bf6873c5SCy Schubert     else
335*bf6873c5SCy Schubert         diag("stopped process %lu", pid);
336*bf6873c5SCy Schubert 
337*bf6873c5SCy Schubert     /* Remove the log and PID file. */
338*bf6873c5SCy Schubert     diag_file_remove(process->logfile);
339*bf6873c5SCy Schubert     unlink(process->pidfile);
340*bf6873c5SCy Schubert     unlink(process->logfile);
341*bf6873c5SCy Schubert 
342*bf6873c5SCy Schubert     /* Free resources. */
343*bf6873c5SCy Schubert     process_free(process);
344*bf6873c5SCy Schubert }
345*bf6873c5SCy Schubert 
346*bf6873c5SCy Schubert 
347*bf6873c5SCy Schubert /*
348*bf6873c5SCy Schubert  * Stop all running processes.  This is called as a cleanup handler during
349*bf6873c5SCy Schubert  * process shutdown.  The first argument, which says whether the test was
350*bf6873c5SCy Schubert  * successful, is ignored, since the same actions should be performed
351*bf6873c5SCy Schubert  * regardless.  The second argument says whether this is the primary process,
352*bf6873c5SCy Schubert  * in which case we do the full shutdown.  Otherwise, we only free resources
353*bf6873c5SCy Schubert  * but don't stop the process.
354*bf6873c5SCy Schubert  */
355*bf6873c5SCy Schubert static void
process_stop_all(int success UNUSED,int primary)356*bf6873c5SCy Schubert process_stop_all(int success UNUSED, int primary)
357*bf6873c5SCy Schubert {
358*bf6873c5SCy Schubert     while (processes != NULL) {
359*bf6873c5SCy Schubert         if (primary)
360*bf6873c5SCy Schubert             process_stop(processes);
361*bf6873c5SCy Schubert         else
362*bf6873c5SCy Schubert             process_free(processes);
363*bf6873c5SCy Schubert     }
364*bf6873c5SCy Schubert }
365*bf6873c5SCy Schubert 
366*bf6873c5SCy Schubert 
367*bf6873c5SCy Schubert /*
368*bf6873c5SCy Schubert  * Read the PID of a process from a file.  This is necessary when running
369*bf6873c5SCy Schubert  * under fakeroot to get the actual PID of the remctld process.
370*bf6873c5SCy Schubert  */
371*bf6873c5SCy Schubert static pid_t
read_pidfile(const char * path)372*bf6873c5SCy Schubert read_pidfile(const char *path)
373*bf6873c5SCy Schubert {
374*bf6873c5SCy Schubert     FILE *file;
375*bf6873c5SCy Schubert     char buffer[BUFSIZ];
376*bf6873c5SCy Schubert     long pid;
377*bf6873c5SCy Schubert 
378*bf6873c5SCy Schubert     file = fopen(path, "r");
379*bf6873c5SCy Schubert     if (file == NULL)
380*bf6873c5SCy Schubert         sysbail("cannot open %s", path);
381*bf6873c5SCy Schubert     if (fgets(buffer, sizeof(buffer), file) == NULL)
382*bf6873c5SCy Schubert         sysbail("cannot read from %s", path);
383*bf6873c5SCy Schubert     fclose(file);
384*bf6873c5SCy Schubert     pid = strtol(buffer, NULL, 10);
385*bf6873c5SCy Schubert     if (pid <= 0)
386*bf6873c5SCy Schubert         bail("cannot read PID from %s", path);
387*bf6873c5SCy Schubert     return (pid_t) pid;
388*bf6873c5SCy Schubert }
389*bf6873c5SCy Schubert 
390*bf6873c5SCy Schubert 
391*bf6873c5SCy Schubert /*
392*bf6873c5SCy Schubert  * Start a process and return its status information.  The status information
393*bf6873c5SCy Schubert  * is also stored in the global processes linked list so that it can be
394*bf6873c5SCy Schubert  * stopped automatically on program exit.
395*bf6873c5SCy Schubert  *
396*bf6873c5SCy Schubert  * The boolean argument says whether to start the process under fakeroot.  If
397*bf6873c5SCy Schubert  * true, PATH_FAKEROOT must be defined, generally by Autoconf.  If it's not
398*bf6873c5SCy Schubert  * found, call skip_all.
399*bf6873c5SCy Schubert  *
400*bf6873c5SCy Schubert  * This is a helper function for process_start and process_start_fakeroot.
401*bf6873c5SCy Schubert  */
402*bf6873c5SCy Schubert static struct process *
process_start_internal(const char * const argv[],const char * pidfile,bool fakeroot)403*bf6873c5SCy Schubert process_start_internal(const char *const argv[], const char *pidfile,
404*bf6873c5SCy Schubert                        bool fakeroot)
405*bf6873c5SCy Schubert {
406*bf6873c5SCy Schubert     size_t i;
407*bf6873c5SCy Schubert     int log_fd;
408*bf6873c5SCy Schubert     const char *name;
409*bf6873c5SCy Schubert     struct timeval tv;
410*bf6873c5SCy Schubert     struct process *process;
411*bf6873c5SCy Schubert     const char **fakeroot_argv = NULL;
412*bf6873c5SCy Schubert     const char *path_fakeroot = PATH_FAKEROOT;
413*bf6873c5SCy Schubert 
414*bf6873c5SCy Schubert     /* Check prerequisites. */
415*bf6873c5SCy Schubert     if (fakeroot && path_fakeroot[0] == '\0')
416*bf6873c5SCy Schubert         skip_all("fakeroot not found");
417*bf6873c5SCy Schubert 
418*bf6873c5SCy Schubert     /* Create the process struct and log file. */
419*bf6873c5SCy Schubert     process = bcalloc(1, sizeof(struct process));
420*bf6873c5SCy Schubert     process->pidfile = bstrdup(pidfile);
421*bf6873c5SCy Schubert     process->tmpdir = test_tmpdir();
422*bf6873c5SCy Schubert     name = strrchr(argv[0], '/');
423*bf6873c5SCy Schubert     if (name != NULL)
424*bf6873c5SCy Schubert         name++;
425*bf6873c5SCy Schubert     else
426*bf6873c5SCy Schubert         name = argv[0];
427*bf6873c5SCy Schubert     basprintf(&process->logfile, "%s/%s.log.XXXXXX", process->tmpdir, name);
428*bf6873c5SCy Schubert     log_fd = mkstemp(process->logfile);
429*bf6873c5SCy Schubert     if (log_fd < 0)
430*bf6873c5SCy Schubert         sysbail("cannot create log file for %s", argv[0]);
431*bf6873c5SCy Schubert 
432*bf6873c5SCy Schubert     /* If using fakeroot, rewrite argv accordingly. */
433*bf6873c5SCy Schubert     if (fakeroot) {
434*bf6873c5SCy Schubert         for (i = 0; argv[i] != NULL; i++)
435*bf6873c5SCy Schubert             ;
436*bf6873c5SCy Schubert         fakeroot_argv = bcalloc(2 + i + 1, sizeof(const char *));
437*bf6873c5SCy Schubert         fakeroot_argv[0] = path_fakeroot;
438*bf6873c5SCy Schubert         fakeroot_argv[1] = "--";
439*bf6873c5SCy Schubert         for (i = 0; argv[i] != NULL; i++)
440*bf6873c5SCy Schubert             fakeroot_argv[i + 2] = argv[i];
441*bf6873c5SCy Schubert         fakeroot_argv[i + 2] = NULL;
442*bf6873c5SCy Schubert         argv = fakeroot_argv;
443*bf6873c5SCy Schubert     }
444*bf6873c5SCy Schubert 
445*bf6873c5SCy Schubert     /*
446*bf6873c5SCy Schubert      * Fork off the child process, redirect its standard output and standard
447*bf6873c5SCy Schubert      * error to the log file, and then exec the program.
448*bf6873c5SCy Schubert      */
449*bf6873c5SCy Schubert     process->pid = fork();
450*bf6873c5SCy Schubert     if (process->pid < 0)
451*bf6873c5SCy Schubert         sysbail("fork failed");
452*bf6873c5SCy Schubert     else if (process->pid == 0) {
453*bf6873c5SCy Schubert         if (dup2(log_fd, STDOUT_FILENO) < 0)
454*bf6873c5SCy Schubert             sysbail("cannot redirect standard output");
455*bf6873c5SCy Schubert         if (dup2(log_fd, STDERR_FILENO) < 0)
456*bf6873c5SCy Schubert             sysbail("cannot redirect standard error");
457*bf6873c5SCy Schubert         close(log_fd);
458*bf6873c5SCy Schubert         if (execv(argv[0], (char *const *) argv) < 0)
459*bf6873c5SCy Schubert             sysbail("exec of %s failed", argv[0]);
460*bf6873c5SCy Schubert     }
461*bf6873c5SCy Schubert     close(log_fd);
462*bf6873c5SCy Schubert     free(fakeroot_argv);
463*bf6873c5SCy Schubert 
464*bf6873c5SCy Schubert     /*
465*bf6873c5SCy Schubert      * In the parent.  Wait for the child to start by watching for the PID
466*bf6873c5SCy Schubert      * file to appear in 100ms intervals.
467*bf6873c5SCy Schubert      */
468*bf6873c5SCy Schubert     for (i = 0; i < PROCESS_WAIT * 10 && access(pidfile, F_OK) != 0; i++) {
469*bf6873c5SCy Schubert         tv.tv_sec = 0;
470*bf6873c5SCy Schubert         tv.tv_usec = 100000;
471*bf6873c5SCy Schubert         select(0, NULL, NULL, NULL, &tv);
472*bf6873c5SCy Schubert     }
473*bf6873c5SCy Schubert 
474*bf6873c5SCy Schubert     /*
475*bf6873c5SCy Schubert      * If the PID file still hasn't appeared after ten seconds, attempt to
476*bf6873c5SCy Schubert      * kill the process and then bail.
477*bf6873c5SCy Schubert      */
478*bf6873c5SCy Schubert     if (access(pidfile, F_OK) != 0) {
479*bf6873c5SCy Schubert         kill(process->pid, SIGTERM);
480*bf6873c5SCy Schubert         alarm(5);
481*bf6873c5SCy Schubert         waitpid(process->pid, NULL, 0);
482*bf6873c5SCy Schubert         alarm(0);
483*bf6873c5SCy Schubert         bail("cannot start %s", argv[0]);
484*bf6873c5SCy Schubert     }
485*bf6873c5SCy Schubert 
486*bf6873c5SCy Schubert     /*
487*bf6873c5SCy Schubert      * Read the PID back from the PID file.  This usually isn't necessary for
488*bf6873c5SCy Schubert      * non-forking daemons, but always doing this makes this function general,
489*bf6873c5SCy Schubert      * and it's required when running under fakeroot.
490*bf6873c5SCy Schubert      */
491*bf6873c5SCy Schubert     if (fakeroot)
492*bf6873c5SCy Schubert         process->pid = read_pidfile(pidfile);
493*bf6873c5SCy Schubert     process->is_child = !fakeroot;
494*bf6873c5SCy Schubert 
495*bf6873c5SCy Schubert     /* Register the log file as a source of diag messages. */
496*bf6873c5SCy Schubert     diag_file_add(process->logfile);
497*bf6873c5SCy Schubert 
498*bf6873c5SCy Schubert     /*
499*bf6873c5SCy Schubert      * Add the process to our global list and set our cleanup handler if this
500*bf6873c5SCy Schubert      * is the first process we started.
501*bf6873c5SCy Schubert      */
502*bf6873c5SCy Schubert     if (processes == NULL)
503*bf6873c5SCy Schubert         test_cleanup_register(process_stop_all);
504*bf6873c5SCy Schubert     process->next = processes;
505*bf6873c5SCy Schubert     processes = process;
506*bf6873c5SCy Schubert 
507*bf6873c5SCy Schubert     /* All done. */
508*bf6873c5SCy Schubert     return process;
509*bf6873c5SCy Schubert }
510*bf6873c5SCy Schubert 
511*bf6873c5SCy Schubert 
512*bf6873c5SCy Schubert /*
513*bf6873c5SCy Schubert  * Start a process and return the opaque process struct.  The process must
514*bf6873c5SCy Schubert  * create pidfile with its PID when startup is complete.
515*bf6873c5SCy Schubert  */
516*bf6873c5SCy Schubert struct process *
process_start(const char * const argv[],const char * pidfile)517*bf6873c5SCy Schubert process_start(const char *const argv[], const char *pidfile)
518*bf6873c5SCy Schubert {
519*bf6873c5SCy Schubert     return process_start_internal(argv, pidfile, false);
520*bf6873c5SCy Schubert }
521*bf6873c5SCy Schubert 
522*bf6873c5SCy Schubert 
523*bf6873c5SCy Schubert /*
524*bf6873c5SCy Schubert  * Start a process under fakeroot and return the opaque process struct.  If
525*bf6873c5SCy Schubert  * fakeroot is not available, calls skip_all.  The process must create pidfile
526*bf6873c5SCy Schubert  * with its PID when startup is complete.
527*bf6873c5SCy Schubert  */
528*bf6873c5SCy Schubert struct process *
process_start_fakeroot(const char * const argv[],const char * pidfile)529*bf6873c5SCy Schubert process_start_fakeroot(const char *const argv[], const char *pidfile)
530*bf6873c5SCy Schubert {
531*bf6873c5SCy Schubert     return process_start_internal(argv, pidfile, true);
532*bf6873c5SCy Schubert }
533