Skip to content

Commit dd6d041

Browse files
committed
implement read/write protection and resource limits to C code executor
1 parent 0f7d455 commit dd6d041

8 files changed

Lines changed: 120 additions & 64 deletions

File tree

config/globals.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@ import "github.com/caarlos0/env/v11"
44

55
type globals struct {
66
RAM_LIMIT uint `env:"GLOBAL_RAM_LIMIT" envDefault:"1048576"` // 1 GB by default (or 1048576 KB), this is the total amount of RAM that can be used by all running sandboxes at any given time
7-
ENABLE_QUEUE bool `env:"ENABLE_QUEUE" envDefault:"true"` // if false, the server will reject requests when ram limit is reached, if true, the server will queue requests until sufficient ram is available
7+
ENABLE_QUEUE bool `env:"ENABLE_QUEUE" envDefault:"false"` // if false, the server will reject requests when ram limit is reached, if true, the server will queue requests until sufficient ram is available
88
ENABLE_DEBUG_ROUTES bool `env:"ENABLE_DEBUG" envDefault:"false"` // if true, the server will expose debug routes for monitoring and debugging purposes
99
}
1010

11+
// i know the docs say that queueing is true by default
12+
// ill set it to false by default for now because queueing is not implemented yet
13+
1114
func (cfg *globals) load() error {
1215
return env.Parse(cfg)
1316
}

dispatcher/dispatch.go

Lines changed: 0 additions & 37 deletions
This file was deleted.

dispatcher/main.go

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,14 @@ package dispatcher
22

33
import (
44
"CodeSandboxAPI/config"
5+
"CodeSandboxAPI/executor"
56
"CodeSandboxAPI/models"
7+
"CodeSandboxAPI/resourcemanager"
68
"fmt"
7-
"time"
8-
9-
"github.com/gin-gonic/gin"
109
)
1110

12-
func CallDispatcher(c *gin.Context) {
13-
var req models.Request
14-
if err := c.ShouldBindJSON(&req); err != nil {
15-
c.JSON(400, gin.H{"error": "Invalid request format"})
16-
return
17-
}
11+
func Dispatch(req models.Request) (models.Response, error) {
1812

19-
req.Timeout *= time.Second // Convert seconds to duration
2013
if req.Timeout == 0 {
2114
req.Timeout = config.Config.Limits.DefaultTimeout
2215
} else if req.Timeout > config.Config.Limits.MaxTimeout {
@@ -28,12 +21,22 @@ func CallDispatcher(c *gin.Context) {
2821
} else if req.MemoryLimit > config.Config.Limits.MaxMemoryLimit {
2922
req.MemoryLimit = config.Config.Limits.MaxMemoryLimit
3023
}
31-
32-
resp, err := dispatch(req)
33-
if err != nil {
34-
fmt.Printf("Error during dispatch: %v\n", err)
35-
c.JSON(500, gin.H{"error": "Internal server error"})
36-
return
24+
if !config.Config.Globals.ENABLE_QUEUE {
25+
if !resourcemanager.ReserveRAM(req.MemoryLimit) { // TODO: handle condition for queueing requests when RAM is not available
26+
return models.Response{
27+
Stdout: "",
28+
Stderr: "Resource limit reached, please try again later",
29+
ExecutionTime: 0,
30+
}, fmt.Errorf("Failed to reserve RAM")
31+
}
32+
defer resourcemanager.ReleaseRAM(req.MemoryLimit)
33+
return executor.Execute(req)
34+
} else {
35+
// TODO: Implement queueing logic
36+
return models.Response{
37+
Stdout: "",
38+
Stderr: "Queueing is not implemented yet",
39+
ExecutionTime: 0,
40+
}, nil
3741
}
38-
c.JSON(200, resp)
3942
}

executor/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ func Execute(req models.Request) (models.Response, error) {
6767
Setpgid: true,
6868
}
6969
if len(req.Stdin) > 0 {
70-
runCmd.Stdin = strings.NewReader(strings.Join(req.Stdin, "\n"))
70+
runCmd.Stdin = strings.NewReader(req.Stdin)
7171
}
7272

7373
var stdoutBuf strings.Builder

models/models.go

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,56 @@
11
package models
22

3-
import "time"
3+
import (
4+
"fmt"
5+
"strings"
6+
"time"
7+
)
48

59
type Request struct {
610
Language string `json:"language"` // Programming language (e.g., "c", "python", "javascript")
711
Code string `json:"code"` // Source code to execute
8-
Timeout time.Duration `json:"timeout"` // time limit in seconds
12+
Timeout time.Duration `json:"timeout"` // time limit in nanoseconds
913
MemoryLimit uint `json:"max_memory"` // memory limit in KB
10-
Stdin []string `json:"inputs,omitempty"` // Array of strings for multiple lines of input, optional
14+
Stdin string `json:"inputs,omitempty"` // input to take from STDIN, optional, can be multiple lines of input
15+
}
16+
17+
type SimpleRequest struct {
18+
Language string `json:"language"` // Programming language (e.g., "c", "python", "javascript")
19+
Code string `json:"code"` // Source code to execute
20+
Timeout uint `json:"timeout"` // time limit in seconds
21+
MemoryLimit uint `json:"max_memory"` // memory limit in KB
22+
Stdin []string `json:"inputs,omitempty"` // Array of strings for multiple lines of input, optional
23+
}
24+
25+
func (sr *SimpleRequest) ToRequest() Request {
26+
return Request{
27+
Language: sr.Language,
28+
Code: sr.Code,
29+
Timeout: time.Duration(sr.Timeout) * time.Second,
30+
MemoryLimit: sr.MemoryLimit,
31+
Stdin: strings.Join(sr.Stdin, "\n"),
32+
}
1133
}
1234

1335
type Response struct {
1436
Stdout string `json:"output"`
1537
Stderr string `json:"error"`
16-
ExecutionTime time.Duration `json:"cpu_time"`
17-
MemoryUsed uint `json:"memory_used"`
38+
ExecutionTime time.Duration `json:"cpu_time"` // CPU time used by the program in nanoseconds
39+
MemoryUsed uint `json:"memory_used"` // Peak memory used by the program in KB
40+
}
41+
42+
type SimpleResponse struct {
43+
Stdout string `json:"output"`
44+
Stderr string `json:"error"`
45+
MemoryUsed string `json:"memory_used"` // Peak memory used by the program in KB
46+
ExecutionTime string `json:"cpu_time"` // CPU time used by the program in seconds (ends in s, e.g., "0.5s")
47+
}
48+
49+
func (r *Response) ToSimpleResponse() SimpleResponse {
50+
return SimpleResponse{
51+
Stdout: r.Stdout,
52+
Stderr: r.Stderr,
53+
ExecutionTime: r.ExecutionTime.String(),
54+
MemoryUsed: fmt.Sprintf("%d KB", r.MemoryUsed),
55+
}
1856
}

routes/execute.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package routes
2+
3+
import (
4+
"CodeSandboxAPI/dispatcher"
5+
"CodeSandboxAPI/models"
6+
7+
"github.com/gin-gonic/gin"
8+
)
9+
10+
func CallDispatcher(c *gin.Context) {
11+
var req models.Request
12+
if err := c.ShouldBindJSON(&req); err != nil {
13+
c.JSON(400, gin.H{"error": "Invalid request format"})
14+
return
15+
}
16+
17+
resp, err := dispatcher.Dispatch(req)
18+
if err != nil {
19+
c.JSON(500, gin.H{"error": "Internal server error"})
20+
return
21+
}
22+
c.JSON(200, resp)
23+
}

routes/main.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
package routes
22

33
import (
4-
"CodeSandboxAPI/dispatcher"
5-
64
"github.com/gin-gonic/gin"
75
)
86

97
func Setup(router *gin.Engine) {
108
router.GET("/", root)
11-
router.POST("/execute", dispatcher.CallDispatcher)
9+
router.POST("/execute", CallDispatcher)
10+
router.POST("/simple-execute", SimpleDispatcher)
1211
}

routes/simple-execute.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package routes
2+
3+
import (
4+
"CodeSandboxAPI/dispatcher"
5+
"CodeSandboxAPI/models"
6+
"fmt"
7+
8+
"github.com/gin-gonic/gin"
9+
)
10+
11+
func SimpleDispatcher(c *gin.Context) {
12+
var req models.SimpleRequest
13+
if err := c.ShouldBindJSON(&req); err != nil {
14+
c.JSON(400, gin.H{"error": "Invalid request format"})
15+
return
16+
}
17+
18+
convertedReq := req.ToRequest()
19+
resp, err := dispatcher.Dispatch(convertedReq)
20+
convertedResp := resp.ToSimpleResponse()
21+
if err != nil {
22+
fmt.Printf("Error during dispatch: %v\n", err)
23+
c.JSON(500, gin.H{"error": "Internal server error"})
24+
return
25+
}
26+
c.JSON(200, convertedResp)
27+
}

0 commit comments

Comments
 (0)