136{
137 if (mIsRunning) {
138 printf("Warning: Experiment already running\n");
139 return false;
140 }
141
142 std::string peblPath = GetPEBLExecutablePath();
143
144
145 std::string workingDir;
146 size_t lastSlash = scriptPath.find_last_of("/\\");
147 if (lastSlash != std::string::npos) {
148 workingDir = scriptPath.substr(0, lastSlash);
149 } else {
150 workingDir = ".";
151 }
152
153
154 std::string scriptFilename;
155 if (lastSlash != std::string::npos) {
156 scriptFilename = scriptPath.substr(lastSlash + 1);
157 } else {
158 scriptFilename = scriptPath;
159 }
160
161
162 std::vector<std::string> fullArgs;
163
164
165 for (const auto& arg : args) {
166 fullArgs.push_back(arg);
167 }
168
169
170 if (!subjectCode.empty()) {
171 fullArgs.push_back("-s");
172 fullArgs.push_back(subjectCode);
173 }
174
175
176 if (!language.empty()) {
177 fullArgs.push_back("--language");
178 fullArgs.push_back(language);
179 }
180
181
182 if (fullscreen) {
183 fullArgs.push_back("--fullscreen");
184 }
185
186#ifdef _WIN32
187
188
189 std::string winPeblPath = NormalizeWindowsPath(peblPath);
190 std::string winWorkingDir = NormalizeWindowsPath(workingDir);
191
192 std::string cmdLine = "\"" + winPeblPath + "\" \"" + scriptFilename + "\"";
193
194
195 for (const auto& arg : fullArgs) {
196 cmdLine += " ";
197 if (arg.find(' ') != std::string::npos) {
198 cmdLine += "\"" + arg + "\"";
199 } else {
200 cmdLine += arg;
201 }
202 }
203
204 printf("Windows CreateProcess: cmd=%s, workdir=%s\n", cmdLine.c_str(), winWorkingDir.c_str());
205
206
207 mStdoutBuffer.clear();
208 mStderrBuffer.clear();
209
210
211 SECURITY_ATTRIBUTES sa;
212 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
213 sa.bInheritHandle = TRUE;
214 sa.lpSecurityDescriptor =
NULL;
215
216
217 HANDLE hStdoutRead, hStdoutWrite;
218 if (!CreatePipe(&hStdoutRead, &hStdoutWrite, &sa, 0)) {
219 printf("Failed to create stdout pipe: %lu\n", GetLastError());
220 return false;
221 }
222
223 SetHandleInformation(hStdoutRead, HANDLE_FLAG_INHERIT, 0);
224
225
226 HANDLE hStderrRead, hStderrWrite;
227 if (!CreatePipe(&hStderrRead, &hStderrWrite, &sa, 0)) {
228 printf("Failed to create stderr pipe: %lu\n", GetLastError());
229 CloseHandle(hStdoutRead);
230 CloseHandle(hStdoutWrite);
231 return false;
232 }
233
234 SetHandleInformation(hStderrRead, HANDLE_FLAG_INHERIT, 0);
235
236
237 mStdoutReadPipe = hStdoutRead;
238 mStdoutWritePipe = hStdoutWrite;
239 mStderrReadPipe = hStderrRead;
240 mStderrWritePipe = hStderrWrite;
241
242 STARTUPINFOA si;
243 PROCESS_INFORMATION pi;
244 ZeroMemory(&si, sizeof(si));
245 si.cb = sizeof(si);
246 si.hStdOutput = hStdoutWrite;
247 si.hStdError = hStderrWrite;
248 si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
249 si.dwFlags |= STARTF_USESTDHANDLES;
250 ZeroMemory(&pi, sizeof(pi));
251
252
253 if (!CreateProcessA(
255 const_cast<char*>(cmdLine.c_str()),
258 TRUE,
259 0,
261 winWorkingDir.c_str(),
262 &si,
263 &pi))
264 {
265 printf("Failed to create process: %s\n", cmdLine.c_str());
266 printf("Working directory was: %s\n", winWorkingDir.c_str());
267 printf("GetLastError: %lu\n", GetLastError());
268
269 CloseHandle(hStdoutRead);
270 CloseHandle(hStdoutWrite);
271 CloseHandle(hStderrRead);
272 CloseHandle(hStderrWrite);
273 mStdoutReadPipe = nullptr;
274 mStdoutWritePipe = nullptr;
275 mStderrReadPipe = nullptr;
276 mStderrWritePipe = nullptr;
277 return false;
278 }
279
280 mProcessHandle = pi.hProcess;
281 mProcessId = pi.dwProcessId;
282 CloseHandle(pi.hThread);
283
284
285 CloseHandle(hStdoutWrite);
286 CloseHandle(hStderrWrite);
287 mStdoutWritePipe = nullptr;
288 mStderrWritePipe = nullptr;
289
290 mIsRunning = true;
291
292#else
293
294 if (pipe(mStdoutPipe) < 0 || pipe(mStderrPipe) < 0) {
295 printf("Failed to create pipes\n");
296 return false;
297 }
298
299
300 fcntl(mStdoutPipe[0], F_SETFL, O_NONBLOCK);
301 fcntl(mStderrPipe[0], F_SETFL, O_NONBLOCK);
302
303
304 mStdoutBuffer.clear();
305 mStderrBuffer.clear();
306
307 pid_t pid = fork();
308
309 if (pid < 0) {
310 printf("Failed to fork process\n");
311 close(mStdoutPipe[0]);
312 close(mStdoutPipe[1]);
313 close(mStderrPipe[0]);
314 close(mStderrPipe[1]);
315 return false;
316 }
317
318 if (pid == 0) {
319
320 close(mStdoutPipe[0]);
321 close(mStderrPipe[0]);
322
323 dup2(mStdoutPipe[1], STDOUT_FILENO);
324 dup2(mStderrPipe[1], STDERR_FILENO);
325
326 close(mStdoutPipe[1]);
327 close(mStderrPipe[1]);
328
329
330 if (chdir(workingDir.c_str()) != 0) {
331 fprintf(stderr, "Failed to change directory to: %s\n", workingDir.c_str());
332 exit(1);
333 }
334
335 std::vector<char*> argv;
336 argv.push_back(const_cast<char*>(peblPath.c_str()));
337 argv.push_back(const_cast<char*>(scriptFilename.c_str()));
338
339 for (const auto& arg : fullArgs) {
340 argv.push_back(const_cast<char*>(arg.c_str()));
341 }
342
343 argv.push_back(nullptr);
344
345 execvp(peblPath.c_str(), argv.data());
346
347
348 fprintf(stderr, "Failed to execute: %s\n", peblPath.c_str());
349 exit(1);
350 }
351
352
353 close(mStdoutPipe[1]);
354 close(mStderrPipe[1]);
355 mStdoutPipe[1] = -1;
356 mStderrPipe[1] = -1;
357
358 mProcessId = pid;
359 mIsRunning = true;
360#endif
361
362 printf("Launched experiment: %s (PID: %lu) in directory: %s\n",
363 scriptFilename.c_str(), (unsigned long)mProcessId, workingDir.c_str());
364
365
366 mCurrentScript = scriptPath;
367 mCurrentSubject = subjectCode;
368 mCurrentLanguage = language;
369 mCurrentFullscreen = fullscreen;
370 mLaunchTime = std::time(nullptr);
371 LogLaunch(scriptPath, subjectCode, language, fullscreen);
372
373 return true;
374}