diff options
Diffstat (limited to 'internal/app')
| -rw-r--r-- | internal/app/dbus.go | 22 | ||||
| -rw-r--r-- | internal/app/ensure.go | 62 | ||||
| -rw-r--r-- | internal/app/launch.go | 4 | ||||
| -rw-r--r-- | internal/app/pulse.go | 26 | ||||
| -rw-r--r-- | internal/app/run.go | 22 | ||||
| -rw-r--r-- | internal/app/setup.go | 49 | ||||
| -rw-r--r-- | internal/app/wayland.go | 10 | ||||
| -rw-r--r-- | internal/app/x.go | 7 |
8 files changed, 149 insertions, 53 deletions
diff --git a/internal/app/dbus.go b/internal/app/dbus.go index 60cf3f51..06523e88 100644 --- a/internal/app/dbus.go +++ b/internal/app/dbus.go @@ -3,6 +3,7 @@ package app import ( "errors" "fmt" + "git.ophivana.moe/cat/fortify/internal/final" "os" "path" "strconv" @@ -10,7 +11,6 @@ import ( "git.ophivana.moe/cat/fortify/dbus" "git.ophivana.moe/cat/fortify/internal/acl" "git.ophivana.moe/cat/fortify/internal/state" - "git.ophivana.moe/cat/fortify/internal/system" "git.ophivana.moe/cat/fortify/internal/util" "git.ophivana.moe/cat/fortify/internal/verbose" ) @@ -32,7 +32,7 @@ func (a *App) ShareDBus(dse, dsg *dbus.Config, log bool) { var binPath string var sessionBus, systemBus [2]string - target := path.Join(system.V.Share, strconv.Itoa(os.Getpid())) + target := path.Join(a.sharePath, strconv.Itoa(os.Getpid())) sessionBus[1] = target + ".bus" systemBus[1] = target + ".system-bus" dbusAddress = [2]string{ @@ -41,7 +41,7 @@ func (a *App) ShareDBus(dse, dsg *dbus.Config, log bool) { } if b, ok := util.Which("xdg-dbus-proxy"); !ok { - state.Fatal("D-Bus: Did not find 'xdg-dbus-proxy' in PATH") + final.Fatal("D-Bus: Did not find 'xdg-dbus-proxy' in PATH") } else { binPath = b } @@ -69,7 +69,7 @@ func (a *App) ShareDBus(dse, dsg *dbus.Config, log bool) { verbose.Println("D-Bus: sealing system proxy", dsg.Args(systemBus)) } if err := p.Seal(dse, dsg); err != nil { - state.Fatal("D-Bus: invalid config when sealing proxy,", err) + final.Fatal("D-Bus: invalid config when sealing proxy,", err) } ready := make(chan bool, 1) @@ -80,7 +80,7 @@ func (a *App) ShareDBus(dse, dsg *dbus.Config, log bool) { verbose.Printf("Starting system bus proxy '%s' for address '%s'\n", dbusAddress[1], systemBus[0]) } if err := p.Start(&ready); err != nil { - state.Fatal("D-Bus: error starting proxy,", err) + final.Fatal("D-Bus: error starting proxy,", err) } verbose.Println("D-Bus proxy launch:", p) @@ -97,24 +97,24 @@ func (a *App) ShareDBus(dse, dsg *dbus.Config, log bool) { }() // register early to enable Fatal cleanup - state.RegisterDBus(p, &done) + final.RegisterDBus(p, &done) if !<-ready { - state.Fatal("D-Bus: proxy did not start correctly") + final.Fatal("D-Bus: proxy did not start correctly") } a.AppendEnv(dbusSessionBusAddress, dbusAddress[0]) if err := acl.UpdatePerm(sessionBus[1], a.UID(), acl.Read, acl.Write); err != nil { - state.Fatal(fmt.Sprintf("Error preparing D-Bus session proxy '%s':", dbusAddress[0]), err) + final.Fatal(fmt.Sprintf("Error preparing D-Bus session proxy '%s':", dbusAddress[0]), err) } else { - state.RegisterRevertPath(sessionBus[1]) + final.RegisterRevertPath(sessionBus[1]) } if dsg != nil { a.AppendEnv(dbusSystemBusAddress, dbusAddress[1]) if err := acl.UpdatePerm(systemBus[1], a.UID(), acl.Read, acl.Write); err != nil { - state.Fatal(fmt.Sprintf("Error preparing D-Bus system proxy '%s':", dbusAddress[1]), err) + final.Fatal(fmt.Sprintf("Error preparing D-Bus system proxy '%s':", dbusAddress[1]), err) } else { - state.RegisterRevertPath(systemBus[1]) + final.RegisterRevertPath(systemBus[1]) } } verbose.Printf("Session bus proxy '%s' for address '%s' configured\n", dbusAddress[0], sessionBus[0]) diff --git a/internal/app/ensure.go b/internal/app/ensure.go new file mode 100644 index 00000000..9ab63ff2 --- /dev/null +++ b/internal/app/ensure.go @@ -0,0 +1,62 @@ +package app + +import ( + "errors" + "fmt" + "git.ophivana.moe/cat/fortify/internal/acl" + "git.ophivana.moe/cat/fortify/internal/final" + "git.ophivana.moe/cat/fortify/internal/verbose" + "io/fs" + "os" + "path" +) + +func (a *App) EnsureRunDir() { + if err := os.Mkdir(a.runDirPath, 0700); err != nil && !errors.Is(err, fs.ErrExist) { + final.Fatal("Error creating runtime directory:", err) + } +} + +func (a *App) EnsureRuntime() { + if s, err := os.Stat(a.runtimePath); err != nil { + if errors.Is(err, fs.ErrNotExist) { + final.Fatal("Runtime directory does not exist") + } + final.Fatal("Error accessing runtime directory:", err) + } else if !s.IsDir() { + final.Fatal(fmt.Sprintf("Path '%s' is not a directory", a.runtimePath)) + } else { + if err = acl.UpdatePerm(a.runtimePath, a.UID(), acl.Execute); err != nil { + final.Fatal("Error preparing runtime directory:", err) + } else { + final.RegisterRevertPath(a.runtimePath) + } + verbose.Printf("Runtime data dir '%s' configured\n", a.runtimePath) + } +} + +func (a *App) EnsureShare() { + // acl is unnecessary as this directory is world executable + if err := os.Mkdir(a.sharePath, 0701); err != nil && !errors.Is(err, fs.ErrExist) { + final.Fatal("Error creating shared directory:", err) + } + + // workaround for launch method sudo + if a.LaunchOption() == LaunchMethodSudo { + // ensure child runtime directory (e.g. `/tmp/fortify.%d/%d.share`) + cr := path.Join(a.sharePath, a.Uid+".share") + if err := os.Mkdir(cr, 0700); err != nil && !errors.Is(err, fs.ErrExist) { + final.Fatal("Error creating child runtime directory:", err) + } else { + if err = acl.UpdatePerm(cr, a.UID(), acl.Read, acl.Write, acl.Execute); err != nil { + final.Fatal("Error preparing child runtime directory:", err) + } else { + final.RegisterRevertPath(cr) + } + a.AppendEnv("XDG_RUNTIME_DIR", cr) + a.AppendEnv("XDG_SESSION_CLASS", "user") + a.AppendEnv("XDG_SESSION_TYPE", "tty") + verbose.Printf("Child runtime data dir '%s' configured\n", cr) + } + } +} diff --git a/internal/app/launch.go b/internal/app/launch.go index 02204aae..886e683c 100644 --- a/internal/app/launch.go +++ b/internal/app/launch.go @@ -5,11 +5,11 @@ import ( "encoding/base64" "encoding/gob" "fmt" + "git.ophivana.moe/cat/fortify/internal/final" "os" "strings" "syscall" - "git.ophivana.moe/cat/fortify/internal/state" "git.ophivana.moe/cat/fortify/internal/util" ) @@ -20,7 +20,7 @@ func (a *App) launcherPayloadEnv() string { enc := base64.NewEncoder(base64.StdEncoding, r) if err := gob.NewEncoder(enc).Encode(a.command); err != nil { - state.Fatal("Error encoding launcher payload:", err) + final.Fatal("Error encoding launcher payload:", err) } _ = enc.Close() diff --git a/internal/app/pulse.go b/internal/app/pulse.go index d7e08ba9..4b67ff2e 100644 --- a/internal/app/pulse.go +++ b/internal/app/pulse.go @@ -3,13 +3,13 @@ package app import ( "errors" "fmt" + "git.ophivana.moe/cat/fortify/internal/final" "io/fs" "os" "path" "git.ophivana.moe/cat/fortify/internal/acl" "git.ophivana.moe/cat/fortify/internal/state" - "git.ophivana.moe/cat/fortify/internal/system" "git.ophivana.moe/cat/fortify/internal/util" "git.ophivana.moe/cat/fortify/internal/verbose" ) @@ -18,46 +18,46 @@ func (a *App) SharePulse() { a.setEnablement(state.EnablePulse) // ensure PulseAudio directory ACL (e.g. `/run/user/%d/pulse`) - pulse := path.Join(system.V.Runtime, "pulse") + pulse := path.Join(a.runtimePath, "pulse") pulseS := path.Join(pulse, "native") if s, err := os.Stat(pulse); err != nil { if !errors.Is(err, fs.ErrNotExist) { - state.Fatal("Error accessing PulseAudio directory:", err) + final.Fatal("Error accessing PulseAudio directory:", err) } - state.Fatal(fmt.Sprintf("PulseAudio dir '%s' not found", pulse)) + final.Fatal(fmt.Sprintf("PulseAudio dir '%s' not found", pulse)) } else { // add environment variable for new process a.AppendEnv(util.PulseServer, "unix:"+pulseS) if err = acl.UpdatePerm(pulse, a.UID(), acl.Execute); err != nil { - state.Fatal("Error preparing PulseAudio:", err) + final.Fatal("Error preparing PulseAudio:", err) } else { - state.RegisterRevertPath(pulse) + final.RegisterRevertPath(pulse) } // ensure PulseAudio socket permission (e.g. `/run/user/%d/pulse/native`) if s, err = os.Stat(pulseS); err != nil { if errors.Is(err, fs.ErrNotExist) { - state.Fatal("PulseAudio directory found but socket does not exist") + final.Fatal("PulseAudio directory found but socket does not exist") } - state.Fatal("Error accessing PulseAudio socket:", err) + final.Fatal("Error accessing PulseAudio socket:", err) } else { if m := s.Mode(); m&0o006 != 0o006 { - state.Fatal(fmt.Sprintf("Unexpected permissions on '%s':", pulseS), m) + final.Fatal(fmt.Sprintf("Unexpected permissions on '%s':", pulseS), m) } } // Publish current user's pulse-cookie for target user pulseCookieSource := util.DiscoverPulseCookie() - pulseCookieFinal := path.Join(system.V.Share, "pulse-cookie") + pulseCookieFinal := path.Join(a.sharePath, "pulse-cookie") a.AppendEnv(util.PulseCookie, pulseCookieFinal) verbose.Printf("Publishing PulseAudio cookie '%s' to '%s'\n", pulseCookieSource, pulseCookieFinal) if err = util.CopyFile(pulseCookieFinal, pulseCookieSource); err != nil { - state.Fatal("Error copying PulseAudio cookie:", err) + final.Fatal("Error copying PulseAudio cookie:", err) } if err = acl.UpdatePerm(pulseCookieFinal, a.UID(), acl.Read); err != nil { - state.Fatal("Error publishing PulseAudio cookie:", err) + final.Fatal("Error publishing PulseAudio cookie:", err) } else { - state.RegisterRevertPath(pulseCookieFinal) + final.RegisterRevertPath(pulseCookieFinal) } verbose.Printf("PulseAudio dir '%s' configured\n", pulse) diff --git a/internal/app/run.go b/internal/app/run.go index 53548d74..d4a9e13c 100644 --- a/internal/app/run.go +++ b/internal/app/run.go @@ -3,12 +3,12 @@ package app import ( "errors" "fmt" + "git.ophivana.moe/cat/fortify/internal/final" "os" "os/exec" "strings" "git.ophivana.moe/cat/fortify/internal/state" - "git.ophivana.moe/cat/fortify/internal/system" "git.ophivana.moe/cat/fortify/internal/util" "git.ophivana.moe/cat/fortify/internal/verbose" ) @@ -47,31 +47,33 @@ func (a *App) Run() { cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr - cmd.Dir = system.V.RunDir + cmd.Dir = a.runDirPath verbose.Println("Executing:", cmd) if err := cmd.Start(); err != nil { - state.Fatal("Error starting process:", err) + final.Fatal("Error starting process:", err) } - state.RegisterEnablement(a.enablements) + final.RegisterEnablement(a.enablements) - if err := state.SaveProcess(a.Uid, cmd); err != nil { + if statePath, err := state.SaveProcess(a.Uid, cmd, a.runDirPath, a.command, a.enablements); err != nil { // process already started, shouldn't be fatal fmt.Println("Error registering process:", err) + } else { + final.RegisterStatePath(statePath) } var r int if err := cmd.Wait(); err != nil { var exitError *exec.ExitError if !errors.As(err, &exitError) { - state.Fatal("Error running process:", err) + final.Fatal("Error running process:", err) } } verbose.Println("Process exited with exit code", r) - state.BeforeExit() + final.BeforeExit() os.Exit(r) } @@ -99,7 +101,7 @@ func (a *App) commandBuilderSudo() (args []string) { func (a *App) commandBuilderBwrap() (args []string) { // TODO: build bwrap command - state.Fatal("bwrap") + final.Fatal("bwrap") panic("unreachable") } @@ -127,7 +129,7 @@ func (a *App) commandBuilderMachineCtl() (args []string) { // /bin/sh -c if sh, ok := util.Which("sh"); !ok { - state.Fatal("Did not find 'sh' in PATH") + final.Fatal("Did not find 'sh' in PATH") } else { args = append(args, sh, "-c") } @@ -145,7 +147,7 @@ func (a *App) commandBuilderMachineCtl() (args []string) { innerCommand.WriteString("; ") if executable, err := os.Executable(); err != nil { - state.Fatal("Error reading executable path:", err) + final.Fatal("Error reading executable path:", err) } else { if a.enablements.Has(state.EnableDBus) { innerCommand.WriteString(dbusSessionBusAddress + "=" + "'" + dbusAddress[0] + "' ") diff --git a/internal/app/setup.go b/internal/app/setup.go index c6b44611..8d6eb881 100644 --- a/internal/app/setup.go +++ b/internal/app/setup.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "os/user" + "path" "strconv" "git.ophivana.moe/cat/fortify/internal/state" @@ -12,18 +13,25 @@ import ( "git.ophivana.moe/cat/fortify/internal/verbose" ) +const ( + xdgRuntimeDir = "XDG_RUNTIME_DIR" +) + type App struct { - launchOptionText string + uid int // assigned + env []string // modified via AppendEnv + command []string // set on initialisation - uid int - env []string - command []string + launchOptionText string // set on initialisation + launchOption uint8 // assigned - launchOption uint8 - toolPath string + sharePath string // set on initialisation + runtimePath string // assigned + runDirPath string // assigned + toolPath string // assigned - enablements state.Enablements - *user.User + enablements state.Enablements // set via setEnablement + *user.User // assigned // absolutely *no* method of this type is thread-safe // so don't treat it as if it is @@ -33,6 +41,10 @@ func (a *App) LaunchOption() uint8 { return a.launchOption } +func (a *App) RunDir() string { + return a.runDirPath +} + func (a *App) setEnablement(e state.Enablement) { if a.enablements.Has(e) { panic("enablement " + e.String() + " set twice") @@ -42,8 +54,25 @@ func (a *App) setEnablement(e state.Enablement) { } func New(userName string, args []string, launchOptionText string) *App { - a := &App{command: args, launchOptionText: launchOptionText} + a := &App{ + command: args, + launchOptionText: launchOptionText, + sharePath: path.Join(os.TempDir(), "fortify."+strconv.Itoa(os.Geteuid())), + } + + // runtimePath, runDirPath + if r, ok := os.LookupEnv(xdgRuntimeDir); !ok { + fmt.Println("Env variable", xdgRuntimeDir, "unset") + + // too early for fatal + os.Exit(1) + } else { + a.runtimePath = r + a.runDirPath = path.Join(a.runtimePath, "fortify") + verbose.Println("Runtime directory at", a.runDirPath) + } + // *user.User if u, err := user.Lookup(userName); err != nil { if errors.As(err, new(user.UnknownUserError)) { fmt.Println("unknown user", userName) @@ -58,6 +87,7 @@ func New(userName string, args []string, launchOptionText string) *App { a.User = u } + // uid if u, err := strconv.Atoi(a.Uid); err != nil { // usually unreachable panic("uid parse") @@ -70,6 +100,7 @@ func New(userName string, args []string, launchOptionText string) *App { verbose.Println("System booted with systemd as init system (PID 1).") } + // launchOption, toolPath switch a.launchOptionText { case "sudo": a.launchOption = LaunchMethodSudo diff --git a/internal/app/wayland.go b/internal/app/wayland.go index 12568712..9834fea9 100644 --- a/internal/app/wayland.go +++ b/internal/app/wayland.go @@ -2,12 +2,12 @@ package app import ( "fmt" + "git.ophivana.moe/cat/fortify/internal/final" "os" "path" "git.ophivana.moe/cat/fortify/internal/acl" "git.ophivana.moe/cat/fortify/internal/state" - "git.ophivana.moe/cat/fortify/internal/system" "git.ophivana.moe/cat/fortify/internal/verbose" ) @@ -21,15 +21,15 @@ func (a *App) ShareWayland() { // ensure Wayland socket ACL (e.g. `/run/user/%d/wayland-%d`) if w, ok := os.LookupEnv(waylandDisplay); !ok { - state.Fatal("Wayland: WAYLAND_DISPLAY not set") + final.Fatal("Wayland: WAYLAND_DISPLAY not set") } else { // add environment variable for new process - wp := path.Join(system.V.Runtime, w) + wp := path.Join(a.runtimePath, w) a.AppendEnv(waylandDisplay, wp) if err := acl.UpdatePerm(wp, a.UID(), acl.Read, acl.Write, acl.Execute); err != nil { - state.Fatal(fmt.Sprintf("Error preparing Wayland '%s':", w), err) + final.Fatal(fmt.Sprintf("Error preparing Wayland '%s':", w), err) } else { - state.RegisterRevertPath(wp) + final.RegisterRevertPath(wp) } verbose.Printf("Wayland socket '%s' configured\n", w) } diff --git a/internal/app/x.go b/internal/app/x.go index 20c0de1c..07034026 100644 --- a/internal/app/x.go +++ b/internal/app/x.go @@ -2,6 +2,7 @@ package app import ( "fmt" + "git.ophivana.moe/cat/fortify/internal/final" "os" "git.ophivana.moe/cat/fortify/internal/state" @@ -16,16 +17,16 @@ func (a *App) ShareX() { // discovery X11 and grant user permission via the `ChangeHosts` command if d, ok := os.LookupEnv(display); !ok { - state.Fatal("X11: DISPLAY not set") + final.Fatal("X11: DISPLAY not set") } else { // add environment variable for new process a.AppendEnv(display, d) verbose.Printf("X11: Adding XHost entry SI:localuser:%s to display '%s'\n", a.Username, d) if err := xcb.ChangeHosts(xcb.HostModeInsert, xcb.FamilyServerInterpreted, "localuser\x00"+a.Username); err != nil { - state.Fatal(fmt.Sprintf("Error adding XHost entry to '%s':", d), err) + final.Fatal(fmt.Sprintf("Error adding XHost entry to '%s':", d), err) } else { - state.XcbActionComplete() + final.XcbActionComplete() } } } |
