Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

systemd.py 5.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. # -*- test-case-name: twisted.python.test.test_systemd -*-
  2. # Copyright (c) Twisted Matrix Laboratories.
  3. # See LICENSE for details.
  4. """
  5. Integration with systemd.
  6. Currently only the minimum APIs necessary for using systemd's socket activation
  7. feature are supported.
  8. """
  9. __all__ = ["ListenFDs"]
  10. from os import getpid
  11. from typing import Dict, List, Mapping, Optional, Sequence
  12. from attrs import Factory, define
  13. @define
  14. class ListenFDs:
  15. """
  16. L{ListenFDs} provides access to file descriptors inherited from systemd.
  17. Typically L{ListenFDs.fromEnvironment} should be used to construct a new
  18. instance of L{ListenFDs}.
  19. @cvar _START: File descriptors inherited from systemd are always
  20. consecutively numbered, with a fixed lowest "starting" descriptor. This
  21. gives the default starting descriptor. Since this must agree with the
  22. value systemd is using, it typically should not be overridden.
  23. @ivar _descriptors: A C{list} of C{int} giving the descriptors which were
  24. inherited.
  25. @ivar _names: A L{Sequence} of C{str} giving the names of the descriptors
  26. which were inherited.
  27. """
  28. _descriptors: Sequence[int]
  29. _names: Sequence[str] = Factory(tuple)
  30. _START = 3
  31. @classmethod
  32. def fromEnvironment(
  33. cls,
  34. environ: Optional[Mapping[str, str]] = None,
  35. start: Optional[int] = None,
  36. ) -> "ListenFDs":
  37. """
  38. @param environ: A dictionary-like object to inspect to discover
  39. inherited descriptors. By default, L{None}, indicating that the
  40. real process environment should be inspected. The default is
  41. suitable for typical usage.
  42. @param start: An integer giving the lowest value of an inherited
  43. descriptor systemd will give us. By default, L{None}, indicating
  44. the known correct (that is, in agreement with systemd) value will be
  45. used. The default is suitable for typical usage.
  46. @return: A new instance of C{cls} which can be used to look up the
  47. descriptors which have been inherited.
  48. """
  49. if environ is None:
  50. from os import environ as _environ
  51. environ = _environ
  52. if start is None:
  53. start = cls._START
  54. if str(getpid()) == environ.get("LISTEN_PID"):
  55. descriptors: List[int] = _parseDescriptors(start, environ)
  56. names: Sequence[str] = _parseNames(environ)
  57. else:
  58. descriptors = []
  59. names = ()
  60. # They may both be missing (consistent with not running under systemd
  61. # at all) or they may both be present (consistent with running under
  62. # systemd 227 or newer). It is not allowed for only one to be present
  63. # or for the values to disagree with each other (per
  64. # systemd.socket(5), systemd will use a default value based on the
  65. # socket unit name if the socket unit doesn't explicitly define a name
  66. # with FileDescriptorName).
  67. if len(names) != len(descriptors):
  68. return cls([], ())
  69. return cls(descriptors, names)
  70. def inheritedDescriptors(self) -> List[int]:
  71. """
  72. @return: The configured descriptors.
  73. """
  74. return list(self._descriptors)
  75. def inheritedNamedDescriptors(self) -> Dict[str, int]:
  76. """
  77. @return: A mapping from the names of configured descriptors to
  78. their integer values.
  79. """
  80. return dict(zip(self._names, self._descriptors))
  81. def _parseDescriptors(start: int, environ: Mapping[str, str]) -> List[int]:
  82. """
  83. Parse the I{LISTEN_FDS} environment variable supplied by systemd.
  84. @param start: systemd provides only a count of the number of descriptors
  85. that have been inherited. This is the integer value of the first
  86. inherited descriptor. Subsequent inherited descriptors are numbered
  87. counting up from here. See L{ListenFDs._START}.
  88. @param environ: The environment variable mapping in which to look for the
  89. value to parse.
  90. @return: The integer values of the inherited file descriptors, in order.
  91. """
  92. try:
  93. count = int(environ["LISTEN_FDS"])
  94. except (KeyError, ValueError):
  95. return []
  96. else:
  97. descriptors = list(range(start, start + count))
  98. # Remove the information from the environment so that a second
  99. # `ListenFDs` cannot find the same information. This is a precaution
  100. # against some application code accidentally trying to handle the same
  101. # inherited descriptor more than once - which probably wouldn't work.
  102. #
  103. # This precaution is perhaps somewhat questionable since it is up to
  104. # the application itself to know whether its handling of the file
  105. # descriptor will actually be safe. Also, nothing stops an
  106. # application from getting the same descriptor more than once using
  107. # multiple calls to `ListenFDs.inheritedDescriptors()` on the same
  108. # `ListenFDs` instance.
  109. del environ["LISTEN_PID"], environ["LISTEN_FDS"]
  110. return descriptors
  111. def _parseNames(environ: Mapping[str, str]) -> Sequence[str]:
  112. """
  113. Parse the I{LISTEN_FDNAMES} environment variable supplied by systemd.
  114. @param environ: The environment variable mapping in which to look for the
  115. value to parse.
  116. @return: The names of the inherited descriptors, in order.
  117. """
  118. names = environ.get("LISTEN_FDNAMES", "")
  119. if len(names) > 0:
  120. return tuple(names.split(":"))
  121. return ()