1/* Copyright (C) 2002-2018 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <http://www.gnu.org/licenses/>. */
18
19#include <errno.h>
20#include <stdlib.h>
21#include <string.h>
22#include <fork.h>
23#include <atomic.h>
24
25#define DYNARRAY_ELEMENT struct fork_handler
26#define DYNARRAY_STRUCT fork_handler_list
27#define DYNARRAY_PREFIX fork_handler_list_
28#define DYNARRAY_INITIAL_SIZE 48
29#include <malloc/dynarray-skeleton.c>
30
31static struct fork_handler_list fork_handlers;
32static bool fork_handler_init = false;
33
34static int atfork_lock = LLL_LOCK_INITIALIZER;
35
36int
37__register_atfork (void (*prepare) (void), void (*parent) (void),
38 void (*child) (void), void *dso_handle)
39{
40 lll_lock (atfork_lock, LLL_PRIVATE);
41
42 if (!fork_handler_init)
43 {
44 fork_handler_list_init (&fork_handlers);
45 fork_handler_init = true;
46 }
47
48 struct fork_handler *newp = fork_handler_list_emplace (&fork_handlers);
49 if (newp != NULL)
50 {
51 newp->prepare_handler = prepare;
52 newp->parent_handler = parent;
53 newp->child_handler = child;
54 newp->dso_handle = dso_handle;
55 }
56
57 /* Release the lock. */
58 lll_unlock (atfork_lock, LLL_PRIVATE);
59
60 return newp == NULL ? ENOMEM : 0;
61}
62libc_hidden_def (__register_atfork)
63
64static struct fork_handler *
65fork_handler_list_find (struct fork_handler_list *fork_handlers,
66 void *dso_handle)
67{
68 for (size_t i = 0; i < fork_handler_list_size (fork_handlers); i++)
69 {
70 struct fork_handler *elem = fork_handler_list_at (fork_handlers, i);
71 if (elem->dso_handle == dso_handle)
72 return elem;
73 }
74 return NULL;
75}
76
77void
78__unregister_atfork (void *dso_handle)
79{
80 lll_lock (atfork_lock, LLL_PRIVATE);
81
82 struct fork_handler *first = fork_handler_list_find (&fork_handlers,
83 dso_handle);
84 /* Removing is done by shifting the elements in the way the elements
85 that are not to be removed appear in the beginning in dynarray.
86 This avoid the quadradic run-time if a naive strategy to remove and
87 shift one element at time. */
88 if (first != NULL)
89 {
90 struct fork_handler *new_end = first;
91 first++;
92 for (; first != fork_handler_list_end (&fork_handlers); ++first)
93 {
94 if (first->dso_handle != dso_handle)
95 {
96 *new_end = *first;
97 ++new_end;
98 }
99 }
100
101 ptrdiff_t removed = first - new_end;
102 for (size_t i = 0; i < removed; i++)
103 fork_handler_list_remove_last (&fork_handlers);
104 }
105
106 lll_unlock (atfork_lock, LLL_PRIVATE);
107}
108
109void
110__run_fork_handlers (enum __run_fork_handler_type who, _Bool do_locking)
111{
112 struct fork_handler *runp;
113
114 if (who == atfork_run_prepare)
115 {
116 if (do_locking)
117 lll_lock (atfork_lock, LLL_PRIVATE);
118 size_t sl = fork_handler_list_size (&fork_handlers);
119 for (size_t i = sl; i > 0; i--)
120 {
121 runp = fork_handler_list_at (&fork_handlers, i - 1);
122 if (runp->prepare_handler != NULL)
123 runp->prepare_handler ();
124 }
125 }
126 else
127 {
128 size_t sl = fork_handler_list_size (&fork_handlers);
129 for (size_t i = 0; i < sl; i++)
130 {
131 runp = fork_handler_list_at (&fork_handlers, i);
132 if (who == atfork_run_child && runp->child_handler)
133 runp->child_handler ();
134 else if (who == atfork_run_parent && runp->parent_handler)
135 runp->parent_handler ();
136 }
137 if (do_locking)
138 lll_unlock (atfork_lock, LLL_PRIVATE);
139 }
140}
141
142
143libc_freeres_fn (free_mem)
144{
145 lll_lock (atfork_lock, LLL_PRIVATE);
146
147 fork_handler_list_free (&fork_handlers);
148
149 lll_unlock (atfork_lock, LLL_PRIVATE);
150}
151