Merge pull request #52 from painless-security/jennifer/subprocess_status
authormrw42 <margaret@painless-security.com>
Thu, 3 May 2018 20:05:51 +0000 (16:05 -0400)
committerGitHub <noreply@github.com>
Thu, 3 May 2018 20:05:51 +0000 (16:05 -0400)
Report whether TID requests succeed and better clean up zombie TID / MON processes (pull request 4)

14 files changed:
CMakeLists.txt
Makefile.am
include/mon_internal.h
include/mons_handlers.h
include/tid_internal.h
include/tr_tid.h
mon/mon_common.c
mon/mons.c
mon/mons_handlers.c
tid/example/tids_main.c
tid/tids.c
tr/tr_main.c
tr/tr_tid.c
tr/tr_tid_mons.c [new file with mode: 0644]

index f7bff87..0d660b5 100644 (file)
@@ -14,6 +14,11 @@ include_directories(${GLIB_INCLUDE_DIRS})
 
 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
@@ -91,7 +96,7 @@ set(SOURCE_FILES
     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})
index c73c623..a65f9c9 100644 (file)
@@ -86,6 +86,7 @@ tr/tr.c \
 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 \
index 8e439d2..45eb60b 100644 (file)
@@ -100,6 +100,7 @@ enum mon_opt_type {
   // 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
@@ -133,6 +134,7 @@ struct mons_instance {
   MONS_AUTH_FUNC *auth_handler;
   void *cookie;
   GPtrArray *handlers;
+  GArray *pids; /* PIDs of active mons processes */
 };
 
 /* Client instance */
index 50bdd43..48a01b3 100644 (file)
@@ -36,7 +36,7 @@
 #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;
index 658b2e4..6b6fb78 100644 (file)
@@ -86,8 +86,14 @@ struct tidc_instance {
   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;
@@ -96,6 +102,7 @@ struct tids_instance {
   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
@@ -116,4 +123,6 @@ TID_RC tid_resp_cpy(TID_RESP *dst, TID_RESP *src);
 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
index 924293d..d8285b0 100644 (file)
 #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 */
index 440a277..5197958 100644 (file)
@@ -96,6 +96,9 @@ const char *mon_opt_type_to_string(MON_OPT_TYPE opt_type)
     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";
 
@@ -121,6 +124,7 @@ MON_OPT_TYPE mon_opt_type_from_string(const char *s)
   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);
index 4d638d6..f2e69c5 100644 (file)
 
 #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;
 }
 
@@ -88,6 +93,12 @@ MONS_INSTANCE *mons_new(TALLOC_CTX *mem_ctx)
       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;
 }
@@ -233,12 +244,41 @@ int mons_accept(MONS_INSTANCE *mons, int listen)
     );
     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));
+      }
+    }
+  }
+}
index b546b89..5e19db0 100644 (file)
@@ -49,7 +49,14 @@ static void request_helper(void *element, void *data);
 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? */
 };
 
 /**
@@ -63,7 +70,7 @@ MON_RESP *mons_handle_request(TALLOC_CTX *mem_ctx, MONS_INSTANCE *mons, MON_REQ
 {
   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");
@@ -71,7 +78,7 @@ MON_RESP *mons_handle_request(TALLOC_CTX *mem_ctx, MONS_INSTANCE *mons, MON_REQ
   /* 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 */
@@ -88,7 +95,7 @@ MON_RESP *mons_handle_request(TALLOC_CTX *mem_ctx, MONS_INSTANCE *mons, MON_REQ
 
   /* 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 */
@@ -103,23 +110,51 @@ MON_RESP *mons_handle_request(TALLOC_CTX *mem_ctx, MONS_INSTANCE *mons, MON_REQ
     }
   }
 
-  /* 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;
 }
 
@@ -213,11 +248,12 @@ static void request_helper(void *element, void *data)
 {
   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);
   }
 }
 
index 8673cdd..1ecc494 100644 (file)
@@ -167,9 +167,6 @@ static int tids_req_handler (TIDS_INSTANCE *tids,
   
 
   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;
index 2e5dbea..6a5b172 100644 (file)
@@ -310,9 +310,26 @@ cleanup:
   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;
 }
 
 /**
@@ -323,8 +340,9 @@ TIDS_INSTANCE *tids_new(TALLOC_CTX *mem_ctx)
  */
 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,
@@ -378,36 +396,123 @@ int tids_accept(TIDS_INSTANCE *tids, int listen)
 {
   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,
index 1968bde..630668c 100644 (file)
@@ -152,15 +152,17 @@ static void configure_signals(void)
 }
 
 /* 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[])
@@ -171,6 +173,7 @@ 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;
 
@@ -237,7 +240,8 @@ int main(int argc, char *argv[])
   /* 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) {
@@ -280,11 +284,7 @@ int main(int argc, char *argv[])
 
   /* 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;
   }
index 89fe994..fdf40d6 100644 (file)
@@ -287,7 +287,6 @@ static int tr_tids_req_handler(TIDS_INSTANCE *tids,
 
   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))) {
@@ -674,7 +673,7 @@ static int tr_tids_gss_handler(gss_name_t client_name, TR_NAME *gss_name,
 /* 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);
@@ -682,17 +681,26 @@ static void tr_tids_event_cb(int listener, short event, void *arg)
     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;
 
@@ -702,6 +710,12 @@ int tr_tids_event_init(struct event_base *base,
     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);
@@ -730,7 +744,7 @@ int tr_tids_event_init(struct event_base *base,
     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],
@@ -740,6 +754,12 @@ int tr_tids_event_init(struct event_base *base,
     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;
diff --git a/tr/tr_tid_mons.c b/tr/tr_tid_mons.c
new file mode 100644 (file)
index 0000000..0a18d2d
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * 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);
+}