Merge "init: Add support "&&" operator in property triggers"
This commit is contained in:
commit
46adfa69b6
5 changed files with 127 additions and 55 deletions
22
init/init.c
22
init/init.c
|
@ -540,17 +540,35 @@ static int is_last_command(struct action *act, struct command *cmd)
|
|||
return (list_tail(&act->commands) == &cmd->clist);
|
||||
}
|
||||
|
||||
|
||||
void build_triggers_string(char *name_str, int length, struct action *cur_action) {
|
||||
struct listnode *node;
|
||||
struct trigger *cur_trigger;
|
||||
|
||||
list_for_each(node, &cur_action->triggers) {
|
||||
cur_trigger = node_to_item(node, struct trigger, nlist);
|
||||
if (node != cur_action->triggers.next) {
|
||||
strlcat(name_str, " " , length);
|
||||
}
|
||||
strlcat(name_str, cur_trigger->name , length);
|
||||
}
|
||||
}
|
||||
|
||||
void execute_one_command(void)
|
||||
{
|
||||
int ret, i;
|
||||
char cmd_str[256] = "";
|
||||
char name_str[256] = "";
|
||||
|
||||
if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {
|
||||
cur_action = action_remove_queue_head();
|
||||
cur_command = NULL;
|
||||
if (!cur_action)
|
||||
return;
|
||||
INFO("processing action %p (%s)\n", cur_action, cur_action->name);
|
||||
|
||||
build_triggers_string(name_str, sizeof(name_str), cur_action);
|
||||
|
||||
INFO("processing action %p (%s)\n", cur_action, name_str);
|
||||
cur_command = get_first_command(cur_action);
|
||||
} else {
|
||||
cur_command = get_next_command(cur_action, cur_command);
|
||||
|
@ -568,7 +586,7 @@ void execute_one_command(void)
|
|||
}
|
||||
}
|
||||
INFO("command '%s' action=%s status=%d (%s:%d)\n",
|
||||
cmd_str, cur_action ? cur_action->name : "", ret, cur_command->filename,
|
||||
cmd_str, cur_action ? name_str : "", ret, cur_command->filename,
|
||||
cur_command->line);
|
||||
}
|
||||
}
|
||||
|
|
10
init/init.h
10
init/init.h
|
@ -37,6 +37,11 @@ struct command
|
|||
char *args[1];
|
||||
};
|
||||
|
||||
struct trigger {
|
||||
struct listnode nlist;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct action {
|
||||
/* node in list of all actions */
|
||||
struct listnode alist;
|
||||
|
@ -46,12 +51,15 @@ struct action {
|
|||
struct listnode tlist;
|
||||
|
||||
unsigned hash;
|
||||
const char *name;
|
||||
|
||||
/* list of actions which triggers the commands*/
|
||||
struct listnode triggers;
|
||||
struct listnode commands;
|
||||
struct command *current;
|
||||
};
|
||||
|
||||
void build_triggers_string(char *name_str, int length, struct action *cur_action);
|
||||
|
||||
struct socketinfo {
|
||||
struct socketinfo *next;
|
||||
const char *name;
|
||||
|
|
|
@ -499,77 +499,92 @@ void service_for_each_flags(unsigned matchflags,
|
|||
void action_for_each_trigger(const char *trigger,
|
||||
void (*func)(struct action *act))
|
||||
{
|
||||
struct listnode *node;
|
||||
struct listnode *node, *node2;
|
||||
struct action *act;
|
||||
struct trigger *cur_trigger;
|
||||
|
||||
list_for_each(node, &action_list) {
|
||||
act = node_to_item(node, struct action, alist);
|
||||
if (!strcmp(act->name, trigger)) {
|
||||
func(act);
|
||||
list_for_each(node2, &act->triggers) {
|
||||
cur_trigger = node_to_item(node2, struct trigger, nlist);
|
||||
if (!strcmp(cur_trigger->name, trigger)) {
|
||||
func(act);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void queue_property_triggers(const char *name, const char *value)
|
||||
{
|
||||
struct listnode *node;
|
||||
struct listnode *node, *node2;
|
||||
struct action *act;
|
||||
struct trigger *cur_trigger;
|
||||
bool match;
|
||||
int name_length;
|
||||
|
||||
list_for_each(node, &action_list) {
|
||||
act = node_to_item(node, struct action, alist);
|
||||
if (!strncmp(act->name, "property:", strlen("property:"))) {
|
||||
const char *test = act->name + strlen("property:");
|
||||
int name_length = strlen(name);
|
||||
match = !name;
|
||||
list_for_each(node2, &act->triggers) {
|
||||
cur_trigger = node_to_item(node2, struct trigger, nlist);
|
||||
if (!strncmp(cur_trigger->name, "property:", strlen("property:"))) {
|
||||
const char *test = cur_trigger->name + strlen("property:");
|
||||
if (!match) {
|
||||
name_length = strlen(name);
|
||||
if (!strncmp(name, test, name_length) &&
|
||||
test[name_length] == '=' &&
|
||||
(!strcmp(test + name_length + 1, value) ||
|
||||
!strcmp(test + name_length + 1, "*"))) {
|
||||
match = true;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
const char* equals = strchr(test, '=');
|
||||
if (equals) {
|
||||
char prop_name[PROP_NAME_MAX + 1];
|
||||
char value[PROP_VALUE_MAX];
|
||||
int length = equals - test;
|
||||
if (length <= PROP_NAME_MAX) {
|
||||
int ret;
|
||||
memcpy(prop_name, test, length);
|
||||
prop_name[length] = 0;
|
||||
|
||||
if (!strncmp(name, test, name_length) &&
|
||||
test[name_length] == '=' &&
|
||||
(!strcmp(test + name_length + 1, value) ||
|
||||
!strcmp(test + name_length + 1, "*"))) {
|
||||
action_add_queue_tail(act);
|
||||
}
|
||||
/* does the property exist, and match the trigger value? */
|
||||
ret = property_get(prop_name, value);
|
||||
if (ret > 0 && (!strcmp(equals + 1, value) ||
|
||||
!strcmp(equals + 1, "*"))) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
if (match) {
|
||||
action_add_queue_tail(act);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void queue_all_property_triggers()
|
||||
{
|
||||
struct listnode *node;
|
||||
struct action *act;
|
||||
list_for_each(node, &action_list) {
|
||||
act = node_to_item(node, struct action, alist);
|
||||
if (!strncmp(act->name, "property:", strlen("property:"))) {
|
||||
/* parse property name and value
|
||||
syntax is property:<name>=<value> */
|
||||
const char* name = act->name + strlen("property:");
|
||||
const char* equals = strchr(name, '=');
|
||||
if (equals) {
|
||||
char prop_name[PROP_NAME_MAX + 1];
|
||||
char value[PROP_VALUE_MAX];
|
||||
int length = equals - name;
|
||||
if (length > PROP_NAME_MAX) {
|
||||
ERROR("property name too long in trigger %s", act->name);
|
||||
} else {
|
||||
int ret;
|
||||
memcpy(prop_name, name, length);
|
||||
prop_name[length] = 0;
|
||||
|
||||
/* does the property exist, and match the trigger value? */
|
||||
ret = property_get(prop_name, value);
|
||||
if (ret > 0 && (!strcmp(equals + 1, value) ||
|
||||
!strcmp(equals + 1, "*"))) {
|
||||
action_add_queue_tail(act);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
queue_property_triggers(NULL, NULL);
|
||||
}
|
||||
|
||||
void queue_builtin_action(int (*func)(int nargs, char **args), char *name)
|
||||
{
|
||||
struct action *act;
|
||||
struct command *cmd;
|
||||
struct trigger *cur_trigger;
|
||||
|
||||
act = calloc(1, sizeof(*act));
|
||||
act->name = name;
|
||||
cur_trigger = calloc(1, sizeof(*cur_trigger));
|
||||
cur_trigger->name = name;
|
||||
list_init(&act->triggers);
|
||||
list_add_tail(&act->triggers, &cur_trigger->nlist);
|
||||
list_init(&act->commands);
|
||||
list_init(&act->qlist);
|
||||
|
||||
|
@ -611,6 +626,7 @@ int action_queue_empty()
|
|||
static void *parse_service(struct parse_state *state, int nargs, char **args)
|
||||
{
|
||||
struct service *svc;
|
||||
struct trigger *cur_trigger;
|
||||
if (nargs < 3) {
|
||||
parse_error(state, "services must have a name and a program\n");
|
||||
return 0;
|
||||
|
@ -635,9 +651,12 @@ static void *parse_service(struct parse_state *state, int nargs, char **args)
|
|||
svc->name = args[1];
|
||||
svc->classname = "default";
|
||||
memcpy(svc->args, args + 2, sizeof(char*) * nargs);
|
||||
cur_trigger = calloc(1, sizeof(*cur_trigger));
|
||||
svc->args[nargs] = 0;
|
||||
svc->nargs = nargs;
|
||||
svc->onrestart.name = "onrestart";
|
||||
list_init(&svc->onrestart.triggers);
|
||||
cur_trigger->name = "onrestart";
|
||||
list_add_tail(&svc->onrestart.triggers, &cur_trigger->nlist);
|
||||
list_init(&svc->onrestart.commands);
|
||||
list_add_tail(&service_list, &svc->slist);
|
||||
return svc;
|
||||
|
@ -821,16 +840,29 @@ static void parse_line_service(struct parse_state *state, int nargs, char **args
|
|||
static void *parse_action(struct parse_state *state, int nargs, char **args)
|
||||
{
|
||||
struct action *act;
|
||||
struct trigger *cur_trigger;
|
||||
int i;
|
||||
if (nargs < 2) {
|
||||
parse_error(state, "actions must have a trigger\n");
|
||||
return 0;
|
||||
}
|
||||
if (nargs > 2) {
|
||||
parse_error(state, "actions may not have extra parameters\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
act = calloc(1, sizeof(*act));
|
||||
act->name = args[1];
|
||||
list_init(&act->triggers);
|
||||
|
||||
for (i = 1; i < nargs; i++) {
|
||||
if (!(i % 2)) {
|
||||
if (strcmp(args[i], "&&")) {
|
||||
parse_error(state, "& is the only symbol allowed to concatenate actions\n");
|
||||
return 0;
|
||||
} else
|
||||
continue;
|
||||
}
|
||||
cur_trigger = calloc(1, sizeof(*cur_trigger));
|
||||
cur_trigger->name = args[i];
|
||||
list_add_tail(&act->triggers, &cur_trigger->nlist);
|
||||
}
|
||||
|
||||
list_init(&act->commands);
|
||||
list_init(&act->qlist);
|
||||
list_add_tail(&action_list, &act->alist);
|
||||
|
|
|
@ -15,9 +15,10 @@ void DUMP(void)
|
|||
struct command *cmd;
|
||||
struct listnode *node;
|
||||
struct listnode *node2;
|
||||
char name_str[256] = "";
|
||||
struct socketinfo *si;
|
||||
int n;
|
||||
|
||||
|
||||
list_for_each(node, &service_list) {
|
||||
svc = node_to_item(node, struct service, slist);
|
||||
RAW("service %s\n", svc->name);
|
||||
|
@ -34,7 +35,11 @@ void DUMP(void)
|
|||
|
||||
list_for_each(node, &action_list) {
|
||||
act = node_to_item(node, struct action, alist);
|
||||
RAW("on %s\n", act->name);
|
||||
RAW("on ");
|
||||
build_triggers_string(name_str, sizeof(name_str), act);
|
||||
RAW("%s", name_str);
|
||||
RAW("\n");
|
||||
|
||||
list_for_each(node2, &act->commands) {
|
||||
cmd = node_to_item(node2, struct command, clist);
|
||||
RAW(" %p", cmd->func);
|
||||
|
|
|
@ -123,6 +123,15 @@ boot
|
|||
Triggers of this form occur when the property <name> is set
|
||||
to the specific value <value>.
|
||||
|
||||
One can also test Mutliple properties to execute a group
|
||||
of commands. For example:
|
||||
|
||||
on property:test.a=1 && property:test.b=1
|
||||
setprop test.c 1
|
||||
|
||||
The above stub sets test.c to 1 only when
|
||||
both test.a=1 and test.b=1
|
||||
|
||||
Commands
|
||||
--------
|
||||
|
||||
|
|
Loading…
Reference in a new issue