From 171c3f96b8a02da15803a739bdb6a2a367527845 Mon Sep 17 00:00:00 2001 From: Jinkun Jang Date: Sat, 16 Mar 2013 21:33:50 +0900 Subject: [PATCH] sync with master --- CMakeLists.txt | 57 ++ LICENSE | 204 +++++ com-core.pc.in | 11 + include/com-core.h | 53 ++ include/com-core_internal.h | 21 + include/com-core_packet-router.h | 53 ++ include/com-core_packet.h | 44 + include/com-core_thread.h | 39 + include/debug.h | 31 + include/dlist.h | 44 + include/packet.h | 79 ++ include/secure_socket.h | 61 ++ include/util.h | 36 + libcom-core.manifest | 5 + packaging/libcom-core.spec | 52 ++ src/com-core.c | 495 +++++++++++ src/com-core_packet-router.c | 1813 ++++++++++++++++++++++++++++++++++++++ src/com-core_packet.c | 635 +++++++++++++ src/com-core_thread.c | 948 ++++++++++++++++++++ src/dlist.c | 181 ++++ src/packet.c | 573 ++++++++++++ src/secure_socket.c | 259 ++++++ src/util.c | 39 + 23 files changed, 5733 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 com-core.pc.in create mode 100644 include/com-core.h create mode 100644 include/com-core_internal.h create mode 100644 include/com-core_packet-router.h create mode 100644 include/com-core_packet.h create mode 100644 include/com-core_thread.h create mode 100644 include/debug.h create mode 100644 include/dlist.h create mode 100644 include/packet.h create mode 100644 include/secure_socket.h create mode 100644 include/util.h create mode 100644 libcom-core.manifest create mode 100644 packaging/libcom-core.spec create mode 100644 src/com-core.c create mode 100644 src/com-core_packet-router.c create mode 100644 src/com-core_packet.c create mode 100644 src/com-core_thread.c create mode 100644 src/dlist.c create mode 100644 src/packet.c create mode 100644 src/secure_socket.c create mode 100644 src/util.c diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..584bf53 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,57 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +PROJECT(com-core C) + +SET(PREFIX ${CMAKE_INSTALL_PREFIX}) +SET(EXEC_PREFIX "\${prefix}") +SET(PROJECT_NAME "${PROJECT_NAME}") +SET(LIBDIR "\${exec_prefix}/lib") +SET(INCLUDEDIR "\${prefix}/include/${PROJECT_NAME}") +SET(VERSION_MAJOR 0) +SET(VERSION "${VERSION_MAJOR}.0.1") + +set(CMAKE_SKIP_BUILD_RPATH true) + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) + +INCLUDE(FindPkgConfig) +pkg_check_modules(pkgs REQUIRED + glib-2.0 + dlog +) + +FOREACH(flag ${pkgs_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fvisibility=hidden -g -Wall -Werror -Winline") +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}") + +ADD_DEFINITIONS("-DPREFIX=\"${PREFIX}\"") +ADD_DEFINITIONS("-DLOG_TAG=\"COM_CORE\"") +ADD_DEFINITIONS("-DNDEBUG") + +ADD_LIBRARY(${PROJECT_NAME} SHARED + src/dlist.c + src/com-core.c + src/util.c + src/packet.c + src/com-core_packet.c + src/secure_socket.c + src/com-core_thread.c + src/com-core_packet-router.c +) +SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES SOVERSION ${VERSION_MAJOR}) +SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES VERSION ${VERSION}) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkgs_LDFLAGS}) + +CONFIGURE_FILE(${PROJECT_NAME}.pc.in ${PROJECT_NAME}.pc @ONLY) +SET_DIRECTORY_PROPERTIES(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${PROJECT_NAME}.pc") + +INSTALL(TARGETS ${PROJECT_NAME} DESTINATION lib) +INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc DESTINATION lib/pkgconfig) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/include/secure_socket.h DESTINATION include/${PROJECT_NAME}) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/include/com-core.h DESTINATION include/${PROJECT_NAME}) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/include/com-core_packet.h DESTINATION include/${PROJECT_NAME}) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/include/com-core_thread.h DESTINATION include/${PROJECT_NAME}) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/include/packet.h DESTINATION include/${PROJECT_NAME}) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/LICENSE DESTINATION /usr/share/license RENAME "lib${PROJECT_NAME}") diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9c13a9b --- /dev/null +++ b/LICENSE @@ -0,0 +1,204 @@ +Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + diff --git a/com-core.pc.in b/com-core.pc.in new file mode 100644 index 0000000..e1f99da --- /dev/null +++ b/com-core.pc.in @@ -0,0 +1,11 @@ +prefix=@PREFIX@ +exec_prefix=@EXEC_PREFIX@ +libdir=@LIBDIR@ +includedir=@INCLUDEDIR@ + +Name: com-core +Description: Light-weight IPC supporting library +Version: @VERSION@ +Libs: -L${libdir} -lcom-core +Cflags: -I${includedir} +cppflags: -I${includedir} diff --git a/include/com-core.h b/include/com-core.h new file mode 100644 index 0000000..5737b83 --- /dev/null +++ b/include/com-core.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * 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. + * +*/ + +#ifndef _COM_CORE_H +#define _COM_CORE_H + +#ifdef __cplusplus +extern "C" { +#endif + +enum com_core_event_type { + CONNECTOR_CONNECTED, + CONNECTOR_DISCONNECTED, +}; + +extern int com_core_server_create(const char *addr, int is_sync, int (*service_cb)(int fd, void *data), void *data); +extern int com_core_client_create(const char *addr, int is_sync, int (*service_cb)(int fd, void *data), void *data); +extern int com_core_server_destroy(int handle); +extern int com_core_client_destroy(int handle); + +/*! + * \brief Used to handling the changing event of connection status. + * These two functions can be work with com_core_thread series functions. + */ +extern int com_core_add_event_callback(enum com_core_event_type type, int (*service_cb)(int handle, void *data), void *data); +extern void *com_core_del_event_callback(enum com_core_event_type type, int (*service_cb)(int handle, void *data), void *data); + +/*! + * \brief If the connection is lost, this recv function will call the disconnected callback. + */ +extern int com_core_recv(int handle, char *buffer, int size, int *sender_pid, double timeout); +extern int com_core_send(int handle, const char *buffer, int size, double timeout); + +#ifdef __cplusplus +} +#endif + +#endif +/* End of a file */ diff --git a/include/com-core_internal.h b/include/com-core_internal.h new file mode 100644 index 0000000..6ba36c2 --- /dev/null +++ b/include/com-core_internal.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * 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. + * +*/ + +extern void invoke_con_cb_list(int handle); +extern void invoke_disconn_cb_list(int handle); + +/* End of a file */ diff --git a/include/com-core_packet-router.h b/include/com-core_packet-router.h new file mode 100644 index 0000000..a484281 --- /dev/null +++ b/include/com-core_packet-router.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * 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. + * +*/ + +#ifndef _COM_CORE_PACKET_ROUTER_H +#define _COM_CORE_PACKET_ROUTER_H + +#ifdef __cplusplus +extern "C" { +#endif + +enum com_core_route_event_type { + COM_CORE_ROUTE_CONNECTED, + COM_CORE_ROUTE_DISCONNECTED, + COM_CORE_ROUTE_ERROR, +}; + +extern int com_core_packet_router_add_route(int handle, unsigned long address, int to); +extern int com_core_packet_router_del_route(int handle, unsigned long address); +extern int com_core_packet_router_update_route(int handle, unsigned long address, int to); + +extern int com_core_packet_router_add_event_callback(enum com_core_route_event_type type, int (*evt_cb)(int handle, void *data), void *data); +extern int com_core_packet_router_del_event_callback(enum com_core_route_event_type type, int (*evt_cb)(int handle, void *data), void *data); + +extern int com_core_packet_router_server_init(const char *sock, double timeout, struct method *table); +extern void *com_core_packet_router_server_fini(int handle); + +extern int com_core_packet_router_client_init(const char *sock, double timeout, struct method *table); +extern void *com_core_packet_router_client_fini(int handle); + +extern int com_core_packet_router_async_send(int handle, struct packet *packet, double timeout, int (*recv_cb)(pid_t pid, int handle, const struct packet *packet, void *data), void *data); +extern int com_core_packet_router_send_only(int handle, struct packet *packet); +extern struct packet *com_core_packet_router_oneshot_send(const char *addr, struct packet *packet, double timeout); + +#ifdef __cplusplus +} +#endif + +#endif +/* End of a file */ diff --git a/include/com-core_packet.h b/include/com-core_packet.h new file mode 100644 index 0000000..ddb18f8 --- /dev/null +++ b/include/com-core_packet.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * 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. + * +*/ + +#ifndef _COM_CORE_PACKET_H +#define _COM_CORE_PACKET_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct method { + const char *cmd; + struct packet *(*handler)(pid_t pid, int handle, const struct packet *packet); +}; + +extern int com_core_packet_async_send(int handle, struct packet *packet, double timeout, int (*recv_cb)(pid_t pid, int handle, const struct packet *packet, void *data), void *data); +extern int com_core_packet_send_only(int handle, struct packet *packet); +extern struct packet *com_core_packet_oneshot_send(const char *addr, struct packet *packet, double timeout); +extern int com_core_packet_client_init(const char *addr, int is_sync, struct method *table); +extern int com_core_packet_client_fini(int handle); +extern int com_core_packet_server_init(const char *addr, struct method *table); +extern int com_core_packet_server_fini(int handle); +extern void com_core_packet_use_thread(int flag); + +#ifdef __cplusplus +} +#endif + +#endif +/* End of a file */ diff --git a/include/com-core_thread.h b/include/com-core_thread.h new file mode 100644 index 0000000..0a6b375 --- /dev/null +++ b/include/com-core_thread.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * 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. + * +*/ + +#ifndef _COM_CORE_THREAD_H +#define _COM_CORE_THREAD_H + +#ifdef __cplusplus +extern "C" { +#endif + +extern int com_core_thread_client_create(const char *addr, int is_sync, int (*service_cb)(int fd, void *data), void *data); +extern int com_core_thread_server_create(const char *addr, int is_sync, int (*service_cb)(int fd, void *data), void *data); + +extern int com_core_thread_server_destroy(int handle); +extern int com_core_thread_client_destroy(int handle); + +extern int com_core_thread_recv(int handle, char *buffer, int size, int *sender_pid, double timeout); +extern int com_core_thread_send(int handle, const char *buffer, int size, double timeout); + +#ifdef __cplusplus +} +#endif + +#endif +/* End of a file */ diff --git a/include/debug.h b/include/debug.h new file mode 100644 index 0000000..4de89c5 --- /dev/null +++ b/include/debug.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * 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. + * +*/ + +#if !defined(FLOG) +#define DbgPrint(format, arg...) LOGD("[%s/%s:%d] " format, util_basename(__FILE__), __func__, __LINE__, ##arg) +#define ErrPrint(format, arg...) LOGE("[%s/%s:%d] " format, util_basename(__FILE__), __func__, __LINE__, ##arg) +#else +extern FILE *__file_log_fp; +#define DbgPrint(format, arg...) do { fprintf(__file_log_fp, "[LOG] [%s/%s:%d] " format, util_basename(__FILE__), __func__, __LINE__, ##arg); fflush(__file_log_fp); } while (0) + +#define ErrPrint(format, arg...) do { fprintf(__file_log_fp, "[ERR] [%s/%s:%d] " format, util_basename(__FILE__), __func__, __LINE__, ##arg); fflush(__file_log_fp); } while (0) +#endif + +#define EAPI __attribute__((visibility("default"))) +#define HAPI __attribute__((visibility("hidden"))) + +/* End of a file */ diff --git a/include/dlist.h b/include/dlist.h new file mode 100644 index 0000000..f840f92 --- /dev/null +++ b/include/dlist.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * 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. + * +*/ + +#define dlist_remove_data(list, data) do { \ + struct dlist *l; \ + l = dlist_find_data(list, data); \ + list = dlist_remove(list, l); \ +} while (0) + +#define dlist_foreach(list, l, data) \ + for ((l) = (list); (l) && ((data) = dlist_data(l)); (l) = dlist_next(l)) + +#define dlist_foreach_safe(list, l, n, data) \ + for ((l) = (list), (n) = dlist_next(l); \ + (l) && ((data) = dlist_data(l)); \ + (l) = (n), (n) = dlist_next(l)) + +struct dlist; + +extern struct dlist *dlist_append(struct dlist *list, void *data); +extern struct dlist *dlist_prepend(struct dlist *list, void *data); +extern struct dlist *dlist_remove(struct dlist *list, struct dlist *l); +extern struct dlist *dlist_find_data(struct dlist *list, void *data); +extern void *dlist_data(struct dlist *l); +extern struct dlist *dlist_next(struct dlist *l); +extern struct dlist *dlist_prev(struct dlist *l); +extern int dlist_count(struct dlist *l); +extern struct dlist *dlist_nth(struct dlist *l, int nth); + +/* End of a file */ diff --git a/include/packet.h b/include/packet.h new file mode 100644 index 0000000..9881ce5 --- /dev/null +++ b/include/packet.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * 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. + * +*/ + +#ifndef _PACKET_H +#define _PACKET_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct packet; + +enum packet_type { + PACKET_REQ, + PACKET_ACK, + PACKET_REQ_NOACK, + PACKET_ERROR, +}; + +enum packet_flag { + PACKET_FLAG_NOROUTE = 0x00, /*!< If possible, route this packet without care of the server */ + PACKET_FLAG_ROUTE = 0x01, /*!< This packet must has to be cared by the server */ + + PACKET_FLAG_ERROR = 0xFF, /*!< Invalid flag */ +}; + +#define PACKET_VERSION 2 +#define PACKET_MAX_CMD 24 + +extern struct packet *packet_create(const char *command, const char *fmt, ...); +extern struct packet *packet_create_noack(const char *command, const char *fmt, ...); +extern struct packet *packet_create_reply(const struct packet *packet, const char *fmt, ...); +extern int packet_get(const struct packet *packet, const char *fmt, ...); +extern int packet_destroy(struct packet *packet); +extern struct packet *packet_ref(struct packet *packet); +extern struct packet *packet_unref(struct packet *packet); + +extern const void * const packet_data(const struct packet *packet); +extern const double const packet_seq(const struct packet *packet); +extern const enum packet_type const packet_type(const struct packet *packet); + +extern const enum packet_flag const packet_flag(const struct packet *packet); +extern int packet_set_flag(struct packet *packet, enum packet_flag flag); +extern const unsigned long const packet_source(const struct packet *packet); +extern int packet_set_source(struct packet *packet, unsigned long source); +extern const unsigned long const packet_destination(const struct packet *packet); +extern int packet_set_destination(struct packet *packet, unsigned long destination); +extern int packet_set_mask(struct packet *packet, unsigned long mask); +extern unsigned long packet_mask(const struct packet *packet); +extern int packet_swap_address(struct packet *packet); + +extern const int const packet_version(const struct packet *packet); +extern const int const packet_payload_size(const struct packet *packet); +extern const char * const packet_command(const const struct packet *packet); +extern const int const packet_header_size(void); +extern const int const packet_size(const struct packet *packet); + +extern struct packet *packet_build(struct packet *packet, int offset, void *data, int size); + +#ifdef __cplusplus +} +#endif + +#endif +/* End of a file */ diff --git a/include/secure_socket.h b/include/secure_socket.h new file mode 100644 index 0000000..24039bb --- /dev/null +++ b/include/secure_socket.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * 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. + * +*/ + +#ifndef _SECURE_SOCKET_H +#define _SECURE_SOCKET_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * Create client connection + */ +extern int secure_socket_create_client(const char *peer); + +/* + * Create server connection + */ +extern int secure_socket_create_server(const char *peer); + +/* + * Get the raw handle to use it for non-blocking mode. + */ +extern int secure_socket_get_connection_handle(int server_handle); + +/* + * Send data to the connected peer. + */ +extern int secure_socket_send(int conn, const char *buffer, int size); + +/* + * Recv data from the connected peer. and its PID value + */ +extern int secure_socket_recv(int conn, char *buffer, int size, int *sender_pid); + +/* + * Destroy a connection + */ +extern int secure_socket_destroy_handle(int conn); + +#ifdef __cplusplus +} +#endif + +#endif +/* End of a file */ diff --git a/include/util.h b/include/util.h new file mode 100644 index 0000000..0dfa576 --- /dev/null +++ b/include/util.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * 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. + * +*/ + +extern const char *util_basename(const char *name); + +#define CRITICAL_SECTION_BEGIN(handle) \ +do { \ + int ret; \ + ret = pthread_mutex_lock(handle); \ + if (ret != 0) \ + ErrPrint("Failed to lock: %s\n", strerror(ret)); \ +} while (0) + +#define CRITICAL_SECTION_END(handle) \ +do { \ + int ret; \ + ret = pthread_mutex_unlock(handle); \ + if (ret != 0) \ + ErrPrint("Failed to unlock: %s\n", strerror(ret)); \ +} while (0) + +/* End of a file */ diff --git a/libcom-core.manifest b/libcom-core.manifest new file mode 100644 index 0000000..a76fdba --- /dev/null +++ b/libcom-core.manifest @@ -0,0 +1,5 @@ + + + + + diff --git a/packaging/libcom-core.spec b/packaging/libcom-core.spec new file mode 100644 index 0000000..4edc40b --- /dev/null +++ b/packaging/libcom-core.spec @@ -0,0 +1,52 @@ +Name: libcom-core +Summary: Library for the light-weight IPC +Version: 0.3.12 +Release: 1 +Group: main/util +License: Apache License +Source0: %{name}-%{version}.tar.gz +BuildRequires: cmake, gettext-tools, coreutils +BuildRequires: pkgconfig(dlog) +BuildRequires: pkgconfig(glib-2.0) + +%description +Light-weight IPC supporting library + +%package devel +Summary: Files for using API for light-weight IPC. +Group: Development/Libraries +Requires: %{name} = %{version}-%{release} + +%description devel +Light-weight IPC supporting library (dev) + +%prep +%setup -q + +%build +cmake . -DCMAKE_INSTALL_PREFIX=%{_prefix} +make %{?jobs:-j%jobs} + +%install +rm -rf %{buildroot} +%make_install +mkdir -p %{buildroot}/%{_datarootdir}/license + +%post + +%files -n libcom-core +%manifest libcom-core.manifest +%defattr(-,root,root,-) +%{_libdir}/*.so* +%{_datarootdir}/license/* + +%files devel +%defattr(-,root,root,-) +%{_includedir}/com-core/com-core.h +%{_includedir}/com-core/packet.h +%{_includedir}/com-core/com-core_packet.h +%{_includedir}/com-core/com-core_thread.h +%{_includedir}/com-core/secure_socket.h +%{_libdir}/pkgconfig/*.pc + +# End of a file diff --git a/src/com-core.c b/src/com-core.c new file mode 100644 index 0000000..b1ed65f --- /dev/null +++ b/src/com-core.c @@ -0,0 +1,495 @@ +/* + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * 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. + * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "dlist.h" +#include "secure_socket.h" +#include "debug.h" +#include "com-core.h" +#include "com-core_internal.h" +#include "util.h" + +static struct { + struct dlist *conn_cb_list; + struct dlist *disconn_cb_list; +} s_info = { + .conn_cb_list = NULL, + .disconn_cb_list = NULL, +}; + +struct cbdata { + int (*service_cb)(int fd, void *data); + void *data; +}; + +struct evtdata { + int (*evt_cb)(int fd, void *data); + void *data; +}; + +HAPI void invoke_con_cb_list(int handle) +{ + struct dlist *l; + struct dlist *n; + struct evtdata *cbdata; + + dlist_foreach_safe(s_info.conn_cb_list, l, n, cbdata) { + if (cbdata->evt_cb(handle, cbdata->data) < 0) { + if (dlist_find_data(s_info.conn_cb_list, cbdata)) { + s_info.conn_cb_list = dlist_remove(s_info.conn_cb_list, l); + free(cbdata); + } + } + } +} + +HAPI void invoke_disconn_cb_list(int handle) +{ + struct dlist *l; + struct dlist *n; + struct evtdata *cbdata; + + dlist_foreach_safe(s_info.disconn_cb_list, l, n, cbdata) { + if (cbdata->evt_cb(handle, cbdata->data) < 0) { + if (dlist_find_data(s_info.disconn_cb_list, cbdata)) { + s_info.disconn_cb_list = dlist_remove(s_info.disconn_cb_list, l); + free(cbdata); + } + } + } +} + +static gboolean client_cb(GIOChannel *src, GIOCondition cond, gpointer data) +{ + int client_fd; + struct cbdata *cbdata = data; + int ret; + + client_fd = g_io_channel_unix_get_fd(src); + + if (!(cond & G_IO_IN)) { + DbgPrint("Client is disconencted\n"); + invoke_disconn_cb_list(client_fd); + secure_socket_destroy_handle(client_fd); + return FALSE; + } + + if ((cond & G_IO_ERR) || (cond & G_IO_HUP) || (cond & G_IO_NVAL)) { + DbgPrint("Client connection is lost\n"); + invoke_disconn_cb_list(client_fd); + secure_socket_destroy_handle(client_fd); + return FALSE; + } + + ret = cbdata->service_cb(client_fd, cbdata->data); + if (ret < 0) { + DbgPrint("service callback returns %d < 0\n", ret); + invoke_disconn_cb_list(client_fd); + secure_socket_destroy_handle(client_fd); + return FALSE; + } + + return TRUE; +} + +static gboolean accept_cb(GIOChannel *src, GIOCondition cond, gpointer cbdata) +{ + int socket_fd; + int client_fd; + GIOChannel *gio; + guint id; + + socket_fd = g_io_channel_unix_get_fd(src); + if (!(cond & G_IO_IN)) { + ErrPrint("Accept socket closed\n"); + secure_socket_destroy_handle(socket_fd); + free(cbdata); + return FALSE; + } + + if ((cond & G_IO_ERR) || (cond & G_IO_HUP) || (cond & G_IO_NVAL)) { + DbgPrint("Client connection is lost\n"); + secure_socket_destroy_handle(socket_fd); + free(cbdata); + return FALSE; + } + + DbgPrint("New connectino arrived: socket(%d)\n", socket_fd); + client_fd = secure_socket_get_connection_handle(socket_fd); + if (client_fd < 0) { + free(cbdata); + return FALSE; + } + DbgPrint("New client: %d\n", client_fd); + + if (fcntl(client_fd, F_SETFD, FD_CLOEXEC) < 0) + ErrPrint("Error: %s\n", strerror(errno)); + + if (fcntl(client_fd, F_SETFL, O_NONBLOCK) < 0) + ErrPrint("Error: %s\n", strerror(errno)); + + gio = g_io_channel_unix_new(client_fd); + if (!gio) { + ErrPrint("Failed to get gio\n"); + secure_socket_destroy_handle(client_fd); + free(cbdata); + return FALSE; + } + + g_io_channel_set_close_on_unref(gio, FALSE); + + id = g_io_add_watch(gio, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, (GIOFunc)client_cb, cbdata); + if (id <= 0) { + GError *err = NULL; + ErrPrint("Failed to add IO watch\n"); + g_io_channel_shutdown(gio, TRUE, &err); + if (err) { + ErrPrint("Shutdown: %s\n", err->message); + g_error_free(err); + } + g_io_channel_unref(gio); + secure_socket_destroy_handle(client_fd); + free(cbdata); + return FALSE; + } + + g_io_channel_unref(gio); + + invoke_con_cb_list(client_fd); + DbgPrint("New client is connected with %d\n", client_fd); + return TRUE; +} + +EAPI int com_core_server_create(const char *addr, int is_sync, int (*service_cb)(int fd, void *data), void *data) +{ + GIOChannel *gio; + guint id; + int fd; + struct cbdata *cbdata; + + cbdata = malloc(sizeof(*cbdata)); + if (!cbdata) { + ErrPrint("Heap: %s\n", strerror(errno)); + return -ENOMEM; + } + + cbdata->service_cb = service_cb; + cbdata->data = data; + + fd = secure_socket_create_server(addr); + if (fd < 0) { + free(cbdata); + return fd; + } + + if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) + ErrPrint("fcntl: %s\n", strerror(errno)); + + if (!is_sync && fcntl(fd, F_SETFL, O_NONBLOCK) < 0) + ErrPrint("fcntl: %s\n", strerror(errno)); + + DbgPrint("Create new IO channel for socket FD: %d\n", fd); + gio = g_io_channel_unix_new(fd); + if (!gio) { + ErrPrint("Failed to create new io channel\n"); + free(cbdata); + secure_socket_destroy_handle(fd); + return -EIO; + } + + g_io_channel_set_close_on_unref(gio, FALSE); + + id = g_io_add_watch(gio, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc)accept_cb, cbdata); + if (id <= 0) { + GError *err = NULL; + ErrPrint("Failed to add IO watch\n"); + free(cbdata); + g_io_channel_shutdown(gio, TRUE, &err); + if (err) { + ErrPrint("Shutdown: %s\n", err->message); + g_error_free(err); + } + g_io_channel_unref(gio); + secure_socket_destroy_handle(fd); + return -EIO; + } + + g_io_channel_unref(gio); + return fd; +} + +EAPI int com_core_client_create(const char *addr, int is_sync, int (*service_cb)(int fd, void *data), void *data) +{ + GIOChannel *gio; + guint id; + int client_fd; + struct cbdata *cbdata; + + cbdata = malloc(sizeof(*cbdata)); + if (!cbdata) { + ErrPrint("Heap: %s\n", strerror(errno)); + return -ENOMEM; + } + + cbdata->service_cb = service_cb; + cbdata->data = data; + + client_fd = secure_socket_create_client(addr); + if (client_fd < 0) { + free(cbdata); + return client_fd; + } + + if (fcntl(client_fd, F_SETFD, FD_CLOEXEC) < 0) + ErrPrint("Error: %s\n", strerror(errno)); + + if (!is_sync && fcntl(client_fd, F_SETFL, O_NONBLOCK) < 0) + ErrPrint("Error: %s\n", strerror(errno)); + + gio = g_io_channel_unix_new(client_fd); + if (!gio) { + ErrPrint("Failed to create a new IO channel\n"); + free(cbdata); + secure_socket_destroy_handle(client_fd); + return -EIO; + } + + g_io_channel_set_close_on_unref(gio, FALSE); + + id = g_io_add_watch(gio, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, (GIOFunc)client_cb, cbdata); + if (id <= 0) { + GError *err = NULL; + ErrPrint("Failed to add IO watch\n"); + free(cbdata); + g_io_channel_shutdown(gio, TRUE, &err); + if (err) { + ErrPrint("Shutdown: %s\n", err->message); + g_error_free(err); + } + g_io_channel_unref(gio); + secure_socket_destroy_handle(client_fd); + return -EIO; + } + + g_io_channel_unref(gio); + invoke_con_cb_list(client_fd); + return client_fd; +} + +EAPI int com_core_add_event_callback(enum com_core_event_type type, int (*evt_cb)(int handle, void *data), void *data) +{ + struct evtdata *cbdata; + cbdata = malloc(sizeof(*cbdata)); + if (!cbdata) { + ErrPrint("Heap: %s\n", strerror(errno)); + return -ENOMEM; + } + + cbdata->evt_cb = evt_cb; + cbdata->data = data; + + if (type == CONNECTOR_CONNECTED) + s_info.conn_cb_list = dlist_append(s_info.conn_cb_list, cbdata); + else + s_info.disconn_cb_list = dlist_append(s_info.disconn_cb_list, cbdata); + return 0; +} + +EAPI int com_core_recv(int handle, char *buffer, int size, int *sender_pid, double timeout) +{ + int readsize; + int ret; + + fd_set set; + + readsize = 0; + while (size > 0) { + FD_ZERO(&set); + FD_SET(handle, &set); + + if (timeout > 0.0f) { + struct timeval tv; + + tv.tv_sec = (unsigned long)timeout; + tv.tv_usec = (timeout - (unsigned long)timeout) * 1000000u; + ret = select(handle + 1, &set, NULL, NULL, &tv); + } else if (timeout == 0.0f) { + ret = select(handle + 1, &set, NULL, NULL, NULL); + } else { + ErrPrint("Invalid timeout: %lf (it must be greater than 0.0)\n", timeout); + return -EINVAL; + } + + if (ret < 0) { + ret = -errno; + if (errno == EINTR) { + DbgPrint("Select receives INTR\n"); + continue; + } + ErrPrint("Error: %s\n", strerror(errno)); + return ret; + } else if (ret == 0) { + ErrPrint("Timeout expired\n"); + break; + } + + if (!FD_ISSET(handle, &set)) { + ErrPrint("Unexpected handle is toggled\n"); + return -EINVAL; + } + + ret = secure_socket_recv(handle, buffer + readsize, size, sender_pid); + if (ret < 0) { + if (ret == -EAGAIN) { + DbgPrint("Retry to get data (%d:%d)\n", readsize, size); + continue; + } + return ret; + } else if (ret == 0) { + return 0; + } + + size -= ret; + readsize += ret; + } + + return readsize; +} + +EAPI int com_core_send(int handle, const char *buffer, int size, double timeout) +{ + int writesize; + int ret; + + fd_set set; + + writesize = 0; + while (size > 0) { + + FD_ZERO(&set); + FD_SET(handle, &set); + + if (timeout > 0.0f) { + struct timeval tv; + + tv.tv_sec = (unsigned long)timeout; + tv.tv_usec = (timeout - (unsigned long)timeout) * 1000000u; + + ret = select(handle + 1, NULL, &set, NULL, &tv); + } else if (timeout == 0.0f) { + ret = select(handle + 1, NULL, &set, NULL, NULL); + } else { + ErrPrint("Invalid timeout: %lf (it must be greater than 0.0)\n", timeout); + return -EINVAL; + } + + if (ret < 0) { + ret = -errno; + if (errno == EINTR) { + DbgPrint("Select receives INTR\n"); + continue; + } + ErrPrint("Error: %s\n", strerror(errno)); + return ret; + } else if (ret == 0) { + ErrPrint("Timeout expired\n"); + break; + } + + if (!FD_ISSET(handle, &set)) { + ErrPrint("Unexpected handle is toggled\n"); + return -EINVAL; + } + + ret = secure_socket_send(handle, buffer + writesize, size); + if (ret < 0) { + if (ret == -EAGAIN) { + DbgPrint("Retry to send data (%d:%d)\n", writesize, size); + continue; + } + DbgPrint("Failed to send: %d\n", ret); + return ret; + } else if (ret == 0) { + DbgPrint("Disconnected? : Send bytes: 0\n"); + return 0; + } + + size -= ret; + writesize += ret; + } + + return writesize; +} + +EAPI void *com_core_del_event_callback(enum com_core_event_type type, int (*cb)(int handle, void *data), void *data) +{ + struct dlist *l; + struct dlist *n; + struct evtdata *cbdata; + + if (type == CONNECTOR_CONNECTED) { + dlist_foreach_safe(s_info.conn_cb_list, l, n, cbdata) { + if (cbdata->evt_cb == cb && cbdata->data == data) { + void *data; + data = cbdata->data; + dlist_remove_data(s_info.conn_cb_list, cbdata); + free(cbdata); + return data; + } + } + } else { + dlist_foreach_safe(s_info.disconn_cb_list, l, n, cbdata) { + if (cbdata->evt_cb == cb && cbdata->data == data) { + void *data; + data = cbdata->data; + dlist_remove_data(s_info.disconn_cb_list, cbdata); + free(cbdata); + return data; + } + } + } + + return NULL; +} + +EAPI int com_core_server_destroy(int handle) +{ + DbgPrint("Close server handle[%d]\n", handle); + secure_socket_destroy_handle(handle); + return 0; +} + +EAPI int com_core_client_destroy(int handle) +{ + DbgPrint("Close client handle[%d]\n", handle); + secure_socket_destroy_handle(handle); + return 0; +} + +/* End of a file */ diff --git a/src/com-core_packet-router.c b/src/com-core_packet-router.c new file mode 100644 index 0000000..2e7b736 --- /dev/null +++ b/src/com-core_packet-router.c @@ -0,0 +1,1813 @@ +/* + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * 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. + * +*/ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include /* Obtain O_* constant definitions */ +#include +#include + +#include +#include + +#include "secure_socket.h" +#include "dlist.h" +#include "packet.h" +#include "com-core.h" +#include "com-core_packet.h" +#include "debug.h" +#include "util.h" +#include "com-core_packet-router.h" + +#define PIPE_READ 0 +#define PIPE_WRITE 1 + +struct packet_item { + pid_t pid; + struct packet *packet; +}; + +struct route { + unsigned long address; + int handle; + int invalid; +}; + +struct client { + struct router *router; + int handle; + + pthread_t thid; +}; + +struct recv_ctx { + enum state { + RECV_STATE_INIT, + RECV_STATE_HEADER, + RECV_STATE_BODY, + RECV_STATE_READY, + } state; + + struct packet *packet; + unsigned long offset; + pid_t pid; + + double timeout; +}; + +struct request_ctx { + pid_t pid; + int handle; + + struct packet *packet; + int (*recv_cb)(pid_t pid, int handle, const struct packet *packet, void *data); + void *data; +}; + +struct router { + int handle; + + char *sock; + struct packet *(*service)(int handle, pid_t pid, const struct packet *packet, void *data); + void *data; + + double timeout; + + pthread_mutex_t recv_packet_list_lock; + struct dlist *recv_packet_list; + + pthread_mutex_t route_list_lock; + struct dlist *route_list; + + pthread_mutex_t send_packet_list_lock; + struct dlist *send_packet_list; + + int recv_pipe[2]; + int send_pipe[2]; + + pthread_t send_thid; + + guint id; + + unsigned long count_of_dropped_packet; + + int is_server; + union { + struct { + struct dlist *client_list; + guint accept_id; + } server; /*!< Only used by the server */ + + struct { + pthread_t thid; + } client; /*!< Only used by the client */ + } info; +}; + +struct event_item { + int (*evt_cb)(int handle, void *data); + void *data; +}; + +static struct info { + struct dlist *router_list; + struct dlist *request_list; + + struct dlist *disconnected_list; + struct dlist *connected_list; + struct dlist *error_list; +} s_info = { + .router_list = NULL, + .request_list = NULL, + + .disconnected_list = NULL, + .connected_list = NULL, + .error_list = NULL, +}; + +static inline struct packet *get_recv_packet(struct router *router, int *handle, pid_t *pid); +static inline int put_recv_packet(struct router *router, int handle, struct packet *packet, pid_t pid); + +static inline struct packet *get_send_packet(struct router *router, int *handle); +static inline int put_send_packet(struct router *router, int handle, struct packet *packet); + +/*! + * \note + * Running thread: Main + */ +static inline int invoke_disconnected_cb(struct router *router, int handle) +{ + struct dlist *l; + struct dlist *n; + struct event_item *item; + struct route *route; + int ret; + + CRITICAL_SECTION_BEGIN(&router->route_list_lock); + + dlist_foreach(router->route_list, l, route) { + if (route->handle == handle) { + /*! + * \NOTE + * Invalidate an entry in the routing table. + * Do not this entry from the routing table from here,. + * Because a user may not want to delete the entry without any notification. + * So we just left this invalid entry on the table. + * Then the user has to manage the routing table correctly + * via connected/disconnected event callbacks. + */ + route->invalid = 1; + } + } + + CRITICAL_SECTION_END(&router->route_list_lock); + + /*! + * \NOTE + * Invoke the disconnected callback + */ + dlist_foreach_safe(s_info.disconnected_list, l, n, item) { + ret = item->evt_cb(handle, item->data); + if (ret < 0 && dlist_find_data(s_info.disconnected_list, item)) { + s_info.disconnected_list = dlist_remove(s_info.disconnected_list, l); + free(item); + } + } + + return 0; +} + +/*! + * \NOTE + * Running thread: Main + */ +static inline int invoke_connected_cb(struct router *router, int handle) +{ + struct dlist *l; + struct dlist *n; + struct event_item *item; + int ret; + + dlist_foreach_safe(s_info.connected_list, l, n, item) { + ret = item->evt_cb(handle, item->data); + if (ret < 0 && dlist_find_data(s_info.connected_list, item)) { + s_info.connected_list = dlist_remove(s_info.connected_list, l); + free(item); + } + } + + return 0; +} + +/*! + * \NOTE + * Running thread: Main + */ +static inline int invoke_error_cb(struct router *router, int handle) +{ + struct dlist *l; + struct dlist *n; + struct event_item *item; + int ret; + + dlist_foreach_safe(s_info.error_list, l, n, item) { + ret = item->evt_cb(handle, item->data); + if (ret < 0 && dlist_find_data(s_info.error_list, item)) { + s_info.error_list = dlist_remove(s_info.error_list, l); + free(item); + } + } + + return 0; +} + +/*! + * \NOTE + * Running thread: Main + */ +static inline struct request_ctx *find_request_ctx(int handle, double seq) +{ + struct request_ctx *ctx; + struct dlist *l; + + dlist_foreach(s_info.request_list, l, ctx) { + if (ctx->handle == handle && packet_seq(ctx->packet) == seq) { + return ctx; + } + } + + return NULL; +} + +/*! + * \NOTE + * Running thread: Main + */ +static inline void destroy_request_ctx(struct request_ctx *ctx) +{ + packet_unref(ctx->packet); + dlist_remove_data(s_info.request_list, ctx); + free(ctx); +} + +/*! + * \NOTE + * Running thread: Main + */ +static inline void clear_request_ctx(int handle) +{ + struct request_ctx *ctx; + struct dlist *l; + struct dlist *n; + + dlist_foreach_safe(s_info.request_list, l, n, ctx) { + if (ctx->handle != handle) + continue; + + if (ctx->recv_cb) + ctx->recv_cb(-1, handle, NULL, ctx->data); + + destroy_request_ctx(ctx); + } +} + +/*! + * \NOTE + * Running thread: Main + */ +static inline struct request_ctx *create_request_ctx(int handle) +{ + struct request_ctx *ctx; + + ctx = malloc(sizeof(*ctx)); + if (!ctx) { + ErrPrint("Heap: %s\n", strerror(errno)); + return NULL; + } + + ctx->handle = handle; + ctx->pid = (pid_t)-1; + ctx->packet = NULL; + ctx->recv_cb = NULL; + ctx->data = NULL; + + s_info.request_list = dlist_append(s_info.request_list, ctx); + return ctx; +} + +/*! + * \NOTE + * Running thread: Main + */ +static inline struct router *find_router_by_handle(int handle) +{ + struct dlist *l; + struct router *router; + + dlist_foreach(s_info.router_list, l, router) { + if (router->is_server) { + struct dlist *cl; + struct client *client; + /*! + * Find the client list + */ + dlist_foreach(router->info.server.client_list, cl, client) { + if (client->handle == handle) + return router; + } + } else if (router->handle == handle) { + return router; + } + } + + return NULL; +} + + +/*! + * \NOTE + * Running thread: Main + */ +static gboolean packet_cb(GIOChannel *src, GIOCondition cond, gpointer data) +{ + struct router *router = data; + struct packet *packet; + struct packet *result_packet; + struct request_ctx *request; + int evt_handle; + int handle = -1; + pid_t pid = (pid_t)-1; + + evt_handle = g_io_channel_unix_get_fd(src); + if (evt_handle != router->recv_pipe[PIPE_READ]) { + ErrPrint("Invalid FD\n"); + goto errout; + } + + if (!(cond & G_IO_IN)) { + DbgPrint("PIPE is not valid\n"); + goto errout; + } + + if ((cond & G_IO_ERR) || (cond & G_IO_HUP) || (cond & G_IO_NVAL)) { + DbgPrint("PIPE is not valid\n"); + goto errout; + } + + packet = get_recv_packet(router, &handle, &pid); + if (!packet) { + (void)invoke_disconnected_cb(router, handle); + clear_request_ctx(handle); + } else { + int ret; + + switch (packet_type(packet)) { + case PACKET_ACK: + request = find_request_ctx(handle, packet_seq(packet)); + if (!request) { + ErrPrint("Unknown ack packet\n"); + packet_destroy(packet); + break; + } + + if (request->recv_cb) + request->recv_cb(pid, handle, packet, request->data); + + destroy_request_ctx(request); + break; + case PACKET_REQ_NOACK: + if (!router->service) { + ErrPrint("Service callback is not registered\n"); + break; + } + + result_packet = router->service(handle, pid, packet, router->data); + if (result_packet) { + ErrPrint("Invalid result packet\n"); + packet_destroy(result_packet); + } + break; + case PACKET_REQ: + if (!router->service) { + ErrPrint("Service callback is not registered, client can be block\n"); + break; + } + + result_packet = router->service(handle, pid, packet, router->data); + if (!result_packet) { + ErrPrint("REQUEST Packet has no ACK Packet, client can be block\n"); + break; + } + + ret = put_send_packet(router, handle, packet); + if (ret < 0) + ErrPrint("Failed to send a packet\n"); + break; + case PACKET_ERROR: + default: + ErrPrint("Invalid packet arrived\n"); + router->count_of_dropped_packet++; + break; + } + } + + /*! + * \TODO: + * How could we disconnect from the client? + */ + packet_destroy(packet); + return TRUE; + +errout: + router->service(handle, pid, NULL, router->data); + return FALSE; +} + +/*! + * \NOTE + * Running thread: Main + */ +static struct packet *service_handler(int handle, pid_t pid, const struct packet *packet, void *data) +{ + struct method *table = data; + struct packet *result; + register int i; + + if (!packet) { + DbgPrint("Connection is lost [%d] [%d]\n", handle, pid); + return NULL; + } + + result = NULL; + for (i = 0; table[i].cmd; i++) { + if (strcmp(table[i].cmd, packet_command(packet))) + continue; + + result = table[i].handler(pid, handle, packet); + break; + } + + return result; +} + +/*! + * \NOTE: + * Running thread: Server or Client or Send thread + */ +static inline int select_event(int handle, double timeout) +{ + fd_set set; + int status; + int ret; + + FD_ZERO(&set); + FD_SET(handle, &set); + + status = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + if (status != 0) + ErrPrint("Failed to set cancelstate: %s\n", strerror(status)); + if (timeout > 0.0f) { + struct timeval tv; + + tv.tv_sec = (unsigned long)timeout; + tv.tv_usec = (timeout - (unsigned long)timeout) * 1000000u; + + ret = select(handle + 1, NULL, &set, NULL, &tv); + } else if (timeout == 0.0f) { + ret = select(handle + 1, NULL, &set, NULL, NULL); + } else { + ErrPrint("Invalid timeout: %lf (it must be greater than 0.0)\n", timeout); + status = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + if (status != 0) + ErrPrint("Failed to set cancelstate: %s\n", strerror(status)); + return -EINVAL; + } + + if (ret < 0) { + ret = -errno; + if (errno == EINTR) { + DbgPrint("Select receives INTR\n"); + status = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + return -EAGAIN; + } + + ErrPrint("Error: %s\n", strerror(errno)); + status = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + if (status != 0) + ErrPrint("Failed to set cancelstate: %s\n", strerror(status)); + return ret; + } else if (ret == 0) { + ErrPrint("Timeout expired\n"); + status = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + if (status != 0) + ErrPrint("Failed to set cancelstate: %s\n", strerror(status)); + return -ETIMEDOUT; + } + status = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + if (status != 0) + ErrPrint("Failed to set cancelstate: %s\n", strerror(status)); + + if (!FD_ISSET(handle, &set)) { + ErrPrint("Unexpected handle is toggled\n"); + return -EINVAL; + } + + return 0; +} + +/*! + * \NOTE + * Running thread: Send thread + */ +static void *send_main(void *data) +{ + struct router *router = data; + struct packet *packet; + int handle; + int ret; + + while (1) { + /*! + * \note + * select event has cancel point + */ + ret = select_event(router->send_pipe[PIPE_READ], 0.0f); + if (ret == -EAGAIN) + continue; + + if (ret < 0) + break; + + packet = get_send_packet(router, &handle); + if (!packet) { + DbgPrint("NULL Packet. Terminate thread\n"); + break; + } + + switch (packet_type(packet)) { + case PACKET_REQ: + case PACKET_REQ_NOACK: + ret = com_core_send(handle, (void *)packet_data(packet), packet_size(packet), router->timeout); + break; + default: + ret = -EINVAL; + break; + } + + packet_destroy(packet); + } + + return (void *)ret; +} + +/*! + * \NOTE + * Running thread: Main + */ +static struct router *create_router(const char *sock, int handle, struct method *table) +{ + struct router *router; + GIOChannel *gio; + int ret; + + router = calloc(1, sizeof(*router)); + if (!router) { + ErrPrint("Heap: %s\n", strerror(errno)); + return NULL; + } + + ret = pthread_mutex_init(&router->recv_packet_list_lock, NULL); + if (ret != 0) { + ErrPrint("Mutex creation failed: %s\n", strerror(ret)); + free(router); + return NULL; + } + + ret = pthread_mutex_init(&router->route_list_lock, NULL); + if (ret != 0) { + ErrPrint("Mutex craetion failed: %s\n", strerror(ret)); + ret = pthread_mutex_destroy(&router->recv_packet_list_lock); + if (ret != 0) + ErrPrint("Mutex destroy failed: %s\n", strerror(ret)); + + free(router); + return NULL; + } + + ret = pthread_mutex_init(&router->send_packet_list_lock, NULL); + if (ret != 0) { + ErrPrint("Mutex creation failed: %s\n", strerror(ret)); + + ret = pthread_mutex_destroy(&router->recv_packet_list_lock); + if (ret != 0) + ErrPrint("Mutex destroy failed: %s\n", strerror(ret)); + + ret = pthread_mutex_destroy(&router->route_list_lock); + if (ret != 0) + ErrPrint("Mutex destroy failed: %s\n", strerror(ret)); + + free(router); + return NULL; + } + + router->sock = strdup(sock); + if (!router->sock) { + ErrPrint("Heap: %s\n", strerror(errno)); + ret = pthread_mutex_destroy(&router->send_packet_list_lock); + if (ret != 0) + ErrPrint("Mutex destroy failed: %s\n", strerror(ret)); + + ret = pthread_mutex_destroy(&router->recv_packet_list_lock); + if (ret != 0) + ErrPrint("Mutex destroy failed: %s\n", strerror(ret)); + + ret = pthread_mutex_destroy(&router->route_list_lock); + if (ret != 0) + ErrPrint("Mutex destroy failed: %s\n", strerror(ret)); + + free(router); + return NULL; + } + + ret = pipe2(router->recv_pipe, O_NONBLOCK | O_CLOEXEC); + if (ret < 0) { + ErrPrint("pipe2: %s\n", strerror(errno)); + free(router->sock); + + ret = pthread_mutex_destroy(&router->send_packet_list_lock); + if (ret != 0) + ErrPrint("Mutex destroy failed: %s\n", strerror(ret)); + + ret = pthread_mutex_destroy(&router->recv_packet_list_lock); + if (ret != 0) + ErrPrint("Mutex destroy failed: %s\n", strerror(ret)); + + ret = pthread_mutex_destroy(&router->route_list_lock); + if (ret != 0) + ErrPrint("Mutex destroy failed: %s\n", strerror(ret)); + + free(router); + return NULL; + } + + ret = pipe2(router->send_pipe, O_NONBLOCK | O_CLOEXEC); + if (ret < 0) { + ErrPrint("pipe2: %s\n", strerror(errno)); + free(router->sock); + + if (close(router->recv_pipe[0]) < 0) + ErrPrint("close: %s\n", strerror(errno)); + + if (close(router->recv_pipe[1]) < 0) + ErrPrint("close: %s\n", strerror(errno)); + + ret = pthread_mutex_destroy(&router->send_packet_list_lock); + if (ret != 0) + ErrPrint("Mutex destroy failed: %s\n", strerror(ret)); + + ret = pthread_mutex_destroy(&router->recv_packet_list_lock); + if (ret != 0) + ErrPrint("Mutex destroy failed: %s\n", strerror(ret)); + + ret = pthread_mutex_destroy(&router->route_list_lock); + if (ret != 0) + ErrPrint("Mutex destroy failed: %s\n", strerror(ret)); + + free(router); + return NULL; + } + + router->handle = handle; + router->service = service_handler; + router->data = table; + + gio = g_io_channel_unix_new(router->recv_pipe[PIPE_READ]); + if (!gio) { + if (close(router->recv_pipe[PIPE_READ]) < 0) + ErrPrint("close: %s\n", strerror(errno)); + + if (close(router->recv_pipe[PIPE_WRITE]) < 0) + ErrPrint("close: %s\n", strerror(errno)); + + if (close(router->send_pipe[PIPE_READ]) < 0) + ErrPrint("close: %s\n", strerror(errno)); + + if (close(router->send_pipe[PIPE_WRITE]) < 0) + ErrPrint("close: %s\n", strerror(errno)); + + free(router->sock); + + ret = pthread_mutex_destroy(&router->send_packet_list_lock); + if (ret != 0) + ErrPrint("Mutex destroy failed: %s\n", strerror(ret)); + + ret = pthread_mutex_destroy(&router->recv_packet_list_lock); + if (ret != 0) + ErrPrint("Mutex destroy failed: %s\n", strerror(ret)); + + ret = pthread_mutex_destroy(&router->route_list_lock); + if (ret != 0) + ErrPrint("Mutex destroy failed: %s\n", strerror(ret)); + + free(router); + return NULL; + } + g_io_channel_set_close_on_unref(gio, FALSE); + + router->id = g_io_add_watch(gio, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc)packet_cb, router); + if (router->id == 0) { + GError *err = NULL; + g_io_channel_shutdown(gio, TRUE, &err); + if (err) { + ErrPrint("Shutdown: %s\n", err->message); + g_error_free(err); + } + g_io_channel_unref(gio); + + if (close(router->recv_pipe[PIPE_READ]) < 0) + ErrPrint("close: %s\n", strerror(errno)); + + if (close(router->recv_pipe[PIPE_WRITE]) < 0) + ErrPrint("close: %s\n", strerror(errno)); + + if (close(router->send_pipe[PIPE_READ]) < 0) + ErrPrint("close: %s\n", strerror(errno)); + + if (close(router->send_pipe[PIPE_WRITE]) < 0) + ErrPrint("close: %s\n", strerror(errno)); + + free(router->sock); + + ret = pthread_mutex_destroy(&router->send_packet_list_lock); + if (ret != 0) + ErrPrint("Mutex destroy failed: %s\n", strerror(ret)); + + ret = pthread_mutex_destroy(&router->recv_packet_list_lock); + if (ret != 0) + ErrPrint("Mutex destroy failed: %s\n", strerror(ret)); + + ret = pthread_mutex_destroy(&router->route_list_lock); + if (ret != 0) + ErrPrint("Mutex destroy failed: %s\n", strerror(ret)); + + free(router); + return NULL; + } + + g_io_channel_unref(gio); + + s_info.router_list = dlist_append(s_info.router_list, router); + + ret = pthread_create(&router->send_thid, NULL, send_main, router); + if (ret != 0) { + ErrPrint("Failed to create a send thread: %s\n", strerror(ret)); + dlist_remove_data(s_info.router_list, router); + + g_source_remove(router->id); + + if (close(router->recv_pipe[PIPE_READ]) < 0) + ErrPrint("Close: %s\n", strerror(errno)); + + if (close(router->recv_pipe[PIPE_WRITE]) < 0) + ErrPrint("Close: %s\n", strerror(errno)); + + if (close(router->send_pipe[PIPE_READ]) < 0) + ErrPrint("Close: %s\n", strerror(errno)); + + if (close(router->send_pipe[PIPE_WRITE]) < 0) + ErrPrint("Close: %s\n", strerror(errno)); + + free(router->sock); + + ret = pthread_mutex_destroy(&router->send_packet_list_lock); + if (ret != 0) + ErrPrint("Mutex destroy failed: %s\n", strerror(ret)); + + ret = pthread_mutex_destroy(&router->recv_packet_list_lock); + if (ret != 0) + ErrPrint("Mutex destroy failed: %s\n", strerror(ret)); + + ret = pthread_mutex_destroy(&router->route_list_lock); + if (ret != 0) + ErrPrint("Mutex destroy failed: %s\n", strerror(ret)); + + free(router); + return NULL; + } + + return router; +} + +/*! + * \NOTE + * Running thread: Main + * + * Before call this, every thread which uses this router object must has to be terminated. + */ +static inline __attribute__((always_inline)) int destroy_router(struct router *router) +{ + int handle; + int ret; + + ret = put_send_packet(router, -1, NULL); + DbgPrint("Put NULL Packet to terminate send thread (%d)\n", ret); + + ret = pthread_join(router->send_thid, NULL); + if (ret != 0) + ErrPrint("Join: %s\n", strerror(ret)); + + dlist_remove_data(s_info.router_list, router); + + if (router->id > 0) + g_source_remove(router->id); + + if (close(router->recv_pipe[PIPE_READ]) < 0) + ErrPrint("close: %s\n", strerror(errno)); + + if (close(router->recv_pipe[PIPE_WRITE]) < 0) + ErrPrint("close: %s\n", strerror(errno)); + + if (close(router->send_pipe[PIPE_READ]) < 0) + ErrPrint("close: %s\n", strerror(errno)); + + if (close(router->send_pipe[PIPE_WRITE]) < 0) + ErrPrint("close: %s\n", strerror(errno)); + + free(router->sock); + + ret = pthread_mutex_destroy(&router->send_packet_list_lock); + if (ret != 0) + ErrPrint("Mutex destroy failed: %s\n", strerror(ret)); + + ret = pthread_mutex_destroy(&router->recv_packet_list_lock); + if (ret != 0) + ErrPrint("Mutex destroy failed: %s\n", strerror(ret)); + + ret = pthread_mutex_destroy(&router->route_list_lock); + if (ret != 0) + ErrPrint("Mutex destroy failed: %s\n", strerror(ret)); + + handle = router->handle; + free(router); + + return handle; +} + +/*! + * \NOTE + * Running thread: Client / Server leaf thread + */ +static inline int route_packet(struct router *router, int handle, struct packet *packet) +{ + struct dlist *l; + struct route *route; + unsigned long destination; + unsigned long source; + unsigned long mask; + int processed = 0; + + destination = packet_destination(packet); + source = packet_source(packet); + mask = packet_mask(packet); + + /*! + * \TODO + * Can we believe this source? + * Validate this source address if possible. + */ + + if (destination && source) { + CRITICAL_SECTION_BEGIN(&router->route_list_lock); + + dlist_foreach(router->route_list, l, route) { + if (!route->invalid && (route->address & mask) == (destination & mask)) { + /*! + * \NOTE + * This code is executed in the CRITICAL SECTION + * If possible, we have to do this from the out of the CRITICAL SECTION + * + * This code can makes the system slow. + * + * We have to optimize the processing time in the CRITICAL SECTION + */ + if (put_send_packet(router, route->handle, packet) < 0) + ErrPrint("Failed to send whole packet\n"); + + processed++; + } + } + + CRITICAL_SECTION_END(&router->route_list_lock); + } + + if (processed == 0) { + DbgPrint("Drop a packet\n"); + router->count_of_dropped_packet++; + } + + packet_destroy(packet); + return 0; +} + +/*! + * \NOTE + * Running Threads: Main / Client / Server + */ +static inline int put_send_packet(struct router *router, int handle, struct packet *packet) +{ + if (packet) { + struct packet_item *item; + + item = malloc(sizeof(*item)); + if (!item) { + packet_destroy(packet); + return -ENOMEM; + } + + item->packet = packet; + item->pid = (pid_t)-1; + + CRITICAL_SECTION_BEGIN(&router->send_packet_list_lock); + + router->send_packet_list = dlist_append(router->send_packet_list, item); + + CRITICAL_SECTION_END(&router->send_packet_list_lock); + } + + /*! + * \note + * Producing an event on event pipe + */ + if (write(router->send_pipe[PIPE_WRITE], &handle, sizeof(handle)) != sizeof(handle)) + ErrPrint("Failed to put an event: %s\n", strerror(errno)); + + return 0; +} + +/*! + * \NOTE + * Running thread: Client / Server leaf thread + */ +static inline int put_recv_packet(struct router *router, int handle, struct packet *packet, pid_t pid) +{ + /*! + * If a packet is NULL, the connection is terminated + */ + if (packet) { + struct packet_item *item; + + item = malloc(sizeof(*item)); + if (!item) { + packet_destroy(packet); + return -ENOMEM; + } + + item->packet = packet; + item->pid = pid; + + CRITICAL_SECTION_BEGIN(&router->recv_packet_list_lock); + + router->recv_packet_list = dlist_append(router->recv_packet_list, item); + + CRITICAL_SECTION_END(&router->recv_packet_list_lock); + } + + /*! + * \note + * Producing an event on event pipe + */ + if (write(router->recv_pipe[PIPE_WRITE], &handle, sizeof(handle)) != sizeof(handle)) + ErrPrint("Failed to put an event: %s\n", strerror(errno)); + + return 0; +} + +/*! + * \NOTE + * Running thread: Send thread + */ +static inline struct packet *get_send_packet(struct router *router, int *handle) +{ + struct packet *packet = NULL; + struct dlist *l; + struct packet_item *item; + + CRITICAL_SECTION_BEGIN(&router->send_packet_list_lock); + + l = dlist_nth(router->send_packet_list, 0); + if (l) { + item = dlist_data(l); + router->send_packet_list = dlist_remove(router->send_packet_list, l); + packet = item->packet; + free(item); + } + + CRITICAL_SECTION_END(&router->send_packet_list_lock); + + if (read(router->send_pipe[PIPE_READ], handle, sizeof(*handle)) != sizeof(*handle)) + ErrPrint("Failed to get an event: %s\n", strerror(errno)); + + return packet; +} + +/*! + * \NOTE + * Running thread: Main thread + */ +static inline struct packet *get_recv_packet(struct router *router, int *handle, pid_t *pid) +{ + struct packet *packet = NULL; + struct dlist *l; + struct packet_item *item; + + CRITICAL_SECTION_BEGIN(&router->recv_packet_list_lock); + + l = dlist_nth(router->recv_packet_list, 0); + if (l) { + item = dlist_data(l); + router->recv_packet_list = dlist_remove(router->recv_packet_list, l); + + packet = item->packet; + if (pid) + *pid = item->pid; + + free(item); + } + + CRITICAL_SECTION_END(&router->recv_packet_list_lock); + + /*! + * \note + * Consuming an event from event pipe + * Even if we cannot get the packet(NULL), we should consuming event + * Because the NULL packet means disconnected + */ + if (read(router->recv_pipe[PIPE_READ], handle, sizeof(*handle)) != sizeof(*handle)) + ErrPrint("Failed to get an event: %s\n", strerror(errno)); + + return packet; +} + +static inline int build_packet(int handle, struct recv_ctx *ctx) +{ + char *ptr; + int size; + int ret; + + switch (ctx->state) { + case RECV_STATE_INIT: + ctx->offset = 0; + ctx->packet = NULL; + case RECV_STATE_HEADER: + size = packet_header_size() - ctx->offset; + + ptr = malloc(size); + if (!ptr) { + ErrPrint("Heap: %s\n", strerror(errno)); + return -ENOMEM; + } + + ret = com_core_recv(handle, ptr, size, &ctx->pid, ctx->timeout); + if (ret == 0) { + free(ptr); + return -ECONNRESET; + } else if (ret < 0) { + free(ptr); + return ret; + } + + ctx->packet = packet_build(ctx->packet, ctx->offset, ptr, ret); + free(ptr); + + if (!ctx->packet) + return -EFAULT; + + ctx->offset += ret; + + if (ctx->offset == packet_header_size()) { + if (packet_size(ctx->packet) == ctx->offset) + ctx->state = RECV_STATE_READY; + else + ctx->state = RECV_STATE_BODY; + } + break; + case RECV_STATE_BODY: + size = packet_size(ctx->packet) - ctx->offset; + if (size == 0) { + ctx->state = RECV_STATE_READY; + break; + } + + ptr = malloc(size); + if (!ptr) { + ErrPrint("Heap: %s\n", strerror(errno)); + return -ENOMEM; + } + + ret = com_core_recv(handle, ptr, size, &ctx->pid, ctx->timeout); + if (ret == 0) { + free(ptr); + return -ECONNRESET; + } else if (ret < 0) { + free(ptr); + return ret; + } + + ctx->packet = packet_build(ctx->packet, ctx->offset, ptr, ret); + free(ptr); + if (!ctx->packet) + return -EFAULT; + + ctx->offset += ret; + if (ctx->offset == packet_size(ctx->packet)) + ctx->state = RECV_STATE_READY; + + break; + case RECV_STATE_READY: + default: + break; + } + + return 0; +} + +static int router_common_main(struct router *router, int handle, struct recv_ctx *ctx) +{ + int ret; + while (1) { + /*! + * \note + * select event has cancel point + */ + ret = select_event(handle, ctx->timeout); + if (ret == -EAGAIN) + continue; + + if (ret < 0) { + packet_destroy(ctx->packet); + break; + } + /*! + * Build a packet + * And push it to the packet list + */ + ret = build_packet(handle, ctx); + if (ret != 0) { + packet_destroy(ctx->packet); + break; + } + + if (ctx->state == RECV_STATE_READY) { + /*! + * \NOTE + * + * If the destination address is ZERO, + * Pull up the packet to this server. + */ + if (packet_destination(ctx->packet)) + route_packet(router, handle, ctx->packet); + else + put_recv_packet(router, handle, ctx->packet, ctx->pid); + + ctx->state = RECV_STATE_INIT; + } + } + + put_recv_packet(router, handle, NULL, ctx->pid); + return ret; +} + +/*! + * \NOTE + * Running thread: Server thread + */ +static void *server_main(void *data) +{ + struct client *client = data; + struct router *router = client->router; + struct recv_ctx ctx; + int ret; + + ctx.state = RECV_STATE_INIT; + ctx.packet = NULL; + ctx.timeout = router->timeout; + ctx.pid = (pid_t)-1; + + ret = router_common_main(router, client->handle, &ctx); + return (void *)ret; +} + +/*! + * \NOTE + * Running thread: Client thread + */ +static void *client_main(void *data) +{ + struct router *router = data; + struct recv_ctx ctx; + int ret; + + ctx.state = RECV_STATE_INIT; + ctx.packet = NULL; + ctx.timeout = router->timeout; + ctx.offset = 0; + ctx.pid = (pid_t)-1; + + ret = router_common_main(router, router->handle, &ctx); + return (void *)ret; +} + +/*! + * \NOTE + * Running thread: Main + */ +static gboolean accept_cb(GIOChannel *src, GIOCondition cond, gpointer data) +{ + int handle; + int fd; + struct router *router = data; + struct client *client; + int status; + + handle = g_io_channel_unix_get_fd(src); + + if (!(cond & G_IO_IN)) { + ErrPrint("Accept socket closed\n"); + (void)invoke_error_cb(router, handle); + return FALSE; + } + + if ((cond & G_IO_ERR) || (cond & G_IO_HUP) || (cond & G_IO_NVAL)) { + DbgPrint("Socket connection is lost\n"); + (void)invoke_error_cb(router, handle); + return FALSE; + } + + DbgPrint("New connection is made: socket(%d)\n", handle); + fd = secure_socket_get_connection_handle(handle); + if (fd < 0) { + ErrPrint("Failed to get client fd from socket\n"); + (void)invoke_error_cb(router, handle); + return FALSE; + } + + if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) + ErrPrint("Error: %s\n", strerror(errno)); + + if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) + ErrPrint("Error: %s\n", strerror(errno)); + + client = calloc(1, sizeof(*client)); + if (!client) { + ErrPrint("Heap: %s\n", strerror(errno)); + secure_socket_destroy_handle(fd); + /*! + * \NOTE + * Just return TRUE to keep this accept handler + */ + return TRUE; + } + + client->handle = fd; + client->router = router; + router->info.server.client_list = dlist_append(router->info.server.client_list, client); + + status = pthread_create(&client->thid, NULL, server_main, client); + if (status != 0) { + ErrPrint("Thread creation failed: %s\n", strerror(status)); + dlist_remove_data(router->info.server.client_list, client); + secure_socket_destroy_handle(client->handle); + free(client); + /*! + * \NOTE + * Just return TRUE to keep this accept handler + */ + return TRUE; + } + + (void)invoke_connected_cb(router, fd); + return TRUE; +} + +/*! + * \NOTE + * Running thread: Main + */ +EAPI int com_core_packet_router_server_init(const char *sock, double timeout, struct method *table) +{ + int handle; + struct router *router; + GIOChannel *gio; + + handle = secure_socket_create_server(sock); + if (handle < 0) + return handle; + + router = create_router(sock, handle, table); + if (!router) { + secure_socket_destroy_handle(handle); + return -ENOMEM; + } + + router->timeout = timeout; + router->is_server = 1; + + gio = g_io_channel_unix_new(router->handle); + if (!gio) { + handle = destroy_router(router); + secure_socket_destroy_handle(handle); + return -EIO; + } + + g_io_channel_set_close_on_unref(gio, FALSE); + + router->info.server.accept_id = g_io_add_watch(gio, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc)accept_cb, router); + if (router->info.server.accept_id == 0) { + GError *err = NULL; + g_io_channel_shutdown(gio, TRUE, &err); + if (err) { + ErrPrint("Shutdown: %s\n", err->message); + g_error_free(err); + } + g_io_channel_unref(gio); + + handle = destroy_router(router); + secure_socket_destroy_handle(handle); + return -EIO; + } + + g_io_channel_unref(gio); + return router->handle; +} + +/*! + * \NOTE + * Running thread: Main + */ +EAPI int com_core_packet_router_client_init(const char *sock, double timeout, struct method *table) +{ + struct router *router; + int handle; + int status; + + handle = secure_socket_create_client(sock); + if (handle < 0) + return handle; + + router = create_router(sock, handle, table); + if (!router) { + secure_socket_destroy_handle(handle); + return -ENOMEM; + } + + router->timeout = timeout; + router->is_server = 0; + + status = pthread_mutex_init(&router->recv_packet_list_lock, NULL); + if (status != 0) { + ErrPrint("Mutex creation failed: %s\n", strerror(status)); + + handle = destroy_router(router); + secure_socket_destroy_handle(handle); + return -EFAULT; + } + + status = pthread_mutex_init(&router->route_list_lock, NULL); + if (status != 0) { + ErrPrint("Mutex creation failed: %s\n", strerror(status)); + handle = destroy_router(router); + secure_socket_destroy_handle(handle); + return -EFAULT; + } + + status = pthread_create(&router->info.client.thid, NULL, client_main, router); + if (status != 0) { + ErrPrint("Thread creation failed: %s\n", strerror(status)); + handle = destroy_router(router); + secure_socket_destroy_handle(handle); + return -EFAULT; + } + + (void)invoke_connected_cb(router, router->handle); + return router->handle; +} + +EAPI void *com_core_packet_router_server_fini(int handle) +{ + struct router *router; + void *data; + int status; + struct dlist *l; + struct dlist *n; + + struct client *client; + struct route *route; + + void *ret; + + router = find_router_by_handle(handle); + if (!router) { + ErrPrint("No such router\n"); + return NULL; + } + + if (!router->is_server) { + ErrPrint("Invalid object\n"); + return NULL; + } + + if(router->info.server.accept_id > 0) + g_source_remove(router->info.server.accept_id); + + dlist_foreach_safe(router->info.server.client_list, l, n, client) { + router->info.server.client_list = dlist_remove(router->info.server.client_list, l); + + status = pthread_cancel(client->thid); + if (status != 0) + ErrPrint("Failed to cacnel a thread: %s\n", strerror(errno)); + + ret = NULL; + status = pthread_join(client->thid, &ret); + if (status != 0) + ErrPrint("Failed to cancel a thread: %s\n", strerror(errno)); + + if (ret == PTHREAD_CANCELED) { + DbgPrint("Thread is canceled\n"); + clear_request_ctx(client->handle); + } + + secure_socket_destroy_handle(client->handle); + free(client); + } + + dlist_foreach_safe(router->route_list, l, n, route) { + router->route_list = dlist_remove(router->route_list, l); + free(route); + } + + data = router->data; + + handle = destroy_router(router); + secure_socket_destroy_handle(handle); + + return data; +} + +/*! + * \NOTE + * Running thread: Main + */ +EAPI void *com_core_packet_router_client_fini(int handle) +{ + struct router *router; + void *data; + int status; + struct dlist *l; + struct dlist *n; + + struct route *route; + + void *ret = NULL; + + router = find_router_by_handle(handle); + if (!router) { + ErrPrint("No such router\n"); + return NULL; + } + + if (router->is_server) { + ErrPrint("Invalid object\n"); + return NULL; + } + + status = pthread_cancel(router->info.client.thid); + if (status != 0) + ErrPrint("Failed to cancel a thread: %s\n", strerror(errno)); + + status = pthread_join(router->info.client.thid, &ret); + if (status != 0) + ErrPrint("Failed to join a thread: %s\n", strerror(errno)); + + if (ret == PTHREAD_CANCELED) { + DbgPrint("Thread is canceled\n"); + clear_request_ctx(router->handle); + } + + dlist_foreach_safe(router->route_list, l, n, route) { + router->route_list = dlist_remove(router->route_list, l); + free(route); + } + + data = router->data; + + handle = destroy_router(router); + secure_socket_destroy_handle(handle); + + return data; +} + +/*! + * \NOTE + * Running thread: Main + */ +EAPI int com_core_packet_router_async_send(int handle, struct packet *packet, double timeout, int (*recv_cb)(pid_t pid, int handle, const struct packet *packet, void *data), void *data) +{ + struct request_ctx *ctx; + struct router *router; + int ret; + + if (handle < 0 || !packet) + return -EINVAL; + + if (packet_type(packet) != PACKET_REQ) { + ErrPrint("Invalid packet - should be PACKET_REQ\n"); + return -EINVAL; + } + + router = find_router_by_handle(handle); + if (!router) { + ErrPrint("Router is not available\n"); + return -EINVAL; + } + + ctx = create_request_ctx(handle); + if (!ctx) + return -ENOMEM; + + ctx->recv_cb = recv_cb; + ctx->data = data; + ctx->packet = packet_ref(packet); + + ret = put_send_packet(router, handle, packet); + if (ret < 0) + destroy_request_ctx(ctx); + + return ret; +} + +/*! + * \NOTE + * Running thread: Main + */ +EAPI int com_core_packet_router_send_only(int handle, struct packet *packet) +{ + struct router *router; + + if (handle < 0 || !packet || packet_type(packet) != PACKET_REQ_NOACK) + return -EINVAL; + + router = find_router_by_handle(handle); + if (!router) { + ErrPrint("Rouer is not available\n"); + return -EINVAL; + } + + return put_send_packet(router, handle, packet); +} + +/*! + * \NOTE + * Running thread: Main + */ +EAPI struct packet *com_core_packet_router_oneshot_send(const char *addr, struct packet *packet, double timeout) +{ + return com_core_packet_oneshot_send(addr, packet, timeout); +} + +/*! + * \NOTE + * Running thread: Main + */ +EAPI int com_core_packet_router_add_route(int handle, unsigned long address, int h) +{ + struct router *router; + struct route *route; + struct route *tmp; + struct dlist *l; + int found = 0; + + if (handle < 0 || !address || h < 0) + return -EINVAL; + + router = find_router_by_handle(handle); + if (!router) { + ErrPrint("Router is not exists\n"); + return -ENOENT; + } + + route = malloc(sizeof(*route)); + if (!route) { + ErrPrint("Heap: %s\n", strerror(errno)); + return -ENOMEM; + } + + route->address = address; + route->handle = h; + route->invalid = 0; + + CRITICAL_SECTION_BEGIN(&router->route_list_lock); + + dlist_foreach(router->route_list, l, tmp) { + if (tmp->address == address) { + found = 1; + break; + } + } + + if (!found) + router->route_list = dlist_append(router->route_list, route); + + CRITICAL_SECTION_END(&router->route_list_lock); + + if (found) { + free(route); + return -EEXIST; + } + + return 0; +} + +/*! + * \NOTE + * Running thread: Main + */ +EAPI int com_core_packet_router_del_route(int handle, unsigned long address) +{ + struct router *router; + struct route *route; + struct dlist *l; + struct dlist *n; + int found = 0; + + if (handle < 0 || !address) + return -EINVAL; + + router = find_router_by_handle(handle); + if (!router) { + ErrPrint("Router is not exists\n"); + return -ENOENT; + } + + CRITICAL_SECTION_BEGIN(&router->route_list_lock); + + dlist_foreach_safe(router->route_list, l, n, route) { + if (route->address != address) + continue; + + router->route_list = dlist_remove(router->route_list, l); + + DbgPrint("Delete an entry from the table (%lu : %d)\n", route->address, route->handle); + free(route); + + found = 1; + break; + } + + CRITICAL_SECTION_END(&router->route_list_lock); + + return found ? 0 : -ENOENT; +} + +/*! + * \NOTE + * Running thread: Main + */ +EAPI int com_core_packet_router_update_route(int handle, unsigned long address, int h) +{ + struct router *router; + struct route *route; + struct dlist *l; + int found = 0; + + if (handle < 0 || !address || h < 0) + return -EINVAL; + + router = find_router_by_handle(handle); + if (!router) { + ErrPrint("Router is not exists\n"); + return -ENOENT; + } + + CRITICAL_SECTION_BEGIN(&router->route_list_lock); + + dlist_foreach(router->route_list, l, route) { + if (route->address != address) + continue; + + route->handle = h; + route->invalid = 0; + found = 1; + break; + } + + CRITICAL_SECTION_END(&router->route_list_lock); + + return found ? 0 : -ENOENT; +} + +/*! + * \NOTE + * Running thread: Main + */ +EAPI int com_core_packet_router_add_event_callback(enum com_core_route_event_type type, int (*evt_cb)(int handle, void *data), void *data) +{ + struct event_item *item; + + if (!evt_cb) { + ErrPrint("Invalid event callback\n"); + return -EINVAL; + } + + item = malloc(sizeof(*item)); + if (!item) { + ErrPrint("Heap: %s\n", strerror(errno)); + return -ENOMEM; + } + + item->evt_cb = evt_cb; + item->data = data; + + switch (type) { + case COM_CORE_ROUTE_CONNECTED: + s_info.connected_list = dlist_prepend(s_info.connected_list, item); + break; + case COM_CORE_ROUTE_DISCONNECTED: + s_info.disconnected_list = dlist_prepend(s_info.disconnected_list, item); + break; + case COM_CORE_ROUTE_ERROR: + s_info.error_list = dlist_prepend(s_info.error_list, item); + break; + default: + free(item); + return -EINVAL; + } + + return 0; +} + +/*! + * \NOTE + * Running thread: Main + */ +EAPI int com_core_packet_router_del_event_callback(enum com_core_route_event_type type, int (*evt_cb)(int handle, void *data), void *data) +{ + struct dlist *l; + struct dlist *n; + struct event_item *item; + + switch (type) { + case COM_CORE_ROUTE_CONNECTED: + dlist_foreach_safe(s_info.connected_list, l, n, item) { + if (item->evt_cb == evt_cb && item->data == data) { + s_info.connected_list = dlist_remove(s_info.connected_list, l); + free(item); + return 0; + } + } + break; + case COM_CORE_ROUTE_DISCONNECTED: + dlist_foreach_safe(s_info.disconnected_list, l, n, item) { + if (item->evt_cb == evt_cb && item->data == data) { + s_info.disconnected_list = dlist_remove(s_info.disconnected_list, l); + free(item); + return 0; + } + } + break; + case COM_CORE_ROUTE_ERROR: + dlist_foreach_safe(s_info.error_list, l, n, item) { + if (item->evt_cb == evt_cb && item->data == data) { + s_info.error_list = dlist_remove(s_info.error_list, l); + free(item); + return 0; + } + } + break; + default: + ErrPrint("Invalid event type\n"); + return -EINVAL; + } + + return -ENOENT; +} + +#undef _GNU_SOURCE +/* End of a file */ diff --git a/src/com-core_packet.c b/src/com-core_packet.c new file mode 100644 index 0000000..2d69011 --- /dev/null +++ b/src/com-core_packet.c @@ -0,0 +1,635 @@ +/* + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * 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. + * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "debug.h" +#include "com-core.h" +#include "com-core_thread.h" +#include "packet.h" +#include "secure_socket.h" +#include "dlist.h" +#include "com-core_packet.h" +#include "util.h" + +#define DEFAULT_TIMEOUT 2.0f + +static struct info { + struct dlist *recv_list; + struct dlist *request_list; + char *addr; + + struct { + int (*server_create)(const char *addr, int is_sync, int (*service_cb)(int fd, void *data), void *data); + int (*client_create)(const char *addr, int is_sync, int (*service_cb)(int fd, void *data), void *data); + int (*server_destroy)(int handle); + int (*client_destroy)(int handle); + + int (*recv)(int handle, char *buffer, int size, int *sender_pid, double timeout); + int (*send)(int handle, const char *buffer, int size, double timeout); + } vtable; + + int initialized; +} s_info = { + .recv_list = NULL, + .request_list = NULL, + .addr = NULL, + .vtable = { + .server_create = com_core_server_create, + .client_create = com_core_client_create, + .server_destroy = com_core_server_destroy, + .client_destroy = com_core_client_destroy, + .recv = com_core_recv, + .send = com_core_send, + }, + .initialized = 0, +}; + +struct request_ctx { + pid_t pid; + int handle; + + struct packet *packet; + int (*recv_cb)(pid_t pid, int handle, const struct packet *packet, void *data); + void *data; +}; + +struct recv_ctx { + enum { + RECV_STATE_INIT, + RECV_STATE_HEADER, + RECV_STATE_BODY, + RECV_STATE_READY, + } state; + int handle; + int offset; + pid_t pid; + struct packet *packet; + double timeout; +}; + +static inline struct request_ctx *find_request_ctx(int handle, double seq) +{ + struct request_ctx *ctx; + struct dlist *l; + + dlist_foreach(s_info.request_list, l, ctx) { + if (ctx->handle == handle && packet_seq(ctx->packet) == seq) { + return ctx; + } + } + + return NULL; +} + +static inline void destroy_request_ctx(struct request_ctx *ctx) +{ + packet_unref(ctx->packet); + dlist_remove_data(s_info.request_list, ctx); + free(ctx); +} + +static inline struct request_ctx *create_request_ctx(int handle) +{ + struct request_ctx *ctx; + + ctx = malloc(sizeof(*ctx)); + if (!ctx) { + ErrPrint("Heap: %s\n", strerror(errno)); + return NULL; + } + + ctx->handle = handle; + ctx->pid = (pid_t)-1; + ctx->packet = NULL; + ctx->recv_cb = NULL; + ctx->data = NULL; + + s_info.request_list = dlist_append(s_info.request_list, ctx); + return ctx; +} + +static inline struct recv_ctx *find_recv_ctx(int handle) +{ + struct recv_ctx *ctx; + struct dlist *l; + + dlist_foreach(s_info.recv_list, l, ctx) { + if (ctx->handle == handle) + return ctx; + } + + return NULL; +} + +static inline void destroy_recv_ctx(struct recv_ctx *ctx) +{ + dlist_remove_data(s_info.recv_list, ctx); + packet_destroy(ctx->packet); + free(ctx); +} + +static inline struct recv_ctx *create_recv_ctx(int handle, double timeout) +{ + struct recv_ctx *ctx; + + ctx = malloc(sizeof(*ctx)); + if (!ctx) { + ErrPrint("heap: %s\n", strerror(errno)); + return NULL; + } + + ctx->state = RECV_STATE_INIT, + ctx->offset = 0; + ctx->packet = NULL; + ctx->handle = handle; + ctx->pid = (pid_t)-1; + ctx->timeout = timeout; + + s_info.recv_list = dlist_append(s_info.recv_list, ctx); + return ctx; +} + +static inline int packet_ready(int handle, const struct recv_ctx *receive, struct method *table) +{ + struct request_ctx *request; + double sequence; + struct packet *result; + register int i; + int ret; + + ret = 0; + + switch (packet_type(receive->packet)) { + case PACKET_ACK: + sequence = packet_seq(receive->packet); + request = find_request_ctx(handle, sequence); + if (!request) { + ErrPrint("This is not requested packet (%s)\n", packet_command(receive->packet)); + break; + } + + if (request->recv_cb) + request->recv_cb(receive->pid, handle, receive->packet, request->data); + + destroy_request_ctx(request); + break; + case PACKET_REQ: + for (i = 0; table[i].cmd; i++) { + if (strcmp(table[i].cmd, packet_command(receive->packet))) + continue; + + result = table[i].handler(receive->pid, handle, receive->packet); + if (result) { + ret = s_info.vtable.send(handle, (void *)packet_data(result), packet_size(result), DEFAULT_TIMEOUT); + if (ret < 0) { + ErrPrint("Failed to send an ack packet\n"); + } else { + ret = 0; + } + packet_destroy(result); + } + break; + } + + break; + case PACKET_REQ_NOACK: + for (i = 0; table[i].cmd; i++) { + if (strcmp(table[i].cmd, packet_command(receive->packet))) + continue; + + result = table[i].handler(receive->pid, handle, receive->packet); + if (result) + packet_destroy(result); + } + break; + default: + break; + } + + /*! + * Return negative value will make call the disconnected_cb + */ + return ret; +} + +static int client_disconnected_cb(int handle, void *data) +{ + struct recv_ctx *receive; + struct request_ctx *request; + struct dlist *l; + struct dlist *n; + pid_t pid = (pid_t)-1; + + receive = find_recv_ctx(handle); + if (receive) { + pid = receive->pid; + destroy_recv_ctx(receive); + } + + DbgPrint("Clean up all requests and a receive context for handle(%d) for pid(%d)\n", handle, pid); + + dlist_foreach_safe(s_info.request_list, l, n, request) { + if (request->handle != handle) + continue; + + if (request->recv_cb) + request->recv_cb(pid, handle, NULL, request->data); + + destroy_request_ctx(request); + } + + return 0; +} + +static int service_cb(int handle, void *data) +{ + struct recv_ctx *receive; + pid_t pid; + int ret; + int size; + char *ptr; + + receive = find_recv_ctx(handle); + if (!receive) { + receive = create_recv_ctx(handle, DEFAULT_TIMEOUT); + if (!receive) { + ErrPrint("Couldn't find or create a receive context\n"); + return -EIO; + } + } + + switch (receive->state) { + case RECV_STATE_INIT: + receive->state = RECV_STATE_HEADER; + receive->offset = 0; + case RECV_STATE_HEADER: + size = packet_header_size() - receive->offset; + /*! + * \note + * Getting header + */ + ptr = malloc(size); + if (!ptr) { + ErrPrint("Heap: %s\n", strerror(errno)); + return -ENOMEM; + } + + ret = s_info.vtable.recv(handle, ptr, size, &pid, receive->timeout); + if (ret < 0) { + ErrPrint("Recv[%d], pid[%d :: %d]\n", ret, receive->pid, pid); + free(ptr); + return -EIO; /*!< Return negative value will invoke the client_disconnected_cb */ + } else if (ret > 0) { + if (receive->pid != -1 && receive->pid != pid) { + ErrPrint("Recv[%d], pid[%d :: %d]\n", ret, receive->pid, pid); + free(ptr); + return -EIO; /*!< Return negative value will invoke the client_disconnected_cb */ + } + + receive->pid = pid; + receive->packet = packet_build(receive->packet, receive->offset, ptr, ret); + free(ptr); + + if (!receive->packet) { + ErrPrint("Built packet is not valid\n"); + return -EFAULT; /*!< Return negative value will invoke the client_disconnected_cb */ + } + + receive->offset += ret; + + if (receive->offset == packet_header_size()) { + if (packet_size(receive->packet) == receive->offset) + receive->state = RECV_STATE_READY; + else + receive->state = RECV_STATE_BODY; + } + } else { + DbgPrint("ZERO bytes receives(%d)\n", pid); + free(ptr); + return -ECONNRESET; + } + break; + case RECV_STATE_BODY: + size = packet_size(receive->packet) - receive->offset; + if (size == 0) { + receive->state = RECV_STATE_READY; + break; + } + /*! + * \note + * Getting body + */ + ptr = malloc(size); + if (!ptr) { + ErrPrint("Heap: %s\n", strerror(errno)); + return -ENOMEM; + } + + ret = s_info.vtable.recv(handle, ptr, size, &pid, receive->timeout); + if (ret < 0) { + ErrPrint("Recv[%d], pid[%d :: %d]\n", ret, receive->pid, pid); + free(ptr); + return -EIO; + } else if (ret > 0) { + if (receive->pid != pid) { + ErrPrint("Recv[%d], pid[%d :: %d]\n", ret, receive->pid, pid); + free(ptr); + return -EIO; + } + + receive->packet = packet_build(receive->packet, receive->offset, ptr, ret); + free(ptr); + + if (!receive->packet) { + ErrPrint("Built packet is not valid\n"); + return -EFAULT; + } + + receive->offset += ret; + + if (receive->offset == packet_size(receive->packet)) { + receive->state = RECV_STATE_READY; + } + } else { + DbgPrint("ZERO bytes receives(%d)\n", pid); + free(ptr); + return -ECONNRESET; + } + + break; + case RECV_STATE_READY: + default: + break; + } + + if (receive->state == RECV_STATE_READY) { + ret = packet_ready(handle, receive, data); + if (ret == 0) + destroy_recv_ctx(receive); + /*! + * if ret is negative value, disconnected_cb will be called after this function + */ + } else { + ret = 0; + } + + return ret; +} + +EAPI int com_core_packet_async_send(int handle, struct packet *packet, double timeout, int (*recv_cb)(pid_t pid, int handle, const struct packet *packet, void *data), void *data) +{ + int ret; + struct request_ctx *ctx; + + if (handle < 0 || !packet) { + ErrPrint("Invalid argument\n"); + return -EINVAL; + } + + if (packet_type(packet) != PACKET_REQ) { + ErrPrint("Invalid packet - should be PACKET_REQ\n"); + return -EINVAL; + } + + ctx = create_request_ctx(handle); + if (!ctx) + return -ENOMEM; + + ctx->recv_cb = recv_cb; + ctx->data = data; + ctx->packet = packet_ref(packet); + + ret = s_info.vtable.send(handle, (void *)packet_data(packet), packet_size(packet), DEFAULT_TIMEOUT); + if (ret != packet_size(packet)) { + ErrPrint("Send failed. %d <> %d (handle: %d)\n", ret, packet_size(packet), handle); + destroy_request_ctx(ctx); + return -EIO; + } + + return 0; +} + +EAPI int com_core_packet_send_only(int handle, struct packet *packet) +{ + int ret; + + if (packet_type(packet) != PACKET_REQ_NOACK) { + ErrPrint("Invalid type - should be PACKET_REQ_NOACK (%p)\n", packet); + return -EINVAL; + } + + ret = s_info.vtable.send(handle, (void *)packet_data(packet), packet_size(packet), DEFAULT_TIMEOUT); + if (ret != packet_size(packet)) { + ErrPrint("Failed to send whole packet\n"); + return -EIO; + } + + return 0; +} + +EAPI struct packet *com_core_packet_oneshot_send(const char *addr, struct packet *packet, double timeout) +{ + int ret; + int fd; + pid_t pid; + int offset; + struct packet *result = NULL; + void *ptr; + int size; + + if (!addr || !packet) { + ErrPrint("Invalid argument\n"); + return NULL; + } + + fd = secure_socket_create_client(addr); + if (fd < 0) + return NULL; + + DbgPrint("FD: %d\n", fd); + + if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) + ErrPrint("fcntl: %s\n", strerror(errno)); + + if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) + ErrPrint("Error: %s\n", strerror(errno)); + + ret = com_core_send(fd, (char *)packet_data(packet), packet_size(packet), DEFAULT_TIMEOUT); + if (ret < 0) + goto out; + + DbgPrint("Sent: %d bytes (%d bytes)\n", ret, packet_size(packet)); + + ptr = malloc(packet_header_size()); + if (!ptr) { + ErrPrint("Heap: %s\n", strerror(errno)); + goto out; + } + + offset = 0; + ret = com_core_recv(fd, (char *)ptr, packet_header_size(), &pid, timeout); + if (ret <= 0) { + DbgPrint("Recv returns %s\n", ret); + free(ptr); + goto out; + } else { + DbgPrint("Recv'd size: %d (header: %d) pid: %d\n", ret, packet_header_size(), pid); + result = packet_build(result, offset, ptr, ret); + offset += ret; + free(ptr); + if (!result) { + ErrPrint("Failed to build a packet\n"); + goto out; + } + } + + size = packet_payload_size(result); + DbgPrint("Payload size: %d\n", size); + if (size < 0) { + packet_destroy(result); + result = NULL; + goto out; + } + + if (size == 0) { + DbgPrint("Has no payload\n"); + goto out; + } + + ptr = malloc(size); + if (!ptr) { + ErrPrint("Heap: %s\n", strerror(errno)); + packet_destroy(result); + result = NULL; + goto out; + } + + ret = com_core_recv(fd, (char *)ptr, size, &pid, timeout); + if (ret <= 0) { + DbgPrint("Recv returns %s\n", ret); + free(ptr); + packet_destroy(result); + result = NULL; + } else { + DbgPrint("Recv'd %d bytes (pid: %d)\n", ret, pid); + result = packet_build(result, offset, ptr, ret); + offset += ret; + free(ptr); + } + +out: + secure_socket_destroy_handle(fd); + DbgPrint("Close connection: %d\n", fd); + return result; +} + +static inline int com_core_packet_init(void) +{ + int ret; + if (s_info.initialized) + return 0; + + ret = com_core_add_event_callback(CONNECTOR_DISCONNECTED, client_disconnected_cb, NULL); + s_info.initialized = (ret == 0); + return ret; +} + +static inline int com_core_packet_fini(void) +{ + if (!s_info.initialized) + return 0; + + s_info.initialized = 0; + com_core_del_event_callback(CONNECTOR_DISCONNECTED, client_disconnected_cb, NULL); + return 0; +} + +EAPI int com_core_packet_client_init(const char *addr, int is_sync, struct method *table) +{ + int ret; + + ret = com_core_packet_init(); + if (ret < 0) + return ret; + + ret = s_info.vtable.client_create(addr, is_sync, service_cb, table); + if (ret < 0) + com_core_packet_fini(); + + return ret; +} + +EAPI int com_core_packet_client_fini(int handle) +{ + s_info.vtable.client_destroy(handle); + com_core_packet_fini(); + return 0; +} + +EAPI int com_core_packet_server_init(const char *addr, struct method *table) +{ + int ret; + + ret = com_core_packet_init(); + if (ret < 0) + return ret; + + ret = s_info.vtable.server_create(addr, 0, service_cb, table); + if (ret < 0) + com_core_packet_fini(); + + return ret; +} + +EAPI int com_core_packet_server_fini(int handle) +{ + s_info.vtable.server_destroy(handle); + com_core_packet_fini(); + return 0; +} + +EAPI void com_core_packet_use_thread(int flag) +{ + if (!!flag) { + s_info.vtable.server_create = com_core_thread_server_create; + s_info.vtable.client_create = com_core_thread_client_create; + s_info.vtable.server_destroy = com_core_thread_server_destroy; + s_info.vtable.client_destroy = com_core_thread_client_destroy; + s_info.vtable.recv = com_core_thread_recv; + s_info.vtable.send = com_core_thread_send; + } else { + s_info.vtable.server_create = com_core_server_create; + s_info.vtable.client_create = com_core_client_create; + s_info.vtable.server_destroy = com_core_server_destroy; + s_info.vtable.client_destroy = com_core_client_destroy; + s_info.vtable.recv = com_core_recv; + s_info.vtable.send = com_core_send; + } +} + +/* End of a file */ diff --git a/src/com-core_thread.c b/src/com-core_thread.c new file mode 100644 index 0000000..64d09e1 --- /dev/null +++ b/src/com-core_thread.c @@ -0,0 +1,948 @@ +/* + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * 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. + * +*/ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "dlist.h" +#include "secure_socket.h" +#include "debug.h" +#include "com-core.h" +#include "com-core_internal.h" +#include "util.h" + +int errno; +#define PIPE_READ 0 +#define PIPE_WRITE 1 +#define EVENT_READY 'a' +#define EVENT_TERM 'e' + +static struct { + struct dlist *tcb_list; + struct dlist *server_list; +} s_info = { + .tcb_list = NULL, + .server_list = NULL, +}; + +/*! + * \brief Representing the Server Object + */ +struct server { + int (*service_cb)(int fd, void *data); + void *data; + + guint id; + int handle; +}; + +/*! + * \brief This is used to holds a packet + */ +struct chunk { + char *data; + int offset; + int size; + pid_t pid; +}; + +/*! + * \brief Thread Control Block + */ +struct tcb { + pthread_t thid; + int handle; + struct dlist *chunk_list; + int evt_pipe[2]; + pthread_mutex_t chunk_lock; + guint id; /*!< g_io_watch */ + + int server_handle; + + int (*service_cb)(int fd, void *data); + void *data; + + int terminated; +}; + +/*! + * \NOTE + * Running thread: Main + */ +static inline void server_destroy(struct server *server) +{ + dlist_remove_data(s_info.server_list, server); + + if (server->id > 0) + g_source_remove(server->id); + + if (server->handle > 0) + secure_socket_destroy_handle(server->handle); + + free(server); +} + +/*! + * \NOTE + * Running thread: Main + */ +static inline struct server *server_create(int handle, int (*service_cb)(int fd, void *data), void *data) +{ + struct server *server; + + server = malloc(sizeof(*server)); + if (!server) { + ErrPrint("Heap: %s\n", strerror(errno)); + return NULL; + } + + server->handle = handle; + server->service_cb = service_cb; + server->data = data; + + s_info.server_list = dlist_append(s_info.server_list, server); + return server; +} + +/*! + * \NOTE + * Running thread: Main + */ +static inline void destroy_chunk(struct chunk *chunk) +{ + free(chunk->data); + free(chunk); +} + +/*! + * \NOTE + * Running thread: Main + */ +static inline void terminate_thread(struct tcb *tcb) +{ + void *res = NULL; + int status; + + status = pthread_cancel(tcb->thid); + if (status != 0) + ErrPrint("Failed to cancel the thread: %s\n", strerror(status)); + + status = pthread_join(tcb->thid, &res); + if (status != 0) + ErrPrint("Join: %s\n", strerror(status)); + + if (res == PTHREAD_CANCELED) { + struct dlist *l; + struct dlist *n; + struct chunk *chunk; + + dlist_foreach_safe(tcb->chunk_list, l, n, chunk) { + /*! + * Discarding all packets + */ + tcb->chunk_list = dlist_remove(tcb->chunk_list, l); + destroy_chunk(chunk); + } + } +} + +/*! + * \NOTE + * Running thread: Main + */ +static inline void chunk_remove(struct tcb *tcb, struct chunk *chunk) +{ + char event_ch; + + /* Consuming the event */ + if (read(tcb->evt_pipe[PIPE_READ], &event_ch, sizeof(event_ch)) != sizeof(event_ch)) { + ErrPrint("Failed to get readsize\n"); + return; + } + + CRITICAL_SECTION_BEGIN(&tcb->chunk_lock); + + dlist_remove_data(tcb->chunk_list, chunk); + + CRITICAL_SECTION_END(&tcb->chunk_lock); + + destroy_chunk(chunk); +} + +/*! + * \NOTE + * Running thread: Other + */ +static inline void chunk_append(struct tcb *tcb, struct chunk *chunk) +{ + char event_ch = EVENT_READY; + int ret; + + CRITICAL_SECTION_BEGIN(&tcb->chunk_lock); + + tcb->chunk_list = dlist_append(tcb->chunk_list, chunk); + + CRITICAL_SECTION_END(&tcb->chunk_lock); + + ret = write(tcb->evt_pipe[PIPE_WRITE], &event_ch, sizeof(event_ch)); + if (ret < 0) { + ErrPrint("write: %s\n", strerror(errno)); + return; + } + + if (ret != sizeof(event_ch)) + ErrPrint("Failed to trigger reader\n"); +} + +/*! + * \NOTE + * Running thread: Main + */ +static inline int wait_event(struct tcb *tcb, double timeout) +{ + fd_set set; + int ret; + + if (tcb->terminated) + return -ECONNRESET; + + FD_ZERO(&set); + FD_SET(tcb->evt_pipe[PIPE_READ], &set); + + if (timeout > 0.0f) { + struct timeval tv; + tv.tv_sec = (unsigned long)timeout; + tv.tv_usec = (timeout - (unsigned long)timeout) * 1000000u; + ret = select(tcb->evt_pipe[PIPE_READ] + 1, &set, NULL, NULL, &tv); + } else if (timeout == 0.0f) { + ret = select(tcb->evt_pipe[PIPE_READ] + 1, &set, NULL, NULL, NULL); + } else { + ErrPrint("Invalid timeout: %lf (it must be greater than 0.0)\n", timeout); + return -EINVAL; + } + + if (ret < 0) { + ret = -errno; + if (errno == EINTR) { + DbgPrint("Select receives INTR\n"); + return -EAGAIN; + } + + ErrPrint("Error: %s\n", strerror(errno)); + return ret; + } else if (ret == 0) { + ErrPrint("Timeout expired\n"); + return -ETIMEDOUT; + } + + if (!FD_ISSET(tcb->evt_pipe[PIPE_READ], &set)) { + ErrPrint("Unexpected handle is toggled\n"); + return -EINVAL; + } + + return 0; +} + +/*! + * \NOTE + * Running thread: Main + */ +static inline struct chunk *create_chunk(int size) +{ + struct chunk *chunk; + + chunk = malloc(sizeof(*chunk)); + if (!chunk) { + ErrPrint("Heap: %s\n", strerror(errno)); + return NULL; + } + + chunk->data = malloc(size); + if (!chunk->data) { + ErrPrint("Heap: %s\n", strerror(errno)); + free(chunk); + return NULL; + } + + chunk->pid = (pid_t)-1; + chunk->size = size; + chunk->offset = 0; + return chunk; +} + +/*! + * \NOTE + * Running thread: Other + */ +static void *client_cb(void *data) +{ + struct tcb *tcb = data; + struct chunk *chunk; + int ret = 0; + fd_set set; + int readsize; + char event_ch; + int status; + + DbgPrint("Thread is created for %d (server: %d)\n", tcb->handle, tcb->server_handle); + /*! + * \NOTE + * Read all data from the socket as possible as it can do + */ + while (1) { + FD_ZERO(&set); + FD_SET(tcb->handle, &set); + + status = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + if (status != 0) + ErrPrint("Error: %s\n", strerror(status)); + + ret = select(tcb->handle + 1, &set, NULL, NULL, NULL); + if (ret < 0) { + ret = -errno; + if (errno == EINTR) { + DbgPrint("Select receives INTR\n"); + status = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + if (status != 0) + ErrPrint("Error: %s\n", strerror(status)); + continue; + } + + /*!< Error */ + ErrPrint("Error: %s\n", strerror(errno)); + status = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + if (status != 0) + ErrPrint("Error: %s\n", strerror(status)); + break; + } else if (ret == 0) { + ErrPrint("What happens? [%d]\n", tcb->handle); + status = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + if (status != 0) + ErrPrint("Error: %s\n", strerror(status)); + continue; + } + status = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + if (status != 0) + ErrPrint("Error: %s\n", strerror(status)); + + if (!FD_ISSET(tcb->handle, &set)) { + ErrPrint("Unexpected handle is toggled\n"); + ret = -EINVAL; + break; + } + + readsize = 0; + ret = ioctl(tcb->handle, FIONREAD, &readsize); + if (ret < 0) { + ErrPrint("ioctl: %s\n", strerror(errno)); + break; + } + + if (readsize <= 0) { + ErrPrint("Available data: %d\n", readsize); + ret = -ECONNRESET; + break; + } + + chunk = create_chunk(readsize); + if (!chunk) { + ErrPrint("Failed to create a new chunk: %d\n", readsize); + ret = -ENOMEM; + break; + } + + chunk->size = secure_socket_recv(tcb->handle, chunk->data, chunk->size, &chunk->pid); + if (chunk->size < 0) { + ret = chunk->size; + destroy_chunk(chunk); + if (ret == -EAGAIN) { + DbgPrint("Retry to get data (%d)\n", readsize); + continue; + } + + ErrPrint("Recv returns: %d\n", ret); + break; + } + + /*! + * Count of chunk elements are same with PIPE'd data + */ + chunk_append(tcb, chunk); + } + + /* Wake up main thread to get disconnected event */ + tcb->terminated = 1; + event_ch = EVENT_TERM; + if (write(tcb->evt_pipe[PIPE_WRITE], &event_ch, sizeof(event_ch)) != sizeof(event_ch)) { + ErrPrint("write: %s\n", strerror(errno)); + } + + return (void *)ret; +} + +/*! + * \NOTE + * Running thread: Main + */ +static inline void tcb_destroy(struct tcb *tcb) +{ + int status; + + dlist_remove_data(s_info.tcb_list, tcb); + + if (tcb->id > 0) + g_source_remove(tcb->id); + + secure_socket_destroy_handle(tcb->handle); + + if (tcb->evt_pipe[PIPE_WRITE] > 0) + close(tcb->evt_pipe[PIPE_WRITE]); + + if (tcb->evt_pipe[PIPE_READ] > 0) + close(tcb->evt_pipe[PIPE_READ]); + + status = pthread_mutex_destroy(&tcb->chunk_lock); + if (status != 0) + ErrPrint("Failed to destroy mutex: %s\n", strerror(status)); + + free(tcb); +} + +/*! + * \NOTE + * Running thread: Main + */ +static gboolean evt_pipe_cb(GIOChannel *src, GIOCondition cond, gpointer data) +{ + int pipe_read; + struct tcb *tcb = data; + int ret; + + pipe_read = g_io_channel_unix_get_fd(src); + + if (tcb->evt_pipe[PIPE_READ] != pipe_read) { + ErrPrint("Closed handle (%d <> %d)\n", tcb->evt_pipe[PIPE_READ], pipe_read); + goto errout; + } + + if (!(cond & G_IO_IN)) { + DbgPrint("PIPE is not valid\n"); + goto errout; + } + + if ((cond & G_IO_ERR) || (cond & G_IO_HUP) || (cond & G_IO_NVAL)) { + DbgPrint("PIPE is not valid\n"); + goto errout; + } + + ret = tcb->service_cb(tcb->handle, tcb->data); + if (ret < 0) { + DbgPrint("Service callback returns %d < 0\n", ret); + goto errout; + } + + return TRUE; + +errout: + invoke_disconn_cb_list(tcb->handle); + terminate_thread(tcb); + tcb_destroy(tcb); + return FALSE; +} + +/*! + * \NOTE + * Running thread: Main + */ +static inline struct tcb *tcb_create(int client_fd, int is_sync, int (*service_cb)(int fd, void *data), void *data) +{ + struct tcb *tcb; + int status; + + tcb = malloc(sizeof(*tcb)); + if (!tcb) { + ErrPrint("Error: %s\n", strerror(errno)); + return NULL; + } + + tcb->handle = client_fd; + tcb->chunk_list = NULL; + tcb->service_cb = service_cb; + tcb->data = data; + tcb->id = 0; + tcb->terminated = 0; + + status = pthread_mutex_init(&tcb->chunk_lock, NULL); + if (status != 0) { + ErrPrint("Error: %s\n", strerror(status)); + free(tcb); + return NULL; + } + + if (pipe2(tcb->evt_pipe, (is_sync ? 0 : O_NONBLOCK) | O_CLOEXEC) < 0) { + ErrPrint("Error: %s\n", strerror(errno)); + status = pthread_mutex_destroy(&tcb->chunk_lock); + if (status != 0) + ErrPrint("Error: %s\n", strerror(status)); + free(tcb); + return NULL; + } + + DbgPrint("[%d] New TCB created: %d, %d\n", client_fd, tcb->evt_pipe[0], tcb->evt_pipe[1]); + return tcb; +} + +/*! + * \NOTE + * Running thread: Main + */ +static gboolean accept_cb(GIOChannel *src, GIOCondition cond, gpointer data) +{ + int socket_fd; + int fd; + int ret; + struct tcb *tcb; + GIOChannel *gio; + struct server *server = data; + + socket_fd = g_io_channel_unix_get_fd(src); + if (!(cond & G_IO_IN)) { + ErrPrint("Accept socket closed\n"); + server_destroy(server); + return FALSE; + } + + if ((cond & G_IO_ERR) || (cond & G_IO_HUP) || (cond & G_IO_NVAL)) { + DbgPrint("Socket connection is lost\n"); + server_destroy(server); + return FALSE; + } + + DbgPrint("New connection is made: socket(%d)\n", socket_fd); + fd = secure_socket_get_connection_handle(socket_fd); + if (fd < 0) { + ErrPrint("Failed to get client fd from socket\n"); + server_destroy(server); + return FALSE; + } + + DbgPrint("New client: %d\n", fd); + if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) + ErrPrint("Error: %s\n", strerror(errno)); + + if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) + ErrPrint("Error: %s\n", strerror(errno)); + + tcb = tcb_create(fd, 0, server->service_cb, server->data); + if (!tcb) { + ErrPrint("Failed to create a TCB\n"); + secure_socket_destroy_handle(fd); + server_destroy(server); + return FALSE; + } + + tcb->server_handle = socket_fd; + + s_info.tcb_list = dlist_append(s_info.tcb_list, tcb); + + gio = g_io_channel_unix_new(tcb->evt_pipe[PIPE_READ]); + if (!gio) { + ErrPrint("Failed to get gio\n"); + tcb_destroy(tcb); + server_destroy(server); + return FALSE; + } + + g_io_channel_set_close_on_unref(gio, FALSE); + tcb->id = g_io_add_watch(gio, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, (GIOFunc)evt_pipe_cb, tcb); + if (tcb->id == 0) { + GError *err = NULL; + ErrPrint("Failed to add IO Watch\n"); + g_io_channel_shutdown(gio, TRUE, &err); + if (err) { + ErrPrint("Shutdown: %s\n", err->message); + g_error_free(err); + } + g_io_channel_unref(gio); + tcb_destroy(tcb); + server_destroy(server); + return FALSE; + } + g_io_channel_unref(gio); + + DbgPrint("New client is connected with %d\n", tcb->handle); + invoke_con_cb_list(tcb->handle); + + ret = pthread_create(&tcb->thid, NULL, client_cb, tcb); + if (ret != 0) { + ErrPrint("Thread creation failed: %s\n", strerror(ret)); + invoke_disconn_cb_list(tcb->handle); + tcb_destroy(tcb); + server_destroy(server); + return FALSE; + } + + return TRUE; +} + +/*! + * \NOTE + * Running thread: Main + */ +EAPI int com_core_thread_client_create(const char *addr, int is_sync, int (*service_cb)(int fd, void *data), void *data) +{ + GIOChannel *gio; + int client_fd; + struct tcb *tcb; + int ret; + + client_fd = secure_socket_create_client(addr); + if (client_fd < 0) + return client_fd; + + if (fcntl(client_fd, F_SETFD, FD_CLOEXEC) < 0) + ErrPrint("Error: %s\n", strerror(errno)); + + if (fcntl(client_fd, F_SETFL, O_NONBLOCK) < 0) + ErrPrint("Error: %s\n", strerror(errno)); + + tcb = tcb_create(client_fd, is_sync, service_cb, data); + if (!tcb) { + ErrPrint("Failed to create a new TCB\n"); + secure_socket_destroy_handle(client_fd); + return -EFAULT; + } + + tcb->server_handle = -1; + + s_info.tcb_list = dlist_append(s_info.tcb_list, tcb); + + gio = g_io_channel_unix_new(tcb->evt_pipe[PIPE_READ]); + if (!gio) { + ErrPrint("Failed to get gio\n"); + tcb_destroy(tcb); + return -EIO; + } + + g_io_channel_set_close_on_unref(gio, FALSE); + + tcb->id = g_io_add_watch(gio, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, (GIOFunc)evt_pipe_cb, tcb); + if (tcb->id == 0) { + GError *err = NULL; + ErrPrint("Failed to add IO Watch\n"); + g_io_channel_shutdown(gio, TRUE, &err); + if (err) { + ErrPrint("Shutdown: %s\n", err->message); + g_error_free(err); + } + g_io_channel_unref(gio); + tcb_destroy(tcb); + return -EIO; + } + + g_io_channel_unref(gio); + + DbgPrint("New client is connected with %d\n", client_fd); + invoke_con_cb_list(tcb->handle); + + ret = pthread_create(&tcb->thid, NULL, client_cb, tcb); + if (ret != 0) { + ErrPrint("Thread creation failed: %s\n", strerror(ret)); + invoke_disconn_cb_list(tcb->handle); + tcb_destroy(tcb); + return -EFAULT; + } + + return tcb->handle; +} + +/*! + * \NOTE + * Running thread: Main + */ +EAPI int com_core_thread_server_create(const char *addr, int is_sync, int (*service_cb)(int fd, void *data), void *data) +{ + GIOChannel *gio; + int fd; + struct server *server; + + fd = secure_socket_create_server(addr); + if (fd < 0) + return fd; + + if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) + ErrPrint("fcntl: %s\n", strerror(errno)); + + if (!is_sync && fcntl(fd, F_SETFL, O_NONBLOCK) < 0) + ErrPrint("fcntl: %s\n", strerror(errno)); + + server = server_create(fd, service_cb, data); + if (!server) { + secure_socket_destroy_handle(fd); + return -ENOMEM; + } + + DbgPrint("Create new IO channel for socket FD: %d\n", fd); + gio = g_io_channel_unix_new(server->handle); + if (!gio) { + ErrPrint("Failed to create new io channel\n"); + server_destroy(server); + return -EIO; + } + + g_io_channel_set_close_on_unref(gio, FALSE); + + server->id = g_io_add_watch(gio, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc)accept_cb, server); + if (server->id == 0) { + GError *err = NULL; + ErrPrint("Failed to add IO watch\n"); + g_io_channel_shutdown(gio, TRUE, &err); + if (err) { + ErrPrint("Shutdown: %s\n", err->message); + g_error_free(err); + } + g_io_channel_unref(gio); + server_destroy(server); + return -EIO; + } + + g_io_channel_unref(gio); + return server->handle; +} + +/*! + * \NOTE + * Running thread: Main + */ +static inline struct tcb *find_tcb_by_handle(int handle) +{ + struct dlist *l; + struct tcb *tcb; + + dlist_foreach(s_info.tcb_list, l, tcb) { + if (tcb->handle == handle) + return tcb; + } + + return NULL; +} + +/*! + * \NOTE + * Running thread: Main + */ +EAPI int com_core_thread_send(int handle, const char *buffer, int size, double timeout) +{ + int writesize; + int ret; + struct tcb *tcb; + + fd_set set; + + tcb = find_tcb_by_handle(handle); + if (!tcb) { + ErrPrint("TCB is not found\n"); + return -EINVAL; + } + + writesize = 0; + while (size > 0) { + FD_ZERO(&set); + FD_SET(tcb->handle, &set); + + if (timeout > 0.0f) { + struct timeval tv; + + tv.tv_sec = (unsigned long)timeout; + tv.tv_usec = (timeout - (unsigned long)timeout) * 1000000u; + + ret = select(tcb->handle + 1, NULL, &set, NULL, &tv); + } else if (timeout == 0.0f) { + ret = select(tcb->handle + 1, NULL, &set, NULL, NULL); + } else { + ErrPrint("Invalid timeout: %lf (it must be greater than 0.0)\n", timeout); + return -EINVAL; + } + + if (ret < 0) { + ret = -errno; + if (errno == EINTR) { + DbgPrint("Select receives INTR\n"); + continue; + } + + ErrPrint("Error: %s\n", strerror(errno)); + return ret; + } else if (ret == 0) { + ErrPrint("Timeout expired\n"); + break; + } + + if (!FD_ISSET(tcb->handle, &set)) { + ErrPrint("Unexpected handle is toggled\n"); + return -EINVAL; + } + + ret = secure_socket_send(tcb->handle, buffer + writesize, size); + if (ret < 0) { + if (ret == -EAGAIN) { + DbgPrint("Retry to send data (%d:%d)\n", writesize, size); + continue; + } + DbgPrint("Failed to send: %d\n", ret); + return ret; + } else if (ret == 0) { + DbgPrint("Disconnected? : Send bytes: 0\n"); + return 0; + } + + size -= ret; + writesize += ret; + } + + return writesize; +} + +/*! + * \NOTE + * Running thread: Main + */ +EAPI int com_core_thread_recv(int handle, char *buffer, int size, int *sender_pid, double timeout) +{ + int readsize; + int ret; + struct chunk *chunk; + struct dlist *l; + struct tcb *tcb; + + tcb = find_tcb_by_handle(handle); + if (!tcb) { + ErrPrint("TCB is not exists\n"); + return -EINVAL; + } + + readsize = 0; + while (readsize < size) { + l = dlist_nth(tcb->chunk_list, 0); + chunk = dlist_data(l); + /*! + * \note + * Pumping up the pipe data + * This is the first time to use a chunk + */ + if (!chunk || chunk->offset == 0) { + ret = wait_event(tcb, timeout); + if (ret == -EAGAIN) { + /* Log is printed from wait_event */ + continue; + } else if (ret == -ECONNRESET) { + DbgPrint("Connection is lost\n"); + break; + } else if (ret < 0) { + /* Log is printed from wait_event */ + return ret; + } + + l = dlist_nth(tcb->chunk_list, 0); + chunk = dlist_data(l); + if (!chunk) { + char event_ch; + + /* Consuming the event */ + if (read(tcb->evt_pipe[PIPE_READ], &event_ch, sizeof(event_ch)) != sizeof(event_ch)) { + ErrPrint("Failed to get readsize: %s\n", strerror(errno)); + } else if (event_ch == EVENT_READY) { + ErrPrint("Failed to get a new chunk\n"); + } + + break; + } + } + + ret = chunk->size - chunk->offset; + ret = ret > (size - readsize) ? (size - readsize) : ret; + memcpy(buffer + readsize, chunk->data + chunk->offset, ret); + readsize += ret; + chunk->offset += ret; + + *sender_pid = chunk->pid; + + if (chunk->offset == chunk->size) + chunk_remove(tcb, chunk); + } + + return readsize; +} + +/*! + * \NOTE + * Running thread: Main + */ +EAPI int com_core_thread_server_destroy(int handle) +{ + struct dlist *l; + struct dlist *n; + struct tcb *tcb; + struct server *server; + + dlist_foreach_safe(s_info.tcb_list, l, n, tcb) { + if (tcb->server_handle != handle) + continue; + + terminate_thread(tcb); + tcb_destroy(tcb); + } + + dlist_foreach_safe(s_info.server_list, l, n, server) { + if (server->handle == handle) { + server_destroy(server); + break; + } + } + + return 0; +} + +/*! + * \NOTE + * Running thread: Main + */ +EAPI int com_core_thread_client_destroy(int handle) +{ + struct tcb *tcb; + + tcb = find_tcb_by_handle(handle); + if (!tcb) + return -ENOENT; + + terminate_thread(tcb); + tcb_destroy(tcb); + return 0; +} + +/* End of a file */ diff --git a/src/dlist.c b/src/dlist.c new file mode 100644 index 0000000..a212608 --- /dev/null +++ b/src/dlist.c @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * 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. + * +*/ + +#include +#include +#include + +#include "dlist.h" + +/*! + * \brief + * This dlist is called Modified Doubly Linked List. + * + * Noramlly, The dobule linked list contains address of previous and next element. + * This dlist also contains them, but the tail element only contains prev address. + * + * The head element's prev pointer indicates the last element. + * But the last element's next pointer indicates NIL. + * + * So we can find the last element while crawling this DList + * But we have to remember the address of the head element. + */ + +struct dlist { + struct dlist *next; + struct dlist *prev; + void *data; +}; + +struct dlist *dlist_append(struct dlist *list, void *data) +{ + struct dlist *item; + + item = malloc(sizeof(*item)); + if (!item) + return NULL; + + item->next = NULL; + item->data = data; + + if (!list) { + item->prev = item; + + list = item; + } else { + item->prev = list->prev; + item->prev->next = item; + list->prev = item; + } + + assert(!list->prev->next && "item NEXT"); + + return list; +} + +struct dlist *dlist_prepend(struct dlist *list, void *data) +{ + struct dlist *item; + + item = malloc(sizeof(*item)); + if (!item) + return NULL; + + item->data = data; + + if (!list) { + item->prev = item; + item->next = NULL; + } else { + if (list->prev->next) + list->prev->next = item; + + item->prev = list->prev; + item->next = list; + + list->prev = item; + + } + + return item; +} + +struct dlist *dlist_remove(struct dlist *list, struct dlist *l) +{ + if (!list || !l) + return NULL; + + if (l == list) + list = l->next; + else + l->prev->next = l->next; + + if (l->next) + l->next->prev = l->prev; + /*! + * \note + * If the removed entry 'l' has no next element, it is the last element. + * In this case, check the existence of the list first, + * and if the list is not empty, update the 'prev' of the list (which is a head element of the list) + * + * If we didn't care about this, the head element(list) can indicates the invalid element. + */ + else if (list) + list->prev = l->prev; + + free(l); + return list; +} + +struct dlist *dlist_find_data(struct dlist *list, void *data) +{ + struct dlist *l; + void *_data; + + dlist_foreach(list, l, _data) { + if (data == _data) + return l; + } + + return NULL; +} + +void *dlist_data(struct dlist *l) +{ + return l ? l->data : NULL; +} + +struct dlist *dlist_next(struct dlist *l) +{ + return l ? l->next : NULL; +} + +struct dlist *dlist_prev(struct dlist *l) +{ + return l ? l->prev : NULL; +} + +int dlist_count(struct dlist *l) +{ + register int i; + struct dlist *n; + void *data; + + i = 0; + dlist_foreach(l, n, data) { + i++; + } + + return i; +} + +struct dlist *dlist_nth(struct dlist *l, int nth) +{ + register int i; + struct dlist *n; + + i = 0; + for (n = l; n; n = n->next) { + if (i == nth) + return n; + i++; + } + + return NULL; +} + +/* End of a file */ diff --git a/src/packet.c b/src/packet.c new file mode 100644 index 0000000..061184e --- /dev/null +++ b/src/packet.c @@ -0,0 +1,573 @@ +/* + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * 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. + * +*/ + +#include +#include +#include +#include +#include +#include + +#include + +#include "debug.h" +#include "packet.h" +#include "util.h" + +int errno; + +struct data { + struct { + int version; + int payload_size; + char command[PACKET_MAX_CMD]; + enum packet_type type; + enum packet_flag flag; + double seq; + unsigned long source; + unsigned long destination; + unsigned long mask; + } head; + + char payload[]; +}; + +struct packet { + enum { + VALID = 0xbeefbeef, + INVALID = 0xdeaddead, + } state; + int refcnt; + struct data *data; +}; + +EAPI const enum packet_type const packet_type(const struct packet *packet) +{ + if (!packet || packet->state != VALID || !packet->data) + return PACKET_ERROR; + + return packet->data->head.type; +} + +EAPI unsigned long packet_mask(const struct packet *packet) +{ + if (!packet || packet->state != VALID || !packet->data) + return 0; + + return packet->data->head.mask; +} + +EAPI int packet_set_mask(struct packet *packet, unsigned long mask) +{ + if (!packet || packet->state != VALID || !packet->data) + return -EINVAL; + + packet->data->head.mask = mask; + return 0; +} + +EAPI const enum packet_flag const packet_flag(const struct packet *packet) +{ + if (!packet || packet->state != VALID || !packet->data) + return PACKET_FLAG_ERROR; + + return packet->data->head.flag; +} + +EAPI int packet_set_flag(struct packet *packet, enum packet_flag flag) +{ + if (!packet || packet->state != VALID || !packet->data) + return -EINVAL; + + packet->data->head.flag = flag; + return 0; +} + +EAPI const unsigned long const packet_source(const struct packet *packet) +{ + if (!packet || packet->state != VALID || !packet->data) + return 0; + + return packet->data->head.source; +} + +EAPI int packet_set_source(struct packet *packet, unsigned long source) +{ + if (!packet || packet->state != VALID || !packet->data || !source) + return -EINVAL; + + packet->data->head.source = source; + return 0; +} + +EAPI const unsigned long const packet_destination(const struct packet *packet) +{ + if (!packet || packet->state != VALID || !packet->data) + return 0; + + return packet->data->head.destination; +} + +EAPI int packet_set_destination(struct packet *packet, unsigned long destination) +{ + if (!packet || packet->state != VALID || !packet->data || !destination) + return -EINVAL; + + packet->data->head.destination = destination; + return 0; +} + +EAPI const int const packet_version(const struct packet *packet) +{ + if (!packet || packet->state != VALID || !packet->data) + return PACKET_ERROR; + + return packet->data->head.version; +} + +EAPI const int const packet_header_size(void) +{ + struct data payload; /* Only for getting the size of header of packet */ + + return sizeof(payload.head); +} + +EAPI const int const packet_size(const struct packet *packet) +{ + if (!packet || packet->state != VALID || !packet->data) + return -EINVAL; + + return sizeof(*packet->data) + packet->data->head.payload_size; +} + +EAPI const double const packet_seq(const struct packet *packet) +{ + if (!packet || packet->state != VALID || !packet->data) + return 0; + + return packet->data->head.seq; +} + +EAPI const int const packet_payload_size(const struct packet *packet) +{ + if (!packet || packet->state != VALID || !packet->data) + return -EINVAL; + + return packet->data->head.payload_size; +} + +EAPI const char * const packet_command(const struct packet *packet) +{ + if (!packet || packet->state != VALID || !packet->data) + return NULL; + + return packet->data->head.command; +} + +EAPI const void * const packet_data(const struct packet *packet) +{ + if (!packet || packet->state != VALID) + return NULL; + + return packet->data; +} + +static inline __attribute__((always_inline)) struct data *check_and_expand_packet(struct data *packet, int *payload_size) +{ + struct data *new_packet; + + if (packet->head.payload_size < *payload_size) + return packet; + + new_packet = realloc(packet, sizeof(*packet) + *payload_size + BUFSIZ); /*!< Expanding to +BUFSIZ */ + if (!new_packet) { + ErrPrint("Heap: %s\n", strerror(errno)); + free(packet); + return NULL; + } + + *payload_size += BUFSIZ; + return new_packet; +} + +static inline struct packet *packet_body_filler(struct packet *packet, int payload_size, const char *ptr, va_list va) +{ + char *payload; + char *str; + + while (*ptr) { + payload = packet->data->payload + packet->data->head.payload_size; + + switch (*ptr) { + case 'i': + case 'I': + packet->data->head.payload_size += sizeof(int); + packet->data = check_and_expand_packet(packet->data, &payload_size); + if (!packet->data) { + packet->state = INVALID; + free(packet); + packet = NULL; + goto out; + } + + *((int *)payload) = (int)va_arg(va, int); + break; + case 's': + case 'S': + str = (char *)va_arg(va, char *); + + if (str) { + packet->data->head.payload_size += strlen(str) + 1; /*!< Including NIL */ + packet->data = check_and_expand_packet(packet->data, &payload_size); + if (!packet->data) { + packet->state = INVALID; + free(packet); + packet = NULL; + goto out; + } + + strcpy(payload, str); /*!< Including NIL */ + } else { + packet->data->head.payload_size += 1; + packet->data = check_and_expand_packet(packet->data, &payload_size); + if (!packet->data) { + packet->state = INVALID; + free(packet); + packet = NULL; + goto out; + } + + payload[0] = '\0'; + } + break; + case 'd': + case 'D': + packet->data->head.payload_size += sizeof(double); + packet->data = check_and_expand_packet(packet->data, &payload_size); + if (!packet->data) { + packet->state = INVALID; + free(packet); + packet = NULL; + goto out; + } + + *((double *)payload) = (double)va_arg(va, double); + break; + default: + ErrPrint("Invalid type [%c]\n", *ptr); + packet->state = INVALID; + free(packet->data); + free(packet); + packet = NULL; + goto out; + } + + ptr++; + } + +out: + return packet; +} + +EAPI struct packet *packet_create_reply(const struct packet *packet, const char *fmt, ...) +{ + int payload_size; + struct packet *result; + va_list va; + + if (!packet || packet->state != VALID) + return NULL; + + result = malloc(sizeof(*result)); + if (!result) { + ErrPrint("Heap: %s\n", strerror(errno)); + return NULL; + } + + payload_size = sizeof(*result->data) + BUFSIZ; + result->refcnt = 0; + result->data = malloc(payload_size); + if (!packet->data) { + ErrPrint("Heap: %s\n", strerror(errno)); + result->state = INVALID; + free(result); + return NULL; + } + + result->state = VALID; + result->data->head.source = packet->data->head.destination; + result->data->head.destination = packet->data->head.source; + result->data->head.mask = 0xFFFFFFFF; + + result->data->head.seq = packet->data->head.seq; + result->data->head.type = PACKET_ACK; + result->data->head.version = packet->data->head.version; + strcpy(result->data->head.command, packet->data->head.command); /* we don't need to use strncmp */ + result->data->head.payload_size = 0; + payload_size -= sizeof(*result->data); + + va_start(va, fmt); + result = packet_body_filler(result, payload_size, fmt, va); + va_end(va); + + return packet_ref(result); +} + +EAPI int packet_swap_address(struct packet *packet) +{ + unsigned long tmp; + + if (!packet || packet->state != VALID) + return -EINVAL; + + tmp = packet->data->head.source; + packet->data->head.source = packet->data->head.destination; + packet->data->head.destination = tmp; + + return 0; +} + +EAPI struct packet *packet_create(const char *cmd, const char *fmt, ...) +{ + struct packet *packet; + int payload_size; + va_list va; + struct timeval tv; + + if (strlen(cmd) >= PACKET_MAX_CMD) { + ErrPrint("Command is too long\n"); + return NULL; + } + + packet = malloc(sizeof(*packet)); + if (!packet) { + ErrPrint("Heap: %s\n", strerror(errno)); + return NULL; + } + + payload_size = sizeof(*packet->data) + BUFSIZ; + packet->refcnt = 0; + packet->data = malloc(payload_size); + if (!packet->data) { + ErrPrint("Heap: %s\n", strerror(errno)); + packet->state = INVALID; + free(packet); + return NULL; + } + + packet->state = VALID; + gettimeofday(&tv, NULL); + packet->data->head.source = 0lu; + packet->data->head.destination = 0lu; + packet->data->head.mask = 0xFFFFFFFF; + packet->data->head.seq = tv.tv_sec + tv.tv_usec / 1000000.0f; + packet->data->head.type = PACKET_REQ; + packet->data->head.version = PACKET_VERSION; + strncpy(packet->data->head.command, cmd, sizeof(packet->data->head.command)); + packet->data->head.payload_size = 0; + payload_size -= sizeof(*packet->data); /*!< Usable payload size (except head size) */ + + va_start(va, fmt); + packet = packet_body_filler(packet, payload_size, fmt, va); + va_end(va); + + return packet_ref(packet); +} + +EAPI struct packet *packet_create_noack(const char *cmd, const char *fmt, ...) +{ + int payload_size; + struct packet *result; + va_list va; + struct timeval tv; + + if (strlen(cmd) >= PACKET_MAX_CMD) { + ErrPrint("Command is too long\n"); + return NULL; + } + + result = malloc(sizeof(*result)); + if (!result) { + ErrPrint("Heap: %s\n", strerror(errno)); + return NULL; + } + + payload_size = sizeof(*result->data) + BUFSIZ; + result->refcnt = 0; + result->data = malloc(payload_size); + if (!result->data) { + ErrPrint("Heap: %s\n", strerror(errno)); + result->state = INVALID; + free(result); + return NULL; + } + + result->state = VALID; + gettimeofday(&tv, NULL); + result->data->head.source = 0lu; + result->data->head.destination = 0lu; + result->data->head.mask = 0xFFFFFFFF; + result->data->head.seq = tv.tv_sec + tv.tv_usec / 1000000.0f; + result->data->head.type = PACKET_REQ_NOACK; + result->data->head.version = PACKET_VERSION; + strncpy(result->data->head.command, cmd, sizeof(result->data->head.command)); + result->data->head.payload_size = 0; + payload_size -= sizeof(*result->data); + + va_start(va, fmt); + result = packet_body_filler(result, payload_size, fmt, va); + va_end(va); + + return packet_ref(result); +} + +EAPI int packet_get(const struct packet *packet, const char *fmt, ...) +{ + const char *ptr; + va_list va; + int ret = 0; + char *payload; + int offset = 0; + int *int_ptr; + double *double_ptr; + char **str_ptr; + + if (!packet || packet->state != VALID) + return -EINVAL; + + va_start(va, fmt); + + ptr = fmt; + while (*ptr && offset < packet->data->head.payload_size) { + payload = packet->data->payload + offset; + switch (*ptr) { + case 'i': + case 'I': + int_ptr = (int *)va_arg(va, int *); + *int_ptr = *((int *)payload); + offset += sizeof(int); + ret++; + break; + case 'd': + case 'D': + double_ptr = (double *)va_arg(va, double *); + *double_ptr = *((double *)payload); + offset += sizeof(double); + ret++; + break; + case 's': + case 'S': + str_ptr = (char **)va_arg(va, char **); + *str_ptr = payload; + offset += (strlen(*str_ptr) + 1); /*!< Including NIL */ + ret++; + break; + default: + ret = -EINVAL; + goto out; + } + ptr++; + } + +out: + va_end(va); + return ret; +} + +EAPI struct packet *packet_ref(struct packet *packet) +{ + if (!packet || packet->state != VALID) + return NULL; + + packet->refcnt++; + return packet; +} + +EAPI struct packet *packet_unref(struct packet *packet) +{ + if (!packet || packet->state != VALID) + return NULL; + + packet->refcnt--; + if (packet->refcnt < 0) { + ErrPrint("Invalid refcnt\n"); + return NULL; + } + + if (packet->refcnt == 0) { + packet->state = INVALID; + free(packet->data); + free(packet); + return NULL; + } + + return packet; +} + +EAPI int packet_destroy(struct packet *packet) +{ + packet_unref(packet); + return 0; +} + +EAPI struct packet *packet_build(struct packet *packet, int offset, void *data, int size) +{ + char *ptr; + + if (packet == NULL) { + if (offset) { + ErrPrint("Invalid argument\n"); + return NULL; + } + + packet = malloc(sizeof(*packet)); + if (!packet) { + ErrPrint("Heap: %s\n", strerror(errno)); + return NULL; + } + + packet->refcnt = 1; + packet->data = malloc(size); + if (!packet->data) { + ErrPrint("Heap: %s\n", strerror(errno)); + packet->state = INVALID; + free(packet); + return NULL; + } + + packet->state = VALID; + memcpy(packet->data, data, size); + packet->data->head.mask = 0xFFFFFFFF; + return packet; + } + + ptr = realloc(packet->data, offset + size); + if (!ptr) { + ErrPrint("Heap: %s\n", strerror(errno)); + packet->state = INVALID; + free(packet->data); + free(packet); + return NULL; + } + + packet->data = (struct data *)ptr; + memcpy(ptr + offset, data, size); + + return packet; +} + +/* End of a file */ diff --git a/src/secure_socket.c b/src/secure_socket.c new file mode 100644 index 0000000..95b419b --- /dev/null +++ b/src/secure_socket.c @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * 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. + * +*/ + +#define _GNU_SOURCE +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "secure_socket.h" +#include "debug.h" +#include "util.h" + +#define BACKLOG 50 /*!< Accept only 50 connections as default */ + +int errno; + +static inline int create_socket(const char *peer, struct sockaddr_un *addr) +{ + int len; + int handle; + + len = sizeof(*addr); + bzero(addr, len); + + if (strlen(peer) >= sizeof(addr->sun_path)) { + ErrPrint("peer %s is too long to remember it\\n", peer); + return -1; + } + + /* We can believe this has no prob, because + * we already check the size of add.rsun_path + */ + strcpy(addr->sun_path, peer); + addr->sun_family = AF_UNIX; + + handle = socket(PF_UNIX, SOCK_STREAM, 0); + if (handle < 0) { + ErrPrint("Failed to create a socket %s\n", strerror(errno)); + return -1; + } + + return handle; +} + +EAPI int secure_socket_create_client(const char *peer) +{ + struct sockaddr_un addr; + int handle; + int state; + int on = 1; + + handle = create_socket(peer, &addr); + if (handle < 0) + return handle; + + state = connect(handle, (struct sockaddr *)&addr, sizeof(addr)); + if (state < 0) { + ErrPrint("Failed to connect to server [%s] %s\n", + peer, strerror(errno)); + if (close(handle) < 0) + ErrPrint("close a handle: %s\n", strerror(errno)); + + return -ENOTCONN; + } + + if (setsockopt(handle, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) { + ErrPrint("Failed to change sock opt : %s\n", strerror(errno)); + if (close(handle) < 0) + ErrPrint("close a handle: %s\n", strerror(errno)); + return -EFAULT; + } + + return handle; +} + +EAPI int secure_socket_create_server(const char *peer) +{ + int handle; + int state; + struct sockaddr_un addr; + + handle = create_socket(peer, &addr); + if (handle < 0) + return handle; + + state = bind(handle, &addr, sizeof(addr)); + if (state < 0) { + state = -errno; + + ErrPrint("Failed to bind a socket %s\n", strerror(errno)); + if (close(handle) < 0) + ErrPrint("Close a handle : %s\n", strerror(errno)); + + return state; + } + + state = listen(handle, BACKLOG); + if (state < 0) { + state = -errno; + ErrPrint("Failed to listen a socket %s\n", strerror(errno)); + + if (close(handle) < 0) + ErrPrint("Close a handle : %s\n", strerror(errno)); + + return state; + } + + if (chmod(peer, 0666) < 0) + ErrPrint("Failed to change the permission of a socket (%s)\n", + strerror(errno)); + + return handle; +} + +EAPI int secure_socket_get_connection_handle(int server_handle) +{ + struct sockaddr_un addr; + int handle; + int on = 1; + socklen_t size = sizeof(addr); + + handle = accept(server_handle, (struct sockaddr *)&addr, &size); + if (handle < 0) { + handle = -errno; + ErrPrint("Failed to accept a new client %s\n", strerror(errno)); + return handle; + } + + if (setsockopt(handle, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) { + int ret; + ret = -errno; + ErrPrint("Failed to change sock opt : %s\n", strerror(errno)); + if (close(handle) < 0) + ErrPrint("Close a handle: %s\n", strerror(errno)); + return ret; + } + + return handle; +} + +EAPI int secure_socket_send(int handle, const char *buffer, int size) +{ + struct msghdr msg; + struct iovec iov; + int ret; + + if (!buffer || size <= 0) { + ErrPrint("Reject: 0 byte data sending\n"); + return -EINVAL; + } + + memset(&msg, 0, sizeof(msg)); + iov.iov_base = (char *)buffer; + iov.iov_len = size; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + ret = sendmsg(handle, &msg, 0); + if (ret < 0) { + ret = -errno; + if (errno == EAGAIN || errno == EWOULDBLOCK) { + ErrPrint("handle[%d] size[%d] Try again [%s]\n", handle, size, strerror(errno)); + return -EAGAIN; + } + ErrPrint("Failed to send message [%s]\n", strerror(errno)); + return ret; + } + + return iov.iov_len; +} + +EAPI int secure_socket_recv(int handle, char *buffer, int size, int *sender_pid) +{ + struct msghdr msg; + struct cmsghdr *cmsg; + struct iovec iov; + char control[1024]; + int ret; + + if (!sender_pid || size <= 0 || !buffer) + return -EINVAL; + + memset(&msg, 0, sizeof(msg)); + iov.iov_base = buffer; + iov.iov_len = size; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + ret = recvmsg(handle, &msg, 0); + if (ret == 0) { + /*!< Disconnected */ + DbgPrint("Disconnected\n"); + return 0; + } + + if (ret < 0) { + ret = -errno; + if (errno == EAGAIN || errno == EWOULDBLOCK) { + ErrPrint("handle[%d] size[%d] Try again [%s]\n", handle, size, strerror(errno)); + return -EAGAIN; + } + + ErrPrint("Failed to recvmsg [%s]\n", strerror(errno)); + return ret; + } + + cmsg = CMSG_FIRSTHDR(&msg); + while (cmsg) { + if (cmsg->cmsg_level == SOL_SOCKET + && cmsg->cmsg_type == SCM_CREDENTIALS) { + struct ucred *cred; + cred = (struct ucred *)CMSG_DATA(cmsg); + *sender_pid = cred->pid; + } + + cmsg = CMSG_NXTHDR(&msg, cmsg); + } + + return iov.iov_len; +} + +EAPI int secure_socket_destroy_handle(int handle) +{ + DbgPrint("Close socket handle %d\n", handle); + if (close(handle) < 0) { + ErrPrint("Failed to close a handle: %s\n", strerror(errno)); + return -1; + } + return 0; +} + +#undef _GNU_SOURCE + +/* End of a file */ diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..2471987 --- /dev/null +++ b/src/util.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * 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. + * +*/ + +#include +#include +#include +#include + +#include "util.h" + +int errno; + +const char *util_basename(const char *name) +{ + int length; + length = name ? strlen(name) : 0; + if (!length) + return "."; + + while (--length > 0 && name[length] != '/'); + + return length <= 0 ? name : name + length + (name[length] == '/'); +} + +/* End of a file */ -- 2.7.4