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