// Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may not // use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, // either express or implied. See the License for the specific language governing // permissions and limitations under the License. // Package main represents the entry point of the agent. package main import ( "os" "os/signal" "path/filepath" "runtime" "runtime/debug" "syscall" "github.com/aws/amazon-ssm-agent/agent/agent" "github.com/aws/amazon-ssm-agent/agent/agentlogstocloudwatch/cloudwatchlogspublisher" "github.com/aws/amazon-ssm-agent/agent/appconfig" "github.com/aws/amazon-ssm-agent/agent/context" "github.com/aws/amazon-ssm-agent/agent/framework/coremanager" "github.com/aws/amazon-ssm-agent/agent/framework/coremodules" "github.com/aws/amazon-ssm-agent/agent/health" "github.com/aws/amazon-ssm-agent/agent/hibernation" "github.com/aws/amazon-ssm-agent/agent/ipc/messagebus" "github.com/aws/amazon-ssm-agent/agent/log" "github.com/aws/amazon-ssm-agent/agent/log/logger" "github.com/aws/amazon-ssm-agent/agent/rebooter" "github.com/aws/amazon-ssm-agent/agent/session/utility" "github.com/aws/amazon-ssm-agent/agent/ssm" "github.com/aws/amazon-ssm-agent/agent/startup" "github.com/aws/amazon-ssm-agent/common/identity/identity" ) const ( activationCodeFlag = "code" activationIDFlag = "id" regionFlag = "region" registerFlag = "register" fingerprintFlag = "fingerprint" similarityThresholdFlag = "similarityThreshold" workerFlag = "worker" ) var ( activationCode, activationID, region string register, clear, force, fpFlag, isWorker bool similarityThreshold int registrationFile = filepath.Join(appconfig.DefaultDataStorePath, "registration") messageBusClient *messagebus.MessageBus process *startup.Processor ) func start(log log.T, shouldCheckHibernation bool) (ssmAgent agent.ISSMAgent, err error) { config, err := appconfig.Config(false) if err != nil { log.Debugf("appconfig could not be loaded - %v", err) return nil, err } selector := identity.NewRuntimeConfigIdentitySelector(log) agentIdentity, err := identity.NewAgentIdentity(log, &config, selector) if err != nil { return nil, err } context := context.Default(log, config, agentIdentity, "[ssm-agent-worker]") //Reset password for default RunAs user if already exists sessionUtil := &utility.SessionUtil{} if err := sessionUtil.ResetPasswordIfDefaultUserExists(context); err != nil { log.Warnf("Reset password failed, %v", err) } //Initializing the health module to send empty health pings to the service. healthModule := health.NewHealthCheck(context, ssm.NewService(context)) hibernateState := hibernation.NewHibernateMode(healthModule, context) messageBusClient = messagebus.NewMessageBus(context) ssmAgent = agent.NewSSMAgent(context, healthModule, hibernateState) go messageBusClient.ProcessTerminationRequest() go messageBusClient.ProcessHealthRequest() // Initialize the startup module during agent start, before agent can potentially enter hibernation mode if !context.AppConfig().Agent.ContainerMode { go func() { process = startup.NewProcessor(context) processErr := process.ModuleExecute() if processErr != nil { log.Errorf("Error occurred during startup of processor: %v", processErr) } }() } if context.AppConfig().Agent.ContainerMode { err = startAgent(ssmAgent, context) return } // Do a health check before starting the agent. // Health check would include creating a health module and sending empty health pings to the service. // If response is positive, start the agent, else retry and eventually back off (hibernate/passive mode). if status, hibernationErr := healthModule.GetAgentState(); shouldCheckHibernation && status == health.Passive { //Starting hibernate mode context.Log().Info("Entering SSM Agent hibernate - ", hibernationErr) go func() { defer func() { if r := recover(); r != nil { context.Log().Errorf("Hibernate panic: %v", r) context.Log().Errorf("Stacktrace:\n%s", debug.Stack()) } }() hibernateState.ExecuteHibernation(context) err = startAgent(ssmAgent, context) }() } else { err = startAgent(ssmAgent, context) } return } func startAgent(ssmAgent agent.ISSMAgent, context context.T) (err error) { cloudwatchPublisher := cloudwatchlogspublisher.NewCloudWatchPublisher(context) coreModules := coremodules.RegisteredCoreModules(context) reboot := &rebooter.SSMRebooter{} var cpm *coremanager.CoreManager if cpm, err = coremanager.NewCoreManager(context, *coreModules, cloudwatchPublisher, reboot); err != nil { context.Log().Errorf("error occurred when starting core manager: %v", err) return } ssmAgent.SetCoreManager(cpm) ssmAgent.Start() return } func blockUntilSignaled(log log.T) { // ssm-agent-worker will rely on the termination channel to be notified when to terminate // the agent worker will listen to OS signals until termination channel is successfully connected // Below channel will handle all machine initiated shutdown/reboot requests. // Set up channel on which to receive signal notifications. // We must use a buffered channel or risk missing the signal // if we're not ready to receive when the signal is sent. c := make(chan os.Signal, 1) // Listening for OS signals is a blocking call. // Only listen to signals that require us to exit. // Otherwise we will continue execution and exit the program. signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGTERM) log.Debug("Listening to os signal until core agent termination channel is connected") select { case s := <-c: log.Info("ssm-agent-worker got signal:", s, " value:", s.Signal) return case <-messageBusClient.GetTerminationChannelConnectedChan(): log.Debug("ssm-agent-worker is connected to core agent termination channel, stopping OS signal listener") } // Clean up OS signal listener signal.Stop(c) close(c) log.Debug("Waiting for termination request from core agent") <-messageBusClient.GetTerminationRequestChan() } // Run as a single process. Used by Unix systems and when running agent from console. func run(log log.T, shouldCheckHibernation bool) { log = log.WithContext("[ssm-agent-worker]") defer func() { // recover in case the agent panics // this should handle some kind of seg fault errors. if msg := recover(); msg != nil { log.Errorf("SSM Agent Worker crashed with message %v!", msg) log.Errorf("%s: %s", msg, debug.Stack()) } }() log.Debugf("Current GoMaxProc value - %v", runtime.GOMAXPROCS(0)) log.WriteEvent(logger.AgentTelemetryMessage, "", logger.AmazonAgentWorkerStartEvent) // run ssm agent agent, err := start(log, shouldCheckHibernation) if err != nil { log.Errorf("error occurred when starting ssm-agent-worker: %v", err) return } blockUntilSignaled(log) agent.Stop() }