feat(executor/local): simple local executor without limits
This commit is contained in:
		
							parent
							
								
									38f11788a0
								
							
						
					
					
						commit
						c3f7b0fa2b
					
				|  | @ -2,6 +2,7 @@ package executors | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	_ "github.com/joint-online-judge/JOJ3/internal/executor/dummy" | 	_ "github.com/joint-online-judge/JOJ3/internal/executor/dummy" | ||||||
|  | 	_ "github.com/joint-online-judge/JOJ3/internal/executor/local" | ||||||
| 	"github.com/joint-online-judge/JOJ3/internal/executor/sandbox" | 	"github.com/joint-online-judge/JOJ3/internal/executor/sandbox" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										35
									
								
								internal/executor/local/buffer.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								internal/executor/local/buffer.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,35 @@ | ||||||
|  | package local | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"errors" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // LimitedBuffer wraps a bytes.Buffer and limits its size.
 | ||||||
|  | type LimitedBuffer struct { | ||||||
|  | 	buf     *bytes.Buffer | ||||||
|  | 	maxSize int | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Write writes data to the buffer and checks the size limit.
 | ||||||
|  | func (lb *LimitedBuffer) Write(p []byte) (n int, err error) { | ||||||
|  | 	if lb.buf.Len()+len(p) > lb.maxSize { | ||||||
|  | 		// Truncate to fit within the limit
 | ||||||
|  | 		allowed := lb.maxSize - lb.buf.Len() | ||||||
|  | 		if allowed > 0 { | ||||||
|  | 			n, _ = lb.buf.Write(p[:allowed]) | ||||||
|  | 		} | ||||||
|  | 		return n, errors.New("buffer size limit exceeded") | ||||||
|  | 	} | ||||||
|  | 	return lb.buf.Write(p) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Bytes returns the buffer's content.
 | ||||||
|  | func (lb *LimitedBuffer) Bytes() []byte { | ||||||
|  | 	return lb.buf.Bytes() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // String returns the buffer's content as a string.
 | ||||||
|  | func (lb *LimitedBuffer) String() string { | ||||||
|  | 	return lb.buf.String() | ||||||
|  | } | ||||||
							
								
								
									
										127
									
								
								internal/executor/local/executor.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								internal/executor/local/executor.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,127 @@ | ||||||
|  | package local | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | 	"os" | ||||||
|  | 	"os/exec" | ||||||
|  | 	"strings" | ||||||
|  | 	"time" | ||||||
|  | 
 | ||||||
|  | 	"github.com/criyle/go-judge/envexec" | ||||||
|  | 	"github.com/joint-online-judge/JOJ3/internal/stage" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type Local struct{} | ||||||
|  | 
 | ||||||
|  | func (e *Local) Run(cmds []stage.Cmd) ([]stage.ExecutorResult, error) { | ||||||
|  | 	var results []stage.ExecutorResult | ||||||
|  | 
 | ||||||
|  | 	for _, cmd := range cmds { | ||||||
|  | 		execCmd := exec.Command(cmd.Args[0], cmd.Args[1:]...) // #nosec G204
 | ||||||
|  | 
 | ||||||
|  | 		if len(cmd.Env) > 0 { | ||||||
|  | 			execCmd.Env = cmd.Env | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if cmd.Stdin != nil { | ||||||
|  | 			if cmd.Stdin.Content != nil { | ||||||
|  | 				execCmd.Stdin = strings.NewReader(*cmd.Stdin.Content) | ||||||
|  | 			} else if cmd.Stdin.Src != nil { | ||||||
|  | 				file, err := os.Open(*cmd.Stdin.Src) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return nil, fmt.Errorf("failed to open stdin file: %v", err) | ||||||
|  | 				} | ||||||
|  | 				defer file.Close() | ||||||
|  | 				execCmd.Stdin = file | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		var stdoutBuffer, stderrBuffer bytes.Buffer | ||||||
|  | 		execCmd.Stdout = &stdoutBuffer | ||||||
|  | 		execCmd.Stderr = &stderrBuffer | ||||||
|  | 
 | ||||||
|  | 		startTime := time.Now() | ||||||
|  | 		err := execCmd.Start() | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, fmt.Errorf("failed to start command: %v", err) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		err = execCmd.Wait() | ||||||
|  | 		endTime := time.Now() | ||||||
|  | 		runTime := endTime.Sub(startTime) | ||||||
|  | 
 | ||||||
|  | 		result := stage.ExecutorResult{ | ||||||
|  | 			Status:     stage.Status(envexec.StatusAccepted), | ||||||
|  | 			ExitStatus: 0, | ||||||
|  | 			Error:      "", | ||||||
|  | 			RunTime: func() uint64 { | ||||||
|  | 				nanos := runTime.Nanoseconds() | ||||||
|  | 				if nanos < 0 { | ||||||
|  | 					return 0 | ||||||
|  | 				} | ||||||
|  | 				return uint64(nanos) | ||||||
|  | 			}(), | ||||||
|  | 			Files:   map[string]string{}, | ||||||
|  | 			FileIDs: map[string]string{}, | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if err != nil { | ||||||
|  | 			if exitErr, ok := err.(*exec.ExitError); ok { | ||||||
|  | 				result.ExitStatus = exitErr.ExitCode() | ||||||
|  | 				result.Status = stage.Status(envexec.StatusNonzeroExitStatus) | ||||||
|  | 				result.Error = exitErr.Error() | ||||||
|  | 			} else { | ||||||
|  | 				result.Status = stage.Status(envexec.StatusInternalError) | ||||||
|  | 				result.Error = err.Error() | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if cmd.Stdout != nil && cmd.Stdout.Name != nil { | ||||||
|  | 			result.Files[*cmd.Stdout.Name] = stdoutBuffer.String() | ||||||
|  | 		} | ||||||
|  | 		if cmd.Stderr != nil && cmd.Stderr.Name != nil { | ||||||
|  | 			result.Files[*cmd.Stderr.Name] = stderrBuffer.String() | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if err := handleCopyOut(&result, cmd); err != nil { | ||||||
|  | 			result.Status = stage.Status(envexec.StatusFileError) | ||||||
|  | 			result.Error = err.Error() | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		results = append(results, result) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return results, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Helper function to handle copyOut files
 | ||||||
|  | func handleCopyOut(result *stage.ExecutorResult, cmd stage.Cmd) error { | ||||||
|  | 	for _, filename := range cmd.CopyOut { | ||||||
|  | 		optional := false | ||||||
|  | 		if strings.HasSuffix(filename, "?") { | ||||||
|  | 			optional = true | ||||||
|  | 			filename = strings.TrimSuffix(filename, "?") | ||||||
|  | 		} | ||||||
|  | 		result.Files[filename] = "" | ||||||
|  | 		// Read file and add to result.Files
 | ||||||
|  | 		file, err := os.Open(filename) | ||||||
|  | 		if err != nil { | ||||||
|  | 			if !optional { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		defer file.Close() | ||||||
|  | 		content, err := io.ReadAll(file) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		result.Files[filename] = string(content) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (e *Local) Cleanup() error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								internal/executor/local/meta.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								internal/executor/local/meta.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | ||||||
|  | package local | ||||||
|  | 
 | ||||||
|  | import "github.com/joint-online-judge/JOJ3/internal/stage" | ||||||
|  | 
 | ||||||
|  | var name = "local" | ||||||
|  | 
 | ||||||
|  | func init() { | ||||||
|  | 	stage.RegisterExecutor(name, &Local{}) | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user