summaryrefslogtreecommitdiff
path: root/lib/play/command.go
diff options
context:
space:
mode:
Diffstat (limited to 'lib/play/command.go')
-rw-r--r--lib/play/command.go81
1 files changed, 81 insertions, 0 deletions
diff --git a/lib/play/command.go b/lib/play/command.go
new file mode 100644
index 00000000..2cbed88e
--- /dev/null
+++ b/lib/play/command.go
@@ -0,0 +1,81 @@
+// SPDX-FileCopyrightText: 2024 M. Shulhan <ms@kilabit.info>
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+package play
+
+import (
+ "bytes"
+ "context"
+ "errors"
+ "fmt"
+ "os"
+ "os/exec"
+ "time"
+)
+
+type command struct {
+ buf *bytes.Buffer
+ execGoRun *exec.Cmd
+ ctx context.Context
+ ctxCancel context.CancelFunc
+ pid chan int
+}
+
+func newCommand(req *Request, workingDir string) (cmd *command, err error) {
+ const defCommandTimeout = 30 * time.Second
+
+ cmd = &command{
+ buf: &bytes.Buffer{},
+ pid: make(chan int, 1),
+ }
+ var ctxParent = context.Background()
+ cmd.ctx, cmd.ctxCancel = context.WithTimeout(ctxParent, defCommandTimeout)
+
+ var userHomeDir string
+
+ userHomeDir, err = os.UserHomeDir()
+ if err != nil {
+ return nil, fmt.Errorf(`newCommand: %w`, err)
+ }
+
+ cmd.execGoRun = exec.CommandContext(cmd.ctx, `go`, `run`, `-race`, `.`)
+ cmd.execGoRun.Env = append(cmd.execGoRun.Env, `CGO_ENABLED=1`)
+ cmd.execGoRun.Env = append(cmd.execGoRun.Env, `HOME=`+userHomeDir)
+ cmd.execGoRun.Env = append(cmd.execGoRun.Env, `PATH=/usr/bin:/usr/local/bin`)
+ cmd.execGoRun.Dir = workingDir
+ cmd.execGoRun.Stdout = cmd.buf
+ cmd.execGoRun.Stderr = cmd.buf
+ cmd.execGoRun.WaitDelay = 100 * time.Millisecond
+
+ return cmd, nil
+}
+
+// run the command using [exec.Command.Start] and [exec.Command.Wait].
+// The Start method is used to get the process ID.
+// When the Start or Wait failed, it will write the error or ProcessState
+// into the last line of out.
+func (cmd *command) run() (out []byte) {
+ defer cmd.ctxCancel()
+
+ var err = cmd.execGoRun.Start()
+ if err != nil {
+ cmd.buf.WriteString("\n" + err.Error() + "\n")
+ goto out
+ }
+
+ cmd.pid <- cmd.execGoRun.Process.Pid
+
+ err = cmd.execGoRun.Wait()
+ if err != nil {
+ var errExit *exec.ExitError
+ if errors.As(err, &errExit) {
+ cmd.buf.WriteString("\n" + errExit.ProcessState.String() + "\n")
+ } else {
+ cmd.buf.WriteString("\n" + err.Error() + "\n")
+ }
+ }
+out:
+ out = cmd.buf.Bytes()
+ return out
+}