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

mendersoftware / mender-mcu / 1728062913

21 Mar 2025 10:30AM UTC coverage: 56.872% (-0.05%) from 56.919%
1728062913

push

gitlab-ci

lluiscampos
fix: Remove duplicated `Host` HTTP header on HTTP requests

Zephyr OS HTTP client code already adds its own `Host:` HTTP header on
the requests, which results in our HTTP requests having a duplicated
header.

See:
* https://github.com/zephyrproject-rtos/zephyr/blob/v4.0.0/subsys/net/lib/http/http_client.c#L595

While some servers accept this (for example hosted Mender API Gateway)
others error with 400 Bad request. In other words it is an invalid
behaviour tolerated in some servers, so better to just clean it up.

Ticket: MEN-8203
Changelog: None

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

2102 of 3696 relevant lines covered (56.87%)

67.43 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) {
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

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) {
116✔
140
    mender_err_t ret;
141

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

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

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

159
    return ret;
26✔
160
}
161

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

176
    /* Get public key in PEM format */
177
    if (MENDER_OK != (ret = mender_tls_get_public_key_pem(&public_key_pem))) {
33✔
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))) {
33✔
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))) {
33✔
190
        mender_log_error("Unable to format identity");
×
191
        goto END;
×
192
    }
193
    if (NULL == (identity_info = cJSON_PrintUnformatted(json_identity))) {
33✔
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())) {
33✔
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);
33✔
206
    cJSON_AddStringToObject(json_payload, "pubkey", public_key_pem);
33✔
207
    if (NULL != api_config.tenant_token) {
33✔
208
        cJSON_AddStringToObject(json_payload, "tenant_token", api_config.tenant_token);
33✔
209
    }
210
    if (NULL == (payload = cJSON_PrintUnformatted(json_payload))) {
33✔
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))) {
33✔
218
        mender_log_error("Unable to sign payload");
×
219
        goto END;
×
220
    }
221

222
    /* Perform HTTP request */
223
    if (MENDER_OK
32✔
224
        != (ret = mender_http_perform(NULL,
33✔
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");
6✔
233
        mender_err_count_net_inc();
6✔
234
        goto END;
6✔
235
    }
236

237
    /* Treatment depending of the status */
238
    if (200 == status) {
26✔
239
        if (NULL == response) {
26✔
240
            mender_log_error("Response is empty");
×
241
            ret = MENDER_FAIL;
×
242
            goto END;
×
243
        }
244
        if (NULL != api_jwt) {
26✔
245
            mender_free(api_jwt);
×
246
        }
247
        if (NULL == (api_jwt = mender_utils_strdup(response))) {
26✔
248
            mender_log_error("Unable to allocate memory");
×
249
            ret = MENDER_FAIL;
×
250
            goto END;
×
251
        }
252
        ret = MENDER_OK;
26✔
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:
32✔
261

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

271
    return ret;
32✔
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) {
110✔
279
    mender_err_t ret;
280

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

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

303
    if (401 == *status) {
90✔
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;
90✔
327
}
328

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

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

344
    /* Create payload */
345
    if (NULL == (json_payload = cJSON_CreateObject())) {
39✔
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;
39✔
352
    if (NULL == (json_provides = cJSON_AddObjectToObject(json_payload, "device_provides"))) {
39✔
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)) {
39✔
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)) {
39✔
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) {
44✔
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)) {
39✔
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)) {
39✔
384
        mender_log_error("Unable to allocate memory");
×
385
        goto END;
×
386
    }
387

388
    if (NULL == (payload = cJSON_PrintUnformatted(json_payload))) {
39✔
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))) {
39✔
395
        mender_log_error("Unable to perform HTTP request");
×
396
        goto END;
×
397
    }
398

399
    ret = MENDER_OK;
27✔
400

401
END:
27✔
402

403
#ifdef CONFIG_MENDER_PROVIDES_DEPENDS
404
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
405
    mender_utils_key_value_list_free(provides);
27✔
406
#endif /* CONFIG_MENDER_FULL_PARSE_ARTIFACT */
407
#endif /* CONFIG_MENDER_PROVIDES_DEPENDS */
408
    cJSON_Delete(json_payload);
27✔
409
    mender_free(payload);
27✔
410
    return ret;
27✔
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) {
39✔
452

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

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

462
    /* Yes, 404 still means MENDER_OK above */
463
    if (404 == status) {
27✔
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) {
27✔
473
        cJSON *json_response = cJSON_Parse(response);
12✔
474
        if (NULL != json_response) {
12✔
475
            cJSON *json_id = cJSON_GetObjectItem(json_response, "id");
12✔
476
            if (NULL != json_id) {
12✔
477
                if (NULL == (deployment->id = mender_utils_strdup(cJSON_GetStringValue(json_id)))) {
12✔
478
                    ret = MENDER_FAIL;
×
479
                    goto END;
×
480
                }
481
            }
482
            cJSON *json_artifact = cJSON_GetObjectItem(json_response, "artifact");
12✔
483
            if (NULL != json_artifact) {
12✔
484
                cJSON *json_artifact_name = cJSON_GetObjectItem(json_artifact, "artifact_name");
12✔
485
                if (NULL != json_artifact_name) {
12✔
486
                    if (NULL == (deployment->artifact_name = mender_utils_strdup(cJSON_GetStringValue(json_artifact_name)))) {
12✔
487
                        ret = MENDER_FAIL;
×
488
                        goto END;
×
489
                    }
490
                }
491
                cJSON *json_source = cJSON_GetObjectItem(json_artifact, "source");
12✔
492
                if (NULL != json_source) {
12✔
493
                    cJSON *json_uri = cJSON_GetObjectItem(json_source, "uri");
12✔
494
                    if (NULL != json_uri) {
12✔
495
                        if (NULL == (deployment->uri = mender_utils_strdup(cJSON_GetStringValue(json_uri)))) {
12✔
496
                            ret = MENDER_FAIL;
×
497
                            goto END;
×
498
                        }
499
                        ret = MENDER_OK;
12✔
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");
12✔
509
                if (NULL != json_device_types_compatible && cJSON_IsArray(json_device_types_compatible)) {
12✔
510
                    deployment->device_types_compatible_size = cJSON_GetArraySize(json_device_types_compatible);
12✔
511
                    deployment->device_types_compatible      = (char **)mender_malloc(deployment->device_types_compatible_size * sizeof(char *));
12✔
512
                    if (NULL == deployment->device_types_compatible) {
12✔
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++) {
24✔
518
                        cJSON *json_device_type = cJSON_GetArrayItem(json_device_types_compatible, i);
12✔
519
                        if (NULL != json_device_type && cJSON_IsString(json_device_type)) {
12✔
520
                            if (NULL == (deployment->device_types_compatible[i] = mender_utils_strdup(cJSON_GetStringValue(json_device_type)))) {
12✔
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);
12✔
538
        } else {
539
            mender_log_error("Invalid response");
×
540
            ret = MENDER_FAIL;
×
541
        }
542
    } else if (204 == status) {
15✔
543
        /* No response expected */
544
        ret = MENDER_NOT_FOUND;
15✔
545
    } else {
546
        mender_api_print_response_error(response, status);
×
547
        ret = MENDER_FAIL;
×
548
    }
549

550
END:
27✔
551

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

555
    return ret;
27✔
556
}
557

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

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

570
    /* Deployment status to string */
571
    if (NULL == (value = mender_utils_deployment_status_to_string(deployment_status))) {
42✔
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())) {
42✔
579
        mender_log_error("Unable to allocate memory");
×
580
        ret = MENDER_FAIL;
×
581
        goto END;
×
582
    }
583
    cJSON_AddStringToObject(json_payload, "status", value);
42✔
584
    if (NULL == (payload = cJSON_PrintUnformatted(json_payload))) {
42✔
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;
42✔
592
    if (NULL == (path = (char *)mender_malloc(str_length))) {
42✔
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);
42✔
598

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

605
    /* Treatment depending of the status */
606
    if (204 == status) {
37✔
607
        /* No response expected */
608
        ret = MENDER_OK;
36✔
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:
40✔
619

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

626
    return ret;
40✔
627
}
628

629
#ifdef CONFIG_MENDER_CLIENT_INVENTORY
630

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

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

639
    /* Format payload */
640
    if (NULL == (payload = cJSON_PrintUnformatted(inventory))) {
29✔
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
29✔
648
        != (ret = authenticated_http_perform(
29✔
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) {
26✔
656
        /* No response expected */
657
        ret = MENDER_OK;
26✔
658
    } else {
659
        mender_api_print_response_error(response, status);
660
        ret = MENDER_FAIL;
661
    }
662

663
END:
29✔
664

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

670
    return ret;
29✔
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) {
321✔
692

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

698
    /* Treatment depending of the event */
699
    switch (event) {
321✔
700
        case MENDER_HTTP_EVENT_CONNECTED:
116✔
701
            /* Nothing to do */
702
            break;
116✔
703
        case MENDER_HTTP_EVENT_DATA_RECEIVED:
89✔
704
            /* Check input data */
705
            if ((NULL == data) || (0 == data_length)) {
89✔
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;
89✔
712
            if (NULL == (tmp = mender_realloc(*response, response_length + data_length + 1))) {
89✔
713
                mender_log_error("Unable to allocate memory");
×
714
                ret = MENDER_FAIL;
×
715
                break;
×
716
            }
717
            *response = tmp;
89✔
718
            memcpy((*response) + response_length, data, data_length);
89✔
719
            *((*response) + response_length + data_length) = '\0';
89✔
720
            break;
89✔
721
        case MENDER_HTTP_EVENT_DISCONNECTED:
116✔
722
            /* Nothing to do */
723
            break;
116✔
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;
321✔
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