#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
-#include <sys/stat.h>
+#include <dirent.h>
#include <errno.h>
+#include <sys/stat.h>
#include "udev.h"
#include "udev-rules.h"
if (linepos[0] == '=' && linepos[1] == '=') {
*operation = KEY_OP_MATCH;
linepos += 2;
- dbg(rules->udev, "operator=match\n");
+ dbg(rules->udev, "match:\n");
} else if (linepos[0] == '!' && linepos[1] == '=') {
*operation = KEY_OP_NOMATCH;
linepos += 2;
- dbg(rules->udev, "operator=nomatch\n");
+ dbg(rules->udev, "nomatch:\n");
} else if (linepos[0] == '+' && linepos[1] == '=') {
*operation = KEY_OP_ADD;
linepos += 2;
- dbg(rules->udev, "operator=add\n");
+ dbg(rules->udev, "add:\n");
} else if (linepos[0] == '=') {
*operation = KEY_OP_ASSIGN;
linepos++;
- dbg(rules->udev, "operator=assign\n");
+ dbg(rules->udev, "assign:\n");
} else if (linepos[0] == ':' && linepos[1] == '=') {
*operation = KEY_OP_ASSIGN_FINAL;
linepos += 2;
- dbg(rules->udev, "operator=assign_final\n");
+ dbg(rules->udev, "assign_final:\n");
} else
return -1;
/* terminate key */
temp[0] = '\0';
- dbg(rules->udev, "key='%s'\n", *key);
/* skip whitespace after operator */
while (isspace(linepos[0]))
return -1;
temp[0] = '\0';
temp++;
- dbg(rules->udev, "value='%s'\n", *value);
+ dbg(rules->udev, "'%s'-'%s'\n", *key, *value);
/* move line to next key */
*line = temp;
static int parse_file(struct udev_rules *rules, const char *filename)
{
+ FILE *f;
char line[UTIL_LINE_SIZE];
- char *bufline;
- unsigned int lineno;
- char *buf;
- size_t bufsize;
- size_t cur;
- size_t count;
- int retval = 0;
size_t start;
struct udev_rule *rule;
struct udev_rules_iter iter;
start = rules->bufsize;
+ info(rules->udev, "reading '%s' as rules file\n", filename);
- if (file_map(filename, &buf, &bufsize) != 0) {
- err(rules->udev, "can't open '%s' as rules file: %m\n", filename);
+ f = fopen(filename, "r");
+ if (f == NULL)
return -1;
- }
- info(rules->udev, "reading '%s' as rules file\n", filename);
- /* loop through the whole file */
- cur = 0;
- lineno = 0;
- while (cur < bufsize) {
- unsigned int i, j;
+ while(fgets(line, sizeof(line), f) != NULL) {
+ int line_nr = 0;
+ char *key;
+ size_t len;
- count = buf_get_line(buf, bufsize, cur);
- bufline = &buf[cur];
- cur += count+1;
- lineno++;
+ /* skip whitespace */
+ line_nr++;
+ key = line;
+ while (isspace(key[0]))
+ key++;
- /* eat the whitespace */
- while ((count > 0) && isspace(bufline[0])) {
- bufline++;
- count--;
- }
- if (count == 0)
+ /* comment */
+ if (key[0] == '#')
continue;
- /* see if this is a comment */
- if (bufline[0] == '#')
+ len = strlen(line);
+ if (len < 3)
continue;
- if (count >= sizeof(line)) {
- err(rules->udev, "line too long, rule skipped '%s:%u'\n", filename, lineno);
- continue;
+ /* continue reading if backslash+newline is found */
+ while (line[len-2] == '\\') {
+ if (fgets(&line[len-2], (sizeof(line)-len)+2, f) == NULL)
+ break;
+ line_nr++;
+ len = strlen(line);
}
- /* skip backslash and newline from multiline rules */
- for (i = j = 0; i < count; i++) {
- if (bufline[i] == '\\' && bufline[i+1] == '\n')
- continue;
-
- line[j++] = bufline[i];
+ if (len+1 >= sizeof(line)) {
+ err(rules->udev, "line too long '%s':%u, ignored\n", filename, line_nr);
+ continue;
}
- line[j] = '\0';
-
- dbg(rules->udev, "read '%s'\n", line);
- add_to_rules(rules, line, filename, lineno);
+ add_to_rules(rules, key, filename, line_nr);
}
+ fclose(f);
- /* Compute all goto targets within this file */
+ /* compute all goto targets within this file */
udev_rules_iter_init(&iter, rules);
udev_rules_iter_goto(&iter, start);
while((rule = udev_rules_iter_next(&iter))) {
}
}
}
+ return 0;
+}
+
+static int add_matching_files(struct udev *udev, struct udev_list_node *file_list, const char *dirname, const char *suffix)
+{
+ struct dirent *ent;
+ DIR *dir;
+ char filename[UTIL_PATH_SIZE];
+
+ dbg(udev, "open directory '%s'\n", dirname);
+ dir = opendir(dirname);
+ if (dir == NULL) {
+ err(udev, "unable to open '%s': %m\n", dirname);
+ return -1;
+ }
- file_unmap(buf, bufsize);
- return retval;
+ while (1) {
+ ent = readdir(dir);
+ if (ent == NULL || ent->d_name[0] == '\0')
+ break;
+
+ if ((ent->d_name[0] == '.') || (ent->d_name[0] == '#'))
+ continue;
+
+ /* look for file matching with specified suffix */
+ if (suffix != NULL) {
+ const char *ext;
+
+ ext = strrchr(ent->d_name, '.');
+ if (ext == NULL)
+ continue;
+ if (strcmp(ext, suffix) != 0)
+ continue;
+ }
+ dbg(udev, "put file '%s/%s' into list\n", dirname, ent->d_name);
+
+ snprintf(filename, sizeof(filename), "%s/%s", dirname, ent->d_name);
+ filename[sizeof(filename)-1] = '\0';
+ udev_list_entry_add(udev, file_list, filename, NULL, 1, 1);
+ }
+
+ closedir(dir);
+ return 0;
}
int udev_rules_init(struct udev *udev, struct udev_rules *rules, int resolve_names)
{
struct stat statbuf;
char filename[PATH_MAX];
- LIST_HEAD(name_list);
- LIST_HEAD(sort_list);
- struct name_entry *name_loop, *name_tmp;
- struct name_entry *sort_loop, *sort_tmp;
+ struct udev_list_node file_list;
+ struct udev_list_entry *file_loop, *file_tmp;
int retval = 0;
memset(rules, 0x00, sizeof(struct udev_rules));
rules->udev = udev;
rules->resolve_names = resolve_names;
+ udev_list_init(&file_list);
if (udev_get_rules_path(udev) != NULL) {
/* custom rules location for testing */
- add_matching_files(udev, &name_list, udev_get_rules_path(udev), ".rules");
+ add_matching_files(udev, &file_list, udev_get_rules_path(udev), ".rules");
} else {
+ struct udev_list_node sort_list;
+ struct udev_list_entry *sort_loop, *sort_tmp;
+
/* read user/custom rules */
- add_matching_files(udev, &name_list, SYSCONFDIR "/udev/rules.d", ".rules");
+ add_matching_files(udev, &file_list, SYSCONFDIR "/udev/rules.d", ".rules");
/* read dynamic/temporary rules */
util_strlcpy(filename, udev_get_dev_path(udev), sizeof(filename));
mkdir(filename, 0755);
udev_selinux_resetfscreatecon(udev);
}
+ udev_list_init(&sort_list);
add_matching_files(udev, &sort_list, filename, ".rules");
/* read default rules */
add_matching_files(udev, &sort_list, UDEV_PREFIX "/lib/udev/rules.d", ".rules");
/* sort all rules files by basename into list of files */
- list_for_each_entry_safe(sort_loop, sort_tmp, &sort_list, node) {
- const char *sort_base = strrchr(sort_loop->name, '/');
+ udev_list_entry_foreach_safe(sort_loop, sort_tmp, udev_list_get_entry(&sort_list)) {
+ const char *sort_name = udev_list_entry_get_name(sort_loop);
+ const char *sort_base = strrchr(sort_name, '/');
if (sort_base == NULL)
continue;
- list_for_each_entry_safe(name_loop, name_tmp, &name_list, node) {
- const char *name_base = strrchr(name_loop->name, '/');
+ udev_list_entry_foreach_safe(file_loop, file_tmp, udev_list_get_entry(&file_list)) {
+ const char *file_name = udev_list_entry_get_name(file_loop);
+ const char *file_base = strrchr(file_name, '/');
- if (name_base == NULL)
+ if (file_base == NULL)
continue;
-
- if (strcmp(name_base, sort_base) == 0) {
- info(udev, "rule file '%s' already added, ignoring '%s'\n",
- name_loop->name, sort_loop->name);
- list_del(&sort_loop->node);
- free(sort_loop);
+ if (strcmp(file_base, sort_base) == 0) {
+ info(udev, "rule file basename '%s' already added, ignoring '%s'\n",
+ file_name, sort_name);
+ udev_list_entry_remove(sort_loop);
sort_loop = NULL;
- continue;
+ break;
}
-
- if (strcmp(name_base, sort_base) > 0)
+ if (strcmp(file_base, sort_base) > 0)
break;
}
if (sort_loop != NULL)
- list_move_tail(&sort_loop->node, &name_loop->node);
+ udev_list_entry_move_before(sort_loop, file_loop);
}
}
/* parse list of files */
- list_for_each_entry_safe(name_loop, name_tmp, &name_list, node) {
- if (stat(name_loop->name, &statbuf) == 0) {
- if (statbuf.st_size)
- parse_file(rules, name_loop->name);
- else
- dbg(udev, "empty rules file '%s'\n", name_loop->name);
- } else
- err(udev, "could not read '%s': %m\n", name_loop->name);
- list_del(&name_loop->node);
- free(name_loop);
+ udev_list_entry_foreach_safe(file_loop, file_tmp, udev_list_get_entry(&file_list)) {
+ const char *file_name = udev_list_entry_get_name(file_loop);
+
+ if (stat(file_name, &statbuf) == 0 && statbuf.st_size > 0)
+ parse_file(rules, file_name);
+ else
+ info(udev, "can not read '%s'\n", file_name);
+ udev_list_entry_remove(file_loop);
}
-
return retval;
}