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