+
+_public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
+ Object *o;
+ JournalFile *f;
+ int r;
+
+ if (!j)
+ return -EINVAL;
+ if (!ret)
+ return -EINVAL;
+
+ f = j->current_file;
+ if (!f)
+ return -EADDRNOTAVAIL;
+
+ if (f->current_offset <= 0)
+ return -EADDRNOTAVAIL;
+
+ r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
+ if (r < 0)
+ return r;
+
+ *ret = le64toh(o->entry.realtime);
+ return 0;
+}
+
+_public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
+ Object *o;
+ JournalFile *f;
+ int r;
+ sd_id128_t id;
+
+ if (!j)
+ return -EINVAL;
+ if (!ret)
+ return -EINVAL;
+
+ f = j->current_file;
+ if (!f)
+ return -EADDRNOTAVAIL;
+
+ if (f->current_offset <= 0)
+ return -EADDRNOTAVAIL;
+
+ r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
+ if (r < 0)
+ return r;
+
+ if (ret_boot_id)
+ *ret_boot_id = o->entry.boot_id;
+ else {
+ r = sd_id128_get_boot(&id);
+ if (r < 0)
+ return r;
+
+ if (!sd_id128_equal(id, o->entry.boot_id))
+ return -ESTALE;
+ }
+
+ *ret = le64toh(o->entry.monotonic);
+ return 0;
+}
+
+_public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
+ JournalFile *f;
+ uint64_t i, n;
+ size_t field_length;
+ int r;
+ Object *o;
+
+ if (!j)
+ return -EINVAL;
+ if (!field)
+ return -EINVAL;
+ if (!data)
+ return -EINVAL;
+ if (!size)
+ return -EINVAL;
+
+ if (isempty(field) || strchr(field, '='))
+ return -EINVAL;
+
+ f = j->current_file;
+ if (!f)
+ return -EADDRNOTAVAIL;
+
+ if (f->current_offset <= 0)
+ return -EADDRNOTAVAIL;
+
+ r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
+ if (r < 0)
+ return r;
+
+ field_length = strlen(field);
+
+ n = journal_file_entry_n_items(o);
+ for (i = 0; i < n; i++) {
+ uint64_t p, l, le_hash;
+ size_t t;
+
+ p = le64toh(o->entry.items[i].object_offset);
+ le_hash = o->entry.items[i].hash;
+ r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
+ if (r < 0)
+ return r;
+
+ if (le_hash != o->data.hash)
+ return -EBADMSG;
+
+ l = le64toh(o->object.size) - offsetof(Object, data.payload);
+
+ if (o->object.flags & OBJECT_COMPRESSED) {
+
+#ifdef HAVE_XZ
+ if (uncompress_startswith(o->data.payload, l,
+ &f->compress_buffer, &f->compress_buffer_size,
+ field, field_length, '=')) {
+
+ uint64_t rsize;
+
+ if (!uncompress_blob(o->data.payload, l,
+ &f->compress_buffer, &f->compress_buffer_size, &rsize))
+ return -EBADMSG;
+
+ *data = f->compress_buffer;
+ *size = (size_t) rsize;
+
+ return 0;
+ }
+#else
+ return -EPROTONOSUPPORT;
+#endif
+
+ } else if (l >= field_length+1 &&
+ memcmp(o->data.payload, field, field_length) == 0 &&
+ o->data.payload[field_length] == '=') {
+
+ t = (size_t) l;
+
+ if ((uint64_t) t != l)
+ return -E2BIG;
+
+ *data = o->data.payload;
+ *size = t;
+
+ return 0;
+ }
+
+ r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
+ if (r < 0)
+ return r;
+ }
+
+ return -ENOENT;
+}
+
+_public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
+ JournalFile *f;
+ uint64_t p, l, n, le_hash;
+ int r;
+ Object *o;
+ size_t t;
+
+ if (!j)
+ return -EINVAL;
+ if (!data)
+ return -EINVAL;
+ if (!size)
+ return -EINVAL;
+
+ f = j->current_file;
+ if (!f)
+ return -EADDRNOTAVAIL;
+
+ if (f->current_offset <= 0)
+ return -EADDRNOTAVAIL;
+
+ r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
+ if (r < 0)
+ return r;
+
+ n = journal_file_entry_n_items(o);
+ if (j->current_field >= n)
+ return 0;
+
+ p = le64toh(o->entry.items[j->current_field].object_offset);
+ le_hash = o->entry.items[j->current_field].hash;
+ r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
+ if (r < 0)
+ return r;
+
+ if (le_hash != o->data.hash)
+ return -EBADMSG;
+
+ l = le64toh(o->object.size) - offsetof(Object, data.payload);
+ t = (size_t) l;
+
+ /* We can't read objects larger than 4G on a 32bit machine */
+ if ((uint64_t) t != l)
+ return -E2BIG;
+
+ if (o->object.flags & OBJECT_COMPRESSED) {
+#ifdef HAVE_XZ
+ uint64_t rsize;
+
+ if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize))
+ return -EBADMSG;
+
+ *data = f->compress_buffer;
+ *size = (size_t) rsize;
+#else
+ return -EPROTONOSUPPORT;
+#endif
+ } else {
+ *data = o->data.payload;
+ *size = t;
+ }
+
+ j->current_field ++;
+
+ return 1;
+}
+
+_public_ void sd_journal_restart_data(sd_journal *j) {
+ if (!j)
+ return;
+
+ j->current_field = 0;
+}
+
+_public_ int sd_journal_get_fd(sd_journal *j) {
+ if (!j)
+ return -EINVAL;
+
+ return j->inotify_fd;
+}
+
+static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
+ char *p;
+ int r;
+
+ assert(j);
+ assert(e);
+
+ /* Is this a subdirectory we watch? */
+ p = hashmap_get(j->inotify_wd_dirs, INT_TO_PTR(e->wd));
+ if (p) {
+
+ if (!(e->mask & IN_ISDIR) && e->len > 0 && 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, p, NULL, e->name);
+ if (r < 0)
+ log_debug("Failed to add file %s/%s: %s", p, e->name, strerror(-r));
+ } else if (e->mask & (IN_DELETE|IN_UNMOUNT)) {
+
+ r = remove_file(j, p, NULL, e->name);
+ if (r < 0)
+ log_debug("Failed to remove file %s/%s: %s", p, e->name, strerror(-r));
+ }
+
+ } else if (e->len == 0) {
+
+ /* Event for the directory itself */
+
+ if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT))
+ remove_directory_wd(j, e->wd);
+ }
+
+ return;
+ }
+
+ /* Must be the root directory then? */
+ p = hashmap_get(j->inotify_wd_roots, INT_TO_PTR(e->wd));
+ if (p) {
+ sd_id128_t id;
+
+ if (!(e->mask & IN_ISDIR) && e->len > 0 && 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, p, NULL, e->name);
+ if (r < 0)
+ log_debug("Failed to add file %s/%s: %s", p, e->name, strerror(-r));
+ } else if (e->mask & (IN_DELETE|IN_UNMOUNT)) {
+
+ r = remove_file(j, p, NULL, e->name);
+ if (r < 0)
+ log_debug("Failed to remove file %s/%s: %s", p, e->name, strerror(-r));
+ }
+
+ } else if ((e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
+
+ /* Event for subdirectory */
+
+ if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
+
+ r = add_directory(j, p, e->name);
+ if (r < 0)
+ log_debug("Failed to add directory %s/%s: %s", p, e->name, strerror(-r));
+ }
+ }
+
+ return;
+ }
+
+ if (e->mask & IN_IGNORED)
+ return;
+
+ log_warning("Unknown inotify event.");
+}
+
+_public_ int sd_journal_process(sd_journal *j) {
+ uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX];
+
+ 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 == EINTR || errno == EAGAIN)
+ return 0;
+
+ return -errno;
+ }
+
+ 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;
+ }
+ }
+}
+
+/* _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; */
+/* } */