mirror of
				https://git.proxmox.com/git/mirror_corosync
				synced 2025-10-26 04:46:34 +00:00 
			
		
		
		
	 7cba14fb61
			
		
	
	
		7cba14fb61
		
	
	
	
	
		
			
			Signed-off-by: Jan Friesse <jfriesse@redhat.com> Reviewed-by: Christine Caulfield <ccaulfie@redhat.com>
		
			
				
	
	
		
			469 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			469 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2006 MontaVista Software, Inc.
 | |
|  * Copyright (c) 2006-2012 Red Hat, Inc.
 | |
|  *
 | |
|  * All rights reserved.
 | |
|  *
 | |
|  * Author: Steven Dake (sdake@redhat.com)
 | |
|  *
 | |
|  * This software licensed under BSD license, the text of which follows:
 | |
|  *
 | |
|  * Redistribution and use in source and binary forms, with or without
 | |
|  * modification, are permitted provided that the following conditions are met:
 | |
|  *
 | |
|  * - Redistributions of source code must retain the above copyright notice,
 | |
|  *   this list of conditions and the following disclaimer.
 | |
|  * - Redistributions in binary form must reproduce the above copyright notice,
 | |
|  *   this list of conditions and the following disclaimer in the documentation
 | |
|  *   and/or other materials provided with the distribution.
 | |
|  * - Neither the name of the MontaVista Software, Inc. nor the names of its
 | |
|  *   contributors may be used to endorse or promote products derived from this
 | |
|  *   software without specific prior written permission.
 | |
|  *
 | |
|  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | |
|  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | |
|  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | |
|  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 | |
|  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | |
|  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | |
|  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | |
|  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | |
|  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | |
|  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 | |
|  * THE POSSIBILITY OF SUCH DAMAGE.
 | |
|  */
 | |
| 
 | |
| #include <config.h>
 | |
| 
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| 
 | |
| #include <corosync/swab.h>
 | |
| #include <corosync/totem/totem.h>
 | |
| 
 | |
| #include <corosync/corotypes.h>
 | |
| #include "util.h"
 | |
| #include <corosync/logsys.h>
 | |
| #include <corosync/icmap.h>
 | |
| 
 | |
| #include "timer.h"
 | |
| #include <corosync/totem/totempg.h>
 | |
| #include <corosync/totem/totemip.h>
 | |
| #include "main.h"
 | |
| #include "service.h"
 | |
| 
 | |
| #include <qb/qbipcs.h>
 | |
| #include <qb/qbloop.h>
 | |
| 
 | |
| LOGSYS_DECLARE_SUBSYS ("SERV");
 | |
| 
 | |
| static struct default_service default_services[] = {
 | |
| 	{
 | |
| 		.name		= "corosync_cmap",
 | |
| 		.ver		= 0,
 | |
| 		.loader		= cmap_get_service_engine_ver0
 | |
| 	},
 | |
| 	{
 | |
| 		.name		= "corosync_cfg",
 | |
| 		.ver		= 0,
 | |
| 		.loader		= cfg_get_service_engine_ver0
 | |
| 	},
 | |
| 	{
 | |
| 		.name		= "corosync_cpg",
 | |
| 		.ver		= 0,
 | |
| 		.loader		= cpg_get_service_engine_ver0
 | |
| 	},
 | |
| 	{
 | |
| 		.name		= "corosync_pload",
 | |
| 		.ver		= 0,
 | |
| 		.loader		= pload_get_service_engine_ver0
 | |
| 	},
 | |
| #ifdef HAVE_MONITORING
 | |
| 	{
 | |
| 		.name		= "corosync_mon",
 | |
| 		.ver		= 0,
 | |
| 		.loader		= mon_get_service_engine_ver0
 | |
| 	},
 | |
| #endif
 | |
| #ifdef HAVE_WATCHDOG
 | |
| 	{
 | |
| 		.name		= "corosync_wd",
 | |
| 		.ver		= 0,
 | |
| 		.loader		= wd_get_service_engine_ver0
 | |
| 	},
 | |
| #endif
 | |
| 	{
 | |
| 		.name		= "corosync_quorum",
 | |
| 		.ver		= 0,
 | |
| 		.loader		= vsf_quorum_get_service_engine_ver0
 | |
| 	},
 | |
| };
 | |
| 
 | |
| /*
 | |
|  * service exit and unlink schedwrk handler data structure
 | |
|  */
 | |
| struct seus_handler_data {
 | |
| 	int service_engine;
 | |
| 	struct corosync_api_v1 *api;
 | |
| };
 | |
| 
 | |
| struct corosync_service_engine *corosync_service[SERVICES_COUNT_MAX];
 | |
| 
 | |
| const char *service_stats_rx[SERVICES_COUNT_MAX][SERVICE_HANDLER_MAXIMUM_COUNT];
 | |
| const char *service_stats_tx[SERVICES_COUNT_MAX][SERVICE_HANDLER_MAXIMUM_COUNT];
 | |
| 
 | |
| static void (*service_unlink_all_complete) (void) = NULL;
 | |
| 
 | |
| char *corosync_service_link_and_init (
 | |
| 	struct corosync_api_v1 *corosync_api,
 | |
| 	struct default_service *service)
 | |
| {
 | |
| 	struct corosync_service_engine *service_engine;
 | |
| 	int fn;
 | |
| 	char *name_sufix;
 | |
| 	char key_name[ICMAP_KEYNAME_MAXLEN];
 | |
| 	char *init_result;
 | |
| 
 | |
| 	/*
 | |
| 	 * Initialize service
 | |
| 	 */
 | |
| 	service_engine = service->loader();
 | |
| 
 | |
| 	corosync_service[service_engine->id] = service_engine;
 | |
| 
 | |
| 	if (service_engine->config_init_fn) {
 | |
| 		service_engine->config_init_fn (corosync_api);
 | |
| 	}
 | |
| 
 | |
| 	if (service_engine->exec_init_fn) {
 | |
| 		init_result = service_engine->exec_init_fn (corosync_api);
 | |
| 		if (init_result) {
 | |
| 			return (init_result);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * Store service in cmap db
 | |
| 	 */
 | |
| 	snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "internal_configuration.service.%u.name", service_engine->id);
 | |
| 	icmap_set_string(key_name, service->name);
 | |
| 
 | |
| 	snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "internal_configuration.service.%u.ver", service_engine->id);
 | |
| 	icmap_set_uint32(key_name, service->ver);
 | |
| 
 | |
| 	name_sufix = strrchr (service->name, '_');
 | |
| 	if (name_sufix)
 | |
| 		name_sufix++;
 | |
| 	else
 | |
| 		name_sufix = (char*)service->name;
 | |
| 
 | |
| 	snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "runtime.services.%s.service_id", name_sufix);
 | |
| 	icmap_set_uint16(key_name, service_engine->id);
 | |
| 
 | |
| 	for (fn = 0; fn < service_engine->exec_engine_count; fn++) {
 | |
| 		snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "runtime.services.%s.%d.tx", name_sufix, fn);
 | |
| 		icmap_set_uint64(key_name, 0);
 | |
| 		service_stats_tx[service_engine->id][fn] = strdup(key_name);
 | |
| 
 | |
| 		snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "runtime.services.%s.%d.rx", name_sufix, fn);
 | |
| 		icmap_set_uint64(key_name, 0);
 | |
| 		service_stats_rx[service_engine->id][fn] = strdup(key_name);
 | |
| 	}
 | |
| 
 | |
| 	log_printf (LOGSYS_LEVEL_NOTICE,
 | |
| 		"Service engine loaded: %s [%d]", service_engine->name, service_engine->id);
 | |
| 	init_result = (char *)cs_ipcs_service_init(service_engine);
 | |
| 	if (init_result != NULL) {
 | |
| 		return (init_result);
 | |
| 	}
 | |
| 
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| static int service_priority_max(void)
 | |
| {
 | |
| 	int lpc = 0, max = 0;
 | |
| 	for(; lpc < SERVICES_COUNT_MAX; lpc++) {
 | |
| 		if(corosync_service[lpc] != NULL && corosync_service[lpc]->priority > max) {
 | |
| 			max = corosync_service[lpc]->priority;
 | |
| 		}
 | |
| 	}
 | |
| 	return max;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * use the force
 | |
|  */
 | |
| static unsigned int
 | |
| corosync_service_unlink_and_exit_priority (
 | |
| 	struct corosync_api_v1 *corosync_api,
 | |
| 	int lowest_priority,
 | |
| 	int *current_priority,
 | |
| 	int *current_service_engine)
 | |
| {
 | |
| 	unsigned short service_id;
 | |
| 	int res;
 | |
| 
 | |
| 	for(; *current_priority >= lowest_priority; *current_priority = *current_priority - 1) {
 | |
| 		for(*current_service_engine = 0;
 | |
| 			*current_service_engine < SERVICES_COUNT_MAX;
 | |
| 			*current_service_engine = *current_service_engine + 1) {
 | |
| 
 | |
| 			if(corosync_service[*current_service_engine] == NULL ||
 | |
| 				corosync_service[*current_service_engine]->priority != *current_priority) {
 | |
| 				continue;
 | |
| 			}
 | |
| 
 | |
| 			/*
 | |
| 			 * find service handle and unload it if possible.
 | |
| 			 *
 | |
| 			 * If the service engine's exec_exit_fn returns -1 indicating
 | |
| 			 * it was busy, this function returns -1 and can be called again
 | |
| 			 * at a later time (usually via the schedwrk api).
 | |
| 			 */
 | |
| 			service_id = corosync_service[*current_service_engine]->id;
 | |
| 
 | |
| 			if (corosync_service[service_id]->exec_exit_fn) {
 | |
| 				res = corosync_service[service_id]->exec_exit_fn ();
 | |
| 				if (res == -1) {
 | |
| 					return (-1);
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			/*
 | |
| 			 * Exit all ipc connections dependent on this service
 | |
| 			 */
 | |
| 			cs_ipcs_service_destroy (*current_service_engine);
 | |
| 
 | |
| 			log_printf(LOGSYS_LEVEL_NOTICE,
 | |
| 				"Service engine unloaded: %s",
 | |
| 				corosync_service[*current_service_engine]->name);
 | |
| 
 | |
| 			corosync_service[*current_service_engine] = NULL;
 | |
| 
 | |
| 			/*
 | |
| 			 * Call should call this function again
 | |
| 			 */
 | |
| 			return (1);
 | |
| 		}
 | |
| 	}
 | |
| 	/*
 | |
| 	 * We finish unlink of all services -> no need to call this function again
 | |
| 	 */
 | |
| 	return (0);
 | |
| }
 | |
| 
 | |
| static unsigned int service_unlink_and_exit (
 | |
| 	struct corosync_api_v1 *corosync_api,
 | |
| 	const char *service_name,
 | |
| 	unsigned int service_ver)
 | |
| {
 | |
| 	unsigned short service_id;
 | |
| 	char *name_sufix;
 | |
| 	int res;
 | |
| 	const char *iter_key_name;
 | |
| 	icmap_iter_t iter;
 | |
| 	char key_name[ICMAP_KEYNAME_MAXLEN];
 | |
| 	unsigned int found_service_ver;
 | |
| 	char *found_service_name;
 | |
| 	int service_found;
 | |
| 
 | |
| 	name_sufix = strrchr (service_name, '_');
 | |
| 	if (name_sufix)
 | |
| 		name_sufix++;
 | |
| 	else
 | |
| 		name_sufix = (char*)service_name;
 | |
| 
 | |
| 
 | |
| 	service_found = 0;
 | |
| 	found_service_name = NULL;
 | |
| 	iter = icmap_iter_init("internal_configuration.service.");
 | |
| 	while ((iter_key_name = icmap_iter_next(iter, NULL, NULL)) != NULL) {
 | |
| 		res = sscanf(iter_key_name, "internal_configuration.service.%hu.%s", &service_id, key_name);
 | |
| 		if (res != 2) {
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "internal_configuration.service.%hu.name", service_id);
 | |
| 		if (icmap_get_string(key_name, &found_service_name) != CS_OK) {
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "internal_configuration.service.%u.ver", service_id);
 | |
| 		if (icmap_get_uint32(key_name, &found_service_ver) != CS_OK) {
 | |
| 			free(found_service_name);
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		if (service_ver == found_service_ver && strcmp(found_service_name, service_name) == 0) {
 | |
| 			free(found_service_name);
 | |
| 			service_found = 1;
 | |
| 			break;
 | |
| 		}
 | |
| 		free(found_service_name);
 | |
| 	}
 | |
| 	icmap_iter_finalize(iter);
 | |
| 
 | |
| 	if (service_found && service_id < SERVICES_COUNT_MAX
 | |
| 		&& corosync_service[service_id] != NULL) {
 | |
| 
 | |
| 		if (corosync_service[service_id]->exec_exit_fn) {
 | |
| 			res = corosync_service[service_id]->exec_exit_fn ();
 | |
| 			if (res == -1) {
 | |
| 				return (-1);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		log_printf(LOGSYS_LEVEL_NOTICE,
 | |
| 			"Service engine unloaded: %s",
 | |
| 			   corosync_service[service_id]->name);
 | |
| 
 | |
| 		corosync_service[service_id] = NULL;
 | |
| 
 | |
| 		cs_ipcs_service_destroy (service_id);
 | |
| 
 | |
| 		snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "internal_configuration.service.%u.handle", service_id);
 | |
| 		icmap_delete(key_name);
 | |
| 		snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "internal_configuration.service.%u.name", service_id);
 | |
| 		icmap_delete(key_name);
 | |
| 		snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "internal_configuration.service.%u.ver", service_id);
 | |
| 		icmap_delete(key_name);
 | |
| 	}
 | |
| 
 | |
| 	return (0);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Links default services into the executive
 | |
|  */
 | |
| unsigned int corosync_service_defaults_link_and_init (struct corosync_api_v1 *corosync_api)
 | |
| {
 | |
| 	unsigned int i;
 | |
| 	char *error;
 | |
| 
 | |
| 	for (i = 0;
 | |
| 		i < sizeof (default_services) / sizeof (struct default_service); i++) {
 | |
| 
 | |
| 		default_services[i].loader();
 | |
| 		error = corosync_service_link_and_init (
 | |
| 			corosync_api,
 | |
| 			&default_services[i]);
 | |
| 		if (error) {
 | |
| 			log_printf(LOGSYS_LEVEL_ERROR,
 | |
| 				"Service engine '%s' failed to load for reason '%s'",
 | |
| 				default_services[i].name,
 | |
| 				error);
 | |
| 			corosync_exit_error (COROSYNC_DONE_SERVICE_ENGINE_INIT);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return (0);
 | |
| }
 | |
| 
 | |
| static void service_exit_schedwrk_handler (void *data) {
 | |
| 	int res;
 | |
| 	static int current_priority = 0;
 | |
| 	static int current_service_engine = 0;
 | |
| 	static int called = 0;
 | |
| 	struct seus_handler_data *cb_data = (struct seus_handler_data *)data;
 | |
| 	struct corosync_api_v1 *api = (struct corosync_api_v1 *)cb_data->api;
 | |
| 
 | |
| 	if (called == 0) {
 | |
| 		log_printf(LOGSYS_LEVEL_NOTICE,
 | |
| 			"Unloading all Corosync service engines.");
 | |
|  		current_priority = service_priority_max ();
 | |
| 		called = 1;
 | |
| 	}
 | |
| 
 | |
| 	res = corosync_service_unlink_and_exit_priority (
 | |
| 		api,
 | |
| 		0,
 | |
| 		¤t_priority,
 | |
| 		¤t_service_engine);
 | |
| 	if (res == 0) {
 | |
| 		service_unlink_all_complete();
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	qb_loop_job_add(cs_poll_handle_get(),
 | |
| 		QB_LOOP_HIGH,
 | |
| 		data,
 | |
| 		service_exit_schedwrk_handler);
 | |
| }
 | |
| 
 | |
| void corosync_service_unlink_all (
 | |
| 	struct corosync_api_v1 *api,
 | |
| 	void (*unlink_all_complete) (void))
 | |
| {
 | |
| 	static int called = 0;
 | |
| 	static struct seus_handler_data cb_data;
 | |
| 
 | |
| 	assert (api);
 | |
| 
 | |
| 	service_unlink_all_complete = unlink_all_complete;
 | |
| 
 | |
| 	if (called) {
 | |
| 		return;
 | |
| 	}
 | |
| 	if (called == 0) {
 | |
| 		called = 1;
 | |
| 	}
 | |
| 
 | |
| 	cb_data.api = api;
 | |
| 
 | |
| 	qb_loop_job_add(cs_poll_handle_get(),
 | |
| 		QB_LOOP_HIGH,
 | |
| 		&cb_data,
 | |
| 		service_exit_schedwrk_handler);
 | |
| }
 | |
| 
 | |
| struct service_unlink_and_exit_data {
 | |
| 	hdb_handle_t handle;
 | |
| 	struct corosync_api_v1 *api;
 | |
| 	const char *name;
 | |
| 	unsigned int ver;
 | |
| };
 | |
| 
 | |
| static void service_unlink_and_exit_schedwrk_handler (void *data)
 | |
| {
 | |
| 	struct service_unlink_and_exit_data *service_unlink_and_exit_data =
 | |
| 		data;
 | |
| 	int res;
 | |
| 
 | |
| 	res = service_unlink_and_exit (
 | |
| 		service_unlink_and_exit_data->api,
 | |
| 		service_unlink_and_exit_data->name,
 | |
| 		service_unlink_and_exit_data->ver);
 | |
| 
 | |
| 	if (res == 0) {
 | |
| 		free (service_unlink_and_exit_data);
 | |
| 	} else {
 | |
| 		qb_loop_job_add(cs_poll_handle_get(),
 | |
| 			QB_LOOP_HIGH,
 | |
| 			data,
 | |
| 			service_unlink_and_exit_schedwrk_handler);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| typedef int (*schedwrk_cast) (const void *);
 | |
| 
 | |
| unsigned int corosync_service_unlink_and_exit (
 | |
|         struct corosync_api_v1 *api,
 | |
|         const char *service_name,
 | |
|         unsigned int service_ver)
 | |
| {
 | |
| 	struct service_unlink_and_exit_data *service_unlink_and_exit_data;
 | |
| 
 | |
| 	assert (api);
 | |
| 	service_unlink_and_exit_data = malloc (sizeof (struct service_unlink_and_exit_data));
 | |
| 	service_unlink_and_exit_data->api = api;
 | |
| 	service_unlink_and_exit_data->name = strdup (service_name);
 | |
| 	service_unlink_and_exit_data->ver = service_ver;
 | |
| 
 | |
| 	qb_loop_job_add(cs_poll_handle_get(),
 | |
| 		QB_LOOP_HIGH,
 | |
| 		service_unlink_and_exit_data,
 | |
| 		service_unlink_and_exit_schedwrk_handler);
 | |
| 	return (0);
 | |
| }
 |