Exemplo n.º 1
0
    def create_compile_process(self, args: List[str]) -> TracedPopen:
        # Some languages may insist on providing certain functionality (e.g. colored highlighting of errors) if they
        # feel they are connected to a terminal. Some are more persistent than others in enforcing this, so this hack
        # aims to provide a convincing-enough lie to the runtime so that it starts singing in color.
        #
        # Emulate the streams of a process connected to a terminal: stdin, stdout, and stderr are all ptys.
        _master, _slave = pty.openpty()
        # Some runtimes *cough cough* Swift *cough cough* actually check the environment variables too.
        env = self.get_compile_env() or os.environ.copy()
        env['TERM'] = 'xterm'
        # Instruct compilers to put their temporary files into the submission directory,
        # so that we can allow it as writeable, rather than of all of /tmp.
        assert self._dir is not None
        env['TMPDIR'] = self._dir

        proc = TracedPopen(
            [utf8bytes(a) for a in args],
            **{
                'executable': utf8bytes(args[0]),
                'security': self.get_compiler_security(),
                'stderr': _slave,
                'stdout': _slave,
                'stdin': _slave,
                'cwd': utf8bytes(self._dir),
                'env': env,
                'nproc': -1,
                'fsize': self.executable_size,
                'time': self.compiler_time_limit or 0,
                'memory': 0,
                **self.get_compile_popen_kwargs(),
            },
        )

        class io_error_wrapper:
            """
            Wrap pty-related IO errors so that we don't crash Popen.communicate()
            """
            def __init__(self, io: IO) -> None:
                self.io = io

            def read(self, *args, **kwargs):
                try:
                    return self.io.read(*args, **kwargs)
                except (IOError, OSError):
                    return b''

            def __getattr__(self, attr):
                return getattr(self.io, attr)

        # Since stderr and stdout are connected to the same slave pty, proc.stderr will contain the merged stdout
        # of the process as well.
        proc.stderr = io_error_wrapper(os.fdopen(_master,
                                                 'rb'))  # type: ignore

        os.close(_slave)
        return proc
Exemplo n.º 2
0
    def launch(self, *args, **kwargs) -> TracedPopen:
        assert self._dir is not None
        for src, dst in kwargs.get('symlinks', {}).items():
            src = os.path.abspath(os.path.join(self._dir, src))
            # Disallow the creation of symlinks outside the submission directory.
            if os.path.commonprefix([src, self._dir]) == self._dir:
                # If a link already exists under this name, it's probably from a
                # previous case, but might point to something different.
                if os.path.islink(src):
                    os.unlink(src)
                os.symlink(dst, src)
            else:
                raise InternalError(
                    'cannot symlink outside of submission directory')

        agent = self._file('setbufsize.so')
        # Hardcode the ABIs for different executors for now
        if self.name == 'CPP17X':
            shutil.copyfile(setbufsize32_path, agent)
        elif self.name == 'TUR':
            shutil.copyfile(setbufsize86_path, agent)
        else:
            shutil.copyfile(setbufsize_path, agent)
        env = {
            # Forward LD_LIBRARY_PATH for systems (e.g. Android Termux) that require
            # it to find shared libraries
            'LD_LIBRARY_PATH': os.environ.get('LD_LIBRARY_PATH', ''),
            'LD_PRELOAD': agent,
            'CPTBOX_STDOUT_BUFFER_SIZE': kwargs.get('stdout_buffer_size'),
            'CPTBOX_STDERR_BUFFER_SIZE': kwargs.get('stderr_buffer_size'),
        }
        env.update(self.get_env())

        executable = self.get_executable()
        assert executable is not None
        return TracedPopen(
            [utf8bytes(a) for a in self.get_cmdline(**kwargs) + list(args)],
            executable=utf8bytes(executable),
            security=self.get_security(launch_kwargs=kwargs),
            address_grace=self.get_address_grace(),
            data_grace=self.data_grace,
            personality=self.personality,
            time=kwargs.get('time', 0),
            memory=kwargs.get('memory', 0),
            wall_time=kwargs.get('wall_time'),
            stdin=kwargs.get('stdin'),
            stdout=kwargs.get('stdout'),
            stderr=kwargs.get('stderr'),
            env=env,
            cwd=utf8bytes(self._dir),
            nproc=self.get_nproc(),
            fsize=self.fsize,
        )