systemd.py 2.5 KB

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