func commandDroppedPrivs()

in cmd/hotdog-hotpatch/main.go [211:280]


func commandDroppedPrivs(name string, arg []string, uid, gid, targetPID int) ([]byte, error) {
	cmd := cap.NewLauncher(name, append([]string{name}, arg...), nil)
	if uid != 0 {
		cmd.SetUID(uid)
		cmd.SetMode(cap.ModeNoPriv)
		logger.Printf("dropping all capabilities and switching UID to %d for %s", uid, name)
	}
	if gid != 0 {
		cmd.SetGroups(gid, nil)
	}
	pr, pw, err := os.Pipe()
	defer pw.Close()
	if err != nil {
		return nil, fmt.Errorf("failed to allocate pipe for %q: %w", name, err)
	}
	buf := &bytes.Buffer{}
	go func() {
		io.Copy(buf, pr)
		pr.Close()
	}()
	cmd.Callback(func(attr *syscall.ProcAttr, i interface{}) error {
		// Capture stdout/stderr, since we can't rely on the exec package to
		//do it for us here.
		attr.Files[1] = pw.Fd()
		attr.Files[2] = pw.Fd()

		// cap.* functions called within the callback only affect the locked OS
		// thread used to launch the program
		if targetPID != 0 && uid == 0 {
			set, err := cap.GetPID(targetPID)
			if err != nil {
				return fmt.Errorf("failed to load caps for %d: %w", targetPID, err)
			}
			if err := set.ClearFlag(cap.Inheritable); err != nil {
				return fmt.Errorf("failed to clear inheritable set: %w", err)
			}
			iabSet, err := cap.IABGetPID(targetPID)
			if err != nil {
				return fmt.Errorf("failed to load IAB for %d: %w", targetPID, err)
			}
			if err := iabSet.SetProc(); err != nil {
				return fmt.Errorf("failed to set IAB: %w", err)
			}
			if err := cap.ResetAmbient(); err != nil {
				return fmt.Errorf("failed to reset ambient: %w", err)
			}
			if err := set.SetProc(); err != nil {
				return fmt.Errorf("failed to set caps: %w", err)
			}
			logger.Printf("setting caps to %q for %q", set.String(), name)
			logger.Printf("setting IAB to %q for %q", iabSet.String(), name)
		}
		return nil
	})
	pid, err := cmd.Launch(nil)
	if err != nil {
		return nil, fmt.Errorf("failed to launch %q: %w", name, err)
	}
	proc := os.Process{Pid: pid}
	state, err := proc.Wait()
	if err != nil {
		return nil, fmt.Errorf("failed to wait on %q: %w", name, err)
	}
	if state.ExitCode() != 0 {
		err = &exec.ExitError{ProcessState: state}
	}

	versionOut := buf.Bytes()
	return versionOut, err
}