internal/daemon/daemon_linux.go (125 lines of code) (raw):

// Copyright 2024 Google Inc. 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. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License 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. //go:build linux package daemon import ( "context" "fmt" "strings" "github.com/GoogleCloudPlatform/google-guest-agent/internal/run" ) const ( // GuestAgent is the name of the guest agent daemon. GuestAgent = "google-guest-agent" // GuestAgentManager is the name of the guest agent manager daemon. GuestAgentManager = "google-guest-agent-manager" // GuestAgentCompatManager is the name of the guest agent compat manager // daemon. GuestAgentCompatManager = "google-guest-compat-manager" ) func init() { // Client is the client for interacting with systemd. Client = systemdClient{} } // systemdClient is the linux implementation of ClientInterface. type systemdClient struct{} // RestartService restarts a systemd service with the given method. func (systemdClient) RestartService(ctx context.Context, service string, method RestartMethod) error { var methodString string switch method { case Restart: methodString = "restart" case Reload: methodString = "reload" case TryRestart: methodString = "try-restart" case ReloadOrRestart: methodString = "reload-or-restart" case TryReloadOrRestart: methodString = "try-reload-or-restart" default: return fmt.Errorf("invalid restart method: %d", method) } if _, err := run.WithContext(ctx, run.Options{ OutputType: run.OutputNone, Name: "systemctl", Args: []string{methodString, service}, }); err != nil { return fmt.Errorf("failed to %s service %q: %w", methodString, service, err) } return nil } // ReloadDaemon reloads a systemd daemon. func (systemdClient) ReloadDaemon(ctx context.Context, daemon string) error { if _, err := run.WithContext(ctx, run.Options{ OutputType: run.OutputNone, Name: "systemctl", Args: []string{"daemon-reload", daemon}, }); err != nil { return fmt.Errorf("failed to reload daemon %q: %w", daemon, err) } return nil } // CheckUnitExists checks if a systemd unit exists. func (systemdClient) CheckUnitExists(ctx context.Context, unit string) (bool, error) { if !strings.HasSuffix(unit, ".service") { unit = unit + ".service" } _, err := run.WithContext(ctx, run.Options{ OutputType: run.OutputNone, Name: "systemctl", Args: []string{"status", unit}, }) if err != nil { return false, nil } return true, nil } // UnitStatus returns the status of a systemd unit. func (systemdClient) UnitStatus(ctx context.Context, unit string) (ServiceStatus, error) { res, err := run.WithContext(ctx, run.Options{ OutputType: run.OutputStdout, Name: "systemctl", Args: []string{"is-active", unit}, }) if err != nil { return Unknown, fmt.Errorf("failed to get status of unit %q: %w", unit, err) } // Remove newlines from the output - systemctl is-active will always return // a single line with a new line character at the end in case of non error. status := strings.TrimSpace(res.Output) switch status { case "active": return Active, nil case "inactive": return Inactive, nil case "failed": return Failed, nil } return Unknown, nil } func (systemdClient) StopDaemon(ctx context.Context, daemon string) error { if _, err := run.WithContext(ctx, run.Options{ OutputType: run.OutputCombined, Name: "systemctl", Args: []string{"stop", daemon}, }); err != nil { return fmt.Errorf("failed to stop daemon %q: %w", daemon, err) } return nil } func (systemdClient) StartDaemon(ctx context.Context, daemon string) error { if _, err := run.WithContext(ctx, run.Options{ OutputType: run.OutputCombined, Name: "systemctl", Args: []string{"start", daemon}, }); err != nil { return fmt.Errorf("failed to start daemon %q: %w", daemon, err) } return nil } func (systemdClient) EnableService(ctx context.Context, daemon string) error { if _, err := run.WithContext(ctx, run.Options{ OutputType: run.OutputCombined, Name: "systemctl", Args: []string{"enable", daemon}, }); err != nil { return fmt.Errorf("failed to enable daemon %q: %w", daemon, err) } return nil } func (systemdClient) DisableService(ctx context.Context, daemon string) error { if _, err := run.WithContext(ctx, run.Options{ OutputType: run.OutputCombined, Name: "systemctl", Args: []string{"--no-reload", "disable", daemon}, }); err != nil { return fmt.Errorf("failed to disable daemon %q: %w", daemon, err) } return nil }