Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
N
nemu3
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
1
Issues
1
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nexedi
nemu3
Commits
09f5b0fe
Commit
09f5b0fe
authored
Jun 30, 2010
by
Martín Ferrari
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
some renaming, prettyfying, etc...
parent
7ebd9b77
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
166 additions
and
145 deletions
+166
-145
src/netns/protocol.py
src/netns/protocol.py
+15
-14
src/netns/subprocess.py
src/netns/subprocess.py
+140
-120
t/test_subprocess.py
t/test_subprocess.py
+11
-11
No files found.
src/netns/protocol.py
View file @
09f5b0fe
...
@@ -71,8 +71,8 @@ class Server(object):
...
@@ -71,8 +71,8 @@ class Server(object):
self
.
closed
=
False
self
.
closed
=
False
# Print debug info
# Print debug info
self
.
debug
=
debug
self
.
debug
=
debug
#
Dictionary
to keep track of started processes
#
Set
to keep track of started processes
self
.
_children
=
dic
t
()
self
.
_children
=
se
t
()
# Buffer and flag for PROC mode
# Buffer and flag for PROC mode
self
.
_proc
=
None
self
.
_proc
=
None
...
@@ -246,8 +246,8 @@ class Server(object):
...
@@ -246,8 +246,8 @@ class Server(object):
self
.
reply
(
221
,
"Sayounara."
);
self
.
reply
(
221
,
"Sayounara."
);
self
.
closed
=
True
self
.
closed
=
True
def
do_PROC_CRTE
(
self
,
cmdname
,
user
,
fi
le
,
*
argv
):
def
do_PROC_CRTE
(
self
,
cmdname
,
user
,
executab
le
,
*
argv
):
self
.
_proc
=
{
'user'
:
user
,
'
file'
:
fi
le
,
'argv'
:
argv
}
self
.
_proc
=
{
'user'
:
user
,
'
executable'
:
executab
le
,
'argv'
:
argv
}
self
.
commands
=
_proc_commands
self
.
commands
=
_proc_commands
self
.
reply
(
200
,
"Entering PROC mode."
)
self
.
reply
(
200
,
"Entering PROC mode."
)
...
@@ -295,7 +295,7 @@ class Server(object):
...
@@ -295,7 +295,7 @@ class Server(object):
def
do_PROC_RUN
(
self
,
cmdname
):
def
do_PROC_RUN
(
self
,
cmdname
):
try
:
try
:
chld
=
netns
.
subprocess
.
Pope
n
(
**
self
.
_proc
)
chld
=
netns
.
subprocess
.
spaw
n
(
**
self
.
_proc
)
except
BaseException
,
e
:
# FIXME
except
BaseException
,
e
:
# FIXME
self
.
reply
(
500
,
"Failure starting process: %s"
%
str
(
e
))
self
.
reply
(
500
,
"Failure starting process: %s"
%
str
(
e
))
self
.
_proc
=
None
self
.
_proc
=
None
...
@@ -317,12 +317,12 @@ class Server(object):
...
@@ -317,12 +317,12 @@ class Server(object):
self
.
reply
(
500
,
"Process does not exist."
)
self
.
reply
(
500
,
"Process does not exist."
)
return
return
if
cmdname
==
'PROC POLL'
:
if
cmdname
==
'PROC POLL'
:
ret
=
self
.
_children
[
pid
].
poll
(
)
ret
=
netns
.
subprocess
.
poll
(
pid
)
else
:
else
:
ret
=
self
.
_children
[
pid
].
wait
(
)
ret
=
netns
.
subprocess
.
wait
(
pid
)
if
ret
!=
None
:
if
ret
!=
None
:
del
self
.
_children
[
pid
]
self
.
_children
.
remove
(
pid
)
self
.
reply
(
200
,
"%d exitcode."
%
ret
)
self
.
reply
(
200
,
"%d exitcode."
%
ret
)
else
:
else
:
self
.
reply
(
450
,
"Not finished yet."
)
self
.
reply
(
450
,
"Not finished yet."
)
...
@@ -330,14 +330,14 @@ class Server(object):
...
@@ -330,14 +330,14 @@ class Server(object):
# Same code for the two commands
# Same code for the two commands
do_PROC_WAIT
=
do_PROC_POLL
do_PROC_WAIT
=
do_PROC_POLL
def
do_PROC_KILL
(
self
,
cmdname
,
pid
,
sig
nal
):
def
do_PROC_KILL
(
self
,
cmdname
,
pid
,
sig
):
if
pid
not
in
self
.
_children
:
if
pid
not
in
self
.
_children
:
self
.
reply
(
500
,
"Process does not exist."
)
self
.
reply
(
500
,
"Process does not exist."
)
return
return
if
signal
:
if
signal
:
self
.
_children
[
pid
].
kill
(
signal
)
os
.
kill
(
pid
,
sig
)
else
:
else
:
self
.
_children
[
pid
].
kill
(
)
os
.
kill
(
pid
,
signal
.
SIGTERM
)
self
.
reply
(
200
,
"Process signalled."
)
self
.
reply
(
200
,
"Process signalled."
)
# def do_IF_LIST(self, cmdname, ifnr = None):
# def do_IF_LIST(self, cmdname, ifnr = None):
...
@@ -416,14 +416,15 @@ class Client(object):
...
@@ -416,14 +416,15 @@ class Client(object):
passfd
.
sendfd
(
self
.
_fd
,
fd
,
"PROC "
+
type
)
passfd
.
sendfd
(
self
.
_fd
,
fd
,
"PROC "
+
type
)
self
.
_read_and_check_reply
()
self
.
_read_and_check_reply
()
def
popen
(
self
,
user
,
fi
le
,
argv
=
None
,
cwd
=
None
,
env
=
None
,
def
spawn
(
self
,
user
,
executab
le
,
argv
=
None
,
cwd
=
None
,
env
=
None
,
stdin
=
None
,
stdout
=
None
,
stderr
=
None
):
stdin
=
None
,
stdout
=
None
,
stderr
=
None
):
"""Start a subprocess in the slave; the interface resembles
"""Start a subprocess in the slave; the interface resembles
subprocess.Popen, but with less functionality. In particular
subprocess.Popen, but with less functionality. In particular
stdin/stdout/stderr can only be None or a open file descriptor."""
stdin/stdout/stderr can only be None or a open file descriptor.
See netns.subprocess.spawn for details."""
params
=
[
"PROC"
,
"CRTE"
,
base64
.
b64encode
(
user
),
params
=
[
"PROC"
,
"CRTE"
,
base64
.
b64encode
(
user
),
base64
.
b64encode
(
fi
le
)]
base64
.
b64encode
(
executab
le
)]
if
argv
!=
None
:
if
argv
!=
None
:
for
i
in
argv
:
for
i
in
argv
:
params
.
append
(
base64
.
b64encode
(
i
))
params
.
append
(
base64
.
b64encode
(
i
))
...
...
src/netns/subprocess.py
View file @
09f5b0fe
...
@@ -3,24 +3,40 @@
...
@@ -3,24 +3,40 @@
import
fcntl
,
grp
,
os
,
pickle
,
pwd
,
signal
,
sys
,
traceback
import
fcntl
,
grp
,
os
,
pickle
,
pwd
,
signal
,
sys
,
traceback
class
Popen
(
object
):
def
spawn
(
user
,
executable
,
argv
,
cwd
=
None
,
env
=
None
,
"""Class that attempts to provide low-leven popen-like behaviour, with the
extra feature of being able to switch user before executing the command."""
def
__init__
(
self
,
user
,
file
,
argv
,
cwd
=
None
,
env
=
None
,
stdin
=
None
,
stdout
=
None
,
stderr
=
None
):
stdin
=
None
,
stdout
=
None
,
stderr
=
None
):
"""Check Python's subprocess.Popen for the intended behaviour. The
"""Forks and execs a program, with stdio redirection and user switching.
extra `user` argument, if not None, specifies a username to run the
The program is specified by `executable', if it does not contain any slash,
command as, including its primary and secondary groups. If a numerical
the PATH environment variable is used to search for the file.
The `user` parameter, if not None, specifies a user name to run the
command as, after setting its primary and secondary groups. If a numerical
UID is given, a reverse lookup is performed to find the user name and
UID is given, a reverse lookup is performed to find the user name and
then set correctly the groups. Note that `stdin`, `stdout`, and
then set correctly the groups.
`stderr` can only be integers representing file descriptors, and that
they are not closed by this class; piping should be handled
To run the program in a different directory than the current one, it should
externally."""
be set in `cwd'.
If specified, `env' replaces the caller's environment with the dictionary
provided.
The standard input, output, and error of the created process will be
redirected to the file descriptors specified by `stdin`, `stdout`, and
`stderr`, respectively. These parameters must be integers or None, in which
case, no redirection will occur. If the value is negative, the respective
file descriptor is closed in the executed program.
Note that the original descriptors are not closed, and that piping should
be handled externally.
Exceptions occurred while trying to set up the environment or executing the
program are propagated to the parent."""
userfd
=
[
stdin
,
stdout
,
stderr
]
userfd
=
[
stdin
,
stdout
,
stderr
]
filtered_userfd
=
filter
(
lambda
x
:
x
!=
None
and
x
>=
0
,
userfd
)
sysfd
=
[
x
.
fileno
()
for
x
in
sys
.
stdin
,
sys
.
stdout
,
sys
.
stderr
]
sysfd
=
[
x
.
fileno
()
for
x
in
sys
.
stdin
,
sys
.
stdout
,
sys
.
stderr
]
# Verify there is no clash
# Verify there is no clash
assert
not
(
set
(
filter
(
None
,
userfd
))
&
set
(
filter
(
None
,
sysfd
)
))
assert
not
(
set
(
sysfd
)
&
set
(
filtered_userfd
))
if
user
!=
None
:
if
user
!=
None
:
if
str
(
user
).
isdigit
():
if
str
(
user
).
isdigit
():
...
@@ -44,9 +60,11 @@ class Popen(object):
...
@@ -44,9 +60,11 @@ class Popen(object):
try
:
try
:
# Set up stdio piping
# Set up stdio piping
for
i
in
range
(
3
):
for
i
in
range
(
3
):
if
userfd
[
i
]
!=
None
:
if
userfd
[
i
]
!=
None
and
userfd
[
i
]
>=
0
:
os
.
dup2
(
userfd
[
i
],
sysfd
[
i
])
os
.
dup2
(
userfd
[
i
],
sysfd
[
i
])
os
.
close
(
userfd
[
i
])
os
.
close
(
userfd
[
i
])
# only in child!
if
userfd
[
i
]
!=
None
and
userfd
[
i
]
<
0
:
os
.
close
(
sysfd
[
i
])
# Set up special control pipe
# Set up special control pipe
os
.
close
(
r
)
os
.
close
(
r
)
fcntl
.
fcntl
(
w
,
fcntl
.
F_SETFD
,
fcntl
.
FD_CLOEXEC
)
fcntl
.
fcntl
(
w
,
fcntl
.
F_SETFD
,
fcntl
.
FD_CLOEXEC
)
...
@@ -95,33 +113,35 @@ class Popen(object):
...
@@ -95,33 +113,35 @@ class Popen(object):
os
.
close
(
r
)
os
.
close
(
r
)
if
s
==
""
:
if
s
==
""
:
self
.
_pid
=
pid
return
pid
return
# It was an error
# It was an error
os
.
waitpid
(
pid
,
0
)
os
.
waitpid
(
pid
,
0
)
raise
pickle
.
loads
(
s
)
exc
=
pickle
.
loads
(
s
)
# XXX: sys.excepthook
raise
exc
@
property
# Used to print extra info in nested exceptions
def
pid
(
self
):
def
_custom_hook
(
t
,
v
,
tb
):
return
self
.
_pid
sys
.
stderr
.
write
(
"wee
\
n
"
)
if
hasattr
(
v
,
"child_traceback"
):
sys
.
stderr
.
write
(
"Nested exception, original traceback "
+
"(most recent call last):
\
n
"
)
sys
.
stderr
.
write
(
v
.
child_traceback
+
(
"-"
*
70
)
+
"
\
n
"
)
sys
.
__excepthook__
(
t
,
v
,
tb
)
def
poll
(
self
):
# XXX: somebody kill me, I deserve it :)
sys
.
excepthook
=
_custom_hook
def
poll
(
pid
):
"""Check if the process already died. Returns the exit code or None if
"""Check if the process already died. Returns the exit code or None if
the process is still alive."""
the process is still alive."""
r
=
os
.
waitpid
(
self
.
_
pid
,
os
.
WNOHANG
)
r
=
os
.
waitpid
(
pid
,
os
.
WNOHANG
)
if
r
[
0
]:
if
r
[
0
]:
del
self
.
_pid
return
r
[
1
]
return
r
[
1
]
return
None
return
None
def
wait
(
self
):
def
wait
(
pid
):
"""Wait for process to die and return the exit code."""
"""Wait for process to die and return the exit code."""
r
=
os
.
waitpid
(
self
.
_pid
,
0
)[
1
]
return
os
.
waitpid
(
pid
,
0
)[
1
]
del
self
.
_pid
return
r
def
kill
(
self
,
sig
=
signal
.
SIGTERM
):
"""Kill the process with the specified signal. Note that the process
still needs to be waited for to avoid zombies."""
os
.
kill
(
self
.
_pid
,
sig
)
t/test_subprocess.py
View file @
09f5b0fe
...
@@ -56,32 +56,32 @@ class TestSubprocess(unittest.TestCase):
...
@@ -56,32 +56,32 @@ class TestSubprocess(unittest.TestCase):
@
test_util
.
skipUnless
(
os
.
getuid
()
==
0
,
"Test requires root privileges"
)
@
test_util
.
skipUnless
(
os
.
getuid
()
==
0
,
"Test requires root privileges"
)
def
test_popen_chuser
(
self
):
def
test_popen_chuser
(
self
):
user
=
'nobody'
user
=
'nobody'
p
=
netns
.
subprocess
.
Popen
(
user
,
'/bin/sleep'
,
[
'/bin/sleep'
,
'10
00'
])
p
id
=
netns
.
subprocess
.
spawn
(
user
,
'/bin/sleep'
,
[
'/bin/sleep'
,
'1
00'
])
self
.
_check_ownership
(
user
,
p
.
p
id
)
self
.
_check_ownership
(
user
,
pid
)
p
.
kill
(
)
os
.
kill
(
pid
,
signal
.
SIGTERM
)
self
.
assertEquals
(
p
.
wait
(
),
signal
.
SIGTERM
)
self
.
assertEquals
(
netns
.
subprocess
.
wait
(
pid
),
signal
.
SIGTERM
)
def
test_popen_basic
(
self
):
def
test_popen_basic
(
self
):
# User does not exist
# User does not exist
self
.
assertRaises
(
ValueError
,
netns
.
subprocess
.
Pope
n
,
self
.
assertRaises
(
ValueError
,
netns
.
subprocess
.
spaw
n
,
self
.
nouser
,
'/bin/sleep'
,
[
'/bin/sleep'
,
'1000'
])
self
.
nouser
,
'/bin/sleep'
,
[
'/bin/sleep'
,
'1000'
])
self
.
assertRaises
(
ValueError
,
netns
.
subprocess
.
Pope
n
,
self
.
assertRaises
(
ValueError
,
netns
.
subprocess
.
spaw
n
,
self
.
nouid
,
'/bin/sleep'
,
[
'/bin/sleep'
,
'1000'
])
self
.
nouid
,
'/bin/sleep'
,
[
'/bin/sleep'
,
'1000'
])
# Invalid CWD: it is a file
# Invalid CWD: it is a file
self
.
assertRaises
(
OSError
,
netns
.
subprocess
.
Pope
n
,
self
.
assertRaises
(
OSError
,
netns
.
subprocess
.
spaw
n
,
None
,
'/bin/sleep'
,
None
,
cwd
=
'/bin/sleep'
)
None
,
'/bin/sleep'
,
None
,
cwd
=
'/bin/sleep'
)
# Invalid CWD: does not exist
# Invalid CWD: does not exist
self
.
assertRaises
(
OSError
,
netns
.
subprocess
.
Pope
n
,
self
.
assertRaises
(
OSError
,
netns
.
subprocess
.
spaw
n
,
None
,
'/bin/sleep'
,
None
,
cwd
=
self
.
nofile
)
None
,
'/bin/sleep'
,
None
,
cwd
=
self
.
nofile
)
# Exec failure
# Exec failure
self
.
assertRaises
(
OSError
,
netns
.
subprocess
.
Pope
n
,
self
.
assertRaises
(
OSError
,
netns
.
subprocess
.
spaw
n
,
None
,
self
.
nofile
,
None
)
None
,
self
.
nofile
,
None
)
# Test that the environment is cleared: sleep should not be found
# Test that the environment is cleared: sleep should not be found
# XXX: This should be a python bug: if I don't set PATH explicitly, it
# XXX: This should be a python bug: if I don't set PATH explicitly, it
# uses a default search path
# uses a default search path
self
.
assertRaises
(
OSError
,
netns
.
subprocess
.
Pope
n
,
self
.
assertRaises
(
OSError
,
netns
.
subprocess
.
spaw
n
,
None
,
'sleep'
,
None
,
env
=
{
'PATH'
:
''
})
None
,
'sleep'
,
None
,
env
=
{
'PATH'
:
''
})
#p = netns.subprocess.
Pope
n(None, '/bin/sleep', ['/bin/sleep', '1000'],
#p = netns.subprocess.
spaw
n(None, '/bin/sleep', ['/bin/sleep', '1000'],
# cwd = '/', env = [])
# cwd = '/', env = [])
# FIXME: tests fds
# FIXME: tests fds
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment