Usage¶
camisole exposes an HTTP API with JSON or MessagePack serialization. Once the HTTP server of camisole is running, you can query it with any HTTP client using a POST request.
The body of your request should contain an object describing the request you want to execute. camisole will return the response as JSON (or MessagePack, see Binary payloads), always including a “success” boolean that indicates whether the request was successful or not.
Simple request¶
The main endpoint of camisole used to compile and execute code is /run
. You
always have to provide two mandatory parameters:
lang
: the language of your inputsource
: the source code to compile and execute.
Example with an interpreted language:
{
"lang": "python",
"source": "print(42)"
}
Result:
{
"success": true,
"tests": [
{
"exitcode": 0,
"meta": {
"cg-mem": 2664,
"csw-forced": 4,
"csw-voluntary": 3,
"exitcode": 0,
"exitsig": 0,
"exitsig-message": null,
"killed": false,
"max-rss": 7416,
"message": null,
"status": "OK",
"time": 0.017,
"wall-time": 0.069
},
"name": "test000",
"stderr": "",
"stdout": "42\n"
}
]
}
Adding limits and quotas¶
The compilation and execution of the program are sandboxed by default, which
means the process won’t be able to access to any of the files in the root
filesystem except /usr
, /bin
and /lib
in readonly, it won’t be able
to interact with any other process on the system or to use the network devices.
That said, the resources of the process aren’t limited by default, which means if you don’t add limits and quotas, the process can consume all your memory or your disk space and will never stop running.
You can put global resource limitations in the compile
and execute
bloc
for the compilation and the execution, respectively.
There are a lot of ways you can limit the resources of both the compilation and the execution of your program:
time
: limit the user time of the program (seconds)wall-time
: limit the wall time of the program (seconds)extra-time
: grace period before killing a program after it exceeded a time limit (seconds)mem
: limit the available memory of each process (kilobytes)virt-mem
: limit the address space of each process (kilobytes)fsize
: limit the size of files created by the program (kilobytes)processes
: limit the number of processes and/or threadsquota
: limit the disk quota to a number of blocks and inodes (separate both numbers by a comma, eg.10,30
)stack
: limit the stack size of each process (kilobytes)
This example demonstrates the use of resource limitations for both the compilation and execution:
{
"lang": "ocaml",
"source": "print_string \"Hello, world!\\\\n\"",
"compile": {
"wall-time": 10,
"fsize": 4096,
"mem": 100000
},
"execute": {
"time": 2,
"wall-time": 5,
"processes": 1,
"quota": "50,3",
"fsize": 256,
"stack": 9000,
"mem": 100000
}
}
To get more information about the different options, visit the documentation of isolate, the isolation backend, where they are described in detail.
Sending a test suite¶
If you want to execute your program on multiple inputs, you shouldn’t send one request per input, as this would recompile the source code every time. You can send a list of tests to camisole, and the compiled program will get executed once per test.
To send a test suite, add a tests
field in your input that contains a list
of tests. A test is a (possibly empty) object that can have the following
attributes:
name
: the name of the test (defaults to an autoincremetaltestXXX
)stdin
: the input that will be given to the program during this test- Any additional test-specific resource limit. These resource limits, when
specified, will override the global ones specified in the
execute
bloc. fatal
: a boolean indicating whether the test is fatal or not. If the test is fatal and its execution fails, the remaining tests won’t be executed.
Note that you can also add the boolean all_fatal
in your main request if
you want the tests to always be fatal.
Example input:
{
"lang": "python",
"source": "print(int(input()) * 2)",
"tests": [{"name": "test_h2g2", "stdin": "42", "mem": 200000},
{"name": "test_notfound", "stdin": "404"},
{"name": "test_leet", "stdin": "1337"},
{"name": "test_666", "stdin": "27972", "time": 1}]
}
Output:
{
"success": true,
"tests": [
{
"exitcode": 0,
"meta": {
"cg-mem": 2644,
"csw-forced": 6,
"csw-voluntary": 3,
"exitcode": 0,
"exitsig": 0,
"exitsig-message": null,
"killed": false,
"max-rss": 7612,
"message": null,
"status": "OK",
"time": 0.016,
"wall-time": 0.05
},
"name": "test_h2g2",
"stderr": "",
"stdout": "84\n"
},
{
"exitcode": 0,
"meta": {
"cg-mem": 2520,
"csw-forced": 24,
"csw-voluntary": 2,
"exitcode": 0,
"exitsig": 0,
"exitsig-message": null,
"killed": false,
"max-rss": 7448,
"message": null,
"status": "OK",
"time": 0.026,
"wall-time": 0.072
},
"name": "test_notfound",
"stderr": "",
"stdout": "808\n"
},
{
"exitcode": 0,
"meta": {
"cg-mem": 2520,
"csw-forced": 4,
"csw-voluntary": 3,
"exitcode": 0,
"exitsig": 0,
"exitsig-message": null,
"killed": false,
"max-rss": 7428,
"message": null,
"status": "OK",
"time": 0.026,
"wall-time": 0.083
},
"name": "test_leet",
"stderr": "",
"stdout": "2674\n"
},
{
"exitcode": 0,
"meta": {
"cg-mem": 2644,
"csw-forced": 34,
"csw-voluntary": 3,
"exitcode": 0,
"exitsig": 0,
"exitsig-message": null,
"killed": false,
"max-rss": 7736,
"message": null,
"status": "OK",
"time": 0.029,
"wall-time": 0.092
},
"name": "test_666",
"stderr": "",
"stdout": "55944\n"
}
]
}
If you don’t specify a test suite, camisole will only execute a single test
named test000
with an empty input.
Response format¶
Here is an example response:
{
"compile": {
"exitcode": 0,
"meta": {
"cg-mem": 14984,
"csw-forced": 53,
"csw-voluntary": 14,
"exitcode": 0,
"exitsig": 0,
"exitsig-message": null,
"killed": false,
"max-rss": 15276,
"message": null,
"status": "OK",
"time": 0.122,
"wall-time": 0.344
},
"stderr": "",
"stdout": ""
},
"success": true,
"tests": [
{
"exitcode": 0,
"meta": {
"cg-mem": 504,
"csw-forced": 4,
"csw-voluntary": 3,
"exitcode": 0,
"exitsig": 0,
"exitsig-message": null,
"killed": false,
"max-rss": 1820,
"message": null,
"status": "OK",
"time": 0.002,
"wall-time": 0.058
},
"name": "test000",
"stderr": "",
"stdout": "Hello, world!\\n"
}
]
}
The three fields of the response are:
- the
success
flag indicating whether the request was performed successfully or if an exception occurred. - the
compile
object containing the compilation report - the
tests
list containing all the executed tests, their names and reports.
A report is an object containing three fields:
stdout
: the standard output of the programstderr
: the standard error output of the programexitcode
: the exit code of isolate. This is not the exit code of the program, but it is advised to use it as if it was the case. The difference is that if the program returns 0 but is killed or violates a resource limitation policy, or ifisolate
crashes, the exitcode flag will report an error even if the program in itself executed fine.meta
: the execution metadata.
Execution metadata¶
The meta object contains metadata about the execution of the program and reports information about what happened to it. The fields are:
cg-mem
: Memory used by the control group (kilobytes)csw-forced
: Number of context switches forced by the kernelcsw-voluntary
: Number of context switches caused by the processexitcode
: The actual exit code of the programexitsig
: The code of the fatal signal received by the processkilled
: True if the program was killed by the sandboxmax-rss
: Maximum resident size of the process (kilobytes)message
: Status messagestatus
: Status codetime
: User time of the process (seconds)wall-time
: Wall time of the process (seconds)
The status code can be one of the following:
OK
: the program ran and exited successfullyRUNTIME_ERROR
: the program exited with a non-zero exit codeTIMED_OUT
: the program timed outSIGNALED
: the program received a fatal signalINTERNAL_ERROR
: the sandbox crashed because of an internal error
Once again, to get more information about the meta object, visit the documentation of isolate.
Versions and options¶
There is a way to retrieve some information about the languages enabled in
camisole: the endpoint /languages
give you information about the versions
of the compilers, interpreters and runtimes and their options.
{
"languages": {
"c": {
"name": "C",
"programs": {
"gcc": {
"opts": [
"-std=c11",
"-Wall",
"-Wextra",
"-O2",
"-lm"
],
"version": "7.2.0"
}
}
},
"python": {
"name": "Python",
"programs": {
"python3": {
"opts": [
"-S"
],
"version": "3.6.2"
}
}
}
},
"success": true
}
System information¶
You can also get information about the system where camisole is running by
doing a request to the /system
endpoint.
{
"success": true,
"system": {
"arch": "x86_64",
"byte_order": "little",
"cpu_cache_L1d": 32768,
"cpu_cache_L1i": 32768,
"cpu_cache_L2": 262144,
"cpu_cache_L3": 6291456,
"cpu_count": 1,
"cpu_mhz": 2494.214,
"cpu_name": "Intel(R) Core(TM) i7-4710HQ CPU @ 2.50GHz",
"kernel": "Linux",
"kernel_release": "4.10.9-1-ARCH",
"kernel_version": "#1 SMP PREEMPT Sat Apr 8 12:39:59 CEST 2017",
"memory": 2102362112
}
}
Binary payloads¶
camisole was primarily designed for text (UTF-8 encodable) inputs and outputs, for both the program source and inputs/outputs. It works fine in most situations, but in case your program outputs binary data or you source is binary (eg. a Piet program), you can not use the JSON serializer to communicate with the camisole server as JSON can only encode and decode Unicode.
camisole automatically fallbacks to MessagePack serialization if the response
cannot be JSON-encoded. You must be prepared to use the proper deserialization
method depending on the Content-Type
header sent by the server.
You can also encode your requests in MessagePack, if needed. Just send the
correct Content-Type: application/msgpack
header.
Note
The server will only send content-types that the client accepts,
depending on the Accept
header you send.
Send Accept: */*
to allow the server to send either JSON or MessagePack
if JSON isn’t suitable.
Send Accept: application/messagepack
to enforce MessagePack responses.
Send Accept: application/json
to enforce JSON responses. Responses
containing binary data will fail with a Not Acceptable
HTTP error.