]> arthur.barton.de Git - netatalk.git/commitdiff
Merge master
authorFrank Lahm <franklahm@googlemail.com>
Fri, 18 Feb 2011 11:44:48 +0000 (12:44 +0100)
committerFrank Lahm <franklahm@googlemail.com>
Fri, 18 Feb 2011 11:44:48 +0000 (12:44 +0100)
38 files changed:
NEWS
configure.in
etc/afpd/acls.c
etc/afpd/afp_config.c
etc/afpd/afp_config.h
etc/afpd/afp_dsi.c
etc/afpd/afp_options.c
etc/afpd/auth.c
etc/afpd/globals.h
etc/afpd/main.c
etc/afpd/uam.c
etc/afpd/volume.c
etc/cnid_dbd/cnid_metad.c
etc/cnid_dbd/comm.c
etc/cnid_dbd/dbif.c
etc/cnid_dbd/main.c
include/atalk/afp.h
include/atalk/cnid_private.h
include/atalk/dsi.h
include/atalk/server_child.h
include/atalk/server_ipc.h
include/atalk/util.h
include/atalk/uuid.h
include/atalk/volume.h
libatalk/acl/uuid.c
libatalk/adouble/ad_attr.c
libatalk/asp/asp_getsess.c
libatalk/dsi/dsi_attn.c
libatalk/dsi/dsi_close.c
libatalk/dsi/dsi_getsess.c
libatalk/dsi/dsi_opensess.c
libatalk/dsi/dsi_read.c
libatalk/dsi/dsi_stream.c
libatalk/dsi/dsi_tcp.c
libatalk/dsi/dsi_tickle.c
libatalk/util/server_child.c
libatalk/util/server_ipc.c
libatalk/util/socket.c

diff --git a/NEWS b/NEWS
index ec4972322661e8e49bc1154dd6d387bbe0a3f186..f40686f5fdb3814126437f72b66fb6e4eab2818b 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,9 @@
+Changes in 2.2beta2
+====================
+
+* NEW: afpd: AFP 3.3
+* UPD: afpd: AFP 3.x can't be disabled
+
 Changes in 2.2beta1
 ====================
 
@@ -64,9 +70,12 @@ Changes in 2.1.6
 ================
 
 * FIX: afpd: Fix for LDAP user cache corruption
+* FIX: afpd: Fix for not shown ACLs for when filesyem uid or gid
+       couldn't be resolved because (eg deleted users/groups)
 * FIX: gentoo: cannot set $CNID_CONFIG
 * FIX: ubuntu: servername was empty
 * FIX: Solaris: configure script failed to enable DDP module
+* FIX: AppleDouble buffer overrun by extremely long filename
 * UPD: afpd: return version info with machine type in DSIGetStatus
 
 Changes in 2.1.5
index 81b02430e6f877ccf9fa4e421f9cac4554be0cb7..3ef40da5d3b7b35159c16e89db1a255ccd62da1c 100644 (file)
@@ -236,39 +236,7 @@ AC_ARG_ENABLE(debugging,
        ]
 )
 
-
-afp3=no
-afp3set=no
-AC_MSG_CHECKING([whether AFP 3.x calls should be enabled])
-AC_ARG_ENABLE(afp3,
-       [  --disable-afp3          disable AFP 3.x calls],
-       [
-           if test x"$enableval" != x"no"; then
-               afp3set=yes
-               afp3=yes
-               AC_MSG_RESULT([yes])
-           else
-               AC_MSG_RESULT([no])
-           fi
-       ],[
-           AC_MSG_RESULT([yes])
-           afp3=yes
-       ]
-)
-
-if test x"$afp3" = x"yes"; then
-        AC_SYS_LARGEFILE([
-               AC_DEFINE(AFP3x, 1, [Define to enable AFP 3.x support])
-       ],[
-               if test x"$afp3set" = x"yes"; then
-                       AC_MSG_ERROR([AFP 3.x support requires Large File Support.])
-               else
-                       AC_MSG_WARN([AFP 3.x support requires Large File Support. AFP3.x support disabled])
-                       afp3=no
-               fi
-       ])
-fi
-
+AC_SYS_LARGEFILE([], AC_MSG_ERROR([AFP 3.x support requires Large File Support.]))
 AC_CHECK_ICONV
 
 dnl ----------- A NOTE ABOUT DROPKLUDGE
index a82d326fe2ff9211b58151617e037272ff45e814..206f2d9f2f774f58c46b4c90e5067ebfd3da7abb 100644 (file)
@@ -1293,11 +1293,14 @@ int afp_getacl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
     /* Shall we return owner UUID ? */
     if (bitmap & kFileSec_UUID) {
         LOG(log_debug, logtype_afpd, "afp_getacl: client requested files owner user UUID");
-        if (NULL == (pw = getpwuid(s_path->st.st_uid)))
-            return AFPERR_MISC;
-        LOG(log_debug, logtype_afpd, "afp_getacl: got uid: %d, name: %s", s_path->st.st_uid, pw->pw_name);
-        if ((ret = getuuidfromname(pw->pw_name, UUID_USER, rbuf)) != 0)
-            return AFPERR_MISC;
+        if (NULL == (pw = getpwuid(s_path->st.st_uid))) {
+            LOG(log_debug, logtype_afpd, "afp_getacl: local uid: %u", s_path->st.st_uid);
+            localuuid_from_id(rbuf, UUID_USER, s_path->st.st_uid);
+        } else {
+            LOG(log_debug, logtype_afpd, "afp_getacl: got uid: %d, name: %s", s_path->st.st_uid, pw->pw_name);
+            if ((ret = getuuidfromname(pw->pw_name, UUID_USER, rbuf)) != 0)
+                return AFPERR_MISC;
+        }
         rbuf += UUID_BINSIZE;
         *rbuflen += UUID_BINSIZE;
     }
@@ -1305,11 +1308,14 @@ int afp_getacl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
     /* Shall we return group UUID ? */
     if (bitmap & kFileSec_GRPUUID) {
         LOG(log_debug, logtype_afpd, "afp_getacl: client requested files owner group UUID");
-        if (NULL == (gr = getgrgid(s_path->st.st_gid)))
-            return AFPERR_MISC;
-        LOG(log_debug, logtype_afpd, "afp_getacl: got gid: %d, name: %s", s_path->st.st_gid, gr->gr_name);
-        if ((ret = getuuidfromname(gr->gr_name, UUID_GROUP, rbuf)) != 0)
-            return AFPERR_MISC;
+        if (NULL == (gr = getgrgid(s_path->st.st_gid))) {
+            LOG(log_debug, logtype_afpd, "afp_getacl: local gid: %u", s_path->st.st_gid);
+            localuuid_from_id(rbuf, UUID_GROUP, s_path->st.st_gid);
+        } else {
+            LOG(log_debug, logtype_afpd, "afp_getacl: got gid: %d, name: %s", s_path->st.st_gid, gr->gr_name);
+            if ((ret = getuuidfromname(gr->gr_name, UUID_GROUP, rbuf)) != 0)
+                return AFPERR_MISC;
+        }
         rbuf += UUID_BINSIZE;
         *rbuflen += UUID_BINSIZE;
     }
index 648eed71589b27bf77f0eccf7d3057db42ed7e3d..52592564cb84b1d9769f68696a5f015a32a581c8 100644 (file)
@@ -214,25 +214,30 @@ 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);
+        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 5db8217846abdf85f48e892b4c7d3caf8cfede28..c89a9082e473b2e5d18b5ef09513118d58224437 100644 (file)
@@ -12,7 +12,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 37a817a8fc30bd1543cf4ad39c7aaecfe9b16e4f..8b15abf4dd014f767e16f23fbab6e0f9f0dfd3b3 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)
-
 /* 
  * We generally pass this from afp_over_dsi to all afp_* funcs, so it should already be
  * available everywhere. Unfortunately some funcs (eg acltoownermode) need acces to it
  */
 AFPObj *AFPobj = NULL;
 
-static struct {
-    AFPObj *obj;
-    unsigned char flags;
-    int tickle;
-} child;
+typedef struct {
+    uint16_t DSIreqID;
+    uint8_t  AFPcommand;
+    uint32_t result;
+} rc_elem_t;
 
+/*
+ * AFP replay cache:
+ * - fix sized array
+ * - indexed just by taking DSIreqID mod REPLAYCACHE_SIZE
+ */
+rc_elem_t replaycache[REPLAYCACHE_SIZE];
 
 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
@@ -109,8 +113,8 @@ static volatile int in_handler;
     */
     in_handler = 1;
 
-    dsi_attention(child.obj->handle, AFPATTN_SHUTDOWN);
-    afp_dsi_close(child.obj);
+    dsi_attention(AFPobj->handle, AFPATTN_SHUTDOWN);
+    afp_dsi_close(AFPobj);
     if (sig) /* if no signal, assume dieing because logins are disabled &
                 don't log it (maintenance mode)*/
         LOG(log_info, logtype_afpd, "Connection terminated");
@@ -122,11 +126,57 @@ static volatile int in_handler;
     }
 }
 
+static void afp_dsi_transfer_session(int sig _U_)
+{
+    uint16_t dsiID;
+    int socket;
+    DSI *dsi = (DSI *)AFPobj->handle;
+
+    LOG(log_note, logtype_afpd, "afp_dsi_transfer_session: got SIGURG, trying to receive session");
+
+    if (readt(AFPobj->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(AFPobj);
+        exit(EXITERR_SYS);
+    }
+
+    if ((socket = recv_fd(AFPobj->ipc_fd, 1)) == -1) {
+        LOG(log_error, logtype_afpd, "afp_dsi_transfer_session: couldn't receive session fd, goodbye");
+        afp_dsi_close(AFPobj);
+        exit(EXITERR_SYS);
+    }
+
+    if (dsi->socket != -1)
+        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;
+
+    /*
+     * The session transfer happens in the middle of FPDisconnect old session, thus we
+     * have to send the reply now.
+     */
+    if (!dsi_cmdreply(dsi, AFP_OK)) {
+        LOG(log_error, logtype_afpd, "dsi_cmdreply: %s", strerror(errno) );
+        afp_dsi_close(AFPobj);
+        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)
 {
-    child.flags |= CHILD_SLEEPING;
-    dsi_sleep(child.obj->handle, 1);
+    dsi_sleep(AFPobj->handle, 1);
 }
 
 /* ------------------- */
@@ -134,13 +184,13 @@ static void afp_dsi_timedown(int sig _U_)
 {
     struct sigaction   sv;
     struct itimerval   it;
-
-    child.flags |= CHILD_DIE;
+    DSI                 *dsi = (DSI *)AFPobj->handle;
+    dsi->flags |= DSI_DIE;
     /* shutdown and don't reconnect. server going down in 5 minutes. */
     setmessage("The server is going down for maintenance.");
-    if (dsi_attention(child.obj->handle, AFPATTN_SHUTDOWN | AFPATTN_NORECONNECT |
+    if (dsi_attention(AFPobj->handle, AFPATTN_SHUTDOWN | AFPATTN_NORECONNECT |
                   AFPATTN_MESG | AFPATTN_TIME(5)) < 0) {
-        DSI *dsi = (DSI *) child.obj->handle;
+        DSI *dsi = (DSI *)AFPobj->handle;
         dsi->down_request = 1;
     }                  
 
@@ -195,48 +245,60 @@ static void afp_dsi_debug(int sig _U_)
 }
 
 /* ---------------------- */
-#ifdef SERVERTEXT
 static void afp_dsi_getmesg (int sig _U_)
 {
-    DSI *dsi = (DSI *) child.obj->handle;
+    DSI *dsi = (DSI *)AFPobj->handle;
 
     dsi->msg_request = 1;
-    if (dsi_attention(child.obj->handle, AFPATTN_MESG | AFPATTN_TIME(5)) < 0)
+    if (dsi_attention(AFPobj->handle, AFPATTN_MESG | AFPATTN_TIME(5)) < 0)
         dsi->msg_request = 2;
 }
-#endif /* SERVERTEXT */
 
 static void alarm_handler(int sig _U_)
 {
     int err;
-    DSI *dsi = (DSI *) child.obj->handle;
+    DSI *dsi = (DSI *)AFPobj->handle;
 
-    /* we have to restart the timer because some libraries 
-     * may use alarm() */
+    /* 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;
+    /* we got some traffic from the client since the previous timer tick. */
+    if ((dsi->flags & DSI_DATA)) {
+        dsi->flags &= ~DSI_DATA;
+        dsi->flags &= ~DSI_DISCONNECTED;
+       return;
     }
 
-    /* if we're in the midst of processing something,
-       don't die. */
-    if ((child.flags & CHILD_SLEEPING) && child.tickle++ < child.obj->options.sleep) {
+    dsi->tickle++;
+
+    if (dsi->flags & DSI_SLEEPING) {
+        if (dsi->tickle < AFPobj->options.sleep) {
+            LOG(log_error, logtype_afpd, "afp_alarm: sleep time ended");
+            afp_dsi_die(EXITERR_CLNT);
+        }
         return;
     } 
-        
-    if ((child.flags & CHILD_RUNNING) || (child.tickle++ < child.obj->options.timeout)) {
-        if (!(err = pollvoltime(child.obj)))
-            err = dsi_tickle(child.obj->handle);
-        if (err <= 0) 
+
+    if (dsi->flags & DSI_DISCONNECTED) {
+        if (dsi->tickle > AFPobj->options.disconnected) {
+             LOG(log_error, logtype_afpd, "afp_alarm: no reconnect within 10 minutes, goodbye");
             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);
+        }
+        return;
+    }
+
+    /* if we're in the midst of processing something, don't die. */        
+    if ( !(dsi->flags & DSI_RUNNING) && (dsi->tickle > AFPobj->options.timeout)) {
+        LOG(log_error, logtype_afpd, "afp_alarm: child timed out, entering disconnected state");
+        dsi->flags |= DSI_DISCONNECTED;
+        return;
+    }
+
+    if ((err = pollvoltime(AFPobj)) == 0)
+        err = dsi_tickle(AFPobj->handle);
+    if (err <= 0) {
+        LOG(log_error, logtype_afpd, "afp_alarm: connection problem, entering disconnected state");
+        dsi->flags |= DSI_DISCONNECTED;
     }
 }
 
@@ -252,14 +314,14 @@ static void pending_request(DSI *dsi)
     if (dsi->msg_request) {
         if (dsi->msg_request == 2) {
             /* didn't send it in signal handler */
-            dsi_attention(child.obj->handle, AFPATTN_MESG | AFPATTN_TIME(5));
+            dsi_attention(AFPobj->handle, AFPATTN_MESG | AFPATTN_TIME(5));
         }
         dsi->msg_request = 0;
-        readmessage(child.obj);
+        readmessage(AFPobj);
     }
     if (dsi->down_request) {
         dsi->down_request = 0;
-        dsi_attention(child.obj->handle, AFPATTN_SHUTDOWN | AFPATTN_NORECONNECT |
+        dsi_attention(AFPobj->handle, AFPATTN_SHUTDOWN | AFPATTN_NORECONNECT |
                   AFPATTN_MESG | AFPATTN_TIME(5));
     }
 }
@@ -270,6 +332,7 @@ static void pending_request(DSI *dsi)
 void afp_over_dsi(AFPObj *obj)
 {
     DSI *dsi = (DSI *) obj->handle;
+    int rc_idx;
     u_int32_t err, cmd;
     u_int8_t function;
     struct sigaction action;
@@ -278,10 +341,8 @@ void afp_over_dsi(AFPObj *obj)
     obj->exit = afp_dsi_die;
     obj->reply = (int (*)()) dsi_cmdreply;
     obj->attention = (int (*)(void *, AFPUserBytes)) dsi_attention;
-
     obj->sleep = afp_dsi_sleep;
-    child.obj = obj;
-    child.tickle = child.flags = 0;
+    dsi->tickle = 0;
 
     memset(&action, 0, sizeof(action));
 
@@ -292,15 +353,27 @@ void afp_over_dsi(AFPObj *obj)
     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( SIGHUP, &action, NULL ) < 0 ) {
         LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
         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);
+    sigaddset(&action.sa_mask, SIGUSR2);
+    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 );
@@ -308,16 +381,13 @@ void afp_over_dsi(AFPObj *obj)
     sigaddset(&action.sa_mask, SIGHUP);
     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( SIGTERM, &action, NULL ) < 0 ) {
         LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
         afp_dsi_die(EXITERR_SYS);
     }
 
-#ifdef SERVERTEXT
     /* Added for server message support */
     action.sa_handler = afp_dsi_getmesg;
     sigemptyset( &action.sa_mask );
@@ -331,7 +401,6 @@ void afp_over_dsi(AFPObj *obj)
         LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
         afp_dsi_die(EXITERR_SYS);
     }
-#endif /* SERVERTEXT */
 
     /*  SIGUSR1 - set down in 5 minutes  */
     action.sa_handler = afp_dsi_timedown;
@@ -340,9 +409,7 @@ void afp_over_dsi(AFPObj *obj)
     sigaddset(&action.sa_mask, SIGHUP);
     sigaddset(&action.sa_mask, SIGTERM);
     sigaddset(&action.sa_mask, SIGINT);
-#ifdef SERVERTEXT
     sigaddset(&action.sa_mask, SIGUSR2);
-#endif    
     action.sa_flags = SA_RESTART;
     if ( sigaction( SIGUSR1, &action, NULL) < 0 ) {
         LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
@@ -366,10 +433,8 @@ void afp_over_dsi(AFPObj *obj)
     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;
+    action.sa_flags = SA_RESTART; /* dont restart, otherwi */
     if ((sigaction(SIGALRM, &action, NULL) < 0) ||
             (setitimer(ITIMER_REAL, &dsi->timer, NULL) < 0)) {
         afp_dsi_die(EXITERR_SYS);
@@ -380,14 +445,22 @@ void afp_over_dsi(AFPObj *obj)
         afp_dsi_die(EXITERR_SYS);
 
     /* get stuck here until the end */
-    while ((cmd = dsi_receive(dsi))) {
-        child.tickle = 0;
-        child.flags &= ~CHILD_SLEEPING;
+    while (1) {
+        cmd = dsi_receive(dsi);
+        if (cmd == 0) {
+            /* Some error on the client connection, enter disconnected state */
+            dsi->flags |= DSI_DISCONNECTED;
+            pause(); /* gets interrupted by SIGALARM or SIGURG tickle */
+            continue; /* continue receiving until disconnect timer expires
+                       * or a primary reconnect succeeds  */
+        }
+
+        dsi->tickle = 0;
         dsi_sleep(dsi, 0); /* wake up */
 
         if (reload_request) {
             reload_request = 0;
-            load_volumes(child.obj);
+            load_volumes(AFPobj);
             dircache_dump();
             log_dircache_stat();
         }
@@ -413,17 +486,17 @@ void afp_over_dsi(AFPObj *obj)
 
         if (cmd == DSIFUNC_TICKLE) {
             /* timer is not every 30 seconds anymore, so we don't get killed on the client side. */
-            if ((child.flags & CHILD_DIE))
+            if ((dsi->flags & DSI_DIE))
                 dsi_tickle(dsi);
             pending_request(dsi);
             continue;
         } 
 
-        child.flags |= CHILD_DATA;
+        dsi->flags |= DSI_DATA;
         switch(cmd) {
         case DSIFUNC_CLOSE:
             afp_dsi_close(obj);
-            LOG(log_info, logtype_afpd, "done");
+            LOG(log_note, logtype_afpd, "done");
             return;
             break;
 
@@ -439,44 +512,61 @@ void afp_over_dsi(AFPObj *obj)
 
             function = (u_char) dsi->commands[0];
 
-            /* send off an afp command. in a couple cases, we take advantage
-             * of the fact that we're a stream-based protocol. */
-            if (afp_switch[function]) {
-                dsi->datalen = DSI_DATASIZ;
-                child.flags |= CHILD_RUNNING;
+            /* AFP replay cache */
+            rc_idx = REPLAYCACHE_SIZE % dsi->clientID;
+            LOG(log_debug, logtype_afpd, "DSI request ID: %u", dsi->clientID);
 
-                LOG(log_debug, logtype_afpd, "<== Start AFP command: %s", AfpNum2name(function));
+            if (replaycache[rc_idx].DSIreqID == dsi->clientID
+                && replaycache[rc_idx].AFPcommand == function) {
+                LOG(log_debug, logtype_afpd, "AFP Replay Cache match: id: %u / cmd: %s",
+                    dsi->clientID, AfpNum2name(function));
+                err = replaycache[rc_idx].result;
+            /* AFP replay cache end */
+            } else {
+                /* send off an afp command. in a couple cases, we take advantage
+                 * of the fact that we're a stream-based protocol. */
+                if (afp_switch[function]) {
+                    dsi->datalen = DSI_DATASIZ;
+                    dsi->flags |= DSI_RUNNING;
 
-                err = (*afp_switch[function])(obj,
-                                              (char *)&dsi->commands, dsi->cmdlen,
-                                              (char *)&dsi->data, &dsi->datalen);
+                    LOG(log_debug, logtype_afpd, "<== Start AFP command: %s", AfpNum2name(function));
 
-                LOG(log_debug, logtype_afpd, "==> Finished AFP command: %s -> %s",
-                    AfpNum2name(function), AfpErr2name(err));
+                    err = (*afp_switch[function])(obj,
+                                                  (char *)&dsi->commands, dsi->cmdlen,
+                                                  (char *)&dsi->data, &dsi->datalen);
 
-                dir_free_invalid_q();
+                    LOG(log_debug, logtype_afpd, "==> Finished AFP command: %s -> %s",
+                        AfpNum2name(function), AfpErr2name(err));
+
+                    dir_free_invalid_q();
 
 #ifdef FORCE_UIDGID
-               /* bring everything back to old euid, egid */
-                if (obj->force_uid)
-                   restore_uidgid ( &obj->uidgid );
+                    /* bring everything back to old euid, egid */
+                    if (obj->force_uid)
+                        restore_uidgid ( &obj->uidgid );
 #endif /* FORCE_UIDGID */
-                child.flags &= ~CHILD_RUNNING;
-            } else {
-                LOG(log_error, logtype_afpd, "bad function %X", function);
-                dsi->datalen = 0;
-                err = AFPERR_NOOP;
+                    dsi->flags &= ~DSI_RUNNING;
+
+                    /* Add result to the AFP replay cache */
+                    replaycache[rc_idx].DSIreqID = dsi->clientID;
+                    replaycache[rc_idx].AFPcommand = function;
+                    replaycache[rc_idx].result = err;
+                } else {
+                    LOG(log_error, logtype_afpd, "bad function %X", function);
+                    dsi->datalen = 0;
+                    err = AFPERR_NOOP;
+                }
             }
 
             /* single shot toggle that gets set by dsi_readinit. */
-            if (dsi->noreply) {
-                dsi->noreply = 0;
+            if (dsi->flags & DSI_NOREPLY) {
+                dsi->flags &= ~DSI_NOREPLY;
                 break;
             }
 
             if (!dsi_cmdreply(dsi, err)) {
                 LOG(log_error, logtype_afpd, "dsi_cmdreply(%d): %s", dsi->socket, strerror(errno) );
-                afp_dsi_die(EXITERR_CLNT);
+                dsi->flags |= DSI_DISCONNECTED;
             }
             break;
 
@@ -484,7 +574,7 @@ void afp_over_dsi(AFPObj *obj)
             function = (u_char) dsi->commands[0];
             if ( afp_switch[ function ] != NULL ) {
                 dsi->datalen = DSI_DATASIZ;
-                child.flags |= CHILD_RUNNING;
+                dsi->flags |= DSI_RUNNING;
 
                 LOG(log_debug, logtype_afpd, "<== Start AFP command: %s", AfpNum2name(function));
 
@@ -495,7 +585,7 @@ void afp_over_dsi(AFPObj *obj)
                 LOG(log_debug, logtype_afpd, "==> Finished AFP command: %s -> %s",
                     AfpNum2name(function), AfpErr2name(err));
 
-                child.flags &= ~CHILD_RUNNING;
+                dsi->flags &= ~DSI_RUNNING;
 #ifdef FORCE_UIDGID
                /* bring everything back to old euid, egid */
                if (obj->force_uid)
@@ -509,7 +599,7 @@ void afp_over_dsi(AFPObj *obj)
 
             if (!dsi_wrtreply(dsi, err)) {
                 LOG(log_error, logtype_afpd, "dsi_wrtreply: %s", strerror(errno) );
-                afp_dsi_die(EXITERR_CLNT);
+                dsi->flags |= DSI_DISCONNECTED;
             }
             break;
 
index c966e464e8ccd2fbe0a716f619e3c76da9e6295d..ef88b1e638a9d7237d4664528fa602a01878f866 100644 (file)
@@ -159,8 +159,9 @@ void afp_options_init(struct afp_options *options)
     options->transports = AFPTRANS_TCP; /*  TCP only */
     options->passwdfile = _PATH_AFPDPWFILE;
     options->tickleval = 30;
-    options->timeout = 4;
+    options->timeout = 4;       /* 4 tickles = 2 minutes */
     options->sleep = 10* 120; /* 10 h in 30 seconds tick */
+    options->disconnected = 20; /* 20 * 30 s (default tickleval) = 10 minutes */
     options->server_notif = 1;
     options->authprintdir = NULL;
     options->signatureopt = "auto";
@@ -482,13 +483,7 @@ static void show_version( void )
 
        puts( "afpd has been compiled with support for these features:\n" );
 
-       printf( "        AFP3.x support:\t" );
-#ifdef AFP3x
-       puts( "Yes" );
-#else
-       puts( "No" );
-#endif
-
+       printf( "        AFP3.x support:\tYes\n" );
         printf( "        TCP/IP Support:\t" );
         puts( "Yes" );
 
index 7d50476481fe429d1d56342c537d4053d5a9219a..6d7008880281aa596e4d321aa8f6224d8f0a5a12 100644 (file)
@@ -77,11 +77,10 @@ static struct afp_versions  afp_versions[] = {
     { "AFPVersion 2.1", 21 },
 #endif /* ! NO_DDP */
     { "AFP2.2", 22 },
-#ifdef AFP3x
     { "AFPX03", 30 },
     { "AFP3.1", 31 },
-    { "AFP3.2", 32 }
-#endif /* AFP3x */
+    { "AFP3.2", 32 },
+    { "AFP3.3", 33 }
 };
 
 static struct uam_mod uam_modules = {NULL, NULL, &uam_modules, &uam_modules};
@@ -544,7 +543,7 @@ int afp_getsession(
             token = obj->sinfo.sessiontoken;
         }
         break;
-    case 3: /* Jaguar */
+    case 3:
     case 4:
         if (ibuflen >= 8 ) {
             p = ibuf;
@@ -557,7 +556,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;
         }
@@ -587,10 +586,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;
@@ -631,11 +630,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 ed91c522d59ea10a5ff6224e504556a6d8e89fef..ef77470a99b4939476cec125e7ece7e1b1d56235 100644 (file)
@@ -56,6 +56,8 @@ struct afp_volume_name {
 
 struct afp_options {
     int connections, transports, tickleval, timeout, server_notif, flags, dircachesize;
+    int sleep;                  /* Maximum time allowed to sleep (in tickles) */
+    int disconnected;           /* Maximum time in disconnected state (in tickles) */
     unsigned char passwdbits, passwdminlen, loginmaxfail;
     u_int32_t server_quantum;
     char hostname[MAXHOSTNAMELEN + 1], *server, *ipaddr, *port, *configfile;
@@ -77,7 +79,6 @@ struct afp_options {
     charset_t maccharset, unixcharset; 
     mode_t umask;
     mode_t save_mask;
-    int    sleep;
 #ifdef ADMIN_GRP
     gid_t admingid;
 #endif /* ADMIN_GRP */
@@ -106,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 ff1f2a54dbc0965e6875d065e57e6fb04b7b4e54..2270db48daf03a0f862f45106ef1027c11a572b0 100644 (file)
@@ -15,7 +15,9 @@
 #include <sys/uio.h>
 #include <sys/time.h>
 #include <sys/socket.h>
+#include <sys/poll.h>
 #include <errno.h>
+#include <sys/wait.h>
 
 #include <atalk/logger.h>
 #include <atalk/adouble.h>
@@ -58,10 +60,15 @@ 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)
 {
@@ -70,30 +77,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)
 {
@@ -102,7 +120,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 :
@@ -111,7 +131,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 :
@@ -133,7 +154,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)
@@ -174,6 +222,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. 
@@ -181,11 +230,10 @@ 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));    
-#ifdef AFP3x
     /* linux at least up to 2.4.22 send a SIGXFZ for vfat fs,
        even if the file is open with O_LARGEFILE ! */
 #ifdef SIGXFSZ
@@ -193,9 +241,8 @@ 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
     
     sv.sa_handler = child_handler;
@@ -208,7 +255,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;
@@ -220,7 +267,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 );
@@ -231,7 +278,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);
     }
 
 
@@ -243,7 +290,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
@@ -267,7 +314,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);
 
@@ -281,10 +328,9 @@ int main(int ac, char **av)
 #endif
     
     /* 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
@@ -293,16 +339,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)
@@ -314,13 +359,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)
@@ -328,17 +376,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 18f57ea1eef7bfe0cecfc0211a6bc4c6e21faac5..b9045743a8abaab5dd132bc3e82645d1d7833415 100644 (file)
 #include "auth.h"
 #include "uam_auth.h"
 
-#ifdef AFP3x
 #define utf8_encoding() (afp_version >= 30)
-#else
-#define utf8_encoding() (0)
-#endif
 
 #ifdef TRU64
 #include <netdb.h>
index cc4ddc98c6e190141836192aae603a625844d172..8c0e646a6489ce932eda5053489b5581b84a318b 100644 (file)
@@ -205,7 +205,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)
@@ -1248,7 +1248,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) &&
@@ -1259,7 +1259,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)
                     &&
@@ -1713,7 +1713,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 e92adf03535970953743642c82ce0af29360a96c..65ed8604ee827f73293f3815fa6ccf7314c8a7b9 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 1310d61c1462ed33f1c1767e0eb0a5722731b709..3e5c9b8f231d25cb4e8d6bb6092f99276396791a 100644 (file)
@@ -104,6 +104,10 @@ static int dbif_init_rootinfo(DBD *dbd, int version)
 
     if (dbif_put(dbd, DBIF_CNID, &key, &data, 0) < 0)
         return -1;
+    if (dbif_txn_commit(dbd) != 1) {
+        LOG(log_error, logtype_cnid, "dbif_init_rootinfo: cant commit txn");
+        return -1;
+    }
 
     return 0;
 }
@@ -120,6 +124,9 @@ static int dbif_getversion(DBD *dbd, uint32_t *version)
     DBT key, data;
     int ret;
 
+    LOG(log_maxdebug, logtype_cnid, "dbif_getversion: reading version info");
+
+    *version = -1;
     memset(&key, 0, sizeof(key));
     memset(&data, 0, sizeof(data));
     key.data = ROOTINFO_KEY;
@@ -133,9 +140,11 @@ static int dbif_getversion(DBD *dbd, uint32_t *version)
         ret = 1;
         break;
     case 0: /* not found */
+        LOG(log_debug, logtype_cnid, "dbif_getversion: no version info found");
         ret = 0;
         break;
     default:
+        LOG(log_error, logtype_cnid, "dbif_getversion: database error");
         ret = -1;
         break;
     }
@@ -146,7 +155,7 @@ static int dbif_getversion(DBD *dbd, uint32_t *version)
 /*!
  * Set CNID database version number
  *
- * Initializes rootinfo key as neccessary, as does dbif_getversion
+ * Initializes rootinfo key as neccessary
  * @returns -1 on error, 0 on success
  */
 static int dbif_setversion(DBD *dbd, uint32_t version)
@@ -184,16 +193,22 @@ static int dbif_setversion(DBD *dbd, uint32_t version)
 }
 
 /*!
- * Upgrade CNID database versions
+ * Upgrade CNID database versions, initialize rootinfo key as as necessary in dbif_setversion()
  *
  * For now this does nothing, as upgrading from ver. 0 to 1 is done in dbif_open
  */
+#define UNINTIALIZED_DB UINT32_MAX
 static int dbif_upgrade(DBD *dbd)
 {
-    uint32_t version;
+    uint32_t version = CNID_VERSION_UNINTIALIZED_DB;
 
     if (dbif_getversion(dbd, &version) == -1)
         return -1;
+    if (version == CNID_VERSION_UNINTIALIZED_DB) {
+        version = CNID_VERSION;
+        if (dbif_setversion(dbd, CNID_VERSION) != 0)
+            return -1;
+    }
 
     /* 
      * Do upgrade stuff ...
@@ -611,6 +626,10 @@ int dbif_open(DBD *dbd, struct db_param *dbp, int reindex)
     if (reindex)
         LOG(log_info, logtype_cnid, "Reindexing name index...");
 
+    /*
+     * Upgrading from version 0 to 1 requires adding the name index below which
+     * must be done by specifying the DB_CREATE flag
+     */
     uint32_t version = CNID_VERSION;
     if (dbd->db_envhome && !reindex) {
         if (dbif_getversion(dbd, &version) == -1)
index e967f4f8765f3a7eb47831496e8b8eb59be843bc..c8207802973149825ff8e985a190392c97267b20 100644 (file)
@@ -368,7 +368,7 @@ int main(int argc, char *argv[])
         exit(1);
     LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
 
-    if (NULL == (dbd = dbif_init(".", "cnid2.db")))
+    if (NULL == (dbd = dbif_init(dbpath, "cnid2.db")))
         exit(2);
 
     if (dbif_env_open(dbd, dbp, DBOPTIONS) < 0)
index b93eb8bce4ea0dd129ee25362ee8f4268cc0f57e..550343d6fb5e4fcbf9a73c81fef867b518fa16ff 100644 (file)
@@ -213,4 +213,7 @@ typedef enum {
 #define AFP_SETACL              74
 #define AFP_ACCESS              75
 
+/* more defines */
+#define REPLAYCACHE_SIZE 128
+
 #endif
index 4e0e0b5570fe5503db28144505d2054757064474..f5f825faa362f806e124cf3e0e32280166756a9c 100644 (file)
@@ -67,8 +67,9 @@
  * 0: up to Netatalk 2.1.x
  * 1: starting with 2.2, additional name index, used in cnid_find
  */
-#define CNID_VERSION_0 0
-#define CNID_VERSION_1 1
+#define CNID_VERSION_0               0
+#define CNID_VERSION_1               1
+#define CNID_VERSION_UNINTIALIZED_DB UINT32_MAX
 
 /* Current CNID version */
 #define CNID_VERSION CNID_VERSION_1
index edb13069cef832f34ec10784e63686edbd244bca..0d4ccc34f1e48b3fc0dbc08ddfc35c844c66fcc8 100644 (file)
@@ -57,12 +57,10 @@ typedef struct DSI {
   dsi_proto protocol;
   struct dsi_block header;
   struct sockaddr_storage server, client;
-  
   struct itimerval timer;
-
-  int     in_write;      /* in the middle of writing multiple packets, signal handlers
-                          * can't write to the socket 
-                         */
+  int      tickle;        /* tickle count */
+  int     in_write;      /* in the middle of writing multiple packets,
+                             signal handlers can't write to the socket */
   int      msg_request;   /* pending message to the client */
   int      down_request;  /* pending SIGUSR1 down in 5 mn */
 
@@ -73,9 +71,7 @@ typedef struct DSI {
   size_t statuslen;
   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;
+  uint32_t flags;             /* DSI flags like DSI_SLEEPING, DSI_DISCONNECTED */
   const char *program; 
   int socket, serversock;
 
@@ -111,6 +107,7 @@ typedef struct DSI {
 /* DSI session options */
 #define DSIOPT_SERVQUANT 0x00   /* server request quantum */
 #define DSIOPT_ATTNQUANT 0x01   /* attention quantum */
+#define DSIOPT_REPLCSIZE 0x02   /* AFP replaycache size supported by the server (that's us) */
 
 /* DSI Commands */
 #define DSIFUNC_CLOSE   1       /* DSICloseSession */
@@ -144,6 +141,14 @@ typedef struct DSI {
 /* default port number */
 #define DSI_AFPOVERTCP_PORT 548
 
+/* DSI session State flags */
+#define DSI_DATA             (1 << 0) /* we have received a DSI command */
+#define DSI_RUNNING          (1 << 1) /* we have received a AFP command */
+#define DSI_SLEEPING         (1 << 2) /* we're sleeping after FPZzz */
+#define DSI_DISCONNECTED     (1 << 3) /* we're in diconnected state after a socket error */
+#define DSI_DIE              (1 << 4) /* SIGUSR1, going down in 5 minutes */
+#define DSI_NOREPLY          (1 << 5) /* in dsi_write we generate our own replies */
+
 /* basic initialization: dsi_init.c */
 extern DSI *dsi_init (const dsi_proto /*protocol*/,
                          const char * /*program*/, 
@@ -153,7 +158,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 15ea49867ffbcadd28e86124d02af5cfc5cb17b3..05f457d00748afc5f0dccb7116003c2e54fef9de 100644 (file)
@@ -22,17 +22,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 b312d825dcda09d4004e855c6254a421fdcb38bd..2ca7e2759d10e3dfad1c27d67fb045f64619a3ca 100644 (file)
@@ -14,7 +14,7 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <unistd.h>
-
+#include <poll.h>
 #ifndef NO_DDP
 #include <netatalk/at.h>
 #endif
@@ -128,11 +128,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 ddc2e3bd5e8be73f989015a748a706d0f8eaf08e..7746768116d5d96f34ece35fa1d97eaa01b3ce0f 100644 (file)
@@ -41,7 +41,8 @@ extern char *ldap_uid_attr;
  ********************************************************/
 
 extern int getuuidfromname( const char *name, uuidtype_t type, unsigned char *uuid);
-extern int getnamefromuuid( uuidp_t uuidp, char **name, uuidtype_t *type);
+extern int getnamefromuuid( const unsigned char *uuid, char **name, uuidtype_t *type);
+extern void localuuid_from_id(unsigned char *buf, uuidtype_t type, unsigned int id);
 extern const char *uuid_bin2string(const unsigned char *uuid);
 extern void uuid_string2bin( const char *uuidstring, unsigned char *uuid);
 
index fe80faca9902f0684c0e17672f12b5892fe2e122..7fe326db1e57a3cb9487c2ac49beaad50b0a4afa 100644 (file)
@@ -194,11 +194,7 @@ int wincheck(const struct vol *vol, const char *path);
 #define VOLPBIT_XBTOTAL 10
 #define VOLPBIT_BSIZE   11        /* block size */
 
-#ifdef AFP3x
 #define utf8_encoding() (afp_version >= 30)
-#else
-#define utf8_encoding() (0)
-#endif
 
 #define vol_noadouble(vol) (((vol)->v_flags & AFPVOL_NOADOUBLE) ? 1 : 0)
 #define vol_nodev(vol) (((vol)->v_flags & AFPVOL_NODEV) ? 1 : 0)
index fb085b2379997283b9649ca3aed383d944192e3a..4b9703617b18d7f2cbc122b2be2685c4e29d3230 100644 (file)
@@ -39,6 +39,34 @@ char *uuidtype[] = {"NULL","USER", "GROUP", "LOCAL"};
  * Public helper function
  ********************************************************/
 
+static unsigned char local_group_uuid[] = {0xab, 0xcd, 0xef,
+                                           0xab, 0xcd, 0xef,
+                                           0xab, 0xcd, 0xef, 
+                                           0xab, 0xcd, 0xef};
+
+static unsigned char local_user_uuid[] = {0xff, 0xff, 0xee, 0xee, 0xdd, 0xdd,
+                                          0xcc, 0xcc, 0xbb, 0xbb, 0xaa, 0xaa};
+
+void localuuid_from_id(unsigned char *buf, uuidtype_t type, unsigned int id)
+{
+    uint32_t tmp;
+
+    switch (type) {
+    case UUID_GROUP:
+        memcpy(buf, local_group_uuid, 12);
+        break;
+    case UUID_USER:
+    default:
+        memcpy(buf, local_user_uuid, 12);
+        break;
+    }
+
+    tmp = htonl(id);
+    memcpy(buf + 12, &tmp, 4);
+
+    return;
+}
+
 /* 
  * convert ascii string that can include dashes to binary uuid.
  * caller must provide a buffer.
@@ -99,14 +127,6 @@ const char *uuid_bin2string(const unsigned char *uuid) {
  * Interface
  ********************************************************/
 
-static unsigned char local_group_uuid[] = {0xab, 0xcd, 0xef,
-                                           0xab, 0xcd, 0xef,
-                                           0xab, 0xcd, 0xef, 
-                                           0xab, 0xcd, 0xef};
-
-static unsigned char local_user_uuid[] = {0xff, 0xff, 0xee, 0xee, 0xdd, 0xdd,
-                                          0xcc, 0xcc, 0xbb, 0xbb, 0xaa, 0xaa};
-
 /*
  *   name: give me his name
  *   type: and type (UUID_USER or UUID_GROUP)
@@ -138,27 +158,21 @@ int getuuidfromname( const char *name, uuidtype_t type, unsigned char *uuid) {
         if (ret != 0) {
             /* Build a local UUID */
             if (type == UUID_USER) {
-                memcpy(uuid, local_user_uuid, 12);
                 struct passwd *pwd;
                 if ((pwd = getpwnam(name)) == NULL) {
                     LOG(log_error, logtype_afpd, "getuuidfromname(\"%s\",t:%u): unknown user",
                         name, uuidtype[type]);
                     goto cleanup;
                 }
-                uint32_t id = pwd->pw_uid;
-                id = htonl(id);
-                memcpy(uuid + 12, &id, 4);
+                localuuid_from_id(uuid, UUID_USER, pwd->pw_uid);
             } else {
-                memcpy(uuid, &local_group_uuid, 12);
                 struct group *grp;
                 if ((grp = getgrnam(name)) == NULL) {
                     LOG(log_error, logtype_afpd, "getuuidfromname(\"%s\",t:%u): unknown user",
                         name, uuidtype[type]);
                     goto cleanup;
                 }
-                uint32_t id = grp->gr_gid;
-                id = htonl(id);
-                memcpy(uuid + 12, &id, 4);
+                localuuid_from_id(uuid, UUID_GROUP, grp->gr_gid);
             }
             LOG(log_debug, logtype_afpd, "getuuidfromname{local}: name: %s, type: %s -> UUID: %s",
                 name, uuidtype[type], uuid_bin2string(uuid));
index 484961eeeb22597330a395504858bd36da24d4d3..731b2b2349b61f2a70cc38e26abaaca382217200 100644 (file)
@@ -168,9 +168,12 @@ u_int32_t ad_forcegetid (struct adouble *adp)
  */
 int ad_setname(struct adouble *ad, const char *path)
 {
+    int len;
+    if ((len = strlen(path)) > ADEDLEN_NAME)
+        len = ADEDLEN_NAME;
     if (path && ad_getentryoff(ad, ADEID_NAME)) {
-        ad_setentrylen( ad, ADEID_NAME, strlen( path ));
-        memcpy(ad_entry( ad, ADEID_NAME ), path, ad_getentrylen( ad, ADEID_NAME ));
+        ad_setentrylen( ad, ADEID_NAME, len);
+        memcpy(ad_entry( ad, ADEID_NAME ), path, len);
         return 1;
     }
     return 0;
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 8a9eaddedaac355cf484fc997ead9973da3311b8..9aef79b53534aeabd9598c622c001b7b8f4904bc 100644 (file)
@@ -34,7 +34,7 @@ int dsi_attention(DSI *dsi, AFPUserBytes flags)
   u_int32_t len, nlen;
   u_int16_t id;
 
-  if (dsi->asleep)
+  if (dsi->flags & DSI_SLEEPING)
       return 1;
 
   if (dsi->in_write) {
index 23c9cf46ce9898faf04b5b0a3dd19fe891d300b3..d409814f5f82c038e736368b8b2bad3ca3138f06 100644 (file)
@@ -17,7 +17,7 @@
 void dsi_close(DSI *dsi)
 {
   /* server generated. need to set all the fields. */
-  if (!dsi->asleep) {
+  if (!(dsi->flags & DSI_SLEEPING)) {
       dsi->header.dsi_flags = DSIFL_REQUEST;
       dsi->header.dsi_command = DSIFUNC_CLOSE;
       dsi->header.dsi_requestID = htons(dsi_serverID(dsi));
index fc2f06def28a4441b49f624bc1e33ea234283323..b8ff4944467becec4eeb035223024cf92cc71fe4 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;
@@ -67,14 +62,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;
@@ -85,8 +79,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 */
@@ -117,7 +110,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 92b5029495066375868e1fe9db6148070ec840ba..e8818e6134bd81f5809a80e2b416284005cbbf72 100644 (file)
@@ -19,6 +19,7 @@
 void dsi_opensession(DSI *dsi)
 {
   u_int32_t i = 0; /* this serves double duty. it must be 4-bytes long */
+  int offs;
 
   /* parse options */
   while (i < dsi->cmdlen) {
@@ -39,7 +40,10 @@ void dsi_opensession(DSI *dsi)
   dsi->header.dsi_flags = DSIFL_REPLY;
   dsi->header.dsi_code = 0;
   /* dsi->header.dsi_command = DSIFUNC_OPEN;*/
-  dsi->cmdlen = 2 + sizeof(i); /* length of data. dsi_send uses it. */
+
+  dsi->cmdlen = 2 * (2 + sizeof(i)); /* length of data. dsi_send uses it. */
+
+  /* DSI Option Server Request Quantum */
   dsi->commands[0] = DSIOPT_SERVQUANT;
   dsi->commands[1] = sizeof(i);
   i = htonl(( dsi->server_quantum < DSI_SERVQUANT_MIN || 
@@ -47,5 +51,11 @@ void dsi_opensession(DSI *dsi)
            DSI_SERVQUANT_DEF : dsi->server_quantum);
   memcpy(dsi->commands + 2, &i, sizeof(i));
 
+  /* AFP replaycache size option */
+  offs = 2 + sizeof(i);
+  dsi->commands[offs] = DSIOPT_REPLCSIZE;
+  dsi->commands[offs+1] = sizeof(i);
+  i = htonl(REPLAYCACHE_SIZE);
+  memcpy(dsi->commands + offs + 2, &i, sizeof(i));
   dsi_send(dsi);
 }
index cd85f8a08dab8aaca7bcdebe96e17b66025bc9e6..c723b3b43b878063c2ede7c1287cb8eea93589d9 100644 (file)
@@ -29,7 +29,7 @@ ssize_t dsi_readinit(DSI *dsi, void *buf, const size_t buflen,
                    const size_t size, const int err)
 {
 
-  dsi->noreply = 1; /* we will handle our own replies */
+  dsi->flags |= DSI_NOREPLY; /* we will handle our own replies */
   dsi->header.dsi_flags = DSIFL_REPLY;
   /*dsi->header.dsi_command = DSIFUNC_CMD;*/
   dsi->header.dsi_len = htonl(size);
index ee595bbd3274a80ab02a09c70a7a4d215f80b3c4..87c559c7a28e672914586307d67461db0bf2c727 100644 (file)
@@ -313,7 +313,9 @@ size_t dsi_stream_read(DSI *dsi, void *data, const size_t length)
     else { /* eof or error */
       /* don't log EOF error if it's just after connect (OSX 10.3 probe) */
       if (len || stored || dsi->read_count) {
-          LOG(log_error, logtype_dsi, "dsi_stream_read(%d): %s", len, (len < 0)?strerror(errno):"unexpected EOF");
+          if (! (dsi->flags & DSI_DISCONNECTED)
+)
+              LOG(log_error, logtype_dsi, "dsi_stream_read(%d): %s", len, (len < 0)?strerror(errno):"unexpected EOF");
       }
       break;
     }
@@ -357,7 +359,10 @@ static size_t dsi_buffered_stream_read(DSI *dsi, u_int8_t *data, const size_t le
 */
 void dsi_sleep(DSI *dsi, const int state)
 {
-    dsi->asleep = state;
+    if (state)
+        dsi->flags |= DSI_SLEEPING;
+    else
+        dsi->flags &= ~DSI_SLEEPING;
 }
 
 /* ---------------------------------------
index 8b3f979b56ba0900fdfcf838454ab14a9243785a..e31fff09369d2d52af1db74cca26d0f52de353c9 100644 (file)
@@ -120,6 +120,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 02aa0536577e2126ad64256b80f0d3e079ceb69b..2a996539a968ba0201c8c19d1f62e9a53a8f7c25 100644 (file)
@@ -23,9 +23,8 @@ int dsi_tickle(DSI *dsi)
 {
   char block[DSI_BLOCKSIZ];
   u_int16_t id;
-  int ret;
   
-  if (dsi->asleep || dsi->in_write)
+  if ((dsi->flags & DSI_SLEEPING) || dsi->in_write)
       return 1;
 
   id = htons(dsi_serverID(dsi));
@@ -36,11 +35,6 @@ int dsi_tickle(DSI *dsi)
   memcpy(block + 2, &id, sizeof(id));
   /* code = len = reserved = 0 */
 
-  ret = dsi_stream_write(dsi, block, DSI_BLOCKSIZ, DSI_NOWAIT);
-  /* we don't really care if we can't send a tickle, it will fail
-   * elsewhere
-  */
-  ret = (ret == -1 || ret == DSI_BLOCKSIZ);
-  return ret;
+  return dsi_stream_write(dsi, block, DSI_BLOCKSIZ, DSI_NOWAIT);
 }
 
index beaec9f533b8d3b992eb6a163ad633e3f52a0a2f..1375a63978f945433d906eeea9abec98e5fee699 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.
  */
 #include <string.h>
 #include <unistd.h>
 #include <signal.h>
+#include <errno.h>
 #include <sys/types.h>
 #include <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 */
+#ifndef WIFEXITED
+#define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif /* ! WIFEXITED */
+#ifndef WIFSTOPPED
+#define WIFSTOPPED(status) (((status) & 0xff) == 0x7f)
+#endif
+#ifndef WIFSIGNALED
+#define WIFSIGNALED(status) (!WIFSTOPPED(status) && !WIFEXITED(status))
+#endif
+#ifndef WTERMSIG
+#define WTERMSIG(status)      ((status) & 0x7f)
+#endif
+
 /* 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;
+
+    /* if we already have an entry. just return. */
+    if (child = resolve_child(fork->table, pid))
+        goto exit;
+
+    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];
 
-  child->pid = pid;
-  child->valid = 0;
-  child->killed = 0;
-  hash_child(fork->table, child);
-  children->count++;
-  pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
+    hash_child(fork->table, child);
+    children->count++;
 
-  return 0;
+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;
@@ -393,18 +371,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 45230a29694b34f4ce2c8c70500afad023960d38..512d83ef2ff00d206e754765d07e2a1980b11a51 100644 (file)
@@ -1,5 +1,7 @@
 /*
  * All rights reserved. See COPYRIGHT.
+ *
+ * IPC over socketpair between parent and children.
  */
 
 #ifdef HAVE_CONFIG_H
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/ipc.h>
-#include <sys/shm.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];
-}
-
-/* ----------------- */
-int server_ipc_parent(void *obj _U_)
-{
-    return pipe_fd[0];
-}
+       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"};
 
-/* ----------------- */
-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;
 
@@ -62,22 +49,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);
@@ -86,17 +76,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;
 }
 
@@ -110,16 +107,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;
 
@@ -135,40 +142,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;
@@ -200,6 +228,7 @@ 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;
+}