]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/udev/udev-rules.c
coccinelle: add reallocarray() coccinelle script
[thirdparty/systemd.git] / src / udev / udev-rules.c
CommitLineData
e7145211 1/* SPDX-License-Identifier: GPL-2.0+ */
2232cac8 2/*
1298001e 3 * Copyright (C) 2003-2012 Kay Sievers <kay@vrfy.org>
2232cac8 4 *
55e9959b
KS
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
3d6d12c6 9 *
55e9959b
KS
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
2232cac8 14 *
55e9959b
KS
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
2232cac8
GKH
17 */
18
2232cac8 19#include <ctype.h>
4f5dd394
LP
20#include <errno.h>
21#include <fcntl.h>
cea61f5c 22#include <fnmatch.h>
4f5dd394
LP
23#include <limits.h>
24#include <stdbool.h>
25#include <stddef.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
959e8b5d 29#include <time.h>
4f5dd394 30#include <unistd.h>
2232cac8 31
b5efdb8a 32#include "alloc-util.h"
2c21044f 33#include "conf-files.h"
8fb3f009 34#include "dirent-util.h"
4f5dd394 35#include "escape.h"
3ffd4af2 36#include "fd-util.h"
fdd21be6 37#include "fs-util.h"
7d50b32a 38#include "glob-util.h"
4f5dd394 39#include "path-util.h"
88b013b2 40#include "proc-cmdline.h"
8fcde012 41#include "stat-util.h"
f4850a1d 42#include "stdio-util.h"
915bf0f6 43#include "strbuf.h"
07630cea 44#include "string-util.h"
84b6ad70 45#include "strv.h"
f4cf2e5b 46#include "sysctl-util.h"
4f5dd394 47#include "udev.h"
b1d4f8e1 48#include "user-util.h"
07630cea 49#include "util.h"
2232cac8 50
a1525d17 51#define PREALLOC_TOKEN 2048
c7521974 52
4052400f 53struct uid_gid {
912541b0
KS
54 unsigned int name_off;
55 union {
a1525d17
KS
56 uid_t uid;
57 gid_t gid;
912541b0 58 };
4052400f
KS
59};
60
2ad8416d
ZJS
61static const char* const rules_dirs[] = {
62 "/etc/udev/rules.d",
63 "/run/udev/rules.d",
64 UDEVLIBEXECDIR "/rules.d",
4f5dd394
LP
65 NULL
66};
2ad8416d 67
4052400f 68struct udev_rules {
912541b0 69 struct udev *udev;
97f2d76d 70 usec_t dirs_ts_usec;
912541b0
KS
71 int resolve_names;
72
73 /* every key in the rules file becomes a token */
74 struct token *tokens;
75 unsigned int token_cur;
76 unsigned int token_max;
77
ab06eef8 78 /* all key strings are copied and de-duplicated in a single continuous string buffer */
915bf0f6 79 struct strbuf *strbuf;
912541b0
KS
80
81 /* during rule parsing, uid/gid lookup results are cached */
82 struct uid_gid *uids;
83 unsigned int uids_cur;
84 unsigned int uids_max;
85 struct uid_gid *gids;
86 unsigned int gids_cur;
87 unsigned int gids_max;
4052400f
KS
88};
89
915bf0f6
KS
90static char *rules_str(struct udev_rules *rules, unsigned int off) {
91 return rules->strbuf->buf + off;
92}
93
94static unsigned int rules_add_string(struct udev_rules *rules, const char *s) {
95 return strbuf_add_string(rules->strbuf, s, strlen(s));
96}
97
8e3ba377 98/* KEY=="", KEY!="", KEY+="", KEY-="", KEY="", KEY:="" */
b0f7409f 99enum operation_type {
912541b0 100 OP_UNSET,
b0f7409f 101
912541b0
KS
102 OP_MATCH,
103 OP_NOMATCH,
104 OP_MATCH_MAX,
b0f7409f 105
912541b0 106 OP_ADD,
8e3ba377 107 OP_REMOVE,
912541b0
KS
108 OP_ASSIGN,
109 OP_ASSIGN_FINAL,
c7521974
KS
110};
111
ac218d9c 112enum string_glob_type {
912541b0 113 GL_UNSET,
a1525d17 114 GL_PLAIN, /* no special chars */
912541b0 115 GL_GLOB, /* shell globs ?,*,[] */
a1525d17
KS
116 GL_SPLIT, /* multi-value A|B */
117 GL_SPLIT_GLOB, /* multi-value with glob A*|B* */
118 GL_SOMETHING, /* commonly used "?*" */
ac218d9c
KS
119};
120
32028733 121enum string_subst_type {
912541b0
KS
122 SB_UNSET,
123 SB_NONE,
124 SB_FORMAT,
125 SB_SUBSYS,
32028733
KS
126};
127
154a7b84 128/* tokens of a rule are sorted/handled in this order */
6880b25d 129enum token_type {
912541b0
KS
130 TK_UNSET,
131 TK_RULE,
132
a1525d17
KS
133 TK_M_ACTION, /* val */
134 TK_M_DEVPATH, /* val */
135 TK_M_KERNEL, /* val */
136 TK_M_DEVLINK, /* val */
137 TK_M_NAME, /* val */
138 TK_M_ENV, /* val, attr */
139 TK_M_TAG, /* val */
140 TK_M_SUBSYSTEM, /* val */
141 TK_M_DRIVER, /* val */
142 TK_M_WAITFOR, /* val */
143 TK_M_ATTR, /* val, attr */
f4cf2e5b 144 TK_M_SYSCTL, /* val, attr */
912541b0
KS
145
146 TK_M_PARENTS_MIN,
a1525d17 147 TK_M_KERNELS, /* val */
912541b0 148 TK_M_SUBSYSTEMS, /* val */
a1525d17
KS
149 TK_M_DRIVERS, /* val */
150 TK_M_ATTRS, /* val, attr */
151 TK_M_TAGS, /* val */
912541b0
KS
152 TK_M_PARENTS_MAX,
153
a1525d17 154 TK_M_TEST, /* val, mode_t */
a1525d17
KS
155 TK_M_PROGRAM, /* val */
156 TK_M_IMPORT_FILE, /* val */
157 TK_M_IMPORT_PROG, /* val */
158 TK_M_IMPORT_BUILTIN, /* val */
159 TK_M_IMPORT_DB, /* val */
160 TK_M_IMPORT_CMDLINE, /* val */
161 TK_M_IMPORT_PARENT, /* val */
162 TK_M_RESULT, /* val */
912541b0
KS
163 TK_M_MAX,
164
165 TK_A_STRING_ESCAPE_NONE,
166 TK_A_STRING_ESCAPE_REPLACE,
167 TK_A_DB_PERSIST,
a1525d17
KS
168 TK_A_INOTIFY_WATCH, /* int */
169 TK_A_DEVLINK_PRIO, /* int */
170 TK_A_OWNER, /* val */
171 TK_A_GROUP, /* val */
172 TK_A_MODE, /* val */
173 TK_A_OWNER_ID, /* uid_t */
174 TK_A_GROUP_ID, /* gid_t */
175 TK_A_MODE_ID, /* mode_t */
84b6ad70 176 TK_A_TAG, /* val */
a1525d17 177 TK_A_STATIC_NODE, /* val */
c26547d6 178 TK_A_SECLABEL, /* val, attr */
a1525d17 179 TK_A_ENV, /* val, attr */
a1525d17
KS
180 TK_A_NAME, /* val */
181 TK_A_DEVLINK, /* val */
182 TK_A_ATTR, /* val, attr */
f4cf2e5b 183 TK_A_SYSCTL, /* val, attr */
83cd6b75
KS
184 TK_A_RUN_BUILTIN, /* val, bool */
185 TK_A_RUN_PROGRAM, /* val, bool */
a1525d17 186 TK_A_GOTO, /* size_t */
912541b0
KS
187
188 TK_END,
c7521974
KS
189};
190
3f3aa9f5 191/* we try to pack stuff in a way that we take only 12 bytes per token */
4052400f 192struct token {
912541b0
KS
193 union {
194 unsigned char type; /* same in rule and key */
195 struct {
196 enum token_type type:8;
197 bool can_set_name:1;
198 bool has_static_node:1;
199 unsigned int unused:6;
200 unsigned short token_count;
201 unsigned int label_off;
202 unsigned short filename_off;
203 unsigned short filename_line;
204 } rule;
205 struct {
206 enum token_type type:8;
207 enum operation_type op:8;
208 enum string_glob_type glob:8;
209 enum string_subst_type subst:4;
210 enum string_subst_type attrsubst:4;
211 unsigned int value_off;
212 union {
213 unsigned int attr_off;
912541b0
KS
214 unsigned int rule_goto;
215 mode_t mode;
216 uid_t uid;
217 gid_t gid;
218 int devlink_prio;
912541b0
KS
219 int watch;
220 enum udev_builtin_cmd builtin_cmd;
221 };
222 } key;
223 };
4052400f
KS
224};
225
912541b0 226#define MAX_TK 64
4052400f 227struct rule_tmp {
912541b0
KS
228 struct udev_rules *rules;
229 struct token rule;
230 struct token token[MAX_TK];
231 unsigned int token_cur;
4052400f
KS
232};
233
baa30fbc 234#ifdef DEBUG
9ec6e95b 235static const char *operation_str(enum operation_type type) {
912541b0 236 static const char *operation_strs[] = {
a1525d17
KS
237 [OP_UNSET] = "UNSET",
238 [OP_MATCH] = "match",
239 [OP_NOMATCH] = "nomatch",
912541b0
KS
240 [OP_MATCH_MAX] = "MATCH_MAX",
241
a1525d17 242 [OP_ADD] = "add",
8e3ba377 243 [OP_REMOVE] = "remove",
a1525d17
KS
244 [OP_ASSIGN] = "assign",
245 [OP_ASSIGN_FINAL] = "assign-final",
912541b0
KS
246} ;
247
248 return operation_strs[type];
5d6a1fa6 249}
4052400f 250
9ec6e95b 251static const char *string_glob_str(enum string_glob_type type) {
912541b0 252 static const char *string_glob_strs[] = {
a1525d17
KS
253 [GL_UNSET] = "UNSET",
254 [GL_PLAIN] = "plain",
255 [GL_GLOB] = "glob",
256 [GL_SPLIT] = "split",
257 [GL_SPLIT_GLOB] = "split-glob",
258 [GL_SOMETHING] = "split-glob",
912541b0
KS
259 };
260
261 return string_glob_strs[type];
5d6a1fa6
KS
262}
263
9ec6e95b 264static const char *token_str(enum token_type type) {
912541b0 265 static const char *token_strs[] = {
a1525d17
KS
266 [TK_UNSET] = "UNSET",
267 [TK_RULE] = "RULE",
912541b0 268
a1525d17 269 [TK_M_ACTION] = "M ACTION",
912541b0 270 [TK_M_DEVPATH] = "M DEVPATH",
a1525d17 271 [TK_M_KERNEL] = "M KERNEL",
912541b0 272 [TK_M_DEVLINK] = "M DEVLINK",
a1525d17
KS
273 [TK_M_NAME] = "M NAME",
274 [TK_M_ENV] = "M ENV",
275 [TK_M_TAG] = "M TAG",
276 [TK_M_SUBSYSTEM] = "M SUBSYSTEM",
277 [TK_M_DRIVER] = "M DRIVER",
912541b0 278 [TK_M_WAITFOR] = "M WAITFOR",
a1525d17 279 [TK_M_ATTR] = "M ATTR",
f4cf2e5b 280 [TK_M_SYSCTL] = "M SYSCTL",
912541b0 281
a1525d17 282 [TK_M_PARENTS_MIN] = "M PARENTS_MIN",
912541b0 283 [TK_M_KERNELS] = "M KERNELS",
a1525d17 284 [TK_M_SUBSYSTEMS] = "M SUBSYSTEMS",
912541b0 285 [TK_M_DRIVERS] = "M DRIVERS",
a1525d17
KS
286 [TK_M_ATTRS] = "M ATTRS",
287 [TK_M_TAGS] = "M TAGS",
288 [TK_M_PARENTS_MAX] = "M PARENTS_MAX",
912541b0 289
a1525d17 290 [TK_M_TEST] = "M TEST",
912541b0 291 [TK_M_PROGRAM] = "M PROGRAM",
a1525d17
KS
292 [TK_M_IMPORT_FILE] = "M IMPORT_FILE",
293 [TK_M_IMPORT_PROG] = "M IMPORT_PROG",
294 [TK_M_IMPORT_BUILTIN] = "M IMPORT_BUILTIN",
295 [TK_M_IMPORT_DB] = "M IMPORT_DB",
296 [TK_M_IMPORT_CMDLINE] = "M IMPORT_CMDLINE",
297 [TK_M_IMPORT_PARENT] = "M IMPORT_PARENT",
298 [TK_M_RESULT] = "M RESULT",
299 [TK_M_MAX] = "M MAX",
300
301 [TK_A_STRING_ESCAPE_NONE] = "A STRING_ESCAPE_NONE",
302 [TK_A_STRING_ESCAPE_REPLACE] = "A STRING_ESCAPE_REPLACE",
303 [TK_A_DB_PERSIST] = "A DB_PERSIST",
304 [TK_A_INOTIFY_WATCH] = "A INOTIFY_WATCH",
305 [TK_A_DEVLINK_PRIO] = "A DEVLINK_PRIO",
306 [TK_A_OWNER] = "A OWNER",
307 [TK_A_GROUP] = "A GROUP",
308 [TK_A_MODE] = "A MODE",
309 [TK_A_OWNER_ID] = "A OWNER_ID",
310 [TK_A_GROUP_ID] = "A GROUP_ID",
311 [TK_A_STATIC_NODE] = "A STATIC_NODE",
c26547d6 312 [TK_A_SECLABEL] = "A SECLABEL",
912541b0 313 [TK_A_MODE_ID] = "A MODE_ID",
a1525d17
KS
314 [TK_A_ENV] = "A ENV",
315 [TK_A_TAG] = "A ENV",
316 [TK_A_NAME] = "A NAME",
912541b0 317 [TK_A_DEVLINK] = "A DEVLINK",
a1525d17 318 [TK_A_ATTR] = "A ATTR",
f4cf2e5b 319 [TK_A_SYSCTL] = "A SYSCTL",
83cd6b75
KS
320 [TK_A_RUN_BUILTIN] = "A RUN_BUILTIN",
321 [TK_A_RUN_PROGRAM] = "A RUN_PROGRAM",
a1525d17 322 [TK_A_GOTO] = "A GOTO",
912541b0 323
a1525d17 324 [TK_END] = "END",
912541b0
KS
325 };
326
327 return token_strs[type];
5d6a1fa6 328}
c7521974 329
9ec6e95b 330static void dump_token(struct udev_rules *rules, struct token *token) {
912541b0
KS
331 enum token_type type = token->type;
332 enum operation_type op = token->key.op;
333 enum string_glob_type glob = token->key.glob;
fa394301
LR
334 const char *value = rules_str(rules, token->key.value_off);
335 const char *attr = &rules->strbuf->buf[token->key.attr_off];
912541b0
KS
336
337 switch (type) {
338 case TK_RULE:
339 {
340 const char *tks_ptr = (char *)rules->tokens;
341 const char *tk_ptr = (char *)token;
342 unsigned int idx = (tk_ptr - tks_ptr) / sizeof(struct token);
343
9f6445e3 344 log_debug("* RULE %s:%u, token: %u, count: %u, label: '%s'",
fa394301 345 &rules->strbuf->buf[token->rule.filename_off], token->rule.filename_line,
baa30fbc 346 idx, token->rule.token_count,
fa394301 347 &rules->strbuf->buf[token->rule.label_off]);
912541b0
KS
348 break;
349 }
350 case TK_M_ACTION:
351 case TK_M_DEVPATH:
352 case TK_M_KERNEL:
353 case TK_M_SUBSYSTEM:
354 case TK_M_DRIVER:
355 case TK_M_WAITFOR:
356 case TK_M_DEVLINK:
357 case TK_M_NAME:
358 case TK_M_KERNELS:
359 case TK_M_SUBSYSTEMS:
360 case TK_M_DRIVERS:
361 case TK_M_TAGS:
362 case TK_M_PROGRAM:
363 case TK_M_IMPORT_FILE:
364 case TK_M_IMPORT_PROG:
365 case TK_M_IMPORT_DB:
366 case TK_M_IMPORT_CMDLINE:
367 case TK_M_IMPORT_PARENT:
368 case TK_M_RESULT:
369 case TK_A_NAME:
370 case TK_A_DEVLINK:
371 case TK_A_OWNER:
372 case TK_A_GROUP:
373 case TK_A_MODE:
83cd6b75
KS
374 case TK_A_RUN_BUILTIN:
375 case TK_A_RUN_PROGRAM:
9f6445e3 376 log_debug("%s %s '%s'(%s)",
baa30fbc 377 token_str(type), operation_str(op), value, string_glob_str(glob));
912541b0
KS
378 break;
379 case TK_M_IMPORT_BUILTIN:
9f6445e3 380 log_debug("%s %i '%s'", token_str(type), token->key.builtin_cmd, value);
912541b0
KS
381 break;
382 case TK_M_ATTR:
f4cf2e5b 383 case TK_M_SYSCTL:
912541b0
KS
384 case TK_M_ATTRS:
385 case TK_M_ENV:
386 case TK_A_ATTR:
f4cf2e5b 387 case TK_A_SYSCTL:
912541b0 388 case TK_A_ENV:
9f6445e3 389 log_debug("%s %s '%s' '%s'(%s)",
baa30fbc 390 token_str(type), operation_str(op), attr, value, string_glob_str(glob));
912541b0
KS
391 break;
392 case TK_M_TAG:
393 case TK_A_TAG:
9f6445e3 394 log_debug("%s %s '%s'", token_str(type), operation_str(op), value);
912541b0
KS
395 break;
396 case TK_A_STRING_ESCAPE_NONE:
397 case TK_A_STRING_ESCAPE_REPLACE:
398 case TK_A_DB_PERSIST:
9f6445e3 399 log_debug("%s", token_str(type));
912541b0
KS
400 break;
401 case TK_M_TEST:
9f6445e3 402 log_debug("%s %s '%s'(%s) %#o",
baa30fbc 403 token_str(type), operation_str(op), value, string_glob_str(glob), token->key.mode);
912541b0
KS
404 break;
405 case TK_A_INOTIFY_WATCH:
9f6445e3 406 log_debug("%s %u", token_str(type), token->key.watch);
912541b0
KS
407 break;
408 case TK_A_DEVLINK_PRIO:
9f6445e3 409 log_debug("%s %u", token_str(type), token->key.devlink_prio);
912541b0
KS
410 break;
411 case TK_A_OWNER_ID:
9f6445e3 412 log_debug("%s %s %u", token_str(type), operation_str(op), token->key.uid);
912541b0
KS
413 break;
414 case TK_A_GROUP_ID:
9f6445e3 415 log_debug("%s %s %u", token_str(type), operation_str(op), token->key.gid);
912541b0
KS
416 break;
417 case TK_A_MODE_ID:
9f6445e3 418 log_debug("%s %s %#o", token_str(type), operation_str(op), token->key.mode);
912541b0
KS
419 break;
420 case TK_A_STATIC_NODE:
9f6445e3 421 log_debug("%s '%s'", token_str(type), value);
912541b0 422 break;
c26547d6 423 case TK_A_SECLABEL:
9f6445e3 424 log_debug("%s %s '%s' '%s'", token_str(type), operation_str(op), attr, value);
c26547d6 425 break;
912541b0 426 case TK_A_GOTO:
9f6445e3 427 log_debug("%s '%s' %u", token_str(type), value, token->key.rule_goto);
912541b0
KS
428 break;
429 case TK_END:
9f6445e3 430 log_debug("* %s", token_str(type));
912541b0
KS
431 break;
432 case TK_M_PARENTS_MIN:
433 case TK_M_PARENTS_MAX:
434 case TK_M_MAX:
435 case TK_UNSET:
9f6445e3 436 log_debug("unknown type %u", type);
912541b0
KS
437 break;
438 }
4052400f 439}
154a7b84 440
9ec6e95b 441static void dump_rules(struct udev_rules *rules) {
912541b0
KS
442 unsigned int i;
443
fa394301 444 log_debug("dumping %u (%zu bytes) tokens, %zu (%zu bytes) strings",
baa30fbc
KS
445 rules->token_cur,
446 rules->token_cur * sizeof(struct token),
fa394301
LR
447 rules->strbuf->nodes_count,
448 rules->strbuf->len);
f168c273 449 for (i = 0; i < rules->token_cur; i++)
912541b0 450 dump_token(rules, &rules->tokens[i]);
4052400f
KS
451}
452#else
4052400f
KS
453static inline void dump_token(struct udev_rules *rules, struct token *token) {}
454static inline void dump_rules(struct udev_rules *rules) {}
baa30fbc 455#endif /* DEBUG */
c7521974 456
9ec6e95b 457static int add_token(struct udev_rules *rules, struct token *token) {
912541b0
KS
458 /* grow buffer if needed */
459 if (rules->token_cur+1 >= rules->token_max) {
460 struct token *tokens;
461 unsigned int add;
462
463 /* double the buffer size */
464 add = rules->token_max;
465 if (add < 8)
466 add = 8;
467
62d74c78 468 tokens = reallocarray(rules->tokens, rules->token_max + add, sizeof(struct token));
912541b0
KS
469 if (tokens == NULL)
470 return -1;
912541b0
KS
471 rules->tokens = tokens;
472 rules->token_max += add;
473 }
474 memcpy(&rules->tokens[rules->token_cur], token, sizeof(struct token));
475 rules->token_cur++;
476 return 0;
c7521974 477}
a27cd06c 478
2da03cbf
ZJS
479static void log_unknown_owner(int error, const char *entity, const char *owner) {
480 if (IN_SET(abs(error), ENOENT, ESRCH))
481 log_error("Specified %s '%s' unknown", entity, owner);
482 else
483 log_error_errno(error, "Error resolving %s '%s': %m", entity, owner);
484}
485
9ec6e95b 486static uid_t add_uid(struct udev_rules *rules, const char *owner) {
912541b0 487 unsigned int i;
23bf8dd7 488 uid_t uid = 0;
912541b0 489 unsigned int off;
23bf8dd7 490 int r;
912541b0
KS
491
492 /* lookup, if we know it already */
493 for (i = 0; i < rules->uids_cur; i++) {
494 off = rules->uids[i].name_off;
915bf0f6 495 if (streq(rules_str(rules, off), owner)) {
912541b0 496 uid = rules->uids[i].uid;
912541b0
KS
497 return uid;
498 }
499 }
23bf8dd7 500 r = get_user_creds(&owner, &uid, NULL, NULL, NULL);
2da03cbf
ZJS
501 if (r < 0)
502 log_unknown_owner(r, "user", owner);
912541b0
KS
503
504 /* grow buffer if needed */
505 if (rules->uids_cur+1 >= rules->uids_max) {
506 struct uid_gid *uids;
507 unsigned int add;
508
509 /* double the buffer size */
510 add = rules->uids_max;
511 if (add < 1)
512 add = 8;
513
62d74c78 514 uids = reallocarray(rules->uids, rules->uids_max + add, sizeof(struct uid_gid));
912541b0
KS
515 if (uids == NULL)
516 return uid;
912541b0
KS
517 rules->uids = uids;
518 rules->uids_max += add;
519 }
520 rules->uids[rules->uids_cur].uid = uid;
915bf0f6 521 off = rules_add_string(rules, owner);
912541b0
KS
522 if (off <= 0)
523 return uid;
524 rules->uids[rules->uids_cur].name_off = off;
525 rules->uids_cur++;
526 return uid;
154a7b84
KS
527}
528
9ec6e95b 529static gid_t add_gid(struct udev_rules *rules, const char *group) {
912541b0 530 unsigned int i;
23bf8dd7 531 gid_t gid = 0;
912541b0 532 unsigned int off;
23bf8dd7 533 int r;
912541b0
KS
534
535 /* lookup, if we know it already */
536 for (i = 0; i < rules->gids_cur; i++) {
537 off = rules->gids[i].name_off;
915bf0f6 538 if (streq(rules_str(rules, off), group)) {
912541b0 539 gid = rules->gids[i].gid;
912541b0
KS
540 return gid;
541 }
542 }
23bf8dd7 543 r = get_group_creds(&group, &gid);
2da03cbf
ZJS
544 if (r < 0)
545 log_unknown_owner(r, "group", group);
912541b0
KS
546
547 /* grow buffer if needed */
548 if (rules->gids_cur+1 >= rules->gids_max) {
549 struct uid_gid *gids;
550 unsigned int add;
551
552 /* double the buffer size */
553 add = rules->gids_max;
554 if (add < 1)
555 add = 8;
556
62d74c78 557 gids = reallocarray(rules->gids, rules->gids_max + add, sizeof(struct uid_gid));
912541b0
KS
558 if (gids == NULL)
559 return gid;
912541b0
KS
560 rules->gids = gids;
561 rules->gids_max += add;
562 }
563 rules->gids[rules->gids_cur].gid = gid;
915bf0f6 564 off = rules_add_string(rules, group);
912541b0
KS
565 if (off <= 0)
566 return gid;
567 rules->gids[rules->gids_cur].name_off = off;
568 rules->gids_cur++;
569 return gid;
154a7b84
KS
570}
571
9ec6e95b 572static int import_property_from_string(struct udev_device *dev, char *line) {
912541b0
KS
573 char *key;
574 char *val;
575 size_t len;
576
577 /* find key */
578 key = line;
579 while (isspace(key[0]))
580 key++;
581
582 /* comment or empty line */
4c701096 583 if (IN_SET(key[0], '#', '\0'))
912541b0
KS
584 return -1;
585
586 /* split key/value */
587 val = strchr(key, '=');
588 if (val == NULL)
589 return -1;
590 val[0] = '\0';
591 val++;
592
593 /* find value */
594 while (isspace(val[0]))
595 val++;
596
597 /* terminate key */
598 len = strlen(key);
599 if (len == 0)
600 return -1;
601 while (isspace(key[len-1]))
602 len--;
603 key[len] = '\0';
604
605 /* terminate value */
606 len = strlen(val);
607 if (len == 0)
608 return -1;
609 while (isspace(val[len-1]))
610 len--;
611 val[len] = '\0';
612
613 if (len == 0)
614 return -1;
615
616 /* unquote */
4c701096 617 if (IN_SET(val[0], '"', '\'')) {
cd05bb8b 618 if (len == 1 || val[len-1] != val[0]) {
9f6445e3 619 log_debug("inconsistent quoting: '%s', skip", line);
912541b0
KS
620 return -1;
621 }
622 val[len-1] = '\0';
623 val++;
624 }
625
df546eb5 626 udev_device_add_property(dev, key, val);
0aa5b4d1 627
912541b0 628 return 0;
be4bedd1
KS
629}
630
9ec6e95b 631static int import_file_into_properties(struct udev_device *dev, const char *filename) {
912541b0
KS
632 FILE *f;
633 char line[UTIL_LINE_SIZE];
634
47ef94ac 635 f = fopen(filename, "re");
912541b0
KS
636 if (f == NULL)
637 return -1;
638 while (fgets(line, sizeof(line), f) != NULL)
639 import_property_from_string(dev, line);
640 fclose(f);
641 return 0;
bd0ed2ff
KS
642}
643
dd5eddd2
KS
644static int import_program_into_properties(struct udev_event *event,
645 usec_t timeout_usec,
67117413 646 usec_t timeout_warn_usec,
8314de1d 647 const char *program) {
912541b0
KS
648 char result[UTIL_LINE_SIZE];
649 char *line;
650 int err;
651
bbf35206 652 err = udev_event_spawn(event, timeout_usec, timeout_warn_usec, true, program, result, sizeof(result));
912541b0
KS
653 if (err < 0)
654 return err;
655
656 line = result;
657 while (line != NULL) {
658 char *pos;
659
660 pos = strchr(line, '\n');
661 if (pos != NULL) {
662 pos[0] = '\0';
663 pos = &pos[1];
664 }
bbf35206 665 import_property_from_string(event->dev, line);
912541b0
KS
666 line = pos;
667 }
668 return 0;
319c6700
KS
669}
670
9ec6e95b 671static int import_parent_into_properties(struct udev_device *dev, const char *filter) {
912541b0
KS
672 struct udev_device *dev_parent;
673 struct udev_list_entry *list_entry;
674
3b64e4d4
TG
675 assert(dev);
676 assert(filter);
677
912541b0
KS
678 dev_parent = udev_device_get_parent(dev);
679 if (dev_parent == NULL)
680 return -1;
681
912541b0
KS
682 udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(dev_parent)) {
683 const char *key = udev_list_entry_get_name(list_entry);
684 const char *val = udev_list_entry_get_value(list_entry);
685
ece174c5 686 if (fnmatch(filter, key, 0) == 0)
df546eb5 687 udev_device_add_property(dev, key, val);
912541b0
KS
688 }
689 return 0;
e2ecb34f
KS
690}
691
f4850a1d
ZJS
692static void attr_subst_subdir(char *attr, size_t len) {
693 const char *pos, *tail, *path;
694 _cleanup_closedir_ DIR *dir = NULL;
695 struct dirent *dent;
696
697 pos = strstr(attr, "/*/");
698 if (!pos)
699 return;
700
701 tail = pos + 2;
702 path = strndupa(attr, pos - attr + 1); /* include slash at end */
703 dir = opendir(path);
704 if (dir == NULL)
705 return;
706
8fb3f009 707 FOREACH_DIRENT_ALL(dent, dir, break)
f4850a1d 708 if (dent->d_name[0] != '.') {
916a8d43 709 char n[strlen(dent->d_name) + strlen(tail) + 1];
f4850a1d 710
916a8d43
ZJS
711 strscpyl(n, sizeof n, dent->d_name, tail, NULL);
712 if (faccessat(dirfd(dir), n, F_OK, 0) == 0) {
f4850a1d
ZJS
713 strscpyl(attr, len, path, n, NULL);
714 break;
912541b0 715 }
912541b0 716 }
0ea5e96e
KS
717}
718
9ec6e95b 719static int get_key(struct udev *udev, char **line, char **key, enum operation_type *op, char **value) {
912541b0
KS
720 char *linepos;
721 char *temp;
7e760b79 722 unsigned i, j;
912541b0
KS
723
724 linepos = *line;
725 if (linepos == NULL || linepos[0] == '\0')
726 return -1;
727
728 /* skip whitespace */
729 while (isspace(linepos[0]) || linepos[0] == ',')
730 linepos++;
731
732 /* get the key */
733 if (linepos[0] == '\0')
734 return -1;
735 *key = linepos;
736
737 for (;;) {
738 linepos++;
739 if (linepos[0] == '\0')
740 return -1;
741 if (isspace(linepos[0]))
742 break;
743 if (linepos[0] == '=')
744 break;
4c701096 745 if (IN_SET(linepos[0], '+', '-', '!', ':'))
912541b0
KS
746 if (linepos[1] == '=')
747 break;
748 }
749
750 /* remember end of key */
751 temp = linepos;
752
753 /* skip whitespace after key */
754 while (isspace(linepos[0]))
755 linepos++;
756 if (linepos[0] == '\0')
757 return -1;
758
759 /* get operation type */
760 if (linepos[0] == '=' && linepos[1] == '=') {
761 *op = OP_MATCH;
762 linepos += 2;
763 } else if (linepos[0] == '!' && linepos[1] == '=') {
764 *op = OP_NOMATCH;
765 linepos += 2;
766 } else if (linepos[0] == '+' && linepos[1] == '=') {
767 *op = OP_ADD;
768 linepos += 2;
8e3ba377
DH
769 } else if (linepos[0] == '-' && linepos[1] == '=') {
770 *op = OP_REMOVE;
771 linepos += 2;
912541b0
KS
772 } else if (linepos[0] == '=') {
773 *op = OP_ASSIGN;
774 linepos++;
775 } else if (linepos[0] == ':' && linepos[1] == '=') {
776 *op = OP_ASSIGN_FINAL;
777 linepos += 2;
778 } else
779 return -1;
780
781 /* terminate key */
782 temp[0] = '\0';
783
784 /* skip whitespace after operator */
785 while (isspace(linepos[0]))
786 linepos++;
787 if (linepos[0] == '\0')
788 return -1;
789
790 /* get the value */
791 if (linepos[0] == '"')
792 linepos++;
793 else
794 return -1;
795 *value = linepos;
796
797 /* terminate */
7e760b79
FB
798 for (i = 0, j = 0; ; i++, j++) {
799
800 if (linepos[i] == '"')
801 break;
802
803 if (linepos[i] == '\0')
804 return -1;
805
806 /* double quotes can be escaped */
807 if (linepos[i] == '\\')
808 if (linepos[i+1] == '"')
809 i++;
810
811 linepos[j] = linepos[i];
812 }
813 linepos[j] = '\0';
912541b0
KS
814
815 /* move line to next key */
7e760b79 816 *line = linepos + i + 1;
912541b0 817 return 0;
6880b25d 818}
95776dc6 819
6880b25d 820/* extract possible KEY{attr} */
9ec6e95b 821static const char *get_key_attribute(struct udev *udev, char *str) {
912541b0
KS
822 char *pos;
823 char *attr;
824
825 attr = strchr(str, '{');
826 if (attr != NULL) {
827 attr++;
828 pos = strchr(attr, '}');
829 if (pos == NULL) {
25f027c5 830 log_error("Missing closing brace for format");
912541b0
KS
831 return NULL;
832 }
833 pos[0] = '\0';
912541b0
KS
834 return attr;
835 }
836 return NULL;
6880b25d 837}
95776dc6 838
19a8e656
ZJS
839static void rule_add_key(struct rule_tmp *rule_tmp, enum token_type type,
840 enum operation_type op,
841 const char *value, const void *data) {
842 struct token *token = rule_tmp->token + rule_tmp->token_cur;
912541b0
KS
843 const char *attr = NULL;
844
19a8e656 845 assert(rule_tmp->token_cur < ELEMENTSOF(rule_tmp->token));
29804cc1 846 memzero(token, sizeof(struct token));
912541b0
KS
847
848 switch (type) {
849 case TK_M_ACTION:
850 case TK_M_DEVPATH:
851 case TK_M_KERNEL:
852 case TK_M_SUBSYSTEM:
853 case TK_M_DRIVER:
854 case TK_M_WAITFOR:
855 case TK_M_DEVLINK:
856 case TK_M_NAME:
857 case TK_M_KERNELS:
858 case TK_M_SUBSYSTEMS:
859 case TK_M_DRIVERS:
860 case TK_M_TAGS:
861 case TK_M_PROGRAM:
862 case TK_M_IMPORT_FILE:
863 case TK_M_IMPORT_PROG:
864 case TK_M_IMPORT_DB:
865 case TK_M_IMPORT_CMDLINE:
866 case TK_M_IMPORT_PARENT:
867 case TK_M_RESULT:
868 case TK_A_OWNER:
869 case TK_A_GROUP:
870 case TK_A_MODE:
8a173387 871 case TK_A_DEVLINK:
912541b0
KS
872 case TK_A_NAME:
873 case TK_A_GOTO:
874 case TK_M_TAG:
875 case TK_A_TAG:
d6f116a7 876 case TK_A_STATIC_NODE:
915bf0f6 877 token->key.value_off = rules_add_string(rule_tmp->rules, value);
912541b0
KS
878 break;
879 case TK_M_IMPORT_BUILTIN:
915bf0f6 880 token->key.value_off = rules_add_string(rule_tmp->rules, value);
912541b0
KS
881 token->key.builtin_cmd = *(enum udev_builtin_cmd *)data;
882 break;
883 case TK_M_ENV:
884 case TK_M_ATTR:
f4cf2e5b 885 case TK_M_SYSCTL:
912541b0
KS
886 case TK_M_ATTRS:
887 case TK_A_ATTR:
f4cf2e5b 888 case TK_A_SYSCTL:
912541b0 889 case TK_A_ENV:
c26547d6 890 case TK_A_SECLABEL:
912541b0 891 attr = data;
915bf0f6
KS
892 token->key.value_off = rules_add_string(rule_tmp->rules, value);
893 token->key.attr_off = rules_add_string(rule_tmp->rules, attr);
912541b0 894 break;
912541b0 895 case TK_M_TEST:
915bf0f6 896 token->key.value_off = rules_add_string(rule_tmp->rules, value);
912541b0
KS
897 if (data != NULL)
898 token->key.mode = *(mode_t *)data;
899 break;
900 case TK_A_STRING_ESCAPE_NONE:
901 case TK_A_STRING_ESCAPE_REPLACE:
902 case TK_A_DB_PERSIST:
903 break;
83cd6b75 904 case TK_A_RUN_BUILTIN:
83cd6b75 905 case TK_A_RUN_PROGRAM:
4590cfe4 906 token->key.builtin_cmd = *(enum udev_builtin_cmd *)data;
915bf0f6 907 token->key.value_off = rules_add_string(rule_tmp->rules, value);
912541b0
KS
908 break;
909 case TK_A_INOTIFY_WATCH:
910 case TK_A_DEVLINK_PRIO:
911 token->key.devlink_prio = *(int *)data;
912 break;
913 case TK_A_OWNER_ID:
914 token->key.uid = *(uid_t *)data;
915 break;
916 case TK_A_GROUP_ID:
917 token->key.gid = *(gid_t *)data;
918 break;
919 case TK_A_MODE_ID:
920 token->key.mode = *(mode_t *)data;
921 break;
912541b0
KS
922 case TK_RULE:
923 case TK_M_PARENTS_MIN:
924 case TK_M_PARENTS_MAX:
925 case TK_M_MAX:
926 case TK_END:
927 case TK_UNSET:
19a8e656 928 assert_not_reached("wrong type");
912541b0
KS
929 }
930
931 if (value != NULL && type < TK_M_MAX) {
932 /* check if we need to split or call fnmatch() while matching rules */
933 enum string_glob_type glob;
934 int has_split;
935 int has_glob;
936
937 has_split = (strchr(value, '|') != NULL);
e3e0314b 938 has_glob = string_is_glob(value);
912541b0
KS
939 if (has_split && has_glob) {
940 glob = GL_SPLIT_GLOB;
941 } else if (has_split) {
942 glob = GL_SPLIT;
943 } else if (has_glob) {
33502ffe 944 if (streq(value, "?*"))
912541b0
KS
945 glob = GL_SOMETHING;
946 else
947 glob = GL_GLOB;
948 } else {
949 glob = GL_PLAIN;
950 }
951 token->key.glob = glob;
952 }
953
954 if (value != NULL && type > TK_M_MAX) {
955 /* check if assigned value has substitution chars */
956 if (value[0] == '[')
957 token->key.subst = SB_SUBSYS;
958 else if (strchr(value, '%') != NULL || strchr(value, '$') != NULL)
959 token->key.subst = SB_FORMAT;
960 else
961 token->key.subst = SB_NONE;
962 }
963
964 if (attr != NULL) {
d6f116a7 965 /* check if property/attribute name has substitution chars */
912541b0
KS
966 if (attr[0] == '[')
967 token->key.attrsubst = SB_SUBSYS;
968 else if (strchr(attr, '%') != NULL || strchr(attr, '$') != NULL)
969 token->key.attrsubst = SB_FORMAT;
970 else
971 token->key.attrsubst = SB_NONE;
972 }
973
974 token->key.type = type;
975 token->key.op = op;
976 rule_tmp->token_cur++;
6880b25d 977}
e57e7bc1 978
9ec6e95b 979static int sort_token(struct udev_rules *rules, struct rule_tmp *rule_tmp) {
912541b0
KS
980 unsigned int i;
981 unsigned int start = 0;
982 unsigned int end = rule_tmp->token_cur;
983
984 for (i = 0; i < rule_tmp->token_cur; i++) {
985 enum token_type next_val = TK_UNSET;
986 unsigned int next_idx = 0;
987 unsigned int j;
988
989 /* find smallest value */
990 for (j = start; j < end; j++) {
991 if (rule_tmp->token[j].type == TK_UNSET)
992 continue;
993 if (next_val == TK_UNSET || rule_tmp->token[j].type < next_val) {
994 next_val = rule_tmp->token[j].type;
995 next_idx = j;
996 }
997 }
998
999 /* add token and mark done */
1000 if (add_token(rules, &rule_tmp->token[next_idx]) != 0)
1001 return -1;
1002 rule_tmp->token[next_idx].type = TK_UNSET;
1003
1004 /* shrink range */
1005 if (next_idx == start)
1006 start++;
1007 if (next_idx+1 == end)
1008 end--;
1009 }
1010 return 0;
724257d9
GKH
1011}
1012
19a8e656
ZJS
1013#define LOG_RULE_ERROR(fmt, ...) log_error("Invalid rule %s:%u: " fmt, filename, lineno, ##__VA_ARGS__)
1014#define LOG_RULE_WARNING(fmt, ...) log_warning("%s:%u: " fmt, filename, lineno, ##__VA_ARGS__)
1015#define LOG_RULE_DEBUG(fmt, ...) log_debug("%s:%u: " fmt, filename, lineno, ##__VA_ARGS__)
1016#define LOG_AND_RETURN(fmt, ...) { LOG_RULE_ERROR(fmt, __VA_ARGS__); return; }
1017
4fd6af76
ZJS
1018static void add_rule(struct udev_rules *rules, char *line,
1019 const char *filename, unsigned int filename_off, unsigned int lineno) {
912541b0 1020 char *linepos;
04a9d3a0 1021 const char *attr;
84198c18
TG
1022 struct rule_tmp rule_tmp = {
1023 .rules = rules,
1024 .rule.type = TK_RULE,
1025 };
912541b0 1026
775f8b3c
KS
1027 /* the offset in the rule is limited to unsigned short */
1028 if (filename_off < USHRT_MAX)
1029 rule_tmp.rule.rule.filename_off = filename_off;
912541b0
KS
1030 rule_tmp.rule.rule.filename_line = lineno;
1031
1032 linepos = line;
1033 for (;;) {
1034 char *key;
1035 char *value;
1036 enum operation_type op;
1037
e736cf35 1038 if (get_key(rules->udev, &linepos, &key, &op, &value) != 0) {
3cf0f8f7
DR
1039 /* Avoid erroring on trailing whitespace. This is probably rare
1040 * so save the work for the error case instead of always trying
1041 * to strip the trailing whitespace with strstrip(). */
1042 while (isblank(*linepos))
1043 linepos++;
1044
e736cf35
DR
1045 /* If we aren't at the end of the line, this is a parsing error.
1046 * Make a best effort to describe where the problem is. */
2783fe06 1047 if (!strchr(NEWLINE, *linepos)) {
6501b52d 1048 char buf[2] = {*linepos};
1291bc89
ZJS
1049 _cleanup_free_ char *tmp;
1050
1051 tmp = cescape(buf);
ff49bc32 1052 log_error("invalid key/value pair in file %s on line %u, starting at character %tu ('%s')",
1291bc89 1053 filename, lineno, linepos - line + 1, tmp);
6501b52d 1054 if (*linepos == '#')
75716dd8 1055 log_error("hint: comments can only start at beginning of line");
1291bc89 1056 }
912541b0 1057 break;
e736cf35 1058 }
912541b0 1059
19a8e656
ZJS
1060 if (rule_tmp.token_cur >= ELEMENTSOF(rule_tmp.token))
1061 LOG_AND_RETURN("temporary rule array too small, aborting event processing with %u items", rule_tmp.token_cur);
1062
33502ffe 1063 if (streq(key, "ACTION")) {
19a8e656
ZJS
1064 if (op > OP_MATCH_MAX)
1065 LOG_AND_RETURN("invalid %s operation", key);
1066
912541b0 1067 rule_add_key(&rule_tmp, TK_M_ACTION, op, value, NULL);
912541b0 1068
99f16bb8 1069 } else if (streq(key, "DEVPATH")) {
19a8e656
ZJS
1070 if (op > OP_MATCH_MAX)
1071 LOG_AND_RETURN("invalid %s operation", key);
1072
912541b0 1073 rule_add_key(&rule_tmp, TK_M_DEVPATH, op, value, NULL);
912541b0 1074
99f16bb8 1075 } else if (streq(key, "KERNEL")) {
19a8e656
ZJS
1076 if (op > OP_MATCH_MAX)
1077 LOG_AND_RETURN("invalid %s operation", key);
1078
912541b0 1079 rule_add_key(&rule_tmp, TK_M_KERNEL, op, value, NULL);
912541b0 1080
99f16bb8 1081 } else if (streq(key, "SUBSYSTEM")) {
19a8e656
ZJS
1082 if (op > OP_MATCH_MAX)
1083 LOG_AND_RETURN("invalid %s operation", key);
1084
912541b0 1085 /* bus, class, subsystem events should all be the same */
99f16bb8
ZJS
1086 if (STR_IN_SET(value, "subsystem", "bus", "class")) {
1087 if (!streq(value, "subsystem"))
19a8e656
ZJS
1088 LOG_RULE_WARNING("'%s' must be specified as 'subsystem'; please fix", value);
1089
912541b0
KS
1090 rule_add_key(&rule_tmp, TK_M_SUBSYSTEM, op, "subsystem|class|bus", NULL);
1091 } else
1092 rule_add_key(&rule_tmp, TK_M_SUBSYSTEM, op, value, NULL);
912541b0 1093
99f16bb8 1094 } else if (streq(key, "DRIVER")) {
19a8e656
ZJS
1095 if (op > OP_MATCH_MAX)
1096 LOG_AND_RETURN("invalid %s operation", key);
1097
912541b0 1098 rule_add_key(&rule_tmp, TK_M_DRIVER, op, value, NULL);
912541b0 1099
99f16bb8 1100 } else if (startswith(key, "ATTR{")) {
fbd0b64f
LP
1101 attr = get_key_attribute(rules->udev,
1102 key + STRLEN("ATTR"));
19a8e656
ZJS
1103 if (attr == NULL)
1104 LOG_AND_RETURN("error parsing %s attribute", "ATTR");
1105
1106 if (op == OP_REMOVE)
1107 LOG_AND_RETURN("invalid %s operation", "ATTR");
1108
f4cf2e5b 1109 if (op < OP_MATCH_MAX)
912541b0 1110 rule_add_key(&rule_tmp, TK_M_ATTR, op, value, attr);
f4cf2e5b 1111 else
912541b0 1112 rule_add_key(&rule_tmp, TK_A_ATTR, op, value, attr);
f4cf2e5b 1113
99f16bb8 1114 } else if (startswith(key, "SYSCTL{")) {
fbd0b64f
LP
1115 attr = get_key_attribute(rules->udev,
1116 key + STRLEN("SYSCTL"));
19a8e656
ZJS
1117 if (attr == NULL)
1118 LOG_AND_RETURN("error parsing %s attribute", "ATTR");
1119
1120 if (op == OP_REMOVE)
1121 LOG_AND_RETURN("invalid %s operation", "ATTR");
1122
f4cf2e5b
KS
1123 if (op < OP_MATCH_MAX)
1124 rule_add_key(&rule_tmp, TK_M_SYSCTL, op, value, attr);
1125 else
1126 rule_add_key(&rule_tmp, TK_A_SYSCTL, op, value, attr);
912541b0 1127
99f16bb8 1128 } else if (startswith(key, "SECLABEL{")) {
fbd0b64f
LP
1129 attr = get_key_attribute(rules->udev,
1130 key + STRLEN("SECLABEL"));
19a8e656
ZJS
1131 if (attr == NULL)
1132 LOG_AND_RETURN("error parsing %s attribute", "SECLABEL");
1133
1134 if (op == OP_REMOVE)
1135 LOG_AND_RETURN("invalid %s operation", "SECLABEL");
c26547d6
KS
1136
1137 rule_add_key(&rule_tmp, TK_A_SECLABEL, op, value, attr);
c26547d6 1138
99f16bb8 1139 } else if (streq(key, "KERNELS")) {
19a8e656
ZJS
1140 if (op > OP_MATCH_MAX)
1141 LOG_AND_RETURN("invalid %s operation", key);
1142
912541b0 1143 rule_add_key(&rule_tmp, TK_M_KERNELS, op, value, NULL);
912541b0 1144
99f16bb8 1145 } else if (streq(key, "SUBSYSTEMS")) {
19a8e656
ZJS
1146 if (op > OP_MATCH_MAX)
1147 LOG_AND_RETURN("invalid %s operation", key);
1148
912541b0 1149 rule_add_key(&rule_tmp, TK_M_SUBSYSTEMS, op, value, NULL);
912541b0 1150
99f16bb8 1151 } else if (streq(key, "DRIVERS")) {
19a8e656
ZJS
1152 if (op > OP_MATCH_MAX)
1153 LOG_AND_RETURN("invalid %s operation", key);
1154
912541b0 1155 rule_add_key(&rule_tmp, TK_M_DRIVERS, op, value, NULL);
912541b0 1156
99f16bb8 1157 } else if (startswith(key, "ATTRS{")) {
19a8e656
ZJS
1158 if (op > OP_MATCH_MAX)
1159 LOG_AND_RETURN("invalid %s operation", "ATTRS");
1160
fbd0b64f
LP
1161 attr = get_key_attribute(rules->udev,
1162 key + STRLEN("ATTRS"));
19a8e656
ZJS
1163 if (attr == NULL)
1164 LOG_AND_RETURN("error parsing %s attribute", "ATTRS");
1165
33502ffe 1166 if (startswith(attr, "device/"))
19a8e656
ZJS
1167 LOG_RULE_WARNING("'device' link may not be available in future kernels; please fix");
1168 if (strstr(attr, "../") != NULL)
1169 LOG_RULE_WARNING("direct reference to parent sysfs directory, may break in future kernels; please fix");
912541b0 1170 rule_add_key(&rule_tmp, TK_M_ATTRS, op, value, attr);
912541b0 1171
99f16bb8 1172 } else if (streq(key, "TAGS")) {
19a8e656
ZJS
1173 if (op > OP_MATCH_MAX)
1174 LOG_AND_RETURN("invalid %s operation", key);
1175
912541b0 1176 rule_add_key(&rule_tmp, TK_M_TAGS, op, value, NULL);
912541b0 1177
99f16bb8 1178 } else if (startswith(key, "ENV{")) {
fbd0b64f
LP
1179 attr = get_key_attribute(rules->udev,
1180 key + STRLEN("ENV"));
19a8e656
ZJS
1181 if (attr == NULL)
1182 LOG_AND_RETURN("error parsing %s attribute", "ENV");
1183
1184 if (op == OP_REMOVE)
1185 LOG_AND_RETURN("invalid %s operation", "ENV");
1186
1187 if (op < OP_MATCH_MAX)
1188 rule_add_key(&rule_tmp, TK_M_ENV, op, value, attr);
1189 else {
99f16bb8
ZJS
1190 if (STR_IN_SET(attr,
1191 "ACTION",
1192 "SUBSYSTEM",
1193 "DEVTYPE",
1194 "MAJOR",
1195 "MINOR",
1196 "DRIVER",
1197 "IFINDEX",
1198 "DEVNAME",
1199 "DEVLINKS",
1200 "DEVPATH",
19a8e656
ZJS
1201 "TAGS"))
1202 LOG_AND_RETURN("invalid ENV attribute, '%s' cannot be set", attr);
1203
1204 rule_add_key(&rule_tmp, TK_A_ENV, op, value, attr);
912541b0 1205 }
912541b0 1206
99f16bb8 1207 } else if (streq(key, "TAG")) {
912541b0
KS
1208 if (op < OP_MATCH_MAX)
1209 rule_add_key(&rule_tmp, TK_M_TAG, op, value, NULL);
1210 else
1211 rule_add_key(&rule_tmp, TK_A_TAG, op, value, NULL);
912541b0 1212
99f16bb8 1213 } else if (streq(key, "PROGRAM")) {
19a8e656
ZJS
1214 if (op == OP_REMOVE)
1215 LOG_AND_RETURN("invalid %s operation", key);
1216
912541b0 1217 rule_add_key(&rule_tmp, TK_M_PROGRAM, op, value, NULL);
912541b0 1218
99f16bb8 1219 } else if (streq(key, "RESULT")) {
19a8e656
ZJS
1220 if (op > OP_MATCH_MAX)
1221 LOG_AND_RETURN("invalid %s operation", key);
1222
912541b0 1223 rule_add_key(&rule_tmp, TK_M_RESULT, op, value, NULL);
912541b0 1224
99f16bb8 1225 } else if (startswith(key, "IMPORT")) {
fbd0b64f
LP
1226 attr = get_key_attribute(rules->udev,
1227 key + STRLEN("IMPORT"));
912541b0 1228 if (attr == NULL) {
19a8e656 1229 LOG_RULE_WARNING("ignoring IMPORT{} with missing type");
912541b0
KS
1230 continue;
1231 }
19a8e656
ZJS
1232 if (op == OP_REMOVE)
1233 LOG_AND_RETURN("invalid %s operation", "IMPORT");
1234
33502ffe 1235 if (streq(attr, "program")) {
912541b0
KS
1236 /* find known built-in command */
1237 if (value[0] != '/') {
19a8e656 1238 const enum udev_builtin_cmd cmd = udev_builtin_lookup(value);
912541b0 1239
912541b0 1240 if (cmd < UDEV_BUILTIN_MAX) {
19a8e656 1241 LOG_RULE_DEBUG("IMPORT found builtin '%s', replacing", value);
912541b0
KS
1242 rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd);
1243 continue;
1244 }
1245 }
912541b0 1246 rule_add_key(&rule_tmp, TK_M_IMPORT_PROG, op, value, NULL);
33502ffe 1247 } else if (streq(attr, "builtin")) {
19a8e656 1248 const enum udev_builtin_cmd cmd = udev_builtin_lookup(value);
912541b0 1249
19a8e656
ZJS
1250 if (cmd >= UDEV_BUILTIN_MAX)
1251 LOG_RULE_WARNING("IMPORT{builtin} '%s' unknown", value);
912541b0 1252 else
19a8e656
ZJS
1253 rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd);
1254 } else if (streq(attr, "file"))
912541b0 1255 rule_add_key(&rule_tmp, TK_M_IMPORT_FILE, op, value, NULL);
19a8e656 1256 else if (streq(attr, "db"))
912541b0 1257 rule_add_key(&rule_tmp, TK_M_IMPORT_DB, op, value, NULL);
19a8e656 1258 else if (streq(attr, "cmdline"))
912541b0 1259 rule_add_key(&rule_tmp, TK_M_IMPORT_CMDLINE, op, value, NULL);
19a8e656 1260 else if (streq(attr, "parent"))
912541b0 1261 rule_add_key(&rule_tmp, TK_M_IMPORT_PARENT, op, value, NULL);
19a8e656
ZJS
1262 else
1263 LOG_RULE_ERROR("ignoring unknown %s{} type '%s'", "IMPORT", attr);
912541b0 1264
99f16bb8 1265 } else if (startswith(key, "TEST")) {
912541b0
KS
1266 mode_t mode = 0;
1267
19a8e656
ZJS
1268 if (op > OP_MATCH_MAX)
1269 LOG_AND_RETURN("invalid %s operation", "TEST");
1270
fbd0b64f
LP
1271 attr = get_key_attribute(rules->udev,
1272 key + STRLEN("TEST"));
912541b0
KS
1273 if (attr != NULL) {
1274 mode = strtol(attr, NULL, 8);
1275 rule_add_key(&rule_tmp, TK_M_TEST, op, value, &mode);
99f16bb8 1276 } else
912541b0 1277 rule_add_key(&rule_tmp, TK_M_TEST, op, value, NULL);
912541b0 1278
99f16bb8 1279 } else if (startswith(key, "RUN")) {
fbd0b64f
LP
1280 attr = get_key_attribute(rules->udev,
1281 key + STRLEN("RUN"));
83cd6b75
KS
1282 if (attr == NULL)
1283 attr = "program";
19a8e656
ZJS
1284 if (op == OP_REMOVE)
1285 LOG_AND_RETURN("invalid %s operation", "RUN");
83cd6b75 1286
33502ffe 1287 if (streq(attr, "builtin")) {
19a8e656 1288 const enum udev_builtin_cmd cmd = udev_builtin_lookup(value);
83cd6b75
KS
1289
1290 if (cmd < UDEV_BUILTIN_MAX)
1291 rule_add_key(&rule_tmp, TK_A_RUN_BUILTIN, op, value, &cmd);
1292 else
19a8e656 1293 LOG_RULE_ERROR("RUN{builtin}: '%s' unknown", value);
33502ffe 1294 } else if (streq(attr, "program")) {
99f16bb8 1295 const enum udev_builtin_cmd cmd = UDEV_BUILTIN_MAX;
83cd6b75 1296
83cd6b75 1297 rule_add_key(&rule_tmp, TK_A_RUN_PROGRAM, op, value, &cmd);
99f16bb8 1298 } else
19a8e656 1299 LOG_RULE_ERROR("ignoring unknown %s{} type '%s'", "RUN", attr);
912541b0 1300
99f16bb8 1301 } else if (streq(key, "LABEL")) {
19a8e656
ZJS
1302 if (op == OP_REMOVE)
1303 LOG_AND_RETURN("invalid %s operation", key);
1304
915bf0f6 1305 rule_tmp.rule.rule.label_off = rules_add_string(rules, value);
912541b0 1306
99f16bb8 1307 } else if (streq(key, "GOTO")) {
19a8e656
ZJS
1308 if (op == OP_REMOVE)
1309 LOG_AND_RETURN("invalid %s operation", key);
1310
912541b0 1311 rule_add_key(&rule_tmp, TK_A_GOTO, 0, value, NULL);
912541b0 1312
99f16bb8 1313 } else if (startswith(key, "NAME")) {
19a8e656
ZJS
1314 if (op == OP_REMOVE)
1315 LOG_AND_RETURN("invalid %s operation", key);
1316
1317 if (op < OP_MATCH_MAX)
912541b0 1318 rule_add_key(&rule_tmp, TK_M_NAME, op, value, NULL);
19a8e656 1319 else {
33502ffe 1320 if (streq(value, "%k")) {
19a8e656 1321 LOG_RULE_WARNING("NAME=\"%%k\" is ignored, because it breaks kernel supplied names; please remove");
912541b0
KS
1322 continue;
1323 }
19a8e656
ZJS
1324 if (isempty(value)) {
1325 LOG_RULE_DEBUG("NAME=\"\" is ignored, because udev will not delete any device nodes; please remove");
912541b0
KS
1326 continue;
1327 }
1328 rule_add_key(&rule_tmp, TK_A_NAME, op, value, NULL);
1329 }
1330 rule_tmp.rule.rule.can_set_name = true;
912541b0 1331
99f16bb8 1332 } else if (streq(key, "SYMLINK")) {
19a8e656
ZJS
1333 if (op == OP_REMOVE)
1334 LOG_AND_RETURN("invalid %s operation", key);
1335
8a173387 1336 if (op < OP_MATCH_MAX)
912541b0 1337 rule_add_key(&rule_tmp, TK_M_DEVLINK, op, value, NULL);
8a173387
KS
1338 else
1339 rule_add_key(&rule_tmp, TK_A_DEVLINK, op, value, NULL);
912541b0 1340 rule_tmp.rule.rule.can_set_name = true;
912541b0 1341
99f16bb8 1342 } else if (streq(key, "OWNER")) {
912541b0
KS
1343 uid_t uid;
1344 char *endptr;
1345
19a8e656
ZJS
1346 if (op == OP_REMOVE)
1347 LOG_AND_RETURN("invalid %s operation", key);
8e3ba377 1348
912541b0 1349 uid = strtoul(value, &endptr, 10);
19a8e656 1350 if (endptr[0] == '\0')
912541b0 1351 rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid);
19a8e656 1352 else if (rules->resolve_names > 0 && strchr("$%", value[0]) == NULL) {
912541b0
KS
1353 uid = add_uid(rules, value);
1354 rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid);
1f6b4113 1355 } else if (rules->resolve_names >= 0)
912541b0 1356 rule_add_key(&rule_tmp, TK_A_OWNER, op, value, NULL);
1f6b4113 1357
912541b0 1358 rule_tmp.rule.rule.can_set_name = true;
912541b0 1359
99f16bb8 1360 } else if (streq(key, "GROUP")) {
912541b0
KS
1361 gid_t gid;
1362 char *endptr;
1363
19a8e656
ZJS
1364 if (op == OP_REMOVE)
1365 LOG_AND_RETURN("invalid %s operation", key);
8e3ba377 1366
912541b0 1367 gid = strtoul(value, &endptr, 10);
19a8e656 1368 if (endptr[0] == '\0')
912541b0 1369 rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid);
19a8e656 1370 else if ((rules->resolve_names > 0) && strchr("$%", value[0]) == NULL) {
912541b0
KS
1371 gid = add_gid(rules, value);
1372 rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid);
1f6b4113 1373 } else if (rules->resolve_names >= 0)
912541b0 1374 rule_add_key(&rule_tmp, TK_A_GROUP, op, value, NULL);
1f6b4113 1375
912541b0 1376 rule_tmp.rule.rule.can_set_name = true;
912541b0 1377
99f16bb8 1378 } else if (streq(key, "MODE")) {
912541b0
KS
1379 mode_t mode;
1380 char *endptr;
1381
19a8e656
ZJS
1382 if (op == OP_REMOVE)
1383 LOG_AND_RETURN("invalid %s operation", key);
8e3ba377 1384
912541b0
KS
1385 mode = strtol(value, &endptr, 8);
1386 if (endptr[0] == '\0')
1387 rule_add_key(&rule_tmp, TK_A_MODE_ID, op, NULL, &mode);
1388 else
1389 rule_add_key(&rule_tmp, TK_A_MODE, op, value, NULL);
1390 rule_tmp.rule.rule.can_set_name = true;
912541b0 1391
99f16bb8 1392 } else if (streq(key, "OPTIONS")) {
912541b0
KS
1393 const char *pos;
1394
19a8e656
ZJS
1395 if (op == OP_REMOVE)
1396 LOG_AND_RETURN("invalid %s operation", key);
8e3ba377 1397
912541b0
KS
1398 pos = strstr(value, "link_priority=");
1399 if (pos != NULL) {
fbd0b64f 1400 int prio = atoi(pos + STRLEN("link_priority="));
912541b0
KS
1401
1402 rule_add_key(&rule_tmp, TK_A_DEVLINK_PRIO, op, NULL, &prio);
912541b0
KS
1403 }
1404
9f20a8a3 1405 pos = strstr(value, "string_escape=");
912541b0 1406 if (pos != NULL) {
fbd0b64f 1407 pos += STRLEN("string_escape=");
33502ffe 1408 if (startswith(pos, "none"))
912541b0 1409 rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_NONE, op, NULL, NULL);
33502ffe 1410 else if (startswith(pos, "replace"))
912541b0
KS
1411 rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_REPLACE, op, NULL, NULL);
1412 }
1413
1414 pos = strstr(value, "db_persist");
1415 if (pos != NULL)
1416 rule_add_key(&rule_tmp, TK_A_DB_PERSIST, op, NULL, NULL);
1417
1418 pos = strstr(value, "nowatch");
1419 if (pos != NULL) {
1420 const int off = 0;
1421
1422 rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &off);
912541b0
KS
1423 } else {
1424 pos = strstr(value, "watch");
1425 if (pos != NULL) {
1426 const int on = 1;
1427
1428 rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &on);
912541b0
KS
1429 }
1430 }
1431
1432 pos = strstr(value, "static_node=");
1433 if (pos != NULL) {
fbd0b64f 1434 pos += STRLEN("static_node=");
19a8e656 1435 rule_add_key(&rule_tmp, TK_A_STATIC_NODE, op, pos, NULL);
912541b0
KS
1436 rule_tmp.rule.rule.has_static_node = true;
1437 }
1438
19a8e656
ZJS
1439 } else
1440 LOG_AND_RETURN("unknown key '%s'", key);
912541b0
KS
1441 }
1442
19a8e656 1443 /* add rule token and sort tokens */
912541b0 1444 rule_tmp.rule.rule.token_count = 1 + rule_tmp.token_cur;
19a8e656
ZJS
1445 if (add_token(rules, &rule_tmp.rule) != 0 || sort_token(rules, &rule_tmp) != 0)
1446 LOG_RULE_ERROR("failed to add rule token");
c7521974
KS
1447}
1448
9ec6e95b 1449static int parse_file(struct udev_rules *rules, const char *filename) {
6c8aaf0c 1450 _cleanup_fclose_ FILE *f = NULL;
912541b0 1451 unsigned int first_token;
775f8b3c 1452 unsigned int filename_off;
912541b0
KS
1453 char line[UTIL_LINE_SIZE];
1454 int line_nr = 0;
1455 unsigned int i;
1456
ed88bcfb
ZJS
1457 f = fopen(filename, "re");
1458 if (!f) {
1459 if (errno == ENOENT)
1460 return 0;
1461 else
1462 return -errno;
775f8b3c 1463 }
912541b0 1464
ed88bcfb
ZJS
1465 if (null_or_empty_fd(fileno(f))) {
1466 log_debug("Skipping empty file: %s", filename);
1467 return 0;
1468 } else
1469 log_debug("Reading rules file: %s", filename);
912541b0
KS
1470
1471 first_token = rules->token_cur;
915bf0f6 1472 filename_off = rules_add_string(rules, filename);
912541b0
KS
1473
1474 while (fgets(line, sizeof(line), f) != NULL) {
1475 char *key;
1476 size_t len;
1477
1478 /* skip whitespace */
1479 line_nr++;
1480 key = line;
1481 while (isspace(key[0]))
1482 key++;
1483
1484 /* comment */
1485 if (key[0] == '#')
1486 continue;
1487
1488 len = strlen(line);
1489 if (len < 3)
1490 continue;
1491
1492 /* continue reading if backslash+newline is found */
1493 while (line[len-2] == '\\') {
1494 if (fgets(&line[len-2], (sizeof(line)-len)+2, f) == NULL)
1495 break;
1496 if (strlen(&line[len-2]) < 2)
1497 break;
1498 line_nr++;
1499 len = strlen(line);
1500 }
1501
1502 if (len+1 >= sizeof(line)) {
9f6445e3 1503 log_error("line too long '%s':%u, ignored", filename, line_nr);
912541b0
KS
1504 continue;
1505 }
1506 add_rule(rules, key, filename, filename_off, line_nr);
1507 }
912541b0
KS
1508
1509 /* link GOTOs to LABEL rules in this file to be able to fast-forward */
1510 for (i = first_token+1; i < rules->token_cur; i++) {
1511 if (rules->tokens[i].type == TK_A_GOTO) {
915bf0f6 1512 char *label = rules_str(rules, rules->tokens[i].key.value_off);
912541b0
KS
1513 unsigned int j;
1514
1515 for (j = i+1; j < rules->token_cur; j++) {
1516 if (rules->tokens[j].type != TK_RULE)
1517 continue;
1518 if (rules->tokens[j].rule.label_off == 0)
1519 continue;
915bf0f6 1520 if (!streq(label, rules_str(rules, rules->tokens[j].rule.label_off)))
912541b0
KS
1521 continue;
1522 rules->tokens[i].key.rule_goto = j;
1523 break;
1524 }
1525 if (rules->tokens[i].key.rule_goto == 0)
9f6445e3 1526 log_error("GOTO '%s' has no matching label in: '%s'", label, filename);
912541b0
KS
1527 }
1528 }
1529 return 0;
c7521974
KS
1530}
1531
9ec6e95b 1532struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names) {
912541b0
KS
1533 struct udev_rules *rules;
1534 struct udev_list file_list;
912541b0 1535 struct token end_token;
775f8b3c
KS
1536 char **files, **f;
1537 int r;
912541b0 1538
955d98c9 1539 rules = new0(struct udev_rules, 1);
912541b0
KS
1540 if (rules == NULL)
1541 return NULL;
1542 rules->udev = udev;
1543 rules->resolve_names = resolve_names;
1544 udev_list_init(udev, &file_list, true);
1545
1546 /* init token array and string buffer */
1547 rules->tokens = malloc(PREALLOC_TOKEN * sizeof(struct token));
0820a4f0
VP
1548 if (rules->tokens == NULL)
1549 return udev_rules_unref(rules);
912541b0
KS
1550 rules->token_max = PREALLOC_TOKEN;
1551
915bf0f6
KS
1552 rules->strbuf = strbuf_new();
1553 if (!rules->strbuf)
0820a4f0
VP
1554 return udev_rules_unref(rules);
1555
3b8c1cb0
KS
1556 udev_rules_check_timestamp(rules);
1557
b5084605 1558 r = conf_files_list_strv(&files, ".rules", NULL, 0, rules_dirs);
775f8b3c 1559 if (r < 0) {
da927ba9 1560 log_error_errno(r, "failed to enumerate rules files: %m");
0820a4f0 1561 return udev_rules_unref(rules);
912541b0
KS
1562 }
1563
775f8b3c
KS
1564 /*
1565 * The offset value in the rules strct is limited; add all
1566 * rules file names to the beginning of the string buffer.
1567 */
1568 STRV_FOREACH(f, files)
915bf0f6 1569 rules_add_string(rules, *f);
912541b0 1570
775f8b3c
KS
1571 STRV_FOREACH(f, files)
1572 parse_file(rules, *f);
1573
1574 strv_free(files);
912541b0 1575
29804cc1 1576 memzero(&end_token, sizeof(struct token));
912541b0
KS
1577 end_token.type = TK_END;
1578 add_token(rules, &end_token);
9f6445e3 1579 log_debug("rules contain %zu bytes tokens (%u * %zu bytes), %zu bytes strings",
915bf0f6 1580 rules->token_max * sizeof(struct token), rules->token_max, sizeof(struct token), rules->strbuf->len);
912541b0 1581
915bf0f6 1582 /* cleanup temporary strbuf data */
9f6445e3 1583 log_debug("%zu strings (%zu bytes), %zu de-duplicated (%zu bytes), %zu trie nodes used",
915bf0f6
KS
1584 rules->strbuf->in_count, rules->strbuf->in_len,
1585 rules->strbuf->dedup_count, rules->strbuf->dedup_len, rules->strbuf->nodes_count);
1586 strbuf_complete(rules->strbuf);
912541b0
KS
1587
1588 /* cleanup uid/gid cache */
a1e58e8e 1589 rules->uids = mfree(rules->uids);
912541b0
KS
1590 rules->uids_cur = 0;
1591 rules->uids_max = 0;
a1e58e8e 1592 rules->gids = mfree(rules->gids);
912541b0
KS
1593 rules->gids_cur = 0;
1594 rules->gids_max = 0;
1595
1596 dump_rules(rules);
1597 return rules;
c7521974
KS
1598}
1599
9ec6e95b 1600struct udev_rules *udev_rules_unref(struct udev_rules *rules) {
912541b0
KS
1601 if (rules == NULL)
1602 return NULL;
1603 free(rules->tokens);
915bf0f6 1604 strbuf_cleanup(rules->strbuf);
912541b0
KS
1605 free(rules->uids);
1606 free(rules->gids);
6b430fdb 1607 return mfree(rules);
c7521974 1608}
6880b25d 1609
9ec6e95b 1610bool udev_rules_check_timestamp(struct udev_rules *rules) {
5c11fbe3
KS
1611 if (!rules)
1612 return false;
1613
2ad8416d 1614 return paths_check_timestamp(rules_dirs, &rules->dirs_ts_usec, true);
6ada823a
KS
1615}
1616
9ec6e95b 1617static int match_key(struct udev_rules *rules, struct token *token, const char *val) {
915bf0f6 1618 char *key_value = rules_str(rules, token->key.value_off);
912541b0
KS
1619 char *pos;
1620 bool match = false;
1621
1622 if (val == NULL)
1623 val = "";
1624
1625 switch (token->key.glob) {
1626 case GL_PLAIN:
33502ffe 1627 match = (streq(key_value, val));
912541b0
KS
1628 break;
1629 case GL_GLOB:
1630 match = (fnmatch(key_value, val, 0) == 0);
1631 break;
1632 case GL_SPLIT:
1633 {
33502ffe 1634 const char *s;
912541b0
KS
1635 size_t len;
1636
915bf0f6 1637 s = rules_str(rules, token->key.value_off);
912541b0
KS
1638 len = strlen(val);
1639 for (;;) {
1640 const char *next;
1641
33502ffe 1642 next = strchr(s, '|');
912541b0 1643 if (next != NULL) {
33502ffe 1644 size_t matchlen = (size_t)(next - s);
912541b0 1645
641906e9 1646 match = (matchlen == len && strneq(s, val, matchlen));
912541b0
KS
1647 if (match)
1648 break;
1649 } else {
33502ffe 1650 match = (streq(s, val));
912541b0
KS
1651 break;
1652 }
33502ffe 1653 s = &next[1];
912541b0
KS
1654 }
1655 break;
1656 }
1657 case GL_SPLIT_GLOB:
1658 {
1659 char value[UTIL_PATH_SIZE];
1660
d5a89d7d 1661 strscpy(value, sizeof(value), rules_str(rules, token->key.value_off));
912541b0
KS
1662 key_value = value;
1663 while (key_value != NULL) {
1664 pos = strchr(key_value, '|');
1665 if (pos != NULL) {
1666 pos[0] = '\0';
1667 pos = &pos[1];
1668 }
912541b0
KS
1669 match = (fnmatch(key_value, val, 0) == 0);
1670 if (match)
1671 break;
1672 key_value = pos;
1673 }
1674 break;
1675 }
1676 case GL_SOMETHING:
1677 match = (val[0] != '\0');
1678 break;
1679 case GL_UNSET:
1680 return -1;
1681 }
1682
baa30fbc 1683 if (match && (token->key.op == OP_MATCH))
912541b0 1684 return 0;
baa30fbc 1685 if (!match && (token->key.op == OP_NOMATCH))
912541b0 1686 return 0;
912541b0 1687 return -1;
6880b25d
KS
1688}
1689
9ec6e95b 1690static int match_attr(struct udev_rules *rules, struct udev_device *dev, struct udev_event *event, struct token *cur) {
912541b0
KS
1691 const char *name;
1692 char nbuf[UTIL_NAME_SIZE];
1693 const char *value;
1694 char vbuf[UTIL_NAME_SIZE];
1695 size_t len;
1696
915bf0f6 1697 name = rules_str(rules, cur->key.attr_off);
912541b0
KS
1698 switch (cur->key.attrsubst) {
1699 case SB_FORMAT:
e20a9171 1700 udev_event_apply_format(event, name, nbuf, sizeof(nbuf), false);
912541b0 1701 name = nbuf;
4831981d 1702 _fallthrough_;
912541b0
KS
1703 case SB_NONE:
1704 value = udev_device_get_sysattr_value(dev, name);
1705 if (value == NULL)
1706 return -1;
1707 break;
1708 case SB_SUBSYS:
1709 if (util_resolve_subsys_kernel(event->udev, name, vbuf, sizeof(vbuf), 1) != 0)
1710 return -1;
1711 value = vbuf;
1712 break;
1713 default:
1714 return -1;
1715 }
1716
1717 /* remove trailing whitespace, if not asked to match for it */
1718 len = strlen(value);
1719 if (len > 0 && isspace(value[len-1])) {
1720 const char *key_value;
1721 size_t klen;
1722
915bf0f6 1723 key_value = rules_str(rules, cur->key.value_off);
912541b0
KS
1724 klen = strlen(key_value);
1725 if (klen > 0 && !isspace(key_value[klen-1])) {
1726 if (value != vbuf) {
d5a89d7d 1727 strscpy(vbuf, sizeof(vbuf), value);
912541b0
KS
1728 value = vbuf;
1729 }
1730 while (len > 0 && isspace(vbuf[--len]))
1731 vbuf[len] = '\0';
912541b0
KS
1732 }
1733 }
1734
1735 return match_key(rules, cur, value);
6880b25d
KS
1736}
1737
1738enum escape_type {
912541b0
KS
1739 ESCAPE_UNSET,
1740 ESCAPE_NONE,
1741 ESCAPE_REPLACE,
6880b25d
KS
1742};
1743
4fd6af76
ZJS
1744void udev_rules_apply_to_event(struct udev_rules *rules,
1745 struct udev_event *event,
1746 usec_t timeout_usec,
1747 usec_t timeout_warn_usec,
1748 struct udev_list *properties_list) {
912541b0
KS
1749 struct token *cur;
1750 struct token *rule;
1751 enum escape_type esc = ESCAPE_UNSET;
1752 bool can_set_name;
88b013b2 1753 int r;
912541b0
KS
1754
1755 if (rules->tokens == NULL)
4fd6af76 1756 return;
912541b0 1757
33502ffe 1758 can_set_name = ((!streq(udev_device_get_action(event->dev), "remove")) &&
912541b0
KS
1759 (major(udev_device_get_devnum(event->dev)) > 0 ||
1760 udev_device_get_ifindex(event->dev) > 0));
1761
1762 /* loop through token list, match, run actions or forward to next rule */
1763 cur = &rules->tokens[0];
1764 rule = cur;
1765 for (;;) {
1766 dump_token(rules, cur);
1767 switch (cur->type) {
1768 case TK_RULE:
1769 /* current rule */
1770 rule = cur;
1771 /* possibly skip rules which want to set NAME, SYMLINK, OWNER, GROUP, MODE */
1772 if (!can_set_name && rule->rule.can_set_name)
1773 goto nomatch;
1774 esc = ESCAPE_UNSET;
1775 break;
1776 case TK_M_ACTION:
1777 if (match_key(rules, cur, udev_device_get_action(event->dev)) != 0)
1778 goto nomatch;
1779 break;
1780 case TK_M_DEVPATH:
1781 if (match_key(rules, cur, udev_device_get_devpath(event->dev)) != 0)
1782 goto nomatch;
1783 break;
1784 case TK_M_KERNEL:
1785 if (match_key(rules, cur, udev_device_get_sysname(event->dev)) != 0)
1786 goto nomatch;
1787 break;
1788 case TK_M_DEVLINK: {
912541b0
KS
1789 struct udev_list_entry *list_entry;
1790 bool match = false;
1791
1792 udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(event->dev)) {
1793 const char *devlink;
1794
fbd0b64f 1795 devlink = udev_list_entry_get_name(list_entry) + STRLEN("/dev/");
912541b0
KS
1796 if (match_key(rules, cur, devlink) == 0) {
1797 match = true;
1798 break;
1799 }
1800 }
1801 if (!match)
1802 goto nomatch;
1803 break;
1804 }
1805 case TK_M_NAME:
1806 if (match_key(rules, cur, event->name) != 0)
1807 goto nomatch;
1808 break;
1809 case TK_M_ENV: {
915bf0f6 1810 const char *key_name = rules_str(rules, cur->key.attr_off);
912541b0
KS
1811 const char *value;
1812
1813 value = udev_device_get_property_value(event->dev, key_name);
adeba500
KS
1814
1815 /* check global properties */
1816 if (!value && properties_list) {
1817 struct udev_list_entry *list_entry;
1818
1819 list_entry = udev_list_get_entry(properties_list);
1820 list_entry = udev_list_entry_get_by_name(list_entry, key_name);
1821 if (list_entry != NULL)
1822 value = udev_list_entry_get_value(list_entry);
1823 }
1824
1825 if (!value)
912541b0 1826 value = "";
912541b0
KS
1827 if (match_key(rules, cur, value))
1828 goto nomatch;
1829 break;
1830 }
1831 case TK_M_TAG: {
1832 struct udev_list_entry *list_entry;
1833 bool match = false;
1834
1835 udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(event->dev)) {
915bf0f6 1836 if (streq(rules_str(rules, cur->key.value_off), udev_list_entry_get_name(list_entry))) {
912541b0
KS
1837 match = true;
1838 break;
1839 }
1840 }
4302857b
FF
1841 if ((!match && (cur->key.op != OP_NOMATCH)) ||
1842 (match && (cur->key.op == OP_NOMATCH)))
912541b0
KS
1843 goto nomatch;
1844 break;
1845 }
1846 case TK_M_SUBSYSTEM:
1847 if (match_key(rules, cur, udev_device_get_subsystem(event->dev)) != 0)
1848 goto nomatch;
1849 break;
1850 case TK_M_DRIVER:
1851 if (match_key(rules, cur, udev_device_get_driver(event->dev)) != 0)
1852 goto nomatch;
1853 break;
912541b0
KS
1854 case TK_M_ATTR:
1855 if (match_attr(rules, event->dev, event, cur) != 0)
1856 goto nomatch;
1857 break;
f4cf2e5b
KS
1858 case TK_M_SYSCTL: {
1859 char filename[UTIL_PATH_SIZE];
1860 _cleanup_free_ char *value = NULL;
1861 size_t len;
1862
e20a9171 1863 udev_event_apply_format(event, rules_str(rules, cur->key.attr_off), filename, sizeof(filename), false);
f4cf2e5b
KS
1864 sysctl_normalize(filename);
1865 if (sysctl_read(filename, &value) < 0)
1866 goto nomatch;
1867
1868 len = strlen(value);
1869 while (len > 0 && isspace(value[--len]))
1870 value[len] = '\0';
1871 if (match_key(rules, cur, value) != 0)
1872 goto nomatch;
1873 break;
1874 }
912541b0
KS
1875 case TK_M_KERNELS:
1876 case TK_M_SUBSYSTEMS:
1877 case TK_M_DRIVERS:
1878 case TK_M_ATTRS:
1879 case TK_M_TAGS: {
1880 struct token *next;
1881
1882 /* get whole sequence of parent matches */
1883 next = cur;
1884 while (next->type > TK_M_PARENTS_MIN && next->type < TK_M_PARENTS_MAX)
1885 next++;
1886
1887 /* loop over parents */
1888 event->dev_parent = event->dev;
1889 for (;;) {
1890 struct token *key;
1891
912541b0
KS
1892 /* loop over sequence of parent match keys */
1893 for (key = cur; key < next; key++ ) {
1894 dump_token(rules, key);
1895 switch(key->type) {
1896 case TK_M_KERNELS:
1897 if (match_key(rules, key, udev_device_get_sysname(event->dev_parent)) != 0)
1898 goto try_parent;
1899 break;
1900 case TK_M_SUBSYSTEMS:
1901 if (match_key(rules, key, udev_device_get_subsystem(event->dev_parent)) != 0)
1902 goto try_parent;
1903 break;
1904 case TK_M_DRIVERS:
1905 if (match_key(rules, key, udev_device_get_driver(event->dev_parent)) != 0)
1906 goto try_parent;
1907 break;
1908 case TK_M_ATTRS:
1909 if (match_attr(rules, event->dev_parent, event, key) != 0)
1910 goto try_parent;
1911 break;
1912 case TK_M_TAGS: {
915bf0f6 1913 bool match = udev_device_has_tag(event->dev_parent, rules_str(rules, cur->key.value_off));
912541b0
KS
1914
1915 if (match && key->key.op == OP_NOMATCH)
1916 goto try_parent;
1917 if (!match && key->key.op == OP_MATCH)
1918 goto try_parent;
1919 break;
1920 }
1921 default:
1922 goto nomatch;
1923 }
912541b0 1924 }
912541b0
KS
1925 break;
1926
1927 try_parent:
1928 event->dev_parent = udev_device_get_parent(event->dev_parent);
1929 if (event->dev_parent == NULL)
1930 goto nomatch;
1931 }
1932 /* move behind our sequence of parent match keys */
1933 cur = next;
1934 continue;
1935 }
1936 case TK_M_TEST: {
1937 char filename[UTIL_PATH_SIZE];
1938 struct stat statbuf;
1939 int match;
1940
e20a9171 1941 udev_event_apply_format(event, rules_str(rules, cur->key.value_off), filename, sizeof(filename), false);
912541b0
KS
1942 if (util_resolve_subsys_kernel(event->udev, filename, filename, sizeof(filename), 0) != 0) {
1943 if (filename[0] != '/') {
1944 char tmp[UTIL_PATH_SIZE];
1945
d5a89d7d
KS
1946 strscpy(tmp, sizeof(tmp), filename);
1947 strscpyl(filename, sizeof(filename),
912541b0
KS
1948 udev_device_get_syspath(event->dev), "/", tmp, NULL);
1949 }
1950 }
1951 attr_subst_subdir(filename, sizeof(filename));
1952
1953 match = (stat(filename, &statbuf) == 0);
baa30fbc 1954 if (match && cur->key.mode > 0)
912541b0 1955 match = ((statbuf.st_mode & cur->key.mode) > 0);
912541b0
KS
1956 if (match && cur->key.op == OP_NOMATCH)
1957 goto nomatch;
1958 if (!match && cur->key.op == OP_MATCH)
1959 goto nomatch;
1960 break;
1961 }
912541b0
KS
1962 case TK_M_PROGRAM: {
1963 char program[UTIL_PATH_SIZE];
209b031e 1964 char result[UTIL_LINE_SIZE];
912541b0 1965
a1e58e8e 1966 event->program_result = mfree(event->program_result);
e20a9171 1967 udev_event_apply_format(event, rules_str(rules, cur->key.value_off), program, sizeof(program), false);
9f6445e3 1968 log_debug("PROGRAM '%s' %s:%u",
baa30fbc 1969 program,
915bf0f6 1970 rules_str(rules, rule->rule.filename_off),
baa30fbc 1971 rule->rule.filename_line);
912541b0 1972
bbf35206 1973 if (udev_event_spawn(event, timeout_usec, timeout_warn_usec, true, program, result, sizeof(result)) < 0) {
912541b0
KS
1974 if (cur->key.op != OP_NOMATCH)
1975 goto nomatch;
1976 } else {
1977 int count;
1978
7546145e 1979 delete_trailing_chars(result, "\n");
4c701096 1980 if (IN_SET(esc, ESCAPE_UNSET, ESCAPE_REPLACE)) {
912541b0
KS
1981 count = util_replace_chars(result, UDEV_ALLOWED_CHARS_INPUT);
1982 if (count > 0)
9f6445e3 1983 log_debug("%i character(s) replaced" , count);
912541b0
KS
1984 }
1985 event->program_result = strdup(result);
912541b0
KS
1986 if (cur->key.op == OP_NOMATCH)
1987 goto nomatch;
1988 }
1989 break;
1990 }
1991 case TK_M_IMPORT_FILE: {
1992 char import[UTIL_PATH_SIZE];
1993
e20a9171 1994 udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import), false);
912541b0
KS
1995 if (import_file_into_properties(event->dev, import) != 0)
1996 if (cur->key.op != OP_NOMATCH)
1997 goto nomatch;
1998 break;
1999 }
2000 case TK_M_IMPORT_PROG: {
2001 char import[UTIL_PATH_SIZE];
2002
e20a9171 2003 udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import), false);
9f6445e3 2004 log_debug("IMPORT '%s' %s:%u",
baa30fbc 2005 import,
915bf0f6 2006 rules_str(rules, rule->rule.filename_off),
baa30fbc 2007 rule->rule.filename_line);
912541b0 2008
8314de1d 2009 if (import_program_into_properties(event, timeout_usec, timeout_warn_usec, import) != 0)
912541b0
KS
2010 if (cur->key.op != OP_NOMATCH)
2011 goto nomatch;
2012 break;
2013 }
2014 case TK_M_IMPORT_BUILTIN: {
2015 char command[UTIL_PATH_SIZE];
2016
2017 if (udev_builtin_run_once(cur->key.builtin_cmd)) {
2018 /* check if we ran already */
2019 if (event->builtin_run & (1 << cur->key.builtin_cmd)) {
9f6445e3 2020 log_debug("IMPORT builtin skip '%s' %s:%u",
baa30fbc 2021 udev_builtin_name(cur->key.builtin_cmd),
915bf0f6 2022 rules_str(rules, rule->rule.filename_off),
baa30fbc 2023 rule->rule.filename_line);
912541b0
KS
2024 /* return the result from earlier run */
2025 if (event->builtin_ret & (1 << cur->key.builtin_cmd))
7d6884b6 2026 if (cur->key.op != OP_NOMATCH)
912541b0
KS
2027 goto nomatch;
2028 break;
2029 }
2030 /* mark as ran */
2031 event->builtin_run |= (1 << cur->key.builtin_cmd);
2032 }
2033
e20a9171 2034 udev_event_apply_format(event, rules_str(rules, cur->key.value_off), command, sizeof(command), false);
9f6445e3 2035 log_debug("IMPORT builtin '%s' %s:%u",
baa30fbc 2036 udev_builtin_name(cur->key.builtin_cmd),
915bf0f6 2037 rules_str(rules, rule->rule.filename_off),
baa30fbc 2038 rule->rule.filename_line);
912541b0
KS
2039
2040 if (udev_builtin_run(event->dev, cur->key.builtin_cmd, command, false) != 0) {
2041 /* remember failure */
9f6445e3 2042 log_debug("IMPORT builtin '%s' returned non-zero",
baa30fbc 2043 udev_builtin_name(cur->key.builtin_cmd));
912541b0
KS
2044 event->builtin_ret |= (1 << cur->key.builtin_cmd);
2045 if (cur->key.op != OP_NOMATCH)
2046 goto nomatch;
2047 }
2048 break;
2049 }
2050 case TK_M_IMPORT_DB: {
915bf0f6 2051 const char *key = rules_str(rules, cur->key.value_off);
912541b0
KS
2052 const char *value;
2053
2054 value = udev_device_get_property_value(event->dev_db, key);
df546eb5
TG
2055 if (value != NULL)
2056 udev_device_add_property(event->dev, key, value);
2057 else {
912541b0
KS
2058 if (cur->key.op != OP_NOMATCH)
2059 goto nomatch;
2060 }
2061 break;
2062 }
2063 case TK_M_IMPORT_CMDLINE: {
88b013b2 2064 _cleanup_free_ char *value = NULL;
912541b0 2065 bool imported = false;
88b013b2 2066 const char *key;
912541b0 2067
88b013b2
LP
2068 key = rules_str(rules, cur->key.value_off);
2069
2070 r = proc_cmdline_get_key(key, PROC_CMDLINE_VALUE_OPTIONAL, &value);
2071 if (r < 0)
2072 log_debug_errno(r, "Failed to read %s from /proc/cmdline, ignoring: %m", key);
2073 else if (r > 0) {
2074 imported = true;
2075
2076 if (value)
2077 udev_device_add_property(event->dev, key, value);
2078 else
2079 /* we import simple flags as 'FLAG=1' */
2080 udev_device_add_property(event->dev, key, "1");
912541b0 2081 }
88b013b2 2082
912541b0
KS
2083 if (!imported && cur->key.op != OP_NOMATCH)
2084 goto nomatch;
2085 break;
2086 }
2087 case TK_M_IMPORT_PARENT: {
2088 char import[UTIL_PATH_SIZE];
2089
e20a9171 2090 udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import), false);
912541b0
KS
2091 if (import_parent_into_properties(event->dev, import) != 0)
2092 if (cur->key.op != OP_NOMATCH)
2093 goto nomatch;
2094 break;
2095 }
2096 case TK_M_RESULT:
2097 if (match_key(rules, cur, event->program_result) != 0)
2098 goto nomatch;
2099 break;
2100 case TK_A_STRING_ESCAPE_NONE:
2101 esc = ESCAPE_NONE;
2102 break;
2103 case TK_A_STRING_ESCAPE_REPLACE:
2104 esc = ESCAPE_REPLACE;
2105 break;
2106 case TK_A_DB_PERSIST:
2107 udev_device_set_db_persist(event->dev);
2108 break;
2109 case TK_A_INOTIFY_WATCH:
2110 if (event->inotify_watch_final)
2111 break;
2112 if (cur->key.op == OP_ASSIGN_FINAL)
2113 event->inotify_watch_final = true;
2114 event->inotify_watch = cur->key.watch;
2115 break;
2116 case TK_A_DEVLINK_PRIO:
2117 udev_device_set_devlink_priority(event->dev, cur->key.devlink_prio);
2118 break;
2119 case TK_A_OWNER: {
2120 char owner[UTIL_NAME_SIZE];
23bf8dd7 2121 const char *ow = owner;
912541b0
KS
2122
2123 if (event->owner_final)
2124 break;
2125 if (cur->key.op == OP_ASSIGN_FINAL)
2126 event->owner_final = true;
e20a9171 2127 udev_event_apply_format(event, rules_str(rules, cur->key.value_off), owner, sizeof(owner), false);
1edefa4f 2128 event->owner_set = true;
23bf8dd7
TG
2129 r = get_user_creds(&ow, &event->uid, NULL, NULL, NULL);
2130 if (r < 0) {
2da03cbf 2131 log_unknown_owner(r, "user", owner);
23bf8dd7
TG
2132 event->uid = 0;
2133 }
9f6445e3 2134 log_debug("OWNER %u %s:%u",
baa30fbc 2135 event->uid,
915bf0f6 2136 rules_str(rules, rule->rule.filename_off),
baa30fbc 2137 rule->rule.filename_line);
912541b0
KS
2138 break;
2139 }
2140 case TK_A_GROUP: {
2141 char group[UTIL_NAME_SIZE];
23bf8dd7 2142 const char *gr = group;
912541b0
KS
2143
2144 if (event->group_final)
2145 break;
2146 if (cur->key.op == OP_ASSIGN_FINAL)
2147 event->group_final = true;
e20a9171 2148 udev_event_apply_format(event, rules_str(rules, cur->key.value_off), group, sizeof(group), false);
1edefa4f 2149 event->group_set = true;
23bf8dd7
TG
2150 r = get_group_creds(&gr, &event->gid);
2151 if (r < 0) {
2da03cbf 2152 log_unknown_owner(r, "group", group);
23bf8dd7
TG
2153 event->gid = 0;
2154 }
9f6445e3 2155 log_debug("GROUP %u %s:%u",
baa30fbc 2156 event->gid,
915bf0f6 2157 rules_str(rules, rule->rule.filename_off),
baa30fbc 2158 rule->rule.filename_line);
912541b0
KS
2159 break;
2160 }
2161 case TK_A_MODE: {
2162 char mode_str[UTIL_NAME_SIZE];
2163 mode_t mode;
2164 char *endptr;
2165
2166 if (event->mode_final)
2167 break;
e20a9171 2168 udev_event_apply_format(event, rules_str(rules, cur->key.value_off), mode_str, sizeof(mode_str), false);
912541b0
KS
2169 mode = strtol(mode_str, &endptr, 8);
2170 if (endptr[0] != '\0') {
9f6445e3 2171 log_error("ignoring invalid mode '%s'", mode_str);
912541b0
KS
2172 break;
2173 }
2174 if (cur->key.op == OP_ASSIGN_FINAL)
2175 event->mode_final = true;
2176 event->mode_set = true;
2177 event->mode = mode;
9f6445e3 2178 log_debug("MODE %#o %s:%u",
baa30fbc 2179 event->mode,
915bf0f6 2180 rules_str(rules, rule->rule.filename_off),
baa30fbc 2181 rule->rule.filename_line);
912541b0
KS
2182 break;
2183 }
2184 case TK_A_OWNER_ID:
2185 if (event->owner_final)
2186 break;
2187 if (cur->key.op == OP_ASSIGN_FINAL)
2188 event->owner_final = true;
1edefa4f 2189 event->owner_set = true;
912541b0 2190 event->uid = cur->key.uid;
9f6445e3 2191 log_debug("OWNER %u %s:%u",
baa30fbc 2192 event->uid,
915bf0f6 2193 rules_str(rules, rule->rule.filename_off),
baa30fbc 2194 rule->rule.filename_line);
912541b0
KS
2195 break;
2196 case TK_A_GROUP_ID:
2197 if (event->group_final)
2198 break;
2199 if (cur->key.op == OP_ASSIGN_FINAL)
2200 event->group_final = true;
1edefa4f 2201 event->group_set = true;
912541b0 2202 event->gid = cur->key.gid;
9f6445e3 2203 log_debug("GROUP %u %s:%u",
baa30fbc 2204 event->gid,
915bf0f6 2205 rules_str(rules, rule->rule.filename_off),
baa30fbc 2206 rule->rule.filename_line);
912541b0
KS
2207 break;
2208 case TK_A_MODE_ID:
2209 if (event->mode_final)
2210 break;
2211 if (cur->key.op == OP_ASSIGN_FINAL)
2212 event->mode_final = true;
2213 event->mode_set = true;
2214 event->mode = cur->key.mode;
9f6445e3 2215 log_debug("MODE %#o %s:%u",
baa30fbc 2216 event->mode,
915bf0f6 2217 rules_str(rules, rule->rule.filename_off),
baa30fbc 2218 rule->rule.filename_line);
912541b0 2219 break;
c26547d6 2220 case TK_A_SECLABEL: {
4f985bd8 2221 char label_str[UTIL_LINE_SIZE] = {};
c26547d6
KS
2222 const char *name, *label;
2223
2224 name = rules_str(rules, cur->key.attr_off);
e20a9171 2225 udev_event_apply_format(event, rules_str(rules, cur->key.value_off), label_str, sizeof(label_str), false);
4f985bd8
MS
2226 if (label_str[0] != '\0')
2227 label = label_str;
2228 else
2229 label = rules_str(rules, cur->key.value_off);
2230
4c701096 2231 if (IN_SET(cur->key.op, OP_ASSIGN, OP_ASSIGN_FINAL))
c26547d6
KS
2232 udev_list_cleanup(&event->seclabel_list);
2233 udev_list_entry_add(&event->seclabel_list, name, label);
9f6445e3 2234 log_debug("SECLABEL{%s}='%s' %s:%u",
c26547d6
KS
2235 name, label,
2236 rules_str(rules, rule->rule.filename_off),
2237 rule->rule.filename_line);
2238 break;
2239 }
912541b0 2240 case TK_A_ENV: {
915bf0f6
KS
2241 const char *name = rules_str(rules, cur->key.attr_off);
2242 char *value = rules_str(rules, cur->key.value_off);
07845c14
KS
2243 char value_new[UTIL_NAME_SIZE];
2244 const char *value_old = NULL;
912541b0 2245
07845c14
KS
2246 if (value[0] == '\0') {
2247 if (cur->key.op == OP_ADD)
2248 break;
912541b0 2249 udev_device_add_property(event->dev, name, NULL);
07845c14 2250 break;
912541b0 2251 }
07845c14
KS
2252
2253 if (cur->key.op == OP_ADD)
2254 value_old = udev_device_get_property_value(event->dev, name);
2255 if (value_old) {
2256 char temp[UTIL_NAME_SIZE];
2257
2258 /* append value separated by space */
e20a9171 2259 udev_event_apply_format(event, value, temp, sizeof(temp), false);
d5a89d7d 2260 strscpyl(value_new, sizeof(value_new), value_old, " ", temp, NULL);
07845c14 2261 } else
e20a9171 2262 udev_event_apply_format(event, value, value_new, sizeof(value_new), false);
07845c14 2263
df546eb5 2264 udev_device_add_property(event->dev, name, value_new);
912541b0
KS
2265 break;
2266 }
2267 case TK_A_TAG: {
2268 char tag[UTIL_PATH_SIZE];
2269 const char *p;
2270
e20a9171 2271 udev_event_apply_format(event, rules_str(rules, cur->key.value_off), tag, sizeof(tag), false);
4c701096 2272 if (IN_SET(cur->key.op, OP_ASSIGN, OP_ASSIGN_FINAL))
912541b0
KS
2273 udev_device_cleanup_tags_list(event->dev);
2274 for (p = tag; *p != '\0'; p++) {
2275 if ((*p >= 'a' && *p <= 'z') ||
2276 (*p >= 'A' && *p <= 'Z') ||
2277 (*p >= '0' && *p <= '9') ||
4c701096 2278 IN_SET(*p, '-', '_'))
912541b0 2279 continue;
9f6445e3 2280 log_error("ignoring invalid tag name '%s'", tag);
912541b0
KS
2281 break;
2282 }
8e3ba377
DH
2283 if (cur->key.op == OP_REMOVE)
2284 udev_device_remove_tag(event->dev, tag);
2285 else
2286 udev_device_add_tag(event->dev, tag);
912541b0
KS
2287 break;
2288 }
2289 case TK_A_NAME: {
915bf0f6 2290 const char *name = rules_str(rules, cur->key.value_off);
0ecfcbd4 2291
912541b0
KS
2292 char name_str[UTIL_PATH_SIZE];
2293 int count;
2294
2295 if (event->name_final)
2296 break;
2297 if (cur->key.op == OP_ASSIGN_FINAL)
2298 event->name_final = true;
e20a9171 2299 udev_event_apply_format(event, name, name_str, sizeof(name_str), false);
4c701096 2300 if (IN_SET(esc, ESCAPE_UNSET, ESCAPE_REPLACE)) {
912541b0
KS
2301 count = util_replace_chars(name_str, "/");
2302 if (count > 0)
9f6445e3 2303 log_debug("%i character(s) replaced", count);
912541b0 2304 }
6ada823a 2305 if (major(udev_device_get_devnum(event->dev)) &&
fbd0b64f 2306 !streq(name_str, udev_device_get_devnode(event->dev) + STRLEN("/dev/"))) {
99f16bb8
ZJS
2307 log_error("NAME=\"%s\" ignored, kernel device nodes cannot be renamed; please fix it in %s:%u\n",
2308 name,
2309 rules_str(rules, rule->rule.filename_off),
2310 rule->rule.filename_line);
6ada823a 2311 break;
0ecfcbd4 2312 }
4fd6af76
ZJS
2313 if (free_and_strdup(&event->name, name_str) < 0) {
2314 log_oom();
2315 return;
2316 }
9f6445e3 2317 log_debug("NAME '%s' %s:%u",
baa30fbc 2318 event->name,
915bf0f6 2319 rules_str(rules, rule->rule.filename_off),
baa30fbc 2320 rule->rule.filename_line);
912541b0
KS
2321 break;
2322 }
2323 case TK_A_DEVLINK: {
2324 char temp[UTIL_PATH_SIZE];
2325 char filename[UTIL_PATH_SIZE];
2326 char *pos, *next;
2327 int count = 0;
2328
2329 if (event->devlink_final)
2330 break;
2331 if (major(udev_device_get_devnum(event->dev)) == 0)
2332 break;
2333 if (cur->key.op == OP_ASSIGN_FINAL)
2334 event->devlink_final = true;
4c701096 2335 if (IN_SET(cur->key.op, OP_ASSIGN, OP_ASSIGN_FINAL))
912541b0
KS
2336 udev_device_cleanup_devlinks_list(event->dev);
2337
2338 /* allow multiple symlinks separated by spaces */
0a10235e 2339 udev_event_apply_format(event, rules_str(rules, cur->key.value_off), temp, sizeof(temp), esc != ESCAPE_NONE);
912541b0
KS
2340 if (esc == ESCAPE_UNSET)
2341 count = util_replace_chars(temp, "/ ");
2342 else if (esc == ESCAPE_REPLACE)
2343 count = util_replace_chars(temp, "/");
2344 if (count > 0)
9f6445e3 2345 log_debug("%i character(s) replaced" , count);
912541b0
KS
2346 pos = temp;
2347 while (isspace(pos[0]))
2348 pos++;
2349 next = strchr(pos, ' ');
2350 while (next != NULL) {
2351 next[0] = '\0';
9f6445e3 2352 log_debug("LINK '%s' %s:%u", pos,
915bf0f6 2353 rules_str(rules, rule->rule.filename_off), rule->rule.filename_line);
d5a89d7d 2354 strscpyl(filename, sizeof(filename), "/dev/", pos, NULL);
8a173387 2355 udev_device_add_devlink(event->dev, filename);
912541b0
KS
2356 while (isspace(next[1]))
2357 next++;
2358 pos = &next[1];
2359 next = strchr(pos, ' ');
2360 }
2361 if (pos[0] != '\0') {
9f6445e3 2362 log_debug("LINK '%s' %s:%u", pos,
915bf0f6 2363 rules_str(rules, rule->rule.filename_off), rule->rule.filename_line);
d5a89d7d 2364 strscpyl(filename, sizeof(filename), "/dev/", pos, NULL);
8a173387 2365 udev_device_add_devlink(event->dev, filename);
912541b0
KS
2366 }
2367 break;
2368 }
2369 case TK_A_ATTR: {
915bf0f6 2370 const char *key_name = rules_str(rules, cur->key.attr_off);
912541b0
KS
2371 char attr[UTIL_PATH_SIZE];
2372 char value[UTIL_NAME_SIZE];
fdd21be6 2373 _cleanup_fclose_ FILE *f = NULL;
912541b0
KS
2374
2375 if (util_resolve_subsys_kernel(event->udev, key_name, attr, sizeof(attr), 0) != 0)
d5a89d7d 2376 strscpyl(attr, sizeof(attr), udev_device_get_syspath(event->dev), "/", key_name, NULL);
912541b0
KS
2377 attr_subst_subdir(attr, sizeof(attr));
2378
e20a9171 2379 udev_event_apply_format(event, rules_str(rules, cur->key.value_off), value, sizeof(value), false);
9f6445e3 2380 log_debug("ATTR '%s' writing '%s' %s:%u", attr, value,
915bf0f6 2381 rules_str(rules, rule->rule.filename_off),
baa30fbc 2382 rule->rule.filename_line);
47ef94ac 2383 f = fopen(attr, "we");
fdd21be6 2384 if (f == NULL)
56f64d95 2385 log_error_errno(errno, "error opening ATTR{%s} for writing: %m", attr);
fdd21be6
ZJS
2386 else if (fprintf(f, "%s", value) <= 0)
2387 log_error_errno(errno, "error writing ATTR{%s}: %m", attr);
912541b0
KS
2388 break;
2389 }
f4cf2e5b
KS
2390 case TK_A_SYSCTL: {
2391 char filename[UTIL_PATH_SIZE];
2392 char value[UTIL_NAME_SIZE];
f4cf2e5b 2393
e20a9171 2394 udev_event_apply_format(event, rules_str(rules, cur->key.attr_off), filename, sizeof(filename), false);
f4cf2e5b 2395 sysctl_normalize(filename);
e20a9171 2396 udev_event_apply_format(event, rules_str(rules, cur->key.value_off), value, sizeof(value), false);
f4cf2e5b
KS
2397 log_debug("SYSCTL '%s' writing '%s' %s:%u", filename, value,
2398 rules_str(rules, rule->rule.filename_off), rule->rule.filename_line);
2399 r = sysctl_write(filename, value);
2400 if (r < 0)
46b57b6e 2401 log_error_errno(r, "error writing SYSCTL{%s}='%s': %m", filename, value);
f4cf2e5b
KS
2402 break;
2403 }
83cd6b75
KS
2404 case TK_A_RUN_BUILTIN:
2405 case TK_A_RUN_PROGRAM: {
2406 struct udev_list_entry *entry;
2407
4c701096 2408 if (IN_SET(cur->key.op, OP_ASSIGN, OP_ASSIGN_FINAL))
912541b0 2409 udev_list_cleanup(&event->run_list);
9f6445e3 2410 log_debug("RUN '%s' %s:%u",
915bf0f6
KS
2411 rules_str(rules, cur->key.value_off),
2412 rules_str(rules, rule->rule.filename_off),
baa30fbc 2413 rule->rule.filename_line);
915bf0f6 2414 entry = udev_list_entry_add(&event->run_list, rules_str(rules, cur->key.value_off), NULL);
83cd6b75 2415 udev_list_entry_set_num(entry, cur->key.builtin_cmd);
912541b0
KS
2416 break;
2417 }
2418 case TK_A_GOTO:
2419 if (cur->key.rule_goto == 0)
2420 break;
2421 cur = &rules->tokens[cur->key.rule_goto];
2422 continue;
2423 case TK_END:
4fd6af76 2424 return;
912541b0
KS
2425
2426 case TK_M_PARENTS_MIN:
2427 case TK_M_PARENTS_MAX:
2428 case TK_M_MAX:
2429 case TK_UNSET:
9f6445e3 2430 log_error("wrong type %u", cur->type);
912541b0
KS
2431 goto nomatch;
2432 }
2433
2434 cur++;
2435 continue;
2436 nomatch:
2437 /* fast-forward to next rule */
2438 cur = rule + rule->rule.token_count;
912541b0 2439 }
6880b25d 2440}
761dfddc 2441
9ec6e95b 2442int udev_rules_apply_static_dev_perms(struct udev_rules *rules) {
912541b0
KS
2443 struct token *cur;
2444 struct token *rule;
2445 uid_t uid = 0;
2446 gid_t gid = 0;
2447 mode_t mode = 0;
84b6ad70
TG
2448 _cleanup_strv_free_ char **tags = NULL;
2449 char **t;
2450 FILE *f = NULL;
2451 _cleanup_free_ char *path = NULL;
fdd21be6 2452 int r;
912541b0
KS
2453
2454 if (rules->tokens == NULL)
84b6ad70 2455 return 0;
912541b0
KS
2456
2457 cur = &rules->tokens[0];
2458 rule = cur;
2459 for (;;) {
2460 switch (cur->type) {
2461 case TK_RULE:
2462 /* current rule */
2463 rule = cur;
2464
2465 /* skip rules without a static_node tag */
2466 if (!rule->rule.has_static_node)
2467 goto next;
2468
2469 uid = 0;
2470 gid = 0;
2471 mode = 0;
97b11eed 2472 tags = strv_free(tags);
912541b0
KS
2473 break;
2474 case TK_A_OWNER_ID:
2475 uid = cur->key.uid;
2476 break;
2477 case TK_A_GROUP_ID:
2478 gid = cur->key.gid;
2479 break;
2480 case TK_A_MODE_ID:
2481 mode = cur->key.mode;
84b6ad70
TG
2482 break;
2483 case TK_A_TAG:
2484 r = strv_extend(&tags, rules_str(rules, cur->key.value_off));
2485 if (r < 0)
2486 goto finish;
2487
912541b0
KS
2488 break;
2489 case TK_A_STATIC_NODE: {
84b6ad70
TG
2490 char device_node[UTIL_PATH_SIZE];
2491 char tags_dir[UTIL_PATH_SIZE];
2492 char tag_symlink[UTIL_PATH_SIZE];
912541b0
KS
2493 struct stat stats;
2494
2495 /* we assure, that the permissions tokens are sorted before the static token */
ca2bb160 2496
84b6ad70 2497 if (mode == 0 && uid == 0 && gid == 0 && tags == NULL)
912541b0 2498 goto next;
d6f116a7 2499
84b6ad70 2500 strscpyl(device_node, sizeof(device_node), "/dev/", rules_str(rules, cur->key.value_off), NULL);
ca2bb160
KS
2501 if (stat(device_node, &stats) != 0)
2502 break;
2503 if (!S_ISBLK(stats.st_mode) && !S_ISCHR(stats.st_mode))
2504 break;
84b6ad70 2505
d6f116a7 2506 /* export the tags to a directory as symlinks, allowing otherwise dead nodes to be tagged */
84b6ad70 2507 if (tags) {
84b6ad70
TG
2508 STRV_FOREACH(t, tags) {
2509 _cleanup_free_ char *unescaped_filename = NULL;
2510
2511 strscpyl(tags_dir, sizeof(tags_dir), "/run/udev/static_node-tags/", *t, "/", NULL);
2512 r = mkdir_p(tags_dir, 0755);
f647962d
MS
2513 if (r < 0)
2514 return log_error_errno(r, "failed to create %s: %m", tags_dir);
84b6ad70
TG
2515
2516 unescaped_filename = xescape(rules_str(rules, cur->key.value_off), "/.");
2517
2518 strscpyl(tag_symlink, sizeof(tag_symlink), tags_dir, unescaped_filename, NULL);
2519 r = symlink(device_node, tag_symlink);
4a62c710
MS
2520 if (r < 0 && errno != EEXIST)
2521 return log_error_errno(errno, "failed to create symlink %s -> %s: %m",
2522 tag_symlink, device_node);
84b6ad70
TG
2523 }
2524 }
2525
15a72200
TG
2526 /* don't touch the permissions if only the tags were set */
2527 if (mode == 0 && uid == 0 && gid == 0)
d6f116a7
KS
2528 break;
2529
912541b0
KS
2530 if (mode == 0) {
2531 if (gid > 0)
2532 mode = 0660;
2533 else
2534 mode = 0600;
2535 }
2536 if (mode != (stats.st_mode & 01777)) {
490f0087 2537 r = chmod(device_node, mode);
25f027c5
ZJS
2538 if (r < 0)
2539 return log_error_errno(errno, "Failed to chmod '%s' %#o: %m",
2540 device_node, mode);
2541 else
9f6445e3 2542 log_debug("chmod '%s' %#o", device_node, mode);
912541b0
KS
2543 }
2544
2545 if ((uid != 0 && uid != stats.st_uid) || (gid != 0 && gid != stats.st_gid)) {
490f0087 2546 r = chown(device_node, uid, gid);
25f027c5
ZJS
2547 if (r < 0)
2548 return log_error_errno(errno, "Failed to chown '%s' %u %u: %m",
2549 device_node, uid, gid);
2550 else
9f6445e3 2551 log_debug("chown '%s' %u %u", device_node, uid, gid);
912541b0
KS
2552 }
2553
84b6ad70 2554 utimensat(AT_FDCWD, device_node, NULL, 0);
912541b0
KS
2555 break;
2556 }
2557 case TK_END:
84b6ad70 2558 goto finish;
912541b0
KS
2559 }
2560
2561 cur++;
2562 continue;
761dfddc 2563next:
912541b0
KS
2564 /* fast-forward to next rule */
2565 cur = rule + rule->rule.token_count;
2566 continue;
2567 }
84b6ad70
TG
2568
2569finish:
2570 if (f) {
2571 fflush(f);
2572 fchmod(fileno(f), 0644);
2573 if (ferror(f) || rename(path, "/run/udev/static_node-tags") < 0) {
fdd21be6
ZJS
2574 unlink_noerrno("/run/udev/static_node-tags");
2575 unlink_noerrno(path);
2576 return -errno;
84b6ad70 2577 }
84b6ad70
TG
2578 }
2579
fdd21be6 2580 return 0;
761dfddc 2581}