]> arthur.barton.de Git - netatalk.git/blobdiff - libatalk/util/server_child.c
remove gcc warnings and cleanup inline mess
[netatalk.git] / libatalk / util / server_child.c
index 7e26eb0332715f875ca64fda77d7dac9bc26aefe..06d944f6455d86e7253147094446d0e3b1d82add 100644 (file)
@@ -1,4 +1,6 @@
 /*
+ * $Id: server_child.c,v 1.7.4.1.2.4.2.2 2008-11-25 15:16:35 didg Exp $
+ *
  * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
  * All rights reserved. See COPYRIGHT.
  *
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
+#endif /* HAVE_CONFIG_H */
 
 #include <stdlib.h>
 #include <string.h>
+#ifdef HAVE_UNISTD_H
 #include <unistd.h>
+#endif /* HAVE_UNISTD_H */
 #include <signal.h>
-#include <sys/syslog.h>
+#include <atalk/logger.h>
+
+/* POSIX.1 sys/wait.h check */
 #include <sys/types.h>
+#ifdef HAVE_SYS_WAIT_H
 #include <sys/wait.h>
+#endif /* HAVE_SYS_WAIT_H */
+#include <sys/time.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
 
 #include <atalk/server_child.h>
 
-#ifndef __inline__
-#define __inline__
-#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; 
+  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;
 };
 
@@ -45,7 +73,7 @@ typedef struct server_child_fork {
 } server_child_fork;
 
  
-static __inline__ void hash_child(struct server_child_data **htable,
+static inline void hash_child(struct server_child_data **htable,
                                  struct server_child_data *child)
 {
   struct server_child_data **table;
@@ -57,7 +85,7 @@ static __inline__ void hash_child(struct server_child_data **htable,
   child->prevp = table;
 }
 
-static __inline__ void unhash_child(struct server_child_data *child)
+static inline void unhash_child(struct server_child_data *child)
 {
   if (child->prevp) {
     if (child->next)
@@ -66,7 +94,7 @@ static __inline__ void unhash_child(struct server_child_data *child)
   }
 }
 
-static __inline__ struct server_child_data 
+static inline struct server_child_data 
 *resolve_child(struct server_child_data **table, const pid_t pid)
 {
   struct server_child_data *child;
@@ -107,18 +135,18 @@ int server_child_add(server_child *children, const int forkid,
 {
   server_child_fork *fork;
   struct server_child_data *child;
-  sigset_t sig;
+  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);
-  sigprocmask(SIG_BLOCK, &sig, NULL);
+  sigprocmask(SIG_BLOCK, &sig, &oldsig);
 
   /* it's possible that the child could have already died before the
    * sigprocmask. we need to check for this. */
   if (kill(pid, 0) < 0) {
-    sigprocmask(SIG_UNBLOCK, &sig, NULL);
+    sigprocmask(SIG_SETMASK, &oldsig, NULL);
     return 1;
   }
 
@@ -126,20 +154,22 @@ int server_child_add(server_child *children, const int forkid,
 
   /* if we already have an entry. just return. */
   if (resolve_child(fork->table, pid)) {
-    sigprocmask(SIG_UNBLOCK, &sig, NULL);
+    sigprocmask(SIG_SETMASK, &oldsig, NULL);
     return 0;
   }
 
   if ((child = (struct server_child_data *) 
        calloc(1, sizeof(struct server_child_data))) == NULL) {
-    sigprocmask(SIG_UNBLOCK, &sig, NULL);
+    sigprocmask(SIG_SETMASK, &oldsig, NULL);
     return -1;
   }
 
   child->pid = pid;
+  child->valid = 0;
+  child->killed = 0;
   hash_child(fork->table, child);
   children->count++;
-  sigprocmask(SIG_UNBLOCK, &sig, NULL);
+  sigprocmask(SIG_SETMASK, &oldsig, NULL);
 
   return 0;
 }
@@ -156,6 +186,9 @@ int server_child_remove(server_child *children, const int forkid,
     return 0;
   
   unhash_child(child);
+  if (child->clientid) {
+      free(child->clientid);
+  }
   free(child);
   children->count--;
   return 1;
@@ -175,6 +208,9 @@ void server_child_free(server_child *children)
       child = fork->table[j]; /* start at the beginning */
       while (child) {
        tmp = child->next;
+        if (child->clientid) {
+            free(child->clientid);
+        }
        free(child);
        child = tmp;
       }
@@ -204,6 +240,114 @@ void server_child_kill(server_child *children, const int forkid,
   }
 }
 
+/* send kill to a child processes.
+ * 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;
+}
+
+/* -------------------- */
+void server_child_kill_one(server_child *children, const int forkid, const pid_t pid, const uid_t uid)
+{
+  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;
+    }
+  }
+}
+
+
+/* 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)
+{
+  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_info, logtype_default, "Setting clientid (len %d) for %d, boottime %X", idlen, child->pid, boottime);
+      }
+      child = tmp;
+    }
+  }
+}
+
 /* for extra cleanup if necessary */
 void server_child_setup(server_child *children, const int forkid,
                          void (*fcn)(const pid_t))
@@ -218,36 +362,71 @@ void server_child_setup(server_child *children, const int forkid,
 /* keep track of children. */
 void server_child_handler(server_child *children)
 {
-  server_child_fork *fork;
   int status, i;
   pid_t pid;
   
 #ifndef WAIT_ANY
 #define WAIT_ANY (-1)
-#endif
+#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)) {
-       syslog(LOG_INFO, "server_child[%d] %d exited %d", i, pid, 
+       LOG(log_info, logtype_default, "server_child[%d] %d exited %d", i, pid, 
               WEXITSTATUS(status));
       } else {
-       syslog(LOG_INFO, "server_child[%d] %d done", i, pid);
+       LOG(log_info, logtype_default, "server_child[%d] %d done", i, pid);
       }
     } else {
-      if (WIFSIGNALED(status)) 
-       syslog(LOG_INFO, "server_child[%d] %d killed", i, pid);
+      if (WIFSIGNALED(status))
+      { 
+       LOG(log_info, logtype_default, "server_child[%d] %d killed by signal %d", i, pid,  
+              WTERMSIG (status));
+      }
       else
-       syslog(LOG_INFO, "server_child[%d] %d died", i, pid);
+      {
+       LOG(log_info, logtype_default, "server_child[%d] %d died", i, pid);
+      }
     }
   }
 }
+
+/* --------------------------- 
+ * reset children signals
+*/
+void server_reset_signal(void)
+{
+    struct sigaction    sv;
+    sigset_t            sigs;
+    const struct itimerval none = {{0, 0}, {0, 0}};
+
+    setitimer(ITIMER_REAL, &none, NULL);
+    memset(&sv, 0, sizeof(sv));
+    sv.sa_handler =  SIG_DFL;
+    sigemptyset( &sv.sa_mask );
+    
+    sigaction(SIGALRM, &sv, 0 );
+    sigaction(SIGHUP,  &sv, 0 );
+    sigaction(SIGTERM, &sv, 0 );
+    sigaction(SIGUSR1, &sv, 0 );
+    sigaction(SIGCHLD, &sv, 0 );
+    
+    sigemptyset(&sigs);
+    sigaddset(&sigs, SIGALRM);
+    sigaddset(&sigs, SIGHUP);
+    sigaddset(&sigs, SIGUSR1);
+    sigaddset(&sigs, SIGCHLD);
+    sigprocmask(SIG_UNBLOCK, &sigs, NULL);
+        
+}