summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBernd Schubert <bernd.schubert@fastmail.fm>2011-02-20 21:51:52 +0100
committerBernd Schubert <bernd.schubert@fastmail.fm>2011-02-20 21:51:52 +0100
commit20bc19d6f2e63351e253a7d811a7fc0ee39f3f6a (patch)
tree8e500bf520b6fd532f0fa851ee7a77d51102bf2a
parent7db87a3f978a3b84ad5ff4baea9e313f559ebb08 (diff)
downloadunionfs-fuse-20bc19d6f2e63351e253a7d811a7fc0ee39f3f6a.tar.gz
syslog: Eventually add support for syslogs
syslog() calls might block IO, so we need a separate thread that does only syslogging independently of IO threads. IO threads only add their messages to that usyslog thread without any risk to block. Still a bit experimental, but seems to work fine.
-rw-r--r--src/CMakeLists.txt3
-rw-r--r--src/Makefile4
-rw-r--r--src/cow.c5
-rw-r--r--src/cow_utils.c39
-rw-r--r--src/debug.h14
-rw-r--r--src/findbranch.c3
-rw-r--r--src/general.c4
-rw-r--r--src/general.h1
-rw-r--r--src/rmdir.c3
-rw-r--r--src/string.c8
-rw-r--r--src/unionfs.c16
-rw-r--r--src/usyslog.c296
-rw-r--r--src/usyslog.h31
13 files changed, 374 insertions, 53 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 970d882..d58d4f4 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,5 +1,6 @@
set(HASHTABLE_SRCS hashtable.c hashtable_itr.c)
-set(UNIONFS_SRCS unionfs.c stats.c opts.c debug.c findbranch.c readdir.c general.c unlink.c cow.c cow_utils.c string.c rmdir.c)
+set(UNIONFS_SRCS unionfs.c stats.c opts.c debug.c findbranch.c readdir.c
+ general.c unlink.c cow.c cow_utils.c string.c rmdir.c usyslog.c)
add_executable(unionfs ${UNIONFS_SRCS} ${HASHTABLE_SRCS})
target_link_libraries(unionfs fuse pthread rt)
diff --git a/src/Makefile b/src/Makefile
index 2f51bf5..1e4e55c 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -8,7 +8,9 @@ LDFLAGS +=
LIB = $(shell pkg-config --libs fuse)
HASHTABLE_OBJ = hashtable.o hashtable_itr.o
-UNIONFS_OBJ = unionfs.o stats.o opts.o debug.o findbranch.o readdir.o general.o unlink.o rmdir.o cow.o cow_utils.o string.o
+UNIONFS_OBJ = unionfs.o stats.o opts.o debug.o findbranch.o readdir.o \
+ general.o unlink.o rmdir.o cow.o cow_utils.o string.o \
+ usyslog.o
unionfs: $(UNIONFS_OBJ) $(HASHTABLE_OBJ) version.h
diff --git a/src/cow.c b/src/cow.c
index cfc692f..06d13d2 100644
--- a/src/cow.c
+++ b/src/cow.c
@@ -24,6 +24,7 @@
#include "cow_utils.h"
#include "string.h"
#include "debug.h"
+#include "usyslog.h"
/**
@@ -53,7 +54,7 @@ static int do_create(const char *path, int nbranch_ro, int nbranch_rw) {
res = mkdir(dirp, buf.st_mode);
if (res == -1) {
- usyslog(LOG_DAEMON, "Creating %s failed: \n", dirp);
+ USYSLOG(LOG_DAEMON, "Creating %s failed: \n", dirp);
RETURN(1);
}
@@ -169,7 +170,7 @@ int cow_cp(const char *path, int branch_ro, int branch_rw) {
res = copy_fifo(&cow);
break;
case S_IFSOCK:
- usyslog(LOG_WARNING, "COW of sockets not supported: %s\n", cow.from_path);
+ USYSLOG(LOG_WARNING, "COW of sockets not supported: %s\n", cow.from_path);
RETURN(1);
default:
res = copy_file(&cow);
diff --git a/src/cow_utils.c b/src/cow_utils.c
index 49126c6..31c8bae 100644
--- a/src/cow_utils.c
+++ b/src/cow_utils.c
@@ -46,6 +46,7 @@
#include "cow_utils.h"
#include "debug.h"
#include "general.h"
+#include "usyslog.h"
// BSD seems to know S_ISTXT itself
#ifndef S_ISTXT
@@ -68,7 +69,7 @@ int setfile(const char *path, struct stat *fs)
ut.actime = fs->st_atime;
ut.modtime = fs->st_mtime;
if (utime(path, &ut)) {
- usyslog(LOG_WARNING, "utimes: %s", path);
+ USYSLOG(LOG_WARNING, "utimes: %s", path);
rval = 1;
}
/*
@@ -79,14 +80,14 @@ int setfile(const char *path, struct stat *fs)
*/
if (chown(path, fs->st_uid, fs->st_gid)) {
if (errno != EPERM) {
- usyslog(LOG_WARNING, "chown: %s", path);
+ USYSLOG(LOG_WARNING, "chown: %s", path);
rval = 1;
}
fs->st_mode &= ~(S_ISTXT | S_ISUID | S_ISGID);
}
if (chmod(path, fs->st_mode)) {
- usyslog(LOG_WARNING, "chown: %s", path);
+ USYSLOG(LOG_WARNING, "chown: %s", path);
rval = 1;
}
@@ -101,7 +102,7 @@ int setfile(const char *path, struct stat *fs)
errno = 0;
if (chflags(path, fs->st_flags)) {
if (errno != EOPNOTSUPP || fs->st_flags != 0) {
- usyslog(LOG_WARNING, "chflags: %s", path);
+ USYSLOG(LOG_WARNING, "chflags: %s", path);
rval = 1;
}
RETURN(rval);
@@ -119,7 +120,7 @@ static int setlink(const char *path, struct stat *fs)
if (lchown(path, fs->st_uid, fs->st_gid)) {
if (errno != EPERM) {
- usyslog(LOG_WARNING, "lchown: %s", path);
+ USYSLOG(LOG_WARNING, "lchown: %s", path);
RETURN(1);
}
}
@@ -143,7 +144,7 @@ int copy_file(struct cow *cow)
#endif
if ((from_fd = open(cow->from_path, O_RDONLY, 0)) == -1) {
- usyslog(LOG_WARNING, "%s", cow->from_path);
+ USYSLOG(LOG_WARNING, "%s", cow->from_path);
RETURN(1);
}
@@ -153,7 +154,7 @@ int copy_file(struct cow *cow)
fs->st_mode & ~(S_ISTXT | S_ISUID | S_ISGID));
if (to_fd == -1) {
- usyslog(LOG_WARNING, "%s", cow->to_path);
+ USYSLOG(LOG_WARNING, "%s", cow->to_path);
(void)close(from_fd);
RETURN(1);
}
@@ -167,17 +168,17 @@ int copy_file(struct cow *cow)
if (fs->st_size > 0 && fs->st_size <= 8 * 1048576) {
if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ,
MAP_FILE|MAP_SHARED, from_fd, (off_t)0)) == MAP_FAILED) {
- usyslog(LOG_WARNING, "mmap: %s", cow->from_path);
+ USYSLOG(LOG_WARNING, "mmap: %s", cow->from_path);
rval = 1;
} else {
madvise(p, fs->st_size, MADV_SEQUENTIAL);
if (write(to_fd, p, fs->st_size) != fs->st_size) {
- usyslog(LOG_WARNING, "%s", cow->to_path);
+ USYSLOG(LOG_WARNING, "%s", cow->to_path);
rval = 1;
}
/* Some systems don't unmap on close(2). */
if (munmap(p, fs->st_size) < 0) {
- usyslog(LOG_WARNING, "%s", cow->from_path);
+ USYSLOG(LOG_WARNING, "%s", cow->from_path);
rval = 1;
}
}
@@ -187,13 +188,13 @@ int copy_file(struct cow *cow)
while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
wcount = write(to_fd, buf, rcount);
if (rcount != wcount || wcount == -1) {
- usyslog(LOG_WARNING, "%s", cow->to_path);
+ USYSLOG(LOG_WARNING, "%s", cow->to_path);
rval = 1;
break;
}
}
if (rcount < 0) {
- usyslog(LOG_WARNING, "copy failed: %s", cow->from_path);
+ USYSLOG(LOG_WARNING, "copy failed: %s", cow->from_path);
RETURN(1);
}
}
@@ -214,17 +215,17 @@ int copy_file(struct cow *cow)
(S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
else if (fs->st_mode & (S_ISUID | S_ISGID) && fs->st_uid == cow->uid) {
if (fstat(to_fd, &to_stat)) {
- usyslog(LOG_WARNING, "%s", cow->to_path);
+ USYSLOG(LOG_WARNING, "%s", cow->to_path);
rval = 1;
} else if (fs->st_gid == to_stat.st_gid &&
fchmod(to_fd, fs->st_mode & RETAINBITS & ~cow->umask)) {
- usyslog(LOG_WARNING, "%s", cow->to_path);
+ USYSLOG(LOG_WARNING, "%s", cow->to_path);
rval = 1;
}
}
(void)close(from_fd);
if (close(to_fd)) {
- usyslog(LOG_WARNING, "%s", cow->to_path);
+ USYSLOG(LOG_WARNING, "%s", cow->to_path);
rval = 1;
}
@@ -242,14 +243,14 @@ int copy_link(struct cow *cow)
char link[PATHLEN_MAX];
if ((len = readlink(cow->from_path, link, sizeof(link)-1)) == -1) {
- usyslog(LOG_WARNING, "readlink: %s", cow->from_path);
+ USYSLOG(LOG_WARNING, "readlink: %s", cow->from_path);
RETURN(1);
}
link[len] = '\0';
if (symlink(link, cow->to_path)) {
- usyslog(LOG_WARNING, "symlink: %s", link);
+ USYSLOG(LOG_WARNING, "symlink: %s", link);
RETURN(1);
}
@@ -265,7 +266,7 @@ int copy_fifo(struct cow *cow)
DBG("from %s to %s\n", cow->from_path, cow->to_path);
if (mkfifo(cow->to_path, cow->stat->st_mode)) {
- usyslog(LOG_WARNING, "mkfifo: %s", cow->to_path);
+ USYSLOG(LOG_WARNING, "mkfifo: %s", cow->to_path);
RETURN(1);
}
RETURN(setfile(cow->to_path, cow->stat));
@@ -280,7 +281,7 @@ int copy_special(struct cow *cow)
DBG("from %s to %s\n", cow->from_path, cow->to_path);
if (mknod(cow->to_path, cow->stat->st_mode, cow->stat->st_rdev)) {
- usyslog(LOG_WARNING, "mknod: %s", cow->to_path);
+ USYSLOG(LOG_WARNING, "mknod: %s", cow->to_path);
RETURN(1);
}
RETURN(setfile(cow->to_path, cow->stat));
diff --git a/src/debug.h b/src/debug.h
index 103d47b..64626eb 100644
--- a/src/debug.h
+++ b/src/debug.h
@@ -24,20 +24,6 @@ extern FILE* dbgfile;
fflush(stdout); \
} while (0)
-/**
- * Calling syslog() will deadlock if the filesystem is for /etc or /var.
- * This is a bit unexpected, since I thought it would just write into a buffer
- * and then the syslog-daemon would then independetely of the unionfs thread
- * write it's log entry. However, it seems the syslog() call waits for until
- * the log entry is written, which will cause a deadlock, of course.
- * Until we find a solution for that, we simply disable syslogs.
- * The only sane solution comming presently into my mind is to write into
- * a buffer and then to have an independent thread which will flush this buffer
- * to syslog. Other suggestions are welcome, of course!
- */
-#define usyslog(priority, format, ...) DBG(format, ##__VA_ARGS__)
-
-
#define RETURN(returncode) \
do { \
if (uopt.debug) DBG("return %d\n", returncode); \
diff --git a/src/findbranch.c b/src/findbranch.c
index 7e9e0ff..8c8d703 100644
--- a/src/findbranch.c
+++ b/src/findbranch.c
@@ -49,6 +49,7 @@
#include "findbranch.h"
#include "string.h"
#include "debug.h"
+#include "usyslog.h"
/**
* Find a branch that has "path". Return the branch number.
@@ -76,7 +77,7 @@ static int find_branch(const char *path, searchflag_t flag) {
if (uopt.branches[i].rw) RETURN(i);
break;
default:
- usyslog(LOG_ERR, "%s: Unknown flag %d\n", __func__, flag);
+ USYSLOG(LOG_ERR, "%s: Unknown flag %d\n", __func__, flag);
}
}
diff --git a/src/general.c b/src/general.c
index e0bb374..fcec693 100644
--- a/src/general.c
+++ b/src/general.c
@@ -30,7 +30,7 @@
#include "findbranch.h"
#include "general.h"
#include "debug.h"
-
+#include "usyslog.h"
/**
* Check if a file or directory with the hidden flag exists.
@@ -200,7 +200,7 @@ int set_owner(const char *path) {
if (ctx->uid != 0 && ctx->gid != 0) {
int res = lchown(path, ctx->uid, ctx->gid);
if (res) {
- usyslog(LOG_WARNING,
+ USYSLOG(LOG_WARNING,
":%s: Setting the correct file owner failed: %s !\n",
__func__, strerror(errno));
RETURN(-errno);
diff --git a/src/general.h b/src/general.h
index 5a51e32..33a67c6 100644
--- a/src/general.h
+++ b/src/general.h
@@ -8,7 +8,6 @@
#define GENERAL_H
#include <stdbool.h>
-#include <syslog.h>
enum whiteout {
WHITEOUT_FILE,
diff --git a/src/rmdir.c b/src/rmdir.c
index b9190a8..5170425 100644
--- a/src/rmdir.c
+++ b/src/rmdir.c
@@ -32,6 +32,7 @@
#include "findbranch.h"
#include "string.h"
#include "readdir.h"
+#include "usyslog.h"
/**
* If the branch that has the directory to be removed is in read-write mode,
@@ -69,7 +70,7 @@ static int rmdir_ro(const char *path, int branch_ro) {
case (ENOTDIR):
case (ENOTEMPTY):
// catch errors not allowed for rmdir()
- usyslog (LOG_ERR, "%s: Creating the whiteout failed: %s\n",
+ USYSLOG (LOG_ERR, "%s: Creating the whiteout failed: %s\n",
__func__, strerror(errno));
errno = EFAULT;
}
diff --git a/src/string.c b/src/string.c
index 36d3e54..71deef7 100644
--- a/src/string.c
+++ b/src/string.c
@@ -23,7 +23,7 @@
#include "opts.h"
#include "debug.h"
#include "general.h"
-
+#include "usyslog.h"
/**
* Check if the given fname suffixes the hide tag
@@ -113,7 +113,7 @@ int build_path(char *path, int max_len, const char *callfunc, int line, ...) {
// +1 for final \0 not counted by strlen
if (len + 1 > max_len) {
- usyslog (LOG_WARNING, "%s():%d Path too long \n", callfunc, line);
+ USYSLOG (LOG_WARNING, "%s():%d Path too long \n", callfunc, line);
errno = ENAMETOOLONG;
RETURN(-errno);
}
@@ -122,7 +122,7 @@ int build_path(char *path, int max_len, const char *callfunc, int line, ...) {
}
if (len == 0) {
- usyslog(LOG_ERR, "from: %s():%d : No argument given?\n", callfunc, line);
+ USYSLOG(LOG_ERR, "from: %s():%d : No argument given?\n", callfunc, line);
errno = EIO;
RETURN(-errno);
}
@@ -141,7 +141,7 @@ char *u_dirname(const char *path) {
char *ret = strdup(path);
if (ret == NULL) {
- usyslog(LOG_WARNING, "strdup failed, probably out of memory!\n");
+ USYSLOG(LOG_WARNING, "strdup failed, probably out of memory!\n");
return ret;
}
diff --git a/src/unionfs.c b/src/unionfs.c
index 726062b..184a714 100644
--- a/src/unionfs.c
+++ b/src/unionfs.c
@@ -50,6 +50,7 @@
#include "readdir.h"
#include "cow.h"
#include "string.h"
+#include "usyslog.h"
static struct fuse_opt unionfs_opts[] = {
@@ -194,7 +195,7 @@ static int unionfs_getattr(const char *path, struct stat *stbuf) {
stbuf->st_size = STATS_SIZE;
RETURN(0);
}
-
+
int i = find_rorw_branch(path);
if (i == -1) RETURN(-errno);
@@ -229,7 +230,7 @@ static void * unionfs_init(struct fuse_conn_info *conn) {
if (uopt.chroot) {
int res = chroot(uopt.chroot);
if (res) {
- usyslog("Chdir to %s failed: %s ! Aborting!\n",
+ USYSLOG(LOG_WARNING, "Chdir to %s failed: %s ! Aborting!\n",
uopt.chroot, strerror(errno));
exit(1);
}
@@ -306,10 +307,10 @@ static int unionfs_mknod(const char *path, mode_t mode, dev_t rdev) {
// since we now have the unionfs_create() method
// So can we remove it?
- usyslog (LOG_INFO, "deprecated mknod workaround, tell the unionfs-fuse authors if you see this!\n");
+ USYSLOG (LOG_INFO, "deprecated mknod workaround, tell the unionfs-fuse authors if you see this!\n");
res = creat(p, 0);
- if (res > 0 && close(res) == -1) usyslog(LOG_WARNING, "Warning, cannot close file\n");
+ if (res > 0 && close(res) == -1) USYSLOG(LOG_WARNING, "Warning, cannot close file\n");
} else {
res = mknod(p, file_type, rdev);
}
@@ -444,7 +445,7 @@ static int unionfs_rename(const char *from, const char *to) {
}
if (i != j) {
- usyslog(LOG_ERR, "%s: from and to are on different writable branches %d vs %d, which"
+ USYSLOG(LOG_ERR, "%s: from and to are on different writable branches %d vs %d, which"
"is not supported yet.\n", __func__, i, j);
RETURN(-EXDEV);
}
@@ -477,11 +478,11 @@ static int unionfs_rename(const char *from, const char *to) {
// if from was on a read-only branch we copied it, but now rename failed so we need to delete it
if (!uopt.branches[i].rw) {
if (unlink(f))
- usyslog(LOG_ERR, "%s: cow of %s succeeded, but rename() failed and now "
+ USYSLOG(LOG_ERR, "%s: cow of %s succeeded, but rename() failed and now "
"also unlink() failed\n", __func__, from);
if (remove_hidden(from, i))
- usyslog(LOG_ERR, "%s: cow of %s succeeded, but rename() failed and now "
+ USYSLOG(LOG_ERR, "%s: cow of %s succeeded, but rename() failed and now "
"also removing the whiteout failed\n", __func__, from);
}
RETURN(-err);
@@ -807,6 +808,7 @@ static struct fuse_operations unionfs_oper = {
int main(int argc, char *argv[]) {
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
+ init_syslog();
uopt_init();
if (fuse_opt_parse(&args, NULL, unionfs_opts, unionfs_opt_proc) == -1) RETURN(1);
diff --git a/src/usyslog.c b/src/usyslog.c
new file mode 100644
index 0000000..75becac
--- /dev/null
+++ b/src/usyslog.c
@@ -0,0 +1,296 @@
+/*
+ * License: BSD-style license
+ * Copyright: Bernd Schubert <bernd.schubert@fastmail.fm>
+ *
+ * Details:
+ * Log files might be located on our own filesystem. If we then want to log
+ * a message to syslog, we would need to log to ourself, which easily ends up
+ * in a deadlock. Initializing openlog() using the flags
+ * LOG_NDELAY | LOG_NOWAIT should prevent that, but real live has shown that
+ * this does not work reliable and systems 'crashed' just because we
+ * tried to log a harmless message.
+ * So this file introduces a syslog thread and a syslog buffer. usyslog()
+ * calls write without a risk to deadlock into the syslog buffer (chained
+ * list) and then the seperate syslog_thread call syslog(). That way our
+ * our filesystem thread(s) cannot stall from syslog() calls.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <malloc.h>
+#include <pthread.h>
+#include <stdarg.h>
+
+#include "usyslog.h"
+#include "debug.h"
+
+static ulogs_t *free_head, *free_bottom; // free chained list log entries
+static ulogs_t *used_head = NULL, *used_bottom = NULL; //used chained list pointers
+
+static pthread_mutex_t list_lock; // locks the entire chained list
+static pthread_cond_t cond_message; // used to wake up the syslog thread
+
+// Only used for debugging, protected by list_lock
+static int free_entries;
+static int used_entries = 0;
+
+//#define USYSLOG_DEBUG
+
+#ifdef USYSLOG_DEBUG
+static void verify_lists()
+{
+ pthread_mutex_lock(&list_lock);
+
+ ulogs_t *entry = free_head;
+ int free_count = -1;
+ bool first_free = true;
+ while (entry) {
+ if (first_free) {
+ first_free = false;
+ free_count = 1;
+ } else
+ free_count++;
+ entry = entry->next;
+ }
+ if (free_count != free_entries && free_entries != 0)
+ DBG("usyslog list error detected: number of free entries inconsistent!"
+ " %d vs. %d", free_count, free_entries);
+
+ entry = used_head;
+ int used_count = -1;
+ bool first_used = true;
+ while (entry) {
+ if (first_used) {
+ first_used = false;
+ used_count = 1;
+ } else
+ used_count++;
+ entry = entry->next;
+ }
+ if (used_count != used_entries && used_entries != 0)
+ DBG("usyslog list error detected: number of used entries inconsistent!"
+ " (used: %d vs. %d) (free: %d vs. %d) \n",
+ used_count, used_entries, free_count, free_entries);
+
+ pthread_mutex_unlock(&list_lock);
+}
+#else
+#define verify_lists()
+#endif
+
+
+/**
+ * Walks the chained used-list and calls syslog()
+ */
+static void do_syslog(void)
+{
+ pthread_mutex_lock(&list_lock); // we MUST ensure not to keep that forever
+
+ ulogs_t *log_entry = used_head;
+
+ while (log_entry) {
+ pthread_mutex_t *entry_lock = &log_entry->lock;
+ int res = pthread_mutex_trylock(entry_lock);
+ if (res) {
+ if (res != EBUSY)
+ DBG("Entirely unexpected locking error %s\n",
+ strerror(res));
+ // If something goes wrong with the log_entry we do not
+ // block the critical list_lock forever!
+ // EBUSY might come up rarely, if we race with usyslog()
+ pthread_mutex_unlock(&list_lock);
+ sleep(1);
+ pthread_mutex_lock(&list_lock);
+ log_entry = used_head;
+ continue;
+ }
+ pthread_mutex_unlock(&list_lock);
+
+ // This syslog call and so this lock might block, so be
+ // carefull on using locks! The filesystem IO thread
+ // *MUST* only try to lock it using pthread_mutex_trylock()
+ syslog(log_entry->priority, "%s", log_entry->message);
+ log_entry->used = false;
+
+ // NOTE: The list is only locked now, after syslog() succeeded!
+ pthread_mutex_lock(&list_lock);
+ ulogs_t *next_entry = log_entry->next; // just to save the pointer
+
+ used_head = log_entry->next;
+ if (!used_head)
+ used_bottom = NULL; // no used entries left
+
+ if (free_bottom)
+ free_bottom->next = log_entry;
+ free_bottom = log_entry;
+ free_bottom->next = NULL;
+
+ if (!free_head)
+ free_head = log_entry;
+
+ free_entries++;
+ used_entries--;
+
+ pthread_mutex_unlock(&list_lock); // unlock ist ASAP
+
+ log_entry = next_entry;
+ pthread_mutex_unlock(entry_lock);
+ }
+
+ verify_lists();
+}
+
+/**
+ * syslog backgroung thread that tries to to empty the syslog buffer
+ */
+static void * syslog_thread(void *arg)
+{
+ // FIXME: What is a better way to prevent a compiler warning about
+ // unused variable 'arg'
+ int tinfo = *((int *) arg);
+ if (1 == 0)
+ printf("Starting thread %d", tinfo);
+
+ pthread_mutex_t sleep_mutex;
+
+ pthread_mutex_init(&sleep_mutex, NULL);
+ pthread_mutex_lock(&sleep_mutex);
+ while (1) {
+ pthread_cond_wait(&cond_message, &sleep_mutex);
+ do_syslog();
+ }
+
+ return NULL;
+}
+
+/**
+ * usyslog - function to be called if something shall be logged to syslog
+ */
+void usyslog(int priority, const char *format, ...)
+{
+ int res;
+ ulogs_t *log;
+
+ // Lock the entire list first, which means the syslog thread MUST NOT
+ // lock it if there is any chance it might be locked forever.
+ pthread_mutex_lock(&list_lock);
+
+ // Some sanity checks. If we fail here, we will leak a log entry,
+ // but will not lock up.
+
+ if (free_head == NULL) {
+ DBG("All syslog entries already busy\n");
+ pthread_mutex_unlock(&list_lock);
+ return;
+ }
+
+ log = free_head;
+ free_head = log->next;
+
+ res = pthread_mutex_trylock(&log->lock);
+ if (res == EBUSY) {
+ // huh, that never should happen!
+ DBG("Critical log error, log entry is BUSY, but should not\n");
+ pthread_mutex_unlock(&list_lock);
+ return;
+ } else if (res) {
+ // huh, that never should happen either!
+ DBG("Never should happen, can get lock: %s\n", strerror(res));
+ pthread_mutex_unlock(&list_lock);
+ return;
+ }
+
+ if (log->used) {
+ // huh, that never should happen either!
+ DBG("Never should happen, entry is busy, but should not!\n");
+ pthread_mutex_unlock(&log->lock);
+ pthread_mutex_unlock(&list_lock);
+ return;
+ }
+
+ if (!used_head)
+ used_head = used_bottom = log;
+ else {
+ used_bottom->next = log;
+ used_bottom = log;
+ }
+
+ if (log->next) {
+ // from free_list to end of used_list, so next is NULL now
+ log->next = NULL;
+ } else {
+ // so the last entry in free_list
+ free_bottom = NULL;
+ }
+
+
+ free_entries--;
+ used_entries++;
+
+ // Everything below is log entry related, so we can free the list_lock
+ pthread_mutex_unlock(&list_lock);
+
+ va_list ap;
+ va_start(ap, format);
+ vsnprintf(log->message, MAX_MSG_SIZE, format, ap);
+ log->priority = priority;
+ log->used = 1;
+
+ pthread_mutex_unlock(&log->lock);
+
+ pthread_cond_signal(&cond_message); // wake up the syslog thread
+}
+
+/**
+ * Initialize syslogs
+ */
+void init_syslog(void)
+{
+ openlog("unionfs-fuse: ", LOG_CONS | LOG_NDELAY | LOG_NOWAIT | LOG_PID, LOG_DAEMON);
+
+ pthread_mutex_init(&list_lock, NULL);
+ pthread_cond_init(&cond_message, NULL);
+ pthread_t thread;
+ pthread_attr_t attr;
+ int t_arg = 0; // thread argument, not required for us
+
+ int i;
+ ulogs_t *log, *last = NULL;
+ for (i = 0; i < MAX_SYSLOG_MESSAGES; i++) {
+ log = malloc(sizeof(ulogs_t));
+ if (log == NULL) {
+ fprintf(stderr, "\nLog initialization failed: %s\n", strerror(errno));
+ fprintf(stderr, "Aborting!\n");
+ // Still initialazation phase, we can abort.
+ exit (1);
+ }
+
+ log->used = false;
+ pthread_mutex_init(&log->lock, NULL);
+
+ if (last) {
+ last->next = log;
+ } else {
+ // so the very first entry
+ free_head = log;
+ }
+ last = log;
+ }
+ last->next = NULL;
+ free_bottom = last;
+
+ free_entries = MAX_SYSLOG_MESSAGES;
+
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ int res = pthread_create(&thread, &attr, syslog_thread, (void *) &t_arg);
+ if (res != 0) {
+ fprintf(stderr, "Failed to initialize the syslog threads: %s\n",
+ strerror(res));
+ exit(1);
+ }
+}
+
diff --git a/src/usyslog.h b/src/usyslog.h
new file mode 100644
index 0000000..5b127a9
--- /dev/null
+++ b/src/usyslog.h
@@ -0,0 +1,31 @@
+/*
+ * License: BSD-style license
+ * Copyright: Bernd Schubert <bernd.schubert@fastmail.fm>
+ */
+
+#include <syslog.h>
+#include <stdbool.h>
+
+#define MAX_SYSLOG_MESSAGES 32 // max number of buffered syslog messages
+#define MAX_MSG_SIZE 256 // max string length for syslog messages
+
+/* chained buffer list of syslog entries */
+typedef struct ulogs {
+ int priority; // first argument for syslog()
+ char message[MAX_MSG_SIZE]; // 2nd argument for syslog()
+ bool used; // is this entry used?
+ pthread_mutex_t lock; // lock a single entry
+ struct ulogs *next; // pointer to the next entry
+} ulogs_t;
+
+
+void init_syslog(void);
+void usyslog(int priority, const char *format, ...);
+
+
+#define USYSLOG(priority, format, ...) \
+ do { \
+ DBG(format, ##__VA_ARGS__); \
+ usyslog(priority, format, ##__VA_ARGS__); \
+ } while (0);
+