async getProjectStates(): Promise<Array<ProjectState>> {
   const projectStates = [];
   for (const connection of this._server.getConnections()) {
     // Just in case the connection is no longer valid, we wrap it with a
     // timeout less than Nuclide RPC's default of 60s. We swallow any
     // errors and return an empty ProjectState if this happens.
     projectStates.push(
       timeoutPromise(
         connection.getAtomCommands().getProjectState(),
         GET_PROJECT_STATES_TIMEOUT_MS,
       ).catch(error => ({
         rootFolders: [],
       })),
     );
   }
   const resolvedProjectStates = await Promise.all(projectStates);
   return [].concat(...resolvedProjectStates);
 }
Example #2
0
 async _closeServerConnection(shutdown: boolean): Promise<void> {
   try {
     // If the Nuclide server has already been shutdown or has crashed,
     // the closeConnection() call will attempt to disconnect from the Nuclide
     // server forever. This sets a 5 second timeout for it so that the rest
     // of this function and anything calling it can complete.
     await timeoutPromise(
       this._getInfoService().closeConnection(shutdown),
       5000,
     );
   } catch (e) {
     getLogger('nuclide-remote-connection').error(
       'Failed to close Nuclide server connection.',
     );
   } finally {
     if (shutdown) {
       // Clear the saved connection config so we don't try it again at startup.
       clearConnectionConfig(this._config.host);
     }
   }
 }
 Array.from(relatedFilesProviders.values()).map(provider =>
   timeoutPromise(provider.getRelatedFiles(path), 2000),
Example #4
0
export async function startServer({
  certificateStrategy,
  ports,
  timeout,
  expirationDays,
  exclusive,
  jsonOutputFile,
  absolutePathToServerMain,
  serverParams,
}: StartServerParams): Promise<void> {
  const logger = getLogger();
  logger.info('in startServer()');

  let paths;
  let certificateGeneratorOutput = {};
  switch (certificateStrategy.type) {
    case 'generate':
      const {
        clientCommonName,
        serverCommonName,
        openSSLConfigPath,
      } = certificateStrategy;
      paths = await generateCertificates(
        clientCommonName,
        serverCommonName,
        openSSLConfigPath,
        expirationDays,
      );
      logger.info('generateCertificates() succeeded!');
      certificateGeneratorOutput = {
        hostname: serverCommonName,
        cert: await fs.readFileAsString(paths.clientCert),
        key: await fs.readFileAsString(paths.clientKey),
      };
      break;
    case 'reuse':
      paths = certificateStrategy.paths;
      logger.info('reusing existing certificates');
      break;
    default:
      (certificateStrategy.type: empty);
      throw Error('invalid certificate strategy');
  }

  const [key, cert, ca] = await Promise.all([
    fs.readFileAsBuffer(paths.serverKey),
    fs.readFileAsBuffer(paths.serverCert),
    fs.readFileAsBuffer(paths.caCert),
  ]);
  const params: LauncherScriptParams = {
    key: key.toString(),
    cert: cert.toString(),
    ca: ca.toString(),
    ports,
    expirationDays,
    exclusive,
    absolutePathToServerMain,
    serverParams,
  };

  // Redirect child stderr to a file so that we can read it.
  // (If we just pipe it, there's no safe way of disconnecting it after.)
  temp.track();
  const stderrLog = temp.openSync('big-dig-stderr');

  const launcherScript = require.resolve('./launchServer-entry.js');
  logger.info(`About to spawn ${launcherScript} to launch Big Dig server.`);
  const child = child_process.spawn(
    process.execPath,
    [
      // Increase stack trace limit for better debug logs.
      // For reference, Atom/Electron does not have a stack trace limit.
      '--stack-trace-limit=50',
      // Increase the maximum heap size if we have enough memory.
      ...(os.totalmem() > 8 * 1024 * 1024 * 1024
        ? ['--max-old-space-size=4096']
        : []),
      launcherScript,
    ],
    {
      detached: true,
      stdio: ['ignore', 'ignore', stderrLog.fd, 'ipc'],
    },
  );
  logger.info(`spawn called for ${launcherScript}`);
  // Send launch parameters over IPC to avoid making them visible in `ps`.
  child.send(params);

  const childPort = await timeoutPromise(
    new Promise((resolve, reject) => {
      const onMessage = ({port: result}) => {
        resolve(result);
        child.removeAllListeners();
      };
      child.on('message', onMessage);
      child.on('error', reject);
      child.on('exit', async code => {
        const stderr = await fs
          .readFileAsString(stderrLog.path)
          .catch(() => '');
        reject(
          Error(`Child exited early with code ${code}.\nstderr: ${stderr}`),
        );
      });
    }),
    timeout,
  ).catch(err => {
    // Make sure we clean up hung children.
    if (err instanceof TimedOutError) {
      child.kill('SIGKILL');
    }
    return Promise.reject(err);
  });

  const {version} = require('../../package.json');
  const json = JSON.stringify(
    // These properties are the ones currently written by nuclide-server.
    {
      ...certificateGeneratorOutput,
      pid: child.pid,
      version,
      port: childPort,
      ca: ca.toString(),
      ca_path: paths.caCert,
      server_cert_path: paths.serverCert,
      server_key_path: paths.serverKey,
      protocol_version: 2,
      success: true,
    },
  );
  await fs.writeFile(jsonOutputFile, json, {mode: 0o600});
  logger.info(`Server config written to ${jsonOutputFile}.`);
  child.unref();
}