aboutsummaryrefslogblamecommitdiffstatshomepage
path: root/utils/libservice/src/service.cpp
blob: 57bb5687b933a4cc1d81be05711fd954c244e724 (plain) (tree)
1
2
3
4
5
6
7
8
9







                                                                       
                             



                    
                       






                    


                                      

                                          
                    





                                               

                                                                              

             
                       







                                            
                    















                                               

                                                                              

             
                       



                                                       
                                               

                                               

                                                                              






                                                      
                                                                               

                                               

                                                                              




                                                           
                                       

                                               

                                                                              

             
 


                                      
         



                                          














                                                  

                                                                                  

             
 




                                                                                




                                                                           

                                               

                                                                              








                                                               
 






































                                                                        
 



                          


                                  
     
                                       

     



                                    
     
                                                    

     


                                  
     
                                         

     

                               
                                                       






                                      
                                                                





                       

                                                        



                              
                                            




                                      
                                                                


                       

                                                        




                                   
                                  

















                                                         
/**
 * \file
 * \author Egor Tensin <Egor.Tensin@gmail.com>
 * \date 2015
 * \copyright This file is licensed under the terms of the MIT License.
 *            See LICENSE.txt for details.
 */

#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);
    }
}