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

mendersoftware / mender-mcu / 1746182773

01 Apr 2025 08:31AM UTC coverage: 56.771% (+56.8%) from 0.0%
1746182773

push

gitlab-ci

lluiscampos
chore: Make inventory "feature" hard to disable

While we want to keep the code around for potentially minimalistic
builds of the Client, we at Mender team consider it a core feature that
should not be opted out.

The middle point is to mark the disabling of the inventory as an
EXPERIMENTAL feature (we won't be testing it, after all) but keep the
code around for hackers or advance users to play with.

Ticket: MEN-7548

Signed-off-by: Lluis Campos <lluis.campos@northern.tech>

2117 of 3729 relevant lines covered (56.77%)

67.18 hits per line

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

57.7
/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 Paths of the mender-server APIs
34
 */
35
#define MENDER_API_PATH_POST_AUTHENTICATION_REQUESTS "/api/devices/v1/authentication/auth_requests"
36
#define MENDER_API_PATH_GET_NEXT_DEPLOYMENT          "/api/devices/v1/deployments/device/deployments/next"
37
#define MENDER_API_PATH_POST_NEXT_DEPLOYMENT_V2      "/api/devices/v2/deployments/device/deployments/next"
38
#define MENDER_API_PATH_PUT_DEPLOYMENT_STATUS        "/api/devices/v1/deployments/device/deployments/%s/status"
39
#define MENDER_API_PATH_GET_DEVICE_CONFIGURATION     "/api/devices/v1/deviceconfig/configuration"
40
#define MENDER_API_PATH_PUT_DEVICE_CONFIGURATION     "/api/devices/v1/deviceconfig/configuration"
41
#define MENDER_API_PATH_GET_DEVICE_CONNECT           "/api/devices/v1/deviceconnect/connect"
42
#define MENDER_API_PATH_PUT_DEVICE_ATTRIBUTES        "/api/devices/v1/inventory/device/attributes"
43

44
/**
45
 * @brief Mender API configuration
46
 */
47
static mender_api_config_t api_config;
48

49
/**
50
 * @brief Authentication token
51
 */
52
static char *api_jwt = NULL;
53

54
/**
55
 * @brief A mutex ensuring there are no concurrent operations using or updating the authentication token
56
 */
57
static void *auth_lock = NULL;
58

59
/**
60
 * @brief HTTP callback used to handle text content
61
 * @param event HTTP client event
62
 * @param data Data received
63
 * @param data_length Data length
64
 * @param params Callback parameters
65
 * @return MENDER_OK if the function succeeds, error code otherwise
66
 */
67
static mender_err_t mender_api_http_text_callback(mender_http_client_event_t event, void *data, size_t data_length, void *params);
68

69
/**
70
 * @brief Perform authentication of the device, retrieve token from mender-server used for the next requests
71
 * @return MENDER_OK if the function succeeds, error code otherwise
72
 */
73
static mender_err_t perform_authentication(void);
74

75
/**
76
 * @brief Ensure authenticated and holding the #auth_lock
77
 * @return MENDER_OK if success, MENDER_LOCK_FAILED in case of lock failure, other errors otherwise
78
 */
79
static mender_err_t ensure_authenticated_and_locked(void);
80

81
mender_err_t
82
mender_api_init(mender_api_config_t *config) {
29✔
83
    assert(NULL != config);
29✔
84
    assert(NULL != config->device_type);
29✔
85
    assert(NULL != config->host);
29✔
86
    assert(NULL != config->identity_cb);
29✔
87

88
    mender_err_t ret;
89

90
    /* Save configuration */
91
    memcpy(&api_config, config, sizeof(mender_api_config_t));
29✔
92

93
    /* Initializations */
94
    mender_http_config_t mender_http_config = { .host = api_config.host };
29✔
95
    if (MENDER_OK != (ret = mender_http_init(&mender_http_config))) {
29✔
96
        mender_log_error("Unable to initialize HTTP");
×
97
        return ret;
×
98
    }
99

100
    if (MENDER_OK != (ret = mender_os_mutex_create(&auth_lock))) {
29✔
101
        mender_log_error("Unable to initialize authentication lock");
×
102
        return ret;
×
103
    }
104

105
    return ret;
29✔
106
}
107

108
mender_err_t
109
mender_api_drop_authentication_data(void) {
6✔
110
    mender_err_t ret;
111
    if (MENDER_OK != (ret = mender_os_mutex_take(auth_lock, -1))) {
6✔
112
        mender_log_error("Unable to obtain the authentication lock");
×
113
        return MENDER_LOCK_FAILED;
×
114
    }
115
    FREE_AND_NULL(api_jwt);
6✔
116
    if (MENDER_OK != (ret = mender_os_mutex_give(auth_lock))) {
6✔
117
        mender_log_error("Unable to release the authentication lock");
×
118
    }
119

120
    return ret;
6✔
121
}
122

123
mender_err_t
124
mender_api_ensure_authenticated(void) {
6✔
125
    mender_err_t ret = ensure_authenticated_and_locked();
6✔
126
    if (MENDER_LOCK_FAILED == ret) {
5✔
127
        /* Error already logged. */
128
        return MENDER_FAIL;
×
129
    }
130
    bool authenticated = ((MENDER_OK == ret) || (MENDER_DONE == ret));
5✔
131

132
    if (MENDER_OK != (ret = mender_os_mutex_give(auth_lock))) {
5✔
133
        mender_log_error("Unable to release the authentication lock");
×
134
    }
135

136
    return authenticated ? ret : MENDER_FAIL;
5✔
137
}
138

139
static mender_err_t
140
ensure_authenticated_and_locked(void) {
116✔
141
    mender_err_t ret;
142

143
    if (MENDER_OK != (ret = mender_os_mutex_take(auth_lock, -1))) {
116✔
144
        mender_log_error("Unable to obtain the authentication lock");
×
145
        return MENDER_LOCK_FAILED;
×
146
    }
147

148
    if (NULL != api_jwt) {
116✔
149
        return MENDER_DONE;
82✔
150
    }
151

152
    /* Perform authentication with the mender server */
153
    if (MENDER_OK != (ret = perform_authentication())) {
34✔
154
        mender_log_error("Authentication failed");
6✔
155
        return MENDER_FAIL;
6✔
156
    } else {
157
        mender_log_debug("Authenticated successfully");
27✔
158
    }
159

160
    return ret;
27✔
161
}
162

163
static mender_err_t
164
perform_authentication(void) {
34✔
165
    mender_err_t             ret;
166
    char                    *public_key_pem   = NULL;
34✔
167
    const mender_identity_t *identity         = NULL;
34✔
168
    cJSON                   *json_identity    = NULL;
34✔
169
    char                    *identity_info    = NULL;
34✔
170
    cJSON                   *json_payload     = NULL;
34✔
171
    char                    *payload          = NULL;
34✔
172
    char                    *response         = NULL;
34✔
173
    char                    *signature        = NULL;
34✔
174
    size_t                   signature_length = 0;
34✔
175
    int                      status           = 0;
34✔
176

177
    /* Get public key in PEM format */
178
    if (MENDER_OK != (ret = mender_tls_get_public_key_pem(&public_key_pem))) {
34✔
179
        mender_log_error("Unable to get public key");
×
180
        goto END;
×
181
    }
182

183
    /* Get identity (we don't own the returned data) */
184
    if (MENDER_OK != (ret = api_config.identity_cb(&identity))) {
34✔
185
        mender_log_error("Unable to get identity");
×
186
        goto END;
×
187
    }
188

189
    /* Format identity */
190
    if (MENDER_OK != (ret = mender_utils_identity_to_json(identity, &json_identity))) {
34✔
191
        mender_log_error("Unable to format identity");
×
192
        goto END;
×
193
    }
194
    if (NULL == (identity_info = cJSON_PrintUnformatted(json_identity))) {
34✔
195
        mender_log_error("Unable to allocate memory");
×
196
        ret = MENDER_FAIL;
×
197
        goto END;
×
198
    }
199

200
    /* Format payload */
201
    if (NULL == (json_payload = cJSON_CreateObject())) {
34✔
202
        mender_log_error("Unable to allocate memory");
×
203
        ret = MENDER_FAIL;
×
204
        goto END;
×
205
    }
206
    cJSON_AddStringToObject(json_payload, "id_data", identity_info);
34✔
207
    cJSON_AddStringToObject(json_payload, "pubkey", public_key_pem);
34✔
208
    if (NULL != api_config.tenant_token) {
34✔
209
        cJSON_AddStringToObject(json_payload, "tenant_token", api_config.tenant_token);
34✔
210
    }
211
    if (NULL == (payload = cJSON_PrintUnformatted(json_payload))) {
34✔
212
        mender_log_error("Unable to allocate memory");
×
213
        ret = MENDER_FAIL;
×
214
        goto END;
×
215
    }
216

217
    /* Sign payload */
218
    if (MENDER_OK != (ret = mender_tls_sign_payload(payload, &signature, &signature_length))) {
34✔
219
        mender_log_error("Unable to sign payload");
×
220
        goto END;
×
221
    }
222

223
    /* Perform HTTP request */
224
    if (MENDER_OK
33✔
225
        != (ret = mender_http_perform(NULL,
34✔
226
                                      MENDER_API_PATH_POST_AUTHENTICATION_REQUESTS,
227
                                      MENDER_HTTP_POST,
228
                                      payload,
229
                                      signature,
230
                                      &mender_api_http_text_callback,
231
                                      (void *)&response,
232
                                      &status))) {
233
        mender_log_error("Unable to perform HTTP request");
6✔
234
        mender_err_count_net_inc();
6✔
235
        goto END;
6✔
236
    }
237

238
    /* Treatment depending of the status */
239
    if (200 == status) {
27✔
240
        if (NULL == response) {
27✔
241
            mender_log_error("Response is empty");
×
242
            ret = MENDER_FAIL;
×
243
            goto END;
×
244
        }
245
        if (NULL != api_jwt) {
27✔
246
            mender_free(api_jwt);
×
247
        }
248
        if (NULL == (api_jwt = mender_utils_strdup(response))) {
27✔
249
            mender_log_error("Unable to allocate memory");
×
250
            ret = MENDER_FAIL;
×
251
            goto END;
×
252
        }
253
        ret = MENDER_OK;
27✔
254
    } else {
255
        mender_api_print_response_error(response, status);
×
256
        /* Maybe the identity is wrong? Let's make sure we get fresh data for the next attempt. */
257
        FREE_AND_NULL(identity_info);
×
258
        ret = MENDER_FAIL;
×
259
    }
260

261
END:
33✔
262

263
    /* Release memory */
264
    mender_free(response);
33✔
265
    mender_free(signature);
33✔
266
    mender_free(payload);
33✔
267
    cJSON_Delete(json_payload);
33✔
268
    cJSON_Delete(json_identity);
33✔
269
    mender_free(identity_info);
33✔
270
    mender_free(public_key_pem);
33✔
271

272
    return ret;
33✔
273
}
274

275
/**
276
 * @see mender_http_perform()
277
 */
278
static mender_err_t
279
authenticated_http_perform(char *path, mender_http_method_t method, char *payload, char *signature, char **response, int *status) {
110✔
280
    mender_err_t ret;
281

282
    if (MENDER_IS_ERROR(ret = ensure_authenticated_and_locked())) {
110✔
283
        /* Errors already logged. */
284
        if (MENDER_LOCK_FAILED != ret) {
6✔
285
            if (MENDER_OK != mender_os_mutex_give(auth_lock)) {
6✔
286
                mender_log_error("Unable to release the authentication lock");
×
287
                return MENDER_FAIL;
×
288
            }
289
        }
290
        return ret;
6✔
291
    }
292

293
    ret = mender_http_perform(api_jwt, path, method, payload, signature, &mender_api_http_text_callback, response, status);
104✔
294
    if (MENDER_OK != mender_os_mutex_give(auth_lock)) {
90✔
295
        mender_log_error("Unable to release the authentication lock");
×
296
        return MENDER_FAIL;
×
297
    }
298
    if (MENDER_OK != ret) {
90✔
299
        /* HTTP errors already logged. */
300
        mender_err_count_net_inc();
1✔
301
        return ret;
1✔
302
    }
303

304
    if (401 == *status) {
89✔
305
        /* Unauthorized => try to re-authenticate and perform the request again */
306
        mender_log_info("Trying to re-authenticate");
×
307
        FREE_AND_NULL(api_jwt);
×
308
        if (MENDER_IS_ERROR(ret = ensure_authenticated_and_locked())) {
×
309
            mender_free(*response);
×
310
            ret = mender_http_perform(api_jwt, path, method, payload, signature, &mender_api_http_text_callback, response, status);
×
311
            if (MENDER_OK != mender_os_mutex_give(auth_lock)) {
×
312
                mender_log_error("Unable to release the authentication lock");
×
313
                return MENDER_FAIL;
×
314
            }
315
            if (MENDER_OK != ret) {
×
316
                /* HTTP errors already logged. */
317
                mender_err_count_net_inc();
×
318
            }
319
        } else if (MENDER_LOCK_FAILED != ret) {
×
320
            if (MENDER_OK != mender_os_mutex_give(auth_lock)) {
×
321
                mender_log_error("Unable to release the authentication lock");
×
322
                return MENDER_FAIL;
×
323
            }
324
        }
325
    }
326

327
    return ret;
89✔
328
}
329

330
static mender_err_t
331
api_check_for_deployment_v2(int *status, char **response) {
39✔
332
    assert(NULL != status);
39✔
333
    assert(NULL != response);
39✔
334

335
    mender_err_t ret           = MENDER_FAIL;
39✔
336
    cJSON       *json_payload  = NULL;
39✔
337
    char        *payload       = NULL;
39✔
338
    const char  *artifact_name = NULL;
39✔
339
#ifdef CONFIG_MENDER_PROVIDES_DEPENDS
340
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
341
    mender_key_value_list_t *provides = NULL;
39✔
342
#endif /* CONFIG_MENDER_FULL_PARSE_ARTIFACT */
343
#endif /* CONFIG_MENDER_PROVIDES_DEPENDS */
344

345
    /* Create payload */
346
    if (NULL == (json_payload = cJSON_CreateObject())) {
39✔
347
        mender_log_error("Unable to allocate memory");
×
348
        goto END;
×
349
    }
350

351
    /* Add "device_provides" entity to payload */
352
    cJSON *json_provides = NULL;
39✔
353
    if (NULL == (json_provides = cJSON_AddObjectToObject(json_payload, "device_provides"))) {
39✔
354
        mender_log_error("Unable to allocate memory");
×
355
        goto END;
×
356
    }
357

358
    if (NULL == cJSON_AddStringToObject(json_provides, "device_type", api_config.device_type)) {
39✔
359
        mender_log_error("Unable to allocate memory");
×
360
        goto END;
×
361
    }
362

363
#ifdef CONFIG_MENDER_PROVIDES_DEPENDS
364
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
365
    /* Add provides from storage */
366
    if (MENDER_FAIL == mender_storage_get_provides(&provides)) {
39✔
367
        mender_log_error("Unable to get provides");
×
368
        goto END;
×
369
    }
370
    for (mender_key_value_list_t *item = provides; NULL != item; item = item->next) {
44✔
371
        if (NULL == cJSON_AddStringToObject(json_provides, item->key, item->value)) {
5✔
372
            mender_log_error("Unable to allocate memory");
×
373
            goto END;
×
374
        }
375
    }
376
#endif /* CONFIG_MENDER_FULL_PARSE_ARTIFACT */
377
#endif /* CONFIG_MENDER_PROVIDES_DEPENDS */
378

379
    if ((MENDER_OK != mender_storage_get_artifact_name(&artifact_name)) && (NULL != artifact_name)) {
39✔
380
        mender_log_error("Unable to get artifact name");
×
381
        return MENDER_FAIL;
×
382
    }
383

384
    if (NULL == cJSON_AddStringToObject(json_provides, "artifact_name", artifact_name)) {
39✔
385
        mender_log_error("Unable to allocate memory");
×
386
        goto END;
×
387
    }
388

389
    if (NULL == (payload = cJSON_PrintUnformatted(json_payload))) {
39✔
390
        mender_log_error("Unable to allocate memory");
×
391
        goto END;
×
392
    }
393

394
    /* Perform HTTP request */
395
    if (MENDER_OK != (ret = authenticated_http_perform(MENDER_API_PATH_POST_NEXT_DEPLOYMENT_V2, MENDER_HTTP_POST, payload, NULL, response, status))) {
39✔
396
        mender_log_error("Unable to perform HTTP request");
×
397
        goto END;
×
398
    }
399

400
    ret = MENDER_OK;
27✔
401

402
END:
27✔
403

404
#ifdef CONFIG_MENDER_PROVIDES_DEPENDS
405
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
406
    mender_utils_key_value_list_free(provides);
27✔
407
#endif /* CONFIG_MENDER_FULL_PARSE_ARTIFACT */
408
#endif /* CONFIG_MENDER_PROVIDES_DEPENDS */
409
    cJSON_Delete(json_payload);
27✔
410
    mender_free(payload);
27✔
411
    return ret;
27✔
412
}
413

414
static mender_err_t
415
api_check_for_deployment_v1(int *status, char **response) {
×
416

417
    assert(NULL != status);
×
418
    assert(NULL != response);
×
419

420
    mender_err_t ret           = MENDER_FAIL;
×
421
    char        *path          = NULL;
×
422
    const char  *artifact_name = NULL;
×
423

424
    if ((MENDER_OK != mender_storage_get_artifact_name(&artifact_name)) && (NULL != artifact_name)) {
×
425
        mender_log_error("Unable to get artifact name");
×
426
        return MENDER_FAIL;
×
427
    }
428

429
    /* Compute path */
430
    if (-1 == mender_utils_asprintf(&path, MENDER_API_PATH_GET_NEXT_DEPLOYMENT "?artifact_name=%s&device_type=%s", artifact_name, api_config.device_type)) {
×
431
        mender_log_error("Unable to allocate memory");
×
432
        goto END;
×
433
    }
434

435
    /* Perform HTTP request */
436
    if (MENDER_OK != (ret = authenticated_http_perform(path, MENDER_HTTP_GET, NULL, NULL, response, status))) {
×
437
        mender_log_error("Unable to perform HTTP request");
×
438
        goto END;
×
439
    }
440

441
    ret = MENDER_OK;
×
442

443
END:
×
444

445
    /* Release memory */
446
    mender_free(path);
×
447

448
    return ret;
×
449
}
450

451
mender_err_t
452
mender_api_check_for_deployment(mender_api_deployment_data_t *deployment) {
39✔
453

454
    assert(NULL != deployment);
39✔
455
    mender_err_t ret      = MENDER_FAIL;
39✔
456
    char        *response = NULL;
39✔
457
    int          status   = 0;
39✔
458

459
    if (MENDER_FAIL == (ret = api_check_for_deployment_v2(&status, &response))) {
39✔
460
        goto END;
×
461
    }
462

463
    /* Yes, 404 still means MENDER_OK above */
464
    if (404 == status) {
27✔
465
        mender_log_debug("POST request to v2 version of the deployments API failed, falling back to v1 version and GET");
466
        FREE_AND_NULL(response);
×
467
        if (MENDER_FAIL == (ret = api_check_for_deployment_v1(&status, &response))) {
×
468
            goto END;
×
469
        }
470
    }
471

472
    /* Treatment depending of the status */
473
    if (200 == status) {
27✔
474
        cJSON *json_response = cJSON_Parse(response);
12✔
475
        if (NULL != json_response) {
12✔
476
            cJSON *json_id = cJSON_GetObjectItem(json_response, "id");
12✔
477
            if (NULL != json_id) {
12✔
478
                if (NULL == (deployment->id = mender_utils_strdup(cJSON_GetStringValue(json_id)))) {
12✔
479
                    ret = MENDER_FAIL;
×
480
                    goto END;
×
481
                }
482
            }
483
            cJSON *json_artifact = cJSON_GetObjectItem(json_response, "artifact");
12✔
484
            if (NULL != json_artifact) {
12✔
485
                cJSON *json_artifact_name = cJSON_GetObjectItem(json_artifact, "artifact_name");
12✔
486
                if (NULL != json_artifact_name) {
12✔
487
                    if (NULL == (deployment->artifact_name = mender_utils_strdup(cJSON_GetStringValue(json_artifact_name)))) {
12✔
488
                        ret = MENDER_FAIL;
×
489
                        goto END;
×
490
                    }
491
                }
492
                cJSON *json_source = cJSON_GetObjectItem(json_artifact, "source");
12✔
493
                if (NULL != json_source) {
12✔
494
                    cJSON *json_uri = cJSON_GetObjectItem(json_source, "uri");
12✔
495
                    if (NULL != json_uri) {
12✔
496
                        if (NULL == (deployment->uri = mender_utils_strdup(cJSON_GetStringValue(json_uri)))) {
12✔
497
                            ret = MENDER_FAIL;
×
498
                            goto END;
×
499
                        }
500
                        ret = MENDER_OK;
12✔
501
                    } else {
502
                        mender_log_error("Invalid response");
×
503
                        ret = MENDER_FAIL;
×
504
                    }
505
                } else {
506
                    mender_log_error("Invalid response");
×
507
                    ret = MENDER_FAIL;
×
508
                }
509
                cJSON *json_device_types_compatible = cJSON_GetObjectItem(json_artifact, "device_types_compatible");
12✔
510
                if (NULL != json_device_types_compatible && cJSON_IsArray(json_device_types_compatible)) {
12✔
511
                    deployment->device_types_compatible_size = cJSON_GetArraySize(json_device_types_compatible);
12✔
512
                    deployment->device_types_compatible      = (char **)mender_malloc(deployment->device_types_compatible_size * sizeof(char *));
12✔
513
                    if (NULL == deployment->device_types_compatible) {
12✔
514
                        mender_log_error("Unable to allocate memory");
×
515
                        ret = MENDER_FAIL;
×
516
                        goto END;
×
517
                    }
518
                    for (size_t i = 0; i < deployment->device_types_compatible_size; i++) {
24✔
519
                        cJSON *json_device_type = cJSON_GetArrayItem(json_device_types_compatible, i);
12✔
520
                        if (NULL != json_device_type && cJSON_IsString(json_device_type)) {
12✔
521
                            if (NULL == (deployment->device_types_compatible[i] = mender_utils_strdup(cJSON_GetStringValue(json_device_type)))) {
12✔
522
                                ret = MENDER_FAIL;
×
523
                                goto END;
×
524
                            }
525
                        } else {
526
                            mender_log_error("Could not get device type form device_types_compatible array");
×
527
                            ret = MENDER_FAIL;
×
528
                        }
529
                    }
530
                } else {
531
                    mender_log_error("Could not load device_types_compatible");
×
532
                    ret = MENDER_FAIL;
×
533
                }
534
            } else {
535
                mender_log_error("Invalid response");
×
536
                ret = MENDER_FAIL;
×
537
            }
538
            cJSON_Delete(json_response);
12✔
539
        } else {
540
            mender_log_error("Invalid response");
×
541
            ret = MENDER_FAIL;
×
542
        }
543
    } else if (204 == status) {
15✔
544
        /* No response expected */
545
        ret = MENDER_NOT_FOUND;
15✔
546
    } else {
547
        mender_api_print_response_error(response, status);
×
548
        ret = MENDER_FAIL;
×
549
    }
550

551
END:
27✔
552

553
    /* Release memory */
554
    mender_free(response);
27✔
555

556
    return ret;
27✔
557
}
558

559
mender_err_t
560
mender_api_publish_deployment_status(const char *id, mender_deployment_status_t deployment_status) {
42✔
561
    assert(NULL != id);
42✔
562

563
    mender_err_t ret;
564
    const char  *value        = NULL;
42✔
565
    cJSON       *json_payload = NULL;
42✔
566
    char        *payload      = NULL;
42✔
567
    char        *path         = NULL;
42✔
568
    char        *response     = NULL;
42✔
569
    int          status       = 0;
42✔
570

571
    /* Deployment status to string */
572
    if (NULL == (value = mender_utils_deployment_status_to_string(deployment_status))) {
42✔
573
        mender_log_error("Invalid status");
×
574
        ret = MENDER_FAIL;
×
575
        goto END;
×
576
    }
577

578
    /* Format payload */
579
    if (NULL == (json_payload = cJSON_CreateObject())) {
42✔
580
        mender_log_error("Unable to allocate memory");
×
581
        ret = MENDER_FAIL;
×
582
        goto END;
×
583
    }
584
    cJSON_AddStringToObject(json_payload, "status", value);
42✔
585
    if (NULL == (payload = cJSON_PrintUnformatted(json_payload))) {
42✔
586
        mender_log_error("Unable to allocate memory");
×
587
        ret = MENDER_FAIL;
×
588
        goto END;
×
589
    }
590

591
    /* Compute path */
592
    size_t str_length = strlen(MENDER_API_PATH_PUT_DEPLOYMENT_STATUS) - strlen("%s") + strlen(id) + 1;
42✔
593
    if (NULL == (path = (char *)mender_malloc(str_length))) {
42✔
594
        mender_log_error("Unable to allocate memory");
×
595
        ret = MENDER_FAIL;
×
596
        goto END;
×
597
    }
598
    snprintf(path, str_length, MENDER_API_PATH_PUT_DEPLOYMENT_STATUS, id);
42✔
599

600
    /* Perform HTTP request */
601
    if (MENDER_OK != (ret = authenticated_http_perform(path, MENDER_HTTP_PUT, payload, NULL, &response, &status))) {
42✔
602
        mender_log_error("Unable to perform HTTP request");
4✔
603
        goto END;
4✔
604
    }
605

606
    /* Treatment depending of the status */
607
    if (204 == status) {
36✔
608
        /* No response expected */
609
        ret = MENDER_OK;
35✔
610
    } else if (409 == status) {
1✔
611
        /* Deployment aborted */
612
        mender_api_print_response_error(response, status);
1✔
613
        ret = MENDER_ABORTED;
1✔
614
    } else {
615
        mender_api_print_response_error(response, status);
×
616
        ret = MENDER_FAIL;
×
617
    }
618

619
END:
40✔
620

621
    /* Release memory */
622
    mender_free(response);
40✔
623
    mender_free(path);
40✔
624
    mender_free(payload);
40✔
625
    cJSON_Delete(json_payload);
40✔
626

627
    return ret;
40✔
628
}
629

630
#ifndef CONFIG_MENDER_CLIENT_INVENTORY_DISABLE
631

632
mender_err_t
633
mender_api_publish_inventory_data(cJSON *inventory, bool patch) {
29✔
634

635
    mender_err_t ret;
636
    char        *payload  = NULL;
29✔
637
    char        *response = NULL;
29✔
638
    int          status   = 0;
29✔
639

640
    /* Format payload */
641
    if (NULL == (payload = cJSON_PrintUnformatted(inventory))) {
29✔
642
        mender_log_error("Unable to allocate memory");
×
643
        ret = MENDER_FAIL;
×
644
        goto END;
×
645
    }
646

647
    /* Perform HTTP request */
648
    if (MENDER_OK
29✔
649
        != (ret = authenticated_http_perform(
29✔
650
                MENDER_API_PATH_PUT_DEVICE_ATTRIBUTES, patch ? MENDER_HTTP_PATCH : MENDER_HTTP_PUT, payload, NULL, &response, &status))) {
651
        mender_log_error("Unable to perform HTTP request");
3✔
652
        goto END;
3✔
653
    }
654

655
    /* Treatment depending of the status */
656
    if (200 == status) {
26✔
657
        /* No response expected */
658
        ret = MENDER_OK;
26✔
659
    } else {
660
        mender_api_print_response_error(response, status);
×
661
        ret = MENDER_FAIL;
×
662
    }
663

664
END:
29✔
665

666
    /* Release memory */
667
    mender_free(response);
29✔
668
    mender_free(payload);
29✔
669
    cJSON_Delete(inventory);
29✔
670

671
    return ret;
29✔
672
}
673

674
#endif /* CONFIG_MENDER_CLIENT_INVENTORY_DISABLE */
675

676
mender_err_t
677
mender_api_exit(void) {
×
678

679
    /* Release all modules */
680
    mender_http_exit();
×
681

682
    /* Destroy the authentication lock */
683
    mender_os_mutex_delete(auth_lock);
×
684

685
    /* Release memory */
686
    FREE_AND_NULL(api_jwt);
×
687

688
    return MENDER_OK;
×
689
}
690

691
static mender_err_t
692
mender_api_http_text_callback(mender_http_client_event_t event, void *data, size_t data_length, void *params) {
323✔
693

694
    assert(NULL != params);
323✔
695
    char       **response = (char **)params;
323✔
696
    mender_err_t ret      = MENDER_OK;
323✔
697
    char        *tmp;
698

699
    /* Treatment depending of the event */
700
    switch (event) {
323✔
701
        case MENDER_HTTP_EVENT_CONNECTED:
116✔
702
            /* Nothing to do */
703
            break;
116✔
704
        case MENDER_HTTP_EVENT_DATA_RECEIVED:
91✔
705
            /* Check input data */
706
            if ((NULL == data) || (0 == data_length)) {
91✔
707
                mender_log_error("Invalid data received");
×
708
                ret = MENDER_FAIL;
×
709
                break;
×
710
            }
711
            /* Concatenate data to the response */
712
            size_t response_length = (NULL != *response) ? strlen(*response) : 0;
91✔
713
            if (NULL == (tmp = mender_realloc(*response, response_length + data_length + 1))) {
91✔
714
                mender_log_error("Unable to allocate memory");
×
715
                ret = MENDER_FAIL;
×
716
                break;
×
717
            }
718
            *response = tmp;
91✔
719
            memcpy((*response) + response_length, data, data_length);
91✔
720
            *((*response) + response_length + data_length) = '\0';
91✔
721
            break;
91✔
722
        case MENDER_HTTP_EVENT_DISCONNECTED:
116✔
723
            /* Nothing to do */
724
            break;
116✔
725
        case MENDER_HTTP_EVENT_ERROR:
×
726
            /* Downloading the response fails */
727
            mender_log_error("An error occurred");
×
728
            ret = MENDER_FAIL;
×
729
            break;
×
730
        default:
×
731
            /* Should no occur */
732
            ret = MENDER_FAIL;
×
733
            break;
×
734
    }
735

736
    return ret;
323✔
737
}
738

739
void
740
mender_api_print_response_error(char *response, int status) {
2✔
741
    const char *desc;
742

743
    /* Treatment depending of the status */
744
    if (NULL != (desc = mender_utils_http_status_to_string(status))) {
2✔
745
        if (NULL != response) {
1✔
746
            cJSON *json_response = cJSON_Parse(response);
1✔
747
            if (NULL != json_response) {
1✔
748
                cJSON *json_error = cJSON_GetObjectItemCaseSensitive(json_response, "error");
1✔
749
                if (NULL != json_error) {
1✔
750
                    mender_log_error("[%d] %s: %s", status, desc, cJSON_GetStringValue(json_error));
1✔
751
                } else {
752
                    mender_log_error("[%d] %s: unknown error", status, desc);
×
753
                }
754
                cJSON_Delete(json_response);
1✔
755
            } else {
756
                mender_log_error("[%d] %s: unknown error", status, desc);
×
757
            }
758
        } else {
759
            mender_log_error("[%d] %s: unknown error", status, desc);
×
760
        }
761
    } else {
762
        mender_log_error("Unknown error occurred, status=%d", status);
1✔
763
    }
764
}
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

© 2026 Coveralls, Inc