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

stefanberger / swtpm / #2773

04 Apr 2025 04:21PM UTC coverage: 73.293% (+0.03%) from 73.265%
#2773

push

travis-ci

web-flow
Merge 55b9ae8c9 into 79430fa0d

26 of 39 new or added lines in 2 files covered. (66.67%)

11 existing lines in 2 files now uncovered.

8060 of 10997 relevant lines covered (73.29%)

13565.46 hits per line

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

61.05
/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

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

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

56
#include <openssl/rand.h>
57

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

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

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

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

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

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

89
    return 0;
90

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

97
err_exit:
98
    return -1;
99
}
100

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

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

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

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

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

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

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

174
    return 0;
175
}
176

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

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

215
    char buffer[64];
×
216
    char *path;
×
217

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

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

227
    return path;
228

229
#elif defined __APPLE__
230

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

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

250
#endif
251
}
252

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

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

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

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

312
    if (numbufs == 1)
22,128✔
313
        return write_full(fd, iov[lastidx].iov_base, iov[lastidx].iov_len);
15,631✔
314

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

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

329
    n = write_full(fd, buf, off);
6,497✔
330

331
    free(buf);
6,497✔
332

333
    return n;
6,497✔
334
}
335

336
/*
337
 * read_einter: Read bytes from a file descriptor into a buffer
338
 *              and handle EINTR. Perform one read().
339
 *
340
 * @fd: file descriptor to read from
341
 * @buffer: buffer
342
 * @buflen: length of buffer
343
 *
344
 * Returns -1 in case an error occurred, number of bytes read otherwise.
345
 */
346
ssize_t read_eintr(int fd, void *buffer, size_t buflen)
248✔
347
{
348
    ssize_t n;
248✔
349

350
    while (true) {
248✔
351
        n = read(fd, buffer, buflen);
248✔
352
        if (n < 0) {
248✔
353
            if (errno == EINTR)
×
354
                continue;
×
355
            return -1;
356
        }
357
        return n;
358
    }
359
}
360

361
/*
362
 * Get the value of a map's key.
363
 *
364
 * Returns:
365
 * 0 : success
366
 * -1 : failure to parse the JSON input
367
 * -2 : could not find the key
368
 */
369
int json_get_map_key_value(const char *json_input,
11✔
370
                           const char *key, char **value)
371
{
372
    g_autoptr(GError) error = NULL;
22✔
373
    g_autoptr(JsonParser) jp = NULL;
11✔
374
    g_autoptr(JsonReader) jr = NULL;
11✔
375
    JsonNode *root;
11✔
376

377
    jp = json_parser_new();
11✔
378
    if (!json_parser_load_from_data(jp, json_input, -1, &error)) {
11✔
379
        logprintf(STDERR_FILENO,
×
380
                  "Could not parse JSON '%s': %s\n", json_input, error->message);
×
381
        return -1;
×
382
    }
383

384
    root = json_parser_get_root(jp);
11✔
385
    if (!root) {
11✔
386
        logprintf(STDERR_FILENO,
×
387
                  "Could not get root of JSON '%s'\n", json_input);
388
        return -1;
×
389
    }
390
    jr = json_reader_new(root);
11✔
391

392
    if (!json_reader_read_member(jr, key))
11✔
393
        return -2;
394

395
    *value = g_strdup(json_reader_get_string_value(jr));
7✔
396
    if (*value == NULL) {
7✔
397
        /* value not a string */
398
        logprintf(STDERR_FILENO,
×
399
                  "'%s' in JSON map is not a string\n", key);
400
        return -1;
×
401
    }
402

403
    return 0;
404
}
405

406
/*
407
 * Set the value of a map's key and return the new string
408
 *
409
 * Returns:
410
 * 0 : success
411
 * -1 : fatal failure
412
 */
413
int json_set_map_key_value(char **json_string,
6✔
414
                           const char *key, const char *value)
415
{
416
    g_autoptr(JsonParser) jp = NULL;
12✔
417
    g_autoptr(GError) error = NULL;
6✔
418
    g_autoptr(JsonGenerator) jg = NULL;
6✔
419
    JsonObject *jo;
6✔
420
    JsonNode *root;
6✔
421

422
    jg = json_generator_new();
6✔
423
    if (!jg)
6✔
424
        return -1;
425

426
    jp = json_parser_new();
6✔
427
    if (!json_parser_load_from_data(jp, *json_string, -1, &error)) {
6✔
428
        logprintf(STDERR_FILENO,
×
429
                  "Could not parse JSON '%s': %s\n", *json_string, error->message);
×
430
        return -1;
×
431
    }
432

433
    root = json_parser_get_root(jp);
6✔
434
    if (!root) {
6✔
435
        logprintf(STDERR_FILENO,
×
436
                  "Could not get root of JSON '%s'\n", *json_string);
437
        return -1;
×
438
    }
439
    json_generator_set_root(jg, root);
6✔
440

441
    jo = json_node_get_object(root);
6✔
442
    json_object_set_string_member(jo, key, value);
6✔
443

444
    g_free(*json_string);
6✔
445
    *json_string = json_generator_to_data(jg, NULL);
6✔
446

447
    return 0;
6✔
448
}
449

450
/*
451
 * In the given JSON map find a map with name @field_name and then
452
 * access the field @field_name2 in this map and return its value.
453
 *
454
 * @json_input: JSON object as string
455
 * @field_name: Name of map
456
 * @field_name2: Name of entry in map
457
 * @value: Results is returned here
458
 *
459
 * Returns 0 in case of success, -1 otherwise.
460
 */
461
int json_get_submap_value(const char *json_input, const char *field_name,
27,567✔
462
                          const char *field_name2, char **value)
463
{
464
    g_autoptr(JsonParser) jp = NULL;
55,134✔
465
    g_autoptr(JsonReader) jr = NULL;
27,567✔
466
    g_autoptr(GError) error = NULL;
27,567✔
467
    JsonNode *root;
27,567✔
468

469
    jp = json_parser_new();
27,567✔
470
    if (!json_parser_load_from_data(jp, json_input, -1, &error)) {
27,567✔
471
        logprintf(STDERR_FILENO,
×
472
                  "Could not parse JSON '%s': %s\n", json_input, error->message);
×
473
        return -1;
×
474
    }
475

476
    root = json_parser_get_root(jp);
27,567✔
477
    if (!root) {
27,567✔
478
        logprintf(STDERR_FILENO,
×
479
                  "Could not get root of JSON '%s'\n", json_input);
480
        return -1;
×
481
    }
482
    jr = json_reader_new(root);
27,567✔
483

484
    if (!json_reader_read_member(jr, field_name)) {
27,567✔
485
        logprintf(STDERR_FILENO, "Missing '%s' field in '%s'\n",
×
486
                  field_name, json_input);
487
        return -1;
×
488
    }
489

490
    if (!json_reader_read_member(jr, field_name2)) {
27,567✔
491
        logprintf(STDERR_FILENO, "Missing '%s/%s' field in '%s'\n",
×
492
                  field_name, field_name2, json_input);
493
        return -1;
×
494
    }
495
    *value = g_strdup(json_reader_get_string_value(jr));
27,567✔
496
    if (*value == NULL) {
27,567✔
497
        /* value not a string */
498
        logprintf(STDERR_FILENO,
×
499
                  "'%s/%s' field in '%s' is not a string\n",
500
                  field_name, field_name2, json_input);
501
        return -1;
×
502
    }
503

504
    return 0;
505
}
506

507
/*
508
 * In the given JSON map select @field0_name whose value must be an array.
509
 * Inside the array of maps, find a map whose @field1_name has the value
510
 * @field1_value. Then select field2_name and return its value.
511
 *
512
 * @json_input: JSON array of maps as a string
513
 * @field0_name: The name of the map entry holding the array of maps
514
 * @field1_name: The name of an entry in the map
515
 * @field1_value: The value of an entry in the map
516
 * @field2_name: Name of entry in map whose value to return
517
 * @value: Results is returned here
518
 *
519
 * Returns 0 in case of success, -1 otherwise.
520
 */
521
int json_get_array_entry_value(const char *json_input,
3✔
522
                               const char *field0_name,
523
                               const char *field1_name, const char *field1_value,
524
                               const char *field2_name, char **value)
525
{
526
    g_autoptr(JsonParser) jp = NULL;
6✔
527
    g_autoptr(JsonReader) jr = NULL;
3✔
528
    g_autoptr(GError) error = NULL;
3✔
529
    const gchar *strval;
3✔
530
    JsonNode *root;
3✔
531
    guint idx;
3✔
532

533
    jp = json_parser_new();
3✔
534
    if (!json_parser_load_from_data(jp, json_input, -1, &error)) {
3✔
NEW
535
        logprintf(STDERR_FILENO,
×
NEW
536
                  "Could not parse JSON '%s': %s\n", json_input, error->message);
×
NEW
537
        return -1;
×
538
    }
539

540
    root = json_parser_get_root(jp);
3✔
541
    if (!root) {
3✔
NEW
542
        logprintf(STDERR_FILENO,
×
543
                  "Could not get root of JSON '%s'\n", json_input);
NEW
544
        return -1;
×
545
    }
546
    jr = json_reader_new(root);
3✔
547

548
    if (!json_reader_read_member(jr, field0_name)) {
3✔
NEW
549
        logprintf(STDERR_FILENO,
×
550
                  "Could not find the initial field '%s'in '%s'\n",
551
                  field0_name, json_input);
NEW
552
        return -1;
×
553
    }
554
    for (idx = 0;; idx++) {
6✔
555
        if (!json_reader_read_element(jr, idx)) {
9✔
NEW
556
            logprintf(STDERR_FILENO,
×
557
                      "Could not find an element with name '%s' and value '%s'\n",
558
                      field1_name, field1_value);
NEW
559
            return -1;
×
560
        }
561
        if (json_reader_read_member(jr, field1_name)) {
9✔
562
            if ((strval = json_reader_get_string_value(jr)) != NULL &&
18✔
563
                g_strcmp0(strval, field1_value) == 0) {
9✔
564

565
                json_reader_end_member(jr);
3✔
566
                if (!json_reader_read_member(jr, field2_name)) {
3✔
NEW
567
                    logprintf(STDERR_FILENO,
×
568
                              "Found map entry in '%s' but could not find field '%s'",
569
                              json_input, field2_name);
NEW
570
                    return -1;
×
571
                }
572
                *value = g_strdup(json_reader_get_string_value(jr));
3✔
573
                if (*value == NULL) {
3✔
574
                    /* value not a string */
NEW
575
                    logprintf(STDERR_FILENO,
×
576
                              "'%s' field in '%s' is not a string\n",
577
                              field2_name, json_input);
NEW
578
                    return -1;
×
579
                }
580
                return 0;
581
            }
582
            json_reader_end_member(jr);
6✔
583
        }
584
        json_reader_end_element(jr);
6✔
585
    }
586
    /* must never get here */
587
    return -1;
588
}
589

590
ssize_t strv_strncmp(const gchar *const*str_array, const gchar *s, size_t n)
6✔
591
{
592
    size_t i;
6✔
593

594
    for (i = 0; str_array[i]; i++) {
87✔
595
        if (strncmp(str_array[i], s, n) == 0)
87✔
596
            return (ssize_t)i;
6✔
597
    }
598
    return -1;
599
}
600

601
/* Try to find exactly the needle in the given haystack */
602
static ssize_t strv_strcmp(const gchar *const*haystack, const gchar *needle)
155,963✔
603
{
604
    size_t i;
155,963✔
605

606
    for (i = 0; haystack[i]; i++) {
1,667,024✔
607
        if (strcmp(haystack[i], needle) == 0)
1,648,345✔
608
            return (ssize_t)i;
137,284✔
609
    }
610
    return -1;
611
}
612

613
/*
614
 * Try to find all the needles in the haystack; both arrays of strings
615
 * must be NULL-terminated.
616
 */
617
gboolean strv_contains_all(const gchar *const*haystack, const gchar *const*needles)
82,692✔
618
{
619
    size_t i;
82,692✔
620

621
    for (i = 0; needles[i]; i++) {
219,976✔
622
        if (strv_strcmp(haystack, needles[i]) < 0)
155,963✔
623
            return false;
624
    }
625
    return true;
626
}
627

628
/*
629
 * Remove all entries in the @array that either fully match @toremove
630
 * (@len = -1) or where @toremove is a prefix of.
631
 * This function returns the number of entries that were removed.
632
 */
633
size_t strv_remove(gchar **array, const gchar *toremove, ssize_t len,
18✔
634
                   gboolean freethem)
635
{
636
    size_t i = 0, j, num = 0;
18✔
637

638
    while (array[i]) {
729✔
639
        if ((len < 0 && strcmp(array[i], toremove) == 0) ||
711✔
640
            (len > 0 && strncmp(array[i], toremove, len) == 0)) {
240✔
641
            if (freethem)
15✔
642
                g_free(array[i]);
15✔
643

644
            j = i;
645
            do {
342✔
646
                j++;
342✔
647
                array[j - 1] = array[j];
342✔
648
            } while(array[j]);
342✔
649

650
            num++;
15✔
651
        } else {
652
            i++;
696✔
653
        }
654
    }
655
    return num;
18✔
656
}
657

658
/*
659
 * Deduplicate items in a NULL-terminated array of strings.
660
 * When a duplicate item is found then the first item is removed and all later
661
 * ones are kept -- this is to deduplicate items in the same way as libtpms
662
 * deduplicates comma separated items in a string. The string to use for
663
 * finding duplicates is expected to be returned from an optional gencmpstr_t
664
 * function that in the simplest case can return the passed string and adjust
665
 * string comparison to be done on full string (len = -1) or prefix comparison.
666
 * If not function is given then full string matching is done.
667
 *
668
 * This function returns the number of entries removed from the array.
669
 */
670
size_t strv_dedup(gchar **array, gencmpstr_t gencmpstr, gboolean freethem)
3✔
671
{
672
    gboolean free_cmp = false;
3✔
673
    size_t num = 0, i = 0, j;
3✔
674
    ssize_t len = 0;
3✔
675
    gchar *cmp;
3✔
676

677
    while (array[i]) {
114✔
678
        if (gencmpstr) {
111✔
679
            cmp = gencmpstr(array[i], &len);
111✔
680
            free_cmp = array[i] != cmp;
111✔
681
        } else {
UNCOV
682
            cmp = array[i];
×
UNCOV
683
            len = strlen(cmp);
×
684
        }
685

686
        j = i + 1;
111✔
687
        while (array[j]) {
2,109✔
688
            if ((len < 0 && strcmp(array[j], cmp) == 0) ||
1,998✔
689
                (len > 0 && strncmp(array[j], cmp, len) == 0)) {
228✔
690

UNCOV
691
                num++;
×
UNCOV
692
                if (freethem)
×
UNCOV
693
                    g_free(array[i]);
×
694

695
                /*
696
                 * Keep the later ones in the array since libtpms also keeps
697
                 * later items ones in string when deduplicating.
698
                 */
699
                j = i;
UNCOV
700
                do {
×
UNCOV
701
                    array[j] = array[j + 1];
×
UNCOV
702
                    j++;
×
UNCOV
703
                } while (array[j]);
×
704
                break;
705
            }
706
            j++;
1,998✔
707
        }
708

709
        if (free_cmp)
111✔
UNCOV
710
            g_free(cmp);
×
711
        i++;
712
    }
713
    return num;
3✔
714
}
715

716
/*
717
 * Append entries from a 2nd string array to the first one. Make copies of
718
 * each entry.
719
 */
720
gchar **strv_extend(gchar **array, const gchar *const*append)
2✔
721
{
722
    size_t len1 = 0, len2 = 0, i;
2✔
723

724
    if (array)
2✔
725
        len1 = g_strv_length(array);
1✔
726

727
    while (append[len2])
8✔
728
        len2++;
6✔
729

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

732
    for (i = 0; i < len2; i++)
10✔
733
        array[len1 + i] = g_strdup(append[i]);
6✔
734
    array[len1 + len2] = NULL;
2✔
735

736
    return array;
2✔
737
}
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