Electroneum
Loading...
Searching...
No Matches
windows_service.cpp
Go to the documentation of this file.
1// Copyrights(c) 2017-2021, The Electroneum Project
2// Copyrights(c) 2014-2019, The Monero Project
3//
4// All rights reserved.
5//
6// Redistribution and use in source and binary forms, with or without modification, are
7// permitted provided that the following conditions are met:
8//
9// 1. Redistributions of source code must retain the above copyright notice, this list of
10// conditions and the following disclaimer.
11//
12// 2. Redistributions in binary form must reproduce the above copyright notice, this list
13// of conditions and the following disclaimer in the documentation and/or other
14// materials provided with the distribution.
15//
16// 3. Neither the name of the copyright holder nor the names of its contributors may be
17// used to endorse or promote products derived from this software without specific
18// prior written permission.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
21// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
22// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
28// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30#include <boost/chrono/chrono.hpp>
31#include <boost/thread/thread.hpp>
32
33#undef UNICODE
34#undef _UNICODE
35
38#include "string_tools.h"
39#include <chrono>
40#include <iostream>
41#include <utility>
42#include <memory>
43#include <shellapi.h>
44#include <thread>
45#include <windows.h>
46
47namespace windows {
48
49namespace {
50 typedef std::unique_ptr<std::remove_pointer<SC_HANDLE>::type, decltype(&::CloseServiceHandle)> service_handle;
51
52 std::string get_last_error()
53 {
54 LPSTR p_error_text = nullptr;
55
56 FormatMessage(
57 FORMAT_MESSAGE_FROM_SYSTEM
58 | FORMAT_MESSAGE_ALLOCATE_BUFFER
59 | FORMAT_MESSAGE_IGNORE_INSERTS
60 , nullptr
61 , GetLastError()
62 , MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)
63 , reinterpret_cast<LPSTR>(&p_error_text)
64 , 0
65 , nullptr
66 );
67
68 if (nullptr == p_error_text)
69 {
70 return "";
71 }
72 else
73 {
74 std::string ret{p_error_text};
75 LocalFree(p_error_text);
76 return ret;
77 }
78 }
79
80 bool relaunch_as_admin(
81 std::string const & command
82 , std::string const & arguments
83 )
84 {
85 SHELLEXECUTEINFO info{};
86 info.cbSize = sizeof(info);
87 info.lpVerb = "runas";
88 info.lpFile = command.c_str();
89 info.lpParameters = arguments.c_str();
90 info.hwnd = nullptr;
91 info.nShow = SW_SHOWNORMAL;
92 if (!ShellExecuteEx(&info))
93 {
94 tools::fail_msg_writer() << "Admin relaunch failed: " << get_last_error();
95 return false;
96 }
97 else
98 {
99 return true;
100 }
101 }
102
103 // When we relaunch as admin, Windows opens a new window. This just pauses
104 // to allow the user to read any output.
105 void pause_to_display_admin_window_messages()
106 {
107 boost::chrono::milliseconds how_long{1500};
108 boost::this_thread::sleep_for(how_long);
109 }
110}
111
112bool check_admin(bool & result)
113{
114 BOOL is_admin = FALSE;
115 PSID p_administrators_group = nullptr;
116
117 SID_IDENTIFIER_AUTHORITY nt_authority = SECURITY_NT_AUTHORITY;
118
119 if (!AllocateAndInitializeSid(
120 &nt_authority
121 , 2
122 , SECURITY_BUILTIN_DOMAIN_RID
123 , DOMAIN_ALIAS_RID_ADMINS
124 , 0, 0, 0, 0, 0, 0
125 , &p_administrators_group
126 ))
127 {
128 tools::fail_msg_writer() << "Security Identifier creation failed: " << get_last_error();
129 return false;
130 }
131
132 if (!CheckTokenMembership(
133 nullptr
134 , p_administrators_group
135 , &is_admin
136 ))
137 {
138 tools::fail_msg_writer() << "Permissions check failed: " << get_last_error();
139 return false;
140 }
141
142 result = is_admin ? true : false;
143
144 return true;
145}
146
148 std::string const & arguments
149 )
150{
151 bool admin;
152
153 if (!check_admin(admin))
154 {
155 return false;
156 }
157
158 if (admin)
159 {
160 return true;
161 }
162 else
163 {
164 std::string command = epee::string_tools::get_current_module_path();
165 relaunch_as_admin(command, arguments);
166 return false;
167 }
168}
169
171 std::string const & service_name
172 , std::string const & arguments
173 )
174{
175 std::string command = epee::string_tools::get_current_module_path();
176 std::string full_command = command + arguments;
177
178 service_handle p_manager{
179 OpenSCManager(
180 nullptr
181 , nullptr
182 , SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE
183 )
184 , &::CloseServiceHandle
185 };
186 if (p_manager == nullptr)
187 {
188 tools::fail_msg_writer() << "Couldn't connect to service manager: " << get_last_error();
189 return false;
190 }
191
192 service_handle p_service{
193 CreateService(
194 p_manager.get()
195 , service_name.c_str()
196 , service_name.c_str()
197 , 0
198 //, GENERIC_EXECUTE | GENERIC_READ
199 , SERVICE_WIN32_OWN_PROCESS
200 , SERVICE_DEMAND_START
201 , SERVICE_ERROR_NORMAL
202 , full_command.c_str()
203 , nullptr
204 , nullptr
205 , ""
206 //, "NT AUTHORITY\\LocalService"
207 , nullptr // Implies LocalSystem account
208 , nullptr
209 )
210 , &::CloseServiceHandle
211 };
212 if (p_service == nullptr)
213 {
214 tools::fail_msg_writer() << "Couldn't create service: " << get_last_error();
215 return false;
216 }
217
218 tools::success_msg_writer() << "Service installed";
219
220 pause_to_display_admin_window_messages();
221
222 return true;
223}
224
226 std::string const & service_name
227 )
228{
229 tools::msg_writer() << "Starting service";
230
231 SERVICE_STATUS_PROCESS service_status = {};
232 DWORD unused = 0;
233
234 service_handle p_manager{
235 OpenSCManager(
236 nullptr
237 , nullptr
238 , SC_MANAGER_CONNECT
239 )
240 , &::CloseServiceHandle
241 };
242 if (p_manager == nullptr)
243 {
244 tools::fail_msg_writer() << "Couldn't connect to service manager: " << get_last_error();
245 return false;
246 }
247
248 service_handle p_service{
249 OpenService(
250 p_manager.get()
251 , service_name.c_str()
252 //, SERVICE_START | SERVICE_QUERY_STATUS
253 , SERVICE_START
254 )
255 , &::CloseServiceHandle
256 };
257 if (p_service == nullptr)
258 {
259 tools::fail_msg_writer() << "Couldn't find service: " << get_last_error();
260 return false;
261 }
262
263 if (!StartService(
264 p_service.get()
265 , 0
266 , nullptr
267 ))
268 {
269 tools::fail_msg_writer() << "Service start request failed: " << get_last_error();
270 return false;
271 }
272
273 tools::success_msg_writer() << "Service started";
274
275 pause_to_display_admin_window_messages();
276
277 return true;
278}
279
281 std::string const & service_name
282 )
283{
284 tools::msg_writer() << "Stopping service";
285
286 service_handle p_manager{
287 OpenSCManager(
288 nullptr
289 , nullptr
290 , SC_MANAGER_CONNECT
291 )
292 , &::CloseServiceHandle
293 };
294 if (p_manager == nullptr)
295 {
296 tools::fail_msg_writer() << "Couldn't connect to service manager: " << get_last_error();
297 return false;
298 }
299
300 service_handle p_service{
301 OpenService(
302 p_manager.get()
303 , service_name.c_str()
304 , SERVICE_STOP | SERVICE_QUERY_STATUS
305 )
306 , &::CloseServiceHandle
307 };
308 if (p_service == nullptr)
309 {
310 tools::fail_msg_writer() << "Couldn't find service: " << get_last_error();
311 return false;
312 }
313
314 SERVICE_STATUS status = {};
315 if (!ControlService(p_service.get(), SERVICE_CONTROL_STOP, &status))
316 {
317 tools::fail_msg_writer() << "Couldn't request service stop: " << get_last_error();
318 return false;
319 }
320
321 tools::success_msg_writer() << "Service stopped";
322
323 pause_to_display_admin_window_messages();
324
325 return true;
326}
327
329 std::string const & service_name
330 )
331{
332 service_handle p_manager{
333 OpenSCManager(
334 nullptr
335 , nullptr
336 , SC_MANAGER_CONNECT
337 )
338 , &::CloseServiceHandle
339 };
340 if (p_manager == nullptr)
341 {
342 tools::fail_msg_writer() << "Couldn't connect to service manager: " << get_last_error();
343 return false;
344 }
345
346 service_handle p_service{
347 OpenService(
348 p_manager.get()
349 , service_name.c_str()
350 , SERVICE_QUERY_STATUS | DELETE
351 )
352 , &::CloseServiceHandle
353 };
354 if (p_service == nullptr)
355 {
356 tools::fail_msg_writer() << "Couldn't find service: " << get_last_error();
357 return false;
358 }
359
360 SERVICE_STATUS status = {};
361 if (!DeleteService(p_service.get()))
362 {
363 tools::fail_msg_writer() << "Couldn't uninstall service: " << get_last_error();
364 return false;
365 }
366
367 tools::success_msg_writer() << "Service uninstalled";
368
369 pause_to_display_admin_window_messages();
370
371 return true;
372}
373
374} // namespace windows
scoped_message_writer fail_msg_writer()
scoped_message_writer msg_writer(epee::console_colors color=epee::console_color_default)
scoped_message_writer success_msg_writer(bool color=true)
bool install_service(std::string const &service_name, std::string const &arguments)
bool check_admin(bool &result)
bool ensure_admin(std::string const &arguments)
bool start_service(std::string const &service_name)
bool stop_service(std::string const &service_name)
bool uninstall_service(std::string const &service_name)
#define true
CXA_THROW_INFO_T * info