Merge lp:~zorba-coders/zorba/process-2 into lp:zorba/process-module
- process-2
- Merge into process-module
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Nicolae Brinza | ||||
Approved revision: | 50 | ||||
Merged at revision: | 35 | ||||
Proposed branch: | lp:~zorba-coders/zorba/process-2 | ||||
Merge into: | lp:zorba/process-module | ||||
Diff against target: |
1667 lines (+1131/-177) 25 files modified
src/CMakeLists.txt (+3/-2) src/com/CMakeLists.txt (+0/-14) src/com/zorba-xquery/CMakeLists.txt (+0/-14) src/com/zorba-xquery/www/CMakeLists.txt (+0/-14) src/process-1.xq (+95/-0) src/process-1.xq.src/process.cpp (+577/-0) src/process-1.xq.src/process.h (+105/-0) src/process-2.xq (+149/-45) src/process-2.xq.src/process.cpp (+122/-73) src/process-2.xq.src/process.h (+9/-6) test/ExpQueryResults/process1-01.xml.res (+2/-0) test/ExpQueryResults/process2-01.xml.res (+1/-0) test/ExpQueryResults/process2-02.xml.res (+1/-0) test/ExpQueryResults/process2-03.xml.res (+1/-0) test/ExpQueryResults/process2-04.xml.res (+1/-0) test/ExpQueryResults/process2-05.xml.res (+1/-0) test/ExpQueryResults/process2-06.xml.res (+1/-0) test/Queries/process1-01.xq (+24/-0) test/Queries/process2-01.xq (+5/-0) test/Queries/process2-02.xq (+5/-0) test/Queries/process2-03.xq (+5/-0) test/Queries/process2-04.xq (+6/-0) test/Queries/process2-05.xq (+5/-0) test/Queries/process2-06.xq (+4/-0) test/Queries/process2-07.xq (+9/-9) |
||||
To merge this branch: | bzr merge lp:~zorba-coders/zorba/process-2 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Nicolae Brinza | Approve | ||
Matthias Brantner | Approve | ||
Review via email: mp+164415@code.launchpad.net |
Commit message
Version 2.0 of the process module, allows running executables directly, without invoking bash/cmd.exe
Description of the change
Version 2.0 of the process module, allows running executables directly, without invoking bash/cmd.exe
Nicolae Brinza (nbrinza) : | # |
Zorba Build Bot (zorba-buildbot) wrote : | # |
Zorba Build Bot (zorba-buildbot) wrote : | # |
The attempt to merge lp:~zorba-coders/zorba/process-2 into lp:zorba/process-module failed. Below is the output from the failed tests.
CMake Error at /home/ceej/
Validation queue job process-
final status was:
1 tests did not succeed - changes not commited.
Error in read script: /home/ceej/
Zorba Build Bot (zorba-buildbot) wrote : | # |
Validation queue starting for merge proposal.
Log at: http://
Zorba Build Bot (zorba-buildbot) wrote : | # |
Validation queue job process-
All tests succeeded!
Zorba Build Bot (zorba-buildbot) wrote : | # |
Voting does not meet specified criteria. Required: Approve > 1, Disapprove < 1, Needs Fixing < 1, Pending < 1, Needs Information < 1, Resubmit < 1. Got: 1 Approve, 1 Pending.
Matthias Brantner (matthias-brantner) wrote : | # |
- I like the signatures and the documentation
- The two modules should both live in the same branch process (subdirectories process/v1 and process/v2) for the versioning to work the v2 one needs to be added first and then the v1 one. If we don't want to change all of our infrastructure immediately, we need to have both versions installed.
- update license header to 2013
- create_
- such as "sh" on Linux or "cmd.exe" on Windows. => such as "sh" on Unix or "cmd.exe" on Windows.
- import module namespace process = "http://
process:
gives me
{
"exit-code" : 139,
"stdout" : "",
"stderr" : "execl: No such file or directory\nID: 80 Referenced URI: http://
}
- the referenced uri message seems to be related to a memory leak
Nicolae Brinza (nbrinza) wrote : | # |
Regarding the referenced URI messages -- these are indeed coming from the StringPool as leaked entries. When the main process is fork()'ed, the child process is basically a copy of the parent process with all the statically allocated objects duplicated. When the child process calls exit(), those objects are destroyed.
Since Zorba doesn't go through a normal shutdown, the StringPool still contains those entries and it prints the error messages and even throws an error. As a result the exit code of the process that was executed is actually lost. Even if the StringPool errors are bypassed somehow, another error is raised in the Lock() class.
This problem was present in 1.0 version as well. The root of the problem is the bad design of engine and store instantiation: they are static singletons which require explicit calls to shutdown.
Some possible solutions are:
1) Call abort() instead of exit() -- this will bypass all the static object destruction but I will need to find a way to pass the exit code to the parent process.
2) Pass the engine and store pointers to the process module and down to the child process so that it can call shutdown on them. This has the added problem of shutting down the store twice -- it is not an issue for an in-memory store, but could be for other stores.
Matthias Brantner (matthias-brantner) wrote : | # |
Let's not worry about the leaks for now. Instead, we should simply disable the debug output printing the referenced uris.
I think we should bring the init/shutdown problem to the Zorba team. This has been bothering us and others for quite some time. Zorba 3.0 is the only time to fix this.
Could you please fix the other comments (especially, the v2 issue)?
Zorba Build Bot (zorba-buildbot) wrote : | # |
Validation queue starting for merge proposal.
Log at: http://
Nicolae Brinza (nbrinza) wrote : | # |
I've fixed all the issues except for the leaks problem. I have not removed the printing of the URIs because it would disable them for the entire Zorba engine. Even if the messages are removed there is still the problem of the exit code, which cannot be fixed easily now.
--
Zorba Build Bot (zorba-buildbot) wrote : | # |
Validation queue job process-
All tests succeeded!
Zorba Build Bot (zorba-buildbot) wrote : | # |
Voting does not meet specified criteria. Required: Approve > 1, Disapprove < 1, Needs Fixing < 1, Pending < 1, Needs Information < 1, Resubmit < 1. Got: 1 Approve, 1 Needs Fixing.
Matthias Brantner (matthias-brantner) wrote : | # |
Looks good now. Only one minor thing:
PATH=foo ./bin/zorba -q 'import module namespace p = "http://
I get { "exit-code" : 139, "stdout" : "", "stderr" : "execl: No such file or directory\nID: 80 Referenced URI: http://
still showing the referenced uris. The same happens for
./bin/zorba -q 'import module namespace p = "http://
Nicolae Brinza (nbrinza) wrote : | # |
Matthias,
They URI messages won't appear anymore, but I've done it by calling abort() instead of exit() in the child fork() in the process module, because:
-- it will keep the Zorba URIs leaks messages in the Release build
-- the exit code in the case of an error is lost anyway due to the thrown exception in Zorba
-- it is not actually correct to deallocate the Zorba store twice (once in the child and the second time in the parent). It could lead to problems if store is not an in-memory store.
I've added a lengthy comment in the code describing what happens and where the problem lies. If the error exit code could be sent to the main process (e.g. through another opened pipe) then it would work perfectly.
Matthias Brantner (matthias-brantner) wrote : | # |
- I didn't see the changes you described in the last comment. Did you not push them?
- Why does the module use XQuery instead of JSONiq? I think it should use JSONiq and not use prefixes for types etc.
- we might have to change the namespace to zorba.io
- comments should use <p> to separate paragraphs instead of newlines.
Nicolae Brinza (nbrinza) wrote : | # |
For some reason, the commits to my checked-out branch are not pushed automatically, and I forgot to do it manually. It is pushed now.
I've also fixed all issues you reported except for the namespace change to "zorba.io". As far as I understood, it will be done for all modules at once -- it is better than changing the namespace for one module at random, so I think it can be merged now with the current namespace.
Matthias Brantner (matthias-brantner) wrote : | # |
I made some final changes, i.e. moved the files and improved the documentation. Concerning me, it's ready to go.
Zorba Build Bot (zorba-buildbot) wrote : | # |
Validation queue starting for merge proposal.
Log at: http://
Zorba Build Bot (zorba-buildbot) wrote : | # |
The attempt to merge lp:~zorba-coders/zorba/process-2 into lp:zorba/process-module failed. Below is the output from the failed tests.
CMake Error at /home/ceej/
Validation queue job process-
final status was:
No tests were run - build or configure step must have failed.
Not commiting changes.
Error in read script: /home/ceej/
Chris Hillery (ceejatec) wrote : | # |
FYI, you're going to need to merge changes from lp:zorba/process-module in order for the build to succeed. Paul's been moving #include files around on the Zorba trunk which affects many non-core modules.
- 50. By Nicolae Brinza
-
Removed unecessary includes
Nicolae Brinza (nbrinza) : | # |
Nicolae Brinza (nbrinza) wrote : | # |
I've merged Paul's change -- it was only a one line change.
Zorba Build Bot (zorba-buildbot) wrote : | # |
Validation queue starting for merge proposal.
Log at: http://
Zorba Build Bot (zorba-buildbot) wrote : | # |
Validation queue job process-
All tests succeeded!
Preview Diff
1 | === removed file 'src/CMakeLists.txt' |
2 | --- src/CMakeLists.txt 2011-07-26 10:43:43 +0000 |
3 | +++ src/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
4 | @@ -1,19 +0,0 @@ |
5 | -# Copyright 2006-2008 The FLWOR Foundation. |
6 | -# |
7 | -# Licensed under the Apache License, Version 2.0 (the "License"); |
8 | -# you may not use this file except in compliance with the License. |
9 | -# You may obtain a copy of the License at |
10 | -# |
11 | -# http://www.apache.org/licenses/LICENSE-2.0 |
12 | -# |
13 | -# Unless required by applicable law or agreed to in writing, software |
14 | -# distributed under the License is distributed on an "AS IS" BASIS, |
15 | -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | -# See the License for the specific language governing permissions and |
17 | -# limitations under the License. |
18 | - |
19 | -# all external module libraries are generated in the directory |
20 | -# of the corresponding .xq file |
21 | -MESSAGE(STATUS "Add com") |
22 | -ADD_SUBDIRECTORY(com) |
23 | -MESSAGE(STATUS "End modules") |
24 | |
25 | === renamed file 'src/com/zorba-xquery/www/modules/CMakeLists.txt' => 'src/CMakeLists.txt' |
26 | --- src/com/zorba-xquery/www/modules/CMakeLists.txt 2011-07-01 09:24:09 +0000 |
27 | +++ src/CMakeLists.txt 2013-06-13 10:54:38 +0000 |
28 | @@ -1,4 +1,4 @@ |
29 | -# Copyright 2006-2008 The FLWOR Foundation. |
30 | +# Copyright 2006-2013 The FLWOR Foundation. |
31 | # |
32 | # Licensed under the Apache License, Version 2.0 (the "License"); |
33 | # you may not use this file except in compliance with the License. |
34 | @@ -12,4 +12,5 @@ |
35 | # See the License for the specific language governing permissions and |
36 | # limitations under the License. |
37 | |
38 | -DECLARE_ZORBA_MODULE (URI "http://www.zorba-xquery.com/modules/process" VERSION 1.0 FILE "process.xq") |
39 | +DECLARE_ZORBA_MODULE (URI "http://zorba.io/modules/process" VERSION 1.0 FILE "process-2.xq") |
40 | +DECLARE_ZORBA_MODULE (URI "http://www.zorba-xquery.com/modules/process" VERSION 1.0 FILE "process-1.xq") |
41 | |
42 | === removed directory 'src/com' |
43 | === removed file 'src/com/CMakeLists.txt' |
44 | --- src/com/CMakeLists.txt 2011-10-06 08:19:19 +0000 |
45 | +++ src/com/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
46 | @@ -1,14 +0,0 @@ |
47 | -# Copyright 2006-2008 The FLWOR Foundation. |
48 | -# |
49 | -# Licensed under the Apache License, Version 2.0 (the "License"); |
50 | -# you may not use this file except in compliance with the License. |
51 | -# You may obtain a copy of the License at |
52 | -# |
53 | -# http://www.apache.org/licenses/LICENSE-2.0 |
54 | -# |
55 | -# Unless required by applicable law or agreed to in writing, software |
56 | -# distributed under the License is distributed on an "AS IS" BASIS, |
57 | -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
58 | -# See the License for the specific language governing permissions and |
59 | -# limitations under the License. |
60 | -ADD_SUBDIRECTORY(zorba-xquery) |
61 | |
62 | === removed directory 'src/com/zorba-xquery' |
63 | === removed file 'src/com/zorba-xquery/CMakeLists.txt' |
64 | --- src/com/zorba-xquery/CMakeLists.txt 2011-10-06 08:19:19 +0000 |
65 | +++ src/com/zorba-xquery/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
66 | @@ -1,14 +0,0 @@ |
67 | -# Copyright 2006-2008 The FLWOR Foundation. |
68 | -# |
69 | -# Licensed under the Apache License, Version 2.0 (the "License"); |
70 | -# you may not use this file except in compliance with the License. |
71 | -# You may obtain a copy of the License at |
72 | -# |
73 | -# http://www.apache.org/licenses/LICENSE-2.0 |
74 | -# |
75 | -# Unless required by applicable law or agreed to in writing, software |
76 | -# distributed under the License is distributed on an "AS IS" BASIS, |
77 | -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
78 | -# See the License for the specific language governing permissions and |
79 | -# limitations under the License. |
80 | -ADD_SUBDIRECTORY(www) |
81 | |
82 | === removed directory 'src/com/zorba-xquery/www' |
83 | === removed file 'src/com/zorba-xquery/www/CMakeLists.txt' |
84 | --- src/com/zorba-xquery/www/CMakeLists.txt 2011-10-06 08:19:19 +0000 |
85 | +++ src/com/zorba-xquery/www/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
86 | @@ -1,14 +0,0 @@ |
87 | -# Copyright 2006-2008 The FLWOR Foundation. |
88 | -# |
89 | -# Licensed under the Apache License, Version 2.0 (the "License"); |
90 | -# you may not use this file except in compliance with the License. |
91 | -# You may obtain a copy of the License at |
92 | -# |
93 | -# http://www.apache.org/licenses/LICENSE-2.0 |
94 | -# |
95 | -# Unless required by applicable law or agreed to in writing, software |
96 | -# distributed under the License is distributed on an "AS IS" BASIS, |
97 | -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
98 | -# See the License for the specific language governing permissions and |
99 | -# limitations under the License. |
100 | -ADD_SUBDIRECTORY(modules) |
101 | |
102 | === removed directory 'src/com/zorba-xquery/www/modules' |
103 | === added file 'src/process-1.xq' |
104 | --- src/process-1.xq 1970-01-01 00:00:00 +0000 |
105 | +++ src/process-1.xq 2013-06-13 10:54:38 +0000 |
106 | @@ -0,0 +1,95 @@ |
107 | +xquery version "3.0"; |
108 | + |
109 | +(: |
110 | + : Copyright 2006-2013 The FLWOR Foundation. |
111 | + : |
112 | + : Licensed under the Apache License, Version 2.0 (the "License"); |
113 | + : you may not use this file except in compliance with the License. |
114 | + : You may obtain a copy of the License at |
115 | + : |
116 | + : http://www.apache.org/licenses/LICENSE-2.0 |
117 | + : |
118 | + : Unless required by applicable law or agreed to in writing, software |
119 | + : distributed under the License is distributed on an "AS IS" BASIS, |
120 | + : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
121 | + : See the License for the specific language governing permissions and |
122 | + : limitations under the License. |
123 | +:) |
124 | + |
125 | +(:~ |
126 | + : This module provides functions to create a native process and return the result |
127 | + : (i.e. exit code, result on standard out and error). |
128 | + : <p> |
129 | + : Example: |
130 | + :<pre class="ace-static" ace-mode="xquery"> |
131 | + : import module namespace proc = "http://www.zorba-xquery.com/modules/process"; |
132 | + : proc:exec("ls") |
133 | + :</pre> |
134 | + : </p> |
135 | + : <p> |
136 | + : Potential result: |
137 | + : <pre class="ace-static" ace-mode="xquery"><![CDATA[ |
138 | + : <result xmlns="http://www.zorba-xquery.com/modules/process"> |
139 | + : <stdout>myfile.txt</stout> |
140 | + : <stderr/> |
141 | + : <exit-code>0</exit-code> |
142 | + : </result> |
143 | + : ]]></pre> |
144 | + : </p> |
145 | + : |
146 | + : @author Cezar Andrei |
147 | + : @project Zorba/IO/Process |
148 | + : |
149 | + :) |
150 | +module namespace process = "http://www.zorba-xquery.com/modules/process"; |
151 | + |
152 | +declare namespace an = "http://www.zorba-xquery.com/annotations"; |
153 | + |
154 | +declare namespace ver = "http://www.zorba-xquery.com/options/versioning"; |
155 | +declare option ver:module-version "1.0"; |
156 | + |
157 | +(:~ |
158 | + : Executes the specified string command in a separate process. |
159 | + : This function does not allow arguments to be passed to |
160 | + : the command. |
161 | + : |
162 | + : @param $cmd command to be executed (without arguments) |
163 | + : |
164 | + : @return the result of the execution as an element as |
165 | + : shown in the documentation of this module. The exit-code |
166 | + : element returns the exit code of the child process. |
167 | + : For POSIX compliant platforms: returns the process exit code. If process is |
168 | + : terminated or stopped: 128 + termination signal code. |
169 | + : For Windows platforms: returns the return value of the process or the exit |
170 | + : or terminate process specified value. |
171 | + : |
172 | + : @error process:PROC01 if an error occurred while communicating |
173 | + : with the executed process. |
174 | + :) |
175 | +declare %an:sequential function process:exec( |
176 | + $cmd as xs:string |
177 | +) as element(process:result) external; |
178 | + |
179 | +(:~ |
180 | + : Executes the specified string command in a separate process. |
181 | + : Each of the strings in the sequence passed in as the second |
182 | + : argument is passed as an argument to the executed command. |
183 | + : |
184 | + : @param $cmd command to be executed (without arguments) |
185 | + : @param $args the arguments passed to the executed command (e.g. "-la") |
186 | + : |
187 | + : @return the result of the execution as an element as |
188 | + : shown in the documentation of this module. The exit-code |
189 | + : element returns the exit code of the child process. |
190 | + : For POSIX compliant platforms: returns the process exit code. If process is |
191 | + : terminated or stopped: 128 + termination signal code. |
192 | + : For Windows platforms: returns the return value of the process or the exit |
193 | + : or terminate process specified value. |
194 | + : |
195 | + : @error process:PROC01 if an error occurred while communicating |
196 | + : with the executed process. |
197 | + :) |
198 | +declare %an:sequential function process:exec( |
199 | + $cmd as xs:string, |
200 | + $args as xs:string* |
201 | +) as element(process:result) external; |
202 | |
203 | === added directory 'src/process-1.xq.src' |
204 | === added file 'src/process-1.xq.src/process.cpp' |
205 | --- src/process-1.xq.src/process.cpp 1970-01-01 00:00:00 +0000 |
206 | +++ src/process-1.xq.src/process.cpp 2013-06-13 10:54:38 +0000 |
207 | @@ -0,0 +1,577 @@ |
208 | +/* |
209 | + * Copyright 2006-2008 The FLWOR Foundation. |
210 | + * |
211 | + * Licensed under the Apache License, Version 2.0 (the "License"); |
212 | + * you may not use this file except in compliance with the License. |
213 | + * You may obtain a copy of the License at |
214 | + * |
215 | + * http://www.apache.org/licenses/LICENSE-2.0 |
216 | + * |
217 | + * Unless required by applicable law or agreed to in writing, software |
218 | + * distributed under the License is distributed on an "AS IS" BASIS, |
219 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
220 | + * See the License for the specific language governing permissions and |
221 | + * limitations under the License. |
222 | + */ |
223 | + |
224 | +#include <sstream> |
225 | +#include <stdlib.h> |
226 | +#include <stdio.h> |
227 | +#include <errno.h> |
228 | +#include <vector> |
229 | +#include <iostream> |
230 | +#include <limits.h> |
231 | +#include <algorithm> |
232 | + |
233 | +#ifdef WIN32 |
234 | +# include <windows.h> |
235 | + |
236 | +# ifndef NDEBUG |
237 | +# define _CRTDBG_MAP_ALLOC |
238 | +# include <stdlib.h> |
239 | +# include <crtdbg.h> |
240 | +# endif |
241 | +#else |
242 | +# include <unistd.h> |
243 | +# ifdef __APPLE__ |
244 | +# include <sys/wait.h> |
245 | +# else |
246 | +# include <wait.h> |
247 | +# endif |
248 | +#endif |
249 | + |
250 | +#include <zorba/item_factory.h> |
251 | +#include <zorba/singleton_item_sequence.h> |
252 | +#include <zorba/diagnostic_list.h> |
253 | +#include <zorba/user_exception.h> |
254 | +#include <zorba/empty_sequence.h> |
255 | + |
256 | +#include "process.h" |
257 | + |
258 | +namespace zorba { |
259 | +namespace processmodule { |
260 | + |
261 | +/****************************************************************************** |
262 | + *****************************************************************************/ |
263 | +void create_result_node( |
264 | + zorba::Item& aResult, |
265 | + const std::string& aStandardOut, |
266 | + const std::string& aErrorOut, |
267 | + int aExitCode, |
268 | + zorba::ItemFactory* aFactory) |
269 | +{ |
270 | + zorba::Item lResultQName = |
271 | + aFactory->createQName("http://www.zorba-xquery.com/modules/process", "result"); |
272 | + zorba::Item lExitCodeQName = |
273 | + aFactory->createQName("http://www.zorba-xquery.com/modules/process", "exit-code"); |
274 | + zorba::Item lOutputQName = |
275 | + aFactory->createQName("http://www.zorba-xquery.com/modules/process", "stdout"); |
276 | + zorba::Item lErrorQName = |
277 | + aFactory->createQName("http://www.zorba-xquery.com/modules/process", "stderr"); |
278 | + zorba::Item lNullItem; |
279 | + zorba::Item lTypeName = |
280 | + aFactory->createQName("http://www.w3.org/2001/XMLSchema", "untyped"); |
281 | + |
282 | + zorba::NsBindings lNSBindings; |
283 | + |
284 | + // root node called result |
285 | + aResult = aFactory->createElementNode( |
286 | + lNullItem, lResultQName, lTypeName, false, false, lNSBindings); |
287 | + |
288 | + // <result><output> aStandardOut </output></result> |
289 | + zorba::Item lOutput; |
290 | + lOutput = aFactory->createElementNode( |
291 | + aResult, lOutputQName, lTypeName, true, false, lNSBindings); |
292 | + aFactory->createTextNode(lOutput, aStandardOut); |
293 | + |
294 | + // <result><error> aErrorOut </error></result> |
295 | + zorba::Item lError; |
296 | + lError = aFactory->createElementNode( |
297 | + aResult, lErrorQName, lTypeName, true, false, lNSBindings); |
298 | + aFactory->createTextNode(lError, aErrorOut); |
299 | + |
300 | + // <result><exit-code> aExitCode </exit-code></result> |
301 | + zorba::Item lExitCode; |
302 | + lExitCode = aFactory->createElementNode( |
303 | + aResult, lExitCodeQName, lTypeName, true, false, lNSBindings); |
304 | + std::ostringstream lExitCodeString; |
305 | + lExitCodeString << aExitCode; |
306 | + aFactory->createTextNode(lExitCode, lExitCodeString.str()); |
307 | +} |
308 | + |
309 | +#ifdef WIN32 |
310 | + |
311 | +/*********************************************** |
312 | +* throw a descriptive message of the last error |
313 | +* accessible with GetLastError() on windows |
314 | +*/ |
315 | +void throw_last_error(const zorba::String& aFilename, unsigned int aLineNumber){ |
316 | + LPVOID lpvMessageBuffer; |
317 | + TCHAR lErrorBuffer[512]; |
318 | + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, |
319 | + NULL, GetLastError(), |
320 | + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
321 | + (LPTSTR)&lpvMessageBuffer, 0, NULL); |
322 | + wsprintf(lErrorBuffer,TEXT("Process Error Code: %d - Message= %s"),GetLastError(), (TCHAR *)lpvMessageBuffer); |
323 | + LocalFree(lpvMessageBuffer); |
324 | + Item lQName = ProcessModule::getItemFactory()->createQName( |
325 | + "http://www.zorba-xquery.com/modules/process", |
326 | + "PROC01"); |
327 | +#ifdef UNICODE |
328 | + char error_str[1024]; |
329 | + WideCharToMultiByte(CP_UTF8, 0, lErrorBuffer, -1, error_str, sizeof(error_str), NULL, NULL); |
330 | + throw USER_EXCEPTION(lQName, error_str); |
331 | +#else |
332 | + throw USER_EXCEPTION(lQName, lErrorBuffer); |
333 | +#endif |
334 | +} |
335 | + |
336 | +/****************************************** |
337 | +* read output from child process on windows |
338 | +*/ |
339 | +void read_child_output(HANDLE aOutputPipe, std::ostringstream& aTargetStream) |
340 | +{ |
341 | + CHAR lBuffer[256]; |
342 | + DWORD lBytesRead; |
343 | + |
344 | + while(TRUE) |
345 | + { |
346 | + if ( |
347 | + !ReadFile(aOutputPipe,lBuffer,sizeof(lBuffer),&lBytesRead,NULL) |
348 | + || !lBytesRead |
349 | + ) |
350 | + { |
351 | + if (GetLastError() == ERROR_BROKEN_PIPE) |
352 | + break; // finished |
353 | + else{ |
354 | + |
355 | + // couldn't read from pipe |
356 | + throw_last_error(__FILE__, __LINE__); |
357 | + } |
358 | + } |
359 | + |
360 | + // remove the windows specific carriage return outputs |
361 | + // std::stringstream lTmp; |
362 | + // lTmp.write(lBuffer,lBytesRead); |
363 | + // std::string lRawString=lTmp.str(); |
364 | + // std::replace( lRawString.begin(), lRawString.end(), '\r', ' ' ); |
365 | + // aTargetStream.write(lRawString.c_str(),static_cast<std::streamsize>(lRawString.length())); |
366 | + for(DWORD i=0;i<lBytesRead;i++) |
367 | + { |
368 | + if(lBuffer[i] != '\r') |
369 | + aTargetStream << lBuffer[i]; |
370 | + } |
371 | + lBytesRead = 0; |
372 | + } |
373 | +} |
374 | + |
375 | +/****************************************** |
376 | +* Create a child process on windows with |
377 | +* redirected output |
378 | +*/ |
379 | +BOOL create_child_process(HANDLE aStdOutputPipe,HANDLE aStdErrorPipe,const std::string& aCommand,PROCESS_INFORMATION& aProcessInformation){ |
380 | + STARTUPINFO lChildStartupInfo; |
381 | + BOOL result=FALSE; |
382 | + |
383 | + // set the output handles |
384 | + FillMemory(&lChildStartupInfo,sizeof(lChildStartupInfo),0); |
385 | + lChildStartupInfo.cb = sizeof(lChildStartupInfo); |
386 | + GetStartupInfo(&lChildStartupInfo); |
387 | + lChildStartupInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; |
388 | + lChildStartupInfo.wShowWindow = SW_HIDE; // don't show the command window |
389 | + lChildStartupInfo.hStdOutput = aStdOutputPipe; |
390 | + lChildStartupInfo.hStdError = aStdErrorPipe; |
391 | + |
392 | + // convert from const char* to char* |
393 | + size_t length = strlen(aCommand.c_str()); |
394 | +#ifdef UNICODE |
395 | + WCHAR *tmpCommand = new WCHAR[length+1]; |
396 | + MultiByteToWideChar(CP_UTF8, 0, aCommand.c_str(), -1, tmpCommand, length+1); |
397 | +#else |
398 | + char *tmpCommand=new char[length+1]; |
399 | + strcpy (tmpCommand,aCommand.c_str()); |
400 | + tmpCommand[length]='\0'; |
401 | +#endif |
402 | + |
403 | + try{ |
404 | + |
405 | + // settings for the child process |
406 | + LPCTSTR lApplicationName=NULL; |
407 | + LPTSTR lCommandLine=tmpCommand; |
408 | + LPSECURITY_ATTRIBUTES lProcessAttributes=NULL; |
409 | + LPSECURITY_ATTRIBUTES lThreadAttributes=NULL; |
410 | + BOOL lInheritHandles=TRUE; // that's what we want |
411 | + DWORD lCreationFlags=CREATE_NEW_CONSOLE; |
412 | + LPVOID lEnvironment=NULL; |
413 | + LPCTSTR lCurrentDirectory=NULL; // same as main process |
414 | + |
415 | + // start child |
416 | + result=CreateProcess( |
417 | + lApplicationName,lCommandLine,lProcessAttributes, |
418 | + lThreadAttributes,lInheritHandles,lCreationFlags, |
419 | + lEnvironment,lCurrentDirectory,&lChildStartupInfo, |
420 | + &aProcessInformation); |
421 | + |
422 | + }catch(...){ |
423 | + delete[] tmpCommand; |
424 | + tmpCommand=0; |
425 | + throw; |
426 | + } |
427 | + |
428 | + delete[] tmpCommand; |
429 | + tmpCommand=0; |
430 | + |
431 | + return result; |
432 | +} |
433 | + |
434 | +/****************************************** |
435 | +* run a process that executes the aCommand |
436 | +* in a new console and reads the output |
437 | +*/ |
438 | +int run_process( |
439 | + const std::string& aCommand, |
440 | + std::ostringstream& aTargetOutStream, |
441 | + std::ostringstream& aTargetErrStream) |
442 | +{ |
443 | + HANDLE lOutRead, lErrRead, lStdOut, lStdErr; |
444 | + SECURITY_ATTRIBUTES lSecurityAttributes; |
445 | + PROCESS_INFORMATION lChildProcessInfo; |
446 | + DWORD exitCode=0; |
447 | + |
448 | + // prepare security attributes |
449 | + lSecurityAttributes.nLength= sizeof(lSecurityAttributes); |
450 | + lSecurityAttributes.lpSecurityDescriptor = NULL; |
451 | + lSecurityAttributes.bInheritHandle = TRUE; |
452 | + |
453 | + // create output pipes |
454 | + if( |
455 | + !CreatePipe(&lOutRead,&lStdOut,&lSecurityAttributes,1024*1024) // std::cout >> lOutRead |
456 | + || !CreatePipe(&lErrRead,&lStdErr,&lSecurityAttributes,1024*1024) // std::cerr >> lErrRead |
457 | + ){ |
458 | + Item lQName = ProcessModule::getItemFactory()->createQName( |
459 | + "http://www.zorba-xquery.com/modules/process", "PROC01"); |
460 | + throw USER_EXCEPTION(lQName, |
461 | + "Couldn't create one of std::cout/std::cerr pipe for child process execution." |
462 | + ); |
463 | + }; |
464 | + |
465 | + //start child process |
466 | + BOOL ok = create_child_process(lStdOut,lStdErr,aCommand,lChildProcessInfo); |
467 | + if(ok==TRUE) |
468 | + { |
469 | + |
470 | + // close unneeded handle |
471 | + CloseHandle(lChildProcessInfo.hThread); |
472 | + |
473 | + // wait for the process to finish |
474 | + WaitForSingleObject(lChildProcessInfo.hProcess,INFINITE); |
475 | + if (!GetExitCodeProcess(lChildProcessInfo.hProcess, &exitCode)) |
476 | + { |
477 | + std::stringstream lErrorMsg; |
478 | + lErrorMsg |
479 | + << "Couldn't get exit code from child process. Executed command: '" << aCommand << "'."; |
480 | + Item lQName = ProcessModule::getItemFactory()->createQName( |
481 | + "http://www.zorba-xquery.com/modules/process", "PROC01"); |
482 | + throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str()); |
483 | + } |
484 | + |
485 | + CloseHandle(lChildProcessInfo.hProcess); |
486 | + CloseHandle(lStdOut); |
487 | + CloseHandle(lStdErr); |
488 | + |
489 | + // read child's output |
490 | + read_child_output(lOutRead,aTargetOutStream); |
491 | + read_child_output(lErrRead,aTargetErrStream); |
492 | + |
493 | + // close |
494 | + CloseHandle(lOutRead); |
495 | + CloseHandle(lErrRead); |
496 | + |
497 | + }else{ |
498 | + CloseHandle(lStdOut); |
499 | + CloseHandle(lStdErr); |
500 | + CloseHandle(lOutRead); |
501 | + CloseHandle(lErrRead); |
502 | + |
503 | + // couldn't launch process |
504 | + throw_last_error(__FILE__, __LINE__); |
505 | + }; |
506 | + |
507 | + |
508 | + return exitCode; |
509 | +} |
510 | + |
511 | +#else |
512 | + |
513 | +#define READ 0 |
514 | +#define WRITE 1 |
515 | + |
516 | +pid_t zorba_popen(const char *command, int *infp, int *outfp, int *errfp) |
517 | +{ |
518 | + int p_stdin[2]; |
519 | + int p_stdout[2]; |
520 | + int p_stderr[2]; |
521 | + pid_t pid; |
522 | + |
523 | + if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0 || pipe(p_stderr) != 0) |
524 | + return -1; |
525 | + |
526 | + pid = fork(); |
527 | + |
528 | + if (pid < 0) |
529 | + return pid; |
530 | + else if (pid == 0) |
531 | + { |
532 | + close(p_stdin[WRITE]); |
533 | + dup2(p_stdin[READ], 0); // duplicate stdin |
534 | + |
535 | + close(p_stdout[READ]); |
536 | + dup2(p_stdout[WRITE], 1); // duplicate stdout |
537 | + |
538 | + close(p_stderr[READ]); |
539 | + dup2(p_stderr[WRITE], 2); // duplicate stderr |
540 | + |
541 | + execl("/bin/sh", "sh", "-c", command, NULL); |
542 | + perror("execl"); // output the result to standard error |
543 | + exit(errno); |
544 | + } |
545 | + |
546 | + if (infp == NULL) |
547 | + close(p_stdin[WRITE]); |
548 | + else |
549 | + *infp = p_stdin[WRITE]; |
550 | + |
551 | + if (outfp == NULL) |
552 | + close(p_stdout[READ]); |
553 | + else |
554 | + *outfp = p_stdout[READ]; |
555 | + |
556 | + if (errfp == NULL) |
557 | + close(p_stderr[READ]); |
558 | + else |
559 | + *errfp = p_stderr[READ]; |
560 | + |
561 | + close(p_stdin[READ]); // We only write to the forks stdin anyway |
562 | + close(p_stdout[WRITE]); // and we only read from its stdout |
563 | + close(p_stderr[WRITE]); // and we only read from its stderr |
564 | + |
565 | + return pid; |
566 | +} |
567 | +#endif |
568 | + |
569 | +/****************************************************************************** |
570 | + *****************************************************************************/ |
571 | +zorba::ItemSequence_t |
572 | +ExecFunction::evaluate( |
573 | + const Arguments_t& aArgs, |
574 | + const zorba::StaticContext* aSctx, |
575 | + const zorba::DynamicContext* aDctx) const |
576 | +{ |
577 | + std::string lCommand; |
578 | + std::vector<std::string> lArgs; |
579 | + int exit_code = 0; |
580 | + |
581 | + lCommand = getOneStringArgument(aArgs, 0).c_str(); |
582 | + |
583 | + if (aArgs.size() > 1) |
584 | + { |
585 | + zorba::Item lArg; |
586 | + Iterator_t arg1_iter = aArgs[1]->getIterator(); |
587 | + arg1_iter->open(); |
588 | + while (arg1_iter->next(lArg)) |
589 | + { |
590 | + lArgs.push_back(lArg.getStringValue().c_str()); |
591 | + } |
592 | + arg1_iter->close(); |
593 | + } |
594 | + |
595 | + std::ostringstream lTmp; |
596 | + |
597 | +#ifdef WIN32 |
598 | + // execute process command in a new commandline |
599 | + // with quotes at the beggining and at the end |
600 | + lTmp << "cmd /C \""; |
601 | +#endif |
602 | + |
603 | + lTmp << "\"" << lCommand << "\""; //quoted for spaced paths/filenames |
604 | + size_t pos=0; |
605 | + for (std::vector<std::string>::const_iterator lIter = lArgs.begin(); |
606 | + lIter != lArgs.end(); ++lIter) |
607 | + { |
608 | + pos = (*lIter).rfind('\\')+(*lIter).rfind('/'); |
609 | + if (int(pos)>=0) |
610 | + lTmp << " \"" << *lIter << "\""; |
611 | + else |
612 | + lTmp << " " << *lIter; |
613 | + } |
614 | +#ifdef WIN32 |
615 | + lTmp << "\""; // with quotes at the end for commandline |
616 | +#endif |
617 | + |
618 | + std::ostringstream lStdout; |
619 | + std::ostringstream lStderr; |
620 | + |
621 | +#ifdef WIN32 |
622 | + std::string lCommandLineString = lTmp.str(); |
623 | + int code = run_process(lCommandLineString, lStdout, lStderr); |
624 | + |
625 | + if (code != 0) |
626 | + { |
627 | + std::stringstream lErrorMsg; |
628 | + lErrorMsg << "Failed to execute the command (" << code << ")"; |
629 | + Item lQName = ProcessModule::getItemFactory()->createQName( |
630 | + "http://www.zorba-xquery.com/modules/process", "PROC01"); |
631 | + throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str()); |
632 | + } |
633 | + exit_code = code; |
634 | + |
635 | +#else //not WIN32 |
636 | + |
637 | + int outfp; |
638 | + int errfp; |
639 | + int status; |
640 | + pid_t pid; |
641 | + |
642 | + pid = zorba_popen(lTmp.str().c_str(), NULL, &outfp, &errfp); |
643 | + if ( pid == -1 ) |
644 | + { |
645 | + std::stringstream lErrorMsg; |
646 | + lErrorMsg << "Failed to execute the command (" << pid << ")"; |
647 | + Item lQName = ProcessModule::getItemFactory()->createQName( |
648 | + "http://www.zorba-xquery.com/modules/process", "PROC01"); |
649 | + throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str()); |
650 | + } |
651 | + else |
652 | + { |
653 | + char lBuf[PATH_MAX]; |
654 | + ssize_t length = 0; |
655 | + while ( (length=read(outfp, lBuf, PATH_MAX)) > 0 ) |
656 | + { |
657 | + lStdout.write(lBuf, length); |
658 | + } |
659 | + |
660 | + status = close(outfp); |
661 | + |
662 | + while ( (length=read(errfp, lBuf, PATH_MAX)) > 0 ) |
663 | + { |
664 | + lStderr.write(lBuf, length); |
665 | + } |
666 | + |
667 | + status = close(errfp); |
668 | + |
669 | + if ( status < 0 ) |
670 | + { |
671 | + std::stringstream lErrorMsg; |
672 | + lErrorMsg << "Failed to close the err stream (" << status << ")"; |
673 | + Item lQName = ProcessModule::getItemFactory()->createQName( |
674 | + "http://www.zorba-xquery.com/modules/process", "PROC01"); |
675 | + throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str()); |
676 | + } |
677 | + |
678 | + int stat = 0; |
679 | + |
680 | + pid_t w = waitpid(pid, &stat, 0); |
681 | + |
682 | + if (w == -1) |
683 | + { |
684 | + std::stringstream lErrorMsg; |
685 | + lErrorMsg << "Failed to wait for child process "; |
686 | + Item lQName = ProcessModule::getItemFactory()->createQName( |
687 | + "http://www.zorba-xquery.com/modules/process", "PROC01"); |
688 | + throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str()); |
689 | + } |
690 | + |
691 | + if (WIFEXITED(stat)) |
692 | + { |
693 | + //std::cout << " WEXITSTATUS : " << WEXITSTATUS(stat) << std::endl; std::cout.flush(); |
694 | + exit_code = WEXITSTATUS(stat); |
695 | + } |
696 | + else if (WIFSIGNALED(stat)) |
697 | + { |
698 | + //std::cout << " WTERMSIG : " << WTERMSIG(stat) << std::endl; std::cout.flush(); |
699 | + exit_code = 128 + WTERMSIG(stat); |
700 | + } |
701 | + else if (WIFSTOPPED(stat)) |
702 | + { |
703 | + //std::cout << " STOPSIG : " << WSTOPSIG(stat) << std::endl; std::cout.flush(); |
704 | + exit_code = 128 + WSTOPSIG(stat); |
705 | + } |
706 | + else |
707 | + { |
708 | + //std::cout << " else : " << std::endl; std::cout.flush(); |
709 | + exit_code = 255; |
710 | + } |
711 | + |
712 | + //std::cout << " exit_code : " << exit_code << std::endl; std::cout.flush(); |
713 | + |
714 | + } |
715 | +#endif // WIN32 |
716 | + |
717 | + zorba::Item lResult; |
718 | + create_result_node(lResult, lStdout.str(), lStderr.str(), exit_code, |
719 | + theModule->getItemFactory()); |
720 | + |
721 | + return zorba::ItemSequence_t(new zorba::SingletonItemSequence(lResult)); |
722 | +} |
723 | + |
724 | +String ExecFunction::getOneStringArgument (const Arguments_t& aArgs, int aPos) |
725 | + const |
726 | +{ |
727 | + Item lItem; |
728 | + Iterator_t args_iter = aArgs[aPos]->getIterator(); |
729 | + args_iter->open(); |
730 | + args_iter->next(lItem); |
731 | + zorba::String lTmpString = lItem.getStringValue(); |
732 | + args_iter->close(); |
733 | + return lTmpString; |
734 | +} |
735 | + |
736 | +/****************************************************************************** |
737 | + *****************************************************************************/ |
738 | +ProcessModule::~ProcessModule() |
739 | +{ |
740 | + for (FuncMap_t::const_iterator lIter = theFunctions.begin(); |
741 | + lIter != theFunctions.end(); ++lIter) { |
742 | + delete lIter->second; |
743 | + } |
744 | + theFunctions.clear(); |
745 | +} |
746 | + |
747 | +zorba::ExternalFunction* |
748 | +ProcessModule::getExternalFunction(const zorba::String& aLocalname) |
749 | +{ |
750 | + FuncMap_t::const_iterator lFind = theFunctions.find(aLocalname); |
751 | + zorba::ExternalFunction*& lFunc = theFunctions[aLocalname]; |
752 | + if (lFind == theFunctions.end()) |
753 | + { |
754 | + if (!aLocalname.compare("exec")) |
755 | + { |
756 | + lFunc = new ExecFunction(this); |
757 | + } |
758 | + } |
759 | + return lFunc; |
760 | +} |
761 | + |
762 | +void ProcessModule::destroy() |
763 | +{ |
764 | + if (!dynamic_cast<ProcessModule*>(this)) { |
765 | + return; |
766 | + } |
767 | + delete this; |
768 | +} |
769 | + |
770 | +ItemFactory* ProcessModule::theFactory = 0; |
771 | + |
772 | +} /* namespace processmodule */ |
773 | +} /* namespace zorba */ |
774 | + |
775 | +#ifdef WIN32 |
776 | +# define DLL_EXPORT __declspec(dllexport) |
777 | +#else |
778 | +# define DLL_EXPORT __attribute__ ((visibility("default"))) |
779 | +#endif |
780 | + |
781 | +extern "C" DLL_EXPORT zorba::ExternalModule* createModule() { |
782 | + return new zorba::processmodule::ProcessModule(); |
783 | +} |
784 | +/* vim:set et sw=2 ts=2: */ |
785 | |
786 | === added file 'src/process-1.xq.src/process.h' |
787 | --- src/process-1.xq.src/process.h 1970-01-01 00:00:00 +0000 |
788 | +++ src/process-1.xq.src/process.h 2013-06-13 10:54:38 +0000 |
789 | @@ -0,0 +1,105 @@ |
790 | +/* |
791 | + * Copyright 2006-2008 The FLWOR Foundation. |
792 | + * |
793 | + * Licensed under the Apache License, Version 2.0 (the "License"); |
794 | + * you may not use this file except in compliance with the License. |
795 | + * You may obtain a copy of the License at |
796 | + * |
797 | + * http://www.apache.org/licenses/LICENSE-2.0 |
798 | + * |
799 | + * Unless required by applicable law or agreed to in writing, software |
800 | + * distributed under the License is distributed on an "AS IS" BASIS, |
801 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
802 | + * See the License for the specific language governing permissions and |
803 | + * limitations under the License. |
804 | + */ |
805 | +#ifndef ZORBA_PROCESSMODULE_PROCESS_H |
806 | +#define ZORBA_PROCESSMODULE_PROCESS_H |
807 | + |
808 | +#include <map> |
809 | + |
810 | +#include <zorba/zorba.h> |
811 | +#include <zorba/error.h> |
812 | +#include <zorba/external_module.h> |
813 | +#include <zorba/function.h> |
814 | + |
815 | +namespace zorba { |
816 | +namespace processmodule { |
817 | + |
818 | +/****************************************************************************** |
819 | + *****************************************************************************/ |
820 | +class ProcessModule : public ExternalModule |
821 | +{ |
822 | +private: |
823 | + static ItemFactory* theFactory; |
824 | + |
825 | +protected: |
826 | + class ltstr |
827 | + { |
828 | + public: |
829 | + bool operator()(const String& s1, const String& s2) const |
830 | + { |
831 | + return s1.compare(s2) < 0; |
832 | + } |
833 | + }; |
834 | + |
835 | + typedef std::map<String, ExternalFunction*, ltstr> FuncMap_t; |
836 | + |
837 | + FuncMap_t theFunctions; |
838 | + |
839 | +public: |
840 | + virtual ~ProcessModule(); |
841 | + |
842 | + virtual zorba::String getURI() const |
843 | + { return "http://www.zorba-xquery.com/modules/process"; } |
844 | + |
845 | + virtual zorba::ExternalFunction* |
846 | + getExternalFunction(const zorba::String& aLocalname); |
847 | + |
848 | + virtual void destroy(); |
849 | + |
850 | + static ItemFactory* getItemFactory() |
851 | + { |
852 | + if(!theFactory) |
853 | + { |
854 | + theFactory = Zorba::getInstance(0)->getItemFactory(); |
855 | + } |
856 | + |
857 | + return theFactory; |
858 | + } |
859 | +}; |
860 | + |
861 | +/****************************************************************************** |
862 | + *****************************************************************************/ |
863 | +class ExecFunction : public ContextualExternalFunction |
864 | +{ |
865 | +public: |
866 | + ExecFunction(const ProcessModule* aModule) : theModule(aModule) {} |
867 | + |
868 | + virtual ~ExecFunction() {} |
869 | + |
870 | + virtual zorba::String |
871 | + getLocalName() const { return "exec"; } |
872 | + |
873 | + virtual zorba::ItemSequence_t |
874 | + evaluate(const Arguments_t&, |
875 | + const zorba::StaticContext*, |
876 | + const zorba::DynamicContext*) const; |
877 | + |
878 | + virtual String getURI() const |
879 | + { |
880 | + return theModule->getURI(); |
881 | + } |
882 | + |
883 | +protected: |
884 | + const ProcessModule* theModule; |
885 | + |
886 | + String getOneStringArgument (const Arguments_t& aArgs, int index) const; |
887 | +}; |
888 | + |
889 | + |
890 | + |
891 | +} /* namespace processmodule */ |
892 | +} /* namespace zorba */ |
893 | + |
894 | +#endif // ZORBA_PROCESSMODULE_PROCESS_H |
895 | |
896 | === renamed file 'src/com/zorba-xquery/www/modules/process.xq' => 'src/process-2.xq' |
897 | --- src/com/zorba-xquery/www/modules/process.xq 2012-12-06 02:13:28 +0000 |
898 | +++ src/process-2.xq 2013-06-13 10:54:38 +0000 |
899 | @@ -1,7 +1,7 @@ |
900 | -xquery version "3.0"; |
901 | +jsoniq version "1.0"; |
902 | |
903 | (: |
904 | - : Copyright 2006-2009 The FLWOR Foundation. |
905 | + : Copyright 2006-2013 The FLWOR Foundation. |
906 | : |
907 | : Licensed under the Apache License, Version 2.0 (the "License"); |
908 | : you may not use this file except in compliance with the License. |
909 | @@ -17,77 +17,181 @@ |
910 | :) |
911 | |
912 | (:~ |
913 | + : <p> |
914 | : This module provides functions to create a native process and return the result |
915 | - : (i.e. exit code, result on standard out and error). |
916 | + : (i.e. exit code, result on standard out and error) of executing the given |
917 | + : file or command. |
918 | + : </p> |
919 | : |
920 | + : <p> |
921 | : Example: |
922 | - :<pre class="ace-static" ace-mode="xquery"> |
923 | - : import module namespace proc = "http://www.zorba-xquery.com/modules/process"; |
924 | - : proc:exec("ls") |
925 | - :</pre> |
926 | + : <pre> |
927 | + : import module namespace proc = "http://zorba.io/modules/process"; |
928 | + : proc:exec("ls") |
929 | + : </pre> |
930 | + : </p> |
931 | : |
932 | + : <p> |
933 | : Potential result: |
934 | - : <pre class="ace-static" ace-mode="xquery"><![CDATA[ |
935 | - : <result xmlns="http://www.zorba-xquery.com/modules/process"> |
936 | - : <stdout>myfile.txt</stout> |
937 | - : <stderr/> |
938 | - : <exit-code>0</exit-code> |
939 | - : </result> |
940 | - : ]]></pre> |
941 | - : |
942 | - : @author Cezar Andrei |
943 | + : <pre> |
944 | + : { |
945 | + : "exit-code": 0, |
946 | + : "stdout": "myfile.txt", |
947 | + : "stderr": "" |
948 | + : } |
949 | + : </pre> |
950 | + : </p> |
951 | + : |
952 | + : <p> |
953 | + : The <tt>exec-command</tt> set of functions allows execution of commands |
954 | + : through the command line interpreter of the operating system, such as "sh" |
955 | + : on Unix systems or "cmd.exe" on Windows. |
956 | + : </p> |
957 | + : |
958 | + : <p> |
959 | + : For POSIX compliant platforms the functions return 128 + termination signal |
960 | + : code of the process as their exit-code. |
961 | + : On Windows platforms, the exit-code is the return value of the process or the exit |
962 | + : or terminate process specified value. |
963 | + : </p> |
964 | + : |
965 | + : @author Cezar Andrei, Nicolae Brinza |
966 | : @project Zorba/IO/Process |
967 | - : |
968 | :) |
969 | -module namespace process = "http://www.zorba-xquery.com/modules/process"; |
970 | +module namespace p = "http://zorba.io/modules/process"; |
971 | |
972 | declare namespace an = "http://www.zorba-xquery.com/annotations"; |
973 | |
974 | declare namespace ver = "http://www.zorba-xquery.com/options/versioning"; |
975 | declare option ver:module-version "1.0"; |
976 | |
977 | + |
978 | (:~ |
979 | - : Executes the specified string command in a separate process. |
980 | + : <p> |
981 | + : Executes the specified program in a separate process. |
982 | + : </p> |
983 | + : |
984 | + : <p> |
985 | : This function does not allow arguments to be passed to |
986 | - : the command. |
987 | + : the command. The $filename parameter can contain the full path to the |
988 | + : executable. On Unix systems, if the specified filename does not contain |
989 | + : a slash "/", the function duplicates the actions of the shell in searching |
990 | + : for an executable file. The file is sought in the colon-separated list of |
991 | + : directory pathnames specified in the PATH environment variable. If this |
992 | + : variable isn't defined, the path list defaults to the current directory |
993 | + : followed by the list of directories returned by the operating system. |
994 | + : </p> |
995 | + : |
996 | + : @param $filename the name of program to be executed |
997 | + : |
998 | + : @return the result of the execution as an object |
999 | + : |
1000 | + : @error p:PROC01 if an error occurred while communicating with the process. |
1001 | + :) |
1002 | +declare %an:sequential function p:exec( |
1003 | + $filename as string |
1004 | +) as object external; |
1005 | + |
1006 | +(:~ |
1007 | + : <p> |
1008 | + : Executes the specified program in a separate process. |
1009 | + : </p> |
1010 | + : |
1011 | + : <p> |
1012 | + : The $filename parameter can contain the full path to the |
1013 | + : executable. On Unix systems, if the specified filename does not contain |
1014 | + : a slash "/", the function duplicates the actions of the shell in searching |
1015 | + : for an executable file. The file is sought in the colon-separated list of |
1016 | + : directory pathnames specified in the PATH environment variable. If this |
1017 | + : variable isn't defined, the path list defaults to the current directory |
1018 | + : followed by the list of directories returned by the operating system. |
1019 | + : The $args parameters will be passed to the executable file as arguments. |
1020 | + : </p> |
1021 | + : |
1022 | + : @param $filename the name of program to be executed |
1023 | + : @param $args arguments to be passed to the executable |
1024 | + : |
1025 | + : @return the result of the execution as an object |
1026 | + : |
1027 | + : @error p:PROC01 if an error occurred while communicating with the process. |
1028 | + :) |
1029 | +declare %an:sequential function p:exec( |
1030 | + $filename as string, |
1031 | + $args as string* |
1032 | +) as object external; |
1033 | + |
1034 | +(:~ |
1035 | + : <p> |
1036 | + : Executes the specified program in a separate process. |
1037 | + : </p> |
1038 | + : |
1039 | + : <p> |
1040 | + : The $filename parameter can contain the full path to the |
1041 | + : executable. On Unix systems, if the specified filename does not contain |
1042 | + : a slash "/", the function duplicates the actions of the shell in searching |
1043 | + : for an executable file. The file is sought in the colon-separated list of |
1044 | + : directory pathnames specified in the PATH environment variable. If this |
1045 | + : variable isn't defined, the path list defaults to the current directory |
1046 | + : followed by the list of directories returned by the operating system. |
1047 | + : </p> |
1048 | + : |
1049 | + : <p> |
1050 | + : The $env allows defining and passing environment variables to the target |
1051 | + : process. They should be in the form "ENVVAR=value" where "ENVVAR" is the |
1052 | + : name of the environment variable and "value' is the string value to set it to. |
1053 | + : </p> |
1054 | + : |
1055 | + : @param $filename the name of program to be executed |
1056 | + : @param $args arguments to be passed to the executable |
1057 | + : @param $env list of environment variables for the executable |
1058 | + : |
1059 | + : @return the result of the execution as an object |
1060 | + : |
1061 | + : @error p:PROC01 if an error occurred while communicating with the process. |
1062 | + :) |
1063 | +declare %an:sequential function p:exec( |
1064 | + $filename as string, |
1065 | + $args as string*, |
1066 | + $env as string* |
1067 | +) as object external; |
1068 | + |
1069 | +(:~ |
1070 | + : <p> |
1071 | + : Executes the specified string command in a separate process. |
1072 | + : </p> |
1073 | + : |
1074 | + : <p> |
1075 | + : This function does not allow arguments to be passed to the command. |
1076 | + : </p> |
1077 | : |
1078 | : @param $cmd command to be executed (without arguments) |
1079 | : |
1080 | - : @return the result of the execution as an element as |
1081 | - : shown in the documentation of this module. The exit-code |
1082 | - : element returns the exit code of the child process. |
1083 | - : For POSIX compliant platforms: returns the process exit code. If process is |
1084 | - : terminated or stopped: 128 + termination signal code. |
1085 | - : For Windows platforms: returns the return value of the process or the exit |
1086 | - : or terminate process specified value. |
1087 | + : @return the result of the execution as an object |
1088 | : |
1089 | - : @error process:PROC01 if an error occurred while communicating |
1090 | - : with the executed process. |
1091 | + : @error p:PROC01 if an error occurred while communicating with the process. |
1092 | :) |
1093 | -declare %an:sequential function process:exec( |
1094 | - $cmd as xs:string |
1095 | -) as element(process:result) external; |
1096 | +declare %an:sequential function p:exec-command( |
1097 | + $cmd as string |
1098 | +) as object external; |
1099 | |
1100 | (:~ |
1101 | + : <p> |
1102 | : Executes the specified string command in a separate process. |
1103 | + : </p> |
1104 | + : |
1105 | + : <p> |
1106 | : Each of the strings in the sequence passed in as the second |
1107 | : argument is passed as an argument to the executed command. |
1108 | + : </p> |
1109 | : |
1110 | : @param $cmd command to be executed (without arguments) |
1111 | : @param $args the arguments passed to the executed command (e.g. "-la") |
1112 | : |
1113 | - : @return the result of the execution as an element as |
1114 | - : shown in the documentation of this module. The exit-code |
1115 | - : element returns the exit code of the child process. |
1116 | - : For POSIX compliant platforms: returns the process exit code. If process is |
1117 | - : terminated or stopped: 128 + termination signal code. |
1118 | - : For Windows platforms: returns the return value of the process or the exit |
1119 | - : or terminate process specified value. |
1120 | + : @return the result of the execution as an object |
1121 | : |
1122 | - : @error process:PROC01 if an error occurred while communicating |
1123 | - : with the executed process. |
1124 | + : @error p:PROC01 if an error occurred while communicating with the process. |
1125 | :) |
1126 | -declare %an:sequential function process:exec( |
1127 | - $cmd as xs:string, |
1128 | - $args as xs:string* |
1129 | -) as element(process:result) external; |
1130 | +declare %an:sequential function p:exec-command( |
1131 | + $cmd as string, |
1132 | + $args as string* |
1133 | +) as object external; |
1134 | |
1135 | === renamed directory 'src/com/zorba-xquery/www/modules/process.xq.src' => 'src/process-2.xq.src' |
1136 | === modified file 'src/process-2.xq.src/process.cpp' |
1137 | --- src/com/zorba-xquery/www/modules/process.xq.src/process.cpp 2013-06-04 02:21:47 +0000 |
1138 | +++ src/process-2.xq.src/process.cpp 2013-06-13 10:54:38 +0000 |
1139 | @@ -1,5 +1,5 @@ |
1140 | /* |
1141 | - * Copyright 2006-2008 The FLWOR Foundation. |
1142 | + * Copyright 2006-2013 The FLWOR Foundation. |
1143 | * |
1144 | * Licensed under the Apache License, Version 2.0 (the "License"); |
1145 | * you may not use this file except in compliance with the License. |
1146 | @@ -25,7 +25,6 @@ |
1147 | |
1148 | #ifdef WIN32 |
1149 | # include <windows.h> |
1150 | - |
1151 | # ifndef NDEBUG |
1152 | # define _CRTDBG_MAP_ALLOC |
1153 | # include <stdlib.h> |
1154 | @@ -48,55 +47,45 @@ |
1155 | |
1156 | #include "process.h" |
1157 | |
1158 | +// Provde the execvpe() function since some platforms don't have it |
1159 | +#ifndef WIN32 |
1160 | +int execvpe(const char *program, char **argv, char **envp) |
1161 | +{ |
1162 | + char **saved = environ; |
1163 | + int rc; |
1164 | + environ = envp; |
1165 | + rc = execvp(program, argv); |
1166 | + environ = saved; |
1167 | + return rc; |
1168 | +} |
1169 | +#endif |
1170 | + |
1171 | + |
1172 | namespace zorba { |
1173 | namespace processmodule { |
1174 | |
1175 | /****************************************************************************** |
1176 | *****************************************************************************/ |
1177 | -void create_result_node( |
1178 | +void create_result_object( |
1179 | zorba::Item& aResult, |
1180 | const std::string& aStandardOut, |
1181 | const std::string& aErrorOut, |
1182 | int aExitCode, |
1183 | zorba::ItemFactory* aFactory) |
1184 | +{ |
1185 | + std::vector<std::pair<zorba::Item,zorba::Item> > pairs; |
1186 | + |
1187 | + pairs.push_back(std::pair<zorba::Item,zorba::Item>(aFactory->createString("exit-code"), aFactory->createInt(aExitCode))); |
1188 | + pairs.push_back(std::pair<zorba::Item,zorba::Item>(aFactory->createString("stdout"), aFactory->createString(aStandardOut))); |
1189 | + pairs.push_back(std::pair<zorba::Item,zorba::Item>(aFactory->createString("stderr"), aFactory->createString(aErrorOut))); |
1190 | + |
1191 | + aResult = aFactory->createJSONObject(pairs); |
1192 | +} |
1193 | + |
1194 | +void free_char_vector(std::vector<char*> argv) |
1195 | { |
1196 | - zorba::Item lResultQName = |
1197 | - aFactory->createQName("http://www.zorba-xquery.com/modules/process", "result"); |
1198 | - zorba::Item lExitCodeQName = |
1199 | - aFactory->createQName("http://www.zorba-xquery.com/modules/process", "exit-code"); |
1200 | - zorba::Item lOutputQName = |
1201 | - aFactory->createQName("http://www.zorba-xquery.com/modules/process", "stdout"); |
1202 | - zorba::Item lErrorQName = |
1203 | - aFactory->createQName("http://www.zorba-xquery.com/modules/process", "stderr"); |
1204 | - zorba::Item lNullItem; |
1205 | - zorba::Item lTypeName = |
1206 | - aFactory->createQName("http://www.w3.org/2001/XMLSchema", "untyped"); |
1207 | - |
1208 | - zorba::NsBindings lNSBindings; |
1209 | - |
1210 | - // root node called result |
1211 | - aResult = aFactory->createElementNode( |
1212 | - lNullItem, lResultQName, lTypeName, false, false, lNSBindings); |
1213 | - |
1214 | - // <result><output> aStandardOut </output></result> |
1215 | - zorba::Item lOutput; |
1216 | - lOutput = aFactory->createElementNode( |
1217 | - aResult, lOutputQName, lTypeName, true, false, lNSBindings); |
1218 | - aFactory->createTextNode(lOutput, aStandardOut); |
1219 | - |
1220 | - // <result><error> aErrorOut </error></result> |
1221 | - zorba::Item lError; |
1222 | - lError = aFactory->createElementNode( |
1223 | - aResult, lErrorQName, lTypeName, true, false, lNSBindings); |
1224 | - aFactory->createTextNode(lError, aErrorOut); |
1225 | - |
1226 | - // <result><exit-code> aExitCode </exit-code></result> |
1227 | - zorba::Item lExitCode; |
1228 | - lExitCode = aFactory->createElementNode( |
1229 | - aResult, lExitCodeQName, lTypeName, true, false, lNSBindings); |
1230 | - std::ostringstream lExitCodeString; |
1231 | - lExitCodeString << aExitCode; |
1232 | - aFactory->createTextNode(lExitCode, lExitCodeString.str()); |
1233 | + for (unsigned int i=0; i<argv.size(); i++) |
1234 | + free(argv[i]); |
1235 | } |
1236 | |
1237 | #ifdef WIN32 |
1238 | @@ -306,7 +295,7 @@ |
1239 | #define READ 0 |
1240 | #define WRITE 1 |
1241 | |
1242 | -pid_t zorba_popen(const char *command, int *infp, int *outfp, int *errfp) |
1243 | +pid_t exec_helper(int *infp, int *outfp, int *errfp, const char *command, char* argv[], char* env[]) |
1244 | { |
1245 | int p_stdin[2]; |
1246 | int p_stdout[2]; |
1247 | @@ -331,9 +320,28 @@ |
1248 | close(p_stderr[READ]); |
1249 | dup2(p_stderr[WRITE], 2); // duplicate stderr |
1250 | |
1251 | - execl("/bin/sh", "sh", "-c", command, NULL); |
1252 | + if (command) |
1253 | + execl("/bin/sh", "sh", "-c", command, NULL); |
1254 | + else if (env == NULL) |
1255 | + execvp(argv[0], argv); |
1256 | + else |
1257 | + execvpe(argv[0], argv, env); |
1258 | + |
1259 | perror("execl"); // output the result to standard error |
1260 | - exit(errno); |
1261 | + |
1262 | + // TODO: |
1263 | + // Currently, if the child process exits with an error, the following happens: |
1264 | + // -- exit(errno) is called |
1265 | + // -- static object destruction ocurrs |
1266 | + // -- Zorba store is destroyed in the child process and this leaks several URIs |
1267 | + // and prints error messages to stderr. An exception is thrown and this overwrites |
1268 | + // the exit code of the invoked process. |
1269 | + // |
1270 | + // Until a proper solution is found, the child fork() process will call abort(), which |
1271 | + // will not trigger static object destruction. |
1272 | + |
1273 | + abort(); |
1274 | + // exit(errno); |
1275 | } |
1276 | |
1277 | if (infp == NULL) |
1278 | @@ -357,10 +365,23 @@ |
1279 | |
1280 | return pid; |
1281 | } |
1282 | + |
1283 | #endif |
1284 | |
1285 | + |
1286 | /****************************************************************************** |
1287 | *****************************************************************************/ |
1288 | +String ExecFunction::getOneStringArgument (const Arguments_t& aArgs, int aPos) const |
1289 | +{ |
1290 | + Item lItem; |
1291 | + Iterator_t args_iter = aArgs[aPos]->getIterator(); |
1292 | + args_iter->open(); |
1293 | + args_iter->next(lItem); |
1294 | + zorba::String lTmpString = lItem.getStringValue(); |
1295 | + args_iter->close(); |
1296 | + return lTmpString; |
1297 | +} |
1298 | + |
1299 | zorba::ItemSequence_t |
1300 | ExecFunction::evaluate( |
1301 | const Arguments_t& aArgs, |
1302 | @@ -369,6 +390,7 @@ |
1303 | { |
1304 | std::string lCommand; |
1305 | std::vector<std::string> lArgs; |
1306 | + std::vector<std::string> lEnv; |
1307 | int exit_code = 0; |
1308 | |
1309 | lCommand = getOneStringArgument(aArgs, 0).c_str(); |
1310 | @@ -378,12 +400,20 @@ |
1311 | zorba::Item lArg; |
1312 | Iterator_t arg1_iter = aArgs[1]->getIterator(); |
1313 | arg1_iter->open(); |
1314 | - while (arg1_iter->next(lArg)) |
1315 | - { |
1316 | + while (arg1_iter->next(lArg)) |
1317 | lArgs.push_back(lArg.getStringValue().c_str()); |
1318 | - } |
1319 | arg1_iter->close(); |
1320 | } |
1321 | + |
1322 | + if (aArgs.size() > 2) |
1323 | + { |
1324 | + zorba::Item lArg; |
1325 | + Iterator_t arg1_iter = aArgs[2]->getIterator(); |
1326 | + arg1_iter->open(); |
1327 | + while (arg1_iter->next(lArg)) |
1328 | + lEnv.push_back(lArg.getStringValue().c_str()); |
1329 | + arg1_iter->close(); |
1330 | + } |
1331 | |
1332 | std::ostringstream lTmp; |
1333 | |
1334 | @@ -431,18 +461,38 @@ |
1335 | int errfp; |
1336 | int status; |
1337 | pid_t pid; |
1338 | + |
1339 | + std::vector<char*> argv(lArgs.size()+2, NULL); |
1340 | + std::vector<char*> env(lEnv.size()+1, NULL); |
1341 | |
1342 | - pid = zorba_popen(lTmp.str().c_str(), NULL, &outfp, &errfp); |
1343 | - if ( pid == -1 ) |
1344 | - { |
1345 | - std::stringstream lErrorMsg; |
1346 | - lErrorMsg << "Failed to execute the command (" << pid << ")"; |
1347 | - Item lQName = ProcessModule::getItemFactory()->createQName( |
1348 | - "http://www.zorba-xquery.com/modules/process", "PROC01"); |
1349 | - throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str()); |
1350 | - } |
1351 | - else |
1352 | - { |
1353 | + try |
1354 | + { |
1355 | + if (theIsExecProgram) |
1356 | + { |
1357 | + argv[0] = strdup(lCommand.c_str()); |
1358 | + for (unsigned int i=0; i<lArgs.size(); i++) |
1359 | + argv[i+1] = strdup(lArgs[i].c_str()); |
1360 | + |
1361 | + for (unsigned int i=0; i<lEnv.size(); i++) |
1362 | + env[i] = strdup(lEnv[i].c_str()); |
1363 | + |
1364 | + pid = exec_helper(NULL, &outfp, &errfp, NULL, argv.data(), lEnv.size() ? env.data() : NULL); |
1365 | + } |
1366 | + else |
1367 | + { |
1368 | + pid = exec_helper(NULL, &outfp, &errfp, lTmp.str().c_str(), argv.data(), NULL); |
1369 | + } |
1370 | + |
1371 | + if ( pid == -1 ) |
1372 | + { |
1373 | + std::stringstream lErrorMsg; |
1374 | + lErrorMsg << "Failed to execute the command (" << pid << ")"; |
1375 | + Item lQName = ProcessModule::getItemFactory()->createQName( |
1376 | + "http://www.zorba-xquery.com/modules/process", "PROC01"); |
1377 | + throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str()); |
1378 | + return NULL; |
1379 | + } |
1380 | + |
1381 | char lBuf[PATH_MAX]; |
1382 | ssize_t length = 0; |
1383 | while ( (length=read(outfp, lBuf, PATH_MAX)) > 0 ) |
1384 | @@ -503,28 +553,23 @@ |
1385 | } |
1386 | |
1387 | //std::cout << " exit_code : " << exit_code << std::endl; std::cout.flush(); |
1388 | - |
1389 | + free_char_vector(argv); |
1390 | + free_char_vector(env); |
1391 | + } |
1392 | + catch (...) |
1393 | + { |
1394 | + free_char_vector(argv); |
1395 | + free_char_vector(env); |
1396 | + throw; |
1397 | } |
1398 | #endif // WIN32 |
1399 | |
1400 | zorba::Item lResult; |
1401 | - create_result_node(lResult, lStdout.str(), lStderr.str(), exit_code, |
1402 | - theModule->getItemFactory()); |
1403 | - |
1404 | + create_result_object(lResult, lStdout.str(), lStderr.str(), exit_code, |
1405 | + theModule->getItemFactory()); |
1406 | return zorba::ItemSequence_t(new zorba::SingletonItemSequence(lResult)); |
1407 | } |
1408 | |
1409 | -String ExecFunction::getOneStringArgument (const Arguments_t& aArgs, int aPos) |
1410 | - const |
1411 | -{ |
1412 | - Item lItem; |
1413 | - Iterator_t args_iter = aArgs[aPos]->getIterator(); |
1414 | - args_iter->open(); |
1415 | - args_iter->next(lItem); |
1416 | - zorba::String lTmpString = lItem.getStringValue(); |
1417 | - args_iter->close(); |
1418 | - return lTmpString; |
1419 | -} |
1420 | |
1421 | /****************************************************************************** |
1422 | *****************************************************************************/ |
1423 | @@ -544,10 +589,14 @@ |
1424 | zorba::ExternalFunction*& lFunc = theFunctions[aLocalname]; |
1425 | if (lFind == theFunctions.end()) |
1426 | { |
1427 | - if (!aLocalname.compare("exec")) |
1428 | + if (aLocalname.compare("exec-command") == 0) |
1429 | { |
1430 | lFunc = new ExecFunction(this); |
1431 | } |
1432 | + else if (aLocalname.compare("exec") == 0) |
1433 | + { |
1434 | + lFunc = new ExecFunction(this, true); |
1435 | + } |
1436 | } |
1437 | return lFunc; |
1438 | } |
1439 | |
1440 | === modified file 'src/process-2.xq.src/process.h' |
1441 | --- src/com/zorba-xquery/www/modules/process.xq.src/process.h 2012-07-21 01:09:37 +0000 |
1442 | +++ src/process-2.xq.src/process.h 2013-06-13 10:54:38 +0000 |
1443 | @@ -1,5 +1,5 @@ |
1444 | /* |
1445 | - * Copyright 2006-2008 The FLWOR Foundation. |
1446 | + * Copyright 2006-2013 The FLWOR Foundation. |
1447 | * |
1448 | * Licensed under the Apache License, Version 2.0 (the "License"); |
1449 | * you may not use this file except in compliance with the License. |
1450 | @@ -51,7 +51,7 @@ |
1451 | virtual ~ProcessModule(); |
1452 | |
1453 | virtual zorba::String getURI() const |
1454 | - { return "http://www.zorba-xquery.com/modules/process"; } |
1455 | + { return "http://zorba.io/modules/process"; } |
1456 | |
1457 | virtual zorba::ExternalFunction* |
1458 | getExternalFunction(const zorba::String& aLocalname); |
1459 | @@ -74,12 +74,13 @@ |
1460 | class ExecFunction : public ContextualExternalFunction |
1461 | { |
1462 | public: |
1463 | - ExecFunction(const ProcessModule* aModule) : theModule(aModule) {} |
1464 | + ExecFunction(const ProcessModule* aModule, bool aExecProgram = false) |
1465 | + : theModule(aModule), theIsExecProgram(aExecProgram) {} |
1466 | |
1467 | virtual ~ExecFunction() {} |
1468 | |
1469 | virtual zorba::String |
1470 | - getLocalName() const { return "exec"; } |
1471 | + getLocalName() const { if (theIsExecProgram) return "exec"; else return "exec-command"; } |
1472 | |
1473 | virtual zorba::ItemSequence_t |
1474 | evaluate(const Arguments_t&, |
1475 | @@ -93,12 +94,14 @@ |
1476 | |
1477 | protected: |
1478 | const ProcessModule* theModule; |
1479 | + |
1480 | + bool theIsExecProgram; // if set to true, will use the execvpe() version of the system function |
1481 | + // if set to false, will build a command string and pass it to |
1482 | + // either "bash" or "cmd.exe" (through execl() on Linux) |
1483 | |
1484 | String getOneStringArgument (const Arguments_t& aArgs, int index) const; |
1485 | }; |
1486 | |
1487 | - |
1488 | - |
1489 | } /* namespace processmodule */ |
1490 | } /* namespace zorba */ |
1491 | |
1492 | |
1493 | === added file 'test/ExpQueryResults/process1-01.xml.res' |
1494 | --- test/ExpQueryResults/process1-01.xml.res 1970-01-01 00:00:00 +0000 |
1495 | +++ test/ExpQueryResults/process1-01.xml.res 2013-06-13 10:54:38 +0000 |
1496 | @@ -0,0 +1,2 @@ |
1497 | +<?xml version="1.0" encoding="UTF-8"?> |
1498 | +<result><out>hello world</out><err>Ooops. an error.</err></result> |
1499 | |
1500 | === added file 'test/ExpQueryResults/process2-01.xml.res' |
1501 | --- test/ExpQueryResults/process2-01.xml.res 1970-01-01 00:00:00 +0000 |
1502 | +++ test/ExpQueryResults/process2-01.xml.res 2013-06-13 10:54:38 +0000 |
1503 | @@ -0,0 +1,1 @@ |
1504 | +true |
1505 | \ No newline at end of file |
1506 | |
1507 | === added file 'test/ExpQueryResults/process2-02.xml.res' |
1508 | --- test/ExpQueryResults/process2-02.xml.res 1970-01-01 00:00:00 +0000 |
1509 | +++ test/ExpQueryResults/process2-02.xml.res 2013-06-13 10:54:38 +0000 |
1510 | @@ -0,0 +1,1 @@ |
1511 | +true |
1512 | \ No newline at end of file |
1513 | |
1514 | === added file 'test/ExpQueryResults/process2-03.xml.res' |
1515 | --- test/ExpQueryResults/process2-03.xml.res 1970-01-01 00:00:00 +0000 |
1516 | +++ test/ExpQueryResults/process2-03.xml.res 2013-06-13 10:54:38 +0000 |
1517 | @@ -0,0 +1,1 @@ |
1518 | +true |
1519 | \ No newline at end of file |
1520 | |
1521 | === added file 'test/ExpQueryResults/process2-04.xml.res' |
1522 | --- test/ExpQueryResults/process2-04.xml.res 1970-01-01 00:00:00 +0000 |
1523 | +++ test/ExpQueryResults/process2-04.xml.res 2013-06-13 10:54:38 +0000 |
1524 | @@ -0,0 +1,1 @@ |
1525 | +true |
1526 | \ No newline at end of file |
1527 | |
1528 | === added file 'test/ExpQueryResults/process2-05.xml.res' |
1529 | --- test/ExpQueryResults/process2-05.xml.res 1970-01-01 00:00:00 +0000 |
1530 | +++ test/ExpQueryResults/process2-05.xml.res 2013-06-13 10:54:38 +0000 |
1531 | @@ -0,0 +1,1 @@ |
1532 | +true |
1533 | \ No newline at end of file |
1534 | |
1535 | === added file 'test/ExpQueryResults/process2-06.xml.res' |
1536 | --- test/ExpQueryResults/process2-06.xml.res 1970-01-01 00:00:00 +0000 |
1537 | +++ test/ExpQueryResults/process2-06.xml.res 2013-06-13 10:54:38 +0000 |
1538 | @@ -0,0 +1,1 @@ |
1539 | +true |
1540 | \ No newline at end of file |
1541 | |
1542 | === renamed file 'test/ExpQueryResults/process.xml.res' => 'test/ExpQueryResults/process2-07.xml.res' |
1543 | === added file 'test/Queries/process1-01.xq' |
1544 | --- test/Queries/process1-01.xq 1970-01-01 00:00:00 +0000 |
1545 | +++ test/Queries/process1-01.xq 2013-06-13 10:54:38 +0000 |
1546 | @@ -0,0 +1,24 @@ |
1547 | +import module namespace proc = "http://www.zorba-xquery.com/modules/process#1.0"; |
1548 | + |
1549 | + |
1550 | +{ |
1551 | + variable $stdOutTest := proc:exec("echo","hello world") ; |
1552 | + variable $stdErrTest := proc:exec("echo","Ooops. an error. 1>&2"); |
1553 | + variable $stdOutWinTest := proc:exec("cmd", ("/c", "echo","hello world")) ; |
1554 | + variable $stdErrWinTest := proc:exec("cmd", ("/c", "echo","Ooops. an error. 1>&2")); |
1555 | + |
1556 | + let $result := |
1557 | + <result> |
1558 | + <out>{normalize-space(data($stdOutTest/proc:stdout))}</out> |
1559 | + <err>{normalize-space(data($stdErrTest/proc:stderr))}</err> |
1560 | + </result> |
1561 | + return |
1562 | + if (contains($result/err/text(),"is not recognized as an internal or external command")) |
1563 | + then |
1564 | + <result> |
1565 | + <out>{normalize-space(data($stdOutWinTest/proc:stdout))}</out> |
1566 | + <err>{normalize-space(data($stdErrWinTest/proc:stderr))}</err> |
1567 | + </result> |
1568 | + else |
1569 | + $result |
1570 | +} |
1571 | |
1572 | === added file 'test/Queries/process2-01.xq' |
1573 | --- test/Queries/process2-01.xq 1970-01-01 00:00:00 +0000 |
1574 | +++ test/Queries/process2-01.xq 2013-06-13 10:54:38 +0000 |
1575 | @@ -0,0 +1,5 @@ |
1576 | +import module namespace proc = "http://zorba.io/modules/process"; |
1577 | + |
1578 | +let $result := proc:exec("echo") |
1579 | +return $result("stdout") eq " |
1580 | +" |
1581 | |
1582 | === added file 'test/Queries/process2-02.xq' |
1583 | --- test/Queries/process2-02.xq 1970-01-01 00:00:00 +0000 |
1584 | +++ test/Queries/process2-02.xq 2013-06-13 10:54:38 +0000 |
1585 | @@ -0,0 +1,5 @@ |
1586 | +import module namespace proc = "http://zorba.io/modules/process"; |
1587 | + |
1588 | +let $result := proc:exec("echo",("hello","world")) |
1589 | +return $result("stdout") eq "hello world |
1590 | +" |
1591 | |
1592 | === added file 'test/Queries/process2-03.xq' |
1593 | --- test/Queries/process2-03.xq 1970-01-01 00:00:00 +0000 |
1594 | +++ test/Queries/process2-03.xq 2013-06-13 10:54:38 +0000 |
1595 | @@ -0,0 +1,5 @@ |
1596 | +import module namespace proc = "http://zorba.io/modules/process"; |
1597 | + |
1598 | +let $result := proc:exec("printenv",("TEST_ENV_VAR"),"TEST_ENV_VAR=foo") |
1599 | +return $result("stdout") eq "foo |
1600 | +" |
1601 | |
1602 | === added file 'test/Queries/process2-04.xq' |
1603 | --- test/Queries/process2-04.xq 1970-01-01 00:00:00 +0000 |
1604 | +++ test/Queries/process2-04.xq 2013-06-13 10:54:38 +0000 |
1605 | @@ -0,0 +1,6 @@ |
1606 | +import module namespace proc = "http://zorba.io/modules/process"; |
1607 | + |
1608 | +let $result := proc:exec("printenv",("TEST_ENV_VAR","VAR2"),("TEST_ENV_VAR=foo","VAR2=bar")) |
1609 | +return $result("stdout") eq "foo |
1610 | +bar |
1611 | +" |
1612 | |
1613 | === added file 'test/Queries/process2-05.xq' |
1614 | --- test/Queries/process2-05.xq 1970-01-01 00:00:00 +0000 |
1615 | +++ test/Queries/process2-05.xq 2013-06-13 10:54:38 +0000 |
1616 | @@ -0,0 +1,5 @@ |
1617 | +import module namespace proc = "http://zorba.io/modules/process"; |
1618 | + |
1619 | +let $result := proc:exec("echo","{}[]()()''~!@#$%^&*_-+|<>/?,.") |
1620 | +return $result("stdout") eq "{}[]()()''~!@#$%^&*_-+|<>/?,. |
1621 | +" |
1622 | |
1623 | === added file 'test/Queries/process2-06.xq' |
1624 | --- test/Queries/process2-06.xq 1970-01-01 00:00:00 +0000 |
1625 | +++ test/Queries/process2-06.xq 2013-06-13 10:54:38 +0000 |
1626 | @@ -0,0 +1,4 @@ |
1627 | +import module namespace proc = "http://zorba.io/modules/process"; |
1628 | + |
1629 | +let $result := proc:exec("this_executable_does_not_exist") |
1630 | +return $result("exit-code") ne 0 |
1631 | |
1632 | === renamed file 'test/Queries/process.xq' => 'test/Queries/process2-07.xq' |
1633 | --- test/Queries/process.xq 2011-08-13 00:08:53 +0000 |
1634 | +++ test/Queries/process2-07.xq 2013-06-13 10:54:38 +0000 |
1635 | @@ -1,23 +1,23 @@ |
1636 | -import module namespace proc = "http://www.zorba-xquery.com/modules/process"; |
1637 | +import module namespace proc = "http://zorba.io/modules/process"; |
1638 | |
1639 | |
1640 | { |
1641 | - variable $stdOutTest := proc:exec("echo","hello world") ; |
1642 | - variable $stdErrTest := proc:exec("echo","Ooops. an error. 1>&2"); |
1643 | - variable $stdOutWinTest := proc:exec("cmd", ("/c", "echo","hello world")) ; |
1644 | - variable $stdErrWinTest := proc:exec("cmd", ("/c", "echo","Ooops. an error. 1>&2")); |
1645 | + variable $stdOutTest := proc:exec-command("echo","hello world") ; |
1646 | + variable $stdErrTest := proc:exec-command("echo","Ooops. an error. 1>&2"); |
1647 | + variable $stdOutWinTest := proc:exec-command("cmd", ("/c", "echo","hello world")) ; |
1648 | + variable $stdErrWinTest := proc:exec-command("cmd", ("/c", "echo","Ooops. an error. 1>&2")); |
1649 | |
1650 | let $result := |
1651 | <result> |
1652 | - <out>{normalize-space(data($stdOutTest/proc:stdout))}</out> |
1653 | - <err>{normalize-space(data($stdErrTest/proc:stderr))}</err> |
1654 | + <out>{normalize-space(data($stdOutTest("stdout")))}</out> |
1655 | + <err>{normalize-space(data($stdErrTest("stderr")))}</err> |
1656 | </result> |
1657 | return |
1658 | if (contains($result/err/text(),"is not recognized as an internal or external command")) |
1659 | then |
1660 | <result> |
1661 | - <out>{normalize-space(data($stdOutWinTest/proc:stdout))}</out> |
1662 | - <err>{normalize-space(data($stdErrWinTest/proc:stderr))}</err> |
1663 | + <out>{normalize-space(data($stdOutWinTest("stdout")))}</out> |
1664 | + <err>{normalize-space(data($stdErrWinTest("stderr")))}</err> |
1665 | </result> |
1666 | else |
1667 | $result |
Validation queue starting for merge proposal. zorbatest. lambda. nu:8080/ remotequeue/ process- 2-2013- 05-17T14- 50-41.743Z/ log.html
Log at: http://