2 * Copyright (C) 2017-2019 Red Hat Inc.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
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.
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.
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
33 /* Test socket activation.
35 * We cannot use the test framework for this since the framework
36 * always uses the -U flag which is incompatible with socket
37 * activation. Unfortunately this does mean we duplicate some code
38 * from the test framework.
40 * It's *almost* possible to test this from a shell script
41 * (cf. test-ip.sh) but as far as I can tell setting LISTEN_PID
42 * correctly is impossible from shell.
56 #include <sys/types.h>
57 #include <sys/socket.h>
62 /* For this file, we don't care if fds are marked cloexec; leaking is okay. */
63 #define SOCK_CLOEXEC 0
66 #define FIRST_SOCKET_ACTIVATION_FD 3
68 #define NBDKIT_START_TIMEOUT 30 /* seconds */
70 /* Declare program_name. */
71 #if HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME == 1
73 #define program_name program_invocation_short_name
75 #define program_name "nbdkit"
78 static char tmpdir
[] = "/tmp/nbdkitXXXXXX";
79 static char sockpath
[] = "/tmp/nbdkitXXXXXX/sock";
80 static char pidpath
[] = "/tmp/nbdkitXXXXXX/pid";
92 /* Check the status of nbdkit is normal on exit. */
93 if (waitpid (pid
, &status
, 0) == -1) {
97 if (WIFEXITED (status
) && WEXITSTATUS (status
) != 0) {
98 _exit (WEXITSTATUS (status
));
100 if (WIFSIGNALED (status
)) {
101 /* Note that nbdkit is supposed to catch the signal we send and
102 * exit cleanly, so the following shouldn't happen.
104 fprintf (stderr
, "nbdkit terminated by signal %d\n", WTERMSIG (status
));
105 _exit (EXIT_FAILURE
);
107 if (WIFSTOPPED (status
)) {
108 fprintf (stderr
, "nbdkit stopped by signal %d\n", WSTOPSIG (status
));
109 _exit (EXIT_FAILURE
);
119 main (int argc
, char *argv
[])
122 struct sockaddr_un addr
;
127 if (mkdtemp (tmpdir
) == NULL
) {
131 len
= strlen (tmpdir
);
132 memcpy (sockpath
, tmpdir
, len
);
133 memcpy (pidpath
, tmpdir
, len
);
137 /* Open the listening socket which will be passed into nbdkit. */
138 sock
= socket (AF_UNIX
, SOCK_STREAM
/* NB do not use SOCK_CLOEXEC */, 0);
144 addr
.sun_family
= AF_UNIX
;
145 len
= strlen (sockpath
);
146 memcpy (addr
.sun_path
, sockpath
, len
+1 /* trailing \0 */);
148 if (bind (sock
, (struct sockaddr
*) &addr
, sizeof addr
) == -1) {
153 if (listen (sock
, 1) == -1) {
158 if (sock
!= FIRST_SOCKET_ACTIVATION_FD
) {
159 if (dup2 (sock
, FIRST_SOCKET_ACTIVATION_FD
) == -1) {
173 /* Run nbdkit in the child. */
174 setenv ("LISTEN_FDS", "1", 1);
175 snprintf (pid_str
, sizeof pid_str
, "%d", (int) getpid ());
176 setenv ("LISTEN_PID", pid_str
, 1);
184 perror ("exec: nbdkit");
185 _exit (EXIT_FAILURE
);
188 /* We don't need the listening socket now. */
191 /* Wait for the pidfile to turn up, which indicates that nbdkit has
192 * started up successfully and is ready to serve requests. However
193 * if 'pid' exits in this time it indicates a failure to start up.
194 * Also there is a timeout in case nbdkit hangs.
196 for (i
= 0; i
< NBDKIT_START_TIMEOUT
; ++i
) {
197 if (waitpid (pid
, NULL
, WNOHANG
) == pid
)
200 if (kill (pid
, 0) == -1) {
201 if (errno
== ESRCH
) {
204 "%s FAILED: nbdkit exited before starting to serve files\n",
212 if (access (pidpath
, F_OK
) == 0)
218 /* Now nbdkit is supposed to be listening on the Unix domain socket
219 * (which it got via the listening socket that we passed down to it,
220 * not from the path), so we should be able to connect to the Unix
221 * domain socket by its path and receive an NBD magic string.
223 sock
= socket (AF_UNIX
, SOCK_STREAM
|SOCK_CLOEXEC
, 0);
229 /* Reuse addr which was set up above. */
230 if (connect (sock
, (struct sockaddr
*) &addr
, sizeof addr
) == -1) {
235 if (read (sock
, magic
, 8) != 8) {
240 if (memcmp (magic
, "NBDMAGIC", 8) != 0) {
241 fprintf (stderr
, "%s FAILED: did not read magic string from server\n",
248 /* Test succeeded. */