2 * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
3 * All rights reserved. See COPYRIGHT.
6 * handle inserting, removing, and freeing of children.
7 * this does it via a hash table. it incurs some overhead over
8 * a linear append/remove in total removal and kills, but it makes
9 * single-entry removals a fast operation. as total removals occur during
10 * child initialization and kills during server shutdown, this is
11 * probably a win for a lot of connections and unimportant for a small
12 * number of connections.
17 #endif /* HAVE_CONFIG_H */
23 #endif /* HAVE_UNISTD_H */
27 /* POSIX.1 sys/wait.h check */
28 #include <sys/types.h>
29 #ifdef HAVE_SYS_WAIT_H
31 #endif /* HAVE_SYS_WAIT_H */
34 #include <atalk/logger.h>
35 #include <atalk/errchk.h>
36 #include <atalk/util.h>
37 #include <atalk/server_child.h>
40 #define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
41 #endif /* ! WEXITSTATUS */
43 #define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
44 #endif /* ! WIFEXITED */
46 #define WIFSTOPPED(status) (((status) & 0xff) == 0x7f)
49 #define WIFSIGNALED(status) (!WIFSTOPPED(status) && !WIFEXITED(status))
52 #define WTERMSIG(status) ((status) & 0x7f)
55 /* hash/child functions: hash OR's pid */
56 #define CHILD_HASHSIZE 32
57 #define HASH(i) ((((i) >> 8) ^ (i)) & (CHILD_HASHSIZE - 1))
59 typedef struct server_child_fork {
60 struct server_child_data *table[CHILD_HASHSIZE];
61 void (*cleanup)(const pid_t);
64 int parent_or_child; /* 0: parent, 1: child */
66 static inline void hash_child(struct server_child_data **htable,
67 struct server_child_data *child)
69 struct server_child_data **table;
71 table = &htable[HASH(child->pid)];
72 if ((child->next = *table) != NULL)
73 (*table)->prevp = &child->next;
78 static inline void unhash_child(struct server_child_data *child)
82 child->next->prevp = child->prevp;
83 *(child->prevp) = child->next;
87 static struct server_child_data *resolve_child(struct server_child_data **table, pid_t pid)
89 struct server_child_data *child;
91 for (child = table[HASH(pid)]; child; child = child->next) {
92 if (child->pid == pid)
99 /* initialize server_child structure */
100 server_child *server_child_alloc(const int connections, const int nforks)
102 server_child *children;
104 children = (server_child *) calloc(1, sizeof(server_child));
108 children->nsessions = connections;
109 children->nforks = nforks;
110 children->fork = (void *) calloc(nforks, sizeof(server_child_fork));
112 if (!children->fork) {
122 * @return pointer to struct server_child_data on success, NULL on error
124 afp_child_t *server_child_add(server_child *children, int forkid, pid_t pid, uint ipc_fds[2])
126 server_child_fork *fork;
127 afp_child_t *child = NULL;
128 sigset_t sig, oldsig;
130 /* we need to prevent deletions from occuring before we get a
131 * chance to add the child in. */
133 sigaddset(&sig, SIGCHLD);
134 pthread_sigmask(SIG_BLOCK, &sig, &oldsig);
136 /* it's possible that the child could have already died before the
137 * pthread_sigmask. we need to check for this. */
138 if (kill(pid, 0) < 0) {
139 LOG(log_error, logtype_default, "server_child_add: no such process pid [%d]", pid);
143 fork = (server_child_fork *) children->fork + forkid;
145 /* if we already have an entry. just return. */
146 if (child = resolve_child(fork->table, pid))
149 if ((child = calloc(1, sizeof(afp_child_t))) == NULL)
155 child->ipc_fds[0] = ipc_fds[0];
156 child->ipc_fds[1] = ipc_fds[1];
158 hash_child(fork->table, child);
162 pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
166 /* remove a child and free it */
167 int server_child_remove(server_child *children, const int forkid, pid_t pid)
170 server_child_fork *fork;
171 struct server_child_data *child;
173 fork = (server_child_fork *) children->fork + forkid;
174 if (!(child = resolve_child(fork->table, pid)))
178 if (child->clientid) {
179 free(child->clientid);
180 child->clientid = NULL;
183 /* In main:child_handler() we need the fd in order to remove it from the pollfd set */
184 fd = child->ipc_fds[0];
185 if (child->ipc_fds[0] != -1) {
186 close(child->ipc_fds[0]);
187 child->ipc_fds[0] = -1;
189 if (child->ipc_fds[1] != -1) {
190 close(child->ipc_fds[1]);
191 child->ipc_fds[1] = -1;
203 /* free everything: by using a hash table, this increases the cost of
204 * this part over a linked list by the size of the hash table */
205 void server_child_free(server_child *children)
207 server_child_fork *fork;
208 struct server_child_data *child, *tmp;
210 pid_t pid = getpid();
212 for (i = 0; i < children->nforks; i++) {
213 fork = (server_child_fork *) children->fork + i;
214 for (j = 0; j < CHILD_HASHSIZE; j++) {
215 child = fork->table[j]; /* start at the beginning */
219 if (child->ipc_fds[0] != -1)
220 close(child->ipc_fds[0]);
221 if (child->clientid) {
222 free(child->clientid);
229 free(children->fork);
233 /* send signal to all child processes */
234 void server_child_kill(server_child *children, int forkid, int sig)
236 server_child_fork *fork;
237 struct server_child_data *child, *tmp;
240 fork = (server_child_fork *) children->fork + forkid;
241 for (i = 0; i < CHILD_HASHSIZE; i++) {
242 child = fork->table[i];
245 kill(child->pid, sig);
251 /* send kill to a child processes.
252 * a plain-old linked list
253 * FIXME use resolve_child ?
255 static int kill_child(struct server_child_data *child)
257 if (!child->killed) {
258 kill(child->pid, SIGTERM);
259 /* we don't wait because there's no guarantee that we can really kill it */
263 LOG(log_info, logtype_default, "Unresponsive child[%d], sending SIGKILL", child->pid);
264 kill(child->pid, SIGKILL);
270 * Try to find an old session and pass socket
271 * @returns -1 on error, 0 if no matching session was found, 1 if session was found and socket passed
273 int server_child_transfer_session(server_child *children,
278 uint16_t DSI_requestID)
281 server_child_fork *fork;
282 struct server_child_data *child;
285 fork = (server_child_fork *) children->fork + forkid;
286 if ((child = resolve_child(fork->table, pid)) == NULL) {
287 LOG(log_note, logtype_default, "Reconnect: no child[%u]", pid);
288 if (kill(pid, 0) == 0) {
289 LOG(log_note, logtype_default, "Reconnect: terminating old session[%u]", pid);
292 if (kill(pid, 0) == 0) {
293 LOG(log_error, logtype_default, "Reconnect: killing old session[%u]", pid);
302 /* hmm, client 'guess' the pid, rogue? */
303 LOG(log_error, logtype_default, "Reconnect: invalidated child[%u]", pid);
305 } else if (child->uid != uid) {
306 LOG(log_error, logtype_default, "Reconnect: child[%u] not the same user", pid);
310 LOG(log_note, logtype_default, "Reconnect: transfering session to child[%u]", pid);
312 if (writet(child->ipc_fds[0], &DSI_requestID, 2, 0, 2) != 2) {
313 LOG(log_error, logtype_default, "Reconnect: error sending DSI id to child[%u]", pid);
317 EC_ZERO_LOG(send_fd(child->ipc_fds[0], afp_socket));
318 EC_ZERO_LOG(kill(pid, SIGURG));
327 /* see if there is a process for the same mac */
328 /* if the times don't match mac has been rebooted */
329 void server_child_kill_one_by_id(server_child *children, int forkid, pid_t pid,
330 uid_t uid, uint32_t idlen, char *id, uint32_t boottime)
332 server_child_fork *fork;
333 struct server_child_data *child, *tmp;
336 fork = (server_child_fork *)children->fork + forkid;
338 for (i = 0; i < CHILD_HASHSIZE; i++) {
339 child = fork->table[i];
342 if ( child->pid != pid) {
343 if (child->idlen == idlen && memcmp(child->clientid, id, idlen) == 0) {
344 if ( child->time != boottime ) {
345 /* Client rebooted */
346 if (uid == child->uid) {
348 LOG(log_warning, logtype_default,
349 "Terminated disconnected child[%u], client rebooted.",
352 LOG(log_warning, logtype_default,
353 "Session with different pid[%u]", child->pid);
356 /* One client with multiple sessions */
357 LOG(log_debug, logtype_default,
358 "Found another session[%u] for client[%u]", child->pid, pid);
362 /* update childs own slot */
363 child->time = boottime;
365 free(child->clientid);
366 LOG(log_debug, logtype_default, "Setting client ID for %u", child->pid);
369 child->idlen = idlen;
370 child->clientid = id;
377 /* for extra cleanup if necessary */
378 void server_child_setup(server_child *children, const int forkid,
379 void (*fcn)(const pid_t))
381 server_child_fork *fork;
383 fork = (server_child_fork *) children->fork + forkid;
388 /* ---------------------------
389 * reset children signals
391 void server_reset_signal(void)
395 const struct itimerval none = {{0, 0}, {0, 0}};
397 setitimer(ITIMER_REAL, &none, NULL);
398 memset(&sv, 0, sizeof(sv));
399 sv.sa_handler = SIG_DFL;
400 sigemptyset( &sv.sa_mask );
402 sigaction(SIGALRM, &sv, NULL );
403 sigaction(SIGHUP, &sv, NULL );
404 sigaction(SIGTERM, &sv, NULL );
405 sigaction(SIGUSR1, &sv, NULL );
406 sigaction(SIGCHLD, &sv, NULL );
409 sigaddset(&sigs, SIGALRM);
410 sigaddset(&sigs, SIGHUP);
411 sigaddset(&sigs, SIGUSR1);
412 sigaddset(&sigs, SIGCHLD);
413 pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);