udev-rules.c revision 1f6b411372076426c0faf0bb350437fb4d82931f
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * Copyright (C) 2003-2012 Kay Sievers <kay@vrfy.org>
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * This program is free software: you can redistribute it and/or modify
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * it under the terms of the GNU General Public License as published by
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * the Free Software Foundation, either version 2 of the License, or
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * (at your option) any later version.
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * This program is distributed in the hope that it will be useful,
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * but WITHOUT ANY WARRANTY; without even the implied warranty of
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * GNU General Public License for more details.
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * You should have received a copy of the GNU General Public License
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * along with this program. If not, see <http://www.gnu.org/licenses/>.
b92bea5d2a9481de69bb627a7b442a9f58fca43dZbigniew Jędrzejewski-Szmekstatic const char* const rules_dirs[] = {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* every key in the rules file becomes a token */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* all key strings are copied and de-duplicated in a single continuous string buffer */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* during rule parsing, uid/gid lookup results are cached */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic char *rules_str(struct udev_rules *rules, unsigned int off) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic unsigned int rules_add_string(struct udev_rules *rules, const char *s) {
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering return strbuf_add_string(rules->strbuf, s, strlen(s));
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering/* KEY=="", KEY!="", KEY+="", KEY-="", KEY="", KEY:="" */
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering GL_SPLIT_GLOB, /* multi-value with glob A*|B* */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering/* tokens of a rule are sorted/handled in this order */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering/* we try to pack stuff in a way that we take only 12 bytes per token */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering unsigned char type; /* same in rule and key */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic const char *operation_str(enum operation_type type) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering static const char *operation_strs[] = {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic const char *string_glob_str(enum string_glob_type type) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering static const char *string_glob_strs[] = {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poetteringstatic const char *token_str(enum token_type type) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering static const char *token_strs[] = {
ac50788b0f5aeee09e7d45db28ae8ab7f39cd52eZbigniew Jędrzejewski-Szmek [TK_M_PARENTS_MIN] = "M PARENTS_MIN",
ac50788b0f5aeee09e7d45db28ae8ab7f39cd52eZbigniew Jędrzejewski-Szmek [TK_M_PARENTS_MAX] = "M PARENTS_MAX",
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering [TK_M_IMPORT_BUILTIN] = "M IMPORT_BUILTIN",
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering [TK_M_IMPORT_CMDLINE] = "M IMPORT_CMDLINE",
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering [TK_M_IMPORT_PARENT] = "M IMPORT_PARENT",
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering [TK_A_STRING_ESCAPE_NONE] = "A STRING_ESCAPE_NONE",
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering [TK_A_STRING_ESCAPE_REPLACE] = "A STRING_ESCAPE_REPLACE",
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering [TK_A_INOTIFY_WATCH] = "A INOTIFY_WATCH",
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poetteringstatic void dump_token(struct udev_rules *rules, struct token *token) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering enum string_glob_type glob = token->key.glob;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering const char *value = str(rules, token->key.value_off);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering const char *attr = &rules->buf[token->key.attr_off];
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering const char *tks_ptr = (char *)rules->tokens;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering unsigned int idx = (tk_ptr - tks_ptr) / sizeof(struct token);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering log_debug("* RULE %s:%u, token: %u, count: %u, label: '%s'",
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering &rules->buf[token->rule.filename_off], token->rule.filename_line,
8457f8d6ac7adc6c6ef31378e6e7761cce522141Lennart Poettering token_str(type), operation_str(op), value, string_glob_str(glob));
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_debug("%s %i '%s'", token_str(type), token->key.builtin_cmd, value);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering token_str(type), operation_str(op), attr, value, string_glob_str(glob));
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_debug("%s %s '%s'", token_str(type), operation_str(op), value);
8457f8d6ac7adc6c6ef31378e6e7761cce522141Lennart Poettering token_str(type), operation_str(op), value, string_glob_str(glob), token->key.mode);
8457f8d6ac7adc6c6ef31378e6e7761cce522141Lennart Poettering log_debug("%s %u", token_str(type), token->key.watch);
8457f8d6ac7adc6c6ef31378e6e7761cce522141Lennart Poettering log_debug("%s %u", token_str(type), token->key.devlink_prio);
8457f8d6ac7adc6c6ef31378e6e7761cce522141Lennart Poettering log_debug("%s %s %u", token_str(type), operation_str(op), token->key.uid);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_debug("%s %s %u", token_str(type), operation_str(op), token->key.gid);
8457f8d6ac7adc6c6ef31378e6e7761cce522141Lennart Poettering log_debug("%s %s %#o", token_str(type), operation_str(op), token->key.mode);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_debug("%s '%s'", token_str(type), value);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_debug("%s %s '%s' '%s'", token_str(type), operation_str(op), attr, value);
968f319679d9069af037240d0c3bcd126181cdacZbigniew Jędrzejewski-Szmek log_debug("%s '%s' %u", token_str(type), value, token->key.rule_goto);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic void dump_rules(struct udev_rules *rules) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering unsigned int i;
4a62c710b62a5a3c7a8a278b810b9d5b5a0c8f4fMichal Schmidt log_debug("dumping %u (%zu bytes) tokens, %u (%zu bytes) strings",
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic inline void dump_token(struct udev_rules *rules, struct token *token) {}
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic inline void dump_rules(struct udev_rules *rules) {}
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering#endif /* DEBUG */
4a62c710b62a5a3c7a8a278b810b9d5b5a0c8f4fMichal Schmidtstatic int add_token(struct udev_rules *rules, struct token *token) {
4a62c710b62a5a3c7a8a278b810b9d5b5a0c8f4fMichal Schmidt /* grow buffer if needed */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (rules->token_cur+1 >= rules->token_max) {
d682b3a7e7c7c2941a4d3e193f1e330dbc9fae89Lennart Poettering /* double the buffer size */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering tokens = realloc(rules->tokens, (rules->token_max + add ) * sizeof(struct token));
23bbb0de4e3f85d9704a5c12a5afa2dfa0159e41Michal Schmidt memcpy(&rules->tokens[rules->token_cur], token, sizeof(struct token));
178cc7700c23ac088cd7190d7854282075028d91Lennart Poetteringstatic uid_t add_uid(struct udev_rules *rules, const char *owner) {
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering unsigned int i;
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering /* lookup, if we know it already */
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering if (streq(rules_str(rules, off), owner)) {
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering r = get_user_creds(&owner, &uid, NULL, NULL, NULL);
unsigned int add;
return uid;
if (off <= 0)
return uid;
return uid;
unsigned int off;
return gid;
unsigned int add;
return gid;
if (off <= 0)
return gid;
return gid;
char *key;
char *val;
key++;
val++;
val++;
if (len == 0)
len--;
if (len == 0)
len--;
if (len == 0)
val++;
FILE *f;
if (f == NULL)
fclose(f);
const char *program) {
char *line;
int err;
err = udev_event_spawn(event, timeout_usec, timeout_warn_usec, true, program, result, sizeof(result));
if (err < 0)
return err;
char *pos;
bool found = false;
char *pos;
const char *tail;
found = true;
return found;
static int get_key(struct udev *udev, char **line, char **key, enum operation_type *op, char **value) {
char *linepos;
char *temp;
linepos++;
linepos++;
linepos++;
linepos++;
linepos++;
linepos++;
if (!temp)
temp++;
char *pos;
char *attr;
attr++;
return NULL;
return attr;
return NULL;
switch (type) {
case TK_M_ACTION:
case TK_M_DEVPATH:
case TK_M_KERNEL:
case TK_M_SUBSYSTEM:
case TK_M_DRIVER:
case TK_M_WAITFOR:
case TK_M_DEVLINK:
case TK_M_NAME:
case TK_M_KERNELS:
case TK_M_SUBSYSTEMS:
case TK_M_DRIVERS:
case TK_M_TAGS:
case TK_M_PROGRAM:
case TK_M_IMPORT_FILE:
case TK_M_IMPORT_PROG:
case TK_M_IMPORT_DB:
case TK_M_IMPORT_CMDLINE:
case TK_M_IMPORT_PARENT:
case TK_M_RESULT:
case TK_A_OWNER:
case TK_A_GROUP:
case TK_A_MODE:
case TK_A_DEVLINK:
case TK_A_NAME:
case TK_A_GOTO:
case TK_M_TAG:
case TK_A_TAG:
case TK_A_STATIC_NODE:
case TK_M_IMPORT_BUILTIN:
case TK_M_ENV:
case TK_M_ATTR:
case TK_M_SYSCTL:
case TK_M_ATTRS:
case TK_A_ATTR:
case TK_A_SYSCTL:
case TK_A_ENV:
case TK_A_SECLABEL:
case TK_M_TEST:
case TK_A_STRING_ESCAPE_NONE:
case TK_A_DB_PERSIST:
case TK_A_RUN_BUILTIN:
case TK_A_RUN_PROGRAM:
case TK_A_INOTIFY_WATCH:
case TK_A_DEVLINK_PRIO:
case TK_A_OWNER_ID:
case TK_A_GROUP_ID:
case TK_A_MODE_ID:
case TK_RULE:
case TK_M_PARENTS_MIN:
case TK_M_PARENTS_MAX:
case TK_M_MAX:
case TK_END:
case TK_UNSET:
int has_split;
int has_glob;
} else if (has_split) {
} else if (has_glob) {
unsigned int start = 0;
unsigned int next_idx = 0;
next_idx = j;
start++;
end--;
char *linepos;
const char *attr;
char *key;
char *value;
linepos++;
goto invalid;
goto invalid;
goto invalid;
goto invalid;
goto invalid;
goto invalid;
goto invalid;
goto invalid;
goto invalid;
if (!attr) {
goto invalid;
goto invalid;
goto invalid;
goto invalid;
goto invalid;
goto invalid;
goto invalid;
goto invalid;
goto invalid;
goto invalid;
goto invalid;
static const char *blacklist[] = {
goto invalid;
goto invalid;
goto invalid;
goto invalid;
goto invalid;
goto invalid;
goto invalid;
goto invalid;
goto invalid;
goto invalid;
goto invalid;
char *endptr;
goto invalid;
char *endptr;
goto invalid;
char *endptr;
goto invalid;
const char *pos;
goto invalid;
const int off = 0;
goto invalid;
goto invalid;
goto invalid;
unsigned int first_token;
unsigned int filename_off;
int line_nr = 0;
return -errno;
char *key;
line_nr++;
key++;
line_nr++;
char **files, **f;
return NULL;
rules->token_max * sizeof(struct token), rules->token_max, sizeof(struct token), rules->strbuf->len);
return rules;
return NULL;
return NULL;
if (!rules)
char *pos;
bool match = false;
case GL_PLAIN:
case GL_GLOB:
case GL_SPLIT:
const char *next;
if (match)
case GL_SPLIT_GLOB:
if (match)
case GL_SOMETHING:
case GL_UNSET:
static int match_attr(struct udev_rules *rules, struct udev_device *dev, struct udev_event *event, struct token *cur) {
const char *name;
const char *value;
case SB_FORMAT:
case SB_NONE:
case SB_SUBSYS:
const char *key_value;
enum escape_type {
bool can_set_name;
case TK_RULE:
goto nomatch;
case TK_M_ACTION:
goto nomatch;
case TK_M_DEVPATH:
goto nomatch;
case TK_M_KERNEL:
goto nomatch;
case TK_M_DEVLINK: {
bool match = false;
const char *devlink;
match = true;
if (!match)
goto nomatch;
case TK_M_NAME:
goto nomatch;
case TK_M_ENV: {
const char *value;
if (!value)
goto nomatch;
case TK_M_TAG: {
bool match = false;
match = true;
goto nomatch;
case TK_M_SUBSYSTEM:
goto nomatch;
case TK_M_DRIVER:
goto nomatch;
case TK_M_ATTR:
goto nomatch;
case TK_M_SYSCTL: {
goto nomatch;
goto nomatch;
case TK_M_KERNELS:
case TK_M_SUBSYSTEMS:
case TK_M_DRIVERS:
case TK_M_ATTRS:
case TK_M_TAGS: {
next++;
case TK_M_KERNELS:
goto try_parent;
case TK_M_SUBSYSTEMS:
goto try_parent;
case TK_M_DRIVERS:
goto try_parent;
case TK_M_ATTRS:
goto try_parent;
case TK_M_TAGS: {
goto try_parent;
goto try_parent;
goto nomatch;
goto nomatch;
case TK_M_TEST: {
int match;
goto nomatch;
goto nomatch;
case TK_M_PROGRAM: {
if (udev_event_spawn(event, timeout_usec, timeout_warn_usec, true, program, result, sizeof(result)) < 0) {
goto nomatch;
int count;
if (count > 0)
goto nomatch;
case TK_M_IMPORT_FILE: {
goto nomatch;
case TK_M_IMPORT_PROG: {
goto nomatch;
case TK_M_IMPORT_BUILTIN: {
goto nomatch;
goto nomatch;
case TK_M_IMPORT_DB: {
const char *value;
goto nomatch;
case TK_M_IMPORT_CMDLINE: {
FILE *f;
bool imported = false;
if (f != NULL) {
char *pos;
imported = true;
const char *value;
pos++;
pos++;
imported = true;
fclose(f);
goto nomatch;
case TK_M_IMPORT_PARENT: {
goto nomatch;
case TK_M_RESULT:
goto nomatch;
case TK_A_STRING_ESCAPE_NONE:
case TK_A_DB_PERSIST:
case TK_A_INOTIFY_WATCH:
case TK_A_DEVLINK_PRIO:
case TK_A_OWNER: {
case TK_A_GROUP: {
case TK_A_MODE: {
char *endptr;
case TK_A_OWNER_ID:
case TK_A_GROUP_ID:
case TK_A_MODE_ID:
case TK_A_SECLABEL: {
case TK_A_ENV: {
if (value_old) {
case TK_A_TAG: {
case TK_A_NAME: {
int count;
if (count > 0)
case TK_A_DEVLINK: {
int count = 0;
if (count > 0)
pos++;
next++;
case TK_A_ATTR: {
FILE *f;
if (f != NULL) {
fclose(f);
case TK_A_SYSCTL: {
case TK_A_RUN_BUILTIN:
case TK_A_RUN_PROGRAM: {
case TK_A_GOTO:
case TK_END:
case TK_M_PARENTS_MIN:
case TK_M_PARENTS_MAX:
case TK_M_MAX:
case TK_UNSET:
goto nomatch;
cur++;
case TK_RULE:
goto next;
uid = 0;
gid = 0;
mode = 0;
case TK_A_OWNER_ID:
case TK_A_GROUP_ID:
case TK_A_MODE_ID:
case TK_A_TAG:
goto finish;
case TK_A_STATIC_NODE: {
goto next;
if (tags) {
if (mode == 0) {
if (gid > 0)
return -errno;
return -errno;
case TK_END:
goto finish;
cur++;
next:
fflush(f);
r = -errno;
fclose(f);