github.com/turbot/steampipe@v1.7.0-rc.0.0.20240517123944-7cef272d4458/pkg/pluginmanager/state.go (about)

     1  package pluginmanager
     2  
     3  import (
     4  	"encoding/json"
     5  	"log"
     6  	"os"
     7  	"syscall"
     8  
     9  	"github.com/hashicorp/go-plugin"
    10  	filehelpers "github.com/turbot/go-kit/files"
    11  	"github.com/turbot/steampipe/pkg/filepaths"
    12  	pb "github.com/turbot/steampipe/pkg/pluginmanager_service/grpc/proto"
    13  	"github.com/turbot/steampipe/pkg/utils"
    14  )
    15  
    16  const PluginManagerStructVersion = 20220411
    17  
    18  type State struct {
    19  	Protocol        plugin.Protocol `json:"protocol"`
    20  	ProtocolVersion int             `json:"protocol_version"`
    21  	Addr            *pb.SimpleAddr  `json:"addr"`
    22  	Pid             int             `json:"pid"`
    23  	// path to the steampipe executable
    24  	Executable string `json:"executable"`
    25  	// is the plugin manager running
    26  	Running       bool  `json:"-"`
    27  	StructVersion int64 `json:"struct_version"`
    28  }
    29  
    30  func NewState(executable string, reattach *plugin.ReattachConfig) *State {
    31  	return &State{
    32  		Executable:      executable,
    33  		Protocol:        reattach.Protocol,
    34  		ProtocolVersion: reattach.ProtocolVersion,
    35  		Addr:            pb.NewSimpleAddr(reattach.Addr),
    36  		Pid:             reattach.Pid,
    37  		StructVersion:   PluginManagerStructVersion,
    38  	}
    39  }
    40  
    41  func LoadState() (*State, error) {
    42  	// always return empty state
    43  	s := new(State)
    44  	if !filehelpers.FileExists(filepaths.PluginManagerStateFilePath()) {
    45  		log.Printf("[TRACE] plugin manager state file not found")
    46  		return s, nil
    47  	}
    48  
    49  	fileContent, err := os.ReadFile(filepaths.PluginManagerStateFilePath())
    50  	if err != nil {
    51  		return s, err
    52  	}
    53  	err = json.Unmarshal(fileContent, s)
    54  	if err != nil {
    55  		log.Printf("[TRACE] failed to unmarshall plugin manager state file at %s with error %s\n", filepaths.PluginManagerStateFilePath(), err.Error())
    56  		log.Printf("[TRACE] deleting invalid plugin manager state file\n")
    57  		s.delete()
    58  		return s, nil
    59  	}
    60  
    61  	// check is the manager is running - this deletes that state file if it is not running,
    62  	// and set the 'Running' property on the state if it is
    63  	pluginManagerRunning, err := s.verifyRunning()
    64  	if err != nil {
    65  		return s, err
    66  	}
    67  	// save the running status on the state struct
    68  	s.Running = pluginManagerRunning
    69  
    70  	// return error (which may be nil)
    71  	return s, err
    72  }
    73  
    74  func (s *State) Save() error {
    75  	// set struct version
    76  	s.StructVersion = PluginManagerStructVersion
    77  
    78  	content, err := json.MarshalIndent(s, "", "  ")
    79  	if err != nil {
    80  		return err
    81  	}
    82  	return os.WriteFile(filepaths.PluginManagerStateFilePath(), content, 0644)
    83  }
    84  
    85  func (s *State) reattachConfig() *plugin.ReattachConfig {
    86  	return &plugin.ReattachConfig{
    87  		Protocol:        s.Protocol,
    88  		ProtocolVersion: s.ProtocolVersion,
    89  		Addr:            *s.Addr,
    90  		Pid:             s.Pid,
    91  	}
    92  }
    93  
    94  // check whether the plugin manager is running
    95  func (s *State) verifyRunning() (bool, error) {
    96  	pidExists, err := utils.PidExists(s.Pid)
    97  	if err != nil {
    98  		return false, err
    99  	}
   100  	return pidExists, nil
   101  }
   102  
   103  // kill the plugin manager process and delete the state
   104  func (s *State) kill() error {
   105  	// the state file contains the Pid of the daemon process - find and kill the process
   106  	process, err := utils.FindProcess(s.Pid)
   107  	if err != nil {
   108  		return err
   109  	}
   110  	if process == nil {
   111  		log.Printf("[TRACE] tried to kill plugin_manager, but couldn't find process (%d)", s.Pid)
   112  		return nil
   113  	}
   114  	// kill the plugin manager process by sending a SIGTERM (to give it a chance to clean up its children)
   115  	err = process.SendSignal(syscall.SIGTERM)
   116  	if err != nil {
   117  		log.Println("[TRACE] tried to kill plugin_manager, but couldn't send signal to process", err)
   118  		return err
   119  	}
   120  	// delete the state file as we have shutdown the plugin manager
   121  	s.delete()
   122  	return nil
   123  }
   124  
   125  func (s *State) delete() {
   126  	_ = os.Remove(filepaths.PluginManagerStateFilePath())
   127  }