+
+ free(item);
+
+ if (k < 0)
+ return k;
+ }
+
+ if ((!seqnum_set || !seqnum_id_set) &&
+ (!monotonic_set || !boot_id_set) &&
+ !realtime_set)
+ return -EINVAL;
+
+ reset_location(j);
+
+ j->current_location.type = LOCATION_DISCRETE;
+
+ if (realtime_set) {
+ j->current_location.realtime = (uint64_t) realtime;
+ j->current_location.realtime_set = true;
+ }
+
+ if (seqnum_set && seqnum_id_set) {
+ j->current_location.seqnum = (uint64_t) seqnum;
+ j->current_location.seqnum_id = seqnum_id;
+ j->current_location.seqnum_set = true;
+ }
+
+ if (monotonic_set && boot_id_set) {
+ j->current_location.monotonic = (uint64_t) monotonic;
+ j->current_location.boot_id = boot_id;
+ j->current_location.monotonic_set = true;
+ }
+
+ if (xor_hash_set) {
+ j->current_location.xor_hash = (uint64_t) xor_hash;
+ j->current_location.xor_hash_set = true;
+ }
+
+ return 0;
+}
+
+_public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
+ if (!j)
+ return -EINVAL;
+
+ reset_location(j);
+ j->current_location.type = LOCATION_DISCRETE;
+ j->current_location.boot_id = boot_id;
+ j->current_location.monotonic = usec;
+ j->current_location.monotonic_set = true;
+
+ return 0;
+}
+
+_public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
+ if (!j)
+ return -EINVAL;
+
+ reset_location(j);
+ j->current_location.type = LOCATION_DISCRETE;
+ j->current_location.realtime = usec;
+ j->current_location.realtime_set = true;
+
+ return 0;
+}
+
+_public_ int sd_journal_seek_head(sd_journal *j) {
+ if (!j)
+ return -EINVAL;
+
+ reset_location(j);
+ j->current_location.type = LOCATION_HEAD;
+
+ return 0;
+}
+
+_public_ int sd_journal_seek_tail(sd_journal *j) {
+ if (!j)
+ return -EINVAL;
+
+ reset_location(j);
+ j->current_location.type = LOCATION_TAIL;
+
+ return 0;
+}
+
+static int add_file(sd_journal *j, const char *prefix, const char *dir, const char *filename) {
+ char *fn;
+ int r;
+ JournalFile *f;
+
+ assert(j);
+ assert(prefix);
+ assert(filename);
+
+ if ((j->flags & SD_JOURNAL_SYSTEM_ONLY) &&
+ !startswith(filename, "system.journal"))
+ return 0;
+
+ if (dir)
+ fn = join(prefix, "/", dir, "/", filename, NULL);
+ else
+ fn = join(prefix, "/", filename, NULL);
+
+ if (!fn)
+ return -ENOMEM;
+
+ if (hashmap_get(j->files, fn)) {
+ free(fn);
+ return 0;
+ }
+
+ if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
+ log_debug("Too many open journal files, not adding %s, ignoring.", fn);
+ free(fn);
+ return 0;
+ }
+
+ r = journal_file_open(fn, O_RDONLY, 0, NULL, &f);
+ free(fn);
+
+ if (r < 0) {
+ if (errno == ENOENT)
+ return 0;
+
+ return r;
+ }
+
+ /* journal_file_dump(f); */
+
+ r = hashmap_put(j->files, f->path, f);
+ if (r < 0) {
+ journal_file_close(f);
+ return r;
+ }
+
+ log_debug("File %s got added.", f->path);
+
+ return 0;
+}
+
+static int remove_file(sd_journal *j, const char *prefix, const char *dir, const char *filename) {
+ char *fn;
+ JournalFile *f;
+
+ assert(j);
+ assert(prefix);
+ assert(filename);
+
+ if (dir)
+ fn = join(prefix, "/", dir, "/", filename, NULL);
+ else
+ fn = join(prefix, "/", filename, NULL);
+
+ if (!fn)
+ return -ENOMEM;
+
+ f = hashmap_get(j->files, fn);
+ free(fn);
+
+ if (!f)
+ return 0;
+
+ hashmap_remove(j->files, f->path);
+ journal_file_close(f);
+
+ log_debug("File %s got removed.", f->path);
+ return 0;
+}
+
+static int add_directory(sd_journal *j, const char *prefix, const char *dir) {
+ char *fn;
+ int r;
+ DIR *d;
+ int wd;
+ sd_id128_t id, mid;
+
+ assert(j);
+ assert(prefix);
+ assert(dir);
+
+ if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
+ (sd_id128_from_string(dir, &id) < 0 ||
+ sd_id128_get_machine(&mid) < 0 ||
+ !sd_id128_equal(id, mid)))
+ return 0;
+
+ fn = join(prefix, "/", dir, NULL);
+ if (!fn)
+ return -ENOMEM;
+
+ d = opendir(fn);
+
+ if (!d) {
+ free(fn);
+ if (errno == ENOENT)
+ return 0;
+
+ return -errno;
+ }
+
+ wd = inotify_add_watch(j->inotify_fd, fn,
+ IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
+ IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|
+ IN_DONT_FOLLOW|IN_ONLYDIR);
+ if (wd > 0) {
+ if (hashmap_put(j->inotify_wd_dirs, INT_TO_PTR(wd), fn) < 0)
+ inotify_rm_watch(j->inotify_fd, wd);
+ else
+ fn = NULL;
+ }
+
+ free(fn);
+
+ for (;;) {
+ struct dirent buf, *de;
+
+ r = readdir_r(d, &buf, &de);
+ if (r != 0 || !de)
+ break;
+
+ if (!dirent_is_file_with_suffix(de, ".journal"))
+ continue;
+
+ r = add_file(j, prefix, dir, de->d_name);
+ if (r < 0)
+ log_debug("Failed to add file %s/%s/%s: %s", prefix, dir, de->d_name, strerror(-r));
+ }
+
+ closedir(d);
+
+ log_debug("Directory %s/%s got added.", prefix, dir);
+
+ return 0;
+}
+
+static void remove_directory_wd(sd_journal *j, int wd) {
+ char *p;
+
+ assert(j);
+ assert(wd > 0);
+
+ if (j->inotify_fd >= 0)
+ inotify_rm_watch(j->inotify_fd, wd);
+
+ p = hashmap_remove(j->inotify_wd_dirs, INT_TO_PTR(wd));
+
+ if (p) {
+ log_debug("Directory %s got removed.", p);
+ free(p);
+ }
+}
+
+static void add_root_wd(sd_journal *j, const char *p) {
+ int wd;
+ char *k;
+
+ assert(j);
+ assert(p);
+
+ wd = inotify_add_watch(j->inotify_fd, p,
+ IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
+ IN_DONT_FOLLOW|IN_ONLYDIR);
+ if (wd <= 0)
+ return;
+
+ k = strdup(p);
+ if (!k || hashmap_put(j->inotify_wd_roots, INT_TO_PTR(wd), k) < 0) {
+ inotify_rm_watch(j->inotify_fd, wd);
+ free(k);
+ }
+}
+
+static void remove_root_wd(sd_journal *j, int wd) {
+ char *p;
+
+ assert(j);
+ assert(wd > 0);
+
+ if (j->inotify_fd >= 0)
+ inotify_rm_watch(j->inotify_fd, wd);
+
+ p = hashmap_remove(j->inotify_wd_roots, INT_TO_PTR(wd));
+
+ if (p) {
+ log_debug("Root %s got removed.", p);
+ free(p);
+ }
+}
+
+_public_ int sd_journal_open(sd_journal **ret, int flags) {
+ sd_journal *j;
+ const char *p;
+ const char search_paths[] =
+ "/run/log/journal\0"
+ "/var/log/journal\0";
+ int r;
+
+ if (!ret)
+ return -EINVAL;
+
+ if (flags & ~(SD_JOURNAL_LOCAL_ONLY|
+ SD_JOURNAL_RUNTIME_ONLY|
+ SD_JOURNAL_SYSTEM_ONLY))
+ return -EINVAL;
+
+ j = new0(sd_journal, 1);
+ if (!j)
+ return -ENOMEM;
+
+ j->flags = flags;
+
+ j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
+ if (j->inotify_fd < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ j->files = hashmap_new(string_hash_func, string_compare_func);
+ if (!j->files) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ j->inotify_wd_dirs = hashmap_new(trivial_hash_func, trivial_compare_func);
+ j->inotify_wd_roots = hashmap_new(trivial_hash_func, trivial_compare_func);
+
+ if (!j->inotify_wd_dirs || !j->inotify_wd_roots) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ /* We ignore most errors here, since the idea is to only open
+ * what's actually accessible, and ignore the rest. */
+
+ NULSTR_FOREACH(p, search_paths) {
+ DIR *d;
+
+ if ((flags & SD_JOURNAL_RUNTIME_ONLY) &&
+ !path_startswith(p, "/run"))
+ continue;
+
+ d = opendir(p);
+ if (!d) {
+ if (errno != ENOENT)
+ log_debug("Failed to open %s: %m", p);
+ continue;
+ }
+
+ add_root_wd(j, p);
+
+ for (;;) {
+ struct dirent buf, *de;
+ sd_id128_t id;
+
+ r = readdir_r(d, &buf, &de);
+ if (r != 0 || !de)
+ break;
+
+ if (dirent_is_file_with_suffix(de, ".journal")) {
+ r = add_file(j, p, NULL, de->d_name);
+ if (r < 0)
+ log_debug("Failed to add file %s/%s: %s", p, de->d_name, strerror(-r));
+
+ } else if ((de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) &&
+ sd_id128_from_string(de->d_name, &id) >= 0) {
+
+ r = add_directory(j, p, de->d_name);
+ if (r < 0)
+ log_debug("Failed to add directory %s/%s: %s", p, de->d_name, strerror(-r));
+ }
+ }
+
+ closedir(d);
+ }