199ca880aSopenharmony_ci/*
299ca880aSopenharmony_ci * Copyright (C) 2003-2012 Kay Sievers <kay@vrfy.org>
399ca880aSopenharmony_ci *
499ca880aSopenharmony_ci * This program is free software: you can redistribute it and/or modify
599ca880aSopenharmony_ci * it under the terms of the GNU General Public License as published by
699ca880aSopenharmony_ci * the Free Software Foundation, either version 2 of the License, or
799ca880aSopenharmony_ci * (at your option) any later version.
899ca880aSopenharmony_ci *
999ca880aSopenharmony_ci * This program is distributed in the hope that it will be useful,
1099ca880aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
1199ca880aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1299ca880aSopenharmony_ci * GNU General Public License for more details.
1399ca880aSopenharmony_ci *
1499ca880aSopenharmony_ci * You should have received a copy of the GNU General Public License
1599ca880aSopenharmony_ci * along with this program.  If not, see <http://www.gnu.org/licenses/>.
1699ca880aSopenharmony_ci */
1799ca880aSopenharmony_ci
1899ca880aSopenharmony_ci#include <stddef.h>
1999ca880aSopenharmony_ci#include <limits.h>
2099ca880aSopenharmony_ci#include <stdlib.h>
2199ca880aSopenharmony_ci#include <stdbool.h>
2299ca880aSopenharmony_ci#include <string.h>
2399ca880aSopenharmony_ci#include <stdio.h>
2499ca880aSopenharmony_ci#include <fcntl.h>
2599ca880aSopenharmony_ci#include <ctype.h>
2699ca880aSopenharmony_ci#include <unistd.h>
2799ca880aSopenharmony_ci#include <errno.h>
2899ca880aSopenharmony_ci#include <dirent.h>
2999ca880aSopenharmony_ci#include <fnmatch.h>
3099ca880aSopenharmony_ci#include <time.h>
3199ca880aSopenharmony_ci#include <sys/sysmacros.h>
3299ca880aSopenharmony_ci
3399ca880aSopenharmony_ci#include "udev.h"
3499ca880aSopenharmony_ci#include "path-util.h"
3599ca880aSopenharmony_ci#include "conf-files.h"
3699ca880aSopenharmony_ci#include "strbuf.h"
3799ca880aSopenharmony_ci#include "strv.h"
3899ca880aSopenharmony_ci#include "util.h"
3999ca880aSopenharmony_ci#include "sysctl-util.h"
4099ca880aSopenharmony_ci
4199ca880aSopenharmony_ci#define PREALLOC_TOKEN          2048
4299ca880aSopenharmony_ci
4399ca880aSopenharmony_cistruct uid_gid {
4499ca880aSopenharmony_ci        unsigned int name_off;
4599ca880aSopenharmony_ci        union {
4699ca880aSopenharmony_ci                uid_t uid;
4799ca880aSopenharmony_ci                gid_t gid;
4899ca880aSopenharmony_ci        };
4999ca880aSopenharmony_ci};
5099ca880aSopenharmony_ci
5199ca880aSopenharmony_cistatic const char* const rules_dirs[] = {
5299ca880aSopenharmony_ci        UDEV_CONF_DIR "/rules.d",
5399ca880aSopenharmony_ci        UDEV_RULES_DIR,
5499ca880aSopenharmony_ci        UDEV_ROOT_RUN "/udev/rules.d",
5599ca880aSopenharmony_ci        UDEV_LIBEXEC_DIR "/rules.d",
5699ca880aSopenharmony_ci#ifdef HAVE_SPLIT_USR
5799ca880aSopenharmony_ci        "/lib/udev/rules.d",
5899ca880aSopenharmony_ci        "/usr/lib/udev/rules.d",
5999ca880aSopenharmony_ci#endif
6099ca880aSopenharmony_ci        NULL};
6199ca880aSopenharmony_ci
6299ca880aSopenharmony_cistruct udev_rules {
6399ca880aSopenharmony_ci        struct udev *udev;
6499ca880aSopenharmony_ci        usec_t dirs_ts_usec;
6599ca880aSopenharmony_ci        int resolve_names;
6699ca880aSopenharmony_ci
6799ca880aSopenharmony_ci        /* every key in the rules file becomes a token */
6899ca880aSopenharmony_ci        struct token *tokens;
6999ca880aSopenharmony_ci        unsigned int token_cur;
7099ca880aSopenharmony_ci        unsigned int token_max;
7199ca880aSopenharmony_ci
7299ca880aSopenharmony_ci        /* all key strings are copied and de-duplicated in a single continuous string buffer */
7399ca880aSopenharmony_ci        struct strbuf *strbuf;
7499ca880aSopenharmony_ci
7599ca880aSopenharmony_ci        /* during rule parsing, uid/gid lookup results are cached */
7699ca880aSopenharmony_ci        struct uid_gid *uids;
7799ca880aSopenharmony_ci        unsigned int uids_cur;
7899ca880aSopenharmony_ci        unsigned int uids_max;
7999ca880aSopenharmony_ci        struct uid_gid *gids;
8099ca880aSopenharmony_ci        unsigned int gids_cur;
8199ca880aSopenharmony_ci        unsigned int gids_max;
8299ca880aSopenharmony_ci};
8399ca880aSopenharmony_ci
8499ca880aSopenharmony_cistatic char *rules_str(struct udev_rules *rules, unsigned int off) {
8599ca880aSopenharmony_ci        return rules->strbuf->buf + off;
8699ca880aSopenharmony_ci}
8799ca880aSopenharmony_ci
8899ca880aSopenharmony_cistatic unsigned int rules_add_string(struct udev_rules *rules, const char *s) {
8999ca880aSopenharmony_ci        return strbuf_add_string(rules->strbuf, s, strlen(s));
9099ca880aSopenharmony_ci}
9199ca880aSopenharmony_ci
9299ca880aSopenharmony_ci/* KEY=="", KEY!="", KEY+="", KEY-="", KEY="", KEY:="" */
9399ca880aSopenharmony_cienum operation_type {
9499ca880aSopenharmony_ci        OP_UNSET,
9599ca880aSopenharmony_ci
9699ca880aSopenharmony_ci        OP_MATCH,
9799ca880aSopenharmony_ci        OP_NOMATCH,
9899ca880aSopenharmony_ci        OP_MATCH_MAX,
9999ca880aSopenharmony_ci
10099ca880aSopenharmony_ci        OP_ADD,
10199ca880aSopenharmony_ci        OP_REMOVE,
10299ca880aSopenharmony_ci        OP_ASSIGN,
10399ca880aSopenharmony_ci        OP_ASSIGN_FINAL,
10499ca880aSopenharmony_ci};
10599ca880aSopenharmony_ci
10699ca880aSopenharmony_cienum string_glob_type {
10799ca880aSopenharmony_ci        GL_UNSET,
10899ca880aSopenharmony_ci        GL_PLAIN,                       /* no special chars */
10999ca880aSopenharmony_ci        GL_GLOB,                        /* shell globs ?,*,[] */
11099ca880aSopenharmony_ci        GL_SPLIT,                       /* multi-value A|B */
11199ca880aSopenharmony_ci        GL_SPLIT_GLOB,                  /* multi-value with glob A*|B* */
11299ca880aSopenharmony_ci        GL_SOMETHING,                   /* commonly used "?*" */
11399ca880aSopenharmony_ci};
11499ca880aSopenharmony_ci
11599ca880aSopenharmony_cienum string_subst_type {
11699ca880aSopenharmony_ci        SB_UNSET,
11799ca880aSopenharmony_ci        SB_NONE,
11899ca880aSopenharmony_ci        SB_FORMAT,
11999ca880aSopenharmony_ci        SB_SUBSYS,
12099ca880aSopenharmony_ci};
12199ca880aSopenharmony_ci
12299ca880aSopenharmony_ci/* tokens of a rule are sorted/handled in this order */
12399ca880aSopenharmony_cienum token_type {
12499ca880aSopenharmony_ci        TK_UNSET,
12599ca880aSopenharmony_ci        TK_RULE,
12699ca880aSopenharmony_ci
12799ca880aSopenharmony_ci        TK_M_ACTION,                    /* val */
12899ca880aSopenharmony_ci        TK_M_DEVPATH,                   /* val */
12999ca880aSopenharmony_ci        TK_M_KERNEL,                    /* val */
13099ca880aSopenharmony_ci        TK_M_DEVLINK,                   /* val */
13199ca880aSopenharmony_ci        TK_M_NAME,                      /* val */
13299ca880aSopenharmony_ci        TK_M_ENV,                       /* val, attr */
13399ca880aSopenharmony_ci        TK_M_TAG,                       /* val */
13499ca880aSopenharmony_ci        TK_M_SUBSYSTEM,                 /* val */
13599ca880aSopenharmony_ci        TK_M_DRIVER,                    /* val */
13699ca880aSopenharmony_ci        TK_M_WAITFOR,                   /* val */
13799ca880aSopenharmony_ci        TK_M_ATTR,                      /* val, attr */
13899ca880aSopenharmony_ci        TK_M_SYSCTL,                    /* val, attr */
13999ca880aSopenharmony_ci
14099ca880aSopenharmony_ci        TK_M_PARENTS_MIN,
14199ca880aSopenharmony_ci        TK_M_KERNELS,                   /* val */
14299ca880aSopenharmony_ci        TK_M_SUBSYSTEMS,                /* val */
14399ca880aSopenharmony_ci        TK_M_DRIVERS,                   /* val */
14499ca880aSopenharmony_ci        TK_M_ATTRS,                     /* val, attr */
14599ca880aSopenharmony_ci        TK_M_TAGS,                      /* val */
14699ca880aSopenharmony_ci        TK_M_PARENTS_MAX,
14799ca880aSopenharmony_ci
14899ca880aSopenharmony_ci        TK_M_TEST,                      /* val, mode_t */
14999ca880aSopenharmony_ci        TK_M_PROGRAM,                   /* val */
15099ca880aSopenharmony_ci        TK_M_IMPORT_FILE,               /* val */
15199ca880aSopenharmony_ci        TK_M_IMPORT_PROG,               /* val */
15299ca880aSopenharmony_ci        TK_M_IMPORT_BUILTIN,            /* val */
15399ca880aSopenharmony_ci        TK_M_IMPORT_DB,                 /* val */
15499ca880aSopenharmony_ci        TK_M_IMPORT_CMDLINE,            /* val */
15599ca880aSopenharmony_ci        TK_M_IMPORT_PARENT,             /* val */
15699ca880aSopenharmony_ci        TK_M_RESULT,                    /* val */
15799ca880aSopenharmony_ci        TK_M_MAX,
15899ca880aSopenharmony_ci
15999ca880aSopenharmony_ci        TK_A_STRING_ESCAPE_NONE,
16099ca880aSopenharmony_ci        TK_A_STRING_ESCAPE_REPLACE,
16199ca880aSopenharmony_ci        TK_A_DB_PERSIST,
16299ca880aSopenharmony_ci        TK_A_INOTIFY_WATCH,             /* int */
16399ca880aSopenharmony_ci        TK_A_DEVLINK_PRIO,              /* int */
16499ca880aSopenharmony_ci        TK_A_OWNER,                     /* val */
16599ca880aSopenharmony_ci        TK_A_GROUP,                     /* val */
16699ca880aSopenharmony_ci        TK_A_MODE,                      /* val */
16799ca880aSopenharmony_ci        TK_A_OWNER_ID,                  /* uid_t */
16899ca880aSopenharmony_ci        TK_A_GROUP_ID,                  /* gid_t */
16999ca880aSopenharmony_ci        TK_A_MODE_ID,                   /* mode_t */
17099ca880aSopenharmony_ci        TK_A_TAG,                       /* val */
17199ca880aSopenharmony_ci        TK_A_STATIC_NODE,               /* val */
17299ca880aSopenharmony_ci        TK_A_SECLABEL,                  /* val, attr */
17399ca880aSopenharmony_ci        TK_A_ENV,                       /* val, attr */
17499ca880aSopenharmony_ci        TK_A_NAME,                      /* val */
17599ca880aSopenharmony_ci        TK_A_DEVLINK,                   /* val */
17699ca880aSopenharmony_ci        TK_A_ATTR,                      /* val, attr */
17799ca880aSopenharmony_ci        TK_A_SYSCTL,                    /* val, attr */
17899ca880aSopenharmony_ci        TK_A_RUN_BUILTIN,               /* val, bool */
17999ca880aSopenharmony_ci        TK_A_RUN_PROGRAM,               /* val, bool */
18099ca880aSopenharmony_ci        TK_A_GOTO,                      /* size_t */
18199ca880aSopenharmony_ci
18299ca880aSopenharmony_ci        TK_END,
18399ca880aSopenharmony_ci};
18499ca880aSopenharmony_ci
18599ca880aSopenharmony_ci/* we try to pack stuff in a way that we take only 12 bytes per token */
18699ca880aSopenharmony_cistruct token {
18799ca880aSopenharmony_ci        union {
18899ca880aSopenharmony_ci                unsigned char type;                /* same in rule and key */
18999ca880aSopenharmony_ci                struct {
19099ca880aSopenharmony_ci                        enum token_type type:8;
19199ca880aSopenharmony_ci                        bool can_set_name:1;
19299ca880aSopenharmony_ci                        bool has_static_node:1;
19399ca880aSopenharmony_ci                        unsigned int unused:6;
19499ca880aSopenharmony_ci                        unsigned short token_count;
19599ca880aSopenharmony_ci                        unsigned int label_off;
19699ca880aSopenharmony_ci                        unsigned short filename_off;
19799ca880aSopenharmony_ci                        unsigned short filename_line;
19899ca880aSopenharmony_ci                } rule;
19999ca880aSopenharmony_ci                struct {
20099ca880aSopenharmony_ci                        enum token_type type:8;
20199ca880aSopenharmony_ci                        enum operation_type op:8;
20299ca880aSopenharmony_ci                        enum string_glob_type glob:8;
20399ca880aSopenharmony_ci                        enum string_subst_type subst:4;
20499ca880aSopenharmony_ci                        enum string_subst_type attrsubst:4;
20599ca880aSopenharmony_ci                        unsigned int value_off;
20699ca880aSopenharmony_ci                        union {
20799ca880aSopenharmony_ci                                unsigned int attr_off;
20899ca880aSopenharmony_ci                                unsigned int rule_goto;
20999ca880aSopenharmony_ci                                mode_t  mode;
21099ca880aSopenharmony_ci                                uid_t uid;
21199ca880aSopenharmony_ci                                gid_t gid;
21299ca880aSopenharmony_ci                                int devlink_prio;
21399ca880aSopenharmony_ci                                int watch;
21499ca880aSopenharmony_ci                                enum udev_builtin_cmd builtin_cmd;
21599ca880aSopenharmony_ci                        };
21699ca880aSopenharmony_ci                } key;
21799ca880aSopenharmony_ci        };
21899ca880aSopenharmony_ci};
21999ca880aSopenharmony_ci
22099ca880aSopenharmony_ci#define MAX_TK                64
22199ca880aSopenharmony_cistruct rule_tmp {
22299ca880aSopenharmony_ci        struct udev_rules *rules;
22399ca880aSopenharmony_ci        struct token rule;
22499ca880aSopenharmony_ci        struct token token[MAX_TK];
22599ca880aSopenharmony_ci        unsigned int token_cur;
22699ca880aSopenharmony_ci};
22799ca880aSopenharmony_ci
22899ca880aSopenharmony_ci#ifdef DEBUG
22999ca880aSopenharmony_cistatic const char *operation_str(enum operation_type type) {
23099ca880aSopenharmony_ci        static const char *operation_strs[] = {
23199ca880aSopenharmony_ci                [OP_UNSET] =            "UNSET",
23299ca880aSopenharmony_ci                [OP_MATCH] =            "match",
23399ca880aSopenharmony_ci                [OP_NOMATCH] =          "nomatch",
23499ca880aSopenharmony_ci                [OP_MATCH_MAX] =        "MATCH_MAX",
23599ca880aSopenharmony_ci
23699ca880aSopenharmony_ci                [OP_ADD] =              "add",
23799ca880aSopenharmony_ci                [OP_REMOVE] =           "remove",
23899ca880aSopenharmony_ci                [OP_ASSIGN] =           "assign",
23999ca880aSopenharmony_ci                [OP_ASSIGN_FINAL] =     "assign-final",
24099ca880aSopenharmony_ci}        ;
24199ca880aSopenharmony_ci
24299ca880aSopenharmony_ci        return operation_strs[type];
24399ca880aSopenharmony_ci}
24499ca880aSopenharmony_ci
24599ca880aSopenharmony_cistatic const char *string_glob_str(enum string_glob_type type) {
24699ca880aSopenharmony_ci        static const char *string_glob_strs[] = {
24799ca880aSopenharmony_ci                [GL_UNSET] =            "UNSET",
24899ca880aSopenharmony_ci                [GL_PLAIN] =            "plain",
24999ca880aSopenharmony_ci                [GL_GLOB] =             "glob",
25099ca880aSopenharmony_ci                [GL_SPLIT] =            "split",
25199ca880aSopenharmony_ci                [GL_SPLIT_GLOB] =       "split-glob",
25299ca880aSopenharmony_ci                [GL_SOMETHING] =        "split-glob",
25399ca880aSopenharmony_ci        };
25499ca880aSopenharmony_ci
25599ca880aSopenharmony_ci        return string_glob_strs[type];
25699ca880aSopenharmony_ci}
25799ca880aSopenharmony_ci
25899ca880aSopenharmony_cistatic const char *token_str(enum token_type type) {
25999ca880aSopenharmony_ci        static const char *token_strs[] = {
26099ca880aSopenharmony_ci                [TK_UNSET] =                    "UNSET",
26199ca880aSopenharmony_ci                [TK_RULE] =                     "RULE",
26299ca880aSopenharmony_ci
26399ca880aSopenharmony_ci                [TK_M_ACTION] =                 "M ACTION",
26499ca880aSopenharmony_ci                [TK_M_DEVPATH] =                "M DEVPATH",
26599ca880aSopenharmony_ci                [TK_M_KERNEL] =                 "M KERNEL",
26699ca880aSopenharmony_ci                [TK_M_DEVLINK] =                "M DEVLINK",
26799ca880aSopenharmony_ci                [TK_M_NAME] =                   "M NAME",
26899ca880aSopenharmony_ci                [TK_M_ENV] =                    "M ENV",
26999ca880aSopenharmony_ci                [TK_M_TAG] =                    "M TAG",
27099ca880aSopenharmony_ci                [TK_M_SUBSYSTEM] =              "M SUBSYSTEM",
27199ca880aSopenharmony_ci                [TK_M_DRIVER] =                 "M DRIVER",
27299ca880aSopenharmony_ci                [TK_M_WAITFOR] =                "M WAITFOR",
27399ca880aSopenharmony_ci                [TK_M_ATTR] =                   "M ATTR",
27499ca880aSopenharmony_ci                [TK_M_SYSCTL] =                 "M SYSCTL",
27599ca880aSopenharmony_ci
27699ca880aSopenharmony_ci                [TK_M_PARENTS_MIN] =            "M PARENTS_MIN",
27799ca880aSopenharmony_ci                [TK_M_KERNELS] =                "M KERNELS",
27899ca880aSopenharmony_ci                [TK_M_SUBSYSTEMS] =             "M SUBSYSTEMS",
27999ca880aSopenharmony_ci                [TK_M_DRIVERS] =                "M DRIVERS",
28099ca880aSopenharmony_ci                [TK_M_ATTRS] =                  "M ATTRS",
28199ca880aSopenharmony_ci                [TK_M_TAGS] =                   "M TAGS",
28299ca880aSopenharmony_ci                [TK_M_PARENTS_MAX] =            "M PARENTS_MAX",
28399ca880aSopenharmony_ci
28499ca880aSopenharmony_ci                [TK_M_TEST] =                   "M TEST",
28599ca880aSopenharmony_ci                [TK_M_PROGRAM] =                "M PROGRAM",
28699ca880aSopenharmony_ci                [TK_M_IMPORT_FILE] =            "M IMPORT_FILE",
28799ca880aSopenharmony_ci                [TK_M_IMPORT_PROG] =            "M IMPORT_PROG",
28899ca880aSopenharmony_ci                [TK_M_IMPORT_BUILTIN] =         "M IMPORT_BUILTIN",
28999ca880aSopenharmony_ci                [TK_M_IMPORT_DB] =              "M IMPORT_DB",
29099ca880aSopenharmony_ci                [TK_M_IMPORT_CMDLINE] =         "M IMPORT_CMDLINE",
29199ca880aSopenharmony_ci                [TK_M_IMPORT_PARENT] =          "M IMPORT_PARENT",
29299ca880aSopenharmony_ci                [TK_M_RESULT] =                 "M RESULT",
29399ca880aSopenharmony_ci                [TK_M_MAX] =                    "M MAX",
29499ca880aSopenharmony_ci
29599ca880aSopenharmony_ci                [TK_A_STRING_ESCAPE_NONE] =     "A STRING_ESCAPE_NONE",
29699ca880aSopenharmony_ci                [TK_A_STRING_ESCAPE_REPLACE] =  "A STRING_ESCAPE_REPLACE",
29799ca880aSopenharmony_ci                [TK_A_DB_PERSIST] =             "A DB_PERSIST",
29899ca880aSopenharmony_ci                [TK_A_INOTIFY_WATCH] =          "A INOTIFY_WATCH",
29999ca880aSopenharmony_ci                [TK_A_DEVLINK_PRIO] =           "A DEVLINK_PRIO",
30099ca880aSopenharmony_ci                [TK_A_OWNER] =                  "A OWNER",
30199ca880aSopenharmony_ci                [TK_A_GROUP] =                  "A GROUP",
30299ca880aSopenharmony_ci                [TK_A_MODE] =                   "A MODE",
30399ca880aSopenharmony_ci                [TK_A_OWNER_ID] =               "A OWNER_ID",
30499ca880aSopenharmony_ci                [TK_A_GROUP_ID] =               "A GROUP_ID",
30599ca880aSopenharmony_ci                [TK_A_STATIC_NODE] =            "A STATIC_NODE",
30699ca880aSopenharmony_ci                [TK_A_SECLABEL] =               "A SECLABEL",
30799ca880aSopenharmony_ci                [TK_A_MODE_ID] =                "A MODE_ID",
30899ca880aSopenharmony_ci                [TK_A_ENV] =                    "A ENV",
30999ca880aSopenharmony_ci                [TK_A_TAG] =                    "A ENV",
31099ca880aSopenharmony_ci                [TK_A_NAME] =                   "A NAME",
31199ca880aSopenharmony_ci                [TK_A_DEVLINK] =                "A DEVLINK",
31299ca880aSopenharmony_ci                [TK_A_ATTR] =                   "A ATTR",
31399ca880aSopenharmony_ci                [TK_A_SYSCTL] =                 "A SYSCTL",
31499ca880aSopenharmony_ci                [TK_A_RUN_BUILTIN] =            "A RUN_BUILTIN",
31599ca880aSopenharmony_ci                [TK_A_RUN_PROGRAM] =            "A RUN_PROGRAM",
31699ca880aSopenharmony_ci                [TK_A_GOTO] =                   "A GOTO",
31799ca880aSopenharmony_ci
31899ca880aSopenharmony_ci                [TK_END] =                      "END",
31999ca880aSopenharmony_ci        };
32099ca880aSopenharmony_ci
32199ca880aSopenharmony_ci        return token_strs[type];
32299ca880aSopenharmony_ci}
32399ca880aSopenharmony_ci
32499ca880aSopenharmony_cistatic void dump_token(struct udev_rules *rules, struct token *token) {
32599ca880aSopenharmony_ci        enum token_type type = token->type;
32699ca880aSopenharmony_ci        enum operation_type op = token->key.op;
32799ca880aSopenharmony_ci        enum string_glob_type glob = token->key.glob;
32899ca880aSopenharmony_ci        const char *value = str(rules, token->key.value_off);
32999ca880aSopenharmony_ci        const char *attr = &rules->buf[token->key.attr_off];
33099ca880aSopenharmony_ci
33199ca880aSopenharmony_ci        switch (type) {
33299ca880aSopenharmony_ci        case TK_RULE:
33399ca880aSopenharmony_ci                {
33499ca880aSopenharmony_ci                        const char *tks_ptr = (char *)rules->tokens;
33599ca880aSopenharmony_ci                        const char *tk_ptr = (char *)token;
33699ca880aSopenharmony_ci                        unsigned int idx = (tk_ptr - tks_ptr) / sizeof(struct token);
33799ca880aSopenharmony_ci
33899ca880aSopenharmony_ci                        log_debug("* RULE %s:%u, token: %u, count: %u, label: '%s'",
33999ca880aSopenharmony_ci                                  &rules->buf[token->rule.filename_off], token->rule.filename_line,
34099ca880aSopenharmony_ci                                  idx, token->rule.token_count,
34199ca880aSopenharmony_ci                                  &rules->buf[token->rule.label_off]);
34299ca880aSopenharmony_ci                        break;
34399ca880aSopenharmony_ci                }
34499ca880aSopenharmony_ci        case TK_M_ACTION:
34599ca880aSopenharmony_ci        case TK_M_DEVPATH:
34699ca880aSopenharmony_ci        case TK_M_KERNEL:
34799ca880aSopenharmony_ci        case TK_M_SUBSYSTEM:
34899ca880aSopenharmony_ci        case TK_M_DRIVER:
34999ca880aSopenharmony_ci        case TK_M_WAITFOR:
35099ca880aSopenharmony_ci        case TK_M_DEVLINK:
35199ca880aSopenharmony_ci        case TK_M_NAME:
35299ca880aSopenharmony_ci        case TK_M_KERNELS:
35399ca880aSopenharmony_ci        case TK_M_SUBSYSTEMS:
35499ca880aSopenharmony_ci        case TK_M_DRIVERS:
35599ca880aSopenharmony_ci        case TK_M_TAGS:
35699ca880aSopenharmony_ci        case TK_M_PROGRAM:
35799ca880aSopenharmony_ci        case TK_M_IMPORT_FILE:
35899ca880aSopenharmony_ci        case TK_M_IMPORT_PROG:
35999ca880aSopenharmony_ci        case TK_M_IMPORT_DB:
36099ca880aSopenharmony_ci        case TK_M_IMPORT_CMDLINE:
36199ca880aSopenharmony_ci        case TK_M_IMPORT_PARENT:
36299ca880aSopenharmony_ci        case TK_M_RESULT:
36399ca880aSopenharmony_ci        case TK_A_NAME:
36499ca880aSopenharmony_ci        case TK_A_DEVLINK:
36599ca880aSopenharmony_ci        case TK_A_OWNER:
36699ca880aSopenharmony_ci        case TK_A_GROUP:
36799ca880aSopenharmony_ci        case TK_A_MODE:
36899ca880aSopenharmony_ci        case TK_A_RUN_BUILTIN:
36999ca880aSopenharmony_ci        case TK_A_RUN_PROGRAM:
37099ca880aSopenharmony_ci                log_debug("%s %s '%s'(%s)",
37199ca880aSopenharmony_ci                          token_str(type), operation_str(op), value, string_glob_str(glob));
37299ca880aSopenharmony_ci                break;
37399ca880aSopenharmony_ci        case TK_M_IMPORT_BUILTIN:
37499ca880aSopenharmony_ci                log_debug("%s %i '%s'", token_str(type), token->key.builtin_cmd, value);
37599ca880aSopenharmony_ci                break;
37699ca880aSopenharmony_ci        case TK_M_ATTR:
37799ca880aSopenharmony_ci        case TK_M_SYSCTL:
37899ca880aSopenharmony_ci        case TK_M_ATTRS:
37999ca880aSopenharmony_ci        case TK_M_ENV:
38099ca880aSopenharmony_ci        case TK_A_ATTR:
38199ca880aSopenharmony_ci        case TK_A_SYSCTL:
38299ca880aSopenharmony_ci        case TK_A_ENV:
38399ca880aSopenharmony_ci                log_debug("%s %s '%s' '%s'(%s)",
38499ca880aSopenharmony_ci                          token_str(type), operation_str(op), attr, value, string_glob_str(glob));
38599ca880aSopenharmony_ci                break;
38699ca880aSopenharmony_ci        case TK_M_TAG:
38799ca880aSopenharmony_ci        case TK_A_TAG:
38899ca880aSopenharmony_ci                log_debug("%s %s '%s'", token_str(type), operation_str(op), value);
38999ca880aSopenharmony_ci                break;
39099ca880aSopenharmony_ci        case TK_A_STRING_ESCAPE_NONE:
39199ca880aSopenharmony_ci        case TK_A_STRING_ESCAPE_REPLACE:
39299ca880aSopenharmony_ci        case TK_A_DB_PERSIST:
39399ca880aSopenharmony_ci                log_debug("%s", token_str(type));
39499ca880aSopenharmony_ci                break;
39599ca880aSopenharmony_ci        case TK_M_TEST:
39699ca880aSopenharmony_ci                log_debug("%s %s '%s'(%s) %#o",
39799ca880aSopenharmony_ci                          token_str(type), operation_str(op), value, string_glob_str(glob), token->key.mode);
39899ca880aSopenharmony_ci                break;
39999ca880aSopenharmony_ci        case TK_A_INOTIFY_WATCH:
40099ca880aSopenharmony_ci                log_debug("%s %u", token_str(type), token->key.watch);
40199ca880aSopenharmony_ci                break;
40299ca880aSopenharmony_ci        case TK_A_DEVLINK_PRIO:
40399ca880aSopenharmony_ci                log_debug("%s %u", token_str(type), token->key.devlink_prio);
40499ca880aSopenharmony_ci                break;
40599ca880aSopenharmony_ci        case TK_A_OWNER_ID:
40699ca880aSopenharmony_ci                log_debug("%s %s %u", token_str(type), operation_str(op), token->key.uid);
40799ca880aSopenharmony_ci                break;
40899ca880aSopenharmony_ci        case TK_A_GROUP_ID:
40999ca880aSopenharmony_ci                log_debug("%s %s %u", token_str(type), operation_str(op), token->key.gid);
41099ca880aSopenharmony_ci                break;
41199ca880aSopenharmony_ci        case TK_A_MODE_ID:
41299ca880aSopenharmony_ci                log_debug("%s %s %#o", token_str(type), operation_str(op), token->key.mode);
41399ca880aSopenharmony_ci                break;
41499ca880aSopenharmony_ci        case TK_A_STATIC_NODE:
41599ca880aSopenharmony_ci                log_debug("%s '%s'", token_str(type), value);
41699ca880aSopenharmony_ci                break;
41799ca880aSopenharmony_ci        case TK_A_SECLABEL:
41899ca880aSopenharmony_ci                log_debug("%s %s '%s' '%s'", token_str(type), operation_str(op), attr, value);
41999ca880aSopenharmony_ci                break;
42099ca880aSopenharmony_ci        case TK_A_GOTO:
42199ca880aSopenharmony_ci                log_debug("%s '%s' %u", token_str(type), value, token->key.rule_goto);
42299ca880aSopenharmony_ci                break;
42399ca880aSopenharmony_ci        case TK_END:
42499ca880aSopenharmony_ci                log_debug("* %s", token_str(type));
42599ca880aSopenharmony_ci                break;
42699ca880aSopenharmony_ci        case TK_M_PARENTS_MIN:
42799ca880aSopenharmony_ci        case TK_M_PARENTS_MAX:
42899ca880aSopenharmony_ci        case TK_M_MAX:
42999ca880aSopenharmony_ci        case TK_UNSET:
43099ca880aSopenharmony_ci                log_debug("unknown type %u", type);
43199ca880aSopenharmony_ci                break;
43299ca880aSopenharmony_ci        }
43399ca880aSopenharmony_ci}
43499ca880aSopenharmony_ci
43599ca880aSopenharmony_cistatic void dump_rules(struct udev_rules *rules) {
43699ca880aSopenharmony_ci        unsigned int i;
43799ca880aSopenharmony_ci
43899ca880aSopenharmony_ci        log_debug("dumping %u (%zu bytes) tokens, %u (%zu bytes) strings",
43999ca880aSopenharmony_ci                  rules->token_cur,
44099ca880aSopenharmony_ci                  rules->token_cur * sizeof(struct token),
44199ca880aSopenharmony_ci                  rules->buf_count,
44299ca880aSopenharmony_ci                  rules->buf_cur);
44399ca880aSopenharmony_ci        for (i = 0; i < rules->token_cur; i++)
44499ca880aSopenharmony_ci                dump_token(rules, &rules->tokens[i]);
44599ca880aSopenharmony_ci}
44699ca880aSopenharmony_ci#else
44799ca880aSopenharmony_cistatic inline void dump_token(struct udev_rules *rules, struct token *token) {}
44899ca880aSopenharmony_cistatic inline void dump_rules(struct udev_rules *rules) {}
44999ca880aSopenharmony_ci#endif /* DEBUG */
45099ca880aSopenharmony_ci
45199ca880aSopenharmony_cistatic int add_token(struct udev_rules *rules, struct token *token) {
45299ca880aSopenharmony_ci        /* grow buffer if needed */
45399ca880aSopenharmony_ci        if (rules->token_cur+1 >= rules->token_max) {
45499ca880aSopenharmony_ci                struct token *tokens;
45599ca880aSopenharmony_ci                unsigned int add;
45699ca880aSopenharmony_ci
45799ca880aSopenharmony_ci                /* double the buffer size */
45899ca880aSopenharmony_ci                add = rules->token_max;
45999ca880aSopenharmony_ci                if (add < 8)
46099ca880aSopenharmony_ci                        add = 8;
46199ca880aSopenharmony_ci
46299ca880aSopenharmony_ci                tokens = realloc(rules->tokens, (rules->token_max + add ) * sizeof(struct token));
46399ca880aSopenharmony_ci                if (tokens == NULL)
46499ca880aSopenharmony_ci                        return -1;
46599ca880aSopenharmony_ci                rules->tokens = tokens;
46699ca880aSopenharmony_ci                rules->token_max += add;
46799ca880aSopenharmony_ci        }
46899ca880aSopenharmony_ci        memcpy(&rules->tokens[rules->token_cur], token, sizeof(struct token));
46999ca880aSopenharmony_ci        rules->token_cur++;
47099ca880aSopenharmony_ci        return 0;
47199ca880aSopenharmony_ci}
47299ca880aSopenharmony_ci
47399ca880aSopenharmony_cistatic uid_t add_uid(struct udev_rules *rules, const char *owner) {
47499ca880aSopenharmony_ci        unsigned int i;
47599ca880aSopenharmony_ci        uid_t uid = 0;
47699ca880aSopenharmony_ci        unsigned int off;
47799ca880aSopenharmony_ci        int r;
47899ca880aSopenharmony_ci
47999ca880aSopenharmony_ci        /* lookup, if we know it already */
48099ca880aSopenharmony_ci        for (i = 0; i < rules->uids_cur; i++) {
48199ca880aSopenharmony_ci                off = rules->uids[i].name_off;
48299ca880aSopenharmony_ci                if (streq(rules_str(rules, off), owner)) {
48399ca880aSopenharmony_ci                        uid = rules->uids[i].uid;
48499ca880aSopenharmony_ci                        return uid;
48599ca880aSopenharmony_ci                }
48699ca880aSopenharmony_ci        }
48799ca880aSopenharmony_ci        r = get_user_creds(&owner, &uid, NULL, NULL, NULL);
48899ca880aSopenharmony_ci        if (r < 0) {
48999ca880aSopenharmony_ci                if (r == -ENOENT || r == -ESRCH)
49099ca880aSopenharmony_ci                        log_error("specified user '%s' unknown", owner);
49199ca880aSopenharmony_ci                else
49299ca880aSopenharmony_ci                        log_error_errno(r, "error resolving user '%s': %m", owner);
49399ca880aSopenharmony_ci        }
49499ca880aSopenharmony_ci
49599ca880aSopenharmony_ci        /* grow buffer if needed */
49699ca880aSopenharmony_ci        if (rules->uids_cur+1 >= rules->uids_max) {
49799ca880aSopenharmony_ci                struct uid_gid *uids;
49899ca880aSopenharmony_ci                unsigned int add;
49999ca880aSopenharmony_ci
50099ca880aSopenharmony_ci                /* double the buffer size */
50199ca880aSopenharmony_ci                add = rules->uids_max;
50299ca880aSopenharmony_ci                if (add < 1)
50399ca880aSopenharmony_ci                        add = 8;
50499ca880aSopenharmony_ci
50599ca880aSopenharmony_ci                uids = realloc(rules->uids, (rules->uids_max + add ) * sizeof(struct uid_gid));
50699ca880aSopenharmony_ci                if (uids == NULL)
50799ca880aSopenharmony_ci                        return uid;
50899ca880aSopenharmony_ci                rules->uids = uids;
50999ca880aSopenharmony_ci                rules->uids_max += add;
51099ca880aSopenharmony_ci        }
51199ca880aSopenharmony_ci        rules->uids[rules->uids_cur].uid = uid;
51299ca880aSopenharmony_ci        off = rules_add_string(rules, owner);
51399ca880aSopenharmony_ci        if (off <= 0)
51499ca880aSopenharmony_ci                return uid;
51599ca880aSopenharmony_ci        rules->uids[rules->uids_cur].name_off = off;
51699ca880aSopenharmony_ci        rules->uids_cur++;
51799ca880aSopenharmony_ci        return uid;
51899ca880aSopenharmony_ci}
51999ca880aSopenharmony_ci
52099ca880aSopenharmony_cistatic gid_t add_gid(struct udev_rules *rules, const char *group) {
52199ca880aSopenharmony_ci        unsigned int i;
52299ca880aSopenharmony_ci        gid_t gid = 0;
52399ca880aSopenharmony_ci        unsigned int off;
52499ca880aSopenharmony_ci        int r;
52599ca880aSopenharmony_ci
52699ca880aSopenharmony_ci        /* lookup, if we know it already */
52799ca880aSopenharmony_ci        for (i = 0; i < rules->gids_cur; i++) {
52899ca880aSopenharmony_ci                off = rules->gids[i].name_off;
52999ca880aSopenharmony_ci                if (streq(rules_str(rules, off), group)) {
53099ca880aSopenharmony_ci                        gid = rules->gids[i].gid;
53199ca880aSopenharmony_ci                        return gid;
53299ca880aSopenharmony_ci                }
53399ca880aSopenharmony_ci        }
53499ca880aSopenharmony_ci        r = get_group_creds(&group, &gid);
53599ca880aSopenharmony_ci        if (r < 0) {
53699ca880aSopenharmony_ci                if (r == -ENOENT || r == -ESRCH)
53799ca880aSopenharmony_ci                        log_error("specified group '%s' unknown", group);
53899ca880aSopenharmony_ci                else
53999ca880aSopenharmony_ci                        log_error_errno(r, "error resolving group '%s': %m", group);
54099ca880aSopenharmony_ci        }
54199ca880aSopenharmony_ci
54299ca880aSopenharmony_ci        /* grow buffer if needed */
54399ca880aSopenharmony_ci        if (rules->gids_cur+1 >= rules->gids_max) {
54499ca880aSopenharmony_ci                struct uid_gid *gids;
54599ca880aSopenharmony_ci                unsigned int add;
54699ca880aSopenharmony_ci
54799ca880aSopenharmony_ci                /* double the buffer size */
54899ca880aSopenharmony_ci                add = rules->gids_max;
54999ca880aSopenharmony_ci                if (add < 1)
55099ca880aSopenharmony_ci                        add = 8;
55199ca880aSopenharmony_ci
55299ca880aSopenharmony_ci                gids = realloc(rules->gids, (rules->gids_max + add ) * sizeof(struct uid_gid));
55399ca880aSopenharmony_ci                if (gids == NULL)
55499ca880aSopenharmony_ci                        return gid;
55599ca880aSopenharmony_ci                rules->gids = gids;
55699ca880aSopenharmony_ci                rules->gids_max += add;
55799ca880aSopenharmony_ci        }
55899ca880aSopenharmony_ci        rules->gids[rules->gids_cur].gid = gid;
55999ca880aSopenharmony_ci        off = rules_add_string(rules, group);
56099ca880aSopenharmony_ci        if (off <= 0)
56199ca880aSopenharmony_ci                return gid;
56299ca880aSopenharmony_ci        rules->gids[rules->gids_cur].name_off = off;
56399ca880aSopenharmony_ci        rules->gids_cur++;
56499ca880aSopenharmony_ci        return gid;
56599ca880aSopenharmony_ci}
56699ca880aSopenharmony_ci
56799ca880aSopenharmony_cistatic int import_property_from_string(struct udev_device *dev, char *line) {
56899ca880aSopenharmony_ci        char *key;
56999ca880aSopenharmony_ci        char *val;
57099ca880aSopenharmony_ci        size_t len;
57199ca880aSopenharmony_ci
57299ca880aSopenharmony_ci        /* find key */
57399ca880aSopenharmony_ci        key = line;
57499ca880aSopenharmony_ci        while (isspace(key[0]))
57599ca880aSopenharmony_ci                key++;
57699ca880aSopenharmony_ci
57799ca880aSopenharmony_ci        /* comment or empty line */
57899ca880aSopenharmony_ci        if (key[0] == '#' || key[0] == '\0')
57999ca880aSopenharmony_ci                return -1;
58099ca880aSopenharmony_ci
58199ca880aSopenharmony_ci        /* split key/value */
58299ca880aSopenharmony_ci        val = strchr(key, '=');
58399ca880aSopenharmony_ci        if (val == NULL)
58499ca880aSopenharmony_ci                return -1;
58599ca880aSopenharmony_ci        val[0] = '\0';
58699ca880aSopenharmony_ci        val++;
58799ca880aSopenharmony_ci
58899ca880aSopenharmony_ci        /* find value */
58999ca880aSopenharmony_ci        while (isspace(val[0]))
59099ca880aSopenharmony_ci                val++;
59199ca880aSopenharmony_ci
59299ca880aSopenharmony_ci        /* terminate key */
59399ca880aSopenharmony_ci        len = strlen(key);
59499ca880aSopenharmony_ci        if (len == 0)
59599ca880aSopenharmony_ci                return -1;
59699ca880aSopenharmony_ci        while (isspace(key[len-1]))
59799ca880aSopenharmony_ci                len--;
59899ca880aSopenharmony_ci        key[len] = '\0';
59999ca880aSopenharmony_ci
60099ca880aSopenharmony_ci        /* terminate value */
60199ca880aSopenharmony_ci        len = strlen(val);
60299ca880aSopenharmony_ci        if (len == 0)
60399ca880aSopenharmony_ci                return -1;
60499ca880aSopenharmony_ci        while (isspace(val[len-1]))
60599ca880aSopenharmony_ci                len--;
60699ca880aSopenharmony_ci        val[len] = '\0';
60799ca880aSopenharmony_ci
60899ca880aSopenharmony_ci        if (len == 0)
60999ca880aSopenharmony_ci                return -1;
61099ca880aSopenharmony_ci
61199ca880aSopenharmony_ci        /* unquote */
61299ca880aSopenharmony_ci        if (val[0] == '"' || val[0] == '\'') {
61399ca880aSopenharmony_ci                if (val[len-1] != val[0]) {
61499ca880aSopenharmony_ci                        log_debug("inconsistent quoting: '%s', skip", line);
61599ca880aSopenharmony_ci                        return -1;
61699ca880aSopenharmony_ci                }
61799ca880aSopenharmony_ci                val[len-1] = '\0';
61899ca880aSopenharmony_ci                val++;
61999ca880aSopenharmony_ci        }
62099ca880aSopenharmony_ci
62199ca880aSopenharmony_ci        udev_device_add_property(dev, key, val);
62299ca880aSopenharmony_ci
62399ca880aSopenharmony_ci        return 0;
62499ca880aSopenharmony_ci}
62599ca880aSopenharmony_ci
62699ca880aSopenharmony_cistatic int import_file_into_properties(struct udev_device *dev, const char *filename) {
62799ca880aSopenharmony_ci        FILE *f;
62899ca880aSopenharmony_ci        char line[UTIL_LINE_SIZE];
62999ca880aSopenharmony_ci
63099ca880aSopenharmony_ci        f = fopen(filename, "re");
63199ca880aSopenharmony_ci        if (f == NULL)
63299ca880aSopenharmony_ci                return -1;
63399ca880aSopenharmony_ci        while (fgets(line, sizeof(line), f) != NULL)
63499ca880aSopenharmony_ci                import_property_from_string(dev, line);
63599ca880aSopenharmony_ci        fclose(f);
63699ca880aSopenharmony_ci        return 0;
63799ca880aSopenharmony_ci}
63899ca880aSopenharmony_ci
63999ca880aSopenharmony_cistatic int import_program_into_properties(struct udev_event *event,
64099ca880aSopenharmony_ci                                          usec_t timeout_usec,
64199ca880aSopenharmony_ci                                          usec_t timeout_warn_usec,
64299ca880aSopenharmony_ci                                          const char *program, const sigset_t *sigmask) {
64399ca880aSopenharmony_ci        struct udev_device *dev = event->dev;
64499ca880aSopenharmony_ci        char **envp;
64599ca880aSopenharmony_ci        char result[UTIL_LINE_SIZE];
64699ca880aSopenharmony_ci        char *line;
64799ca880aSopenharmony_ci        int err;
64899ca880aSopenharmony_ci
64999ca880aSopenharmony_ci        envp = udev_device_get_properties_envp(dev);
65099ca880aSopenharmony_ci        err = udev_event_spawn(event, timeout_usec, timeout_warn_usec, program, envp, sigmask, result, sizeof(result));
65199ca880aSopenharmony_ci        if (err < 0)
65299ca880aSopenharmony_ci                return err;
65399ca880aSopenharmony_ci
65499ca880aSopenharmony_ci        line = result;
65599ca880aSopenharmony_ci        while (line != NULL) {
65699ca880aSopenharmony_ci                char *pos;
65799ca880aSopenharmony_ci
65899ca880aSopenharmony_ci                pos = strchr(line, '\n');
65999ca880aSopenharmony_ci                if (pos != NULL) {
66099ca880aSopenharmony_ci                        pos[0] = '\0';
66199ca880aSopenharmony_ci                        pos = &pos[1];
66299ca880aSopenharmony_ci                }
66399ca880aSopenharmony_ci                import_property_from_string(dev, line);
66499ca880aSopenharmony_ci                line = pos;
66599ca880aSopenharmony_ci        }
66699ca880aSopenharmony_ci        return 0;
66799ca880aSopenharmony_ci}
66899ca880aSopenharmony_ci
66999ca880aSopenharmony_cistatic int import_parent_into_properties(struct udev_device *dev, const char *filter) {
67099ca880aSopenharmony_ci        struct udev_device *dev_parent;
67199ca880aSopenharmony_ci        struct udev_list_entry *list_entry;
67299ca880aSopenharmony_ci
67399ca880aSopenharmony_ci        dev_parent = udev_device_get_parent(dev);
67499ca880aSopenharmony_ci        if (dev_parent == NULL)
67599ca880aSopenharmony_ci                return -1;
67699ca880aSopenharmony_ci
67799ca880aSopenharmony_ci        udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(dev_parent)) {
67899ca880aSopenharmony_ci                const char *key = udev_list_entry_get_name(list_entry);
67999ca880aSopenharmony_ci                const char *val = udev_list_entry_get_value(list_entry);
68099ca880aSopenharmony_ci
68199ca880aSopenharmony_ci                if (fnmatch(filter, key, 0) == 0) {
68299ca880aSopenharmony_ci                        udev_device_add_property(dev, key, val);
68399ca880aSopenharmony_ci                }
68499ca880aSopenharmony_ci        }
68599ca880aSopenharmony_ci        return 0;
68699ca880aSopenharmony_ci}
68799ca880aSopenharmony_ci
68899ca880aSopenharmony_ci#define WAIT_LOOP_PER_SECOND                50
68999ca880aSopenharmony_cistatic int wait_for_file(struct udev_device *dev, const char *file, int timeout) {
69099ca880aSopenharmony_ci        char filepath[UTIL_PATH_SIZE];
69199ca880aSopenharmony_ci        char devicepath[UTIL_PATH_SIZE];
69299ca880aSopenharmony_ci        struct stat stats;
69399ca880aSopenharmony_ci        int loop = timeout * WAIT_LOOP_PER_SECOND;
69499ca880aSopenharmony_ci
69599ca880aSopenharmony_ci        /* a relative path is a device attribute */
69699ca880aSopenharmony_ci        devicepath[0] = '\0';
69799ca880aSopenharmony_ci        if (file[0] != '/') {
69899ca880aSopenharmony_ci                strscpyl(devicepath, sizeof(devicepath), udev_device_get_syspath(dev), NULL);
69999ca880aSopenharmony_ci                strscpyl(filepath, sizeof(filepath), devicepath, "/", file, NULL);
70099ca880aSopenharmony_ci                file = filepath;
70199ca880aSopenharmony_ci        }
70299ca880aSopenharmony_ci
70399ca880aSopenharmony_ci        while (--loop) {
70499ca880aSopenharmony_ci                const struct timespec duration = { 0, 1000 * 1000 * 1000 / WAIT_LOOP_PER_SECOND };
70599ca880aSopenharmony_ci
70699ca880aSopenharmony_ci                /* lookup file */
70799ca880aSopenharmony_ci                if (stat(file, &stats) == 0) {
70899ca880aSopenharmony_ci                        log_debug("file '%s' appeared after %i loops", file, (timeout * WAIT_LOOP_PER_SECOND) - loop-1);
70999ca880aSopenharmony_ci                        return 0;
71099ca880aSopenharmony_ci                }
71199ca880aSopenharmony_ci                /* make sure, the device did not disappear in the meantime */
71299ca880aSopenharmony_ci                if (devicepath[0] != '\0' && stat(devicepath, &stats) != 0) {
71399ca880aSopenharmony_ci                        log_debug("device disappeared while waiting for '%s'", file);
71499ca880aSopenharmony_ci                        return -2;
71599ca880aSopenharmony_ci                }
71699ca880aSopenharmony_ci                log_debug("wait for '%s' for %i mseconds", file, 1000 / WAIT_LOOP_PER_SECOND);
71799ca880aSopenharmony_ci                nanosleep(&duration, NULL);
71899ca880aSopenharmony_ci        }
71999ca880aSopenharmony_ci        log_debug("waiting for '%s' failed", file);
72099ca880aSopenharmony_ci        return -1;
72199ca880aSopenharmony_ci}
72299ca880aSopenharmony_ci
72399ca880aSopenharmony_cistatic int attr_subst_subdir(char *attr, size_t len) {
72499ca880aSopenharmony_ci        bool found = false;
72599ca880aSopenharmony_ci
72699ca880aSopenharmony_ci        if (strstr(attr, "/*/")) {
72799ca880aSopenharmony_ci                char *pos;
72899ca880aSopenharmony_ci                char dirname[UTIL_PATH_SIZE];
72999ca880aSopenharmony_ci                const char *tail;
73099ca880aSopenharmony_ci                DIR *dir;
73199ca880aSopenharmony_ci
73299ca880aSopenharmony_ci                strscpy(dirname, sizeof(dirname), attr);
73399ca880aSopenharmony_ci                pos = strstr(dirname, "/*/");
73499ca880aSopenharmony_ci                if (pos == NULL)
73599ca880aSopenharmony_ci                        return -1;
73699ca880aSopenharmony_ci                pos[0] = '\0';
73799ca880aSopenharmony_ci                tail = &pos[2];
73899ca880aSopenharmony_ci                dir = opendir(dirname);
73999ca880aSopenharmony_ci                if (dir != NULL) {
74099ca880aSopenharmony_ci                        struct dirent *dent;
74199ca880aSopenharmony_ci
74299ca880aSopenharmony_ci                        for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
74399ca880aSopenharmony_ci                                struct stat stats;
74499ca880aSopenharmony_ci
74599ca880aSopenharmony_ci                                if (dent->d_name[0] == '.')
74699ca880aSopenharmony_ci                                        continue;
74799ca880aSopenharmony_ci                                strscpyl(attr, len, dirname, "/", dent->d_name, tail, NULL);
74899ca880aSopenharmony_ci                                if (stat(attr, &stats) == 0) {
74999ca880aSopenharmony_ci                                        found = true;
75099ca880aSopenharmony_ci                                        break;
75199ca880aSopenharmony_ci                                }
75299ca880aSopenharmony_ci                        }
75399ca880aSopenharmony_ci                        closedir(dir);
75499ca880aSopenharmony_ci                }
75599ca880aSopenharmony_ci        }
75699ca880aSopenharmony_ci
75799ca880aSopenharmony_ci        return found;
75899ca880aSopenharmony_ci}
75999ca880aSopenharmony_ci
76099ca880aSopenharmony_cistatic int get_key(struct udev *udev, char **line, char **key, enum operation_type *op, char **value) {
76199ca880aSopenharmony_ci        char *linepos;
76299ca880aSopenharmony_ci        char *temp;
76399ca880aSopenharmony_ci
76499ca880aSopenharmony_ci        linepos = *line;
76599ca880aSopenharmony_ci        if (linepos == NULL || linepos[0] == '\0')
76699ca880aSopenharmony_ci                return -1;
76799ca880aSopenharmony_ci
76899ca880aSopenharmony_ci        /* skip whitespace */
76999ca880aSopenharmony_ci        while (isspace(linepos[0]) || linepos[0] == ',')
77099ca880aSopenharmony_ci                linepos++;
77199ca880aSopenharmony_ci
77299ca880aSopenharmony_ci        /* get the key */
77399ca880aSopenharmony_ci        if (linepos[0] == '\0')
77499ca880aSopenharmony_ci                return -1;
77599ca880aSopenharmony_ci        *key = linepos;
77699ca880aSopenharmony_ci
77799ca880aSopenharmony_ci        for (;;) {
77899ca880aSopenharmony_ci                linepos++;
77999ca880aSopenharmony_ci                if (linepos[0] == '\0')
78099ca880aSopenharmony_ci                        return -1;
78199ca880aSopenharmony_ci                if (isspace(linepos[0]))
78299ca880aSopenharmony_ci                        break;
78399ca880aSopenharmony_ci                if (linepos[0] == '=')
78499ca880aSopenharmony_ci                        break;
78599ca880aSopenharmony_ci                if ((linepos[0] == '+') || (linepos[0] == '-') || (linepos[0] == '!') || (linepos[0] == ':'))
78699ca880aSopenharmony_ci                        if (linepos[1] == '=')
78799ca880aSopenharmony_ci                                break;
78899ca880aSopenharmony_ci        }
78999ca880aSopenharmony_ci
79099ca880aSopenharmony_ci        /* remember end of key */
79199ca880aSopenharmony_ci        temp = linepos;
79299ca880aSopenharmony_ci
79399ca880aSopenharmony_ci        /* skip whitespace after key */
79499ca880aSopenharmony_ci        while (isspace(linepos[0]))
79599ca880aSopenharmony_ci                linepos++;
79699ca880aSopenharmony_ci        if (linepos[0] == '\0')
79799ca880aSopenharmony_ci                return -1;
79899ca880aSopenharmony_ci
79999ca880aSopenharmony_ci        /* get operation type */
80099ca880aSopenharmony_ci        if (linepos[0] == '=' && linepos[1] == '=') {
80199ca880aSopenharmony_ci                *op = OP_MATCH;
80299ca880aSopenharmony_ci                linepos += 2;
80399ca880aSopenharmony_ci        } else if (linepos[0] == '!' && linepos[1] == '=') {
80499ca880aSopenharmony_ci                *op = OP_NOMATCH;
80599ca880aSopenharmony_ci                linepos += 2;
80699ca880aSopenharmony_ci        } else if (linepos[0] == '+' && linepos[1] == '=') {
80799ca880aSopenharmony_ci                *op = OP_ADD;
80899ca880aSopenharmony_ci                linepos += 2;
80999ca880aSopenharmony_ci        } else if (linepos[0] == '-' && linepos[1] == '=') {
81099ca880aSopenharmony_ci                *op = OP_REMOVE;
81199ca880aSopenharmony_ci                linepos += 2;
81299ca880aSopenharmony_ci        } else if (linepos[0] == '=') {
81399ca880aSopenharmony_ci                *op = OP_ASSIGN;
81499ca880aSopenharmony_ci                linepos++;
81599ca880aSopenharmony_ci        } else if (linepos[0] == ':' && linepos[1] == '=') {
81699ca880aSopenharmony_ci                *op = OP_ASSIGN_FINAL;
81799ca880aSopenharmony_ci                linepos += 2;
81899ca880aSopenharmony_ci        } else
81999ca880aSopenharmony_ci                return -1;
82099ca880aSopenharmony_ci
82199ca880aSopenharmony_ci        /* terminate key */
82299ca880aSopenharmony_ci        temp[0] = '\0';
82399ca880aSopenharmony_ci
82499ca880aSopenharmony_ci        /* skip whitespace after operator */
82599ca880aSopenharmony_ci        while (isspace(linepos[0]))
82699ca880aSopenharmony_ci                linepos++;
82799ca880aSopenharmony_ci        if (linepos[0] == '\0')
82899ca880aSopenharmony_ci                return -1;
82999ca880aSopenharmony_ci
83099ca880aSopenharmony_ci        /* get the value */
83199ca880aSopenharmony_ci        if (linepos[0] == '"')
83299ca880aSopenharmony_ci                linepos++;
83399ca880aSopenharmony_ci        else
83499ca880aSopenharmony_ci                return -1;
83599ca880aSopenharmony_ci        *value = linepos;
83699ca880aSopenharmony_ci
83799ca880aSopenharmony_ci        /* terminate */
83899ca880aSopenharmony_ci        temp = strchr(linepos, '"');
83999ca880aSopenharmony_ci        if (!temp)
84099ca880aSopenharmony_ci                return -1;
84199ca880aSopenharmony_ci        temp[0] = '\0';
84299ca880aSopenharmony_ci        temp++;
84399ca880aSopenharmony_ci
84499ca880aSopenharmony_ci        /* move line to next key */
84599ca880aSopenharmony_ci        *line = temp;
84699ca880aSopenharmony_ci        return 0;
84799ca880aSopenharmony_ci}
84899ca880aSopenharmony_ci
84999ca880aSopenharmony_ci/* extract possible KEY{attr} */
85099ca880aSopenharmony_cistatic const char *get_key_attribute(struct udev *udev, char *str) {
85199ca880aSopenharmony_ci        char *pos;
85299ca880aSopenharmony_ci        char *attr;
85399ca880aSopenharmony_ci
85499ca880aSopenharmony_ci        attr = strchr(str, '{');
85599ca880aSopenharmony_ci        if (attr != NULL) {
85699ca880aSopenharmony_ci                attr++;
85799ca880aSopenharmony_ci                pos = strchr(attr, '}');
85899ca880aSopenharmony_ci                if (pos == NULL) {
85999ca880aSopenharmony_ci                        log_error("missing closing brace for format");
86099ca880aSopenharmony_ci                        return NULL;
86199ca880aSopenharmony_ci                }
86299ca880aSopenharmony_ci                pos[0] = '\0';
86399ca880aSopenharmony_ci                return attr;
86499ca880aSopenharmony_ci        }
86599ca880aSopenharmony_ci        return NULL;
86699ca880aSopenharmony_ci}
86799ca880aSopenharmony_ci
86899ca880aSopenharmony_cistatic int rule_add_key(struct rule_tmp *rule_tmp, enum token_type type,
86999ca880aSopenharmony_ci                        enum operation_type op,
87099ca880aSopenharmony_ci                        const char *value, const void *data) {
87199ca880aSopenharmony_ci        struct token *token = &rule_tmp->token[rule_tmp->token_cur];
87299ca880aSopenharmony_ci        const char *attr = NULL;
87399ca880aSopenharmony_ci
87499ca880aSopenharmony_ci        memzero(token, sizeof(struct token));
87599ca880aSopenharmony_ci
87699ca880aSopenharmony_ci        switch (type) {
87799ca880aSopenharmony_ci        case TK_M_ACTION:
87899ca880aSopenharmony_ci        case TK_M_DEVPATH:
87999ca880aSopenharmony_ci        case TK_M_KERNEL:
88099ca880aSopenharmony_ci        case TK_M_SUBSYSTEM:
88199ca880aSopenharmony_ci        case TK_M_DRIVER:
88299ca880aSopenharmony_ci        case TK_M_WAITFOR:
88399ca880aSopenharmony_ci        case TK_M_DEVLINK:
88499ca880aSopenharmony_ci        case TK_M_NAME:
88599ca880aSopenharmony_ci        case TK_M_KERNELS:
88699ca880aSopenharmony_ci        case TK_M_SUBSYSTEMS:
88799ca880aSopenharmony_ci        case TK_M_DRIVERS:
88899ca880aSopenharmony_ci        case TK_M_TAGS:
88999ca880aSopenharmony_ci        case TK_M_PROGRAM:
89099ca880aSopenharmony_ci        case TK_M_IMPORT_FILE:
89199ca880aSopenharmony_ci        case TK_M_IMPORT_PROG:
89299ca880aSopenharmony_ci        case TK_M_IMPORT_DB:
89399ca880aSopenharmony_ci        case TK_M_IMPORT_CMDLINE:
89499ca880aSopenharmony_ci        case TK_M_IMPORT_PARENT:
89599ca880aSopenharmony_ci        case TK_M_RESULT:
89699ca880aSopenharmony_ci        case TK_A_OWNER:
89799ca880aSopenharmony_ci        case TK_A_GROUP:
89899ca880aSopenharmony_ci        case TK_A_MODE:
89999ca880aSopenharmony_ci        case TK_A_DEVLINK:
90099ca880aSopenharmony_ci        case TK_A_NAME:
90199ca880aSopenharmony_ci        case TK_A_GOTO:
90299ca880aSopenharmony_ci        case TK_M_TAG:
90399ca880aSopenharmony_ci        case TK_A_TAG:
90499ca880aSopenharmony_ci        case TK_A_STATIC_NODE:
90599ca880aSopenharmony_ci                token->key.value_off = rules_add_string(rule_tmp->rules, value);
90699ca880aSopenharmony_ci                break;
90799ca880aSopenharmony_ci        case TK_M_IMPORT_BUILTIN:
90899ca880aSopenharmony_ci                token->key.value_off = rules_add_string(rule_tmp->rules, value);
90999ca880aSopenharmony_ci                token->key.builtin_cmd = *(enum udev_builtin_cmd *)data;
91099ca880aSopenharmony_ci                break;
91199ca880aSopenharmony_ci        case TK_M_ENV:
91299ca880aSopenharmony_ci        case TK_M_ATTR:
91399ca880aSopenharmony_ci        case TK_M_SYSCTL:
91499ca880aSopenharmony_ci        case TK_M_ATTRS:
91599ca880aSopenharmony_ci        case TK_A_ATTR:
91699ca880aSopenharmony_ci        case TK_A_SYSCTL:
91799ca880aSopenharmony_ci        case TK_A_ENV:
91899ca880aSopenharmony_ci        case TK_A_SECLABEL:
91999ca880aSopenharmony_ci                attr = data;
92099ca880aSopenharmony_ci                token->key.value_off = rules_add_string(rule_tmp->rules, value);
92199ca880aSopenharmony_ci                token->key.attr_off = rules_add_string(rule_tmp->rules, attr);
92299ca880aSopenharmony_ci                break;
92399ca880aSopenharmony_ci        case TK_M_TEST:
92499ca880aSopenharmony_ci                token->key.value_off = rules_add_string(rule_tmp->rules, value);
92599ca880aSopenharmony_ci                if (data != NULL)
92699ca880aSopenharmony_ci                        token->key.mode = *(mode_t *)data;
92799ca880aSopenharmony_ci                break;
92899ca880aSopenharmony_ci        case TK_A_STRING_ESCAPE_NONE:
92999ca880aSopenharmony_ci        case TK_A_STRING_ESCAPE_REPLACE:
93099ca880aSopenharmony_ci        case TK_A_DB_PERSIST:
93199ca880aSopenharmony_ci                break;
93299ca880aSopenharmony_ci        case TK_A_RUN_BUILTIN:
93399ca880aSopenharmony_ci        case TK_A_RUN_PROGRAM:
93499ca880aSopenharmony_ci                token->key.builtin_cmd = *(enum udev_builtin_cmd *)data;
93599ca880aSopenharmony_ci                token->key.value_off = rules_add_string(rule_tmp->rules, value);
93699ca880aSopenharmony_ci                break;
93799ca880aSopenharmony_ci        case TK_A_INOTIFY_WATCH:
93899ca880aSopenharmony_ci        case TK_A_DEVLINK_PRIO:
93999ca880aSopenharmony_ci                token->key.devlink_prio = *(int *)data;
94099ca880aSopenharmony_ci                break;
94199ca880aSopenharmony_ci        case TK_A_OWNER_ID:
94299ca880aSopenharmony_ci                token->key.uid = *(uid_t *)data;
94399ca880aSopenharmony_ci                break;
94499ca880aSopenharmony_ci        case TK_A_GROUP_ID:
94599ca880aSopenharmony_ci                token->key.gid = *(gid_t *)data;
94699ca880aSopenharmony_ci                break;
94799ca880aSopenharmony_ci        case TK_A_MODE_ID:
94899ca880aSopenharmony_ci                token->key.mode = *(mode_t *)data;
94999ca880aSopenharmony_ci                break;
95099ca880aSopenharmony_ci        case TK_RULE:
95199ca880aSopenharmony_ci        case TK_M_PARENTS_MIN:
95299ca880aSopenharmony_ci        case TK_M_PARENTS_MAX:
95399ca880aSopenharmony_ci        case TK_M_MAX:
95499ca880aSopenharmony_ci        case TK_END:
95599ca880aSopenharmony_ci        case TK_UNSET:
95699ca880aSopenharmony_ci                log_error("wrong type %u", type);
95799ca880aSopenharmony_ci                return -1;
95899ca880aSopenharmony_ci        }
95999ca880aSopenharmony_ci
96099ca880aSopenharmony_ci        if (value != NULL && type < TK_M_MAX) {
96199ca880aSopenharmony_ci                /* check if we need to split or call fnmatch() while matching rules */
96299ca880aSopenharmony_ci                enum string_glob_type glob;
96399ca880aSopenharmony_ci                int has_split;
96499ca880aSopenharmony_ci                int has_glob;
96599ca880aSopenharmony_ci
96699ca880aSopenharmony_ci                has_split = (strchr(value, '|') != NULL);
96799ca880aSopenharmony_ci                has_glob = string_is_glob(value);
96899ca880aSopenharmony_ci                if (has_split && has_glob) {
96999ca880aSopenharmony_ci                        glob = GL_SPLIT_GLOB;
97099ca880aSopenharmony_ci                } else if (has_split) {
97199ca880aSopenharmony_ci                        glob = GL_SPLIT;
97299ca880aSopenharmony_ci                } else if (has_glob) {
97399ca880aSopenharmony_ci                        if (streq(value, "?*"))
97499ca880aSopenharmony_ci                                glob = GL_SOMETHING;
97599ca880aSopenharmony_ci                        else
97699ca880aSopenharmony_ci                                glob = GL_GLOB;
97799ca880aSopenharmony_ci                } else {
97899ca880aSopenharmony_ci                        glob = GL_PLAIN;
97999ca880aSopenharmony_ci                }
98099ca880aSopenharmony_ci                token->key.glob = glob;
98199ca880aSopenharmony_ci        }
98299ca880aSopenharmony_ci
98399ca880aSopenharmony_ci        if (value != NULL && type > TK_M_MAX) {
98499ca880aSopenharmony_ci                /* check if assigned value has substitution chars */
98599ca880aSopenharmony_ci                if (value[0] == '[')
98699ca880aSopenharmony_ci                        token->key.subst = SB_SUBSYS;
98799ca880aSopenharmony_ci                else if (strchr(value, '%') != NULL || strchr(value, '$') != NULL)
98899ca880aSopenharmony_ci                        token->key.subst = SB_FORMAT;
98999ca880aSopenharmony_ci                else
99099ca880aSopenharmony_ci                        token->key.subst = SB_NONE;
99199ca880aSopenharmony_ci        }
99299ca880aSopenharmony_ci
99399ca880aSopenharmony_ci        if (attr != NULL) {
99499ca880aSopenharmony_ci                /* check if property/attribute name has substitution chars */
99599ca880aSopenharmony_ci                if (attr[0] == '[')
99699ca880aSopenharmony_ci                        token->key.attrsubst = SB_SUBSYS;
99799ca880aSopenharmony_ci                else if (strchr(attr, '%') != NULL || strchr(attr, '$') != NULL)
99899ca880aSopenharmony_ci                        token->key.attrsubst = SB_FORMAT;
99999ca880aSopenharmony_ci                else
100099ca880aSopenharmony_ci                        token->key.attrsubst = SB_NONE;
100199ca880aSopenharmony_ci        }
100299ca880aSopenharmony_ci
100399ca880aSopenharmony_ci        token->key.type = type;
100499ca880aSopenharmony_ci        token->key.op = op;
100599ca880aSopenharmony_ci        rule_tmp->token_cur++;
100699ca880aSopenharmony_ci        if (rule_tmp->token_cur >= ELEMENTSOF(rule_tmp->token)) {
100799ca880aSopenharmony_ci                log_error("temporary rule array too small");
100899ca880aSopenharmony_ci                return -1;
100999ca880aSopenharmony_ci        }
101099ca880aSopenharmony_ci        return 0;
101199ca880aSopenharmony_ci}
101299ca880aSopenharmony_ci
101399ca880aSopenharmony_cistatic int sort_token(struct udev_rules *rules, struct rule_tmp *rule_tmp) {
101499ca880aSopenharmony_ci        unsigned int i;
101599ca880aSopenharmony_ci        unsigned int start = 0;
101699ca880aSopenharmony_ci        unsigned int end = rule_tmp->token_cur;
101799ca880aSopenharmony_ci
101899ca880aSopenharmony_ci        for (i = 0; i < rule_tmp->token_cur; i++) {
101999ca880aSopenharmony_ci                enum token_type next_val = TK_UNSET;
102099ca880aSopenharmony_ci                unsigned int next_idx = 0;
102199ca880aSopenharmony_ci                unsigned int j;
102299ca880aSopenharmony_ci
102399ca880aSopenharmony_ci                /* find smallest value */
102499ca880aSopenharmony_ci                for (j = start; j < end; j++) {
102599ca880aSopenharmony_ci                        if (rule_tmp->token[j].type == TK_UNSET)
102699ca880aSopenharmony_ci                                continue;
102799ca880aSopenharmony_ci                        if (next_val == TK_UNSET || rule_tmp->token[j].type < next_val) {
102899ca880aSopenharmony_ci                                next_val = rule_tmp->token[j].type;
102999ca880aSopenharmony_ci                                next_idx = j;
103099ca880aSopenharmony_ci                        }
103199ca880aSopenharmony_ci                }
103299ca880aSopenharmony_ci
103399ca880aSopenharmony_ci                /* add token and mark done */
103499ca880aSopenharmony_ci                if (add_token(rules, &rule_tmp->token[next_idx]) != 0)
103599ca880aSopenharmony_ci                        return -1;
103699ca880aSopenharmony_ci                rule_tmp->token[next_idx].type = TK_UNSET;
103799ca880aSopenharmony_ci
103899ca880aSopenharmony_ci                /* shrink range */
103999ca880aSopenharmony_ci                if (next_idx == start)
104099ca880aSopenharmony_ci                        start++;
104199ca880aSopenharmony_ci                if (next_idx+1 == end)
104299ca880aSopenharmony_ci                        end--;
104399ca880aSopenharmony_ci        }
104499ca880aSopenharmony_ci        return 0;
104599ca880aSopenharmony_ci}
104699ca880aSopenharmony_ci
104799ca880aSopenharmony_cistatic int add_rule(struct udev_rules *rules, char *line,
104899ca880aSopenharmony_ci                    const char *filename, unsigned int filename_off, unsigned int lineno) {
104999ca880aSopenharmony_ci        char *linepos;
105099ca880aSopenharmony_ci        const char *attr;
105199ca880aSopenharmony_ci        struct rule_tmp rule_tmp;
105299ca880aSopenharmony_ci
105399ca880aSopenharmony_ci        memzero(&rule_tmp, sizeof(struct rule_tmp));
105499ca880aSopenharmony_ci        rule_tmp.rules = rules;
105599ca880aSopenharmony_ci        rule_tmp.rule.type = TK_RULE;
105699ca880aSopenharmony_ci        /* the offset in the rule is limited to unsigned short */
105799ca880aSopenharmony_ci        if (filename_off < USHRT_MAX)
105899ca880aSopenharmony_ci                rule_tmp.rule.rule.filename_off = filename_off;
105999ca880aSopenharmony_ci        rule_tmp.rule.rule.filename_line = lineno;
106099ca880aSopenharmony_ci
106199ca880aSopenharmony_ci        linepos = line;
106299ca880aSopenharmony_ci        for (;;) {
106399ca880aSopenharmony_ci                char *key;
106499ca880aSopenharmony_ci                char *value;
106599ca880aSopenharmony_ci                enum operation_type op;
106699ca880aSopenharmony_ci
106799ca880aSopenharmony_ci                if (get_key(rules->udev, &linepos, &key, &op, &value) != 0) {
106899ca880aSopenharmony_ci                        /* Avoid erroring on trailing whitespace. This is probably rare
106999ca880aSopenharmony_ci                         * so save the work for the error case instead of always trying
107099ca880aSopenharmony_ci                         * to strip the trailing whitespace with strstrip(). */
107199ca880aSopenharmony_ci                        while (isblank(*linepos))
107299ca880aSopenharmony_ci                                linepos++;
107399ca880aSopenharmony_ci
107499ca880aSopenharmony_ci                        /* If we aren't at the end of the line, this is a parsing error.
107599ca880aSopenharmony_ci                         * Make a best effort to describe where the problem is. */
107699ca880aSopenharmony_ci                        if (!strchr(NEWLINE, *linepos)) {
107799ca880aSopenharmony_ci                                char buf[2] = {*linepos};
107899ca880aSopenharmony_ci                                _cleanup_free_ char *tmp;
107999ca880aSopenharmony_ci
108099ca880aSopenharmony_ci                                tmp = cescape(buf);
108199ca880aSopenharmony_ci                                log_error("invalid key/value pair in file %s on line %u, starting at character %tu ('%s')",
108299ca880aSopenharmony_ci                                          filename, lineno, linepos - line + 1, tmp);
108399ca880aSopenharmony_ci                                if (*linepos == '#')
108499ca880aSopenharmony_ci                                        log_error("hint: comments can only start at beginning of line");
108599ca880aSopenharmony_ci                        }
108699ca880aSopenharmony_ci                        break;
108799ca880aSopenharmony_ci                }
108899ca880aSopenharmony_ci
108999ca880aSopenharmony_ci                if (streq(key, "ACTION")) {
109099ca880aSopenharmony_ci                        if (op > OP_MATCH_MAX) {
109199ca880aSopenharmony_ci                                log_error("invalid ACTION operation");
109299ca880aSopenharmony_ci                                goto invalid;
109399ca880aSopenharmony_ci                        }
109499ca880aSopenharmony_ci                        rule_add_key(&rule_tmp, TK_M_ACTION, op, value, NULL);
109599ca880aSopenharmony_ci                        continue;
109699ca880aSopenharmony_ci                }
109799ca880aSopenharmony_ci
109899ca880aSopenharmony_ci                if (streq(key, "DEVPATH")) {
109999ca880aSopenharmony_ci                        if (op > OP_MATCH_MAX) {
110099ca880aSopenharmony_ci                                log_error("invalid DEVPATH operation");
110199ca880aSopenharmony_ci                                goto invalid;
110299ca880aSopenharmony_ci                        }
110399ca880aSopenharmony_ci                        rule_add_key(&rule_tmp, TK_M_DEVPATH, op, value, NULL);
110499ca880aSopenharmony_ci                        continue;
110599ca880aSopenharmony_ci                }
110699ca880aSopenharmony_ci
110799ca880aSopenharmony_ci                if (streq(key, "KERNEL")) {
110899ca880aSopenharmony_ci                        if (op > OP_MATCH_MAX) {
110999ca880aSopenharmony_ci                                log_error("invalid KERNEL operation");
111099ca880aSopenharmony_ci                                goto invalid;
111199ca880aSopenharmony_ci                        }
111299ca880aSopenharmony_ci                        rule_add_key(&rule_tmp, TK_M_KERNEL, op, value, NULL);
111399ca880aSopenharmony_ci                        continue;
111499ca880aSopenharmony_ci                }
111599ca880aSopenharmony_ci
111699ca880aSopenharmony_ci                if (streq(key, "SUBSYSTEM")) {
111799ca880aSopenharmony_ci                        if (op > OP_MATCH_MAX) {
111899ca880aSopenharmony_ci                                log_error("invalid SUBSYSTEM operation");
111999ca880aSopenharmony_ci                                goto invalid;
112099ca880aSopenharmony_ci                        }
112199ca880aSopenharmony_ci                        /* bus, class, subsystem events should all be the same */
112299ca880aSopenharmony_ci                        if (streq(value, "subsystem") ||
112399ca880aSopenharmony_ci                            streq(value, "bus") ||
112499ca880aSopenharmony_ci                            streq(value, "class")) {
112599ca880aSopenharmony_ci                                if (streq(value, "bus") || streq(value, "class"))
112699ca880aSopenharmony_ci                                        log_error("'%s' must be specified as 'subsystem' "
112799ca880aSopenharmony_ci                                            "please fix it in %s:%u", value, filename, lineno);
112899ca880aSopenharmony_ci                                rule_add_key(&rule_tmp, TK_M_SUBSYSTEM, op, "subsystem|class|bus", NULL);
112999ca880aSopenharmony_ci                        } else
113099ca880aSopenharmony_ci                                rule_add_key(&rule_tmp, TK_M_SUBSYSTEM, op, value, NULL);
113199ca880aSopenharmony_ci                        continue;
113299ca880aSopenharmony_ci                }
113399ca880aSopenharmony_ci
113499ca880aSopenharmony_ci                if (streq(key, "DRIVER")) {
113599ca880aSopenharmony_ci                        if (op > OP_MATCH_MAX) {
113699ca880aSopenharmony_ci                                log_error("invalid DRIVER operation");
113799ca880aSopenharmony_ci                                goto invalid;
113899ca880aSopenharmony_ci                        }
113999ca880aSopenharmony_ci                        rule_add_key(&rule_tmp, TK_M_DRIVER, op, value, NULL);
114099ca880aSopenharmony_ci                        continue;
114199ca880aSopenharmony_ci                }
114299ca880aSopenharmony_ci
114399ca880aSopenharmony_ci                if (startswith(key, "ATTR{")) {
114499ca880aSopenharmony_ci                        attr = get_key_attribute(rules->udev, key + strlen("ATTR"));
114599ca880aSopenharmony_ci                        if (attr == NULL) {
114699ca880aSopenharmony_ci                                log_error("error parsing ATTR attribute");
114799ca880aSopenharmony_ci                                goto invalid;
114899ca880aSopenharmony_ci                        }
114999ca880aSopenharmony_ci                        if (op == OP_REMOVE) {
115099ca880aSopenharmony_ci                                log_error("invalid ATTR operation");
115199ca880aSopenharmony_ci                                goto invalid;
115299ca880aSopenharmony_ci                        }
115399ca880aSopenharmony_ci                        if (op < OP_MATCH_MAX)
115499ca880aSopenharmony_ci                                rule_add_key(&rule_tmp, TK_M_ATTR, op, value, attr);
115599ca880aSopenharmony_ci                        else
115699ca880aSopenharmony_ci                                rule_add_key(&rule_tmp, TK_A_ATTR, op, value, attr);
115799ca880aSopenharmony_ci                        continue;
115899ca880aSopenharmony_ci                }
115999ca880aSopenharmony_ci
116099ca880aSopenharmony_ci                if (startswith(key, "SYSCTL{")) {
116199ca880aSopenharmony_ci                        attr = get_key_attribute(rules->udev, key + strlen("SYSCTL"));
116299ca880aSopenharmony_ci                        if (attr == NULL) {
116399ca880aSopenharmony_ci                                log_error("error parsing SYSCTL attribute");
116499ca880aSopenharmony_ci                                goto invalid;
116599ca880aSopenharmony_ci                        }
116699ca880aSopenharmony_ci                        if (op == OP_REMOVE) {
116799ca880aSopenharmony_ci                                log_error("invalid SYSCTL operation");
116899ca880aSopenharmony_ci                                goto invalid;
116999ca880aSopenharmony_ci                        }
117099ca880aSopenharmony_ci                        if (op < OP_MATCH_MAX)
117199ca880aSopenharmony_ci                                rule_add_key(&rule_tmp, TK_M_SYSCTL, op, value, attr);
117299ca880aSopenharmony_ci                        else
117399ca880aSopenharmony_ci                                rule_add_key(&rule_tmp, TK_A_SYSCTL, op, value, attr);
117499ca880aSopenharmony_ci                        continue;
117599ca880aSopenharmony_ci                }
117699ca880aSopenharmony_ci
117799ca880aSopenharmony_ci                if (startswith(key, "SECLABEL{")) {
117899ca880aSopenharmony_ci                        attr = get_key_attribute(rules->udev, key + strlen("SECLABEL"));
117999ca880aSopenharmony_ci                        if (!attr) {
118099ca880aSopenharmony_ci                                log_error("error parsing SECLABEL attribute");
118199ca880aSopenharmony_ci                                goto invalid;
118299ca880aSopenharmony_ci                        }
118399ca880aSopenharmony_ci                        if (op == OP_REMOVE) {
118499ca880aSopenharmony_ci                                log_error("invalid SECLABEL operation");
118599ca880aSopenharmony_ci                                goto invalid;
118699ca880aSopenharmony_ci                        }
118799ca880aSopenharmony_ci
118899ca880aSopenharmony_ci                        rule_add_key(&rule_tmp, TK_A_SECLABEL, op, value, attr);
118999ca880aSopenharmony_ci                        continue;
119099ca880aSopenharmony_ci                }
119199ca880aSopenharmony_ci
119299ca880aSopenharmony_ci                if (streq(key, "KERNELS")) {
119399ca880aSopenharmony_ci                        if (op > OP_MATCH_MAX) {
119499ca880aSopenharmony_ci                                log_error("invalid KERNELS operation");
119599ca880aSopenharmony_ci                                goto invalid;
119699ca880aSopenharmony_ci                        }
119799ca880aSopenharmony_ci                        rule_add_key(&rule_tmp, TK_M_KERNELS, op, value, NULL);
119899ca880aSopenharmony_ci                        continue;
119999ca880aSopenharmony_ci                }
120099ca880aSopenharmony_ci
120199ca880aSopenharmony_ci                if (streq(key, "SUBSYSTEMS")) {
120299ca880aSopenharmony_ci                        if (op > OP_MATCH_MAX) {
120399ca880aSopenharmony_ci                                log_error("invalid SUBSYSTEMS operation");
120499ca880aSopenharmony_ci                                goto invalid;
120599ca880aSopenharmony_ci                        }
120699ca880aSopenharmony_ci                        rule_add_key(&rule_tmp, TK_M_SUBSYSTEMS, op, value, NULL);
120799ca880aSopenharmony_ci                        continue;
120899ca880aSopenharmony_ci                }
120999ca880aSopenharmony_ci
121099ca880aSopenharmony_ci                if (streq(key, "DRIVERS")) {
121199ca880aSopenharmony_ci                        if (op > OP_MATCH_MAX) {
121299ca880aSopenharmony_ci                                log_error("invalid DRIVERS operation");
121399ca880aSopenharmony_ci                                goto invalid;
121499ca880aSopenharmony_ci                        }
121599ca880aSopenharmony_ci                        rule_add_key(&rule_tmp, TK_M_DRIVERS, op, value, NULL);
121699ca880aSopenharmony_ci                        continue;
121799ca880aSopenharmony_ci                }
121899ca880aSopenharmony_ci
121999ca880aSopenharmony_ci                if (startswith(key, "ATTRS{")) {
122099ca880aSopenharmony_ci                        if (op > OP_MATCH_MAX) {
122199ca880aSopenharmony_ci                                log_error("invalid ATTRS operation");
122299ca880aSopenharmony_ci                                goto invalid;
122399ca880aSopenharmony_ci                        }
122499ca880aSopenharmony_ci                        attr = get_key_attribute(rules->udev, key + strlen("ATTRS"));
122599ca880aSopenharmony_ci                        if (attr == NULL) {
122699ca880aSopenharmony_ci                                log_error("error parsing ATTRS attribute");
122799ca880aSopenharmony_ci                                goto invalid;
122899ca880aSopenharmony_ci                        }
122999ca880aSopenharmony_ci                        if (startswith(attr, "device/"))
123099ca880aSopenharmony_ci                                log_error("the 'device' link may not be available in a future kernel, "
123199ca880aSopenharmony_ci                                    "please fix it in %s:%u", filename, lineno);
123299ca880aSopenharmony_ci                        else if (strstr(attr, "../") != NULL)
123399ca880aSopenharmony_ci                                log_error("do not reference parent sysfs directories directly, "
123499ca880aSopenharmony_ci                                    "it may break with a future kernel, please fix it in %s:%u", filename, lineno);
123599ca880aSopenharmony_ci                        rule_add_key(&rule_tmp, TK_M_ATTRS, op, value, attr);
123699ca880aSopenharmony_ci                        continue;
123799ca880aSopenharmony_ci                }
123899ca880aSopenharmony_ci
123999ca880aSopenharmony_ci                if (streq(key, "TAGS")) {
124099ca880aSopenharmony_ci                        if (op > OP_MATCH_MAX) {
124199ca880aSopenharmony_ci                                log_error("invalid TAGS operation");
124299ca880aSopenharmony_ci                                goto invalid;
124399ca880aSopenharmony_ci                        }
124499ca880aSopenharmony_ci                        rule_add_key(&rule_tmp, TK_M_TAGS, op, value, NULL);
124599ca880aSopenharmony_ci                        continue;
124699ca880aSopenharmony_ci                }
124799ca880aSopenharmony_ci
124899ca880aSopenharmony_ci                if (startswith(key, "ENV{")) {
124999ca880aSopenharmony_ci                        attr = get_key_attribute(rules->udev, key + strlen("ENV"));
125099ca880aSopenharmony_ci                        if (attr == NULL) {
125199ca880aSopenharmony_ci                                log_error("error parsing ENV attribute");
125299ca880aSopenharmony_ci                                goto invalid;
125399ca880aSopenharmony_ci                        }
125499ca880aSopenharmony_ci                        if (op == OP_REMOVE) {
125599ca880aSopenharmony_ci                                log_error("invalid ENV operation");
125699ca880aSopenharmony_ci                                goto invalid;
125799ca880aSopenharmony_ci                        }
125899ca880aSopenharmony_ci                        if (op < OP_MATCH_MAX) {
125999ca880aSopenharmony_ci                                if (rule_add_key(&rule_tmp, TK_M_ENV, op, value, attr) != 0)
126099ca880aSopenharmony_ci                                        goto invalid;
126199ca880aSopenharmony_ci                        } else {
126299ca880aSopenharmony_ci                                static const char *blacklist[] = {
126399ca880aSopenharmony_ci                                        "ACTION",
126499ca880aSopenharmony_ci                                        "SUBSYSTEM",
126599ca880aSopenharmony_ci                                        "DEVTYPE",
126699ca880aSopenharmony_ci                                        "MAJOR",
126799ca880aSopenharmony_ci                                        "MINOR",
126899ca880aSopenharmony_ci                                        "DRIVER",
126999ca880aSopenharmony_ci                                        "IFINDEX",
127099ca880aSopenharmony_ci                                        "DEVNAME",
127199ca880aSopenharmony_ci                                        "DEVLINKS",
127299ca880aSopenharmony_ci                                        "DEVPATH",
127399ca880aSopenharmony_ci                                        "TAGS",
127499ca880aSopenharmony_ci                                };
127599ca880aSopenharmony_ci                                unsigned int i;
127699ca880aSopenharmony_ci
127799ca880aSopenharmony_ci                                for (i = 0; i < ELEMENTSOF(blacklist); i++) {
127899ca880aSopenharmony_ci                                        if (!streq(attr, blacklist[i]))
127999ca880aSopenharmony_ci                                                continue;
128099ca880aSopenharmony_ci                                        log_error("invalid ENV attribute, '%s' can not be set %s:%u", attr, filename, lineno);
128199ca880aSopenharmony_ci                                        goto invalid;
128299ca880aSopenharmony_ci                                }
128399ca880aSopenharmony_ci                                if (rule_add_key(&rule_tmp, TK_A_ENV, op, value, attr) != 0)
128499ca880aSopenharmony_ci                                        goto invalid;
128599ca880aSopenharmony_ci                        }
128699ca880aSopenharmony_ci                        continue;
128799ca880aSopenharmony_ci                }
128899ca880aSopenharmony_ci
128999ca880aSopenharmony_ci                if (streq(key, "TAG")) {
129099ca880aSopenharmony_ci                        if (op < OP_MATCH_MAX)
129199ca880aSopenharmony_ci                                rule_add_key(&rule_tmp, TK_M_TAG, op, value, NULL);
129299ca880aSopenharmony_ci                        else
129399ca880aSopenharmony_ci                                rule_add_key(&rule_tmp, TK_A_TAG, op, value, NULL);
129499ca880aSopenharmony_ci                        continue;
129599ca880aSopenharmony_ci                }
129699ca880aSopenharmony_ci
129799ca880aSopenharmony_ci                if (streq(key, "PROGRAM")) {
129899ca880aSopenharmony_ci                        if (op == OP_REMOVE) {
129999ca880aSopenharmony_ci                                log_error("invalid PROGRAM operation");
130099ca880aSopenharmony_ci                                goto invalid;
130199ca880aSopenharmony_ci                        }
130299ca880aSopenharmony_ci                        rule_add_key(&rule_tmp, TK_M_PROGRAM, op, value, NULL);
130399ca880aSopenharmony_ci                        continue;
130499ca880aSopenharmony_ci                }
130599ca880aSopenharmony_ci
130699ca880aSopenharmony_ci                if (streq(key, "RESULT")) {
130799ca880aSopenharmony_ci                        if (op > OP_MATCH_MAX) {
130899ca880aSopenharmony_ci                                log_error("invalid RESULT operation");
130999ca880aSopenharmony_ci                                goto invalid;
131099ca880aSopenharmony_ci                        }
131199ca880aSopenharmony_ci                        rule_add_key(&rule_tmp, TK_M_RESULT, op, value, NULL);
131299ca880aSopenharmony_ci                        continue;
131399ca880aSopenharmony_ci                }
131499ca880aSopenharmony_ci
131599ca880aSopenharmony_ci                if (startswith(key, "IMPORT")) {
131699ca880aSopenharmony_ci                        attr = get_key_attribute(rules->udev, key + strlen("IMPORT"));
131799ca880aSopenharmony_ci                        if (attr == NULL) {
131899ca880aSopenharmony_ci                                log_error("IMPORT{} type missing, ignoring IMPORT %s:%u", filename, lineno);
131999ca880aSopenharmony_ci                                continue;
132099ca880aSopenharmony_ci                        }
132199ca880aSopenharmony_ci                        if (op == OP_REMOVE) {
132299ca880aSopenharmony_ci                                log_error("invalid IMPORT operation");
132399ca880aSopenharmony_ci                                goto invalid;
132499ca880aSopenharmony_ci                        }
132599ca880aSopenharmony_ci                        if (streq(attr, "program")) {
132699ca880aSopenharmony_ci                                /* find known built-in command */
132799ca880aSopenharmony_ci                                if (value[0] != '/') {
132899ca880aSopenharmony_ci                                        enum udev_builtin_cmd cmd;
132999ca880aSopenharmony_ci
133099ca880aSopenharmony_ci                                        cmd = udev_builtin_lookup(value);
133199ca880aSopenharmony_ci                                        if (cmd < UDEV_BUILTIN_MAX) {
133299ca880aSopenharmony_ci                                                log_debug("IMPORT found builtin '%s', replacing %s:%u",
133399ca880aSopenharmony_ci                                                          value, filename, lineno);
133499ca880aSopenharmony_ci                                                rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd);
133599ca880aSopenharmony_ci                                                continue;
133699ca880aSopenharmony_ci                                        }
133799ca880aSopenharmony_ci                                }
133899ca880aSopenharmony_ci                                rule_add_key(&rule_tmp, TK_M_IMPORT_PROG, op, value, NULL);
133999ca880aSopenharmony_ci                        } else if (streq(attr, "builtin")) {
134099ca880aSopenharmony_ci                                enum udev_builtin_cmd cmd = udev_builtin_lookup(value);
134199ca880aSopenharmony_ci
134299ca880aSopenharmony_ci                                if (cmd < UDEV_BUILTIN_MAX)
134399ca880aSopenharmony_ci                                        rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd);
134499ca880aSopenharmony_ci                                else
134599ca880aSopenharmony_ci                                        log_error("IMPORT{builtin}: '%s' unknown %s:%u", value, filename, lineno);
134699ca880aSopenharmony_ci                        } else if (streq(attr, "file")) {
134799ca880aSopenharmony_ci                                rule_add_key(&rule_tmp, TK_M_IMPORT_FILE, op, value, NULL);
134899ca880aSopenharmony_ci                        } else if (streq(attr, "db")) {
134999ca880aSopenharmony_ci                                rule_add_key(&rule_tmp, TK_M_IMPORT_DB, op, value, NULL);
135099ca880aSopenharmony_ci                        } else if (streq(attr, "cmdline")) {
135199ca880aSopenharmony_ci                                rule_add_key(&rule_tmp, TK_M_IMPORT_CMDLINE, op, value, NULL);
135299ca880aSopenharmony_ci                        } else if (streq(attr, "parent")) {
135399ca880aSopenharmony_ci                                rule_add_key(&rule_tmp, TK_M_IMPORT_PARENT, op, value, NULL);
135499ca880aSopenharmony_ci                        } else
135599ca880aSopenharmony_ci                                log_error("IMPORT{} unknown type, ignoring IMPORT %s:%u", filename, lineno);
135699ca880aSopenharmony_ci                        continue;
135799ca880aSopenharmony_ci                }
135899ca880aSopenharmony_ci
135999ca880aSopenharmony_ci                if (startswith(key, "TEST")) {
136099ca880aSopenharmony_ci                        mode_t mode = 0;
136199ca880aSopenharmony_ci
136299ca880aSopenharmony_ci                        if (op > OP_MATCH_MAX) {
136399ca880aSopenharmony_ci                                log_error("invalid TEST operation");
136499ca880aSopenharmony_ci                                goto invalid;
136599ca880aSopenharmony_ci                        }
136699ca880aSopenharmony_ci                        attr = get_key_attribute(rules->udev, key + strlen("TEST"));
136799ca880aSopenharmony_ci                        if (attr != NULL) {
136899ca880aSopenharmony_ci                                mode = strtol(attr, NULL, 8);
136999ca880aSopenharmony_ci                                rule_add_key(&rule_tmp, TK_M_TEST, op, value, &mode);
137099ca880aSopenharmony_ci                        } else {
137199ca880aSopenharmony_ci                                rule_add_key(&rule_tmp, TK_M_TEST, op, value, NULL);
137299ca880aSopenharmony_ci                        }
137399ca880aSopenharmony_ci                        continue;
137499ca880aSopenharmony_ci                }
137599ca880aSopenharmony_ci
137699ca880aSopenharmony_ci                if (startswith(key, "RUN")) {
137799ca880aSopenharmony_ci                        attr = get_key_attribute(rules->udev, key + strlen("RUN"));
137899ca880aSopenharmony_ci                        if (attr == NULL)
137999ca880aSopenharmony_ci                                attr = "program";
138099ca880aSopenharmony_ci                        if (op == OP_REMOVE) {
138199ca880aSopenharmony_ci                                log_error("invalid RUN operation");
138299ca880aSopenharmony_ci                                goto invalid;
138399ca880aSopenharmony_ci                        }
138499ca880aSopenharmony_ci
138599ca880aSopenharmony_ci                        if (streq(attr, "builtin")) {
138699ca880aSopenharmony_ci                                enum udev_builtin_cmd cmd = udev_builtin_lookup(value);
138799ca880aSopenharmony_ci
138899ca880aSopenharmony_ci                                if (cmd < UDEV_BUILTIN_MAX)
138999ca880aSopenharmony_ci                                        rule_add_key(&rule_tmp, TK_A_RUN_BUILTIN, op, value, &cmd);
139099ca880aSopenharmony_ci                                else
139199ca880aSopenharmony_ci                                        log_error("RUN{builtin}: '%s' unknown %s:%u", value, filename, lineno);
139299ca880aSopenharmony_ci                        } else if (streq(attr, "program")) {
139399ca880aSopenharmony_ci                                enum udev_builtin_cmd cmd = UDEV_BUILTIN_MAX;
139499ca880aSopenharmony_ci
139599ca880aSopenharmony_ci                                rule_add_key(&rule_tmp, TK_A_RUN_PROGRAM, op, value, &cmd);
139699ca880aSopenharmony_ci                        } else {
139799ca880aSopenharmony_ci                                log_error("RUN{} unknown type, ignoring RUN %s:%u", filename, lineno);
139899ca880aSopenharmony_ci                        }
139999ca880aSopenharmony_ci
140099ca880aSopenharmony_ci                        continue;
140199ca880aSopenharmony_ci                }
140299ca880aSopenharmony_ci
140399ca880aSopenharmony_ci                if (streq(key, "WAIT_FOR") || streq(key, "WAIT_FOR_SYSFS")) {
140499ca880aSopenharmony_ci                        if (op == OP_REMOVE) {
140599ca880aSopenharmony_ci                                log_error("invalid WAIT_FOR/WAIT_FOR_SYSFS operation");
140699ca880aSopenharmony_ci                                goto invalid;
140799ca880aSopenharmony_ci                        }
140899ca880aSopenharmony_ci                        rule_add_key(&rule_tmp, TK_M_WAITFOR, 0, value, NULL);
140999ca880aSopenharmony_ci                        continue;
141099ca880aSopenharmony_ci                }
141199ca880aSopenharmony_ci
141299ca880aSopenharmony_ci                if (streq(key, "LABEL")) {
141399ca880aSopenharmony_ci                        if (op == OP_REMOVE) {
141499ca880aSopenharmony_ci                                log_error("invalid LABEL operation");
141599ca880aSopenharmony_ci                                goto invalid;
141699ca880aSopenharmony_ci                        }
141799ca880aSopenharmony_ci                        rule_tmp.rule.rule.label_off = rules_add_string(rules, value);
141899ca880aSopenharmony_ci                        continue;
141999ca880aSopenharmony_ci                }
142099ca880aSopenharmony_ci
142199ca880aSopenharmony_ci                if (streq(key, "GOTO")) {
142299ca880aSopenharmony_ci                        if (op == OP_REMOVE) {
142399ca880aSopenharmony_ci                                log_error("invalid GOTO operation");
142499ca880aSopenharmony_ci                                goto invalid;
142599ca880aSopenharmony_ci                        }
142699ca880aSopenharmony_ci                        rule_add_key(&rule_tmp, TK_A_GOTO, 0, value, NULL);
142799ca880aSopenharmony_ci                        continue;
142899ca880aSopenharmony_ci                }
142999ca880aSopenharmony_ci
143099ca880aSopenharmony_ci                if (startswith(key, "NAME")) {
143199ca880aSopenharmony_ci                        if (op == OP_REMOVE) {
143299ca880aSopenharmony_ci                                log_error("invalid NAME operation");
143399ca880aSopenharmony_ci                                goto invalid;
143499ca880aSopenharmony_ci                        }
143599ca880aSopenharmony_ci                        if (op < OP_MATCH_MAX) {
143699ca880aSopenharmony_ci                                rule_add_key(&rule_tmp, TK_M_NAME, op, value, NULL);
143799ca880aSopenharmony_ci                        } else {
143899ca880aSopenharmony_ci                                if (streq(value, "%k")) {
143999ca880aSopenharmony_ci                                        log_error("NAME=\"%%k\" is ignored, because it breaks kernel supplied names, "
144099ca880aSopenharmony_ci                                            "please remove it from %s:%u\n", filename, lineno);
144199ca880aSopenharmony_ci                                        continue;
144299ca880aSopenharmony_ci                                }
144399ca880aSopenharmony_ci                                if (value[0] == '\0') {
144499ca880aSopenharmony_ci                                        log_debug("NAME=\"\" is ignored, because udev will not delete any device nodes, "
144599ca880aSopenharmony_ci                                                  "please remove it from %s:%u\n", filename, lineno);
144699ca880aSopenharmony_ci                                        continue;
144799ca880aSopenharmony_ci                                }
144899ca880aSopenharmony_ci                                rule_add_key(&rule_tmp, TK_A_NAME, op, value, NULL);
144999ca880aSopenharmony_ci                        }
145099ca880aSopenharmony_ci                        rule_tmp.rule.rule.can_set_name = true;
145199ca880aSopenharmony_ci                        continue;
145299ca880aSopenharmony_ci                }
145399ca880aSopenharmony_ci
145499ca880aSopenharmony_ci                if (streq(key, "SYMLINK")) {
145599ca880aSopenharmony_ci                        if (op == OP_REMOVE) {
145699ca880aSopenharmony_ci                                log_error("invalid SYMLINK operation");
145799ca880aSopenharmony_ci                                goto invalid;
145899ca880aSopenharmony_ci                        }
145999ca880aSopenharmony_ci                        if (op < OP_MATCH_MAX)
146099ca880aSopenharmony_ci                                rule_add_key(&rule_tmp, TK_M_DEVLINK, op, value, NULL);
146199ca880aSopenharmony_ci                        else
146299ca880aSopenharmony_ci                                rule_add_key(&rule_tmp, TK_A_DEVLINK, op, value, NULL);
146399ca880aSopenharmony_ci                        rule_tmp.rule.rule.can_set_name = true;
146499ca880aSopenharmony_ci                        continue;
146599ca880aSopenharmony_ci                }
146699ca880aSopenharmony_ci
146799ca880aSopenharmony_ci                if (streq(key, "OWNER")) {
146899ca880aSopenharmony_ci                        uid_t uid;
146999ca880aSopenharmony_ci                        char *endptr;
147099ca880aSopenharmony_ci
147199ca880aSopenharmony_ci                        if (op == OP_REMOVE) {
147299ca880aSopenharmony_ci                                log_error("invalid OWNER operation");
147399ca880aSopenharmony_ci                                goto invalid;
147499ca880aSopenharmony_ci                        }
147599ca880aSopenharmony_ci
147699ca880aSopenharmony_ci                        uid = strtoul(value, &endptr, 10);
147799ca880aSopenharmony_ci                        if (endptr[0] == '\0') {
147899ca880aSopenharmony_ci                                rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid);
147999ca880aSopenharmony_ci                        } else if ((rules->resolve_names > 0) && strchr("$%", value[0]) == NULL) {
148099ca880aSopenharmony_ci                                uid = add_uid(rules, value);
148199ca880aSopenharmony_ci                                rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid);
148299ca880aSopenharmony_ci                        } else if (rules->resolve_names >= 0) {
148399ca880aSopenharmony_ci                                rule_add_key(&rule_tmp, TK_A_OWNER, op, value, NULL);
148499ca880aSopenharmony_ci                        }
148599ca880aSopenharmony_ci                        rule_tmp.rule.rule.can_set_name = true;
148699ca880aSopenharmony_ci                        continue;
148799ca880aSopenharmony_ci                }
148899ca880aSopenharmony_ci
148999ca880aSopenharmony_ci                if (streq(key, "GROUP")) {
149099ca880aSopenharmony_ci                        gid_t gid;
149199ca880aSopenharmony_ci                        char *endptr;
149299ca880aSopenharmony_ci
149399ca880aSopenharmony_ci                        if (op == OP_REMOVE) {
149499ca880aSopenharmony_ci                                log_error("invalid GROUP operation");
149599ca880aSopenharmony_ci                                goto invalid;
149699ca880aSopenharmony_ci                        }
149799ca880aSopenharmony_ci
149899ca880aSopenharmony_ci                        gid = strtoul(value, &endptr, 10);
149999ca880aSopenharmony_ci                        if (endptr[0] == '\0') {
150099ca880aSopenharmony_ci                                rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid);
150199ca880aSopenharmony_ci                        } else if ((rules->resolve_names > 0) && strchr("$%", value[0]) == NULL) {
150299ca880aSopenharmony_ci                                gid = add_gid(rules, value);
150399ca880aSopenharmony_ci                                rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid);
150499ca880aSopenharmony_ci                        } else if (rules->resolve_names >= 0) {
150599ca880aSopenharmony_ci                                rule_add_key(&rule_tmp, TK_A_GROUP, op, value, NULL);
150699ca880aSopenharmony_ci                        }
150799ca880aSopenharmony_ci                        rule_tmp.rule.rule.can_set_name = true;
150899ca880aSopenharmony_ci                        continue;
150999ca880aSopenharmony_ci                }
151099ca880aSopenharmony_ci
151199ca880aSopenharmony_ci                if (streq(key, "MODE")) {
151299ca880aSopenharmony_ci                        mode_t mode;
151399ca880aSopenharmony_ci                        char *endptr;
151499ca880aSopenharmony_ci
151599ca880aSopenharmony_ci                        if (op == OP_REMOVE) {
151699ca880aSopenharmony_ci                                log_error("invalid MODE operation");
151799ca880aSopenharmony_ci                                goto invalid;
151899ca880aSopenharmony_ci                        }
151999ca880aSopenharmony_ci
152099ca880aSopenharmony_ci                        mode = strtol(value, &endptr, 8);
152199ca880aSopenharmony_ci                        if (endptr[0] == '\0')
152299ca880aSopenharmony_ci                                rule_add_key(&rule_tmp, TK_A_MODE_ID, op, NULL, &mode);
152399ca880aSopenharmony_ci                        else
152499ca880aSopenharmony_ci                                rule_add_key(&rule_tmp, TK_A_MODE, op, value, NULL);
152599ca880aSopenharmony_ci                        rule_tmp.rule.rule.can_set_name = true;
152699ca880aSopenharmony_ci                        continue;
152799ca880aSopenharmony_ci                }
152899ca880aSopenharmony_ci
152999ca880aSopenharmony_ci                if (streq(key, "OPTIONS")) {
153099ca880aSopenharmony_ci                        const char *pos;
153199ca880aSopenharmony_ci
153299ca880aSopenharmony_ci                        if (op == OP_REMOVE) {
153399ca880aSopenharmony_ci                                log_error("invalid OPTIONS operation");
153499ca880aSopenharmony_ci                                goto invalid;
153599ca880aSopenharmony_ci                        }
153699ca880aSopenharmony_ci
153799ca880aSopenharmony_ci                        pos = strstr(value, "link_priority=");
153899ca880aSopenharmony_ci                        if (pos != NULL) {
153999ca880aSopenharmony_ci                                int prio = atoi(&pos[strlen("link_priority=")]);
154099ca880aSopenharmony_ci
154199ca880aSopenharmony_ci                                rule_add_key(&rule_tmp, TK_A_DEVLINK_PRIO, op, NULL, &prio);
154299ca880aSopenharmony_ci                        }
154399ca880aSopenharmony_ci
154499ca880aSopenharmony_ci                        pos = strstr(value, "string_escape=");
154599ca880aSopenharmony_ci                        if (pos != NULL) {
154699ca880aSopenharmony_ci                                pos = &pos[strlen("string_escape=")];
154799ca880aSopenharmony_ci                                if (startswith(pos, "none"))
154899ca880aSopenharmony_ci                                        rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_NONE, op, NULL, NULL);
154999ca880aSopenharmony_ci                                else if (startswith(pos, "replace"))
155099ca880aSopenharmony_ci                                        rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_REPLACE, op, NULL, NULL);
155199ca880aSopenharmony_ci                        }
155299ca880aSopenharmony_ci
155399ca880aSopenharmony_ci                        pos = strstr(value, "db_persist");
155499ca880aSopenharmony_ci                        if (pos != NULL)
155599ca880aSopenharmony_ci                                rule_add_key(&rule_tmp, TK_A_DB_PERSIST, op, NULL, NULL);
155699ca880aSopenharmony_ci
155799ca880aSopenharmony_ci                        pos = strstr(value, "nowatch");
155899ca880aSopenharmony_ci                        if (pos != NULL) {
155999ca880aSopenharmony_ci                                const int off = 0;
156099ca880aSopenharmony_ci
156199ca880aSopenharmony_ci                                rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &off);
156299ca880aSopenharmony_ci                        } else {
156399ca880aSopenharmony_ci                                pos = strstr(value, "watch");
156499ca880aSopenharmony_ci                                if (pos != NULL) {
156599ca880aSopenharmony_ci                                        const int on = 1;
156699ca880aSopenharmony_ci
156799ca880aSopenharmony_ci                                        rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &on);
156899ca880aSopenharmony_ci                                }
156999ca880aSopenharmony_ci                        }
157099ca880aSopenharmony_ci
157199ca880aSopenharmony_ci                        pos = strstr(value, "static_node=");
157299ca880aSopenharmony_ci                        if (pos != NULL) {
157399ca880aSopenharmony_ci                                rule_add_key(&rule_tmp, TK_A_STATIC_NODE, op, &pos[strlen("static_node=")], NULL);
157499ca880aSopenharmony_ci                                rule_tmp.rule.rule.has_static_node = true;
157599ca880aSopenharmony_ci                        }
157699ca880aSopenharmony_ci
157799ca880aSopenharmony_ci                        continue;
157899ca880aSopenharmony_ci                }
157999ca880aSopenharmony_ci
158099ca880aSopenharmony_ci                log_error("unknown key '%s' in %s:%u", key, filename, lineno);
158199ca880aSopenharmony_ci                goto invalid;
158299ca880aSopenharmony_ci        }
158399ca880aSopenharmony_ci
158499ca880aSopenharmony_ci        /* add rule token */
158599ca880aSopenharmony_ci        rule_tmp.rule.rule.token_count = 1 + rule_tmp.token_cur;
158699ca880aSopenharmony_ci        if (add_token(rules, &rule_tmp.rule) != 0)
158799ca880aSopenharmony_ci                goto invalid;
158899ca880aSopenharmony_ci
158999ca880aSopenharmony_ci        /* add tokens to list, sorted by type */
159099ca880aSopenharmony_ci        if (sort_token(rules, &rule_tmp) != 0)
159199ca880aSopenharmony_ci                goto invalid;
159299ca880aSopenharmony_ci
159399ca880aSopenharmony_ci        return 0;
159499ca880aSopenharmony_ciinvalid:
159599ca880aSopenharmony_ci        log_error("invalid rule '%s:%u'", filename, lineno);
159699ca880aSopenharmony_ci        return -1;
159799ca880aSopenharmony_ci}
159899ca880aSopenharmony_ci
159999ca880aSopenharmony_cistatic int parse_file(struct udev_rules *rules, const char *filename) {
160099ca880aSopenharmony_ci        _cleanup_fclose_ FILE *f = NULL;
160199ca880aSopenharmony_ci        unsigned int first_token;
160299ca880aSopenharmony_ci        unsigned int filename_off;
160399ca880aSopenharmony_ci        char line[UTIL_LINE_SIZE];
160499ca880aSopenharmony_ci        int line_nr = 0;
160599ca880aSopenharmony_ci        unsigned int i;
160699ca880aSopenharmony_ci
160799ca880aSopenharmony_ci        f = fopen(filename, "re");
160899ca880aSopenharmony_ci        if (!f) {
160999ca880aSopenharmony_ci                if (errno == ENOENT)
161099ca880aSopenharmony_ci                        return 0;
161199ca880aSopenharmony_ci                else
161299ca880aSopenharmony_ci                        return -errno;
161399ca880aSopenharmony_ci        }
161499ca880aSopenharmony_ci
161599ca880aSopenharmony_ci        if (null_or_empty_fd(fileno(f))) {
161699ca880aSopenharmony_ci                log_debug("Skipping empty file: %s", filename);
161799ca880aSopenharmony_ci                return 0;
161899ca880aSopenharmony_ci        } else
161999ca880aSopenharmony_ci                log_debug("Reading rules file: %s", filename);
162099ca880aSopenharmony_ci
162199ca880aSopenharmony_ci        first_token = rules->token_cur;
162299ca880aSopenharmony_ci        filename_off = rules_add_string(rules, filename);
162399ca880aSopenharmony_ci
162499ca880aSopenharmony_ci        while (fgets(line, sizeof(line), f) != NULL) {
162599ca880aSopenharmony_ci                char *key;
162699ca880aSopenharmony_ci                size_t len;
162799ca880aSopenharmony_ci
162899ca880aSopenharmony_ci                /* skip whitespace */
162999ca880aSopenharmony_ci                line_nr++;
163099ca880aSopenharmony_ci                key = line;
163199ca880aSopenharmony_ci                while (isspace(key[0]))
163299ca880aSopenharmony_ci                        key++;
163399ca880aSopenharmony_ci
163499ca880aSopenharmony_ci                /* comment */
163599ca880aSopenharmony_ci                if (key[0] == '#')
163699ca880aSopenharmony_ci                        continue;
163799ca880aSopenharmony_ci
163899ca880aSopenharmony_ci                len = strlen(line);
163999ca880aSopenharmony_ci                if (len < 3)
164099ca880aSopenharmony_ci                        continue;
164199ca880aSopenharmony_ci
164299ca880aSopenharmony_ci                /* continue reading if backslash+newline is found */
164399ca880aSopenharmony_ci                while (line[len-2] == '\\') {
164499ca880aSopenharmony_ci                        if (fgets(&line[len-2], (sizeof(line)-len)+2, f) == NULL)
164599ca880aSopenharmony_ci                                break;
164699ca880aSopenharmony_ci                        if (strlen(&line[len-2]) < 2)
164799ca880aSopenharmony_ci                                break;
164899ca880aSopenharmony_ci                        line_nr++;
164999ca880aSopenharmony_ci                        len = strlen(line);
165099ca880aSopenharmony_ci                }
165199ca880aSopenharmony_ci
165299ca880aSopenharmony_ci                if (len+1 >= sizeof(line)) {
165399ca880aSopenharmony_ci                        log_error("line too long '%s':%u, ignored", filename, line_nr);
165499ca880aSopenharmony_ci                        continue;
165599ca880aSopenharmony_ci                }
165699ca880aSopenharmony_ci                add_rule(rules, key, filename, filename_off, line_nr);
165799ca880aSopenharmony_ci        }
165899ca880aSopenharmony_ci
165999ca880aSopenharmony_ci        /* link GOTOs to LABEL rules in this file to be able to fast-forward */
166099ca880aSopenharmony_ci        for (i = first_token+1; i < rules->token_cur; i++) {
166199ca880aSopenharmony_ci                if (rules->tokens[i].type == TK_A_GOTO) {
166299ca880aSopenharmony_ci                        char *label = rules_str(rules, rules->tokens[i].key.value_off);
166399ca880aSopenharmony_ci                        unsigned int j;
166499ca880aSopenharmony_ci
166599ca880aSopenharmony_ci                        for (j = i+1; j < rules->token_cur; j++) {
166699ca880aSopenharmony_ci                                if (rules->tokens[j].type != TK_RULE)
166799ca880aSopenharmony_ci                                        continue;
166899ca880aSopenharmony_ci                                if (rules->tokens[j].rule.label_off == 0)
166999ca880aSopenharmony_ci                                        continue;
167099ca880aSopenharmony_ci                                if (!streq(label, rules_str(rules, rules->tokens[j].rule.label_off)))
167199ca880aSopenharmony_ci                                        continue;
167299ca880aSopenharmony_ci                                rules->tokens[i].key.rule_goto = j;
167399ca880aSopenharmony_ci                                break;
167499ca880aSopenharmony_ci                        }
167599ca880aSopenharmony_ci                        if (rules->tokens[i].key.rule_goto == 0)
167699ca880aSopenharmony_ci                                log_error("GOTO '%s' has no matching label in: '%s'", label, filename);
167799ca880aSopenharmony_ci                }
167899ca880aSopenharmony_ci        }
167999ca880aSopenharmony_ci        return 0;
168099ca880aSopenharmony_ci}
168199ca880aSopenharmony_ci
168299ca880aSopenharmony_cistruct udev_rules *udev_rules_new(struct udev *udev, int resolve_names) {
168399ca880aSopenharmony_ci        struct udev_rules *rules;
168499ca880aSopenharmony_ci        struct udev_list file_list;
168599ca880aSopenharmony_ci        struct token end_token;
168699ca880aSopenharmony_ci        char **files, **f;
168799ca880aSopenharmony_ci        int r;
168899ca880aSopenharmony_ci
168999ca880aSopenharmony_ci        rules = new0(struct udev_rules, 1);
169099ca880aSopenharmony_ci        if (rules == NULL)
169199ca880aSopenharmony_ci                return NULL;
169299ca880aSopenharmony_ci        rules->udev = udev;
169399ca880aSopenharmony_ci        rules->resolve_names = resolve_names;
169499ca880aSopenharmony_ci        udev_list_init(udev, &file_list, true);
169599ca880aSopenharmony_ci
169699ca880aSopenharmony_ci        /* init token array and string buffer */
169799ca880aSopenharmony_ci        rules->tokens = malloc(PREALLOC_TOKEN * sizeof(struct token));
169899ca880aSopenharmony_ci        if (rules->tokens == NULL)
169999ca880aSopenharmony_ci                return udev_rules_unref(rules);
170099ca880aSopenharmony_ci        rules->token_max = PREALLOC_TOKEN;
170199ca880aSopenharmony_ci
170299ca880aSopenharmony_ci        rules->strbuf = strbuf_new();
170399ca880aSopenharmony_ci        if (!rules->strbuf)
170499ca880aSopenharmony_ci                return udev_rules_unref(rules);
170599ca880aSopenharmony_ci
170699ca880aSopenharmony_ci        udev_rules_check_timestamp(rules);
170799ca880aSopenharmony_ci
170899ca880aSopenharmony_ci        r = conf_files_list_strv(&files, ".rules", NULL, rules_dirs);
170999ca880aSopenharmony_ci        if (r < 0) {
171099ca880aSopenharmony_ci                log_error_errno(r, "failed to enumerate rules files: %m");
171199ca880aSopenharmony_ci                return udev_rules_unref(rules);
171299ca880aSopenharmony_ci        }
171399ca880aSopenharmony_ci
171499ca880aSopenharmony_ci        /*
171599ca880aSopenharmony_ci         * The offset value in the rules strct is limited; add all
171699ca880aSopenharmony_ci         * rules file names to the beginning of the string buffer.
171799ca880aSopenharmony_ci         */
171899ca880aSopenharmony_ci        STRV_FOREACH(f, files)
171999ca880aSopenharmony_ci                rules_add_string(rules, *f);
172099ca880aSopenharmony_ci
172199ca880aSopenharmony_ci        STRV_FOREACH(f, files)
172299ca880aSopenharmony_ci                parse_file(rules, *f);
172399ca880aSopenharmony_ci
172499ca880aSopenharmony_ci        strv_free(files);
172599ca880aSopenharmony_ci
172699ca880aSopenharmony_ci        memzero(&end_token, sizeof(struct token));
172799ca880aSopenharmony_ci        end_token.type = TK_END;
172899ca880aSopenharmony_ci        add_token(rules, &end_token);
172999ca880aSopenharmony_ci        log_debug("rules contain %zu bytes tokens (%u * %zu bytes), %zu bytes strings",
173099ca880aSopenharmony_ci                  rules->token_max * sizeof(struct token), rules->token_max, sizeof(struct token), rules->strbuf->len);
173199ca880aSopenharmony_ci
173299ca880aSopenharmony_ci        /* cleanup temporary strbuf data */
173399ca880aSopenharmony_ci        log_debug("%zu strings (%zu bytes), %zu de-duplicated (%zu bytes), %zu trie nodes used",
173499ca880aSopenharmony_ci                  rules->strbuf->in_count, rules->strbuf->in_len,
173599ca880aSopenharmony_ci                  rules->strbuf->dedup_count, rules->strbuf->dedup_len, rules->strbuf->nodes_count);
173699ca880aSopenharmony_ci        strbuf_complete(rules->strbuf);
173799ca880aSopenharmony_ci
173899ca880aSopenharmony_ci        /* cleanup uid/gid cache */
173999ca880aSopenharmony_ci        free(rules->uids);
174099ca880aSopenharmony_ci        rules->uids = NULL;
174199ca880aSopenharmony_ci        rules->uids_cur = 0;
174299ca880aSopenharmony_ci        rules->uids_max = 0;
174399ca880aSopenharmony_ci        free(rules->gids);
174499ca880aSopenharmony_ci        rules->gids = NULL;
174599ca880aSopenharmony_ci        rules->gids_cur = 0;
174699ca880aSopenharmony_ci        rules->gids_max = 0;
174799ca880aSopenharmony_ci
174899ca880aSopenharmony_ci        dump_rules(rules);
174999ca880aSopenharmony_ci        return rules;
175099ca880aSopenharmony_ci}
175199ca880aSopenharmony_ci
175299ca880aSopenharmony_cistruct udev_rules *udev_rules_unref(struct udev_rules *rules) {
175399ca880aSopenharmony_ci        if (rules == NULL)
175499ca880aSopenharmony_ci                return NULL;
175599ca880aSopenharmony_ci        free(rules->tokens);
175699ca880aSopenharmony_ci        strbuf_cleanup(rules->strbuf);
175799ca880aSopenharmony_ci        free(rules->uids);
175899ca880aSopenharmony_ci        free(rules->gids);
175999ca880aSopenharmony_ci        free(rules);
176099ca880aSopenharmony_ci        return NULL;
176199ca880aSopenharmony_ci}
176299ca880aSopenharmony_ci
176399ca880aSopenharmony_cibool udev_rules_check_timestamp(struct udev_rules *rules) {
176499ca880aSopenharmony_ci        if (!rules)
176599ca880aSopenharmony_ci                return false;
176699ca880aSopenharmony_ci
176799ca880aSopenharmony_ci        return paths_check_timestamp(rules_dirs, &rules->dirs_ts_usec, true);
176899ca880aSopenharmony_ci}
176999ca880aSopenharmony_ci
177099ca880aSopenharmony_cistatic int match_key(struct udev_rules *rules, struct token *token, const char *val) {
177199ca880aSopenharmony_ci        char *key_value = rules_str(rules, token->key.value_off);
177299ca880aSopenharmony_ci        char *pos;
177399ca880aSopenharmony_ci        bool match = false;
177499ca880aSopenharmony_ci
177599ca880aSopenharmony_ci        if (val == NULL)
177699ca880aSopenharmony_ci                val = "";
177799ca880aSopenharmony_ci
177899ca880aSopenharmony_ci        switch (token->key.glob) {
177999ca880aSopenharmony_ci        case GL_PLAIN:
178099ca880aSopenharmony_ci                match = (streq(key_value, val));
178199ca880aSopenharmony_ci                break;
178299ca880aSopenharmony_ci        case GL_GLOB:
178399ca880aSopenharmony_ci                match = (fnmatch(key_value, val, 0) == 0);
178499ca880aSopenharmony_ci                break;
178599ca880aSopenharmony_ci        case GL_SPLIT:
178699ca880aSopenharmony_ci                {
178799ca880aSopenharmony_ci                        const char *s;
178899ca880aSopenharmony_ci                        size_t len;
178999ca880aSopenharmony_ci
179099ca880aSopenharmony_ci                        s = rules_str(rules, token->key.value_off);
179199ca880aSopenharmony_ci                        len = strlen(val);
179299ca880aSopenharmony_ci                        for (;;) {
179399ca880aSopenharmony_ci                                const char *next;
179499ca880aSopenharmony_ci
179599ca880aSopenharmony_ci                                next = strchr(s, '|');
179699ca880aSopenharmony_ci                                if (next != NULL) {
179799ca880aSopenharmony_ci                                        size_t matchlen = (size_t)(next - s);
179899ca880aSopenharmony_ci
179999ca880aSopenharmony_ci                                        match = (matchlen == len && strneq(s, val, matchlen));
180099ca880aSopenharmony_ci                                        if (match)
180199ca880aSopenharmony_ci                                                break;
180299ca880aSopenharmony_ci                                } else {
180399ca880aSopenharmony_ci                                        match = (streq(s, val));
180499ca880aSopenharmony_ci                                        break;
180599ca880aSopenharmony_ci                                }
180699ca880aSopenharmony_ci                                s = &next[1];
180799ca880aSopenharmony_ci                        }
180899ca880aSopenharmony_ci                        break;
180999ca880aSopenharmony_ci                }
181099ca880aSopenharmony_ci        case GL_SPLIT_GLOB:
181199ca880aSopenharmony_ci                {
181299ca880aSopenharmony_ci                        char value[UTIL_PATH_SIZE];
181399ca880aSopenharmony_ci
181499ca880aSopenharmony_ci                        strscpy(value, sizeof(value), rules_str(rules, token->key.value_off));
181599ca880aSopenharmony_ci                        key_value = value;
181699ca880aSopenharmony_ci                        while (key_value != NULL) {
181799ca880aSopenharmony_ci                                pos = strchr(key_value, '|');
181899ca880aSopenharmony_ci                                if (pos != NULL) {
181999ca880aSopenharmony_ci                                        pos[0] = '\0';
182099ca880aSopenharmony_ci                                        pos = &pos[1];
182199ca880aSopenharmony_ci                                }
182299ca880aSopenharmony_ci                                match = (fnmatch(key_value, val, 0) == 0);
182399ca880aSopenharmony_ci                                if (match)
182499ca880aSopenharmony_ci                                        break;
182599ca880aSopenharmony_ci                                key_value = pos;
182699ca880aSopenharmony_ci                        }
182799ca880aSopenharmony_ci                        break;
182899ca880aSopenharmony_ci                }
182999ca880aSopenharmony_ci        case GL_SOMETHING:
183099ca880aSopenharmony_ci                match = (val[0] != '\0');
183199ca880aSopenharmony_ci                break;
183299ca880aSopenharmony_ci        case GL_UNSET:
183399ca880aSopenharmony_ci                return -1;
183499ca880aSopenharmony_ci        }
183599ca880aSopenharmony_ci
183699ca880aSopenharmony_ci        if (match && (token->key.op == OP_MATCH))
183799ca880aSopenharmony_ci                return 0;
183899ca880aSopenharmony_ci        if (!match && (token->key.op == OP_NOMATCH))
183999ca880aSopenharmony_ci                return 0;
184099ca880aSopenharmony_ci        return -1;
184199ca880aSopenharmony_ci}
184299ca880aSopenharmony_ci
184399ca880aSopenharmony_cistatic int match_attr(struct udev_rules *rules, struct udev_device *dev, struct udev_event *event, struct token *cur) {
184499ca880aSopenharmony_ci        const char *name;
184599ca880aSopenharmony_ci        char nbuf[UTIL_NAME_SIZE];
184699ca880aSopenharmony_ci        const char *value;
184799ca880aSopenharmony_ci        char vbuf[UTIL_NAME_SIZE];
184899ca880aSopenharmony_ci        size_t len;
184999ca880aSopenharmony_ci
185099ca880aSopenharmony_ci        name = rules_str(rules, cur->key.attr_off);
185199ca880aSopenharmony_ci        switch (cur->key.attrsubst) {
185299ca880aSopenharmony_ci        case SB_FORMAT:
185399ca880aSopenharmony_ci                udev_event_apply_format(event, name, nbuf, sizeof(nbuf), false);
185499ca880aSopenharmony_ci                name = nbuf;
185599ca880aSopenharmony_ci                /* fall through */
185699ca880aSopenharmony_ci        case SB_NONE:
185799ca880aSopenharmony_ci                value = udev_device_get_sysattr_value(dev, name);
185899ca880aSopenharmony_ci                if (value == NULL)
185999ca880aSopenharmony_ci                        return -1;
186099ca880aSopenharmony_ci                break;
186199ca880aSopenharmony_ci        case SB_SUBSYS:
186299ca880aSopenharmony_ci                if (util_resolve_subsys_kernel(event->udev, name, vbuf, sizeof(vbuf), 1) != 0)
186399ca880aSopenharmony_ci                        return -1;
186499ca880aSopenharmony_ci                value = vbuf;
186599ca880aSopenharmony_ci                break;
186699ca880aSopenharmony_ci        default:
186799ca880aSopenharmony_ci                return -1;
186899ca880aSopenharmony_ci        }
186999ca880aSopenharmony_ci
187099ca880aSopenharmony_ci        /* remove trailing whitespace, if not asked to match for it */
187199ca880aSopenharmony_ci        len = strlen(value);
187299ca880aSopenharmony_ci        if (len > 0 && isspace(value[len-1])) {
187399ca880aSopenharmony_ci                const char *key_value;
187499ca880aSopenharmony_ci                size_t klen;
187599ca880aSopenharmony_ci
187699ca880aSopenharmony_ci                key_value = rules_str(rules, cur->key.value_off);
187799ca880aSopenharmony_ci                klen = strlen(key_value);
187899ca880aSopenharmony_ci                if (klen > 0 && !isspace(key_value[klen-1])) {
187999ca880aSopenharmony_ci                        if (value != vbuf) {
188099ca880aSopenharmony_ci                                strscpy(vbuf, sizeof(vbuf), value);
188199ca880aSopenharmony_ci                                value = vbuf;
188299ca880aSopenharmony_ci                        }
188399ca880aSopenharmony_ci                        while (len > 0 && isspace(vbuf[--len]))
188499ca880aSopenharmony_ci                                vbuf[len] = '\0';
188599ca880aSopenharmony_ci                }
188699ca880aSopenharmony_ci        }
188799ca880aSopenharmony_ci
188899ca880aSopenharmony_ci        return match_key(rules, cur, value);
188999ca880aSopenharmony_ci}
189099ca880aSopenharmony_ci
189199ca880aSopenharmony_cienum escape_type {
189299ca880aSopenharmony_ci        ESCAPE_UNSET,
189399ca880aSopenharmony_ci        ESCAPE_NONE,
189499ca880aSopenharmony_ci        ESCAPE_REPLACE,
189599ca880aSopenharmony_ci};
189699ca880aSopenharmony_ci
189799ca880aSopenharmony_ciint udev_rules_apply_to_event(struct udev_rules *rules,
189899ca880aSopenharmony_ci                              struct udev_event *event,
189999ca880aSopenharmony_ci                              usec_t timeout_usec,
190099ca880aSopenharmony_ci                              usec_t timeout_warn_usec,
190199ca880aSopenharmony_ci                              struct udev_list *properties_list,
190299ca880aSopenharmony_ci                              const sigset_t *sigmask) {
190399ca880aSopenharmony_ci        struct token *cur;
190499ca880aSopenharmony_ci        struct token *rule;
190599ca880aSopenharmony_ci        enum escape_type esc = ESCAPE_UNSET;
190699ca880aSopenharmony_ci        bool can_set_name;
190799ca880aSopenharmony_ci
190899ca880aSopenharmony_ci        if (rules->tokens == NULL)
190999ca880aSopenharmony_ci                return -1;
191099ca880aSopenharmony_ci
191199ca880aSopenharmony_ci        can_set_name = ((!streq(udev_device_get_action(event->dev), "remove")) &&
191299ca880aSopenharmony_ci                        (major(udev_device_get_devnum(event->dev)) > 0 ||
191399ca880aSopenharmony_ci                         udev_device_get_ifindex(event->dev) > 0));
191499ca880aSopenharmony_ci
191599ca880aSopenharmony_ci        /* loop through token list, match, run actions or forward to next rule */
191699ca880aSopenharmony_ci        cur = &rules->tokens[0];
191799ca880aSopenharmony_ci        rule = cur;
191899ca880aSopenharmony_ci        for (;;) {
191999ca880aSopenharmony_ci                dump_token(rules, cur);
192099ca880aSopenharmony_ci                switch (cur->type) {
192199ca880aSopenharmony_ci                case TK_RULE:
192299ca880aSopenharmony_ci                        /* current rule */
192399ca880aSopenharmony_ci                        rule = cur;
192499ca880aSopenharmony_ci                        /* possibly skip rules which want to set NAME, SYMLINK, OWNER, GROUP, MODE */
192599ca880aSopenharmony_ci                        if (!can_set_name && rule->rule.can_set_name)
192699ca880aSopenharmony_ci                                goto nomatch;
192799ca880aSopenharmony_ci                        esc = ESCAPE_UNSET;
192899ca880aSopenharmony_ci                        break;
192999ca880aSopenharmony_ci                case TK_M_ACTION:
193099ca880aSopenharmony_ci                        if (match_key(rules, cur, udev_device_get_action(event->dev)) != 0)
193199ca880aSopenharmony_ci                                goto nomatch;
193299ca880aSopenharmony_ci                        break;
193399ca880aSopenharmony_ci                case TK_M_DEVPATH:
193499ca880aSopenharmony_ci                        if (match_key(rules, cur, udev_device_get_devpath(event->dev)) != 0)
193599ca880aSopenharmony_ci                                goto nomatch;
193699ca880aSopenharmony_ci                        break;
193799ca880aSopenharmony_ci                case TK_M_KERNEL:
193899ca880aSopenharmony_ci                        if (match_key(rules, cur, udev_device_get_sysname(event->dev)) != 0)
193999ca880aSopenharmony_ci                                goto nomatch;
194099ca880aSopenharmony_ci                        break;
194199ca880aSopenharmony_ci                case TK_M_DEVLINK: {
194299ca880aSopenharmony_ci                        struct udev_list_entry *list_entry;
194399ca880aSopenharmony_ci                        bool match = false;
194499ca880aSopenharmony_ci
194599ca880aSopenharmony_ci                        udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(event->dev)) {
194699ca880aSopenharmony_ci                                const char *devlink;
194799ca880aSopenharmony_ci
194899ca880aSopenharmony_ci                                devlink =  udev_list_entry_get_name(list_entry) + strlen("/dev/");
194999ca880aSopenharmony_ci                                if (match_key(rules, cur, devlink) == 0) {
195099ca880aSopenharmony_ci                                        match = true;
195199ca880aSopenharmony_ci                                        break;
195299ca880aSopenharmony_ci                                }
195399ca880aSopenharmony_ci                        }
195499ca880aSopenharmony_ci                        if (!match)
195599ca880aSopenharmony_ci                                goto nomatch;
195699ca880aSopenharmony_ci                        break;
195799ca880aSopenharmony_ci                }
195899ca880aSopenharmony_ci                case TK_M_NAME:
195999ca880aSopenharmony_ci                        if (match_key(rules, cur, event->name) != 0)
196099ca880aSopenharmony_ci                                goto nomatch;
196199ca880aSopenharmony_ci                        break;
196299ca880aSopenharmony_ci                case TK_M_ENV: {
196399ca880aSopenharmony_ci                        const char *key_name = rules_str(rules, cur->key.attr_off);
196499ca880aSopenharmony_ci                        const char *value;
196599ca880aSopenharmony_ci
196699ca880aSopenharmony_ci                        value = udev_device_get_property_value(event->dev, key_name);
196799ca880aSopenharmony_ci
196899ca880aSopenharmony_ci                        /* check global properties */
196999ca880aSopenharmony_ci                        if (!value && properties_list) {
197099ca880aSopenharmony_ci                                struct udev_list_entry *list_entry;
197199ca880aSopenharmony_ci
197299ca880aSopenharmony_ci                                list_entry = udev_list_get_entry(properties_list);
197399ca880aSopenharmony_ci                                list_entry = udev_list_entry_get_by_name(list_entry, key_name);
197499ca880aSopenharmony_ci                                if (list_entry != NULL)
197599ca880aSopenharmony_ci                                        value = udev_list_entry_get_value(list_entry);
197699ca880aSopenharmony_ci                        }
197799ca880aSopenharmony_ci
197899ca880aSopenharmony_ci                        if (!value)
197999ca880aSopenharmony_ci                                value = "";
198099ca880aSopenharmony_ci                        if (match_key(rules, cur, value))
198199ca880aSopenharmony_ci                                goto nomatch;
198299ca880aSopenharmony_ci                        break;
198399ca880aSopenharmony_ci                }
198499ca880aSopenharmony_ci                case TK_M_TAG: {
198599ca880aSopenharmony_ci                        struct udev_list_entry *list_entry;
198699ca880aSopenharmony_ci                        bool match = false;
198799ca880aSopenharmony_ci
198899ca880aSopenharmony_ci                        udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(event->dev)) {
198999ca880aSopenharmony_ci                                if (streq(rules_str(rules, cur->key.value_off), udev_list_entry_get_name(list_entry))) {
199099ca880aSopenharmony_ci                                        match = true;
199199ca880aSopenharmony_ci                                        break;
199299ca880aSopenharmony_ci                                }
199399ca880aSopenharmony_ci                        }
199499ca880aSopenharmony_ci                        if (!match && (cur->key.op != OP_NOMATCH))
199599ca880aSopenharmony_ci                                goto nomatch;
199699ca880aSopenharmony_ci                        break;
199799ca880aSopenharmony_ci                }
199899ca880aSopenharmony_ci                case TK_M_SUBSYSTEM:
199999ca880aSopenharmony_ci                        if (match_key(rules, cur, udev_device_get_subsystem(event->dev)) != 0)
200099ca880aSopenharmony_ci                                goto nomatch;
200199ca880aSopenharmony_ci                        break;
200299ca880aSopenharmony_ci                case TK_M_DRIVER:
200399ca880aSopenharmony_ci                        if (match_key(rules, cur, udev_device_get_driver(event->dev)) != 0)
200499ca880aSopenharmony_ci                                goto nomatch;
200599ca880aSopenharmony_ci                        break;
200699ca880aSopenharmony_ci                case TK_M_WAITFOR: {
200799ca880aSopenharmony_ci                        char filename[UTIL_PATH_SIZE];
200899ca880aSopenharmony_ci                        int found;
200999ca880aSopenharmony_ci
201099ca880aSopenharmony_ci                        udev_event_apply_format(event, rules_str(rules, cur->key.value_off), filename, sizeof(filename), false);
201199ca880aSopenharmony_ci                        found = (wait_for_file(event->dev, filename, 10) == 0);
201299ca880aSopenharmony_ci                        if (!found && (cur->key.op != OP_NOMATCH))
201399ca880aSopenharmony_ci                                goto nomatch;
201499ca880aSopenharmony_ci                        break;
201599ca880aSopenharmony_ci                }
201699ca880aSopenharmony_ci                case TK_M_ATTR:
201799ca880aSopenharmony_ci                        if (match_attr(rules, event->dev, event, cur) != 0)
201899ca880aSopenharmony_ci                                goto nomatch;
201999ca880aSopenharmony_ci                        break;
202099ca880aSopenharmony_ci                case TK_M_SYSCTL: {
202199ca880aSopenharmony_ci                        char filename[UTIL_PATH_SIZE];
202299ca880aSopenharmony_ci                        _cleanup_free_ char *value = NULL;
202399ca880aSopenharmony_ci                        size_t len;
202499ca880aSopenharmony_ci
202599ca880aSopenharmony_ci                        udev_event_apply_format(event, rules_str(rules, cur->key.attr_off), filename, sizeof(filename), false);
202699ca880aSopenharmony_ci                        sysctl_normalize(filename);
202799ca880aSopenharmony_ci                        if (sysctl_read(filename, &value) < 0)
202899ca880aSopenharmony_ci                                goto nomatch;
202999ca880aSopenharmony_ci
203099ca880aSopenharmony_ci                        len = strlen(value);
203199ca880aSopenharmony_ci                        while (len > 0 && isspace(value[--len]))
203299ca880aSopenharmony_ci                                value[len] = '\0';
203399ca880aSopenharmony_ci                        if (match_key(rules, cur, value) != 0)
203499ca880aSopenharmony_ci                                goto nomatch;
203599ca880aSopenharmony_ci                        break;
203699ca880aSopenharmony_ci                }
203799ca880aSopenharmony_ci                case TK_M_KERNELS:
203899ca880aSopenharmony_ci                case TK_M_SUBSYSTEMS:
203999ca880aSopenharmony_ci                case TK_M_DRIVERS:
204099ca880aSopenharmony_ci                case TK_M_ATTRS:
204199ca880aSopenharmony_ci                case TK_M_TAGS: {
204299ca880aSopenharmony_ci                        struct token *next;
204399ca880aSopenharmony_ci
204499ca880aSopenharmony_ci                        /* get whole sequence of parent matches */
204599ca880aSopenharmony_ci                        next = cur;
204699ca880aSopenharmony_ci                        while (next->type > TK_M_PARENTS_MIN && next->type < TK_M_PARENTS_MAX)
204799ca880aSopenharmony_ci                                next++;
204899ca880aSopenharmony_ci
204999ca880aSopenharmony_ci                        /* loop over parents */
205099ca880aSopenharmony_ci                        event->dev_parent = event->dev;
205199ca880aSopenharmony_ci                        for (;;) {
205299ca880aSopenharmony_ci                                struct token *key;
205399ca880aSopenharmony_ci
205499ca880aSopenharmony_ci                                /* loop over sequence of parent match keys */
205599ca880aSopenharmony_ci                                for (key = cur; key < next; key++ ) {
205699ca880aSopenharmony_ci                                        dump_token(rules, key);
205799ca880aSopenharmony_ci                                        switch(key->type) {
205899ca880aSopenharmony_ci                                        case TK_M_KERNELS:
205999ca880aSopenharmony_ci                                                if (match_key(rules, key, udev_device_get_sysname(event->dev_parent)) != 0)
206099ca880aSopenharmony_ci                                                        goto try_parent;
206199ca880aSopenharmony_ci                                                break;
206299ca880aSopenharmony_ci                                        case TK_M_SUBSYSTEMS:
206399ca880aSopenharmony_ci                                                if (match_key(rules, key, udev_device_get_subsystem(event->dev_parent)) != 0)
206499ca880aSopenharmony_ci                                                        goto try_parent;
206599ca880aSopenharmony_ci                                                break;
206699ca880aSopenharmony_ci                                        case TK_M_DRIVERS:
206799ca880aSopenharmony_ci                                                if (match_key(rules, key, udev_device_get_driver(event->dev_parent)) != 0)
206899ca880aSopenharmony_ci                                                        goto try_parent;
206999ca880aSopenharmony_ci                                                break;
207099ca880aSopenharmony_ci                                        case TK_M_ATTRS:
207199ca880aSopenharmony_ci                                                if (match_attr(rules, event->dev_parent, event, key) != 0)
207299ca880aSopenharmony_ci                                                        goto try_parent;
207399ca880aSopenharmony_ci                                                break;
207499ca880aSopenharmony_ci                                        case TK_M_TAGS: {
207599ca880aSopenharmony_ci                                                bool match = udev_device_has_tag(event->dev_parent, rules_str(rules, cur->key.value_off));
207699ca880aSopenharmony_ci
207799ca880aSopenharmony_ci                                                if (match && key->key.op == OP_NOMATCH)
207899ca880aSopenharmony_ci                                                        goto try_parent;
207999ca880aSopenharmony_ci                                                if (!match && key->key.op == OP_MATCH)
208099ca880aSopenharmony_ci                                                        goto try_parent;
208199ca880aSopenharmony_ci                                                break;
208299ca880aSopenharmony_ci                                        }
208399ca880aSopenharmony_ci                                        default:
208499ca880aSopenharmony_ci                                                goto nomatch;
208599ca880aSopenharmony_ci                                        }
208699ca880aSopenharmony_ci                                }
208799ca880aSopenharmony_ci                                break;
208899ca880aSopenharmony_ci
208999ca880aSopenharmony_ci                        try_parent:
209099ca880aSopenharmony_ci                                event->dev_parent = udev_device_get_parent(event->dev_parent);
209199ca880aSopenharmony_ci                                if (event->dev_parent == NULL)
209299ca880aSopenharmony_ci                                        goto nomatch;
209399ca880aSopenharmony_ci                        }
209499ca880aSopenharmony_ci                        /* move behind our sequence of parent match keys */
209599ca880aSopenharmony_ci                        cur = next;
209699ca880aSopenharmony_ci                        continue;
209799ca880aSopenharmony_ci                }
209899ca880aSopenharmony_ci                case TK_M_TEST: {
209999ca880aSopenharmony_ci                        char filename[UTIL_PATH_SIZE];
210099ca880aSopenharmony_ci                        struct stat statbuf;
210199ca880aSopenharmony_ci                        int match;
210299ca880aSopenharmony_ci
210399ca880aSopenharmony_ci                        udev_event_apply_format(event, rules_str(rules, cur->key.value_off), filename, sizeof(filename), false);
210499ca880aSopenharmony_ci                        if (util_resolve_subsys_kernel(event->udev, filename, filename, sizeof(filename), 0) != 0) {
210599ca880aSopenharmony_ci                                if (filename[0] != '/') {
210699ca880aSopenharmony_ci                                        char tmp[UTIL_PATH_SIZE];
210799ca880aSopenharmony_ci
210899ca880aSopenharmony_ci                                        strscpy(tmp, sizeof(tmp), filename);
210999ca880aSopenharmony_ci                                        strscpyl(filename, sizeof(filename),
211099ca880aSopenharmony_ci                                                      udev_device_get_syspath(event->dev), "/", tmp, NULL);
211199ca880aSopenharmony_ci                                }
211299ca880aSopenharmony_ci                        }
211399ca880aSopenharmony_ci                        attr_subst_subdir(filename, sizeof(filename));
211499ca880aSopenharmony_ci
211599ca880aSopenharmony_ci                        match = (stat(filename, &statbuf) == 0);
211699ca880aSopenharmony_ci                        if (match && cur->key.mode > 0)
211799ca880aSopenharmony_ci                                match = ((statbuf.st_mode & cur->key.mode) > 0);
211899ca880aSopenharmony_ci                        if (match && cur->key.op == OP_NOMATCH)
211999ca880aSopenharmony_ci                                goto nomatch;
212099ca880aSopenharmony_ci                        if (!match && cur->key.op == OP_MATCH)
212199ca880aSopenharmony_ci                                goto nomatch;
212299ca880aSopenharmony_ci                        break;
212399ca880aSopenharmony_ci                }
212499ca880aSopenharmony_ci                case TK_M_PROGRAM: {
212599ca880aSopenharmony_ci                        char program[UTIL_PATH_SIZE];
212699ca880aSopenharmony_ci                        char **envp;
212799ca880aSopenharmony_ci                        char result[UTIL_LINE_SIZE];
212899ca880aSopenharmony_ci
212999ca880aSopenharmony_ci                        free(event->program_result);
213099ca880aSopenharmony_ci                        event->program_result = NULL;
213199ca880aSopenharmony_ci                        udev_event_apply_format(event, rules_str(rules, cur->key.value_off), program, sizeof(program), false);
213299ca880aSopenharmony_ci                        envp = udev_device_get_properties_envp(event->dev);
213399ca880aSopenharmony_ci                        log_debug("PROGRAM '%s' %s:%u",
213499ca880aSopenharmony_ci                                  program,
213599ca880aSopenharmony_ci                                  rules_str(rules, rule->rule.filename_off),
213699ca880aSopenharmony_ci                                  rule->rule.filename_line);
213799ca880aSopenharmony_ci
213899ca880aSopenharmony_ci                        if (udev_event_spawn(event, timeout_usec, timeout_warn_usec, program, envp, sigmask, result, sizeof(result)) < 0) {
213999ca880aSopenharmony_ci                                if (cur->key.op != OP_NOMATCH)
214099ca880aSopenharmony_ci                                        goto nomatch;
214199ca880aSopenharmony_ci                        } else {
214299ca880aSopenharmony_ci                                int count;
214399ca880aSopenharmony_ci
214499ca880aSopenharmony_ci                                util_remove_trailing_chars(result, '\n');
214599ca880aSopenharmony_ci                                if (esc == ESCAPE_UNSET || esc == ESCAPE_REPLACE) {
214699ca880aSopenharmony_ci                                        count = util_replace_chars(result, UDEV_ALLOWED_CHARS_INPUT);
214799ca880aSopenharmony_ci                                        if (count > 0)
214899ca880aSopenharmony_ci                                                log_debug("%i character(s) replaced" , count);
214999ca880aSopenharmony_ci                                }
215099ca880aSopenharmony_ci                                event->program_result = strdup(result);
215199ca880aSopenharmony_ci                                if (cur->key.op == OP_NOMATCH)
215299ca880aSopenharmony_ci                                        goto nomatch;
215399ca880aSopenharmony_ci                        }
215499ca880aSopenharmony_ci                        break;
215599ca880aSopenharmony_ci                }
215699ca880aSopenharmony_ci                case TK_M_IMPORT_FILE: {
215799ca880aSopenharmony_ci                        char import[UTIL_PATH_SIZE];
215899ca880aSopenharmony_ci
215999ca880aSopenharmony_ci                        udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import), false);
216099ca880aSopenharmony_ci                        if (import_file_into_properties(event->dev, import) != 0)
216199ca880aSopenharmony_ci                                if (cur->key.op != OP_NOMATCH)
216299ca880aSopenharmony_ci                                        goto nomatch;
216399ca880aSopenharmony_ci                        break;
216499ca880aSopenharmony_ci                }
216599ca880aSopenharmony_ci                case TK_M_IMPORT_PROG: {
216699ca880aSopenharmony_ci                        char import[UTIL_PATH_SIZE];
216799ca880aSopenharmony_ci
216899ca880aSopenharmony_ci                        udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import), false);
216999ca880aSopenharmony_ci                        log_debug("IMPORT '%s' %s:%u",
217099ca880aSopenharmony_ci                                  import,
217199ca880aSopenharmony_ci                                  rules_str(rules, rule->rule.filename_off),
217299ca880aSopenharmony_ci                                  rule->rule.filename_line);
217399ca880aSopenharmony_ci
217499ca880aSopenharmony_ci                        if (import_program_into_properties(event, timeout_usec, timeout_warn_usec, import, sigmask) != 0)
217599ca880aSopenharmony_ci                                if (cur->key.op != OP_NOMATCH)
217699ca880aSopenharmony_ci                                        goto nomatch;
217799ca880aSopenharmony_ci                        break;
217899ca880aSopenharmony_ci                }
217999ca880aSopenharmony_ci                case TK_M_IMPORT_BUILTIN: {
218099ca880aSopenharmony_ci                        char command[UTIL_PATH_SIZE];
218199ca880aSopenharmony_ci
218299ca880aSopenharmony_ci                        if (udev_builtin_run_once(cur->key.builtin_cmd)) {
218399ca880aSopenharmony_ci                                /* check if we ran already */
218499ca880aSopenharmony_ci                                if (event->builtin_run & (1 << cur->key.builtin_cmd)) {
218599ca880aSopenharmony_ci                                        log_debug("IMPORT builtin skip '%s' %s:%u",
218699ca880aSopenharmony_ci                                                  udev_builtin_name(cur->key.builtin_cmd),
218799ca880aSopenharmony_ci                                                  rules_str(rules, rule->rule.filename_off),
218899ca880aSopenharmony_ci                                                  rule->rule.filename_line);
218999ca880aSopenharmony_ci                                        /* return the result from earlier run */
219099ca880aSopenharmony_ci                                        if (event->builtin_ret & (1 << cur->key.builtin_cmd))
219199ca880aSopenharmony_ci                                        if (cur->key.op != OP_NOMATCH)
219299ca880aSopenharmony_ci                                                        goto nomatch;
219399ca880aSopenharmony_ci                                        break;
219499ca880aSopenharmony_ci                                }
219599ca880aSopenharmony_ci                                /* mark as ran */
219699ca880aSopenharmony_ci                                event->builtin_run |= (1 << cur->key.builtin_cmd);
219799ca880aSopenharmony_ci                        }
219899ca880aSopenharmony_ci
219999ca880aSopenharmony_ci                        udev_event_apply_format(event, rules_str(rules, cur->key.value_off), command, sizeof(command), false);
220099ca880aSopenharmony_ci                        log_debug("IMPORT builtin '%s' %s:%u",
220199ca880aSopenharmony_ci                                  udev_builtin_name(cur->key.builtin_cmd),
220299ca880aSopenharmony_ci                                  rules_str(rules, rule->rule.filename_off),
220399ca880aSopenharmony_ci                                  rule->rule.filename_line);
220499ca880aSopenharmony_ci
220599ca880aSopenharmony_ci                        if (udev_builtin_run(event->dev, cur->key.builtin_cmd, command, false) != 0) {
220699ca880aSopenharmony_ci                                /* remember failure */
220799ca880aSopenharmony_ci                                log_debug("IMPORT builtin '%s' returned non-zero",
220899ca880aSopenharmony_ci                                          udev_builtin_name(cur->key.builtin_cmd));
220999ca880aSopenharmony_ci                                event->builtin_ret |= (1 << cur->key.builtin_cmd);
221099ca880aSopenharmony_ci                                if (cur->key.op != OP_NOMATCH)
221199ca880aSopenharmony_ci                                        goto nomatch;
221299ca880aSopenharmony_ci                        }
221399ca880aSopenharmony_ci                        break;
221499ca880aSopenharmony_ci                }
221599ca880aSopenharmony_ci                case TK_M_IMPORT_DB: {
221699ca880aSopenharmony_ci                        const char *key = rules_str(rules, cur->key.value_off);
221799ca880aSopenharmony_ci                        const char *value;
221899ca880aSopenharmony_ci
221999ca880aSopenharmony_ci                        value = udev_device_get_property_value(event->dev_db, key);
222099ca880aSopenharmony_ci                        if (value != NULL)
222199ca880aSopenharmony_ci                                udev_device_add_property(event->dev, key, value);
222299ca880aSopenharmony_ci                        else {
222399ca880aSopenharmony_ci                                if (cur->key.op != OP_NOMATCH)
222499ca880aSopenharmony_ci                                        goto nomatch;
222599ca880aSopenharmony_ci                        }
222699ca880aSopenharmony_ci                        break;
222799ca880aSopenharmony_ci                }
222899ca880aSopenharmony_ci                case TK_M_IMPORT_CMDLINE: {
222999ca880aSopenharmony_ci                        FILE *f;
223099ca880aSopenharmony_ci                        bool imported = false;
223199ca880aSopenharmony_ci
223299ca880aSopenharmony_ci                        f = fopen("/proc/cmdline", "re");
223399ca880aSopenharmony_ci                        if (f != NULL) {
223499ca880aSopenharmony_ci                                char cmdline[4096];
223599ca880aSopenharmony_ci
223699ca880aSopenharmony_ci                                if (fgets(cmdline, sizeof(cmdline), f) != NULL) {
223799ca880aSopenharmony_ci                                        const char *key = rules_str(rules, cur->key.value_off);
223899ca880aSopenharmony_ci                                        char *pos;
223999ca880aSopenharmony_ci
224099ca880aSopenharmony_ci                                        pos = strstr(cmdline, key);
224199ca880aSopenharmony_ci                                        if (pos != NULL) {
224299ca880aSopenharmony_ci                                                pos += strlen(key);
224399ca880aSopenharmony_ci                                                if (pos[0] == '\0' || isspace(pos[0])) {
224499ca880aSopenharmony_ci                                                        /* we import simple flags as 'FLAG=1' */
224599ca880aSopenharmony_ci                                                        udev_device_add_property(event->dev, key, "1");
224699ca880aSopenharmony_ci                                                        imported = true;
224799ca880aSopenharmony_ci                                                } else if (pos[0] == '=') {
224899ca880aSopenharmony_ci                                                        const char *value;
224999ca880aSopenharmony_ci
225099ca880aSopenharmony_ci                                                        pos++;
225199ca880aSopenharmony_ci                                                        value = pos;
225299ca880aSopenharmony_ci                                                        while (pos[0] != '\0' && !isspace(pos[0]))
225399ca880aSopenharmony_ci                                                                pos++;
225499ca880aSopenharmony_ci                                                        pos[0] = '\0';
225599ca880aSopenharmony_ci                                                        udev_device_add_property(event->dev, key, value);
225699ca880aSopenharmony_ci                                                        imported = true;
225799ca880aSopenharmony_ci                                                }
225899ca880aSopenharmony_ci                                        }
225999ca880aSopenharmony_ci                                }
226099ca880aSopenharmony_ci                                fclose(f);
226199ca880aSopenharmony_ci                        }
226299ca880aSopenharmony_ci                        if (!imported && cur->key.op != OP_NOMATCH)
226399ca880aSopenharmony_ci                                goto nomatch;
226499ca880aSopenharmony_ci                        break;
226599ca880aSopenharmony_ci                }
226699ca880aSopenharmony_ci                case TK_M_IMPORT_PARENT: {
226799ca880aSopenharmony_ci                        char import[UTIL_PATH_SIZE];
226899ca880aSopenharmony_ci
226999ca880aSopenharmony_ci                        udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import), false);
227099ca880aSopenharmony_ci                        if (import_parent_into_properties(event->dev, import) != 0)
227199ca880aSopenharmony_ci                                if (cur->key.op != OP_NOMATCH)
227299ca880aSopenharmony_ci                                        goto nomatch;
227399ca880aSopenharmony_ci                        break;
227499ca880aSopenharmony_ci                }
227599ca880aSopenharmony_ci                case TK_M_RESULT:
227699ca880aSopenharmony_ci                        if (match_key(rules, cur, event->program_result) != 0)
227799ca880aSopenharmony_ci                                goto nomatch;
227899ca880aSopenharmony_ci                        break;
227999ca880aSopenharmony_ci                case TK_A_STRING_ESCAPE_NONE:
228099ca880aSopenharmony_ci                        esc = ESCAPE_NONE;
228199ca880aSopenharmony_ci                        break;
228299ca880aSopenharmony_ci                case TK_A_STRING_ESCAPE_REPLACE:
228399ca880aSopenharmony_ci                        esc = ESCAPE_REPLACE;
228499ca880aSopenharmony_ci                        break;
228599ca880aSopenharmony_ci                case TK_A_DB_PERSIST:
228699ca880aSopenharmony_ci                        udev_device_set_db_persist(event->dev);
228799ca880aSopenharmony_ci                        break;
228899ca880aSopenharmony_ci                case TK_A_INOTIFY_WATCH:
228999ca880aSopenharmony_ci                        if (event->inotify_watch_final)
229099ca880aSopenharmony_ci                                break;
229199ca880aSopenharmony_ci                        if (cur->key.op == OP_ASSIGN_FINAL)
229299ca880aSopenharmony_ci                                event->inotify_watch_final = true;
229399ca880aSopenharmony_ci                        event->inotify_watch = cur->key.watch;
229499ca880aSopenharmony_ci                        break;
229599ca880aSopenharmony_ci                case TK_A_DEVLINK_PRIO:
229699ca880aSopenharmony_ci                        udev_device_set_devlink_priority(event->dev, cur->key.devlink_prio);
229799ca880aSopenharmony_ci                        break;
229899ca880aSopenharmony_ci                case TK_A_OWNER: {
229999ca880aSopenharmony_ci                        char owner[UTIL_NAME_SIZE];
230099ca880aSopenharmony_ci                        const char *ow = owner;
230199ca880aSopenharmony_ci                        int r;
230299ca880aSopenharmony_ci
230399ca880aSopenharmony_ci                        if (event->owner_final)
230499ca880aSopenharmony_ci                                break;
230599ca880aSopenharmony_ci                        if (cur->key.op == OP_ASSIGN_FINAL)
230699ca880aSopenharmony_ci                                event->owner_final = true;
230799ca880aSopenharmony_ci                        udev_event_apply_format(event, rules_str(rules, cur->key.value_off), owner, sizeof(owner), false);
230899ca880aSopenharmony_ci                        event->owner_set = true;
230999ca880aSopenharmony_ci                        r = get_user_creds(&ow, &event->uid, NULL, NULL, NULL);
231099ca880aSopenharmony_ci                        if (r < 0) {
231199ca880aSopenharmony_ci                                if (r == -ENOENT || r == -ESRCH)
231299ca880aSopenharmony_ci                                        log_error("specified user '%s' unknown", owner);
231399ca880aSopenharmony_ci                                else
231499ca880aSopenharmony_ci                                        log_error_errno(r, "error resolving user '%s': %m", owner);
231599ca880aSopenharmony_ci
231699ca880aSopenharmony_ci                                event->uid = 0;
231799ca880aSopenharmony_ci                        }
231899ca880aSopenharmony_ci                        log_debug("OWNER %u %s:%u",
231999ca880aSopenharmony_ci                                  event->uid,
232099ca880aSopenharmony_ci                                  rules_str(rules, rule->rule.filename_off),
232199ca880aSopenharmony_ci                                  rule->rule.filename_line);
232299ca880aSopenharmony_ci                        break;
232399ca880aSopenharmony_ci                }
232499ca880aSopenharmony_ci                case TK_A_GROUP: {
232599ca880aSopenharmony_ci                        char group[UTIL_NAME_SIZE];
232699ca880aSopenharmony_ci                        const char *gr = group;
232799ca880aSopenharmony_ci                        int r;
232899ca880aSopenharmony_ci
232999ca880aSopenharmony_ci                        if (event->group_final)
233099ca880aSopenharmony_ci                                break;
233199ca880aSopenharmony_ci                        if (cur->key.op == OP_ASSIGN_FINAL)
233299ca880aSopenharmony_ci                                event->group_final = true;
233399ca880aSopenharmony_ci                        udev_event_apply_format(event, rules_str(rules, cur->key.value_off), group, sizeof(group), false);
233499ca880aSopenharmony_ci                        event->group_set = true;
233599ca880aSopenharmony_ci                        r = get_group_creds(&gr, &event->gid);
233699ca880aSopenharmony_ci                        if (r < 0) {
233799ca880aSopenharmony_ci                                if (r == -ENOENT || r == -ESRCH)
233899ca880aSopenharmony_ci                                        log_error("specified group '%s' unknown", group);
233999ca880aSopenharmony_ci                                else
234099ca880aSopenharmony_ci                                        log_error_errno(r, "error resolving group '%s': %m", group);
234199ca880aSopenharmony_ci
234299ca880aSopenharmony_ci                                event->gid = 0;
234399ca880aSopenharmony_ci                        }
234499ca880aSopenharmony_ci                        log_debug("GROUP %u %s:%u",
234599ca880aSopenharmony_ci                                  event->gid,
234699ca880aSopenharmony_ci                                  rules_str(rules, rule->rule.filename_off),
234799ca880aSopenharmony_ci                                  rule->rule.filename_line);
234899ca880aSopenharmony_ci                        break;
234999ca880aSopenharmony_ci                }
235099ca880aSopenharmony_ci                case TK_A_MODE: {
235199ca880aSopenharmony_ci                        char mode_str[UTIL_NAME_SIZE];
235299ca880aSopenharmony_ci                        mode_t mode;
235399ca880aSopenharmony_ci                        char *endptr;
235499ca880aSopenharmony_ci
235599ca880aSopenharmony_ci                        if (event->mode_final)
235699ca880aSopenharmony_ci                                break;
235799ca880aSopenharmony_ci                        udev_event_apply_format(event, rules_str(rules, cur->key.value_off), mode_str, sizeof(mode_str), false);
235899ca880aSopenharmony_ci                        mode = strtol(mode_str, &endptr, 8);
235999ca880aSopenharmony_ci                        if (endptr[0] != '\0') {
236099ca880aSopenharmony_ci                                log_error("ignoring invalid mode '%s'", mode_str);
236199ca880aSopenharmony_ci                                break;
236299ca880aSopenharmony_ci                        }
236399ca880aSopenharmony_ci                        if (cur->key.op == OP_ASSIGN_FINAL)
236499ca880aSopenharmony_ci                                event->mode_final = true;
236599ca880aSopenharmony_ci                        event->mode_set = true;
236699ca880aSopenharmony_ci                        event->mode = mode;
236799ca880aSopenharmony_ci                        log_debug("MODE %#o %s:%u",
236899ca880aSopenharmony_ci                                  event->mode,
236999ca880aSopenharmony_ci                                  rules_str(rules, rule->rule.filename_off),
237099ca880aSopenharmony_ci                                  rule->rule.filename_line);
237199ca880aSopenharmony_ci                        break;
237299ca880aSopenharmony_ci                }
237399ca880aSopenharmony_ci                case TK_A_OWNER_ID:
237499ca880aSopenharmony_ci                        if (event->owner_final)
237599ca880aSopenharmony_ci                                break;
237699ca880aSopenharmony_ci                        if (cur->key.op == OP_ASSIGN_FINAL)
237799ca880aSopenharmony_ci                                event->owner_final = true;
237899ca880aSopenharmony_ci                        event->owner_set = true;
237999ca880aSopenharmony_ci                        event->uid = cur->key.uid;
238099ca880aSopenharmony_ci                        log_debug("OWNER %u %s:%u",
238199ca880aSopenharmony_ci                                  event->uid,
238299ca880aSopenharmony_ci                                  rules_str(rules, rule->rule.filename_off),
238399ca880aSopenharmony_ci                                  rule->rule.filename_line);
238499ca880aSopenharmony_ci                        break;
238599ca880aSopenharmony_ci                case TK_A_GROUP_ID:
238699ca880aSopenharmony_ci                        if (event->group_final)
238799ca880aSopenharmony_ci                                break;
238899ca880aSopenharmony_ci                        if (cur->key.op == OP_ASSIGN_FINAL)
238999ca880aSopenharmony_ci                                event->group_final = true;
239099ca880aSopenharmony_ci                        event->group_set = true;
239199ca880aSopenharmony_ci                        event->gid = cur->key.gid;
239299ca880aSopenharmony_ci                        log_debug("GROUP %u %s:%u",
239399ca880aSopenharmony_ci                                  event->gid,
239499ca880aSopenharmony_ci                                  rules_str(rules, rule->rule.filename_off),
239599ca880aSopenharmony_ci                                  rule->rule.filename_line);
239699ca880aSopenharmony_ci                        break;
239799ca880aSopenharmony_ci                case TK_A_MODE_ID:
239899ca880aSopenharmony_ci                        if (event->mode_final)
239999ca880aSopenharmony_ci                                break;
240099ca880aSopenharmony_ci                        if (cur->key.op == OP_ASSIGN_FINAL)
240199ca880aSopenharmony_ci                                event->mode_final = true;
240299ca880aSopenharmony_ci                        event->mode_set = true;
240399ca880aSopenharmony_ci                        event->mode = cur->key.mode;
240499ca880aSopenharmony_ci                        log_debug("MODE %#o %s:%u",
240599ca880aSopenharmony_ci                                  event->mode,
240699ca880aSopenharmony_ci                                  rules_str(rules, rule->rule.filename_off),
240799ca880aSopenharmony_ci                                  rule->rule.filename_line);
240899ca880aSopenharmony_ci                        break;
240999ca880aSopenharmony_ci                case TK_A_SECLABEL: {
241099ca880aSopenharmony_ci                        const char *name, *label;
241199ca880aSopenharmony_ci
241299ca880aSopenharmony_ci                        name = rules_str(rules, cur->key.attr_off);
241399ca880aSopenharmony_ci                        label = rules_str(rules, cur->key.value_off);
241499ca880aSopenharmony_ci                        if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL)
241599ca880aSopenharmony_ci                                udev_list_cleanup(&event->seclabel_list);
241699ca880aSopenharmony_ci                        udev_list_entry_add(&event->seclabel_list, name, label);
241799ca880aSopenharmony_ci                        log_debug("SECLABEL{%s}='%s' %s:%u",
241899ca880aSopenharmony_ci                                  name, label,
241999ca880aSopenharmony_ci                                  rules_str(rules, rule->rule.filename_off),
242099ca880aSopenharmony_ci                                  rule->rule.filename_line);
242199ca880aSopenharmony_ci                        break;
242299ca880aSopenharmony_ci                }
242399ca880aSopenharmony_ci                case TK_A_ENV: {
242499ca880aSopenharmony_ci                        const char *name = rules_str(rules, cur->key.attr_off);
242599ca880aSopenharmony_ci                        char *value = rules_str(rules, cur->key.value_off);
242699ca880aSopenharmony_ci                        char value_new[UTIL_NAME_SIZE];
242799ca880aSopenharmony_ci                        const char *value_old = NULL;
242899ca880aSopenharmony_ci
242999ca880aSopenharmony_ci                        if (value[0] == '\0') {
243099ca880aSopenharmony_ci                                if (cur->key.op == OP_ADD)
243199ca880aSopenharmony_ci                                        break;
243299ca880aSopenharmony_ci                                udev_device_add_property(event->dev, name, NULL);
243399ca880aSopenharmony_ci                                break;
243499ca880aSopenharmony_ci                        }
243599ca880aSopenharmony_ci
243699ca880aSopenharmony_ci                        if (cur->key.op == OP_ADD)
243799ca880aSopenharmony_ci                                value_old = udev_device_get_property_value(event->dev, name);
243899ca880aSopenharmony_ci                        if (value_old) {
243999ca880aSopenharmony_ci                                char temp[UTIL_NAME_SIZE];
244099ca880aSopenharmony_ci
244199ca880aSopenharmony_ci                                /* append value separated by space */
244299ca880aSopenharmony_ci                                udev_event_apply_format(event, value, temp, sizeof(temp), false);
244399ca880aSopenharmony_ci                                strscpyl(value_new, sizeof(value_new), value_old, " ", temp, NULL);
244499ca880aSopenharmony_ci                        } else
244599ca880aSopenharmony_ci                                udev_event_apply_format(event, value, value_new, sizeof(value_new), false);
244699ca880aSopenharmony_ci
244799ca880aSopenharmony_ci                        udev_device_add_property(event->dev, name, value_new);
244899ca880aSopenharmony_ci                        break;
244999ca880aSopenharmony_ci                }
245099ca880aSopenharmony_ci                case TK_A_TAG: {
245199ca880aSopenharmony_ci                        char tag[UTIL_PATH_SIZE];
245299ca880aSopenharmony_ci                        const char *p;
245399ca880aSopenharmony_ci
245499ca880aSopenharmony_ci                        udev_event_apply_format(event, rules_str(rules, cur->key.value_off), tag, sizeof(tag), false);
245599ca880aSopenharmony_ci                        if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL)
245699ca880aSopenharmony_ci                                udev_device_cleanup_tags_list(event->dev);
245799ca880aSopenharmony_ci                        for (p = tag; *p != '\0'; p++) {
245899ca880aSopenharmony_ci                                if ((*p >= 'a' && *p <= 'z') ||
245999ca880aSopenharmony_ci                                    (*p >= 'A' && *p <= 'Z') ||
246099ca880aSopenharmony_ci                                    (*p >= '0' && *p <= '9') ||
246199ca880aSopenharmony_ci                                    *p == '-' || *p == '_')
246299ca880aSopenharmony_ci                                        continue;
246399ca880aSopenharmony_ci                                log_error("ignoring invalid tag name '%s'", tag);
246499ca880aSopenharmony_ci                                break;
246599ca880aSopenharmony_ci                        }
246699ca880aSopenharmony_ci                        if (cur->key.op == OP_REMOVE)
246799ca880aSopenharmony_ci                                udev_device_remove_tag(event->dev, tag);
246899ca880aSopenharmony_ci                        else
246999ca880aSopenharmony_ci                                udev_device_add_tag(event->dev, tag);
247099ca880aSopenharmony_ci                        break;
247199ca880aSopenharmony_ci                }
247299ca880aSopenharmony_ci                case TK_A_NAME: {
247399ca880aSopenharmony_ci                        const char *name  = rules_str(rules, cur->key.value_off);
247499ca880aSopenharmony_ci
247599ca880aSopenharmony_ci                        char name_str[UTIL_PATH_SIZE];
247699ca880aSopenharmony_ci                        int count;
247799ca880aSopenharmony_ci
247899ca880aSopenharmony_ci                        if (event->name_final)
247999ca880aSopenharmony_ci                                break;
248099ca880aSopenharmony_ci                        if (cur->key.op == OP_ASSIGN_FINAL)
248199ca880aSopenharmony_ci                                event->name_final = true;
248299ca880aSopenharmony_ci                        udev_event_apply_format(event, name, name_str, sizeof(name_str), false);
248399ca880aSopenharmony_ci                        if (esc == ESCAPE_UNSET || esc == ESCAPE_REPLACE) {
248499ca880aSopenharmony_ci                                count = util_replace_chars(name_str, "/");
248599ca880aSopenharmony_ci                                if (count > 0)
248699ca880aSopenharmony_ci                                        log_debug("%i character(s) replaced", count);
248799ca880aSopenharmony_ci                        }
248899ca880aSopenharmony_ci                        if (major(udev_device_get_devnum(event->dev)) &&
248999ca880aSopenharmony_ci                            (!streq(name_str, udev_device_get_devnode(event->dev) + strlen("/dev/")))) {
249099ca880aSopenharmony_ci                                log_error("NAME=\"%s\" ignored, kernel device nodes "
249199ca880aSopenharmony_ci                                    "can not be renamed; please fix it in %s:%u\n", name,
249299ca880aSopenharmony_ci                                    rules_str(rules, rule->rule.filename_off), rule->rule.filename_line);
249399ca880aSopenharmony_ci                                break;
249499ca880aSopenharmony_ci                        }
249599ca880aSopenharmony_ci                        free(event->name);
249699ca880aSopenharmony_ci                        event->name = strdup(name_str);
249799ca880aSopenharmony_ci                        log_debug("NAME '%s' %s:%u",
249899ca880aSopenharmony_ci                                  event->name,
249999ca880aSopenharmony_ci                                  rules_str(rules, rule->rule.filename_off),
250099ca880aSopenharmony_ci                                  rule->rule.filename_line);
250199ca880aSopenharmony_ci                        break;
250299ca880aSopenharmony_ci                }
250399ca880aSopenharmony_ci                case TK_A_DEVLINK: {
250499ca880aSopenharmony_ci                        char temp[UTIL_PATH_SIZE];
250599ca880aSopenharmony_ci                        char filename[UTIL_PATH_SIZE];
250699ca880aSopenharmony_ci                        char *pos, *next;
250799ca880aSopenharmony_ci                        int count = 0;
250899ca880aSopenharmony_ci
250999ca880aSopenharmony_ci                        if (event->devlink_final)
251099ca880aSopenharmony_ci                                break;
251199ca880aSopenharmony_ci                        if (major(udev_device_get_devnum(event->dev)) == 0)
251299ca880aSopenharmony_ci                                break;
251399ca880aSopenharmony_ci                        if (cur->key.op == OP_ASSIGN_FINAL)
251499ca880aSopenharmony_ci                                event->devlink_final = true;
251599ca880aSopenharmony_ci                        if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL)
251699ca880aSopenharmony_ci                                udev_device_cleanup_devlinks_list(event->dev);
251799ca880aSopenharmony_ci
251899ca880aSopenharmony_ci                        /* allow  multiple symlinks separated by spaces */
251999ca880aSopenharmony_ci                        udev_event_apply_format(event, rules_str(rules, cur->key.value_off), temp, sizeof(temp), esc != ESCAPE_NONE);
252099ca880aSopenharmony_ci                        if (esc == ESCAPE_UNSET)
252199ca880aSopenharmony_ci                                count = util_replace_chars(temp, "/ ");
252299ca880aSopenharmony_ci                        else if (esc == ESCAPE_REPLACE)
252399ca880aSopenharmony_ci                                count = util_replace_chars(temp, "/");
252499ca880aSopenharmony_ci                        if (count > 0)
252599ca880aSopenharmony_ci                                log_debug("%i character(s) replaced" , count);
252699ca880aSopenharmony_ci                        pos = temp;
252799ca880aSopenharmony_ci                        while (isspace(pos[0]))
252899ca880aSopenharmony_ci                                pos++;
252999ca880aSopenharmony_ci                        next = strchr(pos, ' ');
253099ca880aSopenharmony_ci                        while (next != NULL) {
253199ca880aSopenharmony_ci                                next[0] = '\0';
253299ca880aSopenharmony_ci                                log_debug("LINK '%s' %s:%u", pos,
253399ca880aSopenharmony_ci                                          rules_str(rules, rule->rule.filename_off), rule->rule.filename_line);
253499ca880aSopenharmony_ci                                strscpyl(filename, sizeof(filename), "/dev/", pos, NULL);
253599ca880aSopenharmony_ci                                udev_device_add_devlink(event->dev, filename);
253699ca880aSopenharmony_ci                                while (isspace(next[1]))
253799ca880aSopenharmony_ci                                        next++;
253899ca880aSopenharmony_ci                                pos = &next[1];
253999ca880aSopenharmony_ci                                next = strchr(pos, ' ');
254099ca880aSopenharmony_ci                        }
254199ca880aSopenharmony_ci                        if (pos[0] != '\0') {
254299ca880aSopenharmony_ci                                log_debug("LINK '%s' %s:%u", pos,
254399ca880aSopenharmony_ci                                          rules_str(rules, rule->rule.filename_off), rule->rule.filename_line);
254499ca880aSopenharmony_ci                                strscpyl(filename, sizeof(filename), "/dev/", pos, NULL);
254599ca880aSopenharmony_ci                                udev_device_add_devlink(event->dev, filename);
254699ca880aSopenharmony_ci                        }
254799ca880aSopenharmony_ci                        break;
254899ca880aSopenharmony_ci                }
254999ca880aSopenharmony_ci                case TK_A_ATTR: {
255099ca880aSopenharmony_ci                        const char *key_name = rules_str(rules, cur->key.attr_off);
255199ca880aSopenharmony_ci                        char attr[UTIL_PATH_SIZE];
255299ca880aSopenharmony_ci                        char value[UTIL_NAME_SIZE];
255399ca880aSopenharmony_ci                        FILE *f;
255499ca880aSopenharmony_ci
255599ca880aSopenharmony_ci                        if (util_resolve_subsys_kernel(event->udev, key_name, attr, sizeof(attr), 0) != 0)
255699ca880aSopenharmony_ci                                strscpyl(attr, sizeof(attr), udev_device_get_syspath(event->dev), "/", key_name, NULL);
255799ca880aSopenharmony_ci                        attr_subst_subdir(attr, sizeof(attr));
255899ca880aSopenharmony_ci
255999ca880aSopenharmony_ci                        udev_event_apply_format(event, rules_str(rules, cur->key.value_off), value, sizeof(value), false);
256099ca880aSopenharmony_ci                        log_debug("ATTR '%s' writing '%s' %s:%u", attr, value,
256199ca880aSopenharmony_ci                                  rules_str(rules, rule->rule.filename_off),
256299ca880aSopenharmony_ci                                  rule->rule.filename_line);
256399ca880aSopenharmony_ci                        f = fopen(attr, "we");
256499ca880aSopenharmony_ci                        if (f != NULL) {
256599ca880aSopenharmony_ci                                if (fprintf(f, "%s", value) <= 0)
256699ca880aSopenharmony_ci                                        log_error_errno(errno, "error writing ATTR{%s}: %m", attr);
256799ca880aSopenharmony_ci                                fclose(f);
256899ca880aSopenharmony_ci                        } else {
256999ca880aSopenharmony_ci                                log_error_errno(errno, "error opening ATTR{%s} for writing: %m", attr);
257099ca880aSopenharmony_ci                        }
257199ca880aSopenharmony_ci                        break;
257299ca880aSopenharmony_ci                }
257399ca880aSopenharmony_ci                case TK_A_SYSCTL: {
257499ca880aSopenharmony_ci                        char filename[UTIL_PATH_SIZE];
257599ca880aSopenharmony_ci                        char value[UTIL_NAME_SIZE];
257699ca880aSopenharmony_ci                        int r;
257799ca880aSopenharmony_ci
257899ca880aSopenharmony_ci                        udev_event_apply_format(event, rules_str(rules, cur->key.attr_off), filename, sizeof(filename), false);
257999ca880aSopenharmony_ci                        sysctl_normalize(filename);
258099ca880aSopenharmony_ci                        udev_event_apply_format(event, rules_str(rules, cur->key.value_off), value, sizeof(value), false);
258199ca880aSopenharmony_ci                        log_debug("SYSCTL '%s' writing '%s' %s:%u", filename, value,
258299ca880aSopenharmony_ci                                  rules_str(rules, rule->rule.filename_off), rule->rule.filename_line);
258399ca880aSopenharmony_ci                        r = sysctl_write(filename, value);
258499ca880aSopenharmony_ci                        if (r < 0)
258599ca880aSopenharmony_ci                                log_error("error writing SYSCTL{%s}='%s': %s", filename, value, strerror(-r));
258699ca880aSopenharmony_ci                        break;
258799ca880aSopenharmony_ci                }
258899ca880aSopenharmony_ci                case TK_A_RUN_BUILTIN:
258999ca880aSopenharmony_ci                case TK_A_RUN_PROGRAM: {
259099ca880aSopenharmony_ci                        struct udev_list_entry *entry;
259199ca880aSopenharmony_ci
259299ca880aSopenharmony_ci                        if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL)
259399ca880aSopenharmony_ci                                udev_list_cleanup(&event->run_list);
259499ca880aSopenharmony_ci                        log_debug("RUN '%s' %s:%u",
259599ca880aSopenharmony_ci                                  rules_str(rules, cur->key.value_off),
259699ca880aSopenharmony_ci                                  rules_str(rules, rule->rule.filename_off),
259799ca880aSopenharmony_ci                                  rule->rule.filename_line);
259899ca880aSopenharmony_ci                        entry = udev_list_entry_add(&event->run_list, rules_str(rules, cur->key.value_off), NULL);
259999ca880aSopenharmony_ci                        udev_list_entry_set_num(entry, cur->key.builtin_cmd);
260099ca880aSopenharmony_ci                        break;
260199ca880aSopenharmony_ci                }
260299ca880aSopenharmony_ci                case TK_A_GOTO:
260399ca880aSopenharmony_ci                        if (cur->key.rule_goto == 0)
260499ca880aSopenharmony_ci                                break;
260599ca880aSopenharmony_ci                        cur = &rules->tokens[cur->key.rule_goto];
260699ca880aSopenharmony_ci                        continue;
260799ca880aSopenharmony_ci                case TK_END:
260899ca880aSopenharmony_ci                        return 0;
260999ca880aSopenharmony_ci
261099ca880aSopenharmony_ci                case TK_M_PARENTS_MIN:
261199ca880aSopenharmony_ci                case TK_M_PARENTS_MAX:
261299ca880aSopenharmony_ci                case TK_M_MAX:
261399ca880aSopenharmony_ci                case TK_UNSET:
261499ca880aSopenharmony_ci                        log_error("wrong type %u", cur->type);
261599ca880aSopenharmony_ci                        goto nomatch;
261699ca880aSopenharmony_ci                }
261799ca880aSopenharmony_ci
261899ca880aSopenharmony_ci                cur++;
261999ca880aSopenharmony_ci                continue;
262099ca880aSopenharmony_ci        nomatch:
262199ca880aSopenharmony_ci                /* fast-forward to next rule */
262299ca880aSopenharmony_ci                cur = rule + rule->rule.token_count;
262399ca880aSopenharmony_ci        }
262499ca880aSopenharmony_ci}
262599ca880aSopenharmony_ci
262699ca880aSopenharmony_ciint udev_rules_apply_static_dev_perms(struct udev_rules *rules) {
262799ca880aSopenharmony_ci        struct token *cur;
262899ca880aSopenharmony_ci        struct token *rule;
262999ca880aSopenharmony_ci        uid_t uid = 0;
263099ca880aSopenharmony_ci        gid_t gid = 0;
263199ca880aSopenharmony_ci        mode_t mode = 0;
263299ca880aSopenharmony_ci        _cleanup_strv_free_ char **tags = NULL;
263399ca880aSopenharmony_ci        char **t;
263499ca880aSopenharmony_ci        FILE *f = NULL;
263599ca880aSopenharmony_ci        _cleanup_free_ char *path = NULL;
263699ca880aSopenharmony_ci        int r = 0;
263799ca880aSopenharmony_ci
263899ca880aSopenharmony_ci        if (rules->tokens == NULL)
263999ca880aSopenharmony_ci                return 0;
264099ca880aSopenharmony_ci
264199ca880aSopenharmony_ci        cur = &rules->tokens[0];
264299ca880aSopenharmony_ci        rule = cur;
264399ca880aSopenharmony_ci        for (;;) {
264499ca880aSopenharmony_ci                switch (cur->type) {
264599ca880aSopenharmony_ci                case TK_RULE:
264699ca880aSopenharmony_ci                        /* current rule */
264799ca880aSopenharmony_ci                        rule = cur;
264899ca880aSopenharmony_ci
264999ca880aSopenharmony_ci                        /* skip rules without a static_node tag */
265099ca880aSopenharmony_ci                        if (!rule->rule.has_static_node)
265199ca880aSopenharmony_ci                                goto next;
265299ca880aSopenharmony_ci
265399ca880aSopenharmony_ci                        uid = 0;
265499ca880aSopenharmony_ci                        gid = 0;
265599ca880aSopenharmony_ci                        mode = 0;
265699ca880aSopenharmony_ci                        strv_free(tags);
265799ca880aSopenharmony_ci                        tags = NULL;
265899ca880aSopenharmony_ci                        break;
265999ca880aSopenharmony_ci                case TK_A_OWNER_ID:
266099ca880aSopenharmony_ci                        uid = cur->key.uid;
266199ca880aSopenharmony_ci                        break;
266299ca880aSopenharmony_ci                case TK_A_GROUP_ID:
266399ca880aSopenharmony_ci                        gid = cur->key.gid;
266499ca880aSopenharmony_ci                        break;
266599ca880aSopenharmony_ci                case TK_A_MODE_ID:
266699ca880aSopenharmony_ci                        mode = cur->key.mode;
266799ca880aSopenharmony_ci                        break;
266899ca880aSopenharmony_ci                case TK_A_TAG:
266999ca880aSopenharmony_ci                        r = strv_extend(&tags, rules_str(rules, cur->key.value_off));
267099ca880aSopenharmony_ci                        if (r < 0)
267199ca880aSopenharmony_ci                                goto finish;
267299ca880aSopenharmony_ci
267399ca880aSopenharmony_ci                        break;
267499ca880aSopenharmony_ci                case TK_A_STATIC_NODE: {
267599ca880aSopenharmony_ci                        char device_node[UTIL_PATH_SIZE];
267699ca880aSopenharmony_ci                        char tags_dir[UTIL_PATH_SIZE];
267799ca880aSopenharmony_ci                        char tag_symlink[UTIL_PATH_SIZE];
267899ca880aSopenharmony_ci                        struct stat stats;
267999ca880aSopenharmony_ci
268099ca880aSopenharmony_ci                        /* we assure, that the permissions tokens are sorted before the static token */
268199ca880aSopenharmony_ci
268299ca880aSopenharmony_ci                        if (mode == 0 && uid == 0 && gid == 0 && tags == NULL)
268399ca880aSopenharmony_ci                                goto next;
268499ca880aSopenharmony_ci
268599ca880aSopenharmony_ci                        strscpyl(device_node, sizeof(device_node), "/dev/", rules_str(rules, cur->key.value_off), NULL);
268699ca880aSopenharmony_ci                        if (stat(device_node, &stats) != 0)
268799ca880aSopenharmony_ci                                break;
268899ca880aSopenharmony_ci                        if (!S_ISBLK(stats.st_mode) && !S_ISCHR(stats.st_mode))
268999ca880aSopenharmony_ci                                break;
269099ca880aSopenharmony_ci
269199ca880aSopenharmony_ci                        /* export the tags to a directory as symlinks, allowing otherwise dead nodes to be tagged */
269299ca880aSopenharmony_ci                        if (tags) {
269399ca880aSopenharmony_ci                                STRV_FOREACH(t, tags) {
269499ca880aSopenharmony_ci                                        _cleanup_free_ char *unescaped_filename = NULL;
269599ca880aSopenharmony_ci
269699ca880aSopenharmony_ci                                        strscpyl(tags_dir, sizeof(tags_dir), UDEV_ROOT_RUN "/udev/static_node-tags/", *t, "/", NULL);
269799ca880aSopenharmony_ci                                        r = udev_mkdir_p(tags_dir, 0755);
269899ca880aSopenharmony_ci                                        if (r < 0)
269999ca880aSopenharmony_ci                                                return log_error_errno(r, "failed to create %s: %m", tags_dir);
270099ca880aSopenharmony_ci
270199ca880aSopenharmony_ci                                        unescaped_filename = xescape(rules_str(rules, cur->key.value_off), "/.");
270299ca880aSopenharmony_ci
270399ca880aSopenharmony_ci                                        strscpyl(tag_symlink, sizeof(tag_symlink), tags_dir, unescaped_filename, NULL);
270499ca880aSopenharmony_ci                                        r = symlink(device_node, tag_symlink);
270599ca880aSopenharmony_ci                                        if (r < 0 && errno != EEXIST)
270699ca880aSopenharmony_ci                                                return log_error_errno(errno, "failed to create symlink %s -> %s: %m",
270799ca880aSopenharmony_ci                                                                       tag_symlink, device_node);
270899ca880aSopenharmony_ci                                        else
270999ca880aSopenharmony_ci                                                r = 0;
271099ca880aSopenharmony_ci                                }
271199ca880aSopenharmony_ci                        }
271299ca880aSopenharmony_ci
271399ca880aSopenharmony_ci                        /* don't touch the permissions if only the tags were set */
271499ca880aSopenharmony_ci                        if (mode == 0 && uid == 0 && gid == 0)
271599ca880aSopenharmony_ci                                break;
271699ca880aSopenharmony_ci
271799ca880aSopenharmony_ci                        if (mode == 0) {
271899ca880aSopenharmony_ci                                if (gid > 0)
271999ca880aSopenharmony_ci                                        mode = 0660;
272099ca880aSopenharmony_ci                                else
272199ca880aSopenharmony_ci                                        mode = 0600;
272299ca880aSopenharmony_ci                        }
272399ca880aSopenharmony_ci                        if (mode != (stats.st_mode & 01777)) {
272499ca880aSopenharmony_ci                                r = chmod(device_node, mode);
272599ca880aSopenharmony_ci                                if (r < 0) {
272699ca880aSopenharmony_ci                                        log_error("failed to chmod '%s' %#o", device_node, mode);
272799ca880aSopenharmony_ci                                        return -errno;
272899ca880aSopenharmony_ci                                } else
272999ca880aSopenharmony_ci                                        log_debug("chmod '%s' %#o", device_node, mode);
273099ca880aSopenharmony_ci                        }
273199ca880aSopenharmony_ci
273299ca880aSopenharmony_ci                        if ((uid != 0 && uid != stats.st_uid) || (gid != 0 && gid != stats.st_gid)) {
273399ca880aSopenharmony_ci                                r = chown(device_node, uid, gid);
273499ca880aSopenharmony_ci                                if (r < 0) {
273599ca880aSopenharmony_ci                                        log_error("failed to chown '%s' %u %u ", device_node, uid, gid);
273699ca880aSopenharmony_ci                                        return -errno;
273799ca880aSopenharmony_ci                                } else
273899ca880aSopenharmony_ci                                        log_debug("chown '%s' %u %u", device_node, uid, gid);
273999ca880aSopenharmony_ci                        }
274099ca880aSopenharmony_ci
274199ca880aSopenharmony_ci                        utimensat(AT_FDCWD, device_node, NULL, 0);
274299ca880aSopenharmony_ci                        break;
274399ca880aSopenharmony_ci                }
274499ca880aSopenharmony_ci                case TK_END:
274599ca880aSopenharmony_ci                        goto finish;
274699ca880aSopenharmony_ci                }
274799ca880aSopenharmony_ci
274899ca880aSopenharmony_ci                cur++;
274999ca880aSopenharmony_ci                continue;
275099ca880aSopenharmony_cinext:
275199ca880aSopenharmony_ci                /* fast-forward to next rule */
275299ca880aSopenharmony_ci                cur = rule + rule->rule.token_count;
275399ca880aSopenharmony_ci                continue;
275499ca880aSopenharmony_ci        }
275599ca880aSopenharmony_ci
275699ca880aSopenharmony_cifinish:
275799ca880aSopenharmony_ci        if (f) {
275899ca880aSopenharmony_ci                fflush(f);
275999ca880aSopenharmony_ci                fchmod(fileno(f), 0644);
276099ca880aSopenharmony_ci                if (ferror(f) || rename(path, UDEV_ROOT_RUN "/udev/static_node-tags") < 0) {
276199ca880aSopenharmony_ci                        r = -errno;
276299ca880aSopenharmony_ci                        unlink(UDEV_ROOT_RUN "/udev/static_node-tags");
276399ca880aSopenharmony_ci                        unlink(path);
276499ca880aSopenharmony_ci                }
276599ca880aSopenharmony_ci                fclose(f);
276699ca880aSopenharmony_ci        }
276799ca880aSopenharmony_ci
276899ca880aSopenharmony_ci        return r;
276999ca880aSopenharmony_ci}
277099ca880aSopenharmony_ci
277199ca880aSopenharmony_ci#ifdef ENABLE_RULE_GENERATOR
277299ca880aSopenharmony_ci/* function to return the count of rules that assign NAME= to a value matching arg#2 - returns 0,1 */
277399ca880aSopenharmony_ciint udev_rules_assigning_name_to(struct udev_rules *rules, const char *match_name)
277499ca880aSopenharmony_ci{
277599ca880aSopenharmony_ci        struct token *cur;
277699ca880aSopenharmony_ci        struct token *rule;
277799ca880aSopenharmony_ci        enum escape_type esc = ESCAPE_UNSET;
277899ca880aSopenharmony_ci        bool name_final = false;
277999ca880aSopenharmony_ci
278099ca880aSopenharmony_ci        if (rules->tokens == NULL)
278199ca880aSopenharmony_ci                return 0;
278299ca880aSopenharmony_ci
278399ca880aSopenharmony_ci        /* loop through token list, match, run actions or forward to next rule */
278499ca880aSopenharmony_ci        cur = &rules->tokens[0];
278599ca880aSopenharmony_ci        rule = cur;
278699ca880aSopenharmony_ci        for (;;) {
278799ca880aSopenharmony_ci                dump_token(rules, cur);
278899ca880aSopenharmony_ci                switch (cur->type) {
278999ca880aSopenharmony_ci                case TK_RULE:
279099ca880aSopenharmony_ci                        /* current rule */
279199ca880aSopenharmony_ci                        rule = cur;
279299ca880aSopenharmony_ci                        if (!rule->rule.can_set_name)
279399ca880aSopenharmony_ci                                goto nomatch;
279499ca880aSopenharmony_ci                        break;
279599ca880aSopenharmony_ci                case TK_M_SUBSYSTEM:
279699ca880aSopenharmony_ci                        if (match_key(rules, cur, "net") != 0)
279799ca880aSopenharmony_ci                                goto nomatch;
279899ca880aSopenharmony_ci                        break;
279999ca880aSopenharmony_ci                case TK_M_ACTION:
280099ca880aSopenharmony_ci                        if (match_key(rules, cur, "add") != 0)
280199ca880aSopenharmony_ci                                goto nomatch;
280299ca880aSopenharmony_ci                        break;
280399ca880aSopenharmony_ci                case TK_A_NAME: {
280499ca880aSopenharmony_ci                        const char *name  = rules_str(rules, cur->key.value_off);
280599ca880aSopenharmony_ci                        char name_str[UTIL_PATH_SIZE];
280699ca880aSopenharmony_ci                        int count;
280799ca880aSopenharmony_ci
280899ca880aSopenharmony_ci                        strscpy(name_str,UTIL_PATH_SIZE,name);
280999ca880aSopenharmony_ci                        count = util_replace_chars(name_str, "/");
281099ca880aSopenharmony_ci                        if (count > 0)
281199ca880aSopenharmony_ci                                log_debug("%i character(s) replaced\n", count);
281299ca880aSopenharmony_ci                        if (streq(name_str,match_name)) {
281399ca880aSopenharmony_ci                                log_debug("found a match! NAME assigns %s in: %s:%u\n",
281499ca880aSopenharmony_ci                                          name,
281599ca880aSopenharmony_ci                                          rules_str(rules, rule->rule.filename_off),
281699ca880aSopenharmony_ci                                          rule->rule.filename_line);
281799ca880aSopenharmony_ci                                return 1; /* no need to find more than one */
281899ca880aSopenharmony_ci                        }
281999ca880aSopenharmony_ci
282099ca880aSopenharmony_ci                        /* skip to next rule */
282199ca880aSopenharmony_ci                        goto nomatch;
282299ca880aSopenharmony_ci                }
282399ca880aSopenharmony_ci                case TK_END:
282499ca880aSopenharmony_ci                        return 0;
282599ca880aSopenharmony_ci                }
282699ca880aSopenharmony_ci
282799ca880aSopenharmony_ci                cur++;
282899ca880aSopenharmony_ci                continue;
282999ca880aSopenharmony_ci        nomatch:
283099ca880aSopenharmony_ci                /* fast-forward to next rule */
283199ca880aSopenharmony_ci                cur = rule + rule->rule.token_count;
283299ca880aSopenharmony_ci        }
283399ca880aSopenharmony_ci}
283499ca880aSopenharmony_ci#endif
283599ca880aSopenharmony_ci
2836