Line data Source code
1 : /* Copyright (c) 2005-2017, Stefan Eilemann <eile@equalizergraphics.com>
2 : * Daniel Nachbaur <danielnachbaur@gmail.com>
3 : *
4 : * This library is free software; you can redistribute it and/or modify it under
5 : * the terms of the GNU Lesser General Public License version 2.1 as published
6 : * by the Free Software Foundation.
7 : *
8 : * This library is distributed in the hope that it will be useful, but WITHOUT
9 : * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10 : * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11 : * details.
12 : *
13 : * You should have received a copy of the GNU Lesser General Public License
14 : * along with this library; if not, write to the Free Software Foundation, Inc.,
15 : * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16 : */
17 :
18 : #include "fork.h"
19 :
20 : #include "debug.h"
21 : #include "log.h"
22 : #include "os.h"
23 :
24 : #include <errno.h>
25 : #include <signal.h>
26 : #include <sstream>
27 : #include <string.h>
28 :
29 : #ifdef _WIN32
30 : #include <io.h>
31 : #else
32 : #include <sys/wait.h>
33 : #include <unistd.h>
34 : #endif
35 :
36 : namespace lunchbox
37 : {
38 : namespace
39 : {
40 : #ifndef _WIN32
41 0 : Strings _buildCommandLine(const std::string& command)
42 : {
43 0 : Strings commandLine;
44 0 : const size_t length = command.size();
45 0 : const char* string = command.c_str();
46 0 : bool inTicks = false;
47 0 : size_t bufferPos = 0;
48 0 : std::vector<char> buffer(length + 1);
49 :
50 0 : commandLine.clear();
51 :
52 : // tokenize command line
53 0 : for (size_t i = 0; i < length; i++)
54 : {
55 0 : const char c = string[i];
56 0 : switch (c)
57 : {
58 : case ' ':
59 0 : if (inTicks)
60 0 : buffer[bufferPos++] = c;
61 : else
62 : {
63 0 : buffer[bufferPos] = '\0';
64 0 : commandLine.push_back(&buffer[0]);
65 0 : bufferPos = 0;
66 : }
67 0 : break;
68 :
69 : case '"':
70 0 : inTicks = !inTicks;
71 0 : break;
72 :
73 : case '\\':
74 0 : i++;
75 0 : buffer[bufferPos++] = string[i];
76 0 : break;
77 :
78 : default:
79 0 : buffer[bufferPos++] = c;
80 0 : break;
81 : }
82 : }
83 :
84 0 : if (bufferPos > 0)
85 : {
86 0 : buffer[bufferPos++] = '\0';
87 0 : commandLine.push_back(&buffer[0]);
88 : }
89 :
90 0 : return commandLine;
91 : }
92 : #endif
93 : } // namespace
94 :
95 0 : bool fork(const std::string& command)
96 : {
97 0 : if (command.empty())
98 0 : return false;
99 :
100 : #ifdef _WIN32
101 : STARTUPINFO startupInfo;
102 : ZeroMemory(&startupInfo, sizeof(STARTUPINFO));
103 :
104 : PROCESS_INFORMATION procInfo;
105 : ZeroMemory(&procInfo, sizeof(PROCESS_INFORMATION));
106 :
107 : const char* cmdLine = command.c_str();
108 :
109 : startupInfo.cb = sizeof(STARTUPINFO);
110 : const bool success =
111 : CreateProcess(0, LPSTR(cmdLine), // program, command line
112 : 0, 0, // process, thread attributes
113 : FALSE, // inherit handles
114 : 0, // creation flags
115 : 0, // environment
116 : 0, // current directory
117 : &startupInfo, &procInfo);
118 :
119 : if (!success)
120 : {
121 : LBERROR << "CreateProcess failed: " << sysError << std::endl;
122 : return false;
123 : }
124 :
125 : // WaitForInputIdle( procInfo.hProcess, 1000 );
126 : CloseHandle(procInfo.hProcess);
127 : CloseHandle(procInfo.hThread);
128 : return true;
129 : #else
130 0 : const Strings commandLine = _buildCommandLine(command);
131 :
132 : struct sigaction act;
133 0 : setZero(&act, sizeof(act));
134 0 : act.sa_handler = SIG_DFL;
135 0 : act.sa_flags = SA_NOCLDWAIT;
136 0 : ::sigaction(SIGCHLD, &act, nullptr);
137 :
138 0 : const int result = ::fork();
139 0 : switch (result)
140 : {
141 : case 0: // child
142 0 : break;
143 :
144 : case -1: // error
145 0 : LBWARN << "Could not fork child process:" << sysError << std::endl;
146 0 : return false;
147 :
148 : default: // parent
149 0 : return true;
150 : }
151 :
152 : // child
153 0 : const size_t argc = commandLine.size();
154 0 : std::vector<char*> argv(argc + 1);
155 0 : std::ostringstream stringStream;
156 :
157 0 : for (size_t i = 0; i < argc; i++)
158 : {
159 0 : argv[i] = (char*)commandLine[i].c_str();
160 0 : stringStream << commandLine[i] << " ";
161 : }
162 :
163 0 : argv[argc] = 0;
164 :
165 0 : LBDEBUG << "Executing: " << stringStream.str() << std::endl;
166 0 : int nTries = 10;
167 0 : while (nTries--)
168 : {
169 0 : execvp(argv[0], &argv[0]);
170 0 : LBWARN << "Error executing '" << argv[0] << "': " << sysError
171 0 : << std::endl;
172 0 : if (errno != ETXTBSY)
173 0 : break;
174 : }
175 :
176 0 : ::exit(EXIT_FAILURE);
177 : return true; // not reached
178 : #endif
179 : }
180 75 : } // namespace lunchbox
|