]> arthur.barton.de Git - netatalk.git/blob - libatalk/util/server_child.c
0d6169e252ecf92e810fbe7278430845330d8463
[netatalk.git] / libatalk / util / server_child.c
1 /*
2  * $Id: server_child.c,v 1.4 2001-09-06 19:04:40 rufustfirefly 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 <syslog.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
41 #include <atalk/server_child.h>
42
43 #ifndef __inline__
44 #define __inline__
45 #endif /* ! __inline__ */
46
47 /* hash/child functions: hash OR's pid */
48 #define CHILD_HASHSIZE 32
49 #define HASH(i) ((((i) >> 8) ^ (i)) & (CHILD_HASHSIZE - 1))
50
51 struct server_child_data {
52   pid_t pid; 
53   struct server_child_data **prevp, *next;
54 };
55
56 typedef struct server_child_fork {
57   struct server_child_data *table[CHILD_HASHSIZE];
58   void (*cleanup)(const pid_t);
59 } server_child_fork;
60
61  
62 static __inline__ void hash_child(struct server_child_data **htable,
63                                   struct server_child_data *child)
64 {
65   struct server_child_data **table;
66
67   table = &htable[HASH(child->pid)];
68   if ((child->next = *table) != NULL)
69     (*table)->prevp = &child->next;
70   *table = child;
71   child->prevp = table;
72 }
73
74 static __inline__ void unhash_child(struct server_child_data *child)
75 {
76   if (child->prevp) {
77     if (child->next)
78       child->next->prevp = child->prevp;
79     *(child->prevp) = child->next;
80   }
81 }
82
83 static __inline__ struct server_child_data 
84 *resolve_child(struct server_child_data **table, const pid_t pid)
85 {
86   struct server_child_data *child;
87
88   for (child = table[HASH(pid)]; child; child = child->next) {
89     if (child->pid == pid)
90       break;
91   }
92
93   return child;
94 }
95
96 /* initialize server_child structure */
97 server_child *server_child_alloc(const int connections, const int nforks)
98 {
99   server_child *children;
100
101   children = (server_child *) calloc(1, sizeof(server_child));
102   if (!children)
103     return NULL;
104
105   children->nsessions = connections;
106   children->nforks = nforks;
107   children->fork = (void *) calloc(nforks, sizeof(server_child_fork));
108   
109   if (!children->fork) {
110     free(children);
111     return NULL;
112   } 
113
114   return children;
115 }
116
117 /* add a child in. return 0 on success, -1 on serious error, and
118  * > 0 for a non-serious error. */
119 int server_child_add(server_child *children, const int forkid,
120                      const pid_t pid)
121 {
122   server_child_fork *fork;
123   struct server_child_data *child;
124   sigset_t sig;
125
126   /* we need to prevent deletions from occuring before we get a 
127    * chance to add the child in. */
128   sigemptyset(&sig);
129   sigaddset(&sig, SIGCHLD);
130   sigprocmask(SIG_BLOCK, &sig, NULL);
131
132   /* it's possible that the child could have already died before the
133    * sigprocmask. we need to check for this. */
134   if (kill(pid, 0) < 0) {
135     sigprocmask(SIG_UNBLOCK, &sig, NULL);
136     return 1;
137   }
138
139   fork = (server_child_fork *) children->fork + forkid;
140
141   /* if we already have an entry. just return. */
142   if (resolve_child(fork->table, pid)) {
143     sigprocmask(SIG_UNBLOCK, &sig, NULL);
144     return 0;
145   }
146
147   if ((child = (struct server_child_data *) 
148        calloc(1, sizeof(struct server_child_data))) == NULL) {
149     sigprocmask(SIG_UNBLOCK, &sig, NULL);
150     return -1;
151   }
152
153   child->pid = pid;
154   hash_child(fork->table, child);
155   children->count++;
156   sigprocmask(SIG_UNBLOCK, &sig, NULL);
157
158   return 0;
159 }
160
161 /* remove a child and free it */
162 int server_child_remove(server_child *children, const int forkid,
163                         const pid_t pid)
164 {
165   server_child_fork *fork;
166   struct server_child_data *child;
167
168   fork = (server_child_fork *) children->fork + forkid;
169   if (!(child = resolve_child(fork->table, pid)))
170     return 0;
171   
172   unhash_child(child);
173   free(child);
174   children->count--;
175   return 1;
176 }
177
178 /* free everything: by using a hash table, this increases the cost of
179  * this part over a linked list by the size of the hash table */
180 void server_child_free(server_child *children)
181 {
182   server_child_fork *fork;
183   struct server_child_data *child, *tmp;
184   int i, j;
185
186   for (i = 0; i < children->nforks; i++) {
187     fork = (server_child_fork *) children->fork + i;
188     for (j = 0; j < CHILD_HASHSIZE; j++) {
189       child = fork->table[j]; /* start at the beginning */
190       while (child) {
191         tmp = child->next;
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 /* for extra cleanup if necessary */
222 void server_child_setup(server_child *children, const int forkid,
223                           void (*fcn)(const pid_t))
224 {
225   server_child_fork *fork;
226
227   fork = (server_child_fork *) children->fork + forkid;
228   fork->cleanup = fcn;
229 }
230
231
232 /* keep track of children. */
233 void server_child_handler(server_child *children)
234 {
235   server_child_fork *fork;
236   int status, i;
237   pid_t pid;
238   
239 #ifndef WAIT_ANY
240 #define WAIT_ANY (-1)
241 #endif /* ! WAIT_ANY */
242
243   while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) {
244     for (i = 0; i < children->nforks; i++) {
245       if (server_child_remove(children, i, pid)) {
246         fork = (server_child_fork *) children->fork + i;
247         if (fork->cleanup)
248           fork->cleanup(pid);
249         break;
250       }
251     }
252     
253     if (WIFEXITED(status)) {
254       if (WEXITSTATUS(status)) {
255         syslog(LOG_INFO, "server_child[%d] %d exited %d", i, pid, 
256                WEXITSTATUS(status));
257       } else {
258         syslog(LOG_INFO, "server_child[%d] %d done", i, pid);
259       }
260     } else {
261       if (WIFSIGNALED(status)) 
262         syslog(LOG_INFO, "server_child[%d] %d killed", i, pid);
263       else
264         syslog(LOG_INFO, "server_child[%d] %d died", i, pid);
265     }
266   }
267 }