Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 64 additions & 3 deletions components/execd/pkg/runtime/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"os"
"os/exec"
"os/signal"
"os/user"
"strconv"
"sync"
"syscall"
Expand All @@ -34,6 +35,46 @@ import (
"github.com/alibaba/opensandbox/execd/pkg/util/safego"
)

func buildCredential(uid, gid *uint32) (*syscall.Credential, error) {
if uid == nil && gid == nil {
return nil, nil
}

cred := &syscall.Credential{}
if uid != nil {
cred.Uid = *uid
// Load user info to get primary GID and supplemental groups
u, err := user.LookupId(strconv.FormatUint(uint64(*uid), 10))
if err == nil {
// Set primary GID if not explicitly provided
if gid == nil {
primaryGid, err := strconv.ParseUint(u.Gid, 10, 32)
if err == nil {
cred.Gid = uint32(primaryGid)
}
}

// Load supplemental groups
gids, err := u.GroupIds()
if err == nil {
for _, g := range gids {
id, err := strconv.ParseUint(g, 10, 32)
if err == nil {
cred.Groups = append(cred.Groups, uint32(id))
}
}
}
}
}

// Override Gid if explicitly provided
if gid != nil {
cred.Gid = *gid
}

return cred, nil
}

// runCommand executes shell commands and streams their output.
func (c *Controller) runCommand(ctx context.Context, request *ExecuteCodeRequest) error {
session := c.newContextID()
Expand Down Expand Up @@ -71,10 +112,19 @@ func (c *Controller) runCommand(ctx context.Context, request *ExecuteCodeRequest
})

cmd.Dir = request.Cwd
// use a dedicated process group so signals propagate to children.
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}

// Configure credentials and process group
cred, err := buildCredential(request.Uid, request.Gid)
if err != nil {
log.Error("failed to build credentials: %v", err)
}
cmd.SysProcAttr = &syscall.SysProcAttr{
Setpgid: true,
Credential: cred,
}

err = cmd.Start()

if err != nil {
request.Hooks.OnExecuteInit(session)
request.Hooks.OnExecuteError(&execute.ErrorOutput{EName: "CommandExecError", EValue: err.Error()})
Expand Down Expand Up @@ -171,8 +221,19 @@ func (c *Controller) runBackgroundCommand(ctx context.Context, cancel context.Ca
cmd := exec.CommandContext(ctx, "bash", "-c", request.Code)

cmd.Dir = request.Cwd
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}

// Configure credentials and process group
cred, err := buildCredential(request.Uid, request.Gid)
if err != nil {
log.Error("failed to build credentials: %v", err)
}
cmd.SysProcAttr = &syscall.SysProcAttr{
Setpgid: true,
Credential: cred,
}

cmd.Stdout = pipe

cmd.Stderr = pipe
cmd.Env = mergeEnvs(os.Environ(), loadExtraEnvFromFile())

Expand Down
17 changes: 9 additions & 8 deletions components/execd/pkg/runtime/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,16 @@ type ExecuteResultHook struct {

// ExecuteCodeRequest represents a code execution request with context and hooks.
type ExecuteCodeRequest struct {
Language Language `json:"language"`
Code string `json:"code"`
Context string `json:"context"`
Timeout time.Duration `json:"timeout"`
Cwd string `json:"cwd"`
Envs map[string]string `json:"envs"`
Hooks ExecuteResultHook
Language Language `json:"language"`
Code string `json:"code"`
Context string `json:"context"`
Timeout time.Duration `json:"timeout"`
Cwd string `json:"cwd"`
Envs map[string]string `json:"envs"`
Uid *uint32 `json:"uid,omitempty"`
Gid *uint32 `json:"gid,omitempty"`
Hooks ExecuteResultHook
}

// SetDefaultHooks installs stdout logging fallbacks for unset hooks.
func (req *ExecuteCodeRequest) SetDefaultHooks() {
if req.Hooks.OnExecuteResult == nil {
Expand Down
Loading