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