aboutsummaryrefslogtreecommitdiff
path: root/src/feature/metrics
diff options
context:
space:
mode:
Diffstat (limited to 'src/feature/metrics')
-rw-r--r--src/feature/metrics/.may_include1
-rw-r--r--src/feature/metrics/include.am10
-rw-r--r--src/feature/metrics/metrics.c280
-rw-r--r--src/feature/metrics/metrics.h37
-rw-r--r--src/feature/metrics/metrics_sys.c37
-rw-r--r--src/feature/metrics/metrics_sys.h22
6 files changed, 387 insertions, 0 deletions
diff --git a/src/feature/metrics/.may_include b/src/feature/metrics/.may_include
new file mode 100644
index 0000000000..424c745c12
--- /dev/null
+++ b/src/feature/metrics/.may_include
@@ -0,0 +1 @@
+*.h
diff --git a/src/feature/metrics/include.am b/src/feature/metrics/include.am
new file mode 100644
index 0000000000..0e875f43ad
--- /dev/null
+++ b/src/feature/metrics/include.am
@@ -0,0 +1,10 @@
+
+# ADD_C_FILE: INSERT SOURCES HERE.
+LIBTOR_APP_A_SOURCES += \
+ src/feature/metrics/metrics.c \
+ src/feature/metrics/metrics_sys.c
+
+# ADD_C_FILE: INSERT HEADERS HERE.
+noinst_HEADERS += \
+ src/feature/metrics/metrics.h \
+ src/feature/metrics/metrics_sys.h
diff --git a/src/feature/metrics/metrics.c b/src/feature/metrics/metrics.c
new file mode 100644
index 0000000000..9a72fe7145
--- /dev/null
+++ b/src/feature/metrics/metrics.c
@@ -0,0 +1,280 @@
+/* Copyright (c) 2007-2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file metrics.c
+ * @brief Metrics subsystem.
+ **/
+
+#include "orconfig.h"
+
+#include "core/or/or.h"
+
+#include "lib/encoding/confline.h"
+#include "lib/log/util_bug.h"
+#include "lib/malloc/malloc.h"
+#include "lib/metrics/metrics_store.h"
+#include "lib/net/resolve.h"
+#include "lib/string/printf.h"
+#include "lib/net/nettypes.h"
+#include "lib/net/address.h"
+
+#include "core/mainloop/connection.h"
+#include "core/or/connection_or.h"
+#include "core/or/connection_st.h"
+#include "core/or/policies.h"
+#include "core/or/port_cfg_st.h"
+#include "core/proto/proto_http.h"
+
+#include "feature/dircommon/directory.h"
+#include "feature/metrics/metrics.h"
+
+#include "app/config/config.h"
+#include "app/main/subsysmgr.h"
+
+/** Metrics format driver set by the MetricsPort option. */
+static metrics_format_t the_format = METRICS_FORMAT_PROMETHEUS;
+
+/** Return true iff the given peer address is allowed by our MetricsPortPolicy
+ * option that is is in that list. */
+static bool
+metrics_request_allowed(const tor_addr_t *peer_addr)
+{
+ tor_assert(peer_addr);
+
+ return metrics_policy_permits_address(peer_addr);
+}
+
+/** Helper: For a metrics port connection, write the HTTP response header
+ * using the data length passed. */
+static void
+write_metrics_http_response(const size_t data_len, connection_t *conn)
+{
+ char date[RFC1123_TIME_LEN+1];
+ buf_t *buf = buf_new_with_capacity(128 + data_len);
+
+ format_rfc1123_time(date, approx_time());
+ buf_add_printf(buf, "HTTP/1.0 200 OK\r\nDate: %s\r\n", date);
+ buf_add_printf(buf, "Content-Type: text/plain; charset=utf-8\r\n");
+ buf_add_printf(buf, "Content-Length: %" TOR_PRIuSZ "\r\n", data_len);
+ buf_add_string(buf, "\r\n");
+
+ connection_buf_add_buf(conn, buf);
+ buf_free(buf);
+}
+
+/** Return newly allocated buffer containing the output of all subsystems
+ * having metrics.
+ *
+ * This is used to output the content on the MetricsPort. */
+buf_t *
+metrics_get_output(const metrics_format_t fmt)
+{
+ buf_t *data = buf_new();
+
+ /* Go over all subsystems that exposes a metrics store. */
+ for (unsigned i = 0; i < n_tor_subsystems; ++i) {
+ const smartlist_t *stores;
+ const subsys_fns_t *sys = tor_subsystems[i];
+
+ /* Skip unsupported subsystems. */
+ if (!sys->supported) {
+ continue;
+ }
+
+ if (sys->get_metrics && (stores = sys->get_metrics())) {
+ SMARTLIST_FOREACH_BEGIN(stores, const metrics_store_t *, store) {
+ metrics_store_get_output(fmt, store, data);
+ } SMARTLIST_FOREACH_END(store);
+ }
+ }
+
+ return data;
+}
+
+/** Process what is in the inbuf of this connection of type metrics.
+ *
+ * Return 0 on success else -1 on error for which the connection is marked for
+ * close. */
+int
+metrics_connection_process_inbuf(connection_t *conn)
+{
+ int ret = -1;
+ char *headers = NULL, *command = NULL, *url = NULL;
+ const char *errmsg = NULL;
+
+ tor_assert(conn);
+ tor_assert(conn->type == CONN_TYPE_METRICS);
+
+ if (!metrics_request_allowed(&conn->addr)) {
+ /* Close connection. Don't bother returning anything if you are not
+ * allowed by being on the policy list. */
+ errmsg = NULL;
+ goto err;
+ }
+
+ const int http_status =
+ connection_fetch_from_buf_http(conn, &headers, 1024, NULL, NULL, 1024, 0);
+ if (http_status < 0) {
+ errmsg = "HTTP/1.0 400 Bad Request\r\n\r\n";
+ goto err;
+ } else if (http_status == 0) {
+ /* no HTTP request yet. */
+ ret = 0;
+ goto done;
+ }
+
+ const int cmd_status = parse_http_command(headers, &command, &url);
+ if (cmd_status < 0) {
+ errmsg = "HTTP/1.0 400 Bad Request\r\n\r\n";
+ goto err;
+ } else if (strcmpstart(command, "GET")) {
+ errmsg = "HTTP/1.0 405 Method Not Allowed\r\n\r\n";
+ goto err;
+ }
+ tor_assert(url);
+
+ /* Where we expect the query to come for. */
+#define EXPECTED_URL_PATH "/metrics"
+#define EXPECTED_URL_PATH_LEN (sizeof(EXPECTED_URL_PATH) - 1) /* No NUL */
+
+ if (!strcmpstart(url, EXPECTED_URL_PATH) &&
+ strlen(url) == EXPECTED_URL_PATH_LEN) {
+ buf_t *data = metrics_get_output(the_format);
+
+ write_metrics_http_response(buf_datalen(data), conn);
+ connection_buf_add_buf(conn, data);
+ buf_free(data);
+ } else {
+ errmsg = "HTTP/1.0 404 Not Found\r\n\r\n";
+ goto err;
+ }
+
+ ret = 0;
+ goto done;
+
+ err:
+ if (errmsg) {
+ log_info(LD_EDGE, "HTTP metrics error: saying %s", escaped(errmsg));
+ connection_buf_add(errmsg, strlen(errmsg), conn);
+ }
+ connection_mark_and_flush(conn);
+
+ done:
+ tor_free(headers);
+ tor_free(command);
+ tor_free(url);
+
+ return ret;
+}
+
+/** Parse metrics ports from options. On success, add the port to the ports
+ * list and return 0. On failure, set err_msg_out to a newly allocated string
+ * describing the problem and return -1. */
+int
+metrics_parse_ports(or_options_t *options, smartlist_t *ports,
+ char **err_msg_out)
+{
+ int num_elems, ok = 0, ret = -1;
+ const char *addrport_str = NULL, *fmt_str = NULL;
+ smartlist_t *elems = NULL;
+ port_cfg_t *cfg = NULL;
+
+ tor_assert(options);
+ tor_assert(ports);
+
+ /* No metrics port to configure, just move on . */
+ if (!options->MetricsPort_lines) {
+ return 0;
+ }
+
+ elems = smartlist_new();
+
+ /* Split between the protocol and the address/port. */
+ num_elems = smartlist_split_string(elems,
+ options->MetricsPort_lines->value, " ",
+ SPLIT_SKIP_SPACE | SPLIT_IGNORE_BLANK, 2);
+ if (num_elems < 1) {
+ *err_msg_out = tor_strdup("MetricsPort is missing port.");
+ goto end;
+ }
+
+ addrport_str = smartlist_get(elems, 0);
+ if (num_elems >= 2) {
+ /* Parse the format if any. */
+ fmt_str = smartlist_get(elems, 1);
+ if (!strcasecmp(fmt_str, "prometheus")) {
+ the_format = METRICS_FORMAT_PROMETHEUS;
+ } else {
+ tor_asprintf(err_msg_out, "MetricsPort unknown format: %s", fmt_str);
+ goto end;
+ }
+ }
+
+ /* Port configuration with default address. */
+ cfg = port_cfg_new(0);
+ cfg->type = CONN_TYPE_METRICS_LISTENER;
+
+ /* Parse the port first. Then an address if any can be found. */
+ cfg->port = (int) tor_parse_long(addrport_str, 10, 0, 65535, &ok, NULL);
+ if (ok) {
+ tor_addr_parse(&cfg->addr, "127.0.0.1");
+ } else {
+ /* We probably have a host:port situation */
+ if (tor_addr_port_lookup(addrport_str, &cfg->addr,
+ (uint16_t *) &cfg->port) < 0) {
+ *err_msg_out = tor_strdup("MetricsPort address/port failed to parse or "
+ "resolve.");
+ goto end;
+ }
+ }
+ /* Add it to the ports list. */
+ smartlist_add(ports, cfg);
+
+ /* It is set. MetricsPort doesn't support the NoListen options or such that
+ * would prevent from being a real listener port. */
+ options->MetricsPort_set = 1;
+
+ /* Success. */
+ ret = 0;
+
+ end:
+ if (ret != 0) {
+ port_cfg_free(cfg);
+ }
+ SMARTLIST_FOREACH(elems, char *, e, tor_free(e));
+ smartlist_free(elems);
+ return ret;
+}
+
+/** Called when conn has gotten its socket closed. */
+int
+metrics_connection_reached_eof(connection_t *conn)
+{
+ tor_assert(conn);
+
+ log_info(LD_EDGE, "Metrics connection reached EOF. Closing.");
+ connection_mark_for_close(conn);
+ return 0;
+}
+
+/** Called when conn has no more bytes left on its outbuf. Return 0 indicating
+ * success. */
+int
+metrics_connection_finished_flushing(connection_t *conn)
+{
+ tor_assert(conn);
+ return 0;
+}
+
+/** Initialize the subsystem. */
+void
+metrics_init(void)
+{
+}
+
+/** Cleanup and free any global memory of this subsystem. */
+void
+metrics_cleanup(void)
+{
+}
diff --git a/src/feature/metrics/metrics.h b/src/feature/metrics/metrics.h
new file mode 100644
index 0000000000..e072519d10
--- /dev/null
+++ b/src/feature/metrics/metrics.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file metrics.h
+ * @brief Header for feature/metrics/metrics.c
+ **/
+
+#ifndef TOR_FEATURE_METRICS_METRICS_H
+#define TOR_FEATURE_METRICS_METRICS_H
+
+#include "lib/buf/buffers.h"
+#include "lib/container/smartlist.h"
+
+#include "app/config/or_options_st.h"
+
+#include "lib/metrics/metrics_common.h"
+
+struct connection_t;
+
+/* Initializer / Cleanup. */
+void metrics_init(void);
+void metrics_cleanup(void);
+
+/* Accessors. */
+buf_t *metrics_get_output(const metrics_format_t fmt);
+
+/* Connection. */
+int metrics_connection_process_inbuf(struct connection_t *conn);
+int metrics_connection_reached_eof(struct connection_t *conn);
+int metrics_connection_finished_flushing(struct connection_t *conn);
+
+/* Configuration. */
+int metrics_parse_ports(or_options_t *options, smartlist_t *ports,
+ char **err_msg_out);
+
+#endif /* !defined(TOR_FEATURE_METRICS_METRICS_H) */
diff --git a/src/feature/metrics/metrics_sys.c b/src/feature/metrics/metrics_sys.c
new file mode 100644
index 0000000000..419318068e
--- /dev/null
+++ b/src/feature/metrics/metrics_sys.c
@@ -0,0 +1,37 @@
+/* Copyright (c) 2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file metrics_sys.c
+ * @brief Setup and tear down the metrics subsystem.
+ **/
+
+#include "lib/subsys/subsys.h"
+
+#include "feature/metrics/metrics.h"
+#include "feature/metrics/metrics_sys.h"
+
+static int
+subsys_metrics_initialize(void)
+{
+ metrics_init();
+ return 0;
+}
+
+static void
+subsys_metrics_shutdown(void)
+{
+ metrics_cleanup();
+}
+
+const subsys_fns_t sys_metrics = {
+ SUBSYS_DECLARE_LOCATION(),
+
+ .name = "metrics",
+ .supported = true,
+ .level = METRICS_SUBSYS_LEVEL,
+
+ .initialize = subsys_metrics_initialize,
+ .shutdown = subsys_metrics_shutdown,
+};
+
diff --git a/src/feature/metrics/metrics_sys.h b/src/feature/metrics/metrics_sys.h
new file mode 100644
index 0000000000..30c1b14836
--- /dev/null
+++ b/src/feature/metrics/metrics_sys.h
@@ -0,0 +1,22 @@
+/* Copyright (c) 2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file metrics_sys.h
+ * @brief Header for feature/metrics/metrics_sys.c
+ **/
+
+#ifndef TOR_FEATURE_METRICS_METRICS_SYS_H
+#define TOR_FEATURE_METRICS_METRICS_SYS_H
+
+extern const struct subsys_fns_t sys_metrics;
+
+/**
+ * Subsystem level for the metrics system.
+ *
+ * Defined here so that it can be shared between the real and stub
+ * definitions.
+ **/
+#define METRICS_SUBSYS_LEVEL (99)
+
+#endif /* !defined(TOR_FEATURE_METRICS_METRICS_SYS_H) */