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

stefanberger / swtpm / #2799

26 Apr 2025 05:33PM UTC coverage: 73.433% (+0.03%) from 73.405%
#2799

push

travis-ci

web-flow
Merge cb33a35cf into a507efb5b

42 of 53 new or added lines in 2 files covered. (79.25%)

85 existing lines in 2 files now uncovered.

8046 of 10957 relevant lines covered (73.43%)

13599.63 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,158✔
266
{
267
    size_t written = 0;
93,158✔
268
    ssize_t n;
93,158✔
269

270
    while (written < buflen) {
186,316✔
271
        n = write(fd, buffer, buflen - written);
93,158✔
272
        if (n == 0)
93,158✔
273
            return -1;
274
        if (n < 0) {
93,158✔
UNCOV
275
            if (errno == EINTR)
×
276
                continue;
×
277
            return -1;
278
        }
279
        written += n;
93,158✔
280
        buffer = (const char *)buffer + n;
93,158✔
281
    }
282
    return written;
93,158✔
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,125✔
296
{
297
    int i;
22,125✔
298
    size_t off;
22,125✔
299
    unsigned char *buf;
22,125✔
300
    ssize_t n;
22,125✔
301
    size_t bytecount = 0;
22,125✔
302
    size_t numbufs = 0;
22,125✔
303
    size_t lastidx = -1;
22,125✔
304

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

313
    if (numbufs == 1)
22,125✔
314
        return write_full(fd, iov[lastidx].iov_base, iov[lastidx].iov_len);
15,629✔
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,164✔
350
                   bool clear_umask, const void *buffer, size_t buflen)
351
{
352
    mode_t orig_umask = 0;
18,164✔
353
    ssize_t res;
18,164✔
354
    int fd;
18,164✔
355

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

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

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

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

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

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

374
    if (res < 0)
18,164✔
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,467✔
391
{
392
    ssize_t n;
27,467✔
393

394
    while (true) {
27,467✔
395
        n = read(fd, buffer, buflen);
27,467✔
396
        if (n < 0) {
27,467✔
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 contents
410
 * @do_chmod: whether to change the file's mode bits
411
 * @mode: the mode bits
412
 *
413
 * Read the contents of the file into a buffer allocated by this function.
414
 * Adjust the file mode bits if @do_chmod is set or if file is not writable.
415
 * Returns -1 on error with errno set, number of bytes read (= size of
416
 * allocated buffer) otherwise.
417
 */
418
ssize_t file_read(const char *filename, void **buffer,
28,187✔
419
                  bool do_chmod, mode_t mode)
420
{
421
    struct stat statbuf;
28,187✔
422
    ssize_t ret = -1;
28,187✔
423
    size_t buflen;
28,187✔
424
    int n, fd;
28,187✔
425

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

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

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

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

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

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

456
    ret = buflen;
457

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

462
    return ret;
463
}
464

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

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

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

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

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

507
    return 0;
508
}
509

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

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

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

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

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

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

551
    return 0;
6✔
552
}
553

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

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

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

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

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

608
    return 0;
609
}
610

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

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

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

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

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

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

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

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

710
    for (i = 0; haystack[i]; i++) {
1,667,024✔
711
        if (strcmp(haystack[i], needle) == 0)
1,648,345✔
712
            return (ssize_t)i;
137,284✔
713
    }
714
    return -1;
715
}
716

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

840
    return array;
2✔
841
}
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