Repository: benoitc/gunicorn

Issue:
20.0.2 raise OSError
I use `kill -USR2 ` to reload process, but it raise error.

```
[2019-11-25 11:33:44 +0800] [3967] [INFO] Starting gunicorn 20.0.2
[2019-11-25 11:33:44 +0800] [3967] [INFO] Listening at: http://127.0.0.1:8000 (3967)
[2019-11-25 11:33:44 +0800] [3967] [INFO] Using worker: sync
[2019-11-25 11:33:44 +0800] [3970] [INFO] Booting worker with pid: 3970
[2019-11-25 11:33:51 +0800] [3967] [INFO] Handling signal: usr2
[2019-11-25 11:33:51 +0800] [3975] [INFO] Starting gunicorn 20.0.2
Traceback (most recent call last):
  File "/Users/xx/project/py37/bin/gunicorn", line 10, in <module>
    sys.exit(run())
  File "/Users/xx/project/py37/lib/python3.7/site-packages/gunicorn/app/wsgiapp.py", line 58, in run
    WSGIApplication("%(prog)s [OPTIONS] [APP_MODULE]").run()
  File "/Users/xx/project/py37/lib/python3.7/site-packages/gunicorn/app/base.py", line 219, in run
    super().run()
  File "/Users/xx/project/py37/lib/python3.7/site-packages/gunicorn/app/base.py", line 71, in run
    Arbiter(self).run()
  File "/Users/xx/project/py37/lib/python3.7/site-packages/gunicorn/arbiter.py", line 198, in run
    self.start()
  File "/Users/xx/project/py37/lib/python3.7/site-packages/gunicorn/arbiter.py", line 155, in start
    self.LISTENERS = sock.create_sockets(self.cfg, self.log, fds)
  File "/Users/xx/project/py37/lib/python3.7/site-packages/gunicorn/sock.py", line 171, in create_sockets
    sock = fromfd(fd)
  File "/Users/xx/project/py37/lib/python3.7/site-packages/gunicorn/socketfromfd.py", line 95, in fromfd
    family = _raw_getsockopt(fd, socket.SOL_SOCKET, SO_DOMAIN)
  File "/Users/xx/project/py37/lib/python3.7/site-packages/gunicorn/socketfromfd.py", line 74, in _raw_getsockopt
    ctypes.byref(optval), ctypes.byref(optlen))
  File "/Users/xx/project/py37/lib/python3.7/site-packages/gunicorn/socketfromfd.py", line 44, in _errcheck_errno
    raise OSError(errno, os.strerror(errno))
OSError: [Errno 42] Protocol not available

```


Discussion:
Thanks for the report. Was it during an upgrade from an old version of
gunicorn? what what your environment previously? (python and gunicorn
version). Also what was your command line?

On Mon 25 Nov 2019 at 04:45 gojuukaze <notifications@github.com> wrote:

> I use kill -USR2 to reload process, but it raise error.
>
> [2019-11-25 11:33:44 +0800] [3967] [INFO] Starting gunicorn 20.0.2
> [2019-11-25 11:33:44 +0800] [3967] [INFO] Listening at: http://127.0.0.1:8000 (3967)
> [2019-11-25 11:33:44 +0800] [3967] [INFO] Using worker: sync
> [2019-11-25 11:33:44 +0800] [3970] [INFO] Booting worker with pid: 3970
> [2019-11-25 11:33:51 +0800] [3967] [INFO] Handling signal: usr2
> [2019-11-25 11:33:51 +0800] [3975] [INFO] Starting gunicorn 20.0.2
> Traceback (most recent call last):
>   File "/Users/xx/project/py37/bin/gunicorn", line 10, in <module>
>     sys.exit(run())
>   File "/Users/xx/project/py37/lib/python3.7/site-packages/gunicorn/app/wsgiapp.py", line 58, in run
>     WSGIApplication("%(prog)s [OPTIONS] [APP_MODULE]").run()
>   File "/Users/xx/project/py37/lib/python3.7/site-packages/gunicorn/app/base.py", line 219, in run
>     super().run()
>   File "/Users/xx/project/py37/lib/python3.7/site-packages/gunicorn/app/base.py", line 71, in run
>     Arbiter(self).run()
>   File "/Users/xx/project/py37/lib/python3.7/site-packages/gunicorn/arbiter.py", line 198, in run
>     self.start()
>   File "/Users/xx/project/py37/lib/python3.7/site-packages/gunicorn/arbiter.py", line 155, in start
>     self.LISTENERS = sock.create_sockets(self.cfg, self.log, fds)
>   File "/Users/xx/project/py37/lib/python3.7/site-packages/gunicorn/sock.py", line 171, in create_sockets
>     sock = fromfd(fd)
>   File "/Users/xx/project/py37/lib/python3.7/site-packages/gunicorn/socketfromfd.py", line 95, in fromfd
>     family = _raw_getsockopt(fd, socket.SOL_SOCKET, SO_DOMAIN)
>   File "/Users/xx/project/py37/lib/python3.7/site-packages/gunicorn/socketfromfd.py", line 74, in _raw_getsockopt
>     ctypes.byref(optval), ctypes.byref(optlen))
>   File "/Users/xx/project/py37/lib/python3.7/site-packages/gunicorn/socketfromfd.py", line 44, in _errcheck_errno
>     raise OSError(errno, os.strerror(errno))
> OSError: [Errno 42] Protocol not available
>
>
> —
> You are receiving this because you are subscribed to this thread.
> Reply to this email directly, view it on GitHub
> <https://github.com/benoitc/gunicorn/issues/2201?email_source=notifications&email_token=AAADRIXUF6NKSCPJVQX5UNDQVNC4TA5CNFSM4JRC42YKYY3PNVWWK3TUL52HS4DFUVEXG43VMWVGG33NNVSW45C7NFSM4H3V65MQ>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AAADRIWIOHGFUYEZGWLJOHLQVNC4TANCNFSM4JRC42YA>
> .
>
-- 
Sent from my Mobile

@benoitc I didn't upgrade gunicorn from old version.   

environment:  
* macos(10.14.5)
* python(3.7.4) 
* gunicorn(20.0.2) 
* django(2.2.6)

run server cmd: 
```
gunicorn my_project.wsgi
```

Resolving patch:
diff --git a/gunicorn/socketfromfd.py b/gunicorn/socketfromfd.py
--- a/gunicorn/socketfromfd.py
+++ b/gunicorn/socketfromfd.py
@@ -1,9 +1,20 @@
-# Copyright (C) 2016  Christian Heimes
-"""socketfromfd -- socket.fromd() with auto-discovery
+# -*- coding: utf-8 -
+#
+# This file is part of gunicorn
+# See the NOTICE for more information.
 
-ATTENTION: Do not remove this backport till the minimum required version is
-           Python 3.7. See https://bugs.python.org/issue28134 for details.
+# Copyright (C) 2016  Christian Heimes under Apache License 2
+
+# source code based on https://github.com/tiran/socketfromfd/blob/master/socketfromfd.py
+# and https://github.com/python/cpython/blob/master/Modules/socketmodule.c
+
+"""socketfromfd -- create a socket from its file descriptor
+This module detect the socket properties.
+
+note: Before python 3.7 auto detecting the socket was not working.
+See  https://bugs.python.org/issue28134 for details.
 """
+
 from __future__ import print_function
 
 import ctypes
@@ -16,10 +27,6 @@
 
 __all__ = ('fromfd',)
 
-SO_DOMAIN = getattr(socket, 'SO_DOMAIN', 39)
-SO_TYPE = getattr(socket, 'SO_TYPE', 3)
-SO_PROTOCOL = getattr(socket, 'SO_PROTOCOL', 38)
-
 _libc_name = find_library('c')
 if _libc_name is not None:
     if sys.platform.startswith("aix"):
@@ -47,8 +54,12 @@ def _errcheck_errno(result, func, arguments):
 
 if platform.system() == 'SunOS':
     _libc_getsockopt = libc._so_getsockopt
+    _lib_getsockname = libc._so_getsockname
 else:
     _libc_getsockopt = libc.getsockopt
+    _libc_getsockname = libc.getsockname
+
+
 _libc_getsockopt.argtypes = [
     ctypes.c_int,  # int sockfd
     ctypes.c_int,  # int level
@@ -59,8 +70,23 @@ def _errcheck_errno(result, func, arguments):
 _libc_getsockopt.restype = ctypes.c_int  # 0: ok, -1: err
 _libc_getsockopt.errcheck = _errcheck_errno
 
+class SockAddr(ctypes.Structure):
+    _fields_ = [
+        ('sa_len', ctypes.c_uint8),
+        ('sa_family', ctypes.c_uint8),
+        ('sa_data', ctypes.c_char * 14)
+    ]
 
-def _raw_getsockopt(fd, level, optname):
+
+_libc_getsockname.argtypes = [
+    ctypes.c_int,
+    ctypes.POINTER(SockAddr),
+    ctypes.POINTER(ctypes.c_int)
+]
+_libc_getsockname.restype = ctypes.c_int  # 0: ok, -1: err
+_libc_getsockname.errcheck = _errcheck_errno
+
+def _getsockopt(fd, level, optname):
     """Make raw getsockopt() call for int32 optval
 
     :param fd: socket fd
@@ -74,6 +100,11 @@ def _raw_getsockopt(fd, level, optname):
                      ctypes.byref(optval), ctypes.byref(optlen))
     return optval.value
 
+def _getsockname(fd):
+    sockaddr = SockAddr()
+    sockaddrlen = ctypes.c_int(ctypes.sizeof(sockaddr))
+    _libc_getsockname(fd, sockaddr, sockaddrlen)
+    return sockaddr
 
 def fromfd(fd, keep_fd=True):
     """Create a socket from a file descriptor
@@ -92,18 +123,18 @@ def fromfd(fd, keep_fd=True):
     :return: socket.socket instance
     :raises OSError: for invalid socket fd
     """
-    family = _raw_getsockopt(fd, socket.SOL_SOCKET, SO_DOMAIN)
-    typ = _raw_getsockopt(fd, socket.SOL_SOCKET, SO_TYPE)
-    proto = _raw_getsockopt(fd, socket.SOL_SOCKET, SO_PROTOCOL)
-    if sys.version_info.major == 2:
-        # Python 2 has no fileno argument and always duplicates the fd
-        sockobj = socket.fromfd(fd, family, typ, proto)
-        sock = socket.socket(None, None, None, _sock=sockobj)
-        if not keep_fd:
-            os.close(fd)
-        return sock
+    sockaddr = _getsockname(fd)
+    family = sockaddr.sa_family
+    if hasattr(socket, 'SO_TYPE'):
+        typ = _getsockopt(fd, socket.SOL_SOCKET, getattr(socket, 'SO_TYPE'))
+    else:
+        typ = socket.SOCK_STREAM
+
+    if hasattr(socket, 'SO_PROTOCOL'):
+        proto = _getsockopt(fd, socket.SOL_SOCKET, getattr(socket, 'SO_PROTOCOL'))
+    else:
+        proto = 0
+    if keep_fd:
+        return socket.fromfd(fd, family, typ, proto)
     else:
-        if keep_fd:
-            return socket.fromfd(fd, family, typ, proto)
-        else:
-            return socket.socket(family, typ, proto, fileno=fd)
+        return socket.socket(family, typ, proto, fileno=fd)


Test patch: