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