2
* Copyright 2006-2008 The FLWOR Foundation.
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
* you may not use this file except in compliance with the License.
6
* You may obtain a copy of the License at
8
* http://www.apache.org/licenses/LICENSE-2.0
10
* Unless required by applicable law or agreed to in writing, software
11
* distributed under the License is distributed on an "AS IS" BASIS,
12
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
* See the License for the specific language governing permissions and
14
* limitations under the License.
30
# define _CRTDBG_MAP_ALLOC
37
# include <sys/wait.h>
43
#include <zorba/item_factory.h>
44
#include <zorba/singleton_item_sequence.h>
45
#include <zorba/diagnostic_list.h>
46
#include <zorba/user_exception.h>
47
#include <zorba/empty_sequence.h>
52
namespace processmodule {
54
/******************************************************************************
55
*****************************************************************************/
56
void create_result_node(
58
const std::string& aStandardOut,
59
const std::string& aErrorOut,
61
zorba::ItemFactory* aFactory)
63
zorba::Item lResultQName =
64
aFactory->createQName("http://www.zorba-xquery.com/modules/process", "result");
65
zorba::Item lExitCodeQName =
66
aFactory->createQName("http://www.zorba-xquery.com/modules/process", "exit-code");
67
zorba::Item lOutputQName =
68
aFactory->createQName("http://www.zorba-xquery.com/modules/process", "stdout");
69
zorba::Item lErrorQName =
70
aFactory->createQName("http://www.zorba-xquery.com/modules/process", "stderr");
71
zorba::Item lNullItem;
72
zorba::Item lTypeName =
73
aFactory->createQName("http://www.w3.org/2001/XMLSchema", "untyped");
75
zorba::NsBindings lNSBindings;
77
// root node called result
78
aResult = aFactory->createElementNode(
79
lNullItem, lResultQName, lTypeName, false, false, lNSBindings);
81
// <result><output> aStandardOut </output></result>
83
lOutput = aFactory->createElementNode(
84
aResult, lOutputQName, lTypeName, true, false, lNSBindings);
85
aFactory->createTextNode(lOutput, aStandardOut);
87
// <result><error> aErrorOut </error></result>
89
lError = aFactory->createElementNode(
90
aResult, lErrorQName, lTypeName, true, false, lNSBindings);
91
aFactory->createTextNode(lError, aErrorOut);
93
// <result><exit-code> aExitCode </exit-code></result>
94
zorba::Item lExitCode;
95
lExitCode = aFactory->createElementNode(
96
aResult, lExitCodeQName, lTypeName, true, false, lNSBindings);
97
std::ostringstream lExitCodeString;
98
lExitCodeString << aExitCode;
99
aFactory->createTextNode(lExitCode, lExitCodeString.str());
104
/***********************************************
105
* throw a descriptive message of the last error
106
* accessible with GetLastError() on windows
108
void throw_last_error(const zorba::String& aFilename, unsigned int aLineNumber){
109
LPVOID lpvMessageBuffer;
110
TCHAR lErrorBuffer[512];
111
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
112
NULL, GetLastError(),
113
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
114
(LPTSTR)&lpvMessageBuffer, 0, NULL);
115
wsprintf(lErrorBuffer,TEXT("Process Error Code: %d - Message= %s"),GetLastError(), (TCHAR *)lpvMessageBuffer);
116
LocalFree(lpvMessageBuffer);
117
Item lQName = ProcessModule::getItemFactory()->createQName(
118
"http://www.zorba-xquery.com/modules/process",
121
char error_str[1024];
122
WideCharToMultiByte(CP_UTF8, 0, lErrorBuffer, -1, error_str, sizeof(error_str), NULL, NULL);
123
throw USER_EXCEPTION(lQName, error_str);
125
throw USER_EXCEPTION(lQName, lErrorBuffer);
129
/******************************************
130
* read output from child process on windows
132
void read_child_output(HANDLE aOutputPipe, std::ostringstream& aTargetStream)
140
!ReadFile(aOutputPipe,lBuffer,sizeof(lBuffer),&lBytesRead,NULL)
144
if (GetLastError() == ERROR_BROKEN_PIPE)
148
// couldn't read from pipe
149
throw_last_error(__FILE__, __LINE__);
153
// remove the windows specific carriage return outputs
154
// std::stringstream lTmp;
155
// lTmp.write(lBuffer,lBytesRead);
156
// std::string lRawString=lTmp.str();
157
// std::replace( lRawString.begin(), lRawString.end(), '\r', ' ' );
158
// aTargetStream.write(lRawString.c_str(),static_cast<std::streamsize>(lRawString.length()));
159
for(DWORD i=0;i<lBytesRead;i++)
161
if(lBuffer[i] != '\r')
162
aTargetStream << lBuffer[i];
168
/******************************************
169
* Create a child process on windows with
172
BOOL create_child_process(HANDLE aStdOutputPipe,HANDLE aStdErrorPipe,const std::string& aCommand,PROCESS_INFORMATION& aProcessInformation){
173
STARTUPINFO lChildStartupInfo;
176
// set the output handles
177
FillMemory(&lChildStartupInfo,sizeof(lChildStartupInfo),0);
178
lChildStartupInfo.cb = sizeof(lChildStartupInfo);
179
GetStartupInfo(&lChildStartupInfo);
180
lChildStartupInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
181
lChildStartupInfo.wShowWindow = SW_HIDE; // don't show the command window
182
lChildStartupInfo.hStdOutput = aStdOutputPipe;
183
lChildStartupInfo.hStdError = aStdErrorPipe;
185
// convert from const char* to char*
186
size_t length = strlen(aCommand.c_str());
188
WCHAR *tmpCommand = new WCHAR[length+1];
189
MultiByteToWideChar(CP_UTF8, 0, aCommand.c_str(), -1, tmpCommand, length+1);
191
char *tmpCommand=new char[length+1];
192
strcpy (tmpCommand,aCommand.c_str());
193
tmpCommand[length]='\0';
198
// settings for the child process
199
LPCTSTR lApplicationName=NULL;
200
LPTSTR lCommandLine=tmpCommand;
201
LPSECURITY_ATTRIBUTES lProcessAttributes=NULL;
202
LPSECURITY_ATTRIBUTES lThreadAttributes=NULL;
203
BOOL lInheritHandles=TRUE; // that's what we want
204
DWORD lCreationFlags=CREATE_NEW_CONSOLE;
205
LPVOID lEnvironment=NULL;
206
LPCTSTR lCurrentDirectory=NULL; // same as main process
209
result=CreateProcess(
210
lApplicationName,lCommandLine,lProcessAttributes,
211
lThreadAttributes,lInheritHandles,lCreationFlags,
212
lEnvironment,lCurrentDirectory,&lChildStartupInfo,
213
&aProcessInformation);
227
/******************************************
228
* run a process that executes the aCommand
229
* in a new console and reads the output
232
const std::string& aCommand,
233
std::ostringstream& aTargetOutStream,
234
std::ostringstream& aTargetErrStream)
236
HANDLE lOutRead, lErrRead, lStdOut, lStdErr;
237
SECURITY_ATTRIBUTES lSecurityAttributes;
238
PROCESS_INFORMATION lChildProcessInfo;
241
// prepare security attributes
242
lSecurityAttributes.nLength= sizeof(lSecurityAttributes);
243
lSecurityAttributes.lpSecurityDescriptor = NULL;
244
lSecurityAttributes.bInheritHandle = TRUE;
246
// create output pipes
248
!CreatePipe(&lOutRead,&lStdOut,&lSecurityAttributes,1024*1024) // std::cout >> lOutRead
249
|| !CreatePipe(&lErrRead,&lStdErr,&lSecurityAttributes,1024*1024) // std::cerr >> lErrRead
251
Item lQName = ProcessModule::getItemFactory()->createQName(
252
"http://www.zorba-xquery.com/modules/process", "PROC01");
253
throw USER_EXCEPTION(lQName,
254
"Couldn't create one of std::cout/std::cerr pipe for child process execution."
258
//start child process
259
BOOL ok = create_child_process(lStdOut,lStdErr,aCommand,lChildProcessInfo);
263
// close unneeded handle
264
CloseHandle(lChildProcessInfo.hThread);
266
// wait for the process to finish
267
WaitForSingleObject(lChildProcessInfo.hProcess,INFINITE);
268
if (!GetExitCodeProcess(lChildProcessInfo.hProcess, &exitCode))
270
std::stringstream lErrorMsg;
272
<< "Couldn't get exit code from child process. Executed command: '" << aCommand << "'.";
273
Item lQName = ProcessModule::getItemFactory()->createQName(
274
"http://www.zorba-xquery.com/modules/process", "PROC01");
275
throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str());
278
CloseHandle(lChildProcessInfo.hProcess);
279
CloseHandle(lStdOut);
280
CloseHandle(lStdErr);
282
// read child's output
283
read_child_output(lOutRead,aTargetOutStream);
284
read_child_output(lErrRead,aTargetErrStream);
287
CloseHandle(lOutRead);
288
CloseHandle(lErrRead);
291
CloseHandle(lStdOut);
292
CloseHandle(lStdErr);
293
CloseHandle(lOutRead);
294
CloseHandle(lErrRead);
296
// couldn't launch process
297
throw_last_error(__FILE__, __LINE__);
309
pid_t zorba_popen(const char *command, int *infp, int *outfp, int *errfp)
316
if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0 || pipe(p_stderr) != 0)
325
close(p_stdin[WRITE]);
326
dup2(p_stdin[READ], 0); // duplicate stdin
328
close(p_stdout[READ]);
329
dup2(p_stdout[WRITE], 1); // duplicate stdout
331
close(p_stderr[READ]);
332
dup2(p_stderr[WRITE], 2); // duplicate stderr
334
execl("/bin/sh", "sh", "-c", command, NULL);
335
perror("execl"); // output the result to standard error
340
close(p_stdin[WRITE]);
342
*infp = p_stdin[WRITE];
345
close(p_stdout[READ]);
347
*outfp = p_stdout[READ];
350
close(p_stderr[READ]);
352
*errfp = p_stderr[READ];
354
close(p_stdin[READ]); // We only write to the forks stdin anyway
355
close(p_stdout[WRITE]); // and we only read from its stdout
356
close(p_stderr[WRITE]); // and we only read from its stderr
362
/******************************************************************************
363
*****************************************************************************/
364
zorba::ItemSequence_t
365
ExecFunction::evaluate(
366
const Arguments_t& aArgs,
367
const zorba::StaticContext* aSctx,
368
const zorba::DynamicContext* aDctx) const
370
std::string lCommand;
371
std::vector<std::string> lArgs;
374
lCommand = getOneStringArgument(aArgs, 0).c_str();
376
if (aArgs.size() > 1)
379
Iterator_t arg1_iter = aArgs[1]->getIterator();
381
while (arg1_iter->next(lArg))
383
lArgs.push_back(lArg.getStringValue().c_str());
388
std::ostringstream lTmp;
391
// execute process command in a new commandline
392
// with quotes at the beggining and at the end
396
lTmp << "\"" << lCommand << "\""; //quoted for spaced paths/filenames
398
for (std::vector<std::string>::const_iterator lIter = lArgs.begin();
399
lIter != lArgs.end(); ++lIter)
401
pos = (*lIter).rfind('\\')+(*lIter).rfind('/');
403
lTmp << " \"" << *lIter << "\"";
405
lTmp << " " << *lIter;
408
lTmp << "\""; // with quotes at the end for commandline
411
std::ostringstream lStdout;
412
std::ostringstream lStderr;
415
std::string lCommandLineString = lTmp.str();
416
int code = run_process(lCommandLineString, lStdout, lStderr);
420
std::stringstream lErrorMsg;
421
lErrorMsg << "Failed to execute the command (" << code << ")";
422
Item lQName = ProcessModule::getItemFactory()->createQName(
423
"http://www.zorba-xquery.com/modules/process", "PROC01");
424
throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str());
435
pid = zorba_popen(lTmp.str().c_str(), NULL, &outfp, &errfp);
438
std::stringstream lErrorMsg;
439
lErrorMsg << "Failed to execute the command (" << pid << ")";
440
Item lQName = ProcessModule::getItemFactory()->createQName(
441
"http://www.zorba-xquery.com/modules/process", "PROC01");
442
throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str());
448
while ( (length=read(outfp, lBuf, PATH_MAX)) > 0 )
450
lStdout.write(lBuf, length);
453
status = close(outfp);
455
while ( (length=read(errfp, lBuf, PATH_MAX)) > 0 )
457
lStderr.write(lBuf, length);
460
status = close(errfp);
464
std::stringstream lErrorMsg;
465
lErrorMsg << "Failed to close the err stream (" << status << ")";
466
Item lQName = ProcessModule::getItemFactory()->createQName(
467
"http://www.zorba-xquery.com/modules/process", "PROC01");
468
throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str());
473
pid_t w = waitpid(pid, &stat, 0);
477
std::stringstream lErrorMsg;
478
lErrorMsg << "Failed to wait for child process ";
479
Item lQName = ProcessModule::getItemFactory()->createQName(
480
"http://www.zorba-xquery.com/modules/process", "PROC01");
481
throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str());
486
//std::cout << " WEXITSTATUS : " << WEXITSTATUS(stat) << std::endl; std::cout.flush();
487
exit_code = WEXITSTATUS(stat);
489
else if (WIFSIGNALED(stat))
491
//std::cout << " WTERMSIG : " << WTERMSIG(stat) << std::endl; std::cout.flush();
492
exit_code = 128 + WTERMSIG(stat);
494
else if (WIFSTOPPED(stat))
496
//std::cout << " STOPSIG : " << WSTOPSIG(stat) << std::endl; std::cout.flush();
497
exit_code = 128 + WSTOPSIG(stat);
501
//std::cout << " else : " << std::endl; std::cout.flush();
505
//std::cout << " exit_code : " << exit_code << std::endl; std::cout.flush();
511
create_result_node(lResult, lStdout.str(), lStderr.str(), exit_code,
512
theModule->getItemFactory());
514
return zorba::ItemSequence_t(new zorba::SingletonItemSequence(lResult));
517
String ExecFunction::getOneStringArgument (const Arguments_t& aArgs, int aPos)
521
Iterator_t args_iter = aArgs[aPos]->getIterator();
523
args_iter->next(lItem);
524
zorba::String lTmpString = lItem.getStringValue();
529
/******************************************************************************
530
*****************************************************************************/
531
ProcessModule::~ProcessModule()
533
for (FuncMap_t::const_iterator lIter = theFunctions.begin();
534
lIter != theFunctions.end(); ++lIter) {
535
delete lIter->second;
537
theFunctions.clear();
540
zorba::ExternalFunction*
541
ProcessModule::getExternalFunction(const zorba::String& aLocalname)
543
FuncMap_t::const_iterator lFind = theFunctions.find(aLocalname);
544
zorba::ExternalFunction*& lFunc = theFunctions[aLocalname];
545
if (lFind == theFunctions.end())
547
if (!aLocalname.compare("exec"))
549
lFunc = new ExecFunction(this);
555
void ProcessModule::destroy()
557
if (!dynamic_cast<ProcessModule*>(this)) {
563
ItemFactory* ProcessModule::theFactory = 0;
565
} /* namespace processmodule */
566
} /* namespace zorba */
569
# define DLL_EXPORT __declspec(dllexport)
571
# define DLL_EXPORT __attribute__ ((visibility("default")))
574
extern "C" DLL_EXPORT zorba::ExternalModule* createModule() {
575
return new zorba::processmodule::ProcessModule();
577
/* vim:set et sw=2 ts=2: */