diff --git a/cli.go b/cli.go index d1f2de1..a6a55e0 100644 --- a/cli.go +++ b/cli.go @@ -7,7 +7,10 @@ import ( ) var ( - userName string + userName string + dbusConfig string + dbusID string + mpris bool mustWayland bool mustX bool @@ -19,7 +22,10 @@ var ( ) func init() { - flag.StringVar(&userName, "u", "chronos", "Specify a username") + flag.StringVar(&userName, "u", "chronos", "Passwd name of user to run as") + flag.StringVar(&dbusConfig, "dbus-config", "builtin", "Path to D-Bus proxy config file, or \"builtin\" for defaults") + flag.StringVar(&dbusID, "dbus-id", "", "D-Bus ID of application, leave empty to disable own paths, has no effect if custom config is available") + flag.BoolVar(&mpris, "mpris", false, "Allow owning MPRIS D-Bus path, has no effect if custom config is available") flag.BoolVar(&mustWayland, "wayland", false, "Share Wayland socket") flag.BoolVar(&mustX, "X", false, "Share X11 socket and allow connection") diff --git a/internal/app/dbus.go b/internal/app/dbus.go index 89394f1..651c0c7 100644 --- a/internal/app/dbus.go +++ b/internal/app/dbus.go @@ -1,14 +1,92 @@ package app import ( + "errors" "fmt" + "os" + "path" + "strconv" + "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" ) -func (a *App) ShareDBus() { +const dbusSessionBusAddress = "DBUS_SESSION_BUS_ADDRESS" + +var dbusAddress string + +func (a *App) ShareDBus(c *dbus.Config) { a.setEnablement(state.EnableDBus) - // TODO: start xdg-dbus-proxy - fmt.Println("warn: dbus proxy not implemented") + var binPath, address string + target := path.Join(system.V.Share, strconv.Itoa(os.Getpid())) + + if b, ok := util.Which("xdg-dbus-proxy"); !ok { + state.Fatal("D-Bus: Did not find 'xdg-dbus-proxy' in PATH") + } else { + binPath = b + } + + if addr, ok := os.LookupEnv(dbusSessionBusAddress); !ok { + state.Fatal("D-Bus: DBUS_SESSION_BUS_ADDRESS not set") + } else { + address = addr + } + + c.Log = system.V.Verbose + p := dbus.New(binPath, address, target) + if system.V.Verbose { + fmt.Println("D-Bus: sealing proxy", c.Args(address, target)) + } + if err := p.Seal(c); err != nil { + state.Fatal("D-Bus: invalid config when sealing proxy,", err) + } + + ready := make(chan bool, 1) + done := make(chan struct{}) + + if system.V.Verbose { + fmt.Printf("Starting session bus proxy '%s' for address '%s'\n", dbusAddress, address) + } + if err := p.Start(&ready); err != nil { + state.Fatal("D-Bus: error starting proxy,", err) + } + if system.V.Verbose { + fmt.Println("D-Bus proxy launch:", p) + } + + go func() { + if err := p.Wait(); err != nil { + fmt.Println("warn: D-Bus proxy returned error,", err) + } else { + if system.V.Verbose { + fmt.Println("D-Bus proxy uneventful wait") + } + } + if err := os.Remove(target); err != nil && !errors.Is(err, os.ErrNotExist) { + fmt.Println("Error removing dangling D-Bus socket:", err) + } + done <- struct{}{} + }() + + // register early to enable Fatal cleanup + state.RegisterDBus(p, &done) + dbusAddress = "unix:path=" + target + + if !<-ready { + state.Fatal("D-Bus: proxy did not start correctly") + } + + a.AppendEnv(dbusSessionBusAddress, dbusAddress) + if err := acl.UpdatePerm(target, a.UID(), acl.Read, acl.Write); err != nil { + state.Fatal(fmt.Sprintf("Error preparing D-Bus proxy '%s':", dbusAddress), err) + } else { + state.RegisterRevertPath(target) + } + if system.V.Verbose { + fmt.Printf("Session bus proxy '%s' for address '%s' configured\n", dbusAddress, address) + } } diff --git a/internal/app/run.go b/internal/app/run.go index 46e0270..4e4e578 100644 --- a/internal/app/run.go +++ b/internal/app/run.go @@ -166,6 +166,9 @@ func (a *App) commandBuilderMachineCtl() (args []string) { if executable, err := os.Executable(); err != nil { state.Fatal("Error reading executable path:", err) } else { + if a.enablements.Has(state.EnableDBus) { + innerCommand.WriteString(dbusSessionBusAddress + "=" + "'" + dbusAddress + "' ") + } innerCommand.WriteString("exec " + executable + " -V") } args = append(args, innerCommand.String()) diff --git a/internal/state/exit.go b/internal/state/exit.go index 3da1f48..4ba0a9d 100644 --- a/internal/state/exit.go +++ b/internal/state/exit.go @@ -65,4 +65,23 @@ func BeforeExit() { fmt.Printf("Stripped ACL entry for user '%s' from '%s'\n", u.Username, candidate) } } + + if dbusProxy != nil { + if system.V.Verbose { + fmt.Println("D-Bus proxy registered, cleaning up") + } + + if err := dbusProxy.Close(); err != nil { + if errors.Is(err, os.ErrClosed) { + if system.V.Verbose { + fmt.Println("D-Bus proxy already closed") + } + } else { + fmt.Println("Error closing D-Bus proxy:", err) + } + } + + // wait for Proxy.Wait to return + <-*dbusDone + } } diff --git a/internal/state/register.go b/internal/state/register.go index ac502ae..be9ce54 100644 --- a/internal/state/register.go +++ b/internal/state/register.go @@ -1,9 +1,14 @@ package state +import "git.ophivana.moe/cat/fortify/dbus" + var ( cleanupCandidate []string enablements *Enablements xcbActionComplete bool + + dbusProxy *dbus.Proxy + dbusDone *chan struct{} ) func RegisterRevertPath(p string) { @@ -23,3 +28,8 @@ func XcbActionComplete() { } xcbActionComplete = true } + +func RegisterDBus(p *dbus.Proxy, done *chan struct{}) { + dbusProxy = p + dbusDone = done +} diff --git a/main.go b/main.go index 9027660..4aca72f 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "encoding/json" "errors" "flag" "fmt" @@ -9,6 +10,7 @@ import ( "strconv" "syscall" + "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/state" @@ -17,7 +19,9 @@ import ( var ( Version = "impure" - a *app.App + + a *app.App + c *dbus.Config ) func tryVersion() { @@ -41,6 +45,21 @@ func main() { a = app.New(userName, flag.Args()) state.Set(*a.User, a.Command(), a.UID()) + // parse D-Bus config file if applicable + if mustDBus { + if dbusConfig == "builtin" { + c = dbus.NewConfig(dbusID, true, mpris) + } else { + if f, err := os.Open(dbusConfig); err != nil { + state.Fatal("Error opening D-Bus proxy config file:", err) + } else { + if err = json.NewDecoder(f).Decode(&c); err != nil { + state.Fatal("Error parsing D-Bus proxy config file:", err) + } + } + } + } + // ensure RunDir (e.g. `/run/user/%d/fortify`) if err := os.Mkdir(system.V.RunDir, 0700); err != nil && !errors.Is(err, fs.ErrExist) { state.Fatal("Error creating runtime directory:", err) @@ -103,7 +122,7 @@ func main() { } if mustDBus { - a.ShareDBus() + a.ShareDBus(c) } if mustPulse {