0
+#ifndef _PASSENGER_APPLICATION_POOL_CLIENT_SERVER_H_
0
+#define _PASSENGER_APPLICATION_POOL_CLIENT_SERVER_H_
0
+#include <boost/thread/thread.hpp>
0
+#include <apr_portable.h>
0
+#include <sys/socket.h>
0
+#include "ApplicationPool.h"
0
+#include "Exceptions.h"
0
+class ApplicationPoolServer {
0
+ * The only thing this struct does is making sure that the
0
+ * ApplicationPoolServer's thread function runs in the context
0
+ * of ApplicationPoolServer. Look at threadMain() instead for
0
+ * the interesting stuff.
0
+ struct ServerThreadDelegator {
0
+ ApplicationPoolServer *self;
0
+ ServerThreadDelegator(ApplicationPoolServer *self) {
0
+ class ApplicationPoolClient: public ApplicationPool {
0
+ ApplicationPoolClient(int sock) {
0
+ ~ApplicationPoolClient() {
0
+ virtual ApplicationPtr get(const string &appRoot, const string &user = "", const string &group = "") {
0
+ MessageChannel channel(sock);
0
+ channel.write(appRoot.c_str(), user.c_str(), group.c_str(), NULL);
0
+ reader = channel.readFileDescriptor();
0
+ writer = channel.readFileDescriptor();
0
+ ApplicationPtr app(new Application(appRoot, atoi(args[0].c_str()), reader, writer));
0
+ class SocketDemultiplexer {
0
+ apr_pollset_t *pollset;
0
+ apr_uint32_t used, capacity;
0
+ void createAprPollFd(int fd, apr_pollfd_t *apr_fd) {
0
+ apr_socket_t *sock = NULL;
0
+ ret = apr_os_sock_put(&sock, &fd, pool);
0
+ if (ret != APR_SUCCESS) {
0
+ throw APRException("Unable to convert a file descriptor to an APR socket", ret);
0
+ apr_fd->desc_type = APR_POLL_SOCKET;
0
+ apr_fd->reqevents = APR_POLLIN;
0
+ apr_fd->desc.s = sock;
0
+ apr_fd->client_data = (void *) fd;
0
+ void addToPollset(apr_pollset_t *pollset, int fd) {
0
+ createAprPollFd(fd, &apr_fd);
0
+ ret = apr_pollset_add(pollset, &apr_fd);
0
+ if (ret != APR_SUCCESS) {
0
+ throw APRException("Cannot add file descriptor to poll set", ret);
0
+ SocketDemultiplexer() {
0
+ ret = apr_pool_create(&pool, NULL);
0
+ if (ret != APR_SUCCESS) {
0
+ throw APRException("Cannot create an APR memory pool", ret);
0
+ ret = apr_pollset_create(&pollset, capacity, pool, 0);
0
+ if (ret != APR_SUCCESS) {
0
+ apr_pool_destroy(pool);
0
+ throw APRException("Cannot create an APR poll set", ret);
0
+ pollResult = (int *) malloc(sizeof(int) * capacity);
0
+ ~SocketDemultiplexer() {
0
+ apr_pool_destroy(pool);
0
+ // Apparently APR does not always close the file descriptor,
0
+ // so we do it manually.
0
+ for (set<int>::const_iterator it(fds.begin()); it != fds.end(); it++) {
0
+ if (used == capacity) {
0
+ apr_pollset_t *newPollset;
0
+ ret = apr_pollset_create(&newPollset, capacity * 2, pool, 0);
0
+ if (ret != APR_SUCCESS) {
0
+ throw APRException("Cannot create a new APR poll set", ret);
0
+ newPollResult = (int *) realloc(pollResult, sizeof(int) * capacity * 2);
0
+ if (newPollResult == NULL) {
0
+ throw MemoryException("Cannot allocate memory for result vector.");
0
+ apr_pollset_destroy(pollset);
0
+ pollResult = newPollResult;
0
+ for (set<int>::const_iterator it(fds.begin()); it != fds.end(); it++) {
0
+ addToPollset(pollset, *it);
0
+ addToPollset(pollset, fd);
0
+ createAprPollFd(fd, &apr_fd);
0
+ if (apr_pollset_remove(pollset, &apr_fd) != APR_SUCCESS) {
0
+ throw MemoryException("Cannot remove a file descriptor from the poll set.");
0
+ fds.erase(fds.find(fd));
0
+ unsigned int poll(int **fileDescriptors, unsigned int timeout) {
0
+ const apr_pollfd_t *result;
0
+ ret = apr_pollset_poll(pollset, timeout * 1000, &num, &result);
0
+ } while (ret == APR_EINTR);
0
+ if (ret == APR_TIMEUP) {
0
+ } else if (ret != APR_SUCCESS) {
0
+ throw APRException("Cannot poll file descriptor set", ret);
0
+ for (apr_int32_t i = 0; i < num; i++) {
0
+ pollResult[i] = (int) result[i].client_data;
0
+ *fileDescriptors = pollResult;
0
+ StandardApplicationPool pool;
0
+ SocketDemultiplexer demultiplexer;
0
+ demultiplexer.add(serverSocket);
0
+ num = demultiplexer.poll(&fds, 500);
0
+ for (unsigned int i = 0; i < num; i++) {
0
+ if (fds[i] == serverSocket) {
0
+ acceptNewClient(demultiplexer);
0
+ void acceptNewClient(SocketDemultiplexer &demultiplexer) {
0
+ // Discard data, not important. Whatever data was sent only serves
0
+ // to wake up the server socket.
0
+ ret = read(serverSocket, &x, 1);
0
+ } while (ret == -1 && errno == EINTR);
0
+ socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
0
+ demultiplexer.add(fds[0]);
0
+ MessageChannel(serverSocket).writeFileDescriptor(fds[1]);
0
+ void handleClient(int client) {
0
+ MessageChannel channel(client);
0
+ if (channel.read(args) && args.size() == 3) {
0
+ ApplicationPtr app(pool.get(args[0], args[1], args[2]));
0
+ channel.write(toString(app->getPid()).c_str(), NULL);
0
+ channel.writeFileDescriptor(app->getReader());
0
+ channel.writeFileDescriptor(app->getWriter());
0
+/* unsigned int randomNumber() {
0
+ FILE *f = fopen("/dev/urandom", "r");
0
+ if (fread(&result, 1, sizeof(result), f) == 0) {
0
+ bool generateUniqueSocketFilename(char *output, size_t size) {
0
+ if (getenv("TMPDIR") && *getenv("TMPDIR")) {
0
+ base << getenv("TMPDIR");
0
+ base << "/phusion_passenger.tmp." << getpid() << ".";
0
+ for (int i = 0; i < 10000; i++) {
0
+ stringstream filename;
0
+ const char *filenameString;
0
+ filename << base.str() << randomNumber() << ".sock";
0
+ if (filename.str().size() > size - 1) {
0
+ filenameString = filename.str().c_str();
0
+ if (stat(filenameString, &buf) == -1 && errno == ENOENT) {
0
+ strncpy(output, filenameString, size);
0
+ ApplicationPoolServer(const string &spawnManagerCommand,
0
+ const string &logFile = "",
0
+ const string &environment = "production",
0
+ const string &rubyCommand = "ruby")
0
+ : pool(spawnManagerCommand, logFile, environment, rubyCommand) {
0
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == -1) {
0
+ throw SystemException("Cannot create a Unix socket pair", errno);
0
+ serverSocket = fds[0];
0
+ connectSocket = fds[1];
0
+ thr = new thread(ServerThreadDelegator(this));
0
+ ~ApplicationPoolServer() {
0
+ ApplicationPoolPtr connect() {
0
+ // Write some random data to wake up the server.
0
+ ret = write(connectSocket, "x", 1);
0
+ } while ((ret == -1 && errno == EAGAIN) || ret == 0);
0
+ ret = MessageChannel(connectSocket).readFileDescriptor();
0
+ return ptr(new ApplicationPoolClient(ret));
0
+ * @warning Never call this method in the process in which this
0
+ * ApplicationPoolServer was created!
0
+ #ifdef VALGRIND_FRIENDLY
0
+typedef shared_ptr<ApplicationPoolServer> ApplicationPoolServerPtr;
0
+} // namespace Passenger
0
+#endif /* _PASSENGER_APPLICATION_POOL_CLIENT_SERVER_H_ */