1/****************************************************************************
2 * Copyright (C) 2017 Intel Corporation.   All Rights Reserved.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 ****************************************************************************/
23
24#include "common/os.h"
25#include <vector>
26#include <array>
27#include <sstream>
28
29#if defined(_WIN32)
30#include <shlobj.h>
31#endif // Windows
32
33#if defined(__APPLE__) || defined(FORCE_LINUX) || defined(__linux__) || defined(__gnu_linux__)
34#include <pthread.h>
35#endif // Linux
36
37#if defined(_WIN32)
38static const DWORD MS_VC_EXCEPTION = 0x406D1388;
39
40#pragma pack(push, 8)
41typedef struct tagTHREADNAME_INFO
42{
43    DWORD  dwType;     // Must be 0x1000.
44    LPCSTR szName;     // Pointer to name (in user addr space).
45    DWORD  dwThreadID; // Thread ID (-1=caller thread).
46    DWORD  dwFlags;    // Reserved for future use, must be zero.
47} THREADNAME_INFO;
48#pragma pack(pop)
49
50void LegacySetThreadName(const char* pThreadName)
51{
52    THREADNAME_INFO info;
53    info.dwType     = 0x1000;
54    info.szName     = pThreadName;
55    info.dwThreadID = GetCurrentThreadId();
56    info.dwFlags    = 0;
57
58    if (!IsDebuggerPresent())
59    {
60        // No debugger attached to interpret exception, no need to actually do it
61        return;
62    }
63
64#pragma warning(push)
65#pragma warning(disable : 6320 6322)
66    __try
67    {
68        RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
69    }
70    __except (EXCEPTION_EXECUTE_HANDLER)
71    {
72    }
73#pragma warning(pop)
74}
75#endif // _WIN32
76
77void SWR_API SetCurrentThreadName(const char* pThreadName)
78{
79#if defined(_WIN32)
80    // The SetThreadDescription API was brought in version 1607 of Windows 10.
81    typedef HRESULT(WINAPI * PFNSetThreadDescription)(HANDLE hThread, PCWSTR lpThreadDescription);
82    // The SetThreadDescription API works even if no debugger is attached.
83    auto pfnSetThreadDescription = reinterpret_cast<PFNSetThreadDescription>(
84        GetProcAddress(GetModuleHandleA("Kernel32.dll"), "SetThreadDescription"));
85
86    if (!pfnSetThreadDescription)
87    {
88        // try KernelBase.dll
89        pfnSetThreadDescription = reinterpret_cast<PFNSetThreadDescription>(
90            GetProcAddress(GetModuleHandleA("KernelBase.dll"), "SetThreadDescription"));
91    }
92
93    if (pfnSetThreadDescription)
94    {
95        std::string  utf8Name = pThreadName;
96        std::wstring wideName;
97        wideName.resize(utf8Name.size() + 1);
98        swprintf_s(&(wideName.front()), wideName.size(), L"%S", utf8Name.c_str());
99        HRESULT hr = pfnSetThreadDescription(GetCurrentThread(), wideName.c_str());
100        SWR_ASSERT(SUCCEEDED(hr), "Failed to set thread name to %s", pThreadName);
101
102        // Fall through - it seems like some debuggers only recognize the exception
103    }
104
105    // Fall back to exception based hack
106    LegacySetThreadName(pThreadName);
107#endif // _WIN32
108
109#if defined(FORCE_LINUX) || defined(__linux__) || defined(__gnu_linux__)
110    pthread_setname_np(pthread_self(), pThreadName);
111#endif // Linux
112}
113
114static void
115SplitString(std::vector<std::string>& out_segments, const std::string& input, char splitToken)
116{
117    out_segments.clear();
118
119    std::istringstream f(input);
120    std::string        s;
121    while (std::getline(f, s, splitToken))
122    {
123        if (s.size())
124        {
125            out_segments.push_back(s);
126        }
127    }
128}
129
130void SWR_API CreateDirectoryPath(const std::string& path)
131{
132#if defined(_WIN32)
133    SHCreateDirectoryExA(nullptr, path.c_str(), nullptr);
134#endif // Windows
135
136#if defined(__APPLE__) || defined(FORCE_LINUX) || defined(__linux__) || defined(__gnu_linux__)
137    std::vector<std::string> pathSegments;
138    SplitString(pathSegments, path, '/');
139
140    std::string tmpPath;
141    for (auto const& segment : pathSegments)
142    {
143        tmpPath.push_back('/');
144        tmpPath += segment;
145
146        int result = mkdir(tmpPath.c_str(), 0777);
147        if (result == -1 && errno != EEXIST)
148        {
149            break;
150        }
151    }
152#endif // Unix
153}
154
155/// Execute Command (block until finished)
156/// @returns process exit value
157int SWR_API ExecCmd(const std::string& cmd,     ///< (In) Command line string
158                    const char* pOptEnvStrings, ///< (Optional In) Environment block for new process
159                    std::string*       pOptStdOut, ///< (Optional Out) Standard Output text
160                    std::string*       pOptStdErr, ///< (Optional Out) Standard Error text
161                    const std::string* pOptStdIn)  ///< (Optional In) Standard Input text
162{
163    int rvalue = -1;
164
165#if defined(_WIN32)
166    struct WinPipe
167    {
168        HANDLE hRead;
169        HANDLE hWrite;
170    };
171    std::array<WinPipe, 3> hPipes = {};
172
173    SECURITY_ATTRIBUTES saAttr  = {sizeof(SECURITY_ATTRIBUTES)};
174    saAttr.bInheritHandle       = TRUE; // Pipe handles are inherited by child process.
175    saAttr.lpSecurityDescriptor = NULL;
176
177    {
178        bool bFail = false;
179        for (WinPipe& p : hPipes)
180        {
181            if (!CreatePipe(&p.hRead, &p.hWrite, &saAttr, 0))
182            {
183                bFail = true;
184            }
185        }
186
187        if (bFail)
188        {
189            for (WinPipe& p : hPipes)
190            {
191                CloseHandle(p.hRead);
192                CloseHandle(p.hWrite);
193            }
194            return rvalue;
195        }
196    }
197
198    STARTUPINFOA StartupInfo{};
199    StartupInfo.cb      = sizeof(STARTUPINFOA);
200    StartupInfo.dwFlags = STARTF_USESTDHANDLES;
201    StartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
202    StartupInfo.wShowWindow = SW_HIDE;
203    if (pOptStdIn)
204    {
205        StartupInfo.hStdInput = hPipes[0].hRead;
206    }
207    StartupInfo.hStdOutput = hPipes[1].hWrite;
208    StartupInfo.hStdError  = hPipes[2].hWrite;
209    PROCESS_INFORMATION procInfo{};
210
211    // CreateProcess can modify the string
212    std::string local_cmd = cmd;
213
214    BOOL ProcessValue = CreateProcessA(NULL,
215                                       (LPSTR)local_cmd.c_str(),
216                                       NULL,
217                                       NULL,
218                                       TRUE,
219                                       0,
220                                       (LPVOID)pOptEnvStrings,
221                                       NULL,
222                                       &StartupInfo,
223                                       &procInfo);
224
225    if (ProcessValue && procInfo.hProcess)
226    {
227        auto ReadFromPipe = [](HANDLE hPipe, std::string* pOutStr) {
228            char  buf[1024];
229            DWORD dwRead  = 0;
230            DWORD dwAvail = 0;
231            while (true)
232            {
233                if (!::PeekNamedPipe(hPipe, NULL, 0, NULL, &dwAvail, NULL))
234                {
235                    break;
236                }
237
238                if (!dwAvail) // no data available, return
239                {
240                    break;
241                }
242
243                if (!::ReadFile(hPipe,
244                                buf,
245                                std::min<size_t>(sizeof(buf) - 1, size_t(dwAvail)),
246                                &dwRead,
247                                NULL) ||
248                    !dwRead)
249                {
250                    // error, the child process might ended
251                    break;
252                }
253
254                buf[dwRead] = 0;
255                if (pOutStr)
256                {
257                    (*pOutStr) += buf;
258                }
259            }
260        };
261        bool   bProcessEnded = false;
262        size_t bytesWritten  = 0;
263        do
264        {
265            if (pOptStdIn && (pOptStdIn->size() > bytesWritten))
266            {
267                DWORD bytesToWrite = static_cast<DWORD>(pOptStdIn->size()) - bytesWritten;
268                if (!::WriteFile(hPipes[0].hWrite,
269                                 pOptStdIn->data() + bytesWritten,
270                                 bytesToWrite,
271                                 &bytesToWrite,
272                                 nullptr))
273                {
274                    // Failed to write to pipe
275                    break;
276                }
277                bytesWritten += bytesToWrite;
278            }
279
280            // Give some timeslice (50ms), so we won't waste 100% cpu.
281            bProcessEnded = (WaitForSingleObject(procInfo.hProcess, 50) == WAIT_OBJECT_0);
282
283            ReadFromPipe(hPipes[1].hRead, pOptStdOut);
284            ReadFromPipe(hPipes[2].hRead, pOptStdErr);
285        } while (!bProcessEnded);
286
287        DWORD exitVal = 0;
288        if (!GetExitCodeProcess(procInfo.hProcess, &exitVal))
289        {
290            exitVal = 1;
291        }
292
293        CloseHandle(procInfo.hProcess);
294        CloseHandle(procInfo.hThread);
295
296        rvalue = exitVal;
297    }
298
299    for (WinPipe& p : hPipes)
300    {
301        CloseHandle(p.hRead);
302        CloseHandle(p.hWrite);
303    }
304
305#else
306
307    // Non-Windows implementation
308
309#endif
310
311    return rvalue;
312}
313