tetragon

Форк
0
/
cgroups.go 
759 строк · 22.1 Кб
1
// SPDX-License-Identifier: Apache-2.0
2
// Copyright Authors of Tetragon
3

4
//go:build linux
5
// +build linux
6

7
package cgroups
8

9
import (
10
	"bufio"
11
	"bytes"
12
	"encoding/binary"
13
	"fmt"
14
	"os"
15
	"path/filepath"
16
	"strconv"
17
	"strings"
18
	"sync"
19
	"syscall"
20

21
	"go.uber.org/multierr"
22
	"golang.org/x/sys/unix"
23

24
	"github.com/cilium/tetragon/pkg/defaults"
25
	"github.com/cilium/tetragon/pkg/logger"
26
	"github.com/cilium/tetragon/pkg/option"
27
	"github.com/sirupsen/logrus"
28
)
29

30
const (
31
	// Generic unset value that means undefined or not set
32
	CGROUP_UNSET_VALUE = 0
33

34
	// Max cgroup subsystems count that is used from BPF side
35
	// to define a max index for the default controllers on tasks.
36
	// For further documentation check BPF part.
37
	CGROUP_SUBSYS_COUNT = 15
38

39
	// The default hierarchy for cgroupv2
40
	CGROUP_DEFAULT_HIERARCHY = 0
41
)
42

43
type CgroupModeCode int
44

45
const (
46
	/* Cgroup Mode:
47
	 * https://systemd.io/CGROUP_DELEGATION/
48
	 * But this should work also for non-systemd environments: where
49
	 * only legacy or unified are available by default.
50
	 */
51
	CGROUP_UNDEF   CgroupModeCode = iota
52
	CGROUP_LEGACY  CgroupModeCode = 1
53
	CGROUP_HYBRID  CgroupModeCode = 2
54
	CGROUP_UNIFIED CgroupModeCode = 3
55
)
56

57
type DeploymentCode int
58

59
type deploymentEnv struct {
60
	id  DeploymentCode
61
	str string
62
}
63

64
const (
65
	// Deployment modes
66
	DEPLOY_UNKNOWN    DeploymentCode = iota
67
	DEPLOY_K8S        DeploymentCode = 1  // K8s deployment
68
	DEPLOY_CONTAINER  DeploymentCode = 2  // Container docker, podman, etc
69
	DEPLOY_SD_SERVICE DeploymentCode = 10 // Systemd service
70
	DEPLOY_SD_USER    DeploymentCode = 11 // Systemd user session
71
)
72

73
type CgroupController struct {
74
	Id     uint32 // Hierarchy unique ID
75
	Idx    uint32 // Cgroup SubSys index
76
	Name   string // Controller name
77
	Active bool   // Will be set to true if controller is set and active
78
}
79

80
var (
81
	// Path where default cgroupfs is mounted
82
	defaultCgroupRoot = "/sys/fs/cgroup"
83

84
	/* Cgroup controllers that we are interested in
85
	 * are usually the ones that are setup by systemd
86
	 * or other init programs.
87
	 */
88
	CgroupControllers = []CgroupController{
89
		{Name: "memory"}, // Memory first
90
		{Name: "pids"},   // pids second
91
		{Name: "cpuset"}, // fallback
92
	}
93

94
	cgroupv2Hierarchy = "0::"
95

96
	/* Ordered from nested to top cgroup parents
97
	 * For k8s we check also config k8s flags.
98
	 */
99
	deployments = []deploymentEnv{
100
		{id: DEPLOY_K8S, str: "kube"},
101
		{id: DEPLOY_CONTAINER, str: "docker"},
102
		{id: DEPLOY_CONTAINER, str: "podman"},
103
		{id: DEPLOY_CONTAINER, str: "libpod"},
104
		{id: DEPLOY_SD_SERVICE, str: "system.slice"},
105
		{id: DEPLOY_SD_USER, str: "user.slice"},
106
	}
107

108
	detectDeploymentOnce sync.Once
109
	deploymentMode       DeploymentCode
110

111
	detectCgrpModeOnce sync.Once
112
	cgroupMode         CgroupModeCode
113

114
	detectCgroupFSOnce sync.Once
115
	cgroupFSPath       string
116
	cgroupFSMagic      uint64
117

118
	// Cgroup Migration Path
119
	findMigPath       sync.Once
120
	cgrpMigrationPath string
121

122
	// Cgroup Tracking Hierarchy
123
	cgrpHierarchy    uint32
124
	cgrpSubsystemIdx uint32
125
)
126

127
func (code CgroupModeCode) String() string {
128
	return [...]string{
129
		CGROUP_UNDEF:   "undefined",
130
		CGROUP_LEGACY:  "Legacy mode (Cgroupv1)",
131
		CGROUP_HYBRID:  "Hybrid mode (Cgroupv1 and Cgroupv2)",
132
		CGROUP_UNIFIED: "Unified mode (Cgroupv2)",
133
	}[code]
134
}
135

136
func (op DeploymentCode) String() string {
137
	return [...]string{
138
		DEPLOY_UNKNOWN:    "unknown",
139
		DEPLOY_K8S:        "Kubernetes",
140
		DEPLOY_CONTAINER:  "Container",
141
		DEPLOY_SD_SERVICE: "systemd service",
142
		DEPLOY_SD_USER:    "systemd user session",
143
	}[op]
144
}
145

146
// DetectCgroupFSMagic() runs by default DetectCgroupMode()
147
// CgroupFsMagicStr() Returns "Cgroupv2" or "Cgroupv1" based on passed magic.
148
func CgroupFsMagicStr(magic uint64) string {
149
	if magic == unix.CGROUP2_SUPER_MAGIC {
150
		return "Cgroupv2"
151
	} else if magic == unix.CGROUP_SUPER_MAGIC {
152
		return "Cgroupv1"
153
	}
154

155
	return ""
156
}
157

158
func GetCgroupFSMagic() uint64 {
159
	return cgroupFSMagic
160
}
161

162
func GetCgroupFSPath() string {
163
	return cgroupFSPath
164
}
165

166
type FileHandle struct {
167
	Id uint64
168
}
169

170
func GetCgroupIdFromPath(cgroupPath string) (uint64, error) {
171
	var fh FileHandle
172

173
	handle, _, err := unix.NameToHandleAt(unix.AT_FDCWD, cgroupPath, 0)
174
	if err != nil {
175
		return 0, err
176
	}
177

178
	err = binary.Read(bytes.NewBuffer(handle.Bytes()), binary.LittleEndian, &fh)
179
	if err != nil {
180
		return 0, fmt.Errorf("decoding NameToHandleAt data failed: %v", err)
181
	}
182

183
	return fh.Id, nil
184
}
185

186
func parseCgroupSubSysIds(filePath string) error {
187
	var allcontrollers []string
188

189
	file, err := os.Open(filePath)
190
	if err != nil {
191
		return err
192
	}
193

194
	defer file.Close()
195

196
	fscanner := bufio.NewScanner(file)
197
	fixed := false
198
	idx := 0
199
	fscanner.Scan() // ignore first entry
200
	for fscanner.Scan() {
201
		line := fscanner.Text()
202
		fields := strings.Fields(line)
203

204
		allcontrollers = append(allcontrollers, fields[0])
205

206
		// No need to read enabled field as it can be enabled on
207
		// root without having a proper cgroup name to reflect that
208
		// or the controller is not active on the unified cgroupv2.
209
		for i, controller := range CgroupControllers {
210
			if fields[0] == controller.Name {
211
				/* We care only for the controllers that we want */
212
				if idx >= CGROUP_SUBSYS_COUNT {
213
					/* Maybe some cgroups are not upstream? */
214
					return fmt.Errorf("Cgroup default subsystem '%s' is indexed at idx=%d higher than CGROUP_SUBSYS_COUNT=%d",
215
						fields[0], idx, CGROUP_SUBSYS_COUNT)
216
				}
217

218
				id, err := strconv.ParseUint(fields[1], 10, 32)
219
				if err == nil {
220
					CgroupControllers[i].Id = uint32(id)
221
					CgroupControllers[i].Idx = uint32(idx)
222
					CgroupControllers[i].Active = true
223
					fixed = true
224
				} else {
225
					logger.GetLogger().WithFields(logrus.Fields{
226
						"cgroup.fs":              cgroupFSPath,
227
						"cgroup.controller.name": controller.Name,
228
					}).WithError(err).Warnf("parsing controller line from '%s' failed", filePath)
229
				}
230
			}
231
		}
232
		idx++
233
	}
234

235
	logger.GetLogger().WithFields(logrus.Fields{
236
		"cgroup.fs":          cgroupFSPath,
237
		"cgroup.controllers": fmt.Sprintf("[%s]", strings.Join(allcontrollers, " ")),
238
	}).Debugf("Cgroup available controllers")
239

240
	// Could not find 'memory', 'pids' nor 'cpuset' controllers, are they compiled in?
241
	if !fixed {
242
		err = fmt.Errorf("detect cgroup controllers IDs from '%s' failed", filePath)
243
		logger.GetLogger().WithFields(logrus.Fields{
244
			"cgroup.fs": cgroupFSPath,
245
		}).WithError(err).Warnf("Cgroup controllers 'memory', 'pids' and 'cpuset' are missing")
246
		return err
247
	}
248

249
	for _, controller := range CgroupControllers {
250
		// Print again everything that is available or not
251
		if controller.Active {
252
			logger.GetLogger().WithFields(logrus.Fields{
253
				"cgroup.fs":                     cgroupFSPath,
254
				"cgroup.controller.name":        controller.Name,
255
				"cgroup.controller.hierarchyID": controller.Id,
256
				"cgroup.controller.index":       controller.Idx,
257
			}).Infof("Supported cgroup controller '%s' is active on the system", controller.Name)
258
		} else {
259
			// Warn with error
260
			err = fmt.Errorf("controller '%s' is not active", controller.Name)
261
			logger.GetLogger().WithField("cgroup.fs", cgroupFSPath).WithError(err).Warnf("Supported cgroup controller '%s' is not active", controller.Name)
262
		}
263
	}
264

265
	return nil
266
}
267

268
// DiscoverSubSysIds() Discover Cgroup SubSys IDs and indexes.
269
// of the corresponding controllers that we are interested
270
// in. We need this dynamic behavior since these controllers are
271
// compile config.
272
func DiscoverSubSysIds() error {
273
	return parseCgroupSubSysIds(filepath.Join(option.Config.ProcFS, "cgroups"))
274
}
275

276
func setDeploymentMode(cgroupPath string) error {
277
	if deploymentMode != DEPLOY_UNKNOWN {
278
		return nil
279
	}
280

281
	if option.Config.EnableK8s {
282
		deploymentMode = DEPLOY_K8S
283
		return nil
284
	}
285

286
	if cgroupPath == "" {
287
		// Probably namespaced
288
		deploymentMode = DEPLOY_CONTAINER
289
		return nil
290
	}
291

292
	// Last go through the deployments
293
	for _, d := range deployments {
294
		if strings.Contains(cgroupPath, d.str) {
295
			deploymentMode = d.id
296
			return nil
297
		}
298
	}
299

300
	return fmt.Errorf("detect deployment mode failed, no match on Cgroup path '%s'", cgroupPath)
301
}
302

303
func GetDeploymentMode() DeploymentCode {
304
	return deploymentMode
305
}
306

307
func GetCgroupMode() CgroupModeCode {
308
	return cgroupMode
309
}
310

311
func setCgrpHierarchyID(controller *CgroupController) {
312
	cgrpHierarchy = controller.Id
313
}
314

315
func setCgrp2HierarchyID() {
316
	cgrpHierarchy = CGROUP_DEFAULT_HIERARCHY
317
}
318

319
func setCgrpSubsystemIdx(controller *CgroupController) {
320
	cgrpSubsystemIdx = controller.Idx
321
}
322

323
// GetCgrpHierarchyID() returns the ID of the Cgroup hierarchy
324
// that is used to track processes. This is used for Cgroupv1 as for
325
// Cgroupv2 we run in the default hierarchy.
326
func GetCgrpHierarchyID() uint32 {
327
	return cgrpHierarchy
328
}
329

330
// GetCgrpSubsystemIdx() returns the Index of the subsys
331
// or hierarchy to be used to track processes.
332
func GetCgrpSubsystemIdx() uint32 {
333
	return cgrpSubsystemIdx
334
}
335

336
// GetCgrpControllerName() returns the name of the controller that is
337
// being used as fallback from the css to get cgroup information and
338
// track processes.
339
func GetCgrpControllerName() string {
340
	for _, controller := range CgroupControllers {
341
		if controller.Active && controller.Idx == cgrpSubsystemIdx {
342
			return controller.Name
343
		}
344
	}
345
	return ""
346
}
347

348
// Validates cgroupPaths obtained from /proc/self/cgroup based on Cgroupv1
349
// and returns it on success
350
func getValidCgroupv1Path(cgroupPaths []string) (string, error) {
351
	for _, controller := range CgroupControllers {
352
		// First lets go again over list of active controllers
353
		if !controller.Active {
354
			logger.GetLogger().WithField("cgroup.fs", cgroupFSPath).Debugf("Cgroup controller '%s' is not active", controller.Name)
355
			continue
356
		}
357

358
		for _, s := range cgroupPaths {
359
			if strings.Contains(s, fmt.Sprintf(":%s:", controller.Name)) {
360
				idx := strings.Index(s, "/")
361
				path := s[idx+1:]
362
				cgroupPath := filepath.Join(cgroupFSPath, controller.Name, path)
363
				finalpath := filepath.Join(cgroupPath, "cgroup.procs")
364
				_, err := os.Stat(finalpath)
365
				if err != nil {
366
					// Probably namespaced... run the deployment mode detection
367
					err = setDeploymentMode(path)
368
					if err == nil {
369
						mode := GetDeploymentMode()
370
						if mode == DEPLOY_K8S || mode == DEPLOY_CONTAINER {
371
							// Cgroups are namespaced let's try again
372
							cgroupPath = filepath.Join(cgroupFSPath, controller.Name)
373
							finalpath = filepath.Join(cgroupPath, "cgroup.procs")
374
							_, err = os.Stat(finalpath)
375
						}
376
					}
377
				}
378

379
				if err != nil {
380
					logger.GetLogger().WithField("cgroup.fs", cgroupFSPath).WithError(err).Warnf("Failed to validate Cgroupv1 path '%s'", finalpath)
381
					continue
382
				}
383

384
				// Run the deployment mode detection last again, fine to rerun.
385
				err = setDeploymentMode(path)
386
				if err != nil {
387
					logger.GetLogger().WithField("cgroup.fs", cgroupFSPath).WithError(err).Warn("Failed to detect deployment mode from Cgroupv1 path")
388
					continue
389
				}
390

391
				logger.GetLogger().WithFields(logrus.Fields{
392
					"cgroup.fs":                     cgroupFSPath,
393
					"cgroup.controller.name":        controller.Name,
394
					"cgroup.controller.hierarchyID": controller.Id,
395
					"cgroup.controller.index":       controller.Idx,
396
				}).Infof("Cgroupv1 controller '%s' will be used", controller.Name)
397

398
				setCgrpHierarchyID(&controller)
399
				setCgrpSubsystemIdx(&controller)
400
				logger.GetLogger().WithFields(logrus.Fields{
401
					"cgroup.fs":   cgroupFSPath,
402
					"cgroup.path": cgroupPath,
403
				}).Info("Cgroupv1 hierarchy validated successfully")
404
				return finalpath, nil
405
			}
406
		}
407
	}
408

409
	// Cgroupv1 hierarchy is not properly setup we can not support such systems,
410
	// reason should have been logged in above messages.
411
	return "", fmt.Errorf("could not validate Cgroupv1 hierarchies")
412
}
413

414
// Lookup Cgroupv2 active controllers and returns one that we support
415
func getCgroupv2Controller(cgroupPath string) (*CgroupController, error) {
416
	file := filepath.Join(cgroupPath, "cgroup.controllers")
417
	data, err := os.ReadFile(file)
418
	if err != nil {
419
		return nil, fmt.Errorf("failed to read %s: %v", file, err)
420
	}
421

422
	activeControllers := strings.TrimRight(string(data), "\n")
423
	if len(activeControllers) == 0 {
424
		return nil, fmt.Errorf("no active controllers from '%s'", file)
425
	}
426

427
	logger.GetLogger().WithFields(logrus.Fields{
428
		"cgroup.fs":          cgroupFSPath,
429
		"cgroup.controllers": strings.Fields(activeControllers),
430
	}).Info("Cgroupv2 supported controllers detected successfully")
431

432
	for i, controller := range CgroupControllers {
433
		if controller.Active && strings.Contains(activeControllers, controller.Name) {
434
			logger.GetLogger().WithFields(logrus.Fields{
435
				"cgroup.fs":                     cgroupFSPath,
436
				"cgroup.controller.name":        controller.Name,
437
				"cgroup.controller.hierarchyID": controller.Id,
438
				"cgroup.controller.index":       controller.Idx,
439
			}).Infof("Cgroupv2 controller '%s' will be used as a fallback for the default hierarchy", controller.Name)
440
			return &CgroupControllers[i], nil
441
		}
442
	}
443

444
	// Cgroupv2 hierarchy does not have the appropriate controllers.
445
	// Maybe init system or any other component failed to prepare cgroups properly.
446
	return nil, fmt.Errorf("Cgroupv2 no appropriate active controller")
447
}
448

449
// Validates cgroupPaths obtained from /proc/self/cgroup based on Cgroupv2
450
func getValidCgroupv2Path(cgroupPaths []string) (string, error) {
451
	for _, s := range cgroupPaths {
452
		if strings.Contains(s, cgroupv2Hierarchy) {
453
			idx := strings.Index(s, "/")
454
			path := s[idx+1:]
455
			cgroupPath := filepath.Join(cgroupFSPath, path)
456
			finalpath := filepath.Join(cgroupPath, "cgroup.procs")
457
			_, err := os.Stat(finalpath)
458
			if err != nil {
459
				// Namespaced ? let's force the check
460
				err = setDeploymentMode(path)
461
				if err == nil {
462
					mode := GetDeploymentMode()
463
					if mode == DEPLOY_K8S || mode == DEPLOY_CONTAINER {
464
						// Cgroups are namespaced let's try again
465
						cgroupPath = cgroupFSPath
466
						finalpath = filepath.Join(cgroupPath, "cgroup.procs")
467
						_, err = os.Stat(finalpath)
468
					}
469
				}
470
			}
471

472
			if err != nil {
473
				logger.GetLogger().WithField("cgroup.fs", cgroupFSPath).WithError(err).Warnf("Failed to validate Cgroupv2 path '%s'", finalpath)
474
				break
475
			}
476

477
			// This should not be necessary but there are broken setups out there
478
			// without cgroupv2 default bpf helpers
479
			controller, err := getCgroupv2Controller(cgroupPath)
480
			if err != nil {
481
				logger.GetLogger().WithField("cgroup.fs", cgroupFSPath).WithError(err).Warnf("Failed to detect current Cgroupv2 active controller")
482
				break
483
			}
484

485
			// Run the deployment mode detection last again, fine to rerun.
486
			err = setDeploymentMode(path)
487
			if err != nil {
488
				logger.GetLogger().WithField("cgroup.fs", cgroupFSPath).WithError(err).Warn("Failed to detect deployment mode from Cgroupv2 path")
489
				break
490
			}
491

492
			setCgrp2HierarchyID()
493
			setCgrpSubsystemIdx(controller)
494
			logger.GetLogger().WithFields(logrus.Fields{
495
				"cgroup.fs":   cgroupFSPath,
496
				"cgroup.path": cgroupPath,
497
			}).Info("Cgroupv2 hierarchy validated successfully")
498
			return finalpath, nil
499
		}
500
	}
501

502
	// Cgroupv2 hierarchy is not properly setup we can not support such systems,
503
	// reason should have been logged in above messages.
504
	return "", fmt.Errorf("could not validate Cgroupv2 hierarchy")
505
}
506

507
func getPidCgroupPaths(pid uint32) ([]string, error) {
508
	file := filepath.Join(option.Config.ProcFS, fmt.Sprint(pid), "cgroup")
509

510
	cgroups, err := os.ReadFile(file)
511
	if err != nil {
512
		return nil, fmt.Errorf("failed to read %s: %v", file, err)
513
	}
514

515
	if len(cgroups) == 0 {
516
		return nil, fmt.Errorf("no entry from %s", file)
517
	}
518

519
	return strings.Split(strings.TrimSpace(string(cgroups)), "\n"), nil
520
}
521

522
func findMigrationPath(pid uint32) (string, error) {
523
	if cgrpMigrationPath != "" {
524
		return cgrpMigrationPath, nil
525
	}
526

527
	cgroupPaths, err := getPidCgroupPaths(pid)
528
	if err != nil {
529
		logger.GetLogger().WithField("cgroup.fs", cgroupFSPath).WithError(err).Warnf("Unable to get Cgroup paths for pid=%d", pid)
530
		return "", err
531
	}
532

533
	mode, err := DetectCgroupMode()
534
	if err != nil {
535
		return "", err
536
	}
537

538
	/* Run the validate and get cgroup migration path once
539
	 * as it triggers lot of checks.
540
	 */
541
	findMigPath.Do(func() {
542
		var err error
543
		switch mode {
544
		case CGROUP_LEGACY, CGROUP_HYBRID:
545
			cgrpMigrationPath, err = getValidCgroupv1Path(cgroupPaths)
546
		case CGROUP_UNIFIED:
547
			cgrpMigrationPath, err = getValidCgroupv2Path(cgroupPaths)
548
		default:
549
			err = fmt.Errorf("could not detect Cgroup Mode")
550
		}
551

552
		if err != nil {
553
			logger.GetLogger().WithField("cgroup.fs", cgroupFSPath).WithError(err).Warnf("Unable to find Cgroup migration path for pid=%d", pid)
554
		}
555
	})
556

557
	if cgrpMigrationPath == "" {
558
		return "", fmt.Errorf("could not detect Cgroup migration path for pid=%d", pid)
559
	}
560

561
	return cgrpMigrationPath, nil
562
}
563

564
func detectCgroupMode(cgroupfs string) (CgroupModeCode, error) {
565
	var st syscall.Statfs_t
566

567
	if err := syscall.Statfs(cgroupfs, &st); err != nil {
568
		return CGROUP_UNDEF, err
569
	}
570

571
	if st.Type == unix.CGROUP2_SUPER_MAGIC {
572
		return CGROUP_UNIFIED, nil
573
	} else if st.Type == unix.TMPFS_MAGIC {
574
		err := syscall.Statfs(filepath.Join(cgroupfs, "unified"), &st)
575
		if err == nil && st.Type == unix.CGROUP2_SUPER_MAGIC {
576
			return CGROUP_HYBRID, nil
577
		}
578
		return CGROUP_LEGACY, nil
579
	}
580

581
	return CGROUP_UNDEF, fmt.Errorf("wrong type '%d' for cgroupfs '%s'", st.Type, cgroupfs)
582
}
583

584
// DetectCgroupMode() Returns the current Cgroup mode that is applied to the system
585
// This applies to systemd and non-systemd machines, possible values:
586
//   - CGROUP_UNDEF: undefined
587
//   - CGROUP_LEGACY: Cgroupv1 legacy controllers
588
//   - CGROUP_HYBRID: Cgroupv1 and Cgroupv2 set up by systemd
589
//   - CGROUP_UNIFIED: Pure Cgroupv2 hierarchy
590
//
591
// Reference: https://systemd.io/CGROUP_DELEGATION/
592
func DetectCgroupMode() (CgroupModeCode, error) {
593
	detectCgrpModeOnce.Do(func() {
594
		var err error
595
		cgroupFSPath = defaultCgroupRoot
596
		cgroupMode, err = detectCgroupMode(cgroupFSPath)
597
		if err != nil {
598
			logger.GetLogger().WithError(err).WithField("cgroup.fs", cgroupFSPath).Debug("Could not detect Cgroup Mode")
599
			cgroupMode, err = detectCgroupMode(defaults.Cgroup2Dir)
600
			if err != nil {
601
				logger.GetLogger().WithError(err).WithField("cgroup.fs", defaults.Cgroup2Dir).Debug("Could not detect Cgroup Mode")
602
			} else {
603
				cgroupFSPath = defaults.Cgroup2Dir
604
			}
605
		}
606
		if cgroupMode != CGROUP_UNDEF {
607
			logger.GetLogger().WithFields(logrus.Fields{
608
				"cgroup.fs":   cgroupFSPath,
609
				"cgroup.mode": cgroupMode.String(),
610
			}).Infof("Cgroup mode detection succeeded")
611
		}
612
	})
613

614
	if cgroupMode == CGROUP_UNDEF {
615
		return CGROUP_UNDEF, fmt.Errorf("could not detect Cgroup Mode")
616
	}
617

618
	return cgroupMode, nil
619
}
620

621
func detectDeploymentMode() (DeploymentCode, error) {
622
	mode := GetDeploymentMode()
623
	if mode != DEPLOY_UNKNOWN {
624
		return mode, nil
625
	}
626

627
	// Let's call findMigrationPath in case to parse own cgroup
628
	// paths and detect the deployment mode.
629
	pid := os.Getpid()
630
	_, err := findMigrationPath(uint32(pid))
631
	if err != nil {
632
		return DEPLOY_UNKNOWN, err
633
	}
634

635
	return GetDeploymentMode(), nil
636
}
637

638
func DetectDeploymentMode() (uint32, error) {
639
	detectDeploymentOnce.Do(func() {
640
		mode, err := detectDeploymentMode()
641
		if err != nil {
642
			logger.GetLogger().WithFields(logrus.Fields{
643
				"cgroup.fs": cgroupFSPath,
644
			}).WithError(err).Warn("Detection of deployment mode failed")
645
			return
646
		}
647

648
		logger.GetLogger().WithFields(logrus.Fields{
649
			"cgroup.fs":       cgroupFSPath,
650
			"deployment.mode": DeploymentCode(mode).String(),
651
		}).Info("Deployment mode detection succeeded")
652
	})
653

654
	mode := GetDeploymentMode()
655
	if mode == DEPLOY_UNKNOWN {
656
		return uint32(mode), fmt.Errorf("detect deployment mode failed, could not parse process cgroup paths")
657
	}
658

659
	return uint32(mode), nil
660
}
661

662
// DetectCgroupFSMagic() runs by default DetectCgroupMode()
663
// Return the Cgroupfs v1 or v2 that will be used by bpf programs
664
func DetectCgroupFSMagic() (uint64, error) {
665
	// Run get cgroup mode again in case
666
	mode, err := DetectCgroupMode()
667
	if err != nil {
668
		return CGROUP_UNSET_VALUE, err
669
	}
670

671
	// Run this once and log output
672
	detectCgroupFSOnce.Do(func() {
673
		switch mode {
674
		case CGROUP_LEGACY, CGROUP_HYBRID:
675
			/* In both legacy or Hybrid modes we switch to Cgroupv1 from bpf side. */
676
			logger.GetLogger().WithField("cgroup.fs", cgroupFSPath).Debug("Cgroup BPF helpers will run in raw Cgroup mode")
677
			cgroupFSMagic = unix.CGROUP_SUPER_MAGIC
678
		case CGROUP_UNIFIED:
679
			logger.GetLogger().WithField("cgroup.fs", cgroupFSPath).Debug("Cgroup BPF helpers will run in Cgroupv2 mode or fallback to raw Cgroup on errors")
680
			cgroupFSMagic = unix.CGROUP2_SUPER_MAGIC
681
		}
682
	})
683

684
	if cgroupFSMagic == CGROUP_UNSET_VALUE {
685
		return CGROUP_UNSET_VALUE, fmt.Errorf("could not detect Cgroup filesystem Magic")
686
	}
687

688
	return cgroupFSMagic, nil
689
}
690

691
// CgroupNameFromCstr() Returns a Golang string from the passed C language format string.
692
func CgroupNameFromCStr(cstr []byte) string {
693
	i := bytes.IndexByte(cstr, 0)
694
	if i == -1 {
695
		i = len(cstr)
696
	}
697
	return string(cstr[:i])
698
}
699

700
func tryHostCgroup(path string) error {
701
	var st, pst unix.Stat_t
702
	if err := unix.Lstat(path, &st); err != nil {
703
		return fmt.Errorf("cannot determine cgroup root: error acessing path '%s': %w", path, err)
704
	}
705

706
	parent := filepath.Dir(path)
707
	if err := unix.Lstat(parent, &pst); err != nil {
708
		return fmt.Errorf("cannot determine cgroup root: error acessing parent path '%s': %w", parent, err)
709
	}
710

711
	if st.Dev == pst.Dev {
712
		return fmt.Errorf("cannot determine cgroup root: '%s' does not appear to be a mount point", path)
713
	}
714

715
	fst := unix.Statfs_t{}
716
	if err := unix.Statfs(path, &fst); err != nil {
717
		return fmt.Errorf("cannot determine cgroup root: failed to get info for '%s'", path)
718
	}
719

720
	switch fst.Type {
721
	case unix.CGROUP2_SUPER_MAGIC, unix.CGROUP_SUPER_MAGIC:
722
		return nil
723
	default:
724
		return fmt.Errorf("cannot determine cgroup root: path '%s' is not a cgroup fs", path)
725
	}
726
}
727

728
// HostCgroupRoot tries to retrieve the host cgroup root
729
//
730
// For cgroupv1, we return the directory of the contoller currently used.
731
//
732
// NB(kkourt): for now we are checking /sys/fs/cgroup under host /proc's init.
733
// For systems where the cgroup is mounted in a non-standard location, we could
734
// also check host's /proc/mounts.
735
func HostCgroupRoot() (string, error) {
736
	components := []string{
737
		option.Config.ProcFS, "1", "root",
738
		"sys", "fs", "cgroup",
739
		GetCgrpControllerName(),
740
	}
741

742
	path1 := filepath.Join(components...)
743
	err1 := tryHostCgroup(path1)
744
	if err1 == nil {
745
		return path1, nil
746
	}
747

748
	path2 := filepath.Join(components[:len(components)-1]...)
749
	err2 := tryHostCgroup(path2)
750
	if err2 == nil {
751
		return path2, nil
752
	}
753

754
	err := multierr.Append(
755
		fmt.Errorf("failed to set path %s as cgroup root %w", path1, err1),
756
		fmt.Errorf("failed to set path %s as cgroup root %w", path2, err2),
757
	)
758
	return "", fmt.Errorf("failed to set cgroup root: %w", err)
759
}
760

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.