]> arthur.barton.de Git - netatalk.git/blob - libatalk/util/server_child.c
remove gcc warnings and cleanup inline mess
[netatalk.git] / libatalk / util / server_child.c
1 /*
2  * $Id: server_child.c,v 1.7.4.1.2.4.2.2 2008-11-25 15:16:35 didg Exp $
3  *
4  * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
5  * All rights reserved. See COPYRIGHT.
6  *
7  *
8  * handle inserting, removing, and freeing of children. 
9  * this does it via a hash table. it incurs some overhead over
10  * a linear append/remove in total removal and kills, but it makes
11  * single-entry removals a fast operation. as total removals occur during
12  * child initialization and kills during server shutdown, this is 
13  * probably a win for a lot of connections and unimportant for a small
14  * number of connections.
15  */
16
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif /* HAVE_CONFIG_H */
20
21 #include <stdlib.h>
22 #include <string.h>
23 #ifdef HAVE_UNISTD_H
24 #include <unistd.h>
25 #endif /* HAVE_UNISTD_H */
26 #include <signal.h>
27 #include <atalk/logger.h>
28
29 /* POSIX.1 sys/wait.h check */
30 #include <sys/types.h>
31 #ifdef HAVE_SYS_WAIT_H
32 #include <sys/wait.h>
33 #endif /* HAVE_SYS_WAIT_H */
34 #include <sys/time.h>
35
36 #ifndef WEXITSTATUS
37 #define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
38 #endif /* ! WEXITSTATUS */
39 #ifndef WIFEXITED
40 #define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
41 #endif /* ! WIFEXITED */
42 #ifndef WIFSTOPPED
43 #define WIFSTOPPED(status) (((status) & 0xff) == 0x7f)
44 #endif
45 #ifndef WIFSIGNALED
46 #define WIFSIGNALED(status) (!WIFSTOPPED(status) && !WIFEXITED(status)) 
47 #endif
48 #ifndef WTERMSIG
49 #define WTERMSIG(status)      ((status) & 0x7f)
50 #endif
51
52 #include <atalk/server_child.h>
53
54 /* hash/child functions: hash OR's pid */
55 #define CHILD_HASHSIZE 32
56 #define HASH(i) ((((i) >> 8) ^ (i)) & (CHILD_HASHSIZE - 1))
57
58 struct server_child_data {
59   pid_t     pid;                /* afpd worker process pid (from the worker afpd process )*/
60   uid_t     uid;                /* user id of connected client (from the worker afpd process) */
61   int       valid;              /* 1 if we have a clientid */
62   u_int32_t time;               /* client boot time (from the mac client) */
63   int       killed;             /* 1 if we already tried to kill the client */
64
65   u_int32_t idlen;              /* clientid len (from the Mac client) */
66   char *clientid;               /* clientid (from the Mac client) */
67   struct server_child_data **prevp, *next;
68 };
69
70 typedef struct server_child_fork {
71   struct server_child_data *table[CHILD_HASHSIZE];
72   void (*cleanup)(const pid_t);
73 } server_child_fork;
74
75  
76 static inline void hash_child(struct server_child_data **htable,
77                                   struct server_child_data *child)
78 {
79   struct server_child_data **table;
80
81   table = &htable[HASH(child->pid)];
82   if ((child->next = *table) != NULL)
83     (*table)->prevp = &child->next;
84   *table = child;
85   child->prevp = table;
86 }
87
88 static inline void unhash_child(struct server_child_data *child)
89 {
90   if (child->prevp) {
91     if (child->next)
92       child->next->prevp = child->prevp;
93     *(child->prevp) = child->next;
94   }
95 }
96
97 static inline struct server_child_data 
98 *resolve_child(struct server_child_data **table, const pid_t pid)
99 {
100   struct server_child_data *child;
101
102   for (child = table[HASH(pid)]; child; child = child->next) {
103     if (child->pid == pid)
104       break;
105   }
106
107   return child;
108 }
109
110 /* initialize server_child structure */
111 server_child *server_child_alloc(const int connections, const int nforks)
112 {
113   server_child *children;
114
115   children = (server_child *) calloc(1, sizeof(server_child));
116   if (!children)
117     return NULL;
118
119   children->nsessions = connections;
120   children->nforks = nforks;
121   children->fork = (void *) calloc(nforks, sizeof(server_child_fork));
122   
123   if (!children->fork) {
124     free(children);
125     return NULL;
126   } 
127
128   return children;
129 }
130
131 /* add a child in. return 0 on success, -1 on serious error, and
132  * > 0 for a non-serious error. */
133 int server_child_add(server_child *children, const int forkid,
134                      const pid_t pid)
135 {
136   server_child_fork *fork;
137   struct server_child_data *child;
138   sigset_t sig, oldsig;
139
140   /* we need to prevent deletions from occuring before we get a 
141    * chance to add the child in. */
142   sigemptyset(&sig);
143   sigaddset(&sig, SIGCHLD);
144   sigprocmask(SIG_BLOCK, &sig, &oldsig);
145
146   /* it's possible that the child could have already died before the
147    * sigprocmask. we need to check for this. */
148   if (kill(pid, 0) < 0) {
149     sigprocmask(SIG_SETMASK, &oldsig, NULL);
150     return 1;
151   }
152
153   fork = (server_child_fork *) children->fork + forkid;
154
155   /* if we already have an entry. just return. */
156   if (resolve_child(fork->table, pid)) {
157     sigprocmask(SIG_SETMASK, &oldsig, NULL);
158     return 0;
159   }
160
161   if ((child = (struct server_child_data *) 
162        calloc(1, sizeof(struct server_child_data))) == NULL) {
163     sigprocmask(SIG_SETMASK, &oldsig, NULL);
164     return -1;
165   }
166
167   child->pid = pid;
168   child->valid = 0;
169   child->killed = 0;
170   hash_child(fork->table, child);
171   children->count++;
172   sigprocmask(SIG_SETMASK, &oldsig, NULL);
173
174   return 0;
175 }
176
177 /* remove a child and free it */
178 int server_child_remove(server_child *children, const int forkid,
179                         const pid_t pid)
180 {
181   server_child_fork *fork;
182   struct server_child_data *child;
183
184   fork = (server_child_fork *) children->fork + forkid;
185   if (!(child = resolve_child(fork->table, pid)))
186     return 0;
187   
188   unhash_child(child);
189   if (child->clientid) {
190       free(child->clientid);
191   }
192   free(child);
193   children->count--;
194   return 1;
195 }
196
197 /* free everything: by using a hash table, this increases the cost of
198  * this part over a linked list by the size of the hash table */
199 void server_child_free(server_child *children)
200 {
201   server_child_fork *fork;
202   struct server_child_data *child, *tmp;
203   int i, j;
204
205   for (i = 0; i < children->nforks; i++) {
206     fork = (server_child_fork *) children->fork + i;
207     for (j = 0; j < CHILD_HASHSIZE; j++) {
208       child = fork->table[j]; /* start at the beginning */
209       while (child) {
210         tmp = child->next;
211         if (child->clientid) {
212             free(child->clientid);
213         }
214         free(child);
215         child = tmp;
216       }
217     }
218   }
219   free(children->fork);
220   free(children);
221 }
222
223 /* send kill to child processes: this also has an increased cost over
224  * a plain-old linked list */
225 void server_child_kill(server_child *children, const int forkid,
226                        const int sig)
227 {
228   server_child_fork *fork;
229   struct server_child_data *child, *tmp;
230   int i;
231
232   fork = (server_child_fork *) children->fork + forkid;
233   for (i = 0; i < CHILD_HASHSIZE; i++) {
234     child = fork->table[i];
235     while (child) {
236       tmp = child->next;
237       kill(child->pid, sig);
238       child = tmp;
239     }
240   }
241 }
242
243 /* send kill to a child processes.
244  * a plain-old linked list 
245  * FIXME use resolve_child ?
246  */
247 static int kill_child(struct server_child_data *child)
248 {
249   if (!child->killed) {
250      kill(child->pid, SIGTERM);
251      /* we don't wait because there's no guarantee that we can really kill it */
252      child->killed = 1;
253      return 1;
254   }
255   else {
256      LOG(log_info, logtype_default, "Already tried to kill (%d) before! Still there?",  child->pid);
257   }
258   return 0;
259 }
260
261 /* -------------------- */
262 void server_child_kill_one(server_child *children, const int forkid, const pid_t pid, const uid_t uid)
263 {
264   server_child_fork *fork;
265   struct server_child_data *child, *tmp;
266   int i;
267
268   fork = (server_child_fork *) children->fork + forkid;
269   for (i = 0; i < CHILD_HASHSIZE; i++) {
270     child = fork->table[i];
271     while (child) {
272       tmp = child->next;
273       if (child->pid == pid) {
274           if (!child->valid) {
275              /* hmm, client 'guess' the pid, rogue? */
276              LOG(log_info, logtype_default, "Disconnecting old session (%d) no token, bailout!",  child->pid);
277           }
278           else if (child->uid != uid) {
279              LOG(log_info, logtype_default, "Disconnecting old session (%d) not the same user, bailout!",  child->pid);
280           }
281           else {
282               kill_child(child);
283           }
284       }
285       child = tmp;
286     }
287   }
288 }
289
290
291 /* see if there is a process for the same mac     */
292 /* if the times don't match mac has been rebooted */
293 void server_child_kill_one_by_id(server_child *children, const int forkid, const pid_t pid,
294           const uid_t uid, 
295           const u_int32_t idlen, char *id, u_int32_t boottime)
296 {
297   server_child_fork *fork;
298   struct server_child_data *child, *tmp;
299   int i;
300
301   fork = (server_child_fork *) children->fork + forkid;
302   for (i = 0; i < CHILD_HASHSIZE; i++) {
303     child = fork->table[i];
304     while (child) {
305       tmp = child->next;
306       if ( child->pid != pid) {
307           if ( child->idlen == idlen && !memcmp(child->clientid, id, idlen)) {
308              if ( child->time != boottime ) {
309                   if (uid == child->uid) {
310                       if (kill_child(child)) {
311                           LOG(log_info, logtype_default, "Disconnecting old session %d, client rebooted.",  child->pid);
312                       }
313                   }
314                   else {
315                       LOG(log_info, logtype_default, "Disconnecting old session not the same uid, bailout!");
316                   }
317              }
318              else if (child->killed) {
319                   /* there's case where a Mac close a connection and restart a new one before
320                    * the first is 'waited' by the master afpd process
321                   */
322                   LOG(log_info, logtype_default, 
323                       "WARNING: connection (%d) killed but still there.", child->pid);
324              }
325              else {
326                   LOG(log_info, logtype_default, 
327                       "WARNING: 2 connections (%d, %d), boottime identical, don't know if one needs to be disconnected.",
328                        child->pid, pid);
329              } 
330                 
331           }
332       }
333       else 
334       {
335           child->time = boottime;
336           /* free old token if any */
337           if (child->clientid) {
338               free(child->clientid);
339           }
340           child->uid = uid; 
341           child->valid = 1;
342           child->idlen = idlen;
343           child->clientid = id;
344           LOG(log_info, logtype_default, "Setting clientid (len %d) for %d, boottime %X", idlen, child->pid, boottime);
345       }
346       child = tmp;
347     }
348   }
349 }
350
351 /* for extra cleanup if necessary */
352 void server_child_setup(server_child *children, const int forkid,
353                           void (*fcn)(const pid_t))
354 {
355   server_child_fork *fork;
356
357   fork = (server_child_fork *) children->fork + forkid;
358   fork->cleanup = fcn;
359 }
360
361
362 /* keep track of children. */
363 void server_child_handler(server_child *children)
364 {
365   int status, i;
366   pid_t pid;
367   
368 #ifndef WAIT_ANY
369 #define WAIT_ANY (-1)
370 #endif /* ! WAIT_ANY */
371
372   while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) {
373     for (i = 0; i < children->nforks; i++) {
374       if (server_child_remove(children, i, pid)) {
375         server_child_fork *fork;
376         
377         fork = (server_child_fork *) children->fork + i;
378         if (fork->cleanup)
379           fork->cleanup(pid);
380         break;
381       }
382     }
383
384     if (WIFEXITED(status)) {
385       if (WEXITSTATUS(status)) {
386         LOG(log_info, logtype_default, "server_child[%d] %d exited %d", i, pid, 
387                WEXITSTATUS(status));
388       } else {
389         LOG(log_info, logtype_default, "server_child[%d] %d done", i, pid);
390       }
391     } else {
392       if (WIFSIGNALED(status))
393       { 
394         LOG(log_info, logtype_default, "server_child[%d] %d killed by signal %d", i, pid,  
395                WTERMSIG (status));
396       }
397       else
398       {
399         LOG(log_info, logtype_default, "server_child[%d] %d died", i, pid);
400       }
401     }
402   }
403 }
404
405 /* --------------------------- 
406  * reset children signals
407 */
408 void server_reset_signal(void)
409 {
410     struct sigaction    sv;
411     sigset_t            sigs;
412     const struct itimerval none = {{0, 0}, {0, 0}};
413
414     setitimer(ITIMER_REAL, &none, NULL);
415     memset(&sv, 0, sizeof(sv));
416     sv.sa_handler =  SIG_DFL;
417     sigemptyset( &sv.sa_mask );
418     
419     sigaction(SIGALRM, &sv, 0 );
420     sigaction(SIGHUP,  &sv, 0 );
421     sigaction(SIGTERM, &sv, 0 );
422     sigaction(SIGUSR1, &sv, 0 );
423     sigaction(SIGCHLD, &sv, 0 );
424     
425     sigemptyset(&sigs);
426     sigaddset(&sigs, SIGALRM);
427     sigaddset(&sigs, SIGHUP);
428     sigaddset(&sigs, SIGUSR1);
429     sigaddset(&sigs, SIGCHLD);
430     sigprocmask(SIG_UNBLOCK, &sigs, NULL);
431         
432 }