Files
libmutton/src/sync/common.go
T

102 lines
3.9 KiB
Go

package sync
import (
"fmt"
"github.com/rwinkhart/MUTN/src/backend"
"io/fs"
"os"
"path/filepath"
"strings"
)
// WalkEntryDir walks the entry directory and returns lists of all files and directories found (two separate lists)
// initCommand is used to specify to the end user how to generate the entry directory if it does not exists
func WalkEntryDir() ([]string, []string) {
// define file/directory containing slices so that they may be accessed by the anonymous WalkDir function
var fileList []string
var dirList []string
// walk entry directory
_ = filepath.WalkDir(backend.EntryRoot,
func(fullPath string, entry fs.DirEntry, err error) error {
// check for errors encountered while walking directory
if err != nil {
if os.IsNotExist(err) {
fmt.Println(backend.AnsiError+"The entry directory does not exist - run \""+os.Args[0], "init"+"\" to create it"+backend.AnsiReset) // TODO implement init command for libmuttonserver
} else {
// otherwise, print the source of the error
fmt.Println(backend.AnsiError + "An unexpected error occurred while generating the entry list: " + err.Error() + backend.AnsiReset)
}
os.Exit(1)
}
// trim root path from each path before storing
trimmedPath := fullPath[rootLength:]
// create separate slices for entries and directories
if !entry.IsDir() {
fileList = append(fileList, trimmedPath)
} else {
dirList = append(dirList, trimmedPath)
}
return nil
})
return fileList, dirList
}
func getModTimes(entryList []string) []int64 {
// get a list of all entry modification times
var modList []int64
for _, file := range entryList {
modTime, _ := os.Stat(backend.EntryRoot + file)
modList = append(modList, modTime.ModTime().Unix())
}
return modList
}
// Shear removes the target file or directory from the system
// if running on the server, it will also add the target to the deletions list
// if running on the client, it will call the server to add the target to the deletions list
func Shear(targetLocationIncomplete string, deviceID string) {
// get the full targetLocation path and remove the target
targetLocationComplete := backend.TargetLocationFormat(targetLocationIncomplete[1:])
backend.TargetIsFile(targetLocationComplete, true, 0) // needed because os.RemoveAll does not return an error if target does not exist
err := os.RemoveAll(targetLocationComplete)
if err != nil {
fmt.Println(backend.AnsiError + "Failed to remove local target: " + err.Error() + backend.AnsiReset)
os.Exit(1)
}
// read the devices directory
var deviceIDList []os.DirEntry
deviceIDList, err = os.ReadDir(backend.ConfigDir + backend.PathSeparator + "devices")
if err != nil {
fmt.Println(backend.AnsiError + "Failed to read the devices directory: " + err.Error() + backend.AnsiReset)
os.Exit(1)
}
// add the sheared target (incomplete, vanity) to the deletions list
if deviceID != "" { // if running on the server...
for _, device := range deviceIDList {
if device.Name() != deviceID {
_, err = os.Create(backend.ConfigDir + backend.PathSeparator + "deletions" + backend.PathSeparator + device.Name() + "\x1d" + strings.ReplaceAll(targetLocationIncomplete, backend.PathSeparator, "\x1e"))
if err != nil {
// do not print error as there is currently no way of seeing server-side errors
os.Exit(1)
}
}
}
} else { // if running on the client... (online mode determined dynamically in GetSSHOutput, will simply exit if not in online mode)
// determine client device ID (to send to server, avoids creating a deletion file for the client device)
deviceID = deviceIDList[0].Name()
// below: deviceID and targetLocationIncomplete are separated by \x1d, path separators are replaced with \x1e, and spaces are replaced with \x1f
GetSSHOutput("libmuttonserver shear "+deviceID+"\x1d"+strings.ReplaceAll(strings.ReplaceAll(targetLocationIncomplete, backend.PathSeparator, "\x1e"), " ", "\x1f"), false)
}
os.Exit(0)
}