#include "conf-files.h"
#include "strbuf.h"
#include "strv.h"
+#include "util.h"
#define PREALLOC_TOKEN 2048
struct udev_rules {
struct udev *udev;
char **dirs;
- usec_t *dirs_ts_usec;
+ usec_t dirs_ts_usec;
int resolve_names;
/* every key in the rules file becomes a token */
TK_A_MODE_ID, /* mode_t */
TK_A_TAG, /* val */
TK_A_STATIC_NODE, /* val */
+ TK_A_SECLABEL, /* val, attr */
TK_A_ENV, /* val, attr */
TK_A_NAME, /* val */
TK_A_DEVLINK, /* val */
[TK_A_OWNER_ID] = "A OWNER_ID",
[TK_A_GROUP_ID] = "A GROUP_ID",
[TK_A_STATIC_NODE] = "A STATIC_NODE",
+ [TK_A_SECLABEL] = "A SECLABEL",
[TK_A_MODE_ID] = "A MODE_ID",
[TK_A_ENV] = "A ENV",
[TK_A_TAG] = "A ENV",
case TK_A_STATIC_NODE:
log_debug("%s '%s'\n", token_str(type), value);
break;
+ case TK_A_SECLABEL:
+ log_debug("%s %s '%s' '%s'\n", token_str(type), operation_str(op), attr, value);
+ break;
case TK_M_EVENT_TIMEOUT:
log_debug("%s %u\n", token_str(type), token->key.event_timeout);
break;
rules->token_cur * sizeof(struct token),
rules->buf_count,
rules->buf_cur);
- for(i = 0; i < rules->token_cur; i++)
+ for (i = 0; i < rules->token_cur; i++)
dump_token(rules, &rules->tokens[i]);
}
#else
char *key;
char *val;
size_t len;
+ struct udev_list_entry *entry;
/* find key */
key = line;
val++;
}
- /* handle device, renamed by external tool, returning new path */
- if (streq(key, "DEVPATH")) {
- char syspath[UTIL_PATH_SIZE];
-
- log_debug("updating devpath from '%s' to '%s'\n",
- udev_device_get_devpath(dev), val);
- strscpyl(syspath, sizeof(syspath), "/sys", val, NULL);
- udev_device_set_syspath(dev, syspath);
- } else {
- struct udev_list_entry *entry;
-
- entry = udev_device_add_property(dev, key, val);
- /* store in db, skip private keys */
- if (key[0] != '.')
- udev_list_entry_set_num(entry, true);
- }
+ entry = udev_device_add_property(dev, key, val);
+ /* store in db, skip private keys */
+ if (key[0] != '.')
+ udev_list_entry_set_num(entry, true);
+
return 0;
}
case TK_M_ATTRS:
case TK_A_ATTR:
case TK_A_ENV:
+ case TK_A_SECLABEL:
attr = data;
token->key.value_off = rules_add_string(rule_tmp->rules, value);
token->key.attr_off = rules_add_string(rule_tmp->rules, attr);
char *value;
enum operation_type op;
- if (get_key(rules->udev, &linepos, &key, &op, &value) != 0)
+ if (get_key(rules->udev, &linepos, &key, &op, &value) != 0) {
+ /* Avoid erroring on trailing whitespace. This is probably rare
+ * so save the work for the error case instead of always trying
+ * to strip the trailing whitespace with strstrip(). */
+ while (isblank(*linepos))
+ linepos++;
+
+ /* If we aren't at the end of the line, this is a parsing error.
+ * Make a best effort to describe where the problem is. */
+ if (*linepos != '\n') {
+ char buf[2] = {linepos[1]};
+ _cleanup_free_ char *tmp;
+
+ tmp = cescape(buf);
+ log_error("invalid key/value pair in file %s on line %u,"
+ "starting at character %tu ('%s')\n",
+ filename, lineno, linepos - line + 1, tmp);
+ if (linepos[1] == '#')
+ log_error("hint: comments can only start at beginning of line");
+ }
break;
+ }
if (streq(key, "ACTION")) {
if (op > OP_MATCH_MAX) {
continue;
}
+ if (startswith(key, "SECLABEL{")) {
+ attr = get_key_attribute(rules->udev, key + sizeof("SECLABEL")-1);
+ if (!attr) {
+ log_error("error parsing SECLABEL attribute\n");
+ goto invalid;
+ }
+
+ rule_add_key(&rule_tmp, TK_A_SECLABEL, op, value, attr);
+ continue;
+ }
+
if (streq(key, "KERNELS")) {
if (op > OP_MATCH_MAX) {
log_error("invalid KERNELS operation\n");
}
strv_uniq(rules->dirs);
- rules->dirs_ts_usec = calloc(strv_length(rules->dirs), sizeof(usec_t));
- if(!rules->dirs_ts_usec)
- return udev_rules_unref(rules);
udev_rules_check_timestamp(rules);
r = conf_files_list_strv(&files, ".rules", NULL, (const char **)rules->dirs);
free(rules->uids);
free(rules->gids);
strv_free(rules->dirs);
- free(rules->dirs_ts_usec);
free(rules);
return NULL;
}
bool udev_rules_check_timestamp(struct udev_rules *rules)
{
- unsigned int i;
- bool changed = false;
-
- if (rules == NULL)
- goto out;
-
- for (i = 0; rules->dirs[i]; i++) {
- struct stat stats;
+ if (!rules)
+ return false;
- if (stat(rules->dirs[i], &stats) < 0)
- continue;
-
- if (rules->dirs_ts_usec[i] == timespec_load(&stats.st_mtim))
- continue;
-
- /* first check */
- if (rules->dirs_ts_usec[i] != 0) {
- log_debug("reload - timestamp of '%s' changed\n", rules->dirs[i]);
- changed = true;
- }
-
- /* update timestamp */
- rules->dirs_ts_usec[i] = timespec_load(&stats.st_mtim);
- }
-out:
- return changed;
+ return paths_check_timestamp(rules->dirs, &rules->dirs_ts_usec, true);
}
static int match_key(struct udev_rules *rules, struct token *token, const char *val)
rules_str(rules, rule->rule.filename_off),
rule->rule.filename_line);
break;
+ case TK_A_SECLABEL: {
+ const char *name, *label;
+
+ name = rules_str(rules, cur->key.attr_off);
+ label = rules_str(rules, cur->key.value_off);
+ if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL)
+ udev_list_cleanup(&event->seclabel_list);
+ udev_list_entry_add(&event->seclabel_list, name, label);
+ log_debug("SECLABEL{%s}='%s' %s:%u\n",
+ name, label,
+ rules_str(rules, rule->rule.filename_off),
+ rule->rule.filename_line);
+ break;
+ }
case TK_A_ENV: {
const char *name = rules_str(rules, cur->key.attr_off);
char *value = rules_str(rules, cur->key.value_off);
strscpyl(tag_symlink, sizeof(tag_symlink), tags_dir, unescaped_filename, NULL);
r = symlink(device_node, tag_symlink);
if (r < 0 && errno != EEXIST) {
- log_error("failed to create symlink %s -> %s: %s\n", tag_symlink, device_node, strerror(errno));
+ log_error("failed to create symlink %s -> %s: %m\n", tag_symlink, device_node);
return -errno;
} else
r = 0;
}
}
+ /* don't touch the permissions if only the tags were set */
+ if (mode == 0 && uid == 0 && gid == 0)
+ goto next;
+
if (mode == 0) {
if (gid > 0)
mode = 0660;
mode = 0600;
}
if (mode != (stats.st_mode & 01777)) {
- chmod(device_node, mode);
- log_debug("chmod '%s' %#o\n", device_node, mode);
+ r = chmod(device_node, mode);
+ if (r < 0) {
+ log_error("failed to chmod '%s' %#o\n", device_node, mode);
+ return -errno;
+ } else
+ log_debug("chmod '%s' %#o\n", device_node, mode);
}
if ((uid != 0 && uid != stats.st_uid) || (gid != 0 && gid != stats.st_gid)) {
- chown(device_node, uid, gid);
- log_debug("chown '%s' %u %u\n", device_node, uid, gid);
+ r = chown(device_node, uid, gid);
+ if (r < 0) {
+ log_error("failed to chown '%s' %u %u \n", device_node, uid, gid);
+ return -errno;
+ } else
+ log_debug("chown '%s' %u %u\n", device_node, uid, gid);
}
utimensat(AT_FDCWD, device_node, NULL, 0);