// RunningFromSystemService tries to detect whether the current process has // been invoked from a system service. The condition for this is whether the // process is _not_ a user process. User processes are those running in session // scopes or under per-user `systemd --user` instances. // // To avoid false positives on systems without `pam_systemd` (which is // responsible for creating user sessions), this function also uses a heuristic // to detect whether it's being invoked from a session leader process. This is // the case if the current process is executed directly from a service file // (e.g. with `ExecStart=/this/cmd`). Note that this heuristic will fail if the // command is instead launched in a subshell or similar so that it is not // session leader (e.g. `ExecStart=/bin/bash -c "/this/cmd"`) // // This function is a wrapper around the libsystemd C library; if this is // unable to successfully open a handle to the library for any reason (e.g. it // cannot be found), an errr will be returned func RunningFromSystemService() (ret bool, err error) { var h *libHandle h, err = getHandle() if err != nil { return } defer func() { if err1 := h.Close(); err1 != nil { err = err1 } }() sym := C.CString("sd_pid_get_owner_uid") defer C.free(unsafe.Pointer(sym)) sd_pid_get_owner_uid := C.dlsym(h.handle, sym) if sd_pid_get_owner_uid == nil { err = fmt.Errorf("error resolving sd_pid_get_owner_uid function") return } var uid C.uid_t errno := C.my_sd_pid_get_owner_uid(sd_pid_get_owner_uid, 0, &uid) serrno := syscall.Errno(-errno) // when we're running from a unit file, sd_pid_get_owner_uid returns // ENOENT (systemd <220) or ENXIO (systemd >=220) switch { case errno >= 0: ret = false case serrno == syscall.ENOENT, serrno == syscall.ENXIO: // Since the implementation of sessions in systemd relies on // the `pam_systemd` module, using the sd_pid_get_owner_uid // heuristic alone can result in false positives if that module // (or PAM itself) is not present or properly configured on the // system. As such, we also check if we're the session leader, // which should be the case if we're invoked from a unit file, // but not if e.g. we're invoked from the command line from a // user's login session ret = C.am_session_leader() == 1 default: err = fmt.Errorf("error calling sd_pid_get_owner_uid: %v", syscall.Errno(-errno)) } return }
func isRunningFromUnitFile() (ret bool, err error) { libname := C.CString("libsystemd.so") defer C.free(unsafe.Pointer(libname)) handle := C.dlopen(libname, C.RTLD_LAZY) if handle == nil { // we can't open libsystemd.so so we assume systemd is not // installed and we're not running from a unit file return } defer func() { if r := C.dlclose(handle); r != 0 { err = fmt.Errorf("error closing libsystemd.so") } }() sd_pid_get_owner_uid := C.dlsym(handle, C.CString("sd_pid_get_owner_uid")) if sd_pid_get_owner_uid == nil { err = fmt.Errorf("error resolving sd_pid_get_owner_uid function") return } var uid C.uid_t errno := C.my_sd_pid_get_owner_uid(sd_pid_get_owner_uid, 0, &uid) // when we're running from a unit file, sd_pid_get_owner_uid returns // ENOENT (systemd <220) or ENXIO (systemd >=220) switch { case errno >= 0: case syscall.Errno(-errno) == syscall.ENOENT || syscall.Errno(-errno) == syscall.ENXIO: if C.am_session_leader() == 1 { ret = true } default: err = fmt.Errorf("error calling sd_pid_get_owner_uid: %v", syscall.Errno(-errno)) } return }