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