LCOV - code coverage report
Current view: top level - udev - udev-rules.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 0 1407 0.0 %
Date: 2019-08-22 15:41:25 Functions: 0 36 0.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: GPL-2.0+ */
       2             : 
       3             : #include <ctype.h>
       4             : 
       5             : #include "alloc-util.h"
       6             : #include "conf-files.h"
       7             : #include "def.h"
       8             : #include "device-util.h"
       9             : #include "dirent-util.h"
      10             : #include "escape.h"
      11             : #include "fd-util.h"
      12             : #include "fileio.h"
      13             : #include "format-util.h"
      14             : #include "fs-util.h"
      15             : #include "glob-util.h"
      16             : #include "libudev-util.h"
      17             : #include "list.h"
      18             : #include "mkdir.h"
      19             : #include "nulstr-util.h"
      20             : #include "parse-util.h"
      21             : #include "path-util.h"
      22             : #include "proc-cmdline.h"
      23             : #include "stat-util.h"
      24             : #include "strv.h"
      25             : #include "strxcpyx.h"
      26             : #include "sysctl-util.h"
      27             : #include "udev-builtin.h"
      28             : #include "udev-event.h"
      29             : #include "udev-rules.h"
      30             : #include "user-util.h"
      31             : 
      32             : #define RULES_DIRS (const char* const*) CONF_PATHS_STRV("udev/rules.d")
      33             : 
      34             : typedef enum {
      35             :         OP_MATCH,        /* == */
      36             :         OP_NOMATCH,      /* != */
      37             :         OP_ADD,          /* += */
      38             :         OP_REMOVE,       /* -= */
      39             :         OP_ASSIGN,       /* = */
      40             :         OP_ASSIGN_FINAL, /* := */
      41             :         _OP_TYPE_MAX,
      42             :         _OP_TYPE_INVALID = -1
      43             : } UdevRuleOperatorType;
      44             : 
      45             : typedef enum {
      46             :         MATCH_TYPE_EMPTY,     /* empty string */
      47             :         MATCH_TYPE_PLAIN,     /* no special characters */
      48             :         MATCH_TYPE_GLOB,      /* shell globs ?,*,[] */
      49             :         MATCH_TYPE_SUBSYSTEM, /* "subsystem", "bus", or "class" */
      50             :         _MATCH_TYPE_MAX,
      51             :         _MATCH_TYPE_INVALID = -1
      52             : } UdevRuleMatchType;
      53             : 
      54             : typedef enum {
      55             :         SUBST_TYPE_PLAIN,  /* no substitution */
      56             :         SUBST_TYPE_FORMAT, /* % or $ */
      57             :         SUBST_TYPE_SUBSYS, /* "[<SUBSYSTEM>/<KERNEL>]<attribute>" format */
      58             :         _SUBST_TYPE_MAX,
      59             :         _SUBST_TYPE_INVALID = -1
      60             : } UdevRuleSubstituteType;
      61             : 
      62             : typedef enum {
      63             :         /* lvalues which take match or nomatch operator */
      64             :         TK_M_ACTION,                        /* string, device_get_action() */
      65             :         TK_M_DEVPATH,                       /* path, sd_device_get_devpath() */
      66             :         TK_M_KERNEL,                        /* string, sd_device_get_sysname() */
      67             :         TK_M_DEVLINK,                       /* strv, sd_device_get_devlink_first(), sd_device_get_devlink_next() */
      68             :         TK_M_NAME,                          /* string, name of network interface */
      69             :         TK_M_ENV,                           /* string, device property, takes key through attribute */
      70             :         TK_M_TAG,                           /* strv, sd_device_get_tag_first(), sd_device_get_tag_next() */
      71             :         TK_M_SUBSYSTEM,                     /* string, sd_device_get_subsystem() */
      72             :         TK_M_DRIVER,                        /* string, sd_device_get_driver() */
      73             :         TK_M_ATTR,                          /* string, takes filename through attribute, sd_device_get_sysattr_value(), util_resolve_subsys_kernel(), etc. */
      74             :         TK_M_SYSCTL,                        /* string, takes kernel parameter through attribute */
      75             : 
      76             :         /* matches parent paramters */
      77             :         TK_M_PARENTS_KERNEL,                /* string */
      78             :         TK_M_PARENTS_SUBSYSTEM,             /* string */
      79             :         TK_M_PARENTS_DRIVER,                /* string */
      80             :         TK_M_PARENTS_ATTR,                  /* string */
      81             :         TK_M_PARENTS_TAG,                   /* strv */
      82             : 
      83             :         TK_M_TEST,                          /* path, optionally mode_t can be specified by attribute, test the existence of a file */
      84             :         TK_M_PROGRAM,                       /* string, execute a program */
      85             :         TK_M_IMPORT_FILE,                   /* path */
      86             :         TK_M_IMPORT_PROGRAM,                /* string, import properties from the result of program */
      87             :         TK_M_IMPORT_BUILTIN,                /* string, import properties from the result of built-in command */
      88             :         TK_M_IMPORT_DB,                     /* string, import properties from database */
      89             :         TK_M_IMPORT_CMDLINE,                /* string, kernel command line */
      90             :         TK_M_IMPORT_PARENT,                 /* string, parent property */
      91             :         TK_M_RESULT,                        /* string, result of TK_M_PROGRAM */
      92             : 
      93             : #define _TK_M_MAX (TK_M_RESULT + 1)
      94             : #define _TK_A_MIN _TK_M_MAX
      95             : 
      96             :         /* lvalues which take one of assign operators */
      97             :         TK_A_OPTIONS_STRING_ESCAPE_NONE,    /* no argument */
      98             :         TK_A_OPTIONS_STRING_ESCAPE_REPLACE, /* no argument */
      99             :         TK_A_OPTIONS_DB_PERSIST,            /* no argument */
     100             :         TK_A_OPTIONS_INOTIFY_WATCH,         /* boolean */
     101             :         TK_A_OPTIONS_DEVLINK_PRIORITY,      /* int */
     102             :         TK_A_OWNER,                         /* user name */
     103             :         TK_A_GROUP,                         /* group name */
     104             :         TK_A_MODE,                          /* mode string */
     105             :         TK_A_OWNER_ID,                      /* uid_t */
     106             :         TK_A_GROUP_ID,                      /* gid_t */
     107             :         TK_A_MODE_ID,                       /* mode_t */
     108             :         TK_A_TAG,                           /* string */
     109             :         TK_A_OPTIONS_STATIC_NODE,           /* device path, /dev/... */
     110             :         TK_A_SECLABEL,                      /* string with attribute */
     111             :         TK_A_ENV,                           /* string with attribute */
     112             :         TK_A_NAME,                          /* ifname */
     113             :         TK_A_DEVLINK,                       /* string */
     114             :         TK_A_ATTR,                          /* string with attribute */
     115             :         TK_A_SYSCTL,                        /* string with attribute */
     116             :         TK_A_RUN_BUILTIN,                   /* string */
     117             :         TK_A_RUN_PROGRAM,                   /* string */
     118             : 
     119             :         _TK_TYPE_MAX,
     120             :         _TK_TYPE_INVALID = -1,
     121             : } UdevRuleTokenType;
     122             : 
     123             : typedef enum {
     124             :         LINE_HAS_NAME         = 1 << 0, /* has NAME= */
     125             :         LINE_HAS_DEVLINK      = 1 << 1, /* has SYMLINK=, OWNER=, GROUP= or MODE= */
     126             :         LINE_HAS_STATIC_NODE  = 1 << 2, /* has OPTIONS=static_node */
     127             :         LINE_HAS_GOTO         = 1 << 3, /* has GOTO= */
     128             :         LINE_HAS_LABEL        = 1 << 4, /* has LABEL= */
     129             :         LINE_UPDATE_SOMETHING = 1 << 5, /* has other TK_A_* or TK_M_IMPORT tokens */
     130             : } UdevRuleLineType;
     131             : 
     132             : typedef struct UdevRuleFile UdevRuleFile;
     133             : typedef struct UdevRuleLine UdevRuleLine;
     134             : typedef struct UdevRuleToken UdevRuleToken;
     135             : 
     136             : struct UdevRuleToken {
     137             :         UdevRuleTokenType type:8;
     138             :         UdevRuleOperatorType op:8;
     139             :         UdevRuleMatchType match_type:8;
     140             :         UdevRuleSubstituteType attr_subst_type:7;
     141             :         bool attr_match_remove_trailing_whitespace:1;
     142             :         const char *value;
     143             :         void *data;
     144             :         LIST_FIELDS(UdevRuleToken, tokens);
     145             : };
     146             : 
     147             : struct UdevRuleLine {
     148             :         char *line;
     149             :         unsigned line_number;
     150             :         UdevRuleLineType type;
     151             : 
     152             :         const char *label;
     153             :         const char *goto_label;
     154             :         UdevRuleLine *goto_line;
     155             : 
     156             :         UdevRuleFile *rule_file;
     157             :         UdevRuleToken *current_token;
     158             :         LIST_HEAD(UdevRuleToken, tokens);
     159             :         LIST_FIELDS(UdevRuleLine, rule_lines);
     160             : };
     161             : 
     162             : struct UdevRuleFile {
     163             :         char *filename;
     164             :         UdevRuleLine *current_line;
     165             :         LIST_HEAD(UdevRuleLine, rule_lines);
     166             :         LIST_FIELDS(UdevRuleFile, rule_files);
     167             : };
     168             : 
     169             : struct UdevRules {
     170             :         usec_t dirs_ts_usec;
     171             :         ResolveNameTiming resolve_name_timing;
     172             :         Hashmap *known_users;
     173             :         Hashmap *known_groups;
     174             :         UdevRuleFile *current_file;
     175             :         LIST_HEAD(UdevRuleFile, rule_files);
     176             : };
     177             : 
     178             : /*** Logging helpers ***/
     179             : 
     180             : #define log_rule_full(device, rules, level, error, fmt, ...)            \
     181             :         ({                                                              \
     182             :                 UdevRules *_r = (rules);                                \
     183             :                 UdevRuleFile *_f = _r ? _r->current_file : NULL;        \
     184             :                 UdevRuleLine *_l = _f ? _f->current_line : NULL;        \
     185             :                 const char *_n = _f ? _f->filename : NULL;              \
     186             :                                                                         \
     187             :                 log_device_full(device, level, error, "%s:%u " fmt,     \
     188             :                                 strna(_n), _l ? _l->line_number : 0,    \
     189             :                                 ##__VA_ARGS__);                         \
     190             :         })
     191             : 
     192             : #define log_rule_debug(device, rules, ...)   log_rule_full(device, rules, LOG_DEBUG, 0, ##__VA_ARGS__)
     193             : #define log_rule_info(device, rules, ...)    log_rule_full(device, rules, LOG_INFO, 0, ##__VA_ARGS__)
     194             : #define log_rule_notice(device, rules, ...)  log_rule_full(device, rules, LOG_NOTICE, 0, ##__VA_ARGS__)
     195             : #define log_rule_warning(device, rules, ...) log_rule_full(device, rules, LOG_WARNING, 0, ##__VA_ARGS__)
     196             : #define log_rule_error(device, rules, ...)   log_rule_full(device, rules, LOG_ERR, 0, ##__VA_ARGS__)
     197             : 
     198             : #define log_rule_debug_errno(device, rules, error, ...)   log_rule_full(device, rules, LOG_DEBUG, error, ##__VA_ARGS__)
     199             : #define log_rule_info_errno(device, rules, error, ...)    log_rule_full(device, rules, LOG_INFO, error, ##__VA_ARGS__)
     200             : #define log_rule_notice_errno(device, rules, error, ...)  log_rule_full(device, rules, LOG_NOTICE, error, ##__VA_ARGS__)
     201             : #define log_rule_warning_errno(device, rules, error, ...) log_rule_full(device, rules, LOG_WARNING, error, ##__VA_ARGS__)
     202             : #define log_rule_error_errno(device, rules, error, ...)   log_rule_full(device, rules, LOG_ERR, error, ##__VA_ARGS__)
     203             : 
     204             : #define log_token_full(rules, ...) log_rule_full(NULL, rules, ##__VA_ARGS__)
     205             : 
     206             : #define log_token_debug(rules, ...)   log_token_full(rules, LOG_DEBUG, 0, ##__VA_ARGS__)
     207             : #define log_token_info(rules, ...)    log_token_full(rules, LOG_INFO, 0, ##__VA_ARGS__)
     208             : #define log_token_notice(rules, ...)  log_token_full(rules, LOG_NOTICE, 0, ##__VA_ARGS__)
     209             : #define log_token_warning(rules, ...) log_token_full(rules, LOG_WARNING, 0, ##__VA_ARGS__)
     210             : #define log_token_error(rules, ...)   log_token_full(rules, LOG_ERR, 0, ##__VA_ARGS__)
     211             : 
     212             : #define log_token_debug_errno(rules, error, ...)   log_token_full(rules, LOG_DEBUG, error, ##__VA_ARGS__)
     213             : #define log_token_info_errno(rules, error, ...)    log_token_full(rules, LOG_INFO, error, ##__VA_ARGS__)
     214             : #define log_token_notice_errno(rules, error, ...)  log_token_full(rules, LOG_NOTICE, error, ##__VA_ARGS__)
     215             : #define log_token_warning_errno(rules, error, ...) log_token_full(rules, LOG_WARNING, error, ##__VA_ARGS__)
     216             : #define log_token_error_errno(rules, error, ...)   log_token_full(rules, LOG_ERR, error, ##__VA_ARGS__)
     217             : 
     218             : #define _log_token_invalid(rules, key, type)                      \
     219             :         log_token_error_errno(rules, SYNTHETIC_ERRNO(EINVAL),     \
     220             :                               "Invalid %s for %s.", type, key)
     221             : 
     222             : #define log_token_invalid_op(rules, key)   _log_token_invalid(rules, key, "operator")
     223             : #define log_token_invalid_attr(rules, key) _log_token_invalid(rules, key, "attribute")
     224             : 
     225             : #define log_token_invalid_attr_format(rules, key, attr, offset, hint)   \
     226             :         log_token_error_errno(rules, SYNTHETIC_ERRNO(EINVAL),           \
     227             :                               "Invalid attribute \"%s\" for %s (char %zu: %s), ignoring, but please fix it.", \
     228             :                               attr, key, offset, hint)
     229             : #define log_token_invalid_value(rules, key, value, offset, hint)        \
     230             :         log_token_error_errno(rules, SYNTHETIC_ERRNO(EINVAL),           \
     231             :                               "Invalid value \"%s\" for %s (char %zu: %s), ignoring, but please fix it.", \
     232             :                               value, key, offset, hint)
     233             : 
     234           0 : static void log_unknown_owner(sd_device *dev, UdevRules *rules, int error, const char *entity, const char *name) {
     235           0 :         if (IN_SET(abs(error), ENOENT, ESRCH))
     236           0 :                 log_rule_error(dev, rules, "Unknown %s '%s', ignoring", entity, name);
     237             :         else
     238           0 :                 log_rule_error_errno(dev, rules, error, "Failed to resolve %s '%s', ignoring: %m", entity, name);
     239           0 : }
     240             : 
     241             : /*** Other functions ***/
     242             : 
     243           0 : static void udev_rule_token_free(UdevRuleToken *token) {
     244           0 :         free(token);
     245           0 : }
     246             : 
     247           0 : static void udev_rule_line_clear_tokens(UdevRuleLine *rule_line) {
     248             :         UdevRuleToken *i, *next;
     249             : 
     250           0 :         assert(rule_line);
     251             : 
     252           0 :         LIST_FOREACH_SAFE(tokens, i, next, rule_line->tokens)
     253           0 :                 udev_rule_token_free(i);
     254             : 
     255           0 :         rule_line->tokens = NULL;
     256           0 : }
     257             : 
     258           0 : static void udev_rule_line_free(UdevRuleLine *rule_line) {
     259           0 :         if (!rule_line)
     260           0 :                 return;
     261             : 
     262           0 :         udev_rule_line_clear_tokens(rule_line);
     263             : 
     264           0 :         if (rule_line->rule_file) {
     265           0 :                 if (rule_line->rule_file->current_line == rule_line)
     266           0 :                         rule_line->rule_file->current_line = rule_line->rule_lines_prev;
     267             : 
     268           0 :                 LIST_REMOVE(rule_lines, rule_line->rule_file->rule_lines, rule_line);
     269             :         }
     270             : 
     271           0 :         free(rule_line->line);
     272           0 :         free(rule_line);
     273             : }
     274             : 
     275           0 : DEFINE_TRIVIAL_CLEANUP_FUNC(UdevRuleLine*, udev_rule_line_free);
     276             : 
     277           0 : static void udev_rule_file_free(UdevRuleFile *rule_file) {
     278             :         UdevRuleLine *i, *next;
     279             : 
     280           0 :         if (!rule_file)
     281           0 :                 return;
     282             : 
     283           0 :         LIST_FOREACH_SAFE(rule_lines, i, next, rule_file->rule_lines)
     284           0 :                 udev_rule_line_free(i);
     285             : 
     286           0 :         free(rule_file->filename);
     287           0 :         free(rule_file);
     288             : }
     289             : 
     290           0 : UdevRules *udev_rules_free(UdevRules *rules) {
     291             :         UdevRuleFile *i, *next;
     292             : 
     293           0 :         if (!rules)
     294           0 :                 return NULL;
     295             : 
     296           0 :         LIST_FOREACH_SAFE(rule_files, i, next, rules->rule_files)
     297           0 :                 udev_rule_file_free(i);
     298             : 
     299           0 :         hashmap_free_free_key(rules->known_users);
     300           0 :         hashmap_free_free_key(rules->known_groups);
     301           0 :         return mfree(rules);
     302             : }
     303             : 
     304           0 : static int rule_resolve_user(UdevRules *rules, const char *name, uid_t *ret) {
     305           0 :         _cleanup_free_ char *n = NULL;
     306             :         uid_t uid;
     307             :         void *val;
     308             :         int r;
     309             : 
     310           0 :         assert(rules);
     311           0 :         assert(name);
     312             : 
     313           0 :         val = hashmap_get(rules->known_users, name);
     314           0 :         if (val) {
     315           0 :                 *ret = PTR_TO_UID(val);
     316           0 :                 return 0;
     317             :         }
     318             : 
     319           0 :         r = get_user_creds(&name, &uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
     320           0 :         if (r < 0) {
     321           0 :                 log_unknown_owner(NULL, rules, r, "user", name);
     322           0 :                 *ret = UID_INVALID;
     323           0 :                 return 0;
     324             :         }
     325             : 
     326           0 :         n = strdup(name);
     327           0 :         if (!n)
     328           0 :                 return -ENOMEM;
     329             : 
     330           0 :         r = hashmap_ensure_allocated(&rules->known_users, &string_hash_ops);
     331           0 :         if (r < 0)
     332           0 :                 return r;
     333             : 
     334           0 :         r = hashmap_put(rules->known_users, n, UID_TO_PTR(uid));
     335           0 :         if (r < 0)
     336           0 :                 return r;
     337             : 
     338           0 :         TAKE_PTR(n);
     339           0 :         *ret = uid;
     340           0 :         return 0;
     341             : }
     342             : 
     343           0 : static int rule_resolve_group(UdevRules *rules, const char *name, gid_t *ret) {
     344           0 :         _cleanup_free_ char *n = NULL;
     345             :         gid_t gid;
     346             :         void *val;
     347             :         int r;
     348             : 
     349           0 :         assert(rules);
     350           0 :         assert(name);
     351             : 
     352           0 :         val = hashmap_get(rules->known_groups, name);
     353           0 :         if (val) {
     354           0 :                 *ret = PTR_TO_GID(val);
     355           0 :                 return 0;
     356             :         }
     357             : 
     358           0 :         r = get_group_creds(&name, &gid, USER_CREDS_ALLOW_MISSING);
     359           0 :         if (r < 0) {
     360           0 :                 log_unknown_owner(NULL, rules, r, "group", name);
     361           0 :                 *ret = GID_INVALID;
     362           0 :                 return 0;
     363             :         }
     364             : 
     365           0 :         n = strdup(name);
     366           0 :         if (!n)
     367           0 :                 return -ENOMEM;
     368             : 
     369           0 :         r = hashmap_ensure_allocated(&rules->known_groups, &string_hash_ops);
     370           0 :         if (r < 0)
     371           0 :                 return r;
     372             : 
     373           0 :         r = hashmap_put(rules->known_groups, n, GID_TO_PTR(gid));
     374           0 :         if (r < 0)
     375           0 :                 return r;
     376             : 
     377           0 :         TAKE_PTR(n);
     378           0 :         *ret = gid;
     379           0 :         return 0;
     380             : }
     381             : 
     382           0 : static UdevRuleSubstituteType rule_get_substitution_type(const char *str) {
     383           0 :         assert(str);
     384             : 
     385           0 :         if (str[0] == '[')
     386           0 :                 return SUBST_TYPE_SUBSYS;
     387           0 :         if (strchr(str, '%') || strchr(str, '$'))
     388           0 :                 return SUBST_TYPE_FORMAT;
     389           0 :         return SUBST_TYPE_PLAIN;
     390             : }
     391             : 
     392           0 : static void rule_line_append_token(UdevRuleLine *rule_line, UdevRuleToken *token) {
     393           0 :         assert(rule_line);
     394           0 :         assert(token);
     395             : 
     396           0 :         if (rule_line->current_token)
     397           0 :                 LIST_APPEND(tokens, rule_line->current_token, token);
     398             :         else
     399           0 :                 LIST_APPEND(tokens, rule_line->tokens, token);
     400             : 
     401           0 :         rule_line->current_token = token;
     402           0 : }
     403             : 
     404           0 : static int rule_line_add_token(UdevRuleLine *rule_line, UdevRuleTokenType type, UdevRuleOperatorType op, char *value, void *data) {
     405             :         UdevRuleToken *token;
     406           0 :         UdevRuleMatchType match_type = _MATCH_TYPE_INVALID;
     407           0 :         UdevRuleSubstituteType subst_type = _SUBST_TYPE_INVALID;
     408           0 :         bool remove_trailing_whitespace = false;
     409             :         size_t len;
     410             : 
     411           0 :         assert(rule_line);
     412           0 :         assert(type >= 0 && type < _TK_TYPE_MAX);
     413           0 :         assert(op >= 0 && op < _OP_TYPE_MAX);
     414             : 
     415           0 :         if (type < _TK_M_MAX) {
     416           0 :                 assert(value);
     417           0 :                 assert(IN_SET(op, OP_MATCH, OP_NOMATCH));
     418             : 
     419           0 :                 if (type == TK_M_SUBSYSTEM && STR_IN_SET(value, "subsystem", "bus", "class"))
     420           0 :                         match_type = MATCH_TYPE_SUBSYSTEM;
     421           0 :                 else if (isempty(value))
     422           0 :                         match_type = MATCH_TYPE_EMPTY;
     423           0 :                 else if (streq(value, "?*")) {
     424             :                         /* Convert KEY=="?*" -> KEY!="" */
     425           0 :                         match_type = MATCH_TYPE_EMPTY;
     426           0 :                         op = op == OP_MATCH ? OP_NOMATCH : OP_MATCH;
     427           0 :                 } else if (string_is_glob(value))
     428           0 :                         match_type = MATCH_TYPE_GLOB;
     429             :                 else
     430           0 :                         match_type = MATCH_TYPE_PLAIN;
     431             : 
     432           0 :                 if (type < TK_M_TEST || type == TK_M_RESULT) {
     433             :                         /* Convert value string to nulstr. */
     434           0 :                         len = strlen(value);
     435           0 :                         if (len > 1 && (value[len - 1] == '|' || strstr(value, "||"))) {
     436             :                                 /* In this case, just replacing '|' -> '\0' does not work... */
     437           0 :                                 _cleanup_free_ char *tmp = NULL;
     438             :                                 char *i, *j;
     439           0 :                                 bool v = true;
     440             : 
     441           0 :                                 tmp = strdup(value);
     442           0 :                                 if (!tmp)
     443           0 :                                         return log_oom();
     444             : 
     445           0 :                                 for (i = tmp, j = value; *i != '\0'; i++)
     446           0 :                                         if (*i == '|')
     447           0 :                                                 v = true;
     448             :                                         else {
     449           0 :                                                 if (v) {
     450           0 :                                                         *j++ = '\0';
     451           0 :                                                         v = false;
     452             :                                                 }
     453           0 :                                                 *j++ = *i;
     454             :                                         }
     455           0 :                                 j[0] = j[1] = '\0';
     456             :                         } else {
     457             :                                 /* Simple conversion. */
     458             :                                 char *i;
     459             : 
     460           0 :                                 for (i = value; *i != '\0'; i++)
     461           0 :                                         if (*i == '|')
     462           0 :                                                 *i = '\0';
     463             :                         }
     464             :                 }
     465             :         }
     466             : 
     467           0 :         if (IN_SET(type, TK_M_ATTR, TK_M_PARENTS_ATTR)) {
     468           0 :                 assert(value);
     469           0 :                 assert(data);
     470             : 
     471           0 :                 len = strlen(value);
     472           0 :                 if (len > 0 && !isspace(value[len - 1]))
     473           0 :                         remove_trailing_whitespace = true;
     474             : 
     475           0 :                 subst_type = rule_get_substitution_type((const char*) data);
     476             :         }
     477             : 
     478           0 :         token = new(UdevRuleToken, 1);
     479           0 :         if (!token)
     480           0 :                 return -ENOMEM;
     481             : 
     482           0 :         *token = (UdevRuleToken) {
     483             :                 .type = type,
     484             :                 .op = op,
     485             :                 .value = value,
     486             :                 .data = data,
     487             :                 .match_type = match_type,
     488             :                 .attr_subst_type = subst_type,
     489             :                 .attr_match_remove_trailing_whitespace = remove_trailing_whitespace,
     490             :         };
     491             : 
     492           0 :         rule_line_append_token(rule_line, token);
     493             : 
     494           0 :         if (token->type == TK_A_NAME)
     495           0 :                 SET_FLAG(rule_line->type, LINE_HAS_NAME, true);
     496             : 
     497           0 :         else if (IN_SET(token->type, TK_A_DEVLINK,
     498             :                         TK_A_OWNER, TK_A_GROUP, TK_A_MODE,
     499             :                         TK_A_OWNER_ID, TK_A_GROUP_ID, TK_A_MODE_ID))
     500           0 :                 SET_FLAG(rule_line->type, LINE_HAS_DEVLINK, true);
     501             : 
     502           0 :         else if (token->type >= _TK_A_MIN ||
     503           0 :                  IN_SET(token->type, TK_M_PROGRAM,
     504             :                         TK_M_IMPORT_FILE, TK_M_IMPORT_PROGRAM, TK_M_IMPORT_BUILTIN,
     505             :                         TK_M_IMPORT_DB, TK_M_IMPORT_CMDLINE, TK_M_IMPORT_PARENT))
     506           0 :                 SET_FLAG(rule_line->type, LINE_UPDATE_SOMETHING, true);
     507             : 
     508           0 :         return 0;
     509             : }
     510             : 
     511           0 : static void check_value_format_and_warn(UdevRules *rules, const char *key, const char *value, bool nonempty) {
     512             :         size_t offset;
     513             :         const char *hint;
     514             : 
     515           0 :         if (nonempty && isempty(value))
     516           0 :                 log_token_invalid_value(rules, key, value, (size_t) 0, "empty value");
     517           0 :         else if (udev_check_format(value, &offset, &hint) < 0)
     518           0 :                 log_token_invalid_value(rules, key, value, offset + 1, hint);
     519           0 : }
     520             : 
     521           0 : static int check_attr_format_and_warn(UdevRules *rules, const char *key, const char *value) {
     522             :         size_t offset;
     523             :         const char *hint;
     524             : 
     525           0 :         if (isempty(value))
     526           0 :                 return log_token_invalid_attr(rules, key);
     527           0 :         if (udev_check_format(value, &offset, &hint) < 0)
     528           0 :                 log_token_invalid_attr_format(rules, key, value, offset + 1, hint);
     529           0 :         return 0;
     530             : }
     531             : 
     532           0 : static int parse_token(UdevRules *rules, const char *key, char *attr, UdevRuleOperatorType op, char *value) {
     533           0 :         bool is_match = IN_SET(op, OP_MATCH, OP_NOMATCH);
     534             :         UdevRuleLine *rule_line;
     535             :         int r;
     536             : 
     537           0 :         assert(rules);
     538           0 :         assert(rules->current_file);
     539           0 :         assert(rules->current_file->current_line);
     540           0 :         assert(key);
     541           0 :         assert(value);
     542             : 
     543           0 :         rule_line = rules->current_file->current_line;
     544             : 
     545           0 :         if (streq(key, "ACTION")) {
     546           0 :                 if (attr)
     547           0 :                         return log_token_invalid_attr(rules, key);
     548           0 :                 if (!is_match)
     549           0 :                         return log_token_invalid_op(rules, key);
     550             : 
     551           0 :                 r = rule_line_add_token(rule_line, TK_M_ACTION, op, value, NULL);
     552           0 :         } else if (streq(key, "DEVPATH")) {
     553           0 :                 if (attr)
     554           0 :                         return log_token_invalid_attr(rules, key);
     555           0 :                 if (!is_match)
     556           0 :                         return log_token_invalid_op(rules, key);
     557             : 
     558           0 :                 r = rule_line_add_token(rule_line, TK_M_DEVPATH, op, value, NULL);
     559           0 :         } else if (streq(key, "KERNEL")) {
     560           0 :                 if (attr)
     561           0 :                         return log_token_invalid_attr(rules, key);
     562           0 :                 if (!is_match)
     563           0 :                         return log_token_invalid_op(rules, key);
     564             : 
     565           0 :                 r = rule_line_add_token(rule_line, TK_M_KERNEL, op, value, NULL);
     566           0 :         } else if (streq(key, "SYMLINK")) {
     567           0 :                 if (attr)
     568           0 :                         return log_token_invalid_attr(rules, key);
     569           0 :                 if (op == OP_REMOVE)
     570           0 :                         return log_token_invalid_op(rules, key);
     571             : 
     572           0 :                 if (!is_match) {
     573           0 :                         check_value_format_and_warn(rules, key, value, false);
     574           0 :                         r = rule_line_add_token(rule_line, TK_A_DEVLINK, op, value, NULL);
     575             :                 } else
     576           0 :                         r = rule_line_add_token(rule_line, TK_M_DEVLINK, op, value, NULL);
     577           0 :         } else if (streq(key, "NAME")) {
     578           0 :                 if (attr)
     579           0 :                         return log_token_invalid_attr(rules, key);
     580           0 :                 if (op == OP_REMOVE)
     581           0 :                         return log_token_invalid_op(rules, key);
     582           0 :                 if (op == OP_ADD) {
     583           0 :                         log_token_warning(rules, "%s key takes '==', '!=', '=', or ':=' operator, assuming '=', but please fix it.", key);
     584           0 :                         op = OP_ASSIGN;
     585             :                 }
     586             : 
     587           0 :                 if (!is_match) {
     588           0 :                         if (streq(value, "%k"))
     589           0 :                                 return log_token_error_errno(rules, SYNTHETIC_ERRNO(EINVAL),
     590             :                                                              "Ignoring NAME=\"%%k\" is ignored, as it breaks kernel supplied names.");
     591           0 :                         if (isempty(value))
     592           0 :                                 return log_token_error_errno(rules, SYNTHETIC_ERRNO(EINVAL),
     593             :                                                              "Ignoring NAME=\"\", as udev will not delete any device nodes.");
     594           0 :                         check_value_format_and_warn(rules, key, value, false);
     595             : 
     596           0 :                         r = rule_line_add_token(rule_line, TK_A_NAME, op, value, NULL);
     597             :                 } else
     598           0 :                         r = rule_line_add_token(rule_line, TK_M_NAME, op, value, NULL);
     599           0 :         } else if (streq(key, "ENV")) {
     600           0 :                 if (isempty(attr))
     601           0 :                         return log_token_invalid_attr(rules, key);
     602           0 :                 if (op == OP_REMOVE)
     603           0 :                         return log_token_invalid_op(rules, key);
     604           0 :                 if (op == OP_ASSIGN_FINAL) {
     605           0 :                         log_token_warning(rules, "%s key takes '==', '!=', '=', or '+=' operator, assuming '=', but please fix it.", key);
     606           0 :                         op = OP_ASSIGN;
     607             :                 }
     608             : 
     609           0 :                 if (!is_match) {
     610           0 :                         if (STR_IN_SET(attr,
     611             :                                        "ACTION", "DEVLINKS", "DEVNAME", "DEVPATH", "DEVTYPE", "DRIVER",
     612             :                                        "IFINDEX", "MAJOR", "MINOR", "SEQNUM", "SUBSYSTEM", "TAGS"))
     613           0 :                                 return log_token_error_errno(rules, SYNTHETIC_ERRNO(EINVAL),
     614             :                                                              "Invalid ENV attribute. '%s' cannot be set.", attr);
     615             : 
     616           0 :                         check_value_format_and_warn(rules, key, value, false);
     617             : 
     618           0 :                         r = rule_line_add_token(rule_line, TK_A_ENV, op, value, attr);
     619             :                 } else
     620           0 :                         r = rule_line_add_token(rule_line, TK_M_ENV, op, value, attr);
     621           0 :         } else if (streq(key, "TAG")) {
     622           0 :                 if (attr)
     623           0 :                         return log_token_invalid_attr(rules, key);
     624           0 :                 if (op == OP_ASSIGN_FINAL) {
     625           0 :                         log_token_warning(rules, "%s key takes '==', '!=', '=', or '+=' operator, assuming '=', but please fix it.", key);
     626           0 :                         op = OP_ASSIGN;
     627             :                 }
     628             : 
     629           0 :                 if (!is_match) {
     630           0 :                         check_value_format_and_warn(rules, key, value, true);
     631             : 
     632           0 :                         r = rule_line_add_token(rule_line, TK_A_TAG, op, value, NULL);
     633             :                 } else
     634           0 :                         r = rule_line_add_token(rule_line, TK_M_TAG, op, value, NULL);
     635           0 :         } else if (streq(key, "SUBSYSTEM")) {
     636           0 :                 if (attr)
     637           0 :                         return log_token_invalid_attr(rules, key);
     638           0 :                 if (!is_match)
     639           0 :                         return log_token_invalid_op(rules, key);
     640             : 
     641           0 :                 if (STR_IN_SET(value, "bus", "class"))
     642           0 :                         log_token_warning(rules, "'%s' must be specified as 'subsystem'; please fix it", value);
     643             : 
     644           0 :                 r = rule_line_add_token(rule_line, TK_M_SUBSYSTEM, op, value, NULL);
     645           0 :         } else if (streq(key, "DRIVER")) {
     646           0 :                 if (attr)
     647           0 :                         return log_token_invalid_attr(rules, key);
     648           0 :                 if (!is_match)
     649           0 :                         return log_token_invalid_op(rules, key);
     650             : 
     651           0 :                 r = rule_line_add_token(rule_line, TK_M_DRIVER, op, value, NULL);
     652           0 :         } else if (streq(key, "ATTR")) {
     653           0 :                 r = check_attr_format_and_warn(rules, key, attr);
     654           0 :                 if (r < 0)
     655           0 :                         return r;
     656           0 :                 if (op == OP_REMOVE)
     657           0 :                         return log_token_invalid_op(rules, key);
     658           0 :                 if (IN_SET(op, OP_ADD, OP_ASSIGN_FINAL)) {
     659           0 :                         log_token_warning(rules, "%s key takes '==', '!=', or '=' operator, assuming '=', but please fix it.", key);
     660           0 :                         op = OP_ASSIGN;
     661             :                 }
     662             : 
     663           0 :                 if (!is_match) {
     664           0 :                         check_value_format_and_warn(rules, key, value, false);
     665           0 :                         r = rule_line_add_token(rule_line, TK_A_ATTR, op, value, attr);
     666             :                 } else
     667           0 :                         r = rule_line_add_token(rule_line, TK_M_ATTR, op, value, attr);
     668           0 :         } else if (streq(key, "SYSCTL")) {
     669           0 :                 r = check_attr_format_and_warn(rules, key, attr);
     670           0 :                 if (r < 0)
     671           0 :                         return r;
     672           0 :                 if (op == OP_REMOVE)
     673           0 :                         return log_token_invalid_op(rules, key);
     674           0 :                 if (IN_SET(op, OP_ADD, OP_ASSIGN_FINAL)) {
     675           0 :                         log_token_warning(rules, "%s key takes '==', '!=', or '=' operator, assuming '=', but please fix it.", key);
     676           0 :                         op = OP_ASSIGN;
     677             :                 }
     678             : 
     679           0 :                 if (!is_match) {
     680           0 :                         check_value_format_and_warn(rules, key, value, false);
     681           0 :                         r = rule_line_add_token(rule_line, TK_A_SYSCTL, op, value, attr);
     682             :                 } else
     683           0 :                         r = rule_line_add_token(rule_line, TK_M_SYSCTL, op, value, attr);
     684           0 :         } else if (streq(key, "KERNELS")) {
     685           0 :                 if (attr)
     686           0 :                         return log_token_invalid_attr(rules, key);
     687           0 :                 if (!is_match)
     688           0 :                         return log_token_invalid_op(rules, key);
     689             : 
     690           0 :                 r = rule_line_add_token(rule_line, TK_M_PARENTS_KERNEL, op, value, NULL);
     691           0 :         } else if (streq(key, "SUBSYSTEMS")) {
     692           0 :                 if (attr)
     693           0 :                         return log_token_invalid_attr(rules, key);
     694           0 :                 if (!is_match)
     695           0 :                         return log_token_invalid_op(rules, key);
     696             : 
     697           0 :                 r = rule_line_add_token(rule_line, TK_M_PARENTS_SUBSYSTEM, op, value, NULL);
     698           0 :         } else if (streq(key, "DRIVERS")) {
     699           0 :                 if (attr)
     700           0 :                         return log_token_invalid_attr(rules, key);
     701           0 :                 if (!is_match)
     702           0 :                         return log_token_invalid_op(rules, key);
     703             : 
     704           0 :                 r = rule_line_add_token(rule_line, TK_M_PARENTS_DRIVER, op, value, NULL);
     705           0 :         } else if (streq(key, "ATTRS")) {
     706           0 :                 r = check_attr_format_and_warn(rules, key, attr);
     707           0 :                 if (r < 0)
     708           0 :                         return r;
     709           0 :                 if (!is_match)
     710           0 :                         return log_token_invalid_op(rules, key);
     711             : 
     712           0 :                 if (startswith(attr, "device/"))
     713           0 :                         log_token_warning(rules, "'device' link may not be available in future kernels; please fix it.");
     714           0 :                 if (strstr(attr, "../"))
     715           0 :                         log_token_warning(rules, "Direct reference to parent sysfs directory, may break in future kernels; please fix it.");
     716             : 
     717           0 :                 r = rule_line_add_token(rule_line, TK_M_PARENTS_ATTR, op, value, attr);
     718           0 :         } else if (streq(key, "TAGS")) {
     719           0 :                 if (attr)
     720           0 :                         return log_token_invalid_attr(rules, key);
     721           0 :                 if (!is_match)
     722           0 :                         return log_token_invalid_op(rules, key);
     723             : 
     724           0 :                 r = rule_line_add_token(rule_line, TK_M_PARENTS_TAG, op, value, NULL);
     725           0 :         } else if (streq(key, "TEST")) {
     726           0 :                 mode_t mode = MODE_INVALID;
     727             : 
     728           0 :                 if (!isempty(attr)) {
     729           0 :                         r = parse_mode(attr, &mode);
     730           0 :                         if (r < 0)
     731           0 :                                 return log_token_error_errno(rules, r, "Failed to parse mode '%s': %m", attr);
     732             :                 }
     733           0 :                 check_value_format_and_warn(rules, key, value, true);
     734           0 :                 if (!is_match)
     735           0 :                         return log_token_invalid_op(rules, key);
     736             : 
     737           0 :                 r = rule_line_add_token(rule_line, TK_M_TEST, op, value, MODE_TO_PTR(mode));
     738           0 :         } else if (streq(key, "PROGRAM")) {
     739           0 :                 if (attr)
     740           0 :                         return log_token_invalid_attr(rules, key);
     741           0 :                 check_value_format_and_warn(rules, key, value, true);
     742           0 :                 if (op == OP_REMOVE)
     743           0 :                         return log_token_invalid_op(rules, key);
     744           0 :                 if (!is_match) {
     745           0 :                         if (op == OP_ASSIGN)
     746           0 :                                 log_token_debug(rules, "Operator '=' is specified to %s key, assuming '=='.", key);
     747             :                         else
     748           0 :                                 log_token_warning(rules, "%s key takes '==' or '!=' operator, assuming '==', but please fix it.", key);
     749           0 :                         op = OP_MATCH;
     750             :                 }
     751             : 
     752           0 :                 r = rule_line_add_token(rule_line, TK_M_PROGRAM, op, value, NULL);
     753           0 :         } else if (streq(key, "IMPORT")) {
     754           0 :                 if (isempty(attr))
     755           0 :                         return log_token_invalid_attr(rules, key);
     756           0 :                 check_value_format_and_warn(rules, key, value, true);
     757           0 :                 if (op == OP_REMOVE)
     758           0 :                         return log_token_invalid_op(rules, key);
     759           0 :                 if (!is_match) {
     760           0 :                         if (op == OP_ASSIGN)
     761           0 :                                 log_token_debug(rules, "Operator '=' is specified to %s key, assuming '=='.", key);
     762             :                         else
     763           0 :                                 log_token_warning(rules, "%s key takes '==' or '!=' operator, assuming '==', but please fix it.", key);
     764           0 :                         op = OP_MATCH;
     765             :                 }
     766             : 
     767           0 :                 if (streq(attr, "file"))
     768           0 :                         r = rule_line_add_token(rule_line, TK_M_IMPORT_FILE, op, value, NULL);
     769           0 :                 else if (streq(attr, "program")) {
     770             :                         UdevBuiltinCommand cmd;
     771             : 
     772           0 :                         cmd = udev_builtin_lookup(value);
     773           0 :                         if (cmd >= 0) {
     774           0 :                                 log_token_debug(rules,"Found builtin command '%s' for %s, replacing attribute", value, key);
     775           0 :                                 r = rule_line_add_token(rule_line, TK_M_IMPORT_BUILTIN, op, value, UDEV_BUILTIN_CMD_TO_PTR(cmd));
     776             :                         } else
     777           0 :                                 r = rule_line_add_token(rule_line, TK_M_IMPORT_PROGRAM, op, value, NULL);
     778           0 :                 } else if (streq(attr, "builtin")) {
     779             :                         UdevBuiltinCommand cmd;
     780             : 
     781           0 :                         cmd = udev_builtin_lookup(value);
     782           0 :                         if (cmd < 0)
     783           0 :                                 return log_token_error_errno(rules, SYNTHETIC_ERRNO(EINVAL),
     784             :                                                              "Unknown builtin command: %s", value);
     785           0 :                         r = rule_line_add_token(rule_line, TK_M_IMPORT_BUILTIN, op, value, UDEV_BUILTIN_CMD_TO_PTR(cmd));
     786           0 :                 } else if (streq(attr, "db"))
     787           0 :                         r = rule_line_add_token(rule_line, TK_M_IMPORT_DB, op, value, NULL);
     788           0 :                 else if (streq(attr, "cmdline"))
     789           0 :                         r = rule_line_add_token(rule_line, TK_M_IMPORT_CMDLINE, op, value, NULL);
     790           0 :                 else if (streq(attr, "parent"))
     791           0 :                         r = rule_line_add_token(rule_line, TK_M_IMPORT_PARENT, op, value, NULL);
     792             :                 else
     793           0 :                         return log_token_invalid_attr(rules, key);
     794           0 :         } else if (streq(key, "RESULT")) {
     795           0 :                 if (attr)
     796           0 :                         return log_token_invalid_attr(rules, key);
     797           0 :                 if (!is_match)
     798           0 :                         return log_token_invalid_op(rules, key);
     799             : 
     800           0 :                 r = rule_line_add_token(rule_line, TK_M_RESULT, op, value, NULL);
     801           0 :         } else if (streq(key, "OPTIONS")) {
     802             :                 char *tmp;
     803             : 
     804           0 :                 if (attr)
     805           0 :                         return log_token_invalid_attr(rules, key);
     806           0 :                 if (is_match || op == OP_REMOVE)
     807           0 :                         return log_token_invalid_op(rules, key);
     808           0 :                 if (op == OP_ADD) {
     809           0 :                         log_token_debug(rules, "Operator '+=' is specified to %s key, assuming '='.", key);
     810           0 :                         op = OP_ASSIGN;
     811             :                 }
     812             : 
     813           0 :                 if (streq(value, "string_escape=none"))
     814           0 :                         r = rule_line_add_token(rule_line, TK_A_OPTIONS_STRING_ESCAPE_NONE, op, NULL, NULL);
     815           0 :                 else if (streq(value, "string_escape=replace"))
     816           0 :                         r = rule_line_add_token(rule_line, TK_A_OPTIONS_STRING_ESCAPE_REPLACE, op, NULL, NULL);
     817           0 :                 else if (streq(value, "db_persist"))
     818           0 :                         r = rule_line_add_token(rule_line, TK_A_OPTIONS_DB_PERSIST, op, NULL, NULL);
     819           0 :                 else if (streq(value, "watch"))
     820           0 :                         r = rule_line_add_token(rule_line, TK_A_OPTIONS_INOTIFY_WATCH, op, NULL, INT_TO_PTR(1));
     821           0 :                 else if (streq(value, "nowatch"))
     822           0 :                         r = rule_line_add_token(rule_line, TK_A_OPTIONS_INOTIFY_WATCH, op, NULL, INT_TO_PTR(0));
     823           0 :                 else if ((tmp = startswith(value, "static_node=")))
     824           0 :                         r = rule_line_add_token(rule_line, TK_A_OPTIONS_STATIC_NODE, op, tmp, NULL);
     825           0 :                 else if ((tmp = startswith(value, "link_priority="))) {
     826             :                         int prio;
     827             : 
     828           0 :                         r = safe_atoi(tmp, &prio);
     829           0 :                         if (r < 0)
     830           0 :                                 return log_token_error_errno(rules, r, "Failed to parse link priority '%s': %m", tmp);
     831           0 :                         r = rule_line_add_token(rule_line, TK_A_OPTIONS_DEVLINK_PRIORITY, op, NULL, INT_TO_PTR(prio));
     832             :                 } else {
     833           0 :                         log_token_warning(rules, "Invalid value for OPTIONS key, ignoring: '%s'", value);
     834           0 :                         return 0;
     835             :                 }
     836           0 :         } else if (streq(key, "OWNER")) {
     837             :                 uid_t uid;
     838             : 
     839           0 :                 if (attr)
     840           0 :                         return log_token_invalid_attr(rules, key);
     841           0 :                 if (is_match || op == OP_REMOVE)
     842           0 :                         return log_token_invalid_op(rules, key);
     843           0 :                 if (op == OP_ADD) {
     844           0 :                         log_token_warning(rules, "%s key takes '=' or ':=' operator, assuming '=', but please fix it.", key);
     845           0 :                         op = OP_ASSIGN;
     846             :                 }
     847             : 
     848           0 :                 if (parse_uid(value, &uid) >= 0)
     849           0 :                         r = rule_line_add_token(rule_line, TK_A_OWNER_ID, op, NULL, UID_TO_PTR(uid));
     850           0 :                 else if (rules->resolve_name_timing == RESOLVE_NAME_EARLY &&
     851           0 :                            rule_get_substitution_type(value) == SUBST_TYPE_PLAIN) {
     852           0 :                         r = rule_resolve_user(rules, value, &uid);
     853           0 :                         if (r < 0)
     854           0 :                                 return log_token_error_errno(rules, r, "Failed to resolve user name '%s': %m", value);
     855             : 
     856           0 :                         r = rule_line_add_token(rule_line, TK_A_OWNER_ID, op, NULL, UID_TO_PTR(uid));
     857           0 :                 } else if (rules->resolve_name_timing != RESOLVE_NAME_NEVER) {
     858           0 :                         check_value_format_and_warn(rules, key, value, true);
     859           0 :                         r = rule_line_add_token(rule_line, TK_A_OWNER, op, value, NULL);
     860             :                 } else {
     861           0 :                         log_token_debug(rules, "Resolving user name is disabled, ignoring %s=%s", key, value);
     862           0 :                         return 0;
     863             :                 }
     864           0 :         } else if (streq(key, "GROUP")) {
     865             :                 gid_t gid;
     866             : 
     867           0 :                 if (attr)
     868           0 :                         return log_token_invalid_attr(rules, key);
     869           0 :                 if (is_match || op == OP_REMOVE)
     870           0 :                         return log_token_invalid_op(rules, key);
     871           0 :                 if (op == OP_ADD) {
     872           0 :                         log_token_warning(rules, "%s key takes '=' or ':=' operator, assuming '=', but please fix it.", key);
     873           0 :                         op = OP_ASSIGN;
     874             :                 }
     875             : 
     876           0 :                 if (parse_gid(value, &gid) >= 0)
     877           0 :                         r = rule_line_add_token(rule_line, TK_A_GROUP_ID, op, NULL, GID_TO_PTR(gid));
     878           0 :                 else if (rules->resolve_name_timing == RESOLVE_NAME_EARLY &&
     879           0 :                            rule_get_substitution_type(value) == SUBST_TYPE_PLAIN) {
     880           0 :                         r = rule_resolve_group(rules, value, &gid);
     881           0 :                         if (r < 0)
     882           0 :                                 return log_token_error_errno(rules, r, "Failed to resolve group name '%s': %m", value);
     883             : 
     884           0 :                         r = rule_line_add_token(rule_line, TK_A_GROUP_ID, op, NULL, GID_TO_PTR(gid));
     885           0 :                 } else if (rules->resolve_name_timing != RESOLVE_NAME_NEVER) {
     886           0 :                         check_value_format_and_warn(rules, key, value, true);
     887           0 :                         r = rule_line_add_token(rule_line, TK_A_GROUP, op, value, NULL);
     888             :                 } else {
     889           0 :                         log_token_debug(rules, "Resolving group name is disabled, ignoring %s=%s", key, value);
     890           0 :                         return 0;
     891             :                 }
     892           0 :         } else if (streq(key, "MODE")) {
     893             :                 mode_t mode;
     894             : 
     895           0 :                 if (attr)
     896           0 :                         return log_token_invalid_attr(rules, key);
     897           0 :                 if (is_match || op == OP_REMOVE)
     898           0 :                         return log_token_invalid_op(rules, key);
     899           0 :                 if (op == OP_ADD) {
     900           0 :                         log_token_warning(rules, "%s key takes '=' or ':=' operator, assuming '=', but please fix it.", key);
     901           0 :                         op = OP_ASSIGN;
     902             :                 }
     903             : 
     904           0 :                 if (parse_mode(value, &mode) >= 0)
     905           0 :                         r = rule_line_add_token(rule_line, TK_A_MODE_ID, op, NULL, MODE_TO_PTR(mode));
     906             :                 else {
     907           0 :                         check_value_format_and_warn(rules, key, value, true);
     908           0 :                         r = rule_line_add_token(rule_line, TK_A_MODE, op, value, NULL);
     909             :                 }
     910           0 :         } else if (streq(key, "SECLABEL")) {
     911           0 :                 if (isempty(attr))
     912           0 :                         return log_token_invalid_attr(rules, key);
     913           0 :                 check_value_format_and_warn(rules, key, value, true);
     914           0 :                 if (is_match || op == OP_REMOVE)
     915           0 :                         return log_token_invalid_op(rules, key);
     916           0 :                 if (op == OP_ASSIGN_FINAL) {
     917           0 :                         log_token_warning(rules, "%s key takes '=' or '+=' operator, assuming '=', but please fix it.", key);
     918           0 :                         op = OP_ASSIGN;
     919             :                 }
     920             : 
     921           0 :                 r = rule_line_add_token(rule_line, TK_A_SECLABEL, op, value, NULL);
     922           0 :         } else if (streq(key, "RUN")) {
     923           0 :                 if (is_match || op == OP_REMOVE)
     924           0 :                         return log_token_invalid_op(rules, key);
     925           0 :                 check_value_format_and_warn(rules, key, value, true);
     926           0 :                 if (!attr || streq(attr, "program"))
     927           0 :                         r = rule_line_add_token(rule_line, TK_A_RUN_PROGRAM, op, value, NULL);
     928           0 :                 else if (streq(attr, "builtin")) {
     929             :                         UdevBuiltinCommand cmd;
     930             : 
     931           0 :                         cmd = udev_builtin_lookup(value);
     932           0 :                         if (cmd < 0)
     933           0 :                                 return log_token_error_errno(rules, SYNTHETIC_ERRNO(EINVAL),
     934             :                                                              "Unknown builtin command '%s', ignoring", value);
     935           0 :                         r = rule_line_add_token(rule_line, TK_A_RUN_BUILTIN, op, value, UDEV_BUILTIN_CMD_TO_PTR(cmd));
     936             :                 } else
     937           0 :                         return log_token_invalid_attr(rules, key);
     938           0 :         } else if (streq(key, "GOTO")) {
     939           0 :                 if (attr)
     940           0 :                         return log_token_invalid_attr(rules, key);
     941           0 :                 if (op != OP_ASSIGN)
     942           0 :                         return log_token_invalid_op(rules, key);
     943           0 :                 if (FLAGS_SET(rule_line->type, LINE_HAS_GOTO)) {
     944           0 :                         log_token_warning(rules, "Contains multiple GOTO key, ignoring GOTO=\"%s\".", value);
     945           0 :                         return 0;
     946             :                 }
     947             : 
     948           0 :                 rule_line->goto_label = value;
     949           0 :                 SET_FLAG(rule_line->type, LINE_HAS_GOTO, true);
     950           0 :                 return 1;
     951           0 :         } else if (streq(key, "LABEL")) {
     952           0 :                 if (attr)
     953           0 :                         return log_token_invalid_attr(rules, key);
     954           0 :                 if (op != OP_ASSIGN)
     955           0 :                         return log_token_invalid_op(rules, key);
     956             : 
     957           0 :                 rule_line->label = value;
     958           0 :                 SET_FLAG(rule_line->type, LINE_HAS_LABEL, true);
     959           0 :                 return 1;
     960             :         } else
     961           0 :                 return log_token_error_errno(rules, SYNTHETIC_ERRNO(EINVAL), "Invalid key '%s'", key);
     962           0 :         if (r < 0)
     963           0 :                 return log_oom();
     964             : 
     965           0 :         return 1;
     966             : }
     967             : 
     968           0 : static UdevRuleOperatorType parse_operator(const char *op) {
     969           0 :         assert(op);
     970             : 
     971           0 :         if (startswith(op, "=="))
     972           0 :                 return OP_MATCH;
     973           0 :         if (startswith(op, "!="))
     974           0 :                 return OP_NOMATCH;
     975           0 :         if (startswith(op, "+="))
     976           0 :                 return OP_ADD;
     977           0 :         if (startswith(op, "-="))
     978           0 :                 return OP_REMOVE;
     979           0 :         if (startswith(op, "="))
     980           0 :                 return OP_ASSIGN;
     981           0 :         if (startswith(op, ":="))
     982           0 :                 return OP_ASSIGN_FINAL;
     983             : 
     984           0 :         return _OP_TYPE_INVALID;
     985             : }
     986             : 
     987           0 : static int parse_line(char **line, char **ret_key, char **ret_attr, UdevRuleOperatorType *ret_op, char **ret_value) {
     988             :         char *key_begin, *key_end, *attr, *tmp, *value, *i, *j;
     989             :         UdevRuleOperatorType op;
     990             : 
     991           0 :         assert(line);
     992           0 :         assert(*line);
     993           0 :         assert(ret_key);
     994           0 :         assert(ret_op);
     995           0 :         assert(ret_value);
     996             : 
     997           0 :         key_begin = skip_leading_chars(*line, WHITESPACE ",");
     998             : 
     999           0 :         if (isempty(key_begin))
    1000           0 :                 return 0;
    1001             : 
    1002           0 :         for (key_end = key_begin; ; key_end++) {
    1003           0 :                 if (key_end[0] == '\0')
    1004           0 :                         return -EINVAL;
    1005           0 :                 if (strchr(WHITESPACE "={", key_end[0]))
    1006           0 :                         break;
    1007           0 :                 if (strchr("+-!:", key_end[0]) && key_end[1] == '=')
    1008           0 :                         break;
    1009             :         }
    1010           0 :         if (key_end[0] == '{') {
    1011           0 :                 attr = key_end + 1;
    1012           0 :                 tmp = strchr(attr, '}');
    1013           0 :                 if (!tmp)
    1014           0 :                         return -EINVAL;
    1015           0 :                 *tmp++ = '\0';
    1016             :         } else {
    1017           0 :                 attr = NULL;
    1018           0 :                 tmp = key_end;
    1019             :         }
    1020             : 
    1021           0 :         tmp = skip_leading_chars(tmp, NULL);
    1022           0 :         op = parse_operator(tmp);
    1023           0 :         if (op < 0)
    1024           0 :                 return -EINVAL;
    1025             : 
    1026           0 :         key_end[0] = '\0';
    1027             : 
    1028           0 :         tmp += op == OP_ASSIGN ? 1 : 2;
    1029           0 :         value = skip_leading_chars(tmp, NULL);
    1030             : 
    1031             :         /* value must be double quotated */
    1032           0 :         if (value[0] != '"')
    1033           0 :                 return -EINVAL;
    1034           0 :         value++;
    1035             : 
    1036             :         /* unescape double quotation '\"' -> '"' */
    1037           0 :         for (i = j = value; ; i++, j++) {
    1038           0 :                 if (*i == '"')
    1039           0 :                         break;
    1040           0 :                 if (*i == '\0')
    1041           0 :                         return -EINVAL;
    1042           0 :                 if (i[0] == '\\' && i[1] == '"')
    1043           0 :                         i++;
    1044           0 :                 *j = *i;
    1045             :         }
    1046           0 :         j[0] = '\0';
    1047             : 
    1048           0 :         *line = i+1;
    1049           0 :         *ret_key = key_begin;
    1050           0 :         *ret_attr = attr;
    1051           0 :         *ret_op = op;
    1052           0 :         *ret_value = value;
    1053           0 :         return 1;
    1054             : }
    1055             : 
    1056           0 : static void sort_tokens(UdevRuleLine *rule_line) {
    1057             :         UdevRuleToken *head_old;
    1058             : 
    1059           0 :         assert(rule_line);
    1060             : 
    1061           0 :         head_old = TAKE_PTR(rule_line->tokens);
    1062           0 :         rule_line->current_token = NULL;
    1063             : 
    1064           0 :         while (!LIST_IS_EMPTY(head_old)) {
    1065           0 :                 UdevRuleToken *t, *min_token = NULL;
    1066             : 
    1067           0 :                 LIST_FOREACH(tokens, t, head_old)
    1068           0 :                         if (!min_token || min_token->type > t->type)
    1069           0 :                                 min_token = t;
    1070             : 
    1071           0 :                 LIST_REMOVE(tokens, head_old, min_token);
    1072           0 :                 rule_line_append_token(rule_line, min_token);
    1073             :         }
    1074           0 : }
    1075             : 
    1076           0 : static int rule_add_line(UdevRules *rules, const char *line_str, unsigned line_nr) {
    1077           0 :         _cleanup_(udev_rule_line_freep) UdevRuleLine *rule_line = NULL;
    1078           0 :         _cleanup_free_ char *line = NULL;
    1079             :         UdevRuleFile *rule_file;
    1080             :         char *p;
    1081             :         int r;
    1082             : 
    1083           0 :         assert(rules);
    1084           0 :         assert(rules->current_file);
    1085           0 :         assert(line_str);
    1086             : 
    1087           0 :         rule_file = rules->current_file;
    1088             : 
    1089           0 :         if (isempty(line_str))
    1090           0 :                 return 0;
    1091             : 
    1092           0 :         line = strdup(line_str);
    1093           0 :         if (!line)
    1094           0 :                 return log_oom();
    1095             : 
    1096           0 :         rule_line = new(UdevRuleLine, 1);
    1097           0 :         if (!rule_line)
    1098           0 :                 return log_oom();
    1099             : 
    1100           0 :         *rule_line = (UdevRuleLine) {
    1101           0 :                 .line = TAKE_PTR(line),
    1102             :                 .line_number = line_nr,
    1103             :                 .rule_file = rule_file,
    1104             :         };
    1105             : 
    1106           0 :         if (rule_file->current_line)
    1107           0 :                 LIST_APPEND(rule_lines, rule_file->current_line, rule_line);
    1108             :         else
    1109           0 :                 LIST_APPEND(rule_lines, rule_file->rule_lines, rule_line);
    1110             : 
    1111           0 :         rule_file->current_line = rule_line;
    1112             : 
    1113           0 :         for (p = rule_line->line; !isempty(p); ) {
    1114             :                 char *key, *attr, *value;
    1115             :                 UdevRuleOperatorType op;
    1116             : 
    1117           0 :                 r = parse_line(&p, &key, &attr, &op, &value);
    1118           0 :                 if (r < 0)
    1119           0 :                         return log_token_error_errno(rules, r, "Invalid key/value pair, ignoring.");
    1120           0 :                 if (r == 0)
    1121           0 :                         break;
    1122             : 
    1123           0 :                 r = parse_token(rules, key, attr, op, value);
    1124           0 :                 if (r < 0)
    1125           0 :                         return r;
    1126             :         }
    1127             : 
    1128           0 :         if (rule_line->type == 0) {
    1129           0 :                 log_token_warning(rules, "The line takes no effect, ignoring.");
    1130           0 :                 return 0;
    1131             :         }
    1132             : 
    1133           0 :         sort_tokens(rule_line);
    1134           0 :         TAKE_PTR(rule_line);
    1135           0 :         return 0;
    1136             : }
    1137             : 
    1138           0 : static void rule_resolve_goto(UdevRuleFile *rule_file) {
    1139             :         UdevRuleLine *line, *line_next, *i;
    1140             : 
    1141           0 :         assert(rule_file);
    1142             : 
    1143             :         /* link GOTOs to LABEL rules in this file to be able to fast-forward */
    1144           0 :         LIST_FOREACH_SAFE(rule_lines, line, line_next, rule_file->rule_lines) {
    1145           0 :                 if (!FLAGS_SET(line->type, LINE_HAS_GOTO))
    1146           0 :                         continue;
    1147             : 
    1148           0 :                 LIST_FOREACH_AFTER(rule_lines, i, line)
    1149           0 :                         if (streq_ptr(i->label, line->goto_label)) {
    1150           0 :                                 line->goto_line = i;
    1151           0 :                                 break;
    1152             :                         }
    1153             : 
    1154           0 :                 if (!line->goto_line) {
    1155           0 :                         log_error("%s:%u: GOTO=\"%s\" has no matching label, ignoring",
    1156             :                                   rule_file->filename, line->line_number, line->goto_label);
    1157             : 
    1158           0 :                         SET_FLAG(line->type, LINE_HAS_GOTO, false);
    1159           0 :                         line->goto_label = NULL;
    1160             : 
    1161           0 :                         if ((line->type & ~LINE_HAS_LABEL) == 0) {
    1162           0 :                                 log_notice("%s:%u: The line takes no effect any more, dropping",
    1163             :                                            rule_file->filename, line->line_number);
    1164           0 :                                 if (line->type == LINE_HAS_LABEL)
    1165           0 :                                         udev_rule_line_clear_tokens(line);
    1166             :                                 else
    1167           0 :                                         udev_rule_line_free(line);
    1168             :                         }
    1169             :                 }
    1170             :         }
    1171           0 : }
    1172             : 
    1173           0 : static int parse_file(UdevRules *rules, const char *filename) {
    1174           0 :         _cleanup_free_ char *continuation = NULL, *name = NULL;
    1175           0 :         _cleanup_fclose_ FILE *f = NULL;
    1176             :         UdevRuleFile *rule_file;
    1177           0 :         bool ignore_line = false;
    1178           0 :         unsigned line_nr = 0;
    1179             :         int r;
    1180             : 
    1181           0 :         f = fopen(filename, "re");
    1182           0 :         if (!f) {
    1183           0 :                 if (errno == ENOENT)
    1184           0 :                         return 0;
    1185             : 
    1186           0 :                 return -errno;
    1187             :         }
    1188             : 
    1189           0 :         (void) fd_warn_permissions(filename, fileno(f));
    1190             : 
    1191           0 :         if (null_or_empty_fd(fileno(f))) {
    1192           0 :                 log_debug("Skipping empty file: %s", filename);
    1193           0 :                 return 0;
    1194             :         }
    1195             : 
    1196           0 :         log_debug("Reading rules file: %s", filename);
    1197             : 
    1198           0 :         name = strdup(filename);
    1199           0 :         if (!name)
    1200           0 :                 return log_oom();
    1201             : 
    1202           0 :         rule_file = new(UdevRuleFile, 1);
    1203           0 :         if (!rule_file)
    1204           0 :                 return log_oom();
    1205             : 
    1206           0 :         *rule_file = (UdevRuleFile) {
    1207           0 :                 .filename = TAKE_PTR(name),
    1208             :         };
    1209             : 
    1210           0 :         if (rules->current_file)
    1211           0 :                 LIST_APPEND(rule_files, rules->current_file, rule_file);
    1212             :         else
    1213           0 :                 LIST_APPEND(rule_files, rules->rule_files, rule_file);
    1214             : 
    1215           0 :         rules->current_file = rule_file;
    1216             : 
    1217           0 :         for (;;) {
    1218           0 :                 _cleanup_free_ char *buf = NULL;
    1219             :                 size_t len;
    1220             :                 char *line;
    1221             : 
    1222           0 :                 r = read_line(f, UTIL_LINE_SIZE, &buf);
    1223           0 :                 if (r < 0)
    1224           0 :                         return r;
    1225           0 :                 if (r == 0)
    1226           0 :                         break;
    1227             : 
    1228           0 :                 line_nr++;
    1229           0 :                 line = skip_leading_chars(buf, NULL);
    1230             : 
    1231           0 :                 if (line[0] == '#')
    1232           0 :                         continue;
    1233             : 
    1234           0 :                 len = strlen(line);
    1235             : 
    1236           0 :                 if (continuation && !ignore_line) {
    1237           0 :                         if (strlen(continuation) + len >= UTIL_LINE_SIZE)
    1238           0 :                                 ignore_line = true;
    1239             : 
    1240           0 :                         if (!strextend(&continuation, line, NULL))
    1241           0 :                                 return log_oom();
    1242             : 
    1243           0 :                         if (!ignore_line) {
    1244           0 :                                 line = continuation;
    1245           0 :                                 len = strlen(line);
    1246             :                         }
    1247             :                 }
    1248             : 
    1249           0 :                 if (len > 0 && line[len - 1] == '\\') {
    1250           0 :                         if (ignore_line)
    1251           0 :                                 continue;
    1252             : 
    1253           0 :                         line[len - 1] = '\0';
    1254           0 :                         if (!continuation) {
    1255           0 :                                 continuation = strdup(line);
    1256           0 :                                 if (!continuation)
    1257           0 :                                         return log_oom();
    1258             :                         }
    1259             : 
    1260           0 :                         continue;
    1261             :                 }
    1262             : 
    1263           0 :                 if (ignore_line)
    1264           0 :                         log_error("%s:%u: Line is too long, ignored", filename, line_nr);
    1265           0 :                 else if (len > 0)
    1266           0 :                         (void) rule_add_line(rules, line, line_nr);
    1267             : 
    1268           0 :                 continuation = mfree(continuation);
    1269           0 :                 ignore_line = false;
    1270             :         }
    1271             : 
    1272           0 :         rule_resolve_goto(rule_file);
    1273           0 :         return 0;
    1274             : }
    1275             : 
    1276           0 : int udev_rules_new(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing) {
    1277           0 :         _cleanup_(udev_rules_freep) UdevRules *rules = NULL;
    1278           0 :         _cleanup_strv_free_ char **files = NULL;
    1279             :         char **f;
    1280             :         int r;
    1281             : 
    1282           0 :         assert(resolve_name_timing >= 0 && resolve_name_timing < _RESOLVE_NAME_TIMING_MAX);
    1283             : 
    1284           0 :         rules = new(UdevRules, 1);
    1285           0 :         if (!rules)
    1286           0 :                 return -ENOMEM;
    1287             : 
    1288           0 :         *rules = (UdevRules) {
    1289             :                 .resolve_name_timing = resolve_name_timing,
    1290             :         };
    1291             : 
    1292           0 :         (void) udev_rules_check_timestamp(rules);
    1293             : 
    1294           0 :         r = conf_files_list_strv(&files, ".rules", NULL, 0, RULES_DIRS);
    1295           0 :         if (r < 0)
    1296           0 :                 return log_error_errno(r, "Failed to enumerate rules files: %m");
    1297             : 
    1298           0 :         STRV_FOREACH(f, files)
    1299           0 :                 (void) parse_file(rules, *f);
    1300             : 
    1301           0 :         *ret_rules = TAKE_PTR(rules);
    1302           0 :         return 0;
    1303             : }
    1304             : 
    1305           0 : bool udev_rules_check_timestamp(UdevRules *rules) {
    1306           0 :         if (!rules)
    1307           0 :                 return false;
    1308             : 
    1309           0 :         return paths_check_timestamp(RULES_DIRS, &rules->dirs_ts_usec, true);
    1310             : }
    1311             : 
    1312           0 : static bool token_match_string(UdevRuleToken *token, const char *str) {
    1313             :         const char *i, *value;
    1314           0 :         bool match = false;
    1315             : 
    1316           0 :         assert(token);
    1317           0 :         assert(token->value);
    1318           0 :         assert(token->type < _TK_M_MAX);
    1319             : 
    1320           0 :         str = strempty(str);
    1321           0 :         value = token->value;
    1322             : 
    1323           0 :         switch (token->match_type) {
    1324           0 :         case MATCH_TYPE_EMPTY:
    1325           0 :                 match = isempty(str);
    1326           0 :                 break;
    1327           0 :         case MATCH_TYPE_SUBSYSTEM:
    1328           0 :                 value = "subsystem\0class\0bus\0";
    1329             :                 _fallthrough_;
    1330           0 :         case MATCH_TYPE_PLAIN:
    1331           0 :                 NULSTR_FOREACH(i, value)
    1332           0 :                         if (streq(i, str)) {
    1333           0 :                                 match = true;
    1334           0 :                                 break;
    1335             :                         }
    1336           0 :                 break;
    1337           0 :         case MATCH_TYPE_GLOB:
    1338           0 :                 NULSTR_FOREACH(i, value)
    1339           0 :                         if ((fnmatch(i, str, 0) == 0)) {
    1340           0 :                                 match = true;
    1341           0 :                                 break;
    1342             :                         }
    1343           0 :                 break;
    1344           0 :         default:
    1345           0 :                 assert_not_reached("Invalid match type");
    1346             :         }
    1347             : 
    1348           0 :         return token->op == (match ? OP_MATCH : OP_NOMATCH);
    1349             : }
    1350             : 
    1351           0 : static bool token_match_attr(UdevRuleToken *token, sd_device *dev, UdevEvent *event) {
    1352             :         char nbuf[UTIL_NAME_SIZE], vbuf[UTIL_NAME_SIZE];
    1353             :         const char *name, *value;
    1354             : 
    1355           0 :         assert(token);
    1356           0 :         assert(dev);
    1357           0 :         assert(event);
    1358             : 
    1359           0 :         name = (const char*) token->data;
    1360             : 
    1361           0 :         switch (token->attr_subst_type) {
    1362           0 :         case SUBST_TYPE_FORMAT:
    1363           0 :                 (void) udev_event_apply_format(event, name, nbuf, sizeof(nbuf), false);
    1364           0 :                 name = nbuf;
    1365             :                 _fallthrough_;
    1366           0 :         case SUBST_TYPE_PLAIN:
    1367           0 :                 if (sd_device_get_sysattr_value(dev, name, &value) < 0)
    1368           0 :                         return false;
    1369           0 :                 break;
    1370           0 :         case SUBST_TYPE_SUBSYS:
    1371           0 :                 if (util_resolve_subsys_kernel(name, vbuf, sizeof(vbuf), true) < 0)
    1372           0 :                         return false;
    1373           0 :                 value = vbuf;
    1374           0 :                 break;
    1375           0 :         default:
    1376           0 :                 assert_not_reached("Invalid attribute substitution type");
    1377             :         }
    1378             : 
    1379             :         /* remove trailing whitespace, if not asked to match for it */
    1380           0 :         if (token->attr_match_remove_trailing_whitespace) {
    1381           0 :                 if (value != vbuf) {
    1382           0 :                         strscpy(vbuf, sizeof(vbuf), value);
    1383           0 :                         value = vbuf;
    1384             :                 }
    1385             : 
    1386           0 :                 delete_trailing_chars(vbuf, NULL);
    1387             :         }
    1388             : 
    1389           0 :         return token_match_string(token, value);
    1390             : }
    1391             : 
    1392           0 : static int get_property_from_string(char *line, char **ret_key, char **ret_value) {
    1393             :         char *key, *val;
    1394             :         size_t len;
    1395             : 
    1396           0 :         assert(line);
    1397           0 :         assert(ret_key);
    1398           0 :         assert(ret_value);
    1399             : 
    1400             :         /* find key */
    1401           0 :         key = skip_leading_chars(line, NULL);
    1402             : 
    1403             :         /* comment or empty line */
    1404           0 :         if (IN_SET(key[0], '#', '\0')) {
    1405           0 :                 *ret_key = *ret_value = NULL;
    1406           0 :                 return 0;
    1407             :         }
    1408             : 
    1409             :         /* split key/value */
    1410           0 :         val = strchr(key, '=');
    1411           0 :         if (!val)
    1412           0 :                 return -EINVAL;
    1413           0 :         *val++ = '\0';
    1414             : 
    1415           0 :         key = strstrip(key);
    1416           0 :         if (isempty(key))
    1417           0 :                 return -EINVAL;
    1418             : 
    1419           0 :         val = strstrip(val);
    1420           0 :         if (isempty(val))
    1421           0 :                 return -EINVAL;
    1422             : 
    1423             :         /* unquote */
    1424           0 :         if (IN_SET(val[0], '"', '\'')) {
    1425           0 :                 len = strlen(val);
    1426           0 :                 if (len == 1 || val[len-1] != val[0])
    1427           0 :                         return -EINVAL;
    1428           0 :                 val[len-1] = '\0';
    1429           0 :                 val++;
    1430             :         }
    1431             : 
    1432           0 :         *ret_key = key;
    1433           0 :         *ret_value = val;
    1434           0 :         return 1;
    1435             : }
    1436             : 
    1437           0 : static int import_parent_into_properties(sd_device *dev, const char *filter) {
    1438             :         const char *key, *val;
    1439             :         sd_device *parent;
    1440             :         int r;
    1441             : 
    1442           0 :         assert(dev);
    1443           0 :         assert(filter);
    1444             : 
    1445           0 :         r = sd_device_get_parent(dev, &parent);
    1446           0 :         if (r == -ENOENT)
    1447           0 :                 return 0;
    1448           0 :         if (r < 0)
    1449           0 :                 return r;
    1450             : 
    1451           0 :         FOREACH_DEVICE_PROPERTY(parent, key, val) {
    1452           0 :                 if (fnmatch(filter, key, 0) != 0)
    1453           0 :                         continue;
    1454           0 :                 r = device_add_property(dev, key, val);
    1455           0 :                 if (r < 0)
    1456           0 :                         return r;
    1457             :         }
    1458             : 
    1459           0 :         return 1;
    1460             : }
    1461             : 
    1462           0 : static int attr_subst_subdir(char attr[static UTIL_PATH_SIZE]) {
    1463           0 :         _cleanup_closedir_ DIR *dir = NULL;
    1464             :         struct dirent *dent;
    1465             :         char buf[UTIL_PATH_SIZE], *p;
    1466             :         const char *tail;
    1467             :         size_t len, size;
    1468             : 
    1469           0 :         assert(attr);
    1470             : 
    1471           0 :         tail = strstr(attr, "/*/");
    1472           0 :         if (!tail)
    1473           0 :             return 0;
    1474             : 
    1475           0 :         len = tail - attr + 1; /* include slash at the end */
    1476           0 :         tail += 2; /* include slash at the beginning */
    1477             : 
    1478           0 :         p = buf;
    1479           0 :         size = sizeof(buf);
    1480           0 :         size -= strnpcpy(&p, size, attr, len);
    1481             : 
    1482           0 :         dir = opendir(buf);
    1483           0 :         if (!dir)
    1484           0 :                 return -errno;
    1485             : 
    1486           0 :         FOREACH_DIRENT_ALL(dent, dir, break) {
    1487           0 :                 if (dent->d_name[0] == '.')
    1488           0 :                         continue;
    1489             : 
    1490           0 :                 strscpyl(p, size, dent->d_name, tail, NULL);
    1491           0 :                 if (faccessat(dirfd(dir), p, F_OK, 0) < 0)
    1492           0 :                         continue;
    1493             : 
    1494           0 :                 strcpy(attr, buf);
    1495           0 :                 return 0;
    1496             :         }
    1497             : 
    1498           0 :         return -ENOENT;
    1499             : }
    1500             : 
    1501           0 : static int udev_rule_apply_token_to_event(
    1502             :                 UdevRules *rules,
    1503             :                 sd_device *dev,
    1504             :                 UdevEvent *event,
    1505             :                 usec_t timeout_usec,
    1506             :                 Hashmap *properties_list) {
    1507             : 
    1508             :         UdevRuleToken *token;
    1509             :         char buf[UTIL_PATH_SIZE];
    1510             :         const char *val;
    1511             :         size_t count;
    1512             :         bool match;
    1513             :         int r;
    1514             : 
    1515           0 :         assert(rules);
    1516           0 :         assert(dev);
    1517           0 :         assert(event);
    1518             : 
    1519             :         /* This returns the following values:
    1520             :          * 0 on the current token does not match the event,
    1521             :          * 1 on the current token matches the event, and
    1522             :          * negative errno on some critical errors. */
    1523             : 
    1524           0 :         token = rules->current_file->current_line->current_token;
    1525             : 
    1526           0 :         switch (token->type) {
    1527           0 :         case TK_M_ACTION: {
    1528             :                 DeviceAction a;
    1529             : 
    1530           0 :                 r = device_get_action(dev, &a);
    1531           0 :                 if (r < 0)
    1532           0 :                         return log_rule_error_errno(dev, rules, r, "Failed to get uevent action type: %m");
    1533             : 
    1534           0 :                 return token_match_string(token, device_action_to_string(a));
    1535             :         }
    1536           0 :         case TK_M_DEVPATH:
    1537           0 :                 r = sd_device_get_devpath(dev, &val);
    1538           0 :                 if (r < 0)
    1539           0 :                         return log_rule_error_errno(dev, rules, r, "Failed to get devpath: %m");
    1540             : 
    1541           0 :                 return token_match_string(token, val);
    1542           0 :         case TK_M_KERNEL:
    1543             :         case TK_M_PARENTS_KERNEL:
    1544           0 :                 r = sd_device_get_sysname(dev, &val);
    1545           0 :                 if (r < 0)
    1546           0 :                         return log_rule_error_errno(dev, rules, r, "Failed to get sysname: %m");
    1547             : 
    1548           0 :                 return token_match_string(token, val);
    1549           0 :         case TK_M_DEVLINK:
    1550           0 :                 FOREACH_DEVICE_DEVLINK(dev, val)
    1551           0 :                         if (token_match_string(token, strempty(startswith(val, "/dev/"))))
    1552           0 :                                 return token->op == OP_MATCH;
    1553           0 :                 return token->op == OP_NOMATCH;
    1554           0 :         case TK_M_NAME:
    1555           0 :                 return token_match_string(token, event->name);
    1556           0 :         case TK_M_ENV:
    1557           0 :                 if (sd_device_get_property_value(dev, (const char*) token->data, &val) < 0)
    1558           0 :                         val = hashmap_get(properties_list, token->data);
    1559             : 
    1560           0 :                 return token_match_string(token, val);
    1561           0 :         case TK_M_TAG:
    1562             :         case TK_M_PARENTS_TAG:
    1563           0 :                 FOREACH_DEVICE_TAG(dev, val)
    1564           0 :                         if (token_match_string(token, val))
    1565           0 :                                 return token->op == OP_MATCH;
    1566           0 :                 return token->op == OP_NOMATCH;
    1567           0 :         case TK_M_SUBSYSTEM:
    1568             :         case TK_M_PARENTS_SUBSYSTEM:
    1569           0 :                 r = sd_device_get_subsystem(dev, &val);
    1570           0 :                 if (r == -ENOENT)
    1571           0 :                         val = NULL;
    1572           0 :                 else if (r < 0)
    1573           0 :                         return log_rule_error_errno(dev, rules, r, "Failed to get subsystem: %m");
    1574             : 
    1575           0 :                 return token_match_string(token, val);
    1576           0 :         case TK_M_DRIVER:
    1577             :         case TK_M_PARENTS_DRIVER:
    1578           0 :                 r = sd_device_get_driver(dev, &val);
    1579           0 :                 if (r == -ENOENT)
    1580           0 :                         val = NULL;
    1581           0 :                 else if (r < 0)
    1582           0 :                         return log_rule_error_errno(dev, rules, r, "Failed to get driver: %m");
    1583             : 
    1584           0 :                 return token_match_string(token, val);
    1585           0 :         case TK_M_ATTR:
    1586             :         case TK_M_PARENTS_ATTR:
    1587           0 :                 return token_match_attr(token, dev, event);
    1588           0 :         case TK_M_SYSCTL: {
    1589           0 :                 _cleanup_free_ char *value = NULL;
    1590             : 
    1591           0 :                 (void) udev_event_apply_format(event, (const char*) token->data, buf, sizeof(buf), false);
    1592           0 :                 r = sysctl_read(sysctl_normalize(buf), &value);
    1593           0 :                 if (r < 0 && r != -ENOENT)
    1594           0 :                         return log_rule_error_errno(dev, rules, r, "Failed to read sysctl '%s': %m", buf);
    1595             : 
    1596           0 :                 return token_match_string(token, strstrip(value));
    1597             :         }
    1598           0 :         case TK_M_TEST: {
    1599           0 :                 mode_t mode = PTR_TO_MODE(token->data);
    1600             :                 struct stat statbuf;
    1601             : 
    1602           0 :                 (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false);
    1603           0 :                 if (!path_is_absolute(buf) &&
    1604           0 :                     util_resolve_subsys_kernel(buf, buf, sizeof(buf), false) < 0) {
    1605             :                         char tmp[UTIL_PATH_SIZE];
    1606             : 
    1607           0 :                         r = sd_device_get_syspath(dev, &val);
    1608           0 :                         if (r < 0)
    1609           0 :                                 return log_rule_error_errno(dev, rules, r, "Failed to get syspath: %m");
    1610             : 
    1611           0 :                         strscpy(tmp, sizeof(tmp), buf);
    1612           0 :                         strscpyl(buf, sizeof(buf), val, "/", tmp, NULL);
    1613             :                 }
    1614             : 
    1615           0 :                 r = attr_subst_subdir(buf);
    1616           0 :                 if (r == -ENOENT)
    1617           0 :                         return token->op == OP_NOMATCH;
    1618           0 :                 if (r < 0)
    1619           0 :                         return log_rule_error_errno(dev, rules, r, "Failed to test the existence of '%s': %m", buf);
    1620             : 
    1621           0 :                 if (stat(buf, &statbuf) < 0)
    1622           0 :                         return token->op == OP_NOMATCH;
    1623             : 
    1624           0 :                 if (mode == MODE_INVALID)
    1625           0 :                         return token->op == OP_MATCH;
    1626             : 
    1627           0 :                 match = (((statbuf.st_mode ^ mode) & 07777) == 0);
    1628           0 :                 return token->op == (match ? OP_MATCH : OP_NOMATCH);
    1629             :         }
    1630           0 :         case TK_M_PROGRAM: {
    1631             :                 char result[UTIL_LINE_SIZE];
    1632             : 
    1633           0 :                 event->program_result = mfree(event->program_result);
    1634           0 :                 (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false);
    1635           0 :                 log_rule_debug(dev, rules, "Running PROGRAM '%s'", buf);
    1636             : 
    1637           0 :                 r = udev_event_spawn(event, timeout_usec, true, buf, result, sizeof(result));
    1638           0 :                 if (r < 0)
    1639           0 :                         return log_rule_error_errno(dev, rules, r, "Failed to execute '%s': %m", buf);
    1640           0 :                 if (r > 0)
    1641           0 :                         return token->op == OP_NOMATCH;
    1642             : 
    1643           0 :                 delete_trailing_chars(result, "\n");
    1644           0 :                 count = util_replace_chars(result, UDEV_ALLOWED_CHARS_INPUT);
    1645           0 :                 if (count > 0)
    1646           0 :                         log_rule_debug(dev, rules, "Replaced %zu character(s) from result of '%s'",
    1647             :                                        count, buf);
    1648             : 
    1649           0 :                 event->program_result = strdup(result);
    1650           0 :                 return token->op == OP_MATCH;
    1651             :         }
    1652           0 :         case TK_M_IMPORT_FILE: {
    1653           0 :                 _cleanup_fclose_ FILE *f = NULL;
    1654             : 
    1655           0 :                 (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false);
    1656           0 :                 log_rule_debug(dev, rules, "Importing properties from '%s'", buf);
    1657             : 
    1658           0 :                 f = fopen(buf, "re");
    1659           0 :                 if (!f) {
    1660           0 :                         if (errno != ENOENT)
    1661           0 :                                 return log_rule_error_errno(dev, rules, errno,
    1662             :                                                             "Failed to open '%s': %m", buf);
    1663           0 :                         return token->op == OP_NOMATCH;
    1664             :                 }
    1665             : 
    1666           0 :                 for (;;) {
    1667           0 :                         _cleanup_free_ char *line = NULL;
    1668             :                         char *key, *value;
    1669             : 
    1670           0 :                         r = read_line(f, LONG_LINE_MAX, &line);
    1671           0 :                         if (r < 0) {
    1672           0 :                                 log_rule_debug_errno(dev, rules, r,
    1673             :                                                      "Failed to read '%s', ignoring: %m", buf);
    1674           0 :                                 return token->op == OP_NOMATCH;
    1675             :                         }
    1676           0 :                         if (r == 0)
    1677           0 :                                 break;
    1678             : 
    1679           0 :                         r = get_property_from_string(line, &key, &value);
    1680           0 :                         if (r < 0) {
    1681           0 :                                 log_rule_debug_errno(dev, rules, r,
    1682             :                                                      "Failed to parse key and value from '%s', ignoring: %m",
    1683             :                                                      line);
    1684           0 :                                 continue;
    1685             :                         }
    1686           0 :                         if (r == 0)
    1687           0 :                                 continue;
    1688             : 
    1689           0 :                         r = device_add_property(dev, key, value);
    1690           0 :                         if (r < 0)
    1691           0 :                                 return log_rule_error_errno(dev, rules, r,
    1692             :                                                             "Failed to add property %s=%s: %m",
    1693             :                                                             key, value);
    1694             :                 }
    1695             : 
    1696           0 :                 return token->op == OP_MATCH;
    1697             :         }
    1698           0 :         case TK_M_IMPORT_PROGRAM: {
    1699             :                 char result[UTIL_LINE_SIZE], *line, *pos;
    1700             : 
    1701           0 :                 (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false);
    1702           0 :                 log_rule_debug(dev, rules, "Importing properties from results of '%s'", buf);
    1703             : 
    1704           0 :                 r = udev_event_spawn(event, timeout_usec, true, buf, result, sizeof result);
    1705           0 :                 if (r < 0)
    1706           0 :                         return log_rule_error_errno(dev, rules, r, "Failed to execute '%s': %m", buf);
    1707           0 :                 if (r > 0) {
    1708           0 :                         log_rule_debug(dev, rules, "Command \"%s\" returned %d (error), ignoring", buf, r);
    1709           0 :                         return token->op == OP_NOMATCH;
    1710             :                 }
    1711             : 
    1712           0 :                 for (line = result; !isempty(line); line = pos) {
    1713             :                         char *key, *value;
    1714             : 
    1715           0 :                         pos = strchr(line, '\n');
    1716           0 :                         if (pos)
    1717           0 :                                 *pos++ = '\0';
    1718             : 
    1719           0 :                         r = get_property_from_string(line, &key, &value);
    1720           0 :                         if (r < 0) {
    1721           0 :                                 log_rule_debug_errno(dev, rules, r,
    1722             :                                                      "Failed to parse key and value from '%s', ignoring: %m",
    1723             :                                                      line);
    1724           0 :                                 continue;
    1725             :                         }
    1726           0 :                         if (r == 0)
    1727           0 :                                 continue;
    1728             : 
    1729           0 :                         r = device_add_property(dev, key, value);
    1730           0 :                         if (r < 0)
    1731           0 :                                 return log_rule_error_errno(dev, rules, r,
    1732             :                                                             "Failed to add property %s=%s: %m",
    1733             :                                                             key, value);
    1734             :                 }
    1735             : 
    1736           0 :                 return token->op == OP_MATCH;
    1737             :         }
    1738           0 :         case TK_M_IMPORT_BUILTIN: {
    1739           0 :                 UdevBuiltinCommand cmd = PTR_TO_UDEV_BUILTIN_CMD(token->data);
    1740           0 :                 unsigned mask = 1U << (int) cmd;
    1741             : 
    1742           0 :                 if (udev_builtin_run_once(cmd)) {
    1743             :                         /* check if we ran already */
    1744           0 :                         if (event->builtin_run & mask) {
    1745           0 :                                 log_rule_debug(dev, rules, "Skipping builtin '%s' in IMPORT key",
    1746             :                                                udev_builtin_name(cmd));
    1747             :                                 /* return the result from earlier run */
    1748           0 :                                 return token->op == (event->builtin_ret & mask ? OP_NOMATCH : OP_MATCH);
    1749             :                         }
    1750             :                         /* mark as ran */
    1751           0 :                         event->builtin_run |= mask;
    1752             :                 }
    1753             : 
    1754           0 :                 (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false);
    1755           0 :                 log_rule_debug(dev, rules, "Importing properties from results of builtin command '%s'", buf);
    1756             : 
    1757           0 :                 r = udev_builtin_run(dev, cmd, buf, false);
    1758           0 :                 if (r < 0) {
    1759             :                         /* remember failure */
    1760           0 :                         log_rule_debug_errno(dev, rules, r, "Failed to run builtin '%s': %m", buf);
    1761           0 :                         event->builtin_ret |= mask;
    1762             :                 }
    1763           0 :                 return token->op == (r >= 0 ? OP_MATCH : OP_NOMATCH);
    1764             :         }
    1765           0 :         case TK_M_IMPORT_DB: {
    1766           0 :                 if (!event->dev_db_clone)
    1767           0 :                         return token->op == OP_NOMATCH;
    1768           0 :                 r = sd_device_get_property_value(event->dev_db_clone, token->value, &val);
    1769           0 :                 if (r == -ENOENT)
    1770           0 :                         return token->op == OP_NOMATCH;
    1771           0 :                 if (r < 0)
    1772           0 :                         return log_rule_error_errno(dev, rules, r,
    1773             :                                                     "Failed to get property '%s' from database: %m",
    1774             :                                                     token->value);
    1775             : 
    1776           0 :                 r = device_add_property(dev, token->value, val);
    1777           0 :                 if (r < 0)
    1778           0 :                         return log_rule_error_errno(dev, rules, r, "Failed to add property '%s=%s': %m",
    1779             :                                                     token->value, val);
    1780           0 :                 return token->op == OP_MATCH;
    1781             :         }
    1782           0 :         case TK_M_IMPORT_CMDLINE: {
    1783           0 :                 _cleanup_free_ char *value = NULL;
    1784             : 
    1785           0 :                 r = proc_cmdline_get_key(token->value, PROC_CMDLINE_VALUE_OPTIONAL, &value);
    1786           0 :                 if (r < 0)
    1787           0 :                         return log_rule_error_errno(dev, rules, r,
    1788             :                                                     "Failed to read '%s' option from /proc/cmdline: %m",
    1789             :                                                     token->value);
    1790           0 :                 if (r == 0)
    1791           0 :                         return token->op == OP_NOMATCH;
    1792             : 
    1793           0 :                 r = device_add_property(dev, token->value, value ?: "1");
    1794           0 :                 if (r < 0)
    1795           0 :                         return log_rule_error_errno(dev, rules, r, "Failed to add property '%s=%s': %m",
    1796             :                                                     token->value, value ?: "1");
    1797           0 :                 return token->op == OP_MATCH;
    1798             :         }
    1799           0 :         case TK_M_IMPORT_PARENT: {
    1800           0 :                 (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false);
    1801           0 :                 r = import_parent_into_properties(dev, buf);
    1802           0 :                 if (r < 0)
    1803           0 :                         return log_rule_error_errno(dev, rules, r,
    1804             :                                                     "Failed to import properties '%s' from parent: %m",
    1805             :                                                     buf);
    1806           0 :                 return token->op == (r > 0 ? OP_MATCH : OP_NOMATCH);
    1807             :         }
    1808           0 :         case TK_M_RESULT:
    1809           0 :                 return token_match_string(token, event->program_result);
    1810           0 :         case TK_A_OPTIONS_STRING_ESCAPE_NONE:
    1811           0 :                 event->esc = ESCAPE_NONE;
    1812           0 :                 break;
    1813           0 :         case TK_A_OPTIONS_STRING_ESCAPE_REPLACE:
    1814           0 :                 event->esc = ESCAPE_REPLACE;
    1815           0 :                 break;
    1816           0 :         case TK_A_OPTIONS_DB_PERSIST:
    1817           0 :                 device_set_db_persist(dev);
    1818           0 :                 break;
    1819           0 :         case TK_A_OPTIONS_INOTIFY_WATCH:
    1820           0 :                 if (event->inotify_watch_final)
    1821           0 :                         break;
    1822           0 :                 if (token->op == OP_ASSIGN_FINAL)
    1823           0 :                         event->inotify_watch_final = true;
    1824             : 
    1825           0 :                 event->inotify_watch = token->data;
    1826           0 :                 break;
    1827           0 :         case TK_A_OPTIONS_DEVLINK_PRIORITY:
    1828           0 :                 device_set_devlink_priority(dev, PTR_TO_INT(token->data));
    1829           0 :                 break;
    1830           0 :         case TK_A_OWNER: {
    1831             :                 char owner[UTIL_NAME_SIZE];
    1832           0 :                 const char *ow = owner;
    1833             : 
    1834           0 :                 if (event->owner_final)
    1835           0 :                         break;
    1836           0 :                 if (token->op == OP_ASSIGN_FINAL)
    1837           0 :                         event->owner_final = true;
    1838             : 
    1839           0 :                 (void) udev_event_apply_format(event, token->value, owner, sizeof(owner), false);
    1840           0 :                 r = get_user_creds(&ow, &event->uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
    1841           0 :                 if (r < 0)
    1842           0 :                         log_unknown_owner(dev, rules, r, "user", owner);
    1843             :                 else
    1844           0 :                         log_rule_debug(dev, rules, "OWNER %s(%u)", owner, event->uid);
    1845           0 :                 break;
    1846             :         }
    1847           0 :         case TK_A_GROUP: {
    1848             :                 char group[UTIL_NAME_SIZE];
    1849           0 :                 const char *gr = group;
    1850             : 
    1851           0 :                 if (event->group_final)
    1852           0 :                         break;
    1853           0 :                 if (token->op == OP_ASSIGN_FINAL)
    1854           0 :                         event->group_final = true;
    1855             : 
    1856           0 :                 (void) udev_event_apply_format(event, token->value, group, sizeof(group), false);
    1857           0 :                 r = get_group_creds(&gr, &event->gid, USER_CREDS_ALLOW_MISSING);
    1858           0 :                 if (r < 0)
    1859           0 :                         log_unknown_owner(dev, rules, r, "group", group);
    1860             :                 else
    1861           0 :                         log_rule_debug(dev, rules, "GROUP %s(%u)", group, event->gid);
    1862           0 :                 break;
    1863             :         }
    1864           0 :         case TK_A_MODE: {
    1865             :                 char mode_str[UTIL_NAME_SIZE];
    1866             : 
    1867           0 :                 if (event->mode_final)
    1868           0 :                         break;
    1869           0 :                 if (token->op == OP_ASSIGN_FINAL)
    1870           0 :                         event->mode_final = true;
    1871             : 
    1872           0 :                 (void) udev_event_apply_format(event, token->value, mode_str, sizeof(mode_str), false);
    1873           0 :                 r = parse_mode(mode_str, &event->mode);
    1874           0 :                 if (r < 0)
    1875           0 :                         log_rule_error_errno(dev, rules, r, "Failed to parse mode '%s', ignoring: %m", mode_str);
    1876             :                 else
    1877           0 :                         log_rule_debug(dev, rules, "MODE %#o", event->mode);
    1878           0 :                 break;
    1879             :         }
    1880           0 :         case TK_A_OWNER_ID:
    1881           0 :                 if (event->owner_final)
    1882           0 :                         break;
    1883           0 :                 if (token->op == OP_ASSIGN_FINAL)
    1884           0 :                         event->owner_final = true;
    1885           0 :                 if (!token->data)
    1886           0 :                         break;
    1887           0 :                 event->uid = PTR_TO_UID(token->data);
    1888           0 :                 log_rule_debug(dev, rules, "OWNER %u", event->uid);
    1889           0 :                 break;
    1890           0 :         case TK_A_GROUP_ID:
    1891           0 :                 if (event->group_final)
    1892           0 :                         break;
    1893           0 :                 if (token->op == OP_ASSIGN_FINAL)
    1894           0 :                         event->group_final = true;
    1895           0 :                 if (!token->data)
    1896           0 :                         break;
    1897           0 :                 event->gid = PTR_TO_GID(token->data);
    1898           0 :                 log_rule_debug(dev, rules, "GROUP %u", event->gid);
    1899           0 :                 break;
    1900           0 :         case TK_A_MODE_ID:
    1901           0 :                 if (event->mode_final)
    1902           0 :                         break;
    1903           0 :                 if (token->op == OP_ASSIGN_FINAL)
    1904           0 :                         event->mode_final = true;
    1905           0 :                 if (!token->data)
    1906           0 :                         break;
    1907           0 :                 event->mode = PTR_TO_MODE(token->data);
    1908           0 :                 log_rule_debug(dev, rules, "MODE %#o", event->mode);
    1909           0 :                 break;
    1910           0 :         case TK_A_SECLABEL: {
    1911           0 :                 _cleanup_free_ char *name = NULL, *label = NULL;
    1912           0 :                 char label_str[UTIL_LINE_SIZE] = {};
    1913             : 
    1914           0 :                 name = strdup((const char*) token->data);
    1915           0 :                 if (!name)
    1916           0 :                         return log_oom();
    1917             : 
    1918           0 :                 (void) udev_event_apply_format(event, token->value, label_str, sizeof(label_str), false);
    1919           0 :                 if (!isempty(label_str))
    1920           0 :                         label = strdup(label_str);
    1921             :                 else
    1922           0 :                         label = strdup(token->value);
    1923           0 :                 if (!label)
    1924           0 :                         return log_oom();
    1925             : 
    1926           0 :                 if (token->op == OP_ASSIGN)
    1927           0 :                         ordered_hashmap_clear_free_free(event->seclabel_list);
    1928             : 
    1929           0 :                 r = ordered_hashmap_ensure_allocated(&event->seclabel_list, NULL);
    1930           0 :                 if (r < 0)
    1931           0 :                         return log_oom();
    1932             : 
    1933           0 :                 r = ordered_hashmap_put(event->seclabel_list, name, label);
    1934           0 :                 if (r < 0)
    1935           0 :                         return log_oom();
    1936           0 :                 log_rule_debug(dev, rules, "SECLABEL{%s}='%s'", name, label);
    1937           0 :                 name = label = NULL;
    1938           0 :                 break;
    1939             :         }
    1940           0 :         case TK_A_ENV: {
    1941           0 :                 const char *name = (const char*) token->data;
    1942           0 :                 char value_new[UTIL_NAME_SIZE], *p = value_new;
    1943           0 :                 size_t l = sizeof(value_new);
    1944             : 
    1945           0 :                 if (isempty(token->value)) {
    1946           0 :                         if (token->op == OP_ADD)
    1947           0 :                                 break;
    1948           0 :                         r = device_add_property(dev, name, NULL);
    1949           0 :                         if (r < 0)
    1950           0 :                                 return log_rule_error_errno(dev, rules, r, "Failed to remove property '%s': %m", name);
    1951           0 :                         break;
    1952             :                 }
    1953             : 
    1954           0 :                 if (token->op == OP_ADD &&
    1955           0 :                     sd_device_get_property_value(dev, name, &val) >= 0)
    1956           0 :                         l = strpcpyl(&p, l, val, " ", NULL);
    1957             : 
    1958           0 :                 (void) udev_event_apply_format(event, token->value, p, l, false);
    1959             : 
    1960           0 :                 r = device_add_property(dev, name, value_new);
    1961           0 :                 if (r < 0)
    1962           0 :                         return log_rule_error_errno(dev, rules, r, "Failed to add property '%s=%s': %m", name, value_new);
    1963           0 :                 break;
    1964             :         }
    1965           0 :         case TK_A_TAG: {
    1966           0 :                 (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false);
    1967           0 :                 if (token->op == OP_ASSIGN)
    1968           0 :                         device_cleanup_tags(dev);
    1969             : 
    1970           0 :                 if (buf[strspn(buf, ALPHANUMERICAL "-_")] != '\0') {
    1971           0 :                         log_rule_error(dev, rules, "Invalid tag name '%s', ignoring", buf);
    1972           0 :                         break;
    1973             :                 }
    1974           0 :                 if (token->op == OP_REMOVE)
    1975           0 :                         device_remove_tag(dev, buf);
    1976             :                 else {
    1977           0 :                         r = device_add_tag(dev, buf);
    1978           0 :                         if (r < 0)
    1979           0 :                                 return log_rule_error_errno(dev, rules, r, "Failed to add tag '%s': %m", buf);
    1980             :                 }
    1981           0 :                 break;
    1982             :         }
    1983           0 :         case TK_A_NAME: {
    1984           0 :                 if (event->name_final)
    1985           0 :                         break;
    1986           0 :                 if (token->op == OP_ASSIGN_FINAL)
    1987           0 :                         event->name_final = true;
    1988             : 
    1989           0 :                 (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false);
    1990           0 :                 if (IN_SET(event->esc, ESCAPE_UNSET, ESCAPE_REPLACE)) {
    1991           0 :                         count = util_replace_chars(buf, "/");
    1992           0 :                         if (count > 0)
    1993           0 :                                 log_rule_debug(dev, rules, "Replaced %zu character(s) from result of NAME=\"%s\"",
    1994             :                                                count, token->value);
    1995             :                 }
    1996           0 :                 if (sd_device_get_devnum(dev, NULL) >= 0 &&
    1997           0 :                     (sd_device_get_devname(dev, &val) < 0 ||
    1998           0 :                      !streq_ptr(buf, startswith(val, "/dev/")))) {
    1999           0 :                         log_rule_error(dev, rules,
    2000             :                                        "Kernel device nodes cannot be renamed, ignoring NAME=\"%s\"; please fix it.",
    2001             :                                        token->value);
    2002           0 :                         break;
    2003             :                 }
    2004           0 :                 if (free_and_strdup(&event->name, buf) < 0)
    2005           0 :                         return log_oom();
    2006             : 
    2007           0 :                 log_rule_debug(dev, rules, "NAME '%s'", event->name);
    2008           0 :                 break;
    2009             :         }
    2010           0 :         case TK_A_DEVLINK: {
    2011             :                 char *p;
    2012             : 
    2013           0 :                 if (event->devlink_final)
    2014           0 :                         break;
    2015           0 :                 if (sd_device_get_devnum(dev, NULL) < 0)
    2016           0 :                         break;
    2017           0 :                 if (token->op == OP_ASSIGN_FINAL)
    2018           0 :                         event->devlink_final = true;
    2019           0 :                 if (IN_SET(token->op, OP_ASSIGN, OP_ASSIGN_FINAL))
    2020           0 :                         device_cleanup_devlinks(dev);
    2021             : 
    2022             :                 /* allow multiple symlinks separated by spaces */
    2023           0 :                 (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), event->esc != ESCAPE_NONE);
    2024           0 :                 if (event->esc == ESCAPE_UNSET)
    2025           0 :                         count = util_replace_chars(buf, "/ ");
    2026           0 :                 else if (event->esc == ESCAPE_REPLACE)
    2027           0 :                         count = util_replace_chars(buf, "/");
    2028             :                 else
    2029           0 :                         count = 0;
    2030           0 :                 if (count > 0)
    2031           0 :                         log_rule_debug(dev, rules, "Replaced %zu character(s) from result of LINK", count);
    2032             : 
    2033           0 :                 p = skip_leading_chars(buf, NULL);
    2034           0 :                 while (!isempty(p)) {
    2035             :                         char filename[UTIL_PATH_SIZE], *next;
    2036             : 
    2037           0 :                         next = strchr(p, ' ');
    2038           0 :                         if (next) {
    2039           0 :                                 *next++ = '\0';
    2040           0 :                                 next = skip_leading_chars(next, NULL);
    2041             :                         }
    2042             : 
    2043           0 :                         strscpyl(filename, sizeof(filename), "/dev/", p, NULL);
    2044           0 :                         r = device_add_devlink(dev, filename);
    2045           0 :                         if (r < 0)
    2046           0 :                                 return log_rule_error_errno(dev, rules, r, "Failed to add devlink '%s': %m", filename);
    2047             : 
    2048           0 :                         log_rule_debug(dev, rules, "LINK '%s'", p);
    2049           0 :                         p = next;
    2050             :                 }
    2051           0 :                 break;
    2052             :         }
    2053           0 :         case TK_A_ATTR: {
    2054           0 :                 const char *key_name = (const char*) token->data;
    2055             :                 char value[UTIL_NAME_SIZE];
    2056             : 
    2057           0 :                 if (util_resolve_subsys_kernel(key_name, buf, sizeof(buf), false) < 0 &&
    2058           0 :                     sd_device_get_syspath(dev, &val) >= 0)
    2059           0 :                         strscpyl(buf, sizeof(buf), val, "/", key_name, NULL);
    2060             : 
    2061           0 :                 r = attr_subst_subdir(buf);
    2062           0 :                 if (r < 0) {
    2063           0 :                         log_rule_error_errno(dev, rules, r, "Could not find file matches '%s', ignoring: %m", buf);
    2064           0 :                         break;
    2065             :                 }
    2066           0 :                 (void) udev_event_apply_format(event, token->value, value, sizeof(value), false);
    2067             : 
    2068           0 :                 log_rule_debug(dev, rules, "ATTR '%s' writing '%s'", buf, value);
    2069           0 :                 r = write_string_file(buf, value, WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_DISABLE_BUFFER);
    2070           0 :                 if (r < 0)
    2071           0 :                         log_rule_error_errno(dev, rules, r, "Failed to write ATTR{%s}, ignoring: %m", buf);
    2072           0 :                 break;
    2073             :         }
    2074           0 :         case TK_A_SYSCTL: {
    2075             :                 char value[UTIL_NAME_SIZE];
    2076             : 
    2077           0 :                 (void) udev_event_apply_format(event, (const char*) token->data, buf, sizeof(buf), false);
    2078           0 :                 (void) udev_event_apply_format(event, token->value, value, sizeof(value), false);
    2079           0 :                 sysctl_normalize(buf);
    2080           0 :                 log_rule_debug(dev, rules, "SYSCTL '%s' writing '%s'", buf, value);
    2081           0 :                 r = sysctl_write(buf, value);
    2082           0 :                 if (r < 0)
    2083           0 :                         log_rule_error_errno(dev, rules, r, "Failed to write SYSCTL{%s}='%s', ignoring: %m", buf, value);
    2084           0 :                 break;
    2085             :         }
    2086           0 :         case TK_A_RUN_BUILTIN:
    2087             :         case TK_A_RUN_PROGRAM: {
    2088           0 :                 _cleanup_free_ char *cmd = NULL;
    2089             : 
    2090           0 :                 if (event->run_final)
    2091           0 :                         break;
    2092           0 :                 if (token->op == OP_ASSIGN_FINAL)
    2093           0 :                         event->run_final = true;
    2094             : 
    2095           0 :                 if (IN_SET(token->op, OP_ASSIGN, OP_ASSIGN_FINAL))
    2096           0 :                         ordered_hashmap_clear_free_key(event->run_list);
    2097             : 
    2098           0 :                 r = ordered_hashmap_ensure_allocated(&event->run_list, NULL);
    2099           0 :                 if (r < 0)
    2100           0 :                         return log_oom();
    2101             : 
    2102           0 :                 (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false);
    2103             : 
    2104           0 :                 cmd = strdup(buf);
    2105           0 :                 if (!cmd)
    2106           0 :                         return log_oom();
    2107             : 
    2108           0 :                 r = ordered_hashmap_put(event->run_list, cmd, token->data);
    2109           0 :                 if (r < 0)
    2110           0 :                         return log_oom();
    2111             : 
    2112           0 :                 TAKE_PTR(cmd);
    2113             : 
    2114           0 :                 log_rule_debug(dev, rules, "RUN '%s'", token->value);
    2115           0 :                 break;
    2116             :         }
    2117           0 :         case TK_A_OPTIONS_STATIC_NODE:
    2118             :                 /* do nothing for events. */
    2119           0 :                 break;
    2120           0 :         default:
    2121           0 :                 assert_not_reached("Invalid token type");
    2122             :         }
    2123             : 
    2124           0 :         return true;
    2125             : }
    2126             : 
    2127           0 : static bool token_is_for_parents(UdevRuleToken *token) {
    2128           0 :         return token->type >= TK_M_PARENTS_KERNEL && token->type <= TK_M_PARENTS_TAG;
    2129             : }
    2130             : 
    2131           0 : static int udev_rule_apply_parent_token_to_event(
    2132             :                 UdevRules *rules,
    2133             :                 UdevEvent *event) {
    2134             : 
    2135             :         UdevRuleLine *line;
    2136             :         UdevRuleToken *head;
    2137             :         int r;
    2138             : 
    2139           0 :         line = rules->current_file->current_line;
    2140           0 :         head = rules->current_file->current_line->current_token;
    2141           0 :         event->dev_parent = event->dev;
    2142             :         for (;;) {
    2143           0 :                 LIST_FOREACH(tokens, line->current_token, head) {
    2144           0 :                         if (!token_is_for_parents(line->current_token))
    2145           0 :                                 return true; /* All parent tokens match. */
    2146           0 :                         r = udev_rule_apply_token_to_event(rules, event->dev_parent, event, 0, NULL);
    2147           0 :                         if (r < 0)
    2148           0 :                                 return r;
    2149           0 :                         if (r == 0)
    2150           0 :                                 break;
    2151             :                 }
    2152           0 :                 if (!line->current_token)
    2153             :                         /* All parent tokens match. But no assign tokens in the line. Hmm... */
    2154           0 :                         return true;
    2155             : 
    2156           0 :                 if (sd_device_get_parent(event->dev_parent, &event->dev_parent) < 0) {
    2157           0 :                         event->dev_parent = NULL;
    2158           0 :                         return false;
    2159             :                 }
    2160             :         }
    2161             : }
    2162             : 
    2163           0 : static int udev_rule_apply_line_to_event(
    2164             :                 UdevRules *rules,
    2165             :                 UdevEvent *event,
    2166             :                 usec_t timeout_usec,
    2167             :                 Hashmap *properties_list,
    2168             :                 UdevRuleLine **next_line) {
    2169             : 
    2170           0 :         UdevRuleLine *line = rules->current_file->current_line;
    2171           0 :         UdevRuleLineType mask = LINE_HAS_GOTO | LINE_UPDATE_SOMETHING;
    2172             :         UdevRuleToken *token, *next_token;
    2173           0 :         bool parents_done = false;
    2174             :         DeviceAction action;
    2175             :         int r;
    2176             : 
    2177           0 :         r = device_get_action(event->dev, &action);
    2178           0 :         if (r < 0)
    2179           0 :                 return r;
    2180             : 
    2181           0 :         if (action != DEVICE_ACTION_REMOVE) {
    2182           0 :                 if (sd_device_get_devnum(event->dev, NULL) >= 0)
    2183           0 :                         mask |= LINE_HAS_DEVLINK;
    2184             : 
    2185           0 :                 if (sd_device_get_ifindex(event->dev, NULL) >= 0)
    2186           0 :                         mask |= LINE_HAS_NAME;
    2187             :         }
    2188             : 
    2189           0 :         if ((line->type & mask) == 0)
    2190           0 :                 return 0;
    2191             : 
    2192           0 :         event->esc = ESCAPE_UNSET;
    2193           0 :         LIST_FOREACH_SAFE(tokens, token, next_token, line->tokens) {
    2194           0 :                 line->current_token = token;
    2195             : 
    2196           0 :                 if (token_is_for_parents(token)) {
    2197           0 :                         if (parents_done)
    2198           0 :                                 continue;
    2199             : 
    2200           0 :                         r = udev_rule_apply_parent_token_to_event(rules, event);
    2201           0 :                         if (r <= 0)
    2202           0 :                                 return r;
    2203             : 
    2204           0 :                         parents_done = true;
    2205           0 :                         continue;
    2206             :                 }
    2207             : 
    2208           0 :                 r = udev_rule_apply_token_to_event(rules, event->dev, event, timeout_usec, properties_list);
    2209           0 :                 if (r <= 0)
    2210           0 :                         return r;
    2211             :         }
    2212             : 
    2213           0 :         if (line->goto_line)
    2214           0 :                 *next_line = line->goto_line;
    2215             : 
    2216           0 :         return 0;
    2217             : }
    2218             : 
    2219           0 : int udev_rules_apply_to_event(
    2220             :                 UdevRules *rules,
    2221             :                 UdevEvent *event,
    2222             :                 usec_t timeout_usec,
    2223             :                 Hashmap *properties_list) {
    2224             : 
    2225             :         UdevRuleFile *file;
    2226             :         UdevRuleLine *next_line;
    2227             :         int r;
    2228             : 
    2229           0 :         assert(rules);
    2230           0 :         assert(event);
    2231             : 
    2232           0 :         LIST_FOREACH(rule_files, file, rules->rule_files) {
    2233           0 :                 rules->current_file = file;
    2234           0 :                 LIST_FOREACH_SAFE(rule_lines, file->current_line, next_line, file->rule_lines) {
    2235           0 :                         r = udev_rule_apply_line_to_event(rules, event, timeout_usec, properties_list, &next_line);
    2236           0 :                         if (r < 0)
    2237           0 :                                 return r;
    2238             :                 }
    2239             :         }
    2240             : 
    2241           0 :         return 0;
    2242             : }
    2243             : 
    2244           0 : static int apply_static_dev_perms(const char *devnode, uid_t uid, gid_t gid, mode_t mode, char **tags) {
    2245             :         char device_node[UTIL_PATH_SIZE], tags_dir[UTIL_PATH_SIZE], tag_symlink[UTIL_PATH_SIZE];
    2246           0 :         _cleanup_free_ char *unescaped_filename = NULL;
    2247             :         struct stat stats;
    2248             :         char **t;
    2249             :         int r;
    2250             : 
    2251           0 :         assert(devnode);
    2252             : 
    2253           0 :         if (uid == UID_INVALID && gid == GID_INVALID && mode == MODE_INVALID && !tags)
    2254           0 :                 return 0;
    2255             : 
    2256           0 :         strscpyl(device_node, sizeof(device_node), "/dev/", devnode, NULL);
    2257           0 :         if (stat(device_node, &stats) < 0) {
    2258           0 :                 if (errno != ENOENT)
    2259           0 :                         return log_error_errno(errno, "Failed to stat %s: %m", device_node);
    2260           0 :                 return 0;
    2261             :         }
    2262             : 
    2263           0 :         if (!S_ISBLK(stats.st_mode) && !S_ISCHR(stats.st_mode)) {
    2264           0 :                 log_warning("%s is neither block nor character device, ignoring.", device_node);
    2265           0 :                 return 0;
    2266             :         }
    2267             : 
    2268           0 :         if (!strv_isempty(tags)) {
    2269           0 :                 unescaped_filename = xescape(devnode, "/.");
    2270           0 :                 if (!unescaped_filename)
    2271           0 :                         return log_oom();
    2272             :         }
    2273             : 
    2274             :         /* export the tags to a directory as symlinks, allowing otherwise dead nodes to be tagged */
    2275           0 :         STRV_FOREACH(t, tags) {
    2276           0 :                 strscpyl(tags_dir, sizeof(tags_dir), "/run/udev/static_node-tags/", *t, "/", NULL);
    2277           0 :                 r = mkdir_p(tags_dir, 0755);
    2278           0 :                 if (r < 0)
    2279           0 :                         return log_error_errno(r, "Failed to create %s: %m", tags_dir);
    2280             : 
    2281           0 :                 strscpyl(tag_symlink, sizeof(tag_symlink), tags_dir, unescaped_filename, NULL);
    2282           0 :                 r = symlink(device_node, tag_symlink);
    2283           0 :                 if (r < 0 && errno != EEXIST)
    2284           0 :                         return log_error_errno(errno, "Failed to create symlink %s -> %s: %m",
    2285             :                                                tag_symlink, device_node);
    2286             :         }
    2287             : 
    2288             :         /* don't touch the permissions if only the tags were set */
    2289           0 :         if (uid == UID_INVALID && gid == GID_INVALID && mode == MODE_INVALID)
    2290           0 :                 return 0;
    2291             : 
    2292           0 :         if (mode == MODE_INVALID)
    2293           0 :                 mode = gid_is_valid(gid) ? 0660 : 0600;
    2294           0 :         if (!uid_is_valid(uid))
    2295           0 :                 uid = 0;
    2296           0 :         if (!gid_is_valid(gid))
    2297           0 :                 gid = 0;
    2298             : 
    2299           0 :         r = chmod_and_chown(device_node, mode, uid, gid);
    2300           0 :         if (r < 0)
    2301           0 :                 return log_error_errno(errno, "Failed to chown '%s' %u %u: %m",
    2302             :                                                device_node, uid, gid);
    2303             :         else
    2304           0 :                 log_debug("chown '%s' %u:%u", device_node, uid, gid);
    2305             : 
    2306           0 :         (void) utimensat(AT_FDCWD, device_node, NULL, 0);
    2307           0 :         return 0;
    2308             : }
    2309             : 
    2310           0 : static int udev_rule_line_apply_static_dev_perms(UdevRuleLine *rule_line) {
    2311             :         UdevRuleToken *token;
    2312           0 :         _cleanup_free_ char **tags = NULL;
    2313           0 :         uid_t uid = UID_INVALID;
    2314           0 :         gid_t gid = GID_INVALID;
    2315           0 :         mode_t mode = MODE_INVALID;
    2316             :         int r;
    2317             : 
    2318           0 :         assert(rule_line);
    2319             : 
    2320           0 :         if (!FLAGS_SET(rule_line->type, LINE_HAS_STATIC_NODE))
    2321           0 :                 return 0;
    2322             : 
    2323           0 :         LIST_FOREACH(tokens, token, rule_line->tokens)
    2324           0 :                 if (token->type == TK_A_OWNER_ID)
    2325           0 :                         uid = PTR_TO_UID(token->data);
    2326           0 :                 else if (token->type == TK_A_GROUP_ID)
    2327           0 :                         gid = PTR_TO_GID(token->data);
    2328           0 :                 else if (token->type == TK_A_MODE_ID)
    2329           0 :                         mode = PTR_TO_MODE(token->data);
    2330           0 :                 else if (token->type == TK_A_TAG) {
    2331           0 :                         r = strv_extend(&tags, token->value);
    2332           0 :                         if (r < 0)
    2333           0 :                                 return log_oom();
    2334           0 :                 } else if (token->type == TK_A_OPTIONS_STATIC_NODE) {
    2335           0 :                         r = apply_static_dev_perms(token->value, uid, gid, mode, tags);
    2336           0 :                         if (r < 0)
    2337           0 :                                 return r;
    2338             :                 }
    2339             : 
    2340           0 :         return 0;
    2341             : }
    2342             : 
    2343           0 : int udev_rules_apply_static_dev_perms(UdevRules *rules) {
    2344             :         UdevRuleFile *file;
    2345             :         UdevRuleLine *line;
    2346             :         int r;
    2347             : 
    2348           0 :         assert(rules);
    2349             : 
    2350           0 :         LIST_FOREACH(rule_files, file, rules->rule_files)
    2351           0 :                 LIST_FOREACH(rule_lines, line, file->rule_lines) {
    2352           0 :                         r = udev_rule_line_apply_static_dev_perms(line);
    2353           0 :                         if (r < 0)
    2354           0 :                                 return r;
    2355             :                 }
    2356             : 
    2357           0 :         return 0;
    2358             : }

Generated by: LCOV version 1.14