Bug Summary

File:build-scan/../src/hwdb/hwdb.c
Warning:line 234, column 29
Potential leak of memory pointed to by 's'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name hwdb.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model static -mframe-pointer=all -relaxed-aliasing -menable-no-infs -menable-no-nans -menable-unsafe-fp-math -fno-signed-zeros -mreassociate -freciprocal-math -fdenormal-fp-math=preserve-sign,preserve-sign -ffp-contract=fast -fno-rounding-math -ffast-math -ffinite-math-only -mconstructor-aliases -munwind-tables -target-cpu x86-64 -tune-cpu generic -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/12.0.0 -include config.h -I systemd-hwdb.p -I . -I .. -I src/basic -I ../src/basic -I src/shared -I ../src/shared -I src/systemd -I ../src/systemd -I src/journal -I ../src/journal -I src/journal-remote -I ../src/journal-remote -I src/nspawn -I ../src/nspawn -I src/resolve -I ../src/resolve -I src/timesync -I ../src/timesync -I ../src/time-wait-sync -I src/login -I ../src/login -I src/udev -I ../src/udev -I src/libudev -I ../src/libudev -I src/core -I ../src/core -I ../src/libsystemd/sd-bus -I ../src/libsystemd/sd-device -I ../src/libsystemd/sd-hwdb -I ../src/libsystemd/sd-id128 -I ../src/libsystemd/sd-netlink -I ../src/libsystemd/sd-network -I src/libsystemd-network -I ../src/libsystemd-network -D _FILE_OFFSET_BITS=64 -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/12.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wwrite-strings -Wno-unused-parameter -Wno-missing-field-initializers -Wno-unused-result -Wno-format-signedness -Wno-error=nonnull -std=gnu99 -fconst-strings -fdebug-compilation-dir /home/mrc0mmand/repos/@redhat-plumbers/systemd-rhel8/build-scan -ferror-limit 19 -fvisibility hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -analyzer-output=html -faddrsig -o /tmp/scan-build-2021-07-16-221226-1465241-1 -x c ../src/hwdb/hwdb.c
1/* SPDX-License-Identifier: LGPL-2.1+ */
2
3#include <ctype.h>
4#include <getopt.h>
5#include <stdlib.h>
6#include <string.h>
7
8#include "alloc-util.h"
9#include "conf-files.h"
10#include "fd-util.h"
11#include "fileio.h"
12#include "fs-util.h"
13#include "hwdb-internal.h"
14#include "hwdb-util.h"
15#include "label.h"
16#include "mkdir.h"
17#include "path-util.h"
18#include "selinux-util.h"
19#include "strbuf.h"
20#include "string-util.h"
21#include "strv.h"
22#include "util.h"
23#include "verbs.h"
24
25/*
26 * Generic udev properties, key-value database based on modalias strings.
27 * Uses a Patricia/radix trie to index all matches for efficient lookup.
28 */
29
30static const char *arg_hwdb_bin_dir = "/etc/udev";
31static const char *arg_root = "";
32static bool_Bool arg_strict;
33
34static const char * const conf_file_dirs[] = {
35 "/etc/udev/hwdb.d",
36 UDEVLIBEXECDIR"/usr/lib/udev" "/hwdb.d",
37 NULL((void*)0)
38};
39
40/* in-memory trie objects */
41struct trie {
42 struct trie_node *root;
43 struct strbuf *strings;
44
45 size_t nodes_count;
46 size_t children_count;
47 size_t values_count;
48};
49
50struct trie_node {
51 /* prefix, common part for all children of this node */
52 size_t prefix_off;
53
54 /* sorted array of pointers to children nodes */
55 struct trie_child_entry *children;
56 uint8_t children_count;
57
58 /* sorted array of key-value pairs */
59 struct trie_value_entry *values;
60 size_t values_count;
61};
62
63/* children array item with char (0-255) index */
64struct trie_child_entry {
65 uint8_t c;
66 struct trie_node *child;
67};
68
69/* value array item with key-value pairs */
70struct trie_value_entry {
71 size_t key_off;
72 size_t value_off;
73 size_t filename_off;
74 uint32_t line_number;
75 uint16_t file_priority;
76};
77
78static int trie_children_cmp(const void *v1, const void *v2) {
79 const struct trie_child_entry *n1 = v1;
80 const struct trie_child_entry *n2 = v2;
81
82 return n1->c - n2->c;
83}
84
85static int node_add_child(struct trie *trie, struct trie_node *node, struct trie_node *node_child, uint8_t c) {
86 struct trie_child_entry *child;
87
88 /* extend array, add new entry, sort for bisection */
89 child = reallocarray(node->children, node->children_count + 1, sizeof(struct trie_child_entry));
90 if (!child)
91 return -ENOMEM12;
92
93 node->children = child;
94 trie->children_count++;
95 node->children[node->children_count].c = c;
96 node->children[node->children_count].child = node_child;
97 node->children_count++;
98 qsort(node->children, node->children_count, sizeof(struct trie_child_entry), trie_children_cmp);
99 trie->nodes_count++;
100
101 return 0;
102}
103
104static struct trie_node *node_lookup(const struct trie_node *node, uint8_t c) {
105 struct trie_child_entry *child;
106 struct trie_child_entry search;
107
108 search.c = c;
109 child = bsearch_safe(&search, node->children, node->children_count, sizeof(struct trie_child_entry), trie_children_cmp);
110 if (child)
111 return child->child;
112 return NULL((void*)0);
113}
114
115static void trie_node_cleanup(struct trie_node *node) {
116 size_t i;
117
118 for (i = 0; i < node->children_count; i++)
119 trie_node_cleanup(node->children[i].child);
120 free(node->children);
121 free(node->values);
122 free(node);
123}
124
125static void trie_free(struct trie *trie) {
126 if (!trie)
127 return;
128
129 if (trie->root)
130 trie_node_cleanup(trie->root);
131
132 strbuf_cleanup(trie->strings);
133 free(trie);
134}
135
136DEFINE_TRIVIAL_CLEANUP_FUNC(struct trie*, trie_free)static inline void trie_freep(struct trie* *p) { if (*p) trie_free
(*p); }
;
137
138static int trie_values_cmp(const void *v1, const void *v2, void *arg) {
139 const struct trie_value_entry *val1 = v1;
140 const struct trie_value_entry *val2 = v2;
141 struct trie *trie = arg;
142
143 return strcmp(trie->strings->buf + val1->key_off,
144 trie->strings->buf + val2->key_off);
145}
146
147static int trie_node_add_value(struct trie *trie, struct trie_node *node,
148 const char *key, const char *value,
149 const char *filename, uint16_t file_priority, uint32_t line_number) {
150 ssize_t k, v, fn;
151 struct trie_value_entry *val;
152
153 k = strbuf_add_string(trie->strings, key, strlen(key));
154 if (k < 0)
155 return k;
156 v = strbuf_add_string(trie->strings, value, strlen(value));
157 if (v < 0)
158 return v;
159 fn = strbuf_add_string(trie->strings, filename, strlen(filename));
160 if (fn < 0)
161 return fn;
162
163 if (node->values_count) {
164 struct trie_value_entry search = {
165 .key_off = k,
166 .value_off = v,
167 };
168
169 val = xbsearch_r(&search, node->values, node->values_count, sizeof(struct trie_value_entry), trie_values_cmp, trie);
170 if (val) {
171 /* At this point we have 2 identical properties on the same match-string.
172 * Since we process files in order, we just replace the previous value.
173 */
174 val->value_off = v;
175 val->filename_off = fn;
176 val->file_priority = file_priority;
177 val->line_number = line_number;
178 return 0;
179 }
180 }
181
182 /* extend array, add new entry, sort for bisection */
183 val = reallocarray(node->values, node->values_count + 1, sizeof(struct trie_value_entry));
184 if (!val)
185 return -ENOMEM12;
186 trie->values_count++;
187 node->values = val;
188 node->values[node->values_count].key_off = k;
189 node->values[node->values_count].value_off = v;
190 node->values[node->values_count].filename_off = fn;
191 node->values[node->values_count].file_priority = file_priority;
192 node->values[node->values_count].line_number = line_number;
193 node->values_count++;
194 qsort_r(node->values, node->values_count, sizeof(struct trie_value_entry), trie_values_cmp, trie);
195 return 0;
196}
197
198static int trie_insert(struct trie *trie, struct trie_node *node, const char *search,
199 const char *key, const char *value,
200 const char *filename, uint16_t file_priority, uint32_t line_number) {
201 size_t i = 0;
202 int r = 0;
203
204 for (;;) {
58
Loop condition is true. Entering loop body
205 size_t p;
206 uint8_t c;
207 struct trie_node *child;
208
209 for (p = 0; (c = trie->strings->buf[node->prefix_off + p]); p++) {
59
Loop condition is true. Entering loop body
210 _cleanup_free___attribute__((cleanup(freep))) char *s = NULL((void*)0);
211 ssize_t off;
212 _cleanup_free___attribute__((cleanup(freep))) struct trie_node *new_child = NULL((void*)0);
213
214 if (c == search[i + p])
60
Assuming the condition is false
61
Taking false branch
215 continue;
216
217 /* split node */
218 new_child = new0(struct trie_node, 1)((struct trie_node*) calloc((1), sizeof(struct trie_node)));
219 if (!new_child)
62
Assuming 'new_child' is non-null
63
Taking false branch
220 return -ENOMEM12;
221
222 /* move values from parent to child */
223 new_child->prefix_off = node->prefix_off + p+1;
224 new_child->children = node->children;
225 new_child->children_count = node->children_count;
226 new_child->values = node->values;
227 new_child->values_count = node->values_count;
228
229 /* update parent; use strdup() because the source gets realloc()d */
230 s = strndup(trie->strings->buf + node->prefix_off, p);
64
Memory is allocated
231 if (!s)
65
Assuming 's' is non-null
66
Taking false branch
232 return -ENOMEM12;
233
234 off = strbuf_add_string(trie->strings, s, p);
67
Potential leak of memory pointed to by 's'
235 if (off < 0)
236 return off;
237
238 node->prefix_off = off;
239 node->children = NULL((void*)0);
240 node->children_count = 0;
241 node->values = NULL((void*)0);
242 node->values_count = 0;
243 r = node_add_child(trie, node, new_child, c);
244 if (r < 0)
245 return r;
246
247 new_child = NULL((void*)0); /* avoid cleanup */
248 break;
249 }
250 i += p;
251
252 c = search[i];
253 if (c == '\0')
254 return trie_node_add_value(trie, node, key, value, filename, file_priority, line_number);
255
256 child = node_lookup(node, c);
257 if (!child) {
258 ssize_t off;
259
260 /* new child */
261 child = new0(struct trie_node, 1)((struct trie_node*) calloc((1), sizeof(struct trie_node)));
262 if (!child)
263 return -ENOMEM12;
264
265 off = strbuf_add_string(trie->strings, search + i+1, strlen(search + i+1));
266 if (off < 0) {
267 free(child);
268 return off;
269 }
270
271 child->prefix_off = off;
272 r = node_add_child(trie, node, child, c);
273 if (r < 0) {
274 free(child);
275 return r;
276 }
277
278 return trie_node_add_value(trie, child, key, value, filename, file_priority, line_number);
279 }
280
281 node = child;
282 i++;
283 }
284}
285
286struct trie_f {
287 FILE *f;
288 struct trie *trie;
289 uint64_t strings_off;
290
291 uint64_t nodes_count;
292 uint64_t children_count;
293 uint64_t values_count;
294};
295
296/* calculate the storage space for the nodes, children arrays, value arrays */
297static void trie_store_nodes_size(struct trie_f *trie, struct trie_node *node) {
298 uint64_t i;
299
300 for (i = 0; i < node->children_count; i++)
301 trie_store_nodes_size(trie, node->children[i].child);
302
303 trie->strings_off += sizeof(struct trie_node_f);
304 for (i = 0; i < node->children_count; i++)
305 trie->strings_off += sizeof(struct trie_child_entry_f);
306 for (i = 0; i < node->values_count; i++)
307 trie->strings_off += sizeof(struct trie_value_entry2_f);
308}
309
310static int64_t trie_store_nodes(struct trie_f *trie, struct trie_node *node) {
311 uint64_t i;
312 struct trie_node_f n = {
313 .prefix_off = htole64(trie->strings_off + node->prefix_off),
314 .children_count = node->children_count,
315 .values_count = htole64(node->values_count),
316 };
317 _cleanup_free___attribute__((cleanup(freep))) struct trie_child_entry_f *children = NULL((void*)0);
318 int64_t node_off;
319
320 if (node->children_count) {
321 children = new(struct trie_child_entry_f, node->children_count)((struct trie_child_entry_f*) malloc_multiply(sizeof(struct trie_child_entry_f
), (node->children_count)))
;
322 if (!children)
323 return -ENOMEM12;
324 }
325
326 /* post-order recursion */
327 for (i = 0; i < node->children_count; i++) {
328 int64_t child_off;
329
330 child_off = trie_store_nodes(trie, node->children[i].child);
331 if (child_off < 0)
332 return child_off;
333
334 children[i] = (struct trie_child_entry_f) {
335 .c = node->children[i].c,
336 .child_off = htole64(child_off),
337 };
338 }
339
340 /* write node */
341 node_off = ftello(trie->f);
342 fwrite(&n, sizeof(struct trie_node_f), 1, trie->f);
343 trie->nodes_count++;
344
345 /* append children array */
346 if (node->children_count) {
347 fwrite(children, sizeof(struct trie_child_entry_f), node->children_count, trie->f);
348 trie->children_count += node->children_count;
349 }
350
351 /* append values array */
352 for (i = 0; i < node->values_count; i++) {
353 struct trie_value_entry2_f v = {
354 .key_off = htole64(trie->strings_off + node->values[i].key_off),
355 .value_off = htole64(trie->strings_off + node->values[i].value_off),
356 .filename_off = htole64(trie->strings_off + node->values[i].filename_off),
357 .line_number = htole32(node->values[i].line_number),
358 .file_priority = htole16(node->values[i].file_priority),
359 };
360
361 fwrite(&v, sizeof(struct trie_value_entry2_f), 1, trie->f);
362 }
363 trie->values_count += node->values_count;
364
365 return node_off;
366}
367
368static int trie_store(struct trie *trie, const char *filename) {
369 struct trie_f t = {
370 .trie = trie,
371 };
372 _cleanup_free___attribute__((cleanup(freep))) char *filename_tmp = NULL((void*)0);
373 int64_t pos;
374 int64_t root_off;
375 int64_t size;
376 struct trie_header_f h = {
377 .signature = HWDB_SIG{ 'K', 'S', 'L', 'P', 'H', 'H', 'R', 'H' },
378 .tool_version = htole64(atoi(PACKAGE_VERSION"239")),
379 .header_size = htole64(sizeof(struct trie_header_f)),
380 .node_size = htole64(sizeof(struct trie_node_f)),
381 .child_entry_size = htole64(sizeof(struct trie_child_entry_f)),
382 .value_entry_size = htole64(sizeof(struct trie_value_entry2_f)),
383 };
384 int r;
385
386 /* calculate size of header, nodes, children entries, value entries */
387 t.strings_off = sizeof(struct trie_header_f);
388 trie_store_nodes_size(&t, trie->root);
389
390 r = fopen_temporary(filename, &t.f, &filename_tmp);
391 if (r < 0)
392 return r;
393 fchmod(fileno(t.f), 0444);
394
395 /* write nodes */
396 if (fseeko(t.f, sizeof(struct trie_header_f), SEEK_SET0) < 0)
397 goto error_fclose;
398
399 root_off = trie_store_nodes(&t, trie->root);
400 h.nodes_root_off = htole64(root_off);
401 pos = ftello(t.f);
402 h.nodes_len = htole64(pos - sizeof(struct trie_header_f));
403
404 /* write string buffer */
405 fwrite(trie->strings->buf, trie->strings->len, 1, t.f);
406 h.strings_len = htole64(trie->strings->len);
407
408 /* write header */
409 size = ftello(t.f);
410 h.file_size = htole64(size);
411 if (fseeko(t.f, 0, SEEK_SET0) < 0)
412 goto error_fclose;
413 fwrite(&h, sizeof(struct trie_header_f), 1, t.f);
414
415 if (ferror(t.f))
416 goto error_fclose;
417 if (fflush(t.f) < 0)
418 goto error_fclose;
419 if (fsync(fileno(t.f)) < 0)
420 goto error_fclose;
421 if (rename(filename_tmp, filename) < 0)
422 goto error_fclose;
423
424 /* write succeeded */
425 fclose(t.f);
426
427 log_debug("=== trie on-disk ===")({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/hwdb/hwdb.c", 427, __func__, "=== trie on-disk ==="
) : -abs(_e); })
;
428 log_debug("size: %8"PRIi64" bytes", size)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/hwdb/hwdb.c", 428, __func__, "size: %8"
"l" "i"" bytes", size) : -abs(_e); })
;
429 log_debug("header: %8zu bytes", sizeof(struct trie_header_f))({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/hwdb/hwdb.c", 429, __func__, "header: %8zu bytes"
, sizeof(struct trie_header_f)) : -abs(_e); })
;
430 log_debug("nodes: %8"PRIu64" bytes (%8"PRIu64")",({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/hwdb/hwdb.c", 431, __func__, "nodes: %8"
"l" "u"" bytes (%8""l" "u"")", t.nodes_count * sizeof(struct trie_node_f
), t.nodes_count) : -abs(_e); })
431 t.nodes_count * sizeof(struct trie_node_f), t.nodes_count)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/hwdb/hwdb.c", 431, __func__, "nodes: %8"
"l" "u"" bytes (%8""l" "u"")", t.nodes_count * sizeof(struct trie_node_f
), t.nodes_count) : -abs(_e); })
;
432 log_debug("child pointers: %8"PRIu64" bytes (%8"PRIu64")",({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/hwdb/hwdb.c", 433, __func__, "child pointers: %8"
"l" "u"" bytes (%8""l" "u"")", t.children_count * sizeof(struct
trie_child_entry_f), t.children_count) : -abs(_e); })
433 t.children_count * sizeof(struct trie_child_entry_f), t.children_count)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/hwdb/hwdb.c", 433, __func__, "child pointers: %8"
"l" "u"" bytes (%8""l" "u"")", t.children_count * sizeof(struct
trie_child_entry_f), t.children_count) : -abs(_e); })
;
434 log_debug("value pointers: %8"PRIu64" bytes (%8"PRIu64")",({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/hwdb/hwdb.c", 435, __func__, "value pointers: %8"
"l" "u"" bytes (%8""l" "u"")", t.values_count * sizeof(struct
trie_value_entry2_f), t.values_count) : -abs(_e); })
435 t.values_count * sizeof(struct trie_value_entry2_f), t.values_count)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/hwdb/hwdb.c", 435, __func__, "value pointers: %8"
"l" "u"" bytes (%8""l" "u"")", t.values_count * sizeof(struct
trie_value_entry2_f), t.values_count) : -abs(_e); })
;
436 log_debug("string store: %8zu bytes", trie->strings->len)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/hwdb/hwdb.c", 436, __func__, "string store: %8zu bytes"
, trie->strings->len) : -abs(_e); })
;
437 log_debug("strings start: %8"PRIu64, t.strings_off)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/hwdb/hwdb.c", 437, __func__, "strings start: %8"
"l" "u", t.strings_off) : -abs(_e); })
;
438 return 0;
439
440 error_fclose:
441 r = -errno(*__errno_location ());
442 fclose(t.f);
443 unlink(filename_tmp);
444 return r;
445}
446
447static int insert_data(struct trie *trie, char **match_list, char *line,
448 const char *filename, uint16_t file_priority, uint32_t line_number) {
449 char *value, **entry;
450
451 assert(line[0] == ' ')do { if ((__builtin_expect(!!(!(line[0] == ' ')),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("line[0] == ' '"), "../src/hwdb/hwdb.c",
451, __PRETTY_FUNCTION__); } while (0)
;
47
Assuming the condition is false
48
Taking false branch
49
Loop condition is false. Exiting loop
452
453 value = strchr(line, '=');
454 if (!value)
50
Assuming 'value' is non-null
51
Taking false branch
455 return log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,({ int _level = (4), _e = (22); (log_get_max_level_realm(LOG_REALM_SYSTEMD
) >= ((_level) & 0x07)) ? log_syntax_internal(((void*)
0), _level, filename, line_number, _e, "../src/hwdb/hwdb.c", 456
, __func__, "Key-value pair expected but got \"%s\", ignoring"
, line) : -abs(_e); })
456 "Key-value pair expected but got \"%s\", ignoring", line)({ int _level = (4), _e = (22); (log_get_max_level_realm(LOG_REALM_SYSTEMD
) >= ((_level) & 0x07)) ? log_syntax_internal(((void*)
0), _level, filename, line_number, _e, "../src/hwdb/hwdb.c", 456
, __func__, "Key-value pair expected but got \"%s\", ignoring"
, line) : -abs(_e); })
;
457
458 value[0] = '\0';
459 value++;
460
461 /* Replace multiple leading spaces by a single space */
462 while (isblank(line[0])((*__ctype_b_loc ())[(int) ((line[0]))] & (unsigned short
int) _ISblank)
&& isblank(line[1])((*__ctype_b_loc ())[(int) ((line[1]))] & (unsigned short
int) _ISblank)
)
52
Assuming the condition is false
463 line++;
464
465 if (isempty(line + 1) || isempty(value))
53
Assuming the condition is false
54
Taking false branch
466 return log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,({ int _level = (4), _e = (22); (log_get_max_level_realm(LOG_REALM_SYSTEMD
) >= ((_level) & 0x07)) ? log_syntax_internal(((void*)
0), _level, filename, line_number, _e, "../src/hwdb/hwdb.c", 469
, __func__, "Empty %s in \"%s=%s\", ignoring", isempty(line +
1) ? "key" : "value", line, value) : -abs(_e); })
467 "Empty %s in \"%s=%s\", ignoring",({ int _level = (4), _e = (22); (log_get_max_level_realm(LOG_REALM_SYSTEMD
) >= ((_level) & 0x07)) ? log_syntax_internal(((void*)
0), _level, filename, line_number, _e, "../src/hwdb/hwdb.c", 469
, __func__, "Empty %s in \"%s=%s\", ignoring", isempty(line +
1) ? "key" : "value", line, value) : -abs(_e); })
468 isempty(line + 1) ? "key" : "value",({ int _level = (4), _e = (22); (log_get_max_level_realm(LOG_REALM_SYSTEMD
) >= ((_level) & 0x07)) ? log_syntax_internal(((void*)
0), _level, filename, line_number, _e, "../src/hwdb/hwdb.c", 469
, __func__, "Empty %s in \"%s=%s\", ignoring", isempty(line +
1) ? "key" : "value", line, value) : -abs(_e); })
469 line, value)({ int _level = (4), _e = (22); (log_get_max_level_realm(LOG_REALM_SYSTEMD
) >= ((_level) & 0x07)) ? log_syntax_internal(((void*)
0), _level, filename, line_number, _e, "../src/hwdb/hwdb.c", 469
, __func__, "Empty %s in \"%s=%s\", ignoring", isempty(line +
1) ? "key" : "value", line, value) : -abs(_e); })
;
470
471 STRV_FOREACH(entry, match_list)for ((entry) = (match_list); (entry) && *(entry); (entry
)++)
55
Assuming 'entry' is non-null
56
Loop condition is true. Entering loop body
472 trie_insert(trie, trie->root, *entry, line, value, filename, file_priority, line_number);
57
Calling 'trie_insert'
473
474 return 0;
475}
476
477static int import_file(struct trie *trie, const char *filename, uint16_t file_priority) {
478 enum {
479 HW_NONE,
480 HW_MATCH,
481 HW_DATA,
482 } state = HW_NONE;
483 _cleanup_fclose___attribute__((cleanup(fclosep))) FILE *f = NULL((void*)0);
484 char line[LINE_MAX2048];
485 _cleanup_strv_free___attribute__((cleanup(strv_freep))) char **match_list = NULL((void*)0);
486 uint32_t line_number = 0;
487 char *match = NULL((void*)0);
488 int r = 0, err;
489
490 f = fopen(filename, "re");
491 if (!f)
17
Assuming 'f' is non-null
18
Taking false branch
492 return -errno(*__errno_location ());
493
494 while (fgets(line, sizeof(line), f)) {
19
Loop condition is true. Entering loop body
35
Loop condition is true. Entering loop body
495 size_t len;
496 char *pos;
497
498 ++line_number;
499
500 /* comment line */
501 if (line[0] == '#')
20
Assuming the condition is false
21
Taking false branch
36
Assuming the condition is false
37
Taking false branch
502 continue;
503
504 /* strip trailing comment */
505 pos = strchr(line, '#');
506 if (pos)
22
Assuming 'pos' is null
23
Taking false branch
38
Assuming 'pos' is null
39
Taking false branch
507 pos[0] = '\0';
508
509 /* strip trailing whitespace */
510 len = strlen(line);
511 while (len > 0 && isspace(line[len-1])((*__ctype_b_loc ())[(int) ((line[len-1]))] & (unsigned short
int) _ISspace)
)
24
Assuming 'len' is > 0
25
Loop condition is false. Execution continues on line 513
40
Assuming 'len' is > 0
41
Loop condition is false. Execution continues on line 513
512 len--;
513 line[len] = '\0';
514
515 switch (state) {
26
Control jumps to 'case HW_NONE:' at line 516
42
Control jumps to 'case HW_MATCH:' at line 540
516 case HW_NONE:
517 if (len
26.1
'len' is not equal to 0
== 0)
27
Taking false branch
518 break;
519
520 if (line[0] == ' ') {
28
Assuming the condition is false
29
Taking false branch
521 log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,({ int _level = (4), _e = (22); (log_get_max_level_realm(LOG_REALM_SYSTEMD
) >= ((_level) & 0x07)) ? log_syntax_internal(((void*)
0), _level, filename, line_number, _e, "../src/hwdb/hwdb.c", 522
, __func__, "Match expected but got indented property \"%s\", ignoring line"
, line) : -abs(_e); })
522 "Match expected but got indented property \"%s\", ignoring line", line)({ int _level = (4), _e = (22); (log_get_max_level_realm(LOG_REALM_SYSTEMD
) >= ((_level) & 0x07)) ? log_syntax_internal(((void*)
0), _level, filename, line_number, _e, "../src/hwdb/hwdb.c", 522
, __func__, "Match expected but got indented property \"%s\", ignoring line"
, line) : -abs(_e); })
;
523 r = -EINVAL22;
524 break;
525 }
526
527 /* start of record, first match */
528 state = HW_MATCH;
529
530 match = strdup(line);
531 if (!match)
30
Assuming 'match' is non-null
31
Taking false branch
532 return -ENOMEM12;
533
534 err = strv_consume(&match_list, match);
535 if (err < 0)
32
Assuming 'err' is >= 0
33
Taking false branch
536 return err;
537
538 break;
34
Execution continues on line 494
539
540 case HW_MATCH:
541 if (len
42.1
'len' is not equal to 0
== 0) {
43
Taking false branch
542 log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,({ int _level = (4), _e = (22); (log_get_max_level_realm(LOG_REALM_SYSTEMD
) >= ((_level) & 0x07)) ? log_syntax_internal(((void*)
0), _level, filename, line_number, _e, "../src/hwdb/hwdb.c", 543
, __func__, "Property expected, ignoring record with no properties"
) : -abs(_e); })
543 "Property expected, ignoring record with no properties")({ int _level = (4), _e = (22); (log_get_max_level_realm(LOG_REALM_SYSTEMD
) >= ((_level) & 0x07)) ? log_syntax_internal(((void*)
0), _level, filename, line_number, _e, "../src/hwdb/hwdb.c", 543
, __func__, "Property expected, ignoring record with no properties"
) : -abs(_e); })
;
544 r = -EINVAL22;
545 state = HW_NONE;
546 strv_clear(match_list);
547 break;
548 }
549
550 if (line[0] != ' ') {
44
Assuming the condition is false
45
Taking false branch
551 /* another match */
552 match = strdup(line);
553 if (!match)
554 return -ENOMEM12;
555
556 err = strv_consume(&match_list, match);
557 if (err < 0)
558 return err;
559
560 break;
561 }
562
563 /* first data */
564 state = HW_DATA;
565 err = insert_data(trie, match_list, line, filename, file_priority, line_number);
46
Calling 'insert_data'
566 if (err < 0)
567 r = err;
568 break;
569
570 case HW_DATA:
571 if (len == 0) {
572 /* end of record */
573 state = HW_NONE;
574 strv_clear(match_list);
575 break;
576 }
577
578 if (line[0] != ' ') {
579 log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,({ int _level = (4), _e = (22); (log_get_max_level_realm(LOG_REALM_SYSTEMD
) >= ((_level) & 0x07)) ? log_syntax_internal(((void*)
0), _level, filename, line_number, _e, "../src/hwdb/hwdb.c", 580
, __func__, "Property or empty line expected, got \"%s\", ignoring record"
, line) : -abs(_e); })
580 "Property or empty line expected, got \"%s\", ignoring record", line)({ int _level = (4), _e = (22); (log_get_max_level_realm(LOG_REALM_SYSTEMD
) >= ((_level) & 0x07)) ? log_syntax_internal(((void*)
0), _level, filename, line_number, _e, "../src/hwdb/hwdb.c", 580
, __func__, "Property or empty line expected, got \"%s\", ignoring record"
, line) : -abs(_e); })
;
581 r = -EINVAL22;
582 state = HW_NONE;
583 strv_clear(match_list);
584 break;
585 }
586
587 err = insert_data(trie, match_list, line, filename, file_priority, line_number);
588 if (err < 0)
589 r = err;
590 break;
591 };
592 }
593
594 if (state == HW_MATCH)
595 log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,({ int _level = (4), _e = (22); (log_get_max_level_realm(LOG_REALM_SYSTEMD
) >= ((_level) & 0x07)) ? log_syntax_internal(((void*)
0), _level, filename, line_number, _e, "../src/hwdb/hwdb.c", 596
, __func__, "Property expected, ignoring record with no properties"
) : -abs(_e); })
596 "Property expected, ignoring record with no properties")({ int _level = (4), _e = (22); (log_get_max_level_realm(LOG_REALM_SYSTEMD
) >= ((_level) & 0x07)) ? log_syntax_internal(((void*)
0), _level, filename, line_number, _e, "../src/hwdb/hwdb.c", 596
, __func__, "Property expected, ignoring record with no properties"
) : -abs(_e); })
;
597
598 return r;
599}
600
601static int hwdb_query(int argc, char *argv[], void *userdata) {
602 _cleanup_(sd_hwdb_unrefp)__attribute__((cleanup(sd_hwdb_unrefp))) sd_hwdb *hwdb = NULL((void*)0);
603 const char *key, *value;
604 const char *modalias;
605 int r;
606
607 assert(argc >= 2)do { if ((__builtin_expect(!!(!(argc >= 2)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("argc >= 2"), "../src/hwdb/hwdb.c", 607
, __PRETTY_FUNCTION__); } while (0)
;
608 assert(argv)do { if ((__builtin_expect(!!(!(argv)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("argv"), "../src/hwdb/hwdb.c", 608, __PRETTY_FUNCTION__
); } while (0)
;
609
610 modalias = argv[1];
611
612 r = sd_hwdb_new(&hwdb);
613 if (r < 0)
614 return r;
615
616 SD_HWDB_FOREACH_PROPERTY(hwdb, modalias, key, value)if (sd_hwdb_seek(hwdb, modalias) < 0) { } else while (sd_hwdb_enumerate
(hwdb, &(key), &(value)) > 0)
617 printf("%s=%s\n", key, value);
618
619 return 0;
620}
621
622static int hwdb_update(int argc, char *argv[], void *userdata) {
623 _cleanup_free___attribute__((cleanup(freep))) char *hwdb_bin = NULL((void*)0);
624 _cleanup_(trie_freep)__attribute__((cleanup(trie_freep))) struct trie *trie = NULL((void*)0);
625 _cleanup_strv_free___attribute__((cleanup(strv_freep))) char **files = NULL((void*)0);
626 char **f;
627 uint16_t file_priority = 1;
628 int r = 0, err;
629
630 trie = new0(struct trie, 1)((struct trie*) calloc((1), sizeof(struct trie)));
631 if (!trie)
1
Assuming 'trie' is non-null
2
Taking false branch
632 return -ENOMEM12;
633
634 /* string store */
635 trie->strings = strbuf_new();
636 if (!trie->strings)
3
Assuming field 'strings' is non-null
4
Taking false branch
637 return -ENOMEM12;
638
639 /* index */
640 trie->root = new0(struct trie_node, 1)((struct trie_node*) calloc((1), sizeof(struct trie_node)));
641 if (!trie->root)
5
Assuming field 'root' is non-null
6
Taking false branch
642 return -ENOMEM12;
643
644 trie->nodes_count++;
645
646 err = conf_files_list_strv(&files, ".hwdb", arg_root, 0, conf_file_dirs);
647 if (err < 0)
7
Assuming 'err' is >= 0
8
Taking false branch
648 return log_error_errno(err, "Failed to enumerate hwdb files: %m")({ int _level = ((3)), _e = ((err)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/hwdb/hwdb.c", 648, __func__, "Failed to enumerate hwdb files: %m"
) : -abs(_e); })
;
649
650 STRV_FOREACH(f, files)for ((f) = (files); (f) && *(f); (f)++) {
9
Assuming 'f' is non-null
10
Loop condition is true. Entering loop body
13
Loop condition is true. Entering loop body
651 log_debug("Reading file \"%s\"", *f)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/hwdb/hwdb.c", 651, __func__, "Reading file \"%s\"",
*f) : -abs(_e); })
;
11
Assuming the condition is false
12
'?' condition is false
14
Assuming the condition is true
15
'?' condition is true
652 err = import_file(trie, *f, file_priority++);
16
Calling 'import_file'
653 if (err
12.1
'err' is >= 0
< 0 && arg_strict)
654 r = err;
655 }
656
657 strbuf_complete(trie->strings);
658
659 log_debug("=== trie in-memory ===")({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/hwdb/hwdb.c", 659, __func__, "=== trie in-memory ==="
) : -abs(_e); })
;
660 log_debug("nodes: %8zu bytes (%8zu)",({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/hwdb/hwdb.c", 661, __func__, "nodes: %8zu bytes (%8zu)"
, trie->nodes_count * sizeof(struct trie_node), trie->nodes_count
) : -abs(_e); })
661 trie->nodes_count * sizeof(struct trie_node), trie->nodes_count)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/hwdb/hwdb.c", 661, __func__, "nodes: %8zu bytes (%8zu)"
, trie->nodes_count * sizeof(struct trie_node), trie->nodes_count
) : -abs(_e); })
;
662 log_debug("children arrays: %8zu bytes (%8zu)",({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/hwdb/hwdb.c", 663, __func__, "children arrays: %8zu bytes (%8zu)"
, trie->children_count * sizeof(struct trie_child_entry), trie
->children_count) : -abs(_e); })
663 trie->children_count * sizeof(struct trie_child_entry), trie->children_count)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/hwdb/hwdb.c", 663, __func__, "children arrays: %8zu bytes (%8zu)"
, trie->children_count * sizeof(struct trie_child_entry), trie
->children_count) : -abs(_e); })
;
664 log_debug("values arrays: %8zu bytes (%8zu)",({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/hwdb/hwdb.c", 665, __func__, "values arrays: %8zu bytes (%8zu)"
, trie->values_count * sizeof(struct trie_value_entry), trie
->values_count) : -abs(_e); })
665 trie->values_count * sizeof(struct trie_value_entry), trie->values_count)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/hwdb/hwdb.c", 665, __func__, "values arrays: %8zu bytes (%8zu)"
, trie->values_count * sizeof(struct trie_value_entry), trie
->values_count) : -abs(_e); })
;
666 log_debug("strings: %8zu bytes",({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/hwdb/hwdb.c", 667, __func__, "strings: %8zu bytes"
, trie->strings->len) : -abs(_e); })
667 trie->strings->len)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/hwdb/hwdb.c", 667, __func__, "strings: %8zu bytes"
, trie->strings->len) : -abs(_e); })
;
668 log_debug("strings incoming: %8zu bytes (%8zu)",({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/hwdb/hwdb.c", 669, __func__, "strings incoming: %8zu bytes (%8zu)"
, trie->strings->in_len, trie->strings->in_count)
: -abs(_e); })
669 trie->strings->in_len, trie->strings->in_count)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/hwdb/hwdb.c", 669, __func__, "strings incoming: %8zu bytes (%8zu)"
, trie->strings->in_len, trie->strings->in_count)
: -abs(_e); })
;
670 log_debug("strings dedup'ed: %8zu bytes (%8zu)",({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/hwdb/hwdb.c", 671, __func__, "strings dedup'ed: %8zu bytes (%8zu)"
, trie->strings->dedup_len, trie->strings->dedup_count
) : -abs(_e); })
671 trie->strings->dedup_len, trie->strings->dedup_count)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/hwdb/hwdb.c", 671, __func__, "strings dedup'ed: %8zu bytes (%8zu)"
, trie->strings->dedup_len, trie->strings->dedup_count
) : -abs(_e); })
;
672
673 hwdb_bin = path_join(arg_root, arg_hwdb_bin_dir, "hwdb.bin");
674 if (!hwdb_bin)
675 return -ENOMEM12;
676
677 mkdir_parents_label(hwdb_bin, 0755);
678 err = trie_store(trie, hwdb_bin);
679 if (err < 0)
680 return log_error_errno(err, "Failure writing database %s: %m", hwdb_bin)({ int _level = ((3)), _e = ((err)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/hwdb/hwdb.c", 680, __func__, "Failure writing database %s: %m"
, hwdb_bin) : -abs(_e); })
;
681
682 err = label_fix(hwdb_bin, 0);
683 if (err < 0)
684 return err;
685
686 return r;
687}
688
689static void help(void) {
690 printf("Usage: %s OPTIONS COMMAND\n\n"
691 "Update or query the hardware database.\n\n"
692 " -h --help Show this help\n"
693 " --version Show package version\n"
694 " -s --strict When updating, return non-zero exit value on any parsing error\n"
695 " --usr Generate in " UDEVLIBEXECDIR"/usr/lib/udev" " instead of /etc/udev\n"
696 " -r --root=PATH Alternative root path in the filesystem\n\n"
697 "Commands:\n"
698 " update Update the hwdb database\n"
699 " query MODALIAS Query database and print result\n",
700 program_invocation_short_name);
701}
702
703static int parse_argv(int argc, char *argv[]) {
704 enum {
705 ARG_VERSION = 0x100,
706 ARG_USR,
707 };
708
709 static const struct option options[] = {
710 { "help", no_argument0, NULL((void*)0), 'h' },
711 { "version", no_argument0, NULL((void*)0), ARG_VERSION },
712 { "usr", no_argument0, NULL((void*)0), ARG_USR },
713 { "strict", no_argument0, NULL((void*)0), 's' },
714 { "root", required_argument1, NULL((void*)0), 'r' },
715 {}
716 };
717
718 int c;
719
720 assert(argc >= 0)do { if ((__builtin_expect(!!(!(argc >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("argc >= 0"), "../src/hwdb/hwdb.c", 720
, __PRETTY_FUNCTION__); } while (0)
;
721 assert(argv)do { if ((__builtin_expect(!!(!(argv)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("argv"), "../src/hwdb/hwdb.c", 721, __PRETTY_FUNCTION__
); } while (0)
;
722
723 while ((c = getopt_long(argc, argv, "ust:r:h", options, NULL((void*)0))) >= 0) {
724 switch(c) {
725
726 case 'h':
727 help();
728 return 0;
729
730 case ARG_VERSION:
731 return version();
732
733 case ARG_USR:
734 arg_hwdb_bin_dir = UDEVLIBEXECDIR"/usr/lib/udev";
735 break;
736
737 case 's':
738 arg_strict = true1;
739 break;
740
741 case 'r':
742 arg_root = optarg;
743 break;
744
745 case '?':
746 return -EINVAL22;
747
748 default:
749 assert_not_reached("Unknown option")do { log_assert_failed_unreachable_realm(LOG_REALM_SYSTEMD, (
"Unknown option"), "../src/hwdb/hwdb.c", 749, __PRETTY_FUNCTION__
); } while (0)
;
750 }
751 }
752
753 return 1;
754}
755
756static int hwdb_main(int argc, char *argv[]) {
757 static const Verb verbs[] = {
758 { "update", 1, 1, 0, hwdb_update },
759 { "query", 2, 2, 0, hwdb_query },
760 {},
761 };
762
763 return dispatch_verb(argc, argv, verbs, NULL((void*)0));
764}
765
766int main (int argc, char *argv[]) {
767 int r;
768
769 log_parse_environment()log_parse_environment_realm(LOG_REALM_SYSTEMD);
770 log_open();
771
772 r = parse_argv(argc, argv);
773 if (r <= 0)
774 goto finish;
775
776 mac_selinux_init();
777
778 r = hwdb_main(argc, argv);
779
780finish:
781 return r < 0 ? EXIT_FAILURE1 : EXIT_SUCCESS0;
782}