1 // =================================
  2 // Copyright (c) 2021 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 #include <soulng/util/System.hpp>
  7 #include <soulng/util/Handle.hpp>
  8 #include <soulng/util/TextUtils.hpp>
  9 #include <sys/stat.h>
 10 #include <fcntl.h>
 11 #include <cstring>
 12 #include <memory>
 13 #ifdef _WIN32
 14 #include <process.h>
 15 #include <windows.h>
 16 #include <io.h>
 17 #include <Psapi.h>
 18 #elif defined(__linux) || defined(__posix) || defined(__unix)
 19 #include <unistd.h>
 20 #include <sys/types.h>
 21 #include <sys/wait.h>
 22 #include <sys/time.h>
 23 #include <sys/select.h>
 24 #endif
 25 
 26 namespace soulng { namespace util {
 27 
 28 bool disableConsoleWindow = false;
 29 
 30 void DisableConsoleWindow()
 31 {
 32     disableConsoleWindow = true;
 33 }
 34 
 35 ProcessFailure::ProcessFailure(const std::string& errorMessage_int exitCode_) : std::runtime_error(errorMessage_)exitCode(exitCode_)
 36 {
 37 }
 38 
 39 #ifdef _WIN32
 40 
 41 
 42 
 43 
 44 
 45 
 46 
 47 
 48 
 49 
 50 
 51 
 52 
 53 #endif
 54 
 55 int get_default_pmode()
 56 {
 57 #if defined(_WIN32)
 58 
 59 
 60 
 61 #elif defined(__linux) || defined(__unix) || defined(__posix)
 62 
 63 
 64 
 65 #else
 66 
 67 #error unknown platform
 68 
 69 #endif
 70 }
 71 
 72 std::std::vector<std::string>ParseCommand(conststd::string&command)
 73 {
 74     std::vector<std::string> args;
 75     int state = 0;
 76     std::string arg;
 77     for (char c : command)
 78     {
 79         switch (state)
 80         {
 81             case 0:
 82             {
 83                 if (c == '"')
 84                 {
 85                     arg.append(1'"');
 86                     state = 1;
 87                 }
 88                 else if (c == ' ')
 89                 {
 90                     args.push_back(arg);
 91                     arg.clear();
 92                     state = 2;
 93                 }
 94                 else
 95                 {
 96                     arg.append(1c);
 97                 }
 98                 break;
 99             }
100             case 1:
101             {
102                 if (c == '"')
103                 {
104                     arg.append(1'"');
105                     state = 0;
106                 }
107                 else
108                 {
109                     arg.append(1c);
110                 }
111                 break;
112             }
113             case 2:
114             {
115                 if (c != ' ')
116                 {
117                     arg.append(1c);
118                     state = 0;
119                 }
120                 break;
121             }
122         }
123     }
124     if (!arg.empty())
125     {
126         args.push_back(arg);
127     }
128     return args;
129 }
130 
131 void System(const std::string& commandbool ignoreReturnValue)
132 {
133     int retVal = 0;
134     if (!disableConsoleWindow)
135     {
136         retVal = system(command.c_str());
137     }
138     else
139     {
140 #ifdef _WIN32
141 
142 
143 
144 
145 
146 
147 
148 
149 
150 
151 
152 
153 
154 
155 
156 
157 
158 
159 
160 
161 
162 
163 
164 
165 
166 
167 
168 
169 
170 
171 
172 
173 #elif defined(__linux) || defined(__unix) || defined(__posix)
174 
175 #else
176 
177 #error unknown platform
178 
179 #endif
180     }
181     if (!ignoreReturnValue)
182     {
183         if (retVal != 0)
184         {
185 #ifdef _WIN32
186 
187 
188 
189 #elif defined(__linux) || defined(__unix) || defined(__posix)
190 
191 
192 
193 
194 
195 
196 
197 
198 
199 #else
200 
201 #error unknown platform
202 
203 #endif
204         }
205     }
206 }
207 
208 void System(const std::string& command)
209 {
210     return System(commandfalse);
211 }
212 
213 void System(const std::string& commandint redirectFdconst std::string& toFilebool ignoreReturnValue)
214 {
215     Handle old = dup(redirectFd);
216     if (old == -1)
217     {
218         throw std::runtime_error("System redirect: could not duplicate handle " + std::to_string(redirectFd));
219     }
220     Handle fd = creat(toFile.c_str()get_default_pmode());
221     if (fd == -1)
222     {
223         throw std::runtime_error("System: could not create file '" + toFile + "'");
224     }
225     if (dup2(fdredirectFd) == -1)
226     {
227         throw std::runtime_error("System redirect: dup2 failed");
228     }
229     try
230     {
231         System(commandignoreReturnValue);
232         dup2(oldredirectFd);
233     }
234     catch (...)
235     {
236         dup2(oldredirectFd);
237         throw ;
238     }
239 }
240 
241 void System(const std::string& commandint redirectFdconst std::string& toFile)
242 {
243     System(commandredirectFdtoFilefalse);
244 }
245 
246 void System(const std::string& commandconst std::std::vector<std::std::pair<intstd::string>>&redirections)
247 {
248     std::vector<std::std::pair<intHandle>>toRestore;
249     for (const std::std::pair<intstd::string>&redirection : redirections)
250     {
251         int handle = redirection.first;
252         std::string toFile = redirection.second;
253         Handle oldHandle = dup(handle);
254         if (oldHandle == -1)
255         {
256             throw std::runtime_error("System redirect: could not duplicate handle " + std::to_string(handle));
257         }
258         toRestore.push_back(std::make_pair(handlestd::move(oldHandle)));
259         Handle fd = creat(toFile.c_str()get_default_pmode());
260         if (fd == -1)
261         {
262             throw std::runtime_error("System: could not create file '" + toFile + "'");
263         }
264         if (dup2(fdhandle) == -1)
265         {
266             throw std::runtime_error("System redirect: dup2 failed");
267         }
268     }
269     try
270     {
271         System(command);
272         for (std::std::pair<intHandle>&r : toRestore)
273         {
274             int handle = r.first;
275             Handle old = std::move(r.second);
276             dup2(oldhandle);
277         }
278     }
279     catch (...)
280     {
281         for (std::std::pair<intHandle>&r : toRestore)
282         {
283             int handle = r.first;
284             Handle old = std::move(r.second);
285             dup2(oldhandle);
286         }
287         throw ;
288     }
289 }
290 
291 #ifdef _WIN32
292 
293 
294 
295 
296 
297 
298 
299 
300 
301 
302 
303 
304 
305 
306 
307 
308 
309 
310 
311 
312 
313 
314 
315 
316 
317 
318 
319 
320 
321 
322 
323 
324 
325 
326 
327 
328 #elif defined(__linux) || defined(__posix) || defined(__unix)
329 
330 
331 
332 
333 
334 
335 
336 
337 
338 
339 
340 
341 
342 
343 
344 
345 
346 
347 
348 
349 
350 
351 
352 
353 
354 
355 
356 
357 
358 
359 
360 
361 
362 
363 
364 
365 
366 #endif 
367 
368 int ReadFromPipe(int pipeHandlevoid* bufferunsigned int count)
369 {
370     return read(pipeHandlebuffercount);
371 }
372 
373 int WriteToPipe(int pipeHandlevoid* bufferunsigned int count)
374 {
375     return write(pipeHandlebuffercount);
376 }
377 
378 void RedirectStdHandlesToPipes(std::std::vector<int>&oldHandlesstd::std::vector<int>&pipeHandles)
379 {
380     int phRead[2];
381     if (pipe(phRead) == -1)
382     {
383         throw std::runtime_error("RedirectStdHandlesToPipes: pipe failed");
384     }
385     int old0 = dup(0);
386     if (old0 == -1)
387     {
388         throw std::runtime_error("RedirectStdHandlesToPipes: dup(0) failed");
389     }
390     oldHandles.push_back(old0);
391     if (dup2(phRead[0]0) == -1)
392     {
393         throw std::runtime_error("RedirectStdHandlesToPipes: dup2(p0, 0) failed");
394     }
395     pipeHandles.push_back(phRead[1]);
396     close(phRead[0]);
397     int phWrite[2];
398     if (pipe(phWrite) == -1)
399     {
400         throw std::runtime_error("RedirectStdHandlesToPipes: pipe failed");
401     }
402     int old1 = dup(1);
403     if (old1 == -1)
404     {
405         throw std::runtime_error("RedirectStdHandlesToPipes: dup(1) failed");
406     }
407     oldHandles.push_back(old1);
408     if (dup2(phWrite[1]1) == -1)
409     {
410         throw std::runtime_error("RedirectStdHandlesToPipes: dup2(p1, 1) failed");
411     }
412     pipeHandles.push_back(phWrite[0]);
413     close(phWrite[1]);
414     int phError[2];
415     if (pipe(phError) == -1)
416     {
417         throw std::runtime_error("RedirectStdHandlesToPipes: pipe failed");
418     }
419     int old2 = dup(2);
420     if (old2 == -1)
421     {
422         throw std::runtime_error("RedirectStdHandlesToPipes: dup(2) failed");
423     }
424     oldHandles.push_back(old2);
425     if (dup2(phError[1]2) == -1)
426     {
427         throw std::runtime_error("RedirectStdHandlesToPipes: dup2(p2, 2) failed");
428     }
429     pipeHandles.push_back(phError[0]);
430     close(phError[1]);
431 }
432 
433 void RestoreStdHandles(const std::std::vector<int>&oldHandles)
434 {
435     if (oldHandles.size() != 3)
436     {
437         throw std::runtime_error("3 old handles expected ");
438     }
439     if (dup2(oldHandles[0]0) == -1)
440     {
441         throw std::runtime_error("RestoreStdHandles: dup2 0, old0 failed");
442     }
443     if (dup2(oldHandles[1]1) == -1)
444     {
445         throw std::runtime_error("RestoreStdHandles: dup2 1, old1 failed");
446     }
447     if (dup2(oldHandles[2]2) == -1)
448     {
449         throw std::runtime_error("RestoreStdHandles: dup2 2, old2 failed");
450     }
451 }
452 
453 std::string GetPathToExecutable()
454 {
455     char buf[4096];
456 #ifdef _WIN32
457 
458 
459 
460 
461 
462 
463 
464 #elif defined(__linux) || defined(__unix) || defined(__posix)
465 
466 
467 
468 
469 
470 
471 
472 
473 
474 
475 
476 
477 #else
478 
479 #error unknown platform
480 
481 #endif
482 }
483 
484 } } // namespace soulng::util