OpenVPN 3 Core Library
Loading...
Searching...
No Matches
call.hpp
Go to the documentation of this file.
1// OpenVPN -- An application to securely tunnel IP networks
2// over a single port, with support for SSL/TLS-based
3// session authentication and key exchange,
4// packet encryption, packet authentication, and
5// packet compression.
6//
7// Copyright (C) 2012- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11
12// execute a Windows command, capture the output
13
14#ifndef OPENVPN_WIN_CALL_H
15#define OPENVPN_WIN_CALL_H
16
17#include <windows.h>
18#include <shlobj.h>
19#include <knownfolders.h>
20
21#include <cstring>
22
26
27namespace openvpn::Win {
28
30
31inline std::string call(const std::string &cmd)
32{
33 // split command name from args
34 std::string name;
35 std::string args;
36 const size_t spcidx = cmd.find_first_of(" ");
37 if (spcidx != std::string::npos)
38 {
39 name = cmd.substr(0, spcidx);
40 if (spcidx + 1 < cmd.length())
41 args = cmd.substr(spcidx + 1);
42 }
43 else
44 name = cmd;
45
46 // get system path
47 wchar_t *syspath_ptr = nullptr;
48 if (::SHGetKnownFolderPath(FOLDERID_System, 0, nullptr, &syspath_ptr) != S_OK)
49 throw win_call("cannot get system path using SHGetKnownFolderPath");
50 unique_ptr_del<wchar_t> syspath(syspath_ptr,
51 [](wchar_t *p)
52 { ::CoTaskMemFree(p); });
53 // build command line
54 const size_t wcmdlen = ::wcslen(syspath.get()) + name.length() + args.length() + 64;
55 std::unique_ptr<wchar_t[]> wcmd(new wchar_t[wcmdlen]);
56 const char *spc = "";
57 if (!args.empty())
58 spc = " ";
59 ::_snwprintf(wcmd.get(), wcmdlen, L"\"%s\\%S.exe\"%S%S", syspath.get(), name.c_str(), spc, args.c_str());
60 wcmd.get()[wcmdlen - 1] = 0;
61 //::wprintf(L"CMD[%d]: %s\n", (int)::wcslen(wcmd.get()), wcmd.get());
62
63 // Set the bInheritHandle flag so pipe handles are inherited.
64 SECURITY_ATTRIBUTES saAttr;
65 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
66 saAttr.bInheritHandle = TRUE;
67 saAttr.lpSecurityDescriptor = nullptr;
68
69 // Create a pipe for the child process's STDOUT.
70 ScopedHANDLE cstdout_r; // child write side
71 ScopedHANDLE cstdout_w; // parent read side
72 if (!::CreatePipe(cstdout_r.ref(), cstdout_w.ref(), &saAttr, 0))
73 throw win_call("cannot create pipe for child stdout");
74
75 // Ensure the read handle to the pipe for STDOUT is not inherited.
76 if (!::SetHandleInformation(cstdout_r(), HANDLE_FLAG_INHERIT, 0))
77 throw win_call("SetHandleInformation failed for child stdout pipe");
78
79 // Set up members of the PROCESS_INFORMATION structure.
80 PROCESS_INFORMATION piProcInfo;
81 ::ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
82
83 // Set up members of the STARTUPINFO structure.
84 // This structure specifies the STDIN and STDOUT handles for redirection.
85 STARTUPINFOW siStartInfo;
86 ::ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
87 siStartInfo.cb = sizeof(STARTUPINFO);
88 siStartInfo.hStdError = cstdout_w();
89 siStartInfo.hStdOutput = cstdout_w();
90 siStartInfo.hStdInput = nullptr;
91 siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
92
93 // Create the child process.
94 if (!::CreateProcessW(nullptr,
95 wcmd.get(), // command line
96 nullptr, // process security attributes
97 nullptr, // primary thread security attributes
98 TRUE, // handles are inherited
99 0, // creation flags
100 nullptr, // use parent's environment
101 nullptr, // use parent's current directory
102 &siStartInfo, // STARTUPINFO pointer
103 &piProcInfo)) // receives PROCESS_INFORMATION
104 throw win_call("cannot create process");
105
106 // wrap handles to child process and its primary thread.
107 ScopedHANDLE process_hand(piProcInfo.hProcess);
108 ScopedHANDLE thread_hand(piProcInfo.hThread);
109
110 // close child's end of stdout/stderr pipe
111 cstdout_w.close();
112
113 // read child's stdout
114 const size_t outbuf_size = 512;
115 std::unique_ptr<char[]> outbuf(new char[outbuf_size]);
116 std::string out;
117 while (true)
118 {
119 DWORD dwRead;
120 if (!::ReadFile(cstdout_r(), outbuf.get(), outbuf_size, &dwRead, nullptr))
121 break;
122 if (dwRead == 0)
123 break;
124 out += std::string(outbuf.get(), 0, dwRead);
125 }
126
127 // decode output using console codepage, convert to utf16
128 // console codepage, used to decode output
129 UTF16 utf16output(Win::utf16(out, ::GetOEMCP()));
130
131 // re-encode utf16 to utf8
132 UTF8 utf8output(Win::utf8(utf16output.get()));
133 out.assign(utf8output.get());
134
135 // wait for child to exit
136 if (::WaitForSingleObject(process_hand(), INFINITE) == WAIT_FAILED)
137 throw win_call("WaitForSingleObject failed on child process handle");
138
139 return out;
140}
141} // namespace openvpn::Win
142
143#endif
#define OPENVPN_EXCEPTION(C)
char * utf8(wchar_t *str)
Definition unicode.hpp:57
std::unique_ptr< wchar_t[]> UTF16
Definition unicode.hpp:24
std::string call(const std::string &cmd)
Definition call.hpp:31
wchar_t * utf16(const std::string &str, int cp=CP_UTF8)
Definition unicode.hpp:29
std::unique_ptr< char[]> UTF8
Definition unicode.hpp:25
std::unique_ptr< T, std::function< void(T *)> > unique_ptr_del
Definition uniqueptr.hpp:21
static std::stringstream out
Definition test_path.cpp:10