199ca880aSopenharmony_ci/* 299ca880aSopenharmony_ci * Copyright (C) 2003-2012 Kay Sievers <kay@vrfy.org> 399ca880aSopenharmony_ci * 499ca880aSopenharmony_ci * This program is free software: you can redistribute it and/or modify 599ca880aSopenharmony_ci * it under the terms of the GNU General Public License as published by 699ca880aSopenharmony_ci * the Free Software Foundation, either version 2 of the License, or 799ca880aSopenharmony_ci * (at your option) any later version. 899ca880aSopenharmony_ci * 999ca880aSopenharmony_ci * This program is distributed in the hope that it will be useful, 1099ca880aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 1199ca880aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1299ca880aSopenharmony_ci * GNU General Public License for more details. 1399ca880aSopenharmony_ci * 1499ca880aSopenharmony_ci * You should have received a copy of the GNU General Public License 1599ca880aSopenharmony_ci * along with this program. If not, see <http://www.gnu.org/licenses/>. 1699ca880aSopenharmony_ci */ 1799ca880aSopenharmony_ci 1899ca880aSopenharmony_ci#include <stddef.h> 1999ca880aSopenharmony_ci#include <limits.h> 2099ca880aSopenharmony_ci#include <stdlib.h> 2199ca880aSopenharmony_ci#include <stdbool.h> 2299ca880aSopenharmony_ci#include <string.h> 2399ca880aSopenharmony_ci#include <stdio.h> 2499ca880aSopenharmony_ci#include <fcntl.h> 2599ca880aSopenharmony_ci#include <ctype.h> 2699ca880aSopenharmony_ci#include <unistd.h> 2799ca880aSopenharmony_ci#include <errno.h> 2899ca880aSopenharmony_ci#include <dirent.h> 2999ca880aSopenharmony_ci#include <fnmatch.h> 3099ca880aSopenharmony_ci#include <time.h> 3199ca880aSopenharmony_ci#include <sys/sysmacros.h> 3299ca880aSopenharmony_ci 3399ca880aSopenharmony_ci#include "udev.h" 3499ca880aSopenharmony_ci#include "path-util.h" 3599ca880aSopenharmony_ci#include "conf-files.h" 3699ca880aSopenharmony_ci#include "strbuf.h" 3799ca880aSopenharmony_ci#include "strv.h" 3899ca880aSopenharmony_ci#include "util.h" 3999ca880aSopenharmony_ci#include "sysctl-util.h" 4099ca880aSopenharmony_ci 4199ca880aSopenharmony_ci#define PREALLOC_TOKEN 2048 4299ca880aSopenharmony_ci 4399ca880aSopenharmony_cistruct uid_gid { 4499ca880aSopenharmony_ci unsigned int name_off; 4599ca880aSopenharmony_ci union { 4699ca880aSopenharmony_ci uid_t uid; 4799ca880aSopenharmony_ci gid_t gid; 4899ca880aSopenharmony_ci }; 4999ca880aSopenharmony_ci}; 5099ca880aSopenharmony_ci 5199ca880aSopenharmony_cistatic const char* const rules_dirs[] = { 5299ca880aSopenharmony_ci UDEV_CONF_DIR "/rules.d", 5399ca880aSopenharmony_ci UDEV_RULES_DIR, 5499ca880aSopenharmony_ci UDEV_ROOT_RUN "/udev/rules.d", 5599ca880aSopenharmony_ci UDEV_LIBEXEC_DIR "/rules.d", 5699ca880aSopenharmony_ci#ifdef HAVE_SPLIT_USR 5799ca880aSopenharmony_ci "/lib/udev/rules.d", 5899ca880aSopenharmony_ci "/usr/lib/udev/rules.d", 5999ca880aSopenharmony_ci#endif 6099ca880aSopenharmony_ci NULL}; 6199ca880aSopenharmony_ci 6299ca880aSopenharmony_cistruct udev_rules { 6399ca880aSopenharmony_ci struct udev *udev; 6499ca880aSopenharmony_ci usec_t dirs_ts_usec; 6599ca880aSopenharmony_ci int resolve_names; 6699ca880aSopenharmony_ci 6799ca880aSopenharmony_ci /* every key in the rules file becomes a token */ 6899ca880aSopenharmony_ci struct token *tokens; 6999ca880aSopenharmony_ci unsigned int token_cur; 7099ca880aSopenharmony_ci unsigned int token_max; 7199ca880aSopenharmony_ci 7299ca880aSopenharmony_ci /* all key strings are copied and de-duplicated in a single continuous string buffer */ 7399ca880aSopenharmony_ci struct strbuf *strbuf; 7499ca880aSopenharmony_ci 7599ca880aSopenharmony_ci /* during rule parsing, uid/gid lookup results are cached */ 7699ca880aSopenharmony_ci struct uid_gid *uids; 7799ca880aSopenharmony_ci unsigned int uids_cur; 7899ca880aSopenharmony_ci unsigned int uids_max; 7999ca880aSopenharmony_ci struct uid_gid *gids; 8099ca880aSopenharmony_ci unsigned int gids_cur; 8199ca880aSopenharmony_ci unsigned int gids_max; 8299ca880aSopenharmony_ci}; 8399ca880aSopenharmony_ci 8499ca880aSopenharmony_cistatic char *rules_str(struct udev_rules *rules, unsigned int off) { 8599ca880aSopenharmony_ci return rules->strbuf->buf + off; 8699ca880aSopenharmony_ci} 8799ca880aSopenharmony_ci 8899ca880aSopenharmony_cistatic unsigned int rules_add_string(struct udev_rules *rules, const char *s) { 8999ca880aSopenharmony_ci return strbuf_add_string(rules->strbuf, s, strlen(s)); 9099ca880aSopenharmony_ci} 9199ca880aSopenharmony_ci 9299ca880aSopenharmony_ci/* KEY=="", KEY!="", KEY+="", KEY-="", KEY="", KEY:="" */ 9399ca880aSopenharmony_cienum operation_type { 9499ca880aSopenharmony_ci OP_UNSET, 9599ca880aSopenharmony_ci 9699ca880aSopenharmony_ci OP_MATCH, 9799ca880aSopenharmony_ci OP_NOMATCH, 9899ca880aSopenharmony_ci OP_MATCH_MAX, 9999ca880aSopenharmony_ci 10099ca880aSopenharmony_ci OP_ADD, 10199ca880aSopenharmony_ci OP_REMOVE, 10299ca880aSopenharmony_ci OP_ASSIGN, 10399ca880aSopenharmony_ci OP_ASSIGN_FINAL, 10499ca880aSopenharmony_ci}; 10599ca880aSopenharmony_ci 10699ca880aSopenharmony_cienum string_glob_type { 10799ca880aSopenharmony_ci GL_UNSET, 10899ca880aSopenharmony_ci GL_PLAIN, /* no special chars */ 10999ca880aSopenharmony_ci GL_GLOB, /* shell globs ?,*,[] */ 11099ca880aSopenharmony_ci GL_SPLIT, /* multi-value A|B */ 11199ca880aSopenharmony_ci GL_SPLIT_GLOB, /* multi-value with glob A*|B* */ 11299ca880aSopenharmony_ci GL_SOMETHING, /* commonly used "?*" */ 11399ca880aSopenharmony_ci}; 11499ca880aSopenharmony_ci 11599ca880aSopenharmony_cienum string_subst_type { 11699ca880aSopenharmony_ci SB_UNSET, 11799ca880aSopenharmony_ci SB_NONE, 11899ca880aSopenharmony_ci SB_FORMAT, 11999ca880aSopenharmony_ci SB_SUBSYS, 12099ca880aSopenharmony_ci}; 12199ca880aSopenharmony_ci 12299ca880aSopenharmony_ci/* tokens of a rule are sorted/handled in this order */ 12399ca880aSopenharmony_cienum token_type { 12499ca880aSopenharmony_ci TK_UNSET, 12599ca880aSopenharmony_ci TK_RULE, 12699ca880aSopenharmony_ci 12799ca880aSopenharmony_ci TK_M_ACTION, /* val */ 12899ca880aSopenharmony_ci TK_M_DEVPATH, /* val */ 12999ca880aSopenharmony_ci TK_M_KERNEL, /* val */ 13099ca880aSopenharmony_ci TK_M_DEVLINK, /* val */ 13199ca880aSopenharmony_ci TK_M_NAME, /* val */ 13299ca880aSopenharmony_ci TK_M_ENV, /* val, attr */ 13399ca880aSopenharmony_ci TK_M_TAG, /* val */ 13499ca880aSopenharmony_ci TK_M_SUBSYSTEM, /* val */ 13599ca880aSopenharmony_ci TK_M_DRIVER, /* val */ 13699ca880aSopenharmony_ci TK_M_WAITFOR, /* val */ 13799ca880aSopenharmony_ci TK_M_ATTR, /* val, attr */ 13899ca880aSopenharmony_ci TK_M_SYSCTL, /* val, attr */ 13999ca880aSopenharmony_ci 14099ca880aSopenharmony_ci TK_M_PARENTS_MIN, 14199ca880aSopenharmony_ci TK_M_KERNELS, /* val */ 14299ca880aSopenharmony_ci TK_M_SUBSYSTEMS, /* val */ 14399ca880aSopenharmony_ci TK_M_DRIVERS, /* val */ 14499ca880aSopenharmony_ci TK_M_ATTRS, /* val, attr */ 14599ca880aSopenharmony_ci TK_M_TAGS, /* val */ 14699ca880aSopenharmony_ci TK_M_PARENTS_MAX, 14799ca880aSopenharmony_ci 14899ca880aSopenharmony_ci TK_M_TEST, /* val, mode_t */ 14999ca880aSopenharmony_ci TK_M_PROGRAM, /* val */ 15099ca880aSopenharmony_ci TK_M_IMPORT_FILE, /* val */ 15199ca880aSopenharmony_ci TK_M_IMPORT_PROG, /* val */ 15299ca880aSopenharmony_ci TK_M_IMPORT_BUILTIN, /* val */ 15399ca880aSopenharmony_ci TK_M_IMPORT_DB, /* val */ 15499ca880aSopenharmony_ci TK_M_IMPORT_CMDLINE, /* val */ 15599ca880aSopenharmony_ci TK_M_IMPORT_PARENT, /* val */ 15699ca880aSopenharmony_ci TK_M_RESULT, /* val */ 15799ca880aSopenharmony_ci TK_M_MAX, 15899ca880aSopenharmony_ci 15999ca880aSopenharmony_ci TK_A_STRING_ESCAPE_NONE, 16099ca880aSopenharmony_ci TK_A_STRING_ESCAPE_REPLACE, 16199ca880aSopenharmony_ci TK_A_DB_PERSIST, 16299ca880aSopenharmony_ci TK_A_INOTIFY_WATCH, /* int */ 16399ca880aSopenharmony_ci TK_A_DEVLINK_PRIO, /* int */ 16499ca880aSopenharmony_ci TK_A_OWNER, /* val */ 16599ca880aSopenharmony_ci TK_A_GROUP, /* val */ 16699ca880aSopenharmony_ci TK_A_MODE, /* val */ 16799ca880aSopenharmony_ci TK_A_OWNER_ID, /* uid_t */ 16899ca880aSopenharmony_ci TK_A_GROUP_ID, /* gid_t */ 16999ca880aSopenharmony_ci TK_A_MODE_ID, /* mode_t */ 17099ca880aSopenharmony_ci TK_A_TAG, /* val */ 17199ca880aSopenharmony_ci TK_A_STATIC_NODE, /* val */ 17299ca880aSopenharmony_ci TK_A_SECLABEL, /* val, attr */ 17399ca880aSopenharmony_ci TK_A_ENV, /* val, attr */ 17499ca880aSopenharmony_ci TK_A_NAME, /* val */ 17599ca880aSopenharmony_ci TK_A_DEVLINK, /* val */ 17699ca880aSopenharmony_ci TK_A_ATTR, /* val, attr */ 17799ca880aSopenharmony_ci TK_A_SYSCTL, /* val, attr */ 17899ca880aSopenharmony_ci TK_A_RUN_BUILTIN, /* val, bool */ 17999ca880aSopenharmony_ci TK_A_RUN_PROGRAM, /* val, bool */ 18099ca880aSopenharmony_ci TK_A_GOTO, /* size_t */ 18199ca880aSopenharmony_ci 18299ca880aSopenharmony_ci TK_END, 18399ca880aSopenharmony_ci}; 18499ca880aSopenharmony_ci 18599ca880aSopenharmony_ci/* we try to pack stuff in a way that we take only 12 bytes per token */ 18699ca880aSopenharmony_cistruct token { 18799ca880aSopenharmony_ci union { 18899ca880aSopenharmony_ci unsigned char type; /* same in rule and key */ 18999ca880aSopenharmony_ci struct { 19099ca880aSopenharmony_ci enum token_type type:8; 19199ca880aSopenharmony_ci bool can_set_name:1; 19299ca880aSopenharmony_ci bool has_static_node:1; 19399ca880aSopenharmony_ci unsigned int unused:6; 19499ca880aSopenharmony_ci unsigned short token_count; 19599ca880aSopenharmony_ci unsigned int label_off; 19699ca880aSopenharmony_ci unsigned short filename_off; 19799ca880aSopenharmony_ci unsigned short filename_line; 19899ca880aSopenharmony_ci } rule; 19999ca880aSopenharmony_ci struct { 20099ca880aSopenharmony_ci enum token_type type:8; 20199ca880aSopenharmony_ci enum operation_type op:8; 20299ca880aSopenharmony_ci enum string_glob_type glob:8; 20399ca880aSopenharmony_ci enum string_subst_type subst:4; 20499ca880aSopenharmony_ci enum string_subst_type attrsubst:4; 20599ca880aSopenharmony_ci unsigned int value_off; 20699ca880aSopenharmony_ci union { 20799ca880aSopenharmony_ci unsigned int attr_off; 20899ca880aSopenharmony_ci unsigned int rule_goto; 20999ca880aSopenharmony_ci mode_t mode; 21099ca880aSopenharmony_ci uid_t uid; 21199ca880aSopenharmony_ci gid_t gid; 21299ca880aSopenharmony_ci int devlink_prio; 21399ca880aSopenharmony_ci int watch; 21499ca880aSopenharmony_ci enum udev_builtin_cmd builtin_cmd; 21599ca880aSopenharmony_ci }; 21699ca880aSopenharmony_ci } key; 21799ca880aSopenharmony_ci }; 21899ca880aSopenharmony_ci}; 21999ca880aSopenharmony_ci 22099ca880aSopenharmony_ci#define MAX_TK 64 22199ca880aSopenharmony_cistruct rule_tmp { 22299ca880aSopenharmony_ci struct udev_rules *rules; 22399ca880aSopenharmony_ci struct token rule; 22499ca880aSopenharmony_ci struct token token[MAX_TK]; 22599ca880aSopenharmony_ci unsigned int token_cur; 22699ca880aSopenharmony_ci}; 22799ca880aSopenharmony_ci 22899ca880aSopenharmony_ci#ifdef DEBUG 22999ca880aSopenharmony_cistatic const char *operation_str(enum operation_type type) { 23099ca880aSopenharmony_ci static const char *operation_strs[] = { 23199ca880aSopenharmony_ci [OP_UNSET] = "UNSET", 23299ca880aSopenharmony_ci [OP_MATCH] = "match", 23399ca880aSopenharmony_ci [OP_NOMATCH] = "nomatch", 23499ca880aSopenharmony_ci [OP_MATCH_MAX] = "MATCH_MAX", 23599ca880aSopenharmony_ci 23699ca880aSopenharmony_ci [OP_ADD] = "add", 23799ca880aSopenharmony_ci [OP_REMOVE] = "remove", 23899ca880aSopenharmony_ci [OP_ASSIGN] = "assign", 23999ca880aSopenharmony_ci [OP_ASSIGN_FINAL] = "assign-final", 24099ca880aSopenharmony_ci} ; 24199ca880aSopenharmony_ci 24299ca880aSopenharmony_ci return operation_strs[type]; 24399ca880aSopenharmony_ci} 24499ca880aSopenharmony_ci 24599ca880aSopenharmony_cistatic const char *string_glob_str(enum string_glob_type type) { 24699ca880aSopenharmony_ci static const char *string_glob_strs[] = { 24799ca880aSopenharmony_ci [GL_UNSET] = "UNSET", 24899ca880aSopenharmony_ci [GL_PLAIN] = "plain", 24999ca880aSopenharmony_ci [GL_GLOB] = "glob", 25099ca880aSopenharmony_ci [GL_SPLIT] = "split", 25199ca880aSopenharmony_ci [GL_SPLIT_GLOB] = "split-glob", 25299ca880aSopenharmony_ci [GL_SOMETHING] = "split-glob", 25399ca880aSopenharmony_ci }; 25499ca880aSopenharmony_ci 25599ca880aSopenharmony_ci return string_glob_strs[type]; 25699ca880aSopenharmony_ci} 25799ca880aSopenharmony_ci 25899ca880aSopenharmony_cistatic const char *token_str(enum token_type type) { 25999ca880aSopenharmony_ci static const char *token_strs[] = { 26099ca880aSopenharmony_ci [TK_UNSET] = "UNSET", 26199ca880aSopenharmony_ci [TK_RULE] = "RULE", 26299ca880aSopenharmony_ci 26399ca880aSopenharmony_ci [TK_M_ACTION] = "M ACTION", 26499ca880aSopenharmony_ci [TK_M_DEVPATH] = "M DEVPATH", 26599ca880aSopenharmony_ci [TK_M_KERNEL] = "M KERNEL", 26699ca880aSopenharmony_ci [TK_M_DEVLINK] = "M DEVLINK", 26799ca880aSopenharmony_ci [TK_M_NAME] = "M NAME", 26899ca880aSopenharmony_ci [TK_M_ENV] = "M ENV", 26999ca880aSopenharmony_ci [TK_M_TAG] = "M TAG", 27099ca880aSopenharmony_ci [TK_M_SUBSYSTEM] = "M SUBSYSTEM", 27199ca880aSopenharmony_ci [TK_M_DRIVER] = "M DRIVER", 27299ca880aSopenharmony_ci [TK_M_WAITFOR] = "M WAITFOR", 27399ca880aSopenharmony_ci [TK_M_ATTR] = "M ATTR", 27499ca880aSopenharmony_ci [TK_M_SYSCTL] = "M SYSCTL", 27599ca880aSopenharmony_ci 27699ca880aSopenharmony_ci [TK_M_PARENTS_MIN] = "M PARENTS_MIN", 27799ca880aSopenharmony_ci [TK_M_KERNELS] = "M KERNELS", 27899ca880aSopenharmony_ci [TK_M_SUBSYSTEMS] = "M SUBSYSTEMS", 27999ca880aSopenharmony_ci [TK_M_DRIVERS] = "M DRIVERS", 28099ca880aSopenharmony_ci [TK_M_ATTRS] = "M ATTRS", 28199ca880aSopenharmony_ci [TK_M_TAGS] = "M TAGS", 28299ca880aSopenharmony_ci [TK_M_PARENTS_MAX] = "M PARENTS_MAX", 28399ca880aSopenharmony_ci 28499ca880aSopenharmony_ci [TK_M_TEST] = "M TEST", 28599ca880aSopenharmony_ci [TK_M_PROGRAM] = "M PROGRAM", 28699ca880aSopenharmony_ci [TK_M_IMPORT_FILE] = "M IMPORT_FILE", 28799ca880aSopenharmony_ci [TK_M_IMPORT_PROG] = "M IMPORT_PROG", 28899ca880aSopenharmony_ci [TK_M_IMPORT_BUILTIN] = "M IMPORT_BUILTIN", 28999ca880aSopenharmony_ci [TK_M_IMPORT_DB] = "M IMPORT_DB", 29099ca880aSopenharmony_ci [TK_M_IMPORT_CMDLINE] = "M IMPORT_CMDLINE", 29199ca880aSopenharmony_ci [TK_M_IMPORT_PARENT] = "M IMPORT_PARENT", 29299ca880aSopenharmony_ci [TK_M_RESULT] = "M RESULT", 29399ca880aSopenharmony_ci [TK_M_MAX] = "M MAX", 29499ca880aSopenharmony_ci 29599ca880aSopenharmony_ci [TK_A_STRING_ESCAPE_NONE] = "A STRING_ESCAPE_NONE", 29699ca880aSopenharmony_ci [TK_A_STRING_ESCAPE_REPLACE] = "A STRING_ESCAPE_REPLACE", 29799ca880aSopenharmony_ci [TK_A_DB_PERSIST] = "A DB_PERSIST", 29899ca880aSopenharmony_ci [TK_A_INOTIFY_WATCH] = "A INOTIFY_WATCH", 29999ca880aSopenharmony_ci [TK_A_DEVLINK_PRIO] = "A DEVLINK_PRIO", 30099ca880aSopenharmony_ci [TK_A_OWNER] = "A OWNER", 30199ca880aSopenharmony_ci [TK_A_GROUP] = "A GROUP", 30299ca880aSopenharmony_ci [TK_A_MODE] = "A MODE", 30399ca880aSopenharmony_ci [TK_A_OWNER_ID] = "A OWNER_ID", 30499ca880aSopenharmony_ci [TK_A_GROUP_ID] = "A GROUP_ID", 30599ca880aSopenharmony_ci [TK_A_STATIC_NODE] = "A STATIC_NODE", 30699ca880aSopenharmony_ci [TK_A_SECLABEL] = "A SECLABEL", 30799ca880aSopenharmony_ci [TK_A_MODE_ID] = "A MODE_ID", 30899ca880aSopenharmony_ci [TK_A_ENV] = "A ENV", 30999ca880aSopenharmony_ci [TK_A_TAG] = "A ENV", 31099ca880aSopenharmony_ci [TK_A_NAME] = "A NAME", 31199ca880aSopenharmony_ci [TK_A_DEVLINK] = "A DEVLINK", 31299ca880aSopenharmony_ci [TK_A_ATTR] = "A ATTR", 31399ca880aSopenharmony_ci [TK_A_SYSCTL] = "A SYSCTL", 31499ca880aSopenharmony_ci [TK_A_RUN_BUILTIN] = "A RUN_BUILTIN", 31599ca880aSopenharmony_ci [TK_A_RUN_PROGRAM] = "A RUN_PROGRAM", 31699ca880aSopenharmony_ci [TK_A_GOTO] = "A GOTO", 31799ca880aSopenharmony_ci 31899ca880aSopenharmony_ci [TK_END] = "END", 31999ca880aSopenharmony_ci }; 32099ca880aSopenharmony_ci 32199ca880aSopenharmony_ci return token_strs[type]; 32299ca880aSopenharmony_ci} 32399ca880aSopenharmony_ci 32499ca880aSopenharmony_cistatic void dump_token(struct udev_rules *rules, struct token *token) { 32599ca880aSopenharmony_ci enum token_type type = token->type; 32699ca880aSopenharmony_ci enum operation_type op = token->key.op; 32799ca880aSopenharmony_ci enum string_glob_type glob = token->key.glob; 32899ca880aSopenharmony_ci const char *value = str(rules, token->key.value_off); 32999ca880aSopenharmony_ci const char *attr = &rules->buf[token->key.attr_off]; 33099ca880aSopenharmony_ci 33199ca880aSopenharmony_ci switch (type) { 33299ca880aSopenharmony_ci case TK_RULE: 33399ca880aSopenharmony_ci { 33499ca880aSopenharmony_ci const char *tks_ptr = (char *)rules->tokens; 33599ca880aSopenharmony_ci const char *tk_ptr = (char *)token; 33699ca880aSopenharmony_ci unsigned int idx = (tk_ptr - tks_ptr) / sizeof(struct token); 33799ca880aSopenharmony_ci 33899ca880aSopenharmony_ci log_debug("* RULE %s:%u, token: %u, count: %u, label: '%s'", 33999ca880aSopenharmony_ci &rules->buf[token->rule.filename_off], token->rule.filename_line, 34099ca880aSopenharmony_ci idx, token->rule.token_count, 34199ca880aSopenharmony_ci &rules->buf[token->rule.label_off]); 34299ca880aSopenharmony_ci break; 34399ca880aSopenharmony_ci } 34499ca880aSopenharmony_ci case TK_M_ACTION: 34599ca880aSopenharmony_ci case TK_M_DEVPATH: 34699ca880aSopenharmony_ci case TK_M_KERNEL: 34799ca880aSopenharmony_ci case TK_M_SUBSYSTEM: 34899ca880aSopenharmony_ci case TK_M_DRIVER: 34999ca880aSopenharmony_ci case TK_M_WAITFOR: 35099ca880aSopenharmony_ci case TK_M_DEVLINK: 35199ca880aSopenharmony_ci case TK_M_NAME: 35299ca880aSopenharmony_ci case TK_M_KERNELS: 35399ca880aSopenharmony_ci case TK_M_SUBSYSTEMS: 35499ca880aSopenharmony_ci case TK_M_DRIVERS: 35599ca880aSopenharmony_ci case TK_M_TAGS: 35699ca880aSopenharmony_ci case TK_M_PROGRAM: 35799ca880aSopenharmony_ci case TK_M_IMPORT_FILE: 35899ca880aSopenharmony_ci case TK_M_IMPORT_PROG: 35999ca880aSopenharmony_ci case TK_M_IMPORT_DB: 36099ca880aSopenharmony_ci case TK_M_IMPORT_CMDLINE: 36199ca880aSopenharmony_ci case TK_M_IMPORT_PARENT: 36299ca880aSopenharmony_ci case TK_M_RESULT: 36399ca880aSopenharmony_ci case TK_A_NAME: 36499ca880aSopenharmony_ci case TK_A_DEVLINK: 36599ca880aSopenharmony_ci case TK_A_OWNER: 36699ca880aSopenharmony_ci case TK_A_GROUP: 36799ca880aSopenharmony_ci case TK_A_MODE: 36899ca880aSopenharmony_ci case TK_A_RUN_BUILTIN: 36999ca880aSopenharmony_ci case TK_A_RUN_PROGRAM: 37099ca880aSopenharmony_ci log_debug("%s %s '%s'(%s)", 37199ca880aSopenharmony_ci token_str(type), operation_str(op), value, string_glob_str(glob)); 37299ca880aSopenharmony_ci break; 37399ca880aSopenharmony_ci case TK_M_IMPORT_BUILTIN: 37499ca880aSopenharmony_ci log_debug("%s %i '%s'", token_str(type), token->key.builtin_cmd, value); 37599ca880aSopenharmony_ci break; 37699ca880aSopenharmony_ci case TK_M_ATTR: 37799ca880aSopenharmony_ci case TK_M_SYSCTL: 37899ca880aSopenharmony_ci case TK_M_ATTRS: 37999ca880aSopenharmony_ci case TK_M_ENV: 38099ca880aSopenharmony_ci case TK_A_ATTR: 38199ca880aSopenharmony_ci case TK_A_SYSCTL: 38299ca880aSopenharmony_ci case TK_A_ENV: 38399ca880aSopenharmony_ci log_debug("%s %s '%s' '%s'(%s)", 38499ca880aSopenharmony_ci token_str(type), operation_str(op), attr, value, string_glob_str(glob)); 38599ca880aSopenharmony_ci break; 38699ca880aSopenharmony_ci case TK_M_TAG: 38799ca880aSopenharmony_ci case TK_A_TAG: 38899ca880aSopenharmony_ci log_debug("%s %s '%s'", token_str(type), operation_str(op), value); 38999ca880aSopenharmony_ci break; 39099ca880aSopenharmony_ci case TK_A_STRING_ESCAPE_NONE: 39199ca880aSopenharmony_ci case TK_A_STRING_ESCAPE_REPLACE: 39299ca880aSopenharmony_ci case TK_A_DB_PERSIST: 39399ca880aSopenharmony_ci log_debug("%s", token_str(type)); 39499ca880aSopenharmony_ci break; 39599ca880aSopenharmony_ci case TK_M_TEST: 39699ca880aSopenharmony_ci log_debug("%s %s '%s'(%s) %#o", 39799ca880aSopenharmony_ci token_str(type), operation_str(op), value, string_glob_str(glob), token->key.mode); 39899ca880aSopenharmony_ci break; 39999ca880aSopenharmony_ci case TK_A_INOTIFY_WATCH: 40099ca880aSopenharmony_ci log_debug("%s %u", token_str(type), token->key.watch); 40199ca880aSopenharmony_ci break; 40299ca880aSopenharmony_ci case TK_A_DEVLINK_PRIO: 40399ca880aSopenharmony_ci log_debug("%s %u", token_str(type), token->key.devlink_prio); 40499ca880aSopenharmony_ci break; 40599ca880aSopenharmony_ci case TK_A_OWNER_ID: 40699ca880aSopenharmony_ci log_debug("%s %s %u", token_str(type), operation_str(op), token->key.uid); 40799ca880aSopenharmony_ci break; 40899ca880aSopenharmony_ci case TK_A_GROUP_ID: 40999ca880aSopenharmony_ci log_debug("%s %s %u", token_str(type), operation_str(op), token->key.gid); 41099ca880aSopenharmony_ci break; 41199ca880aSopenharmony_ci case TK_A_MODE_ID: 41299ca880aSopenharmony_ci log_debug("%s %s %#o", token_str(type), operation_str(op), token->key.mode); 41399ca880aSopenharmony_ci break; 41499ca880aSopenharmony_ci case TK_A_STATIC_NODE: 41599ca880aSopenharmony_ci log_debug("%s '%s'", token_str(type), value); 41699ca880aSopenharmony_ci break; 41799ca880aSopenharmony_ci case TK_A_SECLABEL: 41899ca880aSopenharmony_ci log_debug("%s %s '%s' '%s'", token_str(type), operation_str(op), attr, value); 41999ca880aSopenharmony_ci break; 42099ca880aSopenharmony_ci case TK_A_GOTO: 42199ca880aSopenharmony_ci log_debug("%s '%s' %u", token_str(type), value, token->key.rule_goto); 42299ca880aSopenharmony_ci break; 42399ca880aSopenharmony_ci case TK_END: 42499ca880aSopenharmony_ci log_debug("* %s", token_str(type)); 42599ca880aSopenharmony_ci break; 42699ca880aSopenharmony_ci case TK_M_PARENTS_MIN: 42799ca880aSopenharmony_ci case TK_M_PARENTS_MAX: 42899ca880aSopenharmony_ci case TK_M_MAX: 42999ca880aSopenharmony_ci case TK_UNSET: 43099ca880aSopenharmony_ci log_debug("unknown type %u", type); 43199ca880aSopenharmony_ci break; 43299ca880aSopenharmony_ci } 43399ca880aSopenharmony_ci} 43499ca880aSopenharmony_ci 43599ca880aSopenharmony_cistatic void dump_rules(struct udev_rules *rules) { 43699ca880aSopenharmony_ci unsigned int i; 43799ca880aSopenharmony_ci 43899ca880aSopenharmony_ci log_debug("dumping %u (%zu bytes) tokens, %u (%zu bytes) strings", 43999ca880aSopenharmony_ci rules->token_cur, 44099ca880aSopenharmony_ci rules->token_cur * sizeof(struct token), 44199ca880aSopenharmony_ci rules->buf_count, 44299ca880aSopenharmony_ci rules->buf_cur); 44399ca880aSopenharmony_ci for (i = 0; i < rules->token_cur; i++) 44499ca880aSopenharmony_ci dump_token(rules, &rules->tokens[i]); 44599ca880aSopenharmony_ci} 44699ca880aSopenharmony_ci#else 44799ca880aSopenharmony_cistatic inline void dump_token(struct udev_rules *rules, struct token *token) {} 44899ca880aSopenharmony_cistatic inline void dump_rules(struct udev_rules *rules) {} 44999ca880aSopenharmony_ci#endif /* DEBUG */ 45099ca880aSopenharmony_ci 45199ca880aSopenharmony_cistatic int add_token(struct udev_rules *rules, struct token *token) { 45299ca880aSopenharmony_ci /* grow buffer if needed */ 45399ca880aSopenharmony_ci if (rules->token_cur+1 >= rules->token_max) { 45499ca880aSopenharmony_ci struct token *tokens; 45599ca880aSopenharmony_ci unsigned int add; 45699ca880aSopenharmony_ci 45799ca880aSopenharmony_ci /* double the buffer size */ 45899ca880aSopenharmony_ci add = rules->token_max; 45999ca880aSopenharmony_ci if (add < 8) 46099ca880aSopenharmony_ci add = 8; 46199ca880aSopenharmony_ci 46299ca880aSopenharmony_ci tokens = realloc(rules->tokens, (rules->token_max + add ) * sizeof(struct token)); 46399ca880aSopenharmony_ci if (tokens == NULL) 46499ca880aSopenharmony_ci return -1; 46599ca880aSopenharmony_ci rules->tokens = tokens; 46699ca880aSopenharmony_ci rules->token_max += add; 46799ca880aSopenharmony_ci } 46899ca880aSopenharmony_ci memcpy(&rules->tokens[rules->token_cur], token, sizeof(struct token)); 46999ca880aSopenharmony_ci rules->token_cur++; 47099ca880aSopenharmony_ci return 0; 47199ca880aSopenharmony_ci} 47299ca880aSopenharmony_ci 47399ca880aSopenharmony_cistatic uid_t add_uid(struct udev_rules *rules, const char *owner) { 47499ca880aSopenharmony_ci unsigned int i; 47599ca880aSopenharmony_ci uid_t uid = 0; 47699ca880aSopenharmony_ci unsigned int off; 47799ca880aSopenharmony_ci int r; 47899ca880aSopenharmony_ci 47999ca880aSopenharmony_ci /* lookup, if we know it already */ 48099ca880aSopenharmony_ci for (i = 0; i < rules->uids_cur; i++) { 48199ca880aSopenharmony_ci off = rules->uids[i].name_off; 48299ca880aSopenharmony_ci if (streq(rules_str(rules, off), owner)) { 48399ca880aSopenharmony_ci uid = rules->uids[i].uid; 48499ca880aSopenharmony_ci return uid; 48599ca880aSopenharmony_ci } 48699ca880aSopenharmony_ci } 48799ca880aSopenharmony_ci r = get_user_creds(&owner, &uid, NULL, NULL, NULL); 48899ca880aSopenharmony_ci if (r < 0) { 48999ca880aSopenharmony_ci if (r == -ENOENT || r == -ESRCH) 49099ca880aSopenharmony_ci log_error("specified user '%s' unknown", owner); 49199ca880aSopenharmony_ci else 49299ca880aSopenharmony_ci log_error_errno(r, "error resolving user '%s': %m", owner); 49399ca880aSopenharmony_ci } 49499ca880aSopenharmony_ci 49599ca880aSopenharmony_ci /* grow buffer if needed */ 49699ca880aSopenharmony_ci if (rules->uids_cur+1 >= rules->uids_max) { 49799ca880aSopenharmony_ci struct uid_gid *uids; 49899ca880aSopenharmony_ci unsigned int add; 49999ca880aSopenharmony_ci 50099ca880aSopenharmony_ci /* double the buffer size */ 50199ca880aSopenharmony_ci add = rules->uids_max; 50299ca880aSopenharmony_ci if (add < 1) 50399ca880aSopenharmony_ci add = 8; 50499ca880aSopenharmony_ci 50599ca880aSopenharmony_ci uids = realloc(rules->uids, (rules->uids_max + add ) * sizeof(struct uid_gid)); 50699ca880aSopenharmony_ci if (uids == NULL) 50799ca880aSopenharmony_ci return uid; 50899ca880aSopenharmony_ci rules->uids = uids; 50999ca880aSopenharmony_ci rules->uids_max += add; 51099ca880aSopenharmony_ci } 51199ca880aSopenharmony_ci rules->uids[rules->uids_cur].uid = uid; 51299ca880aSopenharmony_ci off = rules_add_string(rules, owner); 51399ca880aSopenharmony_ci if (off <= 0) 51499ca880aSopenharmony_ci return uid; 51599ca880aSopenharmony_ci rules->uids[rules->uids_cur].name_off = off; 51699ca880aSopenharmony_ci rules->uids_cur++; 51799ca880aSopenharmony_ci return uid; 51899ca880aSopenharmony_ci} 51999ca880aSopenharmony_ci 52099ca880aSopenharmony_cistatic gid_t add_gid(struct udev_rules *rules, const char *group) { 52199ca880aSopenharmony_ci unsigned int i; 52299ca880aSopenharmony_ci gid_t gid = 0; 52399ca880aSopenharmony_ci unsigned int off; 52499ca880aSopenharmony_ci int r; 52599ca880aSopenharmony_ci 52699ca880aSopenharmony_ci /* lookup, if we know it already */ 52799ca880aSopenharmony_ci for (i = 0; i < rules->gids_cur; i++) { 52899ca880aSopenharmony_ci off = rules->gids[i].name_off; 52999ca880aSopenharmony_ci if (streq(rules_str(rules, off), group)) { 53099ca880aSopenharmony_ci gid = rules->gids[i].gid; 53199ca880aSopenharmony_ci return gid; 53299ca880aSopenharmony_ci } 53399ca880aSopenharmony_ci } 53499ca880aSopenharmony_ci r = get_group_creds(&group, &gid); 53599ca880aSopenharmony_ci if (r < 0) { 53699ca880aSopenharmony_ci if (r == -ENOENT || r == -ESRCH) 53799ca880aSopenharmony_ci log_error("specified group '%s' unknown", group); 53899ca880aSopenharmony_ci else 53999ca880aSopenharmony_ci log_error_errno(r, "error resolving group '%s': %m", group); 54099ca880aSopenharmony_ci } 54199ca880aSopenharmony_ci 54299ca880aSopenharmony_ci /* grow buffer if needed */ 54399ca880aSopenharmony_ci if (rules->gids_cur+1 >= rules->gids_max) { 54499ca880aSopenharmony_ci struct uid_gid *gids; 54599ca880aSopenharmony_ci unsigned int add; 54699ca880aSopenharmony_ci 54799ca880aSopenharmony_ci /* double the buffer size */ 54899ca880aSopenharmony_ci add = rules->gids_max; 54999ca880aSopenharmony_ci if (add < 1) 55099ca880aSopenharmony_ci add = 8; 55199ca880aSopenharmony_ci 55299ca880aSopenharmony_ci gids = realloc(rules->gids, (rules->gids_max + add ) * sizeof(struct uid_gid)); 55399ca880aSopenharmony_ci if (gids == NULL) 55499ca880aSopenharmony_ci return gid; 55599ca880aSopenharmony_ci rules->gids = gids; 55699ca880aSopenharmony_ci rules->gids_max += add; 55799ca880aSopenharmony_ci } 55899ca880aSopenharmony_ci rules->gids[rules->gids_cur].gid = gid; 55999ca880aSopenharmony_ci off = rules_add_string(rules, group); 56099ca880aSopenharmony_ci if (off <= 0) 56199ca880aSopenharmony_ci return gid; 56299ca880aSopenharmony_ci rules->gids[rules->gids_cur].name_off = off; 56399ca880aSopenharmony_ci rules->gids_cur++; 56499ca880aSopenharmony_ci return gid; 56599ca880aSopenharmony_ci} 56699ca880aSopenharmony_ci 56799ca880aSopenharmony_cistatic int import_property_from_string(struct udev_device *dev, char *line) { 56899ca880aSopenharmony_ci char *key; 56999ca880aSopenharmony_ci char *val; 57099ca880aSopenharmony_ci size_t len; 57199ca880aSopenharmony_ci 57299ca880aSopenharmony_ci /* find key */ 57399ca880aSopenharmony_ci key = line; 57499ca880aSopenharmony_ci while (isspace(key[0])) 57599ca880aSopenharmony_ci key++; 57699ca880aSopenharmony_ci 57799ca880aSopenharmony_ci /* comment or empty line */ 57899ca880aSopenharmony_ci if (key[0] == '#' || key[0] == '\0') 57999ca880aSopenharmony_ci return -1; 58099ca880aSopenharmony_ci 58199ca880aSopenharmony_ci /* split key/value */ 58299ca880aSopenharmony_ci val = strchr(key, '='); 58399ca880aSopenharmony_ci if (val == NULL) 58499ca880aSopenharmony_ci return -1; 58599ca880aSopenharmony_ci val[0] = '\0'; 58699ca880aSopenharmony_ci val++; 58799ca880aSopenharmony_ci 58899ca880aSopenharmony_ci /* find value */ 58999ca880aSopenharmony_ci while (isspace(val[0])) 59099ca880aSopenharmony_ci val++; 59199ca880aSopenharmony_ci 59299ca880aSopenharmony_ci /* terminate key */ 59399ca880aSopenharmony_ci len = strlen(key); 59499ca880aSopenharmony_ci if (len == 0) 59599ca880aSopenharmony_ci return -1; 59699ca880aSopenharmony_ci while (isspace(key[len-1])) 59799ca880aSopenharmony_ci len--; 59899ca880aSopenharmony_ci key[len] = '\0'; 59999ca880aSopenharmony_ci 60099ca880aSopenharmony_ci /* terminate value */ 60199ca880aSopenharmony_ci len = strlen(val); 60299ca880aSopenharmony_ci if (len == 0) 60399ca880aSopenharmony_ci return -1; 60499ca880aSopenharmony_ci while (isspace(val[len-1])) 60599ca880aSopenharmony_ci len--; 60699ca880aSopenharmony_ci val[len] = '\0'; 60799ca880aSopenharmony_ci 60899ca880aSopenharmony_ci if (len == 0) 60999ca880aSopenharmony_ci return -1; 61099ca880aSopenharmony_ci 61199ca880aSopenharmony_ci /* unquote */ 61299ca880aSopenharmony_ci if (val[0] == '"' || val[0] == '\'') { 61399ca880aSopenharmony_ci if (val[len-1] != val[0]) { 61499ca880aSopenharmony_ci log_debug("inconsistent quoting: '%s', skip", line); 61599ca880aSopenharmony_ci return -1; 61699ca880aSopenharmony_ci } 61799ca880aSopenharmony_ci val[len-1] = '\0'; 61899ca880aSopenharmony_ci val++; 61999ca880aSopenharmony_ci } 62099ca880aSopenharmony_ci 62199ca880aSopenharmony_ci udev_device_add_property(dev, key, val); 62299ca880aSopenharmony_ci 62399ca880aSopenharmony_ci return 0; 62499ca880aSopenharmony_ci} 62599ca880aSopenharmony_ci 62699ca880aSopenharmony_cistatic int import_file_into_properties(struct udev_device *dev, const char *filename) { 62799ca880aSopenharmony_ci FILE *f; 62899ca880aSopenharmony_ci char line[UTIL_LINE_SIZE]; 62999ca880aSopenharmony_ci 63099ca880aSopenharmony_ci f = fopen(filename, "re"); 63199ca880aSopenharmony_ci if (f == NULL) 63299ca880aSopenharmony_ci return -1; 63399ca880aSopenharmony_ci while (fgets(line, sizeof(line), f) != NULL) 63499ca880aSopenharmony_ci import_property_from_string(dev, line); 63599ca880aSopenharmony_ci fclose(f); 63699ca880aSopenharmony_ci return 0; 63799ca880aSopenharmony_ci} 63899ca880aSopenharmony_ci 63999ca880aSopenharmony_cistatic int import_program_into_properties(struct udev_event *event, 64099ca880aSopenharmony_ci usec_t timeout_usec, 64199ca880aSopenharmony_ci usec_t timeout_warn_usec, 64299ca880aSopenharmony_ci const char *program, const sigset_t *sigmask) { 64399ca880aSopenharmony_ci struct udev_device *dev = event->dev; 64499ca880aSopenharmony_ci char **envp; 64599ca880aSopenharmony_ci char result[UTIL_LINE_SIZE]; 64699ca880aSopenharmony_ci char *line; 64799ca880aSopenharmony_ci int err; 64899ca880aSopenharmony_ci 64999ca880aSopenharmony_ci envp = udev_device_get_properties_envp(dev); 65099ca880aSopenharmony_ci err = udev_event_spawn(event, timeout_usec, timeout_warn_usec, program, envp, sigmask, result, sizeof(result)); 65199ca880aSopenharmony_ci if (err < 0) 65299ca880aSopenharmony_ci return err; 65399ca880aSopenharmony_ci 65499ca880aSopenharmony_ci line = result; 65599ca880aSopenharmony_ci while (line != NULL) { 65699ca880aSopenharmony_ci char *pos; 65799ca880aSopenharmony_ci 65899ca880aSopenharmony_ci pos = strchr(line, '\n'); 65999ca880aSopenharmony_ci if (pos != NULL) { 66099ca880aSopenharmony_ci pos[0] = '\0'; 66199ca880aSopenharmony_ci pos = &pos[1]; 66299ca880aSopenharmony_ci } 66399ca880aSopenharmony_ci import_property_from_string(dev, line); 66499ca880aSopenharmony_ci line = pos; 66599ca880aSopenharmony_ci } 66699ca880aSopenharmony_ci return 0; 66799ca880aSopenharmony_ci} 66899ca880aSopenharmony_ci 66999ca880aSopenharmony_cistatic int import_parent_into_properties(struct udev_device *dev, const char *filter) { 67099ca880aSopenharmony_ci struct udev_device *dev_parent; 67199ca880aSopenharmony_ci struct udev_list_entry *list_entry; 67299ca880aSopenharmony_ci 67399ca880aSopenharmony_ci dev_parent = udev_device_get_parent(dev); 67499ca880aSopenharmony_ci if (dev_parent == NULL) 67599ca880aSopenharmony_ci return -1; 67699ca880aSopenharmony_ci 67799ca880aSopenharmony_ci udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(dev_parent)) { 67899ca880aSopenharmony_ci const char *key = udev_list_entry_get_name(list_entry); 67999ca880aSopenharmony_ci const char *val = udev_list_entry_get_value(list_entry); 68099ca880aSopenharmony_ci 68199ca880aSopenharmony_ci if (fnmatch(filter, key, 0) == 0) { 68299ca880aSopenharmony_ci udev_device_add_property(dev, key, val); 68399ca880aSopenharmony_ci } 68499ca880aSopenharmony_ci } 68599ca880aSopenharmony_ci return 0; 68699ca880aSopenharmony_ci} 68799ca880aSopenharmony_ci 68899ca880aSopenharmony_ci#define WAIT_LOOP_PER_SECOND 50 68999ca880aSopenharmony_cistatic int wait_for_file(struct udev_device *dev, const char *file, int timeout) { 69099ca880aSopenharmony_ci char filepath[UTIL_PATH_SIZE]; 69199ca880aSopenharmony_ci char devicepath[UTIL_PATH_SIZE]; 69299ca880aSopenharmony_ci struct stat stats; 69399ca880aSopenharmony_ci int loop = timeout * WAIT_LOOP_PER_SECOND; 69499ca880aSopenharmony_ci 69599ca880aSopenharmony_ci /* a relative path is a device attribute */ 69699ca880aSopenharmony_ci devicepath[0] = '\0'; 69799ca880aSopenharmony_ci if (file[0] != '/') { 69899ca880aSopenharmony_ci strscpyl(devicepath, sizeof(devicepath), udev_device_get_syspath(dev), NULL); 69999ca880aSopenharmony_ci strscpyl(filepath, sizeof(filepath), devicepath, "/", file, NULL); 70099ca880aSopenharmony_ci file = filepath; 70199ca880aSopenharmony_ci } 70299ca880aSopenharmony_ci 70399ca880aSopenharmony_ci while (--loop) { 70499ca880aSopenharmony_ci const struct timespec duration = { 0, 1000 * 1000 * 1000 / WAIT_LOOP_PER_SECOND }; 70599ca880aSopenharmony_ci 70699ca880aSopenharmony_ci /* lookup file */ 70799ca880aSopenharmony_ci if (stat(file, &stats) == 0) { 70899ca880aSopenharmony_ci log_debug("file '%s' appeared after %i loops", file, (timeout * WAIT_LOOP_PER_SECOND) - loop-1); 70999ca880aSopenharmony_ci return 0; 71099ca880aSopenharmony_ci } 71199ca880aSopenharmony_ci /* make sure, the device did not disappear in the meantime */ 71299ca880aSopenharmony_ci if (devicepath[0] != '\0' && stat(devicepath, &stats) != 0) { 71399ca880aSopenharmony_ci log_debug("device disappeared while waiting for '%s'", file); 71499ca880aSopenharmony_ci return -2; 71599ca880aSopenharmony_ci } 71699ca880aSopenharmony_ci log_debug("wait for '%s' for %i mseconds", file, 1000 / WAIT_LOOP_PER_SECOND); 71799ca880aSopenharmony_ci nanosleep(&duration, NULL); 71899ca880aSopenharmony_ci } 71999ca880aSopenharmony_ci log_debug("waiting for '%s' failed", file); 72099ca880aSopenharmony_ci return -1; 72199ca880aSopenharmony_ci} 72299ca880aSopenharmony_ci 72399ca880aSopenharmony_cistatic int attr_subst_subdir(char *attr, size_t len) { 72499ca880aSopenharmony_ci bool found = false; 72599ca880aSopenharmony_ci 72699ca880aSopenharmony_ci if (strstr(attr, "/*/")) { 72799ca880aSopenharmony_ci char *pos; 72899ca880aSopenharmony_ci char dirname[UTIL_PATH_SIZE]; 72999ca880aSopenharmony_ci const char *tail; 73099ca880aSopenharmony_ci DIR *dir; 73199ca880aSopenharmony_ci 73299ca880aSopenharmony_ci strscpy(dirname, sizeof(dirname), attr); 73399ca880aSopenharmony_ci pos = strstr(dirname, "/*/"); 73499ca880aSopenharmony_ci if (pos == NULL) 73599ca880aSopenharmony_ci return -1; 73699ca880aSopenharmony_ci pos[0] = '\0'; 73799ca880aSopenharmony_ci tail = &pos[2]; 73899ca880aSopenharmony_ci dir = opendir(dirname); 73999ca880aSopenharmony_ci if (dir != NULL) { 74099ca880aSopenharmony_ci struct dirent *dent; 74199ca880aSopenharmony_ci 74299ca880aSopenharmony_ci for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { 74399ca880aSopenharmony_ci struct stat stats; 74499ca880aSopenharmony_ci 74599ca880aSopenharmony_ci if (dent->d_name[0] == '.') 74699ca880aSopenharmony_ci continue; 74799ca880aSopenharmony_ci strscpyl(attr, len, dirname, "/", dent->d_name, tail, NULL); 74899ca880aSopenharmony_ci if (stat(attr, &stats) == 0) { 74999ca880aSopenharmony_ci found = true; 75099ca880aSopenharmony_ci break; 75199ca880aSopenharmony_ci } 75299ca880aSopenharmony_ci } 75399ca880aSopenharmony_ci closedir(dir); 75499ca880aSopenharmony_ci } 75599ca880aSopenharmony_ci } 75699ca880aSopenharmony_ci 75799ca880aSopenharmony_ci return found; 75899ca880aSopenharmony_ci} 75999ca880aSopenharmony_ci 76099ca880aSopenharmony_cistatic int get_key(struct udev *udev, char **line, char **key, enum operation_type *op, char **value) { 76199ca880aSopenharmony_ci char *linepos; 76299ca880aSopenharmony_ci char *temp; 76399ca880aSopenharmony_ci 76499ca880aSopenharmony_ci linepos = *line; 76599ca880aSopenharmony_ci if (linepos == NULL || linepos[0] == '\0') 76699ca880aSopenharmony_ci return -1; 76799ca880aSopenharmony_ci 76899ca880aSopenharmony_ci /* skip whitespace */ 76999ca880aSopenharmony_ci while (isspace(linepos[0]) || linepos[0] == ',') 77099ca880aSopenharmony_ci linepos++; 77199ca880aSopenharmony_ci 77299ca880aSopenharmony_ci /* get the key */ 77399ca880aSopenharmony_ci if (linepos[0] == '\0') 77499ca880aSopenharmony_ci return -1; 77599ca880aSopenharmony_ci *key = linepos; 77699ca880aSopenharmony_ci 77799ca880aSopenharmony_ci for (;;) { 77899ca880aSopenharmony_ci linepos++; 77999ca880aSopenharmony_ci if (linepos[0] == '\0') 78099ca880aSopenharmony_ci return -1; 78199ca880aSopenharmony_ci if (isspace(linepos[0])) 78299ca880aSopenharmony_ci break; 78399ca880aSopenharmony_ci if (linepos[0] == '=') 78499ca880aSopenharmony_ci break; 78599ca880aSopenharmony_ci if ((linepos[0] == '+') || (linepos[0] == '-') || (linepos[0] == '!') || (linepos[0] == ':')) 78699ca880aSopenharmony_ci if (linepos[1] == '=') 78799ca880aSopenharmony_ci break; 78899ca880aSopenharmony_ci } 78999ca880aSopenharmony_ci 79099ca880aSopenharmony_ci /* remember end of key */ 79199ca880aSopenharmony_ci temp = linepos; 79299ca880aSopenharmony_ci 79399ca880aSopenharmony_ci /* skip whitespace after key */ 79499ca880aSopenharmony_ci while (isspace(linepos[0])) 79599ca880aSopenharmony_ci linepos++; 79699ca880aSopenharmony_ci if (linepos[0] == '\0') 79799ca880aSopenharmony_ci return -1; 79899ca880aSopenharmony_ci 79999ca880aSopenharmony_ci /* get operation type */ 80099ca880aSopenharmony_ci if (linepos[0] == '=' && linepos[1] == '=') { 80199ca880aSopenharmony_ci *op = OP_MATCH; 80299ca880aSopenharmony_ci linepos += 2; 80399ca880aSopenharmony_ci } else if (linepos[0] == '!' && linepos[1] == '=') { 80499ca880aSopenharmony_ci *op = OP_NOMATCH; 80599ca880aSopenharmony_ci linepos += 2; 80699ca880aSopenharmony_ci } else if (linepos[0] == '+' && linepos[1] == '=') { 80799ca880aSopenharmony_ci *op = OP_ADD; 80899ca880aSopenharmony_ci linepos += 2; 80999ca880aSopenharmony_ci } else if (linepos[0] == '-' && linepos[1] == '=') { 81099ca880aSopenharmony_ci *op = OP_REMOVE; 81199ca880aSopenharmony_ci linepos += 2; 81299ca880aSopenharmony_ci } else if (linepos[0] == '=') { 81399ca880aSopenharmony_ci *op = OP_ASSIGN; 81499ca880aSopenharmony_ci linepos++; 81599ca880aSopenharmony_ci } else if (linepos[0] == ':' && linepos[1] == '=') { 81699ca880aSopenharmony_ci *op = OP_ASSIGN_FINAL; 81799ca880aSopenharmony_ci linepos += 2; 81899ca880aSopenharmony_ci } else 81999ca880aSopenharmony_ci return -1; 82099ca880aSopenharmony_ci 82199ca880aSopenharmony_ci /* terminate key */ 82299ca880aSopenharmony_ci temp[0] = '\0'; 82399ca880aSopenharmony_ci 82499ca880aSopenharmony_ci /* skip whitespace after operator */ 82599ca880aSopenharmony_ci while (isspace(linepos[0])) 82699ca880aSopenharmony_ci linepos++; 82799ca880aSopenharmony_ci if (linepos[0] == '\0') 82899ca880aSopenharmony_ci return -1; 82999ca880aSopenharmony_ci 83099ca880aSopenharmony_ci /* get the value */ 83199ca880aSopenharmony_ci if (linepos[0] == '"') 83299ca880aSopenharmony_ci linepos++; 83399ca880aSopenharmony_ci else 83499ca880aSopenharmony_ci return -1; 83599ca880aSopenharmony_ci *value = linepos; 83699ca880aSopenharmony_ci 83799ca880aSopenharmony_ci /* terminate */ 83899ca880aSopenharmony_ci temp = strchr(linepos, '"'); 83999ca880aSopenharmony_ci if (!temp) 84099ca880aSopenharmony_ci return -1; 84199ca880aSopenharmony_ci temp[0] = '\0'; 84299ca880aSopenharmony_ci temp++; 84399ca880aSopenharmony_ci 84499ca880aSopenharmony_ci /* move line to next key */ 84599ca880aSopenharmony_ci *line = temp; 84699ca880aSopenharmony_ci return 0; 84799ca880aSopenharmony_ci} 84899ca880aSopenharmony_ci 84999ca880aSopenharmony_ci/* extract possible KEY{attr} */ 85099ca880aSopenharmony_cistatic const char *get_key_attribute(struct udev *udev, char *str) { 85199ca880aSopenharmony_ci char *pos; 85299ca880aSopenharmony_ci char *attr; 85399ca880aSopenharmony_ci 85499ca880aSopenharmony_ci attr = strchr(str, '{'); 85599ca880aSopenharmony_ci if (attr != NULL) { 85699ca880aSopenharmony_ci attr++; 85799ca880aSopenharmony_ci pos = strchr(attr, '}'); 85899ca880aSopenharmony_ci if (pos == NULL) { 85999ca880aSopenharmony_ci log_error("missing closing brace for format"); 86099ca880aSopenharmony_ci return NULL; 86199ca880aSopenharmony_ci } 86299ca880aSopenharmony_ci pos[0] = '\0'; 86399ca880aSopenharmony_ci return attr; 86499ca880aSopenharmony_ci } 86599ca880aSopenharmony_ci return NULL; 86699ca880aSopenharmony_ci} 86799ca880aSopenharmony_ci 86899ca880aSopenharmony_cistatic int rule_add_key(struct rule_tmp *rule_tmp, enum token_type type, 86999ca880aSopenharmony_ci enum operation_type op, 87099ca880aSopenharmony_ci const char *value, const void *data) { 87199ca880aSopenharmony_ci struct token *token = &rule_tmp->token[rule_tmp->token_cur]; 87299ca880aSopenharmony_ci const char *attr = NULL; 87399ca880aSopenharmony_ci 87499ca880aSopenharmony_ci memzero(token, sizeof(struct token)); 87599ca880aSopenharmony_ci 87699ca880aSopenharmony_ci switch (type) { 87799ca880aSopenharmony_ci case TK_M_ACTION: 87899ca880aSopenharmony_ci case TK_M_DEVPATH: 87999ca880aSopenharmony_ci case TK_M_KERNEL: 88099ca880aSopenharmony_ci case TK_M_SUBSYSTEM: 88199ca880aSopenharmony_ci case TK_M_DRIVER: 88299ca880aSopenharmony_ci case TK_M_WAITFOR: 88399ca880aSopenharmony_ci case TK_M_DEVLINK: 88499ca880aSopenharmony_ci case TK_M_NAME: 88599ca880aSopenharmony_ci case TK_M_KERNELS: 88699ca880aSopenharmony_ci case TK_M_SUBSYSTEMS: 88799ca880aSopenharmony_ci case TK_M_DRIVERS: 88899ca880aSopenharmony_ci case TK_M_TAGS: 88999ca880aSopenharmony_ci case TK_M_PROGRAM: 89099ca880aSopenharmony_ci case TK_M_IMPORT_FILE: 89199ca880aSopenharmony_ci case TK_M_IMPORT_PROG: 89299ca880aSopenharmony_ci case TK_M_IMPORT_DB: 89399ca880aSopenharmony_ci case TK_M_IMPORT_CMDLINE: 89499ca880aSopenharmony_ci case TK_M_IMPORT_PARENT: 89599ca880aSopenharmony_ci case TK_M_RESULT: 89699ca880aSopenharmony_ci case TK_A_OWNER: 89799ca880aSopenharmony_ci case TK_A_GROUP: 89899ca880aSopenharmony_ci case TK_A_MODE: 89999ca880aSopenharmony_ci case TK_A_DEVLINK: 90099ca880aSopenharmony_ci case TK_A_NAME: 90199ca880aSopenharmony_ci case TK_A_GOTO: 90299ca880aSopenharmony_ci case TK_M_TAG: 90399ca880aSopenharmony_ci case TK_A_TAG: 90499ca880aSopenharmony_ci case TK_A_STATIC_NODE: 90599ca880aSopenharmony_ci token->key.value_off = rules_add_string(rule_tmp->rules, value); 90699ca880aSopenharmony_ci break; 90799ca880aSopenharmony_ci case TK_M_IMPORT_BUILTIN: 90899ca880aSopenharmony_ci token->key.value_off = rules_add_string(rule_tmp->rules, value); 90999ca880aSopenharmony_ci token->key.builtin_cmd = *(enum udev_builtin_cmd *)data; 91099ca880aSopenharmony_ci break; 91199ca880aSopenharmony_ci case TK_M_ENV: 91299ca880aSopenharmony_ci case TK_M_ATTR: 91399ca880aSopenharmony_ci case TK_M_SYSCTL: 91499ca880aSopenharmony_ci case TK_M_ATTRS: 91599ca880aSopenharmony_ci case TK_A_ATTR: 91699ca880aSopenharmony_ci case TK_A_SYSCTL: 91799ca880aSopenharmony_ci case TK_A_ENV: 91899ca880aSopenharmony_ci case TK_A_SECLABEL: 91999ca880aSopenharmony_ci attr = data; 92099ca880aSopenharmony_ci token->key.value_off = rules_add_string(rule_tmp->rules, value); 92199ca880aSopenharmony_ci token->key.attr_off = rules_add_string(rule_tmp->rules, attr); 92299ca880aSopenharmony_ci break; 92399ca880aSopenharmony_ci case TK_M_TEST: 92499ca880aSopenharmony_ci token->key.value_off = rules_add_string(rule_tmp->rules, value); 92599ca880aSopenharmony_ci if (data != NULL) 92699ca880aSopenharmony_ci token->key.mode = *(mode_t *)data; 92799ca880aSopenharmony_ci break; 92899ca880aSopenharmony_ci case TK_A_STRING_ESCAPE_NONE: 92999ca880aSopenharmony_ci case TK_A_STRING_ESCAPE_REPLACE: 93099ca880aSopenharmony_ci case TK_A_DB_PERSIST: 93199ca880aSopenharmony_ci break; 93299ca880aSopenharmony_ci case TK_A_RUN_BUILTIN: 93399ca880aSopenharmony_ci case TK_A_RUN_PROGRAM: 93499ca880aSopenharmony_ci token->key.builtin_cmd = *(enum udev_builtin_cmd *)data; 93599ca880aSopenharmony_ci token->key.value_off = rules_add_string(rule_tmp->rules, value); 93699ca880aSopenharmony_ci break; 93799ca880aSopenharmony_ci case TK_A_INOTIFY_WATCH: 93899ca880aSopenharmony_ci case TK_A_DEVLINK_PRIO: 93999ca880aSopenharmony_ci token->key.devlink_prio = *(int *)data; 94099ca880aSopenharmony_ci break; 94199ca880aSopenharmony_ci case TK_A_OWNER_ID: 94299ca880aSopenharmony_ci token->key.uid = *(uid_t *)data; 94399ca880aSopenharmony_ci break; 94499ca880aSopenharmony_ci case TK_A_GROUP_ID: 94599ca880aSopenharmony_ci token->key.gid = *(gid_t *)data; 94699ca880aSopenharmony_ci break; 94799ca880aSopenharmony_ci case TK_A_MODE_ID: 94899ca880aSopenharmony_ci token->key.mode = *(mode_t *)data; 94999ca880aSopenharmony_ci break; 95099ca880aSopenharmony_ci case TK_RULE: 95199ca880aSopenharmony_ci case TK_M_PARENTS_MIN: 95299ca880aSopenharmony_ci case TK_M_PARENTS_MAX: 95399ca880aSopenharmony_ci case TK_M_MAX: 95499ca880aSopenharmony_ci case TK_END: 95599ca880aSopenharmony_ci case TK_UNSET: 95699ca880aSopenharmony_ci log_error("wrong type %u", type); 95799ca880aSopenharmony_ci return -1; 95899ca880aSopenharmony_ci } 95999ca880aSopenharmony_ci 96099ca880aSopenharmony_ci if (value != NULL && type < TK_M_MAX) { 96199ca880aSopenharmony_ci /* check if we need to split or call fnmatch() while matching rules */ 96299ca880aSopenharmony_ci enum string_glob_type glob; 96399ca880aSopenharmony_ci int has_split; 96499ca880aSopenharmony_ci int has_glob; 96599ca880aSopenharmony_ci 96699ca880aSopenharmony_ci has_split = (strchr(value, '|') != NULL); 96799ca880aSopenharmony_ci has_glob = string_is_glob(value); 96899ca880aSopenharmony_ci if (has_split && has_glob) { 96999ca880aSopenharmony_ci glob = GL_SPLIT_GLOB; 97099ca880aSopenharmony_ci } else if (has_split) { 97199ca880aSopenharmony_ci glob = GL_SPLIT; 97299ca880aSopenharmony_ci } else if (has_glob) { 97399ca880aSopenharmony_ci if (streq(value, "?*")) 97499ca880aSopenharmony_ci glob = GL_SOMETHING; 97599ca880aSopenharmony_ci else 97699ca880aSopenharmony_ci glob = GL_GLOB; 97799ca880aSopenharmony_ci } else { 97899ca880aSopenharmony_ci glob = GL_PLAIN; 97999ca880aSopenharmony_ci } 98099ca880aSopenharmony_ci token->key.glob = glob; 98199ca880aSopenharmony_ci } 98299ca880aSopenharmony_ci 98399ca880aSopenharmony_ci if (value != NULL && type > TK_M_MAX) { 98499ca880aSopenharmony_ci /* check if assigned value has substitution chars */ 98599ca880aSopenharmony_ci if (value[0] == '[') 98699ca880aSopenharmony_ci token->key.subst = SB_SUBSYS; 98799ca880aSopenharmony_ci else if (strchr(value, '%') != NULL || strchr(value, '$') != NULL) 98899ca880aSopenharmony_ci token->key.subst = SB_FORMAT; 98999ca880aSopenharmony_ci else 99099ca880aSopenharmony_ci token->key.subst = SB_NONE; 99199ca880aSopenharmony_ci } 99299ca880aSopenharmony_ci 99399ca880aSopenharmony_ci if (attr != NULL) { 99499ca880aSopenharmony_ci /* check if property/attribute name has substitution chars */ 99599ca880aSopenharmony_ci if (attr[0] == '[') 99699ca880aSopenharmony_ci token->key.attrsubst = SB_SUBSYS; 99799ca880aSopenharmony_ci else if (strchr(attr, '%') != NULL || strchr(attr, '$') != NULL) 99899ca880aSopenharmony_ci token->key.attrsubst = SB_FORMAT; 99999ca880aSopenharmony_ci else 100099ca880aSopenharmony_ci token->key.attrsubst = SB_NONE; 100199ca880aSopenharmony_ci } 100299ca880aSopenharmony_ci 100399ca880aSopenharmony_ci token->key.type = type; 100499ca880aSopenharmony_ci token->key.op = op; 100599ca880aSopenharmony_ci rule_tmp->token_cur++; 100699ca880aSopenharmony_ci if (rule_tmp->token_cur >= ELEMENTSOF(rule_tmp->token)) { 100799ca880aSopenharmony_ci log_error("temporary rule array too small"); 100899ca880aSopenharmony_ci return -1; 100999ca880aSopenharmony_ci } 101099ca880aSopenharmony_ci return 0; 101199ca880aSopenharmony_ci} 101299ca880aSopenharmony_ci 101399ca880aSopenharmony_cistatic int sort_token(struct udev_rules *rules, struct rule_tmp *rule_tmp) { 101499ca880aSopenharmony_ci unsigned int i; 101599ca880aSopenharmony_ci unsigned int start = 0; 101699ca880aSopenharmony_ci unsigned int end = rule_tmp->token_cur; 101799ca880aSopenharmony_ci 101899ca880aSopenharmony_ci for (i = 0; i < rule_tmp->token_cur; i++) { 101999ca880aSopenharmony_ci enum token_type next_val = TK_UNSET; 102099ca880aSopenharmony_ci unsigned int next_idx = 0; 102199ca880aSopenharmony_ci unsigned int j; 102299ca880aSopenharmony_ci 102399ca880aSopenharmony_ci /* find smallest value */ 102499ca880aSopenharmony_ci for (j = start; j < end; j++) { 102599ca880aSopenharmony_ci if (rule_tmp->token[j].type == TK_UNSET) 102699ca880aSopenharmony_ci continue; 102799ca880aSopenharmony_ci if (next_val == TK_UNSET || rule_tmp->token[j].type < next_val) { 102899ca880aSopenharmony_ci next_val = rule_tmp->token[j].type; 102999ca880aSopenharmony_ci next_idx = j; 103099ca880aSopenharmony_ci } 103199ca880aSopenharmony_ci } 103299ca880aSopenharmony_ci 103399ca880aSopenharmony_ci /* add token and mark done */ 103499ca880aSopenharmony_ci if (add_token(rules, &rule_tmp->token[next_idx]) != 0) 103599ca880aSopenharmony_ci return -1; 103699ca880aSopenharmony_ci rule_tmp->token[next_idx].type = TK_UNSET; 103799ca880aSopenharmony_ci 103899ca880aSopenharmony_ci /* shrink range */ 103999ca880aSopenharmony_ci if (next_idx == start) 104099ca880aSopenharmony_ci start++; 104199ca880aSopenharmony_ci if (next_idx+1 == end) 104299ca880aSopenharmony_ci end--; 104399ca880aSopenharmony_ci } 104499ca880aSopenharmony_ci return 0; 104599ca880aSopenharmony_ci} 104699ca880aSopenharmony_ci 104799ca880aSopenharmony_cistatic int add_rule(struct udev_rules *rules, char *line, 104899ca880aSopenharmony_ci const char *filename, unsigned int filename_off, unsigned int lineno) { 104999ca880aSopenharmony_ci char *linepos; 105099ca880aSopenharmony_ci const char *attr; 105199ca880aSopenharmony_ci struct rule_tmp rule_tmp; 105299ca880aSopenharmony_ci 105399ca880aSopenharmony_ci memzero(&rule_tmp, sizeof(struct rule_tmp)); 105499ca880aSopenharmony_ci rule_tmp.rules = rules; 105599ca880aSopenharmony_ci rule_tmp.rule.type = TK_RULE; 105699ca880aSopenharmony_ci /* the offset in the rule is limited to unsigned short */ 105799ca880aSopenharmony_ci if (filename_off < USHRT_MAX) 105899ca880aSopenharmony_ci rule_tmp.rule.rule.filename_off = filename_off; 105999ca880aSopenharmony_ci rule_tmp.rule.rule.filename_line = lineno; 106099ca880aSopenharmony_ci 106199ca880aSopenharmony_ci linepos = line; 106299ca880aSopenharmony_ci for (;;) { 106399ca880aSopenharmony_ci char *key; 106499ca880aSopenharmony_ci char *value; 106599ca880aSopenharmony_ci enum operation_type op; 106699ca880aSopenharmony_ci 106799ca880aSopenharmony_ci if (get_key(rules->udev, &linepos, &key, &op, &value) != 0) { 106899ca880aSopenharmony_ci /* Avoid erroring on trailing whitespace. This is probably rare 106999ca880aSopenharmony_ci * so save the work for the error case instead of always trying 107099ca880aSopenharmony_ci * to strip the trailing whitespace with strstrip(). */ 107199ca880aSopenharmony_ci while (isblank(*linepos)) 107299ca880aSopenharmony_ci linepos++; 107399ca880aSopenharmony_ci 107499ca880aSopenharmony_ci /* If we aren't at the end of the line, this is a parsing error. 107599ca880aSopenharmony_ci * Make a best effort to describe where the problem is. */ 107699ca880aSopenharmony_ci if (!strchr(NEWLINE, *linepos)) { 107799ca880aSopenharmony_ci char buf[2] = {*linepos}; 107899ca880aSopenharmony_ci _cleanup_free_ char *tmp; 107999ca880aSopenharmony_ci 108099ca880aSopenharmony_ci tmp = cescape(buf); 108199ca880aSopenharmony_ci log_error("invalid key/value pair in file %s on line %u, starting at character %tu ('%s')", 108299ca880aSopenharmony_ci filename, lineno, linepos - line + 1, tmp); 108399ca880aSopenharmony_ci if (*linepos == '#') 108499ca880aSopenharmony_ci log_error("hint: comments can only start at beginning of line"); 108599ca880aSopenharmony_ci } 108699ca880aSopenharmony_ci break; 108799ca880aSopenharmony_ci } 108899ca880aSopenharmony_ci 108999ca880aSopenharmony_ci if (streq(key, "ACTION")) { 109099ca880aSopenharmony_ci if (op > OP_MATCH_MAX) { 109199ca880aSopenharmony_ci log_error("invalid ACTION operation"); 109299ca880aSopenharmony_ci goto invalid; 109399ca880aSopenharmony_ci } 109499ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_M_ACTION, op, value, NULL); 109599ca880aSopenharmony_ci continue; 109699ca880aSopenharmony_ci } 109799ca880aSopenharmony_ci 109899ca880aSopenharmony_ci if (streq(key, "DEVPATH")) { 109999ca880aSopenharmony_ci if (op > OP_MATCH_MAX) { 110099ca880aSopenharmony_ci log_error("invalid DEVPATH operation"); 110199ca880aSopenharmony_ci goto invalid; 110299ca880aSopenharmony_ci } 110399ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_M_DEVPATH, op, value, NULL); 110499ca880aSopenharmony_ci continue; 110599ca880aSopenharmony_ci } 110699ca880aSopenharmony_ci 110799ca880aSopenharmony_ci if (streq(key, "KERNEL")) { 110899ca880aSopenharmony_ci if (op > OP_MATCH_MAX) { 110999ca880aSopenharmony_ci log_error("invalid KERNEL operation"); 111099ca880aSopenharmony_ci goto invalid; 111199ca880aSopenharmony_ci } 111299ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_M_KERNEL, op, value, NULL); 111399ca880aSopenharmony_ci continue; 111499ca880aSopenharmony_ci } 111599ca880aSopenharmony_ci 111699ca880aSopenharmony_ci if (streq(key, "SUBSYSTEM")) { 111799ca880aSopenharmony_ci if (op > OP_MATCH_MAX) { 111899ca880aSopenharmony_ci log_error("invalid SUBSYSTEM operation"); 111999ca880aSopenharmony_ci goto invalid; 112099ca880aSopenharmony_ci } 112199ca880aSopenharmony_ci /* bus, class, subsystem events should all be the same */ 112299ca880aSopenharmony_ci if (streq(value, "subsystem") || 112399ca880aSopenharmony_ci streq(value, "bus") || 112499ca880aSopenharmony_ci streq(value, "class")) { 112599ca880aSopenharmony_ci if (streq(value, "bus") || streq(value, "class")) 112699ca880aSopenharmony_ci log_error("'%s' must be specified as 'subsystem' " 112799ca880aSopenharmony_ci "please fix it in %s:%u", value, filename, lineno); 112899ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_M_SUBSYSTEM, op, "subsystem|class|bus", NULL); 112999ca880aSopenharmony_ci } else 113099ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_M_SUBSYSTEM, op, value, NULL); 113199ca880aSopenharmony_ci continue; 113299ca880aSopenharmony_ci } 113399ca880aSopenharmony_ci 113499ca880aSopenharmony_ci if (streq(key, "DRIVER")) { 113599ca880aSopenharmony_ci if (op > OP_MATCH_MAX) { 113699ca880aSopenharmony_ci log_error("invalid DRIVER operation"); 113799ca880aSopenharmony_ci goto invalid; 113899ca880aSopenharmony_ci } 113999ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_M_DRIVER, op, value, NULL); 114099ca880aSopenharmony_ci continue; 114199ca880aSopenharmony_ci } 114299ca880aSopenharmony_ci 114399ca880aSopenharmony_ci if (startswith(key, "ATTR{")) { 114499ca880aSopenharmony_ci attr = get_key_attribute(rules->udev, key + strlen("ATTR")); 114599ca880aSopenharmony_ci if (attr == NULL) { 114699ca880aSopenharmony_ci log_error("error parsing ATTR attribute"); 114799ca880aSopenharmony_ci goto invalid; 114899ca880aSopenharmony_ci } 114999ca880aSopenharmony_ci if (op == OP_REMOVE) { 115099ca880aSopenharmony_ci log_error("invalid ATTR operation"); 115199ca880aSopenharmony_ci goto invalid; 115299ca880aSopenharmony_ci } 115399ca880aSopenharmony_ci if (op < OP_MATCH_MAX) 115499ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_M_ATTR, op, value, attr); 115599ca880aSopenharmony_ci else 115699ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_A_ATTR, op, value, attr); 115799ca880aSopenharmony_ci continue; 115899ca880aSopenharmony_ci } 115999ca880aSopenharmony_ci 116099ca880aSopenharmony_ci if (startswith(key, "SYSCTL{")) { 116199ca880aSopenharmony_ci attr = get_key_attribute(rules->udev, key + strlen("SYSCTL")); 116299ca880aSopenharmony_ci if (attr == NULL) { 116399ca880aSopenharmony_ci log_error("error parsing SYSCTL attribute"); 116499ca880aSopenharmony_ci goto invalid; 116599ca880aSopenharmony_ci } 116699ca880aSopenharmony_ci if (op == OP_REMOVE) { 116799ca880aSopenharmony_ci log_error("invalid SYSCTL operation"); 116899ca880aSopenharmony_ci goto invalid; 116999ca880aSopenharmony_ci } 117099ca880aSopenharmony_ci if (op < OP_MATCH_MAX) 117199ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_M_SYSCTL, op, value, attr); 117299ca880aSopenharmony_ci else 117399ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_A_SYSCTL, op, value, attr); 117499ca880aSopenharmony_ci continue; 117599ca880aSopenharmony_ci } 117699ca880aSopenharmony_ci 117799ca880aSopenharmony_ci if (startswith(key, "SECLABEL{")) { 117899ca880aSopenharmony_ci attr = get_key_attribute(rules->udev, key + strlen("SECLABEL")); 117999ca880aSopenharmony_ci if (!attr) { 118099ca880aSopenharmony_ci log_error("error parsing SECLABEL attribute"); 118199ca880aSopenharmony_ci goto invalid; 118299ca880aSopenharmony_ci } 118399ca880aSopenharmony_ci if (op == OP_REMOVE) { 118499ca880aSopenharmony_ci log_error("invalid SECLABEL operation"); 118599ca880aSopenharmony_ci goto invalid; 118699ca880aSopenharmony_ci } 118799ca880aSopenharmony_ci 118899ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_A_SECLABEL, op, value, attr); 118999ca880aSopenharmony_ci continue; 119099ca880aSopenharmony_ci } 119199ca880aSopenharmony_ci 119299ca880aSopenharmony_ci if (streq(key, "KERNELS")) { 119399ca880aSopenharmony_ci if (op > OP_MATCH_MAX) { 119499ca880aSopenharmony_ci log_error("invalid KERNELS operation"); 119599ca880aSopenharmony_ci goto invalid; 119699ca880aSopenharmony_ci } 119799ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_M_KERNELS, op, value, NULL); 119899ca880aSopenharmony_ci continue; 119999ca880aSopenharmony_ci } 120099ca880aSopenharmony_ci 120199ca880aSopenharmony_ci if (streq(key, "SUBSYSTEMS")) { 120299ca880aSopenharmony_ci if (op > OP_MATCH_MAX) { 120399ca880aSopenharmony_ci log_error("invalid SUBSYSTEMS operation"); 120499ca880aSopenharmony_ci goto invalid; 120599ca880aSopenharmony_ci } 120699ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_M_SUBSYSTEMS, op, value, NULL); 120799ca880aSopenharmony_ci continue; 120899ca880aSopenharmony_ci } 120999ca880aSopenharmony_ci 121099ca880aSopenharmony_ci if (streq(key, "DRIVERS")) { 121199ca880aSopenharmony_ci if (op > OP_MATCH_MAX) { 121299ca880aSopenharmony_ci log_error("invalid DRIVERS operation"); 121399ca880aSopenharmony_ci goto invalid; 121499ca880aSopenharmony_ci } 121599ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_M_DRIVERS, op, value, NULL); 121699ca880aSopenharmony_ci continue; 121799ca880aSopenharmony_ci } 121899ca880aSopenharmony_ci 121999ca880aSopenharmony_ci if (startswith(key, "ATTRS{")) { 122099ca880aSopenharmony_ci if (op > OP_MATCH_MAX) { 122199ca880aSopenharmony_ci log_error("invalid ATTRS operation"); 122299ca880aSopenharmony_ci goto invalid; 122399ca880aSopenharmony_ci } 122499ca880aSopenharmony_ci attr = get_key_attribute(rules->udev, key + strlen("ATTRS")); 122599ca880aSopenharmony_ci if (attr == NULL) { 122699ca880aSopenharmony_ci log_error("error parsing ATTRS attribute"); 122799ca880aSopenharmony_ci goto invalid; 122899ca880aSopenharmony_ci } 122999ca880aSopenharmony_ci if (startswith(attr, "device/")) 123099ca880aSopenharmony_ci log_error("the 'device' link may not be available in a future kernel, " 123199ca880aSopenharmony_ci "please fix it in %s:%u", filename, lineno); 123299ca880aSopenharmony_ci else if (strstr(attr, "../") != NULL) 123399ca880aSopenharmony_ci log_error("do not reference parent sysfs directories directly, " 123499ca880aSopenharmony_ci "it may break with a future kernel, please fix it in %s:%u", filename, lineno); 123599ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_M_ATTRS, op, value, attr); 123699ca880aSopenharmony_ci continue; 123799ca880aSopenharmony_ci } 123899ca880aSopenharmony_ci 123999ca880aSopenharmony_ci if (streq(key, "TAGS")) { 124099ca880aSopenharmony_ci if (op > OP_MATCH_MAX) { 124199ca880aSopenharmony_ci log_error("invalid TAGS operation"); 124299ca880aSopenharmony_ci goto invalid; 124399ca880aSopenharmony_ci } 124499ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_M_TAGS, op, value, NULL); 124599ca880aSopenharmony_ci continue; 124699ca880aSopenharmony_ci } 124799ca880aSopenharmony_ci 124899ca880aSopenharmony_ci if (startswith(key, "ENV{")) { 124999ca880aSopenharmony_ci attr = get_key_attribute(rules->udev, key + strlen("ENV")); 125099ca880aSopenharmony_ci if (attr == NULL) { 125199ca880aSopenharmony_ci log_error("error parsing ENV attribute"); 125299ca880aSopenharmony_ci goto invalid; 125399ca880aSopenharmony_ci } 125499ca880aSopenharmony_ci if (op == OP_REMOVE) { 125599ca880aSopenharmony_ci log_error("invalid ENV operation"); 125699ca880aSopenharmony_ci goto invalid; 125799ca880aSopenharmony_ci } 125899ca880aSopenharmony_ci if (op < OP_MATCH_MAX) { 125999ca880aSopenharmony_ci if (rule_add_key(&rule_tmp, TK_M_ENV, op, value, attr) != 0) 126099ca880aSopenharmony_ci goto invalid; 126199ca880aSopenharmony_ci } else { 126299ca880aSopenharmony_ci static const char *blacklist[] = { 126399ca880aSopenharmony_ci "ACTION", 126499ca880aSopenharmony_ci "SUBSYSTEM", 126599ca880aSopenharmony_ci "DEVTYPE", 126699ca880aSopenharmony_ci "MAJOR", 126799ca880aSopenharmony_ci "MINOR", 126899ca880aSopenharmony_ci "DRIVER", 126999ca880aSopenharmony_ci "IFINDEX", 127099ca880aSopenharmony_ci "DEVNAME", 127199ca880aSopenharmony_ci "DEVLINKS", 127299ca880aSopenharmony_ci "DEVPATH", 127399ca880aSopenharmony_ci "TAGS", 127499ca880aSopenharmony_ci }; 127599ca880aSopenharmony_ci unsigned int i; 127699ca880aSopenharmony_ci 127799ca880aSopenharmony_ci for (i = 0; i < ELEMENTSOF(blacklist); i++) { 127899ca880aSopenharmony_ci if (!streq(attr, blacklist[i])) 127999ca880aSopenharmony_ci continue; 128099ca880aSopenharmony_ci log_error("invalid ENV attribute, '%s' can not be set %s:%u", attr, filename, lineno); 128199ca880aSopenharmony_ci goto invalid; 128299ca880aSopenharmony_ci } 128399ca880aSopenharmony_ci if (rule_add_key(&rule_tmp, TK_A_ENV, op, value, attr) != 0) 128499ca880aSopenharmony_ci goto invalid; 128599ca880aSopenharmony_ci } 128699ca880aSopenharmony_ci continue; 128799ca880aSopenharmony_ci } 128899ca880aSopenharmony_ci 128999ca880aSopenharmony_ci if (streq(key, "TAG")) { 129099ca880aSopenharmony_ci if (op < OP_MATCH_MAX) 129199ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_M_TAG, op, value, NULL); 129299ca880aSopenharmony_ci else 129399ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_A_TAG, op, value, NULL); 129499ca880aSopenharmony_ci continue; 129599ca880aSopenharmony_ci } 129699ca880aSopenharmony_ci 129799ca880aSopenharmony_ci if (streq(key, "PROGRAM")) { 129899ca880aSopenharmony_ci if (op == OP_REMOVE) { 129999ca880aSopenharmony_ci log_error("invalid PROGRAM operation"); 130099ca880aSopenharmony_ci goto invalid; 130199ca880aSopenharmony_ci } 130299ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_M_PROGRAM, op, value, NULL); 130399ca880aSopenharmony_ci continue; 130499ca880aSopenharmony_ci } 130599ca880aSopenharmony_ci 130699ca880aSopenharmony_ci if (streq(key, "RESULT")) { 130799ca880aSopenharmony_ci if (op > OP_MATCH_MAX) { 130899ca880aSopenharmony_ci log_error("invalid RESULT operation"); 130999ca880aSopenharmony_ci goto invalid; 131099ca880aSopenharmony_ci } 131199ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_M_RESULT, op, value, NULL); 131299ca880aSopenharmony_ci continue; 131399ca880aSopenharmony_ci } 131499ca880aSopenharmony_ci 131599ca880aSopenharmony_ci if (startswith(key, "IMPORT")) { 131699ca880aSopenharmony_ci attr = get_key_attribute(rules->udev, key + strlen("IMPORT")); 131799ca880aSopenharmony_ci if (attr == NULL) { 131899ca880aSopenharmony_ci log_error("IMPORT{} type missing, ignoring IMPORT %s:%u", filename, lineno); 131999ca880aSopenharmony_ci continue; 132099ca880aSopenharmony_ci } 132199ca880aSopenharmony_ci if (op == OP_REMOVE) { 132299ca880aSopenharmony_ci log_error("invalid IMPORT operation"); 132399ca880aSopenharmony_ci goto invalid; 132499ca880aSopenharmony_ci } 132599ca880aSopenharmony_ci if (streq(attr, "program")) { 132699ca880aSopenharmony_ci /* find known built-in command */ 132799ca880aSopenharmony_ci if (value[0] != '/') { 132899ca880aSopenharmony_ci enum udev_builtin_cmd cmd; 132999ca880aSopenharmony_ci 133099ca880aSopenharmony_ci cmd = udev_builtin_lookup(value); 133199ca880aSopenharmony_ci if (cmd < UDEV_BUILTIN_MAX) { 133299ca880aSopenharmony_ci log_debug("IMPORT found builtin '%s', replacing %s:%u", 133399ca880aSopenharmony_ci value, filename, lineno); 133499ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd); 133599ca880aSopenharmony_ci continue; 133699ca880aSopenharmony_ci } 133799ca880aSopenharmony_ci } 133899ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_M_IMPORT_PROG, op, value, NULL); 133999ca880aSopenharmony_ci } else if (streq(attr, "builtin")) { 134099ca880aSopenharmony_ci enum udev_builtin_cmd cmd = udev_builtin_lookup(value); 134199ca880aSopenharmony_ci 134299ca880aSopenharmony_ci if (cmd < UDEV_BUILTIN_MAX) 134399ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd); 134499ca880aSopenharmony_ci else 134599ca880aSopenharmony_ci log_error("IMPORT{builtin}: '%s' unknown %s:%u", value, filename, lineno); 134699ca880aSopenharmony_ci } else if (streq(attr, "file")) { 134799ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_M_IMPORT_FILE, op, value, NULL); 134899ca880aSopenharmony_ci } else if (streq(attr, "db")) { 134999ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_M_IMPORT_DB, op, value, NULL); 135099ca880aSopenharmony_ci } else if (streq(attr, "cmdline")) { 135199ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_M_IMPORT_CMDLINE, op, value, NULL); 135299ca880aSopenharmony_ci } else if (streq(attr, "parent")) { 135399ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_M_IMPORT_PARENT, op, value, NULL); 135499ca880aSopenharmony_ci } else 135599ca880aSopenharmony_ci log_error("IMPORT{} unknown type, ignoring IMPORT %s:%u", filename, lineno); 135699ca880aSopenharmony_ci continue; 135799ca880aSopenharmony_ci } 135899ca880aSopenharmony_ci 135999ca880aSopenharmony_ci if (startswith(key, "TEST")) { 136099ca880aSopenharmony_ci mode_t mode = 0; 136199ca880aSopenharmony_ci 136299ca880aSopenharmony_ci if (op > OP_MATCH_MAX) { 136399ca880aSopenharmony_ci log_error("invalid TEST operation"); 136499ca880aSopenharmony_ci goto invalid; 136599ca880aSopenharmony_ci } 136699ca880aSopenharmony_ci attr = get_key_attribute(rules->udev, key + strlen("TEST")); 136799ca880aSopenharmony_ci if (attr != NULL) { 136899ca880aSopenharmony_ci mode = strtol(attr, NULL, 8); 136999ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_M_TEST, op, value, &mode); 137099ca880aSopenharmony_ci } else { 137199ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_M_TEST, op, value, NULL); 137299ca880aSopenharmony_ci } 137399ca880aSopenharmony_ci continue; 137499ca880aSopenharmony_ci } 137599ca880aSopenharmony_ci 137699ca880aSopenharmony_ci if (startswith(key, "RUN")) { 137799ca880aSopenharmony_ci attr = get_key_attribute(rules->udev, key + strlen("RUN")); 137899ca880aSopenharmony_ci if (attr == NULL) 137999ca880aSopenharmony_ci attr = "program"; 138099ca880aSopenharmony_ci if (op == OP_REMOVE) { 138199ca880aSopenharmony_ci log_error("invalid RUN operation"); 138299ca880aSopenharmony_ci goto invalid; 138399ca880aSopenharmony_ci } 138499ca880aSopenharmony_ci 138599ca880aSopenharmony_ci if (streq(attr, "builtin")) { 138699ca880aSopenharmony_ci enum udev_builtin_cmd cmd = udev_builtin_lookup(value); 138799ca880aSopenharmony_ci 138899ca880aSopenharmony_ci if (cmd < UDEV_BUILTIN_MAX) 138999ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_A_RUN_BUILTIN, op, value, &cmd); 139099ca880aSopenharmony_ci else 139199ca880aSopenharmony_ci log_error("RUN{builtin}: '%s' unknown %s:%u", value, filename, lineno); 139299ca880aSopenharmony_ci } else if (streq(attr, "program")) { 139399ca880aSopenharmony_ci enum udev_builtin_cmd cmd = UDEV_BUILTIN_MAX; 139499ca880aSopenharmony_ci 139599ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_A_RUN_PROGRAM, op, value, &cmd); 139699ca880aSopenharmony_ci } else { 139799ca880aSopenharmony_ci log_error("RUN{} unknown type, ignoring RUN %s:%u", filename, lineno); 139899ca880aSopenharmony_ci } 139999ca880aSopenharmony_ci 140099ca880aSopenharmony_ci continue; 140199ca880aSopenharmony_ci } 140299ca880aSopenharmony_ci 140399ca880aSopenharmony_ci if (streq(key, "WAIT_FOR") || streq(key, "WAIT_FOR_SYSFS")) { 140499ca880aSopenharmony_ci if (op == OP_REMOVE) { 140599ca880aSopenharmony_ci log_error("invalid WAIT_FOR/WAIT_FOR_SYSFS operation"); 140699ca880aSopenharmony_ci goto invalid; 140799ca880aSopenharmony_ci } 140899ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_M_WAITFOR, 0, value, NULL); 140999ca880aSopenharmony_ci continue; 141099ca880aSopenharmony_ci } 141199ca880aSopenharmony_ci 141299ca880aSopenharmony_ci if (streq(key, "LABEL")) { 141399ca880aSopenharmony_ci if (op == OP_REMOVE) { 141499ca880aSopenharmony_ci log_error("invalid LABEL operation"); 141599ca880aSopenharmony_ci goto invalid; 141699ca880aSopenharmony_ci } 141799ca880aSopenharmony_ci rule_tmp.rule.rule.label_off = rules_add_string(rules, value); 141899ca880aSopenharmony_ci continue; 141999ca880aSopenharmony_ci } 142099ca880aSopenharmony_ci 142199ca880aSopenharmony_ci if (streq(key, "GOTO")) { 142299ca880aSopenharmony_ci if (op == OP_REMOVE) { 142399ca880aSopenharmony_ci log_error("invalid GOTO operation"); 142499ca880aSopenharmony_ci goto invalid; 142599ca880aSopenharmony_ci } 142699ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_A_GOTO, 0, value, NULL); 142799ca880aSopenharmony_ci continue; 142899ca880aSopenharmony_ci } 142999ca880aSopenharmony_ci 143099ca880aSopenharmony_ci if (startswith(key, "NAME")) { 143199ca880aSopenharmony_ci if (op == OP_REMOVE) { 143299ca880aSopenharmony_ci log_error("invalid NAME operation"); 143399ca880aSopenharmony_ci goto invalid; 143499ca880aSopenharmony_ci } 143599ca880aSopenharmony_ci if (op < OP_MATCH_MAX) { 143699ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_M_NAME, op, value, NULL); 143799ca880aSopenharmony_ci } else { 143899ca880aSopenharmony_ci if (streq(value, "%k")) { 143999ca880aSopenharmony_ci log_error("NAME=\"%%k\" is ignored, because it breaks kernel supplied names, " 144099ca880aSopenharmony_ci "please remove it from %s:%u\n", filename, lineno); 144199ca880aSopenharmony_ci continue; 144299ca880aSopenharmony_ci } 144399ca880aSopenharmony_ci if (value[0] == '\0') { 144499ca880aSopenharmony_ci log_debug("NAME=\"\" is ignored, because udev will not delete any device nodes, " 144599ca880aSopenharmony_ci "please remove it from %s:%u\n", filename, lineno); 144699ca880aSopenharmony_ci continue; 144799ca880aSopenharmony_ci } 144899ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_A_NAME, op, value, NULL); 144999ca880aSopenharmony_ci } 145099ca880aSopenharmony_ci rule_tmp.rule.rule.can_set_name = true; 145199ca880aSopenharmony_ci continue; 145299ca880aSopenharmony_ci } 145399ca880aSopenharmony_ci 145499ca880aSopenharmony_ci if (streq(key, "SYMLINK")) { 145599ca880aSopenharmony_ci if (op == OP_REMOVE) { 145699ca880aSopenharmony_ci log_error("invalid SYMLINK operation"); 145799ca880aSopenharmony_ci goto invalid; 145899ca880aSopenharmony_ci } 145999ca880aSopenharmony_ci if (op < OP_MATCH_MAX) 146099ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_M_DEVLINK, op, value, NULL); 146199ca880aSopenharmony_ci else 146299ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_A_DEVLINK, op, value, NULL); 146399ca880aSopenharmony_ci rule_tmp.rule.rule.can_set_name = true; 146499ca880aSopenharmony_ci continue; 146599ca880aSopenharmony_ci } 146699ca880aSopenharmony_ci 146799ca880aSopenharmony_ci if (streq(key, "OWNER")) { 146899ca880aSopenharmony_ci uid_t uid; 146999ca880aSopenharmony_ci char *endptr; 147099ca880aSopenharmony_ci 147199ca880aSopenharmony_ci if (op == OP_REMOVE) { 147299ca880aSopenharmony_ci log_error("invalid OWNER operation"); 147399ca880aSopenharmony_ci goto invalid; 147499ca880aSopenharmony_ci } 147599ca880aSopenharmony_ci 147699ca880aSopenharmony_ci uid = strtoul(value, &endptr, 10); 147799ca880aSopenharmony_ci if (endptr[0] == '\0') { 147899ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid); 147999ca880aSopenharmony_ci } else if ((rules->resolve_names > 0) && strchr("$%", value[0]) == NULL) { 148099ca880aSopenharmony_ci uid = add_uid(rules, value); 148199ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid); 148299ca880aSopenharmony_ci } else if (rules->resolve_names >= 0) { 148399ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_A_OWNER, op, value, NULL); 148499ca880aSopenharmony_ci } 148599ca880aSopenharmony_ci rule_tmp.rule.rule.can_set_name = true; 148699ca880aSopenharmony_ci continue; 148799ca880aSopenharmony_ci } 148899ca880aSopenharmony_ci 148999ca880aSopenharmony_ci if (streq(key, "GROUP")) { 149099ca880aSopenharmony_ci gid_t gid; 149199ca880aSopenharmony_ci char *endptr; 149299ca880aSopenharmony_ci 149399ca880aSopenharmony_ci if (op == OP_REMOVE) { 149499ca880aSopenharmony_ci log_error("invalid GROUP operation"); 149599ca880aSopenharmony_ci goto invalid; 149699ca880aSopenharmony_ci } 149799ca880aSopenharmony_ci 149899ca880aSopenharmony_ci gid = strtoul(value, &endptr, 10); 149999ca880aSopenharmony_ci if (endptr[0] == '\0') { 150099ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid); 150199ca880aSopenharmony_ci } else if ((rules->resolve_names > 0) && strchr("$%", value[0]) == NULL) { 150299ca880aSopenharmony_ci gid = add_gid(rules, value); 150399ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid); 150499ca880aSopenharmony_ci } else if (rules->resolve_names >= 0) { 150599ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_A_GROUP, op, value, NULL); 150699ca880aSopenharmony_ci } 150799ca880aSopenharmony_ci rule_tmp.rule.rule.can_set_name = true; 150899ca880aSopenharmony_ci continue; 150999ca880aSopenharmony_ci } 151099ca880aSopenharmony_ci 151199ca880aSopenharmony_ci if (streq(key, "MODE")) { 151299ca880aSopenharmony_ci mode_t mode; 151399ca880aSopenharmony_ci char *endptr; 151499ca880aSopenharmony_ci 151599ca880aSopenharmony_ci if (op == OP_REMOVE) { 151699ca880aSopenharmony_ci log_error("invalid MODE operation"); 151799ca880aSopenharmony_ci goto invalid; 151899ca880aSopenharmony_ci } 151999ca880aSopenharmony_ci 152099ca880aSopenharmony_ci mode = strtol(value, &endptr, 8); 152199ca880aSopenharmony_ci if (endptr[0] == '\0') 152299ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_A_MODE_ID, op, NULL, &mode); 152399ca880aSopenharmony_ci else 152499ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_A_MODE, op, value, NULL); 152599ca880aSopenharmony_ci rule_tmp.rule.rule.can_set_name = true; 152699ca880aSopenharmony_ci continue; 152799ca880aSopenharmony_ci } 152899ca880aSopenharmony_ci 152999ca880aSopenharmony_ci if (streq(key, "OPTIONS")) { 153099ca880aSopenharmony_ci const char *pos; 153199ca880aSopenharmony_ci 153299ca880aSopenharmony_ci if (op == OP_REMOVE) { 153399ca880aSopenharmony_ci log_error("invalid OPTIONS operation"); 153499ca880aSopenharmony_ci goto invalid; 153599ca880aSopenharmony_ci } 153699ca880aSopenharmony_ci 153799ca880aSopenharmony_ci pos = strstr(value, "link_priority="); 153899ca880aSopenharmony_ci if (pos != NULL) { 153999ca880aSopenharmony_ci int prio = atoi(&pos[strlen("link_priority=")]); 154099ca880aSopenharmony_ci 154199ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_A_DEVLINK_PRIO, op, NULL, &prio); 154299ca880aSopenharmony_ci } 154399ca880aSopenharmony_ci 154499ca880aSopenharmony_ci pos = strstr(value, "string_escape="); 154599ca880aSopenharmony_ci if (pos != NULL) { 154699ca880aSopenharmony_ci pos = &pos[strlen("string_escape=")]; 154799ca880aSopenharmony_ci if (startswith(pos, "none")) 154899ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_NONE, op, NULL, NULL); 154999ca880aSopenharmony_ci else if (startswith(pos, "replace")) 155099ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_REPLACE, op, NULL, NULL); 155199ca880aSopenharmony_ci } 155299ca880aSopenharmony_ci 155399ca880aSopenharmony_ci pos = strstr(value, "db_persist"); 155499ca880aSopenharmony_ci if (pos != NULL) 155599ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_A_DB_PERSIST, op, NULL, NULL); 155699ca880aSopenharmony_ci 155799ca880aSopenharmony_ci pos = strstr(value, "nowatch"); 155899ca880aSopenharmony_ci if (pos != NULL) { 155999ca880aSopenharmony_ci const int off = 0; 156099ca880aSopenharmony_ci 156199ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &off); 156299ca880aSopenharmony_ci } else { 156399ca880aSopenharmony_ci pos = strstr(value, "watch"); 156499ca880aSopenharmony_ci if (pos != NULL) { 156599ca880aSopenharmony_ci const int on = 1; 156699ca880aSopenharmony_ci 156799ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &on); 156899ca880aSopenharmony_ci } 156999ca880aSopenharmony_ci } 157099ca880aSopenharmony_ci 157199ca880aSopenharmony_ci pos = strstr(value, "static_node="); 157299ca880aSopenharmony_ci if (pos != NULL) { 157399ca880aSopenharmony_ci rule_add_key(&rule_tmp, TK_A_STATIC_NODE, op, &pos[strlen("static_node=")], NULL); 157499ca880aSopenharmony_ci rule_tmp.rule.rule.has_static_node = true; 157599ca880aSopenharmony_ci } 157699ca880aSopenharmony_ci 157799ca880aSopenharmony_ci continue; 157899ca880aSopenharmony_ci } 157999ca880aSopenharmony_ci 158099ca880aSopenharmony_ci log_error("unknown key '%s' in %s:%u", key, filename, lineno); 158199ca880aSopenharmony_ci goto invalid; 158299ca880aSopenharmony_ci } 158399ca880aSopenharmony_ci 158499ca880aSopenharmony_ci /* add rule token */ 158599ca880aSopenharmony_ci rule_tmp.rule.rule.token_count = 1 + rule_tmp.token_cur; 158699ca880aSopenharmony_ci if (add_token(rules, &rule_tmp.rule) != 0) 158799ca880aSopenharmony_ci goto invalid; 158899ca880aSopenharmony_ci 158999ca880aSopenharmony_ci /* add tokens to list, sorted by type */ 159099ca880aSopenharmony_ci if (sort_token(rules, &rule_tmp) != 0) 159199ca880aSopenharmony_ci goto invalid; 159299ca880aSopenharmony_ci 159399ca880aSopenharmony_ci return 0; 159499ca880aSopenharmony_ciinvalid: 159599ca880aSopenharmony_ci log_error("invalid rule '%s:%u'", filename, lineno); 159699ca880aSopenharmony_ci return -1; 159799ca880aSopenharmony_ci} 159899ca880aSopenharmony_ci 159999ca880aSopenharmony_cistatic int parse_file(struct udev_rules *rules, const char *filename) { 160099ca880aSopenharmony_ci _cleanup_fclose_ FILE *f = NULL; 160199ca880aSopenharmony_ci unsigned int first_token; 160299ca880aSopenharmony_ci unsigned int filename_off; 160399ca880aSopenharmony_ci char line[UTIL_LINE_SIZE]; 160499ca880aSopenharmony_ci int line_nr = 0; 160599ca880aSopenharmony_ci unsigned int i; 160699ca880aSopenharmony_ci 160799ca880aSopenharmony_ci f = fopen(filename, "re"); 160899ca880aSopenharmony_ci if (!f) { 160999ca880aSopenharmony_ci if (errno == ENOENT) 161099ca880aSopenharmony_ci return 0; 161199ca880aSopenharmony_ci else 161299ca880aSopenharmony_ci return -errno; 161399ca880aSopenharmony_ci } 161499ca880aSopenharmony_ci 161599ca880aSopenharmony_ci if (null_or_empty_fd(fileno(f))) { 161699ca880aSopenharmony_ci log_debug("Skipping empty file: %s", filename); 161799ca880aSopenharmony_ci return 0; 161899ca880aSopenharmony_ci } else 161999ca880aSopenharmony_ci log_debug("Reading rules file: %s", filename); 162099ca880aSopenharmony_ci 162199ca880aSopenharmony_ci first_token = rules->token_cur; 162299ca880aSopenharmony_ci filename_off = rules_add_string(rules, filename); 162399ca880aSopenharmony_ci 162499ca880aSopenharmony_ci while (fgets(line, sizeof(line), f) != NULL) { 162599ca880aSopenharmony_ci char *key; 162699ca880aSopenharmony_ci size_t len; 162799ca880aSopenharmony_ci 162899ca880aSopenharmony_ci /* skip whitespace */ 162999ca880aSopenharmony_ci line_nr++; 163099ca880aSopenharmony_ci key = line; 163199ca880aSopenharmony_ci while (isspace(key[0])) 163299ca880aSopenharmony_ci key++; 163399ca880aSopenharmony_ci 163499ca880aSopenharmony_ci /* comment */ 163599ca880aSopenharmony_ci if (key[0] == '#') 163699ca880aSopenharmony_ci continue; 163799ca880aSopenharmony_ci 163899ca880aSopenharmony_ci len = strlen(line); 163999ca880aSopenharmony_ci if (len < 3) 164099ca880aSopenharmony_ci continue; 164199ca880aSopenharmony_ci 164299ca880aSopenharmony_ci /* continue reading if backslash+newline is found */ 164399ca880aSopenharmony_ci while (line[len-2] == '\\') { 164499ca880aSopenharmony_ci if (fgets(&line[len-2], (sizeof(line)-len)+2, f) == NULL) 164599ca880aSopenharmony_ci break; 164699ca880aSopenharmony_ci if (strlen(&line[len-2]) < 2) 164799ca880aSopenharmony_ci break; 164899ca880aSopenharmony_ci line_nr++; 164999ca880aSopenharmony_ci len = strlen(line); 165099ca880aSopenharmony_ci } 165199ca880aSopenharmony_ci 165299ca880aSopenharmony_ci if (len+1 >= sizeof(line)) { 165399ca880aSopenharmony_ci log_error("line too long '%s':%u, ignored", filename, line_nr); 165499ca880aSopenharmony_ci continue; 165599ca880aSopenharmony_ci } 165699ca880aSopenharmony_ci add_rule(rules, key, filename, filename_off, line_nr); 165799ca880aSopenharmony_ci } 165899ca880aSopenharmony_ci 165999ca880aSopenharmony_ci /* link GOTOs to LABEL rules in this file to be able to fast-forward */ 166099ca880aSopenharmony_ci for (i = first_token+1; i < rules->token_cur; i++) { 166199ca880aSopenharmony_ci if (rules->tokens[i].type == TK_A_GOTO) { 166299ca880aSopenharmony_ci char *label = rules_str(rules, rules->tokens[i].key.value_off); 166399ca880aSopenharmony_ci unsigned int j; 166499ca880aSopenharmony_ci 166599ca880aSopenharmony_ci for (j = i+1; j < rules->token_cur; j++) { 166699ca880aSopenharmony_ci if (rules->tokens[j].type != TK_RULE) 166799ca880aSopenharmony_ci continue; 166899ca880aSopenharmony_ci if (rules->tokens[j].rule.label_off == 0) 166999ca880aSopenharmony_ci continue; 167099ca880aSopenharmony_ci if (!streq(label, rules_str(rules, rules->tokens[j].rule.label_off))) 167199ca880aSopenharmony_ci continue; 167299ca880aSopenharmony_ci rules->tokens[i].key.rule_goto = j; 167399ca880aSopenharmony_ci break; 167499ca880aSopenharmony_ci } 167599ca880aSopenharmony_ci if (rules->tokens[i].key.rule_goto == 0) 167699ca880aSopenharmony_ci log_error("GOTO '%s' has no matching label in: '%s'", label, filename); 167799ca880aSopenharmony_ci } 167899ca880aSopenharmony_ci } 167999ca880aSopenharmony_ci return 0; 168099ca880aSopenharmony_ci} 168199ca880aSopenharmony_ci 168299ca880aSopenharmony_cistruct udev_rules *udev_rules_new(struct udev *udev, int resolve_names) { 168399ca880aSopenharmony_ci struct udev_rules *rules; 168499ca880aSopenharmony_ci struct udev_list file_list; 168599ca880aSopenharmony_ci struct token end_token; 168699ca880aSopenharmony_ci char **files, **f; 168799ca880aSopenharmony_ci int r; 168899ca880aSopenharmony_ci 168999ca880aSopenharmony_ci rules = new0(struct udev_rules, 1); 169099ca880aSopenharmony_ci if (rules == NULL) 169199ca880aSopenharmony_ci return NULL; 169299ca880aSopenharmony_ci rules->udev = udev; 169399ca880aSopenharmony_ci rules->resolve_names = resolve_names; 169499ca880aSopenharmony_ci udev_list_init(udev, &file_list, true); 169599ca880aSopenharmony_ci 169699ca880aSopenharmony_ci /* init token array and string buffer */ 169799ca880aSopenharmony_ci rules->tokens = malloc(PREALLOC_TOKEN * sizeof(struct token)); 169899ca880aSopenharmony_ci if (rules->tokens == NULL) 169999ca880aSopenharmony_ci return udev_rules_unref(rules); 170099ca880aSopenharmony_ci rules->token_max = PREALLOC_TOKEN; 170199ca880aSopenharmony_ci 170299ca880aSopenharmony_ci rules->strbuf = strbuf_new(); 170399ca880aSopenharmony_ci if (!rules->strbuf) 170499ca880aSopenharmony_ci return udev_rules_unref(rules); 170599ca880aSopenharmony_ci 170699ca880aSopenharmony_ci udev_rules_check_timestamp(rules); 170799ca880aSopenharmony_ci 170899ca880aSopenharmony_ci r = conf_files_list_strv(&files, ".rules", NULL, rules_dirs); 170999ca880aSopenharmony_ci if (r < 0) { 171099ca880aSopenharmony_ci log_error_errno(r, "failed to enumerate rules files: %m"); 171199ca880aSopenharmony_ci return udev_rules_unref(rules); 171299ca880aSopenharmony_ci } 171399ca880aSopenharmony_ci 171499ca880aSopenharmony_ci /* 171599ca880aSopenharmony_ci * The offset value in the rules strct is limited; add all 171699ca880aSopenharmony_ci * rules file names to the beginning of the string buffer. 171799ca880aSopenharmony_ci */ 171899ca880aSopenharmony_ci STRV_FOREACH(f, files) 171999ca880aSopenharmony_ci rules_add_string(rules, *f); 172099ca880aSopenharmony_ci 172199ca880aSopenharmony_ci STRV_FOREACH(f, files) 172299ca880aSopenharmony_ci parse_file(rules, *f); 172399ca880aSopenharmony_ci 172499ca880aSopenharmony_ci strv_free(files); 172599ca880aSopenharmony_ci 172699ca880aSopenharmony_ci memzero(&end_token, sizeof(struct token)); 172799ca880aSopenharmony_ci end_token.type = TK_END; 172899ca880aSopenharmony_ci add_token(rules, &end_token); 172999ca880aSopenharmony_ci log_debug("rules contain %zu bytes tokens (%u * %zu bytes), %zu bytes strings", 173099ca880aSopenharmony_ci rules->token_max * sizeof(struct token), rules->token_max, sizeof(struct token), rules->strbuf->len); 173199ca880aSopenharmony_ci 173299ca880aSopenharmony_ci /* cleanup temporary strbuf data */ 173399ca880aSopenharmony_ci log_debug("%zu strings (%zu bytes), %zu de-duplicated (%zu bytes), %zu trie nodes used", 173499ca880aSopenharmony_ci rules->strbuf->in_count, rules->strbuf->in_len, 173599ca880aSopenharmony_ci rules->strbuf->dedup_count, rules->strbuf->dedup_len, rules->strbuf->nodes_count); 173699ca880aSopenharmony_ci strbuf_complete(rules->strbuf); 173799ca880aSopenharmony_ci 173899ca880aSopenharmony_ci /* cleanup uid/gid cache */ 173999ca880aSopenharmony_ci free(rules->uids); 174099ca880aSopenharmony_ci rules->uids = NULL; 174199ca880aSopenharmony_ci rules->uids_cur = 0; 174299ca880aSopenharmony_ci rules->uids_max = 0; 174399ca880aSopenharmony_ci free(rules->gids); 174499ca880aSopenharmony_ci rules->gids = NULL; 174599ca880aSopenharmony_ci rules->gids_cur = 0; 174699ca880aSopenharmony_ci rules->gids_max = 0; 174799ca880aSopenharmony_ci 174899ca880aSopenharmony_ci dump_rules(rules); 174999ca880aSopenharmony_ci return rules; 175099ca880aSopenharmony_ci} 175199ca880aSopenharmony_ci 175299ca880aSopenharmony_cistruct udev_rules *udev_rules_unref(struct udev_rules *rules) { 175399ca880aSopenharmony_ci if (rules == NULL) 175499ca880aSopenharmony_ci return NULL; 175599ca880aSopenharmony_ci free(rules->tokens); 175699ca880aSopenharmony_ci strbuf_cleanup(rules->strbuf); 175799ca880aSopenharmony_ci free(rules->uids); 175899ca880aSopenharmony_ci free(rules->gids); 175999ca880aSopenharmony_ci free(rules); 176099ca880aSopenharmony_ci return NULL; 176199ca880aSopenharmony_ci} 176299ca880aSopenharmony_ci 176399ca880aSopenharmony_cibool udev_rules_check_timestamp(struct udev_rules *rules) { 176499ca880aSopenharmony_ci if (!rules) 176599ca880aSopenharmony_ci return false; 176699ca880aSopenharmony_ci 176799ca880aSopenharmony_ci return paths_check_timestamp(rules_dirs, &rules->dirs_ts_usec, true); 176899ca880aSopenharmony_ci} 176999ca880aSopenharmony_ci 177099ca880aSopenharmony_cistatic int match_key(struct udev_rules *rules, struct token *token, const char *val) { 177199ca880aSopenharmony_ci char *key_value = rules_str(rules, token->key.value_off); 177299ca880aSopenharmony_ci char *pos; 177399ca880aSopenharmony_ci bool match = false; 177499ca880aSopenharmony_ci 177599ca880aSopenharmony_ci if (val == NULL) 177699ca880aSopenharmony_ci val = ""; 177799ca880aSopenharmony_ci 177899ca880aSopenharmony_ci switch (token->key.glob) { 177999ca880aSopenharmony_ci case GL_PLAIN: 178099ca880aSopenharmony_ci match = (streq(key_value, val)); 178199ca880aSopenharmony_ci break; 178299ca880aSopenharmony_ci case GL_GLOB: 178399ca880aSopenharmony_ci match = (fnmatch(key_value, val, 0) == 0); 178499ca880aSopenharmony_ci break; 178599ca880aSopenharmony_ci case GL_SPLIT: 178699ca880aSopenharmony_ci { 178799ca880aSopenharmony_ci const char *s; 178899ca880aSopenharmony_ci size_t len; 178999ca880aSopenharmony_ci 179099ca880aSopenharmony_ci s = rules_str(rules, token->key.value_off); 179199ca880aSopenharmony_ci len = strlen(val); 179299ca880aSopenharmony_ci for (;;) { 179399ca880aSopenharmony_ci const char *next; 179499ca880aSopenharmony_ci 179599ca880aSopenharmony_ci next = strchr(s, '|'); 179699ca880aSopenharmony_ci if (next != NULL) { 179799ca880aSopenharmony_ci size_t matchlen = (size_t)(next - s); 179899ca880aSopenharmony_ci 179999ca880aSopenharmony_ci match = (matchlen == len && strneq(s, val, matchlen)); 180099ca880aSopenharmony_ci if (match) 180199ca880aSopenharmony_ci break; 180299ca880aSopenharmony_ci } else { 180399ca880aSopenharmony_ci match = (streq(s, val)); 180499ca880aSopenharmony_ci break; 180599ca880aSopenharmony_ci } 180699ca880aSopenharmony_ci s = &next[1]; 180799ca880aSopenharmony_ci } 180899ca880aSopenharmony_ci break; 180999ca880aSopenharmony_ci } 181099ca880aSopenharmony_ci case GL_SPLIT_GLOB: 181199ca880aSopenharmony_ci { 181299ca880aSopenharmony_ci char value[UTIL_PATH_SIZE]; 181399ca880aSopenharmony_ci 181499ca880aSopenharmony_ci strscpy(value, sizeof(value), rules_str(rules, token->key.value_off)); 181599ca880aSopenharmony_ci key_value = value; 181699ca880aSopenharmony_ci while (key_value != NULL) { 181799ca880aSopenharmony_ci pos = strchr(key_value, '|'); 181899ca880aSopenharmony_ci if (pos != NULL) { 181999ca880aSopenharmony_ci pos[0] = '\0'; 182099ca880aSopenharmony_ci pos = &pos[1]; 182199ca880aSopenharmony_ci } 182299ca880aSopenharmony_ci match = (fnmatch(key_value, val, 0) == 0); 182399ca880aSopenharmony_ci if (match) 182499ca880aSopenharmony_ci break; 182599ca880aSopenharmony_ci key_value = pos; 182699ca880aSopenharmony_ci } 182799ca880aSopenharmony_ci break; 182899ca880aSopenharmony_ci } 182999ca880aSopenharmony_ci case GL_SOMETHING: 183099ca880aSopenharmony_ci match = (val[0] != '\0'); 183199ca880aSopenharmony_ci break; 183299ca880aSopenharmony_ci case GL_UNSET: 183399ca880aSopenharmony_ci return -1; 183499ca880aSopenharmony_ci } 183599ca880aSopenharmony_ci 183699ca880aSopenharmony_ci if (match && (token->key.op == OP_MATCH)) 183799ca880aSopenharmony_ci return 0; 183899ca880aSopenharmony_ci if (!match && (token->key.op == OP_NOMATCH)) 183999ca880aSopenharmony_ci return 0; 184099ca880aSopenharmony_ci return -1; 184199ca880aSopenharmony_ci} 184299ca880aSopenharmony_ci 184399ca880aSopenharmony_cistatic int match_attr(struct udev_rules *rules, struct udev_device *dev, struct udev_event *event, struct token *cur) { 184499ca880aSopenharmony_ci const char *name; 184599ca880aSopenharmony_ci char nbuf[UTIL_NAME_SIZE]; 184699ca880aSopenharmony_ci const char *value; 184799ca880aSopenharmony_ci char vbuf[UTIL_NAME_SIZE]; 184899ca880aSopenharmony_ci size_t len; 184999ca880aSopenharmony_ci 185099ca880aSopenharmony_ci name = rules_str(rules, cur->key.attr_off); 185199ca880aSopenharmony_ci switch (cur->key.attrsubst) { 185299ca880aSopenharmony_ci case SB_FORMAT: 185399ca880aSopenharmony_ci udev_event_apply_format(event, name, nbuf, sizeof(nbuf), false); 185499ca880aSopenharmony_ci name = nbuf; 185599ca880aSopenharmony_ci /* fall through */ 185699ca880aSopenharmony_ci case SB_NONE: 185799ca880aSopenharmony_ci value = udev_device_get_sysattr_value(dev, name); 185899ca880aSopenharmony_ci if (value == NULL) 185999ca880aSopenharmony_ci return -1; 186099ca880aSopenharmony_ci break; 186199ca880aSopenharmony_ci case SB_SUBSYS: 186299ca880aSopenharmony_ci if (util_resolve_subsys_kernel(event->udev, name, vbuf, sizeof(vbuf), 1) != 0) 186399ca880aSopenharmony_ci return -1; 186499ca880aSopenharmony_ci value = vbuf; 186599ca880aSopenharmony_ci break; 186699ca880aSopenharmony_ci default: 186799ca880aSopenharmony_ci return -1; 186899ca880aSopenharmony_ci } 186999ca880aSopenharmony_ci 187099ca880aSopenharmony_ci /* remove trailing whitespace, if not asked to match for it */ 187199ca880aSopenharmony_ci len = strlen(value); 187299ca880aSopenharmony_ci if (len > 0 && isspace(value[len-1])) { 187399ca880aSopenharmony_ci const char *key_value; 187499ca880aSopenharmony_ci size_t klen; 187599ca880aSopenharmony_ci 187699ca880aSopenharmony_ci key_value = rules_str(rules, cur->key.value_off); 187799ca880aSopenharmony_ci klen = strlen(key_value); 187899ca880aSopenharmony_ci if (klen > 0 && !isspace(key_value[klen-1])) { 187999ca880aSopenharmony_ci if (value != vbuf) { 188099ca880aSopenharmony_ci strscpy(vbuf, sizeof(vbuf), value); 188199ca880aSopenharmony_ci value = vbuf; 188299ca880aSopenharmony_ci } 188399ca880aSopenharmony_ci while (len > 0 && isspace(vbuf[--len])) 188499ca880aSopenharmony_ci vbuf[len] = '\0'; 188599ca880aSopenharmony_ci } 188699ca880aSopenharmony_ci } 188799ca880aSopenharmony_ci 188899ca880aSopenharmony_ci return match_key(rules, cur, value); 188999ca880aSopenharmony_ci} 189099ca880aSopenharmony_ci 189199ca880aSopenharmony_cienum escape_type { 189299ca880aSopenharmony_ci ESCAPE_UNSET, 189399ca880aSopenharmony_ci ESCAPE_NONE, 189499ca880aSopenharmony_ci ESCAPE_REPLACE, 189599ca880aSopenharmony_ci}; 189699ca880aSopenharmony_ci 189799ca880aSopenharmony_ciint udev_rules_apply_to_event(struct udev_rules *rules, 189899ca880aSopenharmony_ci struct udev_event *event, 189999ca880aSopenharmony_ci usec_t timeout_usec, 190099ca880aSopenharmony_ci usec_t timeout_warn_usec, 190199ca880aSopenharmony_ci struct udev_list *properties_list, 190299ca880aSopenharmony_ci const sigset_t *sigmask) { 190399ca880aSopenharmony_ci struct token *cur; 190499ca880aSopenharmony_ci struct token *rule; 190599ca880aSopenharmony_ci enum escape_type esc = ESCAPE_UNSET; 190699ca880aSopenharmony_ci bool can_set_name; 190799ca880aSopenharmony_ci 190899ca880aSopenharmony_ci if (rules->tokens == NULL) 190999ca880aSopenharmony_ci return -1; 191099ca880aSopenharmony_ci 191199ca880aSopenharmony_ci can_set_name = ((!streq(udev_device_get_action(event->dev), "remove")) && 191299ca880aSopenharmony_ci (major(udev_device_get_devnum(event->dev)) > 0 || 191399ca880aSopenharmony_ci udev_device_get_ifindex(event->dev) > 0)); 191499ca880aSopenharmony_ci 191599ca880aSopenharmony_ci /* loop through token list, match, run actions or forward to next rule */ 191699ca880aSopenharmony_ci cur = &rules->tokens[0]; 191799ca880aSopenharmony_ci rule = cur; 191899ca880aSopenharmony_ci for (;;) { 191999ca880aSopenharmony_ci dump_token(rules, cur); 192099ca880aSopenharmony_ci switch (cur->type) { 192199ca880aSopenharmony_ci case TK_RULE: 192299ca880aSopenharmony_ci /* current rule */ 192399ca880aSopenharmony_ci rule = cur; 192499ca880aSopenharmony_ci /* possibly skip rules which want to set NAME, SYMLINK, OWNER, GROUP, MODE */ 192599ca880aSopenharmony_ci if (!can_set_name && rule->rule.can_set_name) 192699ca880aSopenharmony_ci goto nomatch; 192799ca880aSopenharmony_ci esc = ESCAPE_UNSET; 192899ca880aSopenharmony_ci break; 192999ca880aSopenharmony_ci case TK_M_ACTION: 193099ca880aSopenharmony_ci if (match_key(rules, cur, udev_device_get_action(event->dev)) != 0) 193199ca880aSopenharmony_ci goto nomatch; 193299ca880aSopenharmony_ci break; 193399ca880aSopenharmony_ci case TK_M_DEVPATH: 193499ca880aSopenharmony_ci if (match_key(rules, cur, udev_device_get_devpath(event->dev)) != 0) 193599ca880aSopenharmony_ci goto nomatch; 193699ca880aSopenharmony_ci break; 193799ca880aSopenharmony_ci case TK_M_KERNEL: 193899ca880aSopenharmony_ci if (match_key(rules, cur, udev_device_get_sysname(event->dev)) != 0) 193999ca880aSopenharmony_ci goto nomatch; 194099ca880aSopenharmony_ci break; 194199ca880aSopenharmony_ci case TK_M_DEVLINK: { 194299ca880aSopenharmony_ci struct udev_list_entry *list_entry; 194399ca880aSopenharmony_ci bool match = false; 194499ca880aSopenharmony_ci 194599ca880aSopenharmony_ci udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(event->dev)) { 194699ca880aSopenharmony_ci const char *devlink; 194799ca880aSopenharmony_ci 194899ca880aSopenharmony_ci devlink = udev_list_entry_get_name(list_entry) + strlen("/dev/"); 194999ca880aSopenharmony_ci if (match_key(rules, cur, devlink) == 0) { 195099ca880aSopenharmony_ci match = true; 195199ca880aSopenharmony_ci break; 195299ca880aSopenharmony_ci } 195399ca880aSopenharmony_ci } 195499ca880aSopenharmony_ci if (!match) 195599ca880aSopenharmony_ci goto nomatch; 195699ca880aSopenharmony_ci break; 195799ca880aSopenharmony_ci } 195899ca880aSopenharmony_ci case TK_M_NAME: 195999ca880aSopenharmony_ci if (match_key(rules, cur, event->name) != 0) 196099ca880aSopenharmony_ci goto nomatch; 196199ca880aSopenharmony_ci break; 196299ca880aSopenharmony_ci case TK_M_ENV: { 196399ca880aSopenharmony_ci const char *key_name = rules_str(rules, cur->key.attr_off); 196499ca880aSopenharmony_ci const char *value; 196599ca880aSopenharmony_ci 196699ca880aSopenharmony_ci value = udev_device_get_property_value(event->dev, key_name); 196799ca880aSopenharmony_ci 196899ca880aSopenharmony_ci /* check global properties */ 196999ca880aSopenharmony_ci if (!value && properties_list) { 197099ca880aSopenharmony_ci struct udev_list_entry *list_entry; 197199ca880aSopenharmony_ci 197299ca880aSopenharmony_ci list_entry = udev_list_get_entry(properties_list); 197399ca880aSopenharmony_ci list_entry = udev_list_entry_get_by_name(list_entry, key_name); 197499ca880aSopenharmony_ci if (list_entry != NULL) 197599ca880aSopenharmony_ci value = udev_list_entry_get_value(list_entry); 197699ca880aSopenharmony_ci } 197799ca880aSopenharmony_ci 197899ca880aSopenharmony_ci if (!value) 197999ca880aSopenharmony_ci value = ""; 198099ca880aSopenharmony_ci if (match_key(rules, cur, value)) 198199ca880aSopenharmony_ci goto nomatch; 198299ca880aSopenharmony_ci break; 198399ca880aSopenharmony_ci } 198499ca880aSopenharmony_ci case TK_M_TAG: { 198599ca880aSopenharmony_ci struct udev_list_entry *list_entry; 198699ca880aSopenharmony_ci bool match = false; 198799ca880aSopenharmony_ci 198899ca880aSopenharmony_ci udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(event->dev)) { 198999ca880aSopenharmony_ci if (streq(rules_str(rules, cur->key.value_off), udev_list_entry_get_name(list_entry))) { 199099ca880aSopenharmony_ci match = true; 199199ca880aSopenharmony_ci break; 199299ca880aSopenharmony_ci } 199399ca880aSopenharmony_ci } 199499ca880aSopenharmony_ci if (!match && (cur->key.op != OP_NOMATCH)) 199599ca880aSopenharmony_ci goto nomatch; 199699ca880aSopenharmony_ci break; 199799ca880aSopenharmony_ci } 199899ca880aSopenharmony_ci case TK_M_SUBSYSTEM: 199999ca880aSopenharmony_ci if (match_key(rules, cur, udev_device_get_subsystem(event->dev)) != 0) 200099ca880aSopenharmony_ci goto nomatch; 200199ca880aSopenharmony_ci break; 200299ca880aSopenharmony_ci case TK_M_DRIVER: 200399ca880aSopenharmony_ci if (match_key(rules, cur, udev_device_get_driver(event->dev)) != 0) 200499ca880aSopenharmony_ci goto nomatch; 200599ca880aSopenharmony_ci break; 200699ca880aSopenharmony_ci case TK_M_WAITFOR: { 200799ca880aSopenharmony_ci char filename[UTIL_PATH_SIZE]; 200899ca880aSopenharmony_ci int found; 200999ca880aSopenharmony_ci 201099ca880aSopenharmony_ci udev_event_apply_format(event, rules_str(rules, cur->key.value_off), filename, sizeof(filename), false); 201199ca880aSopenharmony_ci found = (wait_for_file(event->dev, filename, 10) == 0); 201299ca880aSopenharmony_ci if (!found && (cur->key.op != OP_NOMATCH)) 201399ca880aSopenharmony_ci goto nomatch; 201499ca880aSopenharmony_ci break; 201599ca880aSopenharmony_ci } 201699ca880aSopenharmony_ci case TK_M_ATTR: 201799ca880aSopenharmony_ci if (match_attr(rules, event->dev, event, cur) != 0) 201899ca880aSopenharmony_ci goto nomatch; 201999ca880aSopenharmony_ci break; 202099ca880aSopenharmony_ci case TK_M_SYSCTL: { 202199ca880aSopenharmony_ci char filename[UTIL_PATH_SIZE]; 202299ca880aSopenharmony_ci _cleanup_free_ char *value = NULL; 202399ca880aSopenharmony_ci size_t len; 202499ca880aSopenharmony_ci 202599ca880aSopenharmony_ci udev_event_apply_format(event, rules_str(rules, cur->key.attr_off), filename, sizeof(filename), false); 202699ca880aSopenharmony_ci sysctl_normalize(filename); 202799ca880aSopenharmony_ci if (sysctl_read(filename, &value) < 0) 202899ca880aSopenharmony_ci goto nomatch; 202999ca880aSopenharmony_ci 203099ca880aSopenharmony_ci len = strlen(value); 203199ca880aSopenharmony_ci while (len > 0 && isspace(value[--len])) 203299ca880aSopenharmony_ci value[len] = '\0'; 203399ca880aSopenharmony_ci if (match_key(rules, cur, value) != 0) 203499ca880aSopenharmony_ci goto nomatch; 203599ca880aSopenharmony_ci break; 203699ca880aSopenharmony_ci } 203799ca880aSopenharmony_ci case TK_M_KERNELS: 203899ca880aSopenharmony_ci case TK_M_SUBSYSTEMS: 203999ca880aSopenharmony_ci case TK_M_DRIVERS: 204099ca880aSopenharmony_ci case TK_M_ATTRS: 204199ca880aSopenharmony_ci case TK_M_TAGS: { 204299ca880aSopenharmony_ci struct token *next; 204399ca880aSopenharmony_ci 204499ca880aSopenharmony_ci /* get whole sequence of parent matches */ 204599ca880aSopenharmony_ci next = cur; 204699ca880aSopenharmony_ci while (next->type > TK_M_PARENTS_MIN && next->type < TK_M_PARENTS_MAX) 204799ca880aSopenharmony_ci next++; 204899ca880aSopenharmony_ci 204999ca880aSopenharmony_ci /* loop over parents */ 205099ca880aSopenharmony_ci event->dev_parent = event->dev; 205199ca880aSopenharmony_ci for (;;) { 205299ca880aSopenharmony_ci struct token *key; 205399ca880aSopenharmony_ci 205499ca880aSopenharmony_ci /* loop over sequence of parent match keys */ 205599ca880aSopenharmony_ci for (key = cur; key < next; key++ ) { 205699ca880aSopenharmony_ci dump_token(rules, key); 205799ca880aSopenharmony_ci switch(key->type) { 205899ca880aSopenharmony_ci case TK_M_KERNELS: 205999ca880aSopenharmony_ci if (match_key(rules, key, udev_device_get_sysname(event->dev_parent)) != 0) 206099ca880aSopenharmony_ci goto try_parent; 206199ca880aSopenharmony_ci break; 206299ca880aSopenharmony_ci case TK_M_SUBSYSTEMS: 206399ca880aSopenharmony_ci if (match_key(rules, key, udev_device_get_subsystem(event->dev_parent)) != 0) 206499ca880aSopenharmony_ci goto try_parent; 206599ca880aSopenharmony_ci break; 206699ca880aSopenharmony_ci case TK_M_DRIVERS: 206799ca880aSopenharmony_ci if (match_key(rules, key, udev_device_get_driver(event->dev_parent)) != 0) 206899ca880aSopenharmony_ci goto try_parent; 206999ca880aSopenharmony_ci break; 207099ca880aSopenharmony_ci case TK_M_ATTRS: 207199ca880aSopenharmony_ci if (match_attr(rules, event->dev_parent, event, key) != 0) 207299ca880aSopenharmony_ci goto try_parent; 207399ca880aSopenharmony_ci break; 207499ca880aSopenharmony_ci case TK_M_TAGS: { 207599ca880aSopenharmony_ci bool match = udev_device_has_tag(event->dev_parent, rules_str(rules, cur->key.value_off)); 207699ca880aSopenharmony_ci 207799ca880aSopenharmony_ci if (match && key->key.op == OP_NOMATCH) 207899ca880aSopenharmony_ci goto try_parent; 207999ca880aSopenharmony_ci if (!match && key->key.op == OP_MATCH) 208099ca880aSopenharmony_ci goto try_parent; 208199ca880aSopenharmony_ci break; 208299ca880aSopenharmony_ci } 208399ca880aSopenharmony_ci default: 208499ca880aSopenharmony_ci goto nomatch; 208599ca880aSopenharmony_ci } 208699ca880aSopenharmony_ci } 208799ca880aSopenharmony_ci break; 208899ca880aSopenharmony_ci 208999ca880aSopenharmony_ci try_parent: 209099ca880aSopenharmony_ci event->dev_parent = udev_device_get_parent(event->dev_parent); 209199ca880aSopenharmony_ci if (event->dev_parent == NULL) 209299ca880aSopenharmony_ci goto nomatch; 209399ca880aSopenharmony_ci } 209499ca880aSopenharmony_ci /* move behind our sequence of parent match keys */ 209599ca880aSopenharmony_ci cur = next; 209699ca880aSopenharmony_ci continue; 209799ca880aSopenharmony_ci } 209899ca880aSopenharmony_ci case TK_M_TEST: { 209999ca880aSopenharmony_ci char filename[UTIL_PATH_SIZE]; 210099ca880aSopenharmony_ci struct stat statbuf; 210199ca880aSopenharmony_ci int match; 210299ca880aSopenharmony_ci 210399ca880aSopenharmony_ci udev_event_apply_format(event, rules_str(rules, cur->key.value_off), filename, sizeof(filename), false); 210499ca880aSopenharmony_ci if (util_resolve_subsys_kernel(event->udev, filename, filename, sizeof(filename), 0) != 0) { 210599ca880aSopenharmony_ci if (filename[0] != '/') { 210699ca880aSopenharmony_ci char tmp[UTIL_PATH_SIZE]; 210799ca880aSopenharmony_ci 210899ca880aSopenharmony_ci strscpy(tmp, sizeof(tmp), filename); 210999ca880aSopenharmony_ci strscpyl(filename, sizeof(filename), 211099ca880aSopenharmony_ci udev_device_get_syspath(event->dev), "/", tmp, NULL); 211199ca880aSopenharmony_ci } 211299ca880aSopenharmony_ci } 211399ca880aSopenharmony_ci attr_subst_subdir(filename, sizeof(filename)); 211499ca880aSopenharmony_ci 211599ca880aSopenharmony_ci match = (stat(filename, &statbuf) == 0); 211699ca880aSopenharmony_ci if (match && cur->key.mode > 0) 211799ca880aSopenharmony_ci match = ((statbuf.st_mode & cur->key.mode) > 0); 211899ca880aSopenharmony_ci if (match && cur->key.op == OP_NOMATCH) 211999ca880aSopenharmony_ci goto nomatch; 212099ca880aSopenharmony_ci if (!match && cur->key.op == OP_MATCH) 212199ca880aSopenharmony_ci goto nomatch; 212299ca880aSopenharmony_ci break; 212399ca880aSopenharmony_ci } 212499ca880aSopenharmony_ci case TK_M_PROGRAM: { 212599ca880aSopenharmony_ci char program[UTIL_PATH_SIZE]; 212699ca880aSopenharmony_ci char **envp; 212799ca880aSopenharmony_ci char result[UTIL_LINE_SIZE]; 212899ca880aSopenharmony_ci 212999ca880aSopenharmony_ci free(event->program_result); 213099ca880aSopenharmony_ci event->program_result = NULL; 213199ca880aSopenharmony_ci udev_event_apply_format(event, rules_str(rules, cur->key.value_off), program, sizeof(program), false); 213299ca880aSopenharmony_ci envp = udev_device_get_properties_envp(event->dev); 213399ca880aSopenharmony_ci log_debug("PROGRAM '%s' %s:%u", 213499ca880aSopenharmony_ci program, 213599ca880aSopenharmony_ci rules_str(rules, rule->rule.filename_off), 213699ca880aSopenharmony_ci rule->rule.filename_line); 213799ca880aSopenharmony_ci 213899ca880aSopenharmony_ci if (udev_event_spawn(event, timeout_usec, timeout_warn_usec, program, envp, sigmask, result, sizeof(result)) < 0) { 213999ca880aSopenharmony_ci if (cur->key.op != OP_NOMATCH) 214099ca880aSopenharmony_ci goto nomatch; 214199ca880aSopenharmony_ci } else { 214299ca880aSopenharmony_ci int count; 214399ca880aSopenharmony_ci 214499ca880aSopenharmony_ci util_remove_trailing_chars(result, '\n'); 214599ca880aSopenharmony_ci if (esc == ESCAPE_UNSET || esc == ESCAPE_REPLACE) { 214699ca880aSopenharmony_ci count = util_replace_chars(result, UDEV_ALLOWED_CHARS_INPUT); 214799ca880aSopenharmony_ci if (count > 0) 214899ca880aSopenharmony_ci log_debug("%i character(s) replaced" , count); 214999ca880aSopenharmony_ci } 215099ca880aSopenharmony_ci event->program_result = strdup(result); 215199ca880aSopenharmony_ci if (cur->key.op == OP_NOMATCH) 215299ca880aSopenharmony_ci goto nomatch; 215399ca880aSopenharmony_ci } 215499ca880aSopenharmony_ci break; 215599ca880aSopenharmony_ci } 215699ca880aSopenharmony_ci case TK_M_IMPORT_FILE: { 215799ca880aSopenharmony_ci char import[UTIL_PATH_SIZE]; 215899ca880aSopenharmony_ci 215999ca880aSopenharmony_ci udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import), false); 216099ca880aSopenharmony_ci if (import_file_into_properties(event->dev, import) != 0) 216199ca880aSopenharmony_ci if (cur->key.op != OP_NOMATCH) 216299ca880aSopenharmony_ci goto nomatch; 216399ca880aSopenharmony_ci break; 216499ca880aSopenharmony_ci } 216599ca880aSopenharmony_ci case TK_M_IMPORT_PROG: { 216699ca880aSopenharmony_ci char import[UTIL_PATH_SIZE]; 216799ca880aSopenharmony_ci 216899ca880aSopenharmony_ci udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import), false); 216999ca880aSopenharmony_ci log_debug("IMPORT '%s' %s:%u", 217099ca880aSopenharmony_ci import, 217199ca880aSopenharmony_ci rules_str(rules, rule->rule.filename_off), 217299ca880aSopenharmony_ci rule->rule.filename_line); 217399ca880aSopenharmony_ci 217499ca880aSopenharmony_ci if (import_program_into_properties(event, timeout_usec, timeout_warn_usec, import, sigmask) != 0) 217599ca880aSopenharmony_ci if (cur->key.op != OP_NOMATCH) 217699ca880aSopenharmony_ci goto nomatch; 217799ca880aSopenharmony_ci break; 217899ca880aSopenharmony_ci } 217999ca880aSopenharmony_ci case TK_M_IMPORT_BUILTIN: { 218099ca880aSopenharmony_ci char command[UTIL_PATH_SIZE]; 218199ca880aSopenharmony_ci 218299ca880aSopenharmony_ci if (udev_builtin_run_once(cur->key.builtin_cmd)) { 218399ca880aSopenharmony_ci /* check if we ran already */ 218499ca880aSopenharmony_ci if (event->builtin_run & (1 << cur->key.builtin_cmd)) { 218599ca880aSopenharmony_ci log_debug("IMPORT builtin skip '%s' %s:%u", 218699ca880aSopenharmony_ci udev_builtin_name(cur->key.builtin_cmd), 218799ca880aSopenharmony_ci rules_str(rules, rule->rule.filename_off), 218899ca880aSopenharmony_ci rule->rule.filename_line); 218999ca880aSopenharmony_ci /* return the result from earlier run */ 219099ca880aSopenharmony_ci if (event->builtin_ret & (1 << cur->key.builtin_cmd)) 219199ca880aSopenharmony_ci if (cur->key.op != OP_NOMATCH) 219299ca880aSopenharmony_ci goto nomatch; 219399ca880aSopenharmony_ci break; 219499ca880aSopenharmony_ci } 219599ca880aSopenharmony_ci /* mark as ran */ 219699ca880aSopenharmony_ci event->builtin_run |= (1 << cur->key.builtin_cmd); 219799ca880aSopenharmony_ci } 219899ca880aSopenharmony_ci 219999ca880aSopenharmony_ci udev_event_apply_format(event, rules_str(rules, cur->key.value_off), command, sizeof(command), false); 220099ca880aSopenharmony_ci log_debug("IMPORT builtin '%s' %s:%u", 220199ca880aSopenharmony_ci udev_builtin_name(cur->key.builtin_cmd), 220299ca880aSopenharmony_ci rules_str(rules, rule->rule.filename_off), 220399ca880aSopenharmony_ci rule->rule.filename_line); 220499ca880aSopenharmony_ci 220599ca880aSopenharmony_ci if (udev_builtin_run(event->dev, cur->key.builtin_cmd, command, false) != 0) { 220699ca880aSopenharmony_ci /* remember failure */ 220799ca880aSopenharmony_ci log_debug("IMPORT builtin '%s' returned non-zero", 220899ca880aSopenharmony_ci udev_builtin_name(cur->key.builtin_cmd)); 220999ca880aSopenharmony_ci event->builtin_ret |= (1 << cur->key.builtin_cmd); 221099ca880aSopenharmony_ci if (cur->key.op != OP_NOMATCH) 221199ca880aSopenharmony_ci goto nomatch; 221299ca880aSopenharmony_ci } 221399ca880aSopenharmony_ci break; 221499ca880aSopenharmony_ci } 221599ca880aSopenharmony_ci case TK_M_IMPORT_DB: { 221699ca880aSopenharmony_ci const char *key = rules_str(rules, cur->key.value_off); 221799ca880aSopenharmony_ci const char *value; 221899ca880aSopenharmony_ci 221999ca880aSopenharmony_ci value = udev_device_get_property_value(event->dev_db, key); 222099ca880aSopenharmony_ci if (value != NULL) 222199ca880aSopenharmony_ci udev_device_add_property(event->dev, key, value); 222299ca880aSopenharmony_ci else { 222399ca880aSopenharmony_ci if (cur->key.op != OP_NOMATCH) 222499ca880aSopenharmony_ci goto nomatch; 222599ca880aSopenharmony_ci } 222699ca880aSopenharmony_ci break; 222799ca880aSopenharmony_ci } 222899ca880aSopenharmony_ci case TK_M_IMPORT_CMDLINE: { 222999ca880aSopenharmony_ci FILE *f; 223099ca880aSopenharmony_ci bool imported = false; 223199ca880aSopenharmony_ci 223299ca880aSopenharmony_ci f = fopen("/proc/cmdline", "re"); 223399ca880aSopenharmony_ci if (f != NULL) { 223499ca880aSopenharmony_ci char cmdline[4096]; 223599ca880aSopenharmony_ci 223699ca880aSopenharmony_ci if (fgets(cmdline, sizeof(cmdline), f) != NULL) { 223799ca880aSopenharmony_ci const char *key = rules_str(rules, cur->key.value_off); 223899ca880aSopenharmony_ci char *pos; 223999ca880aSopenharmony_ci 224099ca880aSopenharmony_ci pos = strstr(cmdline, key); 224199ca880aSopenharmony_ci if (pos != NULL) { 224299ca880aSopenharmony_ci pos += strlen(key); 224399ca880aSopenharmony_ci if (pos[0] == '\0' || isspace(pos[0])) { 224499ca880aSopenharmony_ci /* we import simple flags as 'FLAG=1' */ 224599ca880aSopenharmony_ci udev_device_add_property(event->dev, key, "1"); 224699ca880aSopenharmony_ci imported = true; 224799ca880aSopenharmony_ci } else if (pos[0] == '=') { 224899ca880aSopenharmony_ci const char *value; 224999ca880aSopenharmony_ci 225099ca880aSopenharmony_ci pos++; 225199ca880aSopenharmony_ci value = pos; 225299ca880aSopenharmony_ci while (pos[0] != '\0' && !isspace(pos[0])) 225399ca880aSopenharmony_ci pos++; 225499ca880aSopenharmony_ci pos[0] = '\0'; 225599ca880aSopenharmony_ci udev_device_add_property(event->dev, key, value); 225699ca880aSopenharmony_ci imported = true; 225799ca880aSopenharmony_ci } 225899ca880aSopenharmony_ci } 225999ca880aSopenharmony_ci } 226099ca880aSopenharmony_ci fclose(f); 226199ca880aSopenharmony_ci } 226299ca880aSopenharmony_ci if (!imported && cur->key.op != OP_NOMATCH) 226399ca880aSopenharmony_ci goto nomatch; 226499ca880aSopenharmony_ci break; 226599ca880aSopenharmony_ci } 226699ca880aSopenharmony_ci case TK_M_IMPORT_PARENT: { 226799ca880aSopenharmony_ci char import[UTIL_PATH_SIZE]; 226899ca880aSopenharmony_ci 226999ca880aSopenharmony_ci udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import), false); 227099ca880aSopenharmony_ci if (import_parent_into_properties(event->dev, import) != 0) 227199ca880aSopenharmony_ci if (cur->key.op != OP_NOMATCH) 227299ca880aSopenharmony_ci goto nomatch; 227399ca880aSopenharmony_ci break; 227499ca880aSopenharmony_ci } 227599ca880aSopenharmony_ci case TK_M_RESULT: 227699ca880aSopenharmony_ci if (match_key(rules, cur, event->program_result) != 0) 227799ca880aSopenharmony_ci goto nomatch; 227899ca880aSopenharmony_ci break; 227999ca880aSopenharmony_ci case TK_A_STRING_ESCAPE_NONE: 228099ca880aSopenharmony_ci esc = ESCAPE_NONE; 228199ca880aSopenharmony_ci break; 228299ca880aSopenharmony_ci case TK_A_STRING_ESCAPE_REPLACE: 228399ca880aSopenharmony_ci esc = ESCAPE_REPLACE; 228499ca880aSopenharmony_ci break; 228599ca880aSopenharmony_ci case TK_A_DB_PERSIST: 228699ca880aSopenharmony_ci udev_device_set_db_persist(event->dev); 228799ca880aSopenharmony_ci break; 228899ca880aSopenharmony_ci case TK_A_INOTIFY_WATCH: 228999ca880aSopenharmony_ci if (event->inotify_watch_final) 229099ca880aSopenharmony_ci break; 229199ca880aSopenharmony_ci if (cur->key.op == OP_ASSIGN_FINAL) 229299ca880aSopenharmony_ci event->inotify_watch_final = true; 229399ca880aSopenharmony_ci event->inotify_watch = cur->key.watch; 229499ca880aSopenharmony_ci break; 229599ca880aSopenharmony_ci case TK_A_DEVLINK_PRIO: 229699ca880aSopenharmony_ci udev_device_set_devlink_priority(event->dev, cur->key.devlink_prio); 229799ca880aSopenharmony_ci break; 229899ca880aSopenharmony_ci case TK_A_OWNER: { 229999ca880aSopenharmony_ci char owner[UTIL_NAME_SIZE]; 230099ca880aSopenharmony_ci const char *ow = owner; 230199ca880aSopenharmony_ci int r; 230299ca880aSopenharmony_ci 230399ca880aSopenharmony_ci if (event->owner_final) 230499ca880aSopenharmony_ci break; 230599ca880aSopenharmony_ci if (cur->key.op == OP_ASSIGN_FINAL) 230699ca880aSopenharmony_ci event->owner_final = true; 230799ca880aSopenharmony_ci udev_event_apply_format(event, rules_str(rules, cur->key.value_off), owner, sizeof(owner), false); 230899ca880aSopenharmony_ci event->owner_set = true; 230999ca880aSopenharmony_ci r = get_user_creds(&ow, &event->uid, NULL, NULL, NULL); 231099ca880aSopenharmony_ci if (r < 0) { 231199ca880aSopenharmony_ci if (r == -ENOENT || r == -ESRCH) 231299ca880aSopenharmony_ci log_error("specified user '%s' unknown", owner); 231399ca880aSopenharmony_ci else 231499ca880aSopenharmony_ci log_error_errno(r, "error resolving user '%s': %m", owner); 231599ca880aSopenharmony_ci 231699ca880aSopenharmony_ci event->uid = 0; 231799ca880aSopenharmony_ci } 231899ca880aSopenharmony_ci log_debug("OWNER %u %s:%u", 231999ca880aSopenharmony_ci event->uid, 232099ca880aSopenharmony_ci rules_str(rules, rule->rule.filename_off), 232199ca880aSopenharmony_ci rule->rule.filename_line); 232299ca880aSopenharmony_ci break; 232399ca880aSopenharmony_ci } 232499ca880aSopenharmony_ci case TK_A_GROUP: { 232599ca880aSopenharmony_ci char group[UTIL_NAME_SIZE]; 232699ca880aSopenharmony_ci const char *gr = group; 232799ca880aSopenharmony_ci int r; 232899ca880aSopenharmony_ci 232999ca880aSopenharmony_ci if (event->group_final) 233099ca880aSopenharmony_ci break; 233199ca880aSopenharmony_ci if (cur->key.op == OP_ASSIGN_FINAL) 233299ca880aSopenharmony_ci event->group_final = true; 233399ca880aSopenharmony_ci udev_event_apply_format(event, rules_str(rules, cur->key.value_off), group, sizeof(group), false); 233499ca880aSopenharmony_ci event->group_set = true; 233599ca880aSopenharmony_ci r = get_group_creds(&gr, &event->gid); 233699ca880aSopenharmony_ci if (r < 0) { 233799ca880aSopenharmony_ci if (r == -ENOENT || r == -ESRCH) 233899ca880aSopenharmony_ci log_error("specified group '%s' unknown", group); 233999ca880aSopenharmony_ci else 234099ca880aSopenharmony_ci log_error_errno(r, "error resolving group '%s': %m", group); 234199ca880aSopenharmony_ci 234299ca880aSopenharmony_ci event->gid = 0; 234399ca880aSopenharmony_ci } 234499ca880aSopenharmony_ci log_debug("GROUP %u %s:%u", 234599ca880aSopenharmony_ci event->gid, 234699ca880aSopenharmony_ci rules_str(rules, rule->rule.filename_off), 234799ca880aSopenharmony_ci rule->rule.filename_line); 234899ca880aSopenharmony_ci break; 234999ca880aSopenharmony_ci } 235099ca880aSopenharmony_ci case TK_A_MODE: { 235199ca880aSopenharmony_ci char mode_str[UTIL_NAME_SIZE]; 235299ca880aSopenharmony_ci mode_t mode; 235399ca880aSopenharmony_ci char *endptr; 235499ca880aSopenharmony_ci 235599ca880aSopenharmony_ci if (event->mode_final) 235699ca880aSopenharmony_ci break; 235799ca880aSopenharmony_ci udev_event_apply_format(event, rules_str(rules, cur->key.value_off), mode_str, sizeof(mode_str), false); 235899ca880aSopenharmony_ci mode = strtol(mode_str, &endptr, 8); 235999ca880aSopenharmony_ci if (endptr[0] != '\0') { 236099ca880aSopenharmony_ci log_error("ignoring invalid mode '%s'", mode_str); 236199ca880aSopenharmony_ci break; 236299ca880aSopenharmony_ci } 236399ca880aSopenharmony_ci if (cur->key.op == OP_ASSIGN_FINAL) 236499ca880aSopenharmony_ci event->mode_final = true; 236599ca880aSopenharmony_ci event->mode_set = true; 236699ca880aSopenharmony_ci event->mode = mode; 236799ca880aSopenharmony_ci log_debug("MODE %#o %s:%u", 236899ca880aSopenharmony_ci event->mode, 236999ca880aSopenharmony_ci rules_str(rules, rule->rule.filename_off), 237099ca880aSopenharmony_ci rule->rule.filename_line); 237199ca880aSopenharmony_ci break; 237299ca880aSopenharmony_ci } 237399ca880aSopenharmony_ci case TK_A_OWNER_ID: 237499ca880aSopenharmony_ci if (event->owner_final) 237599ca880aSopenharmony_ci break; 237699ca880aSopenharmony_ci if (cur->key.op == OP_ASSIGN_FINAL) 237799ca880aSopenharmony_ci event->owner_final = true; 237899ca880aSopenharmony_ci event->owner_set = true; 237999ca880aSopenharmony_ci event->uid = cur->key.uid; 238099ca880aSopenharmony_ci log_debug("OWNER %u %s:%u", 238199ca880aSopenharmony_ci event->uid, 238299ca880aSopenharmony_ci rules_str(rules, rule->rule.filename_off), 238399ca880aSopenharmony_ci rule->rule.filename_line); 238499ca880aSopenharmony_ci break; 238599ca880aSopenharmony_ci case TK_A_GROUP_ID: 238699ca880aSopenharmony_ci if (event->group_final) 238799ca880aSopenharmony_ci break; 238899ca880aSopenharmony_ci if (cur->key.op == OP_ASSIGN_FINAL) 238999ca880aSopenharmony_ci event->group_final = true; 239099ca880aSopenharmony_ci event->group_set = true; 239199ca880aSopenharmony_ci event->gid = cur->key.gid; 239299ca880aSopenharmony_ci log_debug("GROUP %u %s:%u", 239399ca880aSopenharmony_ci event->gid, 239499ca880aSopenharmony_ci rules_str(rules, rule->rule.filename_off), 239599ca880aSopenharmony_ci rule->rule.filename_line); 239699ca880aSopenharmony_ci break; 239799ca880aSopenharmony_ci case TK_A_MODE_ID: 239899ca880aSopenharmony_ci if (event->mode_final) 239999ca880aSopenharmony_ci break; 240099ca880aSopenharmony_ci if (cur->key.op == OP_ASSIGN_FINAL) 240199ca880aSopenharmony_ci event->mode_final = true; 240299ca880aSopenharmony_ci event->mode_set = true; 240399ca880aSopenharmony_ci event->mode = cur->key.mode; 240499ca880aSopenharmony_ci log_debug("MODE %#o %s:%u", 240599ca880aSopenharmony_ci event->mode, 240699ca880aSopenharmony_ci rules_str(rules, rule->rule.filename_off), 240799ca880aSopenharmony_ci rule->rule.filename_line); 240899ca880aSopenharmony_ci break; 240999ca880aSopenharmony_ci case TK_A_SECLABEL: { 241099ca880aSopenharmony_ci const char *name, *label; 241199ca880aSopenharmony_ci 241299ca880aSopenharmony_ci name = rules_str(rules, cur->key.attr_off); 241399ca880aSopenharmony_ci label = rules_str(rules, cur->key.value_off); 241499ca880aSopenharmony_ci if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) 241599ca880aSopenharmony_ci udev_list_cleanup(&event->seclabel_list); 241699ca880aSopenharmony_ci udev_list_entry_add(&event->seclabel_list, name, label); 241799ca880aSopenharmony_ci log_debug("SECLABEL{%s}='%s' %s:%u", 241899ca880aSopenharmony_ci name, label, 241999ca880aSopenharmony_ci rules_str(rules, rule->rule.filename_off), 242099ca880aSopenharmony_ci rule->rule.filename_line); 242199ca880aSopenharmony_ci break; 242299ca880aSopenharmony_ci } 242399ca880aSopenharmony_ci case TK_A_ENV: { 242499ca880aSopenharmony_ci const char *name = rules_str(rules, cur->key.attr_off); 242599ca880aSopenharmony_ci char *value = rules_str(rules, cur->key.value_off); 242699ca880aSopenharmony_ci char value_new[UTIL_NAME_SIZE]; 242799ca880aSopenharmony_ci const char *value_old = NULL; 242899ca880aSopenharmony_ci 242999ca880aSopenharmony_ci if (value[0] == '\0') { 243099ca880aSopenharmony_ci if (cur->key.op == OP_ADD) 243199ca880aSopenharmony_ci break; 243299ca880aSopenharmony_ci udev_device_add_property(event->dev, name, NULL); 243399ca880aSopenharmony_ci break; 243499ca880aSopenharmony_ci } 243599ca880aSopenharmony_ci 243699ca880aSopenharmony_ci if (cur->key.op == OP_ADD) 243799ca880aSopenharmony_ci value_old = udev_device_get_property_value(event->dev, name); 243899ca880aSopenharmony_ci if (value_old) { 243999ca880aSopenharmony_ci char temp[UTIL_NAME_SIZE]; 244099ca880aSopenharmony_ci 244199ca880aSopenharmony_ci /* append value separated by space */ 244299ca880aSopenharmony_ci udev_event_apply_format(event, value, temp, sizeof(temp), false); 244399ca880aSopenharmony_ci strscpyl(value_new, sizeof(value_new), value_old, " ", temp, NULL); 244499ca880aSopenharmony_ci } else 244599ca880aSopenharmony_ci udev_event_apply_format(event, value, value_new, sizeof(value_new), false); 244699ca880aSopenharmony_ci 244799ca880aSopenharmony_ci udev_device_add_property(event->dev, name, value_new); 244899ca880aSopenharmony_ci break; 244999ca880aSopenharmony_ci } 245099ca880aSopenharmony_ci case TK_A_TAG: { 245199ca880aSopenharmony_ci char tag[UTIL_PATH_SIZE]; 245299ca880aSopenharmony_ci const char *p; 245399ca880aSopenharmony_ci 245499ca880aSopenharmony_ci udev_event_apply_format(event, rules_str(rules, cur->key.value_off), tag, sizeof(tag), false); 245599ca880aSopenharmony_ci if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) 245699ca880aSopenharmony_ci udev_device_cleanup_tags_list(event->dev); 245799ca880aSopenharmony_ci for (p = tag; *p != '\0'; p++) { 245899ca880aSopenharmony_ci if ((*p >= 'a' && *p <= 'z') || 245999ca880aSopenharmony_ci (*p >= 'A' && *p <= 'Z') || 246099ca880aSopenharmony_ci (*p >= '0' && *p <= '9') || 246199ca880aSopenharmony_ci *p == '-' || *p == '_') 246299ca880aSopenharmony_ci continue; 246399ca880aSopenharmony_ci log_error("ignoring invalid tag name '%s'", tag); 246499ca880aSopenharmony_ci break; 246599ca880aSopenharmony_ci } 246699ca880aSopenharmony_ci if (cur->key.op == OP_REMOVE) 246799ca880aSopenharmony_ci udev_device_remove_tag(event->dev, tag); 246899ca880aSopenharmony_ci else 246999ca880aSopenharmony_ci udev_device_add_tag(event->dev, tag); 247099ca880aSopenharmony_ci break; 247199ca880aSopenharmony_ci } 247299ca880aSopenharmony_ci case TK_A_NAME: { 247399ca880aSopenharmony_ci const char *name = rules_str(rules, cur->key.value_off); 247499ca880aSopenharmony_ci 247599ca880aSopenharmony_ci char name_str[UTIL_PATH_SIZE]; 247699ca880aSopenharmony_ci int count; 247799ca880aSopenharmony_ci 247899ca880aSopenharmony_ci if (event->name_final) 247999ca880aSopenharmony_ci break; 248099ca880aSopenharmony_ci if (cur->key.op == OP_ASSIGN_FINAL) 248199ca880aSopenharmony_ci event->name_final = true; 248299ca880aSopenharmony_ci udev_event_apply_format(event, name, name_str, sizeof(name_str), false); 248399ca880aSopenharmony_ci if (esc == ESCAPE_UNSET || esc == ESCAPE_REPLACE) { 248499ca880aSopenharmony_ci count = util_replace_chars(name_str, "/"); 248599ca880aSopenharmony_ci if (count > 0) 248699ca880aSopenharmony_ci log_debug("%i character(s) replaced", count); 248799ca880aSopenharmony_ci } 248899ca880aSopenharmony_ci if (major(udev_device_get_devnum(event->dev)) && 248999ca880aSopenharmony_ci (!streq(name_str, udev_device_get_devnode(event->dev) + strlen("/dev/")))) { 249099ca880aSopenharmony_ci log_error("NAME=\"%s\" ignored, kernel device nodes " 249199ca880aSopenharmony_ci "can not be renamed; please fix it in %s:%u\n", name, 249299ca880aSopenharmony_ci rules_str(rules, rule->rule.filename_off), rule->rule.filename_line); 249399ca880aSopenharmony_ci break; 249499ca880aSopenharmony_ci } 249599ca880aSopenharmony_ci free(event->name); 249699ca880aSopenharmony_ci event->name = strdup(name_str); 249799ca880aSopenharmony_ci log_debug("NAME '%s' %s:%u", 249899ca880aSopenharmony_ci event->name, 249999ca880aSopenharmony_ci rules_str(rules, rule->rule.filename_off), 250099ca880aSopenharmony_ci rule->rule.filename_line); 250199ca880aSopenharmony_ci break; 250299ca880aSopenharmony_ci } 250399ca880aSopenharmony_ci case TK_A_DEVLINK: { 250499ca880aSopenharmony_ci char temp[UTIL_PATH_SIZE]; 250599ca880aSopenharmony_ci char filename[UTIL_PATH_SIZE]; 250699ca880aSopenharmony_ci char *pos, *next; 250799ca880aSopenharmony_ci int count = 0; 250899ca880aSopenharmony_ci 250999ca880aSopenharmony_ci if (event->devlink_final) 251099ca880aSopenharmony_ci break; 251199ca880aSopenharmony_ci if (major(udev_device_get_devnum(event->dev)) == 0) 251299ca880aSopenharmony_ci break; 251399ca880aSopenharmony_ci if (cur->key.op == OP_ASSIGN_FINAL) 251499ca880aSopenharmony_ci event->devlink_final = true; 251599ca880aSopenharmony_ci if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) 251699ca880aSopenharmony_ci udev_device_cleanup_devlinks_list(event->dev); 251799ca880aSopenharmony_ci 251899ca880aSopenharmony_ci /* allow multiple symlinks separated by spaces */ 251999ca880aSopenharmony_ci udev_event_apply_format(event, rules_str(rules, cur->key.value_off), temp, sizeof(temp), esc != ESCAPE_NONE); 252099ca880aSopenharmony_ci if (esc == ESCAPE_UNSET) 252199ca880aSopenharmony_ci count = util_replace_chars(temp, "/ "); 252299ca880aSopenharmony_ci else if (esc == ESCAPE_REPLACE) 252399ca880aSopenharmony_ci count = util_replace_chars(temp, "/"); 252499ca880aSopenharmony_ci if (count > 0) 252599ca880aSopenharmony_ci log_debug("%i character(s) replaced" , count); 252699ca880aSopenharmony_ci pos = temp; 252799ca880aSopenharmony_ci while (isspace(pos[0])) 252899ca880aSopenharmony_ci pos++; 252999ca880aSopenharmony_ci next = strchr(pos, ' '); 253099ca880aSopenharmony_ci while (next != NULL) { 253199ca880aSopenharmony_ci next[0] = '\0'; 253299ca880aSopenharmony_ci log_debug("LINK '%s' %s:%u", pos, 253399ca880aSopenharmony_ci rules_str(rules, rule->rule.filename_off), rule->rule.filename_line); 253499ca880aSopenharmony_ci strscpyl(filename, sizeof(filename), "/dev/", pos, NULL); 253599ca880aSopenharmony_ci udev_device_add_devlink(event->dev, filename); 253699ca880aSopenharmony_ci while (isspace(next[1])) 253799ca880aSopenharmony_ci next++; 253899ca880aSopenharmony_ci pos = &next[1]; 253999ca880aSopenharmony_ci next = strchr(pos, ' '); 254099ca880aSopenharmony_ci } 254199ca880aSopenharmony_ci if (pos[0] != '\0') { 254299ca880aSopenharmony_ci log_debug("LINK '%s' %s:%u", pos, 254399ca880aSopenharmony_ci rules_str(rules, rule->rule.filename_off), rule->rule.filename_line); 254499ca880aSopenharmony_ci strscpyl(filename, sizeof(filename), "/dev/", pos, NULL); 254599ca880aSopenharmony_ci udev_device_add_devlink(event->dev, filename); 254699ca880aSopenharmony_ci } 254799ca880aSopenharmony_ci break; 254899ca880aSopenharmony_ci } 254999ca880aSopenharmony_ci case TK_A_ATTR: { 255099ca880aSopenharmony_ci const char *key_name = rules_str(rules, cur->key.attr_off); 255199ca880aSopenharmony_ci char attr[UTIL_PATH_SIZE]; 255299ca880aSopenharmony_ci char value[UTIL_NAME_SIZE]; 255399ca880aSopenharmony_ci FILE *f; 255499ca880aSopenharmony_ci 255599ca880aSopenharmony_ci if (util_resolve_subsys_kernel(event->udev, key_name, attr, sizeof(attr), 0) != 0) 255699ca880aSopenharmony_ci strscpyl(attr, sizeof(attr), udev_device_get_syspath(event->dev), "/", key_name, NULL); 255799ca880aSopenharmony_ci attr_subst_subdir(attr, sizeof(attr)); 255899ca880aSopenharmony_ci 255999ca880aSopenharmony_ci udev_event_apply_format(event, rules_str(rules, cur->key.value_off), value, sizeof(value), false); 256099ca880aSopenharmony_ci log_debug("ATTR '%s' writing '%s' %s:%u", attr, value, 256199ca880aSopenharmony_ci rules_str(rules, rule->rule.filename_off), 256299ca880aSopenharmony_ci rule->rule.filename_line); 256399ca880aSopenharmony_ci f = fopen(attr, "we"); 256499ca880aSopenharmony_ci if (f != NULL) { 256599ca880aSopenharmony_ci if (fprintf(f, "%s", value) <= 0) 256699ca880aSopenharmony_ci log_error_errno(errno, "error writing ATTR{%s}: %m", attr); 256799ca880aSopenharmony_ci fclose(f); 256899ca880aSopenharmony_ci } else { 256999ca880aSopenharmony_ci log_error_errno(errno, "error opening ATTR{%s} for writing: %m", attr); 257099ca880aSopenharmony_ci } 257199ca880aSopenharmony_ci break; 257299ca880aSopenharmony_ci } 257399ca880aSopenharmony_ci case TK_A_SYSCTL: { 257499ca880aSopenharmony_ci char filename[UTIL_PATH_SIZE]; 257599ca880aSopenharmony_ci char value[UTIL_NAME_SIZE]; 257699ca880aSopenharmony_ci int r; 257799ca880aSopenharmony_ci 257899ca880aSopenharmony_ci udev_event_apply_format(event, rules_str(rules, cur->key.attr_off), filename, sizeof(filename), false); 257999ca880aSopenharmony_ci sysctl_normalize(filename); 258099ca880aSopenharmony_ci udev_event_apply_format(event, rules_str(rules, cur->key.value_off), value, sizeof(value), false); 258199ca880aSopenharmony_ci log_debug("SYSCTL '%s' writing '%s' %s:%u", filename, value, 258299ca880aSopenharmony_ci rules_str(rules, rule->rule.filename_off), rule->rule.filename_line); 258399ca880aSopenharmony_ci r = sysctl_write(filename, value); 258499ca880aSopenharmony_ci if (r < 0) 258599ca880aSopenharmony_ci log_error("error writing SYSCTL{%s}='%s': %s", filename, value, strerror(-r)); 258699ca880aSopenharmony_ci break; 258799ca880aSopenharmony_ci } 258899ca880aSopenharmony_ci case TK_A_RUN_BUILTIN: 258999ca880aSopenharmony_ci case TK_A_RUN_PROGRAM: { 259099ca880aSopenharmony_ci struct udev_list_entry *entry; 259199ca880aSopenharmony_ci 259299ca880aSopenharmony_ci if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) 259399ca880aSopenharmony_ci udev_list_cleanup(&event->run_list); 259499ca880aSopenharmony_ci log_debug("RUN '%s' %s:%u", 259599ca880aSopenharmony_ci rules_str(rules, cur->key.value_off), 259699ca880aSopenharmony_ci rules_str(rules, rule->rule.filename_off), 259799ca880aSopenharmony_ci rule->rule.filename_line); 259899ca880aSopenharmony_ci entry = udev_list_entry_add(&event->run_list, rules_str(rules, cur->key.value_off), NULL); 259999ca880aSopenharmony_ci udev_list_entry_set_num(entry, cur->key.builtin_cmd); 260099ca880aSopenharmony_ci break; 260199ca880aSopenharmony_ci } 260299ca880aSopenharmony_ci case TK_A_GOTO: 260399ca880aSopenharmony_ci if (cur->key.rule_goto == 0) 260499ca880aSopenharmony_ci break; 260599ca880aSopenharmony_ci cur = &rules->tokens[cur->key.rule_goto]; 260699ca880aSopenharmony_ci continue; 260799ca880aSopenharmony_ci case TK_END: 260899ca880aSopenharmony_ci return 0; 260999ca880aSopenharmony_ci 261099ca880aSopenharmony_ci case TK_M_PARENTS_MIN: 261199ca880aSopenharmony_ci case TK_M_PARENTS_MAX: 261299ca880aSopenharmony_ci case TK_M_MAX: 261399ca880aSopenharmony_ci case TK_UNSET: 261499ca880aSopenharmony_ci log_error("wrong type %u", cur->type); 261599ca880aSopenharmony_ci goto nomatch; 261699ca880aSopenharmony_ci } 261799ca880aSopenharmony_ci 261899ca880aSopenharmony_ci cur++; 261999ca880aSopenharmony_ci continue; 262099ca880aSopenharmony_ci nomatch: 262199ca880aSopenharmony_ci /* fast-forward to next rule */ 262299ca880aSopenharmony_ci cur = rule + rule->rule.token_count; 262399ca880aSopenharmony_ci } 262499ca880aSopenharmony_ci} 262599ca880aSopenharmony_ci 262699ca880aSopenharmony_ciint udev_rules_apply_static_dev_perms(struct udev_rules *rules) { 262799ca880aSopenharmony_ci struct token *cur; 262899ca880aSopenharmony_ci struct token *rule; 262999ca880aSopenharmony_ci uid_t uid = 0; 263099ca880aSopenharmony_ci gid_t gid = 0; 263199ca880aSopenharmony_ci mode_t mode = 0; 263299ca880aSopenharmony_ci _cleanup_strv_free_ char **tags = NULL; 263399ca880aSopenharmony_ci char **t; 263499ca880aSopenharmony_ci FILE *f = NULL; 263599ca880aSopenharmony_ci _cleanup_free_ char *path = NULL; 263699ca880aSopenharmony_ci int r = 0; 263799ca880aSopenharmony_ci 263899ca880aSopenharmony_ci if (rules->tokens == NULL) 263999ca880aSopenharmony_ci return 0; 264099ca880aSopenharmony_ci 264199ca880aSopenharmony_ci cur = &rules->tokens[0]; 264299ca880aSopenharmony_ci rule = cur; 264399ca880aSopenharmony_ci for (;;) { 264499ca880aSopenharmony_ci switch (cur->type) { 264599ca880aSopenharmony_ci case TK_RULE: 264699ca880aSopenharmony_ci /* current rule */ 264799ca880aSopenharmony_ci rule = cur; 264899ca880aSopenharmony_ci 264999ca880aSopenharmony_ci /* skip rules without a static_node tag */ 265099ca880aSopenharmony_ci if (!rule->rule.has_static_node) 265199ca880aSopenharmony_ci goto next; 265299ca880aSopenharmony_ci 265399ca880aSopenharmony_ci uid = 0; 265499ca880aSopenharmony_ci gid = 0; 265599ca880aSopenharmony_ci mode = 0; 265699ca880aSopenharmony_ci strv_free(tags); 265799ca880aSopenharmony_ci tags = NULL; 265899ca880aSopenharmony_ci break; 265999ca880aSopenharmony_ci case TK_A_OWNER_ID: 266099ca880aSopenharmony_ci uid = cur->key.uid; 266199ca880aSopenharmony_ci break; 266299ca880aSopenharmony_ci case TK_A_GROUP_ID: 266399ca880aSopenharmony_ci gid = cur->key.gid; 266499ca880aSopenharmony_ci break; 266599ca880aSopenharmony_ci case TK_A_MODE_ID: 266699ca880aSopenharmony_ci mode = cur->key.mode; 266799ca880aSopenharmony_ci break; 266899ca880aSopenharmony_ci case TK_A_TAG: 266999ca880aSopenharmony_ci r = strv_extend(&tags, rules_str(rules, cur->key.value_off)); 267099ca880aSopenharmony_ci if (r < 0) 267199ca880aSopenharmony_ci goto finish; 267299ca880aSopenharmony_ci 267399ca880aSopenharmony_ci break; 267499ca880aSopenharmony_ci case TK_A_STATIC_NODE: { 267599ca880aSopenharmony_ci char device_node[UTIL_PATH_SIZE]; 267699ca880aSopenharmony_ci char tags_dir[UTIL_PATH_SIZE]; 267799ca880aSopenharmony_ci char tag_symlink[UTIL_PATH_SIZE]; 267899ca880aSopenharmony_ci struct stat stats; 267999ca880aSopenharmony_ci 268099ca880aSopenharmony_ci /* we assure, that the permissions tokens are sorted before the static token */ 268199ca880aSopenharmony_ci 268299ca880aSopenharmony_ci if (mode == 0 && uid == 0 && gid == 0 && tags == NULL) 268399ca880aSopenharmony_ci goto next; 268499ca880aSopenharmony_ci 268599ca880aSopenharmony_ci strscpyl(device_node, sizeof(device_node), "/dev/", rules_str(rules, cur->key.value_off), NULL); 268699ca880aSopenharmony_ci if (stat(device_node, &stats) != 0) 268799ca880aSopenharmony_ci break; 268899ca880aSopenharmony_ci if (!S_ISBLK(stats.st_mode) && !S_ISCHR(stats.st_mode)) 268999ca880aSopenharmony_ci break; 269099ca880aSopenharmony_ci 269199ca880aSopenharmony_ci /* export the tags to a directory as symlinks, allowing otherwise dead nodes to be tagged */ 269299ca880aSopenharmony_ci if (tags) { 269399ca880aSopenharmony_ci STRV_FOREACH(t, tags) { 269499ca880aSopenharmony_ci _cleanup_free_ char *unescaped_filename = NULL; 269599ca880aSopenharmony_ci 269699ca880aSopenharmony_ci strscpyl(tags_dir, sizeof(tags_dir), UDEV_ROOT_RUN "/udev/static_node-tags/", *t, "/", NULL); 269799ca880aSopenharmony_ci r = udev_mkdir_p(tags_dir, 0755); 269899ca880aSopenharmony_ci if (r < 0) 269999ca880aSopenharmony_ci return log_error_errno(r, "failed to create %s: %m", tags_dir); 270099ca880aSopenharmony_ci 270199ca880aSopenharmony_ci unescaped_filename = xescape(rules_str(rules, cur->key.value_off), "/."); 270299ca880aSopenharmony_ci 270399ca880aSopenharmony_ci strscpyl(tag_symlink, sizeof(tag_symlink), tags_dir, unescaped_filename, NULL); 270499ca880aSopenharmony_ci r = symlink(device_node, tag_symlink); 270599ca880aSopenharmony_ci if (r < 0 && errno != EEXIST) 270699ca880aSopenharmony_ci return log_error_errno(errno, "failed to create symlink %s -> %s: %m", 270799ca880aSopenharmony_ci tag_symlink, device_node); 270899ca880aSopenharmony_ci else 270999ca880aSopenharmony_ci r = 0; 271099ca880aSopenharmony_ci } 271199ca880aSopenharmony_ci } 271299ca880aSopenharmony_ci 271399ca880aSopenharmony_ci /* don't touch the permissions if only the tags were set */ 271499ca880aSopenharmony_ci if (mode == 0 && uid == 0 && gid == 0) 271599ca880aSopenharmony_ci break; 271699ca880aSopenharmony_ci 271799ca880aSopenharmony_ci if (mode == 0) { 271899ca880aSopenharmony_ci if (gid > 0) 271999ca880aSopenharmony_ci mode = 0660; 272099ca880aSopenharmony_ci else 272199ca880aSopenharmony_ci mode = 0600; 272299ca880aSopenharmony_ci } 272399ca880aSopenharmony_ci if (mode != (stats.st_mode & 01777)) { 272499ca880aSopenharmony_ci r = chmod(device_node, mode); 272599ca880aSopenharmony_ci if (r < 0) { 272699ca880aSopenharmony_ci log_error("failed to chmod '%s' %#o", device_node, mode); 272799ca880aSopenharmony_ci return -errno; 272899ca880aSopenharmony_ci } else 272999ca880aSopenharmony_ci log_debug("chmod '%s' %#o", device_node, mode); 273099ca880aSopenharmony_ci } 273199ca880aSopenharmony_ci 273299ca880aSopenharmony_ci if ((uid != 0 && uid != stats.st_uid) || (gid != 0 && gid != stats.st_gid)) { 273399ca880aSopenharmony_ci r = chown(device_node, uid, gid); 273499ca880aSopenharmony_ci if (r < 0) { 273599ca880aSopenharmony_ci log_error("failed to chown '%s' %u %u ", device_node, uid, gid); 273699ca880aSopenharmony_ci return -errno; 273799ca880aSopenharmony_ci } else 273899ca880aSopenharmony_ci log_debug("chown '%s' %u %u", device_node, uid, gid); 273999ca880aSopenharmony_ci } 274099ca880aSopenharmony_ci 274199ca880aSopenharmony_ci utimensat(AT_FDCWD, device_node, NULL, 0); 274299ca880aSopenharmony_ci break; 274399ca880aSopenharmony_ci } 274499ca880aSopenharmony_ci case TK_END: 274599ca880aSopenharmony_ci goto finish; 274699ca880aSopenharmony_ci } 274799ca880aSopenharmony_ci 274899ca880aSopenharmony_ci cur++; 274999ca880aSopenharmony_ci continue; 275099ca880aSopenharmony_cinext: 275199ca880aSopenharmony_ci /* fast-forward to next rule */ 275299ca880aSopenharmony_ci cur = rule + rule->rule.token_count; 275399ca880aSopenharmony_ci continue; 275499ca880aSopenharmony_ci } 275599ca880aSopenharmony_ci 275699ca880aSopenharmony_cifinish: 275799ca880aSopenharmony_ci if (f) { 275899ca880aSopenharmony_ci fflush(f); 275999ca880aSopenharmony_ci fchmod(fileno(f), 0644); 276099ca880aSopenharmony_ci if (ferror(f) || rename(path, UDEV_ROOT_RUN "/udev/static_node-tags") < 0) { 276199ca880aSopenharmony_ci r = -errno; 276299ca880aSopenharmony_ci unlink(UDEV_ROOT_RUN "/udev/static_node-tags"); 276399ca880aSopenharmony_ci unlink(path); 276499ca880aSopenharmony_ci } 276599ca880aSopenharmony_ci fclose(f); 276699ca880aSopenharmony_ci } 276799ca880aSopenharmony_ci 276899ca880aSopenharmony_ci return r; 276999ca880aSopenharmony_ci} 277099ca880aSopenharmony_ci 277199ca880aSopenharmony_ci#ifdef ENABLE_RULE_GENERATOR 277299ca880aSopenharmony_ci/* function to return the count of rules that assign NAME= to a value matching arg#2 - returns 0,1 */ 277399ca880aSopenharmony_ciint udev_rules_assigning_name_to(struct udev_rules *rules, const char *match_name) 277499ca880aSopenharmony_ci{ 277599ca880aSopenharmony_ci struct token *cur; 277699ca880aSopenharmony_ci struct token *rule; 277799ca880aSopenharmony_ci enum escape_type esc = ESCAPE_UNSET; 277899ca880aSopenharmony_ci bool name_final = false; 277999ca880aSopenharmony_ci 278099ca880aSopenharmony_ci if (rules->tokens == NULL) 278199ca880aSopenharmony_ci return 0; 278299ca880aSopenharmony_ci 278399ca880aSopenharmony_ci /* loop through token list, match, run actions or forward to next rule */ 278499ca880aSopenharmony_ci cur = &rules->tokens[0]; 278599ca880aSopenharmony_ci rule = cur; 278699ca880aSopenharmony_ci for (;;) { 278799ca880aSopenharmony_ci dump_token(rules, cur); 278899ca880aSopenharmony_ci switch (cur->type) { 278999ca880aSopenharmony_ci case TK_RULE: 279099ca880aSopenharmony_ci /* current rule */ 279199ca880aSopenharmony_ci rule = cur; 279299ca880aSopenharmony_ci if (!rule->rule.can_set_name) 279399ca880aSopenharmony_ci goto nomatch; 279499ca880aSopenharmony_ci break; 279599ca880aSopenharmony_ci case TK_M_SUBSYSTEM: 279699ca880aSopenharmony_ci if (match_key(rules, cur, "net") != 0) 279799ca880aSopenharmony_ci goto nomatch; 279899ca880aSopenharmony_ci break; 279999ca880aSopenharmony_ci case TK_M_ACTION: 280099ca880aSopenharmony_ci if (match_key(rules, cur, "add") != 0) 280199ca880aSopenharmony_ci goto nomatch; 280299ca880aSopenharmony_ci break; 280399ca880aSopenharmony_ci case TK_A_NAME: { 280499ca880aSopenharmony_ci const char *name = rules_str(rules, cur->key.value_off); 280599ca880aSopenharmony_ci char name_str[UTIL_PATH_SIZE]; 280699ca880aSopenharmony_ci int count; 280799ca880aSopenharmony_ci 280899ca880aSopenharmony_ci strscpy(name_str,UTIL_PATH_SIZE,name); 280999ca880aSopenharmony_ci count = util_replace_chars(name_str, "/"); 281099ca880aSopenharmony_ci if (count > 0) 281199ca880aSopenharmony_ci log_debug("%i character(s) replaced\n", count); 281299ca880aSopenharmony_ci if (streq(name_str,match_name)) { 281399ca880aSopenharmony_ci log_debug("found a match! NAME assigns %s in: %s:%u\n", 281499ca880aSopenharmony_ci name, 281599ca880aSopenharmony_ci rules_str(rules, rule->rule.filename_off), 281699ca880aSopenharmony_ci rule->rule.filename_line); 281799ca880aSopenharmony_ci return 1; /* no need to find more than one */ 281899ca880aSopenharmony_ci } 281999ca880aSopenharmony_ci 282099ca880aSopenharmony_ci /* skip to next rule */ 282199ca880aSopenharmony_ci goto nomatch; 282299ca880aSopenharmony_ci } 282399ca880aSopenharmony_ci case TK_END: 282499ca880aSopenharmony_ci return 0; 282599ca880aSopenharmony_ci } 282699ca880aSopenharmony_ci 282799ca880aSopenharmony_ci cur++; 282899ca880aSopenharmony_ci continue; 282999ca880aSopenharmony_ci nomatch: 283099ca880aSopenharmony_ci /* fast-forward to next rule */ 283199ca880aSopenharmony_ci cur = rule + rule->rule.token_count; 283299ca880aSopenharmony_ci } 283399ca880aSopenharmony_ci} 283499ca880aSopenharmony_ci#endif 283599ca880aSopenharmony_ci 2836