From 947c78887e791596d4a5ec2d1079f8b1a049628b Mon Sep 17 00:00:00 2001 From: takeshi_hoshina Date: Tue, 27 Oct 2020 11:16:21 +0900 Subject: basesystem 0.1 --- otherservice/rpc_library/library/src/rpc_event.c | 158 +++ otherservice/rpc_library/library/src/rpc_lib.c | 1264 ++++++++++++++++++++ .../rpc_library/library/src/rpc_marshall.c | 740 ++++++++++++ otherservice/rpc_library/library/src/rpc_thread.c | 1224 +++++++++++++++++++ otherservice/rpc_library/library/src/rpc_udp.c | 473 ++++++++ 5 files changed, 3859 insertions(+) create mode 100644 otherservice/rpc_library/library/src/rpc_event.c create mode 100644 otherservice/rpc_library/library/src/rpc_lib.c create mode 100644 otherservice/rpc_library/library/src/rpc_marshall.c create mode 100644 otherservice/rpc_library/library/src/rpc_thread.c create mode 100644 otherservice/rpc_library/library/src/rpc_udp.c (limited to 'otherservice/rpc_library/library/src') diff --git a/otherservice/rpc_library/library/src/rpc_event.c b/otherservice/rpc_library/library/src/rpc_event.c new file mode 100644 index 00000000..038900c2 --- /dev/null +++ b/otherservice/rpc_library/library/src/rpc_event.c @@ -0,0 +1,158 @@ +/* + * @copyright Copyright (c) 2016-2019 TOYOTA MOTOR CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file rpc_event.c + * @brief RPC Library Internal Implementation--Event Handling + * + */ +#include +#include +#include +#include + +#include +#include "rpc_internal.h" + +/** @ingroup RPClib_in + */ +RUNS_IN_READING_THREAD +RPC_Result +RpcQueueAPIRequestBefore(RpcIdInfo *id, UINT32 size, char **buff) { + RPC_THREAD_MUTEX_LOCK(id->thread_info); + + int n = RPC_apicall_num_queue(id); // LCOV_EXCL_BR_LINE 15: marco defined in rpc_thread.h + + if (n >= RPC_MAX_APICALL_QUEUE) { + RPC_THREAD_MUTEX_UNLOCK(id->thread_info); + RPC_LOG_STATE("Returned BUSY."); + return RPC_ERR_Busy; + } else { + *buff = rpc_malloc(size);/* malloc */ + if (*buff == NULL) { // LCOV_EXCL_BR_LINE 5: fail safe for libc function malloc + RPC_THREAD_MUTEX_UNLOCK(id->thread_info); // LCOV_EXCL_START 5: fail safe for libc function malloc + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + RPC_LOG_STATE("queue API request: No Memory"); + return RPC_ERR_Fatal; + } // LCOV_EXCL_STOP + } + return RPC_OK; +} +/** @ingroup RPClib_in + */ +RUNS_IN_READING_THREAD +RPC_Result +RpcQueueAPIRequestAfter(RpcIdInfo *id, RPC_ID client, + const char *mesg, UINT32 size, char *args) { + int n = RPC_apicall_num_queue(id); // LCOV_EXCL_BR_LINE 15: marco defined in rpc_thread.h + + UINT16 api_num; + api_num = (UINT16)strtol(mesg, NULL, 10); + memcpy(args, mesg + RPC_APICALL_FORMAT_ARGS_START, size); + RPC_ID_COPY(RPC_apicall_queue_client(id, n), client); + RPC_apicall_queue_api_num(id, n) = api_num; + RPC_apicall_queue_args(id, n) = args; + RPC_apicall_queue_args_size(id, n) = size; + RPC_apicall_num_queue_inc(id); + + /* set RUN state */ + CL_MonitorSetEntry(CL_MONITOR_TYPE_RPC, + RPC_port(id), + CL_MONITOR_STATE_RUN, + (uint32_t)(RPC_apicall_api_timeout_sec(id)), + api_num); + + RPC_THREAD_MUTEX_UNLOCK(id->thread_info); + return RPC_OK; +} + +/** @ingroup RPClib_in + */ +RUNS_IN_CALLERS_THREAD +void +RpcFreeAPIArgsString(char *args_string) { + if (args_string != NULL) { // LCOV_EXCL_BR_LINE 5: free the memory, malloced in RpcQueueAPIRequestBefore(...) + rpc_free(args_string);/* free */ + } +} + +/** @ingroup RPClib_in + */ +RUNS_IN_CALLERS_THREAD +UINT16 +RpcGetAPIRequest(RpcIdInfo *id, RPC_ID_p client, + char **args_string, unsigned int *args_size) { + UINT16 api_num = 0; + + RPC_THREAD_MUTEX_LOCK(id->thread_info); + + UINT32 n = RPC_apicall_num_queue(id); // LCOV_EXCL_BR_LINE 15: marco defined in rpc_thread.h + if (n > 0) { + RPC_ID_COPY(*client, RPC_apicall_queue_client(id, 0)); + api_num = RPC_apicall_queue_api_num(id, 0); + *args_string = RPC_apicall_queue_args(id, 0); + /* this string must be freed by the caller using discard_APIcall_return()*/ + *args_size = RPC_apicall_queue_args_size(id, 0); + + if (n > 1) { + memmove(&(RPC_apicall_queue(id, 0)), &(RPC_apicall_queue(id, 1)), + (n - 1) * sizeof(RPC_apicall_queue(id, 0))); + } + RPC_apicall_num_queue_dec(id); + } + + RPC_THREAD_MUTEX_UNLOCK(id->thread_info); + return api_num; +} + +/** @ingroup RPClib_in + */ +RUNS_IN_READING_THREAD +RPC_Result +RpcSetAPIcallReturn(RpcIdInfo *id, const char *mesg, UINT32 size) { + if (RPC_apicall_return_str(id) != NULL) { // LCOV_EXCL_BR_LINE 6: double check + RPC_LOG_STATE("previous APIcall return string was not used"); // LCOV_EXCL_START 6: double check + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + RpcDiscardAPIcallReturn(id); + } // LCOV_EXCL_STOP + RPC_THREAD_MUTEX_LOCK(id->thread_info); + + RPC_apicall_return_str(id) = rpc_malloc(size);/* malloc */ + if (RPC_apicall_return_str(id) == NULL) { // LCOV_EXCL_BR_LINE 5: fail safe for libc function malloc + RPC_THREAD_MUTEX_UNLOCK(id->thread_info); // LCOV_EXCL_START 5: fail safe for libc function malloc + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + RPC_LOG_STATE("set APIcall return: No Memory"); + return RPC_ERR_Fatal; + } // LCOV_EXCL_STOP + RPC_apicall_return_str_len(id) = size; + memcpy(RPC_apicall_return_str(id), mesg, size); + RPC_THREAD_MUTEX_UNLOCK(id->thread_info); + return RPC_OK; +} + +/** @ingroup RPClib_in + */ +RUNS_IN_CALLERS_THREAD +void +RpcDiscardAPIcallReturn(RpcIdInfo *id) { + RPC_THREAD_MUTEX_LOCK(id->thread_info); + if (RPC_apicall_return_str(id) != NULL) { // LCOV_EXCL_BR_LINE 6: double check + rpc_free(RPC_apicall_return_str(id));/* free */ + RPC_apicall_return_str_len(id) = 0; + RPC_apicall_return_str(id) = NULL; + } + RPC_THREAD_MUTEX_UNLOCK(id->thread_info); +} diff --git a/otherservice/rpc_library/library/src/rpc_lib.c b/otherservice/rpc_library/library/src/rpc_lib.c new file mode 100644 index 00000000..b77775fd --- /dev/null +++ b/otherservice/rpc_library/library/src/rpc_lib.c @@ -0,0 +1,1264 @@ +/* + * @copyright Copyright (c) 2016-2019 TOYOTA MOTOR CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file rpc_lib.c + * @brief RPC Library-API Function Implementation + * + */ +/** @mainpage + * This document describes the following API functions, structures, + * type declarations, and macros. + * - RPC library external specification + * - External specifications for RPC tools + * + * For an overview of other RPC libraries and tools and anomaly processing, + * to reference the Word version of the documentation. + * (external specifications and programming guides) */ +/** @defgroup RPClib RPC library external specification */ +/** @defgroup RPCtool RPC tool (rpc_apidef) */ +/** @defgroup RPC_ID_assign RPC_ID Assignment */ +/** @defgroup RPClib_in RPC library internal specifications (incomplete) */ +/** @addtogroup RPClib + * @{ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "rpc_internal.h" + +#include // for CL_INTFY_FILENAME_FORMAT +#include + + +/* Prototype declarations */ +static RPC_Result ProcessAPIcallRequest(RpcIdInfo *idinfo, RPC_ID client, + UINT16 api_num, char *args_string, + unsigned int args_size); +static RPC_Result RpcAllDeleteSrvrPid(RpcIdInfo *idinfo); +static RPC_Result RpcClientWaitCertification(RpcIdInfo *idinfo, pid_t *srvr_pid); +static RPC_Result RpcRegistSrvrPid(RpcIdInfo *idinfo, RPC_ID srvr_rpc_id, pid_t srvr_pid, int wd); + +#define THREAD_INFO_CHECK(th) \ + if ((th) == NULL) { \ + RPC_LOG_STATE("No thread info"); \ + return RPC_ERR_Fatal; \ + } + +#define SUB_THREAD_CHECK() \ + if (g_rpc_thread_alive == 0) { \ + RPC_LOG_STATE("No sub thread -- shutdown"); \ + RpcDestroyThreadInfo(); \ + return RPC_ERR_Fatal; \ + } + +/** + * Waiting for response packet for API call and API result transmission + * Timeout time in msec. + * If the other program is abnormal (process termination), delay occurs in the meanwhile. + * Confirm the operation on the actual machine and set again. + */ +#define RESPONSE_WAIT_MSEC_DEFAULT 2000 +static unsigned int Response_wait_msec = RESPONSE_WAIT_MSEC_DEFAULT; + +char *RPC_DEBUG = NULL; + +#if defined(RPC_USE_UNIX_AUTOBIND) + + static RPC_Result +CreateClientIdinfo(RpcThreadInfo *th, RPC_ID *pID) { + if (RpcCreateIdInfo(th, *pID, NULL, NO_SECURE_CHECK) < 0) { + RPC_LOG_ERR("Can't create id_info"); + return RPC_ERR_Fatal; + } + + *pID = RPC_my_id(RPC_clnt_idinfo(th)); + RPC_LOG_DEBUG("RPC: assigned client ID %d", *pID); + if (RPC_DEBUG) { // LCOV_EXCL_BR_LINE 7: debug + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + printf("RPC[%s]: client start finished.\n", RPC_DEBUG); // LCOV_EXCL_LINE 7: debug + } + return RPC_OK; +} +#else /* !AUTOBIND */ + +#include + + static int +FindUniqClientId(void) { +#define RPC_ID_FILE_NAME "/tmp/RPC/id" +#define RPC_MIN_CLIENT_ID 1024 +#define RPC_MAX_CLIENT_ID (1024+32768-1) + + int fd, found; + RPC_ID id, next; + struct stat statbuf; + char fname[32]; + + fd = open(RPC_ID_FILE_NAME, O_RDWR|O_CLOEXEC, 0777); + if (fd < 0) { + id = RPC_MIN_CLIENT_ID; + fd = open(RPC_ID_FILE_NAME, O_RDWR|O_CREAT|O_CLOEXEC, 0777); + if (fd < 0) { + return -1; + } + flock(fd, LOCK_EX); // Possible sleep here + } else { + flock(fd, LOCK_EX); // Possible sleep here + read(fd, &id, sizeof(id)); + } + + found = 0; + while(found == 0) { + rpc_set_socket_name(fname, id); + if (stat(fname, &statbuf) < 0) { + found = 1; + break; + } + id++; + if (id > RPC_MAX_CLIENT_ID) { + id = RPC_MIN_CLIENT_ID; + } + } + + next = id + 1; + if (next > RPC_MAX_CLIENT_ID) { + next = RPC_MIN_CLIENT_ID; + } + + lseek(fd, 0, SEEK_SET); + write(fd, &next, sizeof(next)); + flock(fd, LOCK_UN); + close(fd); + + return id; +} + + static RPC_Result +CreateClientIdinfo(RpcThreadInfo *th, UINT16 port) { + if (RpcCreateIdInfo(th, port, NULL, NO_SECURE_CHECK) < 0) { + RPC_LOG_ERR("Can't create id_info"); + return RPC_ERR_Fatal; + } + // LCOV_EXCL_BR_START 7: debug + if (RPC_DEBUG) { + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + printf("RPC[%s]: client start finished.\n", RPC_DEBUG); + } + // LCOV_EXCL_BR_STOP + return RPC_OK; +} +#endif /* AUTOBIND */ + +/** @fn RPC_START_SERVER(ID) + * @brief RPC_start() macro for the server + * + * Used by RPC-API server programs to activate the RPC library. + * ID specifies a symbol of the form XXX_RPC_ID defined in rpc_id.h. + * Only one server program per thread is allowed. + * When a server program calls an API as a client of another server, + * it only needs to call RPC_START_SERVER, + * not RPC_START_CLIENT. + * + * RPC_end() should be called when the RPC library is no longer needed. + * @param ID [IN]RPC_ID of the program + * @see RPC_start + * @see RPC_end + */ +/** @fn RPC_START_CLIENT(pID) + * @brief RPC_start() macro for clients + * + * Use only the client functions of the RPC library + * (only calling APIs provided by other programs), + * This macro is used for the start process. + * + * + * RPC_end() should be called when the RPC library is no longer needed. + * + * @param pID [OUT]Pointer to the RPC_ID of the program + * @see RPC_start + * @see RPC_end + */ +/** @brief Start processing of the RPC library + * + * - With an open socket, RPC_ID, and API call dispatching functions + * Register in the global variable (managed separately for each thread and RPC_ID) + * - Starting use as a server + * - func specifies a function pointer (defined in stub) that is not a NULL. + * - Specifies a fixed ID defined in rpc_id.h as the ID. + * - If the same thread has already started using the server, an error code is returned. + * - When the server function is started for the first time in a process, + * one subthread is started to process received packets + * in the background. + * - Starting use as a client + * - NULL is specified in the func. + * - In principle, an invalid ID value (RPC_NO_PORT) is specified. + * This function automatically assigns IDs and returns the assignment result to pID. + * - However, if another program has already started using it as a client, + * it shares the socket/RPC_ID instead of creating a new socket. + * - Exceptionally, it is allowed to specify a fixed ID defined in rpc_id.h as the ID. + * This is the only way to use RPC_start() in programs + * where it is difficult to always call RPC_end() at the end of use by program structure/design. + * - In this case, the pID argument is not referenced. + * - The maximum number of threads that use the RPC library in a process is + * specified by RPC_MAX_THREADS_IN_PROCESS. + * + * @param id [IN]RPC_ID of the program (in the case of RPC_NO_PORT, this means automatic assignment) + * @param dispatch_func Dispatching functions (defined in stub files for servers, + * and NULL specified in programs that do not provide APIs) + * @param pID [OUT]RPC_ID of the automatically assigned program + * + * @retval RPC_OK: normal completion + * @retval Otherwise: Abnormal termination (FATAL) + * + * @note Normally, this function is not called directly, but the following macro is used. + * - RPC_START_SERVER(ID) + * For programs that provide RPC server functions. + * This macro is also used when both server and client functions are used. + * - RPC_START_CLIENT(pID) + * For programs that use only the RPC client function. + */ + RPC_Result +RPC_start(RPC_ID id, RPC_dispatch_func_t func, RPC_ID *pID, INT32 secure_check) +{ +#if defined(RPC_USE_SYSLOG) + static int init_syslog = 0; + + if (init_syslog == 0) { + openlog("librpc", LOG_PID, LOG_USER); + setlogmask(LOG_UPTO(LOG_INFO)); + /* setlogmask(LOG_MASK(LOG_DEBUG)); */ + init_syslog = 1; + if (func != NULL) + RPC_LOG_STATE("RPC_START_SERVER(%#x)\n", id); + } +#endif /* RPC_USE_SYSLOG */ + + if (func != NULL && id == RPC_NO_ID) { /* No ID is specified despite as a server */ + RPC_LOG_ERR("Invalid RPC_start server arg"); + return RPC_ERR_Fatal; + } + if (func != NULL && pID != NULL) { /* Requested obtaining pID despite as a server */ // LCOV_EXCL_BR_LINE 200: macro guarantee impossible both not NULL, RPC_START_*** + RPC_LOG_ERR("Invalid RPC_start server arg"); // LCOV_EXCL_START 200: macro guarantee impossible both not NULL, RPC_START_*** + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + return RPC_ERR_Fatal; + } // LCOV_EXCL_STOP + if (func == NULL && id != RPC_NO_ID) { /* Specified ID despite as a client */ + RPC_LOG_ERR("Invalid RPC_start server arg"); + return RPC_ERR_Fatal; + } +#if defined(RPC_USE_UNIX_AUTOBIND) + if (func == NULL && pID == NULL) { + /* pID is NULL despite as a client (not supported for ID-specified clients) */ + RPC_LOG_ERR_W_NAME("RPC_Start(ID, NULL, NULL) is no longer supported, ID is auto-assigned"); + return RPC_ERR_Fatal; + } +#else /* !AUTOBIND */ + if (func == NULL && id == RPC_NO_ID && pID == NULL) { + /* pID is NULL despite as an auto-assigned client */ + RPC_LOG_ERR("Invalid RPC_start arg"); + return RPC_ERR_Fatal; + } +#endif /* !AUTOBIND */ + if ((secure_check != NO_SECURE_CHECK) && (secure_check != NEED_SECURE_CHECK)) { // LCOV_EXCL_BR_LINE 200: macro guarantee only two value, RPC_START_*** + /* Check Arguments for Client Authentication */ + RPC_LOG_ERR("Invalid RPC_start arg"); // LCOV_EXCL_START 200: macro guarantee only two value, RPC_START_*** + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + return RPC_ERR_Fatal; + } // LCOV_EXCL_STOP + + RpcThreadInfo *th = RpcMyThreadInfo(); + if (th == NULL) { + th = RpcCreateThreadInfo(); + if (th == NULL) { // LCOV_EXCL_BR_LINE 5: fail safe for libc malloc + return RPC_ERR_Fatal; + } + } + + /* RPC_start is already done in the same thread */ + if (func != NULL) {/* Server start */ + if (RPC_srvr_idinfo(th) != NULL) {/* Server already started */ + RPC_LOG_ERR("duplicated RPC_START_SERVER"); + return RPC_ERR_Fatal; + } + } else { /* Client start */ + if (RPC_clnt_idinfo(th) != NULL) {/* Client already started */ + if (pID != NULL) { // LCOV_EXCL_BR_LINE 6: double check + *pID = (RPC_clnt_idinfo(th))->port; + RPC_LOG_DEBUG("use existing client ID=%d", *pID); + RPC_inc_clnt_count(RPC_clnt_idinfo(th)); + } + /* + * When pID == NULL (RPC_ID fixed), there is no guarantee + * that RPC_end() will be called as many times as needed, so it does not count up. + */ + return RPC_OK; + } + } + + if (func == NULL) {/* Client start */ +#if defined(RPC_USE_UNIX_AUTOBIND) + /* Automatically assign everything without supporting ID specification */ + RPC_ID id_local = RPC_NO_ID; + RPC_Result ret = CreateClientIdinfo(th, &id_local); + if(RPC_OK != ret) return ret; + + if (pID) { // LCOV_EXCL_BR_LINE 6: double check + *pID = id_local; + } + RPC_clnt_inotify_fd(RPC_clnt_idinfo(th)) = inotify_init1(IN_CLOEXEC); + return ret; +#else /* !AUTOBIND */ + if (id == RPC_NO_ID) { + /* No port number specified -> Automatically assign an ID and port number here */ + /* + * FindUniqClientId() returns ID within the range of RPC_MIN_CLIENT_ID(1024) to RPC_MAX_CLIENT_ID(1024+32768-1) + */ + int ret; + ret = FindUniqClientId(); + if (ret < 0) { + return RPC_ERR_Fatal; + } + /* pID != NULL is already checked */ + *pID = (UINT16)ret; + return CreateClientIdinfo(th, *pID); + } else { + /* + * Client start with specified port number + * Programs that cannot guarantee an RPC_end() call + * at termination specify a fixed RPC_ID from the upper level and use it. + * (To avoid memory shortage caused by building up socket-files in /tmp/RPC) + */ + return CreateClientIdinfo(th, id); + } +#endif /* !AUTOBIND */ + } + + /* Dispatch func specified (servers) -> bind */ + CL_MonitorInit(CL_MONITOR_INIT_USER); /* Using the API for Error Monitoring */ + + if (RpcCreateIdInfo(th, id, func, secure_check) < 0) { + RPC_LOG_ERR("Can't create id_info"); + return RPC_ERR_Fatal; + } + + if (RPC_DEBUG) { // LCOV_EXCL_BR_LINE 7: debug + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + printf("RPC[%s]: server thread invoked\n", RPC_DEBUG); // LCOV_EXCL_LINE 7: debug + } + char *env; + if ((env = getenv("RPC_RESPONSE_WAIT")) != NULL) { + Response_wait_msec = (unsigned int)strtol(env, NULL, 10); + } + + return RPC_OK; +} + + static void +RpcEndSub(RpcThreadInfo *th, RpcIdInfo *idinfo) { + int sockfd; +#if !defined(RPC_USE_UNIX_AUTOBIND) + char path[120]; + + rpc_set_socket_name(path, RPC_my_id(idinfo)); +#endif /* !AUTOBIND */ + sockfd = idinfo->sock; + RpcDestroyIdInfo(th, idinfo); + close(sockfd); +#if !defined(RPC_USE_UNIX_AUTOBIND) + unlink(path); +#endif /* !AUTOBIND */ +} + +/** @brief RPC Library End-of-Use Processing + * + * - Perform termination processing such as closing the opened socket, releasing the memory. + * (Server/Client Common) + * - If there are no programs in the thread that use RPC_ID, + * the subthread started by RPC_start is terminated. + * - Each RPC_ID must be called before the program exits. + * - RPC_end must be called by the thread that performed the RPC_start. + * - Ignored unused or no longer used RPC_ID if specified. + * + * @param ID [IN]RPC_ID of the program + */ + void +RPC_end(RPC_ID id) { + RpcThreadInfo *th = RpcMyThreadInfo(); + if (th == NULL) { + return; + } + RpcIdInfo *idinfo; + idinfo = RPC_srvr_idinfo(th); + if (idinfo != NULL && RPC_my_id(idinfo) == id) {/* srvr */ + RpcEndSub(th, idinfo); + } + + idinfo = RPC_clnt_idinfo(th); + if (idinfo != NULL && RPC_my_id(idinfo) == id) {/* client */ + + RPC_dec_clnt_count(idinfo); + if (RPC_clnt_count(idinfo) == 0) { + /* Deleting management table of the communication destination server PID */ + RpcAllDeleteSrvrPid(idinfo); + close(RPC_clnt_inotify_fd(idinfo)); + + RpcEndSub(th, idinfo); + } + } + if (RPC_srvr_idinfo(th) == NULL && RPC_clnt_idinfo(th) == NULL) { + RPC_LOG_DEBUG("destroy thread info %p", th); + RpcDestroyThreadInfo(); + } +} + +/** @brief RPC library use termination processing (forced cleanup at process termination) + * + * Deletes the socket file created for RPC communication in the process. + * For to release the resources of programs that use the RPC library with RPC_end() in principle, + * but this API is an insurance process to eliminate socket file leaks. + * + * - Since it is assumed that the socket is called immediately before the process terminates, + * closing sockets and releasing memory is not performed automatically when the process terminates. + * - All RPC processing (both server and client) after this API call cannot be used. + * Operation is not guaranteed even if RPC_START is performed again after that. + * + * No action for versions of the RPC library that use Unix autobind. + */ + void +RPC_end_all(void) { +#if !defined(RPC_USE_UNIX_AUTOBIND) + RpcUnlinkSocketFiles(); +#endif +} + +/** + * @fn RPC_Result RPC_set_API_timeout(RPC_ID id, UINT32 sec) + * @brief Setting the timeout period during server API processing + * + * @param sec [IN] Timeout (sec) Set within the range of 1 to 60 sec. + * + * @retval RPC_OK: normal completion + * @retval RPC_ERR_Configuration: Argument out of range + * @retval RPC_ERR_Fatal: Fatal error + */ + RPC_Result +RPC_set_API_timeout(INT32 sec) { + /* param check */ + if ((sec > 60) || (sec < 1)) { + RPC_LOG_ERR( "RPC_set_API_timeout() : Invalid Param." ); + return RPC_ERR_Configuration; + } + + /* thread_info check */ + RpcThreadInfo *th = RpcMyThreadInfo(); + THREAD_INFO_CHECK(th); + + /* thread_info->id_info check */ + if (RPC_srvr_idinfo(th) != NULL ) { // LCOV_EXCL_BR_LINE 200: server start abnormal can't test + RpcIdInfo *idinfo = RPC_srvr_idinfo(th); + + /* thread_info->id_info->apicall_info check */ + if( RPC_apicall_info(idinfo) != NULL ) { // LCOV_EXCL_BR_LINE 200: server start abnormal can't test + RPC_apicall_api_timeout_sec(idinfo) = sec; + } else { // LCOV_EXCL_START 200: server start abnormal can't test + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + RPC_LOG_ERR( "No apicall_info" ); + return RPC_ERR_Fatal; + } + + } else { + RPC_LOG_STATE("No srvr_id info"); + return RPC_ERR_Fatal; + } // LCOV_EXCL_STOP + return RPC_OK; +} + + +/** @brief API call execution function (client side) + * + * (These functions are called from within a client-facing STUB. Users never use these directly.) + */ + RPC_Result +RPC_API_call(RPC_ID server, UINT16 api_num, + const char *args_string, unsigned int args_size, + char **ret_string, unsigned int *ret_size) { + *ret_string = NULL; + + RpcThreadInfo *th = RpcMyThreadInfo(); + + THREAD_INFO_CHECK(th); + + RpcIdInfo *idinfo; + idinfo = RPC_clnt_idinfo(th); + + pid_t srvr_pid; + + if (idinfo == NULL) { // LCOV_EXCL_BR_LINE 8: dead code, idinfo always isn't NULL + /* + * In the past, the remedy of uninitialized clients has been done here. + * However, because it becomes to accumulate garbage in the /tmp/RPC by changing UNIX sockets, + * as an error without remedies. + */ + // LCOV_EXCL_START 8: dead code, idinfo always isn't NULL + RPC_LOG_ERR_W_NAME("warning!!! RPC call without RPC_start(): server=%d, api_num=%d", server, api_num); + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert +#if defined(RPC_USE_UNIX_AUTOBIND) + RPC_ID id = RPC_NO_ID; + RPC_Result ret = CreateClientIdinfo(th, &id); +#else /* !AUTOBIND */ + int id; + id = FindUniqClientId(); + if (id < 0) { + return RPC_ERR_Fatal; + } + RPC_Result ret = CreateClientIdinfo(th, (UINT16)id); +#endif /* !AUTOBIND */ + if (ret != RPC_OK) { + return ret; + } + idinfo = RPC_clnt_idinfo(th); + if (idinfo == NULL) { + return RPC_ERR_Fatal; + } + } // LCOV_EXCL_STOP + + char api_head[RPC_APICALL_FORMAT_ARGS_START + 1]; + sprintf(api_head, RPC_APICALL_FORMAT, api_num); + + rpc_send_buf sendbuf[2]; + sendbuf[0].buf = api_head; + sendbuf[0].bytes = RPC_APICALL_FORMAT_ARGS_START; + if (args_size > 0) { + sendbuf[1].buf = args_string; + sendbuf[1].bytes = args_size; + } + + int seq_num; + +send_api_call: + seq_num = RpcSendUdp2(idinfo, server, + (api_num == RPC_API_NUM_RPC_ALIVE + ? RPC_SEND_TO_SERVER_NO_RETRY + : RPC_SEND_TO_SERVER), + RPC_PACKET_APICALL, + (args_size > 0) ? 2 : 1, sendbuf); + if (seq_num < 0) { + return RPC_ERR_No_Response; + } + + // wait for response + UINT16 response; + RPC_Result result = RpcClientWaitResponse(idinfo, (UINT32)seq_num, Response_wait_msec, + &response); + if (result != RPC_OK) { + return result; + } + + if (response == RPC_RESPONSE_API_BUSY) { /* Server is busy */ + return RPC_ERR_Busy; + } else if (response == RPC_RESPONSE_API_ERR) { /* API call error */ + RPC_LOG_STATE("APIcall error (can't queue)"); + return RPC_ERR_Fatal; + } else if (response == RPC_RESPONSE_API_DEADLOCK) { /* Server is in deadlock */ + RPC_LOG_STATE("server DeadLock."); + return RPC_ERR_Server_DeadLock; + } else if (response == RPC_RESPONSE_API_CERTIFY) { /* Authentication request */ + struct sockaddr_un srvr_sa_un, secure_sa_un; + socklen_t srvr_sa_len, secure_sa_len; + int secure_sock_un; + + /* Create client socket for authentication */ + memset(&secure_sa_un, 0, sizeof(secure_sa_un)); + secure_sa_un.sun_family = AF_UNIX; + secure_sock_un = socket(PF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); + RpcSetClientSecureName(secure_sa_un.sun_path, RPC_port(idinfo)); + secure_sa_len = (socklen_t)(sizeof(secure_sa_un.sun_family) + SECURE_SOCK_NAME_LEN); + if (bind(secure_sock_un, (struct sockaddr *)&secure_sa_un, secure_sa_len) < 0) { + RPC_LOG_PERROR("STREAM : bind(unix), ID:%#x", RPC_port(idinfo)); + } + idinfo->secure_sock = secure_sock_un; + + /* Destination Server Socket Setting */ + memset(&srvr_sa_un, 0, sizeof(srvr_sa_un)); + srvr_sa_un.sun_family = AF_UNIX; + RpcSetServerSecureName(srvr_sa_un.sun_path, server); + srvr_sa_len = (socklen_t)(sizeof(srvr_sa_un.sun_family) + SECURE_SOCK_NAME_LEN); + + /* Connection */ + int cr = connect(RPC_my_secure_sock(idinfo), (struct sockaddr *)&srvr_sa_un, srvr_sa_len); + if (0 > cr) { + RPC_LOG_STATE("client connect() is failed."); + return RPC_ERR_Fatal; + } + + /* Waiting for server authentication */ + RPC_Result ret_certify = RpcClientWaitCertification(idinfo, &srvr_pid); + close(RPC_my_secure_sock(idinfo)); + if (RPC_OK != ret_certify) {/* Authentication NG */ + RPC_LOG_STATE("denied the connection."); + return ret_certify; + } + + /* inotify monitoring of server processes */ + char intfy_fname[32]; + snprintf(intfy_fname, sizeof(intfy_fname), CL_INTFY_FILENAME_FORMAT, srvr_pid); + int wd = inotify_add_watch(RPC_clnt_inotify_fd(idinfo), intfy_fname, IN_DELETE_SELF); + if (0 > wd) { + RPC_LOG_STATE("intfy_fname is Not Found [%s].", intfy_fname); + } + + /* Register the communication destination server PID in the management table. */ + if (RPC_OK != RpcRegistSrvrPid(idinfo, server, srvr_pid, wd)) { + return RPC_ERR_Fatal; + } + goto send_api_call; /* Sending the API request again */ + + } else if (response == RPC_RESPONSE_NONE) { // LCOV_EXCL_BR_LINE 8: dead code, not implement + // Not implemented to cancel processing + // LCOV_EXCL_START 8: dead code, not implement + RPC_LOG_STATE("No response for API call"); + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + return RPC_ERR_No_Response; + } // LCOV_EXCL_STOP + + // When the server ALIVE is queried, no result is returned, so return here + if (api_num == RPC_API_NUM_RPC_ALIVE) { + return RPC_OK; + } + + // API call is accepted at server + // now wait for the return packet + result = RpcClientWaitResult(idinfo, server); + if (result != RPC_OK) { + RpcDiscardAPIcallReturn(idinfo); + return result; + } + + /****** Function error and return value must be handled separately *******/ + UINT8 *return_str = RPC_apicall_return_str(idinfo); /* pgr0689 */ +#ifdef APICALL_RETURN_TIMEOUT + if (return_str == NULL) { + RPC_LOG_STATE("APIcall return timeout"); + return RPC_ERR_No_Response; + } +#else + rpc_assert(return_str != NULL); // LCOV_EXCL_BR_LINE 6: double check +#endif + + RPC_THREAD_MUTEX_LOCK(idinfo->thread_info); + + *ret_string = rpc_malloc(RPC_apicall_return_str_len(idinfo)); + if (*ret_string != NULL) { // LCOV_EXCL_BR_LINE 5: fail safe for libc malloc + if (ret_size) { + *ret_size = RPC_apicall_return_str_len(idinfo); + } + memcpy(*ret_string, RPC_apicall_return_str(idinfo), + RPC_apicall_return_str_len(idinfo)); + } else { + RPC_LOG_STATE("APIcall: No Memory"); // LCOV_EXCL_START 5: fail safe for libc malloc + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + result = RPC_ERR_Fatal; + } // LCOV_EXCL_STOP + RPC_THREAD_MUTEX_UNLOCK(idinfo->thread_info); + RpcDiscardAPIcallReturn(idinfo); + + return result; +} + +/** @brief Releasing allocated memory while API call is processing + * + * (These functions are called from within a client-facing STUB. Users never use it directly.) + */ + void +RPC_free_return_string(void *ptr) { + if (ptr != NULL) { + rpc_free(ptr); + } +} + +/** @brief Retrieving file descriptors for determining whether RPC-API calls are requested + * + * - This function returns a file descriptor used to determine whether or not there is a request to the server. + * - For the FD returned by this function, Don't use the other way than the FD specified + * in the poll or select parameter. + * - Intended use + * - Obtain FD using this function. + * - Wait for data to arrive by poll or select. + * - Call RPC_process_API_request() when data arrived. + * - This procedure performs handling request of the API call from the client. + * (Server-side API function is executed.) + * + * @param id [IN]RPC_ID of the server program. + * @param fd [OUT]Storage of file descriptor + * + * @retval RPC_OK: normal completion + * @retval Otherwise: Abnormal termination (FATAL) + * @see RPC_process_API_request + */ + RPC_Result +RPC_get_fd(RPC_ID id, int *fd) { + if ((id == RPC_NO_ID) || (fd == NULL)) { + return RPC_ERR_Fatal; + } + rpc_assert(fd != NULL); // LCOV_EXCL_BR_LINE 6: double check + + RpcThreadInfo *th = RpcMyThreadInfo(); + + THREAD_INFO_CHECK(th); + + SUB_THREAD_CHECK(); // LCOV_EXCL_BR_LINE 15: macro define in rpc_lib.c + + RpcIdInfo *idinfo = RPC_srvr_idinfo(th); + + rpc_assert(idinfo != NULL && RPC_my_id(idinfo) == id); // LCOV_EXCL_BR_LINE 6: double check + + *fd = RPC_pipe_sub_main(th)[PIPE_READ]; + + return RPC_OK; +} + +/** API Call Request Handling +*/ + static RPC_Result +ProcessAPIcallRequest(RpcIdInfo *idinfo, RPC_ID client, UINT16 api_num, + char *args_string, unsigned int args_size) { + /* ---------------------------- + * call API dispatch function + * ---------------------------- + */ + + /* API Running Client RPC_ID Setting */ + RPC_apicall_in_process_client(idinfo) = client; + + char *ret_string = NULL; + unsigned int ret_size = 0; + RPC_dispatch_func_t dispatch = RPC_apicall_dispatch_func(idinfo); + rpc_assert(dispatch != NULL); // LCOV_EXCL_BR_LINE 6: double check + RPC_Result api_ret; + api_ret = dispatch(api_num, args_string, args_size, &ret_string, &ret_size); /* pgr0060 */ + + unsigned char retcode[RPC_RETCODE_LEN+1]; + sprintf((char *)retcode, "%08x ", api_ret); + + rpc_send_buf sendbuf[2]; + sendbuf[0].buf = retcode; + sendbuf[0].bytes = sizeof(retcode) - 1; + + int seq_num; + + if (ret_string != NULL) { + sendbuf[1].buf = ret_string; + sendbuf[1].bytes = ret_size; + seq_num = RpcSendUdp2(idinfo, client, RPC_SEND_TO_CLIENT, + RPC_PACKET_APIRETURN, 2, sendbuf); + } else { + seq_num = RpcSendUdp2(idinfo, client, RPC_SEND_TO_CLIENT, + RPC_PACKET_APIRETURN, 1, sendbuf); + } + RPC_marshall_free(1, ret_string); + + if (seq_num < 0) { + RPC_LOG_ERR("send result failed!"); + return RPC_ERR_Fatal; + } + + /* Set API Running Client RPC_ID to None */ + RPC_apicall_in_process_client(idinfo) = RPC_NO_PORT; + + /* Sleep API-processing entries */ + RPC_THREAD_MUTEX_LOCK(idinfo->thread_info); + CL_MonitorSetEntry(CL_MONITOR_TYPE_RPC, RPC_port(idinfo), + CL_MONITOR_STATE_SLEEP, 0, 0); + RPC_THREAD_MUTEX_UNLOCK(idinfo->thread_info); + + return api_ret; +} + +/** @brief Executing RPC-API calls + * + * Process all queued API execution requests from clients. + * The API function of the server program is called from here. + * If there is no API execution request, the system returns normally immediately without waiting. + * + * @param id [IN]RPC_ID of the server program + * @retval RPC_OK: normal completion + * @retval Otherwise: Fatal errors + */ + RPC_Result +RPC_process_API_request(RPC_ID id) { + if (id == RPC_NO_ID) { + return RPC_ERR_Fatal; + } + RpcThreadInfo *th = RpcMyThreadInfo(); + + THREAD_INFO_CHECK(th); + + SUB_THREAD_CHECK(); + + RpcIdInfo *idinfo = RPC_srvr_idinfo(th); + + rpc_assert(idinfo != NULL && id == RPC_my_id(idinfo)); // LCOV_EXCL_BR_LINE 6: double check + + rpc_assert(RPC_apicall_info(idinfo) != NULL /* pgr0689 */ // LCOV_EXCL_BR_LINE 6: double check + && RPC_apicall_dispatch_func(idinfo) != NULL); // LCOV_EXCL_BR_LINE 6: double check + + char c; + read(RPC_pipe_sub_main(th)[PIPE_READ], &c, sizeof(c)); + + /* Process all API calls here */ + UINT16 api_num; + RPC_ID client = RPC_NO_PORT; + RPC_Result result = RPC_OK; + char *args_string; + unsigned int args_size; + do { + api_num = RpcGetAPIRequest(idinfo, &client, + &args_string, &args_size); + + if (api_num > 0) {/* API calls were queued */ + result = ProcessAPIcallRequest(idinfo, client, api_num, + args_string, args_size); + + RpcFreeAPIArgsString(args_string); + + if (result == RPC_ERR_API_Fatal || result == RPC_ERR_Fatal) { + break; + } else { + result = RPC_OK; + } + } + } while(api_num > 0); + + return result; +} + +/** @brief Returns the operational state of the RPC server + * + * Returns whether or not the server with the specified RPC_ID is running. + * + * @param id [IN]RPC_ID of the server program + * @retval RPC_OK: Running + * @retval RPC_ERR_No_Response: Server is not running + */ + RPC_Result +RPC_is_server_ready(RPC_ID id) { +#if defined(RPC_USE_UNIX_AUTOBIND) + char *ret_string = NULL; + unsigned int ret_size; + RPC_Result ret; + + // Invoke Internal-API Calls for Server ALIVE Queries + ret = RPC_API_call(id, RPC_API_NUM_RPC_ALIVE, NULL, 0, + &ret_string, &ret_size); + RPC_free_return_string(ret_string); + + return ret; +#else /* !AUTOBIND */ + struct stat statbuf; + int err; + char sockname[128]; + + /* Check if server socket is created */ + rpc_set_socket_name(sockname, id); + err = stat(sockname, &statbuf); + if (err == 0) { + return RPC_OK; + } else { + return RPC_ERR_No_Response; + } +#endif /* !AUTOBIND */ +} + +/** @brief Registering UIDs and GID Lists + * + * Register a list of UIDs and GIDs that can be accepted by the RPC server. + * A function used by the RPC server after RPC_START_SECURE_SERVER(). + * Return error if called from an RPC client or an RPC server that has not called RPC_START_SECURE_SERVER(). + * + * @param uid_num [IN] Number of registered UIDs (number of elements in UID list array) + * @param *uid_list [IN] Start pointer of registered UID list + * @param gid_num [IN] Number of registered GIDs (number of elements in the array of GID lists) + * @param *gid_list [IN] Start pointer of registered GID list + * @retval RPC_OK: List registration completed + * @retval RPC_ERR_Fatal: Failed to save to list + */ +RPC_Result +RPC_regist_credential(int uid_num, uid_t *uid_list, int gid_num, gid_t *gid_list) { + RpcThreadInfo *th = RpcMyThreadInfo(); + THREAD_INFO_CHECK(th); + + if (NULL == RPC_srvr_idinfo(th)) { + RPC_LOG_ERR("No server thread info"); + return RPC_ERR_Fatal; + } + + RpcIdInfo *idinfo = RPC_srvr_idinfo(th); + if ((NULL == idinfo) || (NULL == RPC_apicall_info(idinfo))) { // LCOV_EXCL_BR_LINE 8: dead code, idinfo always isn't NULL + // LCOV_EXCL_START 8: dead code, idinfo always isn't NULL + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + RPC_LOG_ERR("No RPC_ID info"); + return RPC_ERR_Fatal; + } // LCOV_EXCL_STOP + + /* Neither UID nor GID list is specified. */ + if ((NULL == uid_list) && (NULL == gid_list)) { + RPC_LOG_ERR("RPC_regist_credential() : UID and GID list is NULL."); + return RPC_ERR_Fatal; + } + + /* UID list argument check */ + if ((NULL == uid_list) && ((0 < uid_num ) && (uid_num <= CREDENTIAL_LIST_NUM_MAX))) {/* UID list is invalid */ + RPC_LOG_ERR("RPC_regist_credential() : UID list is illegal."); + return RPC_ERR_Fatal; + } else if ((NULL != uid_list) && ((1 > uid_num) || (uid_num > CREDENTIAL_LIST_NUM_MAX))) {/* Number of UID list elements is invalid */ + RPC_LOG_ERR("RPC_regist_credential() : UID list num is illegal."); + return RPC_ERR_Configuration; + } + + /* GID list argument check */ + if ((NULL == gid_list) && ((0 < gid_num ) && (gid_num <= CREDENTIAL_LIST_NUM_MAX))) {/* GID list is invalid */ + RPC_LOG_ERR("RPC_regist_credential() : GID list is illegal."); + return RPC_ERR_Fatal; + } else if ((NULL != gid_list) && ((1 > gid_num) || (gid_num > CREDENTIAL_LIST_NUM_MAX))) {/* Number of GID list elements is invalid */ + RPC_LOG_ERR("RPC_regist_credential() : GID list num is illegal."); + return RPC_ERR_Configuration; + } + + if (NO_SECURE_CHECK == RPC_secure_check(idinfo)) {/* Return error if registration is not necessary */ + RPC_LOG_ERR("Needless secure check."); + return RPC_ERR_Fatal; + } + + if (REGISTERED == RPC_regist_credential_info(idinfo)) {/* Return error if a list has already been registered */ + RPC_LOG_ERR("credential info has been registered already."); + return RPC_ERR_Fatal; + } + + /* Allocate as many memory areas as there are UID and GID lists */ + RPC_apicall_info(idinfo)->uid_list = rpc_malloc(sizeof(uid_t) * (unsigned int)uid_num); + if (NULL == RPC_apicall_info(idinfo)->uid_list) { // LCOV_EXCL_BR_LINE 5: fail safe for libc malloc + RPC_LOG_ERR("Can't malloc uid_list."); // LCOV_EXCL_START 5: fail safe for libc malloc + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + return RPC_ERR_Fatal; + } // LCOV_EXCL_STOP + RPC_apicall_info(idinfo)->gid_list = rpc_malloc(sizeof(gid_t) * (unsigned int)gid_num); + if (NULL == RPC_apicall_info(idinfo)->gid_list) { // LCOV_EXCL_BR_LINE 5: fail safe for libc malloc + RPC_LOG_ERR("Can't malloc gid_list."); // LCOV_EXCL_START 5: fail safe for libc malloc + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + return RPC_ERR_Fatal; + } // LCOV_EXCL_STOP + + /* Copy UID and GID registration list and number of elements */ + memcpy(RPC_apicall_info(idinfo)->uid_list, uid_list, sizeof(uid_t) * (unsigned int)uid_num); /* UID list */ + memcpy(RPC_apicall_info(idinfo)->gid_list, gid_list, sizeof(gid_t) * (unsigned int)gid_num); /* GID list */ + RPC_uid_num(idinfo) = uid_num; /* Number of elements in the UID list */ + RPC_gid_num(idinfo) = gid_num; /* Number of elements in the GID list */ + + /* Set whether to register authentication information to Yes/No */ + RPC_regist_credential_info(idinfo) = REGISTERED; + + return RPC_OK; +} + +/** @brief Obtain authentication info (UID, GID) of API requesting client + * + * Obtain the UID and GID of the client that requested the API processing being executed. + * Return error if called from an RPC client or to an RPC server that has not called RPC_START_SECURE_SERVER(). + * This function should be called within an API function (in other words, API is being executed) provided to the client. + * if called the other function, error is returned because there is no API being executed. + * + * @param *client_uid [OUT] Requesting client UID of running API + * @param *client_gid [OUT] Requesting client GID of running API + * @retval RPC_OK: Succeeded to obtain of authentication info + * @retval RPC_ERR_Fatal: Failed to obtain of authentication Info + */ +RPC_Result +RPC_get_client_credential(uid_t *client_uid, gid_t *client_gid) { + RpcThreadInfo *th = RpcMyThreadInfo(); + THREAD_INFO_CHECK(th); + + if (NULL == RPC_srvr_idinfo(th)) { + RPC_LOG_ERR("No server thread info"); + return RPC_ERR_Fatal; + } + + RpcIdInfo *idinfo = RPC_srvr_idinfo(th); + if ((NULL == idinfo) || (NULL == RPC_apicall_info(idinfo))) { // LCOV_EXCL_BR_LINE 8: dead code, idinfo always isn't NULL + // LCOV_EXCL_START 8: dead code, idinfo always isn't NULL + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + RPC_LOG_ERR("No RPC_ID info"); + return RPC_ERR_Fatal; + } // LCOV_EXCL_STOP + + if ((NULL == client_uid) || (NULL == client_gid)) {/* Argument check */ + RPC_LOG_ERR("RPC_regist_credential() : Invalid Param."); + return RPC_ERR_Fatal; + } + + if (NO_SECURE_CHECK == RPC_secure_check(idinfo)) {/* Return error if no security */ + RPC_LOG_ERR("Needless secure check."); + return RPC_ERR_Fatal; + } + + if (RPC_NO_PORT == RPC_apicall_in_process_client(idinfo)) {/* Return error if the API is not being executed */ + RPC_LOG_ERR("NOT in process APIcall."); + return RPC_ERR_Fatal; + } + + char buf[7], client_path_name[SOCK_NAME_LEN]; /* Client socket name */ + + /* Converting client_id to the socket name associated with RPC_ID */ + RpcSetClientName(buf, RPC_apicall_in_process_client(idinfo)); + memcpy(client_path_name, buf + 1, 5); + client_path_name[5] = '\0'; + RpcClientSockNameInfo *current = RPC_sock_info_head(idinfo); + + /* Search info of running client from the management table */ + while (NULL != current) { + if (0 == strcmp(current->client_sock_name, client_path_name)) {/* Match Running Client Info */ + *client_uid = current->uid; + *client_gid = current->gid; + return RPC_OK; + } + current = current->next; + } + RPC_LOG_ERR("Nothing client credential info."); + return RPC_ERR_Fatal; /* Not found Client info */ +} + +/** @brief Waiting for server authentication + * + * When a client requests an API to the server, + * wait for the server to check whether it is allowed to communicate. + * + * @param *idinfo [IN] Information associated with the client's RPC_ID + * + * @retval RPC_OK: Authentication OK (communication with the server is permitted) + * @retval RPC_ERR_Reject_connect: Authentication NG (Communication denied) + */ +static RPC_Result +RpcClientWaitCertification(RpcIdInfo *idinfo, pid_t *srvr_pid) { + fd_set fds; + RpcCertifyResult recv_ret; /* Authentication result from the server */ + RPC_Result ret = RPC_ERR_Fatal; + + for(;;) { + FD_ZERO(&fds); + FD_SET(RPC_my_secure_sock(idinfo), &fds); + + int sret = select(RPC_my_secure_sock(idinfo) + 1, &fds, NULL, NULL, NULL); + if (sret < 0 && errno == EINTR) {/* signal interrupt */ // LCOV_EXCL_BR_LINE 5: fail safe for libc select + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + continue; // LCOV_EXCL_LINE 5: fail safe for libc select + } else if (sret > 0 && FD_ISSET(RPC_my_secure_sock(idinfo), &fds)) { + /* Check the response from the server */ + ssize_t rret = recv(RPC_my_secure_sock(idinfo), (char*)&recv_ret, sizeof(RpcCertifyResult), 0); + if (0 < rret) { // LCOV_EXCL_BR_LINE 5: fail safe for libc recv + switch(recv_ret.certify_res) { + case CERTIFY_OK: /* OK */ + ret = RPC_OK; + *srvr_pid = recv_ret.srvr_pid; + break; + + case CERTIFY_NG: /* NG */ + ret = RPC_ERR_Reject_connect; + break; + + default: + ret = RPC_ERR_Fatal; + break; + } + goto exit_loop_ok; + } else if(0 == rret) { // LCOV_EXCL_START 5: fail safe for libc recv + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + RPC_LOG_ERR("server process terminate."); + return RPC_ERR_Fatal; + } else { + RPC_LOG_PERROR("certification_wait recv()"); + return RPC_ERR_Fatal; + } // LCOV_EXCL_STOP + } + } +exit_loop_ok: + return ret; +} + +/** @brief Register the PID of the communication destination server in the management table. + * + * Register a pair of the PID and ID of the server in the management table. + * + * @param *idinfo [IN] Information associated with the client's RPC_ID + * @param srvr_rpc_id [IN] RPC_ID of the communication destination server + * @param srvr_pid [IN] PID of the communication destination server + * + * @retval RPC_OK : Registration completed + * @retval RPC_ERR_Fatal : Registration failed + */ +static RPC_Result +RpcRegistSrvrPid(RpcIdInfo *idinfo, RPC_ID srvr_rpc_id, pid_t srvr_pid, int wd) { + if ((NULL == idinfo) || (srvr_rpc_id == RPC_NO_ID) || (0 > srvr_pid)) { // LCOV_EXCL_START 8: dead code, idinfo always isn't NULL + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + RPC_LOG_ERR("RpcRegistSrvrPid() : Invalid Param."); + return RPC_ERR_Fatal; + } // LCOV_EXCL_STOP + + RpcSrvrPidInfo *srvr_pid_buf, *current; + + srvr_pid_buf = rpc_malloc(sizeof(RpcSrvrPidInfo)); + if( srvr_pid_buf == NULL ){ // LCOV_EXCL_START 5: fail safe for libc malloc + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + return RPC_ERR_Fatal; + } // LCOV_EXCL_STOP + + srvr_pid_buf->srvr_rpc_id = srvr_rpc_id; /* Server RPC_ID */ + srvr_pid_buf->srvr_pid = srvr_pid; /* Server PID */ + srvr_pid_buf->wd = wd; /* wd */ + srvr_pid_buf->next = NULL; /* Next node in the communication destination server PID management table */ + + if (0 == RPC_srvr_pid_head(idinfo)) { + RPC_srvr_pid_head(idinfo) = srvr_pid_buf; + } else { + for (current = RPC_srvr_pid_head(idinfo); current->next != NULL; current = current->next) + ; + current->next = srvr_pid_buf; + } + return RPC_OK; +} + +/** @brief Delete Termination Process PID from Management Table + * + * Check whether the terminated process matches the communicating server process, and delete it from the management table. + * + * @param *idinfo [IN] Information associated with the client's RPC_ID + * @param srvr_rpc_id [IN] Currently communicating server RPC_ID + * @param wd [IN] Wd of the termination process + * + * @retval RPC_OK : Deletion completed + * @retval RPC_ERR_Server_Finish: Deletion completed (server process in communication terminated) + * @retval RPC_ERR_Fatal : Argument error + */ + RPC_Result +RpcDeleteSrvrPid(RpcIdInfo *idinfo, RPC_ID srvr_rpc_id, int wd) { + if ((NULL == idinfo) || (RPC_NO_ID == srvr_rpc_id)) { // LCOV_EXCL_START 8: dead code, idinfo always isn't NULL + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + RPC_LOG_ERR("rpc_check_srvr_pid() : Invalid Param."); + return RPC_ERR_Fatal; + } // LCOV_EXCL_STOP + + RPC_Result ret = RPC_OK; + int cnt = 0; + RpcSrvrPidInfo *current, *previous; + current = RPC_srvr_pid_head(idinfo); + + while (NULL != current) { + if (wd == current->wd) { + + /* Check whether the termination process matches the communicating server process */ + if (srvr_rpc_id == current->srvr_rpc_id) { + ret = RPC_ERR_Server_Finish; + } + + /* Delete info from the Server info table */ + if (0 == cnt) {/* Delete the start of the management table */ + RPC_srvr_pid_head(idinfo) = RPC_srvr_pid_head(idinfo)->next; + rpc_free(current); + current = RPC_srvr_pid_head(idinfo); + cnt = -1; + } else {/* Delete all but the first element */ + // LCOV_EXCL_START 8: dead code + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + previous->next = current->next; + rpc_free(current); + current = previous->next; + // LCOV_EXCL_STOP + } + } else { + previous = current; + current = current->next; + } + cnt ++; + } + return ret; +} + +/** @brief Delete all PID management tables of the communication destination server + * + * Releases the PID management table and deletes all data when the client terminates with RPC_end(). + * + * @param *idinfo [IN] Information associated with the client's RPC_ID + * + * @retval RPC_OK : Deletion completed + * @retval RPC_ERR_Fatal : Argument error + */ +static RPC_Result +RpcAllDeleteSrvrPid(RpcIdInfo *idinfo) { + if (NULL == idinfo) { // LCOV_EXCL_BR_LINE 6: double check in rpc_lib.c --> RPC_end() + RPC_LOG_ERR("RpcAllDeleteSrvrPid() : Invalid Param."); // LCOV_EXCL_START 6: double check in rpc_lib.c --> RPC_end() + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + return RPC_ERR_Fatal; + } // LCOV_EXCL_STOP + + RpcSrvrPidInfo *current = RPC_srvr_pid_head(idinfo); + while (NULL != current) { + + if (0 <= current->wd) { + inotify_rm_watch(RPC_clnt_inotify_fd(idinfo), current->wd); + } + + RpcSrvrPidInfo *previous = current; + current = current->next; + + rpc_free(previous); + } + RPC_srvr_pid_head(idinfo) = NULL; + return RPC_OK; +} + +/** @brief Logging RPC-API calling process + * + * Receive File name, function name and line-number of the client calling API and API-function-name, and logging them. + * + * @param filename [IN]Pointer to caller filename string + * @param funcname [IN]Pointer to caller function-name string + * @param line [IN]Line-number in the source file of the client + * @param apiname [IN]Pointer to call-API-function-name string + * @retval 0: normal completion + * @retval Otherwise: Fatal error + */ + int +RPC_record_dbg_log( const char *filename, const char *funcname, int line, + const char *apiname ) { + printf( "[RPC debug log]%s() called from %s() [%s, line %d]\n", + apiname, funcname, filename, line ); + return 0; +} + +/** @}*/ diff --git a/otherservice/rpc_library/library/src/rpc_marshall.c b/otherservice/rpc_library/library/src/rpc_marshall.c new file mode 100644 index 00000000..e4a085c0 --- /dev/null +++ b/otherservice/rpc_library/library/src/rpc_marshall.c @@ -0,0 +1,740 @@ +/* + * @copyright Copyright (c) 2016-2019 TOYOTA MOTOR CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file rpc_marshall.c + * @brief RPC Library Internal Implementation--Argument Conversion During API Calls + * + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include "rpc_internal.h" + +#include "apidef.tab.h" +#include "apidef.h" + +/** @addtogroup RPClib_in + * @{ + */ +/** Structures for String Add Operations */ +typedef struct { + char *start; + char *wp; + unsigned int units; + unsigned int remain; +} RpcString; + +#ifdef STRETCH_STRING +#define _STRING_ALLOC_UNIT 512 +#else +/* + * Batch allocate total bytes of arguments + data to be added internally + * (maximal 6 bytes per argument) + */ +#define _STRING_ALLOC_UNIT \ + (RPC_MAX_API_ARG_TOTAL_SIZE + RPC_MAX_API_ARG_NUM * (2 + 4)) +#endif + +#define _ENOUGH_SPACE_FOR_ALL_TYPES \ + (sizeof(UINT64) > sizeof(double) ? sizeof(UINT64) : sizeof(double)) + +static RpcString* NewRpcString(void); +#ifdef STRETCH_STRING +static int StretchString(RpcString *str, UINT16 bytes); +#endif +static char *CopyString(RpcString *str, unsigned int *size); +static void DestroyString(RpcString *str); +static inline int AppendString(RpcString *str, + const UINT8 *append, UINT16 applen); +static inline int MarshallUINT8(RpcString *str, UINT8 uc); +static inline int MarshallUINT16(RpcString *str, UINT16 uh); +static inline int MarshallUINT32(RpcString *str, UINT32 ui); +static inline int MarshallUINT64(RpcString *str, UINT64 ul); +static inline int Marshallfloat(RpcString *str, float f); +static inline int Marshalldouble(RpcString *str, double d); +static inline int MarshallUINT8Stream(RpcString *str, const UINT8 *buf, + UINT16 bytes); +static inline int MarshallString(RpcString *str, char *buf, + UINT16 bytes); +static inline int MarshallNullPointer(RpcString *str); +static inline int MarshallPointer(RpcString *str); + +static RpcString * +NewRpcString(void) { + RpcString *str; + + str = rpc_malloc(sizeof(RpcString)); + if (str == NULL) { // LCOV_EXCL_START 5: fail safe for libc malloc + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + return NULL; + } // LCOV_EXCL_STOP + + str->start = rpc_malloc(_STRING_ALLOC_UNIT); + if (str->start == NULL) { // LCOV_EXCL_START 5: fail safe for libc malloc + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + rpc_free(str); + return NULL; + } // LCOV_EXCL_STOP + str->start[0] = '\0'; + str->wp = str->start; + str->units = 1; + str->remain = _STRING_ALLOC_UNIT - 1; + + return str; +} + +#ifdef STRETCH_STRING +static int +StretchString(RpcString *str, UINT16 bytes) { + unsigned int oldunits = str->units; + unsigned int units = 1 + (bytes / _STRING_ALLOC_UNIT) + oldunits; + if (units == oldunits) { + return 0; + } + unsigned int length = str->wp - str->start; + char *newp = rpc_malloc(units * _STRING_ALLOC_UNIT); + if (newp == NULL) { + return -1; + } + memcpy(newp, str->start, length); + rpc_free(str->start); + str->start = newp; + str->wp = str->start + length; + *(str->wp) = '\0'; + str->units = units; + str->remain += (units - oldunits) * _STRING_ALLOC_UNIT; + return 0; +} +#endif /* STRETCH_STRING */ + +static inline int +AppendString(RpcString *str, const UINT8 *append, UINT16 applen) { + rpc_assert(append != NULL); // LCOV_EXCL_BR_LINE 6: double check +#ifdef STRETCH_STRING + if (applen > str->remain) { + if (StretchString(str, applen - str->remain) < 0) { + return -1; + } + } +#else + rpc_assert(applen <= str->remain); // LCOV_EXCL_BR_LINE 6: double check +#endif + memcpy(str->wp, append, applen); + str->wp += applen; + str->remain -= applen; + return 0; +} + +static char * +CopyString(RpcString *str, unsigned int *size) { + unsigned int length = (unsigned int)(str->wp - str->start); + char *ret = rpc_malloc(length); + // LCOV_EXCL_BR_START 5: fail safe for libc malloc + if (ret == NULL) { + return NULL; + } + // LCOV_EXCL_BR_STOP + memcpy(ret, str->start, length); + if (size != NULL) { + *size = length; + } + + return ret; +} + +static void +DestroyString(RpcString *str) { + rpc_free(str->start); + rpc_free(str); +} + +static inline int +MarshallUINT8(RpcString *str, UINT8 c) { + UINT8 buf[1 + sizeof(c)]; + buf[0] = 'C'; + buf[1] = c; + return AppendString(str, buf, sizeof(buf)); +} + +static inline int +MarshallUINT8Stream(RpcString *str, const UINT8 *buf, UINT16 bytes) { + int ret = AppendString(str, (const UINT8 *)"B", 1); + if (ret < 0) { // LCOV_EXCL_BR_LINE 15: inline func rpc_marshall.c + return -1; + } + return AppendString(str, buf, bytes); +} + +static inline int +MarshallVararrayStream(RpcString *str, const UINT8 *buf, UINT16 bytes) { + char head_str[1+4+1+1]; /* Area where symbol "V" + size is stored */ + sprintf(head_str, "V%03d ", bytes); + head_str[1+4+1] = '\0'; + + if (AppendString(str, (const UINT8 *)head_str, (UINT16)strlen(head_str)) < 0) { // LCOV_EXCL_BR_LINE 15: inline func rpc_marshall.c + return -1; + } + + if (AppendString(str, buf, bytes) < 0) { // LCOV_EXCL_BR_LINE 15: inline func rpc_marshall.c + return -1; + } + return 0; +} + +static inline int +MarshallString(RpcString *str, char *buf, UINT16 bytes) { + char *p = buf; + UINT16 count = 0; + /* count the number of bytes in the argument */ + while(*p != '\0' && count < (UINT16)(bytes - 1)) { + count++; + p++; + } + + char count_str[1+strlen("1024")+1+1]; + sprintf(count_str, "S%d ", count); + + if (AppendString(str, (const UINT8 *)count_str, (UINT16)strlen(count_str)) < 0 || AppendString(str, (const UINT8 *)buf, count) < 0) { // LCOV_EXCL_BR_LINE 11: Unexpected branch // NOLINT(readability/nolint) + return -1; + } + return 0; +} + +static int +MarshallUINT16(RpcString *str, UINT16 uh) { + UINT8 buf[1 + sizeof(uh)]; + buf[0] = 'H'; + memcpy(buf + 1, &uh, sizeof(uh)); + return AppendString(str, buf, sizeof(buf)); +} + +static inline int +MarshallUINT32(RpcString *str, UINT32 ui) { + UINT8 buf[1 + sizeof(ui)]; + buf[0] = 'I'; + memcpy(buf + 1, &ui, sizeof(ui)); + return AppendString(str, buf, sizeof(buf)); +} + +static inline int +Marshallint(RpcString *str, int i) { + return MarshallUINT32(str, (UINT32)i); +} + +static inline int +MarshallUINT64(RpcString *str, UINT64 ul) { + UINT8 buf[1 + sizeof(ul)]; + buf[0] = 'L'; + memcpy(buf + 1, &ul, sizeof(ul)); + return AppendString(str, buf, sizeof(buf)); +} + +static inline int +Marshallfloat(RpcString *str, float f) { + UINT8 buf[1 + sizeof(f)]; + buf[0] = 'F'; + memcpy(buf + 1, &f, sizeof(f)); + return AppendString(str, buf, sizeof(buf)); +} + +static inline int +Marshalldouble(RpcString *str, double d) { + UINT8 buf[1 + sizeof(d)]; + buf[0] = 'D'; + memcpy(buf + 1, &d, sizeof(d)); + return AppendString(str, buf, sizeof(buf)); +} + +static inline int +MarshallPointer(RpcString *str) { + /* only to specify that a non-NULL pointer was delivered */ + return AppendString(str, (const UINT8 *)"P", 1); +} + +static inline int +MarshallNullPointer(RpcString *str /*, int code*/) { + return AppendString(str, (const UINT8 *)"N", 1); +} + +/** @ingroup RPClib_in + */ + +#define MACROMarshallPointer(TYPE) \ + do { \ + TYPE *p = (TYPE *)temp; \ + error = Marshall##TYPE(str, *p); \ + } while(0) + +#define MACROMarshallValue(TYPE, STACK_TYPE) \ + do { \ + TYPE value = (TYPE)va_arg(ap, STACK_TYPE); \ + error = Marshall##TYPE(str, value); \ + } while(0) + +char * +RPC_marshall_arguments(unsigned int *size, int dont_marshall_out_pointer, + int num_args, ...) { + rpc_assert(num_args <= RPC_MAX_API_ARG_NUM); // LCOV_EXCL_BR_LINE 6: double check + va_list ap; + + RpcString *str; + str = NewRpcString(); + if (str == NULL) { // LCOV_EXCL_BR_LINE 5: fail safe for libc malloc + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + return NULL; // LCOV_EXCL_LINE 5: fail safe for libc malloc + } + + va_start(ap, num_args); + + int ii, error; + error = 0; + + for(ii = 0 ; ii < num_args && error == 0 ; ii++) { + unsigned int code, is_vararray, is_pointer, in_out, out_only; + UINT16 bytes; + unsigned int val = va_arg(ap, unsigned int); + + RPC_marshall_flag flag; + flag.all = ntohl(val); + code = flag.bits.code; + is_vararray = flag.bits.is_vararray; + is_pointer = flag.bits.is_pointer; + in_out = flag.bits.in_out; + bytes = flag.bits.bytes; + rpc_assert(bytes <= RPC_MAX_API_ARG_SIZE); // LCOV_EXCL_BR_LINE 6: double check + + + out_only = 0; + if (in_out == RPC_OUT_ARG) { /* OUT only argument */ + out_only = 1; + } + + if (is_pointer) { /* Argument passed by pointer */ + + void *temp = (void *)va_arg(ap, void *); + if (temp == NULL) { + /* NULL pointer */ + error = MarshallNullPointer(str); + } else if (dont_marshall_out_pointer && out_only) { + /* Not refer to the contents of a pointer */ + error = MarshallPointer(str); + } else {/* Marshall the contents of pointers */ + if (is_vararray) { + /* Variable-length array */ + error = MarshallVararrayStream(str, temp, bytes); + } else { + switch(code) { + case rpc_CHAR: + case rpc_INT8: + case rpc_UINT8: + MACROMarshallPointer(UINT8); + break; + case rpc_INT16: + case rpc_UINT16: + MACROMarshallPointer(UINT16); + break; + case rpc_INT: + case rpc_SINT: + case rpc_UINT: + MACROMarshallPointer(int); + break; + case rpc_INT32: + case rpc_UINT32: + MACROMarshallPointer(UINT32); + break; + case rpc_INT64: + case rpc_UINT64: + MACROMarshallPointer(UINT64); + break; + case rpc_FLOAT: + MACROMarshallPointer(float); + break; + case rpc_DOUBLE: + MACROMarshallPointer(double); + break; + case rpc_STRING: + error = MarshallString(str, temp, bytes); + break; + case rpc_USER_DEFINED: + error = MarshallUINT8Stream(str, temp, bytes); + break; + default: + error = -1; + break; + } + } + } + } else {/* Argument passed by value */ + + /* Note: In this area, the code depends on the CPU architecture */ + + switch(code) { + case rpc_CHAR: + case rpc_INT8: + case rpc_UINT8: + MACROMarshallValue(UINT8, unsigned int); + break; + case rpc_INT16: + case rpc_UINT16: + MACROMarshallValue(UINT16, unsigned int); + break; + case rpc_INT: + case rpc_SINT: + case rpc_UINT: + MACROMarshallValue(int, int); // LCOV_EXCL_BR_LINE 15: marco defined in rpc_marshall.c + break; + case rpc_INT32: + case rpc_UINT32: + MACROMarshallValue(UINT32, UINT32); // LCOV_EXCL_BR_LINE 15: marco defined in rpc_marshall.c + break; + case rpc_INT64: + case rpc_UINT64: + MACROMarshallValue(UINT64, UINT64); // LCOV_EXCL_BR_LINE 15: marco defined in rpc_marshall.c + break; + case rpc_FLOAT: + MACROMarshallValue(float, double); // LCOV_EXCL_BR_LINE 15: marco defined in rpc_marshall.c + break; + case rpc_DOUBLE: + MACROMarshallValue(double, double); // LCOV_EXCL_BR_LINE 15: marco defined in rpc_marshall.c + break; + case rpc_USER_DEFINED: + rpc_assert(bytes <= sizeof(UINT64)); + /* This area is very architecture-dependent! */ + if (bytes <= sizeof(UINT32)) { + UINT32 value = (UINT32)va_arg(ap, UINT32); // LCOV_EXCL_BR_LINE 15: macro defined in stdarg.h + error = MarshallUINT8Stream(str, (UINT8 *)&value, bytes); + } else if (bytes <= sizeof(UINT64)) { // LCOV_EXCL_BR_LINE 6: double check + UINT64 value = (UINT64)va_arg(ap, UINT64); // LCOV_EXCL_BR_LINE 15: macro defined in stdarg.h + error = MarshallUINT8Stream(str, (UINT8 *)&value, bytes); + } + break; + default: + error = -1; + break; + } + } + } + va_end(ap); + + if (error != 0) { + DestroyString(str); + return NULL; + } + + char *ret = CopyString(str, size); + DestroyString(str); + return ret; +} + +static inline int DeMarshallUINT8(const char *from, UINT8 *ucp); +static inline int DeMarshallUINT16(const char *from, UINT16 *uhp); +static inline int DeMarshallUINT32(const char *from, UINT32 *uip); +static inline int DeMarshallUINT64(const char *from, UINT64 *ulp); +static inline int DeMarshallfloat(const char *from, float *fp); +static inline int DeMarshalldouble(const char *from, double *dp); +static inline int DeMarshallUINT8Stream(const char *from, UINT8 *buffer, + UINT16 bytes); +static inline int DeMarshallString(const char *from, char *buffer, + UINT16 bytes); + +static inline int +DeMarshallUINT8(const char *from, UINT8 *ucp) { + if (*from == 'C') { + *ucp = *(UINT8 *)(from + 1); + return 1 + sizeof(*ucp); + } else { + return -1; + } +} + +static inline int +DeMarshallUINT16(const char *from, UINT16 *uhp) { + if (*from == 'H') { + memcpy(uhp, from + 1, sizeof(*uhp)); + return 1 + sizeof(*uhp); + } else { + return -1; + } +} + +static inline int +DeMarshallUINT32(const char *from, UINT32 *uip) { + if (*from == 'I') { + memcpy(uip, from + 1, sizeof(*uip)); + return 1 + sizeof(*uip); + } else { + return -1; + } +} + +static inline int +DeMarshallint(const char *from, int *ip) { + return DeMarshallUINT32(from, (UINT32 *)ip); +} + +static inline int +DeMarshallUINT64(const char *from, UINT64 *ulp) { + if (*from == 'L') { + memcpy(ulp, from + 1, sizeof(*ulp)); + return 1 + sizeof(*ulp); + } else { + return -1; + } +} + +static inline int +DeMarshallfloat(const char *from, float *fp) { + if (*from == 'F') { + memcpy(fp, from + 1, sizeof(*fp)); + return 1 + sizeof(*fp); + } else { + return -1; + } +} + +static inline int +DeMarshalldouble(const char *from, double *dp) { + if (*from == 'D') { + memcpy(dp, from + 1, sizeof(*dp)); + + return 1 + sizeof(*dp); + } else { + return -1; + } +} + +static inline int +DeMarshallUINT8Stream(const char *from, UINT8 *buffer, UINT16 bytes) { + if (*from == 'B') { + memcpy(buffer, from + 1, bytes); + return (int)(1 + bytes); + } else { + return -1; + } +} + +static inline int +DeMarshallString(const char *from, char *buffer, UINT16 bytes) { + if (*from == 'S') { + char *start; + long len = strtol(from + 1, &start, 10); + if (len < 0 || len >= bytes) { // LCOV_EXCL_BR_LINE 5: fail safe for libc strtol + return -1; + } + start++; /* skip ' ' */ + int skip = (int)(start - from); + memcpy(buffer, start, (size_t)len); + buffer[len] = '\0'; + return skip + (int)len; + } else { + return -1; + } +} + +/* Variable-length array data */ +static inline int +DemarshallVararrayInfo(const char *from, UINT16 *bytes/* OUT */) { + if (*from == 'V') { + char *end; + long len = strtol((char *)(from + 1), &end, 10); + if (len <= 0 || len > RPC_MAX_API_ARG_SIZE) { // LCOV_EXCL_BR_LINE 5: fail safe for libc strtol + return -1; + } + *bytes = (UINT16)len; + return (int)(end - from + 1); /* skip ' ' */ + } else { + return -1; + } +} + +/** @ingroup RPClib_in + */ + +#define MACRODemarshall(TYPE) \ + do {\ + TYPE *p; \ + if (need_alloc && is_pointer) { \ + p = rpc_malloc(sizeof(TYPE)); \ + *(TYPE **)temp = p; \ + } else { \ + p = (TYPE *)temp; \ + } \ + if (p != NULL) { \ + ret = DeMarshall##TYPE(from, p); \ + } \ + } while(0) + +int +RPC_demarshall_arguments(const char *from, unsigned int size, + int need_alloc, int num_args, ...) { + rpc_assert(num_args <= RPC_MAX_API_ARG_NUM); // LCOV_EXCL_BR_LINE 6: double check + va_list ap; + va_start(ap, num_args); + + int ii, error; + error = 0; + int remain_len = (int)size; + + for(ii = 0 ; ii < num_args && error == 0 && remain_len > 0 ; ii++) { + unsigned int code, is_pointer, is_vararray; + UINT16 bytes; + unsigned int val = va_arg(ap, unsigned int); + RPC_marshall_flag flag; + flag.all = ntohl(val); + code = flag.bits.code; + is_vararray = flag.bits.is_vararray; + is_pointer = flag.bits.is_pointer; + bytes = flag.bits.bytes; + rpc_assert(bytes <= RPC_MAX_API_ARG_SIZE); // LCOV_EXCL_BR_LINE 6: double check + + + void *temp = va_arg(ap, void *); + int ret = -1; + + if (*from == 'N') { /* NULL pointer */ + if (bytes > 0 || is_pointer != 0) { + if (need_alloc) { + *(void **)temp = NULL; + } else { + /* do nothing */ + } + ret = 1; + } + } else if (*from == 'P') { /* Pointer(no content) */ + if (need_alloc) { + if (bytes > 0) { /* String type or user-defined type */ + *(void **)temp = rpc_malloc(bytes); + } else if (is_pointer != 0) { /* Other pointers */ + *(void **)temp = rpc_malloc(_ENOUGH_SPACE_FOR_ALL_TYPES); + } + ret = 1; + } + } else { /* non-NULL pointer */ + if ( is_vararray ) { /* Variable-length array */ + ret = DemarshallVararrayInfo(from, &bytes); + if( ret < 0 ) { + va_end(ap); + return -1; /* pgr0524 */ + } + char *p; + if (need_alloc) { + p = rpc_malloc(sizeof(char)*bytes); + *(char **)temp = p; + } else { + p = (char *)temp; /* pgr0524 */ + } + if (p != NULL) { + memcpy(p, from + ret, bytes); + ret += bytes; + } + } else { + switch(code) { + case rpc_CHAR: + case rpc_INT8: + case rpc_UINT8: + MACRODemarshall(UINT8); /* pgr0524 */ // LCOV_EXCL_BR_LINE 15: marco defined in rpc_marshall.c + break; + case rpc_INT16: + case rpc_UINT16: + MACRODemarshall(UINT16); /* pgr0524 */ // LCOV_EXCL_BR_LINE 15: marco defined in rpc_marshall.c + break; + case rpc_INT: + case rpc_SINT: + case rpc_UINT: + MACRODemarshall(int); /* pgr0524 */ // LCOV_EXCL_BR_LINE 15: marco defined in rpc_marshall.c + break; + case rpc_INT32: + case rpc_UINT32: + MACRODemarshall(UINT32); /* pgr0524 */ // LCOV_EXCL_BR_LINE 15: marco defined in rpc_marshall.c + break; + case rpc_INT64: + case rpc_UINT64: + MACRODemarshall(UINT64); /* pgr0524 */ // LCOV_EXCL_BR_LINE 15: marco defined in rpc_marshall.c + break; + case rpc_FLOAT: + MACRODemarshall(float); /* pgr0524 */ // LCOV_EXCL_BR_LINE 15: marco defined in rpc_marshall.c + break; + case rpc_DOUBLE: + MACRODemarshall(double); /* pgr0524 */ // LCOV_EXCL_BR_LINE 15: marco defined in rpc_marshall.c + break; + case rpc_USER_DEFINED: { + UINT8 *p; + if (need_alloc && is_pointer) { + p = rpc_malloc(bytes); + *(UINT8 **)temp = p; + } else { + p = (UINT8 *)temp; /* pgr0524 */ + } + if (p != NULL) { + ret = DeMarshallUINT8Stream(from, p, bytes); + } + break; + } + case rpc_STRING: { + char *p; + if (need_alloc) { + p = rpc_malloc(sizeof(char)*bytes); + *(char **)temp = p; + } else { + p = (char *)temp; /* pgr0524 */ + } + if (p != NULL) { + ret = DeMarshallString(from, p, bytes); + } + break; + } + default: + break; + } + } + } + if (ret < 0) { + error = 1; + } else { + remain_len -= ret; + from += ret; + } + } + va_end(ap); + + if (error) { + return -1; /* pgr0524 */ + } else { + return 0; /* pgr0524 */ + } +} + +void +RPC_marshall_free(int num, ...) { + va_list ap; + va_start(ap, num); + + int i; + for(i = 0 ; i < num ; i++) { + void *ptr = va_arg(ap, void *); // LCOV_EXCL_BR_LINE 15: macro defined in stdarg.h + if (ptr != NULL) { + rpc_free(ptr); + } + } + va_end(ap); +} diff --git a/otherservice/rpc_library/library/src/rpc_thread.c b/otherservice/rpc_library/library/src/rpc_thread.c new file mode 100644 index 00000000..76778e75 --- /dev/null +++ b/otherservice/rpc_library/library/src/rpc_thread.c @@ -0,0 +1,1224 @@ +/* + * @copyright Copyright (c) 2016-2019 TOYOTA MOTOR CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file rpc_thread.c + * @brief RPC Library Internal Implementation--Processing of Internally Generated Threads + * + */ +/** @addtogroup RPClib_in */ +/** @{ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include "rpc_internal.h" + +#include +#include + +/** Sub-threads that wake up in the RPC library */ +static RpcThreadInfo *Thread_info[RPC_MAX_THREADS_IN_PROCESS]; +static int Num_thread_info; +pthread_t g_rpc_thread = RPC_NO_THREAD; +UINT32 g_rpc_thread_alive;/**< Sub-thread running */ + +#define RPC_MAGIC_ID (('R'<<24)|('P'<<16)|('C'<<8)|'L') + +/** Pipes used for communication with sub-threads + * Read: Main thread, Write: Sub-thread */ +int g_rpc_pipe_main_sub[2] = { -1, -1 }; + +static pthread_mutex_t process_global_mutex = PTHREAD_MUTEX_INITIALIZER; +#define PROCESS_MUTEX_LOCK rpc_mutex_lock(&process_global_mutex) +#define PROCESS_MUTEX_UNLOCK rpc_mutex_unlock(&process_global_mutex) + +static void *RpcThreadMain(void *ptr); +static void NotifyMainThread(RpcThreadInfo *th); + +static void KillRpcThread(void); +static void NotifyAddServer(RpcThreadInfo *th); +static void NotifyRemoveServer(RpcThreadInfo *th); + +static RPC_Result RpcRegistSockName(RpcIdInfo *idinfo, char *client_sock_name, const struct ucred *cr, int wd); +static RPC_Result RpcCheckSockName(RpcIdInfo *idinfo, RPC_ID client_id); +static RPC_Result RpcDeleteSockName(RpcIdInfo *idinfo, int wd); +static RPC_Result RpcAllDeleteSockName(RpcIdInfo *idinfo, int inotify_fd); +static RPC_Result RpcCheckClientCredential(RpcIdInfo *idinfo, const struct ucred *cr); + +#define RPC_SUB_THREAD_WAIT_SEC 5 + +#define WAIT_FOR_SUB_THREAD(loop_cond, sec) \ +{ \ + struct timeval timeout; \ + timeout.tv_sec = sec; \ + timeout.tv_usec = 0; \ + \ + int fd = RPC_pipe_sub_main(th)[PIPE_READ]; \ + fd_set fds; \ + \ + while((loop_cond)) { \ + FD_ZERO(&fds); \ + FD_SET(fd, &fds); \ + int sret = select(fd + 1, &fds, NULL, NULL, &timeout); \ + if (sret < 0 && errno == EINTR) { \ + continue; \ + } else if (sret > 0 && FD_ISSET(fd, &fds)) { \ + char c; \ + read(fd, &c, sizeof(c)); \ + } else { \ + break; \ + } \ + } \ +} + +RUNS_IN_CALLERS_THREAD + RpcThreadInfo * +RpcMyThreadInfo(void) { + RpcThreadInfo *ret = NULL; + int i; + pthread_t me = pthread_self(); + + PROCESS_MUTEX_LOCK; + for(i = 0; i < RPC_MAX_THREADS_IN_PROCESS ; i++) { + if (Thread_info[i] != NULL + && pthread_equal(Thread_info[i]->thread, me)) { + ret = Thread_info[i]; + break; + } + } + PROCESS_MUTEX_UNLOCK; + return ret; +} + +RUNS_IN_CALLERS_THREAD + RpcThreadInfo * +RpcCreateThreadInfo(void) { + int i; + pthread_t me = pthread_self(); + + PROCESS_MUTEX_LOCK; + /* Look for a free slot to store the thread info pointer */ + for(i = 0; i < RPC_MAX_THREADS_IN_PROCESS ; i++) { + if (Thread_info[i] != NULL) { + if (pthread_equal(Thread_info[i]->thread, me)) { // LCOV_EXCL_BR_LINE 6: double check + PROCESS_MUTEX_UNLOCK; // LCOV_EXCL_START 6: double check + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + return Thread_info[i]; + } // LCOV_EXCL_STOP + } else { + break; + } + } + + if (i == RPC_MAX_THREADS_IN_PROCESS) { + PROCESS_MUTEX_UNLOCK; + //CONFIG_ASSERT("Must increase RPC_MAX_THREADS_IN_PROCESS"); + RPC_LOG_ERR("Must increase RPC_MAX_THREADS_IN_PROCESS"); + return NULL; + } + + /* Allocate area for thread info */ + // Because there is a timing when the server sub-thread is accessed without being initialized, + // corrected so as to fill up to 0 in the MUTEX. + RpcThreadInfo *th = rpc_malloc(sizeof(*th)); + if (th != NULL) { // LCOV_EXCL_BR_LINE 5: fail safe for libc malloc + memset(th, 0, sizeof(*th)); + Thread_info[i] = th; + th->magic = RPC_MAGIC_ID; + Num_thread_info++; + } + PROCESS_MUTEX_UNLOCK; + + if (th == NULL) { // LCOV_EXCL_START 5: fail safe for libc malloc + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + RPC_LOG_ERR("Can't alloc thread_info."); + return NULL; + } // LCOV_EXCL_STOP + + /* Initializing Thread Info */ + th->thread = me; + pthread_mutex_init(&(th->th_mtx), NULL); + th->sequence_number = RPC_SEQ_NUM_START; + + return th; +} + +/* + * check if the allocated client ID conflicts with the server ID + * of the same thread + */ + static int +RpcCheckIdConflict(RpcThreadInfo *th, RPC_ID id) { + RpcIdInfo *idinfo; + idinfo = RPC_srvr_idinfo(th); + if (idinfo != NULL && RPC_my_id(idinfo) == id) { // LCOV_EXCL_START 6: double check + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + return 1; + } // LCOV_EXCL_STOP + return 0; +} + +/** Adding IDs with RPC_start() + * + * - Main: Create and initialize structures and tell sub-threads to add + * - Sub: Add pointer to thread info (RpcThreadInfo) and notify it to the main process + * - The main process waits for this procedure to finish. + * Use id_info->thread_info to determine completion (completed if not NULL) + */ +RUNS_IN_CALLERS_THREAD + int +RpcCreateIdInfo(RpcThreadInfo *th, + RPC_ID id, RPC_dispatch_func_t dispatch, INT32 secure_check) { + RpcIdInfo *id_info = rpc_malloc(sizeof(*id_info)); + if (id_info == NULL) { // LCOV_EXCL_START 5: fail safe for libc malloc + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + return -1; + } // LCOV_EXCL_STOP + memset(id_info, 0, sizeof(*id_info)); + + /* + * Creates a Unix domain socket based on a given number of ports + */ +#if defined(RPC_USE_UNIX_AUTOBIND) + int sock_un = -1, secure_sock_un = -1; + socklen_t sa_len; + struct sockaddr_un sa_un; + +get_id_retry: + sock_un = socket(PF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); /* Datagram socket for receiving API requests */ + if (sock_un < 0) { + RPC_LOG_PERROR("socket"); + goto error; + } + SET_NONBLOCK(sock_un); + SET_CLOSE_ON_EXEC(sock_un); + + /* + * Naming Rules for Unix domain Sockets + * Server:(ID=50000-59999) + * sa_un.sun_path[0] = 0x00; + * sa_un.sun_path[1] = 'S'; + * sa_un.sun_path[2-5] = sprintf("%04x", ID); + * + * Client:(ID=1-0xfffff) + * sa_un.sun_path[0] = 0x00; + * sa_un.sun_path[1-5] = sprintf("%05x", ID); + * ID is autobind by kernel during bind(see linux/net/unix/af_unix.c) + * ! Since it depends on the unix socket implementations of Linux, be careful when porting to other operating systems. + * + * ID=50000-59999 is duplicated in Server and Client, + * but generated it according to the above rules when sent in the RPClib (see rpc_udp.c) + * + * Because file deletion is leaked when the system is forcibly terminated and abnormal process termination + * by a traditional way to create and bind files under /tmp/RPC/, + * change to the above method(2009.02.04,2012.01.21) + */ + memset(&sa_un, 0, sizeof(sa_un)); + sa_un.sun_family = AF_UNIX; + if (dispatch != NULL) { // server + RpcSetServerName(sa_un.sun_path, id); + sa_len = sizeof(sa_un.sun_family) + SOCK_NAME_LEN; + } else { // client + // Automatically assign name (ID) by unix_autobind() + sa_len = sizeof(sa_un.sun_family); + } + +#else /* !AUTOBIND */ + int sock_un = -1; + struct sockaddr_un sa_un; + sa_un.sun_family = AF_UNIX; + rpc_set_socket_name(sa_un.sun_path, id); + sock_un = socket(PF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); + if (sock_un < 0) { + RPC_LOG_PERROR("socket(unix)"); + goto error; + } + SET_NONBLOCK(sock_un); + SET_CLOSE_ON_EXEC(sock_un); + unlink(sa_un.sun_path); + sa_len = sizeof(sa_un); +#endif /* !AUTOBIND */ + if (bind(sock_un, (struct sockaddr *)&sa_un, sa_len) < 0) { + RPC_LOG_PERROR("DGRAM : bind(unix), ID:%#x", id); + goto error; + } + +#if defined(RPC_USE_UNIX_AUTOBIND) + if (dispatch == NULL) { // client + // Retrieves the assigned name (ID) + socklen_t len = sizeof(sa_un); + if (getsockname(sock_un, (struct sockaddr *)&sa_un, &len) < 0) { + perror("getsockname"); + goto error; + } + RpcGetClientName(sa_un.sun_path, &id); + if (RpcCheckIdConflict(th, id)) { // LCOV_EXCL_BR_LINE 8: dead code, RpcCheckIdConflict always is false + // LCOV_EXCL_START 8: dead code, RpcCheckIdConflict always is false + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + RPC_LOG_STATE("ID %d conflicts with server -- get next one", id); + close(sock_un); + goto get_id_retry; + } // LCOV_EXCL_STOP + RPC_LOG_DEBUG("client %s", sa_un.sun_path + 1); + } +#endif /* AUTOBIND */ + + id_info->port = id; + id_info->sock = sock_un; + + if (dispatch != NULL) { /* server */ + rpc_assert(th->srvr_id == NULL); // LCOV_EXCL_BR_LINE 6: double check + RpcApicallInfo *apicall = rpc_malloc(sizeof(*apicall)); + if (apicall == NULL) { // LCOV_EXCL_START 5: fail safe for libc malloc + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + goto error; + } // LCOV_EXCL_STOP + + /* Create Socket for Authentication */ + socklen_t secure_sa_len; + struct sockaddr_un secure_sa_un; + + secure_sock_un = socket(PF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); /* stream socket for authentication */ + if (secure_sock_un < 0) { + RPC_LOG_PERROR("socket"); + rpc_free(apicall); + apicall = NULL; + goto error; + } + SET_NONBLOCK(secure_sock_un); + SET_CLOSE_ON_EXEC(secure_sock_un); + + memset(&secure_sa_un, 0, sizeof(secure_sa_un)); + secure_sa_un.sun_family = AF_UNIX; + RpcSetServerSecureName(secure_sa_un.sun_path, id); + secure_sa_len = sizeof(secure_sa_un.sun_family) + SECURE_SOCK_NAME_LEN; + + /* Bind socket for authentication */ + if (bind(secure_sock_un, (struct sockaddr *)&secure_sa_un, secure_sa_len) < 0) { + RPC_LOG_PERROR("STREAM : bind(unix), ID:%#x", id); + rpc_free(apicall); + apicall = NULL; + goto error; + } + + id_info->secure_sock = secure_sock_un; + + memset(apicall, 0, sizeof(*apicall)); + apicall->dispatch_func = dispatch; + apicall->pipe_sub_main[PIPE_READ] = -1; + apicall->pipe_sub_main[PIPE_WRITE] = -1; + apicall->timeout_sec = 30; /* Server API processing timeout */ + apicall->secure_check = secure_check; /* Authentication check by UID list */ + if (NEED_SECURE_CHECK == secure_check) { /* Initializes the UID list with not-registered if secured given. */ + apicall->regist_credential_info = NO_REGISTERED; + } + apicall->sock_info_head = NULL; /* Leading Node of Source Client Socket Info */ + apicall->client_sock_name_num = 0; /* Number of elements in the source client's socket name list */ + apicall->in_process_client = RPC_NO_PORT; /* Client RPC_ID during API processing */ + + id_info->apicall = apicall; + th->srvr_id = id_info; + + /* Creating a pipe for communication pipe from sub-thread to main-thread direction */ + if (pipe(apicall->pipe_sub_main) != 0) { + RPC_LOG_PERROR("pipe"); + goto error; + } + SET_NONBLOCK(apicall->pipe_sub_main[PIPE_READ]); + SET_CLOSE_ON_EXEC(apicall->pipe_sub_main[PIPE_READ]); + SET_NONBLOCK(apicall->pipe_sub_main[PIPE_WRITE]); + SET_CLOSE_ON_EXEC(apicall->pipe_sub_main[PIPE_WRITE]); + + PROCESS_MUTEX_LOCK; + if (g_rpc_thread == RPC_NO_THREAD) { /* There are no sub-threads. */ + /* Creating a pipe for communication from main-thread to sub-thread direction */ + if (pipe(g_rpc_pipe_main_sub) != 0) { + RPC_LOG_PERROR("pipe"); + PROCESS_MUTEX_UNLOCK; + goto error; + } + + SET_NONBLOCK(g_rpc_pipe_main_sub[PIPE_READ]); + SET_CLOSE_ON_EXEC(g_rpc_pipe_main_sub[PIPE_READ]); + SET_NONBLOCK(g_rpc_pipe_main_sub[PIPE_WRITE]); + SET_CLOSE_ON_EXEC(g_rpc_pipe_main_sub[PIPE_WRITE]); + + /* Creating sub-thread */ + pthread_t read_th; + if (pthread_create(&read_th, NULL, RpcThreadMain, 0) != 0) { + RPC_LOG_PERROR("pthread_create"); + PROCESS_MUTEX_UNLOCK; + goto error; + } + g_rpc_thread = read_th; + } + PROCESS_MUTEX_UNLOCK; + + /* Instruct a sub-thread to add and wait for completion */ + NotifyAddServer(th); + // LCOV_EXCL_BR_START 15: macro define in rpc_thread.c + WAIT_FOR_SUB_THREAD((th->srvr_id->thread_info == NULL), + RPC_SUB_THREAD_WAIT_SEC); + // LCOV_EXCL_BR_STOP + rpc_assert(th->srvr_id->thread_info != NULL); // LCOV_EXCL_BR_LINE 6: double check + + } else { /* dispatch == NULL => client */ + id_info->count = 1; + id_info->thread_info = th; + th->clnt_id = id_info; + } + return 0; /* pgr0524 */ + +error: + if (g_rpc_pipe_main_sub[PIPE_READ] >= 0) { // LCOV_EXCL_BR_LINE 5: fail safe for libc socket + close(g_rpc_pipe_main_sub[PIPE_READ]); + } + if (g_rpc_pipe_main_sub[PIPE_WRITE] >= 0) { // LCOV_EXCL_BR_LINE 5: fail safe for libc socket + close(g_rpc_pipe_main_sub[PIPE_WRITE]); + } + if (id_info->apicall != NULL) { + if (id_info->apicall->pipe_sub_main[PIPE_READ] >= 0) { + close(id_info->apicall->pipe_sub_main[PIPE_READ]); + } + if (id_info->apicall->pipe_sub_main[PIPE_WRITE] >= 0) { + close(id_info->apicall->pipe_sub_main[PIPE_WRITE]); + } + rpc_free(id_info->apicall); + } + if (sock_un != -1) { + close(sock_un); +#if !defined(RPC_USE_UNIX_AUTOBIND) + unlink(sa_un.sun_path); +#endif /* !AUTOBIND */ + } + if (secure_sock_un != -1) { + close(secure_sock_un); +#if !defined(RPC_USE_UNIX_AUTOBIND) + unlink(secure_sa_un.sun_path); +#endif /* !AUTOBIND */ + } + rpc_free(id_info); + th->srvr_id = NULL; + return -1; /* pgr0524 */ +} + +/* + * Notify an unfinished request of an error at server termination (RPC_end). + */ + static void +RpcSendErrorToPendingRequest(RpcThreadInfo *th, RpcIdInfo *idinfo) { + UINT16 api_num; + RPC_ID client; + char *args_string; + unsigned int args_size; + rpc_send_buf sendbuf; + char retcode[10]; + + do { + api_num = RpcGetAPIRequest(idinfo, &client, + &args_string, &args_size); + if (api_num > 0) { /* API calls are queued */ + + sprintf(retcode, "%08x ", RPC_ERR_Fatal); + sendbuf.buf = retcode; + sendbuf.bytes = sizeof(retcode) - 1; + RpcSendUdp2(idinfo, client, RPC_SEND_TO_CLIENT, + RPC_PACKET_APIRETURN, 1, &sendbuf); + RPC_LOG_STATE("sent error result to pending client %05x", client); + RpcFreeAPIArgsString(args_string); + } + } while(api_num > 0); +} + +/* + * Notify unfinished request of deadlock when deadlock of the server is detected. + */ + static void +RpcSendDeadlockToPendingRequest(RpcThreadInfo *th, RpcIdInfo *idinfo) { + UINT16 api_num; + RPC_ID client; + char *args_string; + unsigned int args_size; + rpc_send_buf sendbuf; + char retcode[10]; + + do { + api_num = RpcGetAPIRequest(idinfo, &client, + &args_string, &args_size); + if (api_num > 0) { /* API calls are queued */ + + sprintf(retcode, "%08x ", RPC_ERR_Server_DeadLock); + sendbuf.buf = retcode; + sendbuf.bytes = sizeof(retcode) - 1; + RpcSendUdp2(idinfo, client, RPC_SEND_TO_CLIENT, + RPC_PACKET_APIRETURN, 1, &sendbuf); + RPC_LOG_STATE("sent deadlock result to pending client %05x", client); + RpcFreeAPIArgsString(args_string); + } + } while(api_num > 0); + + if (RPC_NO_PORT != RPC_apicall_in_process_client(idinfo)) { + sprintf(retcode, "%08x ", RPC_ERR_Server_DeadLock); + sendbuf.buf = retcode; + sendbuf.bytes = sizeof(retcode) - 1; + RpcSendUdp2(idinfo, RPC_apicall_in_process_client(idinfo), RPC_SEND_TO_CLIENT, + RPC_PACKET_APIRETURN, 1, &sendbuf); + RPC_LOG_STATE("sent deadlock result to pending client %05x", RPC_apicall_in_process_client(idinfo)); + RPC_apicall_in_process_client(idinfo) = RPC_NO_PORT; + } +} + +/** Delete RPC_ID Info by RPC_end() + * + * - Main: Notify sub of deletion of RPC_ID info. + * - Sub: Delete a pointer from thread info (RpcThreadInfo) and notify main of that. + * - Main waits for this procedure to finish. + * Use id_info->thread_info to determine completion (completed if NULL). + * Then, release the memory related to RPC_ID info and close the socket. + */ +RUNS_IN_CALLERS_THREAD + void +RpcDestroyIdInfo(RpcThreadInfo *th, RpcIdInfo *id_info) { + rpc_assert(id_info->count == 0); // LCOV_EXCL_BR_LINE 6: double check + if (id_info->apicall) { + if (g_rpc_thread_alive != 0) { + NotifyRemoveServer(th); + /* Wait for a sub-thread to recognize IDinfo deletion */ + // LCOV_EXCL_BR_LINE 15: macro define in rpc_thread.c + WAIT_FOR_SUB_THREAD((th->srvr_id->thread_info != NULL), + RPC_SUB_THREAD_WAIT_SEC); + // LCOV_EXCL_BR_STOP + rpc_assert(th->srvr_id->thread_info == NULL); /* not recognized yet */ // LCOV_EXCL_BR_LINE 6: double check + } + + if (id_info->apicall->pipe_sub_main[PIPE_READ] >= 0) { + close(id_info->apicall->pipe_sub_main[PIPE_READ]); + } + if (id_info->apicall->pipe_sub_main[PIPE_WRITE] >= 0) { + close(id_info->apicall->pipe_sub_main[PIPE_WRITE]); + } + if (id_info->secure_sock >= 0) { + close(id_info->secure_sock); + } + rpc_free(id_info->apicall); + th->srvr_id = NULL; + } else { + th->clnt_id = NULL; + } + rpc_free(id_info); +} + +RUNS_IN_CALLERS_THREAD + void +RpcDestroyThreadInfo(void) { + unsigned int i; + RpcThreadInfo *th = RpcMyThreadInfo(); + if (th == NULL) { + return; + } + + RPC_THREAD_MUTEX_LOCK(th); + if (th->thread == RPC_NO_THREAD) { // LCOV_EXCL_BR_LINE 6: double check + RPC_THREAD_MUTEX_UNLOCK(th); // LCOV_EXCL_START 6: double check + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + return; + } // LCOV_EXCL_STOP + th->thread = RPC_NO_THREAD; + RPC_THREAD_MUTEX_UNLOCK(th); + + PROCESS_MUTEX_LOCK; + /* + * Remove the pointer from the global variable. + * Subsequent calls to RpcMyThreadInfo() return NULL + */ + for(i = 0; i < RPC_MAX_THREADS_IN_PROCESS ; i++) { + if (th == Thread_info[i]) { + Thread_info[i] = NULL; + Num_thread_info--; + break; + } + } + PROCESS_MUTEX_UNLOCK; + BUG_ASSERT(i < RPC_MAX_THREADS_IN_PROCESS, "No info in Thread_info[]"); // LCOV_EXCL_BR_LINE 15: marco defined in rpc_internal.h + + if (Num_thread_info == 0 && g_rpc_thread_alive != 0) { + KillRpcThread(); + char name[32]; + prctl(PR_GET_NAME, name); + RPC_LOG_DEBUG("[%s]waiting for sub thread to join...", name); + pthread_join(g_rpc_thread, NULL); + RPC_LOG_DEBUG("[%s]sub thread joined.", name); + g_rpc_thread = RPC_NO_THREAD; /* bug fix */ + rpc_assert(g_rpc_thread_alive == 0); // LCOV_EXCL_BR_LINE 6: double check + close(g_rpc_pipe_main_sub[PIPE_READ]); + close(g_rpc_pipe_main_sub[PIPE_WRITE]); + } + + if (th->srvr_id != NULL) { // LCOV_EXCL_BR_LINE 6: double check + RpcDestroyIdInfo(th, th->srvr_id); // LCOV_EXCL_START 6: double check + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + th->srvr_id = NULL; + } // LCOV_EXCL_STOP + if (th->clnt_id != NULL) { // LCOV_EXCL_BR_LINE 6: double check + RpcDestroyIdInfo(th, th->clnt_id); // LCOV_EXCL_START 6: double check + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + th->clnt_id = NULL; + } // LCOV_EXCL_STOP + + pthread_mutex_destroy(&(th->th_mtx)); + rpc_free(th); +} + +#if !defined(RPC_USE_UNIX_AUTOBIND) +/** + * Sub-function of RPC_end_all() + * + * Assuming releasing memory and closing the socket are processing at immediately after the end of the process, + * Suppress socket file leaks by only deleting socket files because to avoid deadlocks and shorten the time + * by exclusive processing. + */ + void +RpcUnlinkSocketFiles(void) { + int i; + char sock_name[16]; + + for(i = 0; i < RPC_MAX_THREADS_IN_PROCESS ; i++) { + RpcThreadInfo *th = Thread_info[i]; + if (th != NULL) { + if (th->srvr_id != NULL) { + rpc_set_socket_name(sock_name, RPC_port(th->srvr_id)); + RPC_LOG_STATE("unlink srvr %s", sock_name); + unlink(sock_name); + } + if (th->clnt_id != NULL) { + rpc_set_socket_name(sock_name, RPC_port(th->clnt_id)); + RPC_LOG_STATE("unlink clnt %s", sock_name); + unlink(sock_name); + } + } + } +} +#endif /* !AUTOBIND */ + +/* + * Deadlock detection check for servers in the thread + */ + static void +RpcDeadlockCheck(RpcThreadInfo** thread_info, unsigned int num_thread_info) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + + unsigned int i = 0; + for(i = 0 ; i < num_thread_info ; i++) { + RpcThreadInfo *th = thread_info[i]; + RpcIdInfo *idinfo = th->srvr_id; + CL_MonitorEntry_t entry; + + if (0 == CL_MonitorGetEntry(CL_MONITOR_TYPE_RPC, RPC_port(idinfo), &entry)) { + if (entry.state == CL_MONITOR_STATE_RUN && entry.timeout <= ts.tv_sec) { + RPC_LOG_ERR("Time Out : RPC_ID = %#x API_NUM = %#x", entry.id, entry.user_data); + fprintf(stderr, "Time Out : RPC_ID = %#x API_NUM = %#x\n", entry.id, entry.user_data); + RpcSendDeadlockToPendingRequest(th, idinfo); + } + } + } +} +/** Main functions of the sub-threads (READING THREAD) +*/ +RUNS_IN_READING_THREAD + static void * +RpcThreadMain(void *ptr __attribute__((unused))) { + struct pollfd wait_files[RPC_MAX_FD_IN_PROCESS]; + RpcThreadInfo *thread_info[RPC_MAX_THREADS_IN_PROCESS]; + unsigned int num_thread_info = 0; + + unsigned int poll_num; + int need_reset_sockfd = 1; + int normal_exit = 0; + RPC_Result result; + unsigned int i, j; + + /* Monitoring for clients process with inotify() *//* Monitoring target filename */ + const int inotify_fd = inotify_init1(IN_CLOEXEC); /* fd for process monitoring with inotify() */ + + UINT8 readbuf[RPC_UDP_PACKET_SIZE]; + memset(readbuf, 0, sizeof(UINT8) * RPC_UDP_PACKET_SIZE); + + g_rpc_thread_alive = 1; + + CL_MonitorInit(CL_MONITOR_INIT_USER); /* Using the API for Error Monitoring */ + + // Name the thread created in the RPClib (append "_R") +#define RPC_APPEND_NAME "_R" +#ifndef PRF_SIZE_PROCESSNAME +#define PRF_SIZE_PROCESSNAME 8 /* Limit name length for Profiler Analysis Tools */ +#endif + { + char *p, name[32]; + prctl(PR_GET_NAME, name); + name[PRF_SIZE_PROCESSNAME] = '\0'; + if (strlen(name) + strlen(RPC_APPEND_NAME) > PRF_SIZE_PROCESSNAME) { + p = name + PRF_SIZE_PROCESSNAME - strlen(RPC_APPEND_NAME); + } else { + p = name + strlen(name); + } + strcpy(p, RPC_APPEND_NAME); + prctl(PR_SET_NAME, name); + } + + /* Set the communication pipe with the main thread to poll fd */ + poll_num = 1; + wait_files[0].fd = g_rpc_pipe_main_sub[PIPE_READ]; + wait_files[0].events = POLLIN; + +restart: + for( ; ; ) { + if (need_reset_sockfd) { + /* Set the UDP socket of each RPC_ID to poll fd */ + PROCESS_MUTEX_LOCK; + for(i = 0, j = 0 ; i < RPC_MAX_THREADS_IN_PROCESS ; i++) { + if (Thread_info[i] != NULL) { + if (Thread_info[i]->magic != RPC_MAGIC_ID) { // LCOV_EXCL_BR_LINE 6: double check + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + RPC_LOG_ERR("Someone(me?) destroyed my area!"); // LCOV_EXCL_LINE 6: double check + } + if (Thread_info[i]->srvr_id != NULL + && Thread_info[i]->srvr_id->thread_info != NULL) { + thread_info[j] = Thread_info[i]; + j++; + } + } + } + PROCESS_MUTEX_UNLOCK; + num_thread_info = j; + + poll_num = 1; + /* Register fd for monitoring with inotify() in poll() */ + wait_files[1].fd = inotify_fd; + wait_files[1].events = POLLIN; + + poll_num = 2; + for(i = 0 ; i < num_thread_info ; i++) { + /* Datagram socket for API request */ + wait_files[poll_num].fd = thread_info[i]->srvr_id->sock; /* pgr0000 */ + wait_files[poll_num].events = POLLIN; + poll_num++; + // LCOV_EXCL_BR_START 5: fail safe for libc listen + /* Authentication stream socket */ + if (0 != listen(thread_info[i]->srvr_id->secure_sock, 10)) { /* Number of queues */ + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + RPC_LOG_PERROR("listen(unix)"); // LCOV_EXCL_LINE 5: fail safe for libc listen + } + // LCOV_EXCL_BR_STOP + wait_files[poll_num].fd = thread_info[i]->srvr_id->secure_sock; + wait_files[poll_num].events = POLLIN; + poll_num++; + } + need_reset_sockfd = 0; + } + + int pollret; + pollret = poll(wait_files, poll_num, TIMEOUT_FOR_DEADLOCK_CHECK); + + int save_errno = errno; + + RpcDeadlockCheck(thread_info, num_thread_info); + if (pollret < 0) { // LCOV_EXCL_BR_LINE 5: fail safe for libc poll + if (save_errno == EINTR) { // LCOV_EXCL_START 5: fail safe for libc poll + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + continue; + } else { + rpc_assert(pollret > 0); + goto exit_read_thread; + } // LCOV_EXCL_STOP + } + + /* Commands from the main thread (via pipe) */ + if ((wait_files[0].revents & POLLIN) == POLLIN) { + char buf[RPC_MAIN_SUB_COMMAND_SIZE]; + long ret, arg; + int cmd; + RpcThreadInfo *th; + ret = read(wait_files[0].fd, buf, sizeof(buf)); + if (ret == sizeof(buf)) { + sscanf(buf, RPC_MAIN_SUB_COMMANDs, &cmd, &arg); + switch(cmd) { + case RPC_COMMAND_ADD_SERVER: + th = (RpcThreadInfo *)arg; + th->srvr_id->thread_info = th; /* Indicate the completion of the processing */ + NotifyMainThread(th); + need_reset_sockfd = 1; + goto restart; + break; + + case RPC_COMMAND_REMOVE_SERVER: + th = (RpcThreadInfo *)arg; + RpcSendErrorToPendingRequest(th, th->srvr_id); + + RpcAllDeleteSockName(th->srvr_id, inotify_fd); /* delete client_sock_name_list */ + rpc_free((th->srvr_id)->apicall->uid_list); /* delete uid_list */ + rpc_free((th->srvr_id)->apicall->gid_list); /* delete gid_list */ + + th->srvr_id->thread_info = NULL;/* Indicate the completion of the processing */ + NotifyMainThread(th); + need_reset_sockfd = 1; + goto restart; + break; + + case RPC_COMMAND_EXIT: + /************ Termination request from the parent thread *************/ + RPC_LOG_DEBUG("Received exit command from main thread."); + normal_exit = 1; + goto exit_read_thread; + break; + } /* switch */ + } /* if (ret == sizeof(buf)) */ + } /* Complete the processing of commands from the main thread */ + + /* Client Monitoring Events with inotify() */ + if ((wait_files[1].revents & POLLIN) == POLLIN) { + UINT32 read_len = 0; + int length = 0; + char *buffer; + buffer = (char *)rpc_malloc(BUF_LEN); + if (NULL == buffer) {// LCOV_EXCL_START 5: fail safe for libc malloc + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + RPC_LOG_ERR("rpc_malloc() ERROR."); + goto exit_read_thread; + } // LCOV_EXCL_STOP + + if( (length = (int)read( inotify_fd, buffer, BUF_LEN ) ) < 0 ) { // LCOV_EXCL_START 5: fail safe for libc read + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + RPC_LOG_PERROR("inotify read() ERROR."); + rpc_free(buffer); + buffer = NULL; + goto exit_read_thread; + } // LCOV_EXCL_STOP + while ( read_len < length ) { + struct inotify_event *event = ( struct inotify_event * )&buffer[read_len]; + + if ( event->mask & IN_DELETE_SELF ) {/* Terminating a Client Process */ + int i; /* Looping variable */ + + /* Delete the source socket name from all RpcThreadInfo in the received thread */ + for(i = 0 ; i < num_thread_info ; i++) { + RpcThreadInfo *th = thread_info[i]; + RpcDeleteSockName(th->srvr_id, event->wd); + } + } + read_len += (UINT32)(EVENT_SIZE + event->len); /* Size of the inotify_event structure */ + } + rpc_free(buffer); + goto restart; + } /* Client Monitoring Events Completed with inotify() */ + + for(i = 2 ; i < poll_num ; i++) { + /* Event to the API request datagram socket */ + if ((i % 2 == 0) && ((wait_files[i].revents & POLLIN) == POLLIN)) { + unsigned int thread_info_num = ((i/2) - 1); /* Compute thread_info[thread_info_num] with events */ + RpcThreadInfo *th = thread_info[thread_info_num]; /* pgr0000 */ + RpcIdInfo *idinfo = th->srvr_id; + for(;;) { + /* RPClib packet received */ + int readret = RpcReadUdpPacket(idinfo, readbuf); + if (readret < 0) { // LCOV_EXCL_BR_LINE 5: fail safe for libc recvfrom + rpc_assert(readret >= 0); // LCOV_EXCL_START 5: fail safe for libc recvfrom + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + goto exit_read_thread; + // LCOV_EXCL_STOP + } else if (readret == 0) { + break; + } + /* successfully read udp packets */ + /* parse the packet and queue events */ + RPC_ID sender = RPC_NO_ID; + UINT32 seq_num = 0; + UINT32 size = 0; + RPC_packet_type command = RPC_PACKET_NONE; + if (RpcParsePacketHeader((const char *)readbuf, &command, &sender, &seq_num, &size) != RPC_OK) { // LCOV_EXCL_BR_LINE 11: Unexpected branch // NOLINT(readability/nolint) + goto exit_read_thread; + } + + long int api_num; + char *buff; + + switch(command) { + case RPC_PACKET_APICALL: + if (RPC_DEBUG != NULL) { // LCOV_EXCL_BR_LINE 7: debug + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + printf("RPC[%s]: received APIcall\n", RPC_DEBUG); // LCOV_EXCL_LINE 7: debug + } + // Return response without queuing for ALIVE query + api_num = strtol((const char *)(readbuf + RPC_PACKET_HEADER_LEN), NULL, 10); + if (api_num == RPC_API_NUM_RPC_ALIVE) { + RpcSendUdpResponse(idinfo, sender, RPC_SEND_TO_CLIENT, + RPC_RESPONSE_APICALL, + seq_num, "OK", sizeof("OK")); + break; + } + + /* Return BUSY if secure and unregistered in UID-list */ + if ((NEED_SECURE_CHECK == RPC_secure_check(idinfo)) + && (NO_REGISTERED == RPC_regist_credential_info(idinfo))) { + RpcSendUdpResponse(idinfo, sender, RPC_SEND_TO_CLIENT, + RPC_RESPONSE_APICALL, + seq_num, "BUSY", sizeof("BUSY")); + RPC_LOG_ERR("Need UID list register."); + break; + } + + result = RpcQueueAPIRequestBefore(idinfo, size, (char**)&buff); + if (result == RPC_OK) { + /* Check whether the source has been authenticated */ + if(RPC_OK == RpcCheckSockName(idinfo, sender)) { /* Registerd the name of the source socket */ + RpcSendUdpResponse(idinfo, sender, RPC_SEND_TO_CLIENT, + RPC_RESPONSE_APICALL, + seq_num, "OK", sizeof("OK")); + RpcQueueAPIRequestAfter(idinfo, sender, + (const char *)(readbuf + RPC_PACKET_HEADER_LEN), + size, buff); + } else { /* Not registered (in other words, first communication with the source client) */ + /* Authentication request to the client */ + RPC_THREAD_MUTEX_UNLOCK(idinfo->thread_info); + RpcSendUdpResponse(idinfo, sender, RPC_SEND_TO_CLIENT, + RPC_RESPONSE_APICALL, + seq_num, "CERT", sizeof("CERT")); + rpc_free(buff); + break; + } + } else if (result == RPC_ERR_Busy) { // LCOV_EXCL_BR_LINE 5: fail safe for libc malloc + RpcSendUdpResponse(idinfo, sender, RPC_SEND_TO_CLIENT, + RPC_RESPONSE_APICALL, + seq_num, "BUSY", sizeof("BUSY")); + } else { // LCOV_EXCL_START 5: fail safe for libc malloc + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + RpcSendUdpResponse(idinfo, sender, RPC_SEND_TO_CLIENT, + RPC_RESPONSE_APICALL, + seq_num, "ERR", sizeof("ERR")); + RPC_LOG_ERR("queueing APIcall failed.(%d)", result); + goto exit_read_thread; + } // LCOV_EXCL_STOP + NotifyMainThread(th); + if (RPC_DEBUG != NULL) { // LCOV_EXCL_BR_LINE 7: debug + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + printf("RPC[%s]: notified APIcall\n", RPC_DEBUG); // LCOV_EXCL_LINE 7: debug + } + break; + + default: + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + BUG_ASSERT(0, "Unknown UDP packet type"); // LCOV_EXCL_LINE 15: marco defined in rpc_internal.h + goto exit_read_thread; + break; + } /* switch(command) */ + }/* for(;;) */ + + /* Event to the stream socket for authentication */ + } else if ((i % 2 != 0) && ((wait_files[i].revents & POLLIN) == POLLIN)) { + unsigned int thread_info_num = ((i-1)/2 - 1); /* Compute thread_info[thread_info_num] with events */ + struct sockaddr_un client_sa_un; + socklen_t client_len = sizeof(struct sockaddr_un); + struct ucred cr; /* Structure of client credit info */ + RpcCertifyResult send_ret; /* Authentication result to pass to the client */ + RpcThreadInfo *th = thread_info[thread_info_num]; + RpcIdInfo *idinfo = th->srvr_id; + + send_ret.certify_res = CERTIFY_NG; + send_ret.srvr_pid = 0; + + /* Obtain client credit info from a connected socket */ + int accept_sock = accept4(wait_files[i].fd, (struct sockaddr *)&client_sa_un, &client_len, SOCK_CLOEXEC); + int ret = getsockopt(accept_sock, SOL_SOCKET, SO_PEERCRED, &cr, &client_len); + if (ret == 0) { // LCOV_EXCL_BR_LINE 5: fail safe for libc getsockopt + client_sa_un = (struct sockaddr_un )client_sa_un; + /* Check if UID of client is allowed to communicate */ + if (RPC_OK == RpcCheckClientCredential(idinfo, &cr)) { + + /* Obtain the socket name associated with the RPC_ID of the client from the socket info */ + char client_sock_name[SOCK_NAME_LEN]; + RpcGetClientNameFromSock(client_sa_un.sun_path, client_sock_name); + + /* Monitoring client processes with inotify */ + char intfy_fname[32]; + snprintf(intfy_fname, sizeof(intfy_fname), CL_INTFY_FILENAME_FORMAT, cr.pid); + int wd = inotify_add_watch(inotify_fd, intfy_fname, IN_DELETE_SELF); + if (0 > wd) { // LCOV_EXCL_BR_LINE 5: fail safe for libc inotify_add_watch + RPC_LOG_STATE("intfy_fname is Not Found [%s].", intfy_fname); + } + + /* Register the source socket name in the management table */ + RpcRegistSockName(idinfo, client_sock_name, &cr, wd); + + /* Send server credit info to the client */ + send_ret.certify_res = CERTIFY_OK; + send_ret.srvr_pid = getpid(); + } + } + + /* Send authentication result to client */ + send(accept_sock, (char*)&send_ret, sizeof(RpcCertifyResult), 0); + close(accept_sock); + + goto restart; + + } else if ((wait_files[i].revents & ~POLLIN) != 0) { // LCOV_EXCL_START 5: fail safe for libc poll + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + // POLLERR, etc. on UDP port + RPC_LOG_STATE("poll error %x", wait_files[i].revents); + + if ((wait_files[i].revents & POLLNVAL) == POLLNVAL) { + need_reset_sockfd = 1; + goto restart; + } + } /* if ((wait_files[i].revents & POLLIN) == POLLIN) */ // LCOV_EXCL_STOP + + } /* processing UDP packets finished */ + + } /* end of forever loop */ + +exit_read_thread: + + g_rpc_thread_alive = 0; + for(i = 0 ; i < num_thread_info ; i++) { // LCOV_EXCL_BR_LINE 6: double check + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + NotifyMainThread(thread_info[i]); /* pgr0000 */ // LCOV_EXCL_LINE 6: double check + } + + close(inotify_fd); + + if (normal_exit == 0) { // LCOV_EXCL_BR_LINE 6: double check + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + RPC_LOG_CRIT("sub thread ABNORMALLY exited."); // LCOV_EXCL_LINE 6: double check + } else { + RPC_LOG_DEBUG("sub thread normally exited."); + } + return NULL; +} + +/* Notification of sub-thread -> main-thread (via pipe) */ +RUNS_IN_READING_THREAD + static void +NotifyMainThread(RpcThreadInfo *th) { + rpc_assert(th->srvr_id->apicall != NULL); // LCOV_EXCL_BR_LINE 6: double check + char c = 0; + write(th->srvr_id->apicall->pipe_sub_main[PIPE_WRITE], &c, sizeof(c)); +} + +/* Notification of main-thread -> sub-thread(via pipe) */ +/* Termination instruction */ +RUNS_IN_CALLERS_THREAD + static void +KillRpcThread(void) { + char buf[RPC_MAIN_SUB_COMMAND_SIZE]; + sprintf(buf, RPC_MAIN_SUB_COMMAND, RPC_COMMAND_EXIT, (unsigned long)0); + write(g_rpc_pipe_main_sub[PIPE_WRITE], buf, sizeof(buf)); +} + +/* AddRPC_ID */ +RUNS_IN_CALLERS_THREAD + static void +NotifyAddServer(RpcThreadInfo *th) { + char buf[RPC_MAIN_SUB_COMMAND_SIZE]; + sprintf(buf, RPC_MAIN_SUB_COMMAND, RPC_COMMAND_ADD_SERVER, + (unsigned long)th); + write(g_rpc_pipe_main_sub[PIPE_WRITE], buf, sizeof(buf)); +} + +/* Remove RPC_ID */ +RUNS_IN_CALLERS_THREAD + static void +NotifyRemoveServer(RpcThreadInfo *th) { + char buf[RPC_MAIN_SUB_COMMAND_SIZE]; + sprintf(buf, RPC_MAIN_SUB_COMMAND, RPC_COMMAND_REMOVE_SERVER, + (unsigned long)th); + write(g_rpc_pipe_main_sub[PIPE_WRITE], buf, sizeof(buf)); +} + +/* Register the socket name of the source client in the management table. */ + static RPC_Result +RpcRegistSockName(RpcIdInfo *idinfo, char *client_sock_name, const struct ucred *cr, int wd) { + if ((NULL == idinfo) || (NULL == client_sock_name) || (NULL == cr) || (0 > cr->pid)) { // LCOV_EXCL_BR_LINE 6: void *RpcThreadMain(void *ptr) + RPC_LOG_ERR("RpcRegistSockName() : Invalid Param."); // LCOV_EXCL_START 6: void *RpcThreadMain(void *ptr) + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + return RPC_ERR_Fatal; + } // LCOV_EXCL_STOP + + RpcClientSockNameInfo *sock_name_buf, *current; + + sock_name_buf = rpc_malloc(sizeof(RpcClientSockNameInfo)); + // LCOV_EXCL_BR_START 5: fail safe for libc malloc + if( sock_name_buf == NULL ){ + return RPC_ERR_Fatal; + } + // LCOV_EXCL_BR_STOP + strcpy(sock_name_buf->client_sock_name, client_sock_name); /* Socket name */ + sock_name_buf->pid = cr->pid; /* PID */ + sock_name_buf->uid = cr->uid; /* UID */ + sock_name_buf->gid = cr->gid; /* GID */ + sock_name_buf->wd = wd; /* Non-negative inotify monitored descriptors */ + sock_name_buf->next = NULL; /* Pointer to next node (NULL since last node) */ + + if (0 == RPC_client_sock_name_num(idinfo)) { + RPC_sock_info_head(idinfo) = sock_name_buf; + } else { + for (current = RPC_sock_info_head(idinfo); current->next != NULL; current = current->next) + ; + current->next = sock_name_buf; + } + RPC_client_sock_name_num_inc(idinfo); + return RPC_OK; +} + +/* Check if the socket name of the source client is registered in the management table */ + static RPC_Result +RpcCheckSockName(RpcIdInfo *idinfo, RPC_ID client_id) { + if ((NULL == idinfo) || (client_id == RPC_NO_ID)) { // LCOV_EXCL_BR_LINE 6: void *RpcThreadMain(void *ptr) + RPC_LOG_ERR("RpcCheckSockName() : Invalid Param."); // LCOV_EXCL_START 6: void *RpcThreadMain(void *ptr) + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + return RPC_ERR_Fatal; + } // LCOV_EXCL_STOP + + char buf[7], client_path_name[SOCK_NAME_LEN]; /* Client socket name */ + + /* Converting client_id to the socket name associated with RPC_ID */ + RpcSetClientName(buf, client_id); + memcpy(client_path_name, buf + 1, 5); + client_path_name[5] = '\0'; + RpcClientSockNameInfo *current = RPC_sock_info_head(idinfo); + + /* Search source socket name in management table */ + while (NULL != current) { + if (0 == strcmp(current->client_sock_name, client_path_name)) { /* Registered socket name (authenticated) */ + return RPC_OK; + } + current = current->next; + } + return RPC_ERR_Fatal; /* Not registerd socket name (unauthenticated) */ +} + +/* Remove source client socket name from management table */ + static RPC_Result +RpcDeleteSockName(RpcIdInfo *idinfo, int wd) { + if ((NULL == idinfo) || (0 > wd)) { + RPC_LOG_ERR("RpcDeleteSockName() : Invalid Param."); + return RPC_ERR_Fatal; + } + + RpcClientSockNameInfo *current, *previous; + current = RPC_sock_info_head(idinfo); + previous = current; + int cnt = 0; + + /* Remove Source Socket Name in Management Table */ + while (NULL != current) { + if (wd == current->wd) { /* Delete element */ + if (0 == cnt) { /* Delete the start element in the management table */ + RPC_sock_info_head(idinfo) = RPC_sock_info_head(idinfo)->next; + rpc_free(current); + current = RPC_sock_info_head(idinfo); + cnt = -1; + } else { /* Delete other than the start element in the management table */ + previous->next = current->next; + rpc_free(current); + current = previous->next; + } + RPC_client_sock_name_num_dec(idinfo); + } else { /* Refer to the next node without deleting */ + previous = current; + current = current->next; + } + cnt ++; + } + + return RPC_OK; +} + +/* Remove all source client socket names in the management table */ + static RPC_Result +RpcAllDeleteSockName(RpcIdInfo *idinfo, int inotify_fd) { + if (NULL == idinfo) { // LCOV_EXCL_BR_LINE 6: double check in void RpcDestroyThreadInfo(void) + RPC_LOG_ERR("RpcAllDeleteSockName() : Invalid Param."); // LCOV_EXCL_START 6: void RpcDestroyThreadInfo(void) + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + return RPC_ERR_Fatal; + } // LCOV_EXCL_STOP + + RpcClientSockNameInfo *current = RPC_sock_info_head(idinfo); + + while (NULL != current) { + RpcClientSockNameInfo *previous = current; + current = current->next; + + if (0 <= previous->wd) { + inotify_rm_watch(inotify_fd, previous->wd); + } + + rpc_free(previous); + previous = NULL; + RPC_client_sock_name_num_dec(idinfo); + } + + return RPC_OK; +} + + +/* Check if client is allowed to communicate */ + static RPC_Result +RpcCheckClientCredential(RpcIdInfo *idinfo, const struct ucred *cr) { + if ((NULL == idinfo) || (NULL == cr)) { // LCOV_EXCL_BR_LINE 6: double check in void *RpcThreadMain(void *ptr) + RPC_LOG_ERR("RpcCheckClientCredential() : Invalid Param."); // LCOV_EXCL_START 6: void *RpcThreadMain(void *ptr) + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + return RPC_ERR_Fatal; + } // LCOV_EXCL_STOP + + /* Retern RPC_OK if authentication is not required */ + if (NO_SECURE_CHECK == RPC_secure_check(idinfo)) { + return RPC_OK; + } + + INT32 i = 0; /* Loop counter */ + + /* Search client UID in registered UID list */ + for(i = 0; i < RPC_uid_num(idinfo); i++) { + if(RPC_uid_list(idinfo, i) == cr->uid) { /* Found UID in registered UID list */ + return RPC_OK; + } + } + + /* Search client GID in registered GID list */ + for(i = 0; i < RPC_gid_num(idinfo); i++) { + if(RPC_gid_list(idinfo, i) == cr->gid) { /* Found GID in registered GID list. */ + return RPC_OK; + } + } + + RPC_LOG_ERR("[Client isn't authenticated!!!!]"); + return RPC_ERR_Fatal; /* Not found UID in registered UID list */ +} diff --git a/otherservice/rpc_library/library/src/rpc_udp.c b/otherservice/rpc_library/library/src/rpc_udp.c new file mode 100644 index 00000000..d16569dc --- /dev/null +++ b/otherservice/rpc_library/library/src/rpc_udp.c @@ -0,0 +1,473 @@ +/* + * @copyright Copyright (c) 2016-2019 TOYOTA MOTOR CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file rpc_udp.c + * @brief RPC Library Internal Implementation--UDP Communication + * + */ +/** @addtogroup RPClib_in */ +/** @{ */ +#include +#include +#include +#include +#include +#include +#include +#include +#include // for usleep + +#include +#include + +#include + +#include +#include "rpc_internal.h" + +static /*inline*/ UINT32 +RpcGetSequenceNumber(RpcThreadInfo *th) { + RPC_THREAD_MUTEX_LOCK(th); + UINT32 ret = th->sequence_number; + (th->sequence_number)++; + if (th->sequence_number == RPC_SEQ_NUM_INVALID) { // LCOV_EXCL_BR_LINE 200: overflow check, but test time is too long + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + th->sequence_number = RPC_SEQ_NUM_START; // LCOV_EXCL_LINE 200: overflow check, but test time is too long + } + RPC_THREAD_MUTEX_UNLOCK(th); + return ret; +} + +/** + */ +int +RpcReadUdpPacket(const RpcIdInfo *id, UINT8 *buf) { + struct sockaddr_un sa; + socklen_t sa_len = sizeof(sa); + // sa passed to recvfrom does not require initialization + + for(;;) { + + ssize_t ret = recvfrom(RPC_my_sock(id), buf, RPC_UDP_PACKET_SIZE, + 0, (struct sockaddr *)&sa, &sa_len); + + if (ret < 0) { + if (errno == EAGAIN) { // LCOV_EXCL_BR_LINE 5: fail safe for libc recvfrom + //RPC_LOG_PERROR("recvfrom port %d", RPC_port(id)); + return 0; + } else if (errno == EINTR) { // LCOV_EXCL_START 5: fail safe for libc recvfrom + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + continue; + } else { + RPC_LOG_PERROR("recvfrom port %d", RPC_port(id)); + return -1; + } // LCOV_EXCL_STOP + } else if (ret == 0) { + RPC_LOG_STATE("*** recvfrom ret 0"); + return 0; + } else { + return (int)ret; + } + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + } // LCOV_EXCL_LINE 10: final line +} + +/** Number of sendto retries */ +#define RPC_SENDTO_RETRY 5 + +RUNS_IN_CALLERS_THREAD +RUNS_IN_READING_THREAD +/** + */ +static int +RpcSendUdpPacket(RpcIdInfo *id, + struct sockaddr_un *to, int do_retry, + void *mesg, unsigned int bytes) { + int nretry = 0; + ssize_t ret; + int myerr; + retry: + ret = sendto(RPC_my_sock(id), mesg, bytes, 0, + (struct sockaddr *)to, RPC_SOCKET_ADDR_LEN); + myerr = errno; + + if (ret < 0) { + RPC_LOG_STATE("*** sendto %s ***", to->sun_path + 1); + if (myerr == EAGAIN || (do_retry && myerr==ECONNREFUSED && ++nretry <= RPC_SENDTO_RETRY)) { // LCOV_EXCL_BR_LINE 11: Unexpected branch // NOLINT(readability/nolint) + usleep(100000); +#if defined(RPC_USE_UNIX_AUTOBIND) + RPC_LOG_DEBUG("*** sendto %s ***", to->sun_path + 1); +#else /* AUTOBIND */ + RPC_LOG_DEBUG("**************** sendto retry *********************"); +#endif /* !AUTOBIND */ + goto retry; + } + errno = myerr; +#if defined(RPC_USE_UNIX_AUTOBIND) + if (do_retry) { + RPC_LOG_PERROR("sendto %s", to->sun_path + 1); + } +#endif /* !AUTOBIND */ + return -1; + } else if ((unsigned int)ret != bytes) { + RPC_LOG_STATE("can't send all"); + return -1; + } + return 0; +} + +RUNS_IN_CALLERS_THREAD +RUNS_IN_READING_THREAD +/** + */ +int +RpcSendUdp(RpcIdInfo *id, const RPC_ID receiver, int direction, RPC_packet_type type, const void *mesg, unsigned int bytes) { // LCOV_EXCL_START 8: dead code + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + rpc_send_buf sendbuf; + sendbuf.buf = mesg; + sendbuf.bytes = bytes; + + return RpcSendUdp2(id, receiver, direction, type, 1, &sendbuf); +} // LCOV_EXCL_STOP +RUNS_IN_CALLERS_THREAD +RUNS_IN_READING_THREAD +/** UDP packet transmission process + * + * UDP packet format + * @verbatim ++--------------+--------------+ +|*1|*2|*3|*4|*5| PAYLOAD | ++--------------+--------------+ +@endverbatim + * + * *1 to *5 indicate the header. @see RPC_PACKET_HEADER + * + * - *1: [@%-2d] Type of packet + * - @see RPC_packet_type + * - *2: [@%-5x] Source RPC_ID + * - *3: [@%-8x] Sequence number + * - Set the value incremented from 1. This value must be unique for each source thread. + * - In the case of response packets, this field contains the sequence number of the packet from which the response was received. + * - *4: [@%-4u] Size of send data + * - In the case of sending data consists of single packet in PAYLOAD, this field contains number of the packet. + * - For a multipacket, the first packet contains the sum of the PAYLOAD of all packets, + * the last packet contains the PAYLOAD bytes of the packet. + * All other intermediate packets are filled with 0.(With up to two packets in the current constraint, + * there are no intermediate packets.) + * - *5: [@%1d] Position of packet + * - @see rpc_packet_position + */ +int +RpcSendUdp2(struct RpcIdInfo *id, RPC_ID receiver, int direction, + RPC_packet_type type, unsigned int num, rpc_send_buf *sendbuf) { + unsigned char buf[RPC_UDP_PACKET_SIZE]; + UINT32 seq_num = RpcGetSequenceNumber(id->thread_info); + unsigned int bytes = 0; + rpc_send_buf *sendbufp = sendbuf; + int i, do_retry = 1; + for(i = 0 ; i < num ; i++) { + bytes += sendbufp->bytes; + sendbufp++; + } + rpc_assert(bytes <= RPC_UDP_PAYLOAD); // LCOV_EXCL_BR_LINE 6: double check + + struct sockaddr_un to; + memset(&to, 0, sizeof(to)); + to.sun_family = AF_UNIX; +#if defined(RPC_USE_UNIX_AUTOBIND) + if (direction != RPC_SEND_TO_CLIENT) { + RpcSetServerName(to.sun_path, receiver); + if (direction == RPC_SEND_TO_SERVER_NO_RETRY) { + do_retry = 0; + } + } else { + RpcSetClientName(to.sun_path, receiver); + } +#else /* !AUTOBIND */ + rpc_set_socket_name(to.sun_path, rpc_get_port(receiver)); +#endif /* !AUTOBIND */ + + sprintf((char *)buf, RPC_PACKET_HEADER, + (int)type, RPC_my_id(id), seq_num, bytes, + RPC_PACKET_POS_ONEANDONLY); + + unsigned char *bufp = buf + RPC_PACKET_HEADER_LEN; + for(i = 0 ; i < num ; i++) { + memcpy(bufp, sendbuf->buf, sendbuf->bytes); + bufp += sendbuf->bytes; + sendbuf++; + } + if (RpcSendUdpPacket(id, &to, do_retry, + buf, RPC_PACKET_HEADER_LEN + bytes) < 0) { + return -1; + } + return (int)seq_num; +} + +RUNS_IN_READING_THREAD +/** + */ +RPC_Result +RpcSendUdpResponse(RpcIdInfo *id, const RPC_ID receiver, int direction, + RPC_packet_type type, UINT32 seq_num, + char *mesg, UINT32 bytes) { + rpc_assert(bytes <= RPC_MAX_RESPONSE_MESSAGE_SIZE); // LCOV_EXCL_BR_LINE 6: double check + char buf[RPC_PACKET_HEADER_LEN + RPC_MAX_RESPONSE_MESSAGE_SIZE]; + sprintf(buf, RPC_PACKET_HEADER, + (int)type, RPC_my_id(id), seq_num, bytes, + (int)RPC_PACKET_POS_ONEANDONLY); + memcpy(buf + RPC_PACKET_HEADER_LEN, mesg, bytes); + + struct sockaddr_un sa; + memset(&sa, 0, sizeof(sa)); + sa.sun_family = AF_UNIX; +#if defined(RPC_USE_UNIX_AUTOBIND) + if (direction != RPC_SEND_TO_CLIENT) { + RpcSetServerName(sa.sun_path, receiver); + } else { + RpcSetClientName(sa.sun_path, receiver); + } +#else /* !AUTOBIND */ + rpc_set_socket_name(sa.sun_path, rpc_get_port(receiver)); +#endif /* !AUTOBIND */ + return RpcSendUdpPacket(id, &sa, 0, buf, RPC_PACKET_HEADER_LEN + bytes); +} + +/** + */ +RPC_Result +RpcParsePacketHeader(const char *str, RPC_packet_type *command, + RPC_ID_p id, UINT32 *seq_num, UINT32 *size) { + // LCOV_EXCL_BR_START 6: double check + rpc_assert(str != NULL && command != NULL && id != NULL + && seq_num != NULL && size != NULL); + // LCOV_EXCL_BR_STOP + + if (sscanf(str, RPC_PACKET_HEADER_scanf, (int *)command, id, seq_num, size) != 4) { // LCOV_EXCL_BR_LINE 11: Unexpected branch // NOLINT(readability/nolint) + *command = RPC_PACKET_NONE; // LCOV_EXCL_START 5: fail safe for libc sscanf + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + BUG_ASSERT(0, "Parsing packet failed"); + // LCOV_EXCL_STOP + return RPC_ERR_Fatal; + } + return RPC_OK; +} + +#include + +RPC_Result +RpcClientWaitResponse(RpcIdInfo *idinfo, UINT32 seq_num, + UINT32 timeout_msec, UINT16 *response) { + unsigned char readbuf[RPC_UDP_PACKET_SIZE]; + fd_set fds; + int fd = idinfo->sock; + + struct timeval timeout; + timeout.tv_sec = (__time_t)(timeout_msec / 1000); + timeout.tv_usec = (__suseconds_t)((timeout_msec % 1000) * 1000); + + *response = RPC_RESPONSE_NONE; + + for(;;) { + FD_ZERO(&fds); + FD_SET(fd, &fds); + int sret = select(fd + 1, &fds, NULL, NULL, &timeout); + if (sret < 0 && errno == EINTR) { /* signal interrupt */ // LCOV_EXCL_START 5: fail safe for libc select + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + continue; + // LCOV_EXCL_STOP + } else if (sret == 0) { /* timeout */ + RPC_LOG_ERR("server response timeout"); + return RPC_ERR_No_Response; + } else if (sret > 0 && FD_ISSET(fd, &fds)) { // LCOV_EXCL_BR_LINE 5: fail safe for libc select + RPC_ID sender; + UINT32 seq; + UINT32 size; + RPC_packet_type command; + int readret = RpcReadUdpPacket(idinfo, readbuf); + if (readret <= 0) { // LCOV_EXCL_START 5: fail safe for libc recvfrom + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + /* debug code to see socket status */ + /* why recvfrom() returned 0 ? */ + struct pollfd pfd; + pfd.fd = fd; + pfd.events = POLLIN|POLLHUP|POLLERR; + sret = poll(&pfd, 1, 0/* timeout 0 */); + RPC_LOG_STATE("** poll revents=%x", pfd.revents); + return RPC_ERR_Fatal; + } // LCOV_EXCL_STOP + if (RpcParsePacketHeader((const char *)readbuf, &command, &sender, &seq, &size) != RPC_OK) { // LCOV_EXCL_BR_LINE 11: Unexpected branch // NOLINT(readability/nolint) + return RPC_ERR_Fatal; + } + unsigned char c; + if (seq == seq_num) { + switch(command) { + case RPC_RESPONSE_APICALL: + c = readbuf[RPC_PACKET_HEADER_LEN]; + switch(c) { + case 'O': + *response = RPC_RESPONSE_API_OK; + goto exit_loop_ok; + break; + case 'B': + *response = RPC_RESPONSE_API_BUSY; + goto exit_loop_ok; + break; + case 'E': + *response = RPC_RESPONSE_API_ERR; + goto exit_loop_ok; + break; + case 'D': + *response = RPC_RESPONSE_API_DEADLOCK; + goto exit_loop_ok; + break; + case 'C': + *response = RPC_RESPONSE_API_CERTIFY; + goto exit_loop_ok; + break; + default: + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + BUG_ASSERT(0, "illegal response\n"); // LCOV_EXCL_LINE 15: marco defined in rpc_internal.h + return RPC_ERR_Fatal; + break; + } + break; + + default: + RPC_LOG_STATE("Unknown packet command=%d", command); + return RPC_ERR_Fatal; + break; + } + } else { /* sequence number mismatch == response to other request */ + RPC_LOG_DEBUG("unwanted response received(delayed response?)"); + continue; + } + } else { /* poll error */ + RPC_LOG_PERROR("select in wait response"); + return RPC_ERR_Fatal; + } + } + exit_loop_ok: + return RPC_OK; +} + +RPC_Result +RpcClientWaitResult(RpcIdInfo *idinfo, RPC_ID srvr_id) { + unsigned char readbuf[RPC_UDP_PACKET_SIZE]; + fd_set fds; + int fd = idinfo->sock; + int inotify_fd = RPC_clnt_inotify_fd(idinfo); + int maxfd; + RPC_Result result = RPC_OK; + + for(;;) { + FD_ZERO(&fds); + FD_SET(fd, &fds); + FD_SET(inotify_fd, &fds); + + /* Determine the maximum value of fd to wait */ + if (fd > inotify_fd) { + maxfd = fd; + } else { + maxfd = inotify_fd; + } + + int sret = select(maxfd + 1, &fds, NULL, NULL, NULL); + if (sret < 0 && errno == EINTR) { /* signal interrupt */ // LCOV_EXCL_START 5: fail safe for libc select + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + continue; + // LCOV_EXCL_STOP + } else if (sret > 0 && FD_ISSET(fd, &fds)) { /* success */ // LCOV_EXCL_BR_LINE 5: fail safe for libc select + RPC_ID sender; + UINT32 seq; + UINT32 size; + RPC_packet_type command; + int readret = RpcReadUdpPacket(idinfo, readbuf); + if (readret <= 0) { // LCOV_EXCL_START 5: fail safe for libc recvfrom + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + rpc_assert(readret >= 0); + result = RPC_ERR_Fatal; + goto exit_loop_ok; + } // LCOV_EXCL_STOP + if (RpcParsePacketHeader((const char *)readbuf, &command, &sender, &seq, &size) != RPC_OK) { // LCOV_EXCL_BR_LINE 11: Unexpected branch // NOLINT(readability/nolint) + // LCOV_EXCL_START 5: fail safe for libc sscanf + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + result = RPC_ERR_Fatal; + goto exit_loop_ok; + } // LCOV_EXCL_STOP + switch(command) { + case RPC_PACKET_APIRETURN: + result = RpcSetAPIcallReturn(idinfo, + (const char *)(readbuf + RPC_PACKET_HEADER_LEN), + size); + if(result == RPC_OK) { // LCOV_EXCL_BR_LINE 5: fail safe for libc malloc + if (sscanf((const char *)(readbuf + RPC_PACKET_HEADER_LEN), "%08x ", (unsigned int *)&result) != 1) { // LCOV_EXCL_BR_LINE 5: fail safe for libc sscanf + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + BUG_ASSERT(0, "Parsing packet failed"); // LCOV_EXCL_LINE 15: marco defined in rpc_internal.h + return RPC_ERR_Fatal; + } + } + goto exit_loop_ok; + default: + RPC_LOG_STATE("unwanted packet received while waiting for API return"); + continue; + break; + } + } else if (sret > 0 && FD_ISSET(inotify_fd, &fds)) { /* server process is terminate. */ + UINT32 read_len = 0; + int length = 0; + char *buffer; + buffer = (char *)rpc_malloc(BUF_LEN); + if (NULL == buffer) { // LCOV_EXCL_START 5: fail safe for libc malloc + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + RPC_LOG_ERR("rpc_malloc() ERROR."); + } // LCOV_EXCL_STOP + + if((length = (int)read( inotify_fd, buffer, BUF_LEN )) < 0 ) { // LCOV_EXCL_START 5: fail safe for libc read + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + RPC_LOG_PERROR("inotify read() ERROR."); + } // LCOV_EXCL_STOP + while (read_len < length) { + struct inotify_event *event = ( struct inotify_event * )&buffer[read_len]; + + if (event->mask & IN_DELETE_SELF) { /* Terminating a Server Process */ + if (RPC_ERR_Server_Finish == RpcDeleteSrvrPid(idinfo, srvr_id, event->wd)) { + RPC_LOG_PERROR("server process is terminate. : srvr_ID = %x", srvr_id); + result = RPC_ERR_Fatal; + } + } + read_len += (UINT32)(EVENT_SIZE + event->len); /* Size of the inotify_event structure */ + } + rpc_free(buffer); + if (RPC_OK != result) { + goto exit_loop_ok; + } + + } else { /* poll error */ // LCOV_EXCL_START 5: fail safe for libc select + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + RPC_LOG_PERROR("select in wait result"); + result = RPC_ERR_Fatal; + goto exit_loop_ok; + // LCOV_EXCL_STOP + } + } +exit_loop_ok: + + return result; +} + +/** @} */ -- cgit 1.2.3-korg