commit 3c072d9e64faaf80517ede4e0e26f68d3ccd39d1
Author: Santtu Lakkala <santtu.lakkala@unikie.com>
Date: Fri, 22 Nov 2024 13:48:54 +0200
Initial import
Diffstat:
A | Makefile | | | 48 | ++++++++++++++++++++++++++++++++++++++++++++++++ |
A | buf.c | | | 11 | +++++++++++ |
A | buf.h | | | 13 | +++++++++++++ |
A | cpu.c | | | 43 | +++++++++++++++++++++++++++++++++++++++++++ |
A | cpu.h | | | 25 | +++++++++++++++++++++++++ |
A | http.c | | | 116 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | http.h | | | 15 | +++++++++++++++ |
A | json.c | | | 291 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | json.h | | | 35 | +++++++++++++++++++++++++++++++++++ |
A | mem.c | | | 44 | ++++++++++++++++++++++++++++++++++++++++++++ |
A | mem.h | | | 24 | ++++++++++++++++++++++++ |
A | minitls.h | | | 121 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | net.c | | | 128 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | net.h | | | 47 | +++++++++++++++++++++++++++++++++++++++++++++++ |
A | plong.c | | | 64 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | util.h | | | 11 | +++++++++++ |
16 files changed, 1036 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile
@@ -0,0 +1,48 @@
+.POSIX:
+SOURCES = plong.c buf.c json.c http.c cpu.c mem.c net.c
+HEADERS = buf.h json.h http.h cpu.h mem.h net.h minitls.h
+OBJS = $(SOURCES:.c=.o)
+PROGRAM = plong
+LDFLAGS = -lssl -lcrypto
+CFLAGS = -W -Wall -std=c99 -D_POSIX_C_SOURCE=200809L -fsanitize=address,undefined -g
+
+all: $(PROGRAM)
+
+$(PROGRAM): $(OBJS)
+ $(CC) $(CFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c $< -o $@
+
+clean:
+ rm -f $(OBJS)
+
+depend:
+ for i in ${HEADERS} ${SOURCES}; do echo "$$i"; done | sort | uniq | while read i; do \
+ sed -ne 's!^# *include *"\([^"]*\)".*!'"$$(echo "$$i" | sed -e 's/\.c$$/\.o/')"': '"$$(dirname "$$i" | sed -ne 's!^[^.].*!&/!; T; p; ')"'\1!; T; p' <"$$i"; \
+ done | sort
+
+.PHONY: depend all
+
+buf.o: buf.h
+cpu.h: json.h
+cpu.o: cpu.h
+cpu.o: util.h
+http.h: buf.h
+http.h: minitls.h
+http.o: buf.h
+http.o: http.h
+http.o: minitls.h
+json.h: buf.h
+json.o: json.h
+mem.h: json.h
+mem.o: mem.h
+net.h: json.h
+net.o: net.h
+net.o: util.h
+plong.o: cpu.h
+plong.o: http.h
+plong.o: json.h
+plong.o: mem.h
+plong.o: minitls.h
+plong.o: net.h
diff --git a/buf.c b/buf.c
@@ -0,0 +1,11 @@
+#include <errno.h>
+#include "buf.h"
+
+int buf_char(struct buf *b, char c)
+{
+ if (b->data >= b->end)
+ return ENOMEM;
+ *b->data++ = c;
+ return 0;
+}
+
diff --git a/buf.h b/buf.h
@@ -0,0 +1,13 @@
+#ifndef PLONG_BUF_H
+#define PLONG_BUF_H
+
+#define BUF_FROM_ARR(a) (struct buf){ .data = a, .end = 1[&a] }
+
+struct buf {
+ char *data;
+ char *end;
+};
+
+int buf_char(struct buf *b, char c);
+
+#endif
diff --git a/cpu.c b/cpu.c
@@ -0,0 +1,43 @@
+#include <stdio.h>
+#include <errno.h>
+#include "cpu.h"
+#include "util.h"
+
+static int _cpu_read(struct cpu *c)
+{
+ FILE *f = fopen("/proc/stat", "r");
+ if (!f)
+ return errno;
+ if (fscanf(f, "cpu" UINT_SCAN(CPU_FIELDS)
+#define X(a) , &c->a
+ CPU_FIELDS(X)
+#undef X
+ ) < COUNT(CPU_FIELDS)) {
+ fclose(f);
+ return ENODATA;
+ }
+ fclose(f);
+
+ return 0;
+}
+
+int cpu_init(struct cpu *c)
+{
+ return _cpu_read(c);
+}
+
+int cpu_poll(struct cpu *c, struct json *j)
+{
+ int r;
+ struct cpu c0 = *c;
+ if ((r = _cpu_read(c)))
+ return r;
+#define X(a) \
+ if ((r = json_obj_key(j, #a))) \
+ return r; \
+ if ((r = json_int(j, c->a - c0.a))) \
+ return r;
+ CPU_FIELDS(X)
+#undef X
+ return 0;
+}
diff --git a/cpu.h b/cpu.h
@@ -0,0 +1,25 @@
+#ifndef PLONG_CPU_H
+#define PLONG_CPU_H
+
+#include <stdint.h>
+#include "json.h"
+
+#define CPU_FIELDS(x) \
+ x(user) \
+ x(nice) \
+ x(system) \
+ x(idle) \
+ x(iowait) \
+ x(irq) \
+ x(softirq)
+
+struct cpu {
+#define X(f) uintmax_t f;
+CPU_FIELDS(X)
+#undef X
+};
+
+int cpu_init(struct cpu *c);
+int cpu_poll(struct cpu *c, struct json *j);
+
+#endif
diff --git a/http.c b/http.c
@@ -0,0 +1,116 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <netdb.h>
+#include <errno.h>
+#include <sys/poll.h>
+
+#include "http.h"
+#include "minitls.h"
+#include "buf.h"
+
+int http_post(const char *server,
+ const char *port,
+ const char *path,
+ const struct buf *b,
+ struct buf *reply,
+ int *af,
+ struct tls *ctx)
+{
+ char buffer[1024];
+ struct addrinfo *res;
+ struct addrinfo *i;
+ struct addrinfo hints = { .ai_family = *af, .ai_socktype = SOCK_STREAM };
+ ssize_t r;
+ ssize_t s;
+ int sock;
+
+ if ((r = getaddrinfo(server, port, &hints, &res)))
+ return r;
+
+ r = ENOENT;
+ for (i = res; i; i = i->ai_next) {
+ sock = socket(i->ai_family, i->ai_socktype, i->ai_protocol);
+
+ *af = i->ai_family;
+ if (!(r = connect(sock, i->ai_addr, i->ai_addrlen)))
+ break;
+ close(sock);
+ }
+
+ freeaddrinfo(res);
+ if (!i)
+ return r;
+
+ if ((r = snprintf(buffer, sizeof(buffer),
+ "POST %s HTTP/1.0\r\nHost: %s\r\nContent-Type: application/json\r\nContent-Length: %zu\r\n\r\n",
+ path, server, b->end - b->data)) >= (int)sizeof(buffer)) {
+ close(sock);
+ return ENOMEM;
+ }
+
+ if (ctx) {
+ if (tls_connect_socket(ctx, sock, server)) {
+ close(sock);
+ return -1;
+ }
+
+ if ((s = tls_write(ctx, buffer, r)) < r) {
+ tls_close(ctx);
+ tls_reset(ctx);
+ tls_configure(ctx, NULL);
+ close(sock);
+ return -1;
+ }
+ } else if ((s = write(sock, buffer, r)) < r) {
+ close(sock);
+ return -1;
+ }
+
+ if (ctx) {
+ if ((s = tls_write(ctx, b->data, b->end - b->data)) < b->end - b->data) {
+ tls_close(ctx);
+ tls_reset(ctx);
+ tls_configure(ctx, NULL);
+ close(sock);
+ return -1;
+ }
+ } else if ((s = write(sock, b->data, b->end - b->data)) < b->end - b->data) {
+ close(sock);
+ return -1;
+ }
+
+ if (reply) {
+ do {
+ if (ctx)
+ r = tls_read(ctx, reply->data, reply->end - reply->data);
+ else
+ r = read(sock, reply->data, reply->end - reply->data);
+ if (r < 0) {
+ if (ctx) {
+ if (r == TLS_WANT_POLLIN) {
+ poll(&(struct pollfd){ .fd = sock, .events = POLLIN }, 1, 0);
+ continue;
+ }
+
+ tls_close(ctx);
+ tls_reset(ctx);
+ tls_configure(ctx, NULL);
+ }
+ close(sock);
+ return -1;
+ }
+ reply->data += r;
+ } while (r && reply->data < reply->end);
+ }
+
+ if (ctx) {
+ tls_close(ctx);
+ tls_reset(ctx);
+ tls_configure(ctx, NULL);
+ }
+
+ close(sock);
+
+ return s;
+}
+
diff --git a/http.h b/http.h
@@ -0,0 +1,15 @@
+#ifndef PLONG_HTTP_H
+#define PLONG_HTTP_H
+
+#include "minitls.h"
+#include "buf.h"
+
+int http_post(const char *server,
+ const char *port,
+ const char *path,
+ const struct buf *b,
+ struct buf *reply,
+ int *af,
+ struct tls *ctx);
+
+#endif
diff --git a/json.c b/json.c
@@ -0,0 +1,291 @@
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include "json.h"
+
+enum mode {
+ MODE_NONE = 0,
+ MODE_OBJECT_FIRST,
+ MODE_OBJECT,
+ MODE_OBJECT_VALUE,
+ MODE_ARRAY_FIRST,
+ MODE_ARRAY,
+};
+
+static enum mode _json_mode(struct json *j)
+{
+ if (j->depth == 0)
+ return MODE_NONE;
+ return j->modes[j->depth - 1];
+}
+
+static int _json_kw(struct json *j, const char *value, size_t vl)
+{
+ int r;
+
+ switch (_json_mode(j)) {
+ case MODE_ARRAY:
+ if ((r = buf_char(&j->b, ',')))
+ return r;
+ break;
+
+ case MODE_ARRAY_FIRST:
+ j->modes[j->depth - 1] = MODE_ARRAY;
+ break;
+
+ case MODE_OBJECT_VALUE:
+ j->modes[j->depth - 1] = MODE_OBJECT;
+ break;
+
+ default:
+ return EINVAL;
+ }
+
+ if (j->b.data + vl >= j->b.end)
+ return ENOMEM;
+ memcpy(j->b.data, value, vl);
+ j->b.data += vl;
+
+ return 0;
+}
+
+int _json_string(struct json *j, const char *value)
+{
+ int r;
+
+ switch (_json_mode(j)) {
+ case MODE_ARRAY:
+ case MODE_OBJECT:
+ if ((r = buf_char(&j->b, ',')))
+ return r;
+ break;
+
+ case MODE_ARRAY_FIRST:
+ j->modes[j->depth - 1] = MODE_ARRAY;
+ break;
+
+ case MODE_OBJECT_FIRST:
+ case MODE_OBJECT_VALUE:
+ break;
+
+ default:
+ return EINVAL;
+ }
+
+ if ((r = buf_char(&j->b, '"')))
+ return r;
+ for (; *value; value++) {
+ char c;
+ switch (*value) {
+ case '\r':
+ c = 'r';
+ break;
+ case '\n':
+ c = 'n';
+ break;
+ case '\b':
+ c = 'b';
+ break;
+ case '\f':
+ c = 'f';
+ break;
+
+ case '\\':
+ case '"':
+ c = *value;
+ break;
+
+ default:
+ if ((r = buf_char(&j->b, *value)))
+ return ENOMEM;
+ continue;
+ }
+ if ((r = buf_char(&j->b, '\\')) ||
+ (r = buf_char(&j->b, c)))
+ return ENOMEM;
+ }
+
+ return buf_char(&j->b, '"');
+}
+
+int json_init(struct json *j, struct buf *b)
+{
+ j->b = *b;
+ j->buffer = b->data;
+ j->depth = 0;
+
+ return 0;
+}
+
+int json_obj_open(struct json *j)
+{
+ int r;
+
+ switch (_json_mode(j)) {
+ case MODE_OBJECT_FIRST:
+ case MODE_OBJECT:
+ return EINVAL;
+
+ case MODE_ARRAY:
+ if ((r = buf_char(&j->b, ',')))
+ return r;
+ break;
+
+ default:
+ break;
+ }
+
+ if (&j->modes[j->depth] == 1[&j->modes])
+ return ENOMEM;
+ j->modes[j->depth++] = MODE_OBJECT_FIRST;
+ return buf_char(&j->b, '{');
+}
+
+int json_obj_close(struct json *j)
+{
+ switch (_json_mode(j)) {
+ case MODE_OBJECT_FIRST:
+ case MODE_OBJECT:
+ break;
+
+ default:
+ return EINVAL;
+ }
+
+ j->depth--;
+ if (_json_mode(j) == MODE_OBJECT_VALUE)
+ j->modes[j->depth - 1] = MODE_OBJECT;
+ return buf_char(&j->b, '}');
+}
+
+int json_string(struct json *j, const char *value)
+{
+ int r;
+
+ switch (_json_mode(j)) {
+ case MODE_OBJECT_FIRST:
+ case MODE_OBJECT:
+ return EINVAL;
+
+ default:
+ break;
+ }
+
+ if ((r = _json_string(j, value)))
+ return r;
+ if (_json_mode(j) == MODE_OBJECT_VALUE)
+ j->modes[j->depth - 1] = MODE_OBJECT;
+ return 0;
+}
+
+int json_obj_key(struct json *j, const char *key)
+{
+ int r;
+
+ switch (_json_mode(j)) {
+ case MODE_OBJECT:
+ case MODE_OBJECT_FIRST:
+ break;
+
+ default:
+ return EINVAL;
+ }
+
+ if ((r = _json_string(j, key)))
+ return r;
+ j->modes[j->depth - 1] = MODE_OBJECT_VALUE;
+ return buf_char(&j->b, ':');
+}
+
+int json_arr_open(struct json *j) {
+ int r;
+
+ switch (_json_mode(j)) {
+ case MODE_OBJECT_FIRST:
+ case MODE_OBJECT:
+ return EINVAL;
+
+ case MODE_ARRAY:
+ if ((r = buf_char(&j->b, ',')))
+ return r;
+ break;
+
+ default:
+ break;
+ }
+
+ if (&j->modes[j->depth] == 1[&j->modes])
+ return ENOMEM;
+ j->modes[j->depth++] = MODE_ARRAY_FIRST;
+ return buf_char(&j->b, '[');
+}
+
+int json_arr_close(struct json *j)
+{
+ switch (_json_mode(j)) {
+ case MODE_ARRAY_FIRST:
+ case MODE_ARRAY:
+ break;
+
+ default:
+ return EINVAL;
+ }
+
+ j->depth--;
+ if (_json_mode(j) == MODE_OBJECT_VALUE)
+ j->modes[j->depth - 1] = MODE_OBJECT;
+ return buf_char(&j->b, ']');
+}
+
+int json_bool(struct json *j, int v)
+{
+ if (v)
+ return _json_kw(j, "true", sizeof("true") - 1);
+ return _json_kw(j, "false", sizeof("false") - 1);
+}
+
+int json_null(struct json *j)
+{
+ return _json_kw(j, "null", sizeof("null") - 1);
+}
+
+int json_int(struct json *j, intmax_t val)
+{
+ int r;
+
+ switch (_json_mode(j)) {
+ case MODE_ARRAY:
+ if ((r = buf_char(&j->b, ',')))
+ return r;
+ break;
+
+ case MODE_ARRAY_FIRST:
+ j->modes[j->depth - 1] = MODE_ARRAY;
+ break;
+
+ case MODE_OBJECT_VALUE:
+ j->modes[j->depth - 1] = MODE_OBJECT;
+ break;
+
+ default:
+ return EINVAL;
+ }
+
+ r = snprintf(j->b.data, j->b.end - j->b.data, "%jd", val);
+ if (j->b.data + r >= j->b.end - 1)
+ return ENOMEM;
+ j->b.data += r;
+
+ return 0;
+}
+
+int json_fini(struct json *j, struct buf *b)
+{
+ if (j->depth != 0)
+ return EINVAL;
+ b->data = j->buffer;
+ b->end = j->b.data;
+
+ return 0;
+}
+
diff --git a/json.h b/json.h
@@ -0,0 +1,35 @@
+#ifndef PLONG_JSON_H
+#define PLONG_JSON_H
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "buf.h"
+
+#define JSON_MAX_DEPTH 16
+
+struct json {
+ char *buffer;
+ struct buf b;
+ int modes[JSON_MAX_DEPTH];
+ size_t depth;
+};
+
+int json_init(struct json *j, struct buf *b);
+int json_fini(struct json *j, struct buf *b);
+
+int json_obj_open(struct json *j);
+int json_obj_key(struct json *j, const char *key);
+int json_obj_close(struct json *j);
+
+int json_arr_open(struct json *j);
+int json_arr_close(struct json *j);
+
+int json_bool(struct json *j, int v);
+int json_null(struct json *j);
+
+int json_int(struct json *j, intmax_t val);
+
+int json_string(struct json *j, const char *value);
+
+#endif
diff --git a/mem.c b/mem.c
@@ -0,0 +1,44 @@
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include "mem.h"
+
+int mem_init(struct mem *m)
+{
+ (void)m;
+ return 0;
+}
+
+int mem_poll(struct mem *m, struct json *j)
+{
+ int r = 0;
+ char line[256];
+ uintmax_t val;
+ FILE *f = fopen("/proc/meminfo", "r");
+
+ (void)m;
+
+ if (!f)
+ return errno;
+ while (fgets(line, sizeof(line), f)) {
+#define X(a, b) \
+ if (!memcmp(line, #a ":", sizeof(#a ":") - 1)) { \
+ if (sscanf(line + sizeof(#a ":") - 1, "%ju", &val) != 1) { \
+ r = ENODATA; \
+ goto out; \
+ } \
+ if ((r = json_obj_key(j, #b))) \
+ goto out; \
+ if ((r = json_int(j, val))) \
+ goto out; \
+ continue; \
+ }
+ MEM_FIELDS(X)
+#undef X
+ }
+out:
+ fclose(f);
+ return 0;
+}
diff --git a/mem.h b/mem.h
@@ -0,0 +1,24 @@
+#ifndef PLONG_MEM_H
+#define PLONG_MEM_H
+
+#include <stdint.h>
+#include "json.h"
+
+#define MEM_FIELDS(x) \
+ x(MemTotal, total) \
+ x(MemFree, free) \
+ x(MemAvailable, available) \
+ x(Buffers, buffers) \
+ x(Cached, cached) \
+ x(SwapCached, swapcached) \
+ x(SwapTotal, swaptotal) \
+ x(SwapFree, swapfree)
+
+struct mem {
+ int dummy;
+};
+
+int mem_init(struct mem *c);
+int mem_poll(struct mem *c, struct json *j);
+
+#endif
diff --git a/minitls.h b/minitls.h
@@ -0,0 +1,121 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Santtu Lakkala
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#ifndef MINITLS_H
+#define MINITLS_H
+#include <openssl/ssl.h>
+#include <stdbool.h>
+
+#define TLS_WANT_POLLIN -2
+#define TLS_WANT_POLLOUT -3
+
+struct tls {
+ SSL_CTX *ctx;
+ SSL *ssl;
+ int err;
+};
+
+static inline struct tls *tls_client(void)
+{
+ static bool ready = false;
+ const SSL_METHOD *method;
+ struct tls *rv = malloc(sizeof(*rv));
+
+ if (!rv)
+ return NULL;
+
+ if (!ready) {
+ SSL_library_init();
+ OpenSSL_add_all_algorithms();
+ ready = true;
+ }
+
+ method = TLS_client_method();
+ rv->ctx = SSL_CTX_new(method);
+ rv->ssl = NULL;
+ SSL_CTX_set_default_verify_paths(rv->ctx);
+
+ return rv;
+}
+
+static inline ssize_t tls_read(struct tls *ctx, void *buf, size_t buflen)
+{
+ ssize_t r = SSL_read(ctx->ssl, buf, buflen);
+ if (r >= 0)
+ return r;
+ ctx->err = r;
+ r = SSL_get_error(ctx->ssl, r);
+ if (r == SSL_ERROR_WANT_READ)
+ return TLS_WANT_POLLIN;
+ if (r == SSL_ERROR_WANT_WRITE)
+ return TLS_WANT_POLLOUT;
+ return -1;
+}
+
+static inline ssize_t tls_write(struct tls *ctx, const void *buf, size_t buflen)
+{
+ ssize_t r = SSL_write(ctx->ssl, buf, buflen);
+ if (r >= 0)
+ return r;
+ ctx->err = r;
+ r = SSL_get_error(ctx->ssl, r);
+ if (r == SSL_ERROR_WANT_READ)
+ return TLS_WANT_POLLIN;
+ if (r == SSL_ERROR_WANT_WRITE)
+ return TLS_WANT_POLLOUT;
+ return -1;
+}
+
+static inline void tls_reset(struct tls *ctx)
+{
+ SSL_free(ctx->ssl);
+ ctx->ssl = NULL;
+}
+
+static inline int tls_connect_socket(struct tls *ctx, int fd, const char *servername)
+{
+ if (!(ctx->ssl = SSL_new(ctx->ctx)))
+ return -1;
+
+ SSL_set_fd(ctx->ssl, fd);
+ SSL_set_tlsext_host_name(ctx->ssl, servername);
+ if ((ctx->err = SSL_connect(ctx->ssl)) != 1)
+ return -1;
+ return 0;
+}
+
+static inline int tls_close(struct tls *ctx)
+{
+ SSL_shutdown(ctx->ssl);
+ return 0;
+}
+
+static inline void tls_free(struct tls *ctx)
+{
+ SSL_CTX_free(ctx->ctx);
+ free(ctx);
+}
+
+#define tls_configure(x, y)
+
+#endif
diff --git a/net.c b/net.c
@@ -0,0 +1,128 @@
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <net/if.h>
+#include "util.h"
+#include "net.h"
+
+#define _STRINGIFY(x) #x
+#define STRINGIFY(x) _STRINGIFY(x)
+
+static int _net_read(struct net *n, FILE *f, char *linebuf, size_t bsz)
+{
+ size_t i;
+ char namebuf[IF_NAMESIZE + 1];
+
+ for (i = 0; fgets(linebuf, bsz, f); i++) {
+ if (sscanf(linebuf, " %" STRINGIFY(IF_NAMESIZE) "[^:]:"
+ UINT_SCAN(NET_FIELDS), namebuf
+#define X(d, na) , &n->ifaces[i].d ## na
+ NET_FIELDS(X)
+#undef X
+ ) < COUNT(NET_FIELDS))
+ return ENODATA;
+ if (!(n->ifaces[i].ifidx = if_nametoindex(namebuf)))
+ i--;
+ }
+ n->n = i;
+
+ clock_gettime(CLOCK_REALTIME, &n->stamp);
+
+ return 0;
+}
+
+int net_init(struct net *n)
+{
+ char linebuf[512];
+ char *i;
+ FILE *f = fopen("/proc/net/dev", "r");
+ int r = ENODATA;
+
+ if (!f)
+ return errno;
+ if (!fgets(linebuf, sizeof(linebuf), f) ||
+ !fgets(linebuf, sizeof(linebuf), f))
+ goto out;
+ if (!*(i = linebuf + strcspn(linebuf, "|")))
+ goto out;
+ i++;
+#define X(d, n) \
+ if (memcmp(i, #n, sizeof(#n) - 1)) \
+ goto out; \
+ i += sizeof(#n) - 1; \
+ if (*i != ' ' && *i != '|' && *i != '\n') \
+ goto out; \
+ i += strspn(i, " ");
+
+ NET_FIELDS_RX(X)
+
+ if (*i++ != '|')
+ goto out;
+
+ NET_FIELDS_TX(X)
+#undef X
+
+ if (*i != '\n')
+ goto out;
+ r = _net_read(n, f, linebuf, sizeof(linebuf));
+out:
+ fclose(f);
+ return r;
+}
+
+int net_poll(struct net *n, struct json *j)
+{
+ int r = ENODATA;
+ FILE *f = fopen("/proc/net/dev", "r");
+ char linebuf[512];
+ struct net n0 = *n;
+ size_t oi;
+ size_t ni;
+
+ if (!f)
+ return errno;
+ if (!fgets(linebuf, sizeof(linebuf), f) ||
+ !fgets(linebuf, sizeof(linebuf), f))
+ goto out;
+
+ if ((r = _net_read(n, f, linebuf, sizeof(linebuf))))
+ goto out;
+
+ for (oi = 0, ni = 0; ni < n->n; ni++) {
+ char namebuf[IF_NAMESIZE + 1];
+ while (oi < n0.n && n0.ifaces[oi].ifidx < n->ifaces[ni].ifidx)
+ oi++;
+ if (!if_indextoname(n->ifaces[ni].ifidx, namebuf))
+ continue;
+ if ((r = json_obj_key(j, "milliseconds")))
+ goto out;
+ if ((r = json_int(j, ((intmax_t)n->stamp.tv_sec - n0.stamp.tv_sec) * 1000 + ((intmax_t)n->stamp.tv_nsec - n0.stamp.tv_nsec) / 1000000)))
+ goto out;
+ if ((r = json_obj_key(j, namebuf)))
+ goto out;
+ if ((r = json_obj_open(j)))
+ goto out;
+ if (oi < n0.n && n->ifaces[ni].ifidx == n0.ifaces[oi].ifidx) {
+#define X(d, na) \
+ if ((r = json_obj_key(j, #d #na))) \
+ goto out; \
+ if ((r = json_int(j, n->ifaces[ni].d ## na - n0.ifaces[oi].d ## na))) \
+ goto out;
+ NET_FIELDS(X)
+#undef X
+ } else {
+#define X(d, na) \
+ if ((r = json_obj_key(j, #d #na))) \
+ goto out; \
+ if ((r = json_int(j, 0))) \
+ goto out;
+ NET_FIELDS(X)
+#undef X
+ }
+ if ((r = json_obj_close(j)))
+ goto out;
+ }
+out:
+ fclose(f);
+ return r;
+}
diff --git a/net.h b/net.h
@@ -0,0 +1,47 @@
+#ifndef PLONG_NET_H
+#define PLONG_NET_H
+
+#include <stdint.h>
+#include <net/if.h>
+#include <time.h>
+#include "json.h"
+
+#define IF_MAX 32
+
+#define NET_FIELDS_RX(x) \
+ x(rx, bytes) \
+ x(rx, packets) \
+ x(rx, errs) \
+ x(rx, drop) \
+ x(rx, fifo) \
+ x(rx, frame) \
+ x(rx, compressed) \
+ x(rx, multicast)
+#define NET_FIELDS_TX(x) \
+ x(tx, bytes) \
+ x(tx, packets) \
+ x(tx, errs) \
+ x(tx, drop) \
+ x(tx, fifo) \
+ x(tx, colls) \
+ x(tx, carrier) \
+ x(tx, compressed)
+#define NET_FIELDS(x) \
+ NET_FIELDS_RX(x) \
+ NET_FIELDS_TX(x)
+
+struct net {
+ struct timespec stamp;
+ struct {
+ unsigned int ifidx;
+#define X(d, n) uintmax_t d ## n;
+NET_FIELDS(X)
+#undef X
+ } ifaces[IF_MAX];
+ size_t n;
+};
+
+int net_init(struct net *n);
+int net_poll(struct net *n, struct json *j);
+
+#endif
diff --git a/plong.c b/plong.c
@@ -0,0 +1,64 @@
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "minitls.h"
+#include "json.h"
+#include "http.h"
+#include "cpu.h"
+#include "mem.h"
+#include "net.h"
+
+int main(int argc, char **argv)
+{
+ char buffer[4096];
+ struct buf b = { buffer, 1[&buffer] };
+ struct buf bjson = b;
+ struct buf breply = b;
+ struct json j;
+ struct tls *ctx;
+
+ struct cpu c;
+ struct mem m;
+ struct net n;
+
+#define E(v) do { int r = v; if (r) { fprintf(stderr, "%s failed: %s\n", #v, strerror(r)); }} while (0)
+ E(net_init(&n));
+ E(cpu_init(&c));
+ E(mem_init(&m));
+
+ sleep(5);
+
+ E(json_init(&j, &b));
+ E(json_obj_open(&j));
+
+ E(json_obj_key(&j, "cpu"));
+ E(json_obj_open(&j));
+ E(cpu_poll(&c, &j));
+ E(json_obj_close(&j));
+
+ E(json_obj_key(&j, "mem"));
+ E(json_obj_open(&j));
+ E(mem_poll(&m, &j));
+ E(json_obj_close(&j));
+
+ E(json_obj_key(&j, "net"));
+ E(json_obj_open(&j));
+ E(net_poll(&n, &j));
+ E(json_obj_close(&j));
+
+ E(json_obj_close(&j));
+
+ E(json_fini(&j, &bjson));
+
+ ctx = tls_client();
+ http_post("inz.fi", "443", "/plop.php", &bjson, &breply, &(int){ AF_UNSPEC }, ctx);
+
+ fwrite(b.data, breply.data - b.data, 1, stdout);
+ puts("");
+
+ tls_free(ctx);
+
+ return 0;
+}
diff --git a/util.h b/util.h
@@ -0,0 +1,11 @@
+#ifndef UTIL_H
+#define UTIL_H
+
+#define UINTFMT(...) "%ju"
+#define UINT_SCAN(X) X(UINTFMT)
+#define INTFMT(...) "%jd"
+#define INT_SCAN(X) X(INTFMT)
+#define COUNTER(...) + 1
+#define COUNT(X) (0 X(COUNTER))
+
+#endif