#include "certd.hpp"
CONF_DEF(config) {
ConfigKey cert_cache;
};
CONF_INIT(config) {
CONF_KEY_INST(cert_cache, true, DiskCache, char*);
}
static Config* global_config = NULL;
static bool read_handler(int fd, char* cn, size_t cnlen) {
DiskCache* cache = config? config->cert_cache.ctx_as<DiskCache>(): NULL;
char* cert;
size_t certlen;
char* key;
size_t keylen;
tristate_t rv;
char* hit = NULL;
ssize_t hitlen = cache? cache->get(cn, hit): -1;
if (hit) {
log(debug, "'%s' cache hit", cn);
rv = separator_write(fd, cn, cnlen, hit, hitlen);
cache->pool.push(hit);
return (rv == TRI_TRUE);
} else if (mkcert(cn, cert, certlen, key, keylen)) {
log(debug, "generated '%s'", cn);
if (cache) {
iovec iov[3];
iov[0].iov_base = cert;
iov[0].iov_len = certlen;
iov[1].iov_base = (void*)&"~";
iov[1].iov_len = 1;
iov[2].iov_base = key;
iov[2].iov_len = keylen;
(void)cache->set(cn, iov, 3, certlen + 1 + keylen);
}
rv = separator_write(fd, cn, cnlen, cert, certlen, key, keylen);
free(cert);
free(key);
return (rv == TRI_TRUE);
} else {
log(error, "no cert for '%s'", cn);
return (separator_write(fd, "", 0) == TRI_TRUE);
}
}
int acceptor(int fd, msec_t tout) {
if (tout && !has_data(fd, tout)) {
return -1;
}
sockaddr_t src;
int len = sizeof(src);
int newfd = accept4(fd, (struct sockaddr*)&src, (socklen_t*)&len, SOCK_NONBLOCK); // non-blocking
if (newfd == -1) {
log_errno(error, "accept");
return -1;
}
return newfd;
}
int main(int argc, char **argv) {
EINTR_RETRY(close(STDIN_FILENO));
EINTR_RETRY(close(STDOUT_FILENO));
const char* config_file = (argc >= 2)? argv[1]: NULL;
const char* socket_file = (argc >= 3)? argv[2]: NULL;
if (!config_file || !socket_file) {
STDERR("Usage: %s <config-file> <socket-file>", argv[0]);
return 1;
}
Hooks::run(Hooks::INIT);
global_config = new Config(config_file);
if (!global_config->load()) {
log(error, "cannot load config '%s'", config_file);
delete global_config;
Hooks::run(Hooks::DEINIT);
return 1;
}
log(notice, "loaded config '%s'", config_file);
Hooks::run(Hooks::PRE_IO);
int acceptfd = uds_listen(socket_file);
if (acceptfd == -1) {
log(error, "cannot open socket '%s'", socket_file);
delete global_config;
Hooks::run(Hooks::DEINIT);
return 1;
}
if (!set_blocking(acceptfd, false)) { // non-blocking
delete global_config;
Hooks::run(Hooks::DEINIT);
return 1;
}
int conn = -1;
while (!SHUTDOWN) {
update_now(); // using timeout below so this gets updated
if (conn == -1) {
conn = acceptor(acceptfd, 1000);
}
if (conn != -1) {
if (separator_read(conn, &read_handler, 1000) == TRI_FALSE) {
EINTR_RETRY(close(conn)); // something seriously wrong with the fd or eof
conn = -1;
}
}
if (unlikely(RECONFIGURE)) {
log(notice, "triggering config reload...");
(void)global_config->load(); // blocking
RECONFIGURE = 0;
CLEANUP = 0; // implicitly done upon config change
} else if (unlikely(CLEANUP)) {
log(notice, "triggering cache clean...");
Hooks::run(Hooks::CACHE_CLEAN);
CLEANUP = 0;
}
}
if (conn != -1) close(conn);
close(acceptfd);
if (unlink(socket_file) == -1) {
log_errno(info, "unlink(%s)", socket_file);
}
Hooks::run(Hooks::POST_IO);
delete global_config;
Hooks::run(Hooks::DEINIT);
return 0;
}