]> git.proxmox.com Git - libgit2.git/blob - src/global.c
Merge pull request #2710 from Therzok/shellcheckScripts
[libgit2.git] / src / global.c
1 /*
2 * Copyright (C) the libgit2 contributors. All rights reserved.
3 *
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
6 */
7 #include "common.h"
8 #include "global.h"
9 #include "hash.h"
10 #include "sysdir.h"
11 #include "git2/threads.h"
12 #include "git2/global.h"
13 #include "thread-utils.h"
14
15
16 git_mutex git__mwindow_mutex;
17
18 #define MAX_SHUTDOWN_CB 8
19
20 #ifdef GIT_SSL
21 # include <openssl/ssl.h>
22 SSL_CTX *git__ssl_ctx;
23 # ifdef GIT_THREADS
24 static git_mutex *openssl_locks;
25 # endif
26 #endif
27
28 static git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB];
29 static git_atomic git__n_shutdown_callbacks;
30 static git_atomic git__n_inits;
31
32 void git__on_shutdown(git_global_shutdown_fn callback)
33 {
34 int count = git_atomic_inc(&git__n_shutdown_callbacks);
35 assert(count <= MAX_SHUTDOWN_CB && count > 0);
36 git__shutdown_callbacks[count - 1] = callback;
37 }
38
39 static void git__shutdown(void)
40 {
41 int pos;
42
43 for (pos = git_atomic_get(&git__n_shutdown_callbacks); pos > 0; pos = git_atomic_dec(&git__n_shutdown_callbacks)) {
44 git_global_shutdown_fn cb = git__swap(git__shutdown_callbacks[pos - 1], NULL);
45 if (cb != NULL)
46 cb();
47 }
48
49 }
50
51 #if defined(GIT_THREADS) && defined(GIT_SSL)
52 void openssl_locking_function(int mode, int n, const char *file, int line)
53 {
54 int lock;
55
56 GIT_UNUSED(file);
57 GIT_UNUSED(line);
58
59 lock = mode & CRYPTO_LOCK;
60
61 if (lock) {
62 git_mutex_lock(&openssl_locks[n]);
63 } else {
64 git_mutex_unlock(&openssl_locks[n]);
65 }
66 }
67
68 static void shutdown_ssl_locking(void)
69 {
70 int num_locks, i;
71
72 num_locks = CRYPTO_num_locks();
73 CRYPTO_set_locking_callback(NULL);
74
75 for (i = 0; i < num_locks; ++i)
76 git_mutex_free(openssl_locks);
77 git__free(openssl_locks);
78 }
79 #endif
80
81 static void init_ssl(void)
82 {
83 #ifdef GIT_SSL
84 long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
85
86 /* Older OpenSSL and MacOS OpenSSL doesn't have this */
87 #ifdef SSL_OP_NO_COMPRESSION
88 ssl_opts |= SSL_OP_NO_COMPRESSION;
89 #endif
90
91 SSL_load_error_strings();
92 OpenSSL_add_ssl_algorithms();
93 /*
94 * Load SSLv{2,3} and TLSv1 so that we can talk with servers
95 * which use the SSL hellos, which are often used for
96 * compatibility. We then disable SSL so we only allow OpenSSL
97 * to speak TLSv1 to perform the encryption itself.
98 */
99 git__ssl_ctx = SSL_CTX_new(SSLv23_method());
100 SSL_CTX_set_options(git__ssl_ctx, ssl_opts);
101 SSL_CTX_set_mode(git__ssl_ctx, SSL_MODE_AUTO_RETRY);
102 SSL_CTX_set_verify(git__ssl_ctx, SSL_VERIFY_NONE, NULL);
103 if (!SSL_CTX_set_default_verify_paths(git__ssl_ctx)) {
104 SSL_CTX_free(git__ssl_ctx);
105 git__ssl_ctx = NULL;
106 }
107 #endif
108 }
109
110 int git_openssl_set_locking(void)
111 {
112 #ifdef GIT_SSL
113 # ifdef GIT_THREADS
114 int num_locks, i;
115
116 num_locks = CRYPTO_num_locks();
117 openssl_locks = git__calloc(num_locks, sizeof(git_mutex));
118 GITERR_CHECK_ALLOC(openssl_locks);
119
120 for (i = 0; i < num_locks; i++) {
121 if (git_mutex_init(&openssl_locks[i]) != 0) {
122 giterr_set(GITERR_SSL, "failed to initialize openssl locks");
123 return -1;
124 }
125 }
126
127 CRYPTO_set_locking_callback(openssl_locking_function);
128 git__on_shutdown(shutdown_ssl_locking);
129 return 0;
130 # else
131 giterr_set(GITERR_THREAD, "libgit2 as not built with threads");
132 return -1;
133 # endif
134 giterr_set(GITERR_SSL, "libgit2 was not built with OpenSSL support");
135 return -1;
136 #endif
137 }
138
139 /**
140 * Handle the global state with TLS
141 *
142 * If libgit2 is built with GIT_THREADS enabled,
143 * the `git_libgit2_init()` function must be called
144 * before calling any other function of the library.
145 *
146 * This function allocates a TLS index (using pthreads
147 * or the native Win32 API) to store the global state
148 * on a per-thread basis.
149 *
150 * Any internal method that requires global state will
151 * then call `git__global_state()` which returns a pointer
152 * to the global state structure; this pointer is lazily
153 * allocated on each thread.
154 *
155 * Before shutting down the library, the
156 * `git_libgit2_shutdown` method must be called to free
157 * the previously reserved TLS index.
158 *
159 * If libgit2 is built without threading support, the
160 * `git__global_statestate()` call returns a pointer to a single,
161 * statically allocated global state. The `git_thread_`
162 * functions are not available in that case.
163 */
164
165 /*
166 * `git_libgit2_init()` allows subsystems to perform global setup,
167 * which may take place in the global scope. An explicit memory
168 * fence exists at the exit of `git_libgit2_init()`. Without this,
169 * CPU cores are free to reorder cache invalidation of `_tls_init`
170 * before cache invalidation of the subsystems' newly written global
171 * state.
172 */
173 #if defined(GIT_THREADS) && defined(GIT_WIN32)
174
175 static DWORD _tls_index;
176 static volatile LONG _mutex = 0;
177
178 static int synchronized_threads_init(void)
179 {
180 int error;
181
182 _tls_index = TlsAlloc();
183 if (git_mutex_init(&git__mwindow_mutex))
184 return -1;
185
186 /* Initialize any other subsystems that have global state */
187 if ((error = git_hash_global_init()) >= 0)
188 error = git_sysdir_global_init();
189
190 win32_pthread_initialize();
191
192 return error;
193 }
194
195 int git_libgit2_init(void)
196 {
197 int error = 0;
198
199 /* Enter the lock */
200 while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); }
201
202 /* Only do work on a 0 -> 1 transition of the refcount */
203 if (1 == git_atomic_inc(&git__n_inits))
204 error = synchronized_threads_init();
205
206 /* Exit the lock */
207 InterlockedExchange(&_mutex, 0);
208
209 return error;
210 }
211
212 static void synchronized_threads_shutdown(void)
213 {
214 /* Shut down any subsystems that have global state */
215 git__shutdown();
216 TlsFree(_tls_index);
217 git_mutex_free(&git__mwindow_mutex);
218 }
219
220 void git_libgit2_shutdown(void)
221 {
222 /* Enter the lock */
223 while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); }
224
225 /* Only do work on a 1 -> 0 transition of the refcount */
226 if (0 == git_atomic_dec(&git__n_inits))
227 synchronized_threads_shutdown();
228
229 /* Exit the lock */
230 InterlockedExchange(&_mutex, 0);
231 }
232
233 git_global_st *git__global_state(void)
234 {
235 void *ptr;
236
237 assert(git_atomic_get(&git__n_inits) > 0);
238
239 if ((ptr = TlsGetValue(_tls_index)) != NULL)
240 return ptr;
241
242 ptr = git__malloc(sizeof(git_global_st));
243 if (!ptr)
244 return NULL;
245
246 memset(ptr, 0x0, sizeof(git_global_st));
247 TlsSetValue(_tls_index, ptr);
248 return ptr;
249 }
250
251 #elif defined(GIT_THREADS) && defined(_POSIX_THREADS)
252
253 static pthread_key_t _tls_key;
254 static pthread_once_t _once_init = PTHREAD_ONCE_INIT;
255 int init_error = 0;
256
257 static void cb__free_status(void *st)
258 {
259 git_global_st *state = (git_global_st *) st;
260 git__free(state->error_t.message);
261
262 git__free(st);
263 }
264
265 static void init_once(void)
266 {
267 if ((init_error = git_mutex_init(&git__mwindow_mutex)) != 0)
268 return;
269 pthread_key_create(&_tls_key, &cb__free_status);
270
271
272 /* Initialize any other subsystems that have global state */
273 if ((init_error = git_hash_global_init()) >= 0)
274 init_error = git_sysdir_global_init();
275
276 /* OpenSSL needs to be initialized from the main thread */
277 init_ssl();
278
279 GIT_MEMORY_BARRIER;
280 }
281
282 int git_libgit2_init(void)
283 {
284 pthread_once(&_once_init, init_once);
285 git_atomic_inc(&git__n_inits);
286 return init_error;
287 }
288
289 void git_libgit2_shutdown(void)
290 {
291 void *ptr = NULL;
292 pthread_once_t new_once = PTHREAD_ONCE_INIT;
293
294 if (git_atomic_dec(&git__n_inits) > 0) return;
295
296 /* Shut down any subsystems that have global state */
297 git__shutdown();
298
299 ptr = pthread_getspecific(_tls_key);
300 pthread_setspecific(_tls_key, NULL);
301 git__free(ptr);
302
303 pthread_key_delete(_tls_key);
304 git_mutex_free(&git__mwindow_mutex);
305 _once_init = new_once;
306 }
307
308 git_global_st *git__global_state(void)
309 {
310 void *ptr;
311
312 assert(git_atomic_get(&git__n_inits) > 0);
313
314 if ((ptr = pthread_getspecific(_tls_key)) != NULL)
315 return ptr;
316
317 ptr = git__malloc(sizeof(git_global_st));
318 if (!ptr)
319 return NULL;
320
321 memset(ptr, 0x0, sizeof(git_global_st));
322 pthread_setspecific(_tls_key, ptr);
323 return ptr;
324 }
325
326 #else
327
328 static git_global_st __state;
329
330 int git_libgit2_init(void)
331 {
332 static int ssl_inited = 0;
333
334 if (!ssl_inited) {
335 init_ssl();
336 ssl_inited = 1;
337 }
338
339 git_atomic_inc(&git__n_inits);
340 return 0;
341 }
342
343 void git_libgit2_shutdown(void)
344 {
345 /* Shut down any subsystems that have global state */
346 if (0 == git_atomic_dec(&git__n_inits))
347 git__shutdown();
348 }
349
350 git_global_st *git__global_state(void)
351 {
352 return &__state;
353 }
354
355 #endif /* GIT_THREADS */