diff --git a/gradle.properties b/gradle.properties index 33eedc6..b611b88 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ group = org.xbib name = systemd-journal version = 2.0.0 -gradle.wrapper.version = 6.4.1 +gradle.wrapper.version = 6.6.1 bridj.version = 0.7.0 jna.version = 5.5.0 log4j.version = 2.13.3 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 21e622d..33682bb 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index fbd7c51..4f906e0 100755 --- a/gradlew +++ b/gradlew @@ -130,7 +130,7 @@ fi if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath diff --git a/gradlew.bat b/gradlew.bat index a9f778a..ac1b06f 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -54,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -64,21 +64,6 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line @@ -86,7 +71,7 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell diff --git a/settings.gradle b/settings.gradle index c63affa..ac65471 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,3 @@ -include 'systemd-journal' include 'log4j-systemd-journal' +include 'systemd-journal-bridj' +include 'systemd-journal-jna' diff --git a/systemd-journal/NOTICE.txt b/systemd-journal-bridj/NOTICE.txt similarity index 100% rename from systemd-journal/NOTICE.txt rename to systemd-journal-bridj/NOTICE.txt diff --git a/systemd-journal/build.gradle b/systemd-journal-bridj/build.gradle similarity index 100% rename from systemd-journal/build.gradle rename to systemd-journal-bridj/build.gradle diff --git a/systemd-journal/src/main/java/org/xbib/systemd/journal/DefaultJournalEntry.java b/systemd-journal-bridj/src/main/java/org/xbib/systemd/journal/DefaultJournalEntry.java similarity index 100% rename from systemd-journal/src/main/java/org/xbib/systemd/journal/DefaultJournalEntry.java rename to systemd-journal-bridj/src/main/java/org/xbib/systemd/journal/DefaultJournalEntry.java diff --git a/systemd-journal/src/main/java/org/xbib/systemd/journal/JournalEntry.java b/systemd-journal-bridj/src/main/java/org/xbib/systemd/journal/JournalEntry.java similarity index 100% rename from systemd-journal/src/main/java/org/xbib/systemd/journal/JournalEntry.java rename to systemd-journal-bridj/src/main/java/org/xbib/systemd/journal/JournalEntry.java diff --git a/systemd-journal/src/main/java/org/xbib/systemd/journal/Syslog.java b/systemd-journal-bridj/src/main/java/org/xbib/systemd/journal/Syslog.java similarity index 100% rename from systemd-journal/src/main/java/org/xbib/systemd/journal/Syslog.java rename to systemd-journal-bridj/src/main/java/org/xbib/systemd/journal/Syslog.java diff --git a/systemd-journal/src/main/java/org/xbib/systemd/journal/SystemdJournalConsumer.java b/systemd-journal-bridj/src/main/java/org/xbib/systemd/journal/SystemdJournalConsumer.java similarity index 100% rename from systemd-journal/src/main/java/org/xbib/systemd/journal/SystemdJournalConsumer.java rename to systemd-journal-bridj/src/main/java/org/xbib/systemd/journal/SystemdJournalConsumer.java diff --git a/systemd-journal/src/main/java/org/xbib/systemd/journal/SystemdJournalLibrary.java b/systemd-journal-bridj/src/main/java/org/xbib/systemd/journal/SystemdJournalLibrary.java similarity index 100% rename from systemd-journal/src/main/java/org/xbib/systemd/journal/SystemdJournalLibrary.java rename to systemd-journal-bridj/src/main/java/org/xbib/systemd/journal/SystemdJournalLibrary.java diff --git a/systemd-journal/src/main/java/org/xbib/systemd/journal/SystemdJournalListener.java b/systemd-journal-bridj/src/main/java/org/xbib/systemd/journal/SystemdJournalListener.java similarity index 100% rename from systemd-journal/src/main/java/org/xbib/systemd/journal/SystemdJournalListener.java rename to systemd-journal-bridj/src/main/java/org/xbib/systemd/journal/SystemdJournalListener.java diff --git a/systemd-journal/src/main/java/org/xbib/systemd/journal/sd_id128.java b/systemd-journal-bridj/src/main/java/org/xbib/systemd/journal/sd_id128.java similarity index 100% rename from systemd-journal/src/main/java/org/xbib/systemd/journal/sd_id128.java rename to systemd-journal-bridj/src/main/java/org/xbib/systemd/journal/sd_id128.java diff --git a/systemd-journal/src/test/java/org/xbib/systemd/journal/SystemdJournalReaderTest.java b/systemd-journal-bridj/src/test/java/org/xbib/systemd/journal/SystemdJournalReaderTest.java similarity index 100% rename from systemd-journal/src/test/java/org/xbib/systemd/journal/SystemdJournalReaderTest.java rename to systemd-journal-bridj/src/test/java/org/xbib/systemd/journal/SystemdJournalReaderTest.java diff --git a/systemd-journal-jna/build.gradle b/systemd-journal-jna/build.gradle new file mode 100644 index 0000000..df5c72e --- /dev/null +++ b/systemd-journal-jna/build.gradle @@ -0,0 +1,4 @@ +dependencies { + implementation "net.java.dev.jna:jna:${project.property('jna.version')}" + testImplementation "org.mockito:mockito-junit-jupiter:${project.property('mockito.version')}" +} diff --git a/systemd-journal-jna/src/main/java/org/xbib/systemd/journal/DefaultJournalEntry.java b/systemd-journal-jna/src/main/java/org/xbib/systemd/journal/DefaultJournalEntry.java new file mode 100644 index 0000000..6e386ec --- /dev/null +++ b/systemd-journal-jna/src/main/java/org/xbib/systemd/journal/DefaultJournalEntry.java @@ -0,0 +1,421 @@ +package org.xbib.systemd.journal; + +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/systemd-journal-jna/src/main/java/org/xbib/systemd/journal/JournalEntry.java b/systemd-journal-jna/src/main/java/org/xbib/systemd/journal/JournalEntry.java new file mode 100644 index 0000000..a4940a7 --- /dev/null +++ b/systemd-journal-jna/src/main/java/org/xbib/systemd/journal/JournalEntry.java @@ -0,0 +1,80 @@ +package org.xbib.systemd.journal; + +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/systemd-journal-jna/src/main/java/org/xbib/systemd/journal/SdJournal.java b/systemd-journal-jna/src/main/java/org/xbib/systemd/journal/SdJournal.java new file mode 100644 index 0000000..2fa2a0e --- /dev/null +++ b/systemd-journal-jna/src/main/java/org/xbib/systemd/journal/SdJournal.java @@ -0,0 +1,6 @@ +package org.xbib.systemd.journal; + +import com.sun.jna.Structure; + +public class SdJournal extends Structure { +} diff --git a/systemd-journal-jna/src/main/java/org/xbib/systemd/journal/SystemdJournalConsumer.java b/systemd-journal-jna/src/main/java/org/xbib/systemd/journal/SystemdJournalConsumer.java new file mode 100644 index 0000000..6ab9380 --- /dev/null +++ b/systemd-journal-jna/src/main/java/org/xbib/systemd/journal/SystemdJournalConsumer.java @@ -0,0 +1,218 @@ +package org.xbib.systemd.journal; + +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.systemd.journal.SystemdLibraryAPI.SD_JOURNAL_LOCAL_ONLY; + +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 (Exception e) { + logger.log(Level.SEVERE, e.getMessage(), e); + } + } + + private void loop() throws IOException { + SystemdLibraryAPI api = SystemdLibraryAPI.getInstance(); + SdJournal sdJournal = new SdJournal() { + @Override + public int hashCode() { + return super.hashCode(); + } + }; + int rc = api.sd_journal_open(sdJournal, SD_JOURNAL_LOCAL_ONLY); + if (rc < 0) { + logger.log(Level.WARNING, "error opening journal for read: " + rc); + } + if (match != null) { + rc = api.sd_journal_add_match(sdJournal, match, match.length()); + if (rc < 0) { + logger.log(Level.WARNING, "error in add_match: " + rc); + } + } + rc = api.sd_journal_get_fd(sdJournal); + rc = api.sd_journal_seek_tail(sdJournal); + rc = api.sd_journal_previous(sdJournal); + rc = api.sd_journal_next(sdJournal); + String[] strings = new String[1]; + StringArray cursor = new StringArray(strings); + api.sd_journal_get_cursor(sdJournal, cursor); + while (true) { + do { + rc = api.sd_journal_wait(sdJournal, -1); + } while (rc == 0); // NOP + while (api.sd_journal_next(sdJournal) > 0) { + 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); + 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); + api.sd_journal_get_cursor(sdJournal, nextCursor); + 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 (api.sd_journal_enumerate_data(sdJournal, dataPointer, sizePointer) > 0) { + //Pointer data = dataPointer.as(Byte.class); + //String line = data.getPointer(Byte.class).getCString(); + 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); + 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/systemd-journal-jna/src/main/java/org/xbib/systemd/journal/SystemdJournalListener.java b/systemd-journal-jna/src/main/java/org/xbib/systemd/journal/SystemdJournalListener.java new file mode 100644 index 0000000..e60e14a --- /dev/null +++ b/systemd-journal-jna/src/main/java/org/xbib/systemd/journal/SystemdJournalListener.java @@ -0,0 +1,8 @@ +package org.xbib.systemd.journal; + +import java.io.IOException; + +public interface SystemdJournalListener { + + void handleEntry(JournalEntry entry) throws IOException; +} diff --git a/systemd-journal-jna/src/main/java/org/xbib/systemd/journal/SystemdLibrary.java b/systemd-journal-jna/src/main/java/org/xbib/systemd/journal/SystemdLibrary.java new file mode 100644 index 0000000..9104c21 --- /dev/null +++ b/systemd-journal-jna/src/main/java/org/xbib/systemd/journal/SystemdLibrary.java @@ -0,0 +1,32 @@ +package org.xbib.systemd.journal; + +import com.sun.jna.Library; +import com.sun.jna.Pointer; +import com.sun.jna.StringArray; + +public interface SystemdLibrary extends Library { + + 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); +} diff --git a/systemd-journal-jna/src/main/java/org/xbib/systemd/journal/SystemdLibraryAPI.java b/systemd-journal-jna/src/main/java/org/xbib/systemd/journal/SystemdLibraryAPI.java new file mode 100644 index 0000000..a971a34 --- /dev/null +++ b/systemd-journal-jna/src/main/java/org/xbib/systemd/journal/SystemdLibraryAPI.java @@ -0,0 +1,105 @@ +package org.xbib.systemd.journal; + +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 SystemdLibraryAPI { + + private static final SystemdLibraryAPI instance = new SystemdLibraryAPI(); + + private final SystemdLibrary systemdLibrary; + + private SystemdLibraryAPI() { + this.systemdLibrary = loadLibrary(); + } + + public static SystemdLibraryAPI 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 args) { + return systemdLibrary.sd_journal_send(format, args.toArray()); + } + + public int sd_journal_open(SdJournal sdJournal, int flag) { + return systemdLibrary.sd_journal_open(sdJournal, flag); + } + + public int sd_journal_get_fd(SdJournal sdJournal) { + return systemdLibrary.sd_journal_get_fd(sdJournal); + } + + public int sd_journal_seek_tail(SdJournal sdJournal) { + return systemdLibrary.sd_journal_seek_tail(sdJournal); + } + + public int sd_journal_previous(SdJournal sdJournal) { + return systemdLibrary.sd_journal_previous(sdJournal); + } + + public int sd_journal_next(SdJournal sdJournal) { + return systemdLibrary.sd_journal_next(sdJournal); + } + + public int sd_journal_get_cursor(SdJournal sdJournal, StringArray cursor) { + return systemdLibrary.sd_journal_get_cursor(sdJournal, cursor); + } + + public int sd_journal_add_match(SdJournal sdJournal, String match, int len) { + return systemdLibrary.sd_journal_add_match(sdJournal, match, len); + } + + public int sd_journal_wait(SdJournal sdJournal, long timeout_musec) { + return systemdLibrary.sd_journal_wait(sdJournal, timeout_musec); + } + + public int sd_journal_get_data(SdJournal sdJournal, String field, Pointer data, Pointer length) { + return systemdLibrary.sd_journal_get_data(sdJournal, field, data, length); + } + + public int sd_journal_enumerate_data(SdJournal sdJournal, Pointer data, Pointer length) { + return systemdLibrary.sd_journal_enumerate_data(sdJournal, data, length); + } + + public int sd_journal_restart_data(SdJournal sdJournal) { + return systemdLibrary.sd_journal_restart_data(sdJournal); + } + + private static SystemdLibrary loadLibrary() { + try { + return Native.load("systemd", SystemdLibrary.class); + } catch (UnsatisfiedLinkError e) { + throw new RuntimeException("Failed to load systemd library." + + " Please note that JNA requires an executable temporary folder." + + " It can be explicitly defined with -Djna.tmpdir", e); + } + } +} diff --git a/systemd-journal-jna/src/test/java/org/xbib/systemd/journal/SystemdJournalReaderTest.java b/systemd-journal-jna/src/test/java/org/xbib/systemd/journal/SystemdJournalReaderTest.java new file mode 100644 index 0000000..1936dd3 --- /dev/null +++ b/systemd-journal-jna/src/test/java/org/xbib/systemd/journal/SystemdJournalReaderTest.java @@ -0,0 +1,34 @@ +package org.xbib.systemd.journal; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.concurrent.Executors; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; + +@EnabledOnOs({OS.LINUX}) +@ExtendWith(MockitoExtension.class) +public class SystemdJournalReaderTest { + + private static final Logger logger = Logger.getLogger(SystemdJournalReaderTest.class.getName()); + + @Mock + private SystemdLibraryAPI api; + + @Test + void testConsumer() throws InterruptedException { + SystemdJournalConsumer consumer = new SystemdJournalConsumer(null, + entry -> logger.log(Level.INFO, entry.toString())); + Executors.newSingleThreadExecutor().submit(consumer); + // consuming for some seconds + Thread.sleep(10000L); + } +}