aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOphestra Umiker <cat@ophivana.moe>2024-09-12 20:53:33 +0900
committerOphestra Umiker <cat@ophivana.moe>2024-09-12 20:53:33 +0900
commitb0aff891661dfa2f438a7264607a4e6e9caef026 (patch)
tree323ccc8c09fee45c29e8e1e79b320a4fe6dc8d77
parent8223a9ee6637495c860aa3531f1db02e8c5bd819 (diff)
app: handle launch method in New function
Signed-off-by: Ophestra Umiker <cat@ophivana.moe>
-rw-r--r--flag.go5
-rw-r--r--internal/app/run.go52
-rw-r--r--internal/app/setup.go55
-rw-r--r--internal/util/early.go12
-rw-r--r--internal/util/std.go16
-rw-r--r--main.go37
6 files changed, 125 insertions, 52 deletions
diff --git a/flag.go b/flag.go
index 8fbdc6b1..aa67908a 100644
--- a/flag.go
+++ b/flag.go
@@ -2,8 +2,6 @@ package main
import (
"flag"
-
- "git.ophivana.moe/cat/fortify/internal/app"
)
var (
@@ -38,9 +36,6 @@ func init() {
flag.BoolVar(&mustDBus, "dbus", false, "Proxy D-Bus connection")
flag.BoolVar(&mustPulse, "pulse", false, "Share PulseAudio socket and cookie")
- flag.BoolVar(&app.LaunchOptions[app.LaunchMethodSudo], "sudo", false, "Use 'sudo' to switch user")
- flag.BoolVar(&app.LaunchOptions[app.LaunchMethodMachineCtl], "machinectl", true, "Use 'machinectl' to switch user")
-
flag.BoolVar(&flagVerbose, "v", false, "Verbose output")
flag.BoolVar(&printVersion, "V", false, "Print version")
}
diff --git a/internal/app/run.go b/internal/app/run.go
index b5eec825..0871e899 100644
--- a/internal/app/run.go
+++ b/internal/app/run.go
@@ -17,15 +17,9 @@ const (
sudoAskPass = "SUDO_ASKPASS"
)
const (
- LaunchMethodSudo = iota
+ LaunchMethodSudo uint8 = iota
+ LaunchMethodBwrap
LaunchMethodMachineCtl
-
- launchOptionLength
-)
-
-var (
- // LaunchOptions is set in main's cli.go
- LaunchOptions [launchOptionLength]bool
)
func (a *App) Run() {
@@ -34,34 +28,20 @@ func (a *App) Run() {
a.AppendEnv(term, t)
}
- commandBuilder := a.commandBuilderSudo
+ var commandBuilder func() (args []string)
- var toolPath string
-
- // dependency checks
- const sudoFallback = "Falling back to 'sudo', some desktop integration features may not work"
- if LaunchOptions[LaunchMethodMachineCtl] && !LaunchOptions[LaunchMethodSudo] { // sudo argument takes priority
- if !util.SdBooted() {
- fmt.Println("This system was not booted through systemd")
- fmt.Println(sudoFallback)
- } else if machineCtlPath, ok := util.Which("machinectl"); !ok {
- fmt.Println("Did not find 'machinectl' in PATH")
- fmt.Println(sudoFallback)
- } else {
- toolPath = machineCtlPath
- commandBuilder = a.commandBuilderMachineCtl
- }
- } else if sudoPath, ok := util.Which("sudo"); !ok {
- state.Fatal("Did not find 'sudo' in PATH")
- } else {
- toolPath = sudoPath
+ switch a.launchOption {
+ case LaunchMethodSudo:
+ commandBuilder = a.commandBuilderSudo
+ case LaunchMethodBwrap:
+ commandBuilder = a.commandBuilderBwrap
+ case LaunchMethodMachineCtl:
+ commandBuilder = a.commandBuilderMachineCtl
+ default:
+ panic("unreachable")
}
- if system.V.Verbose {
- fmt.Printf("Selected launcher '%s'\n", toolPath)
- }
-
- cmd := exec.Command(toolPath, commandBuilder()...)
+ cmd := exec.Command(a.toolPath, commandBuilder()...)
cmd.Env = []string{}
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
@@ -122,6 +102,12 @@ func (a *App) commandBuilderSudo() (args []string) {
return
}
+func (a *App) commandBuilderBwrap() (args []string) {
+ // TODO: build bwrap command
+ state.Fatal("bwrap")
+ panic("unreachable")
+}
+
func (a *App) commandBuilderMachineCtl() (args []string) {
args = make([]string, 0, 9+len(a.env))
diff --git a/internal/app/setup.go b/internal/app/setup.go
index 82765dfb..d8378cfd 100644
--- a/internal/app/setup.go
+++ b/internal/app/setup.go
@@ -9,13 +9,19 @@ import (
"git.ophivana.moe/cat/fortify/internal/state"
"git.ophivana.moe/cat/fortify/internal/system"
+ "git.ophivana.moe/cat/fortify/internal/util"
)
type App struct {
+ launchOptionText string
+
uid int
env []string
command []string
+ launchOption uint8
+ toolPath string
+
enablements state.Enablements
*user.User
@@ -23,6 +29,10 @@ type App struct {
// so don't treat it as if it is
}
+func (a *App) LaunchOption() uint8 {
+ return a.launchOption
+}
+
func (a *App) setEnablement(e state.Enablement) {
if a.enablements.Has(e) {
panic("enablement " + e.String() + " set twice")
@@ -31,8 +41,8 @@ func (a *App) setEnablement(e state.Enablement) {
a.enablements |= e.Mask()
}
-func New(userName string, args []string) *App {
- a := &App{command: args}
+func New(userName string, args []string, launchOptionText string) *App {
+ a := &App{command: args, launchOptionText: launchOptionText}
if u, err := user.Lookup(userName); err != nil {
if errors.As(err, new(user.UnknownUserError)) {
@@ -57,6 +67,47 @@ func New(userName string, args []string) *App {
if system.V.Verbose {
fmt.Println("Running as user", a.Username, "("+a.Uid+"),", "command:", a.command)
+ if util.SdBootedV {
+ fmt.Println("System booted with systemd as init system (PID 1).")
+ }
+ }
+
+ switch a.launchOptionText {
+ case "sudo":
+ a.launchOption = LaunchMethodSudo
+ if sudoPath, ok := util.Which("sudo"); !ok {
+ fmt.Println("Did not find 'sudo' in PATH")
+ os.Exit(1)
+ } else {
+ a.toolPath = sudoPath
+ }
+ case "bubblewrap":
+ a.launchOption = LaunchMethodBwrap
+ if bwrapPath, ok := util.Which("bwrap"); !ok {
+ fmt.Println("Did not find 'bwrap' in PATH")
+ os.Exit(1)
+ } else {
+ a.toolPath = bwrapPath
+ }
+ case "systemd":
+ a.launchOption = LaunchMethodMachineCtl
+ if !util.SdBootedV {
+ fmt.Println("System has not been booted with systemd as init system (PID 1).")
+ os.Exit(1)
+ }
+
+ if machineCtlPath, ok := util.Which("machinectl"); !ok {
+ fmt.Println("Did not find 'machinectl' in PATH")
+ } else {
+ a.toolPath = machineCtlPath
+ }
+ default:
+ fmt.Println("invalid launch method")
+ os.Exit(1)
+ }
+
+ if system.V.Verbose {
+ fmt.Println("Determined launch method to be", a.launchOptionText, "with tool at", a.toolPath)
}
return a
diff --git a/internal/util/early.go b/internal/util/early.go
new file mode 100644
index 00000000..ba7375c7
--- /dev/null
+++ b/internal/util/early.go
@@ -0,0 +1,12 @@
+package util
+
+import "fmt"
+
+var SdBootedV = func() bool {
+ if v, err := SdBooted(); err != nil {
+ fmt.Println("warn: read systemd marker:", err)
+ return false
+ } else {
+ return v
+ }
+}()
diff --git a/internal/util/std.go b/internal/util/std.go
index 5c37a0d7..4f304fb6 100644
--- a/internal/util/std.go
+++ b/internal/util/std.go
@@ -8,7 +8,6 @@ import (
"path"
"git.ophivana.moe/cat/fortify/internal/state"
- "git.ophivana.moe/cat/fortify/internal/system"
)
const (
@@ -22,19 +21,16 @@ const (
)
// SdBooted implements https://www.freedesktop.org/software/systemd/man/sd_booted.html
-func SdBooted() bool {
+func SdBooted() (bool, error) {
_, err := os.Stat(systemdCheckPath)
if err != nil {
- if system.V.Verbose {
- if errors.Is(err, fs.ErrNotExist) {
- fmt.Println("System not booted through systemd")
- } else {
- fmt.Println("Error accessing", systemdCheckPath+":", err.Error())
- }
+ if errors.Is(err, fs.ErrNotExist) {
+ err = nil
}
- return false
+ return false, err
}
- return true
+
+ return true, nil
}
// DiscoverPulseCookie try various standard methods to discover the current user's PulseAudio authentication cookie
diff --git a/main.go b/main.go
index a15dc065..1411b917 100644
--- a/main.go
+++ b/main.go
@@ -7,6 +7,7 @@ import (
"fmt"
"io/fs"
"os"
+ "path"
"strconv"
"syscall"
@@ -15,6 +16,7 @@ import (
"git.ophivana.moe/cat/fortify/internal/app"
"git.ophivana.moe/cat/fortify/internal/state"
"git.ophivana.moe/cat/fortify/internal/system"
+ "git.ophivana.moe/cat/fortify/internal/util"
)
var (
@@ -24,8 +26,19 @@ var (
dbusSession *dbus.Config
dbusSystem *dbus.Config
+
+ launchOptionText string
)
+func init() {
+ methodHelpString := "Method of launching the child process, can be one of \"sudo\", \"bubblewrap\""
+ if util.SdBootedV {
+ methodHelpString += ", \"systemd\""
+ }
+
+ flag.StringVar(&launchOptionText, "method", "sudo", methodHelpString)
+}
+
func tryVersion() {
if printVersion {
fmt.Println(Version)
@@ -44,7 +57,7 @@ func main() {
tryLicense()
system.Retrieve(flagVerbose)
- a = app.New(userName, flag.Args())
+ a = app.New(userName, flag.Args(), launchOptionText)
state.Set(*a.User, a.Command(), a.UID())
// parse D-Bus config file if applicable
@@ -87,6 +100,26 @@ func main() {
state.Fatal("Error creating shared directory:", err)
}
+ if a.LaunchOption() == app.LaunchMethodSudo {
+ // ensure child runtime directory (e.g. `/tmp/fortify.%d/%d.share`)
+ cr := path.Join(system.V.Share, a.Uid+".share")
+ if err := os.Mkdir(cr, 0700); err != nil && !errors.Is(err, fs.ErrExist) {
+ state.Fatal("Error creating child runtime directory:", err)
+ } else {
+ if err = acl.UpdatePerm(cr, a.UID(), acl.Read, acl.Write, acl.Execute); err != nil {
+ state.Fatal("Error preparing child runtime directory:", err)
+ } else {
+ state.RegisterRevertPath(cr)
+ }
+ a.AppendEnv("XDG_RUNTIME_DIR", cr)
+ a.AppendEnv("XDG_SESSION_CLASS", "user")
+ a.AppendEnv("XDG_SESSION_TYPE", "tty")
+ if system.V.Verbose {
+ fmt.Printf("Child runtime data dir '%s' configured\n", cr)
+ }
+ }
+ }
+
// warn about target user home directory ownership
if stat, err := os.Stat(a.HomeDir); err != nil {
if system.V.Verbose {
@@ -117,7 +150,7 @@ func main() {
state.Fatal(fmt.Sprintf("Path '%s' is not a directory", system.V.Runtime))
} else {
if err = acl.UpdatePerm(system.V.Runtime, a.UID(), acl.Execute); err != nil {
- state.Fatal("Error preparing runtime dir:", err)
+ state.Fatal("Error preparing runtime directory:", err)
} else {
state.RegisterRevertPath(system.V.Runtime)
}