Compare commits
2 Commits
03c24c5122
...
5d523f50c2
Author | SHA1 | Date |
---|---|---|
Ophestra | 5d523f50c2 | |
Ophestra | 91fe983fdb |
|
@ -7,10 +7,10 @@ import (
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"git.ophivana.moe/cat/fortify/acl"
|
||||||
"git.ophivana.moe/cat/fortify/dbus"
|
"git.ophivana.moe/cat/fortify/dbus"
|
||||||
"git.ophivana.moe/cat/fortify/internal/acl"
|
"git.ophivana.moe/cat/fortify/internal/final"
|
||||||
"git.ophivana.moe/cat/fortify/internal/state"
|
"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/util"
|
||||||
"git.ophivana.moe/cat/fortify/internal/verbose"
|
"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 binPath string
|
||||||
var sessionBus, systemBus [2]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"
|
sessionBus[1] = target + ".bus"
|
||||||
systemBus[1] = target + ".system-bus"
|
systemBus[1] = target + ".system-bus"
|
||||||
dbusAddress = [2]string{
|
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 {
|
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 {
|
} else {
|
||||||
binPath = b
|
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))
|
verbose.Println("D-Bus: sealing system proxy", dsg.Args(systemBus))
|
||||||
}
|
}
|
||||||
if err := p.Seal(dse, dsg); err != nil {
|
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)
|
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])
|
verbose.Printf("Starting system bus proxy '%s' for address '%s'\n", dbusAddress[1], systemBus[0])
|
||||||
}
|
}
|
||||||
if err := p.Start(&ready); err != nil {
|
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)
|
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
|
// register early to enable Fatal cleanup
|
||||||
state.RegisterDBus(p, &done)
|
final.RegisterDBus(p, &done)
|
||||||
|
|
||||||
if !<-ready {
|
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])
|
a.AppendEnv(dbusSessionBusAddress, dbusAddress[0])
|
||||||
if err := acl.UpdatePerm(sessionBus[1], a.UID(), acl.Read, acl.Write); err != nil {
|
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 {
|
} else {
|
||||||
state.RegisterRevertPath(sessionBus[1])
|
final.RegisterRevertPath(sessionBus[1])
|
||||||
}
|
}
|
||||||
if dsg != nil {
|
if dsg != nil {
|
||||||
a.AppendEnv(dbusSystemBusAddress, dbusAddress[1])
|
a.AppendEnv(dbusSystemBusAddress, dbusAddress[1])
|
||||||
if err := acl.UpdatePerm(systemBus[1], a.UID(), acl.Read, acl.Write); err != nil {
|
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 {
|
} else {
|
||||||
state.RegisterRevertPath(systemBus[1])
|
final.RegisterRevertPath(systemBus[1])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
verbose.Printf("Session bus proxy '%s' for address '%s' configured\n", dbusAddress[0], sessionBus[0])
|
verbose.Printf("Session bus proxy '%s' for address '%s' configured\n", dbusAddress[0], sessionBus[0])
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"git.ophivana.moe/cat/fortify/acl"
|
||||||
|
"git.ophivana.moe/cat/fortify/internal/final"
|
||||||
|
"git.ophivana.moe/cat/fortify/internal/verbose"
|
||||||
|
)
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,11 +5,11 @@ import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/gob"
|
"encoding/gob"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"git.ophivana.moe/cat/fortify/internal/final"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"git.ophivana.moe/cat/fortify/internal/state"
|
|
||||||
"git.ophivana.moe/cat/fortify/internal/util"
|
"git.ophivana.moe/cat/fortify/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ func (a *App) launcherPayloadEnv() string {
|
||||||
enc := base64.NewEncoder(base64.StdEncoding, r)
|
enc := base64.NewEncoder(base64.StdEncoding, r)
|
||||||
|
|
||||||
if err := gob.NewEncoder(enc).Encode(a.command); err != nil {
|
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()
|
_ = enc.Close()
|
||||||
|
|
|
@ -7,9 +7,9 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"git.ophivana.moe/cat/fortify/internal/acl"
|
"git.ophivana.moe/cat/fortify/acl"
|
||||||
|
"git.ophivana.moe/cat/fortify/internal/final"
|
||||||
"git.ophivana.moe/cat/fortify/internal/state"
|
"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/util"
|
||||||
"git.ophivana.moe/cat/fortify/internal/verbose"
|
"git.ophivana.moe/cat/fortify/internal/verbose"
|
||||||
)
|
)
|
||||||
|
@ -18,46 +18,46 @@ func (a *App) SharePulse() {
|
||||||
a.setEnablement(state.EnablePulse)
|
a.setEnablement(state.EnablePulse)
|
||||||
|
|
||||||
// ensure PulseAudio directory ACL (e.g. `/run/user/%d/pulse`)
|
// 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")
|
pulseS := path.Join(pulse, "native")
|
||||||
if s, err := os.Stat(pulse); err != nil {
|
if s, err := os.Stat(pulse); err != nil {
|
||||||
if !errors.Is(err, fs.ErrNotExist) {
|
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 {
|
} else {
|
||||||
// add environment variable for new process
|
// add environment variable for new process
|
||||||
a.AppendEnv(util.PulseServer, "unix:"+pulseS)
|
a.AppendEnv(util.PulseServer, "unix:"+pulseS)
|
||||||
if err = acl.UpdatePerm(pulse, a.UID(), acl.Execute); err != nil {
|
if err = acl.UpdatePerm(pulse, a.UID(), acl.Execute); err != nil {
|
||||||
state.Fatal("Error preparing PulseAudio:", err)
|
final.Fatal("Error preparing PulseAudio:", err)
|
||||||
} else {
|
} else {
|
||||||
state.RegisterRevertPath(pulse)
|
final.RegisterRevertPath(pulse)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure PulseAudio socket permission (e.g. `/run/user/%d/pulse/native`)
|
// ensure PulseAudio socket permission (e.g. `/run/user/%d/pulse/native`)
|
||||||
if s, err = os.Stat(pulseS); err != nil {
|
if s, err = os.Stat(pulseS); err != nil {
|
||||||
if errors.Is(err, fs.ErrNotExist) {
|
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 {
|
} else {
|
||||||
if m := s.Mode(); m&0o006 != 0o006 {
|
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
|
// Publish current user's pulse-cookie for target user
|
||||||
pulseCookieSource := util.DiscoverPulseCookie()
|
pulseCookieSource := util.DiscoverPulseCookie()
|
||||||
pulseCookieFinal := path.Join(system.V.Share, "pulse-cookie")
|
pulseCookieFinal := path.Join(a.sharePath, "pulse-cookie")
|
||||||
a.AppendEnv(util.PulseCookie, pulseCookieFinal)
|
a.AppendEnv(util.PulseCookie, pulseCookieFinal)
|
||||||
verbose.Printf("Publishing PulseAudio cookie '%s' to '%s'\n", pulseCookieSource, pulseCookieFinal)
|
verbose.Printf("Publishing PulseAudio cookie '%s' to '%s'\n", pulseCookieSource, pulseCookieFinal)
|
||||||
if err = util.CopyFile(pulseCookieFinal, pulseCookieSource); err != nil {
|
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 {
|
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 {
|
} else {
|
||||||
state.RegisterRevertPath(pulseCookieFinal)
|
final.RegisterRevertPath(pulseCookieFinal)
|
||||||
}
|
}
|
||||||
|
|
||||||
verbose.Printf("PulseAudio dir '%s' configured\n", pulse)
|
verbose.Printf("PulseAudio dir '%s' configured\n", pulse)
|
||||||
|
|
|
@ -3,12 +3,12 @@ package app
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"git.ophivana.moe/cat/fortify/internal/final"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.ophivana.moe/cat/fortify/internal/state"
|
"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/util"
|
||||||
"git.ophivana.moe/cat/fortify/internal/verbose"
|
"git.ophivana.moe/cat/fortify/internal/verbose"
|
||||||
)
|
)
|
||||||
|
@ -47,31 +47,33 @@ func (a *App) Run() {
|
||||||
cmd.Stdin = os.Stdin
|
cmd.Stdin = os.Stdin
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
cmd.Dir = system.V.RunDir
|
cmd.Dir = a.runDirPath
|
||||||
|
|
||||||
verbose.Println("Executing:", cmd)
|
verbose.Println("Executing:", cmd)
|
||||||
|
|
||||||
if err := cmd.Start(); err != nil {
|
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
|
// process already started, shouldn't be fatal
|
||||||
fmt.Println("Error registering process:", err)
|
fmt.Println("Error registering process:", err)
|
||||||
|
} else {
|
||||||
|
final.RegisterStatePath(statePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
var r int
|
var r int
|
||||||
if err := cmd.Wait(); err != nil {
|
if err := cmd.Wait(); err != nil {
|
||||||
var exitError *exec.ExitError
|
var exitError *exec.ExitError
|
||||||
if !errors.As(err, &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)
|
verbose.Println("Process exited with exit code", r)
|
||||||
state.BeforeExit()
|
final.BeforeExit()
|
||||||
os.Exit(r)
|
os.Exit(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +101,7 @@ func (a *App) commandBuilderSudo() (args []string) {
|
||||||
|
|
||||||
func (a *App) commandBuilderBwrap() (args []string) {
|
func (a *App) commandBuilderBwrap() (args []string) {
|
||||||
// TODO: build bwrap command
|
// TODO: build bwrap command
|
||||||
state.Fatal("bwrap")
|
final.Fatal("bwrap")
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,7 +129,7 @@ func (a *App) commandBuilderMachineCtl() (args []string) {
|
||||||
|
|
||||||
// /bin/sh -c
|
// /bin/sh -c
|
||||||
if sh, ok := util.Which("sh"); !ok {
|
if sh, ok := util.Which("sh"); !ok {
|
||||||
state.Fatal("Did not find 'sh' in PATH")
|
final.Fatal("Did not find 'sh' in PATH")
|
||||||
} else {
|
} else {
|
||||||
args = append(args, sh, "-c")
|
args = append(args, sh, "-c")
|
||||||
}
|
}
|
||||||
|
@ -145,7 +147,7 @@ func (a *App) commandBuilderMachineCtl() (args []string) {
|
||||||
innerCommand.WriteString("; ")
|
innerCommand.WriteString("; ")
|
||||||
|
|
||||||
if executable, err := os.Executable(); err != nil {
|
if executable, err := os.Executable(); err != nil {
|
||||||
state.Fatal("Error reading executable path:", err)
|
final.Fatal("Error reading executable path:", err)
|
||||||
} else {
|
} else {
|
||||||
if a.enablements.Has(state.EnableDBus) {
|
if a.enablements.Has(state.EnableDBus) {
|
||||||
innerCommand.WriteString(dbusSessionBusAddress + "=" + "'" + dbusAddress[0] + "' ")
|
innerCommand.WriteString(dbusSessionBusAddress + "=" + "'" + dbusAddress[0] + "' ")
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"git.ophivana.moe/cat/fortify/internal/state"
|
"git.ophivana.moe/cat/fortify/internal/state"
|
||||||
|
@ -12,18 +13,25 @@ import (
|
||||||
"git.ophivana.moe/cat/fortify/internal/verbose"
|
"git.ophivana.moe/cat/fortify/internal/verbose"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
xdgRuntimeDir = "XDG_RUNTIME_DIR"
|
||||||
|
)
|
||||||
|
|
||||||
type App struct {
|
type App struct {
|
||||||
launchOptionText string
|
uid int // assigned
|
||||||
|
env []string // modified via AppendEnv
|
||||||
|
command []string // set on initialisation
|
||||||
|
|
||||||
uid int
|
launchOptionText string // set on initialisation
|
||||||
env []string
|
launchOption uint8 // assigned
|
||||||
command []string
|
|
||||||
|
|
||||||
launchOption uint8
|
sharePath string // set on initialisation
|
||||||
toolPath string
|
runtimePath string // assigned
|
||||||
|
runDirPath string // assigned
|
||||||
|
toolPath string // assigned
|
||||||
|
|
||||||
enablements state.Enablements
|
enablements state.Enablements // set via setEnablement
|
||||||
*user.User
|
*user.User // assigned
|
||||||
|
|
||||||
// absolutely *no* method of this type is thread-safe
|
// absolutely *no* method of this type is thread-safe
|
||||||
// so don't treat it as if it is
|
// so don't treat it as if it is
|
||||||
|
@ -33,6 +41,10 @@ func (a *App) LaunchOption() uint8 {
|
||||||
return a.launchOption
|
return a.launchOption
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) RunDir() string {
|
||||||
|
return a.runDirPath
|
||||||
|
}
|
||||||
|
|
||||||
func (a *App) setEnablement(e state.Enablement) {
|
func (a *App) setEnablement(e state.Enablement) {
|
||||||
if a.enablements.Has(e) {
|
if a.enablements.Has(e) {
|
||||||
panic("enablement " + e.String() + " set twice")
|
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 {
|
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 u, err := user.Lookup(userName); err != nil {
|
||||||
if errors.As(err, new(user.UnknownUserError)) {
|
if errors.As(err, new(user.UnknownUserError)) {
|
||||||
fmt.Println("unknown user", userName)
|
fmt.Println("unknown user", userName)
|
||||||
|
@ -58,6 +87,7 @@ func New(userName string, args []string, launchOptionText string) *App {
|
||||||
a.User = u
|
a.User = u
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// uid
|
||||||
if u, err := strconv.Atoi(a.Uid); err != nil {
|
if u, err := strconv.Atoi(a.Uid); err != nil {
|
||||||
// usually unreachable
|
// usually unreachable
|
||||||
panic("uid parse")
|
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).")
|
verbose.Println("System booted with systemd as init system (PID 1).")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// launchOption, toolPath
|
||||||
switch a.launchOptionText {
|
switch a.launchOptionText {
|
||||||
case "sudo":
|
case "sudo":
|
||||||
a.launchOption = LaunchMethodSudo
|
a.launchOption = LaunchMethodSudo
|
||||||
|
|
|
@ -5,9 +5,9 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"git.ophivana.moe/cat/fortify/internal/acl"
|
"git.ophivana.moe/cat/fortify/acl"
|
||||||
|
"git.ophivana.moe/cat/fortify/internal/final"
|
||||||
"git.ophivana.moe/cat/fortify/internal/state"
|
"git.ophivana.moe/cat/fortify/internal/state"
|
||||||
"git.ophivana.moe/cat/fortify/internal/system"
|
|
||||||
"git.ophivana.moe/cat/fortify/internal/verbose"
|
"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`)
|
// ensure Wayland socket ACL (e.g. `/run/user/%d/wayland-%d`)
|
||||||
if w, ok := os.LookupEnv(waylandDisplay); !ok {
|
if w, ok := os.LookupEnv(waylandDisplay); !ok {
|
||||||
state.Fatal("Wayland: WAYLAND_DISPLAY not set")
|
final.Fatal("Wayland: WAYLAND_DISPLAY not set")
|
||||||
} else {
|
} else {
|
||||||
// add environment variable for new process
|
// add environment variable for new process
|
||||||
wp := path.Join(system.V.Runtime, w)
|
wp := path.Join(a.runtimePath, w)
|
||||||
a.AppendEnv(waylandDisplay, wp)
|
a.AppendEnv(waylandDisplay, wp)
|
||||||
if err := acl.UpdatePerm(wp, a.UID(), acl.Read, acl.Write, acl.Execute); err != nil {
|
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 {
|
} else {
|
||||||
state.RegisterRevertPath(wp)
|
final.RegisterRevertPath(wp)
|
||||||
}
|
}
|
||||||
verbose.Printf("Wayland socket '%s' configured\n", w)
|
verbose.Printf("Wayland socket '%s' configured\n", w)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,10 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"git.ophivana.moe/cat/fortify/internal/final"
|
||||||
"git.ophivana.moe/cat/fortify/internal/state"
|
"git.ophivana.moe/cat/fortify/internal/state"
|
||||||
"git.ophivana.moe/cat/fortify/internal/verbose"
|
"git.ophivana.moe/cat/fortify/internal/verbose"
|
||||||
"git.ophivana.moe/cat/fortify/internal/xcb"
|
"git.ophivana.moe/cat/fortify/xcb"
|
||||||
)
|
)
|
||||||
|
|
||||||
const display = "DISPLAY"
|
const display = "DISPLAY"
|
||||||
|
@ -16,16 +17,16 @@ func (a *App) ShareX() {
|
||||||
|
|
||||||
// discovery X11 and grant user permission via the `ChangeHosts` command
|
// discovery X11 and grant user permission via the `ChangeHosts` command
|
||||||
if d, ok := os.LookupEnv(display); !ok {
|
if d, ok := os.LookupEnv(display); !ok {
|
||||||
state.Fatal("X11: DISPLAY not set")
|
final.Fatal("X11: DISPLAY not set")
|
||||||
} else {
|
} else {
|
||||||
// add environment variable for new process
|
// add environment variable for new process
|
||||||
a.AppendEnv(display, d)
|
a.AppendEnv(display, d)
|
||||||
|
|
||||||
verbose.Printf("X11: Adding XHost entry SI:localuser:%s to display '%s'\n", a.Username, 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 {
|
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 {
|
} else {
|
||||||
state.XcbActionComplete()
|
final.XcbActionComplete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package state
|
package final
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -6,9 +6,10 @@ import (
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"git.ophivana.moe/cat/fortify/internal/acl"
|
"git.ophivana.moe/cat/fortify/acl"
|
||||||
|
"git.ophivana.moe/cat/fortify/internal/state"
|
||||||
"git.ophivana.moe/cat/fortify/internal/verbose"
|
"git.ophivana.moe/cat/fortify/internal/verbose"
|
||||||
"git.ophivana.moe/cat/fortify/internal/xcb"
|
"git.ophivana.moe/cat/fortify/xcb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Fatal(msg ...any) {
|
func Fatal(msg ...any) {
|
||||||
|
@ -31,7 +32,7 @@ func BeforeExit() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if d, err := readLaunchers(u.Uid); err != nil {
|
if d, err := state.ReadLaunchers(runDirPath, u.Uid); err != nil {
|
||||||
fmt.Println("Error reading active launchers:", err)
|
fmt.Println("Error reading active launchers:", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
} else if len(d) > 0 {
|
} else if len(d) > 0 {
|
|
@ -0,0 +1,20 @@
|
||||||
|
package final
|
||||||
|
|
||||||
|
import "os/user"
|
||||||
|
|
||||||
|
var (
|
||||||
|
u *user.User
|
||||||
|
uid int
|
||||||
|
|
||||||
|
runDirPath string
|
||||||
|
)
|
||||||
|
|
||||||
|
func Prepare(val user.User, d int, s string) {
|
||||||
|
if u != nil {
|
||||||
|
panic("final prepared twice")
|
||||||
|
}
|
||||||
|
|
||||||
|
u = &val
|
||||||
|
uid = d
|
||||||
|
runDirPath = s
|
||||||
|
}
|
|
@ -1,21 +1,26 @@
|
||||||
package state
|
package final
|
||||||
|
|
||||||
import "git.ophivana.moe/cat/fortify/dbus"
|
import (
|
||||||
|
"git.ophivana.moe/cat/fortify/dbus"
|
||||||
|
"git.ophivana.moe/cat/fortify/internal/state"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
cleanupCandidate []string
|
cleanupCandidate []string
|
||||||
enablements *Enablements
|
enablements *state.Enablements
|
||||||
xcbActionComplete bool
|
xcbActionComplete bool
|
||||||
|
|
||||||
dbusProxy *dbus.Proxy
|
dbusProxy *dbus.Proxy
|
||||||
dbusDone *chan struct{}
|
dbusDone *chan struct{}
|
||||||
|
|
||||||
|
statePath string
|
||||||
)
|
)
|
||||||
|
|
||||||
func RegisterRevertPath(p string) {
|
func RegisterRevertPath(p string) {
|
||||||
cleanupCandidate = append(cleanupCandidate, p)
|
cleanupCandidate = append(cleanupCandidate, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RegisterEnablement(e Enablements) {
|
func RegisterEnablement(e state.Enablements) {
|
||||||
if enablements != nil {
|
if enablements != nil {
|
||||||
panic("enablement state set twice")
|
panic("enablement state set twice")
|
||||||
}
|
}
|
||||||
|
@ -33,3 +38,11 @@ func RegisterDBus(p *dbus.Proxy, done *chan struct{}) {
|
||||||
dbusProxy = p
|
dbusProxy = p
|
||||||
dbusDone = done
|
dbusDone = done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RegisterStatePath(v string) {
|
||||||
|
if statePath != "" {
|
||||||
|
panic("statePath set twice")
|
||||||
|
}
|
||||||
|
|
||||||
|
statePath = v
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
package state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/gob"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
)
|
||||||
|
|
||||||
|
// we unfortunately have to assume there are never races between processes
|
||||||
|
// this and launcher should eventually be replaced by a server process
|
||||||
|
|
||||||
|
type launcherState struct {
|
||||||
|
PID int
|
||||||
|
Launcher string
|
||||||
|
Argv []string
|
||||||
|
Command []string
|
||||||
|
Capability Enablements
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadLaunchers(runDirPath, uid string) ([]*launcherState, error) {
|
||||||
|
var f *os.File
|
||||||
|
var r []*launcherState
|
||||||
|
launcherPrefix := path.Join(runDirPath, uid)
|
||||||
|
|
||||||
|
if pl, err := os.ReadDir(launcherPrefix); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
for _, e := range pl {
|
||||||
|
if err = func() error {
|
||||||
|
if f, err = os.Open(path.Join(launcherPrefix, e.Name())); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
defer func() {
|
||||||
|
if f.Close() != nil {
|
||||||
|
// unreachable
|
||||||
|
panic("foreign state file closed prematurely")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
var s launcherState
|
||||||
|
r = append(r, &s)
|
||||||
|
return gob.NewDecoder(f).Decode(&s)
|
||||||
|
}
|
||||||
|
}(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r, nil
|
||||||
|
}
|
|
@ -1,68 +1,37 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
"git.ophivana.moe/cat/fortify/internal/system"
|
|
||||||
"git.ophivana.moe/cat/fortify/internal/verbose"
|
"git.ophivana.moe/cat/fortify/internal/verbose"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
func MustPrintLauncherStateGlobal(w **tabwriter.Writer, runDirPath string) {
|
||||||
stateActionEarly bool
|
if dirs, err := os.ReadDir(runDirPath); err != nil {
|
||||||
stateActionEarlyC bool
|
fmt.Println("Error reading runtime directory:", err)
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
flag.BoolVar(&stateActionEarly, "state", false, "print state information of active launchers")
|
|
||||||
flag.BoolVar(&stateActionEarlyC, "state-current", false, "print state information of active launchers for the specified user")
|
|
||||||
}
|
|
||||||
|
|
||||||
func Early() {
|
|
||||||
var w *tabwriter.Writer
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case stateActionEarly:
|
|
||||||
if runDir, err := os.ReadDir(system.V.RunDir); err != nil {
|
|
||||||
fmt.Println("Error reading runtime directory:", err)
|
|
||||||
} else {
|
|
||||||
for _, e := range runDir {
|
|
||||||
if !e.IsDir() {
|
|
||||||
verbose.Println("Skipped non-directory entry", e.Name())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = strconv.Atoi(e.Name()); err != nil {
|
|
||||||
verbose.Println("Skipped non-uid entry", e.Name())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
printLauncherState(e.Name(), &w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case stateActionEarlyC:
|
|
||||||
printLauncherState(u.Uid, &w)
|
|
||||||
default:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if w != nil {
|
|
||||||
if err := w.Flush(); err != nil {
|
|
||||||
fmt.Println("warn: error formatting output:", err)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("No information available.")
|
for _, e := range dirs {
|
||||||
}
|
if !e.IsDir() {
|
||||||
|
verbose.Println("Skipped non-directory entry", e.Name())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
os.Exit(0)
|
if _, err = strconv.Atoi(e.Name()); err != nil {
|
||||||
|
verbose.Println("Skipped non-uid entry", e.Name())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
MustPrintLauncherState(w, runDirPath, e.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func printLauncherState(uid string, w **tabwriter.Writer) {
|
func MustPrintLauncherState(w **tabwriter.Writer, runDirPath, uid string) {
|
||||||
launchers, err := readLaunchers(uid)
|
launchers, err := ReadLaunchers(runDirPath, uid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error reading launchers:", err)
|
fmt.Println("Error reading launchers:", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
@ -8,42 +8,25 @@ import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"git.ophivana.moe/cat/fortify/internal/system"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// we unfortunately have to assume there are never races between processes
|
|
||||||
// this and launcher should eventually be replaced by a server process
|
|
||||||
|
|
||||||
var (
|
|
||||||
statePath string
|
|
||||||
)
|
|
||||||
|
|
||||||
type launcherState struct {
|
|
||||||
PID int
|
|
||||||
Launcher string
|
|
||||||
Argv []string
|
|
||||||
Command []string
|
|
||||||
Capability Enablements
|
|
||||||
}
|
|
||||||
|
|
||||||
// SaveProcess called after process start, before wait
|
// SaveProcess called after process start, before wait
|
||||||
func SaveProcess(uid string, cmd *exec.Cmd) error {
|
func SaveProcess(uid string, cmd *exec.Cmd, runDirPath string, command []string, enablements Enablements) (string, error) {
|
||||||
statePath = path.Join(system.V.RunDir, uid, strconv.Itoa(cmd.Process.Pid))
|
statePath := path.Join(runDirPath, uid, strconv.Itoa(cmd.Process.Pid))
|
||||||
state := launcherState{
|
state := launcherState{
|
||||||
PID: cmd.Process.Pid,
|
PID: cmd.Process.Pid,
|
||||||
Launcher: cmd.Path,
|
Launcher: cmd.Path,
|
||||||
Argv: cmd.Args,
|
Argv: cmd.Args,
|
||||||
Command: command,
|
Command: command,
|
||||||
Capability: *enablements,
|
Capability: enablements,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.Mkdir(path.Join(system.V.RunDir, uid), 0700); err != nil && !errors.Is(err, fs.ErrExist) {
|
if err := os.Mkdir(path.Join(runDirPath, uid), 0700); err != nil && !errors.Is(err, fs.ErrExist) {
|
||||||
return err
|
return statePath, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if f, err := os.OpenFile(statePath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600); err != nil {
|
if f, err := os.OpenFile(statePath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600); err != nil {
|
||||||
return err
|
return statePath, err
|
||||||
} else {
|
} else {
|
||||||
defer func() {
|
defer func() {
|
||||||
if f.Close() != nil {
|
if f.Close() != nil {
|
||||||
|
@ -51,39 +34,6 @@ func SaveProcess(uid string, cmd *exec.Cmd) error {
|
||||||
panic("state file closed prematurely")
|
panic("state file closed prematurely")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return gob.NewEncoder(f).Encode(state)
|
return statePath, gob.NewEncoder(f).Encode(state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func readLaunchers(uid string) ([]*launcherState, error) {
|
|
||||||
var f *os.File
|
|
||||||
var r []*launcherState
|
|
||||||
launcherPrefix := path.Join(system.V.RunDir, uid)
|
|
||||||
|
|
||||||
if pl, err := os.ReadDir(launcherPrefix); err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else {
|
|
||||||
for _, e := range pl {
|
|
||||||
if err = func() error {
|
|
||||||
if f, err = os.Open(path.Join(launcherPrefix, e.Name())); err != nil {
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
defer func() {
|
|
||||||
if f.Close() != nil {
|
|
||||||
// unreachable
|
|
||||||
panic("foreign state file closed prematurely")
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
var s launcherState
|
|
||||||
r = append(r, &s)
|
|
||||||
return gob.NewDecoder(f).Decode(&s)
|
|
||||||
}
|
|
||||||
}(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return r, nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
package state
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os/user"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
u *user.User
|
|
||||||
uid int
|
|
||||||
command []string
|
|
||||||
)
|
|
||||||
|
|
||||||
func Set(val user.User, c []string, d int) {
|
|
||||||
if u != nil {
|
|
||||||
panic("state set twice")
|
|
||||||
}
|
|
||||||
|
|
||||||
u = &val
|
|
||||||
command = c
|
|
||||||
uid = d
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
package system
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Retrieve() {
|
|
||||||
if V != nil {
|
|
||||||
panic("system info retrieved twice")
|
|
||||||
}
|
|
||||||
|
|
||||||
v := &Values{Share: path.Join(os.TempDir(), "fortify."+strconv.Itoa(os.Geteuid()))}
|
|
||||||
|
|
||||||
if r, ok := os.LookupEnv(xdgRuntimeDir); !ok {
|
|
||||||
fmt.Println("Env variable", xdgRuntimeDir, "unset")
|
|
||||||
|
|
||||||
// too early for fatal
|
|
||||||
os.Exit(1)
|
|
||||||
} else {
|
|
||||||
v.Runtime = r
|
|
||||||
v.RunDir = path.Join(v.Runtime, "fortify")
|
|
||||||
}
|
|
||||||
|
|
||||||
V = v
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
package system
|
|
||||||
|
|
||||||
const (
|
|
||||||
xdgRuntimeDir = "XDG_RUNTIME_DIR"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Values struct {
|
|
||||||
Share string
|
|
||||||
Runtime string
|
|
||||||
RunDir string
|
|
||||||
}
|
|
||||||
|
|
||||||
var V *Values
|
|
|
@ -3,11 +3,10 @@ package util
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"git.ophivana.moe/cat/fortify/internal/final"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"git.ophivana.moe/cat/fortify/internal/state"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -43,7 +42,7 @@ func DiscoverPulseCookie() string {
|
||||||
p = path.Join(p, ".pulse-cookie")
|
p = path.Join(p, ".pulse-cookie")
|
||||||
if s, err := os.Stat(p); err != nil {
|
if s, err := os.Stat(p); err != nil {
|
||||||
if !errors.Is(err, fs.ErrNotExist) {
|
if !errors.Is(err, fs.ErrNotExist) {
|
||||||
state.Fatal("Error accessing PulseAudio cookie:", err)
|
final.Fatal("Error accessing PulseAudio cookie:", err)
|
||||||
// unreachable
|
// unreachable
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
@ -56,7 +55,7 @@ func DiscoverPulseCookie() string {
|
||||||
p = path.Join(p, "pulse", "cookie")
|
p = path.Join(p, "pulse", "cookie")
|
||||||
if s, err := os.Stat(p); err != nil {
|
if s, err := os.Stat(p); err != nil {
|
||||||
if !errors.Is(err, fs.ErrNotExist) {
|
if !errors.Is(err, fs.ErrNotExist) {
|
||||||
state.Fatal("Error accessing PulseAudio cookie:", err)
|
final.Fatal("Error accessing PulseAudio cookie:", err)
|
||||||
// unreachable
|
// unreachable
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
@ -65,7 +64,7 @@ func DiscoverPulseCookie() string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state.Fatal(fmt.Sprintf("Cannot locate PulseAudio cookie (tried $%s, $%s/pulse/cookie, $%s/.pulse-cookie)",
|
final.Fatal(fmt.Sprintf("Cannot locate PulseAudio cookie (tried $%s, $%s/pulse/cookie, $%s/.pulse-cookie)",
|
||||||
PulseCookie, xdgConfigHome, home))
|
PulseCookie, xdgConfigHome, home))
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
61
main.go
61
main.go
|
@ -5,17 +5,14 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"git.ophivana.moe/cat/fortify/internal/final"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"git.ophivana.moe/cat/fortify/dbus"
|
"git.ophivana.moe/cat/fortify/dbus"
|
||||||
"git.ophivana.moe/cat/fortify/internal/acl"
|
|
||||||
"git.ophivana.moe/cat/fortify/internal/app"
|
"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/verbose"
|
"git.ophivana.moe/cat/fortify/internal/verbose"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -48,9 +45,8 @@ func main() {
|
||||||
tryVersion()
|
tryVersion()
|
||||||
tryLicense()
|
tryLicense()
|
||||||
|
|
||||||
system.Retrieve()
|
|
||||||
a = app.New(userName, flag.Args(), launchOptionText)
|
a = app.New(userName, flag.Args(), launchOptionText)
|
||||||
state.Set(*a.User, a.Command(), a.UID())
|
final.Prepare(*a.User, a.UID(), a.RunDir())
|
||||||
|
|
||||||
// parse D-Bus config file if applicable
|
// parse D-Bus config file if applicable
|
||||||
if mustDBus {
|
if mustDBus {
|
||||||
|
@ -58,10 +54,10 @@ func main() {
|
||||||
dbusSession = dbus.NewConfig(dbusID, true, mpris)
|
dbusSession = dbus.NewConfig(dbusID, true, mpris)
|
||||||
} else {
|
} else {
|
||||||
if f, err := os.Open(dbusConfigSession); err != nil {
|
if f, err := os.Open(dbusConfigSession); err != nil {
|
||||||
state.Fatal("Error opening D-Bus proxy config file:", err)
|
final.Fatal("Error opening D-Bus proxy config file:", err)
|
||||||
} else {
|
} else {
|
||||||
if err = json.NewDecoder(f).Decode(&dbusSession); err != nil {
|
if err = json.NewDecoder(f).Decode(&dbusSession); err != nil {
|
||||||
state.Fatal("Error parsing D-Bus proxy config file:", err)
|
final.Fatal("Error parsing D-Bus proxy config file:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,46 +65,23 @@ func main() {
|
||||||
// system bus proxy is optional
|
// system bus proxy is optional
|
||||||
if dbusConfigSystem != "nil" {
|
if dbusConfigSystem != "nil" {
|
||||||
if f, err := os.Open(dbusConfigSystem); err != nil {
|
if f, err := os.Open(dbusConfigSystem); err != nil {
|
||||||
state.Fatal("Error opening D-Bus proxy config file:", err)
|
final.Fatal("Error opening D-Bus proxy config file:", err)
|
||||||
} else {
|
} else {
|
||||||
if err = json.NewDecoder(f).Decode(&dbusSystem); err != nil {
|
if err = json.NewDecoder(f).Decode(&dbusSystem); err != nil {
|
||||||
state.Fatal("Error parsing D-Bus proxy config file:", err)
|
final.Fatal("Error parsing D-Bus proxy config file:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure RunDir (e.g. `/run/user/%d/fortify`)
|
// ensure RunDir (e.g. `/run/user/%d/fortify`)
|
||||||
if err := os.Mkdir(system.V.RunDir, 0700); err != nil && !errors.Is(err, fs.ErrExist) {
|
a.EnsureRunDir()
|
||||||
state.Fatal("Error creating runtime directory:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// state query command early exit
|
// state query command early exit
|
||||||
state.Early()
|
tryState()
|
||||||
|
|
||||||
// ensure Share (e.g. `/tmp/fortify.%d`)
|
// ensure Share (e.g. `/tmp/fortify.%d`)
|
||||||
// acl is unnecessary as this directory is world executable
|
a.EnsureShare()
|
||||||
if err := os.Mkdir(system.V.Share, 0701); err != nil && !errors.Is(err, fs.ErrExist) {
|
|
||||||
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")
|
|
||||||
verbose.Printf("Child runtime data dir '%s' configured\n", cr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// warn about target user home directory ownership
|
// warn about target user home directory ownership
|
||||||
if stat, err := os.Stat(a.HomeDir); err != nil {
|
if stat, err := os.Stat(a.HomeDir); err != nil {
|
||||||
|
@ -131,21 +104,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure runtime directory ACL (e.g. `/run/user/%d`)
|
// ensure runtime directory ACL (e.g. `/run/user/%d`)
|
||||||
if s, err := os.Stat(system.V.Runtime); err != nil {
|
a.EnsureRuntime()
|
||||||
if errors.Is(err, fs.ErrNotExist) {
|
|
||||||
state.Fatal("Runtime directory does not exist")
|
|
||||||
}
|
|
||||||
state.Fatal("Error accessing runtime directory:", err)
|
|
||||||
} else if !s.IsDir() {
|
|
||||||
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 directory:", err)
|
|
||||||
} else {
|
|
||||||
state.RegisterRevertPath(system.V.Runtime)
|
|
||||||
}
|
|
||||||
verbose.Printf("Runtime data dir '%s' configured\n", system.V.Runtime)
|
|
||||||
}
|
|
||||||
|
|
||||||
if mustWayland {
|
if mustWayland {
|
||||||
a.ShareWayland()
|
a.ShareWayland()
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"git.ophivana.moe/cat/fortify/internal/state"
|
||||||
|
"os"
|
||||||
|
"text/tabwriter"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
stateActionEarly [2]bool
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.BoolVar(&stateActionEarly[0], "state", false, "print state information of active launchers")
|
||||||
|
flag.BoolVar(&stateActionEarly[1], "state-current", false, "print state information of active launchers for the specified user")
|
||||||
|
}
|
||||||
|
|
||||||
|
// tryState is called after app initialisation
|
||||||
|
func tryState() {
|
||||||
|
var w *tabwriter.Writer
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case stateActionEarly[0]:
|
||||||
|
state.MustPrintLauncherStateGlobal(&w, a.RunDir())
|
||||||
|
case stateActionEarly[1]:
|
||||||
|
state.MustPrintLauncherState(&w, a.RunDir(), a.Uid)
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if w != nil {
|
||||||
|
if err := w.Flush(); err != nil {
|
||||||
|
fmt.Println("warn: error formatting output:", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Println("No information available")
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
Loading…
Reference in New Issue