github.com/turbot/steampipe@v1.7.0-rc.0.0.20240517123944-7cef272d4458/pkg/db/db_local/running_info.go (about)

     1  package db_local
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"log"
     7  	"os"
     8  	"os/exec"
     9  	"slices"
    10  	"sort"
    11  
    12  	filehelpers "github.com/turbot/go-kit/files"
    13  	"github.com/turbot/go-kit/helpers"
    14  	"github.com/turbot/steampipe/pkg/constants"
    15  	"github.com/turbot/steampipe/pkg/filepaths"
    16  	"github.com/turbot/steampipe/pkg/utils"
    17  )
    18  
    19  const RunningDBStructVersion = 20220411
    20  
    21  // RunningDBInstanceInfo contains data about the running process and it's credentials
    22  type RunningDBInstanceInfo struct {
    23  	Pid int `json:"pid"`
    24  	// store both resolved and user input listen addresses
    25  	// keep the same 'listen' json tag to maintain backward compatibility
    26  	ResolvedListenAddresses []string          `json:"listen"`
    27  	GivenListenAddresses    []string          `json:"raw_listen"`
    28  	Port                    int               `json:"port"`
    29  	Invoker                 constants.Invoker `json:"invoker"`
    30  	Password                string            `json:"password"`
    31  	User                    string            `json:"user"`
    32  	Database                string            `json:"database"`
    33  	StructVersion           int64             `json:"struct_version"`
    34  }
    35  
    36  func newRunningDBInstanceInfo(cmd *exec.Cmd, listenAddresses []string, port int, databaseName string, password string, invoker constants.Invoker) *RunningDBInstanceInfo {
    37  	resolvedListenAddresses := getListenAddresses(listenAddresses)
    38  
    39  	dbState := &RunningDBInstanceInfo{
    40  		Pid:                     cmd.Process.Pid,
    41  		ResolvedListenAddresses: resolvedListenAddresses,
    42  		GivenListenAddresses:    listenAddresses,
    43  		Port:                    port,
    44  		User:                    constants.DatabaseUser,
    45  		Password:                password,
    46  		Database:                databaseName,
    47  		Invoker:                 invoker,
    48  		StructVersion:           RunningDBStructVersion,
    49  	}
    50  
    51  	return dbState
    52  }
    53  
    54  func getListenAddresses(listenAddresses []string) []string {
    55  	addresses := []string{}
    56  
    57  	if helpers.StringSliceContains(listenAddresses, "localhost") {
    58  		loopAddrs, err := utils.LocalLoopbackAddresses()
    59  		if err != nil {
    60  			return nil
    61  		}
    62  		addresses = loopAddrs
    63  	}
    64  
    65  	if helpers.StringSliceContains(listenAddresses, "*") {
    66  		// remove the * wildcard, we want to replace that with the actual addresses
    67  		listenAddresses = helpers.RemoveFromStringSlice(listenAddresses, "*")
    68  		loopAddrs, err := utils.LocalLoopbackAddresses()
    69  		if err != nil {
    70  			return nil
    71  		}
    72  		publicAddrs, err := utils.LocalPublicAddresses()
    73  		if err != nil {
    74  			return nil
    75  		}
    76  		addresses = append(loopAddrs, publicAddrs...)
    77  	}
    78  
    79  	// now add back the listenAddresses to address arguments where the interface addresses were sent
    80  	addresses = append(addresses, listenAddresses...)
    81  	addresses = helpers.StringSliceDistinct(addresses)
    82  
    83  	// sort locals to the top
    84  	sort.SliceStable(addresses, func(i, j int) bool {
    85  		locals := []string{
    86  			"127.0.0.1",
    87  			"::1",
    88  			"localhost",
    89  		}
    90  		return !helpers.StringSliceContains(locals, addresses[j])
    91  	})
    92  
    93  	return addresses
    94  }
    95  
    96  func (r *RunningDBInstanceInfo) MatchWithGivenListenAddresses(listenAddresses []string) bool {
    97  	// make a clone of the slices - we don't want to modify the original data in the subsequent sort
    98  	left := slices.Clone(r.GivenListenAddresses)
    99  	right := slices.Clone(listenAddresses)
   100  
   101  	// sort both of them
   102  	slices.Sort(left)
   103  	slices.Sort(right)
   104  
   105  	return slices.Equal(left, right)
   106  }
   107  
   108  func (r *RunningDBInstanceInfo) Save() error {
   109  	// set struct version
   110  	r.StructVersion = RunningDBStructVersion
   111  
   112  	content, err := json.MarshalIndent(r, "", "  ")
   113  	if err != nil {
   114  		return err
   115  	}
   116  	return os.WriteFile(filepaths.RunningInfoFilePath(), content, 0644)
   117  }
   118  
   119  func (r *RunningDBInstanceInfo) String() string {
   120  	writeBuffer := bytes.NewBufferString("")
   121  	jsonEncoder := json.NewEncoder(writeBuffer)
   122  
   123  	// redact the password from the string, so that it doesn't get printed
   124  	// this should not affect the state file, since we use a json.Marshal there
   125  	p := r.Password
   126  	r.Password = "XXXX-XXXX-XXXX"
   127  
   128  	jsonEncoder.SetIndent("", "")
   129  	err := jsonEncoder.Encode(r)
   130  	if err != nil {
   131  		log.Printf("[TRACE] Encode failed: %v\n", err)
   132  	}
   133  	r.Password = p
   134  	return writeBuffer.String()
   135  }
   136  
   137  func loadRunningInstanceInfo() (*RunningDBInstanceInfo, error) {
   138  	utils.LogTime("db.loadRunningInstanceInfo start")
   139  	defer utils.LogTime("db.loadRunningInstanceInfo end")
   140  
   141  	if !filehelpers.FileExists(filepaths.RunningInfoFilePath()) {
   142  		return nil, nil
   143  	}
   144  
   145  	fileContent, err := os.ReadFile(filepaths.RunningInfoFilePath())
   146  	if err != nil {
   147  		return nil, err
   148  	}
   149  	var info = new(RunningDBInstanceInfo)
   150  	err = json.Unmarshal(fileContent, info)
   151  	if err != nil {
   152  		log.Printf("[TRACE] failed to unmarshal database state file %s: %s\n", filepaths.RunningInfoFilePath(), err.Error())
   153  		return nil, nil
   154  	}
   155  	return info, nil
   156  }
   157  
   158  func removeRunningInstanceInfo() error {
   159  	return os.Remove(filepaths.RunningInfoFilePath())
   160  }