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

mendersoftware / mender-mcu / 1749194361

02 Apr 2025 11:15AM UTC coverage: 23.209% (-34.4%) from 57.567%
1749194361

push

gitlab-ci

web-flow
Merge pull request #179 from danielskinstad/rollback-logic

Fix rollback logic

0 of 9 new or added lines in 1 file covered. (0.0%)

960 existing lines in 10 files now uncovered.

716 of 3085 relevant lines covered (23.21%)

11.45 hits per line

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

27.63
/src/core/deployment-data.c
1
/**
2
 * @file      deployment-data.c
3
 * @brief     Mender Deployment Data interface
4
 *
5
 * Copyright Northern.tech AS
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19

20
#include "deployment-data.h"
21

22
#include "log.h"
23
#include "storage.h"
24

25
/**
26
 * @brief Deployment data version number.
27
 * @note cJSON stores numbers as double, so we might as well define this
28
 *       constant as a double to avoid type casting.
29
 */
30
#define DEPLOYMENT_DATA_VERSION 1.0
31

32
/**
33
 * @brief Deployment data version number.
34
 * @note The default is based on the maximum state transition count
35
 *       from the Mender C++ client. It's a number that won't loop
36
 *       too many times and still give some wiggle room.
37
 *       If this number is exceeded, the update will be aborted.
38
 */
39
#ifndef CONFIG_MENDER_MAX_STATE_DATA_STORE_COUNT
40
#define CONFIG_MENDER_MAX_STATE_DATA_STORE_COUNT 28
41
#endif /* CONFIG_MENDER_MAX_STATE_DATA_STORE_COUNT */
42

43
static cJSON_bool
UNCOV
44
json_is_string_or_null(const cJSON *json) {
×
UNCOV
45
    return cJSON_IsNull(json) || cJSON_IsString(json);
×
46
}
47

48
/**
49
 * @brief Validate deployment data
50
 * @param deployment_data Deployment data
51
 * @return True if valid, otherwise false
52
 */
53
static bool
UNCOV
54
validate_deployment_data(const cJSON *deployment_data) {
×
55

UNCOV
56
    assert(NULL != deployment_data);
×
57

58
    struct key_and_type {
59
        const char *const key;
60
        cJSON_bool (*type)(const cJSON *const);
61
    };
62

63
    static const struct key_and_type fields[] = {
64
        { .key = MENDER_DEPLOYMENT_DATA_KEY_VERSION, .type = cJSON_IsNumber },                /* So we can modify fields later */
65
        { .key = MENDER_DEPLOYMENT_DATA_KEY_ID, .type = json_is_string_or_null },             /* Deployment identifier */
66
        { .key = MENDER_DEPLOYMENT_DATA_KEY_ARTIFACT_NAME, .type = json_is_string_or_null },  /* Name of artifact */
67
        { .key = MENDER_DEPLOYMENT_DATA_KEY_PAYLOAD_TYPES, .type = cJSON_IsArray },           /* Types of payloads embedded in artifact */
68
        { .key = MENDER_DEPLOYMENT_DATA_KEY_PROVIDES, .type = json_is_string_or_null },       /* Artifact provides (filtered on clears provides) */
69
        { .key = MENDER_DEPLOYMENT_DATA_KEY_STATE, .type = cJSON_IsNumber },                  /* State */
70
        { .key = MENDER_DEPLOYMENT_DATA_KEY_STATE_DATA_STORE_COUNT, .type = cJSON_IsNumber }, /* State data store count */
71
    };
72

UNCOV
73
    const size_t num_fields = sizeof(fields) / sizeof(struct key_and_type);
×
UNCOV
74
    for (size_t i = 0; i < num_fields; i++) {
×
75
        const cJSON *item;
76

77
        /* Make sure the field exists */
UNCOV
78
        if (NULL == (item = cJSON_GetObjectItemCaseSensitive(deployment_data, fields[i].key))) {
×
79
            mender_log_debug("Missing key '%s' in deployment data", fields[i].key);
80
            return false;
×
81
        }
82

83
        /* Make sure the field has correct type */
UNCOV
84
        if (!fields[i].type(item)) {
×
85
            mender_log_debug("Bad type for key '%s' in deployment data", fields[i].key);
86
            return false;
×
87
        }
88

89
        /* Check version compatibility */
UNCOV
90
        if (StringEqual(fields[i].key, MENDER_DEPLOYMENT_DATA_KEY_VERSION)) {
×
91
            /* Trying to avoid floating-point precision errors */
UNCOV
92
            const double delta = (DEPLOYMENT_DATA_VERSION > cJSON_GetNumberValue(item)) ? DEPLOYMENT_DATA_VERSION - cJSON_GetNumberValue(item)
×
UNCOV
93
                                                                                        : cJSON_GetNumberValue(item) - DEPLOYMENT_DATA_VERSION;
×
UNCOV
94
            if (delta > 0.01) {
×
95
                mender_log_debug("Unsupported deployment data version");
96
                return false;
×
97
            }
98
        }
99
    }
100

UNCOV
101
    return true;
×
102
}
103

104
mender_err_t
UNCOV
105
mender_set_deployment_data(mender_deployment_data_t *deployment_data) {
×
106

UNCOV
107
    assert(NULL != deployment_data);
×
108

109
    /* Validate deployment data */
UNCOV
110
    if (!validate_deployment_data(deployment_data)) {
×
111
        mender_log_error("Invalid deployment data");
×
112
        return MENDER_FAIL;
×
113
    }
114

115
    /* Check if max state data store count is reached */
UNCOV
116
    cJSON *item = cJSON_GetObjectItemCaseSensitive(deployment_data, MENDER_DEPLOYMENT_DATA_KEY_STATE_DATA_STORE_COUNT);
×
UNCOV
117
    assert(NULL != item); /* Validation above should have catched this already */
×
UNCOV
118
    if ((double)CONFIG_MENDER_MAX_STATE_DATA_STORE_COUNT <= cJSON_GetNumberValue(item)) {
×
119
        /* Reset state data store count */
UNCOV
120
        cJSON_SetNumberValue(item, 0.0);
×
UNCOV
121
        mender_log_error("Reached max state data store count");
×
UNCOV
122
        return MENDER_LOOP_DETECTED;
×
123
    }
124

125
    /* Increment state data store count */
UNCOV
126
    cJSON_SetNumberValue(item, cJSON_GetNumberValue(item) + 1.0);
×
127

128
    /* Compose JSON string */
129
    char *json_str;
UNCOV
130
    if (NULL == (json_str = cJSON_PrintUnformatted(deployment_data))) {
×
131
        mender_log_error("Unable to compose deployment data");
×
132
        return MENDER_FAIL;
×
133
    }
134

135
    /* Write to store */
UNCOV
136
    if (MENDER_OK != mender_storage_set_deployment_data(json_str)) {
×
137
        /* Error already logged */
138
        mender_free(json_str);
×
139
        return MENDER_FAIL;
×
140
    }
UNCOV
141
    mender_free(json_str);
×
142

UNCOV
143
    return MENDER_OK;
×
144
}
145

146
mender_err_t
UNCOV
147
mender_get_deployment_data(mender_deployment_data_t **deployment_data) {
×
148

UNCOV
149
    assert(NULL != deployment_data);
×
150

151
    mender_err_t ret;
152
    char        *json_str;
153

UNCOV
154
    if (MENDER_OK != (ret = mender_storage_get_deployment_data(&json_str))) {
×
155
        /* Error already logged */
UNCOV
156
        return ret;
×
157
    }
158

159
    /* Parse deployment data from JSON string. */
UNCOV
160
    *deployment_data = cJSON_Parse(json_str);
×
UNCOV
161
    mender_free(json_str);
×
UNCOV
162
    if (NULL == deployment_data) {
×
163
        mender_log_error("Unable to parse deployment data");
×
164
        return MENDER_FAIL;
×
165
    }
166

167
    /* Validate deployment data */
UNCOV
168
    if (!validate_deployment_data(*deployment_data)) {
×
169
        mender_log_error("Invalid deployment data");
×
170
        DESTROY_AND_NULL(cJSON_Delete, *deployment_data);
×
171
        return MENDER_FAIL;
×
172
    }
173

UNCOV
174
    return MENDER_OK;
×
175
}
176

177
mender_err_t
178
mender_create_deployment_data(const char *id, const char *artifact_name, mender_deployment_data_t **deployment_data) {
13✔
179

180
    assert(NULL != deployment_data);
13✔
181

182
    cJSON *item = NULL;
13✔
183

184
    if (NULL == (*deployment_data = cJSON_CreateObject())) {
13✔
185
        goto FAIL;
×
186
    }
187

188
    /* Add version field */
189
    if (NULL == cJSON_AddNumberToObject(*deployment_data, MENDER_DEPLOYMENT_DATA_KEY_VERSION, DEPLOYMENT_DATA_VERSION)) {
13✔
190
        goto FAIL;
×
191
    }
192

193
    /* Add deployment ID field */
194
    if (NULL == (item = (NULL == id) ? cJSON_CreateNull() : cJSON_CreateString(id))) {
13✔
195
        goto FAIL;
×
196
    }
197
    if (!cJSON_AddItemToObject(*deployment_data, MENDER_DEPLOYMENT_DATA_KEY_ID, item)) {
13✔
198
        goto FAIL;
×
199
    }
200
    item = NULL;
13✔
201

202
    /* Add artifact name field */
203
    if (NULL == (item = (NULL == artifact_name) ? cJSON_CreateNull() : cJSON_CreateString(artifact_name))) {
13✔
204
        goto FAIL;
×
205
    }
206
    if (!cJSON_AddItemToObject(*deployment_data, MENDER_DEPLOYMENT_DATA_KEY_ARTIFACT_NAME, item)) {
13✔
207
        goto FAIL;
×
208
    }
209
    item = NULL;
13✔
210

211
    /* Initialize payload types field as empty array. This one needs to be populated later */
212
    if (NULL == cJSON_AddArrayToObject(*deployment_data, MENDER_DEPLOYMENT_DATA_KEY_PAYLOAD_TYPES)) {
13✔
213
        goto FAIL;
×
214
    }
215

216
    /* Add provides field */
217
    item = cJSON_CreateNull();
13✔
218
    if (!cJSON_AddItemToObject(*deployment_data, MENDER_DEPLOYMENT_DATA_KEY_PROVIDES, item)) {
13✔
219
        goto FAIL;
×
220
    }
221
    item = NULL;
13✔
222

223
    /* Add state field */
224
    item = cJSON_CreateNull();
13✔
225
    if (!cJSON_AddItemToObject(*deployment_data, MENDER_DEPLOYMENT_DATA_KEY_STATE, item)) {
13✔
226
        goto FAIL;
×
227
    }
228
    item = NULL;
13✔
229

230
    /* Initialize state data store count to zero */
231
    if (NULL == (cJSON_AddNumberToObject(*deployment_data, MENDER_DEPLOYMENT_DATA_KEY_STATE_DATA_STORE_COUNT, 0.0))) {
13✔
232
        goto FAIL;
×
233
    }
234

235
    return MENDER_OK;
13✔
236

237
FAIL:
×
238
    /* Only memory allocation errors are possible */
239
    mender_log_error("Unable to allocate memory");
×
240

241
    cJSON_Delete(item);
×
242
    cJSON_Delete(*deployment_data);
×
243
    *deployment_data = NULL;
×
244

245
    return MENDER_FAIL;
×
246
}
247

248
mender_err_t
249
__mender_deployment_data_get_string(const mender_deployment_data_t *deployment_data, const char *key, const char **str) {
16✔
250

251
    assert(NULL != key);
16✔
252
    assert(NULL != str);
16✔
253

254
    if (NULL == deployment_data) {
16✔
255
        return MENDER_NOT_FOUND;
×
256
    }
257

258
    cJSON *item;
259
    if (NULL == (item = cJSON_GetObjectItemCaseSensitive(deployment_data, key))) {
16✔
260
        return MENDER_FAIL;
×
261
    }
262

263
    *str = cJSON_GetStringValue(item);
16✔
264

265
    /* Can hold JSON null, see mender_create_deployment_data() */
266
    assert(NULL != *str || cJSON_IsNull(item));
16✔
267

268
    return MENDER_OK;
16✔
269
}
270

271
mender_err_t
UNCOV
272
__mender_deployment_data_set_string(mender_deployment_data_t *deployment_data, const char *key, const char *str) {
×
273

UNCOV
274
    assert(NULL != deployment_data);
×
UNCOV
275
    assert(NULL != key);
×
UNCOV
276
    assert(NULL != str);
×
277

278
    cJSON *item;
UNCOV
279
    if (NULL == (item = cJSON_CreateString(str))) {
×
280
        mender_log_error("Unable to allocate memory");
×
281
        return MENDER_FAIL;
×
282
    }
283

UNCOV
284
    if (!cJSON_ReplaceItemInObjectCaseSensitive(deployment_data, key, item)) {
×
285
        mender_log_error("Unable to allocate memory");
×
286
        cJSON_Delete(item);
×
287
        return MENDER_FAIL;
×
288
    }
289

UNCOV
290
    return MENDER_OK;
×
291
}
292

293
mender_err_t
UNCOV
294
mender_deployment_data_get_state(mender_deployment_data_t *deployment_data, mender_update_state_t *state) {
×
295

UNCOV
296
    if (NULL == deployment_data) {
×
UNCOV
297
        return MENDER_NOT_FOUND;
×
298
    }
299

UNCOV
300
    assert(NULL != state);
×
301

302
    cJSON *item;
UNCOV
303
    if (NULL == (item = cJSON_GetObjectItemCaseSensitive(deployment_data, MENDER_DEPLOYMENT_DATA_KEY_STATE))) {
×
304
        return MENDER_FAIL;
×
305
    }
306

307
    /* Assign state value to state pointer */
UNCOV
308
    double state_value = cJSON_GetNumberValue(item);
×
UNCOV
309
    *state             = (mender_update_state_t)state_value;
×
310

UNCOV
311
    return MENDER_OK;
×
312
}
313

314
mender_err_t
UNCOV
315
mender_deployment_data_set_state(mender_deployment_data_t *deployment_data, const mender_update_state_t state) {
×
316

UNCOV
317
    assert(NULL != deployment_data);
×
318

UNCOV
319
    if (!cJSON_ReplaceItemInObjectCaseSensitive(deployment_data, MENDER_DEPLOYMENT_DATA_KEY_STATE, cJSON_CreateNumber(state))) {
×
320
        mender_log_error("Unable to allocate memory");
×
321
        return MENDER_FAIL;
×
322
    }
UNCOV
323
    return MENDER_OK;
×
324
}
325

326
mender_err_t
327
mender_deployment_data_add_payload_type(mender_deployment_data_t *deployment_data, const char *payload_type) {
10✔
328

329
    if (NULL == deployment_data) {
10✔
330
        return MENDER_NOT_FOUND;
1✔
331
    }
332

333
    assert(NULL != payload_type);
9✔
334

335
    cJSON *types;
336
    if (NULL == (types = cJSON_GetObjectItemCaseSensitive(deployment_data, "payload_types"))) {
9✔
337
        return MENDER_FAIL;
×
338
    }
339

340
    bool   found = false;
9✔
341
    cJSON *type  = NULL;
9✔
342
    cJSON_ArrayForEach(type, types) {
9✔
343
        if (StringEqual(payload_type, cJSON_GetStringValue(type))) {
2✔
344
            found = true;
2✔
345
            break;
2✔
346
        }
347
    }
348

349
    if (!found) {
9✔
350
        if (!cJSON_AddItemToArray(types, cJSON_CreateString(payload_type))) {
7✔
351
            mender_log_error("Unable to allocate memory");
×
352
            return MENDER_FAIL;
×
353
        }
354
    }
355

356
    return MENDER_OK;
9✔
357
}
358

359
mender_err_t
UNCOV
360
mender_deployment_data_get_payload_type(const mender_deployment_data_t *deployment_data, const char **payload_type) {
×
361

UNCOV
362
    assert(NULL != payload_type);
×
363

UNCOV
364
    if (NULL == deployment_data) {
×
365
        return MENDER_NOT_FOUND;
×
366
    }
367

368
    cJSON *types;
UNCOV
369
    if (NULL == (types = cJSON_GetObjectItemCaseSensitive(deployment_data, "payload_types"))) {
×
370
        return MENDER_FAIL;
×
371
    }
372

373
    cJSON *type;
UNCOV
374
    if (NULL == (type = cJSON_GetArrayItem(types, 0))) {
×
375
        return MENDER_FAIL;
×
376
    }
UNCOV
377
    *payload_type = type->valuestring;
×
UNCOV
378
    if (NULL == *payload_type) {
×
379
        return MENDER_FAIL;
×
380
    }
381

UNCOV
382
    return MENDER_OK;
×
383
}
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