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