44
55#include " linyaps_box/container.h"
66
7+ #include " linyaps_box/container_monitor.h"
78#include " linyaps_box/impl/disabled_cgroup_manager.h"
8- #include " linyaps_box/io/epoll.h"
9- #include " linyaps_box/io/forwarder.h"
109#include " linyaps_box/terminal.h"
1110#include " linyaps_box/unixsocket.h"
1211#include " linyaps_box/utils/cgroups.h"
@@ -2344,136 +2343,6 @@ void poststop_hooks(const linyaps_box::container &container) noexcept
23442343 }
23452344}
23462345
2347- struct wait_process_ctx
2348- {
2349- pid_t pid{ -1 };
2350- std::optional<linyaps_box::terminal_master> master;
2351- std::optional<linyaps_box::terminal_slave> host_tty;
2352- linyaps_box::utils::file_descriptor in;
2353- linyaps_box::utils::file_descriptor out;
2354- };
2355-
2356- [[nodiscard]] int wait_container_process (wait_process_ctx ctx)
2357- {
2358- LINYAPS_BOX_DEBUG () << " Wait container process: " << ctx.pid ;
2359-
2360- sigset_t set;
2361- linyaps_box::utils::sigfillset (set);
2362- linyaps_box::utils::sigprocmask (SIG_BLOCK , set, nullptr );
2363- auto signalfd = linyaps_box::utils::create_signalfd (set);
2364-
2365- int exit_code{ 0 };
2366- bool child_exited{ false };
2367-
2368- // try to reap child process. Child process may exit before we create signalfd and it wouldn't
2369- // receive SIGCHLD anymore. If we don't do this, the child process (or its children) will be
2370- // zombie process
2371- auto ret = linyaps_box::utils::waitpid (ctx.pid , WNOHANG );
2372-
2373- if (ret.status == linyaps_box::utils::WaitStatus::Reaped) {
2374- child_exited = true ;
2375- exit_code = WIFSIGNALED (ret.exit_code ) ? 128 + WTERMSIG (ret.exit_code )
2376- : WEXITSTATUS (ret.exit_code );
2377- } else if (ret.status == linyaps_box::utils::WaitStatus::NoChild) {
2378- throw std::runtime_error (" target pid " + std::to_string (ctx.pid ) + " is not a child" );
2379- }
2380-
2381- linyaps_box::io::Epoll epoll;
2382- auto signalfd_pollable = epoll.add (signalfd, EPOLLIN );
2383- assert (signalfd_pollable);
2384- if (UNLIKELY (!signalfd_pollable)) {
2385- throw std::runtime_error (" failed to add signalfd to epoll" );
2386- }
2387-
2388- constexpr auto buffer_size = 256 * 1024 ;
2389- std::optional<linyaps_box::utils::file_descriptor> master_out;
2390- std::optional<linyaps_box::io::Forwarder> in_fwd;
2391- std::optional<linyaps_box::io::Forwarder> out_fwd;
2392-
2393- if (ctx.master ) {
2394- master_out = ctx.master ->get ().duplicate ();
2395- master_out->set_nonblock (true );
2396-
2397- if (!child_exited) {
2398- in_fwd.emplace (epoll, buffer_size);
2399- in_fwd->set_src (ctx.in );
2400- in_fwd->set_dst (ctx.master ->get ());
2401- }
2402-
2403- out_fwd.emplace (epoll, buffer_size);
2404- out_fwd->set_src (master_out.value ());
2405- out_fwd->set_dst (ctx.out );
2406- }
2407-
2408- auto handle_signal = [&] {
2409- struct signalfd_siginfo info{};
2410- if (signalfd.read (info) != linyaps_box::unixSocketClient::IOStatus::Success) {
2411- return ;
2412- }
2413-
2414- if (info.ssi_signo == SIGCHLD ) {
2415- auto res = linyaps_box::utils::waitpid (ctx.pid , WNOHANG );
2416- if (res.status == linyaps_box::utils::WaitStatus::Reaped) {
2417- child_exited = true ;
2418- in_fwd.reset ();
2419- exit_code = WIFSIGNALED (res.exit_code ) ? 128 + WTERMSIG (res.exit_code )
2420- : WEXITSTATUS (res.exit_code );
2421- }
2422- } else if (info.ssi_signo == SIGWINCH && ctx.master ) {
2423- ctx.master ->resize (ctx.host_tty ->get_size ());
2424- } else if (!child_exited) {
2425- kill (ctx.pid , info.ssi_signo );
2426- }
2427- };
2428-
2429- while (!child_exited || out_fwd) {
2430- bool keep_pumping{ false };
2431-
2432- if (in_fwd) {
2433- auto s = in_fwd->handle_forwarding ();
2434- if (s == linyaps_box::io::Forwarder::Status::Finished) {
2435- in_fwd.reset ();
2436- } else if (s == linyaps_box::io::Forwarder::Status::Busy
2437- && !in_fwd->is_src_pollable ()) {
2438- keep_pumping = true ;
2439- }
2440- }
2441-
2442- if (out_fwd) {
2443- auto s = out_fwd->handle_forwarding ();
2444- if (s == linyaps_box::io::Forwarder::Status::Finished) {
2445- out_fwd.reset ();
2446- } else if (s == linyaps_box::io::Forwarder::Status::Busy
2447- && !out_fwd->is_dst_pollable ()) {
2448- keep_pumping = true ;
2449- }
2450- }
2451-
2452- const auto &events = epoll.wait (keep_pumping ? 0 : -1 );
2453- for (const auto &ev : events) {
2454- if (ev.data .fd == signalfd.get ()) {
2455- handle_signal ();
2456- if (child_exited) {
2457- break ;
2458- }
2459-
2460- continue ;
2461- }
2462- }
2463-
2464- if (child_exited && out_fwd) {
2465- auto status = out_fwd->handle_forwarding ();
2466- if (status == linyaps_box::io::Forwarder::Status::Finished) {
2467- out_fwd.reset ();
2468- } else if (status == linyaps_box::io::Forwarder::Status::Busy) {
2469- keep_pumping = true ;
2470- }
2471- }
2472- }
2473-
2474- return exit_code;
2475- }
2476-
24772346} // namespace runtime_ns
24782347
24792348} // namespace
@@ -2576,6 +2445,10 @@ int linyaps_box::container::run(run_container_options_t options) const
25762445 // TODO: cgroup preenter
25772446 auto [child_pid, socket] = runtime_ns::start_container_process (*this , options);
25782447
2448+ auto in = utils::file_descriptor{ utils::fileno (stdin), false };
2449+ auto out = utils::file_descriptor{ utils::fileno (stdout), false };
2450+ container_monitor monitor (child_pid);
2451+
25792452 {
25802453 auto status = this ->status ();
25812454 assert (status.status == container_status_t ::runtime_status::CREATING );
@@ -2589,16 +2462,37 @@ int linyaps_box::container::run(run_container_options_t options) const
25892462 runtime_ns::create_runtime_hooks (*this , socket);
25902463 runtime_ns::wait_create_container_result (*this , socket);
25912464
2592- auto master = [&recv_socketpair]() -> std::optional<linyaps_box::terminal_master > {
2465+ auto host_tty = [&recv_socketpair, &monitor, &in, &out ]() -> std::optional<terminal_slave > {
25932466 if (!recv_socketpair) {
25942467 return std::nullopt ;
25952468 }
25962469
2597- auto ret = runtime_ns::recv_master_tty (recv_socketpair.value ());
2470+ LINYAPS_BOX_DEBUG () << " Container requires a terminal" ;
2471+
2472+ auto master = runtime_ns::recv_master_tty (recv_socketpair.value ());
25982473 recv_socketpair->release ();
2599- return ret;
2474+
2475+ in.set_nonblock (true );
2476+ out.set_nonblock (true );
2477+
2478+ std::optional<terminal_slave> host_tty;
2479+ if (utils::isatty (in)) {
2480+ host_tty.emplace (in.duplicate ());
2481+ } else if (utils::isatty (out)) {
2482+ host_tty.emplace (out.duplicate ());
2483+ } else {
2484+ auto default_tty = utils::open (" /dev/tty" , O_RDWR | O_CLOEXEC );
2485+ host_tty.emplace (std::move (default_tty));
2486+ }
2487+
2488+ host_tty->set_raw ();
2489+ monitor.enable_io_forwarding (std::move (master), in, out);
2490+
2491+ return host_tty;
26002492 }();
26012493
2494+ monitor.enable_signal_forwarding (std::move (host_tty));
2495+
26022496 runtime_ns::wait_socket_close (socket);
26032497
26042498 {
@@ -2613,35 +2507,7 @@ int linyaps_box::container::run(run_container_options_t options) const
26132507
26142508 // TODO: support detach from the parent's process
26152509 // Now we wait for the container process to exit
2616- runtime_ns::wait_process_ctx ctx;
2617- ctx.pid = this ->status ().PID ;
2618-
2619- if (master) {
2620- LINYAPS_BOX_DEBUG () << " Container requires a terminal" ;
2621-
2622- master->get ().set_nonblock (true );
2623-
2624- ctx.in = utils::file_descriptor{ utils::fileno (stdin), false };
2625- ctx.in .set_nonblock (true );
2626-
2627- ctx.out = utils::file_descriptor{ utils::fileno (stdout), false };
2628- ctx.out .set_nonblock (true );
2629-
2630- if (utils::isatty (ctx.in )) {
2631- ctx.host_tty = linyaps_box::terminal_slave{ ctx.in .duplicate () };
2632- ctx.host_tty ->set_raw ();
2633- } else if (utils::isatty (ctx.out )) {
2634- ctx.host_tty = linyaps_box::terminal_slave{ ctx.out .duplicate () };
2635- ctx.host_tty ->set_raw ();
2636- } else {
2637- auto default_tty = utils::open (" /dev/tty" , O_RDWR | O_CLOEXEC );
2638- ctx.host_tty = linyaps_box::terminal_slave{ std::move (default_tty) };
2639- }
2640- }
2641-
2642- ctx.master = std::move (master);
2643-
2644- container_process_exit_code = runtime_ns::wait_container_process (std::move (ctx));
2510+ container_process_exit_code = monitor.wait_container_exit ();
26452511
26462512 runtime_ns::poststop_hooks (*this );
26472513 } catch (const std::system_error &e) {
0 commit comments