aboutsummaryrefslogtreecommitdiffstats
path: root/pingu_adm.c
blob: 9062e6e2a53eed185e8ae93f51939a54a539ef5b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157

#include <sys/socket.h>
#include <sys/un.h>

#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <ev.h>

#include "list.h"
#include "log.h"
#include "pingu_adm.h"
#include "pingu_iface.h"
#include "pingu_host.h"

static struct ev_io accept_io;

struct adm_conn {
        struct ev_io io;
        struct ev_timer timeout;
        int num_read;
        char cmd[512];
};

static void pingu_adm_free_conn(struct ev_loop *loop, struct adm_conn *rm)
{
	int fd = rm->io.fd;

	ev_io_stop(loop, &rm->io);
	ev_timer_stop(loop, &rm->timeout);
	close(fd);
	free(rm);
	log_debug("Admin connection closed");
}

static struct {
	const char *command;
	void (*handler)(int fd, char *args);
} adm_handler[] = {
	{ "host-status",	pingu_host_dump_status },
	{ "gateway-status",	pingu_iface_dump_status },
	{ "pings",		pingu_iface_dump_pings },
	{ "routes",		pingu_iface_dump_routes },
	{ NULL,			NULL }
};
	
static void pingu_adm_recv_cb (struct ev_loop *loop, struct ev_io *w,
			       int revents)
{
	struct adm_conn *conn = container_of(w, struct adm_conn, io);
	int len, i;
	char *args;

	len = recv(conn->io.fd, conn->cmd, sizeof(conn->cmd) - conn->num_read,
		   MSG_DONTWAIT);
	if (len < 0 && errno == EAGAIN)
		return;
	if (len <= 0)
		goto err;

	conn->num_read += len;
	if (conn->num_read >= sizeof(conn->cmd))
		goto err;
	if (conn->cmd[conn->num_read - 1] != '\n')
		goto err;

	conn->num_read--;
	conn->cmd[conn->num_read] = '\0';
	
	args = strchr(conn->cmd, ' ');
	if (args != NULL)
		*args++ = '\0';

	for (i = 0; adm_handler[i].command != NULL; i++) {
		if (strncmp(conn->cmd, adm_handler[i].command, len) != 0)
			continue;
		log_debug("Admin command: %s (args='%s')", conn->cmd, args ? args : "NULL");
		adm_handler[i].handler(conn->io.fd, args);
		conn->cmd[0] = '\0';
		conn->num_read = 0;
		break;
	}

	if (adm_handler[i].command == NULL)
		log_error("%s: unknown admim command", conn->cmd);

err:
	pingu_adm_free_conn(loop, conn);
}

static void pingu_adm_timeout_cb (struct ev_loop *loop, struct ev_timer *t,
				  int revents)
{
	log_debug("Admin connection timed out");
	pingu_adm_free_conn(loop, container_of(t, struct adm_conn, timeout));
}

static void pingu_adm_accept_cb(struct ev_loop *loop, struct ev_io *w,
				int revents)
{
	struct adm_conn *conn;
	struct sockaddr_storage from;
	socklen_t fromlen = sizeof(from);
	int fd;

	fd = accept(w->fd, (struct sockaddr *) &from, &fromlen);
	if (fd < 0) {
		log_perror("accept");
		return;
	}
	log_debug("New admin connection");
	fcntl(fd, F_SETFD, FD_CLOEXEC);
	conn = calloc(1, sizeof(struct adm_conn));

	ev_io_init(&conn->io, pingu_adm_recv_cb, fd, EV_READ);
	ev_io_start(loop, &conn->io);
        ev_timer_init(&conn->timeout, pingu_adm_timeout_cb, 10.0, 0.);
        ev_timer_start(loop, &conn->timeout);
}


int pingu_adm_init(struct ev_loop *loop, const char *socket_path)
{
	struct sockaddr_un sun;
	int fd;

	memset(&sun, 0, sizeof(sun));
	sun.sun_family = AF_UNIX;
	strncpy(sun.sun_path, socket_path, sizeof(sun.sun_path));

	fd = socket(AF_UNIX, SOCK_STREAM, 0);
	if (fd < 0) {
		log_perror("socket");
		return -1;
	}

	fcntl(fd, F_SETFD, FD_CLOEXEC);
	unlink(socket_path);
	if (bind(fd, (struct sockaddr *) &sun, sizeof(sun)) < 0)
		goto perr_close;

	if (listen(fd, 5) < 0)
		goto perr_close;

	ev_io_init(&accept_io, pingu_adm_accept_cb, fd, EV_READ);
	ev_io_start(loop, &accept_io);
	return 0;

perr_close:
	log_perror(socket_path);
	close(fd);
	return -1;

}