• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/netatalk-3.0.5/libatalk/util/
1/*
2 * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
3 * Copyright (c) 2013 Frank Lahm <franklahm@gmail.com
4 * All rights reserved. See COPYRIGHT.
5 *
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.
13 */
14
15#ifdef HAVE_CONFIG_H
16#include "config.h"
17#endif /* HAVE_CONFIG_H */
18
19#include <stdlib.h>
20#include <string.h>
21#include <unistd.h>
22#include <signal.h>
23#include <errno.h>
24#include <sys/types.h>
25#include <sys/wait.h>
26#include <sys/time.h>
27#include <pthread.h>
28
29#include <atalk/logger.h>
30#include <atalk/errchk.h>
31#include <atalk/util.h>
32#include <atalk/server_child.h>
33
34#ifndef WEXITSTATUS
35#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
36#endif /* ! WEXITSTATUS */
37#ifndef WIFEXITED
38#define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
39#endif /* ! WIFEXITED */
40#ifndef WIFSTOPPED
41#define WIFSTOPPED(status) (((status) & 0xff) == 0x7f)
42#endif
43#ifndef WIFSIGNALED
44#define WIFSIGNALED(status) (!WIFSTOPPED(status) && !WIFEXITED(status))
45#endif
46#ifndef WTERMSIG
47#define WTERMSIG(status)      ((status) & 0x7f)
48#endif
49
50/* hash/child functions: hash OR's pid */
51#define HASH(i) ((((i) >> 8) ^ (i)) & (CHILD_HASHSIZE - 1))
52
53static inline void hash_child(afp_child_t **htable, afp_child_t *child)
54{
55    afp_child_t **table;
56
57    table = &htable[HASH(child->afpch_pid)];
58    if ((child->afpch_next = *table) != NULL)
59        (*table)->afpch_prevp = &child->afpch_next;
60    *table = child;
61    child->afpch_prevp = table;
62}
63
64static inline void unhash_child(afp_child_t *child)
65{
66    if (child->afpch_prevp) {
67        if (child->afpch_next)
68            child->afpch_next->afpch_prevp = child->afpch_prevp;
69        *(child->afpch_prevp) = child->afpch_next;
70    }
71}
72
73afp_child_t *server_child_resolve(server_child_t *childs, id_t pid)
74{
75    afp_child_t *child;
76
77    for (child = childs->servch_table[HASH(pid)]; child; child = child->afpch_next) {
78        if (child->afpch_pid == pid)
79            break;
80    }
81
82    return child;
83}
84
85/* initialize server_child structure */
86server_child_t *server_child_alloc(int connections)
87{
88    server_child_t *children;
89
90    if (!(children = (server_child_t *)calloc(1, sizeof(server_child_t))))
91        return NULL;
92
93    children->servch_nsessions = connections;
94    pthread_mutex_init(&children->servch_lock, NULL);
95    return children;
96}
97
98/*!
99 * add a child
100 * @return pointer to struct server_child_data on success, NULL on error
101 */
102afp_child_t *server_child_add(server_child_t *children, pid_t pid, int ipc_fd)
103{
104    afp_child_t *child = NULL;
105
106    pthread_mutex_lock(&children->servch_lock);
107
108    /* it's possible that the child could have already died before the
109     * pthread_sigmask. we need to check for this. */
110    if (kill(pid, 0) < 0) {
111        LOG(log_error, logtype_default, "server_child_add: no such process pid [%d]", pid);
112        goto exit;
113    }
114
115    /* if we already have an entry. just return. */
116    if ((child = server_child_resolve(children, pid)))
117        goto exit;
118
119    if ((child = calloc(1, sizeof(afp_child_t))) == NULL)
120        goto exit;
121
122    child->afpch_pid = pid;
123    child->afpch_ipc_fd = ipc_fd;
124    child->afpch_logintime = time(NULL);
125
126    hash_child(children->servch_table, child);
127    children->servch_count++;
128
129exit:
130    pthread_mutex_unlock(&children->servch_lock);
131    return child;
132}
133
134/* remove a child and free it */
135int server_child_remove(server_child_t *children, pid_t pid)
136{
137    int fd;
138    afp_child_t *child;
139
140    if (!(child = server_child_resolve(children, pid)))
141        return -1;
142
143    pthread_mutex_lock(&children->servch_lock);
144
145    unhash_child(child);
146    if (child->afpch_clientid) {
147        free(child->afpch_clientid);
148        child->afpch_clientid = NULL;
149    }
150
151    /* In main:child_handler() we need the fd in order to remove it from the pollfd set */
152    fd = child->afpch_ipc_fd;
153    if (fd != -1)
154        close(fd);
155
156    free(child);
157    children->servch_count--;
158
159    pthread_mutex_unlock(&children->servch_lock);
160
161    return fd;
162}
163
164/* free everything: by using a hash table, this increases the cost of
165 * this part over a linked list by the size of the hash table */
166void server_child_free(server_child_t *children)
167{
168    afp_child_t *child, *tmp;
169    int j;
170
171    for (j = 0; j < CHILD_HASHSIZE; j++) {
172        child = children->servch_table[j]; /* start at the beginning */
173        while (child) {
174            tmp = child->afpch_next;
175            close(child->afpch_ipc_fd);
176            if (child->afpch_clientid)
177                free(child->afpch_clientid);
178            if (child->afpch_volumes)
179                free(child->afpch_volumes);
180            free(child);
181            child = tmp;
182        }
183    }
184
185    free(children);
186}
187
188/* send signal to all child processes */
189void server_child_kill(server_child_t *children, int sig)
190{
191    afp_child_t *child, *tmp;
192    int i;
193
194    for (i = 0; i < CHILD_HASHSIZE; i++) {
195        child = children->servch_table[i];
196        while (child) {
197            tmp = child->afpch_next;
198            kill(child->afpch_pid, sig);
199            child = tmp;
200        }
201    }
202}
203
204/* send kill to a child processes */
205static int kill_child(afp_child_t *child)
206{
207    if (!child->afpch_killed) {
208        kill(child->afpch_pid, SIGTERM);
209        /* we don't wait because there's no guarantee that we can really kill it */
210        child->afpch_killed = 1;
211        return 1;
212    } else {
213        LOG(log_info, logtype_default, "Unresponsive child[%d], sending SIGKILL", child->afpch_pid);
214        kill(child->afpch_pid, SIGKILL);
215    }
216    return 1;
217}
218
219/*!
220 * Try to find an old session and pass socket
221 * @returns -1 on error, 0 if no matching session was found, 1 if session was found and socket passed
222 */
223int server_child_transfer_session(server_child_t *children,
224                                  pid_t pid,
225                                  uid_t uid,
226                                  int afp_socket,
227                                  uint16_t DSI_requestID)
228{
229    EC_INIT;
230    afp_child_t *child;
231
232    if ((child = server_child_resolve(children, pid)) == NULL) {
233        LOG(log_note, logtype_default, "Reconnect: no child[%u]", pid);
234        if (kill(pid, 0) == 0) {
235            LOG(log_note, logtype_default, "Reconnect: terminating old session[%u]", pid);
236            kill(pid, SIGTERM);
237            sleep(2);
238            if (kill(pid, 0) == 0) {
239                LOG(log_error, logtype_default, "Reconnect: killing old session[%u]", pid);
240                kill(pid, SIGKILL);
241                sleep(2);
242            }
243        }
244        return 0;
245    }
246
247    if (!child->afpch_valid) {
248        /* hmm, client 'guess' the pid, rogue? */
249        LOG(log_error, logtype_default, "Reconnect: invalidated child[%u]", pid);
250        return 0;
251    } else if (child->afpch_uid != uid) {
252        LOG(log_error, logtype_default, "Reconnect: child[%u] not the same user", pid);
253        return 0;
254    }
255
256    LOG(log_note, logtype_default, "Reconnect: transfering session to child[%u]", pid);
257
258    if (writet(child->afpch_ipc_fd, &DSI_requestID, 2, 0, 2) != 2) {
259        LOG(log_error, logtype_default, "Reconnect: error sending DSI id to child[%u]", pid);
260        EC_STATUS(-1);
261        goto EC_CLEANUP;
262    }
263    EC_ZERO_LOG(send_fd(child->afpch_ipc_fd, afp_socket));
264    EC_ZERO_LOG(kill(pid, SIGURG));
265
266    EC_STATUS(1);
267
268EC_CLEANUP:
269    EC_EXIT;
270}
271
272
273/* see if there is a process for the same mac     */
274/* if the times don't match mac has been rebooted */
275void server_child_kill_one_by_id(server_child_t *children, pid_t pid,
276                                 uid_t uid, uint32_t idlen, char *id, uint32_t boottime)
277{
278    afp_child_t *child, *tmp;
279    int i;
280
281    pthread_mutex_lock(&children->servch_lock);
282
283    for (i = 0; i < CHILD_HASHSIZE; i++) {
284        child = children->servch_table[i];
285        while (child) {
286            tmp = child->afpch_next;
287            if (child->afpch_pid != pid) {
288                if (child->afpch_idlen == idlen && memcmp(child->afpch_clientid, id, idlen) == 0) {
289                    if ( child->afpch_boottime != boottime ) {
290                        /* Client rebooted */
291                        if (uid == child->afpch_uid) {
292                            kill_child(child);
293                            LOG(log_warning, logtype_default,
294                                "Terminated disconnected child[%u], client rebooted.",
295                                child->afpch_pid);
296                        } else {
297                            LOG(log_warning, logtype_default,
298                                "Session with different pid[%u]", child->afpch_pid);
299                        }
300                    } else {
301                        /* One client with multiple sessions */
302                        LOG(log_debug, logtype_default,
303                            "Found another session[%u] for client[%u]", child->afpch_pid, pid);
304                    }
305                }
306            } else {
307                /* update childs own slot */
308                child->afpch_boottime = boottime;
309                if (child->afpch_clientid)
310                    free(child->afpch_clientid);
311                LOG(log_debug, logtype_default, "Setting client ID for %u", child->afpch_pid);
312                child->afpch_uid = uid;
313                child->afpch_valid = 1;
314                child->afpch_idlen = idlen;
315                child->afpch_clientid = id;
316            }
317            child = tmp;
318        }
319    }
320
321    pthread_mutex_unlock(&children->servch_lock);
322}
323
324/* ---------------------------
325 * reset children signals
326 */
327void server_reset_signal(void)
328{
329    struct sigaction    sv;
330    sigset_t            sigs;
331    const struct itimerval none = {{0, 0}, {0, 0}};
332
333    setitimer(ITIMER_REAL, &none, NULL);
334    memset(&sv, 0, sizeof(sv));
335    sv.sa_handler =  SIG_DFL;
336    sigemptyset( &sv.sa_mask );
337
338    sigaction(SIGALRM, &sv, NULL );
339    sigaction(SIGHUP,  &sv, NULL );
340    sigaction(SIGTERM, &sv, NULL );
341    sigaction(SIGUSR1, &sv, NULL );
342    sigaction(SIGCHLD, &sv, NULL );
343
344    sigemptyset(&sigs);
345    sigaddset(&sigs, SIGALRM);
346    sigaddset(&sigs, SIGHUP);
347    sigaddset(&sigs, SIGUSR1);
348    sigaddset(&sigs, SIGCHLD);
349    pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
350
351}
352