4 |
5 |
8 |
9 |
10 |
11 | <%= yield %>
12 |
13 |
14 |
--------------------------------------------------------------------------------
/scripts/load-config:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | if [[ -f judge0-api.conf ]]; then
3 | CONFIG_FILE=judge0-api.conf
4 | elif [[ -f /judge0-api.conf ]]; then
5 | CONFIG_FILE=/judge0-api.conf
6 | fi
7 |
8 | if [[ -v CONFIG_FILE ]]; then
9 | set -o allexport
10 | source $CONFIG_FILE
11 | set +o allexport
12 | fi
13 |
--------------------------------------------------------------------------------
/db/migrate/20170919215014_add_exit_code_exit_signal_and_message_to_submissions.rb:
--------------------------------------------------------------------------------
1 | class AddExitCodeExitSignalAndMessageToSubmissions < ActiveRecord::Migration[5.0]
2 | def change
3 | add_column :submissions, :exit_code, :integer
4 | add_column :submissions, :exit_signal, :integer
5 | add_column :submissions, :message, :text
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/Dockerfile.dev:
--------------------------------------------------------------------------------
1 | FROM judge0/api
2 |
3 | ARG DEV_USER=judge0
4 | ARG DEV_USER_ID=1000
5 |
6 | RUN apt-get update && \
7 | apt-get install -y --no-install-recommends vim && \
8 | useradd -u $DEV_USER_ID -m -r $DEV_USER && \
9 | echo "$DEV_USER ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers
10 |
11 | USER $DEV_USER
12 |
13 | CMD ["sleep", "infinity"]
14 |
--------------------------------------------------------------------------------
/spec/controllers/statuses_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe StatusesController, type: :controller do
4 | describe "GET #index" do
5 | it "returns all statuses" do
6 | get :index
7 | json = JSON.parse(response.body)
8 | expect(response).to be_success
9 | expect(json).to have_serialized(Status.all).with(StatusSerializer)
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < SessionsController
2 | private
3 |
4 | def pagination_dict(collection)
5 | {
6 | current_page: collection.current_page,
7 | next_page: collection.next_page,
8 | prev_page: collection.previous_page,
9 | total_pages: collection.total_pages,
10 | total_count: collection.total_entries
11 | }
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/docs/api/information/about.md:
--------------------------------------------------------------------------------
1 | ## About [/about]
2 | ## About [GET]
3 | Returns general information.
4 |
5 | + Response 200 (application/json)
6 | + Body
7 | {
8 | "version": "1.5.0",
9 | "homepage": "https://judge0.com",
10 | "source_code": "https://github.com/judge0/api",
11 | "maintainer": "Herman Zvonimir Došilović "
12 | }
--------------------------------------------------------------------------------
/db/migrate/20160903211542_create_submissions.rb:
--------------------------------------------------------------------------------
1 | class CreateSubmissions < ActiveRecord::Migration[5.0]
2 | def change
3 | create_table :submissions do |t|
4 | t.text :source_code
5 | t.integer :language_id
6 | t.text :input
7 | t.text :expected_output
8 | t.text :actual_output
9 | t.integer :status_id
10 | t.datetime :created_at
11 | t.datetime :finished_at
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
5 |
6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
7 | # Rails.backtrace_cleaner.remove_silencers!
8 |
--------------------------------------------------------------------------------
/spec/controllers/languages_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe LanguagesController, type: :controller do
4 | describe "GET #index" do
5 | it "returns all languages" do
6 | create_list(:language, 10)
7 | get :index
8 | json = JSON.parse(response.body)
9 | expect(response).to be_success
10 | expect(json).to have_serialized(Language.all).with(LanguageSerializer)
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/app/controllers/languages_controller.rb:
--------------------------------------------------------------------------------
1 | class LanguagesController < ApplicationController
2 | def index
3 | render json: Language.all, each_serializer: LanguageSerializer, fields: [:id, :name]
4 | end
5 |
6 | def all
7 | render json: Language.unscoped.order(name: :asc), each_serializer: LanguageSerializer, fields: [:id, :name, :is_archived]
8 | end
9 |
10 | def show
11 | render json: Language.unscoped.find(params[:id])
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/docs/api/style.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/api/statuses_and_languages/get_a_language.md:
--------------------------------------------------------------------------------
1 | ## Get a Language [GET]
2 | + Parameters
3 | + id (required, integer, `1`) ... Language ID.
4 |
5 | + Response 200 (application/json)
6 | + Body
7 | {
8 | "id": 1,
9 | "name": "Bash (4.4)",
10 | "is_archived": true,
11 | "source_file": "script.sh",
12 | "compile_cmd": null,
13 | "run_cmd": "/usr/local/bash-4.4/bin/bash script.sh"
14 | }
--------------------------------------------------------------------------------
/spec/routing/submissions_routing_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe SubmissionsController, type: :routing do
4 | describe "routing" do
5 | it "routes to #show" do
6 | token = SecureRandom.uuid
7 | expect(:get => "/submissions/#{token}").to route_to("submissions#show", token: token)
8 | end
9 |
10 | it "routes to #create" do
11 | expect(:post => "/submissions").to route_to("submissions#create")
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/app/models/language.rb:
--------------------------------------------------------------------------------
1 | # == Schema Information
2 | #
3 | # Table name: languages
4 | #
5 | # id :integer not null, primary key
6 | # name :string
7 | # compile_cmd :string
8 | # run_cmd :string
9 | # source_file :string
10 | # is_archived :boolean default(FALSE)
11 | #
12 |
13 | class Language < ApplicationRecord
14 | validates :name, :run_cmd, :source_file, presence: true
15 | default_scope { where(is_archived: false).order(name: :asc) }
16 | end
17 |
--------------------------------------------------------------------------------
/spec/support/database_cleaner.rb:
--------------------------------------------------------------------------------
1 | require 'database_cleaner'
2 |
3 | RSpec.configure do |config|
4 | config.use_transactional_fixtures = false
5 |
6 | config.before(:suite) do
7 | DatabaseCleaner.clean_with(:truncation)
8 | end
9 |
10 | config.before(:each) do
11 | DatabaseCleaner.strategy = :transaction
12 | end
13 |
14 | config.before(:each) do
15 | DatabaseCleaner.start
16 | end
17 |
18 | config.after(:each) do
19 | DatabaseCleaner.clean
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | gem 'rails', '~> 5.0.0'
4 | gem 'pg', '~> 1.2.2'
5 |
6 | gem 'active_model_serializers', '~> 0.10.10'
7 | gem 'enumerations', '~> 2.3.3'
8 | gem 'httparty', '~> 0.17.3'
9 | gem 'pry-byebug', '~> 3.7.0'
10 | gem 'pry-rails', '~> 0.3.9'
11 | gem 'puma', '~> 4.3.1'
12 | gem 'rack-cors', '~> 1.1.1'
13 | gem 'resque', '~> 2.0.0'
14 | gem 'will_paginate', '~> 3.2.1'
15 |
16 | group :development do
17 | gem 'annotate', '~> 3.0.3'
18 | gem 'listen', '~> 3.2.1'
19 | end
--------------------------------------------------------------------------------
/db/migrate/20170422122148_change_encode_all_submissions.rb:
--------------------------------------------------------------------------------
1 | class ChangeEncodeAllSubmissions < ActiveRecord::Migration[5.0]
2 | def change
3 | Submission.all.each do |submission|
4 | submission.source_code = submission[:source_code]
5 | submission.input = submission[:input]
6 | submission.stdout = submission[:stdout]
7 | submission.expected_output = submission[:expected_output]
8 | submission.stderr = submission[:stderr]
9 | submission.save
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/db/seeds.rb:
--------------------------------------------------------------------------------
1 | require_relative 'languages/archived'
2 | require_relative 'languages/active'
3 |
4 | ActiveRecord::Base.transaction do
5 | Language.unscoped.delete_all
6 | @languages.each_with_index do |language, index|
7 | Language.create(
8 | id: language[:id],
9 | name: language[:name],
10 | is_archived: language[:is_archived],
11 | source_file: language[:source_file],
12 | compile_cmd: language[:compile_cmd],
13 | run_cmd: language[:run_cmd],
14 | )
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | require 'codeclimate-test-reporter'
2 |
3 | CodeClimate::TestReporter.start
4 | CodeClimate::TestReporter.configuration.git_dir = ".."
5 |
6 | RSpec.configure do |config|
7 | config.expect_with :rspec do |expectations|
8 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true
9 | end
10 |
11 | config.mock_with :rspec do |mocks|
12 | mocks.verify_partial_doubles = true
13 | end
14 |
15 | config.shared_context_metadata_behavior = :apply_to_host_groups
16 | end
17 |
--------------------------------------------------------------------------------
/app/helpers/system_info.rb:
--------------------------------------------------------------------------------
1 | module SystemInfo
2 | def self.sys_info
3 | @@sys_info ||= self.cpu_info.merge(self.mem_info)
4 | end
5 |
6 | def self.cpu_info #sorryforthisline
7 | @@cpu_info ||= Hash[`lscpu`.split("\n").collect{|l| l = l.split(":"); [l[0].strip, l[1].strip]}]
8 | end
9 |
10 | def self.mem_info #sorryagain #itjustworks
11 | @@mem_info ||= Hash[`free -h`.split("\n")[1..-1].collect{|l| l = l.split(":"); [l[0].strip, l[1].split(" ")[0].strip]}].without("-/+ buffers/cache")
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/bin/spring:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | # This file loads spring without using Bundler, in order to be fast.
4 | # It gets overwritten when you run the `spring binstub` command.
5 |
6 | unless defined?(Spring)
7 | require 'rubygems'
8 | require 'bundler'
9 |
10 | if (match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m))
11 | Gem.paths = { 'GEM_PATH' => [Bundler.bundle_path.to_s, *Gem.path].uniq.join(Gem.path_separator) }
12 | gem 'spring', match[1]
13 | require 'spring/binstub'
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/config/database.yml:
--------------------------------------------------------------------------------
1 | default: &default
2 | adapter: postgresql
3 | encoding: unicode
4 | host: <%= ENV["POSTGRES_HOST"].presence || "localhost" %>
5 | port: <%= ENV["POSTGRES_PORT"].presence || 5432 %>
6 | database: <%= ENV["POSTGRES_DB"].presence || "postgres" %>
7 | username: <%= ENV["POSTGRES_USER"].presence || "postgres" %>
8 | password: <%= ENV["POSTGRES_PASSWORD"] %>
9 | pool: <%= ENV["RAILS_MAX_THREADS"].presence || 5 %>
10 |
11 | development:
12 | <<: *default
13 |
14 | test:
15 | <<: *default
16 |
17 | production:
18 | <<: *default
19 |
--------------------------------------------------------------------------------
/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # This file contains settings for ActionController::ParamsWrapper which
4 | # is enabled by default.
5 |
6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7 | ActiveSupport.on_load(:action_controller) do
8 | wrap_parameters format: [:json]
9 | end
10 |
11 | # To enable root element in JSON for ActiveRecord objects.
12 | # ActiveSupport.on_load(:active_record) do
13 | # self.include_root_in_json = true
14 | # end
15 |
--------------------------------------------------------------------------------
/docs/api/hostname.html:
--------------------------------------------------------------------------------
1 |
14 |
--------------------------------------------------------------------------------
/config/initializers/cors.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Avoid CORS issues when API is called from the frontend app.
4 | # Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests.
5 |
6 | # Read more: https://github.com/cyu/rack-cors
7 |
8 | # Rails.application.config.middleware.insert_before 0, Rack::Cors do
9 | # allow do
10 | # origins 'example.com'
11 | #
12 | # resource '*',
13 | # headers: :any,
14 | # methods: [:get, :post, :put, :patch, :delete, :options, :head]
15 | # end
16 | # end
17 |
--------------------------------------------------------------------------------
/spec/rails_helper.rb:
--------------------------------------------------------------------------------
1 | ENV['RAILS_ENV'] = 'test'
2 | require File.expand_path('../../config/environment', __FILE__)
3 | abort("The Rails environment is running in production mode!") if Rails.env.production?
4 | require 'spec_helper'
5 | require 'rspec/rails'
6 | require 'pry-byebug'
7 |
8 | Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
9 |
10 | ActiveRecord::Migration.maintain_test_schema!
11 |
12 | RSpec.configure do |config|
13 | config.fixture_path = "#{::Rails.root}/spec/fixtures"
14 | config.infer_spec_type_from_file_location!
15 | config.filter_rails_from_backtrace!
16 | end
17 |
--------------------------------------------------------------------------------
/scripts/run-workers:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | source ./scripts/load-config
3 |
4 | export RUBYOPT=-W:no-deprecated
5 |
6 | run_resque=1
7 | resque_pid=0
8 |
9 | exit_gracefully() {
10 | run_resque=0
11 | kill -SIGQUIT $(pgrep -P $resque_pid)
12 | kill -SIGTERM $resque_pid
13 | }
14 |
15 | trap exit_gracefully SIGTERM SIGINT
16 |
17 | mkdir -p tmp/pids &> /dev/null
18 | while [[ $run_resque -eq 1 ]]; do
19 | rm -rf tmp/pids/resque.pid &> /dev/null
20 | COUNT=${COUNT:-1} QUEUE=$JUDGE0_VERSION rails resque:workers &
21 | resque_pid=$!
22 | while ps -p $resque_pid > /dev/null; do sleep 1s; done
23 | done
24 |
--------------------------------------------------------------------------------
/.github/sequence-diagram.txt:
--------------------------------------------------------------------------------
1 | # https://bramp.github.io/js-sequence-diagrams/
2 | User->Rails API: createNewSubmission(source_code, language_id, input, expected_output)
3 | Rails API->PostgreSQL: Submission := createSubmission(source_code, language_id, input, expected_output)
4 | PostgreSQL-->>Rails API: created
5 | Rails API->>Redis: createNewJob(submission)
6 | Rails API-->>User: created(id)
7 | Worker->Redis: * IsolateJob := getNextJob()
8 | Worker->Worker: runInSandbox(submission.source_code)
9 | Worker->Worker: determineStatus()
10 | Worker->PostgreSQL: updateSubmission(submission)
11 | User->Rails API: getSubmission(id)
12 | Rails API->PostgreSQL: Submission := findSubmission(id)
13 | PostgreSQL-->>Rails API:
14 | Rails API-->>User:
15 |
--------------------------------------------------------------------------------
/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format. Inflections
4 | # are locale specific, and you may define rules for as many different
5 | # locales as you wish. All of these examples are active by default:
6 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
7 | # inflect.plural /^(ox)$/i, '\1en'
8 | # inflect.singular /^(ox)en/i, '\1'
9 | # inflect.irregular 'person', 'people'
10 | # inflect.uncountable %w( fish sheep )
11 | # end
12 |
13 | # These inflection rules are supported but not enabled by default:
14 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
15 | # inflect.acronym 'RESTful'
16 | # end
17 |
--------------------------------------------------------------------------------
/app/controllers/health_controller.rb:
--------------------------------------------------------------------------------
1 | class HealthController < ApplicationController
2 | def workers
3 | queues = Hash.new { |h, k| h[k] = [] }
4 | Resque.workers.each do |worker|
5 | worker.queues.each do |queue|
6 | queues[queue] << worker
7 | end
8 | end
9 |
10 | json = []
11 | Resque.queues.each do |queue|
12 | workers = queues[queue]
13 | json << {
14 | queue: queue,
15 | available: workers.count,
16 | idle: workers.count { |w| w.idle? },
17 | working: workers.count { |w| w.working? },
18 | paused: workers.count { |w| w.paused? },
19 | failed: workers.sum { |w| w.failed }
20 | }
21 | end
22 |
23 | render json: json
24 | end
25 | end
--------------------------------------------------------------------------------
/db/migrate/20170203215830_add_configuration_to_submission.rb:
--------------------------------------------------------------------------------
1 | class AddConfigurationToSubmission < ActiveRecord::Migration[5.0]
2 | def change
3 | add_column :submissions, :cpu_time_limit, :decimal
4 | add_column :submissions, :cpu_extra_time, :decimal
5 | add_column :submissions, :wall_time_limit, :decimal
6 | add_column :submissions, :memory_limit, :integer
7 | add_column :submissions, :stack_limit, :integer
8 | add_column :submissions, :max_processes_and_or_threads, :integer
9 | add_column :submissions, :enable_per_process_and_thread_time_limit, :boolean
10 | add_column :submissions, :enable_per_process_and_thread_memory_limit, :boolean
11 | add_column :submissions, :max_file_size, :integer
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/spec/factories/submissions.rb:
--------------------------------------------------------------------------------
1 | FactoryGirl.define do
2 | factory :valid_submission, class: Submission do
3 | sequence(:token) { |n| SecureRandom.uuid + "-#{n}" }
4 | source_code 'name = gets.strip; puts "hello, " + name'
5 | language_id { create(:language).id }
6 | number_of_runs 1
7 | stdin "world"
8 | expected_output "hello, world"
9 | end
10 |
11 | factory :submission, parent: :valid_submission do
12 | stdout "hello, world"
13 | status_id 1
14 | time 1.0
15 | memory 256
16 | end
17 |
18 | factory :invalid_submission, class: Submission do
19 | source_code 'name = gets.strip; puts "hello, " + name'
20 | # language_id 14 # :language_id should be present
21 | stdin "world"
22 | expected_output "hello, world"
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/config/secrets.yml:
--------------------------------------------------------------------------------
1 | default: &default
2 | authn_header: <%= ENV["AUTHN_HEADER"].presence || "X-Auth-Token" %>
3 | authn_token: <%= ENV["AUTHN_TOKEN"].to_s.strip %>
4 | authz_header: <%= ENV["AUTHZ_HEADER"].presence || "X-Auth-User" %>
5 | authz_token: <%= ENV["AUTHZ_TOKEN"].to_s.strip %>
6 |
7 | development:
8 | <<: *default
9 | secret_key_base: 54663e3c477de43bd117ddbf1798951ba5cf692dbf8327bb21f341048f04ea95344eed6c8763625a39648bfc2583f128b296bcb3a4756ef8160e483664efcb0b
10 |
11 | test:
12 | secret_key_base: 334f81f95f09f01e58b950ff5e5f9cd914cc151c186358d509d44520d4a1fb46c5af2dea39ed06a897c7cfe58b9c93316fa6c81081b7a30f5e43d271ca2aec0d
13 |
14 | production:
15 | <<: *default
16 | secret_key_base: <%= ENV["SECRET_KEY_BASE"].presence || (0...128).map { [*'0'..'9', *'a'..'z', *'A'..'Z'].sample }.join %>
17 |
--------------------------------------------------------------------------------
/bin/update:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'pathname'
3 | require 'fileutils'
4 | include FileUtils
5 |
6 | # path to your application root.
7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
8 |
9 | def system!(*args)
10 | system(*args) || abort("\n== Command #{args} failed ==")
11 | end
12 |
13 | chdir APP_ROOT do
14 | # This script is a way to update your development environment automatically.
15 | # Add necessary update steps to this file.
16 |
17 | puts '== Installing dependencies =='
18 | system! 'gem install bundler --conservative'
19 | system('bundle check') || system!('bundle install')
20 |
21 | puts "\n== Updating database =="
22 | system! 'bin/rails db:migrate'
23 |
24 | puts "\n== Removing old logs and tempfiles =="
25 | system! 'bin/rails log:clear tmp:clear'
26 |
27 | puts "\n== Restarting application server =="
28 | system! 'bin/rails restart'
29 | end
30 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 | services:
3 | api:
4 | image: judge0/api
5 | volumes:
6 | - ./judge0-api.conf:/judge0-api.conf:ro
7 | ports:
8 | - "3000:3000"
9 | privileged: true
10 | restart: always
11 |
12 | worker:
13 | image: judge0/api
14 | command: ["./scripts/run-workers"]
15 | volumes:
16 | - ./judge0-api.conf:/judge0-api.conf:ro
17 | privileged: true
18 | restart: always
19 |
20 | db:
21 | image: postgres:9.6
22 | env_file: judge0-api.conf
23 | volumes:
24 | - ./srv/postgres-data:/var/lib/postgresql/data/
25 | restart: always
26 |
27 | redis:
28 | image: redis:4.0
29 | command: [
30 | "bash", "-c",
31 | 'docker-entrypoint.sh --appendonly yes --requirepass "$$REDIS_PASSWORD"'
32 | ]
33 | env_file: judge0-api.conf
34 | volumes:
35 | - ./srv/redis-data:/data
36 | restart: always
37 |
--------------------------------------------------------------------------------
/spec/models/submission_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe Submission, type: :model do
4 | it { should validate_presence_of(:source_code) }
5 | it { should validate_presence_of(:language_id) }
6 |
7 | describe "#language" do
8 | let(:language) { create(:language) }
9 |
10 | it "returns language" do
11 | submission = create(:submission, { language_id: language.id })
12 | expect(submission.language).to eq(language)
13 | end
14 | end
15 |
16 | it "is invalid because Language with given id doesn't exist" do
17 | submission = build(:submission, { language_id: Language.all.size + 1 })
18 | expect(submission).not_to be_valid
19 | end
20 |
21 | it "is valid because Language with given id exists" do
22 | language = create(:language)
23 | submission = build(:submission, { language_id: language.id })
24 | expect(submission).to be_valid
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/config/initializers/new_framework_defaults.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 | #
3 | # This file contains migration options to ease your Rails 5.0 upgrade.
4 | #
5 | # Read the Rails 5.0 release notes for more info on each option.
6 |
7 | # Make Ruby 2.4 preserve the timezone of the receiver when calling `to_time`.
8 | # Previous versions had false.
9 | ActiveSupport.to_time_preserves_timezone = true
10 |
11 | # Require `belongs_to` associations by default. Previous versions had false.
12 | Rails.application.config.active_record.belongs_to_required_by_default = true
13 |
14 | # Do not halt callback chains when a callback returns false. Previous versions had true.
15 | ActiveSupport.halt_callback_chains_on_return_false = false
16 |
17 | # Configure SSL options to enable HSTS with subdomains. Previous versions had false.
18 | Rails.application.config.ssl_options = { hsts: { subdomains: true } }
19 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | Rails.application.routes.draw do
2 | root 'home#docs'
3 |
4 | resources :submissions, only: [:index, :show, :create, :destroy], param: :token do
5 | post 'batch', to: 'submissions#batch_create', on: :collection
6 | get 'batch', to: 'submissions#batch_show', on: :collection
7 | end
8 |
9 | resources :languages, only: [:index, :show] do
10 | get 'all', to: 'languages#all', on: :collection
11 | end
12 |
13 | resources :statuses, only: [:index]
14 |
15 | get 'system_info', to: 'info#system_info'
16 | get 'config_info', to: 'info#config_info'
17 | get 'isolate', to: 'info#isolate'
18 | get 'about', to: 'info#about'
19 | get 'version', to: 'info#version'
20 | get 'license', to: 'info#license'
21 | get 'statistics', to: 'info#statistics'
22 |
23 | post 'authenticate', to: 'sessions#authenticate'
24 | post 'authorize', to: 'sessions#authorize'
25 |
26 | get 'workers', to: 'health#workers'
27 | end
28 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM judge0/api-base:1.0.0
2 |
3 | ENV JUDGE0_HOMEPAGE="https://judge0.com"
4 | LABEL homepage=$JUDGE0_HOMEPAGE
5 |
6 | ENV JUDGE0_SOURCE_CODE="https://github.com/judge0/api"
7 | LABEL source_code=$JUDGE0_SOURCE_CODE
8 |
9 | ENV JUDGE0_MAINTAINER="Herman Zvonimir Došilović "
10 | LABEL maintainer=$JUDGE0_MAINTAINER
11 |
12 | ENV PATH "/usr/local/ruby-2.7.0/bin:/opt/.gem/bin:$PATH"
13 | ENV GEM_HOME "/opt/.gem/"
14 |
15 | RUN apt-get update && \
16 | apt-get install -y --no-install-recommends \
17 | libpq-dev \
18 | sudo && \
19 | rm -rf /var/lib/apt/lists/* && \
20 | echo "gem: --no-document" > /root/.gemrc && \
21 | gem install bundler:2.1.4 && \
22 | npm install -g --unsafe-perm aglio@2.3.0
23 |
24 | EXPOSE 3000
25 |
26 | WORKDIR /api
27 |
28 | COPY Gemfile* ./
29 | RUN RAILS_ENV=production bundle
30 |
31 | COPY . .
32 |
33 | CMD ["./scripts/run-server"]
34 |
35 | ENV JUDGE0_VERSION="1.5.0"
36 | LABEL version=$JUDGE0_VERSION
37 |
--------------------------------------------------------------------------------
/app/services/fields/submission.rb:
--------------------------------------------------------------------------------
1 | module Fields
2 | class Submission
3 | UNIVERSAL_FIELD = :*
4 |
5 | attr_reader :requested_fields, :invalid_fields
6 |
7 | def initialize(fields)
8 | @invalid_fields = []
9 |
10 | fields = fields.to_s.split(",").collect(&:to_sym)
11 |
12 | fields.each do |field|
13 | if field != UNIVERSAL_FIELD && !available_fields.include?(field)
14 | @invalid_fields << field
15 | end
16 | end
17 |
18 | if fields.include?(UNIVERSAL_FIELD)
19 | @requested_fields = available_fields
20 | else
21 | @requested_fields = (fields - @invalid_fields).presence || default_fields
22 | end
23 | end
24 |
25 | def has_invalid_fields?
26 | !@invalid_fields.empty?
27 | end
28 |
29 | def available_fields
30 | serializer._attributes
31 | end
32 |
33 | def default_fields
34 | serializer.default_fields
35 | end
36 |
37 | def serializer
38 | SubmissionSerializer
39 | end
40 | end
41 | end
--------------------------------------------------------------------------------
/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'pathname'
3 | require 'fileutils'
4 | include FileUtils
5 |
6 | # path to your application root.
7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
8 |
9 | def system!(*args)
10 | system(*args) || abort("\n== Command #{args} failed ==")
11 | end
12 |
13 | chdir APP_ROOT do
14 | # This script is a starting point to setup your application.
15 | # Add necessary setup steps to this file.
16 |
17 | puts '== Installing dependencies =='
18 | system! 'gem install bundler --conservative'
19 | system('bundle check') || system!('bundle install')
20 |
21 | # puts "\n== Copying sample files =="
22 | # unless File.exist?('config/database.yml')
23 | # cp 'config/database.yml.sample', 'config/database.yml'
24 | # end
25 |
26 | puts "\n== Preparing database =="
27 | system! 'bin/rails db:setup'
28 |
29 | puts "\n== Removing old logs and tempfiles =="
30 | system! 'bin/rails log:clear tmp:clear'
31 |
32 | puts "\n== Restarting application server =="
33 | system! 'bin/rails restart'
34 | end
35 |
--------------------------------------------------------------------------------
/docs/api/health_check/workers.md:
--------------------------------------------------------------------------------
1 | ## Workers Health Check [/workers]
2 | ### Workers Health Check [GET]
3 | For each queue you will get:
4 | - `queue` name
5 | - `total` number of workers that has been initially run
6 | - `available` number of workers
7 | - how many workers are `idle`
8 | - how many workers are currently `working`
9 | - how many workers are `paused`
10 | - how many jobs `failed`
11 |
12 | + Response 200 (application/json)
13 | [
14 | {
15 | "queue": "default",
16 | "total": 1,
17 | "available": 1,
18 | "idle": 1,
19 | "working": 0,
20 | "paused": 0,
21 | "failed": 0
22 | }
23 | ]
24 |
25 | + Response 500 (application/json)
26 | If `total` is not equal to `available`. This means that some workers died.
27 | + Body
28 | [
29 | {
30 | "queue": "default",
31 | "total": 3,
32 | "available": 1,
33 | "idle": 1,
34 | "working": 0,
35 | "paused": 0,
36 | "failed": 0
37 | }
38 | ]
--------------------------------------------------------------------------------
/app/enumerations/status.rb:
--------------------------------------------------------------------------------
1 | class Status < Enumerations::Base
2 | values queue: { id: 1, name: 'In Queue' },
3 | process: { id: 2, name: 'Processing' },
4 | ac: { id: 3, name: 'Accepted' },
5 | wa: { id: 4, name: 'Wrong Answer' },
6 | tle: { id: 5, name: 'Time Limit Exceeded' },
7 | ce: { id: 6, name: 'Compilation Error' },
8 | sigsegv: { id: 7, name: 'Runtime Error (SIGSEGV)' },
9 | sigxfsz: { id: 8, name: 'Runtime Error (SIGXFSZ)' },
10 | sigfpe: { id: 9, name: 'Runtime Error (SIGFPE)' },
11 | sigabrt: { id: 10, name: 'Runtime Error (SIGABRT)' },
12 | nzec: { id: 11, name: 'Runtime Error (NZEC)' },
13 | other: { id: 12, name: 'Runtime Error (Other)' },
14 | boxerr: { id: 13, name: 'Internal Error' },
15 | exeerr: { id: 14, name: 'Exec Format Error' }
16 |
17 | def self.find_runtime_error_by_status_code(status_code)
18 | case status_code.to_i
19 | when 11 then Status.sigsegv
20 | when 25 then Status.sigxfsz
21 | when 8 then Status.sigfpe
22 | when 6 then Status.sigabrt
23 | else Status.other
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/docs/api/authorization/authorization.md:
--------------------------------------------------------------------------------
1 | # Group Authorization
2 | To issue some API calls you need to be authorized. For example, you need to be authorized to
3 | [list all submissions](#submissions-submission-get-1) on Judge0 API.
4 |
5 | ## Authorize [/authorize{?X-Auth-User}]
6 | ### Authorize [POST]
7 | With this API call you can check if your authorization token is valid. If authentication is enabled you should also
8 | authenticate in this API call.
9 |
10 | ::: note
11 |
Note
12 | * `X-Auth-User` is default header field name, but administrators of Judge0 API instance you are using
13 | can change this default field name.
14 | * Contact administrator of Judge0 API instance you are using to get your authorization token.
15 | :::
16 |
17 | ::: warning
18 |
Security Warning
19 | * Although you can send authorization token as URI parameter, **always** send authorization token through headers.
20 | :::
21 |
22 | + Request
23 | + Headers
24 | ```
25 | X-Auth-User: a1133bc6-a0f6-46bf-a2d8-6157418c6fe2
26 | ```
27 |
28 |
29 | + Response 200
30 | If your authorization token is valid.
31 | + Body
32 |
33 |
34 |
35 | + Response 403
36 | Authorization failed because your authorization token is invalid.
37 | + Body
--------------------------------------------------------------------------------
/config/application.rb:
--------------------------------------------------------------------------------
1 | require_relative 'boot'
2 |
3 | require "rails"
4 | require "active_model/railtie"
5 | require "active_job/railtie"
6 | require "active_record/railtie"
7 | require "action_controller/railtie"
8 | require "action_mailer/railtie"
9 | require "action_view/railtie"
10 | require "action_cable/engine"
11 |
12 | require "rails/test_unit/railtie"
13 |
14 | Bundler.require(*Rails.groups)
15 |
16 | module Judge0API
17 | class Application < Rails::Application
18 | config.api_only = true
19 | config.generators do |g|
20 | g.factory_girl test_framework: :rspec
21 | end
22 |
23 | config.active_job.queue_adapter = :resque
24 |
25 | config.middleware.insert_before 0, Rack::Cors do
26 | origins = []
27 |
28 | disallowed_origins = (ENV['DISALLOW_ORIGIN'].to_s.split - ENV['ALLOW_ORIGIN'].to_s.split).collect{ |s| s.gsub(".", "\\.") }.join("|")
29 | if disallowed_origins.present?
30 | origins.append(Regexp.new("^(?:(?!#{disallowed_origins}).)*$"))
31 | end
32 |
33 | # ALLOW_ORIGIN and DISALLOW_ORIGIN are mutually exclusive so this doesn't have any effect.
34 | origins += (ENV['ALLOW_ORIGIN'].presence || (origins.present? ? '' : '*')).split
35 |
36 | allow do
37 | origins origins
38 | resource '*', headers: :any, methods: :any
39 | end
40 | end
41 | end
42 | end
43 |
--------------------------------------------------------------------------------
/docker-compose.dev.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 | services:
3 | nginx-proxy:
4 | image: jwilder/nginx-proxy
5 | container_name: judge0-dev-nginx-proxy
6 | volumes:
7 | - /var/run/docker.sock:/tmp/docker.sock:ro
8 | environment:
9 | - DEFAULT_HOST=api.judge0.local
10 | ports:
11 | - "8080:80"
12 | restart: always
13 |
14 | api:
15 | build:
16 | context: .
17 | dockerfile: Dockerfile.dev
18 | args:
19 | - DEV_USER=$JUDGE0_DEV_USER
20 | - DEV_USER_ID=$JUDGE0_DEV_UID
21 | image: judge0/api:dev
22 | container_name: judge0-dev-api
23 | environment:
24 | - VIRTUAL_HOST=api.judge0.local
25 | - VIRTUAL_PORT=3000
26 | volumes:
27 | - .:/api
28 | ports:
29 | - "3000:3000"
30 | - "3030:3030"
31 | privileged: true
32 |
33 | db:
34 | image: postgres:9.6
35 | container_name: judge0-dev-db
36 | env_file: judge0-api.conf
37 | volumes:
38 | - postgres-data:/var/lib/postgresql/data/
39 | ports:
40 | - "5432:5432"
41 | restart: always
42 |
43 | redis:
44 | image: redis:4.0
45 | container_name: judge0-dev-redis
46 | command: [
47 | "bash", "-c",
48 | 'docker-entrypoint.sh --appendonly yes --requirepass "$$REDIS_PASSWORD"'
49 | ]
50 | env_file: judge0-api.conf
51 | volumes:
52 | - redis-data:/data
53 | ports:
54 | - "6379:6379"
55 | restart: always
56 |
57 | volumes:
58 | postgres-data:
59 | redis-data:
60 |
--------------------------------------------------------------------------------
/docs/api/authentication/authentication.md:
--------------------------------------------------------------------------------
1 | # Group Authentication
2 | Administrators of Judge0 API can configure Judge0 API to require you to have an authentication token (a.k.a. API key).
3 | If that is the case with the instance of Judge0 API you are using, then you
4 | should provide `X-Auth-Token` header field in **every** API request.
5 |
6 | ::: note
7 |
Note
8 | * For [official public API](#header-official-public-api) you **do not** need an authentication token.
9 | :::
10 |
11 | ## Authenticate [/authenticate]
12 | ### Authenticate [POST]
13 | Check if your authentication token is valid.
14 |
15 | ::: note
16 |
Note
17 | * `X-Auth-Token` is default header field name, but administrators of Judge0 API instance you are using
18 | can change this default field name.
19 | * Contact administrator of Judge0 API instance you are using to get your authentication token.
20 | :::
21 |
22 | ::: warning
23 |
Security Warning
24 | * Although you can send authentication token as URI parameter, **always** send authentication token through headers.
25 | :::
26 |
27 | + Request
28 | + Headers
29 | ```
30 | X-Auth-Token: f6583e60-b13b-4228-b554-2eb332ca64e7
31 | ```
32 |
33 | + Response 200
34 | If your authentication token is valid or authentication is disabled.
35 | If authentication is disabled you do not need an authentication token.
36 | + Body
37 |
38 | + Response 401
39 | Authentication failed because your authentication token is invalid.
40 | + Body
--------------------------------------------------------------------------------
/spec/support/active_model_serializer_matcher.rb:
--------------------------------------------------------------------------------
1 | RSpec::Matchers.define :have_serialized do |unserialized|
2 | chain :with do |serializer, options = {}|
3 | @serializer = serializer
4 | @options = options
5 | end
6 |
7 | chain :wrap do |collection_serializer, options = {}|
8 | @collection_serializer = collection_serializer
9 | end
10 |
11 | def serialize(object)
12 | @collection_serializer ||= ActiveModel::Serializer::CollectionSerializer
13 |
14 | if object.respond_to?(:map)
15 | serializer = @collection_serializer.new(object, serializer: @serializer)
16 | else
17 | serializer = @serializer.new(object)
18 | end
19 |
20 | json = ActiveModelSerializers::Adapter::Attributes.new(serializer, @options).to_json
21 | JSON.parse(json)
22 | end
23 |
24 | match do |serialized|
25 | serialized == serialize(unserialized)
26 | end
27 |
28 | failure_message do |serialized|
29 | <<-FAIL.strip_heredoc
30 | expected:
31 | #{serialized}
32 | to match:
33 | #{serialize(unserialized)}
34 | ---
35 | #{inspect}
36 | FAIL
37 | end
38 |
39 | failure_message_when_negated do |serialized|
40 | <<-FAIL.strip_heredoc
41 | expected
42 | #{serialized}
43 | not to match:
44 | #{serialize(unserialized)}
45 | ---
46 | #{inspect}
47 | FAIL
48 | end
49 |
50 | def inspect
51 | <<-INSPECT.strip_heredoc
52 | Using object serializer: #{@serializer}
53 | Using collection serializer: #{@collection_serializer}
54 | INSPECT
55 | end
56 | end
57 |
--------------------------------------------------------------------------------
/app/serializers/submission_serializer.rb:
--------------------------------------------------------------------------------
1 | class SubmissionSerializer < ActiveModel::Serializer
2 | attributes((Submission.column_names + ["status", "language"] - ["id"]).collect(&:to_sym))
3 |
4 | def self.default_fields
5 | @@default_fields ||= [
6 | :token,
7 | :time,
8 | :memory,
9 | :stdout,
10 | :stderr,
11 | :compile_output,
12 | :message,
13 | :status
14 | ]
15 | end
16 |
17 | def source_code
18 | object_decoder(:source_code)
19 | end
20 |
21 | def stdin
22 | object_decoder(:stdin)
23 | end
24 |
25 | def expected_output
26 | object_decoder(:expected_output)
27 | end
28 |
29 | def stdout
30 | object_decoder(:stdout)
31 | end
32 |
33 | def stderr
34 | object_decoder(:stderr)
35 | end
36 |
37 | def compile_output
38 | object_decoder(:compile_output)
39 | end
40 |
41 | def message
42 | if instance_options[:base64_encoded] and object.message
43 | return Base64Service.encode(object.message)
44 | end
45 | object.message
46 | end
47 |
48 | def status
49 | { id: object.status_id, description: object.status.name }
50 | end
51 |
52 | def language
53 | ActiveModelSerializers::SerializableResource.new(object.language, { serializer: LanguageSerializer, fields: [:id, :name] })
54 | end
55 |
56 | def archive
57 | Base64Service.encode(object.archive)
58 | end
59 |
60 | private
61 |
62 | def object_decoder(method)
63 | instance_options[:base64_encoded] ? object[method] : object.send(method)
64 | end
65 | end
66 |
--------------------------------------------------------------------------------
/docs/api/statuses_and_languages/get_statuses.md:
--------------------------------------------------------------------------------
1 | ## Status [/statuses]
2 | ### Get Statuses [GET]
3 | + Response 200 (application/json)
4 | [
5 | {
6 | "id": 1,
7 | "description": "In Queue"
8 | },
9 | {
10 | "id": 2,
11 | "description": "Processing"
12 | },
13 | {
14 | "id": 3,
15 | "description": "Accepted"
16 | },
17 | {
18 | "id": 4,
19 | "description": "Wrong Answer"
20 | },
21 | {
22 | "id": 5,
23 | "description": "Time Limit Exceeded"
24 | },
25 | {
26 | "id": 6,
27 | "description": "Compilation Error"
28 | },
29 | {
30 | "id": 7,
31 | "description": "Runtime Error (SIGSEGV)"
32 | },
33 | {
34 | "id": 8,
35 | "description": "Runtime Error (SIGXFSZ)"
36 | },
37 | {
38 | "id": 9,
39 | "description": "Runtime Error (SIGFPE)"
40 | },
41 | {
42 | "id": 10,
43 | "description": "Runtime Error (SIGABRT)"
44 | },
45 | {
46 | "id": 11,
47 | "description": "Runtime Error (NZEC)"
48 | },
49 | {
50 | "id": 12,
51 | "description": "Runtime Error (Other)"
52 | },
53 | {
54 | "id": 13,
55 | "description": "Internal Error"
56 | },
57 | {
58 | "id": 14,
59 | "description": "Exec Format Error"
60 | }
61 | ]
62 |
--------------------------------------------------------------------------------
/RELEASE_NOTES_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | # vX.Y.Z (YYYY-MM-DD)
2 | ## API Changes
3 |
4 | ## New Features
5 |
6 | ## Improvements
7 |
8 | ## Security Improvements
9 |
10 | ## Bug Fixes
11 |
12 | ## Security Fixes
13 |
14 | ## Other Changes
15 |
16 | ## Deployment Procedure
17 | ### With HTTPS (SSL/TLS)
18 | 1. Install [Docker](https://docs.docker.com) and [Docker Compose](https://docs.docker.com/compose).
19 | 2. Download and extract release archive:
20 | ```
21 | wget https://github.com/judge0/api/releases/download/vX.Y.Z/judge0-api-vX.Y.Z-https.zip
22 | unzip judge0-api-vX.Y.Z-https.zip
23 | ```
24 |
25 | 3. Change directory to `judge0-api-vX.Y.Z-https`:
26 | ```
27 | cd judge0-api-vX.Y.Z-https
28 | ```
29 | 4. Edit `docker-compose.yml` and change variables `VIRTUAL_HOST`, `LETSENCRYPT_HOST` and `LETSENCRYPT_EMAIL`.
30 | 5. Run all services and wait few seconds until everything is initialized:
31 | ```
32 | docker-compose up -d db redis
33 | sleep 10s
34 | docker-compose up -d
35 | sleep 5s
36 | ```
37 |
38 | 6. Your instance of Judge0 API vX.Y.Z is now available at `https://`.
39 |
40 | ### With HTTP
41 | 1. Install [Docker](https://docs.docker.com) and [Docker Compose](https://docs.docker.com/compose).
42 | 2. Download and extract release archive:
43 | ```
44 | wget https://github.com/judge0/api/releases/download/vX.Y.Z/judge0-api-vX.Y.Z.zip
45 | unzip judge0-api-vX.Y.Z.zip
46 | ```
47 |
48 | 3. Run all services and wait few seconds until everything is initialized:
49 | ```
50 | cd judge0-api-vX.Y.Z
51 | docker-compose up -d db redis
52 | sleep 10s
53 | docker-compose up -d
54 | sleep 5s
55 | ```
56 |
57 | 4. Your instance of Judge0 API vX.Y.Z is now available at `http://`.
--------------------------------------------------------------------------------
/docs/api/submissions/delete_a_submission.md:
--------------------------------------------------------------------------------
1 | ### Delete a Submission [DELETE]
2 | Delete specific submission.
3 |
4 | You need to be authorized to issue this request. Although you are
5 | authorized you might not be able to delete submission because administrator of Judge0 API instance
6 | you are using disallowed submission deletion. So before using this feature please check [configuration](#system-and-configuration-configuration-info-get) of Judge0 API you are using.
7 |
8 | For this request query parameter `base64_encoded` is implicitly set to `true` and cannot be changed.
9 | This guarantees you will successfully get requested submission attributes after deletion.
10 |
11 | + Parameters
12 | + token (required, string, `d85cd024-1548-4165-96c7-7bc88673f194`) ... Token of submission. You got this token when you created submission.
13 | + fields = `stdout,time,memory,stderr,token,compile_output,message,status` (optional, string, `stdout,stderr,status_id,language_id`) ... Return only the desired attributes.
14 |
15 | + Response 200 (applcation/json)
16 | + Body
17 | {
18 | "stdout": "aGVsbG8sIHdvcmxkCg==\n",
19 | "time": "0.045",
20 | "memory": 8556,
21 | "stderr": null,
22 | "token": "e80153f5-e7d8-4cd2-9e10-6c0ddbf9e3bf",
23 | "compile_output": null,
24 | "message": null,
25 | "status": {
26 | "id": 3,
27 | "description": "Accepted"
28 | }
29 | }
30 |
31 | + Response 400 (application/json)
32 | If submission status is `1` or `2`.
33 | + Body
34 | {
35 | "error": "submission cannot be deleted because its status is 1 (In Queue)"
36 | }
37 |
38 |
39 |
--------------------------------------------------------------------------------
/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # In the development environment your application's code is reloaded on
5 | # every request. This slows down response time but is perfect for development
6 | # since you don't have to restart the web server when you make code changes.
7 | config.cache_classes = false
8 |
9 | # Do not eager load code on boot.
10 | config.eager_load = false
11 |
12 | # Show full error reports.
13 | config.consider_all_requests_local = true
14 |
15 | # Enable/disable caching. By default caching is disabled.
16 | if Rails.root.join('tmp/caching-dev.txt').exist?
17 | config.action_controller.perform_caching = true
18 |
19 | config.cache_store = :memory_store
20 | config.public_file_server.headers = {
21 | 'Cache-Control' => 'public, max-age=172800'
22 | }
23 | else
24 | config.action_controller.perform_caching = false
25 |
26 | config.cache_store = :null_store
27 | end
28 |
29 | # Don't care if the mailer can't send.
30 | config.action_mailer.raise_delivery_errors = false
31 |
32 | config.action_mailer.perform_caching = false
33 |
34 | # Print deprecation notices to the Rails logger.
35 | config.active_support.deprecation = :log
36 |
37 | # Raise an error on page load if there are pending migrations.
38 | config.active_record.migration_error = :page_load
39 |
40 |
41 | # Raises error for missing translations
42 | # config.action_view.raise_on_missing_translations = true
43 |
44 | # Use an evented file watcher to asynchronously detect changes in source code,
45 | # routes, locales, etc. This feature depends on the listen gem.
46 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker
47 | end
48 |
--------------------------------------------------------------------------------
/docs/api/system_and_configuration/system_info.md:
--------------------------------------------------------------------------------
1 | ## System Info [/system_info]
2 | ### System Info [GET]
3 | System information gives you detailed information about system on which Judge0 API is running.
4 |
5 | This information is result of two commands on a host system:
6 | - `lscpu`
7 | - `free -h`
8 |
9 | Please note that Judge0 API consists of two systems: **web** and **worker**. **Web** system is the one who
10 | provides you the web API, and **Worker** is the one who processes your submissions. They can be placed on two or more
11 | different hosts with different system configurations. Result of this API request is always from web system.
12 | This means that this request might be irrelevant to you if you as user don't know if web and worker are
13 | hosted on the same machine. To find that out, please contact admins who host Judge0 API you are using.
14 | + Response 200 (application/json)
15 | {
16 | "Architecture": "x86_64",
17 | "CPU op-mode(s)": "32-bit, 64-bit",
18 | "Byte Order": "Little Endian",
19 | "CPU(s)": "4",
20 | "On-line CPU(s) list": "0-3",
21 | "Thread(s) per core": "2",
22 | "Core(s) per socket": "2",
23 | "Socket(s)": "1",
24 | "NUMA node(s)": "1",
25 | "Vendor ID": "GenuineIntel",
26 | "CPU family": "6",
27 | "Model": "61",
28 | "Model name": "Intel(R) Core(TM) i5-5200U CPU @ 2.20GHz",
29 | "Stepping": "4",
30 | "CPU MHz": "2508.703",
31 | "CPU max MHz": "2700.0000",
32 | "CPU min MHz": "500.0000",
33 | "BogoMIPS": "4392.12",
34 | "Virtualization": "VT-x",
35 | "L1d cache": "32K",
36 | "L1i cache": "32K",
37 | "L2 cache": "256K",
38 | "L3 cache": "3072K",
39 | "NUMA node0 CPU(s)": "0-3",
40 | "Mem": "7.7G",
41 | "Swap": "8.0G"
42 | }
--------------------------------------------------------------------------------
/app/controllers/sessions_controller.rb:
--------------------------------------------------------------------------------
1 | class SessionsController < ActionController::API
2 | before_action :verify_ip_address
3 | before_action :authenticate_request
4 | before_action :authorize_request, only: [:authorize]
5 |
6 | def authenticate
7 | head :ok
8 | end
9 |
10 | def authorize
11 | head :ok
12 | end
13 |
14 | private
15 |
16 | def verify_ip_address
17 | @@disallowed_ip_addresses ||= ENV['DISALLOW_IP'].to_s.split - ENV['ALLOW_IP'].to_s.split
18 | @@allowed_ip_addresses ||= ENV['ALLOW_IP'].to_s.split - ENV['DISALLOW_IP'].to_s.split
19 | head :forbidden if @@disallowed_ip_addresses.include?(request.remote_ip)
20 | head :forbidden if @@allowed_ip_addresses.present? && !@@allowed_ip_addresses.include?(request.remote_ip)
21 | end
22 |
23 | def authenticate_request
24 | head :unauthorized if safe_compare(Rails.application.secrets.authn_token, Rails.application.secrets.authn_header)
25 | end
26 |
27 | def authorize_request
28 | head :forbidden unless Rails.application.secrets.authz_token.present?
29 | head :forbidden if safe_compare(Rails.application.secrets.authz_token, Rails.application.secrets.authz_header)
30 | end
31 |
32 | def check_maintenance
33 | @@maintenance_message ||= ENV['MAINTENANCE_MESSAGE'].to_s.presence || "Judge0 API is currently in maintenance."
34 | if Config::MAINTENANCE_MODE
35 | render json: {
36 | error: @@maintenance_message
37 | }, status: :service_unavailable
38 | end
39 | end
40 |
41 | def safe_compare(token, header)
42 | token = token.to_s
43 | header = header.to_s
44 | return false unless token.present?
45 | provided_token = (request.headers[header] || params[header]).to_s
46 | token.split.each do |value|
47 | return false if ActiveSupport::SecurityUtils.secure_compare(value, provided_token)
48 | end
49 | true
50 | end
51 | end
52 |
--------------------------------------------------------------------------------
/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # The test environment is used exclusively to run your application's
5 | # test suite. You never need to work with it otherwise. Remember that
6 | # your test database is "scratch space" for the test suite and is wiped
7 | # and recreated between test runs. Don't rely on the data there!
8 | config.cache_classes = true
9 |
10 | # Do not eager load code on boot. This avoids loading your whole application
11 | # just for the purpose of running a single test. If you are using a tool that
12 | # preloads Rails for running tests, you may have to set it to true.
13 | config.eager_load = false
14 |
15 | # Configure public file server for tests with Cache-Control for performance.
16 | config.public_file_server.enabled = true
17 | config.public_file_server.headers = {
18 | 'Cache-Control' => 'public, max-age=3600'
19 | }
20 |
21 | # Show full error reports and disable caching.
22 | config.consider_all_requests_local = true
23 | config.action_controller.perform_caching = false
24 |
25 | # Raise exceptions instead of rendering exception templates.
26 | config.action_dispatch.show_exceptions = false
27 |
28 | # Disable request forgery protection in test environment.
29 | config.action_controller.allow_forgery_protection = false
30 | config.action_mailer.perform_caching = false
31 |
32 | # Tell Action Mailer not to deliver emails to the real world.
33 | # The :test delivery method accumulates sent emails in the
34 | # ActionMailer::Base.deliveries array.
35 | config.action_mailer.delivery_method = :test
36 |
37 | # Print deprecation notices to the stderr.
38 | config.active_support.deprecation = :stderr
39 |
40 | # Raises error for missing translations
41 | # config.action_view.raise_on_missing_translations = true
42 | end
43 |
--------------------------------------------------------------------------------
/lib/tasks/auto_annotate_models.rake:
--------------------------------------------------------------------------------
1 | if Rails.env.development?
2 | task :set_annotation_options do
3 | # You can override any of these by setting an environment variable of the
4 | # same name.
5 | Annotate.set_defaults(
6 | 'routes' => 'false',
7 | 'position_in_routes' => 'before',
8 | 'position_in_class' => 'before',
9 | 'position_in_test' => 'before',
10 | 'position_in_fixture' => 'before',
11 | 'position_in_factory' => 'before',
12 | 'position_in_serializer' => 'before',
13 | 'show_foreign_keys' => 'true',
14 | 'show_indexes' => 'true',
15 | 'simple_indexes' => 'false',
16 | 'model_dir' => 'app/models',
17 | 'root_dir' => '',
18 | 'include_version' => 'false',
19 | 'require' => '',
20 | 'exclude_tests' => 'true',
21 | 'exclude_fixtures' => 'true',
22 | 'exclude_factories' => 'true',
23 | 'exclude_serializers' => 'true',
24 | 'exclude_scaffolds' => 'true',
25 | 'exclude_controllers' => 'true',
26 | 'exclude_helpers' => 'true',
27 | 'ignore_model_sub_dir' => 'false',
28 | 'ignore_columns' => nil,
29 | 'ignore_routes' => nil,
30 | 'ignore_unknown_models' => 'false',
31 | 'hide_limit_column_types' => 'integer,boolean',
32 | 'skip_on_db_migrate' => 'false',
33 | 'format_bare' => 'true',
34 | 'format_rdoc' => 'false',
35 | 'format_markdown' => 'false',
36 | 'sort' => 'false',
37 | 'force' => 'false',
38 | 'trace' => 'false',
39 | 'wrapper_open' => nil,
40 | 'wrapper_close' => nil
41 | )
42 | end
43 |
44 | Annotate.load_tasks
45 | end
46 |
--------------------------------------------------------------------------------
/spec/controllers/submissions_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe SubmissionsController, type: :controller do
4 |
5 | let(:submission) { create(:submission) }
6 |
7 | describe "GET #show" do
8 | it "returns one submission" do
9 | get :show, params: { token: submission.token }
10 | json = JSON.parse(response.body)
11 | expect(response).to be_success
12 | expect(json).to have_serialized(submission).with(SubmissionSerializer)
13 | end
14 |
15 | it "returns 404" do
16 | expect {
17 | get :show, params: { token: submission.token + " give me 404" }
18 | }.to raise_error(ActiveRecord::RecordNotFound)
19 | end
20 | end
21 |
22 | describe "POST #create" do
23 | context "with valid params" do
24 | it "creates a new Submission" do
25 | expect {
26 | post :create, params: attributes_for(:valid_submission)
27 | }.to change(Submission, :count).by(1)
28 | end
29 |
30 | it "returns only id of new Submission" do
31 | post :create, params: attributes_for(:valid_submission)
32 | json = JSON.parse(response.body)
33 | expect(response).to be_success
34 | expect(json).to have_serialized(Submission.first).with(SubmissionSerializer, { fields: [:token] })
35 | end
36 |
37 | it "doesn't create new Submission because given Language doesn't exist" do
38 | attributes = attributes_for(:valid_submission)
39 | attributes[:language_id] = 142 # Language with id 142 doesn't exist
40 | post :create, params: attributes
41 | expect(response).to have_http_status(422)
42 | end
43 | end
44 |
45 | context "with invalid params" do
46 | it "doesn't create new Submission" do
47 | post :create, params: { submission: attributes_for(:invalid_submission) }
48 | expect(response).to have_http_status(422)
49 | end
50 | end
51 | end
52 | end
53 |
--------------------------------------------------------------------------------
/db/schema.rb:
--------------------------------------------------------------------------------
1 | # This file is auto-generated from the current state of the database. Instead
2 | # of editing this file, please use the migrations feature of Active Record to
3 | # incrementally modify your database, and then regenerate this schema definition.
4 | #
5 | # Note that this schema.rb definition is the authoritative source for your
6 | # database schema. If you need to create the application database on another
7 | # system, you should be using db:schema:load, not running all the migrations
8 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations
9 | # you'll amass, the slower it'll run and the greater likelihood for issues).
10 | #
11 | # It's strongly recommended that you check this file into your version control system.
12 |
13 | ActiveRecord::Schema.define(version: 20200115205044) do
14 |
15 | # These are extensions that must be enabled in order to support this database
16 | enable_extension "plpgsql"
17 |
18 | create_table "languages", force: :cascade do |t|
19 | t.string "name"
20 | t.string "compile_cmd"
21 | t.string "run_cmd"
22 | t.string "source_file"
23 | t.boolean "is_archived", default: false
24 | end
25 |
26 | create_table "submissions", force: :cascade do |t|
27 | t.text "source_code"
28 | t.integer "language_id"
29 | t.text "stdin"
30 | t.text "expected_output"
31 | t.text "stdout"
32 | t.integer "status_id"
33 | t.datetime "created_at"
34 | t.datetime "finished_at"
35 | t.decimal "time"
36 | t.integer "memory"
37 | t.text "stderr"
38 | t.string "token"
39 | t.integer "number_of_runs"
40 | t.decimal "cpu_time_limit"
41 | t.decimal "cpu_extra_time"
42 | t.decimal "wall_time_limit"
43 | t.integer "memory_limit"
44 | t.integer "stack_limit"
45 | t.integer "max_processes_and_or_threads"
46 | t.boolean "enable_per_process_and_thread_time_limit"
47 | t.boolean "enable_per_process_and_thread_memory_limit"
48 | t.integer "max_file_size"
49 | t.text "compile_output"
50 | t.integer "exit_code"
51 | t.integer "exit_signal"
52 | t.text "message"
53 | t.decimal "wall_time"
54 | t.string "compiler_options"
55 | t.string "command_line_arguments"
56 | t.boolean "redirect_stderr_to_stdout"
57 | t.string "callback_url"
58 | t.binary "archive"
59 | t.index ["token"], name: "index_submissions_on_token", using: :btree
60 | end
61 |
62 | end
63 |
--------------------------------------------------------------------------------
/app/controllers/info_controller.rb:
--------------------------------------------------------------------------------
1 | class InfoController < ApplicationController
2 | @@license ||= File.read("LICENSE")
3 | @@isolate ||= `isolate --version`
4 |
5 | def system_info
6 | render json: SystemInfo.sys_info
7 | end
8 |
9 | def config_info
10 | render json: Config.config_info
11 | end
12 |
13 | def about
14 | render json: {
15 | version: ENV["JUDGE0_VERSION"],
16 | homepage: ENV["JUDGE0_HOMEPAGE"],
17 | source_code: ENV["JUDGE0_SOURCE_CODE"],
18 | maintainer: ENV["JUDGE0_MAINTAINER"]
19 | }
20 | end
21 |
22 | def version
23 | render plain: ENV["JUDGE0_VERSION"]
24 | end
25 |
26 | def license
27 | render plain: @@license
28 | end
29 |
30 | def isolate
31 | render plain: @@isolate
32 | end
33 |
34 | def statistics
35 | @@language_name ||= Hash[Language.unscoped.pluck(:id, :name)]
36 |
37 | count_by_language = []
38 | Submission.unscoped.group(:language_id).count.each do |language_id, count|
39 | count_by_language << {
40 | language: {
41 | id: language_id,
42 | name: @@language_name[language_id]
43 | },
44 | count: count
45 | }
46 | end
47 | count_by_language = count_by_language.sort_by { |x| x[:count] }.reverse
48 |
49 | count_by_status = []
50 | Submission.unscoped.group(:status_id).count.each do |status_id, count|
51 | count_by_status << {
52 | status: {
53 | id: status_id,
54 | name: Status.find_by(id: status_id).name # Not a SQL query!
55 | },
56 | count: count
57 | }
58 | end
59 | count_by_status = count_by_status.sort_by{ |x| x[:count] }.reverse
60 |
61 | now = DateTime.now
62 | today = DateTime.now.beginning_of_day.to_date
63 | last_30_days = Submission.unscoped.group("created_at::DATE").where("created_at::DATE >= ?", today - 30).count
64 | last_30_days_result = {}
65 | (today-30...today).each do |day|
66 | last_30_days_result[day.to_date] = last_30_days[day] || 0
67 | end
68 | last_30_days_result = last_30_days_result.sort.reverse.to_h
69 |
70 | database_size = ActiveRecord::Base.connection.execute("SELECT pg_size_pretty(pg_database_size('#{ENV['POSTGRES_DB']}')) AS size").to_a[0]["size"]
71 |
72 | render json: {
73 | now: now,
74 | submissions: {
75 | total: Submission.count,
76 | today: last_30_days[today],
77 | last_30_days: last_30_days_result
78 | },
79 | languages: count_by_language,
80 | statuses: count_by_status,
81 | database: {
82 | size: database_size
83 | }
84 | }
85 | end
86 | end
--------------------------------------------------------------------------------
/docs/api/docs.md:
--------------------------------------------------------------------------------
1 | FORMAT: 1A
2 | HOST: https://api.judge0.com
3 |
4 | # Judge0 API docs
5 |
6 |
7 |
8 | ## About
9 | [Judge0](https://judge0.com) is a non-profit organization that creates free and open-source tools and services for executing and grading untrusted source code.
10 |
11 | [Judge0 API](https://api.judge0.com) is a robust and scalable open-source (source code available on [GitHub](https://github.com/judge0/api)) online code execution system made by Judge0.
12 |
13 | Judge0 API can help you build wide range of applications varying from competitive programming platforms,
14 | educational and recruitment platforms, to online code editors and more.
15 |
16 | To see Judge0 API in action, try [Judge0 IDE](https://ide.judge0.com) - free and open-source code editor that uses Judge0 API for executing the user’s source code. You can also try using a [dummy client](/dummy-client.html), which can help you explore and test almost all the features of Judge0 API.
17 |
18 | ## Official public API
19 | Official public API is available at [https://api.judge0.com](https://api.judge0.com) and is maintained by Judge0 [creator](https://github.com/hermanzdosilovic).
20 |
21 | Status page of all public Judge0 services is available at [https://status.judge0.com](https://status.judge0.com).
22 |
23 | ::: note
24 |
Note
25 | * You **do not** need an authentication token (a.k.a. API key) for official public API.
26 | * Official public API will always be at the latest available version.
27 | :::
28 |
29 | ## Version
30 | This document describes Judge0 API version [`v1.5.0`](https://github.com/judge0/api/tree/v1.5.0).
31 |
32 | ## Date and time formats
33 | [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) standard is used.
34 |
35 | Example: `2016-09-11T10:19:35Z`
36 |
37 | ## License
38 | Judge0 API is licensed under the [GNU General Public License v3.0](https://github.com/judge0/api/blob/master/LICENSE).
39 |
40 | ## Donate
41 | Your are more than welcome to support Judge0 on [Patreon](https://www.patreon.com/hermanzdosilovic) or via [PayPal](https://www.paypal.me/hermanzdosilovic). Your monthly or one-time donation will help pay server costs and will improve future development and maintenance. Thank you ♥
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/docs/api/submissions/get_a_submission.md:
--------------------------------------------------------------------------------
1 | ### Get a Submission [GET]
2 | Returns details about submission.
3 |
4 | Just like in [create submission](/#submission-submission-post) you can receive Base64 encoded data for every
5 | text type attribute (check the [table](#submission-submission) to see which attributes are text type).
6 | By default, this parameter is set to `false` and Judge0 API will send you raw data.
7 |
8 | By default Judge0 API is sending you 8 attributes for submission. As you may read in [Submission](#submission-submission)
9 | section, submission model has 29 attributes. By sending `fields` query parameter you can specify exactly which attributes
10 | you want from Judge0 API.
11 |
12 | + Parameters
13 | + token (required, string, `d85cd024-1548-4165-96c7-7bc88673f194`) ... Token of submission. You got this token when you created submission.
14 | + base64_encoded (optional, boolean, `false`) ... Set to `true` if you want to receive Base64 encoded data from Judge0 API.
15 | + fields = `stdout,time,memory,stderr,token,compile_output,message,status` (optional, string, `stdout,stderr,status_id,language_id`) ... Return only the desired attributes.
16 |
17 | + Response 200 (applicatiion/json)
18 | {
19 | "stdout": "hello, world\n",
20 | "status_id": 5,
21 | "language_id": 4,
22 | "stderr": null
23 | }
24 |
25 | + Response 200 (application/json)
26 | This is the default response. Leave `fields` parameter empty if you want to get default response.
27 | + Body
28 | {
29 | "stdout": "hello, Judge0\n",
30 | "time": "0.001",
31 | "memory": 376,
32 | "stderr": null,
33 | "token": "8531f293-1585-4d36-a34c-73726792e6c9",
34 | "compile_output": null,
35 | "message": null,
36 | "status": {
37 | "id": 3,
38 | "description": "Accepted"
39 | }
40 | }
41 |
42 | + Response 200 (application/json)
43 | Recieving Base64 encoded data for text type attributes. Note that in this request `base64_encoded` query parameter **must** be
44 | set to `true`.
45 | + Body
46 | {
47 | "stdout": "aGVsbG8sIEp1ZGdlMAo=\n",
48 | "time": "0.002",
49 | "memory": 376,
50 | "stderr": null,
51 | "token": "4e00f214-b8cb-4fcb-977b-429113c81ece",
52 | "compile_output": null,
53 | "message": null,
54 | "status": {
55 | "id": 3,
56 | "description": "Accepted"
57 | }
58 | }
59 |
60 | + Response 400 (application/json)
61 | + Body
62 | {
63 | "error": "some attributes for this submission cannot be converted to UTF-8, use base64_encoded=true query parameter"
64 | }
65 |
66 |
67 |
--------------------------------------------------------------------------------
/docs/api/statuses_and_languages/get_languages.md:
--------------------------------------------------------------------------------
1 | ### Get Languages [GET]
2 | Get active languages.
3 |
4 | + Response 200 (application/json)
5 | [
6 | {
7 | "id": 45,
8 | "name": "Assembly (NASM 2.14.02)"
9 | },
10 | {
11 | "id": 46,
12 | "name": "Bash (5.0.0)"
13 | },
14 | {
15 | "id": 47,
16 | "name": "Basic (FBC 1.07.1)"
17 | },
18 | {
19 | "id": 48,
20 | "name": "C (GCC 7.4.0)"
21 | },
22 | {
23 | "id": 52,
24 | "name": "C++ (GCC 7.4.0)"
25 | },
26 | {
27 | "id": 49,
28 | "name": "C (GCC 8.3.0)"
29 | },
30 | {
31 | "id": 53,
32 | "name": "C++ (GCC 8.3.0)"
33 | },
34 | {
35 | "id": 50,
36 | "name": "C (GCC 9.2.0)"
37 | },
38 | {
39 | "id": 54,
40 | "name": "C++ (GCC 9.2.0)"
41 | },
42 | {
43 | "id": 51,
44 | "name": "C# (Mono 6.6.0.161)"
45 | },
46 | {
47 | "id": 55,
48 | "name": "Common Lisp (SBCL 2.0.0)"
49 | },
50 | {
51 | "id": 56,
52 | "name": "D (DMD 2.089.1)"
53 | },
54 | {
55 | "id": 57,
56 | "name": "Elixir (1.9.4)"
57 | },
58 | {
59 | "id": 58,
60 | "name": "Erlang (OTP 22.2)"
61 | },
62 | {
63 | "id": 44,
64 | "name": "Executable"
65 | },
66 | {
67 | "id": 59,
68 | "name": "Fortran (GFortran 9.2.0)"
69 | },
70 | {
71 | "id": 60,
72 | "name": "Go (1.13.5)"
73 | },
74 | {
75 | "id": 61,
76 | "name": "Haskell (GHC 8.8.1)"
77 | },
78 | {
79 | "id": 62,
80 | "name": "Java (OpenJDK 13.0.1)"
81 | },
82 | {
83 | "id": 63,
84 | "name": "JavaScript (Node.js 12.14.0)"
85 | },
86 | {
87 | "id": 64,
88 | "name": "Lua (5.3.5)"
89 | },
90 | {
91 | "id": 65,
92 | "name": "OCaml (4.09.0)"
93 | },
94 | {
95 | "id": 66,
96 | "name": "Octave (5.1.0)"
97 | },
98 | {
99 | "id": 67,
100 | "name": "Pascal (FPC 3.0.4)"
101 | },
102 | {
103 | "id": 68,
104 | "name": "PHP (7.4.1)"
105 | },
106 | {
107 | "id": 43,
108 | "name": "Plain Text"
109 | },
110 | {
111 | "id": 69,
112 | "name": "Prolog (GNU Prolog 1.4.5)"
113 | },
114 | {
115 | "id": 70,
116 | "name": "Python (2.7.17)"
117 | },
118 | {
119 | "id": 71,
120 | "name": "Python (3.8.1)"
121 | },
122 | {
123 | "id": 72,
124 | "name": "Ruby (2.7.0)"
125 | },
126 | {
127 | "id": 73,
128 | "name": "Rust (1.40.0)"
129 | },
130 | {
131 | "id": 74,
132 | "name": "TypeScript (3.7.4)"
133 | }
134 | ]
--------------------------------------------------------------------------------
/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # Code is not reloaded between requests.
5 | config.cache_classes = true
6 |
7 | # Eager load code on boot. This eager loads most of Rails and
8 | # your application in memory, allowing both threaded web servers
9 | # and those relying on copy on write to perform better.
10 | # Rake tasks automatically ignore this option for performance.
11 | config.eager_load = true
12 |
13 | # Full error reports are disabled and caching is turned on.
14 | config.consider_all_requests_local = false
15 | config.action_controller.perform_caching = true
16 |
17 | # Disable serving static files from the `/public` folder by default since
18 | # Apache or NGINX already handles this.
19 | config.public_file_server.enabled = true
20 |
21 |
22 | # Enable serving of images, stylesheets, and JavaScripts from an asset server.
23 | # config.action_controller.asset_host = 'http://assets.example.com'
24 |
25 | # Specifies the header that your server uses for sending files.
26 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
27 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
28 |
29 | # Mount Action Cable outside main process or domain
30 | # config.action_cable.mount_path = nil
31 | # config.action_cable.url = 'wss://example.com/cable'
32 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
33 |
34 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
35 | # config.force_ssl = true
36 |
37 | # Use the lowest log level to ensure availability of diagnostic information
38 | # when problems arise.
39 | config.log_level = :debug
40 |
41 | # Prepend all log lines with the following tags.
42 | config.log_tags = [ :request_id ]
43 |
44 | # Use a different cache store in production.
45 | # config.cache_store = :mem_cache_store
46 |
47 | # Use a real queuing backend for Active Job (and separate queues per environment)
48 | # config.active_job.queue_adapter = :resque
49 | # config.active_job.queue_name_prefix = "Judge0API_#{Rails.env}"
50 | config.action_mailer.perform_caching = false
51 |
52 | # Ignore bad email addresses and do not raise email delivery errors.
53 | # Set this to true and configure the email server for immediate delivery to raise delivery errors.
54 | # config.action_mailer.raise_delivery_errors = false
55 |
56 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
57 | # the I18n.default_locale when a translation cannot be found).
58 | config.i18n.fallbacks = [I18n.default_locale]
59 |
60 | # Send deprecation notices to registered listeners.
61 | config.active_support.deprecation = :notify
62 |
63 | # Use default logging formatter so that PID and timestamp are not suppressed.
64 | config.log_formatter = ::Logger::Formatter.new
65 |
66 | # Use a different logger for distributed setups.
67 | # require 'syslog/logger'
68 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
69 |
70 | if ENV["RAILS_LOG_TO_STDOUT"].present?
71 | logger = ActiveSupport::Logger.new(STDOUT)
72 | logger.formatter = config.log_formatter
73 | config.logger = ActiveSupport::TaggedLogging.new(logger)
74 | end
75 |
76 | # Do not dump schema after migrations.
77 | config.active_record.dump_schema_after_migration = false
78 | end
79 |
--------------------------------------------------------------------------------
/docs/api/submissions/get_submissions.md:
--------------------------------------------------------------------------------
1 | ### Get Submissions [GET]
2 | + Parameters
3 | + base64_encoded = `false` (optional, boolean, `false`) ... Set to `true` if you want to receive Base64 encoded data from Judge0 API.
4 | + page = `1` (optional, integer, `4`) ... Pagination page number.
5 | + per_page = `20` (optional, integer, `2`) ... Number of submissions to return per page.
6 | + fields = `stdout,time,memory,stderr,token,compile_output,message,status` (optional, string, `status,language,time`) ... Return only the desired attributes.
7 |
8 | + Response 200 (application/json)
9 | {
10 | "submissions": [
11 | {
12 | "time": "0.001",
13 | "status": {
14 | "id": 3,
15 | "description": "Accepted"
16 | },
17 | "language": {
18 | "id": 4,
19 | "name": "C (gcc 7.2.0)"
20 | }
21 | },
22 | {
23 | "time": "0.001",
24 | "status": {
25 | "id": 3,
26 | "description": "Accepted"
27 | },
28 | "language": {
29 | "id": 4,
30 | "name": "C (gcc 7.2.0)"
31 | }
32 | }
33 | ],
34 | "meta": {
35 | "current_page": 4,
36 | "next_page": 5,
37 | "prev_page": 3,
38 | "total_pages": 31,
39 | "total_count": 62
40 | }
41 | }
42 |
43 | + Response 200 (application/json)
44 | When `base64_encoded` is set to `true`.
45 | + Body
46 | {
47 | "submissions": [
48 | {
49 | "stdout": "aGVsbG8sIEp1ZGdlMAo=\n",
50 | "time": "0.001",
51 | "memory": 376,
52 | "stderr": null,
53 | "token": "a1133bc6-a0f6-46bf-a2d8-6157418c6fe2",
54 | "compile_output": null,
55 | "message": null,
56 | "status": {
57 | "id": 3,
58 | "description": "Accepted"
59 | }
60 | },
61 | {
62 | "stdout": "aGVsbG8sIEp1ZGdlMAo=\n",
63 | "time": "0.001",
64 | "memory": 380,
65 | "stderr": null,
66 | "token": "eb0dd001-66db-47f4-8a69-b736c9bc23f6",
67 | "compile_output": null,
68 | "message": null,
69 | "status": {
70 | "id": 3,
71 | "description": "Accepted"
72 | }
73 | }
74 | ],
75 | "meta": {
76 | "current_page": 4,
77 | "next_page": 5,
78 | "prev_page": 3,
79 | "total_pages": 31,
80 | "total_count": 62
81 | }
82 | }
83 |
84 | + Response 400 (application/json)
85 | When `page` parameter is invalid.
86 | + Body
87 | {
88 | "error": "invalid page: -4"
89 | }
90 |
91 | + Response 400 (application/json)
92 | When `per_page` parameter is invalid.
93 | + Body
94 | {
95 | "error": "invalid per_page: -2"
96 | }
97 |
98 | + Response 400 (application/json)
99 | + Body
100 | {
101 | "error": "some attributes for one or more submissions cannot be converted to UTF-8, use base64_encoded=true query parameter"
102 | }
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/app/helpers/config.rb:
--------------------------------------------------------------------------------
1 | module Config
2 | # For more info read:
3 | # https://github.com/judge0/api/blob/master/judge0-api.conf.default
4 |
5 | MAINTENANCE_MODE = ENV['MAINTENANCE_MODE'] == "true"
6 | ENABLE_WAIT_RESULT = ENV['ENABLE_WAIT_RESULT'] != "false"
7 | ENABLE_COMPILER_OPTIONS = ENV["ENABLE_COMPILER_OPTIONS"] != "false"
8 | ALLOWED_LANGUAGES_FOR_COMPILER_OPTIONS = ENV["ALLOWED_LANGUAGES_FOR_COMPILER_OPTIONS"].to_s.strip.split
9 | ENABLE_COMMAND_LINE_ARGUMENTS = ENV["ENABLE_COMMAND_LINE_ARGUMENTS"] != "false"
10 | ENABLE_SUBMISSION_DELETE = ENV["ENABLE_SUBMISSION_DELETE"] == "true"
11 | MAX_QUEUE_SIZE = (ENV['MAX_QUEUE_SIZE'].presence || 100).to_i
12 | CPU_TIME_LIMIT = (ENV['CPU_TIME_LIMIT'].presence || 2).to_f
13 | MAX_CPU_TIME_LIMIT = (ENV['MAX_CPU_TIME_LIMIT'].presence || 15).to_f
14 | CPU_EXTRA_TIME = (ENV['CPU_EXTRA_TIME'].presence || 0.5).to_f
15 | MAX_CPU_EXTRA_TIME = (ENV['MAX_CPU_EXTRA_TIME'].presence || 2).to_f
16 | WALL_TIME_LIMIT = (ENV['WALL_TIME_LIMIT'].presence || 5).to_f
17 | MAX_WALL_TIME_LIMIT = (ENV['MAX_WALL_TIME_LIMIT'].presence || 20).to_f
18 | MEMORY_LIMIT = (ENV['MEMORY_LIMIT'].presence || 128000).to_i # in KB
19 | MAX_MEMORY_LIMIT = (ENV['MAX_MEMORY_LIMIT'].presence || 256000).to_i
20 | STACK_LIMIT = (ENV['STACK_LIMIT'].presence || 64000).to_i # in KB
21 | MAX_STACK_LIMIT = (ENV['MAX_STACK_LIMIT'].presence || 128000).to_i
22 | MAX_PROCESSES_AND_OR_THREADS = (ENV['MAX_PROCESSES_AND_OR_THREADS'].presence || 60).to_i
23 | MAX_MAX_PROCESSES_AND_OR_THREADS = (ENV['MAX_MAX_PROCESSES_AND_OR_THREADS'].presence || 120).to_i
24 | ENABLE_PER_PROCESS_AND_THREAD_TIME_LIMIT = ENV['ENABLE_PER_PROCESS_AND_THREAD_TIME_LIMIT'] == "true"
25 | ALLOW_ENABLE_PER_PROCESS_AND_THREAD_TIME_LIMIT = ENV['ALLOW_ENABLE_PER_PROCESS_AND_THREAD_TIME_LIMIT'] != "false"
26 | ENABLE_PER_PROCESS_AND_THREAD_MEMORY_LIMIT = ENV['ENABLE_PER_PROCESS_AND_THREAD_MEMORY_LIMIT'] == "true"
27 | ALLOW_ENABLE_PER_PROCESS_AND_THREAD_MEMORY_LIMIT = ENV['ALLOW_ENABLE_PER_PROCESS_AND_THREAD_MEMORY_LIMIT'] != "false"
28 | MAX_FILE_SIZE = (ENV['MAX_FILE_SIZE'].presence || 1024).to_i
29 | MAX_MAX_FILE_SIZE = (ENV['MAX_MAX_FILE_SIZE'].presence || 4096).to_i
30 | NUMBER_OF_RUNS = (ENV['NUMBER_OF_RUNS'].presence || 1).to_i
31 | MAX_NUMBER_OF_RUNS = (ENV['MAX_NUMBER_OF_RUNS'].presence || 20).to_i
32 | REDIRECT_STDERR_TO_STDOUT = ENV['REDIRECT_STDERR_TO_STDOUT'] == "true"
33 | MAX_EXTRACT_SIZE = (ENV['MAX_EXTRACT_SIZE'].presence || 10240).to_i
34 | MAX_SUBMISSION_BATCH_SIZE = (ENV['MAX_SUBMISSION_BATCH_SIZE'].presence || 20).to_i
35 |
36 | def self.config_info
37 | @@default_confg ||= {
38 | "maintenance_mode": MAINTENANCE_MODE,
39 | "enable_wait_result": ENABLE_WAIT_RESULT,
40 | "enable_compiler_options": ENABLE_COMPILER_OPTIONS,
41 | "allowed_languages_for_compile_options": ALLOWED_LANGUAGES_FOR_COMPILER_OPTIONS,
42 | "enable_command_line_arguments": ENABLE_COMMAND_LINE_ARGUMENTS,
43 | "enable_submission_delete": ENABLE_SUBMISSION_DELETE,
44 | "max_queue_size": MAX_QUEUE_SIZE,
45 | "cpu_time_limit": CPU_TIME_LIMIT,
46 | "max_cpu_time_limit": MAX_CPU_TIME_LIMIT,
47 | "cpu_extra_time": CPU_EXTRA_TIME,
48 | "max_cpu_extra_time": MAX_CPU_EXTRA_TIME,
49 | "wall_time_limit": WALL_TIME_LIMIT,
50 | "max_wall_time_limit": MAX_WALL_TIME_LIMIT,
51 | "memory_limit": MEMORY_LIMIT,
52 | "max_memory_limit": MAX_MEMORY_LIMIT,
53 | "stack_limit": STACK_LIMIT,
54 | "max_stack_limit": MAX_STACK_LIMIT,
55 | "max_processes_and_or_threads": MAX_PROCESSES_AND_OR_THREADS,
56 | "max_max_processes_and_or_threads": MAX_MAX_PROCESSES_AND_OR_THREADS,
57 | "enable_per_process_and_thread_time_limit": ENABLE_PER_PROCESS_AND_THREAD_TIME_LIMIT,
58 | "allow_enable_per_process_and_thread_time_limit": ALLOW_ENABLE_PER_PROCESS_AND_THREAD_TIME_LIMIT,
59 | "enable_per_process_and_thread_memory_limit": ENABLE_PER_PROCESS_AND_THREAD_MEMORY_LIMIT,
60 | "allow_enable_per_process_and_thread_memory_limit": ALLOW_ENABLE_PER_PROCESS_AND_THREAD_MEMORY_LIMIT,
61 | "max_file_size": MAX_FILE_SIZE,
62 | "max_max_file_size": MAX_MAX_FILE_SIZE,
63 | "number_of_runs": NUMBER_OF_RUNS,
64 | "max_number_of_runs": MAX_NUMBER_OF_RUNS,
65 | "redirect_stderr_to_stdout": REDIRECT_STDERR_TO_STDOUT,
66 | "max_extract_size": MAX_EXTRACT_SIZE,
67 | "max_submission_batch_size": MAX_SUBMISSION_BATCH_SIZE
68 | }
69 | end
70 | end
71 |
--------------------------------------------------------------------------------
/docs/api/submissions/submissions.md:
--------------------------------------------------------------------------------
1 | ## Group Submissions
2 | ## Submission [/submissions/{token}{?base64_encoded,wait,fields,page,per_page}]
3 | Submission represents a model for running source code in specific programming language under
4 | certain runtime constraints.
5 |
6 | Submission model has **29** attributes. Attributes **1**-**16** (listed below) are used when creating new submissions - you can set them. Out of those 16 attributes only **2** are required - `source_code` and `language_id`. Attributes **17**-**29** represent detailed information about runtime of submission after it's execution.
7 |
8 | With attributes **7**-**16** you can configure submission runtime constraints. Those attributes are called *configuration attributes* or *configuration variables*. Please read more about them in [configuration](#system-and-configuration-configuration-info) section.
9 |
10 | Submission model has following attributes:
11 | |# |Name |Type |Unit |Description |Default Value |
12 | |:---:|:----|:----:|:----:|:-----------|:-------------|
13 | |1|**`source_code`**|text||Program's source code.|No default. This attribute is **required**.|
14 | |2|**`language_id`**|integer||[Language](#statuses-and-languages-language) ID.|No default. This attribute is **required**|
15 | |3|`compiler_options`|string (max. 128 chars)||Options for the compiler (i.e. compiler flags).|`null`|
16 | |4|`command_line_arguments`|string (max. 128 chars)||Command line arguments for the program.|`null`|
17 | |5|`stdin`|text||Input for program.|`null`. Program won't receive anything to standard input.|
18 | |6|`expected_output`|text||Expected output of program. Used when you want to compare with `stdout`.|`null`. Program's `stdout` won't be compared with `expected_output`.|
19 | |7|`cpu_time_limit`|float|second|Default runtime limit for every program. Time in which the OS assigns the processor to different tasks is not counted.|Depends on [configuration](#system-and-configuration-configuration-info).|
20 | |8|`cpu_extra_time`|float|second|When a time limit is exceeded, wait for extra time, before killing the program. This has the advantage that the real execution time is reported, even though it slightly exceeds the limit.|Depends on [configuration](#system-and-configuration-configuration-info).|
21 | |9|`wall_time_limit`|float|second|Limit wall-clock time in seconds. Decimal numbers are allowed. This clock measures the time from the start of the program to its exit, so it does not stop when the program has lost the CPU or when it is waiting for an external event. We recommend to use `cpu_time_limit` as the main limit, but set `wall_time_limit` to a much higher value as a precaution against sleeping programs.|Depends on [configuration](#system-and-configuration-configuration-info).|
22 | |10|`memory_limit`|float|kilobyte|Limit address space of the program.|Depends on [configuration](#system-and-configuration-configuration-info).|
23 | |11|`stack_limit`|integer|kilobyte|Limit process stack.|Depends on [configuration](#system-and-configuration-configuration-info).|
24 | |12|`max_processes_and_or_threads`|integer||Maximum number of processes and/or threads program can create.|Depends on [configuration](#system-and-configuration-configuration-info).|
25 | |13|`enable_per_process_and_thread_time_limit`|boolean||If `true` then `cpu_time_limit` will be used as per process and thread.|Depends on [configuration](#system-and-configuration-configuration-info).|
26 | |14|`enable_per_process_and_thread_memory_limit`|boolean||If `true` then `memory_limit` will be used as per process and thread.|Depends on [configuration](#system-and-configuration-configuration-info).|
27 | |15|`max_file_size`|integer|kilobyte|Limit file size created or modified by the program.|Depends on [configuration](#system-and-configuration-configuration-info).|
28 | |16|`number_of_runs`|integer||Run each program `number_of_runs` times and take average of `time` and `memory`.|Depends on [configuration](#system-and-configuration-configuration-info).|
29 | |17|`stdout`|text||Standard output of the program after execution.||
30 | |18|`stderr`|text||Standard error of the program after execution.||
31 | |19|`compile_output`|text||Compiler output after compilation.||
32 | |20|`message`|text||If submission status is `Internal Error` then this message comes from Judge0 API itself, otherwise this is status message from Isolate.||
33 | |21|`exit_code`|integer||The program's exit code.||
34 | |22|`exit_signal`|integer||Signal code that the program recieved before exiting.||
35 | |23|`status`|object||Submission [status](#statuses-and-languages-status).||
36 | |24|`created_at`|datetime||Date and time when submission was created.||
37 | |25|`finished_at`|datetime||Date and time when submission was processed.|`null` if submission is still in queue or if submission is processing.|
38 | |26|`token`|string||Unique submission token used for [getting specific submission](#submission-submission-get).||
39 | |27|`time`|float|second|Program's runtime.||
40 | |28|`wall_time`|float|second|Program's wall time. Will be greater or equal to `time`.||
41 | |29|`memory`|float|kilobyte|Memory used by the program after execution.||
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Judge0 API
2 | [](https://api.judge0.com)
3 |
4 | [](https://github.com/judge0/api/blob/master/LICENSE)
5 | [](https://github.com/judge0/api/releases)
6 | [](https://github.com/judge0/api/stargazers)
7 |
8 |
9 |
10 |
11 |
12 | ## About
13 | [**Judge0 API**](https://api.judge0.com) is a free, robust and scalable open-source online code execution system.
14 |
15 | Judge0 API can help you build wide range of applications varying from competitive programming platforms, educational and recruitment platforms, to online code editors and more.
16 |
17 | To see Judge0 API in action, try [**Judge0 IDE**](https://ide.judge0.com) - free and open-source code editor that uses Judge0 API for executing the user’s source code. You can also try using a [dummy client](https://api.judge0.com/dummy-client.html), which can help you explore and test almost all the features of Judge0 API.
18 |
19 | ## Documentation
20 | Detailed API documentation can be found at https://api.judge0.com.
21 |
22 | ## Supported Languages
23 | List of supported languages for the latest release can be found [here](https://jsonpp.judge0.com/?https://api.judge0.com/languages).
24 |
25 | ## Installation
26 | To install Judge0 API on your server follow [deployment procedure](https://github.com/judge0/api/blob/master/CHANGELOG.md#deployment-procedure) for the latest release.
27 |
28 | ## Judge0 API in The World
29 | These projects and organizations successfully use Judge0 API:
30 | - [Adzetech](https://adzetech.com)
31 | - [CodeForum.ORG](https://codeforum.org)
32 | - [CodeInn](https://codeinn.org)
33 | - [CodeRunner](https://github.com/codeclassroom/CodeRunner)
34 | - [Codify](https://codify.herokuapp.com)
35 | - [Dilla University](http://www.duvlab.website)
36 | - [GeekCoders](http://www.geekcoders.co.in)
37 | - [Georg-August University of Göttingen](https://www.uni-goettingen.de)
38 | - [Institute of Aeronautical Engineering](https://www.iare.ac.in)
39 | - [Judge Code Online](http://en.chamcode.net)
40 | - [KPR Institute of Engineering and Technology](https://www.kpriet.ac.in)
41 | - [KodNest](https://www.kodnest.com)
42 | - [Newton School](https://www.newtonschool.co)
43 | - [SparkDatabox](https://sparkdatabox.com)
44 | - [Studyfied](https://studyfied.com)
45 | - [Techie Delight](https://techiedelight.com)
46 | - [Think exam](https://www.thinkexam.com)
47 | - [ThinkingBell](https://www.thinkingbell.com)
48 | - [University of Zagreb, Faculty of Electrical Engineering and Computing](https://www.fer.unizg.hr/en)
49 | - [Xiith](https://xiith.com)
50 | - [judge0api](https://github.com/vCra/judge0api) - Python library for interacting with Judge0 API
51 | - [nGrader](https://ngrader.herokuapp.com)
52 | - [primat.org](http://primat.org)
53 | - [wkspace](http://wkspace.herokuapp.com)
54 |
55 | Feel free to add your project or organization to the list.
56 |
57 | ## Community
58 | Do you have a question, feature request or something else on your mind?
59 | Or you just want to follow Judge0 news?
60 | Check out these links:
61 |
62 | * [Subscribe to Judge0 news](https://judge0.com/#subscribe)
63 | * [Join a Discord server](https://discord.gg/6dvxeA8)
64 | * [Report an issue](https://github.com/judge0/api/issues/new)
65 | * [Contact the author](https://github.com/hermanzdosilovic)
66 |
67 | ## Author and Contributors
68 | Judge0 API was created by [Herman Zvonimir Došilović](https://github.com/hermanzdosilovic).
69 |
70 | Thanks a lot to all [contributors](https://github.com/judge0/api/graphs/contributors) for their contributions in this project.
71 |
72 | ## Special Thanks
73 | Special thanks to these open-source projects without whom Judge0 API probably wouldn't exist: [isolate](https://github.com/ioi/isolate), [Docker](https://github.com/docker), [Ruby on Rails](https://github.com/rails/rails) and others.
74 |
75 | ## Supporters
76 | Thanks a lot to all my [Patrons](https://www.patreon.com/hermanzdosilovic) and [PayPal](https://paypal.me/hermanzdosilovic) donors that helped me keep Judge0 API free for everybody around the World. Thank you ♥
77 |
78 | ## Donate
79 | Your are more than welcome to support Judge0 on [Patreon](https://www.patreon.com/hermanzdosilovic) or via [PayPal](https://paypal.me/hermanzdosilovic). Your monthly or one-time donation will help pay server costs and will improve future development and maintenance. Thank you ♥
80 |
81 | ## License
82 | Judge0 API is licensed under the [GNU General Public License v3.0](https://github.com/judge0/api/blob/master/LICENSE).
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | actioncable (5.0.7.2)
5 | actionpack (= 5.0.7.2)
6 | nio4r (>= 1.2, < 3.0)
7 | websocket-driver (~> 0.6.1)
8 | actionmailer (5.0.7.2)
9 | actionpack (= 5.0.7.2)
10 | actionview (= 5.0.7.2)
11 | activejob (= 5.0.7.2)
12 | mail (~> 2.5, >= 2.5.4)
13 | rails-dom-testing (~> 2.0)
14 | actionpack (5.0.7.2)
15 | actionview (= 5.0.7.2)
16 | activesupport (= 5.0.7.2)
17 | rack (~> 2.0)
18 | rack-test (~> 0.6.3)
19 | rails-dom-testing (~> 2.0)
20 | rails-html-sanitizer (~> 1.0, >= 1.0.2)
21 | actionview (5.0.7.2)
22 | activesupport (= 5.0.7.2)
23 | builder (~> 3.1)
24 | erubis (~> 2.7.0)
25 | rails-dom-testing (~> 2.0)
26 | rails-html-sanitizer (~> 1.0, >= 1.0.3)
27 | active_model_serializers (0.10.10)
28 | actionpack (>= 4.1, < 6.1)
29 | activemodel (>= 4.1, < 6.1)
30 | case_transform (>= 0.2)
31 | jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
32 | activejob (5.0.7.2)
33 | activesupport (= 5.0.7.2)
34 | globalid (>= 0.3.6)
35 | activemodel (5.0.7.2)
36 | activesupport (= 5.0.7.2)
37 | activerecord (5.0.7.2)
38 | activemodel (= 5.0.7.2)
39 | activesupport (= 5.0.7.2)
40 | arel (~> 7.0)
41 | activesupport (5.0.7.2)
42 | concurrent-ruby (~> 1.0, >= 1.0.2)
43 | i18n (>= 0.7, < 2)
44 | minitest (~> 5.1)
45 | tzinfo (~> 1.1)
46 | annotate (3.0.3)
47 | activerecord (>= 3.2, < 7.0)
48 | rake (>= 10.4, < 14.0)
49 | arel (7.1.4)
50 | builder (3.2.4)
51 | byebug (11.0.1)
52 | case_transform (0.2)
53 | activesupport
54 | coderay (1.1.2)
55 | concurrent-ruby (1.1.5)
56 | crass (1.0.5)
57 | enumerations (2.3.3)
58 | activerecord
59 | activesupport
60 | i18n
61 | erubis (2.7.0)
62 | ffi (1.11.3)
63 | globalid (0.4.2)
64 | activesupport (>= 4.2.0)
65 | httparty (0.17.3)
66 | mime-types (~> 3.0)
67 | multi_xml (>= 0.5.2)
68 | i18n (1.8.1)
69 | concurrent-ruby (~> 1.0)
70 | jsonapi-renderer (0.2.2)
71 | listen (3.2.1)
72 | rb-fsevent (~> 0.10, >= 0.10.3)
73 | rb-inotify (~> 0.9, >= 0.9.10)
74 | loofah (2.4.0)
75 | crass (~> 1.0.2)
76 | nokogiri (>= 1.5.9)
77 | mail (2.7.1)
78 | mini_mime (>= 0.1.1)
79 | method_source (0.9.2)
80 | mime-types (3.3.1)
81 | mime-types-data (~> 3.2015)
82 | mime-types-data (3.2019.1009)
83 | mini_mime (1.0.2)
84 | mini_portile2 (2.4.0)
85 | minitest (5.13.0)
86 | mono_logger (1.1.0)
87 | multi_json (1.14.1)
88 | multi_xml (0.6.0)
89 | mustermann (1.1.1)
90 | ruby2_keywords (~> 0.0.1)
91 | nio4r (2.5.2)
92 | nokogiri (1.10.7)
93 | mini_portile2 (~> 2.4.0)
94 | pg (1.2.2)
95 | pry (0.12.2)
96 | coderay (~> 1.1.0)
97 | method_source (~> 0.9.0)
98 | pry-byebug (3.7.0)
99 | byebug (~> 11.0)
100 | pry (~> 0.10)
101 | pry-rails (0.3.9)
102 | pry (>= 0.10.4)
103 | puma (4.3.1)
104 | nio4r (~> 2.0)
105 | rack (2.1.1)
106 | rack-cors (1.1.1)
107 | rack (>= 2.0.0)
108 | rack-protection (2.0.8.1)
109 | rack
110 | rack-test (0.6.3)
111 | rack (>= 1.0)
112 | rails (5.0.7.2)
113 | actioncable (= 5.0.7.2)
114 | actionmailer (= 5.0.7.2)
115 | actionpack (= 5.0.7.2)
116 | actionview (= 5.0.7.2)
117 | activejob (= 5.0.7.2)
118 | activemodel (= 5.0.7.2)
119 | activerecord (= 5.0.7.2)
120 | activesupport (= 5.0.7.2)
121 | bundler (>= 1.3.0)
122 | railties (= 5.0.7.2)
123 | sprockets-rails (>= 2.0.0)
124 | rails-dom-testing (2.0.3)
125 | activesupport (>= 4.2.0)
126 | nokogiri (>= 1.6)
127 | rails-html-sanitizer (1.3.0)
128 | loofah (~> 2.3)
129 | railties (5.0.7.2)
130 | actionpack (= 5.0.7.2)
131 | activesupport (= 5.0.7.2)
132 | method_source
133 | rake (>= 0.8.7)
134 | thor (>= 0.18.1, < 2.0)
135 | rake (13.0.1)
136 | rb-fsevent (0.10.3)
137 | rb-inotify (0.10.1)
138 | ffi (~> 1.0)
139 | redis (4.1.3)
140 | redis-namespace (1.7.0)
141 | redis (>= 3.0.4)
142 | resque (2.0.0)
143 | mono_logger (~> 1.0)
144 | multi_json (~> 1.0)
145 | redis-namespace (~> 1.6)
146 | sinatra (>= 0.9.2)
147 | vegas (~> 0.1.2)
148 | ruby2_keywords (0.0.2)
149 | sinatra (2.0.8.1)
150 | mustermann (~> 1.0)
151 | rack (~> 2.0)
152 | rack-protection (= 2.0.8.1)
153 | tilt (~> 2.0)
154 | sprockets (4.0.0)
155 | concurrent-ruby (~> 1.0)
156 | rack (> 1, < 3)
157 | sprockets-rails (3.2.1)
158 | actionpack (>= 4.0)
159 | activesupport (>= 4.0)
160 | sprockets (>= 3.0.0)
161 | thor (1.0.1)
162 | thread_safe (0.3.6)
163 | tilt (2.0.10)
164 | tzinfo (1.2.6)
165 | thread_safe (~> 0.1)
166 | vegas (0.1.11)
167 | rack (>= 1.0.0)
168 | websocket-driver (0.6.5)
169 | websocket-extensions (>= 0.1.0)
170 | websocket-extensions (0.1.4)
171 | will_paginate (3.2.1)
172 |
173 | PLATFORMS
174 | ruby
175 |
176 | DEPENDENCIES
177 | active_model_serializers (~> 0.10.10)
178 | annotate (~> 3.0.3)
179 | enumerations (~> 2.3.3)
180 | httparty (~> 0.17.3)
181 | listen (~> 3.2.1)
182 | pg (~> 1.2.2)
183 | pry-byebug (~> 3.7.0)
184 | pry-rails (~> 0.3.9)
185 | puma (~> 4.3.1)
186 | rack-cors (~> 1.1.1)
187 | rails (~> 5.0.0)
188 | resque (~> 2.0.0)
189 | will_paginate (~> 3.2.1)
190 |
191 | BUNDLED WITH
192 | 2.1.4
193 |
--------------------------------------------------------------------------------
/docs/api/submissions/create_a_submission.md:
--------------------------------------------------------------------------------
1 | ### Create a Submission [POST]
2 | Creates new submission. Created submission waits in queue to be processed. On successful
3 | creation, you are returned submission token which can be used to check submission status.
4 |
5 | If submission's `source_code`, `stdin` or `expected_output` contains non printable characters, or
6 | characters which cannot be sent with JSON, then set `base64_encoded` parameter to `true` and
7 | send these attributes Base64 encoded. Your responsibility is to encode each of mentioned attributes
8 | (`source_code`, `stdin` and `expected_output`) even if just one of them contains non printable
9 | characters. By default, this parameter is set to `false` and Judge0 API assumes you are sending plain text data.
10 |
11 | By default you are returned submission token on successful submission creation. With this token
12 | you can [check submission status](#submission-submission-get).
13 | Instead of checking submission status by making another request, send `wait` query parameter and set it to `true`
14 | which will enable you to get submission status immediately as part of response to the request you made.
15 | Please note that this feature may or may not be
16 | enabled on all Judge0 API hosts. So before using this feature please check [configuration](#system-and-configuration-configuration-info-get)
17 | of Judge0 API you are using. On an [official Judge0 API](https://api.judge0.com) this feature **is enabled**.
18 |
19 | + Parameters
20 | + base64_encoded = `false` (optional, boolean, `false`) ... Set to `true` if you want to send Base64 encoded data to Judge0 API.
21 | + wait = `false` (optional, boolean, `false`) ... Set to `true` to immediately get submission result.
22 |
23 | + Request (application/json)
24 | {
25 | "source_code": "#include \n\nint main(void) {\n char name[10];\n scanf(\"%s\", name);\n printf(\"hello, %s\\n\", name);\n return 0;\n}",
26 | "language_id": 4,
27 | "stdin": "world"
28 | }
29 |
30 | + Response 201 (application/json)
31 | {
32 | "token": "d85cd024-1548-4165-96c7-7bc88673f194"
33 | }
34 |
35 |
36 | + Request (application/json)
37 | {
38 | "source_code": "#include \n\nint main(void) {\n char name[10];\n scanf(\"%s\", name);\n printf(\"hello, %s\\n\", name);\n return 0;\n}"
39 | }
40 |
41 | + Response 422 (application/json)
42 | {
43 | "language_id": [
44 | "can't be blank"
45 | ]
46 | }
47 |
48 |
49 | + Request (application/json)
50 | {
51 | "source_code": "#include \n\nint main(void) {\n char name[10];\n scanf(\"%s\", name);\n printf(\"hello, %s\\n\", name);\n return 0;\n}",
52 | "language_id": 150000,
53 | "stdin": "world",
54 | "expected_output": "hello, world"
55 | }
56 |
57 | + Response 422 (application/json)
58 | {
59 | "language_id": [
60 | "language with id 150000 doesn't exist"
61 | ]
62 | }
63 |
64 |
65 | + Request (application/json)
66 | {
67 | "source_code": "#include \n\nint main(void) {\n char name[10];\n scanf(\"%s\", name);\n printf(\"hello, %s\\n\", name);\n return 0;\n}",
68 | "language_id": 4,
69 | "number_of_runs": 1,
70 | "stdin": "Judge0",
71 | "expected_output": "hello, Judge0",
72 | "cpu_time_limit": 1,
73 | "cpu_extra_time": 0.5,
74 | "wall_time_limit": 100000,
75 | "memory_limit": 128000,
76 | "stack_limit": 128000,
77 | "enable_per_process_and_thread_time_limit": false,
78 | "enable_per_process_and_thread_memory_limit": false,
79 | "max_file_size": 1024
80 | }
81 |
82 | + Response 422 (application/json)
83 | {
84 | "wall_time_limit": [
85 | "must be less than or equal to 150"
86 | ]
87 | }
88 |
89 |
90 | + Request (appliction/json)
91 | Sending Base64 encoded `source_code` and `stdin`. Note that in this request `base64_encoded` query parameter **must** be
92 | set to `true`.
93 | + Body
94 | {
95 | "source_code": "I2luY2x1ZGUgPHN0ZGlvLmg+CgppbnQgbWFpbih2b2lkKSB7CiAgY2hhciBuYW1lWzEwXTsKICBzY2FuZigiJXMiLCBuYW1lKTsKICBwcmludGYoImhlbGxvLCAlc1xuIiwgbmFtZSk7CiAgcmV0dXJuIDA7Cn0=",
96 | "language_id": 4,
97 | "input": "SnVkZ2Uw"
98 | }
99 |
100 | + Response 201 (application/json)
101 | {
102 | "token": "f3fe0215-72f3-4fe6-97f5-353df6682db4"
103 | }
104 |
105 | + Request (application/json)
106 | Creating a submission with `wait=true` that results with one or more attributes that cannot be serialized to JSON without Base64 encoding.
107 | + Body
108 | {
109 | "language_id": 70,
110 | "source_code": "print(\"\\xFE\")"
111 | }
112 |
113 | + Response 201 (application/json)
114 | {
115 | "token": "fcd0de6d-ee52-4a9d-8a00-6e0d98d394cf",
116 | "error": "some attributes for this submission cannot be converted to UTF-8, use base64_encoded=true query parameter"
117 | }
118 |
119 | + Request (application/json)
120 | Waiting for submission to finish. Note that in this request `wait` query parameter **must** be set to `true`.
121 | + Body
122 | {
123 | "source_code": "#include \n\nint main(void) {\n char name[10];\n scanf(\"%s\", name);\n printf(\"hello, %s\\n\", name);\n return 0;\n}",
124 | "language_id": "4",
125 | "stdin": "Judge0",
126 | "expected_output": "hello, Judge0"
127 | }
128 |
129 | + Response 201 (application/json)
130 | {
131 | "stdout": "hello, Judge0\n",
132 | "time": "0.001",
133 | "memory": 380,
134 | "stderr": null,
135 | "token": "eb0dd001-66db-47f4-8a69-b736c9bc23f6",
136 | "compile_output": null,
137 | "message": null,
138 | "status": {
139 | "id": 3,
140 | "description": "Accepted"
141 | }
142 | }
143 |
144 | + Response 400 (application/json)
145 | If wait is not allowed.
146 | + Body
147 | {
148 | "error": "wait not allowed"
149 | }
150 |
151 | + Response 503 (application/json)
152 | If submission queue is full.
153 | + Body
154 | {
155 | "error": "queue is full"
156 | }
157 |
158 |
159 |
--------------------------------------------------------------------------------
/docs/api/system_and_configuration/configuration_info.md:
--------------------------------------------------------------------------------
1 | ## Configuration Info [/config_info]
2 | ## Configuration Info [GET]
3 | Configuration information gives you detailed information about configuration of Judge0 API.
4 | This configuration can be changed through [judge0-api.conf](https://github.com/judge0/api/blob/master/judge0-api.conf.default)
5 | file by admin who hosts Judge0 API instance.
6 |
7 | This configuration gives every admin a flexibility to configure Judge0 API according to server abilities and needs. It also gives users
8 | insight on some *default configuration values* which are used when their programs are run.
9 |
10 | Each of these *configuration variables* have *default values* which we consider as recommended in case you are not sure should you change them.
11 |
12 | We will refer to *default values* as values which Judge0 API automatically assigns to each of these *configuration variables*,
13 | if admin didn't set them. For example, default value of *configuration variable* `cpu_time_limit` is `2`.
14 |
15 | |# |Name|Type |Unit |Description|Default Value|
16 | |:---:|:---|:---:|:---:|:----------|:------------|
17 | |1|`enable_wait_result`|boolean||If enabled user can request to synchronously wait for submission result on submission create.|true|
18 | |2|`enable_compiler_options`|boolean||If enabled user can set `compiler_options`.|true|
19 | |3|`allowed_languages_for_compile_options`|list of strings||Languages for which setting compiler options is allowed.|Empty, i.e. for all languages it is allowed to set compiler options.|
20 | |4|`enable_command_line_arguments`|boolean||If enabled user can set `command_line_arguments`.|true|
21 | |5|`enable_submission_delete`|boolean||If enabled authorized user can [delete a submission](#submissions-submission-delete).|false|
22 | |6|`max_queue_size`|integer||Maximum number of submissions that can wait in queue.|100|
23 | |7|`cpu_time_limit`|float|second|Default runtime limit for every program (in seconds). Decimal numbers are allowed. Time in which the OS assigns the processor to different tasks is not counted.|2|
24 | |8|`cpu_extra_time`|float|second|When a time limit is exceeded, wait for extra time, before killing the program. This has the advantage that the real execution time is reported, even though it slightly exceeds the limit.|0.5|
25 | |9|`wall_time_limit`|float|second|Limit wall-clock time in seconds. Decimal numbers are allowed. This clock measures the time from the start of the program to its exit, for an external event. We recommend to use `cpu_time_limit` as the main limit, but set `wall_time_limit` to a much higher value as a precaution against sleeping programs.|5|
26 | |10|`memory_limit`|integer|kilobyte|Limit address space of the program in kilobytes.|128000|
27 | |11|`stack_limit`|integer|kilobyte|Limit process stack in kilobytes.|64000|
28 | |12|`max_processes_and_or_threads`|integer||Maximum number of processes and/or threads program can create.|60|
29 | |13|`enable_per_process_and_thread_time_limit`|boolean||If `true` then `cpu_time_limit` will be used as per process and thread.|false|
30 | |14|`enable_per_process_and_thread_memory_limit`|boolean||If `true` then `memory_limit` will be used as per process and thread.|true|
31 | |15|`max_file_size`|integer|kilobyte|Limit size of files created (or modified) by the program.|1024|
32 | |16|`number_of_runs`|integer||Run each program this many times and take average of time and memory.|1|
33 |
34 | *Default configuration value* for each variable is given to you as response of this API call. For example, *default configuration value*
35 | for variable `cpu_extra_time` might be `2`, and if admin didn't set this, then it is `0.5` (*default value*).
36 | This means that admin set `cpu_extra_time` *configuration variable* to value `2` and we say it is now *default configuration value* for this
37 | variable `cpu_extra_time`.
38 |
39 | Every [submission](#submissions-submission) can change each of the configuration variables according to its needs. For example,
40 | user might create submission which has `cpu_time_limit` of `5` seconds. For security reasons we need to limit values of each of these
41 | configuration variables. For example, we don't want user to create a submission which has `cpu_time_limit` of `100000` seconds.
42 |
43 | For this security reason we are introducing *limit configuration variables* for each *configuration variable*.
44 |
45 | |# |Name|Type |Unit |Description|Default Value|
46 | |:---:|:---|:---:|:---:|:----------|:------------|
47 | |1|`max_cpu_time_limit`|float|second|Maximum custom `cpu_time_limit`|15|
48 | |2|`max_cpu_extra_time`|float|second|Maximum custom `cpu_extra_time`|2|
49 | |3|`max_wall_time_limit`|float|second|Maximum custom `wall_time_limit`|20|
50 | |4|`max_memory_limit`|integer|kilobyte|Maximum custom `memory_limit`|256000|
51 | |5|`max_stack_limit`|integer|kilobyte|Maximum custom `stack_limit`|128000|
52 | |6|`max_max_processes_and_or_threads`|integer||Maximum custom `max_processes_and_or_threads`|120|
53 | |7|`allow_enable_per_process_and_thread_time_limit`|boolean||If `false` user won't be able to set `enable_per_process_and_thread_time_limit` to `true`|true|
54 | |8|`allow_enable_per_process_and_thread_memory_limit`|boolean||If `false` user won't be able to set `enable_per_process_and_thread_memory_limit` to `true`|true|
55 | |9|`max_max_file_size`|integer|kilobyte|Maximux custom `max_file_size`|4096|
56 | |10|`max_number_of_runs`|integer||Maximum custom `number_of_runs`|20|
57 |
58 | For example, `max_cpu_time_limit` with value `20` means that user cannot create new submission which has `cpu_time_limit` greater than `20`.
59 |
60 | + Response 200 (application/json)
61 | {
62 | "enable_wait_result": true,
63 | "enable_compiler_options": true,
64 | "allowed_languages_for_compile_options": [],
65 | "enable_command_line_arguments": true,
66 | "enable_submission_delete": false,
67 | "max_queue_size": 100,
68 | "cpu_time_limit": 2,
69 | "max_cpu_time_limit": 15,
70 | "cpu_extra_time": 0.5,
71 | "max_cpu_extra_time": 2,
72 | "wall_time_limit": 5,
73 | "max_wall_time_limit": 20,
74 | "memory_limit": 128000,
75 | "max_memory_limit": 256000,
76 | "stack_limit": 64000,
77 | "max_stack_limit": 128000,
78 | "max_processes_and_or_threads": 60,
79 | "max_max_processes_and_or_threads": 120,
80 | "enable_per_process_and_thread_time_limit": false,
81 | "allow_enable_per_process_and_thread_time_limit": true,
82 | "enable_per_process_and_thread_memory_limit": true,
83 | "allow_enable_per_process_and_thread_memory_limit": true,
84 | "max_file_size": 1024,
85 | "max_max_file_size": 4096,
86 | "number_of_runs": 1,
87 | "max_number_of_runs": 20
88 | }
--------------------------------------------------------------------------------
/db/languages/active.rb:
--------------------------------------------------------------------------------
1 | @languages ||= []
2 | @languages +=
3 | [
4 | {
5 | id: 43,
6 | name: "Plain Text",
7 | is_archived: false,
8 | source_file: "text.txt",
9 | run_cmd: "/bin/cat text.txt"
10 | },
11 | {
12 | id: 44,
13 | name: "Executable",
14 | is_archived: false,
15 | source_file: "a.out",
16 | run_cmd: "/bin/chmod +x a.out && ./a.out"
17 | },
18 | {
19 | id: 45,
20 | name: "Assembly (NASM 2.14.02)",
21 | is_archived: false,
22 | source_file: "main.asm",
23 | compile_cmd: "/usr/local/nasm-2.14.02/bin/nasmld -f elf64 %s main.asm",
24 | run_cmd: "./a.out"
25 | },
26 | {
27 | id: 46,
28 | name: "Bash (5.0.0)",
29 | is_archived: false,
30 | source_file: "script.sh",
31 | run_cmd: "/usr/local/bash-5.0/bin/bash script.sh"
32 | },
33 | {
34 | id: 47,
35 | name: "Basic (FBC 1.07.1)",
36 | is_archived: false,
37 | source_file: "main.bas",
38 | compile_cmd: "/usr/local/fbc-1.07.1/bin/fbc %s main.bas",
39 | run_cmd: "./main"
40 | },
41 | {
42 | id: 48,
43 | name: "C (GCC 7.4.0)",
44 | is_archived: false,
45 | source_file: "main.c",
46 | compile_cmd: "/usr/local/gcc-7.4.0/bin/gcc %s main.c",
47 | run_cmd: "./a.out"
48 | },
49 | {
50 | id: 49,
51 | name: "C (GCC 8.3.0)",
52 | is_archived: false,
53 | source_file: "main.c",
54 | compile_cmd: "/usr/local/gcc-8.3.0/bin/gcc %s main.c",
55 | run_cmd: "./a.out"
56 | },
57 | {
58 | id: 50,
59 | name: "C (GCC 9.2.0)",
60 | is_archived: false,
61 | source_file: "main.c",
62 | compile_cmd: "/usr/local/gcc-9.2.0/bin/gcc %s main.c",
63 | run_cmd: "./a.out"
64 | },
65 | {
66 | id: 51,
67 | name: "C# (Mono 6.6.0.161)",
68 | is_archived: false,
69 | source_file: "Main.cs",
70 | compile_cmd: "/usr/local/mono-6.6.0.161/bin/mcs %s Main.cs",
71 | run_cmd: "/usr/local/mono-6.6.0.161/bin/mono Main.exe"
72 | },
73 | {
74 | id: 52,
75 | name: "C++ (GCC 7.4.0)",
76 | is_archived: false,
77 | source_file: "main.cpp",
78 | compile_cmd: "/usr/local/gcc-7.4.0/bin/g++ %s main.cpp",
79 | run_cmd: "LD_LIBRARY_PATH=/usr/local/gcc-7.4.0/lib64 ./a.out"
80 | },
81 | {
82 | id: 53,
83 | name: "C++ (GCC 8.3.0)",
84 | is_archived: false,
85 | source_file: "main.cpp",
86 | compile_cmd: "/usr/local/gcc-8.3.0/bin/g++ %s main.cpp",
87 | run_cmd: "LD_LIBRARY_PATH=/usr/local/gcc-8.3.0/lib64 ./a.out"
88 | },
89 | {
90 | id: 54,
91 | name: "C++ (GCC 9.2.0)",
92 | is_archived: false,
93 | source_file: "main.cpp",
94 | compile_cmd: "/usr/local/gcc-9.2.0/bin/g++ %s main.cpp",
95 | run_cmd: "LD_LIBRARY_PATH=/usr/local/gcc-9.2.0/lib64 ./a.out"
96 | },
97 | {
98 | id: 55,
99 | name: "Common Lisp (SBCL 2.0.0)",
100 | is_archived: false,
101 | source_file: "script.lisp",
102 | run_cmd: "SBCL_HOME=/usr/local/sbcl-2.0.0/lib/sbcl /usr/local/sbcl-2.0.0/bin/sbcl --script script.lisp"
103 | },
104 | {
105 | id: 56,
106 | name: "D (DMD 2.089.1)",
107 | is_archived: false,
108 | source_file: "main.d",
109 | compile_cmd: "/usr/local/d-2.089.1/linux/bin64/dmd %s main.d",
110 | run_cmd: "./main"
111 | },
112 | {
113 | id: 57,
114 | name: "Elixir (1.9.4)",
115 | is_archived: false,
116 | source_file: "script.exs",
117 | run_cmd: "/usr/local/elixir-1.9.4/bin/elixir script.exs"
118 | },
119 | {
120 | id: 58,
121 | name: "Erlang (OTP 22.2)",
122 | is_archived: false,
123 | source_file: "main.erl",
124 | run_cmd: "/bin/sed -i '1s/^/\\n/' main.erl && /usr/local/erlang-22.2/bin/escript main.erl"
125 | },
126 | {
127 | id: 59,
128 | name: "Fortran (GFortran 9.2.0)",
129 | is_archived: false,
130 | source_file: "main.f90",
131 | compile_cmd: "/usr/local/gcc-9.2.0/bin/gfortran %s main.f90",
132 | run_cmd: "LD_LIBRARY_PATH=/usr/local/gcc-9.2.0/lib64 ./a.out"
133 | },
134 | {
135 | id: 60,
136 | name: "Go (1.13.5)",
137 | is_archived: false,
138 | source_file: "main.go",
139 | compile_cmd: "GOCACHE=/tmp/.cache/go-build /usr/local/go-1.13.5/bin/go build %s main.go",
140 | run_cmd: "./main"
141 | },
142 | {
143 | id: 61,
144 | name: "Haskell (GHC 8.8.1)",
145 | is_archived: false,
146 | source_file: "main.hs",
147 | compile_cmd: "/usr/local/ghc-8.8.1/bin/ghc %s main.hs",
148 | run_cmd: "./main"
149 | },
150 | {
151 | id: 62,
152 | name: "Java (OpenJDK 13.0.1)",
153 | is_archived: false,
154 | source_file: "Main.java",
155 | compile_cmd: "/usr/local/openjdk13/bin/javac %s Main.java",
156 | run_cmd: "/usr/local/openjdk13/bin/java Main"
157 | },
158 | {
159 | id: 63,
160 | name: "JavaScript (Node.js 12.14.0)",
161 | is_archived: false,
162 | source_file: "script.js",
163 | run_cmd: "/usr/local/node-12.14.0/bin/node script.js"
164 | },
165 | {
166 | id: 64,
167 | name: "Lua (5.3.5)",
168 | is_archived: false,
169 | source_file: "script.lua",
170 | compile_cmd: "/usr/local/lua-5.3.5/luac53 %s script.lua",
171 | run_cmd: "/usr/local/lua-5.3.5/lua53 ./luac.out"
172 | },
173 | {
174 | id: 65,
175 | name: "OCaml (4.09.0)",
176 | is_archived: false,
177 | source_file: "main.ml",
178 | compile_cmd: "/usr/local/ocaml-4.09.0/bin/ocamlc %s main.ml",
179 | run_cmd: "./a.out"
180 | },
181 | {
182 | id: 66,
183 | name: "Octave (5.1.0)",
184 | is_archived: false,
185 | source_file: "script.m",
186 | run_cmd: "/usr/local/octave-5.1.0/bin/octave-cli -q --no-gui --no-history script.m"
187 | },
188 | {
189 | id: 67,
190 | name: "Pascal (FPC 3.0.4)",
191 | is_archived: false,
192 | source_file: "main.pas",
193 | compile_cmd: "/usr/local/fpc-3.0.4/bin/fpc %s main.pas",
194 | run_cmd: "./main"
195 | },
196 | {
197 | id: 68,
198 | name: "PHP (7.4.1)",
199 | is_archived: false,
200 | source_file: "script.php",
201 | run_cmd: "/usr/local/php-7.4.1/bin/php script.php"
202 | },
203 | {
204 | id: 69,
205 | name: "Prolog (GNU Prolog 1.4.5)",
206 | is_archived: false,
207 | source_file: "main.pro",
208 | compile_cmd: "PATH=\"/usr/local/gprolog-1.4.5/gprolog-1.4.5/bin:$PATH\" /usr/local/gprolog-1.4.5/gprolog-1.4.5/bin/gplc --no-top-level %s main.pro",
209 | run_cmd: "./main"
210 | },
211 | {
212 | id: 70,
213 | name: "Python (2.7.17)",
214 | is_archived: false,
215 | source_file: "script.py",
216 | run_cmd: "/usr/local/python-2.7.17/bin/python2 script.py"
217 | },
218 | {
219 | id: 71,
220 | name: "Python (3.8.1)",
221 | is_archived: false,
222 | source_file: "script.py",
223 | run_cmd: "/usr/local/python-3.8.1/bin/python3 script.py"
224 | },
225 | {
226 | id: 72,
227 | name: "Ruby (2.7.0)",
228 | is_archived: false,
229 | source_file: "script.rb",
230 | run_cmd: "/usr/local/ruby-2.7.0/bin/ruby script.rb"
231 | },
232 | {
233 | id: 73,
234 | name: "Rust (1.40.0)",
235 | is_archived: false,
236 | source_file: "main.rs",
237 | compile_cmd: "/usr/local/rust-1.40.0/bin/rustc %s main.rs",
238 | run_cmd: "./main"
239 | },
240 | {
241 | id: 74,
242 | name: "TypeScript (3.7.4)",
243 | is_archived: false,
244 | source_file: "script.ts",
245 | compile_cmd: "/usr/bin/tsc %s script.ts",
246 | run_cmd: "/usr/local/node-12.14.0/bin/node script.js"
247 | }
248 | ]
--------------------------------------------------------------------------------
/app/controllers/submissions_controller.rb:
--------------------------------------------------------------------------------
1 | class SubmissionsController < ApplicationController
2 | before_action :authorize_request, only: [:index, :destroy]
3 | before_action :check_maintenance, only: [:create, :destroy]
4 | before_action :check_wait, only: [:create] # Wait in batch_create is not allowed
5 | before_action :check_queue_size, only: [:create, :batch_create]
6 | before_action :check_requested_fields, except: [:batch_create] # Fields are ignored in batch_create
7 | before_action :set_base64_encoded
8 |
9 | def index
10 | page = params[:page].try(:to_i) || 1
11 | per_page = params[:per_page].try(:to_i) || Submission.per_page
12 |
13 | if page <= 0
14 | render json: { error: "invalid page: #{page}" }, status: :bad_request
15 | return
16 | elsif per_page < 0
17 | render json: { error: "invalid per_page: #{per_page}" }, status: :bad_request
18 | return
19 | end
20 |
21 | submissions = Submission.paginate(page: page, per_page: per_page)
22 | serializable_submissions = ActiveModelSerializers::SerializableResource.new(
23 | submissions, { each_serializer: SubmissionSerializer, base64_encoded: @base64_encoded, fields: @requested_fields }
24 | )
25 |
26 | render json: {
27 | submissions: serializable_submissions.as_json,
28 | meta: pagination_dict(submissions)
29 | }
30 | rescue Encoding::UndefinedConversionError => e
31 | render json: {
32 | error: "some attributes for one or more submissions cannot be converted to UTF-8, use base64_encoded=true query parameter"
33 | }, status: :bad_request
34 | end
35 |
36 | def destroy
37 | if !Config::ENABLE_SUBMISSION_DELETE
38 | render json: { error: "delete not allowed" }, status: :bad_request
39 | return
40 | end
41 |
42 | submission = Submission.find_by!(token: params[:token])
43 | if submission.status == Status.queue || submission.status == Status.process
44 | render json: {
45 | error: "submission cannot be deleted because its status is #{submission.status.id} (#{submission.status.name})"
46 | }, status: :bad_request
47 | return
48 | end
49 |
50 | submission.delete
51 |
52 | # Forcing base64_encoded=true because it guarantees user will get requested data after delete.
53 | render json: submission, base64_encoded: true, fields: @requested_fields
54 | end
55 |
56 | def show
57 | render json: Submission.find_by!(token: params[:token]), base64_encoded: @base64_encoded, fields: @requested_fields
58 | rescue Encoding::UndefinedConversionError
59 | render_conversion_error(:bad_request)
60 | end
61 |
62 | def batch_show
63 | tokens = (request.headers[:tokens] || params[:tokens]).to_s.strip.split(",")
64 |
65 | if tokens.length > Config::MAX_SUBMISSION_BATCH_SIZE
66 | render json: {
67 | error: "number of submissions in a batch should be less than or equal to #{Config::MAX_SUBMISSION_BATCH_SIZE}"
68 | }, status: :bad_request
69 | return
70 | elsif tokens.length == 0
71 | render json: {
72 | error: "there should be at least one submission in a batch"
73 | }, status: :bad_request
74 | return
75 | end
76 |
77 | existing_submissions = Hash[Submission.where(token: tokens).collect{ |s| [s.token, s] }]
78 |
79 | submissions = []
80 | tokens.each do |token|
81 | if existing_submissions.has_key?(token)
82 | serialized_submission = ActiveModelSerializers::SerializableResource.new(
83 | existing_submissions[token], { serializer: SubmissionSerializer, base64_encoded: @base64_encoded, fields: @requested_fields }
84 | )
85 | submissions << serialized_submission.as_json
86 | else
87 | submissions << nil
88 | end
89 | end
90 |
91 | render json: { submissions: submissions }
92 | rescue Encoding::UndefinedConversionError => e
93 | render json: {
94 | error: "some attributes for one or more submissions cannot be converted to UTF-8, use base64_encoded=true query parameter"
95 | }, status: :bad_request
96 | end
97 |
98 | def create
99 | submission = Submission.new(submission_params(params))
100 |
101 | if submission.save
102 | if @wait
103 | begin
104 | IsolateJob.perform_now(submission)
105 | render json: submission, status: :created, base64_encoded: @base64_encoded, fields: @requested_fields
106 | rescue Encoding::UndefinedConversionError => e
107 | render_conversion_error(:created, submission.token)
108 | end
109 | else
110 | IsolateJob.perform_later(submission)
111 | render json: submission, status: :created, fields: [:token]
112 | end
113 | else
114 | render json: submission.errors, status: :unprocessable_entity
115 | end
116 | end
117 |
118 | def batch_create
119 | number_of_submissions = params[:submissions].try(:size).to_i
120 |
121 | if number_of_submissions > Config::MAX_SUBMISSION_BATCH_SIZE
122 | render json: {
123 | error: "number of submissions in a batch should be less than or equal to #{Config::MAX_SUBMISSION_BATCH_SIZE}"
124 | }, status: :bad_request
125 | return
126 | elsif number_of_submissions == 0
127 | render json: {
128 | error: "there should be at least one submission in a batch"
129 | }, status: :bad_request
130 | return
131 | end
132 |
133 | submissions = params[:submissions].each.collect{ |p| Submission.new(submission_params(p)) }
134 |
135 | response = []
136 | has_valid_submission = false
137 |
138 | submissions.each do |submission|
139 | if submission.save
140 | IsolateJob.perform_later(submission)
141 | response << { token: submission.token }
142 | has_valid_submission = true
143 | else
144 | response << submission.errors
145 | end
146 | end
147 |
148 | render json: response, status: has_valid_submission ? :created : :unprocessable_entity
149 | end
150 |
151 | private
152 |
153 | def submission_params(params)
154 | submission_params = params.permit(
155 | :source_code,
156 | :language_id,
157 | :compiler_options,
158 | :command_line_arguments,
159 | :number_of_runs,
160 | :stdin,
161 | :expected_output,
162 | :cpu_time_limit,
163 | :cpu_extra_time,
164 | :wall_time_limit,
165 | :memory_limit,
166 | :stack_limit,
167 | :max_processes_and_or_threads,
168 | :enable_per_process_and_thread_time_limit,
169 | :enable_per_process_and_thread_memory_limit,
170 | :max_file_size,
171 | :redirect_stderr_to_stdout,
172 | :callback_url,
173 | :archive
174 | )
175 |
176 | submission_params[:archive] = Base64Service.decode(submission_params[:archive])
177 |
178 | if @base64_encoded
179 | submission_params[:source_code] = Base64Service.decode(submission_params[:source_code])
180 | submission_params[:stdin] = Base64Service.decode(submission_params[:stdin])
181 | submission_params[:expected_output] = Base64Service.decode(submission_params[:expected_output])
182 | end
183 |
184 | submission_params
185 | end
186 |
187 | def check_wait
188 | @wait = params[:wait] == "true"
189 | if @wait && !Config::ENABLE_WAIT_RESULT
190 | render json: { error: "wait not allowed" }, status: :bad_request
191 | end
192 | end
193 |
194 | def check_queue_size
195 | number_of_submissions = params[:submissions].try(:size).presence || 1
196 | if Resque.size(ENV["JUDGE0_VERSION"]) + number_of_submissions > Config::MAX_QUEUE_SIZE
197 | render json: { error: "queue is full" }, status: :service_unavailable
198 | end
199 | end
200 |
201 | def check_requested_fields
202 | fields_service = Fields::Submission.new(params[:fields])
203 | render json: { error: "invalid fields: [#{fields_service.invalid_fields.join(", ")}]" }, status: :bad_request if fields_service.has_invalid_fields?
204 | @requested_fields = fields_service.requested_fields
205 | end
206 |
207 | def set_base64_encoded
208 | @base64_encoded = params[:base64_encoded] == "true"
209 | end
210 |
211 | def render_conversion_error(status, token = nil)
212 | response_json = {
213 | error: "some attributes for this submission cannot be converted to UTF-8, use base64_encoded=true query parameter",
214 | }
215 | response_json[:token] = token if token
216 |
217 | render json: response_json, status: status
218 | end
219 | end
220 |
--------------------------------------------------------------------------------
/app/models/submission.rb:
--------------------------------------------------------------------------------
1 | # == Schema Information
2 | #
3 | # Table name: submissions
4 | #
5 | # id :integer not null, primary key
6 | # source_code :text
7 | # language_id :integer
8 | # stdin :text
9 | # expected_output :text
10 | # stdout :text
11 | # status_id :integer
12 | # created_at :datetime
13 | # finished_at :datetime
14 | # time :decimal(, )
15 | # memory :integer
16 | # stderr :text
17 | # token :string
18 | # number_of_runs :integer
19 | # cpu_time_limit :decimal(, )
20 | # cpu_extra_time :decimal(, )
21 | # wall_time_limit :decimal(, )
22 | # memory_limit :integer
23 | # stack_limit :integer
24 | # max_processes_and_or_threads :integer
25 | # enable_per_process_and_thread_time_limit :boolean
26 | # enable_per_process_and_thread_memory_limit :boolean
27 | # max_file_size :integer
28 | # compile_output :text
29 | # exit_code :integer
30 | # exit_signal :integer
31 | # message :text
32 | # wall_time :decimal(, )
33 | # compiler_options :string
34 | # command_line_arguments :string
35 | #
36 | # Indexes
37 | #
38 | # index_submissions_on_token (token)
39 | #
40 |
41 | class Submission < ApplicationRecord
42 | validates :source_code, :language_id, presence: true
43 | validates :number_of_runs,
44 | numericality: { greater_than: 0, less_than_or_equal_to: Config::MAX_NUMBER_OF_RUNS }
45 | validates :cpu_time_limit,
46 | numericality: { greater_than: 0, less_than_or_equal_to: Config::MAX_CPU_TIME_LIMIT }
47 | validates :cpu_extra_time,
48 | numericality: { greater_than: 0, less_than_or_equal_to: Config::MAX_CPU_EXTRA_TIME }
49 | validates :wall_time_limit,
50 | numericality: { greater_than: 0, less_than_or_equal_to: Config::MAX_WALL_TIME_LIMIT }
51 | validates :memory_limit,
52 | numericality: { greater_than: 0, less_than_or_equal_to: Config::MAX_MEMORY_LIMIT }
53 | validates :stack_limit,
54 | numericality: { greater_than: 0, less_than_or_equal_to: Config::MAX_STACK_LIMIT }
55 | validates :max_processes_and_or_threads,
56 | numericality: { greater_than: 0, less_than_or_equal_to: Config::MAX_MAX_PROCESSES_AND_OR_THREADS }
57 | validates :enable_per_process_and_thread_time_limit,
58 | inclusion: { in: [false], message: "this option cannot be enabled" },
59 | unless: "Config::ALLOW_ENABLE_PER_PROCESS_AND_THREAD_TIME_LIMIT"
60 | validates :enable_per_process_and_thread_memory_limit,
61 | inclusion: { in: [false], message: "this option cannot be enabled" },
62 | unless: "Config::ALLOW_ENABLE_PER_PROCESS_AND_THREAD_MEMORY_LIMIT"
63 | validates :max_file_size,
64 | numericality: { greater_than: 0, less_than_or_equal_to: Config::MAX_MAX_FILE_SIZE }
65 | validates :compiler_options, length: { maximum: 512 }
66 | validates :command_line_arguments, length: { maximum: 512 }
67 | validate :language_existence, :compiler_options_allowed, :command_line_arguments_allowed
68 |
69 | before_create :generate_token
70 | before_validation :set_defaults
71 |
72 | enumeration :status
73 |
74 | default_scope { order(created_at: :desc) }
75 |
76 | self.per_page = 20
77 |
78 | def source_code
79 | @decoded_source_code ||= Base64Service.decode(self[:source_code])
80 | end
81 |
82 | def source_code=(value)
83 | super(value)
84 | self[:source_code] = Base64Service.encode(self[:source_code])
85 | end
86 |
87 |
88 | def stdin
89 | @decoded_stdin ||= Base64Service.decode(self[:stdin])
90 | end
91 |
92 | def stdin=(value)
93 | super(value)
94 | self[:stdin] = Base64Service.encode(self[:stdin])
95 | end
96 |
97 |
98 | def stdout
99 | @decoded_stdout ||= Base64Service.decode(self[:stdout])
100 | end
101 |
102 | def stdout=(value)
103 | super(value)
104 | self[:stdout] = Base64Service.encode(self[:stdout])
105 | end
106 |
107 |
108 | def expected_output
109 | @decoded_expected_output ||= Base64Service.decode(self[:expected_output])
110 | end
111 |
112 | def expected_output=(value)
113 | super(value)
114 | self[:expected_output] = Base64Service.encode(self[:expected_output])
115 | end
116 |
117 |
118 | def stderr
119 | @decoded_stderr ||= Base64Service.decode(self[:stderr])
120 | end
121 |
122 | def stderr=(value)
123 | super(value)
124 | self[:stderr] = Base64Service.encode(self[:stderr])
125 | end
126 |
127 |
128 | def compile_output
129 | @decoded_compile_output ||= Base64Service.decode(self[:compile_output])
130 | end
131 |
132 | def compile_output=(value)
133 | super(value)
134 | self[:compile_output] = Base64Service.encode(self[:compile_output])
135 | end
136 |
137 |
138 | def language
139 | @language ||= Language.unscoped.find(language_id)
140 | end
141 |
142 |
143 | def status
144 | Status.find_by(id: status_id)
145 | end
146 |
147 | def status=(status)
148 | self.status_id = status.id
149 | end
150 |
151 | private
152 |
153 | def language_existence
154 | if language_id && !Language.unscoped.exists?(language_id)
155 | errors.add(:language_id, "language with id #{language_id} doesn't exist")
156 | elsif language_id && Language.unscoped.find(language_id).try(:is_archived)
157 | errors.add(:language_id, "language with id #{language_id} is archived and cannot be used anymore")
158 | end
159 | end
160 |
161 | def compiler_options_allowed
162 | return if compiler_options.blank?
163 |
164 | unless Config::ENABLE_COMPILER_OPTIONS
165 | errors.add(:compiler_options, "setting compiler options is not allowed")
166 | return
167 | end
168 |
169 | if Language.unscoped.exists?(language_id) && language.compile_cmd.nil?
170 | errors.add(:compiler_options, "setting compiler options is only allowed for compiled languages")
171 | return
172 | end
173 |
174 | @@allowed_languages ||= Config::ALLOWED_LANGUAGES_FOR_COMPILER_OPTIONS.collect{ |s| s + " " }
175 | if Language.unscoped.exists?(language_id) && @@allowed_languages.present? && !language.name.starts_with?(*@@allowed_languages)
176 | @@allowed_languages_message ||= @@allowed_languages.size > 1 ? @@allowed_languages[0..-2].collect{ |s| s.strip }.join(", ") + " and " + @@allowed_languages[-1].strip : @@allowed_languages[0].strip
177 | errors.add(:compiler_options, "setting compiler options is only allowed for #{@@allowed_languages_message}")
178 | end
179 | end
180 |
181 | def command_line_arguments_allowed
182 | return if command_line_arguments.blank?
183 |
184 | unless Config::ENABLE_COMMAND_LINE_ARGUMENTS
185 | errors.add(:command_line_arguments, "setting command line arguments is not allowed")
186 | end
187 | end
188 |
189 | def generate_token
190 | begin
191 | self.token = SecureRandom.uuid
192 | end while self.class.exists?(token: token)
193 | end
194 |
195 | def set_defaults
196 | self.status ||= Status.queue
197 | self.number_of_runs ||= Config::NUMBER_OF_RUNS
198 | self.cpu_time_limit ||= Config::CPU_TIME_LIMIT
199 | self.cpu_extra_time ||= Config::CPU_EXTRA_TIME
200 | self.wall_time_limit ||= Config::WALL_TIME_LIMIT
201 | self.memory_limit ||= Config::MEMORY_LIMIT
202 | self.stack_limit ||= Config::STACK_LIMIT
203 | self.max_processes_and_or_threads ||= Config::MAX_PROCESSES_AND_OR_THREADS
204 | self.enable_per_process_and_thread_time_limit = NilValue.value_or_default(
205 | self.enable_per_process_and_thread_time_limit,
206 | Config::ENABLE_PER_PROCESS_AND_THREAD_TIME_LIMIT
207 | )
208 | self.enable_per_process_and_thread_memory_limit = NilValue.value_or_default(
209 | self.enable_per_process_and_thread_memory_limit,
210 | Config::ENABLE_PER_PROCESS_AND_THREAD_MEMORY_LIMIT
211 | )
212 | self.max_file_size ||= Config::MAX_FILE_SIZE
213 | self.redirect_stderr_to_stdout = NilValue.value_or_default(
214 | self.redirect_stderr_to_stdout,
215 | Config::REDIRECT_STDERR_TO_STDOUT
216 | )
217 | end
218 | end
219 |
--------------------------------------------------------------------------------
/db/languages/archived.rb:
--------------------------------------------------------------------------------
1 | @languages ||= []
2 | @languages +=
3 | [
4 | {
5 | id: 1,
6 | name: "Bash (4.4)",
7 | is_archived: true,
8 | source_file: "script.sh",
9 | run_cmd: "/usr/local/bash-4.4/bin/bash script.sh"
10 | },
11 | {
12 | id: 2,
13 | name: "Bash (4.0)",
14 | is_archived: true,
15 | source_file: "script.sh",
16 | run_cmd: "/usr/local/bash-4.0/bin/bash script.sh"
17 | },
18 | {
19 | id: 3,
20 | name: "Basic (fbc 1.05.0)",
21 | is_archived: true,
22 | source_file: "main.bas",
23 | compile_cmd: "/usr/local/fbc-1.05.0/bin/fbc %s main.bas",
24 | run_cmd: "./main"
25 | },
26 | {
27 | id: 4,
28 | name: "C (gcc 7.2.0)",
29 | is_archived: true,
30 | source_file: "main.c",
31 | compile_cmd: "/usr/local/gcc-7.2.0/bin/gcc %s main.c",
32 | run_cmd: "./a.out"
33 | },
34 | {
35 | id: 5,
36 | name: "C (gcc 6.4.0)",
37 | is_archived: true,
38 | source_file: "main.c",
39 | compile_cmd: "/usr/local/gcc-6.4.0/bin/gcc %s main.c",
40 | run_cmd: "./a.out"
41 | },
42 | {
43 | id: 6,
44 | name: "C (gcc 6.3.0)",
45 | is_archived: true,
46 | source_file: "main.c",
47 | compile_cmd: "/usr/local/gcc-6.3.0/bin/gcc %s main.c",
48 | run_cmd: "./a.out"
49 | },
50 | {
51 | id: 7,
52 | name: "C (gcc 5.4.0)",
53 | is_archived: true,
54 | source_file: "main.c",
55 | compile_cmd: "/usr/local/gcc-5.4.0/bin/gcc %s main.c",
56 | run_cmd: "./a.out"
57 | },
58 | {
59 | id: 8,
60 | name: "C (gcc 4.9.4)",
61 | is_archived: true,
62 | source_file: "main.c",
63 | compile_cmd: "/usr/local/gcc-4.9.4/bin/gcc %s main.c",
64 | run_cmd: "./a.out"
65 | },
66 | {
67 | id: 9,
68 | name: "C (gcc 4.8.5)",
69 | is_archived: true,
70 | source_file: "main.c",
71 | compile_cmd: "/usr/local/gcc-4.8.5/bin/gcc %s main.c",
72 | run_cmd: "./a.out"
73 | },
74 |
75 | {
76 | id: 10,
77 | name: "C++ (g++ 7.2.0)",
78 | is_archived: true,
79 | source_file: "main.cpp",
80 | compile_cmd: "/usr/local/gcc-7.2.0/bin/g++ %s main.cpp",
81 | run_cmd: "LD_LIBRARY_PATH=/usr/local/gcc-7.2.0/lib64 ./a.out"
82 | },
83 | {
84 | id: 11,
85 | name: "C++ (g++ 6.4.0)",
86 | is_archived: true,
87 | source_file: "main.cpp",
88 | compile_cmd: "/usr/local/gcc-6.4.0/bin/g++ %s main.cpp",
89 | run_cmd: "LD_LIBRARY_PATH=/usr/local/gcc-6.4.0/lib64 ./a.out"
90 | },
91 | {
92 | id: 12,
93 | name: "C++ (g++ 6.3.0)",
94 | is_archived: true,
95 | source_file: "main.cpp",
96 | compile_cmd: "/usr/local/gcc-6.3.0/bin/g++ %s main.cpp",
97 | run_cmd: "LD_LIBRARY_PATH=/usr/local/gcc-6.3.0/lib64 ./a.out"
98 | },
99 | {
100 | id: 13,
101 | name: "C++ (g++ 5.4.0)",
102 | is_archived: true,
103 | source_file: "main.cpp",
104 | compile_cmd: "/usr/local/gcc-5.4.0/bin/g++ %s main.cpp",
105 | run_cmd: "LD_LIBRARY_PATH=/usr/local/gcc-5.4.0/lib64 ./a.out"
106 | },
107 | {
108 | id: 14,
109 | name: "C++ (g++ 4.9.4)",
110 | is_archived: true,
111 | source_file: "main.cpp",
112 | compile_cmd: "/usr/local/gcc-4.9.4/bin/g++ %s main.cpp",
113 | run_cmd: "LD_LIBRARY_PATH=/usr/local/gcc-4.9.4/lib64 ./a.out"
114 | },
115 | {
116 | id: 15,
117 | name: "C++ (g++ 4.8.5)",
118 | is_archived: true,
119 | source_file: "main.cpp",
120 | compile_cmd: "/usr/local/gcc-4.8.5/bin/g++ %s main.cpp",
121 | run_cmd: "LD_LIBRARY_PATH=/usr/local/gcc-4.8.5/lib64 ./a.out"
122 | },
123 | {
124 | id: 16,
125 | name: "C# (mono 5.4.0.167)",
126 | is_archived: true,
127 | source_file: "Main.cs",
128 | compile_cmd: "/usr/local/mono-5.4.0.167/bin/mcs %s Main.cs",
129 | run_cmd: "/usr/local/mono-5.4.0.167/bin/mono Main.exe"
130 | },
131 | {
132 | id: 17,
133 | name: "C# (mono 5.2.0.224)",
134 | is_archived: true,
135 | source_file: "Main.cs",
136 | compile_cmd: "/usr/local/mono-5.2.0.224/bin/mcs %s Main.cs",
137 | run_cmd: "/usr/local/mono-5.2.0.224/bin/mono Main.exe"
138 | },
139 | {
140 | id: 18,
141 | name: "Clojure (1.8.0)",
142 | is_archived: true,
143 | source_file: "main.clj",
144 | run_cmd: "/usr/bin/java -cp /usr/local/clojure-1.8.0/clojure-1.8.0.jar clojure.main main.clj"
145 | },
146 | {
147 | id: 19,
148 | name: "Crystal (0.23.1)",
149 | is_archived: true,
150 | source_file: "main.cr",
151 | compile_cmd: "/usr/local/crystal-0.23.1-3/bin/crystal build %s main.cr",
152 | run_cmd: "./main"
153 | },
154 | {
155 | id: 20,
156 | name: "Elixir (1.5.1)",
157 | is_archived: true,
158 | source_file: "main.exs",
159 | run_cmd: "/usr/local/elixir-1.5.1/bin/elixir main.exs"
160 | },
161 | {
162 | id: 21,
163 | name: "Erlang (OTP 20.0)",
164 | is_archived: true,
165 | source_file: "main.erl",
166 | run_cmd: "/bin/sed -i \"s/^/\\n/\" main.erl && /usr/local/erlang-20.0/bin/escript main.erl"
167 | },
168 | {
169 | id: 22,
170 | name: "Go (1.9)",
171 | is_archived: true,
172 | source_file: "main.go",
173 | compile_cmd: "/usr/local/go-1.9/bin/go build %s main.go",
174 | run_cmd: "./main"
175 | },
176 | {
177 | id: 23,
178 | name: "Haskell (ghc 8.2.1)",
179 | is_archived: true,
180 | source_file: "main.hs",
181 | compile_cmd: "/usr/local/ghc-8.2.1/bin/ghc %s main.hs",
182 | run_cmd: "./main"
183 | },
184 | {
185 | id: 24,
186 | name: "Haskell (ghc 8.0.2)",
187 | is_archived: true,
188 | source_file: "main.hs",
189 | compile_cmd: "/usr/local/ghc-8.0.2/bin/ghc %s main.hs",
190 | run_cmd: "./main"
191 | },
192 | {
193 | id: 25,
194 | name: "Insect (5.0.0)",
195 | is_archived: true,
196 | source_file: "main.ins",
197 | run_cmd: "/usr/local/insect-5.0.0/insect main.ins"
198 | },
199 | {
200 | id: 26,
201 | name: "Java (OpenJDK 9 with Eclipse OpenJ9)",
202 | is_archived: true,
203 | source_file: "Main.java",
204 | compile_cmd: "/usr/local/openjdk9-openj9/bin/javac %s Main.java",
205 | run_cmd: "/usr/local/openjdk9-openj9/bin/java Main"
206 | },
207 | {
208 | id: 27,
209 | name: "Java (OpenJDK 8)",
210 | is_archived: true,
211 | source_file: "Main.java",
212 | compile_cmd: "/usr/lib/jvm/java-8-openjdk-amd64/bin/javac %s Main.java",
213 | run_cmd: "/usr/lib/jvm/java-8-openjdk-amd64/bin/java Main",
214 | },
215 | {
216 | id: 28,
217 | name: "Java (OpenJDK 7)",
218 | is_archived: true,
219 | source_file: "Main.java",
220 | compile_cmd: "/usr/lib/jvm/java-7-openjdk-amd64/bin/javac %s Main.java",
221 | run_cmd: "/usr/lib/jvm/java-7-openjdk-amd64/bin/java Main",
222 | },
223 | {
224 | id: 29,
225 | name: "JavaScript (nodejs 8.5.0)",
226 | is_archived: true,
227 | source_file: "main.js",
228 | run_cmd: "/usr/local/node-8.5.0/bin/node main.js"
229 | },
230 | {
231 | id: 30,
232 | name: "JavaScript (nodejs 7.10.1)",
233 | is_archived: true,
234 | source_file: "main.js",
235 | run_cmd: "/usr/local/node-7.10.1/bin/node main.js"
236 | },
237 | {
238 | id: 31,
239 | name: "OCaml (4.05.0)",
240 | is_archived: true,
241 | source_file: "main.ml",
242 | compile_cmd: "/usr/local/ocaml-4.05.0/bin/ocamlc %s main.ml",
243 | run_cmd: "./a.out"
244 | },
245 | {
246 | id: 32,
247 | name: "Octave (4.2.0)",
248 | is_archived: true,
249 | source_file: "file.m",
250 | run_cmd: "/usr/local/octave-4.2.0/bin/octave-cli -q --no-gui --no-history file.m"
251 | },
252 | {
253 | id: 33,
254 | name: "Pascal (fpc 3.0.0)",
255 | is_archived: true,
256 | source_file: "main.pas",
257 | compile_cmd: "/usr/local/fpc-3.0.0/bin/fpc %s main.pas",
258 | run_cmd: "./main"
259 | },
260 | {
261 | id: 34,
262 | name: "Python (3.6.0)",
263 | is_archived: true,
264 | source_file: "main.py",
265 | run_cmd: "/usr/local/python-3.6.0/bin/python3 main.py"
266 | },
267 | {
268 | id: 35,
269 | name: "Python (3.5.3)",
270 | is_archived: true,
271 | source_file: "main.py",
272 | run_cmd: "/usr/local/python-3.5.3/bin/python3 main.py"
273 | },
274 | {
275 | id: 36,
276 | name: "Python (2.7.9)",
277 | is_archived: true,
278 | source_file: "main.py",
279 | run_cmd: "/usr/local/python-2.7.9/bin/python main.py"
280 | },
281 | {
282 | id: 37,
283 | name: "Python (2.6.9)",
284 | is_archived: true,
285 | source_file: "main.py",
286 | run_cmd: "/usr/local/python-2.6.9/bin/python main.py"
287 | },
288 | {
289 | id: 38,
290 | name: "Ruby (2.4.0)",
291 | is_archived: true,
292 | source_file: "main.rb",
293 | run_cmd: "/usr/local/ruby-2.4.0/bin/ruby main.rb"
294 | },
295 | {
296 | id: 39,
297 | name: "Ruby (2.3.3)",
298 | is_archived: true,
299 | source_file: "main.rb",
300 | run_cmd: "/usr/local/ruby-2.3.3/bin/ruby main.rb"
301 | },
302 | {
303 | id: 40,
304 | name: "Ruby (2.2.6)",
305 | is_archived: true,
306 | source_file: "main.rb",
307 | run_cmd: "/usr/local/ruby-2.2.6/bin/ruby main.rb"
308 | },
309 | {
310 | id: 41,
311 | name: "Ruby (2.1.9)",
312 | is_archived: true,
313 | source_file: "main.rb",
314 | run_cmd: "/usr/local/ruby-2.1.9/bin/ruby main.rb"
315 | },
316 | {
317 | id: 42,
318 | name: "Rust (1.20.0)",
319 | is_archived: true,
320 | source_file: "main.rs",
321 | compile_cmd: "/usr/local/rust-1.20.0/bin/rustc %s main.rs",
322 | run_cmd: "./main"
323 | }
324 | ]
--------------------------------------------------------------------------------
/judge0-api.conf.default:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Rails Environment
3 | ###############################################################################
4 | # Specify Rails environment: production or development
5 | # Default: production
6 | RAILS_ENV=
7 |
8 | # Specify maximum number of concurrent Rails threads.
9 | # Default: 5
10 | RAILS_MAX_THREADS=
11 |
12 | # Maintenance mode is a mode in which clients cannot
13 | # create or delete submissions while maintenance is enabled.
14 | # Default: false
15 | MAINTENANCE_MODE=
16 |
17 | # Set custom maintenance message that will be returned to clients
18 | # who try to create or delete submisions.
19 | # Default: Judge0 API is currently in maintenance.
20 | MAINTENANCE_MESSAGE=
21 |
22 | # Secret key base for production, if not set it will be randomly generated
23 | # Default: randomly generated
24 | SECRET_KEY_BASE=
25 |
26 | # Allow only specified origins.
27 | # If left blank, then all origins will be allowed (denoted with '*').
28 | # Example:
29 | # ALLOW_ORIGIN="www.judge0.com judge0.com www.example.com blog.example.com"
30 | ALLOW_ORIGIN=
31 |
32 | # Disallow only specified origins.
33 | # If left blank, then no origin will be disallowed.
34 | # Example:
35 | # DISALLOW_ORIGIN="www.judge0.com judge0.com www.example.com blog.example.com"
36 | DISALLOW_ORIGIN=
37 |
38 | # Allow only specified IP addresses.
39 | # If left blank, then all IP addresses will be allowed.
40 | # Example:
41 | # ALLOW_IP="192.168.10.10 96.239.226.228 208.23.207.242"
42 | ALLOW_IP=
43 |
44 | # Disallow only specified IP addresses.
45 | # If left blank, then no IP addresses will be disallowed.
46 | # Example:
47 | # DISALLOW_IP="192.168.10.10 96.239.226.228 208.23.207.242"
48 | DISALLOW_IP=
49 |
50 |
51 | ###############################################################################
52 | # Authentication
53 | ###############################################################################
54 | # You can protect your API with (AUTHN_HEADER, AUTHN_TOKEN) pair.
55 | # Each request then needs to have this pair either in headers or
56 | # query parameters. For example let AUTHN_HEADER=X-Judge0-Token and
57 | # AUTHN_TOKEN=mySecretToken. Then user should authenticate by sending this
58 | # in headers or query parameters in each request, e.g.:
59 | # https://api.judge0.com/system_info?X-Judge0-Token=mySecretToken
60 |
61 | # Specify authentication header name.
62 | # Default: X-Auth-Token
63 | AUTHN_HEADER=
64 |
65 | # Specify valid authentication tokens.
66 | # Default: empty - authentication is disabled
67 | AUTHN_TOKEN=
68 |
69 |
70 | ###############################################################################
71 | # Authorization
72 | ###############################################################################
73 | # Protected API calls can be issued with (AUTHZ_HEADER, AUTHZ_TOKEN) pair.
74 | # To see exactly which API calls are protected with authorization tokens
75 | # please read the docs at https://api.judge0.com.
76 | # API authorization ensures that only specified users call protected API calls.
77 | # For example let AUTHZ_HEADER=X-Judge0-User and AUTHZ_TOKEN=mySecretToken.
78 | # Then user should authorize be sending this in headers or query parameters in
79 | # each request, e.g.: https://api.judge0.com/system_info?X-Judge0-User=mySecretToken
80 | # Note that if you enabled authentication, then user should also send valid
81 | # authentication token.
82 |
83 | # Specify authorization header name.
84 | # Default: X-Auth-User
85 | AUTHZ_HEADER=
86 |
87 | # Specify valid authorization tokens.
88 | # Default: empty - authorization is disabled, protected API calls cannot be issued
89 | AUTHZ_TOKEN=
90 |
91 |
92 | ###############################################################################
93 | # Workers
94 | ###############################################################################
95 | # Specify polling frequency in seconds. Decimal numbers are allowed.
96 | # Default: 0.1
97 | INTERVAL=
98 |
99 | # Specify how many parallel workers to run.
100 | # Default: 1
101 | COUNT=
102 |
103 | # Specify maximum queue size. Represents maximum number of submissions that
104 | # can wait in the queue at once. If request for new submission comes and the
105 | # queue if full then submission will be rejected.
106 | # Default: 100
107 | MAX_QUEUE_SIZE=
108 |
109 |
110 | ###############################################################################
111 | # Database
112 | ###############################################################################
113 | # Specify Redis host
114 | # Default: localhost
115 | REDIS_HOST=redis
116 |
117 | # Specify Redis port.
118 | # Default: 6379
119 | REDIS_PORT=
120 |
121 | # Specify Redis password. Cannot be blank.
122 | # Default: NO DEFAULT! MUST BE SET!
123 | REDIS_PASSWORD=YourPasswordHere1234
124 |
125 | # Specify Postgres host.
126 | # Default: localhost
127 | POSTGRES_HOST=db
128 |
129 | # Specify Postgres port.
130 | # Default: 5432
131 | POSTGRES_PORT=
132 |
133 | # Name of the database to use. Used only in production.
134 | # Default: postgres
135 | POSTGRES_DB=
136 |
137 | # User who can access this database. Used only in production.
138 | # Default: postgres
139 | POSTGRES_USER=
140 |
141 | # Password of the user. Cannot be blank. Used only in production.
142 | # Default: NO DEFAULT, YOU MUST SET YOUR PASSWORD
143 | POSTGRES_PASSWORD=YourPasswordHere1234
144 |
145 |
146 | ###############################################################################
147 | # Configuration
148 | ###############################################################################
149 | # https://raw.githubusercontent.com/ioi/isolate/master/isolate.1.txt
150 |
151 | # If enabled user can request to synchronically wait for submission result on submission create.
152 | # Default: true, i.e. user can request to wait for the result
153 | ENABLE_WAIT_RESULT=
154 |
155 | # If enabled user is allowed to set custom compiler options.
156 | # Default: true
157 | ENABLE_COMPILER_OPTIONS=
158 |
159 | # List language names, separated by space, for which setting compiler options is allowed.
160 | # Note that ENABLE_COMPILER_OPTIONS has higher priority, so this option will be
161 | # ignored if setting compiler options is disabled with ENABLE_COMPILER_OPTIONS.
162 | # For example, ALLOWED_LANGUAGES_FOR_COMPILER_OPTIONS="C C++ Java" would only
163 | # allow setting compiler options for languages C, C++ and Java.
164 | # Default: empty - for every compiled language setting compiler options is allowed.
165 | ALLOWED_LANGUAGES_FOR_COMPILER_OPTIONS=
166 |
167 | # If enabled user is allowed to set custom command line arguments.
168 | # Default: true
169 | ENABLE_COMMAND_LINE_ARGUMENTS=
170 |
171 | # If enabled autorized users can delete a submission.
172 | # Default: false
173 | ENABLE_SUBMISSION_DELETE=
174 |
175 | # Default runtime limit for every program (in seconds). Decimal numbers are allowed.
176 | # Time in which the OS assigns the processor to different tasks is not counted.
177 | # Default: 2
178 | CPU_TIME_LIMIT=
179 |
180 | # Maximum custom CPU_TIME_LIMIT.
181 | # Default: 15
182 | MAX_CPU_TIME_LIMIT=
183 |
184 | # When a time limit is exceeded, wait for extra time (in seconds), before
185 | # killing the program. This has the advantage that the real execution time
186 | # is reported, even though it slightly exceeds the limit.
187 | # Default: 0.5
188 | CPU_EXTRA_TIME=
189 |
190 | # Maximum custom CPU_EXTRA_TIME.
191 | # Default: 2
192 | MAX_CPU_EXTRA_TIME=
193 |
194 | # Limit wall-clock time in seconds. Decimal numbers are allowed.
195 | # This clock measures the time from the start of the program to its exit,
196 | # so it does not stop when the program has lost the CPU or when it is waiting
197 | # for an external event. We recommend to use CPU_TIME_LIMIT as the main limit,
198 | # but set WALL_TIME_LIMIT to a much higher value as a precaution against
199 | # sleeping programs.
200 | # Default: 5
201 | WALL_TIME_LIMIT=
202 |
203 | # Maximum custom WALL_TIME_LIMIT.
204 | # Default: 20
205 | MAX_WALL_TIME_LIMIT=
206 |
207 | # Limit address space of the program in kilobytes.
208 | # Default: 128000
209 | MEMORY_LIMIT=
210 |
211 | # Maximum custom MEMORY_LIMIT.
212 | # Default: 256000
213 | MAX_MEMORY_LIMIT=
214 |
215 | # Limit process stack in kilobytes.
216 | # Default: 64000
217 | STACK_LIMIT=
218 |
219 | # Maximum custom STACK_LIMIT.
220 | # Default: 128000
221 | MAX_STACK_LIMIT=
222 |
223 | # Maximum number of processes and/or threads program can create.
224 | # Default: 60
225 | MAX_PROCESSES_AND_OR_THREADS=
226 |
227 | # Maximum custom MAX_PROCESSES_AND_OR_THREADS.
228 | # Default: 120
229 | MAX_MAX_PROCESSES_AND_OR_THREADS=
230 |
231 | # If true then CPU_TIME_LIMIT will be used as per process and thread.
232 | # Default: false, i.e. CPU_TIME_LIMIT is set as a total limit for all processes and threads.
233 | ENABLE_PER_PROCESS_AND_THREAD_TIME_LIMIT=
234 |
235 | # If false, user won't be able to set ENABLE_PER_PROCESS_AND_THREAD_TIME_LIMIT.
236 | # Default: true
237 | ALLOW_ENABLE_PER_PROCESS_AND_THREAD_TIME_LIMIT=
238 |
239 | # If true then MEMORY_LIMIT will be used as per process and thread.
240 | # Default: false, i.e. MEMORY_LIMIT is set as a total limit for all processes and threads.
241 | ENABLE_PER_PROCESS_AND_THREAD_MEMORY_LIMIT=
242 |
243 | # If false, user won't be able to set ENABLE_PER_PROCESS_AND_THREAD_MEMORY_LIMIT.
244 | # Default: true
245 | ALLOW_ENABLE_PER_PROCESS_AND_THREAD_MEMORY_LIMIT=
246 |
247 | # Limit size of files created (or modified) by the program in kilobytes.
248 | # Default: 1024
249 | MAX_FILE_SIZE=
250 |
251 | # Maximum custom MAX_FILE_SIZE.
252 | # Default: 4096
253 | MAX_MAX_FILE_SIZE=
254 |
255 | # Run each program this many times and take average of time and memory.
256 | # Default: 1
257 | NUMBER_OF_RUNS=
258 |
259 | # Maximum custom NUMBER_OF_RUNS.
260 | # Default: 20
261 | MAX_NUMBER_OF_RUNS=
262 |
263 | # Redirect stderr to stdout.
264 | # Default: false
265 | REDIRECT_STDERR_TO_STDOUT=
266 |
267 | # Maximum total size (in kilobytes) of extracted files from archive.
268 | # Default: 10240, i.e. maximum of 10MB in total can be extracted.
269 | MAX_EXTRACT_SIZE=
270 |
271 | # Maximum number of submissions that can be created or get in a batch.
272 | # Default: 20
273 | MAX_SUBMISSION_BATCH_SIZE=
--------------------------------------------------------------------------------
/app/jobs/isolate_job.rb:
--------------------------------------------------------------------------------
1 | class IsolateJob < ApplicationJob
2 | queue_as ENV["JUDGE0_VERSION"].to_sym
3 |
4 | STDIN_FILE_NAME = "stdin.txt"
5 | STDOUT_FILE_NAME = "stdout.txt"
6 | STDERR_FILE_NAME = "stderr.txt"
7 | METADATA_FILE_NAME = "metadata.txt"
8 | ARCHIVE_FILE_NAME = "archive.zip"
9 |
10 | attr_reader :submission, :cgroups,
11 | :box_id, :workdir, :boxdir, :tmpdir,
12 | :source_file, :stdin_file, :stdout_file,
13 | :stderr_file, :metadata_file, :archive_file
14 |
15 | def perform(submission)
16 | @submission = submission
17 |
18 | time = []
19 | memory = []
20 |
21 | submission.update(status: Status.process)
22 | submission.number_of_runs.times do
23 | initialize_workdir
24 | if compile == :failure
25 | cleanup
26 | return
27 | end
28 | run
29 | verify
30 |
31 | time << submission.time
32 | memory << submission.memory
33 |
34 | cleanup
35 | break if submission.status != Status.ac
36 | end
37 |
38 | submission.time = time.inject(&:+).to_f / time.size
39 | submission.memory = memory.inject(&:+).to_f / memory.size
40 | submission.save
41 |
42 | rescue Exception => e
43 | submission.finished_at ||= DateTime.now
44 | submission.update(message: e.message, status: Status.boxerr)
45 | cleanup(raise_exception = false)
46 | ensure
47 | call_callback
48 | end
49 |
50 | private
51 |
52 | def initialize_workdir
53 | @box_id = submission.id%2147483647
54 | @cgroups = (!submission.enable_per_process_and_thread_time_limit || !submission.enable_per_process_and_thread_memory_limit) ? "--cg" : ""
55 | @workdir = `isolate #{cgroups} -b #{box_id} --init`.chomp
56 | @boxdir = workdir + "/box"
57 | @tmpdir = workdir + "/tmp"
58 | @source_file = boxdir + "/" + submission.language.source_file
59 | @stdin_file = workdir + "/" + STDIN_FILE_NAME
60 | @stdout_file = workdir + "/" + STDOUT_FILE_NAME
61 | @stderr_file = workdir + "/" + STDERR_FILE_NAME
62 | @metadata_file = workdir + "/" + METADATA_FILE_NAME
63 | @archive_file = boxdir + "/" + ARCHIVE_FILE_NAME
64 |
65 | [stdin_file, stdout_file, stderr_file, metadata_file].each do |f|
66 | initialize_file(f)
67 | end
68 |
69 | File.open(source_file, "wb") { |f| f.write(submission.source_code) }
70 | File.open(stdin_file, "wb") { |f| f.write(submission.stdin) }
71 |
72 | extract_archive
73 | end
74 |
75 | def initialize_file(file)
76 | `sudo touch #{file} && sudo chown $(whoami): #{file}`
77 | end
78 |
79 | def extract_archive
80 | return unless submission.archive?
81 |
82 | File.open(archive_file, "wb") { |f| f.write(submission.archive) }
83 |
84 | command = "isolate #{cgroups} \
85 | -s \
86 | -b #{box_id} \
87 | -t 2 \
88 | -x 1 \
89 | -w 4 \
90 | -k #{Config::MAX_STACK_LIMIT} \
91 | -p#{Config::MAX_MAX_PROCESSES_AND_OR_THREADS} \
92 | #{submission.enable_per_process_and_thread_time_limit ? (cgroups.present? ? "--no-cg-timing" : "") : "--cg-timing"} \
93 | #{submission.enable_per_process_and_thread_memory_limit ? "-m " : "--cg-mem="}#{Config::MAX_MEMORY_LIMIT} \
94 | -f #{Config::MAX_EXTRACT_SIZE} \
95 | --run \
96 | -- /usr/bin/unzip -n -qq #{ARCHIVE_FILE_NAME} 2>&1 \
97 | "
98 |
99 | puts "[#{DateTime.now}] Extracting archive for submission #{submission.token} (#{submission.id}):"
100 | puts command.gsub(/\s+/, " ")
101 | puts
102 |
103 | extract_output = `#{command}`.chomp
104 | puts(extract_output)
105 |
106 | File.delete(archive_file)
107 | end
108 |
109 | def compile
110 | return :success unless submission.language.compile_cmd
111 |
112 | # gsub can be skipped if compile script is used, but is kept for additional security.
113 | compiler_options = submission.compiler_options.to_s.strip.encode("UTF-8", invalid: :replace).gsub(/[$&;<>|`]/, "")
114 |
115 | compile_script = boxdir + "/" + "compile"
116 | File.open(compile_script, "w") { |f| f.write("#{submission.language.compile_cmd % compiler_options}")}
117 |
118 | command = "isolate #{cgroups} \
119 | -s \
120 | -b #{box_id} \
121 | -M #{metadata_file} \
122 | -t 5 \
123 | -x 2 \
124 | -w 10 \
125 | -k #{Config::MAX_STACK_LIMIT} \
126 | -p#{Config::MAX_MAX_PROCESSES_AND_OR_THREADS} \
127 | #{submission.enable_per_process_and_thread_time_limit ? (cgroups.present? ? "--no-cg-timing" : "") : "--cg-timing"} \
128 | #{submission.enable_per_process_and_thread_memory_limit ? "-m " : "--cg-mem="}#{Config::MAX_MEMORY_LIMIT} \
129 | -f #{Config::MAX_MAX_FILE_SIZE} \
130 | -E HOME=#{workdir} \
131 | -E PATH=\"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\" \
132 | -E LANG -E LANGUAGE -E LC_ALL -E JUDGE0_HOMEPAGE -E JUDGE0_SOURCE_CODE -E JUDGE0_MAINTAINER -E JUDGE0_VERSION \
133 | -d /etc:noexec \
134 | --run \
135 | -- /bin/bash compile 2>&1 \
136 | "
137 |
138 | puts "[#{DateTime.now}] Compiling submission #{submission.token} (#{submission.id}):"
139 | puts command.gsub(/\s+/, " ")
140 | puts
141 |
142 | compile_output = `#{command}`.chomp
143 | process_status = $?
144 |
145 | compile_output = nil if compile_output.empty?
146 | submission.compile_output = compile_output
147 |
148 | metadata = get_metadata
149 |
150 | reset_metadata_file
151 | `sudo chown $(whoami): #{compile_script} && rm #{compile_script}`
152 |
153 | return :success if process_status.success?
154 |
155 | if metadata[:status] == "TO"
156 | submission.compile_output = "Compilation time limit exceeded."
157 | end
158 |
159 | submission.finished_at ||= DateTime.now
160 | submission.time = nil
161 | submission.wall_time = nil
162 | submission.memory = nil
163 | submission.stdout = nil
164 | submission.stderr = nil
165 | submission.exit_code = nil
166 | submission.exit_signal = nil
167 | submission.message = nil
168 | submission.status = Status.ce
169 | submission.save
170 |
171 | return :failure
172 | end
173 |
174 | def run
175 | # gsub is mandatory!
176 | command_line_arguments = submission.command_line_arguments.to_s.strip.encode("UTF-8", invalid: :replace).gsub(/[$&;<>|`]/, "")
177 |
178 | run_script = boxdir + "/" + "run"
179 | File.open(run_script, "w") { |f| f.write("#{submission.language.run_cmd} #{command_line_arguments}")}
180 |
181 | command = "isolate #{cgroups} \
182 | -s \
183 | -b #{box_id} \
184 | -M #{metadata_file} \
185 | #{submission.redirect_stderr_to_stdout ? "--stderr-to-stdout" : ""} \
186 | -t #{submission.cpu_time_limit} \
187 | -x #{submission.cpu_extra_time} \
188 | -w #{submission.wall_time_limit} \
189 | -k #{submission.stack_limit} \
190 | -p#{submission.max_processes_and_or_threads} \
191 | #{submission.enable_per_process_and_thread_time_limit ? (cgroups.present? ? "--no-cg-timing" : "") : "--cg-timing"} \
192 | #{submission.enable_per_process_and_thread_memory_limit ? "-m " : "--cg-mem="}#{submission.memory_limit} \
193 | -f #{submission.max_file_size} \
194 | -E HOME=#{workdir} \
195 | -E PATH=\"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\" \
196 | -E LANG -E LANGUAGE -E LC_ALL -E JUDGE0_HOMEPAGE -E JUDGE0_SOURCE_CODE -E JUDGE0_MAINTAINER -E JUDGE0_VERSION \
197 | -d /etc:noexec \
198 | --run \
199 | -- /bin/bash run \
200 | < #{stdin_file} > #{stdout_file} 2> #{stderr_file} \
201 | "
202 |
203 | puts "[#{DateTime.now}] Running submission #{submission.token} (#{submission.id}):"
204 | puts command.gsub(/\s+/, " ")
205 | puts
206 |
207 | `#{command}`
208 |
209 | `sudo chown $(whoami): #{run_script} && rm #{run_script}`
210 | end
211 |
212 | def verify
213 | submission.finished_at ||= DateTime.now
214 |
215 | metadata = get_metadata
216 |
217 | program_stdout = File.read(stdout_file)
218 | program_stdout = nil if program_stdout.empty?
219 |
220 | program_stderr = File.read(stderr_file)
221 | program_stderr = nil if program_stderr.empty?
222 |
223 | submission.time = metadata[:time]
224 | submission.wall_time = metadata[:"time-wall"]
225 | submission.memory = (cgroups.present? ? metadata[:"cg-mem"] : metadata[:"max-rss"])
226 | submission.stdout = program_stdout
227 | submission.stderr = program_stderr
228 | submission.exit_code = metadata[:exitcode].try(:to_i) || 0
229 | submission.exit_signal = metadata[:exitsig].try(:to_i)
230 | submission.message = metadata[:message]
231 | submission.status = determine_status(metadata[:status], submission.exit_signal)
232 |
233 | # After adding support for compiler_options and command_line_arguments
234 | # status "Exec Format Error" will no longer occur because compile and run
235 | # is done inside a dynamically created bash script, thus isolate doesn't call
236 | # execve directily on submission.language.compile_cmd or submission.langauge.run_cmd.
237 | # Consequence of running compile and run through bash script is that when
238 | # target binary is not found then submission gets status "Runtime Error (NZEC)".
239 | #
240 | # I think this is for now O.K. behaviour, but I will leave this if block
241 | # here until I am 100% sure that "Exec Format Error" can be deprecated.
242 | if submission.status == Status.boxerr &&
243 | (
244 | submission.message.to_s.match(/^execve\(.+\): Exec format error$/) ||
245 | submission.message.to_s.match(/^execve\(.+\): No such file or directory$/) ||
246 | submission.message.to_s.match(/^execve\(.+\): Permission denied$/)
247 | )
248 | submission.status = Status.exeerr
249 | end
250 | end
251 |
252 | def cleanup(raise_exception = true)
253 | fix_permissions
254 | `sudo rm -rf #{boxdir}/* #{tmpdir}/*`
255 | `isolate #{cgroups} -b #{box_id} --cleanup`
256 | raise "Cleanup of sandbox #{box_id} failed." if raise_exception && Dir.exists?(workdir)
257 | end
258 |
259 | def reset_metadata_file
260 | `sudo rm -rf #{metadata_file}`
261 | initialize_file(metadata_file)
262 | end
263 |
264 | def fix_permissions
265 | `sudo chown -R $(whoami): #{boxdir}`
266 | end
267 |
268 | def call_callback
269 | return unless submission.callback_url.present?
270 |
271 | serialized_submission = ActiveModelSerializers::SerializableResource.new(
272 | submission,
273 | {
274 | serializer: SubmissionSerializer,
275 | base64_encoded: true,
276 | fields: SubmissionSerializer.default_fields
277 | }
278 | )
279 |
280 | response = HTTParty.put(
281 | submission.callback_url,
282 | body: serialized_submission.to_json,
283 | headers: {
284 | "Content-Type" => "application/json"
285 | },
286 | timeout: 2
287 | )
288 | rescue Exception => e
289 | end
290 |
291 | def get_metadata
292 | metadata = File.read(metadata_file).split("\n").collect do |e|
293 | { e.split(":").first.to_sym => e.split(":")[1..-1].join(":") }
294 | end.reduce({}, :merge)
295 | return metadata
296 | end
297 |
298 | def determine_status(status, exit_signal)
299 | if status == "TO"
300 | return Status.tle
301 | elsif status == "SG"
302 | return Status.find_runtime_error_by_status_code(exit_signal)
303 | elsif status == "RE"
304 | return Status.nzec
305 | elsif status == "XX"
306 | return Status.boxerr
307 | elsif submission.expected_output.nil? || strip(submission.expected_output) == strip(submission.stdout)
308 | return Status.ac
309 | else
310 | return Status.wa
311 | end
312 | end
313 |
314 | def strip(text)
315 | return nil unless text
316 | text.split("\n").collect(&:rstrip).join("\n").rstrip
317 | rescue ArgumentError
318 | return text
319 | end
320 | end
321 |
--------------------------------------------------------------------------------
/docs/api/statuses_and_languages/get_active_and_archived_languages.md:
--------------------------------------------------------------------------------
1 | ## Active and Archived Languages [/languages/all]
2 | ### Get Active and Archived Languages [GET]
3 | Get active and archived languages.
4 |
5 | + Response 200 (application/json)
6 | + Body
7 | [
8 | {
9 | "id": 45,
10 | "name": "Assembly (NASM 2.14.02)",
11 | "is_archived": false
12 | },
13 | {
14 | "id": 2,
15 | "name": "Bash (4.0)",
16 | "is_archived": true
17 | },
18 | {
19 | "id": 1,
20 | "name": "Bash (4.4)",
21 | "is_archived": true
22 | },
23 | {
24 | "id": 46,
25 | "name": "Bash (5.0.0)",
26 | "is_archived": false
27 | },
28 | {
29 | "id": 3,
30 | "name": "Basic (fbc 1.05.0)",
31 | "is_archived": true
32 | },
33 | {
34 | "id": 47,
35 | "name": "Basic (FBC 1.07.1)",
36 | "is_archived": false
37 | },
38 | {
39 | "id": 15,
40 | "name": "C++ (g++ 4.8.5)",
41 | "is_archived": true
42 | },
43 | {
44 | "id": 14,
45 | "name": "C++ (g++ 4.9.4)",
46 | "is_archived": true
47 | },
48 | {
49 | "id": 13,
50 | "name": "C++ (g++ 5.4.0)",
51 | "is_archived": true
52 | },
53 | {
54 | "id": 12,
55 | "name": "C++ (g++ 6.3.0)",
56 | "is_archived": true
57 | },
58 | {
59 | "id": 11,
60 | "name": "C++ (g++ 6.4.0)",
61 | "is_archived": true
62 | },
63 | {
64 | "id": 10,
65 | "name": "C++ (g++ 7.2.0)",
66 | "is_archived": true
67 | },
68 | {
69 | "id": 9,
70 | "name": "C (gcc 4.8.5)",
71 | "is_archived": true
72 | },
73 | {
74 | "id": 8,
75 | "name": "C (gcc 4.9.4)",
76 | "is_archived": true
77 | },
78 | {
79 | "id": 7,
80 | "name": "C (gcc 5.4.0)",
81 | "is_archived": true
82 | },
83 | {
84 | "id": 6,
85 | "name": "C (gcc 6.3.0)",
86 | "is_archived": true
87 | },
88 | {
89 | "id": 5,
90 | "name": "C (gcc 6.4.0)",
91 | "is_archived": true
92 | },
93 | {
94 | "id": 4,
95 | "name": "C (gcc 7.2.0)",
96 | "is_archived": true
97 | },
98 | {
99 | "id": 48,
100 | "name": "C (GCC 7.4.0)",
101 | "is_archived": false
102 | },
103 | {
104 | "id": 52,
105 | "name": "C++ (GCC 7.4.0)",
106 | "is_archived": false
107 | },
108 | {
109 | "id": 49,
110 | "name": "C (GCC 8.3.0)",
111 | "is_archived": false
112 | },
113 | {
114 | "id": 53,
115 | "name": "C++ (GCC 8.3.0)",
116 | "is_archived": false
117 | },
118 | {
119 | "id": 50,
120 | "name": "C (GCC 9.2.0)",
121 | "is_archived": false
122 | },
123 | {
124 | "id": 54,
125 | "name": "C++ (GCC 9.2.0)",
126 | "is_archived": false
127 | },
128 | {
129 | "id": 18,
130 | "name": "Clojure (1.8.0)",
131 | "is_archived": true
132 | },
133 | {
134 | "id": 17,
135 | "name": "C# (mono 5.2.0.224)",
136 | "is_archived": true
137 | },
138 | {
139 | "id": 16,
140 | "name": "C# (mono 5.4.0.167)",
141 | "is_archived": true
142 | },
143 | {
144 | "id": 51,
145 | "name": "C# (Mono 6.6.0.161)",
146 | "is_archived": false
147 | },
148 | {
149 | "id": 55,
150 | "name": "Common Lisp (SBCL 2.0.0)",
151 | "is_archived": false
152 | },
153 | {
154 | "id": 19,
155 | "name": "Crystal (0.23.1)",
156 | "is_archived": true
157 | },
158 | {
159 | "id": 56,
160 | "name": "D (DMD 2.089.1)",
161 | "is_archived": false
162 | },
163 | {
164 | "id": 20,
165 | "name": "Elixir (1.5.1)",
166 | "is_archived": true
167 | },
168 | {
169 | "id": 57,
170 | "name": "Elixir (1.9.4)",
171 | "is_archived": false
172 | },
173 | {
174 | "id": 21,
175 | "name": "Erlang (OTP 20.0)",
176 | "is_archived": true
177 | },
178 | {
179 | "id": 58,
180 | "name": "Erlang (OTP 22.2)",
181 | "is_archived": false
182 | },
183 | {
184 | "id": 44,
185 | "name": "Executable",
186 | "is_archived": false
187 | },
188 | {
189 | "id": 59,
190 | "name": "Fortran (GFortran 9.2.0)",
191 | "is_archived": false
192 | },
193 | {
194 | "id": 60,
195 | "name": "Go (1.13.5)",
196 | "is_archived": false
197 | },
198 | {
199 | "id": 22,
200 | "name": "Go (1.9)",
201 | "is_archived": true
202 | },
203 | {
204 | "id": 24,
205 | "name": "Haskell (ghc 8.0.2)",
206 | "is_archived": true
207 | },
208 | {
209 | "id": 23,
210 | "name": "Haskell (ghc 8.2.1)",
211 | "is_archived": true
212 | },
213 | {
214 | "id": 61,
215 | "name": "Haskell (GHC 8.8.1)",
216 | "is_archived": false
217 | },
218 | {
219 | "id": 25,
220 | "name": "Insect (5.0.0)",
221 | "is_archived": true
222 | },
223 | {
224 | "id": 62,
225 | "name": "Java (OpenJDK 13.0.1)",
226 | "is_archived": false
227 | },
228 | {
229 | "id": 28,
230 | "name": "Java (OpenJDK 7)",
231 | "is_archived": true
232 | },
233 | {
234 | "id": 27,
235 | "name": "Java (OpenJDK 8)",
236 | "is_archived": true
237 | },
238 | {
239 | "id": 26,
240 | "name": "Java (OpenJDK 9 with Eclipse OpenJ9)",
241 | "is_archived": true
242 | },
243 | {
244 | "id": 63,
245 | "name": "JavaScript (Node.js 12.14.0)",
246 | "is_archived": false
247 | },
248 | {
249 | "id": 30,
250 | "name": "JavaScript (nodejs 7.10.1)",
251 | "is_archived": true
252 | },
253 | {
254 | "id": 29,
255 | "name": "JavaScript (nodejs 8.5.0)",
256 | "is_archived": true
257 | },
258 | {
259 | "id": 64,
260 | "name": "Lua (5.3.5)",
261 | "is_archived": false
262 | },
263 | {
264 | "id": 31,
265 | "name": "OCaml (4.05.0)",
266 | "is_archived": true
267 | },
268 | {
269 | "id": 65,
270 | "name": "OCaml (4.09.0)",
271 | "is_archived": false
272 | },
273 | {
274 | "id": 32,
275 | "name": "Octave (4.2.0)",
276 | "is_archived": true
277 | },
278 | {
279 | "id": 66,
280 | "name": "Octave (5.1.0)",
281 | "is_archived": false
282 | },
283 | {
284 | "id": 33,
285 | "name": "Pascal (fpc 3.0.0)",
286 | "is_archived": true
287 | },
288 | {
289 | "id": 67,
290 | "name": "Pascal (FPC 3.0.4)",
291 | "is_archived": false
292 | },
293 | {
294 | "id": 68,
295 | "name": "PHP (7.4.1)",
296 | "is_archived": false
297 | },
298 | {
299 | "id": 43,
300 | "name": "Plain Text",
301 | "is_archived": false
302 | },
303 | {
304 | "id": 69,
305 | "name": "Prolog (GNU Prolog 1.4.5)",
306 | "is_archived": false
307 | },
308 | {
309 | "id": 37,
310 | "name": "Python (2.6.9)",
311 | "is_archived": true
312 | },
313 | {
314 | "id": 70,
315 | "name": "Python (2.7.17)",
316 | "is_archived": false
317 | },
318 | {
319 | "id": 36,
320 | "name": "Python (2.7.9)",
321 | "is_archived": true
322 | },
323 | {
324 | "id": 35,
325 | "name": "Python (3.5.3)",
326 | "is_archived": true
327 | },
328 | {
329 | "id": 34,
330 | "name": "Python (3.6.0)",
331 | "is_archived": true
332 | },
333 | {
334 | "id": 71,
335 | "name": "Python (3.8.1)",
336 | "is_archived": false
337 | },
338 | {
339 | "id": 41,
340 | "name": "Ruby (2.1.9)",
341 | "is_archived": true
342 | },
343 | {
344 | "id": 40,
345 | "name": "Ruby (2.2.6)",
346 | "is_archived": true
347 | },
348 | {
349 | "id": 39,
350 | "name": "Ruby (2.3.3)",
351 | "is_archived": true
352 | },
353 | {
354 | "id": 38,
355 | "name": "Ruby (2.4.0)",
356 | "is_archived": true
357 | },
358 | {
359 | "id": 72,
360 | "name": "Ruby (2.7.0)",
361 | "is_archived": false
362 | },
363 | {
364 | "id": 42,
365 | "name": "Rust (1.20.0)",
366 | "is_archived": true
367 | },
368 | {
369 | "id": 73,
370 | "name": "Rust (1.40.0)",
371 | "is_archived": false
372 | },
373 | {
374 | "id": 74,
375 | "name": "TypeScript (3.7.4)",
376 | "is_archived": false
377 | }
378 | ]
--------------------------------------------------------------------------------
/public/dummy-client.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Judge0 API Dummy Client
6 |
7 |
8 |
9 |
10 |
Judge0 API Dummy Client
11 |
12 | Judge0 API dummy client can be used to try and test features of Judge0 API. Data is not validated on client side.
13 | Client first creates new submission and then fetches submission status every 1500ms.
14 | Client stops fetching submission status after recieving an error or when submission status is not In Queue or Processing.
15 |
16 |
17 | Note that you need to define API URL. This is the base url of some Judge0 API host (e.g. https://api.judge0.com).
18 | Description of network errors can be found in console of your browser, so check that for more information.
19 |
20 |
21 |
22 |
23 | API URL 
24 |
25 | AUTHENTICATION HEADER 
26 |
27 | AUTHENTICATION TOKEN 
28 |
29 | AUTHORIZATION HEADER 
30 |
31 | AUTHORIZATION TOKEN 
32 |
33 | Source Code
34 | null
35 |
44 | Language ID 
45 |
46 | null
47 | Number Of Runs 
48 |
49 | null
50 |
51 | CPU Time Limit 
52 |
53 | null
54 |
55 | CPU Extra Time 
56 |
57 | null
58 |
59 | Wall Time Limit 
60 |
61 | null
62 |
63 | Memory Limit 
64 |
65 | null
66 |
67 | Stack Limit 
68 |
69 | null
70 |
71 | Max Processes And Or Threads 
72 |
73 | null
74 |
75 | Enable Per Process And Thread Time Limit
76 | true
77 | false
78 | null
79 |
80 | Enable Per Process And Thread Memory Limit
81 | true
82 | false
83 | null
84 |
85 | Max File Size 
86 |
87 | null
88 |
89 | Stdin
90 | null
91 |
92 |
93 | Expected Output
94 | null
95 |
96 |
97 | Fields 
98 |
99 |
100 |
101 | Wait for submission
102 |
103 |
104 | Send request with Base64 encoded data
105 |
106 |
107 | Accept response with Base64 encoded data