/* * Copyright (c) 2006 MontaVista Software, Inc. * Copyright (c) 2006-2009 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 #include #include #include #include #include #include #include "mainconfig.h" #include "util.h" #include #include #include "timer.h" #include #include #include "main.h" #include "service.h" #include #include LOGSYS_DECLARE_SUBSYS ("SERV"); struct default_service { const char *name; int ver; }; static struct default_service default_services[] = { { .name = "corosync_evs", .ver = 0, }, { .name = "corosync_cfg", .ver = 0, }, { .name = "corosync_cpg", .ver = 0, }, { .name = "corosync_pload", .ver = 0, }, #ifdef HAVE_MONITORING { .name = "corosync_mon", .ver = 0, }, #endif #ifdef HAVE_WATCHDOG { .name = "corosync_wd", .ver = 0, }, #endif { .name = "corosync_quorum", .ver = 0, }, { .name = "corosync_cmap", .ver = 0, }, }; /* * service exit and unlink schedwrk handler data structure */ struct seus_handler_data { hdb_handle_t service_handle; int service_engine; struct corosync_api_v1 *api; }; struct corosync_service_engine *corosync_service[SERVICE_HANDLER_MAXIMUM_COUNT]; const char *service_stats_rx[SERVICE_HANDLER_MAXIMUM_COUNT][64]; const char *service_stats_tx[SERVICE_HANDLER_MAXIMUM_COUNT][64]; int corosync_service_exiting[SERVICE_HANDLER_MAXIMUM_COUNT]; static void (*service_unlink_all_complete) (void) = NULL; static unsigned int default_services_requested (struct corosync_api_v1 *corosync_api) { char *value = NULL; int res; /* * Don't link default services if they have been disabled */ if (icmap_get_string("aisexec.defaultservices", &value) == CS_OK && value != NULL && strcmp(value, "no") == 0) { res = 0; } else { res = -1; } free(value); return (res); } unsigned int corosync_service_link_and_init ( struct corosync_api_v1 *corosync_api, const char *service_name, unsigned int service_ver) { struct corosync_service_engine_iface_ver0 *iface_ver0; void *iface_ver0_p; hdb_handle_t handle; struct corosync_service_engine *service; int res; int fn; char *name_sufix; void* _start; void* _stop; char key_name[ICMAP_KEYNAME_MAXLEN]; /* * reference the service interface */ iface_ver0_p = NULL; res = lcr_ifact_reference ( &handle, service_name, service_ver, &iface_ver0_p, (void *)0); iface_ver0 = (struct corosync_service_engine_iface_ver0 *)iface_ver0_p; if (res == -1 || iface_ver0 == 0) { log_printf(LOGSYS_LEVEL_ERROR, "Service failed to load '%s'.\n", service_name); return (-1); } /* * Initialize service */ service = iface_ver0->corosync_get_service_engine_ver0(); corosync_service[service->id] = service; /* * Register the log sites with libqb */ _start = lcr_ifact_addr_get(handle, "__start___verbose"); _stop = lcr_ifact_addr_get(handle, "__stop___verbose"); qb_log_callsites_register(_start, _stop); if (service->config_init_fn) { res = service->config_init_fn (corosync_api); } if (service->exec_init_fn) { res = service->exec_init_fn (corosync_api); } /* * Store service in cmap db */ snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "internal_configuration.service.%u.name", service->id); icmap_set_string(key_name, service_name); snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "internal_configuration.service.%u.ver", service->id); icmap_set_uint32(key_name, service_ver); snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "internal_configuration.service.%u.handle", service->id); icmap_set_uint64(key_name, handle); 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->id); for (fn = 0; fn < service->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->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->id][fn] = strdup(key_name); } log_printf (LOGSYS_LEVEL_NOTICE, "Service engine loaded: %s [%d]\n", service->name, service->id); cs_ipcs_service_init(service); return (res); } static int service_priority_max(void) { int lpc = 0, max = 0; for(; lpc < SERVICE_HANDLER_MAXIMUM_COUNT; 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_priority ( struct corosync_api_v1 *corosync_api, int lowest_priority, int *current_priority, int *current_service_engine, hdb_handle_t *current_service_handle) { unsigned short service_id; hdb_handle_t found_service_handle; char key_name[ICMAP_KEYNAME_MAXLEN]; int res; for(; *current_priority >= lowest_priority; *current_priority = *current_priority - 1) { for(*current_service_engine = 0; *current_service_engine < SERVICE_HANDLER_MAXIMUM_COUNT; *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). */ snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "internal_configuration.service.%u.handle", corosync_service[*current_service_engine]->id); if (icmap_get_uint64(key_name, &found_service_handle) == CS_OK) { 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); } } *current_service_handle = found_service_handle; corosync_service_exiting[*current_service_engine] = 1; /* * 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; hdb_handle_t found_service_handle; 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); free(found_service_name); 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) { continue; } if (service_ver == found_service_ver && strcmp(found_service_name, service_name) == 0) { free(found_service_name); service_found = 1; break; } } icmap_iter_finalize(iter); if (service_found && service_id < SERVICE_HANDLER_MAXIMUM_COUNT && 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\n", 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); if (icmap_get_uint64(key_name, &found_service_handle) == CS_OK) { lcr_ifact_release (found_service_handle); } 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; icmap_iter_t iter; char *found_service_name; int res; unsigned int found_service_ver; const char *iter_key_name; unsigned int service_pos; char key_name[ICMAP_KEYNAME_MAXLEN]; icmap_set_ro_access("internal_configuration.", 1, 1); icmap_set_ro_access("runtime.services.", 1, 1); found_service_name = NULL; iter = icmap_iter_init("service."); while ((iter_key_name = icmap_iter_next(iter, NULL, NULL)) != NULL) { res = sscanf(iter_key_name, "service.%u.%s", &service_pos, key_name); if (res != 2) { continue; } if (strcmp(key_name, "name") != 0) { continue; } snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "service.%u.name", service_pos); free(found_service_name); if (icmap_get_string(key_name, &found_service_name) != CS_OK) { continue; } snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "service.%u.ver", service_pos); if (icmap_get_uint32(key_name, &found_service_ver) != CS_OK) { continue; } corosync_service_link_and_init ( corosync_api, found_service_name, found_service_ver); } icmap_iter_finalize(iter); if (default_services_requested (corosync_api) == 0) { return (0); } for (i = 0; i < sizeof (default_services) / sizeof (struct default_service); i++) { corosync_service_link_and_init ( corosync_api, default_services[i].name, default_services[i].ver); } return (0); } /* * Declaration of exit_schedwrk_handler, because of cycle * (service_exit_schedwrk_handler calls service_unlink_schedwrk_handler, and vice-versa) */ static void service_exit_schedwrk_handler (void *data); static void service_unlink_schedwrk_handler (void *data) { struct seus_handler_data *cb_data = (struct seus_handler_data *)data; /* * Exit all ipc connections dependent on this service */ if (cs_ipcs_service_destroy (cb_data->service_engine) == -1) { goto redo_this_function; } log_printf(LOGSYS_LEVEL_NOTICE, "Service engine unloaded: %s\n", corosync_service[cb_data->service_engine]->name); corosync_service[cb_data->service_engine] = NULL; lcr_ifact_release (cb_data->service_handle); qb_loop_job_add(cs_poll_handle_get(), QB_LOOP_HIGH, data, service_exit_schedwrk_handler); return; redo_this_function: qb_loop_job_add(cs_poll_handle_get(), QB_LOOP_HIGH, data, service_unlink_schedwrk_handler); } 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; hdb_handle_t service_handle; if (called == 0) { log_printf(LOGSYS_LEVEL_NOTICE, "Unloading all Corosync service engines.\n"); current_priority = service_priority_max (); called = 1; } res = corosync_service_unlink_priority ( api, 0, ¤t_priority, ¤t_service_engine, &service_handle); if (res == 0) { service_unlink_all_complete(); return; } if (res == 1) { cb_data->service_engine = current_service_engine; cb_data->service_handle = service_handle; qb_loop_job_add(cs_poll_handle_get(), QB_LOOP_HIGH, data, service_unlink_schedwrk_handler); 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); }