chiark / gitweb /
Remove src/tty-ask-password-agent
[elogind.git] / src / udev / udev-rules.c
index 2a691f6ab7e6021f0dce588b686d306241cf770b..4262119886b4ab5f007236abd4d10b0602784b14 100644 (file)
@@ -35,6 +35,7 @@
 #include "strbuf.h"
 #include "strv.h"
 #include "util.h"
+#include "sysctl-util.h"
 
 #define PREALLOC_TOKEN          2048
 
@@ -128,6 +129,7 @@ enum token_type {
         TK_M_DRIVER,                    /* val */
         TK_M_WAITFOR,                   /* val */
         TK_M_ATTR,                      /* val, attr */
+        TK_M_SYSCTL,                    /* val, attr */
 
         TK_M_PARENTS_MIN,
         TK_M_KERNELS,                   /* val */
@@ -166,6 +168,7 @@ enum token_type {
         TK_A_NAME,                      /* val */
         TK_A_DEVLINK,                   /* val */
         TK_A_ATTR,                      /* val, attr */
+        TK_A_SYSCTL,                    /* val, attr */
         TK_A_RUN_BUILTIN,               /* val, bool */
         TK_A_RUN_PROGRAM,               /* val, bool */
         TK_A_GOTO,                      /* size_t */
@@ -262,6 +265,7 @@ static const char *token_str(enum token_type type) {
                 [TK_M_DRIVER] =                 "M DRIVER",
                 [TK_M_WAITFOR] =                "M WAITFOR",
                 [TK_M_ATTR] =                   "M ATTR",
+                [TK_M_SYSCTL] =                 "M SYSCTL",
 
                 [TK_M_PARENTS_MIN] =            "M PARENTS_MIN",
                 [TK_M_KERNELS] =                "M KERNELS",
@@ -300,6 +304,7 @@ static const char *token_str(enum token_type type) {
                 [TK_A_NAME] =                   "A NAME",
                 [TK_A_DEVLINK] =                "A DEVLINK",
                 [TK_A_ATTR] =                   "A ATTR",
+                [TK_A_SYSCTL] =                 "A SYSCTL",
                 [TK_A_RUN_BUILTIN] =            "A RUN_BUILTIN",
                 [TK_A_RUN_PROGRAM] =            "A RUN_PROGRAM",
                 [TK_A_GOTO] =                   "A GOTO",
@@ -363,9 +368,11 @@ static void dump_token(struct udev_rules *rules, struct token *token) {
                 log_debug("%s %i '%s'", token_str(type), token->key.builtin_cmd, value);
                 break;
         case TK_M_ATTR:
+        case TK_M_SYSCTL:
         case TK_M_ATTRS:
         case TK_M_ENV:
         case TK_A_ATTR:
+        case TK_A_SYSCTL:
         case TK_A_ENV:
                 log_debug("%s %s '%s' '%s'(%s)",
                           token_str(type), operation_str(op), attr, value, string_glob_str(glob));
@@ -474,9 +481,9 @@ static uid_t add_uid(struct udev_rules *rules, const char *owner) {
         r = get_user_creds(&owner, &uid, NULL, NULL, NULL);
         if (r < 0) {
                 if (r == -ENOENT || r == -ESRCH)
-                        udev_err(rules->udev, "specified user '%s' unknown\n", owner);
+                        log_error("specified user '%s' unknown", owner);
                 else
-                        udev_err(rules->udev, "error resolving user '%s': %s\n", owner, strerror(-r));
+                        log_error_errno(r, "error resolving user '%s': %m", owner);
         }
 
         /* grow buffer if needed */
@@ -521,9 +528,9 @@ static gid_t add_gid(struct udev_rules *rules, const char *group) {
         r = get_group_creds(&group, &gid);
         if (r < 0) {
                 if (r == -ENOENT || r == -ESRCH)
-                        udev_err(rules->udev, "specified group '%s' unknown\n", group);
+                        log_error("specified group '%s' unknown", group);
                 else
-                        udev_err(rules->udev, "error resolving group '%s': %s\n", group, strerror(-r));
+                        log_error_errno(r, "error resolving group '%s': %m", group);
         }
 
         /* grow buffer if needed */
@@ -555,7 +562,6 @@ static int import_property_from_string(struct udev_device *dev, char *line) {
         char *key;
         char *val;
         size_t len;
-        struct udev_list_entry *entry;
 
         /* find key */
         key = line;
@@ -606,10 +612,7 @@ static int import_property_from_string(struct udev_device *dev, char *line) {
                 val++;
         }
 
-        entry = udev_device_add_property(dev, key, val);
-        /* store in db, skip private keys */
-        if (key[0] != '.')
-                udev_list_entry_set_num(entry, true);
+        udev_device_add_property(dev, key, val);
 
         return 0;
 }
@@ -670,12 +673,7 @@ static int import_parent_into_properties(struct udev_device *dev, const char *fi
                 const char *val = udev_list_entry_get_value(list_entry);
 
                 if (fnmatch(filter, key, 0) == 0) {
-                        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);
+                        udev_device_add_property(dev, key, val);
                 }
         }
         return 0;
@@ -906,8 +904,10 @@ static int rule_add_key(struct rule_tmp *rule_tmp, enum token_type type,
                 break;
         case TK_M_ENV:
         case TK_M_ATTR:
+        case TK_M_SYSCTL:
         case TK_M_ATTRS:
         case TK_A_ATTR:
+        case TK_A_SYSCTL:
         case TK_A_ENV:
         case TK_A_SECLABEL:
                 attr = data;
@@ -1042,11 +1042,11 @@ static int add_rule(struct udev_rules *rules, char *line,
                     const char *filename, unsigned int filename_off, unsigned int lineno) {
         char *linepos;
         const char *attr;
-        struct rule_tmp rule_tmp;
+        struct rule_tmp rule_tmp = {
+                .rules = rules,
+                .rule.type = TK_RULE,
+        };
 
-        memzero(&rule_tmp, sizeof(struct rule_tmp));
-        rule_tmp.rules = rules;
-        rule_tmp.rule.type = TK_RULE;
         /* the offset in the rule is limited to unsigned short */
         if (filename_off < USHRT_MAX)
                 rule_tmp.rule.rule.filename_off = filename_off;
@@ -1067,14 +1067,14 @@ static int add_rule(struct udev_rules *rules, char *line,
 
                         /* 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]};
+                        if (!strchr(NEWLINE, *linepos)) {
+                                char buf[2] = {*linepos};
                                 _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",
+                                log_error("invalid key/value pair in file %s on line %u, starting at character %tu ('%s')",
                                           filename, lineno, linepos - line + 1, tmp);
-                                if (linepos[1] == '#')
+                                if (*linepos == '#')
                                         log_error("hint: comments can only start at beginning of line");
                         }
                         break;
@@ -1144,11 +1144,27 @@ static int add_rule(struct udev_rules *rules, char *line,
                                 log_error("invalid ATTR operation");
                                 goto invalid;
                         }
-                        if (op < OP_MATCH_MAX) {
+                        if (op < OP_MATCH_MAX)
                                 rule_add_key(&rule_tmp, TK_M_ATTR, op, value, attr);
-                        } else {
+                        else
                                 rule_add_key(&rule_tmp, TK_A_ATTR, op, value, attr);
+                        continue;
+                }
+
+                if (startswith(key, "SYSCTL{")) {
+                        attr = get_key_attribute(rules->udev, key + strlen("SYSCTL"));
+                        if (attr == NULL) {
+                                log_error("error parsing SYSCTL attribute");
+                                goto invalid;
+                        }
+                        if (op == OP_REMOVE) {
+                                log_error("invalid SYSCTL operation");
+                                goto invalid;
                         }
+                        if (op < OP_MATCH_MAX)
+                                rule_add_key(&rule_tmp, TK_M_SYSCTL, op, value, attr);
+                        else
+                                rule_add_key(&rule_tmp, TK_A_SYSCTL, op, value, attr);
                         continue;
                 }
 
@@ -1575,7 +1591,7 @@ invalid:
 }
 
 static int parse_file(struct udev_rules *rules, const char *filename) {
-        FILE *f;
+        _cleanup_fclose_ FILE *f = NULL;
         unsigned int first_token;
         unsigned int filename_off;
         char line[UTIL_LINE_SIZE];
@@ -1633,7 +1649,6 @@ static int parse_file(struct udev_rules *rules, const char *filename) {
                 }
                 add_rule(rules, key, filename, filename_off, line_nr);
         }
-        fclose(f);
 
         /* link GOTOs to LABEL rules in this file to be able to fast-forward */
         for (i = first_token+1; i < rules->token_cur; i++) {
@@ -1686,7 +1701,7 @@ struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names) {
 
         r = conf_files_list_strv(&files, ".rules", NULL, rules_dirs);
         if (r < 0) {
-                log_error("failed to enumerate rules files: %s", strerror(-r));
+                log_error_errno(r, "failed to enumerate rules files: %m");
                 return udev_rules_unref(rules);
         }
 
@@ -1877,6 +1892,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
                               struct udev_event *event,
                               usec_t timeout_usec,
                               usec_t timeout_warn_usec,
+                              struct udev_list *properties_list,
                               const sigset_t *sigmask) {
         struct token *cur;
         struct token *rule;
@@ -1942,7 +1958,18 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
                         const char *value;
 
                         value = udev_device_get_property_value(event->dev, key_name);
-                        if (value == NULL)
+
+                        /* check global properties */
+                        if (!value && properties_list) {
+                                struct udev_list_entry *list_entry;
+
+                                list_entry = udev_list_get_entry(properties_list);
+                                list_entry = udev_list_entry_get_by_name(list_entry, key_name);
+                                if (list_entry != NULL)
+                                        value = udev_list_entry_get_value(list_entry);
+                        }
+
+                        if (!value)
                                 value = "";
                         if (match_key(rules, cur, value))
                                 goto nomatch;
@@ -1984,6 +2011,23 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
                         if (match_attr(rules, event->dev, event, cur) != 0)
                                 goto nomatch;
                         break;
+                case TK_M_SYSCTL: {
+                        char filename[UTIL_PATH_SIZE];
+                        _cleanup_free_ char *value = NULL;
+                        size_t len;
+
+                        udev_event_apply_format(event, rules_str(rules, cur->key.attr_off), filename, sizeof(filename));
+                        sysctl_normalize(filename);
+                        if (sysctl_read(filename, &value) < 0)
+                                goto nomatch;
+
+                        len = strlen(value);
+                        while (len > 0 && isspace(value[--len]))
+                                value[len] = '\0';
+                        if (match_key(rules, cur, value) != 0)
+                                goto nomatch;
+                        break;
+                }
                 case TK_M_KERNELS:
                 case TK_M_SUBSYSTEMS:
                 case TK_M_DRIVERS:
@@ -2167,12 +2211,9 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
                         const char *value;
 
                         value = udev_device_get_property_value(event->dev_db, key);
-                        if (value != NULL) {
-                                struct udev_list_entry *entry;
-
-                                entry = udev_device_add_property(event->dev, key, value);
-                                udev_list_entry_set_num(entry, true);
-                        } else {
+                        if (value != NULL)
+                                udev_device_add_property(event->dev, key, value);
+                        else {
                                 if (cur->key.op != OP_NOMATCH)
                                         goto nomatch;
                         }
@@ -2192,13 +2233,10 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
 
                                         pos = strstr(cmdline, key);
                                         if (pos != NULL) {
-                                                struct udev_list_entry *entry;
-
                                                 pos += strlen(key);
                                                 if (pos[0] == '\0' || isspace(pos[0])) {
                                                         /* we import simple flags as 'FLAG=1' */
-                                                        entry = udev_device_add_property(event->dev, key, "1");
-                                                        udev_list_entry_set_num(entry, true);
+                                                        udev_device_add_property(event->dev, key, "1");
                                                         imported = true;
                                                 } else if (pos[0] == '=') {
                                                         const char *value;
@@ -2208,8 +2246,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
                                                         while (pos[0] != '\0' && !isspace(pos[0]))
                                                                 pos++;
                                                         pos[0] = '\0';
-                                                        entry = udev_device_add_property(event->dev, key, value);
-                                                        udev_list_entry_set_num(entry, true);
+                                                        udev_device_add_property(event->dev, key, value);
                                                         imported = true;
                                                 }
                                         }
@@ -2266,9 +2303,9 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
                         r = get_user_creds(&ow, &event->uid, NULL, NULL, NULL);
                         if (r < 0) {
                                 if (r == -ENOENT || r == -ESRCH)
-                                        udev_err(event->udev, "specified user '%s' unknown\n", owner);
+                                        log_error("specified user '%s' unknown", owner);
                                 else
-                                        udev_err(event->udev, "error resolving user '%s': %s\n", owner, strerror(-r));
+                                        log_error_errno(r, "error resolving user '%s': %m", owner);
 
                                 event->uid = 0;
                         }
@@ -2292,9 +2329,9 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
                         r = get_group_creds(&gr, &event->gid);
                         if (r < 0) {
                                 if (r == -ENOENT || r == -ESRCH)
-                                        udev_err(event->udev, "specified group '%s' unknown\n", group);
+                                        log_error("specified group '%s' unknown", group);
                                 else
-                                        udev_err(event->udev, "error resolving group '%s': %s\n", group, strerror(-r));
+                                        log_error_errno(r, "error resolving group '%s': %m", group);
 
                                 event->gid = 0;
                         }
@@ -2382,7 +2419,6 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
                         char *value = rules_str(rules, cur->key.value_off);
                         char value_new[UTIL_NAME_SIZE];
                         const char *value_old = NULL;
-                        struct udev_list_entry *entry;
 
                         if (value[0] == '\0') {
                                 if (cur->key.op == OP_ADD)
@@ -2402,10 +2438,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
                         } else
                                 udev_event_apply_format(event, value, value_new, sizeof(value_new));
 
-                        entry = udev_device_add_property(event->dev, name, value_new);
-                        /* store in db, skip private keys */
-                        if (name[0] != '.')
-                                udev_list_entry_set_num(entry, true);
+                        udev_device_add_property(event->dev, name, value_new);
                         break;
                 }
                 case TK_A_TAG: {
@@ -2524,13 +2557,28 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
                         f = fopen(attr, "we");
                         if (f != NULL) {
                                 if (fprintf(f, "%s", value) <= 0)
-                                        log_error("error writing ATTR{%s}: %m", attr);
+                                        log_error_errno(errno, "error writing ATTR{%s}: %m", attr);
                                 fclose(f);
                         } else {
-                                log_error("error opening ATTR{%s} for writing: %m", attr);
+                                log_error_errno(errno, "error opening ATTR{%s} for writing: %m", attr);
                         }
                         break;
                 }
+                case TK_A_SYSCTL: {
+                        char filename[UTIL_PATH_SIZE];
+                        char value[UTIL_NAME_SIZE];
+                        int r;
+
+                        udev_event_apply_format(event, rules_str(rules, cur->key.attr_off), filename, sizeof(filename));
+                        sysctl_normalize(filename);
+                        udev_event_apply_format(event, rules_str(rules, cur->key.value_off), value, sizeof(value));
+                        log_debug("SYSCTL '%s' writing '%s' %s:%u", filename, value,
+                                  rules_str(rules, rule->rule.filename_off), rule->rule.filename_line);
+                        r = sysctl_write(filename, value);
+                        if (r < 0)
+                                log_error("error writing SYSCTL{%s}='%s': %s", filename, value, strerror(-r));
+                        break;
+                }
                 case TK_A_RUN_BUILTIN:
                 case TK_A_RUN_PROGRAM: {
                         struct udev_list_entry *entry;
@@ -2641,19 +2689,17 @@ int udev_rules_apply_static_dev_perms(struct udev_rules *rules) {
 
                                         strscpyl(tags_dir, sizeof(tags_dir), "/run/udev/static_node-tags/", *t, "/", NULL);
                                         r = mkdir_p(tags_dir, 0755);
-                                        if (r < 0) {
-                                                log_error("failed to create %s: %s", tags_dir, strerror(-r));
-                                                return r;
-                                        }
+                                        if (r < 0)
+                                                return log_error_errno(r, "failed to create %s: %m", tags_dir);
 
                                         unescaped_filename = xescape(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: %m", tag_symlink, device_node);
-                                                return -errno;
-                                        else
+                                        if (r < 0 && errno != EEXIST)
+                                                return log_error_errno(errno, "failed to create symlink %s -> %s: %m",
+                                                                       tag_symlink, device_node);
+                                        else
                                                 r = 0;
                                 }
                         }