From da6fce352b9abea519c1d4d24ed940915a9b9e08 Mon Sep 17 00:00:00 2001 From: Jan Friesse Date: Tue, 20 Apr 2010 10:32:07 +0000 Subject: [PATCH] Support for store user data in SAM Ability to in-memory storing of user data which survives between instances of process. Also ability needed ability for bi-directional communication between child and parent is added. git-svn-id: http://svn.fedorahosted.org/svn/corosync/trunk@2769 fd59a12c-fef9-0310-b244-a6a79926bd2f --- include/corosync/sam.h | 43 +++++ lib/libsam.verso | 2 +- lib/sam.c | 352 +++++++++++++++++++++++++++++++++++------ man/Makefile.am | 3 + man/sam_data_getsize.3 | 68 ++++++++ man/sam_data_restore.3 | 77 +++++++++ man/sam_data_store.3 | 83 ++++++++++ man/sam_overview.8 | 10 ++ test/testsam.c | 331 +++++++++++++++++++++++++++++++++++--- 9 files changed, 895 insertions(+), 74 deletions(-) create mode 100644 man/sam_data_getsize.3 create mode 100644 man/sam_data_restore.3 create mode 100644 man/sam_data_store.3 diff --git a/include/corosync/sam.h b/include/corosync/sam.h index bf6b0695..4e60e171 100644 --- a/include/corosync/sam.h +++ b/include/corosync/sam.h @@ -160,6 +160,49 @@ cs_error_t sam_hc_send (void); */ cs_error_t sam_hc_callback_register (sam_hc_callback_t cb); +/* + * Return size of stored data. + * @param size Pointer to variable, where stored data size is returned. If + * nothing or NULL is stored, then 0 is returned. + * @return + * - CS_OK in case no problem appeared + * - CS_ERR_BAD_HANDLE in case you call this function before sam_init or after + * sam_finalize + * - CS_ERR_INVALID_PARAM if size parameter is NULL + */ +cs_error_t sam_data_getsize (size_t *size); + +/* + * Return stored data. + * @param data Pointer to place, where to store data + * @param size Allocated size of data + * @return + * - CS_OK if no problem appeared + * - CS_ERR_BAD_HANDLE if you call this function before sam_init or after sam_finalize + * - CS_ERR_INVALID_PARAM if data is NULL or size is less then currently saved user data length + */ +cs_error_t sam_data_restore ( + void *data, + size_t size); + +/* + * Store user data. Such stored data survives restart of child. + * @param data Data to store. You can use NULL to delete data + * @param size Size of data to store. + * @return + * - CS_OK in case no problem appeared + * - CS_ERR_BAD_HANDLE if you call this function before sam_init or + * after sam_finalize + * - CS_ERR_NO_MEMORY if data is too large and malloc/realloc was not + * sucesfull + * - CS_ERR_LIBRARY if some internal error appeared (communication with parent + * process) + */ +cs_error_t sam_data_store ( + const void *data, + size_t size); + + #ifdef __cplusplus } #endif diff --git a/lib/libsam.verso b/lib/libsam.verso index ee74734a..6aba2b24 100644 --- a/lib/libsam.verso +++ b/lib/libsam.verso @@ -1 +1 @@ -4.1.0 +4.2.0 diff --git a/lib/sam.c b/lib/sam.c index 9487afd1..207d4f94 100644 --- a/lib/sam.c +++ b/lib/sam.c @@ -38,6 +38,7 @@ #include +#include #include #include #include @@ -70,7 +71,13 @@ enum sam_internal_status_t { enum sam_command_t { SAM_COMMAND_START, SAM_COMMAND_STOP, - SAM_COMMAND_HB + SAM_COMMAND_HB, + SAM_COMMAND_DATA_STORE, +}; + +enum sam_reply_t { + SAM_REPLY_OK, + SAM_REPLY_ERROR, }; enum sam_parent_action_t { @@ -85,14 +92,20 @@ static struct { sam_recovery_policy_t recovery_policy; enum sam_internal_status_t internal_status; unsigned int instance_id; - int parent_fd; + 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; } sam_internal_data; cs_error_t sam_initialize ( @@ -115,6 +128,12 @@ cs_error_t sam_initialize ( 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; + return (CS_OK); } @@ -132,7 +151,8 @@ static size_t sam_safe_write ( bytes_write = 0; do { - tmp_bytes_write = write (d, (const char *)buf + bytes_write, nbyte - bytes_write); + 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)) @@ -142,7 +162,176 @@ static size_t sam_safe_write ( } } while (bytes_write != nbyte); - return bytes_write; + 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); +} + +cs_error_t sam_data_getsize (size_t *size) +{ + if (size == NULL) { + return (CS_ERR_INVALID_PARAM); + } + + 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); + } + + *size = sam_internal_data.user_data_size; + + return (CS_OK); +} + +cs_error_t sam_data_restore ( + void *data, + size_t size) +{ + if (data == NULL) { + return (CS_ERR_INVALID_PARAM); + } + + 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 (sam_internal_data.user_data_size == 0) { + return (CS_OK); + } + + if (size < sam_internal_data.user_data_size) { + return (CS_ERR_INVALID_PARAM); + } + + memcpy (data, sam_internal_data.user_data, sam_internal_data.user_data_size); + + return (CS_OK); +} + +cs_error_t sam_data_store ( + const void *data, + size_t size) +{ + cs_error_t err; + char command; + char *new_data; + char reply; + + 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 (sam_internal_data.user_data_allocated < size) { + if ((new_data = realloc (sam_internal_data.user_data, size)) == NULL) { + return (CS_ERR_NO_MEMORY); + } + + sam_internal_data.user_data_allocated = size; + } else { + new_data = sam_internal_data.user_data; + } + + if (data == NULL) { + size = 0; + } + + 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)) { + return (CS_ERR_LIBRARY); + } + + if (sam_safe_write (sam_internal_data.child_fd_out, &size, sizeof (size)) != sizeof (size)) { + return (CS_ERR_LIBRARY); + } + + if (data != NULL && sam_safe_write (sam_internal_data.child_fd_out, data, size) != size) { + return (CS_ERR_LIBRARY); + } + + /* + * And wait for reply + */ + 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; + } + } + + /* + * 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 { + sam_internal_data.user_data = new_data; + sam_internal_data.user_data_size = size; + + memcpy (sam_internal_data.user_data, data, size); + } + + return (CS_OK); } cs_error_t sam_start (void) @@ -155,11 +344,11 @@ cs_error_t sam_start (void) command = SAM_COMMAND_START; - if (sam_safe_write (sam_internal_data.parent_fd, &command, 1) == -1) + if (sam_safe_write (sam_internal_data.child_fd_out, &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, 1) == -1) + 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_STARTED; @@ -177,11 +366,11 @@ cs_error_t sam_stop (void) command = SAM_COMMAND_STOP; - if (sam_safe_write (sam_internal_data.parent_fd, &command, 1) == -1) + if (sam_safe_write (sam_internal_data.child_fd_out, &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, 1) == -1) + 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; @@ -199,7 +388,7 @@ cs_error_t sam_hc_send (void) command = SAM_COMMAND_HB; - if (sam_safe_write (sam_internal_data.parent_fd, &command, 1) == -1) + if (sam_safe_write (sam_internal_data.child_fd_out, &command, sizeof (command)) != sizeof (command)) return (CS_ERR_LIBRARY); return (CS_OK); @@ -223,6 +412,8 @@ cs_error_t sam_finalize (void) sam_internal_data.internal_status = SAM_INTERNAL_STATUS_FINALIZED; + free (sam_internal_data.user_data); + exit_error: return (CS_OK); } @@ -241,7 +432,69 @@ cs_error_t sam_warn_signal_set (int warn_signal) return (CS_OK); } -static enum sam_parent_action_t sam_parent_handler (int pipe_fd, pid_t child_pid) +static cs_error_t sam_parent_data_store ( + int parent_fd_in, + int parent_fd_out) +{ + char reply; + 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 > 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); + if (err != CS_OK) { + goto free_error_reply; + } + + reply = SAM_REPLY_OK; + if (sam_safe_write (parent_fd_out, &reply, sizeof (reply)) != sizeof (reply)) { + err = CS_ERR_LIBRARY; + goto free_error_reply; + } + + free (user_data); + + return (CS_OK); + +free_error_reply: + free (user_data); +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 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; @@ -256,7 +509,7 @@ static enum sam_parent_action_t sam_parent_handler (int pipe_fd, pid_t child_pid action = SAM_PARENT_ACTION_CONTINUE; while (action == SAM_PARENT_ACTION_CONTINUE) { - pfds.fd = pipe_fd; + pfds.fd = parent_fd_in; pfds.events = POLLIN; pfds.revents = 0; @@ -309,7 +562,7 @@ static enum sam_parent_action_t sam_parent_handler (int pipe_fd, pid_t child_pid /* * We have EOF or command in pipe */ - bytes_read = read (pipe_fd, &command, 1); + bytes_read = sam_safe_read (parent_fd_in, &command, 1); if (bytes_read == 0) { /* @@ -324,37 +577,33 @@ static enum sam_parent_action_t sam_parent_handler (int pipe_fd, pid_t child_pid } if (bytes_read == -1) { - /* - * Something really bad happened in read side - */ - if (errno == EAGAIN || errno == EINTR) { - continue; - } - action = SAM_PARENT_ACTION_ERROR; goto action_exit; } /* - * We have read command -> take status + * We have read command */ - switch (status) { - case 0: - /* - * Not started yet - */ - if (command == SAM_COMMAND_START) + switch (command) { + case SAM_COMMAND_START: + if (status == 0) { + /* + * Not started yet + */ status = 1; - break; - - case 1: - /* - * Started - */ - if (command == SAM_COMMAND_STOP) + } + break; + case SAM_COMMAND_STOP: + if (status == 1) { + /* + * Started + */ status = 0; - break; - + } + break; + case SAM_COMMAND_DATA_STORE: + sam_parent_data_store (parent_fd_in, parent_fd_out); + break; } } /* select_error > 0 */ } /* action == SAM_PARENT_ACTION_CONTINUE */ @@ -369,7 +618,7 @@ cs_error_t sam_register ( cs_error_t error; pid_t pid; int pipe_error; - int pipe_fd[2]; + int pipe_fd_out[2], pipe_fd_in[2]; enum sam_parent_action_t action; int child_status; @@ -380,12 +629,15 @@ cs_error_t sam_register ( error = CS_OK; while (1) { - pipe_error = pipe (pipe_fd); + if ((pipe_error = pipe (pipe_fd_out)) != 0) { + error = CS_ERR_LIBRARY; + goto error_exit; + } + + if ((pipe_error = pipe (pipe_fd_in)) != 0) { + close (pipe_fd_out[0]); + close (pipe_fd_out[1]); - if (pipe_error != 0) { - /* - * Pipe creation error - */ error = CS_ERR_LIBRARY; goto error_exit; } @@ -410,12 +662,16 @@ cs_error_t sam_register ( /* * Child process */ - close (pipe_fd[0]); + 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]; - sam_internal_data.parent_fd = pipe_fd[1]; 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; goto error_exit; @@ -423,11 +679,13 @@ cs_error_t sam_register ( /* * Parent process */ - close (pipe_fd[1]); + close (pipe_fd_out[1]); + close (pipe_fd_in[0]); - action = sam_parent_handler (pipe_fd[0], pid); + action = sam_parent_handler (pipe_fd_out[0], pipe_fd_in[1], pid); - close (pipe_fd[0]); + close (pipe_fd_out[0]); + close (pipe_fd_in[1]); if (action == SAM_PARENT_ACTION_ERROR) { error = CS_ERR_LIBRARY; @@ -498,7 +756,7 @@ static void *hc_callback_thread (void *unused_param) } if (poll_error > 0) { - bytes_readed = read (sam_internal_data.cb_rpipe_fd, &command, 1); + bytes_readed = sam_safe_read (sam_internal_data.cb_rpipe_fd, &command, 1); if (bytes_readed > 0) { if (status == 0 && command == SAM_COMMAND_START) diff --git a/man/Makefile.am b/man/Makefile.am index da01c2e5..5817a1af 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -100,6 +100,9 @@ dist_man_MANS = \ votequorum_qdisk_unregister.3 \ votequorum_setexpected.3 \ votequorum_setvotes.3 \ + sam_data_getsize.3 \ + sam_data_restore.3 \ + sam_data_store.3 \ sam_finalize.3 \ sam_hc_callback_register.3 \ sam_hc_send.3 \ diff --git a/man/sam_data_getsize.3 b/man/sam_data_getsize.3 new file mode 100644 index 00000000..33d527e1 --- /dev/null +++ b/man/sam_data_getsize.3 @@ -0,0 +1,68 @@ +.\"/* +.\" * Copyright (c) 2010 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. +.\" */ +.TH "SAM_DATA_GETSIZE" 3 "04/15/2010" "corosync Man Page" "Corosync Cluster Engine Programmer's Manual" + +.SH NAME +.P +sam_data_getsize \- Return size of stored data in bytes + +.SH SYNOPSIS +.P +\fB#include \fR + +.P +\fBcs_error_t sam_data_getsize (size_t *\fIsize\fB);\fR + +.SH DESCRIPTION +.P +The \fBsam_data_getsize\fR function is used to return size of stored +data. Size is returned in bytes. If user data is NULL, zero is returned. +Function is intended to be used before \fBsam_data_restore(3)\fR call to +properly allocate buffer for restored data. + +.SH RETURN VALUE +.P +This call return CS_OK value if successful, otherwise and error is returned. + +.SH ERRORS +.TP +CS_ERR_BAD_HANDLE +component was not initialized by calling \fBsam_initialize(3)\fR or it was finalized. +.TP +CS_ERR_INVALID_PARAM +size parameter is NULL + +.SH "SEE ALSO" +.BR sam_data_store (3), +.BR sam_data_restore (3) diff --git a/man/sam_data_restore.3 b/man/sam_data_restore.3 new file mode 100644 index 00000000..32b816a1 --- /dev/null +++ b/man/sam_data_restore.3 @@ -0,0 +1,77 @@ +.\"/* +.\" * Copyright (c) 2010 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. +.\" */ +.TH "SAM_DATA_RESTORE" 3 "04/15/2010" "corosync Man Page" "Corosync Cluster Engine Programmer's Manual" + +.SH NAME +.P +sam_data_restore \- Restore previously saved user data + +.SH SYNOPSIS +.P +\fB#include \fR + +.P +\fBcs_error_t sam_data_restore (void *\fIdata\fB, size_t \fIsize\fB);\fR + +.SH DESCRIPTION +.P +The \fBsam_data_restore\fR function is used to restore data, previously +saved by calling \fBsam_data_store(3)\fR. Such data survives between instances. + +.P +The \fIdata\fR parameter is pointer to memory initialized by caller. Stored data +are copied there. This also means, that caller is responsible for freeing memory. + +.P +The \fIsize\fR parameter is length of \fIdata\fR. This one must be at least same +length as previously stored data otherwise error is returned. Parameter can +be larger but only stored data size bytes are changed. + +Use \fBsam_data_getsize(3)\fR to find out length of stored data. + +.SH RETURN VALUE +.P +This call return CS_OK value if successful, otherwise and error is returned. + +.SH ERRORS +.TP +CS_ERR_BAD_HANDLE +component was not initialized by calling \fBsam_initialize(3)\fR or it was finalized. +.TP +CS_ERR_INVALID_PARAM +data parameter is NULL or size is less then currently stored data length + +.SH "SEE ALSO" +.BR sam_data_getsize (3), +.BR sam_data_store (3) diff --git a/man/sam_data_store.3 b/man/sam_data_store.3 new file mode 100644 index 00000000..6e906513 --- /dev/null +++ b/man/sam_data_store.3 @@ -0,0 +1,83 @@ +.\"/* +.\" * Copyright (c) 2010 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. +.\" */ +.TH "SAM_DATA_STORE" 3 "04/15/2010" "corosync Man Page" "Corosync Cluster Engine Programmer's Manual" + +.SH NAME +.P +sam_data_store \- Store user data + +.SH SYNOPSIS +.P +\fB#include \fR + +.P +\fBcs_error_t sam_data_store (const void *\fIdata\fB, size_t \fIsize\fB);\fR + +.SH DESCRIPTION +.P +The \fBsam_data_store\fR function is used to store data, which survives between +instances. + +.P +The \fIdata\fR parameter is pointer to memory with data to store. Data +are stored in newly allocated memory inside library, so caller can safely remove +them after call of function. + +You can use NULL as parameter to remove and free previously saved data. In this +case \fIsize\fR argument is ignored. + +.P +The \fIsize\fR parameter is length of \fIdata\fR. + +Use \fBsam_data_getsize(3)\fR to find out length of stored data and \fBsam_data_restore(3)\fR +to restore stored data. + +.SH RETURN VALUE +.P +This call return CS_OK value if successful, otherwise and error is returned. + +.SH ERRORS +.TP +CS_ERR_BAD_HANDLE +component was not initialized by calling \fBsam_initialize(3)\fR or it was finalized. +.TP +CS_ERR_NO_MEMORY +internal malloc/realloc failed because data are too large +.TP +CS_ERR_LIBRARY +some internal error appeared (mostly because communication with parent process failed) + +.SH "SEE ALSO" +.BR sam_data_getsize (3), +.BR sam_data_restore (3) diff --git a/man/sam_overview.8 b/man/sam_overview.8 index 45472129..b670723c 100644 --- a/man/sam_overview.8 +++ b/man/sam_overview.8 @@ -115,9 +115,19 @@ or add timers to the active process to signal a healthcheck operation is successful. To use event driven healthchecking, the \fBsam_hc_callback_register(3)\fR function should be executed. +.SH Storing user data +.P +Sometimes there is need to store some data, which survives between instances. +One can in such case use files, databases, ... or much simpler in memory solution +presented by \fBsam_data_store(3)\fR, \fBsam_data_restore(3)\fR and \fBsam_data_getsize(3)\fR +functions. + .SH BUGS .SH "SEE ALSO" .BR sam_initialize (3), +.BR sam_data_getsize (3), +.BR sam_data_restore (3), +.BR sam_data_store (3), .BR sam_finalize (3), .BR sam_start (3), .BR sam_stop (3), diff --git a/test/testsam.c b/test/testsam.c index ae9a4cf1..ce68d2bc 100644 --- a/test/testsam.c +++ b/test/testsam.c @@ -46,10 +46,11 @@ #include #include #include +#include #include static int test2_sig_delivered = 0; -static int test4_hc_cb_count = 0; +static int test5_hc_cb_count = 0; /* * First test will just register SAM, with policy restart. First instance will @@ -273,11 +274,262 @@ static int test3 (void) { } -static int test4_hc_cb (void) +/* + * Test sam_data_store, sam_data_restore and sam_data_getsize + */ +static int test4 (void) { - printf ("%s %d\n", __FUNCTION__, ++test4_hc_cb_count); + size_t size; + cs_error_t err; + int i; + unsigned int instance_id; + char saved_data[128]; + char saved_data2[128]; - if (test4_hc_cb_count > 10) + 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) +{ + printf ("%s %d\n", __FUNCTION__, ++test5_hc_cb_count); + + sam_data_store (&test5_hc_cb_count, sizeof (test5_hc_cb_count)); + + if (test5_hc_cb_count > 10) return 1; return 0; @@ -285,10 +537,11 @@ static int test4_hc_cb (void) /* * Test event driven healtchecking. */ -static int test4 (void) +static int test5 (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); @@ -305,7 +558,7 @@ static int test4 (void) if (instance_id == 1) { printf ("%s iid %d: hc callback register\n", __FUNCTION__, instance_id); - error = sam_hc_callback_register (test4_hc_cb); + error = sam_hc_callback_register (test5_hc_cb); if (error != CS_OK) { fprintf (stderr, "Can't register hc cb. Error %d\n", error); return 1; @@ -326,12 +579,25 @@ static int test4 (void) } 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; } + int main(int argc, char *argv[]) { pid_t pid; @@ -347,17 +613,14 @@ int main(int argc, char *argv[]) } if (pid == 0) { - err = test1 (); - - fprintf (stderr, "test1 %s\n", (err == 0 ? "passed" : "failed")); - if (err != 0) - all_passed = 0; - - return err; + return (test1 ()); } - waitpid (pid, NULL, 0); + waitpid (pid, &stat, 0); + fprintf (stderr, "test1 %s\n", (WEXITSTATUS (stat) == 0 ? "passed" : "failed")); + if (WEXITSTATUS (stat) != 0) + all_passed = 0; pid = fork (); @@ -386,15 +649,14 @@ int main(int argc, char *argv[]) } if (pid == 0) { - err = test3 (); - - fprintf (stderr, "test3 %s\n", (err == 0 ? "passed" : "failed")); - if (err != 0) - all_passed = 0; - return err; + return (test3 ()); } - waitpid (pid, NULL, 0); + waitpid (pid, &stat, 0); + + fprintf (stderr, "test3 %s\n", (WEXITSTATUS (stat) == 0 ? "passed" : "failed")); + if (WEXITSTATUS (stat) != 0) + all_passed = 0; pid = fork (); @@ -404,15 +666,32 @@ int main(int argc, char *argv[]) } if (pid == 0) { - err = test4 (); + return (test4 ()); + } + + 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 (); - fprintf (stderr, "test4 %s\n", (err == 0 ? "passed" : "failed")); - if (err != 0) - all_passed = 0; return err; } - waitpid (pid, NULL, 0); + waitpid (pid, &stat, 0); + fprintf (stderr, "test5 %s\n", (WEXITSTATUS (stat) == 0 ? "passed" : "failed")); + if (WEXITSTATUS (stat) != 0) + all_passed = 0; if (all_passed) fprintf (stderr, "All tests passed\n");