enum string_glob_type {
GL_UNSET,
- GL_PLAIN,
- GL_GLOB,
- GL_SPLIT,
- GL_SPLIT_GLOB,
+ GL_PLAIN, /* no special chars */
+ GL_GLOB, /* shell globs ?,*,[] */
+ GL_SPLIT, /* multi-value A|B */
+ GL_SPLIT_GLOB, /* multi-value with glob A*|B* */
+ GL_SOMETHING, /* commonly used "?*" */
GL_FORMAT,
};
+#ifdef DEBUG
static const char *string_glob_str[] = {
[GL_UNSET] = "UNSET",
[GL_PLAIN] = "plain",
[GL_GLOB] = "glob",
[GL_SPLIT] = "split",
[GL_SPLIT_GLOB] = "split-glob",
+ [GL_SOMETHING] = "split-glob",
[GL_FORMAT] = "format",
};
+#endif
/* tokens of a rule are sorted/handled in this order */
enum token_type {
{
struct udev *udev = udev_device_get_udev(dev);
char filepath[UTIL_PATH_SIZE];
- char devicepath[UTIL_PATH_SIZE] = "";
+ char devicepath[UTIL_PATH_SIZE];
struct stat stats;
int loop = timeout * WAIT_LOOP_PER_SECOND;
/* a relative path is a device attribute */
+ devicepath[0] = '\0';
if (file[0] != '/') {
util_strlcpy(devicepath, udev_get_sys_path(udev), sizeof(devicepath));
util_strlcat(devicepath, udev_device_get_devpath(dev), sizeof(devicepath));
if (value != NULL) {
if (type < TK_M_MAX) {
/* check if we need to split or call fnmatch() while matching rules */
- int has_split = 0;
- int has_glob = 0;
+ int has_split;
+ int has_glob;
has_split = (strchr(value, '|') != NULL);
has_glob = (strchr(value, '*') != NULL || strchr(value, '?') != NULL ||
strchr(value, '[') != NULL || strchr(value, ']') != NULL);
- if (has_split && has_glob)
+ if (has_split && has_glob) {
glob = GL_SPLIT_GLOB;
- else if (has_split)
+ } else if (has_split) {
glob = GL_SPLIT;
- else if (has_glob)
- glob = GL_GLOB;
+ } else if (has_glob) {
+ if (strcmp(value, "?*") == 0)
+ glob = GL_SOMETHING;
+ else
+ glob = GL_GLOB;
+ }
} else {
/* check if we need to substitute format strings for matching rules */
if (strchr(value, '%') != NULL || strchr(value, '$') != NULL)
dbg(rules->udev, "* %s\n", token_str[type]);
break;
case TK_M_PARENTS_MAX:
+ case TK_M_MAX:
case TK_UNSET:
dbg(rules->udev, "unknown type %u\n", type);
break;
static int match_key(struct udev_rules *rules, struct token *token, const char *val)
{
- const char *key_name = token_str[token->type];
char *key_value = &rules->buf[token->key.value_off];
char *pos;
int match = 0;
break;
case GL_SPLIT:
{
- char value[UTIL_PATH_SIZE];
+ const char *split;
+ size_t len;
- util_strlcpy(value, &rules->buf[token->key.value_off], sizeof(value));
- key_value = value;
- while (key_value != NULL) {
- pos = strchr(key_value, '|');
- if (pos != NULL) {
- pos[0] = '\0';
- pos = &pos[1];
- }
- dbg(rules->udev, "match %s '%s' <-> '%s'\n", key_name, key_value, val);
- match = (strcmp(key_value, val) == 0);
- if (match)
+ split = &rules->buf[token->key.value_off];
+ len = strlen(val);
+ while (1) {
+ const char *next;
+
+ next = strchr(split, '|');
+ if (next != NULL) {
+ size_t matchlen = (size_t)(next - split);
+
+ match = (matchlen == len && strncmp(split, val, matchlen) == 0);
+ if (match)
+ break;
+ } else {
+ match = (strcmp(split, val) == 0);
break;
- key_value = pos;
+ }
+ split = &next[1];
}
break;
}
pos[0] = '\0';
pos = &pos[1];
}
- dbg(rules->udev, "match %s '%s' <-> '%s'\n", key_name, key_value, val);
+ dbg(rules->udev, "match %s '%s' <-> '%s'\n", token_str[token->type], key_value, val);
match = (fnmatch(key_value, val, 0) == 0);
if (match)
break;
}
break;
}
+ case GL_SOMETHING:
+ match = (val[0] != '\0');
+ break;
case GL_FORMAT:
case GL_UNSET:
return -1;
}
if (match && (token->key.op == OP_MATCH)) {
- dbg(rules->udev, "%s is true (matching value)\n", key_name);
+ dbg(rules->udev, "%s is true (matching value)\n", token_str[token->type]);
return 0;
}
if (!match && (token->key.op == OP_NOMATCH)) {
- dbg(rules->udev, "%s is true (non-matching value)\n", key_name);
+ dbg(rules->udev, "%s is true (non-matching value)\n", token_str[token->type]);
return 0;
}
- dbg(rules->udev, "%s is not true\n", key_name);
+ dbg(rules->udev, "%s is not true\n", token_str[token->type]);
return -1;
}
static int match_attr(struct udev_rules *rules, struct udev_device *dev, struct udev_event *event, struct token *cur)
{
- char attr[UTIL_PATH_SIZE];
const char *key_name = &rules->buf[cur->key.attr_off];
const char *key_value = &rules->buf[cur->key.value_off];
- char value[UTIL_NAME_SIZE] = "";
+ char value[UTIL_NAME_SIZE];
size_t len;
- util_strlcpy(attr, key_name, sizeof(attr));
- util_resolve_subsys_kernel(event->udev, attr, value, sizeof(value), 1);
+ value[0] = '\0';
+ if (key_name[0] == '[') {
+ char attr[UTIL_PATH_SIZE];
+
+ util_strlcpy(attr, key_name, sizeof(attr));
+ util_resolve_subsys_kernel(event->udev, attr, value, sizeof(value), 1);
+ }
if (value[0] == '\0') {
const char *val;
if (val != NULL)
util_strlcpy(value, val, sizeof(value));
}
- if (value[0]=='\0')
+ if (value[0] == '\0')
return -1;
/* strip trailing whitespace of value, if not asked to match for it */
{
struct token *cur;
struct token *rule;
+ enum escape_type esc = ESCAPE_UNSET;
if (rules->tokens == NULL)
return -1;
/* loop through token list, match, run actions or forward to next rule */
cur = &rules->tokens[0];
rule = cur;
- while (cur != NULL && cur->type != TK_END) {
- enum escape_type esc = ESCAPE_UNSET;
+ while (1) {
unsigned int idx;
dump_token(rules, cur);
cur = &rules->tokens[cur->key.rule_goto];
continue;
case TK_A_LAST_RULE:
+ case TK_END:
break;
case TK_M_PARENTS_MAX:
case TK_M_MAX:
- case TK_END:
case TK_UNSET:
err(rules->udev, "wrong type %u\n", cur->type);
goto nomatch;