diff --git a/event-journald/build.gradle b/event-journald/build.gradle
new file mode 100644
index 0000000..843d0a5
--- /dev/null
+++ b/event-journald/build.gradle
@@ -0,0 +1,3 @@
+dependencies {
+ implementation libs.jna
+}
diff --git a/event-journald/src/main/java/org/xbib/event/journald/DefaultJournalEntry.java b/event-journald/src/main/java/org/xbib/event/journald/DefaultJournalEntry.java
new file mode 100644
index 0000000..c683637
--- /dev/null
+++ b/event-journald/src/main/java/org/xbib/event/journald/DefaultJournalEntry.java
@@ -0,0 +1,421 @@
+package org.xbib.event.journald;
+
+public class DefaultJournalEntry implements JournalEntry {
+
+ private String message;
+
+ private String messageId;
+
+ private int priority;
+
+ private String codeFile;
+
+ private String codeLine;
+
+ private String codeFunc;
+
+ private String errno;
+
+ private String syslogFacility;
+
+ private String syslogIdentifier;
+
+ private String syslogPid;
+
+ private String syslogTimestamp;
+
+ private String syslogRaw;
+
+ private String pid;
+
+ private String uid;
+
+ private String gid;
+
+ private String comm;
+
+ private String exe;
+
+ private String cmdLine;
+
+ private String capEffective;
+
+ private String auditSession;
+
+ private String auditLoginUid;
+
+ private String systemdCgroup;
+
+ private String systemdSlice;
+
+ private String systemdUnit;
+
+ private String systemdUserSlice;
+
+ private String systemdUserUnit;
+
+ private String systemdSession;
+
+ private String systemdOwnerUid;
+
+ private String selinuxContext;
+
+ private String sourceRealtimeTimestamp;
+
+ private String bootId;
+
+ private String machineId;
+
+ private String systemdInvocationId;
+
+ private String hostname;
+
+ private String transport;
+
+ private String streamId;
+
+ private String lineBreak;
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ @Override
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessageId(String messageId) {
+ this.messageId = messageId;
+ }
+
+ @Override
+ public String getMessageId() {
+ return messageId;
+ }
+
+ public void setPriority(int priority) {
+ this.priority = priority;
+ }
+
+ @Override
+ public int getPriority() {
+ return priority;
+ }
+
+ public void setCodeFile(String codeFile) {
+ this.codeFile = codeFile;
+ }
+
+ @Override
+ public String getCodeFile() {
+ return codeFile;
+ }
+
+ public void setCodeLine(String codeLine) {
+ this.codeLine = codeLine;
+ }
+
+ @Override
+ public String getCodeLine() {
+ return codeLine;
+ }
+
+ public void setCodeFunc(String codeFunc) {
+ this.codeFunc = codeFunc;
+ }
+
+ @Override
+ public String getCodeFunc() {
+ return codeFunc;
+ }
+
+ public void setErrno(String errno) {
+ this.errno = errno;
+ }
+
+ @Override
+ public String getErrno() {
+ return errno;
+ }
+
+ public void setSyslogFacility(String syslogFacility) {
+ this.syslogFacility = syslogFacility;
+ }
+
+ @Override
+ public String getSyslogFacility() {
+ return syslogFacility;
+ }
+
+ public void setSyslogIdentifier(String syslogIdentifier) {
+ this.syslogIdentifier = syslogIdentifier;
+ }
+
+ @Override
+ public String getSyslogIdentifier() {
+ return syslogIdentifier;
+ }
+
+ public void setSyslogPid(String syslogPid) {
+ this.syslogPid = syslogPid;
+ }
+
+ @Override
+ public String getSyslogPid() {
+ return syslogPid;
+ }
+
+ public void setSyslogTimestamp(String syslogTimestamp) {
+ this.syslogTimestamp = syslogTimestamp;
+ }
+
+ @Override
+ public String getSyslogTimestamp() {
+ return syslogTimestamp;
+ }
+
+ public void setSyslogRaw(String syslogRaw) {
+ this.syslogRaw = syslogRaw;
+ }
+
+ @Override
+ public String getSyslogRaw() {
+ return syslogRaw;
+ }
+
+ public void setPid(String pid) {
+ this.pid = pid;
+ }
+
+ @Override
+ public String getPid() {
+ return pid;
+ }
+
+ public void setUid(String uid) {
+ this.uid = uid;
+ }
+
+ @Override
+ public String getUid() {
+ return uid;
+ }
+
+ public void setGid(String gid) {
+ this.gid = gid;
+ }
+
+ @Override
+ public String getGid() {
+ return gid;
+ }
+
+ public void setComm(String comm) {
+ this.comm = comm;
+ }
+
+ @Override
+ public String getComm() {
+ return comm;
+ }
+
+ public void setExe(String exe) {
+ this.exe = exe;
+ }
+
+ @Override
+ public String getExe() {
+ return exe;
+ }
+
+ public void setCmdLine(String cmdline) {
+ this.cmdLine = cmdline;
+ }
+
+ @Override
+ public String getCmdLine() {
+ return cmdLine;
+ }
+
+ public void setCapEffective(String capEffective) {
+ this.capEffective = capEffective;
+ }
+
+ @Override
+ public String getCapEffective() {
+ return capEffective;
+ }
+
+ public void setAuditSession(String auditSession) {
+ this.auditSession = auditSession;
+ }
+
+ @Override
+ public String getAuditSession() {
+ return auditSession;
+ }
+
+ public void setAuditLoginUid(String auditLoginUid) {
+ this.auditLoginUid = auditLoginUid;
+ }
+
+ @Override
+ public String getAuditLoginUid() {
+ return auditLoginUid;
+ }
+
+ public void setSystemdCgroup(String systemdCgroup) {
+ this.systemdCgroup = systemdCgroup;
+ }
+
+ @Override
+ public String getSystemdCgroup() {
+ return systemdCgroup;
+ }
+
+ public void setSystemdSlice(String systemdSlice) {
+ this.systemdSlice = systemdSlice;
+ }
+
+ @Override
+ public String getSystemdSlice() {
+ return systemdSlice;
+ }
+
+ public void setSystemdUnit(String systemdUnit) {
+ this.systemdUnit = systemdUnit;
+ }
+
+ @Override
+ public String getSystemdUnit() {
+ return systemdUnit;
+ }
+
+ public void setSystemdUserSlice(String systemdUserSlice) {
+ this.systemdUserSlice = systemdUserSlice;
+ }
+
+ @Override
+ public String getSystemdUserSlice() {
+ return systemdUserSlice;
+ }
+
+ public void setSystemdUserUnit(String systemdUserUnit) {
+ this.systemdUserUnit = systemdUserUnit;
+ }
+
+ @Override
+ public String getSystemdUserUnit() {
+ return systemdUserUnit;
+ }
+
+ public void setSystemdSession(String systemdSession) {
+ this.systemdSession = systemdSession;
+ }
+
+ @Override
+ public String getSystemdSession() {
+ return systemdSession;
+ }
+
+ public void setSystemdOwnerUid(String systemdOwnerUid) {
+ this.systemdOwnerUid = systemdOwnerUid;
+ }
+
+ @Override
+ public String getSystemdOwnerUid() {
+ return systemdOwnerUid;
+ }
+
+ public void setSelinuxContext(String selinuxContext) {
+ this.selinuxContext = selinuxContext;
+ }
+
+ @Override
+ public String getSelinuxContext() {
+ return selinuxContext;
+ }
+
+ public void setSourceRealtimeTimestamp(String sourceRealtimeTimestamp) {
+ this.sourceRealtimeTimestamp = sourceRealtimeTimestamp;
+ }
+
+ @Override
+ public String getSourceRealtimeTimestamp() {
+ return sourceRealtimeTimestamp;
+ }
+
+ public void setBootId(String bootId) {
+ this.bootId = bootId;
+ }
+
+ @Override
+ public String getBootId() {
+ return bootId;
+ }
+
+ public void setMachineId(String machineId) {
+ this.machineId = machineId;
+ }
+
+ @Override
+ public String getMachineId() {
+ return machineId;
+ }
+
+ public void setSystemdInvocationId(String systemdInvocationId) {
+ this.systemdInvocationId = systemdInvocationId;
+ }
+
+ @Override
+ public String getSystemdInvocationId() {
+ return systemdInvocationId;
+ }
+
+ public void setHostname(String hostname) {
+ this.hostname = hostname;
+ }
+
+ @Override
+ public String getHostname() {
+ return hostname;
+ }
+
+ public void setTransport(String transport) {
+ this.transport = transport;
+ }
+
+ @Override
+ public String getTransport() {
+ return transport;
+ }
+
+ public void setStreamId(String streamId) {
+ this.streamId = streamId;
+ }
+
+ @Override
+ public String getStreamId() {
+ return streamId;
+ }
+
+ public void setLineBreak(String lineBreak) {
+ this.lineBreak = lineBreak;
+ }
+
+ @Override
+ public String getLineBreak() {
+ return lineBreak;
+ }
+
+ @Override
+ public String getFieldValue(String fieldName) {
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return "priority=" + this.priority + ",message=" + this.message;
+ }
+}
diff --git a/event-journald/src/main/java/org/xbib/event/journald/JournalEntry.java b/event-journald/src/main/java/org/xbib/event/journald/JournalEntry.java
new file mode 100644
index 0000000..d78e438
--- /dev/null
+++ b/event-journald/src/main/java/org/xbib/event/journald/JournalEntry.java
@@ -0,0 +1,80 @@
+package org.xbib.event.journald;
+
+public interface JournalEntry {
+
+ String getMessage();
+
+ String getMessageId();
+
+ int getPriority();
+
+ String getCodeFile();
+
+ String getCodeLine();
+
+ String getCodeFunc();
+
+ String getErrno();
+
+ String getSyslogFacility();
+
+ String getSyslogIdentifier();
+
+ String getSyslogPid();
+
+ String getSyslogTimestamp();
+
+ String getSyslogRaw();
+
+ String getPid();
+
+ String getUid();
+
+ String getGid();
+
+ String getComm();
+
+ String getExe();
+
+ String getCmdLine();
+
+ String getCapEffective();
+
+ String getAuditSession();
+
+ String getAuditLoginUid();
+
+ String getSystemdCgroup();
+
+ String getSystemdSlice();
+
+ String getSystemdUnit();
+
+ String getSystemdUserSlice();
+
+ String getSystemdUserUnit();
+
+ String getSystemdSession();
+
+ String getSystemdOwnerUid();
+
+ String getSelinuxContext();
+
+ String getSourceRealtimeTimestamp();
+
+ String getBootId();
+
+ String getMachineId();
+
+ String getSystemdInvocationId();
+
+ String getHostname();
+
+ String getTransport();
+
+ String getStreamId();
+
+ String getLineBreak();
+
+ String getFieldValue(String fieldName);
+}
diff --git a/event-journald/src/main/java/org/xbib/event/journald/SystemdJournalConsumer.java b/event-journald/src/main/java/org/xbib/event/journald/SystemdJournalConsumer.java
new file mode 100644
index 0000000..7c1ce10
--- /dev/null
+++ b/event-journald/src/main/java/org/xbib/event/journald/SystemdJournalConsumer.java
@@ -0,0 +1,240 @@
+package org.xbib.event.journald;
+
+import com.sun.jna.Memory;
+import com.sun.jna.Native;
+import com.sun.jna.Pointer;
+import com.sun.jna.StringArray;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import static org.xbib.event.journald.SystemdLibraryInstance.SD_JOURNAL_LOCAL_ONLY;
+import static org.xbib.event.journald.SystemdLibraryInstance.SD_JOURNAL_NOP;
+import static org.xbib.event.journald.SystemdLibraryInstance.SD_JOURNAL_SYSTEM;
+
+public class SystemdJournalConsumer implements Runnable {
+
+ private static final Logger logger = Logger.getLogger(SystemdJournalConsumer.class.getName());
+
+ private final String match;
+
+ private final String field;
+
+ private final SystemdJournalListener listener;
+
+ public SystemdJournalConsumer(String match, SystemdJournalListener listener) {
+ this(match, null, listener);
+ }
+
+ public SystemdJournalConsumer(String match, String field, SystemdJournalListener listener) {
+ this.match = match;
+ this.field = field;
+ this.listener = listener;
+ }
+
+ @Override
+ public void run() {
+ try {
+ loop();
+ } catch (Throwable e) {
+ logger.log(Level.SEVERE, e.getMessage(), e);
+ }
+ }
+
+ /**
+ * ...
+ * @throws IOException if loop fails
+ */
+ private void loop() throws IOException {
+ SystemdLibraryInstance api = SystemdLibraryInstance.getInstance();
+ SystemdLibrary.SdJournal sdJournal = new SystemdLibrary.SdJournal();
+ int rc = api.sd_journal_open(sdJournal, SD_JOURNAL_LOCAL_ONLY);
+ if (rc < 0) {
+ throw new IOException("error opening journal for read: " + rc);
+ }
+ logger.log(Level.INFO, "top fd = " + sdJournal.top_level_fd +
+ " path = " + sdJournal.path +
+ " prefix = " + sdJournal.prefix +
+ " namespace = " + sdJournal.namespace);
+ if (match != null) {
+ rc = api.sd_journal_add_match(sdJournal, match, match.length());
+ logger.log(Level.INFO, "add_match: " + rc);
+ if (rc < 0) {
+ throw new IOException("error in add_match: " + rc);
+ }
+ }
+ rc = api.sd_journal_get_fd(sdJournal);
+ logger.log(Level.INFO, "get_fd: " + rc);
+ if (rc < 0) {
+ throw new IOException("failed to get file descriptor: " + rc);
+ }
+ rc = api.sd_journal_seek_tail(sdJournal);
+ logger.log(Level.INFO, "seek_tail: " + rc);
+ if (rc < 0) {
+ throw new IOException("failed to seek to tail of journal");
+ }
+ rc = api.sd_journal_previous(sdJournal);
+ logger.log(Level.INFO, "previous: " + rc);
+ rc = api.sd_journal_next(sdJournal);
+ logger.log(Level.INFO, "next: " + rc);
+ String[] strings = new String[1];
+ StringArray cursor = new StringArray(strings);
+ rc = api.sd_journal_get_cursor(sdJournal, cursor);
+ logger.log(Level.INFO, "get_cursor: " + rc);
+ System.exit(1);
+ while (true) {
+ do {
+ rc = api.sd_journal_wait(sdJournal, -1L);
+ logger.log(Level.INFO, "wait rc = " + rc);
+ } while (rc == SD_JOURNAL_NOP);
+ while ((rc = api.sd_journal_next(sdJournal)) > 0) {
+ logger.log(Level.INFO, "next: " + rc);
+ if (field != null) {
+ Pointer dataPointer = new Memory(Native.POINTER_SIZE);
+ Pointer sizePointer = new Memory(Native.POINTER_SIZE);
+ rc = api.sd_journal_get_data(sdJournal, field, dataPointer, sizePointer);
+ logger.log(Level.INFO, "get_data: " + rc);
+ if (rc != 0) {
+ throw new IOException("error in get_data: " + rc);
+ }
+ int size = sizePointer.getInt(0);
+ byte[] b = dataPointer.getByteArray(0, size);
+ String s = new String(b, StandardCharsets.UTF_8);
+ if (listener != null) {
+ listener.handleEntry(makeEntry(Collections.singletonList(s)));
+ }
+ } else {
+ String[] strings2 = new String[1];
+ StringArray nextCursor = new StringArray(strings2);
+ rc = api.sd_journal_get_cursor(sdJournal, nextCursor);
+ logger.log(Level.INFO, "get_cursor: " + rc);
+ if (!cursor.getString(0).equals(nextCursor.getString(0))) {
+ cursor = nextCursor;
+ Pointer dataPointer = new Memory(Native.POINTER_SIZE);
+ Pointer sizePointer = new Memory(Native.POINTER_SIZE);
+ List list = new ArrayList<>();
+ while ((rc = api.sd_journal_enumerate_data(sdJournal, dataPointer, sizePointer)) > 0) {
+ logger.log(Level.INFO, "enumerate_data: " + rc);
+ int size = sizePointer.getInt(0);
+ byte[] b = dataPointer.getByteArray(0, size);
+ String s = new String(b, StandardCharsets.UTF_8);
+ list.add(s);
+ }
+ rc = api.sd_journal_restart_data(sdJournal);
+ logger.log(Level.INFO, "restart_data: " + rc);
+ if (listener != null) {
+ listener.handleEntry(makeEntry(list));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private JournalEntry makeEntry(List list) {
+ DefaultJournalEntry journalEntry = new DefaultJournalEntry();
+ for (String string : list) {
+ if (string.startsWith("MESSAGE=")) {
+ journalEntry.setMessage(string.substring(8));
+ continue;
+ }
+ if (string.startsWith("MESSAGE_ID=")) {
+ journalEntry.setMessageId(string.substring(11));
+ continue;
+ }
+ if (string.startsWith("PRIORITY=")) {
+ journalEntry.setPriority(Integer.parseInt(string.substring(9)));
+ continue;
+ }
+ if (string.startsWith("CODE_FILE=")) {
+ journalEntry.setCodeFile(string.substring(10));
+ continue;
+ }
+ if (string.startsWith("CODE_LINE=")) {
+ journalEntry.setCodeLine(string.substring(10));
+ continue;
+ }
+ if (string.startsWith("CODE_FUNC=")) {
+ journalEntry.setCodeFunc(string.substring(10));
+ continue;
+ }
+ if (string.startsWith("ERRNO=")) {
+ journalEntry.setErrno(string.substring(6));
+ continue;
+ }
+ if (string.startsWith("SYSLOG_FACILITY=")) {
+ journalEntry.setSyslogFacility(string.substring(16));
+ continue;
+ }
+ if (string.startsWith("SYSLOG_IDENTIFIER=")) {
+ journalEntry.setSyslogIdentifier(string.substring(18));
+ continue;
+ }
+ if (string.startsWith("SYSLOG_PID=")) {
+ journalEntry.setSyslogPid(string.substring(11));
+ continue;
+ }
+ if (string.startsWith("SYSLOG_TIMESTAMP=")) {
+ journalEntry.setSyslogTimestamp(string.substring(17));
+ continue;
+ }
+ if (string.startsWith("SYSLOG_RAW=")) {
+ journalEntry.setSyslogRaw(string.substring(11));
+ continue;
+ }
+ if (string.startsWith("_PID=")) {
+ journalEntry.setPid(string.substring(5));
+ continue;
+ }
+ if (string.startsWith("_UID=")) {
+ journalEntry.setUid(string.substring(5));
+ continue;
+ }
+ if (string.startsWith("_GID=")) {
+ journalEntry.setGid(string.substring(5));
+ continue;
+ }
+ if (string.startsWith("_COMM=")) {
+ journalEntry.setComm(string.substring(6));
+ continue;
+ }
+ if (string.startsWith("_EXE=")) {
+ journalEntry.setExe(string.substring(5));
+ continue;
+ }
+ if (string.startsWith("_CMDLINE=")) {
+ journalEntry.setCmdLine(string.substring(9));
+ continue;
+ }
+ if (string.startsWith("_CAP_EFFECTIVE=")) {
+ journalEntry.setCapEffective(string.substring(15));
+ continue;
+ }
+ if (string.startsWith("_TRANSPORT=")) {
+ journalEntry.setTransport(string.substring(11));
+ continue;
+ }
+ if (string.startsWith("_SYSTEMD_OWNER_UID=")) {
+ journalEntry.setSystemdOwnerUid(string.substring(19));
+ continue;
+ }
+ if (string.startsWith("_SYSTEMD_UNIT=")) {
+ journalEntry.setSystemdUnit(string.substring(13));
+ continue;
+ }
+ if (string.startsWith("_SYSTEMD_USER_SLICE=")) {
+ journalEntry.setSystemdUserSlice(string.substring(19));
+ continue;
+ }
+ if (string.startsWith("_SYSTEMD_USER_UNIT=")) {
+ journalEntry.setSystemdUserUnit(string.substring(18));
+ }
+ }
+ return journalEntry;
+ }
+}
diff --git a/event-journald/src/main/java/org/xbib/event/journald/SystemdJournalListener.java b/event-journald/src/main/java/org/xbib/event/journald/SystemdJournalListener.java
new file mode 100644
index 0000000..88fb155
--- /dev/null
+++ b/event-journald/src/main/java/org/xbib/event/journald/SystemdJournalListener.java
@@ -0,0 +1,8 @@
+package org.xbib.event.journald;
+
+import java.io.IOException;
+
+public interface SystemdJournalListener {
+
+ void handleEntry(JournalEntry entry) throws IOException;
+}
diff --git a/event-journald/src/main/java/org/xbib/event/journald/SystemdLibrary.java b/event-journald/src/main/java/org/xbib/event/journald/SystemdLibrary.java
new file mode 100644
index 0000000..fdd53ac
--- /dev/null
+++ b/event-journald/src/main/java/org/xbib/event/journald/SystemdLibrary.java
@@ -0,0 +1,49 @@
+package org.xbib.event.journald;
+
+import com.sun.jna.Library;
+import com.sun.jna.Pointer;
+import com.sun.jna.StringArray;
+import com.sun.jna.Structure;
+import java.util.Arrays;
+import java.util.List;
+
+public interface SystemdLibrary extends Library {
+
+ int sd_notify(int fd, String msg);
+
+ int sd_journal_send(String format, Object... args);
+
+ int sd_journal_open(SdJournal sdJournal, int flag);
+
+ int sd_journal_get_fd(SdJournal sdJournal);
+
+ int sd_journal_seek_tail(SdJournal sdJournal);
+
+ int sd_journal_previous(SdJournal sdJournal);
+
+ int sd_journal_next(SdJournal sdJournal);
+
+ int sd_journal_get_cursor(SdJournal sdJournal, StringArray cursor);
+
+ int sd_journal_add_match(SdJournal sdJournal, String match, int len);
+
+ int sd_journal_wait(SdJournal sdJournal, long timeout);
+
+ int sd_journal_get_data(SdJournal sdJournal, String field, Pointer data, Pointer length);
+
+ int sd_journal_enumerate_data(SdJournal sdJournal, Pointer data, Pointer length);
+
+ int sd_journal_restart_data(SdJournal sdJournal);
+
+ class SdJournal extends Structure {
+ public int top_level_fd;
+ public String path;
+ public String prefix;
+ public String namespace;
+
+ @Override
+ protected List getFieldOrder() {
+ return Arrays.asList("top_level_fd","path","prefix","namespace");
+ }
+ }
+}
diff --git a/event-journald/src/main/java/org/xbib/event/journald/SystemdLibraryInstance.java b/event-journald/src/main/java/org/xbib/event/journald/SystemdLibraryInstance.java
new file mode 100644
index 0000000..dabfafe
--- /dev/null
+++ b/event-journald/src/main/java/org/xbib/event/journald/SystemdLibraryInstance.java
@@ -0,0 +1,105 @@
+package org.xbib.event.journald;
+
+import com.sun.jna.Native;
+import com.sun.jna.Pointer;
+import com.sun.jna.StringArray;
+
+import java.util.List;
+
+/**
+ * The systemd library API, loaded by Java Native Access (JNA).
+ *
+ * The native library is loaded only once, so this class is a singleton.
+ */
+public class SystemdLibraryInstance {
+
+ private static final SystemdLibraryInstance instance = new SystemdLibraryInstance();
+
+ private final SystemdLibrary systemdLibrary;
+
+ private SystemdLibraryInstance() {
+ this.systemdLibrary = loadLibrary();
+ }
+
+ public static SystemdLibraryInstance getInstance() {
+ return instance;
+ }
+
+ public static final int SD_JOURNAL_LOCAL_ONLY = 1;
+
+ public static final int SD_JOURNAL_RUNTIME_ONLY = 2;
+
+ public static final int SD_JOURNAL_SYSTEM = 4;
+
+ public static final int SD_JOURNAL_CURRENT_USER = 8;
+
+ public static final int SD_JOURNAL_NOP = 0;
+
+ public static final int SD_JOURNAL_APPEND = 1;
+
+ public static final int SD_JOURNAL_INVALIDATE = 2;
+
+ public static final String SD_ID128_FORMAT_STR = "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x";
+
+ public int sd_journal_send(String format, Object... args) {
+ return systemdLibrary.sd_journal_send(format, args);
+ }
+
+ public int sd_journal_send(String format, List