@@ -377,9 +377,13 @@ func (c *Container) start(process *Process) (retErr error) {
377377
378378// Signal sends a specified signal to container's init.
379379//
380- // When s is SIGKILL and the container does not have its own PID namespace, all
381- // the container's processes are killed. In this scenario, the libcontainer
380+ // When s is SIGKILL:
381+ // 1. If the container does not have its own PID namespace, all the
382+ // container's processes are killed. In this scenario, the libcontainer
382383// user may be required to implement a proper child reaper.
384+ // 2. Otherwise, we just send the SIGKILL signal to the init process,
385+ // but we don't wait the init process exit. If you want to wait it,
386+ // please use c.KillAndWaitExit instead.
383387func (c * Container ) Signal (s os.Signal ) error {
384388 c .m .Lock ()
385389 defer c .m .Unlock ()
@@ -431,6 +435,80 @@ func (c *Container) signal(s os.Signal) error {
431435 return nil
432436}
433437
438+ func (c * Container ) killViaPidfd () error {
439+ pidfd , err := unix .PidfdOpen (c .initProcess .pid (), 0 )
440+ if err != nil {
441+ return err
442+ }
443+ defer unix .Close (pidfd )
444+
445+ epollfd , err := unix .EpollCreate1 (unix .EPOLL_CLOEXEC )
446+ if err != nil {
447+ return err
448+ }
449+ defer unix .Close (epollfd )
450+
451+ event := unix.EpollEvent {
452+ Events : unix .EPOLLIN ,
453+ Fd : int32 (pidfd ),
454+ }
455+ if err := unix .EpollCtl (epollfd , unix .EPOLL_CTL_ADD , pidfd , & event ); err != nil {
456+ return err
457+ }
458+
459+ // We don't need unix.PidfdSendSignal because go runtime will use it if possible.
460+ _ = c .Signal (unix .SIGKILL )
461+
462+ events := make ([]unix.EpollEvent , 1 )
463+ for {
464+ // Set the timeout to 10s, the same as in kill below.
465+ n , err := unix .EpollWait (epollfd , events , 10000 )
466+ if err != nil {
467+ if err == unix .EINTR {
468+ continue
469+ }
470+ return err
471+ }
472+
473+ if n == 0 {
474+ return errors .New ("container init still running" )
475+ }
476+
477+ if n > 0 {
478+ event := events [0 ]
479+ if event .Fd == int32 (pidfd ) {
480+ return nil
481+ }
482+ }
483+ }
484+ }
485+
486+ func (c * Container ) kill () error {
487+ _ = c .Signal (unix .SIGKILL )
488+ for i := 0 ; i < 100 ; i ++ {
489+ time .Sleep (100 * time .Millisecond )
490+ if err := c .Signal (unix .Signal (0 )); err != nil {
491+ return nil
492+ }
493+ }
494+ return errors .New ("container init still running" )
495+ }
496+
497+ // KillAndWaitExit kills the container and waits for the init process to exit.
498+ func (c * Container ) KillAndWaitExit () error {
499+ // When a container doesn't have a private pidns, we have to kill all processes
500+ // in the cgroup, it's more simpler to use `cgroup.kill` or `unix.Kill`.
501+ if c .config .Namespaces .IsPrivate (configs .NEWPID ) {
502+ err := c .killViaPidfd ()
503+ if err == nil {
504+ return nil
505+ }
506+
507+ logrus .Debugf ("pidfd & epoll failed, falling back to unix.Signal: %v" , err )
508+ }
509+ return c .kill ()
510+ }
511+
434512func (c * Container ) createExecFifo () (retErr error ) {
435513 rootuid , err := c .config .HostRootUID ()
436514 if err != nil {
0 commit comments