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

mendersoftware / mender-mcu / 1688394821

21 Feb 2025 09:23PM UTC coverage: 25.848% (-0.03%) from 25.875%
1688394821

push

gitlab-ci

web-flow
Merge pull request #153 from vpodzime/master-update_mem_leaks

MEN-8057: Memory leaks fixes (round 2)

0 of 3 new or added lines in 2 files covered. (0.0%)

2 existing lines in 1 file now uncovered.

739 of 2859 relevant lines covered (25.85%)

8.53 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 cJSON stores numbers as double, so we might as well define this
35
 *       constant as a double to avoid type casting.
36
 *       This number is based on the maximum state transition count
37
 *       from the Mender C++ client. It's a number that won't loop
38
 *       too many times and still give some wiggle room.
39
 *       If this number is exceeded, the update will be aborted.
40
 */
41
#define MAX_STATE_DATA_STORE_COUNT 28.0
42

43
static cJSON_bool
44
json_is_string_or_null(const cJSON *json) {
×
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
54
validate_deployment_data(const cJSON *deployment_data) {
×
55

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

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

77
        /* Make sure the field exists */
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 */
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 */
90
        if (StringEqual(fields[i].key, MENDER_DEPLOYMENT_DATA_KEY_VERSION)) {
×
91
            /* Trying to avoid floating-point precision errors */
92
            const double delta = (DEPLOYMENT_DATA_VERSION > cJSON_GetNumberValue(item)) ? DEPLOYMENT_DATA_VERSION - cJSON_GetNumberValue(item)
×
93
                                                                                        : cJSON_GetNumberValue(item) - DEPLOYMENT_DATA_VERSION;
×
94
            if (delta > 0.01) {
×
95
                mender_log_debug("Unsupported deployment data version");
96
                return false;
×
97
            }
98
        }
99
    }
100

101
    return true;
×
102
}
103

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

107
    assert(NULL != deployment_data);
×
108

109
    /* Validate deployment data */
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 */
116
    cJSON *item = cJSON_GetObjectItemCaseSensitive(deployment_data, MENDER_DEPLOYMENT_DATA_KEY_STATE_DATA_STORE_COUNT);
×
117
    assert(NULL != item); /* Validation above should have catched this already */
×
118
    if (MAX_STATE_DATA_STORE_COUNT <= cJSON_GetNumberValue(item)) {
×
119
        /* Reset state data store count */
120
        cJSON_SetNumberValue(item, 0.0);
×
121
        mender_log_error("Reached max state data store count");
×
122
        return MENDER_LOOP_DETECTED;
×
123
    }
124

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

128
    /* Compose JSON string */
129
    char *json_str;
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 */
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
    }
NEW
141
    mender_free(json_str);
×
142

143
    return MENDER_OK;
×
144
}
145

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

149
    assert(NULL != deployment_data);
×
150

151
    mender_err_t ret;
152
    char        *json_str;
153

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

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

167
    /* Validate deployment data */
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

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) {
11✔
179

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

182
    cJSON *item = NULL;
11✔
183

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

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

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

202
    /* Add artifact name field */
203
    if (NULL == (item = (NULL == artifact_name) ? cJSON_CreateNull() : cJSON_CreateString(artifact_name))) {
11✔
204
        goto FAIL;
×
205
    }
206
    if (!cJSON_AddItemToObject(*deployment_data, MENDER_DEPLOYMENT_DATA_KEY_ARTIFACT_NAME, item)) {
11✔
207
        goto FAIL;
×
208
    }
209
    item = NULL;
11✔
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)) {
11✔
213
        goto FAIL;
×
214
    }
215

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

223
    /* Add state field */
224
    item = cJSON_CreateNull();
11✔
225
    if (!cJSON_AddItemToObject(*deployment_data, MENDER_DEPLOYMENT_DATA_KEY_STATE, item)) {
11✔
226
        goto FAIL;
×
227
    }
228
    item = NULL;
11✔
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))) {
11✔
232
        goto FAIL;
×
233
    }
234

235
    return MENDER_OK;
11✔
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) {
12✔
250

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

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

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

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

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

268
    return MENDER_OK;
12✔
269
}
270

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

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

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

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

290
    return MENDER_OK;
×
291
}
292

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

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

300
    assert(NULL != state);
×
301

302
    cJSON *item;
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 */
308
    double state_value = cJSON_GetNumberValue(item);
×
309
    *state             = (mender_update_state_t)state_value;
×
310

311
    return MENDER_OK;
×
312
}
313

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

317
    assert(NULL != deployment_data);
×
318

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
    }
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) {
7✔
328

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

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

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

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

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

356
    return MENDER_OK;
6✔
357
}
358

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

362
    assert(NULL != payload_type);
×
363

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

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

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

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

© 2025 Coveralls, Inc