2 * $Id: server_child.c,v 1.4 2001-09-06 19:04:40 rufustfirefly Exp $
4 * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
5 * All rights reserved. See COPYRIGHT.
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.
19 #endif /* HAVE_CONFIG_H */
25 #endif /* HAVE_UNISTD_H */
29 /* POSIX.1 sys/wait.h check */
30 #include <sys/types.h>
31 #ifdef HAVE_SYS_WAIT_H
33 #endif /* HAVE_SYS_WAIT_H */
35 #define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
36 #endif /* ! WEXITSTATUS */
38 #define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
39 #endif /* ! WIFEXITED */
41 #include <atalk/server_child.h>
45 #endif /* ! __inline__ */
47 /* hash/child functions: hash OR's pid */
48 #define CHILD_HASHSIZE 32
49 #define HASH(i) ((((i) >> 8) ^ (i)) & (CHILD_HASHSIZE - 1))
51 struct server_child_data {
53 struct server_child_data **prevp, *next;
56 typedef struct server_child_fork {
57 struct server_child_data *table[CHILD_HASHSIZE];
58 void (*cleanup)(const pid_t);
62 static __inline__ void hash_child(struct server_child_data **htable,
63 struct server_child_data *child)
65 struct server_child_data **table;
67 table = &htable[HASH(child->pid)];
68 if ((child->next = *table) != NULL)
69 (*table)->prevp = &child->next;
74 static __inline__ void unhash_child(struct server_child_data *child)
78 child->next->prevp = child->prevp;
79 *(child->prevp) = child->next;
83 static __inline__ struct server_child_data
84 *resolve_child(struct server_child_data **table, const pid_t pid)
86 struct server_child_data *child;
88 for (child = table[HASH(pid)]; child; child = child->next) {
89 if (child->pid == pid)
96 /* initialize server_child structure */
97 server_child *server_child_alloc(const int connections, const int nforks)
99 server_child *children;
101 children = (server_child *) calloc(1, sizeof(server_child));
105 children->nsessions = connections;
106 children->nforks = nforks;
107 children->fork = (void *) calloc(nforks, sizeof(server_child_fork));
109 if (!children->fork) {
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,
122 server_child_fork *fork;
123 struct server_child_data *child;
126 /* we need to prevent deletions from occuring before we get a
127 * chance to add the child in. */
129 sigaddset(&sig, SIGCHLD);
130 sigprocmask(SIG_BLOCK, &sig, NULL);
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);
139 fork = (server_child_fork *) children->fork + forkid;
141 /* if we already have an entry. just return. */
142 if (resolve_child(fork->table, pid)) {
143 sigprocmask(SIG_UNBLOCK, &sig, NULL);
147 if ((child = (struct server_child_data *)
148 calloc(1, sizeof(struct server_child_data))) == NULL) {
149 sigprocmask(SIG_UNBLOCK, &sig, NULL);
154 hash_child(fork->table, child);
156 sigprocmask(SIG_UNBLOCK, &sig, NULL);
161 /* remove a child and free it */
162 int server_child_remove(server_child *children, const int forkid,
165 server_child_fork *fork;
166 struct server_child_data *child;
168 fork = (server_child_fork *) children->fork + forkid;
169 if (!(child = resolve_child(fork->table, pid)))
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)
182 server_child_fork *fork;
183 struct server_child_data *child, *tmp;
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 */
197 free(children->fork);
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,
206 server_child_fork *fork;
207 struct server_child_data *child, *tmp;
210 fork = (server_child_fork *) children->fork + forkid;
211 for (i = 0; i < CHILD_HASHSIZE; i++) {
212 child = fork->table[i];
215 kill(child->pid, sig);
221 /* for extra cleanup if necessary */
222 void server_child_setup(server_child *children, const int forkid,
223 void (*fcn)(const pid_t))
225 server_child_fork *fork;
227 fork = (server_child_fork *) children->fork + forkid;
232 /* keep track of children. */
233 void server_child_handler(server_child *children)
235 server_child_fork *fork;
240 #define WAIT_ANY (-1)
241 #endif /* ! WAIT_ANY */
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;
253 if (WIFEXITED(status)) {
254 if (WEXITSTATUS(status)) {
255 syslog(LOG_INFO, "server_child[%d] %d exited %d", i, pid,
256 WEXITSTATUS(status));
258 syslog(LOG_INFO, "server_child[%d] %d done", i, pid);
261 if (WIFSIGNALED(status))
262 syslog(LOG_INFO, "server_child[%d] %d killed", i, pid);
264 syslog(LOG_INFO, "server_child[%d] %d died", i, pid);