Coverage Report

Created: 2025-08-24 06:42

/src/S2OPC/src/Common/sks/sopc_sk_scheduler.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Licensed to Systerel under one or more contributor license
3
 * agreements. See the NOTICE file distributed with this work
4
 * for additional information regarding copyright ownership.
5
 * Systerel licenses this file to you under the Apache
6
 * License, Version 2.0 (the "License"); you may not use this
7
 * file except in compliance with the License. You may obtain
8
 * a copy of the License at
9
 *
10
 *   http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing,
13
 * software distributed under the License is distributed on an
14
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
 * KIND, either express or implied.  See the License for the
16
 * specific language governing permissions and limitations
17
 * under the License.
18
 */
19
20
#include <stdio.h>
21
#include <string.h>
22
23
#include "sopc_array.h"
24
#include "sopc_assert.h"
25
#include "sopc_crypto_profiles.h"
26
#include "sopc_crypto_provider.h"
27
#include "sopc_event_handler.h"
28
#include "sopc_event_timer_manager.h"
29
#include "sopc_macros.h"
30
#include "sopc_mem_alloc.h"
31
#include "sopc_mutexes.h"
32
#include "sopc_sk_scheduler.h"
33
34
#ifndef SOPC_SK_SCHEDULER_INITIAL_NB_TASKS
35
0
#define SOPC_SK_SCHEDULER_INITIAL_NB_TASKS 10
36
#endif
37
typedef struct SOPC_SKscheduler_Task
38
{
39
    SOPC_SKBuilder* builder;
40
    SOPC_SKProvider* provider;
41
    SOPC_SKManager* manager;
42
43
    bool run;
44
    uint32_t timerId;
45
    uint64_t msPeriod;
46
47
} SOPC_SKscheduler_Task;
48
49
typedef struct SOPC_SKscheduler_DefaultData
50
{
51
    bool isInitialized;
52
53
    SOPC_Looper* looper;
54
    SOPC_EventHandler* handler;
55
56
    SOPC_Array* taskArray; // Array of SOPC_SKscheduler_Task*
57
58
    SOPC_Mutex mutex;
59
60
} SOPC_SKscheduler_DefaultData;
61
62
/*** DEFAULT IMPLEMENTATION FUNCTIONS ***/
63
64
static void SOPC_SKscheduler_EventHandler_Callback_Default(SOPC_EventHandler* handler,
65
                                                           int32_t event,
66
                                                           uint32_t eltId,
67
                                                           uintptr_t params,
68
                                                           uintptr_t auxParam)
69
0
{
70
    // unused variables
71
0
    SOPC_UNUSED_ARG(handler);
72
0
    SOPC_UNUSED_ARG(event);
73
0
    SOPC_UNUSED_ARG(auxParam);
74
75
0
    SOPC_SKscheduler_DefaultData* data = (SOPC_SKscheduler_DefaultData*) params;
76
0
    SOPC_ReturnStatus status = SOPC_Mutex_Lock(&data->mutex);
77
0
    SOPC_ASSERT(SOPC_STATUS_OK == status);
78
79
0
    SOPC_SKscheduler_Task* task = SOPC_Array_Get(data->taskArray, SOPC_SKscheduler_Task*, eltId);
80
0
    SOPC_ASSERT(NULL != task);
81
82
0
    if (!task->run)
83
0
    {
84
0
        return;
85
0
    }
86
87
0
    SOPC_SKBuilder_Update(task->builder, task->provider, task->manager);
88
89
    /* Get the remaining time to use all available keys */
90
0
    uint64_t halfAllKeysLifeTime = SOPC_SKManager_GetAllKeysLifeTime(task->manager) / 2;
91
0
    if (halfAllKeysLifeTime < SOPC_SK_SCHEDULER_UPDATE_TIMER_MIN)
92
0
    {
93
0
        halfAllKeysLifeTime = SOPC_SK_SCHEDULER_UPDATE_TIMER_MIN;
94
0
    }
95
0
    else if (halfAllKeysLifeTime > SOPC_SK_SCHEDULER_UPDATE_TIMER_MAX)
96
0
    {
97
0
        halfAllKeysLifeTime = SOPC_SK_SCHEDULER_UPDATE_TIMER_MAX;
98
0
    }
99
100
    // Replace the timer by a new one with expiration time updated ( half the Keys Life Time )
101
0
    if (task->run)
102
0
    {
103
0
        SOPC_LooperEvent timerEvent = {.event = 0, .eltId = eltId, .params = (uintptr_t) data};
104
0
        uint32_t timerId = SOPC_EventTimer_Create(handler, timerEvent, halfAllKeysLifeTime);
105
0
        if (0 != timerId)
106
0
        {
107
0
            task->msPeriod = halfAllKeysLifeTime;
108
0
            task->timerId = timerId;
109
0
        }
110
0
        else
111
0
        {
112
0
            task->run = false;
113
0
        }
114
0
    }
115
116
0
    status = SOPC_Mutex_Unlock(&data->mutex);
117
0
    SOPC_ASSERT(SOPC_STATUS_OK == status);
118
0
}
119
120
static SOPC_ReturnStatus SOPC_SKscheduler_Initialize_Default(SOPC_SKscheduler* sko)
121
0
{
122
0
    if (NULL == sko || NULL == sko->data)
123
0
    {
124
0
        return SOPC_STATUS_INVALID_PARAMETERS;
125
0
    }
126
127
0
    SOPC_SKscheduler_DefaultData* data = (SOPC_SKscheduler_DefaultData*) sko->data;
128
129
0
    if (data->isInitialized)
130
0
    {
131
0
        return SOPC_STATUS_OK;
132
0
    }
133
134
0
    SOPC_ReturnStatus status = SOPC_STATUS_OK;
135
136
    // Initialize event timer if not done
137
0
    SOPC_EventTimer_Initialize();
138
139
    // Create an Event Handler in a dedicated Thread.
140
0
    data->looper = SOPC_Looper_Create("Security Keys Looper");
141
0
    if (NULL == data->looper)
142
0
    {
143
0
        status = SOPC_STATUS_NOK;
144
0
    }
145
146
0
    if (SOPC_STATUS_OK == status)
147
0
    {
148
0
        data->handler = SOPC_EventHandler_Create(data->looper, SOPC_SKscheduler_EventHandler_Callback_Default);
149
0
        if (NULL == data->handler)
150
0
        {
151
0
            status = SOPC_STATUS_NOK;
152
0
        }
153
0
    }
154
155
0
    if (SOPC_STATUS_OK == status)
156
0
    {
157
0
        data->isInitialized = true;
158
0
    }
159
0
    else
160
0
    {
161
0
        data->isInitialized = false;
162
0
        if (NULL != data->looper)
163
0
        {
164
0
            SOPC_Looper_Delete(data->looper);
165
0
            data->looper = NULL;
166
0
        }
167
0
    }
168
0
    return status;
169
0
}
170
171
static SOPC_ReturnStatus SOPC_SKscheduler_AddTask_Default(SOPC_SKscheduler* sko,
172
                                                          SOPC_SKBuilder* skb,
173
                                                          SOPC_SKProvider* skp,
174
                                                          SOPC_SKManager* skm,
175
                                                          uint64_t msPeriod)
176
0
{
177
0
    if (NULL == sko || NULL == sko->data || NULL == skb || NULL == skp || NULL == skm)
178
0
    {
179
0
        return SOPC_STATUS_INVALID_PARAMETERS;
180
0
    }
181
182
0
    SOPC_SKscheduler_DefaultData* data = (SOPC_SKscheduler_DefaultData*) sko->data;
183
184
0
    SOPC_ReturnStatus mutStatus = SOPC_Mutex_Lock(&data->mutex);
185
0
    SOPC_ASSERT(SOPC_STATUS_OK == mutStatus);
186
187
0
    SOPC_SKscheduler_Task* newTask = SOPC_Calloc(1, sizeof(SOPC_SKscheduler_Task));
188
0
    SOPC_ReturnStatus status = NULL != newTask ? SOPC_STATUS_OK : SOPC_STATUS_OUT_OF_MEMORY;
189
190
0
    if (SOPC_STATUS_OK == status)
191
0
    {
192
0
        newTask->builder = skb;
193
0
        newTask->provider = skp;
194
0
        newTask->manager = skm;
195
0
        newTask->msPeriod = msPeriod;
196
0
        newTask->run = false;
197
198
        // Add the new task to the array
199
0
        bool res = SOPC_Array_Append(data->taskArray, newTask);
200
0
        if (!res)
201
0
        {
202
0
            status = SOPC_STATUS_OUT_OF_MEMORY;
203
0
        }
204
0
    }
205
206
0
    mutStatus = SOPC_Mutex_Unlock(&data->mutex);
207
0
    SOPC_ASSERT(SOPC_STATUS_OK == mutStatus);
208
209
0
    if (SOPC_STATUS_OK != status)
210
0
    {
211
        // Delete the task in case of error
212
0
        SOPC_Free(newTask);
213
0
    }
214
0
    else
215
0
    {
216
0
        newTask->builder->referencesCounter++;
217
0
        newTask->provider->referencesCounter++;
218
0
    }
219
220
0
    return status;
221
0
}
222
223
static SOPC_ReturnStatus SOPC_SKscheduler_Start_Default(SOPC_SKscheduler* sko)
224
0
{
225
0
    if (NULL == sko || NULL == sko->data)
226
0
    {
227
0
        return SOPC_STATUS_INVALID_PARAMETERS;
228
0
    }
229
230
0
    SOPC_SKscheduler_DefaultData* data = (SOPC_SKscheduler_DefaultData*) sko->data;
231
232
0
    SOPC_ReturnStatus mutStatus = SOPC_Mutex_Lock(&data->mutex);
233
0
    SOPC_ASSERT(SOPC_STATUS_OK == mutStatus);
234
235
0
    SOPC_ReturnStatus status = SOPC_SKscheduler_Initialize_Default(sko);
236
237
0
    size_t nbTasks = SOPC_Array_Size(data->taskArray);
238
0
    for (size_t i = 0; SOPC_STATUS_OK == status && i < nbTasks; i++)
239
0
    {
240
0
        SOPC_SKscheduler_Task* task = SOPC_Array_Get(data->taskArray, SOPC_SKscheduler_Task*, i);
241
0
        status = NULL != task ? status : SOPC_STATUS_NOK;
242
0
        status = i <= UINT32_MAX ? status : SOPC_STATUS_OUT_OF_MEMORY;
243
0
        if (SOPC_STATUS_OK == status)
244
0
        {
245
0
            SOPC_LooperEvent event = {.event = 0, .eltId = (uint32_t) i, .params = (uintptr_t) data};
246
0
            task->timerId = SOPC_EventTimer_Create(data->handler, event, task->msPeriod);
247
0
            task->run = (0 != task->timerId);
248
0
            if (!task->run)
249
0
            {
250
0
                status = SOPC_STATUS_NOK;
251
0
            }
252
0
        }
253
0
    }
254
0
    mutStatus = SOPC_Mutex_Unlock(&data->mutex);
255
0
    SOPC_ASSERT(SOPC_STATUS_OK == mutStatus);
256
257
0
    return status;
258
0
}
259
260
static void SOPC_SKscheduler_StopAndClear_Default(SOPC_SKscheduler* sko)
261
0
{
262
0
    if (NULL == sko)
263
0
    {
264
0
        return;
265
0
    }
266
267
0
    SOPC_SKscheduler_DefaultData* data = (SOPC_SKscheduler_DefaultData*) sko->data;
268
269
    // delete looper => delete handler
270
    // OK to execute out of the mutex since it will stop any execution of the looper / handler after poison pill event
271
0
    SOPC_Looper_Delete(data->looper);
272
0
    data->looper = NULL;
273
0
    data->handler = NULL;
274
275
0
    SOPC_ReturnStatus status = SOPC_Mutex_Lock(&data->mutex);
276
0
    SOPC_ASSERT(SOPC_STATUS_OK == status);
277
278
    // Clear the task array
279
0
    SOPC_Array_Delete(data->taskArray);
280
281
0
    status = SOPC_Mutex_Unlock(&data->mutex);
282
0
    SOPC_ASSERT(SOPC_STATUS_OK == status);
283
284
0
    status = SOPC_Mutex_Clear(&data->mutex);
285
0
    SOPC_ASSERT(SOPC_STATUS_OK == status);
286
287
0
    SOPC_Free(sko->data);
288
0
    sko->data = NULL;
289
0
}
290
291
/*** API FUNCTIONS ***/
292
293
static void free_task(void* vtask)
294
0
{
295
0
    SOPC_ASSERT(NULL != vtask);
296
0
    SOPC_SKscheduler_Task** ppTask = (SOPC_SKscheduler_Task**) vtask;
297
0
    SOPC_ASSERT(NULL != *ppTask);
298
0
    SOPC_SKscheduler_Task* task = *ppTask;
299
300
    // Cancel associated timer
301
0
    SOPC_EventTimer_Cancel(task->timerId);
302
303
0
    SOPC_SKBuilder_MayDelete(&task->builder);
304
305
0
    SOPC_SKProvider_MayDelete(&task->provider);
306
307
0
    SOPC_Free(task);
308
0
}
309
310
SOPC_SKscheduler* SOPC_SKscheduler_Create(void)
311
0
{
312
0
    SOPC_SKscheduler* sko = SOPC_Calloc(1, sizeof(SOPC_SKscheduler));
313
0
    if (NULL == sko)
314
0
    {
315
0
        return NULL;
316
0
    }
317
318
0
    sko->data = SOPC_Calloc(1, sizeof(SOPC_SKscheduler_DefaultData));
319
0
    if (NULL == sko->data)
320
0
    {
321
0
        SOPC_Free(sko);
322
0
        return NULL;
323
0
    }
324
0
    SOPC_SKscheduler_DefaultData* data = (SOPC_SKscheduler_DefaultData*) sko->data;
325
0
    data->isInitialized = false;
326
0
    data->taskArray = SOPC_Array_Create(sizeof(SOPC_SKscheduler_Task*), SOPC_SK_SCHEDULER_INITIAL_NB_TASKS, free_task);
327
0
    if (NULL == data->taskArray)
328
0
    {
329
0
        SOPC_Free(sko->data);
330
0
        SOPC_Free(sko);
331
0
        return NULL;
332
0
    }
333
334
0
    SOPC_Mutex_Initialization(&data->mutex);
335
336
0
    sko->ptrAddTask = SOPC_SKscheduler_AddTask_Default;
337
0
    sko->ptrStart = SOPC_SKscheduler_Start_Default;
338
0
    sko->ptrClear = SOPC_SKscheduler_StopAndClear_Default;
339
340
0
    return sko;
341
0
}
342
343
SOPC_ReturnStatus SOPC_SKscheduler_AddTask(SOPC_SKscheduler* sko,
344
                                           SOPC_SKBuilder* skb,
345
                                           SOPC_SKProvider* skp,
346
                                           SOPC_SKManager* skm,
347
                                           uint64_t msPeriod)
348
0
{
349
0
    if (NULL == sko)
350
0
    {
351
0
        return SOPC_STATUS_INVALID_PARAMETERS;
352
0
    }
353
0
    return sko->ptrAddTask(sko, skb, skp, skm, msPeriod);
354
0
}
355
356
SOPC_ReturnStatus SOPC_SKscheduler_Start(SOPC_SKscheduler* sko)
357
0
{
358
0
    if (NULL == sko)
359
0
    {
360
0
        return SOPC_STATUS_INVALID_PARAMETERS;
361
0
    }
362
0
    return sko->ptrStart(sko);
363
0
}
364
365
void SOPC_SKscheduler_StopAndClear(SOPC_SKscheduler* sko)
366
0
{
367
0
    if (NULL == sko || NULL == sko->ptrClear)
368
0
    {
369
0
        return;
370
0
    }
371
0
    sko->ptrClear(sko);
372
0
}