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
16#include "ueventd_read_cfg.h"
17
18#include <ctype.h>
19#include <string.h>
20#include <stdbool.h>
21#include <stdlib.h>
22#include <unistd.h>
23#include <errno.h>
24#include <fcntl.h>
25#include <sys/stat.h>
26#include "init_utils.h"
27#include "list.h"
28#include "ueventd_utils.h"
29#include "securec.h"
30#define INIT_LOG_TAG "ueventd"
31#include "init_log.h"
32
33// default item count in config files
34#define DEFAULTITEMCOUNT (50)
35#define MAX_CONFIGURE_SIZE (1024 * 1024 * 16)
36
37typedef enum SECTION {
38    SECTION_INVALID = -1,
39    SECTION_DEVICE = 0,
40    SECTION_SYSFS,
41    SECTION_FIRMWARE
42} SECTION;
43
44typedef int (*ParseConfigFunc)(char *);
45typedef struct FunctionMapper {
46    const char *name;
47    ParseConfigFunc func;
48} FUNCTIONMAPPER;
49
50struct ListNode g_devices = {
51    .next = &g_devices,
52    .prev = &g_devices,
53};
54
55struct ListNode g_sysDevices = {
56    .next = &g_sysDevices,
57    .prev = &g_sysDevices,
58};
59
60struct ListNode g_firmwares = {
61    .next = &g_firmwares,
62    .prev = &g_firmwares,
63};
64
65static int ParseDeviceConfig(char *p)
66{
67    INIT_LOGV("Parse device config info: %s", p);
68    char **items = NULL;
69    int count = -1;
70    // format: <device node> <mode> <uid> <gid> <parameter>
71    const int expectedCount = 5;
72
73    INIT_CHECK_ONLY_ELOG(!INVALIDSTRING(p), "Invalid argument");
74    items = SplitStringExt(p, " ", &count, expectedCount);
75    if ((count != expectedCount) && (count != expectedCount - 1)) {
76        INIT_LOGE("Ignore invalid item: %s", p);
77        FreeStringVector(items, count);
78        return 0;
79    }
80
81    struct DeviceUdevConf *config = calloc(1, sizeof(struct DeviceUdevConf));
82    if (config == NULL) {
83        errno = ENOMEM;
84        FreeStringVector(items, count);
85        return -1;
86    }
87    config->name = strdup(items[0]); // device node
88    INIT_ERROR_CHECK(config->name != NULL, FreeStringVector(items, count);
89                     free(config); return -1, "failed dup config name");
90    errno = 0;
91    config->mode = strtoul(items[1], NULL, OCTAL_BASE); // mode
92    INIT_ERROR_CHECK(errno == 0, config->mode = DEVMODE,
93        "Invalid mode in config file for device node %s. use default mode", config->name);
94    config->uid = (uid_t)DecodeUid(items[2]); // uid
95    config->gid = (gid_t)DecodeGid(items[3]); // gid
96    if (count == expectedCount) {
97        config->parameter = strdup(items[4]); // device parameter
98        INIT_ERROR_CHECK(config->parameter != NULL, FreeStringVector(items, count);
99                         free((void*)config->name); free(config); return -1, "failed dup parameter");
100    } else {
101        config->parameter = NULL;
102    }
103    OH_ListInit(&config->paramNode);
104    OH_ListAddTail(&g_devices, &config->list);
105    FreeStringVector(items, count);
106    return 0;
107}
108
109static int ParseSysfsConfig(char *p)
110{
111    INIT_LOGV("Parse sysfs config info: %s", p);
112    char **items = NULL;
113    int count = -1;
114    // format: <syspath> <attribute> <mode> <uid> <gid>
115    const int expectedCount = 5;
116
117    INIT_CHECK_ONLY_ELOG(!INVALIDSTRING(p), "Invalid argument");
118    items = SplitStringExt(p, " ", &count, expectedCount);
119    if (count != expectedCount) {
120        INIT_LOGE("Ignore invalid item: %s", p);
121        FreeStringVector(items, count);
122        return 0;
123    }
124    struct SysUdevConf *config = calloc(1, sizeof(struct SysUdevConf));
125    if (config == NULL) {
126        errno = ENOMEM;
127        FreeStringVector(items, count);
128        return -1;
129    }
130    config->sysPath = strdup(items[0]); // sys path
131    INIT_ERROR_CHECK(config->sysPath != NULL, FreeStringVector(items, count);
132                     free(config); return -1, "failed to dup syspath");
133    config->attr = strdup(items[1]);  // attribute
134    INIT_ERROR_CHECK(config->attr != NULL, FreeStringVector(items, count);
135                     free((void*)config->sysPath); free(config); return -1, "failed to dup attr");
136    errno = 0;
137    config->mode = strtoul(items[2], NULL, OCTAL_BASE); // mode
138    INIT_ERROR_CHECK(errno == 0, config->mode = DEVMODE,
139        "Invalid mode in config file for sys path %s. use default mode", config->sysPath);
140    config->uid = (uid_t)DecodeUid(items[3]); // uid
141    config->gid = (gid_t)DecodeGid(items[4]); // gid
142    OH_ListAddTail(&g_sysDevices, &config->list);
143    FreeStringVector(items, count);
144    return 0;
145}
146
147static int ParseFirmwareConfig(char *p)
148{
149    INIT_LOGV("Parse firmware config info: %s", p);
150    INIT_ERROR_CHECK(!INVALIDSTRING(p), return -1, "Invalid argument");
151
152    // Sanity checks
153    struct stat st = {};
154    INIT_ERROR_CHECK(stat(p, &st) == 0, return -1, "Invalid firmware file: %s, err = %d", p, errno);
155    INIT_ERROR_CHECK(S_ISDIR(st.st_mode), return -1, "Expect directory in firmware config");
156    struct FirmwareUdevConf *config = calloc(1, sizeof(struct FirmwareUdevConf));
157    INIT_CHECK(config != NULL, errno = ENOMEM;
158        return -1);
159    config->fmPath = strdup(p);
160    INIT_ERROR_CHECK(config->fmPath != NULL, free(config); return -1, "failed to dup fmpath");
161    OH_ListAddTail(&g_firmwares, &config->list);
162    return 0;
163}
164
165static SECTION GetSection(const char *section)
166{
167    INIT_CHECK_RETURN_VALUE(!INVALIDSTRING(section), SECTION_INVALID);
168    if (STRINGEQUAL(section, "device")) {
169        return SECTION_DEVICE;
170    } else if (STRINGEQUAL(section, "sysfs")) {
171        return SECTION_SYSFS;
172    } else if (STRINGEQUAL(section, "firmware")) {
173        return SECTION_FIRMWARE;
174    } else {
175        return SECTION_INVALID;
176    }
177}
178
179static const FUNCTIONMAPPER funcMapper[3] = {
180    {"device", ParseDeviceConfig},
181    {"sysfs", ParseSysfsConfig},
182    {"firmware", ParseFirmwareConfig}
183};
184
185ParseConfigFunc callback;
186int ParseUeventConfig(char *buffer)
187{
188    char *section = NULL;
189    char *right = NULL;
190    char *p = buffer;
191    SECTION type;
192
193    INIT_CHECK_RETURN_VALUE(!INVALIDSTRING(buffer), -1);
194    if (*p == '[') {
195        p++;
196        if ((right = strchr(p, ']')) == NULL) {
197            INIT_LOGE("Invalid line \"%s\", miss ']'", buffer);
198            return -1;
199        }
200        *right = '\0'; // Replace ']' with '\0';
201        section = p;
202        INIT_LOGV("section is [%s]", section);
203        if ((type = GetSection(section)) == SECTION_INVALID) {
204            INIT_LOGE("Invalid section \" %s \"", section);
205            callback = NULL; // reset callback
206            return -1;
207        }
208        callback = funcMapper[type].func;
209        return 0;
210    }
211    return (callback != NULL) ? callback(p) : -1;
212}
213
214static void DoUeventConfigParse(char *buffer, size_t length)
215{
216    char **items = NULL;
217    int count = -1;
218    const int maxItemCount = DEFAULTITEMCOUNT;
219
220    items = SplitStringExt(buffer, "\n", &count, maxItemCount);
221    INIT_LOGV("Dump items count = %d", count);
222    for (int i = 0; i < count; i++) {
223        char *p = items[i];
224        // Skip lead white space
225        while (isspace(*p)) {
226            p++;
227        }
228
229        // Skip comment or empty line
230        if (*p == '\0' || *p == '#') {
231            continue;
232        }
233        int rc = ParseUeventConfig(p);
234        if (rc < 0) {
235            INIT_LOGE("Parse uevent config from %s failed", p);
236        }
237    }
238    // release memory
239    FreeStringVector(items, count);
240}
241
242void ParseUeventdConfigFile(const char *file)
243{
244    INIT_CHECK_ONLY_RETURN(!INVALIDSTRING(file));
245    char *config = GetRealPath(file);
246    INIT_CHECK_ONLY_RETURN(config != NULL);
247    int fd = open(config, O_RDONLY | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
248    free(config);
249    INIT_ERROR_CHECK(fd >= 0, return, "Read from %s failed", file);
250
251    struct stat st;
252    if (fstat(fd, &st) < 0) {
253        INIT_LOGE("Failed to get file stat. err = %d", errno);
254        close(fd);
255        return;
256    }
257
258    // st_size should never be less than 0
259    if (st.st_size < 0 || st.st_size > MAX_CONFIGURE_SIZE) {
260        INIT_LOGE("Invalid configure file with size");
261        close(fd);
262        return;
263    }
264    size_t size = (size_t)st.st_size;
265    char *buffer = malloc(size + 1);
266    if (buffer == NULL) {
267        INIT_LOGE("Failed to malloc memory. err = %d", errno);
268        close(fd);
269        return;
270    }
271
272    if (read(fd, buffer, size) != (ssize_t)size) {
273        INIT_LOGE("Read from file %s failed. err = %d", file, errno);
274        free(buffer);
275        buffer = NULL;
276        close(fd);
277        return;
278    }
279
280    buffer[size] = '\0';
281    DoUeventConfigParse(buffer, size);
282    free(buffer);
283    buffer = NULL;
284    close(fd);
285}
286
287// support '*' to match all characters
288// '?' match one character.
289bool IsMatch(const char *target, const char *pattern)
290{
291    INIT_CHECK_RETURN_VALUE(target != NULL, false);
292    INIT_CHECK_RETURN_VALUE(pattern != NULL, true);
293
294    const char *t = target;
295    const char *p = pattern;
296    const char *plast = NULL;
297    bool reMatch = false;
298    while (*t != '\0') {
299        if (*t == *p) {
300            t++;
301            p++;
302            continue;
303        }
304
305        // Match one character.
306        if (*p == '?') {
307            p++;
308            t++;
309            continue;
310        }
311
312        if (*p == '\0') {
313            return reMatch;
314        }
315
316        if (*p == '*') {
317            reMatch = true;
318            // Met '*', record where we will start over.
319            // plast point to next character that we will compare it again.
320            plast = ++p;
321            t++;
322            continue;
323        }
324
325        if (reMatch) {
326            // Start over.
327            p = plast;
328            t++;
329        } else {
330            return false;
331        }
332    }
333
334    while (*p == '*') {
335        p++;
336    }
337    return (*p == '\0');
338}
339
340struct DeviceUdevConf *GetDeviceUdevConfByDevNode(const char *devNode)
341{
342    if (INVALIDSTRING(devNode)) {
343        return NULL;
344    }
345
346    struct ListNode *node = NULL;
347    if (!ListEmpty(g_devices)) {
348        ForEachListEntry(&g_devices, node) {
349            struct DeviceUdevConf *config = ListEntry(node, struct DeviceUdevConf, list);
350            if (IsMatch(devNode, config->name)) {
351                return config;
352            }
353        }
354    }
355
356    return NULL;
357}
358
359int GetDeviceNodePermissions(const char *devNode, uid_t *uid, gid_t *gid, mode_t *mode)
360{
361    if (INVALIDSTRING(devNode)) {
362        return -1;
363    }
364
365    struct ListNode *node = NULL;
366    if (!ListEmpty(g_devices)) {
367        ForEachListEntry(&g_devices, node) {
368            struct DeviceUdevConf *config = ListEntry(node, struct DeviceUdevConf, list);
369            if (IsMatch(devNode, config->name)) {
370                *uid = config->uid;
371                *gid = config->gid;
372                *mode = config->mode;
373                return 0;
374            }
375        }
376    }
377    return -1;
378}
379
380void ChangeSysAttributePermissions(const char *sysPath)
381{
382    if (INVALIDSTRING(sysPath)) {
383        return;
384    }
385
386    struct ListNode *node = NULL;
387    struct SysUdevConf *config = NULL;
388    int matched = 0;
389    if (!ListEmpty(g_sysDevices)) {
390        ForEachListEntry(&g_sysDevices, node) {
391            config = ListEntry(node, struct SysUdevConf, list);
392            if (STRINGEQUAL(config->sysPath, sysPath)) {
393                matched = 1;
394                break;
395            }
396        }
397    }
398
399    if (matched == 0) {
400        return;
401    }
402    char sysAttr[SYSPATH_SIZE] = {};
403    if (snprintf_s(sysAttr, SYSPATH_SIZE, SYSPATH_SIZE - 1, "/sys%s/%s", config->sysPath, config->attr) == -1) {
404        INIT_LOGE("Failed to build sys attribute for sys path %s, attr: %s", config->sysPath, config->attr);
405        return;
406    }
407    if (chown(sysAttr, config->uid, config->gid) < 0) {
408        INIT_LOGE("chown for file %s failed, err = %d", sysAttr, errno);
409    }
410
411    if (chmod(sysAttr, config->mode) < 0) {
412        INIT_LOGE("[uevent][error] chmod for file %s failed, err = %d", sysAttr, errno);
413    }
414}
415
416static void FreeDeviceConfig(ListNode *node)
417{
418    struct DeviceUdevConf *config = ListEntry(node, struct DeviceUdevConf, list);
419    free((void *)config->name);
420    free((void *)config->parameter);
421    OH_ListRemove(&config->paramNode);
422    free(config);
423}
424
425static void FreeSysUdevConf(ListNode *node)
426{
427    struct SysUdevConf *config = ListEntry(node, struct SysUdevConf, list);
428    free((void *)config->sysPath);
429    free((void *)config->attr);
430    free(config);
431}
432
433static void FreeFirmwareUdevConf(ListNode *node)
434{
435    struct FirmwareUdevConf *config = ListEntry(node, struct FirmwareUdevConf, list);
436    free((void *)config->fmPath);
437    free(config);
438}
439
440void CloseUeventConfig(void)
441{
442    OH_ListRemoveAll(&g_devices, FreeDeviceConfig);
443    OH_ListRemoveAll(&g_sysDevices, FreeSysUdevConf);
444    OH_ListRemoveAll(&g_firmwares, FreeFirmwareUdevConf);
445}