summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore8
-rw-r--r--Makefile40
-rw-r--r--README.md70
-rwxr-xr-xexamples/journal-notify-dhcpd.desktop9
-rwxr-xr-xexamples/journal-notify-sshd.desktop9
-rw-r--r--journal-notify.c175
-rw-r--r--screenshot.pngbin0 -> 6817 bytes
7 files changed, 311 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..efc341b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+*~
+*.o
+config.h
+journal-notify
+README.html
+version.h
+journal-notify-*.tar.xz
+journal-notify-*.tar.xz.asc
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..974fc39
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,40 @@
+# journal-notify - Notify about journal log entries
+
+CC := gcc
+MD := markdown
+INSTALL := install
+CP := cp
+RM := rm
+CFLAGS += -O2 -Wall -Werror
+CFLAGS += $(shell pkg-config --cflags --libs libsystemd)
+CFLAGS += $(shell pkg-config --cflags --libs libnotify)
+# this is just a fallback in case you do not use git but downloaded
+# a release tarball...
+VERSION := 0.0.1
+
+all: journal-notify README.html
+
+journal-notify: journal-notify.c
+ $(CC) $(CFLAGS) -o journal-notify journal-notify.c
+
+README.html: README.md
+ $(MD) README.md > README.html
+
+install: install-bin install-doc
+
+install-bin: journal-notify
+ $(INSTALL) -D -m0755 journal-notify $(DESTDIR)/usr/bin/journal-notify
+ $(INSTALL) -D -m0755 examples/journal-notify-dhcpd.desktop $(DESTDIR)/usr/share/journal-notify/examples/journal-notify-dhcpd.desktop
+ $(INSTALL) -D -m0755 examples/journal-notify-sshd.desktop $(DESTDIR)/usr/share/journal-notify/examples/journal-notify-sshd.desktop
+
+install-doc: README.html
+ $(INSTALL) -D -m0644 README.md $(DESTDIR)/usr/share/doc/journal-notify/README.md
+ $(INSTALL) -D -m0644 README.html $(DESTDIR)/usr/share/doc/journal-notify/README.html
+ $(INSTALL) -D -m0644 screenshot.png $(DESTDIR)/usr/share/doc/journal-notify/screenshot.png
+
+clean:
+ rm -f *.o *~ README.html journal-notify
+
+release:
+ git archive --format=tar.xz --prefix=journal-notify$(VERSION)/ $(VERSION) > journal-notify-$(VERSION).tar.xz
+ gpg -ab journal-notify-$(VERSION).tar.xz
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c28a27d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,70 @@
+journal-notify
+==============
+
+**Notify about journal log entries**
+
+This runs in background and produces notifications whenever systemd journal
+logs an entry. Notifications look like this:
+
+![Notification](screenshot.png)
+
+Requirements
+------------
+
+To compile and run `journal-notify` you need:
+
+* [libnotify](http://library.gnome.org/devel/notification-spec/)
+* [libsystemd](http://www.freedesktop.org/wiki/Software/systemd/)
+* [markdown](http://daringfireball.net/projects/markdown/) (HTML
+ documentation)
+* `gnome-icon-theme` (or whatever includes the icons you want to use)
+
+To use `journal-notify` you probably want `systemd-journald`.
+
+Some systems may require additional development packages for the libraries.
+Look for `libnotify-devel`, `libsystemd-devel` or similar.
+
+Build and install
+-----------------
+
+Building and installing is very easy. Just run:
+
+> make
+
+followed by:
+
+> make install
+
+This will place an executable at `/usr/bin/journal-notify`,
+documentation can be found in `/usr/share/doc/journal-notify/`.
+
+Usage
+-----
+
+To use `journal-notify` you need access to the journal log files. Make sure
+you get the expexted log entries by running `journalctl`. Possibly you have
+to add yourself to group `systemd-journal` or similar.
+
+Just running `journal-notify` without parameter will show *all* log entries.
+Be warned: This can flood your disktop with notifications.
+
+`journal-notify` accepts some options:
+
+* *-e*: use extended regular expressions
+* *-h*: show help
+* *-i ICON*: icon to use
+* *-m MATCH*: This can be specified more than once. The option accepts matches
+ to systemd journal fields. (see `man 7 systemd.journal-fields`)
+* *-n*: no case sensitive regular expressions
+* *-r REGEX*: This add a regular expression match for the message field.
+
+The screenshot shown above resulted from this command:
+
+> journal-notify -m SYSLOG_IDENTIFIER=sshd -e -r "^(error:.\*|Accepted.\*)"
+-i security-high
+
+Additionally example desktop files are installed to
+`/usr/share/journal-notify/examples`. You should copy them to
+`~/.config/autostart/` to enable autostart or create your own desktop files
+there.
+
diff --git a/examples/journal-notify-dhcpd.desktop b/examples/journal-notify-dhcpd.desktop
new file mode 100755
index 0000000..092d6c4
--- /dev/null
+++ b/examples/journal-notify-dhcpd.desktop
@@ -0,0 +1,9 @@
+[Desktop Entry]
+Name=journal-notify-dhcpd
+GenericName=Journal-Notify DHCPD
+Comment=Notify about DHCP activities
+Exec=journal-notify -m SYSLOG_IDENTIFIER=dhcpd -r "^DHCPN?ACK" -i network-wired
+StartupNotify=false
+Terminal=false
+Type=Application
+Categories=
diff --git a/examples/journal-notify-sshd.desktop b/examples/journal-notify-sshd.desktop
new file mode 100755
index 0000000..688da64
--- /dev/null
+++ b/examples/journal-notify-sshd.desktop
@@ -0,0 +1,9 @@
+[Desktop Entry]
+Name=journal-notify-sshd
+GenericName=Journal-Notify SSHD
+Comment=Notify about SSH activities
+Exec=journal-notify -m SYSLOG_IDENTIFIER=sshd -e -r "^(error:.*|Accepted.*)" -i security-high
+StartupNotify=false
+Terminal=false
+Type=Application
+Categories=
diff --git a/journal-notify.c b/journal-notify.c
new file mode 100644
index 0000000..7e78232
--- /dev/null
+++ b/journal-notify.c
@@ -0,0 +1,175 @@
+/*
+ * (C) 2014 by Christian Hesse <mail@eworm.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <regex.h>
+
+#include <systemd/sd-journal.h>
+
+#include <libnotify/notify.h>
+
+const char * program = NULL;
+
+#define OPTSTRING "ehi:m:nr:"
+#define DEFAULTICON "dialog-information"
+
+void notify(const char * summary, const char * body, const char * icon) {
+ NotifyNotification * notification = notify_notification_new(summary, body, icon);
+ notify_notification_show(notification, NULL);
+ g_object_unref(G_OBJECT(notification));
+}
+
+int main(int argc, char **argv) {
+ int i, rc = EXIT_FAILURE;
+
+ uint8_t have_regex = 0;
+ regex_t regex;
+ int regex_flags = REG_NOSUB;
+
+ sd_journal * journal;
+ const void * data;
+ size_t length;
+
+ char * summary, * message;
+ const char *summarystr, * messagestr, * icon = DEFAULTICON;
+
+ program = argv[0];
+
+ /* get command line options - part I
+ * just get -h (help), -e and -n (regex options) here */
+ while ((i = getopt(argc, argv, OPTSTRING)) != -1) {
+ switch (i) {
+ case 'e':
+ regex_flags |= REG_EXTENDED;
+ break;
+ case 'h':
+ fprintf(stderr, "usage: %s [-e] [-h] [-m MATCH] [-n] [-r REGEX]\n", program);
+ return EXIT_SUCCESS;
+ case 'n':
+ regex_flags |= REG_ICASE;
+ break;
+ }
+ }
+
+ /* reinitialize getopt() by resetting optind to 0 */
+ optind = 0;
+
+ /* open journal */
+ if ((rc = sd_journal_open(&journal, SD_JOURNAL_LOCAL_ONLY + SD_JOURNAL_SYSTEM)) < 0) {
+ fprintf(stderr, "Failed to open journal: %s\n", strerror(-rc));
+ goto out10;
+ }
+
+ /* get command line options - part II*/
+ while ((i = getopt(argc, argv, OPTSTRING)) != -1) {
+ switch (i) {
+ case 'i':
+ icon = optarg;
+ break;
+ case 'm':
+ if ((rc = sd_journal_add_match(journal, optarg, 0)) < 0) {
+ fprintf(stderr, "Failed to add match '%s': %s\n", optarg, strerror(-rc));
+ goto out20;
+ }
+
+ break;
+ case 'r':
+ if (have_regex > 0) {
+ fprintf(stderr, "Only one regex allowed!\n");
+ rc = EXIT_FAILURE;
+ goto out20;
+ }
+
+ if ((rc = regcomp(&regex, optarg, regex_flags)) != 0) {
+ fprintf(stderr, "Could not compile regex\n");
+ goto out20;
+ }
+ have_regex++;
+
+ break;
+ }
+ }
+
+ /* seek to the end of the journal */
+ if ((rc = sd_journal_seek_tail(journal)) < 0) {
+ fprintf(stderr, "Failed to seek to the tail: %s\n", strerror(-rc));
+ goto out30;
+ }
+
+ /* we are behind the last entry, so use previous one */
+ if ((rc = sd_journal_previous(journal)) < 0) {
+ fprintf(stderr, "Failed to iterate to previous entry: %s\n", strerror(-rc));
+ goto out30;
+ }
+
+ if (notify_init(program) == FALSE) {
+ fprintf(stderr, "Failed to initialize notify.\n");
+ rc = EXIT_FAILURE;
+ goto out30;
+ }
+
+ while (1) {
+ if ((rc = sd_journal_next(journal)) < 0) {
+ fprintf(stderr, "Failed to iterate to next entry: %s\n", strerror(-rc));
+ goto out40;
+ } else if (rc == 0) {
+ if ((rc = sd_journal_wait(journal, (uint64_t) -1)) < 0) {
+ fprintf(stderr, "Failed to wait for changes: %s\n", strerror(-rc));
+ goto out40;
+ }
+ continue;
+ }
+
+ /* get MESSAGE field */
+ if ((rc = sd_journal_get_data(journal, "MESSAGE", &data, &length)) < 0) {
+ fprintf(stderr, "Failed to read message field: %s\n", strerror(-rc));
+ continue;
+ }
+
+ message = strndup(data, length);
+ messagestr = message + 8;
+
+ /* get SYSLOG_IDENTIFIER field */
+ if ((rc = sd_journal_get_data(journal, "SYSLOG_IDENTIFIER", &data, &length)) < 0) {
+ fprintf(stderr, "Failed to read syslog identifier field: %s\n", strerror(-rc));
+ continue;
+ }
+ summary = strndup(data, length);
+ summarystr = summary + 18;
+
+ /* show notification */
+ if (have_regex > 0) {
+ if (regexec(&regex, messagestr, 0, NULL, 0) == 0) {
+ notify(summarystr, messagestr, icon);
+ }
+ } else {
+ notify(summarystr, messagestr, icon);
+ }
+
+ free(summary);
+ free(message);
+ }
+
+ rc = EXIT_SUCCESS;
+
+out40:
+ notify_uninit();
+
+out30:
+ if (have_regex > 0)
+ regfree(&regex);
+
+out20:
+ sd_journal_close(journal);
+
+out10:
+ return rc;
+}
+
+// vim: set syntax=c:
diff --git a/screenshot.png b/screenshot.png
new file mode 100644
index 0000000..7962f70
--- /dev/null
+++ b/screenshot.png
Binary files differ