2
0
mirror of https://github.com/boostorg/process.git synced 2026-01-20 04:42:24 +00:00

Compare commits

..

174 Commits

Author SHA1 Message Date
Klemens Morgenstern
b2a96a3e13 Merge pull request #215 from klemens-morgenstern/develop
Develop merge
2020-06-28 23:25:58 +07:00
Klemens David Morgenstern
1dbb3626a9 Changed async system test to let the subprocess wait, to avoid a race condition 2020-06-28 22:06:07 +07:00
Klemens David Morgenstern
d79e1f2443 changed limit_fd test to use stdin 2020-06-28 16:29:07 +07:00
Klemens David Morgenstern
605dcd19d8 Removed faulty reinterpret cast from posix test 2020-06-28 15:09:58 +07:00
Klemens David Morgenstern
6b6a6fa61c Fixed limit_fd windows test the current broke if stdout and stderr were the same, as a CI system might do 2020-06-27 14:21:53 +07:00
Klemens David Morgenstern
295e2bdd9c Merge branch 'develop' of github.com:boostorg/process into develop 2020-06-27 12:55:04 +07:00
Klemens Morgenstern
dd1513846b Merge pull request #165 from chriskohlhoff/standard-executors-changes
Changes required to compile with new Asio executors model
2020-06-27 12:54:34 +07:00
Christopher Kohlhoff
7a94abfaf2 Use strand<> rather than legacy executor io_context::strand. 2020-06-24 19:52:52 +10:00
Christopher Kohlhoff
b55a09479c New name for polymorphic executor. Remove unused "old services" code. 2020-06-24 19:52:52 +10:00
Klemens David Morgenstern
5afb20760c Fixed exe-args style for windows 2020-05-21 18:48:04 +07:00
Klemens Morgenstern
eec87e1dd9 Update child_handle.hpp 2020-05-21 01:38:37 +07:00
Klemens Morgenstern
f250a33fb4 Switched to report.py script 2020-05-20 21:50:37 +07:00
Klemens David Morgenstern
570cf83a96 Merge branch 'develop' of github.com:boostorg/process into develop 2020-05-20 21:07:49 +07:00
Klemens David Morgenstern
d52d244f83 Closes boostorg/process#134 2020-05-20 21:00:50 +07:00
Klemens Morgenstern
04ab646f12 Merge pull request #136 from jonesmz/patch-10
Fix "comparison between signed and unsigned" warning
2020-05-20 20:51:31 +07:00
Klemens David Morgenstern
31c65b5442 Closes boostorg/process#127 2020-05-20 20:49:46 +07:00
Klemens Morgenstern
ebbb6d8b36 Merge pull request #126 from Martchus/fix-on_exit-example
Pass io_context in on_exit example
2020-05-20 20:44:42 +07:00
Klemens Morgenstern
b9c0140a26 Merge pull request #119 from licekto/double-closed-fd-fixed
Fix for double closed FD: https://github.com/boostorg/process/issues/105
2020-05-20 20:44:02 +07:00
Klemens Morgenstern
27f587a4be Merge pull request #137 from jonesmz/patch-11
Fix async_system_future.cpp:60:9: warning: variable ‘exit_code’ set but not used
2020-05-20 20:40:07 +07:00
Klemens Morgenstern
7f6061c956 Merge pull request #138 from jonesmz/patch-12
Fix "comparison between signed and unsigned" warning
2020-05-20 20:38:58 +07:00
Klemens David Morgenstern
2f32c95341 Closes boostorg/process#145 and boostorg/process#188 2020-05-20 20:33:55 +07:00
Klemens Morgenstern
a8029fc191 Merge pull request #140 from jonesmz/patch-14
Fix comparison between signed and unsigned integer expressions
2020-05-20 20:15:01 +07:00
Klemens Morgenstern
9f4bd9bce3 Merge pull request #139 from jonesmz/patch-13
Fix warning: unused variable ‘env’
2020-05-20 20:14:53 +07:00
Klemens Morgenstern
71f844c24f Merge pull request #149 from jonesmz/patch-22
file_in.hpp:29:17: warning: suggest braces around initialization of subobject
2020-05-20 20:09:30 +07:00
Klemens David Morgenstern
b0b6d67e6f Closes boostorg/process#144 2020-05-20 20:09:14 +07:00
Klemens David Morgenstern
3a2576a4d8 Merge branch 'develop' of github.com:boostorg/process into develop 2020-05-20 20:07:47 +07:00
Klemens Morgenstern
34eaa262dd Merge pull request #147 from jonesmz/patch-20
close_out.hpp:25:52: warning: suggest braces around initialization of subobject
2020-05-20 20:06:46 +07:00
Klemens Morgenstern
ce3b3d8f99 Merge pull request #148 from jonesmz/patch-21
null_in.hpp:28:17: warning: suggest braces around initialization of s…
2020-05-20 20:06:35 +07:00
Klemens Morgenstern
429f2ba95c Merge pull request #150 from jonesmz/patch-23
pipe_in.hpp:30:17: warning: suggest braces around initialization of subobject
2020-05-20 20:05:29 +07:00
Klemens Morgenstern
0c2e7387c8 Merge pull request #152 from Naios/msvcfix
[Build Fix] Workaround for a MSVC 14.1 and 14.2 compiler bug
2020-05-20 20:05:22 +07:00
Klemens Morgenstern
2a2ea4b92d Merge pull request #146 from jonesmz/patch-19
Fix example/intro.cpp includes
2020-05-20 20:01:47 +07:00
Klemens David Morgenstern
873ab2558d Closes boostorg/process#153 2020-05-20 19:58:23 +07:00
Klemens David Morgenstern
03571d4eaf Merge branch 'develop' of github.com:boostorg/process into develop 2020-05-20 19:44:42 +07:00
Klemens Morgenstern
46ab3ba9b8 Merge pull request #158 from acronce/develop
Add the lambda declarator needed to disambiguate the multiple return …
2020-05-20 19:39:24 +07:00
Allen Cronce
3aba1f6eb1 Add the lambda declarator needed to disambiguate the multiple return values from these lambda functions as a bool. Before this change the return of a "false" was interpreted as an int, but the second return is a bool. This caused the following compile error when accessing environment variables under Xcode 11.4.1: Return type 'bool' must match previous return type 'int' when lambda expression has unspecified explicit return type 2020-05-13 07:25:36 -07:00
Denis Blank
44771769fa Workaround for a MSVC 14.1 and 14.2 compiler bug
* boost\include\boost/process/child.hpp(35): error C2600:
    'boost::process::child::child': cannot define a compiler-generated
    special member function (must be declared in the class first)
* Introduced by 8541cae
* See boostorg/process#113
2020-03-23 22:35:59 +01:00
Michael Jones
8704416941 pipe_in.hpp:30:17: warning: suggest braces around initialization of subobject 2020-03-13 02:57:56 -05:00
Michael Jones
f48392399f file_in.hpp:29:17: warning: suggest braces around initialization of subobject 2020-03-13 02:57:11 -05:00
Michael Jones
80f81117aa null_in.hpp:28:17: warning: suggest braces around initialization of subobject 2020-03-13 02:56:28 -05:00
Michael Jones
9cff55215d close_out.hpp:25:52: warning: suggest braces around initialization of subobject 2020-03-13 02:55:35 -05:00
Michael Jones
2e4b3c2406 Fix example/intro.cpp includes 2020-03-13 02:26:10 -05:00
Michael Jones
b510b6a9d9 Fix comparison between signed and unsigned integer expressions 2020-03-13 01:22:01 -05:00
Michael Jones
046b96186f Fix warning: unused variable ‘env’ 2020-03-13 01:20:10 -05:00
Michael Jones
1df2e67bc4 Fix "comparison between signed and unsigned" warning 2020-03-13 01:18:11 -05:00
Michael Jones
6cf69e2797 Fix async_system_future.cpp:60:9: warning: variable ‘exit_code’ set but not used 2020-03-13 01:11:46 -05:00
Michael Jones
d3e4cbf3b3 Fix "comparison between signed and unsigned" warning 2020-03-13 01:03:31 -05:00
Martchus
e67e49c891 Pass io_context in on_exit example
The description already states that an io_context needs
to be passed. This change is adjusting the example accordingly.
2020-03-08 21:17:54 +01:00
Licek Tomáš
29a43b17e4 Fix for double closed FD: https://github.com/boostorg/process/issues/105 2020-02-04 10:22:11 +01:00
Klemens David Morgenstern
6d10c3a807 Merge branch 'develop' of github.com:klemens-morgenstern/boost-process into develop 2020-01-26 15:18:00 +07:00
Klemens Morgenstern
668579ed6f Merge pull request #110 from sorayuki/patch-1
Fix memory over-write in pipe.hpp
2020-01-26 15:17:27 +07:00
Klemens Morgenstern
8af828cd43 Merge pull request #73 from fkonvick/develop
Fix double quotes processing on Windows
2020-01-26 15:16:39 +07:00
Klemens Morgenstern
590cc10b42 Merge pull request #75 from brunom/patch-1
typo
2020-01-26 15:15:07 +07:00
Klemens Morgenstern
3bc11ce3ac Merge pull request #113 from jonesmz/patch-1
Use = default for default constructor
2020-01-26 15:14:52 +07:00
Klemens Morgenstern
603441ecc3 Merge pull request #114 from jonesmz/patch-2
Use static constexpr instead of const for compile-time constant variable
2020-01-26 15:14:32 +07:00
Klemens Morgenstern
612a953369 Merge pull request #115 from jonesmz/patch-3
Use move construction for struct start_dir
2020-01-26 15:13:15 +07:00
Klemens Morgenstern
1554773d39 Merge pull request #213 from dermojo/develop
Documentation fixes
2020-01-26 15:10:44 +07:00
Daniel Evers
dd003bf2b0 Correct some typos etc. 2020-01-05 17:33:59 +01:00
Klemens Morgenstern
0341e08297 Merge pull request #212 from klemens-morgenstern/develop
Master update
2019-12-29 04:37:11 +08:00
Klemens David Morgenstern
5853345715 trying to fix appveyor to upload to report.ci 2019-12-29 04:16:44 +08:00
Klemens David Morgenstern
41f8b1cf00 removed sigtimedwait for group_wait 2019-12-29 03:39:32 +08:00
Klemens David Morgenstern
56ae00c7a4 typo fix 2019-12-29 00:48:51 +08:00
Klemens David Morgenstern
f58882c956 small travis fixes, hopefully fixing the build 2019-12-29 00:42:21 +08:00
Klemens David Morgenstern
3f14ebc755 changed report script 2019-12-29 00:04:26 +08:00
Michael Jones
1502de1001 Use move construction for struct start_dir 2019-12-05 17:48:17 -06:00
Michael Jones
8541cae396 Update child_decl.hpp 2019-12-05 12:59:09 -06:00
Michael Jones
38fa1fd040 Update cmd.hpp 2019-12-05 12:54:35 -06:00
Michael Jones
ba15f760ab Use static constexpr instead of const for compile-time constant variable 2019-12-05 12:53:06 -06:00
Michael Jones
5e3e8f977e Update error.hpp 2019-12-05 12:49:56 -06:00
Michael Jones
6182876d4f Update io.hpp 2019-12-05 12:44:14 -06:00
Michael Jones
5bfd2ee08c Use = default for default constructor 2019-12-05 12:41:31 -06:00
sorayuki
c91b227c47 Fix memory over-write in pipe.hpp 2019-11-25 15:31:48 +08:00
Klemens David Morgenstern
6a4d2ff721 added typedef executor_type; 2019-11-22 14:03:22 +08:00
Klemens David Morgenstern
6bf37ea8e8 Merge branch 'develop' of github.com:boostorg/process into develop
# Conflicts:
#	test/async_pipe.cpp
2019-11-22 13:11:17 +08:00
Klemens Morgenstern
ad38cdfada Merge pull request #106 from treesoft-de/bugfix
fix double CloseHandle after move of file_descriptor on windows
2019-11-22 13:08:17 +08:00
Klemens Morgenstern
167ee79fa9 Merge pull request #102 from res2k/develop
Miscellaneous fixes related to POSIX environments
2019-11-22 13:07:52 +08:00
Klemens David Morgenstern
6b83d0b9dd closes boostorg/process#97 2019-11-22 13:07:08 +08:00
Klemens Morgenstern
16d16d40be Merge pull request #90 from BikingGlobetrotter/develop
fixed hidden local variable
2019-11-22 13:06:12 +08:00
Klemens David Morgenstern
f5f0866745 closes boostorg/process#103 2019-11-22 13:04:59 +08:00
Klemens Morgenstern
408cff1997 Merge pull request #70 from thomassuckow/patch-1
Guard against EINTR in basic_pipe
2019-11-22 13:01:52 +08:00
Klemens David Morgenstern
09faec4732 closes boostorg/process#104 2019-11-22 12:59:05 +08:00
Klemens Morgenstern
5ab43529b7 Merge pull request #209 from killerbot242/changes3
only carry out when buffer size is > 0
2019-11-22 12:52:41 +08:00
Klemens Morgenstern
97f5b3c049 Merge pull request #211 from Instand/patch
Fix basic_ipstream is_open compile error.
2019-11-22 12:51:58 +08:00
Klemens David Morgenstern
6dd3e0bdb4 typo fix, closes klemens-morgenstern/boost-process#210 2019-11-22 12:50:40 +08:00
Arew
eba5cb7be2 Fix basic_ipstream is_open compile error. 2019-11-21 17:57:24 +03:00
Klemens David Morgenstern
f4c51bcd5a closes klemens-morgenstern/boost-process#107 2019-10-26 11:04:42 +07:00
Raymond Häb
410c0d592e fix double CloseHandle after move of file_descriptor on windows 2019-10-18 10:08:21 +02:00
Frank Richter
40df7899b2 test: Add test for direct conversion of wnative_environment to native_environment 2019-09-11 11:31:19 +02:00
Frank Richter
51083a8fa8 posix environment: Fix assignment of wnative_environment to wenvironment
Using 'environ' as the native_handle for a wide environment doesn't work,
as environ returns char**. Instead, use data we already converted.
2019-09-11 11:23:36 +02:00
Frank Richter
fe3cb0efc7 posix environment: Avoid using front() on empty string
That is undefined behaviour. Pushing a single NUL makes the string
non-empty but still gives us "".
2019-09-11 11:20:24 +02:00
Frank Richter
e0dd3b9658 test: Build fix for async_pipe 2019-09-11 11:01:45 +02:00
Lieven de Cock
d7d84f3952 only carry out when buffer size is > 0 2019-08-19 12:11:34 +02:00
Klemens Morgenstern
6ccce9104a Merge pull request #91 from orgads/patch-1
Fix GCC9 warning
2019-06-26 20:30:18 +10:00
Orgad Shaneh
984c0c5b71 Fix GCC9 warning
warning: moving a local object in a return statement prevents copy elision
2019-06-26 13:11:49 +03:00
michael
43523fcf8b fixed hidden local variable 2019-06-21 13:39:54 +02:00
Klemens Morgenstern
cf7ad36438 Merge pull request #87 from OBorce/feature/fix-stream-move-assignments
fix move assignment operators for basic_[io]pstream
2019-06-06 11:30:33 +07:00
boris
ca994c1972 fix move assignment operators for basic_[io]pstream 2019-06-05 00:19:53 +02:00
Klemens David Morgenstern
0a554c92b5 Merge branch 'develop' of github.com:boostorg/process into develop 2019-05-18 11:11:20 +07:00
Klemens Morgenstern
fa81cecffc Merge pull request #196 from klemens-morgenstern/limit_fd
Limit fd
2019-05-18 11:07:55 +07:00
Klemens David Morgenstern
977b76f6aa Merge branch 'develop' of github.com:klemens-morgenstern/boost-process into develop 2019-05-15 12:31:16 +07:00
Klemens Morgenstern
fc1acb82d9 Merge pull request #199 from klemens-morgenstern/stream_close
added opstream::close, closes klemens-morgenstern/boost-process#198
2019-05-14 22:04:21 +07:00
Klemens David Morgenstern
db7af9f87d doc addition 2019-05-14 13:22:05 +07:00
Klemens Morgenstern
a350cc346b Merge pull request #203 from klemens-morgenstern/win_pipe_unlimited_instances
Closes boostorg/process#84 and boostorg/process#83
2019-05-14 12:41:29 +07:00
Klemens David Morgenstern
9fa86d3d65 removed call to 'close_sink' 2019-05-14 12:15:57 +07:00
Klemens David Morgenstern
e426f2bfac remove ill-formed test & minor fixes 2019-05-14 11:57:20 +07:00
Klemens David Morgenstern
6b173117aa Merge branch 'develop' into stream_close 2019-05-14 10:45:53 +07:00
Klemens David Morgenstern
ecbc93408f Merge branch 'develop' into win_pipe_unlimited_instances 2019-05-14 10:45:16 +07:00
Klemens David Morgenstern
6ba9a48d15 Merge branch 'develop' into new_wait_for_test 2019-05-14 10:44:40 +07:00
Klemens David Morgenstern
519c0a636a Merge branch 'develop' into limit_fd 2019-05-14 10:44:08 +07:00
Klemens David Morgenstern
82195c61af Merge branch 'develop' of github.com:klemens-morgenstern/boost-process into develop
# Conflicts:
#	include/boost/process/detail/posix/wait_group.hpp
2019-05-14 00:41:41 +07:00
Klemens David Morgenstern
c604e3a20e switched to a wait/sleep version for OSX - should work fine for groups 2019-05-14 00:36:24 +07:00
Klemens Morgenstern
b27d0170ba Typo fix 2019-05-13 23:50:31 +07:00
Klemens David Morgenstern
98fd4eecf0 small reordering of code 2019-05-13 20:43:03 +07:00
Klemens David Morgenstern
3799315ce7 reverted to pipes for OSX 2019-05-13 20:11:17 +07:00
Klemens David Morgenstern
b9431ba492 fixing signal workaround 2019-05-13 10:30:28 +07:00
Klemens David Morgenstern
c0dca35615 added wrongfully removed build script options 2019-05-13 00:28:10 +07:00
Klemens David Morgenstern
790d79db9c Merge branch 'develop' into limit_fd 2019-05-13 00:23:52 +07:00
Klemens David Morgenstern
5de0a795d1 fixed wait_for_exit 2019-05-13 00:22:46 +07:00
Klemens David Morgenstern
cbaa913e3d Merge branch 'develop' into limit_fd
# Conflicts:
#	include/boost/process/detail/posix/pipe_out.hpp
#	test/Jamfile.jam
2019-05-12 17:43:40 +07:00
Klemens David Morgenstern
5786162fb5 Merge branch 'develop' into new_wait_for_test 2019-05-12 17:42:08 +07:00
Klemens David Morgenstern
78c44dd560 Merge branch 'develop' into win_pipe_unlimited_instances 2019-05-12 17:40:54 +07:00
Klemens David Morgenstern
23ff67d83d added space for pipe close test 2019-05-12 17:37:35 +07:00
Klemens David Morgenstern
476c6ccd95 Merge branch 'develop' into stream_close 2019-05-12 17:13:36 +07:00
Klemens David Morgenstern
28126b3432 osx fix 2019-05-12 17:02:25 +07:00
Klemens David Morgenstern
ed8388d091 actual fix 2019-05-12 16:55:45 +07:00
Klemens David Morgenstern
43c402a5da test logic fix 2019-05-12 16:53:54 +07:00
Klemens David Morgenstern
9ff2f6f3ef still trying for circleci 2019-05-10 16:56:11 +07:00
Klemens David Morgenstern
b2a0fadaca Merge branch 'develop' into new_wait_for_test
# Conflicts:
#	.travis.yml
2019-05-10 16:38:23 +07:00
Klemens David Morgenstern
3c4057204e hopefully fixed wait_for for osx 2019-05-10 16:34:51 +07:00
Klemens David Morgenstern
717ac47510 further investigation of circlci error 2019-05-10 12:21:37 +07:00
Klemens David Morgenstern
4f6f4eb391 updated for circle 2019-05-10 12:00:43 +07:00
Klemens David Morgenstern
99e04036c7 removed report-ci from circle-ci 2019-05-10 00:37:00 +07:00
Klemens David Morgenstern
519e339365 added more checks 2019-05-09 23:05:09 +07:00
Klemens David Morgenstern
d2c930470f removed && false 2019-05-09 17:08:58 +07:00
Klemens David Morgenstern
d5709ae747 trying to fix wait_for 2019-05-09 16:35:58 +07:00
Klemens David Morgenstern
d2ab81b1b9 Merge branch 'develop' into new_wait_for_test
# Conflicts:
#	include/boost/process/detail/posix/wait_for_exit.hpp
#	include/boost/process/detail/posix/wait_group.hpp
2019-05-09 12:20:20 +07:00
Klemens David Morgenstern
885557fe01 still trying to make wait_for work 2019-05-09 12:19:26 +07:00
Klemens David Morgenstern
b5b758f89a Merge branch 'develop' into stream_close 2019-05-09 11:29:31 +07:00
Klemens David Morgenstern
2a954eb809 Merge branch 'develop' into win_pipe_unlimited_instances 2019-05-09 11:24:14 +07:00
Klemens David Morgenstern
0edff5449a Merge branch 'develop' into new_wait_for_test
# Conflicts:
#	include/boost/process/detail/posix/wait_for_exit.hpp
2019-05-08 16:53:45 +07:00
Klemens David Morgenstern
417ea77f2f changed SIGTERM to SIGKILL 2019-05-08 16:50:49 +07:00
Klemens David Morgenstern
9e3fdc9669 SIGSEV fix 2019-05-08 16:44:38 +07:00
Klemens David Morgenstern
66c2867371 removed travis token 2019-05-08 16:30:39 +07:00
Klemens David Morgenstern
caa7b2fcc8 two minor fixes 2019-05-08 16:28:54 +07:00
Klemens David Morgenstern
6263e74bcd Merge branch 'develop' into limit_fd 2019-05-07 12:16:53 +07:00
Klemens David Morgenstern
faae08ee64 replaced 1 with ::boost::winapi::HANDLE_FLAG_INHERIT_ 2019-05-07 12:16:32 +07:00
Klemens David Morgenstern
2314e19f12 restructured osx wait
Check if it solves klemens-morgenstern/boost-process#200
2019-05-07 12:01:04 +07:00
Klemens David Morgenstern
cfd0fc055c fixed overflow func 2019-05-07 11:48:16 +07:00
Klemens David Morgenstern
849b5d0f30 Closes boostorg/process#84 and boostorg/process#83 2019-05-07 10:59:50 +07:00
Klemens Morgenstern
d13df2a194 Merge pull request #82 from Parean/develop
should close klemens-morgenstern/boost-process#201
2019-05-03 21:07:53 +07:00
Denis Smirnov
86fc3b0b4d should close klemens-morgenstern/boost-process#201 2019-05-03 19:07:17 +07:00
Klemens David Morgenstern
4733ca719f typo fix 2019-04-25 22:40:13 +07:00
Klemens David Morgenstern
296f12eb64 RAII issues & resetting flags on windwos handles 2019-04-25 22:36:30 +07:00
Klemens David Morgenstern
96d3470e37 added this-> before clear() 2019-04-25 22:06:23 +07:00
Klemens David Morgenstern
2265c98d81 added missing ; 2019-04-25 20:24:42 +07:00
Klemens David Morgenstern
060e5c2526 fixed name lookup 2019-04-25 15:15:29 +07:00
Klemens David Morgenstern
b4894807f1 added opstream::close, closes klemens-morgenstern/boost-process#198 2019-04-24 18:24:44 +07:00
Klemens David Morgenstern
2aa5e1461c Merge branch 'develop' into new_wait_for_test
# Conflicts:
#	test/Jamfile.jam
2019-04-20 16:41:31 +08:00
Klemens Morgenstern
61fa15fa48 Merge pull request #197 from toonetown/fix-wait_for-fork-macOS
Use SIGTERM instead of `-15`.
2019-04-20 15:39:54 +07:00
Nathan Toone
92508e06a1 Use SIGTERM instead of -15. 2019-04-19 23:44:54 -06:00
Klemens David Morgenstern
5e90c8de9b fixed up tests 2019-04-10 10:09:32 +08:00
Klemens David Morgenstern
a486a25a07 appveyor minor fix, pipe_out naming fix & limit_fd small sanity check because of weird osx bug 2019-04-10 01:33:21 +08:00
Klemens David Morgenstern
f8c0dd4da5 prototype for limit_fd 2019-04-09 23:39:43 +08:00
Klemens David Morgenstern
ee6870bfbc added used_handles & limit_fd for windows 2019-04-09 19:16:55 +08:00
Klemens David Morgenstern
0422b6bfb8 added posix get_handle function 2019-04-09 15:40:26 +08:00
Klemens David Morgenstern
7fc41b2815 added windows handles functionality 2019-04-09 14:31:50 +08:00
Klemens David Morgenstern
a49f1f6e2d Started on limit-fd functionality 2019-04-08 11:11:55 +08:00
Klemens David Morgenstern
8d48d9cbfa dumb workaround -> fix report.ci detection! 2019-03-21 16:21:40 +08:00
Klemens David Morgenstern
f25c31c847 added test by @grives as recommended in #69 2019-03-21 12:51:32 +08:00
Bruno Martinez
192191ecfb typo 2019-03-05 12:30:06 -03:00
Klemens David Morgenstern
3eacae5e38 Merge branch 'develop' 2019-03-04 12:16:07 +08:00
Klemens Morgenstern
881da4f9e2 Merge pull request #191 from klemens-morgenstern/develop
Update master
2019-02-28 12:44:50 +08:00
Filip Konvička
3aeb6601cd Merge pull request #1 from fkonvick/windows-shell-dquotes
Fix double quotes processing on Windows
2019-02-27 14:05:59 +01:00
Filip Konvička
8a7c37617e Fix double quotes processing on Windows
Fix double quotes processing on Windows:
- quotes should only be added when not already present
- quotes need to be used when an argument includes spaces or double quotes
- quotes inside an argument should be doubled (that's the way Windows does escaping)
2019-02-27 14:02:45 +01:00
Thomas Suckow
1b6ccb6d39 Guard against EINTR in basic_pipe
Based on the code from executor.hpp, a signal can cause invoking a child process to fail because reading/writing the pipe was interrupted.
2019-01-22 09:22:52 -08:00
79 changed files with 1796 additions and 423 deletions

View File

@@ -70,7 +70,6 @@ jobs:
python <(curl -s https://report.ci/annotate.py) --tool gcc --input test.log
python <(curl -s https://report.ci/annotate.py) --tool gcc --input no-valgrind.log
python <(curl -s https://report.ci/upload.py) --name "Circle CI Gcc Tests" --framework boost
bash <(curl -s https://codecov.io/bash) -x gcov || true
bash <(curl -s https://codecov.io/bash) -x gcov > /dev/null || true
echo "BUILD_RESULT: $FAILED"
exit $FAILED

View File

@@ -23,8 +23,6 @@ os:
- linux
- osx
secure: "vs7qgXb0lQg8CTyDPSi3RQtOIOtssaCkBIx86UoEvTXwJCTOLPe7ZufQ0lobn0OVWo261AMx9GbumBBzqfsvJc1G6ixGBVwymiGli/R8DZDvvg9UdljsEk65s/XbujE/9qh97zKGGioFyCn1Bmf5+SdDAxsuXTZm/cBny5VxYaaCR7s2cFUmp4up/djqg1GI7uwBh3ceodT3OL1X3dlMV59gOJWWNsB+RO9b9DPhTW7nOlMNRiEFik4rweecQB0JS8LaHDjYwzIRrGYHX+lR9cE/O8GCCHcUOmq9jCozDdxx+HZRu4rb1ST1RiDbvYaoeTif0Df1fVXHWOoO2D4NlXB6tJPXw2mkop00j6zkcydUJYid6T1lwfEpXAhd5A9FvOIXO5hoju1wlqfkU2eFQ9Na8z8bCIX2niZmveZWp4Ag52gEPzJMFx9hHGT8J4FWMvkqTWezux1sPZrjZjc0kXdJrIp84D9MsBc1sKrxOAOb5ekSfIK5n4JDkgUtuwMSTvEdWqNJXFPZq1rEu4GTwX99z3/XF+pM5XaCDQtZ/zUA5SPHhy0dKLH/BvceUqLJt53+lMcpsltJDB+XxQ/CFL7IdgR91OKGus/z4dbVWiSdkoNvcuZqjQLFLOMVNxoqC6PRvDAEhpy21j/5GUPvM5baQS7IEin0NF7bOTtXJdY="
env:
matrix:
- BADGE=linux
@@ -73,7 +71,7 @@ before_install:
# Set this to the name of the library
- PROJECT_TO_TEST=`basename $TRAVIS_BUILD_DIR`
- echo "Testing $PROJECT_TO_TEST"
- if [ $TRAVIS_OS_NAME = "osx" ]; then brew install gcc5; brew install valgrind; brew install llvm; TOOLSET=clang; BOOST_TEST_CATCH_SYSTEM_ERRORS=no; MULTITHREAD=-j8; else TOOLSET=gcc-5; USE_VALGRIND="testing.launcher=valgrind valgrind=on"; fi
- if [ $TRAVIS_OS_NAME = "osx" ]; then brew install gcc5; brew install valgrind; brew install llvm; TOOLSET=clang; BOOST_TEST_CATCH_SYSTEM_ERRORS=no; MULTITHREAD=-j8; else TOOLSET=gcc-5; REPORT_CI=--boost-process-report-ci USE_VALGRIND="testing.launcher=valgrind valgrind=on"; fi
# Cloning Boost libraries (fast nondeep cloning)
- BOOST=$HOME/boost-local
- git init $BOOST
@@ -87,7 +85,7 @@ before_install:
- git checkout $BOOST_BRANCH
- git submodule update --init --merge
- git remote set-branches --add origin $BOOST_BRANCH
- git pull --recurse-submodules
- git pull --recurse-submodules || true
- git submodule update --init
- git checkout $BOOST_BRANCH
- git submodule foreach "git reset --quiet --hard; git clean -fxd"
@@ -103,8 +101,8 @@ before_install:
- echo BOOST_TEST_CATCH_SYSTEM_ERRORS $BOOST_TEST_CATCH_SYSTEM_ERRORS
script:
# `--coverage` flags required to generate coverage info for Coveralls
- ../../../b2 $MULTITHREAD with-valgrind address-model=64 architecture=x86 $USE_VALGRIND toolset=$TOOLSET cxxflags="--coverage -DBOOST_TRAVISCI_BUILD -std=$CXX_STANDARD" linkflags="--coverage" -sBOOST_BUILD_PATH=.
- ../../../b2 $MULTITHREAD without-valgrind address-model=64 architecture=x86 toolset=$TOOLSET cxxflags="--coverage -DBOOST_TRAVISCI_BUILD -std=$CXX_STANDARD" linkflags="--coverage" -sBOOST_BUILD_PATH=.
- ../../../b2 $MULTITHREAD with-valgrind address-model=64 architecture=x86 $USE_VALGRIND toolset=$TOOLSET cxxflags="--coverage -DBOOST_TRAVISCI_BUILD -std=$CXX_STANDARD" linkflags="--coverage" -sBOOST_BUILD_PATH=. $REPORT_CI
- ../../../b2 $MULTITHREAD without-valgrind address-model=64 architecture=x86 toolset=$TOOLSET cxxflags="--coverage -DBOOST_TRAVISCI_BUILD -std=$CXX_STANDARD" linkflags="--coverage" -sBOOST_BUILD_PATH=. $REPORT_CI
after_success:
# Copying Coveralls data to a separate folder
- mkdir -p $TRAVIS_BUILD_DIR/coverals
@@ -133,6 +131,6 @@ after_success:
- coveralls-lcov coverals/coverage.info
after_script:
- curl -s https://report.ci/upload.py | python - --token=$REPORT_CI_TOKEN --name="$BADGE test run"
- bash <(curl -s https://codecov.io/bash)
- cd $BOOST/libs/$PROJECT_TO_TEST/test
- curl -s https://report.ci/report.py | python - --name="$BADGE test run"

View File

@@ -16,11 +16,11 @@ In that it is different than other facilities (like sockets) and provides anothe
Pipes are typically used for interprocess communication. The main reason is, that pipes can be directly assigned to the process stdio, i.e. stderr, stdin and stdout.
Additionally, half of the pipe can be inherited to the child process and closed in the father process. This will cause the pipe to be broken when the child process exits.
Though please not, that if the the same thread reads and write to a pipe, it will only talk to itself.
Though please note, that if the same thread reads and writes to a pipe, it will only talk to itself.
[section:anonymous Anonymous Pipes]
The usual type of pipes, are the anonymous ones. Since the have no name,
The most common pipes are anonymous. Since they have no name,
a handle to them can only be obtained from duplicating either handle.
In this library the following functions are used for the creation of unnamed pipes:
@@ -57,12 +57,12 @@ Every process is identified by a unique number[footnote it is unique as long as
[section:exit_code Exit code]
A process will return an integer value indicating whether it was successful. On posix
there are more codes associated with that, but not so on windows. Therefore there is not such encoding currently in the library.
there are more codes associated with that, but not so on windows. Therefore there is no such encoding currently in the library.
However an exit code of zero means the process was successful, while one different than zero indicates an error.
[endsect]
[section:termination Termination]
Processes can also be forced to exit. There are two ways to do this, signal the process to so and wait, and just terminate the process without conditions.
Processes can also be forced to exit. There are two ways to do this, signal the process to do so and wait, and just terminate the process without conditions.
Usually the first approach is to signal an exit request, but windows - unlike posix - does not provide a consistent way to do this. Hence this is not part of the
library and only the hard terminate is.
@@ -79,4 +79,4 @@ The environment is a map of variables local to every process. The most significa
[endsect]
[endsect]
[endsect]

View File

@@ -1,12 +1,12 @@
[section:design Design Rationale]
[section Scope]
This library is meant to give an wrapper around the different OS-specific methods
This library is meant to give a wrapper around the different OS-specific methods
to launch processes. Its aim is to provide all functionality that is available on
those systems and allow the user to do all related things, which require using the OS APIs.
[*This library does not try to provide a full library for everything process related]
[*This library does not try to provide a full library for everything process related.]
In many discussions the proposal was made to build boost.process into a DSEL [footnote Domain Specific Embedded Language] of some sort.
This is not the goal, it rather provides the facilities to build such a DSEL-Library on top of it.
This is not the goal, it rather provides the facilities to build such a DSEL-library on top of it.
Therefore the library also does [*not] force any particular use (such as only asynchronous communication) on its user.
It rather could be integrated with such a library.
@@ -33,7 +33,7 @@ Both styles can also be mixed in some cases.
system("gcc", "-c", args+={"main.cpp"});
```
In the following section the avaible styles will be described. Note that the
In the following section the available styles will be described. Note that the
overload style is implemented via type traits, so the types will be listed.
[caution There is no guarantee in which order the arguments will be applied!
@@ -54,7 +54,7 @@ interpret each string as an argument.
]
When using the overloading variant, a single string will result in a cmd interpretation,
several strings will yield a exe-args interpretation. Both version can be set explicitly:
several strings will yield a exe-args interpretation. Both versions can be set explicitly:
```
system("grep -c false /etc/passwd"); //cmd style
@@ -65,7 +65,7 @@ system(exe="grep", args={"-c", "false", "/etc/passwd"}); //exe-/args-
```
[note If a '"' sign is used in the argument style, it will be passed as part of the argument.
If the same effect it wanted with the cmd syntax, it ought to be escaped, i.e. '\\\"'. ]
If the same effect is wanted with the cmd syntax, it ought to be escaped, i.e. '\\\"'. ]
[note The `PATH` variable will automatically be searched in the command style,
but the one of the launching process, not the one passed to the child process.]
[endsect]

View File

@@ -15,14 +15,14 @@ To extend the library, the header [headerref boost/process/extend.hpp extend] is
It only provides the explicit style for custom properties, but no implicit style.
What this means is, that a custom initializer can be implemented, a reference to which can be passed to one of the launching functions.
If a class inherits [classref boost::process::extend::handler] it will be regarded as a initializer and thus directly put into the sequence
What this means is, that a custom initializer can be implemented, a reference which can be passed to one of the launching functions.
If a class inherits [classref boost::process::extend::handler] it will be regarded as an initializer and thus directly put into the sequence
the executor gets passed.
[section:structure Structure]
The executor calls different handlers of the initializers during the process launch.
The basic structure is consists of three functions, as given below:
The basic structure consists of three functions, as given below:
* [globalref boost::process::extend::on_setup on_setup]
* [globalref boost::process::extend::on_error on_error]
@@ -53,7 +53,7 @@ namespace ex = bp::extend;
__child__ c("foo", __on_success__=[](auto & exec) {std::cout << "hello world" << std::endl;});
```
Considering that lambda can also capture values, data can easily be shared between handlers.
Considering that lambdas can also capture values, data can easily be shared between handlers.
To see which members the executor has, refer to [classref boost::process::extend::windows_executor windows_executor]
and [classref boost::process::extend::posix_executor posix_executor].
@@ -86,7 +86,7 @@ __child__ c("foo", hello_world());
[note The implementation is done via overloading, not overriding.]
Every handler not implemented dafaults to [classref boost::process::extend::handler handler], where an empty handler is defined for each event.
Every handler not implemented defaults to [classref boost::process::extend::handler handler], where an empty handler is defined for each event.
[endsect]
@@ -100,7 +100,7 @@ this functionality is also available for extensions. If the class needs the __io
```
struct async_foo : __handler__, __require_io_context__
{
tempalte<typename Executor>
template<typename Executor>
void on_setup(Executor & exec)
{
__io_context__ & ios = __get_io_context__(exec.seq); //gives us a reference and a compiler error if not present.
@@ -139,10 +139,10 @@ struct async_bar : __handler, __async_handler__
[section:error Error handling]
If an error occurs in the initializers it shall be told to the executor and not handles directly. This is because
the behaviour can be changed through arguments passed to the launching function. Hence the the executor
If an error occurs in the initializers it shall be told to the executor and not handled directly. This is because
the behaviour can be changed through arguments passed to the launching function. Hence the executor
has the function `set_error`, which takes an [@http://en.cppreference.com/w/cpp/error/error_code std::error_code] and a string.
Depending on the cofiguration of the executor, this may either throw, set an internal `error_code`, or do nothing.
Depending on the configuration of the executor, this may either throw, set an internal `error_code`, or do nothing.
So let's take a simple example, where we set a randomly chosen `error_code`.
@@ -202,8 +202,8 @@ struct hello_exe : __handler__
};
```
So given our example, the definitions with the non-native exectur are still a template so that they will not be evaluated if not used. Hence this provides a
way to implement systems-specific code without using the preprocessor.
So given our example, the definitions with the non-native executor are still a template so that they will not be evaluated if not used. Hence this provides a
way to implement system-specific code without using the preprocessor.
[note If you only write a partial implementation, e.g. only for __posix_executor__, the other variants will default to __handler__].

View File

@@ -47,7 +47,7 @@ while (is >> file)
This will also deadlock, because the pipe does not close when the subprocess exits.
So the `ipstream` will still look for data even though the process has ended.
[note It is not possible to use automatically pipe-closing in this library, because
[note It is not possible to use automatic pipe-closing in this library, because
a pipe might be a file-handle (as for async pipes on windows).]
But, since pipes are buffered, you might get incomplete data if you do this:
@@ -64,7 +64,7 @@ while (c.running())
}
```
It is therefore highly recommended that you use the asynchronous api if you are
It is therefore highly recommended that you use the asynchronous API if you are
not absolutely sure how the output will look.
[endsect]

View File

@@ -68,12 +68,12 @@ namespace bp = boost::process; //we will assume this for all further examples
int result = bp::system("g++ main.cpp");
```
If a single string (or the explicit form bp::cmd), it will be interpreted as a command line.
If a single string is given (or the explicit form bp::cmd), it will be interpreted as a command line.
That will cause the execution function to search the `PATH` variable to find the executable.
The alternative is the `exe-args` style, where the first string will be interpreted as a filename (including the path),
and the rest as arguments passed to said function.
[note For more details on the `cmd`/`exe-args` style look [link boost_process.design.arg_cmd_style here]]
[note For more details on the `cmd`/`exe-args` style look [link boost_process.design.arg_cmd_style here].]
So as a first step, we'll use the `exe-args` style.
@@ -141,7 +141,7 @@ things while the process is running and afterwards get the exit code. The call
to child_wait is necessary, to obtain it and tell the operating system, that no
one is waiting for the process anymore.
[note You can also wait for a time span or a until a time point with __wait_for__ and __wait_until__]
[note You can also wait for a time span or until a time point with __wait_for__ and __wait_until__.]
[warning If you don't call wait on a child object, it will be terminated on destruction.
This can be avoided by calling __detach__ beforehand]
@@ -217,8 +217,7 @@ std::vector<std::string> read_outline(std::string & file)
What this does is redirect the `stdout` of the process into a pipe and we read this
synchronously.
[warning The pipe will cause a deadlock if you try to read after nm exited]
[note You can do the same thing with [globalref boost::process::std_err std_err]]
[note You can do the same thing with [globalref boost::process::std_err std_err].]
Now we get the name from `nm` and we might want to demangle it, so we use input and output.
`nm` has a demangle option, but for the sake of the example, we'll use
@@ -247,7 +246,7 @@ std::vector<std::string> read_demangled_outline(const std::string & file)
std::vector<std::string> outline;
//we just use the same pipe, so the
//we just use the same pipe, so the output of nm is directly passed as input to c++filt
bp::child nm(bp::search_path("nm"), file, bp::std_out > p);
bp::child filt(bp::search_path("c++filt"), bp::std_in < p, bp::std_out > is);
@@ -271,7 +270,7 @@ If you are familiar with [@http://www.boost.org/doc/libs/release/libs/asio/ boos
you can use [classref boost::process::async_pipe async_pipe] which is implemented
as an I/O-Object and can be used like [classref boost::process::pipe pipe] as shown above.
Now we get back to our compiling example. `nm` we might analyze it line by line,
Now we get back to our compiling example. For `nm` we might analyze the output line by line,
but the compiler output will just be put into one large buffer.
With [@http://www.boost.org/doc/libs/release/libs/asio/ boost.asio] this is what it looks like.
@@ -291,7 +290,7 @@ ios.run();
int result = c.exit_code();
```
To make it easier, boost.process provides simpler interface for that, so that the buffer can be passed directly,
To make it easier, boost.process provides a simpler interface for that, so that the buffer can be passed directly,
provided we also pass a reference to an io_service.
```
@@ -305,7 +304,7 @@ int result = c.exit_code();
```
[note Passing an instance of io_service to the launching function automatically cause it to wait asynchronously for the exit, so no call of
[memberref boost::process::child::wait wait] is needed]
[memberref boost::process::child::wait wait] is needed.]
To make it even easier, you can use [@http://en.cppreference.com/w/cpp/thread/future std::future] for asynchronous operations
(you will still need to pass a reference to a io_service) to the launching function, unless you use bp::system or bp::async_system.
@@ -332,7 +331,7 @@ auto err = data.get();
[endsect]
[section:group Groups]
When launching several processes, processes can be grouped together.
When launching several processes, they can be grouped together.
This will also apply for a child process, that launches other processes,
if they do not modify the group membership. E.g. if you call `make` which
launches other processes and call terminate on it,
@@ -343,7 +342,7 @@ The two main reasons to use groups are:
# Being able to terminate child processes of the child process
# Grouping several processes into one, just so they can be terminated at once
If we have program like `make`, which does launch its own child processes,
If we have a program like `make`, which does launch its own child processes,
a call of child_terminate might not suffice. I.e. if we have a makefile launching `gcc`
and use the following code, the `gcc` process will still run afterwards:
@@ -413,14 +412,14 @@ bp::system("stuff", env_);
```
A more convenient way to modify the environment for the child is the
[globalref boost::process::env env] property, which the example as following:
[globalref boost::process::env env] property, which can be used in the example as following:
```
bp::system("stuff", bp::env["VALUE_1"]="foo", bp::env["VALUE_2"]+={"bar1", "bar2"});
```
Please see to the [headerref boost/process/environment.hpp reference] for more information.
Please see the [headerref boost/process/environment.hpp reference] for more information.
[endsect]
[endsect]

View File

@@ -10,6 +10,9 @@
//[intro
#include <boost/process.hpp>
#include <string>
#include <iostream>
using namespace boost::process;
int main()

View File

@@ -30,6 +30,7 @@
#include <boost/process/error.hpp>
#include <boost/process/exe.hpp>
#include <boost/process/group.hpp>
#include <boost/process/handles.hpp>
#include <boost/process/io.hpp>
#include <boost/process/pipe.hpp>
#include <boost/process/shell.hpp>

View File

@@ -138,7 +138,7 @@ struct args_
arg_setter_<char, true> operator()(std::initializer_list<const char*> &&range) const
{
return arg_setter_<char>(range.begin(), range.end());
return arg_setter_<char, true>(range.begin(), range.end());
}
arg_setter_<char, true> operator+=(std::initializer_list<const char*> &&range) const
{
@@ -146,11 +146,11 @@ struct args_
}
arg_setter_<char, false> operator= (std::initializer_list<const char*> &&range) const
{
return arg_setter_<char, true>(range.begin(), range.end());
return arg_setter_<char, false>(range.begin(), range.end());
}
arg_setter_<char, true> operator()(std::initializer_list<std::string> &&range) const
{
return arg_setter_<char>(range.begin(), range.end());
return arg_setter_<char, true>(range.begin(), range.end());
}
arg_setter_<char, true> operator+=(std::initializer_list<std::string> &&range) const
{
@@ -158,12 +158,12 @@ struct args_
}
arg_setter_<char, false> operator= (std::initializer_list<std::string> &&range) const
{
return arg_setter_<char, true>(range.begin(), range.end());
return arg_setter_<char, false>(range.begin(), range.end());
}
arg_setter_<wchar_t, true> operator()(std::initializer_list<const wchar_t*> &&range) const
{
return arg_setter_<wchar_t>(range.begin(), range.end());
return arg_setter_<wchar_t, true>(range.begin(), range.end());
}
arg_setter_<wchar_t, true> operator+=(std::initializer_list<const wchar_t*> &&range) const
{
@@ -171,11 +171,11 @@ struct args_
}
arg_setter_<wchar_t, false> operator= (std::initializer_list<const wchar_t*> &&range) const
{
return arg_setter_<wchar_t, true>(range.begin(), range.end());
return arg_setter_<wchar_t, false>(range.begin(), range.end());
}
arg_setter_<wchar_t, true> operator()(std::initializer_list<std::wstring> &&range) const
{
return arg_setter_<wchar_t>(range.begin(), range.end());
return arg_setter_<wchar_t, true>(range.begin(), range.end());
}
arg_setter_<wchar_t, true> operator+=(std::initializer_list<std::wstring> &&range) const
{
@@ -183,7 +183,7 @@ struct args_
}
arg_setter_<wchar_t, false> operator= (std::initializer_list<std::wstring> &&range) const
{
return arg_setter_<wchar_t, true>(range.begin(), range.end());
return arg_setter_<wchar_t, false>(range.begin(), range.end());
}
};

View File

@@ -109,10 +109,10 @@ with `function` being a callable object with the signature `(int, const std::err
\code{.cpp}
io_context ios;
child c("ls", on_exit=[](int exit, const std::error_code& ec_in){});
child c("ls", ios, on_exit=[](int exit, const std::error_code& ec_in){});
std::future<int> exit_code;
chlid c2("ls", on_exit=exit_code);
chlid c2("ls", ios, on_exit=exit_code);
\endcode

View File

@@ -47,6 +47,8 @@ public:
*/
typedef platform_specific handle_type;
typedef typename handle_type::executor_type executor_type;
/** Construct a new async_pipe, does automatically open the pipe.
* Initializes source and sink with the same io_context.
* @note Windows creates a named pipe here, where the name is automatically generated.

View File

@@ -43,7 +43,7 @@ namespace boost { namespace process { namespace detail {
struct cmd_
{
constexpr cmd_() {}
constexpr cmd_() = default;
template<typename Char>
inline api::cmd_setter_<Char> operator()(const Char *s) const

View File

@@ -73,7 +73,7 @@ public:
template<typename ...Args>
explicit child(Args&&...args);
child() {}
child() { } // Must be kept non defaulted for MSVC 14.1 & 14.2 #113
child& operator=(const child&) = delete;
child& operator=(child && lhs)
{

View File

@@ -7,54 +7,33 @@
#define BOOST_PROCESS_DETAIL_POSIX_ASIO_FWD_HPP_
#include <memory>
#include <boost/asio/ts/netfwd.hpp>
namespace boost { namespace asio {
template<typename T>
struct is_mutable_buffer_sequence;
template<typename T>
struct is_const_buffer_sequence;
class mutable_buffer;
class mutable_buffers_1;
class const_buffer;
class const_buffers_1;
template<typename Allocator>
class basic_streambuf;
typedef basic_streambuf<std::allocator<char>> streambuf;
class io_context;
class executor;
#if defined(BOOST_ASIO_ENABLE_OLD_SERVICES)
class signal_set_service;
template <typename SignalSetService>
class basic_signal_set;
typedef basic_signal_set<signal_set_service> signal_set;
#else /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
template <typename Executor>
class basic_signal_set;
typedef basic_signal_set<executor> signal_set;
#endif /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
typedef basic_signal_set<any_io_executor> signal_set;
template <typename Handler>
class basic_yield_context;
namespace posix {
#if defined(BOOST_ASIO_ENABLE_OLD_SERVICES)
class stream_descriptor_service;
template <typename StreamDesscriptorService>
class basic_stream_descriptor;
typedef basic_stream_descriptor<stream_descriptor_service> stream_descriptor;
#else /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
template <typename Executor>
class basic_stream_descriptor;
typedef basic_stream_descriptor<executor> stream_descriptor;
#endif /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
typedef basic_stream_descriptor<any_io_executor> stream_descriptor;
} //posix
} //asio

View File

@@ -16,13 +16,16 @@
#include <boost/process/async_pipe.hpp>
#include <memory>
#include <future>
#include <boost/process/detail/used_handles.hpp>
#include <array>
namespace boost { namespace process { namespace detail { namespace posix {
template<typename Buffer>
struct async_in_buffer : ::boost::process::detail::posix::handler_base_ext,
::boost::process::detail::posix::require_io_context
::boost::process::detail::posix::require_io_context,
::boost::process::detail::uses_handles
{
Buffer & buf;
@@ -33,6 +36,7 @@ struct async_in_buffer : ::boost::process::detail::posix::handler_base_ext,
fut = promise->get_future(); return std::move(*this);
}
std::shared_ptr<boost::process::async_pipe> pipe;
async_in_buffer(Buffer & buf) : buf(buf)
@@ -76,9 +80,19 @@ struct async_in_buffer : ::boost::process::detail::posix::handler_base_ext,
template<typename Executor>
void on_setup(Executor & exec)
{
pipe = std::make_shared<boost::process::async_pipe>(get_io_context(exec.seq));
if (!pipe)
pipe = std::make_shared<boost::process::async_pipe>(get_io_context(exec.seq));
}
std::array<int, 3> get_used_handles()
{
if (pipe)
return {STDIN_FILENO, pipe->native_source(), pipe->native_sink()};
else //if pipe is not constructed, limit_ds is invoked before -> this also means on_exec_setup gets invoked before.
return {STDIN_FILENO, STDIN_FILENO, STDIN_FILENO};
}
template <typename Executor>
void on_exec_setup(Executor &exec)
{

View File

@@ -19,6 +19,8 @@
#include <memory>
#include <exception>
#include <future>
#include <array>
#include <boost/process/detail/used_handles.hpp>
namespace boost { namespace process { namespace detail { namespace posix {
@@ -45,12 +47,24 @@ inline int apply_out_handles(int handle, std::integral_constant<int, 1>, std::in
template<int p1, int p2, typename Buffer>
struct async_out_buffer : ::boost::process::detail::posix::handler_base_ext,
::boost::process::detail::posix::require_io_context
::boost::process::detail::posix::require_io_context,
::boost::process::detail::uses_handles
{
Buffer & buf;
std::shared_ptr<boost::process::async_pipe> pipe;
std::array<int, 4> get_used_handles()
{
const auto pp1 = p1 != -1 ? p1 : p2;
const auto pp2 = p2 != -1 ? p2 : p1;
if (pipe)
return {pipe->native_source(), pipe->native_sink(), pp1, pp2};
else //if pipe is not constructed, limit_ds is invoked before -> this also means on_exec_setup gets invoked before.
return {pp1, pp2, pp1, pp2};
}
async_out_buffer(Buffer & buf) : buf(buf)
{
@@ -131,8 +145,11 @@ struct async_out_future : ::boost::process::detail::posix::handler_base_ext,
{
std::istream is (buffer_.get());
Type arg;
arg.resize(buffer_->size());
is.read(&*arg.begin(), buffer_->size());
if (buffer_->size() > 0)
{
arg.resize(buffer_->size());
is.read(&*arg.begin(), buffer_->size());
}
promise_->set_value(std::move(arg));
}
});

View File

@@ -23,6 +23,7 @@ class async_pipe
public:
typedef int native_handle_type;
typedef ::boost::asio::posix::stream_descriptor handle_type;
typedef typename handle_type::executor_type executor_type;
inline async_pipe(boost::asio::io_context & ios) : async_pipe(ios, ios) {}
@@ -70,10 +71,8 @@ public:
~async_pipe()
{
if (_sink .native_handle() != -1)
::close(_sink.native_handle());
if (_source.native_handle() != -1)
::close(_source.native_handle());
boost::system::error_code ec;
close(ec);
}
template<class CharT, class Traits = std::char_traits<CharT>>

View File

@@ -82,22 +82,30 @@ public:
int_type write(const char_type * data, int_type count)
{
auto write_len = ::write(_sink, data, count * sizeof(char_type));
if (write_len == -1)
::boost::process::detail::throw_last_error();
int_type write_len;
while ((write_len = ::write(_sink, data, count * sizeof(char_type))) == -1)
{
//Try again if interrupted
auto err = errno;
if (err != EINTR)
::boost::process::detail::throw_last_error();
}
return write_len;
}
int_type read(char_type * data, int_type count)
{
auto read_len = ::read(_source, data, count * sizeof(char_type));
if (read_len == -1)
::boost::process::detail::throw_last_error();
int_type read_len;
while ((read_len = ::read(_source, data, count * sizeof(char_type))) == -1)
{
//Try again if interrupted
auto err = errno;
if (err != EINTR)
::boost::process::detail::throw_last_error();
}
return read_len;
}
bool is_open()
bool is_open() const
{
return (_source != -1) ||
(_sink != -1);

View File

@@ -12,10 +12,11 @@
#include <boost/process/detail/posix/handler.hpp>
#include <boost/process/detail/used_handles.hpp>
namespace boost { namespace process { namespace detail { namespace posix {
struct close_in : handler_base_ext
struct close_in : handler_base_ext, ::boost::process::detail::uses_handles
{
template <class Executor>
void on_exec_setup(Executor &e) const
@@ -23,6 +24,9 @@ struct close_in : handler_base_ext
if (::close(STDIN_FILENO) == -1)
e.set_error(::boost::process::detail::get_last_error(), "close() failed");
}
int get_used_handles() {return STDIN_FILENO;}
};
}}}}

View File

@@ -10,8 +10,9 @@
#ifndef BOOST_PROCESS_DETAIL_POSIX_CLOSE_OUT_HPP
#define BOOST_PROCESS_DETAIL_POSIX_CLOSE_OUT_HPP
#include <boost/process/detail/used_handles.hpp>
#include <boost/process/detail/posix/handler.hpp>
#include <array>
namespace boost { namespace process { namespace detail { namespace posix {
@@ -20,6 +21,8 @@ struct close_out : handler_base_ext
{
template <class Executor>
inline void on_exec_setup(Executor &e) const;
std::array<int, 2> get_used_handles() {return {{p1 != -1 ? p1 : p2, p2 != -1 ? p2 : p1}};}
};
template<>

View File

@@ -94,7 +94,7 @@ public:
native_environment_impl & operator=(native_environment_impl && ) = default;
native_handle_type _env_impl = _impl.data();
native_handle_type native_handle() const {return environ;}
native_handle_type native_handle() const {return _env_impl;}
};
template<>
@@ -230,7 +230,7 @@ template<typename Char>
inline auto basic_environment_impl<Char>::get(const string_type &id) -> string_type
{
auto itr = std::find_if(_data.begin(), _data.end(),
[&](const string_type & st)
[&](const string_type & st) -> bool
{
if (st.size() <= id.size())
return false;
@@ -250,7 +250,7 @@ template<typename Char>
inline void basic_environment_impl<Char>::set(const string_type &id, const string_type &value)
{
auto itr = std::find_if(_data.begin(), _data.end(),
[&](const string_type & st)
[&](const string_type & st) -> bool
{
if (st.size() <= id.size())
return false;
@@ -270,7 +270,7 @@ template<typename Char>
inline void basic_environment_impl<Char>::reset(const string_type &id)
{
auto itr = std::find_if(_data.begin(), _data.end(),
[&](const string_type & st)
[&](const string_type & st) -> bool
{
if (st.size() <= id.size())
return false;
@@ -294,7 +294,11 @@ std::vector<Char*> basic_environment_impl<Char>::_load_var(std::vector<std::basi
ret.reserve(data.size() +1);
for (auto & val : data)
{
if (val.empty())
val.push_back(0);
ret.push_back(&val.front());
}
ret.push_back(nullptr);
return ret;

View File

@@ -274,10 +274,10 @@ class executor
if ((prepare_cmd_style_fn.find('/') == std::string::npos) && ::access(prepare_cmd_style_fn.c_str(), X_OK))
{
auto e = ::environ;
while ((*e != nullptr) && !boost::starts_with(*e, "PATH="))
while ((e != nullptr) && (*e != nullptr) && !boost::starts_with(*e, "PATH="))
e++;
if (e != nullptr)
if ((e != nullptr) && (*e != nullptr))
{
std::vector<std::string> path;
boost::split(path, *e, boost::is_any_of(":"));
@@ -420,7 +420,7 @@ child executor<Sequence>::invoke(boost::mpl::false_, boost::mpl::false_)
_msg = "execve failed";
boost::fusion::for_each(seq, call_on_exec_error(*this, _ec));
_write_error(p.p[1]);
_write_error(_pipe_sink);
::close(p.p[1]);
_exit(EXIT_FAILURE);

View File

@@ -12,11 +12,13 @@
#include <boost/process/detail/posix/handler.hpp>
#include <unistd.h>
#include <boost/process/detail/used_handles.hpp>
#include <array>
namespace boost { namespace process { namespace detail { namespace posix {
struct close_fd_ : handler_base_ext
struct close_fd_ : handler_base_ext, ::boost::process::detail::uses_handles
{
close_fd_(int fd) : fd_(fd) {}
@@ -27,12 +29,15 @@ struct close_fd_ : handler_base_ext
e.set_error(::boost::process::detail::get_last_error(), "close() failed");
}
int get_used_handles() {return fd_;}
private:
int fd_;
};
template <class Range>
struct close_fds_ : handler_base_ext
struct close_fds_ : handler_base_ext, ::boost::process::detail::uses_handles
{
public:
close_fds_(const Range &fds) : fds_(fds) {}
@@ -48,6 +53,8 @@ public:
}
}
Range& get_used_handles() {return fds_;}
private:
Range fds_;
};
@@ -55,7 +62,7 @@ private:
template <class FileDescriptor>
struct bind_fd_ : handler_base_ext
struct bind_fd_ : handler_base_ext, ::boost::process::detail::uses_handles
{
public:
bind_fd_(int id, const FileDescriptor &fd) : id_(id), fd_(fd) {}
@@ -67,6 +74,9 @@ public:
e.set_error(::boost::process::detail::get_last_error(), "dup2() failed");
}
std::array<int, 2> get_used_handles() {return {id_, fd_};}
private:
int id_;
FileDescriptor fd_;

View File

@@ -13,16 +13,22 @@
#include <boost/process/pipe.hpp>
#include <boost/process/detail/posix/handler.hpp>
#include <boost/process/detail/posix/file_descriptor.hpp>
#include <boost/process/detail/used_handles.hpp>
#include <cstdio>
#include <unistd.h>
namespace boost { namespace process { namespace detail { namespace posix {
struct file_in : handler_base_ext
struct file_in : handler_base_ext, ::boost::process::detail::uses_handles
{
file_descriptor file;
int handle = file.handle();
std::array<int, 2> get_used_handles()
{
return {{STDIN_FILENO, handle}};
}
template<typename T>
file_in(T&& t) : file(std::forward<T>(t)) {}
file_in(FILE * f) : handle(fileno(f)) {}

View File

@@ -13,12 +13,13 @@
#include <boost/process/detail/posix/handler.hpp>
#include <boost/process/detail/posix/file_descriptor.hpp>
#include <boost/process/detail/used_handles.hpp>
#include <unistd.h>
namespace boost { namespace process { namespace detail { namespace posix {
template<int p1, int p2>
struct file_out : handler_base_ext
struct file_out : handler_base_ext, ::boost::process::detail::uses_handles
{
file_descriptor file;
int handle = file.handle();
@@ -27,6 +28,13 @@ struct file_out : handler_base_ext
file_out(T&& t) : file(std::forward<T>(t), file_descriptor::write), handle(file.handle()) {}
file_out(FILE * f) : handle(fileno(f)) {}
std::array<int, 3> get_used_handles()
{
const auto pp1 = p1 != -1 ? p1 : p2;
const auto pp2 = p2 != -1 ? p2 : p1;
return {handle, pp1, pp2};
}
template <typename Executor>
void on_exec_setup(Executor &e) const;

View File

@@ -0,0 +1,146 @@
// Copyright (c) 2019 Klemens D. Morgenstern
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_PROCESS_DETAIL_POSIX_HANDLES_HPP_
#define BOOST_PROCESS_DETAIL_POSIX_HANDLES_HPP_
#include <vector>
#include <system_error>
#include <dirent.h>
#include <sys/stat.h>
#include <algorithm>
#include <boost/process/detail/posix/handler.hpp>
namespace boost { namespace process { namespace detail { namespace posix {
using native_handle_type = int;
inline std::vector<native_handle_type> get_handles(std::error_code & ec)
{
std::vector<native_handle_type> res;
std::unique_ptr<DIR, void(*)(DIR*)> dir{::opendir("/dev/fd"), +[](DIR* p){::closedir(p);}};
if (!dir)
{
ec = ::boost::process::detail::get_last_error();
return {};
}
else
ec.clear();
auto my_fd = ::dirfd(dir.get());
struct ::dirent * ent_p;
while ((ent_p = readdir(dir.get())) != nullptr)
{
if (ent_p->d_name[0] == '.')
continue;
const auto conv = std::atoi(ent_p->d_name);
if (conv == 0 && (ent_p->d_name[0] != '0' && ent_p->d_name[1] != '\0'))
continue;
if (conv == my_fd)
continue;
res.push_back(conv);
}
return res;
}
inline std::vector<native_handle_type> get_handles()
{
std::error_code ec;
auto res = get_handles(ec);
if (ec)
boost::process::detail::throw_error(ec, "open_dir(\"/dev/fd\") failed");
return res;
}
inline bool is_stream_handle(native_handle_type handle, std::error_code & ec)
{
struct ::stat stat_;
if (::fstat(handle, &stat_) != 0)
{
ec = ::boost::process::detail::get_last_error();
}
else
ec.clear();
return S_ISCHR (stat_.st_mode) //This macro returns non-zero if the file is a character special file (a device like a terminal).
|| S_ISBLK (stat_.st_mode) // This macro returns non-zero if the file is a block special file (a device like a disk).
|| S_ISREG (stat_.st_mode) // This macro returns non-zero if the file is a regular file.
|| S_ISFIFO (stat_.st_mode) // This macro returns non-zero if the file is a FIFO special file, or a pipe. See section 15. Pipes and FIFOs.
|| S_ISSOCK (stat_.st_mode) ;// This macro returns non-zero if the file is a socket. See section 16. Sockets.;
}
inline bool is_stream_handle(native_handle_type handle)
{
std::error_code ec;
auto res = is_stream_handle(handle, ec);
if (ec)
boost::process::detail::throw_error(ec, "fstat() failed");
return res;
}
struct limit_handles_ : handler_base_ext
{
limit_handles_() {}
~limit_handles_() {}
mutable std::vector<int> used_handles;
template<typename Executor>
void on_setup(Executor & exec) const
{
used_handles = get_used_handles(exec);
}
template<typename Executor>
void on_exec_setup(Executor & exec) const
{
auto dir = ::opendir("/dev/fd");
if (!dir)
{
exec.set_error(::boost::process::detail::get_last_error(), "opendir(\"/dev/fd\")");
return;
}
auto my_fd = ::dirfd(dir);
struct ::dirent * ent_p;
while ((ent_p = readdir(dir)) != nullptr)
{
if (ent_p->d_name[0] == '.')
continue;
const auto conv = std::atoi(ent_p->d_name);
if ((conv == my_fd) || (conv == -1))
continue;
if (std::find(used_handles.begin(), used_handles.end(), conv) != used_handles.end())
continue;
if (::close(conv) != 0)
{
exec.set_error(::boost::process::detail::get_last_error(), "close() failed");
return;
}
}
::closedir(dir);
}
};
}}}}
#endif //PROCESS_HANDLES_HPP

View File

@@ -14,13 +14,21 @@
#include <boost/process/detail/posix/handler.hpp>
#include <boost/process/detail/posix/file_descriptor.hpp>
#include <unistd.h>
#include <boost/process/detail/used_handles.hpp>
#include <array>
namespace boost { namespace process { namespace detail { namespace posix {
struct null_in : handler_base_ext
struct null_in : handler_base_ext, ::boost::process::detail::uses_handles
{
file_descriptor source{"/dev/null", file_descriptor::read};
std::array<int, 2> get_used_handles()
{
return {{STDIN_FILENO, source.handle()}};
}
public:
template <class Executor>
void on_exec_setup(Executor &e) const

View File

@@ -13,17 +13,27 @@
#include <boost/process/detail/posix/handler.hpp>
#include <boost/process/detail/posix/file_descriptor.hpp>
#include <boost/process/detail/used_handles.hpp>
#include <unistd.h>
#include <array>
namespace boost { namespace process { namespace detail { namespace posix {
template<int p1, int p2>
struct null_out : handler_base_ext
struct null_out : handler_base_ext, ::boost::process::detail::uses_handles
{
file_descriptor sink{"/dev/null", file_descriptor::write};
template <typename Executor>
void on_exec_setup(Executor &e) const;
std::array<int, 3> get_used_handles()
{
const auto pp1 = p1 != -1 ? p1 : p2;
const auto pp2 = p2 != -1 ? p2 : p1;
return {sink.handle(), pp1, pp2};
}
};
template<>

View File

@@ -13,17 +13,23 @@
#include <boost/process/pipe.hpp>
#include <boost/process/detail/posix/handler.hpp>
#include <unistd.h>
#include <boost/process/detail/used_handles.hpp>
#include <array>
namespace boost { namespace process { namespace detail { namespace posix {
struct pipe_in : handler_base_ext
struct pipe_in : handler_base_ext, ::boost::process::detail::uses_handles
{
int source;
int sink; //opposite end
pipe_in(int sink, int source) : source(source), sink(sink) {}
std::array<int, 3> get_used_handles()
{
return {{STDIN_FILENO, source, sink}};
}
template<typename T>
pipe_in(T & p) : source(p.native_source()), sink(p.native_sink())
@@ -48,7 +54,9 @@ struct pipe_in : handler_base_ext
{
if (::dup2(source, STDIN_FILENO) == -1)
e.set_error(::boost::process::detail::get_last_error(), "dup2() failed");
::close(source);
if (source != STDIN_FILENO)
::close(source);
::close(sink);
}

View File

@@ -52,8 +52,10 @@ template<typename Executor>
void pipe_out<1,-1>::on_exec_setup(Executor &e) const
{
if (::dup2(sink, STDOUT_FILENO) == -1)
e.set_error(::boost::process::detail::get_last_error(), "dup3() failed");
::close(sink);
e.set_error(::boost::process::detail::get_last_error(), "dup2() failed");
if (sink != STDOUT_FILENO)
::close(sink);
::close(source);
}
@@ -63,7 +65,9 @@ void pipe_out<2,-1>::on_exec_setup(Executor &e) const
{
if (::dup2(sink, STDERR_FILENO) == -1)
e.set_error(::boost::process::detail::get_last_error(), "dup2() failed");
::close(sink);
if (sink != STDOUT_FILENO)
::close(sink);
::close(source);
}
@@ -75,8 +79,8 @@ void pipe_out<1,2>::on_exec_setup(Executor &e) const
e.set_error(::boost::process::detail::get_last_error(), "dup2() failed");
if (::dup2(sink, STDERR_FILENO) == -1)
e.set_error(::boost::process::detail::get_last_error(), "dup2() failed");
::close(sink);
::close(source);
if ((sink != STDOUT_FILENO) && (sink != STDERR_FILENO))
::close(sink);
}
class async_pipe;

View File

@@ -20,7 +20,7 @@ namespace boost { namespace process { namespace detail { namespace posix {
class sigchld_service : public boost::asio::detail::service_base<sigchld_service>
{
boost::asio::io_context::strand _strand{get_io_context()};
boost::asio::strand<boost::asio::io_context::executor_type> _strand{get_io_context().get_executor()};
boost::asio::signal_set _signal_set{get_io_context(), SIGCHLD};
std::vector<std::pair<::pid_t, std::function<void(int, std::error_code)>>> _receivers;

View File

@@ -21,7 +21,7 @@ struct start_dir_init : handler_base_ext
{
typedef Char value_type;
typedef std::basic_string<value_type> string_type;
start_dir_init(const string_type &s) : s_(s) {}
start_dir_init(string_type s) : s_(std::move(s)) {}
template <class PosixExecutor>
void on_exec_setup(PosixExecutor&) const

View File

@@ -27,7 +27,7 @@ inline void terminate(const child_handle &p, std::error_code &ec) noexcept
ec.clear();
int status;
::waitpid(p.pid, &status, 0); //just to clean it up
::waitpid(p.pid, &status, WNOHANG); //just to clean it up
}
inline void terminate(const child_handle &p)

View File

@@ -54,11 +54,35 @@ inline bool wait_until(
const std::chrono::time_point<Clock, Duration>& time_out,
std::error_code & ec) noexcept
{
::sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGCHLD);
//I need to set the signal, because it might be ignore / default, in which case sigwait might not work.
using _signal_t = void(*)(int);
static thread_local _signal_t sigchld_handler = SIG_DFL;
struct signal_interceptor_t
{
static void handler_func(int val)
{
if ((sigchld_handler != SIG_DFL) && (sigchld_handler != SIG_IGN))
sigchld_handler(val);
}
signal_interceptor_t() { sigchld_handler = ::signal(SIGCHLD, &handler_func); }
~signal_interceptor_t() { ::signal(SIGCHLD, sigchld_handler); sigchld_handler = SIG_DFL;}
} signal_interceptor{};
if (sigemptyset(&sigset) != 0)
{
ec = get_last_error();
return false;
}
if (sigaddset(&sigset, SIGCHLD) != 0)
{
ec = get_last_error();
return false;
}
auto get_timespec =
[](const Duration & dur)
@@ -69,8 +93,8 @@ inline bool wait_until(
return ts;
};
pid_t ret;
int status;
int ret;
int status{0};
struct ::sigaction old_sig;
if (-1 == ::sigaction(SIGCHLD, nullptr, &old_sig))
@@ -80,6 +104,7 @@ inline bool wait_until(
}
bool timed_out;
#if defined(BOOST_POSIX_HAS_SIGTIMEDWAIT)
do
{
@@ -113,9 +138,16 @@ inline bool wait_until(
{
auto ts = get_timespec(time_out - Clock::now());
::timespec rem;
::nanosleep(&ts, &rem);
while (rem.tv_sec > 0 || rem.tv_nsec > 0)
::nanosleep(&rem, &rem);
while (ts.tv_sec > 0 || ts.tv_nsec > 0)
{
if (::nanosleep(&ts, &rem) != 0)
{
auto err = errno;
if ((err == EINVAL) || (err == EFAULT))
break;
}
ts = get_timespec(time_out - Clock::now());
}
::exit(0);
}
@@ -125,7 +157,7 @@ inline bool wait_until(
~child_cleaner_t()
{
int res;
::kill(pid, -15);
::kill(pid, SIGKILL);
::waitpid(pid, &res, WNOHANG);
}
};
@@ -133,19 +165,21 @@ inline bool wait_until(
do
{
int ret_sig = 0;
int sig_{0};
if ((::waitpid(timeout_pid, &status, WNOHANG) != 0)
&& (WIFEXITED(status) || WIFSIGNALED(status)))
ret_sig = ::sigwait(&sigset, nullptr);
&& (WIFEXITED(status) || WIFSIGNALED(status)))
return false;
ret = ::sigwait(&sigset, &sig_);
errno = 0;
ret = ::waitpid(p.pid, &status, WNOHANG);
if ((ret_sig == SIGCHLD) &&
if ((sig_ == SIGCHLD) &&
(old_sig.sa_handler != SIG_DFL) && (old_sig.sa_handler != SIG_IGN))
old_sig.sa_handler(ret);
if (ret <= 0)
ret = ::waitpid(p.pid, &status, WNOHANG);
if (ret == 0) // == > is running
{
timed_out = Clock::now() >= time_out;
if (timed_out)

View File

@@ -59,118 +59,33 @@ inline bool wait_until(
std::error_code & ec) noexcept
{
::sigset_t sigset;
::siginfo_t siginfo;
sigemptyset(&sigset);
sigaddset(&sigset, SIGCHLD);
auto get_timespec =
[](const Duration & dur)
{
::timespec ts;
ts.tv_sec = std::chrono::duration_cast<std::chrono::seconds>(dur).count();
ts.tv_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(dur).count() % 1000000000;
return ts;
};
bool timed_out = false;
int ret;
struct ::sigaction old_sig;
if (-1 == ::sigaction(SIGCHLD, nullptr, &old_sig))
{
ec = get_last_error();
return false;
}
::timespec sleep_interval;
sleep_interval.tv_sec = 0;
sleep_interval.tv_nsec = 100000000;
#if defined(BOOST_POSIX_HAS_SIGTIMEDWAIT)
do
{
auto ts = get_timespec(time_out - Clock::now());
ret = ::sigtimedwait(&sigset, nullptr, &ts);
errno = 0;
if ((ret == SIGCHLD) && (old_sig.sa_handler != SIG_DFL) && (old_sig.sa_handler != SIG_IGN))
old_sig.sa_handler(ret);
ret = ::waitpid(-p.grp, &siginfo.si_status, 0); //so in case it exited, we wanna reap it first
while (!(timed_out = (Clock::now() > time_out)))
{
ret = ::waitid(P_PGID, p.grp, &siginfo, WEXITED | WSTOPPED | WNOHANG);
if (ret == -1)
{
ec = get_last_error();
return false;
}
//check if we're done
ret = ::waitid(P_PGID, p.grp, &siginfo, WEXITED | WNOHANG);
}
while (((ret != -1) || (errno != ECHILD)) && !(timed_out = (Clock::now() > time_out)));
#else
//if we do not have sigtimedwait, we fork off a child process to get the signal in time
pid_t timeout_pid = ::fork();
if (timeout_pid == -1)
{
ec = boost::process::detail::get_last_error();
return true;
}
else if (timeout_pid == 0)
{
auto ts = get_timespec(time_out - Clock::now());
::setpgid(0, p.grp);
::nanosleep(&ts, nullptr);
::exit(0);
}
struct child_cleaner_t
{
pid_t pid;
~child_cleaner_t()
{
int res;
::kill(pid, -15);
::waitpid(pid, &res, WNOHANG);
}
};
child_cleaner_t child_cleaner{timeout_pid};
do
{
int status;
if ((::waitpid(timeout_pid, &status, WNOHANG) != 0)
&& (WIFEXITED(status) || WIFSIGNALED(status)))
ret = ::sigwait(&sigset, nullptr);
errno = 0;
if ((ret == SIGCHLD) && (old_sig.sa_handler != SIG_DFL) && (old_sig.sa_handler != SIG_IGN))
old_sig.sa_handler(ret);
ret = ::waitpid(-p.grp, &siginfo.si_status, 0); //so in case it exited, we wanna reap it first
if (ret == -1)
{
ec = get_last_error();
if ((errno == ECHILD) || (errno == ESRCH))
{
ec.clear();
return true;
}
ec = boost::process::detail::get_last_error();
return false;
}
//check if we're done
ret = ::waitid(P_PGID, p.grp, &siginfo, WEXITED | WNOHANG);
//we can wait, because unlike in the wait_for_exit, we have no race condition regarding eh exit code.
::nanosleep(&sleep_interval, nullptr);
}
while (((ret != -1) || (errno != ECHILD)) && !(timed_out = (Clock::now() > time_out)));
#endif
if (errno != ECHILD)
{
ec = boost::process::detail::get_last_error();
return !timed_out;
}
else
{
ec.clear();
return true; //even if timed out, there are no child proccessess left
}
return !timed_out;
}
template< class Clock, class Duration >

View File

@@ -0,0 +1,81 @@
// Copyright (c) 2016 Klemens D. Morgenstern
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_PROCESS_DETAIL_USED_HANDLES_HPP_
#define BOOST_PROCESS_DETAIL_USED_HANDLES_HPP_
#include <type_traits>
#include <boost/fusion/include/filter_if.hpp>
#include <boost/fusion/include/for_each.hpp>
#if defined(BOOST_POSIX_API)
#include <boost/process/detail/posix/handles.hpp>
#include <boost/process/detail/posix/asio_fwd.hpp>
#else
#include <boost/process/detail/windows/handles.hpp>
#include <boost/process/detail/windows/asio_fwd.hpp>
#endif
namespace boost { namespace process { namespace detail {
struct uses_handles
{
//If you get an error here, you must add a `get_handles` function that returns a range or a single handle value
void get_used_handles() const;
};
template<typename T>
struct does_use_handle: std::is_base_of<uses_handles, T> {};
template<typename T>
struct does_use_handle<T&> : std::is_base_of<uses_handles, T> {};
template<typename T>
struct does_use_handle<const T&> : std::is_base_of<uses_handles, T> {};
template<typename Char, typename Sequence>
class executor;
template<typename Func>
struct foreach_handle_invocator
{
Func & func;
foreach_handle_invocator(Func & func) : func(func) {}
template<typename Range>
void invoke(const Range & range) const
{
for (auto handle_ : range)
func(handle_);
}
void invoke(::boost::process::detail::api::native_handle_type handle) const {func(handle);};
template<typename T>
void operator()(T & val) const {invoke(val.get_used_handles());}
};
template<typename Executor, typename Function>
void foreach_used_handle(Executor &exec, Function &&func)
{
boost::fusion::for_each(boost::fusion::filter_if<does_use_handle<boost::mpl::_>>(exec.seq),
foreach_handle_invocator<Function>(func));
}
template<typename Executor>
std::vector<::boost::process::detail::api::native_handle_type>
get_used_handles(Executor &exec)
{
std::vector<::boost::process::detail::api::native_handle_type> res;
foreach_used_handle(exec, [&](::boost::process::detail::api::native_handle_type handle){res.push_back(handle);});
return res;
}
}}}
#endif /* BOOST_PROCESS_DETAIL_USED_HANDLES_HPP_ */

View File

@@ -7,57 +7,32 @@
#define BOOST_PROCESS_DETAIL_WINDOWS_ASIO_FWD_HPP_
#include <memory>
#include <boost/asio/ts/netfwd.hpp>
namespace boost { namespace asio {
template<typename T>
struct is_mutable_buffer_sequence;
template<typename T>
struct is_const_buffer_sequence;
class mutable_buffer;
class mutable_buffers_1;
class const_buffer;
class const_buffers_1;
template<typename Allocator>
class basic_streambuf;
typedef basic_streambuf<std::allocator<char>> streambuf;
class io_context;
class executor;
template <typename Handler>
class basic_yield_context;
namespace windows {
#if defined(BOOST_ASIO_ENABLE_OLD_SERVICES)
class stream_handle_service;
template <typename StreamHandleService>
class basic_stream_handle;
typedef basic_stream_handle<stream_handle_service> stream_handle;
#else /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
template <typename Executor>
class basic_stream_handle;
typedef basic_stream_handle<executor> stream_handle;
typedef basic_stream_handle<any_io_executor> stream_handle;
#endif /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
#if defined(BOOST_ASIO_ENABLE_OLD_SERVICES)
class object_handle_service;
template <typename ObjectHandleService>
class basic_object_handle;
typedef basic_object_handle<object_handle_service> object_handle;
#else /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
template <typename Executor>
class basic_object_handle;
typedef basic_object_handle<executor> object_handle;
#endif /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
typedef basic_object_handle<any_io_executor> object_handle;
} //windows
} //asio

View File

@@ -17,19 +17,20 @@
#include <boost/asio/write.hpp>
#include <boost/process/detail/handler_base.hpp>
#include <boost/process/detail/used_handles.hpp>
#include <boost/process/detail/windows/async_handler.hpp>
#include <boost/process/detail/windows/asio_fwd.hpp>
#include <boost/process/async_pipe.hpp>
#include <memory>
#include <future>
namespace boost { namespace process { namespace detail { namespace windows {
template<typename Buffer>
struct async_in_buffer : ::boost::process::detail::windows::handler_base_ext,
::boost::process::detail::windows::require_io_context
::boost::process::detail::windows::require_io_context,
::boost::process::detail::uses_handles
{
Buffer & buf;
@@ -42,6 +43,11 @@ struct async_in_buffer : ::boost::process::detail::windows::handler_base_ext,
std::shared_ptr<boost::process::async_pipe> pipe;
::boost::winapi::HANDLE_ get_used_handles() const
{
return std::move(*pipe).source().native_handle();
}
async_in_buffer(Buffer & buf) : buf(buf)
{
}

View File

@@ -16,6 +16,7 @@
#include <boost/winapi/error_codes.hpp>
#include <boost/asio/read.hpp>
#include <boost/process/detail/handler_base.hpp>
#include <boost/process/detail/used_handles.hpp>
#include <boost/process/detail/windows/asio_fwd.hpp>
#include <istream>
@@ -108,12 +109,18 @@ struct async_out_buffer : ::boost::process::detail::windows::handler_base_ext,
template<int p1, int p2, typename Type>
struct async_out_future : ::boost::process::detail::windows::handler_base_ext,
::boost::process::detail::windows::require_io_context
::boost::process::detail::windows::require_io_context,
::boost::process::detail::uses_handles
{
std::shared_ptr<boost::process::async_pipe> pipe;
std::shared_ptr<std::promise<Type>> promise = std::make_shared<std::promise<Type>>();
std::shared_ptr<boost::asio::streambuf> buffer = std::make_shared<boost::asio::streambuf>();
::boost::winapi::HANDLE_ get_used_handles() const
{
return std::move(*pipe).sink().native_handle();
}
async_out_future(std::future<Type> & fut)
{

View File

@@ -40,17 +40,27 @@ class async_pipe
{
::boost::asio::windows::stream_handle _source;
::boost::asio::windows::stream_handle _sink ;
public:
typedef ::boost::winapi::HANDLE_ native_handle_type;
typedef ::boost::asio::windows::stream_handle handle_type;
inline async_pipe(boost::asio::io_context & ios,
const std::string & name = make_pipe_name())
: async_pipe(ios, ios, name) {}
inline async_pipe(boost::asio::io_context & ios_source,
boost::asio::io_context & ios_sink,
const std::string & name = make_pipe_name());
const std::string & name, bool private_);
public:
typedef ::boost::winapi::HANDLE_ native_handle_type;
typedef ::boost::asio::windows::stream_handle handle_type;
typedef typename handle_type::executor_type executor_type;
async_pipe(boost::asio::io_context & ios) : async_pipe(ios, ios, make_pipe_name(), true) {}
async_pipe(boost::asio::io_context & ios_source, boost::asio::io_context & ios_sink)
: async_pipe(ios_source, ios_sink, make_pipe_name(), true) {}
async_pipe(boost::asio::io_context & ios, const std::string & name)
: async_pipe(ios, ios, name, false) {}
async_pipe(boost::asio::io_context & ios_source, boost::asio::io_context & ios_sink, const std::string & name)
: async_pipe(ios_source, ios_sink, name, false) {}
inline async_pipe(const async_pipe& rhs);
async_pipe(async_pipe&& rhs) : _source(std::move(rhs._source)), _sink(std::move(rhs._sink))
@@ -274,7 +284,7 @@ async_pipe::async_pipe(const async_pipe& p) :
async_pipe::async_pipe(boost::asio::io_context & ios_source,
boost::asio::io_context & ios_sink,
const std::string & name) : _source(ios_source), _sink(ios_sink)
const std::string & name, bool private_) : _source(ios_source), _sink(ios_sink)
{
static constexpr int FILE_FLAG_OVERLAPPED_ = 0x40000000; //temporary
@@ -286,7 +296,7 @@ async_pipe::async_pipe(boost::asio::io_context & ios_source,
#endif
::boost::winapi::PIPE_ACCESS_INBOUND_
| FILE_FLAG_OVERLAPPED_, //write flag
0, 1, 8192, 8192, 0, nullptr);
0, private_ ? 1 : ::boost::winapi::PIPE_UNLIMITED_INSTANCES_, 8192, 8192, 0, nullptr);
if (source == boost::winapi::INVALID_HANDLE_VALUE_)

View File

@@ -30,14 +30,17 @@ inline std::string build_args(const std::string & exe, std::vector<std::string>
{
std::string st = exe;
//put in quotes if it has spaces
//put in quotes if it has spaces or double quotes
if(!exe.empty())
{
boost::replace_all(st, "\"", "\\\"");
auto it = st.find_first_of(" \"");
auto it = std::find(st.begin(), st.end(), ' ');
if (it != st.end())//contains spaces.
if(it != st.npos)//contains spaces.
{
// double existing quotes
boost::replace_all(st, "\"", "\"\"");
// surround with quotes
st.insert(st.begin(), '"');
st += '"';
}
@@ -45,15 +48,18 @@ inline std::string build_args(const std::string & exe, std::vector<std::string>
for (auto & arg : data)
{
boost::replace_all(arg, "\"", "\\\"");
auto it = std::find(arg.begin(), arg.end(), ' ');//contains space?
if (it != arg.end())//ok, contains spaces.
if(!arg.empty())
{
//the first one is put directly onto the output,
//because then I don't have to copy the whole string
arg.insert(arg.begin(), '"');
arg += '"'; //thats the post one.
auto it = arg.find_first_of(" \"");//contains space or double quotes?
if(it != arg.npos)//yes
{
// double existing quotes
boost::replace_all(arg, "\"", "\"\"");
// surround with quotes
arg.insert(arg.begin(), '"');
arg += '"';
}
}
if (!st.empty())//first one does not need a preceeding space
@@ -68,30 +74,36 @@ inline std::wstring build_args(const std::wstring & exe, std::vector<std::wstrin
{
std::wstring st = exe;
//put in quotes if it has spaces
//put in quotes if it has spaces or double quotes
if(!exe.empty())
{
boost::replace_all(st, L"\"", L"\\\"");
auto it = st.find_first_of(L" \"");
auto it = std::find(st.begin(), st.end(), L' ');
if (it != st.end())//contains spaces.
if(it != st.npos)//contains spaces or double quotes.
{
// double existing quotes
boost::replace_all(st, L"\"", L"\"\"");
// surround with quotes
st.insert(st.begin(), L'"');
st += L'"';
}
}
for (auto & arg : data)
for(auto & arg : data)
{
boost::replace_all(arg, L"\"", L"\\\"");
auto it = std::find(arg.begin(), arg.end(), L' ');//contains space?
if (it != arg.end())//ok, contains spaces.
if(!arg.empty())
{
//the first one is put directly onto the output,
//because then I don't have to copy the whole string
arg.insert(arg.begin(), L'"');
arg += L'"'; //thats the post one.
auto it = arg.find_first_of(L" \"");//contains space or double quotes?
if(it != arg.npos)//yes
{
// double existing quotes
boost::replace_all(arg, L"\"", L"\"\"");
// surround with quotes
arg.insert(arg.begin(), L'"');
arg += '"';
}
}
if (!st.empty())//first one does not need a preceeding space

View File

@@ -98,7 +98,7 @@ public:
return static_cast<int_type>(read_len);
}
bool is_open()
bool is_open() const
{
return (_source != ::boost::winapi::INVALID_HANDLE_VALUE_) ||
(_sink != ::boost::winapi::INVALID_HANDLE_VALUE_);
@@ -152,7 +152,7 @@ basic_pipe<Char, Traits>::basic_pipe(const std::string & name)
name_.c_str(),
::boost::winapi::PIPE_ACCESS_INBOUND_
| FILE_FLAG_OVERLAPPED_, //write flag
0, 1, 8192, 8192, 0, nullptr);
0, ::boost::winapi::PIPE_UNLIMITED_INSTANCES_, 8192, 8192, 0, nullptr);
if (source == boost::winapi::INVALID_HANDLE_VALUE_)
::boost::process::detail::throw_last_error("create_named_pipe() failed");

View File

@@ -17,7 +17,7 @@
namespace boost { namespace process { namespace detail { namespace windows {
typedef int pid_t;
typedef ::boost::winapi::DWORD_ pid_t;
struct child_handle
{
@@ -66,7 +66,7 @@ struct child_handle
pid_t id() const
{
return static_cast<int>(proc_info.dwProcessId);
return static_cast<pid_t>(proc_info.dwProcessId);
}
typedef ::boost::winapi::HANDLE_ process_handle_t;

View File

@@ -11,6 +11,7 @@
#include <boost/winapi/file_management.hpp>
#include <string>
#include <boost/filesystem/path.hpp>
#include <boost/core/exchange.hpp>
namespace boost { namespace process { namespace detail { namespace windows {
@@ -90,10 +91,18 @@ struct file_descriptor
}
file_descriptor(const file_descriptor & ) = delete;
file_descriptor(file_descriptor && ) = default;
file_descriptor(file_descriptor &&other)
: _handle( boost::exchange(other._handle, ::boost::winapi::INVALID_HANDLE_VALUE_) )
{
}
file_descriptor& operator=(const file_descriptor & ) = delete;
file_descriptor& operator=(file_descriptor && ) = default;
file_descriptor& operator=(file_descriptor &&other)
{
if (_handle != ::boost::winapi::INVALID_HANDLE_VALUE_)
::boost::winapi::CloseHandle(_handle);
_handle = boost::exchange(other._handle, ::boost::winapi::INVALID_HANDLE_VALUE_);
}
~file_descriptor()
{

View File

@@ -13,16 +13,20 @@
#include <boost/winapi/process.hpp>
#include <boost/winapi/handles.hpp>
#include <boost/process/detail/handler_base.hpp>
#include <boost/process/detail/used_handles.hpp>
#include <boost/process/detail/windows/file_descriptor.hpp>
#include <io.h>
namespace boost { namespace process { namespace detail { namespace windows {
struct file_in : public ::boost::process::detail::handler_base
struct file_in : public ::boost::process::detail::handler_base,
::boost::process::detail::uses_handles
{
file_descriptor file;
::boost::winapi::HANDLE_ handle = file.handle();
::boost::winapi::HANDLE_ get_used_handles() const { return handle; }
template<typename T>
file_in(T&& t) : file(std::forward<T>(t), file_descriptor::read) {}
file_in(FILE * f) : handle(reinterpret_cast<::boost::winapi::HANDLE_>(_get_osfhandle(_fileno(f)))) {}

View File

@@ -14,16 +14,21 @@
#include <boost/winapi/handles.hpp>
#include <boost/winapi/handle_info.hpp>
#include <boost/process/detail/handler_base.hpp>
#include <boost/process/detail/used_handles.hpp>
#include <boost/process/detail/windows/file_descriptor.hpp>
namespace boost { namespace process { namespace detail { namespace windows {
template<int p1, int p2>
struct file_out : public ::boost::process::detail::handler_base
struct file_out : public ::boost::process::detail::handler_base,
::boost::process::detail::uses_handles
{
file_descriptor file;
::boost::winapi::HANDLE_ handle = file.handle();
::boost::winapi::HANDLE_ get_used_handles() const { return handle; }
template<typename T>
file_out(T&& t) : file(std::forward<T>(t), file_descriptor::write) {}
file_out(FILE * f) : handle(reinterpret_cast<void*>(_get_osfhandle(_fileno(f)))) {}

View File

@@ -6,9 +6,10 @@
#ifndef BOOST_PROCESS_DETAIL_WINDOWS_GROUP_REF_HPP_
#define BOOST_PROCESS_DETAIL_WINDOWS_GROUP_REF_HPP_
#include <boost/winapi/process.hpp>
#include <boost/process/detail/config.hpp>
#include <boost/process/detail/windows/group_handle.hpp>
#include <boost/winapi/process.hpp>
#include <boost/process/detail/used_handles.hpp>
#include <boost/process/detail/windows/handler.hpp>
namespace boost { namespace process {
@@ -17,10 +18,12 @@ namespace detail { namespace windows {
struct group_ref : handler_base_ext
struct group_ref : handler_base_ext, ::boost::process::detail::uses_handles
{
::boost::winapi::HANDLE_ handle;
::boost::winapi::HANDLE_ get_used_handles() const { return handle; }
explicit group_ref(group_handle &g) :
handle(g.handle())
{}

View File

@@ -0,0 +1,262 @@
// Copyright (c) 2018 Klemens D. Morgenstern
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_PROCESS_DETAIL_WINDOWS_HANDLE_WORKAROUND_HPP_
#define BOOST_PROCESS_DETAIL_WINDOWS_HANDLE_WORKAROUND_HPP_
#include <boost/winapi/basic_types.hpp>
#include <boost/winapi/dll.hpp>
#include <boost/winapi/access_rights.hpp>
//#define BOOST_USE_WINDOWS_H 1
#if defined( BOOST_USE_WINDOWS_H )
#include <Winternl.h>
#endif
namespace boost { namespace process { namespace detail { namespace windows { namespace workaround
{
typedef struct _SYSTEM_HANDLE_ENTRY_
{
::boost::winapi::ULONG_ OwnerPid;
::boost::winapi::BYTE_ ObjectType;
::boost::winapi::BYTE_ HandleFlags;
::boost::winapi::USHORT_ HandleValue;
::boost::winapi::PVOID_ ObjectPointer;
::boost::winapi::ULONG_ AccessMask;
} SYSTEM_HANDLE_ENTRY_, *PSYSTEM_HANDLE_ENTRY_;
typedef struct _SYSTEM_HANDLE_INFORMATION_
{
::boost::winapi::ULONG_ Count;
SYSTEM_HANDLE_ENTRY_ Handle[1];
} SYSTEM_HANDLE_INFORMATION_, *PSYSTEM_HANDLE_INFORMATION_;
#if defined( BOOST_USE_WINDOWS_H )
using UNICODE_STRING_ = ::UNICODE_STRING;
using GENERIC_MAPPING_ = ::GENERIC_MAPPING;
using OBJECT_INFORMATION_CLASS_ = ::OBJECT_INFORMATION_CLASS;
constexpr static OBJECT_INFORMATION_CLASS_ ObjectTypeInformation = ::OBJECT_INFORMATION_CLASS::ObjectTypeInformation;
typedef struct _OBJECT_TYPE_INFORMATION_ {
UNICODE_STRING TypeName;
ULONG TotalNumberOfObjects;
ULONG TotalNumberOfHandles;
ULONG TotalPagedPoolUsage;
ULONG TotalNonPagedPoolUsage;
ULONG TotalNamePoolUsage;
ULONG TotalHandleTableUsage;
ULONG HighWaterNumberOfObjects;
ULONG HighWaterNumberOfHandles;
ULONG HighWaterPagedPoolUsage;
ULONG HighWaterNonPagedPoolUsage;
ULONG HighWaterNamePoolUsage;
ULONG HighWaterHandleTableUsage;
ULONG InvalidAttributes;
GENERIC_MAPPING GenericMapping;
ULONG ValidAccessMask;
BOOLEAN SecurityRequired;
BOOLEAN MaintainHandleCount;
UCHAR TypeIndex;
CHAR ReservedByte;
ULONG PoolType;
ULONG DefaultPagedPoolCharge;
ULONG DefaultNonPagedPoolCharge;
} OBJECT_TYPE_INFORMATION_, *POBJECT_TYPE_INFORMATION_;
#else
typedef enum _OBJECT_INFORMATION_CLASS_
{
ObjectBasicInformation,
ObjectNameInformation,
ObjectTypeInformation,
ObjectAllInformation,
ObjectDataInformation
} OBJECT_INFORMATION_CLASS_, *POBJECT_INFORMATION_CLASS_;
typedef struct _UNICODE_STRING_ {
::boost::winapi::USHORT_ Length;
::boost::winapi::USHORT_ MaximumLength;
::boost::winapi::LPWSTR_ Buffer;
} UNICODE_STRING_, *PUNICODE_STRING_;
typedef struct _GENERIC_MAPPING_ {
::boost::winapi::ACCESS_MASK_ GenericRead;
::boost::winapi::ACCESS_MASK_ GenericWrite;
::boost::winapi::ACCESS_MASK_ GenericExecute;
::boost::winapi::ACCESS_MASK_ GenericAll;
} GENERIC_MAPPING_;
#endif
typedef struct _OBJECT_BASIC_INFORMATION {
::boost::winapi::ULONG_ Attributes;
::boost::winapi::ACCESS_MASK_ GrantedAccess;
::boost::winapi::ULONG_ HandleCount;
::boost::winapi::ULONG_ PointerCount;
::boost::winapi::ULONG_ PagedPoolUsage;
::boost::winapi::ULONG_ NonPagedPoolUsage;
::boost::winapi::ULONG_ Reserved[3];
::boost::winapi::ULONG_ NameInformationLength;
::boost::winapi::ULONG_ TypeInformationLength;
::boost::winapi::ULONG_ SecurityDescriptorLength;
::boost::winapi::LARGE_INTEGER_ CreateTime;
} OBJECT_BASIC_INFORMATION_, *POBJECT_BASIC_INFORMATION_;
typedef struct _OBJECT_NAME_INFORMATION {
UNICODE_STRING_ Name;
} OBJECT_NAME_INFORMATION_, *POBJECT_NAME_INFORMATION_;
#if defined( BOOST_USE_WINDOWS_H )
extern "C"
{
using SYSTEM_INFORMATION_CLASS_ = ::SYSTEM_INFORMATION_CLASS;
constexpr static SYSTEM_INFORMATION_CLASS_ SystemHandleInformation_ = static_cast<SYSTEM_INFORMATION_CLASS_>(16);
inline ::boost::winapi::NTSTATUS_ nt_system_query_information(
SYSTEM_INFORMATION_CLASS SystemInformationClass,
void * SystemInformation,
::boost::winapi::ULONG_ SystemInformationLength,
::boost::winapi::PULONG_ ReturnLength)
{
return ::NtQuerySystemInformation(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength);
}
inline ::boost::winapi::NTSTATUS_ nt_query_object(
::boost::winapi::HANDLE_ Handle,
OBJECT_INFORMATION_CLASS_ ObjectInformationClass,
::boost::winapi::PVOID_ ObjectInformation,
::boost::winapi::ULONG_ ObjectInformationLength,
::boost::winapi::PULONG_ ReturnLength
)
{
return ::NtQueryObject(Handle, ObjectInformationClass, ObjectInformation, ObjectInformationLength, ReturnLength);
}
}
#else
//this import workaround is to keep it a header-only library. and enums cannot be imported from the winapi.
extern "C"
{
typedef enum _SYSTEM_INFORMATION_CLASS_
{
SystemBasicInformation_ = 0,
SystemProcessorInformation_ = 1,
SystemPerformanceInformation_ = 2,
SystemTimeOfDayInformation_ = 3,
SystemProcessInformation_ = 5,
SystemProcessorPerformanceInformation_ = 8,
SystemHandleInformation_ = 16,
SystemPagefileInformation_ = 18,
SystemInterruptInformation_ = 23,
SystemExceptionInformation_ = 33,
SystemRegistryQuotaInformation_ = 37,
SystemLookasideInformation_ = 45
} SYSTEM_INFORMATION_CLASS_;
typedef struct _OBJECT_TYPE_INFORMATION_ {
UNICODE_STRING_ TypeName;
::boost::winapi::ULONG_ TotalNumberOfObjects;
::boost::winapi::ULONG_ TotalNumberOfHandles;
::boost::winapi::ULONG_ TotalPagedPoolUsage;
::boost::winapi::ULONG_ TotalNonPagedPoolUsage;
::boost::winapi::ULONG_ TotalNamePoolUsage;
::boost::winapi::ULONG_ TotalHandleTableUsage;
::boost::winapi::ULONG_ HighWaterNumberOfObjects;
::boost::winapi::ULONG_ HighWaterNumberOfHandles;
::boost::winapi::ULONG_ HighWaterPagedPoolUsage;
::boost::winapi::ULONG_ HighWaterNonPagedPoolUsage;
::boost::winapi::ULONG_ HighWaterNamePoolUsage;
::boost::winapi::ULONG_ HighWaterHandleTableUsage;
::boost::winapi::ULONG_ InvalidAttributes;
GENERIC_MAPPING_ GenericMapping;
::boost::winapi::ULONG_ ValidAccessMask;
::boost::winapi::BOOLEAN_ SecurityRequired;
::boost::winapi::BOOLEAN_ MaintainHandleCount;
::boost::winapi::UCHAR_ TypeIndex;
::boost::winapi::CHAR_ ReservedByte;
::boost::winapi::ULONG_ PoolType;
::boost::winapi::ULONG_ DefaultPagedPoolCharge;
::boost::winapi::ULONG_ DefaultNonPagedPoolCharge;
} OBJECT_TYPE_INFORMATION_, *POBJECT_TYPE_INFORMATION_;
/*
__kernel_entry NTSTATUS NtQuerySystemInformation(
IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength
);
*/
typedef ::boost::winapi::NTSTATUS_ (__kernel_entry *nt_system_query_information_p )(
SYSTEM_INFORMATION_CLASS_,
void *,
::boost::winapi::ULONG_,
::boost::winapi::PULONG_);
/*
__kernel_entry NTSYSCALLAPI NTSTATUS NtQueryObject(
HANDLE Handle,
OBJECT_INFORMATION_CLASS ObjectInformationClass,
PVOID ObjectInformation,
ULONG ObjectInformationLength,
PULONG ReturnLength
);
*/
typedef ::boost::winapi::NTSTATUS_ (__kernel_entry *nt_query_object_p )(
::boost::winapi::HANDLE_,
OBJECT_INFORMATION_CLASS_,
void *,
::boost::winapi::ULONG_,
::boost::winapi::PULONG_);
}
inline ::boost::winapi::NTSTATUS_ nt_system_query_information(
SYSTEM_INFORMATION_CLASS_ SystemInformationClass,
void *SystemInformation,
::boost::winapi::ULONG_ SystemInformationLength,
::boost::winapi::PULONG_ ReturnLength)
{
static ::boost::winapi::HMODULE_ h = ::boost::winapi::get_module_handle(L"Ntdll.dll");
static nt_system_query_information_p f = reinterpret_cast<nt_system_query_information_p>(::boost::winapi::get_proc_address(h, "NtQuerySystemInformation"));
return (*f)(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength);
}
inline ::boost::winapi::BOOL_ nt_query_object(
::boost::winapi::HANDLE_ Handle,
OBJECT_INFORMATION_CLASS_ ObjectInformationClass,
void *ObjectInformation,
::boost::winapi::ULONG_ ObjectInformationLength,
::boost::winapi::PULONG_ ReturnLength)
{
static ::boost::winapi::HMODULE_ h = ::boost::winapi::get_module_handle(L"Ntdll.dll");
static nt_query_object_p f = reinterpret_cast<nt_query_object_p>(::boost::winapi::get_proc_address(h, "NtQueryObject"));
return (*f)(Handle, ObjectInformationClass, ObjectInformation, ObjectInformationLength, ReturnLength);
}
#endif
}}}}}
#endif /* BOOST_PROCESS_DETAIL_WINDOWS_JOB_WORKAROUND_HPP_ */

View File

@@ -0,0 +1,176 @@
// Copyright (c) 2019 Klemens D. Morgenstern
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_PROCESS_DETAIL_WINDOWS_HANDLES_HPP_
#define BOOST_PROCESS_DETAIL_WINDOWS_HANDLES_HPP_
#include <vector>
#include <system_error>
#include <boost/process/detail/windows/handle_workaround.hpp>
#include <boost/process/detail/windows/handler.hpp>
#include <boost/winapi/get_current_process_id.hpp>
namespace boost { namespace process { namespace detail {
template<typename Executor, typename Function>
void foreach_used_handle(Executor &exec, Function &&func);
namespace windows {
using native_handle_type = ::boost::winapi::HANDLE_ ;
inline std::vector<native_handle_type> get_handles(std::error_code & ec)
{
auto pid = ::boost::winapi::GetCurrentProcessId();
std::vector<char> buffer(2048);
constexpr static auto STATUS_INFO_LENGTH_MISMATCH_ = static_cast<::boost::winapi::NTSTATUS_>(0xC0000004l);
auto info_pointer = reinterpret_cast<workaround::SYSTEM_HANDLE_INFORMATION_*>(buffer.data());
::boost::winapi::NTSTATUS_ nt_status = STATUS_INFO_LENGTH_MISMATCH_;
for (;
nt_status == STATUS_INFO_LENGTH_MISMATCH_;
nt_status = workaround::nt_system_query_information(
workaround::SystemHandleInformation_,
info_pointer, static_cast<::boost::winapi::ULONG_>(buffer.size()),
nullptr))
{
buffer.resize(buffer.size() * 2);
info_pointer = reinterpret_cast<workaround::SYSTEM_HANDLE_INFORMATION_*>(buffer.data());
}
if (nt_status < 0 || nt_status > 0x7FFFFFFF)
{
ec = ::boost::process::detail::get_last_error();
return {};
}
else
ec.clear();
std::vector<native_handle_type> res;
for (auto itr = info_pointer->Handle; itr != (info_pointer->Handle + info_pointer->Count); itr++)
{
if (itr->OwnerPid == pid)
res.push_back(reinterpret_cast<native_handle_type>(static_cast<std::uintptr_t>(itr->HandleValue)));
}
return res;
}
inline std::vector<native_handle_type> get_handles()
{
std::error_code ec;
auto res = get_handles(ec);
if (ec)
boost::process::detail::throw_error(ec, "NtQuerySystemInformation failed");
return res;
}
inline bool is_stream_handle(native_handle_type handle, std::error_code & ec)
{
::boost::winapi::ULONG_ actual_size;
auto nt_status = workaround::nt_query_object(
handle,
workaround::ObjectTypeInformation,
NULL,
0, &actual_size);
std::vector<char> vec;
vec.resize(actual_size);
workaround::OBJECT_TYPE_INFORMATION_ * type_info_p = reinterpret_cast<workaround::OBJECT_TYPE_INFORMATION_*>(vec.data());
nt_status = workaround::nt_query_object(
handle,
workaround::ObjectTypeInformation,
type_info_p,
actual_size, &actual_size);
if (nt_status < 0 || nt_status > 0x7FFFFFFF)
{
ec = ::boost::process::detail::get_last_error();
return false;
}
else
ec.clear();
auto &nm = type_info_p->TypeName.Buffer;
return type_info_p->TypeName.Length >= 5 &&
nm[0] == L'F' &&
nm[1] == L'i' &&
nm[2] == L'l' &&
nm[3] == L'e' &&
nm[4] == L'\0';
}
inline bool is_stream_handle(native_handle_type handle)
{
std::error_code ec;
auto res = is_stream_handle(handle, ec);
if (ec)
boost::process::detail::throw_error(ec, "NtQueryObject failed");
return res;
}
struct limit_handles_ : handler_base_ext
{
mutable std::vector<::boost::winapi::HANDLE_> handles_with_inherit_flag;
template<typename Executor>
void on_setup(Executor & exec) const
{
auto all_handles = get_handles();
foreach_used_handle(exec,
[&](::boost::winapi::HANDLE_ handle)
{
auto itr = std::find(all_handles.begin(), all_handles .end(), handle);
DWORD flags = 0u;
if (itr != all_handles.end())
*itr = ::boost::winapi::INVALID_HANDLE_VALUE_;
else if ((::boost::winapi::GetHandleInformation(*itr, &flags) != 0)
&&((flags & ::boost::winapi::HANDLE_FLAG_INHERIT_) == 0)) //it is NOT inherited anyhow, so ignore too
*itr = ::boost::winapi::INVALID_HANDLE_VALUE_;
});
auto part_itr = std::partition(all_handles.begin(), all_handles.end(),
[](::boost::winapi::HANDLE_ handle) {return handle != ::boost::winapi::INVALID_HANDLE_VALUE_;});
all_handles.erase(part_itr, all_handles.end()); //remove invalid handles
handles_with_inherit_flag = std::move(all_handles);
for (auto handle : handles_with_inherit_flag)
::boost::winapi::SetHandleInformation(handle, ::boost::winapi::HANDLE_FLAG_INHERIT_, 0);
}
template<typename Executor>
void on_error(Executor & exec, const std::error_code & ec) const
{
for (auto handle : handles_with_inherit_flag)
::boost::winapi::SetHandleInformation(handle, ::boost::winapi::HANDLE_FLAG_INHERIT_, ::boost::winapi::HANDLE_FLAG_INHERIT_);
}
template<typename Executor>
void on_sucess(Executor & exec) const
{
for (auto handle : handles_with_inherit_flag)
::boost::winapi::SetHandleInformation(handle, ::boost::winapi::HANDLE_FLAG_INHERIT_, ::boost::winapi::HANDLE_FLAG_INHERIT_);
}
};
}}}}
#endif //PROCESS_HANDLES_HPP

View File

@@ -14,14 +14,18 @@
#include <boost/winapi/handles.hpp>
#include <boost/winapi/handle_info.hpp>
#include <boost/process/detail/handler_base.hpp>
#include <boost/process/detail/used_handles.hpp>
#include <boost/process/detail/windows/file_descriptor.hpp>
namespace boost { namespace process { namespace detail { namespace windows {
struct null_in : public ::boost::process::detail::handler_base
struct null_in : public ::boost::process::detail::handler_base, ::boost::process::detail::uses_handles
{
file_descriptor source{"NUL", file_descriptor::read};
::boost::winapi::HANDLE_ get_used_handles() const { return source.handle(); }
public:
template <class WindowsExecutor>
void on_setup(WindowsExecutor &e) const

View File

@@ -14,15 +14,18 @@
#include <boost/winapi/handles.hpp>
#include <boost/winapi/handle_info.hpp>
#include <boost/process/detail/handler_base.hpp>
#include <boost/process/detail/used_handles.hpp>
#include <boost/process/detail/windows/file_descriptor.hpp>
namespace boost { namespace process { namespace detail { namespace windows {
template<int p1, int p2>
struct null_out : public ::boost::process::detail::handler_base
struct null_out : public ::boost::process::detail::handler_base, ::boost::process::detail::uses_handles
{
file_descriptor sink {"NUL", file_descriptor::write}; //works because it gets destroyed AFTER launch.
::boost::winapi::HANDLE_ get_used_handles() const { return sink.handle(); }
template <typename WindowsExecutor>
void on_setup(WindowsExecutor &e) const;
};

View File

@@ -12,14 +12,17 @@
#include <boost/winapi/process.hpp>
#include <boost/winapi/handles.hpp>
#include <boost/process/detail/used_handles.hpp>
#include <boost/process/detail/handler_base.hpp>
namespace boost { namespace process { namespace detail { namespace windows {
struct pipe_in : public ::boost::process::detail::handler_base
struct pipe_in : public ::boost::process::detail::handler_base, ::boost::process::detail::uses_handles
{
::boost::winapi::HANDLE_ handle;
::boost::winapi::HANDLE_ get_used_handles() const { return handle; }
pipe_in(::boost::winapi::HANDLE_ handle) : handle(handle) {}
template<typename T> //async_pipe

View File

@@ -13,6 +13,7 @@
#include <boost/winapi/process.hpp>
#include <boost/winapi/handles.hpp>
#include <boost/process/detail/used_handles.hpp>
#include <boost/process/detail/handler_base.hpp>
namespace boost { namespace process { namespace detail { namespace windows {
@@ -20,10 +21,12 @@ namespace boost { namespace process { namespace detail { namespace windows {
template<int p1, int p2>
struct pipe_out : public ::boost::process::detail::handler_base
struct pipe_out : public ::boost::process::detail::handler_base, ::boost::process::detail::uses_handles
{
::boost::winapi::HANDLE_ handle;
::boost::winapi::HANDLE_ get_used_handles() const { return handle; }
pipe_out(::boost::winapi::HANDLE_ handle) : handle(handle) {}
template<typename T>
pipe_out(T & p) : handle(p.native_sink())

View File

@@ -60,14 +60,14 @@ inline boost::filesystem::path search_path(
auto p = pp_ / filename;
for (boost::filesystem::path ext : extensions)
{
boost::filesystem::path pp = p;
pp += ext;
boost::filesystem::path pp_ext = p;
pp_ext += ext;
boost::system::error_code ec;
bool file = boost::filesystem::is_regular_file(pp, ec);
bool file = boost::filesystem::is_regular_file(pp_ext, ec);
if (!ec && file &&
::boost::winapi::sh_get_file_info(pp.native().c_str(), 0, 0, 0, ::boost::winapi::SHGFI_EXETYPE_))
::boost::winapi::sh_get_file_info(pp_ext.native().c_str(), 0, 0, 0, ::boost::winapi::SHGFI_EXETYPE_))
{
return pp;
return pp_ext;
}
}
}

View File

@@ -27,7 +27,7 @@ inline bool wait_impl(const group_handle & p, std::error_code & ec, std::chrono:
while (workaround::get_queued_completion_status(
p._io_port, &completion_code,
&completion_key, &overlapped, wait_time))
&completion_key, &overlapped, static_cast<::boost::winapi::DWORD_>(wait_time)))
{
if (reinterpret_cast<::boost::winapi::HANDLE_>(completion_key) == p._job_object &&
completion_code == workaround::JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO_)

View File

@@ -44,7 +44,7 @@ struct const_entry
bool operator()(char c) const {return c == api::env_seperator<char> ();}
} s;
boost::split(data, _data, s);
return std::move(data);
return data;
}
string_type to_string() const
{
@@ -677,11 +677,11 @@ inline std::vector<boost::filesystem::path> path()
#if defined(BOOST_WINDOWS_API)
const ::boost::process::wnative_environment ne{};
typedef typename ::boost::process::wnative_environment::const_entry_type value_type;
const auto id = L"PATH";
static constexpr auto id = L"PATH";
#else
const ::boost::process::native_environment ne{};
typedef typename ::boost::process::native_environment::const_entry_type value_type;
const auto id = "PATH";
static constexpr auto id = "PATH";
#endif
auto itr = std::find_if(ne.cbegin(), ne.cend(),

View File

@@ -59,7 +59,7 @@ namespace detail {
struct throw_on_error_ : ::boost::process::detail::api::handler_base_ext
{
constexpr throw_on_error_() {};
constexpr throw_on_error_() = default;
template <class Executor>
void on_error(Executor&, const std::error_code & ec) const
@@ -72,7 +72,7 @@ struct throw_on_error_ : ::boost::process::detail::api::handler_base_ext
struct ignore_error_ : ::boost::process::detail::api::handler_base_ext
{
constexpr ignore_error_() {};
constexpr ignore_error_() = default;
};
struct set_on_error : ::boost::process::detail::api::handler_base_ext
@@ -81,7 +81,7 @@ struct set_on_error : ::boost::process::detail::api::handler_base_ext
explicit set_on_error(std::error_code &ec) : ec_(ec) {}
template <class Executor>
void on_error(Executor&, const std::error_code & ec) const
void on_error(Executor&, const std::error_code & ec) const noexcept
{
ec_ = ec;
}
@@ -92,7 +92,7 @@ private:
struct error_
{
constexpr error_() {}
constexpr error_() = default;
set_on_error operator()(std::error_code &ec) const {return set_on_error(ec);}
set_on_error operator= (std::error_code &ec) const {return set_on_error(ec);}

View File

@@ -7,6 +7,7 @@
#define BOOST_PROCESS_EXTENSIONS_HPP_
#include <boost/process/detail/handler.hpp>
#include <boost/process/detail/used_handles.hpp>
#if defined(BOOST_WINDOWS_API)
#include <boost/process/detail/windows/executor.hpp>
@@ -62,6 +63,9 @@ using ::boost::process::detail::api::async_handler;
using ::boost::process::detail::get_io_context;
using ::boost::process::detail::get_last_error;
using ::boost::process::detail::throw_last_error;
using ::boost::process::detail::uses_handles;
using ::boost::process::detail::foreach_used_handle;
using ::boost::process::detail::get_used_handles;
///This handler is invoked before the process in launched, to setup parameters. The required signature is `void(Exec &)`, where `Exec` is a template parameter.
constexpr boost::process::detail::make_handler_t<boost::process::detail::on_setup_> on_setup;

View File

@@ -0,0 +1,107 @@
// Copyright (c) 2019 Klemens D. Morgenstern
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_PROCESS_HANDLES_HPP_
#define BOOST_PROCESS_HANDLES_HPP_
/**
* \file boost/process/handles.hpp
*
* Defines functions to obtain handles of the current process and limit the amount for inherited ones.
*/
#include <boost/process/detail/config.hpp>
#if defined(BOOST_POSIX_API)
#include <boost/process/detail/posix/handles.hpp>
#elif defined(BOOST_WINDOWS_API)
#include <boost/process/detail/windows/handles.hpp>
#endif
#include <boost/process/detail/used_handles.hpp>
namespace boost { namespace this_process
{
///The native type for handles
using native_handle_type = ::boost::process::detail::api::native_handle_type;
/**
* Get a snapshot of all handles of the process (i.e. file descriptors on posix and handles on windows) of the current process.
*
* \note This function might not work on certain posix systems.
*
* \note On Windows version older than windows 8 this function will iterate all the system handles, meaning it might be quite slow.
*
* \warning This functionality is utterly prone to race conditions, since other threads might open or close handles.
*
* \return The list of all open handles of the current process
*/
inline std::vector<native_handle_type> get_handles()
{
return ::boost::process::detail::api::get_handles();
}
/** \overload std::vector<native_handle_type> get_handles() */
inline std::vector<native_handle_type> get_handles(std::error_code &ec)
{
return ::boost::process::detail::api::get_handles(ec);
}
/** Determines if a given handle is a a stream-handle, i.e. any handle that can be used with read and write functions.
* Stream handles include pipes, regular files and sockets.
*
* \return Indicates if it's a stream handle.
*/
inline bool is_stream_handle(native_handle_type handle)
{
return ::boost::process::detail::api::is_stream_handle(handle);
}
/** \overload bool is_stream_handle(native_handle_type handle) */
inline bool is_stream_handle(native_handle_type handle, std::error_code &ec)
{
return ::boost::process::detail::api::is_stream_handle(handle, ec);
}
}
namespace process
{
namespace detail
{
using limit_handles_ = ::boost::process::detail::api::limit_handles_;
}
/**
* The limit_handles property sets all properties to be inherited only expcitly. It closes all unused file-descriptors on posix after the fork and
* removes the inherit flags on windows.
*
* \note This is executed after the fork on posix.
*
* \code{.cpp}
* system("gcc", limit_handles);
* \endcode
*
* Since limit also closes the standard handles unless they are explicitly redirected they can be ignored by `limit_handles` in the following way.
*
* \code{.cpp}
* system("gcc", limit_handles.allowStd())
* \endcode
*
*/
const static ::boost::process::detail::api::limit_handles_ limit_handles;
}
}
#endif //BOOST_PROCESS_HANDLES_HPP_

View File

@@ -122,13 +122,27 @@ system("b2", std_out > null);
namespace boost { namespace process { namespace detail {
struct null_t {constexpr null_t() {}};
template<typename T> using is_streambuf = typename std::is_same<T, boost::asio::streambuf>::type;
template<typename T> using is_const_buffer =
std::integral_constant<bool,
std::is_same< boost::asio::const_buffer, T>::value |
std::is_base_of<boost::asio::const_buffer, T>::value
>;
template<typename T> using is_mutable_buffer =
std::integral_constant<bool,
std::is_same< boost::asio::mutable_buffer, T>::value |
std::is_base_of<boost::asio::mutable_buffer, T>::value
>;
struct null_t {constexpr null_t() = default;};
struct close_t;
template<class>
struct std_in_
{
constexpr std_in_() {}
constexpr std_in_() = default;
api::close_in close() const {return api::close_in(); }
api::close_in operator=(const close_t &) const {return api::close_in();}
@@ -163,25 +177,19 @@ struct std_in_
api::async_pipe_in operator=(async_pipe & p) const {return p;}
api::async_pipe_in operator<(async_pipe & p) const {return p;}
template<typename T>
auto operator=(const T & buf) const -> typename std::enable_if<asio::is_const_buffer_sequence<T>::value, api::async_in_buffer<const T>>::type {return buf;}
template<typename T>
auto operator<(const T & buf) const -> typename std::enable_if<asio::is_const_buffer_sequence<T>::value, api::async_in_buffer<const T>>::type {return buf;}
template<typename T, typename = typename std::enable_if<
is_const_buffer<T>::value || is_mutable_buffer<T>::value
>::type>
api::async_in_buffer<const T> operator=(const T & buf) const {return buf;}
template<typename T, typename = typename std::enable_if<is_streambuf<T>::value>::type >
api::async_in_buffer<T> operator=(T & buf) const {return buf;}
template<typename T>
auto operator=(T & buf) const -> typename std::enable_if<asio::is_const_buffer_sequence<T>::value, api::async_in_buffer<T>>::type {return buf;}
template<typename T>
auto operator<(T & buf) const -> typename std::enable_if<asio::is_const_buffer_sequence<T>::value, api::async_in_buffer<T>>::type {return buf;}
template<typename Allocator>
api::async_in_buffer<asio::basic_streambuf<Allocator>> operator<(boost::asio::basic_streambuf<Allocator> & p) const {return p;}
template<typename Allocator>
api::async_in_buffer<asio::basic_streambuf<Allocator>> operator=(boost::asio::basic_streambuf<Allocator> & p) const {return p;}
template<typename Allocator>
api::async_in_buffer<asio::basic_streambuf<Allocator>> operator<(const boost::asio::basic_streambuf<Allocator> & p) const {return p;}
template<typename Allocator>
api::async_in_buffer<asio::basic_streambuf<Allocator>> operator=(const boost::asio::basic_streambuf<Allocator> & p) const {return p;}
template<typename T, typename = typename std::enable_if<
is_const_buffer<T>::value || is_mutable_buffer<T>::value
>::type>
api::async_in_buffer<const T> operator<(const T & buf) const {return buf;}
template<typename T, typename = typename std::enable_if<is_streambuf<T>::value>::type >
api::async_in_buffer<T> operator<(T & buf) const {return buf;}
};
@@ -191,7 +199,7 @@ struct std_in_
template<int p1, int p2 = -1>
struct std_out_
{
constexpr std_out_() {}
constexpr std_out_() = default;
api::close_out<p1,p2> close() const {return api::close_out<p1,p2>(); }
api::close_out<p1,p2> operator=(const close_t &) const {return api::close_out<p1,p2>();}
@@ -226,24 +234,13 @@ struct std_out_
api::async_pipe_out<p1, p2> operator=(async_pipe & p) const {return p;}
api::async_pipe_out<p1, p2> operator>(async_pipe & p) const {return p;}
template<typename Allocator>
api::async_out_buffer<p1, p2, asio::basic_streambuf<Allocator>> operator=(boost::asio::basic_streambuf<Allocator> & p) const {return p;}
template<typename Allocator>
api::async_out_buffer<p1, p2, asio::basic_streambuf<Allocator>> operator>(boost::asio::basic_streambuf<Allocator> & p) const {return p;}
api::async_out_buffer<p1, p2, const asio::mutable_buffer> operator=(const asio::mutable_buffer & buf) const {return buf;}
api::async_out_buffer<p1, p2, const asio::mutable_buffers_1> operator=(const asio::mutable_buffers_1 & buf) const {return buf;}
api::async_out_buffer<p1, p2, asio::streambuf> operator=(asio::streambuf & os) const {return os ;}
template<typename Buffer>
auto operator=(const Buffer & buf) const
-> typename std::enable_if<asio::is_mutable_buffer_sequence<Buffer>::value, api::async_out_buffer<p1, p2, Buffer>>::type
{
return buf;
}
template<typename Buffer>
auto operator>(const Buffer & buf) const
-> typename std::enable_if<asio::is_mutable_buffer_sequence<Buffer>::value, api::async_out_buffer<p1, p2, Buffer>>::type
{
return buf;
}
api::async_out_buffer<p1, p2, const asio::mutable_buffer> operator>(const asio::mutable_buffer & buf) const {return buf;}
api::async_out_buffer<p1, p2, const asio::mutable_buffers_1> operator>(const asio::mutable_buffers_1 & buf) const {return buf;}
api::async_out_buffer<p1, p2, asio::streambuf> operator>(asio::streambuf & os) const {return os ;}
api::async_out_future<p1,p2, std::string> operator=(std::future<std::string> & fut) const { return fut;}
api::async_out_future<p1,p2, std::string> operator>(std::future<std::string> & fut) const { return fut;}
@@ -263,7 +260,7 @@ struct std_out_
struct close_t
{
constexpr close_t() {}
constexpr close_t() = default;
template<int T, int U>
api::close_out<T,U> operator()(std_out_<T,U>) {return api::close_out<T,U>();}
};

View File

@@ -28,7 +28,7 @@ namespace detail
class codecvt_category_t : public std::error_category
{
public:
codecvt_category_t(){}
codecvt_category_t() = default;
const char* name() const noexcept override {return "codecvt";}
std::string message(int ev) const override
{

View File

@@ -120,6 +120,13 @@ struct basic_pipebuf : std::basic_streambuf<CharT, Traits>
///Move Constructor
basic_pipebuf(basic_pipebuf && ) = default;
///Destructor -> writes the frest of the data
~basic_pipebuf()
{
if (basic_pipebuf::is_open())
basic_pipebuf::overflow(Traits::eof());
}
///Move construct from a pipe.
basic_pipebuf(pipe_type && p) : _pipe(std::move(p)),
_write(default_buffer_size),
@@ -155,15 +162,17 @@ struct basic_pipebuf : std::basic_streambuf<CharT, Traits>
///Writes characters to the associated output sequence from the put area
int_type overflow(int_type ch = traits_type::eof()) override
{
if ((ch != traits_type::eof()) && _pipe.is_open())
if (_pipe.is_open() && (ch != traits_type::eof()))
{
if (this->pptr() == this->epptr())
{
bool wr = this->_write_impl();
*this->pptr() = ch;
this->pbump(1);
if (wr)
{
*this->pptr() = ch;
this->pbump(1);
return ch;
}
}
else
{
@@ -173,6 +182,9 @@ struct basic_pipebuf : std::basic_streambuf<CharT, Traits>
return ch;
}
}
else if (ch == traits_type::eof())
this->sync();
return traits_type::eof();
}
///Synchronizes the buffers with the associated character sequence
@@ -212,6 +224,36 @@ struct basic_pipebuf : std::basic_streambuf<CharT, Traits>
const pipe_type &pipe() const & {return _pipe;}
///Get a rvalue reference to the pipe. Qualified as rvalue.
pipe_type && pipe() && {return std::move(_pipe);}
///Check if the pipe is open
bool is_open() const {return _pipe.is_open(); }
///Open a new pipe
basic_pipebuf<CharT, Traits>* open()
{
if (is_open())
return nullptr;
_pipe = pipe();
return this;
}
///Open a new named pipe
basic_pipebuf<CharT, Traits>* open(const std::string & name)
{
if (is_open())
return nullptr;
_pipe = pipe(name);
return this;
}
///Flush the buffer & close the pipe
basic_pipebuf<CharT, Traits>* close()
{
if (!is_open())
return nullptr;
overflow(Traits::eof());
return this;
}
private:
pipe_type _pipe;
std::vector<char_type> _write;
@@ -223,8 +265,13 @@ private:
return false;
auto base = this->pbase();
if (base == this->pptr())
return true;
std::ptrdiff_t wrt = _pipe.write(base,
static_cast<typename pipe_type::int_type>(this->pptr() - base));
std::ptrdiff_t diff = this->pptr() - base;
if (wrt < diff)
@@ -295,8 +342,9 @@ public:
basic_ipstream& operator=(basic_ipstream && lhs)
{
std::basic_istream<CharT, Traits>::operator=(std::move(lhs));
_buf = std::move(lhs);
_buf = std::move(lhs._buf);
std::basic_istream<CharT, Traits>::rdbuf(&_buf);
return *this;
};
///Move assignment of a pipe.
basic_ipstream& operator=(pipe_type && p)
@@ -320,6 +368,33 @@ public:
const pipe_type &pipe() const & {return _buf.pipe();}
///Get a rvalue reference to the pipe. Qualified as rvalue.
pipe_type && pipe() && {return std::move(_buf).pipe();}
///Check if the pipe is open
bool is_open() const {return _buf.is_open();}
///Open a new pipe
void open()
{
if (_buf.open() == nullptr)
this->setstate(std::ios_base::failbit);
else
this->clear();
}
///Open a new named pipe
void open(const std::string & name)
{
if (_buf.open() == nullptr)
this->setstate(std::ios_base::failbit);
else
this->clear();
}
///Flush the buffer & close the pipe
void close()
{
if (_buf.close() == nullptr)
this->setstate(std::ios_base::failbit);
}
};
typedef basic_ipstream<char> ipstream;
@@ -376,8 +451,9 @@ public:
basic_opstream& operator=(basic_opstream && lhs)
{
std::basic_ostream<CharT, Traits>::operator=(std::move(lhs));
_buf = std::move(lhs);
_buf = std::move(lhs._buf);
std::basic_ostream<CharT, Traits>::rdbuf(&_buf);
return *this;
};
///Move assignment of a pipe.
@@ -402,6 +478,31 @@ public:
const pipe_type &pipe() const & {return _buf.pipe();}
///Get a rvalue reference to the pipe. Qualified as rvalue.
pipe_type && pipe() && {return std::move(_buf).pipe();}
///Open a new pipe
void open()
{
if (_buf.open() == nullptr)
this->setstate(std::ios_base::failbit);
else
this->clear();
}
///Open a new named pipe
void open(const std::string & name)
{
if (_buf.open() == nullptr)
this->setstate(std::ios_base::failbit);
else
this->clear();
}
///Flush the buffer & close the pipe
void close()
{
if (_buf.close() == nullptr)
this->setstate(std::ios_base::failbit);
}
};
typedef basic_opstream<char> opstream;
@@ -459,8 +560,9 @@ public:
basic_pstream& operator=(basic_pstream && lhs)
{
std::basic_istream<CharT, Traits>::operator=(std::move(lhs));
_buf = std::move(lhs);
_buf = std::move(lhs._buf);
std::basic_iostream<CharT, Traits>::rdbuf(&_buf);
return *this;
};
///Move assignment of a pipe.
basic_pstream& operator=(pipe_type && p)
@@ -484,6 +586,31 @@ public:
const pipe_type &pipe() const & {return _buf.pipe();}
///Get a rvalue reference to the pipe. Qualified as rvalue.
pipe_type && pipe() && {return std::move(_buf).pipe();}
///Open a new pipe
void open()
{
if (_buf.open() == nullptr)
this->setstate(std::ios_base::failbit);
else
this->clear();
}
///Open a new named pipe
void open(const std::string & name)
{
if (_buf.open() == nullptr)
this->setstate(std::ios_base::failbit);
else
this->clear();
}
///Flush the buffer & close the pipe
void close()
{
if (_buf.close() == nullptr)
this->setstate(std::ios_base::failbit);
}
};
typedef basic_pstream<char> pstream;

View File

@@ -16,6 +16,7 @@ if [ os.name ] = NT
lib ws2_32 ;
lib shell32 ;
lib Advapi32 ;
lib Ntdll ;
}
project : requirements
@@ -44,7 +45,7 @@ alias coroutine : /boost//coroutine : <link>static ;
lib multi_ref : multi_ref1.cpp multi_ref2.cpp system : <target-os>windows:<source>shell32 ;
exe sparring_partner : sparring_partner.cpp program_options system filesystem iostreams :
<warnings>off <target-os>windows:<source>shell32
<warnings>off <target-os>windows:<source>shell32 <target-os>windows:<source>Ntdll
;
exe exit_argc : exit_argc.cpp :
@@ -55,8 +56,15 @@ exe sub_launch : sub_launcher.cpp program_options iostreams system filesystem :
rule test-options ( name )
{
return --log_sink=log_$(name).xml --log_format=XML --log_level=error --report_sink=report_$(name).xml --report_format=XML --report_level=detailed -- ;
#return --log_level=error --report_level=detailed -- ;
if "--boost-process-report-ci" in [ modules.peek : ARGV ]
{
return --log_sink=log_$(name).xml --log_format=XML --log_level=error --report_sink=report_$(name).xml --report_format=XML --report_level=detailed -- ;
}
else
{
return --log_level=error --report_level=detailed -- ;
}
}
@@ -93,6 +101,7 @@ test-suite with-valgrind :
[ run group.cpp system thread filesystem : [ test-options group ] : sub_launch ]
[ run group.cpp system thread filesystem : [ test-options group ] : sub_launch : <build>no <target-os>windows:<build>yes <define>BOOST_USE_WINDOWS_H=1 : group-windows-h ]
[ run group_wait.cpp system thread filesystem : [ test-options group_wait ] : sparring_partner ]
[ run limit_fd.cpp program_options system filesystem : [ test-options limit_fd ] : sparring_partner ]
[ run run_exe.cpp filesystem : : sparring_partner ]
[ run run_exe_path.cpp filesystem : [ test-options run_exe_path ] : sparring_partner ]
[ run search_path.cpp filesystem system : [ test-options search_path ] : : <target-os>windows:<source>shell32 ]

View File

@@ -67,11 +67,11 @@ build_script:
after_build:
before_test:
test_script:
- ..\..\..\b2.exe address-model=64 architecture=x86 cxxflags="-DBOOST_TRAVISCI_BUILD" -sBOOST_BUILD_PATH=.
- ..\..\..\b2.exe address-model=64 architecture=x86 cxxflags="-DBOOST_TRAVISCI_BUILD" -sBOOST_BUILD_PATH=. --boost-process-report-ci
after_test:
on_success:
on_failure:
on_finish:
- curl -s https://report.ci/upload.py | python - --name "windows test run" --root_dir=%BOOST%/libs/%PROJECT_TO_TEST%/test
- curl -s https://report.ci/upload.py | python - --name "Windows test run" --root_dir=%BOOST%

View File

@@ -293,7 +293,7 @@ BOOST_AUTO_TEST_CASE(async_out_stream, *boost::unit_test::timeout(5))
std::string line;
std::getline(istr, line);
BOOST_REQUIRE_GE(line.size(), 3);
BOOST_REQUIRE_GE(line.size(), 3u);
BOOST_CHECK(boost::algorithm::starts_with(line, "abc"));
c.wait();
}
@@ -413,4 +413,4 @@ BOOST_AUTO_TEST_CASE(mixed_async, *boost::unit_test::timeout(5))
}*/
BOOST_AUTO_TEST_SUITE_END();
BOOST_AUTO_TEST_SUITE_END();

View File

@@ -96,8 +96,13 @@ BOOST_AUTO_TEST_CASE(move_pipe)
bp::async_pipe ap{ios};
BOOST_TEST_CHECKPOINT("First move");
bp::async_pipe ap2{std::move(ap)};
#if defined(BOOST_WINDOWS_API)
BOOST_CHECK_EQUAL(ap.native_source(), ::boost::winapi::INVALID_HANDLE_VALUE_);
BOOST_CHECK_EQUAL(ap.native_sink (), ::boost::winapi::INVALID_HANDLE_VALUE_);
#elif defined(BOOST_POSIX_API)
BOOST_CHECK_EQUAL(ap.native_source(), -1);
BOOST_CHECK_EQUAL(ap.native_sink (), -1);
#endif
BOOST_TEST_CHECKPOINT("Second move");
ap = std::move(ap2);

View File

@@ -57,8 +57,7 @@ BOOST_AUTO_TEST_CASE(future_error, *boost::unit_test::timeout(15))
ios.run();
int exit_code = 0;
BOOST_CHECK_THROW(exit_code = fut.get(), boost::system::system_error);
BOOST_CHECK_THROW(fut.get(), boost::system::system_error);
}
BOOST_AUTO_TEST_SUITE_END();
BOOST_AUTO_TEST_SUITE_END();

View File

@@ -41,9 +41,9 @@ BOOST_AUTO_TEST_CASE(stackful, *boost::unit_test::timeout(15))
bp::async_system(
ios, yield_,
master_test_suite().argv[1],
"test", "--exit-code", "123");
"test", "--wait", "1");
BOOST_CHECK_EQUAL(ret, 123);
BOOST_CHECK_EQUAL(ret, 0);
BOOST_CHECK(did_something_else);
};

View File

@@ -53,7 +53,7 @@ BOOST_AUTO_TEST_CASE(stackless, *boost::unit_test::timeout(15))
master_test_suite().argv[1],
"test", "--exit-code", "42");
BOOST_CHECK_EQUAL(exit_code, 42);
BOOST_CHECK_EQUAL(exit_code, 42u);
BOOST_CHECK(did_something_else);
}
}
@@ -68,4 +68,4 @@ BOOST_AUTO_TEST_CASE(stackless, *boost::unit_test::timeout(15))
}
BOOST_AUTO_TEST_SUITE_END();
BOOST_AUTO_TEST_SUITE_END();

View File

@@ -118,6 +118,29 @@ BOOST_AUTO_TEST_CASE(compare, *boost::unit_test::timeout(5))
BOOST_TEST_PASSPOINT();
}
BOOST_AUTO_TEST_CASE(wcompare, *boost::unit_test::timeout(5))
{
auto nat = boost::this_process::wenvironment();
bp::wenvironment env = nat;
{
BOOST_CHECK_EQUAL(nat.size(), env.size());
auto ni = nat.begin();
auto ei = env.begin();
while ((ni != nat.end()) &&(ei != env.end()))
{
BOOST_CHECK_EQUAL(ni->get_name(), ei->get_name());
BOOST_CHECK_EQUAL(ni->to_string(), ei->to_string());
ni++; ei++;
}
}
BOOST_TEST_PASSPOINT();
env.clear();
BOOST_TEST_PASSPOINT();
}
BOOST_AUTO_TEST_CASE(insert_remove, *boost::unit_test::timeout(5))
{
bp::environment env(boost::this_process::environment());

View File

@@ -29,7 +29,6 @@ struct run_exe
{
e.exe = exe.c_str();
}
};
struct set_on_error
@@ -74,13 +73,11 @@ struct overload_handler : ex::handler
void on_setup(ex::windows_executor<Char, Sequence>& exec) const
{
st = "windows";
const char* env = exec.env;
}
template <class Sequence>
void on_setup(ex::posix_executor<Sequence>& exec) const
{
st = "posix";
char** env = exec.env;
}
};

View File

@@ -58,10 +58,15 @@ BOOST_AUTO_TEST_CASE(group_test, *boost::unit_test::timeout(5))
BOOST_CHECK(!c.running());
if (c.running())
c.terminate();
std::cout << "group_test out" << std::endl;
}
BOOST_AUTO_TEST_CASE(attached, *boost::unit_test::timeout(5))
{
std::cout << "attached" << std::endl;
using boost::unit_test::framework::master_test_suite;
bp::ipstream is;
@@ -89,10 +94,11 @@ BOOST_AUTO_TEST_CASE(attached, *boost::unit_test::timeout(5))
BOOST_REQUIRE(sub_c);
std::this_thread::sleep_for(std::chrono::milliseconds(50)); //just to be sure.
std::this_thread::sleep_for(std::chrono::milliseconds(100)); //just to be sure.
#if defined( BOOST_POSIX_API )
::waitpid(sub_c.id(), nullptr, WNOHANG);
BOOST_CHECK(kill(sub_c.id(), 0) == 0);
#else
BOOST_CHECK(sub_c.running());
@@ -101,23 +107,27 @@ BOOST_AUTO_TEST_CASE(attached, *boost::unit_test::timeout(5))
BOOST_REQUIRE_NO_THROW(g.terminate());
BOOST_CHECK(sub_c);
std::this_thread::sleep_for(std::chrono::milliseconds(50)); //just to be sure.
std::this_thread::sleep_for(std::chrono::milliseconds(100)); //just to be sure.
BOOST_CHECK(!c.running());
#if defined( BOOST_POSIX_API )
bool still_runs = kill(sub_c.id(), 0) == 0;
#else
errno = 0;
::waitpid(sub_c.id(), nullptr, WNOHANG);
bool still_runs = (kill(sub_c.id(), 0) == 0) && (errno != ECHILD) && (errno != ESRCH);
#else
bool still_runs = sub_c.running();
#endif
BOOST_CHECK_MESSAGE(!still_runs, boost::process::detail::get_last_error().message());
BOOST_CHECK(!still_runs);
if (still_runs)
sub_c.terminate();
BOOST_CHECK(!c.running());
if (c.running())
c.terminate();
std::cout << "attached out" << std::endl;
}
@@ -176,4 +186,6 @@ BOOST_AUTO_TEST_CASE(detached, *boost::unit_test::timeout(5))
BOOST_CHECK(!c.running());
if (c.running())
c.terminate();
std::cerr << "detached out" << std::endl;
}

View File

@@ -10,7 +10,7 @@
#define BOOST_TEST_MAIN
#define BOOST_TEST_IGNORE_SIGCHLD
#include <boost/test/included/unit_test.hpp>
#include <fstream>
#include <boost/system/error_code.hpp>
#include <boost/asio.hpp>
@@ -107,7 +107,7 @@ BOOST_AUTO_TEST_CASE(wait_group_test_timeout, *boost::unit_test::timeout(15))
bp::child c2(
master_test_suite().argv[1],
"--wait", "3",
"--wait", "4",
g,
ec
);

180
test/limit_fd.cpp Normal file
View File

@@ -0,0 +1,180 @@
// Copyright (c) 2019 Klemens D. Morgenstern
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#define BOOST_TEST_MAIN
#define BOOST_TEST_IGNORE_SIGCHLD
#include <boost/test/included/unit_test.hpp>
#include <iostream>
#include <boost/process.hpp>
#include <boost/process/handles.hpp>
#include <boost/process/pipe.hpp>
#include <boost/process/io.hpp>
#include <boost/process/async_pipe.hpp>
#include <boost/process/extend.hpp>
#include <boost/filesystem.hpp>
#include <system_error>
#include <string>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ip/udp.hpp>
#if defined(BOOST_WINDOWS_API)
#include <boost/winapi/get_current_thread.hpp>
#include <boost/winapi/get_current_process.hpp>
#endif
namespace fs = boost::filesystem;
namespace bp = boost::process;
namespace bt = boost::this_process;
BOOST_AUTO_TEST_CASE(leak_test, *boost::unit_test::timeout(5))
{
using boost::unit_test::framework::master_test_suite;
#if defined(BOOST_WINDOWS_API)
const auto get_handle = [](FILE * f) {return reinterpret_cast<bt::native_handle_type>(_get_osfhandle(_fileno(f)));};
const auto socket_to_handle = [](::boost::winapi::UINT_PTR_ sock){return reinterpret_cast<::boost::winapi::HANDLE_>(sock);};
#else
const auto get_handle = [](FILE * f) {return fileno(f);};
const auto socket_to_handle = [](int i){ return i;};
#endif
std::error_code ec;
auto fd_list = bt::get_handles(ec);
BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), get_handle(stdin)), 1);
BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), get_handle(stdout)), 1);
BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), get_handle(stderr)), 1);
BOOST_CHECK(bt::is_stream_handle(get_handle(stdin), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
BOOST_CHECK(bt::is_stream_handle(get_handle(stdout), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
BOOST_CHECK(bt::is_stream_handle(get_handle(stderr), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
BOOST_CHECK_GE(fd_list.size(), 3u);
BOOST_CHECK_GE(bt::get_handles(ec).size(), fd_list.size());
bp::pipe p;
{
auto fd_list_new = bt::get_handles(ec);
BOOST_CHECK_MESSAGE(!ec, ec);
BOOST_CHECK_LE(fd_list.size() + 2u, fd_list_new.size());
fd_list = std::move(fd_list_new);
}
BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_source()), 1u);
BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_sink()), 1u);
BOOST_CHECK(bt::is_stream_handle(p.native_source(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
BOOST_CHECK(bt::is_stream_handle(p.native_sink(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
p.close();
fd_list = bt::get_handles(ec);
BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_source()), 0u);
BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_sink()), 0u);
#if defined( BOOST_WINDOWS_API )
std::thread thr([]{});
BOOST_CHECK(!bt::is_stream_handle(thr.native_handle(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
thr.join();
#else
# if defined(TFD_CLOEXEC) //check timer
int timer_fd = ::timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
BOOST_CHECK(!bt::is_stream_handle(timer_fd , ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
#endif
# if defined(EFD_CLOEXEC) && defined(EFD_NONBLOCK)
int event_fd =::eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
BOOST_CHECK(!bt::is_stream_handle(event_fd , ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
#endif
int dir_fd = ::dirfd(::opendir("."));
BOOST_CHECK(!bt::is_stream_handle(dir_fd , ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
#endif
boost::asio::io_context ioc;
boost::asio::ip::tcp::socket tcp_socket(ioc);
boost::asio::ip::udp::socket udp_socket(ioc);
bp::async_pipe ap(ioc);
tcp_socket.open(boost::asio::ip::tcp::v4());
udp_socket.open(boost::asio::ip::udp::v4());
BOOST_CHECK(bt::is_stream_handle(socket_to_handle(tcp_socket.native_handle()), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
BOOST_CHECK(bt::is_stream_handle(socket_to_handle(udp_socket.native_handle()), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
BOOST_CHECK(bt::is_stream_handle(std::move(ap).sink(). native_handle(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
BOOST_CHECK(bt::is_stream_handle(std::move(ap).source().native_handle(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
}
struct on_setup_t
{
std::vector<bt::native_handle_type> &res;
on_setup_t(std::vector<bt::native_handle_type> & res) : res(res) {}
template<typename Executor>
void operator()(Executor & e)
{
bp::extend::foreach_used_handle(e, [this](bt::native_handle_type handle)
{
res.push_back(handle);
});
}
};
BOOST_AUTO_TEST_CASE(iterate_handles, *boost::unit_test::timeout(5))
{
using boost::unit_test::framework::master_test_suite;
std::vector<bt::native_handle_type> res;
bp::pipe p_in;
bp::pipe p_out;
auto source = p_in.native_source();
auto sink = p_out.native_sink();
std::error_code ec;
BOOST_WARN_NE(source, sink); //Sanity check
const auto ret = bp::system(master_test_suite().argv[1], "--exit-code" , "42",
bp::std_in < p_out,
bp::std_out > p_in,
bp::extend::on_setup(on_setup_t(res)), ec);
BOOST_CHECK_MESSAGE(!ec, ec.message());
BOOST_CHECK_EQUAL(ret, 42u);
BOOST_CHECK_EQUAL(std::count(res.begin(), res.end(), p_in. native_sink()), 0u);
BOOST_CHECK_EQUAL(std::count(res.begin(), res.end(), p_out.native_source()), 0u);
}
BOOST_AUTO_TEST_CASE(limit_fd, *boost::unit_test::timeout(5))
{
#if defined(BOOST_WINDOWS_API)
const auto get_handle = [](FILE * f){return std::to_string(_get_osfhandle(_fileno(f)));};
#else
const auto get_handle = [](FILE * f){return std::to_string(fileno(f));};
#endif
using boost::unit_test::framework::master_test_suite;
BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stdin), bp::std_err > stderr), EXIT_SUCCESS);
BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stderr), bp::std_err > stderr), EXIT_SUCCESS);
BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stdin), bp::std_err > stderr, bp::limit_handles), EXIT_FAILURE);
BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stderr), bp::std_err > stderr, bp::limit_handles), EXIT_SUCCESS);
}

View File

@@ -102,6 +102,110 @@ BOOST_AUTO_TEST_CASE(stream, *boost::unit_test::timeout(2))
BOOST_CHECK_EQUAL(i, j);
}
BOOST_AUTO_TEST_CASE(stream_move, *boost::unit_test::timeout(2))
{
bp::pipe pipe;
bp::pstream os(pipe);
bp::ipstream is(pipe);
int i = 42, j = 0, k = 0;
os << i << std::endl;
os << std::endl;
is >> j;
BOOST_CHECK_EQUAL(i, j);
bp::pstream os2 = std::move(os);
bp::ipstream is2 = std::move(is);
os2 << i << std::endl;
os2 << std::endl;
is2 >> k;
BOOST_CHECK_EQUAL(i, k);
}
BOOST_AUTO_TEST_CASE(ostream_move, *boost::unit_test::timeout(2))
{
bp::pipe pipe;
bp::opstream os(pipe);
bp::ipstream is(pipe);
int i = 42, j = 0, k = 0;
os << i << std::endl;
os << std::endl;
is >> j;
BOOST_CHECK_EQUAL(i, j);
bp::opstream os2 = std::move(os);
bp::ipstream is2 = std::move(is);
os2 << i << std::endl;
os2 << std::endl;
is2 >> k;
BOOST_CHECK_EQUAL(i, k);
}
BOOST_AUTO_TEST_CASE(stream_move_assignment, *boost::unit_test::timeout(2))
{
bp::pipe pipe;
bp::pstream os(pipe);
bp::ipstream is(pipe);
int i = 42, j = 0, k = 0;
os << i << std::endl;
os << std::endl;
is >> j;
BOOST_CHECK_EQUAL(i, j);
bp::pstream os2;
os2 = std::move(os);
bp::ipstream is2;
is2 = std::move(is);
os2 << i << std::endl;
os2 << std::endl;
is2 >> k;
BOOST_CHECK_EQUAL(i, k);
}
BOOST_AUTO_TEST_CASE(ostream_move_assignment, *boost::unit_test::timeout(2))
{
bp::pipe pipe;
bp::opstream os(pipe);
bp::ipstream is(pipe);
int i = 42, j = 0, k = 0;
os << i << std::endl;
os << std::endl;
is >> j;
BOOST_CHECK_EQUAL(i, j);
bp::opstream os2;
os2 = std::move(os);
bp::ipstream is2;
is2 = std::move(is);
os2 << i << std::endl;
os2 << std::endl;
is2 >> k;
BOOST_CHECK_EQUAL(i, k);
}
BOOST_AUTO_TEST_CASE(stream_line, *boost::unit_test::timeout(2))
{
@@ -235,4 +339,37 @@ BOOST_AUTO_TEST_CASE(coverage, *boost::unit_test::timeout(5))
}
}
BOOST_AUTO_TEST_SUITE_END();
BOOST_AUTO_TEST_CASE(stream_close, *boost::unit_test::timeout(5))
{
bp::pipe p;
int i = 1234, j = 0;
bp::opstream op{p};
bp::ipstream ip{p};
p.close();
op << i << " ";
op.close();
ip >> j;
BOOST_CHECK_EQUAL(i, j);
}
BOOST_AUTO_TEST_CASE(stream_close_scope, *boost::unit_test::timeout(5))
{
bp::pipe p;
int i = 1234, j = 0;
bp::ipstream ip;
{
bp::opstream op{ip.pipe()};
op << i << " ";
}
ip >> j;
BOOST_CHECK_EQUAL(i, j);
}
BOOST_AUTO_TEST_SUITE_END();

View File

@@ -17,6 +17,7 @@
#include <boost/range/algorithm_ext/push_back.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/process/environment.hpp>
#include <boost/process/handles.hpp>
#include <vector>
#include <string>
#include <iterator>
@@ -57,6 +58,7 @@ int main(int argc, char *argv[])
("pwd", bool_switch())
("query", value<std::string>())
("stdin-to-stdout", bool_switch())
("has-handle", value<std::uintptr_t>())
#if defined(BOOST_POSIX_API)
("posix-echo-one", value<std::vector<std::string> >()->multitoken())
("posix-echo-two", value<std::vector<std::string> >()->multitoken());
@@ -223,5 +225,15 @@ int main(int argc, char *argv[])
std::cout << si.dwFlags << std::endl;
}
#endif
else if (vm.count("has-handle"))
{
#if defined(BOOST_WINDOWS_API)
const auto handle = reinterpret_cast<boost::this_process::native_handle_type>(vm["has-handle"].as<std::uintptr_t>());
#else
const auto handle = static_cast<boost::this_process::native_handle_type>(vm["has-handle"].as<std::uintptr_t>());
#endif
auto all_handles = boost::this_process::get_handles();
return (std::find(all_handles.begin(), all_handles.end(), handle) != all_handles.end()) ? EXIT_SUCCESS : EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@@ -111,4 +111,26 @@ BOOST_AUTO_TEST_CASE(wait_until_ec)
BOOST_CHECK_MESSAGE(!ec, ec.message());
}
BOOST_AUTO_TEST_CASE(wait_for_exit_before_timeout)
{
using boost::unit_test::framework::master_test_suite;
std::error_code ec;
auto launch_time = std::chrono::system_clock::now();
bp::child c(
master_test_suite().argv[1],
bp::args+={"test", "--wait", "1"},
ec
);
BOOST_REQUIRE(!ec);
BOOST_CHECK(c.wait_for(std::chrono::seconds(20)));
auto timeout_t = std::chrono::system_clock::now();
// check that we didn't wait the entire timeout period
BOOST_CHECK_LT(std::chrono::duration_cast<std::chrono::seconds>(timeout_t - launch_time).count(), 20);
}
BOOST_AUTO_TEST_SUITE_END();

View File

@@ -33,7 +33,7 @@ BOOST_AUTO_TEST_CASE(wargs, *boost::unit_test::timeout(2))
std::error_code ec;
bp::child c(
master_test_suite().argv[1],
L"test", "--echo-argv", L"hello thingy", "\"stuff\"", static_cast<const wchar_t*>(L" spa ce "),
L"test", "--echo-argv", L"hello thingy", "\"stuff\"", static_cast<const wchar_t*>(L" spa\" ce "),
bp::std_out>is,
ec
);
@@ -62,9 +62,9 @@ BOOST_AUTO_TEST_CASE(wargs, *boost::unit_test::timeout(2))
BOOST_CHECK_EQUAL(s, "\"stuff\"");
std::getline(is, s);
s.resize(10);
s.resize(11);
BOOST_CHECK_EQUAL(s, " spa ce ");
BOOST_CHECK_EQUAL(s, " spa\" ce ");
}