/*
* Copyright (C) 2003-2012 Kay Sievers <kay@vrfy.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stddef.h>
#include <limits.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <ctype.h>
#include <unistd.h>
#include <errno.h>
#include <dirent.h>
#include <fnmatch.h>
#include <time.h>
#include <sys/sysmacros.h>
#include "udev.h"
#include "path-util.h"
#include "conf-files.h"
#include "strbuf.h"
#include "strv.h"
#include "util.h"
#include "sysctl-util.h"
#define PREALLOC_TOKEN 2048
struct uid_gid {
unsigned int name_off;
union {
uid_t uid;
gid_t gid;
};
};
static const char* const rules_dirs[] = {
UDEV_CONF_DIR "/rules.d",
UDEV_RULES_DIR,
UDEV_ROOT_RUN "/udev/rules.d",
UDEV_LIBEXEC_DIR "/rules.d",
#ifdef HAVE_SPLIT_USR
"/lib/udev/rules.d",
"/usr/lib/udev/rules.d",
#endif
NULL};
struct udev_rules {
struct udev *udev;
usec_t dirs_ts_usec;
int resolve_names;
/* every key in the rules file becomes a token */
struct token *tokens;
unsigned int token_cur;
unsigned int token_max;
/* all key strings are copied and de-duplicated in a single continuous string buffer */
struct strbuf *strbuf;
/* during rule parsing, uid/gid lookup results are cached */
struct uid_gid *uids;
unsigned int uids_cur;
unsigned int uids_max;
struct uid_gid *gids;
unsigned int gids_cur;
unsigned int gids_max;
};
static char *rules_str(struct udev_rules *rules, unsigned int off) {
return rules->strbuf->buf + off;
}
static unsigned int rules_add_string(struct udev_rules *rules, const char *s) {
return strbuf_add_string(rules->strbuf, s, strlen(s));
}
/* KEY=="", KEY!="", KEY+="", KEY-="", KEY="", KEY:="" */
enum operation_type {
OP_UNSET,
OP_MATCH,
OP_NOMATCH,
OP_MATCH_MAX,
OP_ADD,
OP_REMOVE,
OP_ASSIGN,
OP_ASSIGN_FINAL,
};
enum string_glob_type {
GL_UNSET,
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 "?*" */
};
enum string_subst_type {
SB_UNSET,
SB_NONE,
SB_FORMAT,
SB_SUBSYS,
};
/* tokens of a rule are sorted/handled in this order */
enum token_type {
TK_UNSET,
TK_RULE,
TK_M_ACTION, /* val */
TK_M_DEVPATH, /* val */
TK_M_KERNEL, /* val */
TK_M_DEVLINK, /* val */
TK_M_NAME, /* val */
TK_M_ENV, /* val, attr */
TK_M_TAG, /* val */
TK_M_SUBSYSTEM, /* val */
TK_M_DRIVER, /* val */
TK_M_WAITFOR, /* val */
TK_M_ATTR, /* val, attr */
TK_M_SYSCTL, /* val, attr */
TK_M_PARENTS_MIN,
TK_M_KERNELS, /* val */
TK_M_SUBSYSTEMS, /* val */
TK_M_DRIVERS, /* val */
TK_M_ATTRS, /* val, attr */
TK_M_TAGS, /* val */
TK_M_PARENTS_MAX,
TK_M_TEST, /* val, mode_t */
TK_M_PROGRAM, /* val */
TK_M_IMPORT_FILE, /* val */
TK_M_IMPORT_PROG, /* val */
TK_M_IMPORT_BUILTIN, /* val */
TK_M_IMPORT_DB, /* val */
TK_M_IMPORT_CMDLINE, /* val */
TK_M_IMPORT_PARENT, /* val */
TK_M_RESULT, /* val */
TK_M_MAX,
TK_A_STRING_ESCAPE_NONE,
TK_A_STRING_ESCAPE_REPLACE,
TK_A_DB_PERSIST,
TK_A_INOTIFY_WATCH, /* int */
TK_A_DEVLINK_PRIO, /* int */
TK_A_OWNER, /* val */
TK_A_GROUP, /* val */
TK_A_MODE, /* val */
TK_A_OWNER_ID, /* uid_t */
TK_A_GROUP_ID, /* gid_t */
TK_A_MODE_ID, /* mode_t */
TK_A_TAG, /* val */
TK_A_STATIC_NODE, /* val */
TK_A_SECLABEL, /* val, attr */
TK_A_ENV, /* val, attr */
TK_A_NAME, /* val */
TK_A_DEVLINK, /* val */
TK_A_ATTR, /* val, attr */
TK_A_SYSCTL, /* val, attr */
TK_A_RUN_BUILTIN, /* val, bool */
TK_A_RUN_PROGRAM, /* val, bool */
TK_A_GOTO, /* size_t */
TK_END,
};
/* we try to pack stuff in a way that we take only 12 bytes per token */
struct token {
union {
unsigned char type; /* same in rule and key */
struct {
enum token_type type:8;
bool can_set_name:1;
bool has_static_node:1;
unsigned int unused:6;
unsigned short token_count;
unsigned int label_off;
unsigned short filename_off;
unsigned short filename_line;
} rule;
struct {
enum token_type type:8;
enum operation_type op:8;
enum string_glob_type glob:8;
enum string_subst_type subst:4;
enum string_subst_type attrsubst:4;
unsigned int value_off;
union {
unsigned int attr_off;
unsigned int rule_goto;
mode_t mode;
uid_t uid;
gid_t gid;
int devlink_prio;
int watch;
enum udev_builtin_cmd builtin_cmd;
};
} key;
};
};
#define MAX_TK 64
struct rule_tmp {
struct udev_rules *rules;
struct token rule;
struct token token[MAX_TK];
unsigned int token_cur;
};
#ifdef DEBUG
static const char *operation_str(enum operation_type type) {
static const char *operation_strs[] = {
[OP_UNSET] = "UNSET",
[OP_MATCH] = "match",
[OP_NOMATCH] = "nomatch",
[OP_MATCH_MAX] = "MATCH_MAX",
[OP_ADD] = "add",
[OP_REMOVE] = "remove",
[OP_ASSIGN] = "assign",
[OP_ASSIGN_FINAL] =