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  }