mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-01-19 12:10:46 +00:00
710d987cd6
When doing a soft shutdown, we won't be making new connections anymore so there's no point in keeping the namespace file descriptors open anymore. Keeping these open effectively makes it impossible to properly clean up namespaces which are no longer used in the new configuration until all previously opened connections are closed in the old worker process. This change introduces a cleanup function that is called during soft shutdown that closes all namespace file descriptors by iterating over the namespace ebtree.
131 lines
3.1 KiB
C
131 lines
3.1 KiB
C
#define _GNU_SOURCE
|
|
|
|
#include <sched.h>
|
|
#include <stdio.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#include <sys/socket.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <common/namespace.h>
|
|
#include <common/compiler.h>
|
|
#include <common/hash.h>
|
|
#include <common/errors.h>
|
|
#include <proto/log.h>
|
|
#include <proto/signal.h>
|
|
#include <types/global.h>
|
|
|
|
/* Opens the namespace <ns_name> and returns the FD or -1 in case of error
|
|
* (check errno).
|
|
*/
|
|
static int open_named_namespace(const char *ns_name)
|
|
{
|
|
if (chunk_printf(&trash, "/var/run/netns/%s", ns_name) < 0)
|
|
return -1;
|
|
return open(trash.area, O_RDONLY | O_CLOEXEC);
|
|
}
|
|
|
|
static int default_namespace = -1;
|
|
|
|
static int init_default_namespace()
|
|
{
|
|
if (chunk_printf(&trash, "/proc/%d/ns/net", getpid()) < 0)
|
|
return -1;
|
|
default_namespace = open(trash.area, O_RDONLY | O_CLOEXEC);
|
|
return default_namespace;
|
|
}
|
|
|
|
static struct eb_root namespace_tree_root = EB_ROOT;
|
|
|
|
static void netns_sig_stop(struct sig_handler *sh)
|
|
{
|
|
struct ebpt_node *node, *next;
|
|
struct netns_entry *entry;
|
|
|
|
/* close namespace file descriptors and remove registered namespaces from the
|
|
* tree when stopping */
|
|
node = ebpt_first(&namespace_tree_root);
|
|
while (node) {
|
|
next = ebpt_next(node);
|
|
ebpt_delete(node);
|
|
entry = container_of(node, struct netns_entry, node);
|
|
free(entry->node.key);
|
|
close(entry->fd);
|
|
node = next;
|
|
}
|
|
}
|
|
|
|
int netns_init(void)
|
|
{
|
|
int err_code = 0;
|
|
|
|
/* if no namespaces have been defined in the config then
|
|
* there is no point in trying to initialize anything:
|
|
* my_socketat() will never be called with a valid namespace
|
|
* structure and thus switching back to the default namespace
|
|
* is not needed either */
|
|
if (!eb_is_empty(&namespace_tree_root)) {
|
|
if (init_default_namespace() < 0) {
|
|
ha_alert("Failed to open the default namespace.\n");
|
|
err_code |= ERR_ALERT | ERR_FATAL;
|
|
}
|
|
}
|
|
|
|
signal_register_fct(0, netns_sig_stop, 0);
|
|
|
|
return err_code;
|
|
}
|
|
|
|
struct netns_entry* netns_store_insert(const char *ns_name)
|
|
{
|
|
struct netns_entry *entry = NULL;
|
|
int fd = open_named_namespace(ns_name);
|
|
if (fd == -1)
|
|
goto out;
|
|
|
|
entry = calloc(1, sizeof(*entry));
|
|
if (!entry)
|
|
goto out;
|
|
entry->fd = fd;
|
|
entry->node.key = strdup(ns_name);
|
|
entry->name_len = strlen(ns_name);
|
|
ebis_insert(&namespace_tree_root, &entry->node);
|
|
out:
|
|
return entry;
|
|
}
|
|
|
|
const struct netns_entry* netns_store_lookup(const char *ns_name, size_t ns_name_len)
|
|
{
|
|
struct ebpt_node *node;
|
|
|
|
node = ebis_lookup_len(&namespace_tree_root, ns_name, ns_name_len);
|
|
if (node)
|
|
return ebpt_entry(node, struct netns_entry, node);
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
/* Opens a socket in the namespace described by <ns> with the parameters <domain>,
|
|
* <type> and <protocol> and returns the FD or -1 in case of error (check errno).
|
|
*/
|
|
int my_socketat(const struct netns_entry *ns, int domain, int type, int protocol)
|
|
{
|
|
int sock;
|
|
|
|
if (default_namespace >= 0 && ns && setns(ns->fd, CLONE_NEWNET) == -1)
|
|
return -1;
|
|
|
|
sock = socket(domain, type, protocol);
|
|
|
|
if (default_namespace >= 0 && ns && setns(default_namespace, CLONE_NEWNET) == -1) {
|
|
close(sock);
|
|
return -1;
|
|
}
|
|
return sock;
|
|
}
|
|
|
|
REGISTER_BUILD_OPTS("Built with network namespace support.");
|