X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=netatalk.git;a=blobdiff_plain;f=libatalk%2Futil%2Fserver_child.c;h=06d944f6455d86e7253147094446d0e3b1d82add;hp=7e26eb0332715f875ca64fda77d7dac9bc26aefe;hb=9d78f9b52ea18b442fc91c6a1e8d8b607f2f90e8;hpb=458fcac4e6aee7eb54ba744f57169f70cce12505 diff --git a/libatalk/util/server_child.c b/libatalk/util/server_child.c index 7e26eb03..06d944f6 100644 --- a/libatalk/util/server_child.c +++ b/libatalk/util/server_child.c @@ -1,4 +1,6 @@ /* + * $Id: server_child.c,v 1.7.4.1.2.4.2.2 2008-11-25 15:16:35 didg Exp $ + * * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu) * All rights reserved. See COPYRIGHT. * @@ -14,28 +16,54 @@ #ifdef HAVE_CONFIG_H #include "config.h" -#endif +#endif /* HAVE_CONFIG_H */ #include #include +#ifdef HAVE_UNISTD_H #include +#endif /* HAVE_UNISTD_H */ #include -#include +#include + +/* POSIX.1 sys/wait.h check */ #include +#ifdef HAVE_SYS_WAIT_H #include +#endif /* HAVE_SYS_WAIT_H */ +#include + +#ifndef WEXITSTATUS +#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) +#endif /* ! WEXITSTATUS */ +#ifndef WIFEXITED +#define WIFEXITED(stat_val) (((stat_val) & 255) == 0) +#endif /* ! WIFEXITED */ +#ifndef WIFSTOPPED +#define WIFSTOPPED(status) (((status) & 0xff) == 0x7f) +#endif +#ifndef WIFSIGNALED +#define WIFSIGNALED(status) (!WIFSTOPPED(status) && !WIFEXITED(status)) +#endif +#ifndef WTERMSIG +#define WTERMSIG(status) ((status) & 0x7f) +#endif #include -#ifndef __inline__ -#define __inline__ -#endif - /* hash/child functions: hash OR's pid */ #define CHILD_HASHSIZE 32 #define HASH(i) ((((i) >> 8) ^ (i)) & (CHILD_HASHSIZE - 1)) struct server_child_data { - pid_t pid; + pid_t pid; /* afpd worker process pid (from the worker afpd process )*/ + uid_t uid; /* user id of connected client (from the worker afpd process) */ + int valid; /* 1 if we have a clientid */ + u_int32_t time; /* client boot time (from the mac client) */ + int killed; /* 1 if we already tried to kill the client */ + + u_int32_t idlen; /* clientid len (from the Mac client) */ + char *clientid; /* clientid (from the Mac client) */ struct server_child_data **prevp, *next; }; @@ -45,7 +73,7 @@ typedef struct server_child_fork { } server_child_fork; -static __inline__ void hash_child(struct server_child_data **htable, +static inline void hash_child(struct server_child_data **htable, struct server_child_data *child) { struct server_child_data **table; @@ -57,7 +85,7 @@ static __inline__ void hash_child(struct server_child_data **htable, child->prevp = table; } -static __inline__ void unhash_child(struct server_child_data *child) +static inline void unhash_child(struct server_child_data *child) { if (child->prevp) { if (child->next) @@ -66,7 +94,7 @@ static __inline__ void unhash_child(struct server_child_data *child) } } -static __inline__ struct server_child_data +static inline struct server_child_data *resolve_child(struct server_child_data **table, const pid_t pid) { struct server_child_data *child; @@ -107,18 +135,18 @@ int server_child_add(server_child *children, const int forkid, { server_child_fork *fork; struct server_child_data *child; - sigset_t sig; + sigset_t sig, oldsig; /* we need to prevent deletions from occuring before we get a * chance to add the child in. */ sigemptyset(&sig); sigaddset(&sig, SIGCHLD); - sigprocmask(SIG_BLOCK, &sig, NULL); + sigprocmask(SIG_BLOCK, &sig, &oldsig); /* it's possible that the child could have already died before the * sigprocmask. we need to check for this. */ if (kill(pid, 0) < 0) { - sigprocmask(SIG_UNBLOCK, &sig, NULL); + sigprocmask(SIG_SETMASK, &oldsig, NULL); return 1; } @@ -126,20 +154,22 @@ int server_child_add(server_child *children, const int forkid, /* if we already have an entry. just return. */ if (resolve_child(fork->table, pid)) { - sigprocmask(SIG_UNBLOCK, &sig, NULL); + sigprocmask(SIG_SETMASK, &oldsig, NULL); return 0; } if ((child = (struct server_child_data *) calloc(1, sizeof(struct server_child_data))) == NULL) { - sigprocmask(SIG_UNBLOCK, &sig, NULL); + sigprocmask(SIG_SETMASK, &oldsig, NULL); return -1; } child->pid = pid; + child->valid = 0; + child->killed = 0; hash_child(fork->table, child); children->count++; - sigprocmask(SIG_UNBLOCK, &sig, NULL); + sigprocmask(SIG_SETMASK, &oldsig, NULL); return 0; } @@ -156,6 +186,9 @@ int server_child_remove(server_child *children, const int forkid, return 0; unhash_child(child); + if (child->clientid) { + free(child->clientid); + } free(child); children->count--; return 1; @@ -175,6 +208,9 @@ void server_child_free(server_child *children) child = fork->table[j]; /* start at the beginning */ while (child) { tmp = child->next; + if (child->clientid) { + free(child->clientid); + } free(child); child = tmp; } @@ -204,6 +240,114 @@ void server_child_kill(server_child *children, const int forkid, } } +/* send kill to a child processes. + * a plain-old linked list + * FIXME use resolve_child ? + */ +static int kill_child(struct server_child_data *child) +{ + if (!child->killed) { + kill(child->pid, SIGTERM); + /* we don't wait because there's no guarantee that we can really kill it */ + child->killed = 1; + return 1; + } + else { + LOG(log_info, logtype_default, "Already tried to kill (%d) before! Still there?", child->pid); + } + return 0; +} + +/* -------------------- */ +void server_child_kill_one(server_child *children, const int forkid, const pid_t pid, const uid_t uid) +{ + server_child_fork *fork; + struct server_child_data *child, *tmp; + int i; + + fork = (server_child_fork *) children->fork + forkid; + for (i = 0; i < CHILD_HASHSIZE; i++) { + child = fork->table[i]; + while (child) { + tmp = child->next; + if (child->pid == pid) { + if (!child->valid) { + /* hmm, client 'guess' the pid, rogue? */ + LOG(log_info, logtype_default, "Disconnecting old session (%d) no token, bailout!", child->pid); + } + else if (child->uid != uid) { + LOG(log_info, logtype_default, "Disconnecting old session (%d) not the same user, bailout!", child->pid); + } + else { + kill_child(child); + } + } + child = tmp; + } + } +} + + +/* see if there is a process for the same mac */ +/* if the times don't match mac has been rebooted */ +void server_child_kill_one_by_id(server_child *children, const int forkid, const pid_t pid, + const uid_t uid, + const u_int32_t idlen, char *id, u_int32_t boottime) +{ + server_child_fork *fork; + struct server_child_data *child, *tmp; + int i; + + fork = (server_child_fork *) children->fork + forkid; + for (i = 0; i < CHILD_HASHSIZE; i++) { + child = fork->table[i]; + while (child) { + tmp = child->next; + if ( child->pid != pid) { + if ( child->idlen == idlen && !memcmp(child->clientid, id, idlen)) { + if ( child->time != boottime ) { + if (uid == child->uid) { + if (kill_child(child)) { + LOG(log_info, logtype_default, "Disconnecting old session %d, client rebooted.", child->pid); + } + } + else { + LOG(log_info, logtype_default, "Disconnecting old session not the same uid, bailout!"); + } + } + else if (child->killed) { + /* there's case where a Mac close a connection and restart a new one before + * the first is 'waited' by the master afpd process + */ + LOG(log_info, logtype_default, + "WARNING: connection (%d) killed but still there.", child->pid); + } + else { + LOG(log_info, logtype_default, + "WARNING: 2 connections (%d, %d), boottime identical, don't know if one needs to be disconnected.", + child->pid, pid); + } + + } + } + else + { + child->time = boottime; + /* free old token if any */ + if (child->clientid) { + free(child->clientid); + } + child->uid = uid; + child->valid = 1; + child->idlen = idlen; + child->clientid = id; + LOG(log_info, logtype_default, "Setting clientid (len %d) for %d, boottime %X", idlen, child->pid, boottime); + } + child = tmp; + } + } +} + /* for extra cleanup if necessary */ void server_child_setup(server_child *children, const int forkid, void (*fcn)(const pid_t)) @@ -218,36 +362,71 @@ void server_child_setup(server_child *children, const int forkid, /* keep track of children. */ void server_child_handler(server_child *children) { - server_child_fork *fork; int status, i; pid_t pid; #ifndef WAIT_ANY #define WAIT_ANY (-1) -#endif +#endif /* ! WAIT_ANY */ while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) { for (i = 0; i < children->nforks; i++) { if (server_child_remove(children, i, pid)) { + server_child_fork *fork; + fork = (server_child_fork *) children->fork + i; if (fork->cleanup) fork->cleanup(pid); break; } } - + if (WIFEXITED(status)) { if (WEXITSTATUS(status)) { - syslog(LOG_INFO, "server_child[%d] %d exited %d", i, pid, + LOG(log_info, logtype_default, "server_child[%d] %d exited %d", i, pid, WEXITSTATUS(status)); } else { - syslog(LOG_INFO, "server_child[%d] %d done", i, pid); + LOG(log_info, logtype_default, "server_child[%d] %d done", i, pid); } } else { - if (WIFSIGNALED(status)) - syslog(LOG_INFO, "server_child[%d] %d killed", i, pid); + if (WIFSIGNALED(status)) + { + LOG(log_info, logtype_default, "server_child[%d] %d killed by signal %d", i, pid, + WTERMSIG (status)); + } else - syslog(LOG_INFO, "server_child[%d] %d died", i, pid); + { + LOG(log_info, logtype_default, "server_child[%d] %d died", i, pid); + } } } } + +/* --------------------------- + * reset children signals +*/ +void server_reset_signal(void) +{ + struct sigaction sv; + sigset_t sigs; + const struct itimerval none = {{0, 0}, {0, 0}}; + + setitimer(ITIMER_REAL, &none, NULL); + memset(&sv, 0, sizeof(sv)); + sv.sa_handler = SIG_DFL; + sigemptyset( &sv.sa_mask ); + + sigaction(SIGALRM, &sv, 0 ); + sigaction(SIGHUP, &sv, 0 ); + sigaction(SIGTERM, &sv, 0 ); + sigaction(SIGUSR1, &sv, 0 ); + sigaction(SIGCHLD, &sv, 0 ); + + sigemptyset(&sigs); + sigaddset(&sigs, SIGALRM); + sigaddset(&sigs, SIGHUP); + sigaddset(&sigs, SIGUSR1); + sigaddset(&sigs, SIGCHLD); + sigprocmask(SIG_UNBLOCK, &sigs, NULL); + +}