File: monitor_linux.go

package info (click to toggle)
singularity-container 4.1.5%2Bds4-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 43,876 kB
  • sloc: asm: 14,840; sh: 3,190; ansic: 1,751; awk: 414; makefile: 413; python: 99
file content (73 lines) | stat: -rw-r--r-- 2,689 bytes parent folder | download | duplicates (2)
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")
				}
			}
		}
	}
}