udev-rules.c 104 KB
Newer Older
1
/*
2
 * Copyright (C) 2003-2012 Kay Sievers <kay@vrfy.org>
3
 *
Kay Sievers's avatar
Kay Sievers committed
4 5 6 7
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
8
 *
Kay Sievers's avatar
Kay Sievers committed
9 10 11 12
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
13
 *
Kay Sievers's avatar
Kay Sievers committed
14 15
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 17 18
 */

#include <stddef.h>
19
#include <limits.h>
20
#include <stdlib.h>
21
#include <stdbool.h>
22 23 24 25 26 27
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <ctype.h>
#include <unistd.h>
#include <errno.h>
28
#include <dirent.h>
29
#include <fnmatch.h>
30
#include <time.h>
31 32

#include "udev.h"
Kay Sievers's avatar
Kay Sievers committed
33
#include "path-util.h"
Kay Sievers's avatar
Kay Sievers committed
34
#include "conf-files.h"
35
#include "strbuf.h"
36

37
#define PREALLOC_TOKEN          2048
38

39
struct uid_gid {
40 41
        unsigned int name_off;
        union {
42 43
                uid_t uid;
                gid_t gid;
44
        };
45 46 47
};

struct udev_rules {
48
        struct udev *udev;
49
        char **dirs;
Kay Sievers's avatar
Kay Sievers committed
50
        usec_t *dirs_ts_usec;
51 52 53 54 55 56 57
        int resolve_names;

        /* every key in the rules file becomes a token */
        struct token *tokens;
        unsigned int token_cur;
        unsigned int token_max;

58
        /* all key strings are copied and de-duplicated in a single continuous string buffer */
59
        struct strbuf *strbuf;
60 61 62 63 64 65 66 67

        /* during rule parsing, uid/gid lookup results are cached */
        struct uid_gid *uids;
        unsigned int uids_cur;
        unsigned int uids_max;
        struct uid_gid *gids;
        unsigned int gids_cur;
        unsigned int gids_max;
68 69
};

70 71 72 73 74 75 76 77
static char *rules_str(struct udev_rules *rules, unsigned int off) {
        return rules->strbuf->buf + off;
}

static unsigned int rules_add_string(struct udev_rules *rules, const char *s) {
        return strbuf_add_string(rules->strbuf, s, strlen(s));
}

78
/* KEY=="", KEY!="", KEY+="", KEY="", KEY:="" */
79
enum operation_type {
80
        OP_UNSET,
81

82 83 84
        OP_MATCH,
        OP_NOMATCH,
        OP_MATCH_MAX,
85

86 87 88
        OP_ADD,
        OP_ASSIGN,
        OP_ASSIGN_FINAL,
89 90
};

91
enum string_glob_type {
92
        GL_UNSET,
93
        GL_PLAIN,                       /* no special chars */
94
        GL_GLOB,                        /* shell globs ?,*,[] */
95 96 97
        GL_SPLIT,                       /* multi-value A|B */
        GL_SPLIT_GLOB,                  /* multi-value with glob A*|B* */
        GL_SOMETHING,                   /* commonly used "?*" */
98 99
};

100
enum string_subst_type {
101 102 103 104
        SB_UNSET,
        SB_NONE,
        SB_FORMAT,
        SB_SUBSYS,
105 106
};

107
/* tokens of a rule are sorted/handled in this order */
108
enum token_type {
109 110 111
        TK_UNSET,
        TK_RULE,

112 113 114 115 116 117 118 119 120 121 122
        TK_M_ACTION,                    /* val */
        TK_M_DEVPATH,                   /* val */
        TK_M_KERNEL,                    /* val */
        TK_M_DEVLINK,                   /* val */
        TK_M_NAME,                      /* val */
        TK_M_ENV,                       /* val, attr */
        TK_M_TAG,                       /* val */
        TK_M_SUBSYSTEM,                 /* val */
        TK_M_DRIVER,                    /* val */
        TK_M_WAITFOR,                   /* val */
        TK_M_ATTR,                      /* val, attr */
123 124

        TK_M_PARENTS_MIN,
125
        TK_M_KERNELS,                   /* val */
126
        TK_M_SUBSYSTEMS,                /* val */
127 128 129
        TK_M_DRIVERS,                   /* val */
        TK_M_ATTRS,                     /* val, attr */
        TK_M_TAGS,                      /* val */
130 131
        TK_M_PARENTS_MAX,

132 133 134 135 136 137 138 139 140 141
        TK_M_TEST,                      /* val, mode_t */
        TK_M_EVENT_TIMEOUT,             /* int */
        TK_M_PROGRAM,                   /* val */
        TK_M_IMPORT_FILE,               /* val */
        TK_M_IMPORT_PROG,               /* val */
        TK_M_IMPORT_BUILTIN,            /* val */
        TK_M_IMPORT_DB,                 /* val */
        TK_M_IMPORT_CMDLINE,            /* val */
        TK_M_IMPORT_PARENT,             /* val */
        TK_M_RESULT,                    /* val */
142 143 144 145 146
        TK_M_MAX,

        TK_A_STRING_ESCAPE_NONE,
        TK_A_STRING_ESCAPE_REPLACE,
        TK_A_DB_PERSIST,
147 148 149 150 151 152 153 154 155 156 157 158 159 160
        TK_A_INOTIFY_WATCH,             /* int */
        TK_A_DEVLINK_PRIO,              /* int */
        TK_A_OWNER,                     /* val */
        TK_A_GROUP,                     /* val */
        TK_A_MODE,                      /* val */
        TK_A_OWNER_ID,                  /* uid_t */
        TK_A_GROUP_ID,                  /* gid_t */
        TK_A_MODE_ID,                   /* mode_t */
        TK_A_STATIC_NODE,               /* val */
        TK_A_ENV,                       /* val, attr */
        TK_A_TAG,                       /* val */
        TK_A_NAME,                      /* val */
        TK_A_DEVLINK,                   /* val */
        TK_A_ATTR,                      /* val, attr */
161 162
        TK_A_RUN_BUILTIN,               /* val, bool */
        TK_A_RUN_PROGRAM,               /* val, bool */
163
        TK_A_GOTO,                      /* size_t */
164 165

        TK_END,
166 167
};

168
/* we try to pack stuff in a way that we take only 12 bytes per token */
169
struct token {
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
        union {
                unsigned char type;                /* same in rule and key */
                struct {
                        enum token_type type:8;
                        bool can_set_name:1;
                        bool has_static_node:1;
                        unsigned int unused:6;
                        unsigned short token_count;
                        unsigned int label_off;
                        unsigned short filename_off;
                        unsigned short filename_line;
                } rule;
                struct {
                        enum token_type type:8;
                        enum operation_type op:8;
                        enum string_glob_type glob:8;
                        enum string_subst_type subst:4;
                        enum string_subst_type attrsubst:4;
                        unsigned int value_off;
                        union {
                                unsigned int attr_off;
                                unsigned int rule_goto;
                                mode_t  mode;
                                uid_t uid;
                                gid_t gid;
                                int devlink_prio;
                                int event_timeout;
                                int watch;
                                enum udev_builtin_cmd builtin_cmd;
                        };
                } key;
        };
202 203
};

204
#define MAX_TK                64
205
struct rule_tmp {
206 207 208 209
        struct udev_rules *rules;
        struct token rule;
        struct token token[MAX_TK];
        unsigned int token_cur;
210 211
};

212
#ifdef DEBUG
213 214
static const char *operation_str(enum operation_type type)
{
215
        static const char *operation_strs[] = {
216 217 218
                [OP_UNSET] =            "UNSET",
                [OP_MATCH] =            "match",
                [OP_NOMATCH] =          "nomatch",
219 220
                [OP_MATCH_MAX] =        "MATCH_MAX",

221 222 223
                [OP_ADD] =              "add",
                [OP_ASSIGN] =           "assign",
                [OP_ASSIGN_FINAL] =     "assign-final",
224 225 226
}        ;

        return operation_strs[type];
227
}
228

229 230
static const char *string_glob_str(enum string_glob_type type)
{
231
        static const char *string_glob_strs[] = {
232 233 234 235 236 237
                [GL_UNSET] =            "UNSET",
                [GL_PLAIN] =            "plain",
                [GL_GLOB] =             "glob",
                [GL_SPLIT] =            "split",
                [GL_SPLIT_GLOB] =       "split-glob",
                [GL_SOMETHING] =        "split-glob",
238 239 240
        };

        return string_glob_strs[type];
241 242 243 244
}

static const char *token_str(enum token_type type)
{
245
        static const char *token_strs[] = {
246 247
                [TK_UNSET] =                    "UNSET",
                [TK_RULE] =                     "RULE",
248

249
                [TK_M_ACTION] =                 "M ACTION",
250
                [TK_M_DEVPATH] =                "M DEVPATH",
251
                [TK_M_KERNEL] =                 "M KERNEL",
252
                [TK_M_DEVLINK] =                "M DEVLINK",
253 254 255 256 257
                [TK_M_NAME] =                   "M NAME",
                [TK_M_ENV] =                    "M ENV",
                [TK_M_TAG] =                    "M TAG",
                [TK_M_SUBSYSTEM] =              "M SUBSYSTEM",
                [TK_M_DRIVER] =                 "M DRIVER",
258
                [TK_M_WAITFOR] =                "M WAITFOR",
259
                [TK_M_ATTR] =                   "M ATTR",
260

261
                [TK_M_PARENTS_MIN] =            "M PARENTS_MIN",
262
                [TK_M_KERNELS] =                "M KERNELS",
263
                [TK_M_SUBSYSTEMS] =             "M SUBSYSTEMS",
264
                [TK_M_DRIVERS] =                "M DRIVERS",
265 266 267
                [TK_M_ATTRS] =                  "M ATTRS",
                [TK_M_TAGS] =                   "M TAGS",
                [TK_M_PARENTS_MAX] =            "M PARENTS_MAX",
268

269 270
                [TK_M_TEST] =                   "M TEST",
                [TK_M_EVENT_TIMEOUT] =          "M EVENT_TIMEOUT",
271
                [TK_M_PROGRAM] =                "M PROGRAM",
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
                [TK_M_IMPORT_FILE] =            "M IMPORT_FILE",
                [TK_M_IMPORT_PROG] =            "M IMPORT_PROG",
                [TK_M_IMPORT_BUILTIN] =         "M IMPORT_BUILTIN",
                [TK_M_IMPORT_DB] =              "M IMPORT_DB",
                [TK_M_IMPORT_CMDLINE] =         "M IMPORT_CMDLINE",
                [TK_M_IMPORT_PARENT] =          "M IMPORT_PARENT",
                [TK_M_RESULT] =                 "M RESULT",
                [TK_M_MAX] =                    "M MAX",

                [TK_A_STRING_ESCAPE_NONE] =     "A STRING_ESCAPE_NONE",
                [TK_A_STRING_ESCAPE_REPLACE] =  "A STRING_ESCAPE_REPLACE",
                [TK_A_DB_PERSIST] =             "A DB_PERSIST",
                [TK_A_INOTIFY_WATCH] =          "A INOTIFY_WATCH",
                [TK_A_DEVLINK_PRIO] =           "A DEVLINK_PRIO",
                [TK_A_OWNER] =                  "A OWNER",
                [TK_A_GROUP] =                  "A GROUP",
                [TK_A_MODE] =                   "A MODE",
                [TK_A_OWNER_ID] =               "A OWNER_ID",
                [TK_A_GROUP_ID] =               "A GROUP_ID",
                [TK_A_STATIC_NODE] =            "A STATIC_NODE",
292
                [TK_A_MODE_ID] =                "A MODE_ID",
293 294 295
                [TK_A_ENV] =                    "A ENV",
                [TK_A_TAG] =                    "A ENV",
                [TK_A_NAME] =                   "A NAME",
296
                [TK_A_DEVLINK] =                "A DEVLINK",
297
                [TK_A_ATTR] =                   "A ATTR",
298 299
                [TK_A_RUN_BUILTIN] =            "A RUN_BUILTIN",
                [TK_A_RUN_PROGRAM] =            "A RUN_PROGRAM",
300
                [TK_A_GOTO] =                   "A GOTO",
301

302
                [TK_END] =                      "END",
303 304 305
        };

        return token_strs[type];
306
}
307

308 309
static void dump_token(struct udev_rules *rules, struct token *token)
{
310 311 312
        enum token_type type = token->type;
        enum operation_type op = token->key.op;
        enum string_glob_type glob = token->key.glob;
313
        const char *value = str(rules, token->key.value_off);
314 315 316 317 318 319 320 321 322
        const char *attr = &rules->buf[token->key.attr_off];

        switch (type) {
        case TK_RULE:
                {
                        const char *tks_ptr = (char *)rules->tokens;
                        const char *tk_ptr = (char *)token;
                        unsigned int idx = (tk_ptr - tks_ptr) / sizeof(struct token);

323 324 325 326
                        log_debug("* RULE %s:%u, token: %u, count: %u, label: '%s'\n",
                                  &rules->buf[token->rule.filename_off], token->rule.filename_line,
                                  idx, token->rule.token_count,
                                  &rules->buf[token->rule.label_off]);
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
                        break;
                }
        case TK_M_ACTION:
        case TK_M_DEVPATH:
        case TK_M_KERNEL:
        case TK_M_SUBSYSTEM:
        case TK_M_DRIVER:
        case TK_M_WAITFOR:
        case TK_M_DEVLINK:
        case TK_M_NAME:
        case TK_M_KERNELS:
        case TK_M_SUBSYSTEMS:
        case TK_M_DRIVERS:
        case TK_M_TAGS:
        case TK_M_PROGRAM:
        case TK_M_IMPORT_FILE:
        case TK_M_IMPORT_PROG:
        case TK_M_IMPORT_DB:
        case TK_M_IMPORT_CMDLINE:
        case TK_M_IMPORT_PARENT:
        case TK_M_RESULT:
        case TK_A_NAME:
        case TK_A_DEVLINK:
        case TK_A_OWNER:
        case TK_A_GROUP:
        case TK_A_MODE:
353 354
        case TK_A_RUN_BUILTIN:
        case TK_A_RUN_PROGRAM:
355 356
                log_debug("%s %s '%s'(%s)\n",
                          token_str(type), operation_str(op), value, string_glob_str(glob));
357 358
                break;
        case TK_M_IMPORT_BUILTIN:
359
                log_debug("%s %i '%s'\n", token_str(type), token->key.builtin_cmd, value);
360 361 362 363 364 365
                break;
        case TK_M_ATTR:
        case TK_M_ATTRS:
        case TK_M_ENV:
        case TK_A_ATTR:
        case TK_A_ENV:
366 367
                log_debug("%s %s '%s' '%s'(%s)\n",
                          token_str(type), operation_str(op), attr, value, string_glob_str(glob));
368 369 370
                break;
        case TK_M_TAG:
        case TK_A_TAG:
371
                log_debug("%s %s '%s'\n", token_str(type), operation_str(op), value);
372 373 374 375
                break;
        case TK_A_STRING_ESCAPE_NONE:
        case TK_A_STRING_ESCAPE_REPLACE:
        case TK_A_DB_PERSIST:
376
                log_debug("%s\n", token_str(type));
377 378
                break;
        case TK_M_TEST:
379 380
                log_debug("%s %s '%s'(%s) %#o\n",
                          token_str(type), operation_str(op), value, string_glob_str(glob), token->key.mode);
381 382
                break;
        case TK_A_INOTIFY_WATCH:
383
                log_debug("%s %u\n", token_str(type), token->key.watch);
384 385
                break;
        case TK_A_DEVLINK_PRIO:
386
                log_debug("%s %u\n", token_str(type), token->key.devlink_prio);
387 388
                break;
        case TK_A_OWNER_ID:
389
                log_debug("%s %s %u\n", token_str(type), operation_str(op), token->key.uid);
390 391
                break;
        case TK_A_GROUP_ID:
392
                log_debug("%s %s %u\n", token_str(type), operation_str(op), token->key.gid);
393 394
                break;
        case TK_A_MODE_ID:
395
                log_debug("%s %s %#o\n", token_str(type), operation_str(op), token->key.mode);
396 397
                break;
        case TK_A_STATIC_NODE:
398
                log_debug("%s '%s'\n", token_str(type), value);
399 400
                break;
        case TK_M_EVENT_TIMEOUT:
401
                log_debug("%s %u\n", token_str(type), token->key.event_timeout);
402 403
                break;
        case TK_A_GOTO:
404
                log_debug("%s '%s' %u\n", token_str(type), value, token->key.rule_goto);
405 406
                break;
        case TK_END:
407
                log_debug("* %s\n", token_str(type));
408 409 410 411 412
                break;
        case TK_M_PARENTS_MIN:
        case TK_M_PARENTS_MAX:
        case TK_M_MAX:
        case TK_UNSET:
413
                log_debug("unknown type %u\n", type);
414 415
                break;
        }
416
}
417

418 419
static void dump_rules(struct udev_rules *rules)
{
420 421
        unsigned int i;

422 423 424 425 426
        log_debug("dumping %u (%zu bytes) tokens, %u (%zu bytes) strings\n",
                  rules->token_cur,
                  rules->token_cur * sizeof(struct token),
                  rules->buf_count,
                  rules->buf_cur);
427 428
        for(i = 0; i < rules->token_cur; i++)
                dump_token(rules, &rules->tokens[i]);
429 430
}
#else
431 432
static inline const char *operation_str(enum operation_type type) { return NULL; }
static inline const char *token_str(enum token_type type) { return NULL; }
433 434
static inline void dump_token(struct udev_rules *rules, struct token *token) {}
static inline void dump_rules(struct udev_rules *rules) {}
435
#endif /* DEBUG */
436

437 438
static int add_token(struct udev_rules *rules, struct token *token)
{
439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
        /* grow buffer if needed */
        if (rules->token_cur+1 >= rules->token_max) {
                struct token *tokens;
                unsigned int add;

                /* double the buffer size */
                add = rules->token_max;
                if (add < 8)
                        add = 8;

                tokens = realloc(rules->tokens, (rules->token_max + add ) * sizeof(struct token));
                if (tokens == NULL)
                        return -1;
                rules->tokens = tokens;
                rules->token_max += add;
        }
        memcpy(&rules->tokens[rules->token_cur], token, sizeof(struct token));
        rules->token_cur++;
        return 0;
458
}
459

460 461
static uid_t add_uid(struct udev_rules *rules, const char *owner)
{
462 463 464 465 466 467 468
        unsigned int i;
        uid_t uid;
        unsigned int off;

        /* lookup, if we know it already */
        for (i = 0; i < rules->uids_cur; i++) {
                off = rules->uids[i].name_off;
469
                if (streq(rules_str(rules, off), owner)) {
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492
                        uid = rules->uids[i].uid;
                        return uid;
                }
        }
        uid = util_lookup_user(rules->udev, owner);

        /* grow buffer if needed */
        if (rules->uids_cur+1 >= rules->uids_max) {
                struct uid_gid *uids;
                unsigned int add;

                /* double the buffer size */
                add = rules->uids_max;
                if (add < 1)
                        add = 8;

                uids = realloc(rules->uids, (rules->uids_max + add ) * sizeof(struct uid_gid));
                if (uids == NULL)
                        return uid;
                rules->uids = uids;
                rules->uids_max += add;
        }
        rules->uids[rules->uids_cur].uid = uid;
493
        off = rules_add_string(rules, owner);
494 495 496 497 498
        if (off <= 0)
                return uid;
        rules->uids[rules->uids_cur].name_off = off;
        rules->uids_cur++;
        return uid;
499 500 501 502
}

static gid_t add_gid(struct udev_rules *rules, const char *group)
{
503 504 505 506 507 508 509
        unsigned int i;
        gid_t gid;
        unsigned int off;

        /* lookup, if we know it already */
        for (i = 0; i < rules->gids_cur; i++) {
                off = rules->gids[i].name_off;
510
                if (streq(rules_str(rules, off), group)) {
511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533
                        gid = rules->gids[i].gid;
                        return gid;
                }
        }
        gid = util_lookup_group(rules->udev, group);

        /* grow buffer if needed */
        if (rules->gids_cur+1 >= rules->gids_max) {
                struct uid_gid *gids;
                unsigned int add;

                /* double the buffer size */
                add = rules->gids_max;
                if (add < 1)
                        add = 8;

                gids = realloc(rules->gids, (rules->gids_max + add ) * sizeof(struct uid_gid));
                if (gids == NULL)
                        return gid;
                rules->gids = gids;
                rules->gids_max += add;
        }
        rules->gids[rules->gids_cur].gid = gid;
534
        off = rules_add_string(rules, group);
535 536 537 538 539
        if (off <= 0)
                return gid;
        rules->gids[rules->gids_cur].name_off = off;
        rules->gids_cur++;
        return gid;
540 541
}

542
static int import_property_from_string(struct udev_device *dev, char *line)
543
{
544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
        char *key;
        char *val;
        size_t len;

        /* find key */
        key = line;
        while (isspace(key[0]))
                key++;

        /* comment or empty line */
        if (key[0] == '#' || key[0] == '\0')
                return -1;

        /* split key/value */
        val = strchr(key, '=');
        if (val == NULL)
                return -1;
        val[0] = '\0';
        val++;

        /* find value */
        while (isspace(val[0]))
                val++;

        /* terminate key */
        len = strlen(key);
        if (len == 0)
                return -1;
        while (isspace(key[len-1]))
                len--;
        key[len] = '\0';

        /* terminate value */
        len = strlen(val);
        if (len == 0)
                return -1;
        while (isspace(val[len-1]))
                len--;
        val[len] = '\0';

        if (len == 0)
                return -1;

        /* unquote */
        if (val[0] == '"' || val[0] == '\'') {
                if (val[len-1] != val[0]) {
590
                        log_debug("inconsistent quoting: '%s', skip\n", line);
591 592 593 594 595 596 597
                        return -1;
                }
                val[len-1] = '\0';
                val++;
        }

        /* handle device, renamed by external tool, returning new path */
598
        if (streq(key, "DEVPATH")) {
599 600
                char syspath[UTIL_PATH_SIZE];

601 602
                log_debug("updating devpath from '%s' to '%s'\n",
                          udev_device_get_devpath(dev), val);
603
                strscpyl(syspath, sizeof(syspath), "/sys", val, NULL);
604 605 606 607 608 609 610 611 612 613
                udev_device_set_syspath(dev, syspath);
        } else {
                struct udev_list_entry *entry;

                entry = udev_device_add_property(dev, key, val);
                /* store in db, skip private keys */
                if (key[0] != '.')
                        udev_list_entry_set_num(entry, true);
        }
        return 0;
614 615
}

616
static int import_file_into_properties(struct udev_device *dev, const char *filename)
617
{
618 619 620
        FILE *f;
        char line[UTIL_LINE_SIZE];

Kay Sievers's avatar
Kay Sievers committed
621
        f = fopen(filename, "re");
622 623 624 625 626 627
        if (f == NULL)
                return -1;
        while (fgets(line, sizeof(line), f) != NULL)
                import_property_from_string(dev, line);
        fclose(f);
        return 0;
628 629
}

Kay Sievers's avatar
Kay Sievers committed
630
static int import_program_into_properties(struct udev_event *event, const char *program, const sigset_t *sigmask)
631
{
632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655
        struct udev_device *dev = event->dev;
        char **envp;
        char result[UTIL_LINE_SIZE];
        char *line;
        int err;

        envp = udev_device_get_properties_envp(dev);
        err = udev_event_spawn(event, program, envp, sigmask, result, sizeof(result));
        if (err < 0)
                return err;

        line = result;
        while (line != NULL) {
                char *pos;

                pos = strchr(line, '\n');
                if (pos != NULL) {
                        pos[0] = '\0';
                        pos = &pos[1];
                }
                import_property_from_string(dev, line);
                line = pos;
        }
        return 0;
656 657
}

658
static int import_parent_into_properties(struct udev_device *dev, const char *filter)
659
{
660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680
        struct udev_device *dev_parent;
        struct udev_list_entry *list_entry;

        dev_parent = udev_device_get_parent(dev);
        if (dev_parent == NULL)
                return -1;

        udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(dev_parent)) {
                const char *key = udev_list_entry_get_name(list_entry);
                const char *val = udev_list_entry_get_value(list_entry);

                if (fnmatch(filter, key, 0) == 0) {
                        struct udev_list_entry *entry;

                        entry = udev_device_add_property(dev, key, val);
                        /* store in db, skip private keys */
                        if (key[0] != '.')
                                udev_list_entry_set_num(entry, true);
                }
        }
        return 0;
681 682
}

683
#define WAIT_LOOP_PER_SECOND                50
684
static int wait_for_file(struct udev_device *dev, const char *file, int timeout)
685
{
686 687 688 689 690 691 692 693
        char filepath[UTIL_PATH_SIZE];
        char devicepath[UTIL_PATH_SIZE];
        struct stat stats;
        int loop = timeout * WAIT_LOOP_PER_SECOND;

        /* a relative path is a device attribute */
        devicepath[0] = '\0';
        if (file[0] != '/') {
694 695
                strscpyl(devicepath, sizeof(devicepath), udev_device_get_syspath(dev), NULL);
                strscpyl(filepath, sizeof(filepath), devicepath, "/", file, NULL);
696 697 698 699 700 701 702 703
                file = filepath;
        }

        while (--loop) {
                const struct timespec duration = { 0, 1000 * 1000 * 1000 / WAIT_LOOP_PER_SECOND };

                /* lookup file */
                if (stat(file, &stats) == 0) {
704
                        log_debug("file '%s' appeared after %i loops\n", file, (timeout * WAIT_LOOP_PER_SECOND) - loop-1);
705 706 707 708
                        return 0;
                }
                /* make sure, the device did not disappear in the meantime */
                if (devicepath[0] != '\0' && stat(devicepath, &stats) != 0) {
709
                        log_debug("device disappeared while waiting for '%s'\n", file);
710 711
                        return -2;
                }
712
                log_debug("wait for '%s' for %i mseconds\n", file, 1000 / WAIT_LOOP_PER_SECOND);
713 714
                nanosleep(&duration, NULL);
        }
715
        log_debug("waiting for '%s' failed\n", file);
716
        return -1;
717 718
}

719 720
static int attr_subst_subdir(char *attr, size_t len)
{
721 722 723 724 725 726 727 728
        bool found = false;

        if (strstr(attr, "/*/")) {
                char *pos;
                char dirname[UTIL_PATH_SIZE];
                const char *tail;
                DIR *dir;

729
                strscpy(dirname, sizeof(dirname), attr);
730 731 732 733 734 735 736 737 738 739 740 741 742 743
                pos = strstr(dirname, "/*/");
                if (pos == NULL)
                        return -1;
                pos[0] = '\0';
                tail = &pos[2];
                dir = opendir(dirname);
                if (dir != NULL) {
                        struct dirent *dent;

                        for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
                                struct stat stats;

                                if (dent->d_name[0] == '.')
                                        continue;
744
                                strscpyl(attr, len, dirname, "/", dent->d_name, tail, NULL);
745 746 747 748 749 750 751 752 753 754
                                if (stat(attr, &stats) == 0) {
                                        found = true;
                                        break;
                                }
                        }
                        closedir(dir);
                }
        }

        return found;
755 756
}

757
static int get_key(struct udev *udev, char **line, char **key, enum operation_type *op, char **value)
Kay Sievers's avatar
Kay Sievers committed
758
{
759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841
        char *linepos;
        char *temp;

        linepos = *line;
        if (linepos == NULL || linepos[0] == '\0')
                return -1;

        /* skip whitespace */
        while (isspace(linepos[0]) || linepos[0] == ',')
                linepos++;

        /* get the key */
        if (linepos[0] == '\0')
                return -1;
        *key = linepos;

        for (;;) {
                linepos++;
                if (linepos[0] == '\0')
                        return -1;
                if (isspace(linepos[0]))
                        break;
                if (linepos[0] == '=')
                        break;
                if ((linepos[0] == '+') || (linepos[0] == '!') || (linepos[0] == ':'))
                        if (linepos[1] == '=')
                                break;
        }

        /* remember end of key */
        temp = linepos;

        /* skip whitespace after key */
        while (isspace(linepos[0]))
                linepos++;
        if (linepos[0] == '\0')
                return -1;

        /* get operation type */
        if (linepos[0] == '=' && linepos[1] == '=') {
                *op = OP_MATCH;
                linepos += 2;
        } else if (linepos[0] == '!' && linepos[1] == '=') {
                *op = OP_NOMATCH;
                linepos += 2;
        } else if (linepos[0] == '+' && linepos[1] == '=') {
                *op = OP_ADD;
                linepos += 2;
        } else if (linepos[0] == '=') {
                *op = OP_ASSIGN;
                linepos++;
        } else if (linepos[0] == ':' && linepos[1] == '=') {
                *op = OP_ASSIGN_FINAL;
                linepos += 2;
        } else
                return -1;

        /* terminate key */
        temp[0] = '\0';

        /* skip whitespace after operator */
        while (isspace(linepos[0]))
                linepos++;
        if (linepos[0] == '\0')
                return -1;

        /* get the value */
        if (linepos[0] == '"')
                linepos++;
        else
                return -1;
        *value = linepos;

        /* terminate */
        temp = strchr(linepos, '"');
        if (!temp)
                return -1;
        temp[0] = '\0';
        temp++;

        /* move line to next key */
        *line = temp;
        return 0;
842
}
843

844
/* extract possible KEY{attr} */
845
static const char *get_key_attribute(struct udev *udev, char *str)
846
{
847 848 849 850 851 852 853 854
        char *pos;
        char *attr;

        attr = strchr(str, '{');
        if (attr != NULL) {
                attr++;
                pos = strchr(attr, '}');
                if (pos == NULL) {
855
                        log_error("missing closing brace for format\n");
856 857 858 859 860 861
                        return NULL;
                }
                pos[0] = '\0';
                return attr;
        }
        return NULL;
862
}
863

864
static int rule_add_key(struct rule_tmp *rule_tmp, enum token_type type,
865 866
                        enum operation_type op,
                        const char *value, const void *data)
867
{
868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895
        struct token *token = &rule_tmp->token[rule_tmp->token_cur];
        const char *attr = NULL;

        memset(token, 0x00, sizeof(struct token));

        switch (type) {
        case TK_M_ACTION:
        case TK_M_DEVPATH:
        case TK_M_KERNEL:
        case TK_M_SUBSYSTEM:
        case TK_M_DRIVER:
        case TK_M_WAITFOR:
        case TK_M_DEVLINK:
        case TK_M_NAME:
        case TK_M_KERNELS:
        case TK_M_SUBSYSTEMS:
        case TK_M_DRIVERS:
        case TK_M_TAGS:
        case TK_M_PROGRAM:
        case TK_M_IMPORT_FILE:
        case TK_M_IMPORT_PROG:
        case TK_M_IMPORT_DB:
        case TK_M_IMPORT_CMDLINE:
        case TK_M_IMPORT_PARENT:
        case TK_M_RESULT:
        case TK_A_OWNER:
        case TK_A_GROUP:
        case TK_A_MODE:
896
        case TK_A_DEVLINK:
897 898 899 900
        case TK_A_NAME:
        case TK_A_GOTO:
        case TK_M_TAG:
        case TK_A_TAG:
901
                token->key.value_off = rules_add_string(rule_tmp->rules, value);
902 903
                break;
        case TK_M_IMPORT_BUILTIN:
904
                token->key.value_off = rules_add_string(rule_tmp->rules, value);
905 906 907 908 909 910 911 912
                token->key.builtin_cmd = *(enum udev_builtin_cmd *)data;
                break;
        case TK_M_ENV:
        case TK_M_ATTR:
        case TK_M_ATTRS:
        case TK_A_ATTR:
        case TK_A_ENV:
                attr = data;
913 914
                token->key.value_off = rules_add_string(rule_tmp->rules, value);
                token->key.attr_off = rules_add_string(rule_tmp->rules, attr);
915 916
                break;
        case TK_M_TEST:
917
                token->key.value_off = rules_add_string(rule_tmp->rules, value);
918 919 920 921 922 923 924
                if (data != NULL)
                        token->key.mode = *(mode_t *)data;
                break;
        case TK_A_STRING_ESCAPE_NONE:
        case TK_A_STRING_ESCAPE_REPLACE:
        case TK_A_DB_PERSIST:
                break;
925 926
        case TK_A_RUN_BUILTIN:
        case TK_A_RUN_PROGRAM:
927
                token->key.builtin_cmd = *(enum udev_builtin_cmd *)data;
928
                token->key.value_off = rules_add_string(rule_tmp->rules, value);
929 930 931 932 933 934 935 936 937 938 939 940 941 942 943
                break;
        case TK_A_INOTIFY_WATCH:
        case TK_A_DEVLINK_PRIO:
                token->key.devlink_prio = *(int *)data;
                break;
        case TK_A_OWNER_ID:
                token->key.uid = *(uid_t *)data;
                break;
        case TK_A_GROUP_ID:
                token->key.gid = *(gid_t *)data;
                break;
        case TK_A_MODE_ID:
                token->key.mode = *(mode_t *)data;
                break;
        case TK_A_STATIC_NODE:
944
                token->key.value_off = rules_add_string(rule_tmp->rules, value);
945 946 947 948 949 950 951 952 953 954
                break;
        case TK_M_EVENT_TIMEOUT:
                token->key.event_timeout = *(int *)data;
                break;
        case TK_RULE:
        case TK_M_PARENTS_MIN:
        case TK_M_PARENTS_MAX:
        case TK_M_MAX:
        case TK_END:
        case TK_UNSET:
955
                log_error("wrong type %u\n", type);
956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971
                return -1;
        }

        if (value != NULL && type < TK_M_MAX) {
                /* check if we need to split or call fnmatch() while matching rules */
                enum string_glob_type glob;
                int has_split;
                int has_glob;

                has_split = (strchr(value, '|') != NULL);
                has_glob = (strchr(value, '*') != NULL || strchr(value, '?') != NULL || strchr(value, '[') != NULL);
                if (has_split && has_glob) {
                        glob = GL_SPLIT_GLOB;
                } else if (has_split) {
                        glob = GL_SPLIT;
                } else if (has_glob) {
972
                        if (streq(value, "?*"))
973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004
                                glob = GL_SOMETHING;
                        else
                                glob = GL_GLOB;
                } else {
                        glob = GL_PLAIN;
                }
                token->key.glob = glob;
        }

        if (value != NULL && type > TK_M_MAX) {
                /* check if assigned value has substitution chars */
                if (value[0] == '[')
                        token->key.subst = SB_SUBSYS;
                else if (strchr(value, '%') != NULL || strchr(value, '$') != NULL)
                        token->key.subst = SB_FORMAT;
                else
                        token->key.subst = SB_NONE;
        }

        if (attr != NULL) {
                /* check if property/attribut name has substitution chars */
                if (attr[0] == '[')
                        token->key.attrsubst = SB_SUBSYS;
                else if (strchr(attr, '%') != NULL || strchr(attr, '$') != NULL)
                        token->key.attrsubst = SB_FORMAT;
                else
                        token->key.attrsubst = SB_NONE;
        }

        token->key.type = type;
        token->key.op = op;
        rule_tmp->token_cur++;
1005
        if (rule_tmp->token_cur >= ELEMENTSOF(rule_tmp->token)) {
1006
                log_error("temporary rule array too small\n");
1007 1008 1009
                return -1;
        }
        return 0;
1010
}
1011

1012 1013
static int sort_token(struct udev_rules *rules, struct rule_tmp *rule_tmp)
{
1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044
        unsigned int i;
        unsigned int start = 0;
        unsigned int end = rule_tmp->token_cur;

        for (i = 0; i < rule_tmp->token_cur; i++) {
                enum token_type next_val = TK_UNSET;
                unsigned int next_idx = 0;
                unsigned int j;

                /* find smallest value */
                for (j = start; j < end; j++) {
                        if (rule_tmp->token[j].type == TK_UNSET)
                                continue;
                        if (next_val == TK_UNSET || rule_tmp->token[j].type < next_val) {
                                next_val = rule_tmp->token[j].type;
                                next_idx = j;
                        }
                }

                /* add token and mark done */
                if (add_token(rules, &rule_tmp->token[next_idx]) != 0)
                        return -1;
                rule_tmp->token[next_idx].type = TK_UNSET;

                /* shrink range */
                if (next_idx == start)
                        start++;
                if (next_idx+1 == end)
                        end--;
        }
        return 0;
1045 1046
}

1047
static int add_rule(struct udev_rules *rules, char *line,
1048
                    const char *filename, unsigned int filename_off, unsigned int lineno)
1049
{
1050
        char *linepos;
1051
        const char *attr;
1052 1053 1054 1055 1056
        struct rule_tmp rule_tmp;

        memset(&rule_tmp, 0x00, sizeof(struct rule_tmp));
        rule_tmp.rules = rules;
        rule_tmp.rule.type = TK_RULE;
1057 1058 1059
        /* the offset in the rule is limited to unsigned short */
        if (filename_off < USHRT_MAX)
                rule_tmp.rule.rule.filename_off = filename_off;
1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070
        rule_tmp.rule.rule.filename_line = lineno;

        linepos = line;
        for (;;) {
                char *key;
                char *value;
                enum operation_type op;

                if (get_key(rules->udev, &linepos, &key, &op, &value) != 0)
                        break;

1071
                if (streq(key, "ACTION")) {
1072
                        if (op > OP_MATCH_MAX) {
1073
                                log_error("invalid ACTION operation\n");
1074 1075 1076 1077 1078 1079
                                goto invalid;
                        }
                        rule_add_key(&rule_tmp, TK_M_ACTION, op, value, NULL);
                        continue;
                }

1080
                if (streq(key, "DEVPATH")) {
1081
                        if (op > OP_MATCH_MAX) {
1082
                                log_error("invalid DEVPATH operation\n");
1083 1084 1085 1086 1087 1088
                                goto invalid;
                        }
                        rule_add_key(&rule_tmp, TK_M_DEVPATH, op, value, NULL);
                        continue;
                }

1089
                if (streq(key, "KERNEL")) {
1090
                        if (op > OP_MATCH_MAX) {
1091
                                log_error("invalid KERNEL operation\n");
1092 1093 1094 1095 1096 1097
                                goto invalid;
                        }
                        rule_add_key(&rule_tmp, TK_M_KERNEL, op, value, NULL);
                        continue;
                }

1098
                if (streq(key, "SUBSYSTEM")) {
1099
                        if (op > OP_MATCH_MAX) {
1100
                                log_error("invalid SUBSYSTEM operation\n");
1101 1102 1103
                                goto invalid;
                        }
                        /* bus, class, subsystem events should all be the same */
1104 1105 1106 1107
                        if (streq(value, "subsystem") ||
                            streq(value, "bus") ||
                            streq(value, "class")) {
                                if (streq(value, "bus") || streq(value, "class"))
1108
                                        log_error("'%s' must be specified as 'subsystem' \n"
1109 1110 1111 1112 1113 1114 1115
                                            "please fix it in %s:%u", value, filename, lineno);
                                rule_add_key(&rule_tmp, TK_M_SUBSYSTEM, op, "subsystem|class|bus", NULL);
                        } else
                                rule_add_key(&rule_tmp, TK_M_SUBSYSTEM, op, value, NULL);
                        continue;
                }

1116
                if (streq(key, "DRIVER")) {
1117
                        if (op > OP_MATCH_MAX) {
1118
                                log_error("invalid DRIVER operation\n");
1119 1120 1121 1122 1123 1124
                                goto invalid;
                        }
                        rule_add_key(&rule_tmp, TK_M_DRIVER, op, value, NULL);
                        continue;
                }

1125
                if (startswith(key, "ATTR{")) {
1126 1127
                        attr = get_key_attribute(rules->udev, key + sizeof("ATTR")-1);
                        if (attr == NULL) {
1128
                                log_error("error parsing ATTR attribute\n");
1129 1130 1131 1132 1133 1134 1135 1136 1137 1138
                                goto invalid;
                        }
                        if (op < OP_MATCH_MAX) {
                                rule_add_key(&rule_tmp, TK_M_ATTR, op, value, attr);
                        } else {
                                rule_add_key(&rule_tmp, TK_A_ATTR, op, value, attr);
                        }
                        continue;
                }

1139
                if (streq(key, "KERNELS")) {
1140
                        if (op > OP_MATCH_MAX) {
1141
                                log_error("invalid KERNELS operation\n");
1142 1143 1144 1145 1146 1147
                                goto invalid;
                        }
                        rule_add_key(&rule_tmp, TK_M_KERNELS, op, value, NULL);
                        continue;
                }

1148
                if (streq(key, "SUBSYSTEMS")) {
1149
                        if (op > OP_MATCH_MAX) {
1150
                                log_error("invalid SUBSYSTEMS operation\n");
1151 1152 1153 1154 1155 1156
                                goto invalid;
                        }
                        rule_add_key(&rule_tmp, TK_M_SUBSYSTEMS, op, value, NULL);
                        continue;
                }

1157
                if (streq(key, "DRIVERS")) {
1158
                        if (op > OP_MATCH_MAX) {
1159
                                log_error("invalid DRIVERS operation\n");
1160 1161 1162 1163 1164 1165
                                goto invalid;
                        }
                        rule_add_key(&rule_tmp, TK_M_DRIVERS, op, value, NULL);
                        continue;
                }

1166
                if (startswith(key, "ATTRS{")) {
1167
                        if (op > OP_MATCH_MAX) {
1168
                                log_error("invalid ATTRS operation\n");
1169 1170 1171 1172
                                goto invalid;
                        }
                        attr = get_key_attribute(rules->udev, key + sizeof("ATTRS")-1);
                        if (attr == NULL) {
1173
                                log_error("error parsing ATTRS attribute\n");
1174 1175
                                goto invalid;
                        }
1176
                        if (startswith(attr, "device/"))
1177
                                log_error("the 'device' link may not be available in a future kernel, "
1178 1179
                                    "please fix it in %s:%u", filename, lineno);
                        else if (strstr(attr, "../") != NULL)
1180
                                log_error("do not reference parent sysfs directories directly, "
1181 1182 1183 1184 1185
                                    "it may break with a future kernel, please fix it in %s:%u", filename, lineno);
                        rule_add_key(&rule_tmp, TK_M_ATTRS, op, value, attr);
                        continue;
                }

1186
                if (streq(key, "TAGS")) {
1187
                        if (