Works, but not yet integrated with the build system.
--- /dev/null
+{"command": "reconfigure"}
--- /dev/null
+{"command": "show", "options": [{"type": "serial"}, {"type": "version"}, {"type": "uptime"}, {"type": "tid_req_count"}, {"type": "tid_req_pending"}, {"type": "routes"}, {"type": "communities"}]}
--- /dev/null
+{"command": "show"}
--- /dev/null
+//
+// Created by jlr on 4/9/18.
+//
+
+#include <talloc.h>
+#include <jansson.h>
+#include <assert.h>
+#include <string.h>
+#include <glib.h>
+
+#include "../tr_mon_req.h"
+
+/**
+ * @return reconfigure command
+ */
+TR_MON_REQ *reconfigure()
+{
+ TR_MON_REQ *req = tr_mon_req_new(NULL, MON_CMD_RECONFIGURE);
+ assert(req);
+ return req;
+}
+
+/**
+ * @return show command with no options
+ */
+TR_MON_REQ *show_plain()
+{
+ TR_MON_REQ *req = tr_mon_req_new(NULL, MON_CMD_SHOW);
+ assert(req);
+ return req;
+}
+
+/**
+ * @param opts array of option types, terminated with OPT_TYPE_UNKNOWN
+ * @return show command with the requested options, excluding the terminator
+ */
+TR_MON_REQ *show_options(const TR_MON_OPT_TYPE *opts)
+{
+ TR_MON_REQ *req = tr_mon_req_new(NULL, MON_CMD_SHOW);
+ assert(req);
+
+ while (*opts != OPT_TYPE_UNKNOWN) {
+ assert(TR_MON_SUCCESS == tr_mon_req_add_option(req, *opts));
+ opts++;
+ }
+ return req;
+}
+
+/**
+ * @return show command with every option
+ */
+TR_MON_REQ *show_all_options()
+{
+ TR_MON_OPT_TYPE opts[] = {
+ OPT_TYPE_SHOW_SERIAL,
+ OPT_TYPE_SHOW_VERSION,
+ OPT_TYPE_SHOW_UPTIME,
+ OPT_TYPE_SHOW_TID_REQ_COUNT,
+ OPT_TYPE_SHOW_TID_REQ_PENDING,
+ OPT_TYPE_SHOW_ROUTES,
+ OPT_TYPE_SHOW_COMMUNITIES,
+ OPT_TYPE_UNKNOWN // terminator
+ };
+
+ return show_options(opts);
+}
+
+char *read_file(const char *filename)
+{
+ FILE *f = fopen(filename, "r");
+ char *s = NULL;
+ size_t nn = 0;
+ ssize_t n = getline(&s, &nn, f);
+
+ fclose(f);
+
+ if( (n > 0) && (s[n-1] == '\n'))
+ s[n-1] = 0;
+
+ return s;
+}
+
+int equal(TR_MON_REQ *r1, TR_MON_REQ *r2)
+{
+ size_t ii;
+
+ if (r1->command != r2->command)
+ return 0;
+
+ if (tr_mon_req_opt_count(r1) != tr_mon_req_opt_count(r2))
+ return 0;
+
+ for (ii=0; ii < tr_mon_req_opt_count(r1); ii++) {
+ if (tr_mon_req_opt_index(r1, ii)->type != tr_mon_req_opt_index(r2, ii)->type)
+ return 0;
+ }
+
+ return 1;
+}
+
+int run_test(const char *filename, TR_MON_REQ *(generator)())
+{
+ TR_MON_REQ *req = NULL;
+ TR_MON_REQ *expected = NULL;
+ char *req_json_str = NULL;
+
+ expected = generator();
+ assert(expected);
+
+ req_json_str = read_file(filename);
+ assert(req_json_str);
+
+ req = tr_mon_req_decode(NULL, req_json_str);
+ assert(req);
+ assert(equal(req, expected));
+
+ free(req_json_str);
+ tr_mon_req_free(req);
+ tr_mon_req_free(expected);
+
+ return 1;
+}
+
+int main(void)
+{
+
+ // Test reconfigure command
+ assert(run_test("req_reconfigure.test", reconfigure));
+
+ // Test show command with no options
+ assert(run_test("req_show_no_options.test", show_plain));
+
+ // Test show command with all the options
+ assert(run_test("req_show_all_options.test", show_all_options));
+
+ return 0;
+}
\ No newline at end of file
--- /dev/null
+//
+// Created by jlr on 4/9/18.
+//
+
+#include <talloc.h>
+#include <jansson.h>
+#include <assert.h>
+#include <string.h>
+#include <glib.h>
+
+#include "../tr_mon_req.h"
+
+#define JSON_DUMP_OPTS 0
+
+char *reconfigure()
+{
+ TR_MON_REQ *req = tr_mon_req_new(NULL, MON_CMD_RECONFIGURE);
+ json_t *req_json = tr_mon_req_encode(req);
+ char *result = json_dumps(req_json, JSON_DUMP_OPTS);
+ assert(req);
+ assert(req_json);
+ assert(result);
+ json_decref(req_json);
+ tr_mon_req_free(req);
+ return result;
+}
+
+char *show_plain()
+{
+ TR_MON_REQ *req = tr_mon_req_new(NULL, MON_CMD_SHOW);
+ json_t *req_json = tr_mon_req_encode(req);
+ char *result = json_dumps(req_json, JSON_DUMP_OPTS);
+ assert(req);
+ assert(req_json);
+ assert(result);
+ json_decref(req_json);
+ tr_mon_req_free(req);
+ return result;
+}
+
+char *show_options(const TR_MON_OPT_TYPE *opts)
+{
+ TR_MON_REQ *req = tr_mon_req_new(NULL, MON_CMD_SHOW);
+ json_t *req_json = NULL;
+ char *result = NULL;
+
+ assert(req);
+
+ while (*opts != OPT_TYPE_UNKNOWN) {
+ assert(TR_MON_SUCCESS == tr_mon_req_add_option(req, *opts));
+ opts++;
+ }
+
+ req_json = tr_mon_req_encode(req);
+ assert(req_json);
+
+ result = json_dumps(req_json, JSON_DUMP_OPTS);
+ assert(result);
+
+ json_decref(req_json);
+ tr_mon_req_free(req);
+ return result;
+}
+
+char *read_file(const char *filename)
+{
+ FILE *f = fopen(filename, "r");
+ char *s = NULL;
+ size_t nn = 0;
+ ssize_t n = getline(&s, &nn, f);
+ fclose(f);
+
+ if( (n > 0) && (s[n-1] == '\n'))
+ s[n-1] = 0;
+
+ return s;
+}
+int main(void)
+{
+ char *s = NULL;
+ TR_MON_OPT_TYPE opts[10];
+ char *expected = NULL;
+
+ // Test reconfigure command
+ s = reconfigure();
+ expected = read_file("req_reconfigure.test");
+ assert(expected);
+ assert(strcmp(expected, s) == 0);
+ free(s);
+ free(expected);
+
+ // Test show without options
+ s = show_plain();
+ expected = read_file("req_show_no_options.test");
+ assert(expected);
+ assert(strcmp(expected, s) == 0);
+ free(s);
+ free(expected);
+
+ // Test show with empty options (this mostly tests the test)
+ opts[0] = OPT_TYPE_UNKNOWN;
+ s = show_options(opts);
+ expected = read_file("req_show_no_options.test");
+ assert(expected);
+ assert(strcmp(expected, s) == 0);
+ free(s);
+ free(expected);
+
+ // Test show with many options
+ opts[0] = OPT_TYPE_SHOW_SERIAL;
+ opts[1] = OPT_TYPE_SHOW_VERSION;
+ opts[2] = OPT_TYPE_SHOW_UPTIME;
+ opts[3] = OPT_TYPE_SHOW_TID_REQ_COUNT;
+ opts[4] = OPT_TYPE_SHOW_TID_REQ_PENDING;
+ opts[5] = OPT_TYPE_SHOW_ROUTES;
+ opts[6] = OPT_TYPE_SHOW_COMMUNITIES;
+ opts[7] = OPT_TYPE_UNKNOWN;
+ s = show_options(opts);
+ expected = read_file("req_show_all_options.test");
+ assert(expected);
+ assert(strcmp(expected, s) == 0);
+ free(s);
+ free(expected);
+
+ return 0;
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2018, JANET(UK)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of JANET(UK) nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#include <talloc.h>
+#include <gmodule.h>
+#include <string.h>
+
+#include "tr_mon_req.h"
+
+// Monitoring request message common code
+
+/**
+ * Destructor used by talloc to ensure proper cleanup
+ */
+static int tr_mon_req_destructor(void *object)
+{
+ TR_MON_REQ *req = talloc_get_type_abort(object, TR_MON_REQ);
+ if (req->options) {
+ g_array_unref(req->options);
+ }
+ return 0;
+}
+
+/**
+ * Allocate a new monitoring request
+ *
+ * @param mem_ctx talloc context for the new request
+ * @param cmd command for the request
+ * @return newly allocated request, or null on error
+ */
+TR_MON_REQ *tr_mon_req_new(TALLOC_CTX *mem_ctx, TR_MON_CMD cmd)
+{
+ TR_MON_REQ *req=talloc(mem_ctx, TR_MON_REQ);
+ if (req) {
+ req->command = cmd;
+ req->options = g_array_new(FALSE, FALSE, sizeof(TR_MON_OPT));
+ talloc_set_destructor((void *)req, tr_mon_req_destructor);
+ }
+ return req;
+}
+
+/**
+ * Free a monitoring request
+ *
+ * @param req request to free, must not be null
+ */
+void tr_mon_req_free(TR_MON_REQ *req)
+{
+ talloc_free(req);
+}
+
+/**
+ * Add an option to a TR_MON_REQ
+ * @param req request to operate on, not null
+ * @param opt_type type of option
+ * @return TR_MON_SUCCESS on success, error code on error
+ */
+TR_MON_RC tr_mon_req_add_option(TR_MON_REQ *req, TR_MON_OPT_TYPE opt_type)
+{
+ TR_MON_OPT new_opt; // not a pointer
+
+ /* Validate parameters */
+ if ((req == NULL) || (opt_type == OPT_TYPE_UNKNOWN)) {
+ return TR_MON_BADARG;
+ }
+
+ new_opt.type = opt_type;
+
+ /* Add the new option to the list */
+ g_array_append_val(req->options, new_opt);
+ return TR_MON_SUCCESS;
+}
+
+size_t tr_mon_req_opt_count(TR_MON_REQ *req)
+{
+ return req->options->len;
+}
+
+TR_MON_OPT *tr_mon_req_opt_index(TR_MON_REQ *req, size_t index)
+{
+ TR_MON_OPT *result = (TR_MON_OPT *) &g_array_index(req->options, TR_MON_OPT, index);
+ return result;
+}
+
+/**
+ * This method defines the command strings
+ */
+const char *cmd_to_string(TR_MON_CMD cmd)
+{
+ switch(cmd) {
+ case MON_CMD_UNKNOWN:
+ return NULL;
+
+ case MON_CMD_RECONFIGURE:
+ return "reconfigure";
+
+ case MON_CMD_SHOW:
+ return "show";
+ }
+}
+
+// Helper macro for the cmd_from_string method
+#define return_if_matches(s, cmd) \
+ do { \
+ if (strcmp((s), cmd_to_string(cmd))==0) \
+ return (cmd); \
+ } while(0)
+
+TR_MON_CMD cmd_from_string(const char *s)
+{
+ return_if_matches(s, MON_CMD_RECONFIGURE);
+ return_if_matches(s, MON_CMD_SHOW);
+ return MON_CMD_UNKNOWN;
+}
+#undef return_if_matches
+
+/**
+ * This method defines the option type strings
+ */
+const char *opt_type_to_string(TR_MON_OPT_TYPE opt_type)
+{
+ switch(opt_type) {
+ case OPT_TYPE_UNKNOWN:
+ return NULL;
+
+ case OPT_TYPE_SHOW_VERSION:
+ return "version";
+
+ case OPT_TYPE_SHOW_SERIAL:
+ return "serial";
+
+ case OPT_TYPE_SHOW_UPTIME:
+ return "uptime";
+
+ case OPT_TYPE_SHOW_TID_REQ_COUNT:
+ return "tid_req_count";
+
+ case OPT_TYPE_SHOW_TID_REQ_PENDING:
+ return "tid_req_pending";
+
+ case OPT_TYPE_SHOW_ROUTES:
+ return "routes";
+
+ case OPT_TYPE_SHOW_COMMUNITIES:
+ return "communities";
+ }
+}
+
+// Helper macro for the opt_type_from_string method
+#define return_if_matches(s, cmd) \
+ do { \
+ if (strcmp((s), opt_type_to_string(cmd))==0) \
+ return (cmd); \
+ } while(0)
+
+TR_MON_OPT_TYPE opt_type_from_string(const char *s)
+{
+ return_if_matches(s, OPT_TYPE_SHOW_VERSION);
+ return_if_matches(s, OPT_TYPE_SHOW_SERIAL);
+ return_if_matches(s, OPT_TYPE_SHOW_UPTIME);
+ return_if_matches(s, OPT_TYPE_SHOW_TID_REQ_COUNT);
+ return_if_matches(s, OPT_TYPE_SHOW_TID_REQ_PENDING);
+ return_if_matches(s, OPT_TYPE_SHOW_ROUTES);
+ return_if_matches(s, OPT_TYPE_SHOW_COMMUNITIES);
+ return OPT_TYPE_UNKNOWN;
+}
+#undef return_if_matches
--- /dev/null
+/*
+ * Copyright (c) 2018, JANET(UK)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of JANET(UK) nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#ifndef TRUST_ROUTER_TR_MON_REQ_H
+#define TRUST_ROUTER_TR_MON_REQ_H
+
+#include <talloc.h>
+#include <jansson.h>
+#include <gmodule.h>
+
+/* Typedefs */
+typedef struct tr_mon_req TR_MON_REQ;
+
+typedef enum tr_mon_cmd TR_MON_CMD;
+
+typedef struct tr_mon_opt TR_MON_OPT;
+typedef enum tr_mon_opt_type TR_MON_OPT_TYPE;
+
+typedef enum tr_mon_rc TR_MON_RC;
+
+
+/* Struct and enum definitions */
+enum tr_mon_rc {
+ TR_MON_SUCCESS=0,
+ TR_MON_ERROR, /* generic error */
+ TR_MON_BADARG, /* problem with the arguments */
+ TR_MON_NOMEM, /* out of memory */
+ TR_MON_NOPARSE, /* parsing failed */
+};
+
+enum tr_mon_cmd {
+ MON_CMD_UNKNOWN=0,
+ MON_CMD_RECONFIGURE,
+ MON_CMD_SHOW
+};
+
+enum tr_mon_opt_type {
+ OPT_TYPE_UNKNOWN=0,
+
+ // System information
+ OPT_TYPE_SHOW_VERSION,
+ OPT_TYPE_SHOW_SERIAL,
+
+ // System statistics
+ OPT_TYPE_SHOW_UPTIME,
+ OPT_TYPE_SHOW_TID_REQ_COUNT,
+ OPT_TYPE_SHOW_TID_REQ_PENDING,
+
+ // Dynamic trust router state
+ OPT_TYPE_SHOW_ROUTES,
+ OPT_TYPE_SHOW_COMMUNITIES
+};
+
+struct tr_mon_opt {
+ TR_MON_OPT_TYPE type;
+};
+
+struct tr_mon_req {
+ TR_MON_CMD command;
+ GArray *options;
+};
+
+/* Prototypes */
+TR_MON_REQ *tr_mon_req_new(TALLOC_CTX *mem_ctx, TR_MON_CMD cmd);
+void tr_mon_req_free(TR_MON_REQ *req);
+TR_MON_RC tr_mon_req_add_option(TR_MON_REQ *req, TR_MON_OPT_TYPE opt_type);
+size_t tr_mon_req_opt_count(TR_MON_REQ *req);
+TR_MON_OPT *tr_mon_req_opt_index(TR_MON_REQ *req, size_t index);
+
+const char *cmd_to_string(TR_MON_CMD cmd);
+TR_MON_CMD cmd_from_string(const char *s);
+
+const char *opt_type_to_string(TR_MON_OPT_TYPE opt_type);
+TR_MON_OPT_TYPE opt_type_from_string(const char *s);
+
+/* tr_mon_req_encode.c */
+json_t *tr_mon_req_encode(TR_MON_REQ *req);
+
+/* tr_mon_req_decode.c */
+TR_MON_REQ *tr_mon_req_decode(TALLOC_CTX *mem_ctx, const char *req_json);
+
+/* tr_mon_rec_decode.c */
+
+#endif //TRUST_ROUTER_TR_MON_REQ_H
--- /dev/null
+#include "tr_mon_req.h"/*
+ * Copyright (c) 2018, JANET(UK)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of JANET(UK) nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#include <talloc.h>
+#include <jansson.h>
+
+#include "tr_mon_req.h"
+
+// Monitoring request decoders
+
+/**
+ * Decode a single option
+ *
+ * Format:
+ * { "type": "some_tpye" }
+ *
+ * @param opt_json JSON object reference
+ * @param dest allocated memory for the result
+ * @return TR_MON_SUCCESS on success, error on error
+ */
+static TR_MON_RC tr_mon_decode_one_opt(json_t *opt_json, TR_MON_OPT *dest)
+{
+ json_t *jstr = NULL;
+ TR_MON_OPT_TYPE opt_type = OPT_TYPE_UNKNOWN;
+
+ if ( (opt_json == NULL) || (dest == NULL))
+ return TR_MON_BADARG;
+
+ if (! json_is_object(opt_json))
+ return TR_MON_NOPARSE;
+
+ jstr = json_object_get(opt_json, "type");
+ if ( (jstr == NULL) || (! json_is_string(jstr)) )
+ return TR_MON_NOPARSE;
+
+ opt_type = opt_type_from_string(json_string_value(jstr));
+ if (opt_type == OPT_TYPE_UNKNOWN)
+ return TR_MON_NOPARSE;
+
+ dest->type = opt_type;
+ return TR_MON_SUCCESS;
+}
+
+/**
+ * Decode options array
+ *
+ * Format:
+ * [{option}, {option}, ...]
+ *
+ */
+static TR_MON_RC tr_mon_options_decode(json_t *opts_json, TR_MON_REQ *req)
+{
+ TR_MON_OPT opt; // not a pointer
+ size_t n_opts=0;
+ size_t ii=0;
+
+ if ( (opts_json == NULL) || (req == NULL))
+ return TR_MON_BADARG;
+
+ if (! json_is_array(opts_json))
+ return TR_MON_NOPARSE;
+
+ n_opts = json_array_size(opts_json);
+ for (ii=0; ii < n_opts; ii++) {
+ if (tr_mon_decode_one_opt(json_array_get(opts_json, ii),
+ &opt) != TR_MON_SUCCESS) {
+ return TR_MON_NOPARSE;
+ }
+ tr_mon_req_add_option(req, opt.type);
+ }
+ return TR_MON_SUCCESS;
+}
+
+/**
+ * Parse JSON for a request
+ */
+static json_t *tr_mon_req_parse(const char *input)
+{
+ json_t *parsed_json = NULL;
+ json_error_t json_error;
+
+ parsed_json = json_loads(input, JSON_REJECT_DUPLICATES, &json_error);
+ return parsed_json;
+}
+
+/**
+ * Decode a JSON request
+ *
+ * Expected format:
+ * {
+ * "command": "some_command_name",
+ * "options": [{option1}, ...]
+ * }
+ *
+ * (options are optional)
+ *
+ * Caller must free the return value with tr_mon_req_free().
+ *
+ * @param mem_ctx talloc context for the returned struct
+ * @param req_json reference to JSON request object
+ * @return decoded request struct or NULL on failure
+ */
+TR_MON_REQ *tr_mon_req_decode(TALLOC_CTX *mem_ctx, const char *req_str)
+{
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+ TR_MON_REQ *req = NULL;
+ json_t *req_json = NULL;
+ json_t *jval = NULL;
+ json_t *opts_json = NULL;
+ TR_MON_CMD cmd = MON_CMD_UNKNOWN;
+
+ req_json = tr_mon_req_parse(req_str); // TODO: Check errors
+
+ if (! json_is_object(req_json))
+ goto cleanup;
+
+ // Get the command and verify that it is a string value
+ jval = json_object_get(req_json, "command");
+ if (! json_is_string(jval))
+ goto cleanup;
+
+ cmd = cmd_from_string(json_string_value(jval));
+ if (cmd == MON_CMD_UNKNOWN)
+ goto cleanup;
+
+ /* Command is good. Allocate the request in the tmp context */
+ req = tr_mon_req_new(tmp_ctx, cmd);
+ if (req == NULL)
+ goto cleanup;
+
+ /* Parse options if we have any */
+ opts_json = json_object_get(req_json, "options");
+ if (opts_json) {
+ if (tr_mon_options_decode(opts_json, req) != TR_MON_SUCCESS) {
+ req = NULL; // memory still in tmp_ctx, so it will be cleaned up
+ goto cleanup;
+ }
+ }
+
+ /* Success! Put the request in the caller's talloc context */
+ talloc_steal(mem_ctx, req);
+
+cleanup:
+ talloc_free(tmp_ctx);
+ if (req_json)
+ json_decref(req_json);
+
+ return req;
+}
--- /dev/null
+/*
+ * Copyright (c) 2018, JANET(UK)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of JANET(UK) nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#include <talloc.h>
+#include <jansson.h>
+#include <glib.h>
+
+#include "tr_mon_req.h"
+
+// Monitoring request encoders
+
+/**
+ * Encode options array as a JSON array
+ *
+ * Format:
+ * [
+ * { "type": "first_type" },
+ * { "type": "second_type"},
+ * ...
+ * ]
+ *
+ * @param opts array of options
+ * @return reference to a JSON array of options
+ */
+static json_t *tr_mon_opts_decode(GArray *opts)
+{
+ json_t *array_json = json_array(); // the array of options
+ json_t *opt_json = NULL; // individual option JSON object
+ json_t *type_json = NULL;
+ guint ii = 0;
+ TR_MON_OPT this_opt;
+
+ if (array_json == NULL)
+ return NULL; // failed
+
+ /* Iterate over the options */
+ for (ii=0; ii < opts->len; ii++) {
+ this_opt = g_array_index(opts, TR_MON_OPT, ii);
+
+ /* Create the JSON object for this option */
+ opt_json = json_object();
+ if (opt_json == NULL) {
+ json_decref(array_json);
+ return NULL;
+ }
+
+ /* Add to the array, making opt_json a borrowed ref if we succeed */
+ if (json_array_append_new(array_json, opt_json) == -1) {
+ json_decref(array_json);
+ json_decref(opt_json); // handle ourselves because the set failed
+ }
+
+ /* Create the type string for this option */
+ type_json = json_string(opt_type_to_string(this_opt.type));
+ if (type_json == NULL) {
+ json_decref(array_json);
+ return NULL;
+ }
+
+ /* Add the type string to the JSON object, making type_json a borrowed ref */
+ if (json_object_set_new(opt_json, "type", type_json) == -1) {
+ json_decref(array_json);
+ json_decref(type_json); // must handle ourselves because the set failed
+ return NULL;
+ }
+ }
+
+ return array_json;
+}
+
+/**
+ * Encode a request as a JSON object
+ *
+ * Caller must free the return value using json_decref()
+ *
+ * Format:
+ * {
+ * "command": "some_command",
+ * "options": [...see tr_mon_opts_to_json()...]
+ * }
+ *
+ * @param req request to encode
+ * @return reference to a JSON object
+ */
+json_t *tr_mon_req_encode(TR_MON_REQ *req)
+{
+ json_t *req_json = NULL;
+ json_t *cmd_json = NULL;
+ json_t *opts_json = NULL;
+
+ /* Allocate the base JSON object */
+ req_json = json_object();
+ if (req_json == NULL)
+ return NULL;
+
+ /* Allocate the JSON string for the command */
+ cmd_json = json_string(cmd_to_string(req->command));
+ if (cmd_json == NULL) {
+ json_decref(req_json);
+ return NULL;
+ }
+
+ /* Add the command string to the base object. Steals the reference to
+ * the string if successful. */
+ if (json_object_set_new(req_json, "command", cmd_json) == -1) {
+ json_decref(cmd_json); // must clean this up ourselves because the set failed
+ json_decref(req_json);
+ return NULL;
+ }
+
+ /* If we have options, add them to the object */
+ if (req->options->len > 0) {
+ opts_json = tr_mon_opts_decode(req->options);
+ if (opts_json == NULL) {
+ json_decref(req_json);
+ return NULL;
+ }
+
+ if (json_object_set_new(req_json, "options", opts_json) == -1) {
+ json_decref(req_json);
+ json_decref(opts_json); // must clean this up ourselves because set failed
+ return NULL;
+ }
+ }
+
+ /* That's it, we succeeded */
+ return req_json;
+}
+