[PATCH] prepare streams based messaging

Volker Lendecke Volker.Lendecke at SerNet.DE
Fri Jun 17 15:29:00 UTC 2016


On Wed, Jun 01, 2016 at 04:44:53PM +1000, Amitay Isaacs wrote:
> I uncovered a problem while trying to run the tests fddrn/fdsrc.
> 
> I tried to run fddrn as follows:
> 
>     $ bin/fddrn bin/tfdd /tmp/tfd
>     tfd_init failed: No such file or directory
> 
> While debugging this, I figured out that the tfdd path needs to be absolute.
> 
> Q: Do you need to do chdir("/") in tfd_tfdd_child()?  Can we do that in
> tfdd binary?

With the attached patch this should work fine now. Initializing the
daemon is a pretty tricky dance, I'd appreciate review on that particular
piece. I've added a -i (interactive, non-forking) option to tfdd for
consumption in autobuild, which can't deal well with runaway daemons.

Volker

-- 
SerNet GmbH, Bahnhofsallee 1b, 37081 Göttingen
phone: +49-551-370000-0, fax: +49-551-370000-9
AG Göttingen, HRB 2816, GF: Dr. Johannes Loxen
http://www.sernet.de, mailto:kontakt at sernet.de

SerNet & BSI laden ein: 29. Juni 2016,
2. IT-Grundschutztag 2016, BPA Berlin.
Anmeldung: https://www.sernet.de/gstag
-------------- next part --------------
From f0fc2adf93f49b2e3106b3f47f750450e93cd4bd Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Sat, 14 May 2016 10:30:50 +0200
Subject: [PATCH] lib: Add tfd

This is a small daemon that can connect processes with stream sockets
and provide unique IDs.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 lib/tfd/fddrn.c       |  92 +++++++
 lib/tfd/fdsrc.c       | 118 +++++++++
 lib/tfd/sockclnt.c    | 115 +++++++++
 lib/tfd/socksrv.c     | 106 +++++++++
 lib/tfd/tfd.c         | 402 +++++++++++++++++++++++++++++++
 lib/tfd/tfd.h         | 104 ++++++++
 lib/tfd/tfdd.c        | 648 ++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/tfd/wscript_build |  25 ++
 wscript_build         |   1 +
 9 files changed, 1611 insertions(+)
 create mode 100644 lib/tfd/fddrn.c
 create mode 100644 lib/tfd/fdsrc.c
 create mode 100644 lib/tfd/sockclnt.c
 create mode 100644 lib/tfd/socksrv.c
 create mode 100644 lib/tfd/tfd.c
 create mode 100644 lib/tfd/tfd.h
 create mode 100644 lib/tfd/tfdd.c
 create mode 100644 lib/tfd/wscript_build

diff --git a/lib/tfd/fddrn.c b/lib/tfd/fddrn.c
new file mode 100644
index 0000000..e283552
--- /dev/null
+++ b/lib/tfd/fddrn.c
@@ -0,0 +1,92 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Copyright (C) Volker Lendecke 2016
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "replace.h"
+#include "tfd.h"
+#include "poll_funcs/poll_funcs_tevent.h"
+#include <string.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+static void recv_fd_cb(uint64_t src, int fd, void *private_data);
+
+int main(int argc, const char *argv[])
+{
+	struct tevent_context *ev;
+	struct poll_funcs *tevent_poll_funcs;
+	void *poll_funcs_handle;
+	int ret;
+	bool done = false;
+
+	if (argc != 3) {
+		fprintf(stderr, "Usage: %s <tfdd_exec> <sockname>\n",
+			argv[0]);
+		exit(1);
+	}
+
+	ev = tevent_context_init(NULL);
+	if (ev == NULL) {
+		fprintf(stderr, "tevent_context_init failed\n");
+		exit(1);
+	}
+
+	tevent_poll_funcs = poll_funcs_init_tevent(ev);
+	if (tevent_poll_funcs == NULL) {
+		fprintf(stderr, "poll_funcs_init_tevent failed\n");
+		exit(1);
+	}
+
+	poll_funcs_handle = poll_funcs_tevent_register(
+		ev, tevent_poll_funcs, ev);
+	if (poll_funcs_handle == NULL) {
+		fprintf(stderr, "poll_funcs_tevent_register failed\n");
+		exit(1);
+	}
+
+	ret = tfd_init(argv[1], argv[2], tevent_poll_funcs, NULL, 0,
+		       recv_fd_cb, &done, NULL);
+	if (ret != 0) {
+		fprintf(stderr, "tfd_init failed: %s\n", strerror(ret));
+		exit(1);
+	}
+
+	while (!done) {
+		ret = tevent_loop_once(ev);
+		if (ret != 0) {
+			fprintf(stderr, "tevent_loop_once failed: %s\n",
+				strerror(errno));
+			exit(1);
+		}
+	}
+
+	return 0;
+}
+
+static void recv_fd_cb(uint64_t src, int fd, void *private_data)
+{
+	bool *done = private_data;
+
+	if (fd == -1) {
+		*done = true;
+		return;
+	}
+
+	close(fd);
+}
diff --git a/lib/tfd/fdsrc.c b/lib/tfd/fdsrc.c
new file mode 100644
index 0000000..776751f
--- /dev/null
+++ b/lib/tfd/fdsrc.c
@@ -0,0 +1,118 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Copyright (C) Volker Lendecke 2016
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "replace.h"
+#include "tfd.h"
+#include "poll_funcs/poll_funcs_tevent.h"
+#include <string.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+static void recv_fd_cb(uint64_t src, int fd, void *private_data);
+
+int main(int argc, const char *argv[])
+{
+	struct tevent_context *ev;
+	struct poll_funcs *tevent_poll_funcs;
+	void *poll_funcs_handle;
+	int ret;
+	uint64_t dst;
+	bool done = false;
+	uint64_t unique;
+	struct timeval tp1;
+	unsigned count = 0;
+
+	if (argc != 4) {
+		fprintf(stderr, "Usage: %s <tfdd-exec> <sockname> dst\n",
+			argv[0]);
+		exit(1);
+	}
+
+	dst = atoi(argv[3]);
+
+	ev = tevent_context_init(NULL);
+	if (ev == NULL) {
+		fprintf(stderr, "tevent_context_init failed\n");
+		exit(1);
+	}
+
+	tevent_poll_funcs = poll_funcs_init_tevent(ev);
+	if (tevent_poll_funcs == NULL) {
+		fprintf(stderr, "poll_funcs_init_tevent failed\n");
+		exit(1);
+	}
+
+	poll_funcs_handle = poll_funcs_tevent_register(
+		ev, tevent_poll_funcs, ev);
+	if (poll_funcs_handle == NULL) {
+		fprintf(stderr, "poll_funcs_tevent_register failed\n");
+		exit(1);
+	}
+
+	ret = tfd_init(argv[1], argv[2], tevent_poll_funcs, NULL, 0,
+			  recv_fd_cb, &done, &unique);
+	if (ret != 0) {
+		fprintf(stderr, "tfd_init failed: %s\n", strerror(ret));
+		exit(1);
+	}
+
+	printf("I'm unique id %"PRIu64"\n", unique);
+
+	gettimeofday(&tp1, NULL);
+
+	while (true) {
+		struct timeval tp2, diff;
+		int sock;
+
+		ret = tfd_connect(dst, &sock, &unique);
+		if (ret != 0) {
+			fprintf(stderr, "tfd_connect failed: %s\n",
+				strerror(ret));
+			exit(1);
+		}
+		close(sock);
+
+		gettimeofday(&tp2, NULL);
+		diff = tevent_timeval_until(&tp1, &tp2);
+		if (diff.tv_sec != 0) {
+			printf("%8u fds/sec\r", count);
+			fflush(stdout);
+			tp1 = tp2;
+			count = 0;
+		}
+
+		count += 1;
+	}
+
+	printf("Sent to unique id %"PRIu64"\n", unique);
+
+	return 0;
+}
+
+static void recv_fd_cb(uint64_t src, int fd, void *private_data)
+{
+	bool *done = private_data;
+
+	if (fd == -1) {
+		*done = true;
+		return;
+	}
+
+	close(fd);
+}
diff --git a/lib/tfd/sockclnt.c b/lib/tfd/sockclnt.c
new file mode 100644
index 0000000..ca0cefe
--- /dev/null
+++ b/lib/tfd/sockclnt.c
@@ -0,0 +1,115 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Copyright (C) Volker Lendecke 2016
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "replace.h"
+#include "tfd.h"
+#include "poll_funcs/poll_funcs_tevent.h"
+#include <string.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+static void recv_fd_cb(uint64_t src, int fd, void *private_data);
+
+int main(int argc, const char *argv[])
+{
+	struct tevent_context *ev;
+	struct poll_funcs *tevent_poll_funcs;
+	void *poll_funcs_handle;
+	int sock;
+	int ret;
+	uint64_t dst;
+	bool done = false;
+	uint64_t unique;
+	struct timeval tp1;
+
+	if (argc != 4) {
+		fprintf(stderr, "Usage: %s <tfdd_exec> <sockname> dst\n",
+			argv[0]);
+		exit(1);
+	}
+
+	dst = atoi(argv[3]);
+
+	ev = tevent_context_init(NULL);
+	if (ev == NULL) {
+		fprintf(stderr, "tevent_context_init failed\n");
+		exit(1);
+	}
+
+	tevent_poll_funcs = poll_funcs_init_tevent(ev);
+	if (tevent_poll_funcs == NULL) {
+		fprintf(stderr, "poll_funcs_init_tevent failed\n");
+		exit(1);
+	}
+
+	poll_funcs_handle = poll_funcs_tevent_register(
+		ev, tevent_poll_funcs, ev);
+	if (poll_funcs_handle == NULL) {
+		fprintf(stderr, "poll_funcs_tevent_register failed\n");
+		exit(1);
+	}
+
+	ret = tfd_init(argv[1], argv[2], tevent_poll_funcs, NULL, 0,
+			  recv_fd_cb, &done, &unique);
+	if (ret != 0) {
+		fprintf(stderr, "tfd_init failed: %s\n", strerror(ret));
+		exit(1);
+	}
+
+	printf("I'm unique id %"PRIu64"\n", unique);
+
+	gettimeofday(&tp1, NULL);
+
+	ret = tfd_connect(dst, &sock, &unique);
+	if (ret != 0) {
+		fprintf(stderr, "tfd_connect failed: %s\n", strerror(ret));
+		exit(1);
+	}
+
+	printf("Sent to unique id %"PRIu64"\n", unique);
+
+	while (true) {
+		ret = tevent_loop_once(ev);
+		if (ret != 0) {
+			fprintf(stderr, "tevent_loop_once failed: %s\n",
+				strerror(errno));
+			exit(1);
+		}
+	}
+
+	return 0;
+}
+
+static void recv_fd_cb(uint64_t src, int fd, void *private_data)
+{
+	bool *done = private_data;
+	char c;
+	ssize_t ret;
+
+	if (fd == -1) {
+		*done = true;
+		return;
+	}
+
+	do {
+		ret = read(fd, &c, 1);
+	} while ((ret == -1) && (errno == EINTR));
+
+	exit(0);
+}
diff --git a/lib/tfd/socksrv.c b/lib/tfd/socksrv.c
new file mode 100644
index 0000000..7c225da
--- /dev/null
+++ b/lib/tfd/socksrv.c
@@ -0,0 +1,106 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Copyright (C) Volker Lendecke 2016
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "replace.h"
+#include "tfd.h"
+#include "poll_funcs/poll_funcs_tevent.h"
+#include <string.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+static void recv_fd_cb(uint64_t src, int fd, void *private_data);
+
+int main(int argc, const char *argv[])
+{
+	struct tevent_context *ev;
+	struct poll_funcs *tevent_poll_funcs;
+	void *poll_funcs_handle;
+	int ret;
+	bool done = false;
+	int socks[2];
+
+	if (argc != 3) {
+		fprintf(stderr, "Usage: %s <tfdd_exec> <sockname>\n",
+			argv[0]);
+		exit(1);
+	}
+
+	ret = socketpair(AF_UNIX, SOCK_STREAM, 0, socks);
+	if (ret == -1) {
+		fprintf(stderr, "socketpair failed: %s\n", strerror(ret));
+		exit(1);
+	}
+
+	ev = tevent_context_init(NULL);
+	if (ev == NULL) {
+		fprintf(stderr, "tevent_context_init failed\n");
+		exit(1);
+	}
+
+	tevent_poll_funcs = poll_funcs_init_tevent(ev);
+	if (tevent_poll_funcs == NULL) {
+		fprintf(stderr, "poll_funcs_init_tevent failed\n");
+		exit(1);
+	}
+
+	poll_funcs_handle = poll_funcs_tevent_register(
+		ev, tevent_poll_funcs, ev);
+	if (poll_funcs_handle == NULL) {
+		fprintf(stderr, "poll_funcs_tevent_register failed\n");
+		exit(1);
+	}
+
+	ret = tfd_init(argv[1], argv[2], tevent_poll_funcs, NULL, 0,
+			  recv_fd_cb, socks, NULL);
+	if (ret != 0) {
+		fprintf(stderr, "tfd_init failed: %s\n", strerror(ret));
+		exit(1);
+	}
+
+	while (!done) {
+		ret = tevent_loop_once(ev);
+		if (ret != 0) {
+			fprintf(stderr, "tevent_loop_once failed: %s\n",
+				strerror(errno));
+			exit(1);
+		}
+	}
+
+	return 0;
+}
+
+static void recv_fd_cb(uint64_t src, int fd, void *private_data)
+{
+	uint64_t unique;
+	int ret;
+
+	if (fd == -1) {
+		exit(1);
+		return;
+	}
+	close(fd);
+
+	ret = tfd_connect(src, &fd, &unique);
+	if (ret != 0) {
+		fprintf(stderr, "tfd_connect failed: %s\n", strerror(ret));
+		exit(1);
+	}
+	close(fd);
+}
diff --git a/lib/tfd/tfd.c b/lib/tfd/tfd.c
new file mode 100644
index 0000000..1ea0758
--- /dev/null
+++ b/lib/tfd/tfd.c
@@ -0,0 +1,402 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Copyright (C) Volker Lendecke 2016
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "replace.h"
+#include "tfd.h"
+#include "lib/poll_funcs/poll_funcs.h"
+#include "lib/util/msghdr.h"
+#include "lib/util/iov_buf.h"
+#include <poll.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/uio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+
+struct tfd_queue;
+
+struct tfd_state {
+	struct sockaddr_un addr;
+	char tfdd_exec[MAXPATHLEN];
+	int sock;
+	const struct poll_funcs *ev_funcs;
+	struct poll_watch *read_watch;
+	void (*recv_callback)(uint64_t src, int fd, void *private_data);
+	void *private_data;
+	struct tfd_queue *queue;
+};
+
+static struct tfd_state global_tfd_state = { .sock = -1 };
+
+static void tfd_read_cb(struct poll_watch *w, int fd,
+			   short events, void *private_data);
+static int tfd_spawn_tfdd(const char *tfdd_exe, const char *tfdd_sock);
+
+int tfd_init(const char *tfdd_exec, const char *tfdd_sock,
+	     const struct poll_funcs *ev_funcs,
+	     const uint64_t *tfdids, uint64_t num_tfdids,
+	     void (*recv_callback)(uint64_t src, int fd, void *private_data),
+	     void *private_data,
+	     uint64_t *my_unique)
+{
+	struct tfd_state *state = &global_tfd_state;
+	uint64_t pid;
+	ssize_t to_write, rw_ret;
+	int ret;
+	uint64_t unique;
+	struct iovec iov[3];
+
+	if (state->sock != -1) {
+		return EBUSY;
+	}
+
+	if (num_tfdids > 15) {
+		return EMSGSIZE;
+	}
+
+	if (tfdd_exec != NULL) {
+		if (strlcpy(state->tfdd_exec, tfdd_exec,
+			    sizeof(state->tfdd_exec)) >=
+		    sizeof(state->tfdd_exec)) {
+			return ENAMETOOLONG;
+		}
+	} else {
+		state->tfdd_exec[0] = '\0';
+	}
+
+	state->addr.sun_family = AF_UNIX;
+	if (strlcpy(state->addr.sun_path, tfdd_sock,
+		    sizeof(state->addr.sun_path)) >=
+	    sizeof(state->addr.sun_path)) {
+		return ENAMETOOLONG;
+	}
+
+	state->ev_funcs = ev_funcs;
+	state->recv_callback = recv_callback;
+	state->private_data = private_data;
+
+	state->sock = socket(PF_LOCAL, SOCK_STREAM, 0);
+	if (state->sock == -1) {
+		return errno;
+	}
+
+	ret = connect(state->sock, (const struct sockaddr *)&state->addr,
+		      sizeof(state->addr));
+	if ((ret == -1) && (state->tfdd_exec[0] != '\0')) {
+		ret = tfd_spawn_tfdd(state->tfdd_exec, tfdd_sock);
+		if (ret != 0) {
+			goto fail;
+		}
+		ret = connect(state->sock,
+			      (const struct sockaddr *)&state->addr,
+			      sizeof(state->addr));
+		if (ret == -1) {
+			ret = errno;
+			goto fail;
+		}
+	}
+
+	do {
+		rw_ret = read(state->sock, &unique, sizeof(unique));
+	} while ((rw_ret == -1) && (errno == EINTR));
+
+	if (rw_ret == -1) {
+		ret = errno;
+		goto fail;
+	}
+	if (rw_ret != sizeof(unique)) {
+		ret = EPIPE;
+		goto fail;
+	}
+
+	if (my_unique != NULL) {
+		*my_unique = unique;
+	}
+
+	pid = getpid();
+
+	iov[0].iov_base = &num_tfdids;
+	iov[0].iov_len = sizeof(num_tfdids);
+	iov[1].iov_base = &pid;
+	iov[1].iov_len = sizeof(pid);
+
+	iov[2].iov_base = discard_const_p(uint64_t, tfdids);
+	iov[2].iov_len = sizeof(uint64_t) * num_tfdids;
+
+	num_tfdids += 1;
+
+	to_write = iov_buflen(iov, ARRAY_SIZE(iov));
+	if (to_write == -1) {
+		ret = EMSGSIZE;
+		goto fail;
+	}
+
+	do {
+		rw_ret = writev(state->sock, iov, ARRAY_SIZE(iov));
+	} while ((rw_ret == -1) && (errno == EINTR));
+
+	if (rw_ret == -1) {
+		ret = errno;
+		goto fail;
+	}
+	if (rw_ret != to_write) {
+		ret = EPIPE;
+		goto fail;
+	}
+
+	state->read_watch = ev_funcs->watch_new(
+		ev_funcs, state->sock, POLLIN, tfd_read_cb, NULL);
+	if (state->read_watch == NULL) {
+		ret = ENOMEM;
+		goto fail;
+	}
+	return 0;
+
+fail:
+	close(state->sock);
+	state->sock = -1;
+	return ret;
+}
+
+static bool tfd_read(int sock, uint64_t *src, int *fd)
+{
+	size_t bufsize = msghdr_prep_recv_fds(NULL, NULL, 0, 1);
+	uint8_t buf[bufsize];
+	ssize_t ret;
+
+	struct iovec iov = {
+		.iov_base = src, .iov_len = sizeof(*src),
+	};
+	struct msghdr msg = {
+		.msg_iov = &iov, .msg_iovlen = 1,
+	};
+
+	msghdr_prep_recv_fds(&msg, buf, bufsize, 1);
+
+	do {
+		ret = recvmsg(sock, &msg, MSG_NOSIGNAL);
+	} while ((ret == -1) && (errno == EINTR));
+
+	{
+		size_t num_fds = msghdr_extract_fds(&msg, NULL, 0);
+		int fds[num_fds];
+		size_t i;
+
+		msghdr_extract_fds(&msg, fds, num_fds);
+
+		if (ret == iov.iov_len) {
+			if (num_fds == 1) {
+				*fd = fds[0];
+			} else {
+				*fd = -1;
+			}
+			return true;
+		}
+
+		/*
+		 * Close the fds we just received, this is a protocol error
+		 */
+		for (i=0; i<num_fds; i++) {
+			close(fds[i]);
+		}
+	}
+
+	return false;
+}
+
+static void tfd_read_cb(struct poll_watch *w, int fd,
+			short events, void *private_data)
+{
+	struct tfd_state *state = &global_tfd_state;
+	bool ok;
+	uint64_t src;
+	int new_fd;
+
+	if ((events & POLLIN) == 0) {
+		return;
+	}
+
+	ok = tfd_read(state->sock, &src, &new_fd);
+	if (ok) {
+		state->recv_callback(src, new_fd, state->private_data);
+		return;
+	}
+
+	/*
+	 * Protocol error, tell the caller
+	 */
+	state->recv_callback(0, -1, state->private_data);
+}
+
+int tfd_connect(uint64_t dst, int *psock, uint64_t *dst_unique)
+{
+	struct tfd_state *state = &global_tfd_state;
+	int socks[2] = { -1, -1 };
+	struct iovec iov = { .iov_base = &dst, .iov_len = sizeof(dst) };
+	struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1 };
+	size_t msg_len = msghdr_prep_fds(&msg, NULL, 0, &socks[1], 1);
+	uint8_t msg_buf[msg_len];
+	ssize_t rw_ret;
+	int ret;
+
+	uint8_t result_buf[sizeof(ret) + sizeof(*dst_unique)];
+
+	if (state->sock == -1) {
+		return ENOTCONN;
+	}
+
+	ret = socketpair(AF_UNIX, SOCK_STREAM, 0, socks);
+	if (ret == -1) {
+		return errno;
+	}
+
+	msghdr_prep_fds(&msg, msg_buf, msg_len, &socks[1], 1);
+
+	do {
+		rw_ret = sendmsg(state->sock, &msg, MSG_NOSIGNAL);
+	} while ((rw_ret == -1) && (errno == EINTR));
+
+	if (rw_ret == -1) {
+		ret = errno;
+		goto fail;
+	}
+
+	if (rw_ret != sizeof(dst)) {
+		ret = EIO;
+		goto fail;
+	}
+
+	close(socks[1]);
+	socks[1] = -1;
+
+	do {
+		rw_ret = recv(socks[0], result_buf, sizeof(result_buf),
+			      MSG_NOSIGNAL);
+	} while ((rw_ret == -1) && (errno == EINTR));
+
+	if (rw_ret == -1) {
+		ret = errno;
+		goto fail;
+	}
+	if (rw_ret != sizeof(result_buf)) {
+		ret = EIO;
+		goto fail;
+	}
+
+	memcpy(&ret, result_buf, sizeof(ret));
+	if (dst_unique != NULL) {
+		memcpy(dst_unique, result_buf + sizeof(ret),
+		       sizeof(*dst_unique));
+	}
+
+	if (ret == 0) {
+		*psock = socks[0];
+		socks[0] = -1;
+	}
+
+fail:
+	if (socks[0] != -1) {
+		close(socks[0]);
+	}
+	if (socks[1] != -1) {
+		close(socks[1]);
+	}
+
+	return ret;
+
+}
+
+int tfd_fini(void)
+{
+	struct tfd_state *state = &global_tfd_state;
+
+	if (state->sock == -1) {
+		return ENOTCONN;
+	}
+	state->ev_funcs->watch_free(state->read_watch);
+	close(state->sock);
+	state->sock = -1;
+	return 0;
+}
+
+static void tfd_tfdd_child(const char *tfdd_exec, const char *tfdd_sock)
+{
+	char *argvec[3];
+	char *envvec[1];
+	const char *argv0;
+
+	argv0 = strrchr(tfdd_exec, '/');
+	if (argv0 != NULL) {
+		argv0 += 1;
+	} else {
+		argv0 = tfdd_exec;
+	}
+
+	argvec[0] = discard_const_p(char, argv0);
+	argvec[1] = discard_const_p(char, tfdd_sock);
+	argvec[2] = NULL;
+	envvec[0] = NULL;
+
+	execve(tfdd_exec, argvec, envvec);
+	exit(1);
+}
+
+static int tfd_spawn_tfdd(const char *tfdd_exec, const char *tfdd_sock)
+{
+	sigset_t mask, omask;
+	int ret, status;
+	pid_t pid, waited_pid;
+
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGCHLD);
+
+	ret = pthread_sigmask(SIG_BLOCK, &mask, &omask);
+	if (ret != 0) {
+		return ret;
+	}
+
+	pid = fork();
+	if (pid == -1) {
+		ret = errno;
+		pthread_sigmask(SIG_SETMASK, &omask, NULL);
+		return ret;
+	}
+	if (pid == 0) {
+		tfd_tfdd_child(tfdd_exec, tfdd_sock);
+	}
+
+	do {
+		waited_pid = waitpid(pid, &status, 0);
+	} while ((waited_pid == -1) && (errno == EINTR));
+
+	pthread_sigmask(SIG_SETMASK, &omask, NULL);
+
+	if (WIFEXITED(status)) {
+		status = WEXITSTATUS(status);
+		if (status == 0) {
+			return 0;
+		}
+	}
+
+	return EBUSY;
+}
diff --git a/lib/tfd/tfd.h b/lib/tfd/tfd.h
new file mode 100644
index 0000000..ee97451
--- /dev/null
+++ b/lib/tfd/tfd.h
@@ -0,0 +1,104 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Copyright (C) Volker Lendecke 2016
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __TFD_TFD_H__
+#define __TFD_TFD_H__
+
+#include "replace.h"
+
+/**
+ * @file tfd.h
+ *
+ * @brief Routines to connect processes via stream sockets
+ *
+ * To support IPC, this subsystem connects participating processes
+ * with unix domain stream sockets. The function tfd_connect() creates
+ * such a socket pair and sends one end to another process that registered
+ * with the subsystem via tfd_init().
+ *
+ * tfd_init() will fork/exec a central tfdd daemon on demand. tfdd
+ * creates one unix domain stream socket that everybody will connect
+ * to.
+ *
+ * Every participating process has one stream socket open to
+ * tfdd. This stream socket carries file descriptors from and to other
+ * participants, forwarded via tfdd. No IPC data between participants
+ * is transmitted via this stream socket to tfdd, the IPC is done via
+ * the socketpairs created in tfd_connect().
+ *
+ * When connecting, a participant can register 64-bit tfdids that it
+ * can receive messages on. In addition to the explicitly listed
+ * tfdids, tfd_init() will register the current process id.
+ *
+ * As a central point of contact, tfdd assigns a unique 64-bit ID to
+ * every participant. Every time a process calls tfd_connect(), tfdd
+ * increments the unique id that is assigned. This number space is
+ * completely orthogonal to the tfdids a process registers. Samba uses
+ * the unique IDs to protect against recycled PIDs. If a process locks
+ * a file, it registers itself in for example locking.tdb with its
+ * pid/unique combination. A lock contender can make sure that the
+ * lock holder still exists, even if the pid has been recycled by a
+ * different smbd.
+ */
+
+struct poll_funcs;
+
+/**
+ * @brief Connect to the tfdd socket
+ *
+ * Connect to and register the process with tfdd. If tfdd is not
+ * around fork/exec it on demand.
+ *
+ * The current pid is always registered with tfdd.
+ *
+ * @param[in] tfdd_exec tfdd executable, started on demand
+ * @param[in] tfdd_sock Path to tfdd socket
+ * @param[in] ev_funcs poll_funcs structure to register for async fds
+ * @param[in] tfdids IDs to be registered with tfdd
+ * @param[in] num_tfids Number of tfdids
+ * @param[in] recv_callback Callback to receive file descriptors
+ * @param[in] private_data Pointer passed back to recv_callback
+ * @param[out] my_unique Unique ID assigned to the process by tfdd
+ * @return 0 / errno
+ */
+
+int tfd_init(const char *tfdd_exec, const char *tfdd_sock,
+	     const struct poll_funcs *ev_funcs,
+	     const uint64_t *tfdids, uint64_t num_tfdids,
+	     void (*recv_callback)(uint64_t src, int fd, void *private_data),
+	     void *private_data,
+	     uint64_t *my_unique);
+
+/**
+ * @brief Disconnect from the tfdd
+ *
+ * @return 0 / errno
+ */
+int tfd_fini(void);
+
+/**
+ * @brief Connect to a remote process with a stream socket
+ *
+ * @param[in] dst The tfdid of the remote process
+ * @param[out] psock The local end of the stream socket
+ * @param[out] dst_unique The unique id of "dst"
+ * @return 0 / errno
+ */
+int tfd_connect(uint64_t dst, int *psock, uint64_t *dst_unique);
+
+#endif
diff --git a/lib/tfd/tfdd.c b/lib/tfd/tfdd.c
new file mode 100644
index 0000000..6f06b6d
--- /dev/null
+++ b/lib/tfd/tfdd.c
@@ -0,0 +1,648 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Copyright (C) Volker Lendecke 2016
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "replace.h"
+#include <stdio.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <talloc.h>
+#include <tevent.h>
+#include <assert.h>
+#include <syslog.h>
+#include "lib/async_req/async_sock.h"
+#include "lib/util/tevent_unix.h"
+#include "lib/util/dlinklist.h"
+#include "lib/util/blocking.h"
+#include "lib/util/sys_rw_data.h"
+#include "lib/util/msghdr.h"
+
+struct tfdd_state {
+	struct tevent_context *ev;
+	struct tfdd_client_state *clients;
+	uint64_t next_unique;
+	int listen_sock;
+};
+
+struct tfdd_client_state {
+	struct tfdd_client_state *prev, *next;
+	struct tfdd_state *tfdd;
+	int sock;
+	uint64_t unique;
+	uint64_t *tfdids;
+};
+
+static int tfdd_client_state_destructor(struct tfdd_client_state *s);
+static void tfdd_client_handler(struct tevent_context *ev,
+				struct tevent_fd *fde,
+				uint16_t flags,
+				void *private_data);
+
+static struct tevent_req *tfdd_client_send(TALLOC_CTX *mem_ctx,
+					   struct tevent_context *ev,
+					   struct tfdd_state *tfdd,
+					   int sock)
+{
+	struct tevent_req *req;
+	struct tfdd_client_state *state;
+	struct tevent_fd *fde;
+	ssize_t written;
+	int ret;
+
+	req = tevent_req_create(mem_ctx, &state, struct tfdd_client_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	state->sock = sock;
+	state->tfdd = tfdd;
+
+	DLIST_ADD(tfdd->clients, state);
+	talloc_set_destructor(state, tfdd_client_state_destructor);
+
+	fde = tevent_add_fd(ev, state, state->sock, TEVENT_FD_READ,
+			    tfdd_client_handler, req);
+	if (tevent_req_nomem(fde, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_fd_set_auto_close(fde);
+
+	ret = set_blocking(sock, false);
+	if (tevent_req_error(req, ret)) {
+		return tevent_req_post(req, ev);
+	}
+
+	do {
+		written = write(sock, &tfdd->next_unique,
+				sizeof(tfdd->next_unique));
+	} while ((written == -1) && (errno == EINTR));
+
+	if (written != sizeof(tfdd->next_unique)) {
+		tevent_req_done(req);
+		return tevent_req_post(req, ev);
+	}
+
+	state->unique = tfdd->next_unique;
+
+	tfdd->next_unique += 1;
+
+	return req;
+}
+
+static int tfdd_client_state_destructor(struct tfdd_client_state *s)
+{
+	struct tfdd_state *tfdd = s->tfdd;
+
+	DLIST_REMOVE(tfdd->clients, s);
+
+	return 0;
+}
+
+static struct tfdd_client_state *tfdd_find_client(struct tfdd_state *tfdd,
+						  uint64_t tfdid)
+{
+	struct tfdd_client_state *cl;
+
+	for (cl = tfdd->clients; cl; cl = cl->next) {
+		uint64_t *tfdids = cl->tfdids;
+		size_t num_tfdids = talloc_array_length(cl->tfdids);
+		size_t i;
+
+		for (i = 0; i<num_tfdids; i++) {
+			if (tfdids[i] == tfdid) {
+				return cl;
+			}
+		}
+	}
+	return NULL;
+}
+
+static void tfdd_send_fd(struct tfdd_state *tfdd,
+			 uint64_t src, uint64_t dst, int fd)
+{
+	size_t msg_len = msghdr_prep_fds(NULL, NULL, 0, &fd, 1);
+	uint8_t msg_buf[msg_len];
+
+	struct iovec iov = { .iov_base = &src,
+			     .iov_len = sizeof(src) };
+	struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1 };
+	struct tfdd_client_state *dst_cli;
+
+	int err = ENOENT;
+	uint64_t dst_unique = UINT64_MAX;
+	uint8_t result_buf[sizeof(err) + sizeof(dst_unique)];
+
+	ssize_t ret;
+
+	msghdr_prep_fds(&msg, msg_buf, msg_len, &fd, 1);
+
+	dst_cli = tfdd_find_client(tfdd, dst);
+
+	/*
+	 * Send the destination unique id to the originating client
+	 * through the newly passed fd
+	 */
+	if (dst_cli != NULL) {
+		dst_unique = dst_cli->unique;
+
+		do {
+			ret = sendmsg(dst_cli->sock, &msg,
+				      MSG_DONTWAIT|MSG_NOSIGNAL);
+		} while ((ret == -1) && (errno == EINTR));
+
+		switch (ret) {
+		    case sizeof(src):
+			    err = 0;
+			    break;
+		    case -1:
+			    err = errno;
+			    break;
+		    default:
+			    err = EIO;
+			    break;
+		}
+	}
+
+	memcpy(result_buf, &err, sizeof(err));
+	memcpy(result_buf + sizeof(err), &dst_unique, sizeof(dst_unique));
+
+	do {
+		ret = send(fd, result_buf, sizeof(result_buf),
+			   MSG_DONTWAIT|MSG_NOSIGNAL);
+	} while ((ret == -1) && (errno == EINTR));
+
+	/*
+	 * Ignore failure here: This was the status response to the fd
+	 * that we just sent across. The most likely error is EAGAIN,
+	 * but we need to protect ourselves from a DoS. A client could
+	 * send us a full socket, so we would block here or consume
+	 * memory if we queued thing. Normally this is one end of a
+	 * fresh socketpair, so this should never be full at this
+	 * point.
+	 */
+}
+
+static void tfdd_client_handle_msg(struct tevent_context *ev,
+				   struct tevent_req *req,
+				   struct msghdr *msg, uint64_t dst,
+				   struct tfdd_client_state *state)
+{
+	size_t num_fds = msghdr_extract_fds(msg, NULL, 0);
+	int fds[num_fds];
+	size_t i;
+
+	msghdr_extract_fds(msg, fds, num_fds);
+
+	if (state->tfdids == NULL) {
+		ssize_t to_read, nread;
+
+		if (num_fds != 0) {
+			goto fail;
+		}
+
+		if (dst == 0) {
+			goto fail;
+		}
+
+		/*
+		 * The very first client->tfdd destination is the
+		 * number of ids to be registered.
+		 */
+
+		state->tfdids = talloc_array(state, uint64_t, dst);
+		if (state->tfdids == NULL) {
+			goto fail;
+		}
+
+		/*
+		 * To avoid an async engine, assume the client sent
+		 * the number together with the tfdids in one call. If
+		 * this turns out to be a problem, we can always add
+		 * some more complexity here.
+		 */
+
+		to_read = talloc_get_size(state->tfdids);
+
+		nread = read_data(state->sock, state->tfdids, to_read);
+		if (nread != to_read) {
+			goto fail;
+		}
+		return;
+	}
+
+	assert(talloc_array_length(state->tfdids) > 0);
+
+	if (num_fds == 1) {
+		/*
+		 * Pass a fd to the dest
+		 */
+		tfdd_send_fd(state->tfdd, state->tfdids[0], dst, fds[0]);
+		close(fds[0]);
+		return;
+	}
+
+	/*
+	 * Protocol error: We only pass at most one fd
+	 */
+
+fail:
+	for (i=0; i<num_fds; i++) {
+		close(fds[i]);
+	}
+	tevent_req_error(req, EINVAL);
+}
+
+static void tfdd_client_handler(struct tevent_context *ev,
+				struct tevent_fd *fde,
+				uint16_t flags,
+				void *private_data)
+{
+	struct tevent_req *req = talloc_get_type_abort(
+		private_data, struct tevent_req);
+	struct tfdd_client_state *state = tevent_req_data(
+		req, struct tfdd_client_state);
+	size_t bufsize = msghdr_prep_recv_fds(NULL, NULL, 0, 1);
+	uint8_t buf[bufsize];
+	uint64_t dst;
+	ssize_t ret;
+
+	struct iovec iov = {
+		.iov_base = &dst, .iov_len = sizeof(dst)
+	};
+	struct msghdr msg = {
+		.msg_iov = &iov, .msg_iovlen = 1,
+	};
+
+	msghdr_prep_recv_fds(&msg, buf, bufsize, 1);
+
+	do {
+		ret = recvmsg(state->sock, &msg, MSG_NOSIGNAL);
+	} while ((ret == -1) && (errno == EINTR));
+
+	if (ret == 0) {
+		tevent_req_done(req);
+		return;
+	}
+
+	if (ret == -1) {
+		tevent_req_error(req, errno);
+		return;
+	}
+
+	if (ret != sizeof(dst)) {
+		tevent_req_error(req, EINVAL);
+		return;
+	}
+
+	{
+		size_t num_fds = msghdr_extract_fds(&msg, NULL, 0);
+		int fds[num_fds];
+
+		msghdr_extract_fds(&msg, fds, num_fds);
+
+		tfdd_client_handle_msg(ev, req, &msg, dst, state);
+	}
+}
+
+static int tfdd_client_recv(struct tevent_req *req)
+{
+	return tevent_req_simple_recv_unix(req);
+}
+
+static void tfdd_accepted(struct tevent_req *subreq);
+static void tfdd_client_exited(struct tevent_req *subreq);
+
+static struct tevent_req *tfdd_send(TALLOC_CTX *mem_ctx,
+				    struct tevent_context *ev,
+				    int listen_sock)
+{
+	struct tevent_req *req, *subreq;
+	struct tfdd_state *state;
+
+	req = tevent_req_create(mem_ctx, &state, struct tfdd_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	state->ev = ev;
+	state->listen_sock = listen_sock;
+	state->next_unique = 1;
+
+	subreq = accept_send(state, state->ev, state->listen_sock);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, tfdd_accepted, req);
+	return req;
+}
+
+static void tfdd_accepted(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct tfdd_state *state = tevent_req_data(
+		req, struct tfdd_state);
+	int sock, err;
+
+	sock = accept_recv(subreq, NULL, NULL, &err);
+	TALLOC_FREE(subreq);
+	if (sock == -1) {
+		tevent_req_error(req, err);
+		return;
+	}
+
+	subreq = tfdd_client_send(state, state->ev, state, sock);
+	if (tevent_req_nomem(subreq, req)) {
+		return;
+	}
+	tevent_req_set_callback(subreq, tfdd_client_exited, req);
+
+	subreq = accept_send(state, state->ev, state->listen_sock);
+	if (tevent_req_nomem(subreq, req)) {
+		return;
+	}
+	tevent_req_set_callback(subreq, tfdd_accepted, req);
+}
+
+static void tfdd_client_exited(struct tevent_req *subreq)
+{
+	tfdd_client_recv(subreq);
+	TALLOC_FREE(subreq);
+}
+
+static int tfdd_recv(struct tevent_req *req)
+{
+	return tevent_req_simple_recv_unix(req);
+}
+
+static int tfdd_create_lockfile(const char *sockname, int *pfd, bool *locked)
+{
+	ssize_t len = strlen(sockname);
+	char name[len+5];
+	struct flock lck;
+	int ret, fd;
+
+	snprintf(name, sizeof(name), "%s.lck", sockname);
+
+	fd = open(name, O_NONBLOCK|O_CREAT|O_WRONLY, 0644);
+	if (fd == -1) {
+		return errno;
+	}
+
+	*locked = false;
+
+	lck = (struct flock) { .l_type = F_WRLCK, .l_whence = SEEK_SET };
+	do {
+		ret = fcntl(fd, F_SETLK, &lck);
+	} while ((ret == -1) && (errno == EINTR));
+
+	if (ret == -1) {
+		if (errno == EAGAIN) {
+			*locked = true;
+		}
+		goto fail;
+	}
+
+	do {
+		ret = ftruncate(fd, 0);
+	} while ((ret == -1) && (errno == EINTR));
+
+	if (ret == -1) {
+		goto fail;
+	}
+
+	*pfd = fd;
+	return 0;
+fail:
+	ret = errno;
+	close(fd);
+	return ret;
+}
+
+static bool tfdd_try_connect(const struct sockaddr_un *addr)
+{
+	int i, sock;
+
+	sock = socket(PF_LOCAL, SOCK_STREAM, 0);
+	if (sock == -1) {
+		return false;
+	}
+
+	for (i=0; i<5; i++) {
+		int ret;
+		ret = connect(sock, (const struct sockaddr *)addr, sizeof(*addr));
+		if (ret == 0) {
+			close(sock);
+			return true;
+		}
+		poll(NULL, 0, i);
+	}
+
+	close(sock);
+	return false;
+}
+
+static void usage(const char *progname)
+{
+	fprintf(stderr, "Usage: %s [-i] <sockname>\n", progname);
+}
+
+static int daemon_init(const char *progname, int facility,
+		       const int *used_fds, size_t num_used_fds)
+{
+	pid_t pid;
+	int i, ret;
+	int max_used = -1;
+
+	for (i=0; i<num_used_fds; i++) {
+		max_used = MAX(max_used, used_fds[i]);
+	}
+
+	pid = fork();
+	if (pid == -1) {
+		return -1;
+	}
+	if (pid != 0) {
+		/* parent */
+		return pid;
+	}
+
+	ret = setsid();
+	if (ret == -1) {
+		exit(1);
+	}
+
+	chdir("/");
+
+	closefrom(max_used+1);
+
+	for (i=0; i<max_used; i++) {
+		int j;
+
+		for (j=0; j<num_used_fds; j++) {
+			if (i == used_fds[j]) {
+				break;
+			}
+		}
+
+		if (j == num_used_fds) {
+			/* i not found in used_fds[] */
+			close(i);
+		}
+	}
+
+	open("/dev/null", O_RDONLY);
+	open("/dev/null", O_RDWR);
+	open("/dev/null", O_RDWR);
+
+	openlog(progname, LOG_PID, facility);
+
+	return 0;
+}
+
+int main(int argc, char * const *argv)
+{
+	struct tevent_context *ev;
+	struct tevent_req *req;
+	char pidbuf[64];
+	ssize_t written;
+	struct sockaddr_un un_addr = { .sun_family = AF_UNIX };
+	size_t len;
+	int sock, ret;
+	int lockfd = -1;
+	bool locked = false;
+	bool daemonize = true;
+	int c;
+	char *const progname = argv[0];
+
+	while ((c = getopt(argc, argv, "ih")) != -1) {
+		switch (c) {
+		    case 'i':
+			    daemonize = false;
+			    break;
+		    case 'h':
+		    default:
+			    usage(progname);
+			    exit(0);
+			    break;
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+
+	if (argc < 1) {
+		usage(progname);
+		exit(1);
+	}
+
+	len = strlcpy(un_addr.sun_path, argv[0], sizeof(un_addr.sun_path));
+	if (len >= sizeof(un_addr.sun_path)) {
+		fprintf(stderr, "Socket name (%s) too long, max %u\n",
+			argv[0], (unsigned)sizeof(un_addr.sun_path));
+		exit(1);
+	}
+
+	ret = tfdd_create_lockfile(un_addr.sun_path, &lockfd, &locked);
+	if (ret != 0) {
+		if (locked && tfdd_try_connect(&un_addr)) {
+			exit(0);
+		}
+		fprintf(stderr, "Could not create lockfile: %s\n",
+			strerror(ret));
+		exit(1);
+	}
+
+	sock = socket(PF_LOCAL, SOCK_STREAM, 0);
+	if (sock == -1) {
+		perror("socket failed");
+		exit(1);
+	}
+
+	ret = unlink(un_addr.sun_path);
+	if ((ret == -1) && (errno != ENOENT)) {
+		perror("unlink failed");
+		exit(1);
+	}
+
+	ret = bind(sock, (struct sockaddr *)&un_addr, sizeof(un_addr));
+	if (ret == -1) {
+		perror("bind failed");
+		exit(1);
+	}
+
+	ret = listen(sock, 5);
+	if (ret == -1) {
+		perror("listen failed");
+		exit(1);
+	}
+
+	if (daemonize) {
+		int used_fds[] = { [0] = lockfd, [1] = sock };
+
+		ret = daemon_init(progname, LOG_DAEMON, used_fds,
+				  ARRAY_SIZE(used_fds));
+		if (ret == -1) {
+			fprintf(stderr, "fork failed: %s\n", strerror(errno));
+			exit(1);
+		}
+		if (ret != 0) {
+			/* parent */
+			exit(0);
+		}
+	}
+
+	len = snprintf(pidbuf, sizeof(pidbuf), "%ju\n", (uintmax_t)getpid());
+
+	do {
+		written = pwrite(lockfd, pidbuf, len, 0);
+	} while ((written == -1) && (errno == EINTR));
+
+	if (written != len) {
+		exit(1);
+	}
+
+	ev = tevent_context_init(NULL);
+	if (ev == NULL) {
+		perror("tevent_context_create failed");
+		exit(1);
+	}
+
+	req = tfdd_send(ev, ev, sock);
+	if (req == NULL) {
+		fprintf(stderr, "tfdd_send failed\n");
+		exit(1);
+	}
+
+	if (!tevent_req_poll(req, ev)) {
+		perror("tevent_req_poll() failed");
+		exit(1);
+	}
+
+	ret = tfdd_recv(req);
+	TALLOC_FREE(req);
+	if (ret != 0) {
+		fprintf(stderr, "tfdd failed: %s\n", strerror(ret));
+		exit(1);
+	}
+
+	TALLOC_FREE(ev);
+
+	return 0;
+}
diff --git a/lib/tfd/wscript_build b/lib/tfd/wscript_build
new file mode 100644
index 0000000..b5aa4ff
--- /dev/null
+++ b/lib/tfd/wscript_build
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+
+bld.SAMBA_SUBSYSTEM('tfd',
+                    source='tfd.c',
+                    deps='msghdr replace pthread')
+bld.SAMBA_BINARY('tfdd',
+                 source='tfdd.c',
+                 deps='LIBASYNC_REQ msghdr sys_rw',
+                 install_path='${LIBEXECDIR}')
+bld.SAMBA_BINARY('fdsrc',
+                 source='fdsrc.c',
+                 deps='replace talloc POLL_FUNCS_TEVENT tfd',
+                 install=False)
+bld.SAMBA_BINARY('fddrn',
+                 source='fddrn.c',
+                 deps='replace talloc POLL_FUNCS_TEVENT tfd',
+                 install=False)
+bld.SAMBA_BINARY('socksrv',
+                 source='socksrv.c',
+                 deps='replace talloc POLL_FUNCS_TEVENT tfd',
+                 install=False)
+bld.SAMBA_BINARY('sockclnt',
+                 source='sockclnt.c',
+                 deps='replace talloc POLL_FUNCS_TEVENT tfd',
+                 install=False)
diff --git a/wscript_build b/wscript_build
index 3813cff..bf276ed 100644
--- a/wscript_build
+++ b/wscript_build
@@ -44,6 +44,7 @@ bld.RECURSE('lib/addns')
 bld.RECURSE('lib/ldb')
 bld.RECURSE('lib/param')
 bld.RECURSE('lib/poll_funcs')
+bld.RECURSE('lib/tfd')
 bld.RECURSE('dynconfig')
 bld.RECURSE('lib/util/charset')
 bld.RECURSE('python')
-- 
2.1.4



More information about the samba-technical mailing list