2 * $Id: server_child.c,v 1.7.4.1.2.1 2003-11-11 08:48:33 didg 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 */
27 #include <atalk/logger.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 #define WIFSTOPPED(status) (((status) & 0xff) == 0x7f)
44 #define WIFSIGNALED(status) (!WIFSTOPPED(status) && !WIFEXITED(status))
47 #define WTERMSIG(status) ((status) & 0x7f)
50 #include <atalk/server_child.h>
54 #endif /* ! __inline__ */
56 /* hash/child functions: hash OR's pid */
57 #define CHILD_HASHSIZE 32
58 #define HASH(i) ((((i) >> 8) ^ (i)) & (CHILD_HASHSIZE - 1))
60 struct server_child_data {
66 struct server_child_data **prevp, *next;
69 typedef struct server_child_fork {
70 struct server_child_data *table[CHILD_HASHSIZE];
71 void (*cleanup)(const pid_t);
75 static __inline__ void hash_child(struct server_child_data **htable,
76 struct server_child_data *child)
78 struct server_child_data **table;
80 table = &htable[HASH(child->pid)];
81 if ((child->next = *table) != NULL)
82 (*table)->prevp = &child->next;
87 static __inline__ void unhash_child(struct server_child_data *child)
91 child->next->prevp = child->prevp;
92 *(child->prevp) = child->next;
96 static __inline__ struct server_child_data
97 *resolve_child(struct server_child_data **table, const pid_t pid)
99 struct server_child_data *child;
101 for (child = table[HASH(pid)]; child; child = child->next) {
102 if (child->pid == pid)
109 /* initialize server_child structure */
110 server_child *server_child_alloc(const int connections, const int nforks)
112 server_child *children;
114 children = (server_child *) calloc(1, sizeof(server_child));
118 children->nsessions = connections;
119 children->nforks = nforks;
120 children->fork = (void *) calloc(nforks, sizeof(server_child_fork));
122 if (!children->fork) {
130 /* add a child in. return 0 on success, -1 on serious error, and
131 * > 0 for a non-serious error. */
132 int server_child_add(server_child *children, const int forkid,
135 server_child_fork *fork;
136 struct server_child_data *child;
137 sigset_t sig, oldsig;
139 /* we need to prevent deletions from occuring before we get a
140 * chance to add the child in. */
142 sigaddset(&sig, SIGCHLD);
143 sigprocmask(SIG_BLOCK, &sig, &oldsig);
145 /* it's possible that the child could have already died before the
146 * sigprocmask. we need to check for this. */
147 if (kill(pid, 0) < 0) {
148 sigprocmask(SIG_SETMASK, &oldsig, NULL);
152 fork = (server_child_fork *) children->fork + forkid;
154 /* if we already have an entry. just return. */
155 if (resolve_child(fork->table, pid)) {
156 sigprocmask(SIG_SETMASK, &oldsig, NULL);
160 if ((child = (struct server_child_data *)
161 calloc(1, sizeof(struct server_child_data))) == NULL) {
162 sigprocmask(SIG_SETMASK, &oldsig, NULL);
167 hash_child(fork->table, child);
169 sigprocmask(SIG_SETMASK, &oldsig, NULL);
174 /* remove a child and free it */
175 int server_child_remove(server_child *children, const int forkid,
178 server_child_fork *fork;
179 struct server_child_data *child;
181 fork = (server_child_fork *) children->fork + forkid;
182 if (!(child = resolve_child(fork->table, pid)))
186 if (child->clientid) {
187 free(child->clientid);
194 /* free everything: by using a hash table, this increases the cost of
195 * this part over a linked list by the size of the hash table */
196 void server_child_free(server_child *children)
198 server_child_fork *fork;
199 struct server_child_data *child, *tmp;
202 for (i = 0; i < children->nforks; i++) {
203 fork = (server_child_fork *) children->fork + i;
204 for (j = 0; j < CHILD_HASHSIZE; j++) {
205 child = fork->table[j]; /* start at the beginning */
208 if (child->clientid) {
209 free(child->clientid);
216 free(children->fork);
220 /* send kill to child processes: this also has an increased cost over
221 * a plain-old linked list */
222 void server_child_kill(server_child *children, const int forkid,
225 server_child_fork *fork;
226 struct server_child_data *child, *tmp;
229 fork = (server_child_fork *) children->fork + forkid;
230 for (i = 0; i < CHILD_HASHSIZE; i++) {
231 child = fork->table[i];
234 kill(child->pid, sig);
240 /* send kill to a child processes.
241 * a plain-old linked list
242 * FIXME use resolve_child ?
244 void server_child_kill_one(server_child *children, const int forkid, const pid_t pid)
246 server_child_fork *fork;
247 struct server_child_data *child, *tmp;
250 fork = (server_child_fork *) children->fork + forkid;
251 for (i = 0; i < CHILD_HASHSIZE; i++) {
252 child = fork->table[i];
255 if (child->pid == pid) {
256 kill(child->pid, SIGTERM);
264 /* see if there is a process for the same mac */
265 /* if the times don't match mac has been rebooted */
266 void server_child_kill_one_by_id(server_child *children, const int forkid, const pid_t pid,
267 const u_int32_t idlen, char *id, u_int32_t boottime)
269 server_child_fork *fork;
270 struct server_child_data *child, *tmp;
273 fork = (server_child_fork *) children->fork + forkid;
274 for (i = 0; i < CHILD_HASHSIZE; i++) {
275 child = fork->table[i];
278 if ( child->pid != pid) {
279 if ( child->idlen == idlen && !memcmp(child->clientid, id, idlen)) {
280 if ( child->time != boottime ) {
281 kill(child->pid, SIGTERM);
282 LOG(log_info, logtype_default, "Disconnecting old session %d, client rebooted.", child->pid);
285 LOG(log_info, logtype_default, "WARNING: 2 connections (%d, %d), boottime identical, don't know if one needs to be disconnected.");
292 child->time = boottime;
293 /* free old token if any */
294 if (child->clientid) {
295 free(child->clientid);
297 child->idlen = idlen;
298 child->clientid = id;
299 LOG(log_info, logtype_default, "Setting clientid (len %d) for %d, boottime %X", idlen, child->pid, boottime);
306 /* for extra cleanup if necessary */
307 void server_child_setup(server_child *children, const int forkid,
308 void (*fcn)(const pid_t))
310 server_child_fork *fork;
312 fork = (server_child_fork *) children->fork + forkid;
317 /* keep track of children. */
318 void server_child_handler(server_child *children)
324 #define WAIT_ANY (-1)
325 #endif /* ! WAIT_ANY */
327 while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) {
328 for (i = 0; i < children->nforks; i++) {
329 if (server_child_remove(children, i, pid)) {
330 server_child_fork *fork;
332 fork = (server_child_fork *) children->fork + i;
339 if (WIFEXITED(status)) {
340 if (WEXITSTATUS(status)) {
341 LOG(log_info, logtype_default, "server_child[%d] %d exited %d", i, pid,
342 WEXITSTATUS(status));
344 LOG(log_info, logtype_default, "server_child[%d] %d done", i, pid);
347 if (WIFSIGNALED(status))
349 LOG(log_info, logtype_default, "server_child[%d] %d killed by signal %d", i, pid,
354 LOG(log_info, logtype_default, "server_child[%d] %d died", i, pid);