mirror of
https://github.com/rwinkhart/MUTN.git
synced 2026-08-28 04:46:41 -04:00
Implement initial SSH data retrieval
This commit is contained in:
+1
-1
@@ -33,7 +33,7 @@ func TempInitCli() {
|
|||||||
// client device ID
|
// client device ID
|
||||||
deviceIDPrefix, _ := os.Hostname()
|
deviceIDPrefix, _ := os.Hostname()
|
||||||
deviceIDSuffix := backend.StringGen(rand.Intn(48)+48, true, 0.2)
|
deviceIDSuffix := backend.StringGen(rand.Intn(48)+48, true, 0.2)
|
||||||
deviceID := deviceIDPrefix + "-" + deviceIDSuffix // TODO do not save in config file, encrypt in config directory
|
deviceID := deviceIDPrefix + "-" + deviceIDSuffix
|
||||||
|
|
||||||
// necessary SSH info
|
// necessary SSH info
|
||||||
sshUser := input("Remote SSH username:")
|
sshUser := input("Remote SSH username:")
|
||||||
|
|||||||
+117
-14
@@ -3,17 +3,15 @@ package sync
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/rwinkhart/MUTN/src/backend"
|
"github.com/rwinkhart/MUTN/src/backend"
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// returns lists of local entries and mod times (two separate lists)
|
// getSSHOutput runs a command over SSH and returns the output
|
||||||
func getLocalData() {
|
// currently only supports password-less key-based authentication TODO add password support, still require key
|
||||||
fileList, _ := WalkEntryDir()
|
func getSSHOutput(cmd string, manualSync bool) string {
|
||||||
fmt.Println(fileList) // TODO placeholder
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunJob runs the SSH sync job
|
|
||||||
func RunJob(manualSync bool) {
|
|
||||||
// get SSH config info, exit if not configured (displaying an error if the sync job was called manually)
|
// get SSH config info, exit if not configured (displaying an error if the sync job was called manually)
|
||||||
var sshUserIPPortIdentity []string
|
var sshUserIPPortIdentity []string
|
||||||
if manualSync {
|
if manualSync {
|
||||||
@@ -22,21 +20,126 @@ func RunJob(manualSync bool) {
|
|||||||
sshUserIPPortIdentity = backend.ReadConfig([]string{"sshUser", "sshIP", "sshPort", "sshIdentity"}, "0")
|
sshUserIPPortIdentity = backend.ReadConfig([]string{"sshUser", "sshIP", "sshPort", "sshIdentity"}, "0")
|
||||||
}
|
}
|
||||||
|
|
||||||
var sshUser, sshIP, sshPort, sshIdentity string
|
var user, ip, port, identity string
|
||||||
for i, key := range sshUserIPPortIdentity {
|
for i, key := range sshUserIPPortIdentity {
|
||||||
switch i {
|
switch i {
|
||||||
case 0:
|
case 0:
|
||||||
sshUser = key
|
user = key
|
||||||
case 1:
|
case 1:
|
||||||
sshIP = key
|
ip = key
|
||||||
case 2:
|
case 2:
|
||||||
sshPort = key
|
port = key
|
||||||
case 3:
|
case 3:
|
||||||
sshIdentity = key
|
identity = key
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(sshUser, sshIP, sshPort, sshIdentity) // TODO placeholder
|
// read and parse private key
|
||||||
|
key, err := os.ReadFile(identity)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(backend.AnsiError+"Sync failed - unable to read private key file:", identity+backend.AnsiReset)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
parsedKey, err := ssh.ParsePrivateKey(key)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(backend.AnsiError+"Sync failed - Unable to parse private key:", identity+backend.AnsiReset)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// configure SSH client
|
||||||
|
sshConfig := &ssh.ClientConfig{
|
||||||
|
User: user,
|
||||||
|
Auth: []ssh.AuthMethod{
|
||||||
|
ssh.PublicKeys(parsedKey),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
sshConfig.HostKeyCallback = ssh.InsecureIgnoreHostKey() // TODO remove
|
||||||
|
|
||||||
|
// connect to SSH server
|
||||||
|
sshClient, err := ssh.Dial("tcp", ip+":"+port, sshConfig)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(backend.AnsiError+"Sync failed - Unable to connect to remote server:", err.Error()+backend.AnsiReset)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
defer sshClient.Close()
|
||||||
|
|
||||||
|
// create a session
|
||||||
|
sshSession, err := sshClient.NewSession()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(backend.AnsiError+"Sync failed - Unable to establish SSH session:", err.Error()+backend.AnsiReset)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// run the provided command
|
||||||
|
output, err := sshSession.CombinedOutput(cmd)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(backend.AnsiError+"Sync failed - Unable to run SSH command:", err.Error()+backend.AnsiReset)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert the output to a string and remove leading/trailing whitespace
|
||||||
|
outputString := string(output)
|
||||||
|
outputString = strings.TrimSpace(outputString)
|
||||||
|
|
||||||
|
return outputString
|
||||||
|
}
|
||||||
|
|
||||||
|
// getRemoteData returns lists of remote entries, mod times, folders, and deletions (four separate lists)
|
||||||
|
func getRemoteData(manualSync bool) ([]string, []int64, []string, []string) {
|
||||||
|
// get remote output over SSH
|
||||||
|
output := getSSHOutput("ls && uname -r", manualSync) // TODO replace cmd with remote server command (e.g. "libmuttonserver fetch")
|
||||||
|
|
||||||
|
// split output into slice based on occurrences of "\x1f"
|
||||||
|
outputSlice := strings.Split(output, "\x1f")
|
||||||
|
|
||||||
|
// re-form the lists
|
||||||
|
entries := strings.Split(outputSlice[0], "\n")
|
||||||
|
modsStrings := strings.Split(outputSlice[1], "\n")
|
||||||
|
folders := strings.Split(outputSlice[2], "\n")
|
||||||
|
deletions := strings.Split(outputSlice[3], "\n")
|
||||||
|
|
||||||
|
// convert the mod times to int64
|
||||||
|
var mods []int64
|
||||||
|
for _, modString := range modsStrings {
|
||||||
|
mod, _ := strconv.ParseInt(modString, 10, 64)
|
||||||
|
mods = append(mods, mod)
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries, mods, folders, deletions
|
||||||
|
}
|
||||||
|
|
||||||
|
// getLocalData returns lists of local entries and mod times (two separate lists)
|
||||||
|
func getLocalData() ([]string, []int64) {
|
||||||
|
// get a list of all entries
|
||||||
|
fileList, _ := WalkEntryDir()
|
||||||
|
|
||||||
|
// get a list of all entry modification times
|
||||||
|
var modList []int64
|
||||||
|
for _, file := range fileList {
|
||||||
|
modTime, _ := os.Stat(backend.EntryRoot + file)
|
||||||
|
modList = append(modList, modTime.ModTime().Unix())
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the lists
|
||||||
|
return fileList, modList
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunJob runs the SSH sync job
|
||||||
|
func RunJob(manualSync bool) {
|
||||||
|
// TODO fetch remote lists
|
||||||
|
remoteEntries, remoteMods, remoteFolders, remoteDeletions := getRemoteData(manualSync)
|
||||||
|
fmt.Println(remoteEntries, remoteMods, remoteFolders, remoteDeletions) // TODO placeholder
|
||||||
|
|
||||||
|
// TODO sync deletions and folders
|
||||||
|
|
||||||
|
// fetch local lists
|
||||||
|
localEntries, localMods := getLocalData()
|
||||||
|
fmt.Println(localEntries, localMods) // TODO placeholder
|
||||||
|
|
||||||
|
// TODO sort both local and remote entry/mod lists to ensure common entries are listed first
|
||||||
|
|
||||||
|
// TODO sync new and updated entries
|
||||||
|
|
||||||
|
// exit program after successful sync
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user