/* * Copyright (c) 2021 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 "ueventd.h" #include #include #include #include #include #include #include #include "ueventd_device_handler.h" #include "ueventd_firmware_handler.h" #include "ueventd_read_cfg.h" #include "ueventd_socket.h" #include "ueventd_utils.h" #include "securec.h" #define INIT_LOG_TAG "ueventd" #include "init_log.h" #include "init_utils.h" // buffer size refer to kernel kobject uevent #define UEVENT_BUFFER_SIZE (2048 + 1) char bootDevice[CMDLINE_VALUE_LEN_MAX] = { 0 }; #define WRITE_SIZE 4 static const char *actions[] = { [ACTION_ADD] = "add", [ACTION_REMOVE] = "remove", [ACTION_CHANGE] = "change", [ACTION_MOVE] = "move", [ACTION_ONLINE] = "online", [ACTION_OFFLINE] = "offline", [ACTION_BIND] = "bind", [ACTION_UNBIND] = "unbind", [ACTION_UNKNOWN] = "unknow", }; static SUBSYSTEMTYPE GetSubsystemType(const char *subsystem) { if (subsystem == NULL || *subsystem == '\0') { return SUBSYSTEM_EMPTY; } if (strcmp(subsystem, "block") == 0) { return SUBSYSTEM_BLOCK; } else if (strcmp(subsystem, "platform") == 0) { return SUBSYSTEM_PLATFORM; } else if (strcmp(subsystem, "firmware") == 0) { return SUBSYSTEM_FIRMWARE; } else { return SUBSYSTEM_OTHERS; } } const char *ActionString(ACTION action) { return actions[action]; } static ACTION GetUeventAction(const char *action) { if (action == NULL || *action == '\0') { return ACTION_UNKNOWN; } if (STRINGEQUAL(action, "add")) { return ACTION_ADD; } else if (STRINGEQUAL(action, "remove")) { return ACTION_REMOVE; } else if (STRINGEQUAL(action, "change")) { return ACTION_CHANGE; } else if (STRINGEQUAL(action, "move")) { return ACTION_MOVE; } else if (STRINGEQUAL(action, "online")) { return ACTION_ONLINE; } else if (STRINGEQUAL(action, "offline")) { return ACTION_OFFLINE; } else if (STRINGEQUAL(action, "bind")) { return ACTION_BIND; } else if (STRINGEQUAL(action, "unbind")) { return ACTION_UNBIND; } else { return ACTION_UNKNOWN; } } static void HandleUevent(const struct Uevent *uevent) { if (uevent->action == ACTION_ADD || uevent->action == ACTION_CHANGE || uevent->action == ACTION_ONLINE) { ChangeSysAttributePermissions(uevent->syspath); } SUBSYSTEMTYPE type = GetSubsystemType(uevent->subsystem); switch (type) { case SUBSYSTEM_BLOCK: HandleBlockDeviceEvent(uevent); break; case SUBSYSTEM_FIRMWARE: HandleFimwareDeviceEvent(uevent); break; case SUBSYSTEM_OTHERS: HandleOtherDeviceEvent(uevent); break; default: break; } } static void HandleUeventRequired(const struct Uevent *uevent, char **devices, int num) { INIT_ERROR_CHECK(devices != NULL && num > 0, return, "Fault paramters"); if (uevent->action == ACTION_ADD) { ChangeSysAttributePermissions(uevent->syspath); } SUBSYSTEMTYPE type = GetSubsystemType(uevent->subsystem); if (type == SUBSYSTEM_BLOCK) { for (int i = 0; i < num; i++) { if (uevent->partitionName == NULL) { break; } if (strstr(devices[i], uevent->partitionName) != NULL) { INIT_LOGI("Handle block device partitionName %s", uevent->partitionName); HandleBlockDeviceEvent(uevent); break; } } } } static void AddUevent(struct Uevent *uevent, const char *event, size_t len) { if (uevent == NULL || event == NULL || len == 0) { return; } if (STARTSWITH(event, "DEVPATH=")) { uevent->syspath = event + strlen("DEVPATH="); } else if (STARTSWITH(event, "SUBSYSTEM=")) { uevent->subsystem = event + strlen("SUBSYSTEM="); } else if (STARTSWITH(event, "ACTION=")) { uevent->action = GetUeventAction(event + strlen("ACTION=")); } else if (STARTSWITH(event, "DEVNAME=")) { uevent->deviceName = event + strlen("DEVNAME="); } else if (STARTSWITH(event, "PARTNAME=")) { uevent->partitionName = event + strlen("PARTNAME="); } else if (STARTSWITH(event, "PARTN=")) { uevent->partitionNum = StringToInt(event + strlen("PARTN="), -1); } else if (STARTSWITH(event, "MAJOR=")) { uevent->major = StringToInt(event + strlen("MAJOR="), -1); } else if (STARTSWITH(event, "MINOR=")) { uevent->minor = StringToInt(event + strlen("MINOR="), -1); } else if (STARTSWITH(event, "DEVUID")) { uevent->ug.uid = (uid_t)StringToInt(event + strlen("DEVUID="), 0); } else if (STARTSWITH(event, "DEVGID")) { uevent->ug.gid = (gid_t)StringToInt(event + strlen("DEVGID="), 0); } else if (STARTSWITH(event, "FIRMWARE=")) { uevent->firmware = event + strlen("FIRMWARE="); } else if (STARTSWITH(event, "BUSNUM=")) { uevent->busNum = StringToInt(event + strlen("BUSNUM="), -1); } else if (STARTSWITH(event, "DEVNUM=")) { uevent->devNum = StringToInt(event + strlen("DEVNUM="), -1); } // Ignore other events } void ParseUeventMessage(const char *buffer, ssize_t length, struct Uevent *uevent) { if (buffer == NULL || uevent == NULL || length == 0) { // Ignore invalid buffer return; } // reset parititon number, major and minor. uevent->partitionNum = -1; uevent->major = -1; uevent->minor = -1; uevent->busNum = -1; uevent->devNum = -1; ssize_t pos = 0; while (pos < length) { const char *event = buffer + pos; size_t len = strlen(event); if (len == 0) { break; } AddUevent(uevent, event, len); pos += (ssize_t)len + 1; } } void ProcessUevent(int sockFd, char **devices, int num) { // One more bytes for '\0' char ueventBuffer[UEVENT_BUFFER_SIZE] = {}; ssize_t n = 0; struct Uevent uevent = {}; while ((n = ReadUeventMessage(sockFd, ueventBuffer, sizeof(ueventBuffer) - 1)) > 0) { ParseUeventMessage(ueventBuffer, n, &uevent); if (uevent.syspath == NULL) { INIT_LOGD("Ignore unexpected uevent"); return; } if (devices != NULL && num > 0) { HandleUeventRequired(&uevent, devices, num); } else { HandleUevent(&uevent); } } } static int g_triggerDone = 0; static void DoTrigger(const char *ueventPath, int sockFd, char **devices, int num) { if (ueventPath == NULL || ueventPath[0] == '\0') { return; } int fd = open(ueventPath, O_WRONLY | O_CLOEXEC); if (fd < 0) { INIT_LOGE("Open \" %s \" failed, err = %d", ueventPath, errno); } else { ssize_t n = write(fd, "add\n", WRITE_SIZE); if (n < 0) { INIT_LOGE("Write \" %s \" failed, err = %d", ueventPath, errno); close(fd); } else { close(fd); // uevent triggered, now handle it. if (sockFd >= 0) { ProcessUevent(sockFd, devices, num); } } } } static void Trigger(const char *path, int sockFd, char **devices, int num) { if (path == NULL) { return; } DIR *dir = opendir(path); if (dir == NULL) { return; } struct dirent *dirent = NULL; while ((dirent = readdir(dir)) != NULL) { if (dirent->d_name[0] == '.') { continue; } if (dirent->d_type == DT_DIR) { char pathBuffer[PATH_MAX]; if (snprintf_s(pathBuffer, PATH_MAX, PATH_MAX - 1, "%s/%s", path, dirent->d_name) == -1) { continue; } Trigger(pathBuffer, sockFd, devices, num); } else { if (strcmp(dirent->d_name, "uevent") != 0) { continue; } char ueventBuffer[PATH_MAX]; if (snprintf_s(ueventBuffer, PATH_MAX, PATH_MAX - 1, "%s/%s", path, "uevent") == -1) { INIT_LOGW("Cannnot build uevent path under %s", path); continue; } DoTrigger(ueventBuffer, sockFd, devices, num); } } closedir(dir); } void RetriggerUevent(int sockFd, char **devices, int num) { char *buffer = ReadFileData("/proc/cmdline"); int ret = GetProcCmdlineValue("default_boot_device", buffer, bootDevice, CMDLINE_VALUE_LEN_MAX); INIT_CHECK_ONLY_ELOG(ret == 0, "Failed get default_boot_device value from cmdline"); if (!g_triggerDone) { Trigger("/sys/block", sockFd, devices, num); Trigger("/sys/class", sockFd, devices, num); Trigger("/sys/devices", sockFd, devices, num); g_triggerDone = 1; } if (buffer != NULL) { free(buffer); } }