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