152 lines
3.6 KiB
Go
152 lines
3.6 KiB
Go
//go:build linux
|
|
|
|
package installer
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
func Install(opts InstallOptions) error {
|
|
if opts.Listen == "" {
|
|
opts.Listen = "127.0.0.1:8765"
|
|
}
|
|
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
installDir := filepath.Join(home, ".local", "share", ServiceName)
|
|
binPath := filepath.Join(installDir, ServiceName)
|
|
configDir := filepath.Join(home, ".config", ServiceName)
|
|
envFile := filepath.Join(configDir, ServiceName+".env")
|
|
unitDir := filepath.Join(home, ".config", "systemd", "user")
|
|
unitFile := filepath.Join(unitDir, ServiceName+".service")
|
|
|
|
allowArgs := buildLinuxAllowArgs(opts.Allow)
|
|
|
|
if opts.DryRun {
|
|
return nil
|
|
}
|
|
|
|
if err := os.MkdirAll(installDir, 0o755); err != nil {
|
|
return err
|
|
}
|
|
if err := os.MkdirAll(configDir, 0o755); err != nil {
|
|
return err
|
|
}
|
|
if err := os.MkdirAll(unitDir, 0o755); err != nil {
|
|
return err
|
|
}
|
|
|
|
exePath, err := os.Executable()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := copySelfAtomic(exePath, binPath, 0o755); err != nil {
|
|
return err
|
|
}
|
|
|
|
env := fmt.Sprintf("# %s environment\nLISTEN=\"%s\"\nALLOW_ARGS=\"%s\"\n", ServiceName, escapeDoubleQuotes(opts.Listen), escapeDoubleQuotes(allowArgs))
|
|
if err := os.WriteFile(envFile, []byte(env), 0o600); err != nil {
|
|
return err
|
|
}
|
|
|
|
unit := `[Unit]
|
|
Description=luxtools-client (local folder opener helper)
|
|
After=graphical-session.target
|
|
PartOf=graphical-session.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
EnvironmentFile=%h/.config/luxtools-client/luxtools-client.env
|
|
Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=%t/bus
|
|
ExecStart=/bin/sh -lc '%h/.local/share/luxtools-client/luxtools-client -listen "$LISTEN" $ALLOW_ARGS'
|
|
Restart=on-failure
|
|
RestartSec=1
|
|
|
|
NoNewPrivileges=true
|
|
PrivateTmp=true
|
|
|
|
[Install]
|
|
WantedBy=graphical-session.target
|
|
`
|
|
if err := os.WriteFile(unitFile, []byte(unit), 0o644); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := runCmd("systemctl", "--user", "daemon-reload"); err != nil {
|
|
return fmt.Errorf("systemctl --user daemon-reload failed: %w", err)
|
|
}
|
|
_ = runCmd("systemctl", "--user", "enable", ServiceName)
|
|
if err := runCmd("systemctl", "--user", "restart", ServiceName); err != nil {
|
|
return fmt.Errorf("systemctl --user restart failed: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func Uninstall(opts UninstallOptions) error {
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
installDir := filepath.Join(home, ".local", "share", ServiceName)
|
|
configDir := filepath.Join(home, ".config", ServiceName)
|
|
unitFile := filepath.Join(home, ".config", "systemd", "user", ServiceName+".service")
|
|
|
|
if opts.DryRun {
|
|
return nil
|
|
}
|
|
|
|
_ = runCmd("systemctl", "--user", "stop", ServiceName)
|
|
_ = runCmd("systemctl", "--user", "disable", ServiceName)
|
|
_ = runCmd("systemctl", "--user", "reset-failed", ServiceName)
|
|
_ = os.Remove(unitFile)
|
|
_ = runCmd("systemctl", "--user", "daemon-reload")
|
|
_ = os.RemoveAll(installDir)
|
|
if !opts.KeepConfig {
|
|
_ = os.RemoveAll(configDir)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func buildLinuxAllowArgs(allowed []string) string {
|
|
var b strings.Builder
|
|
for _, p := range allowed {
|
|
p = strings.TrimSpace(p)
|
|
if p == "" {
|
|
continue
|
|
}
|
|
pEsc := strings.ReplaceAll(p, "\"", "\\\"")
|
|
b.WriteString(" -allow \"")
|
|
b.WriteString(pEsc)
|
|
b.WriteString("\"")
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
func escapeDoubleQuotes(s string) string {
|
|
return strings.ReplaceAll(s, "\"", "\\\"")
|
|
}
|
|
|
|
func runCmd(name string, args ...string) error {
|
|
cmd := exec.Command(name, args...)
|
|
var stderr bytes.Buffer
|
|
cmd.Stderr = &stderr
|
|
cmd.Stdout = io.Discard
|
|
if err := cmd.Run(); err != nil {
|
|
msg := strings.TrimSpace(stderr.String())
|
|
if msg != "" {
|
|
return errors.New(msg)
|
|
}
|
|
return err
|
|
}
|
|
return nil
|
|
}
|