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