hammer2 - helper thread skeleton listener
authorMatthew Dillon <dillon@apollo.backplane.com>
Fri, 6 Apr 2012 01:16:51 +0000 (18:16 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Fri, 6 Apr 2012 01:16:51 +0000 (18:16 -0700)
* Helper thread skeleton, listen & accept connections (just echo for now).

sbin/hammer2/Makefile
sbin/hammer2/cmd_helper.c [new file with mode: 0644]
sbin/hammer2/hammer2.h
sbin/hammer2/main.c
sbin/hammer2/subs.c
sys/vfs/hammer2/hammer2_mount.h

index 9e03928..332ae01 100644 (file)
@@ -1,10 +1,11 @@
 PROG=  hammer2
 SRCS=  main.c subs.c
-SRCS+= cmd_remote.c cmd_snapshot.c
+SRCS+= cmd_remote.c cmd_snapshot.c cmd_helper.c
 #MAN=  hammer2.8
 NOMAN= TRUE
 
 CFLAGS+= -I${.CURDIR}/../../sys
+CFLAGS+= -pthread
 LDADD= -lm -lutil -lmd
 DPADD= ${LIBM} ${LIBUTIL} ${LIBMD}
 
diff --git a/sbin/hammer2/cmd_helper.c b/sbin/hammer2/cmd_helper.c
new file mode 100644 (file)
index 0000000..e870c26
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2011-2012 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthew Dillon <dillon@dragonflybsd.org>
+ * by Venkatesh Srinivas <vsrinivas@dragonflybsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hammer2.h"
+
+static void helper_master_listen(void);
+static void *helper_master_accept(void *data);
+static void *helper_master_service(void *data);
+
+/*
+ * The first hammer2 helper will also fork off a daemon to listen on
+ * and accept connections from the machine interconnect socket.  This
+ * helper operates across all HAMMER2 mounts.
+ *
+ * An additional independent multi-threaded helper daemon is run for
+ * each HAMMER2 PFS mount.  This helper connects to the master helper
+ * and registers the PFSID for each mount, allowing the master helper
+ * to forward accepted descriptors to the per-PFS helpers after handling
+ * authentication and accepting the PFSID.
+ *
+ * The per-mount helper daemon will then install relay pipe descriptors
+ * into the kernel VFS so the HAMMER2 filesystem can issue requests / accept
+ * commands as needed.  Note that the HAMMER2 filesystem will also track
+ * the cache state and will generally be able to bypass talking to the helper
+ * threads when local media is available and determined to contain the
+ * required data.
+ *
+ * WARNING!  Except for sel_path, we avoid accessing the filesystem.  In
+ *          a fully remote root mount scenario the administrative root
+ *          will be mounted before the helper is started up.
+ */
+int
+cmd_helper(const char *sel_path)
+{
+       int ecode = 0;
+       int fd;
+
+       /*
+        * Install the master server if it is not already running.
+        */
+       helper_master_listen();
+
+       /*
+        * Acquire a handle for ioctls, which will also extract the PFSID
+        * for the mounted PFS.  If sel_path is NULL we just start the
+        * master listener and do not go any further.
+        */
+       if (sel_path == NULL)
+               return(0);
+       if ((fd = hammer2_ioctl_handle(sel_path)) < 0)
+               return(1);
+
+       /*
+        * Connect to the master to register the PFSID and start the
+        * per-PFS helper if we succeed, otherwise a helper is already
+        * running and registered.
+        */
+
+       return ecode;
+}
+
+static
+void
+helper_master_listen(void)
+{
+       struct sockaddr_in lsin;
+       int on;
+       int lfd;
+
+       /*
+        * Acquire socket and set options
+        */
+       if ((lfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+               fprintf(stderr, "helper_master_listen: socket(): %s\n",
+                       strerror(errno));
+               return;
+       }
+       on = 1;
+       setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+
+       /*
+        * Setup listen port and try to bind.  If the bind fails we assume
+        * that a master listener process is already running.
+        */
+       bzero(&lsin, sizeof(lsin));
+       lsin.sin_addr.s_addr = INADDR_ANY;
+       lsin.sin_port = htons(HAMMER2_LISTEN_PORT);
+       if (bind(lfd, (struct sockaddr *)&lsin, sizeof(lsin)) < 0) {
+               close(lfd);
+               return;
+       }
+       listen(lfd, 50);
+
+       /*
+        * Fork and disconnect the controlling terminal and parent process,
+        * executing the specified function as a pthread.
+        *
+        * Returns to the original process which can then continue running.
+        * In debug mode this call will create the pthread without forking
+        * and set NormalExit to 0.
+        */
+       hammer2_disconnect(helper_master_accept, (void *)(intptr_t)lfd);
+       if (NormalExit)
+               close(lfd);
+}
+
+/*
+ * pthread to accept connections on the master socket
+ */
+static
+void *
+helper_master_accept(void *data)
+{
+       struct sockaddr_in asin;
+       socklen_t alen;
+       pthread_t thread;
+       int lfd = (int)(intptr_t)data;
+       int fd;
+
+       /*
+        * Nobody waits for us
+        */
+       setproctitle("hammer2 master listen");
+       pthread_detach(pthread_self());
+
+       /*
+        * Accept connections and create pthreads to handle them after
+        * validating the IP.
+        */
+       for (;;) {
+               alen = sizeof(asin);
+               fd = accept(lfd, (struct sockaddr *)&asin, &alen);
+               if (fd < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       break;
+               }
+               thread = NULL;
+               pthread_create(&thread, NULL,
+                              helper_master_service, (void *)(intptr_t)fd);
+       }
+       return (NULL);
+}
+
+/*
+ * pthread for each connection
+ */
+static
+void *
+helper_master_service(void *data)
+{
+       char buf[256];
+       ssize_t len;
+       int fd = (int)(intptr_t)data;
+
+       while ((len = read(fd, buf, sizeof(buf))) > 0) {
+               write(fd, buf, len);
+       }
+       close(fd);
+
+       return (NULL);
+}
index 32dc042..5de0b75 100644 (file)
 #include <sys/types.h>
 #include <sys/mount.h>
 #include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <sys/tty.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
 #include <vfs/hammer2/hammer2_disk.h>
 #include <vfs/hammer2/hammer2_mount.h>
 #include <vfs/hammer2/hammer2_ioctl.h>
 #include <stdlib.h>
 #include <stdarg.h>
 #include <stddef.h>
-#include <unistd.h>
-#include <string.h>
-#include <fcntl.h>
+
 #include <ctype.h>
 #include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+#include <pthread.h>
+
+extern int DebugOpt;
+extern int NormalExit;
 
 int hammer2_ioctl_handle(const char *sel_path);
+void hammer2_disconnect(void *(*func)(void *), void *arg);
 
 int cmd_remote_connect(const char *sel_path, const char *url);
 int cmd_remote_disconnect(const char *sel_path, const char *url);
 int cmd_remote_status(const char *sel_path, int all_opt);
+int cmd_helper(const char *sel_path);
index 2474426..c7b7afc 100644 (file)
 
 static void usage(int code);
 
+int DebugOpt;
+int NormalExit = 1;    /* if set to 0 main() has to pthread_exit() */
+
 int
 main(int ac, char **av)
 {
-       const char *sel_path = ".";
+       const char *sel_path = NULL;
        const char *uuid_str = NULL;
        int pfs_type = HAMMER2_PFSTYPE_NONE;
        int quick_opt = 0;
@@ -96,6 +99,9 @@ main(int ac, char **av)
                         */
                        uuid_str = optarg;
                        break;
+               case 'd':
+                       DebugOpt = 1;
+                       break;
                default:
                        fprintf(stderr, "Unknown option: %c\n", ch);
                        usage(1);
@@ -153,11 +159,23 @@ main(int ac, char **av)
                 * subsystem manages socket communications for the
                 * filesystem.
                 */
+               ecode = cmd_helper(sel_path);
        } else {
                fprintf(stderr, "Unrecognized command: %s\n", av[0]);
                usage(1);
        }
-       return (ecode);
+
+       /*
+        * In DebugMode we may wind up starting several pthreads in the
+        * original process, in which case we have to let them run and
+        * not actually exit.
+        */
+       if (NormalExit) {
+               return (ecode);
+       } else {
+               pthread_exit(NULL);
+               _exit(2);       /* NOT REACHED */
+       }
 }
 
 static
index a1f036f..51f2694 100644 (file)
@@ -44,6 +44,9 @@ hammer2_ioctl_handle(const char *sel_path)
        struct hammer2_ioc_version info;
        int fd;
 
+       if (sel_path == NULL)
+               sel_path = ".";
+
        fd = open(sel_path, O_RDONLY, 0);
        if (fd < 0) {
                fprintf(stderr, "hammer2: Unable to open %s: %s\n",
@@ -58,3 +61,75 @@ hammer2_ioctl_handle(const char *sel_path)
        }
        return (fd);
 }
+
+void
+hammer2_disconnect(void *(*func)(void *), void *arg)
+{
+       pthread_t thread = NULL;
+       pid_t pid;
+       int ttyfd;
+
+       /*
+        * Do not disconnect in debug mode
+        */
+       if (DebugOpt) {
+                pthread_create(&thread, NULL, func, arg);
+               NormalExit = 0;
+               return;
+       }
+
+       /*
+        * Otherwise disconnect us.  Double-fork to get rid of the ppid
+        * association and disconnect the TTY.
+        */
+       if ((pid = fork()) < 0) {
+               fprintf(stderr, "hammer2: fork(): %s\n", strerror(errno));
+               exit(1);
+       }
+       if (pid > 0) {
+               while (waitpid(pid, NULL, 0) != pid)
+                       ;
+               return;         /* parent returns */
+       }
+
+       /*
+        * Get rid of the TTY/session before double-forking to finish off
+        * the ppid.
+        */
+       ttyfd = open("/dev/null", O_RDWR);
+       if (ttyfd >= 0) {
+               if (ttyfd != 0)
+                       dup2(ttyfd, 0);
+               if (ttyfd != 1)
+                       dup2(ttyfd, 1);
+               if (ttyfd != 2)
+                       dup2(ttyfd, 2);
+               if (ttyfd > 2)
+                       close(ttyfd);
+       }
+
+       ttyfd = open("/dev/tty", O_RDWR);
+       if (ttyfd >= 0) {
+               ioctl(ttyfd, TIOCNOTTY, 0);
+               close(ttyfd);
+       }
+       setsid();
+
+       /*
+        * Second fork to disconnect ppid (the original parent waits for
+        * us to exit).
+        */
+       if ((pid = fork()) < 0) {
+               _exit(2);
+       }
+       if (pid > 0)
+               _exit(0);
+
+       /*
+        * The double child
+        */
+       setsid();
+       pthread_create(&thread, NULL, func, arg);
+       pthread_exit(NULL);
+       _exit(2);       /* NOT REACHED */
+}
index 13018f0..ecedae3 100644 (file)
@@ -53,4 +53,6 @@ struct hammer2_mount_info {
 
 #define HMNT2_USERFLAGS                (HMNT2_NOAUTOSNAP)
 
+#define HAMMER2_LISTEN_PORT    987
+
 #endif