github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/systemd/systemd_linux.go (about) 1 //go:build linux && !android 2 // +build linux,!android 3 4 package systemd 5 6 import ( 7 "errors" 8 "fmt" 9 "net" 10 "os" 11 "os/exec" 12 "strings" 13 14 sdActivation "github.com/coreos/go-systemd/activation" 15 sdDaemon "github.com/coreos/go-systemd/daemon" 16 sdUtil "github.com/coreos/go-systemd/util" 17 ) 18 19 // IsUserSystemdRunning checks that systemd is running at the user- (as opposed 20 // to system-) level. IsRunningSystemd below checks the system level, but there 21 // are cases where the system level is working while the user level is not. 22 // Sudo env weirdness can cause it, and it also happens on older distros. In 23 // those cases, we'll also fall back to non-systemd startup. 24 // 25 // This function prints loud warnings because we only ever run it when 26 // IsRunningSystemd is true, in which case all of these errors are unexpected. 27 func IsUserSystemdRunning() bool { 28 c := exec.Command("systemctl", "--user", "is-system-running") 29 output, err := c.Output() 30 // Ignore non-zero-exit-status errors, because of "degraded" below. 31 _, isExitError := err.(*exec.ExitError) 32 if err != nil && !isExitError { 33 os.Stderr.WriteString(fmt.Sprintf("Failed to run systemctl: check user manager status: %s\n", err)) 34 return false 35 } 36 outputStr := strings.TrimSpace(string(output)) 37 38 switch outputStr { 39 case "running": 40 return true 41 case "degraded": 42 // "degraded" just means that some service has failed to start. That 43 // could be a totally unrelated application on the user's machine, so 44 // we treat it the same as "running". Other methods of detecting this 45 // have turned out to be inconsistent across machines, like checking 46 // the status of dbus or init.scope, or even `systemd-run --user true`. 47 // If this is a false positive, user should specify KEYBASE_SYSTEMD=0. 48 return true 49 case "": 50 os.Stderr.WriteString("Failed to reach user-level systemd daemon.\n") 51 return false 52 default: 53 os.Stderr.WriteString(fmt.Sprintf("Systemd reported an unexpected status: %s\n", outputStr)) 54 return false 55 } 56 } 57 58 func IsRunningSystemd() bool { 59 return sdUtil.IsRunningSystemd() && IsUserSystemdRunning() 60 } 61 62 // NOTE: We no longer configure our keybse.service and kbfs.service units to be 63 // socket-activated by default. It was causing too much trouble when 64 // non-systemd instances deleted the socket files. It's possible this issue 65 // will get fixed in future versions of systemd; see 66 // https://github.com/systemd/systemd/issues/7274. 67 func IsSocketActivated() bool { 68 return (os.Getenv("LISTEN_FDS") != "") 69 } 70 71 // If the service has been started via socket activation, with a socket already 72 // open in the environment, return that socket. Otherwise return (nil, nil). 73 // Currently only implemented for systemd on Linux. 74 func GetListenerFromEnvironment() (net.Listener, error) { 75 listeners, err := sdActivation.Listeners() 76 if err != nil { 77 // Errors here (e.g. out of file descriptors, maybe?) aren't even 78 // returned by go-systemd right now, but they could be in the future. 79 return nil, err 80 } 81 if len(listeners) > 1 { 82 // More than one socket here probably means a messed up .service file. 83 return nil, errors.New("Too many listeners passed from systemd.") 84 } 85 if len(listeners) == 1 { 86 // Found a socket in the environment. Return it. 87 return listeners[0], nil 88 } 89 // No socket found. Either we're not running under systemd at all, or the 90 // socket isn't configured. The caller will create its own socket. 91 return nil, nil 92 } 93 94 func NotifyStartupFinished() { 95 _, _ = sdDaemon.SdNotify(false /* unsetEnv */, "READY=1") 96 }