diff options
Diffstat (limited to 'um/libservice/src/service.cpp')
-rw-r--r-- | um/libservice/src/service.cpp | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/um/libservice/src/service.cpp b/um/libservice/src/service.cpp new file mode 100644 index 0000000..8b5e043 --- /dev/null +++ b/um/libservice/src/service.cpp @@ -0,0 +1,276 @@ +// Copyright (c) 2015 Egor Tensin <Egor.Tensin@gmail.com> +// This file is part of the "Windows 7 drivers" project. +// For details, see https://github.com/egor-tensin/windows7-drivers. +// Distributed under the MIT License. + +#include "libservice/all.hpp" + +#include <Windows.h> + +#include <string> +#include <system_error> +#include <utility> +#include <vector> + +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<BYTE*>(&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>( + libservice::Service& a, + libservice::Service& b) LIBSERVICE_NOEXCEPT + { + a.swap(b); + } +} |