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