From 7d51d12b5518f29e5e6ae7615e546f0df6dee941 Mon Sep 17 00:00:00 2001 From: Randall Winkhart Date: Mon, 12 Aug 2024 15:53:40 -0400 Subject: [PATCH] Remove old device ID from server in DeviceIDGen --- core/configParser.go | 13 +++++++++++++ core/init.go | 8 +++++++- libmuttonserver.go | 4 +++- sync/client.go | 8 ++++---- sync/common.go | 14 +------------- sync/init.go | 27 ++++++++++++++++++++------- 6 files changed, 48 insertions(+), 26 deletions(-) diff --git a/core/configParser.go b/core/configParser.go index 68f829c..454fc3e 100644 --- a/core/configParser.go +++ b/core/configParser.go @@ -2,6 +2,7 @@ package core import ( "fmt" + "io/fs" "os" "gopkg.in/ini.v1" @@ -49,6 +50,18 @@ func ParseConfig(valuesRequested [][2]string, missingValueError string) []string return config } +// GenDeviceIDList returns a pointer to a slice of all registered device IDs. +// Requires: errorOnFail (set to true to throw an error if the device ID list cannot be generated) +func GenDeviceIDList(errorOnFail bool) *[]fs.DirEntry { + // create a slice of all registered devices + deviceIDList, err := os.ReadDir(ConfigDir + PathSeparator + "devices") + if err != nil && errorOnFail { + fmt.Println(AnsiError + "Failed to read the devices directory: " + err.Error() + AnsiReset) + os.Exit(101) + } + return &deviceIDList +} + // WriteConfig writes the provided key-value pairs under the specified section headers in the libmutton.ini file. // Requires: valuesToWrite (a slice of length 3 arrays each containing a section, a key name, and a value). func WriteConfig(valuesToWrite [][3]string, append bool) { diff --git a/core/init.go b/core/init.go index 6a7f9be..32bc3a8 100644 --- a/core/init.go +++ b/core/init.go @@ -53,7 +53,8 @@ func GpgKeyGen() string { } // DirInit creates the libmutton directories. -func DirInit(preserveOldConfigDir bool) { +// Returns: oldDeviceID (before from before the directory reset). +func DirInit(preserveOldConfigDir bool) string { // create EntryRoot err := os.MkdirAll(EntryRoot, 0700) if err != nil { @@ -61,6 +62,9 @@ func DirInit(preserveOldConfigDir bool) { os.Exit(102) } + // get old device ID before its potential removal + oldDeviceID := (*GenDeviceIDList(false))[0].Name() // errorOnFail set to false to ignore error if device ID directory does not exist (error non-critical for this function) + // remove existing config directory (if it exists and not in append mode) if !preserveOldConfigDir { _, isAccessible := TargetIsFile(ConfigDir, false, 1) @@ -79,4 +83,6 @@ func DirInit(preserveOldConfigDir bool) { fmt.Println(AnsiError + "Failed to create \"" + ConfigDir + "\": " + err.Error() + AnsiReset) os.Exit(102) } + + return oldDeviceID } diff --git a/libmuttonserver.go b/libmuttonserver.go index 7ac3672..1a275ab 100644 --- a/libmuttonserver.go +++ b/libmuttonserver.go @@ -56,7 +56,9 @@ func main() { case "register": // register a new device ID // stdin[0] is expected to be the device ID - _, _ = os.Create(core.ConfigDir + core.PathSeparator + "devices" + core.PathSeparator + stdin[0]) // error ignored; failure unlikely to occur if init was successful; "register" is not a user-facing argument and thus the error would not be visible + // stdin[1] is expected to be the old device ID (for removal) + _, _ = os.Create(core.ConfigDir + core.PathSeparator + "devices" + core.PathSeparator + stdin[0]) // errors ignored; failure unlikely to occur if init was successful; "register" is not a user-facing argument and thus the error would not be visible + _ = os.RemoveAll(core.ConfigDir + core.PathSeparator + "devices" + core.PathSeparator + stdin[1]) // print EntryRoot and bool indicating OS type to stdout for client to store in config fmt.Print(core.EntryRoot + sync.FSSpace + strconv.FormatBool(core.IsWindows)) case "init": diff --git a/sync/client.go b/sync/client.go index 7c89207..eafb714 100644 --- a/sync/client.go +++ b/sync/client.go @@ -129,8 +129,8 @@ func GetSSHOutput(sshClient *ssh.Client, cmd, stdin string) string { // getRemoteDataFromClient returns a map of remote entries to their modification times, a list of remote folders, a list of queued deletions, and the current server&client times as UNIX timestamps. func getRemoteDataFromClient(sshClient *ssh.Client, manualSync bool) (map[string]int64, []string, []string, int64, int64) { // get remote output over SSH - clientDeviceID, _ := os.ReadDir(core.ConfigDir + core.PathSeparator + "devices") - if len(clientDeviceID) == 0 { + deviceIDList := core.GenDeviceIDList(true) + if len(*deviceIDList) == 0 { if manualSync { fmt.Println(joinErrorWithEXE("Sync failed - No device ID found; run \"", " init\" to generate a device ID")) os.Exit(105) @@ -139,7 +139,7 @@ func getRemoteDataFromClient(sshClient *ssh.Client, manualSync bool) (map[string } } clientTime := time.Now().Unix() // get client time now to avoid accuracy issues caused by unpredictable sync time - output := GetSSHOutput(sshClient, "libmuttonserver fetch", clientDeviceID[0].Name()) + output := GetSSHOutput(sshClient, "libmuttonserver fetch", (*deviceIDList)[0].Name()) // split output into slice based on occurrences of FSSpace outputSlice := strings.Split(output, FSSpace) @@ -409,7 +409,7 @@ func ShearRemoteFromClient(sshClient *ssh.Client, targetLocationIncomplete strin func RenameRemoteFromClient(sshClient *ssh.Client, oldLocationIncomplete, newLocationIncomplete string) { RenameLocal(oldLocationIncomplete, newLocationIncomplete, false) // move the target on the local system - deviceIDList := genDeviceIDList() + deviceIDList := core.GenDeviceIDList(true) if len(*deviceIDList) > 0 { // ensure a device ID exists (online mode) // call the server to move the target on the remote system and add the old target to the deletions list GetSSHOutput(sshClient, "libmuttonserver rename", diff --git a/sync/common.go b/sync/common.go index 70264cd..5a5176f 100644 --- a/sync/common.go +++ b/sync/common.go @@ -2,7 +2,6 @@ package sync import ( "fmt" - "io/fs" "os" "strings" @@ -20,17 +19,6 @@ func getModTimes(entryList []string) []int64 { return modList } -// genDeviceIDList returns a pointer to a slice of all registered device IDs. -func genDeviceIDList() *[]fs.DirEntry { - // create a slice of all registered devices - deviceIDList, err := os.ReadDir(core.ConfigDir + core.PathSeparator + "devices") - if err != nil { - fmt.Println(core.AnsiError + "Failed to read the devices directory: " + err.Error() + core.AnsiReset) - os.Exit(101) - } - return &deviceIDList -} - // ShearLocal removes the target file or directory from the local system. // Returns: deviceID (only on client; for use in ShearRemoteFromClient). // If the local system is a server, it will also add the target to the deletions list for all clients (except the requesting client). @@ -42,7 +30,7 @@ func ShearLocal(targetLocationIncomplete, clientDeviceID string) string { onServer = true } - deviceIDList := genDeviceIDList() + deviceIDList := core.GenDeviceIDList(true) // add the sheared target (incomplete, vanity) to the deletions list (if running on a server) if onServer { diff --git a/sync/init.go b/sync/init.go index 098b742..373d1cd 100644 --- a/sync/init.go +++ b/sync/init.go @@ -9,25 +9,38 @@ import ( "time" "github.com/rwinkhart/libmutton/core" + "golang.org/x/crypto/ssh" ) -// DeviceIDGen generates a new client device ID and registers it with the server. +// DeviceIDGen generates a new client device ID and registers it with the server (will replace existing one). // Device IDs are only needed for online synchronization. // Device IDs are guaranteed unique as the current UNIX time is appended to them. // Returns: the remote EntryRoot and OS type indicator. -func DeviceIDGen() (string, string) { +func DeviceIDGen(oldDeviceID string) (string, string) { + // generate new device ID deviceIDPrefix, _ := os.Hostname() deviceIDSuffix := core.StringGen(rand.Intn(32)+48, true, 0.2, true) + "-" + strconv.FormatInt(time.Now().Unix(), 10) - deviceID := deviceIDPrefix + "-" + deviceIDSuffix - _, err := os.Create(core.ConfigDir + core.PathSeparator + "devices" + core.PathSeparator + deviceID) // TODO remove existing device ID file if it exists (from both client and server) + newDeviceID := deviceIDPrefix + "-" + deviceIDSuffix + + // create new device ID file (locally) + _, err := os.Create(core.ConfigDir + core.PathSeparator + "devices" + core.PathSeparator + newDeviceID) // TODO remove existing device ID file if it exists (from both client and server) if err != nil { fmt.Println(core.AnsiError + "Failed to create local device ID file: " + err.Error() + core.AnsiReset) + os.Exit(102) } - // register device ID with server and fetch remote EntryRoot and OS type - //manualSync is true so the user is alerted if device ID registration fails + // register new device ID with server and fetch remote EntryRoot and OS type + // also removes the old device ID file (remotely) + // manualSync is true so the user is alerted if device ID registration fails sshClient, _, _ := GetSSHClient(true) - sshEntryRootSSHIsWindows := strings.Split(GetSSHOutput(sshClient, "libmuttonserver register", deviceID), FSSpace) + defer func(sshClient *ssh.Client) { + err = sshClient.Close() + if err != nil { + fmt.Println(core.AnsiError + "Init failed - Unable to close SSH client: " + err.Error() + core.AnsiReset) + os.Exit(104) + } + }(sshClient) + sshEntryRootSSHIsWindows := strings.Split(GetSSHOutput(sshClient, "libmuttonserver register", newDeviceID+"\n"+oldDeviceID), FSSpace) return sshEntryRootSSHIsWindows[0], sshEntryRootSSHIsWindows[1] }