aboutsummaryrefslogblamecommitdiffstatshomepage
path: root/um/service/src/service.cpp
blob: 1cdfe7143ccf91b1f5714755c6159c9fbaac2625 (plain) (tree)
1
2
3
4
5
6
7
8
9
10



                                                                    
 
                             



                    
                       




                    


                                      

                                          
                    





                                               

                                                                              

             
                       







                                            
                    















                                               

                                                                              

             
                       



                                                       
                                               

                                               

                                                                              






                                                      
                                                                               

                                               

                                                                              




                                                           
                                       

                                               

                                                                              

             
 


                                      
         



                                          














                                                  

                                                                                  

             
 




                                                                                




                                                                           

                                               

                                                                              








                                                               
 






































                                                                        
 



                          


                                  
     
                                       

     



                                    
     
                                                    

     


                                  
     
                                         

     

                               
                                                       






                                      
                                                                





                       

                                                        



                              
                                            




                                      
                                                                


                       

                                                        




                                   
                                  
     
 
// 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>

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