Version 1.33.1.
[nbdkit.git] / tests / test.c
blob0c1cbc0d8a73557ec175e9e870fa76be4ea2d18b
1 /* nbdkit
2 * Copyright (C) 2013 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
6 * met:
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
30 * SUCH DAMAGE.
33 #include <config.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <stdbool.h>
38 #include <stdarg.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <signal.h>
42 #include <errno.h>
44 #undef NDEBUG /* Keep test strong even for nbdkit built without assertions */
45 #include <assert.h>
47 #include <sys/types.h>
49 #ifdef HAVE_SYS_WAIT_H
50 #include <sys/wait.h>
51 #endif
53 #include "test.h"
55 #ifndef WIN32
57 /* 'test_start_nbdkit' below makes assumptions about the format of
58 * these strings.
60 #define TEST_NBDKIT_TEMPLATE "/tmp/nbdkitXXXXXX"
61 struct test_nbdkit {
62 char tmpdir[ 17 + 1]; /* template, NUL */
63 char sockpath[ 17 + 5 + 1]; /* template, "/sock", NUL */
64 char unixsockpath[5 + 17 + 5 + 1]; /* "unix:", template, "/sock", NUL */
65 char pidpath[ 17 + 4 + 1]; /* template, "/pid", NUL */
66 pid_t pid;
67 struct test_nbdkit *next;
69 const struct test_nbdkit template = {
70 .tmpdir = TEST_NBDKIT_TEMPLATE,
71 .sockpath = TEST_NBDKIT_TEMPLATE "/sock",
72 .unixsockpath = "unix:" TEST_NBDKIT_TEMPLATE "/sock",
73 .pidpath = TEST_NBDKIT_TEMPLATE "/pid",
76 static struct test_nbdkit *head;
78 pid_t pid = 0;
79 const char *sock = NULL;
80 const char *server[2] = { NULL, NULL };
82 static void
83 cleanup (void)
85 int status;
86 struct test_nbdkit *next;
87 const char *s;
89 while (head) {
90 if (head->pid > 0) {
91 assert (!pid || pid == head->pid);
92 pid = 0;
94 /* This improves the stability when running the tests under
95 * valgrind. We have to wait a little for nbdkit close
96 * callbacks to run.
98 s = getenv ("NBDKIT_VALGRIND");
99 if (s && strcmp (s, "1") == 0)
100 sleep (5);
102 kill (head->pid, SIGTERM);
104 /* Check the status of nbdkit is normal on exit. */
105 if (waitpid (head->pid, &status, 0) == -1) {
106 perror ("waitpid");
107 _exit (EXIT_FAILURE);
109 if (WIFEXITED (status) && WEXITSTATUS (status) != 0) {
110 _exit (WEXITSTATUS (status));
112 if (WIFSIGNALED (status)) {
113 /* Note that nbdkit is supposed to catch the signal we send and
114 * exit cleanly, so the following shouldn't happen.
116 fprintf (stderr, "nbdkit terminated by signal %d\n", WTERMSIG (status));
117 _exit (EXIT_FAILURE);
119 if (WIFSTOPPED (status)) {
120 fprintf (stderr, "nbdkit stopped by signal %d\n", WSTOPSIG (status));
121 _exit (EXIT_FAILURE);
125 unlink (head->pidpath);
126 unlink (head->sockpath);
127 rmdir (head->tmpdir);
129 next = head->next;
130 free (head);
131 head = next;
136 test_start_nbdkit (const char *arg, ...)
138 size_t i, len;
139 struct test_nbdkit *kit = malloc (sizeof *kit);
140 bool exists;
142 if (!kit) {
143 perror ("malloc");
144 return -1;
146 *kit = template;
147 if (mkdtemp (kit->tmpdir) == NULL) {
148 perror ("mkdtemp");
149 free (kit);
150 return -1;
152 len = strlen (kit->tmpdir);
153 memcpy (kit->sockpath, kit->tmpdir, len);
154 memcpy (kit->unixsockpath+5, kit->tmpdir, len);
155 memcpy (kit->pidpath, kit->tmpdir, len);
157 kit->pid = fork ();
158 if (kit->pid == 0) { /* Child (nbdkit). */
159 const char *p;
160 #define MAX_ARGS 64
161 const char *argv[MAX_ARGS+1];
162 va_list args;
164 argv[0] = "nbdkit";
165 argv[1] = "-U";
166 argv[2] = kit->sockpath;
167 argv[3] = "-P";
168 argv[4] = kit->pidpath;
169 argv[5] = "-f";
170 argv[6] = "-v";
171 argv[7] = arg;
172 i = 8;
174 va_start (args, arg);
175 while ((p = va_arg (args, const char *)) != NULL) {
176 if (i >= MAX_ARGS)
177 abort ();
178 argv[i] = p;
179 ++i;
181 va_end (args);
182 argv[i] = NULL;
184 execvp ("nbdkit", (char **) argv);
185 perror ("exec: nbdkit");
186 _exit (EXIT_FAILURE);
189 /* Ensure nbdkit is killed and temporary files are deleted when the
190 * main program exits.
192 if (head)
193 kit->next = head;
194 else
195 atexit (cleanup);
196 head = kit;
197 pid = kit->pid;
198 sock = kit->sockpath;
199 server[0] = kit->unixsockpath;
201 /* Wait for the pidfile to turn up, which indicates that nbdkit has
202 * started up successfully and is ready to serve requests. However
203 * if 'pid' exits in this time it indicates a failure to start up.
204 * Also there is a timeout in case nbdkit hangs.
206 for (i = 0; i < NBDKIT_START_TIMEOUT; ++i) {
207 if (waitpid (pid, NULL, WNOHANG) == pid)
208 goto early_exit;
210 if (kill (pid, 0) == -1) {
211 if (errno == ESRCH) {
212 early_exit:
213 fprintf (stderr,
214 "%s FAILED: nbdkit exited before starting to serve files\n",
215 program_name);
216 pid = 0;
217 return -1;
219 perror ("kill");
222 exists = access (kit->pidpath, F_OK) == 0;
223 if (exists)
224 break;
226 sleep (1);
229 if (!exists) {
230 fprintf (stderr,
231 "%s: nbdkit did not create pidfile %s within "
232 "%d seconds, continuing anyway\n",
233 program_name, kit->pidpath, NBDKIT_START_TIMEOUT);
236 return 0;
239 #else /* WIN32 */
241 /* All of the above code will require a lot of porting work for
242 * Windows. At the moment the test gets skipped.
245 test_start_nbdkit (const char *arg, ...)
247 fprintf (stderr, "%s: test skipped because not ported to Windows.\n",
248 program_name);
249 exit (77);
252 #endif