Date: Wed, 21 Dec 2016 09:32:05 -0800 From: Adrian Chadd <adrian.chadd@gmail.com> To: Ed Schouten <ed@freebsd.org> Cc: "src-committers@freebsd.org" <src-committers@freebsd.org>, "svn-src-all@freebsd.org" <svn-src-all@freebsd.org>, "svn-src-head@freebsd.org" <svn-src-head@freebsd.org> Subject: Re: svn commit: r310354 - in head/usr.sbin: . prometheus_sysctl_exporter Message-ID: <CAJ-VmoksTgmNY1AeYRJV5UN7%2Bew%2BqDgL63_=1Aaa7AQbyrLOhQ@mail.gmail.com> In-Reply-To: <201612210829.uBL8Tijt041811@repo.freebsd.org> References: <201612210829.uBL8Tijt041811@repo.freebsd.org>
next in thread | previous in thread | raw e-mail | index | archive | help
heh, in an ideal world we'd not call this "prometheus", it'd just be the way we export sysctl data via 'sysctl' in a machine parsable format. Ok, since it's HTTP/gzip, maybe a separate standalone program is cool, but it doesn't have to be specific to prometheus. :-) That way anyone wanting to write a plugin for consuming freebsd data can just use this program as the API contract. Maybe just "sysctl_export" would be fine. I'd like to encourage other consumers to use it, including existing ones that currently do all the silly hijinx. :) What do you think? -adrian On 21 December 2016 at 00:29, Ed Schouten <ed@freebsd.org> wrote: > Author: ed > Date: Wed Dec 21 08:29:44 2016 > New Revision: 310354 > URL: https://svnweb.freebsd.org/changeset/base/310354 > > Log: > Add a Prometheus exporter for sysctl values. > > Now that we have our sysctl tree annotated with aggregation labels, > let's go ahead and provide a very simple utility for exporting the > sysctl tree in Prometheus' format. It can either be used in conjunction > with the Prometheus node exporter or run through inetd(8). > > The reason why I'm opting for having it in the base system is because it > has a pretty strong integration with some of sysctl's innards, such as > access to iterators, name lookups, metadata and type information. As I > am investigating whether we can add histograms as native types to sysctl > as well, this integration will only get stronger as we go along. That's > why it would be safer to oversee the development of this exporter > ourselves, as opposed to having it as an external project. > > This exporter is remarkably compact, especially when compared to the > official Linux binary of the Prometheus node exporter (16 KB vs 12 MB). > I guess this could be an interesting aspect for monitoring embedded > FreeBSD-based systems. > > Differential Revision: https://reviews.freebsd.org/D8792 > > Added: > head/usr.sbin/prometheus_sysctl_exporter/ > head/usr.sbin/prometheus_sysctl_exporter/Makefile (contents, props changed) > head/usr.sbin/prometheus_sysctl_exporter/prometheus_sysctl_exporter.8 (contents, props changed) > head/usr.sbin/prometheus_sysctl_exporter/prometheus_sysctl_exporter.c (contents, props changed) > Modified: > head/usr.sbin/Makefile > > Modified: head/usr.sbin/Makefile > ============================================================================== > --- head/usr.sbin/Makefile Wed Dec 21 07:26:04 2016 (r310353) > +++ head/usr.sbin/Makefile Wed Dec 21 08:29:44 2016 (r310354) > @@ -62,6 +62,7 @@ SUBDIR= adduser \ > periodic \ > powerd \ > procctl \ > + prometheus_sysctl_exporter \ > pstat \ > pw \ > pwd_mkdb \ > > Added: head/usr.sbin/prometheus_sysctl_exporter/Makefile > ============================================================================== > --- /dev/null 00:00:00 1970 (empty, because file is newly added) > +++ head/usr.sbin/prometheus_sysctl_exporter/Makefile Wed Dec 21 08:29:44 2016 (r310354) > @@ -0,0 +1,8 @@ > +# $FreeBSD$ > + > +PROG= prometheus_sysctl_exporter > +MAN= prometheus_sysctl_exporter.8 > + > +LIBADD= m z > + > +.include <bsd.prog.mk> > > Added: head/usr.sbin/prometheus_sysctl_exporter/prometheus_sysctl_exporter.8 > ============================================================================== > --- /dev/null 00:00:00 1970 (empty, because file is newly added) > +++ head/usr.sbin/prometheus_sysctl_exporter/prometheus_sysctl_exporter.8 Wed Dec 21 08:29:44 2016 (r310354) > @@ -0,0 +1,106 @@ > +.\" Copyright (c) 2016 Nuxi, https://nuxi.nl/ > +.\" > +.\" 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. > +.\" > +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. > +.\" > +.\" $FreeBSD$ > +.Dd December 18, 2016 > +.Dt PROMETHEUS_SYSCTL_EXPORTER 8 > +.Os > +.Sh NAME > +.Nm prometheus_sysctl_exporter > +.Nd print kernel state as Prometheus metrics > +.Sh SYNOPSIS > +.Nm prometheus_sysctl_exporter > +.Op Fl dgh > +.Op Ar prefix ... > +.Sh DESCRIPTION > +Prometheus is a monitoring system that gathers metrics from its targets > +by fetching them through HTTP GET requests. > +Metrics are identified by a name and an optional set of labels. > +Sample values are required to be numerical. > +.Pp > +The > +.Nm > +utility prints the values of sysctl nodes to standard output, > +formatted such that they can be scraped by Prometheus directly. > +By default, > +it prints metrics for all numerically representable nodes in the sysctl > +namespace. > +It is also possible to limit output to a smaller number of metrics by > +specifying one or more prefixes as arguments. > +.Pp > +Metrics printed by this utility are named > +.Ql sysctl_ , > +followed by the name of the sysctl node having its > +.Ql .\& > +separators replaced by > +.Ql _ . > +Components on which it is desirable to aggregate (e.g., > +names of devices) are omitted from the metric's name, > +but are appended as labels instead. > +.Pp > +There are two different methods for exporting the output of > +.Nm > +to Prometheus. > +The first method is to periodically invoke this utility through > +.Xr cron 8 > +and store its output in a textfile. > +The metrics in this textfile can then be served over HTTP using the > +Prometheus node exporter's textfile collector. > +The second method is to run this utility through > +.Xr inetd 8 . > +TCP port 9124 has been allocated for this purpose. > +.Pp > +The following options are available: > +.Bl -tag -width indent > +.It Fl d > +Print descriptions of metrics when available. > +.It Fl g > +Gzip compresses the HTTP response body. > +.It Fl h > +Precede the output with a HTTP response header. > +This flag is required when running this utility through > +.Xr inetd 8 . > +.El > +.Sh SEE ALSO > +.Xr cron 8 , > +.Xr inetd 8 , > +.Xr sysctl 8 , > +.Xr SYSCTL_ADD_NODE_WITH_LABEL 9 > +.Pp > +Prometheus project: > +.Pa https://prometheus.io/ . > +.Pp > +Prometheus exposition formats: > +.Pa https://prometheus.io/docs/instrumenting/exposition_formats/ . > +.Pp > +Prometheus node exporter: > +.Pa https://github.com/prometheus/node_exporter . > +.Pp > +Prometheus default port allocations: > +.Pa https://github.com/prometheus/prometheus/wiki/Default-port-allocations . > +.Sh HISTORY > +.Nm > +first appeared in > +.Fx 12.0 . > +.Sh AUTHORS > +.An Nuxi : Pa https://nuxi.nl/ . > > Added: head/usr.sbin/prometheus_sysctl_exporter/prometheus_sysctl_exporter.c > ============================================================================== > --- /dev/null 00:00:00 1970 (empty, because file is newly added) > +++ head/usr.sbin/prometheus_sysctl_exporter/prometheus_sysctl_exporter.c Wed Dec 21 08:29:44 2016 (r310354) > @@ -0,0 +1,652 @@ > +/*- > + * Copyright (c) 2016 Nuxi, https://nuxi.nl/ > + * > + * 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. > + * > + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 <sys/cdefs.h> > +__FBSDID("$FreeBSD$"); > + > +#include <sys/param.h> > +#include <sys/resource.h> > +#include <sys/socket.h> > +#include <sys/sysctl.h> > + > +#include <assert.h> > +#include <err.h> > +#include <errno.h> > +#include <math.h> > +#include <stdbool.h> > +#include <stdint.h> > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > +#include <unistd.h> > +#include <zlib.h> > + > +/* > + * Cursor for iterating over all of the system's sysctl OIDs. > + */ > +struct oid { > + int id[CTL_MAXNAME]; > + size_t len; > +}; > + > +/* Initializes the cursor to point to start of the tree. */ > +static void > +oid_get_root(struct oid *o) > +{ > + > + o->id[0] = 1; > + o->len = 1; > +} > + > +/* Obtains the OID for a sysctl by name. */ > +static void > +oid_get_by_name(struct oid *o, const char *name) > +{ > + > + o->len = nitems(o->id); > + if (sysctlnametomib(name, o->id, &o->len) != 0) > + err(1, "sysctl(%s)", name); > +} > + > +/* Returns whether an OID is placed below another OID. */ > +static bool > +oid_is_beneath(struct oid *oa, struct oid *ob) > +{ > + > + return (oa->len >= ob->len && > + memcmp(oa->id, ob->id, ob->len * sizeof(oa->id[0])) == 0); > +} > + > +/* Advances the cursor to the next OID. */ > +static bool > +oid_get_next(const struct oid *cur, struct oid *next) > +{ > + int lookup[CTL_MAXNAME + 2]; > + size_t nextsize; > + > + lookup[0] = 0; > + lookup[1] = 2; > + memcpy(lookup + 2, cur->id, cur->len * sizeof(lookup[0])); > + nextsize = sizeof(next->id); > + if (sysctl(lookup, 2 + cur->len, &next->id, &nextsize, 0, 0) != 0) { > + if (errno == ENOENT) > + return (false); > + err(1, "sysctl(next)"); > + } > + next->len = nextsize / sizeof(next->id[0]); > + return (true); > +} > + > +/* > + * OID formatting metadata. > + */ > +struct oidformat { > + unsigned int kind; > + char format[BUFSIZ]; > +}; > + > +/* Returns whether the OID represents a temperature value. */ > +static bool > +oidformat_is_temperature(const struct oidformat *of) > +{ > + > + return (of->format[0] == 'I' && of->format[1] == 'K'); > +} > + > +/* Returns whether the OID represents a timeval structure. */ > +static bool > +oidformat_is_timeval(const struct oidformat *of) > +{ > + > + return (strcmp(of->format, "S,timeval") == 0); > +} > + > +/* Fetches the formatting metadata for an OID. */ > +static bool > +oid_get_format(const struct oid *o, struct oidformat *of) > +{ > + int lookup[CTL_MAXNAME + 2]; > + size_t oflen; > + > + lookup[0] = 0; > + lookup[1] = 4; > + memcpy(lookup + 2, o->id, o->len * sizeof(lookup[0])); > + oflen = sizeof(*of); > + if (sysctl(lookup, 2 + o->len, of, &oflen, 0, 0) != 0) { > + if (errno == ENOENT) > + return (false); > + err(1, "sysctl(oidfmt)"); > + } > + return (true); > +} > + > +/* > + * Container for holding the value of an OID. > + */ > +struct oidvalue { > + enum { SIGNED, UNSIGNED, FLOAT } type; > + union { > + intmax_t s; > + uintmax_t u; > + double f; > + } value; > +}; > + > +/* Extracts the value of an OID, converting it to a floating-point number. */ > +static double > +oidvalue_get_float(const struct oidvalue *ov) > +{ > + > + switch (ov->type) { > + case SIGNED: > + return (ov->value.s); > + case UNSIGNED: > + return (ov->value.u); > + case FLOAT: > + return (ov->value.f); > + default: > + assert(0 && "Unknown value type"); > + } > +} > + > +/* Sets the value of an OID as a signed integer. */ > +static void > +oidvalue_set_signed(struct oidvalue *ov, intmax_t s) > +{ > + > + ov->type = SIGNED; > + ov->value.s = s; > +} > + > +/* Sets the value of an OID as an unsigned integer. */ > +static void > +oidvalue_set_unsigned(struct oidvalue *ov, uintmax_t u) > +{ > + > + ov->type = UNSIGNED; > + ov->value.u = u; > +} > + > +/* Sets the value of an OID as a floating-point number. */ > +static void > +oidvalue_set_float(struct oidvalue *ov, double f) > +{ > + > + ov->type = FLOAT; > + ov->value.f = f; > +} > + > +/* Prints the value of an OID to a file stream. */ > +static void > +oidvalue_print(const struct oidvalue *ov, FILE *fp) > +{ > + > + switch (ov->type) { > + case SIGNED: > + fprintf(fp, "%jd", ov->value.s); > + break; > + case UNSIGNED: > + fprintf(fp, "%ju", ov->value.u); > + break; > + case FLOAT: > + switch (fpclassify(ov->value.f)) { > + case FP_INFINITE: > + if (signbit(ov->value.f)) > + fprintf(fp, "-Inf"); > + else > + fprintf(fp, "+Inf"); > + break; > + case FP_NAN: > + fprintf(fp, "Nan"); > + break; > + default: > + fprintf(fp, "%.6f", ov->value.f); > + break; > + } > + break; > + } > +} > + > +/* Fetches the value of an OID. */ > +static bool > +oid_get_value(const struct oid *o, const struct oidformat *of, > + struct oidvalue *ov) > +{ > + > + switch (of->kind & CTLTYPE) { > +#define GET_VALUE(ctltype, type) \ > + case (ctltype): { \ > + type value; \ > + size_t valuesize; \ > + \ > + valuesize = sizeof(value); \ > + if (sysctl(o->id, o->len, &value, &valuesize, 0, 0) != 0) \ > + return (false); \ > + if ((type)-1 > 0) \ > + oidvalue_set_unsigned(ov, value); \ > + else \ > + oidvalue_set_signed(ov, value); \ > + break; \ > + } > + GET_VALUE(CTLTYPE_INT, int); > + GET_VALUE(CTLTYPE_UINT, unsigned int); > + GET_VALUE(CTLTYPE_LONG, long); > + GET_VALUE(CTLTYPE_ULONG, unsigned long); > + GET_VALUE(CTLTYPE_S8, int8_t); > + GET_VALUE(CTLTYPE_U8, uint8_t); > + GET_VALUE(CTLTYPE_S16, int16_t); > + GET_VALUE(CTLTYPE_U16, uint16_t); > + GET_VALUE(CTLTYPE_S32, int32_t); > + GET_VALUE(CTLTYPE_U32, uint32_t); > + GET_VALUE(CTLTYPE_S64, int64_t); > + GET_VALUE(CTLTYPE_U64, uint64_t); > +#undef GET_VALUE > + case CTLTYPE_OPAQUE: > + if (oidformat_is_timeval(of)) { > + struct timeval tv; > + size_t tvsize; > + > + tvsize = sizeof(tv); > + if (sysctl(o->id, o->len, &tv, &tvsize, 0, 0) != 0) > + return (false); > + oidvalue_set_float(ov, > + (double)tv.tv_sec + (double)tv.tv_usec / 1000000); > + return (true); > + } else if (strcmp(of->format, "S,loadavg") == 0) { > + struct loadavg la; > + size_t lasize; > + > + /* > + * Only return the one minute load average, as > + * the others can be inferred using avg_over_time(). > + */ > + lasize = sizeof(la); > + if (sysctl(o->id, o->len, &la, &lasize, 0, 0) != 0) > + return (false); > + oidvalue_set_float(ov, > + (double)la.ldavg[0] / (double)la.fscale); > + return (true); > + } > + return (false); > + default: > + return (false); > + } > + > + /* Convert temperatures from decikelvin to degrees Celcius. */ > + if (oidformat_is_temperature(of)) { > + double v; > + int e; > + > + v = oidvalue_get_float(ov); > + if (v < 0) { > + oidvalue_set_float(ov, NAN); > + } else { > + e = of->format[2] >= '0' && of->format[2] <= '9' ? > + of->format[2] - '0' : 1; > + oidvalue_set_float(ov, v / pow(10, e) - 273.15); > + } > + } > + return (true); > +} > + > +/* > + * The full name of an OID, stored as a series of components. > + */ > +struct oidname { > + struct oid oid; > + char names[BUFSIZ]; > + char labels[BUFSIZ]; > +}; > + > +/* > + * Initializes the OID name object with an empty value. > + */ > +static void > +oidname_init(struct oidname *on) > +{ > + > + on->oid.len = 0; > +} > + > +/* Fetches the name and labels of an OID, reusing the previous results. */ > +static void > +oid_get_name(const struct oid *o, struct oidname *on) > +{ > + int lookup[CTL_MAXNAME + 2]; > + char *c, *label; > + size_t i, len; > + > + /* Fetch the name and split it up in separate components. */ > + lookup[0] = 0; > + lookup[1] = 1; > + memcpy(lookup + 2, o->id, o->len * sizeof(lookup[0])); > + len = sizeof(on->names); > + if (sysctl(lookup, 2 + o->len, on->names, &len, 0, 0) != 0) > + err(1, "sysctl(name)"); > + for (c = strchr(on->names, '.'); c != NULL; c = strchr(c + 1, '.')) > + *c = '\0'; > + > + /* No need to fetch labels for components that we already have. */ > + label = on->labels; > + for (i = 0; i < o->len && i < on->oid.len && o->id[i] == on->oid.id[i]; > + ++i) > + label += strlen(label) + 1; > + > + /* Fetch the remaining labels. */ > + lookup[1] = 6; > + for (; i < o->len; ++i) { > + len = on->labels + sizeof(on->labels) - label; > + if (sysctl(lookup, 2 + i + 1, label, &len, 0, 0) == 0) { > + label += len; > + } else if (errno == ENOENT) { > + *label++ = '\0'; > + } else { > + err(1, "sysctl(oidlabel)"); > + } > + } > + on->oid = *o; > +} > + > +/* Prints the name and labels of an OID to a file stream. */ > +static void > +oidname_print(const struct oidname *on, const struct oidformat *of, > + FILE *fp) > +{ > + const char *name, *label; > + size_t i; > + char separator; > + > + /* Print the name of the metric. */ > + fprintf(fp, "sysctl"); > + name = on->names; > + label = on->labels; > + for (i = 0; i < on->oid.len; ++i) { > + if (*label == '\0') { > + assert(name[strspn(name, > + "abcdefghijklmnopqrstuvwxyz" > + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" > + "0123456789_")] == '\0'); > + fprintf(fp, "_%s", name); > + } > + name += strlen(name) + 1; > + label += strlen(label) + 1; > + } > + if (oidformat_is_temperature(of)) > + fprintf(fp, "_celcius"); > + else if (oidformat_is_timeval(of)) > + fprintf(fp, "_seconds"); > + > + /* Print the labels of the metric. */ > + name = on->names; > + label = on->labels; > + separator = '{'; > + for (i = 0; i < on->oid.len; ++i) { > + if (*label != '\0') { > + assert(name[strspn(name, > + "abcdefghijklmnopqrstuvwxyz" > + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" > + "0123456789_-")] == '\0'); > + assert(label[strspn(label, > + "abcdefghijklmnopqrstuvwxyz" > + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" > + "0123456789_")] == '\0'); > + fprintf(fp, "%c%s=\"%s\"", separator, label, name); > + separator = ','; > + } > + name += strlen(name) + 1; > + label += strlen(label) + 1; > + } > + if (separator != '{') > + fputc('}', fp); > +} > + > +/* Returns whether the OID name has any labels associated to it. */ > +static bool > +oidname_has_labels(const struct oidname *on) > +{ > + size_t i; > + > + for (i = 0; i < on->oid.len; ++i) > + if (on->labels[i] != 0) > + return (true); > + return (false); > +} > + > +/* > + * The description of an OID. > + */ > +struct oiddescription { > + char description[BUFSIZ]; > +}; > + > +/* > + * Fetches the description of an OID. > + */ > +static bool > +oid_get_description(const struct oid *o, struct oiddescription *od) > +{ > + int lookup[CTL_MAXNAME + 2]; > + char *newline; > + size_t odlen; > + > + lookup[0] = 0; > + lookup[1] = 5; > + memcpy(lookup + 2, o->id, o->len * sizeof(lookup[0])); > + odlen = sizeof(od->description); > + if (sysctl(lookup, 2 + o->len, &od->description, &odlen, 0, 0) != 0) { > + if (errno == ENOENT) > + return (false); > + err(1, "sysctl(oiddescr)"); > + } > + > + newline = strchr(od->description, '\n'); > + if (newline != NULL) > + *newline = '\0'; > + > + return (*od->description != '\0'); > +} > + > +/* Prints the description of an OID to a file stream. */ > +static void > +oiddescription_print(const struct oiddescription *od, FILE *fp) > +{ > + > + fprintf(fp, "%s", od->description); > +} > + > +static void > +oid_print(const struct oid *o, struct oidname *on, bool print_description, > + FILE *fp) > +{ > + struct oidformat of; > + struct oidvalue ov; > + struct oiddescription od; > + > + if (!oid_get_format(o, &of) || !oid_get_value(o, &of, &ov)) > + return; > + oid_get_name(o, on); > + > + /* > + * Print the line with the description. Prometheus expects a > + * single unique description for every metric, which cannot be > + * guaranteed by sysctl if labels are present. Omit the > + * description if labels are present. > + */ > + if (print_description && !oidname_has_labels(on) && > + oid_get_description(o, &od)) { > + fprintf(fp, "# HELP "); > + oidname_print(on, &of, fp); > + fputc(' ', fp); > + oiddescription_print(&od, fp); > + fputc('\n', fp); > + } > + > + /* Print the line with the value. */ > + oidname_print(on, &of, fp); > + fputc(' ', fp); > + oidvalue_print(&ov, fp); > + fputc('\n', fp); > +} > + > +/* Gzip compresses a buffer of memory. */ > +static bool > +buf_gzip(const char *in, size_t inlen, char *out, size_t *outlen) > +{ > + z_stream stream = { > + .next_in = __DECONST(unsigned char *, in), > + .avail_in = inlen, > + .next_out = (unsigned char *)out, > + .avail_out = *outlen, > + }; > + > + if (deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, > + MAX_WBITS + 16, 8, Z_DEFAULT_STRATEGY) != Z_OK || > + deflate(&stream, Z_FINISH) != Z_STREAM_END) { > + return (false); > + } > + *outlen = stream.total_out; > + return (deflateEnd(&stream) == Z_OK); > +} > + > +static void > +usage(void) > +{ > + > + fprintf(stderr, > + "usage: prometheus_sysctl_exporter [-dgh] [prefix ...]\n"); > + exit(1); > +} > + > +int > +main(int argc, char *argv[]) > +{ > + struct oidname on; > + char *http_buf; > + FILE *fp; > + size_t http_buflen; > + int ch; > + bool gzip_mode, http_mode, print_descriptions; > + > + /* Parse command line flags. */ > + gzip_mode = http_mode = print_descriptions = false; > + while ((ch = getopt(argc, argv, "dgh")) != -1) { > + switch (ch) { > + case 'd': > + print_descriptions = true; > + break; > + case 'g': > + gzip_mode = true; > + break; > + case 'h': > + http_mode = true; > + break; > + default: > + usage(); > + } > + } > + argc -= optind; > + argv += optind; > + > + /* HTTP output: cache metrics in buffer. */ > + if (http_mode) { > + fp = open_memstream(&http_buf, &http_buflen); > + if (fp == NULL) > + err(1, "open_memstream"); > + } else { > + fp = stdout; > + } > + > + oidname_init(&on); > + if (argc == 0) { > + struct oid o; > + > + /* Print all OIDs. */ > + oid_get_root(&o); > + do { > + oid_print(&o, &on, print_descriptions, fp); > + } while (oid_get_next(&o, &o)); > + } else { > + int i; > + > + /* Print only trees provided as arguments. */ > + for (i = 0; i < argc; ++i) { > + struct oid o, root; > + > + oid_get_by_name(&root, argv[i]); > + o = root; > + do { > + oid_print(&o, &on, print_descriptions, fp); > + } while (oid_get_next(&o, &o) && > + oid_is_beneath(&o, &root)); > + } > + } > + > + if (http_mode) { > + const char *content_encoding = ""; > + > + if (ferror(fp) || fclose(fp) != 0) > + err(1, "Cannot generate output"); > + > + /* Gzip compress the output. */ > + if (gzip_mode) { > + char *buf; > + size_t buflen; > + > + buflen = http_buflen; > + buf = malloc(buflen); > + if (buf == NULL) > + err(1, "Cannot allocate compression buffer"); > + if (buf_gzip(http_buf, http_buflen, buf, &buflen)) { > + content_encoding = "Content-Encoding: gzip\r\n"; > + free(http_buf); > + http_buf = buf; > + http_buflen = buflen; > + } else { > + free(buf); > + } > + } > + > + /* Print HTTP header and metrics. */ > + dprintf(STDOUT_FILENO, > + "HTTP/1.1 200 OK\r\n" > + "Connection: close\r\n" > + "%s" > + "Content-Length: %zu\r\n" > + "Content-Type: text/plain; version=0.0.4\r\n" > + "\r\n", > + content_encoding, http_buflen); > + write(STDOUT_FILENO, http_buf, http_buflen); > + free(http_buf); > + > + /* Drain output. */ > + if (shutdown(STDIN_FILENO, SHUT_WR) == 0) { > + char buf[1024]; > + > + while (read(STDIN_FILENO, buf, sizeof(buf)) > 0) { > + } > + } > + } > + return (0); > +} >
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?CAJ-VmoksTgmNY1AeYRJV5UN7%2Bew%2BqDgL63_=1Aaa7AQbyrLOhQ>