ApplicationPool_Server_PoolTest() { createServerInstanceDirAndGeneration(serverInstanceDir, generation); socketFilename = generation->getPath() + "/socket"; accountsDatabase = ptr(new AccountsDatabase()); accountsDatabase->add("test", "12345", false); messageServer = ptr(new MessageServer(socketFilename, accountsDatabase)); realPool = ptr(new ApplicationPool::Pool("../helper-scripts/passenger-spawn-server", generation)); poolServer = ptr(new ApplicationPool::Server(realPool)); messageServer->addHandler(poolServer); serverThread = ptr(new oxt::thread( boost::bind(&MessageServer::mainLoop, messageServer.get()) )); pool = newPoolConnection(); pool2 = newPoolConnection(); }
Server(FileDescriptor feedbackFd, const AgentOptions &_options) : options(_options), requestLoop(true), serverInstanceDir(_options.serverInstanceDir, false), resourceLocator(options.passengerRoot) { TRACE_POINT(); this->feedbackFd = feedbackFd; UPDATE_TRACE_POINT(); generation = serverInstanceDir.getGeneration(options.generationNumber); startListening(); accountsDatabase = boost::make_shared<AccountsDatabase>(); accountsDatabase->add("_passenger-status", options.adminToolStatusPassword, false, Account::INSPECT_BASIC_INFO | Account::INSPECT_SENSITIVE_INFO | Account::INSPECT_BACKTRACES | Account::INSPECT_REQUESTS); accountsDatabase->add("_web_server", options.exitPassword, false, Account::EXIT); messageServer = boost::make_shared<MessageServer>( parseUnixSocketAddress(options.adminSocketAddress), accountsDatabase); createFile(generation->getPath() + "/helper_agent.pid", toString(getpid()), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (geteuid() == 0 && !options.userSwitching) { lowerPrivilege(options.defaultUser, options.defaultGroup); } UPDATE_TRACE_POINT(); randomGenerator = boost::make_shared<RandomGenerator>(); // Check whether /dev/urandom is actually random. // https://code.google.com/p/phusion-passenger/issues/detail?id=516 if (randomGenerator->generateByteString(16) == randomGenerator->generateByteString(16)) { throw RuntimeException("Your random number device, /dev/urandom, appears to be broken. " "It doesn't seem to be returning random data. Please fix this."); } UPDATE_TRACE_POINT(); loggerFactory = boost::make_shared<UnionStation::LoggerFactory>(options.loggingAgentAddress, "logging", options.loggingAgentPassword); spawnerFactory = boost::make_shared<SpawnerFactory>(poolLoop.safe, resourceLocator, generation, boost::make_shared<SpawnerConfig>(randomGenerator)); pool = boost::make_shared<Pool>(poolLoop.safe.get(), spawnerFactory, loggerFactory, randomGenerator); pool->initialize(); pool->setMax(options.maxPoolSize); //pool->setMaxPerApp(maxInstancesPerApp); pool->setMaxIdleTime(options.poolIdleTime * 1000000); requestHandler = boost::make_shared<RequestHandler>(requestLoop.safe, requestSocket, pool, options); messageServer->addHandler(boost::make_shared<RemoteController>(requestHandler, pool)); messageServer->addHandler(ptr(new ExitHandler(exitEvent))); sigquitWatcher.set(requestLoop.loop); sigquitWatcher.set(SIGQUIT); sigquitWatcher.set<Server, &Server::onSigquit>(this); sigquitWatcher.start(); UPDATE_TRACE_POINT(); writeArrayMessage(feedbackFd, "initialized", getRequestSocketFilename().c_str(), messageServer->getSocketFilename().c_str(), NULL); boost::function<void ()> func = boost::bind(prestartWebApps, resourceLocator, options.defaultRubyCommand, options.prestartUrls ); prestarterThread = ptr(new oxt::thread( boost::bind(runAndPrintExceptions, func, true) )); }
Server(FileDescriptor feedbackFd, const AgentOptions &_options) : options(_options), requestLoop(true), serverInstanceDir(options.webServerPid, options.tempDir, false), resourceLocator(options.passengerRoot) { TRACE_POINT(); this->feedbackFd = feedbackFd; UPDATE_TRACE_POINT(); generation = serverInstanceDir.getGeneration(options.generationNumber); startListening(); accountsDatabase = AccountsDatabase::createDefault(generation, options.userSwitching, options.defaultUser, options.defaultGroup); accountsDatabase->add("_web_server", options.messageSocketPassword, false, Account::EXIT); messageServer = ptr(new MessageServer(generation->getPath() + "/socket", accountsDatabase)); createFile(generation->getPath() + "/helper_agent.pid", toString(getpid()), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (geteuid() == 0 && !options.userSwitching) { lowerPrivilege(options.defaultUser, options.defaultGroup); } UPDATE_TRACE_POINT(); loggerFactory = make_shared<UnionStation::LoggerFactory>(options.loggingAgentAddress, "logging", options.loggingAgentPassword); randomGenerator = make_shared<RandomGenerator>(); spawnerFactory = make_shared<SpawnerFactory>(poolLoop.safe, resourceLocator, generation, randomGenerator); pool = make_shared<Pool>(poolLoop.safe.get(), spawnerFactory, loggerFactory, randomGenerator); pool->setMax(options.maxPoolSize); //pool->setMaxPerApp(maxInstancesPerApp); pool->setMaxIdleTime(options.poolIdleTime * 1000000); messageServer->addHandler(make_shared<RemoteController>(pool)); messageServer->addHandler(make_shared<BacktracesServer>()); messageServer->addHandler(ptr(new ExitHandler(exitEvent))); requestHandler = make_shared<RequestHandler>(requestLoop.safe, requestSocket, pool, options); sigquitWatcher.set(requestLoop.loop); sigquitWatcher.set(SIGQUIT); sigquitWatcher.set<Server, &Server::onSigquit>(this); sigquitWatcher.start(); UPDATE_TRACE_POINT(); writeArrayMessage(feedbackFd, "initialized", getRequestSocketFilename().c_str(), messageServer->getSocketFilename().c_str(), NULL); function<void ()> func = boost::bind(prestartWebApps, resourceLocator, options.prestartUrls ); prestarterThread = ptr(new oxt::thread( boost::bind(runAndPrintExceptions, func, true) )); }
int main(int argc, char *argv[]) { VariantMap options = initializeAgent(argc, argv, "PassengerLoggingAgent"); string socketAddress = options.get("logging_agent_address"); string dumpFile = options.get("analytics_dump_file", false, "/dev/null"); string password = options.get("logging_agent_password"); string username = options.get("analytics_log_user", false, myself()); string groupname = options.get("analytics_log_group", false); string unionStationGatewayAddress = options.get("union_station_gateway_address", false, DEFAULT_UNION_STATION_GATEWAY_ADDRESS); int unionStationGatewayPort = options.getInt("union_station_gateway_port", false, DEFAULT_UNION_STATION_GATEWAY_PORT); string unionStationGatewayCert = options.get("union_station_gateway_cert", false); string unionStationProxyAddress = options.get("union_station_proxy_address", false); curl_global_init(CURL_GLOBAL_ALL); try { /********** Now begins the real initialization **********/ /* Create all the necessary objects and sockets... */ AccountsDatabasePtr accountsDatabase; FileDescriptor serverSocketFd; struct passwd *user; struct group *group; int ret; eventLoop = createEventLoop(); accountsDatabase = ptr(new AccountsDatabase()); serverSocketFd = createServer(socketAddress.c_str()); if (getSocketAddressType(socketAddress) == SAT_UNIX) { do { ret = chmod(parseUnixSocketAddress(socketAddress).c_str(), S_ISVTX | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH); } while (ret == -1 && errno == EINTR); } /* Sanity check user accounts. */ user = getpwnam(username.c_str()); if (user == NULL) { throw NonExistentUserException(string("The configuration option ") + "'PassengerAnalyticsLogUser' (Apache) or " + "'passenger_analytics_log_user' (Nginx) was set to '" + username + "', but this user doesn't exist. Please fix " + "the configuration option."); } if (groupname.empty()) { group = getgrgid(user->pw_gid); if (group == NULL) { throw NonExistentGroupException(string("The configuration option ") + "'PassengerAnalyticsLogGroup' (Apache) or " + "'passenger_analytics_log_group' (Nginx) wasn't set, " + "so PassengerLoggingAgent tried to use the default group " + "for user '" + username + "' - which is GID #" + toString(user->pw_gid) + " - as the group for the analytics " + "log dir, but this GID doesn't exist. " + "You can solve this problem by explicitly " + "setting PassengerAnalyticsLogGroup (Apache) or " + "passenger_analytics_log_group (Nginx) to a group that " + "does exist. In any case, it looks like your system's user " + "database is broken; Phusion Passenger can work fine even " + "with this broken user database, but you should still fix it."); } else { groupname = group->gr_name; } } else { group = getgrnam(groupname.c_str()); if (group == NULL) { throw NonExistentGroupException(string("The configuration option ") + "'PassengerAnalyticsLogGroup' (Apache) or " + "'passenger_analytics_log_group' (Nginx) was set to '" + groupname + "', but this group doesn't exist. Please fix " + "the configuration option."); } } /* Now's a good time to lower the privilege. */ if (geteuid() == 0) { lowerPrivilege(username, user, group); } /* Now setup the actual logging server. */ accountsDatabase->add("logging", password, false); LoggingServer server(eventLoop, serverSocketFd, accountsDatabase, dumpFile, unionStationGatewayAddress, unionStationGatewayPort, unionStationGatewayCert, unionStationProxyAddress); loggingServer = &server; ev::io feedbackFdWatcher(eventLoop); ev::sig sigintWatcher(eventLoop); ev::sig sigtermWatcher(eventLoop); ev::sig sigquitWatcher(eventLoop); if (feedbackFdAvailable()) { feedbackFdWatcher.set<&feedbackFdBecameReadable>(); feedbackFdWatcher.start(FEEDBACK_FD, ev::READ); writeArrayMessage(FEEDBACK_FD, "initialized", NULL); } sigintWatcher.set<&caughtExitSignal>(); sigintWatcher.start(SIGINT); sigtermWatcher.set<&caughtExitSignal>(); sigtermWatcher.start(SIGTERM); sigquitWatcher.set<&printInfo>(); sigquitWatcher.start(SIGQUIT); /********** Initialized! Enter main loop... **********/ P_WARN("PassengerLoggingAgent online, listening at " << socketAddress); ev_run(eventLoop, 0); P_DEBUG("Logging agent exiting with code " << exitCode << "."); return exitCode; } catch (const tracable_exception &e) { P_ERROR("*** ERROR: " << e.what() << "\n" << e.backtrace()); return 1; } }
Server(FileDescriptor feedbackFd, pid_t webServerPid, const string &tempDir, bool userSwitching, const string &defaultUser, const string &defaultGroup, const string &passengerRoot, const string &rubyCommand, unsigned int generationNumber, unsigned int maxPoolSize, unsigned int maxInstancesPerApp, unsigned int poolIdleTime, const VariantMap &options) : serverInstanceDir(webServerPid, tempDir, false), resourceLocator(passengerRoot) { TRACE_POINT(); string messageSocketPassword; string loggingAgentPassword; this->feedbackFd = feedbackFd; feedbackChannel = MessageChannel(feedbackFd); UPDATE_TRACE_POINT(); messageSocketPassword = Base64::decode(options.get("message_socket_password")); loggingAgentPassword = options.get("logging_agent_password"); generation = serverInstanceDir.getGeneration(generationNumber); accountsDatabase = AccountsDatabase::createDefault(generation, userSwitching, defaultUser, defaultGroup); accountsDatabase->add("_web_server", messageSocketPassword, false, Account::GET | Account::DETACH | Account::SET_PARAMETERS | Account::EXIT); messageServer = ptr(new MessageServer(generation->getPath() + "/socket", accountsDatabase)); createFile(generation->getPath() + "/helper_server.pid", toString(getpid()), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (geteuid() == 0 && !userSwitching) { lowerPrivilege(defaultUser, defaultGroup); } UPDATE_TRACE_POINT(); analyticsLogger = ptr(new AnalyticsLogger(options.get("logging_agent_address"), "logging", loggingAgentPassword)); pool = ptr(new ApplicationPool::Pool( resourceLocator.getSpawnServerFilename(), generation, accountsDatabase, rubyCommand, analyticsLogger, options.getInt("log_level"), options.get("debug_log_file", false) )); pool->setMax(maxPoolSize); pool->setMaxPerApp(maxInstancesPerApp); pool->setMaxIdleTime(poolIdleTime); messageServer->addHandler(ptr(new TimerUpdateHandler(exitTimer))); messageServer->addHandler(ptr(new ApplicationPool::Server(pool))); messageServer->addHandler(ptr(new BacktracesServer())); messageServer->addHandler(ptr(new ExitHandler(exitEvent))); UPDATE_TRACE_POINT(); feedbackChannel.write("initialized", "", // Request socket filename; not available in the Apache helper server. messageServer->getSocketFilename().c_str(), NULL); prestarterThread = ptr(new oxt::thread( boost::bind(prestartWebApps, resourceLocator, options.get("prestart_urls")) )); }