Ejemplo n.º 1
0
	~Server() {
		TRACE_POINT();
		this_thread::disable_syscall_interruption dsi;
		this_thread::disable_interruption di;
		
		P_DEBUG("Shutting down helper agent...");
		prestarterThread->interrupt_and_join();
		if (messageServerThread != NULL) {
			messageServerThread->interrupt_and_join();
		}
		
		messageServer.reset();
		P_DEBUG("Destroying application pool...");
		pool->destroy();
		uninstallDiagnosticsDumper();
		pool.reset();
		poolLoop.stop();
		requestLoop.stop();
		requestHandler.reset();

		if (!options.requestSocketLink.empty()) {
			syscalls::unlink(options.requestSocketLink.c_str());
		}
		
		P_TRACE(2, "All threads have been shut down.");
	}
Ejemplo n.º 2
0
	void mainLoop() {
		TRACE_POINT();
		boost::function<void ()> func;

		func = boost::bind(&MessageServer::mainLoop, messageServer.get());
		messageServerThread = ptr(new oxt::thread(
			boost::bind(runAndPrintExceptions, func, true),
			"MessageServer thread", MESSAGE_SERVER_THREAD_STACK_SIZE
		));
		
		poolLoop.start("Pool event loop", 0);
		requestLoop.start("Request event loop", 0);

		
		/* Wait until the watchdog closes the feedback fd (meaning it
		 * was killed) or until we receive an exit message.
		 */
		this_thread::disable_syscall_interruption dsi;
		fd_set fds;
		int largestFd;
		
		FD_ZERO(&fds);
		FD_SET(feedbackFd, &fds);
		FD_SET(exitEvent.fd(), &fds);
		largestFd = (feedbackFd > exitEvent.fd()) ? (int) feedbackFd : exitEvent.fd();
		UPDATE_TRACE_POINT();
		installDiagnosticsDumper();
		if (syscalls::select(largestFd + 1, &fds, NULL, NULL, NULL) == -1) {
			int e = errno;
			uninstallDiagnosticsDumper();
			throw SystemException("select() failed", e);
		}
		
		if (FD_ISSET(feedbackFd, &fds)) {
			/* If the watchdog has been killed then we'll kill all descendant
			 * processes and exit. There's no point in keeping this helper
			 * server running because we can't detect when the web server exits,
			 * and because this helper agent doesn't own the server instance
			 * directory. As soon as passenger-status is run, the server
			 * instance directory will be cleaned up, making this helper agent
			 * inaccessible.
			 */
			P_DEBUG("Watchdog seems to be killed; forcing shutdown of all subprocesses");
			syscalls::killpg(getpgrp(), SIGKILL);
			_exit(2); // In case killpg() fails.
		} else {
			/* We received an exit command. We want to exit 5 seconds after
			 * all clients have disconnected have become inactive.
			 */
			P_DEBUG("Received command to exit gracefully. "
				"Waiting until 5 seconds after all clients have disconnected...");
			while (requestHandler->inactivityTime() < 5000) {
				syscalls::usleep(250000);
			}
			P_DEBUG("It's now 5 seconds after all clients have disconnected. "
				"Proceeding with graceful exit.");
		}
	}
Ejemplo n.º 3
0
	void mainLoop() {
		TRACE_POINT();
		
		messageServerThread.reset(new oxt::thread(
			boost::bind(&MessageServer::mainLoop, messageServer.get()),
			"MessageServer thread",
			MESSAGE_SERVER_STACK_SIZE
		));
		
		/* Wait until the watchdog closes the feedback fd (meaning it
		 * was killed) or until we receive an exit message.
		 */
		this_thread::disable_syscall_interruption dsi;
		fd_set fds;
		int largestFd;
		
		FD_ZERO(&fds);
		FD_SET(feedbackFd, &fds);
		FD_SET(exitEvent.fd(), &fds);
		largestFd = (feedbackFd > exitEvent.fd()) ? (int) feedbackFd : exitEvent.fd();
		UPDATE_TRACE_POINT();
		if (syscalls::select(largestFd + 1, &fds, NULL, NULL, NULL) == -1) {
			int e = errno;
			throw SystemException("select() failed", e);
		}
		
		if (FD_ISSET(feedbackFd, &fds)) {
			/* If the watchdog has been killed then we'll kill all descendant
			 * processes and exit. There's no point in keeping this helper
			 * server running because we can't detect when the web server exits,
			 * and because this helper server doesn't own the server instance
			 * directory. As soon as passenger-status is run, the server
			 * instance directory will be cleaned up, making this helper server
			 * inaccessible.
			 */
			syscalls::killpg(getpgrp(), SIGKILL);
			_exit(2); // In case killpg() fails.
		} else {
			/* We received an exit command. We want to exit 5 seconds after
			 * the last client has disconnected, .
			 */
			exitTimer.start();
			exitTimer.wait(5000);
		}
	}
Ejemplo n.º 4
0
	~Server() {
		TRACE_POINT();
		this_thread::disable_syscall_interruption dsi;
		this_thread::disable_interruption di;
		
		P_DEBUG("Shutting down helper agent...");
		prestarterThread->interrupt_and_join();
		if (messageServerThread != NULL) {
			messageServerThread->interrupt_and_join();
		}
		
		messageServer.reset();
		P_DEBUG("Destroying application pool...");
		pool->destroy();
		uninstallDiagnosticsDumper();
		pool.reset();
		poolLoop.stop();
		requestLoop.stop();
		requestHandler.reset();

		if (!options.requestSocketLink.empty()) {
			char path[PATH_MAX + 1];
			ssize_t ret;
			bool shouldUnlink;

			ret = readlink(options.requestSocketLink.c_str(), path, PATH_MAX);
			if (ret != -1) {
				path[ret] = '\0';
				// Only unlink if a new Flying Passenger instance hasn't overwritten the
				// symlink.
				// https://code.google.com/p/phusion-passenger/issues/detail?id=939
				shouldUnlink = getRequestSocketFilename() == path;
			} else {
				shouldUnlink = true;
			}

			if (shouldUnlink) {
				syscalls::unlink(options.requestSocketLink.c_str());
			}
		}
		
		P_TRACE(2, "All threads have been shut down.");
	}
Ejemplo n.º 5
0
	~Server() {
		TRACE_POINT();
		this_thread::disable_syscall_interruption dsi;
		this_thread::disable_interruption di;
		
		P_DEBUG("Shutting down helper agent...");
		prestarterThread->interrupt_and_join();
		if (messageServerThread != NULL) {
			messageServerThread->interrupt_and_join();
		}
		
		messageServer.reset();
		pool->destroy();
		pool.reset();
		requestHandler.reset();
		poolLoop.stop();
		requestLoop.stop();
		
		P_TRACE(2, "All threads have been shut down.");
	}
Ejemplo n.º 6
0
	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)
		));
	}
Ejemplo n.º 7
0
	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)
		));
	}
Ejemplo n.º 8
0
	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"))
		));
	}