/** * \file * \author Egor Tensin * \date 2015 * \copyright This file is licensed under the terms of the MIT License. * See LICENSE.txt for details. */ #include "libservice/all.hpp" #include #include #include #include #include namespace libservice { namespace { ServiceHandle open_service( const ServiceManager& mgr, const std::string& name) { const auto raw = OpenServiceA( mgr, name.c_str(), SERVICE_ALL_ACCESS); if (NULL == raw) { const auto ec = GetLastError(); throw std::system_error( ec, WindowsErrorCategory::get(), LIBSERVICE_ERROR_PREFIX); } return raw; } ServiceHandle install_service( const ServiceManager& mgr, const std::string& name, const std::string& bin_path) { const auto raw = CreateServiceA( mgr, name.c_str(), name.c_str(), SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, bin_path.c_str(), NULL, NULL, NULL, NULL, NULL); if (NULL == raw) { const auto ec = GetLastError(); throw std::system_error( ec, WindowsErrorCategory::get(), LIBSERVICE_ERROR_PREFIX); } return raw; } void start_service(const ServiceHandle& handle) { if (!StartService(handle, 0, NULL)) { const auto ec = GetLastError(); throw std::system_error( ec, WindowsErrorCategory::get(), LIBSERVICE_ERROR_PREFIX); } } void stop_service(const ServiceHandle& handle) { SERVICE_STATUS service_status; if (!ControlService(handle, SERVICE_CONTROL_STOP, &service_status)) { const auto ec = GetLastError(); throw std::system_error( ec, WindowsErrorCategory::get(), LIBSERVICE_ERROR_PREFIX); } } void uninstall_service(const ServiceHandle& handle) { if (!DeleteService(handle)) { const auto ec = GetLastError(); throw std::system_error( ec, WindowsErrorCategory::get(), LIBSERVICE_ERROR_PREFIX); } } bool service_exists( const ServiceManager& mgr, const std::string& name) { const auto raw = OpenServiceA( mgr, name.c_str(), SERVICE_QUERY_STATUS); if (NULL != raw) { ServiceHandle handle(raw); return true; } const auto ec = GetLastError(); switch (ec) { case ERROR_SERVICE_DOES_NOT_EXIST: return false; default: throw std::system_error( ec, WindowsErrorCategory::get(), LIBSERVICE_ERROR_PREFIX); } } SERVICE_STATUS_PROCESS query_service_status(const ServiceHandle& handle) { SERVICE_STATUS_PROCESS status; DWORD nbreq; const auto buf_ptr = reinterpret_cast(&status); const auto buf_size = sizeof(status); if (!QueryServiceStatusEx( handle, SC_STATUS_PROCESS_INFO, buf_ptr, buf_size, &nbreq)) { const auto ec = GetLastError(); throw std::system_error( ec, WindowsErrorCategory::get(), LIBSERVICE_ERROR_PREFIX); } return status; } DWORD query_service_state(const ServiceHandle& handle) { return query_service_status(handle).dwCurrentState; } SERVICE_STATUS_PROCESS wait_for_service_state( const ServiceHandle& handle, const DWORD desired_state) { auto status = query_service_status(handle); DWORD old_timestamp = GetTickCount(); DWORD old_check_point = status.dwCheckPoint; DWORD old_wait_hint = status.dwWaitHint; while (desired_state != status.dwCurrentState) { DWORD wait_time = old_wait_hint / 10; if (wait_time < 1000) wait_time = 1000; else if (wait_time > 10000) wait_time = 10000; Sleep(wait_time); status = query_service_status(handle); if (desired_state == status.dwCurrentState) break; if (status.dwCheckPoint > old_check_point) { old_timestamp = GetTickCount(); old_check_point = status.dwCheckPoint; old_wait_hint = status.dwWaitHint; } else if (GetTickCount() - old_timestamp > old_wait_hint) { return status; } } return status; } } Service Service::open( const ServiceManager& mgr, const std::string& name) { return open_service(mgr, name); } Service Service::install( const ServiceManager& mgr, const std::string& name, const std::string& bin_path) { return install_service(mgr, name, bin_path); } bool Service::exists( const ServiceManager& mgr, const std::string& name) { return service_exists(mgr, name); } void Service::start() const { const auto state = query_service_state(handle); switch (state) { case SERVICE_STOPPED: break; case SERVICE_STOP_PENDING: wait_for_service_state(handle, SERVICE_STOPPED); break; default: return; } start_service(handle); wait_for_service_state(handle, SERVICE_RUNNING); } void Service::stop() const { switch (query_service_state(handle)) { case SERVICE_STOPPED: return; case SERVICE_STOP_PENDING: wait_for_service_state(handle, SERVICE_STOPPED); return; } stop_service(handle); wait_for_service_state(handle, SERVICE_STOPPED); } void Service::uninstall() const { stop(); uninstall_service(handle); } void swap(Service& a, Service& b) LIBSERVICE_NOEXCEPT { a.swap(b); } } namespace std { template <> void swap( libservice::Service& a, libservice::Service& b) LIBSERVICE_NOEXCEPT { a.swap(b); } }