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

mendersoftware / mender-mcu / 2111356208

21 Oct 2025 10:51AM UTC coverage: 61.258% (+4.4%) from 56.866%
2111356208

push

gitlab-ci

danielskinstad
build: support warning/failing on artifact sizes

Add support for specifying size limits for Mender
Artifacts during a build. This uses the feature implemented in
mender-artifact 4.2.0. The limits can be configured through the Kconfig,
    or by enabling `MENDER_ARTIFACT_SIZE_LIMITS` and setting
    `MENDER_ARTIFACT_WARN_SIZE` and or `MENDER_ARTIFACT_MAX_SIZE`.

Ticket: MEN-8584

Signed-off-by: Daniel Skinstad Drabitzius <daniel.drabitzius@northern.tech>

2473 of 4037 relevant lines covered (61.26%)

70.55 hits per line

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

62.92
/src/core/api.c
1
/**
2
 * @file      api.c
3
 * @brief     Implementation of the Mender API
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 "alloc.h"
22
#include "api.h"
23
#include "artifact.h"
24
#include "error-counters.h"
25
#include "os.h"
26
#include "storage.h"
27
#include "http.h"
28
#include "log.h"
29
#include "tls.h"
30
#include "utils.h"
31

32
/**
33
 * @brief HTTP retry mechanism parameters
34
 *
35
 * On unreliable networks, an HTTP request can easily fail due to some short
36
 * network issue. Thus there needs to be a retry mechanism to prevent every
37
 * single such temporary failure to cause an error.
38
 *
39
 * 5 attempts (so 4 retries, to be precise) with 100ms as the first pause
40
 * interval doubled with every attempt gives a total of 1500ms period of retries
41
 * with nice progressive pauses in between (100, 200, 400, 800ms).
42
 *
43
 * @note: RETRY_ATTEMPTS is uint8_t,
44
 *        INTERVAL_BASE * INTERVAL_FACTOR**(ATTEMPTS - 1) has to fit in uint16_t
45
 *        Or the code below has to be adjusted, but such a long sleep doesn't make sense here!
46
 */
47
#define HTTP_RETRY_ATTEMPTS        5
48
#define HTTP_RETRY_INTERVAL_BASE   100 /* milliseconds */
49
#define HTTP_RETRY_INTERVAL_FACTOR 2
50

51
/**
52
 * @brief Paths of the mender-server APIs
53
 */
54
#define MENDER_API_PATH_POST_AUTHENTICATION_REQUESTS "/api/devices/v1/authentication/auth_requests"
55
#define MENDER_API_PATH_GET_NEXT_DEPLOYMENT          "/api/devices/v1/deployments/device/deployments/next"
56
#define MENDER_API_PATH_POST_NEXT_DEPLOYMENT_V2      "/api/devices/v2/deployments/device/deployments/next"
57
#define MENDER_API_PATH_PUT_DEPLOYMENT_STATUS        "/api/devices/v1/deployments/device/deployments/%s/status"
58
#define MENDER_API_PATH_PUT_DEPLOYMENT_LOGS          "/api/devices/v1/deployments/device/deployments/%s/log"
59
#define MENDER_API_PATH_GET_DEVICE_CONFIGURATION     "/api/devices/v1/deviceconfig/configuration"
60
#define MENDER_API_PATH_PUT_DEVICE_CONFIGURATION     "/api/devices/v1/deviceconfig/configuration"
61
#define MENDER_API_PATH_GET_DEVICE_CONNECT           "/api/devices/v1/deviceconnect/connect"
62
#define MENDER_API_PATH_PUT_DEVICE_ATTRIBUTES        "/api/devices/v1/inventory/device/attributes"
63

64
/**
65
 * @brief Mender API configuration
66
 */
67
static mender_api_config_t api_config;
68

69
/**
70
 * @brief Authentication token
71
 */
72
static char *api_jwt = NULL;
73

74
/**
75
 * @brief A mutex ensuring there are no concurrent operations using or updating the authentication token
76
 */
77
static void *auth_lock = NULL;
78

79
/**
80
 * @brief HTTP callback used to handle text content
81
 * @param event HTTP client event
82
 * @param data Data received
83
 * @param data_length Data length
84
 * @param params Callback parameters
85
 * @return MENDER_OK if the function succeeds, error code otherwise
86
 */
87
static mender_err_t mender_api_http_text_callback(mender_http_client_event_t event, void *data, size_t data_length, void *params);
88

89
/**
90
 * @brief Perform authentication of the device, retrieve token from mender-server used for the next requests
91
 * @return MENDER_OK if the function succeeds, error code otherwise
92
 */
93
static mender_err_t perform_authentication(void);
94

95
/**
96
 * @brief Ensure authenticated and holding the #auth_lock
97
 * @return MENDER_OK if success, MENDER_LOCK_FAILED in case of lock failure, other errors otherwise
98
 */
99
static mender_err_t ensure_authenticated_and_locked(void);
100

101
mender_err_t
102
mender_api_init(mender_api_config_t *config) {
34✔
103
    assert(NULL != config);
34✔
104
    assert(NULL != config->device_type);
34✔
105
    assert(NULL != config->host);
34✔
106
    assert(NULL != config->identity_cb);
34✔
107
    assert(NULL != config->device_tier);
34✔
108

109
    mender_err_t ret;
110

111
    /* Save configuration */
112
    memcpy(&api_config, config, sizeof(mender_api_config_t));
34✔
113

114
    /* Initializations */
115
    mender_http_config_t mender_http_config = { .host = api_config.host };
34✔
116
    if (MENDER_OK != (ret = mender_http_init(&mender_http_config))) {
34✔
117
        mender_log_error("Unable to initialize HTTP");
×
118
        return ret;
×
119
    }
120

121
    if (MENDER_OK != (ret = mender_os_mutex_create(&auth_lock))) {
34✔
122
        mender_log_error("Unable to initialize authentication lock");
×
123
        return ret;
×
124
    }
125

126
    return ret;
34✔
127
}
128

129
mender_err_t
130
mender_api_drop_authentication_data(void) {
6✔
131
    mender_err_t ret;
132
    if (MENDER_OK != (ret = mender_os_mutex_take(auth_lock, -1))) {
6✔
133
        mender_log_error("Unable to obtain the authentication lock");
×
134
        return MENDER_LOCK_FAILED;
×
135
    }
136
    FREE_AND_NULL(api_jwt);
6✔
137
    if (MENDER_OK != (ret = mender_os_mutex_give(auth_lock))) {
6✔
138
        mender_log_error("Unable to release the authentication lock");
×
139
    }
140

141
    return ret;
6✔
142
}
143

144
mender_err_t
145
mender_api_ensure_authenticated(void) {
6✔
146
    mender_err_t ret = ensure_authenticated_and_locked();
6✔
147
    if (MENDER_LOCK_FAILED == ret) {
5✔
148
        /* Error already logged. */
149
        return MENDER_FAIL;
×
150
    }
151
    bool authenticated = ((MENDER_OK == ret) || (MENDER_DONE == ret));
5✔
152

153
    if (MENDER_OK != (ret = mender_os_mutex_give(auth_lock))) {
5✔
154
        mender_log_error("Unable to release the authentication lock");
×
155
    }
156

157
    return authenticated ? ret : MENDER_FAIL;
5✔
158
}
159

160
static mender_err_t
161
ensure_authenticated_and_locked(void) {
133✔
162
    mender_err_t ret;
163

164
    if (MENDER_OK != (ret = mender_os_mutex_take(auth_lock, -1))) {
133✔
165
        mender_log_error("Unable to obtain the authentication lock");
×
166
        return MENDER_LOCK_FAILED;
×
167
    }
168

169
    if (NULL != api_jwt) {
133✔
170
        return MENDER_DONE;
103✔
171
    }
172

173
    /* Perform authentication with the mender server */
174
    if (MENDER_OK != (ret = perform_authentication())) {
30✔
175
        mender_log_error("Authentication failed");
×
176
        return ret;
×
177
    } else {
178
        mender_log_debug("Authenticated successfully");
29✔
179
    }
180

181
    return ret;
29✔
182
}
183

184
static mender_err_t
185
perform_authentication(void) {
30✔
186
    mender_err_t             ret;
187
    char                    *public_key_pem   = NULL;
30✔
188
    const mender_identity_t *identity         = NULL;
30✔
189
    cJSON                   *json_identity    = NULL;
30✔
190
    char                    *identity_info    = NULL;
30✔
191
    cJSON                   *json_payload     = NULL;
30✔
192
    char                    *payload          = NULL;
30✔
193
    char                    *response         = NULL;
30✔
194
    char                    *signature        = NULL;
30✔
195
    size_t                   signature_length = 0;
30✔
196
    int                      status           = 0;
30✔
197
    uint8_t                  remaining_attempts;
198
    uint16_t                 retry_interval;
199

200
    /* Get public key in PEM format */
201
    if (MENDER_OK != (ret = mender_tls_get_public_key_pem(&public_key_pem))) {
30✔
202
        mender_log_error("Unable to get public key");
×
203
        goto END;
×
204
    }
205

206
    /* Get identity (we don't own the returned data) */
207
    if (MENDER_OK != (ret = api_config.identity_cb(&identity))) {
30✔
208
        mender_log_error("Unable to get identity");
×
209
        goto END;
×
210
    }
211

212
    /* Format identity */
213
    if (MENDER_OK != (ret = mender_utils_identity_to_json(identity, &json_identity))) {
30✔
214
        mender_log_error("Unable to format identity");
×
215
        goto END;
×
216
    }
217
    if (NULL == (identity_info = cJSON_PrintUnformatted(json_identity))) {
30✔
218
        mender_log_error("Unable to allocate memory");
×
219
        ret = MENDER_FAIL;
×
220
        goto END;
×
221
    }
222

223
    /* Format payload */
224
    if (NULL == (json_payload = cJSON_CreateObject())) {
30✔
225
        mender_log_error("Unable to allocate memory");
×
226
        ret = MENDER_FAIL;
×
227
        goto END;
×
228
    }
229
    cJSON_AddStringToObject(json_payload, "id_data", identity_info);
30✔
230
    cJSON_AddStringToObject(json_payload, "pubkey", public_key_pem);
30✔
231
    if (NULL != api_config.tenant_token) {
30✔
232
        cJSON_AddStringToObject(json_payload, "tenant_token", api_config.tenant_token);
30✔
233
    }
234
    if (NULL != api_config.device_tier) {
30✔
235
        cJSON_AddStringToObject(json_payload, "tier", api_config.device_tier);
30✔
236
    }
237
    if (NULL == (payload = cJSON_PrintUnformatted(json_payload))) {
30✔
238
        mender_log_error("Unable to allocate memory");
×
239
        ret = MENDER_FAIL;
×
240
        goto END;
×
241
    }
242

243
    /* Sign payload */
244
    if (MENDER_OK != (ret = mender_tls_sign_payload(payload, &signature, &signature_length))) {
30✔
245
        mender_log_error("Unable to sign payload");
×
246
        goto END;
×
247
    }
248

249
    /* Perform HTTP request */
250
    remaining_attempts = HTTP_RETRY_ATTEMPTS;
30✔
251
    retry_interval     = HTTP_RETRY_INTERVAL_BASE;
30✔
252
    do {
253
        ret = mender_http_perform(NULL,
31✔
254
                                  MENDER_API_PATH_POST_AUTHENTICATION_REQUESTS,
255
                                  MENDER_HTTP_POST,
256
                                  payload,
257
                                  signature,
258
                                  &mender_api_http_text_callback,
259
                                  (void *)&response,
260
                                  &status);
261
        if (MENDER_RETRY_ERROR == ret) {
30✔
262
            mender_os_sleep(retry_interval);
1✔
263
            retry_interval = retry_interval * HTTP_RETRY_INTERVAL_FACTOR;
1✔
264
            remaining_attempts--;
1✔
265

266
            /* Just in case something was already gathered as response. */
267
            FREE_AND_NULL(response);
1✔
268
        }
269
    } while ((MENDER_RETRY_ERROR == ret) && (remaining_attempts > 0));
30✔
270

271
    if (MENDER_OK != ret) {
29✔
272
        mender_log_error("Unable to perform HTTP request");
×
273
        mender_err_count_net_inc();
×
274
        goto END;
×
275
    }
276

277
    /* Treatment depending of the status */
278
    if (200 == status) {
29✔
279
        if (NULL == response) {
29✔
280
            mender_log_error("Response is empty");
×
281
            ret = MENDER_FAIL;
×
282
            goto END;
×
283
        }
284
        if (NULL != api_jwt) {
29✔
285
            mender_free(api_jwt);
×
286
        }
287
        if (NULL == (api_jwt = mender_utils_strdup(response))) {
29✔
288
            mender_log_error("Unable to allocate memory");
×
289
            ret = MENDER_FAIL;
×
290
            goto END;
×
291
        }
292
        ret = MENDER_OK;
29✔
293
    } else {
294
        mender_api_print_response_error(response, status);
×
295
        /* Maybe the identity is wrong? Let's make sure we get fresh data for the next attempt. */
296
        FREE_AND_NULL(identity_info);
×
297
        ret = MENDER_RETRY_ERROR;
×
298
    }
299

300
END:
29✔
301

302
    /* Release memory */
303
    mender_free(response);
29✔
304
    mender_free(signature);
29✔
305
    mender_free(payload);
29✔
306
    cJSON_Delete(json_payload);
29✔
307
    cJSON_Delete(json_identity);
29✔
308
    mender_free(identity_info);
29✔
309
    mender_free(public_key_pem);
29✔
310

311
    return ret;
29✔
312
}
313

314
/**
315
 * @see mender_http_perform()
316
 */
317
static mender_err_t
318
authenticated_http_perform(char *path, mender_http_method_t method, char *payload, char *signature, char **response, int *status) {
127✔
319
    mender_err_t ret;
320
    uint8_t      remaining_attempts;
321
    uint16_t     retry_interval;
322

323
    if (MENDER_IS_ERROR(ret = ensure_authenticated_and_locked())) {
127✔
324
        /* Errors already logged. */
325
        if (MENDER_LOCK_FAILED != ret) {
×
326
            if (MENDER_OK != mender_os_mutex_give(auth_lock)) {
×
327
                mender_log_error("Unable to release the authentication lock");
×
328
                return MENDER_FAIL;
×
329
            }
330
        }
331
        return ret;
×
332
    }
333

334
    remaining_attempts = HTTP_RETRY_ATTEMPTS;
127✔
335
    retry_interval     = HTTP_RETRY_INTERVAL_BASE;
127✔
336
    do {
337
        ret = mender_http_perform(api_jwt, path, method, payload, signature, &mender_api_http_text_callback, response, status);
127✔
338
        if (MENDER_RETRY_ERROR == ret) {
113✔
339
            mender_os_sleep(retry_interval);
×
340
            retry_interval = retry_interval * HTTP_RETRY_INTERVAL_FACTOR;
×
341
            remaining_attempts--;
×
342

343
            /* Just in case something was already gathered as response. */
344
            FREE_AND_NULL(*response);
×
345
        }
346
    } while ((MENDER_RETRY_ERROR == ret) && (remaining_attempts > 0));
113✔
347

348
    if (MENDER_OK != mender_os_mutex_give(auth_lock)) {
113✔
349
        mender_log_error("Unable to release the authentication lock");
×
350
        return MENDER_FAIL;
×
351
    }
352
    if (MENDER_OK != ret) {
113✔
353
        /* HTTP errors already logged. */
354
        mender_err_count_net_inc();
×
355
        return ret;
×
356
    }
357

358
    if (401 == *status) {
113✔
359
        /* Unauthorized => try to re-authenticate and perform the request again */
360
        mender_log_info("Trying to re-authenticate");
×
361
        FREE_AND_NULL(api_jwt);
×
362
        if (MENDER_IS_ERROR(ret = ensure_authenticated_and_locked())) {
×
363
            mender_free(*response);
×
364
            ret = mender_http_perform(api_jwt, path, method, payload, signature, &mender_api_http_text_callback, response, status);
×
365
            if (MENDER_OK != mender_os_mutex_give(auth_lock)) {
×
366
                mender_log_error("Unable to release the authentication lock");
×
367
                return MENDER_FAIL;
×
368
            }
369
            if (MENDER_OK != ret) {
×
370
                /* HTTP errors already logged. */
371
                mender_err_count_net_inc();
×
372
            }
373
        } else if (MENDER_LOCK_FAILED != ret) {
×
374
            if (MENDER_OK != mender_os_mutex_give(auth_lock)) {
×
375
                mender_log_error("Unable to release the authentication lock");
×
376
                return MENDER_FAIL;
×
377
            }
378
        }
379
    }
380

381
    return ret;
113✔
382
}
383

384
static mender_err_t
385
api_check_for_deployment_v2(int *status, char **response) {
44✔
386
    assert(NULL != status);
44✔
387
    assert(NULL != response);
44✔
388

389
    mender_err_t ret           = MENDER_FAIL;
44✔
390
    cJSON       *json_payload  = NULL;
44✔
391
    char        *payload       = NULL;
44✔
392
    const char  *artifact_name = NULL;
44✔
393
#ifdef CONFIG_MENDER_PROVIDES_DEPENDS
394
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
395
    mender_key_value_list_t *provides = NULL;
44✔
396
#endif /* CONFIG_MENDER_FULL_PARSE_ARTIFACT */
397
#endif /* CONFIG_MENDER_PROVIDES_DEPENDS */
398

399
    /* Create payload */
400
    if (NULL == (json_payload = cJSON_CreateObject())) {
44✔
401
        mender_log_error("Unable to allocate memory");
×
402
        goto END;
×
403
    }
404

405
    /* Add "device_provides" entity to payload */
406
    cJSON *json_provides = NULL;
44✔
407
    if (NULL == (json_provides = cJSON_AddObjectToObject(json_payload, "device_provides"))) {
44✔
408
        mender_log_error("Unable to allocate memory");
×
409
        goto END;
×
410
    }
411

412
    if (NULL == cJSON_AddStringToObject(json_provides, "device_type", api_config.device_type)) {
44✔
413
        mender_log_error("Unable to allocate memory");
×
414
        goto END;
×
415
    }
416

417
#ifdef CONFIG_MENDER_PROVIDES_DEPENDS
418
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
419
    /* Add provides from storage */
420
    if (MENDER_FAIL == mender_storage_get_provides(&provides)) {
44✔
421
        mender_log_error("Unable to get provides");
×
422
        goto END;
×
423
    }
424
    for (mender_key_value_list_t *item = provides; NULL != item; item = item->next) {
49✔
425
        if (NULL == cJSON_AddStringToObject(json_provides, item->key, item->value)) {
5✔
426
            mender_log_error("Unable to allocate memory");
×
427
            goto END;
×
428
        }
429
    }
430
#endif /* CONFIG_MENDER_FULL_PARSE_ARTIFACT */
431
#endif /* CONFIG_MENDER_PROVIDES_DEPENDS */
432

433
    if ((MENDER_OK != mender_storage_get_artifact_name(&artifact_name)) && (NULL != artifact_name)) {
44✔
434
        mender_log_error("Unable to get artifact name");
×
435
        return MENDER_FAIL;
×
436
    }
437

438
    if (NULL == cJSON_AddStringToObject(json_provides, "artifact_name", artifact_name)) {
44✔
439
        mender_log_error("Unable to allocate memory");
×
440
        goto END;
×
441
    }
442

443
    if (NULL == (payload = cJSON_PrintUnformatted(json_payload))) {
44✔
444
        mender_log_error("Unable to allocate memory");
×
445
        goto END;
×
446
    }
447

448
    /* Perform HTTP request */
449
    if (MENDER_OK != (ret = authenticated_http_perform(MENDER_API_PATH_POST_NEXT_DEPLOYMENT_V2, MENDER_HTTP_POST, payload, NULL, response, status))) {
44✔
450
        mender_log_error("Unable to perform HTTP request");
×
451
        goto END;
×
452
    }
453

454
    ret = MENDER_OK;
32✔
455

456
END:
32✔
457

458
#ifdef CONFIG_MENDER_PROVIDES_DEPENDS
459
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
460
    mender_utils_key_value_list_free(provides);
32✔
461
#endif /* CONFIG_MENDER_FULL_PARSE_ARTIFACT */
462
#endif /* CONFIG_MENDER_PROVIDES_DEPENDS */
463
    cJSON_Delete(json_payload);
32✔
464
    mender_free(payload);
32✔
465
    return ret;
32✔
466
}
467

468
static mender_err_t
469
api_check_for_deployment_v1(int *status, char **response) {
×
470

471
    assert(NULL != status);
×
472
    assert(NULL != response);
×
473

474
    mender_err_t ret           = MENDER_FAIL;
×
475
    char        *path          = NULL;
×
476
    const char  *artifact_name = NULL;
×
477

478
    if ((MENDER_OK != mender_storage_get_artifact_name(&artifact_name)) && (NULL != artifact_name)) {
×
479
        mender_log_error("Unable to get artifact name");
×
480
        return MENDER_FAIL;
×
481
    }
482

483
    /* Compute path */
484
    if (-1 == mender_utils_asprintf(&path, MENDER_API_PATH_GET_NEXT_DEPLOYMENT "?artifact_name=%s&device_type=%s", artifact_name, api_config.device_type)) {
×
485
        mender_log_error("Unable to allocate memory");
×
486
        goto END;
×
487
    }
488

489
    /* Perform HTTP request */
490
    if (MENDER_OK != (ret = authenticated_http_perform(path, MENDER_HTTP_GET, NULL, NULL, response, status))) {
×
491
        mender_log_error("Unable to perform HTTP request");
×
492
        goto END;
×
493
    }
494

495
    ret = MENDER_OK;
×
496

497
END:
×
498

499
    /* Release memory */
500
    mender_free(path);
×
501

502
    return ret;
×
503
}
504

505
mender_err_t
506
mender_api_check_for_deployment(mender_api_deployment_data_t *deployment) {
44✔
507

508
    assert(NULL != deployment);
44✔
509
    mender_err_t ret      = MENDER_FAIL;
44✔
510
    char        *response = NULL;
44✔
511
    int          status   = 0;
44✔
512

513
    if (MENDER_FAIL == (ret = api_check_for_deployment_v2(&status, &response))) {
44✔
514
        goto END;
×
515
    }
516

517
    /* Yes, 404 still means MENDER_OK above */
518
    if (404 == status) {
32✔
519
        mender_log_debug("POST request to v2 version of the deployments API failed, falling back to v1 version and GET");
520
        FREE_AND_NULL(response);
×
521
        if (MENDER_FAIL == (ret = api_check_for_deployment_v1(&status, &response))) {
×
522
            goto END;
×
523
        }
524
    }
525

526
    /* Treatment depending of the status */
527
    if (200 == status) {
32✔
528
        cJSON *json_response = cJSON_Parse(response);
12✔
529
        if (NULL != json_response) {
12✔
530
            cJSON *json_id = cJSON_GetObjectItem(json_response, "id");
12✔
531
            if (NULL != json_id) {
12✔
532
                if (NULL == (deployment->id = mender_utils_strdup(cJSON_GetStringValue(json_id)))) {
12✔
533
                    ret = MENDER_FAIL;
×
534
                    goto END;
×
535
                }
536
            }
537
            cJSON *json_artifact = cJSON_GetObjectItem(json_response, "artifact");
12✔
538
            if (NULL != json_artifact) {
12✔
539
                cJSON *json_artifact_name = cJSON_GetObjectItem(json_artifact, "artifact_name");
12✔
540
                if (NULL != json_artifact_name) {
12✔
541
                    if (NULL == (deployment->artifact_name = mender_utils_strdup(cJSON_GetStringValue(json_artifact_name)))) {
12✔
542
                        ret = MENDER_FAIL;
×
543
                        goto END;
×
544
                    }
545
                }
546
                cJSON *json_source = cJSON_GetObjectItem(json_artifact, "source");
12✔
547
                if (NULL != json_source) {
12✔
548
                    cJSON *json_uri = cJSON_GetObjectItem(json_source, "uri");
12✔
549
                    if (NULL != json_uri) {
12✔
550
                        if (NULL == (deployment->uri = mender_utils_strdup(cJSON_GetStringValue(json_uri)))) {
12✔
551
                            ret = MENDER_FAIL;
×
552
                            goto END;
×
553
                        }
554
                        ret = MENDER_OK;
12✔
555
                    } else {
556
                        mender_log_error("Invalid response");
×
557
                        ret = MENDER_FAIL;
×
558
                    }
559
                } else {
560
                    mender_log_error("Invalid response");
×
561
                    ret = MENDER_FAIL;
×
562
                }
563
                cJSON *json_device_types_compatible = cJSON_GetObjectItem(json_artifact, "device_types_compatible");
12✔
564
                if (NULL != json_device_types_compatible && cJSON_IsArray(json_device_types_compatible)) {
12✔
565
                    deployment->device_types_compatible_size = cJSON_GetArraySize(json_device_types_compatible);
12✔
566
                    deployment->device_types_compatible      = (char **)mender_malloc(deployment->device_types_compatible_size * sizeof(char *));
12✔
567
                    if (NULL == deployment->device_types_compatible) {
12✔
568
                        mender_log_error("Unable to allocate memory");
×
569
                        ret = MENDER_FAIL;
×
570
                        goto END;
×
571
                    }
572
                    for (size_t i = 0; i < deployment->device_types_compatible_size; i++) {
24✔
573
                        cJSON *json_device_type = cJSON_GetArrayItem(json_device_types_compatible, i);
12✔
574
                        if (NULL != json_device_type && cJSON_IsString(json_device_type)) {
12✔
575
                            if (NULL == (deployment->device_types_compatible[i] = mender_utils_strdup(cJSON_GetStringValue(json_device_type)))) {
12✔
576
                                ret = MENDER_FAIL;
×
577
                                goto END;
×
578
                            }
579
                        } else {
580
                            mender_log_error("Could not get device type form device_types_compatible array");
×
581
                            ret = MENDER_FAIL;
×
582
                        }
583
                    }
584
                } else {
585
                    mender_log_error("Could not load device_types_compatible");
×
586
                    ret = MENDER_FAIL;
×
587
                }
588
            } else {
589
                mender_log_error("Invalid response");
×
590
                ret = MENDER_FAIL;
×
591
            }
592
            cJSON_Delete(json_response);
12✔
593
        } else {
594
            mender_log_error("Invalid response");
×
595
            ret = MENDER_FAIL;
×
596
        }
597
    } else if (204 == status) {
20✔
598
        /* No response expected */
599
        ret = MENDER_NOT_FOUND;
20✔
600
    } else {
601
        mender_api_print_response_error(response, status);
×
602
        ret = MENDER_RETRY_ERROR;
×
603
    }
604

605
END:
32✔
606

607
    /* Release memory */
608
    mender_free(response);
32✔
609

610
    return ret;
32✔
611
}
612

613
#ifdef CONFIG_MENDER_DEPLOYMENT_LOGS
614
static mender_err_t mender_api_publish_deployment_logs(const char *id);
615
#endif /* CONFIG_MENDER_DEPLOYMENT_LOGS */
616

617
mender_err_t
618
mender_api_publish_deployment_status(const char *id, mender_deployment_status_t deployment_status) {
47✔
619
    assert(NULL != id);
47✔
620

621
    mender_err_t ret;
622
    const char  *value        = NULL;
47✔
623
    cJSON       *json_payload = NULL;
47✔
624
    char        *payload      = NULL;
47✔
625
    char        *path         = NULL;
47✔
626
    char        *response     = NULL;
47✔
627
    int          status       = 0;
47✔
628

629
    /* Deployment status to string */
630
    if (NULL == (value = mender_utils_deployment_status_to_string(deployment_status))) {
47✔
631
        mender_log_error("Invalid status");
×
632
        ret = MENDER_FAIL;
×
633
        goto END;
×
634
    }
635

636
    /* Format payload */
637
    if (NULL == (json_payload = cJSON_CreateObject())) {
47✔
638
        mender_log_error("Unable to allocate memory");
×
639
        ret = MENDER_FAIL;
×
640
        goto END;
×
641
    }
642
    cJSON_AddStringToObject(json_payload, "status", value);
47✔
643
    if (NULL == (payload = cJSON_PrintUnformatted(json_payload))) {
47✔
644
        mender_log_error("Unable to allocate memory");
×
645
        ret = MENDER_FAIL;
×
646
        goto END;
×
647
    }
648

649
    /* Compute path */
650
    if (mender_utils_asprintf(&path, MENDER_API_PATH_PUT_DEPLOYMENT_STATUS, id) <= 0) {
47✔
651
        mender_log_error("Unable to allocate memory");
×
652
        ret = MENDER_FAIL;
×
653
        goto END;
×
654
    }
655

656
    /* Perform HTTP request */
657
    if (MENDER_OK != (ret = authenticated_http_perform(path, MENDER_HTTP_PUT, payload, NULL, &response, &status))) {
47✔
658
        mender_log_error("Unable to perform HTTP request");
×
659
        goto END;
×
660
    }
661

662
    /* Treatment depending of the status */
663
    if (204 == status) {
45✔
664
        /* No response expected */
665
        ret = MENDER_OK;
44✔
666
    } else if (409 == status) {
1✔
667
        /* Deployment aborted */
668
        mender_api_print_response_error(response, status);
1✔
669
        ret = MENDER_ABORTED;
1✔
670
    } else {
671
        mender_api_print_response_error(response, status);
×
672
        ret = MENDER_RETRY_ERROR;
×
673
    }
674

675
END:
45✔
676

677
    /* Release memory */
678
    mender_free(response);
45✔
679
    mender_free(path);
45✔
680
    mender_free(payload);
45✔
681
    cJSON_Delete(json_payload);
45✔
682

683
#ifdef CONFIG_MENDER_DEPLOYMENT_LOGS
684
    /* Do this after we have released memory above, potentially giving us some
685
       extra room we may need. */
686
    if ((MENDER_OK == ret) && (MENDER_DEPLOYMENT_STATUS_FAILURE == deployment_status)) {
45✔
687
        /* Successfully reported a deployment failure, upload deployment
688
           logs.  */
689
        if (MENDER_OK != mender_api_publish_deployment_logs(id)) {
9✔
690
            mender_log_error("Failed to publish deployment logs");
691
        }
692
    }
693
#endif /* CONFIG_MENDER_DEPLOYMENT_LOGS */
694

695
    return ret;
45✔
696
}
697

698
#ifdef CONFIG_MENDER_DEPLOYMENT_LOGS
699
static void
700
append_depl_log_msg(char *msg, void *ctx) {
17✔
701
    assert(NULL != ctx);
17✔
702

703
    char  *tstamp   = NULL;
17✔
704
    char  *level    = NULL;
17✔
705
    char  *log_msg  = NULL;
17✔
706
    cJSON *json_msg = NULL;
17✔
707
    cJSON *messages = ctx;
17✔
708

709
    /* Example log message we expect:
710
     *   "[00:39:06.746,000] <err> mender: Unable to perform HTTP request"
711
     * The code below goes through the string, searches for the expected parts and breaks the string
712
     * down accordingly.
713
     */
714

715
    /* Start by setting log_msg to the whole message. In case all of the
716
       break-down below fails, we send the whole message as the log message with
717
       no extra metadata. */
718
    log_msg = msg;
17✔
719

720
    char *c = msg;
17✔
721
    if ('[' == *c) {
17✔
722
        /* if it does start with a timestamp, like above, store the pointer and find its end */
723
        c++;
17✔
724
        tstamp = c;
17✔
725
        while (('\0' != *c) && (']' != *c)) {
476✔
726
            c++;
459✔
727
        }
728
        if ('\0' == *c) {
17✔
729
            goto DONE_PARSING;
730
        }
731
        *c = '\0';
17✔
732
        c++;
17✔
733
    }
734

735
    if (' ' == *c) {
17✔
736
        /* skip the space */
737
        c++;
17✔
738
    }
739

740
    if ('<' == *c) {
17✔
741
        /* if the log level follow, like above, store the pointer and find its end */
742
        c++;
17✔
743
        level = c;
17✔
744
        while (('\0' != *c) && ('>' != *c)) {
68✔
745
            c++;
51✔
746
        }
747
        if ('\0' == *c) {
17✔
748
            goto DONE_PARSING;
749
        }
750
        *c = '\0';
17✔
751
        c++;
17✔
752
    }
753

754
    if (' ' == *c) {
17✔
755
        /* skip the space */
756
        c++;
17✔
757
    }
758

759
    if ('\0' != *c) {
17✔
760
        log_msg = c;
17✔
761
        if (mender_utils_strbeginswith(log_msg, "mender: ")) {
17✔
762
            log_msg += strlen("mender: ");
17✔
763
        }
764
    }
765

766
DONE_PARSING:
767
    if (NULL == (json_msg = cJSON_CreateObject())) {
17✔
768
        mender_log_error("Unable to allocate memory");
769
        return;
770
    }
771

772
    if (NULL != tstamp) {
17✔
773
        if (NULL == cJSON_AddStringToObject(json_msg, "timestamp", tstamp)) {
17✔
774
            mender_log_error("Unable to allocate memory");
775
            goto END;
776
        }
777
    } else {
778
        if (NULL == cJSON_AddNullToObject(json_msg, "timestamp")) {
779
            mender_log_error("Unable to allocate memory");
780
            goto END;
781
        }
782
    }
783

784
    if (NULL != level) {
17✔
785
        if (NULL == cJSON_AddStringToObject(json_msg, "level", level)) {
17✔
786
            mender_log_error("Unable to allocate memory");
787
            goto END;
788
        }
789
    } else {
790
        if (NULL == cJSON_AddNullToObject(json_msg, "level")) {
791
            mender_log_error("Unable to allocate memory");
792
            goto END;
793
        }
794
    }
795

796
    if (NULL == cJSON_AddStringToObject(json_msg, "message", log_msg)) {
17✔
797
        mender_log_error("Unable to allocate memory");
798
        goto END;
799
    }
800

801
    if (!cJSON_AddItemToArray(messages, json_msg)) {
17✔
802
        mender_log_error("Unable to allocate memory");
803
    }
804
    json_msg = NULL;
17✔
805

806
END:
17✔
807
    cJSON_Delete(json_msg);
17✔
808
}
809

810
static mender_err_t
811
mender_api_publish_deployment_logs(const char *id) {
9✔
812
    assert(NULL != id);
9✔
813

814
    mender_err_t ret;
815
    cJSON       *json_payload  = NULL;
9✔
816
    cJSON       *json_messages = NULL;
9✔
817
    char        *payload       = NULL;
9✔
818
    char        *path          = NULL;
9✔
819
    char        *response      = NULL;
9✔
820
    int          status        = 0;
9✔
821

822
    /* Format payload */
823
    if (NULL == (json_payload = cJSON_CreateObject())) {
9✔
824
        mender_log_error("Unable to allocate memory");
825
        ret = MENDER_FAIL;
826
        goto END;
827
    }
828
    if (NULL == (json_messages = cJSON_AddArrayToObject(json_payload, "messages"))) {
9✔
829
        mender_log_error("Unable to allocate memory");
830
        ret = MENDER_FAIL;
831
        goto END;
832
    }
833

834
    if (MENDER_OK != (ret = mender_storage_deployment_log_walk(append_depl_log_msg, json_messages))) {
9✔
835
        mender_log_error("Failed to add deployment log messages to payload");
836
        ret = MENDER_FAIL;
837
        goto END;
838
    }
839

840
    if (0 == cJSON_GetArraySize(json_messages)) {
9✔
841
        /* Nothing to do, no logs to submit. */
842
        ret = MENDER_OK;
3✔
843
        goto END;
3✔
844
    }
845

846
    if (NULL == (payload = cJSON_PrintUnformatted(json_payload))) {
6✔
847
        mender_log_error("Unable to allocate memory");
848
        ret = MENDER_FAIL;
849
        goto END;
850
    }
851
    /* We no longer need the JSON now that we have the string representation so
852
       reclaim that (potentially big) chunk of memory). */
853
    DESTROY_AND_NULL(cJSON_Delete, json_payload);
6✔
854

855
    /* Perform HTTP request */
856
    if (mender_utils_asprintf(&path, MENDER_API_PATH_PUT_DEPLOYMENT_LOGS, id) <= 0) {
6✔
857
        mender_log_error("Unable to allocate memory");
858
        goto END;
859
    }
860

861
    mender_log_info("Publishing deployment logs");
6✔
862
    if (MENDER_OK != (ret = authenticated_http_perform(path, MENDER_HTTP_PUT, payload, NULL, &response, &status))) {
6✔
863
        mender_log_error("Unable to perform HTTP request");
864
        goto END;
865
    }
866

867
    /* Treatment depending of the status */
868
    if (204 == status) {
6✔
869
        /* No response expected */
870
        ret = MENDER_OK;
6✔
871
    } else if (409 == status) {
872
        /* Deployment aborted */
873
        mender_api_print_response_error(response, status);
874
        ret = MENDER_ABORTED;
875
    } else {
876
        mender_api_print_response_error(response, status);
877
        ret = MENDER_FAIL;
878
    }
879

880
END:
9✔
881

882
    /* Release memory */
883
    mender_free(response);
9✔
884
    mender_free(path);
9✔
885
    mender_free(payload);
9✔
886
    cJSON_Delete(json_payload);
9✔
887

888
    return ret;
9✔
889
}
890
#endif /* CONFIG_MENDER_DEPLOYMENT_LOGS */
891

892
#ifndef CONFIG_MENDER_CLIENT_INVENTORY_DISABLE
893

894
mender_err_t
895
mender_api_publish_inventory_data(cJSON *inventory, bool patch) {
30✔
896

897
    mender_err_t ret;
898
    char        *payload  = NULL;
30✔
899
    char        *response = NULL;
30✔
900
    int          status   = 0;
30✔
901

902
    /* Format payload */
903
    if (NULL == (payload = cJSON_PrintUnformatted(inventory))) {
30✔
904
        mender_log_error("Unable to allocate memory");
×
905
        ret = MENDER_FAIL;
×
906
        goto END;
×
907
    }
908

909
    /* Perform HTTP request */
910
    if (MENDER_OK
30✔
911
        != (ret = authenticated_http_perform(
30✔
912
                MENDER_API_PATH_PUT_DEVICE_ATTRIBUTES, patch ? MENDER_HTTP_PATCH : MENDER_HTTP_PUT, payload, NULL, &response, &status))) {
913
        mender_log_error("Unable to perform HTTP request");
×
914
        goto END;
×
915
    }
916

917
    /* Treatment depending of the status */
918
    if (200 == status) {
30✔
919
        /* No response expected */
920
        ret = MENDER_OK;
30✔
921
    } else {
922
        mender_api_print_response_error(response, status);
×
923
        ret = MENDER_RETRY_ERROR;
×
924
    }
925

926
END:
30✔
927

928
    /* Release memory */
929
    mender_free(response);
30✔
930
    mender_free(payload);
30✔
931
    cJSON_Delete(inventory);
30✔
932

933
    return ret;
30✔
934
}
935

936
#endif /* CONFIG_MENDER_CLIENT_INVENTORY_DISABLE */
937

938
mender_err_t
939
mender_api_exit(void) {
5✔
940

941
    /* Release all modules */
942
    mender_http_exit();
5✔
943

944
    /* Destroy the authentication lock */
945
    mender_os_mutex_delete(auth_lock);
5✔
946

947
    /* Release memory */
948
    FREE_AND_NULL(api_jwt);
5✔
949

950
    return MENDER_OK;
5✔
951
}
952

953
static mender_err_t
954
mender_api_http_text_callback(mender_http_client_event_t event, void *data, size_t data_length, void *params) {
409✔
955

956
    assert(NULL != params);
409✔
957
    char       **response = (char **)params;
409✔
958
    mender_err_t ret      = MENDER_OK;
409✔
959
    char        *tmp;
960

961
    /* Treatment depending of the event */
962
    switch (event) {
409✔
963
        case MENDER_HTTP_EVENT_CONNECTED:
142✔
964
            /* Nothing to do */
965
            break;
142✔
966
        case MENDER_HTTP_EVENT_DATA_RECEIVED:
125✔
967
            /* Check input data */
968
            if ((NULL == data) || (0 == data_length)) {
125✔
969
                mender_log_error("Invalid data received");
×
970
                ret = MENDER_FAIL;
×
971
                break;
×
972
            }
973
            /* Concatenate data to the response */
974
            size_t response_length = (NULL != *response) ? strlen(*response) : 0;
125✔
975
            if (NULL == (tmp = mender_realloc(*response, response_length + data_length + 1))) {
125✔
976
                mender_log_error("Unable to allocate memory");
×
977
                ret = MENDER_FAIL;
×
978
                break;
×
979
            }
980
            *response = tmp;
125✔
981
            memcpy((*response) + response_length, data, data_length);
125✔
982
            *((*response) + response_length + data_length) = '\0';
125✔
983
            break;
125✔
984
        case MENDER_HTTP_EVENT_DISCONNECTED:
142✔
985
            /* Nothing to do */
986
            break;
142✔
987
        case MENDER_HTTP_EVENT_ERROR:
×
988
            /* Downloading the response fails */
989
            mender_log_error("An error occurred");
×
990
            ret = MENDER_FAIL;
×
991
            break;
×
992
        default:
×
993
            /* Should no occur */
994
            ret = MENDER_FAIL;
×
995
            break;
×
996
    }
997

998
    return ret;
409✔
999
}
1000

1001
void
1002
mender_api_print_response_error(char *response, int status) {
2✔
1003
    const char *desc;
1004

1005
    /* Treatment depending of the status */
1006
    if (NULL != (desc = mender_utils_http_status_to_string(status))) {
2✔
1007
        if (NULL != response) {
1✔
1008
            cJSON *json_response = cJSON_Parse(response);
1✔
1009
            if (NULL != json_response) {
1✔
1010
                cJSON *json_error = cJSON_GetObjectItemCaseSensitive(json_response, "error");
1✔
1011
                if (NULL != json_error) {
1✔
1012
                    mender_log_error("[%d] %s: %s", status, desc, cJSON_GetStringValue(json_error));
1✔
1013
                } else {
1014
                    mender_log_error("[%d] %s: unknown error", status, desc);
×
1015
                }
1016
                cJSON_Delete(json_response);
1✔
1017
            } else {
1018
                mender_log_error("[%d] %s: unknown error", status, desc);
×
1019
            }
1020
        } else {
1021
            mender_log_error("[%d] %s: unknown error", status, desc);
×
1022
        }
1023
    } else {
1024
        mender_log_error("Unknown error occurred, status=%d", status);
1✔
1025
    }
1026
}
2✔
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

© 2025 Coveralls, Inc