# HG changeset patch # User Edouard Tisserant # Date 1540304014 -7200 # Node ID 7ce4e5cf63395e86de2a194de6f00ee1142fd590 # Parent 0a3103cd825d91440e9417b1bb7fb18a85d007b2 Added runtime/spawn_subprocess.py. Force use posix spawn instead of fork, with API similar to subprocess. Using fork in runtime is incompatible with Xenomai, because memory is locked and this can lead to out of memory error. diff -r 0a3103cd825d -r 7ce4e5cf6339 runtime/spawn_subprocess.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/runtime/spawn_subprocess.py Tue Oct 23 16:13:34 2018 +0200 @@ -0,0 +1,132 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# subset of subprocess built-in module using posix_spawn rather than fork. + +import posix_spawn +import os +import signal + +PIPE = "42" + +class Popen(object): + def __init__(self, args,stdin=None, stdout=None): + self.returncode = None + self.stdout = None + self.stdin = None + # TODO: stderr + file_actions = posix_spawn.FileActions() + if stdout is not None: + # child's stdout, child 2 parent pipe + c2pread, c2pwrite = os.pipe() + # attach child's stdout to writing en of c2p pipe + file_actions.add_dup2(c2pwrite, 1) + # close other end + file_actions.add_close(c2pread) + if stdin is not None: + # child's stdin, parent to child pipe + p2cread, p2cwrite = os.pipe() + # attach child's stdin to reading en of p2c pipe + file_actions.add_dup2(p2cread, 0) + # close other end + file_actions.add_close(p2cwrite) + self.pid = posix_spawn.posix_spawnp(args[0], args, file_actions=file_actions) + if stdout is not None: + self.stdout = os.fdopen(c2pread) + os.close(c2pwrite) + if stdin is not None: + self.stdin = os.fdopen(p2cwrite, 'w') + os.close(p2cread) + + def _wait(self): + if self.returncode is None: + self.returncode = os.waitpid(self.pid,0)[1] + + def communicate(self): + if self.stdin is not None: + self.stdin.close() + self.stdin = None + if self.stdout is not None: + stdoutdata = self.stdout.read() + else: + stdoutdata = "" + + # TODO + stderrdata = "" + + self._wait() + if self.stdout is not None: + self.stdout.close() + self.stdout = None + + return (stdoutdata, stderrdata) + + def wait(self): + if self.stdin is not None: + self.stdin.close() + self.stdin = None + self._wait() + if self.stdout is not None: + self.stdout.close() + self.stdout = None + return self.returncode + + def poll(self): + if self.returncode is None: + pid, ret = os.waitpid(self.pid, os.WNOHANG) + if (pid,ret) != (0,0): + self.returncode = ret + + if self.stdin is not None: + self.stdin.close() + self.stdin = None + if self.stdout is not None: + self.stdout.close() + self.stdout = None + + return self.returncode + + def kill(self): + os.kill(self.pid, signal.SIGKILL) + + if self.stdin is not None: + self.stdin.close() + self.stdin = None + if self.stdout is not None: + self.stdout.close() + self.stdout = None + + + +def call(*args): + cmd = [] + if isinstance(args[0], str): + if len(args)==1: + # oversimplified splitting of arguments, + # TODO: care about use of simple and double quotes + cmd = args[0].split() + else: + cmd = args + elif isinstance(args[0], list) and len(args)==1: + cmd = args[0] + else: + raise Exception("Wrong arguments passed to subprocess.call") + pid = posix_spawn.posix_spawnp(cmd[0], cmd) + return os.waitpid(pid,0) + +if __name__ == '__main__': + # unit test + + p = Popen(["tr", "abc", "def"], stdin=PIPE, stdout=PIPE) + p.stdin.write("blah") + p.stdin.close() + print p.stdout.read() + p.wait() + + p = Popen(["tr", "abc", "def"], stdin=PIPE, stdout=PIPE) + p.stdin.write("blah") + print p.communicate() + + call("echo blah0") + call(["echo", "blah1"]) + call("echo", "blah2")