Brian Waters | 13d9601 | 2017-12-08 16:53:31 -0600 | [diff] [blame] | 1 | /********************************************************************************************************* |
| 2 | * Software License Agreement (BSD License) * |
| 3 | * Author: Sebastien Decugis <sdecugis@freediameter.net> * |
| 4 | * * |
| 5 | * Copyright (c) 2013, WIDE Project and NICT * |
| 6 | * All rights reserved. * |
| 7 | * * |
| 8 | * Redistribution and use of this software in source and binary forms, with or without modification, are * |
| 9 | * permitted provided that the following conditions are met: * |
| 10 | * * |
| 11 | * * Redistributions of source code must retain the above * |
| 12 | * copyright notice, this list of conditions and the * |
| 13 | * following disclaimer. * |
| 14 | * * |
| 15 | * * Redistributions in binary form must reproduce the above * |
| 16 | * copyright notice, this list of conditions and the * |
| 17 | * following disclaimer in the documentation and/or other * |
| 18 | * materials provided with the distribution. * |
| 19 | * * |
| 20 | * * Neither the name of the WIDE Project or NICT nor the * |
| 21 | * names of its contributors may be used to endorse or * |
| 22 | * promote products derived from this software without * |
| 23 | * specific prior written permission of WIDE Project and * |
| 24 | * NICT. * |
| 25 | * * |
| 26 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * |
| 27 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * |
| 28 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * |
| 29 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * |
| 30 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * |
| 31 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * |
| 32 | * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * |
| 33 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * |
| 34 | *********************************************************************************************************/ |
| 35 | |
| 36 | #include "fdcore-internal.h" |
| 37 | |
| 38 | GCC_DIAG_OFF("-Wdeprecated-declarations") |
| 39 | #include <gcrypt.h> |
| 40 | GCC_DIAG_ON("-Wdeprecated-declarations") |
| 41 | |
| 42 | /* The static configuration structure */ |
| 43 | static struct fd_config g_conf; |
| 44 | struct fd_config * fd_g_config = NULL; |
| 45 | |
| 46 | /* gcrypt functions to support posix threads */ |
| 47 | #ifndef GNUTLS_VERSION_210 |
| 48 | GCRY_THREAD_OPTION_PTHREAD_IMPL; |
| 49 | #endif /* GNUTLS_VERSION_210 */ |
| 50 | |
| 51 | /* Thread that process incoming events on the main queue -- and terminates the framework when requested */ |
| 52 | static pthread_t core_runner = (pthread_t)NULL; |
| 53 | |
| 54 | /* Signal extensions when the framework is completely initialized (they are waiting in fd_core_waitstartcomplete()) */ |
| 55 | static enum core_state { |
| 56 | CORE_NOT_INIT, /* initial state */ |
| 57 | CORE_LIBS_INIT, /* fd_core_initialize was called */ |
| 58 | CORE_CONF_READY,/* Configuration was parsed, extensions are loaded */ |
| 59 | CORE_RUNNING, /* Servers and clients are started, core_runner thread is running */ |
| 60 | CORE_SHUTDOWN, /* The framework is terminating all objects */ |
| 61 | CORE_TERM /* Shutdown complete. */ |
| 62 | } core_state = CORE_NOT_INIT; |
| 63 | static pthread_mutex_t core_mtx = PTHREAD_MUTEX_INITIALIZER; |
| 64 | static pthread_cond_t core_cnd = PTHREAD_COND_INITIALIZER; |
| 65 | |
| 66 | static enum core_state core_state_get(void) |
| 67 | { |
| 68 | enum core_state cur_state; |
| 69 | CHECK_POSIX_DO( pthread_mutex_lock( &core_mtx ), ); |
| 70 | cur_state = core_state; |
| 71 | CHECK_POSIX_DO( pthread_mutex_unlock( &core_mtx ), ); |
| 72 | return cur_state; |
| 73 | } |
| 74 | |
| 75 | static void core_state_set(enum core_state newstate) |
| 76 | { |
| 77 | CHECK_POSIX_DO( pthread_mutex_lock( &core_mtx ), ); |
| 78 | LOG_D("Core state: %d -> %d", core_state, newstate); |
| 79 | core_state = newstate; |
| 80 | CHECK_POSIX_DO( pthread_cond_broadcast( &core_cnd ), ); |
| 81 | CHECK_POSIX_DO( pthread_mutex_unlock( &core_mtx ), ); |
| 82 | } |
| 83 | |
| 84 | static int core_state_wait(enum core_state waitstate) |
| 85 | { |
| 86 | int ret = 0; |
| 87 | CHECK_POSIX( pthread_mutex_lock( &core_mtx )); |
| 88 | pthread_cleanup_push( fd_cleanup_mutex, &core_mtx ); |
| 89 | while (waitstate > core_state) { |
| 90 | CHECK_POSIX_DO(ret = pthread_cond_wait(&core_cnd, &core_mtx), break); |
| 91 | } |
| 92 | pthread_cleanup_pop( 0 ); |
| 93 | CHECK_POSIX( pthread_mutex_unlock( &core_mtx )); |
| 94 | return ret; |
| 95 | } |
| 96 | |
| 97 | static void core_shutdown(void) |
| 98 | { |
| 99 | LOG_N( FD_PROJECT_BINARY " framework is stopping..."); |
| 100 | fd_log_threadname("fD Core Shutdown"); |
| 101 | |
| 102 | /* cleanups */ |
| 103 | CHECK_FCT_DO( fd_servers_stop(), /* Stop accepting new connections */ ); |
| 104 | CHECK_FCT_DO( fd_rtdisp_cleanstop(), /* Stop dispatch thread(s) after a clean loop if possible */ ); |
| 105 | CHECK_FCT_DO( fd_peer_fini(), /* Stop all connections */ ); |
| 106 | CHECK_FCT_DO( fd_rtdisp_fini(), /* Stop routing threads and destroy routing queues */ ); |
| 107 | |
| 108 | CHECK_FCT_DO( fd_ext_term(), /* Cleanup all extensions */ ); |
| 109 | CHECK_FCT_DO( fd_rtdisp_cleanup(), /* destroy remaining handlers */ ); |
| 110 | |
| 111 | GNUTLS_TRACE( gnutls_global_deinit() ); |
| 112 | |
| 113 | CHECK_FCT_DO( fd_conf_deinit(), ); |
| 114 | |
| 115 | CHECK_FCT_DO( fd_event_trig_fini(), ); |
| 116 | |
| 117 | fd_log_debug(FD_PROJECT_BINARY " framework is terminated."); |
| 118 | |
| 119 | fd_libproto_fini(); |
| 120 | |
| 121 | } |
| 122 | |
| 123 | |
| 124 | static void * core_runner_thread(void * arg) |
| 125 | { |
| 126 | fd_log_threadname("fD Core Runner"); |
| 127 | |
| 128 | core_state_wait(CORE_RUNNING); |
| 129 | |
| 130 | /* Handle events incoming on the main event queue */ |
| 131 | while (1) { |
| 132 | int code; size_t sz; void * data; |
| 133 | CHECK_FCT_DO( fd_event_get(fd_g_config->cnf_main_ev, &code, &sz, &data), break ); |
| 134 | switch (code) { |
| 135 | case FDEV_TRIGGER: |
| 136 | { |
| 137 | int tv, *p; |
| 138 | CHECK_PARAMS_DO( sz == sizeof(int), |
| 139 | { |
| 140 | TRACE_DEBUG(NONE, "Internal error: got FDEV_TRIGGER without trigger value!"); |
| 141 | ASSERT(0); |
| 142 | goto end; |
| 143 | } ); |
| 144 | p = data; |
| 145 | tv = *p; |
| 146 | free(p); |
| 147 | CHECK_FCT_DO( fd_event_trig_call_cb(tv), goto end ); |
| 148 | } |
| 149 | break; |
| 150 | |
| 151 | case FDEV_TERMINATE_INT: |
| 152 | goto end; |
| 153 | |
| 154 | default: |
| 155 | TRACE_DEBUG(INFO, "Unexpected event in the main event queue (%d), ignored.", code); |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | end: |
| 160 | core_shutdown(); |
| 161 | |
| 162 | return NULL; |
| 163 | } |
| 164 | |
| 165 | /*********************************/ |
| 166 | /* Public interfaces */ |
| 167 | |
| 168 | /* Initialize the libfdcore internals. This also initializes libfdproto */ |
| 169 | int fd_core_initialize(void) |
| 170 | { |
| 171 | int ret; |
| 172 | |
| 173 | if (core_state_get() != CORE_NOT_INIT) { |
| 174 | fprintf(stderr, "fd_core_initialize() called more than once!\n"); |
| 175 | return EINVAL; |
| 176 | } |
| 177 | |
| 178 | /* Initialize the library -- must come first since it initializes the debug facility */ |
| 179 | ret = fd_libproto_init(); |
| 180 | if (ret != 0) { |
| 181 | fprintf(stderr, "Unable to initialize libfdproto: %s\n", strerror(ret)); |
| 182 | return ret; |
| 183 | } |
| 184 | |
| 185 | /* Name this thread */ |
| 186 | fd_log_threadname("Main"); |
| 187 | |
| 188 | LOG_N("libfdproto '%s' initialized.", fd_libproto_version); |
| 189 | |
| 190 | /* Initialize gcrypt and gnutls */ |
| 191 | #ifndef GNUTLS_VERSION_210 |
| 192 | GNUTLS_TRACE( (void) gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread) ); |
| 193 | GNUTLS_TRACE( (void) gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0) ); |
| 194 | #endif /* GNUTLS_VERSION_210 */ |
| 195 | CHECK_GNUTLS_DO( gnutls_global_init(), return EINVAL ); |
| 196 | if ( ! gnutls_check_version(GNUTLS_VERSION) ) { |
| 197 | TRACE_ERROR( "The GNUTLS library is too old; found '%s', need '" GNUTLS_VERSION "'", gnutls_check_version(NULL)); |
| 198 | return EINVAL; |
| 199 | } else { |
| 200 | #ifdef GNUTLS_VERSION_210 |
| 201 | TRACE_DEBUG(INFO, "libgnutls '%s' initialized.", gnutls_check_version(NULL) ); |
| 202 | #else /* GNUTLS_VERSION_210 */ |
| 203 | TRACE_DEBUG(INFO, "libgnutls '%s', libgcrypt '%s', initialized.", gnutls_check_version(NULL), gcry_check_version(NULL) ); |
| 204 | #endif /* GNUTLS_VERSION_210 */ |
| 205 | } |
| 206 | |
| 207 | /* Initialize the config with default values */ |
| 208 | memset(&g_conf, 0, sizeof(struct fd_config)); |
| 209 | fd_g_config = &g_conf; |
| 210 | CHECK_FCT( fd_conf_init() ); |
| 211 | |
| 212 | /* Add definitions of the base protocol */ |
| 213 | CHECK_FCT( fd_dict_base_protocol(fd_g_config->cnf_dict) ); |
| 214 | |
| 215 | /* Initialize some modules */ |
| 216 | CHECK_FCT( fd_hooks_init() ); |
| 217 | CHECK_FCT( fd_queues_init() ); |
| 218 | CHECK_FCT( fd_sess_start() ); |
| 219 | CHECK_FCT( fd_p_expi_init() ); |
| 220 | |
| 221 | core_state_set(CORE_LIBS_INIT); |
| 222 | |
| 223 | LOG_N("libfdcore '%s' initialized.", fd_core_version); |
| 224 | |
| 225 | |
| 226 | /* Next thing is to parse the config, leave this for a different function */ |
| 227 | return 0; |
| 228 | } |
| 229 | |
| 230 | static pthread_mutex_t core_lock = PTHREAD_MUTEX_INITIALIZER; |
| 231 | |
| 232 | /* Parse the freeDiameter.conf configuration file, load the extensions */ |
| 233 | static int fd_core_parseconf_int(const char * conffile) |
| 234 | { |
| 235 | char * buf = NULL, *b; |
| 236 | size_t len = 0, offset=0; |
| 237 | |
| 238 | |
| 239 | TRACE_ENTRY("%p", conffile); |
| 240 | |
| 241 | /* Conf file */ |
| 242 | if (conffile) |
| 243 | fd_g_config->cnf_file = conffile; /* otherwise, we use the default name */ |
| 244 | |
| 245 | CHECK_FCT( fd_conf_parse() ); |
| 246 | |
| 247 | /* The following module use data from the configuration */ |
| 248 | CHECK_FCT( fd_rtdisp_init() ); |
| 249 | |
| 250 | /* Now, load all dynamic extensions */ |
| 251 | CHECK_FCT( fd_ext_load() ); |
| 252 | |
| 253 | /* Display configuration */ |
| 254 | b = fd_conf_dump(&buf, &len, NULL); |
| 255 | LOG_SPLIT(FD_LOG_NOTICE, NULL, b ?: "<Error during configuration dump...>", NULL); |
| 256 | |
| 257 | /* Display extensions status */ |
| 258 | b = fd_ext_dump(&buf, &len, NULL); |
| 259 | LOG_SPLIT(FD_LOG_NOTICE, "Loaded extensions: ", b?:"<Error during extensions dump...>", NULL); |
| 260 | |
| 261 | /* Display registered triggers for FDEV_TRIGGER */ |
| 262 | b = fd_event_trig_dump(&buf, &len, &offset); |
| 263 | if (!b || offset) { |
| 264 | LOG_N("%s", b ?: "Error during triggers dump..."); |
| 265 | } |
| 266 | |
| 267 | free(buf); |
| 268 | |
| 269 | /* Since some extensions might have modified the definitions from the dict_base_protocol, we only load the objects now */ |
| 270 | CHECK_FCT( fd_msg_init() ); |
| 271 | |
| 272 | /* Ok, ready for next step */ |
| 273 | core_state_set(CORE_CONF_READY); |
| 274 | |
| 275 | return 0; |
| 276 | } |
| 277 | |
| 278 | int fd_core_parseconf(const char * conffile) |
| 279 | { |
| 280 | int ret; |
| 281 | CHECK_POSIX( pthread_mutex_lock(&core_lock) ); |
| 282 | ret = fd_core_parseconf_int(conffile); |
| 283 | CHECK_POSIX( pthread_mutex_unlock(&core_lock) ); |
| 284 | return ret; |
| 285 | } |
| 286 | |
| 287 | |
| 288 | /* For threads that would need to wait complete start of the framework (ex: in extensions) */ |
| 289 | int fd_core_waitstartcomplete(void) |
| 290 | { |
| 291 | TRACE_ENTRY(""); |
| 292 | |
| 293 | return core_state_wait(CORE_RUNNING); |
| 294 | } |
| 295 | |
| 296 | /* Start the server & client threads */ |
| 297 | static int fd_core_start_int(void) |
| 298 | { |
| 299 | /* Start server threads */ |
| 300 | CHECK_FCT( fd_servers_start() ); |
| 301 | |
| 302 | /* Start the peer state machines */ |
| 303 | CHECK_FCT( fd_psm_start() ); |
| 304 | |
| 305 | /* Start the core runner thread that handles main events (until shutdown) */ |
| 306 | CHECK_POSIX( pthread_create(&core_runner, NULL, core_runner_thread, NULL) ); |
| 307 | |
| 308 | /* Unlock threads waiting into fd_core_waitstartcomplete */ |
| 309 | core_state_set(CORE_RUNNING); |
| 310 | |
| 311 | /* Ok, everything is running now... */ |
| 312 | return 0; |
| 313 | } |
| 314 | |
| 315 | int fd_core_start(void) |
| 316 | { |
| 317 | int ret; |
| 318 | CHECK_POSIX( pthread_mutex_lock(&core_lock) ); |
| 319 | ret = fd_core_start_int(); |
| 320 | CHECK_POSIX( pthread_mutex_unlock(&core_lock) ); |
| 321 | return ret; |
| 322 | } |
| 323 | |
| 324 | /* Initialize shutdown of the framework. This is not blocking. */ |
| 325 | int fd_core_shutdown(void) |
| 326 | { |
| 327 | enum core_state cur_state = core_state_get(); |
| 328 | |
| 329 | LOG_F("Initiating freeDiameter shutdown sequence (%d)", cur_state); |
| 330 | |
| 331 | if (cur_state < CORE_RUNNING) { |
| 332 | /* Calling application must make sure the initialization is not ongoing in a separate thread... */ |
| 333 | if (pthread_mutex_lock(&core_lock) != 0) { |
| 334 | /* This function must not be called asynchronously from fd_core_parseconf / fd_core_start ! Please review your main app design */ |
| 335 | ASSERT(0); |
| 336 | return EINVAL; |
| 337 | } |
| 338 | core_shutdown(); |
| 339 | core_state_set(CORE_TERM); |
| 340 | pthread_mutex_unlock(&core_lock); |
| 341 | } else if (cur_state == CORE_RUNNING) { |
| 342 | core_state_set(CORE_SHUTDOWN); |
| 343 | CHECK_FCT( fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE_INT, 0, NULL) ); |
| 344 | } |
| 345 | |
| 346 | /* Other case, the framework is already shutting down */ |
| 347 | |
| 348 | return 0; |
| 349 | } |
| 350 | |
| 351 | |
| 352 | /* Wait for the shutdown to be complete -- this must be called after fd_core_shutdown to reclaim some resources. */ |
| 353 | int fd_core_wait_shutdown_complete(void) |
| 354 | { |
| 355 | enum core_state cur_state = core_state_get(); |
| 356 | void * th_ret = NULL; |
| 357 | |
| 358 | CHECK_FCT(core_state_wait(CORE_SHUTDOWN)); |
| 359 | |
| 360 | if (cur_state == CORE_TERM) |
| 361 | return 0; |
| 362 | |
| 363 | /* Just wait for core_runner_thread to complete and return gracefully */ |
| 364 | CHECK_POSIX(pthread_join(core_runner, &th_ret)); |
| 365 | |
| 366 | core_state_set(CORE_TERM); |
| 367 | |
| 368 | return 0; |
| 369 | } |
| 370 | |
| 371 | |
| 372 | |
| 373 | |