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
ba619ec3
Commit
ba619ec3
authored
Nov 15, 2023
by
Tom Niget
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
nemu works in python 3
parent
f8914e80
Changes
9
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
340 additions
and
225 deletions
+340
-225
src/nemu/compat.py
src/nemu/compat.py
+30
-0
src/nemu/environ.py
src/nemu/environ.py
+57
-28
src/nemu/interface.py
src/nemu/interface.py
+2
-2
src/nemu/node.py
src/nemu/node.py
+2
-1
src/nemu/protocol.py
src/nemu/protocol.py
+143
-112
src/nemu/subprocess_.py
src/nemu/subprocess_.py
+65
-48
test/test_protocol.py
test/test_protocol.py
+12
-9
test/test_subprocess.py
test/test_subprocess.py
+28
-24
test/test_util.py
test/test_util.py
+1
-1
No files found.
src/nemu/compat.py
0 → 100644
View file @
ba619ec3
import
os
import
socket
as
pysocket
def
pipe
()
->
tuple
[
int
,
int
]:
a
,
b
=
os
.
pipe2
(
0
)
os
.
set_inheritable
(
a
,
True
)
os
.
set_inheritable
(
b
,
True
)
return
a
,
b
def
socket
(
*
args
,
**
kwargs
)
->
pysocket
.
socket
:
s
=
pysocket
.
socket
(
*
args
,
**
kwargs
)
s
.
set_inheritable
(
True
)
return
s
def
socketpair
(
*
args
,
**
kwargs
)
->
tuple
[
pysocket
.
socket
,
pysocket
.
socket
]:
a
,
b
=
pysocket
.
socketpair
(
*
args
,
**
kwargs
)
a
.
set_inheritable
(
True
)
b
.
set_inheritable
(
True
)
return
a
,
b
def
fromfd
(
*
args
,
**
kwargs
)
->
pysocket
.
socket
:
s
=
pysocket
.
fromfd
(
*
args
,
**
kwargs
)
s
.
set_inheritable
(
True
)
return
s
def
fdopen
(
*
args
,
**
kwargs
)
->
pysocket
.
socket
:
s
=
os
.
fdopen
(
*
args
,
**
kwargs
)
s
.
set_inheritable
(
True
)
return
s
\ No newline at end of file
src/nemu/environ.py
View file @
ba619ec3
...
@@ -25,8 +25,12 @@ import subprocess
...
@@ -25,8 +25,12 @@ import subprocess
import
sys
import
sys
import
syslog
import
syslog
from
syslog
import
LOG_ERR
,
LOG_WARNING
,
LOG_NOTICE
,
LOG_INFO
,
LOG_DEBUG
from
syslog
import
LOG_ERR
,
LOG_WARNING
,
LOG_NOTICE
,
LOG_INFO
,
LOG_DEBUG
from
typing
import
TypeVar
,
Callable
__all__
=
[
"IP_PATH"
,
"TC_PATH"
,
"BRCTL_PATH"
,
"SYSCTL_PATH"
,
"HZ"
]
__all__
=
[
"IP_PATH"
,
"TC_PATH"
,
"BRCTL_PATH"
,
"SYSCTL_PATH"
,
"HZ"
]
from
nemu
import
compat
__all__
+=
[
"TCPDUMP_PATH"
,
"NETPERF_PATH"
,
"XAUTH_PATH"
,
"XDPYINFO_PATH"
]
__all__
+=
[
"TCPDUMP_PATH"
,
"NETPERF_PATH"
,
"XAUTH_PATH"
,
"XDPYINFO_PATH"
]
__all__
+=
[
"execute"
,
"backticks"
,
"eintr_wrapper"
]
__all__
+=
[
"execute"
,
"backticks"
,
"eintr_wrapper"
]
__all__
+=
[
"find_listen_port"
]
__all__
+=
[
"find_listen_port"
]
...
@@ -35,14 +39,14 @@ __all__ += ["set_log_level", "logger"]
...
@@ -35,14 +39,14 @@ __all__ += ["set_log_level", "logger"]
__all__
+=
[
"error"
,
"warning"
,
"notice"
,
"info"
,
"debug"
]
__all__
+=
[
"error"
,
"warning"
,
"notice"
,
"info"
,
"debug"
]
def
find_bin
(
name
,
extra_path
=
None
):
def
find_bin
(
name
,
extra_path
=
None
):
"""Try hard to find the location of needed programs."""
"""Try hard to find the location of needed programs."""
search
=
[]
search
=
[]
if
"PATH"
in
os
.
environ
:
if
"PATH"
in
os
.
environ
:
search
+=
os
.
environ
[
"PATH"
].
split
(
":"
)
search
+=
os
.
environ
[
"PATH"
].
split
(
":"
)
search
.
extend
(
os
.
path
.
join
(
x
,
y
)
search
.
extend
(
os
.
path
.
join
(
x
,
y
)
for
x
in
(
"/"
,
"/usr/"
,
"/usr/local/"
)
for
x
in
(
"/"
,
"/usr/"
,
"/usr/local/"
)
for
y
in
(
"bin"
,
"sbin"
))
for
y
in
(
"bin"
,
"sbin"
))
if
extra_path
:
if
extra_path
:
search
+=
extra_path
search
+=
extra_path
...
@@ -52,16 +56,18 @@ def find_bin(name, extra_path = None):
...
@@ -52,16 +56,18 @@ def find_bin(name, extra_path = None):
return
path
return
path
return
None
return
None
def
find_bin_or_die
(
name
,
extra_path
=
None
):
def
find_bin_or_die
(
name
,
extra_path
=
None
):
"""Try hard to find the location of needed programs; raise on failure."""
"""Try hard to find the location of needed programs; raise on failure."""
res
=
find_bin
(
name
,
extra_path
)
res
=
find_bin
(
name
,
extra_path
)
if
not
res
:
if
not
res
:
raise
RuntimeError
(
"Cannot find `%s', impossible to continue."
%
name
)
raise
RuntimeError
(
"Cannot find `%s', impossible to continue."
%
name
)
return
res
return
res
IP_PATH
=
find_bin_or_die
(
"ip"
)
TC_PATH
=
find_bin_or_die
(
"tc"
)
IP_PATH
=
find_bin_or_die
(
"ip"
)
BRCTL_PATH
=
find_bin_or_die
(
"brctl"
)
TC_PATH
=
find_bin_or_die
(
"tc"
)
BRCTL_PATH
=
find_bin_or_die
(
"brctl"
)
SYSCTL_PATH
=
find_bin_or_die
(
"sysctl"
)
SYSCTL_PATH
=
find_bin_or_die
(
"sysctl"
)
# Optional tools
# Optional tools
...
@@ -78,21 +84,23 @@ try:
...
@@ -78,21 +84,23 @@ try:
os
.
stat
(
"/sys/class/net"
)
os
.
stat
(
"/sys/class/net"
)
except
:
except
:
raise
RuntimeError
(
"Sysfs does not seem to be mounted, impossible to "
+
raise
RuntimeError
(
"Sysfs does not seem to be mounted, impossible to "
+
"continue."
)
"continue."
)
def
execute
(
cmd
):
def
execute
(
cmd
:
list
[
str
]):
"""Execute a command, if the return value is non-zero, raise an exception.
"""Execute a command, if the return value is non-zero, raise an exception.
Raises:
Raises:
RuntimeError: the command was unsuccessful (return code != 0).
RuntimeError: the command was unsuccessful (return code != 0).
"""
"""
debug
(
"execute(%s)"
%
cmd
)
debug
(
"execute(%s)"
%
cmd
)
proc
=
subprocess
.
Popen
(
cmd
,
stdout
=
subprocess
.
DEVNULL
,
stderr
=
subprocess
.
PIPE
)
proc
=
subprocess
.
Popen
(
cmd
,
stdout
=
subprocess
.
DEVNULL
,
stderr
=
subprocess
.
PIPE
)
_
,
err
=
proc
.
communicate
()
_
,
err
=
proc
.
communicate
()
if
proc
.
returncode
!=
0
:
if
proc
.
returncode
!=
0
:
raise
RuntimeError
(
"Error executing `%s': %s"
%
(
" "
.
join
(
cmd
),
err
))
raise
RuntimeError
(
"Error executing `%s': %s"
%
(
" "
.
join
(
cmd
),
err
))
def
backticks
(
cmd
):
def
backticks
(
cmd
:
list
[
str
])
->
str
:
"""Execute a command and capture its output.
"""Execute a command and capture its output.
If the return value is non-zero, raise an exception.
If the return value is non-zero, raise an exception.
...
@@ -102,30 +110,35 @@ def backticks(cmd):
...
@@ -102,30 +110,35 @@ def backticks(cmd):
RuntimeError: the command was unsuccessful (return code != 0).
RuntimeError: the command was unsuccessful (return code != 0).
"""
"""
debug
(
"backticks(%s)"
%
cmd
)
debug
(
"backticks(%s)"
%
cmd
)
proc
=
subprocess
.
Popen
(
cmd
,
stdout
=
subprocess
.
PIPE
,
proc
=
subprocess
.
Popen
(
cmd
,
stdout
=
subprocess
.
PIPE
,
stderr
=
subprocess
.
PIPE
)
stderr
=
subprocess
.
PIPE
)
out
,
err
=
proc
.
communicate
()
out
,
err
=
proc
.
communicate
()
if
proc
.
returncode
!=
0
:
if
proc
.
returncode
!=
0
:
raise
RuntimeError
(
"Error executing `%s': %s"
%
(
" "
.
join
(
cmd
),
err
))
raise
RuntimeError
(
"Error executing `%s': %s"
%
(
" "
.
join
(
cmd
),
err
))
return
out
.
decode
(
"utf-8"
)
return
out
.
decode
(
"utf-8"
)
def
eintr_wrapper
(
func
,
*
args
):
T
=
TypeVar
(
"T"
)
def
eintr_wrapper
(
func
:
Callable
[...,
T
],
*
args
)
->
T
:
"Wraps some callable with a loop that retries on EINTR."
"Wraps some callable with a loop that retries on EINTR."
while
True
:
while
True
:
try
:
try
:
return
func
(
*
args
)
return
func
(
*
args
)
except
OSError
as
ex
:
# pragma: no cover
except
OSError
as
ex
:
# pragma: no cover
if
ex
.
errno
==
errno
.
EINTR
:
if
ex
.
errno
==
errno
.
EINTR
:
continue
continue
raise
raise
except
IOError
as
ex
:
# pragma: no cover
except
IOError
as
ex
:
# pragma: no cover
if
ex
.
errno
==
errno
.
EINTR
:
if
ex
.
errno
==
errno
.
EINTR
:
continue
continue
raise
raise
def
find_listen_port
(
family
=
socket
.
AF_INET
,
type
=
socket
.
SOCK_STREAM
,
proto
=
0
,
addr
=
"127.0.0.1"
,
min_port
=
1
,
max_port
=
65535
):
def
find_listen_port
(
family
=
socket
.
AF_INET
,
type
=
socket
.
SOCK_STREAM
,
sock
=
socket
.
socket
(
family
,
type
,
proto
)
proto
=
0
,
addr
=
"127.0.0.1"
,
min_port
=
1
,
max_port
=
65535
):
sock
=
compat
.
socket
(
family
,
type
,
proto
)
for
port
in
range
(
min_port
,
max_port
+
1
):
for
port
in
range
(
min_port
,
max_port
+
1
):
try
:
try
:
sock
.
bind
((
addr
,
port
))
sock
.
bind
((
addr
,
port
))
...
@@ -134,44 +147,50 @@ def find_listen_port(family = socket.AF_INET, type = socket.SOCK_STREAM,
...
@@ -134,44 +147,50 @@ def find_listen_port(family = socket.AF_INET, type = socket.SOCK_STREAM,
pass
pass
raise
RuntimeError
(
"Cannot find an usable port in the range specified"
)
raise
RuntimeError
(
"Cannot find an usable port in the range specified"
)
# Logging
# Logging
_log_level
=
LOG_
DEBU
G
_log_level
=
LOG_
WARNIN
G
_log_use_syslog
=
False
_log_use_syslog
=
False
_log_stream
=
sys
.
stderr
_log_stream
=
sys
.
stderr
_log_syslog_opts
=
()
_log_syslog_opts
=
()
_log_pid
=
os
.
getpid
()
_log_pid
=
os
.
getpid
()
def
set_log_level
(
level
):
def
set_log_level
(
level
):
"Sets the log level for console messages, does not affect syslog logging."
"Sets the log level for console messages, does not affect syslog logging."
global
_log_level
global
_log_level
assert
level
>
LOG_ERR
and
level
<=
LOG_DEBUG
assert
level
>
LOG_ERR
and
level
<=
LOG_DEBUG
_log_level
=
level
_log_level
=
level
def
set_log_output
(
stream
):
def
set_log_output
(
stream
):
"Redirect console messages to the provided stream."
"Redirect console messages to the provided stream."
global
_log_stream
global
_log_stream
assert
hasattr
(
stream
,
"write"
)
and
hasattr
(
stream
,
"flush"
)
assert
hasattr
(
stream
,
"write"
)
and
hasattr
(
stream
,
"flush"
)
_log_stream
=
stream
_log_stream
=
stream
def
log_use_syslog
(
use
=
True
,
ident
=
None
,
logopt
=
0
,
facility
=
syslog
.
LOG_USER
):
def
log_use_syslog
(
use
=
True
,
ident
=
None
,
logopt
=
0
,
facility
=
syslog
.
LOG_USER
):
"Enable or disable the use of syslog for logging messages."
"Enable or disable the use of syslog for logging messages."
global
_log_use_syslog
,
_log_syslog_opts
global
_log_use_syslog
,
_log_syslog_opts
_log_syslog_opts
=
(
ident
,
logopt
,
facility
)
_log_syslog_opts
=
(
ident
,
logopt
,
facility
)
_log_use_syslog
=
use
_log_use_syslog
=
use
_init_log
()
_init_log
()
def
_init_log
():
def
_init_log
():
if
not
_log_use_syslog
:
if
not
_log_use_syslog
:
syslog
.
closelog
()
syslog
.
closelog
()
return
return
(
ident
,
logopt
,
facility
)
=
_log_syslog_opts
(
ident
,
logopt
,
facility
)
=
_log_syslog_opts
if
not
ident
:
if
not
ident
:
#ident = os.path.basename(sys.argv[0])
#
ident = os.path.basename(sys.argv[0])
ident
=
"nemu"
ident
=
"nemu"
syslog
.
openlog
(
"%s[%d]"
%
(
ident
,
os
.
getpid
()),
logopt
,
facility
)
syslog
.
openlog
(
"%s[%d]"
%
(
ident
,
os
.
getpid
()),
logopt
,
facility
)
info
(
"Syslog logging started"
)
info
(
"Syslog logging started"
)
def
logger
(
priority
,
message
):
def
logger
(
priority
,
message
):
"Print a log message in syslog, console or both."
"Print a log message in syslog, console or both."
if
_log_use_syslog
:
if
_log_use_syslog
:
...
@@ -183,27 +202,37 @@ def logger(priority, message):
...
@@ -183,27 +202,37 @@ def logger(priority, message):
return
return
eintr_wrapper
(
_log_stream
.
write
,
eintr_wrapper
(
_log_stream
.
write
,
"[%d] %s
\
n
"
%
(
os
.
getpid
(),
message
.
rstrip
()))
"[%d] %s
\
n
"
%
(
os
.
getpid
(),
message
.
rstrip
()))
_log_stream
.
flush
()
_log_stream
.
flush
()
def
error
(
message
):
def
error
(
message
):
logger
(
LOG_ERR
,
message
)
logger
(
LOG_ERR
,
message
)
def
warning
(
message
):
def
warning
(
message
):
logger
(
LOG_WARNING
,
message
)
logger
(
LOG_WARNING
,
message
)
def
notice
(
message
):
def
notice
(
message
):
logger
(
LOG_NOTICE
,
message
)
logger
(
LOG_NOTICE
,
message
)
def
info
(
message
):
def
info
(
message
):
logger
(
LOG_INFO
,
message
)
logger
(
LOG_INFO
,
message
)
def
debug
(
message
):
def
debug
(
message
):
logger
(
LOG_DEBUG
,
message
)
logger
(
LOG_DEBUG
,
message
)
def
_custom_hook
(
tipe
,
value
,
traceback
):
# pragma: no cover
def
_custom_hook
(
tipe
,
value
,
traceback
):
# pragma: no cover
"""Custom exception hook, to print nested exceptions information."""
"""Custom exception hook, to print nested exceptions information."""
if
hasattr
(
value
,
"child_traceback"
):
if
hasattr
(
value
,
"child_traceback"
):
sys
.
stderr
.
write
(
"Nested exception, original traceback "
+
sys
.
stderr
.
write
(
"Nested exception, original traceback "
+
"(most recent call last):
\
n
"
)
"(most recent call last):
\
n
"
)
sys
.
stderr
.
write
(
value
.
child_traceback
+
(
"-"
*
70
)
+
"
\
n
"
)
sys
.
stderr
.
write
(
value
.
child_traceback
+
(
"-"
*
70
)
+
"
\
n
"
)
sys
.
__excepthook__
(
tipe
,
value
,
traceback
)
sys
.
__excepthook__
(
tipe
,
value
,
traceback
)
sys
.
excepthook
=
_custom_hook
sys
.
excepthook
=
_custom_hook
src/nemu/interface.py
View file @
ba619ec3
...
@@ -40,7 +40,7 @@ class Interface(object):
...
@@ -40,7 +40,7 @@ class Interface(object):
def
_gen_if_name
():
def
_gen_if_name
():
n
=
Interface
.
_gen_next_id
()
n
=
Interface
.
_gen_next_id
()
# Max 15 chars
# Max 15 chars
return
"NETNSif-%.4x%.3x"
%
(
os
.
getpid
()
%
0xffff
,
n
)
return
"NETNSif-%.4x%.3x"
%
(
os
.
getpid
()
&
0xffff
,
n
)
def
__init__
(
self
,
index
):
def
__init__
(
self
,
index
):
self
.
_idx
=
index
self
.
_idx
=
index
...
@@ -386,7 +386,7 @@ class Switch(ExternalInterface):
...
@@ -386,7 +386,7 @@ class Switch(ExternalInterface):
def
_gen_br_name
():
def
_gen_br_name
():
n
=
Switch
.
_gen_next_id
()
n
=
Switch
.
_gen_next_id
()
# Max 15 chars
# Max 15 chars
return
"NETNSbr-%.4x%.3x"
%
(
os
.
getpid
()
%
0xffff
,
n
)
return
"NETNSbr-%.4x%.3x"
%
(
os
.
getpid
()
&
0xffff
,
n
)
def
__init__
(
self
,
**
args
):
def
__init__
(
self
,
**
args
):
"""Creates a new Switch object, which models a linux bridge device.
"""Creates a new Switch object, which models a linux bridge device.
...
...
src/nemu/node.py
View file @
ba619ec3
...
@@ -27,6 +27,7 @@ import weakref
...
@@ -27,6 +27,7 @@ import weakref
import
nemu.interface
import
nemu.interface
import
nemu.protocol
import
nemu.protocol
import
nemu.subprocess_
import
nemu.subprocess_
from
nemu
import
compat
from
nemu.environ
import
*
from
nemu.environ
import
*
__all__
=
[
'Node'
,
'get_nodes'
,
'import_if'
]
__all__
=
[
'Node'
,
'get_nodes'
,
'import_if'
]
...
@@ -195,7 +196,7 @@ class Node(object):
...
@@ -195,7 +196,7 @@ class Node(object):
# Requires CAP_SYS_ADMIN privileges to run.
# Requires CAP_SYS_ADMIN privileges to run.
def
_start_child
(
nonetns
)
->
(
socket
.
socket
,
int
):
def
_start_child
(
nonetns
)
->
(
socket
.
socket
,
int
):
# Create socket pair to communicate
# Create socket pair to communicate
(
s0
,
s1
)
=
socke
t
.
socketpair
(
socket
.
AF_UNIX
,
socket
.
SOCK_STREAM
,
0
)
(
s0
,
s1
)
=
compa
t
.
socketpair
(
socket
.
AF_UNIX
,
socket
.
SOCK_STREAM
,
0
)
# Spawn a child that will run in a loop
# Spawn a child that will run in a loop
pid
=
os
.
fork
()
pid
=
os
.
fork
()
if
pid
:
if
pid
:
...
...
src/nemu/protocol.py
View file @
ba619ec3
This diff is collapsed.
Click to expand it.
src/nemu/subprocess_.py
View file @
ba619ec3
This diff is collapsed.
Click to expand it.
test/test_protocol.py
View file @
ba619ec3
...
@@ -6,14 +6,16 @@ import nemu.protocol
...
@@ -6,14 +6,16 @@ import nemu.protocol
import
os
,
socket
,
sys
,
threading
,
unittest
import
os
,
socket
,
sys
,
threading
,
unittest
import
test_util
import
test_util
from
nemu
import
compat
class
TestServer
(
unittest
.
TestCase
):
class
TestServer
(
unittest
.
TestCase
):
@
test_util
.
skip
(
"python 3 can't makefile a socket in r+"
)
def
test_server_startup
(
self
):
def
test_server_startup
(
self
):
# Test the creation of the server object with different ways of passing
# Test the creation of the server object with different ways of passing
# the file descriptor; and check the banner.
# the file descriptor; and check the banner.
(
s0
,
s1
)
=
socke
t
.
socketpair
(
socket
.
AF_UNIX
,
socket
.
SOCK_STREAM
,
0
)
(
s0
,
s1
)
=
compa
t
.
socketpair
(
socket
.
AF_UNIX
,
socket
.
SOCK_STREAM
,
0
)
(
s2
,
s3
)
=
socke
t
.
socketpair
(
socket
.
AF_UNIX
,
socket
.
SOCK_STREAM
,
0
)
(
s2
,
s3
)
=
compa
t
.
socketpair
(
socket
.
AF_UNIX
,
socket
.
SOCK_STREAM
,
0
)
def
test_help
(
fd
):
def
test_help
(
fd
):
fd
.
write
(
"HELP
\
n
"
)
fd
.
write
(
"HELP
\
n
"
)
...
@@ -34,13 +36,13 @@ class TestServer(unittest.TestCase):
...
@@ -34,13 +36,13 @@ class TestServer(unittest.TestCase):
t
=
threading
.
Thread
(
target
=
run_server
)
t
=
threading
.
Thread
(
target
=
run_server
)
t
.
start
()
t
.
start
()
s
=
os
.
fdopen
(
s1
.
fileno
(),
"r
+
"
,
1
)
s
=
os
.
fdopen
(
s1
.
fileno
(),
"r"
,
1
)
self
.
assertEqual
(
s
.
readline
()[
0
:
4
],
"220 "
)
self
.
assertEqual
(
s
.
readline
()[
0
:
4
],
"220 "
)
test_help
(
s
)
test_help
(
s
)
s
.
close
()
s
.
close
()
s0
.
close
()
s0
.
close
()
s
=
os
.
fdopen
(
s3
.
fileno
(),
"r
+
"
,
1
)
s
=
os
.
fdopen
(
s3
.
fileno
(),
"r"
,
1
)
self
.
assertEqual
(
s
.
readline
()[
0
:
4
],
"220 "
)
self
.
assertEqual
(
s
.
readline
()[
0
:
4
],
"220 "
)
test_help
(
s
)
test_help
(
s
)
s
.
close
()
s
.
close
()
...
@@ -48,7 +50,7 @@ class TestServer(unittest.TestCase):
...
@@ -48,7 +50,7 @@ class TestServer(unittest.TestCase):
t
.
join
()
t
.
join
()
def
test_server_clean
(
self
):
def
test_server_clean
(
self
):
(
s0
,
s1
)
=
socke
t
.
socketpair
(
socket
.
AF_UNIX
,
socket
.
SOCK_STREAM
,
0
)
(
s0
,
s1
)
=
compa
t
.
socketpair
(
socket
.
AF_UNIX
,
socket
.
SOCK_STREAM
,
0
)
def
run_server
():
def
run_server
():
nemu
.
protocol
.
Server
(
s0
,
s0
).
run
()
nemu
.
protocol
.
Server
(
s0
,
s0
).
run
()
...
@@ -56,8 +58,9 @@ class TestServer(unittest.TestCase):
...
@@ -56,8 +58,9 @@ class TestServer(unittest.TestCase):
t
.
start
()
t
.
start
()
cli
=
nemu
.
protocol
.
Client
(
s1
,
s1
)
cli
=
nemu
.
protocol
.
Client
(
s1
,
s1
)
argv
=
[
'/bin/sh'
,
'-c'
,
'yes'
]
argv
=
[
'/bin/sh'
,
'-c'
,
'yes'
]
pid
=
cli
.
spawn
(
argv
,
stdout
=
subprocess
.
DEVNULL
)
nullfd
=
open
(
"/dev/null"
,
"wb"
)
pid
=
cli
.
spawn
(
argv
,
stdout
=
nullfd
.
fileno
())
self
.
assertTrue
(
os
.
path
.
exists
(
"/proc/%d"
%
pid
))
self
.
assertTrue
(
os
.
path
.
exists
(
"/proc/%d"
%
pid
))
# try to exit while there are still processes running
# try to exit while there are still processes running
cli
.
shutdown
()
cli
.
shutdown
()
...
@@ -68,7 +71,7 @@ class TestServer(unittest.TestCase):
...
@@ -68,7 +71,7 @@ class TestServer(unittest.TestCase):
self
.
assertFalse
(
os
.
path
.
exists
(
"/proc/%d"
%
pid
))
self
.
assertFalse
(
os
.
path
.
exists
(
"/proc/%d"
%
pid
))
def
test_spawn_recovery
(
self
):
def
test_spawn_recovery
(
self
):
(
s0
,
s1
)
=
socke
t
.
socketpair
(
socket
.
AF_UNIX
,
socket
.
SOCK_STREAM
,
0
)
(
s0
,
s1
)
=
compa
t
.
socketpair
(
socket
.
AF_UNIX
,
socket
.
SOCK_STREAM
,
0
)
def
run_server
():
def
run_server
():
nemu
.
protocol
.
Server
(
s0
,
s0
).
run
()
nemu
.
protocol
.
Server
(
s0
,
s0
).
run
()
...
@@ -93,7 +96,7 @@ class TestServer(unittest.TestCase):
...
@@ -93,7 +96,7 @@ class TestServer(unittest.TestCase):
@
test_util
.
skip
(
"python 3 can't makefile a socket in r+"
)
@
test_util
.
skip
(
"python 3 can't makefile a socket in r+"
)
def
test_basic_stuff
(
self
):
def
test_basic_stuff
(
self
):
(
s0
,
s1
)
=
socke
t
.
socketpair
(
socket
.
AF_UNIX
,
socket
.
SOCK_STREAM
,
0
)
(
s0
,
s1
)
=
compa
t
.
socketpair
(
socket
.
AF_UNIX
,
socket
.
SOCK_STREAM
,
0
)
srv
=
nemu
.
protocol
.
Server
(
s0
,
s0
)
srv
=
nemu
.
protocol
.
Server
(
s0
,
s0
)
s1
=
s1
.
makefile
(
"r+"
,
1
)
s1
=
s1
.
makefile
(
"r+"
,
1
)
...
...
test/test_subprocess.py
View file @
ba619ec3
...
@@ -6,6 +6,9 @@ import nemu, test_util
...
@@ -6,6 +6,9 @@ import nemu, test_util
import
nemu.subprocess_
as
sp
import
nemu.subprocess_
as
sp
import
grp
,
os
,
pwd
,
signal
,
socket
,
sys
,
time
,
unittest
import
grp
,
os
,
pwd
,
signal
,
socket
,
sys
,
time
,
unittest
from
nemu
import
compat
def
_stat
(
path
):
def
_stat
(
path
):
try
:
try
:
return
os
.
stat
(
path
)
return
os
.
stat
(
path
)
...
@@ -104,7 +107,7 @@ class TestSubprocess(unittest.TestCase):
...
@@ -104,7 +107,7 @@ class TestSubprocess(unittest.TestCase):
# uses a default search path
# uses a default search path
self
.
assertRaises
(
OSError
,
sp
.
spawn
,
'sleep'
,
env
=
{
'PATH'
:
''
})
self
.
assertRaises
(
OSError
,
sp
.
spawn
,
'sleep'
,
env
=
{
'PATH'
:
''
})
r
,
w
=
os
.
pipe
()
r
,
w
=
compat
.
pipe
()
p
=
sp
.
spawn
(
'/bin/echo'
,
[
'echo'
,
'hello world'
],
stdout
=
w
)
p
=
sp
.
spawn
(
'/bin/echo'
,
[
'echo'
,
'hello world'
],
stdout
=
w
)
os
.
close
(
w
)
os
.
close
(
w
)
self
.
assertEqual
(
_readall
(
r
),
b"hello world
\
n
"
)
self
.
assertEqual
(
_readall
(
r
),
b"hello world
\
n
"
)
...
@@ -120,8 +123,8 @@ class TestSubprocess(unittest.TestCase):
...
@@ -120,8 +123,8 @@ class TestSubprocess(unittest.TestCase):
# It cannot be wait()ed again.
# It cannot be wait()ed again.
self
.
assertRaises
(
OSError
,
sp
.
wait
,
p
)
self
.
assertRaises
(
OSError
,
sp
.
wait
,
p
)
r0
,
w0
=
os
.
pipe
()
r0
,
w0
=
compat
.
pipe
()
r1
,
w1
=
os
.
pipe
()
r1
,
w1
=
compat
.
pipe
()
p
=
sp
.
spawn
(
'/bin/cat'
,
stdout
=
w0
,
stdin
=
r1
,
close_fds
=
[
r0
,
w1
])
p
=
sp
.
spawn
(
'/bin/cat'
,
stdout
=
w0
,
stdin
=
r1
,
close_fds
=
[
r0
,
w1
])
os
.
close
(
w0
)
os
.
close
(
w0
)
os
.
close
(
r1
)
os
.
close
(
r1
)
...
@@ -140,25 +143,25 @@ class TestSubprocess(unittest.TestCase):
...
@@ -140,25 +143,25 @@ class TestSubprocess(unittest.TestCase):
self
.
assertRaises
(
ValueError
,
node
.
Subprocess
,
self
.
assertRaises
(
ValueError
,
node
.
Subprocess
,
[
'/bin/sleep'
,
'1000'
],
user
=
self
.
nouid
)
[
'/bin/sleep'
,
'1000'
],
user
=
self
.
nouid
)
# Invalid CWD: it is a file
# Invalid CWD: it is a file
self
.
assertRaises
(
OS
Error
,
node
.
Subprocess
,
self
.
assertRaises
(
NotADirectory
Error
,
node
.
Subprocess
,
'/bin/sleep'
,
cwd
=
'/bin/sleep'
)
'/bin/sleep'
,
cwd
=
'/bin/sleep'
)
# Invalid CWD: does not exist
# Invalid CWD: does not exist
self
.
assertRaises
(
OS
Error
,
node
.
Subprocess
,
self
.
assertRaises
(
FileNotFound
Error
,
node
.
Subprocess
,
'/bin/sleep'
,
cwd
=
self
.
nofile
)
'/bin/sleep'
,
cwd
=
self
.
nofile
)
# Exec failure
# Exec failure
self
.
assertRaises
(
OS
Error
,
node
.
Subprocess
,
self
.
nofile
)
self
.
assertRaises
(
FileNotFound
Error
,
node
.
Subprocess
,
self
.
nofile
)
# Test that the environment is cleared: sleep should not be found
# Test that the environment is cleared: sleep should not be found
self
.
assertRaises
(
OS
Error
,
node
.
Subprocess
,
self
.
assertRaises
(
FileNotFound
Error
,
node
.
Subprocess
,
'sleep'
,
env
=
{
'PATH'
:
''
})
'sleep'
,
env
=
{
'PATH'
:
''
})
# Argv
# Argv
self
.
assertRaises
(
OS
Error
,
node
.
Subprocess
,
'true; false'
)
self
.
assertRaises
(
FileNotFound
Error
,
node
.
Subprocess
,
'true; false'
)
self
.
assertEqual
(
node
.
Subprocess
(
'true'
).
wait
(),
0
)
self
.
assertEqual
(
node
.
Subprocess
(
'true'
).
wait
(),
0
)
self
.
assertEqual
(
node
.
Subprocess
(
'true; false'
,
shell
=
True
).
wait
(),
self
.
assertEqual
(
node
.
Subprocess
(
'true; false'
,
shell
=
True
).
wait
(),
1
)
1
)
# Piping
# Piping
r
,
w
=
os
.
pipe
()
r
,
w
=
compat
.
pipe
()
p
=
node
.
Subprocess
([
'echo'
,
'hello world'
],
stdout
=
w
)
p
=
node
.
Subprocess
([
'echo'
,
'hello world'
],
stdout
=
w
)
os
.
close
(
w
)
os
.
close
(
w
)
self
.
assertEqual
(
_readall
(
r
),
b"hello world
\
n
"
)
self
.
assertEqual
(
_readall
(
r
),
b"hello world
\
n
"
)
...
@@ -166,7 +169,7 @@ class TestSubprocess(unittest.TestCase):
...
@@ -166,7 +169,7 @@ class TestSubprocess(unittest.TestCase):
p
.
wait
()
p
.
wait
()
# cwd
# cwd
r
,
w
=
os
.
pipe
()
r
,
w
=
compat
.
pipe
()
p
=
node
.
Subprocess
(
'/bin/pwd'
,
stdout
=
w
,
cwd
=
"/"
)
p
=
node
.
Subprocess
(
'/bin/pwd'
,
stdout
=
w
,
cwd
=
"/"
)
os
.
close
(
w
)
os
.
close
(
w
)
self
.
assertEqual
(
_readall
(
r
),
b"/
\
n
"
)
self
.
assertEqual
(
_readall
(
r
),
b"/
\
n
"
)
...
@@ -194,7 +197,7 @@ class TestSubprocess(unittest.TestCase):
...
@@ -194,7 +197,7 @@ class TestSubprocess(unittest.TestCase):
# closing stdout (so _readall finishes)
# closing stdout (so _readall finishes)
cmd
=
'trap "" TERM; echo; exec sleep 100 > /dev/null'
cmd
=
'trap "" TERM; echo; exec sleep 100 > /dev/null'
r
,
w
=
os
.
pipe
()
r
,
w
=
compat
.
pipe
()
p
=
node
.
Subprocess
(
cmd
,
shell
=
True
,
stdout
=
w
)
p
=
node
.
Subprocess
(
cmd
,
shell
=
True
,
stdout
=
w
)
os
.
close
(
w
)
os
.
close
(
w
)
self
.
assertEqual
(
_readall
(
r
),
b"
\
n
"
)
# wait for trap to be installed
self
.
assertEqual
(
_readall
(
r
),
b"
\
n
"
)
# wait for trap to be installed
...
@@ -202,9 +205,10 @@ class TestSubprocess(unittest.TestCase):
...
@@ -202,9 +205,10 @@ class TestSubprocess(unittest.TestCase):
pid
=
p
.
pid
pid
=
p
.
pid
os
.
kill
(
pid
,
0
)
# verify process still there
os
.
kill
(
pid
,
0
)
# verify process still there
# Avoid the warning about the process being killed
# Avoid the warning about the process being killed
old_err
=
sys
.
stderr
with
open
(
"/dev/null"
,
"w"
)
as
sys
.
stderr
:
with
open
(
"/dev/null"
,
"w"
)
as
sys
.
stderr
:
p
.
destroy
()
p
.
destroy
()
sys
.
stderr
=
sys
.
__stderr__
sys
.
stderr
=
old_err
self
.
assertRaises
(
OSError
,
os
.
kill
,
pid
,
0
)
# should be dead by now
self
.
assertRaises
(
OSError
,
os
.
kill
,
pid
,
0
)
# should be dead by now
p
=
node
.
Subprocess
([
'sleep'
,
'100'
])
p
=
node
.
Subprocess
([
'sleep'
,
'100'
])
...
@@ -217,8 +221,8 @@ class TestSubprocess(unittest.TestCase):
...
@@ -217,8 +221,8 @@ class TestSubprocess(unittest.TestCase):
node
=
nemu
.
Node
(
nonetns
=
True
)
node
=
nemu
.
Node
(
nonetns
=
True
)
# repeat test with Popen interface
# repeat test with Popen interface
r0
,
w0
=
os
.
pipe
()
r0
,
w0
=
compat
.
pipe
()
r1
,
w1
=
os
.
pipe
()
r1
,
w1
=
compat
.
pipe
()
p
=
node
.
Popen
(
'cat'
,
stdout
=
w0
,
stdin
=
r1
)
p
=
node
.
Popen
(
'cat'
,
stdout
=
w0
,
stdin
=
r1
)
os
.
close
(
w0
)
os
.
close
(
w0
)
os
.
close
(
r1
)
os
.
close
(
r1
)
...
@@ -228,7 +232,7 @@ class TestSubprocess(unittest.TestCase):
...
@@ -228,7 +232,7 @@ class TestSubprocess(unittest.TestCase):
os
.
close
(
r0
)
os
.
close
(
r0
)
# now with a socketpair, not using integers
# now with a socketpair, not using integers
(
s0
,
s1
)
=
socke
t
.
socketpair
(
socket
.
AF_UNIX
,
socket
.
SOCK_STREAM
,
0
)
(
s0
,
s1
)
=
compa
t
.
socketpair
(
socket
.
AF_UNIX
,
socket
.
SOCK_STREAM
,
0
)
p
=
node
.
Popen
(
'cat'
,
stdout
=
s0
,
stdin
=
s0
)
p
=
node
.
Popen
(
'cat'
,
stdout
=
s0
,
stdin
=
s0
)
s0
.
close
()
s0
.
close
()
s1
.
send
(
b"hello world
\
n
"
)
s1
.
send
(
b"hello world
\
n
"
)
...
@@ -255,9 +259,9 @@ class TestSubprocess(unittest.TestCase):
...
@@ -255,9 +259,9 @@ class TestSubprocess(unittest.TestCase):
p
=
node
.
Popen
(
'cat >&2'
,
shell
=
True
,
stdin
=
sp
.
PIPE
,
p
=
node
.
Popen
(
'cat >&2'
,
shell
=
True
,
stdin
=
sp
.
PIPE
,
stderr
=
sp
.
PIPE
)
stderr
=
sp
.
PIPE
)
p
.
stdin
.
write
(
"hello world
\
n
"
)
p
.
stdin
.
write
(
b
"hello world
\
n
"
)
p
.
stdin
.
close
()
p
.
stdin
.
close
()
self
.
assertEqual
(
p
.
stderr
.
readlines
(),
[
"hello world
\
n
"
])
self
.
assertEqual
(
p
.
stderr
.
readlines
(),
[
b
"hello world
\
n
"
])
self
.
assertEqual
(
p
.
stdout
,
None
)
self
.
assertEqual
(
p
.
stdout
,
None
)
self
.
assertEqual
(
p
.
wait
(),
0
)
self
.
assertEqual
(
p
.
wait
(),
0
)
...
@@ -268,9 +272,9 @@ class TestSubprocess(unittest.TestCase):
...
@@ -268,9 +272,9 @@ class TestSubprocess(unittest.TestCase):
#
#
p
=
node
.
Popen
([
'sh'
,
'-c'
,
'cat >&2'
],
p
=
node
.
Popen
([
'sh'
,
'-c'
,
'cat >&2'
],
stdin
=
sp
.
PIPE
,
stdout
=
sp
.
PIPE
,
stderr
=
sp
.
STDOUT
)
stdin
=
sp
.
PIPE
,
stdout
=
sp
.
PIPE
,
stderr
=
sp
.
STDOUT
)
p
.
stdin
.
write
(
"hello world
\
n
"
)
p
.
stdin
.
write
(
b
"hello world
\
n
"
)
p
.
stdin
.
close
()
p
.
stdin
.
close
()
self
.
assertEqual
(
p
.
stdout
.
readlines
(),
[
"hello world
\
n
"
])
self
.
assertEqual
(
p
.
stdout
.
readlines
(),
[
b
"hello world
\
n
"
])
self
.
assertEqual
(
p
.
stderr
,
None
)
self
.
assertEqual
(
p
.
stderr
,
None
)
self
.
assertEqual
(
p
.
wait
(),
0
)
self
.
assertEqual
(
p
.
wait
(),
0
)
...
@@ -281,9 +285,9 @@ class TestSubprocess(unittest.TestCase):
...
@@ -281,9 +285,9 @@ class TestSubprocess(unittest.TestCase):
#
#
p
=
node
.
Popen
([
'tee'
,
'/dev/stderr'
],
p
=
node
.
Popen
([
'tee'
,
'/dev/stderr'
],
stdin
=
sp
.
PIPE
,
stdout
=
sp
.
PIPE
,
stderr
=
sp
.
STDOUT
)
stdin
=
sp
.
PIPE
,
stdout
=
sp
.
PIPE
,
stderr
=
sp
.
STDOUT
)
p
.
stdin
.
write
(
"hello world
\
n
"
)
p
.
stdin
.
write
(
b
"hello world
\
n
"
)
p
.
stdin
.
close
()
p
.
stdin
.
close
()
self
.
assertEqual
(
p
.
stdout
.
readlines
(),
[
"hello world
\
n
"
]
*
2
)
self
.
assertEqual
(
p
.
stdout
.
readlines
(),
[
b
"hello world
\
n
"
]
*
2
)
self
.
assertEqual
(
p
.
stderr
,
None
)
self
.
assertEqual
(
p
.
stderr
,
None
)
self
.
assertEqual
(
p
.
wait
(),
0
)
self
.
assertEqual
(
p
.
wait
(),
0
)
...
@@ -295,10 +299,10 @@ class TestSubprocess(unittest.TestCase):
...
@@ -295,10 +299,10 @@ class TestSubprocess(unittest.TestCase):
#
#
p
=
node
.
Popen
([
'tee'
,
'/dev/stderr'
],
p
=
node
.
Popen
([
'tee'
,
'/dev/stderr'
],
stdin
=
sp
.
PIPE
,
stdout
=
sp
.
PIPE
,
stderr
=
sp
.
PIPE
)
stdin
=
sp
.
PIPE
,
stdout
=
sp
.
PIPE
,
stderr
=
sp
.
PIPE
)
p
.
stdin
.
write
(
"hello world
\
n
"
)
p
.
stdin
.
write
(
b
"hello world
\
n
"
)
p
.
stdin
.
close
()
p
.
stdin
.
close
()
self
.
assertEqual
(
p
.
stdout
.
readlines
(),
[
"hello world
\
n
"
])
self
.
assertEqual
(
p
.
stdout
.
readlines
(),
[
b
"hello world
\
n
"
])
self
.
assertEqual
(
p
.
stderr
.
readlines
(),
[
"hello world
\
n
"
])
self
.
assertEqual
(
p
.
stderr
.
readlines
(),
[
b
"hello world
\
n
"
])
self
.
assertEqual
(
p
.
wait
(),
0
)
self
.
assertEqual
(
p
.
wait
(),
0
)
p
=
node
.
Popen
([
'tee'
,
'/dev/stderr'
],
p
=
node
.
Popen
([
'tee'
,
'/dev/stderr'
],
...
...
test/test_util.py
View file @
ba619ec3
...
@@ -5,7 +5,7 @@ import os, re, subprocess, sys
...
@@ -5,7 +5,7 @@ import os, re, subprocess, sys
import
nemu.subprocess_
import
nemu.subprocess_
from
nemu.environ
import
*
from
nemu.environ
import
*
def
process_ipcmd
(
str
):
def
process_ipcmd
(
str
:
str
):
cur
=
None
cur
=
None
out
=
{}
out
=
{}
for
line
in
str
.
split
(
"
\
n
"
):
for
line
in
str
.
split
(
"
\
n
"
):
...
...
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