summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJinkun Jang <jinkun.jang@samsung.com>2013-03-16 21:33:50 +0900
committerJinkun Jang <jinkun.jang@samsung.com>2013-03-16 21:33:50 +0900
commit171c3f96b8a02da15803a739bdb6a2a367527845 (patch)
treea4f0d97f9325587394e7dacd428293449d526f7d
parent3fadf1b0d6aa6eb8b5cb302db7fd7a2eadb97488 (diff)
downloadcom-core-171c3f96b8a02da15803a739bdb6a2a367527845.tar.gz
com-core-171c3f96b8a02da15803a739bdb6a2a367527845.tar.bz2
com-core-171c3f96b8a02da15803a739bdb6a2a367527845.zip
sync with master
-rw-r--r--CMakeLists.txt57
-rw-r--r--LICENSE204
-rw-r--r--com-core.pc.in11
-rw-r--r--include/com-core.h53
-rw-r--r--include/com-core_internal.h21
-rw-r--r--include/com-core_packet-router.h53
-rw-r--r--include/com-core_packet.h44
-rw-r--r--include/com-core_thread.h39
-rw-r--r--include/debug.h31
-rw-r--r--include/dlist.h44
-rw-r--r--include/packet.h79
-rw-r--r--include/secure_socket.h61
-rw-r--r--include/util.h36
-rw-r--r--libcom-core.manifest5
-rw-r--r--packaging/libcom-core.spec52
-rw-r--r--src/com-core.c495
-rw-r--r--src/com-core_packet-router.c1813
-rw-r--r--src/com-core_packet.c635
-rw-r--r--src/com-core_thread.c948
-rw-r--r--src/dlist.c181
-rw-r--r--src/packet.c573
-rw-r--r--src/secure_socket.c259
-rw-r--r--src/util.c39
23 files changed, 5733 insertions, 0 deletions
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 @@
+<manifest>
+ <request>
+ <domain name="_" />
+ </request>
+</manifest>
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 <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include <glib.h>
+
+#include <dlog.h>
+
+#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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <pthread.h>
+#include <fcntl.h> /* Obtain O_* constant definitions */
+#include <unistd.h>
+#include <sys/select.h>
+
+#include <glib.h>
+#include <dlog.h>
+
+#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 <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include <glib.h>
+#include <dlog.h>
+
+#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 <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <pthread.h>
+
+#include <glib.h>
+
+#include <dlog.h>
+
+#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 <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#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 <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include <dlog.h>
+
+#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 <stdio.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <errno.h>
+
+#include <dlog.h>
+
+#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 <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#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 */