#include <unistd.h>
#include <sys/inotify.h>
#include <sys/poll.h>
+#include <sys/vfs.h>
+#include <linux/magic.h>
#include "sd-journal.h"
#include "journal-def.h"
#include "lookup3.h"
#include "compress.h"
#include "journal-internal.h"
+#include "missing.h"
#define JOURNAL_FILES_MAX 1024
+#define JOURNAL_FILES_RECHECK_USEC (2 * USEC_PER_SEC)
+
static void detach_location(sd_journal *j) {
Iterator i;
JournalFile *f;
return 0;
}
+static void check_network(sd_journal *j, int fd) {
+ struct statfs sfs;
+
+ assert(j);
+
+ if (j->on_network)
+ return;
+
+ if (fstatfs(fd, &sfs) < 0)
+ return;
+
+ j->on_network =
+ sfs.f_type == CIFS_MAGIC_NUMBER ||
+ sfs.f_type == CODA_SUPER_MAGIC ||
+ sfs.f_type == NCP_SUPER_MAGIC ||
+ sfs.f_type == NFS_SUPER_MAGIC ||
+ sfs.f_type == SMB_SUPER_MAGIC;
+}
+
static int add_file(sd_journal *j, const char *prefix, const char *filename) {
char *path;
int r;
return r;
}
+ check_network(j, f->fd);
+
j->current_invalidate_counter ++;
log_debug("File %s got added.", f->path);
return 0;
hashmap_remove(j->files, f->path);
+
+ log_debug("File %s got removed.", f->path);
+
+ if (j->current_file == f) {
+ j->current_file = NULL;
+ j->current_field = 0;
+ }
+
+ if (j->unique_file == f) {
+ j->unique_file = NULL;
+ j->unique_offset = 0;
+ }
+
journal_file_close(f);
j->current_invalidate_counter ++;
- log_debug("File %s got removed.", f->path);
return 0;
}
m->wd = inotify_add_watch(j->inotify_fd, m->path,
IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
- IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|
+ IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
IN_ONLYDIR);
if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
}
}
+ check_network(j, dirfd(d));
+
closedir(d);
return 0;
}
}
+ check_network(j, dirfd(d));
+
closedir(d);
return 0;
mmap_cache_unref(j->mmap);
free(j->path);
+ free(j->unique_field);
free(j);
}
return -ENOENT;
}
+static int return_data(JournalFile *f, Object *o, const void **data, size_t *size) {
+ size_t t;
+ uint64_t l;
+
+ 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;
+ }
+
+ return 0;
+}
+
_public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
JournalFile *f;
- uint64_t p, l, n;
+ uint64_t p, n;
le64_t le_hash;
int r;
Object *o;
- size_t t;
if (!j)
return -EINVAL;
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;
- }
+ r = return_data(f, o, data, size);
+ if (r < 0)
+ return r;
j->current_field ++;
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)) {
+ } else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) {
r = remove_file(j, d->path, e->name);
if (r < 0)
return determine_change(j);
}
+ if (j->on_network) {
+ /* If we are on the network we need to regularly check
+ * for changes manually */
+
+ if (timeout_usec == (uint64_t) -1 || timeout_usec > JOURNAL_FILES_RECHECK_USEC)
+ timeout_usec = JOURNAL_FILES_RECHECK_USEC;
+ }
+
do {
r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
} while (r == -EINTR);
if (from)
*from = MIN(fr, *from);
if (to)
- *to = MIN(t, *to);
+ *to = MAX(t, *to);
}
}
if (from)
*from = MIN(fr, *from);
if (to)
- *to = MIN(t, *to);
+ *to = MAX(t, *to);
}
}
return 0;
}
-/* _public_ int sd_journal_query_unique(sd_journal *j, const char *field) { */
-/* if (!j) */
-/* return -EINVAL; */
-/* if (!field) */
-/* return -EINVAL; */
+_public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
+ char *f;
-/* return -ENOTSUP; */
-/* } */
+ if (!j)
+ return -EINVAL;
+ if (isempty(field))
+ return -EINVAL;
+ if (!field_is_valid(field))
+ return -EINVAL;
+
+ f = strdup(field);
+ if (!f)
+ return -ENOMEM;
+
+ free(j->unique_field);
+ j->unique_field = f;
+ j->unique_file = NULL;
+ j->unique_offset = 0;
+
+ return 0;
+}
+
+_public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
+ Object *o;
+ size_t k;
+ int r;
+
+ if (!j)
+ return -EINVAL;
+ if (!data)
+ return -EINVAL;
+ if (!l)
+ return -EINVAL;
+ if (!j->unique_field)
+ return -EINVAL;
+
+ k = strlen(j->unique_field);
+
+ if (!j->unique_file) {
+ j->unique_file = hashmap_first(j->files);
+ if (!j->unique_file)
+ return 0;
+ j->unique_offset = 0;
+ }
+
+ for (;;) {
+ JournalFile *of;
+ Iterator i;
+ const void *odata;
+ size_t ol;
+ bool found;
+
+ /* Proceed to next data object in the field's linked list */
+ if (j->unique_offset == 0) {
+ r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
+ if (r < 0)
+ return r;
+
+ j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
+ } else {
+ r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
+ if (r < 0)
+ return r;
+
+ j->unique_offset = le64toh(o->data.next_field_offset);
+ }
-/* _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; */
+ /* We reached the end of the list? Then start again, with the next file */
+ if (j->unique_offset == 0) {
+ JournalFile *n;
-/* return -ENOTSUP; */
-/* } */
+ n = hashmap_next(j->files, j->unique_file->path);
+ if (!n)
+ return 0;
+
+ j->unique_file = n;
+ continue;
+ }
+
+ /* We do not use the type context here, but 0 instead,
+ * so that we can look at this data object at the same
+ * time as one on another file */
+ r = journal_file_move_to_object(j->unique_file, 0, j->unique_offset, &o);
+ if (r < 0)
+ return r;
+
+ /* Let's do the type check by hand, since we used 0 context above. */
+ if (o->object.type != OBJECT_DATA)
+ return -EBADMSG;
+
+ r = return_data(j->unique_file, o, &odata, &ol);
+ if (r < 0)
+ return r;
+
+ /* OK, now let's see if we already returned this data
+ * object by checking if it exists in the earlier
+ * traversed files. */
+ found = false;
+ HASHMAP_FOREACH(of, j->files, i) {
+ Object *oo;
+ uint64_t op;
+
+ if (of == j->unique_file)
+ break;
-/* _public_ void sd_journal_restart_unique(sd_journal *j) { */
-/* if (!j) */
-/* return; */
-/* } */
+ /* Skip this file it didn't have any fields
+ * indexed */
+ if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) &&
+ le64toh(of->header->n_fields) <= 0)
+ continue;
+
+ r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), &oo, &op);
+ if (r < 0)
+ return r;
+
+ if (r > 0)
+ found = true;
+ }
+
+ if (found)
+ continue;
+
+ r = return_data(j->unique_file, o, data, l);
+ if (r < 0)
+ return r;
+
+ return 1;
+ }
+}
+
+_public_ void sd_journal_restart_unique(sd_journal *j) {
+ if (!j)
+ return;
+
+ j->unique_file = NULL;
+ j->unique_offset = 0;
+}
+
+_public_ int sd_journal_reliable_fd(sd_journal *j) {
+ if (!j)
+ return -EINVAL;
+
+ return !j->on_network;
+}