# HG changeset patch
# User Victor Stinner <victor.stinner@gmail.com>
# Date 1417423044 -3600
#      Mon Dec 01 09:37:24 2014 +0100
# Node ID eba1a6dba205559d724d32c80d955a65e078505e
# Parent  e6fef4231a8a28ef91c1feaf3bfbcbd29822c939
Issue #17: syscall parser now supports O_CLOEXEC and SOCK_CLOEXEC, fix unit
tests on Python 3.4 and newer

diff --git a/doc/changelog.rst b/doc/changelog.rst
--- a/doc/changelog.rst
+++ b/doc/changelog.rst
@@ -3,6 +3,12 @@
 Changelog
 =========
 
+python-ptrace 0.8.2
+-------------------
+
+* Issue #17: syscall parser now supports O_CLOEXEC and SOCK_CLOEXEC, fix unit
+  tests on Python 3.4 and newer
+
 python-ptrace 0.8.1 (2014-10-30)
 --------------------------------
 
diff --git a/ptrace/syscall/posix_arg.py b/ptrace/syscall/posix_arg.py
--- a/ptrace/syscall/posix_arg.py
+++ b/ptrace/syscall/posix_arg.py
@@ -24,7 +24,7 @@
     return formatBits(argument.value, ACCESS_MODE_BITMASK, "F_OK")
 
 # From /usr/include/bits/fcntl.h (Ubuntu Feisty, i386)
-OPEN_MODE_BITMASK = (
+OPEN_MODE_BITMASK = [
     (0o1, "O_WRONLY"),
     (0o2, "O_RDWR"),
     (0o100, "O_CREAT"),
@@ -40,10 +40,17 @@
     (0o200000, "O_DIRECTORY"),
     (0o400000, "O_NOFOLLOW"),
     (0o1000000, "O_NOATIME"),
-)
+]
+O_CLOEXEC = 0o02000000
 
 def formatOpenMode(argument):
-    return formatBits(int(argument.value), OPEN_MODE_BITMASK, "O_RDONLY", oct)
+    value = argument.value
+    cloexec = bool(value & O_CLOEXEC)
+    value = value & ~O_CLOEXEC
+    text = formatBits(int(value), OPEN_MODE_BITMASK, "O_RDONLY", oct)
+    if cloexec:
+        text += '|O_CLOEXEC'
+    return text
 
 CLONE_FLAGS_BITMASK = (
     (0x00000100, "CLONE_VM"),
diff --git a/ptrace/syscall/posix_constants.py b/ptrace/syscall/posix_constants.py
--- a/ptrace/syscall/posix_constants.py
+++ b/ptrace/syscall/posix_constants.py
@@ -1,5 +1,5 @@
 from ptrace.syscall.socketcall_constants import (
-    SOCKET_FAMILY, SOCKET_TYPE, SOCKET_PROTOCOL,
+    SOCKET_FAMILY, SOCKET_PROTOCOL,
     SETSOCKOPT_LEVEL, SETSOCKOPT_OPTNAME)
 
 SYSCALL_ARG_DICT = {
@@ -51,7 +51,6 @@
     },
     "socket": {
         "domain": SOCKET_FAMILY,
-        "type": SOCKET_TYPE,
         "protocol": SOCKET_PROTOCOL,
     },
     "getsockopt": {
diff --git a/ptrace/syscall/socketcall_constants.py b/ptrace/syscall/socketcall_constants.py
--- a/ptrace/syscall/socketcall_constants.py
+++ b/ptrace/syscall/socketcall_constants.py
@@ -1,3 +1,5 @@
+import socket
+
 SOCKETCALL = {
     1: "socket",
     2: "bind",
@@ -56,6 +58,19 @@
     10: "SOCK_PACKET",
 }
 
+def formatSocketType(argument):
+    value = argument.value
+    text = []
+    if hasattr(socket, 'SOCK_CLOEXEC'):
+        cloexec = value & socket.SOCK_CLOEXEC
+        value &= ~socket.SOCK_CLOEXEC
+    else:
+        cloexec = False
+    text = SOCKET_TYPE.get(value, value)
+    if cloexec:
+        text += '|SOCK_CLOEXEC'
+    return text
+
 SOCKET_PROTOCOL = {
      1: "IPPROTO_ICMP",
     58: "IPPROTO_ICMPV6",
diff --git a/ptrace/syscall/syscall_argument.py b/ptrace/syscall/syscall_argument.py
--- a/ptrace/syscall/syscall_argument.py
+++ b/ptrace/syscall/syscall_argument.py
@@ -22,6 +22,7 @@
     from ptrace.syscall.freebsd_constants import SYSCALL_ARG_DICT
 else:
     SYSCALL_ARG_DICT = {}
+from ptrace.syscall.socketcall_constants import formatSocketType
 
 KNOWN_STRUCTS = []
 if RUNNING_LINUX:
@@ -35,6 +36,7 @@
     "mmap": {"prot": formatMmapProt},
     "mmap2": {"prot": formatMmapProt},
     "clone": {"flags": formatCloneFlags},
+    "socket": {"type": formatSocketType},
     "setsockopt": {"optval": formatOptVal},
 }
 
diff --git a/tests/test_strace.py b/tests/test_strace.py
--- a/tests/test_strace.py
+++ b/tests/test_strace.py
@@ -39,10 +39,19 @@
             expected = os.fsencode(expected)
         self.assertEqual(match.group(1), expected)
 
+    def test_open(self):
+        if PY3:
+            code = 'open(%a).close()' % __file__
+        else:
+            code = 'open(%r).close()' % __file__
+        stdout = self.strace(sys.executable, '-c', code)
+        pattern = re.compile(br"^open\(.*test_strace\.py', O_RDONLY(\|O_CLOEXEC)?\)", re.MULTILINE)
+        self.assertTrue(pattern.search(stdout), stdout)
+
     def test_socket(self):
         code = 'import socket; socket.socket(socket.AF_INET, socket.SOCK_STREAM).close()'
         stdout = self.strace(sys.executable, '-c', code)
-        pattern = re.compile(b'^socket\\(AF_INET, SOCK_STREAM, ', re.MULTILINE)
+        pattern = re.compile(br'^socket\(AF_INET, SOCK_STREAM(\|SOCK_CLOEXEC)?, ', re.MULTILINE)
         self.assertTrue(pattern.search(stdout), stdout)
 
 if __name__ == "__main__":
