From 3900746c958132786b8e6ea932e77e974fe3cecc Mon Sep 17 00:00:00 2001 From: zhong_ning Date: Wed, 21 Apr 2021 22:11:55 +0800 Subject: [PATCH] updater_start_l1 --- services/BUILD.gn | 138 +++-- services/include/device.h | 33 ++ services/include/list.h | 41 ++ services/src/device.c | 51 ++ services/src/init_cmds.c | 100 +++- services/src/init_service.c | 2 +- services/src/init_service_manager.c | 4 +- services/src/list.c | 36 ++ services/src/main.c | 22 +- services/src/uevent.c | 768 ++++++++++++++++++++++++++++ 10 files changed, 1139 insertions(+), 56 deletions(-) create mode 100755 services/include/device.h create mode 100755 services/include/list.h create mode 100755 services/src/device.c create mode 100755 services/src/list.c create mode 100755 services/src/uevent.c diff --git a/services/BUILD.gn b/services/BUILD.gn index a161e6e2..1fb8e4cb 100644 --- a/services/BUILD.gn +++ b/services/BUILD.gn @@ -10,60 +10,110 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -import("//build/lite/config/component/lite_component.gni") +if (defined(ohos_lite)) { + import("//build/lite/config/component/lite_component.gni") -lite_component("init_lite") { - features = [ ":init" ] -} + lite_component("init_lite") { + features = [ ":init" ] + } -# feature: init -executable("init") { - defines = [ - "_GNU_SOURCE", #syscall function need this macro definition - ] - sources = [ - "src/init_adapter.c", - "src/init_cmds.c", - "src/init_jobs.c", - "src/init_read_cfg.c", - "src/init_service.c", - "src/init_service_manager.c", - "src/init_signal_handler.c", - "src/main.c", - ] + # feature: init + executable("init") { + defines = [ + "_GNU_SOURCE", #syscall function need this macro definition + "OHOS_LITE=1", + ] + sources = [ + "src/init_adapter.c", + "src/init_cmds.c", + "src/init_jobs.c", + "src/init_read_cfg.c", + "src/init_service.c", + "src/init_service_manager.c", + "src/init_signal_handler.c", + "src/main.c", + ] - include_dirs = [ - "include", - "//third_party/cJSON", - "//third_party/bounds_checking_function/include", - "//base/startup/syspara_lite/interfaces/kits", - ] + include_dirs = [ + "include", + "//third_party/cJSON", + "//third_party/bounds_checking_function/include", + "//base/startup/syspara_lite/interfaces/kits", + ] - cflags = [ "-Wall" ] + cflags = [ "-Wall" ] - deps = [ - "//base/startup/syspara_lite/frameworks/parameter:parameter", - "//build/lite/config/component/cJSON:cjson_shared", - "//third_party/bounds_checking_function:libsec_shared", - ] + deps = [ + "//base/startup/syspara_lite/frameworks/parameter:parameter", + "//build/lite/config/component/cJSON:cjson_shared", + "//third_party/bounds_checking_function:libsec_shared", + ] + ldflags = [] + if (ohos_kernel_type == "liteos_a") { + include_dirs += [ "//kernel/liteos_a/syscall" ] + } + if (ohos_kernel_type == "linux") { + defines += [ "NEED_EXEC_RCS_LINUX" ] + ldflags += [ + "-lm", + "-lpthread", + ] + } + } - ldflags = [] + if (ohos_build_type == "debug") { + group("unittest") { + deps = [ "//base/startup/init_lite/services/test/unittest/common:unittest" ] + } + } +} else { + import("//build/ohos.gni") - if (ohos_kernel_type == "liteos_a") { - include_dirs += [ "//kernel/liteos_a/syscall" ] + ohos_executable("updaterueventd") { + sources = [ + "src/uevent.c", + "src/list.c", + ] + include_dirs = [ + "include", + "//third_party/bounds_checking_function/include", + ] + deps = [ + "//third_party/bounds_checking_function:libsec_static", + ] + install_enable = true + part_name = "updater" } - if (ohos_kernel_type == "linux") { - defines += [ "NEED_EXEC_RCS_LINUX" ] - ldflags += [ - "-lm", - "-lpthread", - ] + ohos_executable("updaterinit") { + sources = [ + "src/main.c", + "src/init_cmds.c", + "src/init_jobs.c", + "src/init_read_cfg.c", + "src/init_adapter.c", + "src/init_service.c", + "src/init_service_manager.c", + "src/init_signal_handler.c", + "src/device.c", + ] + defines = [ "OHOS_LITE=0" ] + include_dirs = [ + "include", + "//third_party/cJSON", + "//third_party/bounds_checking_function/include", + ] + deps = [ + "//third_party/bounds_checking_function:libsec_static", + "//third_party/cJSON:cjson_static", + ] + install_enable = true + part_name = "updater" } -} -if (ohos_build_type == "debug") { - group("unittest") { - deps = [ "//base/startup/init_lite/services/test/unittest/common:unittest" ] + ohos_prebuilt_etc("init.cfg") { + source = "//device/hisilicon/hi3516dv300/updater/init.cfg" + relative_install_dir = "init" + subsystem_name = "updater" } } diff --git a/services/include/device.h b/services/include/device.h new file mode 100755 index 00000000..6739b2bc --- /dev/null +++ b/services/include/device.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2020 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BASE_STARTUP_INITLITE_DEVICE_H +#define BASE_STARTUP_INITLITE_DEVICE_H + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif + +void MountBasicFs(); +void CreateDeviceNode(); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif +#endif // BASE_STARTUP_INITLITE_DEVICE_H diff --git a/services/include/list.h b/services/include/list.h new file mode 100755 index 00000000..4fa91287 --- /dev/null +++ b/services/include/list.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2020 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BASE_STARTUP_INITLITE_LIST_H +#define BASE_STARTUP_INITLITE_LIST_H + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif + +#include + +struct ListNode { + struct ListNode *next; + struct ListNode *prev; +}; + +void ListInit(struct ListNode *list); +void ListAddTail(struct ListNode *list, struct ListNode *item); +void ListRemove(struct ListNode *item); +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif + +#endif // BASE_STARTUP_INITLITE_LIST_H diff --git a/services/src/device.c b/services/src/device.c new file mode 100755 index 00000000..9411a6a7 --- /dev/null +++ b/services/src/device.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2020 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include + +void MountBasicFs() +{ + if (mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755") != 0) { + printf("Mount tmpfs failed. %s\n", strerror(errno)); + } + if (mount("proc", "/proc", "proc", 0, "hidepid=2") != 0) { + printf("Mount procfs failed. %s\n", strerror(errno)); + } + if (mount("sysfs", "/sys", "sysfs", 0, NULL) != 0) { + printf("Mount sysfs failed. %s\n", strerror(errno)); + } +} + +void CreateDeviceNode() +{ + if (mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)) != 0) { + printf("Create /dev/kmsg device node failed. %s\n", strerror(errno)); + } + if (mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)) != 0) { + printf("Create /dev/null device node failed. %s\n", strerror(errno)); + } + if (mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)) != 0) { + printf("Create /dev/random device node failed. %s\n", strerror(errno)); + } + + if (mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)) != 0) { + printf("Create /dev/urandom device node failed. %s\n", strerror(errno)); + } +} diff --git a/services/src/init_cmds.c b/services/src/init_cmds.c index f1083374..6f3f2266 100644 --- a/services/src/init_cmds.c +++ b/services/src/init_cmds.c @@ -24,11 +24,14 @@ #include #include #include - +#if !OHOS_LITE +#include +#include +#include +#endif #include "init_service_manager.h" #include "securec.h" - #define MODE_LEN 4 // for chmod mode, format 0xxx #define DEFAULT_DIR_MODE 0755 // mkdir, default mode #define SPACES_CNT_IN_CMD_MAX 10 // mount, max number of spaces in cmdline @@ -48,6 +51,7 @@ static const char* g_supportedCmds[] = { "chown ", "mount ", "loadcfg ", + "insmod ", }; void ParseCmdLine(const char* cmdStr, CmdLine* resCmd) @@ -172,14 +176,14 @@ static void DoChown(const char* cmdContent) static char* CopySubStr(const char* srcStr, size_t startPos, size_t endPos) { if (endPos <= startPos) { - printf("[Init] DoMount, invalid params<%lu, %lu> for %s.\n", endPos, startPos, srcStr); + printf("[Init] DoMount, invalid params<%zu, %zu> for %s.\n", endPos, startPos, srcStr); return NULL; } size_t mallocLen = endPos - startPos + 1; char* retStr = (char*)malloc(mallocLen); if (retStr == NULL) { - printf("[Init] DoMount, malloc failed! malloc size %lu, for %s.\n", mallocLen, srcStr); + printf("[Init] DoMount, malloc failed! malloc size %zu, for %s.\n", mallocLen, srcStr); return NULL; } @@ -316,6 +320,86 @@ static void DoMount(const char* cmdContent) free(target); } +#if !OHOS_LITE +// format insmod [-f] [options] +static void DoInsmod(const char *cmdContent) +{ +#define OPTIONS_SIZE (128u) + char *p = NULL; + char *line = NULL; + char *restPtr = NULL; + char *fileName = NULL; + int flags = 0; + int fd = -1; + char options[OPTIONS_SIZE] = {0}; + + size_t count = strlen(cmdContent); + if (count > OPTIONS_SIZE) { + printf("[Init], options too long, maybe lost some of options\n"); + } + line = (char *)malloc(count + 1); + if (line == NULL) { + printf("[Init] Allocate memory failed.\n"); + return; + } + if (memcpy_s(line, count, cmdContent, count) != EOK) { + printf("[Init] memcpy failed\n"); + } + + line[count] = '\0'; + + do { + if ((p = strtok_r(line, " ", &restPtr)) == NULL) { + printf("[Init] debug, cannot get filename\n"); + free(line); + return; + } + fileName = p; + printf("[Init] debug, fileName is [%s]\n", fileName); + if ((p = strtok_r(NULL, " ", &restPtr)) == NULL) { + break; + } + if (!strcmp(p, "-f")) { + flags = MODULE_INIT_IGNORE_VERMAGIC | MODULE_INIT_IGNORE_MODVERSIONS; + } + } while (0); + + if (flags != 0) { // '-f' option + p = restPtr; // grab all rest of contents. + } else { // no '-f' option, should combine p and resetPtr + if (p != NULL) { + if (restPtr != NULL) { + if (snprintf_s(options, sizeof(options), OPTIONS_SIZE -1, "%s %s", p, restPtr) == -1) { + return; + } + } else { + if (strncpy_s(options, OPTIONS_SIZE - 1, p, strlen(p)) != 0) { + return; + } + } + } + } + // Open ko files + fd = open(fileName, O_RDONLY | O_NOFOLLOW | O_CLOEXEC); + if (fd < 0) { + printf("[Init] failed to open %s: %d\n", fileName, errno); + goto out; + } + + int rc = syscall(__NR_finit_module, fd, options, flags); + if (rc == -1) { + printf("[Init] finit_module for %s failed: %d\n", fileName, errno); + } +out: + if (fd > 0) { + close(fd); + } + if (line != NULL) { + free(line); + } +} +#endif + static bool CheckValidCfg(const char *path) { size_t cfgCnt = sizeof(g_supportCfg) / sizeof(g_supportCfg[0]); @@ -402,7 +486,13 @@ void DoCmd(const CmdLine* curCmd) DoMount(curCmd->cmdContent); } else if (strncmp(curCmd->name, "loadcfg ", strlen("loadcfg ")) == 0) { DoLoadCfg(curCmd->cmdContent); - } else { + } +#if !OHOS_LITE + else if (strncmp(curCmd->name, "insmod ", strlen("insmod ")) == 0) { + DoInsmod(curCmd->cmdContent); + } +#endif + else { printf("[Init] DoCmd, unknown cmd name %s.\n", curCmd->name); } } diff --git a/services/src/init_service.c b/services/src/init_service.c index fd850455..1e6e577a 100644 --- a/services/src/init_service.c +++ b/services/src/init_service.c @@ -64,7 +64,7 @@ static int SetPerms(const Service *service) capHeader.version = _LINUX_CAPABILITY_VERSION_3; capHeader.pid = 0; - struct __user_cap_data_struct capData[CAP_NUM] = {0}; + struct __user_cap_data_struct capData[CAP_NUM] = {}; for (unsigned int i = 0; i < service->servPerm.capsCnt; ++i) { if (service->servPerm.caps[i] == FULL_CAP) { for (int i = 0; i < CAP_NUM; ++i) { diff --git a/services/src/init_service_manager.c b/services/src/init_service_manager.c index d06fec7f..2a7cbcbb 100755 --- a/services/src/init_service_manager.c +++ b/services/src/init_service_manager.c @@ -66,7 +66,7 @@ void StartServiceByName(const char* servName) void StopAllServices() { - for (size_t i = 0; i < g_servicesCnt; i++) { + for (int i = 0; i < g_servicesCnt; i++) { if (ServiceStop(&g_services[i]) != SERVICE_SUCCESS) { printf("[Init] StopAllServices, service %s stop failed!\n", g_services[i].name); } @@ -75,7 +75,7 @@ void StopAllServices() void ReapServiceByPID(int pid) { - for (size_t i = 0; i < g_servicesCnt; i++) { + for (int i = 0; i < g_servicesCnt; i++) { if (g_services[i].pid == pid) { if (g_services[i].attribute & SERVICE_ATTR_IMPORTANT) { // important process exit, need to reboot system diff --git a/services/src/list.c b/services/src/list.c new file mode 100755 index 00000000..17e6f468 --- /dev/null +++ b/services/src/list.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "list.h" + +void ListInit(struct ListNode *node) +{ + node->next = node; + node->prev = node; +} + +void ListAddTail(struct ListNode *head, struct ListNode *item) +{ + item->next = head; + item->prev = head->prev; + head->prev->next = item; + head->prev = item; +} + +void ListRemove(struct ListNode *item) +{ + item->next->prev = item->prev; + item->prev->next = item->next; +} diff --git a/services/src/main.c b/services/src/main.c index 7f143651..0c116e7d 100755 --- a/services/src/main.c +++ b/services/src/main.c @@ -27,12 +27,19 @@ #include "init_adapter.h" #include "init_read_cfg.h" #include "init_signal_handler.h" +#if OHOS_LITE #include "parameter.h" +#endif + +#if !OHOS_LITE +#include "device.h" +#endif static const pid_t INIT_PROCESS_PID = 1; static void PrintSysInfo() { +#if OHOS_LITE char* sysInfo = GetVersionId(); if (sysInfo != NULL) { printf("[Init] %s\n", sysInfo); @@ -41,6 +48,7 @@ static void PrintSysInfo() return; } printf("[Init] main, GetVersionId failed!\n"); +#endif } #ifdef OHOS_DEBUG @@ -72,7 +80,13 @@ int main(int argc, char * const argv[]) // 1. print system info PrintSysInfo(); - // 2. signal register +#if !OHOS_LITE + // 2. Mount basic filesystem and create common device node. + MountBasicFs(); + CreateDeviceNode(); +#endif + + // 3. signal register SignalInitModule(); #ifdef OHOS_DEBUG @@ -82,7 +96,7 @@ int main(int argc, char * const argv[]) } #endif // OHOS_DEBUG - // 3. execute rcs + // 4. execute rcs ExecuteRcs(); #ifdef OHOS_DEBUG @@ -92,7 +106,7 @@ int main(int argc, char * const argv[]) } #endif // OHOS_DEBUG - // 4. read configuration file and do jobs + // 5. read configuration file and do jobs InitReadCfg(); #ifdef OHOS_DEBUG @@ -102,7 +116,7 @@ int main(int argc, char * const argv[]) } #endif // OHOS_DEBUG - // 5. keep process alive + // 6. keep process alive #ifdef OHOS_DEBUG printf("[Init] main, time used: sigInfo %ld ms, rcs %ld ms, cfg %ld ms.\n", \ TimeDiffMs(&tmEnter, &tmSysInfo), TimeDiffMs(&tmSysInfo, &tmRcs), TimeDiffMs(&tmRcs, &tmCfg)); diff --git a/services/src/uevent.c b/services/src/uevent.c new file mode 100755 index 00000000..a2b5d222 --- /dev/null +++ b/services/src/uevent.c @@ -0,0 +1,768 @@ +/* + * Copyright (c) 2020 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "list.h" +#include "securec.h" + +int g_ueventFD = -1; + +struct Uevent { + const char *action; + const char *path; + const char *subsystem; + const char *firmware; + const char *partitionName; + const char *deviceName; + int partitionNum; + int major; + int minor; +}; + +struct PlatformNode { + char *name; + char *path; + size_t pathLen; + struct ListNode list; +}; + +static struct ListNode g_platformNames = { + .next = &g_platformNames, + .prev = &g_platformNames, +}; + +const char *TRIGGER = "/dev/.trigger_uevent"; +static void HandleUevent(); + +static int UeventFD() +{ + return g_ueventFD; +} + +static void DoTrigger(DIR *dir) +{ + struct dirent *de = NULL; + int dfd = dirfd(dir); + int fd = openat(dfd, "uevent", O_WRONLY); + if (fd >= 0) { + write(fd, "add\n", 4); + close(fd); + HandleUevent(); + } + + while ((de = readdir(dir)) != NULL) { + DIR *dir2 = NULL; + if (de->d_type != DT_DIR || de->d_name[0] == '.') { + continue; + } + fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY); + if (fd < 0) { + continue; + } + + dir2 = fdopendir(fd); + if (dir2 == NULL) { + close(fd); + } else { + DoTrigger(dir2); + closedir(dir2); + } + } +} + +void Trigger(const char *sysPath) +{ + DIR *dir = opendir(sysPath); + if (dir) { + DoTrigger(dir); + closedir(dir); + } +} + +static void RetriggerUevent() +{ + if (access(TRIGGER, F_OK) == 0) { + printf("Skip trigger uevent, alread done\n"); + return; + } + Trigger("/sys/class"); + Trigger("/sys/block"); + Trigger("/sys/devices"); + int fd = open(TRIGGER, O_WRONLY | O_CREAT | O_CLOEXEC, 0000); + if (fd > 0) { + close(fd); + } + printf("Re-trigger uevent done\n"); +} + +static void UeventSockInit() +{ + struct sockaddr_nl addr; + int buffSize = 256 * 1024; + int on = 1; + + if (memset_s(&addr, sizeof(addr), 0, sizeof(addr)) != 0) { + return; + } + addr.nl_family = AF_NETLINK; + addr.nl_pid = getpid(); + addr.nl_groups = 0xffffffff; + + int sockfd = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT); + if (sockfd < 0) { + printf("Create socket failed. %d\n", errno); + return; + } + + setsockopt(sockfd, SOL_SOCKET, SO_RCVBUFFORCE, &buffSize, sizeof(buffSize)); + setsockopt(sockfd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); + + if (bind(sockfd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + printf("Bind socket failed. %d\n", errno); + close(sockfd); + return; + } + g_ueventFD = sockfd; + fcntl(g_ueventFD, F_SETFD, FD_CLOEXEC); + fcntl(g_ueventFD, F_SETFL, O_NONBLOCK); + RetriggerUevent(); + return; +} + +ssize_t ReadUevent(int fd, void *buf, size_t len) +{ + struct iovec iov = { buf, len }; + struct sockaddr_nl addr; + char control[CMSG_SPACE(sizeof(struct ucred))]; + uid_t uid = -1; + struct msghdr hdr = { + &addr, + sizeof(addr), + &iov, + 1, + control, + sizeof(control), + 0, + }; + ssize_t n = recvmsg(fd, &hdr, 0); + if (n <= 0) { + return n; + } + struct cmsghdr *cMsg = CMSG_FIRSTHDR(&hdr); + if (cMsg == NULL || cMsg->cmsg_type != SCM_CREDENTIALS) { + goto out; + } + struct ucred *cRed = (struct ucred *)CMSG_DATA(cMsg); + uid = cRed->uid; + if (uid != 0) { + goto out; + } + + if (addr.nl_groups == 0 || addr.nl_pid != 0) { + /* ignoring non-kernel or unicast netlink message */ + goto out; + } + + return n; +out: + bzero(buf, len); + errno = -EIO; + return n; +} + +static void InitUevent(struct Uevent *event) +{ + event->action = ""; + event->path = ""; + event->subsystem = ""; + event->firmware = ""; + event->partitionName = ""; + event->deviceName = ""; + event->partitionNum = -1; +} + +static void ParseUevent(const char *buf, struct Uevent *event) +{ + InitUevent(event); + while (*buf) { + if (strncmp(buf, "ACTION=", 7) == 0) { + buf += 7; + event->action = buf; + } else if (strncmp(buf, "DEVPATH=", 8) == 0) { + buf += 8; + event->path = buf; + } else if (strncmp(buf, "SUBSYSTEM=", 10) == 0) { + buf += 10; + event->subsystem = buf; + } else if (strncmp(buf, "FIRMWARE=", 9) == 0) { + buf += 9; + event->firmware = buf; + } else if (strncmp(buf, "MAJOR=", 6) == 0) { + buf += 6; + event->major = atoi(buf); + } else if (strncmp(buf, "MINOR=", 6) == 0) { + buf += 6; + event->minor = atoi(buf); + } else if (strncmp(buf, "PARTN=", 6) == 0) { + buf += 6; + event->partitionNum = atoi(buf); + } else if (strncmp(buf, "PARTNAME=", 9) == 0) { + buf += 9; + event->partitionName = buf; + } else if (strncmp(buf, "DEVNAME=", 8) == 0) { + buf += 8; + event->deviceName = buf; + } + // Drop reset. + while (*buf++) {} + } +} + +static int MakeDir(const char *path, mode_t mode) +{ + int rc = mkdir(path, mode); + if (rc < 0 && errno != EEXIST) { + printf("Create %s failed. %d\n", path, errno); + } + return rc; +} + +static struct PlatformNode *FindPlatformDevice(const char *path) +{ + size_t pathLen = strlen(path); + struct ListNode *node = NULL; + struct PlatformNode *bus = NULL; + + for (node = (&g_platformNames)->prev; node != &g_platformNames; node = node->prev) { + bus = (struct PlatformNode *)(((char*)(node)) - offsetof(struct PlatformNode, list)); + if ((bus->pathLen < pathLen) && (path[bus->pathLen] == '/') && !strncmp(path, bus->path, bus->pathLen)) { + return bus; + } + } + return NULL; +} + +static void Sanitize(char *s) +{ + const char* accept = + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789" + "_-."; + + if (!s) { + return; + } + + for (; *s; s++) { + s += strspn(s, accept); + if (*s) { + *s = '_'; + } + } +} + +static char **ParsePlatformBlockDevice(const struct Uevent *uevent) +{ + const char *device; + char *slash = NULL; + const char *type; + char linkPath[256]; + int linkNum = 0; + char *p = NULL; + + struct PlatformNode *pDev = FindPlatformDevice(uevent->path); + if (pDev) { + device = pDev->name; + type = "platform"; + } else { + printf("Non platform device.\n"); + return NULL; + } + + char **links = malloc(sizeof(char *) * 4); + if (!links) { + return NULL; + } + if (memset_s(links, sizeof(char *) * 4, 0, sizeof(char *) * 4) != 0) { + return NULL; + } + printf("found %s device %s\n", type, device); + if (snprintf_s(linkPath, sizeof(linkPath), sizeof(linkPath), "/dev/block/%s/%s", type, device) == -1) { + return NULL; + } + if (uevent->partitionName) { + p = strdup(uevent->partitionName); + Sanitize(p); + if (strcmp(uevent->partitionName, p)) { + printf("Linking partition '%s' as '%s'\n", uevent->partitionName, p); + } + if (asprintf(&links[linkNum], "%s/by-name/%s", linkPath, p) > 0) { + linkNum++; + } else { + links[linkNum] = NULL; + } + free(p); + } + if (uevent->partitionNum >= 0) { + if (asprintf(&links[linkNum], "%s/by-num/p%d", linkPath, uevent->partitionNum) > 0) { + linkNum++; + } else { + links[linkNum] = NULL; + } + } + slash = strrchr(uevent->path, '/'); + if (asprintf(&links[linkNum], "%s/%s", linkPath, slash + 1) > 0) { + linkNum++; + } else { + links[linkNum] = NULL; + } + return links; +} + +static void MakeDevice(const char *devpath, const char *path, int block, int major, int minor) +{ + /* Only for super user */ + gid_t gid = 0; + dev_t dev; + mode_t mode = 0600; + mode |= (block ? S_IFBLK : S_IFCHR); + dev = makedev(major, minor); + setegid(gid); + if (mknod(devpath, mode, dev) != 0) { + if (errno != EEXIST) { + printf("Make device node[%d, %d] failed. %d\n", major, minor, errno); + } + } +} + +int MkdirRecursive(const char *pathName, mode_t mode) +{ + char buf[128]; + const char *slash; + const char *p = pathName; + struct stat info; + + while ((slash = strchr(p, '/')) != NULL) { + int width = slash - pathName; + p = slash + 1; + if (width < 0) { + break; + } + if (width == 0) { + continue; + } + if ((unsigned int)width > sizeof(buf) - 1) { + printf("path too long for MkdirRecursive\n"); + return -1; + } + if (memcpy_s(buf, width, pathName, width) != 0) { + return -1; + } + buf[width] = 0; + if (stat(buf, &info) != 0) { + int ret = MakeDir(buf, mode); + if (ret && errno != EEXIST) { + return ret; + } + } + } + int ret = MakeDir(pathName, mode); + if (ret && errno != EEXIST) { + return ret; + } + return 0; +} + +void RemoveLink(const char *oldpath, const char *newpath) +{ + char path[256]; + ssize_t ret = readlink(newpath, path, sizeof(path) - 1); + if (ret <= 0) { + return; + } + path[ret] = 0; + if (!strcmp(path, oldpath)) { + unlink(newpath); + } +} + +static void MakeLink(const char *oldPath, const char *newPath) +{ + char buf[256]; + char *slash = strrchr(newPath, '/'); + if (!slash) { + return; + } + int width = slash - newPath; + if (width <= 0 || width > (int)sizeof(buf) - 1) { + return; + } + if (memcpy_s(buf, sizeof(buf), newPath, width) != 0) { + return; + } + buf[width] = 0; + int ret = MkdirRecursive(buf, 0755); + if (ret) { + printf("Failed to create directory %s: %s (%d)\n", buf, strerror(errno), errno); + } + ret = symlink(oldPath, newPath); + if (ret && errno != EEXIST) { + printf("Failed to symlink %s to %s: %s (%d)\n", oldPath, newPath, strerror(errno), errno); + } +} + +static void HandleDevice(const char *action, const char *devpath, const char *path, int block, int major, + int minor, char **links) +{ + int i; + if (!strcmp(action, "add")) { + MakeDevice(devpath, path, block, major, minor); + if (links) { + for (i = 0; links[i]; i++) { + MakeLink(devpath, links[i]); + } + } + } + + if (!strcmp(action, "remove")) { + if (links) { + for (i = 0; links[i]; i++) { + RemoveLink(devpath, links[i]); + } + } + unlink(devpath); + } + + if (links) { + for (i = 0; links[i]; i++) { + free(links[i]); + } + free(links); + } +} + +static void HandleBlockDevice(struct Uevent *event) +{ + const char *base = "/dev/block"; + char devpath[96]; + char **links = NULL; + + if (event->major < 0 || event->minor < 0) { + return; + } + const char *name = strrchr(event->path, '/'); + if (name == NULL) { + return; + } + name++; + if (strlen(name) > 64) { // too long + return; + } + if (snprintf_s(devpath, sizeof(devpath), sizeof(devpath), "%s/%s", base, name) == -1) { + return; + } + MakeDir(base, 0755); + if (!strncmp(event->path, "/devices/", 9)) { + links = ParsePlatformBlockDevice(event); + } + HandleDevice(event->action, devpath, event->path, 1, event->major, event->minor, links); +} + +static void AddPlatformDevice(const char *path) +{ + size_t pathLen = strlen(path); + const char *name = path; + + if (!strncmp(path, "/devices/", 9)) { + name += 9; + if (!strncmp(name, "platform/", 9)) { + name += 9; + } + } + printf("adding platform device %s (%s)\n", name, path); + struct PlatformNode *bus = calloc(1, sizeof(struct PlatformNode)); + bus->path = strdup(path); + bus->pathLen = pathLen; + bus->name = bus->path + (name - path); + ListAddTail(&g_platformNames, &bus->list); +} + +static void RemovePlatformDevice(const char *path) +{ + struct ListNode *node = NULL; + struct PlatformNode *bus = NULL; + + for (node = (&g_platformNames)->prev; node != &g_platformNames; node = node->prev) { + bus = (struct PlatformNode *)(((char*)(node)) - offsetof(struct PlatformNode, list)); + if (!strcmp(path, bus->path)) { + printf("removing platform device %s\n", bus->name); + free(bus->path); + ListRemove(node); + free(bus); + return; + } + } +} + +static void HandlePlatformDevice(const struct Uevent *event) +{ + const char *path = event->path; + if (strcmp(event->action, "add") == 0) { + AddPlatformDevice(path); + } else if (strcmp(event->action, "remove") == 0) { + RemovePlatformDevice(path); + } +} + +static const char *ParseDeviceName(const struct Uevent *uevent, unsigned int len) +{ + /* if it's not a /dev device, nothing else to do */ + if ((uevent->major < 0) || (uevent->minor < 0)) { + return NULL; + } + /* do we have a name? */ + const char *name = strrchr(uevent->path, '/'); + if (!name) { + return NULL; + } + name++; + /* too-long names would overrun our buffer */ + if (strlen(name) > len) { + return NULL; + } + return name; +} + +static char **GetCharacterDeviceSymlinks(const struct Uevent *uevent) +{ + char *slash = NULL; + int linkNum = 0; + int width; + + struct PlatformNode *pDev = FindPlatformDevice(uevent->path); + if (!pDev) { + return NULL; + } + + char **links = malloc(sizeof(char *) * 2); + if (!links) { + return NULL; + } + if (memset_s(links, sizeof(char *) * 2, 0, sizeof(char *) * 2) != 0) { + return NULL; + } + + /* skip "/devices/platform/" */ + const char *parent = strchr(uevent->path + pDev->pathLen, '/'); + if (!*parent) { + goto err; + } + + if (!strncmp(parent, "/usb", 4)) { + /* skip root hub name and device. use device interface */ + while (*++parent && *parent != '/') {} + if (*parent) { + while (*++parent && *parent != '/') {} + } + if (!*parent) { + goto err; + } + slash = strchr(++parent, '/'); + if (!slash) { + goto err; + } + width = slash - parent; + if (width <= 0) { + goto err; + } + + if (asprintf(&links[linkNum], "/dev/usb/%s%.*s", uevent->subsystem, width, parent) > 0) { + linkNum++; + } else { + links[linkNum] = NULL; + } + mkdir("/dev/usb", 0755); + } else { + goto err; + } + return links; +err: + free(links); + return NULL; +} + +static void HandleGenericDevice(struct Uevent *event) +{ + char *base = NULL; + char devpath[96] = {0}; + char **links = NULL; + + const char *name = ParseDeviceName(event, 64); + if (!name) { + return; + } + if (!strncmp(event->subsystem, "usb", 3)) { + if (!strcmp(event->subsystem, "usb")) { + if (event->deviceName) { + /* + * create device node provided by kernel if present + * see drivers/base/core.c + */ + char *p = devpath; + if (snprintf_s(devpath, sizeof(devpath), sizeof(devpath), "/dev/%s", event->deviceName) == -1) { + return; + } + /* skip leading /dev/ */ + p += 5; + /* build directories */ + while (*p) { + if (*p == '/') { + *p = 0; + MakeDir(devpath, 0755); + *p = '/'; + } + p++; + } + } else { + /* This imitates the file system that would be created + * if we were using devfs instead. + * Minors are broken up into groups of 128, starting at "001" + */ + int busId = event->minor / 128 + 1; + int deviceId = event->minor % 128 + 1; + /* build directories */ + MakeDir("/dev/bus", 0755); + MakeDir("/dev/bus/usb", 0755); + if (snprintf_s(devpath, sizeof(devpath), sizeof(devpath), "/dev/bus/usb/%03d", busId) == -1) { + return; + } + MakeDir(devpath, 0755); + if (snprintf_s(devpath, sizeof(devpath), sizeof(devpath), "/dev/bus/usb/%03d/%03d", busId, + deviceId) == -1) { + return; + } + } + } else { + /* ignore other USB events */ + return; + } + } else if (!strncmp(event->subsystem, "graphics", 8)) { + base = "/dev/graphics/"; + MakeDir(base, 0755); + } else if (!strncmp(event->subsystem, "drm", 3)) { + base = "/dev/dri/"; + MakeDir(base, 0755); + } else if (!strncmp(event->subsystem, "oncrpc", 6)) { + base = "/dev/oncrpc/"; + MakeDir(base, 0755); + } else if (!strncmp(event->subsystem, "adsp", 4)) { + base = "/dev/adsp/"; + MakeDir(base, 0755); + } else if (!strncmp(event->subsystem, "input", 5)) { + base = "/dev/input/"; + MakeDir(base, 0755); + } else if (!strncmp(event->subsystem, "mtd", 3)) { + base = "/dev/mtd/"; + MakeDir(base, 0755); + } else if (!strncmp(event->subsystem, "sound", 5)) { + base = "/dev/snd/"; + MakeDir(base, 0755); + } else if (!strncmp(event->subsystem, "misc", 4) && !strncmp(name, "log_", 4)) { + base = "/dev/log/"; + MakeDir(base, 0755); + name += 4; + } else { + base = "/dev/"; + } + links = GetCharacterDeviceSymlinks(event); + if (!devpath[0]) { + if (snprintf_s(devpath, sizeof(devpath), sizeof(devpath), "%s%s", base, name) == -1) { + return; + } + } + HandleDevice(event->action, devpath, event->path, 0, + event->major, event->minor, links); +} + +static void HandleDeviceUevent(struct Uevent *event) +{ + if (strcmp(event->action, "add") == 0 || strcmp(event->action, "change") == 0) { + /* Do nothing for now */ + } + if (strncmp(event->subsystem, "block", 5) == 0) { + HandleBlockDevice(event); + } else if (strncmp(event->subsystem, "platform", 8) == 0) { + HandlePlatformDevice(event); + } else { + HandleGenericDevice(event); + } +} + +static void HandleUevent() +{ + char buf[1024 + 2]; + int ret; + struct Uevent event; + while ((ret = ReadUevent(g_ueventFD, buf, 1024)) > 0) { + if (ret >= 1024) { + continue; + } + buf[ret] = '\0'; + buf[ret + 1] = '\0'; + ParseUevent(buf, &event); + HandleDeviceUevent(&event); + } +} + +void UeventInit() +{ + struct pollfd ufd; + UeventSockInit(); + ufd.events = POLLIN; + ufd.fd = UeventFD(); + while (1) { + ufd.revents = 0; + int ret = poll(&ufd, 1, -1); + if (ret <= 0) { + continue; + } + if (ufd.revents & POLLIN) { + HandleUevent(); + } + } + return; +} + +int main(int argc, char **argv) +{ + printf("Uevent demo starting...\n"); + UeventInit(); + return 0; +} -- GitLab