torservice.cpp

Go to the documentation of this file.
00001 /*
00002 **  This file is part of Vidalia, and is subject to the license terms in the
00003 **  LICENSE file, found in the top level directory of this distribution. If 
00004 **  you did not receive the LICENSE file with this file, you may obtain it
00005 **  from the Vidalia source package distributed by the Vidalia Project at
00006 **  http://www.vidalia-project.net/. No part of Vidalia, including this file,
00007 **  may be copied, modified, propagated, or distributed except according to
00008 **  the terms described in the LICENSE file.
00009 */
00010 
00011 /* 
00012 ** \file torservice.cpp
00013 ** \version $Id: torservice.cpp 2695 2008-06-12 06:50:05Z edmanm $
00014 ** \brief Starts, stops, installs, and uninstalls a Tor service (Win32).
00015 */
00016 
00017 #include <QLibrary>
00018 
00019 #include "tcglobal.h"
00020 #include "torservice.h"
00021 
00022 /** Returned by TorService::exitCode() when we are unable to determine the
00023  * actual exit code of the service (unless, of course, Tor returns -999999). */
00024 #define UNKNOWN_EXIT_CODE     -999999
00025 
00026 /** List of dynamically loaded NT service functions. */
00027 ServiceFunctions TorService::_service_fns = 
00028   { false,
00029     NULL, NULL, NULL, NULL, NULL,
00030     NULL, NULL, NULL, NULL, NULL };
00031 
00032 
00033 /** Default ctor. */
00034 TorService::TorService(QObject *parent)
00035   : QObject(parent)
00036 {
00037   _scm = openSCM();
00038 }
00039 
00040 /** Default dtor. */
00041 TorService::~TorService()
00042 {
00043   closeHandle(_scm);
00044 }
00045 
00046 /** Returns true if services are supported. */
00047 bool
00048 TorService::isSupported()
00049 {
00050   return (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based);
00051 }
00052 
00053 /** Dyanmically loads NT service related functions from advapi32.dll. This
00054  * function is adapted from Tor's nt_service_load_library() function. See
00055  * LICENSE for details on Tor's license. */
00056 bool
00057 TorService::loadServiceFunctions()
00058 {
00059 #define LOAD_SERVICE_FN(f) do {                                         \
00060   void *fn;                                                             \
00061   if (!((fn = QLibrary::resolve("advapi32", #f)))) {                    \
00062       return false;                                                     \
00063     } else {                                                            \
00064       _service_fns.f = (f ## _fn) fn;                                   \
00065     }                                                                   \
00066   } while (0)
00067 
00068   if (!isSupported()) {
00069     _service_fns.loaded = false;
00070   } else if (!_service_fns.loaded) {
00071     LOAD_SERVICE_FN(ChangeServiceConfig2A);
00072     LOAD_SERVICE_FN(CloseServiceHandle);
00073     LOAD_SERVICE_FN(ControlService);
00074     LOAD_SERVICE_FN(CreateServiceA);
00075     LOAD_SERVICE_FN(DeleteService);
00076     LOAD_SERVICE_FN(OpenSCManagerA);
00077     LOAD_SERVICE_FN(OpenServiceA);
00078     LOAD_SERVICE_FN(QueryServiceStatus);
00079     LOAD_SERVICE_FN(SetServiceStatus);
00080     LOAD_SERVICE_FN(StartServiceA);
00081     _service_fns.loaded = true;
00082   }
00083   return _service_fns.loaded;
00084 }
00085 
00086 /** Opens a handle to the Tor service. Returns NULL on error. */
00087 SC_HANDLE
00088 TorService::openService()
00089 {
00090   if (!loadServiceFunctions())
00091     return NULL;
00092   if (!_scm)
00093     return NULL;
00094   return _service_fns.OpenServiceA(_scm, 
00095                                    (LPCTSTR)TOR_SERVICE_NAME, 
00096                                    TOR_SERVICE_ACCESS);
00097 }
00098 
00099 /** Opens a handle to the service control manager. Returns NULL on error. */
00100 SC_HANDLE
00101 TorService::openSCM()
00102 {
00103   if (!loadServiceFunctions())
00104     return NULL;
00105   return _service_fns.OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS);
00106 }
00107 
00108 /** Closes the service <b>handle</b>. */
00109 void
00110 TorService::closeHandle(SC_HANDLE handle)
00111 {
00112   if (!loadServiceFunctions())
00113     return;
00114   _service_fns.CloseServiceHandle(handle);
00115 }
00116 
00117 /** Returns true if the Tor service is installed. */
00118 bool
00119 TorService::isInstalled()
00120 {
00121   bool installed;
00122   SC_HANDLE service = openService();
00123   installed = (service != NULL);
00124   closeHandle(service);
00125   return installed;
00126 }
00127 
00128 /** Returns true if the Tor service is running. */
00129 bool
00130 TorService::isRunning()
00131 {
00132   return (status() == SERVICE_RUNNING);
00133 }
00134 
00135 /** Starts Tor service. */
00136 void
00137 TorService::start()
00138 {
00139   SC_HANDLE service = openService();
00140 
00141   if (!service) {
00142     tc::error("Bug: We tried to start the Tor service, but it is not installed.");
00143     emit startFailed(tr("The Tor service is not installed."));
00144     return;
00145   }
00146 
00147   /* Starting a service can take up to 30 seconds! */
00148   if (status() != SERVICE_RUNNING) {
00149     int tries = 0;
00150     tc::debug("Starting the Tor service.");
00151     _service_fns.StartServiceA(service, 0, NULL);
00152 
00153     while ((status() != SERVICE_RUNNING) && ++tries <= 5)
00154       Sleep(1000);
00155   }
00156 
00157   if (status() == SERVICE_RUNNING) {
00158     emit started();
00159   } else {
00160     tc::error("Unable to start the Tor service.");
00161     emit startFailed(tr("Unable to start the Tor service."));
00162   }
00163   closeHandle(service);
00164 }
00165 
00166 /** Stops Tor service. */
00167 bool
00168 TorService::stop()
00169 {
00170   SC_HANDLE service = openService();
00171 
00172   if (!service)
00173     return false;
00174 
00175   if (status() != SERVICE_STOPPED) {
00176     SERVICE_STATUS stat;
00177     stat.dwCurrentState = SERVICE_RUNNING;
00178     tc::debug("Stopping the Tor service.");
00179     if (_service_fns.ControlService(service, SERVICE_CONTROL_STOP, &stat)) {
00180       /* XXX Five seconds isn't long enough to wait when we're stopping a Tor
00181        * that is running as a server, but we don't want to block for 30
00182        * seconds. It would be nice if we could get an async notification when
00183        * the service stops or fails to stop. */
00184       int tries = 0;
00185       while ((status() != SERVICE_STOPPED) && (++tries <= 5))
00186         Sleep(1000);
00187     }
00188   }
00189   closeHandle(service);
00190 
00191   /* Find out if the service really stopped and return the result */
00192   if (status() == SERVICE_STOPPED) {
00193     emit finished(exitCode(), exitStatus());
00194     return true;
00195   }
00196   /* XXX This needs an actual reason message. */
00197   tc::error("Unable to stop the Tor service.");
00198   return false;
00199 }
00200 
00201 /** Returns the exit code of the last Tor service that finished. */
00202 int
00203 TorService::exitCode()
00204 {
00205   SC_HANDLE service;
00206   int exitCode = UNKNOWN_EXIT_CODE;
00207   
00208   service = openService();
00209   if (service) {
00210     SERVICE_STATUS s;
00211     if (_service_fns.QueryServiceStatus(service, &s)) {
00212       /* Services return one exit code, but it could be in one of two
00213        * variables. Fun. */
00214       exitCode = (int)(s.dwWin32ExitCode == ERROR_SERVICE_SPECIFIC_ERROR
00215                                             ? s.dwServiceSpecificExitCode
00216                                             : s.dwWin32ExitCode);
00217     }
00218     closeHandle(service);
00219   }
00220   return exitCode;
00221 }
00222 
00223 /** Returns the exit status of the last Tor service that finished. */
00224 QProcess::ExitStatus
00225 TorService::exitStatus()
00226 {
00227   /* NT services don't really have an equivalent to QProcess::CrashExit, so 
00228    * this just returns QProcess::NormalExit. Tor _could_ set
00229    * dwServiceSpecificExitCode to some magic value when it starts and then
00230    * set it to the real exit code when Tor exits. Then we would know if the
00231    * service crashed when dwServiceSpecificExitCode is still the magic value.
00232    * However, I don't care and it doesn't really matter anyway. */
00233   return QProcess::NormalExit;
00234 }
00235 
00236 /** Installs the Tor service. Returns true if the service was successfully
00237  * installed or already exists. */
00238 bool
00239 TorService::install(const QString &torPath, const QString &torrc,
00240                     quint16 controlPort)
00241 {
00242   SC_HANDLE service;
00243   
00244   if (!_scm)
00245     return false;
00246  
00247   service = openService();
00248   if (!service) {
00249     QString command = QString("\"%1\" --nt-service -f \"%2\" ControlPort %3")
00250                                                  .arg(torPath)
00251                                                  .arg(torrc)
00252                                                  .arg(controlPort);
00253 
00254     tc::debug("Installing the Tor service using the command line '%1'")
00255                                                           .arg(command);
00256     service = _service_fns.CreateServiceA(_scm, 
00257                               (LPCTSTR)TOR_SERVICE_NAME, (LPCTSTR)TOR_SERVICE_DISP,
00258                               TOR_SERVICE_ACCESS, SERVICE_WIN32_OWN_PROCESS,
00259                               SERVICE_AUTO_START, SERVICE_ERROR_IGNORE,
00260                               (LPCTSTR)command.toAscii().data(), NULL, NULL, NULL, 
00261                               NULL, NULL);
00262     if (!service) {
00263       /* XXX This needs an actual reason message. */
00264       tc::error("Failed to install the Tor service.");
00265       return false;
00266     }
00267 
00268     SERVICE_DESCRIPTION desc;
00269     desc.lpDescription = TOR_SERVICE_DESC;
00270     _service_fns.ChangeServiceConfig2A(service, 
00271                                        SERVICE_CONFIG_DESCRIPTION, &desc);
00272     closeHandle(service);
00273   }
00274   return true;
00275 }
00276 
00277 /** Removes the Tor service. Returns true if the service was removed
00278  * successfully or does not exist. */
00279 bool
00280 TorService::remove()
00281 {
00282   bool removed = true;
00283   SC_HANDLE service = openService();
00284 
00285   if (service) {
00286     stop();
00287     tc::debug("Removing the Tor service.");
00288     removed = _service_fns.DeleteService(service);
00289     closeHandle(service);
00290   }
00291   if (!removed) {
00292     /* XXX This needs an actual reason message. */
00293     tc::error("Failed to remove the Tor service.");
00294   }
00295   return removed;
00296 }
00297 
00298 /** Gets the status of the Tor service. */
00299 DWORD
00300 TorService::status()
00301 {
00302   SC_HANDLE service;
00303   SERVICE_STATUS s;
00304   DWORD stat = SERVICE_ERROR;
00305   
00306   service = openService();
00307   if (service && _service_fns.QueryServiceStatus(service, &s))
00308     stat = s.dwCurrentState;
00309   closeHandle(service);
00310   return stat;
00311 }
00312 

Generated on Wed Nov 26 21:02:42 2008 for Vidalia by  doxygen 1.5.7.1