aboutsummaryrefslogtreecommitdiffstats
path: root/src/main.c
blob: 67c4559523364fcb589b6b85009c7a2ebd7b6585 (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
/* stdlib */
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* unix */
#include <dlfcn.h>
#include <signal.h>
/* dynamic */
#include <microhttpd.h>
#include <sqlite3.h>
/* my libs */
#include <log.h>
#define COMMON_IMPLEMENTATION
#include <common.h>

#ifndef DEFAULT_ENDPOINTS_PATH
#	define DEFAULT_ENDPOINTS_PATH "./endpoints.so"
#endif
#ifndef DEFAULT_DATABASE_PATH
#	define DEFAULT_DATABASE_PATH "./jals.db"
#endif

struct connarg {
	void* dylib;
	query_func query;
	void (*constructor)(const char*);
	void (*destructor)(void);
} arg = {NULL, NULL, NULL, NULL};

volatile char reload_library = 0;

enum MHD_Result process_connection(void *cls, struct MHD_Connection *connection, const char *url, 
	const char *method, const char *version, const char *upload_data, 
	size_t *upload_data_size, void **req_cls)
{
	LDEBUGF("\nurl: %s\nmethod:%s\nversion:%s\nupload_data:%s\nsize:%zu\n", 
			url, method, version, upload_data, *upload_data_size);
	struct connarg *arg = cls;
	/* Load function with the name of the url from shared library */
	endpoint handler = dlsym(arg->dylib, url);
	struct MHD_Response *res; 
	int status = MHD_HTTP_OK;
	const char *response;
	char *newurl;
	if (strcasecmp(method, "GET")) {
		status = MHD_HTTP_METHOD_NOT_ALLOWED;
		res = MHD_create_response_from_buffer(18, "Method not allowed", MHD_RESPMEM_PERSISTENT);
	} else if (handler != NULL) {
		/* endpoint access */
		LINFOF("Accessed endpoint at url %s", url);
		res = handler(method, connection, &status);
	} else if ((newurl = arg->query(url + 1)) != NULL) {
		LINFOF("Redirect from %s to %s", url, newurl);
		response = "<!DOCTYPE html><html><body>Recirecting...</body></html>";
		res = MHD_create_response_from_buffer(strlen(response), (void*)response, MHD_RESPMEM_PERSISTENT);
		MHD_add_response_header(res, "Location", newurl);
		status = MHD_HTTP_FOUND;
		free(newurl);
	} else if ((res = get_from_file(url + 1)) != NULL) {
		/* fallback file access */
		LINFOF("Accessed file at url %s", url);
	} else {
		/* error */
		LINFOF("Failed access to endpoint\t%s", url);
		const char* error = "\"resource does not exist\"";
		res = MHD_create_response_from_buffer(strlen(error), (void*)error, MHD_RESPMEM_PERSISTENT);
		status = MHD_HTTP_NOT_FOUND;
	}
	MHD_queue_response(connection, status, res);
	MHD_destroy_response(res);
	return MHD_YES;
}

/* Because calling dlclose and dlopen from here is undefined */
void sigusr1_handler(int i) {
	reload_library = 1;
}

void reload_dylib(const char *so_path, const char *db_path) {
	LINFOF("Reloading dymanic library: %s", so_path);
	if (arg.destructor) arg.destructor();
	if (arg.dylib != NULL) dlclose(arg.dylib);
	arg.dylib = dlopen(so_path, RTLD_LAZY);
	if (!arg.dylib) LCRITVF(1, "Could not open dynamic library: %s\n", dlerror());
	arg.query = dlsym(arg.dylib, "db_get_url");
	if (!arg.query) LCRITVF(1, "Could not load `db_get_url` from shared library: %s", dlerror());
	arg.constructor = dlsym(arg.dylib, "init");
	if (!arg.constructor) LCRITVF(1, "Could not load `init` from shared library: %s", dlerror());
	arg.constructor(db_path);
	arg.destructor = dlsym(arg.dylib, "sqlite_deinit");
	if (!arg.destructor) LCRITVF(1, "Could not load `sqlite_deinit` from shared library: %s", dlerror());
}

struct MHD_Daemon *init(sigset_t *sigset, const char *dlpath, const char *dbpath) {
	sigset_t newmask;
	errno = 0;
	reload_dylib(dlpath, dbpath);
	
	sigemptyset(&newmask);
	sigaddset(&newmask, SIGUSR1);

	struct sigaction handler = {.sa_handler = sigusr1_handler};
	if (sigaction(SIGUSR1, &handler, NULL) < 0) LFAILV("Setting signal handler for SIGUSR1");

	sigset_t oldmask;
	pthread_sigmask(SIG_BLOCK, &newmask, sigset); /* To prevent MHD from recieving the signal */
	struct MHD_Daemon *daemon = MHD_start_daemon(MHD_USE_ERROR_LOG | MHD_USE_POLL | MHD_USE_INTERNAL_POLLING_THREAD, 8080, NULL, NULL, process_connection, &arg, MHD_OPTION_END);
	pthread_sigmask(SIG_UNBLOCK, &newmask, NULL);

	if (daemon == NULL) LFAILV("could not initialize daemon");

	return daemon;
}

int main(int argc, char *argv[]) {
	sigset_t sig;
	const char *dlpath = DEFAULT_ENDPOINTS_PATH;
	const char *dbpath = DEFAULT_DATABASE_PATH;
	struct MHD_Daemon *daemon = init(&sig, dlpath, dbpath);
	while (reload_library == 0) {
		sigsuspend(&sig);
		if (reload_library == 1) {
			reload_dylib(dlpath, dbpath);
			reload_library = 0;
		}
	}
	getchar(); /* to be replaced with fork later */
	arg.destructor();
	dlclose(arg.dylib);
	MHD_stop_daemon(daemon);
	return 0;
}