2 * Copyright (C) 2018-2021 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 /* This test constructs a plugin and 3 layers of filters:
35 * NBD ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌────────┐
36 * client ───▶│ filter3 │───▶│ filter2 │───▶│ filter1 │───▶│ plugin │
37 * request └─────────┘ └─────────┘ └─────────┘ └────────┘
39 * We then run every possible request and ensure that each method in
40 * each filter and the plugin is called in the right order.
53 #include <sys/types.h>
60 #include "byte-swapping.h"
63 /* Declare program_name. */
64 #if HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME == 1
66 #define program_name program_invocation_short_name
68 #define program_name "nbdkit"
73 static void *start_log_capture (void *);
74 static void log_verify_seen (const char *msg
);
75 static void log_verify_seen_in_order (const char *msg
, ...)
76 __attribute__((sentinel
));
77 static void log_free (void);
78 static void short_sleep (void);
80 static int extent (void *opaque
, const char *context
, uint64_t offset
,
81 uint32_t *entries
, size_t nr_entries
, int *error
)
86 #if LIBNBD_HAVE_NBD_SET_OPT_MODE
87 static int export (void *opaque
, const char *name
, const char *desc
)
94 main (int argc
, char *argv
[])
96 struct nbd_handle
*nbd
;
104 if (system ("nbdkit --exit-with-parent --version") != 0) {
105 printf ("%s: this test requires --exit-with-parent functionality\n",
110 /* Prepare libnbd. */
111 fprintf (stderr
, "%s: beginning test\n", program_name
);
114 fprintf (stderr
, "nbd_create: %s\n", nbd_get_error ());
117 if (nbd_add_meta_context (nbd
, LIBNBD_CONTEXT_BASE_ALLOCATION
) == -1) {
118 fprintf (stderr
, "nbd_add_meta_context: %s\n", nbd_get_error ());
121 #if LIBNBD_HAVE_NBD_SET_OPT_MODE
122 if (nbd_set_opt_mode (nbd
, true) == -1) {
123 fprintf (stderr
, "nbd_set_opt_mode: %s\n", nbd_get_error ());
128 /* Start a thread which will just listen on the pipe and
129 * place the log messages in a memory buffer.
132 r
= pipe2 (pfd
, O_CLOEXEC
);
134 /* Just ignore the O_CLOEXEC requirement, it's only a test. */
141 err
= pthread_create (&thread
, NULL
, start_log_capture
, &pfd
[0]);
144 perror ("pthread_create");
147 err
= pthread_detach (thread
);
150 perror ("pthread_detach");
154 /* Shuffle stderr. Until we restore it later, avoid direct use of stderr. */
155 orig_stderr
= fcntl (STDERR_FILENO
, F_DUPFD_CLOEXEC
, STDERR_FILENO
);
156 if (orig_stderr
== -1) {
160 if (dup2 (pfd
[1], STDERR_FILENO
) == -1) {
161 dprintf (orig_stderr
, "dup2: %s\n", strerror (errno
));
166 if (nbd_connect_command (nbd
,
168 "nbdkit", "--exit-with-parent", "-fvns",
169 /* Because of asynchronous shutdown with
170 * threads, finalize isn't reliably
171 * called unless we disable parallel.
174 "--filter", ".libs/test-layers-filter3." SOEXT
,
175 "--filter", ".libs/test-layers-filter2." SOEXT
,
176 "--filter", ".libs/test-layers-filter1." SOEXT
,
177 ".libs/test-layers-plugin." SOEXT
,
180 dprintf (orig_stderr
, "nbd_connect_command: %s\n", nbd_get_error ());
184 /* Restore normal stderr, now that child is forked. */
186 dup2 (orig_stderr
, STDERR_FILENO
);
190 fprintf (stderr
, "%s: nbdkit passed preconnect\n", program_name
);
192 /* Note for the purposes of this test we're not very careful about
193 * checking for errors (except for the bare minimum). This is
194 * because we can be certain about exactly which server we are
195 * connecting to and what it supports. Don't use this as example
196 * code for connecting to NBD servers.
198 * Expect to receive newstyle handshake.
200 if (strcmp (nbd_get_protocol (nbd
), "newstyle-fixed") != 0) {
201 fprintf (stderr
, "%s: unexpected NBDMAGIC or version\n",
206 /* Plugin and 3 filters should run the load method in any order. */
207 log_verify_seen ("test_layers_plugin_load");
208 log_verify_seen ("filter1: test_layers_filter_load");
209 log_verify_seen ("filter2: test_layers_filter_load");
210 log_verify_seen ("filter3: test_layers_filter_load");
212 /* config methods called in order. */
213 log_verify_seen_in_order
214 ("testlayersfilter3: config key=foo, value=bar",
215 "filter3: test_layers_filter_config",
216 "testlayersfilter2: config key=foo, value=bar",
217 "filter2: test_layers_filter_config",
218 "testlayersfilter1: config key=foo, value=bar",
219 "filter1: test_layers_filter_config",
220 "testlayersplugin: config key=foo, value=bar",
221 "test_layers_plugin_config",
224 /* config_complete methods called in order. */
225 log_verify_seen_in_order
226 ("testlayersfilter3: config_complete",
227 "filter3: test_layers_filter_config_complete",
228 "testlayersfilter2: config_complete",
229 "filter2: test_layers_filter_config_complete",
230 "testlayersfilter1: config_complete",
231 "filter1: test_layers_filter_config_complete",
232 "testlayersplugin: config_complete",
233 "test_layers_plugin_config_complete",
236 /* thread_model methods called in inner-to-outer order. */
237 log_verify_seen_in_order
238 ("test_layers_plugin_thread_model",
239 "filter1: test_layers_filter_thread_model",
240 "filter2: test_layers_filter_thread_model",
241 "filter3: test_layers_filter_thread_model",
244 /* get_ready methods called in inner-to-outer order. */
245 log_verify_seen_in_order
246 ("testlayersplugin: get_ready",
247 "test_layers_plugin_get_ready",
248 "testlayersfilter1: get_ready",
249 "filter1: test_layers_filter_get_ready",
250 "testlayersfilter2: get_ready",
251 "filter2: test_layers_filter_get_ready",
252 "testlayersfilter3: get_ready",
253 "filter3: test_layers_filter_get_ready",
256 /* after_fork methods called in inner-to-outer order. */
257 log_verify_seen_in_order
258 ("testlayersplugin: after_fork",
259 "test_layers_plugin_after_fork",
260 "testlayersfilter1: after_fork",
261 "filter1: test_layers_filter_after_fork",
262 "testlayersfilter2: after_fork",
263 "filter2: test_layers_filter_after_fork",
264 "testlayersfilter3: after_fork",
265 "filter3: test_layers_filter_after_fork",
268 /* preconnect methods called in outer-to-inner order, complete
269 * in inner-to-outer order.
271 log_verify_seen_in_order
272 ("testlayersfilter3: preconnect",
273 "filter3: test_layers_filter_preconnect",
274 "testlayersfilter2: preconnect",
275 "filter2: test_layers_filter_preconnect",
276 "testlayersfilter1: preconnect",
277 "filter1: test_layers_filter_preconnect",
278 "testlayersplugin: preconnect",
279 "test_layers_plugin_preconnect",
282 #if LIBNBD_HAVE_NBD_SET_OPT_MODE
283 /* We can only test .list_exports if we can send NBD_OPT_INFO; if we
284 * can test it, they are called in order.
286 if (nbd_opt_list (nbd
, (nbd_list_callback
) { .callback
= export
}) == -1) {
287 fprintf (stderr
, "nbd_opt_list: %s\n", nbd_get_error ());
290 if (nbd_opt_go (nbd
) == -1) {
291 fprintf (stderr
, "nbd_opt_go: %s\n", nbd_get_error ());
296 log_verify_seen_in_order
297 ("testlayersfilter3: list_exports",
298 "filter3: test_layers_filter_list_exports",
299 "testlayersfilter2: list_exports",
300 "filter2: test_layers_filter_list_exports",
301 "testlayersfilter1: list_exports",
302 "filter1: test_layers_filter_list_exports",
303 "testlayersplugin: list_exports",
304 "test_layers_plugin_list_exports",
306 #endif /* LIBNBD_HAVE_NBD_SET_OPT_MODE */
308 fprintf (stderr
, "%s: nbdkit running\n", program_name
);
310 /* Verify export size (see tests/test-layers-plugin.c). */
311 if (nbd_get_size (nbd
) != 1024) {
312 fprintf (stderr
, "%s: unexpected export size %" PRIu64
" != 1024\n",
313 program_name
, nbd_get_size (nbd
));
317 /* Verify export flags. */
318 if (nbd_is_read_only (nbd
) != 0) {
319 fprintf (stderr
, "%s: unexpected eflags: NBD_FLAG_READ_ONLY not clear\n",
323 if (nbd_can_flush (nbd
) != 1) {
324 fprintf (stderr
, "%s: unexpected eflags: NBD_FLAG_SEND_FLUSH not set\n",
328 if (nbd_can_fua (nbd
) != 1) {
329 fprintf (stderr
, "%s: unexpected eflags: NBD_FLAG_SEND_FUA not set\n",
333 if (nbd_is_rotational (nbd
) != 1) {
334 fprintf (stderr
, "%s: unexpected eflags: NBD_FLAG_ROTATIONAL not set\n",
338 if (nbd_can_trim (nbd
) != 1) {
339 fprintf (stderr
, "%s: unexpected eflags: NBD_FLAG_SEND_TRIM not set\n",
343 if (nbd_can_zero (nbd
) != 1) {
345 "%s: unexpected eflags: NBD_FLAG_SEND_WRITE_ZEROES not set\n",
349 if (nbd_can_meta_context (nbd
, LIBNBD_CONTEXT_BASE_ALLOCATION
) != 1) {
351 "%s: unexpected setup: META_CONTEXT not supported\n",
356 /* default_export called in outer-to-inner order. */
357 log_verify_seen_in_order
358 ("testlayersfilter3: default_export",
359 "filter3: test_layers_filter_default_export",
360 "testlayersfilter2: default_export",
361 "filter2: test_layers_filter_default_export",
362 "testlayersfilter1: default_export",
363 "filter1: test_layers_filter_default_export",
364 "testlayersplugin: default_export",
365 "test_layers_plugin_default_export",
368 /* open methods called in outer-to-inner order, but thanks to next
369 * pointer, complete in inner-to-outer order. */
370 log_verify_seen_in_order
371 ("testlayersfilter3: open readonly=0",
372 "testlayersfilter2: open readonly=0",
373 "testlayersfilter1: open readonly=0",
374 "testlayersplugin: open readonly=0",
375 "test_layers_plugin_open",
376 "filter1: test_layers_filter_open",
377 "filter2: test_layers_filter_open",
378 "filter3: test_layers_filter_open",
381 /* prepare methods called in inner-to-outer order.
383 * Note that prepare methods only exist for filters, and they must
384 * be called from inner to outer (but finalize methods below are
385 * called the other way around).
387 log_verify_seen_in_order
388 ("filter1: test_layers_filter_prepare",
389 "filter2: test_layers_filter_prepare",
390 "filter3: test_layers_filter_prepare",
393 /* get_size methods called in order. */
394 log_verify_seen_in_order
395 ("filter3: test_layers_filter_get_size",
396 "filter2: test_layers_filter_get_size",
397 "filter1: test_layers_filter_get_size",
398 "test_layers_plugin_get_size",
401 /* can_* / is_* methods called in order. */
402 log_verify_seen_in_order
403 ("filter3: test_layers_filter_can_write",
404 "filter2: test_layers_filter_can_write",
405 "filter1: test_layers_filter_can_write",
406 "test_layers_plugin_can_write",
408 log_verify_seen_in_order
409 ("filter3: test_layers_filter_can_zero",
410 "filter2: test_layers_filter_can_zero",
411 "filter1: test_layers_filter_can_zero",
412 "test_layers_plugin_can_zero",
414 log_verify_seen_in_order
415 ("filter3: test_layers_filter_can_fast_zero",
416 "filter2: test_layers_filter_can_fast_zero",
417 "filter1: test_layers_filter_can_fast_zero",
418 "test_layers_plugin_can_fast_zero",
420 log_verify_seen_in_order
421 ("filter3: test_layers_filter_can_trim",
422 "filter2: test_layers_filter_can_trim",
423 "filter1: test_layers_filter_can_trim",
424 "test_layers_plugin_can_trim",
426 log_verify_seen_in_order
427 ("filter3: test_layers_filter_can_fua",
428 "filter2: test_layers_filter_can_fua",
429 "filter1: test_layers_filter_can_fua",
430 "test_layers_plugin_can_fua",
432 log_verify_seen_in_order
433 ("filter3: test_layers_filter_can_flush",
434 "filter2: test_layers_filter_can_flush",
435 "filter1: test_layers_filter_can_flush",
436 "test_layers_plugin_can_flush",
438 log_verify_seen_in_order
439 ("filter3: test_layers_filter_is_rotational",
440 "filter2: test_layers_filter_is_rotational",
441 "filter1: test_layers_filter_is_rotational",
442 "test_layers_plugin_is_rotational",
444 log_verify_seen_in_order
445 ("filter3: test_layers_filter_can_multi_conn",
446 "filter2: test_layers_filter_can_multi_conn",
447 "filter1: test_layers_filter_can_multi_conn",
448 "test_layers_plugin_can_multi_conn",
450 log_verify_seen_in_order
451 ("filter3: test_layers_filter_can_extents",
452 "filter2: test_layers_filter_can_extents",
453 "filter1: test_layers_filter_can_extents",
454 "test_layers_plugin_can_extents",
456 log_verify_seen_in_order
457 ("filter3: test_layers_filter_can_cache",
458 "filter2: test_layers_filter_can_cache",
459 "filter1: test_layers_filter_can_cache",
460 "test_layers_plugin_can_cache",
463 fprintf (stderr
, "%s: protocol connected\n", program_name
);
465 /* Send one command of each type. */
466 if (nbd_pread (nbd
, data
, sizeof data
, 0, 0) != 0) {
467 fprintf (stderr
, "%s: NBD_CMD_READ failed with %d %s\n",
468 program_name
, nbd_get_errno (), nbd_get_error ());
473 log_verify_seen_in_order
474 ("testlayersfilter3: pread count=512 offset=0",
475 "filter3: test_layers_filter_pread",
476 "testlayersfilter2: pread count=512 offset=0",
477 "filter2: test_layers_filter_pread",
478 "testlayersfilter1: pread count=512 offset=0",
479 "filter1: test_layers_filter_pread",
480 "testlayersplugin: pread count=512 offset=0",
481 "test_layers_plugin_pread",
484 if (nbd_pwrite (nbd
, data
, sizeof data
, 0, 0) != 0) {
485 fprintf (stderr
, "%s: NBD_CMD_WRITE failed with %d %s\n",
486 program_name
, nbd_get_errno (), nbd_get_error ());
491 log_verify_seen_in_order
492 ("testlayersfilter3: pwrite count=512 offset=0 fua=0",
493 "filter3: test_layers_filter_pwrite",
494 "testlayersfilter2: pwrite count=512 offset=0 fua=0",
495 "filter2: test_layers_filter_pwrite",
496 "testlayersfilter1: pwrite count=512 offset=0 fua=0",
497 "filter1: test_layers_filter_pwrite",
498 "testlayersplugin: pwrite count=512 offset=0 fua=0",
499 "test_layers_plugin_pwrite",
502 if (nbd_flush (nbd
, 0) != 0) {
503 fprintf (stderr
, "%s: NBD_CMD_FLUSH failed with %d %s\n",
504 program_name
, nbd_get_errno (), nbd_get_error ());
509 log_verify_seen_in_order
510 ("testlayersfilter3: flush",
511 "filter3: test_layers_filter_flush",
512 "testlayersfilter2: flush",
513 "filter2: test_layers_filter_flush",
514 "testlayersfilter1: flush",
515 "filter1: test_layers_filter_flush",
516 "testlayersplugin: flush",
517 "test_layers_plugin_flush",
520 if (nbd_trim (nbd
, sizeof data
, 0, 0) != 0) {
521 fprintf (stderr
, "%s: NBD_CMD_TRIM failed with %d %s\n",
522 program_name
, nbd_get_errno (), nbd_get_error ());
527 log_verify_seen_in_order
528 ("testlayersfilter3: trim count=512 offset=0 fua=0",
529 "filter3: test_layers_filter_trim",
530 "testlayersfilter2: trim count=512 offset=0 fua=0",
531 "filter2: test_layers_filter_trim",
532 "testlayersfilter1: trim count=512 offset=0 fua=0",
533 "filter1: test_layers_filter_trim",
534 "testlayersplugin: trim count=512 offset=0 fua=0",
535 "test_layers_plugin_trim",
538 if (nbd_zero (nbd
, sizeof data
, 0, 0) != 0) {
539 fprintf (stderr
, "%s: NBD_CMD_WRITE_ZEROES failed with %d %s\n",
540 program_name
, nbd_get_errno (), nbd_get_error ());
545 log_verify_seen_in_order
546 ("testlayersfilter3: zero count=512 offset=0 may_trim=1 fua=0 fast=0",
547 "filter3: test_layers_filter_zero",
548 "testlayersfilter2: zero count=512 offset=0 may_trim=1 fua=0 fast=0",
549 "filter2: test_layers_filter_zero",
550 "testlayersfilter1: zero count=512 offset=0 may_trim=1 fua=0 fast=0",
551 "filter1: test_layers_filter_zero",
552 "testlayersplugin: zero count=512 offset=0 may_trim=1 fua=0 fast=0",
553 "test_layers_plugin_zero",
556 if (nbd_cache (nbd
, sizeof data
, 0, 0) != 0) {
557 fprintf (stderr
, "%s: NBD_CMD_CACHE failed with %d %s\n",
558 program_name
, nbd_get_errno (), nbd_get_error ());
563 log_verify_seen_in_order
564 ("testlayersfilter3: cache count=512 offset=0",
565 "filter3: test_layers_filter_cache",
566 "testlayersfilter2: cache count=512 offset=0",
567 "filter2: test_layers_filter_cache",
568 "testlayersfilter1: cache count=512 offset=0",
569 "filter1: test_layers_filter_cache",
570 "testlayersplugin: cache count=512 offset=0",
571 "test_layers_plugin_cache",
574 if (nbd_block_status (nbd
, sizeof data
, 0,
575 (nbd_extent_callback
) { .callback
= extent
}, 0) != 0) {
576 fprintf (stderr
, "%s: NBD_CMD_BLOCK_STATUS failed with %d %s\n",
577 program_name
, nbd_get_errno (), nbd_get_error ());
582 log_verify_seen_in_order
583 ("testlayersfilter3: extents count=512 offset=0",
584 "filter3: test_layers_filter_extents",
585 "testlayersfilter2: extents count=512 offset=0",
586 "filter2: test_layers_filter_extents",
587 "testlayersfilter1: extents count=512 offset=0",
588 "filter1: test_layers_filter_extents",
589 "testlayersplugin: extents count=512 offset=0",
590 "test_layers_plugin_extents",
593 /* Close the connection. */
594 fprintf (stderr
, "%s: closing the connection\n", program_name
);
595 if (nbd_shutdown (nbd
, 0) != 0) {
596 fprintf (stderr
, "%s: NBD_CMD_DISC failed with %d %s\n",
597 program_name
, nbd_get_errno (), nbd_get_error ());
602 /* finalize methods called in reverse order of prepare */
604 log_verify_seen_in_order
605 ("filter3: test_layers_filter_finalize",
606 "filter2: test_layers_filter_finalize",
607 "filter1: test_layers_filter_finalize",
610 /* close methods called outer-to-inner, which is reverse of completion
612 log_verify_seen_in_order
613 ("filter3: test_layers_filter_close",
614 "filter2: test_layers_filter_close",
615 "filter1: test_layers_filter_close",
616 "test_layers_plugin_close",
619 /* cleanup methods called in outer-to-inner order. */
620 log_verify_seen_in_order
621 ("testlayersfilter3: cleanup",
622 "filter3: test_layers_filter_cleanup",
623 "testlayersfilter2: cleanup",
624 "filter2: test_layers_filter_cleanup",
625 "testlayersfilter1: cleanup",
626 "filter1: test_layers_filter_cleanup",
627 "testlayersplugin: cleanup",
628 "test_layers_plugin_cleanup",
631 /* unload methods should be run in any order. */
632 log_verify_seen ("test_layers_plugin_unload");
633 log_verify_seen ("filter1: test_layers_filter_unload");
634 log_verify_seen ("filter2: test_layers_filter_unload");
635 log_verify_seen ("filter3: test_layers_filter_unload");
642 /* The log from nbdkit is captured in a separate thread. */
643 static char *log_buf
= NULL
;
644 static size_t log_len
= 0;
645 static size_t last_out
= 0;
646 static pthread_mutex_t log_lock
= PTHREAD_MUTEX_INITIALIZER
;
649 start_log_capture (void *arg
)
651 int fd
= *(int *)arg
;
652 size_t allocated
= 0;
657 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&log_lock
);
658 if (allocated
<= log_len
) {
660 log_buf
= realloc (log_buf
, allocated
);
661 if (log_buf
== NULL
) {
662 perror ("log: realloc");
668 r
= read (fd
, &log_buf
[log_len
], allocated
-log_len
);
670 perror ("log: read");
676 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&log_lock
);
680 /* nbdkit closed the connection. */
684 static void short_sleep (void)
687 /* Copy what we have received so far into stderr */
688 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&log_lock
);
689 if (fwrite (&log_buf
[last_out
], log_len
- last_out
, 1, stderr
) == -1)
690 perror ("log: fwrite");
694 /* These functions are called from the main thread to verify messages
695 * appeared as expected in the log.
697 * NB: The log buffer is NOT \0-terminated.
700 static void no_message_error (const char *msg
) __attribute__((noreturn
));
703 no_message_error (const char *msg
)
705 fprintf (stderr
, "%s: did not find expected message \"%s\"\n",
711 log_verify_seen (const char *msg
)
713 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&log_lock
);
714 if (memmem (log_buf
, log_len
, msg
, strlen (msg
)) == NULL
)
715 no_message_error (msg
);
718 static void messages_out_of_order (const char *msg1
, const char *msg2
)
719 __attribute__((noreturn
));
722 messages_out_of_order (const char *msg1
, const char *msg2
)
724 fprintf (stderr
, "%s: message \"%s\" expected before message \"%s\"\n",
725 program_name
, msg1
, msg2
);
730 log_verify_seen_in_order (const char *msg
, ...)
734 const char *prev_msg
, *curr_msg
;
736 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&log_lock
);
738 prev
= memmem (log_buf
, log_len
, msg
, strlen (msg
));
739 if (prev
== NULL
) no_message_error (msg
);
742 va_start (args
, msg
);
743 while ((curr_msg
= va_arg (args
, char *)) != NULL
) {
744 curr
= memmem (log_buf
, log_len
, curr_msg
, strlen (curr_msg
));
745 if (curr
== NULL
) no_message_error (curr_msg
);
746 if (prev
> curr
) messages_out_of_order (prev_msg
, curr_msg
);
756 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&log_lock
);
764 /* A lot of porting work required for Windows. For now, skip the test. */
766 main (int argc
, char *argv
[])
768 fprintf (stderr
, "%s: test skipped because not ported to Windows.\n",