//go:build linux package activator import ( "context" "os" "os/exec" "path/filepath" "strconv" "strings" "github.com/carlosframework/platform/internal/cgroup" "github.com/carlosframework/platform/internal/hostbox" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/hostwrite" "golang.org/x/sys/unix" ) // Start never executes tenant code in the agent's namespace. With the // isolated root protocol it asks root to run the reserved role in main's // isolated unit; without it every shape stays refused. func (l RoleLaunch) Start(ctx context.Context, role string, attempt hostclaim.WakeRetryAttempt, build func() *exec.Cmd, before func() error) (*exec.Cmd, *cgroup.Prepared, func(context.Context) error, RoleProducer, error) { if l.Root == nil { return nil, nil, nil, RoleProducer{}, ErrClaimIsolationUnavailable } return l.startIsolated(ctx, role, attempt, build, before) } func roleProcessStart(pid int) (string, error) { raw, err := os.ReadFile("/proc/" + strconv.Itoa(pid) + "/stat") if err != nil { return "", err } i := strings.LastIndexByte(string(raw), ')') if i < 0 { return "", hostwrite.ErrUnavailable } fields := strings.Fields(string(raw)[i+1:]) if len(fields) < 20 || fields[0] == "Z" || fields[0] == "X" { return "", hostwrite.ErrUnavailable } return fields[19], nil } func (l RoleLaunch) ExactHealth(ctx context.Context, cmd *exec.Cmd, a hostclaim.WakeRetryAttempt) error { _, err := l.RoleCgroup(ctx, cmd, a) return err } // RoleCgroup returns the absolute cgroup directory of the started main role // for attempt a after the same checks as ExactHealth: the journalled started // reservation names cmd's pid and process start, its group still has the // journalled device and inode, and the pid is a member of exactly that group. func (l RoleLaunch) RoleCgroup(ctx context.Context, cmd *exec.Cmd, a hostclaim.WakeRetryAttempt) (string, error) { if l.Journal == nil || cmd == nil || cmd.Process == nil || !filepath.IsAbs(l.CgroupRoot) { return "", hostwrite.ErrUnavailable } inventory, err := l.Journal.Read(ctx, l.Target) if err != nil { return "", err } started := "started" if l.Root != nil { // A root-launched role is only ever a unit reservation root wrote. started = hostbox.UnitStarted } for _, r := range inventory.Reservations { if r.ID != a.ID+":main" || r.State != started || r.InvocationID != a.InvocationID || r.Inputs != a.LaunchInputs || r.PID != cmd.Process.Pid || r.Target.Session != a.Session { continue } start, err := roleProcessStart(r.PID) if err != nil { return "", err } if start != r.ProcessStart { return "", hostbox.ErrChanged } group := filepath.Join(l.CgroupRoot, r.Group.RelativePath) var stat unix.Stat_t if err = unix.Stat(group, &stat); err != nil { return "", err } if uint64(stat.Dev) != r.Group.Device || stat.Ino != r.Group.Inode { return "", hostbox.ErrChanged } rel, err := filepath.Rel("/sys/fs/cgroup", group) if err != nil || strings.HasPrefix(rel, "..") { return "", hostwrite.ErrUnavailable } raw, err := os.ReadFile("/proc/" + strconv.Itoa(r.PID) + "/cgroup") if err != nil { return "", err } found := false for _, line := range strings.Split(string(raw), "\n") { if line == "0::/"+rel { found = true } } if !found { return "", hostbox.ErrChanged } return group, nil } return "", hostbox.ErrChanged }