namespaces.go 3.19 KB
Newer Older
1
2
3
4
5
6
7
8
9
// namespaces provides Linux namespace introspection for CRIU checkpoint.
package checkpoint

import (
	"fmt"

	"golang.org/x/sys/unix"
)

10
11
12
13
14
15
16
// NamespaceManifestEntry stores namespace information saved in checkpoint manifests.
type NamespaceManifestEntry struct {
	Type       string `yaml:"type"`       // net, pid, mnt, etc.
	Inode      uint64 `yaml:"inode"`      // Namespace inode
	IsExternal bool   `yaml:"isExternal"` // Whether namespace is external (shared)
}

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// NamespaceType represents a Linux namespace type
type NamespaceType string

const (
	NamespaceNet    NamespaceType = "net"
	NamespacePID    NamespaceType = "pid"
	NamespaceMnt    NamespaceType = "mnt"
	NamespaceUTS    NamespaceType = "uts"
	NamespaceIPC    NamespaceType = "ipc"
	NamespaceUser   NamespaceType = "user"
	NamespaceCgroup NamespaceType = "cgroup"
)

// NamespaceInfo holds namespace identification information
type NamespaceInfo struct {
	Type       NamespaceType
	Inode      uint64
	IsExternal bool // Whether NS is external (shared with pause container)
}

37
38
39
40
// NewNamespaceManifestEntries constructs namespace manifest entries from introspected namespaces.
func NewNamespaceManifestEntries(namespaces map[NamespaceType]*NamespaceInfo) []NamespaceManifestEntry {
	if len(namespaces) == 0 {
		return nil
41
42
	}

43
44
45
46
47
48
49
50
51
52
53
54
55
56
	result := make([]NamespaceManifestEntry, 0, len(namespaces))
	for nsType, nsInfo := range namespaces {
		result = append(result, NamespaceManifestEntry{
			Type:       string(nsType),
			Inode:      nsInfo.Inode,
			IsExternal: nsInfo.IsExternal,
		})
	}
	return result
}

// GetNamespaceInode returns the inode number for a namespace
func GetNamespaceInode(pid int, nsType NamespaceType) (uint64, error) {
	nsPath := fmt.Sprintf("%s/%d/ns/%s", HostProcPath, pid, nsType)
57
58
59
60
61
62
63
64
65
	var stat unix.Stat_t
	if err := unix.Stat(nsPath, &stat); err != nil {
		return 0, fmt.Errorf("failed to stat namespace %s: %w", nsPath, err)
	}

	return stat.Ino, nil
}

// GetNamespaceInfo returns detailed namespace information
66
67
func GetNamespaceInfo(pid int, nsType NamespaceType) (*NamespaceInfo, error) {
	nsPath := fmt.Sprintf("%s/%d/ns/%s", HostProcPath, pid, nsType)
68
69
70
71
72
73
74
75

	// Get inode
	var stat unix.Stat_t
	if err := unix.Stat(nsPath, &stat); err != nil {
		return nil, fmt.Errorf("failed to stat namespace %s: %w", nsPath, err)
	}

	// Check if this is different from init's namespace (PID 1)
76
	initNsPath := fmt.Sprintf("%s/1/ns/%s", HostProcPath, nsType)
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
	var initStat unix.Stat_t
	isExternal := false
	if err := unix.Stat(initNsPath, &initStat); err == nil {
		// If the inode is different from init's, it's an external namespace
		isExternal = stat.Ino != initStat.Ino
	}

	return &NamespaceInfo{
		Type:       nsType,
		Inode:      stat.Ino,
		IsExternal: isExternal,
	}, nil
}

// GetAllNamespaces returns information about all namespaces for a process
92
func GetAllNamespaces(pid int) (map[NamespaceType]*NamespaceInfo, error) {
93
94
95
96
97
98
99
100
101
102
103
104
	nsTypes := []NamespaceType{
		NamespaceNet,
		NamespacePID,
		NamespaceMnt,
		NamespaceUTS,
		NamespaceIPC,
		NamespaceUser,
		NamespaceCgroup,
	}

	namespaces := make(map[NamespaceType]*NamespaceInfo)
	for _, nsType := range nsTypes {
105
106
		if info, err := GetNamespaceInfo(pid, nsType); err == nil {
			namespaces[nsType] = info
107
108
109
110
111
		}
	}

	return namespaces, nil
}