LCOV - code coverage report
Current view: top level - udev - udev-rules.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 0 1407 0.0 %
Date: 2019-08-23 13:36:53 Functions: 0 36 0.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 3282 0.0 %

           Branch data     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