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         int logLevel;
    00105         string debugLogFile;
    00106
    00107         boost::mutex lock;
    00108         RandomGenerator random;
    00109
    00110         pid_t pid;
    00111         FileDescriptor ownerSocket;
    00112         string socketFilename;
    00113         string socketPassword;
    00114         bool serverNeedsRestart;
    00115
    00116         static void deleteAccount(AccountsDatabasePtr accountsDatabase, const string &username) {
    00117                 accountsDatabase->remove(username);
    00118         }
    00119
    00120         /**
    00121          * Restarts the spawn server.
    00122          *
    00123          * @pre System call interruption is disabled.
    00124          * @throws RuntimeException An error occurred while creating a Unix server socket.
    00125          * @throws SystemException An error occured while trying to setup the spawn server.
    00126          * @throws IOException An error occurred while generating random data.
    00127          */
    00128         void restartServer() {
    00129                 TRACE_POINT();
    00130                 if (pid != 0) {
    00131                         UPDATE_TRACE_POINT();
    00132                         ownerSocket.close();
    00133
    00134                         /* Wait at most 5 seconds for the spawn server to exit.
    00135                          * If that doesn't work, kill it, then wait at most 5 seconds
    00136                          * for it to exit.
    00137                          */
    00138                         time_t begin = syscalls::time(NULL);
    00139                         bool done = false;
    00140                         while (!done && syscalls::time(NULL) - begin < 5) {
    00141                                 if (syscalls::waitpid(pid, NULL, WNOHANG) > 0) {
    00142                                         done = true;
    00143                                 } else {
    00144                                         syscalls::usleep(100000);
    00145                                 }
    00146                         }
    00147                         UPDATE_TRACE_POINT();
    00148                         if (!done) {
    00149                                 UPDATE_TRACE_POINT();
    00150                                 P_TRACE(2, "Spawn server did not exit in time, killing it...");
    00151                                 syscalls::kill(pid, SIGTERM);
    00152                                 begin = syscalls::time(NULL);
    00153                                 while (syscalls::time(NULL) - begin < 5) {
    00154                                         if (syscalls::waitpid(pid, NULL, WNOHANG) > 0) {
    00155                                                 break;
    00156                                         } else {
    00157                                                 syscalls::usleep(100000);
    00158                                         }
    00159                                 }
    00160                         }
    00161                         pid = 0;
    00162                 }
    00163
    00164                 FileDescriptor serverSocket;
    00165                 string socketFilename;
    00166                 string socketPassword;
    00167                 int ret, fds[2];
    00168
    00169                 UPDATE_TRACE_POINT();
    00170                 socketFilename = generation->getPath() + "/spawn-server/socket." +
    00171                         toString(getpid()) + "." +
    00172                         pointerToIntString(this);
    00173                 socketPassword = Base64::encode(random.generateByteString(32));
    00174                 serverSocket = createUnixServer(socketFilename.c_str());
    00175                 do {
    00176                         ret = chmod(socketFilename.c_str(), S_IRUSR | S_IWUSR | S_IXUSR);
    00177                 } while (ret == -1 && errno == EINTR);
    00178                 if (ret == -1) {
    00179                         int e = errno;
    00180                         syscalls::unlink(socketFilename.c_str());
    00181                         throw FileSystemException("Cannot set permissions on '" + socketFilename + "'",
    00182                                 e, socketFilename);
    00183                 }
    00184
    00185                 if (syscalls::socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == -1) {
    00186                         int e = errno;
    00187                         syscalls::unlink(socketFilename.c_str());
    00188                         throw SystemException("Cannot create a Unix socket", e);
    00189                 }
    00190
    00191                 UPDATE_TRACE_POINT();
    00192                 pid = syscalls::fork();
    00193                 if (pid == 0) {
    00194                         dup2(serverSocket, HIGHEST_FD + 1);
    00195                         dup2(fds[1], HIGHEST_FD + 2);
    00196                         dup2(HIGHEST_FD + 1, SERVER_SOCKET_FD);
    00197                         dup2(HIGHEST_FD + 2, OWNER_SOCKET_FD);
    00198
    00199                         // Close all unnecessary file descriptors
    00200                         for (long i = sysconf(_SC_OPEN_MAX) - 1; i > HIGHEST_FD; i--) {
    00201                                 close(i);
    00202                         }
    00203
    00204                         execlp(rubyCommand.c_str(),
    00205                                 rubyCommand.c_str(),
    00206                                 spawnServerCommand.c_str(),
    00207                                 /* The spawn server changes the process names of the subservers
    00208                                  * that it starts, for better usability. However, the process name length
    00209                                  * (as shown by ps) is limited. Here, we try to expand that limit by
    00210                                  * deliberately passing a useless whitespace string to the spawn server.
    00211                                  * This argument is ignored by the spawn server. This works on some
    00212                                  * systems, such as Ubuntu Linux.
    00213                                  */
    00214                                 "                                                             ",
    00215                                 (char *) NULL);
    00216                         int e = errno;
    00217                         fprintf(stderr, "*** Passenger ERROR (%s:%d):\n"
    00218                                 "Could not start the spawn server: %s: %s (%d)\n",
    00219                                 __FILE__, __LINE__,
    00220                                 rubyCommand.c_str(), strerror(e), e);
    00221                         fflush(stderr);
    00222                         _exit(1);
    00223                 } else if (pid == -1) {
    00224                         int e = errno;
    00225                         syscalls::unlink(socketFilename.c_str());
    00226                         syscalls::close(fds[0]);
    00227                         syscalls::close(fds[1]);
    00228                         pid = 0;
    00229                         throw SystemException("Unable to fork a process", e);
    00230                 } else {
    00231                         FileDescriptor ownerSocket = fds[0];
    00232                         syscalls::close(fds[1]);
    00233                         serverSocket.close();
    00234
    00235                         // Pass arguments to spawn server.
    00236                         MessageChannel ownerSocketChannel(ownerSocket);
    00237                         ownerSocketChannel.writeRaw(socketFilename + "\n");
    00238                         ownerSocketChannel.writeRaw(socketPassword + "\n");
    00239                         ownerSocketChannel.writeRaw(generation->getPath() + "\n");
    00240                         if (analyticsLogger != NULL) {
    00241                                 ownerSocketChannel.writeRaw(analyticsLogger->getAddress() + "\n");
    00242                                 ownerSocketChannel.writeRaw(analyticsLogger->getUsername() + "\n");
    00243                                 ownerSocketChannel.writeRaw(Base64::encode(analyticsLogger->getPassword()) + "\n");
    00244                                 ownerSocketChannel.writeRaw(analyticsLogger->getNodeName() + "\n");
    00245                         } else {
    00246                                 ownerSocketChannel.writeRaw("\n");
    00247                                 ownerSocketChannel.writeRaw("\n");
    00248                                 ownerSocketChannel.writeRaw("\n");
    00249                                 ownerSocketChannel.writeRaw("\n");
    00250                         }
    00251                         ownerSocketChannel.writeRaw(toString(logLevel) + "\n");
    00252                         ownerSocketChannel.writeRaw(debugLogFile + "\n");
    00253
    00254                         this->ownerSocket    = ownerSocket;
    00255                         this->socketFilename = socketFilename;
    00256                         this->socketPassword = socketPassword;
    00257                         spawnServerStarted();
    00258                 }
    00259         }
    00260
    00261         /**
    00262          * Connects to the spawn server and returns the connection.
    00263          *
    00264          * @throws RuntimeException
    00265          * @throws SystemException
    00266          * @throws boost::thread_interrupted
    00267          */
    00268         FileDescriptor connect() const {
    00269                 TRACE_POINT();
    00270                 FileDescriptor fd = connectToUnixServer(socketFilename.c_str());
    00271                 MessageChannel channel(fd);
    00272                 UPDATE_TRACE_POINT();
    00273                 channel.writeScalar(socketPassword);
    00274                 return fd;
    00275         }
    00276
    00277         /**
    00278          * Send the spawn command to the spawn server.
    00279          *
    00280          * @param PoolOptions The spawn options to use.
    00281          * @return A Process smart pointer, representing the spawned process.
    00282          * @throws SpawnException Something went wrong.
    00283          * @throws Anything thrown by options.environmentVariables->getItems().
    00284          * @throws boost::thread_interrupted
    00285          */
    00286         ProcessPtr sendSpawnCommand(const PoolOptions &options) {
    00287                 TRACE_POINT();
    00288                 FileDescriptor connection;
    00289                 MessageChannel channel;
    00290
    00291                 P_DEBUG("Spawning a new application process for " << options.appRoot << "...");
    00292
    00293                 try {
    00294                         connection = connect();
    00295                         channel = MessageChannel(connection);
    00296                 } catch (const SystemException &e) {
    00297                         throw SpawnException(string("Could not connect to the spawn server: ") +
    00298                                 e.sys());
    00299                 } catch (const std::exception &e) {
    00300                         throw SpawnException(string("Could not connect to the spawn server: ") +
    00301                                 e.what());
    00302                 }
    00303
    00304                 UPDATE_TRACE_POINT();
    00305                 vector<string> args;
    00306                 string appRoot;
    00307                 pid_t appPid;
    00308                 int i, nServerSockets, ownerPipe;
    00309                 Process::SocketInfoMap serverSockets;
    00310                 string detachKey = random.generateAsciiString(43);
    00311                 // The connect password must be a URL-friendly string because users will
    00312                 // insert it in HTTP headers.
    00313                 string connectPassword = random.generateAsciiString(43);
    00314                 string gupid = integerToHex(SystemTime::get() / 60) + "-" +
    00315                         random.generateAsciiString(11);
    00316                 AccountPtr account;
    00317                 function<void ()> destructionCallback;
    00318
    00319                 try {
    00320                         args.push_back("spawn_application");
    00321                         options.toVector(args);
    00322
    00323                         args.push_back("detach_key");
    00324                         args.push_back(detachKey);
    00325                         args.push_back("connect_password");
    00326                         args.push_back(connectPassword);
    00327                         if (accountsDatabase != NULL) {
    00328                                 string username = "_backend-" + toString(accountsDatabase->getUniqueNumber());
    00329                                 string password = random.generateByteString(MESSAGE_SERVER_MAX_PASSWORD_SIZE);
    00330                                 account = accountsDatabase->add(username, password, false, options.rights);
    00331                                 destructionCallback = boost::bind(&SpawnManager::deleteAccount,
    00332                                         accountsDatabase, username);
    00333
    00334                                 args.push_back("pool_account_username");
    00335                                 args.push_back(username);
    00336                                 args.push_back("pool_account_password_base64");
    00337                                 args.push_back(Base64::encode(password));
    00338                         }
    00339
    00340                         channel.write(args);
    00341                 } catch (const SystemException &e) {
    00342                         throw SpawnException(string("Could not write 'spawn_application' "
    00343                                 "command to the spawn server: ") + e.sys());
    00344                 }
    00345
    00346                 try {
    00347                         UPDATE_TRACE_POINT();
    00348                         // Read status.
    00349                         if (!channel.read(args)) {
    00350                                 throw SpawnException("The spawn server has exited unexpectedly.");
    00351                         }
    00352                         if (args.size() != 1) {
    00353                                 throw SpawnException("The spawn server sent an invalid message.");
    00354                         }
    00355                         if (args[0] == "error_page") {
    00356                                 UPDATE_TRACE_POINT();
    00357                                 string errorPage;
    00358
    00359                                 if (!channel.readScalar(errorPage)) {
    00360                                         throw SpawnException("The spawn server has exited unexpectedly.");
    00361                                 }
    00362                                 throw SpawnException("An error occured while spawning the application.",
    00363                                         errorPage);
    00364                         } else if (args[0] != "ok") {
    00365                                 throw SpawnException("The spawn server sent an invalid message.");
    00366                         }
    00367
    00368                         // Read application info.
    00369                         UPDATE_TRACE_POINT();
    00370                         if (!channel.read(args)) {
    00371                                 throw SpawnException("The spawn server has exited unexpectedly.");
    00372                         }
    00373                         if (args.size() != 3) {
    00374                                 throw SpawnException("The spawn server sent an invalid message.");
    00375                         }
    00376
    00377                         appRoot = args[0];
    00378                         appPid  = (pid_t) stringToULL(args[1]);
    00379                         nServerSockets = atoi(args[2]);
    00380
    00381                         UPDATE_TRACE_POINT();
    00382                         for (i = 0; i < nServerSockets; i++) {
    00383                                 if (!channel.read(args)) {
    00384                                         throw SpawnException("The spawn server has exited unexpectedly.");
    00385                                 }
    00386                                 if (args.size() != 3) {
    00387                                         throw SpawnException("The spawn server sent an invalid message.");
    00388                                 }
    00389                                 serverSockets[args[0]] = Process::SocketInfo(args[1], args[2]);
    00390                         }
    00391                         if (serverSockets.find("main") == serverSockets.end()) {
    00392                                 UPDATE_TRACE_POINT();
    00393                                 throw SpawnException("The spawn server sent an invalid message.");
    00394                         }
    00395                 } catch (const SystemException &e) {
    00396                         throw SpawnException(string("Could not read from the spawn server: ") + e.sys());
    00397                 }
    00398
    00399                 UPDATE_TRACE_POINT();
    00400                 try {
    00401                         ownerPipe = channel.readFileDescriptor();
    00402                 } catch (const SystemException &e) {
    00403                         throw SpawnException(string("Could not receive the spawned "
    00404                                 "application's owner pipe from the spawn server: ") +
    00405                                 e.sys());
    00406                 } catch (const IOException &e) {
    00407                         throw SpawnException(string("Could not receive the spawned "
    00408                                 "application's owner pipe from the spawn server: ") +
    00409                                 e.what());
    00410                 }
    00411
    00412                 UPDATE_TRACE_POINT();
    00413                 P_DEBUG("Application process " << appPid << " spawned");
    00414                 return ProcessPtr(new Process(appRoot, appPid, ownerPipe, serverSockets,
    00415                         detachKey, connectPassword, gupid, destructionCallback));
    00416         }
    00417
    00418         /**
    00419          * @throws boost::thread_interrupted
    00420          * @throws Anything thrown by options.environmentVariables->getItems().
    00421          */
    00422         ProcessPtr
    00423         handleSpawnException(const SpawnException &e, const PoolOptions &options) {
    00424                 TRACE_POINT();
    00425                 bool restarted;
    00426                 try {
    00427                         P_DEBUG("Spawn server died. Attempting to restart it...");
    00428                         this_thread::disable_syscall_interruption dsi;
    00429                         restartServer();
    00430                         P_DEBUG("Restart seems to be successful.");
    00431                         restarted = true;
    00432                 } catch (const IOException &e) {
    00433                         P_DEBUG("Restart failed: " << e.what());
    00434                         restarted = false;
    00435                 } catch (const SystemException &e) {
    00436                         P_DEBUG("Restart failed: " << e.what());
    00437                         restarted = false;
    00438                 }
    00439                 if (restarted) {
    00440                         return sendSpawnCommand(options);
    00441                 } else {
    00442                         throw SpawnException("The spawn server died unexpectedly, and restarting it failed.");
    00443                 }
    00444         }
    00445
    00446         /**
    00447          * Send the reload command to the spawn server.
    00448          *
    00449          * @param appRoot The application root to reload.
    00450          * @throws RuntimeException
    00451          * @throws SystemException
    00452          * @throws boost::thread_interrupted
    00453          */
    00454         void sendReloadCommand(const string &appRoot) {
    00455                 TRACE_POINT();
    00456                 FileDescriptor connection;
    00457                 MessageChannel channel;
    00458
    00459                 try {
    00460                         connection = connect();
    00461                         channel = MessageChannel(connection);
    00462                 } catch (SystemException &e) {
    00463                         e.setBriefMessage("Could not connect to the spawn server");
    00464                         throw;
    00465                 } catch (const RuntimeException &e) {
    00466                         throw RuntimeException(string("Could not connect to the spawn server: ") +
    00467                                 e.what());
    00468                 }
    00469
    00470                 try {
    00471                         channel.write("reload", appRoot.c_str(), NULL);
    00472                 } catch (SystemException &e) {
    00473                         e.setBriefMessage("Could not write 'reload' command to the spawn server");
    00474                         throw;
    00475                 }
    00476         }
    00477
    00478         void handleReloadException(const SystemException &e, const string &appRoot) {
    00479                 TRACE_POINT();
    00480                 bool restarted;
    00481                 try {
    00482                         P_DEBUG("Spawn server died. Attempting to restart it...");
    00483                         restartServer();
    00484                         P_DEBUG("Restart seems to be successful.");
    00485                         restarted = true;
    00486                 } catch (const IOException &e) {
    00487                         P_DEBUG("Restart failed: " << e.what());
    00488                         restarted = false;
    00489                 } catch (const SystemException &e) {
    00490                         P_DEBUG("Restart failed: " << e.what());
    00491                         restarted = false;
    00492                 }
    00493                 if (restarted) {
    00494                         return sendReloadCommand(appRoot);
    00495                 } else {
    00496                         throw SpawnException("The spawn server died unexpectedly, and restarting it failed.");
    00497                 }
    00498         }
    00499
    00500         IOException prependMessageToException(const IOException &e, const string &message) {
    00501                 return IOException(message + ": " + e.what());
    00502         }
    00503
    00504         SystemException prependMessageToException(const SystemException &e, const string &message) {
    00505                 return SystemException(message + ": " + e.brief(), e.code());
    00506         }
    00507
    00508 protected:
    00509         /**
    00510          * A method which is called after the spawn server has started.
    00511          * It doesn't do anything by default and serves as a hook for unit tests.
    00512          */
    00513         virtual void spawnServerStarted() { }
    00514
    00515 public:
    00516         /**
    00517          * Construct a new SpawnManager.
    00518          *
    00519          * @param spawnServerCommand The filename of the spawn server to use.
    00520          * @param generation The server instance dir generation in which
    00521          *                   generation-specific are stored.
    00522          * @param accountsDatabase An accounts database. SpawnManager will automatically
    00523          *                         create a new account for each spawned process, assigning
    00524          *                         it the rights as set in the PoolOptions object. This
    00525          *                         account is also automatically deleted when no longer needed.
    00526          *                         May be a null pointer.
    00527          * @param rubyCommand The Ruby interpreter's command.
    00528          * @throws RuntimeException An error occurred while creating a Unix server socket.
    00529          * @throws SystemException An error occured while trying to setup the spawn server.
    00530          * @throws IOException An error occurred while generating random data.
    00531          */
    00532         SpawnManager(const string &spawnServerCommand,
    00533                      const ServerInstanceDir::GenerationPtr &generation,
    00534                      const AccountsDatabasePtr &accountsDatabase = AccountsDatabasePtr(),
    00535                      const string &rubyCommand = "ruby",
    00536                      const AnalyticsLoggerPtr &analyticsLogger = AnalyticsLoggerPtr(),
    00537                      int logLevel = 0,
    00538                      const string &debugLogFile = ""
    00539         ) {
    00540                 TRACE_POINT();
    00541                 this->spawnServerCommand = spawnServerCommand;
    00542                 this->generation  = generation;
    00543                 this->accountsDatabase = accountsDatabase;
    00544                 this->rubyCommand = rubyCommand;
    00545                 this->analyticsLogger = analyticsLogger;
    00546                 this->logLevel = logLevel;
    00547                 this->debugLogFile = debugLogFile;
    00548                 pid = 0;
    00549                 this_thread::disable_interruption di;
    00550                 this_thread::disable_syscall_interruption dsi;
    00551                 try {
    00552                         restartServer();
    00553                 } catch (const IOException &e) {
    00554                         throw prependMessageToException(e, "Could not start the spawn server");
    00555                 } catch (const SystemException &e) {
    00556                         throw prependMessageToException(e, "Could not start the spawn server");
    00557                 }
    00558         }
    00559
    00560         virtual ~SpawnManager() {
    00561                 TRACE_POINT();
    00562                 if (pid != 0) {
    00563                         UPDATE_TRACE_POINT();
    00564                         this_thread::disable_interruption di;
    00565                         this_thread::disable_syscall_interruption dsi;
    00566                         syscalls::unlink(socketFilename.c_str());
    00567                         ownerSocket.close();
    00568                         syscalls::waitpid(pid, NULL, 0);
    00569                 }
    00570         }
    00571
    00572         virtual ProcessPtr spawn(const PoolOptions &options) {
    00573                 TRACE_POINT();
    00574                 AnalyticsScopeLog scope(options.log, "spawn app process");
    00575                 ProcessPtr result;
    00576                 boost::mutex::scoped_lock l(lock);
    00577
    00578                 try {
    00579                         result = sendSpawnCommand(options);
    00580                 } catch (const SpawnException &e) {
    00581                         if (e.hasErrorPage()) {
    00582                                 throw;
    00583                         } else {
    00584                                 result = handleSpawnException(e, options);
    00585                         }
    00586                 }
    00587                 scope.success();
    00588                 return result;
    00589         }
    00590
    00591         virtual void reload(const string &appRoot) {
    00592                 TRACE_POINT();
    00593                 this_thread::disable_interruption di;
    00594                 this_thread::disable_syscall_interruption dsi;
    00595                 try {
    00596                         return sendReloadCommand(appRoot);
    00597                 } catch (const SystemException &e) {
    00598                         return handleReloadException(e, appRoot);
    00599                 }
    00600         }
    00601
    00602         virtual void killSpawnServer() const {
    00603                 kill(pid, SIGKILL);
    00604         }
    00605
    00606         virtual pid_t getServerPid() const {
    00607                 return pid;
    00608         }
    00609 };
    00610
    00611 /** Convenient alias for SpawnManager smart pointer. */
    00612 typedef shared_ptr<SpawnManager> SpawnManagerPtr;
    00613
    00614 } // namespace Passenger
    00615
    00616 #endif /* _PASSENGER_SPAWN_MANAGER_H_ */