mirror of
https://codeberg.org/anoncontributorxmr/monero.git
synced 2024-11-14 07:03:29 +01:00
342 lines
7.1 KiB
C++
342 lines
7.1 KiB
C++
|
#undef UNICODE
|
||
|
#undef _UNICODE
|
||
|
|
||
|
#include "common/scoped_message_writer.h"
|
||
|
#include "daemonizer/windows_service.h"
|
||
|
#include "string_tools.h"
|
||
|
#include <chrono>
|
||
|
#include <iostream>
|
||
|
#include <utility>
|
||
|
#include <memory>
|
||
|
#include <shellapi.h>
|
||
|
#include <thread>
|
||
|
#include <windows.h>
|
||
|
|
||
|
namespace windows {
|
||
|
|
||
|
namespace {
|
||
|
typedef std::unique_ptr<std::remove_pointer<SC_HANDLE>::type, decltype(&::CloseServiceHandle)> service_handle;
|
||
|
|
||
|
std::string get_last_error()
|
||
|
{
|
||
|
LPSTR p_error_text = nullptr;
|
||
|
|
||
|
FormatMessage(
|
||
|
FORMAT_MESSAGE_FROM_SYSTEM
|
||
|
| FORMAT_MESSAGE_ALLOCATE_BUFFER
|
||
|
| FORMAT_MESSAGE_IGNORE_INSERTS
|
||
|
, nullptr
|
||
|
, GetLastError()
|
||
|
, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)
|
||
|
, reinterpret_cast<LPSTR>(&p_error_text)
|
||
|
, 0
|
||
|
, nullptr
|
||
|
);
|
||
|
|
||
|
if (nullptr == p_error_text)
|
||
|
{
|
||
|
return "";
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return std::string{p_error_text};
|
||
|
LocalFree(p_error_text);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool relaunch_as_admin(
|
||
|
std::string const & command
|
||
|
, std::string const & arguments
|
||
|
)
|
||
|
{
|
||
|
SHELLEXECUTEINFO info{};
|
||
|
info.cbSize = sizeof(info);
|
||
|
info.lpVerb = "runas";
|
||
|
info.lpFile = command.c_str();
|
||
|
info.lpParameters = arguments.c_str();
|
||
|
info.hwnd = nullptr;
|
||
|
info.nShow = SW_SHOWNORMAL;
|
||
|
if (!ShellExecuteEx(&info))
|
||
|
{
|
||
|
tools::fail_msg_writer() << "Admin relaunch failed: " << get_last_error();
|
||
|
return false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// When we relaunch as admin, Windows opens a new window. This just pauses
|
||
|
// to allow the user to read any output.
|
||
|
void pause_to_display_admin_window_messages()
|
||
|
{
|
||
|
std::chrono::milliseconds how_long{1500};
|
||
|
std::this_thread::sleep_for(how_long);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool check_admin(bool & result)
|
||
|
{
|
||
|
BOOL is_admin = FALSE;
|
||
|
PSID p_administrators_group = nullptr;
|
||
|
|
||
|
SID_IDENTIFIER_AUTHORITY nt_authority = SECURITY_NT_AUTHORITY;
|
||
|
|
||
|
if (!AllocateAndInitializeSid(
|
||
|
&nt_authority
|
||
|
, 2
|
||
|
, SECURITY_BUILTIN_DOMAIN_RID
|
||
|
, DOMAIN_ALIAS_RID_ADMINS
|
||
|
, 0, 0, 0, 0, 0, 0
|
||
|
, &p_administrators_group
|
||
|
))
|
||
|
{
|
||
|
tools::fail_msg_writer() << "Security Identifier creation failed: " << get_last_error();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (!CheckTokenMembership(
|
||
|
nullptr
|
||
|
, p_administrators_group
|
||
|
, &is_admin
|
||
|
))
|
||
|
{
|
||
|
tools::fail_msg_writer() << "Permissions check failed: " << get_last_error();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
result = is_admin ? true : false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool ensure_admin(
|
||
|
std::string const & arguments
|
||
|
)
|
||
|
{
|
||
|
bool admin;
|
||
|
|
||
|
if (!check_admin(admin))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (admin)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
std::string command = epee::string_tools::get_current_module_path();
|
||
|
relaunch_as_admin(command, arguments);
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool install_service(
|
||
|
std::string const & service_name
|
||
|
, std::string const & arguments
|
||
|
)
|
||
|
{
|
||
|
std::string command = epee::string_tools::get_current_module_path();
|
||
|
std::string full_command = command + arguments;
|
||
|
|
||
|
service_handle p_manager{
|
||
|
OpenSCManager(
|
||
|
nullptr
|
||
|
, nullptr
|
||
|
, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE
|
||
|
)
|
||
|
, &::CloseServiceHandle
|
||
|
};
|
||
|
if (p_manager == nullptr)
|
||
|
{
|
||
|
tools::fail_msg_writer() << "Couldn't connect to service manager: " << get_last_error();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
service_handle p_service{
|
||
|
CreateService(
|
||
|
p_manager.get()
|
||
|
, service_name.c_str()
|
||
|
, service_name.c_str()
|
||
|
, 0
|
||
|
//, GENERIC_EXECUTE | GENERIC_READ
|
||
|
, SERVICE_WIN32_OWN_PROCESS
|
||
|
, SERVICE_DEMAND_START
|
||
|
, SERVICE_ERROR_NORMAL
|
||
|
, full_command.c_str()
|
||
|
, nullptr
|
||
|
, nullptr
|
||
|
, ""
|
||
|
//, "NT AUTHORITY\\LocalService"
|
||
|
, nullptr // Implies LocalSystem account
|
||
|
, nullptr
|
||
|
)
|
||
|
, &::CloseServiceHandle
|
||
|
};
|
||
|
if (p_service == nullptr)
|
||
|
{
|
||
|
tools::fail_msg_writer() << "Couldn't create service: " << get_last_error();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
tools::success_msg_writer() << "Service installed";
|
||
|
|
||
|
pause_to_display_admin_window_messages();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool start_service(
|
||
|
std::string const & service_name
|
||
|
)
|
||
|
{
|
||
|
tools::msg_writer() << "Starting service";
|
||
|
|
||
|
SERVICE_STATUS_PROCESS service_status = {};
|
||
|
DWORD unused = 0;
|
||
|
|
||
|
service_handle p_manager{
|
||
|
OpenSCManager(
|
||
|
nullptr
|
||
|
, nullptr
|
||
|
, SC_MANAGER_CONNECT
|
||
|
)
|
||
|
, &::CloseServiceHandle
|
||
|
};
|
||
|
if (p_manager == nullptr)
|
||
|
{
|
||
|
tools::fail_msg_writer() << "Couldn't connect to service manager: " << get_last_error();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
service_handle p_service{
|
||
|
OpenService(
|
||
|
p_manager.get()
|
||
|
, service_name.c_str()
|
||
|
//, SERVICE_START | SERVICE_QUERY_STATUS
|
||
|
, SERVICE_START
|
||
|
)
|
||
|
, &::CloseServiceHandle
|
||
|
};
|
||
|
if (p_service == nullptr)
|
||
|
{
|
||
|
tools::fail_msg_writer() << "Couldn't find service: " << get_last_error();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (!StartService(
|
||
|
p_service.get()
|
||
|
, 0
|
||
|
, nullptr
|
||
|
))
|
||
|
{
|
||
|
tools::fail_msg_writer() << "Service start request failed: " << get_last_error();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
tools::success_msg_writer() << "Service started";
|
||
|
|
||
|
pause_to_display_admin_window_messages();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool stop_service(
|
||
|
std::string const & service_name
|
||
|
)
|
||
|
{
|
||
|
tools::msg_writer() << "Stopping service";
|
||
|
|
||
|
service_handle p_manager{
|
||
|
OpenSCManager(
|
||
|
nullptr
|
||
|
, nullptr
|
||
|
, SC_MANAGER_CONNECT
|
||
|
)
|
||
|
, &::CloseServiceHandle
|
||
|
};
|
||
|
if (p_manager == nullptr)
|
||
|
{
|
||
|
tools::fail_msg_writer() << "Couldn't connect to service manager: " << get_last_error();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
service_handle p_service{
|
||
|
OpenService(
|
||
|
p_manager.get()
|
||
|
, service_name.c_str()
|
||
|
, SERVICE_STOP | SERVICE_QUERY_STATUS
|
||
|
)
|
||
|
, &::CloseServiceHandle
|
||
|
};
|
||
|
if (p_service == nullptr)
|
||
|
{
|
||
|
tools::fail_msg_writer() << "Couldn't find service: " << get_last_error();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
SERVICE_STATUS status = {};
|
||
|
if (!ControlService(p_service.get(), SERVICE_CONTROL_STOP, &status))
|
||
|
{
|
||
|
tools::fail_msg_writer() << "Couldn't request service stop: " << get_last_error();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
tools::success_msg_writer() << "Service stopped";
|
||
|
|
||
|
pause_to_display_admin_window_messages();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool uninstall_service(
|
||
|
std::string const & service_name
|
||
|
)
|
||
|
{
|
||
|
service_handle p_manager{
|
||
|
OpenSCManager(
|
||
|
nullptr
|
||
|
, nullptr
|
||
|
, SC_MANAGER_CONNECT
|
||
|
)
|
||
|
, &::CloseServiceHandle
|
||
|
};
|
||
|
if (p_manager == nullptr)
|
||
|
{
|
||
|
tools::fail_msg_writer() << "Couldn't connect to service manager: " << get_last_error();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
service_handle p_service{
|
||
|
OpenService(
|
||
|
p_manager.get()
|
||
|
, service_name.c_str()
|
||
|
, SERVICE_QUERY_STATUS | DELETE
|
||
|
)
|
||
|
, &::CloseServiceHandle
|
||
|
};
|
||
|
if (p_service == nullptr)
|
||
|
{
|
||
|
tools::fail_msg_writer() << "Couldn't find service: " << get_last_error();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
SERVICE_STATUS status = {};
|
||
|
if (!DeleteService(p_service.get()))
|
||
|
{
|
||
|
tools::fail_msg_writer() << "Couldn't uninstall service: " << get_last_error();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
tools::success_msg_writer() << "Service uninstalled";
|
||
|
|
||
|
pause_to_display_admin_window_messages();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
} // namespace windows
|