#include #include #include #include #include #include "action.h" #include "block.h" #include "any_log.h" bool action_perform(action_t *action, block_t *block, config_t *config) { block_t *target = block; bool warned = false; bool notify = false; for (size_t i = 0; i < action->length; i++) { const char *key = action->parts[i].key; const char *value = action->parts[i].value; switch (action->parts[i].type) { case ACTION_TARGET: { if (strncmp(value, "block.", 6)) log_panic("Invalid target %s", value); if (!strcmp(value + 6, "default")) { target = block; } else { for (size_t i = 0; i < config->n_blocks; i++) { if (!strcmp(value + 6, config->blocks[i]->label)) { target = config->blocks[i]; break; } } } if (notify && target->scheme->validate_change) { assert(target->scheme->validate_fn != NULL); int errors = target->scheme->validate_fn(target, config); if (config->action_failfast && errors) goto error; } notify = false; warned = false; break; } case ACTION_SET_PAIR: { assert(target != NULL); if (!warned && target->scheme->change_fn == NULL) { log_warn("Block '%s' does not support 'set' actions", target->label); warned = true; continue; } // Skip "set-" in key bool success = block_change(target, config, key + 4, value); if (config->action_failfast && !success) goto error; notify = true; break; } case ACTION_RUN_SYNC: { // TODO: Pass information about the action/block to the script pid_t pid = fork(); if (pid == -1) { if (config->action_failfast) { log_trace("Failed to fork"); goto error; } } else if (pid == 0) { execl("/bin/sh", "sh", "-c", value, (char *)NULL); unreachable(); } else { int status; waitpid(pid, &status, 0); bool success = !WIFEXITED(status) || WEXITSTATUS(status) != 0; if (config->action_strict_run && !success) { if (config->action_failfast) goto error; } } break; } case ACTION_RUN_ASYNC: { log_panic("Async not implemented"); break; } default: unreachable(); } log_value_trace("Performed action step", "i:type", action->parts[i].type, "s:key", key, "s:value", value); } if (notify && target->scheme->validate_change) { assert(target->scheme->validate_fn != NULL); int errors = target->scheme->validate_fn(target, config); if (config->action_failfast && errors != 0) goto error; } log_debug("Performed action '%s'", action->label); return true; error: log_debug("Aborted action '%s'", action->label); return false; } int action_validate(action_t *action, config_t *config) { action->validated = true; return 0; } bool action_resolve(action_t *action, config_t *config) { action->resolved = true; return true; } void action_free(action_t *action) { for (size_t i = 0; i < action->length; i++) { free(action->parts[i].key); free(action->parts[i].value); } free(action->parts); free(action->label); }