summaryrefslogblamecommitdiffstats
path: root/srv/d.c
blob: 377241ff405de1b9c2a5731088fa89bc7fe6bef8 (plain) (tree)
























































































































































                                                                                                                                                               
                           








                                                                                   
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/udp.h>
#include <poll.h>
#include <sys/types.h>
#include <error.h>
#include <endian.h>
#include <inttypes.h>
#include <signal.h>
#include <netdb.h>
#define S0(x) (x ? x : "")
struct entry {
	uint64_t time __attribute__((packed));
	uint64_t value __attribute__((packed));
} __attribute__((packed));
int samomor = 0;
void handle_me (int s __attribute__((unused))) {
	samomor++;
}
int main (int argc, char ** argv) {
	if (argc < 1+1)
		error_at_line(1, 0, __FILE__, __LINE__, "uporaba: %s db [port=35358] [bind=::] [src=]", S0(argv[0]));
	struct sigaction act = {
		.sa_handler = handle_me,
		.sa_flags = SA_RESTART
	};
	if (sigaction(SIGINT, &act, NULL) == -1)
		error_at_line(2, errno, __FILE__, __LINE__, "sigaction SIGINT");
	if (sigaction(SIGTERM, &act, NULL) == -1)
		error_at_line(3, errno, __FILE__, __LINE__, "sigaction SIGTERM");
#define DB argv[1]
	int port = 35358;
	if (argc >= 1+2)
		port = atoi(argv[2]);
	struct sockaddr_in6 bind_address = {
		.sin6_family = AF_INET6,
		.sin6_port = htons(port),
		.sin6_addr = in6addr_any
	};
	if (argc >= 1+3)
		switch (inet_pton(AF_INET6, argv[3], &bind_address.sin6_addr)) {
			case 0:
				error_at_line(4, 0, __FILE__, __LINE__, "!inet_pton(AF_INET6, argv[3], &bind_address.sin6_addr)");
				break;
			case -1:
				error_at_line(5, errno, __FILE__, __LINE__, "inet_pton");
		}
	struct sockaddr_in6 src = {
		.sin6_family = AF_INET6,
		.sin6_port = htons(port),
		.sin6_addr = in6addr_any
	};
	int whitelist_src = 0;
	if (argc >= 1+4) {
		whitelist_src++;
		switch (inet_pton(AF_INET6, argv[4], &src.sin6_addr)) {
			case 0:
				error_at_line(6, 0, __FILE__, __LINE__, "!inet_pton(AF_INET6, argv[4], &src.sin6_addr)");
				break;
			case -1:
				error_at_line(7, errno, __FILE__, __LINE__, "inet_pton");
		}
	}
	int udp = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
	if (udp == -1)
		error_at_line(10, errno, __FILE__, __LINE__, "socket");
	FILE * db = NULL;
	int z = 1;
	int r = 0;
	if (setsockopt(udp, SOL_SOCKET, SO_BROADCAST, &z, sizeof z) == -1) {
		error_at_line(0, errno, __FILE__, __LINE__, "setsockopt");
		r = 11;
		goto r;
	}
	if (bind(udp, (struct sockaddr *) &bind_address, sizeof bind_address)) {
		error_at_line(0, errno, __FILE__, __LINE__, "bind");
		r = 12;
		goto r;
	}
	if (!(db = fopen(DB, "a"))) {
		error_at_line(0, errno, __FILE__, __LINE__, "fopen");
		r = 13;
		goto r;
	}
	while (!samomor) {
		struct pollfd pollfd = {
			.fd = udp,
			.events = POLLIN | POLLERR | POLLHUP | POLLNVAL
		};
		if (poll(&pollfd, 1, -1) == -1) {
			error_at_line(0, errno, __FILE__, __LINE__, "poll");
			r = 14;
			goto r;
		}
		if ((pollfd.revents & POLLERR) || (pollfd.revents & POLLHUP) || (pollfd.revents & POLLNVAL)) {
			error_at_line(0, 0, __FILE__, __LINE__, "POLLERR || POLLHUP || POLLNVAL");
			r = 15;
			goto r;
		}
		struct sockaddr_storage sender;
		socklen_t sender_len = sizeof sender;
		char buf[512];
		ssize_t bytes = recvfrom(udp, buf, 256, MSG_DONTWAIT, (struct sockaddr *) &sender, &sender_len);
		if (bytes == -1) {
			error_at_line(0, errno, __FILE__, __LINE__, "recvfrom");
			r = 16;
			goto r;
		}
		struct timespec current_time;
		if (clock_gettime(CLOCK_REALTIME, &current_time) == -1) {
			error_at_line(0, errno, __FILE__, __LINE__, "clock_gettime");
			r = 17;
			goto r;
		}
		uint64_t current_time_us = current_time.tv_sec*1000000+current_time.tv_nsec/1000;
		unsigned long long int current_Wh = strtoull(buf, NULL, 10);
		struct entry entry = {
			.time = htobe64(current_time_us),
			.value = htobe64(current_Wh)
		};
		char sender_host[512];
		char sender_port[512];
		int gni = getnameinfo((struct sockaddr *) &sender, sender_len, sender_host, 512, sender_port, 512, NI_DGRAM | NI_NUMERICHOST | NI_NUMERICSERV);
		if (gni) {
			error_at_line(0, 0, __FILE__, __LINE__, "getnameinfo: %s", gai_strerror(gni));
			r = 19;
			goto r;
		}
		time_t sedaj = time(NULL);
		strcpy(buf, ctime(&sedaj));
		buf[strlen(buf)-1] = '\0';
		if (sender.ss_family != AF_INET6)
			if (whitelist_src) {
				fprintf(stderr, "[%s] %s:%s\tsender.ss_family != AF_INET6\n", buf, sender_host, sender_port);
				continue;
			}
		if (whitelist_src && memcmp(&((struct sockaddr_in6 *) &sender)->sin6_addr, &src.sin6_addr, sizeof(src.sin6_addr))) {
			fprintf(stderr, "[%s] %s:%s\tfailed src whitelist\n", buf, sender_host, sender_port);
			continue;
		}
		fprintf(stderr, "[%s] %s:%s\t%llu Wh\n", buf, sender_host, sender_port, current_Wh);
		if (fwrite(&entry, sizeof entry, 1, db) != 1) {
			error_at_line(0, 0, __FILE__, __LINE__, "fwrite");
			r = 18;
			goto r;
		}
		fflush(db);
	}
r:
	if (close(udp) == -1)
		error_at_line(19, errno, __FILE__, __LINE__, "close(udp)");
	if (db != NULL)
		if (fclose(db) == -1)
			error_at_line(20, errno, __FILE__, __LINE__, "fclose(db)");
	return r;
}