00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #ifndef _PASSENGER_APPLICATION_POOL_CLIENT_SERVER_H_
00019 #define _PASSENGER_APPLICATION_POOL_CLIENT_SERVER_H_
00020
00021 #include <boost/bind.hpp>
00022 #include <boost/thread/thread.hpp>
00023
00024 #include <set>
00025
00026 #include <sys/types.h>
00027 #include <sys/socket.h>
00028 #include <cstdlib>
00029 #include <errno.h>
00030 #include <unistd.h>
00031
00032 #include "StandardApplicationPool.h"
00033 #include "MessageChannel.h"
00034 #include "Exceptions.h"
00035 #include "Logging.h"
00036
00037 namespace Passenger {
00038
00039 using namespace std;
00040 using namespace boost;
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
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
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121 class ApplicationPoolServer {
00122 private:
00123
00124
00125
00126
00127
00128
00129
00130 struct SharedData {
00131
00132
00133
00134
00135 int server;
00136
00137 ~SharedData() {
00138 close(server);
00139 }
00140 };
00141
00142 typedef shared_ptr<SharedData> SharedDataPtr;
00143
00144
00145
00146
00147 class RemoteSession: public Application::Session {
00148 private:
00149 SharedDataPtr data;
00150 int id;
00151 int reader;
00152 int writer;
00153 pid_t pid;
00154 public:
00155 RemoteSession(SharedDataPtr data, pid_t pid, int id, int reader, int writer) {
00156 this->data = data;
00157 this->pid = pid;
00158 this->id = id;
00159 this->reader = reader;
00160 this->writer = writer;
00161 }
00162
00163 virtual ~RemoteSession() {
00164 closeReader();
00165 closeWriter();
00166 MessageChannel(data->server).write("close", toString(id).c_str(), NULL);
00167 }
00168
00169 virtual int getReader() const {
00170 return reader;
00171 }
00172
00173 virtual void closeReader() {
00174 if (reader != -1) {
00175 close(reader);
00176 reader = -1;
00177 }
00178 }
00179
00180 virtual int getWriter() const {
00181 return writer;
00182 }
00183
00184 virtual void closeWriter() {
00185 if (writer != -1) {
00186 close(writer);
00187 writer = -1;
00188 }
00189 }
00190
00191 virtual pid_t getPid() const {
00192 return pid;
00193 }
00194 };
00195
00196
00197
00198
00199
00200
00201 class Client: public ApplicationPool {
00202 private:
00203 SharedDataPtr data;
00204
00205 public:
00206
00207
00208
00209
00210
00211 Client(int sock) {
00212 data = ptr(new SharedData());
00213 data->server = sock;
00214 }
00215
00216 virtual void clear() {
00217 MessageChannel channel(data->server);
00218 channel.write("clear", NULL);
00219 }
00220
00221 virtual void setMaxIdleTime(unsigned int seconds) {
00222 MessageChannel channel(data->server);
00223 channel.write("setMaxIdleTime", toString(seconds).c_str(), NULL);
00224 }
00225
00226 virtual void setMax(unsigned int max) {
00227 MessageChannel channel(data->server);
00228 channel.write("setMax", toString(max).c_str(), NULL);
00229 }
00230
00231 virtual unsigned int getActive() const {
00232 MessageChannel channel(data->server);
00233 vector<string> args;
00234
00235 channel.write("getActive", NULL);
00236 channel.read(args);
00237 return atoi(args[0].c_str());
00238 }
00239
00240 virtual unsigned int getCount() const {
00241 MessageChannel channel(data->server);
00242 vector<string> args;
00243
00244 channel.write("getCount", NULL);
00245 channel.read(args);
00246 return atoi(args[0].c_str());
00247 }
00248
00249 virtual pid_t getSpawnServerPid() const {
00250 MessageChannel channel(data->server);
00251 vector<string> args;
00252
00253 channel.write("getSpawnServerPid", NULL);
00254 channel.read(args);
00255 return atoi(args[0].c_str());
00256 }
00257
00258 virtual Application::SessionPtr get(const string &appRoot, bool lowerPrivilege = true, const string &lowestUser = "nobody") {
00259 MessageChannel channel(data->server);
00260 vector<string> args;
00261 int reader, writer;
00262
00263 channel.write("get", appRoot.c_str(),
00264 (lowerPrivilege) ? "true" : "false",
00265 lowestUser.c_str(), NULL);
00266 if (!channel.read(args)) {
00267 throw IOException("The ApplicationPool server unexpectedly closed the connection.");
00268 }
00269 if (args[0] == "ok") {
00270 reader = channel.readFileDescriptor();
00271 writer = channel.readFileDescriptor();
00272 return ptr(new RemoteSession(data, atoi(args[1]), atoi(args[2]), reader, writer));
00273 } else if (args[0] == "SpawnException") {
00274 if (args[2] == "true") {
00275 string errorPage;
00276
00277 if (!channel.readScalar(errorPage)) {
00278 throw IOException("The ApplicationPool server unexpectedly closed the connection.");
00279 }
00280 throw SpawnException(args[1], errorPage);
00281 } else {
00282 throw SpawnException(args[1]);
00283 }
00284 } else if (args[0] == "IOException") {
00285 throw IOException(args[1]);
00286 } else {
00287 throw IOException("The ApplicationPool server returned an unknown message.");
00288 }
00289 }
00290 };
00291
00292
00293
00294
00295 struct ClientInfo {
00296
00297 int fd;
00298
00299 thread *thr;
00300 bool detached;
00301
00302 ClientInfo() {
00303 detached = false;
00304 }
00305
00306 void detach() {
00307 detached = true;
00308 close(fd);
00309 fd = -1;
00310 }
00311
00312 ~ClientInfo() {
00313
00314
00315
00316
00317
00318
00319 if (!detached) {
00320 close(fd);
00321 delete thr;
00322 }
00323 }
00324 };
00325
00326 typedef shared_ptr<ClientInfo> ClientInfoPtr;
00327
00328 StandardApplicationPool pool;
00329 int serverSocket;
00330 int connectSocket;
00331 bool done, detached;
00332
00333 mutex lock;
00334 thread *serverThread;
00335 set<ClientInfoPtr> clients;
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356 void serverThreadMainLoop() {
00357 while (!done) {
00358 int fds[2], ret;
00359 char x;
00360
00361
00362
00363 do {
00364 ret = read(serverSocket, &x, 1);
00365 } while (ret == -1 && errno == EINTR);
00366 if (ret == 0) {
00367 break;
00368 }
00369
00370
00371 do {
00372 ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
00373 } while (ret == -1 && errno == EINTR);
00374 if (ret == -1) {
00375 int e = errno;
00376 P_ERROR("Cannot create an anonymous Unix socket: " <<
00377 strerror(e) << " (" << e << ") --- aborting!");
00378 abort();
00379
00380
00381 bool x = false;
00382 if (x) {
00383 printf("%d", e);
00384 }
00385 }
00386
00387 try {
00388 MessageChannel(serverSocket).writeFileDescriptor(fds[1]);
00389 do {
00390 ret = close(fds[1]);
00391 } while (ret == -1 && errno == EINTR);
00392 } catch (SystemException &e) {
00393 P_ERROR("Cannot send a file descriptor: " << e.sys() <<
00394 " --- aborting!");
00395 abort();
00396 } catch (const exception &e) {
00397 P_ERROR("Cannot send a file descriptor: " << e.what() <<
00398 " --- aborting!");
00399 abort();
00400 }
00401
00402 ClientInfoPtr info(new ClientInfo());
00403 info->fd = fds[0];
00404 info->thr = new thread(bind(&ApplicationPoolServer::clientThreadMainLoop, this, info));
00405 mutex::scoped_lock l(lock);
00406 clients.insert(info);
00407 }
00408 }
00409
00410
00411
00412
00413 void clientThreadMainLoop(ClientInfoPtr client) {
00414 MessageChannel channel(client->fd);
00415 vector<string> args;
00416 map<int, Application::SessionPtr> sessions;
00417 int lastID = 0;
00418
00419 try {
00420 while (!done) {
00421 if (!channel.read(args)) {
00422 break;
00423 }
00424
00425 P_TRACE(3, "Client " << this << ": received message: " <<
00426 toString(args));
00427 if (args[0] == "get" && args.size() == 4) {
00428 Application::SessionPtr session;
00429 bool failed = false;
00430 try {
00431 session = pool.get(args[1], args[2] == "true", args[3]);
00432 sessions[lastID] = session;
00433 lastID++;
00434 } catch (const SpawnException &e) {
00435 if (e.hasErrorPage()) {
00436 P_TRACE(3, "Client " << this << ": SpawnException "
00437 "occured (with error page)");
00438 channel.write("SpawnException", e.what(), "true", NULL);
00439 channel.writeScalar(e.getErrorPage());
00440 } else {
00441 P_TRACE(3, "Client " << this << ": SpawnException "
00442 "occured (no error page)");
00443 channel.write("SpawnException", e.what(), "false", NULL);
00444 }
00445 failed = true;
00446 } catch (const IOException &e) {
00447 channel.write("IOException", e.what(), NULL);
00448 failed = true;
00449 }
00450 if (!failed) {
00451 try {
00452 channel.write("ok", toString(session->getPid()).c_str(),
00453 toString(lastID - 1).c_str(), NULL);
00454 channel.writeFileDescriptor(session->getReader());
00455 channel.writeFileDescriptor(session->getWriter());
00456 session->closeReader();
00457 session->closeWriter();
00458 } catch (const exception &) {
00459 P_TRACE(3, "Client " << this << ": something went wrong "
00460 "while sending 'ok' back to the client.");
00461 sessions.erase(lastID - 1);
00462 throw;
00463 }
00464 }
00465
00466 } else if (args[0] == "close" && args.size() == 2) {
00467 sessions.erase(atoi(args[1]));
00468
00469 } else if (args[0] == "clear" && args.size() == 1) {
00470 pool.clear();
00471
00472 } else if (args[0] == "setMaxIdleTime" && args.size() == 2) {
00473 pool.setMaxIdleTime(atoi(args[1]));
00474
00475 } else if (args[0] == "setMax" && args.size() == 2) {
00476 pool.setMax(atoi(args[1]));
00477
00478 } else if (args[0] == "getActive" && args.size() == 1) {
00479 channel.write(toString(pool.getActive()).c_str(), NULL);
00480
00481 } else if (args[0] == "getCount" && args.size() == 1) {
00482 channel.write(toString(pool.getCount()).c_str(), NULL);
00483
00484 } else if (args[0] == "getSpawnServerPid" && args.size() == 1) {
00485 channel.write(toString(pool.getSpawnServerPid()).c_str(), NULL);
00486
00487 } else {
00488 string name;
00489 if (args.empty()) {
00490 name = "(null)";
00491 } else {
00492 name = args[0];
00493 }
00494 P_WARN("An ApplicationPoolServer client sent an invalid command: "
00495 << name << " (" << args.size() << " elements)");
00496 done = true;
00497 }
00498 }
00499 } catch (const exception &e) {
00500 P_WARN("Uncaught exception in ApplicationPoolServer client thread: " <<
00501 e.what());
00502 }
00503
00504 mutex::scoped_lock l(lock);
00505 clients.erase(client);
00506 }
00507
00508 public:
00509
00510
00511
00512
00513
00514
00515
00516
00517
00518
00519
00520
00521
00522
00523
00524
00525
00526
00527
00528
00529
00530
00531
00532
00533
00534 ApplicationPoolServer(const string &spawnServerCommand,
00535 const string &logFile = "",
00536 const string &environment = "production",
00537 const string &rubyCommand = "ruby",
00538 const string &user = "")
00539 : pool(spawnServerCommand, logFile, environment, rubyCommand, user) {
00540 int fds[2];
00541
00542 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == -1) {
00543 throw SystemException("Cannot create a Unix socket pair", errno);
00544 }
00545 serverSocket = fds[0];
00546 connectSocket = fds[1];
00547 done = false;
00548 detached = false;
00549 try {
00550 serverThread = new thread(bind(&ApplicationPoolServer::serverThreadMainLoop, this));
00551 } catch (const thread_resource_error &e) {
00552 throw thread_resource_error("Could not create the ApplicationPoolServer "
00553 "server main loop thread", e.native_error());
00554 }
00555 }
00556
00557 ~ApplicationPoolServer() {
00558 if (!detached) {
00559 done = true;
00560 close(connectSocket);
00561 serverThread->join();
00562 delete serverThread;
00563 close(serverSocket);
00564
00565 set<ClientInfoPtr> clientsCopy;
00566 {
00567 mutex::scoped_lock l(lock);
00568 clientsCopy = clients;
00569 }
00570 set<ClientInfoPtr>::iterator it;
00571 for (it = clientsCopy.begin(); it != clientsCopy.end(); it++) {
00572 (*it)->thr->join();
00573 }
00574 }
00575 }
00576
00577
00578
00579
00580
00581
00582
00583
00584
00585
00586
00587
00588
00589
00590
00591
00592
00593
00594
00595
00596
00597
00598
00599
00600
00601
00602
00603
00604
00605 ApplicationPoolPtr connect() {
00606 MessageChannel channel(connectSocket);
00607 int fd;
00608
00609
00610 channel.writeRaw("x", 1);
00611
00612 fd = channel.readFileDescriptor();
00613 return ptr(new Client(fd));
00614 }
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627 void detach() {
00628 detached = true;
00629 close(connectSocket);
00630 close(serverSocket);
00631 delete serverThread;
00632
00633
00634
00635
00636
00637
00638
00639 set<ClientInfoPtr>::iterator it;
00640 for (it = clients.begin(); it != clients.end(); it++) {
00641 (*it)->detach();
00642 }
00643 clients.clear();
00644
00645 pool.detach();
00646 }
00647 };
00648
00649 typedef shared_ptr<ApplicationPoolServer> ApplicationPoolServerPtr;
00650
00651 }
00652
00653 #endif