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
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
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
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