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

stefanberger / swtpm / #2835

27 May 2025 04:25PM UTC coverage: 73.372% (+0.03%) from 73.345%
#2835

push

travis-ci

web-flow
Merge a4758d76c into ec7730c70

13 of 29 new or added lines in 5 files covered. (44.83%)

520 existing lines in 8 files now uncovered.

8156 of 11116 relevant lines covered (73.37%)

13441.59 hits per line

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

60.23
/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
/* sys/stat.h does not always define ACCESSPERMS */
65
#ifndef ACCESSPERMS
66
# define ACCESSPERMS (S_IRWXU|S_IRWXG|S_IRWXO)
67
#endif
68

69
void uninstall_sighandlers()
412✔
70
{
71
    if (signal(SIGTERM, SIG_DFL) == SIG_ERR)
412✔
72
        logprintf(STDERR_FILENO, "Could not uninstall signal handler for SIGTERM.\n");
×
73

74
    if (signal(SIGPIPE, SIG_DFL) == SIG_ERR)
412✔
75
        logprintf(STDERR_FILENO, "Could not uninstall signal handler for SIGPIPE.\n");
×
76
}
412✔
77

78
int install_sighandlers(int pipefd[2], sighandler_t handler)
412✔
79
{
80
    if (pipe(pipefd) < 0) {
412✔
81
        logprintf(STDERR_FILENO, "Error: Could not open pipe.\n");
×
82
        goto err_exit;
×
83
    }
84

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

90
    if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
412✔
91
        logprintf(STDERR_FILENO, "Could not install signal handler for SIGPIPE.\n");
×
92
        goto err_close_pipe;
×
93
    }
94

95
    return 0;
96

97
err_close_pipe:
×
98
    close(pipefd[0]);
×
99
    pipefd[0] = -1;
×
100
    close(pipefd[1]);
×
101
    pipefd[1] = -1;
×
102

103
err_exit:
104
    return -1;
105
}
106

107
int
108
change_process_owner(const char *user)
×
109
{
110
    struct passwd *passwd;
×
111
    long int uid, gid;
×
112
    char *endptr = NULL;
×
113

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

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

144
    if (setgid(gid) < 0) {
×
145
        logprintf(STDERR_FILENO,
×
146
                  "Error: setgid(%d) failed.\n",
147
                  gid);
148
        return -11;
×
149
    }
150
    if (setuid(uid) < 0) {
×
151
        logprintf(STDERR_FILENO,
×
152
                  "Error: setuid(%d) failed.\n",
153
                  uid);
154
        return -12;
×
155
    }
156
    return 0;
157
}
158

159
int
160
do_chroot(const char *path)
×
161
{
162
    if (chroot(path) < 0) {
×
163
        logprintf(STDERR_FILENO, "chroot failed: %s\n",
×
164
                  strerror(errno));
×
165
        return -1;
×
166
    }
167

168
    if (chdir("/") < 0) {
×
169
        logprintf(STDERR_FILENO, "chdir failed: %s\n",
×
170
                  strerror(errno));
×
171
        return -1;
×
172
    }
173

174
    if (!RAND_status()) {
×
175
        logprintf(STDERR_FILENO,
×
176
                  "Error: no good entropy source in chroot environment\n");
177
        return -1;
×
178
    }
179

180
    return 0;
181
}
182

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

217
char *fd_to_filename(int fd)
×
218
{
219
#if defined __linux__
220

221
    char buffer[64];
×
222
    char *path;
×
223

224
    snprintf(buffer, sizeof(buffer), "/proc/self/fd/%d", fd);
×
225

226
    path = realpath(buffer, NULL);
×
227
    if (!path) {
×
228
        logprintf(STDERR_FILENO, "Could not read %s: %s\n",
×
229
                  buffer, strerror(errno));
×
230
        return NULL;
×
231
    }
232

233
    return path;
234

235
#elif defined __APPLE__
236

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

250
#else
251
    (void)fd;
252
    logprintf(STDERR_FILENO,
253
              "Cannot convert file descriptor to filename on this platform.\n");
254
    return NULL;
255

256
#endif
257
}
258

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

275
    while (written < buflen) {
186,870✔
276
        n = write(fd, buffer, buflen - written);
93,435✔
277
        if (n == 0)
93,435✔
278
            return -1;
279
        if (n < 0) {
93,435✔
280
            if (errno == EINTR)
×
281
                continue;
×
282
            return -1;
283
        }
284
        written += n;
93,435✔
285
        buffer = (const char *)buffer + n;
93,435✔
286
    }
287
    return written;
93,435✔
288
}
289

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

310
    for (i = 0; i < iovcnt; i++) {
88,593✔
311
        if (iov[i].iov_len) {
66,430✔
312
            bytecount += iov[i].iov_len;
35,170✔
313
            numbufs++;
35,170✔
314
            lastidx = i;
35,170✔
315
        }
316
    }
317

318
    if (numbufs == 1)
22,163✔
319
        return write_full(fd, iov[lastidx].iov_base, iov[lastidx].iov_len);
15,633✔
320

321
    buf = malloc(bytecount);
6,530✔
322
    if (!buf) {
6,530✔
323
        errno = ENOMEM;
×
324
        return -1;
×
325
    }
326

327
    off = 0;
328
    for (i = 0; i < iovcnt; i++) {
26,067✔
329
        if (!iov[i].iov_len)
19,537✔
330
            continue;
×
331
        memcpy(&buf[off], iov[i].iov_base, iov[i].iov_len);
19,537✔
332
        off += iov[i].iov_len;
19,537✔
333
    }
334

335
    n = write_full(fd, buf, off);
6,530✔
336

337
    free(buf);
6,530✔
338

339
    return n;
6,530✔
340
}
341

342
/*
343
 * fsync_eintr: fsync and handle EINTR
344
 *
345
 * @fd: file descriptor to fsync on
346
 *
347
 * Returns -1 in case an error occurred, 0 otherwise.
348
 */
349
static int fsync_eintr(int fd)
350
{
NEW
351
    int n;
×
352

NEW
353
    while (true) {
×
NEW
354
        n = fsync(fd);
×
NEW
355
        if (n < 0) {
×
NEW
356
            if (errno == EINTR)
×
NEW
357
                continue;
×
358
            return -1;
359
        }
360
        return n;
361
    }
362
}
363

364
/*
365
 * fsync_on_dir: Call fsync() on a directory
366
 *
367
 * @fsync_dir: name of the directory to fsync on
368
 *
369
 * Returns -1 on error with errno set, 0 otherwise.
370
 */
371
static int fsync_on_dir(const char *fsync_dir)
372
{
NEW
373
    int dir_fd;
×
NEW
374
    int res = 0;
×
375

NEW
376
    dir_fd = open(fsync_dir, O_RDONLY);
×
NEW
377
    if (dir_fd < 0)
×
378
        return -1;
379

NEW
380
    if (fsync_eintr(dir_fd) < 0)
×
NEW
381
        res = -1;
×
382

NEW
383
    if (close(dir_fd) < 0)
×
NEW
384
        res = -1;
×
385

386
    return res;
387
}
388

389
/*
390
 * file_write: Write a buffer to a file.
391
 *
392
 * @filename: filename
393
 * @flags: file open flags
394
 * @mode: file mode bits
395
 * @clear_umask: whether to clear the umask and restore it after
396
 * @buffer: buffer
397
 * @buflen: length of buffer
398
 * @do_fsync: whether to call fsync on the file and directory
399
 * @fsync_dir: the directory to call fsync on; may be NULL
400
 *
401
 * Returns -1 in case an error occurred, number of bytes written otherwise.
402
 */
403
ssize_t file_write(const char *filename, int flags, mode_t mode,
18,241✔
404
                   bool clear_umask, const void *buffer, size_t buflen,
405
                   bool do_fsync, const char *fsync_dir)
406
{
407
    mode_t orig_umask = 0;
18,241✔
408
    ssize_t res;
18,241✔
409
    int fd;
18,241✔
410

411
    if (clear_umask)
18,241✔
412
        orig_umask = umask(0);
6✔
413

414
    fd = open(filename, flags, mode);
18,241✔
415

416
    if (clear_umask)
18,241✔
417
        umask(orig_umask);
6✔
418

419
    if (fd < 0)
18,241✔
420
        return -1;
421

422
    res = buflen;
18,241✔
423
    if (write_full(fd, buffer, buflen) != res)
18,241✔
424
        res = -1;
×
425

426
    if (do_fsync && fsync_eintr(fd) < 0)
18,241✔
NEW
427
        res = -1;
×
428

429
    if (close(fd) < 0)
18,241✔
430
        res = -1;
×
431

432
    if (do_fsync && fsync_dir)
18,241✔
NEW
433
        res = fsync_on_dir(fsync_dir);
×
434

435
    if (res < 0)
18,241✔
436
        unlink(filename);
×
437

438
    return res;
439
}
440

441
/*
442
 * read_einter: Read bytes from a file descriptor into a buffer
443
 *              and handle EINTR. Perform one read().
444
 *
445
 * @fd: file descriptor to read from
446
 * @buffer: buffer
447
 * @buflen: length of buffer
448
 *
449
 * Returns -1 in case an error occurred, number of bytes read otherwise.
450
 */
451
ssize_t read_eintr(int fd, void *buffer, size_t buflen)
27,611✔
452
{
453
    ssize_t n;
27,611✔
454

455
    while (true) {
27,611✔
456
        n = read(fd, buffer, buflen);
27,611✔
457
        if (n < 0) {
27,611✔
UNCOV
458
            if (errno == EINTR)
×
UNCOV
459
                continue;
×
460
            return -1;
461
        }
462
        return n;
463
    }
464
}
465

466
/*
467
 * file_read: Read contents of file and adjust mode bits
468
 *
469
 * @filename: filename
470
 * @buffer: pointer to buffer pointer to allocate memory for file contents
471
 * @do_chmod: whether to change the file's mode bits
472
 * @mode: the mode bits
473
 *
474
 * Read the contents of the file into a buffer allocated by this function.
475
 * Adjust the file mode bits if @do_chmod is set or if file is not writable.
476
 * Returns -1 on error with errno set, number of bytes read (= size of
477
 * allocated buffer) otherwise.
478
 */
479
ssize_t file_read(const char *filename, void **buffer,
28,340✔
480
                  bool do_chmod, mode_t mode)
481
{
482
    struct stat statbuf;
28,340✔
483
    ssize_t ret = -1;
28,340✔
484
    size_t buflen;
28,340✔
485
    int n, fd;
28,340✔
486

487
    fd = open(filename, O_RDONLY);
28,340✔
488
    if (fd < 0)
28,340✔
489
        return -1;
490

491
    n = fstat(fd, &statbuf);
27,361✔
492
    if (n < 0)
27,361✔
UNCOV
493
        goto err_close;
×
494

495
    buflen = statbuf.st_size;
27,361✔
496
    *buffer = malloc(buflen);
27,361✔
497
    if (!*buffer) {
27,361✔
UNCOV
498
        errno = ENOMEM;
×
UNCOV
499
        goto err_close;
×
500
    }
501

502
    ret = read_eintr(fd, *buffer, buflen);
27,361✔
503
    if (ret < 0 || (size_t)ret != buflen)
27,361✔
UNCOV
504
        goto err_close;
×
505

506
    /* make sure file is always writable */
507
    if (!do_chmod && (statbuf.st_mode & 0200) == 0) {
27,361✔
UNCOV
508
        mode |= 0200;
×
UNCOV
509
        do_chmod = true;
×
510
    } else if (do_chmod && (statbuf.st_mode & ACCESSPERMS) == mode) {
27,361✔
511
        do_chmod = false;
512
    }
513

514
    if (do_chmod && fchmod(fd, mode) < 0)
27,361✔
515
        goto err_close;
×
516

517
    ret = buflen;
518

519
err_close:
27,361✔
520
    if (close(fd) < 0)
27,361✔
UNCOV
521
        ret = -1;
×
522

523
    return ret;
524
}
525

526
/*
527
 * Get the value of a map's key.
528
 *
529
 * Returns:
530
 * 0 : success
531
 * -1 : failure to parse the JSON input
532
 * -2 : could not find the key
533
 */
534
int json_get_map_key_value(const char *json_input,
11✔
535
                           const char *key, char **value)
536
{
537
    g_autoptr(GError) error = NULL;
22✔
538
    g_autoptr(JsonParser) jp = NULL;
11✔
539
    g_autoptr(JsonReader) jr = NULL;
11✔
540
    JsonNode *root;
11✔
541

542
    jp = json_parser_new();
11✔
543
    if (!json_parser_load_from_data(jp, json_input, -1, &error)) {
11✔
UNCOV
544
        logprintf(STDERR_FILENO,
×
UNCOV
545
                  "Could not parse JSON '%s': %s\n", json_input, error->message);
×
UNCOV
546
        return -1;
×
547
    }
548

549
    root = json_parser_get_root(jp);
11✔
550
    if (!root) {
11✔
UNCOV
551
        logprintf(STDERR_FILENO,
×
552
                  "Could not get root of JSON '%s'\n", json_input);
UNCOV
553
        return -1;
×
554
    }
555
    jr = json_reader_new(root);
11✔
556

557
    if (!json_reader_read_member(jr, key))
11✔
558
        return -2;
559

560
    *value = g_strdup(json_reader_get_string_value(jr));
7✔
561
    if (*value == NULL) {
7✔
562
        /* value not a string */
UNCOV
563
        logprintf(STDERR_FILENO,
×
564
                  "'%s' in JSON map is not a string\n", key);
565
        return -1;
×
566
    }
567

568
    return 0;
569
}
570

571
/*
572
 * Set the value of a map's key and return the new string
573
 *
574
 * Returns:
575
 * 0 : success
576
 * -1 : fatal failure
577
 */
578
int json_set_map_key_value(char **json_string,
6✔
579
                           const char *key, const char *value)
580
{
581
    g_autoptr(JsonParser) jp = NULL;
12✔
582
    g_autoptr(GError) error = NULL;
6✔
583
    g_autoptr(JsonGenerator) jg = NULL;
6✔
584
    JsonObject *jo;
6✔
585
    JsonNode *root;
6✔
586

587
    jg = json_generator_new();
6✔
588
    if (!jg)
6✔
589
        return -1;
590

591
    jp = json_parser_new();
6✔
592
    if (!json_parser_load_from_data(jp, *json_string, -1, &error)) {
6✔
UNCOV
593
        logprintf(STDERR_FILENO,
×
UNCOV
594
                  "Could not parse JSON '%s': %s\n", *json_string, error->message);
×
UNCOV
595
        return -1;
×
596
    }
597

598
    root = json_parser_get_root(jp);
6✔
599
    if (!root) {
6✔
600
        logprintf(STDERR_FILENO,
×
601
                  "Could not get root of JSON '%s'\n", *json_string);
602
        return -1;
×
603
    }
604
    json_generator_set_root(jg, root);
6✔
605

606
    jo = json_node_get_object(root);
6✔
607
    json_object_set_string_member(jo, key, value);
6✔
608

609
    g_free(*json_string);
6✔
610
    *json_string = json_generator_to_data(jg, NULL);
6✔
611

612
    return 0;
6✔
613
}
614

615
/*
616
 * In the given JSON map find a map with name @field_name and then
617
 * access the field @field_name2 in this map and return its value.
618
 *
619
 * @json_input: JSON object as string
620
 * @field_name: Name of map
621
 * @field_name2: Name of entry in map
622
 * @value: Results is returned here
623
 *
624
 * Returns 0 in case of success, -1 otherwise.
625
 */
626
int json_get_submap_value(const char *json_input, const char *field_name,
27,705✔
627
                          const char *field_name2, char **value)
628
{
629
    g_autoptr(JsonParser) jp = NULL;
55,410✔
630
    g_autoptr(JsonReader) jr = NULL;
27,705✔
631
    g_autoptr(GError) error = NULL;
27,705✔
632
    JsonNode *root;
27,705✔
633

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

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

649
    if (!json_reader_read_member(jr, field_name)) {
27,705✔
650
        logprintf(STDERR_FILENO, "Missing '%s' field in '%s'\n",
×
651
                  field_name, json_input);
UNCOV
652
        return -1;
×
653
    }
654

655
    if (!json_reader_read_member(jr, field_name2)) {
27,705✔
656
        logprintf(STDERR_FILENO, "Missing '%s/%s' field in '%s'\n",
×
657
                  field_name, field_name2, json_input);
658
        return -1;
×
659
    }
660
    *value = g_strdup(json_reader_get_string_value(jr));
27,705✔
661
    if (*value == NULL) {
27,705✔
662
        /* value not a string */
UNCOV
663
        logprintf(STDERR_FILENO,
×
664
                  "'%s/%s' field in '%s' is not a string\n",
665
                  field_name, field_name2, json_input);
UNCOV
666
        return -1;
×
667
    }
668

669
    return 0;
670
}
671

672
/*
673
 * In the given JSON map select @field0_name whose value must be an array.
674
 * Inside the array of maps, find a map whose @field1_name has the value
675
 * @field1_value. Then select field2_name and return its value.
676
 *
677
 * @json_input: JSON array of maps as a string
678
 * @field0_name: The name of the map entry holding the array of maps
679
 * @field1_name: The name of an entry in the map
680
 * @field1_value: The value of an entry in the map
681
 * @field2_name: Name of entry in map whose value to return
682
 * @value: Results is returned here
683
 *
684
 * Returns 0 in case of success, -1 otherwise.
685
 */
686
int json_get_array_entry_value(const char *json_input,
3✔
687
                               const char *field0_name,
688
                               const char *field1_name, const char *field1_value,
689
                               const char *field2_name, char **value)
690
{
691
    g_autoptr(JsonParser) jp = NULL;
6✔
692
    g_autoptr(JsonReader) jr = NULL;
3✔
693
    g_autoptr(GError) error = NULL;
3✔
694
    const gchar *strval;
3✔
695
    JsonNode *root;
3✔
696
    guint idx;
3✔
697

698
    jp = json_parser_new();
3✔
699
    if (!json_parser_load_from_data(jp, json_input, -1, &error)) {
3✔
UNCOV
700
        logprintf(STDERR_FILENO,
×
701
                  "Could not parse JSON '%s': %s\n", json_input, error->message);
×
UNCOV
702
        return -1;
×
703
    }
704

705
    root = json_parser_get_root(jp);
3✔
706
    if (!root) {
3✔
UNCOV
707
        logprintf(STDERR_FILENO,
×
708
                  "Could not get root of JSON '%s'\n", json_input);
UNCOV
709
        return -1;
×
710
    }
711
    jr = json_reader_new(root);
3✔
712

713
    if (!json_reader_read_member(jr, field0_name)) {
3✔
714
        logprintf(STDERR_FILENO,
×
715
                  "Could not find the initial field '%s'in '%s'\n",
716
                  field0_name, json_input);
UNCOV
717
        return -1;
×
718
    }
719
    for (idx = 0;; idx++) {
9✔
720
        if (!json_reader_read_element(jr, idx)) {
12✔
UNCOV
721
            logprintf(STDERR_FILENO,
×
722
                      "Could not find an element with name '%s' and value '%s'\n",
723
                      field1_name, field1_value);
UNCOV
724
            return -1;
×
725
        }
726
        if (json_reader_read_member(jr, field1_name)) {
12✔
727
            if ((strval = json_reader_get_string_value(jr)) != NULL &&
24✔
728
                g_strcmp0(strval, field1_value) == 0) {
12✔
729

730
                json_reader_end_member(jr);
3✔
731
                if (!json_reader_read_member(jr, field2_name)) {
3✔
UNCOV
732
                    logprintf(STDERR_FILENO,
×
733
                              "Found map entry in '%s' but could not find field '%s'",
734
                              json_input, field2_name);
UNCOV
735
                    return -1;
×
736
                }
737
                *value = g_strdup(json_reader_get_string_value(jr));
3✔
738
                if (*value == NULL) {
3✔
739
                    /* value not a string */
UNCOV
740
                    logprintf(STDERR_FILENO,
×
741
                              "'%s' field in '%s' is not a string\n",
742
                              field2_name, json_input);
UNCOV
743
                    return -1;
×
744
                }
745
                return 0;
746
            }
747
            json_reader_end_member(jr);
9✔
748
        }
749
        json_reader_end_element(jr);
9✔
750
    }
751
    /* must never get here */
752
    return -1;
753
}
754

755
ssize_t strv_strncmp(const gchar *const*str_array, const gchar *s, size_t n)
6✔
756
{
757
    size_t i;
6✔
758

759
    for (i = 0; str_array[i]; i++) {
87✔
760
        if (strncmp(str_array[i], s, n) == 0)
87✔
761
            return (ssize_t)i;
6✔
762
    }
763
    return -1;
764
}
765

766
/* Try to find exactly the needle in the given haystack */
767
static ssize_t strv_strcmp(const gchar *const*haystack, const gchar *needle)
156,745✔
768
{
769
    size_t i;
156,745✔
770

771
    for (i = 0; haystack[i]; i++) {
1,675,350✔
772
        if (strcmp(haystack[i], needle) == 0)
1,656,579✔
773
            return (ssize_t)i;
137,974✔
774
    }
775
    return -1;
776
}
777

778
/*
779
 * Try to find all the needles in the haystack; both arrays of strings
780
 * must be NULL-terminated.
781
 */
782
gboolean strv_contains_all(const gchar *const*haystack, const gchar *const*needles)
83,106✔
783
{
784
    size_t i;
83,106✔
785

786
    for (i = 0; needles[i]; i++) {
221,080✔
787
        if (strv_strcmp(haystack, needles[i]) < 0)
156,745✔
788
            return false;
789
    }
790
    return true;
791
}
792

793
/*
794
 * Remove all entries in the @array that either fully match @toremove
795
 * (@len = -1) or where @toremove is a prefix of.
796
 * This function returns the number of entries that were removed.
797
 */
798
size_t strv_remove(gchar **array, const gchar *toremove, ssize_t len,
18✔
799
                   gboolean freethem)
800
{
801
    size_t i = 0, j, num = 0;
18✔
802

803
    while (array[i]) {
729✔
804
        if ((len < 0 && strcmp(array[i], toremove) == 0) ||
711✔
805
            (len > 0 && strncmp(array[i], toremove, len) == 0)) {
240✔
806
            if (freethem)
15✔
807
                g_free(array[i]);
15✔
808

809
            j = i;
810
            do {
342✔
811
                j++;
342✔
812
                array[j - 1] = array[j];
342✔
813
            } while(array[j]);
342✔
814

815
            num++;
15✔
816
        } else {
817
            i++;
696✔
818
        }
819
    }
820
    return num;
18✔
821
}
822

823
/*
824
 * Deduplicate items in a NULL-terminated array of strings.
825
 * When a duplicate item is found then the first item is removed and all later
826
 * ones are kept -- this is to deduplicate items in the same way as libtpms
827
 * deduplicates comma separated items in a string. The string to use for
828
 * finding duplicates is expected to be returned from an optional gencmpstr_t
829
 * function that in the simplest case can return the passed string and adjust
830
 * string comparison to be done on full string (len = -1) or prefix comparison.
831
 * If not function is given then full string matching is done.
832
 *
833
 * This function returns the number of entries removed from the array.
834
 */
835
size_t strv_dedup(gchar **array, gencmpstr_t gencmpstr, gboolean freethem)
3✔
836
{
837
    gboolean free_cmp = false;
3✔
838
    size_t num = 0, i = 0, j;
3✔
839
    ssize_t len = 0;
3✔
840
    gchar *cmp;
3✔
841

842
    while (array[i]) {
114✔
843
        if (gencmpstr) {
111✔
844
            cmp = gencmpstr(array[i], &len);
111✔
845
            free_cmp = array[i] != cmp;
111✔
846
        } else {
UNCOV
847
            cmp = array[i];
×
UNCOV
848
            len = strlen(cmp);
×
849
        }
850

851
        j = i + 1;
111✔
852
        while (array[j]) {
2,109✔
853
            if ((len < 0 && strcmp(array[j], cmp) == 0) ||
1,998✔
854
                (len > 0 && strncmp(array[j], cmp, len) == 0)) {
228✔
855

UNCOV
856
                num++;
×
UNCOV
857
                if (freethem)
×
UNCOV
858
                    g_free(array[i]);
×
859

860
                /*
861
                 * Keep the later ones in the array since libtpms also keeps
862
                 * later items ones in string when deduplicating.
863
                 */
864
                j = i;
UNCOV
865
                do {
×
UNCOV
866
                    array[j] = array[j + 1];
×
UNCOV
867
                    j++;
×
UNCOV
868
                } while (array[j]);
×
869
                break;
870
            }
871
            j++;
1,998✔
872
        }
873

874
        if (free_cmp)
111✔
UNCOV
875
            g_free(cmp);
×
876
        i++;
877
    }
878
    return num;
3✔
879
}
880

881
/*
882
 * Append entries from a 2nd string array to the first one. Make copies of
883
 * each entry.
884
 */
885
gchar **strv_extend(gchar **array, const gchar *const*append)
2✔
886
{
887
    size_t len1 = 0, len2 = 0, i;
2✔
888

889
    if (array)
2✔
890
        len1 = g_strv_length(array);
1✔
891

892
    while (append[len2])
8✔
893
        len2++;
6✔
894

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

897
    for (i = 0; i < len2; i++)
10✔
898
        array[len1 + i] = g_strdup(append[i]);
6✔
899
    array[len1 + len2] = NULL;
2✔
900

901
    return array;
2✔
902
}
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