diff options
Diffstat (limited to 'src/ctl.c')
-rw-r--r-- | src/ctl.c | 442 |
1 files changed, 398 insertions, 44 deletions
@@ -1000,8 +1000,20 @@ ctlio_do_command (struct ctlio *io, struct wordsplit *uri) //FIXME: Reply json } } + else if (method != METH_POST + && uri->ws_wordc > 1 + && strchr ("\"{[", uri->ws_wordv[uri->ws_wordc-1][0])) + { + json = json_parse_string (uri->ws_wordv[uri->ws_wordc-1], + strlen (uri->ws_wordv[uri->ws_wordc-1])); + if (!json) + ctlio_reply (io, 400, NULL); + free (uri->ws_wordv[uri->ws_wordc-1]); + uri->ws_wordv[uri->ws_wordc-1] = NULL; + uri->ws_wordc--; + } else - json = json_value_create (json_null); + json = json_new_bool (1); res->handler (io, method, uri->ws_wordc, uri->ws_wordv, json); json_value_free (json); @@ -1138,11 +1150,9 @@ ctl_accept (int socket, void *data) return 0; } -void -ctl_open () +char const * +pies_control_url (void) { - int fd; - if (!control.url) { char *str = xasprintf (DEFAULT_CONTROL_URL, instance); @@ -1150,11 +1160,19 @@ ctl_open () { logmsg (LOG_CRIT, _("%s: cannot create URL: %s"), str, strerror (errno)); - return; + exit (EX_OSERR); } free (str); } - + return control.url->string; +} + +void +ctl_open () +{ + int fd; + + pies_control_url (); fd = create_socket (control.url, SOCK_STREAM, NULL, 077); if (fd == -1) { @@ -1271,7 +1289,8 @@ res_instance (struct ctlio *io, enum http_method meth, struct eval_env { struct ctlio *io; - struct json_value *json; + struct pcond_node *cond; + struct json_value *result; }; static int @@ -1299,26 +1318,14 @@ auth_prog (struct prog *prog, struct ctlio *io) } return 0; } - -static struct json_value *prog_serialize (struct prog *prog); - -static int -selector (struct prog *prog, void *data) -{ - struct eval_env *env = data; - - if (auth_prog (prog, env->io)) - json_array_append (env->json, prog_serialize (prog)); - return 0; -} -static char const *pies_type_str[] = { +static char * const pies_type_str[] = { [TYPE_COMPONENT] = "component", [TYPE_REDIRECTOR] = "redirector", [TYPE_COMMAND] = "command" }; -static char const *pies_comp_mode_str[] = { +static char * const pies_comp_mode_str[] = { [pies_comp_exec] = "exec", [pies_comp_accept] = "accept", [pies_comp_inetd] = "inetd", @@ -1337,7 +1344,7 @@ static char const *pies_comp_mode_str[] = { [pies_comp_kbrequest] = "kbrequest" }; -static char const *status_str[] = { +static char * const pies_status_str[] = { [status_enabled] = "enabled", [status_disabled] = "disabled", [status_listener] = "listener", @@ -1348,7 +1355,7 @@ static char const *status_str[] = { static void format_idx (struct json_value *obj, const char *name, unsigned i, - char const **a, size_t s) + char * const *a, size_t s) { if (i < s && a[i]) json_object_set (obj, name, json_new_string (a[i])); @@ -1356,7 +1363,318 @@ format_idx (struct json_value *obj, const char *name, unsigned i, #define FORMAT_IDX(io,n,a,i) \ format_idx (io, n, i, a, sizeof(a)/sizeof(a[0])) + +enum pcond_type + { + pcond_true, + pcond_component, + pcond_type, + pcond_mode, + pcond_status, + pcond_not, + pcond_and, + pcond_or + }; + +struct pcond_node +{ + enum pcond_type type; + union + { + char *tag; + enum pies_comp_mode mode; + enum prog_status status; + enum prog_type type; + struct pcond_node *unary; + struct pcond_node *binary[2]; + } v; +}; + +static struct pcond_node * +pcond_node_alloc (enum pcond_type t) +{ + struct pcond_node *pn; + + pn = grecs_zalloc (sizeof (*pn)); + pn->type = t; + return pn; +} + +static void +pcond_node_free (struct pcond_node *node) +{ + if (!node) + return; + switch (node->type) + { + case pcond_not: + pcond_node_free (node->v.unary); + break; + + case pcond_and: + case pcond_or: + pcond_node_free (node->v.binary[0]); + pcond_node_free (node->v.binary[1]); + break; + + default: + break; + } + grecs_free (node); +} + +static int +pcond_eval (struct pcond_node *node, struct prog *p) +{ + if (!node) + return 0; + switch (node->type) + { + case pcond_true: + return 1; + + case pcond_component: + return strcmp (p->tag, node->v.tag) == 0; + + case pcond_type: + return p->type == node->v.type; + + case pcond_mode: + return IS_COMPONENT (p) && p->v.p.comp->mode == node->v.mode; + + case pcond_status: + return IS_COMPONENT (p) && p->v.p.status == node->v.status; + + case pcond_not: + return !pcond_eval (node->v.unary, p); + + case pcond_and: + if (!pcond_eval (node->v.binary[0], p)) + return 0; + return pcond_eval (node->v.binary[1], p); + + case pcond_or: + if (pcond_eval (node->v.binary[0], p)) + return 1; + return pcond_eval (node->v.binary[1], p); + + default: + abort (); + } +} + +static int json_to_pcond (struct ctlio *io, struct json_value *val, + struct pcond_node **pnode); + +static int +pcond_conv_component (struct pcond_node *node, struct json_value *arg, + struct ctlio *io) +{ + if (arg->type != json_string) + { + ctlio_reply (io, 400, "component must be string"); + return -1; + } + node->v.tag = arg->v.s; + return 0; +} + +static int +pcond_conv_n (struct pcond_node *node, struct json_value *arg, + struct ctlio *io, char * const *ar, const char *what) +{ + int n; + + if (arg->type != json_string) + { + ctlio_reply (io, 400, "%s must be string", what); + return -1; + } + n = array_index (ar, arg->v.s); + if (n == -1) + { + ctlio_reply (io, 400, "bad %s", what); + return -1; + } + return n; +} + +static int +pcond_conv_type (struct pcond_node *node, struct json_value *arg, + struct ctlio *io) +{ + int n = pcond_conv_n (node, arg, io, pies_type_str, "type"); + if (n == -1) + return -1; + node->v.type = n; + return 0; +} + +static int +pcond_conv_mode (struct pcond_node *node, struct json_value *arg, + struct ctlio *io) +{ + int n = pcond_conv_n (node, arg, io, pies_type_str, "mode"); + if (n == -1) + return -1; + node->v.mode = n; + return 0; +} + +static int +pcond_conv_status (struct pcond_node *node, struct json_value *arg, + struct ctlio *io) +{ + int n = pcond_conv_n (node, arg, io, pies_status_str, "status"); + if (n == -1) + return -1; + node->v.status = n; + return 0; +} +static int +pcond_conv_not (struct pcond_node *node, struct json_value *arg, + struct ctlio *io) +{ + return json_to_pcond (io, arg, &node->v.unary); +} + +static int +pcond_conv_binary (struct pcond_node *node, struct json_value *arg, + struct ctlio *io) +{ + struct json_value *v; + + if (arg->type != json_arr) + { + ctlio_reply (io, 400, "arguments to binary opcode must be array"); + return -1; + } + json_array_get (arg, 0, &v); + if (json_to_pcond (io, v, &node->v.binary[0])) + return -1; + json_array_get (arg, 1, &v); + if (json_to_pcond (io, v, &node->v.binary[1])) + return -1; + return 0; +} + +struct pcond_conv { + char *term; + int (*handler) (struct pcond_node *, struct json_value *arg, struct ctlio *); +}; + +static struct pcond_conv pcond_conv[] = { + [pcond_true] = { "true", NULL }, + [pcond_component] = { "component", pcond_conv_component }, + [pcond_type] = { "type", pcond_conv_type }, + [pcond_mode] = { "mode", pcond_conv_mode }, + [pcond_status] = { "status", pcond_conv_status }, + [pcond_not] = { "not", pcond_conv_not }, + [pcond_and] = { "and", pcond_conv_binary }, + [pcond_or] = { "or", pcond_conv_binary }, +}; + +static int max_type = sizeof (pcond_conv) / sizeof (pcond_conv[0]); + +static int +pcond_conv_find (char const *name) +{ + int i; + for (i = 0; i < max_type; i++) + { + if (strcmp (pcond_conv[i].term, name) == 0) + return i; + } + return -1; +} + +static int +object_to_cond (struct ctlio *io, struct json_value *obj, + struct pcond_node **pnode) +{ + struct json_value *op, *arg; + struct pcond_node *node; + int type; + + if (json_object_get (obj, "op", &op)) + { + ctlio_reply (io, 400, "bad conditional: missing op"); + return -1; + } + if (json_object_get (obj, "arg", &arg)) + { + ctlio_reply (io, 400, "bad conditional: missing arg"); + return -1; + } + + if (op->type != json_string) + { + ctlio_reply (io, 400, "bad conditional: bad op type"); + return -1; + } + + type = pcond_conv_find (op->v.s); + if (type == -1) + { + ctlio_reply (io, 400, "bad conditional: unknown opcode"); + return -1; + } + + node = pcond_node_alloc (type); + if (pcond_conv[type].handler && pcond_conv[type].handler (node, arg, io)) + { + pcond_node_free (node); + return -1; + } + *pnode = node; + return 0; +} + +static int +json_to_pcond (struct ctlio *io, struct json_value *val, + struct pcond_node **pnode) +{ + if (!val) + *pnode = pcond_node_alloc (pcond_true); + else + switch (val->type) + { + case json_null: + *pnode = NULL; + break; + + case json_bool: + if (val->v.b) + *pnode = pcond_node_alloc (pcond_true); + else + *pnode = NULL; + break; + + case json_number: + case json_string: + case json_arr: + ctlio_reply (io, 400, "bad conditional"); + return -1; + + case json_object: + return object_to_cond (io, val, pnode); + } + return 0; +} + +static struct json_value *prog_serialize (struct prog *prog); + +static int +selector (struct prog *prog, void *data) +{ + struct eval_env *env = data; + + if (auth_prog (prog, env->io) && pcond_eval (env->cond, prog)) + json_array_append (env->result, prog_serialize (prog)); + return 0; +} + static struct json_value * prog_serialize (struct prog *prog) { @@ -1370,7 +1688,7 @@ prog_serialize (struct prog *prog) { case TYPE_COMPONENT: FORMAT_IDX (ret, "mode", pies_comp_mode_str, prog->v.p.comp->mode); - FORMAT_IDX (ret, "status", status_str, prog->v.p.status); + FORMAT_IDX (ret, "status", pies_status_str, prog->v.p.status); if (prog->pid) json_object_set_number (ret, "PID", prog->pid); @@ -1402,6 +1720,33 @@ prog_serialize (struct prog *prog) } static void +component_stop (struct ctlio *io, struct prog *prog) +{ + if (prog->v.p.comp->flags & CF_DISABLED) + ctlio_reply (io, 409, "already stopped"); + else + { + progman_stop_component (prog); + prog->v.p.comp->flags |= CF_DISABLED; + ctlio_reply (io, 200, "Component stopped"); + } +} + +static void +component_start (struct ctlio *io, struct prog *prog) +{ + if (!(prog->v.p.comp->flags & CF_DISABLED)) + ctlio_reply (io, 409, "already running"); + else + { + prog->v.p.comp->flags &= ~CF_DISABLED; + prog->v.p.status = status_enabled; + kill (getpid (), SIGALRM); + ctlio_reply (io, 200, "Component started"); + } +} + +static void res_programs (struct ctlio *io, enum http_method meth, size_t pathc, char **pathv, struct json_value *json) @@ -1411,9 +1756,12 @@ res_programs (struct ctlio *io, enum http_method meth, struct eval_env env; env.io = io; - env.json = json_new_array (); + if (json_to_pcond (io, json, &env.cond)) + return; + env.result = json_new_array (); progman_foreach (selector, &env); - io->output.reply = env.json; + pcond_node_free (env.cond); + io->output.reply = env.result; io->code = 200; } else if (meth == METH_DELETE || meth == METH_PUT) @@ -1431,32 +1779,38 @@ res_programs (struct ctlio *io, enum http_method meth, ctlio_reply (io, 404, "Not a component"); else if (meth == METH_DELETE) { - if (prog->v.p.comp->flags & CF_DISABLED) - ctlio_reply (io, 409, "already stopped"); - else - { - progman_stop_component (prog); - prog->v.p.comp->flags |= CF_DISABLED; - ctlio_reply (io, 200, "Component stopped"); - } + component_stop (io, prog); } else { - if (!(prog->v.p.comp->flags & CF_DISABLED)) - ctlio_reply (io, 409, "already running"); - else - { - prog->v.p.comp->flags &= ~CF_DISABLED; - prog->v.p.status = status_enabled; - kill (getpid (), SIGALRM); - ctlio_reply (io, 200, "Component started"); - } + component_start (io, prog); } } else ctlio_reply (io, 403, NULL); } } + else if (meth == METH_POST) + { + if (json->type != json_arr) + { + ctlio_reply (io, 400, NULL); + } + else + { + size_t i, n; + + n = json_array_size (json); + for (i = 0; i < n; i++) + { + struct json_value *v; + if (json_array_get (json, i, &v) == 0 && v->type == json_string) + progman_stop_tag (v->v.s); + } + kill (getpid (), SIGALRM); + } + ctlio_reply (io, 200, "Done"); + } else ctlio_reply (io, 405, NULL); } |