aboutsummaryrefslogtreecommitdiff
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
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.
-rw-r--r--src/ctl.c246
-rw-r--r--src/pies.c88
-rw-r--r--src/pies.h10
-rw-r--r--src/piesctl.c612
4 files changed, 787 insertions, 169 deletions
diff --git a/src/ctl.c b/src/ctl.c
index 54282a2..3cbe459 100644
--- a/src/ctl.c
+++ b/src/ctl.c
@@ -907,12 +907,14 @@ method_decode (char const *method)
static void res_instance (struct ctlio *, enum http_method, char const *,
struct json_value *);
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);
struct ctlio_resource
{
char const *uri;
@@ -923,12 +925,13 @@ struct ctlio_resource
char const *uri, struct json_value *);
};
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 },
{ NULL }
#undef S
};
@@ -2104,7 +2107,250 @@ res_runlevel (struct ctlio *io, enum http_method meth,
}
}
}
else
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
@@ -95,28 +95,81 @@ str_to_config_syntax (const char *str)
for (i = 0; i < ARRAY_SIZE (config_syntax_tab); i++)
if (strcmp (config_syntax_tab[i].name, str) == 0)
return &config_syntax_tab[i];
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)
{
struct config_file *file = grecs_malloc (sizeof (file[0]));
file->syntax = syntax;
file->name = grecs_strdup (name);
if (!config_list)
- config_list = grecs_list_create ();
+ {
+ config_list = grecs_list_create ();
+ config_list->free_entry = config_file_free;
+ }
grecs_list_append (config_list, file);
}
void
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 ()
{
int fd = dup (0);
@@ -1489,12 +1542,15 @@ pies_config_parse (char const *name)
if (grecs_tree_process (tree, pies_keywords))
return 1;
grecs_tree_free (tree);
+ if (grecs_error_count)
+ return 1;
+
return 0;
}
void
config_help ()
{
@@ -1524,28 +1580,12 @@ pies_read_config (void)
if (err)
component_config_rollback ();
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"
@@ -2183,13 +2223,25 @@ main (int argc, char **argv)
logmsg (LOG_INFO, _("restart command ignored"));
action = ACTION_CONT;
}
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;
case ACTION_STOP:
if (init_process)
diff --git a/src/pies.h b/src/pies.h
index b61e11b..5154317 100644
--- a/src/pies.h
+++ b/src/pies.h
@@ -268,13 +268,14 @@ enum pies_action {
ACTION_CONT,
ACTION_STOP,
ACTION_RESTART,
ACTION_RELOAD,
ACTION_CTRLALTDEL,
ACTION_KBREQUEST,
- ACTION_POWER
+ ACTION_POWER,
+ ACTION_COMMIT
};
extern char *instance;
extern char *log_tag;
extern int log_facility;
extern unsigned long shutdown_timeout;
@@ -306,12 +307,16 @@ enum config_syntax_type
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);
void pies_schedule_action (int act);
void free_action (struct action *act);
@@ -319,15 +324,18 @@ void free_action (struct action *act);
#define PIES_CHLD_CLEANUP 0x01
#define PIES_CHLD_WAKEUP 0x02
#define PIES_CHLD_GC 0x04
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);
void progman_filter (int (*filter) (struct component *, void *data),
void *data);
int progman_wait_until (int (*cond) (void *), void *data);
diff --git a/src/piesctl.c b/src/piesctl.c
index e2e0483..310dcc3 100644
--- a/src/piesctl.c
+++ b/src/piesctl.c
@@ -21,12 +21,13 @@
#include <sys/un.h>
#include <sys/stat.h>
#include <netdb.h>
#include <locale.h>
#include <errno.h>
#include <fcntl.h>
+#include <assert.h>
#include <configmake.h>
#include <grecs.h>
#include <grecs-locus.h>
#include <json.h>
#include <wordsplit.h>
#include <sysexits.h>
@@ -1236,49 +1237,116 @@ print_comp (FILE *fp, struct json_value *v, size_t n)
p = getval (v, "command", json_string, 0);
if (p)
fprintf (fp, "%s", p->v.s);
}
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;
--state->argc;
return *state->argv++;
}
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)
{
struct json_value *ret = json_new_object ();
json_object_set (ret, "op", json_new_string (op));
json_object_set (ret, "arg", 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);
if (!term)
*ret = json_new_bool (1);
else
@@ -1289,44 +1357,32 @@ pcond_parse_unary (struct pcond_parser_state *state, struct json_value **ret)
if (strcasecmp (term, "all") == 0)
*ret = json_new_bool (1);
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);
}
else if (term[0] == '(')
{
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);
}
}
static int
is_op (struct json_value *val, char const *opcode)
{
@@ -1362,13 +1418,13 @@ binop_append_optimized (struct json_value *ar, struct json_value *val,
}
else
json_array_append (ar, 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;
pcond_parse_unary (state, &left);
token = peek_token (state);
@@ -1383,13 +1439,13 @@ pcond_parse_and (struct pcond_parser_state *state, struct json_value **ret)
json_array_append (ar, left);
binop_append_optimized (ar, right, token);
*ret = json_encode_op (token, ar);
}
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;
pcond_parse_and (state, &left);
token = peek_token (state);
@@ -1410,72 +1466,35 @@ static struct json_value *
parse_condition (int argc, char **argv)
{
struct json_value *val = NULL;
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);
#endif
}
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);
- json_value_free (val);
- }
- grecs_txtacc_grow_char (acc, 0);
-
- ret = grecs_txtacc_finish (acc, 1);
- grecs_txtacc_free (acc);
+ ret = piesctl_format (val, base);
+ json_value_free (val);
return ret;
}
static void
com_list (struct shttp_connection *conn, int argc, char **argv)
{
@@ -1513,12 +1532,27 @@ json_object_get_type (struct json_value *obj, char const *name,
return -1;
}
*ret = v;
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)
{
if (!dump && conn->result && conn->result->type == json_arr)
{
size_t i, n = json_array_size (conn->result);
@@ -1725,14 +1759,13 @@ com_reboot (struct shttp_connection *conn, int argc, char **argv)
/*
<telinit> ::= "telinit" <initcmd>
<initcmd> ::= "runlevel" | "runlevel" <level>
*/
struct telinit_parser
{
- int argc;
- char **argv;
+ struct cmdline_parser_state state;
int method;
struct json_value *query;
char *uri;
void (*format) (struct shttp_connection *);
};
@@ -1758,25 +1791,16 @@ telinit_format_runlevel (struct shttp_connection *conn)
fmt->format (v);
putchar ('\n');
}
}
}
-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)
{
p->method = METH_GET;
p->format = telinit_format_runlevel;
}
@@ -1789,36 +1813,30 @@ 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
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);
shttp_io_init (&conn->req);
switch (parser.method)
@@ -1842,124 +1860,421 @@ com_telinit (struct shttp_connection *conn, int argc, char **argv)
conn->req.content_length = strlen (conn->req.content);
grecs_txtacc_free (acc);
}
break;
default:
- grecs_error (NULL, 0, _("bad syntax"));
- exit (EX_USAGE);
+ parse_error ("%s", _("bad syntax"));
}
json_value_free (parser.query);
shttp_process (conn, parser.method, "/runlevel");
if (conn->resp.code == 200 && !dump && parser.format)
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);
+}