Skip to content

Commit 0b7172b

Browse files
author
Laszlo Ersek
committed
lib/utils: add unit tests for async-signal-safe execvpe()
Don't try to test async-signal-safety, but strive to exercise as many as possible paths through nbd_internal_execvpe_init() and nbd_internal_fork_safe_execvpe(). Signed-off-by: Laszlo Ersek <lersek@redhat.com> Reviewed-by: Richard W.M. Jones <rjones@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Message-Id: <20230319094139.285312-3-lersek@redhat.com>
1 parent aa696c0 commit 0b7172b

File tree

4 files changed

+406
-0
lines changed

4 files changed

+406
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ Makefile.in
130130
/lib/states.h
131131
/lib/test-fork-safe-assert
132132
/lib/test-fork-safe-assert.err
133+
/lib/test-fork-safe-execvpe
133134
/lib/unlocked.h
134135
/libtool
135136
/ltmain.sh

lib/Makefile.am

+11
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ EXTRA_DIST = \
3333
$(generator_built) \
3434
libnbd.syms \
3535
test-fork-safe-assert.sh \
36+
test-fork-safe-execvpe.sh \
3637
$(NULL)
3738

3839
lib_LTLIBRARIES = libnbd.la
@@ -105,10 +106,12 @@ pkgconfig_DATA = libnbd.pc
105106

106107
TESTS = \
107108
test-fork-safe-assert.sh \
109+
test-fork-safe-execvpe.sh \
108110
$(NULL)
109111

110112
check_PROGRAMS = \
111113
test-fork-safe-assert \
114+
test-fork-safe-execvpe \
112115
$(NULL)
113116

114117
test_fork_safe_assert_SOURCES = \
@@ -118,3 +121,11 @@ test_fork_safe_assert_SOURCES = \
118121
utils.c \
119122
$(NULL)
120123
test_fork_safe_assert_LDADD = $(PTHREAD_LIBS)
124+
125+
test_fork_safe_execvpe_SOURCES = \
126+
$(top_srcdir)/common/utils/vector.c \
127+
errors.c \
128+
test-fork-safe-execvpe.c \
129+
utils.c \
130+
$(NULL)
131+
test_fork_safe_execvpe_LDADD = $(PTHREAD_LIBS)

lib/test-fork-safe-execvpe.c

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/* nbd client library in userspace
2+
* Copyright (C) 2013-2023 Red Hat Inc.
3+
*
4+
* This library is free software; you can redistribute it and/or
5+
* modify it under the terms of the GNU Lesser General Public
6+
* License as published by the Free Software Foundation; either
7+
* version 2 of the License, or (at your option) any later version.
8+
*
9+
* This library is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+
* Lesser General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public
15+
* License along with this library; if not, write to the Free Software
16+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17+
*/
18+
19+
#include <config.h>
20+
21+
#include <errno.h>
22+
#include <stdio.h>
23+
#include <stdlib.h>
24+
#include <string.h>
25+
26+
#include "internal.h"
27+
28+
extern char **environ;
29+
30+
/* This is a perror() replacement that makes the error message more
31+
* machine-readable, for a select few error numbers. Do not use it as a general
32+
* error() replacement, only upon nbd_internal_execvpe_init() and
33+
* nbd_internal_fork_safe_execvpe() failure.
34+
*/
35+
static void
36+
xperror (const char *s)
37+
{
38+
const char *err;
39+
40+
if (s != NULL && *s != '\0')
41+
(void)fprintf (stderr, "%s: ", s);
42+
43+
switch (errno) {
44+
case EACCES:
45+
err = "EACCES";
46+
break;
47+
case ELOOP:
48+
err = "ELOOP";
49+
break;
50+
case ENOENT:
51+
err = "ENOENT";
52+
break;
53+
case ENOTDIR:
54+
err = "ENOTDIR";
55+
break;
56+
default:
57+
err = strerror (errno);
58+
}
59+
(void)fprintf (stderr, "%s\n", err);
60+
}
61+
62+
int
63+
main (int argc, char **argv)
64+
{
65+
struct execvpe ctx;
66+
const char *prog_file;
67+
string_vector prog_argv;
68+
size_t i;
69+
70+
if (argc < 3) {
71+
fprintf (stderr, "%1$s: usage: %1$s program-to-exec argv0 ...\n", argv[0]);
72+
return EXIT_FAILURE;
73+
}
74+
75+
prog_file = argv[1];
76+
77+
/* For the argv of the program to execute, we need to drop our argv[0] (= our
78+
* own name) and argv[1] (= the program we need to execute), and to tack on a
79+
* terminating null pointer. Note that "argc" does not include the terminating
80+
* NULL.
81+
*/
82+
prog_argv = (string_vector)empty_vector;
83+
if (string_vector_reserve (&prog_argv, argc - 2 + 1) == -1) {
84+
perror ("string_vector_reserve");
85+
return EXIT_FAILURE;
86+
}
87+
88+
for (i = 2; i < argc; ++i)
89+
(void)string_vector_append (&prog_argv, argv[i]);
90+
(void)string_vector_append (&prog_argv, NULL);
91+
92+
if (nbd_internal_execvpe_init (&ctx, prog_file, prog_argv.len) == -1) {
93+
xperror ("nbd_internal_execvpe_init");
94+
goto reset_prog_argv;
95+
}
96+
97+
/* Print out the generated candidates. */
98+
for (i = 0; i < ctx.pathnames.len; ++i)
99+
(void)fprintf (stdout, "%s\n", ctx.pathnames.ptr[i]);
100+
101+
if (fflush (stdout) == EOF) {
102+
perror ("fflush");
103+
goto uninit_execvpe;
104+
}
105+
106+
(void)nbd_internal_fork_safe_execvpe (&ctx, &prog_argv, environ);
107+
xperror ("nbd_internal_fork_safe_execvpe");
108+
/* fall through */
109+
110+
uninit_execvpe:
111+
nbd_internal_execvpe_uninit (&ctx);
112+
113+
reset_prog_argv:
114+
string_vector_reset (&prog_argv);
115+
116+
return EXIT_FAILURE;
117+
}

0 commit comments

Comments
 (0)