]> arthur.barton.de Git - netatalk.git/commitdiff
Primary reconnect
authorFrank Lahm <franklahm@googlemail.com>
Tue, 15 Feb 2011 11:27:13 +0000 (12:27 +0100)
committerFrank Lahm <franklahm@googlemail.com>
Tue, 15 Feb 2011 11:27:13 +0000 (12:27 +0100)
20 files changed:
NEWS
etc/afpd/afp_config.c
etc/afpd/afp_config.h
etc/afpd/afp_dsi.c
etc/afpd/auth.c
etc/afpd/globals.h
etc/afpd/main.c
etc/afpd/volume.c
etc/cnid_dbd/cnid_metad.c
etc/cnid_dbd/comm.c
include/atalk/dsi.h
include/atalk/server_child.h
include/atalk/server_ipc.h
include/atalk/util.h
libatalk/asp/asp_getsess.c
libatalk/dsi/dsi_getsess.c
libatalk/dsi/dsi_tcp.c
libatalk/util/server_child.c
libatalk/util/server_ipc.c
libatalk/util/socket.c

diff --git a/NEWS b/NEWS
index 5c16be9f3cad23e0b2f6c5345979254bcd418193..4b86a33b0ec08e715e7630f0519f04742b8f6953 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,8 @@
+Changes in 2.2beta2
+====================
+
+* NEW: primary reconnect
+
 Changes in 2.2beta1
 ====================
 
index 648eed71589b27bf77f0eccf7d3057db42ed7e3d..28fe7cbe8b09a1d6608321fbfb23496f21a95395 100644 (file)
@@ -214,25 +214,31 @@ static int asp_start(AFPConfig *config, AFPConfig *configs,
 }
 #endif /* no afp/asp */
 
-static int dsi_start(AFPConfig *config, AFPConfig *configs,
-                     server_child *server_children)
+static afp_child_t *dsi_start(AFPConfig *config, AFPConfig *configs,
+                              server_child *server_children)
 {
-    DSI *dsi;
+    DSI *dsi = config->obj.handle;
+    afp_child_t *child = NULL;
 
-    if (!(dsi = dsi_getsession(config->obj.handle, server_children,
-                               config->obj.options.tickleval))) {
-        LOG(log_error, logtype_afpd, "main: dsi_getsession: %s", strerror(errno) );
-        exit( EXITERR_CLNT );
+    if (!(child = dsi_getsession(dsi,
+                                 server_children,
+                                 config->obj.options.tickleval))) {
+        LOG(log_error, logtype_afpd, "dsi_start: session error: %s", strerror(errno));
+        return NULL;
     }
 
     /* we've forked. */
-    if (dsi->child) {
+    if (parent_or_child == 1) {
         configfree(configs, config);
+        LOG(log_error, logtype_afpd, "dsi_start: IPC fd: %i", child->ipc_fds[1]);
+        config->obj.ipc_fd = child->ipc_fds[1];
+        close(child->ipc_fds[0]); /* Close parent IPC fd */
+        free(child);
         afp_over_dsi(&config->obj); /* start a session */
         exit (0);
     }
 
-    return 0;
+    return child;
 }
 
 #ifndef NO_DDP
index 1a0d08ef8859426a5e7fa8d113046d95308ff2c3..ba800ce3aa45c5b547b296ea0e650ebf9a48d6f7 100644 (file)
@@ -19,7 +19,7 @@ typedef struct AFPConfig {
     unsigned char *optcount;
     char status[1400];
     const void *defoptions, *signature;
-    int (*server_start) (struct AFPConfig *, struct AFPConfig *,
+    afp_child_t *(*server_start) (struct AFPConfig *, struct AFPConfig *,
                              server_child *);
     void (*server_cleanup) (const struct AFPConfig *);
     struct AFPConfig *next;
index 963a291244bcf8b55d0d5a5fb6064c815ae2d6c2..261834f508116dfc3ba9197fa7ee98c3d5056758 100644 (file)
 #include "uid.h"
 #endif /* FORCE_UIDGID */
 
-#define CHILD_DIE         (1 << 0)
-#define CHILD_RUNNING     (1 << 1)
-#define CHILD_SLEEPING    (1 << 2)
-#define CHILD_DATA        (1 << 3)
+#define CHILD_DIE          (1 << 0)
+#define CHILD_RUNNING      (1 << 1)
+#define CHILD_SLEEPING     (1 << 2)
+#define CHILD_DATA         (1 << 3)
+#define CHILD_DISCONNECTED (1 << 4)
 
 /* 
  * We generally pass this from afp_over_dsi to all afp_* funcs, so it should already be
@@ -68,6 +69,9 @@ static void afp_dsi_close(AFPObj *obj)
 {
     DSI *dsi = obj->handle;
 
+    close(obj->ipc_fd);
+    obj->ipc_fd = -1;
+
     /* we may have been called from a signal handler caught when afpd was running
      * as uid 0, that's the wrong user for volume's prexec_close scripts if any,
      * restore our login user
@@ -121,6 +125,49 @@ static volatile int in_handler;
     }
 }
 
+static void afp_dsi_transfer_session(int sig _U_)
+{
+    uint16_t dsiID;
+    int socket;
+    DSI *dsi = (DSI *)child.obj->handle;
+
+    LOG(log_note, logtype_afpd, "afp_dsi_transfer_session: got SIGURG, trying to receive session fd");
+
+    if (readt(child.obj->ipc_fd, &dsiID, 2, 0, 2) != 2) {
+        LOG(log_error, logtype_afpd, "afp_dsi_transfer_session: couldn't receive DSI id, goodbye");
+        afp_dsi_close(child.obj);
+        exit(EXITERR_SYS);
+    }
+
+    if ((socket = recv_fd(child.obj->ipc_fd, 1)) == -1) {
+        LOG(log_error, logtype_afpd, "afp_dsi_transfer_session: couldn't receive session fd, goodbye");
+        afp_dsi_close(child.obj);
+        exit(EXITERR_SYS);
+    }
+
+    close(dsi->socket);
+    dsi->socket = socket;
+    dsi->header.dsi_requestID = dsiID;
+    dsi->header.dsi_len = 0;
+    dsi->header.dsi_code = AFP_OK;
+    dsi->header.dsi_command = DSIFUNC_CMD;
+    dsi->header.dsi_flags = DSIFL_REPLY;
+
+    if (!dsi_cmdreply(dsi, AFP_OK)) {
+        LOG(log_error, logtype_afpd, "dsi_cmdreply: %s", strerror(errno) );
+        afp_dsi_close(child.obj);
+        exit(EXITERR_CLNT);
+    }
+
+
+    LOG(log_note, logtype_afpd, "afp_dsi_transfer_session: succesfull primary reconnect");
+    /* 
+     * Now returning from this signal handler return to dsi_receive which should start
+     * reading/continuing from the connected socket that was passed via the parent from
+     * another session. The parent will terminate that session.
+     */
+}
+
 /* */
 static void afp_dsi_sleep(void)
 {
@@ -210,15 +257,20 @@ static void alarm_handler(int sig _U_)
     int err;
     DSI *dsi = (DSI *) child.obj->handle;
 
-    /* we have to restart the timer because some libraries 
-     * may use alarm() */
+    if (child.flags & CHILD_DISCONNECTED) {
+        LOG(log_note, logtype_afpd, "afp_alarm: no reconnect within 10 hours, goodbye");
+        afp_dsi_die(EXITERR_CLNT);
+    }
+
+    /* we have to restart the timer because some libraries may use alarm() */
     setitimer(ITIMER_REAL, &dsi->timer, NULL);
 
     /* we got some traffic from the client since the previous timer 
      * tick. */
     if ((child.flags & CHILD_DATA)) {
         child.flags &= ~CHILD_DATA;
-        return;
+        child.flags &= ~CHILD_DISCONNECTED;
+       return;
     }
 
     /* if we're in the midst of processing something,
@@ -232,10 +284,11 @@ static void alarm_handler(int sig _U_)
             err = dsi_tickle(child.obj->handle);
         if (err <= 0) 
             afp_dsi_die(EXITERR_CLNT);
-        
-    } else { /* didn't receive a tickle. close connection */
-        LOG(log_error, logtype_afpd, "afp_alarm: child timed out");
-        afp_dsi_die(EXITERR_CLNT);
+    } else { /* didn't receive a tickle, enter disconnected state */
+        LOG(log_error, logtype_afpd, "afp_alarm: child timed out, entering disconnected state");
+        struct itimerval t = {{0, 60 * 60 * 10}, {0, 0}}; /* 10 hours */
+        setitimer(ITIMER_REAL, &t, NULL);
+        child.flags |= CHILD_DISCONNECTED;
     }
 }
 
@@ -300,6 +353,22 @@ void afp_over_dsi(AFPObj *obj)
         afp_dsi_die(EXITERR_SYS);
     }
 
+    /* install SIGURG */
+    action.sa_handler = afp_dsi_transfer_session;
+    sigemptyset( &action.sa_mask );
+    sigaddset(&action.sa_mask, SIGALRM);
+    sigaddset(&action.sa_mask, SIGTERM);
+    sigaddset(&action.sa_mask, SIGUSR1);
+    sigaddset(&action.sa_mask, SIGINT);
+#ifdef SERVERTEXT
+    sigaddset(&action.sa_mask, SIGUSR2);
+#endif    
+    action.sa_flags = SA_RESTART;
+    if ( sigaction( SIGURG, &action, NULL ) < 0 ) {
+        LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
+        afp_dsi_die(EXITERR_SYS);
+    }
+
     /* install SIGTERM */
     action.sa_handler = afp_dsi_die;
     sigemptyset( &action.sa_mask );
index 6027b8515850760ec8f4274907ae3be95b70e19a..3e0fd5ebeecae396babc23d48c6d7ee859e99a1e 100644 (file)
@@ -546,7 +546,7 @@ int afp_getsession(
             token = obj->sinfo.sessiontoken;
         }
         break;
-    case 3: /* Jaguar */
+    case 3:
     case 4:
         if (ibuflen >= 8 ) {
             p = ibuf;
@@ -559,7 +559,7 @@ int afp_getsession(
             if (ibuflen < idlen || idlen > (90-10)) {
                 return AFPERR_PARAM;
             }
-            server_ipc_write(IPC_GETSESSION, idlen+8, p );
+            ipc_child_write(obj->ipc_fd, IPC_GETSESSION, idlen+8, p);
             tklen = obj->sinfo.sessiontoken_len;
             token = obj->sinfo.sessiontoken;
         }
@@ -589,10 +589,10 @@ int afp_getsession(
 }
 
 /* ---------------------- */
-int afp_disconnect(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
+int afp_disconnect(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
 {
+    DSI                 *dsi = (DSI *)obj->handle;
     u_int16_t           type;
-
     u_int32_t           tklen;
     pid_t               token;
     int                 i;
@@ -633,11 +633,26 @@ int afp_disconnect(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _
         }
     }
 
-    /* killed old session, not easy */
-    server_ipc_write(IPC_KILLTOKEN, tklen, &token);
-    sleep(1);
+    LOG(log_note, logtype_afpd, "afp_disconnect: trying primary reconnect");
 
-    return AFPERR_SESSCLOS;   /* was AFP_OK */
+    /* check for old session, possibly transfering session from here to there */
+    if (ipc_child_write(obj->ipc_fd, IPC_DISCOLDSESSION, tklen, &token) == -1)
+        goto exit;
+    /* write uint16_t DSI request ID */
+    if (writet(obj->ipc_fd, &dsi->header.dsi_requestID, 2, 0, 2) != 2) {
+        LOG(log_error, logtype_afpd, "afp_disconnect: couldn't send DSI request ID");
+        goto exit;
+    }
+    /* now send our connected AFP client socket */
+    if (send_fd(obj->ipc_fd, dsi->socket) != 0)
+        goto exit;
+    /* Now see what happens: either afpd master kills us because our session */
+    /* has been transfered to a old disconnected session, or we continue    */
+    sleep(2);
+
+exit:
+    LOG(log_error, logtype_afpd, "afp_disconnect: primary reconnect failed");
+    return AFPERR_MISC;
 }
 
 /* ---------------------- */
index e37ebf64644d7546e8a35f7dc8dc5cd9926af090..0910c3514d667b5d5a7e8c5844c1dec0b633d912 100644 (file)
@@ -107,7 +107,7 @@ typedef struct _AFPObj {
     void *uam_cookie; /* cookie for uams */
     struct session_info  sinfo;
     uid_t uid;         /* client running user id */
-
+    int ipc_fd; /* anonymous PF_UNIX socket for IPC with afpd parent */
 #ifdef FORCE_UIDGID
     int                 force_uid;
     uidgidset          uidgid;
index 30a32346238aefc4425c0ba14062ef18dd21aa26..6fa49cf4017f07919ff84e55ddc14c0d42f82049 100644 (file)
@@ -17,8 +17,9 @@
 #include <atalk/logger.h>
 #include <sys/time.h>
 #include <sys/socket.h>
-
+#include <sys/poll.h>
 #include <errno.h>
+#include <sys/wait.h>
 
 #include <atalk/adouble.h>
 
@@ -54,10 +55,15 @@ unsigned char       nologin = 0;
 struct afp_options default_options;
 static AFPConfig *configs;
 static server_child *server_children;
-static fd_set save_rfds;
-static int    Ipc_fd = -1;
 static sig_atomic_t reloadconfig = 0;
 
+/* Two pointers to dynamic allocated arrays which store pollfds and associated data */
+static struct pollfd *fdset;
+static struct polldata *polldata;
+static int fdset_size;          /* current allocated size */
+static int fdset_used;          /* number of used elements */
+
+
 #ifdef TRU64
 void afp_get_cmdline( int *ac, char ***av)
 {
@@ -66,30 +72,41 @@ void afp_get_cmdline( int *ac, char ***av)
 }
 #endif /* TRU64 */
 
-static void afp_exit(const int i)
+/* This is registered with atexit() */
+static void afp_exit(void)
 {
-    server_unlock(default_options.pidfile);
-    exit(i);
+    if (parent_or_child == 0)
+        /* Only do this in the parent */
+        server_unlock(default_options.pidfile);
 }
 
+
 /* ------------------
    initialize fd set we are waiting for.
 */
-static void set_fd(int ipc_fd)
+static void fd_set_listening_sockets(void)
 {
     AFPConfig   *config;
 
-    FD_ZERO(&save_rfds);
     for (config = configs; config; config = config->next) {
         if (config->fd < 0) /* for proxies */
             continue;
-        FD_SET(config->fd, &save_rfds);
-    }
-    if (ipc_fd >= 0) {
-        FD_SET(ipc_fd, &save_rfds);
+        fdset_add_fd(&fdset, &polldata, &fdset_used, &fdset_size, config->fd, LISTEN_FD, config);
     }
 }
  
+static void fd_reset_listening_sockets(void)
+{
+    AFPConfig   *config;
+
+    for (config = configs; config; config = config->next) {
+        if (config->fd < 0) /* for proxies */
+            continue;
+        fdset_del_fd(&fdset, &polldata, &fdset_used, &fdset_size, config->fd);
+    }
+    fd_set_listening_sockets();
+}
+
 /* ------------------ */
 static void afp_goaway(int sig)
 {
@@ -98,7 +115,9 @@ static void afp_goaway(int sig)
     asp_kill(sig);
 #endif /* ! NO_DDP */
 
-    dsi_kill(sig);
+    if (server_children)
+        server_child_kill(server_children, CHILD_DSIFORK, sig);
+
     switch( sig ) {
 
     case SIGTERM :
@@ -107,7 +126,8 @@ static void afp_goaway(int sig)
         for (config = configs; config; config = config->next)
             if (config->server_cleanup)
                 config->server_cleanup(config);
-        afp_exit(0);
+        server_unlock(default_options.pidfile);
+        exit(0);
         break;
 
     case SIGUSR1 :
@@ -129,7 +149,34 @@ static void afp_goaway(int sig)
 
 static void child_handler(int sig _U_)
 {
-    server_child_handler(server_children);
+    int fd;
+    int status, i;
+    pid_t pid;
+  
+#ifndef WAIT_ANY
+#define WAIT_ANY (-1)
+#endif /* ! WAIT_ANY */
+
+    while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) {
+        for (i = 0; i < server_children->nforks; i++) {
+            if ((fd = server_child_remove(server_children, i, pid)) != -1) {
+                fdset_del_fd(&fdset, &polldata, &fdset_used, &fdset_size, fd);        
+                break;
+            }
+        }
+
+        if (WIFEXITED(status)) {
+            if (WEXITSTATUS(status))
+                LOG(log_info, logtype_afpd, "child[%d]: exited %d", pid, WEXITSTATUS(status));
+            else
+                LOG(log_info, logtype_afpd, "child[%d]: done", pid);
+        } else {
+            if (WIFSIGNALED(status))
+                LOG(log_info, logtype_afpd, "child[%d]: killed by signal %d", pid, WTERMSIG(status));
+            else
+                LOG(log_info, logtype_afpd, "child[%d]: died", pid);
+        }
+    }
 }
 
 int main(int ac, char **av)
@@ -170,6 +217,7 @@ int main(int ac, char **av)
     default: /* server */
         exit(0);
     }
+    atexit(afp_exit);
 
     /* install child handler for asp and dsi. we do this before afp_goaway
      * as afp_goaway references stuff from here. 
@@ -177,7 +225,7 @@ int main(int ac, char **av)
     if (!(server_children = server_child_alloc(default_options.connections,
                             CHILD_NFORKS))) {
         LOG(log_error, logtype_afpd, "main: server_child alloc: %s", strerror(errno) );
-        afp_exit(EXITERR_SYS);
+        exit(EXITERR_SYS);
     }
 
     memset(&sv, 0, sizeof(sv));    
@@ -189,7 +237,7 @@ int main(int ac, char **av)
     sigemptyset( &sv.sa_mask );
     if (sigaction(SIGXFSZ, &sv, NULL ) < 0 ) {
         LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
-        afp_exit(EXITERR_SYS);
+        exit(EXITERR_SYS);
     }
 #endif
 #endif
@@ -204,7 +252,7 @@ int main(int ac, char **av)
     sv.sa_flags = SA_RESTART;
     if ( sigaction( SIGCHLD, &sv, NULL ) < 0 ) {
         LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
-        afp_exit(EXITERR_SYS);
+        exit(EXITERR_SYS);
     }
 
     sv.sa_handler = afp_goaway;
@@ -216,7 +264,7 @@ int main(int ac, char **av)
     sv.sa_flags = SA_RESTART;
     if ( sigaction( SIGUSR1, &sv, NULL ) < 0 ) {
         LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
-        afp_exit(EXITERR_SYS);
+        exit(EXITERR_SYS);
     }
 
     sigemptyset( &sv.sa_mask );
@@ -227,7 +275,7 @@ int main(int ac, char **av)
     sv.sa_flags = SA_RESTART;
     if ( sigaction( SIGHUP, &sv, NULL ) < 0 ) {
         LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
-        afp_exit(EXITERR_SYS);
+        exit(EXITERR_SYS);
     }
 
 
@@ -239,7 +287,7 @@ int main(int ac, char **av)
     sv.sa_flags = SA_RESTART;
     if ( sigaction( SIGTERM, &sv, NULL ) < 0 ) {
         LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
-        afp_exit(EXITERR_SYS);
+        exit(EXITERR_SYS);
     }
 
     /* afpd.conf: not in config file: lockfile, connections, configfile
@@ -263,7 +311,7 @@ int main(int ac, char **av)
     pthread_sigmask(SIG_BLOCK, &sigs, NULL);
     if (!(configs = configinit(&default_options))) {
         LOG(log_error, logtype_afpd, "main: no servers configured");
-        afp_exit(EXITERR_CONF);
+        exit(EXITERR_CONF);
     }
     pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
 
@@ -271,10 +319,9 @@ int main(int ac, char **av)
     cnid_init();
 
     /* watch atp, dsi sockets and ipc parent/child file descriptor. */
-    if ((ipc = server_ipc_create())) {
-        Ipc_fd = server_ipc_parent(ipc);
-    }
-    set_fd(Ipc_fd);
+    fd_set_listening_sockets();
+
+    afp_child_t *child;
 
     /* wait for an appleshare connection. parent remains in the loop
      * while the children get handled by afp_over_{asp,dsi}.  this is
@@ -283,16 +330,15 @@ int main(int ac, char **av)
      * afterwards. establishing timeouts for logins is a possible 
      * solution. */
     while (1) {
-        rfds = save_rfds;
+        LOG(log_maxdebug, logtype_afpd, "main: polling %i fds", fdset_used);
         pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
-        ret = select(FD_SETSIZE, &rfds, NULL, NULL, NULL);
+        ret = poll(fdset, fdset_used, -1);
         pthread_sigmask(SIG_BLOCK, &sigs, NULL);
         int saveerrno = errno;
 
         if (reloadconfig) {
             nologin++;
             auth_unload();
-            AFPConfig *config;
 
             LOG(log_info, logtype_afpd, "re-reading configuration file");
             for (config = configs; config; config = config->next)
@@ -304,13 +350,16 @@ int main(int ac, char **av)
             configfree(configs, NULL);
             if (!(configs = configinit(&default_options))) {
                 LOG(log_error, logtype_afpd, "config re-read: no servers configured");
-                afp_exit(EXITERR_CONF);
+                exit(EXITERR_CONF);
             }
-            set_fd(Ipc_fd);
+            fd_reset_listening_sockets();
             nologin = 0;
             reloadconfig = 0;
             errno = saveerrno;
         }
+
+        if (ret == 0)
+            continue;
         
         if (ret < 0) {
             if (errno == EINTR)
@@ -318,17 +367,34 @@ int main(int ac, char **av)
             LOG(log_error, logtype_afpd, "main: can't wait for input: %s", strerror(errno));
             break;
         }
-        if (Ipc_fd >=0 && FD_ISSET(Ipc_fd, &rfds)) {
-            server_ipc_read(server_children);
-        }
-        for (config = configs; config; config = config->next) {
-            if (config->fd < 0)
-                continue;
-            if (FD_ISSET(config->fd, &rfds)) {
-                config->server_start(config, configs, server_children);
-            }
-        }
-    }
+
+        for (int i = 0; i < fdset_used; i++) {
+            if (fdset[i].revents & POLLIN) {
+                switch (polldata[i].fdtype) {
+                case LISTEN_FD:
+                    config = (AFPConfig *)polldata[i].data;
+                    /* config->server_start is afp_config.c:dsi_start() for DSI */
+                    if (child = config->server_start(config, configs, server_children)) {
+                        /* Add IPC fd to select fd set */
+                        fdset_add_fd(&fdset, &polldata, &fdset_used, &fdset_size, child->ipc_fds[0], IPC_FD, child);
+                    }
+                    break;
+                case IPC_FD:
+                    child = (afp_child_t *)polldata[i].data;
+                    LOG(log_debug, logtype_afpd, "main: IPC request from child[%u]", child->pid);
+                    if ((ret = ipc_server_read(server_children, child->ipc_fds[0])) == 0) {
+                        fdset_del_fd(&fdset, &polldata, &fdset_used, &fdset_size, child->ipc_fds[0]);
+                        close(child->ipc_fds[0]);
+                        child->ipc_fds[0] = -1;
+                    }
+                    break;
+                default:
+                    LOG(log_debug, logtype_afpd, "main: IPC request for unknown type");
+                    break;
+                } /* switch */
+            }  /* if */
+        } /* for (i)*/
+    } /* while (1) */
 
     return 0;
 }
index e53599b0149202940c62436733b307106c42abee..7596a75013dcdb973538987337ac05cd67a7fe73 100644 (file)
@@ -225,7 +225,7 @@ static char *volxlate(AFPObj *obj,
     int afpmaster = 0;
     int xlatevolname = 0;
 
-    if (! ((DSI *)obj->handle)->child)
+    if (parent_or_child == 0)
         afpmaster = 1;
 
     if (path && !volname)
@@ -1274,7 +1274,7 @@ int readvolfile(AFPObj *obj, struct afp_volume_name *p1, char *p2, int user, str
             /* check allow/deny lists (if not afpd master loading volumes for Zeroconf reg.):
                allow -> either no list (-1), or in list (1)
                deny -> either no list (-1), or not in list (0) */
-            if (!((DSI *)obj->handle)->child
+            if (parent_or_child == 0
                 ||
                 (accessvol(options[VOLOPT_ALLOW].c_value, obj->username) &&
                  (accessvol(options[VOLOPT_DENY].c_value, obj->username) < 1) &&
@@ -1285,7 +1285,7 @@ int readvolfile(AFPObj *obj, struct afp_volume_name *p1, char *p2, int user, str
                  * 1) neither the rolist nor the rwlist exist -> rw
                  * 2) rolist exists -> ro if user is in it.
                  * 3) rwlist exists -> ro unless user is in it. */
-                if (((DSI *)obj->handle)->child
+                if (parent_or_child == 1
                     &&
                     ((options[VOLOPT_FLAGS].i_value & AFPVOL_RO) == 0)
                     &&
@@ -1739,7 +1739,7 @@ void load_volumes(AFPObj *obj)
         free_volumes();
     }
 
-    if (! ((DSI *)obj->handle)->child) {
+    if (parent_or_child == 0) {
         LOG(log_debug, logtype_afpd, "load_volumes: AFP MASTER");
     } else {
         LOG(log_debug, logtype_afpd, "load_volumes: user: %s", obj->username);
index 85ea7a4fb21b88fd71b19a05a4376fe49c142b7f..0e32b8d5e12d83e20d92ddb3764286c4ad3d9b49 100644 (file)
@@ -47,7 +47,6 @@
 #include <sys/socket.h>
 #include <stdio.h>
 #include <time.h>
-#include <sys/ioctl.h>
 
 #ifndef WEXITSTATUS
 #define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
@@ -150,59 +149,6 @@ static struct server *test_usockfn(struct volinfo *volinfo)
     return NULL;
 }
 
-/* -------------------- */
-static int send_cred(int socket, int fd)
-{
-    int ret;
-    struct msghdr msgh;
-    struct iovec iov[1];
-    struct cmsghdr *cmsgp = NULL;
-    char *buf;
-    size_t size;
-    int er=0;
-
-    size = CMSG_SPACE(sizeof fd);
-    buf = malloc(size);
-    if (!buf) {
-        LOG(log_error, logtype_cnid, "error in sendmsg: %s", strerror(errno));
-        return -1;
-    }
-
-    memset(&msgh,0,sizeof (msgh));
-    memset(buf,0, size);
-
-    msgh.msg_name = NULL;
-    msgh.msg_namelen = 0;
-
-    msgh.msg_iov = iov;
-    msgh.msg_iovlen = 1;
-
-    iov[0].iov_base = &er;
-    iov[0].iov_len = sizeof(er);
-
-    msgh.msg_control = buf;
-    msgh.msg_controllen = size;
-
-    cmsgp = CMSG_FIRSTHDR(&msgh);
-    cmsgp->cmsg_level = SOL_SOCKET;
-    cmsgp->cmsg_type = SCM_RIGHTS;
-    cmsgp->cmsg_len = CMSG_LEN(sizeof(fd));
-
-    *((int *)CMSG_DATA(cmsgp)) = fd;
-    msgh.msg_controllen = cmsgp->cmsg_len;
-
-    do  {
-        ret = sendmsg(socket,&msgh, 0);
-    } while ( ret == -1 && errno == EINTR );
-    if (ret == -1) {
-        LOG(log_error, logtype_cnid, "error in sendmsg: %s", strerror(errno));
-        free(buf);
-        return -1;
-    }
-    free(buf);
-    return 0;
-}
-
 /* -------------------- */
 static int maybe_start_dbd(char *dbdpn, struct volinfo *volinfo)
 {
@@ -220,7 +166,7 @@ static int maybe_start_dbd(char *dbdpn, struct volinfo *volinfo)
     up = test_usockfn(volinfo);
     if (up && up->pid) {
         /* we already have a process, send our fd */
-        if (send_cred(up->control_fd, rqstfd) < 0) {
+        if (send_fd(up->control_fd, rqstfd) < 0) {
             /* FIXME */
             return -1;
         }
index 89355d767b8c0072a01d42763a935850b03e5a9c..0a56be91087f79133e175591a44f908fad25e2f3 100644 (file)
@@ -69,51 +69,6 @@ static void invalidate_fd(int fd)
     return;
 }
 
-static int recv_cred(int fd)
-{
-    int ret;
-    struct msghdr msgh;
-    struct iovec iov[1];
-    struct cmsghdr *cmsgp = NULL;
-    char buf[CMSG_SPACE(sizeof(int))];
-    char dbuf[80];
-
-    memset(&msgh,0,sizeof(msgh));
-    memset(buf,0,sizeof(buf));
-
-    msgh.msg_name = NULL;
-    msgh.msg_namelen = 0;
-
-    msgh.msg_iov = iov;
-    msgh.msg_iovlen = 1;
-
-    iov[0].iov_base = dbuf;
-    iov[0].iov_len = sizeof(dbuf);
-
-    msgh.msg_control = buf;
-    msgh.msg_controllen = sizeof(buf);
-
-    do  {
-        ret = recvmsg(fd ,&msgh,0);
-    } while ( ret == -1 && errno == EINTR );
-
-    if ( ret == -1 ) {
-        return -1;
-    }
-
-    for ( cmsgp = CMSG_FIRSTHDR(&msgh); cmsgp != NULL; cmsgp = CMSG_NXTHDR(&msgh,cmsgp) ) {
-        if ( cmsgp->cmsg_level == SOL_SOCKET && cmsgp->cmsg_type == SCM_RIGHTS ) {
-            return *(int *) CMSG_DATA(cmsgp);
-        }
-    }
-
-    if ( ret == sizeof (int) )
-        errno = *(int *)dbuf; /* Rcvd errno */
-    else
-        errno = ENOENT;    /* Default errno */
-
-    return -1;
-}
 
 /*
  *  Check for client requests. We keep up to fd_table_size open descriptors in
@@ -163,7 +118,7 @@ static int check_fd(time_t timeout, const sigset_t *sigmask, time_t *now)
     if (FD_ISSET(control_fd, &readfds)) {
         int    l = 0;
 
-        fd = recv_cred(control_fd);
+        fd = recv_fd(control_fd, 0);
         if (fd < 0) {
             return -1;
         }
index 0616774da18da49e2afb74ccc971bd13d40b6d19..82b98348e6a35e8c41a23a627043535d03bbcde8 100644 (file)
@@ -78,8 +78,8 @@ typedef struct DSI {
   size_t datalen, cmdlen;
   off_t  read_count, write_count;
   int asleep; /* client won't reply AFP 0x7a ? */
-  /* inited = initialized?, child = a child?, noreply = send reply? */
-  char child, inited, noreply;
+  /* noreply = send reply? */
+  char noreply;
   const char *program; 
   int socket, serversock;
 
@@ -157,7 +157,7 @@ extern DSI *dsi_init (const dsi_proto /*protocol*/,
 extern void dsi_setstatus (DSI *, char *, const size_t);
 
 /* in dsi_getsess.c */
-extern DSI *dsi_getsession (DSI *, server_child *, const int);
+extern afp_child_t *dsi_getsession (DSI *, server_child *, const int);
 extern void dsi_kill (int);
 
 
index 8479367bb4f42b2c8786d0ce4a48145aef07b659..b45a3a42daf088c958618a69a6f350c4ff6653a4 100644 (file)
@@ -23,17 +23,30 @@ typedef struct server_child {
   int count, nsessions, nforks;
 } server_child;
 
+typedef struct server_child_data {
+  pid_t     pid;               /* afpd worker process pid (from the worker afpd process )*/
+  uid_t     uid;               /* user id of connected client (from the worker afpd process) */
+  int       valid;             /* 1 if we have a clientid */
+  uint32_t  time;              /* client boot time (from the mac client) */
+  int       killed;            /* 1 if we already tried to kill the client */
+  uint32_t  idlen;             /* clientid len (from the Mac client) */
+  char      *clientid;  /* clientid (from the Mac client) */
+  int       ipc_fds[2]; /* socketpair for IPC bw */
+  struct server_child_data **prevp, *next;
+} afp_child_t;
+
+extern int parent_or_child;
+
 /* server_child.c */
 extern server_child *server_child_alloc (const int, const int);
-extern int server_child_add (server_child *, const int, const pid_t);
-extern int server_child_remove (server_child *, const int, const pid_t);
+extern afp_child_t *server_child_add (server_child *, int, pid_t, uint ipc_fds[2]);
+extern int  server_child_remove (server_child *, const int, const pid_t);
 extern void server_child_free (server_child *);
 
 extern void server_child_kill (server_child *, const int, const int);
-extern void server_child_kill_one (server_child *children, const int forkid, const pid_t, const uid_t);
 extern void server_child_kill_one_by_id (server_child *children, const int forkid, const pid_t pid, const uid_t,
                                                const u_int32_t len, char *id, u_int32_t boottime);
-
+extern int  server_child_transfer_session(server_child *children, int forkid, pid_t, uid_t, int, uint16_t);
 extern void server_child_setup (server_child *, const int, void (*)(const pid_t));
 extern void server_child_handler (server_child *);
 extern void server_reset_signal (void);
index eb899f8c891564cb7fe2a509c9a0531eba6d8475..332897a0993bd2f34ca3be79f942bfd75fe27818 100644 (file)
@@ -1,16 +1,12 @@
+#ifndef ATALK_SERVER_IPC_H
+#define ATALK_SERVER_IPC_H
 
 #include <atalk/server_child.h>
 
-#define IPC_KILLTOKEN   1
-#define IPC_GETSESSION  2
-
-void *server_ipc_create(void);
-int server_ipc_child(void *obj);
-int server_ipc_parent(void *obj);
-int server_ipc_read(server_child *children);
-int server_ipc_write(u_int16_t command, int len, void *token);
-
-
-
+#define IPC_DISCOLDSESSION   0
+#define IPC_GETSESSION       1
 
+int ipc_server_read(server_child *children, int fd);
+int ipc_child_write(int fd, uint16_t command, int len, void *token);
 
+#endif /* IPC_GETSESSION_LOGIN */
index 63da2935df0733c74fceb9f31aee241b7c3f4cef..457b0c8a97a9dda25f01750705a06b6f50c417b1 100644 (file)
@@ -16,6 +16,7 @@
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif /* HAVE_UNISTD_H */
+#include <poll.h>
 #include <netatalk/at.h>
 #include <atalk/unicode.h>
 
@@ -128,11 +129,35 @@ extern int lock_reg(int fd, int cmd, int type, off_t offest, int whence, off_t l
 
 extern int setnonblock(int fd, int cmd);
 extern ssize_t readt(int socket, void *data, const size_t length, int setnonblocking, int timeout);
+extern ssize_t writet(int socket, void *data, const size_t length, int setnonblocking, int timeout);
 extern const char *getip_string(const struct sockaddr *sa);
 extern unsigned int getip_port(const struct sockaddr *sa);
 extern void apply_ip_mask(struct sockaddr *ai, int maskbits);
 extern int compare_ip(const struct sockaddr *sa1, const struct sockaddr *sa2);
 
+/* Structures and functions dealing with dynamic pollfd arrays */
+enum fdtype {IPC_FD, LISTEN_FD};
+struct polldata {
+    enum fdtype fdtype; /* IPC fd or listening socket fd                 */
+    void *data;         /* pointer to AFPconfig for listening socket and *
+                         * pointer to afp_child_t for IPC fd             */
+};
+
+extern void fdset_add_fd(struct pollfd **fdsetp,
+                         struct polldata **polldatap,
+                         int *fdset_usedp,
+                         int *fdset_sizep,
+                         int fd,
+                         enum fdtype fdtype,
+                         void *data);
+extern void fdset_del_fd(struct pollfd **fdsetp,
+                         struct polldata **polldatap,
+                         int *fdset_usedp,
+                         int *fdset_sizep,
+                         int fd);
+extern int send_fd(int socket, int fd);
+extern int recv_fd(int fd, int nonblocking);
+
 /******************************************************************
  * unix.c
  *****************************************************************/
index 51d87846820c0f34c92b36af9b5556faace25b98..5a1d585494af94c6811ff3efcf01714a3b9052d1 100644 (file)
@@ -266,6 +266,7 @@ ASP asp_getsession(ASP asp, server_child *server_children,
                            &(atp_sockaddr(asp->asp_atp)->sat_addr))) == NULL) 
          return NULL;
 
+    int dummy[2];
        switch ((pid = fork())) {
        case 0 : /* child */
          server_reset_signal();
@@ -296,29 +297,26 @@ ASP asp_getsession(ASP asp, server_child *server_children,
          break;
          
        default : /* parent process */
-         /* we need atomic setting or pb with tickle_handler 
-         */ 
-         switch (server_child_add(children, CHILD_ASPFORK, pid)) {
-         case 0: /* added child */
-           if ((asp_ac_tmp = (struct asp_child *) 
-                malloc(sizeof(struct asp_child)))) {
-             asp_ac_tmp->ac_pid = pid;
-             asp_ac_tmp->ac_state = ACSTATE_OK;
-             asp_ac_tmp->ac_sat = sat;
-             asp_ac_tmp->ac_sat.sat_port = asp->cmdbuf[1];
-             
-             asp->cmdbuf[0] = atp_sockaddr(atp)->sat_port;
-             asp->cmdbuf[1] = sid;
-             set_asp_ac(sid, asp_ac_tmp);
-             asperr = ASPERR_OK;
-             break;
-           } /* fall through if malloc fails */
-         case -1: /* bad error */
+         /* we need atomic setting or pb with tickle_handler */ 
+      if (server_child_add(children, CHILD_ASPFORK, pid, dummy)) {
+           if ((asp_ac_tmp = malloc(sizeof(struct asp_child))) == NULL) {
+            kill(pid, SIGQUIT); 
+            break;
+        }
+        asp_ac_tmp->ac_pid = pid;
+        asp_ac_tmp->ac_state = ACSTATE_OK;
+        asp_ac_tmp->ac_sat = sat;
+        asp_ac_tmp->ac_sat.sat_port = asp->cmdbuf[1];
+           
+        asp->cmdbuf[0] = atp_sockaddr(atp)->sat_port;
+        asp->cmdbuf[1] = sid;
+        set_asp_ac(sid, asp_ac_tmp);
+        asperr = ASPERR_OK;
+        break;
+      } else {
            kill(pid, SIGQUIT); 
            break;
-         default: /* non-fatal error */
-           break;
-         }
+      }
          atp_close(atp);
          break;
        }
index bde7318f28a6e7baa86d5868a3aa5ca03f6c5ffd..f7b89c8e1cf3deee9e21402bcea3be109226cc35 100644 (file)
 #include <atalk/dsi.h>
 #include <atalk/server_child.h>
 
-static server_child *children = NULL;
-
-void dsi_kill(int sig)
-{
-  if (children)
-    server_child_kill(children, CHILD_DSIFORK, sig);
-}
-
 /* hand off the command. return child connection to the main program */
-DSI *dsi_getsession(DSI *dsi, server_child *serv_children, 
-                   const int tickleval)
+afp_child_t *dsi_getsession(DSI *dsi, server_child *serv_children, int tickleval)
 {
   pid_t pid;
-  
-  /* do a couple things on first entry */
-  if (!dsi->inited) {
-    if (!(children = serv_children))
-      return NULL;
-    dsi->inited = 1;
+  unsigned int ipc_fds[2];  
+  afp_child_t *child;
+
+  if (socketpair(PF_UNIX, SOCK_STREAM, 0, ipc_fds) < 0) {
+      LOG(log_error, logtype_afpd, "dsi_getsess: %s", strerror(errno));
+      exit( EXITERR_CLNT );
   }
-  
-  switch (pid = dsi->proto_open(dsi)) {
+
+  if (setnonblock(ipc_fds[0], 1) != 0 || setnonblock(ipc_fds[1], 1) != 0) {
+      LOG(log_error, logtype_afpd, "dsi_getsess: setnonblock: %s", strerror(errno));
+      exit(EXITERR_CLNT);
+  }
+
+  switch (pid = dsi->proto_open(dsi)) { /* in libatalk/dsi/dsi_tcp.c */
   case -1:
     /* if we fail, just return. it might work later */
     LOG(log_error, logtype_dsi, "dsi_getsess: %s", strerror(errno));
-    return dsi;
+    return NULL;
 
   case 0: /* child. mostly handled below. */
-    dsi->child = 1;
     break;
 
   default: /* parent */
     /* using SIGQUIT is hokey, but the child might not have
      * re-established its signal handler for SIGTERM yet. */
-    if (server_child_add(children, CHILD_DSIFORK, pid) < 0) {
+    if ((child = server_child_add(serv_children, CHILD_DSIFORK, pid, ipc_fds)) < 0) {
       LOG(log_error, logtype_dsi, "dsi_getsess: %s", strerror(errno));
       dsi->header.dsi_flags = DSIFL_REPLY;
       dsi->header.dsi_code = DSIERR_SERVBUSY;
@@ -79,14 +74,13 @@ DSI *dsi_getsession(DSI *dsi, server_child *serv_children,
       dsi->header.dsi_code = DSIERR_OK;
       kill(pid, SIGQUIT);
     }
-
     dsi->proto_close(dsi);
-    return dsi;
+    return child;
   }
   
   /* child: check number of open connections. this is one off the
    * actual count. */
-  if ((children->count >= children->nsessions) &&
+  if ((serv_children->count >= serv_children->nsessions) &&
       (dsi->header.dsi_command == DSIFUNC_OPEN)) {
     LOG(log_info, logtype_dsi, "dsi_getsess: too many connections");
     dsi->header.dsi_flags = DSIFL_REPLY;
@@ -97,8 +91,7 @@ DSI *dsi_getsession(DSI *dsi, server_child *serv_children,
 
   /* get rid of some stuff */
   close(dsi->serversock);
-  server_child_free(children); 
-  children = NULL;
+  server_child_free(serv_children); 
 
   switch (dsi->header.dsi_command) {
   case DSIFUNC_STAT: /* send off status and return */
@@ -129,7 +122,10 @@ DSI *dsi_getsession(DSI *dsi, server_child *serv_children,
     dsi->timer.it_interval.tv_usec = dsi->timer.it_value.tv_usec = 0;
     signal(SIGPIPE, SIG_IGN); /* we catch these ourselves */
     dsi_opensession(dsi);
-    return dsi;
+    if ((child = calloc(1, sizeof(afp_child_t))) == NULL)
+        exit(EXITERR_SYS);
+    child->ipc_fds[1] = ipc_fds[1];
+    return child;
     break;
 
   default: /* just close */
index 9c44116a8494944fa15a96346c4b31e02ab9d6de..0c60303ecb6159c5310d44d28f59242876e69e2d 100644 (file)
@@ -127,6 +127,9 @@ static int dsi_tcp_open(DSI *dsi)
         u_int8_t block[DSI_BLOCKSIZ];
         size_t stored;
 
+        /* Immediateyl mark globally that we're a child now */
+        parent_or_child = 1;
+
         /* reset signals */
         server_reset_signal();
 
index b21bae35f08fe427b4f1f8eed027f0420cb2dd57..a5707fa2d9f5afdf2078ba653085b8da2356cb23 100644 (file)
@@ -1,15 +1,13 @@
 /*
- * $Id: server_child.c,v 1.12 2010-01-21 14:14:49 didg Exp $
- *
  * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
  * All rights reserved. See COPYRIGHT.
  *
  *
- * handle inserting, removing, and freeing of children. 
+ * handle inserting, removing, and freeing of children.
  * this does it via a hash table. it incurs some overhead over
  * a linear append/remove in total removal and kills, but it makes
  * single-entry removals a fast operation. as total removals occur during
- * child initialization and kills during server shutdown, this is 
+ * child initialization and kills during server shutdown, this is
  * probably a win for a lot of connections and unimportant for a small
  * number of connections.
  */
@@ -24,7 +22,7 @@
 #include <unistd.h>
 #endif /* HAVE_UNISTD_H */
 #include <signal.h>
-#include <atalk/logger.h>
+#include <errno.h>
 
 /* POSIX.1 sys/wait.h check */
 #include <sys/types.h>
 #endif /* HAVE_SYS_WAIT_H */
 #include <sys/time.h>
 
+#include <atalk/logger.h>
+#include <atalk/errchk.h>
+#include <atalk/util.h>
+#include <atalk/server_child.h>
+
 #ifndef WEXITSTATUS
 #define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
 #endif /* ! WEXITSTATUS */
 #define WIFSTOPPED(status) (((status) & 0xff) == 0x7f)
 #endif
 #ifndef WIFSIGNALED
-#define WIFSIGNALED(status) (!WIFSTOPPED(status) && !WIFEXITED(status)) 
+#define WIFSIGNALED(status) (!WIFSTOPPED(status) && !WIFEXITED(status))
 #endif
 #ifndef WTERMSIG
 #define WTERMSIG(status)      ((status) & 0x7f)
 #endif
 
-#include <atalk/server_child.h>
-
 /* hash/child functions: hash OR's pid */
 #define CHILD_HASHSIZE 32
 #define HASH(i) ((((i) >> 8) ^ (i)) & (CHILD_HASHSIZE - 1))
 
-struct server_child_data {
-  pid_t     pid;               /* afpd worker process pid (from the worker afpd process )*/
-  uid_t     uid;               /* user id of connected client (from the worker afpd process) */
-  int       valid;             /* 1 if we have a clientid */
-  u_int32_t time;              /* client boot time (from the mac client) */
-  int       killed;            /* 1 if we already tried to kill the client */
-
-  u_int32_t idlen;             /* clientid len (from the Mac client) */
-  char *clientid;              /* clientid (from the Mac client) */
-  struct server_child_data **prevp, *next;
-};
-
 typedef struct server_child_fork {
-  struct server_child_data *table[CHILD_HASHSIZE];
-  void (*cleanup)(const pid_t);
+    struct server_child_data *table[CHILD_HASHSIZE];
+    void (*cleanup)(const pid_t);
 } server_child_fork;
 
+int parent_or_child; /* 0: parent, 1: child */
+
 static inline void hash_child(struct server_child_data **htable,
-                                 struct server_child_data *child)
+                              struct server_child_data *child)
 {
-  struct server_child_data **table;
+    struct server_child_data **table;
 
-  table = &htable[HASH(child->pid)];
-  if ((child->next = *table) != NULL)
-    (*table)->prevp = &child->next;
-  *table = child;
-  child->prevp = table;
+    table = &htable[HASH(child->pid)];
+    if ((child->next = *table) != NULL)
+        (*table)->prevp = &child->next;
+    *table = child;
+    child->prevp = table;
 }
 
 static inline void unhash_child(struct server_child_data *child)
 {
-  if (child->prevp) {
-    if (child->next)
-      child->next->prevp = child->prevp;
-    *(child->prevp) = child->next;
-  }
+    if (child->prevp) {
+        if (child->next)
+            child->next->prevp = child->prevp;
+        *(child->prevp) = child->next;
+    }
 }
 
-static inline struct server_child_data 
-*resolve_child(struct server_child_data **table, const pid_t pid)
+static struct server_child_data *resolve_child(struct server_child_data **table, pid_t pid)
 {
-  struct server_child_data *child;
+    struct server_child_data *child;
 
-  for (child = table[HASH(pid)]; child; child = child->next) {
-    if (child->pid == pid)
-      break;
-  }
+    for (child = table[HASH(pid)]; child; child = child->next) {
+        if (child->pid == pid)
+            break;
+    }
 
-  return child;
+    return child;
 }
 
 /* initialize server_child structure */
 server_child *server_child_alloc(const int connections, const int nforks)
 {
-  server_child *children;
+    server_child *children;
 
-  children = (server_child *) calloc(1, sizeof(server_child));
-  if (!children)
-    return NULL;
+    children = (server_child *) calloc(1, sizeof(server_child));
+    if (!children)
+        return NULL;
 
-  children->nsessions = connections;
-  children->nforks = nforks;
-  children->fork = (void *) calloc(nforks, sizeof(server_child_fork));
-  
-  if (!children->fork) {
-    free(children);
-    return NULL;
-  } 
+    children->nsessions = connections;
+    children->nforks = nforks;
+    children->fork = (void *) calloc(nforks, sizeof(server_child_fork));
+
+    if (!children->fork) {
+        free(children);
+        return NULL;
+    }
 
-  return children;
+    return children;
 }
 
-/* add a child in. return 0 on success, -1 on serious error, and
- * > 0 for a non-serious error. */
-int server_child_add(server_child *children, const int forkid,
-                    const pid_t pid)
+/*!
+ * add a child
+ * @return pointer to struct server_child_data on success, NULL on error
+ */
+afp_child_t *server_child_add(server_child *children, int forkid, pid_t pid, uint ipc_fds[2])
 {
-  server_child_fork *fork;
-  struct server_child_data *child;
-  sigset_t sig, oldsig;
-
-  /* we need to prevent deletions from occuring before we get a 
-   * chance to add the child in. */
-  sigemptyset(&sig);
-  sigaddset(&sig, SIGCHLD);
-  pthread_sigmask(SIG_BLOCK, &sig, &oldsig);
-
-  /* it's possible that the child could have already died before the
-   * pthread_sigmask. we need to check for this. */
-  if (kill(pid, 0) < 0) {
-    pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
-    return 1;
-  }
+    server_child_fork *fork;
+    afp_child_t *child = NULL;
+    sigset_t sig, oldsig;
 
-  fork = (server_child_fork *) children->fork + forkid;
+    /* we need to prevent deletions from occuring before we get a
+     * chance to add the child in. */
+    sigemptyset(&sig);
+    sigaddset(&sig, SIGCHLD);
+    pthread_sigmask(SIG_BLOCK, &sig, &oldsig);
 
-  /* if we already have an entry. just return. */
-  if (resolve_child(fork->table, pid)) {
-    pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
-    return 0;
-  }
+    /* it's possible that the child could have already died before the
+     * pthread_sigmask. we need to check for this. */
+    if (kill(pid, 0) < 0)
+        goto exit;
 
-  if ((child = (struct server_child_data *) 
-       calloc(1, sizeof(struct server_child_data))) == NULL) {
-    pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
-    return -1;
-  }
+    fork = (server_child_fork *) children->fork + forkid;
 
-  child->pid = pid;
-  child->valid = 0;
-  child->killed = 0;
-  hash_child(fork->table, child);
-  children->count++;
-  pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
+    /* if we already have an entry. just return. */
+    if (child = resolve_child(fork->table, pid))
+        goto exit;
 
-  return 0;
+    if ((child = calloc(1, sizeof(afp_child_t))) == NULL)
+        goto exit;
+
+    child->pid = pid;
+    child->valid = 0;
+    child->killed = 0;
+    child->ipc_fds[0] = ipc_fds[0];
+    child->ipc_fds[1] = ipc_fds[1];
+
+    hash_child(fork->table, child);
+    children->count++;
+
+exit:
+    pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
+    return child;
 }
 
 /* remove a child and free it */
-int server_child_remove(server_child *children, const int forkid,
-                       const pid_t pid)
+int server_child_remove(server_child *children, const int forkid, pid_t pid)
 {
-  server_child_fork *fork;
-  struct server_child_data *child;
-
-  fork = (server_child_fork *) children->fork + forkid;
-  if (!(child = resolve_child(fork->table, pid)))
-    return 0;
-  
-  unhash_child(child);
-  if (child->clientid) {
-      free(child->clientid);
-  }
-  free(child);
-  children->count--;
-  return 1;
+    int fd;
+    server_child_fork *fork;
+    struct server_child_data *child;
+
+    fork = (server_child_fork *) children->fork + forkid;
+    if (!(child = resolve_child(fork->table, pid)))
+        return -1;
+
+    unhash_child(child);
+    if (child->clientid) {
+        free(child->clientid);
+        child->clientid = NULL;
+    }
+
+    /* In main:child_handler() we need the fd in order to remove it from the pollfd set */
+    fd = child->ipc_fds[0];
+    if (child->ipc_fds[0] != -1) {
+        close(child->ipc_fds[0]);
+        child->ipc_fds[0] = -1;
+    }
+    if (child->ipc_fds[1] != -1) {
+        close(child->ipc_fds[1]);
+        child->ipc_fds[1] = -1;
+    }
+
+    free(child);
+    children->count--;
+
+    if (fork->cleanup)
+        fork->cleanup(pid);
+
+    return fd;
 }
 
 /* free everything: by using a hash table, this increases the cost of
  * this part over a linked list by the size of the hash table */
 void server_child_free(server_child *children)
 {
-  server_child_fork *fork;
-  struct server_child_data *child, *tmp;
-  int i, j;
-
-  for (i = 0; i < children->nforks; i++) {
-    fork = (server_child_fork *) children->fork + i;
-    for (j = 0; j < CHILD_HASHSIZE; j++) {
-      child = fork->table[j]; /* start at the beginning */
-      while (child) {
-       tmp = child->next;
-        if (child->clientid) {
-            free(child->clientid);
+    server_child_fork *fork;
+    struct server_child_data *child, *tmp;
+    int i, j;
+
+    for (i = 0; i < children->nforks; i++) {
+        fork = (server_child_fork *) children->fork + i;
+        for (j = 0; j < CHILD_HASHSIZE; j++) {
+            child = fork->table[j]; /* start at the beginning */
+            while (child) {
+                tmp = child->next;
+                if (child->clientid) {
+                    free(child->clientid);
+                }
+                free(child);
+                child = tmp;
+            }
         }
-       free(child);
-       child = tmp;
-      }
     }
-  }
-  free(children->fork);
-  free(children);
+    free(children->fork);
+    free(children);
 }
 
-/* send kill to child processes: this also has an increased cost over
- * a plain-old linked list */
-void server_child_kill(server_child *children, const int forkid,
-                      const int sig)
+/* send signal to all child processes */
+void server_child_kill(server_child *children, int forkid, int sig)
 {
-  server_child_fork *fork;
-  struct server_child_data *child, *tmp;
-  int i;
-
-  fork = (server_child_fork *) children->fork + forkid;
-  for (i = 0; i < CHILD_HASHSIZE; i++) {
-    child = fork->table[i];
-    while (child) {
-      tmp = child->next;
-      kill(child->pid, sig);
-      child = tmp;
+    server_child_fork *fork;
+    struct server_child_data *child, *tmp;
+    int i;
+
+    fork = (server_child_fork *) children->fork + forkid;
+    for (i = 0; i < CHILD_HASHSIZE; i++) {
+        child = fork->table[i];
+        while (child) {
+            tmp = child->next;
+            kill(child->pid, sig);
+            child = tmp;
+        }
     }
-  }
 }
 
 /* send kill to a child processes.
- * a plain-old linked list 
+ * a plain-old linked list
  * FIXME use resolve_child ?
  */
 static int kill_child(struct server_child_data *child)
 {
-  if (!child->killed) {
-     kill(child->pid, SIGTERM);
-     /* we don't wait because there's no guarantee that we can really kill it */
-     child->killed = 1;
-     return 1;
-  }
-  else {
-     LOG(log_info, logtype_default, "Already tried to kill (%d) before! Still there?",  child->pid);
-  }
-  return 0;
+    if (!child->killed) {
+        kill(child->pid, SIGTERM);
+        /* we don't wait because there's no guarantee that we can really kill it */
+        child->killed = 1;
+        return 1;
+    } else {
+        LOG(log_info, logtype_default, "Unresponsive child[%d], sending SIGKILL", child->pid);
+        kill(child->pid, SIGKILL);
+    }
+    return 1;
 }
 
-/* -------------------- */
-void server_child_kill_one(server_child *children, const int forkid, const pid_t pid, const uid_t uid)
+/*!
+ * Try to find an old session and pass socket
+ * @returns -1 on error, 0 if no matching session was found, 1 if session was found and socket passed
+ */
+int server_child_transfer_session(server_child *children,
+                                  int forkid,
+                                  pid_t pid,
+                                  uid_t uid,
+                                  int afp_socket,
+                                  uint16_t DSI_requestID)
 {
-  server_child_fork *fork;
-  struct server_child_data *child, *tmp;
-  int i;
-
-  fork = (server_child_fork *) children->fork + forkid;
-  for (i = 0; i < CHILD_HASHSIZE; i++) {
-    child = fork->table[i];
-    while (child) {
-      tmp = child->next;
-      if (child->pid == pid) {
-          if (!child->valid) {
-             /* hmm, client 'guess' the pid, rogue? */
-             LOG(log_info, logtype_default, "Disconnecting old session (%d) no token, bailout!",  child->pid);
-          }
-          else if (child->uid != uid) {
-             LOG(log_info, logtype_default, "Disconnecting old session (%d) not the same user, bailout!",  child->pid);
-          }
-          else {
-              kill_child(child);
-          }
-      }
-      child = tmp;
+    EC_INIT;
+    server_child_fork *fork;
+    struct server_child_data *child;
+    int i;
+
+    fork = (server_child_fork *) children->fork + forkid;
+    if ((child = resolve_child(fork->table, pid)) == NULL) {
+        LOG(log_note, logtype_default, "Reconnect: no child[%u]", pid);
+        return 0;
     }
-  }
+
+    if (!child->valid) {
+        /* hmm, client 'guess' the pid, rogue? */
+        LOG(log_error, logtype_default, "Reconnect: invalidated child[%u]", pid);
+        return 0;
+    } else if (child->uid != uid) {
+        LOG(log_error, logtype_default, "Reconnect: child[%u] not the same user", pid);
+        return 0;
+    }
+
+    LOG(log_note, logtype_default, "Reconnect: transfering session to child[%u]", pid);
+    
+    if (writet(child->ipc_fds[0], &DSI_requestID, 2, 0, 2) != 2) {
+        LOG(log_error, logtype_default, "Reconnect: error sending DSI id to child[%u]", pid);
+        EC_STATUS(-1);
+        goto EC_CLEANUP;
+    }
+    EC_ZERO_LOG(send_fd(child->ipc_fds[0], afp_socket));
+    EC_ZERO_LOG(kill(pid, SIGURG));
+
+    EC_STATUS(1);
+
+EC_CLEANUP:
+    EC_EXIT;
 }
 
 
 /* see if there is a process for the same mac     */
 /* if the times don't match mac has been rebooted */
-void server_child_kill_one_by_id(server_child *children, const int forkid, const pid_t pid,
-          const uid_t uid, 
-          const u_int32_t idlen, char *id, u_int32_t boottime)
+void server_child_kill_one_by_id(server_child *children, int forkid, pid_t pid,
+                                 uid_t uid, uint32_t idlen, char *id, uint32_t boottime)
 {
-  server_child_fork *fork;
-  struct server_child_data *child, *tmp;
-  int i;
-
-  fork = (server_child_fork *) children->fork + forkid;
-  for (i = 0; i < CHILD_HASHSIZE; i++) {
-    child = fork->table[i];
-    while (child) {
-      tmp = child->next;
-      if ( child->pid != pid) {
-          if ( child->idlen == idlen && !memcmp(child->clientid, id, idlen)) {
-            if ( child->time != boottime ) {
-                 if (uid == child->uid) {
-                     if (kill_child(child)) {
-                         LOG(log_info, logtype_default, "Disconnecting old session %d, client rebooted.",  child->pid);
-                     }
-                 }
-                 else {
-                     LOG(log_info, logtype_default, "Disconnecting old session not the same uid, bailout!");
-                 }
-            }
-            else if (child->killed) {
-                 /* there's case where a Mac close a connection and restart a new one before
-                  * the first is 'waited' by the master afpd process
-                 */
-                 LOG(log_info, logtype_default, 
-                     "WARNING: connection (%d) killed but still there.", child->pid);
-            }
-            else {
-                 LOG(log_info, logtype_default, 
-                     "WARNING: 2 connections (%d, %d), boottime identical, don't know if one needs to be disconnected.",
-                      child->pid, pid);
-            } 
-               
-         }
-      }
-      else 
-      {
-         child->time = boottime;
-         /* free old token if any */
-         if (child->clientid) {
-             free(child->clientid);
-         }
-          child->uid = uid; 
-          child->valid = 1;
-         child->idlen = idlen;
-          child->clientid = id;
-         LOG(log_debug, logtype_default, "Setting clientid (len %d) for %d, boottime %X", idlen, child->pid, boottime);
-      }
-      child = tmp;
+    server_child_fork *fork;
+    struct server_child_data *child, *tmp;
+    int i;
+
+    fork = (server_child_fork *)children->fork + forkid;
+
+    for (i = 0; i < CHILD_HASHSIZE; i++) {
+        child = fork->table[i];
+        while (child) {
+            tmp = child->next;
+            if ( child->pid != pid) {
+                if (child->idlen == idlen && memcmp(child->clientid, id, idlen) == 0) {
+                    if ( child->time != boottime ) {
+                        if (uid == child->uid) {
+                            kill_child(child);
+                            LOG(log_note, logtype_default, "Disconnected child[%u], client rebooted.", child->pid);
+                        } else {
+                            LOG(log_note, logtype_default, "Session with different pid[%u]", child->pid);
+                        }
+                    }
+                    if (child->killed)
+                        kill_child(child); /* this will send SIGKILL */
+                    LOG(log_note, logtype_default, "", child->pid, pid);
+                }
+            } else {
+                /* update childs own slot */
+                child->time = boottime;
+                if (child->clientid)
+                    free(child->clientid);
+                LOG(log_debug, logtype_default, "Setting client ID for %d", child->pid);
+                child->uid = uid;
+                child->valid = 1;
+                child->idlen = idlen;
+                child->clientid = id;
+            }
+            child = tmp;
+        }
     }
-  }
 }
 
 /* for extra cleanup if necessary */
 void server_child_setup(server_child *children, const int forkid,
-                         void (*fcn)(const pid_t))
+                        void (*fcn)(const pid_t))
 {
-  server_child_fork *fork;
+    server_child_fork *fork;
 
-  fork = (server_child_fork *) children->fork + forkid;
-  fork->cleanup = fcn;
+    fork = (server_child_fork *) children->fork + forkid;
+    fork->cleanup = fcn;
 }
 
 
-/* keep track of children. */
-void server_child_handler(server_child *children)
-{
-  int status, i;
-  pid_t pid;
-  
-#ifndef WAIT_ANY
-#define WAIT_ANY (-1)
-#endif /* ! WAIT_ANY */
-
-  while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) {
-    for (i = 0; i < children->nforks; i++) {
-      if (server_child_remove(children, i, pid)) {
-        server_child_fork *fork;
-        
-       fork = (server_child_fork *) children->fork + i;
-       if (fork->cleanup)
-         fork->cleanup(pid);
-       break;
-      }
-    }
-
-    if (WIFEXITED(status)) {
-      if (WEXITSTATUS(status)) {
-       LOG(log_info, logtype_default, "server_child[%d] %d exited %d", i, pid, 
-              WEXITSTATUS(status));
-      } else {
-       LOG(log_info, logtype_default, "server_child[%d] %d done", i, pid);
-      }
-    } else {
-      if (WIFSIGNALED(status))
-      { 
-       LOG(log_info, logtype_default, "server_child[%d] %d killed by signal %d", i, pid,  
-              WTERMSIG (status));
-      }
-      else
-      {
-       LOG(log_info, logtype_default, "server_child[%d] %d died", i, pid);
-      }
-    }
-  }
-}
-
-/* --------------------------- 
+/* ---------------------------
  * reset children signals
-*/
+ */
 void server_reset_signal(void)
 {
     struct sigaction    sv;
@@ -415,18 +377,18 @@ void server_reset_signal(void)
     memset(&sv, 0, sizeof(sv));
     sv.sa_handler =  SIG_DFL;
     sigemptyset( &sv.sa_mask );
-    
+
     sigaction(SIGALRM, &sv, NULL );
     sigaction(SIGHUP,  &sv, NULL );
     sigaction(SIGTERM, &sv, NULL );
     sigaction(SIGUSR1, &sv, NULL );
     sigaction(SIGCHLD, &sv, NULL );
-    
+
     sigemptyset(&sigs);
     sigaddset(&sigs, SIGALRM);
     sigaddset(&sigs, SIGHUP);
     sigaddset(&sigs, SIGUSR1);
     sigaddset(&sigs, SIGCHLD);
     pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
-        
+
 }
index 289197fb7ac89a76c2da7e0cd91a3abfa2527e96..76d3bc3b8a1f492433b77084ab6d207e0e5179ec 100644 (file)
@@ -1,10 +1,7 @@
 /*
- * $Id: server_ipc.c,v 1.4 2010-01-21 14:14:49 didg Exp $
- *
  * All rights reserved. See COPYRIGHT.
  *
- *
- * ipc between parent and children.
+ * IPC over socketpair between parent and children.
  */
 
 #ifdef HAVE_CONFIG_H
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <signal.h>
 
 #include <atalk/server_child.h>
 #include <atalk/server_ipc.h>
 #include <atalk/logger.h>
+#include <atalk/util.h>
 
 typedef struct ipc_header {
-       u_int16_t command;
-        pid_t    child_pid;
-        uid_t     uid;
-        u_int32_t len;
-       char      *msg;
-} ipc_header;
-
-static int pipe_fd[2];
-   
-void *server_ipc_create(void)
-{
-    if (pipe(pipe_fd)) {
-        return NULL;
-    }
-    return &pipe_fd;
-}
-
-/* ----------------- */
-int server_ipc_child(void *obj _U_)
-{
-    /* close input */
-    close(pipe_fd[0]);
-    return pipe_fd[1];
-}
+       uint16_t command;
+    pid_t       child_pid;
+    uid_t    uid;
+    uint32_t len;
+       char     *msg;
+    int      afp_socket;
+    uint16_t DSI_requestID;
+} ipc_header_t;
+
+static char *ipc_cmd_str[] = { "IPC_DISCOLDSESSION",
+                               "IPC_GETSESSION"};
 
-/* ----------------- */
-int server_ipc_parent(void *obj _U_)
-{
-    return pipe_fd[0];
-}
-
-/* ----------------- */
-static int ipc_kill_token (struct ipc_header *ipc, server_child *children)
+/*
+ * Pass afp_socket to old disconnected session if one has a matching token (token = pid)
+ * @returns -1 on error, 0 if no matching session was found, 1 if session was found and socket passed
+ */
+static int ipc_kill_token(struct ipc_header *ipc, server_child *children)
 {
     pid_t pid;
 
@@ -66,22 +51,25 @@ static int ipc_kill_token (struct ipc_header *ipc, server_child *children)
     /* assume signals SA_RESTART set */
     memcpy (&pid, ipc->msg, sizeof(pid_t));
 
-    LOG(log_info, logtype_default, "child %d user %d disconnected", pid, ipc->uid);
-    server_child_kill_one(children, CHILD_DSIFORK, pid, ipc->uid);
-    return 0;
+    return server_child_transfer_session(children,
+                                         CHILD_DSIFORK,
+                                         pid,
+                                         ipc->uid,
+                                         ipc->afp_socket,
+                                         ipc->DSI_requestID);
 }
 
 /* ----------------- */
-static int ipc_get_session (struct ipc_header *ipc, server_child *children)
+static int ipc_get_session(struct ipc_header *ipc, server_child *children)
 {
     u_int32_t boottime;
     u_int32_t idlen;
     char     *clientid, *p;
 
+    
+    if (ipc->len < (sizeof(idlen) + sizeof(boottime)) )
+        return -1;
 
-    if (ipc->len < (sizeof(idlen) + sizeof(boottime)) ) {
-       return -1;
-    }
     p = ipc->msg;
     memcpy (&idlen, p, sizeof(idlen));
     idlen = ntohl (idlen);
@@ -90,17 +78,24 @@ static int ipc_get_session (struct ipc_header *ipc, server_child *children)
     memcpy (&boottime, p, sizeof(boottime));
     p += sizeof(boottime);
     
-    if (ipc->len < idlen + sizeof(idlen) + sizeof(boottime)) {
-       return -1;
-    }
-    if (NULL == (clientid = (char*) malloc(idlen)) ) {
-       return -1;
-    }
+    if (ipc->len < idlen + sizeof(idlen) + sizeof(boottime))
+        return -1;
+
+    if (NULL == (clientid = (char*) malloc(idlen)) )
+        return -1;
     memcpy (clientid, p, idlen);
   
-    server_child_kill_one_by_id (children, CHILD_DSIFORK, ipc->child_pid, ipc->uid, idlen, clientid, boottime);
-    /* FIXME byte to ascii if we want to log clientid */
-    LOG (log_debug, logtype_afpd, "ipc_get_session: len: %u, idlen %d, time %x", ipc->len, idlen, boottime); 
+    LOG (log_debug, logtype_afpd, "ipc_get_session(pid: %u, uid: %u, time %x)",
+         ipc->child_pid, ipc->uid, boottime); 
+
+    server_child_kill_one_by_id(children,
+                                CHILD_DSIFORK,
+                                ipc->child_pid,
+                                ipc->uid,
+                                idlen,
+                                clientid,
+                                boottime);
+
     return 0;
 }
 
@@ -114,16 +109,26 @@ static int ipc_get_session (struct ipc_header *ipc, server_child *children)
  * uid
  * 
 */
-int server_ipc_read(server_child *children)
+
+/*!
+ * Read a IPC message from a child
+ *
+ * @args children  (rw) pointer to our structure with all childs
+ * @args fd        (r)  IPC socket with child
+ *
+ * @returns number of bytes transfered, -1 on error, 0 on EOF
+ */
+int ipc_server_read(server_child *children, int fd)
 {
     int       ret = 0;
     struct ipc_header ipc;
     char      buf[IPC_MAXMSGSIZE], *p;
 
-    if ((ret = read(pipe_fd[0], buf, IPC_HEADERLEN)) != IPC_HEADERLEN) {
-       LOG (log_info, logtype_afpd, "Reading IPC header failed (%u of %u  bytes read)", ret, IPC_HEADERLEN);
-       return -1;
-    } 
+    if ((ret = read(fd, buf, IPC_HEADERLEN)) != IPC_HEADERLEN) {
+        LOG(log_error, logtype_afpd, "Reading IPC header failed (%i of %u bytes read): %s",
+            ret, IPC_HEADERLEN, strerror(errno));
+        return ret;
+    }
 
     p = buf;
 
@@ -139,40 +144,61 @@ int server_ipc_read(server_child *children)
     memcpy(&ipc.len, p, sizeof(ipc.len));
 
     /* This should never happen */
-    if (ipc.len > (IPC_MAXMSGSIZE - IPC_HEADERLEN))
-    {
-       LOG (log_info, logtype_afpd, "IPC message exceeds allowed size (%u)", ipc.len);
-       return -1;
+    if (ipc.len > (IPC_MAXMSGSIZE - IPC_HEADERLEN)) {
+        LOG (log_info, logtype_afpd, "IPC message exceeds allowed size (%u)", ipc.len);
+        return -1;
     }
 
     memset (buf, 0, IPC_MAXMSGSIZE);
     if ( ipc.len != 0) {
-           if ((ret = read(pipe_fd[0], buf, ipc.len)) != (int) ipc.len) {
-               LOG (log_info, logtype_afpd, "Reading IPC message failed (%u of %u  bytes read)", ret, ipc.len);
-               return -1;
+           if ((ret = read(fd, buf, ipc.len)) != (int) ipc.len) {
+            LOG(log_info, logtype_afpd, "Reading IPC message failed (%u of %u  bytes read): %s",
+                ret, ipc.len, strerror(errno));
+            return ret;
        }        
     }
     ipc.msg = buf;
-    
-    LOG (log_debug, logtype_afpd, "ipc_read: command: %u, pid: %u, len: %u", ipc.command, ipc.child_pid, ipc.len); 
 
-    switch (ipc.command)
-    {
-       case IPC_KILLTOKEN:
-               return (ipc_kill_token(&ipc, children));
-               break;
+    LOG(log_debug, logtype_afpd, "ipc_server_read(%s): pid: %u",
+        ipc_cmd_str[ipc.command], ipc.child_pid); 
+
+    int afp_socket;
+
+    switch (ipc.command) {
+
+       case IPC_DISCOLDSESSION:
+        if (readt(fd, &ipc.DSI_requestID, 2, 0, 2) != 2) {
+            LOG (log_error, logtype_afpd, "ipc_read(%s:child[%u]): couldnt read DSI id: %s",
+                 ipc_cmd_str[ipc.command], ipc.child_pid, strerror(errno));
+        }
+        if ((ipc.afp_socket = recv_fd(fd, 1)) == -1) {
+            LOG (log_error, logtype_afpd, "ipc_read(%s:child[%u]): recv_fd: %s",
+                 ipc_cmd_str[ipc.command], ipc.child_pid, strerror(errno));
+            return -1;
+        }
+               if (ipc_kill_token(&ipc, children) == 1) {
+            /* Transfered session (ie afp_socket) to old disconnected child, now kill the new one */
+            LOG(log_note, logtype_afpd, "Reconnect: killing new session child[%u] after transfer",
+                ipc.child_pid);
+            kill(ipc.child_pid, SIGKILL);
+        }        
+        break;
+
        case IPC_GETSESSION:
-               return (ipc_get_session(&ipc, children));
-               break;
+               if (ipc_get_session(&ipc, children) != 0)
+            return -1;
+        break;
+
        default:
                LOG (log_info, logtype_afpd, "ipc_read: unknown command: %d", ipc.command);
                return -1;
     }
 
+    return ret;
 }
 
 /* ----------------- */
-int server_ipc_write( u_int16_t command, int len, void *msg)
+int ipc_child_write(int fd, uint16_t command, int len, void *msg)
 {
    char block[IPC_MAXMSGSIZE], *p;
    pid_t pid;
@@ -204,7 +230,8 @@ int server_ipc_write( u_int16_t command, int len, void *msg)
 
    memcpy(p, msg, len);
 
-   LOG (log_debug, logtype_afpd, "ipc_write: command: %u, pid: %u, msglen: %u", command, pid, len); 
-   return write(pipe_fd[1], block, len+IPC_HEADERLEN );
+   LOG(log_debug, logtype_afpd, "ipc_child_write(%s)", ipc_cmd_str[command]);
+
+   return write(fd, block, len+IPC_HEADERLEN );
 }
 
index f8b610335108213f97707613b794922accd3d159..0dfe1044249a2b21b07823d37c5d2b5e5ab54938 100644 (file)
 #include <errno.h>
 #include <sys/time.h>
 #include <time.h>
+#include <sys/ioctl.h>
 
 #include <atalk/logger.h>
+#include <atalk/util.h>
 
 static char ipv4mapprefix[] = {0,0,0,0,0,0,0,0,0,0,0xff,0xff};
 
@@ -161,6 +163,101 @@ exit:
     return stored;
 }
 
+/*!
+ * non-blocking drop-in replacement for read with timeout using select
+ *
+ * @param socket          (r)  socket, if in blocking mode, pass "setnonblocking" arg as 1
+ * @param data            (rw) buffer for the read data
+ * @param lenght          (r)  how many bytes to read
+ * @param setnonblocking  (r)  when non-zero this func will enable and disable non blocking
+ *                             io mode for the socket
+ * @param timeout         (r)  number of seconds to try reading
+ *
+ * @returns number of bytes actually read or -1 on fatal error
+ */
+ssize_t writet(int socket, void *data, const size_t length, int setnonblocking, int timeout)
+{
+    size_t stored = 0;
+    ssize_t len = 0;
+    struct timeval now, end, tv;
+    fd_set rfds;
+    int ret;
+
+    if (setnonblocking) {
+        if (setnonblock(socket, 1) != 0)
+            return -1;
+    }
+
+    /* Calculate end time */
+    (void)gettimeofday(&now, NULL);
+    end = now;
+    end.tv_sec += timeout;
+
+    while (stored < length) {
+        len = write(socket, (char *) data + stored, length - stored);
+        if (len == -1) {
+            switch (errno) {
+            case EINTR:
+                continue;
+            case EAGAIN:
+                FD_ZERO(&rfds);
+                FD_SET(socket, &rfds);
+                tv.tv_usec = 0;
+                tv.tv_sec  = timeout;
+                        
+                while ((ret = select(socket + 1, &rfds, NULL, NULL, &tv)) < 1) {
+                    switch (ret) {
+                    case 0:
+                        LOG(log_warning, logtype_afpd, "select timeout %d s", timeout);
+                        goto exit;
+
+                    default: /* -1 */
+                        if (errno == EINTR) {
+                            (void)gettimeofday(&now, NULL);
+                            if (now.tv_sec >= end.tv_sec && now.tv_usec >= end.tv_usec) {
+                                LOG(log_warning, logtype_afpd, "select timeout %d s", timeout);
+                                goto exit;
+                            }
+                            if (now.tv_usec > end.tv_usec) {
+                                tv.tv_usec = 1000000 + end.tv_usec - now.tv_usec;
+                                tv.tv_sec  = end.tv_sec - now.tv_sec - 1;
+                            } else {
+                                tv.tv_usec = end.tv_usec - now.tv_usec;
+                                tv.tv_sec  = end.tv_sec - now.tv_sec;
+                            }
+                            FD_ZERO(&rfds);
+                            FD_SET(socket, &rfds);
+                            continue;
+                        }
+                        LOG(log_error, logtype_afpd, "select: %s", strerror(errno));
+                        stored = -1;
+                        goto exit;
+                    }
+                } /* while (select) */
+                continue;
+            } /* switch (errno) */
+            LOG(log_error, logtype_afpd, "read: %s", strerror(errno));
+            stored = -1;
+            goto exit;
+        } /* (len == -1) */
+        else if (len > 0)
+            stored += len;
+        else
+            break;
+    } /* while (stored < length) */
+
+exit:
+    if (setnonblocking) {
+        if (setnonblock(socket, 0) != 0)
+            return -1;
+    }
+
+    if (len == -1 && stored == 0)
+        /* last read or select got an error and we haven't got yet anything => return -1*/
+        return -1;
+    return stored;
+}
+
 /*!
  * @brief convert an IPv4 or IPv6 address to a static string using inet_ntop
  *
@@ -233,7 +330,7 @@ unsigned int getip_port(const struct sockaddr  *sa)
  * @param  ai        (rw) pointer to an struct sockaddr
  * @parma  mask      (r) number of maskbits
  */
-void apply_ip_mask(struct sockaddr *sa, uint32_t mask)
+void apply_ip_mask(struct sockaddr *sa, int mask)
 {
 
     switch (sa->sa_family) {
@@ -300,3 +397,234 @@ int compare_ip(const struct sockaddr *sa1, const struct sockaddr *sa2)
 
     return ret;
 }
+
+#define POLL_FD_SET_STARTSIZE 512
+#define POLL_FD_SET_INCREASE  128
+/*!
+ * Add a fd to a dynamic pollfd array that is allocated and grown as needed
+ *
+ * This uses an additional array of struct polldata which stores type information
+ * (enum fdtype) and a pointer to anciliary user data.
+ *
+ * 1. Allocate the arrays with an intial size of [POLL_FD_SET_STARTSIZE] if
+ *    *fdsetp is NULL.
+ * 2. Grow array as needed
+ * 3. Fill in both array elements and increase count of used elements
+ * 
+ * @param fdsetp      (rw) pointer to callers pointer to the pollfd array
+ * @param polldatap   (rw) pointer to callers pointer to the polldata array
+ * @param fdset_usedp (rw) pointer to an int with the number of used elements
+ * @param fdset_sizep (rw) pointer to an int which stores the array sizes
+ * @param fd          (r)  file descriptor to add to the arrays
+ * @param fdtype      (r)  type of fd, currently IPC_FD or LISTEN_FD
+ * @param data        (rw) pointer to data the caller want to associate with an fd
+ */
+void fdset_add_fd(struct pollfd **fdsetp,
+                  struct polldata **polldatap,
+                  int *fdset_usedp,
+                  int *fdset_sizep,
+                  int fd,
+                  enum fdtype fdtype,
+                  void *data)
+{
+    struct pollfd *fdset = *fdsetp;
+    struct polldata *polldata = *polldatap;
+    int fdset_size = *fdset_sizep;
+
+    LOG(log_debug, logtype_default, "fdset_add_fd: adding fd %i in slot %i", fd, *fdset_usedp);
+
+    if (fdset == NULL) { /* 1 */
+        /* Initialize with space for 512 fds */
+        fdset = calloc(POLL_FD_SET_STARTSIZE, sizeof(struct pollfd));
+        if (! fdset)
+            exit(EXITERR_SYS);
+
+        polldata = calloc(POLL_FD_SET_STARTSIZE, sizeof(struct polldata));
+        if (! polldata)
+            exit(EXITERR_SYS);
+
+        fdset_size = 512;
+        *fdset_sizep = fdset_size;
+        *fdsetp = fdset;
+        *polldatap = polldata;
+    }
+
+    if (*fdset_usedp >= fdset_size) { /* 2 */
+        fdset = realloc(fdset, sizeof(struct pollfd) * (fdset_size + POLL_FD_SET_INCREASE));
+        if (fdset == NULL)
+            exit(EXITERR_SYS);
+
+        polldata = realloc(polldata, sizeof(struct polldata) * (fdset_size + POLL_FD_SET_INCREASE));
+        if (polldata == NULL)
+            exit(EXITERR_SYS);
+
+        fdset_size += POLL_FD_SET_INCREASE;
+        *fdset_sizep = fdset_size;
+        *fdsetp = fdset;
+        *polldatap = polldata;
+    }
+
+    /* 3 */
+    fdset[*fdset_usedp].fd = fd;
+    fdset[*fdset_usedp].events = POLLIN;
+    polldata[*fdset_usedp].fdtype = fdtype;
+    polldata[*fdset_usedp].data = data;
+    (*fdset_usedp)++;
+}
+
+/*!
+ * Remove a fd from our pollfd array
+ *
+ * 1. Search fd
+ * 2. If we remove the last array elemnt, just decrease count
+ * 3. If found move all following elements down by one
+ * 4. Decrease count of used elements in array
+ *
+ * This currently doesn't shrink the allocated storage of the array.
+ *
+ * @param fdsetp      (rw) pointer to callers pointer to the pollfd array
+ * @param polldatap   (rw) pointer to callers pointer to the polldata array
+ * @param fdset_usedp (rw) pointer to an int with the number of used elements
+ * @param fdset_sizep (rw) pointer to an int which stores the array sizes
+ * @param fd          (r)  file descriptor to remove from the arrays
+ */
+void fdset_del_fd(struct pollfd **fdsetp,
+                  struct polldata **polldatap,
+                  int *fdset_usedp,
+                  int *fdset_sizep _U_,
+                  int fd)
+{
+    struct pollfd *fdset = *fdsetp;
+    struct polldata *polldata = *polldatap;
+
+    for (int i = 0; i < *fdset_usedp; i++) {
+        if (fdset[i].fd == fd) { /* 1 */
+            if (i < (*fdset_usedp - 1)) { /* 2 */
+                memmove(&fdset[i], &fdset[i+1], (*fdset_usedp - 1) * sizeof(struct pollfd)); /* 3 */
+                memmove(&polldata[i], &polldata[i+1], (*fdset_usedp - 1) * sizeof(struct polldata)); /* 3 */
+            }
+            (*fdset_usedp)--;
+            break;
+        }
+    }
+}
+
+/*
+ * Receive a fd on a suitable socket
+ * @args fd          (r) PF_UNIX socket to receive on
+ * @args nonblocking (r) 0: fd is in blocking mode - 1: fd is nonblocking, poll for 1 sec
+ * @returns fd on success, -1 on error
+ */
+int recv_fd(int fd, int nonblocking)
+{
+    int ret;
+    struct msghdr msgh;
+    struct iovec iov[1];
+    struct cmsghdr *cmsgp = NULL;
+    char buf[CMSG_SPACE(sizeof(int))];
+    char dbuf[80];
+    struct pollfd pollfds[1];
+
+    pollfds[0].fd = fd;
+    pollfds[0].events = POLLIN;
+
+    memset(&msgh,0,sizeof(msgh));
+    memset(buf,0,sizeof(buf));
+
+    msgh.msg_name = NULL;
+    msgh.msg_namelen = 0;
+
+    msgh.msg_iov = iov;
+    msgh.msg_iovlen = 1;
+
+    iov[0].iov_base = dbuf;
+    iov[0].iov_len = sizeof(dbuf);
+
+    msgh.msg_control = buf;
+    msgh.msg_controllen = sizeof(buf);
+
+    if (nonblocking) {
+        do {
+            ret = poll(pollfds, 1, 2000); /* poll 2 seconds, evtl. multipe times (EINTR) */
+        } while ( ret == -1 && errno == EINTR );
+        if (ret != 1)
+            return -1;
+        ret = recvmsg(fd, &msgh, 0);
+    } else {
+        do  {
+            ret = recvmsg(fd, &msgh, 0);
+        } while ( ret == -1 && errno == EINTR );
+    }
+
+    if ( ret == -1 ) {
+        return -1;
+    }
+
+    for ( cmsgp = CMSG_FIRSTHDR(&msgh); cmsgp != NULL; cmsgp = CMSG_NXTHDR(&msgh,cmsgp) ) {
+        if ( cmsgp->cmsg_level == SOL_SOCKET && cmsgp->cmsg_type == SCM_RIGHTS ) {
+            return *(int *) CMSG_DATA(cmsgp);
+        }
+    }
+
+    if ( ret == sizeof (int) )
+        errno = *(int *)dbuf; /* Rcvd errno */
+    else
+        errno = ENOENT;    /* Default errno */
+
+    return -1;
+}
+
+/*
+ * Send a fd across a suitable socket
+ */
+int send_fd(int socket, int fd)
+{
+    int ret;
+    struct msghdr msgh;
+    struct iovec iov[1];
+    struct cmsghdr *cmsgp = NULL;
+    char *buf;
+    size_t size;
+    int er=0;
+
+    size = CMSG_SPACE(sizeof fd);
+    buf = malloc(size);
+    if (!buf) {
+        LOG(log_error, logtype_cnid, "error in sendmsg: %s", strerror(errno));
+        return -1;
+    }
+
+    memset(&msgh,0,sizeof (msgh));
+    memset(buf,0, size);
+
+    msgh.msg_name = NULL;
+    msgh.msg_namelen = 0;
+
+    msgh.msg_iov = iov;
+    msgh.msg_iovlen = 1;
+
+    iov[0].iov_base = &er;
+    iov[0].iov_len = sizeof(er);
+
+    msgh.msg_control = buf;
+    msgh.msg_controllen = size;
+
+    cmsgp = CMSG_FIRSTHDR(&msgh);
+    cmsgp->cmsg_level = SOL_SOCKET;
+    cmsgp->cmsg_type = SCM_RIGHTS;
+    cmsgp->cmsg_len = CMSG_LEN(sizeof(fd));
+
+    *((int *)CMSG_DATA(cmsgp)) = fd;
+    msgh.msg_controllen = cmsgp->cmsg_len;
+
+    do  {
+        ret = sendmsg(socket,&msgh, 0);
+    } while ( ret == -1 && errno == EINTR );
+    if (ret == -1) {
+        LOG(log_error, logtype_cnid, "error in sendmsg: %s", strerror(errno));
+        free(buf);
+        return -1;
+    }
+    free(buf);
+    return 0;
+}