+
+_public_ int sd_journal_get_fd(sd_journal *j) {
+ int r;
+
+ if (!j)
+ return -EINVAL;
+
+ if (j->inotify_fd >= 0)
+ return j->inotify_fd;
+
+ r = allocate_inotify(j);
+ if (r < 0)
+ return r;
+
+ /* Iterate through all dirs again, to add them to the
+ * inotify */
+ if (j->path)
+ r = add_root_directory(j, j->path);
+ else
+ r = add_search_paths(j);
+ if (r < 0)
+ return r;
+
+ return j->inotify_fd;
+}
+
+static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
+ Directory *d;
+ int r;
+
+ assert(j);
+ assert(e);
+
+ /* Is this a subdirectory we watch? */
+ d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
+ if (d) {
+ sd_id128_t id;
+
+ if (!(e->mask & IN_ISDIR) && e->len > 0 &&
+ (endswith(e->name, ".journal") ||
+ endswith(e->name, ".journal~"))) {
+
+ /* Event for a journal file */
+
+ if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
+ r = add_file(j, d->path, e->name);
+ if (r < 0)
+ log_debug("Failed to add file %s/%s: %s", d->path, e->name, strerror(-r));
+
+ } else if (e->mask & (IN_DELETE|IN_UNMOUNT)) {
+
+ r = remove_file(j, d->path, e->name);
+ if (r < 0)
+ log_debug("Failed to remove file %s/%s: %s", d->path, e->name, strerror(-r));
+ }
+
+ } else if (!d->is_root && e->len == 0) {
+
+ /* Event for a subdirectory */
+
+ if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
+ r = remove_directory(j, d);
+ if (r < 0)
+ log_debug("Failed to remove directory %s: %s", d->path, strerror(-r));
+ }
+
+
+ } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
+
+ /* Event for root directory */
+
+ if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
+ r = add_directory(j, d->path, e->name);
+ if (r < 0)
+ log_debug("Failed to add directory %s/%s: %s", d->path, e->name, strerror(-r));
+ }
+ }
+
+ return;
+ }
+
+ if (e->mask & IN_IGNORED)
+ return;
+
+ log_warning("Unknown inotify event.");
+}
+
+static int determine_change(sd_journal *j) {
+ bool b;
+
+ assert(j);
+
+ b = j->current_invalidate_counter != j->last_invalidate_counter;
+ j->last_invalidate_counter = j->current_invalidate_counter;
+
+ return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
+}
+
+_public_ int sd_journal_process(sd_journal *j) {
+ uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX] _alignas_(struct inotify_event);
+ bool got_something = false;
+
+ if (!j)
+ return -EINVAL;
+
+ for (;;) {
+ struct inotify_event *e;
+ ssize_t l;
+
+ l = read(j->inotify_fd, buffer, sizeof(buffer));
+ if (l < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ return got_something ? determine_change(j) : SD_JOURNAL_NOP;
+
+ return -errno;
+ }
+
+ got_something = true;
+
+ e = (struct inotify_event*) buffer;
+ while (l > 0) {
+ size_t step;
+
+ process_inotify_event(j, e);
+
+ step = sizeof(struct inotify_event) + e->len;
+ assert(step <= (size_t) l);
+
+ e = (struct inotify_event*) ((uint8_t*) e + step);
+ l -= step;
+ }
+ }
+
+ return determine_change(j);
+}
+
+_public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
+ int r;
+
+ assert(j);
+
+ if (j->inotify_fd < 0) {
+
+ /* This is the first invocation, hence create the
+ * inotify watch */
+ r = sd_journal_get_fd(j);
+ if (r < 0)
+ return r;
+
+ /* The journal might have changed since the context
+ * object was created and we weren't watching before,
+ * hence don't wait for anything, and return
+ * immediately. */
+ return determine_change(j);
+ }
+
+ do {
+ r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
+ } while (r == -EINTR);
+
+ if (r < 0)
+ return r;
+
+ return sd_journal_process(j);
+}
+
+_public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
+ Iterator i;
+ JournalFile *f;
+ bool first = true;
+ int r;
+
+ if (!j)
+ return -EINVAL;
+ if (!from && !to)
+ return -EINVAL;
+
+ HASHMAP_FOREACH(f, j->files, i) {
+ usec_t fr, t;
+
+ r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
+ if (r == -ENOENT)
+ continue;
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ if (first) {
+ if (from)
+ *from = fr;
+ if (to)
+ *to = t;
+ first = false;
+ } else {
+ if (from)
+ *from = MIN(fr, *from);
+ if (to)
+ *to = MIN(t, *to);
+ }
+ }
+
+ return first ? 0 : 1;
+}
+
+_public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
+ Iterator i;
+ JournalFile *f;
+ bool first = true;
+ int r;
+
+ if (!j)
+ return -EINVAL;
+ if (!from && !to)
+ return -EINVAL;
+
+ HASHMAP_FOREACH(f, j->files, i) {
+ usec_t fr, t;
+
+ r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
+ if (r == -ENOENT)
+ continue;
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ if (first) {
+ if (from)
+ *from = fr;
+ if (to)
+ *to = t;
+ first = false;
+ } else {
+ if (from)
+ *from = MIN(fr, *from);
+ if (to)
+ *to = MIN(t, *to);
+ }
+ }
+
+ return first ? 0 : 1;
+}
+
+void journal_print_header(sd_journal *j) {
+ Iterator i;
+ JournalFile *f;
+ bool newline = false;
+
+ assert(j);
+
+ HASHMAP_FOREACH(f, j->files, i) {
+ if (newline)
+ putchar('\n');
+ else
+ newline = true;
+
+ journal_file_print_header(f);
+ }
+}
+
+_public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
+ Iterator i;
+ JournalFile *f;
+ uint64_t sum = 0;
+
+ if (!j)
+ return -EINVAL;
+ if (!bytes)
+ return -EINVAL;
+
+ HASHMAP_FOREACH(f, j->files, i) {
+ struct stat st;
+
+ if (fstat(f->fd, &st) < 0)
+ return -errno;
+
+ sum += (uint64_t) st.st_blocks * 512ULL;
+ }
+
+ *bytes = sum;
+ return 0;
+}
+
+/* _public_ int sd_journal_query_unique(sd_journal *j, const char *field) { */
+/* if (!j) */
+/* return -EINVAL; */
+/* if (!field) */
+/* return -EINVAL; */
+
+/* return -ENOTSUP; */
+/* } */
+
+/* _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) { */
+/* if (!j) */
+/* return -EINVAL; */
+/* if (!data) */
+/* return -EINVAL; */
+/* if (!l) */
+/* return -EINVAL; */
+
+/* return -ENOTSUP; */
+/* } */
+
+/* _public_ void sd_journal_restart_unique(sd_journal *j) { */
+/* if (!j) */
+/* return; */
+/* } */