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>
61 #include "byte-swapping.h"
62 #include "nbd-protocol.h"
65 /* For this file, we don't care if fds are marked cloexec; leaking is okay. */
66 #define SOCK_CLOEXEC 0
69 #define FIRST_SOCKET_ACTIVATION_FD 3
71 #define NBDKIT_START_TIMEOUT 30 /* seconds */
73 /* Declare program_name. */
74 #if HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME == 1
76 #define program_name program_invocation_short_name
78 #define program_name "nbdkit"
81 static char tmpdir
[] = "/tmp/nbdkitXXXXXX";
82 static char sockpath
[] = "/tmp/nbdkitXXXXXX/sock";
83 static char pidpath
[] = "/tmp/nbdkitXXXXXX/pid";
99 main (int argc
, char *argv
[])
102 struct sockaddr_un addr
;
107 if (mkdtemp (tmpdir
) == NULL
) {
111 len
= strlen (tmpdir
);
112 memcpy (sockpath
, tmpdir
, len
);
113 memcpy (pidpath
, tmpdir
, len
);
117 /* Open the listening socket which will be passed into nbdkit. */
118 sock
= socket (AF_UNIX
, SOCK_STREAM
/* NB do not use SOCK_CLOEXEC */, 0);
124 addr
.sun_family
= AF_UNIX
;
125 len
= strlen (sockpath
);
126 memcpy (addr
.sun_path
, sockpath
, len
+1 /* trailing \0 */);
128 if (bind (sock
, (struct sockaddr
*) &addr
, sizeof addr
) == -1) {
133 if (listen (sock
, 1) == -1) {
138 if (sock
!= FIRST_SOCKET_ACTIVATION_FD
) {
139 if (dup2 (sock
, FIRST_SOCKET_ACTIVATION_FD
) == -1) {
153 /* Run nbdkit in the child. */
154 setenv ("LISTEN_FDS", "1", 1);
155 snprintf (pid_str
, sizeof pid_str
, "%d", (int) getpid ());
156 setenv ("LISTEN_PID", pid_str
, 1);
164 perror ("exec: nbdkit");
165 _exit (EXIT_FAILURE
);
168 /* We don't need the listening socket now. */
171 /* Wait for the pidfile to turn up, which indicates that nbdkit has
172 * started up successfully and is ready to serve requests. However
173 * if 'pid' exits in this time it indicates a failure to start up.
174 * Also there is a timeout in case nbdkit hangs.
176 for (i
= 0; i
< NBDKIT_START_TIMEOUT
; ++i
) {
177 if (waitpid (pid
, NULL
, WNOHANG
) == pid
)
180 if (kill (pid
, 0) == -1) {
181 if (errno
== ESRCH
) {
184 "%s FAILED: nbdkit exited before starting to serve files\n",
192 if (access (pidpath
, F_OK
) == 0)
198 /* Now nbdkit is supposed to be listening on the Unix domain socket
199 * (which it got via the listening socket that we passed down to it,
200 * not from the path), so we should be able to connect to the Unix
201 * domain socket by its path and receive an NBD magic string.
203 sock
= socket (AF_UNIX
, SOCK_STREAM
|SOCK_CLOEXEC
, 0);
209 /* Reuse addr which was set up above. */
210 if (connect (sock
, (struct sockaddr
*) &addr
, sizeof addr
) == -1) {
215 if (read (sock
, &magic
, sizeof magic
) != sizeof magic
) {
220 if (be64toh (magic
) != NBD_MAGIC
) {
221 fprintf (stderr
, "%s FAILED: did not read magic string from server\n",
228 /* Test succeeded. */