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

mendersoftware / mender-mcu / 1752223040

04 Apr 2025 09:16AM UTC coverage: 57.871% (+0.1%) from 57.774%
1752223040

push

gitlab-ci

danielskinstad
fix: reschedule work items with backoff on retry errors

If `MENDER_RETRY_ERROR` is detected in a work function it will be retried
with a separate backoff interval which doubles on each try. It will
continue until it reaches the max backoff interval and keep
retrying using the max backoff interval.

Added configurable options for backoff:
* `MENDER_RETRY_ERROR_BACKOFF`
* `MENDER_RETRY_ERROR_MAX_BACKOFF`

Ticket: MEN-8222
Changelog: Title

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

34 of 43 new or added lines in 6 files covered. (79.07%)

435 existing lines in 6 files now uncovered.

2305 of 3983 relevant lines covered (57.87%)

70.3 hits per line

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

62.5
/src/core/client.c
1
/**
2
 * @file      client.c
3
 * @brief     Mender MCU client implementation
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 "client.h"
24
#include "artifact.h"
25
#include "artifact-download.h"
26
#include "log.h"
27
#include "os.h"
28
#include "storage.h"
29
#include "tls.h"
30
#include "update-module.h"
31
#include "utils.h"
32
#include "deployment-data.h"
33
#include "error-counters.h"
34

35
#ifndef CONFIG_MENDER_CLIENT_INVENTORY_DISABLE
36
#include "inventory.h"
37
#endif /* CONFIG_MENDER_CLIENT_INVENTORY_DISABLE */
38

39
/**
40
 * @brief Default host
41
 */
42
#ifndef CONFIG_MENDER_SERVER_HOST
43
#define CONFIG_MENDER_SERVER_HOST "https://hosted.mender.io"
44
#endif /* CONFIG_MENDER_SERVER_HOST */
45

46
/**
47
 * @brief Default tenant token
48
 */
49
#ifndef CONFIG_MENDER_SERVER_TENANT_TOKEN
50
#define CONFIG_MENDER_SERVER_TENANT_TOKEN NULL
51
#endif /* CONFIG_MENDER_SERVER_TENANT_TOKEN */
52

53
/**
54
 * @brief Default device type
55
 */
56
#ifndef CONFIG_MENDER_DEVICE_TYPE
57
#define CONFIG_MENDER_DEVICE_TYPE NULL
58
#endif /* CONFIG_MENDER_DEVICE_TYPE */
59

60
/**
61
 * @brief Default update poll interval (seconds)
62
 */
63
#ifndef CONFIG_MENDER_CLIENT_UPDATE_POLL_INTERVAL
64
#define CONFIG_MENDER_CLIENT_UPDATE_POLL_INTERVAL (1800)
65
#endif /* CONFIG_MENDER_CLIENT_UPDATE_POLL_INTERVAL */
66

67
/**
68
 * @brief Default backoff interval (seconds)
69
 */
70
#ifndef CONFIG_MENDER_RETRY_ERROR_BACKOFF
71
#define CONFIG_MENDER_RETRY_ERROR_BACKOFF (60)
72
#endif /* CONFIG_MENDER_RETRY_ERROR_BACKOFF */
73

74
/**
75
 * @brief Default max backoff interval interval (seconds)
76
 */
77
#ifndef CONFIG_MENDER_RETRY_ERROR_MAX_BACKOFF
78
#define CONFIG_MENDER_RETRY_ERROR_MAX_BACKOFF (540)
79
#endif /* CONFIG_MENDER_RETRY_ERROR_MAX_BACKOFF */
80

81
/**
82
 * @brief Mender client configuration
83
 */
84
static mender_client_config_t mender_client_config;
85

86
/**
87
 * @brief Mender client callbacks
88
 */
89
mender_client_callbacks_t mender_client_callbacks = { 0 };
90

91
mender_client_state_t mender_client_state = MENDER_CLIENT_STATE_INITIALIZATION;
92

93
struct mender_update_state_transition_s {
94
    mender_update_state_t success;
95
    mender_update_state_t failure;
96
};
97

98
/**
99
 * @brief Mender Update (module) state transitions
100
 */
101
static const struct mender_update_state_transition_s update_state_transitions[N_MENDER_UPDATE_STATES] = {
102
    /* MENDER_UPDATE_STATE_DOWNLOAD               */ { MENDER_UPDATE_STATE_INSTALL, MENDER_UPDATE_STATE_CLEANUP },
103
    /* MENDER_UPDATE_STATE_INSTALL                */ { MENDER_UPDATE_STATE_REBOOT, MENDER_UPDATE_STATE_FAILURE },
104
    /* MENDER_UPDATE_STATE_REBOOT                 */ { MENDER_UPDATE_STATE_VERIFY_REBOOT, MENDER_UPDATE_STATE_ROLLBACK },
105
    /* MENDER_UPDATE_STATE_VERIFY_REBOOT          */ { MENDER_UPDATE_STATE_COMMIT, MENDER_UPDATE_STATE_ROLLBACK },
106
    /* MENDER_UPDATE_STATE_COMMIT                 */ { MENDER_UPDATE_STATE_CLEANUP, MENDER_UPDATE_STATE_ROLLBACK },
107
    /* MENDER_UPDATE_STATE_CLEANUP                */ { MENDER_UPDATE_STATE_END, MENDER_UPDATE_STATE_END },
108
    /* MENDER_UPDATE_STATE_ROLLBACK               */ { MENDER_UPDATE_STATE_ROLLBACK_REBOOT, MENDER_UPDATE_STATE_FAILURE },
109
    /* MENDER_UPDATE_STATE_ROLLBACK_REBOOT        */ { MENDER_UPDATE_STATE_ROLLBACK_VERIFY_REBOOT, MENDER_UPDATE_STATE_ROLLBACK_VERIFY_REBOOT },
110
    /* MENDER_UPDATE_STATE_ROLLBACK_VERIFY_REBOOT */ { MENDER_UPDATE_STATE_FAILURE, MENDER_UPDATE_STATE_ROLLBACK_REBOOT },
111
    /* MENDER_UPDATE_STATE_FAILURE                */ { MENDER_UPDATE_STATE_CLEANUP, MENDER_UPDATE_STATE_CLEANUP },
112
};
113

114
#if CONFIG_MENDER_LOG_LEVEL >= MENDER_LOG_LEVEL_DBG
115
/* This is only needed for debug messages. */
116
static const char *update_state_str[N_MENDER_UPDATE_STATES + 1] = {
117
    "MENDER_UPDATE_STATE_DOWNLOAD",
118
    "MENDER_UPDATE_STATE_INSTALL",
119
    "MENDER_UPDATE_STATE_REBOOT",
120
    "MENDER_UPDATE_STATE_VERIFY_REBOOT",
121
    "MENDER_UPDATE_STATE_COMMIT",
122
    "MENDER_UPDATE_STATE_CLEANUP",
123
    "MENDER_UPDATE_STATE_ROLLBACK",
124
    "MENDER_UPDATE_STATE_ROLLBACK_REBOOT",
125
    "MENDER_UPDATE_STATE_ROLLBACK_VERIFY_REBOOT",
126
    "MENDER_UPDATE_STATE_FAILURE",
127
    "MENDER_UPDATE_STATE_END",
128
};
129
static const char *client_state_str[N_MENDER_CLIENT_STATES + 1] = {
130
    "MENDER_CLIENT_STATE_INITIALIZATION",
131
    "MENDER_CLIENT_STATE_OPERATIONAL",
132
    "MENDER_CLIENT_STATE_PENDING_REBOOT",
133
};
134

135
#endif
136

137
/**
138
 * @brief Flag to know if network connection was requested or not
139
 */
140
static bool mender_client_network_connected = false;
141

142
/**
143
 * @brief Deployment data. Used to track progress of an update, so that the
144
 *        operation can resume or roll back across reboots
145
 */
146
static mender_deployment_data_t *mender_client_deployment_data = NULL;
147

148
/**
149
 * @brief Update module being used by the current deployment
150
 */
151
static mender_update_module_t *mender_update_module = NULL;
152

153
/**
154
 * @brief The main Mender work item
155
 */
156
static mender_work_t *mender_client_work = NULL;
157

158
/**
159
 * @brief Mender client work function
160
 * @return MENDER_OK if the function succeeds, error code otherwise
161
 */
162
static mender_err_t mender_client_work_function(void);
163

164
/**
165
 * @brief Mender client initialization work function
166
 * @return MENDER_OK if the function succeeds, error code otherwise
167
 */
168
static mender_err_t mender_client_initialization_work_function(void);
169

170
/**
171
 * @brief Function to request network access
172
 * @return MENDER_OK if network is connected following the request, error code otherwise
173
 */
174
static mender_err_t mender_client_network_connect(void);
175

176
/**
177
 * @brief Function to release network access
178
 * @return MENDER_OK if network is released following the request, error code otherwise
179
 */
180
static mender_err_t mender_client_network_release(void);
181

182
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
183
/**
184
 * @brief Compare artifact, device and deployment device types
185
 * @param device_type_artifact Device type of artifact
186
 * @param device_type_device Device type of configuration
187
 * @param device_type_deployment Device types of deployment
188
 * @param device_type_deployment_size Deployment device types size
189
 * @return MENDER_OK if the function succeeds, error code otherwise
190
 */
191
static mender_err_t mender_compare_device_types(const char  *device_type_artifact,
192
                                                const char  *device_type_device,
193
                                                const char **device_type_deployment,
194
                                                const size_t device_type_deployment_size);
195
#ifdef CONFIG_MENDER_PROVIDES_DEPENDS
196
/**
197
 * @brief Filter provides and merge the two lists
198
 * @param mender_artifact_ctx Mender artifact context
199
 * @param new_provides New provides list
200
 * @param stored_provides Stored provides list
201
 * @return MENDER_OK if the function succeeds, error code otherwise
202
 */
203
static mender_err_t mender_filter_provides(mender_artifact_ctx_t    *mender_artifact_ctx,
204
                                           mender_key_value_list_t **new_provides,
205
                                           mender_key_value_list_t **stored_provides);
206
/**
207
 * @brief Prepare the new provides data to be commited on a successful deployment
208
 * @param mender_artifact_ctx Mender artifact context
209
 * @param provides Provies data to be written
210
 * @return MENDER_OK if the function succeeds, error code otherwise
211
 */
212
static mender_err_t mender_prepare_new_provides(mender_artifact_ctx_t *mender_artifact_ctx, char **provides, const char **artifact_name);
213

214
/**
215
 * @brief Determine the compatiblity of the deployment by: comparing artifact's depend with the stored provides
216
 * @param mender_artifact_ctx Mender artifact context
217
 * @return MENDER_OK if the function succeeds, error code otherwise
218
 */
219
static mender_err_t mender_check_device_compatibility(mender_artifact_ctx_t *mender_artifact_ctx);
220
#endif /* CONFIG_MENDER_PROVIDES_DEPENDS */
221
#endif /* CONFIG_MENDER_FULL_PARSE_ARTIFACT */
222

223
/**
224
 * @brief Mender client update work function
225
 * @return MENDER_OK if the function succeeds, error code otherwise
226
 */
227
static mender_err_t mender_client_update_work_function(void);
228

229
/**
230
 * @brief Publish deployment status of the device to the mender-server and invoke deployment status callback
231
 * @param id ID of the deployment
232
 * @param deployment_status Deployment status
233
 * @return MENDER_OK if the function succeeds, error code otherwise
234
 */
235
static mender_err_t mender_client_publish_deployment_status(const char *id, mender_deployment_status_t deployment_status);
236

237
/**
238
 * @brief Set state in deployment data and store it in permanent storage
239
 * @param state State to set and store
240
 * @return MENDER_OK in case of success, error code otherwise
241
 */
242
static mender_err_t set_and_store_state(const mender_update_state_t state);
243

244
const char *
UNCOV
245
mender_client_version(void) {
×
246

247
    /* Return version as string */
UNCOV
248
    return MENDER_CLIENT_VERSION;
×
249
}
250

251
mender_err_t
252
mender_client_init(mender_client_config_t *config, mender_client_callbacks_t *callbacks) {
29✔
253
    assert(NULL != config);
29✔
254
    assert(NULL != callbacks);
29✔
255
    assert(NULL != callbacks->restart);
29✔
256

257
    /* Either all allocation functions set or none. */
258
    assert(
29✔
259
        ((NULL == config->allocation_funcs.malloc_func) && (NULL == config->allocation_funcs.realloc_func) && (NULL == config->allocation_funcs.free_func))
260
        || ((NULL != config->allocation_funcs.malloc_func) && (NULL != config->allocation_funcs.realloc_func) && (NULL != config->allocation_funcs.free_func)));
261

262
    mender_err_t ret;
263

264
    if (NULL != config->allocation_funcs.malloc_func) {
29✔
UNCOV
265
        mender_set_allocation_funcs(config->allocation_funcs.malloc_func, config->allocation_funcs.realloc_func, config->allocation_funcs.free_func);
×
266
    } else {
267
        mender_set_platform_allocation_funcs();
29✔
268
    }
269

270
    {
271
        cJSON_Hooks cjson_alloc_funcs = { mender_malloc, mender_free };
29✔
272
        cJSON_InitHooks(&cjson_alloc_funcs);
29✔
273
    }
274

275
    /* Prefer client config over Kconfig */
276
    mender_client_config.device_type = IS_NULL_OR_EMPTY(config->device_type) ? CONFIG_MENDER_DEVICE_TYPE : config->device_type;
29✔
277
    if (IS_NULL_OR_EMPTY(mender_client_config.device_type)) {
29✔
UNCOV
278
        mender_log_error("Invalid device type configuration, can't be null or empty");
×
279
        ret = MENDER_FAIL;
×
UNCOV
280
        goto END;
×
281
    }
282
    mender_log_info("Device type: [%s]", mender_client_config.device_type);
29✔
283

284
    if ((NULL != config->host) && (strlen(config->host) > 0)) {
29✔
UNCOV
285
        mender_client_config.host = config->host;
×
286
    } else {
287
        mender_client_config.host = CONFIG_MENDER_SERVER_HOST;
29✔
288
    }
289
    if ((NULL == mender_client_config.host) || (0 == strlen(mender_client_config.host))) {
29✔
UNCOV
290
        mender_log_error("Invalid server host configuration, can't be null or empty");
×
UNCOV
291
        ret = MENDER_FAIL;
×
292
        goto END;
×
293
    }
294
    if ('/' == mender_client_config.host[strlen(mender_client_config.host) - 1]) {
29✔
UNCOV
295
        mender_log_error("Invalid server host configuration, trailing '/' is not allowed");
×
UNCOV
296
        ret = MENDER_FAIL;
×
UNCOV
297
        goto END;
×
298
    }
299
    if ((NULL != config->tenant_token) && (strlen(config->tenant_token) > 0)) {
29✔
UNCOV
300
        mender_client_config.tenant_token = config->tenant_token;
×
301
    } else {
302
        mender_client_config.tenant_token = CONFIG_MENDER_SERVER_TENANT_TOKEN;
29✔
303
    }
304
    if ((NULL != mender_client_config.tenant_token) && (0 == strlen(mender_client_config.tenant_token))) {
29✔
305
        mender_client_config.tenant_token = NULL;
×
306
    }
307
    if (0 != config->update_poll_interval) {
29✔
UNCOV
308
        mender_client_config.update_poll_interval = config->update_poll_interval;
×
309
    } else {
310
        mender_client_config.update_poll_interval = CONFIG_MENDER_CLIENT_UPDATE_POLL_INTERVAL;
29✔
311
    }
312
    if (0 != config->backoff_interval) {
29✔
NEW
313
        mender_client_config.backoff_interval = config->backoff_interval;
×
314
    } else {
315
        mender_client_config.backoff_interval = CONFIG_MENDER_RETRY_ERROR_BACKOFF;
29✔
316
    }
317
    if (0 != config->max_backoff_interval) {
29✔
NEW
318
        mender_client_config.max_backoff_interval = config->max_backoff_interval;
×
319
    } else {
320
        mender_client_config.max_backoff_interval = CONFIG_MENDER_RETRY_ERROR_MAX_BACKOFF;
29✔
321
    }
322
    mender_client_config.recommissioning = config->recommissioning;
29✔
323

324
    /* Save callbacks */
325
    memcpy(&mender_client_callbacks, callbacks, sizeof(mender_client_callbacks_t));
29✔
326

327
    /* Initializations */
328
    // TODO: what to do with the authentication interval?
329
    if (MENDER_OK != (ret = mender_os_scheduler_init())) {
29✔
UNCOV
330
        mender_log_error("Unable to initialize scheduler");
×
UNCOV
331
        goto END;
×
332
    }
333
    if (MENDER_OK != (ret = mender_log_init())) {
29✔
UNCOV
334
        mender_log_error("Unable to initialize log");
×
UNCOV
335
        goto END;
×
336
    }
337
    if (MENDER_OK != (ret = mender_storage_init())) {
29✔
UNCOV
338
        mender_log_error("Unable to initialize storage");
×
UNCOV
339
        goto END;
×
340
    }
341
    if (MENDER_OK != (ret = mender_tls_init())) {
29✔
342
        mender_log_error("Unable to initialize TLS");
×
UNCOV
343
        goto END;
×
344
    }
345
    mender_api_config_t mender_api_config = {
29✔
346
        .device_type  = mender_client_config.device_type,
29✔
347
        .host         = mender_client_config.host,
29✔
348
        .tenant_token = mender_client_config.tenant_token,
29✔
349
        .identity_cb  = callbacks->get_identity,
29✔
350
    };
351
    if (MENDER_OK != (ret = mender_api_init(&mender_api_config))) {
29✔
UNCOV
352
        mender_log_error("Unable to initialize API");
×
UNCOV
353
        goto END;
×
354
    }
355

356
#ifndef CONFIG_MENDER_CLIENT_INVENTORY_DISABLE
357
    if (MENDER_OK
29✔
358
        != (ret = mender_inventory_init(mender_client_config.inventory_update_interval,
29✔
359
                                        mender_client_config.device_type,
29✔
360
                                        mender_client_config.backoff_interval,
29✔
361
                                        mender_client_config.max_backoff_interval))) {
29✔
362
        mender_log_error("Failed to initialize the inventory functionality");
×
363
        goto END;
×
364
    }
365
    if (MENDER_OK != mender_inventory_add_default_callbacks()) {
29✔
366
        mender_log_error("Failed to enable default inventory");
×
367
        /* unlikely to happen and not a fatal issue, keep going */
368
    }
369
#endif /* CONFIG_MENDER_CLIENT_INVENTORY_DISABLE */
370

371
END:
29✔
372

373
    return ret;
29✔
374
}
375

376
mender_err_t
377
mender_client_activate(void) {
29✔
378
    mender_err_t ret;
379

380
    mender_os_scheduler_backoff_t backoff = {
29✔
381
        .interval     = mender_client_config.backoff_interval,
29✔
382
        .max_interval = mender_client_config.max_backoff_interval,
29✔
383
    };
384

385
    mender_os_scheduler_work_params_t work_params = {
29✔
386
        .function = mender_client_work_function,
387
        .period   = mender_client_config.update_poll_interval,
29✔
388
        .name     = "mender_client_main",
389
        .backoff  = backoff,
390
    };
391

392
    if ((MENDER_OK != (ret = mender_os_scheduler_work_create(&work_params, &mender_client_work)))
29✔
393
        || (MENDER_OK != (ret = mender_os_scheduler_work_activate(mender_client_work)))) {
29✔
UNCOV
394
        mender_log_error("Unable to activate the main work");
×
UNCOV
395
        return ret;
×
396
    }
397

398
#ifndef CONFIG_MENDER_CLIENT_INVENTORY_DISABLE
399
    /* Activate inventory work */
400
    if (MENDER_OK != (ret = mender_inventory_activate())) {
29✔
UNCOV
401
        mender_log_error("Unable to activate the inventory functionality");
×
UNCOV
402
        return ret;
×
403
    }
404
#endif /* CONFIG_MENDER_CLIENT_INVENTORY_DISABLE */
405

406
    return ret;
29✔
407
}
408

409
mender_err_t
410
mender_client_ensure_connected(void) {
110✔
411
    if (mender_client_network_connected) {
110✔
412
        return MENDER_DONE;
82✔
413
    }
414

415
    return mender_client_network_connect();
28✔
416
}
417

418
static mender_err_t
419
mender_client_network_connect(void) {
28✔
420
    if (mender_client_network_connected) {
28✔
UNCOV
421
        return MENDER_OK;
×
422
    }
423

424
    /* Request network access */
425
    if (NULL != mender_client_callbacks.network_connect) {
28✔
426
        if (MENDER_OK != mender_client_callbacks.network_connect()) {
28✔
UNCOV
427
            mender_log_error("Unable to connect network");
×
428
            return MENDER_FAIL;
×
429
        }
430
    }
431

432
    mender_client_network_connected = true;
28✔
433

434
    return MENDER_OK;
28✔
435
}
436

437
static mender_err_t
UNCOV
438
mender_client_network_release(void) {
×
UNCOV
439
    if (!mender_client_network_connected) {
×
UNCOV
440
        return MENDER_OK;
×
441
    }
442

443
    /* Release network access */
UNCOV
444
    if (NULL != mender_client_callbacks.network_release) {
×
UNCOV
445
        if (MENDER_OK != mender_client_callbacks.network_release()) {
×
UNCOV
446
            mender_log_error("Unable to release network");
×
UNCOV
447
            return MENDER_FAIL;
×
448
        }
449
    }
UNCOV
450
    mender_client_network_connected = false;
×
451

UNCOV
452
    return MENDER_OK;
×
453
}
454

455
mender_err_t
UNCOV
456
mender_client_deactivate(void) {
×
457
    mender_err_t ret;
458

UNCOV
459
    if (NULL != mender_client_work) {
×
UNCOV
460
        if (MENDER_OK != (ret = mender_os_scheduler_work_deactivate(mender_client_work))) {
×
461
            mender_log_error("Failed to deactivate main work");
×
462
            return ret;
×
463
        }
464
    }
465
#ifndef CONFIG_MENDER_CLIENT_INVENTORY_DISABLE
UNCOV
466
    if (MENDER_OK != (ret = mender_inventory_deactivate())) {
×
467
        /* error already logged */
UNCOV
468
        return ret;
×
469
    }
470
#endif /* CONFIG_MENDER_CLIENT_INVENTORY_DISABLE */
471

472
    return MENDER_OK;
×
473
}
474

475
mender_err_t
UNCOV
476
mender_client_exit(void) {
×
UNCOV
477
    bool some_error = false;
×
478

479
    if (MENDER_OK != mender_client_deactivate()) {
×
480
        /* error already logged; keep going on, we want to do as much cleanup as possible */
481
        some_error = true;
×
482
    }
483

484
    if (NULL != mender_client_work) {
×
UNCOV
485
        if (MENDER_OK != mender_os_scheduler_work_delete(mender_client_work)) {
×
486
            mender_log_error("Failed to delete main work");
×
487
            /* keep going on, we want to do as much cleanup as possible */
UNCOV
488
            some_error = true;
×
489
        } else {
490
            mender_client_work = NULL;
×
491
        }
492
    }
493

494
#ifndef CONFIG_MENDER_CLIENT_INVENTORY_DISABLE
495
    if (MENDER_OK != mender_inventory_exit()) {
×
496
        mender_log_error("Unable to cleanup after the inventory functionality");
×
497
        /* keep going on, we want to do as much cleanup as possible */
UNCOV
498
        some_error = true;
×
499
    }
500
#endif /* CONFIG_MENDER_CLIENT_INVENTORY_DISABLE */
501

502
    /* Stop scheduling new work */
UNCOV
503
    mender_os_scheduler_exit();
×
504

505
    /* Release all modules */
506
    mender_api_exit();
×
UNCOV
507
    mender_tls_exit();
×
UNCOV
508
    mender_storage_exit();
×
UNCOV
509
    mender_log_exit();
×
510
    mender_client_network_release();
×
511

512
    /* Release memory */
513
    mender_client_config.device_type          = NULL;
×
UNCOV
514
    mender_client_config.host                 = NULL;
×
515
    mender_client_config.tenant_token         = NULL;
×
UNCOV
516
    mender_client_config.update_poll_interval = 0;
×
UNCOV
517
    DESTROY_AND_NULL(mender_delete_deployment_data, mender_client_deployment_data);
×
518

519
    mender_update_module_unregister_all();
×
520

UNCOV
521
    return some_error ? MENDER_FAIL : MENDER_OK;
×
522
}
523

524
static mender_err_t
525
mender_client_work_function(void) {
69✔
526
    mender_err_t ret;
527
    mender_log_debug("Inside work function [state: %s]", client_state_str[mender_client_state]);
69✔
528

529
    switch (mender_client_state) {
69✔
530
        case MENDER_CLIENT_STATE_PENDING_REBOOT:
14✔
531
            mender_log_info("Waiting for a reboot");
14✔
532
            if (MENDER_OK != mender_err_count_reboot_inc()) {
14✔
533
                /* It appears we are stuck in this state. The only thing we can do is to mark the
534
                   deployment as failed and revert to normal operation. */
UNCOV
535
                mender_log_error("Waiting for reboot for too long, trying unconditional reboot");
×
UNCOV
536
                mender_os_reboot();
×
537

UNCOV
538
                mender_log_error("Failed to reboot unconditionally, trying to resume operations");
×
UNCOV
539
                if (NULL == mender_client_deployment_data) {
×
540
                    mender_log_error("No deployment data to use for deployment abortion");
×
541
                } else {
542
                    mender_update_state_t update_state;
543
                    if (MENDER_OK != mender_deployment_data_get_state(mender_client_deployment_data, &update_state)) {
×
544
                        mender_log_error("Failed to get current update state, going to ROLLBACK state");
×
UNCOV
545
                        update_state = MENDER_UPDATE_STATE_ROLLBACK;
×
546
                    } else {
547
                        update_state = update_state_transitions[update_state].failure;
×
548
                    }
549
                    if (MENDER_OK != set_and_store_state(update_state)) {
×
550
                        mender_log_error("Failed to save new state");
×
551
                    }
552
                }
553

UNCOV
554
                mender_client_state = MENDER_CLIENT_STATE_OPERATIONAL;
×
555
            }
556
            /* else:
557
               Nothing to do, but let's make sure we have a chance to detect we are stuck in this
558
               state (i.e. MENDER_OK, not MENDER_DONE which would tell the scheduler we are
559
               done and don't need to run again). */
560
            return MENDER_OK;
14✔
561
        case MENDER_CLIENT_STATE_INITIALIZATION:
29✔
562
            /* Perform initialization of the client */
563
            mender_err_count_reboot_reset();
29✔
564
            if (MENDER_DONE != mender_client_initialization_work_function()) {
29✔
UNCOV
565
                return MENDER_FAIL;
×
566
            }
567
            mender_client_state = MENDER_CLIENT_STATE_OPERATIONAL;
29✔
568
            /* fallthrough */
569
        case MENDER_CLIENT_STATE_OPERATIONAL:
55✔
570
            mender_err_count_reboot_reset();
55✔
571
            ret = mender_client_update_work_function();
55✔
572
            if (MENDER_FAIL == ret) {
40✔
573
                if (MENDER_FAIL == mender_err_count_net_check()) {
×
574
                    /* Try to release network so that it gets set up again next
575
                       time. */
UNCOV
576
                    mender_client_network_release();
×
577
                }
578
            } else if (!MENDER_IS_ERROR(ret)) {
40✔
579
                mender_err_count_net_reset();
40✔
580
            }
581
            if (MENDER_DONE == ret) {
40✔
582
                /* We should only be done when waiting for a reboot. */
583
                assert(MENDER_CLIENT_STATE_PENDING_REBOOT == mender_client_state);
14✔
584

585
                /* We don't want to tell the scheduler we are done because
586
                   otherwise we won't have a chance to detect that we are
587
                   waiting for a reboot forever. */
588
                ret = MENDER_OK;
14✔
589
            }
590
            return ret;
40✔
591
    }
592

593
    /* This should never be reached, all the cases should be covered in the
594
       above switch and they all return. */
UNCOV
595
    return MENDER_FAIL;
×
596
}
597

598
/* Flag to indicate whether a deployment has had a spontaneous reboot */
599
static bool spontaneous_reboot;
600

601
static mender_err_t
602
mender_client_initialization_work_function(void) {
29✔
603

604
    mender_err_t ret   = MENDER_DONE;
29✔
605
    spontaneous_reboot = false;
29✔
606

607
    /* Retrieve or generate authentication keys */
608
    if (MENDER_OK != (ret = mender_tls_init_authentication_keys(mender_client_callbacks.get_user_provided_keys, mender_client_config.recommissioning))) {
29✔
UNCOV
609
        mender_log_error("Unable to retrieve or generate authentication keys");
×
610
        goto END;
×
611
    }
612

613
    /* Retrieve deployment data if it is found (following an update) */
614
    if (MENDER_OK != (ret = mender_get_deployment_data(&mender_client_deployment_data))) {
29✔
615
        if (MENDER_NOT_FOUND != ret) {
13✔
UNCOV
616
            mender_log_error("Unable to get deployment data");
×
UNCOV
617
            goto REBOOT;
×
618
        }
619
    }
620

621
    /* Handle spontaneous reboots in  MENDER_UPDATE_STATE_INSTALL and MENDER_UPDATE_STATE_COMMIT
622
     See https://docs.mender.io/artifact-creation/state-scripts#power-loss */
623
    mender_update_state_t update_state;
624
    if (MENDER_OK == (ret = mender_deployment_data_get_state(mender_client_deployment_data, &update_state))) {
29✔
625
        if ((MENDER_UPDATE_STATE_INSTALL == update_state) || (MENDER_UPDATE_STATE_COMMIT == update_state)) {
16✔
626
            mender_log_debug("Spontaneous reboot detected in state %s", update_state_str[update_state]);
2✔
627
            spontaneous_reboot = true;
2✔
628
        }
629
    }
630

631
    mender_log_info("Initialization done");
29✔
632

633
    return MENDER_DONE;
29✔
634

UNCOV
635
END:
×
636

UNCOV
637
    return ret;
×
638

UNCOV
639
REBOOT:
×
640

UNCOV
641
    mender_log_info("Rebooting...");
×
642

643
    /* Delete pending deployment */
644
    mender_storage_delete_deployment_data();
×
645

646
    /* Invoke restart callback, application is responsible to shutdown properly and restart the system */
647
    /* Set the client's state to PENDING_REBOOT so that we can potentially
648
       detect a failure to reboot (i.e. waiting for reboot taking too long).  */
UNCOV
649
    mender_client_state = MENDER_CLIENT_STATE_PENDING_REBOOT;
×
650
    if (NULL != mender_client_callbacks.restart) {
×
651
        mender_client_callbacks.restart();
×
652
    }
653

UNCOV
654
    return ret;
×
655
}
656

657
static mender_err_t
658
mender_commit_artifact_data(void) {
5✔
659

660
    assert(NULL != mender_client_deployment_data);
5✔
661

662
    const char *artifact_name;
663
    if (MENDER_OK != mender_deployment_data_get_artifact_name(mender_client_deployment_data, &artifact_name)) {
5✔
UNCOV
664
        mender_log_error("Unable to get artifact name from the deployment data");
×
UNCOV
665
        return MENDER_FAIL;
×
666
    }
667

668
    if (MENDER_OK != mender_storage_set_artifact_name(artifact_name)) {
5✔
669
        mender_log_error("Unable to set artifact name");
×
UNCOV
670
        return MENDER_FAIL;
×
671
    }
672

673
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
674
#ifdef CONFIG_MENDER_PROVIDES_DEPENDS
675
    /* Get provides from the deployment data */
676
    const char *provides;
677
    if (MENDER_OK != mender_deployment_data_get_provides(mender_client_deployment_data, &provides)) {
5✔
678
        mender_log_error("Unable to get new_provides from the deployment data");
×
UNCOV
679
        return MENDER_FAIL;
×
680
    }
681

682
    /* Parse provides */
683
    mender_key_value_list_t *new_provides = NULL;
5✔
684
    if (MENDER_OK != mender_utils_string_to_key_value_list(provides, &new_provides)) {
5✔
685
        mender_log_error("Unable to parse provides from the deployment data");
×
UNCOV
686
        return MENDER_FAIL;
×
687
    }
688
    /* Replace the stored provides with the new provides */
689
    if (MENDER_OK != mender_storage_set_provides(new_provides)) {
5✔
UNCOV
690
        mender_log_error("Unable to set provides");
×
UNCOV
691
        mender_utils_key_value_list_free(new_provides);
×
UNCOV
692
        return MENDER_FAIL;
×
693
    }
694
    mender_utils_key_value_list_free(new_provides);
5✔
695
#endif /* CONFIG_MENDER_PROVIDES_DEPENDS */
696
#endif /* CONFIG_MENDER_FULL_PARSE_ARTIFACT */
697

698
    return MENDER_OK;
5✔
699
}
700

701
static mender_err_t
702
deployment_destroy(mender_api_deployment_data_t *deployment) {
40✔
703
    if (NULL != deployment) {
40✔
704
        mender_free(deployment->id);
25✔
705
        mender_free(deployment->artifact_name);
25✔
706
        mender_free(deployment->uri);
25✔
707
        for (size_t i = 0; i < deployment->device_types_compatible_size; ++i) {
35✔
708
            mender_free(deployment->device_types_compatible[i]);
10✔
709
        }
710
        mender_free(deployment->device_types_compatible);
25✔
711
        mender_free(deployment);
25✔
712
    }
713
    return MENDER_OK;
40✔
714
}
715

716
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
717
static mender_err_t
718
mender_compare_device_types(const char  *device_type_artifact,
11✔
719
                            const char  *device_type_device,
720
                            const char **device_type_deployment,
721
                            const size_t device_type_deployment_size) {
722

723
    assert(NULL != device_type_artifact);
11✔
724
    assert(NULL != device_type_deployment);
11✔
725
    assert(NULL != device_type_device);
11✔
726
    assert(0 < device_type_deployment_size);
11✔
727

728
    if (!StringEqual(device_type_artifact, device_type_device)) {
11✔
UNCOV
729
        mender_log_error("Device type from artifact '%s' is not compatible with device '%s'", device_type_artifact, device_type_device);
×
UNCOV
730
        return MENDER_FAIL;
×
731
    }
732

733
    /* Return MENDER_OK if one of the devices in the deployment are compatible with the device */
734
    for (size_t i = 0; i < device_type_deployment_size; i++) {
11✔
735
        if (StringEqual(device_type_deployment[i], device_type_device)) {
11✔
736
            return MENDER_OK;
11✔
737
        }
738
    }
UNCOV
739
    mender_log_error("None of the device types from the deployment are compatible with device '%s'", device_type_device);
×
UNCOV
740
    return MENDER_FAIL;
×
741
}
742

743
#ifdef CONFIG_MENDER_PROVIDES_DEPENDS
744
static mender_err_t
745
mender_filter_provides(mender_artifact_ctx_t *mender_artifact_ctx, mender_key_value_list_t **new_provides, mender_key_value_list_t **stored_provides) {
11✔
746

747
    mender_err_t ret = MENDER_FAIL;
11✔
748
    /* Clears provides */
749
    bool matches;
750
    for (size_t i = 0; i < mender_artifact_ctx->payloads.size; i++) {
22✔
751
        for (size_t j = 0; j < mender_artifact_ctx->payloads.values[i].clears_provides_size; j++) {
22✔
752
            const char *to_clear = mender_artifact_ctx->payloads.values[i].clears_provides[j];
11✔
753
            for (mender_key_value_list_t *item = *stored_provides; NULL != item; item = item->next) {
11✔
UNCOV
754
                if (MENDER_OK != mender_utils_compare_wildcard(item->key, to_clear, &matches)) {
×
UNCOV
755
                    mender_log_error("Unable to compare wildcard %s with key %s", to_clear, item->key);
×
UNCOV
756
                    goto END;
×
757
                }
UNCOV
758
                if (matches && MENDER_OK != mender_utils_key_value_list_delete_node(stored_provides, item->key)) {
×
UNCOV
759
                    mender_log_error("Unable to delete node containing key %s", item->key);
×
UNCOV
760
                    goto END;
×
761
                }
762
            }
763
        }
764
    }
765

766
    /* Combine the stored provides with the new ones */
767
    if (MENDER_OK != mender_utils_key_value_list_append_unique(new_provides, stored_provides)) {
11✔
UNCOV
768
        mender_log_error("Unable to merge provides");
×
UNCOV
769
        goto END;
×
770
    }
771

772
    /* Make sure the artifact name is not in the new provides */
773
    if (MENDER_OK != mender_utils_key_value_list_delete_node(new_provides, "artifact_name")) {
11✔
774
        mender_log_error("Unable to delete node containing key 'artifact_name'");
×
UNCOV
775
        goto END;
×
776
    }
777

778
    ret = MENDER_OK;
11✔
779

780
END:
11✔
781

782
    mender_utils_key_value_list_free(*stored_provides);
11✔
783
    return ret;
11✔
784
}
785

786
static mender_err_t
787
mender_prepare_new_provides(mender_artifact_ctx_t *mender_artifact_ctx, char **new_provides, const char **artifact_name) {
11✔
788

789
    assert(NULL != artifact_name);
11✔
790
    assert(NULL != mender_artifact_ctx);
11✔
791

792
    /* Load the currently stored provides */
793
    mender_key_value_list_t *stored_provides = NULL;
11✔
794
    if (MENDER_FAIL == mender_storage_get_provides(&stored_provides)) {
11✔
UNCOV
795
        mender_log_error("Unable to get provides");
×
UNCOV
796
        return MENDER_FAIL;
×
797
    }
798

799
    mender_key_value_list_t *provides = NULL;
11✔
800
    for (size_t i = 0; i < mender_artifact_ctx->payloads.size; i++) {
22✔
801
        if (MENDER_OK != mender_utils_key_value_list_append(&provides, &mender_artifact_ctx->payloads.values[i].provides)) {
11✔
802
            mender_log_error("Unable to merge provides");
×
803
            mender_utils_key_value_list_free(stored_provides);
×
UNCOV
804
            return MENDER_FAIL;
×
805
        }
806
    }
807

808
    /* Get artifact name from provides */
809
    for (mender_key_value_list_t *item = mender_artifact_ctx->artifact_info.provides; NULL != item; item = item->next) {
11✔
810
        if (StringEqual("artifact_name", item->key)) {
11✔
811
            *artifact_name = item->value;
11✔
812
            break;
11✔
813
        }
814
    }
815

816
    if (NULL == *artifact_name) {
11✔
UNCOV
817
        mender_log_error("No artifact name found in provides");
×
UNCOV
818
        mender_utils_key_value_list_free(stored_provides);
×
UNCOV
819
        return MENDER_FAIL;
×
820
    }
821

822
    /* Filter provides */
823
    /* `stored_provides` is freed in `mender_filter_provides` */
824
    if (MENDER_OK != mender_filter_provides(mender_artifact_ctx, &provides, &stored_provides)) {
11✔
UNCOV
825
        return MENDER_FAIL;
×
826
    }
827

828
    if (MENDER_OK != mender_utils_key_value_list_to_string(provides, new_provides)) {
11✔
829
        mender_utils_key_value_list_free(provides);
×
830
        return MENDER_FAIL;
×
831
    }
832

833
    mender_utils_key_value_list_free(provides);
11✔
834
    return MENDER_OK;
11✔
835
}
836

837
static mender_err_t
838
mender_check_device_compatibility(mender_artifact_ctx_t *mender_artifact_ctx) {
11✔
839

840
    /* We need to load the stored provides */
841
    mender_key_value_list_t *stored_provides = NULL;
11✔
842
    if (MENDER_FAIL == mender_storage_get_provides(&stored_provides)) {
11✔
UNCOV
843
        return MENDER_FAIL;
×
844
    }
845

846
    mender_err_t ret = MENDER_FAIL;
11✔
847

848
    /* Get depends */
849
    mender_key_value_list_t *depends = NULL;
11✔
850
    for (size_t i = 0; i < mender_artifact_ctx->payloads.size; i++) {
22✔
851
        if (MENDER_OK != mender_utils_key_value_list_append(&depends, &mender_artifact_ctx->payloads.values[i].depends)) {
11✔
852
            mender_log_error("Unable to append depends");
×
853
            goto END;
×
854
        }
855
    }
856

857
    /* Match depends from artifact with device's provides */
858
    for (mender_key_value_list_t *depends_item = depends; NULL != depends_item; depends_item = depends_item->next) {
11✔
859
        bool matches = false;
×
UNCOV
860
        for (mender_key_value_list_t *provides_item = stored_provides; NULL != provides_item; provides_item = provides_item->next) {
×
861
            /* Match key-value from depends with provides */
UNCOV
862
            if (StringEqual(depends_item->key, provides_item->key)) {
×
863
                if (!StringEqual(depends_item->value, provides_item->value)) {
×
864
                    mender_log_error("Value mismatch for key '%s': depends-value '%s' does not match provides-value '%s'",
×
865
                                     depends_item->key,
866
                                     depends_item->value,
867
                                     provides_item->value);
UNCOV
868
                    break;
×
869
                }
UNCOV
870
                matches = true;
×
UNCOV
871
                break;
×
872
            }
873
        }
UNCOV
874
        if (!matches) {
×
UNCOV
875
            mender_log_error("Missing '%s:%s' in provides, required by artifact depends", depends_item->key, depends_item->value);
×
UNCOV
876
            goto END;
×
877
        }
878
    }
879

880
    ret = MENDER_OK;
11✔
881

882
END:
11✔
883
    mender_utils_key_value_list_free(stored_provides);
11✔
884
    return ret;
11✔
885
}
886
#endif /* CONFIG_MENDER_PROVIDES_DEPENDS */
887
#endif /* CONFIG_MENDER_FULL_PARSE_ARTIFACT */
888

889
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
890
static mender_err_t
891
mender_check_artifact_requirements(mender_artifact_ctx_t *mender_artifact_ctx, mender_api_deployment_data_t *deployment) {
11✔
892
    mender_err_t ret;
893

894
    /* Retrieve device type from artifact */
895
    const char *device_type_artifact = NULL;
11✔
896
    if (MENDER_OK != (ret = mender_artifact_get_device_type(mender_artifact_ctx, &device_type_artifact))) {
11✔
897
        mender_log_error("Unable to get device type from artifact");
×
898
        return ret;
×
899
    }
900

901
    mender_log_debug("Checking device type compatibility");
11✔
902

903
    /* Match device type  */
904
    if (MENDER_OK
11✔
905
        != (ret = mender_compare_device_types(device_type_artifact,
11✔
906
                                              mender_client_config.device_type,
11✔
907
                                              (const char **)deployment->device_types_compatible,
11✔
908
                                              deployment->device_types_compatible_size))) {
909
        return ret;
×
910
    }
911

912
#ifdef CONFIG_MENDER_PROVIDES_DEPENDS
913
    /* Compare Artifact's depends with the stored provides */
914
    if (MENDER_OK != (ret = mender_check_device_compatibility(mender_artifact_ctx))) {
11✔
UNCOV
915
        return ret;
×
916
    }
917
#endif /* CONFIG_MENDER_PROVIDES_DEPENDS */
918

919
    /* Check payload integrity by comparing computed checksum(s) with those
920
     * listed in the artifact manifest */
921
    if (MENDER_OK != mender_artifact_check_integrity_remaining(mender_artifact_ctx)) {
11✔
UNCOV
922
        return MENDER_FAIL;
×
923
    }
924

925
    return MENDER_OK;
11✔
926
}
927
#endif /* CONFIG_MENDER_FULL_PARSE_ARTIFACT */
928

929
static mender_err_t
930
mender_client_check_deployment(mender_api_deployment_data_t **deployment_data) {
39✔
931
    assert(NULL != deployment_data);
39✔
932

933
    if (MENDER_FAIL == mender_client_ensure_connected()) {
39✔
934
        /* network errors logged already */
UNCOV
935
        mender_log_error("Cannot check for new deployment");
×
UNCOV
936
        return MENDER_FAIL;
×
937
    }
938

939
    if (NULL == (*deployment_data = mender_calloc(1, sizeof(mender_api_deployment_data_t)))) {
39✔
UNCOV
940
        mender_log_error("Unable to allocate memory for deployment data");
×
UNCOV
941
        return MENDER_FAIL;
×
942
    }
943

944
    mender_api_deployment_data_t *deployment = *deployment_data;
39✔
945

946
    mender_err_t ret = MENDER_OK;
39✔
947

948
    mender_log_info("Checking for deployment...");
39✔
949
    if (MENDER_NOT_FOUND == (ret = mender_api_check_for_deployment(deployment))) {
39✔
950
        mender_log_info("No deployment available");
15✔
951
        return MENDER_DONE;
15✔
952
    } else if (MENDER_OK != ret) {
12✔
UNCOV
953
        mender_log_error("Unable to check for deployment");
×
NEW
954
        return ret;
×
955
    }
956

957
    /* Check if deployment is valid */
958
    if ((NULL == deployment->id) || (NULL == deployment->artifact_name) || (NULL == deployment->uri) || (NULL == deployment->device_types_compatible)) {
12✔
UNCOV
959
        mender_log_error("Invalid deployment data");
×
UNCOV
960
        return MENDER_FAIL;
×
961
    }
962

963
    /* Create deployment data */
964
    if (NULL != mender_client_deployment_data) {
12✔
UNCOV
965
        mender_log_warning("Unexpected stale deployment data");
×
UNCOV
966
        mender_delete_deployment_data(mender_client_deployment_data);
×
967
    }
968
    if (MENDER_OK != (mender_create_deployment_data(deployment->id, deployment->artifact_name, &mender_client_deployment_data))) {
12✔
969
        /* Error already logged */
970
        return MENDER_FAIL;
×
971
    }
972

973
    return MENDER_OK;
12✔
974
}
975

976
static mender_err_t
977
set_and_store_state(const mender_update_state_t state) {
79✔
978

979
    /*
980
     * Set the state in `mender_client_deployment_data` and write it to the nvs
981
     */
982

983
    mender_err_t ret = MENDER_OK;
79✔
984

985
    /* Set state in deployment data */
986
    if (MENDER_OK != (ret = mender_deployment_data_set_state(mender_client_deployment_data, state))) {
79✔
987
        mender_log_error("Failed to set deployment data state");
×
988
        return ret;
×
989
    }
990

991
    /* Store deployment data */
992
    if (MENDER_OK != (ret = mender_set_deployment_data(mender_client_deployment_data))) {
79✔
993
        mender_log_error("Failed to store deployment data");
1✔
994
        return ret;
1✔
995
    }
996
    return ret;
78✔
997
}
998

999
static mender_err_t
1000
mender_client_update_work_function(void) {
55✔
1001
    mender_err_t ret = MENDER_OK;
55✔
1002

1003
    /* Ensure that the context is initialized to NULL before goto END */
1004
    mender_artifact_ctx_t *mender_artifact_ctx = NULL;
55✔
1005

1006
    /* Check for deployment */
1007
    mender_api_deployment_data_t *deployment    = NULL;
55✔
1008
    mender_update_state_t         update_state  = MENDER_UPDATE_STATE_DOWNLOAD;
55✔
1009
    const char                   *deployment_id = NULL;
55✔
1010

1011
    /* reset the currently used update module */
1012
    mender_update_module = NULL;
55✔
1013

1014
    if (NULL != mender_client_deployment_data) {
55✔
1015
        mender_deployment_data_get_id(mender_client_deployment_data, &deployment_id);
16✔
1016
    }
1017

1018
    {
1019
        const char           *artifact_type;
1020
        mender_update_state_t update_state_resume;
1021
        if (MENDER_OK == (ret = mender_deployment_data_get_state(mender_client_deployment_data, &update_state_resume))
55✔
1022
            && MENDER_OK == mender_deployment_data_get_payload_type(mender_client_deployment_data, &artifact_type)) {
16✔
1023
            update_state = update_state_resume;
16✔
1024
            mender_log_debug("Resuming from state %s", update_state_str[update_state]);
16✔
1025

1026
#ifdef CONFIG_MENDER_DEPLOYMENT_LOGS
1027
            if (MENDER_UPDATE_STATE_DOWNLOAD != update_state) {
16✔
1028
                if (MENDER_OK != mender_deployment_logs_activate()) {
16✔
1029
                    mender_log_error("Failed to activate deployment logs saving");
1030
                    /* Not a fatal issue to abort the deployment, keep going. */
1031
                }
1032
            }
1033
#endif /* CONFIG_MENDER_DEPLOYMENT_LOGS */
1034

1035
            mender_update_module = mender_update_module_get(artifact_type);
16✔
1036
            if (NULL == mender_update_module) {
16✔
1037
                /* The artifact_type from the saved state does not match any update module */
UNCOV
1038
                mender_log_error("No update module found for artifact type '%s'", artifact_type);
×
UNCOV
1039
                mender_client_publish_deployment_status(deployment_id, MENDER_DEPLOYMENT_STATUS_FAILURE);
×
UNCOV
1040
                mender_storage_delete_deployment_data();
×
UNCOV
1041
                goto END;
×
1042
            }
1043
        }
1044
    }
1045

1046
    /* Skip the block below if we just resume from a saved state. */
1047

1048
/* A macro to advance to the next state -- on success we just keep going to the
1049
 * code below the macro invocation (fallthrough to the next case), on error we
1050
 * go to the beginning of the loop (the switch statement) again using 'continue'
1051
 * (see below).
1052
 *
1053
 * mender_update_module is guaranteed be not NULL since the first
1054
 * successful transition (from the DOWNLOAD state). */
1055
#define NEXT_STATE                                                             \
1056
    if (MENDER_OK == ret) {                                                    \
1057
        update_state = update_state_transitions[update_state].success;         \
1058
        assert(NULL != mender_update_module);                                  \
1059
        mender_log_debug("Entering state %s", update_state_str[update_state]); \
1060
        if (MENDER_LOOP_DETECTED == set_and_store_state(update_state)) {       \
1061
            update_state = MENDER_UPDATE_STATE_FAILURE;                        \
1062
        }                                                                      \
1063
    } else {                                                                   \
1064
        update_state = update_state_transitions[update_state].failure;         \
1065
        mender_log_debug("Entering state %s", update_state_str[update_state]); \
1066
        if (NULL != mender_update_module) {                                    \
1067
            if (MENDER_LOOP_DETECTED == set_and_store_state(update_state)) {   \
1068
                update_state = MENDER_UPDATE_STATE_FAILURE;                    \
1069
            }                                                                  \
1070
        }                                                                      \
1071
        ret = MENDER_OK;                                                       \
1072
        continue;                                                              \
1073
    }
1074

1075
    if (spontaneous_reboot) {
55✔
1076
        mender_log_error("Failing deployment, spontaneous reboot detected");
2✔
1077
        spontaneous_reboot = false;
2✔
1078
        update_state       = update_state_transitions[update_state].failure;
2✔
1079
        mender_log_debug("Entering state %s", update_state_str[update_state]);
2✔
1080
    }
1081
    while (MENDER_UPDATE_STATE_END != update_state) {
151✔
1082
        switch (update_state) {
85✔
1083
            case MENDER_UPDATE_STATE_DOWNLOAD:
39✔
1084
                /* This is usually logged in the NEXT_STATE macro, but since nothing
1085
                 * transitions to this state, we log it here */
1086
                mender_log_debug("Entering state %s", update_state_str[update_state]);
39✔
1087

1088
                /* Check for deployment */
1089
                if (MENDER_OK != (ret = mender_client_check_deployment(&deployment))) {
39✔
1090
                    /* No deployment available, but we are not done, we need to keep checking. */
1091
                    if (MENDER_DONE == ret) {
15✔
1092
                        ret = MENDER_OK;
15✔
1093
                    }
1094
                    goto END;
15✔
1095
                }
1096

1097
#ifdef CONFIG_MENDER_DEPLOYMENT_LOGS
1098
                if (MENDER_OK != mender_storage_deployment_log_clear()) {
12✔
1099
                    mender_log_error("Failed to clean old deployment logs");
1100
                    /* Not a fatal issue to abort the deployment, keep going. */
1101
                }
1102
                if (MENDER_OK != mender_deployment_logs_activate()) {
12✔
1103
                    mender_log_error("Failed to activate deployment logs saving");
1104
                    /* Not a fatal issue to abort the deployment, keep going. */
1105
                }
1106
#endif /* CONFIG_MENDER_DEPLOYMENT_LOGS */
1107

1108
#if CONFIG_MENDER_LOG_LEVEL >= MENDER_LOG_LEVEL_INF
1109
                if (strlen(deployment->id) > 10) {
12✔
1110
                    mender_log_info("Downloading artifact with id '%.7s...', name '%s', uri '%s'", deployment->id, deployment->artifact_name, deployment->uri);
12✔
1111
                } else {
UNCOV
1112
                    mender_log_info("Downloading artifact with id '%s', name '%s', uri '%s'", deployment->id, deployment->artifact_name, deployment->uri);
×
1113
                }
1114
#endif
1115
                /* Set deployment_id */
1116
                deployment_id = deployment->id;
12✔
1117

1118
                /* Check ret to see if the deployment is aborted */
1119
                ret = mender_client_publish_deployment_status(deployment->id, MENDER_DEPLOYMENT_STATUS_DOWNLOADING);
12✔
1120
                if ((MENDER_ABORTED != ret)
12✔
1121
                    && (MENDER_OK
12✔
1122
                        == (ret = mender_download_artifact(deployment->uri, mender_client_deployment_data, &mender_update_module, &mender_artifact_ctx)))) {
12✔
1123
                    assert(NULL != mender_update_module);
11✔
1124
                    assert(NULL != mender_artifact_ctx);
11✔
1125

1126
                    /* Get artifact context if artifact download succeeded */
1127
                    if ((NULL != mender_update_module) && (NULL != mender_artifact_ctx)) {
11✔
1128
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
1129
                        if (MENDER_OK == (ret = mender_check_artifact_requirements(mender_artifact_ctx, deployment))) {
22✔
1130
#ifdef CONFIG_MENDER_PROVIDES_DEPENDS
1131
                            /* Add the new provides to the deployment data (we need the artifact context) */
1132
                            char       *new_provides  = NULL;
11✔
1133
                            const char *artifact_name = NULL;
11✔
1134
                            if (MENDER_OK == (ret = mender_prepare_new_provides(mender_artifact_ctx, &new_provides, &artifact_name))) {
11✔
1135
                                if (MENDER_OK != (ret = mender_deployment_data_set_provides(mender_client_deployment_data, new_provides))) {
11✔
UNCOV
1136
                                    mender_log_error("Failed to set deployment data provides");
×
1137
                                }
1138
                                /* Replace artifact_name with the one from provides */
1139
                                else if (MENDER_OK != (ret = mender_deployment_data_set_artifact_name(mender_client_deployment_data, artifact_name))) {
11✔
UNCOV
1140
                                    mender_log_error("Failed to set deployment data artifact name");
×
1141
                                }
1142
                                mender_free(new_provides);
11✔
1143
                            } else {
UNCOV
1144
                                mender_log_error("Unable to prepare new provides");
×
1145
                            }
1146
#endif /* CONFIG_MENDER_PROVIDES_DEPENDS */
1147
                        } else {
UNCOV
1148
                            mender_log_error("Artifact check failed");
×
1149
                        }
1150
#endif /* CONFIG_MENDER_FULL_PARSE_ARTIFACT */
1151
                    } else {
UNCOV
1152
                        mender_log_error("Unable to get artifact type and context");
×
1153
                    }
1154
                } else {
1155
                    mender_log_error("Unable to download artifact");
1✔
1156
                    /* Error logged in mender_client_download_artifact_callback() */
1157
                    ret = MENDER_FAIL;
1✔
1158
                }
1159
                if (MENDER_OK != ret) {
12✔
1160
                    mender_client_publish_deployment_status(deployment_id, MENDER_DEPLOYMENT_STATUS_FAILURE);
1✔
1161
                }
1162
                NEXT_STATE;
12✔
1163
                /* fallthrough */
1164

1165
            case MENDER_UPDATE_STATE_INSTALL:
1166
                mender_log_info("Download done, installing artifact");
11✔
1167
                /* Check ret to see if the deployment is aborted */
1168
                ret = mender_client_publish_deployment_status(deployment_id, MENDER_DEPLOYMENT_STATUS_INSTALLING);
11✔
1169
                if ((MENDER_ABORTED != ret) && (NULL != mender_update_module->callbacks[update_state])) {
10✔
1170
                    if (mender_update_module->requires_reboot) {
9✔
1171
                        /* Save the next state before running the install callback.
1172
                         * If there is a spontaneous reboot right after the callback
1173
                         * we assure that we don't get stuck in a potential pending image in slot 1 */
1174
                        set_and_store_state(MENDER_UPDATE_STATE_REBOOT);
8✔
1175
                    }
1176
                    ret = mender_update_module->callbacks[update_state](update_state, (mender_update_state_data_t)NULL);
9✔
1177
                }
1178
                if (MENDER_OK == ret) {
10✔
1179
                    if (mender_update_module->requires_reboot) {
8✔
1180
                        update_state = MENDER_UPDATE_STATE_REBOOT;
7✔
1181
                        mender_log_debug("Entering state %s", update_state_str[update_state]);
7✔
1182
                        /* State already stored */
1183
                        continue;
7✔
1184
                    } else {
1185
                        /* skip reboot */
1186
                        update_state = MENDER_UPDATE_STATE_COMMIT;
1✔
1187
                        mender_log_debug("Entering state %s", update_state_str[update_state]);
1✔
1188
                        set_and_store_state(update_state);
1✔
1189
                        continue;
1✔
1190
                    }
1191
                }
1192
                /* else continue to the next successful/failure state */
1193
                NEXT_STATE;
2✔
1194
                /* fallthrough */
1195

1196
            case MENDER_UPDATE_STATE_REBOOT:
1197
                assert(mender_update_module->requires_reboot);
7✔
1198
                mender_log_info("Artifact installation done, rebooting");
7✔
1199
                /* Check ret to see if the deployment is aborted */
1200
                ret = mender_client_publish_deployment_status(deployment_id, MENDER_DEPLOYMENT_STATUS_REBOOTING);
7✔
1201
                if ((MENDER_ABORTED != ret) && (NULL != mender_update_module->callbacks[update_state])) {
7✔
1202
                    /* Save the next state before running the reboot callback --
1203
                     * if there is an interrupt (power, crash,...) right after,
1204
                     * it will reboot anyway so after the new boot, reboot
1205
                     * verification should happen anyway, the callback in that
1206
                     * state should be able to see if things went well or
1207
                     * wrong. */
1208
                    set_and_store_state(MENDER_UPDATE_STATE_VERIFY_REBOOT);
7✔
1209
                    ret = mender_update_module->callbacks[update_state](update_state, (mender_update_state_data_t)NULL);
7✔
1210
                    if (MENDER_OK == ret) {
7✔
1211
                        /* now we need to get outside of the loop so that a
1212
                         * potential asynchronous reboot has a chance to kick in
1213
                         * after a proper cleanup below */
1214
                        mender_client_state = MENDER_CLIENT_STATE_PENDING_REBOOT;
7✔
1215
                        ret                 = MENDER_DONE;
7✔
1216
                        goto END;
7✔
1217
                    }
1218
                }
UNCOV
1219
                NEXT_STATE;
×
1220
                /* fallthrough */
1221

1222
            case MENDER_UPDATE_STATE_VERIFY_REBOOT:
1223
                assert(mender_update_module->requires_reboot);
7✔
1224
                if (NULL != mender_update_module->callbacks[update_state]) {
7✔
1225
                    ret = mender_update_module->callbacks[update_state](update_state, (mender_update_state_data_t)NULL);
7✔
1226
                }
1227
                NEXT_STATE;
7✔
1228
                /* fallthrough */
1229

1230
            case MENDER_UPDATE_STATE_COMMIT:
1231
                /* Check for pending deployment */
1232
                if (NULL == mender_client_deployment_data) {
6✔
UNCOV
1233
                    mender_log_error("No deployment data found on commit");
×
UNCOV
1234
                    mender_client_publish_deployment_status(deployment_id, MENDER_DEPLOYMENT_STATUS_FAILURE);
×
UNCOV
1235
                    goto END;
×
1236
                }
1237
#ifdef CONFIG_MENDER_COMMIT_REQUIRE_AUTH
1238
                if (MENDER_OK != mender_api_drop_authentication_data()) {
6✔
UNCOV
1239
                    mender_log_error("Failed to drop authentication data before artifact commit");
×
1240
                    /* Unlikely (practically impossible?) to happen and if it does, we don't have
1241
                       much to about it. */
1242
                }
1243
                if (MENDER_IS_ERROR(ret = mender_api_ensure_authenticated())) {
6✔
UNCOV
1244
                    mender_log_error("Failed to authenticate before commit, rejecting the update");
×
1245
                }
1246
#endif /* CONFIG_MENDER_COMMIT_REQUIRE_AUTH */
1247
                if (!MENDER_IS_ERROR(ret) && (MENDER_OK != (ret = mender_commit_artifact_data()))) {
5✔
UNCOV
1248
                    mender_log_error("Unable to commit artifact data");
×
1249
                }
1250
                if (!MENDER_IS_ERROR(ret) && (NULL != mender_update_module->callbacks[update_state])) {
5✔
1251
                    ret = mender_update_module->callbacks[update_state](update_state, (mender_update_state_data_t)NULL);
5✔
1252
                }
1253
#ifndef CONFIG_MENDER_CLIENT_INVENTORY_DISABLE
1254
                /* If there was no reboot, we need to tell inventory to refresh
1255
                   the persistent data (because the deployment must have changed
1256
                   artifact name, at least) and we should trigger an inventory
1257
                   submission to refresh the data on the server. */
1258
                if (!mender_update_module->requires_reboot) {
5✔
1259
                    if (MENDER_OK != (ret = mender_inventory_reset_persistent())) {
1✔
1260
                        mender_log_error("Failed to reset persistent inventory after deployment commit with no reboot");
×
1261
                    } else if (MENDER_OK != (ret = mender_inventory_execute())) {
1✔
UNCOV
1262
                        mender_log_error("Failed to trigger inventory refresh after deployment commit with no reboot");
×
1263
                    }
1264
                }
1265
#endif /* CONFIG_MENDER_CLIENT_INVENTORY_DISABLE */
1266
                if (!MENDER_IS_ERROR(ret)) {
5✔
1267
                    mender_client_publish_deployment_status(deployment_id, MENDER_DEPLOYMENT_STATUS_SUCCESS);
2✔
1268
                }
1269
                NEXT_STATE;
5✔
1270
                /* fallthrough */
1271

1272
            case MENDER_UPDATE_STATE_CLEANUP:
1273
                if (NULL != mender_update_module) {
11✔
1274
                    if (NULL != mender_update_module->callbacks[update_state]) {
10✔
UNCOV
1275
                        ret = mender_update_module->callbacks[update_state](update_state, (mender_update_state_data_t)NULL);
×
1276
                    }
1277
                } else {
1278
                    ret = MENDER_FAIL;
1✔
1279
                }
1280
                NEXT_STATE;
11✔
1281
                mender_storage_delete_deployment_data();
10✔
1282
                break; /* below is the failure path */
10✔
1283

1284
            case MENDER_UPDATE_STATE_ROLLBACK:
6✔
1285
                if (!mender_update_module->supports_rollback) {
6✔
1286
                    mender_log_warning("Rollback not supported for artifacts of type '%s'", mender_update_module->artifact_type);
1✔
1287
                    ret = MENDER_FAIL;
1✔
1288
                } else if (NULL != mender_update_module->callbacks[update_state]) {
5✔
1289
                    ret = mender_update_module->callbacks[update_state](update_state, (mender_update_state_data_t)NULL);
5✔
1290
                }
1291
                NEXT_STATE;
6✔
1292
                /* fallthrough */
1293

1294
            case MENDER_UPDATE_STATE_ROLLBACK_REBOOT:
1295
                /* Save the next state before running the reboot callback (see
1296
                 * STATE_REBOOT for details). */
1297
                set_and_store_state(MENDER_UPDATE_STATE_ROLLBACK_VERIFY_REBOOT);
7✔
1298
                ret = mender_update_module->callbacks[update_state](update_state, (mender_update_state_data_t)NULL);
7✔
1299

1300
                if (MENDER_OK == ret) {
7✔
1301
                    /* now we need to get outside of the loop so that a
1302
                     * potential asynchronous reboot has a chance to kick in
1303
                     * after a proper cleanup below */
1304
                    mender_client_state = MENDER_CLIENT_STATE_PENDING_REBOOT;
7✔
1305
                    ret                 = MENDER_DONE;
7✔
1306
                    goto END;
7✔
1307
                }
UNCOV
1308
                NEXT_STATE;
×
1309
                /* fallthrough */
1310

1311
            case MENDER_UPDATE_STATE_ROLLBACK_VERIFY_REBOOT:
1312
                if (NULL != mender_update_module->callbacks[update_state]) {
7✔
1313
                    ret = mender_update_module->callbacks[update_state](update_state, (mender_update_state_data_t)NULL);
7✔
1314
                }
1315

1316
                if (MENDER_OK != ret) {
7✔
1317
                    /* If the rollback verify reboot fails,
1318
                     * we will retry the rollback reboot.
1319
                     *
1320
                     * The `rollback-reboot -> rollback-verify-reboot -> rollback-reboot -> ...`
1321
                     * loop is broken when a state loop is detected
1322
                     */
1323
                    mender_log_error("Rollback verify reboot failed. Retry rollback reboot");
4✔
1324
                }
1325

1326
                NEXT_STATE;
7✔
1327
                /* fallthrough */
1328

1329
            case MENDER_UPDATE_STATE_FAILURE:
1330
                mender_client_publish_deployment_status(deployment_id, MENDER_DEPLOYMENT_STATUS_FAILURE);
9✔
1331
                if (NULL != mender_update_module->callbacks[update_state]) {
8✔
1332
                    ret = mender_update_module->callbacks[update_state](update_state, (mender_update_state_data_t)NULL);
8✔
1333
                }
1334
                NEXT_STATE;
8✔
1335
                break; /* end of the failure path */
8✔
1336

UNCOV
1337
            case MENDER_UPDATE_STATE_END:
×
1338
                /* This is only here to cover all possible values of the
1339
                 * update_state enum, there is nothing to do here, the while
1340
                 * loop shall stop when we get here. */
UNCOV
1341
                break;
×
1342
        }
1343
    }
1344
#undef NEXT_STATE /* should not be used anywhere else */
1345

1346
    ret = MENDER_OK;
11✔
1347

1348
END:
40✔
1349
    /* Release memory */
1350
    deployment_destroy(deployment);
40✔
1351
    DESTROY_AND_NULL(mender_delete_deployment_data, mender_client_deployment_data);
40✔
1352
    mender_artifact_release_ctx(mender_artifact_ctx);
40✔
1353

1354
#ifdef CONFIG_MENDER_DEPLOYMENT_LOGS
1355
    mender_deployment_logs_deactivate();
40✔
1356
#endif /* CONFIG_MENDER_DEPLOYMENT_LOGS */
1357

1358
    return ret;
40✔
1359
}
1360

1361
static mender_err_t
1362
mender_client_publish_deployment_status(const char *id, mender_deployment_status_t deployment_status) {
42✔
1363
    if (MENDER_FAIL == mender_client_ensure_connected()) {
42✔
1364
        /* connection errors logged already */
UNCOV
1365
        mender_log_error("Cannot publish deployment status");
×
UNCOV
1366
        return MENDER_FAIL;
×
1367
    }
1368

1369
    mender_err_t ret;
1370

1371
    if (NULL == id) {
42✔
UNCOV
1372
        mender_log_error("Cannot publish deployment status: unknown status");
×
UNCOV
1373
        return MENDER_FAIL;
×
1374
    }
1375

1376
    /* Publish status to the mender server */
1377
    ret = mender_api_publish_deployment_status(id, deployment_status);
42✔
1378

1379
    /* Invoke deployment status callback if defined */
1380
    if (NULL != mender_client_callbacks.deployment_status) {
40✔
1381
        mender_client_callbacks.deployment_status(deployment_status, mender_utils_deployment_status_to_string(deployment_status));
40✔
1382
    }
1383

1384
    return ret;
40✔
1385
}
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