NT_Service.cpp

Go to the documentation of this file.
00001 // NT_Service.cpp,v 4.28 2005/10/28 16:14:53 ossama Exp
00002 
00003 #include "ace/config-all.h"
00004 #if defined (ACE_WIN32) && \
00005    !defined (ACE_HAS_PHARLAP) && !defined (ACE_HAS_WINCE)
00006 
00007 #include "ace/NT_Service.h"
00008 
00009 #if !defined (__ACE_INLINE__)
00010 #include "ace/NT_Service.inl"
00011 #endif /* __ACE_INLINE__ */
00012 
00013 #include "ace/Log_Msg.h"
00014 #include "ace/Service_Object.h"
00015 #include "ace/OS_NS_errno.h"
00016 
00017 ACE_BEGIN_VERSIONED_NAMESPACE_DECL
00018 
00019 ACE_ALLOC_HOOK_DEFINE(ACE_NT_Service)
00020 
00021 // ACE_NT_Service destructor.
00022 
00023 ACE_NT_Service::~ACE_NT_Service (void)
00024 {
00025   if (this->svc_sc_handle_ != 0)
00026     {
00027       CloseServiceHandle (this->svc_sc_handle_);
00028       this->svc_sc_handle_ = 0;
00029     }
00030   delete [] this->desc_;
00031   delete [] this->name_;
00032   delete [] this->host_;
00033 }
00034 
00035 // This default implementation of ACE_NT_Service::open sets the
00036 // service's status to START_PENDING with the estimated time until
00037 // STARTED set to the value given when this object was constructed.
00038 // Then the svc function is called, which implements the guts of the
00039 // service.  Note that this function is running in a thread created by
00040 // the OS, not by ACE_Thread_Manager.  The thread manager does not
00041 // know anything about this thread.  The service can, however, use
00042 // ACE_Thread_Manager to start more threads if desired.  When the svc
00043 // function returns, the service status is set to STOPPED, and exit
00044 // codes set based on errno/GetLastError if the svc function returns
00045 // -1.
00046 //
00047 // The svc function is expected to set the service status to SERVICE_RUNNING
00048 // after it initializes.
00049 //
00050 // The handle_control function will be called for each time there is a
00051 // request for the service.  It is up to that function and svc to
00052 // cooperate to both respond appropriately to the request (by at least
00053 // updating the service's status) and to fulfill the request.
00054 
00055 int
00056 ACE_NT_Service::open (void *args)
00057 {
00058   ACE_UNUSED_ARG (args);
00059   this->report_status (SERVICE_START_PENDING, 0);
00060 
00061   int svc_return = this->svc ();
00062   if (svc_return == 0)
00063     {
00064       this->svc_status_.dwWin32ExitCode = NO_ERROR;
00065       this->svc_status_.dwServiceSpecificExitCode = 0;
00066     }
00067   else
00068     {
00069       if (errno == 0)
00070         {
00071           this->svc_status_.dwWin32ExitCode = GetLastError ();
00072         }
00073       else
00074         {
00075           this->svc_status_.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
00076           this->svc_status_.dwServiceSpecificExitCode = errno;
00077         }
00078     }
00079 
00080   return svc_return;
00081 
00082 }
00083 
00084 int
00085 ACE_NT_Service::fini (void)
00086 {
00087   return this->report_status (SERVICE_STOPPED, 0);
00088 }
00089 
00090 
00091 void
00092 ACE_NT_Service::handle_control (DWORD control_code)
00093 {
00094   switch (control_code)
00095     {
00096     case SERVICE_CONTROL_SHUTDOWN:
00097     case SERVICE_CONTROL_STOP:
00098       this->stop_requested (control_code);
00099       break;
00100 
00101     case SERVICE_CONTROL_PAUSE:
00102       this->pause_requested (control_code);
00103       break;
00104 
00105     case SERVICE_CONTROL_CONTINUE:
00106       this->continue_requested (control_code);
00107       break;
00108 
00109     case SERVICE_CONTROL_INTERROGATE:
00110       this->interrogate_requested (control_code);
00111       break;
00112     }
00113 }
00114 
00115 void
00116 ACE_NT_Service::stop_requested (DWORD)
00117 {
00118   this->report_status (SERVICE_STOP_PENDING);
00119   /* how to cancel? */
00120 }
00121 
00122 void
00123 ACE_NT_Service::pause_requested (DWORD)
00124 {
00125   this->report_status (SERVICE_PAUSE_PENDING);
00126   this->suspend ();
00127   this->report_status (SERVICE_PAUSED);
00128 }
00129 
00130 void
00131 ACE_NT_Service::continue_requested (DWORD)
00132 {
00133   this->report_status (SERVICE_CONTINUE_PENDING);
00134   this->resume ();
00135   this->report_status (SERVICE_RUNNING);
00136 }
00137 
00138 void
00139 ACE_NT_Service::interrogate_requested (DWORD)
00140 {
00141   this->report_status (0);
00142 }
00143 
00144 void
00145 ACE_NT_Service::name (const ACE_TCHAR *name, const ACE_TCHAR *desc)
00146 {
00147   delete [] this->desc_;
00148   delete [] this->name_;
00149 
00150   if (desc == 0)
00151     desc = name;
00152 
00153   this->name_ = ACE::strnew (name);
00154   this->desc_ = ACE::strnew (desc);
00155 }
00156 
00157 void
00158 ACE_NT_Service::host (const ACE_TCHAR *host)
00159 {
00160   delete [] this->host_;
00161 
00162   if (this->svc_sc_handle_ != 0)
00163     {
00164       CloseServiceHandle (this->svc_sc_handle_);
00165       this->svc_sc_handle_ = 0;
00166     }
00167 
00168   if (host == 0)
00169     {
00170       this->host_ = 0;
00171     }
00172   else
00173     {
00174       this->host_ = ACE::strnew (host);
00175     }
00176 }
00177 
00178 int
00179 ACE_NT_Service::insert (DWORD start_type,
00180                         DWORD error_control,
00181                         const ACE_TCHAR *exe_path,
00182                         const ACE_TCHAR *group_name,
00183                         LPDWORD tag_id,
00184                         const ACE_TCHAR *dependencies,
00185                         const ACE_TCHAR *account_name,
00186                         const ACE_TCHAR *password)
00187 {
00188   ACE_TCHAR this_exe[MAXPATHLEN + 2];
00189 
00190   // Insure ACE_OS::last_error finds GetLastError unless we set errno.
00191   errno = 0;
00192 
00193   if (exe_path == 0)
00194     {
00195       if (ACE_TEXT_GetModuleFileName (0, this_exe + 1, MAXPATHLEN) == 0)
00196         return -1;
00197       // Make sure that this_exe is quoted
00198       this_exe[0] = ACE_LIB_TEXT ('\"');
00199       ACE_OS::strcat (this_exe, ACE_LIB_TEXT ("\""));
00200       exe_path = this_exe;
00201     }
00202 
00203   SC_HANDLE sc_mgr = ACE_TEXT_OpenSCManager (this->host (),
00204                                              0,
00205                                              SC_MANAGER_ALL_ACCESS);
00206   if (sc_mgr == 0)
00207     return -1;
00208 
00209   SC_HANDLE sh = ACE_TEXT_CreateService (sc_mgr,
00210                                          this->name (),
00211                                          this->desc (),
00212                                          SERVICE_ALL_ACCESS,
00213                                          this->svc_status_.dwServiceType,
00214                                          start_type,
00215                                          error_control,
00216                                          exe_path,
00217                                          group_name,
00218                                          tag_id,
00219                                          dependencies,
00220                                          account_name,
00221                                          password);
00222   // If there was an error, stash GetLastError before CloseServiceHandle
00223   // smashes it. ACE_OS::last_error will find the saved error value.
00224   if (sh == 0)
00225     ACE_OS::set_errno_to_last_error ();
00226 
00227   CloseServiceHandle (sc_mgr);
00228 
00229   if (sh == 0)
00230     return -1;
00231 
00232   if (this->svc_sc_handle_ != 0)
00233     CloseServiceHandle (this->svc_sc_handle_);
00234   this->svc_sc_handle_ = sh;
00235 
00236   return 0;
00237 
00238 }
00239 
00240 int
00241 ACE_NT_Service::remove (void)
00242 {
00243   if (this->svc_sc_handle () == 0)
00244     return -1;
00245 
00246   if (DeleteService (this->svc_sc_handle()) == 0
00247       && GetLastError () != ERROR_SERVICE_MARKED_FOR_DELETE)
00248     return -1;
00249 
00250   return 0;
00251 }
00252 
00253 // Sets the startup type for the service.  Returns -1 on error, 0 on
00254 // success.
00255 int
00256 ACE_NT_Service::startup (DWORD startup)
00257 {
00258   SC_HANDLE svc = this->svc_sc_handle ();
00259   if (svc == 0)
00260     return -1;
00261 
00262   BOOL ok =
00263     ChangeServiceConfig (svc,
00264                          (DWORD) SERVICE_NO_CHANGE,// No change to service type
00265                          startup,                  // New startup type
00266                          (DWORD) SERVICE_NO_CHANGE,// No change to error ctrl
00267                          0,                        // No change to pathname
00268                          0,                        // No change to load group
00269                          0,                        // No change to tag
00270                          0,                        // No change to dependencies
00271                          0, 0,                     // No change to acct/passwd
00272                          0);                       // No change to name
00273 
00274   return ok ? 0 : -1;
00275 }
00276 
00277 // Returns the current startup type.
00278 
00279 DWORD
00280 ACE_NT_Service::startup (void)
00281 {
00282   // The query buffer will hold strings as well as the defined struct.
00283   // The string pointers in the struct point to other areas in the
00284   // passed memory area, so it has to be large enough to hold the
00285   // struct plus all the strings.
00286   char cfgbuff[1024];
00287   LPQUERY_SERVICE_CONFIG cfg;
00288   DWORD cfgsize, needed_size;
00289 
00290   SC_HANDLE svc = this->svc_sc_handle ();
00291   if (svc == 0)
00292   {
00293     // To distinguish this error from the QueryServiceConfig failure
00294     // below, return the DWORD equivalent of -2, rather than -1.
00295     return MAXDWORD - 1;
00296   }
00297   cfgsize = sizeof cfgbuff;
00298   cfg = (LPQUERY_SERVICE_CONFIG) cfgbuff;
00299   BOOL ok = QueryServiceConfig (svc, cfg, cfgsize, &needed_size);
00300   if (ok)
00301     return cfg->dwStartType;
00302   // Zero is a valid return value for QueryServiceConfig, so if
00303   // QueryServiceConfig fails, return the DWORD equivalent of -1.
00304   return MAXDWORD;
00305 
00306 }
00307 
00308 
00309 void
00310 ACE_NT_Service::capture_log_msg_attributes (void)
00311 {
00312   ACE_Log_Msg::init_hook (this->log_msg_attributes_);
00313 }
00314 
00315 void
00316 ACE_NT_Service::inherit_log_msg_attributes (void)
00317 {
00318   // There's no thread descriptor involved with a NT-started
00319   // thread, so the first arg is 0.
00320   ACE_Log_Msg::inherit_hook (0, this->log_msg_attributes_);
00321 }
00322 
00323 
00324 int
00325 ACE_NT_Service::start_svc (ACE_Time_Value *wait_time,
00326                            DWORD *svc_state,
00327                            DWORD argc, const ACE_TCHAR **argv)
00328 {
00329   SC_HANDLE svc = this->svc_sc_handle ();
00330   if (svc == 0)
00331     return -1;
00332 
00333   if (!ACE_TEXT_StartService (svc, argc, argv))
00334     return -1;
00335 
00336   this->wait_for_service_state (SERVICE_RUNNING, wait_time);
00337   if (svc_state != 0)
00338     *svc_state = this->svc_status_.dwCurrentState;
00339 
00340   return 0;
00341 }
00342 
00343 int
00344 ACE_NT_Service::stop_svc (ACE_Time_Value *wait_time,
00345                           DWORD *svc_state)
00346 {
00347   SC_HANDLE svc = this->svc_sc_handle ();
00348   if (svc == 0)
00349     return -1;
00350 
00351   if (!ControlService (svc,
00352                        SERVICE_CONTROL_STOP,
00353                        &this->svc_status_))
00354     return -1;
00355 
00356   this->wait_for_service_state (SERVICE_STOPPED,
00357                                 wait_time);
00358   if (svc_state != 0)
00359     *svc_state = this->svc_status_.dwCurrentState;
00360 
00361   return 0;
00362 }
00363 
00364 int
00365 ACE_NT_Service::pause_svc (ACE_Time_Value *wait_time,
00366                            DWORD *svc_state)
00367 {
00368   SC_HANDLE svc = this->svc_sc_handle ();
00369   if (svc == 0)
00370     return -1;
00371 
00372   if (!ControlService (svc,
00373                        SERVICE_CONTROL_PAUSE,
00374                        &this->svc_status_))
00375     return -1;
00376 
00377   this->wait_for_service_state (SERVICE_PAUSED,
00378                                 wait_time);
00379   if (svc_state != 0)
00380     *svc_state = this->svc_status_.dwCurrentState;
00381 
00382   return 0;
00383 }
00384 
00385 int
00386 ACE_NT_Service::continue_svc (ACE_Time_Value *wait_time,
00387                               DWORD *svc_state)
00388 {
00389   SC_HANDLE svc = this->svc_sc_handle ();
00390   if (svc == 0)
00391     return -1;
00392 
00393   if (!ControlService (svc,
00394                        SERVICE_CONTROL_CONTINUE,
00395                        &this->svc_status_))
00396     return -1;
00397 
00398   this->wait_for_service_state (SERVICE_RUNNING,
00399                                 wait_time);
00400   if (svc_state != 0)
00401     *svc_state = this->svc_status_.dwCurrentState;
00402 
00403   return 0;
00404 }
00405 
00406 DWORD
00407 ACE_NT_Service::state (ACE_Time_Value *wait_hint)
00408 {
00409   DWORD curr_state;
00410 
00411   if (this->state (&curr_state,
00412                    wait_hint) == -1)
00413     return 0;
00414   return curr_state;
00415 }
00416 
00417 int
00418 ACE_NT_Service::state (DWORD *pstate,
00419                        ACE_Time_Value *wait_hint)
00420 {
00421   SC_HANDLE svc = this->svc_sc_handle ();
00422 
00423   if (svc == 0)
00424     return -1;
00425 
00426   // Need to create a temporary copy of this variable since the
00427   // QueryServiceStatus call will modify the setting depending on the
00428   // current state of the Service.  If the service is currently
00429   // STOPPED, the value will be cleared.
00430   DWORD controls_accepted = this->svc_status_.dwControlsAccepted;
00431 
00432   if (QueryServiceStatus (svc,
00433                           &this->svc_status_) == 0)
00434     return -1;
00435 
00436   if (wait_hint != 0)
00437     wait_hint->msec (this->svc_status_.dwWaitHint);
00438 
00439   *pstate = this->svc_status_.dwCurrentState;
00440   this->svc_status_.dwControlsAccepted = controls_accepted;
00441   return 0;
00442 }
00443 
00444 // test_access
00445 //
00446 // Open a new handle, ignoring any handle open in svc_sc_handle_.
00447 // This function's results are returned without leaving the handle
00448 // open.
00449 
00450 int
00451 ACE_NT_Service::test_access (DWORD desired_access)
00452 {
00453   int status = -1;     // Guilty until proven innocent
00454 
00455   SC_HANDLE sc_mgr = ACE_TEXT_OpenSCManager (this->host (),
00456                                              0,
00457                                              GENERIC_READ);
00458   if (sc_mgr != 0)
00459     {
00460       SC_HANDLE handle = ACE_TEXT_OpenService (sc_mgr,
00461                                                this->name (),
00462                                                desired_access);
00463       CloseServiceHandle (sc_mgr);
00464       if (handle != 0)
00465         {
00466           status = 0;
00467           CloseServiceHandle (handle);
00468         }
00469     }
00470 
00471   return status;
00472 }
00473 
00474 // report_status
00475 //
00476 // Reports the current status.  If new_status is not 0, it sets the
00477 // status to the new value before reporting.  NOTE - this assumes that
00478 // no actual service status values have the value 0.  This is true in
00479 // WinNT 4.  If the status is a 'pending' type, the supplied time hint
00480 // is used unless it's 0, in which case the existing hint is used.
00481 // The dwWaitHint is not updated by this function.  The checkpoint is
00482 // incremented by one after a pending report.
00483 
00484 int
00485 ACE_NT_Service::report_status (DWORD new_status,
00486                                DWORD time_hint)
00487 {
00488   int bump_checkpoint = 0;
00489   int retval = 0;
00490   DWORD save_controls = 0;
00491 
00492   if (new_status != 0)
00493     this->svc_status_.dwCurrentState = new_status;
00494   switch (this->svc_status_.dwCurrentState)
00495     {
00496   case SERVICE_START_PENDING:
00497     save_controls = this->svc_status_.dwControlsAccepted;
00498     this->svc_status_.dwControlsAccepted = 0;
00499     /* Fall through */
00500   case SERVICE_STOP_PENDING:
00501   case SERVICE_CONTINUE_PENDING:
00502   case SERVICE_PAUSE_PENDING:
00503     this->svc_status_.dwWaitHint = time_hint ? time_hint : this->start_time_;
00504     bump_checkpoint = 1;
00505     break;
00506 
00507   default:
00508     this->svc_status_.dwCheckPoint = 0;
00509   }
00510 
00511   retval = SetServiceStatus (this->svc_handle_,
00512                              &this->svc_status_) ? 0 : -1;
00513 
00514   if (save_controls != 0)
00515     this->svc_status_.dwControlsAccepted = save_controls;
00516 
00517   if (bump_checkpoint)
00518     ++this->svc_status_.dwCheckPoint;
00519 
00520   return retval;
00521 }
00522 
00523 SC_HANDLE
00524 ACE_NT_Service::svc_sc_handle (void)
00525 {
00526   if (this->svc_sc_handle_ == 0)
00527     {
00528       SC_HANDLE sc_mgr = ACE_TEXT_OpenSCManager (this->host (),
00529                                                  0,
00530                                                  SC_MANAGER_ALL_ACCESS);
00531       if (sc_mgr != 0)
00532         {
00533           this->svc_sc_handle_ = ACE_TEXT_OpenService (sc_mgr,
00534                                                        this->name (),
00535                                                        SERVICE_ALL_ACCESS);
00536           if (this->svc_sc_handle_ == 0)
00537             ACE_OS::set_errno_to_last_error ();
00538           CloseServiceHandle (sc_mgr);
00539         }
00540       else
00541         ACE_OS::set_errno_to_last_error ();
00542     }
00543 
00544   return this->svc_sc_handle_;
00545 }
00546 
00547 void
00548 ACE_NT_Service::wait_for_service_state (DWORD desired_state,
00549                                         ACE_Time_Value *wait_time)
00550 {
00551   DWORD last_state = 0;
00552   DWORD last_check_point = 0;
00553   int first_time = 1;
00554   int service_ok;
00555 
00556   ACE_Time_Value time_out = ACE_OS::gettimeofday ();
00557   if (wait_time != 0)
00558     time_out += *wait_time;
00559 
00560   // Poll until the service reaches the desired state.
00561   for (;;)
00562     {
00563       service_ok = 0 != QueryServiceStatus (this->svc_sc_handle_,
00564                                             &this->svc_status_);
00565 
00566       // If we cannot query the service, we are done.
00567       if (!service_ok)
00568         break;
00569 
00570       // If the service has the desired state, we are done.
00571       if (desired_state == this->svc_status_.dwCurrentState)
00572         break;
00573 
00574       // If we time-out, we are done
00575       if (wait_time != 0 && ACE_OS::gettimeofday () > time_out )
00576         {
00577           errno = ETIME;
00578           break;
00579         }
00580 
00581       if (first_time)
00582         {
00583           // remember the service state, the first time we wait
00584           last_state       = this->svc_status_.dwCurrentState;
00585           last_check_point = this->svc_status_.dwCheckPoint;
00586           first_time = 0;
00587         }
00588       else
00589         {
00590           // update the state change.
00591           if (last_state != this->svc_status_.dwCurrentState)
00592             {
00593               last_state       = this->svc_status_.dwCurrentState;
00594               last_check_point = this->svc_status_.dwCheckPoint;
00595             }
00596           else
00597             {
00598               // The check-point should have increased
00599               if (this->svc_status_.dwCheckPoint > last_check_point)
00600                 last_check_point = this->svc_status_.dwCheckPoint;
00601               else
00602                 {
00603                   // Service control failure, we are done.
00604                   service_ok = 0;
00605                   break;
00606                 }
00607             }
00608         }
00609 
00610       ::Sleep (this->svc_status_.dwWaitHint);
00611     }
00612 
00613   return;
00614 }
00615 
00616 ACE_END_VERSIONED_NAMESPACE_DECL
00617 
00618 #endif /* ACE_WIN32 && !ACE_HAS_PHARLAP */

Generated on Thu Nov 9 09:41:57 2006 for ACE by doxygen 1.3.6