X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=mon%2Fmons.c;h=1df11f2e06050c8b0fedc43c529edce18b03b8fe;hb=6f65c9cce86719147d0b4dcc9823b25443c2d185;hp=29f405b5ba4c9a50be85a5b117dc86ac829da96d;hpb=1a3ad555c6b58de28efb85e7ab07c2f35208ab0d;p=trust_router.git diff --git a/mon/mons.c b/mon/mons.c index 29f405b..1df11f2 100644 --- a/mon/mons.c +++ b/mon/mons.c @@ -42,6 +42,24 @@ #include #include #include +#include +#include + +#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) + g_ptr_array_unref(mons->handlers); + + if (mons->pids) + g_array_unref(mons->pids); + + return 0; +} /** * Allocate a new MONS_INSTANCE @@ -54,20 +72,97 @@ MONS_INSTANCE *mons_new(TALLOC_CTX *mem_ctx) MONS_INSTANCE *mons = talloc(mem_ctx, MONS_INSTANCE); if (mons) { - mons->port = 0; + mons->hostname = NULL; + mons->mon_port = 0; + mons->tids = NULL; + mons->trps = NULL; mons->req_handler = NULL; mons->auth_handler = NULL; mons->cookie = NULL; + + /* Before any steps that may fail, install the destructor */ + talloc_set_destructor((void *)mons, mons_destructor); + mons->authorized_gss_names = tr_gss_names_new(mons); if (mons->authorized_gss_names == NULL) { talloc_free(mons); - mons = NULL; + return NULL; + } + + mons->handlers = g_ptr_array_new(); + if (mons->handlers == NULL) { + 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; } /** + * Callback to process a request and produce a response + * + * @param req_str JSON-encoded request + * @param data pointer to a MONS_INSTANCE + * @return pointer to the response string or null to send no response + */ +static TR_GSS_RC mons_req_cb(TALLOC_CTX *mem_ctx, TR_MSG *req_msg, TR_MSG **resp_msg, void *data) +{ + TALLOC_CTX *tmp_ctx = talloc_new(NULL); + MONS_INSTANCE *mons = talloc_get_type_abort(data, MONS_INSTANCE); + MON_REQ *req = NULL; + MON_RESP *resp = NULL; + TR_GSS_RC rc = TR_GSS_ERROR; + + /* Validate inputs */ + if (req_msg == NULL) + goto cleanup; + + req = tr_msg_get_mon_req(req_msg); + if (req == NULL) { + /* this is an internal error */ + tr_err("mons_req_cb: Received incorrect message type (was %d, expected %d)", + tr_msg_get_msg_type(req_msg), + MON_REQUEST); + /* TODO send an error response */ + goto cleanup; + } + + /* Allocate a response message */ + *resp_msg = talloc(tmp_ctx, TR_MSG); + if (*resp_msg == NULL) { + /* can't return a message, just emit an error */ + tr_crit("mons_req_cb: Error allocating response message."); + goto cleanup; + } + + /* Handle the request */ + resp = mons_handle_request(*resp_msg, mons, req); + if (resp == NULL) { + /* error processing the request */ + /* TODO send back an error */ + *resp_msg = NULL; /* null this out so the caller doesn't mistake it for valid */ + goto cleanup; + } + + /* Set the response message payload */ + tr_msg_set_mon_resp(*resp_msg, resp); + + /* Put the response message in the caller's context so it does not get freed when we exit */ + talloc_steal(mem_ctx, *resp_msg); + rc = TR_GSS_SUCCESS; + +cleanup: + talloc_free(tmp_ctx); + return rc; +} + +/** * Create a listener for monitoring requests * * Accept connections with mons_accept() @@ -85,7 +180,8 @@ MONS_INSTANCE *mons_new(TALLOC_CTX *mem_ctx) int mons_get_listener(MONS_INSTANCE *mons, MONS_REQ_FUNC *req_handler, MONS_AUTH_FUNC *auth_handler, - unsigned int port, + const char *hostname, + int port, void *cookie, int *fd_out, size_t max_fd) @@ -93,10 +189,10 @@ int mons_get_listener(MONS_INSTANCE *mons, size_t n_fd=0; size_t ii=0; - mons->port = port; + mons->mon_port = port; n_fd = tr_sock_listen_all(port, fd_out, max_fd); if (n_fd<=0) - tr_err("mons_get_listener: Error opening port %d"); + tr_err("mons_get_listener: Error opening port %d", port); else { /* opening port succeeded */ tr_info("mons_get_listener: Opened port %d.", port); @@ -119,6 +215,7 @@ int mons_get_listener(MONS_INSTANCE *mons, /* store the caller's request handler & cookie */ mons->req_handler = req_handler; mons->auth_handler = auth_handler; + mons->hostname = hostname; mons->cookie = cookie; } @@ -126,6 +223,48 @@ int mons_get_listener(MONS_INSTANCE *mons, } /** + * Process to handle an incoming monitoring request + * + * This should be run in a child process after fork(). Handles the request + * and terminates. Never returns to the caller. + * + * @param mons the monitoring server instance + * @param conn_fd file descriptor for the incoming connection + */ +static void mons_handle_proc(MONS_INSTANCE *mons, int conn_fd) +{ + struct rlimit rlim; /* for disabling core dump */ + + switch(tr_gss_handle_connection(conn_fd, + "trustmonitor", mons->hostname, /* acceptor name */ + mons->auth_handler, mons->cookie, /* auth callback and cookie */ + mons_req_cb, mons /* req callback and cookie */ + )) { + case TR_GSS_SUCCESS: + /* do nothing */ + break; + + case TR_GSS_ERROR: + tr_debug("mons_accept: Error returned by tr_gss_handle_connection()"); + break; + + default: + tr_err("mons_accept: Unexpected value returned by tr_gss_handle_connection()"); + break; + } + close(conn_fd); + + /* This ought to be an exit(0), but log4shib does not play well with fork() due to + * threading issues. To ensure we do not get stuck in the exit handler, we will + * abort. First disable core dump for this subprocess (the main process will still + * dump core if the environment allows). */ + rlim.rlim_cur = 0; /* max core size of 0 */ + rlim.rlim_max = 0; /* prevent the core size limit from being raised later */ + setrlimit(RLIMIT_CORE, &rlim); + abort(); /* exit hard */ +} + +/** * Accept and process a connection on a port opened with mons_get_listener() * * @param mons monitoring interface instance @@ -137,8 +276,8 @@ int mons_accept(MONS_INSTANCE *mons, int listen) int conn=-1; int pid=-1; - if (0 > (conn = accept(listen, NULL, NULL))) { - perror("Error from monitoring interface accept()"); + if (0 > (conn = tr_sock_accept(listen))) { + tr_debug("mons_accept: Error accepting connection"); return 1; } @@ -148,16 +287,44 @@ int mons_accept(MONS_INSTANCE *mons, int listen) } if (pid == 0) { - close(listen); - mons_handle_connection(mons, conn); - close(conn); - exit(0); /* exit to kill forked child process */ - } else { - close(conn); + /* Only the child process gets here */ + close(listen); /* this belongs to the parent */ + mons_handle_proc(mons, conn); /* never returns */ } + /* Only the parent process gets here */ + close(conn); /* this belongs to the child */ + 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)); + } + } + } +}