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