source: filezilla/trunk/fuentes/src/engine/fzprocess.cpp @ 130

Last change on this file since 130 was 130, checked in by jrpelegrina, 4 years ago

First release to xenial

File size: 8.5 KB
Line 
1#include <filezilla.h>
2#include "fzprocess.h"
3
4#ifdef __WXMSW__
5
6namespace {
7void ResetHandle(HANDLE& handle)
8{
9        if (handle != INVALID_HANDLE_VALUE) {
10                CloseHandle(handle);
11                handle = INVALID_HANDLE_VALUE;
12        }
13};
14
15bool Uninherit(HANDLE& handle)
16{
17        if (handle != INVALID_HANDLE_VALUE) {
18                HANDLE newHandle = INVALID_HANDLE_VALUE;
19
20                if (!DuplicateHandle(GetCurrentProcess(), handle, GetCurrentProcess(), &newHandle, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
21                        newHandle = INVALID_HANDLE_VALUE;
22                }
23                CloseHandle(handle);
24                handle = newHandle;
25        }
26
27        return handle != INVALID_HANDLE_VALUE;
28}
29
30class Pipe final
31{
32public:
33        Pipe() = default;
34
35        ~Pipe()
36        {
37                reset();
38        }
39
40        Pipe(Pipe const&) = delete;
41        Pipe& operator=(Pipe const&) = delete;
42
43        bool Create(bool local_is_input)
44        {
45                reset();
46
47                SECURITY_ATTRIBUTES sa{};
48                sa.bInheritHandle = TRUE;
49                sa.nLength = sizeof(sa);
50
51                BOOL res = CreatePipe(&read_, &write_, &sa, 0);
52                if (res) {
53                        // We only want one side of the pipe to be inheritable
54                        if (!Uninherit(local_is_input ? read_ : write_)) {
55                                reset();
56                        }
57                }
58                else {
59                        read_ = INVALID_HANDLE_VALUE;
60                        write_ = INVALID_HANDLE_VALUE;
61                }
62                return valid();
63        }
64
65        bool valid() const {
66                return read_ != INVALID_HANDLE_VALUE && write_ != INVALID_HANDLE_VALUE;
67        }
68
69        void reset()
70        {
71                ResetHandle(read_);
72                ResetHandle(write_);
73        }
74
75        HANDLE read_{INVALID_HANDLE_VALUE};
76        HANDLE write_{INVALID_HANDLE_VALUE};
77};
78}
79
80class CProcess::Impl
81{
82public:
83        Impl() = default;
84        ~Impl()
85        {
86                Kill();
87        }
88
89        Impl(Impl const&) = delete;
90        Impl& operator=(Impl const&) = delete;
91
92        bool CreatePipes()
93        {
94                return
95                        in_.Create(false) &&
96                        out_.Create(true) &&
97                        err_.Create(true);
98        }
99
100        bool Execute(wxString const& cmd, std::vector<wxString> const& args)
101        {
102                DWORD flags = CREATE_UNICODE_ENVIRONMENT | CREATE_DEFAULT_ERROR_MODE | CREATE_NO_WINDOW;
103
104                if (!CreatePipes()) {
105                        return false;
106                }
107
108                STARTUPINFO si{};
109                si.cb = sizeof(si);
110                si.dwFlags = STARTF_USESTDHANDLES;
111                si.hStdInput = in_.read_;
112                si.hStdOutput = out_.write_;
113                si.hStdError = err_.write_;
114
115                auto cmdline = GetCmdLine(cmd, args);
116
117                PROCESS_INFORMATION pi{};
118                BOOL res = CreateProcess(cmd, cmdline.get(), 0, 0, TRUE, flags, 0, 0, &si, &pi);
119                if (!res) {
120                        return false;
121                }
122
123                process_ = pi.hProcess;
124
125                // We don't need to use these
126                ResetHandle(pi.hThread);
127                ResetHandle(in_.read_);
128                ResetHandle(out_.write_);
129                ResetHandle(err_.write_);
130
131                return true;
132        }
133
134        void Kill()
135        {
136                if (process_ != INVALID_HANDLE_VALUE) {
137                        in_.reset();
138                        if (WaitForSingleObject(process_, 500) == WAIT_TIMEOUT) {
139                                TerminateProcess(process_, 0);
140                        }
141                        ResetHandle(process_);
142                        out_.reset();
143                        err_.reset();
144                }
145        }
146
147        int Read(char* buffer, unsigned int len)
148        {
149                DWORD read = 0;
150                BOOL res = ReadFile(out_.read_, buffer, len, &read, 0);
151                if (!res) {
152                        return -1;
153                }
154                return read;
155        }
156
157        bool Write(char const* buffer, unsigned int len)
158        {
159                while (len > 0) {
160                        DWORD written = 0;
161                        BOOL res = WriteFile(in_.write_, buffer, len, &written, 0);
162                        if (!res || written == 0) {
163                                return false;
164                        }
165                        buffer += written;
166                        len -= written;
167                }
168                return true;
169        }
170
171private:
172        wxString EscapeArgument(wxString const& arg)
173        {
174                wxString ret;
175
176                // Treat newlines are whitespace just to be sure, even if MSDN doesn't mention it
177                if (arg.find_first_of(_T(" \"\t\r\n\v")) != wxString::npos) {
178                        // Quite horrible, as per MSDN:
179                        // Backslashes are interpreted literally, unless they immediately precede a double quotation mark.
180                        // If an even number of backslashes is followed by a double quotation mark, one backslash is placed in the argv array for every pair of backslashes, and the double quotation mark is interpreted as a string delimiter.
181                        // If an odd number of backslashes is followed by a double quotation mark, one backslash is placed in the argv array for every pair of backslashes, and the double quotation mark is "escaped" by the remaining backslash, causing a literal double quotation mark (") to be placed in argv.
182
183                        ret = _T("\"");
184                        int backslashCount = 0;
185                        for (auto it = arg.begin(); it != arg.end(); ++it) {
186                                if (*it == '\\') {
187                                        ++backslashCount;
188                                }
189                                else {
190                                        if (*it == '"') {
191                                                // Escape all preceeding backslashes and escape the quote
192                                                ret += wxString(backslashCount + 1, '\\');
193                                        }
194                                        backslashCount = 0;
195                                }
196                                ret += *it;
197                        }
198                        if (backslashCount) {
199                                // Escape all preceeding backslashes
200                                ret += wxString(backslashCount, '\\');
201                        }
202
203                        ret += _T("\"");
204                }
205                else {
206                        ret = arg;
207                }
208
209                return ret;
210        }
211
212        std::unique_ptr<wxChar[]> GetCmdLine(wxString const& cmd, std::vector<wxString> const& args)
213        {
214                wxString cmdline = EscapeArgument(cmd);
215               
216                for (auto const& arg : args) {
217                        if (!arg.empty()) {
218                                cmdline += _T(" ") + EscapeArgument(arg);
219                        }
220                }
221                std::unique_ptr<wxChar[]> ret;
222                ret.reset(new wxChar[cmdline.size() + 1]);
223                wxStrcpy(ret.get(), cmdline);
224                return ret;
225        }
226
227        HANDLE process_{INVALID_HANDLE_VALUE};
228
229        Pipe in_;
230        Pipe out_;
231        Pipe err_;
232};
233
234#else
235
236#include <errno.h>
237#include <signal.h>
238#include <sys/wait.h>
239
240namespace {
241void ResetFd(int& fd)
242{
243        if (fd != -1) {
244                close(fd);
245                fd = -1;
246        }
247}
248
249class Pipe final
250{
251public:
252        Pipe() = default;
253
254        ~Pipe()
255        {
256                reset();
257        }
258
259        Pipe(Pipe const&) = delete;
260        Pipe& operator=(Pipe const&) = delete;
261
262        bool Create()
263        {
264                reset();
265
266                int fds[2];
267                if (pipe(fds) != 0) {
268                        return false;
269                }
270
271                read_ = fds[0];
272                write_ = fds[1];
273
274                return valid();
275        }
276
277        bool valid() const {
278                return read_ != -1 && write_ != -1;
279        }
280
281        void reset()
282        {
283                ResetFd(read_);
284                ResetFd(write_);
285        }
286
287        int read_{-1};
288        int write_{-1};
289};
290}
291
292class CProcess::Impl
293{
294public:
295        Impl() = default;
296        ~Impl()
297        {
298                Kill();
299        }
300
301        Impl(Impl const&) = delete;
302        Impl& operator=(Impl const&) = delete;
303
304        bool CreatePipes()
305        {
306                return
307                        in_.Create() &&
308                        out_.Create() &&
309                        err_.Create();
310        }
311
312        void MakeArg(wxString const& arg, std::vector<std::unique_ptr<char[]>> & argList)
313        {
314                wxCharBuffer buf = arg.mb_str();
315                std::unique_ptr<char[]> ret;
316                ret.reset(new char[buf.length() + 1]);
317                strcpy(ret.get(), buf);
318                argList.push_back(std::move(ret));
319        }
320
321        void GetArgv(wxString const& cmd, std::vector<wxString> const& args, std::vector<std::unique_ptr<char[]>> & argList, std::unique_ptr<char *[]> & argV)
322        {
323                MakeArg(cmd, argList);
324                for (auto const& a : args) {
325                        MakeArg(a, argList);
326                }
327
328                argV.reset(new char *[argList.size() + 1]);
329                char ** v = argV.get();
330                for (auto const& a : argList) {
331                        *(v++) = a.get();
332                }
333                *v = 0;
334        }
335
336        bool Execute(wxString const& cmd, std::vector<wxString> const& args)
337        {
338                if (!CreatePipes()) {
339                        return false;
340                }
341
342                int pid = fork();
343                if (pid < 0) {
344                        return false;
345                }
346                else if (!pid) {
347                        // We're the child.
348
349                        // Close uneeded descriptors
350                        ResetFd(in_.write_);
351                        ResetFd(out_.read_);
352                        ResetFd(err_.read_);
353
354                        // Redirect to pipe
355                        if (dup2(in_.read_, STDIN_FILENO) == -1 ||
356                                dup2(out_.write_, STDOUT_FILENO) == -1 ||
357                                dup2(err_.write_, STDERR_FILENO) == -1)
358                        {
359                                _exit(-1);
360                        }
361
362                        std::vector<std::unique_ptr<char[]>> argList;
363                        std::unique_ptr<char *[]> argV;
364                        GetArgv(cmd, args, argList, argV);
365
366                        // Execute process
367                        execv(cmd.mb_str(), argV.get());//argV.get()); // noreturn on success
368
369                        _exit(-1);
370                }
371                else {
372                        // We're the parent
373                        pid_ = pid;
374
375                        // Close unneeded descriptors
376                        ResetFd(in_.read_);
377                        ResetFd(out_.write_);
378                        ResetFd(err_.write_);
379                }
380
381                return true;
382        }
383
384        void Kill()
385        {
386                in_.reset();
387
388                if (pid_ != -1) {
389                        kill(pid_, SIGTERM);
390
391                        int ret;
392                        do {
393                        }
394                        while((ret = waitpid(pid_, 0, 0)) == -1 && errno == EINTR);
395
396                        (void)ret;
397
398                        pid_ = -1;
399                }
400
401                out_.reset();
402                err_.reset();
403        }
404
405        int Read(char* buffer, unsigned int len)
406        {
407                int r;
408                do {
409                        r = read(out_.read_, buffer, len);
410                }
411                while (r == -1 && (errno == EAGAIN || errno == EINTR));
412
413                return r;
414        }
415
416        bool Write(char const* buffer, unsigned int len)
417        {
418                while (len) {
419                        int written;
420                        do {
421                                written = write(in_.write_, buffer, len);
422                        }
423                        while (written == -1 && (errno == EAGAIN || errno == EINTR));
424
425                        if (written <= 0) {
426                                return false;
427                        }
428
429                        len -= written;
430                        buffer += written;
431                }
432                return true;
433        }
434
435        Pipe in_;
436        Pipe out_;
437        Pipe err_;
438
439        int pid_{-1};
440};
441
442#endif
443
444
445CProcess::CProcess()
446        : impl_(std::make_unique<Impl>())
447{
448}
449
450CProcess::~CProcess()
451{
452        impl_.reset();
453}
454
455bool CProcess::Execute(wxString const& cmd, std::vector<wxString> const& args)
456{
457        return impl_ ? impl_->Execute(cmd, args) : false;
458}
459
460void CProcess::Kill()
461{
462        if (impl_) {
463                impl_->Kill();
464        }
465}
466
467int CProcess::Read(char* buffer, unsigned int len)
468{
469        return impl_ ? impl_->Read(buffer, len) : -1;
470}
471
472bool CProcess::Write(char const* buffer, unsigned int len)
473{
474        return impl_ ? impl_->Write(buffer, len) : false;
475}
Note: See TracBrowser for help on using the repository browser.