Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members   Related Pages  

NT_Service.cpp

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

Generated on Mon Jun 16 11:20:29 2003 for ACE by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002