1/*
2 * Copyright (C) 2003,2004 Greg Kroah-Hartman <greg@kroah.com>
3 * Copyright (C) 2003-2006 Kay Sievers <kay.sievers@vrfy.org>
4 *
5 *	This program is free software; you can redistribute it and/or modify it
6 *	under the terms of the GNU General Public License as published by the
7 *	Free Software Foundation version 2 of the License.
8 *
9 *	This program is distributed in the hope that it will be useful, but
10 *	WITHOUT ANY WARRANTY; without even the implied warranty of
11 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 *	General Public License for more details.
13 *
14 *	You should have received a copy of the GNU General Public License along
15 *	with this program; if not, write to the Free Software Foundation, Inc.,
16 *	51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 *
18 */
19
20#include <stddef.h>
21#include <stdlib.h>
22#include <string.h>
23#include <stdio.h>
24#include <ctype.h>
25#include <unistd.h>
26#include <sys/stat.h>
27#include <errno.h>
28
29#include "udev.h"
30#include "udev_rules.h"
31
32
33void udev_rules_iter_init(struct udev_rules *rules)
34{
35	dbg("bufsize=%zi", rules->bufsize);
36	rules->current = 0;
37}
38
39struct udev_rule *udev_rules_iter_next(struct udev_rules *rules)
40{
41	static struct udev_rule *rule;
42
43	if (!rules)
44		return NULL;
45
46	dbg("current=%zi", rules->current);
47	if (rules->current >= rules->bufsize) {
48		dbg("no more rules");
49		return NULL;
50	}
51
52	/* get next rule */
53	rule = (struct udev_rule *) (rules->buf + rules->current);
54	rules->current += sizeof(struct udev_rule) + rule->bufsize;
55
56	return rule;
57}
58
59struct udev_rule *udev_rules_iter_label(struct udev_rules *rules, const char *label)
60{
61	static struct udev_rule *rule;
62
63next:
64	dbg("current=%zi", rules->current);
65	if (rules->current >= rules->bufsize) {
66		dbg("no more rules");
67		return NULL;
68	}
69	rule = (struct udev_rule *) (rules->buf + rules->current);
70
71	if (strcmp(&rule->buf[rule->label.val_off], label) != 0) {
72		dbg("moving forward, looking for label '%s'", label);
73		rules->current += sizeof(struct udev_rule) + rule->bufsize;
74		goto next;
75	}
76
77	dbg("found label '%s'", label);
78	return rule;
79}
80
81static int get_key(char **line, char **key, enum key_operation *operation, char **value)
82{
83	char *linepos;
84	char *temp;
85
86	linepos = *line;
87	if (linepos == NULL && linepos[0] == '\0')
88		return -1;
89
90	/* skip whitespace */
91	while (isspace(linepos[0]) || linepos[0] == ',')
92		linepos++;
93
94	/* get the key */
95	if (linepos[0] == '\0')
96		return -1;
97	*key = linepos;
98
99	while (1) {
100		linepos++;
101		if (linepos[0] == '\0')
102			return -1;
103		if (isspace(linepos[0]))
104			break;
105		if (linepos[0] == '=')
106			break;
107		if ((linepos[0] == '+') || (linepos[0] == '!') || (linepos[0] == ':'))
108			if (linepos[1] == '=')
109				break;
110	}
111
112	/* remember end of key */
113	temp = linepos;
114
115	/* skip whitespace after key */
116	while (isspace(linepos[0]))
117		linepos++;
118	if (linepos[0] == '\0')
119		return -1;
120
121	/* get operation type */
122	if (linepos[0] == '=' && linepos[1] == '=') {
123		*operation = KEY_OP_MATCH;
124		linepos += 2;
125		dbg("operator=match");
126	} else if (linepos[0] == '!' && linepos[1] == '=') {
127		*operation = KEY_OP_NOMATCH;
128		linepos += 2;
129		dbg("operator=nomatch");
130	} else if (linepos[0] == '+' && linepos[1] == '=') {
131		*operation = KEY_OP_ADD;
132		linepos += 2;
133		dbg("operator=add");
134	} else if (linepos[0] == '=') {
135		*operation = KEY_OP_ASSIGN;
136		linepos++;
137		dbg("operator=assign");
138	} else if (linepos[0] == ':' && linepos[1] == '=') {
139		*operation = KEY_OP_ASSIGN_FINAL;
140		linepos += 2;
141		dbg("operator=assign_final");
142	} else
143		return -1;
144
145	/* terminate key */
146	temp[0] = '\0';
147	dbg("key='%s'", *key);
148
149	/* skip whitespace after operator */
150	while (isspace(linepos[0]))
151		linepos++;
152	if (linepos[0] == '\0')
153		return -1;
154
155	/* get the value*/
156	if (linepos[0] == '"')
157		linepos++;
158	else
159		return -1;
160	*value = linepos;
161
162	temp = strchr(linepos, '"');
163	if (!temp)
164		return -1;
165	temp[0] = '\0';
166	temp++;
167	dbg("value='%s'", *value);
168
169	/* move line to next key */
170	*line = temp;
171
172	return 0;
173}
174
175/* extract possible KEY{attr} */
176static char *get_key_attribute(char *str)
177{
178	char *pos;
179	char *attr;
180
181	attr = strchr(str, '{');
182	if (attr != NULL) {
183		attr++;
184		pos = strchr(attr, '}');
185		if (pos == NULL) {
186			err("missing closing brace for format");
187			return NULL;
188		}
189		pos[0] = '\0';
190		dbg("attribute='%s'", attr);
191		return attr;
192	}
193
194	return NULL;
195}
196
197static int add_rule_key(struct udev_rule *rule, struct key *key,
198			enum key_operation operation, const char *value)
199{
200	size_t val_len = strnlen(value, PATH_SIZE);
201
202	key->operation = operation;
203
204	key->val_off = rule->bufsize;
205	strlcpy(rule->buf + rule->bufsize, value, val_len+1);
206	rule->bufsize += val_len+1;
207
208	return 0;
209}
210
211static int add_rule_key_pair(struct udev_rule *rule, struct key_pairs *pairs,
212			     enum key_operation operation, const char *key, const char *value)
213{
214	size_t key_len = strnlen(key, PATH_SIZE);
215
216	if (pairs->count >= PAIRS_MAX) {
217		err("skip, too many keys of the same type in a single rule");
218		return -1;
219	}
220
221	add_rule_key(rule, &pairs->keys[pairs->count].key, operation, value);
222
223	/* add the key-name of the pair */
224	pairs->keys[pairs->count].key_name_off = rule->bufsize;
225	strlcpy(rule->buf + rule->bufsize, key, key_len+1);
226	rule->bufsize += key_len+1;
227
228	pairs->count++;
229
230	return 0;
231}
232
233static int add_to_rules(struct udev_rules *rules, char *line, const char *filename, unsigned int lineno)
234{
235	char buf[sizeof(struct udev_rule) + LINE_SIZE];
236	struct udev_rule *rule;
237	size_t rule_size;
238	int valid;
239	char *linepos;
240	char *attr;
241	size_t padding;
242	int physdev = 0;
243	int retval;
244
245	memset(buf, 0x00, sizeof(buf));
246	rule = (struct udev_rule *) buf;
247	linepos = line;
248	valid = 0;
249
250	/* get all the keys */
251	while (1) {
252		char *key;
253		char *value;
254		enum key_operation operation = KEY_OP_UNSET;
255
256		retval = get_key(&linepos, &key, &operation, &value);
257		if (retval)
258			break;
259
260		if (strcasecmp(key, "ACTION") == 0) {
261			if (operation != KEY_OP_MATCH &&
262			    operation != KEY_OP_NOMATCH) {
263				err("invalid ACTION operation");
264				goto invalid;
265			}
266			add_rule_key(rule, &rule->action, operation, value);
267			valid = 1;
268			continue;
269		}
270
271		if (strcasecmp(key, "DEVPATH") == 0) {
272			if (operation != KEY_OP_MATCH &&
273			    operation != KEY_OP_NOMATCH) {
274				err("invalid DEVPATH operation");
275				goto invalid;
276			}
277			add_rule_key(rule, &rule->devpath, operation, value);
278			valid = 1;
279			continue;
280		}
281
282		if (strcasecmp(key, "KERNEL") == 0) {
283			if (operation != KEY_OP_MATCH &&
284			    operation != KEY_OP_NOMATCH) {
285				err("invalid KERNEL operation");
286				goto invalid;
287			}
288			add_rule_key(rule, &rule->kernel, operation, value);
289			valid = 1;
290			continue;
291		}
292
293		if (strcasecmp(key, "SUBSYSTEM") == 0) {
294			if (operation != KEY_OP_MATCH &&
295			    operation != KEY_OP_NOMATCH) {
296				err("invalid SUBSYSTEM operation");
297				goto invalid;
298			}
299			/* bus, class, subsystem events should all be the same */
300			if (strcmp(value, "subsystem") == 0 ||
301			    strcmp(value, "bus") == 0 ||
302			    strcmp(value, "class") == 0) {
303				if (strcmp(value, "bus") == 0 || strcmp(value, "class") == 0)
304					err("'%s' must be specified as 'subsystem' "
305					    "please fix it in %s:%u", value, filename, lineno);
306				add_rule_key(rule, &rule->subsystem, operation, "subsystem|class|bus");
307			} else
308				add_rule_key(rule, &rule->subsystem, operation, value);
309			valid = 1;
310			continue;
311		}
312
313		if (strcasecmp(key, "DRIVER") == 0) {
314			if (operation != KEY_OP_MATCH &&
315			    operation != KEY_OP_NOMATCH) {
316				err("invalid DRIVER operation");
317				goto invalid;
318			}
319			add_rule_key(rule, &rule->driver, operation, value);
320			valid = 1;
321			continue;
322		}
323
324		if (strncasecmp(key, "ATTR{", sizeof("ATTR{")-1) == 0) {
325			attr = get_key_attribute(key + sizeof("ATTR")-1);
326			if (attr == NULL) {
327				err("error parsing ATTR attribute");
328				goto invalid;
329			}
330			if (add_rule_key_pair(rule, &rule->attr, operation, attr, value) != 0)
331				goto invalid;
332			valid = 1;
333			continue;
334		}
335
336		if (strcasecmp(key, "KERNELS") == 0 ||
337		    strcasecmp(key, "ID") == 0) {
338			if (operation != KEY_OP_MATCH &&
339			    operation != KEY_OP_NOMATCH) {
340				err("invalid KERNELS operation");
341				goto invalid;
342			}
343			add_rule_key(rule, &rule->kernels, operation, value);
344			valid = 1;
345			continue;
346		}
347
348		if (strcasecmp(key, "SUBSYSTEMS") == 0 ||
349		    strcasecmp(key, "BUS") == 0) {
350			if (operation != KEY_OP_MATCH &&
351			    operation != KEY_OP_NOMATCH) {
352				err("invalid SUBSYSTEMS operation");
353				goto invalid;
354			}
355			add_rule_key(rule, &rule->subsystems, operation, value);
356			valid = 1;
357			continue;
358		}
359
360		if (strcasecmp(key, "DRIVERS") == 0) {
361			if (operation != KEY_OP_MATCH &&
362			    operation != KEY_OP_NOMATCH) {
363				err("invalid DRIVERS operation");
364				goto invalid;
365			}
366			add_rule_key(rule, &rule->drivers, operation, value);
367			valid = 1;
368			continue;
369		}
370
371		if (strncasecmp(key, "ATTRS{", sizeof("ATTRS{")-1) == 0 ||
372		    strncasecmp(key, "SYSFS{", sizeof("SYSFS{")-1) == 0) {
373			if (operation != KEY_OP_MATCH &&
374			    operation != KEY_OP_NOMATCH) {
375				err("invalid ATTRS operation");
376				goto invalid;
377			}
378			attr = get_key_attribute(key + sizeof("ATTRS")-1);
379			if (attr == NULL) {
380				err("error parsing ATTRS attribute");
381				goto invalid;
382			}
383			if (strncmp(attr, "device/", 7) == 0)
384				err("the 'device' link is deprecated and will be removed from a future kernel, "
385				    "please fix it in %s:%u", filename, lineno);
386			else if (strstr(attr, "../") != NULL)
387				err("do not reference parent sysfs directories directly, that may break with a future kernel, "
388				    "please fix it in %s:%u", filename, lineno);
389			if (add_rule_key_pair(rule, &rule->attrs, operation, attr, value) != 0)
390				goto invalid;
391			valid = 1;
392			continue;
393		}
394
395		if (strncasecmp(key, "ENV{", sizeof("ENV{")-1) == 0) {
396			attr = get_key_attribute(key + sizeof("ENV")-1);
397			if (attr == NULL) {
398				err("error parsing ENV attribute");
399				goto invalid;
400			}
401			if (strncmp(attr, "PHYSDEV", 7) == 0)
402				physdev = 1;
403			if (add_rule_key_pair(rule, &rule->env, operation, attr, value) != 0)
404				goto invalid;
405			valid = 1;
406			continue;
407		}
408
409		if (strcasecmp(key, "PROGRAM") == 0) {
410			add_rule_key(rule, &rule->program, operation, value);
411			valid = 1;
412			continue;
413		}
414
415		if (strcasecmp(key, "RESULT") == 0) {
416			if (operation != KEY_OP_MATCH &&
417			    operation != KEY_OP_NOMATCH) {
418				err("invalid RESULT operation");
419				goto invalid;
420			}
421			add_rule_key(rule, &rule->result, operation, value);
422			valid = 1;
423			continue;
424		}
425
426		if (strncasecmp(key, "IMPORT", sizeof("IMPORT")-1) == 0) {
427			attr = get_key_attribute(key + sizeof("IMPORT")-1);
428			if (attr && strstr(attr, "program")) {
429				dbg("IMPORT will be executed");
430				rule->import_type  = IMPORT_PROGRAM;
431			} else if (attr && strstr(attr, "file")) {
432				dbg("IMPORT will be included as file");
433				rule->import_type  = IMPORT_FILE;
434			} else if (attr && strstr(attr, "parent")) {
435				dbg("IMPORT will include the parent values");
436				rule->import_type = IMPORT_PARENT;
437			} else {
438				/* figure it out if it is executable */
439				char file[PATH_SIZE];
440				char *pos;
441				struct stat stats;
442
443				strlcpy(file, value, sizeof(file));
444				pos = strchr(file, ' ');
445				if (pos)
446					pos[0] = '\0';
447
448				/* allow programs in /lib/udev called without the path */
449				if (strchr(file, '/') == NULL) {
450					strlcpy(file, "/lib/udev/", sizeof(file));
451					strlcat(file, value, sizeof(file));
452					pos = strchr(file, ' ');
453					if (pos)
454						pos[0] = '\0';
455				}
456
457				dbg("IMPORT auto mode for '%s'", file);
458				if (!lstat(file, &stats) && (stats.st_mode & S_IXUSR)) {
459					dbg("IMPORT is executable, will be executed (autotype)");
460					rule->import_type  = IMPORT_PROGRAM;
461				} else {
462					dbg("IMPORT is not executable, will be included as file (autotype)");
463					rule->import_type  = IMPORT_FILE;
464				}
465			}
466			add_rule_key(rule, &rule->import, operation, value);
467			valid = 1;
468			continue;
469		}
470
471		if (strncasecmp(key, "TEST", sizeof("TEST")-1) == 0) {
472			attr = get_key_attribute(key + sizeof("TEST")-1);
473			if (attr != NULL)
474				rule->test_mode_mask = strtol(attr, NULL, 8);
475			add_rule_key(rule, &rule->test, operation, value);
476			valid = 1;
477			continue;
478		}
479
480		if (strcasecmp(key, "RUN") == 0) {
481			add_rule_key(rule, &rule->run, operation, value);
482			valid = 1;
483			continue;
484		}
485
486		if (strcasecmp(key, "WAIT_FOR_SYSFS") == 0) {
487			add_rule_key(rule, &rule->wait_for_sysfs, operation, value);
488			valid = 1;
489			continue;
490		}
491
492		if (strcasecmp(key, "LABEL") == 0) {
493			add_rule_key(rule, &rule->label, operation, value);
494			valid = 1;
495			continue;
496		}
497
498		if (strcasecmp(key, "GOTO") == 0) {
499			add_rule_key(rule, &rule->goto_label, operation, value);
500			valid = 1;
501			continue;
502		}
503
504		if (strncasecmp(key, "NAME", sizeof("NAME")-1) == 0) {
505			attr = get_key_attribute(key + sizeof("NAME")-1);
506			if (attr != NULL) {
507				if (strstr(attr, "all_partitions") != NULL) {
508					dbg("creation of partition nodes requested");
509					rule->partitions = DEFAULT_PARTITIONS_COUNT;
510				}
511				if (strstr(attr, "ignore_remove") != NULL) {
512					dbg("remove event should be ignored");
513					rule->ignore_remove = 1;
514				}
515			}
516			if (value[0] == '\0')
517				dbg("name empty, node creation supressed");
518			add_rule_key(rule, &rule->name, operation, value);
519			continue;
520		}
521
522		if (strcasecmp(key, "SYMLINK") == 0) {
523			add_rule_key(rule, &rule->symlink, operation, value);
524			valid = 1;
525			continue;
526		}
527
528		if (strcasecmp(key, "OWNER") == 0) {
529			valid = 1;
530			if (rules->resolve_names && (!strchr(value, '$') && !strchr(value, '%'))) {
531				char *endptr;
532				strtoul(value, &endptr, 10);
533				if (endptr[0] != '\0') {
534					char owner[32];
535					uid_t uid = lookup_user(value);
536					dbg("replacing username='%s' by id=%i", value, uid);
537					sprintf(owner, "%u", (unsigned int) uid);
538					add_rule_key(rule, &rule->owner, operation, owner);
539					continue;
540				}
541			}
542
543			add_rule_key(rule, &rule->owner, operation, value);
544			continue;
545		}
546
547		if (strcasecmp(key, "GROUP") == 0) {
548			valid = 1;
549			if (rules->resolve_names && (!strchr(value, '$') && !strchr(value, '%'))) {
550				char *endptr;
551				strtoul(value, &endptr, 10);
552				if (endptr[0] != '\0') {
553					char group[32];
554					gid_t gid = lookup_group(value);
555					dbg("replacing groupname='%s' by id=%i", value, gid);
556					sprintf(group, "%u", (unsigned int) gid);
557					add_rule_key(rule, &rule->group, operation, group);
558					continue;
559				}
560			}
561
562			add_rule_key(rule, &rule->group, operation, value);
563			continue;
564		}
565
566		if (strcasecmp(key, "MODE") == 0) {
567			rule->mode = strtol(value, NULL, 8);
568			rule->mode_operation = operation;
569			valid = 1;
570			continue;
571		}
572
573		if (strcasecmp(key, "OPTIONS") == 0) {
574			const char *pos;
575
576			if (strstr(value, "last_rule") != NULL) {
577				dbg("last rule to be applied");
578				rule->last_rule = 1;
579			}
580			if (strstr(value, "ignore_device") != NULL) {
581				dbg("device should be ignored");
582				rule->ignore_device = 1;
583			}
584			if (strstr(value, "ignore_remove") != NULL) {
585				dbg("remove event should be ignored");
586				rule->ignore_remove = 1;
587			}
588			pos = strstr(value, "link_priority=");
589			if (pos != NULL) {
590				rule->link_priority = atoi(&pos[strlen("link_priority=")]);
591				info("link priority=%i", rule->link_priority);
592			}
593			pos = strstr(value, "string_escape=");
594			if (pos != NULL) {
595				pos = &pos[strlen("string_escape=")];
596				if (strncmp(pos, "none", strlen("none")) == 0)
597					rule->string_escape = ESCAPE_NONE;
598				else if (strncmp(pos, "replace", strlen("replace")) == 0)
599					rule->string_escape = ESCAPE_REPLACE;
600			}
601			if (strstr(value, "all_partitions") != NULL) {
602				dbg("creation of partition nodes requested");
603				rule->partitions = DEFAULT_PARTITIONS_COUNT;
604			}
605			valid = 1;
606			continue;
607		}
608
609		err("unknown key '%s' in %s:%u", key, filename, lineno);
610	}
611
612	if (physdev && rule->wait_for_sysfs.operation == KEY_OP_UNSET)
613		err("PHYSDEV* values are deprecated and will be removed from a future kernel, "
614		    "please fix it in %s:%u", filename, lineno);
615
616	/* skip line if not any valid key was found */
617	if (!valid)
618		goto invalid;
619
620	/* grow buffer and add rule */
621	rule_size = sizeof(struct udev_rule) + rule->bufsize;
622	padding = (sizeof(size_t) - rule_size % sizeof(size_t)) % sizeof(size_t);
623	dbg("add %zi padding bytes", padding);
624	rule_size += padding;
625	rule->bufsize += padding;
626
627	rules->buf = realloc(rules->buf, rules->bufsize + rule_size);
628	if (!rules->buf) {
629		err("realloc failed");
630		goto exit;
631	}
632	dbg("adding rule to offset %zi", rules->bufsize);
633	memcpy(rules->buf + rules->bufsize, rule, rule_size);
634	rules->bufsize += rule_size;
635exit:
636	return 0;
637
638invalid:
639	err("invalid rule '%s:%u'", filename, lineno);
640	return -1;
641}
642
643static int parse_file(struct udev_rules *rules, const char *filename)
644{
645	char line[LINE_SIZE];
646	char *bufline;
647	unsigned int lineno;
648	char *buf;
649	size_t bufsize;
650	size_t cur;
651	size_t count;
652	int retval = 0;
653
654	if (file_map(filename, &buf, &bufsize) != 0) {
655		err("can't open '%s' as rules file: %s", filename, strerror(errno));
656		return -1;
657	}
658	info("reading '%s' as rules file", filename);
659
660	/* loop through the whole file */
661	cur = 0;
662	lineno = 0;
663	while (cur < bufsize) {
664		unsigned int i, j;
665
666		count = buf_get_line(buf, bufsize, cur);
667		bufline = &buf[cur];
668		cur += count+1;
669		lineno++;
670
671		/* eat the whitespace */
672		while ((count > 0) && isspace(bufline[0])) {
673			bufline++;
674			count--;
675		}
676		if (count == 0)
677			continue;
678
679		/* see if this is a comment */
680		if (bufline[0] == COMMENT_CHARACTER)
681			continue;
682
683		if (count >= sizeof(line)) {
684			err("line too long, rule skipped '%s:%u'", filename, lineno);
685			continue;
686		}
687
688		/* skip backslash and newline from multiline rules */
689		for (i = j = 0; i < count; i++) {
690			if (bufline[i] == '\\' && bufline[i+1] == '\n')
691				continue;
692
693			line[j++] = bufline[i];
694		}
695		line[j] = '\0';
696
697		dbg("read '%s'", line);
698		add_to_rules(rules, line, filename, lineno);
699	}
700
701	file_unmap(buf, bufsize);
702	return retval;
703}
704
705int udev_rules_init(struct udev_rules *rules, int resolve_names)
706{
707	struct stat stats;
708	int retval;
709
710	memset(rules, 0x00, sizeof(struct udev_rules));
711	rules->resolve_names = resolve_names;
712
713	/* parse rules file or all matching files in directory */
714	if (stat(udev_rules_dir, &stats) != 0)
715		return -1;
716
717	if ((stats.st_mode & S_IFMT) != S_IFDIR) {
718		dbg("parse single rules file '%s'", udev_rules_dir);
719		retval = parse_file(rules, udev_rules_dir);
720	} else {
721		struct name_entry *name_loop, *name_tmp;
722		LIST_HEAD(name_list);
723
724		dbg("parse rules directory '%s'", udev_rules_dir);
725		retval = add_matching_files(&name_list, udev_rules_dir, RULESFILE_SUFFIX);
726
727		list_for_each_entry_safe(name_loop, name_tmp, &name_list, node) {
728			if (stat(name_loop->name, &stats) == 0) {
729				if (stats.st_size)
730					parse_file(rules, name_loop->name);
731				else
732					dbg("empty rules file '%s'", name_loop->name);
733			} else
734				err("could not read '%s': %s", name_loop->name, strerror(errno));
735			list_del(&name_loop->node);
736			free(name_loop);
737		}
738	}
739
740	return retval;
741}
742
743void udev_rules_cleanup(struct udev_rules *rules)
744{
745	if (rules->buf) {
746		free(rules->buf);
747		rules->buf = NULL;
748	}
749}
750