systemd.py 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. #
  2. # This file is part of gunicorn released under the MIT license.
  3. # See the NOTICE for more information.
  4. import os
  5. import socket
  6. SD_LISTEN_FDS_START = 3
  7. def listen_fds(unset_environment=True):
  8. """
  9. Get the number of sockets inherited from systemd socket activation.
  10. :param unset_environment: clear systemd environment variables unless False
  11. :type unset_environment: bool
  12. :return: the number of sockets to inherit from systemd socket activation
  13. :rtype: int
  14. Returns zero immediately if $LISTEN_PID is not set to the current pid.
  15. Otherwise, returns the number of systemd activation sockets specified by
  16. $LISTEN_FDS.
  17. When $LISTEN_PID matches the current pid, unsets the environment variables
  18. unless the ``unset_environment`` flag is ``False``.
  19. .. note::
  20. Unlike the sd_listen_fds C function, this implementation does not set
  21. the FD_CLOEXEC flag because the gunicorn arbiter never needs to do this.
  22. .. seealso::
  23. `<https://www.freedesktop.org/software/systemd/man/sd_listen_fds.html>`_
  24. """
  25. fds = int(os.environ.get('LISTEN_FDS', 0))
  26. listen_pid = int(os.environ.get('LISTEN_PID', 0))
  27. if listen_pid != os.getpid():
  28. return 0
  29. if unset_environment:
  30. os.environ.pop('LISTEN_PID', None)
  31. os.environ.pop('LISTEN_FDS', None)
  32. return fds
  33. def sd_notify(state, logger, unset_environment=False):
  34. """Send a notification to systemd. state is a string; see
  35. the man page of sd_notify (http://www.freedesktop.org/software/systemd/man/sd_notify.html)
  36. for a description of the allowable values.
  37. If the unset_environment parameter is True, sd_notify() will unset
  38. the $NOTIFY_SOCKET environment variable before returning (regardless of
  39. whether the function call itself succeeded or not). Further calls to
  40. sd_notify() will then fail, but the variable is no longer inherited by
  41. child processes.
  42. """
  43. addr = os.environ.get('NOTIFY_SOCKET')
  44. if addr is None:
  45. # not run in a service, just a noop
  46. return
  47. try:
  48. sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM | socket.SOCK_CLOEXEC)
  49. if addr[0] == '@':
  50. addr = '\0' + addr[1:]
  51. sock.connect(addr)
  52. sock.sendall(state.encode('utf-8'))
  53. except Exception:
  54. logger.debug("Exception while invoking sd_notify()", exc_info=True)
  55. finally:
  56. if unset_environment:
  57. os.environ.pop('NOTIFY_SOCKET')
  58. sock.close()