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.
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 */
24 #include <sys/types.h>
29 #include <atalk/logger.h>
30 #include <atalk/errchk.h>
31 #include <atalk/util.h>
32 #include <atalk/server_child.h>
35 #define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
36 #endif /* ! WEXITSTATUS */
38 #define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
39 #endif /* ! WIFEXITED */
41 #define WIFSTOPPED(status) (((status) & 0xff) == 0x7f)
44 #define WIFSIGNALED(status) (!WIFSTOPPED(status) && !WIFEXITED(status))
47 #define WTERMSIG(status) ((status) & 0x7f)
50 /* hash/child functions: hash OR's pid */
51 #define HASH(i) ((((i) >> 8) ^ (i)) & (CHILD_HASHSIZE - 1))
53 static inline void hash_child(afp_child_t **htable, afp_child_t *child)
57 table = &htable[HASH(child->afpch_pid)];
58 if ((child->afpch_next = *table) != NULL)
59 (*table)->afpch_prevp = &child->afpch_next;
61 child->afpch_prevp = table;
64 static inline void unhash_child(afp_child_t *child)
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;
73 afp_child_t *server_child_resolve(server_child_t *childs, id_t pid)
77 for (child = childs->servch_table[HASH(pid)]; child; child = child->afpch_next) {
78 if (child->afpch_pid == pid)
85 /* initialize server_child structure */
86 server_child_t *server_child_alloc(int connections)
88 server_child_t *children;
90 if (!(children = (server_child_t *)calloc(1, sizeof(server_child_t))))
93 children->servch_nsessions = connections;
94 pthread_mutex_init(&children->servch_lock, NULL);
100 * @return pointer to struct server_child_data on success, NULL on error
102 afp_child_t *server_child_add(server_child_t *children, pid_t pid, int ipc_fd)
104 afp_child_t *child = NULL;
106 pthread_mutex_lock(&children->servch_lock);
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);
115 /* if we already have an entry. just return. */
116 if ((child = server_child_resolve(children, pid)))
119 if ((child = calloc(1, sizeof(afp_child_t))) == NULL)
122 child->afpch_pid = pid;
123 child->afpch_ipc_fd = ipc_fd;
124 child->afpch_logintime = time(NULL);
126 hash_child(children->servch_table, child);
127 children->servch_count++;
130 pthread_mutex_unlock(&children->servch_lock);
134 /* remove a child and free it */
135 int server_child_remove(server_child_t *children, pid_t pid)
140 if (!(child = server_child_resolve(children, pid)))
143 pthread_mutex_lock(&children->servch_lock);
146 if (child->afpch_clientid) {
147 free(child->afpch_clientid);
148 child->afpch_clientid = NULL;
151 /* In main:child_handler() we need the fd in order to remove it from the pollfd set */
152 fd = child->afpch_ipc_fd;
157 children->servch_count--;
159 pthread_mutex_unlock(&children->servch_lock);
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 */
166 void server_child_free(server_child_t *children)
168 afp_child_t *child, *tmp;
171 for (j = 0; j < CHILD_HASHSIZE; j++) {
172 child = children->servch_table[j]; /* start at the beginning */
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);
188 /* send signal to all child processes */
189 void server_child_kill(server_child_t *children, int sig)
191 afp_child_t *child, *tmp;
194 for (i = 0; i < CHILD_HASHSIZE; i++) {
195 child = children->servch_table[i];
197 tmp = child->afpch_next;
198 kill(child->afpch_pid, sig);
204 /* send kill to a child processes */
205 static int kill_child(afp_child_t *child)
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;
213 LOG(log_info, logtype_default, "Unresponsive child[%d], sending SIGKILL", child->afpch_pid);
214 kill(child->afpch_pid, SIGKILL);
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
223 int server_child_transfer_session(server_child_t *children,
227 uint16_t DSI_requestID)
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);
238 if (kill(pid, 0) == 0) {
239 LOG(log_error, logtype_default, "Reconnect: killing old session[%u]", pid);
247 if (!child->afpch_valid) {
248 /* hmm, client 'guess' the pid, rogue? */
249 LOG(log_error, logtype_default, "Reconnect: invalidated child[%u]", pid);
251 } else if (child->afpch_uid != uid) {
252 LOG(log_error, logtype_default, "Reconnect: child[%u] not the same user", pid);
256 LOG(log_note, logtype_default, "Reconnect: transfering session to child[%u]", pid);
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);
263 EC_ZERO_LOG(send_fd(child->afpch_ipc_fd, afp_socket));
264 EC_ZERO_LOG(kill(pid, SIGURG));
273 /* see if there is a process for the same mac */
274 /* if the times don't match mac has been rebooted */
275 void 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)
278 afp_child_t *child, *tmp;
281 pthread_mutex_lock(&children->servch_lock);
283 for (i = 0; i < CHILD_HASHSIZE; i++) {
284 child = children->servch_table[i];
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) {
293 LOG(log_warning, logtype_default,
294 "Terminated disconnected child[%u], client rebooted.",
297 LOG(log_warning, logtype_default,
298 "Session with different pid[%u]", child->afpch_pid);
301 /* One client with multiple sessions */
302 LOG(log_debug, logtype_default,
303 "Found another session[%u] for client[%u]", child->afpch_pid, pid);
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;
321 pthread_mutex_unlock(&children->servch_lock);
324 /* ---------------------------
325 * reset children signals
327 void server_reset_signal(void)
331 const struct itimerval none = {{0, 0}, {0, 0}};
333 setitimer(ITIMER_REAL, &none, NULL);
334 memset(&sv, 0, sizeof(sv));
335 sv.sa_handler = SIG_DFL;
336 sigemptyset( &sv.sa_mask );
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 );
345 sigaddset(&sigs, SIGALRM);
346 sigaddset(&sigs, SIGHUP);
347 sigaddset(&sigs, SIGUSR1);
348 sigaddset(&sigs, SIGCHLD);
349 pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);