1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
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
}
|