plong

Unnamed repository; edit this file 'description' to name the repository.
git clone https://git.inz.fi/plong
Log | Files | Refs

commit 3c072d9e64faaf80517ede4e0e26f68d3ccd39d1
Author: Santtu Lakkala <santtu.lakkala@unikie.com>
Date:   Fri, 22 Nov 2024 13:48:54 +0200

Initial import

Diffstat:
AMakefile | 48++++++++++++++++++++++++++++++++++++++++++++++++
Abuf.c | 11+++++++++++
Abuf.h | 13+++++++++++++
Acpu.c | 43+++++++++++++++++++++++++++++++++++++++++++
Acpu.h | 25+++++++++++++++++++++++++
Ahttp.c | 116+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahttp.h | 15+++++++++++++++
Ajson.c | 291++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ajson.h | 35+++++++++++++++++++++++++++++++++++
Amem.c | 44++++++++++++++++++++++++++++++++++++++++++++
Amem.h | 24++++++++++++++++++++++++
Aminitls.h | 121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Anet.c | 128+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Anet.h | 47+++++++++++++++++++++++++++++++++++++++++++++++
Aplong.c | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Autil.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