diff --git a/lib/sam.c b/lib/sam.c index 94dbf2ae..b45cae0b 100644 --- a/lib/sam.c +++ b/lib/sam.c @@ -1,1489 +1,1549 @@ /* * Copyright (c) 2009-2011 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@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 Red Hat, 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. */ /* * Provides a SAM API */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "util.h" #include #include #include #define SAM_CMAP_S_FAILED "failed" #define SAM_CMAP_S_REGISTERED "stopped" #define SAM_CMAP_S_STARTED "running" #define SAM_CMAP_S_Q_WAIT "waiting for quorum" #define SAM_RP_MASK_Q(pol) (pol & (~SAM_RECOVERY_POLICY_QUORUM)) #define SAM_RP_MASK_C(pol) (pol & (~SAM_RECOVERY_POLICY_CMAP)) #define SAM_RP_MASK(pol) (pol & (~(SAM_RECOVERY_POLICY_QUORUM | SAM_RECOVERY_POLICY_CMAP))) enum sam_internal_status_t { SAM_INTERNAL_STATUS_NOT_INITIALIZED = 0, SAM_INTERNAL_STATUS_INITIALIZED, SAM_INTERNAL_STATUS_REGISTERED, SAM_INTERNAL_STATUS_STARTED, SAM_INTERNAL_STATUS_FINALIZED }; enum sam_command_t { SAM_COMMAND_START, SAM_COMMAND_STOP, SAM_COMMAND_HB, SAM_COMMAND_DATA_STORE, SAM_COMMAND_WARN_SIGNAL_SET, SAM_COMMAND_MARK_FAILED, }; enum sam_reply_t { SAM_REPLY_OK, SAM_REPLY_ERROR, }; enum sam_parent_action_t { SAM_PARENT_ACTION_ERROR, SAM_PARENT_ACTION_RECOVERY, SAM_PARENT_ACTION_QUIT, SAM_PARENT_ACTION_CONTINUE }; enum sam_cmap_key_t { SAM_CMAP_KEY_RECOVERY, SAM_CMAP_KEY_HC_PERIOD, SAM_CMAP_KEY_LAST_HC, SAM_CMAP_KEY_STATE, }; static struct { int time_interval; sam_recovery_policy_t recovery_policy; enum sam_internal_status_t internal_status; unsigned int instance_id; int child_fd_out; int child_fd_in; int term_send; int warn_signal; int am_i_child; sam_hc_callback_t hc_callback; pthread_t cb_thread; int cb_rpipe_fd, cb_wpipe_fd; int cb_registered; void *user_data; size_t user_data_size; size_t user_data_allocated; pthread_mutex_t lock; quorum_handle_t quorum_handle; uint32_t quorate; int quorum_fd; cmap_handle_t cmap_handle; char cmap_pid_path[CMAP_KEYNAME_MAXLEN]; -} sam_internal_data; - -extern const char *__progname; +} sam_internal_data = { + .lock = PTHREAD_MUTEX_INITIALIZER +}; static cs_error_t sam_cmap_update_key (enum sam_cmap_key_t key, const char *value) { cs_error_t err; const char *svalue; uint64_t hc_period, last_hc; const char *ssvalue[] = { [SAM_RECOVERY_POLICY_QUIT] = "quit", [SAM_RECOVERY_POLICY_RESTART] = "restart" }; char key_name[CMAP_KEYNAME_MAXLEN]; switch (key) { case SAM_CMAP_KEY_RECOVERY: svalue = ssvalue[SAM_RP_MASK (sam_internal_data.recovery_policy)]; if (snprintf(key_name, CMAP_KEYNAME_MAXLEN, "%s%s", sam_internal_data.cmap_pid_path, "recovery") >= CMAP_KEYNAME_MAXLEN) { err = CS_ERR_NAME_TOO_LONG; goto exit_error; } if ((err = cmap_set_string(sam_internal_data.cmap_handle, key_name, svalue)) != CS_OK) { goto exit_error; } break; case SAM_CMAP_KEY_HC_PERIOD: hc_period = sam_internal_data.time_interval; if (snprintf(key_name, CMAP_KEYNAME_MAXLEN, "%s%s", sam_internal_data.cmap_pid_path, "poll_period") >= CMAP_KEYNAME_MAXLEN) { err = CS_ERR_NAME_TOO_LONG; goto exit_error; } if ((err = cmap_set_uint64(sam_internal_data.cmap_handle, key_name, hc_period)) != CS_OK) { goto exit_error; } break; case SAM_CMAP_KEY_LAST_HC: last_hc = cs_timestamp_get(); if (snprintf(key_name, CMAP_KEYNAME_MAXLEN, "%s%s", sam_internal_data.cmap_pid_path, "last_updated") >= CMAP_KEYNAME_MAXLEN) { err = CS_ERR_NAME_TOO_LONG; goto exit_error; } if ((err = cmap_set_uint64(sam_internal_data.cmap_handle, key_name, last_hc)) != CS_OK) { goto exit_error; } break; case SAM_CMAP_KEY_STATE: svalue = value; if (snprintf(key_name, CMAP_KEYNAME_MAXLEN, "%s%s", sam_internal_data.cmap_pid_path, "state") >= CMAP_KEYNAME_MAXLEN) { err = CS_ERR_NAME_TOO_LONG; goto exit_error; } if ((err = cmap_set_string(sam_internal_data.cmap_handle, key_name, svalue)) != CS_OK) { goto exit_error; } break; } return (CS_OK); exit_error: return (err); } static cs_error_t sam_cmap_destroy_pid_path (void) { cmap_iter_handle_t iter; cs_error_t err; char key_name[CMAP_KEYNAME_MAXLEN + 1]; err = cmap_iter_init(sam_internal_data.cmap_handle, sam_internal_data.cmap_pid_path, &iter); if (err != CS_OK) { goto error_exit; } while ((err = cmap_iter_next(sam_internal_data.cmap_handle, iter, key_name, NULL, NULL)) == CS_OK) { cmap_delete(sam_internal_data.cmap_handle, key_name); } err = cmap_iter_finalize(sam_internal_data.cmap_handle, iter); error_exit: return (err); } static cs_error_t sam_cmap_register (void) { cs_error_t err; cmap_handle_t cmap_handle; if ((err = cmap_initialize (&cmap_handle)) != CS_OK) { return (err); } snprintf(sam_internal_data.cmap_pid_path, CMAP_KEYNAME_MAXLEN, "resources.process.%d.", getpid()); sam_internal_data.cmap_handle = cmap_handle; if ((err = sam_cmap_update_key (SAM_CMAP_KEY_RECOVERY, NULL)) != CS_OK) { goto destroy_finalize_error; } if ((err = sam_cmap_update_key (SAM_CMAP_KEY_HC_PERIOD, NULL)) != CS_OK) { goto destroy_finalize_error; } return (CS_OK); destroy_finalize_error: sam_cmap_destroy_pid_path (); cmap_finalize (cmap_handle); return (err); } static void quorum_notification_fn ( quorum_handle_t handle, uint32_t quorate, uint64_t ring_id, uint32_t view_list_entries, uint32_t *view_list) { + sam_internal_data.quorate = quorate; } cs_error_t sam_initialize ( int time_interval, sam_recovery_policy_t recovery_policy) { quorum_callbacks_t quorum_callbacks; uint32_t quorum_type; - cs_error_t err; + cs_error_t ret; - if (sam_internal_data.internal_status != SAM_INTERNAL_STATUS_NOT_INITIALIZED) { - return (CS_ERR_BAD_HANDLE); - } + ret = CS_OK; if (SAM_RP_MASK (recovery_policy) != SAM_RECOVERY_POLICY_QUIT && SAM_RP_MASK (recovery_policy) != SAM_RECOVERY_POLICY_RESTART) { return (CS_ERR_INVALID_PARAM); } + pthread_mutex_lock (&sam_internal_data.lock); + + if (sam_internal_data.internal_status != SAM_INTERNAL_STATUS_NOT_INITIALIZED) { + ret = CS_ERR_BAD_HANDLE; + goto exit_mutex_unlock; + } + if (recovery_policy & SAM_RECOVERY_POLICY_QUORUM) { /* * Initialize quorum */ quorum_callbacks.quorum_notify_fn = quorum_notification_fn; - if ((err = quorum_initialize (&sam_internal_data.quorum_handle, &quorum_callbacks, &quorum_type)) != CS_OK) { - goto exit_error; + if ((ret = quorum_initialize (&sam_internal_data.quorum_handle, &quorum_callbacks, &quorum_type)) != CS_OK) { + goto exit_mutex_unlock; } - if ((err = quorum_trackstart (sam_internal_data.quorum_handle, CS_TRACK_CHANGES)) != CS_OK) { + if ((ret = quorum_trackstart (sam_internal_data.quorum_handle, CS_TRACK_CHANGES)) != CS_OK) { goto exit_error_quorum; } - if ((err = quorum_fd_get (sam_internal_data.quorum_handle, &sam_internal_data.quorum_fd)) != CS_OK) { + if ((ret = quorum_fd_get (sam_internal_data.quorum_handle, &sam_internal_data.quorum_fd)) != CS_OK) { goto exit_error_quorum; } /* * Dispatch initial quorate state */ - if ((err = quorum_dispatch (sam_internal_data.quorum_handle, CS_DISPATCH_ONE)) != CS_OK) { + if ((ret = quorum_dispatch (sam_internal_data.quorum_handle, CS_DISPATCH_ONE)) != CS_OK) { goto exit_error_quorum; } } sam_internal_data.recovery_policy = recovery_policy; sam_internal_data.time_interval = time_interval; sam_internal_data.internal_status = SAM_INTERNAL_STATUS_INITIALIZED; sam_internal_data.warn_signal = SIGTERM; sam_internal_data.am_i_child = 0; sam_internal_data.user_data = NULL; sam_internal_data.user_data_size = 0; sam_internal_data.user_data_allocated = 0; - pthread_mutex_init (&sam_internal_data.lock, NULL); + pthread_mutex_unlock (&sam_internal_data.lock); return (CS_OK); exit_error_quorum: quorum_finalize (sam_internal_data.quorum_handle); -exit_error: - return (err); + +exit_mutex_unlock: + pthread_mutex_unlock (&sam_internal_data.lock); + + return (ret); } /* * Wrapper on top of write(2) function. It handles EAGAIN and EINTR states and sends whole buffer if possible. */ static size_t sam_safe_write ( int d, const void *buf, size_t nbyte) { ssize_t bytes_write; ssize_t tmp_bytes_write; bytes_write = 0; do { tmp_bytes_write = write (d, (const char *)buf + bytes_write, (nbyte - bytes_write > SSIZE_MAX) ? SSIZE_MAX : nbyte - bytes_write); if (tmp_bytes_write == -1) { if (!(errno == EAGAIN || errno == EINTR)) return -1; } else { bytes_write += tmp_bytes_write; } } while (bytes_write != nbyte); return (bytes_write); } /* * Wrapper on top of read(2) function. It handles EAGAIN and EINTR states and reads whole buffer if possible. */ static size_t sam_safe_read ( int d, void *buf, size_t nbyte) { ssize_t bytes_read; ssize_t tmp_bytes_read; bytes_read = 0; do { tmp_bytes_read = read (d, (char *)buf + bytes_read, (nbyte - bytes_read > SSIZE_MAX) ? SSIZE_MAX : nbyte - bytes_read); if (tmp_bytes_read == -1) { if (!(errno == EAGAIN || errno == EINTR)) return -1; } else { bytes_read += tmp_bytes_read; } } while (bytes_read != nbyte && tmp_bytes_read != 0); return (bytes_read); } static cs_error_t sam_read_reply ( int child_fd_in) { char reply; cs_error_t err; if (sam_safe_read (sam_internal_data.child_fd_in, &reply, sizeof (reply)) != sizeof (reply)) { return (CS_ERR_LIBRARY); } switch (reply) { case SAM_REPLY_ERROR: /* * Read error and return that */ if (sam_safe_read (sam_internal_data.child_fd_in, &err, sizeof (err)) != sizeof (err)) { return (CS_ERR_LIBRARY); } return (err); break; case SAM_REPLY_OK: /* * Everything correct */ break; default: return (CS_ERR_LIBRARY); break; } return (CS_OK); } cs_error_t sam_data_getsize (size_t *size) { + cs_error_t ret; + + ret = CS_OK; + if (size == NULL) { return (CS_ERR_INVALID_PARAM); } + pthread_mutex_lock (&sam_internal_data.lock); + if (sam_internal_data.internal_status != SAM_INTERNAL_STATUS_INITIALIZED && sam_internal_data.internal_status != SAM_INTERNAL_STATUS_REGISTERED && sam_internal_data.internal_status != SAM_INTERNAL_STATUS_STARTED) { - return (CS_ERR_BAD_HANDLE); + ret = CS_ERR_BAD_HANDLE; + goto exit_mutex_unlock; } - pthread_mutex_lock (&sam_internal_data.lock); *size = sam_internal_data.user_data_size; +exit_mutex_unlock: pthread_mutex_unlock (&sam_internal_data.lock); - return (CS_OK); + return (ret); } cs_error_t sam_data_restore ( void *data, size_t size) { - cs_error_t err; + cs_error_t ret; - err = CS_OK; + ret = CS_OK; if (data == NULL) { return (CS_ERR_INVALID_PARAM); } + pthread_mutex_lock (&sam_internal_data.lock); + if (sam_internal_data.internal_status != SAM_INTERNAL_STATUS_INITIALIZED && sam_internal_data.internal_status != SAM_INTERNAL_STATUS_REGISTERED && sam_internal_data.internal_status != SAM_INTERNAL_STATUS_STARTED) { - return (CS_ERR_BAD_HANDLE); + ret = CS_ERR_BAD_HANDLE; + goto exit_mutex_unlock; } - pthread_mutex_lock (&sam_internal_data.lock); if (sam_internal_data.user_data_size == 0) { - err = CS_OK; + ret = CS_OK; - goto error_unlock; + goto exit_mutex_unlock; } if (size < sam_internal_data.user_data_size) { - err = CS_ERR_INVALID_PARAM; + ret = CS_ERR_INVALID_PARAM; - goto error_unlock; + goto exit_mutex_unlock; } memcpy (data, sam_internal_data.user_data, sam_internal_data.user_data_size); +exit_mutex_unlock: pthread_mutex_unlock (&sam_internal_data.lock); - return (CS_OK); - -error_unlock: - pthread_mutex_unlock (&sam_internal_data.lock); - - return (err); + return (ret); } -cs_error_t sam_data_store ( +static cs_error_t sam_data_store_nolock ( const void *data, size_t size) { cs_error_t err; char command; char *new_data; - if (sam_internal_data.internal_status != SAM_INTERNAL_STATUS_INITIALIZED && - sam_internal_data.internal_status != SAM_INTERNAL_STATUS_REGISTERED && - sam_internal_data.internal_status != SAM_INTERNAL_STATUS_STARTED) { + if (size >= SSIZE_MAX) { + return (CS_ERR_TOO_BIG); + } + if (sam_internal_data.internal_status != SAM_INTERNAL_STATUS_INITIALIZED && + sam_internal_data.internal_status != SAM_INTERNAL_STATUS_REGISTERED && + sam_internal_data.internal_status != SAM_INTERNAL_STATUS_STARTED) { return (CS_ERR_BAD_HANDLE); } - if (data == NULL) { size = 0; } - pthread_mutex_lock (&sam_internal_data.lock); - if (sam_internal_data.am_i_child) { /* * We are child so we must send data to parent */ command = SAM_COMMAND_DATA_STORE; if (sam_safe_write (sam_internal_data.child_fd_out, &command, sizeof (command)) != sizeof (command)) { - err = CS_ERR_LIBRARY; - - goto error_unlock; + return (CS_ERR_LIBRARY); } if (sam_safe_write (sam_internal_data.child_fd_out, &size, sizeof (size)) != sizeof (size)) { - err = CS_ERR_LIBRARY; - - goto error_unlock; + return (CS_ERR_LIBRARY); } if (data != NULL && sam_safe_write (sam_internal_data.child_fd_out, data, size) != size) { - err = CS_ERR_LIBRARY; - - goto error_unlock; + return (CS_ERR_LIBRARY); } /* * And wait for reply */ if ((err = sam_read_reply (sam_internal_data.child_fd_in)) != CS_OK) { - goto error_unlock; + return (err); } } /* * We are parent or we received OK reply from parent -> do required action */ if (data == NULL) { free (sam_internal_data.user_data); sam_internal_data.user_data = NULL; sam_internal_data.user_data_allocated = 0; sam_internal_data.user_data_size = 0; } else { if (sam_internal_data.user_data_allocated < size) { if ((new_data = realloc (sam_internal_data.user_data, size)) == NULL) { - err = CS_ERR_NO_MEMORY; - - goto error_unlock; + return (CS_ERR_NO_MEMORY); } sam_internal_data.user_data_allocated = size; } else { new_data = sam_internal_data.user_data; } sam_internal_data.user_data = new_data; sam_internal_data.user_data_size = size; memcpy (sam_internal_data.user_data, data, size); } - pthread_mutex_unlock (&sam_internal_data.lock); - return (CS_OK); +} + +cs_error_t sam_data_store ( + const void *data, + size_t size) +{ + cs_error_t ret; + + pthread_mutex_lock (&sam_internal_data.lock); + + ret = sam_data_store_nolock (data, size); -error_unlock: pthread_mutex_unlock (&sam_internal_data.lock); - return (err); + return (ret); } cs_error_t sam_start (void) { char command; - cs_error_t err; + cs_error_t ret; sam_recovery_policy_t recpol; + ret = CS_OK; + + pthread_mutex_lock (&sam_internal_data.lock); + if (sam_internal_data.internal_status != SAM_INTERNAL_STATUS_REGISTERED) { - return (CS_ERR_BAD_HANDLE); + ret = CS_ERR_BAD_HANDLE; + goto exit_mutex_unlock; } recpol = sam_internal_data.recovery_policy; - if (recpol & SAM_RECOVERY_POLICY_QUORUM || recpol & SAM_RECOVERY_POLICY_CMAP) { - pthread_mutex_lock (&sam_internal_data.lock); - } - command = SAM_COMMAND_START; if (sam_safe_write (sam_internal_data.child_fd_out, &command, sizeof (command)) != sizeof (command)) { - if (recpol & SAM_RECOVERY_POLICY_QUORUM || recpol & SAM_RECOVERY_POLICY_CMAP) { - pthread_mutex_unlock (&sam_internal_data.lock); - } - - return (CS_ERR_LIBRARY); + ret = CS_ERR_LIBRARY; + goto exit_mutex_unlock; } if (recpol & SAM_RECOVERY_POLICY_QUORUM || recpol & SAM_RECOVERY_POLICY_CMAP) { /* * Wait for parent reply */ - if ((err = sam_read_reply (sam_internal_data.child_fd_in)) != CS_OK) { - pthread_mutex_unlock (&sam_internal_data.lock); - - return (err); + if ((ret = sam_read_reply (sam_internal_data.child_fd_in)) != CS_OK) { + goto exit_mutex_unlock; } - - pthread_mutex_unlock (&sam_internal_data.lock); } - if (sam_internal_data.hc_callback) - if (sam_safe_write (sam_internal_data.cb_wpipe_fd, &command, sizeof (command)) != sizeof (command)) - return (CS_ERR_LIBRARY); + if (sam_internal_data.hc_callback) { + if (sam_safe_write (sam_internal_data.cb_wpipe_fd, &command, sizeof (command)) != sizeof (command)) { + ret = CS_ERR_LIBRARY; + goto exit_mutex_unlock; + } + } sam_internal_data.internal_status = SAM_INTERNAL_STATUS_STARTED; - return (CS_OK); +exit_mutex_unlock: + pthread_mutex_unlock (&sam_internal_data.lock); + + return (ret); } -cs_error_t sam_stop (void) +static cs_error_t sam_stop_nolock (void) { char command; - cs_error_t err; + cs_error_t cs_err; if (sam_internal_data.internal_status != SAM_INTERNAL_STATUS_STARTED) { return (CS_ERR_BAD_HANDLE); } command = SAM_COMMAND_STOP; - if (sam_internal_data.recovery_policy & SAM_RECOVERY_POLICY_CMAP) { - pthread_mutex_lock (&sam_internal_data.lock); - } - if (sam_safe_write (sam_internal_data.child_fd_out, &command, sizeof (command)) != sizeof (command)) { - if (sam_internal_data.recovery_policy & SAM_RECOVERY_POLICY_CMAP) { - pthread_mutex_unlock (&sam_internal_data.lock); - } - return (CS_ERR_LIBRARY); } if (sam_internal_data.recovery_policy & SAM_RECOVERY_POLICY_CMAP) { /* * Wait for parent reply */ - if ((err = sam_read_reply (sam_internal_data.child_fd_in)) != CS_OK) { - pthread_mutex_unlock (&sam_internal_data.lock); - - return (err); + if ((cs_err = sam_read_reply (sam_internal_data.child_fd_in)) != CS_OK) { + return (cs_err); } - - pthread_mutex_unlock (&sam_internal_data.lock); } - if (sam_internal_data.hc_callback) - if (sam_safe_write (sam_internal_data.cb_wpipe_fd, &command, sizeof (command)) != sizeof (command)) + if (sam_internal_data.hc_callback) { + if (sam_safe_write (sam_internal_data.cb_wpipe_fd, &command, sizeof (command)) != sizeof (command)) { return (CS_ERR_LIBRARY); + } + } sam_internal_data.internal_status = SAM_INTERNAL_STATUS_REGISTERED; return (CS_OK); } +cs_error_t sam_stop (void) +{ + cs_error_t ret; + + pthread_mutex_lock (&sam_internal_data.lock); + + ret = sam_stop_nolock (); + + pthread_mutex_unlock (&sam_internal_data.lock); + + return (ret); +} + cs_error_t sam_hc_send (void) { char command; + cs_error_t ret; + + ret = CS_OK; + + pthread_mutex_lock (&sam_internal_data.lock); if (sam_internal_data.internal_status != SAM_INTERNAL_STATUS_STARTED) { - return (CS_ERR_BAD_HANDLE); + ret = CS_ERR_BAD_HANDLE; + goto exit_mutex_unlock; } command = SAM_COMMAND_HB; - if (sam_safe_write (sam_internal_data.child_fd_out, &command, sizeof (command)) != sizeof (command)) - return (CS_ERR_LIBRARY); + if (sam_safe_write (sam_internal_data.child_fd_out, &command, sizeof (command)) != sizeof (command)) { + ret = CS_ERR_LIBRARY; + goto exit_mutex_unlock; + } - return (CS_OK); +exit_mutex_unlock: + pthread_mutex_unlock (&sam_internal_data.lock); + + return (ret); } cs_error_t sam_finalize (void) { - cs_error_t error; + cs_error_t ret; + + ret = CS_OK; + + pthread_mutex_lock (&sam_internal_data.lock); if (sam_internal_data.internal_status != SAM_INTERNAL_STATUS_INITIALIZED && - sam_internal_data.internal_status != SAM_INTERNAL_STATUS_REGISTERED && - sam_internal_data.internal_status != SAM_INTERNAL_STATUS_STARTED) { - return (CS_ERR_BAD_HANDLE); + sam_internal_data.internal_status != SAM_INTERNAL_STATUS_REGISTERED && + sam_internal_data.internal_status != SAM_INTERNAL_STATUS_STARTED) { + ret = CS_ERR_BAD_HANDLE; + goto exit_mutex_unlock; } if (sam_internal_data.internal_status == SAM_INTERNAL_STATUS_STARTED) { - error = sam_stop (); - if (error != CS_OK) - goto exit_error; + ret = sam_stop_nolock (); + if (ret != CS_OK) { + goto exit_mutex_unlock; + } } sam_internal_data.internal_status = SAM_INTERNAL_STATUS_FINALIZED; free (sam_internal_data.user_data); + sam_internal_data.user_data = NULL; + sam_internal_data.user_data_allocated = 0; + sam_internal_data.user_data_size = 0; -exit_error: - return (CS_OK); +exit_mutex_unlock: + pthread_mutex_unlock (&sam_internal_data.lock); + + return (ret); } cs_error_t sam_mark_failed (void) { char command; + cs_error_t ret; + + ret = CS_OK; + + pthread_mutex_lock (&sam_internal_data.lock); if (sam_internal_data.internal_status != SAM_INTERNAL_STATUS_STARTED && sam_internal_data.internal_status != SAM_INTERNAL_STATUS_REGISTERED) { - return (CS_ERR_BAD_HANDLE); + ret = CS_ERR_BAD_HANDLE; + goto exit_mutex_unlock; } if (!(sam_internal_data.recovery_policy & SAM_RECOVERY_POLICY_CMAP)) { - return (CS_ERR_INVALID_PARAM); + ret = CS_ERR_INVALID_PARAM; + goto exit_mutex_unlock; } command = SAM_COMMAND_MARK_FAILED; - if (sam_safe_write (sam_internal_data.child_fd_out, &command, sizeof (command)) != sizeof (command)) - return (CS_ERR_LIBRARY); + if (sam_safe_write (sam_internal_data.child_fd_out, &command, sizeof (command)) != sizeof (command)) { + ret = CS_ERR_LIBRARY; + goto exit_mutex_unlock; + } - return (CS_OK); +exit_mutex_unlock: + pthread_mutex_unlock (&sam_internal_data.lock); + + return (ret); } -cs_error_t sam_warn_signal_set (int warn_signal) +static cs_error_t sam_warn_signal_set_nolock (int warn_signal) { char command; cs_error_t err; if (sam_internal_data.internal_status != SAM_INTERNAL_STATUS_INITIALIZED && - sam_internal_data.internal_status != SAM_INTERNAL_STATUS_REGISTERED && - sam_internal_data.internal_status != SAM_INTERNAL_STATUS_STARTED) { + sam_internal_data.internal_status != SAM_INTERNAL_STATUS_REGISTERED && + sam_internal_data.internal_status != SAM_INTERNAL_STATUS_STARTED) { return (CS_ERR_BAD_HANDLE); } - pthread_mutex_lock (&sam_internal_data.lock); - if (sam_internal_data.am_i_child) { /* * We are child so we must send data to parent */ command = SAM_COMMAND_WARN_SIGNAL_SET; if (sam_safe_write (sam_internal_data.child_fd_out, &command, sizeof (command)) != sizeof (command)) { - err = CS_ERR_LIBRARY; - - goto error_unlock; + return (CS_ERR_BAD_HANDLE); } if (sam_safe_write (sam_internal_data.child_fd_out, &warn_signal, sizeof (warn_signal)) != sizeof (warn_signal)) { - err = CS_ERR_LIBRARY; - - goto error_unlock; + return (CS_ERR_LIBRARY); } /* * And wait for reply */ if ((err = sam_read_reply (sam_internal_data.child_fd_in)) != CS_OK) { - goto error_unlock; + return (err); } } /* * We are parent or we received OK reply from parent -> do required action */ sam_internal_data.warn_signal = warn_signal; - pthread_mutex_unlock (&sam_internal_data.lock); - return (CS_OK); +} + +cs_error_t sam_warn_signal_set (int warn_signal) +{ + cs_error_t ret; + + pthread_mutex_lock (&sam_internal_data.lock); + + ret = sam_warn_signal_set_nolock (warn_signal); -error_unlock: pthread_mutex_unlock (&sam_internal_data.lock); - return (err); + return (ret); } static cs_error_t sam_parent_reply_send ( cs_error_t err, int parent_fd_in, int parent_fd_out) { char reply; if (err == CS_OK) { reply = SAM_REPLY_OK; if (sam_safe_write (parent_fd_out, &reply, sizeof (reply)) != sizeof (reply)) { err = CS_ERR_LIBRARY; goto error_reply; } return (CS_OK); } error_reply: reply = SAM_REPLY_ERROR; if (sam_safe_write (parent_fd_out, &reply, sizeof (reply)) != sizeof (reply)) { return (CS_ERR_LIBRARY); } if (sam_safe_write (parent_fd_out, &err, sizeof (err)) != sizeof (err)) { return (CS_ERR_LIBRARY); } return (err); } static cs_error_t sam_parent_warn_signal_set ( int parent_fd_in, int parent_fd_out) { int warn_signal; cs_error_t err; err = CS_OK; if (sam_safe_read (parent_fd_in, &warn_signal, sizeof (warn_signal)) != sizeof (warn_signal)) { err = CS_ERR_LIBRARY; goto error_reply; } - err = sam_warn_signal_set (warn_signal); + err = sam_warn_signal_set_nolock (warn_signal); if (err != CS_OK) { goto error_reply; } return (sam_parent_reply_send (CS_OK, parent_fd_in, parent_fd_out)); error_reply: return (sam_parent_reply_send (err, parent_fd_in, parent_fd_out)); } static cs_error_t sam_parent_wait_for_quorum ( int parent_fd_in, int parent_fd_out) { cs_error_t err; struct pollfd pfds[2]; int poll_err; if (sam_internal_data.recovery_policy & SAM_RECOVERY_POLICY_CMAP) { if ((err = sam_cmap_update_key (SAM_CMAP_KEY_STATE, SAM_CMAP_S_Q_WAIT)) != CS_OK) { goto error_reply; } } /* * Update current quorum */ if ((err = quorum_dispatch (sam_internal_data.quorum_handle, CS_DISPATCH_ALL)) != CS_OK) { goto error_reply; } /* * Wait for quorum */ while (!sam_internal_data.quorate) { pfds[0].fd = parent_fd_in; pfds[0].events = 0; pfds[0].revents = 0; pfds[1].fd = sam_internal_data.quorum_fd; pfds[1].events = POLLIN; pfds[1].revents = 0; poll_err = poll (pfds, 2, -1); if (poll_err == -1) { /* * Error in poll * If it is EINTR, continue, otherwise QUIT */ if (errno != EINTR) { err = CS_ERR_LIBRARY; goto error_reply; } } if (pfds[0].revents != 0) { if (pfds[0].revents == POLLERR || pfds[0].revents == POLLHUP ||pfds[0].revents == POLLNVAL) { /* * Child has exited */ return (CS_OK); } } if (pfds[1].revents != 0) { if ((err = quorum_dispatch (sam_internal_data.quorum_handle, CS_DISPATCH_ONE)) != CS_OK) { goto error_reply; } } } if (sam_internal_data.recovery_policy & SAM_RECOVERY_POLICY_CMAP) { if ((err = sam_cmap_update_key (SAM_CMAP_KEY_STATE, SAM_CMAP_S_STARTED)) != CS_OK) { goto error_reply; } } return (sam_parent_reply_send (CS_OK, parent_fd_in, parent_fd_out)); error_reply: if (sam_internal_data.recovery_policy & SAM_RECOVERY_POLICY_CMAP) { sam_cmap_update_key (SAM_CMAP_KEY_STATE, SAM_CMAP_S_REGISTERED); } return (sam_parent_reply_send (err, parent_fd_in, parent_fd_out)); } static cs_error_t sam_parent_cmap_state_set ( int parent_fd_in, int parent_fd_out, int state) { cs_error_t err; const char *state_s; if (state == 1) { state_s = SAM_CMAP_S_STARTED; } else { state_s = SAM_CMAP_S_REGISTERED; } if ((err = sam_cmap_update_key (SAM_CMAP_KEY_STATE, state_s)) != CS_OK) { goto error_reply; } return (sam_parent_reply_send (CS_OK, parent_fd_in, parent_fd_out)); error_reply: return (sam_parent_reply_send (err, parent_fd_in, parent_fd_out)); } static cs_error_t sam_parent_kill_child ( int *action, pid_t child_pid) { /* * Kill child process */ if (!sam_internal_data.term_send) { /* * We didn't send warn_signal yet. */ kill (child_pid, sam_internal_data.warn_signal); sam_internal_data.term_send = 1; } else { /* * We sent child warning. Now, we will not be so nice */ kill (child_pid, SIGKILL); *action = SAM_PARENT_ACTION_RECOVERY; } return (CS_OK); } static cs_error_t sam_parent_mark_child_failed ( int *action, pid_t child_pid) { sam_recovery_policy_t recpol; recpol = sam_internal_data.recovery_policy; sam_internal_data.term_send = 1; sam_internal_data.recovery_policy = SAM_RECOVERY_POLICY_QUIT | (SAM_RP_MASK_C (recpol) ? SAM_RECOVERY_POLICY_CMAP : 0) | (SAM_RP_MASK_Q (recpol) ? SAM_RECOVERY_POLICY_QUORUM : 0); return (sam_parent_kill_child (action, child_pid)); } static cs_error_t sam_parent_data_store ( int parent_fd_in, int parent_fd_out) { char *user_data; ssize_t size; cs_error_t err; err = CS_OK; user_data = NULL; if (sam_safe_read (parent_fd_in, &size, sizeof (size)) != sizeof (size)) { err = CS_ERR_LIBRARY; goto error_reply; } + if (size >= SSIZE_MAX) { + err = CS_ERR_TOO_BIG; + goto error_reply; + } + if (size > 0) { user_data = malloc (size); if (user_data == NULL) { err = CS_ERR_NO_MEMORY; goto error_reply; } if (sam_safe_read (parent_fd_in, user_data, size) != size) { err = CS_ERR_LIBRARY; goto free_error_reply; } } - err = sam_data_store (user_data, size); + err = sam_data_store_nolock (user_data, size); if (err != CS_OK) { goto free_error_reply; } free (user_data); return (sam_parent_reply_send (CS_OK, parent_fd_in, parent_fd_out)); free_error_reply: free (user_data); error_reply: return (sam_parent_reply_send (err, parent_fd_in, parent_fd_out)); } static enum sam_parent_action_t sam_parent_handler ( int parent_fd_in, int parent_fd_out, pid_t child_pid) { int poll_error; int action; int status; ssize_t bytes_read; char command; int time_interval; struct pollfd pfds[2]; nfds_t nfds; cs_error_t err; sam_recovery_policy_t recpol; status = 0; action = SAM_PARENT_ACTION_CONTINUE; recpol = sam_internal_data.recovery_policy; while (action == SAM_PARENT_ACTION_CONTINUE) { pfds[0].fd = parent_fd_in; pfds[0].events = POLLIN; pfds[0].revents = 0; nfds = 1; if (status == 1 && sam_internal_data.time_interval != 0) { time_interval = sam_internal_data.time_interval; } else { time_interval = -1; } if (recpol & SAM_RECOVERY_POLICY_QUORUM) { pfds[nfds].fd = sam_internal_data.quorum_fd; pfds[nfds].events = POLLIN; pfds[nfds].revents = 0; nfds++; } poll_error = poll (pfds, nfds, time_interval); if (poll_error == -1) { /* * Error in poll * If it is EINTR, continue, otherwise QUIT */ if (errno != EINTR) { action = SAM_PARENT_ACTION_ERROR; } } if (poll_error == 0) { /* * Time limit expires */ if (status == 0) { action = SAM_PARENT_ACTION_QUIT; } else { sam_parent_kill_child (&action, child_pid); } } if (poll_error > 0) { if (pfds[0].revents != 0) { /* * We have EOF or command in pipe */ bytes_read = sam_safe_read (parent_fd_in, &command, 1); if (bytes_read == 0) { /* * Handle EOF -> Take recovery action or quit if sam_start wasn't called */ if (status == 0) action = SAM_PARENT_ACTION_QUIT; else action = SAM_PARENT_ACTION_RECOVERY; continue; } if (bytes_read == -1) { action = SAM_PARENT_ACTION_ERROR; goto action_exit; } if (recpol & SAM_RECOVERY_POLICY_CMAP) { sam_cmap_update_key (SAM_CMAP_KEY_LAST_HC, NULL); } /* * We have read command */ switch (command) { case SAM_COMMAND_START: if (status == 0) { /* * Not started yet */ if (recpol & SAM_RECOVERY_POLICY_QUORUM) { if (sam_parent_wait_for_quorum (parent_fd_in, parent_fd_out) != CS_OK) { continue; } } if (recpol & SAM_RECOVERY_POLICY_CMAP) { if (sam_parent_cmap_state_set (parent_fd_in, parent_fd_out, 1) != CS_OK) { continue; } } status = 1; } break; case SAM_COMMAND_STOP: if (status == 1) { /* * Started */ if (recpol & SAM_RECOVERY_POLICY_CMAP) { if (sam_parent_cmap_state_set (parent_fd_in, parent_fd_out, 0) != CS_OK) { continue; } } status = 0; } break; case SAM_COMMAND_DATA_STORE: sam_parent_data_store (parent_fd_in, parent_fd_out); break; case SAM_COMMAND_WARN_SIGNAL_SET: sam_parent_warn_signal_set (parent_fd_in, parent_fd_out); break; case SAM_COMMAND_MARK_FAILED: status = 1; sam_parent_mark_child_failed (&action, child_pid); break; } } /* if (pfds[0].revents != 0) */ if ((sam_internal_data.recovery_policy & SAM_RECOVERY_POLICY_QUORUM) && pfds[1].revents != 0) { /* * Handle quorum change */ err = quorum_dispatch (sam_internal_data.quorum_handle, CS_DISPATCH_ALL); if (status == 1 && (!sam_internal_data.quorate || (err != CS_ERR_TRY_AGAIN && err != CS_OK))) { sam_parent_kill_child (&action, child_pid); } } } /* select_error > 0 */ } /* action == SAM_PARENT_ACTION_CONTINUE */ action_exit: return action; } cs_error_t sam_register ( unsigned int *instance_id) { - cs_error_t error; pid_t pid; int pipe_error; int pipe_fd_out[2], pipe_fd_in[2]; enum sam_parent_action_t action, old_action; int child_status; sam_recovery_policy_t recpol; + cs_error_t ret; + + ret = CS_OK; + + pthread_mutex_lock (&sam_internal_data.lock); if (sam_internal_data.internal_status != SAM_INTERNAL_STATUS_INITIALIZED) { - return (CS_ERR_BAD_HANDLE); + ret = CS_ERR_BAD_HANDLE; + goto exit_mutex_unlock; } recpol = sam_internal_data.recovery_policy; if (recpol & SAM_RECOVERY_POLICY_CMAP) { /* * Register to cmap */ - if ((error = sam_cmap_register ()) != CS_OK) { - goto error_exit; + if ((ret = sam_cmap_register ()) != CS_OK) { + ret = CS_ERR_BAD_HANDLE; + goto exit_mutex_unlock; } } - error = CS_OK; - while (1) { if ((pipe_error = pipe (pipe_fd_out)) != 0) { - error = CS_ERR_LIBRARY; - goto error_exit; + ret = CS_ERR_LIBRARY; + goto exit_mutex_unlock; } if ((pipe_error = pipe (pipe_fd_in)) != 0) { close (pipe_fd_out[0]); close (pipe_fd_out[1]); - error = CS_ERR_LIBRARY; - goto error_exit; + ret = CS_ERR_BAD_HANDLE; + goto exit_mutex_unlock; } if (recpol & SAM_RECOVERY_POLICY_CMAP) { - if ((error = sam_cmap_update_key (SAM_CMAP_KEY_STATE, SAM_CMAP_S_REGISTERED)) != CS_OK) { - goto error_exit; + if ((ret = sam_cmap_update_key (SAM_CMAP_KEY_STATE, SAM_CMAP_S_REGISTERED)) != CS_OK) { + goto exit_mutex_unlock; } } sam_internal_data.instance_id++; sam_internal_data.term_send = 0; pid = fork (); if (pid == -1) { /* * Fork error */ sam_internal_data.instance_id--; - error = CS_ERR_LIBRARY; - goto error_exit; + ret = CS_ERR_BAD_HANDLE; + goto exit_mutex_unlock; } if (pid == 0) { /* * Child process */ close (pipe_fd_out[0]); close (pipe_fd_in[1]); sam_internal_data.child_fd_out = pipe_fd_out[1]; sam_internal_data.child_fd_in = pipe_fd_in[0]; if (instance_id) *instance_id = sam_internal_data.instance_id; sam_internal_data.am_i_child = 1; sam_internal_data.internal_status = SAM_INTERNAL_STATUS_REGISTERED; - pthread_mutex_init (&sam_internal_data.lock, NULL); - - goto error_exit; + goto exit_mutex_unlock; } else { /* * Parent process */ close (pipe_fd_out[1]); close (pipe_fd_in[0]); action = sam_parent_handler (pipe_fd_out[0], pipe_fd_in[1], pid); close (pipe_fd_out[0]); close (pipe_fd_in[1]); if (action == SAM_PARENT_ACTION_ERROR) { - error = CS_ERR_LIBRARY; - goto error_exit; + ret = CS_ERR_LIBRARY; + goto exit_mutex_unlock; } /* * We really don't like zombies */ while (waitpid (pid, &child_status, 0) == -1 && errno == EINTR) ; old_action = action; if (action == SAM_PARENT_ACTION_RECOVERY) { if (SAM_RP_MASK (sam_internal_data.recovery_policy) == SAM_RECOVERY_POLICY_QUIT) action = SAM_PARENT_ACTION_QUIT; } if (action == SAM_PARENT_ACTION_QUIT) { if (recpol & SAM_RECOVERY_POLICY_QUORUM) { quorum_finalize (sam_internal_data.quorum_handle); } if (recpol & SAM_RECOVERY_POLICY_CMAP) { if (old_action == SAM_PARENT_ACTION_RECOVERY) { /* * Mark as failed */ sam_cmap_update_key (SAM_CMAP_KEY_STATE, SAM_CMAP_S_FAILED); } else { sam_cmap_destroy_pid_path (); } } exit (WEXITSTATUS (child_status)); } } } -error_exit: - return (error); +exit_mutex_unlock: + pthread_mutex_unlock (&sam_internal_data.lock); + + return (ret); } static void *hc_callback_thread (void *unused_param) { int poll_error; int status; ssize_t bytes_readed; char command; int time_interval, tmp_time_interval; int counter; struct pollfd pfds; status = 0; counter = 0; time_interval = sam_internal_data.time_interval >> 2; while (1) { pfds.fd = sam_internal_data.cb_rpipe_fd; pfds.events = POLLIN; pfds.revents = 0; if (status == 1) { tmp_time_interval = time_interval; } else { tmp_time_interval = -1; } poll_error = poll (&pfds, 1, tmp_time_interval); if (poll_error == 0) { if (sam_hc_send () == CS_OK) { counter++; } if (counter >= 4) { if (sam_internal_data.hc_callback () != 0) { status = 3; } counter = 0; } } if (poll_error > 0) { bytes_readed = sam_safe_read (sam_internal_data.cb_rpipe_fd, &command, 1); if (bytes_readed > 0) { if (status == 0 && command == SAM_COMMAND_START) status = 1; if (status == 1 && command == SAM_COMMAND_STOP) status = 0; } } } /* * This makes compiler happy, it's same as return (NULL); */ return (unused_param); } cs_error_t sam_hc_callback_register (sam_hc_callback_t cb) { cs_error_t error = CS_OK; pthread_attr_t thread_attr; int pipe_error; int pipe_fd[2]; if (sam_internal_data.internal_status != SAM_INTERNAL_STATUS_REGISTERED) { return (CS_ERR_BAD_HANDLE); } if (sam_internal_data.time_interval == 0) { return (CS_ERR_INVALID_PARAM); } if (sam_internal_data.cb_registered) { sam_internal_data.hc_callback = cb; return (CS_OK); } /* * We know, this is first registration */ if (cb == NULL) { return (CS_ERR_INVALID_PARAM); } pipe_error = pipe (pipe_fd); if (pipe_error != 0) { /* * Pipe creation error */ error = CS_ERR_LIBRARY; goto error_exit; } sam_internal_data.cb_rpipe_fd = pipe_fd[0]; sam_internal_data.cb_wpipe_fd = pipe_fd[1]; /* * Create thread attributes */ error = pthread_attr_init (&thread_attr); if (error != 0) { error = CS_ERR_LIBRARY; goto error_close_fd_exit; } pthread_attr_setdetachstate (&thread_attr, PTHREAD_CREATE_DETACHED); pthread_attr_setstacksize (&thread_attr, 32768); /* * Create thread */ error = pthread_create (&sam_internal_data.cb_thread, &thread_attr, hc_callback_thread, NULL); if (error != 0) { error = CS_ERR_LIBRARY; goto error_attr_destroy_exit; } /* * Cleanup */ pthread_attr_destroy(&thread_attr); sam_internal_data.cb_registered = 1; sam_internal_data.hc_callback = cb; return (CS_OK); error_attr_destroy_exit: pthread_attr_destroy(&thread_attr); error_close_fd_exit: sam_internal_data.cb_rpipe_fd = sam_internal_data.cb_wpipe_fd = 0; close (pipe_fd[0]); close (pipe_fd[1]); error_exit: return (error); } diff --git a/test/testsam.c b/test/testsam.c index d31262ad..ee16e064 100644 --- a/test/testsam.c +++ b/test/testsam.c @@ -1,1476 +1,1446 @@ /* - * Copyright (c) 2009-2011 Red Hat, Inc. + * Copyright (c) 2009-2025 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@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 Red Hat, 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. */ /* * Provides test of SAM API */ #include +#include +#include + #include #include -#include #include #include #include #include #include #include #include #include #include #include -extern const char *__progname; -static int test2_sig_delivered = 0; -static int test5_hc_cb_count = 0; -static int test6_sig_delivered = 0; +static int test_sig_delivered = 0; +static int test_hc_cb_count = 0; /* * First test will just register SAM, with policy restart. First instance will * sleep one second, send hc and sleep another 3 seconds. This should force restart. * Second instance will sleep one second, send hc, stop hc and sleep 3 seconds. * Then start hc again and sleep 3 seconds. This should force restart again. * Last instance just calls initialize again. This should end with error. * Then call start, followed by stop and start again. Finally, we will call finalize * twice. One should succeed, second should fail. After this, we will call every function * (none should succeed). */ -static int test1 (void) +static int test_basics (void) { cs_error_t error; unsigned int instance_id; int i; printf ("%s: initialize\n", __FUNCTION__); error = sam_initialize (2000, SAM_RECOVERY_POLICY_RESTART); if (error != CS_OK) { fprintf (stderr, "Can't initialize SAM API. Error %d\n", error); return 1; } printf ("%s: register\n", __FUNCTION__); error = sam_register (&instance_id); if (error != CS_OK) { fprintf (stderr, "Can't register. Error %d\n", error); return 1; } if (instance_id == 1 || instance_id == 2) { printf ("%s iid %d: start\n", __FUNCTION__, instance_id); error = sam_start (); if (error != CS_OK) { fprintf (stderr, "Can't start hc. Error %d\n", error); return 1; } for (i = 0; i < 10; i++) { printf ("%s iid %d: sleep 1\n", __FUNCTION__, instance_id); sleep (1); printf ("%s iid %d: hc send\n", __FUNCTION__, instance_id); error = sam_hc_send (); if (error != CS_OK) { fprintf (stderr, "Can't send hc. Error %d\n", error); return 1; } } if (instance_id == 2) { printf ("%s iid %d: stop\n", __FUNCTION__, instance_id); error = sam_stop (); if (error != CS_OK) { - fprintf (stderr, "Can't send hc. Error %d\n", error); + fprintf (stderr, "Can't stop hc. Error %d\n", error); return 1; } } printf ("%s iid %d: sleep 3\n", __FUNCTION__, instance_id); sleep (3); + if (instance_id == 1) { + printf ("%s iid %d: Failed. Wasn't killed.\n", __FUNCTION__, instance_id); + + return 1; + } + printf ("%s iid %d: start\n", __FUNCTION__, instance_id); error = sam_start (); if (error != CS_OK) { fprintf (stderr, "Can't start hc. Error %d\n", error); return 1; } printf ("%s iid %d: sleep 3\n", __FUNCTION__, instance_id); sleep (3); return 0; } if (instance_id == 3) { error = sam_initialize (2000, SAM_RECOVERY_POLICY_RESTART); if (error == CS_OK) { fprintf (stderr, "Can initialize SAM API after initialization"); return 1; } error = sam_start (); if (error != CS_OK) { fprintf (stderr, "Can't start hc. Error %d\n", error); return 1; } error = sam_stop (); if (error != CS_OK) { fprintf (stderr, "Can't stop hc. Error %d\n", error); return 1; } error = sam_finalize (); if (error != CS_OK) { fprintf (stderr, "Can't finalize sam. Error %d\n", error); return 1; } error = sam_finalize (); if (error == CS_OK) { fprintf (stderr, "Can finalize sam after finalization!\n"); return 1; } if (sam_initialize (2, SAM_RECOVERY_POLICY_RESTART) == CS_OK || sam_start () == CS_OK || sam_stop () == CS_OK || sam_register (NULL) == CS_OK || sam_hc_send () == CS_OK || sam_hc_callback_register (NULL) == CS_OK) { fprintf (stderr, "Can call one of function after finalization!\n"); return 1; } return 0; } return 1; } -static void test2_signal (int sig) { +static void test_signal_handler (int sig) { printf ("%s\n", __FUNCTION__); - test2_sig_delivered = 1; + test_sig_delivered = 1; } /* * This tests recovery policy quit and callback. */ -static int test2 (void) { +static int test_signal (void) { cs_error_t error; unsigned int instance_id; + test_sig_delivered = 0; + printf ("%s: initialize\n", __FUNCTION__); error = sam_initialize (2000, SAM_RECOVERY_POLICY_QUIT); if (error != CS_OK) { fprintf (stderr, "Can't initialize SAM API. Error %d\n", error); return 1; } printf ("%s: register\n", __FUNCTION__); error = sam_register (&instance_id); if (error != CS_OK) { fprintf (stderr, "Can't register. Error %d\n", error); return 1; } if (instance_id == 1) { - signal (SIGTERM, test2_signal); + signal (SIGTERM, test_signal_handler); printf ("%s iid %d: start\n", __FUNCTION__, instance_id); error = sam_start (); if (error != CS_OK) { fprintf (stderr, "Can't start hc. Error %d\n", error); return 1; } printf ("%s iid %d: sleep 1\n", __FUNCTION__, instance_id); sleep (1); printf ("%s iid %d: hc send\n", __FUNCTION__, instance_id); error = sam_hc_send (); if (error != CS_OK) { fprintf (stderr, "Can't send hc. Error %d\n", error); return 1; } printf ("%s iid %d: wait for delivery of signal\n", __FUNCTION__, instance_id); - while (!test2_sig_delivered) { + while (!test_sig_delivered) { sleep (1); } printf ("%s iid %d: wait for real kill\n", __FUNCTION__, instance_id); sleep (3); } return 1; - } + /* - * Smoke test. Better to turn off coredump ;) This has no time limit, just restart process + * Smoke test. This has no time limit, just restart process * when it dies. */ -static int test3 (void) { +static int test_smoke (void) { cs_error_t error; unsigned int instance_id; + struct rlimit lim; + + lim.rlim_cur = lim.rlim_max = 0; + + /* + * Try to turn off core creation + */ + setrlimit(RLIMIT_CORE, &lim); printf ("%s: initialize\n", __FUNCTION__); error = sam_initialize (0, SAM_RECOVERY_POLICY_RESTART); if (error != CS_OK) { fprintf (stderr, "Can't initialize SAM API. Error %d\n", error); return 1; } printf ("%s: register\n", __FUNCTION__); error = sam_register (&instance_id); if (error != CS_OK) { fprintf (stderr, "Can't register. Error %d\n", error); return 1; } if (instance_id < 100) { printf ("%s iid %d: start\n", __FUNCTION__, instance_id); error = sam_start (); if (error != CS_OK) { fprintf (stderr, "Can't start hc. Error %d\n", error); return 1; } printf ("%s iid %d: Sending signal\n", __FUNCTION__, instance_id); kill(getpid(), SIGSEGV); return 1; } return 0; } /* * Test sam_data_store, sam_data_restore and sam_data_getsize */ -static int test4 (void) +static int test_data_store_restore (void) { size_t size; cs_error_t err; int i; unsigned int instance_id; char saved_data[128]; char saved_data2[128]; printf ("%s: sam_data_getsize 1\n", __FUNCTION__); err = sam_data_getsize (&size); if (err != CS_ERR_BAD_HANDLE) { fprintf (stderr, "Test should return CS_ERR_BAD_HANDLE. Error returned %d\n", err); return 1; } printf ("%s: sam_data_getsize 2\n", __FUNCTION__); err = sam_data_getsize (NULL); if (err != CS_ERR_INVALID_PARAM) { fprintf (stderr, "Test should return CS_ERR_INVALID_PARAM. Error returned %d\n", err); return 1; } printf ("%s: sam_data_store 1\n", __FUNCTION__); err = sam_data_store (NULL, 0); if (err != CS_ERR_BAD_HANDLE) { fprintf (stderr, "Test should return CS_ERR_BAD_HANDLE. Error returned %d\n", err); return 1; } printf ("%s: sam_data_restore 1\n", __FUNCTION__); err = sam_data_restore (saved_data, sizeof (saved_data)); if (err != CS_ERR_BAD_HANDLE) { fprintf (stderr, "Test should return CS_ERR_BAD_HANDLE. Error returned %d\n", err); return 1; } printf ("%s: sam_initialize\n", __FUNCTION__); err = sam_initialize (0, SAM_RECOVERY_POLICY_RESTART); if (err != CS_OK) { fprintf (stderr, "Can't initialize SAM API. Error %d\n", err); return 1; } printf ("%s: sam_data_getsize 3\n", __FUNCTION__); err = sam_data_getsize (&size); if (err != CS_OK) { fprintf (stderr, "Test should return CS_ERR_BAD_HANDLE. Error returned %d\n", err); return 1; } if (size != 0) { fprintf (stderr, "Test should return size of 0. Returned %zx\n", size); return 1; } printf ("%s: sam_data_restore 2\n", __FUNCTION__); err = sam_data_restore (NULL, sizeof (saved_data)); if (err != CS_ERR_INVALID_PARAM) { fprintf (stderr, "Test should return CS_ERR_INVALID_PARAM. Error returned %d\n", err); return 1; } /* * Store some real data */ for (i = 0; i < sizeof (saved_data); i++) { saved_data[i] = (char)(i + 5); } printf ("%s: sam_data_store 2\n", __FUNCTION__); err = sam_data_store (saved_data, sizeof (saved_data)); if (err != CS_OK) { fprintf (stderr, "Test should return CS_OK. Error returned %d\n", err); return 1; } printf ("%s: sam_data_getsize 4\n", __FUNCTION__); err = sam_data_getsize (&size); if (err != CS_OK) { fprintf (stderr, "Test should return CS_OK. Error returned %d\n", err); return 1; } if (size != sizeof (saved_data)) { fprintf (stderr, "Test should return size of 0. Returned %zx\n", size); return 1; } printf ("%s: sam_data_restore 3\n", __FUNCTION__); err = sam_data_restore (saved_data2, sizeof (saved_data2) - 1); if (err != CS_ERR_INVALID_PARAM) { fprintf (stderr, "Test should return CS_ERR_INVALID_PARAM. Error returned %d\n", err); return 1; } printf ("%s: sam_data_restore 4\n", __FUNCTION__); err = sam_data_restore (saved_data2, sizeof (saved_data2)); if (err != CS_OK) { fprintf (stderr, "Test should return CS_OK. Error returned %d\n", err); return 1; } if (memcmp (saved_data, saved_data2, sizeof (saved_data2)) != 0) { fprintf (stderr, "Retored data are not same\n"); return 1; } memset (saved_data2, 0, sizeof (saved_data2)); printf ("%s: sam_data_store 3\n", __FUNCTION__); err = sam_data_store (NULL, 1); if (err != CS_OK) { fprintf (stderr, "Test should return CS_OK. Error returned %d\n", err); return 1; } printf ("%s: sam_data_getsize 5\n", __FUNCTION__); err = sam_data_getsize (&size); if (err != CS_OK) { fprintf (stderr, "Test should return CS_OK. Error returned %d\n", err); return 1; } if (size != 0) { fprintf (stderr, "Test should return size of 0. Returned %zx\n", size); return 1; } printf ("%s: sam_data_store 4\n", __FUNCTION__); err = sam_data_store (saved_data, sizeof (saved_data)); if (err != CS_OK) { fprintf (stderr, "Test should return CS_OK. Error returned %d\n", err); return 1; } printf ("%s: register\n", __FUNCTION__); err = sam_register (&instance_id); if (err != CS_OK) { fprintf (stderr, "Can't register. Error %d\n", err); return 1; } if (instance_id == 1) { printf ("%s iid %d: sam_start\n", __FUNCTION__, instance_id); err = sam_start (); if (err != CS_OK) { fprintf (stderr, "Can't start hc. Error %d\n", err); return 1; } printf ("%s iid %d: sam_data_getsize 6\n", __FUNCTION__, instance_id); err = sam_data_getsize (&size); if (err != CS_OK) { fprintf (stderr, "Test should return CS_OK. Error returned %d\n", err); return 1; } if (size != sizeof (saved_data2)) { fprintf (stderr, "Test should return size of 0. Returned %zx\n", size); return 1; } printf ("%s iid %d: sam_data_restore 5\n", __FUNCTION__, instance_id); err = sam_data_restore (saved_data2, sizeof (saved_data2)); if (err != CS_OK) { fprintf (stderr, "Test should return CS_OK. Error returned %d\n", err); return 1; } if (memcmp (saved_data, saved_data2, sizeof (saved_data2)) != 0) { fprintf (stderr, "Retored data are not same\n"); return 1; } for (i = 0; i < sizeof (saved_data); i++) { saved_data[i] = (char)(i - 5); } printf ("%s iid %d: sam_data_store 5\n", __FUNCTION__, instance_id); err = sam_data_store (saved_data, sizeof (saved_data) - 7); if (err != CS_OK) { fprintf (stderr, "Test should return CS_OK. Error returned %d\n", err); return 1; } exit (1); } if (instance_id == 2) { printf ("%s iid %d: sam_start\n", __FUNCTION__, instance_id); err = sam_start (); if (err != CS_OK) { fprintf (stderr, "Can't start hc. Error %d\n", err); return 1; } printf ("%s iid %d: sam_data_getsize 7\n", __FUNCTION__, instance_id); err = sam_data_getsize (&size); if (err != CS_OK) { fprintf (stderr, "Test should return CS_OK. Error returned %d\n", err); return 1; } if (size != sizeof (saved_data2) - 7) { fprintf (stderr, "Test should return size of 0. Returned %zx\n", size); return 1; } printf ("%s iid %d: sam_data_restore 6\n", __FUNCTION__, instance_id); err = sam_data_restore (saved_data2, sizeof (saved_data2)); if (err != CS_OK) { fprintf (stderr, "Test should return CS_OK. Error returned %d\n", err); return 1; } for (i = 0; i < sizeof (saved_data); i++) { saved_data[i] = (char)(i - 5); } if (memcmp (saved_data, saved_data2, sizeof (saved_data2) - 7) != 0) { fprintf (stderr, "Retored data are not same\n"); return 1; } printf ("%s iid %d: sam_data_store 6\n", __FUNCTION__, instance_id); err = sam_data_store (NULL, 0); if (err != CS_OK) { fprintf (stderr, "Test should return CS_OK. Error returned %d\n", err); return 1; } exit (1); } if (instance_id == 3) { printf ("%s iid %d: sam_data_getsize 8\n", __FUNCTION__, instance_id); err = sam_data_getsize (&size); if (err != CS_OK) { fprintf (stderr, "Test should return CS_OK. Error returned %d\n", err); return 1; } if (size != 0) { fprintf (stderr, "Test should return size of 0. Returned %zx\n", size); return 1; } } return (0); } -static int test5_hc_cb (void) +static int test_hc_cb (void) { cs_error_t res; - printf ("%s %d\n", __FUNCTION__, ++test5_hc_cb_count); + printf ("%s %d\n", __FUNCTION__, ++test_hc_cb_count); - res = sam_data_store (&test5_hc_cb_count, sizeof (test5_hc_cb_count)); + res = sam_data_store (&test_hc_cb_count, sizeof (test_hc_cb_count)); if (res != CS_OK) return 1; - if (test5_hc_cb_count > 10) + if (test_hc_cb_count > 10) return 1; return 0; } /* * Test event driven healtchecking. */ -static int test5 (void) +static int test_hc (void) { cs_error_t error; unsigned int instance_id; int hc_cb_count; printf ("%s: initialize\n", __FUNCTION__); error = sam_initialize (100, SAM_RECOVERY_POLICY_RESTART); if (error != CS_OK) { fprintf (stderr, "Can't initialize SAM API. Error %d\n", error); return 1; } printf ("%s: register\n", __FUNCTION__); error = sam_register (&instance_id); if (error != CS_OK) { fprintf (stderr, "Can't register. Error %d\n", error); return 1; } if (instance_id == 1) { printf ("%s iid %d: hc callback register\n", __FUNCTION__, instance_id); - error = sam_hc_callback_register (test5_hc_cb); + error = sam_hc_callback_register (test_hc_cb); if (error != CS_OK) { fprintf (stderr, "Can't register hc cb. Error %d\n", error); return 1; } printf ("%s iid %d: start\n", __FUNCTION__, instance_id); error = sam_start (); if (error != CS_OK) { fprintf (stderr, "Can't start hc. Error %d\n", error); return 1; } sleep (2); printf ("%s iid %d: Failed. Wasn't killed.\n", __FUNCTION__, instance_id); return 1; } if (instance_id == 2) { error = sam_data_restore (&hc_cb_count, sizeof (hc_cb_count)); if (error != CS_OK) { fprintf (stderr, "sam_data_restore should return CS_OK. Error returned %d\n", error); return 1; } if (hc_cb_count != 11) { fprintf (stderr, "%s iid %d: Premature killed. hc_cb_count should be 11 and it is %d\n", __FUNCTION__, instance_id - 1, hc_cb_count); return 1; } return 0; } return 1; } -static void test6_signal (int sig) { +static void test_warn_signal_handler (int sig) { cs_error_t error; printf ("%s\n", __FUNCTION__); - test6_sig_delivered++; + test_sig_delivered++; - if ((error = sam_data_store (&test6_sig_delivered, sizeof (test6_sig_delivered))) != CS_OK) { + if ((error = sam_data_store (&test_sig_delivered, sizeof (test_sig_delivered))) != CS_OK) { fprintf (stderr, "Can't store data! Error : %d\n", error); } } /* * Test warn signal set. */ -static int test6 (void) { +static int test_warn_signal (void) { cs_error_t error; unsigned int instance_id; - int test6_sig_del; + int tmp_sig_delivered; + + test_sig_delivered = 0; printf ("%s: initialize\n", __FUNCTION__); error = sam_initialize (2000, SAM_RECOVERY_POLICY_RESTART); if (error != CS_OK) { fprintf (stderr, "Can't initialize SAM API. Error %d\n", error); return 1; } printf ("%s: register\n", __FUNCTION__); error = sam_register (&instance_id); if (error != CS_OK) { fprintf (stderr, "Can't register. Error %d\n", error); return 1; } if (instance_id == 1) { error = sam_warn_signal_set (SIGUSR1); if (error != CS_OK) { fprintf (stderr, "Can't set warn signal. Error %d\n", error); return 1; } - signal (SIGUSR1, test6_signal); + signal (SIGUSR1, test_warn_signal_handler); printf ("%s iid %d: start\n", __FUNCTION__, instance_id); error = sam_start (); if (error != CS_OK) { fprintf (stderr, "Can't start hc. Error %d\n", error); return 1; } printf ("%s iid %d: sleep 1\n", __FUNCTION__, instance_id); sleep (1); printf ("%s iid %d: hc send\n", __FUNCTION__, instance_id); error = sam_hc_send (); if (error != CS_OK) { fprintf (stderr, "Can't send hc. Error %d\n", error); return 1; } printf ("%s iid %d: wait for delivery of signal\n", __FUNCTION__, instance_id); - while (!test6_sig_delivered) { + while (!test_sig_delivered) { sleep (1); } printf ("%s iid %d: wait for real kill\n", __FUNCTION__, instance_id); sleep (3); printf ("%s iid %d: wasn't killed\n", __FUNCTION__, instance_id); return (1); } if (instance_id == 2) { - error = sam_data_restore (&test6_sig_del, sizeof (test6_sig_del)); + error = sam_data_restore (&tmp_sig_delivered, sizeof (tmp_sig_delivered)); if (error != CS_OK) { fprintf (stderr, "Can't restore data. Error %d\n", error); return 1; } - if (test6_sig_del != 1) { + if (tmp_sig_delivered != 1) { fprintf (stderr, "Previous test failed. Signal was not delivered\n"); return 1; } error = sam_warn_signal_set (SIGKILL); if (error != CS_OK) { fprintf (stderr, "Can't set warn signal. Error %d\n", error); return 1; } - signal (SIGUSR1, test6_signal); + signal (SIGUSR1, test_warn_signal_handler); printf ("%s iid %d: start\n", __FUNCTION__, instance_id); error = sam_start (); if (error != CS_OK) { fprintf (stderr, "Can't start hc. Error %d\n", error); return 1; } printf ("%s iid %d: sleep 1\n", __FUNCTION__, instance_id); sleep (1); printf ("%s iid %d: hc send\n", __FUNCTION__, instance_id); error = sam_hc_send (); if (error != CS_OK) { fprintf (stderr, "Can't send hc. Error %d\n", error); return 1; } printf ("%s iid %d: wait for delivery of signal\n", __FUNCTION__, instance_id); - while (!test6_sig_delivered) { + while (!test_sig_delivered) { sleep (1); } printf ("%s iid %d: wasn't killed\n", __FUNCTION__, instance_id); return (1); } if (instance_id == 3) { - error = sam_data_restore (&test6_sig_del, sizeof (test6_sig_del)); + error = sam_data_restore (&tmp_sig_delivered, sizeof (tmp_sig_delivered)); if (error != CS_OK) { fprintf (stderr, "Can't restore data. Error %d\n", error); return 1; } - if (test6_sig_del != 1) { + if (tmp_sig_delivered != 1) { fprintf (stderr, "Previous test failed. Signal WAS delivered\n"); return 1; } return (0); } return 1; } -static void *test7_thread (void *arg) +static void *test_thread_sleep_exit (void *arg) { /* Wait 5s */ sleep (5); exit (0); } /* * Test quorum */ -static int test7 (void) { +static int test_quorum (void) { cmap_handle_t cmap_handle; cs_error_t err; unsigned int instance_id; pthread_t kill_thread; char *str; + uint32_t expected_votes; err = cmap_initialize (&cmap_handle); if (err != CS_OK) { printf ("Could not initialize Cluster Map API instance error %d. Test skipped\n", err); return (1); } if (cmap_get_string(cmap_handle, "quorum.provider", &str) != CS_OK) { printf ("Could not get \"provider\" key: %d. Test skipped\n", err); return (1); } - if (strcmp(str, "testquorum") != 0) { - printf ("Provider is not testquorum. Test skipped\n"); + if (strcmp(str, "corosync_votequorum") != 0) { + printf ("Provider is not corosync_votequorum. Test skipped\n"); free(str); return (1); } free(str); + if (cmap_get_uint32(cmap_handle, "quorum.expected_votes", &expected_votes) != CS_OK) { + printf ("Could not get \"expected_votes\" key: %d. Test skipped\n", err); + return (1); + } + + if (expected_votes != 1) { + printf ("Expected_votes is not 1. Test skipped\n"); + return (1); + } + /* * Set to not quorate */ - err = cmap_set_uint8(cmap_handle, "quorum.quorate", 0); + err = cmap_set_uint32(cmap_handle, "quorum.expected_votes", 2); if (err != CS_OK) { printf ("Can't set map key. Error %d\n", err); return (2); } printf ("%s: initialize\n", __FUNCTION__); err = sam_initialize (2000, SAM_RECOVERY_POLICY_QUORUM_RESTART); if (err != CS_OK) { fprintf (stderr, "Can't initialize SAM API. Error %d\n", err); return 2; } printf ("%s: register\n", __FUNCTION__); err = sam_register (&instance_id); if (err != CS_OK) { fprintf (stderr, "Can't register. Error %d\n", err); return 2; } if (instance_id == 1) { /* - * Sam start should block forever, but 10s for us should be enough + * Sam start should block forever, but 5s for us should be enough */ - pthread_create (&kill_thread, NULL, test7_thread, NULL); + pthread_create (&kill_thread, NULL, test_thread_sleep_exit, NULL); printf ("%s iid %d: start - should block forever (waiting 5s)\n", __FUNCTION__, instance_id); err = sam_start (); if (err != CS_OK) { fprintf (stderr, "Can't start hc. Error %d\n", err); return 2; } printf ("%s iid %d: wasn't killed\n", __FUNCTION__, instance_id); return (2); } if (instance_id == 2) { /* * Set to quorate */ - err = cmap_set_uint8(cmap_handle, "quorum.quorate", 1); + err = cmap_set_uint32(cmap_handle, "quorum.expected_votes", 1); if (err != CS_OK) { printf ("Can't set map key. Error %d\n", err); return (2); } printf ("%s iid %d: start\n", __FUNCTION__, instance_id); err = sam_start (); if (err != CS_OK) { fprintf (stderr, "Can't start hc. Error %d\n", err); return 2; } /* * Set corosync unquorate */ - err = cmap_set_uint8(cmap_handle, "quorum.quorate", 0); + err = cmap_set_uint32(cmap_handle, "quorum.expected_votes", 2); if (err != CS_OK) { printf ("Can't set map key. Error %d\n", err); return (2); } printf ("%s iid %d: sleep 3\n", __FUNCTION__, instance_id); sleep (3); printf ("%s iid %d: wasn't killed\n", __FUNCTION__, instance_id); return (2); } if (instance_id == 3) { return (0); } return (2); } +static int test_finalize_deadlock (void) { + cs_error_t error; + unsigned int instance_id; + pthread_t kill_thread; + + test_sig_delivered = 0; + + printf ("%s: initialize\n", __FUNCTION__); + error = sam_initialize (2000, SAM_RECOVERY_POLICY_RESTART); + if (error != CS_OK) { + fprintf (stderr, "Can't initialize SAM API. Error %d\n", error); + return 1; + } + printf ("%s: register\n", __FUNCTION__); + error = sam_register (&instance_id); + if (error != CS_OK) { + fprintf (stderr, "Can't register. Error %d\n", error); + return 1; + } + + if (instance_id == 1) { + /* + * Sam finalize might block forever, but 5s for us should be enough + */ + pthread_create (&kill_thread, NULL, test_thread_sleep_exit, NULL); + + printf ("%s iid %d: start\n", __FUNCTION__, instance_id); + error = sam_start (); + if (error != CS_OK) { + fprintf (stderr, "Can't start hc. Error %d\n", error); + return 1; + } + + printf ("%s iid %d: call finalize (shouldn't deadlock)\n", __FUNCTION__, instance_id); + error = sam_finalize (); + if (error != CS_OK) { + fprintf (stderr, "Can't finalize sam. Error %d\n", error); + return 1; + } + + printf ("%s iid %d: finalize succeeded\n", __FUNCTION__, instance_id); + return 0; + } + + if (instance_id == 2) { + printf ("%s iid %d: previous sam_finalize deadlocked\n", __FUNCTION__, instance_id); + } + + return 1; +} + /* * Test cmap integration + quit policy */ -static int test8 (pid_t pid, pid_t old_pid, int test_n) { +static int test_cmap_quit (pid_t pid, pid_t old_pid, int test_n) +{ cmap_handle_t cmap_handle; cs_error_t err; uint64_t tstamp1, tstamp2; int32_t msec_diff; unsigned int instance_id; char key_name[CMAP_KEYNAME_MAXLEN]; char *str; err = cmap_initialize (&cmap_handle); if (err != CS_OK) { printf ("Could not initialize Cluster Map API instance error %d. Test skipped\n", err); return (1); } printf ("%s test %d\n", __FUNCTION__, test_n); if (test_n == 2) { /* * Object should not exist */ printf ("%s Testing if object exists (it shouldn't)\n", __FUNCTION__); snprintf(key_name, CMAP_KEYNAME_MAXLEN, "resources.process.%d.state", pid); err = cmap_get_string(cmap_handle, key_name, &str); if (err == CS_OK) { printf ("Could find key \"%s\": %d.\n", key_name, err); free(str); return (2); } } if (test_n == 1 || test_n == 2) { printf ("%s: initialize\n", __FUNCTION__); err = sam_initialize (2000, SAM_RECOVERY_POLICY_QUIT | SAM_RECOVERY_POLICY_CMAP); if (err != CS_OK) { fprintf (stderr, "Can't initialize SAM API. Error %d\n", err); return 2; } printf ("%s: register\n", __FUNCTION__); err = sam_register (&instance_id); if (err != CS_OK) { fprintf (stderr, "Can't register. Error %d\n", err); return 2; } snprintf(key_name, CMAP_KEYNAME_MAXLEN, "resources.process.%d.recovery", pid); err = cmap_get_string(cmap_handle, key_name, &str); if (err != CS_OK) { printf ("Could not get \"recovery\" key: %d.\n", err); return (2); } if (strcmp(str, "quit") != 0) { printf ("Recovery key \"%s\" is not \"quit\".\n", key_name); free(str); return (2); } free(str); snprintf(key_name, CMAP_KEYNAME_MAXLEN, "resources.process.%d.state", pid); err = cmap_get_string(cmap_handle, key_name, &str); if (err != CS_OK) { printf ("Could not get \"state\" key: %d.\n", err); return (2); } if (strcmp(str, "stopped") != 0) { printf ("State key is not \"stopped\".\n"); free(str); return (2); } free(str); printf ("%s iid %d: start\n", __FUNCTION__, instance_id); err = sam_start (); if (err != CS_OK) { fprintf (stderr, "Can't start hc. Error %d\n", err); return 2; } err = cmap_get_string(cmap_handle, key_name, &str); if (err != CS_OK) { printf ("Could not get \"state\" key: %d.\n", err); return (2); } if (strcmp(str, "running") != 0) { printf ("State key is not \"running\".\n"); free(str); return (2); } free(str); printf ("%s iid %d: stop\n", __FUNCTION__, instance_id); err = sam_stop (); if (err != CS_OK) { fprintf (stderr, "Can't stop hc. Error %d\n", err); return 2; } err = cmap_get_string(cmap_handle, key_name, &str); if (err != CS_OK) { printf ("Could not get \"state\" key: %d.\n", err); return (2); } if (strcmp(str, "stopped") != 0) { printf ("State key is not \"stopped\".\n"); free(str); return (2); } free(str); printf ("%s iid %d: sleeping 5\n", __FUNCTION__, instance_id); sleep (5); err = cmap_get_string(cmap_handle, key_name, &str); if (err != CS_OK) { printf ("Could not get \"state\" key: %d.\n", err); return (2); } if (strcmp(str, "stopped") != 0) { printf ("State key is not \"stopped\".\n"); free(str); return (2); } free(str); printf ("%s iid %d: start 2\n", __FUNCTION__, instance_id); err = sam_start (); if (err != CS_OK) { fprintf (stderr, "Can't start hc. Error %d\n", err); return 2; } err = cmap_get_string(cmap_handle, key_name, &str); if (err != CS_OK) { printf ("Could not get \"state\" key: %d.\n", err); return (2); } if (strcmp(str, "running") != 0) { printf ("State key is not \"running\".\n"); free(str); return (2); } free(str); if (test_n == 2) { printf ("%s iid %d: sleeping 5. Should be killed\n", __FUNCTION__, instance_id); sleep (5); return (2); } else { printf ("%s iid %d: Test HC\n", __FUNCTION__, instance_id); err = sam_hc_send (); if (err != CS_OK) { fprintf (stderr, "Can't send hc. Error %d\n", err); return 2; } snprintf(key_name, CMAP_KEYNAME_MAXLEN, "resources.process.%d.last_updated", pid); err = cmap_get_uint64(cmap_handle, key_name, &tstamp1); if (err != CS_OK) { printf ("Could not get \"last_updated\" key: %d.\n", err); return (2); } printf ("%s iid %d: Sleep 1\n", __FUNCTION__, instance_id); sleep (1); err = sam_hc_send (); if (err != CS_OK) { fprintf (stderr, "Can't send hc. Error %d\n", err); return 2; } sleep (1); err = cmap_get_uint64(cmap_handle, key_name, &tstamp2); if (err != CS_OK) { printf ("Could not get \"last_updated\" key: %d.\n", err); return (2); } msec_diff = (tstamp2 - tstamp1)/CS_TIME_NS_IN_MSEC; if (msec_diff < 500 || msec_diff > 2000) { printf ("Difference %d is not within <500, 2000> interval.\n", msec_diff); return (2); } printf ("%s iid %d: stop 2\n", __FUNCTION__, instance_id); err = sam_stop (); if (err != CS_OK) { fprintf (stderr, "Can't stop hc. Error %d\n", err); return 2; } snprintf(key_name, CMAP_KEYNAME_MAXLEN, "resources.process.%d.state", pid); err = cmap_get_string(cmap_handle, key_name, &str); if (err != CS_OK) { printf ("Could not get \"state\" key: %d.\n", err); return (2); } if (strcmp(str, "stopped") != 0) { printf ("State key is not \"stopped\".\n"); free(str); return (2); } free(str); printf ("%s iid %d: exiting\n", __FUNCTION__, instance_id); return (0); } } if (test_n == 3) { printf ("%s Testing if status is failed\n", __FUNCTION__); /* * Previous should be FAILED */ - snprintf(key_name, CMAP_KEYNAME_MAXLEN, "resources.process.%d.state", pid); + snprintf(key_name, CMAP_KEYNAME_MAXLEN, "resources.process.%d.state", old_pid); err = cmap_get_string(cmap_handle, key_name, &str); if (err != CS_OK) { printf ("Could not get \"state\" key: %d.\n", err); return (2); } if (strcmp(str, "failed") != 0) { printf ("State key is not \"failed\".\n"); free(str); return (2); } free(str); return (0); } return (2); } /* * Test cmap integration + restart policy */ -static int test9 (pid_t pid, pid_t old_pid, int test_n) { +static int test_cmap_restart (pid_t pid, pid_t old_pid, int test_n) { cs_error_t err; cmap_handle_t cmap_handle; unsigned int instance_id; char *str; char key_name[CMAP_KEYNAME_MAXLEN]; err = cmap_initialize (&cmap_handle); if (err != CS_OK) { printf ("Could not initialize Cluster Map API instance error %d. Test skipped\n", err); return (1); } printf ("%s test %d\n", __FUNCTION__, test_n); if (test_n == 1) { printf ("%s: initialize\n", __FUNCTION__); err = sam_initialize (2000, SAM_RECOVERY_POLICY_RESTART | SAM_RECOVERY_POLICY_CMAP); if (err != CS_OK) { fprintf (stderr, "Can't initialize SAM API. Error %d\n", err); return 2; } printf ("%s: register\n", __FUNCTION__); err = sam_register (&instance_id); if (err != CS_OK) { fprintf (stderr, "Can't register. Error %d\n", err); return 2; } printf ("%s: iid %d\n", __FUNCTION__, instance_id); if (instance_id < 3) { snprintf(key_name, CMAP_KEYNAME_MAXLEN, "resources.process.%d.recovery", pid); err = cmap_get_string(cmap_handle, key_name, &str); if (err != CS_OK) { printf ("Could not get \"recovery\" key: %d.\n", err); return (2); } if (strcmp(str, "restart") != 0) { printf ("Recovery key \"%s\" is not \"restart\".\n", str); free(str); return (2); } free(str); snprintf(key_name, CMAP_KEYNAME_MAXLEN, "resources.process.%d.state", pid); err = cmap_get_string(cmap_handle, key_name, &str); if (err != CS_OK) { printf ("Could not get \"state\" key: %d.\n", err); return (2); } if (strcmp(str, "stopped") != 0) { printf ("State key is not \"stopped\".\n"); free(str); return (2); } free(str); printf ("%s iid %d: start\n", __FUNCTION__, instance_id); err = sam_start (); if (err != CS_OK) { fprintf (stderr, "Can't start hc. Error %d\n", err); return 2; } err = cmap_get_string(cmap_handle, key_name, &str); if (err != CS_OK) { printf ("Could not get \"state\" key: %d.\n", err); return (2); } if (strcmp(str, "running") != 0) { printf ("State key is not \"running\".\n"); free(str); return (2); } free(str); printf ("%s iid %d: waiting for kill\n", __FUNCTION__, instance_id); sleep (10); return (2); } if (instance_id == 3) { printf ("%s iid %d: mark failed\n", __FUNCTION__, instance_id); err = sam_mark_failed (); if (err != CS_OK) { fprintf (stderr, "Can't mark failed. Error %d\n", err); return 2; } sleep (10); return (2); } return (2); } if (test_n == 2) { printf ("%s Testing if status is failed\n", __FUNCTION__); /* * Previous should be FAILED */ - snprintf(key_name, CMAP_KEYNAME_MAXLEN, "resources.process.%d.state", pid); + snprintf(key_name, CMAP_KEYNAME_MAXLEN, "resources.process.%d.state", old_pid); err = cmap_get_string(cmap_handle, key_name, &str); if (err != CS_OK) { printf ("Could not get \"state\" key: %d.\n", err); return (2); } if (strcmp(str, "failed") != 0) { printf ("State key is not \"failed\".\n"); free(str); return (2); } free(str); return (0); } return (2); } -int main(int argc, char *argv[]) +static int run_test(const char *test_name, int (test_func)(void), int skippable, int *all_passed, int *no_skipped) { - pid_t pid, old_pid; + pid_t pid; int err; int stat; - int all_passed = 1; - int no_skipped = 0; + const char *res_str; + fprintf (stderr, "Running %s test\n", test_name); pid = fork (); if (pid == -1) { fprintf (stderr, "Can't fork\n"); - return 1; - } - if (pid == 0) { - err = test1 (); - sam_finalize (); - return err; - } - - waitpid (pid, &stat, 0); - - fprintf (stderr, "test1 %s\n", (WEXITSTATUS (stat) == 0 ? "passed" : "failed")); - if (WEXITSTATUS (stat) != 0) - all_passed = 0; - - pid = fork (); - - if (pid == -1) { - fprintf (stderr, "Can't fork\n"); - return 1; + exit(1); } if (pid == 0) { - err = test2 (); - - sam_finalize (); - return (err); - } - - waitpid (pid, &stat, 0); - - fprintf (stderr, "test2 %s\n", (WEXITSTATUS (stat) == 0 ? "passed" : "failed")); - if (WEXITSTATUS (stat) != 0) - all_passed = 0; - - pid = fork (); - - if (pid == -1) { - fprintf (stderr, "Can't fork\n"); - return 1; - } - - if (pid == 0) { - err = test3 (); - sam_finalize (); - return (err); - } - - waitpid (pid, &stat, 0); - - fprintf (stderr, "test3 %s\n", (WEXITSTATUS (stat) == 0 ? "passed" : "failed")); - if (WEXITSTATUS (stat) != 0) - all_passed = 0; - - pid = fork (); - - if (pid == -1) { - fprintf (stderr, "Can't fork\n"); - return 1; - } + /* + * Child runs tests and sets exit code to result of test + */ + err = test_func (); - if (pid == 0) { - err = test4 (); + /* + * After sam_register this is child of child + */ sam_finalize (); - return (err); - } - - waitpid (pid, &stat, 0); - - fprintf (stderr, "test4 %s\n", (WEXITSTATUS (stat) == 0 ? "passed" : "failed")); - if (WEXITSTATUS (stat) != 0) - all_passed = 0; - - pid = fork (); - - if (pid == -1) { - fprintf (stderr, "Can't fork\n"); - return 1; - } - - if (pid == 0) { - err = test5 (); - sam_finalize (); - return (err); + exit (err); } + /* + * Parent waits for child to run the test and return exit code + */ waitpid (pid, &stat, 0); - fprintf (stderr, "test5 %s\n", (WEXITSTATUS (stat) == 0 ? "passed" : "failed")); - if (WEXITSTATUS (stat) != 0) - all_passed = 0; - - pid = fork (); - - if (pid == -1) { - fprintf (stderr, "Can't fork\n"); - return 1; - } - if (pid == 0) { - err = test6 (); - sam_finalize (); - return (err); - } + err = WEXITSTATUS (stat); - waitpid (pid, &stat, 0); - fprintf (stderr, "test6 %s\n", (WEXITSTATUS (stat) == 0 ? "passed" : "failed")); - if (WEXITSTATUS (stat) != 0) - all_passed = 0; + if (!skippable) { + res_str = (err == 0 ? "passed" : "failed"); - pid = fork (); - - if (pid == -1) { - fprintf (stderr, "Can't fork\n"); - return 2; - } + if (err != 0) { + (*all_passed) = 0; + } + } else { + res_str = (err == 0 ? "passed" : (err == 1 ? "skipped" : "failed")); - if (pid == 0) { - err = test7 (); - sam_finalize (); - return (err); + if (err == 1) { + (*no_skipped)++; + } else if (err > 1) { + (*all_passed) = 0; + } } - waitpid (pid, &stat, 0); - fprintf (stderr, "test7 %s\n", (WEXITSTATUS (stat) == 0 ? "passed" : (WEXITSTATUS (stat) == 1 ? "skipped" : "failed"))); - if (WEXITSTATUS (stat) == 1) - no_skipped++; - if (WEXITSTATUS (stat) > 1) - all_passed = 0; + fprintf (stderr, "%s test %s\n", test_name, res_str); - pid = fork (); + return (err); +} - if (pid == -1) { - fprintf (stderr, "Can't fork\n"); - return 2; - } +static int run_cmap_test(const char *test_name, + int (test_func)(pid_t pid, pid_t old_pid, int test_n), + int no_tests, int *all_passed, int *no_skipped) +{ + pid_t pid, old_pid; + int err; + int stat; + const char *res_str; + int n; - if (pid == 0) { - err = test8 (getpid (), 0, 1); - sam_finalize (); - return (err); - } + old_pid = 0; - waitpid (pid, &stat, 0); - old_pid = pid; + fprintf (stderr, "Running %s test\n", test_name); - if (WEXITSTATUS (stat) == 0) { + for (n = 1; n <= no_tests; n++) { pid = fork (); if (pid == -1) { fprintf (stderr, "Can't fork\n"); - return 2; + + exit (2); } if (pid == 0) { - err = test8 (getpid (), old_pid, 2); + err = test_func (getpid (), old_pid, n); sam_finalize (); - return (err); + exit (err); } waitpid (pid, &stat, 0); old_pid = pid; - if (WEXITSTATUS (stat) == 0) { - pid = fork (); - - if (pid == -1) { - fprintf (stderr, "Can't fork\n"); - return 2; - } - - if (pid == 0) { - err = test8 (old_pid, 0, 3); - sam_finalize (); - return (err); - } - - waitpid (pid, &stat, 0); + err = WEXITSTATUS (stat); + if (err != 0) { + break; } } - fprintf (stderr, "test8 %s\n", (WEXITSTATUS (stat) == 0 ? "passed" : (WEXITSTATUS (stat) == 1 ? "skipped" : "failed"))); - if (WEXITSTATUS (stat) == 1) - no_skipped++; - if (WEXITSTATUS (stat) > 1) - all_passed = 0; - - pid = fork (); + res_str = (err == 0 ? "passed" : (err == 1 ? "skipped" : "failed")); - if (pid == -1) { - fprintf (stderr, "Can't fork\n"); - return 2; + if (err == 1) { + (*no_skipped)++; + } else if (err > 1) { + (*all_passed) = 0; } - if (pid == 0) { - err = test9 (getpid (), 0, 1); - sam_finalize (); - return (err); - } + fprintf (stderr, "%s test %s\n", test_name, res_str); - waitpid (pid, &stat, 0); - old_pid = pid; - - if (WEXITSTATUS (stat) == 0) { - pid = fork (); - - if (pid == -1) { - fprintf (stderr, "Can't fork\n"); - return 2; - } - - if (pid == 0) { - err = test9 (old_pid, 0, 2); - sam_finalize (); - return (err); - } + return (err); +} - waitpid (pid, &stat, 0); - } - fprintf (stderr, "test9 %s\n", (WEXITSTATUS (stat) == 0 ? "passed" : (WEXITSTATUS (stat) == 1 ? "skipped" : "failed"))); - if (WEXITSTATUS (stat) == 1) - no_skipped++; +int main(int argc, char *argv[]) +{ + int all_passed = 1; + int no_skipped = 0; - if (WEXITSTATUS (stat) > 1) - all_passed = 0; + setlinebuf(stdout); + + run_test("basics", test_basics, 0, &all_passed, &no_skipped); + run_test("signal", test_signal, 0, &all_passed, &no_skipped); + run_test("smoke", test_smoke, 0, &all_passed, &no_skipped); + run_test("data_store/restore", test_data_store_restore, 0, &all_passed, &no_skipped); + run_test("hc", test_hc, 0, &all_passed, &no_skipped); + run_test("warn_signal", test_warn_signal, 0, &all_passed, &no_skipped); + run_test("quorum", test_quorum, 1, &all_passed, &no_skipped); + run_test("finalize deadlock", test_finalize_deadlock, 0, &all_passed, &no_skipped); + run_cmap_test("cmap quit", test_cmap_quit, 3, &all_passed, &no_skipped); + run_cmap_test("cmap restart", test_cmap_restart, 2, &all_passed, &no_skipped); if (all_passed) fprintf (stderr, "All tests passed (%d skipped)\n", no_skipped); return (all_passed ? 0 : 1); } diff --git a/tools/corosync-cmapctl.c b/tools/corosync-cmapctl.c index 6c79c9ac..c50b7257 100644 --- a/tools/corosync-cmapctl.c +++ b/tools/corosync-cmapctl.c @@ -1,996 +1,1003 @@ /* * Copyright (c) 2011-2012 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@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 Red Hat, 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 "../lib/util.h" #ifndef INFTIM #define INFTIM -1 #endif #define MAX_TRY_AGAIN 10 enum user_action { ACTION_GET, ACTION_SET, ACTION_DELETE, ACTION_DELETE_PREFIX, ACTION_PRINT_PREFIX, ACTION_TRACK, ACTION_LOAD, ACTION_CLEARSTATS, }; struct name_to_type_item { const char *name; cmap_value_types_t type; }; struct name_to_type_item name_to_type[] = { {"i8", CMAP_VALUETYPE_INT8}, {"u8", CMAP_VALUETYPE_UINT8}, {"i16", CMAP_VALUETYPE_INT16}, {"u16", CMAP_VALUETYPE_UINT16}, {"i32", CMAP_VALUETYPE_INT32}, {"u32", CMAP_VALUETYPE_UINT32}, {"i64", CMAP_VALUETYPE_INT64}, {"u64", CMAP_VALUETYPE_UINT64}, {"flt", CMAP_VALUETYPE_FLOAT}, {"dbl", CMAP_VALUETYPE_DOUBLE}, {"str", CMAP_VALUETYPE_STRING}, {"bin", CMAP_VALUETYPE_BINARY}}; int show_binary = 0; int quiet = 0; static int convert_name_to_type(const char *name) { int i; for (i = 0; i < sizeof(name_to_type) / sizeof(*name_to_type); i++) { if (strcmp(name, name_to_type[i].name) == 0) { return (name_to_type[i].type); } } return (-1); } static int print_help(void) { printf("\n"); printf("usage: corosync-cmapctl [-b] [-DdghsqTCt] [-p filename] [-m map] [params...]\n"); printf("\n"); printf(" -b show binary values\n"); printf("\n"); printf(" -m select map to use\n"); printf(" The default map is 'icmap' which contains configuration information and some runtime variables used by corosync. \n"); printf(" A 'stats' map is also available which displays network statistics - in great detail when knet is used as the transport.\n"); printf("Set key:\n"); printf(" corosync-cmapctl -s key_name type value\n"); printf("\n"); printf(" where type is one of ([i|u][8|16|32|64] | flt | dbl | str | bin)\n"); printf(" for bin, value is file name (or - for stdin)\n"); printf("\n"); printf(" map can be either 'icmap' (the default) which contains corosync\n"); printf(" configuration information, or 'stats' which contains statistics\n"); printf(" about the networking and IPC traffic in some detail.\n"); printf("\n"); printf("Clear stats:\n"); printf(" corosync-cmapctl -C [knet|ipc|totem|schedmiss|all]\n"); printf(" The 'stats' map is implied\n"); printf("\n"); printf("Load settings from a file:\n"); printf(" corosync-cmapctl -p filename\n"); printf("\n"); printf(" the format of the file is:\n"); printf(" [^[^]][ ]\n"); printf(" Keys prefixed with single caret ('^') are deleted (see -d).\n"); printf(" Keys (actually prefixes) prefixed with double caret ('^^') are deleted by prefix (see -D).\n"); printf(" and are optional (not checked) in above cases.\n"); printf(" Other keys are set (see -s) so both and are required.\n"); printf("\n"); printf("Delete key:\n"); printf(" corosync-cmapctl -d key_name...\n"); printf("\n"); printf("Delete multiple keys with prefix:\n"); printf(" corosync-cmapctl -D key_prefix...\n"); printf("\n"); printf("Get key:\n"); printf(" corosync-cmapctl [-b] -g key_name...\n"); printf("\n"); printf("Quiet mode:\n"); printf(" corosync-cmapctl [-b] -q -g key_name...\n"); printf("\n"); printf("Display all keys:\n"); printf(" corosync-cmapctl [-b]\n"); printf("\n"); printf("Display keys with prefix key_name:\n"); printf(" corosync-cmapctl [-b] key_name...\n"); printf("\n"); printf("Track changes on keys with key_name:\n"); printf(" corosync-cmapctl [-b] -t key_name\n"); printf("\n"); printf("Track changes on keys with key prefix:\n"); printf(" corosync-cmapctl [-b] -T key_prefix\n"); printf("\n"); return (0); } static void print_binary_key (char *value, size_t value_len) { size_t i; char c; for (i = 0; i < value_len; i++) { c = value[i]; if (c >= ' ' && c < 0x7f && c != '\\') { fputc (c, stdout); } else { if (c == '\\') { printf ("\\\\"); } else { printf ("\\x%02X", c); } } } } static void print_key(cmap_handle_t handle, const char *key_name, size_t value_len, const void *value, cmap_value_types_t type) { char *str; char *bin_value = NULL; cs_error_t err; int8_t i8; uint8_t u8; int16_t i16; uint16_t u16; int32_t i32; uint32_t u32; int64_t i64; uint64_t u64; float flt; double dbl; int end_loop; int no_retries; size_t bin_value_len; end_loop = 0; no_retries = 0; err = CS_OK; while (!end_loop) { switch (type) { case CMAP_VALUETYPE_INT8: if (value == NULL) { err = cmap_get_int8(handle, key_name, &i8); } else { i8 = *((int8_t *)value); } break; case CMAP_VALUETYPE_INT16: if (value == NULL) { err = cmap_get_int16(handle, key_name, &i16); } else { i16 = *((int16_t *)value); } break; case CMAP_VALUETYPE_INT32: if (value == NULL) { err = cmap_get_int32(handle, key_name, &i32); } else { i32 = *((int32_t *)value); } break; case CMAP_VALUETYPE_INT64: if (value == NULL) { err = cmap_get_int64(handle, key_name, &i64); } else { i64 = *((int64_t *)value); } break; case CMAP_VALUETYPE_UINT8: if (value == NULL) { err = cmap_get_uint8(handle, key_name, &u8); } else { u8 = *((uint8_t *)value); } break; case CMAP_VALUETYPE_UINT16: if (value == NULL) { err = cmap_get_uint16(handle, key_name, &u16); } else { u16 = *((uint16_t *)value); } break; case CMAP_VALUETYPE_UINT32: if (value == NULL) { err = cmap_get_uint32(handle, key_name, &u32); } else { u32 = *((uint32_t *)value); } break; case CMAP_VALUETYPE_UINT64: if (value == NULL) { err = cmap_get_uint64(handle, key_name, &u64); } else { u64 = *((uint64_t *)value); } break; case CMAP_VALUETYPE_FLOAT: if (value == NULL) { err = cmap_get_float(handle, key_name, &flt); } else { flt = *((float *)value); } break; case CMAP_VALUETYPE_DOUBLE: if (value == NULL) { err = cmap_get_double(handle, key_name, &dbl); } else { dbl = *((double *)value); } break; case CMAP_VALUETYPE_STRING: if (value == NULL) { err = cmap_get_string(handle, key_name, &str); } else { str = (char *)value; } break; case CMAP_VALUETYPE_BINARY: if (show_binary) { if (value == NULL) { bin_value = malloc(value_len); if (bin_value == NULL) { fprintf(stderr, "Can't alloc memory\n"); exit(EXIT_FAILURE); } bin_value_len = value_len; err = cmap_get(handle, key_name, bin_value, &bin_value_len, NULL); } else { bin_value = (char *)value; } } break; } if (err == CS_OK) { end_loop = 1; } else if (err == CS_ERR_TRY_AGAIN) { sleep(1); no_retries++; if (no_retries > MAX_TRY_AGAIN) { end_loop = 1; } } else { end_loop = 1; } }; if (err != CS_OK) { fprintf(stderr, "Can't get value of %s. Error %s\n", key_name, cs_strerror(err)); /* * bin_value was newly allocated */ if (bin_value != NULL && value == NULL) { free(bin_value); } return ; } if (!quiet) printf("%s (", key_name); switch (type) { case CMAP_VALUETYPE_INT8: if (!quiet) printf("%s) = %"PRId8, "i8", i8); else printf("%"PRId8, i8); break; case CMAP_VALUETYPE_UINT8: if (!quiet) printf("%s) = %"PRIu8, "u8", u8); else printf("%"PRIu8, u8); break; case CMAP_VALUETYPE_INT16: if (!quiet) printf("%s) = %"PRId16, "i16", i16); else printf("%"PRId16, i16); break; case CMAP_VALUETYPE_UINT16: if (!quiet) printf("%s) = %"PRIu16, "u16", u16); else printf("%"PRIu16, u16); break; case CMAP_VALUETYPE_INT32: if (!quiet) printf("%s) = %"PRId32, "i32", i32); else printf("%"PRId32, i32); break; case CMAP_VALUETYPE_UINT32: if (!quiet) printf("%s) = %"PRIu32, "u32", u32); else printf("%"PRIu32, u32); break; case CMAP_VALUETYPE_INT64: if (!quiet) printf("%s) = %"PRId64, "i64", i64); else printf("%"PRId64, i64); break; case CMAP_VALUETYPE_UINT64: if (!quiet) printf("%s) = %"PRIu64, "u64", u64); else printf("%"PRIu64, u64); break; case CMAP_VALUETYPE_FLOAT: if (!quiet) printf("%s) = %f", "flt", flt); else printf("%f", flt); break; case CMAP_VALUETYPE_DOUBLE: if (!quiet) printf("%s) = %lf", "dbl", dbl); else printf("%lf", dbl); break; case CMAP_VALUETYPE_STRING: if (!quiet) printf("%s) = %s", "str", str); else printf("%s", str); if (value == NULL) { free(str); } break; case CMAP_VALUETYPE_BINARY: printf("%s)", "bin"); if (show_binary) { printf(" = "); if (bin_value) { print_binary_key(bin_value, value_len); if (value == NULL) { free(bin_value); } } else { printf("*empty*"); } } break; } printf("\n"); } static int print_iter(cmap_handle_t handle, const char *prefix) { cmap_iter_handle_t iter_handle; char key_name[CMAP_KEYNAME_MAXLEN + 1]; size_t value_len; cmap_value_types_t type; cs_error_t err; int no_result = 1; err = cmap_iter_init(handle, prefix, &iter_handle); if (err != CS_OK) { fprintf (stderr, "Failed to initialize iteration. Error %s\n", cs_strerror(err)); exit (EXIT_FAILURE); } while ((err = cmap_iter_next(handle, iter_handle, key_name, &value_len, &type)) == CS_OK) { no_result = 0; print_key(handle, key_name, value_len, NULL, type); } cmap_iter_finalize(handle, iter_handle); return no_result; } static void delete_with_prefix(cmap_handle_t handle, const char *prefix) { cmap_iter_handle_t iter_handle; char key_name[CMAP_KEYNAME_MAXLEN + 1]; size_t value_len; cmap_value_types_t type; cs_error_t err; cs_error_t err2; err = cmap_iter_init(handle, prefix, &iter_handle); if (err != CS_OK) { fprintf (stderr, "Failed to initialize iteration. Error %s\n", cs_strerror(err)); exit (EXIT_FAILURE); } while ((err = cmap_iter_next(handle, iter_handle, key_name, &value_len, &type)) == CS_OK) { err2 = cmap_delete(handle, key_name); if (err2 != CS_OK) { fprintf(stderr, "Can't delete key %s. Error %s\n", key_name, cs_strerror(err2)); } } cmap_iter_finalize(handle, iter_handle); } static void cmap_notify_fn( cmap_handle_t cmap_handle, cmap_track_handle_t cmap_track_handle, int32_t event, const char *key_name, struct cmap_notify_value new_val, struct cmap_notify_value old_val, void *user_data) { switch (event) { case CMAP_TRACK_ADD: printf("create> "); print_key(cmap_handle, key_name, new_val.len, new_val.data, new_val.type); break; case CMAP_TRACK_DELETE: printf("delete> "); print_key(cmap_handle, key_name, old_val.len, old_val.data, old_val.type); break; case CMAP_TRACK_MODIFY: printf("modify> "); print_key(cmap_handle, key_name, new_val.len, new_val.data, new_val.type); break; default: printf("unknown change> "); break; } } static void add_track(cmap_handle_t handle, const char *key_name, int prefix) { cmap_track_handle_t track_handle; int32_t track_type; cs_error_t err; track_type = CMAP_TRACK_ADD | CMAP_TRACK_DELETE | CMAP_TRACK_MODIFY; if (prefix) { track_type |= CMAP_TRACK_PREFIX; } err = cmap_track_add(handle, key_name, track_type, cmap_notify_fn, NULL, &track_handle); if (err != CS_OK) { fprintf(stderr, "Failed to add tracking function. Error %s\n", cs_strerror(err)); exit (EXIT_FAILURE); } } static void track_changes(cmap_handle_t handle) { struct pollfd pfd[2]; int cmap_fd; cs_error_t err; int poll_res; char inbuf[3]; int quit = CS_FALSE; err = cmap_fd_get(handle, &cmap_fd); if (err != CS_OK) { fprintf(stderr, "Failed to get file handle. Error %s\n", cs_strerror(err)); exit (EXIT_FAILURE); } pfd[0].fd = cmap_fd; pfd[1].fd = STDIN_FILENO; pfd[0].events = pfd[1].events = POLLIN; printf("Type \"q\" to finish\n"); do { pfd[0].revents = pfd[1].revents = 0; poll_res = poll(pfd, 2, INFTIM); if (poll_res == -1) { perror("poll"); } if (pfd[1].revents & POLLIN) { if (fgets(inbuf, sizeof(inbuf), stdin) == NULL) { quit = CS_TRUE; } else if (strncmp(inbuf, "q", 1) == 0) { quit = CS_TRUE; } } if (pfd[0].revents & POLLIN) { err = cmap_dispatch(handle, CS_DISPATCH_ALL); if (err != CS_OK) { fprintf(stderr, "Dispatch error %s\n", cs_strerror(err)); quit = CS_TRUE; } } } while (poll_res > 0 && !quit); } static cs_error_t set_key_bin(cmap_handle_t handle, const char *key_name, const char *fname) { FILE *f; char *val; char buf[4096]; size_t size; size_t readed; size_t pos; cs_error_t err; if (strcmp(fname, "-") == 0) { f = stdin; } else { f = fopen(fname, "rb"); if (f == NULL) { perror("Can't open input file"); exit(EXIT_FAILURE); } } val = NULL; size = 0; pos = 0; while ((readed = fread(buf, 1, sizeof(buf), f)) != 0) { size += readed; if ((val = realloc(val, size)) == NULL) { fprintf(stderr, "Can't alloc memory\n"); exit (EXIT_FAILURE); } memcpy(val + pos, buf, readed); pos += readed; } if (f != stdin) { fclose(f); } err = cmap_set(handle, key_name, val, size, CMAP_VALUETYPE_BINARY); free(val); return (err); } static void set_key(cmap_handle_t handle, const char *key_name, const char *key_type_s, const char *key_value_s) { int64_t i64; uint64_t u64; double dbl; float flt; cs_error_t err = CS_OK; int scanf_res = 0; cmap_value_types_t type; if (convert_name_to_type(key_type_s) == -1) { fprintf(stderr, "Unknown type %s\n", key_type_s); exit (EXIT_FAILURE); } type = convert_name_to_type(key_type_s); switch (type) { case CMAP_VALUETYPE_INT8: case CMAP_VALUETYPE_INT16: case CMAP_VALUETYPE_INT32: case CMAP_VALUETYPE_INT64: scanf_res = sscanf(key_value_s, "%"PRId64, &i64); break; case CMAP_VALUETYPE_UINT8: case CMAP_VALUETYPE_UINT16: case CMAP_VALUETYPE_UINT32: case CMAP_VALUETYPE_UINT64: scanf_res = sscanf(key_value_s, "%"PRIu64, &u64); break; case CMAP_VALUETYPE_FLOAT: scanf_res = sscanf(key_value_s, "%f", &flt); break; case CMAP_VALUETYPE_DOUBLE: scanf_res = sscanf(key_value_s, "%lf", &dbl); break; case CMAP_VALUETYPE_STRING: case CMAP_VALUETYPE_BINARY: /* * Do nothing */ scanf_res = 1; break; } if (scanf_res != 1) { fprintf(stderr, "%s is not valid %s type value\n", key_value_s, key_type_s); exit(EXIT_FAILURE); } /* * We have parsed value, so insert value */ switch (type) { case CMAP_VALUETYPE_INT8: if (i64 > INT8_MAX || i64 < INT8_MIN) { fprintf(stderr, "%s is not valid i8 integer\n", key_value_s); exit(EXIT_FAILURE); } err = cmap_set_int8(handle, key_name, i64); break; case CMAP_VALUETYPE_INT16: if (i64 > INT16_MAX || i64 < INT16_MIN) { fprintf(stderr, "%s is not valid i16 integer\n", key_value_s); exit(EXIT_FAILURE); } err = cmap_set_int16(handle, key_name, i64); break; case CMAP_VALUETYPE_INT32: if (i64 > INT32_MAX || i64 < INT32_MIN) { fprintf(stderr, "%s is not valid i32 integer\n", key_value_s); exit(EXIT_FAILURE); } err = cmap_set_int32(handle, key_name, i64); break; case CMAP_VALUETYPE_INT64: err = cmap_set_int64(handle, key_name, i64); break; case CMAP_VALUETYPE_UINT8: if (u64 > UINT8_MAX) { fprintf(stderr, "%s is not valid u8 integer\n", key_value_s); exit(EXIT_FAILURE); } err = cmap_set_uint8(handle, key_name, u64); break; case CMAP_VALUETYPE_UINT16: if (u64 > UINT16_MAX) { fprintf(stderr, "%s is not valid u16 integer\n", key_value_s); exit(EXIT_FAILURE); } err = cmap_set_uint16(handle, key_name, u64); break; case CMAP_VALUETYPE_UINT32: if (u64 > UINT32_MAX) { fprintf(stderr, "%s is not valid u32 integer\n", key_value_s); exit(EXIT_FAILURE); } err = cmap_set_uint32(handle, key_name, u64); break; case CMAP_VALUETYPE_UINT64: err = cmap_set_uint64(handle, key_name, u64); break; case CMAP_VALUETYPE_FLOAT: err = cmap_set_float(handle, key_name, flt); break; case CMAP_VALUETYPE_DOUBLE: err = cmap_set_double(handle, key_name, dbl); break; case CMAP_VALUETYPE_STRING: err = cmap_set_string(handle, key_name, key_value_s); break; case CMAP_VALUETYPE_BINARY: err = set_key_bin(handle, key_name, key_value_s); break; } if (err != CS_OK) { fprintf (stderr, "Failed to set key %s. Error %s\n", key_name, cs_strerror(err)); exit (EXIT_FAILURE); } } static void read_in_config_file(cmap_handle_t handle, char * filename) { int ignore; int c; FILE* fh; char buf[1024]; char * line; char *key_name; char *key_type_s; char *key_value_s; fh = fopen(filename, "r"); if (fh == NULL) { perror ("Couldn't open file."); return; } while (fgets (buf, 1024, fh) != NULL) { /* find the first real character, if it is * a '#' then ignore this line. * else process. * if no real characters then also ignore. */ ignore = 1; for (c = 0; c < 1024; c++) { if (isblank (buf[c])) { continue; } if (buf[c] == '#' || buf[c] == '\n') { ignore = 1; break; } ignore = 0; line = &buf[c]; break; } if (ignore == 1) { continue; } /* * should be: * [^[^]][ ] */ key_name = strtok(line, " \n"); if (key_name && *key_name == '^') { key_name++; if (*key_name == '^') { key_name++; delete_with_prefix(handle, key_name); } else { cs_error_t err; err = cmap_delete(handle, key_name); if (err != CS_OK) { fprintf(stderr, "Can't delete key %s. Error %s\n", key_name, cs_strerror(err)); } } } else { key_type_s = strtok(NULL, " \n"); key_value_s = strtok(NULL, " \n"); if (key_type_s == NULL || key_value_s == NULL) { fprintf(stderr, "Both type and value for key %s are required\n", key_name); exit (EXIT_FAILURE); } set_key(handle, key_name, key_type_s, key_value_s); } } fclose (fh); } static void clear_stats(cmap_handle_t handle, char *clear_opt) { char key_name[CMAP_KEYNAME_MAXLEN + 1]; + cs_error_t err; sprintf(key_name, "stats.clear.%s", clear_opt); - cmap_set_uint32(handle, key_name, 1); + + if ((err = cmap_set_uint32(handle, key_name, 1)) != CS_OK) { + fprintf(stderr, "Can't set cmap stats clear key %s. Error %s\n", + key_name, cs_strerror(err)); + + exit (EXIT_FAILURE); + } } int main(int argc, char *argv[]) { enum user_action action; int c; cs_error_t err; cmap_handle_t handle; int i; size_t value_len; cmap_value_types_t type; cmap_map_t map = CMAP_MAP_DEFAULT; int track_prefix; int map_set = 0; int no_retries; char * clear_opt = NULL; char * settings_file = NULL; int count_of_no_result = 0; action = ACTION_PRINT_PREFIX; track_prefix = 1; while ((c = getopt(argc, argv, "m:hqgsdDtTbp:C:")) != -1) { switch (c) { case 'h': return print_help(); break; case 'b': show_binary++; break; case 'q': quiet = 1; break; case 'g': action = ACTION_GET; break; case 's': action = ACTION_SET; break; case 'd': action = ACTION_DELETE; break; case 'D': action = ACTION_DELETE_PREFIX; break; case 'p': settings_file = optarg; action = ACTION_LOAD; break; case 'C': if (strcmp(optarg, "knet") == 0 || strcmp(optarg, "totem") == 0 || strcmp(optarg, "ipc") == 0 || strcmp(optarg, "schedmiss") == 0 || strcmp(optarg, "all") == 0) { action = ACTION_CLEARSTATS; clear_opt = optarg; /* Force the map to be STATS */ map = CMAP_MAP_STATS; } else { fprintf(stderr, "argument to -C should be 'knet', 'totem', 'ipc', 'schedmiss' or 'all'\n"); return (EXIT_FAILURE); } break; case 't': action = ACTION_TRACK; track_prefix = 0; break; case 'T': action = ACTION_TRACK; break; case 'm': if (strcmp(optarg, "icmap") == 0 || strcmp(optarg, "default") == 0) { map = CMAP_MAP_ICMAP; map_set = 1; } if (strcmp(optarg, "stats") == 0) { map = CMAP_MAP_STATS; map_set = 1; } if (!map_set) { fprintf(stderr, "invalid map name, must be 'default', 'icmap' or 'stats'\n"); return (EXIT_FAILURE); } break; case '?': return (EXIT_FAILURE); break; default: action = ACTION_PRINT_PREFIX; break; } } argc -= optind; argv += optind; if (argc == 0 && action != ACTION_LOAD && action != ACTION_CLEARSTATS && action != ACTION_PRINT_PREFIX) { fprintf(stderr, "Expected key after options\n"); return (EXIT_FAILURE); } no_retries = 0; while ((err = cmap_initialize_map(&handle, map)) == CS_ERR_TRY_AGAIN && no_retries++ < MAX_TRY_AGAIN) { sleep(1); } if (err != CS_OK) { fprintf (stderr, "Failed to initialize the cmap API. Error %s\n", cs_strerror(err)); exit (EXIT_FAILURE); } switch (action) { case ACTION_PRINT_PREFIX: if (argc == 0) { count_of_no_result = print_iter(handle, NULL); } else { for (i = 0; i < argc; i++) { count_of_no_result += print_iter(handle, argv[i]); } } if (count_of_no_result > 0 && count_of_no_result >= argc) { return (EXIT_FAILURE); } break; case ACTION_GET: for (i = 0; i < argc; i++) { err = cmap_get(handle, argv[i], NULL, &value_len, &type); if (err == CS_OK) { print_key(handle, argv[i], value_len, NULL, type); } else { fprintf(stderr, "Can't get key %s. Error %s\n", argv[i], cs_strerror(err)); return (EXIT_FAILURE); } } break; case ACTION_DELETE: for (i = 0; i < argc; i++) { err = cmap_delete(handle, argv[i]); if (err != CS_OK) { fprintf(stderr, "Can't delete key %s. Error %s\n", argv[i], cs_strerror(err)); return (EXIT_FAILURE); } } break; case ACTION_DELETE_PREFIX: for (i = 0; i < argc; i++) { delete_with_prefix(handle, argv[i]); } break; case ACTION_LOAD: read_in_config_file(handle, settings_file); break; case ACTION_TRACK: for (i = 0; i < argc; i++) { add_track(handle, argv[i], track_prefix); } track_changes(handle); break; case ACTION_SET: if (argc < 3) { fprintf(stderr, "At least 3 parameters are expected for set\n"); return (EXIT_FAILURE); } set_key(handle, argv[0], argv[1], argv[2]); break; case ACTION_CLEARSTATS: clear_stats(handle, clear_opt); break; } err = cmap_finalize(handle); if (err != CS_OK) { fprintf (stderr, "Failed to finalize the cmap API. Error %s\n", cs_strerror(err)); exit (EXIT_FAILURE); } return (0); }