1
// SPDX-License-Identifier: Apache-2.0
2
// Copyright Authors of Tetragon
14
"github.com/cilium/tetragon/pkg/cgroups"
15
"github.com/cilium/tetragon/pkg/logger"
16
"github.com/google/uuid"
20
// special error to inidicate that fileystem scanning found a file that
21
// matches the container id, without, however, matching the pod id.
22
ErrContainerPathWithoutMatchingPodID = errors.New("found cgroup file that matches the container id, but not the pod id")
25
// FsScanner is a utility for scanning the filesystem to find container cgroup directories.
26
type FsScanner interface {
27
// FindContainer returns:
28
// path, nil: if the container cgroup was found
29
// path, ErrContainerPathWithoutMatchingPodID: if the container cgroup was found, but the pod id did not match the parent directory
30
// "", err: if the container cgroup was not found.
32
// NB: FindContainerPath returns ErrContainerPathWithoutMatchingPodID only if the directory
33
// matching the pod id did not existed in the fs.
35
// Callers need to serialize concurrent access to this function on their own.
36
FindContainerPath(podID uuid.UUID, containerID string) (string, error)
40
return &fsScannerState{}
43
type fsScannerState struct {
49
// We 've seen some cases, where the container cgroup file is not in a
50
// directory that mathces the pod id. To handle these cases, do a full scan of
51
// the root to find a directory with the container id.
52
func fsFullContainerScan(root string, containerID string) string {
53
found := errors.New("Found")
55
// NB: there might be more efficient ways of searching the file-system than WalkDir, but
56
// use that for now and we can improve later as needed.
57
filepath.WalkDir(root, func(path string, dentry fs.DirEntry, err error) error {
58
if err != nil || !dentry.IsDir() {
61
base := filepath.Base(path)
62
if strings.Contains(base, containerID) {
71
// FindContainer implements FindContainer method from FsScanner
72
func (fs *fsScannerState) FindContainerPath(podID uuid.UUID, containerID string) (string, error) {
74
// first, check the known (cached) locations
75
for _, loc := range fs.knownPodDirs {
76
podDir, containerDir := findPodAndContainerDirectory(loc, podID, containerID)
81
if containerDir != "" {
82
return filepath.Join(podDir, containerDir), nil
85
// found the pod dir, but not the container dir. Return an error
86
return "", fmt.Errorf("found pod dir=%s but failed to find container for id=%s", podDir, containerID)
89
if fs.root == "" && fs.rootErr == nil {
90
fs.root, fs.rootErr = cgroups.HostCgroupRoot()
91
if fs.rootErr != nil {
92
logger.GetLogger().WithError(fs.rootErr).Warnf("failed to retrieve host cgroup root")
95
if fs.rootErr != nil {
96
return "", fmt.Errorf("no cgroup root")
99
podPath := fsFindPodPath(fs.root, podID)
101
contPath := fsFullContainerScan(fs.root, containerID)
103
return "", fmt.Errorf("no directory found that matches container-id '%s'", containerID)
105
return contPath, ErrContainerPathWithoutMatchingPodID
107
podDir := filepath.Dir(podPath)
108
// found a new pod directory, added it to the cached locations
109
fs.knownPodDirs = append(fs.knownPodDirs, podDir)
110
logger.GetLogger().Infof("adding %s to cgroup pod directories", podDir)
111
containerDir := findContainerDirectory(podPath, containerID)
112
if containerDir == "" {
113
return "", fmt.Errorf("found pod dir=%s but failed to find container for id=%s", podPath, containerID)
115
return filepath.Join(podPath, containerDir), nil
118
func podDirMatcher(podID uuid.UUID) func(p string) bool {
120
// replace '-' with '_' in "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
121
s2 := strings.Replace(s1, "-", "_", 4)
122
return func(p string) bool {
123
if strings.Contains(p, s1) {
126
if strings.Contains(p, s2) {
133
func findPodAndContainerDirectory(poddir string, podID uuid.UUID, containerID string) (string, string) {
134
podpath := findPodDirectory(poddir, podID)
138
podpath = filepath.Join(poddir, podpath)
139
contpath := findContainerDirectory(podpath, containerID)
140
return podpath, contpath
143
func findContainerDirectory(podpath string, containerID string) string {
144
entries, err := os.ReadDir(podpath)
148
for _, dentry := range entries {
153
name := dentry.Name()
154
// skip crio's conmon container
155
if strings.Contains(name, "crio-conmon") {
158
if strings.Contains(name, containerID) {
166
func findPodDirectory(poddir string, podID uuid.UUID) string {
167
podMatcher := podDirMatcher(podID)
168
entries, err := os.ReadDir(poddir)
172
for _, dentry := range entries {
177
name := dentry.Name()
178
if podMatcher(name) {
186
func fsFindPodPath(root string, podID uuid.UUID) string {
187
found := errors.New("Found")
188
podMatcher := podDirMatcher(podID)
190
// NB: there might be more efficient ways of searching the file-system than WalkDir, but
191
// use that for now and we can improve later as needed.
192
filepath.WalkDir(root, func(path string, dentry fs.DirEntry, err error) error {
193
if err != nil || !dentry.IsDir() {
196
base := filepath.Base(path)
197
if podMatcher(base) {