1
0
mirror of https://github.com/systemd/systemd.git synced 2024-12-22 17:35:35 +03:00

Merge pull request #11704 from yuwata/fix-oss-fuzz-12980

udev-rule: make rule_add_key() return negative errno when too much tokens
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2019-02-13 23:27:12 +01:00 committed by GitHub
commit dc9cced4ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 138 additions and 68 deletions

View File

@ -833,13 +833,15 @@ static const char *get_key_attribute(char *str) {
return NULL;
}
static void rule_add_key(struct rule_tmp *rule_tmp, enum token_type type,
enum operation_type op,
const char *value, const void *data) {
static int rule_add_key(struct rule_tmp *rule_tmp, enum token_type type,
enum operation_type op,
const char *value, const void *data) {
struct token *token = rule_tmp->token + rule_tmp->token_cur;
const char *attr = NULL;
assert(rule_tmp->token_cur < ELEMENTSOF(rule_tmp->token));
if (rule_tmp->token_cur >= ELEMENTSOF(rule_tmp->token))
return -E2BIG;
memzero(token, sizeof(struct token));
switch (type) {
@ -970,6 +972,8 @@ static void rule_add_key(struct rule_tmp *rule_tmp, enum token_type type,
token->key.type = type;
token->key.op = op;
rule_tmp->token_cur++;
return 0;
}
static int sort_token(UdevRules *rules, struct rule_tmp *rule_tmp) {
@ -1011,6 +1015,7 @@ static int sort_token(UdevRules *rules, struct rule_tmp *rule_tmp) {
#define LOG_RULE_WARNING(fmt, ...) LOG_RULE_FULL(LOG_WARNING, fmt, ##__VA_ARGS__)
#define LOG_RULE_DEBUG(fmt, ...) LOG_RULE_FULL(LOG_DEBUG, fmt, ##__VA_ARGS__)
#define LOG_AND_RETURN(fmt, ...) { LOG_RULE_ERROR(fmt, __VA_ARGS__); return; }
#define LOG_AND_RETURN_ADD_KEY LOG_AND_RETURN("Temporary rule array too small, aborting event processing with %u items", rule_tmp.token_cur);
static void add_rule(UdevRules *rules, char *line,
const char *filename, unsigned filename_off, unsigned lineno) {
@ -1020,6 +1025,7 @@ static void add_rule(UdevRules *rules, char *line,
.rules = rules,
.rule.type = TK_RULE,
};
int r;
/* the offset in the rule is limited to unsigned short */
if (filename_off < USHRT_MAX)
@ -1053,26 +1059,26 @@ static void add_rule(UdevRules *rules, char *line,
break;
}
if (rule_tmp.token_cur >= ELEMENTSOF(rule_tmp.token))
LOG_AND_RETURN("Temporary rule array too small, aborting event processing with %u items", rule_tmp.token_cur);
if (streq(key, "ACTION")) {
if (op > OP_MATCH_MAX)
LOG_AND_RETURN("Invalid %s operation", key);
rule_add_key(&rule_tmp, TK_M_ACTION, op, value, NULL);
if (rule_add_key(&rule_tmp, TK_M_ACTION, op, value, NULL) < 0)
LOG_AND_RETURN_ADD_KEY;
} else if (streq(key, "DEVPATH")) {
if (op > OP_MATCH_MAX)
LOG_AND_RETURN("Invalid %s operation", key);
rule_add_key(&rule_tmp, TK_M_DEVPATH, op, value, NULL);
if (rule_add_key(&rule_tmp, TK_M_DEVPATH, op, value, NULL) < 0)
LOG_AND_RETURN_ADD_KEY;
} else if (streq(key, "KERNEL")) {
if (op > OP_MATCH_MAX)
LOG_AND_RETURN("Invalid %s operation", key);
rule_add_key(&rule_tmp, TK_M_KERNEL, op, value, NULL);
if (rule_add_key(&rule_tmp, TK_M_KERNEL, op, value, NULL) < 0)
LOG_AND_RETURN_ADD_KEY;
} else if (streq(key, "SUBSYSTEM")) {
if (op > OP_MATCH_MAX)
@ -1083,15 +1089,18 @@ static void add_rule(UdevRules *rules, char *line,
if (!streq(value, "subsystem"))
LOG_RULE_WARNING("'%s' must be specified as 'subsystem'; please fix", value);
rule_add_key(&rule_tmp, TK_M_SUBSYSTEM, op, "subsystem|class|bus", NULL);
r = rule_add_key(&rule_tmp, TK_M_SUBSYSTEM, op, "subsystem|class|bus", NULL);
} else
rule_add_key(&rule_tmp, TK_M_SUBSYSTEM, op, value, NULL);
r = rule_add_key(&rule_tmp, TK_M_SUBSYSTEM, op, value, NULL);
if (r < 0)
LOG_AND_RETURN_ADD_KEY;
} else if (streq(key, "DRIVER")) {
if (op > OP_MATCH_MAX)
LOG_AND_RETURN("Invalid %s operation", key);
rule_add_key(&rule_tmp, TK_M_DRIVER, op, value, NULL);
if (rule_add_key(&rule_tmp, TK_M_DRIVER, op, value, NULL) < 0)
LOG_AND_RETURN_ADD_KEY;
} else if (startswith(key, "ATTR{")) {
attr = get_key_attribute(key + STRLEN("ATTR"));
@ -1102,9 +1111,11 @@ static void add_rule(UdevRules *rules, char *line,
LOG_AND_RETURN("Invalid %s operation", "ATTR");
if (op < OP_MATCH_MAX)
rule_add_key(&rule_tmp, TK_M_ATTR, op, value, attr);
r = rule_add_key(&rule_tmp, TK_M_ATTR, op, value, attr);
else
rule_add_key(&rule_tmp, TK_A_ATTR, op, value, attr);
r = rule_add_key(&rule_tmp, TK_A_ATTR, op, value, attr);
if (r < 0)
LOG_AND_RETURN_ADD_KEY;
} else if (startswith(key, "SYSCTL{")) {
attr = get_key_attribute(key + STRLEN("SYSCTL"));
@ -1115,9 +1126,11 @@ static void add_rule(UdevRules *rules, char *line,
LOG_AND_RETURN("Invalid %s operation", "ATTR");
if (op < OP_MATCH_MAX)
rule_add_key(&rule_tmp, TK_M_SYSCTL, op, value, attr);
r = rule_add_key(&rule_tmp, TK_M_SYSCTL, op, value, attr);
else
rule_add_key(&rule_tmp, TK_A_SYSCTL, op, value, attr);
r = rule_add_key(&rule_tmp, TK_A_SYSCTL, op, value, attr);
if (r < 0)
LOG_AND_RETURN_ADD_KEY;
} else if (startswith(key, "SECLABEL{")) {
attr = get_key_attribute(key + STRLEN("SECLABEL"));
@ -1127,25 +1140,29 @@ static void add_rule(UdevRules *rules, char *line,
if (op == OP_REMOVE)
LOG_AND_RETURN("Invalid %s operation", "SECLABEL");
rule_add_key(&rule_tmp, TK_A_SECLABEL, op, value, attr);
if (rule_add_key(&rule_tmp, TK_A_SECLABEL, op, value, attr) < 0)
LOG_AND_RETURN_ADD_KEY;
} else if (streq(key, "KERNELS")) {
if (op > OP_MATCH_MAX)
LOG_AND_RETURN("Invalid %s operation", key);
rule_add_key(&rule_tmp, TK_M_KERNELS, op, value, NULL);
if (rule_add_key(&rule_tmp, TK_M_KERNELS, op, value, NULL) < 0)
LOG_AND_RETURN_ADD_KEY;
} else if (streq(key, "SUBSYSTEMS")) {
if (op > OP_MATCH_MAX)
LOG_AND_RETURN("Invalid %s operation", key);
rule_add_key(&rule_tmp, TK_M_SUBSYSTEMS, op, value, NULL);
if (rule_add_key(&rule_tmp, TK_M_SUBSYSTEMS, op, value, NULL) < 0)
LOG_AND_RETURN_ADD_KEY;
} else if (streq(key, "DRIVERS")) {
if (op > OP_MATCH_MAX)
LOG_AND_RETURN("Invalid %s operation", key);
rule_add_key(&rule_tmp, TK_M_DRIVERS, op, value, NULL);
if (rule_add_key(&rule_tmp, TK_M_DRIVERS, op, value, NULL) < 0)
LOG_AND_RETURN_ADD_KEY;
} else if (startswith(key, "ATTRS{")) {
if (op > OP_MATCH_MAX)
@ -1159,13 +1176,15 @@ static void add_rule(UdevRules *rules, char *line,
LOG_RULE_WARNING("'device' link may not be available in future kernels; please fix");
if (strstr(attr, "../"))
LOG_RULE_WARNING("Direct reference to parent sysfs directory, may break in future kernels; please fix");
rule_add_key(&rule_tmp, TK_M_ATTRS, op, value, attr);
if (rule_add_key(&rule_tmp, TK_M_ATTRS, op, value, attr) < 0)
LOG_AND_RETURN_ADD_KEY;
} else if (streq(key, "TAGS")) {
if (op > OP_MATCH_MAX)
LOG_AND_RETURN("Invalid %s operation", key);
rule_add_key(&rule_tmp, TK_M_TAGS, op, value, NULL);
if (rule_add_key(&rule_tmp, TK_M_TAGS, op, value, NULL) < 0)
LOG_AND_RETURN_ADD_KEY;
} else if (startswith(key, "ENV{")) {
attr = get_key_attribute(key + STRLEN("ENV"));
@ -1176,7 +1195,7 @@ static void add_rule(UdevRules *rules, char *line,
LOG_AND_RETURN("Invalid %s operation", "ENV");
if (op < OP_MATCH_MAX)
rule_add_key(&rule_tmp, TK_M_ENV, op, value, attr);
r = rule_add_key(&rule_tmp, TK_M_ENV, op, value, attr);
else {
if (STR_IN_SET(attr,
"ACTION",
@ -1192,26 +1211,32 @@ static void add_rule(UdevRules *rules, char *line,
"TAGS"))
LOG_AND_RETURN("Invalid ENV attribute, '%s' cannot be set", attr);
rule_add_key(&rule_tmp, TK_A_ENV, op, value, attr);
r = rule_add_key(&rule_tmp, TK_A_ENV, op, value, attr);
}
if (r < 0)
LOG_AND_RETURN_ADD_KEY;
} else if (streq(key, "TAG")) {
if (op < OP_MATCH_MAX)
rule_add_key(&rule_tmp, TK_M_TAG, op, value, NULL);
r = rule_add_key(&rule_tmp, TK_M_TAG, op, value, NULL);
else
rule_add_key(&rule_tmp, TK_A_TAG, op, value, NULL);
r = rule_add_key(&rule_tmp, TK_A_TAG, op, value, NULL);
if (r < 0)
LOG_AND_RETURN_ADD_KEY;
} else if (streq(key, "PROGRAM")) {
if (op == OP_REMOVE)
LOG_AND_RETURN("Invalid %s operation", key);
rule_add_key(&rule_tmp, TK_M_PROGRAM, op, value, NULL);
if (rule_add_key(&rule_tmp, TK_M_PROGRAM, op, value, NULL) < 0)
LOG_AND_RETURN_ADD_KEY;
} else if (streq(key, "RESULT")) {
if (op > OP_MATCH_MAX)
LOG_AND_RETURN("Invalid %s operation", key);
rule_add_key(&rule_tmp, TK_M_RESULT, op, value, NULL);
if (rule_add_key(&rule_tmp, TK_M_RESULT, op, value, NULL) < 0)
LOG_AND_RETURN_ADD_KEY;
} else if (startswith(key, "IMPORT")) {
attr = get_key_attribute(key + STRLEN("IMPORT"));
@ -1229,28 +1254,34 @@ static void add_rule(UdevRules *rules, char *line,
if (cmd >= 0) {
LOG_RULE_DEBUG("IMPORT found builtin '%s', replacing", value);
rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd);
if (rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd) < 0)
LOG_AND_RETURN_ADD_KEY;
continue;
}
}
rule_add_key(&rule_tmp, TK_M_IMPORT_PROG, op, value, NULL);
r = rule_add_key(&rule_tmp, TK_M_IMPORT_PROG, op, value, NULL);
} else if (streq(attr, "builtin")) {
const enum udev_builtin_cmd cmd = udev_builtin_lookup(value);
if (cmd < 0)
LOG_RULE_WARNING("IMPORT{builtin} '%s' unknown", value);
else
rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd);
if (cmd < 0) {
LOG_RULE_WARNING("IMPORT{builtin} '%s' unknown, ignoring", value);
continue;
} else
r = rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd);
} else if (streq(attr, "file"))
rule_add_key(&rule_tmp, TK_M_IMPORT_FILE, op, value, NULL);
r = rule_add_key(&rule_tmp, TK_M_IMPORT_FILE, op, value, NULL);
else if (streq(attr, "db"))
rule_add_key(&rule_tmp, TK_M_IMPORT_DB, op, value, NULL);
r = rule_add_key(&rule_tmp, TK_M_IMPORT_DB, op, value, NULL);
else if (streq(attr, "cmdline"))
rule_add_key(&rule_tmp, TK_M_IMPORT_CMDLINE, op, value, NULL);
r = rule_add_key(&rule_tmp, TK_M_IMPORT_CMDLINE, op, value, NULL);
else if (streq(attr, "parent"))
rule_add_key(&rule_tmp, TK_M_IMPORT_PARENT, op, value, NULL);
else
r = rule_add_key(&rule_tmp, TK_M_IMPORT_PARENT, op, value, NULL);
else {
LOG_RULE_ERROR("Ignoring unknown %s{} type '%s'", "IMPORT", attr);
continue;
}
if (r < 0)
LOG_AND_RETURN_ADD_KEY;
} else if (startswith(key, "TEST")) {
mode_t mode = 0;
@ -1261,9 +1292,11 @@ static void add_rule(UdevRules *rules, char *line,
attr = get_key_attribute(key + STRLEN("TEST"));
if (attr) {
mode = strtol(attr, NULL, 8);
rule_add_key(&rule_tmp, TK_M_TEST, op, value, &mode);
r = rule_add_key(&rule_tmp, TK_M_TEST, op, value, &mode);
} else
rule_add_key(&rule_tmp, TK_M_TEST, op, value, NULL);
r = rule_add_key(&rule_tmp, TK_M_TEST, op, value, NULL);
if (r < 0)
LOG_AND_RETURN_ADD_KEY;
} else if (startswith(key, "RUN")) {
attr = get_key_attribute(key + STRLEN("RUN"));
@ -1275,16 +1308,21 @@ static void add_rule(UdevRules *rules, char *line,
if (streq(attr, "builtin")) {
const enum udev_builtin_cmd cmd = udev_builtin_lookup(value);
if (cmd < 0)
LOG_RULE_ERROR("RUN{builtin}: '%s' unknown", value);
else
rule_add_key(&rule_tmp, TK_A_RUN_BUILTIN, op, value, &cmd);
if (cmd < 0) {
LOG_RULE_ERROR("RUN{builtin}: '%s' unknown, ignoring", value);
continue;
} else
r = rule_add_key(&rule_tmp, TK_A_RUN_BUILTIN, op, value, &cmd);
} else if (streq(attr, "program")) {
const enum udev_builtin_cmd cmd = _UDEV_BUILTIN_MAX;
rule_add_key(&rule_tmp, TK_A_RUN_PROGRAM, op, value, &cmd);
} else
r = rule_add_key(&rule_tmp, TK_A_RUN_PROGRAM, op, value, &cmd);
} else {
LOG_RULE_ERROR("Ignoring unknown %s{} type '%s'", "RUN", attr);
continue;
}
if (r < 0)
LOG_AND_RETURN_ADD_KEY;
} else if (streq(key, "LABEL")) {
if (op == OP_REMOVE)
@ -1296,14 +1334,15 @@ static void add_rule(UdevRules *rules, char *line,
if (op == OP_REMOVE)
LOG_AND_RETURN("Invalid %s operation", key);
rule_add_key(&rule_tmp, TK_A_GOTO, 0, value, NULL);
if (rule_add_key(&rule_tmp, TK_A_GOTO, 0, value, NULL) < 0)
LOG_AND_RETURN_ADD_KEY;
} else if (startswith(key, "NAME")) {
if (op == OP_REMOVE)
LOG_AND_RETURN("Invalid %s operation", key);
if (op < OP_MATCH_MAX)
rule_add_key(&rule_tmp, TK_M_NAME, op, value, NULL);
r = rule_add_key(&rule_tmp, TK_M_NAME, op, value, NULL);
else {
if (streq(value, "%k")) {
LOG_RULE_WARNING("NAME=\"%%k\" is ignored, because it breaks kernel supplied names; please remove");
@ -1313,8 +1352,10 @@ static void add_rule(UdevRules *rules, char *line,
LOG_RULE_DEBUG("NAME=\"\" is ignored, because udev will not delete any device nodes; please remove");
continue;
}
rule_add_key(&rule_tmp, TK_A_NAME, op, value, NULL);
r = rule_add_key(&rule_tmp, TK_A_NAME, op, value, NULL);
}
if (r < 0)
LOG_AND_RETURN_ADD_KEY;
rule_tmp.rule.rule.can_set_name = true;
} else if (streq(key, "SYMLINK")) {
@ -1322,9 +1363,11 @@ static void add_rule(UdevRules *rules, char *line,
LOG_AND_RETURN("Invalid %s operation", key);
if (op < OP_MATCH_MAX)
rule_add_key(&rule_tmp, TK_M_DEVLINK, op, value, NULL);
r = rule_add_key(&rule_tmp, TK_M_DEVLINK, op, value, NULL);
else
rule_add_key(&rule_tmp, TK_A_DEVLINK, op, value, NULL);
r = rule_add_key(&rule_tmp, TK_A_DEVLINK, op, value, NULL);
if (r < 0)
LOG_AND_RETURN_ADD_KEY;
rule_tmp.rule.rule.can_set_name = true;
} else if (streq(key, "OWNER")) {
@ -1336,12 +1379,18 @@ static void add_rule(UdevRules *rules, char *line,
uid = strtoul(value, &endptr, 10);
if (endptr[0] == '\0')
rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid);
r = rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid);
else if (rules->resolve_name_timing == RESOLVE_NAME_EARLY && !strchr("$%", value[0])) {
uid = add_uid(rules, value);
rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid);
r = rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid);
} else if (rules->resolve_name_timing != RESOLVE_NAME_NEVER)
rule_add_key(&rule_tmp, TK_A_OWNER, op, value, NULL);
r = rule_add_key(&rule_tmp, TK_A_OWNER, op, value, NULL);
else {
LOG_RULE_ERROR("Invalid %s operation", key);
continue;
}
if (r < 0)
LOG_AND_RETURN_ADD_KEY;
rule_tmp.rule.rule.can_set_name = true;
@ -1354,12 +1403,18 @@ static void add_rule(UdevRules *rules, char *line,
gid = strtoul(value, &endptr, 10);
if (endptr[0] == '\0')
rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid);
r = rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid);
else if ((rules->resolve_name_timing == RESOLVE_NAME_EARLY) && !strchr("$%", value[0])) {
gid = add_gid(rules, value);
rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid);
r = rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid);
} else if (rules->resolve_name_timing != RESOLVE_NAME_NEVER)
rule_add_key(&rule_tmp, TK_A_GROUP, op, value, NULL);
r = rule_add_key(&rule_tmp, TK_A_GROUP, op, value, NULL);
else {
LOG_RULE_ERROR("Invalid %s operation", key);
continue;
}
if (r < 0)
LOG_AND_RETURN_ADD_KEY;
rule_tmp.rule.rule.can_set_name = true;
@ -1372,9 +1427,12 @@ static void add_rule(UdevRules *rules, char *line,
mode = strtol(value, &endptr, 8);
if (endptr[0] == '\0')
rule_add_key(&rule_tmp, TK_A_MODE_ID, op, NULL, &mode);
r = rule_add_key(&rule_tmp, TK_A_MODE_ID, op, NULL, &mode);
else
rule_add_key(&rule_tmp, TK_A_MODE, op, value, NULL);
r = rule_add_key(&rule_tmp, TK_A_MODE, op, value, NULL);
if (r < 0)
LOG_AND_RETURN_ADD_KEY;
rule_tmp.rule.rule.can_set_name = true;
} else if (streq(key, "OPTIONS")) {
@ -1387,37 +1445,48 @@ static void add_rule(UdevRules *rules, char *line,
if (pos) {
int prio = atoi(pos + STRLEN("link_priority="));
rule_add_key(&rule_tmp, TK_A_DEVLINK_PRIO, op, NULL, &prio);
if (rule_add_key(&rule_tmp, TK_A_DEVLINK_PRIO, op, NULL, &prio) < 0)
LOG_AND_RETURN_ADD_KEY;
}
pos = strstr(value, "string_escape=");
if (pos) {
pos += STRLEN("string_escape=");
if (startswith(pos, "none"))
rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_NONE, op, NULL, NULL);
r = rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_NONE, op, NULL, NULL);
else if (startswith(pos, "replace"))
rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_REPLACE, op, NULL, NULL);
r = rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_REPLACE, op, NULL, NULL);
else {
LOG_RULE_ERROR("OPTIONS: unknown string_escape mode '%s', ignoring", pos);
r = 0;
}
if (r < 0)
LOG_AND_RETURN_ADD_KEY;
}
pos = strstr(value, "db_persist");
if (pos)
rule_add_key(&rule_tmp, TK_A_DB_PERSIST, op, NULL, NULL);
if (rule_add_key(&rule_tmp, TK_A_DB_PERSIST, op, NULL, NULL) < 0)
LOG_AND_RETURN_ADD_KEY;
pos = strstr(value, "nowatch");
if (pos) {
static const int zero = 0;
rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &zero);
if (rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &zero) < 0)
LOG_AND_RETURN_ADD_KEY;
} else {
static const int one = 1;
pos = strstr(value, "watch");
if (pos)
rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &one);
if (rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &one) < 0)
LOG_AND_RETURN_ADD_KEY;
}
pos = strstr(value, "static_node=");
if (pos) {
pos += STRLEN("static_node=");
rule_add_key(&rule_tmp, TK_A_STATIC_NODE, op, pos, NULL);
if (rule_add_key(&rule_tmp, TK_A_STATIC_NODE, op, pos, NULL) < 0)
LOG_AND_RETURN_ADD_KEY;
rule_tmp.rule.rule.has_static_node = true;
}

View File

@ -0,0 +1 @@
SUBSYSTEM==" " KERNEL==" " GROUP=" " KERNEL==" " GROUP=" " MODE =" " KERNEL==" " GROUP=" " KERNEL==" " GROUP=" " MODE =" " KERNEL==" " GROUP=" " MODE=" " GROUP=" " KERNEL==" " GROUP=" " KERNEL==" "GROUP=" " KERNEL==" " GROUP=" " MODE =" "KERNEL==" " GROUP=" " KERNEL==" " GROUP=" "MODE =" " KERNEL==" " KERNEL==" " GROUP=" " MODE =" " KERNEL==" " GROUP=" " KERNEL==" " GROUP=" " MODE =" " KERNEL==" " GROUP=" " MODE=" " GROUP=" " KERNEL==" " GROUP=" " KERNEL==" " GROUP=" " KERNEL==" " GROUP=" " MODE =" " KERNEL==" " GROUP=" " MODE="" MODE =" " KERNEL==" " GROUP=" " KERNEL==" " GROUP=" " MODE =" " KERNEL=="" GROUP=" " KERNEL==" " GROUP=" " MODE =" " KERNEL==" " KERNEL==" " OPTIONS =" string_escape=replace watch "