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

mendersoftware / mender-mcu / 1744429624

31 Mar 2025 10:22AM UTC coverage: 57.657% (-0.03%) from 57.689%
1744429624

push

gitlab-ci

vpodzime
feat: Submit deployment logs for failed deployments

Ticket: MEN-7626
Changelog: none
Signed-off-by: Vratislav Podzimek <vratislav.podzimek+auto-signed@northern.tech>

66 of 66 new or added lines in 1 file covered. (100.0%)

172 existing lines in 2 files now uncovered.

2244 of 3892 relevant lines covered (57.66%)

73.49 hits per line

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

64.19
/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_PUT_DEPLOYMENT_LOGS          "/api/devices/v1/deployments/device/deployments/%s/log"
40
#define MENDER_API_PATH_GET_DEVICE_CONFIGURATION     "/api/devices/v1/deviceconfig/configuration"
41
#define MENDER_API_PATH_PUT_DEVICE_CONFIGURATION     "/api/devices/v1/deviceconfig/configuration"
42
#define MENDER_API_PATH_GET_DEVICE_CONNECT           "/api/devices/v1/deviceconnect/connect"
43
#define MENDER_API_PATH_PUT_DEVICE_ATTRIBUTES        "/api/devices/v1/inventory/device/attributes"
44

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

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

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

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

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

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

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

89
    mender_err_t ret;
90

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

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

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

106
    return ret;
29✔
107
}
108

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

121
    return ret;
6✔
122
}
123

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

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

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

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

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

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

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

161
    return ret;
26✔
162
}
163

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

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

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

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

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

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

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

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

262
END:
32✔
263

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

273
    return ret;
32✔
274
}
275

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

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

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

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

328
    return ret;
91✔
329
}
330

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

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

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

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

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

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

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

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

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

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

401
    ret = MENDER_OK;
28✔
402

403
END:
28✔
404

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

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

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

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

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

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

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

UNCOV
442
    ret = MENDER_OK;
×
443

UNCOV
444
END:
×
445

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

UNCOV
449
    return ret;
×
450
}
451

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

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

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

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

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

552
END:
28✔
553

554
    /* Release memory */
555
    mender_free(response);
28✔
556

557
    return ret;
28✔
558
}
559

560
#ifdef CONFIG_MENDER_DEPLOYMENT_LOGS
561
static mender_err_t mender_api_publish_deployment_logs(const char *id);
562
#endif /* CONFIG_MENDER_DEPLOYMENT_LOGS */
563

564
mender_err_t
565
mender_api_publish_deployment_status(const char *id, mender_deployment_status_t deployment_status) {
42✔
566
    assert(NULL != id);
42✔
567

568
    mender_err_t ret;
569
    const char  *value        = NULL;
42✔
570
    cJSON       *json_payload = NULL;
42✔
571
    char        *payload      = NULL;
42✔
572
    char        *path         = NULL;
42✔
573
    char        *response     = NULL;
42✔
574
    int          status       = 0;
42✔
575

576
    /* Deployment status to string */
577
    if (NULL == (value = mender_utils_deployment_status_to_string(deployment_status))) {
42✔
UNCOV
578
        mender_log_error("Invalid status");
×
UNCOV
579
        ret = MENDER_FAIL;
×
UNCOV
580
        goto END;
×
581
    }
582

583
    /* Format payload */
584
    if (NULL == (json_payload = cJSON_CreateObject())) {
42✔
585
        mender_log_error("Unable to allocate memory");
×
UNCOV
586
        ret = MENDER_FAIL;
×
UNCOV
587
        goto END;
×
588
    }
589
    cJSON_AddStringToObject(json_payload, "status", value);
42✔
590
    if (NULL == (payload = cJSON_PrintUnformatted(json_payload))) {
42✔
591
        mender_log_error("Unable to allocate memory");
×
592
        ret = MENDER_FAIL;
×
UNCOV
593
        goto END;
×
594
    }
595

596
    /* Compute path */
597
    size_t str_length = strlen(MENDER_API_PATH_PUT_DEPLOYMENT_STATUS) - strlen("%s") + strlen(id) + 1;
42✔
598
    if (NULL == (path = (char *)mender_malloc(str_length))) {
42✔
UNCOV
599
        mender_log_error("Unable to allocate memory");
×
UNCOV
600
        ret = MENDER_FAIL;
×
UNCOV
601
        goto END;
×
602
    }
603
    snprintf(path, str_length, MENDER_API_PATH_PUT_DEPLOYMENT_STATUS, id);
42✔
604

605
    /* Perform HTTP request */
606
    if (MENDER_OK != (ret = authenticated_http_perform(path, MENDER_HTTP_PUT, payload, NULL, &response, &status))) {
42✔
607
        mender_log_error("Unable to perform HTTP request");
3✔
608
        goto END;
3✔
609
    }
610

611
    /* Treatment depending of the status */
612
    if (204 == status) {
37✔
613
        /* No response expected */
614
        ret = MENDER_OK;
36✔
615
    } else if (409 == status) {
1✔
616
        /* Deployment aborted */
617
        mender_api_print_response_error(response, status);
1✔
618
        ret = MENDER_ABORTED;
1✔
619
    } else {
UNCOV
620
        mender_api_print_response_error(response, status);
×
UNCOV
621
        ret = MENDER_FAIL;
×
622
    }
623

624
END:
40✔
625

626
    /* Release memory */
627
    mender_free(response);
40✔
628
    mender_free(path);
40✔
629
    mender_free(payload);
40✔
630
    cJSON_Delete(json_payload);
40✔
631

632
#ifdef CONFIG_MENDER_DEPLOYMENT_LOGS
633
    /* Do this after we have released memory above, potentially giving us some
634
       extra room we may need. */
635
    if ((MENDER_OK == ret) && (MENDER_DEPLOYMENT_STATUS_FAILURE == deployment_status)) {
40✔
636
        /* Successfully reported a deployment failure, upload deployment
637
           logs.  */
638
        if (MENDER_OK != mender_api_publish_deployment_logs(id)) {
6✔
639
            mender_log_error("Failed to publish deployment logs");
6✔
640
        }
641
    }
642
#endif /* CONFIG_MENDER_DEPLOYMENT_LOGS */
643

644
    return ret;
40✔
645
}
646

647
#ifdef CONFIG_MENDER_DEPLOYMENT_LOGS
648
static void
649
append_depl_log_msg(char *msg, void *ctx) {
14✔
650
    assert(NULL != ctx);
14✔
651

652
    char  *tstamp   = NULL;
14✔
653
    char  *level    = NULL;
14✔
654
    char  *log_msg  = NULL;
14✔
655
    cJSON *json_msg = NULL;
14✔
656
    cJSON *messages = ctx;
14✔
657

658
    /* Example log message we expect:
659
     *   "[00:39:06.746,000] <err> mender: Unable to perform HTTP request"
660
     * The code below goes through the string, searches for the expected parts and breaks the string
661
     * down accordingly.
662
     */
663

664
    /* Start by setting log_msg to the whole message. In case all of the
665
       break-down below fails, we send the whole message as the log message with
666
       no extra metadata. */
667
    log_msg = msg;
14✔
668

669
    char *c = msg;
14✔
670
    if ('[' == *c) {
14✔
671
        /* if it does start with a timestamp, like above, store the pointer and find its end */
672
        c++;
14✔
673
        tstamp = c;
14✔
674
        while (('\0' != *c) && (']' != *c)) {
392✔
675
            c++;
378✔
676
        }
677
        if ('\0' == *c) {
14✔
678
            goto DONE_PARSING;
679
        }
680
        *c = '\0';
14✔
681
        c++;
14✔
682
    }
683

684
    if (' ' == *c) {
14✔
685
        /* skip the space */
686
        c++;
14✔
687
    }
688

689
    if ('<' == *c) {
14✔
690
        /* if the log level follow, like above, store the pointer and find its end */
691
        c++;
14✔
692
        level = c;
14✔
693
        while (('\0' != *c) && ('>' != *c)) {
56✔
694
            c++;
42✔
695
        }
696
        if ('\0' == *c) {
14✔
697
            goto DONE_PARSING;
698
        }
699
        *c = '\0';
14✔
700
        c++;
14✔
701
    }
702

703
    if (' ' == *c) {
14✔
704
        /* skip the space */
705
        c++;
14✔
706
    }
707

708
    if ('\0' != *c) {
14✔
709
        log_msg = c;
14✔
710
        if (mender_utils_strbeginswith(log_msg, "mender: ")) {
14✔
711
            log_msg += strlen("mender: ");
14✔
712
        }
713
    }
714

715
DONE_PARSING:
716
    if (NULL == (json_msg = cJSON_CreateObject())) {
14✔
717
        mender_log_error("Unable to allocate memory");
718
        return;
719
    }
720

721
    if (NULL != tstamp) {
14✔
722
        if (NULL == cJSON_AddStringToObject(json_msg, "timestamp", tstamp)) {
14✔
723
            mender_log_error("Unable to allocate memory");
724
            goto END;
725
        }
726
    } else {
727
        if (NULL == cJSON_AddNullToObject(json_msg, "timestamp")) {
728
            mender_log_error("Unable to allocate memory");
729
            goto END;
730
        }
731
    }
732

733
    if (NULL != level) {
14✔
734
        if (NULL == cJSON_AddStringToObject(json_msg, "level", level)) {
14✔
735
            mender_log_error("Unable to allocate memory");
736
            goto END;
737
        }
738
    } else {
739
        if (NULL == cJSON_AddNullToObject(json_msg, "level")) {
740
            mender_log_error("Unable to allocate memory");
741
            goto END;
742
        }
743
    }
744

745
    if (NULL == cJSON_AddStringToObject(json_msg, "message", log_msg)) {
14✔
746
        mender_log_error("Unable to allocate memory");
747
        goto END;
748
    }
749

750
    if (!cJSON_AddItemToArray(messages, json_msg)) {
14✔
751
        mender_log_error("Unable to allocate memory");
752
    }
753
    json_msg = NULL;
14✔
754

755
END:
14✔
756
    cJSON_Delete(json_msg);
14✔
757
}
758

759
static mender_err_t
760
mender_api_publish_deployment_logs(const char *id) {
6✔
761
    assert(NULL != id);
6✔
762

763
    mender_err_t ret;
764
    cJSON       *json_payload  = NULL;
6✔
765
    cJSON       *json_messages = NULL;
6✔
766
    char        *payload       = NULL;
6✔
767
    char        *path          = NULL;
6✔
768
    char        *response      = NULL;
6✔
769
    int          status        = 0;
6✔
770

771
    /* Format payload */
772
    if (NULL == (json_payload = cJSON_CreateObject())) {
6✔
773
        mender_log_error("Unable to allocate memory");
774
        ret = MENDER_FAIL;
775
        goto END;
776
    }
777
    if (NULL == (json_messages = cJSON_AddArrayToObject(json_payload, "messages"))) {
6✔
778
        mender_log_error("Unable to allocate memory");
779
        ret = MENDER_FAIL;
780
        goto END;
781
    }
782

783
    if (MENDER_OK != (ret = mender_storage_deployment_log_walk(append_depl_log_msg, json_messages))) {
6✔
784
        mender_log_error("Failed to add deployment log messages to payload");
6✔
785
        ret = MENDER_FAIL;
6✔
786
        goto END;
6✔
787
    }
788

789
    if (0 == cJSON_GetArraySize(json_messages)) {
790
        /* Nothing to do, no logs to submit. */
791
        ret = MENDER_OK;
792
        goto END;
793
    }
794

795
    if (NULL == (payload = cJSON_PrintUnformatted(json_payload))) {
796
        mender_log_error("Unable to allocate memory");
797
        ret = MENDER_FAIL;
798
        goto END;
799
    }
800
    /* We no longer need the JSON now that we have the string representation so
801
       reclaim that (potentially big) chunk of memory). */
802
    DESTROY_AND_NULL(cJSON_Delete, json_payload);
803

804
    /* Perform HTTP request */
805
    if (mender_utils_asprintf(&path, MENDER_API_PATH_PUT_DEPLOYMENT_LOGS, id) <= 0) {
806
        mender_log_error("Unable to allocate memory");
807
        goto END;
808
    }
809

810
    mender_log_info("Publishing deployment logs");
811
    if (MENDER_OK != (ret = authenticated_http_perform(path, MENDER_HTTP_PUT, payload, NULL, &response, &status))) {
812
        mender_log_error("Unable to perform HTTP request");
813
        goto END;
814
    }
815

816
    /* Treatment depending of the status */
817
    if (204 == status) {
818
        /* No response expected */
819
        ret = MENDER_OK;
820
    } else if (409 == status) {
821
        /* Deployment aborted */
822
        mender_api_print_response_error(response, status);
823
        ret = MENDER_ABORTED;
824
    } else {
825
        mender_api_print_response_error(response, status);
826
        ret = MENDER_FAIL;
827
    }
828

829
END:
6✔
830

831
    /* Release memory */
832
    mender_free(response);
6✔
833
    mender_free(path);
6✔
834
    mender_free(payload);
6✔
835
    cJSON_Delete(json_payload);
6✔
836

837
    return ret;
6✔
838
}
839
#endif /* CONFIG_MENDER_DEPLOYMENT_LOGS */
840

841
#ifdef CONFIG_MENDER_CLIENT_INVENTORY
842

843
mender_err_t
844
mender_api_publish_inventory_data(cJSON *inventory, bool patch) {
29✔
845

846
    mender_err_t ret;
847
    char        *payload  = NULL;
29✔
848
    char        *response = NULL;
29✔
849
    int          status   = 0;
29✔
850

851
    /* Format payload */
852
    if (NULL == (payload = cJSON_PrintUnformatted(inventory))) {
29✔
853
        mender_log_error("Unable to allocate memory");
854
        ret = MENDER_FAIL;
855
        goto END;
856
    }
857

858
    /* Perform HTTP request */
859
    if (MENDER_OK
29✔
860
        != (ret = authenticated_http_perform(
29✔
861
                MENDER_API_PATH_PUT_DEVICE_ATTRIBUTES, patch ? MENDER_HTTP_PATCH : MENDER_HTTP_PUT, payload, NULL, &response, &status))) {
862
        mender_log_error("Unable to perform HTTP request");
3✔
863
        goto END;
3✔
864
    }
865

866
    /* Treatment depending of the status */
867
    if (200 == status) {
26✔
868
        /* No response expected */
869
        ret = MENDER_OK;
26✔
870
    } else {
871
        mender_api_print_response_error(response, status);
872
        ret = MENDER_FAIL;
873
    }
874

875
END:
29✔
876

877
    /* Release memory */
878
    mender_free(response);
29✔
879
    mender_free(payload);
29✔
880
    cJSON_Delete(inventory);
29✔
881

882
    return ret;
29✔
883
}
884

885
#endif /* CONFIG_MENDER_CLIENT_INVENTORY */
886

887
mender_err_t
UNCOV
888
mender_api_exit(void) {
×
889

890
    /* Release all modules */
UNCOV
891
    mender_http_exit();
×
892

893
    /* Destroy the authentication lock */
UNCOV
894
    mender_os_mutex_delete(auth_lock);
×
895

896
    /* Release memory */
UNCOV
897
    FREE_AND_NULL(api_jwt);
×
898

UNCOV
899
    return MENDER_OK;
×
900
}
901

902
static mender_err_t
903
mender_api_http_text_callback(mender_http_client_event_t event, void *data, size_t data_length, void *params) {
323✔
904

905
    assert(NULL != params);
323✔
906
    char       **response = (char **)params;
323✔
907
    mender_err_t ret      = MENDER_OK;
323✔
908
    char        *tmp;
909

910
    /* Treatment depending of the event */
911
    switch (event) {
323✔
912
        case MENDER_HTTP_EVENT_CONNECTED:
117✔
913
            /* Nothing to do */
914
            break;
117✔
915
        case MENDER_HTTP_EVENT_DATA_RECEIVED:
89✔
916
            /* Check input data */
917
            if ((NULL == data) || (0 == data_length)) {
89✔
UNCOV
918
                mender_log_error("Invalid data received");
×
UNCOV
919
                ret = MENDER_FAIL;
×
UNCOV
920
                break;
×
921
            }
922
            /* Concatenate data to the response */
923
            size_t response_length = (NULL != *response) ? strlen(*response) : 0;
89✔
924
            if (NULL == (tmp = mender_realloc(*response, response_length + data_length + 1))) {
89✔
UNCOV
925
                mender_log_error("Unable to allocate memory");
×
UNCOV
926
                ret = MENDER_FAIL;
×
UNCOV
927
                break;
×
928
            }
929
            *response = tmp;
89✔
930
            memcpy((*response) + response_length, data, data_length);
89✔
931
            *((*response) + response_length + data_length) = '\0';
89✔
932
            break;
89✔
933
        case MENDER_HTTP_EVENT_DISCONNECTED:
117✔
934
            /* Nothing to do */
935
            break;
117✔
UNCOV
936
        case MENDER_HTTP_EVENT_ERROR:
×
937
            /* Downloading the response fails */
UNCOV
938
            mender_log_error("An error occurred");
×
UNCOV
939
            ret = MENDER_FAIL;
×
UNCOV
940
            break;
×
UNCOV
941
        default:
×
942
            /* Should no occur */
UNCOV
943
            ret = MENDER_FAIL;
×
UNCOV
944
            break;
×
945
    }
946

947
    return ret;
323✔
948
}
949

950
void
951
mender_api_print_response_error(char *response, int status) {
2✔
952
    const char *desc;
953

954
    /* Treatment depending of the status */
955
    if (NULL != (desc = mender_utils_http_status_to_string(status))) {
2✔
956
        if (NULL != response) {
1✔
957
            cJSON *json_response = cJSON_Parse(response);
1✔
958
            if (NULL != json_response) {
1✔
959
                cJSON *json_error = cJSON_GetObjectItemCaseSensitive(json_response, "error");
1✔
960
                if (NULL != json_error) {
1✔
961
                    mender_log_error("[%d] %s: %s", status, desc, cJSON_GetStringValue(json_error));
1✔
962
                } else {
UNCOV
963
                    mender_log_error("[%d] %s: unknown error", status, desc);
×
964
                }
965
                cJSON_Delete(json_response);
1✔
966
            } else {
UNCOV
967
                mender_log_error("[%d] %s: unknown error", status, desc);
×
968
            }
969
        } else {
UNCOV
970
            mender_log_error("[%d] %s: unknown error", status, desc);
×
971
        }
972
    } else {
973
        mender_log_error("Unknown error occurred, status=%d", status);
1✔
974
    }
975
}
2✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc