mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-11 17:26:57 +02:00
- module github.com/R0m1k3/Loki, cmd/loki, internal/loki (package loki) - LOKI_HOME, LOKI_MODEL_DIRS, LOKI_SERVICE, LOKI_DL_CONNS ; /etc/loki ; units loki-engine / loki-ui ; binaire et aide CLI - updateRepo pointe sur R0m1k3/Loki (l'auto-update ne tirera plus les binaires AJEAN amont) Conservé à l'identique : le domaine ajean.link (service de tunnel amont), les littéraux de migration 0.7.x (migrate_07.go), RELEASE_NOTES.md et LICENSE (historique et licence de l'amont). go build/vet/test : verts.
110 lines
3.7 KiB
Go
110 lines
3.7 KiB
Go
//go:build !windows
|
|
|
|
package nodeclient
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"runtime"
|
|
)
|
|
|
|
// RunService : hors Windows, pas de gestionnaire de services intégré — on tourne
|
|
// simplement en avant-plan (le service est géré par systemd/launchd, cf Install).
|
|
func RunService(cfg Config) error {
|
|
Run(context.Background(), cfg, false)
|
|
return nil
|
|
}
|
|
|
|
// Install installe un service de démarrage automatique : systemd --user (Linux)
|
|
// ou launchd (macOS). Le binaire est copié à un emplacement stable.
|
|
func Install() error {
|
|
self, err := os.Executable()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
dst := InstalledExePath()
|
|
if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil {
|
|
return err
|
|
}
|
|
if abs, _ := filepath.Abs(self); abs != dst {
|
|
if err := copyFile(self, dst); err != nil {
|
|
return fmt.Errorf("copie du binaire: %w", err)
|
|
}
|
|
}
|
|
switch runtime.GOOS {
|
|
case "linux":
|
|
return installSystemd(dst)
|
|
case "darwin":
|
|
return installLaunchd(dst)
|
|
}
|
|
return fmt.Errorf("installation auto non gérée sur %s — lance « loki remote connect » via ton propre gestionnaire de démarrage", runtime.GOOS)
|
|
}
|
|
|
|
func Uninstall() error {
|
|
switch runtime.GOOS {
|
|
case "linux":
|
|
_ = exec.Command("systemctl", "--user", "disable", "--now", "loki-remote.service").Run()
|
|
return os.Remove(filepath.Join(os.Getenv("HOME"), ".config/systemd/user/loki-remote.service"))
|
|
case "darwin":
|
|
p := filepath.Join(os.Getenv("HOME"), "Library/LaunchAgents/link.loki.remote.plist")
|
|
_ = exec.Command("launchctl", "unload", p).Run()
|
|
return os.Remove(p)
|
|
}
|
|
return fmt.Errorf("désinstallation non gérée sur %s", runtime.GOOS)
|
|
}
|
|
|
|
func installSystemd(exe string) error {
|
|
unit := "[Unit]\nDescription=Loki poste distant\nAfter=network-online.target\n\n" +
|
|
"[Service]\nExecStart=" + exe + " remote run\nRestart=always\nRestartSec=5\n\n" +
|
|
"[Install]\nWantedBy=default.target\n"
|
|
dir := filepath.Join(os.Getenv("HOME"), ".config/systemd/user")
|
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
return err
|
|
}
|
|
if err := os.WriteFile(filepath.Join(dir, "loki-remote.service"), []byte(unit), 0o644); err != nil {
|
|
return err
|
|
}
|
|
_ = exec.Command("systemctl", "--user", "daemon-reload").Run()
|
|
if err := exec.Command("systemctl", "--user", "enable", "--now", "loki-remote.service").Run(); err != nil {
|
|
return fmt.Errorf("systemctl --user enable: %w (astuce : « loginctl enable-linger %s » pour survivre à la déconnexion)", err, os.Getenv("USER"))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func installLaunchd(exe string) error {
|
|
plist := `<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
<plist version="1.0"><dict>
|
|
<key>Label</key><string>link.loki.remote</string>
|
|
<key>ProgramArguments</key><array><string>` + exe + `</string><string>remote</string><string>run</string></array>
|
|
<key>RunAtLoad</key><true/>
|
|
<key>KeepAlive</key><true/>
|
|
</dict></plist>
|
|
`
|
|
dir := filepath.Join(os.Getenv("HOME"), "Library/LaunchAgents")
|
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
return err
|
|
}
|
|
p := filepath.Join(dir, "link.loki.remote.plist")
|
|
if err := os.WriteFile(p, []byte(plist), 0o644); err != nil {
|
|
return err
|
|
}
|
|
return exec.Command("launchctl", "load", p).Run()
|
|
}
|
|
|
|
func copyFile(src, dst string) error {
|
|
b, err := os.ReadFile(src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Ré-installation : Linux refuse d'ÉCRASER l'image d'un binaire en cours
|
|
// (ETXTBSY « text file busy ») mais autorise à le DÉLIER — le service vivant
|
|
// garde son inode ouvert, et le nouveau fichier prend la place du nom. Sans ce
|
|
// unlink, « loki remote install » relancé échouait tant que le service tournait.
|
|
_ = os.Remove(dst)
|
|
return os.WriteFile(dst, b, 0o755)
|
|
}
|