1 /* nbdkit
2  * Copyright (C) 2017 Red Hat Inc.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *
11  * * Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  *
15  * * Neither the name of Red Hat nor the names of its contributors may be
16  * used to endorse or promote products derived from this software without
17  * specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <config.h>
34 
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <stdint.h>
38 #include <inttypes.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <sys/types.h>
42 #include <sys/wait.h>
43 #include <errno.h>
44 
45 #include "exit-with-parent.h"
46 #include "test.h"
47 
48 static char pidpath[] = "/tmp/nbdkitpidXXXXXX";
49 
50 static void run_test (void);
51 
52 int
main(int argc,char * argv[])53 main (int argc, char *argv[])
54 {
55 #ifndef HAVE_EXIT_WITH_PARENT
56   printf ("--exit-with-parent is not implemented on this platform, skipping\n");
57   exit (77);
58 #else
59   run_test ();
60   exit (EXIT_SUCCESS);
61 #endif
62 }
63 
64 static void
run_test(void)65 run_test (void)
66 {
67   pid_t cpid, nbdpid;
68   int i, fd, status;
69   FILE *fp;
70   ssize_t r;
71   size_t n;
72   char *pidstr;
73 
74   fd = mkstemp (pidpath);
75   if (fd == -1) {
76     perror ("mkstemp");
77     exit (EXIT_FAILURE);
78   }
79   close (fd);
80   unlink (pidpath);
81 
82   /* We're going to create:
83    *
84    *    monitoring process (this)
85    *       |
86    *       `--- child process waits for nbdkit to start then exits (cpid)
87    *                |
88    *                `--- exec nbdkit --exit-with-parent (pidpath)
89    *
90    * We can read the nbdkit PID in the monitoring process using
91    * the pidpath file.
92    */
93   cpid = fork ();
94   if (cpid == 0) {              /* child process */
95     nbdpid = fork ();
96     if (nbdpid == 0) {             /* exec nbdkit process */
97       const char *argv[] = {
98         "nbdkit", "-U", "-", "-P", pidpath, "-f", "--exit-with-parent",
99         "example1",
100         NULL
101       };
102 
103       execvp ("nbdkit", (char **) argv);
104       perror ("exec: nbdkit");
105       _exit (EXIT_FAILURE);
106     }
107 
108     /* Wait for the pidfile to turn up, which indicates that nbdkit has
109      * started up successfully and is ready to serve requests.  However
110      * if 'nbdpid' exits in this time it indicates a failure to start up.
111      * Also there is a timeout in case nbdkit hangs.
112      */
113     for (i = 0; i < NBDKIT_START_TIMEOUT; ++i) {
114       if (waitpid (nbdpid, NULL, WNOHANG) == nbdpid)
115         goto early_exit;
116 
117       if (kill (nbdpid, 0) == -1) {
118         if (errno == ESRCH) {
119         early_exit:
120           fprintf (stderr,
121                    "%s FAILED: nbdkit exited before starting to serve files\n",
122                    program_name);
123           nbdpid = 0;
124           exit (EXIT_FAILURE);
125         }
126         perror ("kill");
127       }
128 
129       if (access (pidpath, F_OK) == 0)
130         break;
131 
132       sleep (1);
133     }
134 
135     /* nbdkit is now running, check that --exit-with-parent works
136      * by exiting abruptly here.
137      */
138     _exit (EXIT_SUCCESS);
139   }
140 
141   /* Monitoring process. */
142   if (waitpid (cpid, &status, 0) == -1) {
143     perror ("waitpid (cpid)");
144     exit (EXIT_FAILURE);
145   }
146   if (WIFEXITED (status) && WEXITSTATUS (status) != 1)
147     exit (WEXITSTATUS (status));
148   if (WIFSIGNALED (status)) {
149     fprintf (stderr, "child terminated by signal %d\n", WTERMSIG (status));
150     exit (EXIT_FAILURE);
151   }
152   if (WIFSTOPPED (status)) {
153     fprintf (stderr, "child stopped by signal %d\n", WSTOPSIG (status));
154     exit (EXIT_FAILURE);
155   }
156 
157   /* Get the PID of nbdkit from the pidpath file. */
158   fp = fopen (pidpath, "r");
159   if (fp == NULL) {
160     perror (pidpath);
161     exit (EXIT_FAILURE);
162   }
163   r = getline (&pidstr, &n, fp);
164   if (r == -1) {
165     perror ("read");
166     exit (EXIT_FAILURE);
167   }
168   if (sscanf (pidstr, "%d", &nbdpid) != 1) {
169     fprintf (stderr, "could not read nbdkit PID from -P pidfile (%s)\n",
170              pidpath);
171     exit (EXIT_FAILURE);
172   }
173   fclose (fp);
174   free (pidstr);
175   unlink (pidpath);
176 
177   /* We expect PID to go away, but it might take a few seconds. */
178   for (i = 0; i < NBDKIT_START_TIMEOUT; ++i) {
179     if (kill (nbdpid, 0) == -1) {
180       if (errno == ESRCH) goto done; /* good - gone away */
181       perror ("kill");
182       exit (EXIT_FAILURE);
183     }
184 
185     sleep (1);
186   }
187 
188   fprintf (stderr, "--exit-with-parent does not appear to work\n");
189   exit (EXIT_FAILURE);
190 
191  done:
192   ;
193 }
194