torservice.cpp

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

Generated on Wed Sep 5 15:49:28 2007 for Vidalia by  doxygen 1.5.3