Reputation: 1139
I need to run an executable and pass an fd to it which is from os.pipe, so that the parent process can read results from it. I don't want to use stdout for that because I need to see log messages in the terminal of the child, so I'm trying to implement a pipe for that purpose.
I can't use fork and multiprocessing.Process, because furthermore that child process is supposed to run via sudo as opposed to the parent, which should not run with root permissions.
No other process should be allowed to see what is in that pipe.
This is ospipeexample.py
and the isolated problem. For simplicity, this minimal example is squeezed into one single file with two branches.
#!/usr/bin/python3
import os
import pickle
import sys
if len(sys.argv) == 2:
# write in the child process started via os.system
print('child opening', sys.argv[-1])
w = os.fdopen(int(sys.argv[-1]), 'wb')
w.write(pickle.dumps(1))
w.write(b'\n')
w.flush()
else:
# read in the parent process
rfd, wfd = os.pipe()
print('parent created', rfd, wfd)
r = os.fdopen(rfd, 'rb')
os.system('sudo python3 ospipeexample.py %d' % wfd)
print(pickle.loads(r.readline()))
my output from running python3 ospipeexample.py
is (EDIT: this has been solved, see below):
parent created 3 4
child opening 4
Traceback (most recent call last):
File "/mnt/data/Code/ospipeexample.py", line 8, in <module>
os.fdopen(int(sys.argv[-1]), 'wb')
File "/usr/lib/python3.9/os.py", line 1023, in fdopen
return io.open(fd, *args, **kwargs)
OSError: [Errno 9] Bad file descriptor
expected output:
parent created 3 4
child opening 4
1
here is the output of ps -aux --forest
mango 12102 0.2 0.0 14480 9236 pts/2 S+ 15:40 0:00 | \_ python3 ospipeexample.py
root 12103 0.0 0.0 16184 7468 pts/2 S+ 15:40 0:00 | \_ sudo python3 ospipeexample.py 4
root 12104 0.2 0.0 14476 8816 pts/2 S+ 15:40 0:00 | \_ python3 ospipeexample.py 4
what would be an alternative, is there any way to set up shared memory or something using python and pass it to the child process via os.system or subprocess.Popen?
Upvotes: 0
Views: 1520
Reputation: 58731
Your ultimate child process' call to os.fdopen
fails because sudo
already closed that fd (as well as the inherited read-end fd) before spawning that child.
This is a safety measure built in to sudo
, which normally "close[s] all open file descriptors other than standard input, standard output and standard error."
(see the sudo 1.8.3 manual)
You'll could (dangerously) change this behavior, or access the pipe some other way (as you've done by opening the special Linux procfs representation of the parent pipe), or otherwise contrive some other IPC.
Upvotes: 1
Reputation: 1139
The child process probably checked in /proc/child-pid/fd, so I passed the path of the fd as argument instead.
#!/usr/bin/python3
import os
import pickle
import sys
import subprocess
if len(sys.argv) == 2:
# write in the child process started via os.system
print('child writing 1 to', sys.argv[-1])
w = open(sys.argv[1], 'wb')
w.write(pickle.dumps(1))
w.write(b'\n')
w.flush()
else:
# read in the parent process
rfd, wfd = os.pipe()
print('parent created', rfd, wfd)
r = os.fdopen(rfd, 'rb')
path = f'/proc/{os.getpid()}/fd/{wfd}'
subprocess.Popen(['sudo', 'python3', 'ospipeexample.py', path])
print(pickle.loads(r.readline()))
parent created 3 4
child writing 1 to /proc/12501/fd/4
1
So it works now.
However, via opening a new unrelated terminal I can get those contents as well though, which should not be possible
>>> p = open('/proc/12501/fd/4', 'rb')
>>> p.readline()
b'\x80\x04K\x01.\n'
>>> pickle.loads(b'\x80\x04K\x01.\n')
1
Upvotes: 1