1 /*********************************************************************************************************
2 * Software License Agreement (BSD License)                                                               *
3 * Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
4 *													 *
5 * Copyright (c) 2019, 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 
core_state_get(void)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 
core_state_set(enum core_state newstate)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 
core_state_wait(enum core_state waitstate)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 
core_shutdown(void)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 
core_runner_thread(void * arg)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 */
fd_core_initialize(void)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 */
fd_core_parseconf_int(const char * conffile)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 
fd_core_parseconf(const char * conffile)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) */
fd_core_waitstartcomplete(void)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 */
fd_core_start_int(void)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 
fd_core_start(void)315 int fd_core_start(void)
316 {
317 	int ret;
318 	CHECK_FCT( fd_queues_init_after_conf() );
319 
320 	CHECK_POSIX( pthread_mutex_lock(&core_lock) );
321 	ret = fd_core_start_int();
322 	CHECK_POSIX( pthread_mutex_unlock(&core_lock) );
323 	return ret;
324 }
325 
326 /* Initialize shutdown of the framework. This is not blocking. */
fd_core_shutdown(void)327 int fd_core_shutdown(void)
328 {
329 	enum core_state cur_state = core_state_get();
330 
331 	LOG_N("Initiating freeDiameter shutdown sequence (%d)", cur_state);
332 
333 	if (cur_state < CORE_RUNNING) {
334 		/* Calling application must make sure the initialization is not ongoing in a separate thread... */
335 		if (pthread_mutex_lock(&core_lock) != 0) {
336 			/* This function must not be called asynchronously from fd_core_parseconf / fd_core_start ! Please review your main app design */
337 			ASSERT(0);
338 			return EINVAL;
339 		}
340 		core_shutdown();
341 		core_state_set(CORE_TERM);
342 		pthread_mutex_unlock(&core_lock);
343 	} else if (cur_state == CORE_RUNNING) {
344 		core_state_set(CORE_SHUTDOWN);
345 		CHECK_FCT( fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE_INT, 0, NULL) );
346 	}
347 
348 	/* Other case, the framework is already shutting down */
349 
350 	return 0;
351 }
352 
353 
354 /* Wait for the shutdown to be complete -- this must be called after fd_core_shutdown to reclaim some resources. */
fd_core_wait_shutdown_complete(void)355 int fd_core_wait_shutdown_complete(void)
356 {
357 	enum core_state cur_state = core_state_get();
358 	void * th_ret = NULL;
359 
360 	CHECK_FCT(core_state_wait(CORE_SHUTDOWN));
361 
362 	if (cur_state == CORE_TERM)
363 		return 0;
364 
365 	/* Just wait for core_runner_thread to complete and return gracefully */
366 	CHECK_POSIX(pthread_join(core_runner, &th_ret));
367 
368 	core_state_set(CORE_TERM);
369 
370 	return 0;
371 }
372 
373 
374 
375 
376