00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
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
00059 namespace Passenger {
00060
00061 using namespace std;
00062 using namespace boost;
00063 using namespace oxt;
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092 class SpawnManager: public AbstractSpawnManager {
00093 private:
00094 static const int SERVER_SOCKET_FD = 3;
00095 static const int OWNER_SOCKET_FD = 4;
00096 static const int HIGHEST_FD = OWNER_SOCKET_FD;
00097
00098 string spawnServerCommand;
00099 ServerInstanceDir::GenerationPtr generation;
00100 AccountsDatabasePtr accountsDatabase;
00101 string rubyCommand;
00102 string loggingAgentAddress;
00103 string loggingAgentUsername;
00104 string loggingAgentPassword;
00105
00106 boost::mutex lock;
00107 RandomGenerator random;
00108
00109 pid_t pid;
00110 FileDescriptor ownerSocket;
00111 string socketFilename;
00112 string socketPassword;
00113 bool serverNeedsRestart;
00114
00115 static void deleteAccount(AccountsDatabasePtr accountsDatabase, const string &username) {
00116 accountsDatabase->remove(username);
00117 }
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127 void restartServer() {
00128 TRACE_POINT();
00129 if (pid != 0) {
00130 UPDATE_TRACE_POINT();
00131 ownerSocket.close();
00132
00133
00134
00135
00136
00137 time_t begin = syscalls::time(NULL);
00138 bool done = false;
00139 while (!done && syscalls::time(NULL) - begin < 5) {
00140 if (syscalls::waitpid(pid, NULL, WNOHANG) > 0) {
00141 done = true;
00142 } else {
00143 syscalls::usleep(100000);
00144 }
00145 }
00146 UPDATE_TRACE_POINT();
00147 if (!done) {
00148 UPDATE_TRACE_POINT();
00149 P_TRACE(2, "Spawn server did not exit in time, killing it...");
00150 syscalls::kill(pid, SIGTERM);
00151 begin = syscalls::time(NULL);
00152 while (syscalls::time(NULL) - begin < 5) {
00153 if (syscalls::waitpid(pid, NULL, WNOHANG) > 0) {
00154 break;
00155 } else {
00156 syscalls::usleep(100000);
00157 }
00158 }
00159 }
00160 pid = 0;
00161 }
00162
00163 FileDescriptor serverSocket;
00164 string socketFilename;
00165 string socketPassword;
00166 int ret, fds[2];
00167
00168 UPDATE_TRACE_POINT();
00169 socketFilename = generation->getPath() + "/spawn-server/socket." +
00170 toString(getpid()) + "." +
00171 toString((unsigned long long) this);
00172 socketPassword = Base64::encode(random.generateByteString(32));
00173 serverSocket = createUnixServer(socketFilename.c_str());
00174 do {
00175 ret = chmod(socketFilename.c_str(), S_IRUSR | S_IWUSR | S_IXUSR);
00176 } while (ret == -1 && errno == EINTR);
00177 if (ret == -1) {
00178 int e = errno;
00179 syscalls::unlink(socketFilename.c_str());
00180 throw FileSystemException("Cannot set permissions on '" + socketFilename + "'",
00181 e, socketFilename);
00182 }
00183
00184 if (syscalls::socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == -1) {
00185 int e = errno;
00186 syscalls::unlink(socketFilename.c_str());
00187 throw SystemException("Cannot create a Unix socket", e);
00188 }
00189
00190 UPDATE_TRACE_POINT();
00191 pid = syscalls::fork();
00192 if (pid == 0) {
00193 dup2(serverSocket, HIGHEST_FD + 1);
00194 dup2(fds[1], HIGHEST_FD + 2);
00195 dup2(HIGHEST_FD + 1, SERVER_SOCKET_FD);
00196 dup2(HIGHEST_FD + 2, OWNER_SOCKET_FD);
00197
00198
00199 for (long i = sysconf(_SC_OPEN_MAX) - 1; i > HIGHEST_FD; i--) {
00200 close(i);
00201 }
00202
00203 execlp(rubyCommand.c_str(),
00204 rubyCommand.c_str(),
00205 spawnServerCommand.c_str(),
00206
00207
00208
00209
00210
00211
00212
00213 " ",
00214 (char *) NULL);
00215 int e = errno;
00216 fprintf(stderr, "*** Passenger ERROR (%s:%d):\n"
00217 "Could not start the spawn server: %s: %s (%d)\n",
00218 __FILE__, __LINE__,
00219 rubyCommand.c_str(), strerror(e), e);
00220 fflush(stderr);
00221 _exit(1);
00222 } else if (pid == -1) {
00223 int e = errno;
00224 syscalls::unlink(socketFilename.c_str());
00225 syscalls::close(fds[0]);
00226 syscalls::close(fds[1]);
00227 pid = 0;
00228 throw SystemException("Unable to fork a process", e);
00229 } else {
00230 FileDescriptor ownerSocket = fds[0];
00231 syscalls::close(fds[1]);
00232 serverSocket.close();
00233
00234
00235 MessageChannel ownerSocketChannel(ownerSocket);
00236 ownerSocketChannel.writeRaw(socketFilename + "\n");
00237 ownerSocketChannel.writeRaw(socketPassword + "\n");
00238 ownerSocketChannel.writeRaw(generation->getPath() + "\n");
00239 ownerSocketChannel.writeRaw(loggingAgentAddress + "\n");
00240 ownerSocketChannel.writeRaw(loggingAgentUsername + "\n");
00241 ownerSocketChannel.writeRaw(Base64::encode(loggingAgentPassword) + "\n");
00242
00243 this->ownerSocket = ownerSocket;
00244 this->socketFilename = socketFilename;
00245 this->socketPassword = socketPassword;
00246 spawnServerStarted();
00247 }
00248 }
00249
00250
00251
00252
00253
00254
00255
00256
00257 FileDescriptor connect() const {
00258 TRACE_POINT();
00259 FileDescriptor fd = connectToUnixServer(socketFilename.c_str());
00260 MessageChannel channel(fd);
00261 UPDATE_TRACE_POINT();
00262 channel.writeScalar(socketPassword);
00263 return fd;
00264 }
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275 ProcessPtr sendSpawnCommand(const PoolOptions &options) {
00276 TRACE_POINT();
00277 FileDescriptor connection;
00278 MessageChannel channel;
00279
00280 try {
00281 connection = connect();
00282 channel = MessageChannel(connection);
00283 } catch (const SystemException &e) {
00284 throw SpawnException(string("Could not connect to the spawn server: ") +
00285 e.sys());
00286 } catch (const std::exception &e) {
00287 throw SpawnException(string("Could not connect to the spawn server: ") +
00288 e.what());
00289 }
00290
00291 UPDATE_TRACE_POINT();
00292 vector<string> args;
00293 string appRoot;
00294 pid_t appPid;
00295 int i, nServerSockets, ownerPipe;
00296 Process::SocketInfoMap serverSockets;
00297 string detachKey = Base64::encode(random.generateByteString(32));
00298
00299
00300 string connectPassword = Base64::encodeForUrl(random.generateByteString(32));
00301 AccountPtr account;
00302 function<void ()> destructionCallback;
00303
00304 try {
00305 args.push_back("spawn_application");
00306 options.toVector(args);
00307
00308 args.push_back("detach_key");
00309 args.push_back(detachKey);
00310 args.push_back("connect_password");
00311 args.push_back(connectPassword);
00312 if (accountsDatabase != NULL) {
00313 string username = "_backend-" + toString(accountsDatabase->getUniqueNumber());
00314 string password = random.generateByteString(MESSAGE_SERVER_MAX_PASSWORD_SIZE);
00315 account = accountsDatabase->add(username, password, false, options.rights);
00316 destructionCallback = boost::bind(&SpawnManager::deleteAccount,
00317 accountsDatabase, username);
00318
00319 args.push_back("pool_account_username");
00320 args.push_back(username);
00321 args.push_back("pool_account_password_base64");
00322 args.push_back(Base64::encode(password));
00323 }
00324
00325 channel.write(args);
00326 } catch (const SystemException &e) {
00327 throw SpawnException(string("Could not write 'spawn_application' "
00328 "command to the spawn server: ") + e.sys());
00329 }
00330
00331 try {
00332 UPDATE_TRACE_POINT();
00333
00334 if (!channel.read(args)) {
00335 throw SpawnException("The spawn server has exited unexpectedly.");
00336 }
00337 if (args.size() != 1) {
00338 throw SpawnException("The spawn server sent an invalid message.");
00339 }
00340 if (args[0] == "error_page") {
00341 UPDATE_TRACE_POINT();
00342 string errorPage;
00343
00344 if (!channel.readScalar(errorPage)) {
00345 throw SpawnException("The spawn server has exited unexpectedly.");
00346 }
00347 throw SpawnException("An error occured while spawning the application.",
00348 errorPage);
00349 } else if (args[0] != "ok") {
00350 throw SpawnException("The spawn server sent an invalid message.");
00351 }
00352
00353
00354 UPDATE_TRACE_POINT();
00355 if (!channel.read(args)) {
00356 throw SpawnException("The spawn server has exited unexpectedly.");
00357 }
00358 if (args.size() != 3) {
00359 throw SpawnException("The spawn server sent an invalid message.");
00360 }
00361
00362 appRoot = args[0];
00363 appPid = (pid_t) atoll(args[1].c_str());
00364 nServerSockets = atoi(args[2]);
00365
00366 UPDATE_TRACE_POINT();
00367 for (i = 0; i < nServerSockets; i++) {
00368 if (!channel.read(args)) {
00369 throw SpawnException("The spawn server has exited unexpectedly.");
00370 }
00371 if (args.size() != 3) {
00372 throw SpawnException("The spawn server sent an invalid message.");
00373 }
00374 serverSockets[args[0]] = Process::SocketInfo(args[1], args[2]);
00375 }
00376 if (serverSockets.find("main") == serverSockets.end()) {
00377 UPDATE_TRACE_POINT();
00378 throw SpawnException("The spawn server sent an invalid message.");
00379 }
00380 } catch (const SystemException &e) {
00381 throw SpawnException(string("Could not read from the spawn server: ") + e.sys());
00382 }
00383
00384 UPDATE_TRACE_POINT();
00385 try {
00386 ownerPipe = channel.readFileDescriptor();
00387 } catch (const SystemException &e) {
00388 throw SpawnException(string("Could not receive the spawned "
00389 "application's owner pipe from the spawn server: ") +
00390 e.sys());
00391 } catch (const IOException &e) {
00392 throw SpawnException(string("Could not receive the spawned "
00393 "application's owner pipe from the spawn server: ") +
00394 e.what());
00395 }
00396
00397 UPDATE_TRACE_POINT();
00398 return ProcessPtr(new Process(appRoot, appPid, ownerPipe, serverSockets,
00399 detachKey, connectPassword, destructionCallback));
00400 }
00401
00402
00403
00404
00405
00406 ProcessPtr
00407 handleSpawnException(const SpawnException &e, const PoolOptions &options) {
00408 TRACE_POINT();
00409 bool restarted;
00410 try {
00411 P_DEBUG("Spawn server died. Attempting to restart it...");
00412 this_thread::disable_syscall_interruption dsi;
00413 restartServer();
00414 P_DEBUG("Restart seems to be successful.");
00415 restarted = true;
00416 } catch (const IOException &e) {
00417 P_DEBUG("Restart failed: " << e.what());
00418 restarted = false;
00419 } catch (const SystemException &e) {
00420 P_DEBUG("Restart failed: " << e.what());
00421 restarted = false;
00422 }
00423 if (restarted) {
00424 return sendSpawnCommand(options);
00425 } else {
00426 throw SpawnException("The spawn server died unexpectedly, and restarting it failed.");
00427 }
00428 }
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438 void sendReloadCommand(const string &appRoot) {
00439 TRACE_POINT();
00440 FileDescriptor connection;
00441 MessageChannel channel;
00442
00443 try {
00444 connection = connect();
00445 channel = MessageChannel(connection);
00446 } catch (SystemException &e) {
00447 e.setBriefMessage("Could not connect to the spawn server");
00448 throw;
00449 } catch (const RuntimeException &e) {
00450 throw RuntimeException(string("Could not connect to the spawn server: ") +
00451 e.what());
00452 }
00453
00454 try {
00455 channel.write("reload", appRoot.c_str(), NULL);
00456 } catch (SystemException &e) {
00457 e.setBriefMessage("Could not write 'reload' command to the spawn server");
00458 throw;
00459 }
00460 }
00461
00462 void handleReloadException(const SystemException &e, const string &appRoot) {
00463 TRACE_POINT();
00464 bool restarted;
00465 try {
00466 P_DEBUG("Spawn server died. Attempting to restart it...");
00467 restartServer();
00468 P_DEBUG("Restart seems to be successful.");
00469 restarted = true;
00470 } catch (const IOException &e) {
00471 P_DEBUG("Restart failed: " << e.what());
00472 restarted = false;
00473 } catch (const SystemException &e) {
00474 P_DEBUG("Restart failed: " << e.what());
00475 restarted = false;
00476 }
00477 if (restarted) {
00478 return sendReloadCommand(appRoot);
00479 } else {
00480 throw SpawnException("The spawn server died unexpectedly, and restarting it failed.");
00481 }
00482 }
00483
00484 IOException prependMessageToException(const IOException &e, const string &message) {
00485 return IOException(message + ": " + e.what());
00486 }
00487
00488 SystemException prependMessageToException(const SystemException &e, const string &message) {
00489 return SystemException(message + ": " + e.brief(), e.code());
00490 }
00491
00492 protected:
00493
00494
00495
00496
00497 virtual void spawnServerStarted() { }
00498
00499 public:
00500
00501
00502
00503
00504
00505
00506
00507
00508
00509
00510
00511
00512
00513
00514
00515
00516 SpawnManager(const string &spawnServerCommand,
00517 const ServerInstanceDir::GenerationPtr &generation,
00518 const AccountsDatabasePtr &accountsDatabase = AccountsDatabasePtr(),
00519 const string &rubyCommand = "ruby",
00520 const string &loggingAgentAddress = "",
00521 const string &loggingAgentUsername = "",
00522 const string &loggingAgentPassword = ""
00523 ) {
00524 TRACE_POINT();
00525 this->spawnServerCommand = spawnServerCommand;
00526 this->generation = generation;
00527 this->accountsDatabase = accountsDatabase;
00528 this->rubyCommand = rubyCommand;
00529 this->loggingAgentAddress = loggingAgentAddress;
00530 this->loggingAgentUsername = loggingAgentUsername;
00531 this->loggingAgentPassword = loggingAgentPassword;
00532 pid = 0;
00533 this_thread::disable_interruption di;
00534 this_thread::disable_syscall_interruption dsi;
00535 try {
00536 restartServer();
00537 } catch (const IOException &e) {
00538 throw prependMessageToException(e, "Could not start the spawn server");
00539 } catch (const SystemException &e) {
00540 throw prependMessageToException(e, "Could not start the spawn server");
00541 }
00542 }
00543
00544 virtual ~SpawnManager() {
00545 TRACE_POINT();
00546 if (pid != 0) {
00547 UPDATE_TRACE_POINT();
00548 this_thread::disable_interruption di;
00549 this_thread::disable_syscall_interruption dsi;
00550 syscalls::unlink(socketFilename.c_str());
00551 ownerSocket.close();
00552 syscalls::waitpid(pid, NULL, 0);
00553 }
00554 }
00555
00556 virtual ProcessPtr spawn(const PoolOptions &options) {
00557 TRACE_POINT();
00558 boost::mutex::scoped_lock l(lock);
00559 try {
00560 return sendSpawnCommand(options);
00561 } catch (const SpawnException &e) {
00562 if (e.hasErrorPage()) {
00563 throw;
00564 } else {
00565 return handleSpawnException(e, options);
00566 }
00567 }
00568 }
00569
00570 virtual void reload(const string &appRoot) {
00571 TRACE_POINT();
00572 this_thread::disable_interruption di;
00573 this_thread::disable_syscall_interruption dsi;
00574 try {
00575 return sendReloadCommand(appRoot);
00576 } catch (const SystemException &e) {
00577 return handleReloadException(e, appRoot);
00578 }
00579 }
00580
00581 virtual void killSpawnServer() const {
00582 kill(pid, SIGKILL);
00583 }
00584
00585 virtual pid_t getServerPid() const {
00586 return pid;
00587 }
00588 };
00589
00590
00591 typedef shared_ptr<SpawnManager> SpawnManagerPtr;
00592
00593 }
00594
00595 #endif