	SpawnResult sendSpawnCommand(const Options &options) {
		TRACE_POINT();
		FileDescriptor fd;
		try {
			fd = connectToServer(socketAddress);
		} catch (const SystemException &e) {
			BackgroundIOCapturerPtr stderrCapturer;
			throwPreloaderSpawnException("An error occurred while starting "
				"the application. Unable to connect to the preloader's "
				"socket: " + string(e.what()),
				SpawnException::APP_STARTUP_PROTOCOL_ERROR,
				stderrCapturer,
				DebugDirPtr());
		}
		
		UPDATE_TRACE_POINT();
		BufferedIO io(fd);
		unsigned long long timeout = options.startTimeout * 1000;
		string result;
		vector<string> args;
		vector<string>::const_iterator it;
		
		writeExact(fd, "spawn\n", &timeout);
		options.toVector(args, resourceLocator);
		for (it = args.begin(); it != args.end(); it++) {
			const string &key = *it;
			it++;
			const string &value = *it;
			writeExact(fd, key + ": " + value + "\n", &timeout);
		}
		writeExact(fd, "\n", &timeout);
		
		result = io.readLine(1024, &timeout);
		if (result == "OK\n") {
			UPDATE_TRACE_POINT();
			pid_t spawnedPid;
			
			spawnedPid = atoi(io.readLine(1024, &timeout).c_str());
			if (spawnedPid <= 0) {
				BackgroundIOCapturerPtr stderrCapturer;
				throwPreloaderSpawnException("An error occurred while starting "
					"the web application. Its preloader responded to the "
					"'spawn' command with an invalid PID: '" +
					toString(spawnedPid) + "'",
					SpawnException::APP_STARTUP_PROTOCOL_ERROR,
					stderrCapturer,
					DebugDirPtr());
			}
			// TODO: we really should be checking UID.
			// FIXME: for Passenger 4 we *must* check the UID otherwise this is a gaping security hole.
			if (getsid(spawnedPid) != getsid(pid)) {
				BackgroundIOCapturerPtr stderrCapturer;
				throwPreloaderSpawnException("An error occurred while starting "
					"the web application. Its preloader responded to the "
					"'spawn' command with a PID that doesn't belong to "
					"the same session: '" + toString(spawnedPid) + "'",
					SpawnException::APP_STARTUP_PROTOCOL_ERROR,
					stderrCapturer,
					DebugDirPtr());
			}
			
			SpawnResult result;
			result.pid = spawnedPid;
			result.adminSocket = fd;
			result.io = io;
			return result;
			
		} else if (result == "Error\n") {
			UPDATE_TRACE_POINT();
			NegotiationDetails details;
			details.io = io;
			details.timeout = timeout;
			handleSpawnErrorResponse(details);
			
		} else {
			UPDATE_TRACE_POINT();
			NegotiationDetails details;
			handleInvalidSpawnResponseType(result, details);
		}
		
		return SpawnResult(); // Never reached.
	}
	SpawnResult sendSpawnCommand(const Options &options) {
		TRACE_POINT();
		FileDescriptor fd;
		try {
			fd = connectToServer(socketAddress);
		} catch (const SystemException &e) {
			BackgroundIOCapturerPtr stderrCapturer;
			throwPreloaderSpawnException("An error occurred while starting "
				"the application. Unable to connect to the preloader's "
				"socket: " + string(e.what()),
				SpawnException::APP_STARTUP_PROTOCOL_ERROR,
				stderrCapturer,
				DebugDirPtr());
		}
		
		UPDATE_TRACE_POINT();
		BufferedIO io(fd);
		unsigned long long timeout = options.startTimeout * 1000;
		string result;
		vector<string> args;
		vector<string>::const_iterator it;
		
		writeExact(fd, "spawn\n", &timeout);
		options.toVector(args, resourceLocator);
		for (it = args.begin(); it != args.end(); it++) {
			const string &key = *it;
			it++;
			const string &value = *it;
			writeExact(fd, key + ": " + value + "\n", &timeout);
		}
		writeExact(fd, "\n", &timeout);
		
		result = io.readLine(1024, &timeout);
		if (result == "OK\n") {
			UPDATE_TRACE_POINT();
			pid_t spawnedPid;
			
			spawnedPid = atoi(io.readLine(1024, &timeout).c_str());
			if (spawnedPid <= 0) {
				BackgroundIOCapturerPtr stderrCapturer;
				throwPreloaderSpawnException("An error occurred while starting "
					"the web application. Its preloader responded to the "
					"'spawn' command with an invalid PID: '" +
					toString(spawnedPid) + "'",
					SpawnException::APP_STARTUP_PROTOCOL_ERROR,
					stderrCapturer,
					DebugDirPtr());
			}
			 
			 
			if (getsid(spawnedPid) != getsid(pid)) {
				BackgroundIOCapturerPtr stderrCapturer;
				throwPreloaderSpawnException("An error occurred while starting "
					"the web application. Its preloader responded to the "
					"'spawn' command with a PID that doesn't belong to "
					"the same session: '" + toString(spawnedPid) + "'",
					SpawnException::APP_STARTUP_PROTOCOL_ERROR,
					stderrCapturer,
					DebugDirPtr());
			}
			
			SpawnResult result;
			result.pid = spawnedPid;
			result.adminSocket = fd;
			result.io = io;
			return result;
			
		} else if (result == "Error\n") {
			UPDATE_TRACE_POINT();
			NegotiationDetails details;
			details.io = io;
			details.timeout = timeout;
			handleSpawnErrorResponse(details);
			
		} else {
			UPDATE_TRACE_POINT();
			NegotiationDetails details;
			handleInvalidSpawnResponseType(result, details);
		}
		
		return SpawnResult();  
	}