Search in sources :

Example 1 with Exec

use of com.aws.greengrass.util.Exec in project aws-greengrass-nucleus by aws-greengrass.

the class GenericExternalService method run.

@SuppressWarnings("PMD.CloseResource")
protected Pair<RunStatus, Exec> run(Topic t, String cmd, IntConsumer background, List<Exec> trackingList, boolean requiresPrivilege) throws InterruptedException {
    if (runWith == null) {
        Optional<RunWith> opt = computeRunWithConfiguration();
        if (!opt.isPresent()) {
            logger.atError().log("Could not determine user/group to run with. Ensure that {} is set for {}", DeviceConfiguration.RUN_WITH_TOPIC, deviceConfiguration.getNucleusComponentName());
            return new Pair<>(RunStatus.Errored, null);
        }
        runWith = opt.get();
        LogEventBuilder logEvent = logger.atDebug().kv("user", runWith.getUser());
        if (runWith.getGroup() != null) {
            logEvent.kv("group", runWith.getGroup());
        }
        if (runWith.getShell() != null) {
            logEvent.kv("shell", runWith.getShell());
        }
        logEvent.log("Saving user information for service execution");
        if (!updateComponentPathOwner()) {
            logger.atError().log("Service artifacts may not be accessible to user");
        }
    }
    final ShellRunner shellRunner = context.get(ShellRunner.class);
    Exec exec;
    try {
        exec = shellRunner.setup(t.getFullName(), cmd, this);
    } catch (IOException e) {
        logger.atError().log("Error setting up to run {}", t.getFullName(), e);
        return new Pair<>(RunStatus.Errored, null);
    }
    if (exec == null) {
        return new Pair<>(RunStatus.NothingDone, null);
    }
    exec = addUser(exec, requiresPrivilege);
    exec = addShell(exec);
    addEnv(exec, t.parent);
    logger.atDebug().setEventType("generic-service-run").log();
    // Track all running processes that we fork
    if (exec.isRunning()) {
        trackingList.add(exec);
    }
    RunStatus ret = shellRunner.successful(exec, t.getFullName(), background, this) ? RunStatus.OK : RunStatus.Errored;
    return new Pair<>(ret, exec);
}
Also used : Exec(com.aws.greengrass.util.Exec) LogEventBuilder(com.aws.greengrass.logging.api.LogEventBuilder) IOException(java.io.IOException) Pair(com.aws.greengrass.util.Pair)

Example 2 with Exec

use of com.aws.greengrass.util.Exec in project aws-greengrass-nucleus by aws-greengrass.

the class GenericExternalService method bootstrap.

/**
 * Run the command under 'bootstrap' and returns the exit code. The timeout can be configured with 'timeout' field
 * in seconds. If not configured, by default, it times out after 2 minutes.
 *
 * @return exit code of process
 * @throws InterruptedException when the command execution is interrupted.
 * @throws TimeoutException     when the command execution times out.
 */
@Override
@SuppressFBWarnings(value = { "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE", "NP_LOAD_OF_KNOWN_NULL_VALUE" }, justification = "Known false-positives")
public synchronized int bootstrap() throws InterruptedException, TimeoutException {
    // this is redundant because all lifecycle processes should have been before calling this method.
    // stopping here again to be safer
    stopAllLifecycleProcesses();
    CountDownLatch timeoutLatch = new CountDownLatch(1);
    AtomicInteger atomicExitCode = new AtomicInteger();
    // run the command at background thread so that the main thread can handle it when it times out
    // note that this could be a foreground process but it requires run() methods, ShellerRunner, and Exec's method
    // signature changes to deal with timeout, so we decided to go with background thread.
    Pair<RunStatus, Exec> pair = run(Lifecycle.LIFECYCLE_BOOTSTRAP_NAMESPACE_TOPIC, exitCode -> {
        atomicExitCode.set(exitCode);
        timeoutLatch.countDown();
    }, lifecycleProcesses);
    try (Exec exec = pair.getRight()) {
        if (exec == null) {
            if (pair.getLeft() == RunStatus.Errored) {
                return 1;
            }
            // no bootstrap command found
            return 0;
        }
        // timeout handling
        int timeoutInSec = Coerce.toInt(config.findOrDefault(DEFAULT_BOOTSTRAP_TIMEOUT_SEC, SERVICE_LIFECYCLE_NAMESPACE_TOPIC, Lifecycle.LIFECYCLE_BOOTSTRAP_NAMESPACE_TOPIC, Lifecycle.TIMEOUT_NAMESPACE_TOPIC));
        boolean completedInTime = timeoutLatch.await(timeoutInSec, TimeUnit.SECONDS);
        if (!completedInTime) {
            String msg = String.format("Bootstrap step timed out after '%d' seconds.", timeoutInSec);
            throw new TimeoutException(msg);
        }
    } catch (IOException e) {
        logger.atError("bootstrap-process-close-error").setCause(e).log("Error closing process at bootstrap step.");
    // No need to return special error code here because the exit code is handled by atomicExitCode.
    }
    return atomicExitCode.get();
}
Also used : Exec(com.aws.greengrass.util.Exec) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) CaseInsensitiveString(com.aws.greengrass.config.CaseInsensitiveString) IOException(java.io.IOException) CountDownLatch(java.util.concurrent.CountDownLatch) TimeoutException(java.util.concurrent.TimeoutException) SuppressFBWarnings(edu.umd.cs.findbugs.annotations.SuppressFBWarnings)

Example 3 with Exec

use of com.aws.greengrass.util.Exec in project aws-greengrass-nucleus by aws-greengrass.

the class GenericExternalService method handleRunScript.

@SuppressWarnings("PMD.CloseResource")
private synchronized void handleRunScript() throws InterruptedException {
    stopAllLifecycleProcesses();
    long startingStateGeneration = getStateGeneration();
    Pair<RunStatus, Exec> result = run(LIFECYCLE_RUN_NAMESPACE_TOPIC, exit -> {
        // the reportStates outside of the callback
        synchronized (this) {
            logger.atInfo().kv(EXIT_CODE, exit).log("Run script exited");
            separateLogger.atInfo().kv(EXIT_CODE, exit).log("Run script exited");
            if (startingStateGeneration == getStateGeneration() && currentOrReportedStateIs(State.RUNNING)) {
                if (exit == 0) {
                    logger.atInfo().setEventType("generic-service-stopping").log("Service finished running");
                    this.requestStop();
                } else {
                    reportState(State.ERRORED);
                }
            }
        }
    }, lifecycleProcesses);
    if (result.getLeft() == RunStatus.NothingDone) {
        reportState(State.FINISHED);
        logger.atInfo().setEventType("generic-service-finished").log("Nothing done");
        return;
    } else if (result.getLeft() == RunStatus.Errored) {
        serviceErrored("Script errored in run");
        return;
    } else if (result.getRight() != null) {
        reportState(State.RUNNING);
        updateSystemResourceLimits();
        systemResourceController.addComponentProcess(this, result.getRight().getProcess());
    }
    Topic timeoutTopic = config.find(SERVICE_LIFECYCLE_NAMESPACE_TOPIC, LIFECYCLE_RUN_NAMESPACE_TOPIC, Lifecycle.TIMEOUT_NAMESPACE_TOPIC);
    Integer timeout = timeoutTopic == null ? null : (Integer) timeoutTopic.getOnce();
    if (timeout != null) {
        Exec processToClose = result.getRight();
        context.get(ScheduledExecutorService.class).schedule(() -> {
            if (processToClose.isRunning()) {
                try {
                    logger.atWarn("service-run-timed-out").log("Service failed to run within timeout, calling close in process");
                    reportState(State.ERRORED);
                    processToClose.close();
                } catch (IOException e) {
                    logger.atError("service-close-error").setCause(e).log("Error closing service after run timed out");
                }
            }
        }, timeout, TimeUnit.SECONDS);
    }
}
Also used : Exec(com.aws.greengrass.util.Exec) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) ScheduledExecutorService(java.util.concurrent.ScheduledExecutorService) IOException(java.io.IOException) Topic(com.aws.greengrass.config.Topic)

Example 4 with Exec

use of com.aws.greengrass.util.Exec in project aws-greengrass-nucleus by aws-greengrass.

the class GenericExternalService method stopProcesses.

@SuppressWarnings("PMD.CloseResource")
private synchronized void stopProcesses(List<Exec> processes) {
    for (Exec e : processes) {
        if (e != null && e.isRunning()) {
            logger.atInfo().log("Shutting down process {}", e);
            try {
                e.close();
                logger.atInfo().log("Shutdown completed for process {}", e);
                processes.remove(e);
            } catch (IOException ex) {
                logger.atWarn().log("Shutdown timed out for process {}", e);
            }
        } else {
            processes.remove(e);
        }
    }
}
Also used : Exec(com.aws.greengrass.util.Exec) IOException(java.io.IOException)

Example 5 with Exec

use of com.aws.greengrass.util.Exec in project aws-greengrass-nucleus by aws-greengrass.

the class ExecIntegTest method GIVEN_windows_exec_with_user_WHEN_set_env_vars_from_multiple_sources_THEN_precedence_is_correct.

@Test
@EnabledOnOs(OS.WINDOWS)
void GIVEN_windows_exec_with_user_WHEN_set_env_vars_from_multiple_sources_THEN_precedence_is_correct() throws IOException, InterruptedException {
    // Check setting default env works
    // setDefaultEnv is static and will persist throughout this test.
    String expectedVal = "Exec default";
    Exec.setDefaultEnv(TEST_ENV_ENTRY, expectedVal);
    try (Exec exec = Platform.getInstance().createNewProcessRunner()) {
        String output = // cd in case test user doesn't have permission to access current directory
        exec.cd("C:\\").withUser(WINDOWS_TEST_UESRNAME).withShell("echo", "%" + TEST_ENV_ENTRY + "%").execAndGetStringOutput();
        assertEquals(expectedVal, output);
    }
    // Set system-level env var via registry. should override the default
    expectedVal = "system env var";
    try (Exec exec = Platform.getInstance().createNewProcessRunner()) {
        String output = exec.withShell("reg", "add", SYSTEM_ENV_REG_KEY, "/f", "/v", TEST_ENV_ENTRY, "/t", "REG_SZ", "/d", expectedVal).execAndGetStringOutput();
        assertThat(output, containsString("completed successfully"));
    }
    try (Exec exec = Platform.getInstance().createNewProcessRunner()) {
        String output = exec.cd("C:\\").withUser(WINDOWS_TEST_UESRNAME).withShell("echo", "%" + TEST_ENV_ENTRY + "%").execAndGetStringOutput();
        assertEquals(expectedVal, output);
    }
    // Set user-level env var via registry. should override the system-level setting
    // FYI: Usually, user-level setting has higher precedence than system-level. PATH is special.
    // User PATH is appended to system-level PATH by windows.
    expectedVal = "user account env var";
    String testUserSid = Advapi32Util.getAccountByName(WINDOWS_TEST_UESRNAME).sidString;
    testUserEnvRegKey = String.format("HKU\\%s\\Environment", testUserSid);
    try (Exec exec = Platform.getInstance().createNewProcessRunner()) {
        String output = exec.cd("C:\\").withUser(WINDOWS_TEST_UESRNAME).withShell("reg", "add", testUserEnvRegKey, "/f", "/v", TEST_ENV_ENTRY, "/t", "REG_SZ", "/d", expectedVal).execAndGetStringOutput();
        assertThat(output, containsString("completed successfully"));
    }
    try (Exec exec = Platform.getInstance().createNewProcessRunner()) {
        String output = exec.cd("C:\\").withUser(WINDOWS_TEST_UESRNAME).withShell("echo", "%" + TEST_ENV_ENTRY + "%").execAndGetStringOutput();
        assertEquals(expectedVal, output);
    }
    // Use setenv, which overrides everything
    expectedVal = "setenv override";
    try (Exec exec = Platform.getInstance().createNewProcessRunner()) {
        String output = exec.cd("C:\\").withUser(WINDOWS_TEST_UESRNAME).setenv(TEST_ENV_ENTRY, expectedVal).withShell("echo", "%" + TEST_ENV_ENTRY + "%").execAndGetStringOutput();
        assertEquals(expectedVal, output);
    }
}
Also used : Exec(com.aws.greengrass.util.Exec) Matchers.containsString(org.hamcrest.Matchers.containsString) EnabledOnOs(org.junit.jupiter.api.condition.EnabledOnOs) Test(org.junit.jupiter.api.Test)

Aggregations

Exec (com.aws.greengrass.util.Exec)20 Test (org.junit.jupiter.api.Test)11 IOException (java.io.IOException)10 Platform (com.aws.greengrass.util.platforms.Platform)5 Path (java.nio.file.Path)4 CountDownLatch (java.util.concurrent.CountDownLatch)4 Topic (com.aws.greengrass.config.Topic)3 AtomicInteger (java.util.concurrent.atomic.AtomicInteger)3 Topics (com.aws.greengrass.config.Topics)2 Context (com.aws.greengrass.dependency.Context)2 DeviceConfiguration (com.aws.greengrass.deployment.DeviceConfiguration)2 SERVICE_UNIQUE_ID_KEY (com.aws.greengrass.ipc.AuthenticationHandler.SERVICE_UNIQUE_ID_KEY)2 LogEventBuilder (com.aws.greengrass.logging.api.LogEventBuilder)2 Logger (com.aws.greengrass.logging.api.Logger)2 GGServiceTestUtil (com.aws.greengrass.testcommons.testutilities.GGServiceTestUtil)2 NucleusPaths (com.aws.greengrass.util.NucleusPaths)2 Pair (com.aws.greengrass.util.Pair)2 File (java.io.File)2 Arrays (java.util.Arrays)2 EnabledOnOs (org.junit.jupiter.api.condition.EnabledOnOs)2