--- /dev/null
+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 ${LIB_INSTALL_DIR})
+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
+ libsystemd-daemon
+)
+
+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_DEFINITIONS("-D_USE_ECORE_TIME_GET")
+
+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_DIR})
+INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc DESTINATION ${LIB_INSTALL_DIR}/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}")
+
+# End of a file
--- /dev/null
+Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved.\r
+\r
+ Apache License\r
+ Version 2.0, January 2004\r
+ http://www.apache.org/licenses/\r
+\r
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\r
+\r
+ 1. Definitions.\r
+\r
+ "License" shall mean the terms and conditions for use, reproduction,\r
+ and distribution as defined by Sections 1 through 9 of this document.\r
+\r
+ "Licensor" shall mean the copyright owner or entity authorized by\r
+ the copyright owner that is granting the License.\r
+\r
+ "Legal Entity" shall mean the union of the acting entity and all\r
+ other entities that control, are controlled by, or are under common\r
+ control with that entity. For the purposes of this definition,\r
+ "control" means (i) the power, direct or indirect, to cause the\r
+ direction or management of such entity, whether by contract or\r
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the\r
+ outstanding shares, or (iii) beneficial ownership of such entity.\r
+\r
+ "You" (or "Your") shall mean an individual or Legal Entity\r
+ exercising permissions granted by this License.\r
+\r
+ "Source" form shall mean the preferred form for making modifications,\r
+ including but not limited to software source code, documentation\r
+ source, and configuration files.\r
+\r
+ "Object" form shall mean any form resulting from mechanical\r
+ transformation or translation of a Source form, including but\r
+ not limited to compiled object code, generated documentation,\r
+ and conversions to other media types.\r
+\r
+ "Work" shall mean the work of authorship, whether in Source or\r
+ Object form, made available under the License, as indicated by a\r
+ copyright notice that is included in or attached to the work\r
+ (an example is provided in the Appendix below).\r
+\r
+ "Derivative Works" shall mean any work, whether in Source or Object\r
+ form, that is based on (or derived from) the Work and for which the\r
+ editorial revisions, annotations, elaborations, or other modifications\r
+ represent, as a whole, an original work of authorship. For the purposes\r
+ of this License, Derivative Works shall not include works that remain\r
+ separable from, or merely link (or bind by name) to the interfaces of,\r
+ the Work and Derivative Works thereof.\r
+\r
+ "Contribution" shall mean any work of authorship, including\r
+ the original version of the Work and any modifications or additions\r
+ to that Work or Derivative Works thereof, that is intentionally\r
+ submitted to Licensor for inclusion in the Work by the copyright owner\r
+ or by an individual or Legal Entity authorized to submit on behalf of\r
+ the copyright owner. For the purposes of this definition, "submitted"\r
+ means any form of electronic, verbal, or written communication sent\r
+ to the Licensor or its representatives, including but not limited to\r
+ communication on electronic mailing lists, source code control systems,\r
+ and issue tracking systems that are managed by, or on behalf of, the\r
+ Licensor for the purpose of discussing and improving the Work, but\r
+ excluding communication that is conspicuously marked or otherwise\r
+ designated in writing by the copyright owner as "Not a Contribution."\r
+\r
+ "Contributor" shall mean Licensor and any individual or Legal Entity\r
+ on behalf of whom a Contribution has been received by Licensor and\r
+ subsequently incorporated within the Work.\r
+\r
+ 2. Grant of Copyright License. Subject to the terms and conditions of\r
+ this License, each Contributor hereby grants to You a perpetual,\r
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable\r
+ copyright license to reproduce, prepare Derivative Works of,\r
+ publicly display, publicly perform, sublicense, and distribute the\r
+ Work and such Derivative Works in Source or Object form.\r
+\r
+ 3. Grant of Patent License. Subject to the terms and conditions of\r
+ this License, each Contributor hereby grants to You a perpetual,\r
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable\r
+ (except as stated in this section) patent license to make, have made,\r
+ use, offer to sell, sell, import, and otherwise transfer the Work,\r
+ where such license applies only to those patent claims licensable\r
+ by such Contributor that are necessarily infringed by their\r
+ Contribution(s) alone or by combination of their Contribution(s)\r
+ with the Work to which such Contribution(s) was submitted. If You\r
+ institute patent litigation against any entity (including a\r
+ cross-claim or counterclaim in a lawsuit) alleging that the Work\r
+ or a Contribution incorporated within the Work constitutes direct\r
+ or contributory patent infringement, then any patent licenses\r
+ granted to You under this License for that Work shall terminate\r
+ as of the date such litigation is filed.\r
+\r
+ 4. Redistribution. You may reproduce and distribute copies of the\r
+ Work or Derivative Works thereof in any medium, with or without\r
+ modifications, and in Source or Object form, provided that You\r
+ meet the following conditions:\r
+\r
+ (a) You must give any other recipients of the Work or\r
+ Derivative Works a copy of this License; and\r
+\r
+ (b) You must cause any modified files to carry prominent notices\r
+ stating that You changed the files; and\r
+\r
+ (c) You must retain, in the Source form of any Derivative Works\r
+ that You distribute, all copyright, patent, trademark, and\r
+ attribution notices from the Source form of the Work,\r
+ excluding those notices that do not pertain to any part of\r
+ the Derivative Works; and\r
+\r
+ (d) If the Work includes a "NOTICE" text file as part of its\r
+ distribution, then any Derivative Works that You distribute must\r
+ include a readable copy of the attribution notices contained\r
+ within such NOTICE file, excluding those notices that do not\r
+ pertain to any part of the Derivative Works, in at least one\r
+ of the following places: within a NOTICE text file distributed\r
+ as part of the Derivative Works; within the Source form or\r
+ documentation, if provided along with the Derivative Works; or,\r
+ within a display generated by the Derivative Works, if and\r
+ wherever such third-party notices normally appear. The contents\r
+ of the NOTICE file are for informational purposes only and\r
+ do not modify the License. You may add Your own attribution\r
+ notices within Derivative Works that You distribute, alongside\r
+ or as an addendum to the NOTICE text from the Work, provided\r
+ that such additional attribution notices cannot be construed\r
+ as modifying the License.\r
+\r
+ You may add Your own copyright statement to Your modifications and\r
+ may provide additional or different license terms and conditions\r
+ for use, reproduction, or distribution of Your modifications, or\r
+ for any such Derivative Works as a whole, provided Your use,\r
+ reproduction, and distribution of the Work otherwise complies with\r
+ the conditions stated in this License.\r
+\r
+ 5. Submission of Contributions. Unless You explicitly state otherwise,\r
+ any Contribution intentionally submitted for inclusion in the Work\r
+ by You to the Licensor shall be under the terms and conditions of\r
+ this License, without any additional terms or conditions.\r
+ Notwithstanding the above, nothing herein shall supersede or modify\r
+ the terms of any separate license agreement you may have executed\r
+ with Licensor regarding such Contributions.\r
+\r
+ 6. Trademarks. This License does not grant permission to use the trade\r
+ names, trademarks, service marks, or product names of the Licensor,\r
+ except as required for reasonable and customary use in describing the\r
+ origin of the Work and reproducing the content of the NOTICE file.\r
+\r
+ 7. Disclaimer of Warranty. Unless required by applicable law or\r
+ agreed to in writing, Licensor provides the Work (and each\r
+ Contributor provides its Contributions) on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\r
+ implied, including, without limitation, any warranties or conditions\r
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\r
+ PARTICULAR PURPOSE. You are solely responsible for determining the\r
+ appropriateness of using or redistributing the Work and assume any\r
+ risks associated with Your exercise of permissions under this License.\r
+\r
+ 8. Limitation of Liability. In no event and under no legal theory,\r
+ whether in tort (including negligence), contract, or otherwise,\r
+ unless required by applicable law (such as deliberate and grossly\r
+ negligent acts) or agreed to in writing, shall any Contributor be\r
+ liable to You for damages, including any direct, indirect, special,\r
+ incidental, or consequential damages of any character arising as a\r
+ result of this License or out of the use or inability to use the\r
+ Work (including but not limited to damages for loss of goodwill,\r
+ work stoppage, computer failure or malfunction, or any and all\r
+ other commercial damages or losses), even if such Contributor\r
+ has been advised of the possibility of such damages.\r
+\r
+ 9. Accepting Warranty or Additional Liability. While redistributing\r
+ the Work or Derivative Works thereof, You may choose to offer,\r
+ and charge a fee for, acceptance of support, warranty, indemnity,\r
+ or other liability obligations and/or rights consistent with this\r
+ License. However, in accepting such obligations, You may act only\r
+ on Your own behalf and on Your sole responsibility, not on behalf\r
+ of any other Contributor, and only if You agree to indemnify,\r
+ defend, and hold each Contributor harmless for any liability\r
+ incurred by, or claims asserted against, such Contributor by reason\r
+ of your accepting any such warranty or additional liability.\r
+\r
+ END OF TERMS AND CONDITIONS\r
+\r
+ APPENDIX: How to apply the Apache License to your work.\r
+\r
+ To apply the Apache License to your work, attach the following\r
+ boilerplate notice, with the fields enclosed by brackets "[]"\r
+ replaced with your own identifying information. (Don't include\r
+ the brackets!) The text should be enclosed in the appropriate\r
+ comment syntax for the file format. We also recommend that a\r
+ file or class name and description of purpose be included on the\r
+ same "printed page" as the copyright notice for easier\r
+ identification within third-party archives.\r
+\r
+ Copyright [yyyy] [name of copyright owner]\r
+\r
+ Licensed under the Apache License, Version 2.0 (the "License");\r
+ you may not use this file except in compliance with the License.\r
+ You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
--- /dev/null
+prefix=@PREFIX@
+exec_prefix=@EXEC_PREFIX@
+libdir=@LIB_INSTALL_DIR@
+includedir=@INCLUDEDIR@
+
+Name: com-core
+Description: Light-weight IPC supporting library
+Version: @VERSION@
+Libs: -L${libdir} -lcom-core
+Cflags: -I${includedir}
+cppflags: -I${includedir}
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2013 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
+};
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] addr
+ * \param[in] is_sync
+ * \param[in] service_cb
+ * \param[in] data
+ * \return int
+ * \retval
+ * \sa
+ */
+extern int com_core_server_create(const char *addr, int is_sync, const char *label, int (*service_cb)(int fd, void *data), void *data);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] addr
+ * \param[in] is_sync
+ * \param[in] service_cb
+ * \param[in] data
+ * \return int
+ * \retval
+ * \sa
+ */
+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_client_create_by_fd(int client_fd, int is_sync, int (*service_cb)(int fd, void *data), void *data);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] handle
+ * \return int
+ * \retval
+ * \sa
+ */
+extern int com_core_server_destroy(int handle);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] handle
+ * \return int
+ * \retval
+ * \sa
+ */
+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.
+ * \details N/A
+ * \remarks N/A
+ * \param[in] type
+ * \param[in] service_cb
+ * \param[in] data
+ * \return int
+ * \retval
+ * \sa
+ */
+extern int com_core_add_event_callback(enum com_core_event_type type, int (*service_cb)(int handle, void *data), void *data);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] type
+ * \param[in] service_cb
+ * \param[in] data
+ * \return void *
+ * \retval
+ * \sa
+ */
+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.
+ * \details N/A
+ * \remarks N/A
+ * \param[in] handle
+ * \param[in] buffer
+ * \param[in] size
+ * \param[in] sender_pid
+ * \param[in] timeout
+ * \return int
+ * \retval
+ * \sa
+ */
+extern int com_core_recv(int handle, char *buffer, int size, int *sender_pid, double timeout);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] handle
+ * \param[in] buffer
+ * \param[in] size
+ * \param[in] timeout
+ * \return int
+ * \retval
+ * \sa
+ */
+extern int com_core_send(int handle, const char *buffer, int size, double timeout);
+
+/**
+ * @brief
+ * @details
+ * @remarks
+ * @param[in] handle
+ * @param[in] buffer
+ * @param[in] size
+ * @param[in] timeout
+ * @param[in] fd
+ * @return int
+ * @retval
+ * @sa
+ */
+extern int com_core_send_with_fd(int handle, const char *buffer, int size, double timeout, int fd);
+
+/**
+ * @brief
+ * @details
+ * @remarks
+ * @param[in] handle
+ * @param[out] buffer
+ * @param[in] size
+ * @param[out] sender_pid
+ * @param[in] timeout
+ * @param[out] fd
+ */
+extern int com_core_recv_with_fd(int handle, char *buffer, int size, int *sender_pid, double timeout, int *fd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+/* End of a file */
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2013 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 server_fd, int handle, guint id, void *data, int watch);
+
+/**
+ * @note
+ * This function will returns 0 if it is processed peacefully,
+ * But 1 will be returned if it is already called. (in case of the nested call)
+ */
+extern int invoke_disconn_cb_list(int handle, int remove_id, int remove_data, int watch);
+
+/* End of a file */
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2013 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
+};
+
+/*!
+ * \brief Add permanent path to route a packet
+ * \details N/A
+ * \remarks N/A
+ * \param[in] handle
+ * \param[in] address
+ * \param[in] to
+ * \return int
+ * \retval
+ * \sa
+ */
+extern int com_core_packet_router_add_route(int handle, unsigned long address, int to);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] handle
+ * \param[in] address
+ * \return int
+ * \retval
+ * \sa
+ */
+extern int com_core_packet_router_del_route(int handle, unsigned long address);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] handle
+ * \param[in] address
+ * \param[in] to
+ * \return int
+ * \retval
+ * \sa
+ */
+extern int com_core_packet_router_update_route(int handle, unsigned long address, int to);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] type
+ * \param[in] evt_cb
+ * \param[in] data
+ * \return int
+ * \retval
+ * \sa
+ */
+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);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] type
+ * \param[in] evt_cb
+ * \param[in] data
+ * \return int
+ * \retval
+ * \sa
+ */
+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);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] sock
+ * \param[in] timeout
+ * \param[in] table
+ * \return int
+ * \retval
+ * \sa
+ */
+extern int com_core_packet_router_server_init(const char *sock, double timeout, struct method *table);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] handle
+ * \return void *
+ * \retval
+ * \sa
+ */
+extern void *com_core_packet_router_server_fini(int handle);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] sock
+ * \param[in] timeout
+ * \param[in] table
+ * \return int
+ * \retval
+ * \sa
+ */
+extern int com_core_packet_router_client_init(const char *sock, double timeout, struct method *table);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] handle
+ * \return void *
+ * \retval
+ * \sa
+ */
+extern void *com_core_packet_router_client_fini(int handle);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] handle
+ * \param[in] packet
+ * \param[in] timeout
+ * \param[in] recv_cb
+ * \param[in] data
+ * \return int
+ * \retval
+ * \sa
+ */
+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);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] handle
+ * \param[in] packet
+ * \return int
+ * \retval
+ * \sa
+ */
+extern int com_core_packet_router_send_only(int handle, struct packet *packet);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] addr
+ * \param[in] packet
+ * \param[in] timeout
+ * \return struct packet *
+ * \retval
+ * \sa
+ */
+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 */
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2013 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);
+};
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] handle
+ * \param[in] packet
+ * \param[in] timeout
+ * \param[in] recv_cb
+ * \param[in] data
+ * \return int
+ * \retval
+ * \sa
+ */
+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);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] handle
+ * \param[in] packet
+ * \return int
+ * \retval
+ * \sa
+ */
+extern int com_core_packet_send_only(int handle, struct packet *packet);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] addr
+ * \param[in] packet
+ * \param[in] timeout
+ * \return struct packet *
+ * \retval
+ * \sa
+ */
+extern struct packet *com_core_packet_oneshot_send(const char *addr, struct packet *packet, double timeout);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] addr
+ * \param[in] is_sync
+ * \param[in] table
+ * \return int
+ * \retval
+ * \sa
+ */
+extern int com_core_packet_client_init(const char *addr, int is_sync, struct method *table);
+
+/**
+ * @brief
+ * @details
+ * @remarks
+ * @param[in]
+ * @return int
+ * @retval
+ * @sa
+ */
+extern int com_core_packet_client_init_by_fd(int fd, int is_sync, struct method *table);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] handle
+ * \return int
+ * \retval
+ * \sa
+ */
+extern int com_core_packet_client_fini(int handle);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] addr
+ * \param[in] table
+ * \return int
+ * \retval
+ * \sa
+ */
+extern int com_core_packet_server_init(const char *addr, struct method *table);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] addr
+ * \param[in] table
+ * \return int
+ * \retval
+ * \sa
+ */
+extern int com_core_packet_server_init_with_permission(const char *addr, struct method *table, const char *label);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] handle
+ * \return int
+ * \retval
+ * \sa
+ */
+extern int com_core_packet_server_fini(int handle);
+extern void com_core_packet_server_disconnect_handle(int handle);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] flag
+ * \return void
+ * \sa
+ */
+extern void com_core_packet_use_thread(int flag);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+/* End of a file */
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2013 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, const char *label, int (*service_cb)(int fd, void *data), void *data);
+extern int com_core_thread_client_create_by_fd(int client_fd, 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);
+
+extern int com_core_thread_recv_with_fd(int handle, char *buffer, int size, int *sender_pid, double timeout, int *fd);
+extern int com_core_thread_send_with_fd(int handle, const char *buffer, int size, double timeout, int fd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+/* End of a file */
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2013 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(SECURE_LOGD)
+#define SECURE_LOGD LOGD
+#endif
+
+#if !defined(SECURE_LOGE)
+#define SECURE_LOGE LOGE
+#endif
+
+#if !defined(SECURE_LOGW)
+#define SECURE_LOGW LOGW
+#endif
+
+#if !defined(FLOG)
+#define DbgPrint(format, arg...) SECURE_LOGD(format, ##arg)
+#define ErrPrint(format, arg...) SECURE_LOGE(format, ##arg)
+#else
+extern FILE *__file_log_fp;
+#define DbgPrint(format, arg...) do { fprintf(__file_log_fp, "[LOG] [\e[32m%s/%s\e[0m:%d] " format, util_basename(__FILE__), __func__, __LINE__, ##arg); fflush(__file_log_fp); } while (0)
+
+#define ErrPrint(format, arg...) do { fprintf(__file_log_fp, "[ERR] [\e[32m%s/%s\e[0m:%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 */
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2013 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 */
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2013 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 */
+};
+
+/*!
+ * \brief Version of current protocol (packet)
+ */
+#define PACKET_VERSION 3
+
+/*!
+ * \brief Maximum length of a command string
+ */
+#define PACKET_MAX_CMD 24
+#define PACKET_CMD_INT_TAG 0x01
+
+/*!
+ * \brief Create a packet
+ * \details N/A
+ * \remarks N/A
+ * \param[in] command
+ * \param[in] fmt
+ * \param[in] ...
+ * \return struct packet *
+ * \retval
+ * \sa packet_create_noack
+ * \sa packet_create_reply
+ * \sa packet_destroy
+ * \sa packet_type
+ */
+extern struct packet *packet_create(const char *command, const char *fmt, ...);
+
+/*!
+ * \brief Create a packet which doesn't need reply
+ * \details N/A
+ * \remarks N/A
+ * \param[in] command
+ * \param[in] fmt
+ * \param[in] ...
+ * \return struct packet *
+ * \retval
+ * \sa packet_create
+ * \sa packet_create_reply
+ * \sa packet_destroy
+ * \sa packet_type
+ */
+extern struct packet *packet_create_noack(const char *command, const char *fmt, ...);
+
+/*!
+ * \brief Create a reply packet
+ * \details
+ * This API related with packet_create function
+ * If the packet is created using packet_create_noack, this function will returns ERROR
+ * \remarks N/A
+ * \param[in] packet
+ * \param[in] fmt
+ * \param[in] ...
+ * \return struct packet *
+ * \retval
+ * \sa packet_create
+ * \sa packet_create_reply
+ * \sa packet_destroy
+ */
+extern struct packet *packet_create_reply(const struct packet *packet, const char *fmt, ...);
+
+/*!
+ * \brief Parse the data from a packet
+ * \details
+ * parsed data will be destroyed if the packet is destroyed.
+ * \remarks N/A
+ * \param[in] packet
+ * \param[in] fmt Format string: s - string, i - integer, d - double
+ * \param[out] ... Lists of variables to get the address of each data.
+ * \return int
+ * \retval
+ * \sa packet_create
+ * \sa packet_create_reply
+ * \sa pcaket_create_noack
+ */
+extern int packet_get(const struct packet *packet, const char *fmt, ...);
+
+/*!
+ * \brief Destroy a packet
+ * \details N/A
+ * \remarks N/A
+ * \param[in] packet
+ * \return int
+ * \retval
+ * \sa packet_create
+ * \sa packet_create_reply
+ * \sa packet_create_noack
+ */
+extern int packet_destroy(struct packet *packet);
+
+/*!
+ * \brief Increase the reference count
+ * \details
+ * com_core_packet_send series functions will destroy the packet after it sends them
+ * If you want reuse the sent packet again, increase the reference count of a packet
+ * Then the packet will not be destroyed even though returns from the com_core_packet_send series functions
+ * \remarks N/A
+ * \param[in] packet
+ * \return struct packet *
+ * \retval
+ * \sa packet_unref
+ */
+extern struct packet *packet_ref(struct packet *packet);
+
+/*!
+ * \brief Decrease the reference count
+ * \details
+ * If the reference count reaches to ZERO, the packet will be destroyed automatically.
+ * \remarks N/A
+ * \param[in] packet
+ * \return struct packet *
+ * \retval
+ * \sa packet_ref
+ */
+extern struct packet *packet_unref(struct packet *packet);
+
+
+/*!
+ * \brief Get the payload.
+ * \details N/A
+ * \remarks N/A
+ * \param[in] packet
+ * \return void *
+ * \retval
+ */
+extern const void * const packet_data(const struct packet *packet);
+
+/*!
+ * \brief Get the sequence number
+ * \details
+ * Sequence number is based on current clock time.
+ * Its uniq attribute will be kept if your system supports clock_get_time.
+ * \remarks N/A
+ * \param[in] packet
+ * \return double
+ * \retval 0 Invalid argument
+ * \retval sequence number
+ */
+extern const double const packet_seq(const struct packet *packet);
+
+/*!
+ * \brief Get the type of packet
+ * \details N/A
+ * \remarks N/A
+ * \param[in] packet
+ * \return enum packet_type
+ * \retval PACKET_REQ Request packet
+ * \retval PACKET_ACK Reply packet
+ * \retval PACKET_REQ_NOACK Request packet but it doesn't requires reply packet
+ * \retval PACKET_ERROR Error, invalid argument.
+ * \sa packet_create
+ * \sa packet_create_noack
+ * \sa packet_create_reply
+ */
+extern const enum packet_type const packet_type(const struct packet *packet);
+
+
+/*!
+ * \brief Get the packet flag
+ * \details
+ * Packet router process should concerns flag.
+ * If the packet flag'd _ROUTE, the server must has to route it by its address.
+ * So the router process should have routing table.
+ * And the router process should manage all address which are assigned to each procecss.
+ * \remarks
+ * Address and PID are different.
+ * \param[in] packet
+ * \return enum packet_flag
+ * \retval PACKET_FLAG_NOROUTE Default packet.
+ * \retval PACKET_FLAG_ROUTE Server process must has to route this packet before reach to its client.
+ * \sa packet_set_flag
+ */
+extern const enum packet_flag const packet_flag(const struct packet *packet);
+
+/*!
+ * \brief Set the packet flag
+ * \details N/A
+ * \remarks N/A
+ * \param[in] packet
+ * \param[in] flag
+ * \return int
+ * \retval -EINVAL Invalid argument
+ * \retval 0 Successfully set new flag
+ * \sa packet_flag
+ */
+extern int packet_set_flag(struct packet *packet, enum packet_flag flag);
+
+/*!
+ * \brief Get the source address of a packet
+ * \details N/A
+ * \remarks
+ * Address must has not to be ZERO it will be delat as an error case.
+ * \param[in] packet
+ * \return unsigned long
+ * \retval 0 Invalid argument
+ * \retval Address
+ * \sa packet_set_source
+ */
+extern const unsigned long const packet_source(const struct packet *packet);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks
+ * Address must has not to be ZERO it will be delat as an error case.
+ * \param[in] packet
+ * \param[in] source
+ * \return int
+ * \retval
+ * \sa packet_source
+ */
+extern int packet_set_source(struct packet *packet, unsigned long source);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks
+ * Address must has not to be ZERO it will be delat as an error case.
+ * \param[in] packet
+ * \return unsigned long
+ * \retval
+ * \sa packet_set_destination
+ */
+extern const unsigned long const packet_destination(const struct packet *packet);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks
+ * Address must has not to be ZERO it will be delat as an error case.
+ * \param[in] packet
+ * \param[in] destination
+ * \return int
+ * \retval
+ * \sa packet_destination
+ */
+extern int packet_set_destination(struct packet *packet, unsigned long destination);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] packet
+ * \param[in] mask
+ * \return int
+ * \retval
+ * \sa packet_mask
+ */
+extern int packet_set_mask(struct packet *packet, unsigned long mask);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] packet
+ * \return unsigned long
+ * \retval
+ * \sa packet_set_mask
+ */
+extern unsigned long packet_mask(const struct packet *packet);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] packet
+ * \return int
+ * \retval
+ * \sa packet_set_source
+ * \sa packet_set_destination
+ */
+extern int packet_swap_address(struct packet *packet);
+
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] packet
+ * \return int
+ * \retval
+ * \sa PACKET_VERSION
+ */
+extern const int const packet_version(const struct packet *packet);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] packet
+ * \return int
+ * \retval
+ */
+extern const int const packet_payload_size(const struct packet *packet);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] packet
+ * \return char *
+ * \retval
+ * \sa packet_create
+ * \sa packet_create_noack
+ */
+extern const char * const packet_command(const const struct packet *packet);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \return int
+ * \retval
+ */
+extern const int const packet_header_size(void);
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] packet
+ * \return int
+ * \retval
+ */
+extern const int const packet_size(const struct packet *packet);
+
+
+/*!
+ * \brief
+ * \details N/A
+ * \remarks N/A
+ * \param[in] packet
+ * \param[in] offset
+ * \param[in] data
+ * \param[in] size
+ * \return struct packet *
+ * \retval
+ */
+extern struct packet *packet_build(struct packet *packet, int offset, void *data, int size);
+
+extern int packet_fd(const struct packet *packet);
+extern int packet_set_fd(struct packet *packet, int fd);
+extern int packet_set_fd_close_handler_on_destroy(struct packet *packet, void (*close_cb)(int fd, void *data), void *data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+/* End of a file */
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2013 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
+
+/*!
+ * local:///tmp/.socket.file => /tmp/.socket.file
+ */
+#define COM_CORE_LOCAL_SCHEME "local://"
+#define COM_CORE_LOCAL_SCHEME_LEN (8)
+
+/*!
+ * remote://IPADDR:PORT
+ * remote://:PORT => Using INADDR_ANY in this case
+ */
+#define COM_CORE_REMOTE_SCHEME "remote://"
+#define COM_CORE_REMOTE_SCHEME_LEN (9)
+
+/*
+ * sdlocal:///tmp/.socket.file => /tmp/.socket.file
+ */
+#define COM_CORE_SD_LOCAL_SCHEME "sdlocal://"
+#define COM_CORE_SD_LOCAL_SCHEME_LEN (10)
+
+/*!
+ * \brief Create client connection
+ * \details N/A
+ * \remarks N/A
+ * \param[in] peer
+ * \return int
+ * \retval
+ * \sa
+ */
+extern int secure_socket_create_client(const char *peer);
+
+/*!
+ * \brief Create server connection
+ * \details N/A
+ * \remarks N/A
+ * \param[in] peer
+ * \return int
+ * \retval
+ * \sa
+ */
+extern int secure_socket_create_server(const char *peer);
+
+/*!
+ * \brief Create server connection
+ * \details N/A
+ * \remarks N/A
+ * \param[in] peer
+ * \return int
+ * \retval
+ * \sa
+ */
+extern int secure_socket_create_server_with_permission(const char *peer, const char *label);
+
+
+/*!
+ * \brief Get the raw handle to use it for non-blocking mode.
+ * \details N/A
+ * \remarks N/A
+ * \param[in] server_handle
+ * \return int
+ * \retval
+ * \sa
+ */
+extern int secure_socket_get_connection_handle(int server_handle);
+
+/*!
+ * \brief Send data to the connected peer.
+ * \details N/A
+ * \remarks N/A
+ * \param[in] conn
+ * \param[in] buffer
+ * \param[in] size
+ * \param[in] fd Shared fd which will be used from receiver process.
+ * \return int
+ * \retval
+ * \sa
+ */
+extern int secure_socket_send(int conn, const char *buffer, int size);
+extern int secure_socket_send_with_fd(int handle, const char *buffer, int size, int fd);
+
+/*!
+ * \brief Recv data from the connected peer. and its PID value
+ * \details N/A
+ * \remarks N/A
+ * \param[in] connn
+ * \param[out] buffer
+ * \param[in] size
+ * \param[out] sender_pid
+ * \param[out] fd shared fd which is comes from sender process.
+ * \return int
+ * \retval
+ * \sa
+ */
+extern int secure_socket_recv(int conn, char *buffer, int size, int *sender_pid);
+extern int secure_socket_recv_with_fd(int conn, char *buffer, int size, int *sender_pid, int *fd);
+
+/*!
+ * \brief Destroy a connection
+ * \details N/A
+ * \remarks N/A
+ * \param[in] conn
+ * \return int
+ * \retval
+ * \sa
+ */
+extern int secure_socket_destroy_handle(int conn);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+/* End of a file */
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2013 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);
+extern double util_timestamp(void);
+
+#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)
+
+#define CLOSE_PIPE(p) do { \
+ int status; \
+ status = close(p[PIPE_READ]); \
+ if (status < 0) \
+ ErrPrint("close: %s\n", strerror(errno)); \
+ status = close(p[PIPE_WRITE]); \
+ if (status < 0) \
+ ErrPrint("close: %s\n", strerror(errno)); \
+} while (0)
+
+#define PIPE_READ 0
+#define PIPE_WRITE 1
+#define PIPE_MAX 2
+
+/* End of a file */
--- /dev/null
+* Thu Sep 05 2013 Rusty Lynch <rusty.lynch@intel.com> accepted/tizen/20130710.221527@99bbffd
+- Add systemd socket activation support for secure sockets
+
+* Thu Sep 05 2013 Rusty Lynch <rusty.lynch@intel.com> accepted/tizen/20130710.221527@99bbffd
+- Add systemd socket activation support for secure sockets
+
+* Wed May 29 2013 Xavier Roche <xavrock.os@gmail.com> accepted/tizen/20130520.100914@a1a091c
+- Fix x86_64 build-install compliance
+
--- /dev/null
+<manifest>
+ <request>
+ <domain name="_"/>
+ </request>
+</manifest>
--- /dev/null
+Name: libcom-core
+Summary: Library for the light-weight IPC
+Version: 1.0.2
+Release: 1
+Group: Base/IPC
+License: Apache-2.0
+Source0: %{name}-%{version}.tar.gz
+Source1001: %{name}.manifest
+BuildRequires: cmake, gettext-tools, coreutils
+BuildRequires: libattr-devel
+BuildRequires: pkgconfig(dlog)
+BuildRequires: pkgconfig(glib-2.0)
+BuildRequires: pkgconfig(libsystemd-daemon)
+
+%description
+Light-weight IPC supporting library for Tizen
+
+%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 for Tizen (dev)
+
+%prep
+%setup -q
+cp %{SOURCE1001} .
+
+%build
+%if 0%{?sec_build_binary_debug_enable}
+export CFLAGS="$CFLAGS -DTIZEN_DEBUG_ENABLE"
+export CXXFLAGS="$CXXFLAGS -DTIZEN_DEBUG_ENABLE"
+export FFLAGS="$FFLAGS -DTIZEN_DEBUG_ENABLE"
+%endif
+
+%if 0%{?tizen_build_binary_release_type_eng}
+export CFLAGS="${CFLAGS} -DTIZEN_ENGINEER_MODE"
+export CXXFLAGS="${CXXFLAGS} -DTIZEN_ENGINEER_MODE"
+export FFLAGS="${FFLAGS} -DTIZEN_ENGINEER_MODE"
+%endif
+
+%cmake .
+make %{?jobs:-j%jobs}
+
+%install
+rm -rf %{buildroot}
+%make_install
+mkdir -p %{buildroot}/%{_datarootdir}/license
+
+%post -n libcom-core -p /sbin/ldconfig
+
+%postun -n libcom-core -p /sbin/ldconfig
+
+%files -n libcom-core
+%manifest %{name}.manifest
+%defattr(-,root,root,-)
+%{_libdir}/*.so*
+%{_datarootdir}/license/*
+
+%files devel
+%manifest %{name}.manifest
+%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
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2013 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 <sys/socket.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 *watch_list;
+ struct dlist *conn_cb_list;
+ struct dlist *disconn_cb_list;
+ struct dlist *disconn_fd_list;
+ enum processing_event_callback {
+ PROCESSING_NONE = 0x0,
+ PROCESSING_DISCONNECTION = 0x01,
+ PROCESSING_CONNECTION = 0x02,
+ } processing_event_callback;
+} s_info = {
+ .watch_list = NULL,
+ .conn_cb_list = NULL,
+ .disconn_cb_list = NULL,
+ .disconn_fd_list = NULL,
+ .processing_event_callback = PROCESSING_NONE,
+};
+
+struct watch_item {
+ int server_fd;
+ int fd;
+ guint id;
+ void *cbdata;
+};
+
+struct cbdata {
+ int (*service_cb)(int fd, void *data);
+ void *data;
+};
+
+struct evtdata {
+ int deleted;
+ int (*evt_cb)(int fd, void *data);
+ void *data;
+};
+
+static int watch_item_create(int server_fd, int handle, guint id, void *cbdata)
+{
+ struct watch_item *item;
+
+ item = malloc(sizeof(*item));
+ if (!item) {
+ return -ENOMEM;
+ }
+
+ item->server_fd = server_fd;
+ item->fd = handle;
+ item->id = id;
+ item->cbdata = cbdata;
+
+ DbgPrint("Watch Item is created for %d/%d\n", server_fd, handle);
+ s_info.watch_list = dlist_append(s_info.watch_list, item);
+ return 0;
+}
+
+static int watch_item_destroy(int handle, int remove_id, int remove_cbdata)
+{
+ struct dlist *l;
+ struct dlist *n;
+ struct watch_item *item;
+
+ dlist_foreach_safe(s_info.watch_list, l, n, item) {
+ if (item->fd == handle) {
+ s_info.watch_list = dlist_remove(s_info.watch_list, l);
+
+ DbgPrint("Watch item is destroyed for %d/%d\n", item->server_fd, item->fd);
+
+ if (remove_id && item->id) {
+ g_source_remove(item->id);
+ }
+
+ if (remove_cbdata && item->cbdata) {
+ free(item->cbdata);
+ }
+
+ free(item);
+ return 0;
+ }
+ }
+
+ DbgPrint("No entry found\n");
+ return -ENOENT;
+}
+
+static void watch_item_destroy_all(int socket_fd)
+{
+ struct dlist *l;
+ struct dlist *n;
+ struct watch_item *item;
+
+ dlist_foreach_safe(s_info.watch_list, l, n, item) {
+ if (item->server_fd == socket_fd) {
+ DbgPrint("Watch item removed: %d/%d\n", item->server_fd, item->fd);
+ /**
+ * @WARN
+ * If the watch_list item is removed from disconnected
+ * callback, this list loop can be broken.
+ * Please check it again.
+ */
+ (void)invoke_disconn_cb_list(item->fd, 0, 0, 0);
+ /**
+ * @note
+ * socket_fd will be closed by caller.
+ * so we do not need to close it at here.
+ */
+
+ s_info.watch_list = dlist_remove(s_info.watch_list, l);
+ if (item->id > 0) {
+ g_source_remove(item->id);
+ }
+ free(item->cbdata);
+ free(item);
+ }
+ }
+}
+
+HAPI void invoke_con_cb_list(int server_fd, int handle, guint id, void *data, int watch)
+{
+ struct dlist *l;
+ struct dlist *n;
+ struct evtdata *cbdata;
+
+ if (watch) {
+ if (watch_item_create(server_fd, handle, id, data) < 0) {
+ ErrPrint("Failed to create a watch item\n");
+ }
+ }
+
+ s_info.processing_event_callback |= PROCESSING_CONNECTION;
+ dlist_foreach_safe(s_info.conn_cb_list, l, n, cbdata) {
+ /*!
+ * \NOTE
+ * cbdata->deleted must has to be checked before call the function and
+ * return from the function call.
+ */
+ if (cbdata->deleted || cbdata->evt_cb(handle, cbdata->data) < 0 || cbdata->deleted) {
+ s_info.conn_cb_list = dlist_remove(s_info.conn_cb_list, l);
+ free(cbdata);
+ }
+ }
+ s_info.processing_event_callback &= ~PROCESSING_CONNECTION;
+}
+
+HAPI int invoke_disconn_cb_list(int handle, int remove_id, int remove_data, int watch)
+{
+ struct dlist *l;
+ struct dlist *n;
+ struct evtdata *cbdata;
+ void *item;
+
+ /**
+ * @note
+ * Basically, the disconnected handler will be called once.
+ * But from the disconnected callback, someone calls fini(fd),
+ * this disconnection callback can be called again.
+ * So we have to check whether this is a nested call or not.
+ * If it is a nested call, we should not do anything anymore.
+ */
+ dlist_foreach_safe(s_info.disconn_fd_list, l, n, item) {
+ if (handle == (int)((long)item)) { /*!< Cast for 64 bits */
+ DbgPrint("nested destroyer %d\n", handle);
+ return 1;
+ }
+ }
+
+ s_info.disconn_fd_list = dlist_append(s_info.disconn_fd_list, (void *)((long)handle)); /*!< Cast for 64 bits */
+
+ s_info.processing_event_callback |= PROCESSING_DISCONNECTION;
+ dlist_foreach_safe(s_info.disconn_cb_list, l, n, cbdata) {
+ /**
+ * @note
+ * cbdata->deleted must has to be checked before call the function and
+ * return from the function call.
+ */
+ if (cbdata->deleted || cbdata->evt_cb(handle, cbdata->data) < 0 || cbdata->deleted) {
+ s_info.disconn_cb_list = dlist_remove(s_info.disconn_cb_list, l);
+ free(cbdata);
+ }
+ }
+ s_info.processing_event_callback &= ~PROCESSING_DISCONNECTION;
+
+ if (watch) {
+ if (watch_item_destroy(handle, remove_id, remove_data) < 0) {
+ ErrPrint("Failed to destroy watch item\n");
+ }
+ }
+
+ dlist_remove_data(s_info.disconn_fd_list, (void *)((long)handle)); /*!< Cast for 64 bits */
+ return 0;
+}
+
+static int validate_handle(int fd)
+{
+ int error;
+ socklen_t len;
+
+ len = sizeof(error);
+ if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
+ ErrPrint("getsockopt: %s\n", strerror(errno));
+ return 0;
+ }
+
+ return !(error == EBADF);
+}
+
+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");
+ (void)invoke_disconn_cb_list(client_fd, 0, 1, 1);
+ 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");
+ (void)invoke_disconn_cb_list(client_fd, 0, 1, 1);
+ 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);
+ (void)invoke_disconn_cb_list(client_fd, 0, 1, 1);
+ secure_socket_destroy_handle(client_fd);
+ return FALSE;
+ }
+
+ /* Check whether the socket FD is closed or not */
+ if (!validate_handle(client_fd)) {
+ (void)invoke_disconn_cb_list(client_fd, 0, 1, 1);
+ 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");
+ watch_item_destroy_all(socket_fd);
+ secure_socket_destroy_handle(socket_fd);
+ free(cbdata);
+ return FALSE;
+ }
+
+ if ((cond & G_IO_ERR) || (cond & G_IO_HUP) || (cond & G_IO_NVAL)) {
+ ErrPrint("Client connection is lost\n");
+ watch_item_destroy_all(socket_fd);
+ secure_socket_destroy_handle(socket_fd);
+ free(cbdata);
+ return FALSE;
+ }
+
+ client_fd = secure_socket_get_connection_handle(socket_fd);
+ if (client_fd < 0) {
+ /* Keep server running */
+ return TRUE;
+ }
+ DbgPrint("New connectino arrived: server(%d), client(%d)\n", socket_fd, 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);
+ /* Keep server running */
+ return TRUE;
+ }
+
+ 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);
+ /* Keep server running */
+ return TRUE;
+ }
+
+ g_io_channel_unref(gio);
+
+ invoke_con_cb_list(socket_fd, client_fd, id, NULL, 1);
+
+ if (!validate_handle(socket_fd)) {
+ watch_item_destroy_all(socket_fd);
+ return FALSE;
+ }
+
+ /* Keep server running */
+ return TRUE;
+}
+
+EAPI int com_core_server_create(const char *addr, int is_sync, const char *label, 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_with_permission(addr, label);
+ 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 server 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;
+ }
+
+ if (watch_item_create(fd, fd, id, cbdata) < 0) {
+ GError *err = NULL;
+
+ ErrPrint("Failed to create a watch item\n");
+ g_source_remove(id);
+
+ 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 -ENOMEM;
+ }
+
+ 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, client_fd, id, cbdata, 1);
+ return client_fd;
+}
+
+EAPI int com_core_client_create_by_fd(int client_fd, int is_sync, int (*service_cb)(int fd, void *data), void *data)
+{
+ GIOChannel *gio;
+ guint id;
+ struct cbdata *cbdata;
+
+ if (!validate_handle(client_fd)) {
+ ErrPrint("Invalid handle: %d\n", client_fd);
+ return -EINVAL;
+ }
+
+ cbdata = malloc(sizeof(*cbdata));
+ if (!cbdata) {
+ ErrPrint("Heap: %s\n", strerror(errno));
+ return -ENOMEM;
+ }
+
+ cbdata->service_cb = service_cb;
+ cbdata->data = data;
+
+ 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, client_fd, id, cbdata, 1);
+ 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;
+ cbdata->deleted = 0;
+
+ 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_with_fd(int handle, char *buffer, int size, int *sender_pid, double timeout, int *fd)
+{
+ int readsize;
+ int ret;
+ int *recv_fd;
+
+ fd_set set;
+
+ recv_fd = fd;
+ 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_with_fd(handle, buffer + readsize, size, sender_pid, recv_fd);
+ if (ret < 0) {
+ if (ret == -EAGAIN) {
+ DbgPrint("Retry to get data (%d:%d)\n", readsize, size);
+ continue;
+ }
+ DbgPrint("[%d] recv returns: %d\n", handle, ret);
+ return ret;
+ } else if (ret == 0) {
+ DbgPrint("Disconnected(req.size: %d)\n", size);
+ return 0;
+ }
+
+ recv_fd = NULL; /** Get it only for the first time */
+ size -= ret;
+ readsize += ret;
+ }
+
+ return readsize;
+}
+
+EAPI int com_core_recv(int handle, char *buffer, int size, int *sender_pid, double timeout)
+{
+ return com_core_recv_with_fd(handle, buffer, size, sender_pid, timeout, NULL);
+}
+
+EAPI int com_core_send_with_fd(int handle, const char *buffer, int size, double timeout, int fd)
+{
+ 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_with_fd(handle, buffer + writesize, size, fd);
+ 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;
+ }
+
+ fd = -1; /** Send only once if it is fd */
+ size -= ret;
+ writesize += ret;
+ }
+
+ return writesize;
+}
+
+EAPI int com_core_send(int handle, const char *buffer, int size, double timeout)
+{
+ return com_core_send_with_fd(handle, buffer, size, timeout, -1);
+}
+
+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;
+
+ if ((s_info.processing_event_callback & PROCESSING_CONNECTION) == PROCESSING_CONNECTION) {
+ cbdata->deleted = 1;
+ } else {
+ 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;
+
+ if ((s_info.processing_event_callback & PROCESSING_DISCONNECTION) == PROCESSING_DISCONNECTION) {
+ cbdata->deleted = 1;
+ } else {
+ 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);
+ if (invoke_disconn_cb_list(handle, 1, 1, 1) == 0) {
+ secure_socket_destroy_handle(handle);
+ }
+ return 0;
+}
+
+EAPI int com_core_client_destroy(int handle)
+{
+ DbgPrint("Close client handle[%d]\n", handle);
+ if (invoke_disconn_cb_list(handle, 1, 1, 1) == 0) {
+ secure_socket_destroy_handle(handle);
+ }
+ return 0;
+}
+
+/* End of a file */
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2013 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"
+
+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[PIPE_MAX];
+ int send_pipe[PIPE_MAX];
+
+ 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 struct packet *get_recv_packet(struct router *router, int *handle, pid_t *pid);
+static int put_recv_packet(struct router *router, int handle, struct packet *packet, pid_t pid);
+
+static struct packet *get_send_packet(struct router *router, int *handle);
+static 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 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;
+
+ const char *cmd = packet_command(packet);
+ if (cmd) {
+ for (i = 0; table[i].cmd; i++) {
+
+ if (strcmp(table[i].cmd, cmd)) {
+ 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 *)(unsigned long)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);
+
+ CLOSE_PIPE(router->recv_pipe);
+
+ 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) {
+ CLOSE_PIPE(router->recv_pipe);
+ CLOSE_PIPE(router->send_pipe);
+
+ 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);
+
+ CLOSE_PIPE(router->recv_pipe);
+ CLOSE_PIPE(router->send_pipe);
+
+ 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);
+
+ CLOSE_PIPE(router->recv_pipe);
+ CLOSE_PIPE(router->send_pipe);
+
+ 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);
+ }
+
+ CLOSE_PIPE(router->recv_pipe);
+ CLOSE_PIPE(router->send_pipe);
+
+ 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 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 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 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 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 *)(unsigned long)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 *)(unsigned long)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 join 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 */
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2013 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
+/**
+ * @brief
+ * If the first character is started with 0x01,
+ * The command string should be treated as a index of command table.
+ */
+
+static struct info {
+ struct dlist *recv_list;
+ struct dlist *request_list;
+ char *addr;
+
+ struct {
+ int (*server_create)(const char *addr, int is_sync, const char *label, 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 (*client_create_by_fd)(int fd, 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);
+
+ int (*recv_with_fd)(int handle, char *buffer, int size, int *sender_pid, double timeout, int *fd);
+ int (*send_with_fd)(int handle, const char *buffer, int size, double timeout, int fd);
+ } 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,
+ .client_create_by_fd = com_core_client_create_by_fd,
+ .server_destroy = com_core_server_destroy,
+ .client_destroy = com_core_client_destroy,
+ .recv = com_core_recv,
+ .send = com_core_send,
+ .recv_with_fd = com_core_recv_with_fd,
+ .send_with_fd = com_core_send_with_fd,
+ },
+ .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;
+
+ int inuse;
+};
+
+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;
+
+ int inuse;
+};
+
+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)
+{
+ struct dlist *l;
+
+ if (ctx->inuse) {
+ return;
+ }
+
+ l = dlist_find_data(s_info.request_list, ctx);
+ if (!l) {
+ return;
+ }
+
+ s_info.request_list = dlist_remove(s_info.request_list, l);
+
+ packet_unref(ctx->packet);
+ 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;
+ ctx->inuse = 0;
+
+ 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 recreate_recv_ctx(struct recv_ctx *ctx)
+{
+ if (ctx->packet) {
+ packet_destroy(ctx->packet);
+ ctx->packet = NULL;
+ }
+ ctx->state = RECV_STATE_INIT;
+ ctx->offset = 0;
+ ctx->pid = (pid_t)-1;
+ // ctx->inuse
+ // ctx->handle
+ // ctx->timeout
+}
+
+static inline void destroy_recv_ctx(struct recv_ctx *ctx)
+{
+ struct dlist *l;
+
+ if (ctx->inuse) {
+ return;
+ }
+
+ l = dlist_find_data(s_info.recv_list, ctx);
+ if (!l) {
+ return;
+ }
+
+ s_info.recv_list = dlist_remove(s_info.recv_list, l);
+
+ 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;
+ ctx->inuse = 0;
+
+ s_info.recv_list = dlist_append(s_info.recv_list, ctx);
+ return ctx;
+}
+
+static inline int packet_ready(int handle, struct recv_ctx *receive, struct method *table)
+{
+ struct request_ctx *request;
+ double sequence;
+ struct packet *result;
+ register int i;
+ int ret;
+ const char *cmd;
+ unsigned int cmd_idx;
+
+ 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->inuse = 1;
+ receive->inuse = 1;
+ request->recv_cb(receive->pid, handle, receive->packet, request->data);
+ receive->inuse = 0;
+ request->inuse = 0;
+ }
+
+ destroy_request_ctx(request);
+ break;
+ case PACKET_REQ:
+ cmd_idx = 0xFFFFFFFF;
+ cmd = packet_command(receive->packet);
+ if (cmd) {
+ if (cmd[0] == PACKET_CMD_INT_TAG) {
+ unsigned int *p_cmd_idx = (unsigned int *)cmd;
+
+ /* Get rid of LSB 8 bits */
+ cmd_idx = *p_cmd_idx >> 8;
+ } else {
+ for (i = 0; table[i].cmd; i++) {
+ if (strcmp(table[i].cmd, cmd)) {
+ continue;
+ }
+
+ cmd_idx = (unsigned int)i;
+ break;
+ }
+ }
+ }
+
+ if (cmd_idx != 0xFFFFFFFF) {
+ receive->inuse = 1;
+ result = table[cmd_idx].handler(receive->pid, handle, receive->packet);
+ receive->inuse = 0;
+ if (result) {
+ if (packet_fd(result) >= 0) {
+ ret = s_info.vtable.send_with_fd(handle, (void *)packet_data(result), packet_size(result), DEFAULT_TIMEOUT, packet_fd(result));
+ } else {
+ 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;
+ case PACKET_REQ_NOACK:
+ cmd_idx = 0xFFFFFFFF;
+ cmd = packet_command(receive->packet);
+ if (cmd) {
+ if (cmd[0] == PACKET_CMD_INT_TAG) {
+ unsigned int *p_cmd_idx = (unsigned int *)cmd;
+
+ /* Get rid of LSB 8 bits */
+ cmd_idx = *p_cmd_idx >> 8;
+ } else {
+ for (i = 0; table[i].cmd; i++) {
+ if (strcmp(table[i].cmd, cmd)) {
+ continue;
+ }
+
+ cmd_idx = (unsigned int)i;
+ break;
+ }
+ }
+ }
+
+ if (cmd_idx != 0xFFFFFFFF) {
+ receive->inuse = 1;
+ result = table[cmd_idx].handler(receive->pid, handle, receive->packet);
+ receive->inuse = 0;
+ 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;
+ int inuse_found = 0;
+ pid_t pid = (pid_t)-1;
+
+ receive = find_recv_ctx(handle);
+ if (receive) {
+ pid = receive->pid;
+ }
+
+ 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->inuse) {
+ inuse_found = 1;
+ continue;
+ }
+
+ if (request->recv_cb) {
+ request->recv_cb(pid, handle, NULL, request->data);
+ }
+
+ destroy_request_ctx(request);
+ }
+
+ if (receive && !inuse_found) {
+ destroy_recv_ctx(receive);
+ }
+
+ return 0;
+}
+
+static int service_cb(int handle, void *data)
+{
+ struct recv_ctx *receive;
+ pid_t pid;
+ int ret;
+ int size;
+ char *ptr;
+ int fd = -1;
+
+ 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 (%d)\n", strerror(errno), size);
+ return -ENOMEM;
+ }
+
+ ret = s_info.vtable.recv_with_fd(handle, ptr, size, &pid, receive->timeout, &fd);
+ 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 */
+ }
+
+ if (fd >= 0) {
+ if (packet_fd(receive->packet) >= 0) {
+ DbgPrint("Packet already has FD: %d (new: %d)\n", packet_fd(receive->packet), fd);
+ }
+
+ packet_set_fd(receive->packet, fd);
+ }
+
+ 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 (%d)\n", strerror(errno), size);
+ return -ENOMEM;
+ }
+
+ ret = s_info.vtable.recv_with_fd(handle, ptr, size, &pid, receive->timeout, &fd);
+ 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;
+ }
+
+ if (fd >= 0) {
+ if (packet_fd(receive->packet) >= 0) {
+ DbgPrint("Packet already has FD: %d (new: %d)\n", packet_fd(receive->packet), fd);
+ }
+
+ packet_set_fd(receive->packet, fd);
+ }
+
+ 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) {
+ /*!
+ * If ret is negative value, the receive context will be destroyed from disconnected callback
+ */
+ recreate_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);
+
+ if (packet_fd(packet) >= 0) {
+ ret = s_info.vtable.send_with_fd(handle, (void *)packet_data(packet), packet_size(packet), DEFAULT_TIMEOUT, packet_fd(packet));
+ } else {
+ 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;
+ }
+
+ if (packet_fd(packet) >= 0) {
+ ret = s_info.vtable.send_with_fd(handle, (void *)packet_data(packet), packet_size(packet), DEFAULT_TIMEOUT, packet_fd(packet));
+ } else {
+ 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;
+ int recv_fd = -1;
+
+ if (!addr || !packet) {
+ ErrPrint("Invalid argument\n");
+ return NULL;
+ }
+
+ fd = secure_socket_create_client(addr);
+ if (fd < 0) {
+ return NULL;
+ }
+
+ 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));
+ }
+
+ if (packet_fd(packet) >= 0) {
+ ret = com_core_send_with_fd(fd, (char *)packet_data(packet), packet_size(packet), DEFAULT_TIMEOUT, packet_fd(packet));
+ } else {
+ ret = com_core_send(fd, (char *)packet_data(packet), packet_size(packet), DEFAULT_TIMEOUT);
+ }
+ if (ret < 0) {
+ goto out;
+ }
+
+ ptr = malloc(packet_header_size());
+ if (!ptr) {
+ ErrPrint("Heap: %s (%d)\n", strerror(errno), packet_header_size());
+ goto out;
+ }
+
+ offset = 0;
+ ret = com_core_recv_with_fd(fd, (char *)ptr, packet_header_size(), &pid, timeout, &recv_fd);
+ if (ret <= 0) {
+ DbgPrint("Recv returns %d\n", ret);
+ free(ptr);
+ goto out;
+ } else {
+ DbgPrint("Recv'd size: %d (header: %d) pid: %d, fd: %d\n", ret, packet_header_size(), pid, recv_fd);
+ result = packet_build(result, offset, ptr, ret);
+ offset += ret;
+ free(ptr);
+ if (!result) {
+ ErrPrint("Failed to build a packet\n");
+ goto out;
+ }
+
+ if (recv_fd >= 0) {
+ if (packet_fd(result) >= 0) {
+ DbgPrint("Packet already has FD: %d (new: %d)\n", packet_fd(result), recv_fd);
+ }
+
+ DbgPrint("Update FD: %d\n", recv_fd);
+ packet_set_fd(result, recv_fd);
+ }
+ }
+
+ size = packet_payload_size(result);
+ 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 (%d)\n", strerror(errno), size);
+ packet_destroy(result);
+ result = NULL;
+ goto out;
+ }
+
+ ret = com_core_recv_with_fd(fd, (char *)ptr, size, &pid, timeout, &recv_fd);
+ if (ret <= 0) {
+ DbgPrint("Recv returns %d\n", ret);
+ free(ptr);
+ packet_destroy(result);
+ result = NULL;
+ } else {
+ DbgPrint("Recv'd %d bytes (pid: %d), fd: %d\n", ret, pid, recv_fd);
+ result = packet_build(result, offset, ptr, ret);
+ offset += ret;
+ free(ptr);
+
+ if (result && recv_fd >= 0) {
+ if (packet_fd(result) >= 0) {
+ DbgPrint("Packet already has FD: %d (new: %d)\n", packet_fd(result), recv_fd);
+ }
+
+ DbgPrint("Update FD: %d\n", recv_fd);
+ packet_set_fd(result, recv_fd);
+ }
+ }
+
+out:
+ secure_socket_destroy_handle(fd);
+ DbgPrint("Close connection: %d (recv_fd: %d)\n", fd, recv_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_init_by_fd(int fd, int is_sync, struct method *table)
+{
+ int ret;
+
+ ret = com_core_packet_init();
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = s_info.vtable.client_create_by_fd(fd, 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, NULL, service_cb, table);
+ if (ret < 0) {
+ com_core_packet_fini();
+ }
+
+ return ret;
+}
+
+EAPI int com_core_packet_server_init_with_permission(const char *addr, struct method *table, const char *label)
+{
+ int ret;
+
+ ret = com_core_packet_init();
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = s_info.vtable.server_create(addr, 0, label, service_cb, table);
+ if (ret < 0) {
+ com_core_packet_fini();
+ }
+
+ return ret;
+}
+
+EAPI void com_core_packet_server_disconnect_handle(int handle)
+{
+ s_info.vtable.server_destroy(handle);
+}
+
+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 (s_info.initialized) {
+ ErrPrint("com-core method is in use\n");
+ return;
+ }
+
+ 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.client_create_by_fd = com_core_thread_client_create_by_fd;
+ 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;
+ s_info.vtable.recv_with_fd = com_core_thread_recv_with_fd;
+ s_info.vtable.send_with_fd = com_core_thread_send_with_fd;
+ } else {
+ s_info.vtable.server_create = com_core_server_create;
+ s_info.vtable.client_create = com_core_client_create;
+ s_info.vtable.client_create_by_fd = com_core_client_create_by_fd;
+ 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;
+ s_info.vtable.recv_with_fd = com_core_recv_with_fd;
+ s_info.vtable.send_with_fd = com_core_send_with_fd;
+ }
+}
+
+/* End of a file */
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2013 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 <sys/types.h>
+#include <sys/socket.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 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;
+ int fd;
+};
+
+/*!
+ * \brief Thread Control Block
+ */
+struct tcb {
+ pthread_t thid;
+ int handle;
+ struct dlist *chunk_list;
+ int evt_pipe[PIPE_MAX];
+ int ctrl_pipe[PIPE_MAX];
+ pthread_mutex_t chunk_lock;
+ guint id; /*!< g_io_watch */
+
+ int server_handle;
+
+ int (*service_cb)(int fd, void *data);
+ void *data;
+};
+
+static ssize_t write_safe(int fd, const void *data, size_t bufsz)
+{
+ int ret;
+ int again;
+
+ do {
+ again = 0;
+ ret = write(fd, data, bufsz);
+ if (ret < 0) {
+ ret = -errno;
+ switch (ret) {
+ case -EAGAIN:
+ case -EINTR:
+ again = 1;
+ ErrPrint("Interrupted[%d] Again[%d]\n", fd, -ret);
+ break;
+ default:
+ ErrPrint("Failed to write: %s (%d)\n", strerror(-ret), -ret);
+ return ret;
+ }
+ }
+ } while (again);
+
+ return ret;
+}
+
+/*!
+ * \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)
+{
+ int status;
+ struct dlist *l;
+ struct dlist *n;
+ void *res = NULL;
+ struct chunk *chunk;
+
+ if (write_safe(tcb->ctrl_pipe[PIPE_WRITE], &tcb, sizeof(tcb)) != sizeof(tcb)) {
+ ErrPrint("Unable to write CTRL pipe (%d)\n", sizeof(tcb));
+ }
+
+ secure_socket_destroy_handle(tcb->handle);
+
+ status = pthread_join(tcb->thid, &res);
+ if (status != 0) {
+ ErrPrint("Join: %s\n", strerror(status));
+ } else {
+ ErrPrint("Thread returns: %d\n", (int)res);
+ }
+
+ dlist_foreach_safe(tcb->chunk_list, l, n, chunk) {
+ /*!
+ * Discarding all packets
+ */
+ DbgPrint("Discarding chunks\n");
+ 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 int 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_safe(tcb->evt_pipe[PIPE_WRITE], &event_ch, sizeof(event_ch));
+ if (ret < 0) {
+ CRITICAL_SECTION_BEGIN(&tcb->chunk_lock);
+
+ dlist_remove_data(tcb->chunk_list, chunk);
+
+ CRITICAL_SECTION_END(&tcb->chunk_lock);
+ return ret;
+ }
+
+ if (ret != sizeof(event_ch)) {
+ ErrPrint("Failed to trigger reader\n");
+ }
+
+ /* Take a breathe */
+ pthread_yield();
+ return 0;
+}
+
+/*!
+ * \NOTE
+ * Running thread: Main
+ */
+static inline int wait_event(struct tcb *tcb, double timeout)
+{
+ fd_set set;
+ int ret;
+
+ 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;
+ chunk->fd = -1;
+ 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 fd;
+
+ 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);
+ FD_SET(tcb->ctrl_pipe[PIPE_READ], &set);
+
+ fd = tcb->handle > tcb->ctrl_pipe[PIPE_READ] ? tcb->handle : tcb->ctrl_pipe[PIPE_READ];
+
+ ret = select(fd + 1, &set, NULL, NULL, NULL);
+ if (ret < 0) {
+ if (errno == EINTR) {
+ DbgPrint("Select receives INTR\n");
+ continue;
+ }
+ ret = -errno;
+ /*!< Error */
+ ErrPrint("Error: %s\n", strerror(errno));
+ break;
+ } else if (ret == 0) {
+ ErrPrint("What happens? [%d]\n", tcb->handle);
+ continue;
+ }
+
+ if (FD_ISSET(tcb->ctrl_pipe[PIPE_READ], &set)) {
+ DbgPrint("Thread is canceled\n");
+ ret = -ECANCELED;
+ break;
+ }
+
+ 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;
+ }
+
+ ret = secure_socket_recv_with_fd(tcb->handle, chunk->data, chunk->size, &chunk->pid, &chunk->fd);
+ if (ret <= 0) {
+ destroy_chunk(chunk);
+ if (ret == -EAGAIN) {
+ DbgPrint("Retry to get data\n");
+ continue;
+ }
+
+ DbgPrint("Recv returns: %d\n", ret);
+ break;
+ }
+
+ /* Update chunk size */
+ chunk->size = ret;
+
+ /*!
+ * Count of chunk elements are same with PIPE'd data
+ */
+ if (chunk_append(tcb, chunk) < 0) {
+ destroy_chunk(chunk);
+ break;
+ }
+ }
+
+ DbgPrint("Client CB is terminated (%d)\n", tcb->handle);
+ /* Wake up main thread to get disconnected event */
+ event_ch = EVENT_TERM;
+
+ if (write_safe(tcb->evt_pipe[PIPE_WRITE], &event_ch, sizeof(event_ch)) != sizeof(event_ch)) {
+ ErrPrint("%d byte is not written\n", sizeof(event_ch));
+ }
+
+ return (void *)(unsigned long)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);
+ }
+
+ CLOSE_PIPE(tcb->evt_pipe);
+ CLOSE_PIPE(tcb->ctrl_pipe);
+
+ 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)) {
+ ErrPrint("PIPE is not valid\n");
+ goto errout;
+ }
+
+ if ((cond & G_IO_ERR) || (cond & G_IO_HUP) || (cond & G_IO_NVAL)) {
+ ErrPrint("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:
+ DbgPrint("Disconnecting\n");
+ (void)invoke_disconn_cb_list(tcb->handle, 0, 0, 0);
+ 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;
+
+ 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, 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;
+ }
+
+ if (pipe2(tcb->ctrl_pipe, O_CLOEXEC) < 0) {
+ ErrPrint("Error: %s\n", strerror(errno));
+
+ CLOSE_PIPE(tcb->evt_pipe);
+
+ 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: R(%d), W(%d)\n", client_fd, tcb->evt_pipe[PIPE_READ], tcb->evt_pipe[PIPE_WRITE]);
+ 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;
+ pthread_attr_t attr;
+ pthread_attr_t *pattr = NULL;
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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");
+ secure_socket_destroy_handle(tcb->handle);
+ 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);
+ secure_socket_destroy_handle(tcb->handle);
+ tcb_destroy(tcb);
+ server_destroy(server);
+ return FALSE;
+ }
+ g_io_channel_unref(gio);
+
+ invoke_con_cb_list(tcb->handle, tcb->handle, 0, NULL, 0);
+
+ ret = pthread_attr_init(&attr);
+ if (ret == 0) {
+ pattr = &attr;
+
+ ret = pthread_attr_setscope(pattr, PTHREAD_SCOPE_SYSTEM);
+ if (ret != 0) {
+ ErrPrint("setscope: %s\n", strerror(ret));
+ }
+
+ ret = pthread_attr_setinheritsched(pattr, PTHREAD_EXPLICIT_SCHED);
+ if (ret != 0) {
+ ErrPrint("setinheritsched: %s\n", strerror(ret));
+ }
+ } else {
+ ErrPrint("attr_init: %s\n", strerror(ret));
+ }
+ ret = pthread_create(&tcb->thid, pattr, client_cb, tcb);
+ if (pattr) {
+ pthread_attr_destroy(pattr);
+ }
+ if (ret != 0) {
+ ErrPrint("Thread creation failed: %s\n", strerror(ret));
+ (void)invoke_disconn_cb_list(tcb->handle, 0, 0, 0);
+ secure_socket_destroy_handle(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;
+ pthread_attr_t attr;
+ pthread_attr_t *pattr = NULL;
+
+ 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");
+ secure_socket_destroy_handle(tcb->handle);
+ 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);
+ secure_socket_destroy_handle(tcb->handle);
+ tcb_destroy(tcb);
+ return -EIO;
+ }
+
+ g_io_channel_unref(gio);
+
+ invoke_con_cb_list(tcb->handle, tcb->handle, 0, NULL, 0);
+
+ ret = pthread_attr_init(&attr);
+ if (ret == 0) {
+ pattr = &attr;
+
+ ret = pthread_attr_setscope(pattr, PTHREAD_SCOPE_SYSTEM);
+ if (ret != 0) {
+ ErrPrint("setscope: %s\n", strerror(ret));
+ }
+
+ ret = pthread_attr_setinheritsched(pattr, PTHREAD_EXPLICIT_SCHED);
+ if (ret != 0) {
+ ErrPrint("setinheritsched: %s\n", strerror(ret));
+ }
+ } else {
+ ErrPrint("attr_init: %s\n", strerror(ret));
+ }
+ ret = pthread_create(&tcb->thid, pattr, client_cb, tcb);
+ if (pattr) {
+ pthread_attr_destroy(pattr);
+ }
+ if (ret != 0) {
+ ErrPrint("Thread creation failed: %s\n", strerror(ret));
+ (void)invoke_disconn_cb_list(tcb->handle, 0, 0, 0);
+ secure_socket_destroy_handle(tcb->handle);
+ tcb_destroy(tcb);
+ return -EFAULT;
+ }
+
+ return tcb->handle;
+}
+
+static int validate_handle(int fd)
+{
+ int error;
+ socklen_t len;
+
+ len = sizeof(error);
+ if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
+ ErrPrint("getsockopt: %s\n", strerror(errno));
+ return 0;
+ }
+
+ return !(error == EBADF);
+}
+
+EAPI int com_core_thread_client_create_by_fd(int client_fd, int is_sync, int (*service_cb)(int fd, void *data), void *data)
+{
+ GIOChannel *gio;
+ struct tcb *tcb;
+ int ret;
+ pthread_attr_t attr;
+ pthread_attr_t *pattr = NULL;
+
+ if (!validate_handle(client_fd)) {
+ ErrPrint("Invalid handle: %d\n", client_fd);
+ return -EINVAL;
+ }
+
+ if (fcntl(client_fd, F_SETFD, FD_CLOEXEC) < 0) {
+ ErrPrint("Error: %s (%d)\n", strerror(errno), client_fd);
+ }
+
+ if (fcntl(client_fd, F_SETFL, O_NONBLOCK) < 0) {
+ ErrPrint("Error: %s (%d)\n", strerror(errno), client_fd);
+ }
+
+ 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");
+ secure_socket_destroy_handle(tcb->handle);
+ 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);
+ secure_socket_destroy_handle(tcb->handle);
+ tcb_destroy(tcb);
+ return -EIO;
+ }
+
+ g_io_channel_unref(gio);
+
+ invoke_con_cb_list(tcb->handle, tcb->handle, 0, NULL, 0);
+
+ ret = pthread_attr_init(&attr);
+ if (ret == 0) {
+ pattr = &attr;
+
+ ret = pthread_attr_setscope(pattr, PTHREAD_SCOPE_SYSTEM);
+ if (ret != 0) {
+ ErrPrint("setscope: %s\n", strerror(ret));
+ }
+
+ ret = pthread_attr_setinheritsched(pattr, PTHREAD_EXPLICIT_SCHED);
+ if (ret != 0) {
+ ErrPrint("setinheritsched: %s\n", strerror(ret));
+ }
+ } else {
+ ErrPrint("attr_init: %s\n", strerror(ret));
+ }
+ ret = pthread_create(&tcb->thid, pattr, client_cb, tcb);
+ if (pattr) {
+ pthread_attr_destroy(pattr);
+ }
+ if (ret != 0) {
+ ErrPrint("Thread creation failed: %s\n", strerror(ret));
+ (void)invoke_disconn_cb_list(tcb->handle, 0, 0, 0);
+ secure_socket_destroy_handle(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, const char *label, int (*service_cb)(int fd, void *data), void *data)
+{
+ GIOChannel *gio;
+ int fd;
+ struct server *server;
+
+ fd = secure_socket_create_server_with_permission(addr, label);
+ 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;
+}
+
+EAPI int com_core_thread_send_with_fd(int handle, const char *buffer, int size, double timeout, int fd)
+{
+ 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_with_fd(tcb->handle, buffer + writesize, size, fd);
+ 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;
+ }
+
+ fd = -1; /* Send only once if it is fd */
+ size -= ret;
+ writesize += ret;
+ }
+
+ return writesize;
+}
+
+/*!
+ * \NOTE
+ * Running thread: Main
+ */
+EAPI int com_core_thread_send(int handle, const char *buffer, int size, double timeout)
+{
+ return com_core_thread_send_with_fd(handle, buffer, size, timeout, -1);
+}
+
+EAPI int com_core_thread_recv_with_fd(int handle, char *buffer, int size, int *sender_pid, double timeout, int *fd)
+{
+ int readsize;
+ int ret;
+ struct chunk *chunk;
+ struct dlist *l;
+ struct tcb *tcb;
+ int _sender_pid;
+ int _fd;
+
+ tcb = find_tcb_by_handle(handle);
+ if (!tcb) {
+ ErrPrint("TCB is not exists\n");
+ return -EINVAL;
+ }
+
+ if (!sender_pid) {
+ sender_pid = &_sender_pid;
+ }
+
+ if (!fd) {
+ fd = &_fd;
+ }
+
+ *fd = -1;
+ 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) {
+ 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");
+ } else if (event_ch == EVENT_TERM) {
+ DbgPrint("Disconnected\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->fd >= 0) {
+ *fd = chunk->fd;
+ }
+
+ if (chunk->offset == chunk->size) {
+ chunk_remove(tcb, chunk);
+ }
+ }
+
+ return readsize;
+}
+
+/*!
+ * \NOTE
+ * Running thread: Main
+ */
+EAPI int com_core_thread_recv(int handle, char *buffer, int size, int *sender_pid, double timeout)
+{
+ return com_core_thread_recv_with_fd(handle, buffer, size, sender_pid, timeout, NULL);
+}
+
+/*!
+ * \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;
+ }
+
+ if (invoke_disconn_cb_list(handle, 0, 0, 0) == 0) {
+ terminate_thread(tcb);
+ tcb_destroy(tcb);
+ }
+ return 0;
+ }
+
+ dlist_foreach_safe(s_info.server_list, l, n, server) {
+ if (server->handle != handle) {
+ continue;
+ }
+
+ if (invoke_disconn_cb_list(handle, 0, 0, 0) == 0) {
+ server_destroy(server);
+ }
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+/*!
+ * \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;
+ }
+
+ if (invoke_disconn_cb_list(handle, 0, 0, 0) == 0) {
+ terminate_thread(tcb);
+ tcb_destroy(tcb);
+ }
+ return 0;
+}
+
+/* End of a file */
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2013 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 */
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2013 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 <unistd.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;
+ void (*close_fd_cb)(int fd, void *data);
+ void *close_fd_cbdata;
+ int fd;
+ 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 int packet_set_fd(struct packet *packet, int fd)
+{
+ if (!packet || packet->state != VALID || !packet->data || fd < 0) {
+ return -EINVAL;
+ }
+
+ packet->fd = fd;
+ return 0;
+}
+
+EAPI int packet_fd(const struct packet *packet)
+{
+ if (!packet || packet->state != VALID || !packet->data) {
+ return -EINVAL;
+ }
+
+ return packet->fd;
+}
+
+EAPI int packet_set_fd_close_handler_on_destroy(struct packet *packet, void (*close_cb)(int fd, void *data), void *data)
+{
+ if (!packet || packet->state != VALID || !packet->data) {
+ return -EINVAL;
+ }
+
+ packet->close_fd_cb = close_cb;
+ packet->close_fd_cbdata = data;
+ 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;
+ int align;
+
+ while (*ptr) {
+ payload = packet->data->payload + packet->data->head.payload_size;
+
+ switch (*ptr) {
+ case 'i':
+ case 'I':
+ align = (unsigned long)payload & (sizeof(int) - 1);
+ if (align) {
+ align = sizeof(int) - align;
+ }
+
+ packet->data->head.payload_size += sizeof(int) + align;
+ packet->data = check_and_expand_packet(packet->data, &payload_size);
+ if (!packet->data) {
+ packet->state = INVALID;
+ free(packet);
+ packet = NULL;
+ goto out;
+ }
+
+ *((int *)(payload + align)) = (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':
+ align = (unsigned long)payload & (sizeof(double) - 1);
+ if (align) {
+ align = sizeof(double) - align;
+ }
+
+ packet->data->head.payload_size += sizeof(double) + align;
+ packet->data = check_and_expand_packet(packet->data, &payload_size);
+ if (!packet->data) {
+ packet->state = INVALID;
+ free(packet);
+ packet = NULL;
+ goto out;
+ }
+
+ *((double *)(payload + align)) = (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->fd = -1;
+ result->close_fd_cb = NULL;
+ result->close_fd_cbdata = NULL;
+ result->data = calloc(1, payload_size);
+ if (!result->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;
+ if (packet->data->head.command[0] == PACKET_CMD_INT_TAG) {
+ unsigned int *head_cmd = (unsigned int *)result->data->head.command;
+ unsigned int *packet_cmd = (unsigned int *)packet->data->head.command;
+
+ *head_cmd = *packet_cmd;
+ } else {
+ 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;
+
+ 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->fd = -1;
+ packet->close_fd_cb = NULL;
+ packet->close_fd_cbdata = NULL;
+ packet->data = calloc(1, payload_size);
+ if (!packet->data) {
+ ErrPrint("Heap: %s\n", strerror(errno));
+ packet->state = INVALID;
+ free(packet);
+ return NULL;
+ }
+
+ packet->state = VALID;
+ packet->data->head.source = 0lu;
+ packet->data->head.destination = 0lu;
+ packet->data->head.mask = 0xFFFFFFFF;
+ packet->data->head.seq = util_timestamp();
+ packet->data->head.type = PACKET_REQ;
+ packet->data->head.version = PACKET_VERSION;
+ if (cmd[0] == PACKET_CMD_INT_TAG) {
+ unsigned int *head_cmd = (unsigned int *)packet->data->head.command;
+ unsigned int *in_cmd = (unsigned int *)cmd;
+
+ *head_cmd = *in_cmd;
+ } else {
+ 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;
+
+ 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->fd = -1;
+ result->close_fd_cb = NULL;
+ result->close_fd_cbdata = NULL;
+ result->data = calloc(1, payload_size);
+ if (!result->data) {
+ ErrPrint("Heap: %s\n", strerror(errno));
+ result->state = INVALID;
+ free(result);
+ return NULL;
+ }
+
+ result->state = VALID;
+ result->data->head.source = 0lu;
+ result->data->head.destination = 0lu;
+ result->data->head.mask = 0xFFFFFFFF;
+ result->data->head.seq = util_timestamp();
+ result->data->head.type = PACKET_REQ_NOACK;
+ result->data->head.version = PACKET_VERSION;
+ if (cmd[0] == PACKET_CMD_INT_TAG) {
+ unsigned int *head_cmd = (unsigned int *)result->data->head.command;
+ unsigned int *cmd_in = (unsigned int *)cmd;
+ *head_cmd = *cmd_in;
+ } else {
+ 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;
+ int align;
+
+ 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':
+ align = (unsigned long)payload & (sizeof(int) - 1);
+ if (align) {
+ align = sizeof(int) - align;
+ }
+
+ int_ptr = (int *)va_arg(va, int *);
+ *int_ptr = *((int *)(payload + align));
+ offset += (sizeof(int) + align);
+ ret++;
+ break;
+ case 'd':
+ case 'D':
+ align = (unsigned long)payload & (sizeof(double) - 1);
+ if (align) {
+ align = sizeof(double) - align;
+ }
+ double_ptr = (double *)va_arg(va, double *);
+ *double_ptr = *((double *)(payload + align));
+ offset += (sizeof(double) + align);
+ 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) {
+ if (packet->close_fd_cb && packet->fd >= 0) {
+ packet->close_fd_cb(packet->fd, packet->close_fd_cbdata);
+ }
+
+ 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->fd = -1;
+ packet->close_fd_cb = NULL;
+ packet->close_fd_cbdata = NULL;
+ packet->data = calloc(1, 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 */
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2013 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 <netdb.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <systemd/sd-daemon.h>
+#include <attr/xattr.h>
+#include <dlog.h>
+
+#include "secure_socket.h"
+#include "debug.h"
+#include "util.h"
+
+#define BACKLOG 50 /*!< Accept only 50 connections as default */
+#define SNDBUF_SZ 262144 /*!< 256 KB, this will be doubled by kernel */
+#define RCVBUF_SZ 524288 /*!< 512 KB, this will be doubled by kernel */
+
+enum scheme {
+ SCHEME_LOCAL = 0x00,
+ SCHEME_SDLOCAL = 0x01,
+ SCHEME_REMOTE = 0x02,
+ SCHEME_UNKNOWN = 0x03,
+};
+
+struct function_table {
+ int type;
+ int (*create_socket)(const char *peer, int port, struct sockaddr *addr);
+ int (*setup_handle)(int handle);
+};
+
+int errno;
+
+static inline int create_unix_socket(const char *peer, int port, struct sockaddr *addr)
+{
+ int len;
+ int handle;
+ struct sockaddr_un *un_addr = (struct sockaddr_un *)addr;
+
+ len = sizeof(*un_addr);
+ bzero(un_addr, len);
+
+ if (strlen(peer) >= sizeof(un_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(un_addr->sun_path, peer);
+ un_addr->sun_family = AF_UNIX;
+
+ handle = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (handle < 0) {
+ handle = -errno;
+ ErrPrint("Failed to create a socket %s\n", strerror(errno));
+ }
+
+ return handle;
+}
+
+static inline int create_inet_socket(const char *peer, int port, struct sockaddr *addr)
+{
+ int handle;
+ struct sockaddr_in *in_addr = (struct sockaddr_in *)addr;
+
+ bzero(in_addr, sizeof(*in_addr));
+
+ in_addr->sin_port = htons(port);
+ in_addr->sin_family = AF_INET;
+ if (*peer == '\0') {
+ in_addr->sin_addr.s_addr = htonl(INADDR_ANY);
+ } else {
+ in_addr->sin_addr.s_addr = inet_addr(peer);
+ }
+
+ handle = socket(AF_INET, SOCK_STREAM, 0);
+ if (handle < 0) {
+ handle = -errno;
+ ErrPrint("socket: %s\n", strerror(errno));
+ }
+
+ return handle;
+}
+
+static inline int create_systemd_socket(const char *peer, int port, struct sockaddr *addr)
+{
+ int handle;
+ int cnt;
+
+ cnt = sd_listen_fds(0);
+ DbgPrint("Usable socket %s cnt[%d]\n", peer, cnt);
+
+ for (handle = SD_LISTEN_FDS_START; handle < SD_LISTEN_FDS_START + cnt; ++handle) {
+ if (sd_is_socket_unix(handle, SOCK_STREAM, 1, peer, 0) > 0) {
+ DbgPrint("Usable socket %s was passed by SystemD under descriptor %d\n", peer, handle);
+ return handle;
+ }
+ }
+
+ DbgPrint("Not found socket: %s\n", peer);
+ return -1;
+}
+
+static inline int setup_unix_handle(int handle)
+{
+ int on = 1;
+ int sndbuf = SNDBUF_SZ;
+ int rcvbuf = RCVBUF_SZ;
+
+ 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));
+ return ret;
+ }
+
+ (void)setsockopt(handle, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
+ (void)setsockopt(handle, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf));
+ (void)setsockopt(handle, IPPROTO_IP, TCP_NODELAY, &on, sizeof(on));
+
+ return 0;
+}
+
+static inline int setup_inet_handle(int handle)
+{
+ int on = 1;
+
+ (void)setsockopt(handle, IPPROTO_IP, TCP_NODELAY, &on, sizeof(on));
+
+ return 0;
+}
+
+static inline char *parse_scheme(const char *peer, int *port, struct function_table *vtable)
+{
+ int _port;
+ char *addr = NULL;
+
+ if (!port)
+ port = &_port;
+
+ *port = 0;
+
+ if (!strncasecmp(peer, COM_CORE_LOCAL_SCHEME, COM_CORE_LOCAL_SCHEME_LEN)) {
+ vtable->type = (int)SCHEME_LOCAL;
+ peer += COM_CORE_LOCAL_SCHEME_LEN;
+
+ addr = strdup(peer);
+ if (!addr) {
+ ErrPrint("Heap: %s\n", strerror(errno));
+ }
+
+ vtable->create_socket = create_unix_socket;
+ vtable->setup_handle = setup_unix_handle;
+ } else if (!strncasecmp(peer, COM_CORE_SD_LOCAL_SCHEME, COM_CORE_SD_LOCAL_SCHEME_LEN)) {
+ vtable->type = (int)SCHEME_SDLOCAL;
+ peer += COM_CORE_SD_LOCAL_SCHEME_LEN;
+
+ addr = strdup(peer);
+ if (!addr) {
+ ErrPrint("Heap: %s\n", strerror(errno));
+ }
+
+ vtable->create_socket = create_systemd_socket;
+ vtable->setup_handle = setup_unix_handle;
+ } else if (!strncasecmp(peer, COM_CORE_REMOTE_SCHEME, COM_CORE_REMOTE_SCHEME_LEN)) {
+ register int len;
+ char *endptr;
+
+ vtable->type = (int)SCHEME_REMOTE;
+ peer += COM_CORE_REMOTE_SCHEME_LEN;
+
+ for (len = 0; peer[len] && peer[len] != ':'; len++);
+ if (peer[len] != ':') {
+ ErrPrint("Invalid syntax: %s\n", peer);
+ goto out;
+ }
+
+ addr = malloc(len + 1);
+ if (!addr) {
+ ErrPrint("Heap: %s\n", strerror(errno));
+ goto out;
+ }
+
+ if (len > 0) {
+ strncpy(addr, peer, len);
+ }
+
+ addr[len] = '\0';
+
+ peer += len + 1;
+ *port = strtoul(peer, &endptr, 10);
+ if (*endptr != '\0' || peer == endptr) {
+ ErrPrint("Invalid: %s[%d]\n", peer - len - 1, len + 1);
+ free(addr);
+ addr = NULL;
+ goto out;
+ }
+
+ vtable->create_socket = create_inet_socket;
+ vtable->setup_handle = setup_inet_handle;
+ } else {
+ /* Fallback to local scheme */
+ vtable->type = (int)SCHEME_LOCAL;
+ addr = strdup(peer);
+ if (!addr) {
+ ErrPrint("Heap: %s\n", strerror(errno));
+ goto out;
+ }
+
+ vtable->create_socket = create_unix_socket;
+ vtable->setup_handle = setup_unix_handle;
+ }
+
+out:
+ return addr;
+}
+
+EAPI int secure_socket_create_client(const char *peer)
+{
+ int port;
+ char *addr;
+ int ret;
+ struct function_table vtable;
+ struct sockaddr *sockaddr;
+ struct sockaddr_in in_addr;
+ struct sockaddr_un un_addr;
+ int handle;
+ int addrlen;
+
+ addr = parse_scheme(peer, &port, &vtable);
+ if (!addr) {
+ ErrPrint("peer: [%s] is not valid\n", peer);
+ return -EINVAL;
+ }
+
+ switch (vtable.type) {
+ case SCHEME_LOCAL:
+ case SCHEME_SDLOCAL:
+ sockaddr = (struct sockaddr *)&un_addr;
+ addrlen = sizeof(un_addr);
+ break;
+ case SCHEME_REMOTE:
+ sockaddr = (struct sockaddr *)&in_addr;
+ addrlen = sizeof(in_addr);
+ break;
+ default:
+ free(addr);
+ return -EINVAL;
+ }
+
+ handle = vtable.create_socket(addr, port, sockaddr);
+ free(addr);
+ if (handle < 0) {
+ return handle;
+ }
+
+ /**
+ * @note
+ * In case of a FD which is activated by systemd,
+ * Does not need to do connecting.
+ * It will be already connected.
+ */
+ if (vtable.type != SCHEME_SDLOCAL) {
+ ret = connect(handle, sockaddr, addrlen);
+ if (ret < 0) {
+ ret = -errno;
+ ErrPrint("Failed to connect to server [%s] %s\n", peer, strerror(errno));
+ if (close(handle) < 0) {
+ ErrPrint("close: %s\n", strerror(errno));
+ }
+
+ return ret;
+ }
+ }
+
+ ret = vtable.setup_handle(handle);
+ if (ret < 0) {
+ if (close(handle) < 0) {
+ ErrPrint("close: %s\n", strerror(errno));
+ }
+
+ return ret;
+ }
+
+ return handle;
+}
+
+EAPI int secure_socket_create_server(const char *peer)
+{
+ return secure_socket_create_server_with_permission(peer, NULL);
+}
+
+EAPI int secure_socket_create_server_with_permission(const char *peer, const char *label)
+{
+ int port;
+ char *addr;
+ int handle;
+ int ret;
+ struct sockaddr *sockaddr;
+ struct sockaddr_in in_addr;
+ struct sockaddr_un un_addr;
+ struct function_table vtable;
+ int addrlen;
+
+ addr = parse_scheme(peer, &port, &vtable);
+ if (!addr) {
+ ErrPrint("Failed to parse scheme\n");
+ return -EINVAL;
+ }
+
+ switch (vtable.type) {
+ case SCHEME_LOCAL:
+ case SCHEME_SDLOCAL:
+ sockaddr = (struct sockaddr *)&un_addr;
+ addrlen = sizeof(un_addr);
+ break;
+ case SCHEME_REMOTE:
+ sockaddr = (struct sockaddr *)&in_addr;
+ addrlen = sizeof(in_addr);
+ break;
+ default:
+ free(addr);
+ return -EINVAL;
+ }
+
+ handle = vtable.create_socket(addr, port, sockaddr);
+ free(addr);
+ if (handle < 0) {
+ return handle;
+ }
+
+ if (label) {
+ /**
+ * @note
+ * Maybe this code will not work for FD from systemd.
+ * These functions should be done before "bind" and the systemd will do "bind" already. (maybe)
+ * Then these functions are not working on it.
+ * But I just leave it on here. ;)
+ *
+ * In case of the Tizen, they have security server which maintains all security related functionalities.
+ * So this API(fsetxattr) will not work in the Tizen.
+ * But if you are using this package for the other platform, like general linux system,
+ * It will work for you. But It just my expectation not tested.. ;)
+ */
+ if (fsetxattr(handle, "security.SMACK64IPIN", label, strlen(label), 0) < 0) {
+ ErrPrint("Failed to set SMACK label[%s] [%s]\n", label, strerror(errno));
+ }
+
+ if (fsetxattr(handle, "security.SMACK64IPOUT", label, strlen(label), 0) < 0) {
+ ErrPrint("Failed to set SMACK label[%s] [%s]\n", label, strerror(errno));
+ }
+ }
+
+ /**
+ * @note
+ * If the handle is created by systemd,
+ * We should not touch it anymore after gettting FD from sd library.
+ */
+ if (vtable.type == SCHEME_SDLOCAL) {
+ return handle;
+ }
+
+ ret = bind(handle, sockaddr, addrlen);
+ if (ret < 0) {
+ ret = -errno;
+ ErrPrint("bind: %s\n", strerror(errno));
+ if (close(handle) < 0) {
+ ErrPrint("close: %s\n", strerror(errno));
+ }
+ return ret;
+ }
+
+ ret = listen(handle, BACKLOG);
+ if (ret < 0) {
+ ret = -errno;
+ ErrPrint("listen: %s\n", strerror(errno));
+ if (close(handle) < 0) {
+ ErrPrint("close: %s\n", strerror(errno));
+ }
+ return ret;
+ }
+
+ if (vtable.type == SCHEME_LOCAL) {
+ 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_in in_addr;
+ struct sockaddr_un un_addr;
+ struct sockaddr *addr;
+ int handle;
+ int ret;
+ socklen_t size = sizeof(un_addr);
+
+ /* Finding the largest buffer */
+ if (sizeof(in_addr) > sizeof(un_addr)) {
+ addr = (struct sockaddr *)&in_addr;
+ size = sizeof(in_addr);
+ } else {
+ addr = (struct sockaddr *)&un_addr;
+ size = sizeof(un_addr);
+ }
+
+ handle = accept(server_handle, addr, &size);
+ if (handle < 0) {
+ ret = -errno;
+ ErrPrint("Failed to accept a new client %s\n", strerror(errno));
+ return ret;
+ }
+
+ if (addr->sa_family == AF_UNIX) {
+ ret = setup_unix_handle(handle);
+ if (ret < 0) {
+ if (close(handle) < 0) {
+ ErrPrint("close: %s\n", strerror(errno));
+ }
+
+ handle = ret;
+ }
+ } else if (addr->sa_family == AF_INET) {
+ ret = setup_inet_handle(handle);
+ if (ret < 0) {
+ if (close(handle) < 0) {
+ ErrPrint("close: %s\n", strerror(errno));
+ }
+
+ handle = ret;
+ }
+ } else {
+ ErrPrint("Unknown address family: %d\n", addr->sa_family);
+ }
+
+ return handle;
+}
+
+EAPI int secure_socket_send_with_fd(int handle, const char *buffer, int size, int fd)
+{
+ struct msghdr msg;
+ struct iovec iov;
+ union {
+ struct cmsghdr hdr;
+ char control[CMSG_SPACE(sizeof(int))];
+ } cmsgu;
+ 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_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ if (fd >= 0) {
+ struct cmsghdr *cmsg;
+ int *cdata;
+
+ msg.msg_control = cmsgu.control;
+ msg.msg_controllen = sizeof(cmsgu.control);
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cdata = (int *)CMSG_DATA(cmsg);
+ *cdata = fd;
+ }
+
+ 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], handle(%d)\n", strerror(errno), handle);
+ return ret;
+ }
+
+ return iov.iov_len;
+}
+
+EAPI int secure_socket_send(int handle, const char *buffer, int size)
+{
+ return secure_socket_send_with_fd(handle, buffer, size, -1);
+}
+
+EAPI int secure_socket_recv_with_fd(int handle, char *buffer, int size, int *sender_pid, int *fd)
+{
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ struct iovec iov;
+ char control[128];
+ int _pid;
+ int _fd;
+ int ret;
+
+ if (size <= 0 || !buffer) {
+ return -EINVAL;
+ }
+
+ if (!sender_pid) {
+ sender_pid = &_pid;
+ }
+
+ if (!fd) {
+ fd = &_fd;
+ }
+
+ 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) {
+ 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;
+ }
+
+ if ((msg.msg_flags & MSG_CTRUNC) == MSG_CTRUNC) {
+ ErrPrint("Controll buffer is too short (%d): %d, %d\n", msg.msg_controllen, size, sizeof(control));
+ } else if (msg.msg_flags) {
+ DbgPrint("Flags: %X\n", msg.msg_flags);
+ }
+
+ *sender_pid = -1; /* In case of remote socket, cannot delivery this */
+ *fd = -1;
+ 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;
+ } else if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
+ int *cdata;
+ int count;
+
+ count = cmsg->cmsg_len / CMSG_LEN(sizeof(int));
+ cdata = (int *)CMSG_DATA(cmsg);
+ if (count > 1 || &_fd == fd) {
+ int i;
+
+ ErrPrint("Unawared controll data. discards all\n");
+ for (i = 0; i < count; i++) {
+ if (close(cdata[i]) < 0) {
+ ErrPrint("close: %d\n", errno);
+ }
+ }
+ } else {
+ *fd = *cdata;
+ }
+ } else {
+ DbgPrint("Unknown message type\n");
+ }
+
+ cmsg = CMSG_NXTHDR(&msg, cmsg);
+ }
+
+ if (ret == 0) {
+ /*!< Disconnected */
+ DbgPrint("Disconnected\n");
+ }
+
+ return ret == 0 ? 0 : iov.iov_len;
+}
+
+EAPI int secure_socket_recv(int handle, char *buffer, int size, int *sender_pid)
+{
+ return secure_socket_recv_with_fd(handle, buffer, size, sender_pid, NULL);
+}
+
+EAPI int secure_socket_destroy_handle(int handle)
+{
+ if (close(handle) < 0) {
+ int ret;
+ ret = -errno;
+ ErrPrint("close: %s\n", strerror(errno));
+ return ret;
+ }
+
+ return 0;
+}
+
+#undef _GNU_SOURCE
+
+/* End of a file */
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2013 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 <stdlib.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <time.h>
+#include <dlog.h>
+
+#include "debug.h"
+#include "util.h"
+
+int errno;
+#if defined(_USE_ECORE_TIME_GET)
+static struct {
+ clockid_t type;
+} s_info = {
+ .type = CLOCK_MONOTONIC,
+};
+#endif
+
+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] == '/');
+}
+
+double util_timestamp(void)
+{
+#if defined(_USE_ECORE_TIME_GET)
+ struct timespec ts;
+
+ do {
+ if (clock_gettime(s_info.type, &ts) == 0) {
+ return ts.tv_sec + ts.tv_nsec / 1000000000.0f;
+ }
+
+ ErrPrint("%d: %s\n", s_info.type, strerror(errno));
+ if (s_info.type == CLOCK_MONOTONIC) {
+ s_info.type = CLOCK_REALTIME;
+ } else if (s_info.type == CLOCK_REALTIME) {
+ struct timeval tv;
+ if (gettimeofday(&tv, NULL) < 0) {
+ ErrPrint("gettimeofday: %s\n", strerror(errno));
+ break;
+ }
+
+ return tv.tv_sec + tv.tv_usec / 1000000.0f;
+ }
+ } while (1);
+
+ return 0.0f;
+#else
+ struct timeval tv;
+
+ if (gettimeofday(&tv, NULL) < 0) {
+ ErrPrint("gettimeofday: %s\n", strerror(errno));
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ }
+
+ return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0f;
+#endif
+}
+
+/* End of a file */