125 lines
2.5 KiB
Go
125 lines
2.5 KiB
Go
|
package app
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"os"
|
||
|
"os/exec"
|
||
|
"os/user"
|
||
|
"strconv"
|
||
|
|
||
|
"git.ophivana.moe/cat/fortify/internal/state"
|
||
|
"git.ophivana.moe/cat/fortify/internal/system"
|
||
|
"git.ophivana.moe/cat/fortify/internal/util"
|
||
|
)
|
||
|
|
||
|
type App struct {
|
||
|
uid int
|
||
|
env []string
|
||
|
command []string
|
||
|
|
||
|
*user.User
|
||
|
}
|
||
|
|
||
|
func (a *App) Run() {
|
||
|
f := a.launchBySudo
|
||
|
m, b := false, false
|
||
|
switch {
|
||
|
case system.MethodFlags[0]: // sudo
|
||
|
case system.MethodFlags[1]: // bare
|
||
|
m, b = true, true
|
||
|
default: // machinectl
|
||
|
m, b = true, false
|
||
|
}
|
||
|
|
||
|
var toolPath string
|
||
|
|
||
|
// dependency checks
|
||
|
const sudoFallback = "Falling back to 'sudo', some desktop integration features may not work"
|
||
|
if m {
|
||
|
if !util.SdBooted() {
|
||
|
fmt.Println("This system was not booted through systemd")
|
||
|
fmt.Println(sudoFallback)
|
||
|
} else if tp, ok := util.Which("machinectl"); !ok {
|
||
|
fmt.Println("Did not find 'machinectl' in PATH")
|
||
|
fmt.Println(sudoFallback)
|
||
|
} else {
|
||
|
toolPath = tp
|
||
|
f = func() []string { return a.launchByMachineCtl(b) }
|
||
|
}
|
||
|
} else if tp, ok := util.Which("sudo"); !ok {
|
||
|
state.Fatal("Did not find 'sudo' in PATH")
|
||
|
} else {
|
||
|
toolPath = tp
|
||
|
}
|
||
|
|
||
|
if system.V.Verbose {
|
||
|
fmt.Printf("Selected launcher '%s' bare=%t\n", toolPath, b)
|
||
|
}
|
||
|
|
||
|
cmd := exec.Command(toolPath, f()...)
|
||
|
cmd.Env = a.env
|
||
|
cmd.Stdin = os.Stdin
|
||
|
cmd.Stdout = os.Stdout
|
||
|
cmd.Stderr = os.Stderr
|
||
|
cmd.Dir = system.V.RunDir
|
||
|
|
||
|
if system.V.Verbose {
|
||
|
fmt.Println("Executing:", cmd)
|
||
|
}
|
||
|
|
||
|
if err := cmd.Start(); err != nil {
|
||
|
state.Fatal("Error starting process:", err)
|
||
|
}
|
||
|
|
||
|
if err := state.SaveProcess(a.Uid, cmd); err != nil {
|
||
|
// process already started, shouldn't be fatal
|
||
|
fmt.Println("Error registering process:", err)
|
||
|
}
|
||
|
|
||
|
var r int
|
||
|
if err := cmd.Wait(); err != nil {
|
||
|
var exitError *exec.ExitError
|
||
|
if !errors.As(err, &exitError) {
|
||
|
state.Fatal("Error running process:", err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if system.V.Verbose {
|
||
|
fmt.Println("Process exited with exit code", r)
|
||
|
}
|
||
|
state.BeforeExit()
|
||
|
os.Exit(r)
|
||
|
}
|
||
|
|
||
|
func New(userName string, args []string) *App {
|
||
|
a := &App{command: args}
|
||
|
|
||
|
if u, err := user.Lookup(userName); err != nil {
|
||
|
if errors.As(err, new(user.UnknownUserError)) {
|
||
|
fmt.Println("unknown user", userName)
|
||
|
} else {
|
||
|
// unreachable
|
||
|
panic(err)
|
||
|
}
|
||
|
|
||
|
// too early for fatal
|
||
|
os.Exit(1)
|
||
|
} else {
|
||
|
a.User = u
|
||
|
}
|
||
|
|
||
|
if u, err := strconv.Atoi(a.Uid); err != nil {
|
||
|
// usually unreachable
|
||
|
panic("uid parse")
|
||
|
} else {
|
||
|
a.uid = u
|
||
|
}
|
||
|
|
||
|
if system.V.Verbose {
|
||
|
fmt.Println("Running as user", a.Username, "("+a.Uid+"),", "command:", a.command)
|
||
|
}
|
||
|
|
||
|
return a
|
||
|
}
|