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 }