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 #include <sys/types.h>
29 #include <atalk/logger.h>
30 #include <atalk/server_child.h>
32 /* hash/child functions: hash OR's pid */
33 #define CHILD_HASHSIZE 32
34 #define HASH(i) ((((i) >> 8) ^ (i)) & (CHILD_HASHSIZE - 1))
36 struct server_child_data {
37 pid_t pid; /* afpd worker process pid (from the worker afpd process )*/
38 uid_t uid; /* user id of connected client (from the worker afpd process) */
39 int valid; /* 1 if we have a clientid */
40 u_int32_t time; /* client boot time (from the mac client) */
41 int killed; /* 1 if we already tried to kill the client */
43 u_int32_t idlen; /* clientid len (from the Mac client) */
44 char *clientid; /* clientid (from the Mac client) */
45 struct server_child_data **prevp, *next;
48 typedef struct server_child_fork {
49 struct server_child_data *table[CHILD_HASHSIZE];
50 void (*cleanup)(const pid_t);
54 static inline void hash_child(struct server_child_data **htable,
55 struct server_child_data *child)
57 struct server_child_data **table;
59 table = &htable[HASH(child->pid)];
60 if ((child->next = *table) != NULL)
61 (*table)->prevp = &child->next;
66 static inline void unhash_child(struct server_child_data *child)
70 child->next->prevp = child->prevp;
71 *(child->prevp) = child->next;
75 static inline struct server_child_data
76 *resolve_child(struct server_child_data **table, const pid_t pid)
78 struct server_child_data *child;
80 for (child = table[HASH(pid)]; child; child = child->next) {
81 if (child->pid == pid)
88 /* initialize server_child structure */
89 server_child *server_child_alloc(const int connections, const int nforks)
91 server_child *children;
93 children = (server_child *) calloc(1, sizeof(server_child));
97 children->nsessions = connections;
98 children->nforks = nforks;
99 children->fork = (void *) calloc(nforks, sizeof(server_child_fork));
101 if (!children->fork) {
109 /* add a child in. return 0 on success, -1 on serious error, and
110 * > 0 for a non-serious error. */
111 int server_child_add(server_child *children, const int forkid,
114 server_child_fork *fork;
115 struct server_child_data *child;
116 sigset_t sig, oldsig;
118 /* we need to prevent deletions from occuring before we get a
119 * chance to add the child in. */
121 sigaddset(&sig, SIGCHLD);
122 pthread_sigmask(SIG_BLOCK, &sig, &oldsig);
124 /* it's possible that the child could have already died before the
125 * pthread_sigmask. we need to check for this. */
126 if (kill(pid, 0) < 0) {
127 pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
131 fork = (server_child_fork *) children->fork + forkid;
133 /* if we already have an entry. just return. */
134 if (resolve_child(fork->table, pid)) {
135 pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
139 if ((child = (struct server_child_data *)
140 calloc(1, sizeof(struct server_child_data))) == NULL) {
141 pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
148 hash_child(fork->table, child);
150 pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
155 /* remove a child and free it */
156 int server_child_remove(server_child *children, const int forkid,
159 server_child_fork *fork;
160 struct server_child_data *child;
162 fork = (server_child_fork *) children->fork + forkid;
163 if (!(child = resolve_child(fork->table, pid)))
167 if (child->clientid) {
168 free(child->clientid);
175 /* free everything: by using a hash table, this increases the cost of
176 * this part over a linked list by the size of the hash table */
177 void server_child_free(server_child *children)
179 server_child_fork *fork;
180 struct server_child_data *child, *tmp;
183 for (i = 0; i < children->nforks; i++) {
184 fork = (server_child_fork *) children->fork + i;
185 for (j = 0; j < CHILD_HASHSIZE; j++) {
186 child = fork->table[j]; /* start at the beginning */
189 if (child->clientid) {
190 free(child->clientid);
197 free(children->fork);
201 /* send kill to child processes: this also has an increased cost over
202 * a plain-old linked list */
203 void server_child_kill(server_child *children, const int forkid,
206 server_child_fork *fork;
207 struct server_child_data *child, *tmp;
210 fork = (server_child_fork *) children->fork + forkid;
211 for (i = 0; i < CHILD_HASHSIZE; i++) {
212 child = fork->table[i];
215 kill(child->pid, sig);
221 /* send kill to a child processes.
222 * a plain-old linked list
223 * FIXME use resolve_child ?
225 static int kill_child(struct server_child_data *child)
227 if (!child->killed) {
228 kill(child->pid, SIGTERM);
229 /* we don't wait because there's no guarantee that we can really kill it */
234 LOG(log_info, logtype_default, "Already tried to kill (%d) before! Still there?", child->pid);
239 /* -------------------- */
240 void server_child_kill_one(server_child *children, const int forkid, const pid_t pid, const uid_t uid)
242 server_child_fork *fork;
243 struct server_child_data *child, *tmp;
246 fork = (server_child_fork *) children->fork + forkid;
247 for (i = 0; i < CHILD_HASHSIZE; i++) {
248 child = fork->table[i];
251 if (child->pid == pid) {
253 /* hmm, client 'guess' the pid, rogue? */
254 LOG(log_info, logtype_default, "Disconnecting old session (%d) no token, bailout!", child->pid);
256 else if (child->uid != uid) {
257 LOG(log_info, logtype_default, "Disconnecting old session (%d) not the same user, bailout!", child->pid);
269 /* see if there is a process for the same mac */
270 /* if the times don't match mac has been rebooted */
271 void server_child_kill_one_by_id(server_child *children, const int forkid, const pid_t pid,
273 const u_int32_t idlen, char *id, u_int32_t boottime)
275 server_child_fork *fork;
276 struct server_child_data *child, *tmp;
279 fork = (server_child_fork *) children->fork + forkid;
280 for (i = 0; i < CHILD_HASHSIZE; i++) {
281 child = fork->table[i];
284 if ( child->pid != pid) {
285 if ( child->idlen == idlen && !memcmp(child->clientid, id, idlen)) {
286 if ( child->time != boottime ) {
287 if (uid == child->uid) {
288 if (kill_child(child)) {
289 LOG(log_info, logtype_default, "Disconnecting old session %d, client rebooted.", child->pid);
293 LOG(log_info, logtype_default, "Disconnecting old session not the same uid, bailout!");
296 else if (child->killed) {
297 /* there's case where a Mac close a connection and restart a new one before
298 * the first is 'waited' by the master afpd process
300 LOG(log_info, logtype_default,
301 "WARNING: connection (%d) killed but still there.", child->pid);
304 LOG(log_info, logtype_default,
305 "WARNING: 2 connections (%d, %d), boottime identical, don't know if one needs to be disconnected.",
313 child->time = boottime;
314 /* free old token if any */
315 if (child->clientid) {
316 free(child->clientid);
320 child->idlen = idlen;
321 child->clientid = id;
322 LOG(log_debug, logtype_default, "Setting clientid (len %d) for %d, boottime %X", idlen, child->pid, boottime);
329 /* for extra cleanup if necessary */
330 void server_child_setup(server_child *children, const int forkid,
331 void (*fcn)(const pid_t))
333 server_child_fork *fork;
335 fork = (server_child_fork *) children->fork + forkid;
340 /* keep track of children. */
341 void server_child_handler(server_child *children)
347 #define WAIT_ANY (-1)
348 #endif /* ! WAIT_ANY */
350 while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) {
351 for (i = 0; i < children->nforks; i++) {
352 if (server_child_remove(children, i, pid)) {
353 server_child_fork *fork;
355 fork = (server_child_fork *) children->fork + i;
362 if (WIFEXITED(status)) {
363 if (WEXITSTATUS(status)) {
364 LOG(log_info, logtype_default, "server_child[%d] %d exited %d", i, pid,
365 WEXITSTATUS(status));
367 LOG(log_info, logtype_default, "server_child[%d] %d done", i, pid);
370 if (WIFSIGNALED(status))
372 LOG(log_info, logtype_default, "server_child[%d] %d killed by signal %d", i, pid,
377 LOG(log_info, logtype_default, "server_child[%d] %d died", i, pid);
383 /* ---------------------------
384 * reset children signals
386 void server_reset_signal(void)
390 const struct itimerval none = {{0, 0}, {0, 0}};
392 setitimer(ITIMER_REAL, &none, NULL);
393 memset(&sv, 0, sizeof(sv));
394 sv.sa_handler = SIG_DFL;
395 sigemptyset( &sv.sa_mask );
397 sigaction(SIGALRM, &sv, NULL );
398 sigaction(SIGHUP, &sv, NULL );
399 sigaction(SIGTERM, &sv, NULL );
400 sigaction(SIGUSR1, &sv, NULL );
401 sigaction(SIGCHLD, &sv, NULL );
404 sigaddset(&sigs, SIGALRM);
405 sigaddset(&sigs, SIGHUP);
406 sigaddset(&sigs, SIGUSR1);
407 sigaddset(&sigs, SIGCHLD);
408 pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);