aboutgitcodebugslistschat
path: root/pasta.c
diff options
context:
space:
mode:
authorStefano Brivio <sbrivio@redhat.com>2024-02-15 23:24:32 +0100
committerStefano Brivio <sbrivio@redhat.com>2024-02-16 08:47:14 +0100
commit8f3f8e190c43fc6a3adc7912aaa262e86f9d0748 (patch)
tree52538ea8bd1e6d89062a2eaeaeaeb405c734433d /pasta.c
parentf57a2fb4d5ee8728d92250fc6eb45ffeab221990 (diff)
downloadpasst-8f3f8e190c43fc6a3adc7912aaa262e86f9d0748.tar
passt-8f3f8e190c43fc6a3adc7912aaa262e86f9d0748.tar.gz
passt-8f3f8e190c43fc6a3adc7912aaa262e86f9d0748.tar.bz2
passt-8f3f8e190c43fc6a3adc7912aaa262e86f9d0748.tar.lz
passt-8f3f8e190c43fc6a3adc7912aaa262e86f9d0748.tar.xz
passt-8f3f8e190c43fc6a3adc7912aaa262e86f9d0748.tar.zst
passt-8f3f8e190c43fc6a3adc7912aaa262e86f9d0748.zip
pasta: Add fallback timer mechanism to check if namespace is gone
We don't know how frequently this happens, but hitting fs.inotify.max_user_watches or similar sysctl limits is definitely not out of question, and Paul mentioned that, for example, Podman's CI environments hit similar issues in the past. Introduce a fallback mechanism based on a timer file descriptor: we grab the directory handle at startup, and we can then use openat(), triggered periodically, to check if the (network) namespace directory still exists. If openat() fails at some point, exit. Link: https://github.com/containers/podman/pull/21563#issuecomment-1943505707 Reported-by: Paul Holzinger <pholzing@redhat.com> Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
Diffstat (limited to 'pasta.c')
-rw-r--r--pasta.c101
1 files changed, 80 insertions, 21 deletions
diff --git a/pasta.c b/pasta.c
index 94807a3..01d1511 100644
--- a/pasta.c
+++ b/pasta.c
@@ -30,6 +30,7 @@
#include <sys/epoll.h>
#include <sys/inotify.h>
#include <sys/mount.h>
+#include <sys/timerfd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
@@ -357,46 +358,78 @@ void pasta_ns_conf(struct ctx *c)
}
/**
+ * pasta_netns_quit_timer() - Set up fallback timer to monitor namespace
+ *
+ * Return: timerfd file descriptor, negative error code on failure
+ */
+static int pasta_netns_quit_timer(void)
+{
+ int fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
+ struct itimerspec it = { { 1, 0 }, { 1, 0 } }; /* one-second interval */
+
+ if (fd == -1) {
+ err("timerfd_create(): %s", strerror(errno));
+ return -errno;
+ }
+
+ if (timerfd_settime(fd, 0, &it, NULL) < 0) {
+ err("timerfd_settime(): %s", strerror(errno));
+ close(fd);
+ return -errno;
+ }
+
+ return fd;
+}
+
+/**
* pasta_netns_quit_init() - Watch network namespace to quit once it's gone
* @c: Execution context
- *
- * Return: inotify file descriptor, -1 on failure or if not needed/applicable
*/
-int pasta_netns_quit_init(const struct ctx *c)
+void pasta_netns_quit_init(const struct ctx *c)
{
+ union epoll_ref ref = { .type = EPOLL_TYPE_NSQUIT_INOTIFY };
+ struct epoll_event ev = { .events = EPOLLIN };
int flags = O_NONBLOCK | O_CLOEXEC;
- union epoll_ref ref = { .type = EPOLL_TYPE_NSQUIT };
- struct epoll_event ev = {
- .events = EPOLLIN
- };
- int inotify_fd;
+ int fd;
if (c->mode != MODE_PASTA || c->no_netns_quit || !*c->netns_base)
- return -1;
+ return;
- if ((inotify_fd = inotify_init1(flags)) < 0) {
- perror("inotify_init(): won't quit once netns is gone");
- return -1;
+ if ((fd = inotify_init1(flags)) < 0)
+ warn("inotify_init1(): %s, use a timer", strerror(errno));
+
+ if (fd >= 0 && inotify_add_watch(fd, c->netns_dir, IN_DELETE) < 0) {
+ warn("inotify_add_watch(): %s, use a timer",
+ strerror(errno));
+ close(fd);
+ fd = -1;
}
- if (inotify_add_watch(inotify_fd, c->netns_dir, IN_DELETE) < 0) {
- perror("inotify_add_watch(): won't quit once netns is gone");
- return -1;
+ if (fd < 0) {
+ if ((fd = pasta_netns_quit_timer()) < 0)
+ die("Failed to set up fallback netns timer, exiting");
+
+ ref.nsdir_fd = open(c->netns_dir, O_CLOEXEC | O_RDONLY);
+ if (ref.nsdir_fd < 0)
+ die("netns dir open: %s, exiting", strerror(errno));
+
+ ref.type = EPOLL_TYPE_NSQUIT_TIMER;
}
- ref.fd = inotify_fd;
- ev.data.u64 = ref.u64;
- epoll_ctl(c->epollfd, EPOLL_CTL_ADD, inotify_fd, &ev);
+ if (fd > FD_REF_MAX)
+ die("netns monitor file number %i too big, exiting", fd);
- return inotify_fd;
+ ref.fd = fd;
+ ev.data.u64 = ref.u64;
+ epoll_ctl(c->epollfd, EPOLL_CTL_ADD, fd, &ev);
}
/**
- * pasta_netns_quit_handler() - Handle ns directory events, exit if ns is gone
+ * pasta_netns_quit_inotify_handler() - Handle inotify watch, exit if ns is gone
* @c: Execution context
* @inotify_fd: inotify file descriptor with watch on namespace directory
*/
-void pasta_netns_quit_handler(struct ctx *c, int inotify_fd)
+void pasta_netns_quit_inotify_handler(struct ctx *c, int inotify_fd)
{
char buf[sizeof(struct inotify_event) + NAME_MAX + 1];
const struct inotify_event *in_ev = (struct inotify_event *)buf;
@@ -410,3 +443,29 @@ void pasta_netns_quit_handler(struct ctx *c, int inotify_fd)
info("Namespace %s is gone, exiting", c->netns_base);
exit(EXIT_SUCCESS);
}
+
+/**
+ * pasta_netns_quit_timer_handler() - Handle timer, exit if ns is gone
+ * @c: Execution context
+ * @ref: epoll reference for timer descriptor
+ */
+void pasta_netns_quit_timer_handler(struct ctx *c, union epoll_ref ref)
+{
+ uint64_t expirations;
+ ssize_t n;
+ int fd;
+
+ n = read(ref.fd, &expirations, sizeof(expirations));
+ if (n < 0)
+ die("Namespace watch timer read() error: %s", strerror(errno));
+ if ((size_t)n < sizeof(expirations))
+ warn("Namespace watch timer: short read(): %zi", n);
+
+ fd = openat(ref.nsdir_fd, c->netns_base, O_PATH | O_CLOEXEC);
+ if (fd < 0) {
+ info("Namespace %s is gone, exiting", c->netns_base);
+ exit(EXIT_SUCCESS);
+ }
+
+ close(fd);
+}