2 * $Id: server_child.c,v 1.12 2010-01-21 14:14:49 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>
54 /* hash/child functions: hash OR's pid */
55 #define CHILD_HASHSIZE 32
56 #define HASH(i) ((((i) >> 8) ^ (i)) & (CHILD_HASHSIZE - 1))
58 struct server_child_data {
59 pid_t pid; /* afpd worker process pid (from the worker afpd process )*/
60 uid_t uid; /* user id of connected client (from the worker afpd process) */
61 int valid; /* 1 if we have a clientid */
62 u_int32_t time; /* client boot time (from the mac client) */
63 int killed; /* 1 if we already tried to kill the client */
65 u_int32_t idlen; /* clientid len (from the Mac client) */
66 char *clientid; /* clientid (from the Mac client) */
67 struct server_child_data **prevp, *next;
70 typedef struct server_child_fork {
71 struct server_child_data *table[CHILD_HASHSIZE];
72 void (*cleanup)(const pid_t);
76 static inline void hash_child(struct server_child_data **htable,
77 struct server_child_data *child)
79 struct server_child_data **table;
81 table = &htable[HASH(child->pid)];
82 if ((child->next = *table) != NULL)
83 (*table)->prevp = &child->next;
88 static inline void unhash_child(struct server_child_data *child)
92 child->next->prevp = child->prevp;
93 *(child->prevp) = child->next;
97 static inline struct server_child_data
98 *resolve_child(struct server_child_data **table, const pid_t pid)
100 struct server_child_data *child;
102 for (child = table[HASH(pid)]; child; child = child->next) {
103 if (child->pid == pid)
110 /* initialize server_child structure */
111 server_child *server_child_alloc(const int connections, const int nforks)
113 server_child *children;
115 children = (server_child *) calloc(1, sizeof(server_child));
119 children->nsessions = connections;
120 children->nforks = nforks;
121 children->fork = (void *) calloc(nforks, sizeof(server_child_fork));
123 if (!children->fork) {
131 /* add a child in. return 0 on success, -1 on serious error, and
132 * > 0 for a non-serious error. */
133 int server_child_add(server_child *children, const int forkid,
136 server_child_fork *fork;
137 struct server_child_data *child;
138 sigset_t sig, oldsig;
140 /* we need to prevent deletions from occuring before we get a
141 * chance to add the child in. */
143 sigaddset(&sig, SIGCHLD);
144 pthread_sigmask(SIG_BLOCK, &sig, &oldsig);
146 /* it's possible that the child could have already died before the
147 * pthread_sigmask. we need to check for this. */
148 if (kill(pid, 0) < 0) {
149 pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
153 fork = (server_child_fork *) children->fork + forkid;
155 /* if we already have an entry. just return. */
156 if (resolve_child(fork->table, pid)) {
157 pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
161 if ((child = (struct server_child_data *)
162 calloc(1, sizeof(struct server_child_data))) == NULL) {
163 pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
170 hash_child(fork->table, child);
172 pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
177 /* remove a child and free it */
178 int server_child_remove(server_child *children, const int forkid,
181 server_child_fork *fork;
182 struct server_child_data *child;
184 fork = (server_child_fork *) children->fork + forkid;
185 if (!(child = resolve_child(fork->table, pid)))
189 if (child->clientid) {
190 free(child->clientid);
197 /* free everything: by using a hash table, this increases the cost of
198 * this part over a linked list by the size of the hash table */
199 void server_child_free(server_child *children)
201 server_child_fork *fork;
202 struct server_child_data *child, *tmp;
205 for (i = 0; i < children->nforks; i++) {
206 fork = (server_child_fork *) children->fork + i;
207 for (j = 0; j < CHILD_HASHSIZE; j++) {
208 child = fork->table[j]; /* start at the beginning */
211 if (child->clientid) {
212 free(child->clientid);
219 free(children->fork);
223 /* send kill to child processes: this also has an increased cost over
224 * a plain-old linked list */
225 void server_child_kill(server_child *children, const int forkid,
228 server_child_fork *fork;
229 struct server_child_data *child, *tmp;
232 fork = (server_child_fork *) children->fork + forkid;
233 for (i = 0; i < CHILD_HASHSIZE; i++) {
234 child = fork->table[i];
237 kill(child->pid, sig);
243 /* send kill to a child processes.
244 * a plain-old linked list
245 * FIXME use resolve_child ?
247 static int kill_child(struct server_child_data *child)
249 if (!child->killed) {
250 kill(child->pid, SIGTERM);
251 /* we don't wait because there's no guarantee that we can really kill it */
256 LOG(log_info, logtype_default, "Already tried to kill (%d) before! Still there?", child->pid);
261 /* -------------------- */
262 void server_child_kill_one(server_child *children, const int forkid, const pid_t pid, const uid_t uid)
264 server_child_fork *fork;
265 struct server_child_data *child, *tmp;
268 fork = (server_child_fork *) children->fork + forkid;
269 for (i = 0; i < CHILD_HASHSIZE; i++) {
270 child = fork->table[i];
273 if (child->pid == pid) {
275 /* hmm, client 'guess' the pid, rogue? */
276 LOG(log_info, logtype_default, "Disconnecting old session (%d) no token, bailout!", child->pid);
278 else if (child->uid != uid) {
279 LOG(log_info, logtype_default, "Disconnecting old session (%d) not the same user, bailout!", child->pid);
291 /* see if there is a process for the same mac */
292 /* if the times don't match mac has been rebooted */
293 void server_child_kill_one_by_id(server_child *children, const int forkid, const pid_t pid,
295 const u_int32_t idlen, char *id, u_int32_t boottime)
297 server_child_fork *fork;
298 struct server_child_data *child, *tmp;
301 fork = (server_child_fork *) children->fork + forkid;
302 for (i = 0; i < CHILD_HASHSIZE; i++) {
303 child = fork->table[i];
306 if ( child->pid != pid) {
307 if ( child->idlen == idlen && !memcmp(child->clientid, id, idlen)) {
308 if ( child->time != boottime ) {
309 if (uid == child->uid) {
310 if (kill_child(child)) {
311 LOG(log_info, logtype_default, "Disconnecting old session %d, client rebooted.", child->pid);
315 LOG(log_info, logtype_default, "Disconnecting old session not the same uid, bailout!");
318 else if (child->killed) {
319 /* there's case where a Mac close a connection and restart a new one before
320 * the first is 'waited' by the master afpd process
322 LOG(log_info, logtype_default,
323 "WARNING: connection (%d) killed but still there.", child->pid);
326 LOG(log_info, logtype_default,
327 "WARNING: 2 connections (%d, %d), boottime identical, don't know if one needs to be disconnected.",
335 child->time = boottime;
336 /* free old token if any */
337 if (child->clientid) {
338 free(child->clientid);
342 child->idlen = idlen;
343 child->clientid = id;
344 LOG(log_debug, logtype_default, "Setting clientid (len %d) for %d, boottime %X", idlen, child->pid, boottime);
351 /* for extra cleanup if necessary */
352 void server_child_setup(server_child *children, const int forkid,
353 void (*fcn)(const pid_t))
355 server_child_fork *fork;
357 fork = (server_child_fork *) children->fork + forkid;
362 /* keep track of children. */
363 void server_child_handler(server_child *children)
369 #define WAIT_ANY (-1)
370 #endif /* ! WAIT_ANY */
372 while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) {
373 for (i = 0; i < children->nforks; i++) {
374 if (server_child_remove(children, i, pid)) {
375 server_child_fork *fork;
377 fork = (server_child_fork *) children->fork + i;
384 if (WIFEXITED(status)) {
385 if (WEXITSTATUS(status)) {
386 LOG(log_info, logtype_default, "server_child[%d] %d exited %d", i, pid,
387 WEXITSTATUS(status));
389 LOG(log_info, logtype_default, "server_child[%d] %d done", i, pid);
392 if (WIFSIGNALED(status))
394 LOG(log_info, logtype_default, "server_child[%d] %d killed by signal %d", i, pid,
399 LOG(log_info, logtype_default, "server_child[%d] %d died", i, pid);
405 /* ---------------------------
406 * reset children signals
408 void server_reset_signal(void)
412 const struct itimerval none = {{0, 0}, {0, 0}};
414 setitimer(ITIMER_REAL, &none, NULL);
415 memset(&sv, 0, sizeof(sv));
416 sv.sa_handler = SIG_DFL;
417 sigemptyset( &sv.sa_mask );
419 sigaction(SIGALRM, &sv, NULL );
420 sigaction(SIGHUP, &sv, NULL );
421 sigaction(SIGTERM, &sv, NULL );
422 sigaction(SIGUSR1, &sv, NULL );
423 sigaction(SIGCHLD, &sv, NULL );
426 sigaddset(&sigs, SIGALRM);
427 sigaddset(&sigs, SIGHUP);
428 sigaddset(&sigs, SIGUSR1);
429 sigaddset(&sigs, SIGCHLD);
430 pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);