1//
2// Process_UNIX.cpp
3//
4// Library: Foundation
5// Package: Processes
6// Module: Process
7//
8// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
9// and Contributors.
10//
11// SPDX-License-Identifier: BSL-1.0
12//
13
14
15#include "Poco/Process_UNIX.h"
16#include "Poco/Exception.h"
17#include "Poco/NumberFormatter.h"
18#include "Poco/Pipe.h"
19#include "Poco/Thread.h"
20#include <limits>
21#include <errno.h>
22#include <signal.h>
23#include <stdlib.h>
24#include <sys/time.h>
25#include <sys/types.h>
26#include <sys/resource.h>
27#include <sys/wait.h>
28
29
30#if defined(__QNX__)
31#include <process.h>
32#include <spawn.h>
33#include <cstring>
34#endif
35
36
37namespace Poco {
38
39
40//
41// ProcessHandleImpl
42//
43ProcessHandleImpl::ProcessHandleImpl(pid_t pid):
44 _pid(pid),
45 _mutex(),
46 _event(Event::EVENT_MANUALRESET),
47 _status()
48{
49}
50
51
52ProcessHandleImpl::~ProcessHandleImpl()
53{
54}
55
56
57pid_t ProcessHandleImpl::id() const
58{
59 return _pid;
60}
61
62
63int ProcessHandleImpl::wait() const
64{
65 if (wait(0) != _pid)
66 throw SystemException("Cannot wait for process", NumberFormatter::format(_pid));
67
68 const int status = _status.value();
69 if (WIFEXITED(status))
70 return WEXITSTATUS(status);
71 if (WIFSIGNALED(status))
72 return -WTERMSIG(status);
73
74 // This line should never be reached.
75 return std::numeric_limits<int>::max();
76}
77
78
79int ProcessHandleImpl::wait(int options) const
80{
81 {
82 FastMutex::ScopedLock lock(_mutex);
83 if (_status.isSpecified())
84 {
85 return _pid;
86 }
87 }
88
89 int status;
90 int rc;
91 do
92 {
93 rc = waitpid(_pid, &status, options);
94 }
95 while (rc < 0 && errno == EINTR);
96
97 if (rc == _pid)
98 {
99 FastMutex::ScopedLock lock(_mutex);
100 _status = status;
101 _event.set();
102 }
103 else if (rc < 0 && errno == ECHILD)
104 {
105 // Looks like another thread was lucky and it should update the status for us shortly
106 _event.wait();
107
108 FastMutex::ScopedLock lock(_mutex);
109 if (_status.isSpecified())
110 {
111 rc = _pid;
112 }
113 }
114
115 return rc;
116}
117
118
119//
120// ProcessImpl
121//
122ProcessImpl::PIDImpl ProcessImpl::idImpl()
123{
124 return getpid();
125}
126
127
128void ProcessImpl::timesImpl(long& userTime, long& kernelTime)
129{
130 struct rusage usage;
131 getrusage(RUSAGE_SELF, &usage);
132 userTime = usage.ru_utime.tv_sec;
133 kernelTime = usage.ru_stime.tv_sec;
134}
135
136
137ProcessHandleImpl* ProcessImpl::launchImpl(const std::string& command, const ArgsImpl& args, const std::string& initialDirectory, Pipe* inPipe, Pipe* outPipe, Pipe* errPipe, const EnvImpl& env)
138{
139#if defined(__QNX__)
140 if (initialDirectory.empty())
141 {
142 /// use QNX's spawn system call which is more efficient than fork/exec.
143 char** argv = new char*[args.size() + 2];
144 int i = 0;
145 argv[i++] = const_cast<char*>(command.c_str());
146 for (ArgsImpl::const_iterator it = args.begin(); it != args.end(); ++it)
147 argv[i++] = const_cast<char*>(it->c_str());
148 argv[i] = NULL;
149 struct inheritance inherit;
150 std::memset(&inherit, 0, sizeof(inherit));
151 inherit.flags = SPAWN_ALIGN_DEFAULT | SPAWN_CHECK_SCRIPT | SPAWN_SEARCH_PATH;
152 int fdmap[3];
153 fdmap[0] = inPipe ? inPipe->readHandle() : 0;
154 fdmap[1] = outPipe ? outPipe->writeHandle() : 1;
155 fdmap[2] = errPipe ? errPipe->writeHandle() : 2;
156
157 char** envPtr = 0;
158 std::vector<char> envChars;
159 std::vector<char*> envPtrs;
160 if (!env.empty())
161 {
162 envChars = getEnvironmentVariablesBuffer(env);
163 envPtrs.reserve(env.size() + 1);
164 char* p = &envChars[0];
165 while (*p)
166 {
167 envPtrs.push_back(p);
168 while (*p) ++p;
169 ++p;
170 }
171 envPtrs.push_back(0);
172 envPtr = &envPtrs[0];
173 }
174
175 int pid = spawn(command.c_str(), 3, fdmap, &inherit, argv, envPtr);
176 delete [] argv;
177 if (pid == -1)
178 throw SystemException("cannot spawn", command);
179
180 if (inPipe) inPipe->close(Pipe::CLOSE_READ);
181 if (outPipe) outPipe->close(Pipe::CLOSE_WRITE);
182 if (errPipe) errPipe->close(Pipe::CLOSE_WRITE);
183 return new ProcessHandleImpl(pid);
184 }
185 else
186 {
187 return launchByForkExecImpl(command, args, initialDirectory, inPipe, outPipe, errPipe, env);
188 }
189#else
190 return launchByForkExecImpl(command, args, initialDirectory, inPipe, outPipe, errPipe, env);
191#endif
192}
193
194
195ProcessHandleImpl* ProcessImpl::launchByForkExecImpl(const std::string& command, const ArgsImpl& args, const std::string& initialDirectory, Pipe* inPipe, Pipe* outPipe, Pipe* errPipe, const EnvImpl& env)
196{
197#if !defined(POCO_NO_FORK_EXEC)
198 // We must not allocated memory after fork(),
199 // therefore allocate all required buffers first.
200 std::vector<char> envChars = getEnvironmentVariablesBuffer(env);
201 std::vector<char*> argv(args.size() + 2);
202 int i = 0;
203 argv[i++] = const_cast<char*>(command.c_str());
204 for (ArgsImpl::const_iterator it = args.begin(); it != args.end(); ++it)
205 {
206 argv[i++] = const_cast<char*>(it->c_str());
207 }
208 argv[i] = NULL;
209
210 const char* pInitialDirectory = initialDirectory.empty() ? 0 : initialDirectory.c_str();
211
212 int pid = fork();
213 if (pid < 0)
214 {
215 throw SystemException("Cannot fork process for", command);
216 }
217 else if (pid == 0)
218 {
219 if (pInitialDirectory)
220 {
221 if (chdir(pInitialDirectory) != 0)
222 {
223 _exit(72);
224 }
225 }
226
227 // set environment variables
228 char* p = &envChars[0];
229 while (*p)
230 {
231 putenv(p);
232 while (*p) ++p;
233 ++p;
234 }
235
236 // setup redirection
237 if (inPipe)
238 {
239 dup2(inPipe->readHandle(), STDIN_FILENO);
240 inPipe->close(Pipe::CLOSE_BOTH);
241 }
242 // outPipe and errPipe may be the same, so we dup first and close later
243 if (outPipe) dup2(outPipe->writeHandle(), STDOUT_FILENO);
244 if (errPipe) dup2(errPipe->writeHandle(), STDERR_FILENO);
245 if (outPipe) outPipe->close(Pipe::CLOSE_BOTH);
246 if (errPipe) errPipe->close(Pipe::CLOSE_BOTH);
247 // close all open file descriptors other than stdin, stdout, stderr
248 for (int fd = 3; fd < sysconf(_SC_OPEN_MAX); ++fd)
249 {
250 close(fd);
251 }
252
253 execvp(argv[0], &argv[0]);
254 _exit(72);
255 }
256
257 if (inPipe) inPipe->close(Pipe::CLOSE_READ);
258 if (outPipe) outPipe->close(Pipe::CLOSE_WRITE);
259 if (errPipe) errPipe->close(Pipe::CLOSE_WRITE);
260 return new ProcessHandleImpl(pid);
261#else
262 throw Poco::NotImplementedException("platform does not allow fork/exec");
263#endif
264}
265
266
267void ProcessImpl::killImpl(ProcessHandleImpl& handle)
268{
269 killImpl(handle.id());
270}
271
272
273void ProcessImpl::killImpl(PIDImpl pid)
274{
275 if (kill(pid, SIGKILL) != 0)
276 {
277 switch (errno)
278 {
279 case ESRCH:
280 throw NotFoundException("cannot kill process");
281 case EPERM:
282 throw NoPermissionException("cannot kill process");
283 default:
284 throw SystemException("cannot kill process");
285 }
286 }
287}
288
289
290bool ProcessImpl::isRunningImpl(const ProcessHandleImpl& handle)
291{
292 return handle.wait(WNOHANG) == 0;
293}
294
295
296bool ProcessImpl::isRunningImpl(PIDImpl pid)
297{
298 if (kill(pid, 0) == 0)
299 {
300 return true;
301 }
302 else
303 {
304 return false;
305 }
306}
307
308
309void ProcessImpl::requestTerminationImpl(PIDImpl pid)
310{
311 if (kill(pid, SIGINT) != 0)
312 {
313 switch (errno)
314 {
315 case ESRCH:
316 throw NotFoundException("cannot terminate process");
317 case EPERM:
318 throw NoPermissionException("cannot terminate process");
319 default:
320 throw SystemException("cannot terminate process");
321 }
322 }
323}
324
325
326} // namespace Poco
327