]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
2abb5b3b | 2 | |
2abb5b3b | 3 | #include <net/if.h> |
eb43e8a7 | 4 | #include <stdlib.h> |
ca78ad1d | 5 | #include <unistd.h> |
2abb5b3b | 6 | |
eb43e8a7 YW |
7 | #include "af-list.h" |
8 | #include "alloc-util.h" | |
e2aa384b | 9 | #include "dlfcn-util.h" |
8ef114c6 | 10 | #include "env-util.h" |
eb43e8a7 | 11 | #include "errno-list.h" |
518a66ec | 12 | #include "format-util.h" |
eb43e8a7 YW |
13 | #include "hexdecoct.h" |
14 | #include "hostname-util.h" | |
15 | #include "in-addr-util.h" | |
16 | #include "local-addresses.h" | |
2abb5b3b | 17 | #include "log.h" |
f68a2622 | 18 | #include "main-func.h" |
f0d12668 | 19 | #include "nss-test-util.h" |
2abb5b3b | 20 | #include "nss-util.h" |
f0cb09bb | 21 | #include "parse-util.h" |
2abb5b3b | 22 | #include "path-util.h" |
2abb5b3b | 23 | #include "stdio-util.h" |
eb43e8a7 | 24 | #include "string-util.h" |
2abb5b3b | 25 | #include "strv.h" |
f68a2622 | 26 | #include "tests.h" |
2abb5b3b | 27 | |
f0cb09bb ZJS |
28 | static size_t arg_bufsize = 1024; |
29 | ||
2abb5b3b ZJS |
30 | static const char* af_to_string(int family, char *buf, size_t buf_len) { |
31 | const char *name; | |
32 | ||
33 | if (family == AF_UNSPEC) | |
34 | return "*"; | |
35 | ||
36 | name = af_to_name(family); | |
37 | if (name) | |
38 | return name; | |
39 | ||
121ed16c | 40 | (void) snprintf(buf, buf_len, "%i", family); |
2abb5b3b ZJS |
41 | return buf; |
42 | } | |
43 | ||
2abb5b3b | 44 | static int print_gaih_addrtuples(const struct gaih_addrtuple *tuples) { |
01afd0f7 | 45 | int r, n = 0; |
2abb5b3b | 46 | |
890e5a4d | 47 | for (const struct gaih_addrtuple *it = tuples; it; it = it->next) { |
2abb5b3b ZJS |
48 | _cleanup_free_ char *a = NULL; |
49 | union in_addr_union u; | |
2abb5b3b | 50 | char family_name[DECIMAL_STR_MAX(int)]; |
2abb5b3b ZJS |
51 | |
52 | memcpy(&u, it->addr, 16); | |
53 | r = in_addr_to_string(it->family, &u, &a); | |
4c701096 | 54 | assert_se(IN_SET(r, 0, -EAFNOSUPPORT)); |
2abb5b3b | 55 | if (r == -EAFNOSUPPORT) |
dcd6361e | 56 | assert_se(a = hexmem(it->addr, 16)); |
2abb5b3b | 57 | |
01afd0f7 | 58 | log_info(" \"%s\" %s %s %s", |
2abb5b3b ZJS |
59 | it->name, |
60 | af_to_string(it->family, family_name, sizeof family_name), | |
61 | a, | |
01afd0f7 YW |
62 | FORMAT_IFNAME_FULL(it->scopeid, FORMAT_IFNAME_IFINDEX_WITH_PERCENT)); |
63 | ||
64 | n++; | |
2abb5b3b ZJS |
65 | } |
66 | return n; | |
67 | } | |
68 | ||
69 | static void print_struct_hostent(struct hostent *host, const char *canon) { | |
2abb5b3b ZJS |
70 | log_info(" \"%s\"", host->h_name); |
71 | STRV_FOREACH(s, host->h_aliases) | |
72 | log_info(" alias \"%s\"", *s); | |
73 | STRV_FOREACH(s, host->h_addr_list) { | |
74 | union in_addr_union u; | |
75 | _cleanup_free_ char *a = NULL; | |
76 | char family_name[DECIMAL_STR_MAX(int)]; | |
77 | int r; | |
78 | ||
79 | assert_se((unsigned) host->h_length == FAMILY_ADDRESS_SIZE(host->h_addrtype)); | |
80 | memcpy(&u, *s, host->h_length); | |
81 | r = in_addr_to_string(host->h_addrtype, &u, &a); | |
82 | assert_se(r == 0); | |
83 | log_info(" %s %s", | |
84 | af_to_string(host->h_addrtype, family_name, sizeof family_name), | |
85 | a); | |
86 | } | |
87 | if (canon) | |
88 | log_info(" canonical: \"%s\"", canon); | |
89 | } | |
90 | ||
91 | static void test_gethostbyname4_r(void *handle, const char *module, const char *name) { | |
92 | const char *fname; | |
93 | _nss_gethostbyname4_r_t f; | |
f0cb09bb | 94 | char buffer[arg_bufsize]; |
2abb5b3b ZJS |
95 | struct gaih_addrtuple *pat = NULL; |
96 | int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */ | |
97 | int32_t ttl = INT32_MAX; /* nss-dns wants to return the lowest ttl, | |
98 | and will access this variable through *ttlp, | |
99 | so we need to set it to something. | |
100 | I'm not sure if this is a bug in nss-dns | |
101 | or not. */ | |
102 | enum nss_status status; | |
103 | char pretty_status[DECIMAL_STR_MAX(enum nss_status)]; | |
104 | int n; | |
105 | ||
106 | fname = strjoina("_nss_", module, "_gethostbyname4_r"); | |
107 | f = dlsym(handle, fname); | |
108 | log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f); | |
08554d47 ZJS |
109 | if (!f) { |
110 | log_info("%s not defined", fname); | |
111 | return; | |
112 | } | |
2abb5b3b ZJS |
113 | |
114 | status = f(name, &pat, buffer, sizeof buffer, &errno1, &errno2, &ttl); | |
115 | if (status == NSS_STATUS_SUCCESS) { | |
116 | log_info("%s(\"%s\") → status=%s%-20spat=buffer+0x%tx errno=%d/%s h_errno=%d/%s ttl=%"PRIi32, | |
117 | fname, name, | |
118 | nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n", | |
119 | pat ? (char*) pat - buffer : 0, | |
120 | errno1, errno_to_name(errno1) ?: "---", | |
121 | errno2, hstrerror(errno2), | |
122 | ttl); | |
123 | n = print_gaih_addrtuples(pat); | |
124 | } else { | |
125 | log_info("%s(\"%s\") → status=%s%-20spat=0x%p errno=%d/%s h_errno=%d/%s", | |
126 | fname, name, | |
127 | nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n", | |
128 | pat, | |
129 | errno1, errno_to_name(errno1) ?: "---", | |
130 | errno2, hstrerror(errno2)); | |
131 | n = 0; | |
132 | } | |
133 | ||
134 | if (STR_IN_SET(module, "resolve", "mymachines") && status == NSS_STATUS_UNAVAIL) | |
135 | return; | |
136 | ||
8ef114c6 ZJS |
137 | if (STR_IN_SET(module, "myhostname", "resolve") && |
138 | streq(name, "localhost") && | |
139 | getenv_bool_secure("SYSTEMD_NSS_RESOLVE_SYNTHESIZE") != 0) { | |
2abb5b3b ZJS |
140 | assert_se(status == NSS_STATUS_SUCCESS); |
141 | assert_se(n == 2); | |
142 | } | |
143 | } | |
144 | ||
2abb5b3b ZJS |
145 | static void test_gethostbyname3_r(void *handle, const char *module, const char *name, int af) { |
146 | const char *fname; | |
147 | _nss_gethostbyname3_r_t f; | |
f0cb09bb | 148 | char buffer[arg_bufsize]; |
2abb5b3b ZJS |
149 | int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */ |
150 | int32_t ttl = INT32_MAX; /* nss-dns wants to return the lowest ttl, | |
151 | and will access this variable through *ttlp, | |
152 | so we need to set it to something. | |
153 | I'm not sure if this is a bug in nss-dns | |
154 | or not. */ | |
155 | enum nss_status status; | |
156 | char pretty_status[DECIMAL_STR_MAX(enum nss_status)]; | |
157 | struct hostent host; | |
158 | char *canon; | |
159 | char family_name[DECIMAL_STR_MAX(int)]; | |
160 | ||
161 | fname = strjoina("_nss_", module, "_gethostbyname3_r"); | |
162 | f = dlsym(handle, fname); | |
163 | log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f); | |
08554d47 ZJS |
164 | if (!f) { |
165 | log_info("%s not defined", fname); | |
166 | return; | |
167 | } | |
2abb5b3b ZJS |
168 | |
169 | status = f(name, af, &host, buffer, sizeof buffer, &errno1, &errno2, &ttl, &canon); | |
170 | log_info("%s(\"%s\", %s) → status=%s%-20serrno=%d/%s h_errno=%d/%s ttl=%"PRIi32, | |
171 | fname, name, af_to_string(af, family_name, sizeof family_name), | |
172 | nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n", | |
173 | errno1, errno_to_name(errno1) ?: "---", | |
174 | errno2, hstrerror(errno2), | |
175 | ttl); | |
176 | if (status == NSS_STATUS_SUCCESS) | |
177 | print_struct_hostent(&host, canon); | |
178 | } | |
179 | ||
180 | static void test_gethostbyname2_r(void *handle, const char *module, const char *name, int af) { | |
181 | const char *fname; | |
182 | _nss_gethostbyname2_r_t f; | |
f0cb09bb | 183 | char buffer[arg_bufsize]; |
2abb5b3b ZJS |
184 | int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */ |
185 | enum nss_status status; | |
186 | char pretty_status[DECIMAL_STR_MAX(enum nss_status)]; | |
187 | struct hostent host; | |
188 | char family_name[DECIMAL_STR_MAX(int)]; | |
189 | ||
190 | fname = strjoina("_nss_", module, "_gethostbyname2_r"); | |
191 | f = dlsym(handle, fname); | |
192 | log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f); | |
08554d47 ZJS |
193 | if (!f) { |
194 | log_info("%s not defined", fname); | |
195 | return; | |
196 | } | |
2abb5b3b ZJS |
197 | |
198 | status = f(name, af, &host, buffer, sizeof buffer, &errno1, &errno2); | |
199 | log_info("%s(\"%s\", %s) → status=%s%-20serrno=%d/%s h_errno=%d/%s", | |
200 | fname, name, af_to_string(af, family_name, sizeof family_name), | |
201 | nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n", | |
202 | errno1, errno_to_name(errno1) ?: "---", | |
203 | errno2, hstrerror(errno2)); | |
204 | if (status == NSS_STATUS_SUCCESS) | |
205 | print_struct_hostent(&host, NULL); | |
206 | } | |
207 | ||
208 | static void test_gethostbyname_r(void *handle, const char *module, const char *name) { | |
209 | const char *fname; | |
210 | _nss_gethostbyname_r_t f; | |
f0cb09bb | 211 | char buffer[arg_bufsize]; |
2abb5b3b ZJS |
212 | int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */ |
213 | enum nss_status status; | |
214 | char pretty_status[DECIMAL_STR_MAX(enum nss_status)]; | |
215 | struct hostent host; | |
216 | ||
217 | fname = strjoina("_nss_", module, "_gethostbyname_r"); | |
218 | f = dlsym(handle, fname); | |
219 | log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f); | |
08554d47 ZJS |
220 | if (!f) { |
221 | log_info("%s not defined", fname); | |
222 | return; | |
223 | } | |
2abb5b3b ZJS |
224 | |
225 | status = f(name, &host, buffer, sizeof buffer, &errno1, &errno2); | |
226 | log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s", | |
227 | fname, name, | |
228 | nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n", | |
229 | errno1, errno_to_name(errno1) ?: "---", | |
230 | errno2, hstrerror(errno2)); | |
231 | if (status == NSS_STATUS_SUCCESS) | |
232 | print_struct_hostent(&host, NULL); | |
233 | } | |
234 | ||
235 | static void test_gethostbyaddr2_r(void *handle, | |
236 | const char *module, | |
237 | const void* addr, socklen_t len, | |
238 | int af) { | |
239 | ||
240 | const char *fname; | |
241 | _nss_gethostbyaddr2_r_t f; | |
f0cb09bb | 242 | char buffer[arg_bufsize]; |
2abb5b3b ZJS |
243 | int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */ |
244 | enum nss_status status; | |
245 | char pretty_status[DECIMAL_STR_MAX(enum nss_status)]; | |
246 | struct hostent host; | |
247 | int32_t ttl = INT32_MAX; | |
248 | _cleanup_free_ char *addr_pretty = NULL; | |
249 | ||
250 | fname = strjoina("_nss_", module, "_gethostbyaddr2_r"); | |
251 | f = dlsym(handle, fname); | |
252 | ||
253 | log_full_errno(f ? LOG_DEBUG : LOG_INFO, errno, | |
254 | "dlsym(0x%p, %s) → 0x%p: %m", handle, fname, f); | |
08554d47 ZJS |
255 | if (!f) { |
256 | log_info("%s not defined", fname); | |
2abb5b3b | 257 | return; |
08554d47 | 258 | } |
2abb5b3b ZJS |
259 | |
260 | assert_se(in_addr_to_string(af, addr, &addr_pretty) >= 0); | |
261 | ||
262 | status = f(addr, len, af, &host, buffer, sizeof buffer, &errno1, &errno2, &ttl); | |
263 | log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s ttl=%"PRIi32, | |
264 | fname, addr_pretty, | |
265 | nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n", | |
266 | errno1, errno_to_name(errno1) ?: "---", | |
267 | errno2, hstrerror(errno2), | |
268 | ttl); | |
269 | if (status == NSS_STATUS_SUCCESS) | |
270 | print_struct_hostent(&host, NULL); | |
271 | } | |
272 | ||
273 | static void test_gethostbyaddr_r(void *handle, | |
274 | const char *module, | |
275 | const void* addr, socklen_t len, | |
276 | int af) { | |
277 | ||
278 | const char *fname; | |
279 | _nss_gethostbyaddr_r_t f; | |
f0cb09bb | 280 | char buffer[arg_bufsize]; |
2abb5b3b ZJS |
281 | int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */ |
282 | enum nss_status status; | |
283 | char pretty_status[DECIMAL_STR_MAX(enum nss_status)]; | |
284 | struct hostent host; | |
285 | _cleanup_free_ char *addr_pretty = NULL; | |
286 | ||
287 | fname = strjoina("_nss_", module, "_gethostbyaddr_r"); | |
288 | f = dlsym(handle, fname); | |
289 | ||
290 | log_full_errno(f ? LOG_DEBUG : LOG_INFO, errno, | |
291 | "dlsym(0x%p, %s) → 0x%p: %m", handle, fname, f); | |
08554d47 ZJS |
292 | if (!f) { |
293 | log_info("%s not defined", fname); | |
2abb5b3b | 294 | return; |
08554d47 | 295 | } |
2abb5b3b ZJS |
296 | |
297 | assert_se(in_addr_to_string(af, addr, &addr_pretty) >= 0); | |
298 | ||
299 | status = f(addr, len, af, &host, buffer, sizeof buffer, &errno1, &errno2); | |
300 | log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s", | |
301 | fname, addr_pretty, | |
302 | nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n", | |
303 | errno1, errno_to_name(errno1) ?: "---", | |
304 | errno2, hstrerror(errno2)); | |
305 | if (status == NSS_STATUS_SUCCESS) | |
306 | print_struct_hostent(&host, NULL); | |
307 | } | |
308 | ||
309 | static void test_byname(void *handle, const char *module, const char *name) { | |
310 | test_gethostbyname4_r(handle, module, name); | |
311 | puts(""); | |
312 | ||
313 | test_gethostbyname3_r(handle, module, name, AF_INET); | |
314 | puts(""); | |
315 | test_gethostbyname3_r(handle, module, name, AF_INET6); | |
316 | puts(""); | |
317 | test_gethostbyname3_r(handle, module, name, AF_UNSPEC); | |
318 | puts(""); | |
319 | test_gethostbyname3_r(handle, module, name, AF_LOCAL); | |
320 | puts(""); | |
321 | ||
322 | test_gethostbyname2_r(handle, module, name, AF_INET); | |
323 | puts(""); | |
324 | test_gethostbyname2_r(handle, module, name, AF_INET6); | |
325 | puts(""); | |
326 | test_gethostbyname2_r(handle, module, name, AF_UNSPEC); | |
327 | puts(""); | |
328 | test_gethostbyname2_r(handle, module, name, AF_LOCAL); | |
329 | puts(""); | |
330 | ||
331 | test_gethostbyname_r(handle, module, name); | |
332 | puts(""); | |
333 | } | |
334 | ||
335 | static void test_byaddr(void *handle, | |
336 | const char *module, | |
337 | const void* addr, socklen_t len, | |
338 | int af) { | |
339 | test_gethostbyaddr2_r(handle, module, addr, len, af); | |
340 | puts(""); | |
341 | ||
342 | test_gethostbyaddr_r(handle, module, addr, len, af); | |
343 | puts(""); | |
344 | } | |
345 | ||
9f7672b3 ZJS |
346 | static int make_addresses(struct local_address **addresses) { |
347 | int n; | |
9f7672b3 ZJS |
348 | _cleanup_free_ struct local_address *addrs = NULL; |
349 | ||
350 | n = local_addresses(NULL, 0, AF_UNSPEC, &addrs); | |
351 | if (n < 0) | |
352 | log_info_errno(n, "Failed to query local addresses: %m"); | |
353 | ||
319a4f4b | 354 | assert_se(GREEDY_REALLOC(addrs, n + 3)); |
9f7672b3 ZJS |
355 | |
356 | addrs[n++] = (struct local_address) { .family = AF_INET, | |
357 | .address.in = { htobe32(0x7F000001) } }; | |
358 | addrs[n++] = (struct local_address) { .family = AF_INET, | |
359 | .address.in = { htobe32(0x7F000002) } }; | |
360 | addrs[n++] = (struct local_address) { .family = AF_INET6, | |
361 | .address.in6 = in6addr_loopback }; | |
362 | return 0; | |
363 | } | |
364 | ||
890e5a4d | 365 | static int test_one_module(const char *dir, |
9f7672b3 ZJS |
366 | const char *module, |
367 | char **names, | |
368 | struct local_address *addresses, | |
369 | int n_addresses) { | |
9f7672b3 | 370 | |
9f7672b3 ZJS |
371 | log_info("======== %s ========", module); |
372 | ||
e2aa384b | 373 | _cleanup_(dlclosep) void *handle = nss_open_handle(dir, module, RTLD_LAZY|RTLD_NODELETE); |
9f7672b3 ZJS |
374 | if (!handle) |
375 | return -EINVAL; | |
376 | ||
377 | STRV_FOREACH(name, names) | |
378 | test_byname(handle, module, *name); | |
379 | ||
890e5a4d | 380 | for (int i = 0; i < n_addresses; i++) |
9f7672b3 ZJS |
381 | test_byaddr(handle, module, |
382 | &addresses[i].address, | |
383 | FAMILY_ADDRESS_SIZE(addresses[i].family), | |
384 | addresses[i].family); | |
385 | ||
386 | log_info(" "); | |
9f7672b3 ZJS |
387 | return 0; |
388 | } | |
389 | ||
390 | static int parse_argv(int argc, char **argv, | |
391 | char ***the_modules, | |
392 | char ***the_names, | |
393 | struct local_address **the_addresses, int *n_addresses) { | |
394 | ||
9f7672b3 ZJS |
395 | _cleanup_strv_free_ char **modules = NULL, **names = NULL; |
396 | _cleanup_free_ struct local_address *addrs = NULL; | |
f0cb09bb | 397 | const char *p; |
f0d12668 | 398 | int r, n = 0; |
9f7672b3 | 399 | |
f0cb09bb ZJS |
400 | p = getenv("SYSTEMD_TEST_NSS_BUFSIZE"); |
401 | if (p) { | |
402 | r = safe_atozu(p, &arg_bufsize); | |
403 | if (r < 0) | |
404 | return log_error_errno(r, "Failed to parse $SYSTEMD_TEST_NSS_BUFSIZE"); | |
405 | } | |
406 | ||
9f7672b3 | 407 | if (argc > 1) |
bea1a013 | 408 | modules = strv_new(argv[1]); |
9f7672b3 ZJS |
409 | else |
410 | modules = strv_new( | |
08540a95 | 411 | #if ENABLE_NSS_MYHOSTNAME |
9f7672b3 | 412 | "myhostname", |
2abb5b3b | 413 | #endif |
08540a95 | 414 | #if ENABLE_NSS_RESOLVE |
9f7672b3 | 415 | "resolve", |
2abb5b3b | 416 | #endif |
08540a95 | 417 | #if ENABLE_NSS_MYMACHINES |
9f7672b3 | 418 | "mymachines", |
2abb5b3b | 419 | #endif |
951280ce | 420 | NULL); |
e2aa384b | 421 | assert_se(modules); |
9f7672b3 ZJS |
422 | |
423 | if (argc > 2) { | |
9f7672b3 ZJS |
424 | int family; |
425 | union in_addr_union address; | |
426 | ||
427 | STRV_FOREACH(name, argv + 2) { | |
428 | r = in_addr_from_string_auto(*name, &family, &address); | |
429 | if (r < 0) { | |
430 | /* assume this is a name */ | |
431 | r = strv_extend(&names, *name); | |
432 | if (r < 0) | |
433 | return r; | |
434 | } else { | |
319a4f4b | 435 | assert_se(GREEDY_REALLOC0(addrs, n + 1)); |
9f7672b3 ZJS |
436 | |
437 | addrs[n++] = (struct local_address) { .family = family, | |
438 | .address = address }; | |
439 | } | |
440 | } | |
441 | } else { | |
442 | _cleanup_free_ char *hostname; | |
e2aa384b | 443 | assert_se(hostname = gethostname_malloc()); |
a1fdbcbe | 444 | assert_se(names = strv_new("localhost", "_gateway", "_outbound", "foo_no_such_host", hostname)); |
9f7672b3 ZJS |
445 | |
446 | n = make_addresses(&addrs); | |
e2aa384b | 447 | assert_se(n >= 0); |
9f7672b3 ZJS |
448 | } |
449 | ||
f0d12668 ZJS |
450 | *the_modules = TAKE_PTR(modules); |
451 | *the_names = TAKE_PTR(names); | |
452 | *the_addresses = TAKE_PTR(addrs); | |
9f7672b3 | 453 | *n_addresses = n; |
9f7672b3 ZJS |
454 | return 0; |
455 | } | |
456 | ||
f68a2622 | 457 | static int run(int argc, char **argv) { |
9f7672b3 ZJS |
458 | _cleanup_free_ char *dir = NULL; |
459 | _cleanup_strv_free_ char **modules = NULL, **names = NULL; | |
2abb5b3b | 460 | _cleanup_free_ struct local_address *addresses = NULL; |
6a909d41 | 461 | int n_addresses = 0; |
9f7672b3 | 462 | int r; |
2abb5b3b | 463 | |
f68a2622 | 464 | test_setup_logging(LOG_INFO); |
2abb5b3b | 465 | |
9f7672b3 | 466 | r = parse_argv(argc, argv, &modules, &names, &addresses, &n_addresses); |
e2aa384b ZJS |
467 | if (r < 0) |
468 | return log_error_errno(r, "Failed to parse arguments: %m"); | |
2abb5b3b | 469 | |
e2aa384b | 470 | assert_se(path_extract_directory(argv[0], &dir) >= 0); |
2abb5b3b | 471 | |
9f7672b3 ZJS |
472 | STRV_FOREACH(module, modules) { |
473 | r = test_one_module(dir, *module, names, addresses, n_addresses); | |
474 | if (r < 0) | |
f68a2622 | 475 | return r; |
2abb5b3b ZJS |
476 | } |
477 | ||
f68a2622 | 478 | return 0; |
2abb5b3b | 479 | } |
f68a2622 ZJS |
480 | |
481 | DEFINE_MAIN_FUNCTION(run); |