mirror of
https://github.com/rwinkhart/libmutton.git
synced 2026-08-27 20:36:29 -04:00
Implement error-specific exit codes
This commit is contained in:
@@ -16,10 +16,10 @@ See the [developer guide](https://github.com/rwinkhart/libmutton/blob/main/wiki/
|
||||
- [x] Only run getSSHClient once to prevent being asked for keyfile password multiple times
|
||||
- [ ] After this, handle all errors in sync/client.go
|
||||
- [ ] Ensure all config files and entry files are created with 0600 permissions
|
||||
- [ ] Add fail-specific error codes
|
||||
- [x] Add error-specific exit codes
|
||||
- [x] Split into separate repos
|
||||
1. libmutton: backend package (rename to core), sync package, libmuttonserver
|
||||
3. MUTN: cli package
|
||||
2. MUTN: cli package
|
||||
#### Release v0.3.0
|
||||
- [ ] Swap to native (cascade) encryption (custom)
|
||||
- [ ] Implement "netpin" (quick-unlock) with new encryption
|
||||
|
||||
@@ -13,3 +13,17 @@ const (
|
||||
AnsiReset = "\033[0m"
|
||||
LibmuttonVersion = "0.2.B" // untagged releases feature a letter suffix corresponding to the eventual release version, e.g "0.2.A" -> "0.2.0", "0.2.B" -> "0.2.1"
|
||||
)
|
||||
|
||||
// numeric error codes legend
|
||||
// 0: no error
|
||||
// 101: read error
|
||||
// 102: write error
|
||||
// 103: sync process error
|
||||
// 104: server connection error
|
||||
// 105: target not found
|
||||
// 106: target already exists
|
||||
// 107: target wrong type (file/directory)
|
||||
// 108: decryption error
|
||||
// 109: encryption error
|
||||
// 110: clipboard error
|
||||
// 111: other error
|
||||
|
||||
@@ -13,7 +13,7 @@ func loadConfig() *ini.File {
|
||||
cfg, err := ini.Load(ConfigPath)
|
||||
if err != nil {
|
||||
fmt.Println(AnsiError + "Failed to load libmutton.ini: " + err.Error() + AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(101)
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
@@ -40,7 +40,7 @@ func ParseConfig(valuesRequested [][2]string, missingValueError string) []string
|
||||
default:
|
||||
fmt.Println(AnsiError + missingValueError + AnsiReset)
|
||||
}
|
||||
os.Exit(1)
|
||||
os.Exit(101)
|
||||
}
|
||||
|
||||
config = append(config, value)
|
||||
@@ -81,6 +81,6 @@ func WriteConfig(valuesToWrite [][3]string, append bool) {
|
||||
err := cfg.SaveTo(ConfigPath)
|
||||
if err != nil {
|
||||
fmt.Println(AnsiError + "Failed to save libmutton.ini: " + err.Error() + AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(102)
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -24,7 +24,7 @@ func CopyArgument(executableName, targetLocation string, field int) {
|
||||
// ensure field is not empty
|
||||
if decryptedEntry[field] == "" {
|
||||
fmt.Println(AnsiError + "Field is empty" + AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(105)
|
||||
}
|
||||
|
||||
if field != 2 {
|
||||
@@ -40,7 +40,7 @@ func CopyArgument(executableName, targetLocation string, field int) {
|
||||
secret = decryptedEntry[2]
|
||||
}
|
||||
|
||||
fmt.Println("TOTP code has been copied to the clipboard - your clipboard will be kept up to date with the current code until this process is closed")
|
||||
fmt.Println("TOTP code has been copied to the clipboard - Your clipboard will be kept up to date with the current code until this process is closed")
|
||||
|
||||
for { // keep field copied to clipboard, refresh on 30-second intervals
|
||||
currentTime := time.Now()
|
||||
@@ -51,7 +51,7 @@ func CopyArgument(executableName, targetLocation string, field int) {
|
||||
}
|
||||
} else {
|
||||
fmt.Println(AnsiError + "Field does not exist in entry" + AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(105)
|
||||
}
|
||||
|
||||
// copy field to clipboard, launch clipboard clearing process
|
||||
@@ -84,7 +84,7 @@ func GenTOTP(secret string, time time.Time, forSteam bool) string {
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(AnsiError + "Error generating TOTP code" + AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(111)
|
||||
}
|
||||
|
||||
return totpToken
|
||||
|
||||
+5
-5
@@ -17,7 +17,7 @@ func copyField(executableName, copySubject string) {
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
fmt.Println(AnsiError + "Failed to copy to clipboard: " + err.Error() + AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(110)
|
||||
}
|
||||
|
||||
// launch clipboard clearing process if executableName is provided
|
||||
@@ -26,8 +26,8 @@ func copyField(executableName, copySubject string) {
|
||||
writeToStdin(cmd, copySubject)
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
fmt.Println(AnsiError + "Failed to launch automated clipboard clearing process - does this libmutton implementation support the \"clipclear\" argument?" + AnsiReset)
|
||||
os.Exit(1)
|
||||
fmt.Println(AnsiError + "Failed to launch automated clipboard clearing process - Does this libmutton implementation support the \"clipclear\" argument?" + AnsiReset)
|
||||
os.Exit(110)
|
||||
}
|
||||
Exit(0) // only exit if clipboard clearing process is launched, otherwise assume continuous clipboard refresh
|
||||
}
|
||||
@@ -41,7 +41,7 @@ func clipClear(oldContents string) {
|
||||
newContents, err := cmd.Output()
|
||||
if err != nil {
|
||||
fmt.Println(AnsiError + "Failed to read clipboard contents: " + err.Error() + AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(110)
|
||||
}
|
||||
|
||||
if oldContents == strings.TrimRight(string(newContents), "\r\n") {
|
||||
@@ -50,7 +50,7 @@ func clipClear(oldContents string) {
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
fmt.Println(AnsiError + "Failed to clear clipboard: " + err.Error() + AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(110)
|
||||
}
|
||||
}
|
||||
os.Exit(0) // use os.Exit instead of core.Exit, as this function runs out of a background subprocess that is invisible to the user (will never appear in GUI/TUI environment)
|
||||
|
||||
+5
-5
@@ -17,7 +17,7 @@ func copyField(executableName, copySubject string) {
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
fmt.Println(AnsiError + "Failed to copy to clipboard: " + err.Error() + AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(110)
|
||||
}
|
||||
|
||||
// launch clipboard clearing process if executableName is provided
|
||||
@@ -26,8 +26,8 @@ func copyField(executableName, copySubject string) {
|
||||
writeToStdin(cmd, copySubject)
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
fmt.Println(AnsiError + "Failed to launch automated clipboard clearing process - does this libmutton implementation support the \"clipclear\" argument?" + AnsiReset)
|
||||
os.Exit(1)
|
||||
fmt.Println(AnsiError + "Failed to launch automated clipboard clearing process - Does this libmutton implementation support the \"clipclear\" argument?" + AnsiReset)
|
||||
os.Exit(110)
|
||||
}
|
||||
Exit(0) // only exit if clipboard clearing process is launched, otherwise assume continuous clipboard refresh
|
||||
}
|
||||
@@ -41,7 +41,7 @@ func clipClear(oldContents string) {
|
||||
newContents, err := cmd.Output()
|
||||
if err != nil {
|
||||
fmt.Println(AnsiError + "Failed to read clipboard contents: " + err.Error() + AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(110)
|
||||
}
|
||||
|
||||
if oldContents == strings.TrimRight(string(newContents), "\r\n") {
|
||||
@@ -50,7 +50,7 @@ func clipClear(oldContents string) {
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
fmt.Println(AnsiError + "Failed to clear clipboard: " + err.Error() + AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(110)
|
||||
}
|
||||
}
|
||||
os.Exit(0) // use os.Exit instead of core.Exit, as this function runs out of a background subprocess that is invisible to the user (will never appear in GUI/TUI environment)
|
||||
|
||||
@@ -20,15 +20,15 @@ func copyField(executableName, copySubject string) {
|
||||
} else if _, envSet = os.LookupEnv("DISPLAY"); envSet {
|
||||
cmd = exec.Command("xclip", "-sel", "c")
|
||||
} else {
|
||||
fmt.Println(AnsiError + "Clipboard platform could not be determined - note that the clipboard does not function in a raw TTY" + AnsiReset)
|
||||
os.Exit(1)
|
||||
fmt.Println(AnsiError + "Clipboard platform could not be determined - Note that the clipboard does not function in a raw TTY" + AnsiReset)
|
||||
os.Exit(110)
|
||||
}
|
||||
|
||||
writeToStdin(cmd, copySubject)
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
fmt.Println(AnsiError + "Failed to copy to clipboard: " + err.Error() + AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(110)
|
||||
}
|
||||
|
||||
// launch clipboard clearing process if executableName is provided
|
||||
@@ -37,8 +37,8 @@ func copyField(executableName, copySubject string) {
|
||||
writeToStdin(cmd, copySubject)
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
fmt.Println(AnsiError + "Failed to launch automated clipboard clearing process - does this libmutton implementation support the \"clipclear\" argument?" + AnsiReset)
|
||||
os.Exit(1)
|
||||
fmt.Println(AnsiError + "Failed to launch automated clipboard clearing process - Does this libmutton implementation support the \"clipclear\" argument?" + AnsiReset)
|
||||
os.Exit(110)
|
||||
}
|
||||
Exit(0) // only exit if clipboard clearing process is launched, otherwise assume continuous clipboard refresh
|
||||
}
|
||||
@@ -58,15 +58,15 @@ func clipClear(oldContents string) {
|
||||
cmdClear = exec.Command("xclip", "-i", "/dev/null", "-sel", "c")
|
||||
cmdPaste = exec.Command("xclip", "-o", "-sel", "c")
|
||||
} else {
|
||||
fmt.Println(AnsiError + "Clipboard platform could not be determined - neither $WAYLAND_DISPLAY nor $DISPLAY are set" + AnsiReset)
|
||||
os.Exit(1)
|
||||
fmt.Println(AnsiError + "Clipboard platform could not be determined - Neither $WAYLAND_DISPLAY nor $DISPLAY are set" + AnsiReset)
|
||||
os.Exit(110)
|
||||
}
|
||||
|
||||
// read current clipboard contents
|
||||
newContents, err := cmdPaste.Output()
|
||||
if err != nil {
|
||||
fmt.Println(AnsiError + "Failed to read clipboard contents: " + err.Error() + AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(110)
|
||||
}
|
||||
|
||||
// clear clipboard if contents have not been modified
|
||||
@@ -74,7 +74,7 @@ func clipClear(oldContents string) {
|
||||
err = cmdClear.Run()
|
||||
if err != nil {
|
||||
fmt.Println(AnsiError + "Failed to clear clipboard: " + err.Error() + AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(110)
|
||||
}
|
||||
}
|
||||
os.Exit(0) // use os.Exit instead of core.Exit, as this function runs out of a background subprocess that is invisible to the user (will never appear in GUI/TUI environment)
|
||||
|
||||
+5
-5
@@ -16,7 +16,7 @@ func copyField(executableName, copySubject string) {
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
fmt.Println(AnsiError + "Failed to copy to clipboard: " + err.Error() + AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(110)
|
||||
}
|
||||
|
||||
// launch clipboard clearing process if executableName is provided
|
||||
@@ -25,8 +25,8 @@ func copyField(executableName, copySubject string) {
|
||||
writeToStdin(cmd, copySubject)
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
fmt.Println(AnsiError + "Failed to launch automated clipboard clearing process - does this libmutton implementation support the \"clipclear\" argument?" + AnsiReset)
|
||||
os.Exit(1)
|
||||
fmt.Println(AnsiError + "Failed to launch automated clipboard clearing process - Does this libmutton implementation support the \"clipclear\" argument?" + AnsiReset)
|
||||
os.Exit(110)
|
||||
}
|
||||
Exit(0) // only exit if clipboard clearing process is launched, otherwise assume continuous clipboard refresh
|
||||
}
|
||||
@@ -40,7 +40,7 @@ func clipClear(oldContents string) {
|
||||
newContents, err := cmd.Output()
|
||||
if err != nil {
|
||||
fmt.Println(AnsiError + "Failed to read clipboard contents: " + err.Error() + AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(110)
|
||||
}
|
||||
|
||||
if oldContents == strings.TrimRight(string(newContents), "\r\n") {
|
||||
@@ -48,7 +48,7 @@ func clipClear(oldContents string) {
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
fmt.Println(AnsiError + "Failed to clear clipboard: " + err.Error() + AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(110)
|
||||
}
|
||||
}
|
||||
os.Exit(0) // use os.Exit instead of core.Exit, as this function runs out of a background subprocess that is invisible to the user (will never appear in GUI/TUI environment)
|
||||
|
||||
+5
-5
@@ -7,7 +7,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// TODO GPG support is a temporary feature - it will be replaced with a different encryption scheme in the future
|
||||
// TODO GPG support is a temporary feature - It will be replaced with a different encryption scheme in the future
|
||||
|
||||
// DecryptGPG decrypts a GPG-encrypted file and returns the contents as a slice of (trimmed) strings.
|
||||
func DecryptGPG(targetLocation string) []string {
|
||||
@@ -18,8 +18,8 @@ func DecryptGPG(targetLocation string) []string {
|
||||
enableVirtualTerminalProcessing()
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(AnsiError + "Failed to decrypt \"" + targetLocation + "\" - ensure it is a valid GPG-encrypted file and that you entered your passphrase correctly" + AnsiReset)
|
||||
os.Exit(1)
|
||||
fmt.Println(AnsiError + "Failed to decrypt \"" + targetLocation + "\" - Ensure it is a valid GPG-encrypted file and that you entered your passphrase correctly" + AnsiReset)
|
||||
os.Exit(108)
|
||||
}
|
||||
outputSlice := strings.Split(string(output), "\n")
|
||||
|
||||
@@ -32,8 +32,8 @@ func EncryptGPG(input []string) []byte {
|
||||
writeToStdin(cmd, strings.Join(input, "\n"))
|
||||
encryptedBytes, err := cmd.Output()
|
||||
if err != nil {
|
||||
fmt.Println(AnsiError + "Failed to encrypt data - ensure that your GPG key is valid and that you have a valid GPG ID set in libmutton.ini" + AnsiReset)
|
||||
os.Exit(1)
|
||||
fmt.Println(AnsiError + "Failed to encrypt data - Ensure that your GPG key is valid and that you have a valid GPG ID set in libmutton.ini" + AnsiReset)
|
||||
os.Exit(109)
|
||||
}
|
||||
return encryptedBytes
|
||||
}
|
||||
|
||||
+4
-4
@@ -46,7 +46,7 @@ func GpgKeyGen() string {
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
fmt.Println(AnsiError + "Failed to generate GPG key: " + err.Error() + AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(111)
|
||||
}
|
||||
|
||||
return "libmutton-" + unixTime + " (gpg-libmutton) <github.com/rwinkhart/libmutton>"
|
||||
@@ -58,7 +58,7 @@ func DirInit(preserveOldConfigDir bool) {
|
||||
err := os.MkdirAll(EntryRoot, 0700)
|
||||
if err != nil {
|
||||
fmt.Println(AnsiError + "Failed to create \"" + EntryRoot + "\":" + err.Error() + AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(102)
|
||||
}
|
||||
|
||||
// remove existing config directory (if it exists and not in append mode)
|
||||
@@ -68,7 +68,7 @@ func DirInit(preserveOldConfigDir bool) {
|
||||
err = os.RemoveAll(ConfigDir)
|
||||
if err != nil {
|
||||
fmt.Println(AnsiError + "Failed to remove existing config directory: " + err.Error() + AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(102)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,6 +77,6 @@ func DirInit(preserveOldConfigDir bool) {
|
||||
err = os.MkdirAll(ConfigDir+PathSeparator+"devices", 0700)
|
||||
if err != nil {
|
||||
fmt.Println(AnsiError + "Failed to create \"" + ConfigDir + "\":" + err.Error() + AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(102)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@ func TargetIsFile(targetLocation string, errorOnFail bool, failCondition uint8)
|
||||
targetInfo, err := os.Stat(targetLocation)
|
||||
if err != nil {
|
||||
if errorOnFail {
|
||||
fmt.Println(AnsiError + "Failed to access \"" + targetLocation + "\" - ensure it exists and has the correct permissions" + AnsiReset)
|
||||
os.Exit(1)
|
||||
fmt.Println(AnsiError + "Failed to access \"" + targetLocation + "\" - Ensure it exists and has the correct permissions" + AnsiReset)
|
||||
os.Exit(105)
|
||||
} else {
|
||||
return false, false
|
||||
}
|
||||
@@ -27,13 +27,13 @@ func TargetIsFile(targetLocation string, errorOnFail bool, failCondition uint8)
|
||||
if targetInfo.IsDir() {
|
||||
if errorOnFail && failCondition == 2 {
|
||||
fmt.Println(AnsiError + "\"" + targetLocation + "\" is a directory" + AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(107)
|
||||
}
|
||||
return false, true
|
||||
} else {
|
||||
if errorOnFail && failCondition == 1 {
|
||||
fmt.Println(AnsiError + "\"" + targetLocation + "\" is a file" + AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(107)
|
||||
}
|
||||
return true, true
|
||||
}
|
||||
@@ -45,7 +45,7 @@ func WriteEntry(targetLocation string, entryData []string, verifyEntryDoesNotExi
|
||||
_, isAccessible := TargetIsFile(targetLocation, false, 0)
|
||||
if isAccessible {
|
||||
fmt.Println(AnsiError + "Target location already exists" + AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(106)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ func WriteEntry(targetLocation string, entryData []string, verifyEntryDoesNotExi
|
||||
err := os.WriteFile(targetLocation, encryptedBytes, 0600)
|
||||
if err != nil {
|
||||
fmt.Println(AnsiError + "Failed to write to file: " + err.Error() + AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(102)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ func writeToStdin(cmd *exec.Cmd, input string) {
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
fmt.Println(AnsiError + "Failed to access stdin for system command: " + err.Error() + AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(111)
|
||||
}
|
||||
|
||||
go func() {
|
||||
@@ -78,7 +78,7 @@ func CreateTempFile() *os.File {
|
||||
tempFile, err := os.CreateTemp("", "*.markdown")
|
||||
if err != nil {
|
||||
fmt.Println(AnsiError + "Failed to create temporary file: " + err.Error() + AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(102)
|
||||
}
|
||||
return tempFile
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ require (
|
||||
github.com/pkg/sftp v1.13.6
|
||||
github.com/pquerna/otp v1.4.1-0.20231130234153-3357de7c0481
|
||||
golang.org/x/crypto v0.26.0
|
||||
golang.org/x/term v0.23.0
|
||||
gopkg.in/ini.v1 v1.67.0
|
||||
)
|
||||
|
||||
@@ -14,5 +15,4 @@ require (
|
||||
github.com/boombuler/barcode v1.0.2 // indirect
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
golang.org/x/sys v0.23.0 // indirect
|
||||
golang.org/x/term v0.23.0 // indirect
|
||||
)
|
||||
|
||||
+4
-4
@@ -40,18 +40,18 @@ func main() {
|
||||
case "rename":
|
||||
// move an entry to a new location before using fallthrough to add its previous iteration to the deletions directory
|
||||
// stdin[0] is evaluated after fallthrough
|
||||
// stdin[1] is expected to be the OLD incomplete target location with FSPath representing path separators - always pass in UNIX format
|
||||
// stdin[2] is expected to be the NEW incomplete target location with FSPath representing path separators - always pass in UNIX format
|
||||
// stdin[1] is expected to be the OLD incomplete target location with FSPath representing path separators - Always pass in UNIX format
|
||||
// stdin[2] is expected to be the NEW incomplete target location with FSPath representing path separators - Always pass in UNIX format
|
||||
sync.RenameLocal(strings.ReplaceAll(stdin[1], sync.FSPath, "/"), strings.ReplaceAll(stdin[2], sync.FSPath, "/"), true)
|
||||
fallthrough // fallthrough to add the old entry to the deletions directory
|
||||
case "shear":
|
||||
// shear an entry from the server and add it to the deletions directory
|
||||
// stdin[0] is expected to be the device ID
|
||||
// stdin[1] is expected to be the incomplete target location with FSPath representing path separators - always pass in UNIX format
|
||||
// stdin[1] is expected to be the incomplete target location with FSPath representing path separators - Always pass in UNIX format
|
||||
sync.ShearLocal(strings.ReplaceAll(stdin[1], sync.FSPath, "/"), stdin[0])
|
||||
case "addfolder":
|
||||
// add a new folder to the server
|
||||
// stdin[0] is expected to be the incomplete target location with FSPath representing path separators - always pass in UNIX format
|
||||
// stdin[0] is expected to be the incomplete target location with FSPath representing path separators - Always pass in UNIX format
|
||||
sync.AddFolderLocal(strings.ReplaceAll(stdin[0], sync.FSPath, "/"))
|
||||
case "register":
|
||||
// register a new device ID
|
||||
|
||||
+21
-21
@@ -27,7 +27,7 @@ func GetSSHClient(manualSync bool) (*ssh.Client, string, bool) {
|
||||
var sshUserConfig []string
|
||||
var missingValueError string
|
||||
if manualSync {
|
||||
missingValueError = joinErrorWithEXE("SSH settings not configured - run \"", " init\" to configure")
|
||||
missingValueError = joinErrorWithEXE("SSH settings not configured - Run \"", " init\" to configure")
|
||||
} else {
|
||||
missingValueError = "0"
|
||||
}
|
||||
@@ -57,8 +57,8 @@ func GetSSHClient(manualSync bool) (*ssh.Client, string, bool) {
|
||||
// read private key
|
||||
key, err := os.ReadFile(keyFile)
|
||||
if err != nil {
|
||||
fmt.Println(core.AnsiError+"Sync failed - unable to read private key file:", keyFile+core.AnsiReset)
|
||||
os.Exit(1)
|
||||
fmt.Println(core.AnsiError+"Sync failed - Unable to read private key file:", keyFile+core.AnsiReset)
|
||||
os.Exit(101)
|
||||
}
|
||||
|
||||
// parse private key
|
||||
@@ -70,14 +70,14 @@ func GetSSHClient(manualSync bool) (*ssh.Client, string, bool) {
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Println(core.AnsiError+"Sync failed - Unable to parse private key:", keyFile+core.AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(101)
|
||||
}
|
||||
|
||||
// read known hosts file
|
||||
hostKeyCallback, err := knownhosts.New(core.Home + core.PathSeparator + ".ssh" + core.PathSeparator + "known_hosts")
|
||||
if err != nil {
|
||||
fmt.Println(core.AnsiError + "Sync failed - Unable to read known hosts file:" + err.Error() + core.AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(101)
|
||||
}
|
||||
|
||||
// configure SSH client
|
||||
@@ -93,7 +93,7 @@ func GetSSHClient(manualSync bool) (*ssh.Client, string, bool) {
|
||||
sshClient, err := ssh.Dial("tcp", ip+":"+port, sshConfig)
|
||||
if err != nil {
|
||||
fmt.Println(core.AnsiError+"Sync failed - Unable to connect to remote server:", err.Error()+core.AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(104)
|
||||
}
|
||||
|
||||
return sshClient, entryRoot, isWindows
|
||||
@@ -105,7 +105,7 @@ func GetSSHOutput(sshClient *ssh.Client, cmd, stdin string) string {
|
||||
sshSession, err := sshClient.NewSession()
|
||||
if err != nil {
|
||||
fmt.Println(core.AnsiError+"Sync failed - Unable to establish SSH session:", err.Error()+core.AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(104)
|
||||
}
|
||||
|
||||
// provide stdin data for session
|
||||
@@ -116,7 +116,7 @@ func GetSSHOutput(sshClient *ssh.Client, cmd, stdin string) string {
|
||||
output, err = sshSession.CombinedOutput(cmd)
|
||||
if err != nil {
|
||||
fmt.Println(core.AnsiError+"Sync failed - Unable to run SSH command:", err.Error()+core.AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(103)
|
||||
}
|
||||
|
||||
// convert the output to a string and remove leading/trailing whitespace
|
||||
@@ -133,7 +133,7 @@ func getRemoteDataFromClient(sshClient *ssh.Client, manualSync bool) (map[string
|
||||
if len(clientDeviceID) == 0 {
|
||||
if manualSync {
|
||||
fmt.Println(joinErrorWithEXE("Sync failed - No device ID found; run \"", " init\" to generate a device ID"))
|
||||
os.Exit(1)
|
||||
os.Exit(105)
|
||||
} else {
|
||||
core.Exit(0) // exit silently if the sync job was called automatically, as the user may just be in offline mode
|
||||
}
|
||||
@@ -147,7 +147,7 @@ func getRemoteDataFromClient(sshClient *ssh.Client, manualSync bool) (map[string
|
||||
// parse output/re-form lists
|
||||
if len(outputSlice) != 5 { // ensure information from server is complete
|
||||
fmt.Println(core.AnsiError + "Sync failed - Unable to fetch remote data; server returned an unexpected response" + core.AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(103)
|
||||
}
|
||||
serverTime, _ := strconv.ParseInt(outputSlice[0], 10, 64)
|
||||
entries := strings.Split(outputSlice[1], FSMisc)[1:]
|
||||
@@ -204,7 +204,7 @@ func sftpSync(sshClient *ssh.Client, sshEntryRoot string, sshIsWindows bool, dow
|
||||
sftpClient, err := sftp.NewClient(sshClient)
|
||||
if err != nil {
|
||||
fmt.Println(core.AnsiError+"Sync failed - Unable to establish SFTP session:", err.Error()+core.AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(104)
|
||||
}
|
||||
defer sftpClient.Close()
|
||||
|
||||
@@ -223,7 +223,7 @@ func sftpSync(sshClient *ssh.Client, sshEntryRoot string, sshIsWindows bool, dow
|
||||
fileInfo, err = sftpClient.Stat(remoteEntryFullPath)
|
||||
if err != nil {
|
||||
fmt.Println(core.AnsiError+"Sync failed - Unable to get remote file info (mod time):", err.Error()+core.AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(101)
|
||||
}
|
||||
modTime := fileInfo.ModTime()
|
||||
|
||||
@@ -232,7 +232,7 @@ func sftpSync(sshClient *ssh.Client, sshEntryRoot string, sshIsWindows bool, dow
|
||||
remoteFile, err = sftpClient.Open(remoteEntryFullPath)
|
||||
if err != nil {
|
||||
fmt.Println(core.AnsiError+"Sync failed - Unable to open remote file:", err.Error()+core.AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(101)
|
||||
}
|
||||
|
||||
// store path to local entry
|
||||
@@ -243,14 +243,14 @@ func sftpSync(sshClient *ssh.Client, sshEntryRoot string, sshIsWindows bool, dow
|
||||
localFile, err = os.Create(localEntryFullPath)
|
||||
if err != nil {
|
||||
fmt.Println(core.AnsiError+"Sync failed - Unable to create local file:", err.Error()+core.AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(102)
|
||||
}
|
||||
|
||||
// download the file
|
||||
_, err = remoteFile.WriteTo(localFile)
|
||||
if err != nil {
|
||||
fmt.Println(core.AnsiError+"Sync failed - Unable to download remote file:", err.Error()+core.AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(103)
|
||||
}
|
||||
|
||||
// close the files
|
||||
@@ -280,7 +280,7 @@ func sftpSync(sshClient *ssh.Client, sshEntryRoot string, sshIsWindows bool, dow
|
||||
fileInfo, err = os.Stat(localEntryFullPath)
|
||||
if err != nil {
|
||||
fmt.Println(core.AnsiError+"Sync failed - Unable to get local file info (mod time):", err.Error()+core.AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(101)
|
||||
}
|
||||
modTime := fileInfo.ModTime()
|
||||
|
||||
@@ -289,7 +289,7 @@ func sftpSync(sshClient *ssh.Client, sshEntryRoot string, sshIsWindows bool, dow
|
||||
localFile, err = os.Open(localEntryFullPath)
|
||||
if err != nil {
|
||||
fmt.Println(core.AnsiError+"Sync failed - Unable to open local file:", err.Error()+core.AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(101)
|
||||
}
|
||||
|
||||
// store path to remote entry
|
||||
@@ -300,14 +300,14 @@ func sftpSync(sshClient *ssh.Client, sshEntryRoot string, sshIsWindows bool, dow
|
||||
remoteFile, err = sftpClient.Create(remoteEntryFullPath)
|
||||
if err != nil {
|
||||
fmt.Println(core.AnsiError+"Sync failed - Unable to create remote file ("+remoteEntryFullPath+"):", err.Error()+core.AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(102)
|
||||
}
|
||||
|
||||
// upload the file
|
||||
_, err = localFile.WriteTo(remoteFile)
|
||||
if err != nil {
|
||||
fmt.Println(core.AnsiError+"Sync failed - Unable to upload local file:", err.Error()+core.AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(103)
|
||||
}
|
||||
|
||||
// close the files
|
||||
@@ -432,8 +432,8 @@ func folderSync(folders []string) {
|
||||
if !isFile && !isAccessible {
|
||||
os.MkdirAll(folderFullPath, 0700)
|
||||
} else if isFile {
|
||||
fmt.Println(core.AnsiError + "Sync failed - Failed to create folder \"" + folder + "\" - a file with the same name already exists" + core.AnsiReset)
|
||||
os.Exit(1)
|
||||
fmt.Println(core.AnsiError + "Sync failed - Failed to create folder \"" + folder + "\" - A file with the same name already exists" + core.AnsiReset)
|
||||
os.Exit(106)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-7
@@ -26,7 +26,7 @@ func genDeviceIDList() *[]fs.DirEntry {
|
||||
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(1)
|
||||
os.Exit(101)
|
||||
}
|
||||
return &deviceIDList
|
||||
}
|
||||
@@ -52,7 +52,7 @@ func ShearLocal(targetLocationIncomplete, clientDeviceID string) string {
|
||||
if err != nil {
|
||||
// do not print error as there is currently no way of seeing server-side errors
|
||||
// failure to add the target to the deletions list will exit the program and result in a client re-uploading the target (non-critical)
|
||||
os.Exit(1)
|
||||
os.Exit(102)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,7 +66,7 @@ func ShearLocal(targetLocationIncomplete, clientDeviceID string) string {
|
||||
err := os.RemoveAll(targetLocationComplete)
|
||||
if err != nil {
|
||||
fmt.Println(core.AnsiError + "Failed to remove local target: " + err.Error() + core.AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(102)
|
||||
}
|
||||
|
||||
if !onServer && len(*deviceIDList) > 0 { // return the device ID if running on the client and a device ID exists (online mode)
|
||||
@@ -92,13 +92,13 @@ func RenameLocal(oldLocationIncomplete, newLocationIncomplete string, verifyOldL
|
||||
_, isAccessible := core.TargetIsFile(newLocation, false, 0)
|
||||
if isAccessible {
|
||||
fmt.Println(core.AnsiError + "\"" + newLocation + "\" already exists" + core.AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(106)
|
||||
}
|
||||
|
||||
// rename oldLocation to newLocation
|
||||
err := os.Rename(oldLocation, newLocation)
|
||||
if err != nil {
|
||||
fmt.Println(core.AnsiError + "Failed to rename - does the target containing directory exist?" + core.AnsiReset)
|
||||
fmt.Println(core.AnsiError + "Failed to rename - Does the target containing directory exist?" + core.AnsiReset)
|
||||
}
|
||||
|
||||
// do not exit program, as this function is used as part of RenameRemoteFromClient
|
||||
@@ -113,10 +113,10 @@ func AddFolderLocal(targetLocationIncomplete string) {
|
||||
if err != nil {
|
||||
if os.IsExist(err) {
|
||||
fmt.Println(core.AnsiError + "Directory already exists" + core.AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(106)
|
||||
} else {
|
||||
fmt.Println(core.AnsiError + "Failed to create directory: " + err.Error() + core.AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(102)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -25,12 +25,12 @@ func WalkEntryDir() ([]string, []string) {
|
||||
// check for errors encountered while walking directory
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
fmt.Println(core.AnsiError+"The entry directory does not exist - run \""+os.Args[0], "init"+"\" to create it"+core.AnsiReset)
|
||||
fmt.Println(core.AnsiError+"The entry directory does not exist - Run \""+os.Args[0], "init"+"\" to create it"+core.AnsiReset)
|
||||
} else {
|
||||
// otherwise, print the source of the error
|
||||
fmt.Println(core.AnsiError + "An unexpected error occurred while generating the entry list: " + err.Error() + core.AnsiReset)
|
||||
}
|
||||
os.Exit(1)
|
||||
os.Exit(111)
|
||||
}
|
||||
|
||||
// trim root path from each path before storing
|
||||
|
||||
+2
-2
@@ -26,12 +26,12 @@ func WalkEntryDir() ([]string, []string) {
|
||||
// check for errors encountered while walking directory
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
fmt.Println(joinErrorWithEXE("The entry directory does not exist - run \"", " init"+"\" to create it"))
|
||||
fmt.Println(joinErrorWithEXE("The entry directory does not exist - Run \"", " init"+"\" to create it"))
|
||||
} else {
|
||||
// otherwise, print the source of the error
|
||||
fmt.Println(core.AnsiError + "An unexpected error occurred while generating the entry list: " + err.Error() + core.AnsiReset)
|
||||
}
|
||||
os.Exit(1)
|
||||
os.Exit(111)
|
||||
}
|
||||
|
||||
// trim root path from each path before storing and replace backslashes with forward slashes
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ func GetRemoteDataFromServer(clientDeviceID string) {
|
||||
deletionsList, err := os.ReadDir(core.ConfigDir + core.PathSeparator + "deletions")
|
||||
if err != nil {
|
||||
fmt.Println(core.AnsiError + "Failed to read the deletions directory: " + err.Error() + core.AnsiReset)
|
||||
os.Exit(1)
|
||||
os.Exit(101)
|
||||
}
|
||||
|
||||
// print the current UNIX timestamp to stdout
|
||||
|
||||
Reference in New Issue
Block a user