+ case TK_M_RESULT:
+ if (match_key(rules, cur, event->program_result) != 0)
+ goto nomatch;
+ break;
+ case TK_A_STRING_ESCAPE_NONE:
+ esc = ESCAPE_NONE;
+ break;
+ case TK_A_STRING_ESCAPE_REPLACE:
+ esc = ESCAPE_REPLACE;
+ break;
+ case TK_A_INOTIFY_WATCH:
+ event->inotify_watch = cur->key.watch;
+ break;
+ case TK_A_DEVLINK_PRIO:
+ udev_device_set_devlink_priority(event->dev, cur->key.devlink_prio);
+ break;
+ case TK_A_OWNER:
+ {
+ char owner[UTIL_NAME_SIZE];
+
+ if (event->owner_final)
+ break;
+ if (cur->key.op == OP_ASSIGN_FINAL)
+ event->owner_final = 1;
+ udev_event_apply_format(event, &rules->buf[cur->key.value_off], owner, sizeof(owner));
+ event->uid = util_lookup_user(event->udev, owner);
+ info(event->udev, "OWNER %u %s:%u\n",
+ event->uid,
+ &rules->buf[rule->rule.filename_off],
+ rule->rule.filename_line);
+ break;
+ }
+ case TK_A_GROUP:
+ {
+ char group[UTIL_NAME_SIZE];
+
+ if (event->group_final)
+ break;
+ if (cur->key.op == OP_ASSIGN_FINAL)
+ event->group_final = 1;
+ udev_event_apply_format(event, &rules->buf[cur->key.value_off], group, sizeof(group));
+ event->gid = util_lookup_group(event->udev, group);
+ info(event->udev, "GROUP %u %s:%u\n",
+ event->gid,
+ &rules->buf[rule->rule.filename_off],
+ rule->rule.filename_line);
+ break;
+ }
+ case TK_A_MODE:
+ {
+ char mode[UTIL_NAME_SIZE];
+ char *endptr;
+
+ if (event->mode_final)
+ break;
+ if (cur->key.op == OP_ASSIGN_FINAL)
+ event->mode_final = 1;
+ udev_event_apply_format(event, &rules->buf[cur->key.value_off], mode, sizeof(mode));
+ event->mode = strtol(mode, &endptr, 8);
+ if (endptr[0] != '\0') {
+ err(event->udev, "invalide mode '%s' set default mode 0660\n", mode);
+ event->mode = 0660;
+ }
+ info(event->udev, "MODE %#o %s:%u\n",
+ event->mode,
+ &rules->buf[rule->rule.filename_off],
+ rule->rule.filename_line);
+ break;
+ }
+ case TK_A_OWNER_ID:
+ if (event->owner_final)
+ break;
+ if (cur->key.op == OP_ASSIGN_FINAL)
+ event->owner_final = 1;
+ event->uid = cur->key.uid;
+ info(event->udev, "OWNER %u %s:%u\n",
+ event->uid,
+ &rules->buf[rule->rule.filename_off],
+ rule->rule.filename_line);
+ break;
+ case TK_A_GROUP_ID:
+ if (event->group_final)
+ break;
+ if (cur->key.op == OP_ASSIGN_FINAL)
+ event->group_final = 1;
+ event->gid = cur->key.gid;
+ info(event->udev, "GROUP %u %s:%u\n",
+ event->gid,
+ &rules->buf[rule->rule.filename_off],
+ rule->rule.filename_line);
+ break;
+ case TK_A_MODE_ID:
+ if (event->mode_final)
+ break;
+ if (cur->key.op == OP_ASSIGN_FINAL)
+ event->mode_final = 1;
+ event->mode = cur->key.mode;
+ info(event->udev, "MODE %#o %s:%u\n",
+ event->mode,
+ &rules->buf[rule->rule.filename_off],
+ rule->rule.filename_line);
+ break;
+ case TK_A_ENV:
+ {
+ const char *name = &rules->buf[cur->key.attr_off];
+ char *value = &rules->buf[cur->key.value_off];
+
+ if (value[0] != '\0') {
+ char temp_value[UTIL_NAME_SIZE];
+ struct udev_list_entry *entry;
+
+ udev_event_apply_format(event, value, temp_value, sizeof(temp_value));
+ entry = udev_device_add_property(event->dev, name, temp_value);
+ /* store in db, skip private keys */
+ if (name[0] != '.')
+ udev_list_entry_set_flags(entry, 1);
+ } else {
+ udev_device_add_property(event->dev, name, NULL);
+ }
+ break;
+ }
+ case TK_A_NAME:
+ {
+ const char *name = &rules->buf[cur->key.value_off];
+ char name_str[UTIL_PATH_SIZE];
+ int count;
+
+ if (event->name_final)
+ break;
+ if (cur->key.op == OP_ASSIGN_FINAL)
+ event->name_final = 1;
+ udev_event_apply_format(event, name, name_str, sizeof(name_str));
+ if (esc == ESCAPE_UNSET || esc == ESCAPE_REPLACE) {
+ count = udev_util_replace_chars(name_str, "/");
+ if (count > 0)
+ info(event->udev, "%i character(s) replaced\n", count);
+ }
+ free(event->name);
+ event->name = strdup(name_str);
+ info(event->udev, "NAME '%s' %s:%u\n",
+ event->name,
+ &rules->buf[rule->rule.filename_off],
+ rule->rule.filename_line);
+ break;
+ }
+ case TK_A_DEVLINK:
+ {
+ char temp[UTIL_PATH_SIZE];
+ char filename[UTIL_PATH_SIZE];
+ char *pos, *next;
+ int count = 0;
+
+ if (event->devlink_final)
+ break;
+ if (major(udev_device_get_devnum(event->dev)) == 0)
+ break;
+ if (cur->key.op == OP_ASSIGN_FINAL)
+ event->devlink_final = 1;
+ if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL)
+ udev_device_cleanup_devlinks_list(event->dev);
+
+ /* allow multiple symlinks separated by spaces */
+ udev_event_apply_format(event, &rules->buf[cur->key.value_off], temp, sizeof(temp));
+ if (esc == ESCAPE_UNSET)
+ count = udev_util_replace_chars(temp, "/ ");
+ else if (esc == ESCAPE_REPLACE)
+ count = udev_util_replace_chars(temp, "/");
+ if (count > 0)
+ info(event->udev, "%i character(s) replaced\n" , count);
+ dbg(event->udev, "rule applied, added symlink(s) '%s'\n", temp);
+ pos = temp;
+ while (isspace(pos[0]))
+ pos++;
+ next = strchr(pos, ' ');
+ while (next != NULL) {
+ next[0] = '\0';
+ info(event->udev, "LINK '%s' %s:%u\n", pos,
+ &rules->buf[rule->rule.filename_off], rule->rule.filename_line);
+ util_strscpyl(filename, sizeof(filename), udev_get_dev_path(event->udev), "/", pos, NULL);
+ udev_device_add_devlink(event->dev, filename, cur->key.devlink_unique);
+ while (isspace(next[1]))
+ next++;
+ pos = &next[1];
+ next = strchr(pos, ' ');
+ }
+ if (pos[0] != '\0') {
+ info(event->udev, "LINK '%s' %s:%u\n", pos,
+ &rules->buf[rule->rule.filename_off], rule->rule.filename_line);
+ util_strscpyl(filename, sizeof(filename), udev_get_dev_path(event->udev), "/", pos, NULL);
+ udev_device_add_devlink(event->dev, filename, cur->key.devlink_unique);
+ }
+ }
+ break;
+ case TK_A_EVENT_TIMEOUT:
+ udev_device_set_event_timeout(event->dev, cur->key.event_timeout);
+ break;
+ case TK_A_IGNORE_REMOVE:
+ udev_device_set_ignore_remove(event->dev, 1);
+ break;
+ case TK_A_ATTR:
+ {
+ const char *key_name = &rules->buf[cur->key.attr_off];
+ char attr[UTIL_PATH_SIZE];
+ char value[UTIL_NAME_SIZE];
+ FILE *f;
+
+ if (util_resolve_subsys_kernel(event->udev, key_name, attr, sizeof(attr), 0) != 0)
+ util_strscpyl(attr, sizeof(attr), udev_device_get_syspath(event->dev), "/", key_name, NULL);
+ attr_subst_subdir(attr, sizeof(attr));
+
+ udev_event_apply_format(event, &rules->buf[cur->key.value_off], value, sizeof(value));
+ info(event->udev, "ATTR '%s' writing '%s' %s:%u\n", attr, value,
+ &rules->buf[rule->rule.filename_off],
+ rule->rule.filename_line);
+ f = fopen(attr, "w");
+ if (f != NULL) {
+ if (fprintf(f, "%s", value) <= 0)
+ err(event->udev, "error writing ATTR{%s}: %m\n", attr);
+ fclose(f);
+ } else {
+ err(event->udev, "error opening ATTR{%s} for writing: %m\n", attr);
+ }
+ break;
+ }
+ case TK_A_RUN:
+ {
+ struct udev_list_entry *list_entry;
+
+ if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL)
+ udev_list_cleanup_entries(event->udev, &event->run_list);
+ info(event->udev, "RUN '%s' %s:%u\n",
+ &rules->buf[cur->key.value_off],
+ &rules->buf[rule->rule.filename_off],
+ rule->rule.filename_line);
+ list_entry = udev_list_entry_add(event->udev, &event->run_list,
+ &rules->buf[cur->key.value_off], NULL, 1, 0);
+ if (cur->key.fail_on_error)
+ udev_list_entry_set_flags(list_entry, 1);
+ break;
+ }
+ case TK_A_GOTO:
+ if (cur->key.rule_goto == 0)
+ break;
+ cur = &rules->tokens[cur->key.rule_goto];
+ continue;
+ case TK_END:
+ return 0;
+
+ case TK_M_PARENTS_MIN:
+ case TK_M_PARENTS_MAX:
+ case TK_M_MAX:
+ case TK_UNSET:
+ err(rules->udev, "wrong type %u\n", cur->type);
+ goto nomatch;