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

stefanberger / swtpm / #2812

02 May 2025 12:09PM UTC coverage: 73.552% (+0.3%) from 73.293%
#2812

push

travis-ci

web-flow
Merge db5122d7f into 6d951a7a3

100 of 128 new or added lines in 5 files covered. (78.13%)

154 existing lines in 5 files now uncovered.

8140 of 11067 relevant lines covered (73.55%)

13478.66 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
/* 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()
410✔
70
{
71
    if (signal(SIGTERM, SIG_DFL) == SIG_ERR)
410✔
UNCOV
72
        logprintf(STDERR_FILENO, "Could not uninstall signal handler for SIGTERM.\n");
×
73

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

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

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

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

95
    return 0;
96

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

103
err_exit:
104
    return -1;
105
}
106

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

UNCOV
114
    uid = strtoul(user, &endptr, 10);
×
UNCOV
115
    if (*endptr != '\0') {
×
116
        /* a string */
UNCOV
117
        passwd = getpwnam(user);
×
UNCOV
118
        if (!passwd) {
×
119
            logprintf(STDERR_FILENO,
×
120
                      "Error: User '%s' does not exist.\n",
121
                      user);
UNCOV
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
        }
UNCOV
131
        gid = passwd->pw_gid;
×
UNCOV
132
        uid = passwd->pw_uid;
×
133
    } else {
134
        /* an integer */
UNCOV
135
        if ((unsigned long int)uid > UINT_MAX) {
×
UNCOV
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
    }
UNCOV
150
    if (setuid(uid) < 0) {
×
UNCOV
151
        logprintf(STDERR_FILENO,
×
152
                  "Error: setuid(%d) failed.\n",
153
                  uid);
154
        return -12;
×
155
    }
156
    return 0;
157
}
158

159
int
UNCOV
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",
×
UNCOV
170
                  strerror(errno));
×
171
        return -1;
×
172
    }
173

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

180
    return 0;
181
}
182

183
void tpmlib_debug_libtpms_parameters(TPMLIB_TPMVersion tpmversion)
447✔
184
{
185
    switch (tpmversion) {
447✔
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
}
447✔
216

UNCOV
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

UNCOV
226
    path = realpath(buffer, NULL);
×
UNCOV
227
    if (!path) {
×
UNCOV
228
        logprintf(STDERR_FILENO, "Could not read %s: %s\n",
×
UNCOV
229
                  buffer, strerror(errno));
×
UNCOV
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,226✔
271
{
272
    size_t written = 0;
93,226✔
273
    ssize_t n;
93,226✔
274

275
    while (written < buflen) {
186,452✔
276
        n = write(fd, buffer, buflen - written);
93,226✔
277
        if (n == 0)
93,226✔
278
            return -1;
279
        if (n < 0) {
93,226✔
UNCOV
280
            if (errno == EINTR)
×
UNCOV
281
                continue;
×
282
            return -1;
283
        }
284
        written += n;
93,226✔
285
        buffer = (const char *)buffer + n;
93,226✔
286
    }
287
    return written;
93,226✔
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,127✔
301
{
302
    int i;
22,127✔
303
    size_t off;
22,127✔
304
    unsigned char *buf;
22,127✔
305
    ssize_t n;
22,127✔
306
    size_t bytecount = 0;
22,127✔
307
    size_t numbufs = 0;
22,127✔
308
    size_t lastidx = -1;
22,127✔
309

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

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

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

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

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

337
    free(buf);
6,496✔
338

339
    return n;
6,496✔
340
}
341

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

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

364
    fd = open(filename, flags, mode);
18,164✔
365

366
    if (clear_umask)
18,164✔
367
        umask(orig_umask);
6✔
368

369
    if (fd < 0)
18,164✔
370
        return -1;
371

372
    res = buflen;
18,164✔
373
    if (write_full(fd, buffer, buflen) != res)
18,164✔
UNCOV
374
        res = -1;
×
375

376
    if (close(fd) < 0)
18,164✔
377
        res = -1;
378

379
    if (res < 0)
18,164✔
380
        unlink(filename);
×
381

382
    return res;
383
}
384

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

399
    while (true) {
27,483✔
400
        n = read(fd, buffer, buflen);
27,483✔
401
        if (n < 0) {
27,483✔
UNCOV
402
            if (errno == EINTR)
×
UNCOV
403
                continue;
×
404
            return -1;
405
        }
406
        return n;
407
    }
408
}
409

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

431
    fd = open(filename, O_RDONLY);
28,204✔
432
    if (fd < 0)
28,204✔
433
        return -1;
434

435
    n = fstat(fd, &statbuf);
27,230✔
436
    if (n < 0)
27,230✔
437
        goto err_close;
×
438

439
    buflen = statbuf.st_size;
27,230✔
440
    *buffer = malloc(buflen);
27,230✔
441
    if (!*buffer) {
27,230✔
UNCOV
442
        errno = ENOMEM;
×
UNCOV
443
        goto err_close;
×
444
    }
445

446
    ret = read_eintr(fd, *buffer, buflen);
27,230✔
447
    if (ret < 0 || (size_t)ret != buflen)
27,230✔
UNCOV
448
        goto err_close;
×
449

450
    /* make sure file is always writable */
451
    if (!do_chmod && (statbuf.st_mode & 0200) == 0) {
27,230✔
UNCOV
452
        mode |= 0200;
×
UNCOV
453
        do_chmod = true;
×
454
    } else if (do_chmod && (statbuf.st_mode & ACCESSPERMS) == mode) {
27,230✔
455
        do_chmod = false;
456
    }
457

458
    if (do_chmod && fchmod(fd, mode) < 0)
27,230✔
UNCOV
459
        goto err_close;
×
460

461
    ret = buflen;
462

463
err_close:
27,230✔
464
    if (close(fd) < 0)
27,230✔
UNCOV
465
        ret = -1;
×
466

467
    return ret;
468
}
469

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

486
    jp = json_parser_new();
11✔
487
    if (!json_parser_load_from_data(jp, json_input, -1, &error)) {
11✔
UNCOV
488
        logprintf(STDERR_FILENO,
×
UNCOV
489
                  "Could not parse JSON '%s': %s\n", json_input, error->message);
×
UNCOV
490
        return -1;
×
491
    }
492

493
    root = json_parser_get_root(jp);
11✔
494
    if (!root) {
11✔
UNCOV
495
        logprintf(STDERR_FILENO,
×
496
                  "Could not get root of JSON '%s'\n", json_input);
UNCOV
497
        return -1;
×
498
    }
499
    jr = json_reader_new(root);
11✔
500

501
    if (!json_reader_read_member(jr, key))
11✔
502
        return -2;
503

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

512
    return 0;
513
}
514

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

531
    jg = json_generator_new();
6✔
532
    if (!jg)
6✔
533
        return -1;
534

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

542
    root = json_parser_get_root(jp);
6✔
543
    if (!root) {
6✔
544
        logprintf(STDERR_FILENO,
×
545
                  "Could not get root of JSON '%s'\n", *json_string);
UNCOV
546
        return -1;
×
547
    }
548
    json_generator_set_root(jg, root);
6✔
549

550
    jo = json_node_get_object(root);
6✔
551
    json_object_set_string_member(jo, key, value);
6✔
552

553
    g_free(*json_string);
6✔
554
    *json_string = json_generator_to_data(jg, NULL);
6✔
555

556
    return 0;
6✔
557
}
558

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

578
    jp = json_parser_new();
27,570✔
579
    if (!json_parser_load_from_data(jp, json_input, -1, &error)) {
27,570✔
UNCOV
580
        logprintf(STDERR_FILENO,
×
UNCOV
581
                  "Could not parse JSON '%s': %s\n", json_input, error->message);
×
UNCOV
582
        return -1;
×
583
    }
584

585
    root = json_parser_get_root(jp);
27,570✔
586
    if (!root) {
27,570✔
UNCOV
587
        logprintf(STDERR_FILENO,
×
588
                  "Could not get root of JSON '%s'\n", json_input);
UNCOV
589
        return -1;
×
590
    }
591
    jr = json_reader_new(root);
27,570✔
592

593
    if (!json_reader_read_member(jr, field_name)) {
27,570✔
UNCOV
594
        logprintf(STDERR_FILENO, "Missing '%s' field in '%s'\n",
×
595
                  field_name, json_input);
UNCOV
596
        return -1;
×
597
    }
598

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

613
    return 0;
614
}
615

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

642
    jp = json_parser_new();
3✔
643
    if (!json_parser_load_from_data(jp, json_input, -1, &error)) {
3✔
UNCOV
644
        logprintf(STDERR_FILENO,
×
UNCOV
645
                  "Could not parse JSON '%s': %s\n", json_input, error->message);
×
UNCOV
646
        return -1;
×
647
    }
648

649
    root = json_parser_get_root(jp);
3✔
650
    if (!root) {
3✔
UNCOV
651
        logprintf(STDERR_FILENO,
×
652
                  "Could not get root of JSON '%s'\n", json_input);
UNCOV
653
        return -1;
×
654
    }
655
    jr = json_reader_new(root);
3✔
656

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

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

699
ssize_t strv_strncmp(const gchar *const*str_array, const gchar *s, size_t n)
6✔
700
{
701
    size_t i;
6✔
702

703
    for (i = 0; str_array[i]; i++) {
87✔
704
        if (strncmp(str_array[i], s, n) == 0)
87✔
705
            return (ssize_t)i;
6✔
706
    }
707
    return -1;
708
}
709

710
/* Try to find exactly the needle in the given haystack */
711
static ssize_t strv_strcmp(const gchar *const*haystack, const gchar *needle)
155,980✔
712
{
713
    size_t i;
155,980✔
714

715
    for (i = 0; haystack[i]; i++) {
1,667,205✔
716
        if (strcmp(haystack[i], needle) == 0)
1,648,524✔
717
            return (ssize_t)i;
137,299✔
718
    }
719
    return -1;
720
}
721

722
/*
723
 * Try to find all the needles in the haystack; both arrays of strings
724
 * must be NULL-terminated.
725
 */
726
gboolean strv_contains_all(const gchar *const*haystack, const gchar *const*needles)
82,701✔
727
{
728
    size_t i;
82,701✔
729

730
    for (i = 0; needles[i]; i++) {
220,000✔
731
        if (strv_strcmp(haystack, needles[i]) < 0)
155,980✔
732
            return false;
733
    }
734
    return true;
735
}
736

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

747
    while (array[i]) {
729✔
748
        if ((len < 0 && strcmp(array[i], toremove) == 0) ||
711✔
749
            (len > 0 && strncmp(array[i], toremove, len) == 0)) {
240✔
750
            if (freethem)
15✔
751
                g_free(array[i]);
15✔
752

753
            j = i;
754
            do {
342✔
755
                j++;
342✔
756
                array[j - 1] = array[j];
342✔
757
            } while(array[j]);
342✔
758

759
            num++;
15✔
760
        } else {
761
            i++;
696✔
762
        }
763
    }
764
    return num;
18✔
765
}
766

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

786
    while (array[i]) {
114✔
787
        if (gencmpstr) {
111✔
788
            cmp = gencmpstr(array[i], &len);
111✔
789
            free_cmp = array[i] != cmp;
111✔
790
        } else {
UNCOV
791
            cmp = array[i];
×
UNCOV
792
            len = strlen(cmp);
×
793
        }
794

795
        j = i + 1;
111✔
796
        while (array[j]) {
2,109✔
797
            if ((len < 0 && strcmp(array[j], cmp) == 0) ||
1,998✔
798
                (len > 0 && strncmp(array[j], cmp, len) == 0)) {
228✔
799

UNCOV
800
                num++;
×
UNCOV
801
                if (freethem)
×
UNCOV
802
                    g_free(array[i]);
×
803

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

818
        if (free_cmp)
111✔
UNCOV
819
            g_free(cmp);
×
820
        i++;
821
    }
822
    return num;
3✔
823
}
824

825
/*
826
 * Append entries from a 2nd string array to the first one. Make copies of
827
 * each entry.
828
 */
829
gchar **strv_extend(gchar **array, const gchar *const*append)
2✔
830
{
831
    size_t len1 = 0, len2 = 0, i;
2✔
832

833
    if (array)
2✔
834
        len1 = g_strv_length(array);
1✔
835

836
    while (append[len2])
8✔
837
        len2++;
6✔
838

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

841
    for (i = 0; i < len2; i++)
10✔
842
        array[len1 + i] = g_strdup(append[i]);
6✔
843
    array[len1 + len2] = NULL;
2✔
844

845
    return array;
2✔
846
}
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