// NewJournal returns a new Journal instance pointing to the local journal func NewJournal() (j *Journal, err error) { h, err := dlopen.GetHandle(libsystemdNames) if err != nil { return nil, err } defer func() { if err == nil { return } err2 := h.Close() if err2 != nil { err = fmt.Errorf(`%q and "error closing handle: %v"`, err, err2) } }() j = &Journal{lib: h} sd_journal_open, err := j.getFunction("sd_journal_open") if err != nil { return nil, err } r := C.my_sd_journal_open(sd_journal_open, &j.cjournal, C.SD_JOURNAL_LOCAL_ONLY) if r < 0 { return nil, fmt.Errorf("failed to open journal: %d", syscall.Errno(-r)) } return j, nil }
func getFunction(name string) (unsafe.Pointer, error) { libsystemdMutex.Lock() defer libsystemdMutex.Unlock() if libsystemdHandle == nil { h, err := dlopen.GetHandle(libsystemdNames) if err != nil { return nil, err } libsystemdHandle = h } f, ok := libsystemdFunctions[name] if !ok { var err error f, err = libsystemdHandle.GetSymbolPointer(name) if err != nil { return nil, err } libsystemdFunctions[name] = f } return f, nil }
// GetRunningSlice attempts to retrieve the name of the systemd slice in which // the current process is running. // This function is a wrapper around the libsystemd C library; if it cannot be // opened, an error is returned. func GetRunningSlice() (slice string, err error) { var h *dlopen.LibHandle h, err = dlopen.GetHandle(libsystemdNames) if err != nil { return } defer func() { if err1 := h.Close(); err1 != nil { err = err1 } }() sd_pid_get_slice, err := h.GetSymbolPointer("sd_pid_get_slice") if err != nil { return } var s string sl := C.CString(s) defer C.free(unsafe.Pointer(sl)) ret := C.my_sd_pid_get_slice(sd_pid_get_slice, 0, &sl) if ret < 0 { err = fmt.Errorf("error calling sd_pid_get_slice: %v", syscall.Errno(-ret)) return } return C.GoString(sl), nil }
// CurrentUnitName attempts to retrieve the name of the systemd system unit // from which the calling process has been invoked. It wraps the systemd // `sd_pid_get_unit` call, with the same caveat: for processes not part of a // systemd system unit, this function will return an error. func CurrentUnitName() (unit string, err error) { var h *dlopen.LibHandle h, err = dlopen.GetHandle(libsystemdNames) if err != nil { return } defer func() { if err1 := h.Close(); err1 != nil { err = err1 } }() sd_pid_get_unit, err := h.GetSymbolPointer("sd_pid_get_unit") if err != nil { return } var s string u := C.CString(s) defer C.free(unsafe.Pointer(u)) ret := C.my_sd_pid_get_unit(sd_pid_get_unit, 0, &u) if ret < 0 { err = fmt.Errorf("error calling sd_pid_get_unit: %v", syscall.Errno(-ret)) return } unit = C.GoString(u) return }
// 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 *dlopen.LibHandle h, err = dlopen.GetHandle(libsystemdNames) if err != nil { return } defer func() { if err1 := h.Close(); err1 != nil { err = err1 } }() sd_pid_get_owner_uid, err := h.GetSymbolPointer("sd_pid_get_owner_uid") if err != nil { 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 }
// NewJournalFromDir returns a new Journal instance pointing to a journal residing // in a given directory. The supplied path may be relative or absolute; if // relative, it will be converted to an absolute path before being opened. func NewJournalFromDir(path string) (j *Journal, err error) { h, err := dlopen.GetHandle(libsystemdNames) if err != nil { return nil, err } defer func() { if err == nil { return } err2 := h.Close() if err2 != nil { err = fmt.Errorf(`%q and "error closing handle: %v"`, err, err2) } }() path, err = filepath.Abs(path) if err != nil { return nil, err } j = &Journal{lib: h} sd_journal_open_directory, err := j.getFunction("sd_journal_open_directory") if err != nil { return nil, err } p := C.CString(path) defer C.free(unsafe.Pointer(p)) r := C.my_sd_journal_open_directory(sd_journal_open_directory, &j.cjournal, p, 0) if r < 0 { return nil, fmt.Errorf("failed to open journal in directory %q: %d", path, syscall.Errno(-r)) } return j, nil }