1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
|
// Copyright (c) 2018-2019, Sylabs Inc. All rights reserved.
// This software is licensed under a 3-clause BSD license. Please consult the
// LICENSE.md file distributed with the sources of this project regarding your
// rights to use or distribute this software.
package singularity
import (
"fmt"
"os"
"syscall"
"github.com/sylabs/singularity/v4/internal/pkg/plugin"
singularitycallback "github.com/sylabs/singularity/v4/pkg/plugin/callback/runtime/engine/singularity"
)
// MonitorContainer is called from master once the container has
// been spawned. It will block until the container exists.
//
// Additional privileges may be gained when running
// in suid flow. However, when a user namespace is requested and it is not
// a hybrid workflow (e.g. fakeroot), then there is no privileged saved uid
// and thus no additional privileges can be gained.
//
// Particularly here no additional privileges are gained as monitor does
// not need them for wait4 and kill syscalls.
func (e *EngineOperations) MonitorContainer(pid int, signals chan os.Signal) (syscall.WaitStatus, error) {
var status syscall.WaitStatus
callbackType := (singularitycallback.MonitorContainer)(nil)
callbacks, err := plugin.LoadCallbacks(callbackType)
if err != nil {
return status, fmt.Errorf("while loading plugins callbacks '%T': %s", callbackType, err)
}
if len(callbacks) > 1 {
return status, fmt.Errorf("multiple plugins have registered callback for '%T'", callbackType)
} else if len(callbacks) == 1 {
//nolint:forcetypeassert
return callbacks[0].(singularitycallback.MonitorContainer)(e.CommonConfig, pid, signals)
}
for {
s := <-signals
switch s {
case syscall.SIGCHLD:
if wpid, err := syscall.Wait4(pid, &status, syscall.WNOHANG, nil); err != nil {
return status, fmt.Errorf("error while waiting child: %s", err)
} else if wpid != pid {
continue
}
return status, nil
case syscall.SIGURG:
// Ignore SIGURG, which is used for non-cooperative goroutine
// preemption starting with Go 1.14. For more information, see
// https://github.com/golang/go/issues/24543.
break
default:
if e.EngineConfig.GetSignalPropagation() {
//nolint:forcetypeassert
if err := syscall.Kill(pid, s.(syscall.Signal)); err != nil {
return status, fmt.Errorf("interrupted by signal %s", s.String())
}
}
// Handle CTRL-Z and send ourself a SIGSTOP to implicitly send SIGCHLD
// signal to parent process as this process is the direct child
if s == syscall.SIGTSTP {
if err := syscall.Kill(os.Getpid(), syscall.SIGSTOP); err != nil {
return status, fmt.Errorf("received SIGTSTP but was not able to stop")
}
}
}
}
}
|