0e2d4f07c3
struct UCLogDestination, and similar for other structs)
310 lines
7.4 KiB
C
310 lines
7.4 KiB
C
#define DEBUG
|
|
#include <getopt.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/queue.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <ucore/logging.h>
|
|
#include <ucore/logging.h>
|
|
#include <ucore/iomux.h>
|
|
#include <ucore/utils.h>
|
|
#include <ucore/pack.h>
|
|
#include <ucore/tailq.h>
|
|
|
|
#define HB_MAGIX 0xBEA7BEA7
|
|
|
|
#define LMAIN 0
|
|
#define PEER_HB_SEC 5
|
|
#define PEER_HB_MISS 3
|
|
|
|
static struct UCLogModule modules[] = {
|
|
{
|
|
.id = LMAIN,
|
|
.short_name = "MAIN",
|
|
.long_name = "Main logging module",
|
|
.log_level = UC_LL_DEBUG
|
|
}
|
|
};
|
|
|
|
static struct UCLogModules log_modules = {
|
|
.mods = modules,
|
|
.cnt = ARRAY_SIZE(modules)
|
|
};
|
|
|
|
enum PeerState {
|
|
PeerUnknown,
|
|
PeerAlive,
|
|
PeerDead
|
|
};
|
|
|
|
enum PeerEvent {
|
|
PeerReceived,
|
|
PeerTimedout,
|
|
};
|
|
|
|
struct HBPeer {
|
|
struct UCTimer timer;
|
|
struct sockaddr_in peer_addr;
|
|
time_t last_ok;
|
|
enum PeerState state;
|
|
//TAILQ_ENTRY(HBPeer) entry;
|
|
struct TailQ entry;
|
|
};
|
|
|
|
struct HBContext {
|
|
struct IOMuxFD sock_fd;
|
|
//TAILQ_HEAD(, HBPeer) peers;
|
|
struct TailQ peers; //of HBPeer
|
|
struct IOMux *mux;
|
|
};
|
|
|
|
struct HBPeer *hb_peer_find(struct HBContext *ctx, const struct sockaddr_in *peer_addr);
|
|
void peer_send(struct HBContext *ctx, struct HBPeer *peer);
|
|
void peer_do_state(struct HBPeer *peer, enum PeerEvent event);
|
|
|
|
|
|
void peer_timer_cb(struct UCTimers *timers, struct UCTimer *timer)
|
|
{
|
|
struct HBPeer *peer = UC_CONTAINER_OF(timer, struct HBPeer, timer);
|
|
struct HBContext *ctx = timer->cookie_ptr;
|
|
time_t now;
|
|
|
|
uc_timers_add(timers, timer, PEER_HB_MISS , 0);
|
|
now = time(NULL);
|
|
|
|
if (now - peer->last_ok > PEER_HB_MISS * PEER_HB_SEC) {
|
|
peer_do_state(peer, PeerTimedout);
|
|
}
|
|
|
|
peer_send(ctx, peer);
|
|
}
|
|
|
|
void peer_add_addr(struct HBContext *ctx, const struct sockaddr_in *addr)
|
|
{
|
|
struct HBPeer *peer = hb_peer_find(ctx, addr);
|
|
int sec;
|
|
int usec;
|
|
|
|
if (peer != NULL) {
|
|
UC_LOGF(UC_LL_INFO, LMAIN, "Ignoring adding existing peer %s:%u\n",
|
|
inet_ntoa(addr->sin_addr), addr->sin_port);
|
|
return;
|
|
}
|
|
|
|
peer = calloc(1, sizeof *peer);
|
|
peer->peer_addr = *addr;
|
|
//TAILQ_INSERT_TAIL(&ctx->peers, peer, entry);
|
|
uc_tailq_insert_tail(&ctx->peers, &peer->entry);
|
|
|
|
peer->timer.callback = peer_timer_cb;
|
|
peer->timer.cookie_ptr = ctx;
|
|
peer->state = PeerUnknown;
|
|
|
|
sec = rand() % PEER_HB_SEC;
|
|
usec = rand() % 1000000;
|
|
|
|
uc_timers_add(iomux_get_timers(ctx->mux), &peer->timer, sec, usec);
|
|
}
|
|
|
|
void peer_add(struct HBContext *ctx, const char *host, uint16_t port)
|
|
{
|
|
struct sockaddr_in peer_addr;
|
|
|
|
UC_DEBUGF(LMAIN, "Adding peer %s:%u\n", host, port);
|
|
memset(&peer_addr, 0, sizeof peer_addr);
|
|
|
|
peer_addr.sin_family = AF_INET;
|
|
peer_addr.sin_addr.s_addr = inet_addr(host);
|
|
peer_addr.sin_port = htons(port);
|
|
|
|
peer_add_addr(ctx, &peer_addr);
|
|
}
|
|
|
|
void peer_send(struct HBContext *ctx, struct HBPeer *peer)
|
|
{
|
|
uint8_t buf[4];
|
|
ssize_t rc;
|
|
|
|
uc_pack_32_be(HB_MAGIX, buf);
|
|
|
|
UC_DEBUGF(LMAIN, "Sending to %s:%u\n", inet_ntoa(peer->peer_addr.sin_addr),
|
|
ntohs(peer->peer_addr.sin_port));
|
|
|
|
rc = sendto(ctx->sock_fd.fd, buf, sizeof buf, 0,
|
|
(struct sockaddr*)&peer->peer_addr, sizeof peer->peer_addr);
|
|
if (rc == -1) {
|
|
UC_DEBUGF(LMAIN, "Could not send packet: %s\n", strerror(errno));
|
|
}
|
|
}
|
|
|
|
struct HBPeer *hb_peer_find(struct HBContext *ctx, const struct sockaddr_in *peer_addr)
|
|
{
|
|
struct HBPeer *peer = NULL;
|
|
|
|
UC_TAILQ_FOREACH_CONTAINER(peer, entry, &ctx->peers) {
|
|
if (peer_addr->sin_addr.s_addr == peer->peer_addr.sin_addr.s_addr
|
|
&& peer_addr->sin_port == peer->peer_addr.sin_port) {
|
|
|
|
return peer;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void peer_do_state(struct HBPeer *peer, enum PeerEvent event)
|
|
{
|
|
if (peer->state == PeerUnknown || peer->state == PeerDead) {
|
|
if (event == PeerReceived) {
|
|
UC_LOGF(UC_LL_INFO, LMAIN, "Peer is now alive\n");
|
|
peer->state = PeerAlive;
|
|
}
|
|
} else if (peer->state == PeerAlive) {
|
|
if (event == PeerTimedout) {
|
|
UC_LOGF(UC_LL_INFO, LMAIN, "Peer is dead !\n");
|
|
peer->state = PeerDead;
|
|
}
|
|
}
|
|
|
|
if (peer->state == PeerUnknown && event == PeerTimedout) {
|
|
peer->state = PeerDead;
|
|
}
|
|
}
|
|
|
|
void peer_received(struct HBPeer *peer)
|
|
{
|
|
peer->last_ok = time(NULL);
|
|
peer_do_state(peer, PeerReceived);
|
|
}
|
|
|
|
void hb_read(struct IOMux *mux, struct IOMuxFD *fd, unsigned int event)
|
|
{
|
|
struct HBContext *ctx = UC_CONTAINER_OF(fd, struct HBContext, sock_fd);
|
|
ssize_t rc;
|
|
struct sockaddr_in peer_addr;
|
|
socklen_t peer_len = sizeof peer_addr;
|
|
uint8_t buf[128];
|
|
uint32_t magic;
|
|
struct HBPeer *peer;
|
|
|
|
rc = recvfrom(fd->fd, buf, sizeof buf, 0, (struct sockaddr*)&peer_addr, &peer_len);
|
|
if (rc == -1 && errno == EWOULDBLOCK) {
|
|
UC_DEBUGF(LMAIN, "recvfrom EWOULDBLOCK\n");
|
|
return;
|
|
}
|
|
|
|
UC_LOGF(UC_LL_INFO, LMAIN, "Message from %s:%u\n", inet_ntoa(peer_addr.sin_addr),
|
|
ntohs(peer_addr.sin_port));
|
|
|
|
if (rc < 4) {
|
|
UC_DEBUGF(LMAIN, "short read of %u\n", (unsigned)rc);
|
|
return;
|
|
}
|
|
|
|
magic = uc_unpack_32_be(buf);
|
|
if (magic != HB_MAGIX) {
|
|
UC_LOGF(UC_LL_WARNING, LMAIN, "Received unknown message, magic = 0x%X\n", magic);
|
|
return;
|
|
}
|
|
|
|
peer = hb_peer_find(ctx, &peer_addr);
|
|
if (peer == NULL) {
|
|
UC_LOGF(UC_LL_WARNING, LMAIN, "Found no peer\n");
|
|
return;
|
|
}
|
|
|
|
peer_received(peer);
|
|
}
|
|
|
|
void init_hb_context(struct HBContext *ctx, uint16_t local_port)
|
|
{
|
|
int sock_fd;
|
|
struct sockaddr_in addr;
|
|
|
|
memset(&addr, 0, sizeof addr);
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(local_port);
|
|
addr.sin_addr.s_addr = INADDR_ANY;
|
|
|
|
sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
|
if (sock_fd == -1) {
|
|
UC_LOGF(UC_LL_ERROR, LMAIN, "socket failed : %s\n", strerror(errno));
|
|
return;
|
|
|
|
}
|
|
|
|
if (bind(sock_fd, (struct sockaddr*)&addr, sizeof addr) != 0) {
|
|
UC_LOGF(UC_LL_ERROR, LMAIN, "Cannot bind socket to port %u : %s\n",
|
|
local_port, strerror(errno));
|
|
close(sock_fd);
|
|
return;
|
|
}
|
|
|
|
uc_tailq_init(&ctx->peers);
|
|
|
|
ctx->sock_fd.fd = sock_fd;
|
|
ctx->sock_fd.what = MUX_EV_READ;
|
|
ctx->sock_fd.callback = hb_read;
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
struct HBContext ctx;
|
|
struct IOMux *mux;
|
|
uint16_t local_port = 23456;
|
|
uint16_t remote_port = 23456;
|
|
int c;
|
|
struct UCLogDestination *dest;
|
|
|
|
srand(getpid());
|
|
|
|
uc_log_init(&log_modules);
|
|
dest = uc_log_new_stderr(UC_LL_DEBUG, 1);
|
|
uc_log_add_destination(dest);
|
|
|
|
mux = iomux_create(IOMUX_TYPE_DEFAULT);
|
|
ctx.mux = mux;
|
|
|
|
init_hb_context(&ctx, local_port);
|
|
|
|
while ((c = getopt(argc, argv, "m:p:P:a:")) != -1) {
|
|
switch (c) {
|
|
case 'p':
|
|
local_port = atoi(optarg);
|
|
break;
|
|
case 'P':
|
|
remote_port = atoi(optarg);
|
|
break;
|
|
case 'a':
|
|
peer_add(&ctx, optarg, remote_port);
|
|
break;
|
|
case 'm':
|
|
uc_log_destination_set_mask(dest, optarg);
|
|
break;
|
|
default:
|
|
printf("Unknown option -%c\n", c);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
UC_DEBUGF(LMAIN, "Creating IOMux\n");
|
|
|
|
iomux_register_fd(mux, &ctx.sock_fd);
|
|
|
|
iomux_run(mux);
|
|
|
|
|
|
return 0;
|
|
}
|