aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2016-02-20 22:33:00 +0200
committerSergey Poznyakoff <gray@gnu.org.ua>2016-02-20 22:42:46 +0200
commit46130027d8e6e91951226456174e6ad471ddc50f (patch)
treef88ce6edd9b078b5f331497bc452525f3c4c9c94 /src
parente6902abfddb4d7b16dc9a4231a3781f354a08cd5 (diff)
downloadpies-46130027d8e6e91951226456174e6ad471ddc50f.tar.gz
pies-46130027d8e6e91951226456174e6ad471ddc50f.tar.bz2
Improve control interface
This commit implements the following operations on the new /conf endpoints: GET /conf/runtime - List configuration PUT /conf/runtime - Reload configuration POST /conf/runtime - Post new configuration GET /conf/files - List configuration files DELETE /conf/files - Delete some or all configuration files from the list POST /conf/files - Install new configuration file Piesctl supports the following new commands: piesctl config reload piesctl config file clear piesctl config file add SYNTAX NAME piesctl config file del[ete] NAME [NAME...] piesctl config file list * src/ctl.c (res_conf): New function. (restab): New endpoint /conf * src/pies.c (config_file_add): Set free_entry. (config_file_remove, config_file_remove_all) (config_file_list_serialize): New functions. (pies_config_parse): Bail out if tree processing fails. (pies_reload): Remove unused function. (main): Redo ACTION_RELOAD handling. Handle ACTION_COMMIT. * src/pies.h (ACTION_COMMIT): New action. (config_file_remove, config_file_remove_all) (config_file_list_serialize) (pies_read_config, progman_gc): New protos. * src/piesctl.c (cmdline_parser_state): New struct (next_token, peek_token): Take ptr to cmdline_parser_state as argument. (parse_error, require_token, assert_eol) (piesctl_format): New functions. (parse_condition_to_uri): Rewrite using piesctl_format. New subcommand: config. Improve help output.
Diffstat (limited to 'src')
-rw-r--r--src/ctl.c246
-rw-r--r--src/pies.c86
-rw-r--r--src/pies.h10
-rw-r--r--src/piesctl.c568
4 files changed, 764 insertions, 146 deletions
diff --git a/src/ctl.c b/src/ctl.c
index 54282a2..3cbe459 100644
--- a/src/ctl.c
+++ b/src/ctl.c
@@ -910,6 +910,8 @@ static void res_programs (struct ctlio *, enum http_method, char const *,
struct json_value *);
static void res_runlevel (struct ctlio *, enum http_method, char const *,
struct json_value *);
+static void res_conf (struct ctlio *, enum http_method, char const *,
+ struct json_value *);
static int pred_sysvinit (void);
@@ -926,6 +928,7 @@ struct ctlio_resource
static struct ctlio_resource restab[] = {
#define S(s) #s, (sizeof (#s)-1)
{ S(/instance), CTL_ADMIN_STATE, NULL, res_instance },
+ { S(/conf), CTL_ADMIN_STATE, NULL, res_conf },
{ S(/programs), CTL_ADMIN_STATE|CTL_USER_STATE, NULL,
res_programs },
{ S(/runlevel), CTL_ADMIN_STATE, pred_sysvinit, res_runlevel },
@@ -2108,3 +2111,246 @@ res_runlevel (struct ctlio *io, enum http_method meth,
ctlio_reply (io, 405, NULL);
}
+/* GET /conf/runtime - List configuration
+ * PUT /conf/runtime - Reload configuration
+ * POST /conf/runtime - Post new configuration
+ *
+ * GET /conf/files - List configuration files
+ * DELETE /conf/files - Delete some or all configuration files
+ * from the list
+ * POST /conf/files - Install new list configuration file
+ */
+
+static void
+conf_list_files (struct ctlio *io)
+{
+ io->output.reply = json_new_array ();
+ io->code = 200;
+ config_file_list_serialize (io->output.reply);
+}
+
+static void
+conf_add_file (struct ctlio *io, struct json_value *json)
+{
+ struct json_value *v;
+ struct config_syntax *synt;
+
+ if (json->type != json_object)
+ {
+ ctlio_reply (io, 400, NULL);
+ return;
+ }
+ if (json_object_get (json, "syntax", &v) || v->type != json_string)
+ {
+ ctlio_reply (io, 400, NULL);
+ return;
+ }
+ synt = str_to_config_syntax (v->v.s);
+ if (!synt)
+ {
+ ctlio_reply (io, 400, NULL);
+ return;
+ }
+ if (json_object_get (json, "file", &v) || v->type != json_string)
+ {
+ ctlio_reply (io, 400, NULL);
+ return;
+ }
+
+ config_file_add (synt, v->v.s);
+ ctlio_reply (io, 201, NULL);
+}
+
+static void
+conf_delete_files (struct ctlio *io, struct json_value *json)
+{
+ io->code = 200;
+ io->output.reply = json_reply_create ();
+
+ if ((json->type == json_bool && json->v.b == 1)
+ || (json->type == json_arr && json_array_size (json) == 0))
+ {
+ config_file_remove_all ();
+ json_object_set_string (io->output.reply, "status", "OK");
+ json_object_set_string (io->output.reply, "message",
+ "file list cleared");
+ }
+ else if (json->type == json_arr)
+ {
+ size_t i, n, fails = 0;
+ struct json_value *reply;
+
+ n = json_array_size (json);
+
+ /* Check request */
+ for (i = 0; i < n; i++)
+ {
+ struct json_value *val;
+ json_array_get (json, i, &val);
+ if (val->type != json_string)
+ {
+ json_object_set_string (io->output.reply, "status", "ER");
+ json_object_set_string (io->output.reply, "error_message",
+ "malformed request");
+ return;
+ }
+ }
+
+ reply = json_new_array ();
+ /* Process request */
+ for (i = 0; i < n; i++)
+ {
+ struct json_value *val;
+ int rc;
+
+ json_array_get (json, i, &val);
+
+ rc = config_file_remove (val->v.s);
+ if (rc)
+ ++fails;
+ json_array_append (reply, json_new_bool (!rc));
+ }
+
+ if (fails == n)
+ {
+ json_object_set_string (io->output.reply, "status", "ER");
+ json_object_set_string (io->output.reply, "error_message",
+ "no matching files found");
+ json_value_free (reply);
+ }
+ else
+ {
+ json_object_set_string (io->output.reply, "status", "OK");
+ if (fails)
+ {
+ json_object_set_string (io->output.reply, "error_message",
+ "some files not removed");
+ json_object_set (io->output.reply, "result", reply);
+ }
+ else
+ json_value_free (reply);
+ }
+ }
+}
+
+static void (*saved_diag_fun)(grecs_locus_t const *, int, int,
+ const char *msg);
+static struct json_value *messages;
+
+static void
+reload_diag_fun (grecs_locus_t const *locus, int err, int errcode,
+ const char *text)
+{
+ struct json_value *msg = json_new_object ();
+
+ if (locus)
+ {
+ struct json_value *ar = json_new_array ();
+ json_array_append (ar, json_new_string (locus->beg.file));
+ json_array_append (ar, json_new_string (locus->end.file));
+ json_object_set (msg, "file", ar);
+
+ ar = json_new_array ();
+ json_array_append (ar, json_new_number (locus->beg.line));
+ json_array_append (ar, json_new_number (locus->end.line));
+ json_object_set (msg, "line", ar);
+
+ ar = json_new_array ();
+ json_array_append (ar, json_new_number (locus->beg.col));
+ json_array_append (ar, json_new_number (locus->end.col));
+ json_object_set (msg, "col", ar);
+
+ json_object_set (msg, "error", json_new_bool (err));
+
+ if (errcode)
+ {
+ json_object_set (msg, "syserrno",
+ json_new_number (errno));
+ json_object_set (msg, "syserrstr",
+ json_new_string (strerror (errno)));
+ }
+
+ json_object_set (msg, "message", json_new_string (text));
+ json_array_append (messages, msg);
+ }
+
+ saved_diag_fun (locus, err, errcode, text);
+}
+
+static void
+conf_reload (struct ctlio *io)
+{
+ io->code = 200;
+ io->output.reply = json_reply_create ();
+
+ saved_diag_fun = grecs_print_diag_fun;
+ grecs_print_diag_fun = reload_diag_fun;
+ messages = json_new_array ();
+ if (pies_read_config ())
+ {
+ json_object_set_string (io->output.reply, "status", "ER");
+ json_object_set_string (io->output.reply, "error_message",
+ "configuration syntax error");
+ }
+ else
+ {
+ pies_schedule_action (ACTION_COMMIT);
+ json_object_set_string (io->output.reply, "status", "OK");
+ json_object_set_string (io->output.reply, "message",
+ "reload successful");
+ }
+ json_object_set (io->output.reply, "parser_messages", messages);
+ grecs_print_diag_fun = saved_diag_fun;
+}
+
+static void
+res_conf (struct ctlio *io, enum http_method meth,
+ char const *uri, struct json_value *json)
+{
+ if (!uri)
+ {
+ ctlio_reply (io, 404, NULL);
+ return;
+ }
+
+ ++uri; /* skip leading / */
+ if (strcmp (uri, "files") == 0)
+ {
+ switch (meth)
+ {
+ case METH_GET:
+ conf_list_files (io);
+ break;
+
+ case METH_POST:
+ conf_add_file (io, json);
+ break;
+
+ case METH_DELETE:
+ conf_delete_files (io, json);
+ break;
+
+ default:
+ ctlio_reply (io, 405, NULL);
+ }
+ }
+ else if (strcmp (uri, "runtime") == 0)
+ {
+ switch (meth)
+ {
+ case METH_GET:
+ case METH_POST:
+ ctlio_reply (io, 501, NULL);
+ break;
+
+ case METH_PUT:
+ conf_reload (io);
+ break;
+
+ default:
+ ctlio_reply (io, 405, NULL);
+ }
+ }
+ else
+ ctlio_reply (io, 404, NULL);
+}
diff --git a/src/pies.c b/src/pies.c
index cb4d2f5..59f3bbb 100644
--- a/src/pies.c
+++ b/src/pies.c
@@ -98,6 +98,17 @@ str_to_config_syntax (const char *str)
return NULL;
}
+static void
+config_file_free (void *ptr)
+{
+ if (ptr)
+ {
+ struct config_file *file = ptr;
+ grecs_free (file->name);
+ grecs_free (file);
+ }
+}
+
void
config_file_add (struct config_syntax *syntax, const char *name)
{
@@ -105,7 +116,10 @@ config_file_add (struct config_syntax *syntax, const char *name)
file->syntax = syntax;
file->name = grecs_strdup (name);
if (!config_list)
+ {
config_list = grecs_list_create ();
+ config_list->free_entry = config_file_free;
+ }
grecs_list_append (config_list, file);
}
@@ -115,6 +129,45 @@ config_file_add_type (enum config_syntax_type syntax, const char *name)
config_file_add (&config_syntax_tab[syntax], name);
}
+int
+config_file_remove (const char *name)
+{
+ struct grecs_list_entry *ep;
+
+ for (ep = config_list->head; ep; ep = ep->next)
+ {
+ struct config_file *file = ep->data;
+ if (strcmp (file->name, name) == 0)
+ {
+ grecs_list_remove_entry (config_list, ep);
+ config_file_free (file);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+void
+config_file_remove_all (void)
+{
+ grecs_list_clear (config_list);
+}
+
+void
+config_file_list_serialize (struct json_value *ar)
+{
+ struct grecs_list_entry *ep;
+
+ for (ep = config_list->head; ep; ep = ep->next)
+ {
+ struct config_file *file = ep->data;
+ struct json_value *obj = json_new_object ();
+ json_object_set (obj, "syntax", json_new_string (file->syntax->name));
+ json_object_set (obj, "file", json_new_string (file->name));
+ json_array_append (ar, obj);
+ }
+}
+
/* Logging */
static int
stderr_closed_p ()
@@ -1492,6 +1545,9 @@ pies_config_parse (char const *name)
grecs_tree_free (tree);
+ if (grecs_error_count)
+ return 1;
+
return 0;
}
@@ -1528,22 +1584,6 @@ pies_read_config (void)
return err;
}
-int
-pies_reload (void)
-{
- int rc = pies_read_config ();
- if (rc == 0)
- {
- component_config_commit ();
- if (init_process)
- sysvinit_runlevel_setup (PIES_COMP_DEFAULT);
- progman_create_sockets ();
- progman_start ();
- }
-
- return rc;
-}
-
static struct config_syntax *current_syntax = &config_syntax_tab[CONF_PIES];
#include "cmdline.h"
@@ -2186,7 +2226,19 @@ main (int argc, char **argv)
break;
case ACTION_RELOAD:
- pies_reload ();
+ if (pies_read_config ())
+ {
+ action = ACTION_CONT;
+ break;
+ }
+ /* fall through */
+ case ACTION_COMMIT:
+ component_config_commit ();
+ if (init_process)
+ sysvinit_runlevel_setup (PIES_COMP_DEFAULT);
+ progman_create_sockets ();
+ progman_start ();
+
pies_schedule_children (PIES_CHLD_WAKEUP);
action = ACTION_CONT;
break;
diff --git a/src/pies.h b/src/pies.h
index b61e11b..5154317 100644
--- a/src/pies.h
+++ b/src/pies.h
@@ -271,7 +271,8 @@ enum pies_action {
ACTION_RELOAD,
ACTION_CTRLALTDEL,
ACTION_KBREQUEST,
- ACTION_POWER
+ ACTION_POWER,
+ ACTION_COMMIT
};
extern char *instance;
@@ -309,6 +310,10 @@ struct config_syntax;
struct config_syntax *str_to_config_syntax (const char *str);
void config_file_add (struct config_syntax *syntax, const char *name);
void config_file_add_type (enum config_syntax_type syntax, const char *name);
+void config_file_list_serialize (struct json_value *ar);
+
+int config_file_remove (const char *name);
+void config_file_remove_all (void);
void free_redirector (struct redirector *rp);
@@ -322,9 +327,12 @@ void free_action (struct action *act);
void pies_schedule_children (int op);
+int pies_read_config (void);
+
void register_prog (struct component *comp);
int progman_waiting_p (void);
void progman_start (void);
+void progman_gc (void);
void progman_wake_sleeping (int);
void progman_stop (void);
void progman_cleanup (int expect_term);
diff --git a/src/piesctl.c b/src/piesctl.c
index e2e0483..310dcc3 100644
--- a/src/piesctl.c
+++ b/src/piesctl.c
@@ -24,6 +24,7 @@
#include <locale.h>
#include <errno.h>
#include <fcntl.h>
+#include <assert.h>
#include <configmake.h>
#include <grecs.h>
#include <grecs-locus.h>
@@ -1240,14 +1241,15 @@ print_comp (FILE *fp, struct json_value *v, size_t n)
fputc ('\n', fp);
}
-struct pcond_parser_state
+struct cmdline_parser_state
{
int argc;
char **argv;
+ struct shttp_connection *conn;
};
-static char *
-next_token (struct pcond_parser_state *state)
+static char const *
+next_token (struct cmdline_parser_state *state)
{
if (state->argc == 0)
return NULL;
@@ -1256,13 +1258,79 @@ next_token (struct pcond_parser_state *state)
}
static char const *
-peek_token (struct pcond_parser_state *state)
+peek_token (struct cmdline_parser_state *state)
{
if (state->argc == 0)
return NULL;
return *state->argv;
}
+static void
+parse_error (char const *fmt, ...)
+{
+ va_list ap;
+ va_start (ap, fmt);
+ fflush (stdout);
+ fprintf (stderr, "%s: ", program_name);
+ vfprintf (stderr, fmt, ap);
+ fputc ('\n', stderr);
+ va_end (ap);
+ exit (EX_USAGE);
+}
+
+static char const *
+require_token (struct cmdline_parser_state *state)
+{
+ char const *tok = next_token (state);
+ if (!tok)
+ parse_error ("%s", _("unexpected end of statement"));
+ return tok;
+}
+
+static void
+assert_eol (struct cmdline_parser_state *state)
+{
+ char const *tok = next_token (state);
+ if (tok)
+ parse_error (_("expected end of statement, but found \"%s\""), tok);
+}
+
+static void
+acc_writer (void *closure, char const *text, size_t len)
+{
+ grecs_txtacc_grow ((struct grecs_txtacc *)closure, text, len);
+}
+
+static char *
+piesctl_format (struct json_value *val, char const *base)
+{
+ char *ret = NULL;
+ struct grecs_txtacc *acc;
+
+ acc = grecs_txtacc_create ();
+ if (base)
+ {
+ grecs_txtacc_grow_string (acc, base);
+ if (val)
+ grecs_txtacc_grow_char (acc, '?');
+ }
+ if (val)
+ {
+ struct json_format fmt = {
+ .indent = 0,
+ .precision = 0,
+ .write = acc_writer,
+ .data = acc
+ };
+ json_format_value (val, &fmt);
+ }
+ grecs_txtacc_grow_char (acc, 0);
+
+ ret = grecs_txtacc_finish (acc, 1);
+ grecs_txtacc_free (acc);
+ return ret;
+}
+
static struct json_value *
json_encode_op (char const *op, struct json_value *arg)
{
@@ -1272,10 +1340,10 @@ json_encode_op (char const *op, struct json_value *arg)
return ret;
}
-static void pcond_parse_or (struct pcond_parser_state *, struct json_value **);
+static void pcond_parse_or (struct cmdline_parser_state *, struct json_value **);
static void
-pcond_parse_unary (struct pcond_parser_state *state, struct json_value **ret)
+pcond_parse_unary (struct cmdline_parser_state *state, struct json_value **ret)
{
char const *term = next_token (state);
@@ -1292,20 +1360,14 @@ pcond_parse_unary (struct pcond_parser_state *state, struct json_value **ret)
else if (is_array_member (arg_terms, term))
{
if (!peek_token (state))
- {
- grecs_error (NULL, 0, _("%s requires argument"), term);
- exit (EX_USAGE);
- }
+ parse_error (_("%s requires argument"), term);
*ret = json_encode_op (term, json_new_string (next_token (state)));
}
else if (strcasecmp (term, "not") == 0)
{
struct json_value *val;
if (!peek_token (state))
- {
- grecs_error (NULL, 0, _("%s requires argument"), term);
- exit (EX_USAGE);
- }
+ parse_error (_("%s requires argument"), term);
pcond_parse_unary (state, &val);
*ret = json_encode_op (term, val);
}
@@ -1314,16 +1376,10 @@ pcond_parse_unary (struct pcond_parser_state *state, struct json_value **ret)
pcond_parse_or (state, ret);
term = next_token (state);
if (!term || term[0] != ')')
- {
- grecs_error (NULL, 0, _("unbalanced parentesis"));
- exit (EX_USAGE);
- }
+ parse_error ("%s", _("unbalanced parentesis"));
}
else
- {
- grecs_error (NULL, 0, _("parse error at %s"), term);
- exit (EX_USAGE);
- }
+ parse_error (_("parse error at %s"), term);
}
}
@@ -1365,7 +1421,7 @@ binop_append_optimized (struct json_value *ar, struct json_value *val,
}
static void
-pcond_parse_and (struct pcond_parser_state *state, struct json_value **ret)
+pcond_parse_and (struct cmdline_parser_state *state, struct json_value **ret)
{
char const *token;
struct json_value *left, *right, *ar;
@@ -1386,7 +1442,7 @@ pcond_parse_and (struct pcond_parser_state *state, struct json_value **ret)
}
static void
-pcond_parse_or (struct pcond_parser_state *state, struct json_value **ret)
+pcond_parse_or (struct cmdline_parser_state *state, struct json_value **ret)
{
char const *token;
struct json_value *left, *right, *ar;
@@ -1413,17 +1469,11 @@ parse_condition (int argc, char **argv)
if (argc > 1)
{
- struct pcond_parser_state state;
+ struct cmdline_parser_state state;
state.argc = argc - 1;
state.argv = argv + 1;
pcond_parse_or (&state, &val);
- if (state.argc)
- {
- grecs_error (NULL, 0,
- _("expected end of statement, but found \"%s\""),
- *state.argv);
- exit (EX_USAGE);
- }
+ assert_eol (&state);
#if 0
print_json (stdout, val);
exit (0);
@@ -1432,47 +1482,16 @@ parse_condition (int argc, char **argv)
return val;
}
-static void
-acc_writer (void *closure, char const *text, size_t len)
-{
- grecs_txtacc_grow ((struct grecs_txtacc *)closure, text, len);
-}
-
static char *
parse_condition_to_uri (char const *base, int argc, char **argv, int mandatory)
{
- char *ret = NULL;
- struct grecs_txtacc *acc;
struct json_value *val;
-
+ char *ret;
if (mandatory && argc == 1)
- {
- grecs_error (NULL, 0, _("condition must be specified"));
- exit (EX_USAGE);
- }
-
- acc = grecs_txtacc_create ();
- if (base)
- grecs_txtacc_grow_string (acc, base);
-
+ parse_error ("%s", _("condition must be specified"));
val = parse_condition (argc, argv);
- if (val)
- {
- struct json_format fmt = {
- .indent = 0,
- .precision = 0,
- .write = acc_writer,
- .data = acc
- };
- if (base)
- grecs_txtacc_grow_char (acc, '?');
- json_format_value (val, &fmt);
+ ret = piesctl_format (val, base);
json_value_free (val);
- }
- grecs_txtacc_grow_char (acc, 0);
-
- ret = grecs_txtacc_finish (acc, 1);
- grecs_txtacc_free (acc);
return ret;
}
@@ -1516,6 +1535,21 @@ json_object_get_type (struct json_value *obj, char const *name,
return 0;
}
+static struct json_value *
+json_object_require_type (struct json_value *obj, char const *name,
+ enum json_value_type type)
+{
+ struct json_value *val;
+
+ if (json_object_get_type (obj, name, type, &val))
+ {
+ grecs_error (NULL, 0, _("can't get attribute %s"), name);
+ // shttp_format_result (conn, stderr);
+ exit (EX_PROTOCOL);
+ }
+ return val;
+}
+
static void
shttp_print_response_status (struct shttp_connection *conn)
{
@@ -1728,8 +1762,7 @@ com_reboot (struct shttp_connection *conn, int argc, char **argv)
*/
struct telinit_parser
{
- int argc;
- char **argv;
+ struct cmdline_parser_state state;
int method;
struct json_value *query;
char *uri;
@@ -1761,19 +1794,10 @@ telinit_format_runlevel (struct shttp_connection *conn)
}
}
-static char *
-telinit_get_token (struct telinit_parser *p)
-{
- if (p->argc == 0)
- return NULL;
- --p->argc;
- return *p->argv++;
-}
-
static void
telinit_parse_runlevel (struct telinit_parser *p)
{
- char *tok = telinit_get_token (p);
+ char const *tok = next_token (&p->state);
if (!tok)
{
@@ -1792,15 +1816,12 @@ telinit_parse_runlevel (struct telinit_parser *p)
static void
telinit_parse (struct telinit_parser *p)
{
- char *tok = telinit_get_token (p);
+ char const *tok = next_token (&p->state);
if (strcmp (tok, "runlevel") == 0)
telinit_parse_runlevel (p);
else
- {
- grecs_error (NULL, 0, _("unrecognized subcommand: %s"), tok);
- exit (EX_USAGE);
- }
+ parse_error (_("unrecognized subcommand: %s"), tok);
}
static void
@@ -1809,13 +1830,10 @@ com_telinit (struct shttp_connection *conn, int argc, char **argv)
struct telinit_parser parser;
if (argc == 1)
- {
- grecs_error (NULL, 0, _("not enough arguments to telinit"));
- exit (EX_USAGE);
- }
+ parse_error ("%s", _("not enough arguments to telinit"));
- parser.argc = argc-1;
- parser.argv = argv+1;
+ parser.state.argc = argc-1;
+ parser.state.argv = argv+1;
parser.method = METH_INVALID;
parser.query = NULL;
telinit_parse (&parser);
@@ -1845,8 +1863,7 @@ com_telinit (struct shttp_connection *conn, int argc, char **argv)
break;
default:
- grecs_error (NULL, 0, _("bad syntax"));
- exit (EX_USAGE);
+ parse_error ("%s", _("bad syntax"));
}
json_value_free (parser.query);
@@ -1856,6 +1873,267 @@ com_telinit (struct shttp_connection *conn, int argc, char **argv)
parser.format (conn);
}
+/*
+ * piesctl config reload
+ * piesctl config file clear
+ * piesctl config file add syntax name
+ * piesctl config file del[ete] name [name...]
+ * piesctl config file list
+ */
+
+static void
+locus_deserialize (struct grecs_locus *loc, struct json_value *msg)
+{
+ struct json_value *tmp, *x;
+
+ memset (loc, 0, sizeof *loc);
+ if (json_object_get_type (msg, "file", json_arr, &tmp) == 0
+ && json_array_size (tmp) == 2)
+ {
+ if (json_array_get (tmp, 0, &x) == 0 && x->type == json_string)
+ loc->beg.file = x->v.s;
+ else
+ loc->beg.file = "<unknown>";
+ if (json_array_get (tmp, 1, &x) == 0 && x->type == json_string)
+ loc->end.file = x->v.s;
+ else
+ loc->beg.file = "<unknown>";
+ }
+
+ if (json_object_get_type (msg, "line", json_arr, &tmp) == 0
+ && json_array_size (tmp) == 2)
+ {
+ if (json_array_get (tmp, 0, &x) == 0 && x->type == json_number)
+ loc->beg.line = x->v.n;
+ if (json_array_get (tmp, 1, &x) == 0 && x->type == json_number)
+ loc->end.line = x->v.n;
+ }
+
+ if (json_object_get_type (msg, "col", json_arr, &tmp) == 0
+ && json_array_size (tmp) == 2)
+ {
+ if (json_array_get (tmp, 0, &x) == 0 && x->type == json_number)
+ loc->beg.col = x->v.n;
+ if (json_array_get (tmp, 1, &x) == 0 && x->type == json_number)
+ loc->end.col = x->v.n;
+ }
+}
+
+static void
+conf_reload (struct cmdline_parser_state *state)
+{
+ struct shttp_connection *conn = state->conn;
+ struct json_value *val;
+
+ assert_eol (state);
+ shttp_io_init (&conn->req);
+ shttp_process (conn, METH_PUT, "/conf/runtime");
+ if (dump)
+ return;
+ assert (conn->result && conn->result->type == json_object);
+
+ val = json_object_require_type (conn->result, "status", json_string);
+ if (strcmp (val->v.s, "ER") == 0)
+ {
+ if (json_object_get_type (conn->result, "error_message",
+ json_string, &val) == 0)
+ fputs (val->v.s, stdout);
+ else
+ printf ("%s", _("unknown error"));
+ fputc ('\n', stdout);
+ }
+
+ if (json_object_get_type (conn->result, "parser_messages", json_arr,
+ &val) == 0)
+ {
+ size_t i, n = json_array_size (val);
+ struct json_value *msg, *tmp;
+
+ for (i = 0; i < n; i++)
+ {
+ struct grecs_locus loc;
+ int err = 1;
+ int ec;
+
+ if (json_array_get (val, i, &msg) || msg->type != json_object)
+ continue;
+ locus_deserialize (&loc, msg);
+ if (json_object_get_type (msg, "error", json_bool, &tmp) == 0)
+ err = tmp->v.b;
+ if (json_object_get_type (msg, "syserrno", json_number, &tmp) == 0)
+ ec = tmp->v.n;
+
+ tmp = json_object_require_type (msg, "message", json_string);
+ piesctl_diag (&loc, err, ec, tmp->v.s);
+ }
+ }
+}
+
+static void
+conf_file_list (struct cmdline_parser_state *state)
+{
+ struct shttp_connection *conn = state->conn;
+ struct json_value *val, *tmp;
+ size_t i, n;
+
+ assert_eol (state);
+ shttp_io_init (&conn->req);
+ shttp_process (conn, METH_GET, "/conf/files");
+ if (dump)
+ return;
+ assert (conn->result && conn->result->type == json_arr);
+ n = json_array_size (conn->result);
+ for (i = 0; i < n; i++)
+ {
+ char const *syntax, *file;
+ if (json_array_get (conn->result, i, &val) || val->type != json_object)
+ continue;
+ if (json_object_get_type (val, "syntax", json_string, &tmp))
+ continue;
+ syntax = tmp->v.s;
+ if (json_object_get_type (val, "file", json_string, &tmp))
+ continue;
+ file = tmp->v.s;
+ printf ("%-16s%s\n", syntax, file);
+ }
+}
+
+static void
+conf_file_clear (struct cmdline_parser_state *state)
+{
+ struct shttp_connection *conn = state->conn;
+ struct json_value *val;
+
+ assert_eol (state);
+ shttp_io_init (&conn->req);
+ shttp_process (conn, METH_DELETE, "/conf/files");
+ val = json_object_require_type (conn->result, "status", json_string);
+ if (strcmp (val->v.s, "OK"))
+ {
+ if (json_object_get_type (conn->result, "error_message",
+ json_string, &val) == 0)
+ fputs (val->v.s, stderr);
+ else
+ fputs (_("unknown error"), stderr);
+ fputc ('\n', stderr);
+ }
+}
+
+static void
+conf_file_add (struct cmdline_parser_state *state)
+{
+ struct shttp_connection *conn = state->conn;
+ struct json_value *val;
+ char const *name, *syntax;
+
+ syntax = require_token (state);
+ name = require_token (state);
+
+ val = json_new_object ();
+ json_object_set (val, "syntax", json_new_string (syntax));
+ json_object_set (val, "file", json_new_string (name));
+
+ shttp_io_init (&conn->req);
+ conn->req.content = piesctl_format (val, NULL);
+ conn->req.content_length = strlen (conn->req.content);
+ shttp_process (conn, METH_POST, "/conf/files");
+ // FIXME conn->resp.code == 201?
+}
+
+static void
+conf_file_del (struct cmdline_parser_state *state)
+{
+ struct shttp_connection *conn = state->conn;
+ struct json_value *val = json_new_array ();
+ char const *tok;
+ char *uri;
+ struct cmdline_parser_state save_state = *state;
+
+ while ((tok = next_token (state)))
+ json_array_append (val, json_new_string (tok));
+
+ shttp_io_init (&conn->req);
+ uri = piesctl_format (val, "/conf/files");
+ json_value_free (val);
+
+ shttp_process (conn, METH_DELETE, uri);
+ free (uri);
+
+ val = json_object_require_type (conn->result, "status", json_string);
+ if (strcmp (val->v.s, "OK") == 0)
+ {
+ if (json_object_get_type (conn->result, "error_message",
+ json_string, &val) == 0)
+ fputs (val->v.s, stderr);
+ fputc ('\n', stderr);
+ if (json_object_get_type (conn->result, "result", json_arr, &val) == 0)
+ {
+ size_t i, n = json_array_size (val);
+ assert (n == save_state.argc);
+ for (i = 0; i < n; i++)
+ {
+ struct json_value *v;
+ if (json_array_get (val, i, &v) == 0
+ && v->type == json_bool
+ && !v->v.b)
+ {
+ fputs (save_state.argv[i], stderr);
+ fputc ('\n', stderr);
+ }
+ }
+ }
+ }
+ else
+ {
+ if (json_object_get_type (conn->result, "error_message",
+ json_string, &val) == 0)
+ fputs (val->v.s, stderr);
+ else
+ fputs (_("unknown error"), stderr);
+ fputc ('\n', stderr);
+ }
+}
+
+static void
+conf_file (struct cmdline_parser_state *state)
+{
+ char const *tok = require_token (state);
+ if (strcmp (tok, "add") == 0)
+ conf_file_add (state);
+ else if (strcmp (tok, "del") == 0 || strcmp (tok, "delete") == 0)
+ conf_file_del (state);
+ else if (strcmp (tok, "list") == 0)
+ conf_file_list (state);
+ else if (strcmp (tok, "clear") == 0)
+ conf_file_clear (state);
+ else
+ parse_error (_("unexpected word: %s"), tok);
+}
+
+static void
+conf_parse (struct cmdline_parser_state *state)
+{
+ char const *tok = require_token (state);
+
+ if (strcmp (tok, "reload") == 0)
+ conf_reload (state);
+ else if (strcmp (tok, "file") == 0)
+ conf_file (state);
+ else
+ parse_error (_("unexpected word: %s"), tok);
+}
+
+static void
+com_config (struct shttp_connection *conn, int argc, char **argv)
+{
+ struct cmdline_parser_state state;
+
+ state.argc = argc - 1;
+ state.argv = argv + 1;
+ state.conn = conn;
+ conf_parse (&state);
+}
+
typedef void (*ctlcom_t) (struct shttp_connection *, int, char **);
struct comtab
@@ -1867,20 +2145,64 @@ struct comtab
};
static struct comtab comtab[] = {
- { "list", N_("[CONDITION]"), N_("list configured components"), com_list },
- { "stop", N_("CONDITION"), N_("stop components"), com_stop },
- { "start", N_("CONDITION"), N_("start components"), com_start },
- { "restart", N_("CONDITION"), N_("restart components"), com_restart },
+ { "list", N_("[CONDITION]"),
+ N_("list configured components"), com_list },
+ { "stop", N_("CONDITION"),
+ N_("stop components"), com_stop },
+ { "start", N_("CONDITION"),
+ N_("start components"), com_start },
+ { "restart", N_("CONDITION"),
+ N_("restart components"), com_restart },
+ { NULL, NULL,
+ N_("CONDITION is defined as follows:\
+\n\
+ <condition> ::= <disjunction>\n\
+ <disjunction> ::= <conjunction> | <conjunction> \"or\" <disjunction>\n\
+ <conjunction> ::= <unary> | <unary> \"and\" <conjunction>\n\
+ <unary> ::= <term> | \"not\" <condition> | \"(\" <condition> \")\"\n\
+ <term> ::= \"all\" | <keyword> <value>\n\
+ <keyword> ::= \"type\" | \"mode\" | \"status\" | \"component\"\n\
+ <value> ::= <word> | <quoted-string>\n\
+ <word> ::= <printable> | <word> <printable>\n\
+ <printable> ::= \"A\" - \"Z\" | \"a\" - \"z\" | \"0\" - \"9\" |\n\
+ \"_\" | \".\" | \"*\" | \":\" | \"@\" | \"[\" | \"]\" | \"-\" | \"/\"\n\
+ <quoted-string> ::= \"\"\" <string> \"\"\"\n\
+ <string> ::= <char> | <string> <char>\n\
+ <char> ::= <any character except \"\\\" and \"\"\"> | \"\\\\\" | \"\\\"\""),
+ NULL },
+
{ "id", N_("[KEYWORDS...]"),
N_("show info about the running GNU Pies instance"),
com_id },
- { "shutdown", NULL, N_("stop running pies instance"), com_shutdown },
- { "reboot", NULL, N_("restart pies instance"), com_reboot },
- { "telinit", "ARGS", N_("communicate with the init process"),
+ { NULL, NULL,
+ N_("Available KEYWORDS are: package, version, instance, binary, argv, PID"), NULL },
+ { "shutdown", "",
+ N_("stop running pies instance"), com_shutdown },
+ { "reboot", "",
+ N_("restart pies instance"), com_reboot },
+
+ { "config", "reload",
+ N_("reload configuration"), com_config },
+ { NULL, "file clear",
+ N_("clear configuration file list"), NULL },
+ { NULL, "file add SYNTAX FILE",
+ N_("add FILE of given SYNTAX to the list of configuration files"), NULL },
+ { NULL, "file del[ete] NAME [NAME...]",
+ N_("remove listed names from the list of configuration files"), NULL },
+ { NULL, "file list",
+ N_("list configuration files"), NULL },
+
+ { "telinit", "runlevel [N]", N_("list or change the runlevel"),