1 /*
2  *  Process.cpp
3  *  OpenLieroX
4  *
5  *  Created by Albert Zeyer on 10.02.09.
6  *  code under LGPL
7  *
8  */
9 
10 #include <cassert>
11 #include "Process.h"
12 #include "Debug.h"
13 #include "FindFile.h"
14 
15 #if ( ! defined(HAVE_BOOST) && defined(WIN32) ) || ( defined(_MSC_VER) && (_MSC_VER <= 1200) )
16 
17 #include <windows.h>
18 
19 struct ProcessIntern	// Stub
20 {
21 	int dummy;
ProcessInternProcessIntern22 	ProcessIntern(): dummy(0) {};
inProcessIntern23 	std::ostream & in(){ return std::cout; };
outProcessIntern24 	std::istream & out(){ return std::cin; };
closeProcessIntern25 	void close() {  }
openProcessIntern26 	bool open( const std::string & cmd, std::vector< std::string > params, const std::string& working_dir )
27 	{
28 		errors << "Dedicated server is not compiled into this version of OpenLieroX" << endl;
29 		MessageBox( NULL, "ERROR: Dedicated server is not compiled into this version of OpenLieroX", "OpenLieroX", MB_OK );
30 		return false;
31 	}
32 };
33 
34 #elif WIN32
35 
36 // Install Boost headers for your compiler and #define HAVE_BOOST to compile dedicated server for Win32
37 // You don't need to link to any lib to compile it, just headers.
38 #ifdef new  // Boost is incompatible with leak detection in MSVC, just disable it for the boost headers
39 #undef new
40 #include <boost/process.hpp> // This one header pulls turdy shitload of Boost headers
41 #define new DEBUG_NEW // Re-enable
42 #else
43 #include <boost/process.hpp>
44 #endif
45 
46 struct ProcessIntern
47 {
48 	boost::process::child *p;
ProcessInternProcessIntern49 	ProcessIntern(): p(NULL) {};
inProcessIntern50 	std::ostream & in() { assert(p != NULL); return p->get_stdin(); };
outProcessIntern51 	std::istream & out() { assert(p != NULL); return p->get_stdout(); };
closeProcessIntern52 	void close() { if (p) { p->get_stdin().close(); } }
openProcessIntern53 	bool open( const std::string & cmd, std::vector< std::string > params, const std::string& working_dir )
54 	{
55 		if(p)
56 			delete p;
57 
58 		for (std::vector<std::string>::iterator it = params.begin(); it != params.end(); it++)
59 			*it = Utf8ToSystemNative(*it);
60 
61 		boost::process::context ctx;
62 		ctx.m_stdin_behavior = boost::process::capture_stream(); // Pipe for win32
63 		ctx.m_stdout_behavior = boost::process::capture_stream();
64 		ctx.m_stderr_behavior = boost::process::close_stream(); // we don't grap the stderr, it is not outputted anywhere, sadly
65 		ctx.m_work_directory = Utf8ToSystemNative(working_dir);
66 		if(ctx.m_work_directory == "")
67 			ctx.m_work_directory = Utf8ToSystemNative(".");
68 		try
69 		{
70 			p = new boost::process::child(boost::process::launch(Utf8ToSystemNative(cmd), params, ctx)); // Throws exception on error
71 		}
72 		catch( const std::exception & e )
73 		{
74 			errors << "Error running command " << cmd << " : " << e.what() << endl;
75 			return false;
76 		}
77 		return true;
78 	}
~ProcessInternProcessIntern79 	~ProcessIntern(){ close(); if(p) delete p; };
80 };
81 
82 #else
83 #include <pstream.h>
84 struct ProcessIntern
85 {
ProcessInternProcessIntern86 	ProcessIntern() : p(NULL) {}
~ProcessInternProcessIntern87 	~ProcessIntern() { close(); reset(); }
88 	redi::pstream* p;
inProcessIntern89 	std::ostream & in() { return *p; };
outProcessIntern90 	std::istream & out() { return p->out(); };
resetProcessIntern91 	void reset() { if(p) delete p; p = NULL; }
closeProcessIntern92 	void close() {
93 		// p << redi::peof;
94 		if(!p)
95 			return;
96 		if(p->rdbuf()) p->rdbuf()->kill();
97 		if(p->rdbuf()) p->rdbuf()->kill(SIGKILL);
98 	}
openProcessIntern99 	bool open( const std::string & cmd, std::vector< std::string > params, const std::string& working_dir )
100 	{
101 		reset(); p = new redi::pstream();
102 		p->open( cmd, params, redi::pstreams::pstdin | redi::pstreams::pstdout, working_dir ); // we don't grap the stderr, it should directly be forwarded to console
103 		return p->rdbuf()->error() == 0;
104 	}
105 };
106 #endif
107 
108 
Process()109 Process::Process() {
110 	data = new ProcessIntern();
111 }
112 
~Process()113 Process::~Process() {
114 	assert(data != NULL);
115 	delete data;
116 	data = NULL;
117 }
118 
119 
in()120 std::ostream& Process::in() { return data->in(); }
out()121 std::istream& Process::out() { return data->out(); }
close()122 void Process::close() { data->close(); }
123 
124 #ifdef WIN32
125 struct SysCommand {
126 	std::string exec;
127 	std::vector<std::string> params;
128 };
129 
GetExecForScriptInterpreter(const std::string & interpreter)130 static SysCommand GetExecForScriptInterpreter(const std::string& interpreter) {
131 	SysCommand ret;
132 	std::string& command = ret.exec;
133 	std::vector<std::string>& commandArgs = ret.params;
134 
135 	std::string cmdPathRegKey = "";
136 	std::string cmdPathRegValue = "";
137 	// TODO: move that out to an own function!
138 	if( interpreter == "python" )
139 	{
140 		// TODO: move that out to an own function!
141 		command = "python.exe";
142 		commandArgs.clear();
143 		commandArgs.push_back(command);
144 		commandArgs.push_back("-u");
145 		cmdPathRegKey = "SOFTWARE\\Python\\PythonCore\\2.5\\InstallPath";
146 	}
147 	else if( interpreter == "bash" )
148 	{
149 		// TODO: move that out to an own function!
150 		command = "bash.exe";
151 		commandArgs.clear();
152 		commandArgs.push_back(command);
153 		//commandArgs.push_back("-l");	// Not needed for Cygwin
154 		commandArgs.push_back("-c");
155 		cmdPathRegKey = "SOFTWARE\\Cygnus Solutions\\Cygwin\\mounts v2\\/usr/bin";
156 		cmdPathRegValue = "native";
157 	}
158 	else if( interpreter == "php" )
159 	{
160 		// TODO: move that out to an own function!
161 		command = "php.exe";
162 		commandArgs.clear();
163 		commandArgs.push_back(command);
164 		commandArgs.push_back("-f");
165 		cmdPathRegKey = "SOFTWARE\\PHP";
166 		cmdPathRegValue = "InstallDir";
167 	}
168 	else
169 	{
170 		command = interpreter + ".exe";
171 	}
172 
173 	// TODO: move that out to an own function!
174 	if( cmdPathRegKey != "" )
175 	{
176 		HKEY hKey;
177 		LONG returnStatus;
178 		DWORD dwType=REG_SZ;
179 		char lszCmdPath[256]="";
180 		DWORD dwSize=255;
181 		returnStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, cmdPathRegKey.c_str(), 0L,  KEY_READ, &hKey);
182 		if (returnStatus != ERROR_SUCCESS)
183 		{
184 			errors << "registry key " << cmdPathRegKey << "\\" << cmdPathRegValue << " not found - make sure interpreter is installed" << endl;
185 			lszCmdPath[0] = '\0'; // Perhaps it is installed in PATH
186 		}
187 		returnStatus = RegQueryValueEx(hKey, cmdPathRegValue.c_str(), NULL, &dwType,(LPBYTE)lszCmdPath, &dwSize);
188 		RegCloseKey(hKey);
189 		if (returnStatus != ERROR_SUCCESS)
190 		{
191 			errors << "registry key " << cmdPathRegKey << "\\" << cmdPathRegValue << " could not be read - make sure interpreter is installed" << endl;
192 			lszCmdPath[0] = '\0'; // Perhaps it is installed in PATH
193 		}
194 
195 		// Add trailing slash if needed
196 		std::string path(lszCmdPath);
197 		if (path.size())  {
198 			if (*path.rbegin() != '\\' && *path.rbegin() != '/')
199 				path += '\\';
200 		}
201 		command = std::string(lszCmdPath) + command;
202 		commandArgs[0] = command;
203 	}
204 
205 	return ret;
206 }
207 #endif
208 
open(const std::string & cmd,std::vector<std::string> params,const std::string & working_dir)209 bool Process::open( const std::string & cmd, std::vector< std::string > params, const std::string& working_dir ) {
210 	if(params.size() == 0)
211 		params.push_back(cmd);
212 
213 	std::string realcmd = cmd;
214 #ifdef WIN32
215 	std::string interpreter = GetScriptInterpreterCommandForFile(cmd);
216 	if(interpreter != "") {
217 		size_t f = interpreter.find(" ");
218 		if(f != std::string::npos) interpreter.erase(f);
219 		interpreter = GetBaseFilename(interpreter);
220 		SysCommand newcmd = GetExecForScriptInterpreter(interpreter);
221 		realcmd = newcmd.exec;
222 		params.swap( newcmd.params );
223 		params.reserve( params.size() + newcmd.params.size() );
224 		for(std::vector<std::string>::iterator i = newcmd.params.begin(); i != newcmd.params.end(); ++i)
225 			params.push_back(*i);
226 
227 		notes << "running \"" << realcmd << "\" for script \"" << cmd << "\"" << endl;
228 	}
229 #endif
230 
231 	return data->open( realcmd, params, working_dir );
232 }
233 
234