aboutsummaryrefslogblamecommitdiffstats
path: root/glougloud/server.c
blob: 4fb0be60106c264c59cc9d3de51e4fa1e71f239b (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11










                       
                 

                      
                            
























                                                                 
                                                








































                                                                  
                                                                             





































































                                                                                     
                                                                    









                                     





                                        















































                                                                           
                                  



                                
                                                          













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

#include <netinet/in.h>

#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <err.h>
#include <errno.h>
#include <time.h>

#include "glougloud.h"
#include "external/imsgev.h"

#define USER_TIMEOUT 300
#define USERTIMER 10

struct server {
	struct imsgev		iev;
	struct event		ev;

	struct event		usrtimer_ev;
	struct timeval		usrtimer_tv;
	time_t			time;
};
enum user_status {
	USER_REQUESTED,
	USER_ACCEPTED,
	USER_REFUSED
};
struct user {
	LIST_ENTRY(user)	entry;
	struct sockaddr_in	addr;
	enum user_status	status;
	time_t			lastseen;
};

static void	imsgev_main(struct imsgev *, int, struct imsg *);
static void imsgev_main_needfd(struct imsgev *);
static void	receive(int, short, void *);
static void	ev_usrtimer(int, short, void *);

struct server	*srv;
LIST_HEAD(, user) usr_list;

static void
sig_handler(int sig, short why, void *data)
{
	log_info("server: got signal %d", sig);
	if (sig == SIGINT || sig == SIGTERM)
		event_loopexit(NULL);
}

int
server_init(int fd[2])
{
	struct event ev_sigint, ev_sigterm, ev_sigchld, ev_sighup;
	int pid;

	pid = fork();
	if (pid < 0)
		fatal("server fork");
	if (pid > 0)
		return pid;

	setproctitle("server");
	event_init();
	close(fd[0]);

	signal_set(&ev_sigint, SIGINT, sig_handler, NULL);
	signal_set(&ev_sigterm, SIGTERM, sig_handler, NULL);
	signal_set(&ev_sigchld, SIGCHLD, sig_handler, NULL);
	signal_set(&ev_sighup, SIGHUP, sig_handler, NULL);
	signal_add(&ev_sigint, NULL);
	signal_add(&ev_sigterm, NULL);
	signal_add(&ev_sigchld, NULL);
	signal_add(&ev_sighup, NULL);
	signal(SIGPIPE, SIG_IGN);

	srv = xmalloc(sizeof(struct server));
	imsgev_init(&srv->iev, fd[1], NULL, imsgev_main, imsgev_main_needfd);
	srv->time = time(NULL);

	event_set(&srv->ev, net_socket, EV_READ|EV_PERSIST, receive, NULL);
	event_add(&srv->ev, NULL);

	srv->usrtimer_tv.tv_sec = USERTIMER;
	srv->usrtimer_tv.tv_usec = 0;
	evtimer_set(&srv->usrtimer_ev, ev_usrtimer, NULL);
	if (event_add(&srv->usrtimer_ev, &srv->usrtimer_tv) == -1)
		fatal("server: event_add usrtimr failed: %s", strerror(errno));

	droppriv();

	log_info("server: entering event loop");
	event_dispatch();

	log_info("server: exiting");
	exit(0);
}

static struct user *
finduser(struct sockaddr_in *remote) {
	struct user *usr;
	struct sockaddr_in *u;

	LIST_FOREACH(usr, &usr_list, entry) {
		u = &usr->addr;
		if (u->sin_addr.s_addr == remote->sin_addr.s_addr &&
			u->sin_port == remote->sin_port)
			return usr;
	}
	return NULL;
}

static void
srvconn(struct imsgev *iev, struct imsg *imsg)
{
	struct imsg_srvconn *res;
	struct user *usr;

	res = imsg->data;
	usr = finduser(&res->addr);
	if (!usr || usr->status != USER_REQUESTED)
		fatal("server: received srvconn response for inexistant connection");
	if (res->ok) {
		usr->status = USER_ACCEPTED;
	} else {
		usr->status = USER_REFUSED;
	}
}

static void
imsgev_main(struct imsgev *iev, int code, struct imsg *imsg)
{
	switch (code) {
	case IMSGEV_IMSG:
		log_debug("server: %s got imsg %i on fd %i",
			__func__, imsg->hdr.type, iev->ibuf.fd);
		switch (imsg->hdr.type) {
		case IMSG_SRVCONN_RES:
			srvconn(iev, imsg);
			break;
		default:
			fatal("%s, unexpected imsg %d", 
				__func__, imsg->hdr.type);
		}
		break;
	case IMSGEV_EREAD:
	case IMSGEV_EWRITE:
	case IMSGEV_EIMSG:
		fatal("server: imsgev read/write error (%d)", code);
		/* NOTREACHED */
		break;
	case IMSGEV_DONE:
		event_loopexit(NULL);
		/* NOTREACHED */
		break;
	}
}

static void
imsgev_main_needfd(struct imsgev *iev)
{
    fatal("server: imsgev_main_needfd");
}

static void
receive(int fd, short why, void *data)
{
	struct sockaddr_in	remote;
	struct imsg_srvconn	req;
	socklen_t		len;
	char			buf[16384];
	struct user		*usr;
	int			rv;

	len = sizeof(remote);
	rv = recvfrom(fd, buf, sizeof(buf), 0,
		(struct sockaddr *)&remote, &len);
	if (rv < 0) {
		log_warn("server: recvfrom failed");
		return;
	}
	usr = finduser(&remote);
	if (usr) {
		switch (usr->status) {
		case USER_ACCEPTED:
			log_tmp("server: data for existing user");
			break;
		case USER_REQUESTED:
			log_warn("server: data for not yet accepted user");
			break;
		case USER_REFUSED:
			log_warn("server: data for banned user !");
			break;
		}
	} else {
		log_debug("server: new user request");
		
		usr = xmalloc(sizeof(struct user));
		addrcpy(&usr->addr, &remote);
		usr->status = USER_REQUESTED;
		usr->lastseen = srv->time;
		LIST_INSERT_HEAD(&usr_list, usr, entry);

		addrcpy(&req.addr, &remote);
		req.deco = 0;
		imsgev_compose(&srv->iev, IMSG_SRVCONN_REQ, 0, 0, -1,
			&req, sizeof(req));
	}
}

static void
ev_usrtimer(int fd, short why, void *data)
{
	struct user *usr, *usrtmp;
	struct imsg_srvconn req;

	srv->time = time(NULL);

	LIST_FOREACH_SAFE(usr, &usr_list, entry, usrtmp) {
		if (srv->time > usr->lastseen + USER_TIMEOUT) {
			addrcpy(&req.addr, &usr->addr);
			req.deco = 1;
			imsgev_compose(&srv->iev, IMSG_SRVCONN_REQ, 0, 0, -1,
				&req, sizeof(req));

			LIST_REMOVE(usr, entry);
			free(usr);
		}
	}

	if (event_add(&srv->usrtimer_ev, &srv->usrtimer_tv) == -1)
		fatal("server: event_add usrtimer failed : %s", strerror(errno));
}