2 * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
3 * All rights reserved. See COPYRIGHT.
6 * handle inserting, removing, and freeing of children.
7 * this does it via a hash table. it incurs some overhead over
8 * a linear append/remove in total removal and kills, but it makes
9 * single-entry removals a fast operation. as total removals occur during
10 * child initialization and kills during server shutdown, this is
11 * probably a win for a lot of connections and unimportant for a small
12 * number of connections.
19 #include <sys/syslog.h>
20 #include <sys/types.h>
23 #include <atalk/server_child.h>
29 /* hash/child functions: hash OR's pid */
30 #define CHILD_HASHSIZE 32
31 #define HASH(i) ((((i) >> 8) ^ (i)) & (CHILD_HASHSIZE - 1))
33 struct server_child_data {
35 struct server_child_data **prevp, *next;
38 typedef struct server_child_fork {
39 struct server_child_data *table[CHILD_HASHSIZE];
40 void (*cleanup)(const pid_t);
44 static __inline__ void hash_child(struct server_child_data **htable,
45 struct server_child_data *child)
47 struct server_child_data **table;
49 table = &htable[HASH(child->pid)];
50 if ((child->next = *table) != NULL)
51 (*table)->prevp = &child->next;
56 static __inline__ void unhash_child(struct server_child_data *child)
60 child->next->prevp = child->prevp;
61 *(child->prevp) = child->next;
65 static __inline__ struct server_child_data
66 *resolve_child(struct server_child_data **table, const pid_t pid)
68 struct server_child_data *child;
70 for (child = table[HASH(pid)]; child; child = child->next) {
71 if (child->pid == pid)
78 /* initialize server_child structure */
79 server_child *server_child_alloc(const int connections, const int nforks)
81 server_child *children;
83 children = (server_child *) calloc(1, sizeof(server_child));
87 children->nsessions = connections;
88 children->nforks = nforks;
89 children->fork = (void *) calloc(nforks, sizeof(server_child_fork));
91 if (!children->fork) {
99 /* add a child in. return 0 on success, -1 on serious error, and
100 * > 0 for a non-serious error. */
101 int server_child_add(server_child *children, const int forkid,
104 server_child_fork *fork;
105 struct server_child_data *child;
108 /* we need to prevent deletions from occuring before we get a
109 * chance to add the child in. */
111 sigaddset(&sig, SIGCHLD);
112 sigprocmask(SIG_BLOCK, &sig, NULL);
114 /* it's possible that the child could have already died before the
115 * sigprocmask. we need to check for this. */
116 if (kill(pid, 0) < 0) {
117 sigprocmask(SIG_UNBLOCK, &sig, NULL);
121 fork = (server_child_fork *) children->fork + forkid;
123 /* if we already have an entry. just return. */
124 if (resolve_child(fork->table, pid)) {
125 sigprocmask(SIG_UNBLOCK, &sig, NULL);
129 if ((child = (struct server_child_data *)
130 calloc(1, sizeof(struct server_child_data))) == NULL) {
131 sigprocmask(SIG_UNBLOCK, &sig, NULL);
136 hash_child(fork->table, child);
138 sigprocmask(SIG_UNBLOCK, &sig, NULL);
143 /* remove a child and free it */
144 int server_child_remove(server_child *children, const int forkid,
147 server_child_fork *fork;
148 struct server_child_data *child;
150 fork = (server_child_fork *) children->fork + forkid;
151 if (!(child = resolve_child(fork->table, pid)))
160 /* free everything: by using a hash table, this increases the cost of
161 * this part over a linked list by the size of the hash table */
162 void server_child_free(server_child *children)
164 server_child_fork *fork;
165 struct server_child_data *child, *tmp;
168 for (i = 0; i < children->nforks; i++) {
169 fork = (server_child_fork *) children->fork + i;
170 for (j = 0; j < CHILD_HASHSIZE; j++) {
171 child = fork->table[j]; /* start at the beginning */
179 free(children->fork);
183 /* send kill to child processes: this also has an increased cost over
184 * a plain-old linked list */
185 void server_child_kill(server_child *children, const int forkid,
188 server_child_fork *fork;
189 struct server_child_data *child, *tmp;
192 fork = (server_child_fork *) children->fork + forkid;
193 for (i = 0; i < CHILD_HASHSIZE; i++) {
194 child = fork->table[i];
197 kill(child->pid, sig);
203 /* for extra cleanup if necessary */
204 void server_child_setup(server_child *children, const int forkid,
205 void (*fcn)(const pid_t))
207 server_child_fork *fork;
209 fork = (server_child_fork *) children->fork + forkid;
214 /* keep track of children. */
215 void server_child_handler(server_child *children)
217 server_child_fork *fork;
222 #define WAIT_ANY (-1)
225 while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) {
226 for (i = 0; i < children->nforks; i++) {
227 if (server_child_remove(children, i, pid)) {
228 fork = (server_child_fork *) children->fork + i;
235 if (WIFEXITED(status)) {
236 if (WEXITSTATUS(status)) {
237 syslog(LOG_INFO, "server_child[%d] %d exited %d", i, pid,
238 WEXITSTATUS(status));
240 syslog(LOG_INFO, "server_child[%d] %d done", i, pid);
243 if (WIFSIGNALED(status))
244 syslog(LOG_INFO, "server_child[%d] %d killed", i, pid);
246 syslog(LOG_INFO, "server_child[%d] %d died", i, pid);