1/*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15#include <ctype.h>
16#include <errno.h>
17#include <stdio.h>
18#include <string.h>
19#include <sys/socket.h>
20
21#include "init_log.h"
22#include "init_param.h"
23#include "init_utils.h"
24#include "loop_event.h"
25#include "param_manager.h"
26#include "param_message.h"
27#include "trigger_manager.h"
28#include "securec.h"
29#ifdef PARAM_SUPPORT_SELINUX
30#include "selinux_parameter.h"
31#include <policycoreutils.h>
32#include <selinux/selinux.h>
33#endif
34
35static ParamService g_paramService = {};
36
37PARAM_STATIC void OnClose(ParamTaskPtr client)
38{
39    ParamWatcher *watcher = (ParamWatcher *)ParamGetTaskUserData(client);
40    if (client == g_paramService.watcherTask) {
41        ClearWatchTrigger(watcher, TRIGGER_PARAM_WATCH);
42        g_paramService.watcherTask = NULL;
43    } else {
44        ClearWatchTrigger(watcher, TRIGGER_PARAM_WAIT);
45    }
46}
47
48static void TimerCallback(const ParamTaskPtr timer, void *context)
49{
50    UNUSED(context);
51    UNUSED(timer);
52    int ret = CheckWatchTriggerTimeout();
53    // no wait node
54    if (ret == 0 && g_paramService.timer != NULL) {
55        ParamTaskClose(g_paramService.timer);
56        g_paramService.timer = NULL;
57    }
58}
59
60static void CheckAndSendTrigger(uint32_t dataIndex, const char *name, const char *value)
61{
62    WorkSpace *workspace = GetWorkSpaceByName(name);
63    PARAM_CHECK(workspace != NULL, return, "Failed to get workspace %s ", name);
64    ParamNode *entry = (ParamNode *)GetTrieNode(workspace, dataIndex);
65    PARAM_CHECK(entry != NULL, return, "Failed to get data %s ", name);
66    uint32_t trigger = 1;
67    if ((ATOMIC_LOAD_EXPLICIT(&entry->commitId, MEMORY_ORDER_RELAXED) & PARAM_FLAGS_TRIGGED) != PARAM_FLAGS_TRIGGED) {
68        trigger = (CheckAndMarkTrigger(TRIGGER_PARAM, name) != 0) ? 1 : 0;
69    }
70    if (trigger) {
71        ATOMIC_SYNC_OR_AND_FETCH(&entry->commitId, PARAM_FLAGS_TRIGGED, MEMORY_ORDER_RELEASE);
72        // notify event to process trigger
73        PostParamTrigger(EVENT_TRIGGER_PARAM, name, value);
74    }
75
76    int wait = 1;
77    if ((ATOMIC_LOAD_EXPLICIT(&entry->commitId, MEMORY_ORDER_RELAXED) & PARAM_FLAGS_WAITED) != PARAM_FLAGS_WAITED) {
78        wait = (CheckAndMarkTrigger(TRIGGER_PARAM_WAIT, name) != 0) ? 1 : 0;
79    }
80    if (wait) {
81        ATOMIC_SYNC_OR_AND_FETCH(&entry->commitId, PARAM_FLAGS_WAITED, MEMORY_ORDER_RELEASE);
82        PostParamTrigger(EVENT_TRIGGER_PARAM_WAIT, name, value);
83    }
84    PostParamTrigger(EVENT_TRIGGER_PARAM_WATCH, name, value);
85}
86
87static int SendResponseMsg(ParamTaskPtr worker, const ParamMessage *msg, int result)
88{
89    ParamResponseMessage *response = NULL;
90    response = (ParamResponseMessage *)CreateParamMessage(msg->type, msg->key, sizeof(ParamResponseMessage));
91    PARAM_CHECK(response != NULL, return PARAM_CODE_ERROR, "Failed to alloc memory for response");
92    response->msg.id.msgId = msg->id.msgId;
93    response->result = result;
94    response->msg.msgSize = sizeof(ParamResponseMessage);
95    ParamTaskSendMsg(worker, (ParamMessage *)response);
96    PARAM_LOGV("Send response msg msgId %d result %d", msg->id.msgId, result);
97    return 0;
98}
99
100static int SendWatcherNotifyMessage(const TriggerExtInfo *extData, const char *content, uint32_t size)
101{
102    PARAM_CHECK(content != NULL, return -1, "Invalid content");
103    PARAM_CHECK(extData != NULL && extData->stream != NULL, return -1, "Invalid extData");
104    uint32_t msgSize = sizeof(ParamMessage) + PARAM_ALIGN(strlen(content) + 1);
105    ParamMessage *msg = (ParamMessage *)CreateParamMessage(MSG_NOTIFY_PARAM, "*", msgSize);
106    PARAM_CHECK(msg != NULL, return -1, "Failed to create msg ");
107
108    uint32_t offset = 0;
109    int ret;
110    char *tmp = strstr(content, "=");
111    if (tmp != NULL) {
112        ret = strncpy_s(msg->key, sizeof(msg->key) - 1, content, tmp - content);
113        PARAM_CHECK(ret == 0, free(msg);
114            return -1, "Failed to fill value");
115        tmp++;
116        ret = FillParamMsgContent(msg, &offset, PARAM_VALUE, tmp, strlen(tmp));
117        PARAM_CHECK(ret == 0, free(msg);
118            return -1, "Failed to fill value");
119    } else if (content != NULL && strlen(content) > 0) {
120        ret = FillParamMsgContent(msg, &offset, PARAM_VALUE, content, strlen(content));
121        PARAM_CHECK(ret == 0, free(msg);
122            return -1, "Failed to fill value");
123    }
124
125    msg->id.msgId = 0;
126    if (extData->type == TRIGGER_PARAM_WAIT) {
127        msg->id.msgId = extData->info.waitInfo.waitId;
128    } else {
129        msg->id.msgId = extData->info.watchInfo.watchId;
130    }
131    msg->msgSize = sizeof(ParamMessage) + offset;
132    PARAM_LOGV("SendWatcherNotifyMessage cmd %s, id %d msgSize %d para: %s",
133        (extData->type == TRIGGER_PARAM_WAIT) ? "wait" : "watcher",
134        msg->id.msgId, msg->msgSize, content);
135    ParamTaskSendMsg(extData->stream, msg);
136    return 0;
137}
138
139static int SystemSetParam(const char *name, const char *value, const ParamSecurityLabel *srcLabel)
140{
141    PARAM_LOGV("SystemWriteParam name %s value: %s", name, value);
142    int ctrlService = 0;
143    int ret = CheckParameterSet(name, value, srcLabel, &ctrlService);
144    PARAM_CHECK(ret == 0, return ret, "Forbid to set parameter %s", name);
145
146    unsigned int mode = 0;
147    if (strncmp(name, PARAM_PERSIST_PREFIX, strlen(PARAM_PERSIST_PREFIX)) == 0) {
148        mode |= LOAD_PARAM_PERSIST;
149    }
150    if ((ctrlService & PARAM_CTRL_SERVICE) != PARAM_CTRL_SERVICE) { // ctrl param
151        uint32_t dataIndex = 0;
152        ret = WriteParam(name, value, &dataIndex, mode);
153        PARAM_CHECK(ret == 0, return ret, "Failed to set param %d name %s %s", ret, name, value);
154        ret = WritePersistParam(name, value);
155        PARAM_CHECK(ret == 0, return ret, "Failed to set persist param name %s", name);
156        CheckAndSendTrigger(dataIndex, name, value);
157    }
158    return ret;
159}
160
161static int HandleParamSet(const ParamTaskPtr worker, const ParamMessage *msg)
162{
163    uint32_t offset = 0;
164    ParamMsgContent *valueContent = GetNextContent(msg, &offset);
165    PARAM_CHECK(valueContent != NULL, return -1, "Invalid msg for %s", msg->key);
166    ParamSecurityLabel srcLabel = {0};
167    struct ucred cr = {-1, -1, -1};
168    socklen_t crSize = sizeof(cr);
169    if (getsockopt(LE_GetSocketFd(worker), SOL_SOCKET, SO_PEERCRED, &cr, &crSize) < 0) {
170        PARAM_LOGE("Failed to get opt %d", errno);
171#ifndef STARTUP_INIT_TEST
172        return SendResponseMsg(worker, msg, -1);
173#endif
174    }
175    srcLabel.sockFd = LE_GetSocketFd(worker);
176    srcLabel.cred.uid = cr.uid;
177    srcLabel.cred.pid = cr.pid;
178    srcLabel.cred.gid = cr.gid;
179    PARAM_LOGI("Handle set param msgId %d pid %d key: %s", msg->id.msgId, cr.pid, msg->key);
180    int ret = SystemSetParam(msg->key, valueContent->content, &srcLabel);
181    return SendResponseMsg(worker, msg, ret);
182}
183
184static int32_t AddWatchNode(struct tagTriggerNode_ *trigger, const struct TriggerExtInfo_ *extInfo)
185{
186    ParamWatcher *watcher = NULL;
187    if (extInfo != NULL && extInfo->stream != NULL) {
188        watcher = (ParamWatcher *)ParamGetTaskUserData(extInfo->stream);
189    }
190    PARAM_CHECK(watcher != NULL, return -1, "Failed to get param watcher data");
191    if (extInfo->type == TRIGGER_PARAM_WAIT) {
192        WaitNode *node = (WaitNode *)trigger;
193        OH_ListInit(&node->item);
194        node->timeout = extInfo->info.waitInfo.timeout;
195        node->stream = extInfo->stream;
196        node->waitId = extInfo->info.waitInfo.waitId;
197        OH_ListAddTail(&watcher->triggerHead, &node->item);
198    } else {
199        WatchNode *node = (WatchNode *)trigger;
200        OH_ListInit(&node->item);
201        node->watchId = extInfo->info.watchInfo.watchId;
202        OH_ListAddTail(&watcher->triggerHead, &node->item);
203    }
204    return 0;
205}
206
207static TriggerNode *AddWatcherTrigger(int triggerType, const char *condition, const TriggerExtInfo *extData)
208{
209    TriggerWorkSpace *workSpace = GetTriggerWorkSpace();
210    TriggerHeader *header = (TriggerHeader *)&workSpace->triggerHead[extData->type];
211    return header->addTrigger(workSpace, condition, extData);
212}
213
214static int32_t ExecuteWatchTrigger_(const struct tagTriggerNode_ *trigger, const char *content, uint32_t size)
215{
216    TriggerExtInfo extData = {};
217    extData.type = trigger->type;
218    if (trigger->type == TRIGGER_PARAM_WAIT) {
219        WaitNode *node = (WaitNode *)trigger;
220        extData.stream = node->stream;
221        extData.info.waitInfo.waitId = node->waitId;
222        extData.info.waitInfo.timeout = node->timeout;
223    } else {
224        WatchNode *node = (WatchNode *)trigger;
225        extData.stream = g_paramService.watcherTask;
226        extData.info.watchInfo.watchId = node->watchId;
227    }
228    if (content == NULL) {
229        return SendWatcherNotifyMessage(&extData, "", 0);
230    }
231    return SendWatcherNotifyMessage(&extData, content, size);
232}
233
234static int HandleParamWaitAdd(const ParamTaskPtr worker, const ParamMessage *msg)
235{
236    PARAM_CHECK(msg != NULL, return -1, "Invalid message");
237    uint32_t offset = 0;
238#ifndef STARTUP_INIT_TEST
239    uint32_t timeout = DEFAULT_PARAM_WAIT_TIMEOUT;
240#else
241    uint32_t timeout = 0;
242#endif
243    ParamMsgContent *valueContent = GetNextContent(msg, &offset);
244    PARAM_CHECK(valueContent != NULL, return -1, "Invalid msg");
245    PARAM_CHECK(valueContent->contentSize <= PARAM_CONST_VALUE_LEN_MAX, return -1, "Invalid msg");
246    ParamMsgContent *timeoutContent = GetNextContent(msg, &offset);
247    if (timeoutContent != NULL) {
248        timeout = *((uint32_t *)(timeoutContent->content));
249    }
250
251    PARAM_LOGV("HandleParamWaitAdd name %s timeout %d", msg->key, timeout);
252    TriggerExtInfo extData = {};
253    extData.addNode = AddWatchNode;
254    extData.type = TRIGGER_PARAM_WAIT;
255    extData.stream = worker;
256    extData.info.waitInfo.waitId = msg->id.watcherId;
257    extData.info.waitInfo.timeout = timeout;
258    // first check match, if match send response to client
259    ParamNode *param = SystemCheckMatchParamWait(msg->key, valueContent->content);
260    if (param != NULL) {
261        SendWatcherNotifyMessage(&extData, param->data, param->valueLength);
262        return 0;
263    }
264
265    uint32_t buffSize = strlen(msg->key) + valueContent->contentSize + 1 + 1;
266    char *condition = calloc(1, buffSize);
267    PARAM_CHECK(condition != NULL, return -1, "Failed to create condition for %s", msg->key);
268    int ret = sprintf_s(condition, buffSize - 1, "%s=%s", msg->key, valueContent->content);
269    PARAM_CHECK(ret > EOK, free(condition);
270        return -1, "Failed to copy name for %s", msg->key);
271    TriggerNode *trigger = AddWatcherTrigger(TRIGGER_PARAM_WAIT, condition, &extData);
272    PARAM_CHECK(trigger != NULL, free(condition);
273        return -1, "Failed to add trigger for %s", msg->key);
274    free(condition);
275    if (g_paramService.timer == NULL) {
276        ret = ParamTimerCreate(&g_paramService.timer, TimerCallback, &g_paramService);
277        PARAM_CHECK(ret == 0, return 0, "Failed to create timer %s", msg->key);
278        ret = ParamTimerStart(g_paramService.timer, MS_UNIT, (uint64_t)-1);
279        PARAM_CHECK(ret == 0,
280            ParamTaskClose(g_paramService.timer);
281            g_paramService.timer = NULL;
282            return 0, "Failed to set timer %s", msg->key);
283    }
284    return 0;
285}
286
287static int HandleParamWatcherAdd(const ParamTaskPtr worker, const ParamMessage *msg)
288{
289    PARAM_CHECK(msg != NULL, return -1, "Invalid message");
290    PARAM_CHECK((g_paramService.watcherTask == NULL) ||
291        (g_paramService.watcherTask == worker), return -1, "Invalid watcher worker");
292    g_paramService.watcherTask = worker;
293    TriggerExtInfo extData = {};
294    extData.type = TRIGGER_PARAM_WATCH;
295    extData.addNode = AddWatchNode;
296    extData.stream = worker;
297    extData.info.watchInfo.watchId = msg->id.watcherId;
298    TriggerNode *trigger = AddWatcherTrigger(TRIGGER_PARAM_WATCH, msg->key, &extData);
299    if (trigger == NULL) {
300        PARAM_LOGE("Failed to add trigger for %s", msg->key);
301        return SendResponseMsg(worker, msg, -1);
302    }
303    PARAM_LOGV("HandleParamWatcherAdd name %s watcher: %d", msg->key, msg->id.watcherId);
304    return SendResponseMsg(worker, msg, 0);
305}
306
307static int HandleParamWatcherDel(const ParamTaskPtr worker, const ParamMessage *msg)
308{
309    PARAM_CHECK(msg != NULL, return -1, "Invalid message");
310    PARAM_LOGV("HandleParamWatcherDel name %s watcher: %d", msg->key, msg->id.watcherId);
311    DelWatchTrigger(TRIGGER_PARAM_WATCH, (const void *)&msg->id.watcherId);
312    return SendResponseMsg(worker, msg, 0);
313}
314
315static int HandleParamSave(const ParamTaskPtr worker, const ParamMessage *msg)
316{
317    PARAM_CHECK(msg != NULL, return -1, "Invalid message");
318    struct ucred cr = {-1, -1, -1};
319    socklen_t crSize = sizeof(cr);
320    if (getsockopt(LE_GetSocketFd(worker), SOL_SOCKET, SO_PEERCRED, &cr, &crSize) < 0) {
321        PARAM_LOGE("Failed to get opt %d", errno);
322#ifndef STARTUP_INIT_TEST
323        return SendResponseMsg(worker, msg, -1);
324#endif
325    }
326    PARAM_LOGI("process info:pid = %d, uid = %d, gid = %d", cr.pid, cr.uid, cr.gid);
327    int ret = CheckIfUidInGroup(cr.uid, "servicectrl");
328    PARAM_CHECK(ret == 0, return SendResponseMsg(worker, msg, -1), "Failed to process save parameters : ret %d", ret);
329    CheckAndSavePersistParam();
330    return SendResponseMsg(worker, msg, 0);
331}
332
333PARAM_STATIC int ProcessMessage(const ParamTaskPtr worker, const ParamMessage *msg)
334{
335    PARAM_CHECK((msg != NULL) && (msg->msgSize >= sizeof(ParamMessage)), return -1, "Invalid msg");
336    PARAM_CHECK(worker != NULL, return -1, "Invalid worker");
337    int ret = PARAM_CODE_INVALID_PARAM;
338    switch (msg->type) {
339        case MSG_SET_PARAM:
340            ret = HandleParamSet(worker, msg);
341            break;
342        case MSG_WAIT_PARAM:
343            ret = HandleParamWaitAdd(worker, msg);
344            break;
345        case MSG_ADD_WATCHER:
346            ret = HandleParamWatcherAdd(worker, msg);
347            break;
348        case MSG_DEL_WATCHER:
349            ret = HandleParamWatcherDel(worker, msg);
350            break;
351        case MSG_SAVE_PARAM:
352            ret = HandleParamSave(worker, msg);
353            break;
354        default:
355            break;
356    }
357    PARAM_CHECK(ret == 0, return -1, "Failed to process message ret %d", ret);
358    return 0;
359}
360
361PARAM_STATIC int OnIncomingConnect(LoopHandle loop, TaskHandle server)
362{
363    ParamStreamInfo info = {};
364#ifdef STARTUP_INIT_TEST
365    info.flags = PARAM_TEST_FLAGS;
366#endif
367    info.server = NULL;
368    info.close = OnClose;
369    info.recvMessage = ProcessMessage;
370    info.incomingConnect = NULL;
371    ParamTaskPtr client = NULL;
372    int ret = ParamStreamCreate(&client, server, &info, sizeof(ParamWatcher));
373    PARAM_CHECK(ret == 0, return -1, "Failed to create client");
374
375    ParamWatcher *watcher = (ParamWatcher *)ParamGetTaskUserData(client);
376    PARAM_CHECK(watcher != NULL, return -1, "Failed to get watcher");
377    OH_ListInit(&watcher->triggerHead);
378    watcher->stream = client;
379#ifdef STARTUP_INIT_TEST
380    g_paramService.watcherTask = client;
381#endif
382    return 0;
383}
384
385static void LoadSelinuxLabel(const char *op)
386{
387    // load security label
388#ifdef PARAM_SUPPORT_SELINUX
389    ParamSecurityOps *ops = GetParamSecurityOps(PARAM_SECURITY_SELINUX);
390    if (ops != NULL && ops->securityGetLabel != NULL) {
391        ops->securityGetLabel(op);
392    }
393#endif
394}
395
396int InitParamService(void)
397{
398    PARAM_LOGI("InitParamService pipe: %s.", PIPE_NAME);
399    CheckAndCreateDir(PIPE_NAME);
400    CheckAndCreateDir(PARAM_STORAGE_PATH"/");
401    // param space
402    PARAM_WORKSPACE_OPS ops = {0};
403    ops.updaterMode = InUpdaterMode();
404    // init open log
405    ops.logFunc = InitLog;
406    ops.getServiceGroupIdByPid = GetServiceGroupIdByPid;
407#ifdef PARAM_SUPPORT_SELINUX
408    ops.setfilecon = setfilecon;
409#endif
410    int ret = InitParamWorkSpace(0, &ops);
411    PARAM_CHECK(ret == 0, return ret, "Init parameter workspace fail");
412    ret = InitPersistParamWorkSpace();
413    PARAM_CHECK(ret == 0, return ret, "Init persist parameter workspace fail");
414    // param server
415    if (g_paramService.serverTask == NULL) {
416        ParamStreamInfo info = {};
417        info.server = PIPE_NAME;
418        info.close = NULL;
419        info.recvMessage = NULL;
420        info.incomingConnect = OnIncomingConnect;
421        ret = ParamServerCreate(&g_paramService.serverTask, &info);
422        PARAM_CHECK(ret == 0, return ret, "Failed to create server");
423    }
424
425    // init trigger space
426    ret = InitTriggerWorkSpace();
427    PARAM_CHECK(ret == 0, return ret, "Failed to init trigger");
428    RegisterTriggerExec(TRIGGER_PARAM_WAIT, ExecuteWatchTrigger_);
429    RegisterTriggerExec(TRIGGER_PARAM_WATCH, ExecuteWatchTrigger_);
430
431    return 0;
432}
433
434void LoadSpecialParam(void)
435{
436    // read param area size from cfg and save to dac
437    LoadParamAreaSize();
438    // read selinux label
439    LoadSelinuxLabel("init");
440    // from cmdline
441    LoadParamFromCmdLine();
442    // from build
443    LoadParamFromBuild();
444}
445
446int StartParamService(void)
447{
448#ifdef STARTUP_INIT_TEST
449    return 0;
450#endif
451    // read selinux label
452    LoadSelinuxLabel("permission");
453    return ParamServiceStart();
454}
455
456void StopParamService(void)
457{
458    PARAM_LOGI("StopParamService.");
459    ClosePersistParamWorkSpace();
460    CloseParamWorkSpace();
461    CloseTriggerWorkSpace();
462    ParamTaskClose(g_paramService.serverTask);
463    g_paramService.serverTask = NULL;
464    ParamServiceStop();
465}
466
467int SystemWriteParam(const char *name, const char *value)
468{
469    return SystemSetParam(name, value, GetParamSecurityLabel());
470}
471
472#ifdef STARTUP_INIT_TEST
473ParamService *GetParamService()
474{
475    return &g_paramService;
476}
477#endif
478