Passenger: SpawnManager.h Source File

- Main Page
- Modules
- Namespaces
- Classes
- Files

- File List

# SpawnManager.h

    00001 /*
    00002  *  Phusion Passenger - http://www.modrails.com/
    00003  *  Copyright (c) 2010 Phusion
    00004  *
    00005  *  "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
    00006  *
    00007  *  Permission is hereby granted, free of charge, to any person obtaining a copy
    00008  *  of this software and associated documentation files (the "Software"), to deal
    00009  *  in the Software without restriction, including without limitation the rights
    00010  *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    00011  *  copies of the Software, and to permit persons to whom the Software is
    00012  *  furnished to do so, subject to the following conditions:
    00013  *
    00014  *  The above copyright notice and this permission notice shall be included in
    00015  *  all copies or substantial portions of the Software.
    00016  *
    00017  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    00018  *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    00019  *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    00020  *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    00021  *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    00022  *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    00023  *  THE SOFTWARE.
    00024  */
    00025 #ifndef _PASSENGER_SPAWN_MANAGER_H_
    00026 #define _PASSENGER_SPAWN_MANAGER_H_
    00027
    00028 #include <string>
    00029 #include <vector>
    00030 #include <boost/shared_ptr.hpp>
    00031 #include <boost/thread.hpp>
    00032 #include <boost/function.hpp>
    00033 #include <oxt/system_calls.hpp>
    00034 #include <oxt/backtrace.hpp>
    00035
    00036 #include <sys/types.h>
    00037 #include <sys/wait.h>
    00038 #include <sys/stat.h>
    00039 #include <arpa/inet.h>
    00040 #include <cstdio>
    00041 #include <cstdarg>
    00042 #include <unistd.h>
    00043 #include <errno.h>
    00044 #include <grp.h>
    00045 #include <pwd.h>
    00046 #include <signal.h>
    00047
    00048 #include "AbstractSpawnManager.h"
    00049 #include "ServerInstanceDir.h"
    00050 #include "FileDescriptor.h"
    00051 #include "Constants.h"
    00052 #include "MessageChannel.h"
    00053 #include "AccountsDatabase.h"
    00054 #include "RandomGenerator.h"
    00055 #include "Exceptions.h"
    00056 #include "Logging.h"
    00057 #include "Utils/Base64.h"
    00058 #include "Utils/SystemTime.h"
    00059
    00060 namespace Passenger {
    00061
    00062 using namespace std;
    00063 using namespace boost;
    00064 using namespace oxt;
    00065
    00066 /**
    00067  * An AbstractSpawnManager implementation.
    00068  *
    00069  * Internally, this class makes use of a spawn server, which is written in Ruby. This server
    00070  * is automatically started when a SpawnManager instance is created, and automatically
    00071  * shutdown when that instance is destroyed. The existance of the spawn server is almost
    00072  * totally transparent to users of this class. Spawn requests are sent to the server,
    00073  * and details about the spawned process is returned.
    00074  *
    00075  * If the spawn server dies during the middle of an operation, it will be restarted.
    00076  * See spawn() for full details.
    00077  *
    00078  * The communication channel with the server is anonymous, i.e. no other processes
    00079  * can access the communication channel, so communication is guaranteed to be safe
    00080  * (unless, of course, if the spawn server itself is a trojan).
    00081  *
    00082  * The server will try to keep the spawning time as small as possible, by keeping
    00083  * corresponding Ruby on Rails frameworks and application code in memory. So the second
    00084  * time a process of the same application is spawned, the spawn time is significantly
    00085  * lower than the first time. Nevertheless, spawning is a relatively expensive operation
    00086  * (compared to the processing of a typical HTTP request/response), and so should be
    00087  * avoided whenever possible.
    00088  *
    00089  * See the documentation of the spawn server for full implementation details.
    00090  *
    00091  * @ingroup Support
    00092  */
    00093 class SpawnManager: public AbstractSpawnManager {
    00094 private:
    00095         static const int SERVER_SOCKET_FD = 3;
    00096         static const int OWNER_SOCKET_FD  = 4;
    00097         static const int HIGHEST_FD       = OWNER_SOCKET_FD;
    00098
    00099         string spawnServerCommand;
    00100         ServerInstanceDir::GenerationPtr generation;
    00101         AccountsDatabasePtr accountsDatabase;
    00102         string rubyCommand;
    00103         AnalyticsLoggerPtr analyticsLogger;
    00104
    00105         boost::mutex lock;
    00106         RandomGenerator random;
    00107
    00108         pid_t pid;
    00109         FileDescriptor ownerSocket;
    00110         string socketFilename;
    00111         string socketPassword;
    00112         bool serverNeedsRestart;
    00113
    00114         static void deleteAccount(AccountsDatabasePtr accountsDatabase, const string &username) {
    00115                 accountsDatabase->remove(username);
    00116         }
    00117
    00118         /**
    00119          * Restarts the spawn server.
    00120          *
    00121          * @pre System call interruption is disabled.
    00122          * @throws RuntimeException An error occurred while creating a Unix server socket.
    00123          * @throws SystemException An error occured while trying to setup the spawn server.
    00124          * @throws IOException An error occurred while generating random data.
    00125          */
    00126         void restartServer() {
    00127                 TRACE_POINT();
    00128                 if (pid != 0) {
    00129                         UPDATE_TRACE_POINT();
    00130                         ownerSocket.close();
    00131
    00132                         /* Wait at most 5 seconds for the spawn server to exit.
    00133                          * If that doesn't work, kill it, then wait at most 5 seconds
    00134                          * for it to exit.
    00135                          */
    00136                         time_t begin = syscalls::time(NULL);
    00137                         bool done = false;
    00138                         while (!done && syscalls::time(NULL) - begin < 5) {
    00139                                 if (syscalls::waitpid(pid, NULL, WNOHANG) > 0) {
    00140                                         done = true;
    00141                                 } else {
    00142                                         syscalls::usleep(100000);
    00143                                 }
    00144                         }
    00145                         UPDATE_TRACE_POINT();
    00146                         if (!done) {
    00147                                 UPDATE_TRACE_POINT();
    00148                                 P_TRACE(2, "Spawn server did not exit in time, killing it...");
    00149                                 syscalls::kill(pid, SIGTERM);
    00150                                 begin = syscalls::time(NULL);
    00151                                 while (syscalls::time(NULL) - begin < 5) {
    00152                                         if (syscalls::waitpid(pid, NULL, WNOHANG) > 0) {
    00153                                                 break;
    00154                                         } else {
    00155                                                 syscalls::usleep(100000);
    00156                                         }
    00157                                 }
    00158                         }
    00159                         pid = 0;
    00160                 }
    00161
    00162                 FileDescriptor serverSocket;
    00163                 string socketFilename;
    00164                 string socketPassword;
    00165                 int ret, fds[2];
    00166
    00167                 UPDATE_TRACE_POINT();
    00168                 socketFilename = generation->getPath() + "/spawn-server/socket." +
    00169                         toString(getpid()) + "." +
    00170                         toString((unsigned long long) this);
    00171                 socketPassword = Base64::encode(random.generateByteString(32));
    00172                 serverSocket = createUnixServer(socketFilename.c_str());
    00173                 do {
    00174                         ret = chmod(socketFilename.c_str(), S_IRUSR | S_IWUSR | S_IXUSR);
    00175                 } while (ret == -1 && errno == EINTR);
    00176                 if (ret == -1) {
    00177                         int e = errno;
    00178                         syscalls::unlink(socketFilename.c_str());
    00179                         throw FileSystemException("Cannot set permissions on '" + socketFilename + "'",
    00180                                 e, socketFilename);
    00181                 }
    00182
    00183                 if (syscalls::socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == -1) {
    00184                         int e = errno;
    00185                         syscalls::unlink(socketFilename.c_str());
    00186                         throw SystemException("Cannot create a Unix socket", e);
    00187                 }
    00188
    00189                 UPDATE_TRACE_POINT();
    00190                 pid = syscalls::fork();
    00191                 if (pid == 0) {
    00192                         dup2(serverSocket, HIGHEST_FD + 1);
    00193                         dup2(fds[1], HIGHEST_FD + 2);
    00194                         dup2(HIGHEST_FD + 1, SERVER_SOCKET_FD);
    00195                         dup2(HIGHEST_FD + 2, OWNER_SOCKET_FD);
    00196
    00197                         // Close all unnecessary file descriptors
    00198                         for (long i = sysconf(_SC_OPEN_MAX) - 1; i > HIGHEST_FD; i--) {
    00199                                 close(i);
    00200                         }
    00201
    00202                         execlp(rubyCommand.c_str(),
    00203                                 rubyCommand.c_str(),
    00204                                 spawnServerCommand.c_str(),
    00205                                 /* The spawn server changes the process names of the subservers
    00206                                  * that it starts, for better usability. However, the process name length
    00207                                  * (as shown by ps) is limited. Here, we try to expand that limit by
    00208                                  * deliberately passing a useless whitespace string to the spawn server.
    00209                                  * This argument is ignored by the spawn server. This works on some
    00210                                  * systems, such as Ubuntu Linux.
    00211                                  */
    00212                                 "                                                             ",
    00213                                 (char *) NULL);
    00214                         int e = errno;
    00215                         fprintf(stderr, "*** Passenger ERROR (%s:%d):\n"
    00216                                 "Could not start the spawn server: %s: %s (%d)\n",
    00217                                 __FILE__, __LINE__,
    00218                                 rubyCommand.c_str(), strerror(e), e);
    00219                         fflush(stderr);
    00220                         _exit(1);
    00221                 } else if (pid == -1) {
    00222                         int e = errno;
    00223                         syscalls::unlink(socketFilename.c_str());
    00224                         syscalls::close(fds[0]);
    00225                         syscalls::close(fds[1]);
    00226                         pid = 0;
    00227                         throw SystemException("Unable to fork a process", e);
    00228                 } else {
    00229                         FileDescriptor ownerSocket = fds[0];
    00230                         syscalls::close(fds[1]);
    00231                         serverSocket.close();
    00232
    00233                         // Pass arguments to spawn server.
    00234                         MessageChannel ownerSocketChannel(ownerSocket);
    00235                         ownerSocketChannel.writeRaw(socketFilename + "\n");
    00236                         ownerSocketChannel.writeRaw(socketPassword + "\n");
    00237                         ownerSocketChannel.writeRaw(generation->getPath() + "\n");
    00238                         if (analyticsLogger != NULL) {
    00239                                 ownerSocketChannel.writeRaw(analyticsLogger->getAddress() + "\n");
    00240                                 ownerSocketChannel.writeRaw(analyticsLogger->getUsername() + "\n");
    00241                                 ownerSocketChannel.writeRaw(Base64::encode(analyticsLogger->getPassword()) + "\n");
    00242                                 ownerSocketChannel.writeRaw(analyticsLogger->getNodeName() + "\n");
    00243                         } else {
    00244                                 ownerSocketChannel.writeRaw("\n");
    00245                                 ownerSocketChannel.writeRaw("\n");
    00246                                 ownerSocketChannel.writeRaw("\n");
    00247                                 ownerSocketChannel.writeRaw("\n");
    00248                         }
    00249
    00250                         this->ownerSocket    = ownerSocket;
    00251                         this->socketFilename = socketFilename;
    00252                         this->socketPassword = socketPassword;
    00253                         spawnServerStarted();
    00254                 }
    00255         }
    00256
    00257         /**
    00258          * Connects to the spawn server and returns the connection.
    00259          *
    00260          * @throws RuntimeException
    00261          * @throws SystemException
    00262          * @throws boost::thread_interrupted
    00263          */
    00264         FileDescriptor connect() const {
    00265                 TRACE_POINT();
    00266                 FileDescriptor fd = connectToUnixServer(socketFilename.c_str());
    00267                 MessageChannel channel(fd);
    00268                 UPDATE_TRACE_POINT();
    00269                 channel.writeScalar(socketPassword);
    00270                 return fd;
    00271         }
    00272
    00273         /**
    00274          * Send the spawn command to the spawn server.
    00275          *
    00276          * @param PoolOptions The spawn options to use.
    00277          * @return A Process smart pointer, representing the spawned process.
    00278          * @throws SpawnException Something went wrong.
    00279          * @throws Anything thrown by options.environmentVariables->getItems().
    00280          * @throws boost::thread_interrupted
    00281          */
    00282         ProcessPtr sendSpawnCommand(const PoolOptions &options) {
    00283                 TRACE_POINT();
    00284                 FileDescriptor connection;
    00285                 MessageChannel channel;
    00286
    00287                 try {
    00288                         connection = connect();
    00289                         channel = MessageChannel(connection);
    00290                 } catch (const SystemException &e) {
    00291                         throw SpawnException(string("Could not connect to the spawn server: ") +
    00292                                 e.sys());
    00293                 } catch (const std::exception &e) {
    00294                         throw SpawnException(string("Could not connect to the spawn server: ") +
    00295                                 e.what());
    00296                 }
    00297
    00298                 UPDATE_TRACE_POINT();
    00299                 vector<string> args;
    00300                 string appRoot;
    00301                 pid_t appPid;
    00302                 int i, nServerSockets, ownerPipe;
    00303                 Process::SocketInfoMap serverSockets;
    00304                 string detachKey = Base64::encode(random.generateByteString(32));
    00305                 // The connect password must be a URL-friendly string because users will
    00306                 // insert it in HTTP headers.
    00307                 string connectPassword = Base64::encodeForUrl(random.generateByteString(32));
    00308                 string gupid = random.generateAsciiString(11) + "-" + toString(SystemTime::get());
    00309                 AccountPtr account;
    00310                 function<void ()> destructionCallback;
    00311
    00312                 try {
    00313                         args.push_back("spawn_application");
    00314                         options.toVector(args);
    00315
    00316                         args.push_back("detach_key");
    00317                         args.push_back(detachKey);
    00318                         args.push_back("connect_password");
    00319                         args.push_back(connectPassword);
    00320                         if (accountsDatabase != NULL) {
    00321                                 string username = "_backend-" + toString(accountsDatabase->getUniqueNumber());
    00322                                 string password = random.generateByteString(MESSAGE_SERVER_MAX_PASSWORD_SIZE);
    00323                                 account = accountsDatabase->add(username, password, false, options.rights);
    00324                                 destructionCallback = boost::bind(&SpawnManager::deleteAccount,
    00325                                         accountsDatabase, username);
    00326
    00327                                 args.push_back("pool_account_username");
    00328                                 args.push_back(username);
    00329                                 args.push_back("pool_account_password_base64");
    00330                                 args.push_back(Base64::encode(password));
    00331                         }
    00332
    00333                         channel.write(args);
    00334                 } catch (const SystemException &e) {
    00335                         throw SpawnException(string("Could not write 'spawn_application' "
    00336                                 "command to the spawn server: ") + e.sys());
    00337                 }
    00338
    00339                 try {
    00340                         UPDATE_TRACE_POINT();
    00341                         // Read status.
    00342                         if (!channel.read(args)) {
    00343                                 throw SpawnException("The spawn server has exited unexpectedly.");
    00344                         }
    00345                         if (args.size() != 1) {
    00346                                 throw SpawnException("The spawn server sent an invalid message.");
    00347                         }
    00348                         if (args[0] == "error_page") {
    00349                                 UPDATE_TRACE_POINT();
    00350                                 string errorPage;
    00351
    00352                                 if (!channel.readScalar(errorPage)) {
    00353                                         throw SpawnException("The spawn server has exited unexpectedly.");
    00354                                 }
    00355                                 throw SpawnException("An error occured while spawning the application.",
    00356                                         errorPage);
    00357                         } else if (args[0] != "ok") {
    00358                                 throw SpawnException("The spawn server sent an invalid message.");
    00359                         }
    00360
    00361                         // Read application info.
    00362                         UPDATE_TRACE_POINT();
    00363                         if (!channel.read(args)) {
    00364                                 throw SpawnException("The spawn server has exited unexpectedly.");
    00365                         }
    00366                         if (args.size() != 3) {
    00367                                 throw SpawnException("The spawn server sent an invalid message.");
    00368                         }
    00369
    00370                         appRoot = args[0];
    00371                         appPid  = (pid_t) atoll(args[1].c_str());
    00372                         nServerSockets = atoi(args[2]);
    00373
    00374                         UPDATE_TRACE_POINT();
    00375                         for (i = 0; i < nServerSockets; i++) {
    00376                                 if (!channel.read(args)) {
    00377                                         throw SpawnException("The spawn server has exited unexpectedly.");
    00378                                 }
    00379                                 if (args.size() != 3) {
    00380                                         throw SpawnException("The spawn server sent an invalid message.");
    00381                                 }
    00382                                 serverSockets[args[0]] = Process::SocketInfo(args[1], args[2]);
    00383                         }
    00384                         if (serverSockets.find("main") == serverSockets.end()) {
    00385                                 UPDATE_TRACE_POINT();
    00386                                 throw SpawnException("The spawn server sent an invalid message.");
    00387                         }
    00388                 } catch (const SystemException &e) {
    00389                         throw SpawnException(string("Could not read from the spawn server: ") + e.sys());
    00390                 }
    00391
    00392                 UPDATE_TRACE_POINT();
    00393                 try {
    00394                         ownerPipe = channel.readFileDescriptor();
    00395                 } catch (const SystemException &e) {
    00396                         throw SpawnException(string("Could not receive the spawned "
    00397                                 "application's owner pipe from the spawn server: ") +
    00398                                 e.sys());
    00399                 } catch (const IOException &e) {
    00400                         throw SpawnException(string("Could not receive the spawned "
    00401                                 "application's owner pipe from the spawn server: ") +
    00402                                 e.what());
    00403                 }
    00404
    00405                 UPDATE_TRACE_POINT();
    00406                 return ProcessPtr(new Process(appRoot, appPid, ownerPipe, serverSockets,
    00407                         detachKey, connectPassword, gupid, destructionCallback));
    00408         }
    00409
    00410         /**
    00411          * @throws boost::thread_interrupted
    00412          * @throws Anything thrown by options.environmentVariables->getItems().
    00413          */
    00414         ProcessPtr
    00415         handleSpawnException(const SpawnException &e, const PoolOptions &options) {
    00416                 TRACE_POINT();
    00417                 bool restarted;
    00418                 try {
    00419                         P_DEBUG("Spawn server died. Attempting to restart it...");
    00420                         this_thread::disable_syscall_interruption dsi;
    00421                         restartServer();
    00422                         P_DEBUG("Restart seems to be successful.");
    00423                         restarted = true;
    00424                 } catch (const IOException &e) {
    00425                         P_DEBUG("Restart failed: " << e.what());
    00426                         restarted = false;
    00427                 } catch (const SystemException &e) {
    00428                         P_DEBUG("Restart failed: " << e.what());
    00429                         restarted = false;
    00430                 }
    00431                 if (restarted) {
    00432                         return sendSpawnCommand(options);
    00433                 } else {
    00434                         throw SpawnException("The spawn server died unexpectedly, and restarting it failed.");
    00435                 }
    00436         }
    00437
    00438         /**
    00439          * Send the reload command to the spawn server.
    00440          *
    00441          * @param appRoot The application root to reload.
    00442          * @throws RuntimeException
    00443          * @throws SystemException
    00444          * @throws boost::thread_interrupted
    00445          */
    00446         void sendReloadCommand(const string &appRoot) {
    00447                 TRACE_POINT();
    00448                 FileDescriptor connection;
    00449                 MessageChannel channel;
    00450
    00451                 try {
    00452                         connection = connect();
    00453                         channel = MessageChannel(connection);
    00454                 } catch (SystemException &e) {
    00455                         e.setBriefMessage("Could not connect to the spawn server");
    00456                         throw;
    00457                 } catch (const RuntimeException &e) {
    00458                         throw RuntimeException(string("Could not connect to the spawn server: ") +
    00459                                 e.what());
    00460                 }
    00461
    00462                 try {
    00463                         channel.write("reload", appRoot.c_str(), NULL);
    00464                 } catch (SystemException &e) {
    00465                         e.setBriefMessage("Could not write 'reload' command to the spawn server");
    00466                         throw;
    00467                 }
    00468         }
    00469
    00470         void handleReloadException(const SystemException &e, const string &appRoot) {
    00471                 TRACE_POINT();
    00472                 bool restarted;
    00473                 try {
    00474                         P_DEBUG("Spawn server died. Attempting to restart it...");
    00475                         restartServer();
    00476                         P_DEBUG("Restart seems to be successful.");
    00477                         restarted = true;
    00478                 } catch (const IOException &e) {
    00479                         P_DEBUG("Restart failed: " << e.what());
    00480                         restarted = false;
    00481                 } catch (const SystemException &e) {
    00482                         P_DEBUG("Restart failed: " << e.what());
    00483                         restarted = false;
    00484                 }
    00485                 if (restarted) {
    00486                         return sendReloadCommand(appRoot);
    00487                 } else {
    00488                         throw SpawnException("The spawn server died unexpectedly, and restarting it failed.");
    00489                 }
    00490         }
    00491
    00492         IOException prependMessageToException(const IOException &e, const string &message) {
    00493                 return IOException(message + ": " + e.what());
    00494         }
    00495
    00496         SystemException prependMessageToException(const SystemException &e, const string &message) {
    00497                 return SystemException(message + ": " + e.brief(), e.code());
    00498         }
    00499
    00500 protected:
    00501         /**
    00502          * A method which is called after the spawn server has started.
    00503          * It doesn't do anything by default and serves as a hook for unit tests.
    00504          */
    00505         virtual void spawnServerStarted() { }
    00506
    00507 public:
    00508         /**
    00509          * Construct a new SpawnManager.
    00510          *
    00511          * @param spawnServerCommand The filename of the spawn server to use.
    00512          * @param generation The server instance dir generation in which
    00513          *                   generation-specific are stored.
    00514          * @param accountsDatabase An accounts database. SpawnManager will automatically
    00515          *                         create a new account for each spawned process, assigning
    00516          *                         it the rights as set in the PoolOptions object. This
    00517          *                         account is also automatically deleted when no longer needed.
    00518          *                         May be a null pointer.
    00519          * @param rubyCommand The Ruby interpreter's command.
    00520          * @throws RuntimeException An error occurred while creating a Unix server socket.
    00521          * @throws SystemException An error occured while trying to setup the spawn server.
    00522          * @throws IOException An error occurred while generating random data.
    00523          */
    00524         SpawnManager(const string &spawnServerCommand,
    00525                      const ServerInstanceDir::GenerationPtr &generation,
    00526                      const AccountsDatabasePtr &accountsDatabase = AccountsDatabasePtr(),
    00527                      const string &rubyCommand = "ruby",
    00528                      const AnalyticsLoggerPtr &analyticsLogger = AnalyticsLoggerPtr()
    00529         ) {
    00530                 TRACE_POINT();
    00531                 this->spawnServerCommand = spawnServerCommand;
    00532                 this->generation  = generation;
    00533                 this->accountsDatabase = accountsDatabase;
    00534                 this->rubyCommand = rubyCommand;
    00535                 this->analyticsLogger = analyticsLogger;
    00536                 pid = 0;
    00537                 this_thread::disable_interruption di;
    00538                 this_thread::disable_syscall_interruption dsi;
    00539                 try {
    00540                         restartServer();
    00541                 } catch (const IOException &e) {
    00542                         throw prependMessageToException(e, "Could not start the spawn server");
    00543                 } catch (const SystemException &e) {
    00544                         throw prependMessageToException(e, "Could not start the spawn server");
    00545                 }
    00546         }
    00547
    00548         virtual ~SpawnManager() {
    00549                 TRACE_POINT();
    00550                 if (pid != 0) {
    00551                         UPDATE_TRACE_POINT();
    00552                         this_thread::disable_interruption di;
    00553                         this_thread::disable_syscall_interruption dsi;
    00554                         syscalls::unlink(socketFilename.c_str());
    00555                         ownerSocket.close();
    00556                         syscalls::waitpid(pid, NULL, 0);
    00557                 }
    00558         }
    00559
    00560         virtual ProcessPtr spawn(const PoolOptions &options) {
    00561                 TRACE_POINT();
    00562                 AnalyticsScopeLog scope(options.log, "spawn app process");
    00563                 ProcessPtr result;
    00564                 boost::mutex::scoped_lock l(lock);
    00565
    00566                 try {
    00567                         result = sendSpawnCommand(options);
    00568                 } catch (const SpawnException &e) {
    00569                         if (e.hasErrorPage()) {
    00570                                 throw;
    00571                         } else {
    00572                                 result = handleSpawnException(e, options);
    00573                         }
    00574                 }
    00575                 scope.success();
    00576                 return result;
    00577         }
    00578
    00579         virtual void reload(const string &appRoot) {
    00580                 TRACE_POINT();
    00581                 this_thread::disable_interruption di;
    00582                 this_thread::disable_syscall_interruption dsi;
    00583                 try {
    00584                         return sendReloadCommand(appRoot);
    00585                 } catch (const SystemException &e) {
    00586                         return handleReloadException(e, appRoot);
    00587                 }
    00588         }
    00589
    00590         virtual void killSpawnServer() const {
    00591                 kill(pid, SIGKILL);
    00592         }
    00593
    00594         virtual pid_t getServerPid() const {
    00595                 return pid;
    00596         }
    00597 };
    00598
    00599 /** Convenient alias for SpawnManager smart pointer. */
    00600 typedef shared_ptr<SpawnManager> SpawnManagerPtr;
    00601
    00602 } // namespace Passenger
    00603
    00604 #endif /* _PASSENGER_SPAWN_MANAGER_H_ */