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
00330
00331
00332
00333
00334
00335 int ret;
00336 do {
00337 ret = chmod(args[1].c_str(), S_IRUSR | S_IWUSR);
00338 } while (ret == -1 && errno == EINTR);
00339 do {
00340 ret = chown(args[1].c_str(), getuid(), getgid());
00341 } while (ret == -1 && errno == EINTR);
00342 }
00343 return ApplicationPtr(new Application(PoolOptions.appRoot,
00344 pid, args[1], args[2], ownerPipe));
00345 }
00346
00347
00348
00349
00350 ApplicationPtr
00351 handleSpawnException(const SpawnException &e, const PoolOptions &PoolOptions) {
00352 TRACE_POINT();
00353 bool restarted;
00354 try {
00355 P_DEBUG("Spawn server died. Attempting to restart it...");
00356 this_thread::disable_syscall_interruption dsi;
00357 restartServer();
00358 P_DEBUG("Restart seems to be successful.");
00359 restarted = true;
00360 } catch (const IOException &e) {
00361 P_DEBUG("Restart failed: " << e.what());
00362 restarted = false;
00363 } catch (const SystemException &e) {
00364 P_DEBUG("Restart failed: " << e.what());
00365 restarted = false;
00366 }
00367 if (restarted) {
00368 return sendSpawnCommand(PoolOptions);
00369 } else {
00370 throw SpawnException("The spawn server died unexpectedly, and restarting it failed.");
00371 }
00372 }
00373
00374
00375
00376
00377
00378
00379
00380 void sendReloadCommand(const string &appRoot) {
00381 TRACE_POINT();
00382 try {
00383 channel.write("reload", appRoot.c_str(), NULL);
00384 } catch (const SystemException &e) {
00385 throw SystemException("Could not write 'reload' command "
00386 "to the spawn server", e.code());
00387 }
00388 }
00389
00390 void handleReloadException(const SystemException &e, const string &appRoot) {
00391 TRACE_POINT();
00392 bool restarted;
00393 try {
00394 P_DEBUG("Spawn server died. Attempting to restart it...");
00395 restartServer();
00396 P_DEBUG("Restart seems to be successful.");
00397 restarted = true;
00398 } catch (const IOException &e) {
00399 P_DEBUG("Restart failed: " << e.what());
00400 restarted = false;
00401 } catch (const SystemException &e) {
00402 P_DEBUG("Restart failed: " << e.what());
00403 restarted = false;
00404 }
00405 if (restarted) {
00406 return sendReloadCommand(appRoot);
00407 } else {
00408 throw SpawnException("The spawn server died unexpectedly, and restarting it failed.");
00409 }
00410 }
00411
00412 IOException prependMessageToException(const IOException &e, const string &message) {
00413 return IOException(message + ": " + e.what());
00414 }
00415
00416 SystemException prependMessageToException(const SystemException &e, const string &message) {
00417 return SystemException(message + ": " + e.brief(), e.code());
00418 }
00419
00420 public:
00421 #ifdef TESTING_SPAWN_MANAGER
00422 bool nextRestartShouldFail;
00423 #endif
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444 SpawnManager(const string &spawnServerCommand,
00445 const string &logFile = "",
00446 const string &rubyCommand = "ruby",
00447 const string &user = "") {
00448 TRACE_POINT();
00449 this->spawnServerCommand = spawnServerCommand;
00450 this->logFile = logFile;
00451 this->rubyCommand = rubyCommand;
00452 this->user = user;
00453 pid = 0;
00454 #ifdef TESTING_SPAWN_MANAGER
00455 nextRestartShouldFail = false;
00456 #endif
00457 this_thread::disable_interruption di;
00458 this_thread::disable_syscall_interruption dsi;
00459 try {
00460 restartServer();
00461 } catch (const IOException &e) {
00462 throw prependMessageToException(e, "Could not start the spawn server");
00463 } catch (const SystemException &e) {
00464 throw prependMessageToException(e, "Could not start the spawn server");
00465 }
00466 }
00467
00468 ~SpawnManager() throw() {
00469 TRACE_POINT();
00470 if (pid != 0) {
00471 UPDATE_TRACE_POINT();
00472 this_thread::disable_interruption di;
00473 this_thread::disable_syscall_interruption dsi;
00474 P_TRACE(2, "Shutting down spawn manager (PID " << pid << ").");
00475 channel.close();
00476 syscalls::waitpid(pid, NULL, 0);
00477 P_TRACE(2, "Spawn manager exited.");
00478 }
00479 }
00480
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498 ApplicationPtr spawn(const PoolOptions &PoolOptions) {
00499 TRACE_POINT();
00500 boost::mutex::scoped_lock l(lock);
00501 try {
00502 return sendSpawnCommand(PoolOptions);
00503 } catch (const SpawnException &e) {
00504 if (e.hasErrorPage()) {
00505 throw;
00506 } else {
00507 return handleSpawnException(e, PoolOptions);
00508 }
00509 }
00510 }
00511
00512
00513
00514
00515
00516
00517
00518
00519
00520
00521
00522
00523
00524
00525
00526
00527 void reload(const string &appRoot) {
00528 TRACE_POINT();
00529 this_thread::disable_interruption di;
00530 this_thread::disable_syscall_interruption dsi;
00531 try {
00532 return sendReloadCommand(appRoot);
00533 } catch (const SystemException &e) {
00534 return handleReloadException(e, appRoot);
00535 }
00536 }
00537
00538
00539
00540
00541
00542 pid_t getServerPid() const {
00543 return pid;
00544 }
00545 };
00546
00547
00548 typedef shared_ptr<SpawnManager> SpawnManagerPtr;
00549
00550 }
00551
00552 #endif