python.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. package python
  2. import (
  3. "context"
  4. _ "embed"
  5. "fmt"
  6. "os"
  7. "os/exec"
  8. "strconv"
  9. "strings"
  10. "time"
  11. "github.com/google/uuid"
  12. "github.com/langgenius/dify-sandbox/internal/core/runner"
  13. "github.com/langgenius/dify-sandbox/internal/static"
  14. "github.com/langgenius/dify-sandbox/internal/utils/log"
  15. )
  16. type PythonRunner struct {
  17. runner.Runner
  18. runner.SeccompRunner
  19. }
  20. //go:embed prescript.py
  21. var python_sandbox_fs []byte
  22. //go:embed python.so
  23. var python_lib []byte
  24. func (p *PythonRunner) Run(code string, timeout time.Duration, stdin []byte) (chan []byte, chan []byte, chan bool, error) {
  25. // check if libpython.so exists
  26. if _, err := os.Stat("/tmp/sandbox-python/python.so"); os.IsNotExist(err) {
  27. err := os.MkdirAll("/tmp/sandbox-python", 0755)
  28. if err != nil {
  29. return nil, nil, nil, err
  30. }
  31. err = os.WriteFile("/tmp/sandbox-python/python.so", python_lib, 0755)
  32. if err != nil {
  33. return nil, nil, nil, err
  34. }
  35. }
  36. // create a tmp dir and copy the python script
  37. temp_code_name := strings.ReplaceAll(uuid.New().String(), "-", "_")
  38. temp_code_name = strings.ReplaceAll(temp_code_name, "/", ".")
  39. temp_code_path := fmt.Sprintf("/tmp/code/%s.py", temp_code_name)
  40. err := os.MkdirAll("/tmp/code", 0755)
  41. if err != nil {
  42. return nil, nil, nil, err
  43. }
  44. err = os.WriteFile(temp_code_path, []byte(code), 0755)
  45. if err != nil {
  46. return nil, nil, nil, err
  47. }
  48. stdout := make(chan []byte, 1)
  49. stderr := make(chan []byte, 1)
  50. done_chan := make(chan bool, 1)
  51. err = p.WithTempDir([]string{
  52. temp_code_path,
  53. "/tmp/sandbox-python/python.so",
  54. }, func(root_path string) error {
  55. // create a new process
  56. ctx, cancel := context.WithTimeout(context.Background(), timeout)
  57. cmd := exec.CommandContext(ctx,
  58. "/usr/bin/python3",
  59. "-c",
  60. string(python_sandbox_fs),
  61. temp_code_path,
  62. strconv.Itoa(static.SANDBOX_USER_UID),
  63. strconv.Itoa(static.SANDBOX_GROUP_ID),
  64. )
  65. cmd.Env = []string{}
  66. // create a pipe for the stdout
  67. stdout_reader, err := cmd.StdoutPipe()
  68. if err != nil {
  69. cancel()
  70. return err
  71. }
  72. // create a pipe for the stderr
  73. stderr_reader, err := cmd.StderrPipe()
  74. if err != nil {
  75. cancel()
  76. return err
  77. }
  78. // start the process
  79. err = cmd.Start()
  80. if err != nil {
  81. cancel()
  82. return err
  83. }
  84. // read the output
  85. go func() {
  86. for {
  87. buf := make([]byte, 1024)
  88. n, err := stdout_reader.Read(buf)
  89. if err != nil {
  90. break
  91. }
  92. stdout <- buf[:n]
  93. }
  94. }()
  95. // read the error
  96. go func() {
  97. buf := make([]byte, 1024)
  98. for {
  99. n, err := stderr_reader.Read(buf)
  100. if err != nil {
  101. break
  102. }
  103. stderr <- buf[:n]
  104. }
  105. }()
  106. // wait for the process to finish
  107. go func() {
  108. status, err := cmd.Process.Wait()
  109. if err != nil {
  110. log.Error("process finished with status: %v", status.String())
  111. stderr <- []byte(fmt.Sprintf("error: %v\n", err))
  112. } else if status.ExitCode() != 0 {
  113. exit_string := status.String()
  114. if strings.Contains(exit_string, "bad system call (core dumped)") {
  115. stderr <- []byte("error: operation not permitted\n")
  116. } else {
  117. stderr <- []byte(fmt.Sprintf("exit code: %v\n", status.ExitCode()))
  118. }
  119. }
  120. os.Remove(temp_code_path)
  121. os.RemoveAll(root_path)
  122. os.Remove(root_path)
  123. stderr_reader.Close()
  124. stdout_reader.Close()
  125. cancel()
  126. done_chan <- true
  127. }()
  128. return nil
  129. })
  130. if err != nil {
  131. return nil, nil, nil, err
  132. }
  133. return stdout, stderr, done_chan, nil
  134. }