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