internal/ps/ps.go (58 lines of code) (raw):

// Copyright 2024 Google LLC // // 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. // Package ps provides a way to find a process in linux without using the ps CLI // tool. package ps import ( "context" "fmt" "os" ) // KillMode is the mode to use when killing a process. type KillMode int const ( // KillModeNoWait is the default mode and does not wait for the process to exit. KillModeNoWait KillMode = iota // KillModeWait waits for the process to exit. KillModeWait ) // Client is the backing (platform specific) implementation of ps operations. var Client ProcessInterface // commonClient is a platform agnostic implementation of the Client interface. type commonClient struct{} // Process describes an OS process. type Process struct { // PID is the process id. PID int // Exe is the path of the processes executable file. Exe string // CommandLine contains the processes executable path and its command // line arguments (honoring the order they were presented when executed). CommandLine []string } // ProcessInterface is the minimum required Ps interface for Guest Agent. type ProcessInterface interface { // FindRegex finds all processes matching the provided regex. FindRegex(exeMatch string) ([]Process, error) // Memory gets the memory usage of the process with the provided PID. // The value returned is the memory usage of the process, in kB. Memory(pid int) (int, error) // CPUUsage gets the CPU usage of the process with the provided PID. CPUUsage(ctx context.Context, pid int) (float64, error) // IsProcessAlive returns true if the process with the given pid is alive. // Only Windows returns an error if it fails to find the process. Error is // returned instead of false to allow the caller to differentiate between // the two cases and implement any fallback logic if required. IsProcessAlive(pid int) (bool, error) // KillProcess kills the process with the given pid. If wait is true, it will // wait for the process to exit, otherwise it will return immediately. KillProcess(pid int, mode KillMode) error FindPid(pid int) (Process, error) } // FindRegex finds all processes with the executable matching the provided // regex. func FindRegex(exeMatch string) ([]Process, error) { return Client.FindRegex(exeMatch) } // Memory gets the memory usage in kB. of the process with the provided PID. func Memory(pid int) (int, error) { return Client.Memory(pid) } // CPUUsage gets the percentage CPU usage of the process with the provided PID. func CPUUsage(ctx context.Context, pid int) (float64, error) { return Client.CPUUsage(ctx, pid) } // IsProcessAlive returns true if the process with the given pid is alive. func IsProcessAlive(pid int) (bool, error) { return Client.IsProcessAlive(pid) } // KillProcess kills the process with the given pid. func KillProcess(pid int, mode KillMode) error { return Client.KillProcess(pid, mode) } // FindPid finds the process with the given pid. func FindPid(pid int) (Process, error) { return Client.FindPid(pid) } // KillProcess kills the process with the given pid. If wait is true, it will // wait for the process to exit, otherwise it will return immediately. In most // cases, it is not required to set wait to true, but in some cases like unit // tests where we do a PID check immediately after the kill, it is required. func (c commonClient) KillProcess(pid int, mode KillMode) error { p, err := os.FindProcess(pid) if err != nil { return fmt.Errorf("failed to find process with pid %d: %w", pid, err) } if err = p.Kill(); err != nil { return fmt.Errorf("failed to kill process with pid %d: %w", pid, err) } if mode == KillModeWait { // We don't care about the process state here. We just want to wait for // the process to exit. _, err = p.Wait() return err } return nil }