00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #ifndef _PASSENGER_SPAWN_MANAGER_H_
00021 #define _PASSENGER_SPAWN_MANAGER_H_
00022
00023 #include <string>
00024 #include <list>
00025 #include <boost/shared_ptr.hpp>
00026 #include <boost/thread.hpp>
00027 #include <oxt/system_calls.hpp>
00028 #include <oxt/backtrace.hpp>
00029
00030 #include <sys/types.h>
00031 #include <sys/wait.h>
00032 #include <sys/stat.h>
00033 #include <arpa/inet.h>
00034 #include <cstdio>
00035 #include <cstdarg>
00036 #include <unistd.h>
00037 #include <errno.h>
00038 #include <grp.h>
00039 #include <pwd.h>
00040 #include <signal.h>
00041
00042 #include "Application.h"
00043 #include "PoolOptions.h"
00044 #include "MessageChannel.h"
00045 #include "Exceptions.h"
00046 #include "Logging.h"
00047
00048 namespace Passenger {
00049
00050 using namespace std;
00051 using namespace boost;
00052 using namespace oxt;
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087 class SpawnManager {
00088 private:
00089 static const int SPAWN_SERVER_INPUT_FD = 3;
00090
00091 string spawnServerCommand;
00092 string logFile;
00093 string rubyCommand;
00094 string user;
00095
00096 boost::mutex lock;
00097
00098 MessageChannel channel;
00099 pid_t pid;
00100 bool serverNeedsRestart;
00101
00102
00103
00104
00105
00106
00107
00108
00109 void restartServer() {
00110 TRACE_POINT();
00111 if (pid != 0) {
00112 UPDATE_TRACE_POINT();
00113 channel.close();
00114
00115
00116
00117
00118 time_t begin = syscalls::time(NULL);
00119 bool done = false;
00120 while (!done && syscalls::time(NULL) - begin < 5) {
00121 if (syscalls::waitpid(pid, NULL, WNOHANG) > 0) {
00122 done = true;
00123 } else {
00124 syscalls::usleep(100000);
00125 }
00126 }
00127 UPDATE_TRACE_POINT();
00128 if (!done) {
00129 UPDATE_TRACE_POINT();
00130 P_TRACE(2, "Spawn server did not exit in time, killing it...");
00131 syscalls::kill(pid, SIGTERM);
00132 begin = syscalls::time(NULL);
00133 while (syscalls::time(NULL) - begin < 5) {
00134 if (syscalls::waitpid(pid, NULL, WNOHANG) > 0) {
00135 break;
00136 } else {
00137 syscalls::usleep(100000);
00138 }
00139 }
00140 P_TRACE(2, "Spawn server has exited.");
00141 }
00142 pid = 0;
00143 }
00144
00145 int fds[2];
00146 FILE *logFileHandle = NULL;
00147
00148 serverNeedsRestart = true;
00149 if (syscalls::socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == -1) {
00150 throw SystemException("Cannot create a Unix socket", errno);
00151 }
00152 if (!logFile.empty()) {
00153 logFileHandle = syscalls::fopen(logFile.c_str(), "a");
00154 if (logFileHandle == NULL) {
00155 string message("Cannot open log file '");
00156 message.append(logFile);
00157 message.append("' for writing.");
00158 throw IOException(message);
00159 }
00160 }
00161
00162 UPDATE_TRACE_POINT();
00163 pid = syscalls::fork();
00164 if (pid == 0) {
00165 if (!logFile.empty()) {
00166 dup2(fileno(logFileHandle), STDERR_FILENO);
00167 fclose(logFileHandle);
00168 }
00169 dup2(STDERR_FILENO, STDOUT_FILENO);
00170 dup2(fds[1], SPAWN_SERVER_INPUT_FD);
00171
00172
00173 for (long i = sysconf(_SC_OPEN_MAX) - 1; i > SPAWN_SERVER_INPUT_FD; i--) {
00174 close(i);
00175 }
00176
00177 if (!user.empty()) {
00178 struct passwd *entry = getpwnam(user.c_str());
00179 if (entry != NULL) {
00180 if (initgroups(user.c_str(), entry->pw_gid) != 0) {
00181 int e = errno;
00182 fprintf(stderr, "*** Passenger: cannot set supplementary "
00183 "groups for user %s: %s (%d)\n",
00184 user.c_str(),
00185 strerror(e),
00186 e);
00187 }
00188 if (setgid(entry->pw_gid) != 0) {
00189 int e = errno;
00190 fprintf(stderr, "*** Passenger: cannot run spawn "
00191 "manager as group %d: %s (%d)\n",
00192 entry->pw_gid,
00193 strerror(e),
00194 e);
00195 }
00196 if (setuid(entry->pw_uid) != 0) {
00197 int e = errno;
00198 fprintf(stderr, "*** Passenger: cannot run spawn "
00199 "manager as user %s (%d): %s (%d)\n",
00200 user.c_str(), entry->pw_uid,
00201 strerror(e),
00202 e);
00203 }
00204 } else {
00205 fprintf(stderr, "*** Passenger: cannot run spawn manager "
00206 "as nonexistant user '%s'.\n",
00207 user.c_str());
00208 }
00209 fflush(stderr);
00210 }
00211
00212 execlp(rubyCommand.c_str(),
00213 rubyCommand.c_str(),
00214 spawnServerCommand.c_str(),
00215
00216
00217
00218
00219
00220
00221 " ",
00222 (char *) NULL);
00223 int e = errno;
00224 fprintf(stderr, "*** Passenger ERROR (%s:%d):\n"
00225 "Could not start the spawn server: %s: %s (%d)\n",
00226 __FILE__, __LINE__,
00227 rubyCommand.c_str(), strerror(e), e);
00228 fflush(stderr);
00229 _exit(1);
00230 } else if (pid == -1) {
00231 int e = errno;
00232 syscalls::close(fds[0]);
00233 syscalls::close(fds[1]);
00234 if (logFileHandle != NULL) {
00235 syscalls::fclose(logFileHandle);
00236 }
00237 pid = 0;
00238 throw SystemException("Unable to fork a process", e);
00239 } else {
00240 syscalls::close(fds[1]);
00241 if (!logFile.empty()) {
00242 syscalls::fclose(logFileHandle);
00243 }
00244 channel = MessageChannel(fds[0]);
00245 serverNeedsRestart = false;
00246
00247 #ifdef TESTING_SPAWN_MANAGER
00248 if (nextRestartShouldFail) {
00249 syscalls::kill(pid, SIGTERM);
00250 syscalls::usleep(500000);
00251 }
00252 #endif
00253 }
00254 }
00255
00256
00257
00258
00259
00260
00261
00262
00263 ApplicationPtr sendSpawnCommand(const PoolOptions &PoolOptions) {
00264 TRACE_POINT();
00265 vector<string> args;
00266 int ownerPipe;
00267
00268 try {
00269 args.push_back("spawn_application");
00270 PoolOptions.toVector(args);
00271 channel.write(args);
00272 } catch (const SystemException &e) {
00273 throw SpawnException(string("Could not write 'spawn_application' "
00274 "command to the spawn server: ") + e.sys());
00275 }
00276
00277 try {
00278 UPDATE_TRACE_POINT();
00279
00280 if (!channel.read(args)) {
00281 throw SpawnException("The spawn server has exited unexpectedly.");
00282 }
00283 if (args.size() != 1) {
00284 throw SpawnException("The spawn server sent an invalid message.");
00285 }
00286 if (args[0] == "error_page") {
00287 string errorPage;
00288
00289 if (!channel.readScalar(errorPage)) {
00290 throw SpawnException("The spawn server has exited unexpectedly.");
00291 }
00292 throw SpawnException("An error occured while spawning the application.",
00293 errorPage);
00294 } else if (args[0] != "ok") {
00295 throw SpawnException("The spawn server sent an invalid message.");
00296 }
00297
00298
00299 if (!channel.read(args)) {
00300 throw SpawnException("The spawn server has exited unexpectedly.");
00301 }
00302 } catch (const SystemException &e) {
00303 throw SpawnException(string("Could not read from the spawn server: ") + e.sys());
00304 }
00305
00306 UPDATE_TRACE_POINT();
00307 try {
00308 ownerPipe = channel.readFileDescriptor();
00309 } catch (const SystemException &e) {
00310 throw SpawnException(string("Could not receive the spawned "
00311 "application's owner pipe from the spawn server: ") +
00312 e.sys());
00313 } catch (const IOException &e) {
00314 throw SpawnException(string("Could not receive the spawned "
00315 "application's owner pipe from the spawn server: ") +
00316 e.what());
00317 }
00318
00319 if (args.size() != 3) {
00320 UPDATE_TRACE_POINT();
00321 syscalls::close(ownerPipe);
00322 throw SpawnException("The spawn server sent an invalid message.");
00323 }
00324
00325 pid_t pid = atoi(args[0]);
00326
00327 UPDATE_TRACE_POINT();
00328 if (args[2] == "unix") {
00329 int ret;
00330 do {
00331 ret = chmod(args[1].c_str(), S_IRUSR | S_IWUSR);
00332 } while (ret == -1 && errno == EINTR);
00333 do {
00334 ret = chown(args[1].c_str(), getuid(), getgid());
00335 } while (ret == -1 && errno == EINTR);
00336 }
00337 return ApplicationPtr(new Application(PoolOptions.appRoot,
00338 pid, args[1], args[2], ownerPipe));
00339 }
00340
00341
00342
00343
00344 ApplicationPtr
00345 handleSpawnException(const SpawnException &e, const PoolOptions &PoolOptions) {
00346 TRACE_POINT();
00347 bool restarted;
00348 try {
00349 P_DEBUG("Spawn server died. Attempting to restart it...");
00350 this_thread::disable_syscall_interruption dsi;
00351 restartServer();
00352 P_DEBUG("Restart seems to be successful.");
00353 restarted = true;
00354 } catch (const IOException &e) {
00355 P_DEBUG("Restart failed: " << e.what());
00356 restarted = false;
00357 } catch (const SystemException &e) {
00358 P_DEBUG("Restart failed: " << e.what());
00359 restarted = false;
00360 }
00361 if (restarted) {
00362 return sendSpawnCommand(PoolOptions);
00363 } else {
00364 throw SpawnException("The spawn server died unexpectedly, and restarting it failed.");
00365 }
00366 }
00367
00368
00369
00370
00371
00372
00373
00374 void sendReloadCommand(const string &appRoot) {
00375 TRACE_POINT();
00376 try {
00377 channel.write("reload", appRoot.c_str(), NULL);
00378 } catch (const SystemException &e) {
00379 throw SystemException("Could not write 'reload' command "
00380 "to the spawn server", e.code());
00381 }
00382 }
00383
00384 void handleReloadException(const SystemException &e, const string &appRoot) {
00385 TRACE_POINT();
00386 bool restarted;
00387 try {
00388 P_DEBUG("Spawn server died. Attempting to restart it...");
00389 restartServer();
00390 P_DEBUG("Restart seems to be successful.");
00391 restarted = true;
00392 } catch (const IOException &e) {
00393 P_DEBUG("Restart failed: " << e.what());
00394 restarted = false;
00395 } catch (const SystemException &e) {
00396 P_DEBUG("Restart failed: " << e.what());
00397 restarted = false;
00398 }
00399 if (restarted) {
00400 return sendReloadCommand(appRoot);
00401 } else {
00402 throw SpawnException("The spawn server died unexpectedly, and restarting it failed.");
00403 }
00404 }
00405
00406 IOException prependMessageToException(const IOException &e, const string &message) {
00407 return IOException(message + ": " + e.what());
00408 }
00409
00410 SystemException prependMessageToException(const SystemException &e, const string &message) {
00411 return SystemException(message + ": " + e.brief(), e.code());
00412 }
00413
00414 public:
00415 #ifdef TESTING_SPAWN_MANAGER
00416 bool nextRestartShouldFail;
00417 #endif
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438 SpawnManager(const string &spawnServerCommand,
00439 const string &logFile = "",
00440 const string &rubyCommand = "ruby",
00441 const string &user = "") {
00442 TRACE_POINT();
00443 this->spawnServerCommand = spawnServerCommand;
00444 this->logFile = logFile;
00445 this->rubyCommand = rubyCommand;
00446 this->user = user;
00447 pid = 0;
00448 #ifdef TESTING_SPAWN_MANAGER
00449 nextRestartShouldFail = false;
00450 #endif
00451 this_thread::disable_interruption di;
00452 this_thread::disable_syscall_interruption dsi;
00453 try {
00454 restartServer();
00455 } catch (const IOException &e) {
00456 throw prependMessageToException(e, "Could not start the spawn server");
00457 } catch (const SystemException &e) {
00458 throw prependMessageToException(e, "Could not start the spawn server");
00459 }
00460 }
00461
00462 ~SpawnManager() throw() {
00463 TRACE_POINT();
00464 if (pid != 0) {
00465 UPDATE_TRACE_POINT();
00466 this_thread::disable_interruption di;
00467 this_thread::disable_syscall_interruption dsi;
00468 P_TRACE(2, "Shutting down spawn manager (PID " << pid << ").");
00469 channel.close();
00470 syscalls::waitpid(pid, NULL, 0);
00471 P_TRACE(2, "Spawn manager exited.");
00472 }
00473 }
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492 ApplicationPtr spawn(const PoolOptions &PoolOptions) {
00493 TRACE_POINT();
00494 boost::mutex::scoped_lock l(lock);
00495 try {
00496 return sendSpawnCommand(PoolOptions);
00497 } catch (const SpawnException &e) {
00498 if (e.hasErrorPage()) {
00499 throw;
00500 } else {
00501 return handleSpawnException(e, PoolOptions);
00502 }
00503 }
00504 }
00505
00506
00507
00508
00509
00510
00511
00512
00513
00514
00515
00516
00517
00518
00519
00520
00521 void reload(const string &appRoot) {
00522 TRACE_POINT();
00523 this_thread::disable_interruption di;
00524 this_thread::disable_syscall_interruption dsi;
00525 try {
00526 return sendReloadCommand(appRoot);
00527 } catch (const SystemException &e) {
00528 return handleReloadException(e, appRoot);
00529 }
00530 }
00531
00532
00533
00534
00535
00536 pid_t getServerPid() const {
00537 return pid;
00538 }
00539 };
00540
00541
00542 typedef shared_ptr<SpawnManager> SpawnManagerPtr;
00543
00544 }
00545
00546 #endif