user.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. package systemd
  2. import (
  3. "bufio"
  4. "bytes"
  5. "errors"
  6. "fmt"
  7. "os"
  8. "os/exec"
  9. "path/filepath"
  10. "strconv"
  11. "strings"
  12. systemdDbus "github.com/coreos/go-systemd/v22/dbus"
  13. dbus "github.com/godbus/dbus/v5"
  14. "github.com/opencontainers/runc/libcontainer/userns"
  15. )
  16. // newUserSystemdDbus creates a connection for systemd user-instance.
  17. func newUserSystemdDbus() (*systemdDbus.Conn, error) {
  18. addr, err := DetectUserDbusSessionBusAddress()
  19. if err != nil {
  20. return nil, err
  21. }
  22. uid, err := DetectUID()
  23. if err != nil {
  24. return nil, err
  25. }
  26. return systemdDbus.NewConnection(func() (*dbus.Conn, error) {
  27. conn, err := dbus.Dial(addr)
  28. if err != nil {
  29. return nil, fmt.Errorf("error while dialing %q: %w", addr, err)
  30. }
  31. methods := []dbus.Auth{dbus.AuthExternal(strconv.Itoa(uid))}
  32. err = conn.Auth(methods)
  33. if err != nil {
  34. conn.Close()
  35. return nil, fmt.Errorf("error while authenticating connection (address=%q, UID=%d): %w", addr, uid, err)
  36. }
  37. if err = conn.Hello(); err != nil {
  38. conn.Close()
  39. return nil, fmt.Errorf("error while sending Hello message (address=%q, UID=%d): %w", addr, uid, err)
  40. }
  41. return conn, nil
  42. })
  43. }
  44. // DetectUID detects UID from the OwnerUID field of `busctl --user status`
  45. // if running in userNS. The value corresponds to sd_bus_creds_get_owner_uid(3) .
  46. //
  47. // Otherwise returns os.Getuid() .
  48. func DetectUID() (int, error) {
  49. if !userns.RunningInUserNS() {
  50. return os.Getuid(), nil
  51. }
  52. b, err := exec.Command("busctl", "--user", "--no-pager", "status").CombinedOutput()
  53. if err != nil {
  54. return -1, fmt.Errorf("could not execute `busctl --user --no-pager status` (output: %q): %w", string(b), err)
  55. }
  56. scanner := bufio.NewScanner(bytes.NewReader(b))
  57. for scanner.Scan() {
  58. s := strings.TrimSpace(scanner.Text())
  59. if strings.HasPrefix(s, "OwnerUID=") {
  60. uidStr := strings.TrimPrefix(s, "OwnerUID=")
  61. i, err := strconv.Atoi(uidStr)
  62. if err != nil {
  63. return -1, fmt.Errorf("could not detect the OwnerUID: %w", err)
  64. }
  65. return i, nil
  66. }
  67. }
  68. if err := scanner.Err(); err != nil {
  69. return -1, err
  70. }
  71. return -1, errors.New("could not detect the OwnerUID")
  72. }
  73. // DetectUserDbusSessionBusAddress returns $DBUS_SESSION_BUS_ADDRESS if set.
  74. // Otherwise returns "unix:path=$XDG_RUNTIME_DIR/bus" if $XDG_RUNTIME_DIR/bus exists.
  75. // Otherwise parses the value from `systemctl --user show-environment` .
  76. func DetectUserDbusSessionBusAddress() (string, error) {
  77. if env := os.Getenv("DBUS_SESSION_BUS_ADDRESS"); env != "" {
  78. return env, nil
  79. }
  80. if xdr := os.Getenv("XDG_RUNTIME_DIR"); xdr != "" {
  81. busPath := filepath.Join(xdr, "bus")
  82. if _, err := os.Stat(busPath); err == nil {
  83. busAddress := "unix:path=" + busPath
  84. return busAddress, nil
  85. }
  86. }
  87. b, err := exec.Command("systemctl", "--user", "--no-pager", "show-environment").CombinedOutput()
  88. if err != nil {
  89. return "", fmt.Errorf("could not execute `systemctl --user --no-pager show-environment` (output=%q): %w", string(b), err)
  90. }
  91. scanner := bufio.NewScanner(bytes.NewReader(b))
  92. for scanner.Scan() {
  93. s := strings.TrimSpace(scanner.Text())
  94. if strings.HasPrefix(s, "DBUS_SESSION_BUS_ADDRESS=") {
  95. return strings.TrimPrefix(s, "DBUS_SESSION_BUS_ADDRESS="), nil
  96. }
  97. }
  98. return "", errors.New("could not detect DBUS_SESSION_BUS_ADDRESS from `systemctl --user --no-pager show-environment`. Make sure you have installed the dbus-user-session or dbus-daemon package and then run: `systemctl --user start dbus`")
  99. }