Skip to content

Instantly share code, notes, and snippets.

@moreati
Last active June 17, 2021 21:38
Show Gist options
  • Save moreati/b40db27759d068e0ac120a12e98c64a4 to your computer and use it in GitHub Desktop.
Save moreati/b40db27759d068e0ac120a12e98c64a4 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
"""
Determine whether stderr is connected to systemd journal, and hence whether
structured events can be written to the journal.
Based on https://systemd.io/JOURNAL_NATIVE_PROTOCOL/#automatic-protocol-upgrading
"""
import os
import sys
def stderr_is_systemd_journal() -> bool:
try:
journal_stream = os.environ['JOURNAL_STREAM']
except KeyError:
return False
try:
part1, part2 = journal_stream.split(':', maxsplit=1)
except ValueError:
return False
try:
journal_dev = int(part1, 10)
journal_inode = int(part2, 10)
except ValueError:
return False
stderr_stat = os.fstat(sys.stderr.fileno())
return (stderr_stat.st_dev, stderr_stat.st_ino) == (journal_dev, journal_inode)
def main():
stderr_stat = os.fstat(sys.stderr.fileno())
print(f"{stderr_stat.st_dev=} {stderr_stat.st_ino=}")
print(f"{os.environ.get('JOURNAL_STREAM')=}")
print(f"{stderr_is_systemd_journal()=}")
if stderr_is_systemd_journal():
print("Hello systemd journal")
if __name__ == '__main__':
main()
package main
import (
"errors"
"fmt"
"os"
"strconv"
"strings"
"syscall"
)
// StdErrIsJournal returns true if standard error is connected to the systemd
// journal, false otherwise
func StderrIsJournal() (bool, error) {
// When a processes is started by systemd and standard error is connected
// to the journal, then $JOURNAL_STREAM contains the device and inode
// number of the standard error file descriptor (e.g. "123:789").
// https://www.freedesktop.org/software/systemd/man/systemd.exec.html#%24JOURNAL_STREAM
js := os.Getenv("JOURNAL_STREAM")
parts := strings.SplitN(js, ":", 2)
if len(parts) != 2 {
return false, errors.New(js)
}
journalDev, err := strconv.ParseUint(parts[0], 10, 64)
if err != nil {
return false, err
}
journalIno, err := strconv.ParseUint(parts[1], 10, 64)
if err != nil {
return false, err
}
stat, err := os.Stderr.Stat()
if err != nil {
return false, err
}
statRaw, ok := stat.Sys().(*syscall.Stat_t)
if !ok {
return false, errors.New("couldn't convert stat")
}
return journalDev == statRaw.Dev && journalIno == statRaw.Ino, nil
}
func main() {
x, y := StderrIsJournal()
fmt.Println(x, y)
}
@moreati
Copy link
Author

moreati commented Jun 8, 2021

Invoked from a terminal the script correctly detects that stderr is not attached to the journal. The journal is available, but stderr is a different file.

:~/src$ ./journal-detect.py 
stderr_stat.st_dev=24 stderr_stat.st_ino=3
os.environ.get('JOURNAL_STREAM')='8:57469'
stderr_is_systemd_journal()=False

Invoked as a (temporary) systemd unit the script correctly detects that stderr is the journal, and it can adjust behaviour as desired.

:~/src$ systemd-run --user --wait ./journal-detect.py
Running as unit: run-u240.service
Finished with result: success
Main processes terminated with: code=exited/status=0
Service runtime: 27ms
:~/src$ journalctl --no-pager --user -u run-u240.service
-- Logs begin at Fri 2020-05-15 13:14:09 BST, end at Tue 2021-06-08 23:55:16 BST. --
Jun 08 23:55:16 martha systemd[2428]: Started /home/alex/src/./journal-detect.py.
Jun 08 23:55:16 martha journal-detect.py[6989]: stderr_stat.st_dev=8 stderr_stat.st_ino=94208
Jun 08 23:55:16 martha journal-detect.py[6989]: os.environ.get('JOURNAL_STREAM')='8:94208'
Jun 08 23:55:16 martha journal-detect.py[6989]: stderr_is_systemd_journal()=True
Jun 08 23:55:16 martha journal-detect.py[6989]: Hello systemd journal
Jun 08 23:55:16 martha systemd[2428]: run-u240.service: Succeeded.

@moreati
Copy link
Author

moreati commented Jun 17, 2021

:~/tmp$ go build journal_detect.go 
:~/tmp$ ./journal_detect
false <nil>
:~/tmp$ systemd-run --user ./journal_detect
Running as unit: run-r4668e6b91b744684a0f6ac2bf75defee.service
:~/tmp$ journalctl --user -u run-r4668e6b91b744684a0f6ac2bf75defee.service
-- Logs begin at Fri 2020-05-15 13:14:09 BST, end at Thu 2021-06-17 22:26:20 BST. --
Jun 17 22:26:20 martha systemd[2428]: Started /home/alex/tmp/./main.
Jun 17 22:26:20 martha journal_detect[293679]: true <nil>
Jun 17 22:26:20 martha systemd[2428]: run-r4668e6b91b744684a0f6ac2bf75defee.service: Succeeded.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment