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

mendersoftware / mender-mcu / 1722225786

18 Mar 2025 11:43AM UTC coverage: 56.733% (-0.07%) from 56.802%
1722225786

push

gitlab-ci

web-flow
Merge pull request #175 from danielskinstad/spontaneous_reboot

chore: fail deployment on spontaneous reboot

5 of 9 new or added lines in 1 file covered. (55.56%)

2 existing lines in 1 file now uncovered.

2098 of 3698 relevant lines covered (56.73%)

58.66 hits per line

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

57.82
/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) {
23✔
83
    assert(NULL != config);
23✔
84
    assert(NULL != config->device_type);
23✔
85
    assert(NULL != config->host);
23✔
86
    assert(NULL != config->identity_cb);
23✔
87

88
    mender_err_t ret;
89

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

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

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

105
    return ret;
23✔
106
}
107

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

120
    return ret;
5✔
121
}
122

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

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

135
    return ret;
5✔
136
}
137

138
static mender_err_t
139
ensure_authenticated_and_locked(void) {
97✔
140
    mender_err_t ret;
141

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

147
    if (NULL != api_jwt) {
97✔
148
        return MENDER_DONE;
71✔
149
    }
150

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

159
    return ret;
21✔
160
}
161

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

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

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

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

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

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

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

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

260
END:
26✔
261

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

271
    return ret;
26✔
272
}
273

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

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

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

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

326
    return ret;
76✔
327
}
328

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

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

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

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

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

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

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

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

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

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

399
    ret = MENDER_OK;
23✔
400

401
END:
23✔
402

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

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

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

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

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

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

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

440
    ret = MENDER_OK;
×
441

442
END:
×
443

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

447
    return ret;
×
448
}
449

450
mender_err_t
451
mender_api_check_for_deployment(mender_api_deployment_data_t *deployment) {
33✔
452

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

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

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

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

550
END:
23✔
551

552
    /* Release memory */
553
    mender_free(response);
23✔
554

555
    return ret;
23✔
556
}
557

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

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

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

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

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

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

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

618
END:
34✔
619

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

626
    return ret;
34✔
627
}
628

629
#ifdef CONFIG_MENDER_CLIENT_INVENTORY
630

631
mender_err_t
632
mender_api_publish_inventory_data(cJSON *inventory, bool patch) {
24✔
633

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

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

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

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

663
END:
24✔
664

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

670
    return ret;
24✔
671
}
672

673
#endif /* CONFIG_MENDER_CLIENT_INVENTORY */
674

675
mender_err_t
676
mender_api_exit(void) {
×
677

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

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

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

687
    return MENDER_OK;
×
688
}
689

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

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

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

735
    return ret;
267✔
736
}
737

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

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