Skip to content

Commit

Permalink
Merge pull request #1623 from giuseppe/fix-hang-on-unresponsive-tty
Browse files Browse the repository at this point in the history
linux: fix a hang if there are no reads from the tty
  • Loading branch information
giuseppe authored Dec 16, 2024
2 parents 017b5fd + 8b972be commit 3dbc7e7
Show file tree
Hide file tree
Showing 8 changed files with 830 additions and 69 deletions.
10 changes: 8 additions & 2 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ endif
check_LTLIBRARIES = libcrun_testing.la

libcrun_SOURCES = src/libcrun/utils.c \
src/libcrun/ring_buffer.c \
src/libcrun/blake3/blake3.c \
src/libcrun/blake3/blake3_portable.c \
src/libcrun/cgroup-cgroupfs.c \
Expand Down Expand Up @@ -154,12 +155,12 @@ EXTRA_DIST = COPYING COPYING.libcrun README.md NEWS SECURITY.md rpm/crun.spec au
src/libcrun/handlers/handler-utils.h \
src/libcrun/linux.h src/libcrun/utils.h src/libcrun/error.h src/libcrun/criu.h \
src/libcrun/scheduler.h src/libcrun/status.h src/libcrun/terminal.h \
src/libcrun/mount_flags.h src/libcrun/intelrdt.h \
src/libcrun/mount_flags.h src/libcrun/intelrdt.h src/libcrun/ring_buffer.h \
crun.1.md crun.1 libcrun.lds \
krun.1.md krun.1 \
lua/luacrun.rockspec

UNIT_TESTS = tests/tests_libcrun_utils tests/tests_libcrun_errors tests/tests_libcrun_intelrdt
UNIT_TESTS = tests/tests_libcrun_utils tests/tests_libcrun_ring_buffer tests/tests_libcrun_errors tests/tests_libcrun_intelrdt

if ENABLE_CRUN
bin_PROGRAMS = crun
Expand All @@ -181,6 +182,11 @@ tests_tests_libcrun_utils_SOURCES = tests/tests_libcrun_utils.c
tests_tests_libcrun_utils_LDADD = $(TESTS_LDADD)
tests_tests_libcrun_utils_LDFLAGS = $(crun_LDFLAGS)

tests_tests_libcrun_ring_buffer_CFLAGS = -I $(abs_top_builddir)/libocispec/src -I $(abs_top_srcdir)/libocispec/src -I $(abs_top_builddir)/src -I $(abs_top_srcdir)/src
tests_tests_libcrun_ring_buffer_SOURCES = tests/tests_libcrun_ring_buffer.c
tests_tests_libcrun_ring_buffer_LDADD = $(TESTS_LDADD)
tests_tests_libcrun_ring_buffer_LDFLAGS = $(crun_LDFLAGS)

tests_tests_libcrun_intelrdt_CFLAGS = -I $(abs_top_builddir)/libocispec/src -I $(abs_top_srcdir)/libocispec/src -I $(abs_top_builddir)/src -I $(abs_top_srcdir)/src
tests_tests_libcrun_intelrdt_SOURCES = tests/tests_libcrun_intelrdt.c
tests_tests_libcrun_intelrdt_LDADD = $(TESTS_LDADD)
Expand Down
95 changes: 62 additions & 33 deletions src/libcrun/container.c
Original file line number Diff line number Diff line change
Expand Up @@ -1968,21 +1968,34 @@ struct wait_for_process_args
static int
wait_for_process (struct wait_for_process_args *args, libcrun_error_t *err)
{
cleanup_channel_fd_pair struct channel_fd_pair *from_terminal = NULL;
cleanup_channel_fd_pair struct channel_fd_pair *to_terminal = NULL;
int ret, container_exit_code = 0, last_process;
cleanup_close int terminal_fd_from = -1;
cleanup_close int terminal_fd_to = -1;
const size_t max_events = 10;
cleanup_close int epollfd = -1;
cleanup_close int signalfd = -1;
int ret, container_exit_code = 0, last_process;
sigset_t mask;
int fds[10];
int levelfds[10];
int levelfds_len = 0;
int fds_len = 0;
int in_fds[max_events];
int in_fds_len = 0;
int out_fds[max_events];
int out_fds_len = 0;
size_t i;

cleanup_seccomp_notify_context struct seccomp_notify_context_s *seccomp_notify_ctx = NULL;

container_exit_code = 0;

if (args == NULL || args->context == NULL)
return crun_make_error (err, 0, "internal error: context is empty");

for (i = 0; i < max_events; i++)
{
in_fds[i] = -1;
out_fds[i] = -1;
}

if (args->context->pid_file)
{
char buf[32];
Expand Down Expand Up @@ -2041,7 +2054,7 @@ wait_for_process (struct wait_for_process_args *args, libcrun_error_t *err)
conf.bundle_path = args->context->bundle;
conf.oci_config_path = oci_config_path;

ret = set_blocking_fd (args->seccomp_notify_fd, 0, err);
ret = set_blocking_fd (args->seccomp_notify_fd, false, err);
if (UNLIKELY (ret < 0))
return ret;

Expand All @@ -2051,65 +2064,81 @@ wait_for_process (struct wait_for_process_args *args, libcrun_error_t *err)
if (UNLIKELY (ret < 0))
return ret;

fds[fds_len++] = args->seccomp_notify_fd;
in_fds[in_fds_len++] = args->seccomp_notify_fd;
}

if (args->terminal_fd >= 0)
{
/* The terminal_fd is dup()ed so that it can be registered with
epoll multiple times using different masks. */
terminal_fd_from = dup (args->terminal_fd);
if (UNLIKELY (terminal_fd_from < 0))
return crun_make_error (err, errno, "dup terminal fd");
terminal_fd_to = dup (args->terminal_fd);
if (UNLIKELY (terminal_fd_to < 0))
return crun_make_error (err, errno, "dup terminal fd");

int i, non_blocking_fds[] = { terminal_fd_from, terminal_fd_to, 0, 1, -1 };
for (i = 0; non_blocking_fds[i] >= 0; i++)
{
ret = set_blocking_fd (non_blocking_fds[i], false, err);
if (UNLIKELY (ret < 0))
return ret;
}

from_terminal = channel_fd_pair_new (terminal_fd_from, 1, BUFSIZ);
to_terminal = channel_fd_pair_new (0, terminal_fd_to, BUFSIZ);
}

fds[fds_len++] = signalfd;
in_fds[in_fds_len++] = signalfd;
if (args->notify_socket >= 0)
fds[fds_len++] = args->notify_socket;
in_fds[in_fds_len++] = args->notify_socket;
if (args->terminal_fd >= 0)
{
fds[fds_len++] = 0;
levelfds[levelfds_len++] = args->terminal_fd;
in_fds[in_fds_len++] = 0;
out_fds[out_fds_len++] = terminal_fd_to;

in_fds[in_fds_len++] = terminal_fd_from;
out_fds[out_fds_len++] = 1;
}
fds[fds_len++] = -1;
levelfds[levelfds_len++] = -1;

epollfd = epoll_helper (fds, levelfds, err);
epollfd = epoll_helper (in_fds, NULL, out_fds, NULL, err);
if (UNLIKELY (epollfd < 0))
return epollfd;

while (1)
{
struct epoll_event events[max_events];
struct signalfd_siginfo si;
struct winsize ws;
ssize_t res;
struct epoll_event events[10];
int i, nr_events;
ssize_t res;

nr_events = TEMP_FAILURE_RETRY (epoll_wait (epollfd, events, 10, -1));
nr_events = TEMP_FAILURE_RETRY (epoll_wait (epollfd, events, max_events, -1));
if (UNLIKELY (nr_events < 0))
return crun_make_error (err, errno, "epoll_wait");

for (i = 0; i < nr_events; i++)
{
if (events[i].data.fd == 0)
if (events[i].data.fd == 0 || events[i].data.fd == terminal_fd_to)
{
ret = copy_from_fd_to_fd (0, args->terminal_fd, 0, err);
ret = channel_fd_pair_process (to_terminal, epollfd, err);
if (UNLIKELY (ret < 0))
return crun_error_wrap (err, "copy to terminal fd");
}
else if (events[i].data.fd == 1 || events[i].data.fd == terminal_fd_from)
{
ret = channel_fd_pair_process (from_terminal, epollfd, err);
if (UNLIKELY (ret < 0))
return crun_error_wrap (err, "copy from terminal fd");
}
else if (events[i].data.fd == args->seccomp_notify_fd)
{
ret = libcrun_seccomp_notify_plugins (seccomp_notify_ctx,
args->seccomp_notify_fd, err);
if (UNLIKELY (ret < 0))
return ret;
}
else if (events[i].data.fd == args->terminal_fd)
{
ret = set_blocking_fd (args->terminal_fd, 0, err);
if (UNLIKELY (ret < 0))
return crun_error_wrap (err, "set terminal fd not blocking");

ret = copy_from_fd_to_fd (args->terminal_fd, 1, 1, err);
if (UNLIKELY (ret < 0))
return crun_error_wrap (err, "copy from terminal fd");

ret = set_blocking_fd (args->terminal_fd, 1, err);
if (UNLIKELY (ret < 0))
return crun_error_wrap (err, "set terminal fd blocking");
}
else if (events[i].data.fd == args->notify_socket)
{
ret = handle_notify_socket (args->notify_socket, err);
Expand Down
Loading

0 comments on commit 3dbc7e7

Please sign in to comment.