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.
23 #include <sys/syslog.h>
24 #include <sys/types.h>
27 #include <atalk/server_child.h>
33 /* hash/child functions: hash OR's pid */
34 #define CHILD_HASHSIZE 32
35 #define HASH(i) ((((i) >> 8) ^ (i)) & (CHILD_HASHSIZE - 1))
37 struct server_child_data {
39 struct server_child_data **prevp, *next;
42 typedef struct server_child_fork {
43 struct server_child_data *table[CHILD_HASHSIZE];
44 void (*cleanup)(const pid_t);
48 static __inline__ void hash_child(struct server_child_data **htable,
49 struct server_child_data *child)
51 struct server_child_data **table;
53 table = &htable[HASH(child->pid)];
54 if ((child->next = *table) != NULL)
55 (*table)->prevp = &child->next;
60 static __inline__ void unhash_child(struct server_child_data *child)
64 child->next->prevp = child->prevp;
65 *(child->prevp) = child->next;
69 static __inline__ struct server_child_data
70 *resolve_child(struct server_child_data **table, const pid_t pid)
72 struct server_child_data *child;
74 for (child = table[HASH(pid)]; child; child = child->next) {
75 if (child->pid == pid)
82 /* initialize server_child structure */
83 server_child *server_child_alloc(const int connections, const int nforks)
85 server_child *children;
87 children = (server_child *) calloc(1, sizeof(server_child));
91 children->nsessions = connections;
92 children->nforks = nforks;
93 children->fork = (void *) calloc(nforks, sizeof(server_child_fork));
95 if (!children->fork) {
103 /* add a child in. return 0 on success, -1 on serious error, and
104 * > 0 for a non-serious error. */
105 int server_child_add(server_child *children, const int forkid,
108 server_child_fork *fork;
109 struct server_child_data *child;
112 /* we need to prevent deletions from occuring before we get a
113 * chance to add the child in. */
115 sigaddset(&sig, SIGCHLD);
116 sigprocmask(SIG_BLOCK, &sig, NULL);
118 /* it's possible that the child could have already died before the
119 * sigprocmask. we need to check for this. */
120 if (kill(pid, 0) < 0) {
121 sigprocmask(SIG_UNBLOCK, &sig, NULL);
125 fork = (server_child_fork *) children->fork + forkid;
127 /* if we already have an entry. just return. */
128 if (resolve_child(fork->table, pid)) {
129 sigprocmask(SIG_UNBLOCK, &sig, NULL);
133 if ((child = (struct server_child_data *)
134 calloc(1, sizeof(struct server_child_data))) == NULL) {
135 sigprocmask(SIG_UNBLOCK, &sig, NULL);
140 hash_child(fork->table, child);
142 sigprocmask(SIG_UNBLOCK, &sig, NULL);
147 /* remove a child and free it */
148 int server_child_remove(server_child *children, const int forkid,
151 server_child_fork *fork;
152 struct server_child_data *child;
154 fork = (server_child_fork *) children->fork + forkid;
155 if (!(child = resolve_child(fork->table, pid)))
164 /* free everything: by using a hash table, this increases the cost of
165 * this part over a linked list by the size of the hash table */
166 void server_child_free(server_child *children)
168 server_child_fork *fork;
169 struct server_child_data *child, *tmp;
172 for (i = 0; i < children->nforks; i++) {
173 fork = (server_child_fork *) children->fork + i;
174 for (j = 0; j < CHILD_HASHSIZE; j++) {
175 child = fork->table[j]; /* start at the beginning */
183 free(children->fork);
187 /* send kill to child processes: this also has an increased cost over
188 * a plain-old linked list */
189 void server_child_kill(server_child *children, const int forkid,
192 server_child_fork *fork;
193 struct server_child_data *child, *tmp;
196 fork = (server_child_fork *) children->fork + forkid;
197 for (i = 0; i < CHILD_HASHSIZE; i++) {
198 child = fork->table[i];
201 kill(child->pid, sig);
207 /* for extra cleanup if necessary */
208 void server_child_setup(server_child *children, const int forkid,
209 void (*fcn)(const pid_t))
211 server_child_fork *fork;
213 fork = (server_child_fork *) children->fork + forkid;
218 /* keep track of children. */
219 void server_child_handler(server_child *children)
221 server_child_fork *fork;
226 #define WAIT_ANY (-1)
229 while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) {
230 for (i = 0; i < children->nforks; i++) {
231 if (server_child_remove(children, i, pid)) {
232 fork = (server_child_fork *) children->fork + i;
239 if (WIFEXITED(status)) {
240 if (WEXITSTATUS(status)) {
241 syslog(LOG_INFO, "server_child[%d] %d exited %d", i, pid,
242 WEXITSTATUS(status));
244 syslog(LOG_INFO, "server_child[%d] %d done", i, pid);
247 if (WIFSIGNALED(status))
248 syslog(LOG_INFO, "server_child[%d] %d killed", i, pid);
250 syslog(LOG_INFO, "server_child[%d] %d died", i, pid);