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

mendersoftware / mender-mcu / 1749194361

02 Apr 2025 11:15AM UTC coverage: 23.209% (-34.4%) from 57.567%
1749194361

push

gitlab-ci

web-flow
Merge pull request #179 from danielskinstad/rollback-logic

Fix rollback logic

0 of 9 new or added lines in 1 file covered. (0.0%)

960 existing lines in 10 files now uncovered.

716 of 3085 relevant lines covered (23.21%)

11.45 hits per line

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

73.8
/src/core/utils.c
1
/**
2
 * @file      utils.c
3
 * @brief     Mender utility functions
4
 *
5
 * Copyright joelguittet and mender-mcu-client contributors
6
 * Copyright Northern.tech AS
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *     http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20

21
#include "log.h"
22
#include "utils.h"
23

24
/* ASCII unit separator */
25
#define MENDER_KEY_VALUE_DELIMITER "\x1F"
26
/* ASCII record separator */
27
#define MENDER_KEY_VALUE_SEPARATOR "\x1E"
28

29
const char *
UNCOV
30
mender_utils_http_status_to_string(int status) {
×
31

32
    /* Definition of status strings */
33
    static const struct {
34
        uint16_t    status;
35
        const char *str;
36
    } desc[] = { { 100, "Continue" },
37
                 { 101, "Switching Protocols" },
38
                 { 103, "Early Hints" },
39
                 { 200, "OK" },
40
                 { 201, "Created" },
41
                 { 202, "Accepted" },
42
                 { 203, "Non-Authoritative Information" },
43
                 { 204, "No Content" },
44
                 { 205, "Reset Content" },
45
                 { 206, "Partial Content" },
46
                 { 300, "Multiple Choices" },
47
                 { 301, "Moved Permanently" },
48
                 { 302, "Found" },
49
                 { 303, "See Other" },
50
                 { 304, "Not Modified" },
51
                 { 307, "Temporary Redirect" },
52
                 { 308, "Permanent Redirect" },
53
                 { 400, "Bad Request" },
54
                 { 401, "Unauthorized" },
55
                 { 402, "Payment Required" },
56
                 { 403, "Forbidden" },
57
                 { 404, "Not Found" },
58
                 { 405, "Method Not Allowed" },
59
                 { 406, "Not Acceptable" },
60
                 { 407, "Proxy Authentication Required" },
61
                 { 408, "Request Timeout" },
62
                 { 409, "Conflict" },
63
                 { 410, "Gone" },
64
                 { 411, "Length Required" },
65
                 { 412, "Precondition Failed" },
66
                 { 413, "Payload Too Large" },
67
                 { 414, "URI Too Long" },
68
                 { 415, "Unsupported Media Type" },
69
                 { 416, "Range Not Satisfiable" },
70
                 { 417, "Expectation Failed" },
71
                 { 418, "I'm a teapot" },
72
                 { 422, "Unprocessable Entity" },
73
                 { 425, "Too Early" },
74
                 { 426, "Upgrade Required" },
75
                 { 428, "Precondition Required" },
76
                 { 429, "Too Many Requests" },
77
                 { 431, "Request Header Fields Too Large" },
78
                 { 451, "Unavailable For Legal Reasons" },
79
                 { 500, "Internal Server Error" },
80
                 { 501, "Not Implemented" },
81
                 { 502, "Bad Gateway" },
82
                 { 503, "Service Unavailable" },
83
                 { 504, "Gateway Timeout" },
84
                 { 505, "HTTP Version Not Supported" },
85
                 { 506, "Variant Also Negotiates" },
86
                 { 507, "Insufficient Storage" },
87
                 { 508, "Loop Detected" },
88
                 { 510, "Not Extended" },
89
                 { 511, "Network Authentication Required" } };
90

91
    /* Return HTTP status as string */
UNCOV
92
    for (size_t index = 0; index < sizeof(desc) / sizeof(desc[0]); index++) {
×
UNCOV
93
        if (desc[index].status == status) {
×
UNCOV
94
            return desc[index].str;
×
95
        }
96
    }
97

UNCOV
98
    return NULL;
×
99
}
100

101
char *
102
mender_utils_strrstr(const char *haystack, const char *needle) {
107✔
103

104
    assert(NULL != haystack);
107✔
105
    assert(NULL != needle);
107✔
106

107
    char *r = NULL;
107✔
108

109
    if (!needle[0]) {
107✔
110
        return (char *)haystack + strlen(haystack);
1✔
111
    }
112

113
    while (1) {
69✔
114
        char *p = strstr(haystack, needle);
175✔
115
        if (!p) {
175✔
116
            return r;
106✔
117
        }
118
        r        = p;
69✔
119
        haystack = p + 1;
69✔
120
    }
121
}
122

123
char *
124
mender_utils_strdup(const char *str) {
187✔
125
    assert(NULL != str);
187✔
126

127
    size_t str_len = strlen(str);
187✔
128
    return mender_utils_strndup(str, str_len);
187✔
129
}
130

131
char *
132
mender_utils_strndup(const char *str, size_t n) {
189✔
133
    assert(NULL != str);
189✔
134

135
    char *dup = mender_malloc(n + 1);
189✔
136
    if (NULL == dup) {
189✔
137
        return dup;
×
138
    }
139
    dup[n] = '\0';
189✔
140
    return memcpy(dup, str, n);
189✔
141
}
142

143
int
UNCOV
144
mender_utils_asprintf(char **result, const char *fmt, ...) {
×
UNCOV
145
    assert(NULL != result);
×
146

147
    va_list ap;
148
    int     ret;
149

UNCOV
150
    va_start(ap, fmt);
×
UNCOV
151
    ret = mender_utils_vasprintf(result, fmt, ap);
×
UNCOV
152
    va_end(ap);
×
153

UNCOV
154
    return ret;
×
155
}
156

157
int
UNCOV
158
mender_utils_vasprintf(char **result, const char *fmt, va_list ap) {
×
UNCOV
159
    assert(NULL != result);
×
160

161
    int     len;
162
    va_list ap_copy;
163

164
    /* We need to create a copy to not mess the original ap for the second
165
       use (because we cannot use va_start() in this function). */
UNCOV
166
    va_copy(ap_copy, ap);
×
167

168
    /* First, run vsnprintf() in a way that it tells us how much space it needs,
169
       then allocate the appropriate buffer and then run vsnprintf() again to
170
       actually format the string into the buffer. */
UNCOV
171
    len = vsnprintf(NULL, 0, fmt, ap_copy);
×
UNCOV
172
    va_end(ap_copy);
×
UNCOV
173
    if (len <= 0) {
×
174
        return len;
×
175
    }
UNCOV
176
    *result = mender_malloc((size_t)len + 1);
×
UNCOV
177
    if (NULL == *result) {
×
178
        return -1;
×
179
    }
UNCOV
180
    va_copy(ap_copy, ap);
×
UNCOV
181
    len = vsnprintf(*result, len + 1, fmt, ap_copy);
×
UNCOV
182
    va_end(ap_copy);
×
183

UNCOV
184
    return len;
×
185
}
186

187
bool
188
mender_utils_strbeginswith(const char *s1, const char *s2) {
241✔
189

190
    /* Check parameters */
191
    if ((NULL == s1) || (NULL == s2)) {
241✔
192
        return false;
15✔
193
    }
194

195
    /* Compare the beginning of the string */
196
    return (0 == strncmp(s1, s2, strlen(s2)));
226✔
197
}
198

199
bool
200
mender_utils_strendswith(const char *s1, const char *s2) {
93✔
201

202
    /* Check parameters */
203
    if ((NULL == s1) || (NULL == s2)) {
93✔
204
        return false;
15✔
205
    }
206

207
    const size_t len1 = strlen(s1);
78✔
208
    const size_t len2 = strlen(s2);
78✔
209

210
    if (len1 < len2) {
78✔
211
        return false;
1✔
212
    }
213

214
    /* Compare the end of the string */
215
    return (0 == strncmp(s1 + len1 - len2, s2, len2));
77✔
216
}
217

218
const char *
219
mender_utils_deployment_status_to_string(mender_deployment_status_t deployment_status) {
6✔
220

221
    /* Return deployment status as string */
222
    if (MENDER_DEPLOYMENT_STATUS_DOWNLOADING == deployment_status) {
6✔
223
        return "downloading";
1✔
224
    } else if (MENDER_DEPLOYMENT_STATUS_INSTALLING == deployment_status) {
5✔
225
        return "installing";
1✔
226
    } else if (MENDER_DEPLOYMENT_STATUS_REBOOTING == deployment_status) {
4✔
227
        return "rebooting";
1✔
228
    } else if (MENDER_DEPLOYMENT_STATUS_SUCCESS == deployment_status) {
3✔
229
        return "success";
1✔
230
    } else if (MENDER_DEPLOYMENT_STATUS_FAILURE == deployment_status) {
2✔
231
        return "failure";
1✔
232
    } else if (MENDER_DEPLOYMENT_STATUS_ALREADY_INSTALLED == deployment_status) {
1✔
233
        return "already-installed";
1✔
234
    }
235

236
    return NULL;
×
237
}
238

239
static inline unsigned char
240
hexdigit_value(char digit) {
2,234✔
241
    if (digit < 'a') {
2,234✔
242
        return digit - '0';
1,415✔
243
    } else {
244
        return digit - 'a' + 10;
819✔
245
    }
246
}
247

248
bool
249
mender_utils_hexdump_to_bytes(const char *hexdump, unsigned char *bytes, size_t n_bytes) {
37✔
250
    if (NULL == hexdump) {
37✔
251
        mender_log_error("Hexdump is NULL");
1✔
252
        return false;
1✔
253
    }
254

255
    for (size_t i = 0; i < n_bytes; i++) {
1,153✔
256
        size_t idx = 2 * i;
1,118✔
257
        if (!(((hexdump[idx] >= '0') && (hexdump[idx] <= '9')) || ((hexdump[idx] >= 'a') && (hexdump[idx] <= 'f')))
1,118✔
258
            || !(((hexdump[idx + 1] >= '0') && (hexdump[idx + 1] <= '9')) || ((hexdump[idx + 1] >= 'a') && (hexdump[idx + 1] <= 'f')))) {
1,117✔
259
            mender_log_error("Invalid hex byte: %c%c", hexdump[idx], hexdump[idx + 1]);
1✔
260
            return false;
1✔
261
        }
262
        bytes[i] = (hexdigit_value(hexdump[idx]) << 4) + hexdigit_value(hexdump[idx + 1]);
1,117✔
263
    }
264
    return true;
35✔
265
}
266

267
mender_err_t
UNCOV
268
mender_utils_identity_to_json(const mender_identity_t *identity, cJSON **object) {
×
269

UNCOV
270
    assert(NULL != object);
×
271

272
    /* Format data */
UNCOV
273
    *object = cJSON_CreateObject();
×
UNCOV
274
    if (NULL == *object) {
×
275
        mender_log_error("Unable to allocate memory");
×
276
        return MENDER_FAIL;
×
277
    }
UNCOV
278
    if (NULL == cJSON_AddStringToObject(*object, identity->name, identity->value)) {
×
279
        mender_log_error("Unable to add identity to JSON object");
×
280
        return MENDER_FAIL;
×
281
    }
UNCOV
282
    return MENDER_OK;
×
283
}
284

285
void
286
mender_utils_keystore_delete(mender_keystore_t *keystore, uint8_t keystore_len) {
1✔
287
    if (NULL == keystore) {
1✔
UNCOV
288
        return;
×
289
    }
290

291
    for (uint8_t idx = 0; idx < keystore_len; idx++) {
4✔
292
        mender_free(keystore[idx].name);
3✔
293
        mender_free(keystore[idx].value);
3✔
294
    }
295
    mender_free(keystore);
1✔
296
}
297

298
mender_err_t
299
mender_utils_key_value_list_free(mender_key_value_list_t *list) {
57✔
300
    mender_key_value_list_t *item = list;
57✔
301
    while (NULL != item) {
93✔
302
        mender_key_value_list_t *next = item->next;
36✔
303
        mender_free(item->key);
36✔
304
        mender_free(item->value);
36✔
305
        mender_free(item);
36✔
306
        item = next;
36✔
307
    }
308
    return MENDER_OK;
57✔
309
}
310

311
size_t
312
mender_utils_key_value_list_length(const mender_key_value_list_t *list) {
2✔
313
    size_t n_items = 0;
2✔
314
    for (const mender_key_value_list_t *item = list; NULL != item; item = item->next) {
7✔
315
        n_items++;
5✔
316
    }
317
    return n_items;
2✔
318
}
319

320
mender_err_t
321
mender_utils_key_value_list_create_node(const char *type, const char *value, mender_key_value_list_t **list) {
37✔
322

323
    assert(NULL != type);
37✔
324
    assert(NULL != value);
37✔
325
    assert(NULL != list);
37✔
326

327
    mender_key_value_list_t *item = (mender_key_value_list_t *)mender_calloc(1, sizeof(mender_key_value_list_t));
37✔
328
    if (NULL == item) {
37✔
329
        mender_log_error("Unable to allocate memory for linked list node");
×
330
        return MENDER_FAIL;
×
331
    }
332

333
    item->key = mender_utils_strdup(type);
37✔
334
    if (NULL == item->key) {
37✔
335
        mender_log_error("Unable to allocate memory for type");
×
336
        goto ERROR;
×
337
    }
338

339
    item->value = mender_utils_strdup(value);
37✔
340
    if (NULL == item->value) {
37✔
341
        mender_log_error("Unable to allocate memory for value");
×
342
        goto ERROR;
×
343
    }
344

345
    item->next = *list;
37✔
346
    *list      = item;
37✔
347

348
    return MENDER_OK;
37✔
349

350
ERROR:
×
351
    mender_utils_key_value_list_free(item);
×
352
    return MENDER_FAIL;
×
353
}
354

355
mender_err_t
356
mender_utils_key_value_list_to_string(mender_key_value_list_t *list, char **key_value_str) {
1✔
357

358
    /*
359
     * Converts key-value linked list to string of format :
360
     *      "key<\x1F>value<\x1E>...key<\x1Fvalue<\x1E>"
361
     *  Where \x1F is the ASCII unit separator and \x1E is the ASCII record separator
362
     * */
363

364
    /* Start with 1 for the null terminator */
365
    size_t total_len = 1;
1✔
366
    for (mender_key_value_list_t *item = list; NULL != item; item = item->next) {
4✔
367
        if (NULL != item->key && NULL != item->value) {
3✔
368
            total_len += strlen(item->key) + strlen(item->value) + 3; // key=value<space>
3✔
369
        }
370
    }
371

372
    *key_value_str = (char *)mender_calloc(1, total_len);
1✔
373
    if (NULL == *key_value_str) {
1✔
374
        mender_log_error("Unable to allocate memory for string");
×
375
        return MENDER_FAIL;
×
376
    }
377

378
    /* Pointer to key_value_str pointer */
379
    char *str_ptr = *key_value_str;
1✔
380
    for (mender_key_value_list_t *item = list; NULL != item; item = item->next) {
4✔
381
        if (NULL != item->key && NULL != item->value) {
3✔
382
            int ret = snprintf(
3✔
383
                str_ptr, total_len - (str_ptr - *key_value_str), "%s" MENDER_KEY_VALUE_DELIMITER "%s" MENDER_KEY_VALUE_SEPARATOR, item->key, item->value);
3✔
384
            if (0 > ret) {
3✔
385
                mender_log_error("Unable to write to string");
×
386
                return MENDER_FAIL;
×
387
            }
388
            str_ptr += ret;
3✔
389
        }
390
    }
391

392
    return MENDER_OK;
1✔
393
}
394

395
mender_err_t
396
mender_utils_string_to_key_value_list(const char *key_value_str, mender_key_value_list_t **list) {
1✔
397

398
    /*
399
     * Converts of format:
400
     *      "key<\x1F>value<\x1E>...key<\x1Fvalue<\x1E>"
401
     *  to key-value linked list
402
     *  Where \x1F is the ASCII unit separator and \x1E is the ASCII record separator
403
     * */
404

405
    assert(NULL != key_value_str);
1✔
406
    assert(NULL != list);
1✔
407

408
    char *str = mender_utils_strdup(key_value_str);
1✔
409
    if (NULL == str) {
1✔
410
        mender_log_error("Unable to allocate memory for string");
×
411
        return MENDER_FAIL;
×
412
    }
413
    char *saveptr;
414
    char *token = strtok_r(str, MENDER_KEY_VALUE_SEPARATOR, &saveptr);
1✔
415

416
    mender_err_t ret = MENDER_FAIL;
1✔
417

418
    char *delimiter_pos = NULL;
1✔
419
    while (NULL != token) {
4✔
420
        delimiter_pos = strchr(token, MENDER_KEY_VALUE_DELIMITER[0]);
3✔
421
        if (NULL == delimiter_pos) {
3✔
422
            mender_log_error("Invalid key-value string");
×
423
            goto END;
×
424
        }
425
        /* Add null terminator to split key and value to get the key from the token */
426
        token[delimiter_pos - token] = '\0';
3✔
427
        if (MENDER_OK != mender_utils_key_value_list_create_node(token, delimiter_pos + 1, list)) {
3✔
428
            mender_log_error("Unable to create key-value node");
×
429
            goto END;
×
430
        }
431
        token = strtok_r(NULL, MENDER_KEY_VALUE_SEPARATOR, &saveptr);
3✔
432
    }
433

434
    ret = MENDER_OK;
1✔
435
END:
1✔
436
    mender_free(str);
1✔
437
    return ret;
1✔
438
}
439

440
mender_err_t
441
mender_utils_key_value_list_append(mender_key_value_list_t **list1, mender_key_value_list_t **list2) {
1✔
442

443
    /* Combine two linked lists by pointing the last element of the first list
444
     * to the first element of the second list
445
     * Sets list2 to NULL
446
     * */
447

448
    mender_key_value_list_t *item = *list1;
1✔
449
    if (NULL != item) {
1✔
450
        while (NULL != item->next) {
2✔
451
            item = item->next;
1✔
452
        }
453
        item->next = *list2;
1✔
454
    } else {
UNCOV
455
        *list1 = *list2;
×
456
    }
457
    *list2 = NULL;
1✔
458
    return MENDER_OK;
1✔
459
}
460

461
mender_err_t
462
mender_utils_key_value_list_append_unique(mender_key_value_list_t **list1, mender_key_value_list_t **list2) {
1✔
463

464
    /* Get the last item of list1 */
465
    mender_key_value_list_t *last_item1 = *list1;
1✔
466
    if (NULL != last_item1) {
1✔
467
        while (NULL != last_item1->next) {
2✔
468
            last_item1 = last_item1->next;
1✔
469
        }
470
    }
471

472
    mender_key_value_list_t *prev_item2 = NULL;
1✔
473
    mender_key_value_list_t *item2      = *list2;
1✔
474

475
    while (NULL != item2) {
2✔
476
        bool unique = true;
1✔
477
        /* Check if the item2 key is unique in list1 */
478
        for (mender_key_value_list_t *item1 = *list1; item1 != NULL; item1 = item1->next) {
1✔
479
            if (StringEqual(item1->key, item2->key)) {
1✔
480
                unique = false;
1✔
481
                break;
1✔
482
            }
483
        }
484

485
        /* If unique, append item2 to list1 */
486
        if (unique) {
1✔
487
            /* Detach item2 from list2 */
488
            if (NULL != prev_item2) {
×
489
                prev_item2->next = item2->next;
×
490
            } else {
491
                *list2 = item2->next;
×
492
            }
493

494
            /* Append item2 to list1 */
495
            if (NULL != last_item1) {
×
496
                last_item1->next = item2;
×
497
            } else {
498
                *list1 = item2;
×
499
            }
500

501
            /* Update the last_item1 to the newly added node */
502
            last_item1 = item2;
×
503
            /* Move to the next item in list2 */
504
            item2 = item2->next;
×
505
            /* Ensure the last node of list1 has next set to NULL */
506
            last_item1->next = NULL;
×
507
        } else {
508
            /* Move to the next item in list2 */
509
            prev_item2 = item2;
1✔
510
            item2      = item2->next;
1✔
511
        }
512
    }
513

514
    return MENDER_OK;
1✔
515
}
516

517
mender_err_t
518
mender_utils_key_value_list_delete_node(mender_key_value_list_t **list, const char *key) {
1✔
519

520
    mender_key_value_list_t *to_free = NULL;
1✔
521
    mender_key_value_list_t *prev    = NULL;
1✔
522
    mender_key_value_list_t *item    = *list;
1✔
523
    while (NULL != item) {
1✔
524
        if (StringEqual(item->key, key)) {
1✔
525
            to_free = item;
1✔
526
            if (NULL == prev) {
1✔
527
                *list = item->next;
1✔
528
                break;
1✔
529
            } else {
530
                prev->next = item->next;
×
531
            }
532
            break;
×
533
        }
UNCOV
534
        prev = item;
×
UNCOV
535
        item = item->next;
×
536
    }
537

538
    if (NULL != to_free) {
1✔
539
        mender_free(to_free->key);
1✔
540
        mender_free(to_free->value);
1✔
541
        mender_free(to_free);
1✔
542
    }
543
    return MENDER_OK;
1✔
544
}
545

546
mender_err_t
547
mender_utils_compare_wildcard(const char *str, const char *wildcard_str, bool *match) {
28✔
548

549
    assert(NULL != str);
28✔
550
    assert(NULL != wildcard_str);
28✔
551
    assert(NULL != match);
28✔
552

553
    const char *to_match = str;
28✔
554
    const char *boundary = wildcard_str;
28✔
555

556
    char *ptr = strchr(boundary, '*');
28✔
557

558
    /* Check if the wildcard contains wildcard, else compare strings */
559
    if (NULL == ptr) {
28✔
560
        *match = (StringEqual(str, wildcard_str));
7✔
561
        return MENDER_OK;
7✔
562
    }
563

564
    *match = true;
21✔
565

566
    /* Check if the wildcard string starts with wildcard */
567
    if (to_match[0] != '*') {
21✔
568
        if (0 != strncmp(wildcard_str, str, ptr - boundary)) {
21✔
569
            *match = false;
2✔
570
            return MENDER_OK;
2✔
571
        }
572
    }
573

574
    /*
575
     * Iterate over substrings separated by wildcard *
576
     * Attempt to find the substring in the string
577
     */
578
    while (NULL != (ptr = strchr(boundary, '*'))) {
54✔
579
        const size_t len          = (size_t)(ptr - boundary);
37✔
580
        const size_t to_match_len = strlen(to_match);
37✔
581
        const char  *find         = NULL;
37✔
582

583
        for (size_t i = 0; i <= to_match_len - len; i++) {
74✔
584
            if (0 == memcmp(to_match + i, boundary, len)) {
72✔
585
                find = to_match + i;
35✔
586
                break;
35✔
587
            }
588
        }
589

590
        if (NULL == find) {
37✔
591
            *match = false;
2✔
592
            break;
2✔
593
        }
594
        to_match = find + len;
35✔
595
        boundary = ptr + 1;
35✔
596
    }
597

598
    if (NULL == strstr(to_match, boundary)) {
19✔
599
        *match = false;
5✔
600
    }
601

602
    return MENDER_OK;
19✔
603
}
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