[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