• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

stefanberger / swtpm / #2796

26 Apr 2025 04:38PM UTC coverage: 73.437%. Remained the same
#2796

push

travis-ci

web-flow
Merge 27ec537e2 into a507efb5b

43 of 54 new or added lines in 2 files covered. (79.63%)

85 existing lines in 2 files now uncovered.

8048 of 10959 relevant lines covered (73.44%)

13600.47 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.11
/src/swtpm/utils.c
1
/*
2
 * utils.s -- utilities
3
 *
4
 * (c) Copyright IBM Corporation 2014, 2015, 2019.
5
 *
6
 * Author: Stefan Berger <stefanb@us.ibm.com>
7
 *
8
 * All rights reserved.
9
 *
10
 * Redistribution and use in source and binary forms, with or without
11
 * modification, are permitted provided that the following conditions are
12
 * met:
13
 *
14
 * Redistributions of source code must retain the above copyright notice,
15
 * this list of conditions and the following disclaimer.
16
 *
17
 * Redistributions in binary form must reproduce the above copyright
18
 * notice, this list of conditions and the following disclaimer in the
19
 * documentation and/or other materials provided with the distribution.
20
 *
21
 * Neither the names of the IBM Corporation nor the names of its
22
 * contributors may be used to endorse or promote products derived from
23
 * this software without specific prior written permission.
24
 *
25
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36
 */
37

38
#include "config.h"
39

40
#include <grp.h>
41
#include <pwd.h>
42
#include <fcntl.h>
43
#include <unistd.h>
44
#include <limits.h>
45
#include <stdlib.h>
46
#include <string.h>
47
#include <errno.h>
48
#include <sys/stat.h>
49

50
#if defined __APPLE__
51
#include <fcntl.h>
52
#include <sys/param.h>
53
#endif
54

55
#include <json-glib/json-glib.h>
56

57
#include <openssl/rand.h>
58

59
#include "utils.h"
60
#include "logging.h"
61
#include "tpmlib.h"
62
#include "swtpm_debug.h"
63

64
void uninstall_sighandlers()
409✔
65
{
66
    if (signal(SIGTERM, SIG_DFL) == SIG_ERR)
409✔
UNCOV
67
        logprintf(STDERR_FILENO, "Could not uninstall signal handler for SIGTERM.\n");
×
68

69
    if (signal(SIGPIPE, SIG_DFL) == SIG_ERR)
409✔
UNCOV
70
        logprintf(STDERR_FILENO, "Could not uninstall signal handler for SIGPIPE.\n");
×
71
}
409✔
72

73
int install_sighandlers(int pipefd[2], sighandler_t handler)
409✔
74
{
75
    if (pipe(pipefd) < 0) {
409✔
UNCOV
76
        logprintf(STDERR_FILENO, "Error: Could not open pipe.\n");
×
77
        goto err_exit;
×
78
    }
79

80
    if (signal(SIGTERM, handler) == SIG_ERR) {
409✔
UNCOV
81
        logprintf(STDERR_FILENO, "Could not install signal handler for SIGTERM.\n");
×
82
        goto err_close_pipe;
×
83
    }
84

85
    if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
409✔
UNCOV
86
        logprintf(STDERR_FILENO, "Could not install signal handler for SIGPIPE.\n");
×
87
        goto err_close_pipe;
×
88
    }
89

90
    return 0;
91

UNCOV
92
err_close_pipe:
×
93
    close(pipefd[0]);
×
94
    pipefd[0] = -1;
×
95
    close(pipefd[1]);
×
96
    pipefd[1] = -1;
×
97

98
err_exit:
99
    return -1;
100
}
101

102
int
UNCOV
103
change_process_owner(const char *user)
×
104
{
UNCOV
105
    struct passwd *passwd;
×
106
    long int uid, gid;
×
107
    char *endptr = NULL;
×
108

UNCOV
109
    uid = strtoul(user, &endptr, 10);
×
110
    if (*endptr != '\0') {
×
111
        /* a string */
UNCOV
112
        passwd = getpwnam(user);
×
113
        if (!passwd) {
×
114
            logprintf(STDERR_FILENO,
×
115
                      "Error: User '%s' does not exist.\n",
116
                      user);
UNCOV
117
            return -14;
×
118
        }
119

UNCOV
120
        if (initgroups(passwd->pw_name, passwd->pw_gid) < 0) {
×
121
            logprintf(STDERR_FILENO,
×
122
                      "Error: initgroups(%s, %d) failed.\n",
123
                      passwd->pw_name, passwd->pw_gid);
UNCOV
124
           return -10;
×
125
        }
UNCOV
126
        gid = passwd->pw_gid;
×
127
        uid = passwd->pw_uid;
×
128
    } else {
129
        /* an integer */
UNCOV
130
        if ((unsigned long int)uid > UINT_MAX) {
×
131
            logprintf(STDERR_FILENO,
×
132
                      "Error: uid %s outside valid range.\n",
133
                      user);
UNCOV
134
            return -13;
×
135
        }
136
        gid = uid;
137
    }
138

UNCOV
139
    if (setgid(gid) < 0) {
×
140
        logprintf(STDERR_FILENO,
×
141
                  "Error: setgid(%d) failed.\n",
142
                  gid);
UNCOV
143
        return -11;
×
144
    }
UNCOV
145
    if (setuid(uid) < 0) {
×
146
        logprintf(STDERR_FILENO,
×
147
                  "Error: setuid(%d) failed.\n",
148
                  uid);
UNCOV
149
        return -12;
×
150
    }
151
    return 0;
152
}
153

154
int
UNCOV
155
do_chroot(const char *path)
×
156
{
UNCOV
157
    if (chroot(path) < 0) {
×
158
        logprintf(STDERR_FILENO, "chroot failed: %s\n",
×
159
                  strerror(errno));
×
160
        return -1;
×
161
    }
162

UNCOV
163
    if (chdir("/") < 0) {
×
164
        logprintf(STDERR_FILENO, "chdir failed: %s\n",
×
165
                  strerror(errno));
×
166
        return -1;
×
167
    }
168

UNCOV
169
    if (!RAND_status()) {
×
170
        logprintf(STDERR_FILENO,
×
171
                  "Error: no good entropy source in chroot environment\n");
UNCOV
172
        return -1;
×
173
    }
174

175
    return 0;
176
}
177

178
void tpmlib_debug_libtpms_parameters(TPMLIB_TPMVersion tpmversion)
442✔
179
{
180
    switch (tpmversion) {
442✔
181
    case TPMLIB_TPM_VERSION_1_2:
182
        TPM_DEBUG("TPM 1.2: Compiled for %u auth, %u transport, "
183
                  "and %u DAA session slots\n",
184
            tpmlib_get_tpm_property(TPMPROP_TPM_MIN_AUTH_SESSIONS),
185
            tpmlib_get_tpm_property(TPMPROP_TPM_MIN_TRANS_SESSIONS),
186
            tpmlib_get_tpm_property(TPMPROP_TPM_MIN_DAA_SESSIONS));
187
        TPM_DEBUG("TPM 1.2: Compiled for %u key slots, %u owner evict slots\n",
188
            tpmlib_get_tpm_property(TPMPROP_TPM_KEY_HANDLES),
189
            tpmlib_get_tpm_property(TPMPROP_TPM_OWNER_EVICT_KEY_HANDLES));
190
        TPM_DEBUG("TPM 1.2: Compiled for %u counters, %u saved sessions\n",
191
            tpmlib_get_tpm_property(TPMPROP_TPM_MIN_COUNTERS),
192
            tpmlib_get_tpm_property(TPMPROP_TPM_MIN_SESSION_LIST));
193
        TPM_DEBUG("TPM 1.2: Compiled for %u family, "
194
                  "%u delegate table entries\n",
195
            tpmlib_get_tpm_property(TPMPROP_TPM_NUM_FAMILY_TABLE_ENTRY_MIN),
196
            tpmlib_get_tpm_property(TPMPROP_TPM_NUM_DELEGATE_TABLE_ENTRY_MIN));
197
        TPM_DEBUG("TPM 1.2: Compiled for %u total NV, %u savestate, "
198
                  "%u volatile space\n",
199
            tpmlib_get_tpm_property(TPMPROP_TPM_MAX_NV_SPACE),
200
            tpmlib_get_tpm_property(TPMPROP_TPM_MAX_SAVESTATE_SPACE),
201
            tpmlib_get_tpm_property(TPMPROP_TPM_MAX_VOLATILESTATE_SPACE));
202
#if 0
203
        TPM_DEBUG("TPM1.2: Compiled for %u NV defined space\n",
204
            tpmlib_get_tpm_property(TPMPROP_TPM_MAX_NV_DEFINED_SIZE));
205
#endif
206
    break;
207
    case TPMLIB_TPM_VERSION_2:
208
    break;
209
    }
210
}
442✔
211

UNCOV
212
char *fd_to_filename(int fd)
×
213
{
214
#if defined __linux__
215

UNCOV
216
    char buffer[64];
×
217
    char *path;
×
218

UNCOV
219
    snprintf(buffer, sizeof(buffer), "/proc/self/fd/%d", fd);
×
220

UNCOV
221
    path = realpath(buffer, NULL);
×
222
    if (!path) {
×
223
        logprintf(STDERR_FILENO, "Could not read %s: %s\n",
×
224
                  buffer, strerror(errno));
×
225
        return NULL;
×
226
    }
227

228
    return path;
229

230
#elif defined __APPLE__
231

232
    char *path = malloc(MAXPATHLEN);
233
    if (!path) {
234
        logprintf(STDERR_FILENO, "Out of memory.\n");
235
        return NULL;
236
    }
237
    if (fcntl(fd, F_GETPATH, path) < 0) {
238
        logprintf(STDERR_FILENO, "fcntl for F_GETPATH failed: %\n",
239
                  strerror(errno));
240
        free(path);
241
        return NULL;
242
    }
243
    return path;
244

245
#else
246
    (void)fd;
247
    logprintf(STDERR_FILENO,
248
              "Cannot convert file descriptor to filename on this platform.\n");
249
    return NULL;
250

251
#endif
252
}
253

254
/*
255
 * write_full: Write all bytes of a buffer into the file descriptor
256
 *             and handle partial writes on the way.
257
 *
258
 * @fd: file descriptor to write to
259
 * @buffer: buffer
260
 * @buflen: length of buffer
261
 *
262
 * Returns -1 in case not all bytes could be transferred, number of
263
 * bytes written otherwise (must be equal to buflen).
264
 */
265
ssize_t write_full(int fd, const void *buffer, size_t buflen)
93,205✔
266
{
267
    size_t written = 0;
93,205✔
268
    ssize_t n;
93,205✔
269

270
    while (written < buflen) {
186,410✔
271
        n = write(fd, buffer, buflen - written);
93,205✔
272
        if (n == 0)
93,205✔
273
            return -1;
274
        if (n < 0) {
93,205✔
UNCOV
275
            if (errno == EINTR)
×
276
                continue;
×
277
            return -1;
278
        }
279
        written += n;
93,205✔
280
        buffer = (const char *)buffer + n;
93,205✔
281
    }
282
    return written;
93,205✔
283
}
284

285
/*
286
 * writev_full: Write all bytes of an iovec into the file descriptor
287
 *              and handle partial writes on the way.
288
 * @fd: file descriptor to write to
289
 * @iov: pointer to iov
290
 * @iovcnt: length of iov array
291
 *
292
 * Returns -1 in case not all bytes could be transferred, number of
293
 * bytes written otherwise (must be equal to buflen).
294
 */
295
ssize_t writev_full(int fd, const struct iovec *iov, int iovcnt)
22,126✔
296
{
297
    int i;
22,126✔
298
    size_t off;
22,126✔
299
    unsigned char *buf;
22,126✔
300
    ssize_t n;
22,126✔
301
    size_t bytecount = 0;
22,126✔
302
    size_t numbufs = 0;
22,126✔
303
    size_t lastidx = -1;
22,126✔
304

305
    for (i = 0; i < iovcnt; i++) {
88,445✔
306
        if (iov[i].iov_len) {
66,319✔
307
            bytecount += iov[i].iov_len;
35,065✔
308
            numbufs++;
35,065✔
309
            lastidx = i;
35,065✔
310
        }
311
    }
312

313
    if (numbufs == 1)
22,126✔
314
        return write_full(fd, iov[lastidx].iov_base, iov[lastidx].iov_len);
15,630✔
315

316
    buf = malloc(bytecount);
6,496✔
317
    if (!buf) {
6,496✔
UNCOV
318
        errno = ENOMEM;
×
319
        return -1;
×
320
    }
321

322
    off = 0;
323
    for (i = 0; i < iovcnt; i++) {
25,931✔
324
        if (!iov[i].iov_len)
19,435✔
UNCOV
325
            continue;
×
326
        memcpy(&buf[off], iov[i].iov_base, iov[i].iov_len);
19,435✔
327
        off += iov[i].iov_len;
19,435✔
328
    }
329

330
    n = write_full(fd, buf, off);
6,496✔
331

332
    free(buf);
6,496✔
333

334
    return n;
6,496✔
335
}
336

337
/*
338
 * file_write: Write a buffer to a file.
339
 *
340
 * @filename: filename
341
 * @flags: file open flags
342
 * @mode: file mode bits
343
 * @clear_umask: whether to clear the umask and restore it after
344
 * @buffer: buffer
345
 * @buflen: length of buffer
346
 *
347
 * Returns -1 in case an error occurred, number of bytes written otherwise.
348
 */
349
ssize_t file_write(const char *filename, int flags, mode_t mode,
18,162✔
350
                   bool clear_umask, const void *buffer, size_t buflen)
351
{
352
    mode_t orig_umask = 0;
18,162✔
353
    ssize_t res;
18,162✔
354
    int fd;
18,162✔
355

356
    if (clear_umask)
18,162✔
357
        orig_umask = umask(0);
6✔
358

359
    fd = open(filename, flags, mode);
18,162✔
360

361
    if (clear_umask)
18,162✔
362
        umask(orig_umask);
6✔
363

364
    if (fd < 0)
18,162✔
365
        return -1;
366

367
    res = buflen;
18,162✔
368
    if (write_full(fd, buffer, buflen) != res)
18,162✔
NEW
369
        res = -1;
×
370

371
    if (close(fd) < 0)
18,162✔
372
        res = -1;
373

374
    if (res < 0)
18,162✔
NEW
375
        unlink(filename);
×
376

377
    return res;
378
}
379

380
/*
381
 * read_einter: Read bytes from a file descriptor into a buffer
382
 *              and handle EINTR. Perform one read().
383
 *
384
 * @fd: file descriptor to read from
385
 * @buffer: buffer
386
 * @buflen: length of buffer
387
 *
388
 * Returns -1 in case an error occurred, number of bytes read otherwise.
389
 */
390
ssize_t read_eintr(int fd, void *buffer, size_t buflen)
27,460✔
391
{
392
    ssize_t n;
27,460✔
393

394
    while (true) {
27,460✔
395
        n = read(fd, buffer, buflen);
27,460✔
396
        if (n < 0) {
27,460✔
UNCOV
397
            if (errno == EINTR)
×
UNCOV
398
                continue;
×
399
            return -1;
400
        }
401
        return n;
402
    }
403
}
404

405
/*
406
 * file_read: Read contents of file and adjust mode bits
407
 *
408
 * @filename: filename
409
 * @buffer: pointer to buffer pointer to allocate memory for file
410
 * @buflen: pointer to size of buffer returned by function
411
 * @do_chmod: whether to change the file's mode bits
412
 * @mode: the mode bits
413
 *
414
 * Read the contents of the file into a buffer allocated by this function.
415
 * Adjust the file mode bits if @do_chmod is set or if file is not writable.
416
 * Returns -1 on error with errno set, number of bytes read otherwise.
417
 */
418
ssize_t file_read(const char *filename, void **buffer, size_t *buflen,
28,184✔
419
                  bool do_chmod, mode_t mode)
420
{
421
    struct stat statbuf;
28,184✔
422
    ssize_t ret = -1;
28,184✔
423
    int n, fd;
28,184✔
424

425
    fd = open(filename, O_RDONLY);
28,184✔
426
    if (fd < 0)
28,184✔
427
        return -1;
428

429
    n = fstat(fd, &statbuf);
27,211✔
430
    if (n < 0)
27,211✔
NEW
431
        goto err_close;
×
432

433
    *buflen = statbuf.st_size;
27,211✔
434
    *buffer = malloc(*buflen);
27,211✔
435
    if (!*buffer) {
27,211✔
NEW
436
        errno = ENOMEM;
×
NEW
437
        goto err_close;
×
438
    }
439

440
    ret = read_eintr(fd, *buffer, *buflen);
27,211✔
441
    if (ret < 0 || (size_t)ret != *buflen)
27,211✔
NEW
442
        goto err_close;
×
443

444
    /* make sure file is always writable */
445
    if (!do_chmod && (statbuf.st_mode & 0200) == 0) {
27,211✔
NEW
446
        mode |= 0200;
×
NEW
447
        do_chmod = true;
×
448
    } else if (do_chmod && (statbuf.st_mode & ACCESSPERMS) == mode) {
27,211✔
449
        do_chmod = false;
450
    }
451

452
    if (do_chmod && fchmod(fd, mode) < 0)
27,211✔
NEW
453
        goto err_close;
×
454

455
    ret = *buflen;
27,211✔
456

457
err_close:
27,211✔
458
    if (close(fd) < 0)
27,211✔
NEW
459
        ret = -1;
×
460

461
    return ret;
462
}
463

464
/*
465
 * Get the value of a map's key.
466
 *
467
 * Returns:
468
 * 0 : success
469
 * -1 : failure to parse the JSON input
470
 * -2 : could not find the key
471
 */
472
int json_get_map_key_value(const char *json_input,
11✔
473
                           const char *key, char **value)
474
{
475
    g_autoptr(GError) error = NULL;
22✔
476
    g_autoptr(JsonParser) jp = NULL;
11✔
477
    g_autoptr(JsonReader) jr = NULL;
11✔
478
    JsonNode *root;
11✔
479

480
    jp = json_parser_new();
11✔
481
    if (!json_parser_load_from_data(jp, json_input, -1, &error)) {
11✔
UNCOV
482
        logprintf(STDERR_FILENO,
×
UNCOV
483
                  "Could not parse JSON '%s': %s\n", json_input, error->message);
×
UNCOV
484
        return -1;
×
485
    }
486

487
    root = json_parser_get_root(jp);
11✔
488
    if (!root) {
11✔
UNCOV
489
        logprintf(STDERR_FILENO,
×
490
                  "Could not get root of JSON '%s'\n", json_input);
UNCOV
491
        return -1;
×
492
    }
493
    jr = json_reader_new(root);
11✔
494

495
    if (!json_reader_read_member(jr, key))
11✔
496
        return -2;
497

498
    *value = g_strdup(json_reader_get_string_value(jr));
7✔
499
    if (*value == NULL) {
7✔
500
        /* value not a string */
501
        logprintf(STDERR_FILENO,
×
502
                  "'%s' in JSON map is not a string\n", key);
UNCOV
503
        return -1;
×
504
    }
505

506
    return 0;
507
}
508

509
/*
510
 * Set the value of a map's key and return the new string
511
 *
512
 * Returns:
513
 * 0 : success
514
 * -1 : fatal failure
515
 */
516
int json_set_map_key_value(char **json_string,
6✔
517
                           const char *key, const char *value)
518
{
519
    g_autoptr(JsonParser) jp = NULL;
12✔
520
    g_autoptr(GError) error = NULL;
6✔
521
    g_autoptr(JsonGenerator) jg = NULL;
6✔
522
    JsonObject *jo;
6✔
523
    JsonNode *root;
6✔
524

525
    jg = json_generator_new();
6✔
526
    if (!jg)
6✔
527
        return -1;
528

529
    jp = json_parser_new();
6✔
530
    if (!json_parser_load_from_data(jp, *json_string, -1, &error)) {
6✔
UNCOV
531
        logprintf(STDERR_FILENO,
×
UNCOV
532
                  "Could not parse JSON '%s': %s\n", *json_string, error->message);
×
UNCOV
533
        return -1;
×
534
    }
535

536
    root = json_parser_get_root(jp);
6✔
537
    if (!root) {
6✔
UNCOV
538
        logprintf(STDERR_FILENO,
×
539
                  "Could not get root of JSON '%s'\n", *json_string);
540
        return -1;
×
541
    }
542
    json_generator_set_root(jg, root);
6✔
543

544
    jo = json_node_get_object(root);
6✔
545
    json_object_set_string_member(jo, key, value);
6✔
546

547
    g_free(*json_string);
6✔
548
    *json_string = json_generator_to_data(jg, NULL);
6✔
549

550
    return 0;
6✔
551
}
552

553
/*
554
 * In the given JSON map find a map with name @field_name and then
555
 * access the field @field_name2 in this map and return its value.
556
 *
557
 * @json_input: JSON object as string
558
 * @field_name: Name of map
559
 * @field_name2: Name of entry in map
560
 * @value: Results is returned here
561
 *
562
 * Returns 0 in case of success, -1 otherwise.
563
 */
564
int json_get_submap_value(const char *json_input, const char *field_name,
27,564✔
565
                          const char *field_name2, char **value)
566
{
567
    g_autoptr(JsonParser) jp = NULL;
55,128✔
568
    g_autoptr(JsonReader) jr = NULL;
27,564✔
569
    g_autoptr(GError) error = NULL;
27,564✔
570
    JsonNode *root;
27,564✔
571

572
    jp = json_parser_new();
27,564✔
573
    if (!json_parser_load_from_data(jp, json_input, -1, &error)) {
27,564✔
UNCOV
574
        logprintf(STDERR_FILENO,
×
UNCOV
575
                  "Could not parse JSON '%s': %s\n", json_input, error->message);
×
UNCOV
576
        return -1;
×
577
    }
578

579
    root = json_parser_get_root(jp);
27,564✔
580
    if (!root) {
27,564✔
UNCOV
581
        logprintf(STDERR_FILENO,
×
582
                  "Could not get root of JSON '%s'\n", json_input);
UNCOV
583
        return -1;
×
584
    }
585
    jr = json_reader_new(root);
27,564✔
586

587
    if (!json_reader_read_member(jr, field_name)) {
27,564✔
UNCOV
588
        logprintf(STDERR_FILENO, "Missing '%s' field in '%s'\n",
×
589
                  field_name, json_input);
UNCOV
590
        return -1;
×
591
    }
592

593
    if (!json_reader_read_member(jr, field_name2)) {
27,564✔
594
        logprintf(STDERR_FILENO, "Missing '%s/%s' field in '%s'\n",
×
595
                  field_name, field_name2, json_input);
UNCOV
596
        return -1;
×
597
    }
598
    *value = g_strdup(json_reader_get_string_value(jr));
27,564✔
599
    if (*value == NULL) {
27,564✔
600
        /* value not a string */
UNCOV
601
        logprintf(STDERR_FILENO,
×
602
                  "'%s/%s' field in '%s' is not a string\n",
603
                  field_name, field_name2, json_input);
604
        return -1;
×
605
    }
606

607
    return 0;
608
}
609

610
/*
611
 * In the given JSON map select @field0_name whose value must be an array.
612
 * Inside the array of maps, find a map whose @field1_name has the value
613
 * @field1_value. Then select field2_name and return its value.
614
 *
615
 * @json_input: JSON array of maps as a string
616
 * @field0_name: The name of the map entry holding the array of maps
617
 * @field1_name: The name of an entry in the map
618
 * @field1_value: The value of an entry in the map
619
 * @field2_name: Name of entry in map whose value to return
620
 * @value: Results is returned here
621
 *
622
 * Returns 0 in case of success, -1 otherwise.
623
 */
624
int json_get_array_entry_value(const char *json_input,
3✔
625
                               const char *field0_name,
626
                               const char *field1_name, const char *field1_value,
627
                               const char *field2_name, char **value)
628
{
629
    g_autoptr(JsonParser) jp = NULL;
6✔
630
    g_autoptr(JsonReader) jr = NULL;
3✔
631
    g_autoptr(GError) error = NULL;
3✔
632
    const gchar *strval;
3✔
633
    JsonNode *root;
3✔
634
    guint idx;
3✔
635

636
    jp = json_parser_new();
3✔
637
    if (!json_parser_load_from_data(jp, json_input, -1, &error)) {
3✔
UNCOV
638
        logprintf(STDERR_FILENO,
×
UNCOV
639
                  "Could not parse JSON '%s': %s\n", json_input, error->message);
×
UNCOV
640
        return -1;
×
641
    }
642

643
    root = json_parser_get_root(jp);
3✔
644
    if (!root) {
3✔
UNCOV
645
        logprintf(STDERR_FILENO,
×
646
                  "Could not get root of JSON '%s'\n", json_input);
UNCOV
647
        return -1;
×
648
    }
649
    jr = json_reader_new(root);
3✔
650

651
    if (!json_reader_read_member(jr, field0_name)) {
3✔
UNCOV
652
        logprintf(STDERR_FILENO,
×
653
                  "Could not find the initial field '%s'in '%s'\n",
654
                  field0_name, json_input);
UNCOV
655
        return -1;
×
656
    }
657
    for (idx = 0;; idx++) {
6✔
658
        if (!json_reader_read_element(jr, idx)) {
9✔
UNCOV
659
            logprintf(STDERR_FILENO,
×
660
                      "Could not find an element with name '%s' and value '%s'\n",
661
                      field1_name, field1_value);
UNCOV
662
            return -1;
×
663
        }
664
        if (json_reader_read_member(jr, field1_name)) {
9✔
665
            if ((strval = json_reader_get_string_value(jr)) != NULL &&
18✔
666
                g_strcmp0(strval, field1_value) == 0) {
9✔
667

668
                json_reader_end_member(jr);
3✔
669
                if (!json_reader_read_member(jr, field2_name)) {
3✔
UNCOV
670
                    logprintf(STDERR_FILENO,
×
671
                              "Found map entry in '%s' but could not find field '%s'",
672
                              json_input, field2_name);
UNCOV
673
                    return -1;
×
674
                }
675
                *value = g_strdup(json_reader_get_string_value(jr));
3✔
676
                if (*value == NULL) {
3✔
677
                    /* value not a string */
678
                    logprintf(STDERR_FILENO,
×
679
                              "'%s' field in '%s' is not a string\n",
680
                              field2_name, json_input);
UNCOV
681
                    return -1;
×
682
                }
683
                return 0;
684
            }
685
            json_reader_end_member(jr);
6✔
686
        }
687
        json_reader_end_element(jr);
6✔
688
    }
689
    /* must never get here */
690
    return -1;
691
}
692

693
ssize_t strv_strncmp(const gchar *const*str_array, const gchar *s, size_t n)
6✔
694
{
695
    size_t i;
6✔
696

697
    for (i = 0; str_array[i]; i++) {
87✔
698
        if (strncmp(str_array[i], s, n) == 0)
87✔
699
            return (ssize_t)i;
6✔
700
    }
701
    return -1;
702
}
703

704
/* Try to find exactly the needle in the given haystack */
705
static ssize_t strv_strcmp(const gchar *const*haystack, const gchar *needle)
155,946✔
706
{
707
    size_t i;
155,946✔
708

709
    for (i = 0; haystack[i]; i++) {
1,666,843✔
710
        if (strcmp(haystack[i], needle) == 0)
1,648,166✔
711
            return (ssize_t)i;
137,269✔
712
    }
713
    return -1;
714
}
715

716
/*
717
 * Try to find all the needles in the haystack; both arrays of strings
718
 * must be NULL-terminated.
719
 */
720
gboolean strv_contains_all(const gchar *const*haystack, const gchar *const*needles)
82,683✔
721
{
722
    size_t i;
82,683✔
723

724
    for (i = 0; needles[i]; i++) {
219,952✔
725
        if (strv_strcmp(haystack, needles[i]) < 0)
155,946✔
726
            return false;
727
    }
728
    return true;
729
}
730

731
/*
732
 * Remove all entries in the @array that either fully match @toremove
733
 * (@len = -1) or where @toremove is a prefix of.
734
 * This function returns the number of entries that were removed.
735
 */
736
size_t strv_remove(gchar **array, const gchar *toremove, ssize_t len,
18✔
737
                   gboolean freethem)
738
{
739
    size_t i = 0, j, num = 0;
18✔
740

741
    while (array[i]) {
729✔
742
        if ((len < 0 && strcmp(array[i], toremove) == 0) ||
711✔
743
            (len > 0 && strncmp(array[i], toremove, len) == 0)) {
240✔
744
            if (freethem)
15✔
745
                g_free(array[i]);
15✔
746

747
            j = i;
748
            do {
342✔
749
                j++;
342✔
750
                array[j - 1] = array[j];
342✔
751
            } while(array[j]);
342✔
752

753
            num++;
15✔
754
        } else {
755
            i++;
696✔
756
        }
757
    }
758
    return num;
18✔
759
}
760

761
/*
762
 * Deduplicate items in a NULL-terminated array of strings.
763
 * When a duplicate item is found then the first item is removed and all later
764
 * ones are kept -- this is to deduplicate items in the same way as libtpms
765
 * deduplicates comma separated items in a string. The string to use for
766
 * finding duplicates is expected to be returned from an optional gencmpstr_t
767
 * function that in the simplest case can return the passed string and adjust
768
 * string comparison to be done on full string (len = -1) or prefix comparison.
769
 * If not function is given then full string matching is done.
770
 *
771
 * This function returns the number of entries removed from the array.
772
 */
773
size_t strv_dedup(gchar **array, gencmpstr_t gencmpstr, gboolean freethem)
3✔
774
{
775
    gboolean free_cmp = false;
3✔
776
    size_t num = 0, i = 0, j;
3✔
777
    ssize_t len = 0;
3✔
778
    gchar *cmp;
3✔
779

780
    while (array[i]) {
114✔
781
        if (gencmpstr) {
111✔
782
            cmp = gencmpstr(array[i], &len);
111✔
783
            free_cmp = array[i] != cmp;
111✔
784
        } else {
UNCOV
785
            cmp = array[i];
×
UNCOV
786
            len = strlen(cmp);
×
787
        }
788

789
        j = i + 1;
111✔
790
        while (array[j]) {
2,109✔
791
            if ((len < 0 && strcmp(array[j], cmp) == 0) ||
1,998✔
792
                (len > 0 && strncmp(array[j], cmp, len) == 0)) {
228✔
793

UNCOV
794
                num++;
×
UNCOV
795
                if (freethem)
×
UNCOV
796
                    g_free(array[i]);
×
797

798
                /*
799
                 * Keep the later ones in the array since libtpms also keeps
800
                 * later items ones in string when deduplicating.
801
                 */
802
                j = i;
UNCOV
803
                do {
×
UNCOV
804
                    array[j] = array[j + 1];
×
UNCOV
805
                    j++;
×
UNCOV
806
                } while (array[j]);
×
807
                break;
808
            }
809
            j++;
1,998✔
810
        }
811

812
        if (free_cmp)
111✔
UNCOV
813
            g_free(cmp);
×
814
        i++;
815
    }
816
    return num;
3✔
817
}
818

819
/*
820
 * Append entries from a 2nd string array to the first one. Make copies of
821
 * each entry.
822
 */
823
gchar **strv_extend(gchar **array, const gchar *const*append)
2✔
824
{
825
    size_t len1 = 0, len2 = 0, i;
2✔
826

827
    if (array)
2✔
828
        len1 = g_strv_length(array);
1✔
829

830
    while (append[len2])
8✔
831
        len2++;
6✔
832

833
    array = g_realloc(array, sizeof(char *) * (len1 + len2 + 1));
2✔
834

835
    for (i = 0; i < len2; i++)
10✔
836
        array[len1 + i] = g_strdup(append[i]);
6✔
837
    array[len1 + len2] = NULL;
2✔
838

839
    return array;
2✔
840
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc