All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.elasticsearch.bootstrap.Elasticsearch Maven / Gradle / Ivy

There is a newer version: 9.0.0-beta1
Show newest version
/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the Elastic License
 * 2.0 and the Server Side Public License, v 1; you may not use this file except
 * in compliance with, at your election, the Elastic License 2.0 or the Server
 * Side Public License, v 1.
 */

package org.elasticsearch.bootstrap;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configurator;
import org.apache.lucene.util.Constants;
import org.apache.lucene.util.StringHelper;
import org.apache.lucene.util.VectorUtil;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.Version;
import org.elasticsearch.action.support.SubscribableListener;
import org.elasticsearch.common.ReferenceDocs;
import org.elasticsearch.common.filesystem.FileSystemNatives;
import org.elasticsearch.common.io.stream.InputStreamStreamInput;
import org.elasticsearch.common.logging.LogConfigurator;
import org.elasticsearch.common.network.IfConfig;
import org.elasticsearch.common.settings.SecureSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.BoundTransportAddress;
import org.elasticsearch.core.AbstractRefCounted;
import org.elasticsearch.core.IOUtils;
import org.elasticsearch.core.SuppressForbidden;
import org.elasticsearch.env.Environment;
import org.elasticsearch.index.IndexVersion;
import org.elasticsearch.jdk.JarHell;
import org.elasticsearch.monitor.jvm.HotThreads;
import org.elasticsearch.monitor.jvm.JvmInfo;
import org.elasticsearch.monitor.os.OsProbe;
import org.elasticsearch.monitor.process.ProcessProbe;
import org.elasticsearch.node.Node;
import org.elasticsearch.node.NodeValidationException;

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.lang.invoke.MethodHandles;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.Permission;
import java.security.Security;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import static org.elasticsearch.bootstrap.BootstrapSettings.SECURITY_FILTER_BAD_DEFAULTS_SETTING;

/**
 * This class starts elasticsearch.
 */
class Elasticsearch {

    /**
     * Main entry point for starting elasticsearch.
     */
    public static void main(final String[] args) {

        Bootstrap bootstrap = initPhase1();
        assert bootstrap != null;

        try {
            initPhase2(bootstrap);
            initPhase3(bootstrap);
        } catch (NodeValidationException e) {
            bootstrap.exitWithNodeValidationException(e);
        } catch (Throwable t) {
            bootstrap.exitWithUnknownException(t);
        }
    }

    @SuppressForbidden(reason = "grab stderr for communication with server-cli")
    private static PrintStream getStderr() {
        return System.err;
    }

    // TODO: remove this, just for debugging
    @SuppressForbidden(reason = "grab stdout for communication with server-cli")
    private static PrintStream getStdout() {
        return System.out;
    }

    /**
     * First phase of process initialization.
     *
     * 

Phase 1 consists of some static initialization, reading args from the CLI process, and * finally initializing logging. As little as possible should be done in this phase because * initializing logging is the last step. */ private static Bootstrap initPhase1() { final PrintStream out = getStdout(); final PrintStream err = getStderr(); final ServerArgs args; try { initSecurityProperties(); /* * We want the JVM to think there is a security manager installed so that if internal policy decisions that would be based on * the presence of a security manager or lack thereof act as if there is a security manager present (e.g., DNS cache policy). * This forces such policies to take effect immediately. */ org.elasticsearch.bootstrap.Security.setSecurityManager(new SecurityManager() { @Override public void checkPermission(Permission perm) { // grant all permissions so that we can later set the security manager to the one that we want } }); LogConfigurator.registerErrorListener(); BootstrapInfo.init(); // note that reading server args does *not* close System.in, as it will be read from later for shutdown notification var in = new InputStreamStreamInput(System.in); args = new ServerArgs(in); // mostly just paths are used in phase 1, so secure settings are not needed Environment nodeEnv = new Environment(args.nodeSettings(), args.configDir()); BootstrapInfo.setConsole(ConsoleLoader.loadConsole(nodeEnv)); // DO NOT MOVE THIS // Logging must remain the last step of phase 1. Anything init steps needing logging should be in phase 2. LogConfigurator.setNodeName(Node.NODE_NAME_SETTING.get(args.nodeSettings())); LogConfigurator.configure(nodeEnv, args.quiet() == false); } catch (Throwable t) { // any exception this early needs to be fully printed and fail startup t.printStackTrace(err); err.flush(); Bootstrap.exit(1); // mimic JDK exit code on exception return null; // unreachable, to satisfy compiler } return new Bootstrap(out, err, args); } /** * Second phase of process initialization. * *

Phase 2 consists of everything that must occur up to and including security manager initialization. */ private static void initPhase2(Bootstrap bootstrap) throws IOException { final ServerArgs args = bootstrap.args(); final SecureSettings secrets = args.secrets(); bootstrap.setSecureSettings(secrets); Environment nodeEnv = createEnvironment(args.configDir(), args.nodeSettings(), secrets); bootstrap.setEnvironment(nodeEnv); initPidFile(args.pidFile()); // install the default uncaught exception handler; must be done before security is // initialized as we do not want to grant the runtime permission // setDefaultUncaughtExceptionHandler Thread.setDefaultUncaughtExceptionHandler(new ElasticsearchUncaughtExceptionHandler()); bootstrap.spawner().spawnNativeControllers(nodeEnv); nodeEnv.validateNativesConfig(); // temporary directories are important for JNA initializeNatives( nodeEnv.tmpFile(), BootstrapSettings.MEMORY_LOCK_SETTING.get(args.nodeSettings()), true, // always install system call filters, not user-configurable since 8.0.0 BootstrapSettings.CTRLHANDLER_SETTING.get(args.nodeSettings()) ); // initialize probes before the security manager is installed initializeProbes(); Runtime.getRuntime().addShutdownHook(new Thread(Elasticsearch::shutdown)); // look for jar hell final Logger logger = LogManager.getLogger(JarHell.class); JarHell.checkJarHell(logger::debug); // Log ifconfig output before SecurityManager is installed IfConfig.logIfNecessary(); ensureInitialized( // ReferenceDocs class does nontrivial static initialization which should always succeed but load it now (before SM) to be sure ReferenceDocs.class, // The following classes use MethodHandles.lookup during initialization, load them now (before SM) to be sure they succeed AbstractRefCounted.class, SubscribableListener.class, // We eagerly initialize to work around log4j permissions & JDK-8309727 VectorUtil.class ); // install SM after natives, shutdown hooks, etc. org.elasticsearch.bootstrap.Security.configure( nodeEnv, SECURITY_FILTER_BAD_DEFAULTS_SETTING.get(args.nodeSettings()), args.pidFile() ); } private static void ensureInitialized(Class... classes) { for (final var clazz : classes) { try { MethodHandles.publicLookup().ensureInitialized(clazz); } catch (IllegalAccessException unexpected) { throw new AssertionError(unexpected); } } } /** * Third phase of initialization. * *

Phase 3 consists of everything after security manager is initialized. Up until now, the system has been single * threaded. This phase can spawn threads, write to the log, and is subject ot the security manager policy. * *

At the end of phase 3 the system is ready to accept requests and the main thread is ready to terminate. This means: *

    *
  • The node components have been constructed and started
  • *
  • Cleanup has been done (eg secure settings are closed)
  • *
  • At least one thread other than the main thread is alive and will stay alive after the main thread terminates
  • *
  • The parent CLI process has been notified the system is ready
  • *
* * @param bootstrap the bootstrap state * @throws IOException if a problem with filesystem or network occurs * @throws NodeValidationException if the node cannot start due to a node configuration issue */ private static void initPhase3(Bootstrap bootstrap) throws IOException, NodeValidationException { checkLucene(); Node node = new Node(bootstrap.environment()) { @Override protected void validateNodeBeforeAcceptingRequests( final BootstrapContext context, final BoundTransportAddress boundTransportAddress, List checks ) throws NodeValidationException { BootstrapChecks.check(context, boundTransportAddress, checks); } }; INSTANCE = new Elasticsearch(bootstrap.spawner(), node); // any secure settings must be read during node construction IOUtils.close(bootstrap.secureSettings()); INSTANCE.start(); if (bootstrap.args().daemonize()) { LogConfigurator.removeConsoleAppender(); } // DO NOT MOVE THIS // Signaling readiness to accept requests must remain the last step of initialization. Note that it is extremely // important closing the err stream to the CLI when daemonizing is the last statement since that is the only // way to pass errors to the CLI bootstrap.sendCliMarker(BootstrapInfo.SERVER_READY_MARKER); if (bootstrap.args().daemonize()) { bootstrap.closeStreams(); } else { startCliMonitorThread(System.in); } } /** * Initialize native resources. * * @param tmpFile the temp directory * @param mlockAll whether or not to lock memory * @param systemCallFilter whether or not to install system call filters * @param ctrlHandler whether or not to install the ctrl-c handler (applies to Windows only) */ static void initializeNatives(final Path tmpFile, final boolean mlockAll, final boolean systemCallFilter, final boolean ctrlHandler) { final Logger logger = LogManager.getLogger(Elasticsearch.class); // check if the user is running as root, and bail if (Natives.definitelyRunningAsRoot()) { throw new RuntimeException("can not run elasticsearch as root"); } if (systemCallFilter) { /* * Try to install system call filters; if they fail to install; a bootstrap check will fail startup in production mode. * * TODO: should we fail hard here if system call filters fail to install, or remain lenient in non-production environments? */ Natives.tryInstallSystemCallFilter(tmpFile); } // mlockall if requested if (mlockAll) { if (Constants.WINDOWS) { Natives.tryVirtualLock(); } else { Natives.tryMlockall(); } } // listener for windows close event if (ctrlHandler) { Natives.addConsoleCtrlHandler(new ConsoleCtrlHandler() { @Override public boolean handle(int code) { if (CTRL_CLOSE_EVENT == code) { logger.info("running graceful exit on windows"); shutdown(); return true; } return false; } }); } // force remainder of JNA to be loaded (if available). try { JNAKernel32Library.getInstance(); } catch (Exception ignored) { // we've already logged this. } Natives.trySetMaxNumberOfThreads(); Natives.trySetMaxSizeVirtualMemory(); Natives.trySetMaxFileSize(); // init lucene random seed. it will use /dev/urandom where available: StringHelper.randomId(); // init filesystem natives FileSystemNatives.init(); } static void initializeProbes() { // Force probes to be loaded ProcessProbe.getInstance(); OsProbe.getInstance(); JvmInfo.jvmInfo(); HotThreads.initializeRuntimeMonitoring(); } static void checkLucene() { if (IndexVersion.CURRENT.luceneVersion().equals(org.apache.lucene.util.Version.LATEST) == false) { throw new AssertionError( "Lucene version mismatch this version of Elasticsearch requires lucene version [" + IndexVersion.CURRENT.luceneVersion() + "] but the current lucene version is [" + org.apache.lucene.util.Version.LATEST + "]" ); } } /** * Starts a thread that monitors stdin for a shutdown signal. * * If the shutdown signal is received, Elasticsearch exits with status code 0. * If the pipe is broken, Elasticsearch exits with status code 1. * * @param stdin Standard input for this process */ private static void startCliMonitorThread(InputStream stdin) { new Thread(() -> { int msg = -1; try { msg = stdin.read(); } catch (IOException e) { // ignore, whether we cleanly got end of stream (-1) or an error, we will shut down below } finally { if (msg == BootstrapInfo.SERVER_SHUTDOWN_MARKER) { Bootstrap.exit(0); } else { // parent process died or there was an error reading from it Bootstrap.exit(1); } } }).start(); } /** * Writes the current process id into the given pidfile, if not null. The pidfile is cleaned up on system exit. * * @param pidFile A path to a file, or null of no pidfile should be written */ private static void initPidFile(Path pidFile) throws IOException { if (pidFile == null) { return; } Runtime.getRuntime().addShutdownHook(new Thread(() -> { try { Files.deleteIfExists(pidFile); } catch (IOException e) { throw new ElasticsearchException("Failed to delete pid file " + pidFile, e); } }, "elasticsearch[pidfile-cleanup]")); // It has to be an absolute path, otherwise pidFile.getParent() will return null assert pidFile.isAbsolute(); if (Files.exists(pidFile.getParent()) == false) { Files.createDirectories(pidFile.getParent()); } Files.writeString(pidFile, Long.toString(ProcessHandle.current().pid())); } private static void initSecurityProperties() { for (final String property : new String[] { "networkaddress.cache.ttl", "networkaddress.cache.negative.ttl" }) { final String overrideProperty = "es." + property; final String overrideValue = System.getProperty(overrideProperty); if (overrideValue != null) { try { // round-trip the property to an integer and back to a string to ensure that it parses properly Security.setProperty(property, Integer.toString(Integer.valueOf(overrideValue))); } catch (final NumberFormatException e) { throw new IllegalArgumentException("failed to parse [" + overrideProperty + "] with value [" + overrideValue + "]", e); } } } // policy file codebase declarations in security.policy rely on property expansion, see PolicyUtil.readPolicy Security.setProperty("policy.expandProperties", "true"); } private static Environment createEnvironment(Path configDir, Settings initialSettings, SecureSettings secureSettings) { Settings.Builder builder = Settings.builder(); builder.put(initialSettings); if (secureSettings != null) { builder.setSecureSettings(secureSettings); } return new Environment(builder.build(), configDir); } // -- instance private static volatile Elasticsearch INSTANCE; private final Spawner spawner; private final Node node; private final CountDownLatch keepAliveLatch = new CountDownLatch(1); private final Thread keepAliveThread; private Elasticsearch(Spawner spawner, Node node) { this.spawner = Objects.requireNonNull(spawner); this.node = Objects.requireNonNull(node); this.keepAliveThread = new Thread(() -> { try { keepAliveLatch.await(); } catch (InterruptedException e) { // bail out } }, "elasticsearch[keepAlive/" + Version.CURRENT + "]"); } private void start() throws NodeValidationException { node.start(); keepAliveThread.start(); } private static void shutdown() { if (INSTANCE == null) { return; // never got far enough } var es = INSTANCE; try { es.node.prepareForClose(); IOUtils.close(es.node, es.spawner); if (es.node.awaitClose(10, TimeUnit.SECONDS) == false) { throw new IllegalStateException( "Node didn't stop within 10 seconds. " + "Any outstanding requests or tasks might get killed." ); } } catch (IOException ex) { throw new ElasticsearchException("Failure occurred while shutting down node", ex); } catch (InterruptedException e) { LogManager.getLogger(Elasticsearch.class).warn("Thread got interrupted while waiting for the node to shutdown."); Thread.currentThread().interrupt(); } finally { LoggerContext context = (LoggerContext) LogManager.getContext(false); Configurator.shutdown(context); es.keepAliveLatch.countDown(); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy