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}