Files
wehub-resource-sync e0e362d700
SDK Tests / changes (push) Successful in 2m29s
Real E2E Tests / changes (push) Successful in 2m29s
Deploy Docs Pages / build (push) Has been cancelled
Deploy Docs Pages / deploy (push) Has been cancelled
Real E2E Tests / JavaScript E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Python E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Java E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / C# E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Go E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Real E2E CI (push) Has been cancelled
SDK Tests / SDK CI (push) Has been cancelled
SDK Tests / CLI Tests (push) Has been cancelled
SDK Tests / Python SDK Quality (code-interpreter) (push) Has been cancelled
SDK Tests / Python SDK Quality (sandbox) (push) Has been cancelled
SDK Tests / Python SDK Tests (code-interpreter) (push) Has been cancelled
SDK Tests / JavaScript SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / JavaScript SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Python SDK Tests (sandbox) (push) Has been cancelled
SDK Tests / CLI Quality (push) Has been cancelled
SDK Tests / Kotlin SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Kotlin SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / C# SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / C# SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Go SDK Quality And Tests (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:39:33 +08:00

196 lines
4.9 KiB
Go

// Copyright 2026 Alibaba Group Holding Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build !windows
package runtime
import (
"fmt"
"io"
"os/exec"
"sync"
"syscall"
"time"
"github.com/alibaba/opensandbox/execd/pkg/isolation"
)
// IsolatedSessionOptions bundles the parameters for creating an isolated session.
type IsolatedSessionOptions struct {
Profile string
WorkspacePath string
WorkspaceMode string
ExtraWritable []string
Binds []isolation.BindMount
ShareNet *bool
EnvPassthroughMode string
EnvPassthroughKeys []string
Uid *uint32
Gid *uint32
UidMode string // "setpriv" (default) or "userns"
IdleTimeoutSeconds int
}
// isolatedSession holds a long-running bash process inside a bwrap namespace.
type isolatedSession struct {
id string
mu sync.RWMutex
runMu sync.Mutex // serializes concurrent Run calls
opts *IsolatedSessionOptions
cmd *exec.Cmd
stdin io.WriteCloser
stdout io.ReadCloser
doneCh chan struct{} // closed when the bwrap process exits
upperID string // key in UpperManager, used for Release/Remove
upperDir string
workDir string
createdAt time.Time
lastRunAt time.Time
isolator isolation.Isolator
}
func newIsolatedSession(id string, opts *IsolatedSessionOptions, iso isolation.Isolator) *isolatedSession {
return &isolatedSession{
id: id,
opts: opts,
isolator: iso,
doneCh: make(chan struct{}),
createdAt: time.Now(),
lastRunAt: time.Now(),
}
}
// start launches bwrap + bash inside a namespace.
func (s *isolatedSession) start() error {
cmd := exec.Command("bash", "--noprofile", "--norc")
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
wrapOpts := isolation.WrapOptions{
ExtraWritable: s.opts.ExtraWritable,
Binds: s.opts.Binds,
ShareNet: true,
}
switch s.opts.Profile {
case string(isolation.ProfileBalanced):
wrapOpts.Profile = isolation.ProfileBalanced
case string(isolation.ProfileStrict), "":
wrapOpts.Profile = isolation.ProfileStrict
default:
return fmt.Errorf("unknown isolation profile %q", s.opts.Profile)
}
wrapOpts.Workspace.Path = s.opts.WorkspacePath
switch isolation.WorkspaceMode(s.opts.WorkspaceMode) {
case isolation.WorkspaceRW:
wrapOpts.Workspace.Mode = isolation.WorkspaceRW
case isolation.WorkspaceRO:
wrapOpts.Workspace.Mode = isolation.WorkspaceRO
default:
wrapOpts.Workspace.Mode = isolation.WorkspaceOverlay
}
if s.opts.ShareNet != nil {
wrapOpts.ShareNet = *s.opts.ShareNet
}
if s.opts.EnvPassthroughMode != "" {
wrapOpts.EnvPassthrough.Mode = isolation.EnvMode(s.opts.EnvPassthroughMode)
wrapOpts.EnvPassthrough.Keys = s.opts.EnvPassthroughKeys
} else {
wrapOpts.EnvPassthrough.Mode = isolation.EnvModeDeny
}
wrapOpts.Uid = s.opts.Uid
wrapOpts.Gid = s.opts.Gid
if s.opts.UidMode != "" {
wrapOpts.UidMode = isolation.UidMode(s.opts.UidMode)
}
wrapOpts.UpperDir = s.upperDir
wrapOpts.WorkDir = s.workDir
if err := s.isolator.Wrap(cmd, wrapOpts); err != nil {
return err
}
stdin, err := cmd.StdinPipe()
if err != nil {
return err
}
stdout, err := cmd.StdoutPipe()
if err != nil {
stdin.Close()
return err
}
cmd.Stderr = cmd.Stdout
if err := cmd.Start(); err != nil {
stdin.Close()
stdout.Close()
for _, f := range cmd.ExtraFiles {
f.Close()
}
return err
}
for _, f := range cmd.ExtraFiles {
f.Close()
}
s.cmd = cmd
s.stdin = stdin
s.stdout = stdout
go func() {
_ = cmd.Wait()
close(s.doneCh)
}()
// Brief startup check — if bwrap fails immediately (bad capabilities,
// missing binary inside namespace, etc.) we detect it here instead of
// waiting until the first Run call.
select {
case <-s.doneCh:
return fmt.Errorf("bwrap process exited immediately after start")
case <-time.After(100 * time.Millisecond):
}
return nil
}
// stop kills the bwrap process group and waits for process reaping.
func (s *isolatedSession) stop() error {
if s.stdin != nil {
s.stdin.Close()
}
if s.stdout != nil {
s.stdout.Close()
}
if s.cmd != nil && s.cmd.Process != nil {
_ = syscall.Kill(-s.cmd.Process.Pid, syscall.SIGKILL)
// Wait for the death-watch goroutine to finish cmd.Wait().
<-s.doneCh
}
return nil
}
// dead returns true if the bwrap process has exited.
func (s *isolatedSession) dead() bool {
select {
case <-s.doneCh:
return true
default:
return false
}
}