예제 #1
0
def _run_cmd(cmd, working_dir, timelimit):
    """
    Run a compilation command in the sandbox.
    :param cmd: The command to run.
    :param working_dir: The directory to run the command in.
    :param timelimit: The number of seconds the command is allowed to run.
    :return: The value of stdout as well as any errors that occurred.
    """
    absoluteWorkingDir = os.path.abspath(working_dir)
    cmd = "sudo -H -iu bot_compilation bash -c \"cd " + absoluteWorkingDir + "; " + cmd + "\""
    print(cmd)
    process = subprocess.Popen(cmd,
                               cwd=working_dir,
                               shell=True,
                               stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE,
                               stdin=subprocess.PIPE)

    try:
        rawOut, rawErrors = process.communicate(timeout=timelimit)
        outString = rawOut.decode("utf-8").strip()
        out = outString.split(
            "\n") if outString.isspace() == False and outString != "" else None

        errorsString = rawErrors.decode("utf-8").strip()
        errors = errorsString.split("\n") if errorsString.isspace(
        ) == False and errorsString != "" else None
    except subprocess.TimeoutExpired:
        out = []
        errors = ["Compilation timed out with command %s" % (cmd, )]

    # Clean up any processes that didn't exit cleanly
    util.kill_processes_as("bot_compilation")

    return out, errors
def _run_cmd(cmd, working_dir, timelimit=10, envvars=''):
    """Run a compilation command in an isolated sandbox. Returns the value of stdout as well as any errors that occurred."""
    absolute_working_dir = os.path.abspath(working_dir)
    cmd = 'sudo -H -iu bot_compilation ' + envvars + ' bash -c "cd ' + absolute_working_dir + ' && ' + cmd + '"'
    logging.info("> %s" % cmd)
    process = subprocess.Popen(cmd,
                               cwd=working_dir,
                               shell=True,
                               stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE,
                               stdin=subprocess.PIPE)

    try:
        raw_stdout, raw_strerr = process.communicate(timeout=timelimit)
        str_stdout = raw_stdout.decode("utf-8").strip()
        arr_stdout = str_stdout.split(
            "\n") if not str_stdout.isspace() and str_stdout != "" else []

        str_stderr = raw_strerr.decode("utf-8").strip()
        arr_stderr = str_stderr.split(
            "\n") if not str_stderr.isspace() and str_stderr != "" else []

        if str_stdout is not None and len(str_stdout) > 0:
            logging.info(str_stdout)

        if str_stderr is not None and len(str_stderr) > 0:
            logging.info(str_stderr)
    except subprocess.TimeoutExpired:
        arr_stdout = []
        arr_stderr = ["Compilation timed out while executing command %s" % cmd]

    # Clean up any processes that didn't exit cleanly
    util.kill_processes_as("bot_compilation")

    return arr_stdout, arr_stderr, process.returncode
예제 #3
0
def runGame(environment_parameters, users, offset=0):
    with tempfile.TemporaryDirectory(dir=TEMP_DIR) as temp_dir:
        shutil.copy(ENVIRONMENT, os.path.join(temp_dir, ENVIRONMENT))

        command = [
            "./" + ENVIRONMENT,
            "--results-as-json",
        ]

        for key, value in environment_parameters.items():
            command.append("--{}".format(key))
            if value:
                command.append("{}".format(value))

        # Make sure bots have access to the temp dir as a whole
        # Otherwise, Python can't import modules from the bot dir
        # Based on strace, Python lstat()s the full dir path to the dir it's
        # in, and fails when it tries to lstat the temp dir, which this
        # fixes
        os.chmod(temp_dir, 0o755)

        for user_index, user in enumerate(users):
            bot_command, bot_name, bot_dir = setupParticipant(user_index + offset, user, temp_dir)
            command.append(bot_command)
            command.append("-o")
            command.append(bot_name)
            user['bot_dir'] = bot_dir

        logging.debug("Run game command %s\n" % command)
        logging.debug(command)
        logging.debug("Waiting for game output...\n")
        lines = subprocess.Popen(
            command,
            stdout=subprocess.PIPE).stdout.read().decode('utf-8').split('\n')
        logging.debug("\n-----Here is game output: -----")
        logging.debug("\n".join(lines))
        logging.debug("--------------------------------\n")

        # tempdir will automatically be cleaned up, but we need to do things
        # manually because the bot might have made files it owns
        for user_index, user in enumerate(users):
            # keep any bot logs
            user['bot_logs'] = ''
            log_files_read = 0
            for filename in os.listdir(user['bot_dir']):
                try:
                    _, ext = os.path.splitext(filename)
                    if ext.lower() == '.log':
                        log_files_read += 1
                        user['bot_logs'] += '===== Log file {}\n'.format(filename)
                        with open(os.path.join(user['bot_dir'], filename)) as logfile:
                            user['bot_logs'] += logfile.read(MAX_LOG_FILE_SIZE)
                        user['bot_logs'] += '\n===== End of log {}\n'.format(filename)
                except Exception:
                    # Ignore log and move on if we fail
                    pass

                if log_files_read >= MAX_LOG_FILES:
                    break

            bot_user = "******".format(user_index + offset)
            rm_as_user(bot_user, temp_dir)

            # The processes won't necessarily be automatically cleaned up, so
            # let's do it ourselves
            util.kill_processes_as(bot_user)

        return lines
예제 #4
0
def runGame(width, height, users):
    with tempfile.TemporaryDirectory(dir=TEMP_DIR) as temp_dir:
        shutil.copy(ENVIRONMENT, os.path.join(temp_dir, ENVIRONMENT))

        command = [
            "./" + ENVIRONMENT,
            "-d",
            "{} {}".format(width, height),
            "-q",
            "-o",
        ]

        # Make sure bots have access to the temp dir as a whole
        # Otherwise, Python can't import modules from the bot dir
        # Based on strace, Python lstat()s the full dir path to the dir it's
        # in, and fails when it tries to lstat the temp dir, which this
        # fixes
        os.chmod(temp_dir, 0o755)

        for user_index, user in enumerate(users):
            bot_dir = "{}_{}".format(user["user_id"], user["bot_id"])
            bot_dir = os.path.join(temp_dir, bot_dir)
            os.mkdir(bot_dir)
            archive.unpack(
                backend.storeBotLocally(user["user_id"], user["bot_id"],
                                        bot_dir))

            # Make the start script executable
            os.chmod(os.path.join(bot_dir, RUNFILE), 0o755)

            # Give the bot user ownership of their directory
            # We should set up each user's default group as a group that the
            # worker is also a part of. Then we always have access to their
            # files, but not vice versa.
            # https://superuser.com/questions/102253/how-to-make-files-created-in-a-directory-owned-by-directory-group

            bot_user = "******".format(user_index)
            bot_cgroup = "bot_{}".format(user_index)

            # We want 775 so that the bot can create files still; leading 2
            # is equivalent to g+s which forces new files to be owned by the
            # group
            give_ownership(bot_dir, "bots", 0o2775)

            command.append(
                BOT_COMMAND.format(
                    cgroup=bot_cgroup,
                    bot_dir=bot_dir,
                    bot_user=bot_user,
                    runfile=RUNFILE,
                ))
            command.append("{} v{}".format(user["username"],
                                           user["version_number"]))

        logging.debug("Run game command %s\n" % command)
        logging.debug("Waiting for game output...\n")
        lines = subprocess.Popen(
            command,
            stdout=subprocess.PIPE).stdout.read().decode('utf-8').split('\n')
        logging.debug("\n-----Here is game output: -----")
        logging.debug("\n".join(lines))
        logging.debug("--------------------------------\n")
        # tempdir will automatically be cleaned up, but we need to do things
        # manually because the bot might have made files it owns
        for user_index, user in enumerate(users):
            bot_user = "******".format(user_index)
            rm_as_user(bot_user, temp_dir)

            # The processes won't necessarily be automatically cleaned up, so
            # let's do it ourselves
            util.kill_processes_as(bot_user, "bash")

        return lines
예제 #5
0
def run_game(game):
    with tempfile.TemporaryDirectory(dir=TEMP_DIR) as temp_dir:
        try:
            bomberjam_exec_path = os.path.join(temp_dir, BOMBERJAM_EXEC_NAME)
            game_result_path = os.path.join(temp_dir, 'game.json')
            bot_names_override = ','.join(
                [x.player_name for x in game.players])
            bot_ids_override = ','.join([x.player_id for x in game.players])

            shutil.copy(BOMBERJAM_EXEC_NAME, bomberjam_exec_path)
            command = [
                bomberjam_exec_path, '-q', '-o', game_result_path, '-n',
                bot_names_override, '-i', bot_ids_override
            ]

            # Make sure bots have access to the temp dir as a whole
            # Otherwise, Python can't import modules from the bot dir
            # Based on strace, Python lstat()s the full dir path to the dir it's in,
            # and fails when it tries to lstat the temp dir, which this fixes
            os.chmod(temp_dir, 0o755)

            for player_index, player in enumerate(game.players):
                bot_user = "******" % player_index
                util.rm_everything_owned_in_tmp_and_home_by(bot_user)

                bot_command, bot_dir = setup_participant(player, temp_dir)
                command.append(bot_command)
                player.bot_dir = bot_dir
                game.players_bot_ids[player.player_index] = player.bot_id

            logging.debug("Running game command %s" % command)
            process = subprocess.Popen(command,
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.PIPE)

            try:
                game_timeout_seconds = 10 * 60
                logging.debug("Reading process stdout and stderr")
                out_bytes, err_bytes = process.communicate(
                    timeout=game_timeout_seconds)
                game.game_stdout = out_bytes.decode('utf-8')
                game.game_stderr = err_bytes.decode('utf-8')
            finally:
                process.kill()

            # logging.debug("Process stdout: %s" % game.game_stdout)
            # logging.debug("Process stderr: %s" % game.game_stderr)

            logging.debug("Reading game json results")
            with open(game_result_path, 'r') as f:
                game.game_result = f.read()

            logging.debug("Game result json length: %s characters" %
                          len(game.game_result))

            return game
        finally:
            # tempdir will automatically be cleaned up, but we need to do things
            # manually because the bot might have made files it owns
            for player_index, player in enumerate(game.players):
                bot_user = "******" % player_index

                try:
                    # The processes won't necessarily be automatically cleaned up, so let's do it ourselves
                    util.kill_processes_as(bot_user)
                except:
                    pass

                try:
                    util.rm_as_user(bot_user, temp_dir)
                except:
                    pass

                try:
                    util.rm_everything_owned_in_tmp_and_home_by(bot_user)
                except:
                    pass