summaryrefslogtreecommitdiffstats
path: root/daemon/network.c
diff options
context:
space:
mode:
authorMarek Vavrusa <marek@vavrusa.com>2016-03-29 01:08:24 +0200
committerMarek Vavrusa <marek@vavrusa.com>2016-03-29 03:32:49 +0200
commitd944e311dcf4578ddf021da8818b3f6a9f2e5d75 (patch)
treedb65ba39e5ac736b1ca3bd4810035bd40aa863bc /daemon/network.c
parentlib/resolve: worked around bug with multiple TAs (diff)
downloadknot-resolver-d944e311dcf4578ddf021da8818b3f6a9f2e5d75.tar.xz
knot-resolver-d944e311dcf4578ddf021da8818b3f6a9f2e5d75.zip
daemon: support running in supervised mode (--fd=X)
daemon can accept existing fds on command line, thus supporting process managers like circus or upstart. a tiny supervisor script is attached
Diffstat (limited to 'daemon/network.c')
-rw-r--r--daemon/network.c89
1 files changed, 89 insertions, 0 deletions
diff --git a/daemon/network.c b/daemon/network.c
index ace17259..334edcf5 100644
--- a/daemon/network.c
+++ b/daemon/network.c
@@ -15,6 +15,7 @@
*/
#include <unistd.h>
+#include <assert.h>
#include "daemon/network.h"
#include "daemon/worker.h"
#include "daemon/io.h"
@@ -153,6 +154,44 @@ static int open_endpoint(struct network *net, struct endpoint *ep, struct sockad
return kr_ok();
}
+/** Open fd as endpoint. */
+static int open_endpoint_fd(struct network *net, struct endpoint *ep, int fd, int sock_type)
+{
+ if (sock_type == SOCK_DGRAM) {
+ if (ep->udp) {
+ return kr_error(EEXIST);
+ }
+ ep->udp = malloc(sizeof(*ep->udp));
+ if (!ep->udp) {
+ return kr_error(ENOMEM);
+ }
+ uv_udp_init(net->loop, ep->udp);
+ int ret = uv_udp_open(ep->udp, (uv_os_sock_t) fd);
+ if (ret != 0) {
+ close_handle((uv_handle_t *)ep->udp, false);
+ return ret;
+ }
+ ep->flags |= NET_UDP;
+ }
+ if (sock_type == SOCK_STREAM) {
+ if (ep->tcp) {
+ return kr_error(EEXIST);
+ }
+ ep->tcp = malloc(sizeof(*ep->tcp));
+ if (!ep->tcp) {
+ return kr_error(ENOMEM);
+ }
+ uv_tcp_init(net->loop, ep->tcp);
+ int ret = uv_tcp_open(ep->tcp, (uv_os_sock_t) fd);
+ if (ret != 0) {
+ close_handle((uv_handle_t *)ep->tcp, false);
+ return ret;
+ }
+ ep->flags |= NET_TCP;
+ }
+ return kr_ok();
+}
+
/** @internal Fetch endpoint array and offset of the address/port query. */
static endpoint_array_t *network_get(struct network *net, const char *addr, uint16_t port, size_t *index)
{
@@ -169,6 +208,56 @@ static endpoint_array_t *network_get(struct network *net, const char *addr, uint
return NULL;
}
+int network_listen_fd(struct network *net, int fd)
+{
+ /* Extract local address and socket type. */
+ int sock_type = SOCK_DGRAM;
+ socklen_t len = sizeof(sock_type);
+ int ret = getsockopt(fd, SOL_SOCKET, SO_TYPE, &sock_type, &len);
+ if (ret != 0) {
+ return kr_error(EBADF);
+ }
+ /* Extract local address for this socket. */
+ struct sockaddr_storage ss;
+ socklen_t addr_len = sizeof(ss);
+ ret = getsockname(fd, (struct sockaddr *)&ss, &addr_len);
+ if (ret != 0) {
+ return kr_error(EBADF);
+ }
+ int port = 0;
+ char addr_str[INET6_ADDRSTRLEN]; /* http://tools.ietf.org/html/rfc4291 */
+ if (ss.ss_family == AF_INET) {
+ uv_ip4_name((const struct sockaddr_in*)&ss, addr_str, sizeof(addr_str));
+ port = ntohs(((struct sockaddr_in *)&ss)->sin_port);
+ } else if (ss.ss_family == AF_INET6) {
+ uv_ip6_name((const struct sockaddr_in6*)&ss, addr_str, sizeof(addr_str));
+ port = ntohs(((struct sockaddr_in6 *)&ss)->sin6_port);
+ } else {
+ uv_ip4_name((const struct sockaddr_in*)&ss, addr_str, sizeof(addr_str));
+ port = ntohs(((struct sockaddr_in *)&ss)->sin_port);
+ return kr_error(EAFNOSUPPORT);
+ }
+ /* Fetch or create endpoint for this fd */
+ size_t index = 0;
+ endpoint_array_t *ep_array = network_get(net, addr_str, port, &index);
+ if (!ep_array) {
+ struct endpoint *ep = malloc(sizeof(*ep));
+ memset(ep, 0, sizeof(*ep));
+ ep->flags = NET_DOWN;
+ ep->port = port;
+ ret = insert_endpoint(net, addr_str, ep);
+ if (ret != 0) {
+ return ret;
+ }
+ ep_array = network_get(net, addr_str, port, &index);
+ }
+ /* Open fd in found/created endpoint. */
+ struct endpoint *ep = ep_array->at[index];
+ assert(ep != NULL);
+ /* Create a libuv struct for this socket. */
+ return open_endpoint_fd(net, ep, fd, sock_type);
+}
+
int network_listen(struct network *net, const char *addr, uint16_t port, uint32_t flags)
{
if (net == NULL || addr == 0 || port == 0) {