/*___INFO__MARK_BEGIN__*/ /************************************************************************* * * The Contents of this file are made available subject to the terms of * the Sun Industry Standards Source License Version 1.2 * * Sun Microsystems Inc., March, 2001 * * * Sun Industry Standards Source License Version 1.2 * ================================================= * The contents of this file are subject to the Sun Industry Standards * Source License Version 1.2 (the "License"); You may not use this file * except in compliance with the License. You may obtain a copy of the * License at http://gridengine.sunsource.net/Gridengine_SISSL_license.html * * Software provided under this License is provided on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, * WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS, * MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING. * See the License for the specific provisions governing your rights and * obligations concerning the Software. * * The Initial Developer of the Original Code is: Sun Microsystems, Inc. * * Copyright: 2001 by Sun Microsystems, Inc. * * All Rights Reserved. * ************************************************************************/ /*___INFO__MARK_END__*/ #include #include #include #include #include #include #include #ifndef TIOCGWINSZ #include /* 44BSD requires this too */ #endif #include "uti/sge_arch.h" #include "uti/config_file.h" #include "uti/sge_string.h" #include "cl_data_types.h" #include "cl_commlib.h" #include "cl_endpoint_list.h" #include "sgermon.h" #include "sge_utility.h" #include "sge_security.h" #include "sge_ijs_comm.h" #include "sge_mtutil.h" #include "msg_commlistslib.h" #include "sge_log.h" #include "msg_gdilib.h" extern sig_atomic_t received_signal; /* * TODO: Cleanup / Headers * This is just slightly modified copy of the gdi commlib error handling, * perhaps it's possible to do minimal changes in the original functions * and remove these. */ static pthread_mutex_t ijs_general_communication_error_mutex = PTHREAD_MUTEX_INITIALIZER; /* local static struct to store communication errors. The boolean * values com_access_denied and com_endpoint_not_unique will never be * restored to false again */ typedef struct sge_gdi_com_error_type { int com_error; /* current commlib error */ bool com_was_error; /* set if there was an communication error (but not CL_RETVAL_ACCESS_DENIED or CL_RETVAL_ENDPOINT_NOT_UNIQUE)*/ int com_last_error; /* last logged commlib error */ bool com_access_denied; /* set when commlib reports CL_RETVAL_ACCESS_DENIED */ int com_access_denied_counter; /* counts access denied errors (TODO: workaround for BT: 6350264, IZ: 1893) */ unsigned long com_access_denied_time; /* timeout for counts access denied errors (TODO: workaround for BT: 6350264, IZ: 1893) */ bool com_endpoint_not_unique; /* set when commlib reports CL_RETVAL_ENDPOINT_NOT_UNIQUE */ int com_endpoint_not_unique_counter; /* counts access denied errors (TODO: workaround for BT: 6350264, IZ: 1893) */ unsigned long com_endpoint_not_unique_time; /* timeout for counts access denied errors (TODO: workaround for BT: 6350264, IZ: 1893) */ } sge_gdi_com_error_t; static sge_gdi_com_error_t ijs_communication_error = {CL_RETVAL_OK, false, CL_RETVAL_OK, false, 0, 0, false, 0, 0}; static bool do_timeout_handling(unsigned long *time, int *counter) { struct timeval now; unsigned long time_diff = 0; bool ret = false; gettimeofday(&now, NULL); if ((now.tv_sec - *time) > (3 * CL_DEFINE_READ_TIMEOUT)) { *time = 0; *counter = 0; } if (*time < now.tv_sec) { if (*time == 0) { time_diff = 1; } else { time_diff = now.tv_sec - *time; } *counter += time_diff; if (*counter > 2*CL_DEFINE_READ_TIMEOUT) { ret = true; } *time = now.tv_sec; } return ret; } static void ijs_general_communication_error( const cl_application_error_list_elem_t *commlib_error) { DENTER(TOP_LAYER, "ijs_general_communication_error"); if (commlib_error == NULL) { DEXIT; return; } sge_mutex_lock("ijs_general_communication_error_mutex", SGE_FUNC, __LINE__, &ijs_general_communication_error_mutex); /* save the communication error to react later */ ijs_communication_error.com_error = commlib_error->cl_error; switch (commlib_error->cl_error) { case CL_RETVAL_OK: break; case CL_RETVAL_ACCESS_DENIED: if (ijs_communication_error.com_access_denied == false) { /* counts access denied errors (TODO: workaround for BT: 6350264, IZ: 1893) */ /* increment counter only once per second and allow max CL_DEFINE_READ_TIMEOUT + 2 access denied */ ijs_communication_error.com_access_denied = do_timeout_handling(&ijs_communication_error.com_access_denied_time, &ijs_communication_error.com_access_denied_counter); } break; case CL_RETVAL_ENDPOINT_NOT_UNIQUE: if (ijs_communication_error.com_endpoint_not_unique == false) { /* counts endpoint not unique errors (TODO: workaround for BT: 6350264, IZ: 1893) */ /* increment counter only once per second and allow max CL_DEFINE_READ_TIMEOUT + 2 endpoint not unique */ DPRINTF(("got endpint not unique")); ijs_communication_error.com_endpoint_not_unique = do_timeout_handling(&ijs_communication_error.com_endpoint_not_unique_time, &ijs_communication_error.com_endpoint_not_unique_counter); } break; default: ijs_communication_error.com_was_error = true; break; } /* * now log the error if not already reported the * least CL_DEFINE_MESSAGE_DUP_LOG_TIMEOUT seconds */ if (commlib_error->cl_already_logged == CL_FALSE && ijs_communication_error.com_last_error != ijs_communication_error.com_error) { /* never log the same messages again and again (commlib * will erase cl_already_logged flag every CL_DEFINE_MESSAGE_DUP_LOG_TIMEOUT * seconds (30 seconds), so we have to save the last one! */ ijs_communication_error.com_last_error = ijs_communication_error.com_error; switch (commlib_error->cl_err_type) { case CL_LOG_ERROR: if (commlib_error->cl_info != NULL) { ERROR((SGE_EVENT, MSG_GDI_GENERAL_COM_ERROR_SS, cl_get_error_text(commlib_error->cl_error), commlib_error->cl_info)); } else { ERROR((SGE_EVENT, MSG_GDI_GENERAL_COM_ERROR_S, cl_get_error_text(commlib_error->cl_error))); } break; case CL_LOG_WARNING: if (commlib_error->cl_info != NULL) { WARNING((SGE_EVENT, MSG_GDI_GENERAL_COM_ERROR_SS, cl_get_error_text(commlib_error->cl_error), commlib_error->cl_info)); } else { WARNING((SGE_EVENT, MSG_GDI_GENERAL_COM_ERROR_S, cl_get_error_text(commlib_error->cl_error))); } break; case CL_LOG_INFO: if (commlib_error->cl_info != NULL) { INFO((SGE_EVENT, MSG_GDI_GENERAL_COM_ERROR_SS, cl_get_error_text(commlib_error->cl_error), commlib_error->cl_info)); } else { INFO((SGE_EVENT, MSG_GDI_GENERAL_COM_ERROR_S, cl_get_error_text(commlib_error->cl_error))); } break; case CL_LOG_DEBUG: if (commlib_error->cl_info != NULL) { DEBUG((SGE_EVENT, MSG_GDI_GENERAL_COM_ERROR_SS, cl_get_error_text(commlib_error->cl_error), commlib_error->cl_info)); } else { DEBUG((SGE_EVENT, MSG_GDI_GENERAL_COM_ERROR_S, cl_get_error_text(commlib_error->cl_error))); } break; case CL_LOG_OFF: break; } } sge_mutex_unlock("ijs_general_communication_error_mutex", SGE_FUNC, __LINE__, &ijs_general_communication_error_mutex); DRETURN_VOID; } int comm_get_application_error(dstring *err_msg) { int ret = COMM_RETVAL_OK; DENTER(TOP_LAYER, "comm_get_application_error"); sge_mutex_lock("ijs_general_communication_error_mutex", SGE_FUNC, __LINE__, &ijs_general_communication_error_mutex); if (ijs_communication_error.com_endpoint_not_unique == true) { sge_dstring_sprintf(err_msg, "%s", MSG_CL_RETVAL_ENDPOINT_NOT_UNIQUE); DPRINTF(("%s", sge_dstring_get_string(err_msg))); ret = COMM_ENDPOINT_NOT_UNIQUE; } if (ijs_communication_error.com_access_denied == true) { sge_dstring_sprintf(err_msg, "%s", MSG_CL_RETVAL_ACCESS_DENIED); DPRINTF(("%s", sge_dstring_get_string(err_msg))); ret = COMM_ACCESS_DENIED; } sge_mutex_unlock("ijs_general_communication_error_mutex", SGE_FUNC, __LINE__, &ijs_general_communication_error_mutex); DRETURN(ret); } /* redirects the commlib logging to a file */ /* this is a modified copy of the cl_log_list_flush_list() */ int my_log_list_flush_list(cl_raw_list_t* list_p) { cl_log_list_elem_t *elem = NULL; FILE *fp = NULL; struct timeval now; int ret_val; if (list_p == NULL) { return CL_RETVAL_LOG_NO_LOGLIST; } if ((ret_val = cl_raw_list_lock(list_p)) != CL_RETVAL_OK) { return ret_val; } if ((fp = fopen("cl_log.txt", "a")) == NULL) { return CL_RETVAL_NOT_OPEN; } while ((elem = cl_log_list_get_first_elem(list_p)) != NULL) { gettimeofday(&now,NULL); fprintf(fp, "%-76s|", elem->log_module_name); if (elem->log_parameter == NULL) { fprintf(fp, "%ld.%ld|%20s|%s|%s| %s\n", (long)now.tv_sec, (long)now.tv_usec, elem->log_thread_name, cl_thread_convert_state_id(elem->log_thread_state), cl_log_list_convert_type_id(elem->log_type), elem->log_message); } else { fprintf(fp, "%ld.%ld|%20s|%s|%s| %s %s\n", (long)now.tv_sec, (long)now.tv_usec, elem->log_thread_name, cl_thread_convert_state_id(elem->log_thread_state), cl_log_list_convert_type_id(elem->log_type), elem->log_message, elem->log_parameter); } cl_log_list_del_log(list_p); fflush(fp); } fclose(fp); return cl_raw_list_unlock(list_p); } /****** sge_ijs_comm/comm_init_lib() ******************************************* * NAME * comm_init_lib() -- Initializes the communication library * * SYNOPSIS * int comm_init_lib(dstring *err_msg) * * FUNCTION * Initializes the communication library, call it before using any other * communication function. * * INPUTS * dstring *err_msg - Gets the error reason in case of error. * * RESULT * int - COMM_RETVAL_OK: * Communication library was successfully initialized. * * COMM_CANT_SETUP_COMMLIB: * Error initializing the communication library, err_msg contains * the error reason. * * NOTES * MT-NOTE: comm_init_lib() is not MT safe * * SEE ALSO * communication/comm_cleanup_lib() *******************************************************************************/ int comm_init_lib(dstring *err_msg) { int ret, ret_val = COMM_RETVAL_OK; DENTER(TOP_LAYER, "comm_init_lib"); /* * To enable commlib logging to a file (see my_log_list_flush_list() * for the file path), exchange this line with the one below. * Caution: On some architectures, logging causes problems! */ /*ret = cl_com_setup_commlib(CL_RW_THREAD, CL_LOG_DEBUG, my_log_list_flush_list);*/ ret = cl_com_setup_commlib(CL_RW_THREAD, CL_LOG_OFF, NULL); if (ret != CL_RETVAL_OK) { sge_dstring_sprintf(err_msg, cl_get_error_text(ret)); DPRINTF(("cl_com_setup_commlib() failed: %s (%d)\n", sge_dstring_get_string(err_msg), ret)); ret_val = COMM_CANT_SETUP_COMMLIB; } else { const char *alias_path = NULL; /* set the alias file */ alias_path = sge_get_alias_path(); ret = cl_com_set_alias_file(alias_path); if (ret != CL_RETVAL_OK) { sge_dstring_sprintf(err_msg, cl_get_error_text(ret)); DPRINTF(("cl_com_set_alias_file() failed: %s (%d)\n", sge_dstring_get_string(err_msg), ret)); ret_val = COMM_CANT_SETUP_COMMLIB; } FREE(alias_path); if (ret_val == COMM_RETVAL_OK) { cl_host_resolve_method_t resolve_method = CL_SHORT; char *help = NULL; char *default_domain = NULL; /* setup the resolve method */ if (atoi(get_conf_val("ignore_fqdn")) == 0) { resolve_method = CL_LONG; } if ((help = get_conf_val("default_domain")) != NULL) { if (SGE_STRCASECMP(help, NONE_STR) != 0) { default_domain = help; } } ret = cl_com_set_resolve_method(resolve_method, default_domain); if (ret != CL_RETVAL_OK) { sge_dstring_sprintf(err_msg, cl_get_error_text(ret)); DPRINTF(("cl_com_set_resolve_method() failed: %s (%d)\n", sge_dstring_get_string(err_msg), ret)); ret_val = COMM_CANT_SETUP_COMMLIB; } } } DRETURN(ret_val); } /****** sge_ijs_comm/comm_cleanup_lib() *************************************** * NAME * comm_cleanup_lib() -- Clean up the communication library * * SYNOPSIS * int comm_cleanup_lib(dstring *err_msg) * * FUNCTION * Cleans up the communication library. Call it when done using the library. * * INPUTS * dstring *err_msg - Pointer to a dstring that receives a static error * string. If no error happens it get's set to * "no error happened". * * RESULT * int - COMM_RETVAL_OK: * Communication library was successfully cleaned up. * * COMM_CANT_CLEANUP_COMMLIB: * Error cleaning up the communication library, err_msg contains * the error reason. * * NOTES * MT-NOTE: comm_cleanup_lib() is not MT safe * * SEE ALSO * communication/comm_init_lib() *******************************************************************************/ int comm_cleanup_lib(dstring *err_msg) { int ret, ret_val = COMM_RETVAL_OK; DENTER(TOP_LAYER, "comm_cleanup_lib"); ret = cl_com_cleanup_commlib(); if (ret != CL_RETVAL_OK) { sge_dstring_sprintf(err_msg, cl_get_error_text(ret)); DPRINTF(("cl_com_cleanup_commlib() failed: %s (%d)\n", sge_dstring_get_string(err_msg), ret)); ret_val = COMM_CANT_CLEANUP_COMMLIB; } DEXIT; return ret_val; } /****** sge_ijs_comm/comm_open_connection() *********************************** * NAME * comm_open_connection() -- Connects to or starts a comm server * * SYNOPSIS * int comm_open_connection(bool b_server, int port, * const char *component_name, bool b_secure, const char *user_name, * COMM_HANDLE **handle, dstring *err_msg) * * FUNCTION * Either start a comm server or connect to a running comm server. * * INPUTS * bool b_server - If true, a comm server is started, if false * a connection to a server is established. * bool b_secure - If true: Use secured connections * const char *this_component - A unique name for this end of the connection. * int port - In case of server: Port on which the server * should listen. If this is 0, a free port is * selected. * In case of client: Port on which the server * listens. * const char *other_component - The unique name of the other end of the * connection. * char *remote_host - Name of the host to connect to. Ignored if * b_server == true. * const char *user_name - For secured connections: Name of the user * whose certificates are to be used. * Ignored for unsecured connections. * COMM_HANDLE **handle - The address of a COMM_HANDLE pointer * which must be initialized to NULL. * dstring *err_msg - Pointer to an empty dstring to receive * error messages. * * OUTPUT * COMM_HANDLE **handle - The COMM_HANDLE of the connection. * dstring *err_msg - In case of error: The error reason. * * * RESULT * int - COMM_RETVAL_OK: * Connection was successfully opened. * * COMM_INVALID_PARAMETER: * The *handle is not NULL. * * COMM_CANT_SETUP_SSL: * err_msg contains the reason. * * COMM_CANT_CREATE_HANDLE: * err_msg contains the reason. * * NOTES * MT-NOTE: comm_open_connection() is not MT safe * * SEE ALSO * communication/comm_shutdown_connection() *******************************************************************************/ int comm_open_connection(bool b_server, bool b_secure, const char *this_component, int port, const char *other_component, char *remote_host, const char *user_name, COMM_HANDLE **handle, dstring *err_msg) { int ret; int old_euid = SGE_SUPERUSER_UID; int ret_val = COMM_RETVAL_OK; int commlib_error = CL_RETVAL_OK; cl_framework_t communication_framework = CL_CT_TCP; cl_tcp_connect_t connect_type = CL_TCP_DEFAULT; cl_xml_connection_type_t connection_type = CL_CM_CT_MESSAGE; DENTER(TOP_LAYER, "open_connection"); /* Check validity of parameters */ if (*handle != NULL) { sge_dstring_sprintf(err_msg, "Invalid parameter: *handle is not NULL"); DPRINTF((sge_dstring_get_string(err_msg))); DEXIT; return COMM_INVALID_PARAMETER; } if (b_secure == true) { #ifdef SECURE communication_framework = CL_CT_SSL; /* * Got to do this with euid = root */ if (getuid() == SGE_SUPERUSER_UID) { old_euid = geteuid(); seteuid(SGE_SUPERUSER_UID); } ret = sge_ssl_setup_security_path(this_component, user_name); /* * Switch back to old euid before error handling to do tracing as * the SGE admin user. */ if (old_euid != SGE_SUPERUSER_UID) { seteuid(old_euid); } if (ret != 0) { DPRINTF(("sge_ssl_setup_security_path() failed!\n")); sge_dstring_sprintf(err_msg, "Setting up SSL failed!"); ret_val = COMM_CANT_SETUP_SSL; } #else /* * If secure communication was requested but we cannot provide it * because seclib support was not compiled in, we must not fall back to * insecure mode, instead we must return with a fatal error. */ sge_dstring_sprintf(err_msg, "No security support compiled into this binary!"); DPRINTF(("%s\n", sge_dstring_get_string(err_msg))); return COMM_NO_SECURITY_COMPILED_IN; #endif } if (ret_val == COMM_RETVAL_OK) { /* * Define a error handling function for the commlib here - * the default error handling function of the commlib prints * error messages to stderr! */ ret = cl_com_set_error_func(ijs_general_communication_error); if (ret != CL_RETVAL_OK) { sge_dstring_sprintf(err_msg, "can't set commlib error function: %s", cl_get_error_text(ret)); DPRINTF(("cl_com_set_error_func() failed: %s (%d)\n", sge_dstring_get_string(err_msg), ret)); ret_val = COMM_CANT_SETUP_COMMLIB; } else { DPRINTF(("trying to create commlib handle\n")); if (b_server == false) { *handle = cl_com_create_handle(&commlib_error, communication_framework, connection_type, CL_FALSE, port, connect_type, (char*)this_component, 0, 1, 0); } else { *handle = cl_com_create_handle(&commlib_error, communication_framework, connection_type, CL_TRUE, port, connect_type, (char*)this_component, 1, 1, 0); } if (*handle == NULL) { sge_dstring_sprintf(err_msg, cl_get_error_text(commlib_error)); DPRINTF(("cl_com_create_handle() failed: %s (%d)\n", sge_dstring_get_string(err_msg), commlib_error)); ret_val = COMM_CANT_CREATE_HANDLE; } else { /* Set connection timeout to 'infinite' */ (*handle)->connection_timeout = 0x0fffffff; DPRINTF(("(*handle)->connect_port = %d\n", (*handle)->connect_port)); DPRINTF(("(*handle)->service_port = %d\n", (*handle)->service_port)); /* Set synchron receive timeout */ cl_com_set_synchron_receive_timeout(*handle, 1); } } } /* * Need to do this as SUPERUSER, because in csp mode we need the permissions * to load the job users keys. */ if (b_server == false) { if (getuid() == SGE_SUPERUSER_UID) { old_euid = geteuid(); seteuid(SGE_SUPERUSER_UID); } ret = cl_commlib_open_connection(*handle, remote_host, (char*)other_component, 1); if (old_euid != SGE_SUPERUSER_UID) { seteuid(old_euid); } if (ret != CL_RETVAL_OK) { ret_val = COMM_CANT_OPEN_CONNECTION; } } if (ret_val == COMM_RETVAL_OK) { ret_val = comm_get_application_error(err_msg); } DEXIT; return ret_val; } /****** sge_ijs_comm/comm_shutdown_connection() ******************************* * NAME * comm_shutdown_connection() -- gracefully shuts down a connection * * SYNOPSIS * int comm_shutdown_connection(COMM_HANDLE *handle, * const char *component_name, dstring *err_msg) * * FUNCTION * All connections get closed and then the communication handle gets freed. * * INPUTS * COMM_HANDLE *handle - Handle of the connection to be shut down. * const char *component_name - Name of the remote component of the * connection to be shut down. * char *remote_host - Name of the server we are connected to. * Ignored if we are a server. * dstring *err_msg - Gets the error reason in case of error. * * RESULT * int - COMM_RETVAL_OK: * Connection was successfully opened. * * COMM_CANT_CLOSE_CONNECTION: * err_msg contains the reason. * * COMM_CANT_SHUTDOOWN_HANDLE: * err_msg contains the reason. * * NOTES * MT-NOTE: comm_shutdown_connection() is not MT safe * * SEE ALSO * communication/comm_open_connection() *******************************************************************************/ int comm_shutdown_connection(COMM_HANDLE *handle, const char *component_name, char *remote_host, dstring *err_msg) { int ret; int ret_val = COMM_RETVAL_OK; DENTER(TOP_LAYER, "comm_shutdown_connection"); /* * From here on the user shouldn't get informed of any errors occuring * during the shutdown of the connection - just shut down. */ ret = cl_com_set_error_func(NULL); ret = cl_commlib_close_connection(handle, remote_host, (char*)component_name, 1, CL_FALSE); if (ret != CL_RETVAL_OK && ret != CL_RETVAL_UNKNOWN_ENDPOINT) { /* shutting down the endpoint returned commlib error */ sge_dstring_sprintf(err_msg, cl_get_error_text(ret)); DPRINTF(("cl_commlib_close_connection() failed: %s (%d)\n", sge_dstring_get_string(err_msg), ret)); ret_val = COMM_CANT_CLOSE_CONNECTION; cl_com_ignore_timeouts(CL_TRUE); cl_commlib_shutdown_handle(handle, CL_FALSE); } else { ret = cl_commlib_shutdown_handle(handle, CL_FALSE); if (ret != CL_RETVAL_OK) { sge_dstring_sprintf(err_msg, cl_get_error_text(ret)); DPRINTF(("cl_commlib_close_connection() failed: %s (%d)\n", sge_dstring_get_string(err_msg), ret)); ret_val = COMM_CANT_SHUTDOWN_HANDLE; } } DEXIT; return ret_val; } /****** sge_ijs_comm/comm_set_connection_param() ****************************** * NAME * comm_set_connection_param() -- Set several connection parameters. * * SYNOPSIS * int comm_set_connection_param(COMM_HANDLE *handle, int param, * int value, dstring *err_msg) * * FUNCTION * Sets several connection parameter. Valid parameters are: * HEARD_FROM_TIMEOUT: The time until the communication library will * treat a connection as lost. * * INPUTS * COMM_HANDLE *handle - Handle of the connection. * int param - ID of the param to set. Currently * HEARD_FROM_TIMEOUT (in seconds) is supported. * int value - Value to set the param to. * dstring *err_msg - Gets the error reason in case of error. * * RESULT * int - COMM_RETVAL_OK: * Connection was successfully opened. * * COMM_CANT_SET_CONNECTION_PARAM: * err_msg contains the reason. * * NOTES * MT-NOTE: comm_set_connection_param() is not MT safe *******************************************************************************/ int comm_set_connection_param(COMM_HANDLE *handle, int param, int value, dstring *err_msg) { int ret; int ret_val = COMM_RETVAL_OK; DENTER(TOP_LAYER, "comm_set_connection_param"); ret = cl_commlib_set_connection_param(handle, param, value); if (ret != CL_RETVAL_OK) { sge_dstring_sprintf(err_msg, cl_get_error_text(ret)); DPRINTF(("cl_commlib_set_connection_param() failed: %s (%d)\n", sge_dstring_get_string(err_msg), ret)); ret_val = COMM_CANT_SET_CONNECTION_PARAM; } DEXIT; return ret_val; } /****** sge_ijs_comm/comm_ignore_timeouts() *********************************** * NAME * comm_ignore_timeouts() -- Use timeouts or wait infinitely. * * SYNOPSIS * int comm_ignore_timeouts(bool b_ignore) * * FUNCTION * Tells the communication library to either use timeouts or just wait * until all work is done. * * INPUTS * bool b_ignore - If true, the comm. library ignores timeouts, * if false, timeouts are enabled. * dstring *err_msg - Gets the error reason in case of error. * * * RESULT * int - COMM_RETVAL_OK: * Connection was successfully opened. * * COMM_CANT_SET_IGNORE_TIMEOUTS: * err_msg contains the reason. * * NOTES * MT-NOTE: comm_ignore_timeouts() is not MT safe *******************************************************************************/ int comm_ignore_timeouts(bool b_ignore, dstring *err_msg) { int ret = CL_RETVAL_OK; int ret_val = COMM_RETVAL_OK; DENTER(TOP_LAYER, "comm_ignore_timeouts"); cl_com_ignore_timeouts(b_ignore==true ? CL_TRUE : CL_FALSE); if (ret != CL_RETVAL_OK) { sge_dstring_sprintf(err_msg, cl_get_error_text(ret)); DPRINTF(("cl_com_ignore_timeouts() failed: %s (%d)\n", sge_dstring_get_string(err_msg), ret)); ret_val = COMM_CANT_SET_IGNORE_TIMEOUTS; } DEXIT; return ret_val; } /****** sge_ijs_comm/comm_wait_for_connection() ******************************* * NAME * comm_wait_for_connection() -- Waits until at least one client has connected * * SYNOPSIS * int comm_wait_for_connection(COMM_HANDLE *handle, const char * *component, int wait_secs, const char **host, dstring *err_msg) * * FUNCTION * On a server, waits until at least one client has connected. * * INPUTS * COMM_HANDLE *handle - Handle of the connection. * const char *component - Wait for a client with this component name. * int wait_secs - Wait at most wait_secs seconds. * const char **host - Name of the host from where the client connects. * dstring *err_msg - Gets the error reason in case of error. * * RESULT * int - COMM_RETVAL_OK: * A client is connected to us. * * COMM_GOT_TIMEOUT: * 'wait_seconds' have elapsed. * * COMM_CANT_TRIGGER: * err_msg contains the reason. * * COMM_CANT_SEARCH_ENDPOINT: * err_msg contains the reason. * * COMM_INVALID_PARAMETER: * handle = NULL, err_msg doesn't contain an error reason. * * NOTES * MT-NOTE: comm_wait_for_connection() is not MT safe * * SEE ALSO * communication/comm_wait_for_no_connection() *******************************************************************************/ int comm_wait_for_connection(COMM_HANDLE *handle, const char *component, int wait_secs, const char **host, dstring *err_msg) { int waited_usec = 0; int ret = 0; int ret2 = 0; int ret_val = COMM_RETVAL_OK; cl_raw_list_t *endpoint_list = NULL; cl_endpoint_list_elem_t *endpoint; DENTER(TOP_LAYER, "wait_for_connection"); if (handle == NULL) { return COMM_INVALID_PARAMETER; } /* * In the while loop, do this: * Call cl_commlib_trigger(), ignore the return value (it won't return 99) * Get the list of endpoints of expected kind * If endpointlist is returned and contains 0 elements, sleep for * 10 milliseconds and loop again. */ while ((ret2=cl_commlib_trigger(handle, 0)) != 99 && (ret = cl_commlib_search_endpoint(handle, NULL, (char*)component, 0, CL_TRUE, &endpoint_list)) == CL_RETVAL_OK && endpoint_list != NULL && endpoint_list->elem_count == 0 && waited_usec/1000000 < wait_secs) { cl_endpoint_list_cleanup(&endpoint_list); usleep(10000); waited_usec += 10000; if (received_signal == SIGINT) { break; } } if (waited_usec/1000000 >= wait_secs) { sge_dstring_sprintf(err_msg, "Timeout occured while waiting for connection"); DPRINTF((sge_dstring_get_string(err_msg))); ret_val = COMM_GOT_TIMEOUT; } else if (ret2 != CL_RETVAL_OK) { sge_dstring_sprintf(err_msg, cl_get_error_text(ret2)); DPRINTF(("cl_commlib_trigger() failed: %s (%d)\n", sge_dstring_get_string(err_msg), ret2)); ret_val = COMM_CANT_TRIGGER; } else if (ret != CL_RETVAL_OK) { sge_dstring_sprintf(err_msg, cl_get_error_text(ret)); DPRINTF(("cl_commlib_search_endpoint() failed: %s (%d)\n", sge_dstring_get_string(err_msg), ret)); ret_val = COMM_CANT_SEARCH_ENDPOINT; } if (endpoint_list != NULL) { /* A client connected to us, get it's hostname */ if (endpoint_list->elem_count > 0) { endpoint = cl_endpoint_list_get_first_elem(endpoint_list); FREE(*host); *host = strdup(endpoint->endpoint->comp_host); DPRINTF(("A client from host %s has connected\n", *host)); } cl_endpoint_list_cleanup(&endpoint_list); } if (ret_val == COMM_RETVAL_OK) { ret_val = comm_get_application_error(err_msg); } DEXIT; return ret_val; } /****** sge_ijs_comm/comm_wait_for_no_connection() **************************** * NAME * comm_wait_for_no_connection() -- Wait until no client is connected any * more * * SYNOPSIS * int comm_wait_for_no_connection(COMM_HANDLE *handle, const char * *component, int wait_secs, dstring *err_msg) * * FUNCTION * Waits until no client is connected to us any more. * * INPUTS * COMM_HANDLE *handle - Handle of the connection. * const char *component - Filter for clients with this component name * int wait_secs - Wait at most wait_secs seconds. * dstring *err_msg - Gets the error reason in case of error. * * RESULT * int - COMM_RETVAL_OK: * No client is connected to us. * * COMM_GOT_TIMEOUT: * 'wait_seconds' have elapsed. * * COMM_CANT_TRIGGER: * err_msg contains the reason. * * COMM_CANT_SEARCH_ENDPOINT: * err_msg contains the reason. * * NOTES * MT-NOTE: comm_wait_for_no_connection() is not MT safe * * SEE ALSO * communication/comm_wait_for_connection() *******************************************************************************/ int comm_wait_for_no_connection(COMM_HANDLE *handle, const char *component, int wait_secs, dstring *err_msg) { int waited_usec = 0; int ret = 0; int ret2 = 0; int ret_val = COMM_RETVAL_OK; cl_raw_list_t *endpoint_list = NULL; bool do_exit = false; DENTER(TOP_LAYER, "comm_wait_for_no_connection"); /* * In the while loop, do this: * Call cl_commlib_trigger(), ignore the return value (it won't return 99) * Get the list of endpoints of expected kind * If endpointlist is returned and contains >0 elements, sleep for * 10 milliseconds and loop again. */ while (do_exit == false) { /* Let commlib update it's lists */ ret2 = cl_commlib_trigger(handle, 0); /* Get list of all endpoints */ ret = cl_commlib_search_endpoint(handle, NULL, (char*)component, 0, CL_TRUE, &endpoint_list); if (ret == CL_RETVAL_OK && endpoint_list != NULL && endpoint_list->elem_count > 0 && waited_usec/1000000 < wait_secs) { cl_endpoint_list_cleanup(&endpoint_list); endpoint_list = NULL; usleep(10000); waited_usec += 10000; if (received_signal == SIGINT) { do_exit = true; continue; } } else { DPRINTF(("No known endpoint left or timeout -> exit loop\n")); do_exit = true; continue; } } DPRINTF(("wait_for_no_connection: after while\n")); if (waited_usec/1000000 >= wait_secs) { sge_dstring_sprintf(err_msg, "Timeout occured while waiting for no connection"); DPRINTF((sge_dstring_get_string(err_msg))); ret_val = COMM_GOT_TIMEOUT; } if (ret2 != CL_RETVAL_OK) { sge_dstring_sprintf(err_msg, cl_get_error_text(ret2)); DPRINTF(("cl_commlib_trigger() failed: %s (%d)\n", sge_dstring_get_string(err_msg), ret2)); ret_val = COMM_CANT_TRIGGER; } if (ret != CL_RETVAL_OK) { sge_dstring_sprintf(err_msg, cl_get_error_text(ret)); DPRINTF(("cl_commlib_search_endpoint() failed: %s (%d)\n", sge_dstring_get_string(err_msg), ret)); ret_val = COMM_CANT_SEARCH_ENDPOINT; } if (endpoint_list != NULL) { DPRINTF(("wait_for_no_connection: cleaning up endpoint list\n")); cl_endpoint_list_cleanup(&endpoint_list); } DEXIT; return ret_val; } /****** sge_ijs_comm/comm_get_connection_count() ****************************** * NAME * comm_get_connection_count() -- Retrieves the current number of connections * * SYNOPSIS * int comm_get_connection_count(COMM_HANDLE *handle, dstring *err_msg) * * FUNCTION * Retrieves the current number of connections. * * INPUTS * COMM_HANDLE *handle - Handle of the connection. * dstring *err_msg - Gets the error reason in case of error. * * RESULT * int - Number of connections. * <0 in case of error: * -COMM_CANT_LOCK_CONNECTION_LIST: * err_msg contains the reason. * * -COMM_CANT_UNLOCK_CONNECTION_LIST: * err_msg contains the reason. * * * NOTES * MT-NOTE: comm_get_connection_count() is not MT safe *******************************************************************************/ int comm_get_connection_count(const COMM_HANDLE *handle, dstring *err_msg) { int ret; int ret_val = 1; cl_connection_list_elem_t* elem = NULL; DENTER(TOP_LAYER, "comm_get_connection_count"); ret = cl_raw_list_lock(handle->connection_list); if (ret != CL_RETVAL_OK) { sge_dstring_sprintf(err_msg, cl_get_error_text(ret)); DPRINTF(("cl_raw_list_lock() failed: %s (%d)\n", sge_dstring_get_string(err_msg), ret)); ret_val = -COMM_CANT_LOCK_CONNECTION_LIST; } else { elem = cl_connection_list_get_first_elem(handle->connection_list); if (elem == NULL) { ret_val = 0; } ret = cl_raw_list_unlock(handle->connection_list); if (ret != CL_RETVAL_OK) { sge_dstring_sprintf(err_msg, cl_get_error_text(ret)); DPRINTF(("cl_raw_list_unlock() failed: %s (%d)\n", sge_dstring_get_string(err_msg), ret)); ret_val = -COMM_CANT_UNLOCK_CONNECTION_LIST; } } DEXIT; return ret_val; } /****** sge_ijs_comm/comm_trigger() ******************************************* * NAME * comm_trigger() -- Trigger communication library * * SYNOPSIS * int comm_trigger(COMM_HANDLE *handle, int synchron, dstring *err_msg) * * FUNCTION * Triggers the communication library to do pending tasks. * * INPUTS * COMM_HANDLE *handle - Handle of the connection. * int synchron - Set to != 0 to wait until all pending * messages are sent, == 0 to just do one * piece of work and return then. * dstring *err_msg - Gets the error reason in case of error. * * RESULT * int - COMM_RETVAL_OK: * Trigger was successful. * * COMM_GOT_TIMEOUT: * 'wait_seconds' have elapsed. * * COMM_CANT_TRIGGER: * err_msg contains the reason. * * COMM_CANT_SEARCH_ENDPOINT: * err_msg contains the reason. * * NOTES * MT-NOTE: comm_trigger() is not MT safe *******************************************************************************/ int comm_trigger(COMM_HANDLE *handle, int synchron, dstring *err_msg) { int ret; int ret_val = COMM_RETVAL_OK; DENTER(TOP_LAYER, "comm_trigger"); ret = cl_commlib_trigger(handle, synchron); if (ret != CL_RETVAL_OK) { sge_dstring_sprintf(err_msg, cl_get_error_text(ret)); DPRINTF(("cl_commlib_trigger() failed: %s (%d)\n", sge_dstring_get_string(err_msg), ret)); ret_val = COMM_CANT_TRIGGER; } if (ret_val == COMM_RETVAL_OK) { ret_val = comm_get_application_error(err_msg); } DEXIT; return ret_val; } /****** sge_ijs_comm/comm_write_message() ************************************* * NAME * comm_write_message() -- Write a message to the connection * * SYNOPSIS * unsigned long comm_write_message(COMM_HANDLE *handle, const char * *unresolved_hostname, const char *component_name, unsigned long component_id, * unsigned char *buffer, unsigned long size, unsigned char type, dstring * *err_msg) * * FUNCTION * Writes a message to the connection. * * INPUTS * COMM_HANDLE *handle - Handle of the connection. * const char *unresolved_hostname - Hostname of the destination host. * const char *component_name - Component name of the destination. * unsigned long component_id - Component ID of the destination. * unsigned char *buffer - The message data. * unsigned long size - Message data length. * unsigned char type - Message type. * dstring *err_msg - Gets the error reason in case of error. * * RESULT * unsigned long - the number of bytes written. * 0 in case of error. * * NOTES * MT-NOTE: comm_write_message() is not MT safe * * SEE ALSO * communication/comm_recv_message *******************************************************************************/ unsigned long comm_write_message(COMM_HANDLE *handle, const char *unresolved_hostname, const char *component_name, unsigned long component_id, unsigned char *buffer, unsigned long size, unsigned char type, dstring *err_msg) { int ret; cl_byte_t *sendbuf; unsigned long nwritten = 0; DENTER(TOP_LAYER, "comm_write_message"); /* * Copy only 'size' bytes from 'buffer' to a new sendbuf and add * one byte for the message type at the beginning of the sendbuf. * The commlib will free this buffer when it's content was sent. */ sendbuf = malloc(size+1); sendbuf[0] = type; memcpy(&sendbuf[1], buffer, size); ret = cl_commlib_send_message(handle, (char*)unresolved_hostname, (char*)component_name, component_id, CL_MIH_MAT_NAK, &sendbuf, size+1, NULL, 0, 0, CL_FALSE, /* don't copy the sendbuf */ CL_FALSE); /* don't wait for ack */ /* TODO: change send_message to ACK, i.e. CL_MIH_MAT_ACK && CL_TRUE in * the last line. This ensures that the connection is open * before the threads are created when this function is used by * parent_loop(). Better solution: Move this to a function * comm_write_first_message() or so. * * Problem seems to be: Both threads try to open a connection * to the qrsh client. This problem shoud be handled by the commlib. */ /* sendbuf was freed by the commlib */ sge_dstring_sprintf(err_msg, "%s", cl_get_error_text(ret)); if (ret == CL_RETVAL_OK) { nwritten = size; } else { sge_dstring_sprintf(err_msg, cl_get_error_text(ret)); DPRINTF(("cl_commlib_send_message() failed: %s (%d)\n", sge_dstring_get_string(err_msg), ret)); } DEXIT; return nwritten; } /****** sge_ijs_comm/comm_flush_write_messages() ****************************** * NAME * comm_flush_write_messages() -- Flush all messages still in the write list * of the communication library * * SYNOPSIS * int comm_flush_write_messages(COMM_HANDLE *handle, dstring *err_msg) * * FUNCTION * Flushes all messages still in the write list of the communication library. * comm_write_message() adds a message to the write list and tries to send * it immediately. This isn't always possible, so comm_flush_write_messages() * makes sure all messages are really written. * * INPUTS * COMM_HANDLE *handle - Handle of the connection. * dstring *err_msg - Contains error message in case of error. * * RESULT * int - 0: Ok, all messages were flushed. * <0: Retries needed to flush all messages * -1 * >0: An error occured, error number is a commlib error. * * NOTES * MT-NOTE: comm_flush_write_messages() is not MT safe * * SEE ALSO * communication/comm_write_message *******************************************************************************/ int comm_flush_write_messages(COMM_HANDLE *handle, dstring *err_msg) { unsigned long elems = 0; int ret = 0, retries = 0; elems = cl_com_messages_in_send_queue(handle); while (elems > 0) { /* * Don't set the cl_commlib_trigger()-call to be blocking and * get rid of the usleep() - it's much slower! * The last cl_commlib_trigger()-call will take 1 s. */ ret = cl_commlib_trigger(handle, 0); /* * Bail out if trigger fails with an error that indicates that we * won't be able to send the messages in the near future. */ if (ret != CL_RETVAL_OK && ret != CL_RETVAL_SELECT_TIMEOUT && ret != CL_RETVAL_SELECT_INTERRUPT) { sge_dstring_sprintf(err_msg, cl_get_error_text(ret)); retries = ret; break; } elems = cl_com_messages_in_send_queue(handle); /* * We just tried to send the messages and it wasn't possible to send * all messages - give the network some time to recover. */ /* TODO (NEW): make this working correctly by calling check_client_alive */ if (elems > 0) { usleep(10000); retries--; } } return retries; } /****** sge_ijs_comm/comm_recv_message() ************************************** * NAME * comm_recv_message() -- Receives a message from the connection * * SYNOPSIS * int comm_recv_message(COMM_HANDLE *handle, cl_bool_t b_synchron, * recv_message_t *recv_mess, dstring *err_msg) * * FUNCTION * Receives a message from the connection. * * INPUTS * COMM_HANDLE *handle - Handle of the connection. * cl_bool_t b_synchron - true: Wait until a complete message was read * false: Get what's available and return. * recv_message_t *recv_mess - The message gets filled into this struct. * The caller has to free buffers. * dstring *err_msg - Gets the error reason in case of error. * * RESULT * int - COMM_RETVAL_OK: * A message was received. * * COMM_GOT_TIMEOUT: * 'wait_seconds' have elapsed. * * COMM_CANT_TRIGGER: * err_msg contains the reason. * * COMM_CANT_SEARCH_ENDPOINT: * err_msg contains the reason. * * NOTES * MT-NOTE: comm_recv_message() is not MT safe * * SEE ALSO * communication/comm_send_message, communication/comm_free_message *******************************************************************************/ int comm_recv_message(COMM_HANDLE *handle, cl_bool_t b_synchron, recv_message_t *recv_mess, dstring *err_msg) { int ret_val = COMM_RETVAL_OK; int ret = 0; char sub_type[10]; cl_com_message_t *message = NULL; cl_com_endpoint_t *sender = NULL; DENTER(TOP_LAYER, "recv_message"); /* check validity of parameters */ if (handle == NULL || recv_mess == NULL) { if (handle == NULL) { sge_dstring_sprintf(err_msg, "Invalid parameter: handle == NULL"); } else { sge_dstring_sprintf(err_msg, "Invalid parameter: recv_mess == NULL"); } DPRINTF((sge_dstring_get_string(err_msg))); DEXIT; return COMM_INVALID_PARAMETER; } ret = cl_commlib_receive_message(handle, NULL, /* unresolved_hostname, */ NULL, /* component_name, */ 0, /* component_id, */ CL_FALSE, 0, &message, &sender); if (ret != CL_RETVAL_OK) { switch (ret) { case CL_RETVAL_NO_SELECT_DESCRIPTORS: sge_dstring_sprintf(err_msg, cl_get_error_text(ret)); DPRINTF(("cl_commlib_receive_message() failed: %s (%d)\n", sge_dstring_get_string(err_msg), ret)); ret_val = COMM_NO_SELECT_DESCRIPTORS; break; case CL_RETVAL_CONNECTION_NOT_FOUND: sge_dstring_sprintf(err_msg, cl_get_error_text(ret)); DPRINTF(("cl_commlib_receive_message() failed: %s (%d)\n", sge_dstring_get_string(err_msg), ret)); ret_val = COMM_CONNECTION_NOT_FOUND; break; case CL_RETVAL_SYNC_RECEIVE_TIMEOUT: sge_dstring_sprintf(err_msg, cl_get_error_text(ret)); DPRINTF(("cl_commlib_receive_message() failed: %s (%d)\n", sge_dstring_get_string(err_msg), ret)); ret_val = COMM_SYNC_RECEIVE_TIMEOUT; break; case CL_RETVAL_NO_MESSAGE: sge_dstring_sprintf(err_msg, cl_get_error_text(ret)); DPRINTF(("cl_commlib_receive_message() failed: %s (%d)\n", sge_dstring_get_string(err_msg), ret)); ret_val = COMM_NO_MESSAGE_AVAILABLE; break; default: sge_dstring_sprintf(err_msg, "can't receive message"); DPRINTF(("cl_commlib_receive_message() couldn't receive a message\n")); ret_val = COMM_CANT_RECEIVE_MESSAGE; } } if(sender != NULL) { cl_com_free_endpoint(&sender); } if (message != NULL) { recv_mess->cl_message = message; if (message->message_length>0) { char tmpbuf[100]; switch (message->message[0]) { case STDIN_DATA_MSG: case STDOUT_DATA_MSG: case STDERR_DATA_MSG: case REGISTER_CTRL_MSG: case UNREGISTER_CTRL_MSG: case UNREGISTER_RESPONSE_CTRL_MSG: case SETTINGS_CTRL_MSG: DPRINTF(("length of message: %d\n", (int)message->message_length)); /* data message */ recv_mess->type = message->message[0]; recv_mess->data = (char*)&(message->message[1]); DPRINTF(("recv_mess->type = %d\n", recv_mess->type)); memcpy(tmpbuf, recv_mess->data, MIN(99, message->message_length - 1)); tmpbuf[MIN(99, message->message_length - 1)] = 0; DPRINTF(("recv_mess->data = %s\n", tmpbuf)); break; case WINDOW_SIZE_CTRL_MSG: memcpy(tmpbuf, message->message, MIN(99, message->message_length)); tmpbuf[MIN(99, message->message_length)] = 0; /* control message */ recv_mess->type = tmpbuf[0]; /* scan subtype */ sscanf((char*)&(tmpbuf[1]), "%s", sub_type); if (strcmp(sub_type, "WS") == 0) { int row, col, xpixel, ypixel; sscanf((char*)&(tmpbuf[4]), "%d%d%d%d", &row, &col, &xpixel, &ypixel); recv_mess->ws.ws_row = row; recv_mess->ws.ws_col = col; recv_mess->ws.ws_xpixel = xpixel; recv_mess->ws.ws_ypixel = ypixel; } break; } } } else { cl_commlib_trigger(handle, b_synchron); } DEXIT; return ret_val; } /****** sge_ijs_comm/comm_free_message() ************************************** * NAME * comm_free_message() -- free contents of a received message struct * * SYNOPSIS * int comm_free_message(recv_message_t *recv_mess, dstring *err_msg) * * FUNCTION * Frees the content of a received message struct. * * INPUTS * recv_message_t *recv_mess - The message struct that is to be freed. * dstring *err_msg - Gets the error reason in case of error. * * RESULT * int - COMM_RETVAL_OK: * The message is freed. * * COMM_CANT_FREE_MESSAGE: * err_msg contains the error reason. * * NOTES * MT-NOTE: comm_free_message() is not MT safe * * SEE ALSO * communication/comm_recv_message() *******************************************************************************/ int comm_free_message(recv_message_t *recv_mess, dstring *err_msg) { int ret; int ret_val = COMM_RETVAL_OK; DENTER(TOP_LAYER, "comm_free_message"); if (recv_mess != NULL && recv_mess->cl_message != NULL) { ret = cl_com_free_message(&(recv_mess->cl_message)); if (ret != CL_RETVAL_OK) { sge_dstring_sprintf(err_msg, cl_get_error_text(ret)); DPRINTF(("cl_com_free_message() failed: %s (%d)\n", sge_dstring_get_string(err_msg), ret)); ret_val = COMM_CANT_FREE_MESSAGE; } } DEXIT; return ret_val; } /****** sge_ijs_comm/check_client_alive() ************************************* * NAME * check_client_alive() -- Checks is a know, connected client is still alive * * SYNOPSIS * int check_client_alive(COMM_HANDLE *handle, * const char *component_name, dstring *err_msg) * * FUNCTION * Checks if a known, connected client is still alive. * * INPUTS * COMM_HANDLE *handle - Handle to the connection. * const char *component_name - Name of the comonent to check. * char *hostname - Host of the client. * dstring *err_msg - Gets the error reason in case of error. * * RESULT * int - COMM_RETVAL_OK: * The client is alive. * * COMM_CANT_GET_CLIENT_STATUS: * err_msg contains the error reason. * * NOTES * MT-NOTE: check_client_alive() is not MT safe * *******************************************************************************/ int check_client_alive(COMM_HANDLE *handle, const char *component_name, char *hostname, dstring *err_msg) { int ret; int ret_val = COMM_RETVAL_OK; cl_com_SIRM_t *status = NULL; DENTER(TOP_LAYER, "check_client_alive"); DPRINTF(("handle->connect_port = %d\n", handle->connect_port)); DPRINTF(("handle->service_port = %d\n", handle->service_port)); DPRINTF(("client component name = %s\n", component_name)); DPRINTF(("hostname = %s\n", hostname)); ret = cl_commlib_get_endpoint_status(handle, hostname, (char*)component_name, 1, &status); if (ret != CL_RETVAL_OK) { sge_dstring_sprintf(err_msg, cl_get_error_text(ret)); DPRINTF(("cl_commlib_get_endpoint() failed: %s (%d)\n", sge_dstring_get_string(err_msg), ret)); ret_val = COMM_CANT_GET_CLIENT_STATUS; } cl_com_free_sirm_message(&status); DEXIT; return ret_val; }