Report whether TID requests succeed and better clean up zombie TID / MON processes (pull request 4)
include_directories(include)
+# Fill these in so CLion doesn't complain. The real versions are set in the Makefile
+add_definitions(-DPACKAGE_VERSION="built-with-cmake"
+ -DPACKAGE_NAME="Moonshot Trust Router"
+ -DPACKAGE_BUGREPORT="bugs@painless-security.com")
+
set(SOURCE_FILES
common/tests/cfg_test.c
common/tests/commtest.c
trp/trp_upd.c
trp/trpc.c
trp/trps.c include/tr_name_internal.h mon/mon_req.c mon/mon_req_encode.c mon/mon_req_decode.c
- mon/mon_resp.c mon/mon_common.c mon/mon_resp_encode.c mon/mon_resp_decode.c tr/tr_mon.c mon/mons.c include/tr_socket.h common/tr_gss.c include/tr_gss.h common/tr_config_internal.c mon/mons_handlers.c include/mons_handlers.h)
+ mon/mon_resp.c mon/mon_common.c mon/mon_resp_encode.c mon/mon_resp_decode.c tr/tr_mon.c mon/mons.c include/tr_socket.h common/tr_gss.c include/tr_gss.h common/tr_config_internal.c mon/mons_handlers.c include/mons_handlers.h tr/tr_tid_mons.c)
# Does not actually build!
add_executable(trust_router ${SOURCE_FILES})
tr/tr_event.c \
tr/tr_cfgwatch.c \
tr/tr_tid.c \
+tr/tr_tid_mons.c \
tr/tr_trp.c \
tr/tr_mon.c \
common/tr_gss.c \
// System statistics
OPT_TYPE_SHOW_UPTIME,
OPT_TYPE_SHOW_TID_REQ_COUNT,
+ OPT_TYPE_SHOW_TID_REQ_ERR_COUNT,
OPT_TYPE_SHOW_TID_REQ_PENDING,
// Dynamic trust router state
MONS_AUTH_FUNC *auth_handler;
void *cookie;
GPtrArray *handlers;
+ GArray *pids; /* PIDs of active mons processes */
};
/* Client instance */
#ifndef TRUST_ROUTER_MONS_HANDLERS_H
#define TRUST_ROUTER_MONS_HANDLERS_H
-typedef json_t *(MONS_HANDLER_FUNC)(void *);
+typedef MON_RC (MONS_HANDLER_FUNC)(void *cookie, json_t **result_ptr);
struct mons_dispatch_table_entry {
MON_CMD command;
TR_GSSC_INSTANCE *gssc;
};
+struct tid_process {
+ pid_t pid;
+ int read_fd;
+};
+
struct tids_instance {
int req_count;
+ int error_count;
char *priv_key;
char *ipaddr;
const char *hostname;
void *cookie;
unsigned int tids_port;
TR_NAME *gss_name; /* GSS name client used for authentication */
+ GArray *pids; /* PIDs of active tids processes */
};
/** Decrement a reference to #json when this tid_req is cleaned up. A
void tid_resp_set_cons(TID_RESP *resp, TR_CONSTRAINT_SET *cons);
void tid_resp_set_error_path(TID_RESP *resp, json_t *ep);
+void tids_sweep_procs(TIDS_INSTANCE *tids);
+
#endif
#include <trp_internal.h>
#include <tr_event.h>
#include <tr_config.h>
+#include <mon.h>
#define TR_TID_MAX_AAA_SERVERS 10
-int tr_tids_event_init(struct event_base *base,
- TIDS_INSTANCE *tids,
- TR_CFG_MGR *cfg_mgr,
- TRPS_INSTANCE *trps,
- struct tr_socket_event *tids_ev);
+int tr_tids_event_init(struct event_base *base, TIDS_INSTANCE *tids, TR_CFG_MGR *cfg_mgr, TRPS_INSTANCE *trps,
+ struct tr_socket_event *tids_ev, struct event **sweep_ev);
+
+/* tr_tid_mons.c */
+void tr_tid_register_mons_handlers(TIDS_INSTANCE *tids, MONS_INSTANCE *mons);
#endif /* TR_TID_H */
case OPT_TYPE_SHOW_TID_REQ_COUNT:
return "tid_req_count";
+ case OPT_TYPE_SHOW_TID_REQ_ERR_COUNT:
+ return "tid_req_error_count";
+
case OPT_TYPE_SHOW_TID_REQ_PENDING:
return "tid_req_pending";
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_ERR_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);
#include "mons_handlers.h"
+static void mons_sweep_procs(MONS_INSTANCE *mons);
+
static int mons_destructor(void *object)
{
MONS_INSTANCE *mons = talloc_get_type_abort(object, MONS_INSTANCE);
- if (mons->handlers) {
+ if (mons->handlers)
g_ptr_array_unref(mons->handlers);
- }
+
+ if (mons->pids)
+ g_array_unref(mons->pids);
+
return 0;
}
talloc_free(mons);
return NULL;
}
+
+ mons->pids = g_array_new(FALSE, FALSE, sizeof(pid_t));
+ if (mons->pids == NULL) {
+ talloc_free(mons);
+ return NULL;
+ }
}
return mons;
}
);
close(conn);
exit(0); /* exit to kill forked child process */
- } else {
- close(conn);
}
+ /* Only the parent process gets here */
+ close(conn);
+ g_array_append_val(mons->pids, pid);
+
/* clean up any processes that have completed */
- while (waitpid(-1, 0, WNOHANG) > 0);
+ mons_sweep_procs(mons);
return 0;
}
+
+void mons_sweep_procs(MONS_INSTANCE *mons)
+{
+ guint ii;
+ pid_t pid;
+ int status;
+
+ /* loop backwards over the array so we can remove elements as we go */
+ for (ii=mons->pids->len; ii > 0; ii--) {
+ /* ii-1 is the current index */
+ pid = g_array_index(mons->pids, pid_t, ii-1);
+ if (waitpid(pid, &status, WNOHANG) > 0) {
+ /* the process exited */
+ tr_debug("mons_sweep_procs: monitoring process %d terminated.", pid);
+
+ g_array_remove_index_fast(mons->pids, ii-1); /* disturbs only indices >= ii-1 which we've already handled */
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) == 0)
+ tr_debug("mons_sweep_procs: monitoring process %d succeeded.", pid);
+ else
+ tr_debug("mons_sweep_procs: monitoring process %d exited with status %d.", pid, WTERMSIG(status));
+ } else if (WIFSIGNALED(status)) {
+ tr_debug("mons_sweep_procs: monitoring process %d terminated by signal %d.", pid, WTERMSIG(status));
+ }
+ }
+ }
+}
struct request_helper_data {
MON_CMD command;
MON_OPT_TYPE opt_type;
- json_t *payload;
+ json_t *payload; /* json object to add responses to */
+ GArray *results;
+};
+
+struct handler_result {
+ MON_OPT_TYPE opt_type; /* what opt type set this? */
+ MON_RC rc; /* what was its result code? */
+ json_t *json_data; /* what data, if any, is it returning? */
};
/**
{
MON_RESP *resp = NULL;
json_t *payload = NULL;
- struct request_helper_data cookie;
+ struct request_helper_data cookie = {0};
size_t ii = 0;
tr_debug("mons_handle_request: Handling a request");
/* Start off by allocating our response with a generic error message */
resp = mon_resp_new(mem_ctx,
MON_RESP_ERROR,
- "Error processing show request",
+ "error processing request",
NULL);
if (resp == NULL) {
/* we can't respond, just return */
/* Now call handlers */
cookie.command = req->command;
- cookie.payload = payload; /* borrowed reference */
+ cookie.results = g_array_new(FALSE, TRUE, sizeof(struct handler_result));
if (mon_req_opt_count(req) == 0) {
/* call every handler that matches the command */
}
}
- /* If we get here, then we successfully processed the request. Return a successful reply. */
- if (mon_resp_set_message(resp, "success") == 0) {
- /* Failed to set the response message to success - fail ironically */
- tr_crit("mons_handle_request: Error setting response message to 'success'.");
- goto cleanup;
+ /* We now have an array of results in cookie.results. If any of these failed, return an error. */
+ tr_debug("mons_handle_request: Examining %d handler results", cookie.results->len);
+ resp->code = MON_RESP_SUCCESS; /* tentatively set this to success */
+ for (ii=0; ii < cookie.results->len; ii++) {
+ struct handler_result *this = &g_array_index(cookie.results, struct handler_result, ii);
+ if (this->rc != MON_SUCCESS) {
+ tr_debug("mons_handle_request: Result %d was an error.", ii);
+ resp->code = MON_RESP_ERROR;
+ }
+
+ /* add the JSON response even if there was an error */
+ if (this->json_data) {
+ tr_debug("mons_handle_request: Result %d returned JSON data.", ii);
+ json_object_set_new(payload, mon_opt_type_to_string(this->opt_type), this->json_data);
+ }
+ }
+
+ if (resp->code == MON_RESP_SUCCESS) {
+ if (mon_resp_set_message(resp, "success") == 0) {
+ /* Failed to set the response message to success - fail ironically, don't send
+ * an inconsistent response. */
+ tr_crit("mons_handle_request: Error setting response message to 'success'.");
+ goto cleanup;
+ }
+ } else {
+ /* Failed - send a response indicating that the overall command succeeded */
+ if (mon_resp_set_message(resp, "request processed but an error occurred") == 0) {
+ tr_crit("mons_handle_request: Error setting response message after a handler error.");
+ goto cleanup;
+ }
}
/* Attach the accumulated payload to the response */
- if (json_object_size(payload) > 0)
+ if (json_object_size(payload) > 0) {
+ tr_debug("mons_handle_request: Attaching payload to response.");
mon_resp_set_payload(resp, payload);
+ }
- resp->code = MON_RESP_SUCCESS; /* at last... */
tr_debug("mons_handle_request: Successfully processed request.");
cleanup:
if (payload)
json_decref(payload);
+ if (cookie.results)
+ g_array_free(cookie.results, TRUE);
return resp;
}
{
MONS_DISPATCH_TABLE_ENTRY *entry = talloc_get_type_abort(element, MONS_DISPATCH_TABLE_ENTRY);
struct request_helper_data *helper_data = data;
+ struct handler_result result = {0};
if (dispatch_entry_matches(entry, helper_data->command, helper_data->opt_type)) {
- json_object_set(helper_data->payload,
- mon_opt_type_to_string(entry->opt_type),
- entry->handler(entry->cookie));
+ result.rc = entry->handler(entry->cookie, &(result.json_data));
+ result.opt_type = entry->opt_type;
+ g_array_append_val(helper_data->results, result);
}
}
tr_debug("tids_req_handler: Request received! target_realm = %s, community = %s", req->realm->buf, req->comm->buf);
- if (tids)
- tids->req_count++;
-
if (!(resp) || !resp) {
tr_debug("tids_req_handler: No response structure.");
return -1;
return resp_msg;
}
+static int tids_destructor(void *object)
+{
+ TIDS_INSTANCE *tids = talloc_get_type_abort(object, TIDS_INSTANCE);
+ if (tids->pids)
+ g_array_unref(tids->pids);
+ return 0;
+}
+
TIDS_INSTANCE *tids_new(TALLOC_CTX *mem_ctx)
{
- return talloc_zero(mem_ctx, TIDS_INSTANCE);
+ TIDS_INSTANCE *tids = talloc_zero(mem_ctx, TIDS_INSTANCE);
+ if (tids) {
+ tids->pids = g_array_new(FALSE, FALSE, sizeof(struct tid_process));
+ if (tids->pids == NULL) {
+ talloc_free(tids);
+ return NULL;
+ }
+ talloc_set_destructor((void *)tids, tids_destructor);
+ }
+ return tids;
}
/**
*/
TIDS_INSTANCE *tids_create(void)
{
- return talloc_zero(NULL, TIDS_INSTANCE);
+ return tids_new(NULL);
}
+
/* Get a listener for tids requests, returns its socket fd. Accept
* connections with tids_accept() */
nfds_t tids_get_listener(TIDS_INSTANCE *tids,
{
int conn=-1;
int pid=-1;
+ int pipe_fd[2];
+ struct tid_process tp = {0};
if (0 > (conn = accept(listen, NULL, NULL))) {
perror("Error from TIDS Server accept()");
return 1;
}
+ if (0 > pipe(pipe_fd)) {
+ perror("Error on pipe()");
+ return 1;
+ }
+ /* pipe_fd[0] is for reading, pipe_fd[1] is for writing */
+
if (0 > (pid = fork())) {
perror("Error on fork()");
return 1;
}
if (pid == 0) {
+ close(pipe_fd[0]); /* child only writes */
close(listen);
tr_gss_handle_connection(conn,
"trustidentity", tids->hostname, /* acceptor name */
tids->auth_handler, tids->cookie, /* auth callback and cookie */
tids_req_cb, tids /* req callback and cookie */
);
+ if (write(pipe_fd[1], "OK\0", 3) < 0)
+ tr_crit("tids_accept: child process unable to write to pipe");
+ close(pipe_fd[1]);
close(conn);
exit(0); /* exit to kill forked child process */
- } else {
- close(conn);
}
- /* clean up any processes that have completed (TBD: move to main loop?) */
- while (waitpid(-1, 0, WNOHANG) > 0);
+ /* Only the parent process gets here */
+ close(pipe_fd[1]); /* parent only listens */
+ close(conn); /* connection belongs to the child */
+ tp.pid = pid;
+ tp.read_fd = pipe_fd[0];
+ g_array_append_val(tids->pids, tp); /* remember the PID of our child process */
+ /* clean up any processes that have completed */
+ tids_sweep_procs(tids);
return 0;
}
+/**
+ * Clean up any finished TID request processes
+ *
+ * This is called by the main process after forking each TID request. If you want to be
+ * sure finished processes are cleaned up promptly even during a lull in TID requests,
+ * this can be called from the main thread of the main process. It is not thread-safe,
+ * so should not be used from sub-threads. It should not be called by child processes -
+ * this would probably be harmless but ineffective.
+ *
+ * @param tids
+ */
+void tids_sweep_procs(TIDS_INSTANCE *tids)
+{
+ guint ii;
+ struct tid_process tp = {0};
+ char result[10] = {0};
+ ssize_t result_len;
+ int status;
+ int wait_rc;
+
+ /* loop backwards over the array so we can remove elements as we go */
+ for (ii=tids->pids->len; ii > 0; ii--) {
+ /* ii-1 is the current index - get our own copy, we may destroy the list's copy */
+ tp = g_array_index(tids->pids, struct tid_process, ii-1);
+
+ wait_rc = waitpid(tp.pid, &status, WNOHANG);
+ if (wait_rc == 0)
+ continue; /* process still running */
+
+ if (wait_rc < 0) {
+ /* invalid options will probably keep being invalid, report that condition */
+ if(errno == EINVAL)
+ tr_crit("tids_sweep_procs: waitpid called with invalid options");
+
+ /* If we got ECHILD, that means the PID was invalid; we'll assume the process was
+ * terminated and we missed it. For all other errors, move on
+ * to the next PID to check. */
+ if (errno != ECHILD)
+ continue;
+
+ tr_warning("tid_sweep_procs: TID process %d disappeared", tp.pid);
+ }
+
+ /* remove the item (we still have a copy of the data) */
+ g_array_remove_index_fast(tids->pids, ii-1); /* disturbs only indices >= ii-1 which we've already handled */
+
+ /* Report exit status unless we got ECHILD above or somehow waitpid returned the wrong pid */
+ if (wait_rc == tp.pid) {
+ if (WIFEXITED(status)) {
+ tr_debug("tids_sweep_procs: TID process %d exited with status %d.", tp.pid, WTERMSIG(status));
+ } else if (WIFSIGNALED(status)) {
+ tr_debug("tids_sweep_procs: TID process %d terminated by signal %d.", tp.pid, WTERMSIG(status));
+ }
+ } else if (wait_rc > 0) {
+ tr_err("tids_sweep_procs: waitpid returned pid %d, expected %d", wait_rc, tp.pid);
+ }
+
+ /* read the pipe - if the TID request worked, it will have written status before terminating */
+ result_len = read(tp.read_fd, result, sizeof(result)/sizeof(result[0]));
+ close(tp.read_fd);
+
+ if ((result_len > 0) && (strcmp(result, "OK") == 0)) {
+ tids->req_count++;
+ tr_debug("tids_sweep_procs: TID process %d succeeded", tp.pid);
+ } else {
+ tids->error_count++;
+ tr_debug("tids_sweep_procs: TID process %d failed", tp.pid);
+ }
+ }
+}
+
/* Process tids requests forever. Should not return except on error. */
int tids_start (TIDS_INSTANCE *tids,
TIDS_REQ_FUNC *req_handler,
}
/* TODO move this function */
-static json_t *tr_mon_handle_version(void *cookie)
+static MON_RC tr_mon_handle_version(void *cookie, json_t **result_ptr)
{
- return json_string(PACKAGE_VERSION);
+ *result_ptr = json_string(PACKAGE_VERSION);
+ return (*result_ptr == NULL) ? MON_NOMEM : MON_SUCCESS;
}
-static json_t *tr_mon_handle_uptime(void *cookie)
+static MON_RC tr_mon_handle_uptime(void *cookie, json_t **result_ptr)
{
time_t *start_time = cookie;
- return json_integer(time(NULL) - (*start_time));
+ *result_ptr = json_integer(time(NULL) - (*start_time));
+ return (*result_ptr == NULL) ? MON_NOMEM : MON_SUCCESS;
}
int main(int argc, char *argv[])
struct cmdline_args opts;
struct event_base *ev_base;
struct tr_socket_event tids_ev = {0};
+ struct event *tids_sweep_ev;
struct tr_socket_event mon_ev = {0};
struct event *cfgwatch_ev;
/* TODO do this more systematically */
mons_register_handler(tr->mons, MON_CMD_SHOW, OPT_TYPE_SHOW_VERSION, tr_mon_handle_version, NULL);
mons_register_handler(tr->mons, MON_CMD_SHOW, OPT_TYPE_SHOW_UPTIME, tr_mon_handle_uptime, &start_time);
-
+ tr_tid_register_mons_handlers(tr->tids, tr->mons);
+
/***** process configuration *****/
tr->cfgwatch=tr_cfgwatch_create(tr);
if (tr->cfgwatch == NULL) {
/* install TID server events */
tr_debug("Initializing TID server events.");
- if (0 != tr_tids_event_init(ev_base,
- tr->tids,
- tr->cfg_mgr,
- tr->trps,
- &tids_ev)) {
+ if (0 != tr_tids_event_init(ev_base, tr->tids, tr->cfg_mgr, tr->trps, &tids_ev, &tids_sweep_ev)) {
tr_crit("Error initializing Trust Path Query Server instance.");
return 1;
}
tr_debug("tr_tids_req_handler: Request received (conn = %d)! Realm = %s, Comm = %s", orig_req->conn,
orig_req->realm->buf, orig_req->comm->buf);
- tids->req_count++;
/* Duplicate the request, so we can modify and forward it */
if (NULL == (fwd_req=tid_dup_req(orig_req))) {
/* called when a connection to the TIDS port is received */
static void tr_tids_event_cb(int listener, short event, void *arg)
{
- TIDS_INSTANCE *tids = (TIDS_INSTANCE *)arg;
+ TIDS_INSTANCE *tids = talloc_get_type_abort(arg, TIDS_INSTANCE);
if (0==(event & EV_READ))
tr_debug("tr_tids_event_cb: unexpected event on TIDS socket (event=0x%X)", event);
tids_accept(tids, listener);
}
-/* Configure the tids instance and set up its event handler.
+/* called when it's time to sweep for completed TID child processes */
+static void tr_tids_sweep_cb(int listener, short event, void *arg)
+{
+ TIDS_INSTANCE *tids = talloc_get_type_abort(arg, TIDS_INSTANCE);
+
+ if (0==(event & EV_TIMEOUT))
+ tr_debug("tr_tids_event_cb: unexpected event on TID process sweep timer (event=0x%X)", event);
+ else
+ tids_sweep_procs(tids);
+}
+
+/* Configure the tids instance and set up its event handlers.
* Returns 0 on success, nonzero on failure. Fills in
* *tids_event (which should be allocated by caller). */
-int tr_tids_event_init(struct event_base *base,
- TIDS_INSTANCE *tids,
- TR_CFG_MGR *cfg_mgr,
- TRPS_INSTANCE *trps,
- struct tr_socket_event *tids_ev)
+int tr_tids_event_init(struct event_base *base, TIDS_INSTANCE *tids, TR_CFG_MGR *cfg_mgr, TRPS_INSTANCE *trps,
+ struct tr_socket_event *tids_ev, struct event **sweep_ev)
{
TALLOC_CTX *tmp_ctx=talloc_new(NULL);
struct tr_tids_event_cookie *cookie=NULL;
+ struct timeval sweep_interval;
int retval=0;
int ii=0;
goto cleanup;
}
+ if (sweep_ev == NULL) {
+ tr_debug("tr_tids_event_init: Null sweep_ev.");
+ retval = 1;
+ goto cleanup;
+ }
+
/* Create the cookie for callbacks. We'll put it in the tids context, so it will
* be cleaned up when tids is freed by talloc_free. */
cookie=talloc(tmp_ctx, struct tr_tids_event_cookie);
goto cleanup;
}
- /* Set up events */
+ /* Set up listener events */
for (ii=0; ii<tids_ev->n_sock_fd; ii++) {
tids_ev->ev[ii]=event_new(base,
tids_ev->sock_fd[ii],
event_add(tids_ev->ev[ii], NULL);
}
+ /* Set up a periodic check for completed TID handler processes */
+ *sweep_ev = event_new(base, -1, EV_TIMEOUT|EV_PERSIST, tr_tids_sweep_cb, tids);
+ sweep_interval.tv_sec = 10;
+ sweep_interval.tv_usec = 0;
+ event_add(*sweep_ev, &sweep_interval);
+
cleanup:
talloc_free(tmp_ctx);
return retval;
--- /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.
+ *
+ */
+
+/* Monitoring handlers for trust router TID server */
+
+#include <jansson.h>
+#include <tid_internal.h>
+#include <tr_tid.h>
+#include <mon_internal.h>
+#include <mons_handlers.h>
+
+/**
+ * Get the count of completed TID requests
+ */
+static MON_RC handle_show_req_count(void *cookie, json_t **response_ptr)
+{
+ TIDS_INSTANCE *tids = talloc_get_type_abort(cookie, TIDS_INSTANCE);
+ *response_ptr = json_integer(tids->req_count);
+ return (*response_ptr == NULL) ? MON_NOMEM : MON_SUCCESS;
+}
+
+static MON_RC handle_show_req_err_count(void *cookie, json_t **response_ptr)
+{
+ TIDS_INSTANCE *tids = talloc_get_type_abort(cookie, TIDS_INSTANCE);
+ *response_ptr = json_integer(tids->error_count);
+ return (*response_ptr == NULL) ? MON_NOMEM : MON_SUCCESS;
+}
+
+static MON_RC handle_show_req_pending(void *cookie, json_t **response_ptr)
+{
+ TIDS_INSTANCE *tids = talloc_get_type_abort(cookie, TIDS_INSTANCE);
+ *response_ptr = json_integer(tids->pids->len);
+ return (*response_ptr == NULL) ? MON_NOMEM : MON_SUCCESS;
+}
+
+void tr_tid_register_mons_handlers(TIDS_INSTANCE *tids, MONS_INSTANCE *mons)
+{
+ mons_register_handler(mons,
+ MON_CMD_SHOW, OPT_TYPE_SHOW_TID_REQ_COUNT,
+ handle_show_req_count, tids);
+ mons_register_handler(mons,
+ MON_CMD_SHOW, OPT_TYPE_SHOW_TID_REQ_ERR_COUNT,
+ handle_show_req_err_count, tids);
+ mons_register_handler(mons,
+ MON_CMD_SHOW, OPT_TYPE_SHOW_TID_REQ_PENDING,
+ handle_show_req_pending, tids);
+}