/* * (C) 2014 by Christian Hesse * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. */ #include #include #include #include #include #include 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(®ex, 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(®ex, 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(®ex); out20: sd_journal_close(journal); out10: return rc; } // vim: set syntax=c: