CANConfigPython 0.1.0a1__py3-none-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. canconfigpython/__init__.py +7 -0
  2. canconfigpython/__main__.py +3 -0
  3. canconfigpython/cli.py +111 -0
  4. canconfigpython/client.py +243 -0
  5. canconfigpython/commands.py +57 -0
  6. canconfigpython/errors.py +19 -0
  7. canconfigpython/resources/docs/native-source.tar.gz +0 -0
  8. canconfigpython/resources/docs/user-guide.en.md +274 -0
  9. canconfigpython/resources/docs/user-guide.zh-CN.md +265 -0
  10. canconfigpython/resources/skill/canconfig-python/SKILL.md +71 -0
  11. canconfigpython/resources/skill/canconfig-python/agents/openai.yaml +4 -0
  12. canconfigpython/resources/skill/canconfig-python/assets/changes.schema.json +832 -0
  13. canconfigpython/resources/skill/canconfig-python/assets/examples/bridge_dual_rates.json +18 -0
  14. canconfigpython/resources/skill/canconfig-python/assets/examples/bridge_mapping.json +13 -0
  15. canconfigpython/resources/skill/canconfig-python/assets/examples/cancom_115200_8n1.json +10 -0
  16. canconfigpython/resources/skill/canconfig-python/assets/examples/cancom_format_crc.json +8 -0
  17. canconfigpython/resources/skill/canconfig-python/assets/examples/cancom_gap.json +8 -0
  18. canconfigpython/resources/skill/canconfig-python/assets/examples/cancom_transparent_id.json +10 -0
  19. canconfigpython/resources/skill/canconfig-python/assets/examples/fb_1m.json +12 -0
  20. canconfigpython/resources/skill/canconfig-python/assets/examples/fb_500k.json +12 -0
  21. canconfigpython/resources/skill/canconfig-python/assets/examples/fb_adaptive_single.json +7 -0
  22. canconfigpython/resources/skill/canconfig-python/assets/examples/fb_return_manual.json +22 -0
  23. canconfigpython/resources/skill/canconfig-python/assets/examples/fb_silent_adaptive_option.json +7 -0
  24. canconfigpython/resources/skill/canconfig-python/assets/examples/filter_blacklist.json +7 -0
  25. canconfigpython/resources/skill/canconfig-python/assets/examples/filter_mask.json +12 -0
  26. canconfigpython/resources/skill/canconfig-python/assets/examples/filter_range.json +12 -0
  27. canconfigpython/resources/skill/canconfig-python/assets/examples/hub_h_external.json +13 -0
  28. canconfigpython/resources/skill/canconfig-python/assets/examples/hub_input_filter.json +13 -0
  29. canconfigpython/resources/skill/canconfig-python/assets/examples/hub_l_external.json +13 -0
  30. canconfigpython/resources/skill/canconfig-python/assets/examples/hub_mapping.json +46 -0
  31. canconfigpython/resources/skill/canconfig-python/assets/examples/hub_routing.json +8 -0
  32. canconfigpython/resources/skill/canconfig-python/assets/examples/index.en.md +55 -0
  33. canconfigpython/resources/skill/canconfig-python/assets/examples/index.json +661 -0
  34. canconfigpython/resources/skill/canconfig-python/assets/examples/index.zh-CN.md +55 -0
  35. canconfigpython/resources/skill/canconfig-python/assets/examples/negative/adaptive_mixed.json +8 -0
  36. canconfigpython/resources/skill/canconfig-python/assets/examples/negative/hub_missing_ps.json +12 -0
  37. canconfigpython/resources/skill/canconfig-python/assets/examples/negative/restricted_inheritance.json +12 -0
  38. canconfigpython/resources/skill/canconfig-python/assets/examples/negative/short_user_data.json +7 -0
  39. canconfigpython/resources/skill/canconfig-python/assets/examples/negative/string_numeric_id.json +7 -0
  40. canconfigpython/resources/skill/canconfig-python/assets/examples/negative/unknown_field.json +7 -0
  41. canconfigpython/resources/skill/canconfig-python/assets/examples/negative/unsupported_adaptive.json +7 -0
  42. canconfigpython/resources/skill/canconfig-python/assets/examples/negative/wrong_uid.json +7 -0
  43. canconfigpython/resources/skill/canconfig-python/assets/examples/rpt_adaptive_all.json +12 -0
  44. canconfigpython/resources/skill/canconfig-python/assets/examples/rpt_silent.json +7 -0
  45. canconfigpython/resources/skill/canconfig-python/assets/examples/user_data.json +7 -0
  46. canconfigpython/resources/skill/canconfig-python/references/operations.md +68 -0
  47. canconfigpython/resources.py +17 -0
  48. canconfigpython/runtime/Qt5Core.dll +0 -0
  49. canconfigpython/runtime/Qt5Network.dll +0 -0
  50. canconfigpython/runtime/Qt5SerialPort.dll +0 -0
  51. canconfigpython/runtime/WIICAN.dll +0 -0
  52. canconfigpython/runtime/canconfigconsole.exe +0 -0
  53. canconfigpython/runtime/libgcc_s_dw2-1.dll +0 -0
  54. canconfigpython/runtime/libstdc++-6.dll +0 -0
  55. canconfigpython/runtime/libwinpthread-1.dll +0 -0
  56. canconfigpython/runtime/manifest.json +19 -0
  57. canconfigpython/runtime.py +61 -0
  58. canconfigpython-0.1.0a1.dist-info/GPL-3.0.txt +674 -0
  59. canconfigpython-0.1.0a1.dist-info/LICENSE +21 -0
  60. canconfigpython-0.1.0a1.dist-info/METADATA +197 -0
  61. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-LICENSE.txt +866 -0
  62. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-LICENSE.FDL.txt +450 -0
  63. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-LICENSE.GPL2.txt +339 -0
  64. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-LICENSE.GPL3-EXCEPT.txt +704 -0
  65. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-LICENSE.GPL3.txt +674 -0
  66. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-LICENSE.GPLv3.txt +686 -0
  67. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-LICENSE.LGPL3.txt +165 -0
  68. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-LICENSE.LGPLv21.txt +514 -0
  69. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-LICENSE.LGPLv3.txt +175 -0
  70. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-LICENSE.PREVIEW.COMMERCIAL.txt +626 -0
  71. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__angle__LICENSE.txt +32 -0
  72. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__angle__src__libANGLE__renderer__d3d__d3d11__dxgi_support_data.json.txt +1164 -0
  73. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__angle__src__libANGLE__renderer__d3d__d3d11__load_functions_data.json.txt +1116 -0
  74. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__angle__src__libANGLE__renderer__d3d__d3d11__swizzle_format_data.json.txt +77 -0
  75. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__angle__src__libANGLE__renderer__d3d__d3d11__texture_format_data.json.txt +692 -0
  76. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__angle__src__third_party__compiler__LICENSE.txt +22 -0
  77. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__angle__src__third_party__murmurhash__LICENSE.txt +2 -0
  78. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__double-conversion__LICENSE.txt +26 -0
  79. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__easing__easing.cpp.txt +670 -0
  80. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__easing__legal.qdoc.txt +35 -0
  81. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__forkfd__forkfd.c.txt +875 -0
  82. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__forkfd__forkfd.h.txt +66 -0
  83. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__forkfd__forkfd_gcc.h.txt +63 -0
  84. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__freebsd__0001-Patch-the-FreeBSD-strto-u-ll-functions-to-work-insid.patch.txt +151 -0
  85. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__freebsd__strtoll.c.txt +134 -0
  86. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__freebsd__strtoull.c.txt +112 -0
  87. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__freetype__docs__LICENSE.TXT.txt +37 -0
  88. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__freetype__src__tools__no-copyright.txt +61 -0
  89. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__freetype__src__tools__update-copyright-year.txt +135 -0
  90. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__freetype__src__tools__update-copyright.txt +14 -0
  91. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__harfbuzz-ng__COPYING.txt +36 -0
  92. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__harfbuzz__COPYING.txt +24 -0
  93. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__libpng__LICENSE.txt +112 -0
  94. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__md4__md4.cpp.txt +265 -0
  95. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__md4__md4.h.txt +31 -0
  96. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__md5__md5.cpp.txt +247 -0
  97. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__md5__md5.h.txt +48 -0
  98. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__pcre__COPYING.txt +5 -0
  99. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__pcre__LICENCE.txt +93 -0
  100. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__rfc6234__patches__0001-Fix-compilation-MSVC-warning-in-3rdparty-rfc6234.patch.txt +86 -0
  101. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__rfc6234__sha-private.h.txt +28 -0
  102. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__rfc6234__sha.h.txt +357 -0
  103. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__rfc6234__sha224-256.c.txt +583 -0
  104. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__rfc6234__sha384-512.c.txt +1034 -0
  105. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__sha1__sha1.cpp.txt +261 -0
  106. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__sha3__KeccakF-1600-32-rvk.macros.txt +555 -0
  107. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__sha3__KeccakF-1600-32.macros.txt +26 -0
  108. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__sha3__KeccakF-1600-64.macros.txt +728 -0
  109. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__sha3__KeccakF-1600-int-set.h.txt +6 -0
  110. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__sha3__KeccakF-1600-interface.h.txt +46 -0
  111. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__sha3__KeccakF-1600-opt32-settings.h.txt +4 -0
  112. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__sha3__KeccakF-1600-opt32.c.txt +524 -0
  113. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__sha3__KeccakF-1600-opt64-settings.h.txt +7 -0
  114. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__sha3__KeccakF-1600-opt64.c.txt +512 -0
  115. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__sha3__KeccakF-1600-unrolling.macros.txt +124 -0
  116. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__sha3__KeccakNISTInterface.c.txt +82 -0
  117. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__sha3__KeccakNISTInterface.h.txt +70 -0
  118. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__sha3__KeccakSponge.c.txt +266 -0
  119. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__sha3__KeccakSponge.h.txt +80 -0
  120. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__sha3__brg_endian.h.txt +142 -0
  121. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__xkbcommon__COPYING.txt +188 -0
  122. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtserialport-LICENSE.FDL.txt +450 -0
  123. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtserialport-LICENSE.GPL2.txt +339 -0
  124. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtserialport-LICENSE.GPL3-EXCEPT.txt +704 -0
  125. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtserialport-LICENSE.GPL3.txt +674 -0
  126. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtserialport-LICENSE.GPLv2.txt +292 -0
  127. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtserialport-LICENSE.GPLv3.txt +686 -0
  128. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtserialport-LICENSE.LGPL3.txt +165 -0
  129. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtserialport-LICENSE.LGPLv21.txt +514 -0
  130. canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtserialport-LICENSE.LGPLv3.txt +175 -0
  131. canconfigpython-0.1.0a1.dist-info/RECORD +165 -0
  132. canconfigpython-0.1.0a1.dist-info/THIRD_PARTY_NOTICES.md +69 -0
  133. canconfigpython-0.1.0a1.dist-info/WHEEL +5 -0
  134. canconfigpython-0.1.0a1.dist-info/entry_points.txt +3 -0
  135. canconfigpython-0.1.0a1.dist-info/gcc-COPYING.RUNTIME.txt +73 -0
  136. canconfigpython-0.1.0a1.dist-info/gcc-COPYING3.LIB.txt +165 -0
  137. canconfigpython-0.1.0a1.dist-info/gcc-COPYING3.txt +674 -0
  138. canconfigpython-0.1.0a1.dist-info/linux-libc6-copyright.txt +529 -0
  139. canconfigpython-0.1.0a1.dist-info/linux-libcap2-copyright.txt +112 -0
  140. canconfigpython-0.1.0a1.dist-info/linux-libcom-err2-copyright.txt +403 -0
  141. canconfigpython-0.1.0a1.dist-info/linux-libdouble-conversion3-copyright.txt +41 -0
  142. canconfigpython-0.1.0a1.dist-info/linux-libgcc-s1-copyright.txt +1532 -0
  143. canconfigpython-0.1.0a1.dist-info/linux-libglib2.0-0t64-copyright.txt +1250 -0
  144. canconfigpython-0.1.0a1.dist-info/linux-libgssapi-krb5-2-copyright.txt +1374 -0
  145. canconfigpython-0.1.0a1.dist-info/linux-libicu74-copyright.txt +49 -0
  146. canconfigpython-0.1.0a1.dist-info/linux-libk5crypto3-copyright.txt +1374 -0
  147. canconfigpython-0.1.0a1.dist-info/linux-libkeyutils1-copyright.txt +52 -0
  148. canconfigpython-0.1.0a1.dist-info/linux-libkrb5-3-copyright.txt +1374 -0
  149. canconfigpython-0.1.0a1.dist-info/linux-libkrb5support0-copyright.txt +1374 -0
  150. canconfigpython-0.1.0a1.dist-info/linux-libpcre2-16-0-copyright.txt +122 -0
  151. canconfigpython-0.1.0a1.dist-info/linux-libpcre2-8-0-copyright.txt +122 -0
  152. canconfigpython-0.1.0a1.dist-info/linux-libqt5core5t64-copyright.txt +2932 -0
  153. canconfigpython-0.1.0a1.dist-info/linux-libqt5network5t64-copyright.txt +2932 -0
  154. canconfigpython-0.1.0a1.dist-info/linux-libqt5serialport5-copyright.txt +134 -0
  155. canconfigpython-0.1.0a1.dist-info/linux-libstdc++6-copyright.txt +1532 -0
  156. canconfigpython-0.1.0a1.dist-info/linux-libudev1-copyright.txt +287 -0
  157. canconfigpython-0.1.0a1.dist-info/linux-libzstd1-copyright.txt +123 -0
  158. canconfigpython-0.1.0a1.dist-info/linux-packages.json +102 -0
  159. canconfigpython-0.1.0a1.dist-info/linux-zlib1g-copyright.txt +84 -0
  160. canconfigpython-0.1.0a1.dist-info/mingw-w64-COPYING.MinGW-w64-runtime.txt.txt +240 -0
  161. canconfigpython-0.1.0a1.dist-info/mingw-w64-COPYING.MinGW-w64.txt.txt +135 -0
  162. canconfigpython-0.1.0a1.dist-info/mingw-w64-COPYING.txt +43 -0
  163. canconfigpython-0.1.0a1.dist-info/qt-source-notices.json +278 -0
  164. canconfigpython-0.1.0a1.dist-info/top_level.txt +1 -0
  165. canconfigpython-0.1.0a1.dist-info/winpthreads-COPYING.txt +57 -0
@@ -0,0 +1,875 @@
1
+ /****************************************************************************
2
+ **
3
+ ** Copyright (C) 2016 Intel Corporation.
4
+ ** Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
5
+ **
6
+ ** Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ ** of this software and associated documentation files (the "Software"), to deal
8
+ ** in the Software without restriction, including without limitation the rights
9
+ ** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ ** copies of the Software, and to permit persons to whom the Software is
11
+ ** furnished to do so, subject to the following conditions:
12
+ **
13
+ ** The above copyright notice and this permission notice shall be included in
14
+ ** all copies or substantial portions of the Software.
15
+ **
16
+ ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ ** THE SOFTWARE.
23
+ **
24
+ ****************************************************************************/
25
+
26
+ #ifndef _GNU_SOURCE
27
+ # define _GNU_SOURCE
28
+ #endif
29
+
30
+ #include "forkfd.h"
31
+
32
+ #include <sys/types.h>
33
+ #if defined(__OpenBSD__) || defined(__NetBSD__)
34
+ # include <sys/param.h>
35
+ #endif
36
+ #include <sys/time.h>
37
+ #include <sys/resource.h>
38
+ #include <sys/wait.h>
39
+ #include <assert.h>
40
+ #include <errno.h>
41
+ #include <pthread.h>
42
+ #include <signal.h>
43
+ #include <stdlib.h>
44
+ #include <string.h>
45
+ #include <time.h>
46
+ #include <unistd.h>
47
+
48
+ #ifdef __linux__
49
+ # define HAVE_WAIT4 1
50
+ # if defined(__BIONIC__) || (defined(__GLIBC__) && (__GLIBC__ << 8) + __GLIBC_MINOR__ >= 0x207 && \
51
+ (!defined(__UCLIBC__) || ((__UCLIBC_MAJOR__ << 16) + (__UCLIBC_MINOR__ << 8) + __UCLIBC_SUBLEVEL__ > 0x90201)))
52
+ # include <sys/eventfd.h>
53
+ # define HAVE_EVENTFD 1
54
+ # endif
55
+ # if defined(__BIONIC__) || (defined(__GLIBC__) && (__GLIBC__ << 8) + __GLIBC_MINOR__ >= 0x209 && \
56
+ (!defined(__UCLIBC__) || ((__UCLIBC_MAJOR__ << 16) + (__UCLIBC_MINOR__ << 8) + __UCLIBC_SUBLEVEL__ > 0x90201)))
57
+ # define HAVE_PIPE2 1
58
+ # endif
59
+ #endif
60
+ #if defined(__FreeBSD__) && __FreeBSD__ >= 9
61
+ # include <sys/procdesc.h>
62
+ #endif
63
+
64
+ #if _POSIX_VERSION-0 >= 200809L || _XOPEN_VERSION-0 >= 500
65
+ # define HAVE_WAITID 1
66
+ #endif
67
+ #if !defined(WEXITED) || !defined(WNOWAIT)
68
+ # undef HAVE_WAITID
69
+ #endif
70
+
71
+ #if (defined(__FreeBSD__) && defined(__FreeBSD_version) && __FreeBSD_version >= 1000032) || \
72
+ (defined(__OpenBSD__) && OpenBSD >= 201505) || \
73
+ (defined(__NetBSD__) && __NetBSD_Version__ >= 600000000)
74
+ # define HAVE_PIPE2 1
75
+ #endif
76
+ #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) || \
77
+ defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
78
+ # define HAVE_WAIT4 1
79
+ #endif
80
+
81
+ #if defined(__APPLE__)
82
+ /* Up until OS X 10.7, waitid(P_ALL, ...) will return success, but will not
83
+ * fill in the details of the dead child. That means waitid is not useful to us.
84
+ * Therefore, we only enable waitid() support if we're targetting OS X 10.8 or
85
+ * later.
86
+ */
87
+ # include <Availability.h>
88
+ # include <AvailabilityMacros.h>
89
+ # if MAC_OS_X_VERSION_MIN_REQUIRED <= 1070
90
+ # define HAVE_BROKEN_WAITID 1
91
+ # endif
92
+ #endif
93
+
94
+ #ifndef FFD_ATOMIC_RELAXED
95
+ # include "forkfd_gcc.h"
96
+ #endif
97
+
98
+ #define CHILDREN_IN_SMALL_ARRAY 16
99
+ #define CHILDREN_IN_BIG_ARRAY 256
100
+ #define sizeofarray(array) (sizeof(array)/sizeof(array[0]))
101
+ #define EINTR_LOOP(ret, call) \
102
+ do { \
103
+ ret = call; \
104
+ } while (ret == -1 && errno == EINTR)
105
+
106
+ struct pipe_payload
107
+ {
108
+ struct forkfd_info info;
109
+ struct rusage rusage;
110
+ };
111
+
112
+ typedef struct process_info
113
+ {
114
+ ffd_atomic_int pid;
115
+ int deathPipe;
116
+ } ProcessInfo;
117
+
118
+ struct BigArray;
119
+ typedef struct Header
120
+ {
121
+ ffd_atomic_pointer(struct BigArray) nextArray;
122
+ ffd_atomic_int busyCount;
123
+ } Header;
124
+
125
+ typedef struct BigArray
126
+ {
127
+ Header header;
128
+ ProcessInfo entries[CHILDREN_IN_BIG_ARRAY];
129
+ } BigArray;
130
+
131
+ typedef struct SmallArray
132
+ {
133
+ Header header;
134
+ ProcessInfo entries[CHILDREN_IN_SMALL_ARRAY];
135
+ } SmallArray;
136
+ static SmallArray children;
137
+
138
+ static struct sigaction old_sigaction;
139
+ static pthread_once_t forkfd_initialization = PTHREAD_ONCE_INIT;
140
+ static ffd_atomic_int forkfd_status = FFD_ATOMIC_INIT(0);
141
+
142
+ #ifdef HAVE_BROKEN_WAITID
143
+ static int waitid_works = 0;
144
+ #else
145
+ static const int waitid_works = 1;
146
+ #endif
147
+
148
+ static ProcessInfo *tryAllocateInSection(Header *header, ProcessInfo entries[], int maxCount)
149
+ {
150
+ /* we use ACQUIRE here because the signal handler might have released the PID */
151
+ int busyCount = ffd_atomic_add_fetch(&header->busyCount, 1, FFD_ATOMIC_ACQUIRE);
152
+ if (busyCount <= maxCount) {
153
+ /* there's an available entry in this section, find it and take it */
154
+ int i;
155
+ for (i = 0; i < maxCount; ++i) {
156
+ /* if the PID is 0, it's free; mark it as used by swapping it with -1 */
157
+ int expected_pid = 0;
158
+ if (ffd_atomic_compare_exchange(&entries[i].pid, &expected_pid,
159
+ -1, FFD_ATOMIC_RELAXED, FFD_ATOMIC_RELAXED))
160
+ return &entries[i];
161
+ }
162
+ }
163
+
164
+ /* there isn't an available entry, undo our increment */
165
+ ffd_atomic_add_fetch(&header->busyCount, -1, FFD_ATOMIC_RELAXED);
166
+ return NULL;
167
+ }
168
+
169
+ static ProcessInfo *allocateInfo(Header **header)
170
+ {
171
+ Header *currentHeader = &children.header;
172
+
173
+ /* try to find an available entry in the small array first */
174
+ ProcessInfo *info =
175
+ tryAllocateInSection(currentHeader, children.entries, sizeofarray(children.entries));
176
+
177
+ /* go on to the next arrays */
178
+ while (info == NULL) {
179
+ BigArray *array = ffd_atomic_load(&currentHeader->nextArray, FFD_ATOMIC_ACQUIRE);
180
+ if (array == NULL) {
181
+ /* allocate an array and try to use it */
182
+ BigArray *allocatedArray = (BigArray *)calloc(1, sizeof(BigArray));
183
+ if (allocatedArray == NULL)
184
+ return NULL;
185
+
186
+ if (ffd_atomic_compare_exchange(&currentHeader->nextArray, &array, allocatedArray,
187
+ FFD_ATOMIC_RELEASE, FFD_ATOMIC_ACQUIRE)) {
188
+ /* success */
189
+ array = allocatedArray;
190
+ } else {
191
+ /* failed, the atomic updated 'array' */
192
+ free(allocatedArray);
193
+ }
194
+ }
195
+
196
+ currentHeader = &array->header;
197
+ info = tryAllocateInSection(currentHeader, array->entries, sizeofarray(array->entries));
198
+ }
199
+
200
+ *header = currentHeader;
201
+ return info;
202
+ }
203
+
204
+ #ifdef HAVE_WAITID
205
+ static int isChildReady(pid_t pid, siginfo_t *info)
206
+ {
207
+ info->si_pid = 0;
208
+ return waitid(P_PID, pid, info, WEXITED | WNOHANG | WNOWAIT) == 0 && info->si_pid == pid;
209
+ }
210
+ #endif
211
+
212
+ static void convertStatusToForkfdInfo(int status, struct forkfd_info *info)
213
+ {
214
+ if (WIFEXITED(status)) {
215
+ info->code = CLD_EXITED;
216
+ info->status = WEXITSTATUS(status);
217
+ } else if (WIFSIGNALED(status)) {
218
+ info->code = CLD_KILLED;
219
+ # ifdef WCOREDUMP
220
+ if (WCOREDUMP(status))
221
+ info->code = CLD_DUMPED;
222
+ # endif
223
+ info->status = WTERMSIG(status);
224
+ }
225
+ }
226
+
227
+ static int tryReaping(pid_t pid, struct pipe_payload *payload)
228
+ {
229
+ /* reap the child */
230
+ #if defined(HAVE_WAIT4)
231
+ int status;
232
+ if (wait4(pid, &status, WNOHANG, &payload->rusage) <= 0)
233
+ return 0;
234
+ convertStatusToForkfdInfo(status, &payload->info);
235
+ #else
236
+ # if defined(HAVE_WAITID)
237
+ if (waitid_works) {
238
+ /* we have waitid(2), which gets us some payload values on some systems */
239
+ siginfo_t info;
240
+ info.si_pid = 0;
241
+ int ret = waitid(P_PID, pid, &info, WEXITED | WNOHANG) == 0 && info.si_pid == pid;
242
+ if (!ret)
243
+ return ret;
244
+
245
+ payload->info.code = info.si_code;
246
+ payload->info.status = info.si_status;
247
+ # ifdef __linux__
248
+ payload->rusage.ru_utime.tv_sec = info.si_utime / CLOCKS_PER_SEC;
249
+ payload->rusage.ru_utime.tv_usec = info.si_utime % CLOCKS_PER_SEC;
250
+ payload->rusage.ru_stime.tv_sec = info.si_stime / CLOCKS_PER_SEC;
251
+ payload->rusage.ru_stime.tv_usec = info.si_stime % CLOCKS_PER_SEC;
252
+ # endif
253
+ return 1;
254
+ }
255
+ # endif // HAVE_WAITID
256
+ int status;
257
+ if (waitpid(pid, &status, WNOHANG) <= 0)
258
+ return 0; // child did not change state
259
+ convertStatusToForkfdInfo(status, &payload->info);
260
+ #endif // !HAVE_WAIT4
261
+
262
+ return 1;
263
+ }
264
+
265
+ static void freeInfo(Header *header, ProcessInfo *entry)
266
+ {
267
+ entry->deathPipe = -1;
268
+ entry->pid = 0;
269
+
270
+ ffd_atomic_add_fetch(&header->busyCount, -1, FFD_ATOMIC_RELEASE);
271
+ assert(header->busyCount >= 0);
272
+ }
273
+
274
+ static void notifyAndFreeInfo(Header *header, ProcessInfo *entry,
275
+ const struct pipe_payload *payload)
276
+ {
277
+ ssize_t ret;
278
+ EINTR_LOOP(ret, write(entry->deathPipe, payload, sizeof(*payload)));
279
+ EINTR_LOOP(ret, close(entry->deathPipe));
280
+
281
+ freeInfo(header, entry);
282
+ }
283
+
284
+ static void sigchld_handler(int signum)
285
+ {
286
+ /*
287
+ * This is a signal handler, so we need to be careful about which functions
288
+ * we can call. See the full, official listing in the POSIX.1-2008
289
+ * specification at:
290
+ * http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04_03
291
+ *
292
+ */
293
+
294
+ if (ffd_atomic_load(&forkfd_status, FFD_ATOMIC_RELAXED) == 1) {
295
+ /* is this one of our children? */
296
+ BigArray *array;
297
+ siginfo_t info;
298
+ struct pipe_payload payload;
299
+ int i;
300
+
301
+ memset(&info, 0, sizeof info);
302
+ memset(&payload, 0, sizeof payload);
303
+
304
+ #ifdef HAVE_WAITID
305
+ if (!waitid_works)
306
+ goto search_arrays;
307
+
308
+ /* be optimistic: try to see if we can get the child that exited */
309
+ search_next_child:
310
+ /* waitid returns -1 ECHILD if there are no further children at all;
311
+ * it returns 0 and sets si_pid to 0 if there are children but they are not ready
312
+ * to be waited (we're passing WNOHANG). We should not get EINTR because
313
+ * we're passing WNOHANG and we should definitely not get EINVAL or anything else.
314
+ * That means we can actually ignore the return code and only inspect si_pid.
315
+ */
316
+ info.si_pid = 0;
317
+ waitid(P_ALL, 0, &info, WNOHANG | WNOWAIT | WEXITED);
318
+ if (info.si_pid == 0) {
319
+ /* there are no further un-waited-for children, so we can just exit.
320
+ * But before, transfer control to the chained SIGCHLD handler.
321
+ */
322
+ goto chain_handler;
323
+ }
324
+
325
+ for (i = 0; i < (int)sizeofarray(children.entries); ++i) {
326
+ /* acquire the child first: swap the PID with -1 to indicate it's busy */
327
+ int pid = info.si_pid;
328
+ if (ffd_atomic_compare_exchange(&children.entries[i].pid, &pid, -1,
329
+ FFD_ATOMIC_ACQUIRE, FFD_ATOMIC_RELAXED)) {
330
+ /* this is our child, send notification and free up this entry */
331
+ /* ### FIXME: what if tryReaping returns false? */
332
+ if (tryReaping(pid, &payload))
333
+ notifyAndFreeInfo(&children.header, &children.entries[i], &payload);
334
+ goto search_next_child;
335
+ }
336
+ }
337
+
338
+ /* try the arrays */
339
+ array = ffd_atomic_load(&children.header.nextArray, FFD_ATOMIC_ACQUIRE);
340
+ while (array != NULL) {
341
+ for (i = 0; i < (int)sizeofarray(array->entries); ++i) {
342
+ int pid = info.si_pid;
343
+ if (ffd_atomic_compare_exchange(&array->entries[i].pid, &pid, -1,
344
+ FFD_ATOMIC_ACQUIRE, FFD_ATOMIC_RELAXED)) {
345
+ /* this is our child, send notification and free up this entry */
346
+ /* ### FIXME: what if tryReaping returns false? */
347
+ if (tryReaping(pid, &payload))
348
+ notifyAndFreeInfo(&array->header, &array->entries[i], &payload);
349
+ goto search_next_child;
350
+ }
351
+ }
352
+
353
+ array = ffd_atomic_load(&array->header.nextArray, FFD_ATOMIC_ACQUIRE);
354
+ }
355
+
356
+ /* if we got here, we couldn't find this child in our list. That means this child
357
+ * belongs to one of the chained SIGCHLD handlers. However, there might be another
358
+ * child that exited and does belong to us, so we need to check each one individually.
359
+ */
360
+
361
+ search_arrays:
362
+ #endif
363
+
364
+ for (i = 0; i < (int)sizeofarray(children.entries); ++i) {
365
+ int pid = ffd_atomic_load(&children.entries[i].pid, FFD_ATOMIC_ACQUIRE);
366
+ if (pid <= 0)
367
+ continue;
368
+ #ifdef HAVE_WAITID
369
+ if (waitid_works) {
370
+ /* The child might have been reaped by the block above in another thread,
371
+ * so first check if it's ready and, if it is, lock it */
372
+ if (!isChildReady(pid, &info) ||
373
+ !ffd_atomic_compare_exchange(&children.entries[i].pid, &pid, -1,
374
+ FFD_ATOMIC_RELAXED, FFD_ATOMIC_RELAXED))
375
+ continue;
376
+ }
377
+ #endif
378
+ if (tryReaping(pid, &payload)) {
379
+ /* this is our child, send notification and free up this entry */
380
+ notifyAndFreeInfo(&children.header, &children.entries[i], &payload);
381
+ }
382
+ }
383
+
384
+ /* try the arrays */
385
+ array = ffd_atomic_load(&children.header.nextArray, FFD_ATOMIC_ACQUIRE);
386
+ while (array != NULL) {
387
+ for (i = 0; i < (int)sizeofarray(array->entries); ++i) {
388
+ int pid = ffd_atomic_load(&array->entries[i].pid, FFD_ATOMIC_ACQUIRE);
389
+ if (pid <= 0)
390
+ continue;
391
+ #ifdef HAVE_WAITID
392
+ if (waitid_works) {
393
+ /* The child might have been reaped by the block above in another thread,
394
+ * so first check if it's ready and, if it is, lock it */
395
+ if (!isChildReady(pid, &info) ||
396
+ !ffd_atomic_compare_exchange(&array->entries[i].pid, &pid, -1,
397
+ FFD_ATOMIC_RELAXED, FFD_ATOMIC_RELAXED))
398
+ continue;
399
+ }
400
+ #endif
401
+ if (tryReaping(pid, &payload)) {
402
+ /* this is our child, send notification and free up this entry */
403
+ notifyAndFreeInfo(&array->header, &array->entries[i], &payload);
404
+ }
405
+ }
406
+
407
+ array = ffd_atomic_load(&array->header.nextArray, FFD_ATOMIC_ACQUIRE);
408
+ }
409
+ }
410
+
411
+ #ifdef HAVE_WAITID
412
+ chain_handler:
413
+ #endif
414
+ if (old_sigaction.sa_handler != SIG_IGN && old_sigaction.sa_handler != SIG_DFL)
415
+ old_sigaction.sa_handler(signum);
416
+ }
417
+
418
+ static void ignore_sigpipe()
419
+ {
420
+ #ifdef O_NOSIGPIPE
421
+ static ffd_atomic_int done = FFD_ATOMIC_INIT(0);
422
+ if (ffd_atomic_load(&done, FFD_ATOMIC_RELAXED))
423
+ return;
424
+ #endif
425
+
426
+ struct sigaction action;
427
+ memset(&action, 0, sizeof action);
428
+ sigemptyset(&action.sa_mask);
429
+ action.sa_handler = SIG_IGN;
430
+ action.sa_flags = 0;
431
+ sigaction(SIGPIPE, &action, NULL);
432
+
433
+ #ifdef O_NOSIGPIPE
434
+ ffd_atomic_store(&done, 1, FFD_ATOMIC_RELAXED);
435
+ #endif
436
+ }
437
+
438
+ static void forkfd_initialize()
439
+ {
440
+ #if defined(HAVE_BROKEN_WAITID)
441
+ pid_t pid = fork();
442
+ if (pid == 0) {
443
+ _exit(0);
444
+ } else if (pid > 0) {
445
+ siginfo_t info;
446
+ waitid(P_ALL, 0, &info, WNOWAIT | WEXITED);
447
+ waitid_works = (info.si_pid != 0);
448
+ info.si_pid = 0;
449
+
450
+ // now really reap the child
451
+ waitid(P_PID, pid, &info, WEXITED);
452
+ waitid_works = waitid_works && (info.si_pid != 0);
453
+ }
454
+ #endif
455
+
456
+ /* install our signal handler */
457
+ struct sigaction action;
458
+ memset(&action, 0, sizeof action);
459
+ sigemptyset(&action.sa_mask);
460
+ action.sa_flags = SA_NOCLDSTOP;
461
+ action.sa_handler = sigchld_handler;
462
+
463
+ /* ### RACE CONDITION
464
+ * The sigaction function does a memcpy from an internal buffer
465
+ * to old_sigaction, which we use in the SIGCHLD handler. If a
466
+ * SIGCHLD is delivered before or during that memcpy, the handler will
467
+ * see an inconsistent state.
468
+ *
469
+ * There is no solution. pthread_sigmask doesn't work here because the
470
+ * signal could be delivered to another thread.
471
+ */
472
+ sigaction(SIGCHLD, &action, &old_sigaction);
473
+
474
+ #ifndef O_NOSIGPIPE
475
+ /* disable SIGPIPE too */
476
+ ignore_sigpipe();
477
+ #endif
478
+
479
+ #ifndef __GNUC__
480
+ atexit(cleanup);
481
+ #endif
482
+
483
+ ffd_atomic_store(&forkfd_status, 1, FFD_ATOMIC_RELAXED);
484
+ }
485
+
486
+ #ifdef __GNUC__
487
+ __attribute((destructor, unused)) static void cleanup();
488
+ #endif
489
+
490
+ static void cleanup()
491
+ {
492
+ BigArray *array;
493
+ /* This function is not thread-safe!
494
+ * It must only be called when the process is shutting down.
495
+ * At shutdown, we expect no one to be calling forkfd(), so we don't
496
+ * need to be thread-safe with what is done there.
497
+ *
498
+ * But SIGCHLD might be delivered to any thread, including this one.
499
+ * There's no way to prevent that. The correct solution would be to
500
+ * cooperatively delete. We don't do that.
501
+ */
502
+ if (ffd_atomic_load(&forkfd_status, FFD_ATOMIC_RELAXED) == 0)
503
+ return;
504
+
505
+ /* notify the handler that we're no longer in operation */
506
+ ffd_atomic_store(&forkfd_status, 0, FFD_ATOMIC_RELAXED);
507
+
508
+ /* free any arrays we might have */
509
+ array = children.header.nextArray;
510
+ while (array != NULL) {
511
+ BigArray *next = array->header.nextArray;
512
+ free(array);
513
+ array = next;
514
+ }
515
+ }
516
+
517
+ static int create_pipe(int filedes[], int flags)
518
+ {
519
+ int ret = -1;
520
+ #ifdef HAVE_PIPE2
521
+ /* use pipe2(2) whenever possible, since it can thread-safely create a
522
+ * cloexec pair of pipes. Without it, we have a race condition setting
523
+ * FD_CLOEXEC
524
+ */
525
+
526
+ # ifdef O_NOSIGPIPE
527
+ /* try first with O_NOSIGPIPE */
528
+ ret = pipe2(filedes, O_CLOEXEC | O_NOSIGPIPE);
529
+ if (ret == -1) {
530
+ /* O_NOSIGPIPE not supported, ignore SIGPIPE */
531
+ ignore_sigpipe();
532
+ }
533
+ # endif
534
+ if (ret == -1)
535
+ ret = pipe2(filedes, O_CLOEXEC);
536
+ if (ret == -1)
537
+ return ret;
538
+
539
+ if ((flags & FFD_CLOEXEC) == 0)
540
+ fcntl(filedes[0], F_SETFD, 0);
541
+ #else
542
+ ret = pipe(filedes);
543
+ if (ret == -1)
544
+ return ret;
545
+
546
+ fcntl(filedes[1], F_SETFD, FD_CLOEXEC);
547
+ if (flags & FFD_CLOEXEC)
548
+ fcntl(filedes[0], F_SETFD, FD_CLOEXEC);
549
+ #endif
550
+ if (flags & FFD_NONBLOCK)
551
+ fcntl(filedes[0], F_SETFL, fcntl(filedes[0], F_GETFL) | O_NONBLOCK);
552
+ return ret;
553
+ }
554
+
555
+ #if defined(FORKFD_NO_SPAWNFD) && defined(__FreeBSD__) && __FreeBSD__ >= 9
556
+ # if __FreeBSD__ == 9
557
+ /* PROCDESC is an optional feature in the kernel and wasn't enabled
558
+ * by default on FreeBSD 9. So we need to check for it at runtime. */
559
+ static ffd_atomic_int system_has_forkfd = FFD_ATOMIC_INIT(1);
560
+ # else
561
+ /* On FreeBSD 10, PROCDESC was enabled by default. On v11, it's not an option
562
+ * anymore and can't be disabled. */
563
+ static const int system_has_forkfd = 1;
564
+ # endif
565
+
566
+ static int system_forkfd(int flags, pid_t *ppid)
567
+ {
568
+ int ret;
569
+ pid_t pid;
570
+ pid = pdfork(&ret, PD_DAEMON);
571
+ if (__builtin_expect(pid == -1, 0)) {
572
+ # if __FreeBSD__ == 9
573
+ if (errno == ENOSYS) {
574
+ /* PROCDESC wasn't compiled into the kernel: don't try it again. */
575
+ ffd_atomic_store(&system_has_forkfd, 0, FFD_ATOMIC_RELAXED);
576
+ }
577
+ # endif
578
+ return -1;
579
+ }
580
+ if (pid == 0) {
581
+ /* child process */
582
+ return FFD_CHILD_PROCESS;
583
+ }
584
+
585
+ /* parent process */
586
+ if (flags & FFD_CLOEXEC)
587
+ fcntl(ret, F_SETFD, FD_CLOEXEC);
588
+ if (flags & FFD_NONBLOCK)
589
+ fcntl(ret, F_SETFL, fcntl(ret, F_GETFL) | O_NONBLOCK);
590
+ if (ppid)
591
+ *ppid = pid;
592
+ return ret;
593
+ }
594
+ #else
595
+ static const int system_has_forkfd = 0;
596
+ static int system_forkfd(int flags, pid_t *ppid)
597
+ {
598
+ (void)flags;
599
+ (void)ppid;
600
+ return -1;
601
+ }
602
+ #endif
603
+
604
+ #ifndef FORKFD_NO_FORKFD
605
+ /**
606
+ * @brief forkfd returns a file descriptor representing a child process
607
+ * @return a file descriptor, or -1 in case of failure
608
+ *
609
+ * forkfd() creates a file descriptor that can be used to be notified of when a
610
+ * child process exits. This file descriptor can be monitored using select(2),
611
+ * poll(2) or similar mechanisms.
612
+ *
613
+ * The @a flags parameter can contain the following values ORed to change the
614
+ * behaviour of forkfd():
615
+ *
616
+ * @li @c FFD_NONBLOCK Set the O_NONBLOCK file status flag on the new open file
617
+ * descriptor. Using this flag saves extra calls to fnctl(2) to achieve the same
618
+ * result.
619
+ *
620
+ * @li @c FFD_CLOEXEC Set the close-on-exec (FD_CLOEXEC) flag on the new file
621
+ * descriptor. You probably want to set this flag, since forkfd() does not work
622
+ * if the original parent process dies.
623
+ *
624
+ * The file descriptor returned by forkfd() supports the following operations:
625
+ *
626
+ * @li read(2) When the child process exits, then the buffer supplied to
627
+ * read(2) is used to return information about the status of the child in the
628
+ * form of one @c siginfo_t structure. The buffer must be at least
629
+ * sizeof(siginfo_t) bytes. The return value of read(2) is the total number of
630
+ * bytes read.
631
+ *
632
+ * @li poll(2), select(2) (and similar) The file descriptor is readable (the
633
+ * select(2) readfds argument; the poll(2) POLLIN flag) if the child has exited
634
+ * or signalled via SIGCHLD.
635
+ *
636
+ * @li close(2) When the file descriptor is no longer required it should be closed.
637
+ */
638
+ int forkfd(int flags, pid_t *ppid)
639
+ {
640
+ Header *header;
641
+ ProcessInfo *info;
642
+ pid_t pid;
643
+ int fd = -1;
644
+ int death_pipe[2];
645
+ int sync_pipe[2];
646
+ int ret;
647
+ #ifdef __linux__
648
+ int efd;
649
+ #endif
650
+
651
+ if (system_has_forkfd) {
652
+ ret = system_forkfd(flags, ppid);
653
+ if (system_has_forkfd)
654
+ return ret;
655
+ }
656
+
657
+ (void) pthread_once(&forkfd_initialization, forkfd_initialize);
658
+
659
+ info = allocateInfo(&header);
660
+ if (info == NULL) {
661
+ errno = ENOMEM;
662
+ return -1;
663
+ }
664
+
665
+ /* create the pipes before we fork */
666
+ if (create_pipe(death_pipe, flags) == -1)
667
+ goto err_free; /* failed to create the pipes, pass errno */
668
+
669
+ #ifdef HAVE_EVENTFD
670
+ /* try using an eventfd, which consumes less resources */
671
+ efd = eventfd(0, EFD_CLOEXEC);
672
+ if (efd == -1)
673
+ #endif
674
+ {
675
+ /* try a pipe */
676
+ if (create_pipe(sync_pipe, FFD_CLOEXEC) == -1) {
677
+ /* failed both at eventfd and pipe; fail and pass errno */
678
+ goto err_close;
679
+ }
680
+ }
681
+
682
+ /* now fork */
683
+ pid = fork();
684
+ if (pid == -1)
685
+ goto err_close2; /* failed to fork, pass errno */
686
+ if (ppid)
687
+ *ppid = pid;
688
+
689
+ /*
690
+ * We need to store the child's PID in the info structure, so
691
+ * the SIGCHLD handler knows that this child is present and it
692
+ * knows the writing end of the pipe to pass information on.
693
+ * However, the child process could exit before we stored the
694
+ * information (or the handler could run for other children exiting).
695
+ * We prevent that from happening by blocking the child process in
696
+ * a read(2) until we're finished storing the information.
697
+ */
698
+ if (pid == 0) {
699
+ /* this is the child process */
700
+ /* first, wait for the all clear */
701
+ #ifdef HAVE_EVENTFD
702
+ if (efd != -1) {
703
+ eventfd_t val64;
704
+ EINTR_LOOP(ret, eventfd_read(efd, &val64));
705
+ EINTR_LOOP(ret, close(efd));
706
+ } else
707
+ #endif
708
+ {
709
+ char c;
710
+ EINTR_LOOP(ret, close(sync_pipe[1]));
711
+ EINTR_LOOP(ret, read(sync_pipe[0], &c, sizeof c));
712
+ EINTR_LOOP(ret, close(sync_pipe[0]));
713
+ }
714
+
715
+ /* now close the pipes and return to the caller */
716
+ EINTR_LOOP(ret, close(death_pipe[0]));
717
+ EINTR_LOOP(ret, close(death_pipe[1]));
718
+ fd = FFD_CHILD_PROCESS;
719
+ } else {
720
+ /* parent process */
721
+ info->deathPipe = death_pipe[1];
722
+ fd = death_pipe[0];
723
+ ffd_atomic_store(&info->pid, pid, FFD_ATOMIC_RELEASE);
724
+
725
+ /* release the child */
726
+ #ifdef HAVE_EVENTFD
727
+ if (efd != -1) {
728
+ eventfd_t val64 = 42;
729
+ EINTR_LOOP(ret, eventfd_write(efd, val64));
730
+ EINTR_LOOP(ret, close(efd));
731
+ } else
732
+ #endif
733
+ {
734
+ /*
735
+ * Usually, closing would be enough to make read(2) return and the child process
736
+ * continue. We need to write here: another thread could be calling forkfd at the
737
+ * same time, which means auxpipe[1] might be open in another child process.
738
+ */
739
+ EINTR_LOOP(ret, close(sync_pipe[0]));
740
+ EINTR_LOOP(ret, write(sync_pipe[1], "", 1));
741
+ EINTR_LOOP(ret, close(sync_pipe[1]));
742
+ }
743
+ }
744
+
745
+ return fd;
746
+
747
+ err_close2:
748
+ #ifdef HAVE_EVENTFD
749
+ if (efd != -1) {
750
+ EINTR_LOOP(ret, close(efd));
751
+ } else
752
+ #endif
753
+ {
754
+ EINTR_LOOP(ret, close(sync_pipe[0]));
755
+ EINTR_LOOP(ret, close(sync_pipe[1]));
756
+ }
757
+ err_close:
758
+ EINTR_LOOP(ret, close(death_pipe[0]));
759
+ EINTR_LOOP(ret, close(death_pipe[1]));
760
+ err_free:
761
+ /* free the info pointer */
762
+ freeInfo(header, info);
763
+ return -1;
764
+ }
765
+ #endif // FORKFD_NO_FORKFD
766
+
767
+ #if _POSIX_SPAWN > 0 && !defined(FORKFD_NO_SPAWNFD)
768
+ int spawnfd(int flags, pid_t *ppid, const char *path, const posix_spawn_file_actions_t *file_actions,
769
+ posix_spawnattr_t *attrp, char *const argv[], char *const envp[])
770
+ {
771
+ Header *header;
772
+ ProcessInfo *info;
773
+ struct pipe_payload payload;
774
+ pid_t pid;
775
+ int death_pipe[2];
776
+ int ret = -1;
777
+ /* we can only do work if we have a way to start the child in stopped mode;
778
+ * otherwise, we have a major race condition. */
779
+
780
+ assert(!system_has_forkfd);
781
+
782
+ (void) pthread_once(&forkfd_initialization, forkfd_initialize);
783
+
784
+ info = allocateInfo(&header);
785
+ if (info == NULL) {
786
+ errno = ENOMEM;
787
+ goto out;
788
+ }
789
+
790
+ /* create the pipe before we spawn */
791
+ if (create_pipe(death_pipe, flags) == -1)
792
+ goto err_free; /* failed to create the pipes, pass errno */
793
+
794
+ /* start the process */
795
+ if (flags & FFD_SPAWN_SEARCH_PATH) {
796
+ /* use posix_spawnp */
797
+ if (posix_spawnp(&pid, path, file_actions, attrp, argv, envp) != 0)
798
+ goto err_close;
799
+ } else {
800
+ if (posix_spawn(&pid, path, file_actions, attrp, argv, envp) != 0)
801
+ goto err_close;
802
+ }
803
+
804
+ if (ppid)
805
+ *ppid = pid;
806
+
807
+ /* Store the child's PID in the info structure.
808
+ */
809
+ info->deathPipe = death_pipe[1];
810
+ ffd_atomic_store(&info->pid, pid, FFD_ATOMIC_RELEASE);
811
+
812
+ /* check if the child has already exited */
813
+ if (tryReaping(pid, &payload))
814
+ notifyAndFreeInfo(header, info, &payload);
815
+
816
+ ret = death_pipe[0];
817
+ return ret;
818
+
819
+ err_close:
820
+ EINTR_LOOP(ret, close(death_pipe[0]));
821
+ EINTR_LOOP(ret, close(death_pipe[1]));
822
+
823
+ err_free:
824
+ /* free the info pointer */
825
+ freeInfo(header, info);
826
+
827
+ out:
828
+ return -1;
829
+ }
830
+ #endif // _POSIX_SPAWN && !FORKFD_NO_SPAWNFD
831
+
832
+
833
+ int forkfd_wait(int ffd, forkfd_info *info, struct rusage *rusage)
834
+ {
835
+ struct pipe_payload payload;
836
+ int ret;
837
+
838
+ if (system_has_forkfd) {
839
+ #if defined(__FreeBSD__) && __FreeBSD__ >= 9
840
+ pid_t pid;
841
+ int status;
842
+ int options = WEXITED;
843
+
844
+ ret = pdgetpid(ffd, &pid);
845
+ if (ret == -1)
846
+ return ret;
847
+ ret = fcntl(ffd, F_GETFL);
848
+ if (ret == -1)
849
+ return ret;
850
+ options |= (ret & O_NONBLOCK) ? WNOHANG : 0;
851
+ ret = wait4(pid, &status, options, rusage);
852
+ if (ret != -1 && info)
853
+ convertStatusToForkfdInfo(status, info);
854
+ return ret == -1 ? -1 : 0;
855
+ #endif
856
+ }
857
+
858
+ ret = read(ffd, &payload, sizeof(payload));
859
+ if (ret == -1)
860
+ return ret; /* pass errno, probably EINTR, EBADF or EWOULDBLOCK */
861
+
862
+ assert(ret == sizeof(payload));
863
+ if (info)
864
+ *info = payload.info;
865
+ if (rusage)
866
+ *rusage = payload.rusage;
867
+
868
+ return 0; /* success */
869
+ }
870
+
871
+
872
+ int forkfd_close(int ffd)
873
+ {
874
+ return close(ffd);
875
+ }