2 * $Id: server_child.c,v 1.7.4.1.2.4.2.1 2005-03-31 00:25:55 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 */
37 #define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
38 #endif /* ! WEXITSTATUS */
40 #define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
41 #endif /* ! WIFEXITED */
43 #define WIFSTOPPED(status) (((status) & 0xff) == 0x7f)
46 #define WIFSIGNALED(status) (!WIFSTOPPED(status) && !WIFEXITED(status))
49 #define WTERMSIG(status) ((status) & 0x7f)
52 #include <atalk/server_child.h>
56 #endif /* ! __inline__ */
58 /* hash/child functions: hash OR's pid */
59 #define CHILD_HASHSIZE 32
60 #define HASH(i) ((((i) >> 8) ^ (i)) & (CHILD_HASHSIZE - 1))
62 struct server_child_data {
63 pid_t pid; /* afpd worker process pid (from the worker afpd process )*/
64 uid_t uid; /* user id of connected client (from the worker afpd process) */
65 int valid; /* 1 if we have a clientid */
66 u_int32_t time; /* client boot time (from the mac client) */
67 int killed; /* 1 if we already tried to kill the client */
69 u_int32_t idlen; /* clientid len (from the Mac client) */
70 char *clientid; /* clientid (from the Mac client) */
71 struct server_child_data **prevp, *next;
74 typedef struct server_child_fork {
75 struct server_child_data *table[CHILD_HASHSIZE];
76 void (*cleanup)(const pid_t);
80 static __inline__ void hash_child(struct server_child_data **htable,
81 struct server_child_data *child)
83 struct server_child_data **table;
85 table = &htable[HASH(child->pid)];
86 if ((child->next = *table) != NULL)
87 (*table)->prevp = &child->next;
92 static __inline__ void unhash_child(struct server_child_data *child)
96 child->next->prevp = child->prevp;
97 *(child->prevp) = child->next;
101 static __inline__ struct server_child_data
102 *resolve_child(struct server_child_data **table, const pid_t pid)
104 struct server_child_data *child;
106 for (child = table[HASH(pid)]; child; child = child->next) {
107 if (child->pid == pid)
114 /* initialize server_child structure */
115 server_child *server_child_alloc(const int connections, const int nforks)
117 server_child *children;
119 children = (server_child *) calloc(1, sizeof(server_child));
123 children->nsessions = connections;
124 children->nforks = nforks;
125 children->fork = (void *) calloc(nforks, sizeof(server_child_fork));
127 if (!children->fork) {
135 /* add a child in. return 0 on success, -1 on serious error, and
136 * > 0 for a non-serious error. */
137 int server_child_add(server_child *children, const int forkid,
140 server_child_fork *fork;
141 struct server_child_data *child;
142 sigset_t sig, oldsig;
144 /* we need to prevent deletions from occuring before we get a
145 * chance to add the child in. */
147 sigaddset(&sig, SIGCHLD);
148 sigprocmask(SIG_BLOCK, &sig, &oldsig);
150 /* it's possible that the child could have already died before the
151 * sigprocmask. we need to check for this. */
152 if (kill(pid, 0) < 0) {
153 sigprocmask(SIG_SETMASK, &oldsig, NULL);
157 fork = (server_child_fork *) children->fork + forkid;
159 /* if we already have an entry. just return. */
160 if (resolve_child(fork->table, pid)) {
161 sigprocmask(SIG_SETMASK, &oldsig, NULL);
165 if ((child = (struct server_child_data *)
166 calloc(1, sizeof(struct server_child_data))) == NULL) {
167 sigprocmask(SIG_SETMASK, &oldsig, NULL);
174 hash_child(fork->table, child);
176 sigprocmask(SIG_SETMASK, &oldsig, NULL);
181 /* remove a child and free it */
182 int server_child_remove(server_child *children, const int forkid,
185 server_child_fork *fork;
186 struct server_child_data *child;
188 fork = (server_child_fork *) children->fork + forkid;
189 if (!(child = resolve_child(fork->table, pid)))
193 if (child->clientid) {
194 free(child->clientid);
201 /* free everything: by using a hash table, this increases the cost of
202 * this part over a linked list by the size of the hash table */
203 void server_child_free(server_child *children)
205 server_child_fork *fork;
206 struct server_child_data *child, *tmp;
209 for (i = 0; i < children->nforks; i++) {
210 fork = (server_child_fork *) children->fork + i;
211 for (j = 0; j < CHILD_HASHSIZE; j++) {
212 child = fork->table[j]; /* start at the beginning */
215 if (child->clientid) {
216 free(child->clientid);
223 free(children->fork);
227 /* send kill to child processes: this also has an increased cost over
228 * a plain-old linked list */
229 void server_child_kill(server_child *children, const int forkid,
232 server_child_fork *fork;
233 struct server_child_data *child, *tmp;
236 fork = (server_child_fork *) children->fork + forkid;
237 for (i = 0; i < CHILD_HASHSIZE; i++) {
238 child = fork->table[i];
241 kill(child->pid, sig);
247 /* send kill to a child processes.
248 * a plain-old linked list
249 * FIXME use resolve_child ?
251 static int kill_child(struct server_child_data *child)
253 if (!child->killed) {
254 kill(child->pid, SIGTERM);
255 /* we don't wait because there's no guarantee that we can really kill it */
260 LOG(log_info, logtype_default, "Already tried to kill (%d) before! Still there?", child->pid);
265 /* -------------------- */
266 void server_child_kill_one(server_child *children, const int forkid, const pid_t pid, const uid_t uid)
268 server_child_fork *fork;
269 struct server_child_data *child, *tmp;
272 fork = (server_child_fork *) children->fork + forkid;
273 for (i = 0; i < CHILD_HASHSIZE; i++) {
274 child = fork->table[i];
277 if (child->pid == pid) {
279 /* hmm, client 'guess' the pid, rogue? */
280 LOG(log_info, logtype_default, "Disconnecting old session (%d) no token, bailout!", child->pid);
282 else if (child->uid != uid) {
283 LOG(log_info, logtype_default, "Disconnecting old session (%d) not the same user, bailout!", child->pid);
295 /* see if there is a process for the same mac */
296 /* if the times don't match mac has been rebooted */
297 void server_child_kill_one_by_id(server_child *children, const int forkid, const pid_t pid,
299 const u_int32_t idlen, char *id, u_int32_t boottime)
301 server_child_fork *fork;
302 struct server_child_data *child, *tmp;
305 fork = (server_child_fork *) children->fork + forkid;
306 for (i = 0; i < CHILD_HASHSIZE; i++) {
307 child = fork->table[i];
310 if ( child->pid != pid) {
311 if ( child->idlen == idlen && !memcmp(child->clientid, id, idlen)) {
312 if ( child->time != boottime ) {
313 if (uid == child->uid) {
314 if (kill_child(child)) {
315 LOG(log_info, logtype_default, "Disconnecting old session %d, client rebooted.", child->pid);
319 LOG(log_info, logtype_default, "Disconnecting old session not the same uid, bailout!");
322 else if (child->killed) {
323 /* there's case where a Mac close a connection and restart a new one before
324 * the first is 'waited' by the master afpd process
326 LOG(log_info, logtype_default,
327 "WARNING: connection (%d) killed but still there.", child->pid);
330 LOG(log_info, logtype_default,
331 "WARNING: 2 connections (%d, %d), boottime identical, don't know if one needs to be disconnected.",
339 child->time = boottime;
340 /* free old token if any */
341 if (child->clientid) {
342 free(child->clientid);
346 child->idlen = idlen;
347 child->clientid = id;
348 LOG(log_info, logtype_default, "Setting clientid (len %d) for %d, boottime %X", idlen, child->pid, boottime);
355 /* for extra cleanup if necessary */
356 void server_child_setup(server_child *children, const int forkid,
357 void (*fcn)(const pid_t))
359 server_child_fork *fork;
361 fork = (server_child_fork *) children->fork + forkid;
366 /* keep track of children. */
367 void server_child_handler(server_child *children)
373 #define WAIT_ANY (-1)
374 #endif /* ! WAIT_ANY */
376 while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) {
377 for (i = 0; i < children->nforks; i++) {
378 if (server_child_remove(children, i, pid)) {
379 server_child_fork *fork;
381 fork = (server_child_fork *) children->fork + i;
388 if (WIFEXITED(status)) {
389 if (WEXITSTATUS(status)) {
390 LOG(log_info, logtype_default, "server_child[%d] %d exited %d", i, pid,
391 WEXITSTATUS(status));
393 LOG(log_info, logtype_default, "server_child[%d] %d done", i, pid);
396 if (WIFSIGNALED(status))
398 LOG(log_info, logtype_default, "server_child[%d] %d killed by signal %d", i, pid,
403 LOG(log_info, logtype_default, "server_child[%d] %d died", i, pid);
409 /* ---------------------------
410 * reset children signals
412 void server_reset_signal(void)
416 const struct itimerval none = {{0, 0}, {0, 0}};
418 setitimer(ITIMER_REAL, &none, NULL);
419 memset(&sv, 0, sizeof(sv));
420 sv.sa_handler = SIG_DFL;
421 sigemptyset( &sv.sa_mask );
423 sigaction(SIGALRM, &sv, 0 );
424 sigaction(SIGHUP, &sv, 0 );
425 sigaction(SIGTERM, &sv, 0 );
426 sigaction(SIGUSR1, &sv, 0 );
427 sigaction(SIGCHLD, &sv, 0 );
430 sigaddset(&sigs, SIGALRM);
431 sigaddset(&sigs, SIGHUP);
432 sigaddset(&sigs, SIGUSR1);
433 sigaddset(&sigs, SIGCHLD);
434 sigprocmask(SIG_UNBLOCK, &sigs, NULL);