CANConfigPython 0.1.0a1__py3-none-win32.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.
- canconfigpython/__init__.py +7 -0
- canconfigpython/__main__.py +3 -0
- canconfigpython/cli.py +111 -0
- canconfigpython/client.py +243 -0
- canconfigpython/commands.py +57 -0
- canconfigpython/errors.py +19 -0
- canconfigpython/resources/docs/native-source.tar.gz +0 -0
- canconfigpython/resources/docs/user-guide.en.md +274 -0
- canconfigpython/resources/docs/user-guide.zh-CN.md +265 -0
- canconfigpython/resources/skill/canconfig-python/SKILL.md +71 -0
- canconfigpython/resources/skill/canconfig-python/agents/openai.yaml +4 -0
- canconfigpython/resources/skill/canconfig-python/assets/changes.schema.json +832 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/bridge_dual_rates.json +18 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/bridge_mapping.json +13 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/cancom_115200_8n1.json +10 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/cancom_format_crc.json +8 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/cancom_gap.json +8 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/cancom_transparent_id.json +10 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/fb_1m.json +12 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/fb_500k.json +12 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/fb_adaptive_single.json +7 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/fb_return_manual.json +22 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/fb_silent_adaptive_option.json +7 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/filter_blacklist.json +7 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/filter_mask.json +12 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/filter_range.json +12 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/hub_h_external.json +13 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/hub_input_filter.json +13 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/hub_l_external.json +13 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/hub_mapping.json +46 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/hub_routing.json +8 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/index.en.md +55 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/index.json +661 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/index.zh-CN.md +55 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/negative/adaptive_mixed.json +8 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/negative/hub_missing_ps.json +12 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/negative/restricted_inheritance.json +12 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/negative/short_user_data.json +7 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/negative/string_numeric_id.json +7 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/negative/unknown_field.json +7 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/negative/unsupported_adaptive.json +7 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/negative/wrong_uid.json +7 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/rpt_adaptive_all.json +12 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/rpt_silent.json +7 -0
- canconfigpython/resources/skill/canconfig-python/assets/examples/user_data.json +7 -0
- canconfigpython/resources/skill/canconfig-python/references/operations.md +68 -0
- canconfigpython/resources.py +17 -0
- canconfigpython/runtime/Qt5Core.dll +0 -0
- canconfigpython/runtime/Qt5Network.dll +0 -0
- canconfigpython/runtime/Qt5SerialPort.dll +0 -0
- canconfigpython/runtime/WIICAN.dll +0 -0
- canconfigpython/runtime/canconfigconsole.exe +0 -0
- canconfigpython/runtime/libgcc_s_dw2-1.dll +0 -0
- canconfigpython/runtime/libstdc++-6.dll +0 -0
- canconfigpython/runtime/libwinpthread-1.dll +0 -0
- canconfigpython/runtime/manifest.json +19 -0
- canconfigpython/runtime.py +61 -0
- canconfigpython-0.1.0a1.dist-info/GPL-3.0.txt +674 -0
- canconfigpython-0.1.0a1.dist-info/LICENSE +21 -0
- canconfigpython-0.1.0a1.dist-info/METADATA +197 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-LICENSE.txt +866 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-LICENSE.FDL.txt +450 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-LICENSE.GPL2.txt +339 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-LICENSE.GPL3-EXCEPT.txt +704 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-LICENSE.GPL3.txt +674 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-LICENSE.GPLv3.txt +686 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-LICENSE.LGPL3.txt +165 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-LICENSE.LGPLv21.txt +514 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-LICENSE.LGPLv3.txt +175 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-LICENSE.PREVIEW.COMMERCIAL.txt +626 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__angle__LICENSE.txt +32 -0
- 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
- 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
- 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
- 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
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__angle__src__third_party__compiler__LICENSE.txt +22 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__angle__src__third_party__murmurhash__LICENSE.txt +2 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__double-conversion__LICENSE.txt +26 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__easing__easing.cpp.txt +670 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__easing__legal.qdoc.txt +35 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__forkfd__forkfd.c.txt +875 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__forkfd__forkfd.h.txt +66 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__forkfd__forkfd_gcc.h.txt +63 -0
- 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
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__freebsd__strtoll.c.txt +134 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__freebsd__strtoull.c.txt +112 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__freetype__docs__LICENSE.TXT.txt +37 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__freetype__src__tools__no-copyright.txt +61 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__freetype__src__tools__update-copyright-year.txt +135 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__freetype__src__tools__update-copyright.txt +14 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__harfbuzz-ng__COPYING.txt +36 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__harfbuzz__COPYING.txt +24 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__libpng__LICENSE.txt +112 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__md4__md4.cpp.txt +265 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__md4__md4.h.txt +31 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__md5__md5.cpp.txt +247 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__md5__md5.h.txt +48 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__pcre__COPYING.txt +5 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__pcre__LICENCE.txt +93 -0
- 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
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__rfc6234__sha-private.h.txt +28 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__rfc6234__sha.h.txt +357 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__rfc6234__sha224-256.c.txt +583 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__rfc6234__sha384-512.c.txt +1034 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__sha1__sha1.cpp.txt +261 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__sha3__KeccakF-1600-32-rvk.macros.txt +555 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__sha3__KeccakF-1600-32.macros.txt +26 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__sha3__KeccakF-1600-64.macros.txt +728 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__sha3__KeccakF-1600-int-set.h.txt +6 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__sha3__KeccakF-1600-interface.h.txt +46 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__sha3__KeccakF-1600-opt32-settings.h.txt +4 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__sha3__KeccakF-1600-opt32.c.txt +524 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__sha3__KeccakF-1600-opt64-settings.h.txt +7 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__sha3__KeccakF-1600-opt64.c.txt +512 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__sha3__KeccakF-1600-unrolling.macros.txt +124 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__sha3__KeccakNISTInterface.c.txt +82 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__sha3__KeccakNISTInterface.h.txt +70 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__sha3__KeccakSponge.c.txt +266 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__sha3__KeccakSponge.h.txt +80 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__sha3__brg_endian.h.txt +142 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtbase-src__3rdparty__xkbcommon__COPYING.txt +188 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtserialport-LICENSE.FDL.txt +450 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtserialport-LICENSE.GPL2.txt +339 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtserialport-LICENSE.GPL3-EXCEPT.txt +704 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtserialport-LICENSE.GPL3.txt +674 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtserialport-LICENSE.GPLv2.txt +292 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtserialport-LICENSE.GPLv3.txt +686 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtserialport-LICENSE.LGPL3.txt +165 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtserialport-LICENSE.LGPLv21.txt +514 -0
- canconfigpython-0.1.0a1.dist-info/Qt-5.7.1-qtserialport-LICENSE.LGPLv3.txt +175 -0
- canconfigpython-0.1.0a1.dist-info/RECORD +165 -0
- canconfigpython-0.1.0a1.dist-info/THIRD_PARTY_NOTICES.md +69 -0
- canconfigpython-0.1.0a1.dist-info/WHEEL +5 -0
- canconfigpython-0.1.0a1.dist-info/entry_points.txt +3 -0
- canconfigpython-0.1.0a1.dist-info/gcc-COPYING.RUNTIME.txt +73 -0
- canconfigpython-0.1.0a1.dist-info/gcc-COPYING3.LIB.txt +165 -0
- canconfigpython-0.1.0a1.dist-info/gcc-COPYING3.txt +674 -0
- canconfigpython-0.1.0a1.dist-info/linux-libc6-copyright.txt +529 -0
- canconfigpython-0.1.0a1.dist-info/linux-libcap2-copyright.txt +112 -0
- canconfigpython-0.1.0a1.dist-info/linux-libcom-err2-copyright.txt +403 -0
- canconfigpython-0.1.0a1.dist-info/linux-libdouble-conversion3-copyright.txt +41 -0
- canconfigpython-0.1.0a1.dist-info/linux-libgcc-s1-copyright.txt +1532 -0
- canconfigpython-0.1.0a1.dist-info/linux-libglib2.0-0t64-copyright.txt +1250 -0
- canconfigpython-0.1.0a1.dist-info/linux-libgssapi-krb5-2-copyright.txt +1374 -0
- canconfigpython-0.1.0a1.dist-info/linux-libicu74-copyright.txt +49 -0
- canconfigpython-0.1.0a1.dist-info/linux-libk5crypto3-copyright.txt +1374 -0
- canconfigpython-0.1.0a1.dist-info/linux-libkeyutils1-copyright.txt +52 -0
- canconfigpython-0.1.0a1.dist-info/linux-libkrb5-3-copyright.txt +1374 -0
- canconfigpython-0.1.0a1.dist-info/linux-libkrb5support0-copyright.txt +1374 -0
- canconfigpython-0.1.0a1.dist-info/linux-libpcre2-16-0-copyright.txt +122 -0
- canconfigpython-0.1.0a1.dist-info/linux-libpcre2-8-0-copyright.txt +122 -0
- canconfigpython-0.1.0a1.dist-info/linux-libqt5core5t64-copyright.txt +2932 -0
- canconfigpython-0.1.0a1.dist-info/linux-libqt5network5t64-copyright.txt +2932 -0
- canconfigpython-0.1.0a1.dist-info/linux-libqt5serialport5-copyright.txt +134 -0
- canconfigpython-0.1.0a1.dist-info/linux-libstdc++6-copyright.txt +1532 -0
- canconfigpython-0.1.0a1.dist-info/linux-libudev1-copyright.txt +287 -0
- canconfigpython-0.1.0a1.dist-info/linux-libzstd1-copyright.txt +123 -0
- canconfigpython-0.1.0a1.dist-info/linux-packages.json +102 -0
- canconfigpython-0.1.0a1.dist-info/linux-zlib1g-copyright.txt +84 -0
- canconfigpython-0.1.0a1.dist-info/mingw-w64-COPYING.MinGW-w64-runtime.txt.txt +240 -0
- canconfigpython-0.1.0a1.dist-info/mingw-w64-COPYING.MinGW-w64.txt.txt +135 -0
- canconfigpython-0.1.0a1.dist-info/mingw-w64-COPYING.txt +43 -0
- canconfigpython-0.1.0a1.dist-info/qt-source-notices.json +278 -0
- canconfigpython-0.1.0a1.dist-info/top_level.txt +1 -0
- 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(¤tHeader->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(¤tHeader->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
|
+
}
|