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