udev-rules.c revision ff944daa019c1101d6464412e6682732ec11143a
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen * Copyright (C) 2003-2012 Kay Sievers <kay.sievers@vrfy.org>
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen * This program is free software: you can redistribute it and/or modify
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen * it under the terms of the GNU General Public License as published by
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen * the Free Software Foundation, either version 2 of the License, or
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen * (at your option) any later version.
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen * This program is distributed in the hope that it will be useful,
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen * but WITHOUT ANY WARRANTY; without even the implied warranty of
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen * GNU General Public License for more details.
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen * You should have received a copy of the GNU General Public License
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen * along with this program. If not, see <http://www.gnu.org/licenses/>.
a501033335ed402c8f7e86fe41a15531ba69abd7Tom Gundersen unsigned long long *dirs_ts_usec;
1c4baffc1895809bae9ac36b670af90a4cb9cd7dTom Gundersen /* every key in the rules file becomes a token */
2ad8416dd057e7e3185169609ca3006e7649f576Zbigniew Jędrzejewski-Szmek /* all key strings are copied and de-duplicated in a single continous string buffer */
2ad8416dd057e7e3185169609ca3006e7649f576Zbigniew Jędrzejewski-Szmek /* during rule parsing, uid/gid lookup results are cached */
5b9d4dc05560ddda89e48b6b39365824b15e1300Tom Gundersenstatic char *rules_str(struct udev_rules *rules, unsigned int off) {
43d60b77a83b3185e37c65c4f2649d24c227c7f0Tom Gundersenstatic unsigned int rules_add_string(struct udev_rules *rules, const char *s) {
43d60b77a83b3185e37c65c4f2649d24c227c7f0Tom Gundersen return strbuf_add_string(rules->strbuf, s, strlen(s));
9a4b012e43f23516373bf398dd9a458439d19939Tom Gundersen/* KEY=="", KEY!="", KEY+="", KEY="", KEY:="" */
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen GL_SPLIT_GLOB, /* multi-value with glob A*|B* */
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen/* tokens of a rule are sorted/handled in this order */
ed88bcfb7c15029f9fc95ee2380759a9eb782d46Zbigniew Jędrzejewski-Szmek TK_A_RUN_BUILTIN, /* val, bool */
ed88bcfb7c15029f9fc95ee2380759a9eb782d46Zbigniew Jędrzejewski-Szmek TK_A_RUN_PROGRAM, /* val, bool */
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen/* we try to pack stuff in a way that we take only 12 bytes per token */
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen unsigned char type; /* same in rule and key */
98a375f6d5cac24eb80d6d4e00699851324afdecTom Gundersen unsigned short filename_off;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen unsigned short filename_line;
ecb08ec6a5c52f2d940f3b8147e2a480affd46e1Zbigniew Jędrzejewski-Szmek enum string_subst_type attrsubst:4;
edf029b7fd9a5853a87d3ca99aac2922bb8a277eTom Gundersenstatic const char *operation_str(enum operation_type type)
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen static const char *operation_strs[] = {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersenstatic const char *string_glob_str(enum string_glob_type type)
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen static const char *string_glob_strs[] = {
464cf22f17e0cf2d8bfa6d72b5e7a662d634f149Tom Gundersenstatic const char *token_str(enum token_type type)
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen static const char *token_strs[] = {
dc75168823540076b354135f6e2de7a9a978fbcaZbigniew Jędrzejewski-Szmek [TK_M_EVENT_TIMEOUT] = "M EVENT_TIMEOUT",
32bc8adcd836baff68e4d0f53b9a382f358cccf8Tom Gundersen [TK_A_STRING_ESCAPE_NONE] = "A STRING_ESCAPE_NONE",
ca6038b89645c0c1bd547d6a420bf95eb3d6f4ccTom Gundersen [TK_A_STRING_ESCAPE_REPLACE] = "A STRING_ESCAPE_REPLACE",
3c9b886068d99e5d3cbabcac32a4decf37244c54Tom Gundersenstatic void dump_token(struct udev_rules *rules, struct token *token)
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen const char *value = str(rules, token->key.value_off);
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen const char *attr = &rules->buf[token->key.attr_off];
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen unsigned int idx = (tk_ptr - tks_ptr) / sizeof(struct token);
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen log_debug("* RULE %s:%u, token: %u, count: %u, label: '%s'\n",
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen &rules->buf[token->rule.filename_off], token->rule.filename_line,
9bf3b53533cdc9b95c921b71da755401f223f765Lennart Poettering token_str(type), operation_str(op), value, string_glob_str(glob));
55428d84f31b52da1c50b7469f14e15740547f20Tom Gundersen log_debug("%s %i '%s'\n", token_str(type), token->key.builtin_cmd, value);
3c9b886068d99e5d3cbabcac32a4decf37244c54Tom Gundersen token_str(type), operation_str(op), attr, value, string_glob_str(glob));
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen log_debug("%s %s '%s'\n", token_str(type), operation_str(op), value);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen token_str(type), operation_str(op), value, string_glob_str(glob), token->key.mode);
3e137a1b9a0eac2bf43d493d3302c3c959b6ccdbTom Gundersen log_debug("%s %u\n", token_str(type), token->key.watch);
3e137a1b9a0eac2bf43d493d3302c3c959b6ccdbTom Gundersen log_debug("%s %u\n", token_str(type), token->key.devlink_prio);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen log_debug("%s %s %u\n", token_str(type), operation_str(op), token->key.uid);
dab495dc23bf9a5ba0487a057bb594355555a0e9Tom Gundersen log_debug("%s %s %u\n", token_str(type), operation_str(op), token->key.gid);
a501033335ed402c8f7e86fe41a15531ba69abd7Tom Gundersen log_debug("%s %s %#o\n", token_str(type), operation_str(op), token->key.mode);
755bde375f4db393ad06e73340bfcf4d0cf91bb2Lennart Poettering log_debug("%s '%s'\n", token_str(type), value);
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen log_debug("%s %u\n", token_str(type), token->key.event_timeout);
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen log_debug("%s '%s' %u\n", token_str(type), value, token->key.rule_goto);
e51660ae56bb747ece2cab8fe6eec37f4d06a438Tom Gundersenstatic void dump_rules(struct udev_rules *rules)
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen unsigned int i;
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen log_debug("dumping %u (%zu bytes) tokens, %u (%zu bytes) strings\n",
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersenstatic inline const char *operation_str(enum operation_type type) { return NULL; }
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersenstatic inline const char *token_str(enum token_type type) { return NULL; }
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersenstatic inline void dump_token(struct udev_rules *rules, struct token *token) {}
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersenstatic inline void dump_rules(struct udev_rules *rules) {}
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen#endif /* DEBUG */
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersenstatic int add_token(struct udev_rules *rules, struct token *token)
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen /* grow buffer if needed */
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen unsigned int add;
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen /* double the buffer size */
1c25683e0f40c6169676cc44fa1897082597feecTom Gundersen tokens = realloc(rules->tokens, (rules->token_max + add ) * sizeof(struct token));
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen memcpy(&rules->tokens[rules->token_cur], token, sizeof(struct token));
1c25683e0f40c6169676cc44fa1897082597feecTom Gundersenstatic uid_t add_uid(struct udev_rules *rules, const char *owner)
1c25683e0f40c6169676cc44fa1897082597feecTom Gundersen unsigned int i;
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen unsigned int off;
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen /* lookup, if we know it already */
d95b83b87d7d7c50e550f7128827f73a321c8934Tom Gundersen /* grow buffer if needed */
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen unsigned int add;
847a8a5fed4d265dfa659917515c6f9bd1b8d5c4Tom Gundersen /* double the buffer size */
847a8a5fed4d265dfa659917515c6f9bd1b8d5c4Tom Gundersen uids = realloc(rules->uids, (rules->uids_max + add ) * sizeof(struct uid_gid));
66d3752e812915a549ebee01769ee761c1495667Jacob Kellerstatic gid_t add_gid(struct udev_rules *rules, const char *group)
be32eb9b7fbcb22e4b648086d644135e38279633Tom Gundersen unsigned int i;
464cf22f17e0cf2d8bfa6d72b5e7a662d634f149Tom Gundersen unsigned int off;
be32eb9b7fbcb22e4b648086d644135e38279633Tom Gundersen /* lookup, if we know it already */
be32eb9b7fbcb22e4b648086d644135e38279633Tom Gundersen /* grow buffer if needed */
464cf22f17e0cf2d8bfa6d72b5e7a662d634f149Tom Gundersen 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);
static int import_program_into_properties(struct udev_event *event, const char *program, const sigset_t *sigmask)
char **envp;
char *line;
int err;
if (err < 0)
return err;
char *pos;
while (--loop) {
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_M_IMPORT_BUILTIN:
case TK_M_ENV:
case TK_M_ATTR:
case TK_M_ATTRS:
case TK_A_ATTR:
case TK_A_ENV:
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_A_STATIC_NODE:
case TK_M_EVENT_TIMEOUT:
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;
has_glob = (strchr(value, '*') != NULL || strchr(value, '?') != NULL || strchr(value, '[') != NULL);
} 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;
goto invalid;
goto invalid;
goto invalid;
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;
char *endptr;
char *endptr;
char *endptr;
const char *pos;
const int off = 0;
goto invalid;
goto invalid;
goto invalid;
FILE *f;
unsigned int first_token;
unsigned int filename_off;
int line_nr = 0;
if (f == NULL)
char *key;
line_nr++;
key++;
line_nr++;
fclose(f);
char **files, **f;
return NULL;
NULL);
rules->token_max * sizeof(struct token), rules->token_max, sizeof(struct token), rules->strbuf->len);
return rules;
return NULL;
return NULL;
bool changed = false;
goto out;
changed = true;
out:
return changed;
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 {
int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event, const sigset_t *sigmask)
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;
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_WAITFOR: {
int found;
goto nomatch;
case TK_M_ATTR:
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_EVENT_TIMEOUT:
case TK_M_PROGRAM: {
char **envp;
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_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_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_STATIC_NODE: {
goto next;
goto next;
goto next;
if (mode == 0) {
if (gid > 0)
case TK_END:
cur++;
next: