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