From a2f0f7452226a4411f402aedcbe6e00f400a7827 Mon Sep 17 00:00:00 2001 From: overweight <5324761+overweight@user.noreply.gitee.com> Date: Mon, 30 Sep 2019 10:35:21 -0400 Subject: [PATCH] Package init --- .clang-format | 123 ++ .gitignore | 48 + CMakeLists.txt | 62 + License/LICENSE | 121 ++ README.md | 24 + clibcni.pc.in | 12 + clibcni.spec | 91 ++ cmake/checker.cmake | 44 + src/CMakeLists.txt | 50 + src/api.c | 1021 ++++++++++++++ src/api.h | 100 ++ src/conf.c | 642 +++++++++ src/conf.h | 61 + src/invoke/CMakeLists.txt | 3 + src/invoke/args.c | 202 +++ src/invoke/args.h | 44 + src/invoke/exec.c | 610 ++++++++ src/invoke/exec.h | 39 + src/invoke/invoke_errno.h | 35 + src/invoke/tools.c | 110 ++ src/invoke/tools.h | 29 + src/json/CMakeLists.txt | 2 + src/json/schema/CMakeLists.txt | 15 + src/json/schema/schema/cni_version.json | 12 + src/json/schema/schema/defs.json | 243 ++++ src/json/schema/schema/exec_error.json | 19 + src/json/schema/schema/inner_plugin_info.json | 15 + src/json/schema/schema/net_args.json | 85 ++ src/json/schema/schema/net_conf.json | 88 ++ src/json/schema/schema/net_conf_list.json | 18 + src/json/schema/schema/network/dns.json | 18 + src/json/schema/schema/network/interface.json | 18 + src/json/schema/schema/network/ipconfig.json | 22 + .../schema/schema/network/ipconfig_020.json | 21 + src/json/schema/schema/network/route.json | 15 + src/json/schema/schema/port_mapping.json | 18 + src/json/schema/schema/result_curr.json | 30 + src/json/schema/src/CMakeLists.txt | 3 + src/json/schema/src/common_c.py | 1223 +++++++++++++++++ src/json/schema/src/common_h.py | 198 +++ src/json/schema/src/generate.py | 802 +++++++++++ src/json/schema/src/headers.py | 197 +++ src/json/schema/src/helpers.py | 313 +++++ src/json/schema/src/read_file.c | 151 ++ src/json/schema/src/read_file.h | 25 + src/json/schema/src/sources.py | 1013 ++++++++++++++ src/log.c | 393 ++++++ src/log.h | 138 ++ src/types/CMakeLists.txt | 3 + src/types/current.c | 809 +++++++++++ src/types/current.h | 27 + src/types/types.c | 831 +++++++++++ src/types/types.h | 112 ++ src/utils.c | 647 +++++++++ src/utils.h | 62 + src/version/CMakeLists.txt | 3 + src/version/version.c | 235 ++++ src/version/version.h | 58 + tools/static_check | 547 ++++++++ update-version.bash | 56 + 60 files changed, 11956 insertions(+) create mode 100644 .clang-format create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 License/LICENSE create mode 100644 README.md create mode 100644 clibcni.pc.in create mode 100644 clibcni.spec create mode 100644 cmake/checker.cmake create mode 100644 src/CMakeLists.txt create mode 100644 src/api.c create mode 100644 src/api.h create mode 100644 src/conf.c create mode 100644 src/conf.h create mode 100644 src/invoke/CMakeLists.txt create mode 100644 src/invoke/args.c create mode 100644 src/invoke/args.h create mode 100644 src/invoke/exec.c create mode 100644 src/invoke/exec.h create mode 100644 src/invoke/invoke_errno.h create mode 100644 src/invoke/tools.c create mode 100644 src/invoke/tools.h create mode 100644 src/json/CMakeLists.txt create mode 100644 src/json/schema/CMakeLists.txt create mode 100644 src/json/schema/schema/cni_version.json create mode 100644 src/json/schema/schema/defs.json create mode 100644 src/json/schema/schema/exec_error.json create mode 100644 src/json/schema/schema/inner_plugin_info.json create mode 100644 src/json/schema/schema/net_args.json create mode 100644 src/json/schema/schema/net_conf.json create mode 100644 src/json/schema/schema/net_conf_list.json create mode 100644 src/json/schema/schema/network/dns.json create mode 100644 src/json/schema/schema/network/interface.json create mode 100644 src/json/schema/schema/network/ipconfig.json create mode 100644 src/json/schema/schema/network/ipconfig_020.json create mode 100644 src/json/schema/schema/network/route.json create mode 100644 src/json/schema/schema/port_mapping.json create mode 100644 src/json/schema/schema/result_curr.json create mode 100644 src/json/schema/src/CMakeLists.txt create mode 100644 src/json/schema/src/common_c.py create mode 100644 src/json/schema/src/common_h.py create mode 100644 src/json/schema/src/generate.py create mode 100644 src/json/schema/src/headers.py create mode 100644 src/json/schema/src/helpers.py create mode 100644 src/json/schema/src/read_file.c create mode 100644 src/json/schema/src/read_file.h create mode 100644 src/json/schema/src/sources.py create mode 100644 src/log.c create mode 100644 src/log.h create mode 100644 src/types/CMakeLists.txt create mode 100644 src/types/current.c create mode 100644 src/types/current.h create mode 100644 src/types/types.c create mode 100644 src/types/types.h create mode 100644 src/utils.c create mode 100644 src/utils.h create mode 100644 src/version/CMakeLists.txt create mode 100644 src/version/version.c create mode 100644 src/version/version.h create mode 100755 tools/static_check create mode 100755 update-version.bash diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..5d9d455 --- /dev/null +++ b/.clang-format @@ -0,0 +1,123 @@ +--- +BasedOnStyle: llvm +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: false +# AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +# AllowAllConstructorInitializersOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +# AllowShortLambdasOnASingleLine: Empty +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + # AfterCaseLabel: false + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: false +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeComma +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: false +ColumnLimit: 120 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 8 +ContinuationIndentWidth: 8 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true + +# Taken from: +# git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' include/ \ +# | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$, - '\1'," \ +# | sort | uniq +ForEachMacros: + - 'linked_list_for_each_safe' + - 'linked_list_for_each' + +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '.*' + Priority: 1 +IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 4 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: Inner +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 4 +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true + +# Taken from git's rules +PenaltyBreakAssignment: 10 +PenaltyBreakBeforeFirstCallParameter: 30 +PenaltyBreakComment: 10 +PenaltyBreakFirstLessLess: 0 +PenaltyBreakString: 10 +PenaltyExcessCharacter: 100 +PenaltyReturnTypeOnItsOwnLine: 60 + +PointerAlignment: Right +ReflowComments: false +SortIncludes: false +SortUsingDeclarations: false +SpaceAfterCStyleCast: false +# SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 4 +UseTab: Never +... diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5317068 --- /dev/null +++ b/.gitignore @@ -0,0 +1,48 @@ +# Object files +*.o + +# Libraries +*.lib +*.a +*.la +*.so +*.lo +**/.libs + +# Shared objects +*.so +*.so.* +*.dylib + +*~ +*.orig + +tags +aclocal.m4 +ar-lib +autom4te.cache +build +compile +config.guess +config.h +config.h.in +config.sub +config.log +config.status +depcomp +install-sh +libtool +ltmain.sh +missing +**/Makefile.in +**/Makefile + +configure +lcrd.spec +lcrd.pc +*.tar.gz +m4/* + +**/.deps +**/.dirstamp +stamp-h1 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..7db422c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,62 @@ +cmake_minimum_required (VERSION 3.12.1) +project (clibcni) + +option(VERSION "set clibcni version" ON) +if (VERSION STREQUAL "ON") + set(CLIBCNI_VERSION "1.0.6") +endif() + +option(DEBUG "set clibcni gcc option" ON) +if (DEBUG STREQUAL "ON") + add_definitions("-g -o2") +endif() + +option(GCOV "set clibcni gcov option" OFF) +if (GCOV STREQUAL "ON") + set(CLIBCNI_GCOV "ON") +endif() + +# check depends libs and headers +include(cmake/checker.cmake) +if (CHECKER_RESULT) + return() +endif() + +set(CMAKE_C_COMPILER "gcc" CACHE PATH "c compiler") +set(CMAKE_C_FLAGS "-fPIC -fstack-protector-all -D_FORTIFY_SOURCE=2 -O2 -Wall -Werror") +set(CMAKE_SHARED_LINKER_FLAGS "-Wl,-E -Wl,-z,relro -Wl,-z,now -Wl,-z,noexecstack -Wtrampolines -fPIE -pie -shared -pthread") + +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/config.h.in" + "${CMAKE_BINARY_DIR}/conf/config.h" +) + +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/clibcni.pc.in" + "${CMAKE_BINARY_DIR}/conf/clibcni.pc" +) + +# build which type of clibcni library +option(USESHARED "set type of libclibcni, default is shared" ON) +if (USESHARED STREQUAL "ON") + set(LIBTYPE "SHARED") + message("-- Build shared library") +else () + set(LIBTYPE "STATIC") + message("-- Build static library") +endif() + +if (LIB_INSTALL_DIR) + set(LIB_INSTALL_DIR_DEFAULT ${LIB_INSTALL_DIR}) +else() + set(LIB_INSTALL_DIR_DEFAULT "lib") +endif() + +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src) + +# install all files +install(FILES ${CMAKE_BINARY_DIR}/conf/clibcni.pc + DESTINATION ${LIB_INSTALL_DIR_DEFAULT}/pkgconfig PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ GROUP_WRITE) +install(FILES src/types/types.h DESTINATION include/clibcni) +install(FILES src/version/version.h DESTINATION include/clibcni) +install(FILES src/api.h DESTINATION include/clibcni) diff --git a/License/LICENSE b/License/LICENSE new file mode 100644 index 0000000..cefc2d2 --- /dev/null +++ b/License/LICENSE @@ -0,0 +1,121 @@ + 木兰宽松许可证, 第1版 + + 木兰宽松许可证, 第1版 + 2019年8月 http://license.coscl.org.cn/MulanPSL + + 您对“软件”的复制、使用、修改及分发受木兰宽松许可证,第1版(“本许可证”)的如下条款的约束: + + 0. 定义 + + “软件”是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。 + + “贡献者”是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。 + + “法人实体”是指提交贡献的机构及其“关联实体”。 + + “关联实体”是指,对“本许可证”下的一方而言,控制、受控制或与其共同受控制的机构,此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。 + + “贡献”是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。 + + 1. 授予版权许可 + + 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其“贡献”,不论修改与否。 + + 2. 授予专利许可 + + 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软件”结合而将必然会侵犯的专利权利要求,不包括仅因您或他人修改“贡献”或其他结合而将必然会侵犯到的专利权利要求。如您或您的“关联实体”直接或间接地(包括通过代理、专利被许可人或受让人),就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权行动之日终止。 + + 3. 无商标许可 + + “本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可,但您为满足第4条规定的声明义务而必须使用除外。 + + 4. 分发限制 + + 您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。 + + 5. 免责声明与责任限制 + + “软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。 + + 条款结束。 + + 如何将木兰宽松许可证,第1版,应用到您的软件 + + 如果您希望将木兰宽松许可证,第1版,应用到您的新软件,为了方便接收者查阅,建议您完成如下三步: + + 1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字; + + 2, 请您在软件包的一级目录下创建以“LICENSE”为名的文件,将整个许可证文本放入该文件中; + + 3, 请将如下声明文本放入每个源文件的头部注释中。 + + Copyright (c) [2019] [name of copyright holder] + [Software Name] is licensed under the Mulan PSL v1. + You can use this software according to the terms and conditions of the Mulan PSL v1. + You may obtain a copy of Mulan PSL v1 at: + http://license.coscl.org.cn/MulanPSL + THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR + PURPOSE. + See the Mulan PSL v1 for more details. + + + Mulan Permissive Software License,Version 1 + + Mulan Permissive Software License,Version 1 (Mulan PSL v1) + August 2019 http://license.coscl.org.cn/MulanPSL + + Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v1 (this License) with following terms and conditions: + + 0. Definition + + Software means the program and related documents which are comprised of those Contribution and licensed under this License. + + Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License. + + Legal Entity means the entity making a Contribution and all its Affiliates. + + Affiliates means entities that control, or are controlled by, or are under common control with a party to this License, ‘control’ means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity. + + Contribution means the copyrightable work licensed by a particular Contributor under this License. + + 1. Grant of Copyright License + + Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not. + + 2. Grant of Patent License + + Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed, excluding of any patent claims solely be infringed by your or others’ modification or other combinations. If you or your Affiliates directly or indirectly (including through an agent, patent licensee or assignee), institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken. + + 3. No Trademark License + + No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in section 4. + + 4. Distribution Restriction + + You may distribute the Software in any medium with or without modification, whether in source or executable forms, provided that you provide recipients with a copy of this License and retain copyright, patent, trademark and disclaimer statements in the Software. + + 5. Disclaimer of Warranty and Limitation of Liability + + The Software and Contribution in it are provided without warranties of any kind, either express or implied. In no event shall any Contributor or copyright holder be liable to you for any damages, including, but not limited to any direct, or indirect, special or consequential damages arising from your use or inability to use the Software or the Contribution in it, no matter how it’s caused or based on which legal theory, even if advised of the possibility of such damages. + + End of the Terms and Conditions + + How to apply the Mulan Permissive Software License,Version 1 (Mulan PSL v1) to your software + + To apply the Mulan PSL v1 to your work, for easy identification by recipients, you are suggested to complete following three steps: + + i. Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner; + ii. Create a file named “LICENSE” which contains the whole context of this License in the first directory of your software package; + iii. Attach the statement to the appropriate annotated syntax at the beginning of each source file. + + Copyright (c) [2019] [name of copyright holder] + [Software Name] is licensed under the Mulan PSL v1. + You can use this software according to the terms and conditions of the Mulan PSL v1. + You may obtain a copy of Mulan PSL v1 at: + http://license.coscl.org.cn/MulanPSL + THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR + PURPOSE. + + See the Mulan PSL v1 for more details. diff --git a/README.md b/README.md new file mode 100644 index 0000000..f39728c --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# clibcni + +CNI (Container Network Interface), a Cloud Native Computing Foundation project. +clibcni is a library used by iSulad to configure network interfaces in containers, following +the specification of CNI (Container Network Interface), a Cloud Native Computing Foundation project. + +## How to Contribute + +We always welcome new contributors. And we are happy to provide guidance for the new contributors. +iSulad follows the kernel coding conventions. You can find a detailed introduction at: + +- https://www.kernel.org/doc/html/v4.10/process/coding-style.html + +## Building + +Without considering distribution specific details a simple + + mkdir -p build && cd ./build && cmake .. && make && sudo make install + +is usually sufficient. + +## Licensing + +clibcni is licensed under the Mulan PSL v1. diff --git a/clibcni.pc.in b/clibcni.pc.in new file mode 100644 index 0000000..3632fa6 --- /dev/null +++ b/clibcni.pc.in @@ -0,0 +1,12 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +libdir=@CMAKE_INSTALL_PREFIX@/lib +localstatedir=@CMAKE_INSTALL_PREFIX@/var +includedir=@CMAKE_INSTALL_PREFIX@/include + +Name: libclibcni +Description: c library of cni +Version: @CLIBCNI_VERSION@ +URL: http://code-sh.rnd.huawei.com/containers/plugins/clibcni.git +Libs: -L@CMAKE_INSTALL_PREFIX@/lib -lclibcni +Cflags: -I@CMAKE_INSTALL_PREFIX@/include + diff --git a/clibcni.spec b/clibcni.spec new file mode 100644 index 0000000..d03ec09 --- /dev/null +++ b/clibcni.spec @@ -0,0 +1,91 @@ +%global _version 1.0.6 +%global _release 20190919.235256.gitc4730268 +Name: clibcni +Version: %{_version} +Release: %{_release}%{?dist} +Summary: CNI - the Container Network Interface +Group: System Environment/Libraries +License: Mulan PSL v1 +URL: http://code.huawei.com/containers/plugins/clibcni +Source0: %{name}-1.0.tar.gz +BuildRoot: %{_tmppath}/%{name}-%{version} + +BuildRequires: gcc +BuildRequires: cmake +BuildRequires: libsecurec libsecurec-devel +BuildRequires: yajl yajl-devel + +Requires: libsecurec +Requires: yajl + +%ifarch x86_64 aarch64 +Provides: lib%{name}.so()(64bit) +%endif + +%description +CNI (Container Network Interface), a Cloud Native Computing Foundation project, +consists of a specification and libraries for writing plugins to configure +network interfaces in Linux containers, along with a number of supported +plugins. CNI concerns itself only with network connectivity of containers and +removing allocated resources when the container is deleted. Because of this +focus, CNI has a wide range of support and the specification is simple to implement. + +%package devel +Summary: Huawei CNI C Library +Group: Libraries +ExclusiveArch: x86_64 aarch64 +Requires: %{name} = %{version}-%{release} + +%description devel +the %{name}-libs package contains libraries for running %{name} applications. + +%global debug_package %{nil} + +%prep +%setup -c -n %{name}-%{version} + +%build +mkdir -p build +cd build +%cmake -DDEBUG=OFF -DLIB_INSTALL_DIR=%{_libdir} ../ +%make_build + +%install +rm -rf %{buildroot} +cd build +install -d $RPM_BUILD_ROOT/%{_libdir} +install -m 0644 ./src/libclibcni.so %{buildroot}/%{_libdir}/libclibcni.so + +install -d $RPM_BUILD_ROOT/%{_libdir}/pkgconfig +install -m 0644 ./conf/clibcni.pc %{buildroot}/%{_libdir}/pkgconfig/clibcni.pc + +install -d $RPM_BUILD_ROOT/%{_includedir}/clibcni +install -m 0644 ../src/api.h %{buildroot}/%{_includedir}/clibcni/api.h +install -m 0644 ../src/types/types.h %{buildroot}/%{_includedir}/clibcni/types.h +install -m 0644 ../src/version/version.h %{buildroot}/%{_includedir}/clibcni/version.h + + +find %{buildroot} -type f -name '*.la' -exec rm -f {} ';' +find %{buildroot} -name '*.a' -exec rm -f {} ';' +find %{buildroot} -name '*.cmake' -exec rm -f {} ';' + +%clean +rm -rf %{buildroot} + +%pre + +%post -p /sbin/ldconfig + +%postun -p /sbin/ldconfig + +%files +%defattr(-,root,root,-) +%{_libdir}/lib%{name}.so* + +%files devel +%defattr(-,root,root,-) +%{_includedir}/%{name}/*.h +%{_libdir}/pkgconfig/%{name}.pc + + +%changelog diff --git a/cmake/checker.cmake b/cmake/checker.cmake new file mode 100644 index 0000000..62a2eac --- /dev/null +++ b/cmake/checker.cmake @@ -0,0 +1,44 @@ +# check depends library and headers +find_package(PkgConfig) + +macro(_CHECK) +if (${ARGV0} STREQUAL "${ARGV1}") + message("error: can not find " ${ARGV2} " program") + set(CHECKER_RESULT 1) +else() + message("-- found " ${ARGV2} " --- works") +endif() +endmacro() + +#check python +find_program(CMD_PYTHON python) +_CHECK(CMD_PYTHON "CMD_PYTHON-NOTFOUND" "python") + +# check securec +find_path(LIBSECUREC_INCLUDE_DIR securec.h) +_CHECK(LIBSECUREC_INCLUDE_DIR "LIBSECUREC_INCLUDE_DIR-NOTFOUND" "securec.h") + +find_library(LIBSECUREC_LIBRARY securec) +_CHECK(LIBSECUREC_LIBRARY "LIBSECUREC_LIBRARY-NOTFOUND" "libsecurec.so") + +# check libyajl +pkg_check_modules(PC_LIBYAJL REQUIRED "yajl>=2") +if (NOT PC_LIBYAJL_FOUND) + message("error: can not find yajl>=2") + set(CHECKER_RESULT 1) +endif() +find_path(LIBYAJL_INCLUDE_DIR yajl/yajl_tree.h + HINTS ${PC_LIBYAJL_INCLUDEDIR} ${PC_LIBYAJL_INCLUDE_DIRS}) +_CHECK(LIBYAJL_INCLUDE_DIR "LIBYAJL_INCLUDE_DIR-NOTFOUND" "yajl/yajl_tree.h") + +find_library(LIBYAJL_LIBRARY yajl + HINTS ${PC_LIBYAJL_LIBDIR} ${PC_LIBYAJL_LIBRARY_DIRS}) +_CHECK(LIBYAJL_LIBRARY "LIBYAJL_LIBRARY-NOTFOUND" "libyajl.so") + +if (ENABLE_TESTS STREQUAL "ON") + pkg_check_modules(PC_CHECK REQUIRED "check>=0.9.12") + if (NOT PC_CHECK_FOUND) + message("error: can not find check>=0.9.12") + set(CHECKER_RESULT 1) + endif() +endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..e3e3ee1 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,50 @@ +# generate .c and .h files for json +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/json) + +# get libclibcni source files +aux_source_directory(${CMAKE_BINARY_DIR}/json generatesrcs) +message("-- Get generate srcs: " ${generatesrcs}) +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/json/schema/src commonjsonsrcs) +message("-- Get common json srcs: " ${commonjsonsrcs}) + +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} topsrcs) +message("-- Get top srcs: " ${topsrcs}) + +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/version) +message("-- Get version srcs: " ${VERSION_SRCS}) + +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/types) +message("-- Get types srcs: " ${TYPE_SRCS}) + +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/invoke) +message("-- Get invoke srcs: " ${INVOKE_SRCS}) + +# set libclibcni library +add_library(clibcni ${LIBTYPE} ${topsrcs} ${VERSION_SRCS} ${TYPE_SRCS} ${INVOKE_SRCS} ${commonjsonsrcs} ${generatesrcs}) + +# set include dirs +target_include_directories(clibcni + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/version/ + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/types/ + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/invoke/ + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/json + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/json/schema/src + PUBLIC ${CMAKE_BINARY_DIR}/json + PUBLIC ${CMAKE_BINARY_DIR}/conf + ) + +# set liblcr compile flags +if (CLIBCNI_GCOV) + set(CMAKE_C_FLAGS_DEBUG "-Wall -fprofile-arcs -ftest-coverage") + message("------compile with gcov-------------") + message("-----CFLAGS: " ${CMAKE_C_FLAGS_DEBUG}) + message("------------------------------------") + target_link_libraries(clibcni -lgcov) +endif() + +target_link_libraries(clibcni -lyajl -lsecurec) + +# install all files +install(TARGETS clibcni + LIBRARY DESTINATION ${LIB_INSTALL_DIR_DEFAULT} PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE) diff --git a/src/api.c b/src/api.c new file mode 100644 index 0000000..58149a6 --- /dev/null +++ b/src/api.c @@ -0,0 +1,1021 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. + * clibcni licensed under the Mulan PSL v1. + * You can use this software according to the terms and conditions of the Mulan PSL v1. + * You may obtain a copy of Mulan PSL v1 at: + * http://license.coscl.org.cn/MulanPSL + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR + * PURPOSE. + * See the Mulan PSL v1 for more details. + * Author: tanyifeng + * Create: 2019-04-25 + * Description: provide cni api functions + ********************************************************************************/ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include +#include + +#include "api.h" + +#include "log.h" +#include "invoke_errno.h" +#include "current.h" +#include "conf.h" +#include "args.h" +#include "tools.h" +#include "exec.h" +#include "utils.h" +#include "types.h" + +static int add_network_list(const struct network_config_list *list, const struct runtime_conf *rc, + const char * const *paths, size_t paths_len, struct result **pret, char **err); + +static int del_network_list(const struct network_config_list *list, const struct runtime_conf *rc, + const char * const *paths, size_t paths_len, char **err); + +static int add_network(const struct network_config *net, const struct runtime_conf *rc, const char * const *paths, + size_t paths_len, struct result **add_result, char **err); + +static int del_network(const struct network_config *net, const struct runtime_conf *rc, const char * const *paths, + size_t paths_len, char **err); + +static int args(const char *action, const struct runtime_conf *rc, const char * const *paths, size_t paths_len, + struct cni_args **cargs, char **err); + +static int copy_cni_port_mapping(port_mapping *dst, const struct cni_port_mapping *src) +{ + bool invalid_arg = (dst == NULL || src == NULL); + if (invalid_arg) { + return -1; + } + if (src->protocol != NULL) { + dst->protocol = util_strdup_s(src->protocol); + } + if (src->host_ip != NULL) { + dst->host_ip = util_strdup_s(src->host_ip); + } + dst->container_port = src->container_port; + dst->host_port = src->host_port; + + return 0; +} + +static int inject_port_mappings(const struct runtime_conf *rt, net_conf_runtime_config *rt_config, char **err) +{ + size_t j = 0; + + if (rt_config->port_mappings != NULL) { + for (j = 0; j < rt_config->port_mappings_len; j++) { + free_port_mapping(rt_config->port_mappings[j]); + rt_config->port_mappings[j] = NULL; + } + free(rt_config->port_mappings); + rt_config->port_mappings = NULL; + } + + if (rt->p_mapping_len > (SIZE_MAX / sizeof(port_mapping *))) { + *err = util_strdup_s("Too many mapping"); + ERROR("Too many mapping"); + return -1; + } + + rt_config->port_mappings = util_common_calloc_s(sizeof(port_mapping *) * (rt->p_mapping_len)); + if (rt_config->port_mappings == NULL) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + return -1; + } + for (j = 0; j < rt->p_mapping_len; j++) { + rt_config->port_mappings[j] = util_common_calloc_s(sizeof(port_mapping)); + if (rt_config->port_mappings[j] == NULL) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + return -1; + } + (rt_config->port_mappings_len)++; + if (copy_cni_port_mapping(rt_config->port_mappings[j], rt->p_mapping[j]) != 0) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + return -1; + } + } + return 0; +} + +static int inject_runtime_config_items(const struct network_config *orig, const struct runtime_conf *rt, + net_conf_runtime_config **rt_config, bool *inserted, char **err) +{ + char *work = NULL; + bool value = false; + int ret = -1; + size_t i = 0; + + *rt_config = util_common_calloc_s(sizeof(net_conf_runtime_config)); + if (*rt_config == NULL) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + goto free_out; + } + for (i = 0; i < orig->network->capabilities->len; i++) { + work = orig->network->capabilities->keys[i]; + value = orig->network->capabilities->values[i]; + if (!value || work == NULL) { + continue; + } + if (strcmp(work, "portMappings") == 0 && rt->p_mapping_len > 0) { + if (inject_port_mappings(rt, *rt_config, err) != 0) { + ERROR("Inject port mappings failed"); + goto free_out; + } + *inserted = true; + } + /* new capabilities add here */ + } + ret = 0; +free_out: + return ret; +} + +static int do_generate_net_conf_json(const struct network_config *orig, char **result, char **err) +{ + struct parser_context ctx = { OPT_GEN_SIMPLIFY, 0 }; + parser_error jerr = NULL; + int ret = 0; + + /* generate new json str for injected config */ + *result = net_conf_generate_json(orig->network, &ctx, &jerr); + if (*result == NULL) { + if (asprintf(err, "generate json failed: %s", jerr) < 0) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + } + ERROR("Generate json: %s", jerr); + ret = -1; + goto out; + } + +out: + free(jerr); + return ret; +} + +static inline bool check_inject_runtime_config_args(const struct network_config *orig, const struct runtime_conf *rt, + char * const *result, char * const *err) +{ + return (orig == NULL || rt == NULL || result == NULL || err == NULL); +} + +static int inject_runtime_config(const struct network_config *orig, const struct runtime_conf *rt, char **result, + char **err) +{ + bool insert_rt_config = false; + int ret = -1; + net_conf_runtime_config *rt_config = NULL; + net_conf_runtime_config *save_conf = NULL; + + if (check_inject_runtime_config_args(orig, rt, result, err)) { + ERROR("Invalid arguments"); + return -1; + } + + if (orig->network == NULL || orig->network->capabilities == NULL) { + return 0; + } + + save_conf = orig->network->runtime_config; + + ret = inject_runtime_config_items(orig, rt, &rt_config, &insert_rt_config, err); + if (ret != 0) { + ERROR("inject runtime config failed: %s", *err != NULL ? *err : ""); + goto free_out; + } + + if (!insert_rt_config) { + goto generate_result; + } + + orig->network->runtime_config = rt_config; + +generate_result: + ret = do_generate_net_conf_json(orig, result, err); + +free_out: + orig->network->runtime_config = save_conf; + free_net_conf_runtime_config(rt_config); + if (ret != 0) { + free(*result); + *result = NULL; + } + return ret; +} + +static int do_inject_prev_result(const struct result *prev_result, net_conf *work, char **err) +{ + if (prev_result == NULL) { + return 0; + } + + free_result_curr(work->prev_result); + work->prev_result = result_curr_to_json_result(prev_result, err); + if (work->prev_result == NULL) { + return -1; + } + return 0; +} + +static inline bool check_build_one_config(const struct network_config_list *list, const struct network_config *orig, + const struct runtime_conf *rt, char * const *result, char * const *err) +{ + return (list == NULL || orig == NULL || rt == NULL || result == NULL || err == NULL); +} + +static int build_one_config(const struct network_config_list *list, struct network_config *orig, + const struct result *prev_result, const struct runtime_conf *rt, char **result, char **err) +{ + int ret = -1; + net_conf *work = NULL; + + if (check_build_one_config(list, orig, rt, result, err)) { + ERROR("Invalid arguments"); + return ret; + } + + work = orig->network; + free(work->name); + work->name = util_strdup_s(list->list->name); + free(work->cni_version); + work->cni_version = util_strdup_s(list->list->cni_version); + + if (do_inject_prev_result(prev_result, work, err) != 0) { + ERROR("Inject pre result failed: %s", *err != NULL ? *err : ""); + goto free_out; + } + + if (inject_runtime_config(orig, rt, result, err) != 0) { + ERROR("Inject runtime config failed: %s", *err != NULL ? *err : ""); + goto free_out; + } + + ret = 0; +free_out: + if (ret != 0 && *err == NULL) { + *err = util_strdup_s("Out of memory"); + } + return ret; +} + +static int do_check_generate_net_conf_json(char **full_conf_bytes, struct network_config *pnet, char **err) +{ + struct parser_context ctx = { OPT_GEN_SIMPLIFY, 0 }; + parser_error serr = NULL; + int ret = 0; + + if (*full_conf_bytes != NULL) { + pnet->bytes = *full_conf_bytes; + *full_conf_bytes = NULL; + } else { + pnet->bytes = net_conf_generate_json(pnet->network, &ctx, &serr); + if (pnet->bytes == NULL) { + if (asprintf(err, "Generate json failed: %s", serr) < 0) { + *err = util_strdup_s("Out of memory"); + } + ERROR("Generate json: %s", serr); + ret = -1; + goto out; + } + } + +out: + free(serr); + return ret; +} + +static int run_cni_plugin(const struct network_config_list *list, size_t i, const char *operator, + const struct runtime_conf *rc, const char * const *paths, size_t paths_len, + struct result **pret, char **err) +{ + int ret = -1; + struct network_config net = { 0 }; + char *plugin_path = NULL; + struct cni_args *cargs = NULL; + char *full_conf_bytes = NULL; + struct result *tmp_result = NULL; + int save_errno = 0; + + net.network = list->list->plugins[i]; + if (net.network == NULL) { + *err = util_strdup_s("Empty network"); + ERROR("Empty network"); + goto free_out; + } + + ret = find_in_path(net.network->type, paths, paths_len, &plugin_path, &save_errno); + if (ret != 0) { + if (asprintf(err, "find plugin: \"%s\" failed: %s", net.network->type, get_invoke_err_msg(save_errno)) < 0) { + *err = util_strdup_s("Out of memory"); + } + ERROR("find plugin: \"%s\" failed: %s", net.network->type, get_invoke_err_msg(save_errno)); + goto free_out; + } + + tmp_result = pret != NULL ? *pret : NULL; + ret = build_one_config(list, &net, tmp_result, rc, &full_conf_bytes, err); + if (ret != 0) { + ERROR("build config failed: %s", *err != NULL ? *err : ""); + goto free_out; + } + + ret = do_check_generate_net_conf_json(&full_conf_bytes, &net, err); + if (ret != 0) { + ERROR("check gengerate net config failed: %s", *err != NULL ? *err : ""); + goto free_out; + } + + ret = args(operator, rc, paths, paths_len, &cargs, err); + if (ret != 0) { + ERROR("get plugin arguments failed: %s", *err != NULL ? *err : ""); + goto free_out; + } + + if (pret == NULL) { + ret = exec_plugin_without_result(plugin_path, net.bytes, cargs, err); + } else { + free_result(*pret); + *pret = NULL; + ret = exec_plugin_with_result(plugin_path, net.bytes, cargs, pret, err); + } +free_out: + free_cni_args(cargs); + free(plugin_path); + free(net.bytes); + return ret; +} + +static inline bool check_add_network_list_args(const struct network_config_list *list, const struct runtime_conf *rc, + struct result * const *pret, char * const *err) +{ + return (list == NULL || list->list == NULL || rc == NULL || pret == NULL || err == NULL); +} + +static int add_network_list(const struct network_config_list *list, const struct runtime_conf *rc, + const char * const *paths, size_t paths_len, struct result **pret, char **err) +{ + int ret = -1; + size_t i = 0; + struct result *prev_result = NULL; + + if (check_add_network_list_args(list, rc, pret, err)) { + ERROR("Empty arguments"); + return -1; + } + + for (i = 0; i < list->list->plugins_len; i++) { + ret = run_cni_plugin(list, i, "ADD", rc, paths, paths_len, &prev_result, err); + if (ret != 0) { + ERROR("Run ADD cni failed: %s", *err != NULL ? *err : ""); + goto free_out; + } + } + + *pret = prev_result; + ret = 0; +free_out: + if (ret != 0) { + free_result(prev_result); + } + return ret; +} + +static inline bool check_del_network_list_args(const struct network_config_list *list, const struct runtime_conf *rc, + char * const *err) +{ + return (list == NULL || list->list == NULL || rc == NULL || err == NULL); +} + +static int del_network_list(const struct network_config_list *list, const struct runtime_conf *rc, + const char * const *paths, size_t paths_len, char **err) +{ + size_t i = 0; + int ret = 0; + + if (check_del_network_list_args(list, rc, err)) { + ERROR("Empty arguments"); + return -1; + } + + for (i = list->list->plugins_len; i > 0; i--) { + ret = run_cni_plugin(list, (i - 1), "DEL", rc, paths, paths_len, NULL, err); + if (ret != 0) { + ERROR("Run DEL cni failed: %s", *err != NULL ? *err : ""); + goto free_out; + } + } + +free_out: + return ret; +} + +static inline bool check_add_network_args(const struct network_config *net, const struct runtime_conf *rc, + char * const *err) +{ + return (net == NULL || rc == NULL || err == NULL); +} + +static int add_network(const struct network_config *net, const struct runtime_conf *rc, const char * const *paths, + size_t paths_len, struct result **add_result, char **err) +{ + int ret = 0; + char *plugin_path = NULL; + char *net_bytes = NULL; + struct cni_args *cargs = NULL; + int save_errno = 0; + + if (check_add_network_args(net, rc, err)) { + ERROR("Empty arguments"); + return -1; + } + ret = find_in_path(net->network->type, paths, paths_len, &plugin_path, &save_errno); + if (ret != 0) { + if (asprintf(err, "find plugin: \"%s\" failed: %s", net->network->type, get_invoke_err_msg(save_errno)) < 0) { + *err = util_strdup_s("Out of memory"); + } + ERROR("find plugin: \"%s\" failed: %s", net->network->type, get_invoke_err_msg(save_errno)); + goto free_out; + } + + ret = inject_runtime_config(net, rc, &net_bytes, err); + if (ret != 0) { + ERROR("Inject runtime config: %s", *err != NULL ? *err : ""); + goto free_out; + } + + ret = args("ADD", rc, paths, paths_len, &cargs, err); + if (ret != 0) { + ERROR("Get ADD cni arguments: %s", *err != NULL ? *err : ""); + goto free_out; + } + + ret = exec_plugin_with_result(plugin_path, net_bytes, cargs, add_result, err); +free_out: + free(plugin_path); + free(net_bytes); + free_cni_args(cargs); + return ret; +} + +static inline bool check_del_network_args(const struct network_config *net, const struct runtime_conf *rc, + char * const *err) +{ + return (net == NULL || net->network == NULL || rc == NULL || err == NULL); +} + +static int del_network(const struct network_config *net, const struct runtime_conf *rc, const char * const *paths, + size_t paths_len, char **err) +{ + int ret = 0; + char *plugin_path = NULL; + char *net_bytes = NULL; + struct cni_args *cargs = NULL; + int save_errno = 0; + + if (check_del_network_args(net, rc, err)) { + ERROR("Empty arguments"); + return -1; + } + ret = find_in_path(net->network->type, paths, paths_len, &plugin_path, &save_errno); + if (ret != 0) { + if (asprintf(err, "find plugin: \"%s\" failed: %s", net->network->type, get_invoke_err_msg(save_errno)) < 0) { + *err = util_strdup_s("Out of memory"); + } + ERROR("find plugin: \"%s\" failed: %s", net->network->type, get_invoke_err_msg(save_errno)); + goto free_out; + } + + ret = inject_runtime_config(net, rc, &net_bytes, err); + if (ret != 0) { + ERROR("Inject runtime config: %s", *err != NULL ? *err : ""); + goto free_out; + } + + ret = args("DEL", rc, paths, paths_len, &cargs, err); + if (ret != 0) { + ERROR("Get DEL cni arguments: %s", *err != NULL ? *err : ""); + goto free_out; + } + + ret = exec_plugin_without_result(plugin_path, net_bytes, cargs, err); +free_out: + free(plugin_path); + free(net_bytes); + free_cni_args(cargs); + return ret; +} + +static int do_copy_plugin_args(const struct runtime_conf *rc, struct cni_args **cargs) +{ + size_t i = 0; + + if (rc->args_len == 0) { + return 0; + } + + if (rc->args_len > (INT_MAX / sizeof(char *)) / 2) { + ERROR("Large arguments"); + return -1; + } + (*cargs)->plugin_args = util_common_calloc_s((rc->args_len) * sizeof(char *) * 2); + if ((*cargs)->plugin_args == NULL) { + ERROR("Out of memory"); + return -1; + } + for (i = 0; i < rc->args_len; i++) { + (*cargs)->plugin_args[i][0] = util_strdup_s(rc->args[i][0]); + (*cargs)->plugin_args[i][1] = util_strdup_s(rc->args[i][1]); + (*cargs)->plugin_args_len = (i + 1); + } + + return 0; +} + +static int copy_args(const struct runtime_conf *rc, struct cni_args **cargs) +{ + if (rc->container_id != NULL) { + (*cargs)->container_id = util_strdup_s(rc->container_id); + } + if (rc->netns != NULL) { + (*cargs)->netns = util_strdup_s(rc->netns); + } + if (rc->ifname != NULL) { + (*cargs)->ifname = util_strdup_s(rc->ifname); + } + + return do_copy_plugin_args(rc, cargs); +} + +static int do_copy_args_paths(const char * const *paths, size_t paths_len, struct cni_args **cargs) +{ + if (paths == NULL) { + return 0; + } + + if (paths_len == 0) { + (*cargs)->path = util_strdup_s(""); + } else { + (*cargs)->path = cni_util_string_join(":", paths, paths_len); + if ((*cargs)->path == NULL) { + ERROR("Out of memory"); + return -1; + } + } + return 0; +} + +static inline bool check_args_args(const struct runtime_conf *rc, struct cni_args * const *cargs, char * const *err) +{ + return (rc == NULL || cargs == NULL || err == NULL); +} + +static int args(const char *action, const struct runtime_conf *rc, const char * const *paths, size_t paths_len, + struct cni_args **cargs, char **err) +{ + int ret = -1; + + if (check_args_args(rc, cargs, err)) { + ERROR("Empty arguments"); + return ret; + } + *cargs = util_common_calloc_s(sizeof(struct cni_args)); + if (*cargs == NULL) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + goto free_out; + } + if (action != NULL) { + (*cargs)->command = util_strdup_s(action); + } + if (do_copy_args_paths(paths, paths_len, cargs) != 0) { + goto free_out; + } + ret = copy_args(rc, cargs); + +free_out: + if (ret != 0) { + free_cni_args(*cargs); + *cargs = NULL; + if (*err == NULL) { + *err = util_strdup_s("Out of memory"); + } + } + return ret; +} + +void free_cni_port_mapping(struct cni_port_mapping *val) +{ + if (val != NULL) { + free(val->protocol); + free(val->host_ip); + free(val); + } +} + +void free_cni_network_conf(struct cni_network_conf *val) +{ + if (val != NULL) { + free(val->name); + free(val->type); + free(val->bytes); + free(val); + } +} + +void free_cni_network_list_conf(struct cni_network_list_conf *val) +{ + if (val != NULL) { + free(val->bytes); + free(val->name); + free(val->first_plugin_name); + free(val->first_plugin_type); + free(val); + } +} + +void free_runtime_conf(struct runtime_conf *rc) +{ + size_t i = 0; + + if (rc == NULL) { + return; + } + + free(rc->container_id); + rc->container_id = NULL; + free(rc->netns); + rc->netns = NULL; + free(rc->ifname); + rc->ifname = NULL; + + for (i = 0; i < rc->args_len; i++) { + free(rc->args[i][0]); + free(rc->args[i][1]); + } + free(rc->args); + rc->args = NULL; + + for (i = 0; i < rc->p_mapping_len; i++) { + free_cni_port_mapping(rc->p_mapping[i]); + } + free(rc->p_mapping); + rc->p_mapping = NULL; + free(rc); +} + +int cni_add_network_list(const char *net_list_conf_str, const struct runtime_conf *rc, char **paths, + struct result **pret, char **err) +{ + struct network_config_list *list = NULL; + int ret = 0; + size_t len = 0; + + if (err == NULL) { + ERROR("Empty arguments"); + return -1; + } + if (net_list_conf_str == NULL) { + *err = util_strdup_s("Empty net list conf argument"); + ERROR("Empty net list conf argument"); + return -1; + } + + ret = conflist_from_bytes(net_list_conf_str, &list, err); + if (ret != 0) { + ERROR("Parse conf list failed: %s", *err != NULL ? *err : ""); + return ret; + } + + len = util_array_len((const char * const *)paths); + ret = add_network_list(list, rc, (const char * const *)paths, len, pret, err); + + DEBUG("Add network list return with: %d", ret); + free_network_config_list(list); + return ret; +} + +int cni_add_network(const char *net_conf_str, const struct runtime_conf *rc, char **paths, struct result **add_result, + char **err) +{ + struct network_config *net = NULL; + int ret = 0; + size_t len = 0; + + if (err == NULL) { + ERROR("Empty err"); + return -1; + } + if (net_conf_str == NULL) { + *err = util_strdup_s("Empty net conf argument"); + ERROR("Empty net conf argument"); + return -1; + } + + ret = conf_from_bytes(net_conf_str, &net, err); + if (ret != 0) { + ERROR("Parse conf failed: %s", *err != NULL ? *err : ""); + return ret; + } + + len = util_array_len((const char * const *)paths); + ret = add_network(net, rc, (const char * const *)paths, len, add_result, err); + free_network_config(net); + return ret; +} + +int cni_del_network_list(const char *net_list_conf_str, const struct runtime_conf *rc, char **paths, char **err) +{ + struct network_config_list *list = NULL; + int ret = 0; + size_t len = 0; + + if (err == NULL) { + ERROR("Empty err"); + return -1; + } + if (net_list_conf_str == NULL) { + *err = util_strdup_s("Empty net list conf argument"); + ERROR("Empty net list conf argument"); + return -1; + } + + ret = conflist_from_bytes(net_list_conf_str, &list, err); + if (ret != 0) { + ERROR("Parse conf list failed: %s", *err != NULL ? *err : ""); + return ret; + } + + len = util_array_len((const char * const *)paths); + ret = del_network_list(list, rc, (const char * const *)paths, len, err); + + DEBUG("Delete network list return with: %d", ret); + free_network_config_list(list); + return ret; +} + +int cni_del_network(const char *net_conf_str, const struct runtime_conf *rc, char **paths, char **err) +{ + struct network_config *net = NULL; + int ret = 0; + size_t len = 0; + + if (err == NULL) { + ERROR("Empty err"); + return -1; + } + if (net_conf_str == NULL) { + *err = util_strdup_s("Empty net conf argument"); + ERROR("Empty net conf argument"); + return -1; + } + + ret = conf_from_bytes(net_conf_str, &net, err); + if (ret != 0) { + ERROR("Parse conf failed: %s", *err != NULL ? *err : ""); + return ret; + } + + len = util_array_len((const char * const *)paths); + ret = del_network(net, rc, (const char * const *)paths, len, err); + free_network_config(net); + return ret; +} + +int cni_get_version_info(const char *plugin_type, char **paths, struct plugin_info **pinfo, char **err) +{ + int ret = 0; + char *plugin_path = NULL; + size_t len; + int save_errno = 0; + + if (err == NULL) { + ERROR("Empty err"); + return -1; + } + len = util_array_len((const char * const *)paths); + ret = find_in_path(plugin_type, (const char * const *)paths, len, &plugin_path, &save_errno); + if (ret != 0) { + if (asprintf(err, "find plugin: \"%s\" failed: %s", plugin_type, get_invoke_err_msg(save_errno)) < 0) { + *err = util_strdup_s("Out of memory"); + } + ERROR("find plugin: \"%s\" failed: %s", plugin_type, get_invoke_err_msg(save_errno)); + return ret; + } + + ret = raw_get_version_info(plugin_path, pinfo, err); + free(plugin_path); + return ret; +} + +int cni_conf_files(const char *dir, const char **extensions, size_t ext_len, char ***result, char **err) +{ + if (err == NULL) { + ERROR("Empty err"); + return -1; + } + return conf_files(dir, extensions, ext_len, result, err); +} + +int cni_conf_from_file(const char *filename, struct cni_network_conf **config, char **err) +{ + int ret = 0; + struct network_config *netconf = NULL; + + if (err == NULL) { + ERROR("Empty err"); + return -1; + } + ret = conf_from_file(filename, &netconf, err); + if (ret != 0) { + ERROR("Parse conf file: %s failed: %s", filename, *err != NULL ? *err : ""); + return ret; + } + + *config = util_common_calloc_s(sizeof(struct cni_network_conf)); + if (*config == NULL) { + *err = util_strdup_s("Out of memory"); + ret = -1; + ERROR("Out of memory"); + goto free_out; + } + + if (netconf != NULL && netconf->network != NULL) { + (*config)->type = netconf->network->type ? util_strdup_s(netconf->network->type) : NULL; + (*config)->name = netconf->network->name ? util_strdup_s(netconf->network->name) : NULL; + } + if (netconf != NULL) { + (*config)->bytes = netconf->bytes; + netconf->bytes = NULL; + } + + ret = 0; + +free_out: + free_network_config(netconf); + return ret; +} + +static void json_obj_to_cni_list_conf(struct network_config_list *src, struct cni_network_list_conf *list) +{ + if (src == NULL) { + return; + } + + list->bytes = src->bytes; + src->bytes = NULL; + if (src->list != NULL) { + list->name = src->list->name ? util_strdup_s(src->list->name) : NULL; + list->plugin_len = src->list->plugins_len; + if (src->list->plugins_len > 0 && src->list->plugins != NULL && src->list->plugins[0] != NULL) { + list->first_plugin_name = src->list->plugins[0]->name != NULL ? + util_strdup_s(src->list->plugins[0]->name) : NULL; + list->first_plugin_type = src->list->plugins[0]->type != NULL ? + util_strdup_s(src->list->plugins[0]->type) : NULL; + } + } +} + +int cni_conflist_from_bytes(const char *bytes, struct cni_network_list_conf **list, char **err) +{ + struct network_config_list *tmp_net_conf_list = NULL; + int ret = 0; + + if (err == NULL) { + ERROR("Empty err"); + return -1; + } + ret = conflist_from_bytes(bytes, &tmp_net_conf_list, err); + if (ret != 0) { + return ret; + } + *list = util_common_calloc_s(sizeof(struct cni_network_list_conf)); + if (*list == NULL) { + *err = util_strdup_s("Out of memory"); + ret = -1; + ERROR("Out of memory"); + goto free_out; + } + + json_obj_to_cni_list_conf(tmp_net_conf_list, *list); + + ret = 0; +free_out: + free_network_config_list(tmp_net_conf_list); + return ret; +} + +int cni_conflist_from_file(const char *filename, struct cni_network_list_conf **list, char **err) +{ + struct network_config_list *tmp_net_conf_list = NULL; + int ret = 0; + + if (err == NULL) { + ERROR("Empty err"); + return -1; + } + ret = conflist_from_file(filename, &tmp_net_conf_list, err); + if (ret != 0) { + return ret; + } + *list = util_common_calloc_s(sizeof(struct cni_network_list_conf)); + if (*list == NULL) { + *err = util_strdup_s("Out of memory"); + ret = -1; + ERROR("Out of memory"); + goto free_out; + } + + json_obj_to_cni_list_conf(tmp_net_conf_list, *list); + + ret = 0; +free_out: + free_network_config_list(tmp_net_conf_list); + return ret; +} + +static inline bool check_cni_conflist_from_conf_args(const struct cni_network_conf *cni_conf, + struct cni_network_list_conf * const *cni_conf_list) +{ + return (cni_conf == NULL || cni_conf_list == NULL); +} + +int cni_conflist_from_conf(const struct cni_network_conf *cni_conf, struct cni_network_list_conf **cni_conf_list, + char **err) +{ + struct network_config *net = NULL; + struct network_config_list *net_list = NULL; + int ret = 0; + bool invalid_arg = false; + + if (err == NULL) { + ERROR("Empty err"); + return -1; + } + + invalid_arg = check_cni_conflist_from_conf_args(cni_conf, cni_conf_list); + if (invalid_arg) { + *err = util_strdup_s("Empty cni conf or conflist argument"); + ERROR("Empty cni conf or conflist argument"); + return -1; + } + + ret = conf_from_bytes(cni_conf->bytes, &net, err); + if (ret != 0) { + goto free_out; + } + + ret = conflist_from_conf(net, &net_list, err); + if (ret != 0) { + goto free_out; + } + + *cni_conf_list = util_common_calloc_s(sizeof(struct cni_network_list_conf)); + if (*cni_conf_list == NULL) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + ret = -1; + goto free_out; + } + + json_obj_to_cni_list_conf(net_list, *cni_conf_list); + ret = 0; + +free_out: + if (net != NULL) { + free_network_config(net); + } + free_network_config_list(net_list); + return ret; +} + +int cni_log_init(const char *driver, const char *file, const char *priority) +{ + struct clibcni_log_config conf = { 0 }; + + conf.name = "clibcni"; + conf.driver = driver; + conf.file = file; + conf.priority = priority; + return clibcni_log_enable(&conf); +} + +void cni_set_log_prefix(const char *prefix) +{ + clibcni_set_log_prefix(prefix); +} + +void cni_free_log_prefix() +{ + clibcni_free_log_prefix(); +} + diff --git a/src/api.h b/src/api.h new file mode 100644 index 0000000..95b3cb8 --- /dev/null +++ b/src/api.h @@ -0,0 +1,100 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. + * clibcni licensed under the Mulan PSL v1. + * You can use this software according to the terms and conditions of the Mulan PSL v1. + * You may obtain a copy of Mulan PSL v1 at: + * http://license.coscl.org.cn/MulanPSL + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR + * PURPOSE. + * See the Mulan PSL v1 for more details. + * Author: tanyifeng + * Create: 2019-04-25 + * Description: provide cni function definition + *********************************************************************************/ +#ifndef CLIBCNI_API_H +#define CLIBCNI_API_H + +#include + +#include "version.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct cni_port_mapping { + int32_t host_port; + int32_t container_port; + char *protocol; + char *host_ip; +}; + +struct runtime_conf { + char *container_id; + char *netns; + char *ifname; + char *(*args)[2]; + size_t args_len; + + struct cni_port_mapping **p_mapping; + size_t p_mapping_len; +}; + +struct cni_network_conf { + char *name; + char *type; + char *bytes; +}; + +struct cni_network_list_conf { + size_t plugin_len; + char *first_plugin_name; + char *first_plugin_type; + char *name; + char *bytes; +}; + +int cni_add_network_list(const char *net_list_conf_str, const struct runtime_conf *rc, char **paths, + struct result **pret, char **err); + +int cni_add_network(const char *net_conf_str, const struct runtime_conf *rc, char **paths, struct result **add_result, + char **err); + +int cni_del_network_list(const char *net_list_conf_str, const struct runtime_conf *rc, char **paths, char **err); + +int cni_del_network(const char *net_conf_str, const struct runtime_conf *rc, char **paths, char **err); + +int cni_get_version_info(const char *plugin_type, char **paths, struct plugin_info **pinfo, char **err); + +int cni_conf_files(const char *dir, const char **extensions, size_t ext_len, char ***result, char **err); + +int cni_conf_from_file(const char *filename, struct cni_network_conf **config, char **err); + +int cni_conflist_from_bytes(const char *bytes, struct cni_network_list_conf **list, char **err); + +int cni_conflist_from_file(const char *filename, struct cni_network_list_conf **list, char **err); + +int cni_conflist_from_conf(const struct cni_network_conf *net_conf, struct cni_network_list_conf **net_conf_list, + char **err); + +void free_cni_network_conf(struct cni_network_conf *val); + +void free_cni_network_list_conf(struct cni_network_list_conf *val); + +void free_cni_port_mapping(struct cni_port_mapping *val); + +void free_runtime_conf(struct runtime_conf *rc); + +int cni_log_init(const char *driver, const char *file, const char *priority); + +void cni_set_log_prefix(const char *prefix); + +void cni_free_log_prefix(); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/conf.c b/src/conf.c new file mode 100644 index 0000000..abd7d9e --- /dev/null +++ b/src/conf.c @@ -0,0 +1,642 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. + * clibcni licensed under the Mulan PSL v1. + * You can use this software according to the terms and conditions of the Mulan PSL v1. + * You may obtain a copy of Mulan PSL v1 at: + * http://license.coscl.org.cn/MulanPSL + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR + * PURPOSE. + * See the Mulan PSL v1 for more details. + * Author: tanyifeng + * Create: 2019-04-25 + * Description: provide conf functions + *********************************************************************************/ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include "conf.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "log.h" +#include "net_conf.h" +#include "net_conf_list.h" +#include "api.h" + +static int do_conf_from_bytes(const char *conf_str, struct network_config *config, char **err) +{ + int ret = 0; + parser_error jerr = NULL; + + config->network = net_conf_parse_data(conf_str, NULL, &jerr); + if (config->network == NULL) { + ret = asprintf(err, "Error parsing configuration: %s", jerr); + if (ret < 0) { + *err = util_strdup_s("Out of memory"); + } + ERROR("Error parsing configuration: %s", jerr); + ret = -1; + goto out; + } + if (config->network->name != NULL && util_validate_name(config->network->name) != 0) { + ret = asprintf(err, "Invalid network name: %s", config->network->name); + if (ret < 0) { + *err = util_strdup_s("Out of memory"); + } + ERROR("Invalid network name: %s", config->network->name); + ret = -1; + goto out; + } + + config->bytes = util_strdup_s(conf_str); +out: + free(jerr); + return ret; +} + +static inline bool check_conf_from_bytes_args(struct network_config * const *config, char * const *err) +{ + return (config == NULL || err == NULL); +} + +int conf_from_bytes(const char *conf_str, struct network_config **config, char **err) +{ + int ret = -1; + + if (check_conf_from_bytes_args(config, err)) { + ERROR("Invalid arguments"); + return ret; + } + if (conf_str == NULL) { + *err = util_strdup_s("Empty json"); + ERROR("Empty json"); + return ret; + } + *config = util_common_calloc_s(sizeof(struct network_config)); + if (*config == NULL) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + goto free_out; + } + + ret = do_conf_from_bytes(conf_str, *config, err); +free_out: + if (ret != 0) { + free_network_config(*config); + *config = NULL; + } + return ret; +} + +static char *do_get_net_confs_json(const char *filename, char **err) +{ + size_t filesize = 0; + char *content = NULL; + + content = read_file(filename, &filesize); + if (content == NULL) { + if (asprintf(err, "Read file %s failed: %s", filename, strerror(errno)) < 0) { + *err = util_strdup_s("Read file failed"); + } + ERROR("Read file %s failed: %s", filename, strerror(errno)); + } + + return content; +} + +static inline bool check_conf_from_file_args(const char *filename, struct network_config * const *config, + char * const *err) +{ + return (filename == NULL || config == NULL || err == NULL); +} + +int conf_from_file(const char *filename, struct network_config **config, char **err) +{ + char *content = NULL; + int ret = -1; + + if (check_conf_from_file_args(filename, config, err)) { + ERROR("Invalid arguments"); + return -1; + } + content = do_get_net_confs_json(filename, err); + if (content == NULL) { + ERROR("Parse net conf file: %s failed: %s", filename, *err != NULL ? *err : ""); + ret = -1; + goto free_out; + } + + ret = conf_from_bytes(content, config, err); +free_out: + free(content); + return ret; +} + +static int do_check_net_conf_list_plugins(const net_conf_list *tmp_list, char **err) +{ + size_t i = 0; + + if (tmp_list->plugins == NULL) { + *err = util_strdup_s("Error parsing configuration list: no 'plugins' key"); + ERROR("Error parsing configuration list: no 'plugins' key"); + return -1; + } + if (tmp_list->plugins_len == 0) { + *err = util_strdup_s("Error parsing configuration list: no plugins in list"); + ERROR("Error parsing configuration list: no plugins in list"); + return -1; + } + for (i = 0; i < tmp_list->plugins_len; i++) { + if (tmp_list->plugins[i]->name != NULL && util_validate_name(tmp_list->plugins[i]->name) != 0) { + if (asprintf(err, "Invalid network name: %s", tmp_list->plugins[i]->name) < 0) { + *err = util_strdup_s("Out of memory"); + } + ERROR("Invalid network name: %s", tmp_list->plugins[i]->name); + return -1; + } + } + return 0; +} + +static int check_net_conf_list(const net_conf_list *tmp_list, char **err) +{ + if (tmp_list->name == NULL) { + *err = util_strdup_s("Error parsing configuration list: no name"); + ERROR("Name is NULL"); + return -1; + } + + if (util_validate_name(tmp_list->name) != 0) { + if (asprintf(err, "Invalid network name: %s", tmp_list->name) < 0) { + *err = util_strdup_s("Out of memory"); + } + ERROR("Invalid network name: %s", tmp_list->name); + return -1; + } + + return do_check_net_conf_list_plugins(tmp_list, err); +} + +static inline bool check_conflist_from_bytes_args(struct network_config_list * const *list, char * const *err) +{ + return (list == NULL || err == NULL); +} + +int conflist_from_bytes(const char *json_str, struct network_config_list **list, char **err) +{ + int ret = -1; + parser_error jerr = NULL; + net_conf_list *tmp_list = NULL; + + if (check_conflist_from_bytes_args(list, err)) { + ERROR("Invalid arguments"); + return ret; + } + if (json_str == NULL) { + *err = util_strdup_s("Empty json"); + ERROR("Empty json"); + return -1; + } + *list = util_common_calloc_s(sizeof(struct network_config_list)); + if (*list == NULL) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + goto free_out; + } + tmp_list = net_conf_list_parse_data(json_str, NULL, &jerr); + if (tmp_list == NULL) { + ret = asprintf(err, "Error parsing configuration list: %s", jerr); + if (ret < 0) { + *err = util_strdup_s("Out of memory"); + } + ERROR("Error parsing configuration list: %s", jerr); + ret = -1; + goto free_out; + } + + ret = check_net_conf_list(tmp_list, err); + if (ret != 0) { + goto free_out; + } + + (*list)->bytes = util_strdup_s(json_str); + (*list)->list = tmp_list; + + ret = 0; +free_out: + free(jerr); + if (ret != 0) { + free_net_conf_list(tmp_list); + free_network_config_list(*list); + *list = NULL; + } + return ret; +} + +static inline bool check_conflist_from_file_args(const char *filename, struct network_config_list * const *list, + char * const *err) +{ + return (filename == NULL || list == NULL || err == NULL); +} + +int conflist_from_file(const char *filename, struct network_config_list **list, char **err) +{ + char *content = NULL; + int ret = -1; + + if (check_conflist_from_file_args(filename, list, err)) { + ERROR("Invalid arguments"); + return -1; + } + content = do_get_net_confs_json(filename, err); + if (content == NULL) { + ERROR("Parse net conf file: %s failed: %s", filename, *err != NULL ? *err : ""); + ret = -1; + goto free_out; + } + + ret = conflist_from_bytes(content, list, err); +free_out: + free(content); + return ret; +} + +static int get_ext(const char *fname) +{ + int i = 0; + int ret = -1; + + if (fname == NULL) { + ERROR("File is NULL"); + return -1; + } + for (i = (int)strlen(fname) - 1; i >= 0; i--) { + if (fname[i] == '/') { + break; + } + if (fname[i] == '.') { + ret = i; + break; + } + } + + return ret; +} + +static int check_conf_dir(const char *dir, DIR **directory, char **err) +{ + *directory = opendir(dir); + if (*directory == NULL) { + if (errno == ENOENT) { + return 0; + } + if (asprintf(err, "Open dir failed: %s", strerror(errno)) < 0) { + *err = util_strdup_s("Out of memory"); + } + SYSERROR("Open dir failed"); + return -1; + } + return 1; +} + +static int do_check_file_is_valid(const char *fname, int *result, char **err) +{ + struct stat tmp_fstat; + int nret = -1; + + nret = lstat(fname, &tmp_fstat); + if (nret != 0) { + nret = asprintf(err, "lstat %s failed: %s", fname, strerror(errno)); + if (nret < 0) { + *err = util_strdup_s("Out of memory"); + } + SYSERROR("lstat %s failed", fname); + *result = -1; + return -1; + } + + if (S_ISDIR(tmp_fstat.st_mode)) { + // ignore dir + *result = 0; + ERROR("conf file %s is dir", fname); + return -1; + } + + if (tmp_fstat.st_size > MB) { + nret = asprintf(err, "Too large config file: %s", fname); + if (nret < 0) { + *err = util_strdup_s("Out of memory"); + } + ERROR("Too large config file: %s", fname); + *result = -1; + return -1; + } + + return 0; +} + +static int check_conf_file(const char *dir, const char * const *extensions, size_t ext_len, + const struct dirent *pdirent, + size_t *result_size, char ***result, char **err) +{ + char fname[PATH_MAX] = { 0 }; + size_t i = 0; + const char *ext_name = NULL; + int nret = -1; + int ret = 0; + size_t cap = *result_size; + + nret = sprintf_s(fname, PATH_MAX, "%s/%s", dir, pdirent->d_name); + if (nret < 0) { + *err = util_strdup_s("Pathname too long"); + ERROR("Pathname too long"); + return -1; + } + + nret = do_check_file_is_valid(fname, &ret, err); + if (nret != 0) { + return ret; + } + + /* compare extension */ + nret = get_ext(pdirent->d_name); + if (nret < 0) { + // ignore this error + return 0; + } + ext_name = (pdirent->d_name) + nret; + for (i = 0; i < ext_len; i++) { + if (extensions[i] != NULL && strcmp(ext_name, extensions[i]) == 0) { + if (util_grow_array(result, &cap, (*result_size) + 1, 2) != 0) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + return -1; + } + (*result)[(*result_size)++] = util_strdup_s(fname); + break; + } + } + + return 0; +} + +static inline bool check_conf_files_args(const char *dir, const char * const *extensions, char ** const *result, + char * const *err) +{ + return (dir == NULL || extensions == NULL || result == NULL || err == NULL); +} + +int conf_files(const char *dir, const char * const *extensions, size_t ext_len, char ***result, char **err) +{ +#define MAX_FILES 200 + int ret = -1; + int nret = -1; + DIR *directory = NULL; + struct dirent *pdirent = NULL; + size_t size = 0; + + if (check_conf_files_args(dir, extensions, result, err)) { + ERROR("Invalid arguments"); + return -1; + } + nret = check_conf_dir(dir, &directory, err); + if (nret != 1) { + /* dir is not exist, just ignore, do not return error */ + return nret; + } + + pdirent = readdir(directory); + while (pdirent != NULL) { + if (strcmp(pdirent->d_name, ".") == 0 || strcmp(pdirent->d_name, "..") == 0) { + pdirent = readdir(directory); + continue; + } + + nret = check_conf_file(dir, extensions, ext_len, pdirent, &size, result, err); + if (nret < 0) { + goto free_out; + } + + pdirent = readdir(directory); + } + + if (size > MAX_FILES) { + nret = asprintf(err, "Too more config files, current support max count of config file is %d.", MAX_FILES); + if (nret < 0) { + *err = util_strdup_s("Out of memory"); + } + ERROR("Too more config files, current support max count of config file is %d.", MAX_FILES); + ret = -1; + goto free_out; + } + + ret = 0; +free_out: + nret = closedir(directory); + if (nret != 0) { + if (*err == NULL) { + *err = util_strdup_s("Failed to close directory"); + SYSERROR("Failed to close directory"); + } + ret = -1; + } + if (ret != 0) { + util_free_array(*result); + *result = NULL; + } + return ret; +} + +int cmpstr(const void *a, const void *b) +{ + return strcmp(*((const char **)a), *((const char **)b)); +} + +static inline bool check_load_conf_args(const char *dir, const char *name, struct network_config * const *conf, + char * const *err) + +{ + return (dir == NULL || name == NULL || conf == NULL || err == NULL); +} + +int load_conf(const char *dir, const char *name, struct network_config **conf, char **err) +{ + char **files = NULL; + const char *exts[] = { ".conf", ".json" }; + int ret = 0; + size_t len = 0; + size_t i = 0; + + if (check_load_conf_args(dir, name, conf, err)) { + ERROR("Invalid arguments"); + return -1; + } + + ret = conf_files(dir, exts, sizeof(exts) / sizeof(char *), &files, err); + if (ret != 0) { + return -1; + } + len = util_array_len((const char * const *)files); + if (len == 0) { + if (asprintf(err, "no net configurations found in %s", dir) < 0) { + *err = util_strdup_s("Out of memory"); + } + ERROR("no net configurations found in %s", dir); + goto free_out; + } + + qsort((void *)files, len, sizeof(char *), cmpstr); + + for (i = 0; i < len; i++) { + ret = conf_from_file(files[i], conf, err); + if (ret != 0) { + goto free_out; + } + if (((*conf)->network->name) != NULL && strcmp((*conf)->network->name, name) == 0) { + ret = 0; + goto free_out; + } + free_network_config(*conf); + *conf = NULL; + } + ret = asprintf(err, "No net configuration with name \"%s\" in %s", name, dir); + if (ret < 0) { + *err = util_strdup_s("Out of memory"); + } + ERROR("No net configuration with name \"%s\" in %s", name, dir); + ret = -1; + +free_out: + util_free_array(files); + return ret; +} + +static int generate_new_conflist(const net_conf_list *list, struct network_config_list **conf_list, char **err) +{ + struct parser_context ctx = { OPT_GEN_SIMPLIFY, 0 }; + parser_error jerr = NULL; + char *net_conf_json_str = NULL; + int ret = -1; + + net_conf_json_str = net_conf_list_generate_json(list, &ctx, &jerr); + if (net_conf_json_str == NULL) { + ret = asprintf(err, "Generate conf list json failed: %s", jerr); + if (ret < 0) { + *err = util_strdup_s("Out of memory"); + } + ERROR("Generate conf list json failed: %s", jerr); + goto free_out; + } + free(jerr); + jerr = NULL; + (*conf_list)->bytes = net_conf_json_str; + + (*conf_list)->list = net_conf_list_parse_data(net_conf_json_str, &ctx, &jerr); + if ((*conf_list)->list == NULL) { + ret = asprintf(err, "Parse conf list from json failed: %s", jerr); + if (ret < 0) { + *err = util_strdup_s("Out of memory"); + } + ERROR("Parse conf list from json failed: %s", jerr); + goto free_out; + } + ret = 0; +free_out: + free(jerr); + return ret; +} + +static inline bool check_conflist_from_conf_args(const struct network_config *conf, + struct network_config_list * const *conf_list, char * const *err) +{ + return (conf == NULL || conf->network == NULL || conf_list == NULL || err == NULL); +} + +int conflist_from_conf(const struct network_config *conf, struct network_config_list **conf_list, char **err) +{ + int ret = -1; + net_conf_list *list = NULL; + + if (check_conflist_from_conf_args(conf, conf_list, err)) { + ERROR("Invalid arguments"); + return -1; + } + *conf_list = util_common_calloc_s(sizeof(struct network_config_list)); + if (*conf_list == NULL) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + return -1; + } + + list = util_common_calloc_s(sizeof(net_conf_list)); + if (list == NULL) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + goto free_out; + } + list->plugins = util_common_calloc_s(sizeof(net_conf *) * (1 + 1)); + if (list->plugins == NULL) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + goto free_out; + } + list->plugins[0] = conf->network; + list->plugins_len = 1; + + if (conf->network->cni_version != NULL) { + list->cni_version = util_strdup_s(conf->network->cni_version); + } + if (conf->network->name != NULL) { + list->name = util_strdup_s(conf->network->name); + } + ret = generate_new_conflist(list, conf_list, err); + +free_out: + if (list != NULL && list->plugins != NULL) { + list->plugins_len = 0; + list->plugins[0] = NULL; + } + free_net_conf_list(list); + + if (ret != 0) { + free_network_config_list(*conf_list); + *conf_list = NULL; + } + return ret; +} + +void free_network_config(struct network_config *config) +{ + if (config != NULL) { + free_net_conf(config->network); + config->network = NULL; + free(config->bytes); + config->bytes = NULL; + free(config); + } +} + +void free_network_config_list(struct network_config_list *conf_list) +{ + if (conf_list != NULL) { + free_net_conf_list(conf_list->list); + conf_list->list = NULL; + free(conf_list->bytes); + conf_list->bytes = NULL; + free(conf_list); + } +} + diff --git a/src/conf.h b/src/conf.h new file mode 100644 index 0000000..27d577e --- /dev/null +++ b/src/conf.h @@ -0,0 +1,61 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. + * clibcni licensed under the Mulan PSL v1. + * You can use this software according to the terms and conditions of the Mulan PSL v1. + * You may obtain a copy of Mulan PSL v1 at: + * http://license.coscl.org.cn/MulanPSL + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR + * PURPOSE. + * See the Mulan PSL v1 for more details. + * Author: tanyifeng + * Create: 2019-04-25 + * Description: provide conf function definition + *********************************************************************************/ + +#ifndef CLIBCNI_CONF_H +#define CLIBCNI_CONF_H + +#include +#include "net_conf_list.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct network_config { + net_conf *network; + + char *bytes; +}; + +struct network_config_list { + net_conf_list *list; + + char *bytes; +}; + +void free_network_config(struct network_config *config); + +void free_network_config_list(struct network_config_list *conf_list); + +int conf_from_bytes(const char *conf_str, struct network_config **config, char **err); + +int conf_from_file(const char *filename, struct network_config **config, char **err); + +int conflist_from_bytes(const char *json_str, struct network_config_list **list, char **err); + +int conflist_from_file(const char *filename, struct network_config_list **list, char **err); + +int load_conf(const char *dir, const char *name, struct network_config **conf, char **err); + +int conflist_from_conf(const struct network_config *conf, struct network_config_list **conf_list, char **err); + +int conf_files(const char *dir, const char * const *extensions, size_t ext_len, char ***result, char **err); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/invoke/CMakeLists.txt b/src/invoke/CMakeLists.txt new file mode 100644 index 0000000..28296d2 --- /dev/null +++ b/src/invoke/CMakeLists.txt @@ -0,0 +1,3 @@ +# get current directory sources files +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} invokesrcs) +set(INVOKE_SRCS ${invokesrcs} PARENT_SCOPE) diff --git a/src/invoke/args.c b/src/invoke/args.c new file mode 100644 index 0000000..e55896a --- /dev/null +++ b/src/invoke/args.c @@ -0,0 +1,202 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. + * clibcni licensed under the Mulan PSL v1. + * You can use this software according to the terms and conditions of the Mulan PSL v1. + * You may obtain a copy of Mulan PSL v1 at: + * http://license.coscl.org.cn/MulanPSL + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR + * PURPOSE. + * See the Mulan PSL v1 for more details. + * Author: tanyifeng + * Create: 2019-04-25 + * Description: provide args functions + *********************************************************************************/ +#define _GNU_SOURCE +#define __USE_GNU +#include +#include +#include +#include + +#include "utils.h" +#include "args.h" +#include "log.h" + +void free_cni_args(struct cni_args *cargs) +{ + size_t i = 0; + + if (cargs == NULL) { + return; + } + + free(cargs->command); + cargs->command = NULL; + free(cargs->container_id); + cargs->container_id = NULL; + free(cargs->netns); + cargs->netns = NULL; + free(cargs->plugin_args_str); + cargs->plugin_args_str = NULL; + free(cargs->ifname); + cargs->ifname = NULL; + free(cargs->path); + cargs->path = NULL; + for (i = 0; i < cargs->plugin_args_len; i++) { + free(cargs->plugin_args[i][0]); + cargs->plugin_args[i][0] = NULL; + free(cargs->plugin_args[i][1]); + cargs->plugin_args[i][1] = NULL; + } + free(cargs->plugin_args); + cargs->plugin_args = NULL; + free(cargs); +} + +static char *env_stringify(char *(*pargs)[2], size_t len) +{ + char **entries = NULL; + const char **work = NULL; + char *result = NULL; + size_t i = 0; + bool invalid_arg = (pargs == NULL || len == 0); + + if (invalid_arg) { + ERROR("Invalid arguments"); + return NULL; + } + + if (len > (INT_MAX / sizeof(char *)) - 1) { + ERROR("Too large arguments"); + return NULL; + } + entries = util_common_calloc_s(sizeof(char *) * (len + 1)); + if (entries == NULL) { + ERROR("Out of memory"); + return NULL; + } + for (i = 0; i < len; i++) { + work = (const char **)pargs[i]; + entries[i] = cni_util_string_join("=", work, 2); + if (entries[i] == NULL) { + ERROR("Join args failed"); + goto free_out; + } + } + + result = cni_util_string_join(";", (const char **)entries, len); +free_out: + util_free_array(entries); + return result; +} + +static int add_cni_envs(const struct cni_args *cniargs, size_t *pos, char **result) +{ + char *plugin_args_str = NULL; + char *buffer = NULL; + size_t i = *pos; + int nret = 0; + int ret = -1; + + plugin_args_str = cniargs->plugin_args_str ? util_strdup_s(cniargs->plugin_args_str) : NULL; + if (is_null_or_empty(plugin_args_str)) { + free(plugin_args_str); + plugin_args_str = env_stringify(cniargs->plugin_args, cniargs->plugin_args_len); + } + + nret = asprintf(&buffer, "%s=%s", ENV_CNI_COMMAND, cniargs->command); + if (nret < 0) { + ERROR("Sprintf failed"); + goto free_out; + } + result[i++] = buffer; + buffer = NULL; + nret = asprintf(&buffer, "%s=%s", ENV_CNI_CONTAINERID, cniargs->container_id); + if (nret < 0) { + ERROR("Sprintf failed"); + goto free_out; + } + result[i++] = buffer; + buffer = NULL; + nret = asprintf(&buffer, "%s=%s", ENV_CNI_NETNS, cniargs->netns); + if (nret < 0) { + ERROR("Sprintf failed"); + goto free_out; + } + result[i++] = buffer; + buffer = NULL; + nret = asprintf(&buffer, "%s=%s", ENV_CNI_ARGS, plugin_args_str); + if (nret < 0) { + ERROR("Sprintf failed"); + goto free_out; + } + result[i++] = buffer; + buffer = NULL; + nret = asprintf(&buffer, "%s=%s", ENV_CNI_IFNAME, cniargs->ifname); + if (nret < 0) { + ERROR("Sprintf failed"); + goto free_out; + } + result[i++] = buffer; + buffer = NULL; + nret = asprintf(&buffer, "%s=%s", ENV_CNI_PATH, cniargs->path); + if (nret < 0) { + ERROR("Sprintf failed"); + goto free_out; + } + result[i++] = buffer; + + ret = 0; +free_out: + free(plugin_args_str); + *pos = i; + return ret; +} + +char **as_env(const struct cni_args *cniargs) +{ + char **result = NULL; + char **pos = NULL; + size_t len = 0; + size_t i = 0; + size_t j = 0; + char **envir = environ; + + if (cniargs == NULL) { + ERROR("Invlaid cni args"); + return NULL; + } + + len = util_array_len((const char * const *)envir); + + if (len > ((SIZE_MAX / sizeof(char *)) - (CNI_ENVS_LEN + 1))) { + ERROR("Too large arguments"); + return NULL; + } + + len += (CNI_ENVS_LEN + 1); + result = util_common_calloc_s(len * sizeof(char *)); + if (result == NULL) { + ERROR("Out of memory"); + return NULL; + } + + if (add_cni_envs(cniargs, &i, result) != 0) { + goto free_out; + } + + /* inherit environs of parent */ + for (pos = envir; pos != NULL && *pos != NULL && i < len; pos++) { + result[i] = util_strdup_s(*pos); + i++; + } + + return result; +free_out: + for (j = 0; j < i; j++) { + free(result[j]); + } + free(result); + return NULL; +} diff --git a/src/invoke/args.h b/src/invoke/args.h new file mode 100644 index 0000000..0b8c273 --- /dev/null +++ b/src/invoke/args.h @@ -0,0 +1,44 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. + * clibcni licensed under the Mulan PSL v1. + * You can use this software according to the terms and conditions of the Mulan PSL v1. + * You may obtain a copy of Mulan PSL v1 at: + * http://license.coscl.org.cn/MulanPSL + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR + * PURPOSE. + * See the Mulan PSL v1 for more details. + * Author: tanyifeng + * Create: 2019-04-25 + * Description: provide cni args function definition + *********************************************************************************/ + +#ifndef CLIBCNI_INVOKE_ARGS_H +#define CLIBCNI_INVOKE_ARGS_H + +#include + +#define CNI_ENVS_LEN 6 +#define ENV_CNI_COMMAND "CNI_COMMAND" +#define ENV_CNI_CONTAINERID "CNI_CONTAINERID" +#define ENV_CNI_NETNS "CNI_NETNS" +#define ENV_CNI_ARGS "CNI_ARGS" +#define ENV_CNI_IFNAME "CNI_IFNAME" +#define ENV_CNI_PATH "CNI_PATH" + +struct cni_args { + char *command; + char *container_id; + char *netns; + char *(*plugin_args)[2]; + size_t plugin_args_len; + char *plugin_args_str; + char *ifname; + char *path; +}; + +char **as_env(const struct cni_args *cniargs); + +void free_cni_args(struct cni_args *cargs); + +#endif diff --git a/src/invoke/exec.c b/src/invoke/exec.c new file mode 100644 index 0000000..6457893 --- /dev/null +++ b/src/invoke/exec.c @@ -0,0 +1,610 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. + * clibcni licensed under the Mulan PSL v1. + * You can use this software according to the terms and conditions of the Mulan PSL v1. + * You may obtain a copy of Mulan PSL v1 at: + * http://license.coscl.org.cn/MulanPSL + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR + * PURPOSE. + * See the Mulan PSL v1 for more details. + * Author: tanyifeng + * Create: 2019-04-25 + * Description: provide exec functions + *********************************************************************************/ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "exec.h" + +#include "utils.h" +#include "tools.h" +#include "invoke_errno.h" +#include "log.h" + +static int raw_exec(const char *plugin_path, const char *stdin_data, char * const environs[], char **stdout_str, + exec_error **err); + +static char *str_exec_error(const exec_error *e_err) +{ + char *result = NULL; + int ret = 0; + + if (e_err == NULL) { + ERROR("Argument is NULL"); + return result; + } + ret = asprintf(&result, "%s%s", e_err->msg ? e_err->msg : "", e_err->details ? e_err->details : ""); + if (ret < 0) { + ERROR("Sprintf failed"); + return NULL; + } + return result; +} + +static int do_parse_exec_stdout_str(int exec_ret, const char *net_conf_json, const exec_error *e_err, + const char *stdout_str, struct result **result, char **err) +{ + int ret = exec_ret; + char *version = NULL; + + if (exec_ret != 0) { + if (e_err != NULL) { + *err = str_exec_error(e_err); + } else { + *err = util_strdup_s("raw exec fail"); + } + } else { + version = cniversion_decode(net_conf_json, err); + if (version == NULL) { + ret = -1; + ERROR("Decode cni version failed: %s", *err != NULL ? *err : ""); + goto out; + } + if (is_null_or_empty(stdout_str)) { + ERROR("Get empty stdout message"); + goto out; + } + *result = new_result(version, stdout_str, err); + if (*result == NULL) { + ERROR("Parse result failed: %s", *err != NULL ? *err : ""); + ret = -1; + } + } + +out: + free(version); + return ret; +} + +static inline bool check_exec_plugin_with_result_args(const char *net_conf_json, struct result * const *result, + char * const *err) +{ + return (net_conf_json == NULL || result == NULL || err == NULL); +} + +int exec_plugin_with_result(const char *plugin_path, const char *net_conf_json, const struct cni_args *cniargs, + struct result **result, char **err) +{ + char **envs = NULL; + char *stdout_str = NULL; + exec_error *e_err = NULL; + int ret = 0; + + if (check_exec_plugin_with_result_args(net_conf_json, result, err)) { + ERROR("Invalid arguments"); + return -1; + } + if (cniargs != NULL) { + envs = as_env(cniargs); + if (envs == NULL) { + *err = util_strdup_s("As env failed"); + ret = -1; + goto out; + } + } + + ret = raw_exec(plugin_path, net_conf_json, envs, &stdout_str, &e_err); + DEBUG("Raw exec \"%s\" result: %d", plugin_path, ret); + ret = do_parse_exec_stdout_str(ret, net_conf_json, e_err, stdout_str, result, err); +out: + free(stdout_str); + util_free_array(envs); + free_exec_error(e_err); + return ret; +} + +int exec_plugin_without_result(const char *plugin_path, const char *net_conf_json, const struct cni_args *cniargs, + char **err) +{ + char **envs = NULL; + exec_error *e_err = NULL; + int ret = 0; + bool invalid_arg = (net_conf_json == NULL || err == NULL); + + if (invalid_arg) { + ERROR("Invalid arguments"); + return -1; + } + if (cniargs != NULL) { + envs = as_env(cniargs); + if (envs == NULL) { + *err = util_strdup_s("As env failed"); + goto out; + } + } + + ret = raw_exec(plugin_path, net_conf_json, envs, NULL, &e_err); + if (ret != 0) { + if (e_err != NULL) { + *err = str_exec_error(e_err); + } else { + *err = util_strdup_s("raw exec fail"); + } + } + DEBUG("Raw exec \"%s\" result: %d", plugin_path, ret); +out: + util_free_array(envs); + free_exec_error(e_err); + return ret; +} + +static int do_parse_get_version_errmsg(int exec_ret, const exec_error *e_err, struct plugin_info **result, char **err) +{ + char *str_err = NULL; + + if (exec_ret == 0) { + return 0; + } + + str_err = str_exec_error(e_err); + if (str_err != NULL && strcmp(str_err, "unknown CNI_COMMAND: VERSION") == 0) { + const char *default_supports[] = { "0.1.0", NULL }; + *result = plugin_supports(default_supports, 1, err); + if (*result == NULL) { + ERROR("Parse result failed: %s", *err != NULL ? *err : ""); + goto free_out; + } + } + *err = str_err; + str_err = NULL; +free_out: + free(str_err); + return -1; +} + +int raw_get_version_info(const char *plugin_path, struct plugin_info **result, char **err) +{ + int ret = 0; + struct cni_args args = { + .command = "VERSION", + .netns = "dummy", + .ifname = "dummy", + .path = "dummy", + .container_id = NULL, + .plugin_args = NULL, + .plugin_args_len = 0, + .plugin_args_str = NULL + }; + char *stdin_data = NULL; + char *stdout_str = NULL; + const char *version = current(); + size_t len = 0; + char **envs = NULL; + exec_error *e_err = NULL; + bool invalid_arg = (result == NULL || err == NULL); + + if (invalid_arg) { + ERROR("Invalid arguments"); + return -1; + } + + envs = as_env(&args); + if (envs == NULL) { + ret = -1; + *err = util_strdup_s("As env failed"); + goto free_out; + } + len = strlen("{\"cniVersion\":}") + strlen(version) + 1; + stdin_data = util_common_calloc_s(len); + if (stdin_data == NULL) { + ERROR("Out of memory"); + ret = -1; + goto free_out; + } + ret = sprintf_s(stdin_data, len, "{\"cniVersion\":%s}", version); + if (ret < 0) { + ERROR("Sprintf failed"); + *err = util_strdup_s("Sprintf failed"); + goto free_out; + } + ret = raw_exec(plugin_path, stdin_data, envs, &stdout_str, &e_err); + DEBUG("Raw exec \"%s\" result: %d", plugin_path, ret); + ret = do_parse_get_version_errmsg(ret, e_err, result, err); + if (ret != 0) { + goto free_out; + } + *result = plugin_info_decode(stdout_str, err); + if (*result == NULL) { + ret = -1; + } + +free_out: + free_exec_error(e_err); + util_free_array(envs); + free(stdin_data); + free(stdout_str); + return ret; +} + +static int prepare_child(int pipe_stdin, int pipe_stdout) +{ + sigset_t mask; + int ecode = 0; + int ret = 0; + + if (pipe_stdin != STDIN_FILENO) { + ret = dup2(pipe_stdin, STDIN_FILENO); + } else { + ret = fcntl(pipe_stdin, F_SETFD, 0); + } + if (ret != 0) { + ecode = EXIT_FAILURE; + goto child_err_out; + } + (void)close(pipe_stdin); + pipe_stdin = -1; + + if (pipe_stdout != STDOUT_FILENO) { + ret = dup2(pipe_stdout, STDOUT_FILENO); + } else { + ret = fcntl(pipe_stdout, F_SETFD, 0); + } + if (ret < 0) { + ecode = EXIT_FAILURE; + goto child_err_out; + } + (void)close(pipe_stdout); + pipe_stdout = -1; + + { + /* + * unblock all signal + * */ + ret = sigfillset(&mask); + if (ret < 0) { + ecode = EXIT_FAILURE; + goto child_err_out; + } + ret = sigprocmask(SIG_UNBLOCK, &mask, NULL); + if (ret < 0) { + ecode = EXIT_FAILURE; + goto child_err_out; + } + } + +child_err_out: + return ecode; +} + +static void child_fun(const char *plugin_path, int pipe_stdin, int pipe_stdout, char * const environs[], + size_t envs_len) +{ + char *argv[2] = { NULL }; + int ecode = 0; + + argv[0] = util_strdup_s(plugin_path); + + ecode = prepare_child(pipe_stdin, pipe_stdout); + if (ecode != 0) { + goto child_err_out; + } + + if (envs_len > 0) { + ecode = execvpe(plugin_path, argv, environs); + } else { + ecode = execvp(plugin_path, argv); + } + + (void)fprintf(stdout, "Execv: %s failed %s", plugin_path, strerror(errno)); + +child_err_out: + free(argv[0]); + if (ecode == 0) { + ecode = 127; + } + if (pipe_stdin != -1) { + (void)close(pipe_stdin); + } + if (pipe_stdout != -1) { + (void)close(pipe_stdout); + } + exit(ecode); +} + +static inline bool check_prepare_raw_exec_args(const char *plugin_path) +{ + return (plugin_path == NULL || util_validate_absolute_path(plugin_path)); +} + +static int prepare_raw_exec(const char *plugin_path, int pipe_stdin[2], int pipe_stdout[2], char *errmsg, size_t len) +{ + int ret = 0; + + if (check_prepare_raw_exec_args(plugin_path)) { + ret = sprintf_s(errmsg, len, "Empty or not absolute path: %s", plugin_path); + if (ret < 0) { + ERROR("Sprintf failed"); + } + return -1; + } + + ret = pipe2(pipe_stdin, O_CLOEXEC | O_NONBLOCK); + if (ret < 0) { + ret = sprintf_s(errmsg, len, "Pipe stdin failed: %s", strerror(errno)); + if (ret < 0) { + ERROR("Sprintf failed"); + } + return -1; + } + + ret = pipe2(pipe_stdout, O_CLOEXEC | O_NONBLOCK); + if (ret < 0) { + ret = sprintf_s(errmsg, len, "Pipe stdout failed: %s", strerror(errno)); + if (ret < 0) { + ERROR("Sprintf failed"); + } + return -1; + } + return 0; +} + +static int write_stdin_data_to_child(int pipe_stdin[2], const char *stdin_data, char *errmsg, size_t errmsg_len) +{ + int ret = 0; + size_t len = 0; + + if (stdin_data == NULL) { + goto close_pipe; + } + + len = strlen(stdin_data); + if (util_write_nointr(pipe_stdin[1], stdin_data, len) != (ssize_t)len) { + ret = sprintf_s(errmsg, errmsg_len, "Write stdin data failed: %s", strerror(errno)); + if (ret < 0) { + ERROR("Sprintf failed"); + } + ret = -1; + } +close_pipe: + (void)close(pipe_stdin[1]); + pipe_stdin[1] = -1; + return ret; +} + +static int read_child_stdout_msg(const int pipe_stdout[2], char *errmsg, size_t errmsg_len, char **stdout_str) +{ + int ret = 0; + + if (errmsg == NULL) { + return 0; + } + if (stdout_str != NULL) { + char buffer[BUFFER_SIZE] = { 0 }; + ssize_t tmp_len = util_read_nointr(pipe_stdout[0], buffer, BUFFER_SIZE - 1); + if (tmp_len < 0) { + ret = sprintf_s(errmsg, errmsg_len, "%s; read stdout failed: %s", strlen(errmsg) > 0 ? errmsg : "", + strerror(errno)); + if (ret < 0) { + ERROR("Sprintf failed"); + } + ret = -1; + } else if (tmp_len > 0) { + *stdout_str = util_strdup_s(buffer); + } + } + + return ret; +} + +static int wait_pid_for_raw_exec_child(pid_t child_pid, const int pipe_stdout[2], char **stdout_str, char *errmsg, + size_t errmsg_len, bool *parse_exec_err) +{ + pid_t wait_pid = 0; + int wait_status = 0; + int ret = 0; + + if (errmsg == NULL) { + return -1; + } + do { + wait_pid = waitpid(child_pid, &wait_status, 0); + } while (wait_pid < 0 && errno == EINTR); + + ret = read_child_stdout_msg(pipe_stdout, errmsg, errmsg_len, stdout_str); + + if (wait_pid < 0) { + ret = sprintf_s(errmsg, errmsg_len, "%s; waitpid failed: %s", strlen(errmsg) > 0 ? errmsg : "", + strerror(errno)); + if (ret < 0) { + ERROR("Sprintf failed"); + } + ret = -1; + goto err_free_out; + } else if (WIFEXITED(wait_status) && WEXITSTATUS(wait_status)) { + ret = sprintf_s(errmsg, errmsg_len, "%s; get child status: %d", strlen(errmsg) > 0 ? errmsg : "", + WEXITSTATUS(wait_status)); + if (ret < 0) { + ERROR("Sprintf failed"); + } + ret = WEXITSTATUS(wait_status); + *parse_exec_err = true; + goto err_free_out; + } else if (WIFSIGNALED(wait_status)) { + ret = sprintf_s(errmsg, errmsg_len, "%s; child get signal: %d", strlen(errmsg) > 0 ? errmsg : "", + WTERMSIG(wait_status)); + if (ret < 0) { + ERROR("Sprintf failed"); + } + ret = INK_ERR_TERM_BY_SIG; + *parse_exec_err = true; + goto err_free_out; + } + +err_free_out: + return ret; +} + +static void close_raw_exec_pipes(int pipe_stdin[2], int pipe_stdout[2]) +{ + if (pipe_stdout[0] >= 0) { + (void)close(pipe_stdout[0]); + pipe_stdout[0] = -1; + } + if (pipe_stdout[1] >= 0) { + (void)close(pipe_stdout[1]); + pipe_stdout[1] = -1; + } + if (pipe_stdin[0] >= 0) { + (void)close(pipe_stdin[0]); + pipe_stdin[0] = -1; + } + if (pipe_stdin[1] >= 0) { + (void)close(pipe_stdin[1]); + pipe_stdin[1] = -1; + } +} + +static inline bool check_make_err_message_args(bool parse_exec_err, char * const *stdout_str) +{ + return (parse_exec_err && stdout_str != NULL && *stdout_str != NULL); +} + +static void make_err_message(const char *plugin_path, char **stdout_str, int ret, bool parse_exec_err, char *errmsg, + size_t errmsg_len, exec_error **err) +{ + int nret = ret; + bool get_err_msg = false; + + if (errmsg == NULL) { + return; + } + if (check_make_err_message_args(parse_exec_err, stdout_str)) { + parser_error json_err = NULL; + *err = exec_error_parse_data(*stdout_str, NULL, &json_err); + if (*err == NULL) { + nret = sprintf_s(errmsg, errmsg_len, "exec \'%s\': %s; parse failed: %s", plugin_path, + strlen(errmsg) > 0 ? errmsg : "", json_err); + if (nret < 0) { + ERROR("Sprintf failed"); + } + nret = INK_ERR_PARSE_JSON_TO_OBJECT_FAILED; + } + free(json_err); + } + + get_err_msg = (nret != 0 && *err == NULL && strlen(errmsg) > 0); + if (get_err_msg) { + *err = util_common_calloc_s(sizeof(exec_error)); + if (*err != NULL) { + char *tmp_err = NULL; + nret = asprintf(&tmp_err, "exec \'%s\' failed: %s", plugin_path, errmsg); + if (nret < 0) { + tmp_err = util_strdup_s(errmsg); + } + (*err)->msg = tmp_err; + (*err)->code = 1; + } + } +} + +static int do_parent_waitpid(int pipe_stdin[2], const int pipe_stdout[2], pid_t child_pid, char *errmsg, + size_t errmsg_len, const char *stdin_data, char **stdout_str, bool *parse_exec_err) +{ + int ret = 0; + + if (errmsg == NULL) { + return -1; + } + /* write stdin_data into stdin of child process */ + if (write_stdin_data_to_child(pipe_stdin, stdin_data, errmsg, errmsg_len) != 0) { + ERROR("Write stdin data failed: %s", errmsg); + ret = -1; + } + + /* wait child exit, and deal with exitcode */ + if (wait_pid_for_raw_exec_child(child_pid, pipe_stdout, stdout_str, errmsg, errmsg_len, parse_exec_err) != 0) { + ERROR("Wait pid for child failed: %s", errmsg); + ret = -1; + } + + return ret; +} + +static int raw_exec(const char *plugin_path, const char *stdin_data, char * const environs[], char **stdout_str, + exec_error **err) +{ + int ret = 0; + int pipe_stdout[2] = { -1, -1 }; + int pipe_stdin[2] = { -1, -1 }; + pid_t child_pid = 0; + char errmsg[BUFFER_SIZE] = { 0 }; + bool parse_exec_err = false; + + if (prepare_raw_exec(plugin_path, pipe_stdin, pipe_stdout, errmsg, sizeof(errmsg)) != 0) { + ret = -1; + goto err_free_out; + } + + child_pid = fork(); + if (child_pid < 0) { + if (sprintf_s(errmsg, sizeof(errmsg), "Fork failed: %s", strerror(errno)) < 0) { + ERROR("Sprintf failed"); + } + ret = -1; + goto err_free_out; + } + + if (child_pid == 0) { + (void)close(pipe_stdin[1]); + pipe_stdin[1] = -1; + (void)close(pipe_stdout[0]); + pipe_stdout[0] = -1; + + size_t envs_len = 0; + envs_len = util_array_len((const char * const *)environs); + child_fun(plugin_path, pipe_stdin[0], pipe_stdout[1], environs, envs_len); + /* exit in child_fun */ + } + + (void)close(pipe_stdout[1]); + pipe_stdout[1] = -1; + (void)close(pipe_stdin[0]); + pipe_stdin[0] = -1; + + ret = do_parent_waitpid(pipe_stdin, pipe_stdout, child_pid, errmsg, sizeof(errmsg), stdin_data, stdout_str, + &parse_exec_err); +err_free_out: + /* parse error json message */ + make_err_message(plugin_path, stdout_str, ret, parse_exec_err, errmsg, sizeof(errmsg), err); + + if (ret != 0 && stdout_str != NULL) { + free(*stdout_str); + *stdout_str = NULL; + } + + close_raw_exec_pipes(pipe_stdin, pipe_stdout); + + return ret; +} + diff --git a/src/invoke/exec.h b/src/invoke/exec.h new file mode 100644 index 0000000..7408977 --- /dev/null +++ b/src/invoke/exec.h @@ -0,0 +1,39 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. + * clibcni licensed under the Mulan PSL v1. + * You can use this software according to the terms and conditions of the Mulan PSL v1. + * You may obtain a copy of Mulan PSL v1 at: + * http://license.coscl.org.cn/MulanPSL + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR + * PURPOSE. + * See the Mulan PSL v1 for more details. + * Author: tanyifeng + * Create: 2019-04-25 + * Description: provide exec function definition + ********************************************************************************/ + +#ifndef CLIBCNI_INVOKE_EXEC_H +#define CLIBCNI_INVOKE_EXEC_H + +#include "args.h" +#include "types.h" +#include "version.h" +#include "exec_error.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int exec_plugin_with_result(const char *plugin_path, const char *net_conf_json, const struct cni_args *cniargs, + struct result **ret, char **err); + +int exec_plugin_without_result(const char *plugin_path, const char *net_conf_json, const struct cni_args *cniargs, + char **err); + +int raw_get_version_info(const char *plugin_path, struct plugin_info **result, char **err); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/invoke/invoke_errno.h b/src/invoke/invoke_errno.h new file mode 100644 index 0000000..b01b082 --- /dev/null +++ b/src/invoke/invoke_errno.h @@ -0,0 +1,35 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. + * clibcni licensed under the Mulan PSL v1. + * You can use this software according to the terms and conditions of the Mulan PSL v1. + * You may obtain a copy of Mulan PSL v1 at: + * http://license.coscl.org.cn/MulanPSL + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR + * PURPOSE. + * See the Mulan PSL v1 for more details. + * Author: tanyifeng + * Create: 2019-04-25 + * Description: provide errno definition + *********************************************************************************/ +#ifndef CLIBCNI_INVOKE_ERRNO_H +#define CLIBCNI_INVOKE_ERRNO_H + +/* + * [ -255 ... -1 ] are errors define by us; + * 0 is success + * [ 1 .... ] are errors return by call syscall. + * */ +enum InvokeErrCode { + INK_ERR_MIN = -5, + INK_ERR_INVALID_ARG, // invalid arguments + INK_ERR_SPRINT_FAILED, + INK_ERR_TERM_BY_SIG, + INK_ERR_PARSE_JSON_TO_OBJECT_FAILED, + INK_SUCCESS = 0, + INK_ERR_MAX = 1024 +}; + +extern const char *get_invoke_err_msg(int errcode); + +#endif diff --git a/src/invoke/tools.c b/src/invoke/tools.c new file mode 100644 index 0000000..49827b6 --- /dev/null +++ b/src/invoke/tools.c @@ -0,0 +1,110 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. + * clibcni licensed under the Mulan PSL v1. + * You can use this software according to the terms and conditions of the Mulan PSL v1. + * You may obtain a copy of Mulan PSL v1 at: + * http://license.coscl.org.cn/MulanPSL + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR + * PURPOSE. + * See the Mulan PSL v1 for more details. + * Author: tanyifeng + * Create: 2019-04-25 + * Description: provide tools functions + **********************************************************************************/ +#define _GNU_SOURCE +#include "tools.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "invoke_errno.h" +#include "log.h" + +const char * const g_CNI_INVOKE_ERR_MSGS[] = { + "Invalid ERROR code", + "Invalid invoke argument", + "Call sprintf_s failed", + "Terminal by signal", + "Parse json string failed", + /* new error message add here */ + "Success" +}; + +static inline bool check_get_invoke_err_msg_args(int errcode) +{ + return (errcode <= INK_ERR_MIN || errcode >= INK_ERR_MAX); +} + +const char *get_invoke_err_msg(int errcode) +{ + if (check_get_invoke_err_msg_args(errcode)) { + return NULL; + } + if (errcode <= INK_SUCCESS) { + return g_CNI_INVOKE_ERR_MSGS[errcode - (INK_ERR_MIN)]; + } + return strerror(errcode); +} + +static int do_check_file(const char *plugin, const char *path, char **find_path, int *save_errno) +{ + int nret = 0; + char tmp_path[PATH_MAX] = { 0 }; + struct stat rt_stat = { 0 }; + + nret = sprintf_s(tmp_path, PATH_MAX, "%s/%s", path, plugin); + if (nret < 0) { + ERROR("Sprint failed"); + *save_errno = INK_ERR_SPRINT_FAILED; + return -1; + } + nret = stat(tmp_path, &rt_stat); + if (nret == 0 && S_ISREG(rt_stat.st_mode)) { + *find_path = util_strdup_s(tmp_path); + *save_errno = 0; + return 0; + } else { + *save_errno = ENOENT; + return -1; + } +} + +static inline bool check_find_in_path_args(const char *plugin, const char * const *paths, size_t len, + char * const *find_path) +{ + return (is_null_or_empty(plugin) || paths == NULL || len == 0 || find_path == NULL); +} + +int find_in_path(const char *plugin, const char * const *paths, size_t len, char **find_path, int *save_errno) +{ + int ret = -1; + size_t i = 0; + + if (check_find_in_path_args(plugin, paths, len, find_path)) { + ERROR("Invalid arguments"); + return -1; + } + for (i = 0; i < len; i++) { + if (do_check_file(plugin, paths[i], find_path, save_errno) == 0) { + ret = 0; + break; + } + } + + if (ret != 0) { + ERROR("Can not find plugin: %s", plugin); + } + + return ret; +} + diff --git a/src/invoke/tools.h b/src/invoke/tools.h new file mode 100644 index 0000000..d3ec143 --- /dev/null +++ b/src/invoke/tools.h @@ -0,0 +1,29 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. + * clibcni licensed under the Mulan PSL v1. + * You can use this software according to the terms and conditions of the Mulan PSL v1. + * You may obtain a copy of Mulan PSL v1 at: + * http://license.coscl.org.cn/MulanPSL + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR + * PURPOSE. + * See the Mulan PSL v1 for more details. + * Author: tanyifeng + * Create: 2019-04-25 + * Description: provide some tool function definition + *********************************************************************************/ +#ifndef CLIBCNI_INVOKE_TOOLS_H +#define CLIBCNI_INVOKE_TOOLS_H + +#include "exec_error.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int find_in_path(const char *plugin, const char * const *paths, size_t len, char **find_path, int *save_errno); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/json/CMakeLists.txt b/src/json/CMakeLists.txt new file mode 100644 index 0000000..d3cd170 --- /dev/null +++ b/src/json/CMakeLists.txt @@ -0,0 +1,2 @@ +# generate .c and .h files for json +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/schema) diff --git a/src/json/schema/CMakeLists.txt b/src/json/schema/CMakeLists.txt new file mode 100644 index 0000000..beb7a61 --- /dev/null +++ b/src/json/schema/CMakeLists.txt @@ -0,0 +1,15 @@ +set(cmdpath python) +set(pysrcpath ${CMAKE_CURRENT_SOURCE_DIR}/src/generate.py) +set(schemapath ${CMAKE_CURRENT_SOURCE_DIR}/schema) +set(outputpath ${CMAKE_BINARY_DIR}/json) + +message("-- Generate .c and .h file into: " ${outputpath}) +add_subdirectory(src) + +execute_process(COMMAND ${cmdpath} ${pysrcpath} --gen-common --gen-ref -r --root=${schemapath} --out=${outputpath} ${schemapath} + ERROR_VARIABLE err + ) + +if (err) + message("error: " ${err}) +endif() diff --git a/src/json/schema/schema/cni_version.json b/src/json/schema/schema/cni_version.json new file mode 100644 index 0000000..3831508 --- /dev/null +++ b/src/json/schema/schema/cni_version.json @@ -0,0 +1,12 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "cniVersion": { + "type": "string" + } + }, + "required": [ + "cniVersion" + ] +} diff --git a/src/json/schema/schema/defs.json b/src/json/schema/schema/defs.json new file mode 100644 index 0000000..23f71a8 --- /dev/null +++ b/src/json/schema/schema/defs.json @@ -0,0 +1,243 @@ +{ + "description": "Definitions used throughout the OpenContainer Specification", + "definitions": { + "int8": { + "type": "integer", + "minimum": -128, + "maximum": 127 + }, + "int16": { + "type": "integer", + "minimum": -32768, + "maximum": 32767 + }, + "int32": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647 + }, + "int64": { + "type": "integer", + "minimum": -9223372036854776000, + "maximum": 9223372036854776000 + }, + "uint8": { + "type": "integer", + "minimum": 0, + "maximum": 255 + }, + "uint16": { + "type": "integer", + "minimum": 0, + "maximum": 65535 + }, + "uint32": { + "type": "integer", + "minimum": 0, + "maximum": 4294967295 + }, + "uint64": { + "type": "integer", + "minimum": 0, + "maximum": 18446744073709552000 + }, + "int32Pointer": { + "oneOf": [ + { + "$ref": "#/definitions/int32" + }, + { + "type": "null" + } + ] + }, + "uint16Pointer": { + "oneOf": [ + { + "$ref": "#/definitions/uint16" + }, + { + "type": "null" + } + ] + }, + "uint64Pointer": { + "oneOf": [ + { + "$ref": "#/definitions/uint64" + }, + { + "type": "null" + } + ] + }, + "stringPointer": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "percent": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "UID": { + "$ref": "#/definitions/uint32" + }, + "GID": { + "$ref": "#/definitions/uint32" + }, + "ArrayOfGIDs": { + "type": "array", + "items": { + "$ref": "#/definitions/GID" + } + }, + "FilePath": { + "type": "string" + }, + "Env": { + "$ref": "#/definitions/ArrayOfStrings" + }, + "Hook": { + "type": "object", + "properties": { + "path": { + "$ref": "#/definitions/FilePath" + }, + "args": { + "$ref": "#/definitions/ArrayOfStrings" + }, + "env": { + "$ref": "#/definitions/Env" + }, + "timeout": { + "type": "integer", + "minimum": 1 + } + }, + "required": [ + "path" + ] + }, + "ArrayOfHooks": { + "type": "array", + "items": { + "$ref": "#/definitions/Hook" + } + }, + "IDMapping": { + "type": "object", + "properties": { + "hostID": { + "$ref": "#/definitions/uint32" + }, + "containerID": { + "$ref": "#/definitions/uint32" + }, + "size": { + "$ref": "#/definitions/uint32" + } + }, + "required": [ + "hostID", + "containerID", + "size" + ] + }, + "Mount": { + "type": "object", + "properties": { + "source": { + "$ref": "#/definitions/FilePath" + }, + "destination": { + "$ref": "#/definitions/FilePath" + }, + "options": { + "$ref": "#/definitions/ArrayOfStrings" + }, + "type": { + "type": "string" + } + }, + "required": [ + "destination" + ] + }, + "ArrayOfStrings": { + "type": "array", + "items": { + "type": "string" + } + }, + "mapStringString": { + "type": "object", + "patternProperties": { + ".{1,}": { + "type": "string" + } + } + }, + "mapStringInt": { + "type": "object", + "patternProperties": { + ".{1,}": { + "type": "integer" + } + } + }, + "mapStringBool": { + "type": "object", + "patternProperties": { + ".{1,}": { + "type": "boolean" + } + } + }, + "mapIntString": { + "type": "object", + "patternProperties": { + ".{2,}": { + "type": "string" + } + } + }, + "mapIntInt": { + "type": "object", + "patternProperties": { + ".{2,}": { + "type": "integer" + } + } + }, + "mapIntBool": { + "type": "object", + "patternProperties": { + ".{2,}": { + "type": "boolean" + } + } + }, + "mapStringObject": { + "type": "object", + "patternProperties": { + ".{1,}": { + "type": "object" + } + } + }, + "ociVersion": { + "description": "The version of Open Container Runtime Specification that the document complies with", + "type": "string" + }, + "annotations": { + "$ref": "#/definitions/mapStringString" + } + } +} diff --git a/src/json/schema/schema/exec_error.json b/src/json/schema/schema/exec_error.json new file mode 100644 index 0000000..6bc0a69 --- /dev/null +++ b/src/json/schema/schema/exec_error.json @@ -0,0 +1,19 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "code": { + "type": "uint32" + }, + "msg": { + "type": "string" + }, + "details": { + "type": "string" + } + }, + "required": [ + "code", + "msg" + ] +} diff --git a/src/json/schema/schema/inner_plugin_info.json b/src/json/schema/schema/inner_plugin_info.json new file mode 100644 index 0000000..97620fb --- /dev/null +++ b/src/json/schema/schema/inner_plugin_info.json @@ -0,0 +1,15 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "cniVersion": { + "type": "string" + }, + "supportedVersions": { + "type": "ArrayOfStrings" + } + }, + "required": [ + "cniVersion" + ] +} diff --git a/src/json/schema/schema/net_args.json b/src/json/schema/schema/net_args.json new file mode 100644 index 0000000..98af839 --- /dev/null +++ b/src/json/schema/schema/net_args.json @@ -0,0 +1,85 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "K8S_POD_NAME": { + "type": "string" + }, + "K8S_POD_NAMESPACE": { + "type": "string" + }, + "SECURE_CONTAINER": { + "type": "string" + }, + "multi_port": { + "type": "int32" + }, + "phy-direct": { + "type": "string" + }, + "dpdk-direct": { + "type": "string" + }, + "phynet": { + "type": "string" + }, + "tenant_id": { + "type": "string" + }, + "vpc_id": { + "type": "string" + }, + "secret_name": { + "type": "string" + }, + "IP": { + "type": "string" + }, + "K8S_POD_NETWORK_ARGS": { + "type": "string" + }, + "INSTANCE_NAME": { + "type": "string" + }, + "dist_gateway_disable": { + "type": "boolean" + }, + "endpoint_policies": { + "type": "array", + "items": { + "type": "object", + "properties": { + "Type": { + "type": "string" + }, + "ExceptionList": { + "type": "ArrayOfStrings" + }, + "NeedEncap": { + "type": "boolean" + }, + "DestinationPrefix": { + "type": "string" + } + } + } + }, + "port_map": { + "type": "array", + "items": { + "type": "object", + "properties": { + "local_port": { + "type": "int32" + }, + "host_port": { + "type": "int32" + }, + "protocol": { + "type": "ArrayOfStrings" + } + } + } + } + } +} diff --git a/src/json/schema/schema/net_conf.json b/src/json/schema/schema/net_conf.json new file mode 100644 index 0000000..12eae65 --- /dev/null +++ b/src/json/schema/schema/net_conf.json @@ -0,0 +1,88 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "cniVersion": { + "type": "string" + }, + "name": { + "type": "string" + }, + "type": { + "type": "string" + }, + "ipMasq": { + "type": "boolean" + }, + "ipam": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "subnet": { + "type": "string" + }, + "gateway": { + "type": "string" + }, + "range-start": { + "type": "string" + }, + "range-end": { + "type": "string" + }, + "routes": { + "type": "array", + "items": { + "$ref": "network/route.json" + } + } + } + }, + "dns": { + "$ref": "network/dns.json" + }, + "multi_entry": { + "type": "int32" + }, + "backup_mode": { + "type": "boolean" + }, + "vlanID": { + "type": "int32" + }, + "vlan_inside": { + "type": "boolean" + }, + "vxlanID": { + "type": "int32" + }, + "vxlanID_inside": { + "type": "boolean" + }, + "actions": { + "type": "string" + }, + "args": { + "$ref": "net_args.json" + }, + "prevResult": { + "$ref": "result_curr.json" + }, + "runtimeConfig": { + "type": "object", + "properties": { + "portMappings": { + "type": "array", + "items": { + "$ref": "port_mapping.json" + } + } + } + }, + "capabilities": { + "$ref": "./defs.json#/definitions/mapStringBool" + } + } +} diff --git a/src/json/schema/schema/net_conf_list.json b/src/json/schema/schema/net_conf_list.json new file mode 100644 index 0000000..df63821 --- /dev/null +++ b/src/json/schema/schema/net_conf_list.json @@ -0,0 +1,18 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "cniVersion": { + "type": "string" + }, + "name": { + "type": "string" + }, + "plugins": { + "type": "array", + "items": { + "$ref": "net_conf.json" + } + } + } +} diff --git a/src/json/schema/schema/network/dns.json b/src/json/schema/schema/network/dns.json new file mode 100644 index 0000000..f29aa3c --- /dev/null +++ b/src/json/schema/schema/network/dns.json @@ -0,0 +1,18 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "nameservers": { + "type": "ArrayOfStrings" + }, + "domain": { + "type": "string" + }, + "search": { + "type": "ArrayOfStrings" + }, + "options": { + "type": "ArrayOfStrings" + } + } +} diff --git a/src/json/schema/schema/network/interface.json b/src/json/schema/schema/network/interface.json new file mode 100644 index 0000000..a9f799f --- /dev/null +++ b/src/json/schema/schema/network/interface.json @@ -0,0 +1,18 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "mac": { + "type": "string" + }, + "sandbox": { + "type": "string" + } + }, + "required": [ + "name" + ] +} diff --git a/src/json/schema/schema/network/ipconfig.json b/src/json/schema/schema/network/ipconfig.json new file mode 100644 index 0000000..ac921ba --- /dev/null +++ b/src/json/schema/schema/network/ipconfig.json @@ -0,0 +1,22 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "version": { + "type": "string" + }, + "interface": { + "$ref": "../defs.json#/definitions/int32Pointer" + }, + "address": { + "type": "string" + }, + "gateway": { + "type": "string" + } + }, + "required": [ + "version", + "address" + ] +} diff --git a/src/json/schema/schema/network/ipconfig_020.json b/src/json/schema/schema/network/ipconfig_020.json new file mode 100644 index 0000000..cd44621 --- /dev/null +++ b/src/json/schema/schema/network/ipconfig_020.json @@ -0,0 +1,21 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "ip": { + "type": "string" + }, + "gateway": { + "type": "string" + }, + "routes": { + "type": "array", + "items": { + "$ref": "route.json" + } + } + }, + "required": [ + "ip" + ] +} diff --git a/src/json/schema/schema/network/route.json b/src/json/schema/schema/network/route.json new file mode 100644 index 0000000..116816b --- /dev/null +++ b/src/json/schema/schema/network/route.json @@ -0,0 +1,15 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "dst": { + "type": "string" + }, + "gw": { + "type": "string" + } + }, + "required": [ + "dst" + ] +} diff --git a/src/json/schema/schema/port_mapping.json b/src/json/schema/schema/port_mapping.json new file mode 100644 index 0000000..c96ae8a --- /dev/null +++ b/src/json/schema/schema/port_mapping.json @@ -0,0 +1,18 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "hostPort": { + "type": "int32" + }, + "containerPort": { + "type": "int32" + }, + "protocol": { + "type": "string" + }, + "hostIP": { + "type": "string" + } + } +} diff --git a/src/json/schema/schema/result_curr.json b/src/json/schema/schema/result_curr.json new file mode 100644 index 0000000..42559c9 --- /dev/null +++ b/src/json/schema/schema/result_curr.json @@ -0,0 +1,30 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "cniVersion": { + "type": "string" + }, + "interfaces": { + "type": "array", + "items": { + "$ref": "network/interface.json" + } + }, + "ips": { + "type": "array", + "items": { + "$ref": "network/ipconfig.json" + } + }, + "routes": { + "type": "array", + "items": { + "$ref": "network/route.json" + } + }, + "ip4": {"$ref": "network/ipconfig_020.json"}, + "ip6": {"$ref": "network/ipconfig_020.json"}, + "dns": {"$ref": "network/dns.json"} + } +} diff --git a/src/json/schema/src/CMakeLists.txt b/src/json/schema/src/CMakeLists.txt new file mode 100644 index 0000000..8db178e --- /dev/null +++ b/src/json/schema/src/CMakeLists.txt @@ -0,0 +1,3 @@ +# get current directory sources files + +message("-- do nothing ") diff --git a/src/json/schema/src/common_c.py b/src/json/schema/src/common_c.py new file mode 100644 index 0000000..edb5768 --- /dev/null +++ b/src/json/schema/src/common_c.py @@ -0,0 +1,1223 @@ +# -*- coding: utf-8 -*- +''' +Description: commom source file +Interface: None +History: 2019-06-17 +''' +# - Copyright (C) Huawei Technologies Co., Ltd. 2019. All rights reserved. +# - clibcni licensed under the Mulan PSL v1. +# - You can use this software according to the terms and conditions of the Mulan PSL v1. +# - You may obtain a copy of Mulan PSL v1 at: +# - http://license.coscl.org.cn/MulanPSL +# - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR +# - IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR +# - PURPOSE. +# - See the Mulan PSL v1 for more details. +# - Description: generate json +# - Author: tanyifeng +# - Create: 2019-04-25 +#!/usr/bin/python -Es + +CODE = '''// Auto generated file. Do not edit! +# define _GNU_SOURCE +# include +# include +# include +# include "json_common.h" + +# define MAX_NUM_STR_LEN 21 + + + +yajl_gen_status map_uint(void *ctx, long long unsigned int num) { + char numstr[MAX_NUM_STR_LEN]; + int ret; + + ret = sprintf_s(numstr, sizeof(numstr), "%llu", num); + if (ret < 0) { + return yajl_gen_in_error_state; + } + return yajl_gen_number((yajl_gen)ctx, (const char *)numstr, strlen(numstr)); +} + +yajl_gen_status map_int(void *ctx, long long int num) { + char numstr[MAX_NUM_STR_LEN]; + int ret; + + ret = sprintf_s(numstr, sizeof(numstr), "%lld", num); + if (ret < 0) { + return yajl_gen_in_error_state; + } + return yajl_gen_number((yajl_gen)ctx, (const char *)numstr, strlen(numstr)); +} + + +bool json_gen_init(yajl_gen *g, const struct parser_context *ctx) { + *g = yajl_gen_alloc(NULL); + if (NULL == *g) { + return false; + + } + yajl_gen_config(*g, yajl_gen_beautify, (int)(!(ctx->options & OPT_GEN_SIMPLIFY))); + yajl_gen_config(*g, yajl_gen_validate_utf8, (int)(!(ctx->options & OPT_GEN_NO_VALIDATE_UTF8))); + return true; +} + +yajl_val get_val(yajl_val tree, const char *name, yajl_type type) { + const char *path[] = { name, NULL }; + return yajl_tree_get(tree, path, type); +} + +void *safe_malloc(size_t size) { + void *ret = NULL; + if (size == 0) { + abort(); + } + ret = calloc(1, size); + if (ret == NULL) { + abort(); + } + return ret; +} + +int common_safe_double(const char *numstr, double *converted) { + char *err_str = NULL; + double d; + + if (numstr == NULL) { + return -EINVAL; + } + + errno = 0; + d = strtod(numstr, &err_str); + if (errno > 0) { + return -errno; + } + + if (err_str == NULL || err_str == numstr || *err_str != '\\0') { + return -EINVAL; + } + + *converted = d; + return 0; +} + +int common_safe_uint8(const char *numstr, uint8_t *converted) { + char *err = NULL; + unsigned long int uli; + + if (numstr == NULL) { + return -EINVAL; + } + + errno = 0; + uli = strtoul(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (err == NULL || err == numstr || *err != '\\0') { + return -EINVAL; + } + + if (uli > UINT8_MAX) { + return -ERANGE; + } + + *converted = (uint8_t)uli; + return 0; +} + +int common_safe_uint16(const char *numstr, uint16_t *converted) { + char *err = NULL; + unsigned long int uli; + + if (numstr == NULL) { + return -EINVAL; + } + + errno = 0; + uli = strtoul(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (err == NULL || err == numstr || *err != '\\0') { + return -EINVAL; + } + + if (uli > UINT16_MAX) { + return -ERANGE; + } + + *converted = (uint16_t)uli; + return 0; +} + +int common_safe_uint32(const char *numstr, uint32_t *converted) { + char *err = NULL; + unsigned long long int ull; + + if (numstr == NULL) { + return -EINVAL; + } + + errno = 0; + ull = strtoull(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (err == NULL || err == numstr || *err != '\\0') { + return -EINVAL; + } + + if (ull > UINT32_MAX) { + return -ERANGE; + } + + *converted = (uint32_t)ull; + return 0; +} + +int common_safe_uint64(const char *numstr, uint64_t *converted) { + char *err = NULL; + unsigned long long int ull; + + if (numstr == NULL) { + return -EINVAL; + } + + errno = 0; + ull = strtoull(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (err == NULL || err == numstr || *err != '\\0') { + return -EINVAL; + } + + *converted = (uint64_t)ull; + return 0; +} + +int common_safe_uint(const char *numstr, unsigned int *converted) { + char *err = NULL; + unsigned long long int ull; + + if (numstr == NULL) { + return -EINVAL; + } + + errno = 0; + ull = strtoull(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (err == NULL || err == numstr || *err != '\\0') { + return -EINVAL; + } + + if (ull > UINT_MAX) { + return -ERANGE; + } + + *converted = (unsigned int)ull; + return 0; +} + +int common_safe_int8(const char *numstr, int8_t *converted) { + char *err = NULL; + long int li; + + if (numstr == NULL) { + return -EINVAL; + } + + errno = 0; + li = strtol(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (err == NULL || err == numstr || *err != '\\0') { + return -EINVAL; + } + + if (li > INT8_MAX || li < INT8_MIN) { + return -ERANGE; + } + + *converted = (int8_t)li; + return 0; +} + +int common_safe_int16(const char *numstr, int16_t *converted) { + char *err = NULL; + long int li; + + if (numstr == NULL) { + return -EINVAL; + } + + errno = 0; + li = strtol(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (err == NULL || err == numstr || *err != '\\0') { + return -EINVAL; + } + + if (li > INT16_MAX || li < INT16_MIN) { + return -ERANGE; + } + + *converted = (int16_t)li; + return 0; +} + +int common_safe_int32(const char *numstr, int32_t *converted) { + char *err = NULL; + long long int lli; + + if (numstr == NULL) { + return -EINVAL; + } + + errno = 0; + lli = strtol(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (err == NULL || err == numstr || *err != '\\0') { + return -EINVAL; + } + + if (lli > INT32_MAX || lli < INT32_MIN) { + return -ERANGE; + } + + *converted = (int32_t)lli; + return 0; +} + +int common_safe_int64(const char *numstr, int64_t *converted) { + char *err = NULL; + long long int lli; + + if (numstr == NULL) { + return -EINVAL; + } + + errno = 0; + lli = strtoll(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (err == NULL || err == numstr || *err != '\\0') { + return -EINVAL; + } + + *converted = (int64_t)lli; + return 0; +} + +int common_safe_int(const char *numstr, int *converted) { + char *err = NULL; + long long int lli; + + if (numstr == NULL) { + return -EINVAL; + } + + errno = 0; + lli = strtol(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (err == NULL || err == numstr || *err != '\\0') { + return -EINVAL; + } + + if (lli > INT_MAX || lli < INT_MIN) { + return -ERANGE; + } + + *converted = (int)lli; + return 0; +} + +char *safe_strdup(const char *src) +{ + char *dst = NULL; + + if (src == NULL) { + return NULL; + } + + dst = strdup(src); + if (dst == NULL) { + abort(); + } + + return dst; +} + + +yajl_gen_status gen_json_map_int_int(void *ctx, const json_map_int_int *map, const struct parser_context *ptx, parser_error *err) { + yajl_gen_status stat = yajl_gen_status_ok; + yajl_gen g = (yajl_gen) ctx; + size_t len = 0, i = 0; + if (map != NULL) { + len = map->len; + } + if (!len && !(ptx->options & OPT_GEN_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 0); + } + stat = yajl_gen_map_open((yajl_gen)g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + + } + for (i = 0; i < len; i++) { + char numstr[MAX_NUM_STR_LEN]; + int nret; + nret = sprintf_s(numstr, sizeof(numstr), "%lld", (long long int)map->keys[i]); + if (nret < 0) { + if (!*err && asprintf(err, "Error to print string") < 0) { + *(err) = safe_strdup("error allocating memory"); + } + return yajl_gen_in_error_state; + } + stat = yajl_gen_string((yajl_gen)g, (const unsigned char *)numstr, strlen(numstr)); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = map_int(g, map->values[i]); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + } + + stat = yajl_gen_map_close((yajl_gen)g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + if (!len && !(ptx->options & OPT_GEN_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 1); + } + return yajl_gen_status_ok; +} + +void free_json_map_int_int(json_map_int_int *map) { + if (map != NULL) { + free(map->keys); + map->keys = NULL; + free(map->values); + map->values = NULL; + free(map); + } +} +json_map_int_int *make_json_map_int_int(yajl_val src, const struct parser_context *ctx, parser_error *err) { + json_map_int_int *ret = NULL; + if (src != NULL && YAJL_GET_OBJECT(src) != NULL) { + size_t i; + size_t len = YAJL_GET_OBJECT(src)->len; + ret = safe_malloc(sizeof(*ret)); + ret->len = len; + ret->keys = safe_malloc((len + 1) * sizeof(int)); + ret->values = safe_malloc((len + 1) * sizeof(int)); + for (i = 0; i < len; i++) { + const char *srckey = YAJL_GET_OBJECT(src)->keys[i]; + yajl_val srcval = YAJL_GET_OBJECT(src)->values[i]; + + if (srckey != NULL) { + int invalid; + invalid = common_safe_int(srckey, &(ret->keys[i])); + if (invalid) { + if (*err == NULL && asprintf(err, "Invalid key '%s' with type 'int': %s", srckey, strerror(-invalid)) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_int_int(ret); + return NULL; + } + } + + if (srcval != NULL) { + int invalid; + if (!YAJL_IS_NUMBER(srcval)) { + if (*err == NULL && asprintf(err, "Invalid value with type 'int' for key '%s'", srckey) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_int_int(ret); + return NULL; + } + invalid = common_safe_int(YAJL_GET_NUMBER(srcval), &(ret->values[i])); + if (invalid) { + if (*err == NULL && asprintf(err, "Invalid value with type 'int' for key '%s': %s", srckey, strerror(-invalid)) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_int_int(ret); + return NULL; + } + } + } + } + return ret; +} +int append_json_map_int_int(json_map_int_int *map, int key, int val) { + size_t len; + int *keys = NULL; + int *vals = NULL; + + if (map == NULL) { + return -1; + } + + if ((SIZE_MAX / sizeof(int) - 1) < map->len) { + return -1; + } + + len = map->len + 1; + keys = safe_malloc(len * sizeof(int)); + vals = safe_malloc(len * sizeof(int)); + + if (map->len) { + if (memcpy_s(keys, len * sizeof(int), map->keys, map->len * sizeof(int)) != EOK) { + free(keys); + free(vals); + return -1; + } + if (memcpy_s(vals, len * sizeof(int), map->values, map->len * sizeof(int)) != EOK) { + free(keys); + free(vals); + return -1; + } + } + free(map->keys); + map->keys = keys; + free(map->values); + map->values = vals; + map->keys[map->len] = key; + map->values[map->len] = val; + + map->len++; + return 0; +} + +yajl_gen_status gen_json_map_int_bool(void *ctx, const json_map_int_bool *map, const struct parser_context *ptx, parser_error *err) { + yajl_gen_status stat = yajl_gen_status_ok; + yajl_gen g = (yajl_gen) ctx; + size_t len = 0, i = 0; + if (map != NULL) { + len = map->len; + } + if (!len && !(ptx->options & OPT_GEN_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 0); + } + stat = yajl_gen_map_open((yajl_gen)g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + + } + for (i = 0; i < len; i++) { + char numstr[MAX_NUM_STR_LEN]; + int nret; + nret = sprintf_s(numstr, sizeof(numstr), "%lld", (long long int)map->keys[i]); + if (nret < 0) { + if (!*err && asprintf(err, "Error to print string") < 0) { + *(err) = safe_strdup("error allocating memory"); + } + return yajl_gen_in_error_state; + } + stat = yajl_gen_string((yajl_gen)g, (const unsigned char *)numstr, strlen(numstr)); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = yajl_gen_bool((yajl_gen)g, (int)(map->values[i])); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + } + + stat = yajl_gen_map_close((yajl_gen)g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + if (!len && !(ptx->options & OPT_GEN_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 1); + } + return yajl_gen_status_ok; +} + +void free_json_map_int_bool(json_map_int_bool *map) { + if (map != NULL) { + size_t i; + for (i = 0; i < map->len; i++) { + // No need to free key for type int + // No need to free value for type bool + } + free(map->keys); + map->keys = NULL; + free(map->values); + map->values = NULL; + free(map); + } +} +json_map_int_bool *make_json_map_int_bool(yajl_val src, const struct parser_context *ctx, parser_error *err) { + json_map_int_bool *ret = NULL; + if (src != NULL && YAJL_GET_OBJECT(src) != NULL) { + size_t i; + size_t len = YAJL_GET_OBJECT(src)->len; + ret = safe_malloc(sizeof(*ret)); + ret->len = len; + ret->keys = safe_malloc((len + 1) * sizeof(int)); + ret->values = safe_malloc((len + 1) * sizeof(bool)); + for (i = 0; i < len; i++) { + const char *srckey = YAJL_GET_OBJECT(src)->keys[i]; + yajl_val srcval = YAJL_GET_OBJECT(src)->values[i]; + + if (srckey != NULL) { + int invalid; + invalid = common_safe_int(srckey, &(ret->keys[i])); + if (invalid) { + if (*err == NULL && asprintf(err, "Invalid key '%s' with type 'int': %s", srckey, strerror(-invalid)) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_int_bool(ret); + return NULL; + } + } + + if (srcval != NULL) { + if (YAJL_IS_TRUE(srcval)) { + ret->values[i] = true; + } else if (YAJL_IS_FALSE(srcval)) { + ret->values[i] = false; + } else { + if (*err == NULL && asprintf(err, "Invalid value with type 'bool' for key '%s'", srckey) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_int_bool(ret); + return NULL; + } + } + } + } + return ret; +} +int append_json_map_int_bool(json_map_int_bool *map, int key, bool val) { + size_t len; + int *keys = NULL; + bool *vals = NULL; + + if (map == NULL) { + return -1; + } + + if ((SIZE_MAX / sizeof(int) - 1) < map->len || (SIZE_MAX / sizeof(bool) - 1) < map->len) { + return -1; + } + + len = map->len + 1; + keys = safe_malloc(len * sizeof(int)); + vals = safe_malloc(len * sizeof(bool)); + + if (map->len) { + if (memcpy_s(keys, len * sizeof(int), map->keys, map->len * sizeof(int)) != EOK) { + free(keys); + free(vals); + return -1; + } + if (memcpy_s(vals, len * sizeof(bool), map->values, map->len * sizeof(bool)) != EOK) { + free(keys); + free(vals); + return -1; + } + } + free(map->keys); + map->keys = keys; + free(map->values); + map->values = vals; + map->keys[map->len] = key; + map->values[map->len] = val; + + map->len++; + return 0; +} + +yajl_gen_status gen_json_map_int_string(void *ctx, const json_map_int_string *map, const struct parser_context *ptx, parser_error *err) { + yajl_gen_status stat = yajl_gen_status_ok; + yajl_gen g = (yajl_gen) ctx; + size_t len = 0, i = 0; + if (map != NULL) { + len = map->len; + } + if (!len && !(ptx->options & OPT_GEN_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 0); + } + stat = yajl_gen_map_open((yajl_gen)g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + + } + for (i = 0; i < len; i++) { + char numstr[MAX_NUM_STR_LEN]; + int nret; + nret = sprintf_s(numstr, sizeof(numstr), "%lld", (long long int)map->keys[i]); + if (nret < 0) { + if (!*err && asprintf(err, "Error to print string") < 0) { + *(err) = safe_strdup("error allocating memory"); + } + return yajl_gen_in_error_state; + } + stat = yajl_gen_string((yajl_gen)g, (const unsigned char *)numstr, strlen(numstr)); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = yajl_gen_string((yajl_gen)g, (const unsigned char *)(map->values[i]), strlen(map->values[i])); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + } + + stat = yajl_gen_map_close((yajl_gen)g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + if (!len && !(ptx->options & OPT_GEN_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 1); + } + return yajl_gen_status_ok; +} + +void free_json_map_int_string(json_map_int_string *map) { + if (map != NULL) { + size_t i; + for (i = 0; i < map->len; i++) { + // No need to free key for type int + free(map->values[i]); + map->values[i] = NULL; + } + free(map->keys); + map->keys = NULL; + free(map->values); + map->values = NULL; + free(map); + } +} +json_map_int_string *make_json_map_int_string(yajl_val src, const struct parser_context *ctx, parser_error *err) { + json_map_int_string *ret = NULL; + if (src != NULL && YAJL_GET_OBJECT(src) != NULL) { + size_t i; + size_t len = YAJL_GET_OBJECT(src)->len; + ret = safe_malloc(sizeof(*ret)); + ret->len = len; + ret->keys = safe_malloc((len + 1) * sizeof(int)); + ret->values = safe_malloc((len + 1) * sizeof(char *)); + for (i = 0; i < len; i++) { + const char *srckey = YAJL_GET_OBJECT(src)->keys[i]; + yajl_val srcval = YAJL_GET_OBJECT(src)->values[i]; + + if (srckey != NULL) { + int invalid; + invalid = common_safe_int(srckey, &(ret->keys[i])); + if (invalid) { + if (*err == NULL && asprintf(err, "Invalid key '%s' with type 'int': %s", srckey, strerror(-invalid)) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_int_string(ret); + return NULL; + } + } + + if (srcval != NULL) { + if (!YAJL_IS_STRING(srcval)) { + if (*err == NULL && asprintf(err, "Invalid value with type 'string' for key '%s'", srckey) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_int_string(ret); + return NULL; + } + char *str = YAJL_GET_STRING(srcval); + ret->values[i] = safe_strdup(str ? str : ""); + } + } + } + return ret; +} +int append_json_map_int_string(json_map_int_string *map, int key, const char *val) { + size_t len; + int *keys = NULL; + char **vals = NULL; + + if (map == NULL) { + return -1; + } + + if ((SIZE_MAX / sizeof(int) - 1) < map->len || (SIZE_MAX / sizeof(char *) - 1) < map->len) { + return -1; + } + + len = map->len + 1; + keys = safe_malloc(len * sizeof(int)); + vals = safe_malloc(len * sizeof(char *)); + + if (map->len) { + if (memcpy_s(keys, len * sizeof(int), map->keys, map->len * sizeof(int)) != EOK) { + free(keys); + free(vals); + return -1; + } + if (memcpy_s(vals, len * sizeof(char *), map->values, map->len * sizeof(char *)) != EOK) { + free(keys); + free(vals); + return -1; + } + } + free(map->keys); + map->keys = keys; + free(map->values); + map->values = vals; + map->keys[map->len] = key; + map->values[map->len] = safe_strdup(val ? val : ""); + + map->len++; + return 0; +} + +yajl_gen_status gen_json_map_string_int(void *ctx, const json_map_string_int *map, const struct parser_context *ptx, parser_error *err) { + yajl_gen_status stat = yajl_gen_status_ok; + yajl_gen g = (yajl_gen) ctx; + size_t len = 0, i = 0; + if (map != NULL) { + len = map->len; + } + if (!len && !(ptx->options & OPT_GEN_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 0); + } + stat = yajl_gen_map_open((yajl_gen)g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + + } + for (i = 0; i < len; i++) { + stat = yajl_gen_string((yajl_gen)g, (const unsigned char *)(map->keys[i]), strlen(map->keys[i])); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = map_int(g, map->values[i]); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + } + + stat = yajl_gen_map_close((yajl_gen)g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + if (!len && !(ptx->options & OPT_GEN_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 1); + } + return yajl_gen_status_ok; +} + +void free_json_map_string_int(json_map_string_int *map) { + if (map != NULL) { + size_t i; + for (i = 0; i < map->len; i++) { + free(map->keys[i]); + map->keys[i] = NULL; + } + free(map->keys); + map->keys = NULL; + free(map->values); + map->values = NULL; + free(map); + } +} +json_map_string_int *make_json_map_string_int(yajl_val src, const struct parser_context *ctx, parser_error *err) { + json_map_string_int *ret = NULL; + if (src != NULL && YAJL_GET_OBJECT(src) != NULL) { + size_t i; + size_t len = YAJL_GET_OBJECT(src)->len; + ret = safe_malloc(sizeof(*ret)); + ret->len = len; + ret->keys = safe_malloc((len + 1) * sizeof(char *)); + ret->values = safe_malloc((len + 1) * sizeof(int)); + for (i = 0; i < len; i++) { + const char *srckey = YAJL_GET_OBJECT(src)->keys[i]; + yajl_val srcval = YAJL_GET_OBJECT(src)->values[i]; + ret->keys[i] = safe_strdup(srckey ? srckey : ""); + + if (srcval != NULL) { + int invalid; + if (!YAJL_IS_NUMBER(srcval)) { + if (*err == NULL && asprintf(err, "Invalid value with type 'int' for key '%s'", srckey) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_string_int(ret); + return NULL; + } + invalid = common_safe_int(YAJL_GET_NUMBER(srcval), &(ret->values[i])); + if (invalid) { + if (*err == NULL && asprintf(err, "Invalid value with type 'int' for key '%s': %s", srckey, strerror(-invalid)) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_string_int(ret); + return NULL; + } + } + } + } + return ret; +} +int append_json_map_string_int(json_map_string_int *map, const char *key, int val) { + size_t len; + char **keys = NULL; + int *vals = NULL; + + if (map == NULL) { + return -1; + } + + if ((SIZE_MAX / sizeof(char *) - 1) < map->len || (SIZE_MAX / sizeof(int) - 1) < map->len) { + return -1; + } + + len = map->len + 1; + keys = safe_malloc(len * sizeof(char *)); + vals = safe_malloc(len * sizeof(int)); + + if (map->len) { + if (memcpy_s(keys, len * sizeof(char *), map->keys, map->len * sizeof(char *)) != EOK) { + free(keys); + free(vals); + return -1; + } + if (memcpy_s(vals, len * sizeof(int), map->values, map->len * sizeof(int)) != EOK) { + free(keys); + free(vals); + return -1; + } + } + free(map->keys); + map->keys = keys; + free(map->values); + map->values = vals; + map->keys[map->len] = safe_strdup(key ? key : ""); + map->values[map->len] = val; + + map->len++; + return 0; +} + +yajl_gen_status gen_json_map_string_bool(void *ctx, const json_map_string_bool *map, const struct parser_context *ptx, parser_error *err) { + yajl_gen_status stat = yajl_gen_status_ok; + yajl_gen g = (yajl_gen) ctx; + size_t len = 0, i = 0; + if (map != NULL) { + len = map->len; + } + if (!len && !(ptx->options & OPT_GEN_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 0); + } + stat = yajl_gen_map_open((yajl_gen)g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + + } + for (i = 0; i < len; i++) { + stat = yajl_gen_string((yajl_gen)g, (const unsigned char *)(map->keys[i]), strlen(map->keys[i])); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = yajl_gen_bool((yajl_gen)g, (int)(map->values[i])); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + } + + stat = yajl_gen_map_close((yajl_gen)g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + if (!len && !(ptx->options & OPT_GEN_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 1); + } + return yajl_gen_status_ok; +} + +void free_json_map_string_bool(json_map_string_bool *map) { + if (map != NULL) { + size_t i; + for (i = 0; i < map->len; i++) { + free(map->keys[i]); + map->keys[i] = NULL; + // No need to free value for type bool + } + free(map->keys); + map->keys = NULL; + free(map->values); + map->values = NULL; + free(map); + } +} +json_map_string_bool *make_json_map_string_bool(yajl_val src, const struct parser_context *ctx, parser_error *err) { + json_map_string_bool *ret = NULL; + if (src != NULL && YAJL_GET_OBJECT(src) != NULL) { + size_t i; + size_t len = YAJL_GET_OBJECT(src)->len; + ret = safe_malloc(sizeof(*ret)); + ret->len = len; + ret->keys = safe_malloc((len + 1) * sizeof(char *)); + ret->values = safe_malloc((len + 1) * sizeof(bool)); + for (i = 0; i < len; i++) { + const char *srckey = YAJL_GET_OBJECT(src)->keys[i]; + yajl_val srcval = YAJL_GET_OBJECT(src)->values[i]; + ret->keys[i] = safe_strdup(srckey ? srckey : ""); + + if (srcval != NULL) { + if (YAJL_IS_TRUE(srcval)) { + ret->values[i] = true; + } else if (YAJL_IS_FALSE(srcval)) { + ret->values[i] = false; + } else { + if (*err == NULL && asprintf(err, "Invalid value with type 'bool' for key '%s'", srckey) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_string_bool(ret); + return NULL; + } + } + } + } + return ret; +} +int append_json_map_string_bool(json_map_string_bool *map, const char *key, bool val) { + size_t len; + char **keys = NULL; + bool *vals = NULL; + + if (map == NULL) { + return -1; + } + + if ((SIZE_MAX / sizeof(char *) - 1) < map->len || (SIZE_MAX / sizeof(bool) - 1) < map->len) { + return -1; + } + + len = map->len + 1; + keys = safe_malloc(len * sizeof(char *)); + vals = safe_malloc(len * sizeof(bool)); + + if (map->len) { + if (memcpy_s(keys, len * sizeof(char *), map->keys, map->len * sizeof(char *)) != EOK) { + free(keys); + free(vals); + return -1; + } + if (memcpy_s(vals, len * sizeof(bool), map->values, map->len * sizeof(bool)) != EOK) { + free(keys); + free(vals); + return -1; + } + } + free(map->keys); + map->keys = keys; + free(map->values); + map->values = vals; + map->keys[map->len] = safe_strdup(key ? key : ""); + map->values[map->len] = val; + + map->len++; + return 0; +} + +yajl_gen_status gen_json_map_string_string(void *ctx, const json_map_string_string *map, const struct parser_context *ptx, parser_error *err) { + yajl_gen_status stat = yajl_gen_status_ok; + yajl_gen g = (yajl_gen) ctx; + size_t len = 0, i = 0; + if (map != NULL) { + len = map->len; + } + if (!len && !(ptx->options & OPT_GEN_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 0); + } + stat = yajl_gen_map_open((yajl_gen)g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + + } + for (i = 0; i < len; i++) { + stat = yajl_gen_string((yajl_gen)g, (const unsigned char *)(map->keys[i]), strlen(map->keys[i])); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = yajl_gen_string((yajl_gen)g, (const unsigned char *)(map->values[i]), strlen(map->values[i])); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + } + + stat = yajl_gen_map_close((yajl_gen)g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + if (!len && !(ptx->options & OPT_GEN_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 1); + } + return yajl_gen_status_ok; +} + +void free_json_map_string_string(json_map_string_string *map) { + if (map != NULL) { + size_t i; + for (i = 0; i < map->len; i++) { + free(map->keys[i]); + map->keys[i] = NULL; + free(map->values[i]); + map->values[i] = NULL; + } + free(map->keys); + map->keys = NULL; + free(map->values); + map->values = NULL; + free(map); + } +} +json_map_string_string *make_json_map_string_string(yajl_val src, const struct parser_context *ctx, parser_error *err) { + json_map_string_string *ret = NULL; + if (src != NULL && YAJL_GET_OBJECT(src) != NULL) { + size_t i; + size_t len = YAJL_GET_OBJECT(src)->len; + ret = safe_malloc(sizeof(*ret)); + ret->len = len; + ret->keys = safe_malloc((len + 1) * sizeof(char *)); + ret->values = safe_malloc((len + 1) * sizeof(char *)); + for (i = 0; i < len; i++) { + const char *srckey = YAJL_GET_OBJECT(src)->keys[i]; + yajl_val srcval = YAJL_GET_OBJECT(src)->values[i]; + ret->keys[i] = safe_strdup(srckey ? srckey : ""); + + if (srcval != NULL) { + if (!YAJL_IS_STRING(srcval)) { + if (*err == NULL && asprintf(err, "Invalid value with type 'string' for key '%s'", srckey) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_string_string(ret); + return NULL; + } + char *str = YAJL_GET_STRING(srcval); + ret->values[i] = safe_strdup(str ? str : ""); + } + } + } + return ret; +} +int append_json_map_string_string(json_map_string_string *map, const char *key, const char *val) { + size_t len, i; + char **keys = NULL; + char **vals = NULL; + + if (map == NULL) { + return -1; + } + + for (i = 0; i < map->len; i++) { + if (strcmp(map->keys[i], key) == 0) { + free(map->values[i]); + map->values[i] = safe_strdup(val ? val : ""); + return 0; + } + } + + if ((SIZE_MAX / sizeof(char *) - 1) < map->len) { + return -1; + } + + len = map->len + 1; + keys = safe_malloc(len * sizeof(char *)); + vals = safe_malloc(len * sizeof(char *)); + + if (map->len) { + if (memcpy_s(keys, len * sizeof(char *), map->keys, map->len * sizeof(char *)) != EOK) { + free(keys); + free(vals); + return -1; + } + if (memcpy_s(vals, len * sizeof(char *), map->values, map->len * sizeof(char *)) != EOK) { + free(keys); + free(vals); + return -1; + } + } + free(map->keys); + map->keys = keys; + free(map->values); + map->values = vals; + map->keys[map->len] = safe_strdup(key ? key : ""); + map->values[map->len] = safe_strdup(val ? val : ""); + + map->len++; + return 0; +} + +char *json_marshal_string(const char *str, size_t strlen, const struct parser_context *ctx, parser_error *err) +{ + yajl_gen g = NULL; + struct parser_context tmp_ctx = { 0 }; + const unsigned char *gen_buf = NULL; + char *json_buf = NULL; + size_t gen_len = 0; + yajl_gen_status stat; + + if (str == NULL || err == NULL) + return NULL; + + *err = NULL; + if (ctx == NULL) { + ctx = (const struct parser_context *)(&tmp_ctx); + } + + if (!json_gen_init(&g, ctx)) { + *err = safe_strdup("Json_gen init failed"); + goto out; + } + stat = yajl_gen_string((yajl_gen)g, (const unsigned char *)str, strlen); + if (yajl_gen_status_ok != stat) { + if (asprintf(err, "error generating json, errcode: %d", (int)stat) < 0) { + *err = safe_strdup("error allocating memory"); + } + goto free_out; + } + yajl_gen_get_buf(g, &gen_buf, &gen_len); + if (gen_buf == NULL) { + *err = safe_strdup("Error to get generated json"); + goto free_out; + } + + json_buf = safe_malloc(gen_len + 1); + if (memcpy_s(json_buf, gen_len + 1, gen_buf, gen_len) != EOK) { + *err = safe_strdup("Error to memcpy json"); + free(json_buf); + json_buf = NULL; + goto free_out; + } + json_buf[gen_len] = '\\0'; + +free_out: + yajl_gen_clear(g); + yajl_gen_free(g); +out: + return json_buf; +} + +''' diff --git a/src/json/schema/src/common_h.py b/src/json/schema/src/common_h.py new file mode 100644 index 0000000..7aba064 --- /dev/null +++ b/src/json/schema/src/common_h.py @@ -0,0 +1,198 @@ +# -*- coding: utf-8 -*- +''' +Description: commom header file +Interface: None +History: 2019-06-17 +''' +# - Copyright (C) Huawei Technologies Co., Ltd. 2019. All rights reserved. +# - clibcni licensed under the Mulan PSL v1. +# - You can use this software according to the terms and conditions of the Mulan PSL v1. +# - You may obtain a copy of Mulan PSL v1 at: +# - http://license.coscl.org.cn/MulanPSL +# - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR +# - IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR +# - PURPOSE. +# - See the Mulan PSL v1 for more details. +# - Description: generate json +# - Author: tanyifeng +# - Create: 2019-04-25 +#!/usr/bin/python -Es + +""" +Description: json common c code +Interface: None +History: 2019-06-18 +Purpose: defined the common tool function for parse json +Defined the CODE global variable to hold the c code +""" +# - Defined the CODE global variable to hold the c code +CODE = '''// Auto generated file. Do not edit! +# ifndef _JSON_COMMON_H +# define _JSON_COMMON_H + +# include +# include +# include +# include +# include +# include +# include +# include "securec.h" + +# ifdef __cplusplus +extern "C" { +# endif + +# undef linux + +//options to report error if there is unknown key found in json +# define OPT_PARSE_STRICT 0x01 +//options to generate all key and value +# define OPT_GEN_KAY_VALUE 0x02 +//options to generate simplify(no indent) json string +# define OPT_GEN_SIMPLIFY 0x04 +//options not to validate utf8 data +# define OPT_GEN_NO_VALIDATE_UTF8 0x08 + +# define GEN_SET_ERROR_AND_RETURN(stat, err) { \\ + if (*(err) == NULL) {\\ + if (asprintf(err, "%s: %s: %d: error generating json, errcode: %u", __FILE__, __func__, __LINE__, stat) < 0) { \\ + *(err) = safe_strdup("error allocating memory"); \\ + } \\ + }\\ + return stat; \\ +} + +typedef char *parser_error; + +struct parser_context { + unsigned int options; + FILE *stderr; +}; + +yajl_gen_status map_uint(void *ctx, long long unsigned int num); + +yajl_gen_status map_int(void *ctx, long long int num); + +bool json_gen_init(yajl_gen *g, const struct parser_context *ctx); + +yajl_val get_val(yajl_val tree, const char *name, yajl_type type); + +void *safe_malloc(size_t size); + +int common_safe_double(const char *numstr, double *converted); + +int common_safe_uint8(const char *numstr, uint8_t *converted); + +int common_safe_uint16(const char *numstr, uint16_t *converted); + +int common_safe_uint32(const char *numstr, uint32_t *converted); + +int common_safe_uint64(const char *numstr, uint64_t *converted); + +int common_safe_uint(const char *numstr, unsigned int *converted); + +int common_safe_int8(const char *numstr, int8_t *converted); + +int common_safe_int16(const char *numstr, int16_t *converted); + +int common_safe_int32(const char *numstr, int32_t *converted); + +int common_safe_int64(const char *numstr, int64_t *converted); + +int common_safe_int(const char *numstr, int *converted); + +char *safe_strdup(const char *src); + +typedef struct { + int *keys; + int *values; + size_t len; +} json_map_int_int; + +void free_json_map_int_int(json_map_int_int *map); + +json_map_int_int *make_json_map_int_int(yajl_val src, const struct parser_context *ctx, parser_error *err); + +yajl_gen_status gen_json_map_int_int(void *ctx, const json_map_int_int *map, const struct parser_context *ptx, parser_error *err); + +int append_json_map_int_int(json_map_int_int *map, int key, int val); + +typedef struct { + int *keys; + bool *values; + size_t len; +} json_map_int_bool; + +void free_json_map_int_bool(json_map_int_bool *map); + +json_map_int_bool *make_json_map_int_bool(yajl_val src, const struct parser_context *ctx, parser_error *err); + +yajl_gen_status gen_json_map_int_bool(void *ctx, const json_map_int_bool *map, const struct parser_context *ptx, parser_error *err); + +int append_json_map_int_bool(json_map_int_bool *map, int key, bool val); + +typedef struct { + int *keys; + char **values; + size_t len; +} json_map_int_string; + +void free_json_map_int_string(json_map_int_string *map); + +json_map_int_string *make_json_map_int_string(yajl_val src, const struct parser_context *ctx, parser_error *err); + +yajl_gen_status gen_json_map_int_string(void *ctx, const json_map_int_string *map, const struct parser_context *ptx, parser_error *err); + +int append_json_map_int_string(json_map_int_string *map, int key, const char *val); + +typedef struct { + char **keys; + int *values; + size_t len; +} json_map_string_int; + +void free_json_map_string_int(json_map_string_int *map); + +json_map_string_int *make_json_map_string_int(yajl_val src, const struct parser_context *ctx, parser_error *err); + +yajl_gen_status gen_json_map_string_int(void *ctx, const json_map_string_int *map, const struct parser_context *ptx, parser_error *err); + +int append_json_map_string_int(json_map_string_int *map, const char *key, int val); + +typedef struct { + char **keys; + bool *values; + size_t len; +} json_map_string_bool; + +void free_json_map_string_bool(json_map_string_bool *map); + +json_map_string_bool *make_json_map_string_bool(yajl_val src, const struct parser_context *ctx, parser_error *err); + +yajl_gen_status gen_json_map_string_bool(void *ctx, const json_map_string_bool *map, const struct parser_context *ptx, parser_error *err); + +int append_json_map_string_bool(json_map_string_bool *map, const char *key, bool val); + +typedef struct { + char **keys; + char **values; + size_t len; +} json_map_string_string; + +void free_json_map_string_string(json_map_string_string *map); + +json_map_string_string *make_json_map_string_string(yajl_val src, const struct parser_context *ctx, parser_error *err); + +yajl_gen_status gen_json_map_string_string(void *ctx, const json_map_string_string *map, const struct parser_context *ptx, parser_error *err); + +int append_json_map_string_string(json_map_string_string *map, const char *key, const char *val); + +char *json_marshal_string(const char *str, size_t strlen, const struct parser_context *ctx, parser_error *err); + +# ifdef __cplusplus +} +# endif + +# endif +''' diff --git a/src/json/schema/src/generate.py b/src/json/schema/src/generate.py new file mode 100644 index 0000000..7f57679 --- /dev/null +++ b/src/json/schema/src/generate.py @@ -0,0 +1,802 @@ +# -*- coding: utf-8 -*- +''' +Description: header class and functions +Interface: None +History: 2019-06-17 +''' +# - Copyright (C) Huawei Technologies Co., Ltd. 2019. All rights reserved. +# - clibcni licensed under the Mulan PSL v1. +# - You can use this software according to the terms and conditions of the Mulan PSL v1. +# - You may obtain a copy of Mulan PSL v1 at: +# - http://license.coscl.org.cn/MulanPSL +# - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR +# - IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR +# - PURPOSE. +# - See the Mulan PSL v1 for more details. +# - Description: generate json +# - Author: tanyifeng +# - Create: 2019-04-25 +#!/usr/bin/python -Es + +import traceback +import os +import sys +import json +import fcntl +import argparse + +from collections import OrderedDict +import helpers +import headers +import sources +import common_h +import common_c + +# - json suffix +JSON_SUFFIX = ".json" + +''' +Description: ref suffix +Interface: ref_suffix +History: 2019-06-17 +''' +# - Description: ref suffix +REF_SUFFIX = "_json" + +''' +Description: root paths +Interface: rootpaths +History: 2019-06-17 +''' +class MyRoot(object): + ''' + Description: Store schema information + Interface: None + History: 2019-06-17 + ''' + def __init__(self, root_path): + self.root_path = root_path + + def get_repr(self): + ''' + Description: Store schema information + Interface: None + History: 2019-06-17 + ''' + return "{root_path:(%s)}" % (self.root_path) + + def get_path(self): + ''' + Description: Store schema information + Interface: None + History: 2019-06-17 + ''' + return self.root_path + + +def trimJsonSuffix(name): + """ + Description: generate c language for parse json map string object + Interface: None + History: 2019-06-17 + """ + if name.endswith(JSON_SUFFIX) or name.endswith(REF_SUFFIX): + name = name[:-len(JSON_SUFFIX)] + return helpers.convertToCStyle(name.replace('.', '_').replace('-', '_')) + + +def getPrefixPackage(filepath, rootpath): + """ + Description: generate c language for parse json map string object + Interface: None + History: 2019-06-17 + """ + realpath = os.path.realpath(filepath) + + if realpath.startswith(rootpath) and len(realpath) > len(rootpath): + return helpers.convertToCStyle(os.path.dirname(realpath)[(len(rootpath) + 1):]) + else: + raise RuntimeError('schema path \"%s\" is not in scope of root path \"%s\"' \ + % (realpath, rootpath)) + + +def getPrefixFromFile(filepath): + """ + Description: generate c language for parse json map string object + Interface: None + History: 2019-06-17 + """ + prefix_file = trimJsonSuffix(os.path.basename(filepath)) + root_path = MyRoot.root_path + prefix_package = getPrefixPackage(filepath, root_path) + prefix = prefix_file if prefix_package == "" else prefix_package + "_" + prefix_file + return prefix + +def schemaFromFile(filepath, srcpath): + """ + Description: generate c language for parse json map string object + Interface: None + History: 2019-06-17 + """ + schemapath = helpers.FilePath(filepath) + prefix = getPrefixFromFile(schemapath.name) + header = helpers.FilePath(os.path.join(srcpath, prefix + ".h")) + source = helpers.FilePath(os.path.join(srcpath, prefix + ".c")) + schema_info = helpers.SchemaInfo(schemapath, header, source, prefix, srcpath) + return schema_info + +def makeRefName(refname, reffile): + """ + Description: generate c language for parse json map string object + Interface: None + History: 2019-06-17 + """ + prefix = getPrefixFromFile(reffile) + if refname == "" or prefix.endswith(refname): + return prefix + return prefix + "_" + helpers.convertToCStyle(refname) + +def splitRefName(ref): + """ + Description: generate c language for parse json map string object + Interface: None + History: 2019-06-17 + """ + tmp_f, tmp_r = ref.split("#/") if '#/' in ref else (ref, "") + return tmp_f, tmp_r + +def merge(children): + """ + Description: generate c language for parse json map string object + Interface: None + History: 2019-06-17 + """ + subchildren = [] + for i in children: + for j in i.children: + subchildren.append(j) + + return subchildren + +# BASIC_TYPES include all basic types +BASIC_TYPES = ( + "byte", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "UID", "GID", + "bytePointer", "doublePointer", "int8Pointer", "int16Pointer", "int32Pointer", "int64Pointer", + "uint8Pointer", "uint16Pointer", "uint32Pointer", "uint64Pointer", "ArrayOfStrings", + "booleanPointer" +) + +def judgeSupportedType(typ): + """ + Description: generate c language for parse json map string object + Interface: None + History: 2019-06-17 + """ + return typ in ("integer", "boolean", "string", "double") or typ in BASIC_TYPES + +def getRefSubref(src, subref): + """ + Description: generate c language for parse json map string object + Interface: None + History: 2019-06-17 + """ + cur = src + subrefname = "" + for j in subref.split('/'): + subrefname = j + if j in BASIC_TYPES: + return src, {"type": j}, subrefname + cur = cur[j] + + return src, cur, subrefname + +def getRefRoot(schema_info, src, ref, curfile): + """ + Description: generate c language for parse json map string object + Interface: None + History: 2019-06-17 + """ + refname = "" + tmp_f, tmp_r = splitRefName(ref) + + if tmp_f == "": + cur = src + else: + realpath = os.path.realpath(os.path.join(os.path.dirname(curfile), tmp_f)) + curfile = realpath + + subschema = schemaFromFile(realpath, schema_info.filesdir) + if schema_info.refs is None: + schema_info.refs = {} + schema_info.refs[subschema.header.basename] = subschema + with open(realpath) as i: + cur = src = json.loads(i.read()) + subcur = cur + if tmp_r != "": + src, subcur, refname = getRefSubref(src, tmp_r) + + if 'type' not in subcur and '$ref' in subcur: + subf, subr = splitRefName(subcur['$ref']) + if subf == "": + src, subcur, refname = getRefSubref(src, subr) + if 'type' not in subcur: + raise RuntimeError("Not support reference of nesting more than 2 level: ", ref) + else: + return getRefRoot(schema_info, src, subcur['$ref'], curfile) + return src, subcur, curfile, makeRefName(refname, curfile) + +def getTypePatternInCur(cur, schema_info, src, curfile): + """ + Description: generate c language for parse json map string object + Interface: None + History: 2019-06-17 + """ + # pattern of key: + # '.{1,}' represents type 'string', + # '.{2,}' represents type 'integer' + if '.{2,}' in cur['patternProperties']: + map_key = 'Int' + else: + map_key = 'String' + for i, value in enumerate(cur['patternProperties'].values()): + # only use the first value + if i == 0: + if 'type' in value: + val = value["type"] + else: + dummy_subsrc, subcur, dummy_subcurfile, dummy_subrefname = getRefRoot( + schema_info, src, value['$ref'], curfile) + val = subcur['type'] + break + + mapKey = { + 'object': 'Object', + 'string': 'String', + 'integer': 'Int', + 'boolean': 'Bool' + }[val] + map_val = mapKey + + typ = 'map' + map_key + map_val + return typ + +class GenerateNodeInfo(object): + ''' + Description: Store schema information + Interface: None + History: 2019-06-17 + ''' + def __init__(self, schema_info, name, cur, curfile): + self.schema_info = schema_info + self.name = name + self.cur = cur + self.curfile = curfile + + def get_repr(self): + ''' + Description: Store schema information + Interface: None + History: 2019-06-17 + ''' + return "{schema_info:(%s) name:(%s) cur:(%s) curfile:(%s)}" \ + % (self.schema_info, self.name, self.cur, self.curfile) + + def get_name(self): + ''' + Description: Store schema information + Interface: None + History: 2019-06-17 + ''' + return self.name + +def generateAllofArrayTypNode(node_info, src, typ, refname): + """ + Description: generate c language for parse json map string object + Interface: None + History: 2019-06-17 + """ + schema_info = node_info.schema_info + name = node_info.name + cur = node_info.cur + curfile = node_info.curfile + subtyp = None + subtypobj = None + required = None + children = merge(parseList(schema_info, name, src, cur["items"]['allOf'], curfile)) + subtyp = children[0].typ + subtypobj = children + return helpers.Unite(name, + typ, + children, + subtyp=subtyp, + subtypobj=subtypobj, + subtypname=refname, + required=required), src + +def generateAnyofArrayTypNode(node_info, src, typ, refname): + """ + Description: generate c language for parse json map string object + Interface: None + History: 2019-06-17 + """ + schema_info = node_info.schema_info + name = node_info.name + cur = node_info.cur + curfile = node_info.curfile + subtyp = None + subtypobj = None + required = None + anychildren = parseList(schema_info, name, src, cur["items"]['anyOf'], curfile) + subtyp = anychildren[0].typ + children = anychildren[0].children + subtypobj = children + refname = anychildren[0].subtypname + return helpers.Unite(name, + typ, + children, + subtyp=subtyp, + subtypobj=subtypobj, + subtypname=refname, + required=required), src + +def generateRefArrayTypNode(node_info, src, typ, refname): + """ + Description: generate c language for parse json map string object + Interface: None + History: 2019-06-17 + """ + schema_info = node_info.schema_info + name = node_info.name + cur = node_info.cur + curfile = node_info.curfile + + item_type, src = resolveType(schema_info, name, src, cur["items"], curfile) + ref_file, subref = splitRefName(cur['items']['$ref']) + if ref_file == "": + src, dummy_subcur, subrefname = getRefSubref(src, subref) + refname = makeRefName(subrefname, curfile) + else: + refname = item_type.subtypname + return helpers.Unite(name, + typ, + None, + subtyp=item_type.typ, + subtypobj=item_type.children, + subtypname=refname, + required=item_type.required), src + +def generateTypeArrayTypNode(node_info, src, typ, refname): + """ + Description: generate c language for parse json map string object + Interface: None + History: 2019-06-17 + """ + schema_info = node_info.schema_info + name = node_info.name + cur = node_info.cur + curfile = node_info.curfile + + item_type, src = resolveType(schema_info, name, src, cur["items"], curfile) + return helpers.Unite(name, + typ, + None, + subtyp=item_type.typ, + subtypobj=item_type.children, + subtypname=refname, + required=item_type.required), src + + +def generateArrayTypNode(node_info, src, typ, refname): + """ + Description: generate c language for parse json map string object + Interface: None + History: 2019-06-17 + """ + cur = node_info.cur + + if 'allOf' in cur["items"]: + return generateAllofArrayTypNode(node_info, src, typ, refname) + elif 'anyOf' in cur["items"]: + return generateAnyofArrayTypNode(node_info, src, typ, refname) + elif '$ref' in cur["items"]: + return generateRefArrayTypNode(node_info, src, typ, refname) + elif 'type' in cur["items"]: + return generateTypeArrayTypNode(node_info, src, typ, refname) + return None + +def generateObjTypNode(node_info, src, typ, refname): + """ + Description: generate c language for parse json map string object + Interface: None + History: 2019-06-17 + """ + schema_info = node_info.schema_info + name = node_info.name + cur = node_info.cur + curfile = node_info.curfile + children = None + subtyp = None + subtypobj = None + required = None + + if 'allOf' in cur: + children = merge(parseList(schema_info, name, src, cur['allOf'], curfile)) + elif 'anyOf' in cur: + children = parseList(schema_info, name, src, cur['anyOf'], curfile) + elif 'patternProperties' in cur: + children = parseProperties(schema_info, name, src, cur, curfile) + children[0].name = children[0].name.replace('_{1,}', 'element').replace('_{2,}', \ + 'element') + children[0].fixname = "values" + if helpers.validBasicMapName(children[0].typ): + children[0].name = helpers.makeBasicMapName(children[0].typ) + else: + children = parseProperties(schema_info, name, src, cur, curfile) \ + if 'properties' in cur else None + if 'required' in cur: + required = cur['required'] + return helpers.Unite(name,\ + typ,\ + children,\ + subtyp=subtyp,\ + subtypobj=subtypobj,\ + subtypname=refname,\ + required=required), src + +def getTypNotOneof(schema_info, src, cur, curfile): + """ + Description: generate c language for parse json map string object + Interface: None + History: 2019-06-17 + """ + if 'patternProperties' in cur: + typ = getTypePatternInCur(cur, schema_info, src, curfile) + elif "type" in cur: + typ = cur["type"] + else: + typ = "object" + + return typ + + +def resolveType(schema_info, name, src, cur, curfile): + """ + Description: generate c language for parse json map string object + Interface: None + History: 2019-06-17 + """ + children = None + subtyp = None + subtypobj = None + required = None + refname = None + + if '$ref' in cur: + src, cur, curfile, refname = getRefRoot(schema_info, src, cur['$ref'], curfile) + + if "oneOf" in cur: + cur = cur['oneOf'][0] + if '$ref' in cur: + return resolveType(schema_info, name, src, cur, curfile) + else: + typ = cur['type'] + else: + typ = getTypNotOneof(schema_info, src, cur, curfile) + + node_info = GenerateNodeInfo(schema_info, name, cur, curfile) + + if helpers.validBasicMapName(typ): + pass + elif typ == 'array': + return generateArrayTypNode(node_info, src, typ, refname) + elif typ == 'object' or typ == 'mapStringObject': + return generateObjTypNode(node_info, src, typ, refname) + elif typ == 'ArrayOfStrings': + typ = 'array' + subtyp = 'string' + children = subtypobj = None + else: + if not judgeSupportedType(typ): + raise RuntimeError("Invalid schema type: %s" % typ) + children = None + + return helpers.Unite(name, + typ, + children, + subtyp=subtyp, + subtypobj=subtypobj, + subtypname=refname, + required=required), src + + +def parseList(schema_info, name, schema, objs, curfile): + """ + Description: generate c language for parse json map string object + Interface: None + History: 2019-06-17 + """ + obj = [] + index = 0 + for i in objs: + generated_name = helpers.CombinationName( \ + i['$ref'].split("/")[-1]) if '$ref' in i else helpers.CombinationName(name.name + str(index)) + node, _ = resolveType(schema_info, generated_name, schema, i, curfile) + if node: + obj.append(node) + index += 1 + if not obj: + obj = None + return obj + + +def parseDictionary(schema_info, name, schema, objs, curfile): + """ + Description: generate c language for parse json map string object + Interface: None + History: 2019-06-17 + """ + obj = [] + for i in objs: + node, _ = resolveType(schema_info, name.append(i), schema, objs[i], curfile) + if node: + obj.append(node) + if not obj: + obj = None + return obj + + +def parseProperties(schema_info, name, schema, props, curfile): + """ + Description: generate c language for parse json map string object + Interface: None + History: 2019-06-17 + """ + if 'definitions' in props: + return parseDictionary(schema_info, name, schema, props['definitions'], curfile) + if 'patternProperties' in props: + return parseDictionary(schema_info, name, schema, props['patternProperties'], curfile) + return parseDictionary(schema_info, name, schema, props['properties'], curfile) + +def handleTypeNotInSchema(schema_info, schema, prefix): + """ + Description: generate c language for parse json map string object + Interface: None + History: 2019-06-17 + """ + required = None + if 'definitions' in schema: + return helpers.Unite( \ + helpers.CombinationName("definitions"), 'definitions', \ + parseProperties(schema_info, helpers.CombinationName(""), schema, schema, \ + schema_info.name.name), None, None, None, None) + else: + if len(schema) > 1: + print('More than one element found in schema') + return None + value_nodes = [] + for value in schema: + if 'required' in schema[value]: + required = schema[value]['required'] + childrens = parseProperties(schema_info, helpers.CombinationName(""), schema[value], \ + schema[value], schema_info.name.name) + value_node = helpers.Unite(helpers.CombinationName(prefix), 'object', childrens, None, None, \ + None, required) + value_nodes.append(value_node) + return helpers.Unite(helpers.CombinationName("definitions"), 'definitions', value_nodes, None, None, \ + None, None) + +def parseSchema(schema_info, schema, prefix): + """ + Description: generate c language for parse json map string object + Interface: None + History: 2019-06-17 + """ + required = None + if 'type' not in schema: + return handleTypeNotInSchema(schema_info, schema, prefix) + + if 'type' not in schema: + print("No 'type' defined in schema") + return prefix, None + + if 'object' in schema['type']: + if 'required' in schema: + required = schema['required'] + return helpers.Unite( + helpers.CombinationName(prefix), 'object', + parseProperties(schema_info, helpers.CombinationName(""), schema, schema, schema_info.name.name), \ + None, None, None, required) + elif 'array' in schema['type']: + item_type, _ = resolveType(schema_info, helpers.CombinationName(""), schema['items'], \ + schema['items'], schema_info.name.name) + return helpers.Unite(helpers.CombinationName(prefix), 'array', None, item_type.typ, \ + item_type.children, None, item_type.required) + else: + print("Not supported type '%s'") % schema['type'] + return prefix, None + + +def expand(tree, structs, visited): + """ + Description: generate c language for parse json map string object + Interface: None + History: 2019-06-17 + """ + if tree.children is not None: + for i in tree.children: + if tree.subtypname: + i.subtypname = "from_ref" + expand(i, structs, visited=visited) + if tree.subtypobj is not None: + for i in tree.subtypobj: + expand(i, structs, visited=visited) + + if tree.typ == 'array' and helpers.validBasicMapName(tree.subtyp): + name = helpers.CombinationName(tree.name + "_element") + node = helpers.Unite(name, tree.subtyp, None) + expand(node, structs, visited) + + id_ = "%s:%s" % (tree.name, tree.typ) + if id_ not in visited.keys(): + structs.append(tree) + visited[id_] = tree + + return structs + + +def reflection(schema_info, gen_ref): + """ + Description: generate c language for parse json map string object + Interface: None + History: 2019-06-17 + """ + with open(schema_info.header.name, "w") as \ + header_file, open(schema_info.source.name, "w") as source_file: + fcntl.flock(header_file, fcntl.LOCK_EX) + fcntl.flock(source_file, fcntl.LOCK_EX) + + with open(schema_info.name.name) as schema_file: + schema_json = json.loads(schema_file.read(), object_pairs_hook=OrderedDict) + try: + tree = parseSchema(schema_info, schema_json, schema_info.prefix) + if tree is None: + print("Failed parse schema") + sys.exit(1) + structs = expand(tree, [], {}) + headers.headerReflection(structs, schema_info, header_file) + sources.sourceReflection(structs, schema_info, source_file, tree.typ) + except RuntimeError: + traceback.print_exc() + print("Failed to parse schema file: %s") % schema_info.name.name + sys.exit(1) + finally: + pass + + fcntl.flock(source_file, fcntl.LOCK_UN) + fcntl.flock(header_file, fcntl.LOCK_UN) + + if gen_ref is True: + if schema_info.refs: + for reffile in schema_info.refs.values(): + reflection(reffile, True) + + +def generateCommonFiles(out): + """ + Description: generate c language for parse json map string object + Interface: None + History: 2019-06-17 + """ + print(out, " gao\n") + with open(os.path.join(out, 'json_common.h'), "w") as \ + header_file, open(os.path.join(out, 'json_common.c'), "w") as source_file: + fcntl.flock(header_file, fcntl.LOCK_EX) + fcntl.flock(source_file, fcntl.LOCK_EX) + + header_file.write(common_h.CODE) + source_file.write(common_c.CODE) + + fcntl.flock(source_file, fcntl.LOCK_UN) + fcntl.flock(header_file, fcntl.LOCK_UN) + +def handlerSingleFile(args, srcpath, gen_ref, schemapath): + """ + Description: generate c language for parse json map string object + Interface: None + History: 2019-06-17 + """ + if not os.path.exists(schemapath.name) or not os.path.exists(srcpath.name): + print('Path %s is not exist') % schemapath.name + sys.exit(1) + + if os.path.isdir(schemapath.name): + if args.recursive is True: + # recursively parse schema + for dirpath, dummy_dirnames, files in os.walk(schemapath.name): + for target_file in files: + if target_file.endswith(JSON_SUFFIX): + schema_info = schemaFromFile(os.path.join(dirpath, target_file), \ + srcpath.name) + reflection(schema_info, gen_ref) + else: + # only parse files in current direcotory + for target_file in os.listdir(schemapath.name): + fullpath = os.path.join(schemapath.name, target_file) + if fullpath.endswith(JSON_SUFFIX) and os.path.isfile(fullpath): + schema_info = schemaFromFile(fullpath, srcpath.name) + reflection(schema_info, gen_ref) + else: + if schemapath.name.endswith(JSON_SUFFIX): + schema_info = schemaFromFile(schemapath.name, srcpath.name) + reflection(schema_info, gen_ref) + else: + print('File %s is not ends with .json') % schemapath.name + + +def handlerFiles(args, srcpath): + """ + Description: generate c language for parse json map string object + Interface: None + History: 2019-06-17 + """ + for path in args.path: + gen_ref = args.gen_ref + schemapath = helpers.FilePath(path) + handlerSingleFile(args, srcpath, gen_ref, schemapath) + +def main(): + """ + Description: generate c language for parse json map string object + Interface: None + History: 2019-06-17 + """ + parser = argparse.ArgumentParser(prog='generate.py', + usage='%(prog)s [options] path [path ...]', + description='Generate C header and source from json-schema') + parser.add_argument('path', nargs='+', help='File or directory to parse') + parser.add_argument( + '--root', + required=True, + help= + 'All schema files must be placed in root directory or sub-directory of root," \ + " and naming of C variables is started from this path' + ) + parser.add_argument('--gen-common', + action='store_true', + help='Generate json_common.c and json_common.h') + parser.add_argument('--gen-ref', + action='store_true', + help='Generate reference file defined in schema with key \"$ref\"') + parser.add_argument('-r', + '--recursive', + action='store_true', + help='Recursively generate all schema files in directory') + parser.add_argument( + '--out', + help='Specify a directory to save C header and source(default is current directory)') + args = parser.parse_args() + + if not args.root: + print('Missing root path, see help') + sys.exit(1) + + root_path = os.path.realpath(args.root) + if not os.path.exists(root_path): + print('Root %s is not exist') % args.root + sys.exit(1) + + MyRoot.root_path = root_path + + if args.out: + srcpath = helpers.FilePath(args.out) + else: + srcpath = helpers.FilePath(os.getcwd()) + if not os.path.exists(srcpath.name): + os.makedirs(srcpath.name) + + if args.gen_common: + generateCommonFiles(srcpath.name) + handlerFiles(args, srcpath) + +if __name__ == "__main__": + main() diff --git a/src/json/schema/src/headers.py b/src/json/schema/src/headers.py new file mode 100644 index 0000000..cdde974 --- /dev/null +++ b/src/json/schema/src/headers.py @@ -0,0 +1,197 @@ +# -*- coding: utf-8 -*- +''' +Description: header class and functions +Interface: None +History: 2019-06-17 +''' +# - Copyright (C) Huawei Technologies Co., Ltd. 2019. All rights reserved. +# - clibcni licensed under the Mulan PSL v1. +# - You can use this software according to the terms and conditions of the Mulan PSL v1. +# - You may obtain a copy of Mulan PSL v1 at: +# - http://license.coscl.org.cn/MulanPSL +# - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR +# - IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR +# - PURPOSE. +# - See the Mulan PSL v1 for more details. +# - Description: generate json +# - Author: tanyifeng +# - Create: 2019-04-25 +#!/usr/bin/python -Es +import helpers + +def appendHeaderArray(obj, header, prefix): + ''' + Description: Write c header file of array + Interface: None + History: 2019-06-17 + ''' + if not obj.subtypobj or obj.subtypname: + return + header.write("typedef struct {\n") + for i in obj.subtypobj: + if i.typ == 'array': + c_typ = helpers.getPrefixPointer(i.name, i.subtyp, prefix) or \ + helpers.getMapCTypes(i.subtyp) + if i.subtypobj is not None: + c_typ = helpers.getNameSubstr(i.name, prefix) + + if not helpers.judgeComplex(i.subtyp): + header.write(" %s%s*%s;\n" % (c_typ, " " if '*' not in c_typ else "", \ + i.fixname)) + else: + header.write(" %s **%s;\n" % (c_typ, i.fixname)) + header.write(" size_t %s;\n\n" % (i.fixname + "_len")) + else: + c_typ = helpers.getPrefixPointer(i.name, i.typ, prefix) or \ + helpers.getMapCTypes(i.typ) + header.write(" %s%s%s;\n" % (c_typ, " " if '*' not in c_typ else "", i.fixname)) + typename = helpers.getNameSubstr(obj.name, prefix) + header.write("}\n%s;\n\n" % typename) + header.write("void free_%s(%s *ptr);\n\n" % (typename, typename)) + header.write("%s *make_%s(yajl_val tree, const struct parser_context *ctx, parser_error *err);"\ + "\n\n" % (typename, typename)) + +def appendHeaderMapStrObj(obj, header, prefix): + ''' + Description: Write c header file of mapStringObject + Interface: None + History: 2019-06-17 + ''' + child = obj.children[0] + header.write("typedef struct {\n") + header.write(" char **keys;\n") + if helpers.validBasicMapName(child.typ): + c_typ = helpers.getPrefixPointer("", child.typ, "") + elif child.subtypname: + c_typ = child.subtypname + else: + c_typ = helpers.getPrefixPointer(child.name, child.typ, prefix) + header.write(" %s%s*%s;\n" % (c_typ, " " if '*' not in c_typ else "", child.fixname)) + header.write(" size_t len;\n") + +def appendHeaderChildArray(child, header, prefix): + ''' + Description: Write c header file of array of child + Interface: None + History: 2019-06-17 + ''' + if helpers.getMapCTypes(child.subtyp) != "": + c_typ = helpers.getMapCTypes(child.subtyp) + elif helpers.validBasicMapName(child.subtyp): + c_typ = '%s *' % helpers.makeBasicMapName(child.subtyp) + elif child.subtypname is not None: + c_typ = child.subtypname + elif child.subtypobj is not None: + c_typ = helpers.getNameSubstr(child.name, prefix) + else: + c_typ = helpers.getPrefixPointer(child.name, child.subtyp, prefix) + + if helpers.validBasicMapName(child.subtyp): + header.write(" %s **%s;\n" % (helpers.makeBasicMapName(child.subtyp), child.fixname)) + elif not helpers.judgeComplex(child.subtyp): + header.write(" %s%s*%s;\n" % (c_typ, " " if '*' not in c_typ else "", child.fixname)) + else: + header.write(" %s%s**%s;\n" % (c_typ, " " if '*' not in c_typ else "", child.fixname)) + header.write(" size_t %s;\n\n" % (child.fixname + "_len")) + +def appendHeaderChildOthers(child, header, prefix): + ''' + Description: Write c header file of others of child + Interface: None + History: 2019-06-17 + ''' + if helpers.getMapCTypes(child.typ) != "": + c_typ = helpers.getMapCTypes(child.typ) + elif helpers.validBasicMapName(child.typ): + c_typ = '%s *' % helpers.makeBasicMapName(child.typ) + elif child.subtypname: + c_typ = helpers.getPrefixPointer(child.subtypname, child.typ, "") + else: + c_typ = helpers.getPrefixPointer(child.name, child.typ, prefix) + header.write(" %s%s%s;\n\n" % (c_typ, " " if '*' not in c_typ else "", child.fixname)) + +def appendTypeCHeader(obj, header, prefix): + ''' + Description: Write c header file + Interface: None + History: 2019-06-17 + ''' + if not helpers.judgeComplex(obj.typ): + return + + if obj.typ == 'array': + appendHeaderArray(obj, header, prefix) + return + + if obj.typ == 'mapStringObject': + if obj.subtypname is not None: + return + appendHeaderMapStrObj(obj, header, prefix) + elif obj.typ == 'object': + if obj.subtypname is not None: + return + header.write("typedef struct {\n") + if obj.children is None: + header.write(" char unuseful;//unuseful definition to avoid empty struct\n") + for i in obj.children or [ ]: + if i.typ == 'array': + appendHeaderChildArray(i, header, prefix) + else: + appendHeaderChildOthers(i, header, prefix) + + typename = helpers.getPrefixName(obj.name, prefix) + header.write("}\n%s;\n\n" % typename) + header.write("void free_%s(%s *ptr);\n\n" % (typename, typename)) + header.write("%s *make_%s(yajl_val tree, const struct parser_context *ctx, parser_error *err)"\ + ";\n\n" % (typename, typename)) + header.write("yajl_gen_status gen_%s(yajl_gen g, const %s *ptr, const struct parser_context "\ + "*ctx, parser_error *err);\n\n" % (typename, typename)) + +def headerReflection(structs, schema_info, header): + ''' + Description: Reflection header files + Interface: None + History: 2019-06-17 + ''' + prefix = schema_info.prefix + header.write("// Generated from %s. Do not edit!\n" % (schema_info.name.basename)) + header.write("#ifndef %s_SCHEMA_H\n" % prefix.upper()) + header.write("#define %s_SCHEMA_H\n\n" % prefix.upper()) + header.write("#include \n") + header.write("#include \n") + header.write("#include \"json_common.h\"\n") + if schema_info.refs: + for ref in schema_info.refs.keys(): + header.write("#include \"%s\"\n" % (ref)) + header.write("\n#ifdef __cplusplus\n") + header.write("extern \"C\" {\n") + header.write("#endif\n\n") + + for i in structs: + appendTypeCHeader(i, header, prefix) + length = len(structs) + toptype = structs[length - 1].typ if length != 0 else "" + if toptype == 'object': + header.write("%s *%s_parse_file(const char *filename, const struct parser_context *ctx, "\ + "parser_error *err);\n\n" % (prefix, prefix)) + header.write("%s *%s_parse_file_stream(FILE *stream, const struct parser_context *ctx, "\ + "parser_error *err);\n\n" % (prefix, prefix)) + header.write("%s *%s_parse_data(const char *jsondata, const struct parser_context *ctx, "\ + "parser_error *err);\n\n" % (prefix, prefix)) + header.write("char *%s_generate_json(const %s *ptr, const struct parser_context *ctx, "\ + "parser_error *err);\n\n" % (prefix, prefix)) + elif toptype == 'array': + header.write("void free_%s(%s_element **ptr, size_t len);\n\n" % (prefix, prefix)) + header.write("%s_element **%s_parse_file(const char *filename, const struct "\ + "parser_context *ctx, parser_error *err, size_t *len);\n\n" % (prefix, prefix)) + header.write("%s_element **%s_parse_file_stream(FILE *stream, const struct "\ + "parser_context *ctx, parser_error *err, size_t *len);\n\n" % (prefix, prefix)) + header.write("%s_element **%s_parse_data(const char *jsondata, const struct "\ + "parser_context *ctx, parser_error *err, size_t *len);\n\n" % (prefix, prefix)) + header.write("char *%s_generate_json(const %s_element **ptr, size_t len, "\ + "const struct parser_context *ctx, parser_error *err);\n\n" % (prefix, prefix)) + + header.write("#ifdef __cplusplus\n") + header.write("}\n") + header.write("#endif\n\n") + header.write("#endif\n\n") diff --git a/src/json/schema/src/helpers.py b/src/json/schema/src/helpers.py new file mode 100644 index 0000000..ed6fb5e --- /dev/null +++ b/src/json/schema/src/helpers.py @@ -0,0 +1,313 @@ +# -*- coding: utf-8 -*- +''' +Description: helper class and functions +Interface: None +History: 2019-06-17 +''' +# - Copyright (C) Huawei Technologies Co., Ltd. 2019. All rights reserved. +# - clibcni licensed under the Mulan PSL v1. +# - You can use this software according to the terms and conditions of the Mulan PSL v1. +# - You may obtain a copy of Mulan PSL v1 at: +# - http://license.coscl.org.cn/MulanPSL +# - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR +# - IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR +# - PURPOSE. +# - See the Mulan PSL v1 for more details. +# - Description: generate json +# - Author: tanyifeng +# - Create: 2019-04-25 +#!/usr/bin/python -Es +import os +import sys + +def appendSeparator(subStr): + ''' + Description: append only '_' at last position of subStr + Interface: None + History: 2019-09-20 + ''' + if subStr and subStr[-1] != '_': + subStr.append('_') + +def convertToCStyle(name): + ''' + Description: convert name to linux c format + Interface: None + History: 2019-06-17 + ''' + if name is None or name == "": + return "" + name = name.replace('.', '_').replace('-', '_').replace('/', '_') + subStr = [] + preindex = 0 + index = 0 + for index, char in enumerate(name): + if char == '_': + appendSeparator(subStr) + subStr.append(name[preindex:index].lower()) + preindex = index + 1 + if not char.isupper() and name[preindex].isupper() and \ + name[preindex + 1].isupper(): + appendSeparator(subStr) + subStr.append(name[preindex:index - 1].lower()) + preindex = index - 1 + continue + if char.isupper() and index > 0 and name[index - 1].islower(): + appendSeparator(subStr) + subStr.append(name[preindex:index].lower()) + preindex = index + + if preindex <= index and index >= 0 and name[index] != '_' and \ + preindex != 0: + appendSeparator(subStr) + subStr.append(name[preindex:index + 1].lower()) + result = ''.join(subStr) + return result + +def getMapCTypes(typ): + ''' + Description: Get map c types + Interface: None + History: 2019-06-17 + ''' + map_c_types = { + 'byte': 'uint8_t', + 'string': 'char *', + 'integer': 'int', + 'boolean': 'bool', + 'double': 'double', + 'int8': 'int8_t', + "int16": 'int16_t', + "int32": "int32_t", + "int64": "int64_t", + 'uint8': 'uint8_t', + "uint16": 'uint16_t', + "uint32": "uint32_t", + "uint64": "uint64_t", + "UID": "uid_t", + "GID": "gid_t", + "booleanPointer": "bool *", + 'bytePointer': 'uint8_t *', + 'integerPointer': 'int *', + 'doublePointer': 'double *', + 'int8Pointer': 'int8_t *', + "int16Pointer": 'int16_t *', + "int32Pointer": "int32_t *", + "int64Pointer": "int64_t *", + 'uint8Pointer': 'uint8_t *', + "uint16Pointer": 'uint16_t *', + "uint32Pointer": "uint32_t *", + "uint64Pointer": "uint64_t *", + } + if typ in map_c_types: + return map_c_types[typ] + return "" + +def validBasicMapName(typ): + ''' + Description: Valid basic map name + Interface: None + History: 2019-06-17 + ''' + return typ != 'mapStringObject' and hasattr(typ, 'startswith') and \ + typ.startswith('map') + +def makeBasicMapName(mapname): + ''' + Description: Make basic map name + Interface: None + History: 2019-06-17 + ''' + basic_map_types = ('string', 'int', 'bool') + parts = convertToCStyle(mapname).split('_') + if len(parts) != 3 or parts[0] != 'map' or \ + (parts[1] not in basic_map_types) or \ + (parts[2] not in basic_map_types): + print('Invalid map name: %s') % mapname + sys.exit(1) + return "json_map_%s_%s" % (parts[1], parts[2]) + + +def getNameSubstr(name, prefix): + ''' + Description: Make array name + Interface: None + History: 2019-06-17 + ''' + return "%s_element" % prefix if name is None or name == "" or prefix == name \ + else "%s_%s_element" % (prefix, name) + +def getPrefixName(name, prefix): + ''' + Description: Make name + Interface: None + History: 2019-06-17 + ''' + if name is None or name == "" or prefix.endswith(name): + return "%s" % prefix + if prefix is None or prefix == "" or prefix == name or name.endswith(prefix): + return "%s" % name + return "%s_%s" % (prefix, name) + +def getPrefixPointer(name, typ, prefix): + ''' + Description: Make pointer name + Interface: None + History: 2019-06-17 + ''' + if typ != 'object' and typ != 'mapStringObject' and \ + not validBasicMapName(typ): + return "" + return '%s *' % makeBasicMapName(typ) if validBasicMapName(typ) \ + else "%s *" % getPrefixName(name, prefix) + +def judgeComplex(typ): + ''' + Description: Check compound object + Interface: None + History: 2019-06-17 + ''' + return typ in ('object', 'array', 'mapStringObject') + +def judgeDataType(typ): + ''' + Description: Check numeric type + Interface: None + History: 2019-06-17 + ''' + if (typ.startswith("int") or typ.startswith("uint")) and \ + "Pointer" not in typ: + return True + return typ in ("integer", "UID", "GID", "double") + +def judgeDataPointerType(typ): + ''' + Description: Check numeric pointer type + Interface: None + History: 2019-06-17 + ''' + if (typ.startswith("int") or typ.startswith("uint")) and "Pointer" in typ: + return True + return False + +def obtainDataPointerType(typ): + ''' + Description: Get numeric pointer type + Interface: None + History: 2019-06-17 + ''' + index = typ.find("Pointer") + return typ[0:index] if index != -1 else "" + +def obtainPointer(name, typ, prefix): + ''' + Description: Obtain pointer string + Interface: None + History: 2019-06-17 + ''' + ptr = getPrefixPointer(name, typ, prefix) + if ptr != "": + return ptr + + return "char *" if typ == "string" else \ + ("%s *" % typ if typ == "ArrayOfStrings" else "") + +class CombinationName(object): + ''' + Description: Store CombinationName information + Interface: None + History: 2019-06-17 + ''' + + def __init__(self, name, leaf=None): + self.name = name + self.leaf = leaf + + def __repr__(self): + return self.name + + def __str__(self): + return self.name + + def append(self, leaf): + ''' + Description: append name + Interface: None + History: 2019-06-17 + ''' + prefix_name = self.name + '_' if self.name != "" else "" + return CombinationName(prefix_name + leaf, leaf) + + +class Unite(object): + ''' + Description: Store Unite information + Interface: None + History: 2019-06-17 + ''' + def __init__(self, name, typ, children, subtyp=None, subtypobj=None, subtypname=None, \ + required=None): + self.typ = typ + self.children = children + self.subtyp = subtyp + self.subtypobj = subtypobj + self.subtypname = subtypname + self.required = required + self.name = convertToCStyle(name.name.replace('.', '_')) + self.origname = name.leaf or name.name + self.fixname = convertToCStyle(self.origname.replace('.', '_')) + + + + def __repr__(self): + if self.subtyp is not None: + return "name:(%s) type:(%s -> %s)" \ + % (self.name, self.typ, self.subtyp) + return "name:(%s) type:(%s)" % (self.name, self.typ) + + def __str__(self): + return self.__repr__(self) + + +class FilePath(object): + ''' + Description: Store filepath information + Interface: None + History: 2019-06-17 + ''' + def __init__(self, name): + self.name = os.path.realpath(name) + self.dirname = os.path.dirname(self.name) + self.basename = os.path.basename(self.name) + + def __repr__(self): + return "{name:(%s) dirname:(%s) basename:(%s)}" \ + % (self.name, self.dirname, self.basename) + + def __str__(self): + return self.__repr__(self) + + +class SchemaInfo(object): + ''' + Description: Store schema information + Interface: None + History: 2019-06-17 + ''' + + def __init__(self, name, header, source, prefix, filesdir, refs=None): + self.name = name + self.fileprefix = convertToCStyle( \ + name.basename.replace('.', '_').replace('-', '_')) + self.header = header + self.source = source + self.prefix = prefix + self.refs = refs + self.filesdir = os.path.realpath(filesdir) + + def __repr__(self): + return "{name:(%s) header:(%s) source:(%s) prefix:(%s)}" \ + % (self.name, self.header, self.source, self.prefix) + + def __str__(self): + return self.__repr__(self) diff --git a/src/json/schema/src/read_file.c b/src/json/schema/src/read_file.c new file mode 100644 index 0000000..5e5d501 --- /dev/null +++ b/src/json/schema/src/read_file.c @@ -0,0 +1,151 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. + * clibcni licensed under the Mulan PSL v1. + * You can use this software according to the terms and conditions of the Mulan PSL v1. + * You may obtain a copy of Mulan PSL v1 at: + * http://license.coscl.org.cn/MulanPSL + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR + * PURPOSE. + * See the Mulan PSL v1 for more details. + * Author: tanyifeng + * Create: 2019-04-25 + * Description: provide file read functions + ********************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "securec.h" +#include "read_file.h" + +#ifndef O_CLOEXEC +#define O_CLOEXEC 02000000 +#endif + +#define JSON_MAX_SIZE (10LL * 1024LL * 1024LL) +#define FILE_MODE 0640 + +static int do_check_fread_args(const FILE *stream, const size_t *length) +{ + if (stream == NULL) { + return -1; + } + if (length == NULL) { + return -1; + } + + return 0; +} + +char *fread_file(FILE *stream, size_t *length) +{ + char *buf = NULL; + char *tmpbuf = NULL; + size_t off = 0; + + if (do_check_fread_args(stream, length) != 0) { + return NULL; + } + + while (1) { + size_t ret, newsize, sizejudge; + int pret; + errno_t rc = EOK; + sizejudge = (JSON_MAX_SIZE - BUFSIZ) - 1; + if (sizejudge < off) { + goto out; + } + newsize = off + BUFSIZ + 1; + + tmpbuf = (char *)calloc(1, newsize); + if (tmpbuf == NULL) { + goto out; + } + + if (buf != NULL) { + pret = memcpy_s(tmpbuf, newsize, buf, off); + if (pret) { + goto out; + } + + rc = memset_s(buf, off, 0, off); + if (rc != EOK) { + goto out; + } + + free(buf); + } + + buf = tmpbuf; + tmpbuf = NULL; + + ret = fread(buf + off, 1, BUFSIZ, stream); + if (ret == 0 && ferror(stream)) { + goto out; + } + if (ret < BUFSIZ || feof(stream)) { + *length = off + ret + 1; + buf[*length - 1] = '\0'; + return buf; + } + + off += BUFSIZ; + } +out: + free(buf); + free(tmpbuf); + return NULL; +} + +static int do_check_args(const char *path, const size_t *length) +{ + if (path == NULL || length == NULL) { + return -1; + } + if (strlen(path) > PATH_MAX) { + return -1; + } + return 0; +} + +char *read_file(const char *path, size_t *length) +{ + char *buf = NULL; + char rpath[PATH_MAX + 1] = { 0 }; + int fd = -1; + int tmperrno = -1; + FILE *fp = NULL; + + if (do_check_args(path, length) != 0) { + return NULL; + } + + if (realpath(path, rpath) == NULL) { + return NULL; + } + + fd = open(rpath, O_RDONLY | O_CLOEXEC, FILE_MODE); + if (fd < 0) { + return NULL; + } + + fp = fdopen(fd, "r"); + tmperrno = errno; + if (fp == NULL) { + (void)close(fd); + errno = tmperrno; + return NULL; + } + + buf = fread_file(fp, length); + (void)fclose(fp); + return buf; +} diff --git a/src/json/schema/src/read_file.h b/src/json/schema/src/read_file.h new file mode 100644 index 0000000..154693e --- /dev/null +++ b/src/json/schema/src/read_file.h @@ -0,0 +1,25 @@ +/***************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. + * clibcni licensed under the Mulan PSL v1. + * You can use this software according to the terms and conditions of the Mulan PSL v1. + * You may obtain a copy of Mulan PSL v1 at: + * http://license.coscl.org.cn/MulanPSL + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR + * PURPOSE. + * See the Mulan PSL v1 for more details. + * Author: tanyifeng + * Create: 2019-4-08 + * Description: provide container read file definition + ****************************************************************************/ +#ifndef __JSON_READ_FILE_H_ +#define __JSON_READ_FILE_H_ + +#include +#include + +char *fread_file(FILE *stream, size_t *length); + +char *read_file(const char *path, size_t *length); + +#endif diff --git a/src/json/schema/src/sources.py b/src/json/schema/src/sources.py new file mode 100644 index 0000000..818b05e --- /dev/null +++ b/src/json/schema/src/sources.py @@ -0,0 +1,1013 @@ +# -*- coding: utf-8 -*- +""" +Copyright (C) Huawei Technologies Co., Ltd. 2019. All rights reserved. +# - clibcni licensed under the Mulan PSL v1. +# - You can use this software according to the terms and conditions of the Mulan PSL v1. +# - You may obtain a copy of Mulan PSL v1 at: +# - http://license.coscl.org.cn/MulanPSL +# - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR +# - IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR +# - PURPOSE. +# - See the Mulan PSL v1 for more details. +Description: generate json +Author: tanyifeng +Interface: None +History: 2018-04-25 created +2019-06-17 Code specification +""" +#!/usr/bin/python -Es +import helpers + + +def appendCCode(obj, c_file, prefix): + """ + Description: append c language code to file + Interface: None + History: 2019-06-17 + """ + parseJsonToC(obj, c_file, prefix) + makeCFree(obj, c_file, prefix) + obtainCJson(obj, c_file, prefix) + +def parseMapStringObject(obj, c_file, prefix, obj_typename): + """ + Description: generate c language for parse json map string object + Interface: None + History: 2019-06-17 + """ + child = obj.children[0] + if helpers.validBasicMapName(child.typ): + childname = helpers.makeBasicMapName(child.typ) + else: + if child.subtypname: + childname = child.subtypname + else: + childname = helpers.getPrefixName(child.name, prefix) + c_file.write(' if (YAJL_GET_OBJECT(tree) != NULL && YAJL_GET_OBJECT(tree)->len > 0) {\n') + c_file.write(' size_t i;\n') + c_file.write(' ret->len = YAJL_GET_OBJECT(tree)->len;\n') + c_file.write(' ret->keys = safe_malloc((YAJL_GET_OBJECT(tree)->len + 1) ' \ + '* sizeof(*ret->keys));\n') + c_file.write(' ret->%s = safe_malloc((YAJL_GET_OBJECT(tree)->len + 1) ' \ + '* sizeof(*ret->%s));\n' % (child.fixname, child.fixname)) + c_file.write(' for (i = 0; i < YAJL_GET_OBJECT(tree)->len; i++) {\n') + c_file.write(' const char *tmpkey = YAJL_GET_OBJECT(tree)->keys[i];\n') + c_file.write(' ret->keys[i] = safe_strdup(tmpkey ? tmpkey : "");\n') + c_file.write(' yajl_val val = YAJL_GET_OBJECT(tree)->values[i];\n') + c_file.write(' ret->%s[i] = make_%s(val, ctx, err);\n' \ + % (child.fixname, childname)) + c_file.write(' if (ret->%s[i] == NULL) {\n' % (child.fixname)) + c_file.write(" free_%s(ret);\n" % obj_typename) + c_file.write(" return NULL;\n") + c_file.write(' }\n') + c_file.write(' }\n') + c_file.write(' }\n') + +def parseObjectType(obj, c_file, prefix, obj_typename): + """ + Description: generate c language for parse object type + Interface: None + History: 2019-06-17 + """ + if obj.typ == 'string': + c_file.write(' {\n') + readValueGenerator(c_file, 2, 'get_val(tree, "%s", yajl_t_string)' % obj.origname, \ + "ret->%s" % obj.fixname, obj.typ, obj.origname, obj_typename) + c_file.write(' }\n') + elif helpers.judgeDataType(obj.typ): + c_file.write(' {\n') + readValueGenerator(c_file, 2, 'get_val(tree, "%s", yajl_t_number)' % obj.origname, \ + "ret->%s" % obj.fixname, obj.typ, obj.origname, obj_typename) + c_file.write(' }\n') + elif helpers.judgeDataPointerType(obj.typ): + c_file.write(' {\n') + readValueGenerator(c_file, 2, 'get_val(tree, "%s", yajl_t_number)' % obj.origname, \ + "ret->%s" % obj.fixname, obj.typ, obj.origname, obj_typename) + c_file.write(' }\n') + if obj.typ == 'boolean': + c_file.write(' {\n') + readValueGenerator(c_file, 2, 'get_val(tree, "%s", yajl_t_true)' % obj.origname, \ + "ret->%s" % obj.fixname, obj.typ, obj.origname, obj_typename) + c_file.write(' }\n') + if obj.typ == 'booleanPointer': + c_file.write(' {\n') + readValueGenerator(c_file, 2, 'get_val(tree, "%s", yajl_t_true)' % obj.origname, \ + "ret->%s" % obj.fixname, obj.typ, obj.origname, obj_typename) + c_file.write(' }\n') + elif obj.typ == 'object' or obj.typ == 'mapStringObject': + if obj.subtypname is not None: + typename = obj.subtypname + else: + typename = helpers.getPrefixName(obj.name, prefix) + c_file.write( + ' ret->%s = make_%s(get_val(tree, "%s", yajl_t_object), ctx, err);\n' \ + % (obj.fixname, typename, obj.origname)) + c_file.write(" if (ret->%s == NULL && *err != 0) {\n" % obj.fixname) + c_file.write(" free_%s(ret);\n" % obj_typename) + c_file.write(" return NULL;\n") + c_file.write(" }\n") + elif obj.typ == 'array' and (obj.subtypobj or obj.subtyp == 'object'): + if obj.subtypname: + typename = obj.subtypname + else: + typename = helpers.getNameSubstr(obj.name, prefix) + c_file.write(' {\n') + c_file.write(' yajl_val tmp = get_val(tree, "%s", yajl_t_array);\n' \ + % (obj.origname)) + c_file.write(' if (tmp != NULL && YAJL_GET_ARRAY(tmp) != NULL &&' \ + ' YAJL_GET_ARRAY(tmp)->len > 0) {\n') + c_file.write(' size_t i;\n') + c_file.write(' ret->%s_len = YAJL_GET_ARRAY(tmp)->len;\n' % (obj.fixname)) + c_file.write(' ret->%s = safe_malloc((YAJL_GET_ARRAY(tmp)->len + 1) ' \ + '* sizeof(*ret->%s));\n' % (obj.fixname, obj.fixname)) + c_file.write(' for (i = 0; i < YAJL_GET_ARRAY(tmp)->len; i++) {\n') + c_file.write(' yajl_val val = YAJL_GET_ARRAY(tmp)->values[i];\n') + c_file.write(' ret->%s[i] = make_%s(val, ctx, err);\n' \ + % (obj.fixname, typename)) + c_file.write(' if (ret->%s[i] == NULL) {\n' % (obj.fixname)) + c_file.write(" free_%s(ret);\n" % obj_typename) + c_file.write(" return NULL;\n") + c_file.write(' }\n') + c_file.write(' }\n') + c_file.write(' }\n') + c_file.write(' }\n') + elif obj.typ == 'array' and obj.subtyp == 'byte': + c_file.write(' {\n') + c_file.write(' yajl_val tmp = get_val(tree, "%s", yajl_t_string);\n' \ + % (obj.origname)) + c_file.write(' if (tmp != NULL) {\n') + c_file.write(' char *str = YAJL_GET_STRING(tmp);\n') + c_file.write(' ret->%s = (uint8_t *)safe_strdup(str ? str : "");\n' \ + % obj.fixname) + c_file.write(' ret->%s_len = str != NULL ? strlen(str) : 0;\n' \ + % obj.fixname) + c_file.write(' }\n') + c_file.write(' }\n') + elif obj.typ == 'array': + c_file.write(' {\n') + c_file.write(' yajl_val tmp = get_val(tree, "%s", yajl_t_array);\n' \ + % (obj.origname)) + c_file.write(' if (tmp != NULL && YAJL_GET_ARRAY(tmp) != NULL &&' \ + ' YAJL_GET_ARRAY(tmp)->len > 0) {\n') + c_file.write(' size_t i;\n') + c_file.write(' ret->%s_len = YAJL_GET_ARRAY(tmp)->len;\n' % (obj.fixname)) + c_file.write( + ' ret->%s = safe_malloc((YAJL_GET_ARRAY(tmp)->len + 1) *' \ + ' sizeof(*ret->%s));\n' % (obj.fixname, obj.fixname)) + c_file.write(' for (i = 0; i < YAJL_GET_ARRAY(tmp)->len; i++) {\n') + readValueGenerator(c_file, 4, 'YAJL_GET_ARRAY(tmp)->values[i]', \ + "ret->%s[i]" % obj.fixname, obj.subtyp, obj.origname, obj_typename) + c_file.write(' }\n') + c_file.write(' }\n') + c_file.write(' }\n') + elif helpers.validBasicMapName(obj.typ): + c_file.write(' {\n') + c_file.write(' yajl_val tmp = get_val(tree, "%s", yajl_t_object);\n' \ + % (obj.origname)) + c_file.write(' if (tmp != NULL) {\n') + c_file.write(' ret->%s = make_%s(tmp, ctx, err);\n' \ + % (obj.fixname, helpers.makeBasicMapName(obj.typ))) + c_file.write(' if (ret->%s == NULL) {\n' % (obj.fixname)) + c_file.write(' char *new_error = NULL;\n') + c_file.write(" if (asprintf(&new_error, \"Value error for key" \ + " '%s': %%s\", *err ? *err : \"null\") < 0) {\n" % (obj.origname)) + c_file.write(' new_error = safe_strdup(' \ + '"error allocating memory");\n') + c_file.write(' }\n') + c_file.write(' free(*err);\n') + c_file.write(' *err = new_error;\n') + c_file.write(' free_%s(ret);\n' % obj_typename) + c_file.write(' return NULL;\n') + c_file.write(' }\n') + c_file.write(' }\n') + c_file.write(' }\n') + +def parseObjectOrArrayObject(obj, c_file, prefix, obj_typename): + """ + Description: generate c language for parse object or array object + Interface: None + History: 2019-06-17 + """ + nodes = obj.children if obj.typ == 'object' else obj.subtypobj + + required_to_check = [] + for i in nodes or []: + if obj.required and i.origname in obj.required and \ + not helpers.judgeDataType(i.typ) and i.typ != 'boolean': + required_to_check.append(i) + parseObjectType(i, c_file, prefix, obj_typename) + + for i in required_to_check: + c_file.write(' if (ret->%s == NULL) {\n' % i.fixname) + c_file.write(' if (asprintf(err, "Required field \'%%s\' not present", ' \ + ' "%s") < 0)\n' % i.origname) + c_file.write(' *err = safe_strdup("error allocating memory");\n') + c_file.write(" free_%s(ret);\n" % obj_typename) + c_file.write(" return NULL;\n") + c_file.write(' }\n') + + if obj.typ == 'object' and obj.children is not None: + # O(n^2) complexity, but the objects should not really be big... + condition = " &&\n ".join( \ + ['strcmp(tree->u.object.keys[i], "%s")' % i.origname for i in obj.children]) + c_file.write(""" + if (tree->type == yajl_t_object && (ctx->options & OPT_PARSE_STRICT)) { + size_t i; + for (i = 0; i < tree->u.object.len; i++) + if (%s) { + if (ctx->stderr > 0) + (void)fprintf(ctx->stderr, "WARNING: unknown key found: %%s\\n", + tree->u.object.keys[i]); + } + } +""" % condition) + +def parseJsonToC(obj, c_file, prefix): + """ + Description: generate c language for parse json file + Interface: None + History: 2019-06-17 + """ + if not helpers.judgeComplex(obj.typ): + return + + if obj.typ == 'object' or obj.typ == 'mapStringObject': + if obj.subtypname: + return + obj_typename = typename = helpers.getPrefixName(obj.name, prefix) + + if obj.typ == 'array': + obj_typename = typename = helpers.getNameSubstr(obj.name, prefix) + objs = obj.subtypobj + if objs is None or obj.subtypname: + return + + c_file.write("%s *make_%s(yajl_val tree, const struct parser_context *ctx, "\ + "parser_error *err) {\n" % (typename, typename)) + c_file.write(" %s *ret = NULL;\n" % (typename)) + c_file.write(" *err = 0;\n") + c_file.write(" if (tree == NULL)\n") + c_file.write(" return ret;\n") + c_file.write(" ret = safe_malloc(sizeof(*ret));\n") + + if obj.typ == 'mapStringObject': + parseMapStringObject(obj, c_file, prefix, obj_typename) + + if obj.typ == 'object' or (obj.typ == 'array' and obj.subtypobj): + parseObjectOrArrayObject(obj, c_file, prefix, obj_typename) + + c_file.write(' return ret;\n') + c_file.write("}\n\n") + + +def obtainMapStringObject(obj, c_file, prefix): + """ + Description: c language generate map string object + Interface: None + History: 2019-06-17 + """ + child = obj.children[0] + if helpers.validBasicMapName(child.typ): + childname = helpers.makeBasicMapName(child.typ) + else: + if child.subtypname: + childname = child.subtypname + else: + childname = helpers.getPrefixName(child.name, prefix) + c_file.write(' size_t len = 0, i;\n') + c_file.write(" if (ptr != NULL)\n") + c_file.write(" len = ptr->len;\n") + c_file.write(" if (!len && !(ctx->options & OPT_GEN_SIMPLIFY))\n") + c_file.write(' yajl_gen_config(g, yajl_gen_beautify, 0);\n') + c_file.write(" stat = yajl_gen_map_open((yajl_gen)g);\n") + c_file.write(" if (yajl_gen_status_ok != stat)\n") + c_file.write(" GEN_SET_ERROR_AND_RETURN(stat, err);\n") + c_file.write(' if (len ||(ptr != NULL && ptr->keys != NULL && ptr->%s != NULL)) {\n' \ + % child.fixname) + c_file.write(' for (i = 0; i < len; i++) {\n') + c_file.write(' char *str = ptr->keys[i] ? ptr->keys[i] : "";\n') + c_file.write(' stat = yajl_gen_string((yajl_gen)g, (const unsigned char *)str, strlen(str));\n') + c_file.write(" if (yajl_gen_status_ok != stat)\n") + c_file.write(" GEN_SET_ERROR_AND_RETURN(stat, err);\n") + c_file.write(' stat = gen_%s(g, ptr->%s[i], ctx, err);\n' \ + % (childname, child.fixname)) + c_file.write(" if (yajl_gen_status_ok != stat)\n") + c_file.write(" GEN_SET_ERROR_AND_RETURN(stat, err);\n") + c_file.write(' }\n') + c_file.write(' }\n') + c_file.write(" stat = yajl_gen_map_close((yajl_gen)g);\n") + c_file.write(" if (yajl_gen_status_ok != stat)\n") + c_file.write(" GEN_SET_ERROR_AND_RETURN(stat, err);\n") + c_file.write(" if (!len && !(ctx->options & OPT_GEN_SIMPLIFY))\n") + c_file.write(' yajl_gen_config(g, yajl_gen_beautify, 1);\n') + +def obtainObjectOrArrayObject(obj, c_file, prefix): + """ + Description: c language generate object or array object + Interface: None + History: 2019-06-17 + """ + if obj.typ == 'string': + c_file.write(' if ((ctx->options & OPT_GEN_KAY_VALUE) ||' \ + ' (ptr != NULL && ptr->%s != NULL)) {\n' % obj.fixname) + c_file.write(' char *str = "";\n') + c_file.write(' stat = yajl_gen_string((yajl_gen)g, (const unsigned char *)("%s"), strlen("%s"));\n' \ + % (obj.origname, obj.origname)) + c_file.write(" if (yajl_gen_status_ok != stat)\n") + c_file.write(" GEN_SET_ERROR_AND_RETURN(stat, err);\n") + c_file.write(" if (ptr != NULL && ptr->%s != NULL) {\n" % obj.fixname) + c_file.write(" str = ptr->%s;\n" % obj.fixname) + c_file.write(" }\n") + jsonValueGenerator(c_file, 2, "str", 'g', 'ctx', obj.typ) + c_file.write(" }\n") + + elif helpers.judgeDataType(obj.typ): + c_file.write(' if ((ctx->options & OPT_GEN_KAY_VALUE) ||' \ + ' (ptr != NULL && ptr->%s)) {\n' % obj.fixname) + if obj.typ == 'double': + numtyp = 'double' + elif obj.typ.startswith("uint") or obj.typ == 'GID' or obj.typ == 'UID': + numtyp = 'long long unsigned int' + else: + numtyp = 'long long int' + c_file.write(' %s num = 0;\n' % numtyp) + c_file.write(' stat = yajl_gen_string((yajl_gen)g, (const unsigned char *)("%s"), strlen("%s"));\n' \ + % (obj.origname, obj.origname)) + c_file.write(" if (yajl_gen_status_ok != stat)\n") + c_file.write(" GEN_SET_ERROR_AND_RETURN(stat, err);\n") + c_file.write(" if (ptr != NULL && ptr->%s) {\n" % obj.fixname) + c_file.write(" num = (%s)ptr->%s;\n" % (numtyp, obj.fixname)) + c_file.write(" }\n") + jsonValueGenerator(c_file, 2, "num", 'g', 'ctx', obj.typ) + c_file.write(" }\n") + + elif helpers.judgeDataPointerType(obj.typ): + c_file.write(' if ((ptr != NULL && ptr->%s != NULL)) {\n' % obj.fixname) + numtyp = helpers.obtainDataPointerType(obj.typ) + if numtyp == "": + return + c_file.write(' %s num = 0;\n' % helpers.getMapCTypes(numtyp)) + c_file.write(' stat = yajl_gen_string((yajl_gen)g, (const unsigned char *)("%s"), strlen("%s"));\n' \ + % (obj.origname, obj.origname)) + c_file.write(" if (yajl_gen_status_ok != stat)\n") + c_file.write(" GEN_SET_ERROR_AND_RETURN(stat, err);\n") + c_file.write(" if (ptr != NULL && ptr->%s != NULL) {\n" % obj.fixname) + c_file.write(" num = (%s)*(ptr->%s);\n" \ + % (helpers.getMapCTypes(numtyp), obj.fixname)) + c_file.write(" }\n") + jsonValueGenerator(c_file, 2, "num", 'g', 'ctx', numtyp) + c_file.write(" }\n") + + elif obj.typ == 'boolean': + c_file.write(' if ((ctx->options & OPT_GEN_KAY_VALUE) ||' \ + ' (ptr != NULL && ptr->%s)) {\n' % obj.fixname) + c_file.write(' bool b = false;\n') + c_file.write(' stat = yajl_gen_string((yajl_gen)g, (const unsigned char *)("%s"), strlen("%s"));\n' \ + % (obj.origname, obj.origname)) + c_file.write(" if (yajl_gen_status_ok != stat)\n") + c_file.write(" GEN_SET_ERROR_AND_RETURN(stat, err);\n") + c_file.write(" if (ptr != NULL && ptr->%s) {\n" % obj.fixname) + c_file.write(" b = ptr->%s;\n" % obj.fixname) + c_file.write(" }\n") + jsonValueGenerator(c_file, 2, "b", 'g', 'ctx', obj.typ) + c_file.write(" }\n") + elif obj.typ == 'object' or obj.typ == 'mapStringObject': + if obj.subtypname: + typename = obj.subtypname + else: + typename = helpers.getPrefixName(obj.name, prefix) + c_file.write(' if ((ctx->options & OPT_GEN_KAY_VALUE) ||' \ + ' (ptr != NULL && ptr->%s != NULL)) {\n' % obj.fixname) + c_file.write(' stat = yajl_gen_string((yajl_gen)g, (const unsigned char *)("%s"), strlen("%s"));\n' \ + % (obj.origname, obj.origname)) + c_file.write(" if (yajl_gen_status_ok != stat)\n") + c_file.write(" GEN_SET_ERROR_AND_RETURN(stat, err);\n") + c_file.write(' stat = gen_%s(g, ptr != NULL ? ptr->%s : NULL, ctx, err);\n' \ + % (typename, obj.fixname)) + c_file.write(" if (yajl_gen_status_ok != stat)\n") + c_file.write(" GEN_SET_ERROR_AND_RETURN(stat, err);\n") + c_file.write(" }\n") + + elif obj.typ == 'array' and (obj.subtypobj or obj.subtyp == 'object'): + if obj.subtypname: + typename = obj.subtypname + else: + typename = helpers.getNameSubstr(obj.name, prefix) + c_file.write(' if ((ctx->options & OPT_GEN_KAY_VALUE) || ' \ + '(ptr != NULL && ptr->%s != NULL)) {\n' % obj.fixname) + c_file.write(' size_t len = 0, i;\n') + c_file.write(' stat = yajl_gen_string((yajl_gen)g, (const unsigned char *)("%s"), strlen("%s"));\n' \ + % (obj.origname, obj.origname)) + c_file.write(" if (yajl_gen_status_ok != stat)\n") + c_file.write(" GEN_SET_ERROR_AND_RETURN(stat, err);\n") + c_file.write(" if (ptr != NULL && ptr->%s != NULL) {\n" % obj.fixname) + c_file.write(" len = ptr->%s_len;\n" % obj.fixname) + c_file.write(" }\n") + c_file.write(" if (!len && !(ctx->options & OPT_GEN_SIMPLIFY))\n") + c_file.write(' yajl_gen_config(g, yajl_gen_beautify, 0);\n') + c_file.write(' stat = yajl_gen_array_open((yajl_gen)g);\n') + c_file.write(" if (yajl_gen_status_ok != stat)\n") + c_file.write(" GEN_SET_ERROR_AND_RETURN(stat, err);\n") + c_file.write(' for (i = 0; i < len; i++) {\n') + c_file.write(' stat = gen_%s(g, ptr->%s[i], ctx, err);\n' \ + % (typename, obj.fixname)) + c_file.write(" if (yajl_gen_status_ok != stat)\n") + c_file.write(" GEN_SET_ERROR_AND_RETURN(stat, err);\n") + c_file.write(' }\n') + c_file.write(' stat = yajl_gen_array_close((yajl_gen)g);\n') + c_file.write(" if (!len && !(ctx->options & OPT_GEN_SIMPLIFY))\n") + c_file.write(' yajl_gen_config(g, yajl_gen_beautify, 1);\n') + c_file.write(" if (yajl_gen_status_ok != stat)\n") + c_file.write(" GEN_SET_ERROR_AND_RETURN(stat, err);\n") + c_file.write(' }\n') + elif obj.typ == 'array' and obj.subtyp == 'byte': + c_file.write(' if ((ctx->options & OPT_GEN_KAY_VALUE) ||' \ + ' (ptr != NULL && ptr->%s != NULL && ptr->%s_len)) {\n' \ + % (obj.fixname, obj.fixname)) + c_file.write(' const char *str = "";\n') + c_file.write(' size_t len = 0;\n') + c_file.write(' stat = yajl_gen_string((yajl_gen)g, (const unsigned char *)("%s"), strlen("%s"));\n' \ + % (obj.origname, obj.origname)) + c_file.write(" if (yajl_gen_status_ok != stat)\n") + c_file.write(" GEN_SET_ERROR_AND_RETURN(stat, err);\n") + c_file.write(" if (ptr != NULL && ptr->%s != NULL) {\n" % obj.fixname) + c_file.write(" str = (const char *)ptr->%s;\n" % obj.fixname) + c_file.write(" len = ptr->%s_len;\n" % obj.fixname) + c_file.write(" }\n") + c_file.write(' stat = yajl_gen_string((yajl_gen)g, (const unsigned char *)str, len);\n') + c_file.write(" if (yajl_gen_status_ok != stat)\n") + c_file.write(" GEN_SET_ERROR_AND_RETURN(stat, err);\n") + c_file.write(" }\n") + elif obj.typ == 'array': + c_file.write(' if ((ctx->options & OPT_GEN_KAY_VALUE) || ' \ + '(ptr != NULL && ptr->%s != NULL)) {\n' % obj.fixname) + c_file.write(' size_t len = 0, i;\n') + c_file.write(' stat = yajl_gen_string((yajl_gen)g, (const unsigned char *)("%s"), strlen("%s"));\n' \ + % (obj.origname, obj.origname)) + c_file.write(" if (yajl_gen_status_ok != stat)\n") + c_file.write(" GEN_SET_ERROR_AND_RETURN(stat, err);\n") + c_file.write(" if (ptr != NULL && ptr->%s != NULL) {\n" % obj.fixname) + c_file.write(" len = ptr->%s_len;\n" % obj.fixname) + c_file.write(" }\n") + c_file.write(" if (!len && !(ctx->options & OPT_GEN_SIMPLIFY))\n") + c_file.write(' yajl_gen_config(g, yajl_gen_beautify, 0);\n') + c_file.write(' stat = yajl_gen_array_open((yajl_gen)g);\n') + c_file.write(" if (yajl_gen_status_ok != stat)\n") + c_file.write(" GEN_SET_ERROR_AND_RETURN(stat, err);\n") + c_file.write(' for (i = 0; i < len; i++) {\n') + jsonValueGenerator(c_file, 3, "ptr->%s[i]" % obj.fixname, 'g', 'ctx', obj.subtyp) + c_file.write(' }\n') + c_file.write(' stat = yajl_gen_array_close((yajl_gen)g);\n') + c_file.write(" if (yajl_gen_status_ok != stat)\n") + c_file.write(" GEN_SET_ERROR_AND_RETURN(stat, err);\n") + c_file.write(" if (!len && !(ctx->options & OPT_GEN_SIMPLIFY))\n") + c_file.write(' yajl_gen_config(g, yajl_gen_beautify, 1);\n') + c_file.write(' }\n') + + elif helpers.validBasicMapName(obj.typ): + c_file.write(' if ((ctx->options & OPT_GEN_KAY_VALUE) || ' \ + '(ptr != NULL && ptr->%s != NULL)) {\n' % obj.fixname) + c_file.write(' stat = yajl_gen_string((yajl_gen)g, (const unsigned char *)("%s"), strlen("%s"));\n' \ + % (obj.origname, obj.origname)) + c_file.write(" if (yajl_gen_status_ok != stat)\n") + c_file.write(" GEN_SET_ERROR_AND_RETURN(stat, err);\n") + c_file.write(' stat = gen_%s(g, ptr ? ptr->%s : NULL, ctx, err);\n' \ + % (helpers.makeBasicMapName(obj.typ), obj.fixname)) + c_file.write(" if (yajl_gen_status_ok != stat)\n") + c_file.write(" GEN_SET_ERROR_AND_RETURN(stat, err);\n") + c_file.write(" }\n") + +def obtainCJson(obj, c_file, prefix): + """ + Description: c language generate json file + Interface: None + History: 2019-06-17 + """ + if not helpers.judgeComplex(obj.typ) or obj.subtypname: + return + if obj.typ == 'object' or obj.typ == 'mapStringObject': + obj_typename = typename = helpers.getPrefixName(obj.name, prefix) + elif obj.typ == 'array': + obj_typename = typename = helpers.getNameSubstr(obj.name, prefix) + objs = obj.subtypobj + if objs is None: + return + + c_file.write( + "yajl_gen_status gen_%s(yajl_gen g, const %s *ptr, const struct parser_context " \ + "*ctx, parser_error *err) {\n" % (typename, typename)) + c_file.write(" yajl_gen_status stat = yajl_gen_status_ok;\n") + c_file.write(" *err = 0;\n") + + if obj.typ == 'mapStringObject': + obtainMapStringObject(obj, c_file, prefix) + elif obj.typ == 'object' or (obj.typ == 'array' and obj.subtypobj): + nodes = obj.children if obj.typ == 'object' else obj.subtypobj + if nodes is None: + c_file.write(' if (!(ctx->options & OPT_GEN_SIMPLIFY))\n') + c_file.write(' yajl_gen_config(g, yajl_gen_beautify, 0);\n') + + c_file.write(" stat = yajl_gen_map_open((yajl_gen)g);\n") + c_file.write(" if (yajl_gen_status_ok != stat)\n") + c_file.write(" GEN_SET_ERROR_AND_RETURN(stat, err);\n") + + for i in nodes or []: + obtainObjectOrArrayObject(i, c_file, prefix) + + c_file.write(" stat = yajl_gen_map_close((yajl_gen)g);\n") + c_file.write(" if (yajl_gen_status_ok != stat)\n") + c_file.write(" GEN_SET_ERROR_AND_RETURN(stat, err);\n") + if nodes is None: + c_file.write(' if (!(ctx->options & OPT_GEN_SIMPLIFY))\n') + c_file.write(' yajl_gen_config(g, yajl_gen_beautify, 1);\n') + c_file.write(' return yajl_gen_status_ok;\n') + c_file.write("}\n\n") + + +def readValueGenerator(c_file, level, src, dest, typ, keyname, obj_typename): + """ + Description: read value generateor + Interface: None + History: 2019-06-17 + """ + if helpers.validBasicMapName(typ): + c_file.write('%syajl_val val = %s;\n' % (' ' * level, src)) + c_file.write('%sif (val != NULL) {\n' % (' ' * level)) + c_file.write('%s%s = make_%s(val, ctx, err);\n' \ + % (' ' * (level + 1), dest, helpers.makeBasicMapName(typ))) + c_file.write('%sif (%s == NULL) {\n' % (' ' * (level + 1), dest)) + c_file.write('%s char *new_error = NULL;\n' % (' ' * (level + 1))) + c_file.write("%s if (asprintf(&new_error, \"Value error for key" \ + " '%s': %%s\", *err ? *err : \"null\") < 0) {\n" \ + % (' ' * (level + 1), keyname)) + c_file.write('%s new_error = safe_strdup("error allocating memory");\n' \ + % (' ' * (level + 1))) + c_file.write('%s }\n' % (' ' * (level + 1))) + c_file.write('%s free(*err);\n' % (' ' * (level + 1))) + c_file.write('%s *err = new_error;\n' % (' ' * (level + 1))) + c_file.write('%s free_%s(ret);\n' % (' ' * (level + 1), obj_typename)) + c_file.write('%s return NULL;\n' % (' ' * (level + 1))) + c_file.write('%s}\n' % (' ' * (level + 1))) + c_file.write('%s}\n' % (' ' * (level))) + elif typ == 'string': + c_file.write('%syajl_val val = %s;\n' % (' ' * (level), src)) + c_file.write('%sif (val != NULL) {\n' % (' ' * (level))) + c_file.write('%schar *str = YAJL_GET_STRING(val);\n' % (' ' * (level + 1))) + c_file.write('%s%s = safe_strdup(str ? str : "");\n' % (' ' * (level + 1), dest)) + c_file.write('%s}\n' % (' ' * level)) + elif helpers.judgeDataType(typ): + c_file.write('%syajl_val val = %s;\n' % (' ' * (level), src)) + c_file.write('%sif (val != NULL) {\n' % (' ' * (level))) + if typ.startswith("uint") or \ + (typ.startswith("int") and typ != "integer") or typ == "double": + c_file.write('%sint invalid = common_safe_%s(YAJL_GET_NUMBER(val), &%s);\n' \ + % (' ' * (level + 1), typ, dest)) + elif typ == "integer": + c_file.write('%sint invalid = common_safe_int(YAJL_GET_NUMBER(val), (int *)&%s);\n' \ + % (' ' * (level + 1), dest)) + elif typ == "UID" or typ == "GID": + c_file.write('%sint invalid = common_safe_uint(YAJL_GET_NUMBER(val),' \ + ' (unsigned int *)&%s);\n' % (' ' * (level + 1), dest)) + c_file.write('%sif (invalid) {\n' % (' ' * (level + 1))) + c_file.write('%s if (asprintf(err, "Invalid value \'%%s\' with type \'%s\' ' + 'for key \'%s\': %%s", YAJL_GET_NUMBER(val), strerror(-invalid)) < 0)\n' \ + % (' ' * (level + 1), typ, keyname)) + c_file.write('%s *err = safe_strdup("error allocating memory");\n' \ + % (' ' * (level + 1))) + c_file.write('%s free_%s(ret);\n' % (' ' * (level + 1), obj_typename)) + c_file.write('%s return NULL;\n' % (' ' * (level + 1))) + c_file.write('%s}\n' % (' ' * (level + 1))) + c_file.write('%s}\n' % (' ' * (level))) + elif helpers.judgeDataPointerType(typ): + num_type = helpers.obtainDataPointerType(typ) + if num_type == "": + return + c_file.write('%syajl_val val = %s;\n' % (' ' * (level), src)) + c_file.write('%sif (val != NULL) {\n' % (' ' * (level))) + c_file.write('%s%s = safe_malloc(sizeof(%s));\n' % + (' ' * (level + 1), dest, helpers.getMapCTypes(num_type))) + c_file.write('%sint invalid = common_safe_%s(YAJL_GET_NUMBER(val), %s);\n' \ + % (' ' * (level + 1), num_type, dest)) + c_file.write('%sif (invalid) {\n' % (' ' * (level + 1))) + c_file.write('%s if (asprintf(err, "Invalid value \'%%s\' with type \'%s\' ' \ + 'for key \'%s\': %%s", YAJL_GET_NUMBER(val), strerror(-invalid)) < 0)\n' \ + % (' ' * (level + 1), typ, keyname)) + c_file.write('%s *err = safe_strdup("error allocating memory");\n' \ + % (' ' * (level + 1))) + c_file.write('%s free_%s(ret);\n' % (' ' * (level + 1), obj_typename)) + c_file.write('%s return NULL;\n' % (' ' * (level + 1))) + c_file.write('%s}\n' % (' ' * (level + 1))) + c_file.write('%s}\n' % (' ' * (level))) + elif typ == 'boolean': + c_file.write('%syajl_val val = %s;\n' % (' ' * (level), src)) + c_file.write('%sif (val != NULL)\n' % (' ' * (level))) + c_file.write('%s%s = YAJL_IS_TRUE(val);\n' % (' ' * (level + 1), dest)) + elif typ == 'booleanPointer': + c_file.write('%syajl_val val = %s;\n' % (' ' * (level), src)) + c_file.write('%sif (val != NULL) {\n' % (' ' * (level))) + c_file.write('%s%s = safe_malloc(sizeof(bool));\n' % (' ' * (level + 1), dest)) + c_file.write('%s*(%s) = YAJL_IS_TRUE(val);\n' % (' ' * (level + 1), dest)) + c_file.write('%s} else {\n' % (' ' * (level))) + c_file.write('%sval = get_val(tree, "%s", yajl_t_false);\n' \ + % (' ' * (level + 1), keyname)) + c_file.write('%sif (val != NULL) {\n' % (' ' * (level + 1))) + c_file.write('%s%s = safe_malloc(sizeof(bool));\n' % (' ' * (level + 2), dest)) + c_file.write('%s*(%s) = YAJL_IS_TRUE(val);\n' % (' ' * (level + 2), dest)) + c_file.write('%s}\n' % (' ' * (level + 1))) + c_file.write('%s}\n' % (' ' * (level))) + + +def jsonValueGenerator(c_file, level, src, dst, ptx, typ): + """ + Description: json value generateor + Interface: None + History: 2019-06-17 + """ + if helpers.validBasicMapName(typ): + c_file.write('%sstat = gen_%s(%s, %s, %s, err);\n' \ + % (' ' * (level), helpers.makeBasicMapName(typ), dst, src, ptx)) + c_file.write("%sif (yajl_gen_status_ok != stat)\n" % (' ' * (level))) + c_file.write("%sGEN_SET_ERROR_AND_RETURN(stat, err);\n" % (' ' * (level + 1))) + elif typ == 'string': + c_file.write('%sstat = yajl_gen_string((yajl_gen)%s, (const unsigned char *)(%s), strlen(%s));\n' \ + % (' ' * (level), dst, src, src)) + c_file.write("%sif (yajl_gen_status_ok != stat)\n" % (' ' * (level))) + c_file.write("%sGEN_SET_ERROR_AND_RETURN(stat, err);\n" % (' ' * (level + 1))) + + elif helpers.judgeDataType(typ): + if typ == 'double': + c_file.write('%sstat = yajl_gen_double((yajl_gen)%s, %s);\n' % (' ' * (level), dst, src)) + elif typ.startswith("uint") or typ == 'GID' or typ == 'UID': + c_file.write('%sstat = map_uint(%s, %s);\n' % (' ' * (level), dst, src)) + else: + c_file.write('%sstat = map_int(%s, %s);\n' % (' ' * (level), dst, src)) + c_file.write("%sif (yajl_gen_status_ok != stat)\n" % (' ' * (level))) + c_file.write("%sGEN_SET_ERROR_AND_RETURN(stat, err);\n" % (' ' * (level + 1))) + + elif typ == 'boolean': + c_file.write('%sstat = yajl_gen_bool((yajl_gen)%s, (int)(%s));\n' % (' ' * (level), dst, src)) + c_file.write("%sif (yajl_gen_status_ok != stat)\n" % (' ' * (level))) + c_file.write("%sGEN_SET_ERROR_AND_RETURN(stat, err);\n" % (' ' * (level + 1))) + + +def makeCFree(obj, c_file, prefix): + """ + Description: generate c free function + Interface: None + History: 2019-06-17 + """ + if not helpers.judgeComplex(obj.typ) or obj.subtypname: + return + + typename = helpers.getPrefixName(obj.name, prefix) + + case = obj.typ + result = { + 'mapStringObject': lambda x: [], + 'object': lambda x: x.children, + 'array': lambda x: x.subtypobj + }[case](obj) + + objs = result + if obj.typ == 'array': + if objs is None: + return + else: + typename = helpers.getNameSubstr(obj.name, prefix) + + c_file.write("void free_%s(%s *ptr) {\n" % (typename, typename)) + c_file.write(" if (ptr == NULL)\n") + c_file.write(" return;\n") + if obj.typ == 'mapStringObject': + child = obj.children[0] + if helpers.validBasicMapName(child.typ): + childname = helpers.makeBasicMapName(child.typ) + else: + if child.subtypname: + childname = child.subtypname + else: + childname = helpers.getPrefixName(child.name, prefix) + c_file.write(" if (ptr->keys != NULL && ptr->%s != NULL) {\n" % child.fixname) + c_file.write(" size_t i;\n") + c_file.write(" for (i = 0; i < ptr->len; i++) {\n") + c_file.write(" free(ptr->keys[i]);\n") + c_file.write(" ptr->keys[i] = NULL;\n") + c_file.write(" free_%s(ptr->%s[i]);\n" % (childname, child.fixname)) + c_file.write(" ptr->%s[i] = NULL;\n" % (child.fixname)) + c_file.write(" }\n") + c_file.write(" free(ptr->keys);\n") + c_file.write(" ptr->keys = NULL;\n") + c_file.write(" free(ptr->%s);\n" % (child.fixname)) + c_file.write(" ptr->%s = NULL;\n" % (child.fixname)) + c_file.write(" }\n") + + for i in objs or []: + if helpers.validBasicMapName(i.typ): + free_func = helpers.makeBasicMapName(i.typ) + c_file.write(" free_%s(ptr->%s);\n" % (free_func, i.fixname)) + c_file.write(" ptr->%s = NULL;\n" % (i.fixname)) + if i.typ == 'mapStringObject': + if i.subtypname: + free_func = i.subtypname + else: + free_func = helpers.getPrefixName(i.name, prefix) + c_file.write(" free_%s(ptr->%s);\n" % (free_func, i.fixname)) + c_file.write(" ptr->%s = NULL;\n" % (i.fixname)) + elif i.typ == 'array': + if helpers.validBasicMapName(i.subtyp): + free_func = helpers.makeBasicMapName(i.subtyp) + c_file.write(" if (ptr->%s != NULL) {\n" % i.fixname) + c_file.write(" size_t i;\n") + c_file.write(" for (i = 0; i < ptr->%s_len; i++) {\n" % i.fixname) + c_file.write(" if (ptr->%s[i] != NULL) {\n" % (i.fixname)) + c_file.write(" free_%s(ptr->%s[i]);\n" % (free_func, i.fixname)) + c_file.write(" ptr->%s[i] = NULL;\n" % (i.fixname)) + c_file.write(" }\n") + c_file.write(" }\n") + c_file.write(" free(ptr->%s);\n" % (i.fixname)) + c_file.write(" ptr->%s = NULL;\n" % (i.fixname)) + c_file.write(" }\n") + elif i.subtyp == 'string': + c_file.write(" if (ptr->%s != NULL) {\n" % i.fixname) + c_file.write(" size_t i;\n") + c_file.write(" for (i = 0; i < ptr->%s_len; i++) {\n" % i.fixname) + c_file.write(" if (ptr->%s[i] != NULL) {\n" % (i.fixname)) + c_file.write(" free(ptr->%s[i]);\n" % (i.fixname)) + c_file.write(" ptr->%s[i] = NULL;\n" % (i.fixname)) + c_file.write(" }\n") + c_file.write(" }\n") + c_file.write(" free(ptr->%s);\n" % (i.fixname)) + c_file.write(" ptr->%s = NULL;\n" % (i.fixname)) + c_file.write(" }\n") + elif not helpers.judgeComplex(i.subtyp): + c_file.write(" free(ptr->%s);\n" % (i.fixname)) + c_file.write(" ptr->%s = NULL;\n" % (i.fixname)) + elif i.subtyp == 'object' or i.subtypobj is not None: + if i.subtypname is not None: + free_func = i.subtypname + else: + free_func = helpers.getNameSubstr(i.name, prefix) + c_file.write(" if (ptr->%s != NULL) {\n" % i.fixname) + c_file.write(" size_t i;\n") + c_file.write(" for (i = 0; i < ptr->%s_len; i++)\n" % i.fixname) + c_file.write(" if (ptr->%s[i] != NULL) {\n" % (i.fixname)) + c_file.write(" free_%s(ptr->%s[i]);\n" % (free_func, i.fixname)) + c_file.write(" ptr->%s[i] = NULL;\n" % (i.fixname)) + c_file.write(" }\n") + c_file.write(" free(ptr->%s);\n" % i.fixname) + c_file.write(" ptr->%s = NULL;\n" % (i.fixname)) + c_file.write(" }\n") + + c_typ = helpers.obtainPointer(i.name, i.subtypobj, prefix) + if c_typ == "": + continue + if i.subobj is not None: + c_typ = c_typ + "_element" + c_file.write(" free_%s(ptr->%s);\n" % (c_typ, i.fixname)) + c_file.write(" ptr->%s = NULL;\n" % (i.fixname)) + else: # not array + typename = helpers.getPrefixName(i.name, prefix) + if i.typ == 'string': + c_file.write(" free(ptr->%s);\n" % (i.fixname)) + c_file.write(" ptr->%s = NULL;\n" % (i.fixname)) + elif i.typ == 'booleanPointer': + c_file.write(" free(ptr->%s);\n" % (i.fixname)) + c_file.write(" ptr->%s = NULL;\n" % (i.fixname)) + elif helpers.judgeDataPointerType(i.typ): + c_file.write(" free(ptr->%s);\n" % (i.fixname)) + c_file.write(" ptr->%s = NULL;\n" % (i.fixname)) + elif i.typ == 'object': + if i.subtypname is not None: + typename = i.subtypname + c_file.write(" if (ptr->%s != NULL) {\n" % (i.fixname)) + c_file.write(" free_%s(ptr->%s);\n" % (typename, i.fixname)) + c_file.write(" ptr->%s = NULL;\n" % (i.fixname)) + c_file.write(" }\n") + c_file.write(" free(ptr);\n") + c_file.write("}\n\n") + + +def sourceReflection(structs, schema_info, c_file, root_typ): + """ + Description: reflect code + Interface: None + History: 2019-06-17 + """ + c_file.write("// Generated from %s. Do not edit!\n" % (schema_info.name.basename)) + c_file.write("#ifndef _GNU_SOURCE\n") + c_file.write("#define _GNU_SOURCE\n") + c_file.write("#endif\n") + c_file.write('#include \n') + c_file.write('#include \n') + c_file.write('#include "securec.h"\n') + c_file.write('#include "%s"\n\n' % schema_info.header.basename) + + for i in structs: + appendCCode(i, c_file, schema_info.prefix) + + obtainCEpilogue(c_file, schema_info.prefix, root_typ) + + +def obtainCEpilogue(c_file, prefix, typ): + """ + Description: generate c language epilogue + Interface: None + History: 2019-06-17 + """ + if typ != 'array' and typ != 'object': + return + + if typ == 'array': + c_file.write("""\n +%s_element **make_%s(yajl_val tree, const struct parser_context *ctx, parser_error *err, size_t *len) { + %s_element **ptr = NULL; + size_t i, alen; + if (tree == NULL || err == NULL || !len || YAJL_GET_ARRAY(tree) == NULL) + return NULL; + *err = 0; + alen = YAJL_GET_ARRAY(tree)->len; + if (alen == 0) + return NULL; + ptr = safe_malloc((alen + 1) * sizeof(%s_element *)); + for (i = 0; i < alen; i++) { + yajl_val val = YAJL_GET_ARRAY(tree)->values[i]; + ptr[i] = make_%s_element(val, ctx, err); + if (ptr[i] == NULL) { + free_%s(ptr, alen); + return NULL; + } + } + *len = alen; + return ptr; +} +""" % (prefix, prefix, prefix, prefix, prefix, prefix)) + c_file.write("""\n +void free_%s(%s_element **ptr, size_t len) { + size_t i; + + if (ptr == NULL || len == 0) + return; + + for (i = 0; i < len; i++) { + if (ptr[i] != NULL) { + free_%s_element(ptr[i]); + ptr[i] = NULL; + } + } + free(ptr); +} +""" % (prefix, prefix, prefix)) + c_file.write("""\n +yajl_gen_status gen_%s(yajl_gen g, const %s_element **ptr, size_t len, const struct parser_context *ctx, + parser_error *err) { + yajl_gen_status stat; + size_t i; + *err = 0; + stat = yajl_gen_array_open((yajl_gen)g); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + for (i = 0; i < len; i++) { + stat = gen_%s_element(g, ptr[i], ctx, err); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = yajl_gen_array_close((yajl_gen)g); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + return yajl_gen_status_ok; +} +""" % (prefix, prefix, prefix)) + + c_file.write(""" +%s%s*%s_parse_file(const char *filename, const struct parser_context *ctx, parser_error *err%s) { + %s%s*ptr = NULL;""" % (prefix, ' ' if typ == 'object' else '_element *', \ + prefix, '' if typ == 'object' else ', size_t *len', \ + prefix, ' ' if typ == 'object' else '_element *')) + + c_file.write(""" + size_t filesize; + char *content = NULL; + + if (filename == NULL || err == NULL) + return NULL; + + *err = NULL; + content = read_file(filename, &filesize); + if (content == NULL) { + if (asprintf(err, "cannot read the file: %%s", filename) < 0) + *err = safe_strdup("error allocating memory"); + return NULL; + } + ptr = %s_parse_data(content, ctx, err%s); + free(content); + return ptr; +} +""" % (prefix, '' if typ == 'object' else ', len')) + + c_file.write(""" +%s%s*%s_parse_file_stream(FILE *stream, const struct parser_context *ctx, parser_error *err%s) { + %s%s*ptr = NULL;""" % (prefix, ' ' if typ == 'object' else '_element *', \ + prefix, '' if typ == 'object' else ', size_t *len', \ + prefix, ' ' if typ == 'object' else '_element *')) + + c_file.write(""" + size_t filesize; + char *content = NULL ; + + if (stream == NULL || err == NULL) + return NULL; + + *err = NULL; + content = fread_file(stream, &filesize); + if (content == NULL) { + *err = safe_strdup("cannot read the file"); + return NULL; + } + ptr = %s_parse_data(content, ctx, err%s); + free(content); + return ptr; +} +""" % (prefix, '' if typ == 'object' else ', len')) + + c_file.write(""" +%s%s*%s_parse_data(const char *jsondata, const struct parser_context *ctx, parser_error *err%s) { + %s%s*ptr = NULL;""" % (prefix, ' ' if typ == 'object' else '_element *', \ + prefix, '' if typ == 'object' else ', size_t *len', \ + prefix, ' ' if typ == 'object' else '_element *')) + + c_file.write(""" + yajl_val tree; + char errbuf[1024]; + struct parser_context tmp_ctx = { 0 }; + + if (jsondata == NULL || err == NULL) + return NULL; + + *err = NULL; + if (ctx == NULL) { + ctx = (const struct parser_context *)(&tmp_ctx); + } + tree = yajl_tree_parse(jsondata, errbuf, sizeof(errbuf)); + if (tree == NULL) { + if (asprintf(err, "cannot parse the data: %%s", errbuf) < 0) + *err = safe_strdup("error allocating memory"); + return NULL; + } + ptr = make_%s(tree, ctx, err%s); + yajl_tree_free(tree); + return ptr; +} +""" % (prefix, '' if typ == 'object' else ', len')) + + c_file.write("char *%s_generate_json(const %s%s*ptr%s, const struct parser_context *ctx," \ + " parser_error *err) {" % (prefix, prefix, \ + ' ' if typ == 'object' else '_element *', \ + '' if typ == 'object' else ', size_t len')) + + c_file.write(""" + yajl_gen g = NULL; + struct parser_context tmp_ctx = { 0 }; + const unsigned char *gen_buf = NULL; + char *json_buf = NULL; + size_t gen_len = 0; + + if (ptr == NULL || err == NULL) + return NULL; + + *err = NULL; + if (ctx == NULL) { + ctx = (const struct parser_context *)(&tmp_ctx); + } + + if (!json_gen_init(&g, ctx)) { + *err = safe_strdup("Json_gen init failed"); + goto out; + } + if (yajl_gen_status_ok != gen_%s(g, ptr%s, ctx, err)) { + if (*err == NULL) + *err = safe_strdup("Failed to generate json"); + goto free_out; + } + yajl_gen_get_buf(g, &gen_buf, &gen_len); + if (gen_buf == NULL) { + *err = safe_strdup("Error to get generated json"); + goto free_out; + } + + json_buf = safe_malloc(gen_len + 1); + if (memcpy_s(json_buf, gen_len + 1, gen_buf, gen_len) != EOK) { + *err = safe_strdup("Error to memcpy json"); + free(json_buf); + json_buf = NULL; + goto free_out; + } + json_buf[gen_len] = '\\0'; + +free_out: + yajl_gen_clear(g); + yajl_gen_free(g); +out: + return json_buf; +} + +""" % (prefix, '' if typ == 'object' else ', len')) diff --git a/src/log.c b/src/log.c new file mode 100644 index 0000000..54a83af --- /dev/null +++ b/src/log.c @@ -0,0 +1,393 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. + * clibcni licensed under the Mulan PSL v1. + * You can use this software according to the terms and conditions of the Mulan PSL v1. + * You may obtain a copy of Mulan PSL v1 at: + * http://license.coscl.org.cn/MulanPSL + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR + * PURPOSE. + * See the Mulan PSL v1 for more details. + * Author: wujing + * Create: 2019-4-08 + * Description: provide container log functions + ******************************************************************************/ +#define _GNU_SOURCE +#define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "securec.h" +#include "utils.h" +#include "log.h" + +const char * const g_clibcni_log_prio_name[] = { + "FATAL", "ALERT", "CRIT", "ERROR", "WARN", "NOTICE", "INFO", "DEBUG", "TRACE" +}; + +#define MAX_MSG_LENGTH 4096 +#define MAX_LOG_PREFIX_LENGTH 15 + +static __thread char *g_clibcni_log_prefix = NULL; + +static char *g_clibcni_log_vmname = NULL; +static bool g_clibcni_log_quiet = false; +static int g_clibcni_log_level = CLIBCNI_LOG_DEBUG; +static int g_clibcni_log_driver = LOG_DRIVER_STDOUT; +int g_clibcni_log_fd = -1; + +/* engine set log prefix */ +void clibcni_set_log_prefix(const char *prefix) +{ + if (prefix == NULL) { + return; + } + + free(g_clibcni_log_prefix); + g_clibcni_log_prefix = util_strdup_s(prefix); +} + +/* engine free log prefix */ +void clibcni_free_log_prefix(void) +{ + free(g_clibcni_log_prefix); + g_clibcni_log_prefix = NULL; +} + +static ssize_t write_nointr(int fd, const void *buf, size_t count); + +static void log_append_logfile(const struct clibcni_log_object_metadata *metadata, const char *timestamp, + const char *msg); + +static void log_append_stderr(const struct clibcni_log_object_metadata *metadata, const char *timestamp, + const char *msg); + +/* engine change str logdriver to enum */ +static int clibcni_change_str_logdriver_to_enum(const char *driver) +{ + if (driver == NULL) { + return LOG_DRIVER_NOSET; + } + if (strcasecmp(driver, "stdout") == 0) { + return LOG_DRIVER_STDOUT; + } + if (strcasecmp(driver, "fifo") == 0) { + return LOG_DRIVER_FIFO; + } + + return -1; +} + +#define LOG_FIFO_SIZE (1024 * 1024) + +/* open fifo */ +static int open_fifo(const char *fifo_path) +{ + int nret; + int fifo_fd = -1; + + nret = mknod(fifo_path, S_IFIFO | S_IRUSR | S_IWUSR, (dev_t)0); + if (nret && errno != EEXIST) { + COMMAND_ERROR("Mknod failed: %s", strerror(errno)); + return nret; + } + + fifo_fd = util_open(fifo_path, O_RDWR | O_NONBLOCK, 0); + if (fifo_fd == -1) { + COMMAND_ERROR("Open fifo %s failed: %s", fifo_path, strerror(errno)); + return -1; + } + + if (fcntl(fifo_fd, F_SETPIPE_SZ, LOG_FIFO_SIZE) == -1) { + COMMAND_ERROR("Set fifo buffer size failed: %s", strerror(errno)); + close(fifo_fd); + return -1; + } + + return fifo_fd; +} + +/* init log driver */ +static int init_log_driver(const struct clibcni_log_config *log) +{ + int i, driver; + + for (i = CLIBCNI_LOG_FATAL; i < CLIBCNI_LOG_MAX; i++) { + if (strcasecmp(g_clibcni_log_prio_name[i], log->priority) == 0) { + g_clibcni_log_level = i; + break; + } + } + + if (i == CLIBCNI_LOG_MAX) { + COMMAND_ERROR("Unable to parse logging level:%s", log->priority); + return -1; + } + + driver = clibcni_change_str_logdriver_to_enum(log->driver); + if (driver < 0) { + COMMAND_ERROR("Invalid log driver: %s", log->driver); + return -1; + } + g_clibcni_log_driver = driver; + return 0; +} + +static inline bool check_log_config_args(const struct clibcni_log_config *log) +{ + return (log == NULL || log->name == NULL || log->priority == NULL); +} + +static int do_check_log_configs(const struct clibcni_log_config *log) +{ + bool invalid_arg = false; + + if (check_log_config_args(log)) { + COMMAND_ERROR("Invalid arguments"); + return -1; + } + + invalid_arg = ((log->file == NULL || strcmp(log->file, "none") == 0) && (g_clibcni_log_driver == LOG_DRIVER_FIFO)); + if (invalid_arg) { + COMMAND_ERROR("Must set log file for driver %s", log->driver); + return -1; + } + return 0; +} + +/* log enable */ +int clibcni_log_enable(const struct clibcni_log_config *log) +{ + int nret = 0; + char *full_path = NULL; + + if (g_clibcni_log_fd != -1) { + COMMAND_ERROR("engine log already initialized"); + return 0; + } + + nret = do_check_log_configs(log); + if (nret != 0) { + return -1; + } + + nret = init_log_driver(log); + if (nret != 0) { + return -1; + } + + free(g_clibcni_log_vmname); + g_clibcni_log_vmname = util_strdup_s(log->name); + + g_clibcni_log_quiet = log->quiet; + full_path = util_strdup_s(log->file); + + nret = util_build_dir(full_path); + if (nret != 0) { + COMMAND_ERROR("failed to create dir for log file"); + goto out; + } + + g_clibcni_log_fd = open_fifo(full_path); + if (g_clibcni_log_fd == -1) { + nret = -1; + } + +out: + if (nret != 0) { + if (g_clibcni_log_driver == LOG_DRIVER_FIFO) { + g_clibcni_log_driver = LOG_DRIVER_NOSET; + } + } + free(full_path); + + return nret; +} + +static int do_log_append_by_driver(const struct clibcni_log_object_metadata *metadata, const char *msg, + const char *date_time) +{ + switch (g_clibcni_log_driver) { + case LOG_DRIVER_STDOUT: + if (g_clibcni_log_quiet) { + break; + } + log_append_stderr(metadata, date_time, msg); + break; + case LOG_DRIVER_FIFO: + if (g_clibcni_log_fd == -1) { + COMMAND_ERROR("Do not set log file\n"); + return -1; + } + log_append_logfile(metadata, date_time, msg); + break; + case LOG_DRIVER_NOSET: + break; + default: + COMMAND_ERROR("Invalid log driver\n"); + return -1; + } + return 0; +} + +static char *parse_timespec_to_human() +{ + struct timespec timestamp; + struct tm ptm = {0}; + char date_time[CLIBCNI_LOG_TIME_SIZE] = { 0 }; + int nret; + + if (clock_gettime(CLOCK_REALTIME, ×tamp) == -1) { + COMMAND_ERROR("Failed to get real time"); + return 0; + } + + if (localtime_r(&(timestamp.tv_sec), &ptm) == NULL) { + SYSERROR("Transfer timespec failed"); + return NULL; + } + + nret = sprintf_s(date_time, CLIBCNI_LOG_TIME_SIZE, "%04d%02d%02d%02d%02d%02d.%03ld", + ptm.tm_year + 1900, ptm.tm_mon + 1, ptm.tm_mday, ptm.tm_hour, ptm.tm_min, ptm.tm_sec, + timestamp.tv_nsec / 1000000); + + if (nret < 0) { + COMMAND_ERROR("Sprintf failed"); + return NULL; + } + + return util_strdup_s(date_time); +} + +/* use to append log to driver */ +int clibcni_log_append(const struct clibcni_log_object_metadata *metadata, const char *format, ...) +{ + int rc; + char msg[MAX_MSG_LENGTH] = { 0 }; + va_list args; + char *date_time = NULL; + int ret = 0; + + va_start(args, format); + rc = vsprintf_s(msg, MAX_MSG_LENGTH, format, args); + va_end(args); + if (rc < 0 || rc >= MAX_MSG_LENGTH) { + rc = sprintf_s(msg, MAX_MSG_LENGTH, "%s", "!!LONG LONG A LOG!!"); + if (rc < 0) { + return 0; + } + } + + date_time = parse_timespec_to_human(); + if (date_time == NULL) { + goto out; + } + + ret = do_log_append_by_driver(metadata, msg, date_time); + +out: + free(date_time); + return ret; +} + +static void do_write_log_into_file(int log_fd, char *log_msg, size_t max_len, size_t write_size) +{ + size_t size = 0; + + size = write_size; + if (size > (max_len - 1)) { + size = max_len - 1; + } + + log_msg[size] = '\n'; + + if (write_nointr(log_fd, log_msg, (size + 1)) == -1) { + COMMAND_ERROR("write log into logfile failed"); + } +} + +/* log append logfile */ +static void log_append_logfile(const struct clibcni_log_object_metadata *metadata, const char *timestamp, + const char *msg) +{ + char log_buffer[CLIBCNI_LOG_BUFFER_SIZE] = { 0 }; + int log_fd = -1; + int nret; + size_t size; + char *tmp_prefix = NULL; + + if (metadata->level > g_clibcni_log_level) { + return; + } + log_fd = g_clibcni_log_fd; + if (log_fd == -1) { + return; + } + + tmp_prefix = g_clibcni_log_prefix ? g_clibcni_log_prefix : g_clibcni_log_vmname; + if (tmp_prefix != NULL && strlen(tmp_prefix) > MAX_LOG_PREFIX_LENGTH) { + tmp_prefix = tmp_prefix + (strlen(tmp_prefix) - MAX_LOG_PREFIX_LENGTH); + } + nret = sprintf_s(log_buffer, sizeof(log_buffer), "%15s %s %-8s %s - %s:%s:%d - %s", tmp_prefix ? tmp_prefix : "", + timestamp, g_clibcni_log_prio_name[metadata->level], + g_clibcni_log_vmname ? g_clibcni_log_vmname : "clibcni", metadata->file, + metadata->func, metadata->line, msg); + + if (nret < 0) { + nret = sprintf_s(log_buffer, sizeof(log_buffer), "%15s %s %-8s %s - %s:%s:%d - %s", + tmp_prefix ? tmp_prefix : "", timestamp, g_clibcni_log_prio_name[metadata->level], + g_clibcni_log_vmname ? g_clibcni_log_vmname : "clibcni", metadata->file, + metadata->func, metadata->line, "Large log message"); + if (nret < 0) { + return; + } + } + size = (size_t)nret; + + do_write_log_into_file(log_fd, log_buffer, sizeof(log_buffer), size); +} + +/* log append stderr */ +static void log_append_stderr(const struct clibcni_log_object_metadata *metadata, const char *timestamp, + const char *msg) +{ + char *tmp_prefix = NULL; + if (metadata->level > g_clibcni_log_level) { + return; + } + + tmp_prefix = g_clibcni_log_prefix ? g_clibcni_log_prefix : g_clibcni_log_vmname; + if (tmp_prefix != NULL && strlen(tmp_prefix) > MAX_LOG_PREFIX_LENGTH) { + tmp_prefix = tmp_prefix + (strlen(tmp_prefix) - MAX_LOG_PREFIX_LENGTH); + } + COMMAND_ERROR("%15s %s %-8s ", tmp_prefix ? tmp_prefix : "", timestamp, g_clibcni_log_prio_name[metadata->level]); + COMMAND_ERROR("%s - ", (g_clibcni_log_vmname ? g_clibcni_log_vmname : "clibcni")); + COMMAND_ERROR("%s:%s:%d - ", metadata->file, metadata->func, metadata->line); + COMMAND_ERROR("%s\n", msg); +} + +/* write nointr */ +static ssize_t write_nointr(int fd, const void *buf, size_t count) +{ + ssize_t nret; + for (;;) { + nret = write(fd, buf, count); + if (nret < 0 && errno == EINTR) { + continue; + } else { + break; + } + } + return nret; +} + diff --git a/src/log.h b/src/log.h new file mode 100644 index 0000000..8882670 --- /dev/null +++ b/src/log.h @@ -0,0 +1,138 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. + * clibcni licensed under the Mulan PSL v1. + * You can use this software according to the terms and conditions of the Mulan PSL v1. + * You may obtain a copy of Mulan PSL v1 at: + * http://license.coscl.org.cn/MulanPSL + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR + * PURPOSE. + * See the Mulan PSL v1 for more details. + * Author: wujing + * Create: 2019-4-08 + * Description: provide container log functions + ******************************************************************************/ +#ifndef __CLIBCNI_LOG_H +#define __CLIBCNI_LOG_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef O_CLOEXEC +#define O_CLOEXEC 02000000 +#endif + +#ifndef F_DUPFD_CLOEXEC +#define F_DUPFD_CLOEXEC 1030 +#endif + +#define CLIBCNI_LOG_BUFFER_SIZE 4096 + +/* We're logging in seconds and nanoseconds. Assuming that the underlying + * datatype is currently at maximum a 64bit integer, we have a date string that + * is of maximum length (2^64 - 1) * 2 = (21 + 21) = 42. + * */ +#define CLIBCNI_LOG_TIME_SIZE 42 + +enum clibcni_log_drivers { + LOG_DRIVER_STDOUT, + LOG_DRIVER_FIFO, + LOG_DRIVER_SYSLOG, + LOG_DRIVER_NOSET, +}; + +enum clibcni_log_level { + CLIBCNI_LOG_FATAL = 0, + CLIBCNI_LOG_ALERT, + CLIBCNI_LOG_CRIT, + CLIBCNI_LOG_ERROR, + CLIBCNI_LOG_WARN, + CLIBCNI_LOG_NOTICE, + CLIBCNI_LOG_INFO, + CLIBCNI_LOG_DEBUG, + CLIBCNI_LOG_TRACE, + CLIBCNI_LOG_MAX +}; + +struct clibcni_log_config { + const char *name; + const char *file; + const char *priority; + const char *prefix; + const char *driver; + bool quiet; +}; + +#define CLIBCNI_LOG_LOCINFO_INIT \ + { \ + .file = __FILE__, .func = __func__, .line = __LINE__, \ + } + +/* brief logging event object */ +struct clibcni_log_object_metadata { + /* location information of the logging item */ + const char *file; + const char *func; + int line; + + int level; +}; + +int clibcni_log_enable(const struct clibcni_log_config *log); + +void clibcni_set_log_prefix(const char *prefix); + +void clibcni_free_log_prefix(void); + +int clibcni_log_append(const struct clibcni_log_object_metadata *metadata, const char *format, ...); + +#define COMMON_LOG(loglevel, format, ...) \ + do { \ + struct clibcni_log_object_metadata meta = CLIBCNI_LOG_LOCINFO_INIT; \ + meta.level = loglevel; \ + (void)clibcni_log_append(&meta, format, ##__VA_ARGS__); \ + } while (0) + +#define DEBUG(format, ...) \ + COMMON_LOG(CLIBCNI_LOG_DEBUG, format, ##__VA_ARGS__) + +#define INFO(format, ...) \ + COMMON_LOG(CLIBCNI_LOG_INFO, format, ##__VA_ARGS__) + +#define NOTICE(format, ...) \ + COMMON_LOG(CLIBCNI_LOG_NOTICE, format, ##__VA_ARGS__) + +#define WARN(format, ...) \ + COMMON_LOG(CLIBCNI_LOG_WARN, format, ##__VA_ARGS__) + +#define ERROR(format, ...) \ + COMMON_LOG(CLIBCNI_LOG_ERROR, format, ##__VA_ARGS__) + +#define CRIT(format, ...) \ + COMMON_LOG(CLIBCNI_LOG_CRIT, format, ##__VA_ARGS__) + +#define ALERT(format, ...) \ + COMMON_LOG(CLIBCNI_LOG_ALERT, format, ##__VA_ARGS__) + +#define FATAL(format, ...) \ + COMMON_LOG(CLIBCNI_LOG_FATAL, format, ##__VA_ARGS__) + +#define SYSERROR(format, ...) \ + do { \ + ERROR("%s - " format, strerror(errno), ##__VA_ARGS__); \ + } while (0) + +#define COMMAND_ERROR(fmt, args...) \ + do { \ + (void)fprintf(stderr, fmt "\n", ##args); \ + } while (0) + +#ifdef __cplusplus +} +#endif + +#endif /* __LCR_LOG_H */ diff --git a/src/types/CMakeLists.txt b/src/types/CMakeLists.txt new file mode 100644 index 0000000..bdc20cd --- /dev/null +++ b/src/types/CMakeLists.txt @@ -0,0 +1,3 @@ +# get current directory sources files +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} typessrcs) +set(TYPE_SRCS ${typessrcs} PARENT_SCOPE) diff --git a/src/types/current.c b/src/types/current.c new file mode 100644 index 0000000..2aa97e2 --- /dev/null +++ b/src/types/current.c @@ -0,0 +1,809 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. + * clibcni licensed under the Mulan PSL v1. + * You can use this software according to the terms and conditions of the Mulan PSL v1. + * You may obtain a copy of Mulan PSL v1 at: + * http://license.coscl.org.cn/MulanPSL + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR + * PURPOSE. + * See the Mulan PSL v1 for more details. + * Author: tanyifeng + * Create: 2019-04-25 + * Description: provide result functions + ********************************************************************************/ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include "current.h" +#include +#include + +#include "utils.h" +#include "log.h" + +static struct result *get_result(const result_curr *curr_result, char **err); + +static result_curr *new_curr_result_helper(const char *json_data, char **err) +{ + result_curr *result = NULL; + parser_error errmsg = NULL; + + if (json_data == NULL) { + ERROR("Json data is NULL"); + return NULL; + } + result = result_curr_parse_data(json_data, NULL, &errmsg); + if (result == NULL) { + if (asprintf(err, "parse json failed: %s", errmsg) < 0) { + *err = util_strdup_s("Out of memory"); + } + ERROR("Parse failed: %s", errmsg); + goto free_out; + } + return result; + +free_out: + free(errmsg); + return NULL; +} + +static void do_append_result_errmsg(const struct result *ret, const char *save_err, char **err) +{ + char *tmp_err = NULL; + int nret = 0; + + if (ret != NULL) { + return; + } + + tmp_err = *err; + *err = NULL; + nret = asprintf(err, "parse err: %s, convert err: %s", save_err ? save_err : "", tmp_err ? tmp_err : ""); + if (nret < 0) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + } + free(tmp_err); +} + +struct result *new_curr_result(const char *json_data, char **err) +{ + struct result *ret = NULL; + result_curr *tmp_result = NULL; + char *save_err = NULL; + + if (err == NULL) { + ERROR("Invalid argument"); + return NULL; + } + tmp_result = new_curr_result_helper(json_data, err); + if (tmp_result == NULL) { + return NULL; + } + if (*err != NULL) { + save_err = *err; + *err = NULL; + } + ret = get_result(tmp_result, err); + do_append_result_errmsg(ret, save_err, err); + + free_result_curr(tmp_result); + free(save_err); + return ret; +} + +static struct interface *convert_curr_interface(const network_interface *curr_interface) + { + struct interface *result = NULL; + + if (curr_interface == NULL) { + ERROR("Invalid argument"); + return NULL; + } + + result = util_common_calloc_s(sizeof(struct interface)); + if (result == NULL) { + ERROR("Out of memory"); + return NULL; + } + + result->name = util_strdup_s(curr_interface->name); + result->mac = util_strdup_s(curr_interface->mac); + result->sandbox = util_strdup_s(curr_interface->sandbox); + return result; + } + +static int do_parse_ipnet(const char *cidr_str, const char *ip_str, uint8_t **ip, size_t *ip_len, + struct ipnet **ipnet_val, char **err) +{ + int ret = 0; + + ret = parse_cidr(cidr_str, ipnet_val, err); + if (ret != 0) { + ERROR("Parse cidr failed: %s", *err != NULL ? *err : ""); + return -1; + } + ret = parse_ip_from_str(ip_str, ip, ip_len, err); + if (ret != 0) { + ERROR("Parse ip failed: %s", *err != NULL ? *err : ""); + free_ipnet_type(*ipnet_val); + *ipnet_val = NULL; + return -1; + } + return 0; +} + +static struct ipconfig *convert_curr_ipconfig(const network_ipconfig *curr_ipconfig, char **err) +{ + struct ipconfig *result = NULL; + struct ipnet *ipnet_val = NULL; + int ret = 0; + uint8_t *gateway = NULL; + size_t gateway_len = 0; + + if (curr_ipconfig == NULL) { + ERROR("Invalid argument"); + return NULL; + } + + result = util_common_calloc_s(sizeof(struct ipconfig)); + if (result == NULL) { + ERROR("Out of memory"); + *err = util_strdup_s("Out of memory"); + return NULL; + } + /* parse address to ipnet */ + ret = do_parse_ipnet(curr_ipconfig->address, curr_ipconfig->gateway, &gateway, &gateway_len, &ipnet_val, err); + if (ret != 0) { + goto err_out; + } + result->address = ipnet_val; + result->gateway = gateway; + result->gateway_len = gateway_len; + result->version = util_strdup_s(curr_ipconfig->version); + + if (curr_ipconfig->interface != NULL) { + result->interface = util_common_calloc_s(sizeof(int32_t)); + if (result->interface == NULL) { + ERROR("Out of memory"); + *err = util_strdup_s("Out of memory"); + goto err_out; + } + *(result->interface) = *(curr_ipconfig->interface); + } + + return result; + +err_out: + free_ipconfig_type(result); + return NULL; +} + +static struct route *convert_curr_route(const network_route *curr_route, char **err) +{ + struct route *result = NULL; + struct ipnet *dst = NULL; + int ret = 0; + uint8_t *gw = NULL; + size_t gw_len = 0; + + if (curr_route == NULL) { + ERROR("Invalid argument"); + return NULL; + } + ret = do_parse_ipnet(curr_route->dst, curr_route->gw, &gw, &gw_len, &dst, err); + if (ret != 0) { + return NULL; + } + + result = util_common_calloc_s(sizeof(struct route)); + if (result == NULL) { + ERROR("Out of memory"); + free(gw); + free_ipnet_type(dst); + *err = util_strdup_s("Out of memory"); + return NULL; + } + + result->dst = dst; + result->gw = gw; + result->gw_len = gw_len; + + return result; +} + +static struct dns *convert_curr_dns(network_dns *curr_dns, char **err) +{ + struct dns *result = NULL; + + if (curr_dns == NULL) { + *err = util_strdup_s("Empty dns argument"); + ERROR("Empty dns argument"); + return NULL; + } + + result = util_common_calloc_s(sizeof(struct dns)); + if (result == NULL) { + ERROR("Out of memory"); + *err = util_strdup_s("Out of memory"); + return NULL; + } + + result->name_servers = curr_dns->nameservers; + result->name_servers_len = curr_dns->nameservers_len; + result->domain = curr_dns->domain; + result->options = curr_dns->options; + result->options_len = curr_dns->options_len; + result->search = curr_dns->search; + result->search_len = curr_dns->search_len; + + if (memset_s(curr_dns, sizeof(network_dns), 0, sizeof(network_dns)) != EOK) { + *err = util_strdup_s("Memset failed"); + ERROR("Memset failed"); + } + + return result; +} + +static int copy_result_interface(const result_curr *curr_result, struct result *value, char **err) +{ + value->interfaces_len = curr_result->interfaces_len; + if (value->interfaces_len > 0) { + if (value->interfaces_len > (SIZE_MAX / sizeof(struct interface *))) { + *err = util_strdup_s("Too many interface"); + value->interfaces_len = 0; + ERROR("Too many interface"); + return -1; + } + value->interfaces = util_common_calloc_s(sizeof(struct interface *) * value->interfaces_len); + if (value->interfaces == NULL) { + *err = util_strdup_s("Out of memory"); + value->interfaces_len = 0; + ERROR("Out of memory"); + return -1; + } + size_t i; + for (i = 0; i < curr_result->interfaces_len; i++) { + value->interfaces[i] = convert_curr_interface(curr_result->interfaces[i]); + if (value->interfaces[i] == NULL) { + *err = util_strdup_s("Convert interfaces failed"); + value->interfaces_len = i; + ERROR("Convert interfaces failed"); + return -1; + } + } + } + return 0; +} + +static int copy_result_ips(const result_curr *curr_result, struct result *value, char **err) +{ + size_t i = 0; + value->ips_len = curr_result->ips_len; + + if (value->ips_len == 0) { + return 0; + } + + if (value->ips_len > (SIZE_MAX / sizeof(struct ipconfig *))) { + *err = util_strdup_s("Too many ips"); + ERROR("Too many ips"); + value->ips_len = 0; + return -1; + } + + value->ips = util_common_calloc_s(sizeof(struct ipconfig *) * value->ips_len); + if (value->ips == NULL) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + value->ips_len = 0; + return -1; + } + for (i = 0; i < value->ips_len; i++) { + value->ips[i] = convert_curr_ipconfig(curr_result->ips[i], err); + if (value->ips[i] == NULL) { + ERROR("Convert ips failed: %s", *err != NULL ? *err : ""); + value->ips_len = i; + return -1; + } + } + return 0; +} + +static int copy_result_routes(const result_curr *curr_result, struct result *value, char **err) +{ + size_t i = 0; + + value->routes_len = curr_result->routes_len; + if (value->routes_len == 0) { + return 0; + } + + if (value->routes_len > (SIZE_MAX / sizeof(struct route *))) { + *err = util_strdup_s("Too many routes"); + ERROR("Too many routes"); + value->routes_len = 0; + return -1; + } + + value->routes = util_common_calloc_s(sizeof(struct route *) * value->routes_len); + if (value->routes == NULL) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + value->routes_len = 0; + return -1; + } + for (i = 0; i < value->routes_len; i++) { + value->routes[i] = convert_curr_route(curr_result->routes[i], err); + if (value->routes[i] == NULL) { + ERROR("Convert routes failed: %s", *err != NULL ? *err : ""); + value->routes_len = i; + return -1; + } + } + return 0; +} + +static struct result *get_result(const result_curr *curr_result, char **err) +{ + struct result *value = NULL; + bool invalid_arg = (curr_result == NULL || err == NULL); + + if (invalid_arg) { + return NULL; + } + value = util_common_calloc_s(sizeof(struct result)); + if (value == NULL) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + return NULL; + } + + /* copy cni version */ + value->cniversion = util_strdup_s(curr_result->cni_version); + + /* copy interfaces */ + if (copy_result_interface(curr_result, value, err) != 0) { + goto free_out; + } + + /* copy ips */ + if (copy_result_ips(curr_result, value, err) != 0) { + goto free_out; + } + + /* copy routes */ + if (copy_result_routes(curr_result, value, err) != 0) { + goto free_out; + } + + /* copy dns */ + value->my_dns = convert_curr_dns(curr_result->dns, err); + if (value->my_dns == NULL) { + goto free_out; + } + + return value; +free_out: + free_result(value); + return NULL; +} + +static network_interface *interface_to_json_interface(const struct interface *src) +{ + network_interface *result = NULL; + + if (src == NULL) { + ERROR("Invalid arguments"); + return NULL; + } + + result = util_common_calloc_s(sizeof(network_interface)); + if (result == NULL) { + ERROR("Out of memory"); + return NULL; + } + + result->name = util_strdup_s(src->name); + result->mac = util_strdup_s(src->mac); + result->sandbox = util_strdup_s(src->sandbox); + + return result; +} + +static int parse_ip_and_gateway(const struct ipconfig *src, network_ipconfig *result, char **err) +{ + if (src->address != NULL) { + result->address = ipnet_to_string(src->address, err); + if (result->address == NULL) { + ERROR("Covert ipnet failed: %s", *err != NULL ? *err : ""); + return -1; + } + } + + if (src->gateway && src->gateway_len > 0) { + result->gateway = ip_to_string(src->gateway, src->gateway_len); + if (result->gateway == NULL) { + if (asprintf(err, "ip: %s to string failed", src->gateway) < 0) { + *err = util_strdup_s("ip to string failed"); + } + ERROR("IP: %s to string failed", src->gateway); + return -1; + } + } + return 0; +} + +static network_ipconfig *ipconfig_to_json_ipconfig(const struct ipconfig *src, char **err) +{ + network_ipconfig *result = NULL; + int ret = -1; + + if (src == NULL) { + ERROR("Invalid arguments"); + return result; + } + + result = util_common_calloc_s(sizeof(network_ipconfig)); + if (result == NULL) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + goto out; + } + + /* parse address and ip */ + if (parse_ip_and_gateway(src, result, err) != 0) { + goto out; + } + + if (src->version != NULL) { + result->version = util_strdup_s(src->version); + } + + if (src->interface != NULL) { + result->interface = util_common_calloc_s(sizeof(int32_t)); + if (result->interface == NULL) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + goto out; + } + *(result->interface) = *(src->interface); + } + + ret = 0; +out: + if (ret != 0) { + free_network_ipconfig(result); + result = NULL; + } + return result; +} + +static network_route *route_to_json_route(const struct route *src, char **err) +{ + network_route *result = NULL; + int ret = -1; + + if (src == NULL) { + ERROR("Invalid arguments"); + return NULL; + } + + result = (network_route *)util_common_calloc_s(sizeof(network_route)); + if (result == NULL) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + goto out; + } + + if (src->dst != NULL) { + result->dst = ipnet_to_string(src->dst, err); + if (result->dst == NULL) { + goto out; + } + } + + if (src->gw != NULL && src->gw_len > 0) { + result->gw = ip_to_string(src->gw, src->gw_len); + if (result->gw == NULL) { + *err = util_strdup_s("ip to string failed"); + ERROR("ip to string failed"); + goto out; + } + } + + ret = 0; +out: + if (ret != 0) { + free_network_route(result); + result = NULL; + } + return result; +} + +static int dns_to_json_copy_servers(const struct dns *src, network_dns *result, char **err) +{ + bool need_copy = (src->name_servers != NULL && src->name_servers_len > 0); + + if (need_copy) { + if (src->name_servers_len > (SIZE_MAX / sizeof(char *))) { + *err = util_strdup_s("Too many servers"); + ERROR("Too many servers"); + return -1; + } + + result->nameservers = (char **)util_common_calloc_s(sizeof(char *) * src->name_servers_len); + if (result->nameservers == NULL) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + return -1; + } + result->nameservers_len = src->name_servers_len; + if (memcpy_s(result->nameservers, result->nameservers_len, src->name_servers, src->name_servers_len) != EOK) { + *err = util_strdup_s("Memcpy failed"); + ERROR("Memcpy failed"); + return -1; + } + } + return 0; +} + +static int dns_to_json_copy_options(const struct dns *src, network_dns *result, char **err) +{ + bool need_copy = (src->options != NULL && src->options_len > 0); + + if (need_copy) { + if (src->options_len > (SIZE_MAX / sizeof(char *))) { + *err = util_strdup_s("Too many options"); + ERROR("Too many options"); + return -1; + } + + result->options = (char **)util_common_calloc_s(sizeof(char *) * src->options_len); + if (result->options == NULL) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + return -1; + } + result->options_len = src->options_len; + if (memcpy_s(result->options, result->options_len, src->options, src->options_len) != EOK) { + *err = util_strdup_s("Memcpy failed"); + ERROR("Memcpy failed"); + return -1; + } + } + return 0; +} + +static int dns_to_json_copy_searchs(const struct dns *src, network_dns *result, char **err) +{ + bool need_copy = (src->search != NULL && src->search_len > 0); + + if (need_copy) { + if (src->search_len > (SIZE_MAX / sizeof(char *))) { + *err = util_strdup_s("Too many searchs"); + ERROR("Too many searchs"); + return -1; + } + + result->search = (char **)util_common_calloc_s(sizeof(char *) * src->search_len); + if (result->search == NULL) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + return -1; + } + result->search_len = src->search_len; + if (memcpy_s(result->search, result->search_len, src->search, src->search_len) != EOK) { + *err = util_strdup_s("Memcpy failed"); + ERROR("Memcpy failed"); + return -1; + } + } + return 0; +} + +static int do_copy_dns_configs_to_json(const struct dns *src, network_dns *result, char **err) +{ + if (dns_to_json_copy_servers(src, result, err) != 0) { + return -1; + } + + if (dns_to_json_copy_options(src, result, err) != 0) { + return -1; + } + + if (dns_to_json_copy_searchs(src, result, err) != 0) { + return -1; + } + return 0; +} + +static network_dns *dns_to_json_dns(const struct dns *src, char **err) +{ + network_dns *result = NULL; + int ret = -1; + + if (src == NULL) { + return NULL; + } + + result = (network_dns *)util_common_calloc_s(sizeof(network_dns)); + if (result == NULL) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + goto out; + } + + if (src->domain != NULL) { + result->domain = util_strdup_s(src->domain); + } + + ret = do_copy_dns_configs_to_json(src, result, err); +out: + if (ret != 0) { + free_network_dns(result); + result = NULL; + } + return result; +} + +static bool copy_interfaces_from_result_to_json(const struct result *src, result_curr *res, char **err) +{ + size_t i = 0; + bool empty_src = (src->interfaces == NULL || src->interfaces_len == 0); + + if (empty_src) { + return true; + } + + res->interfaces_len = 0; + + if (src->interfaces_len > (SIZE_MAX / sizeof(network_interface *))) { + *err = util_strdup_s("Too many interfaces"); + ERROR("Too many interfaces"); + return false; + } + + res->interfaces = (network_interface **)util_common_calloc_s(sizeof(network_interface *) * src->interfaces_len); + if (res->interfaces == NULL) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + return false; + } + for (i = 0; i < src->interfaces_len; i++) { + if (src->interfaces[i] == NULL) { + continue; + } + res->interfaces[i] = interface_to_json_interface(src->interfaces[i]); + if (res->interfaces[i] == NULL) { + *err = util_strdup_s("interface to json struct failed"); + ERROR("interface to json struct failed"); + return false; + } + res->interfaces_len++; + } + return true; +} + +static bool copy_ips_from_result_to_json(const struct result *src, result_curr *res, char **err) +{ + bool need_copy = (src->ips && src->ips_len > 0); + + res->ips_len = 0; + if (need_copy) { + if (src->ips_len > (SIZE_MAX / sizeof(network_ipconfig *))) { + *err = util_strdup_s("Too many ips"); + ERROR("Too many ips"); + return false; + } + + res->ips = (network_ipconfig **)util_common_calloc_s(sizeof(network_ipconfig *) * src->ips_len); + if (res->ips == NULL) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + return false; + } + size_t i = 0; + for (i = 0; i < src->ips_len; i++) { + res->ips[i] = ipconfig_to_json_ipconfig(src->ips[i], err); + if (res->ips[i] == NULL) { + ERROR("parse ip failed: %s", *err != NULL ? *err : ""); + return false; + } + res->ips_len++; + } + } + return true; +} + +static bool copy_routes_from_result_to_json(const struct result *src, result_curr *res, char **err) +{ + bool need_copy = (src->routes && src->routes_len > 0); + + res->routes_len = 0; + if (need_copy) { + if (src->routes_len > (SIZE_MAX / sizeof(network_route *))) { + *err = util_strdup_s("Too many routes"); + ERROR("Too many routes"); + return false; + } + res->routes = (network_route **)util_common_calloc_s(sizeof(network_route *) * src->routes_len); + if (res->routes == NULL) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + return false; + } + size_t i = 0; + for (i = 0; i < src->routes_len; i++) { + res->routes[i] = route_to_json_route(src->routes[i], err); + if (res->routes[i] == NULL) { + ERROR("Parse route failed: %s", *err != NULL ? *err : ""); + return false; + } + res->routes_len++; + } + } + return true; +} + +static int do_result_copy_configs_to_json(const struct result *src, result_curr *res, char **err) +{ + /* copy interfaces */ + if (!copy_interfaces_from_result_to_json(src, res, err)) { + return -1; + } + + /* copy ips */ + if (!copy_ips_from_result_to_json(src, res, err)) { + return -1; + } + + /* copy routes */ + if (!copy_routes_from_result_to_json(src, res, err)) { + return -1; + } + + /* copy dns */ + if (src->my_dns != NULL) { + res->dns = dns_to_json_dns(src->my_dns, err); + if (res->dns == NULL) { + return -1; + } + } + + return 0; +} + +result_curr *result_curr_to_json_result(const struct result *src, char **err) +{ + result_curr *res = NULL; + int ret = -1; + bool invalid_arg = (src == NULL || err == NULL); + + if (invalid_arg) { + ERROR("Invalid arguments"); + return res; + } + + res = (result_curr *)util_common_calloc_s(sizeof(result_curr)); + if (res == NULL) { + ERROR("Out of memory"); + *err = util_strdup_s("Out of memory"); + goto out; + } + + /* copy cni version */ + if (src->cniversion != NULL) { + res->cni_version = util_strdup_s(src->cniversion); + } + + ret = do_result_copy_configs_to_json(src, res, err); +out: + if (ret != 0) { + free_result_curr(res); + res = NULL; + } + return res; +} + diff --git a/src/types/current.h b/src/types/current.h new file mode 100644 index 0000000..c5ecfe0 --- /dev/null +++ b/src/types/current.h @@ -0,0 +1,27 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. + * clibcni licensed under the Mulan PSL v1. + * You can use this software according to the terms and conditions of the Mulan PSL v1. + * You may obtain a copy of Mulan PSL v1 at: + * http://license.coscl.org.cn/MulanPSL + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR + * PURPOSE. + * See the Mulan PSL v1 for more details. + * Author: tanyifeng + * Create: 2019-04-25 + * Description: provide result function definition + ********************************************************************************/ +#ifndef CLIBCNI_TYPES_CURRENT_H +#define CLIBCNI_TYPES_CURRENT_H + +#include "types.h" +#include "result_curr.h" + +#define curr_implemented_spec_version "0.3.1" + +struct result *new_curr_result(const char *json_data, char **err); + +result_curr *result_curr_to_json_result(const struct result *src, char **err); + +#endif diff --git a/src/types/types.c b/src/types/types.c new file mode 100644 index 0000000..818c252 --- /dev/null +++ b/src/types/types.c @@ -0,0 +1,831 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. + * clibcni licensed under the Mulan PSL v1. + * You can use this software according to the terms and conditions of the Mulan PSL v1. + * You may obtain a copy of Mulan PSL v1 at: + * http://license.coscl.org.cn/MulanPSL + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR + * PURPOSE. + * See the Mulan PSL v1 for more details. + * Author: tanyifeng + * Create: 2019-04-25 + * Description: provide types functions + *********************************************************************************/ +#define _GNU_SOURCE /* See feature_test_macros(7) */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "types.h" +#include "utils.h" +#include "log.h" + +#define IPV4_TO_V6_EMPTY_PREFIX_BYTES 12 + +void free_ipnet_type(struct ipnet *val) +{ + if (val == NULL) { + return; + } + free(val->ip); + val->ip = NULL; + free(val->ip_mask); + val->ip_mask = NULL; + free(val); +} + +void free_ipconfig_type(struct ipconfig *ipc) +{ + if (ipc == NULL) { + return; + } + free(ipc->gateway); + ipc->gateway = NULL; + free(ipc->version); + ipc->version = NULL; + free_ipnet_type(ipc->address); + ipc->address = NULL; + free(ipc->interface); + ipc->interface = NULL; + free(ipc); +} + +void free_route_type(struct route *val) +{ + if (val == NULL) { + return; + } + free(val->gw); + val->gw = NULL; + free_ipnet_type(val->dst); + val->dst = NULL; + free(val); +} + +void free_interface_type(struct interface *val) +{ + if (val == NULL) { + return; + } + free(val->mac); + val->mac = NULL; + free(val->name); + val->name = NULL; + free(val->sandbox); + val->sandbox = NULL; + free(val); +} + +void free_dns_type(struct dns *val) +{ + size_t i = 0; + if (val == NULL) { + return; + } + free(val->domain); + val->domain = NULL; + for (i = 0; i < val->name_servers_len; i++) { + free(val->name_servers[i]); + val->name_servers[i] = NULL; + } + free(val->name_servers); + val->name_servers = NULL; + for (i = 0; i < val->options_len; i++) { + free(val->options[i]); + val->options[i] = NULL; + } + free(val->options); + val->options = NULL; + for (i = 0; i < (val->search_len); i++) { + free(val->search[i]); + val->search[i] = NULL; + } + free(val->search); + val->search = NULL; + free(val); +} + +void free_result(struct result *val) +{ + size_t i = 0; + + if (val == NULL) { + return; + } + + free(val->cniversion); + val->cniversion = NULL; + for (i = 0; i < val->interfaces_len; i++) { + free_interface_type(val->interfaces[i]); + val->interfaces[i] = NULL; + } + free(val->interfaces); + val->interfaces = NULL; + for (i = 0; i < val->ips_len; i++) { + free_ipconfig_type(val->ips[i]); + val->ips[i] = NULL; + } + free(val->ips); + val->ips = NULL; + for (i = 0; i < val->routes_len; i++) { + free_route_type(val->routes[i]); + val->routes[i] = NULL; + } + free(val->routes); + val->routes = NULL; + free_dns_type(val->my_dns); + val->my_dns = NULL; + free(val); +} + +static bool is_ipv4(const uint8_t *ip, size_t len) +{ + size_t i = 0; + bool invalid_arg = (ip == NULL || len < 10); + + if (invalid_arg) { + return false; + } + for (i = 0; i < 10; i++) { + if (ip[i] != 0) { + return false; + } + } + + return true; +} + +static int simple_mask_len(const uint8_t *mask, size_t len) +{ + size_t i = 0; + int ret = 0; + + for (i = 0; i < len; i++) { + uint8_t work = mask[i]; + if (work == 0xff) { + ret += 8; + continue; + } + + while ((work & 0x80) != 0) { + ret++; + work <<= 1; + } + + if (work != 0) { + return -1; + } + + size_t j = i; + for (j++; j < len; j++) { + if (mask[j] != 0) { + return -1; + } + } + break; + } + return ret; +} + +static size_t to_ipv4(const uint8_t *src, size_t src_len, uint8_t **ipv4) +{ + uint8_t *ip = NULL; + bool invalid_arg = (src == NULL || ipv4 == NULL); + + if (invalid_arg) { + return 0; + } + if (src_len == IPV4LEN) { + ip = util_common_calloc_s(IPV4LEN * sizeof(uint8_t)); + if (ip == NULL) { + return 0; + } + if (memcpy_s(ip, IPV4LEN, src, IPV4LEN) != EOK) { + free(ip); + return 0; + } + *ipv4 = ip; + return IPV4LEN; + } + + if (src_len == IPV6LEN && is_ipv4(src, src_len) && src[10] == 0xff && src[11] == 0xff) { + ip = util_common_calloc_s(IPV4LEN * sizeof(uint8_t)); + if (ip == NULL) { + return 0; + } + size_t i = 0; + for (i = 0; i < IPV4LEN; i++) { + ip[i] = src[i + 12]; + } + *ipv4 = ip; + return IPV4LEN; + } + return 0; +} + +// IPV6 max address "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" +#define IPV6_MAX_ADDR_LEN 40 +const char g_HEX_DICT[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + +/* + * return: + * 0 means continue to find ip + * 1 means get right ip + * -1 means something wrong + * */ +static int do_parse_ip_to_string(const uint8_t *ip, size_t len, char **result) +{ + char *tmp = NULL; + int ret = 0; + int nret = 0; + size_t res_len = 0; + + tmp = util_uint8_join("", "%x", ip, len); + if (tmp == NULL) { + return -1; + } + + if (strlen(tmp) > (SIZE_MAX - 2)) { + ret = -1; + goto free_out; + } + + res_len = 1 + strlen(tmp) + 1; + *result = util_common_calloc_s(res_len); + if (*result == NULL) { + ret = -1; + goto free_out; + } + nret = sprintf_s(*result, res_len, "%s%s", "?", tmp); + if (nret < 0) { + free(*result); + *result = NULL; + ret = -1; + } else { + ret = 1; + } + +free_out: + free(tmp); + return ret; +} + +static int get_ip_string(const uint8_t *ip, size_t len, char **result) +{ + size_t work_ip_len = 0; + uint8_t *work_ip = NULL; + int ret = 0; + + work_ip_len = to_ipv4(ip, len, &work_ip); + if (work_ip_len == IPV4LEN) { + *result = util_uint8_join(".", "%u", work_ip, work_ip_len); + ret = 1; // get right result + goto free_out; + } + if (len != IPV6LEN) { + ret = do_parse_ip_to_string(ip, len, result); + } + +free_out: + free(work_ip); + return ret; +} + +static void generate_ip_string(const uint8_t *ip, int e0, int e1, char **result) +{ + int i = 0; + int j = 0; + + *result = util_common_calloc_s(IPV6_MAX_ADDR_LEN); + if (*result == NULL) { + return; + } + for (i = 0, j = 0; i < IPV6LEN; i += 2) { + if (i == e0) { + (*result)[j++] = ':'; + (*result)[j++] = ':'; + i = e1; + if (i >= IPV6LEN) { + break; + } + } else if (i > 0) { + (*result)[j++] = ':'; + } + int nret = (ip[i] >> 4); + (*result)[j++] = g_HEX_DICT[nret]; + nret = (ip[i] & 0x0f); + (*result)[j++] = g_HEX_DICT[nret]; + } + return; +} + +char *ip_to_string(const uint8_t *ip, size_t len) +{ + char *result = NULL; + int i = 0; + int j = 0; + int e0 = 0; + int e1 = 0; + + if (len == 0) { + return util_strdup_s(""); + } + + if (get_ip_string(ip, len, &result) != 0) { + goto free_out; + } + + /* find zeros */ + e0 = e1 = -1; + for (i = 0; i < IPV6LEN; i += 2) { + j = i; + while (j < IPV6LEN && ip[j] == 0 && ip[j + 1] == 0) { + j += 2; + } + if (j > i && (j - i) > (e1 - e0)) { + e0 = i; + e1 = j; + i = j; + } + } + + if (e1 - e0 <= 2) { + e1 = -1; + e0 = -1; + } + + generate_ip_string(ip, e0, e1, &result); + +free_out: + return result; +} + +static char *mask_hex_string(const uint8_t *mask, size_t len) +{ + char *result = NULL; + size_t res_len = 0; + size_t i = 0; + size_t j = 0; + + if (len == 0) { + return util_strdup_s(""); + } + + if (len > ((SIZE_MAX - 1) / 2)) { + return NULL; + } + res_len = (len * 2) + 1; + + result = util_common_calloc_s(res_len); + if (result == NULL) { + return NULL; + } + for (i = 0, j = 0; i < len; i++) { + int tmp = (mask[i] >> 4); + result[j++] = g_HEX_DICT[tmp]; + tmp = (mask[i] & 0x0f); + result[j++] = g_HEX_DICT[tmp]; + } + return result; +} + +static size_t try_to_ipv4(const struct ipnet *value, uint8_t **pip, char **err) +{ + size_t iplen = 0; + + iplen = to_ipv4(value->ip, value->ip_len, pip); + if (iplen == 0) { + if (value->ip_len == IPV6LEN) { + *pip = util_common_calloc_s(IPV6LEN * sizeof(uint8_t)); + if (*pip == NULL) { + ERROR("Out of memory"); + *err = util_strdup_s("Out of memory"); + return 0; + } + if (memcpy_s(*pip, IPV6LEN, value->ip, IPV6LEN) != EOK) { + *err = util_strdup_s("Memcpy failed"); + free(*pip); + *pip = NULL; + ERROR("Memcpy failed"); + return 0; + } + iplen = IPV6LEN; + } else { + if (asprintf(err, "Invalid ip, len=%lu", iplen) < 0) { + ERROR("Out of memory"); + *err = util_strdup_s("Out of memory"); + } + return 0; + } + } + return iplen; +} + +static int get_ipv4_mask(const struct ipnet *value, size_t iplen, uint8_t **mask, char **err) +{ + if (iplen != IPV4LEN) { + int nret = asprintf(err, "len of IP: %lu diffrent to len of mask: %lu", iplen, value->ip_mask_len); + if (nret < 0) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + } + return 0; + } + *mask = util_common_calloc_s(IPV4LEN * sizeof(uint8_t)); + if (*mask == NULL) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + return -1; + } + if (memcpy_s(*mask, IPV4LEN, value->ip_mask, IPV4LEN) != EOK) { + *err = util_strdup_s("Memcpy failed"); + ERROR("Memcpy failed"); + return -1; + } + return IPV4LEN; +} + +static int get_ipv6_mask(const struct ipnet *value, size_t iplen, uint8_t **mask, char **err) +{ + if (iplen == IPV4LEN) { + *mask = util_common_calloc_s(IPV4LEN * sizeof(uint8_t)); + if (*mask == NULL) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + return 0; + } + if (memcpy_s(*mask, IPV4LEN, (value->ip_mask + IPV4_TO_V6_EMPTY_PREFIX_BYTES), IPV4LEN) != EOK) { + *err = util_strdup_s("Memcpy failed"); + ERROR("Memcpy failed"); + return -1; + } + return IPV4LEN; + } else { + if (memcpy_s(*mask, IPV6LEN, value->ip_mask, IPV6LEN) != EOK) { + *err = util_strdup_s("Memcpy failed"); + ERROR("Memcpy failed"); + return -1; + } + return IPV6LEN; + } +} + +static size_t try_get_mask(const struct ipnet *value, size_t iplen, uint8_t **mask, char **err) +{ + size_t masklen = 0; + int nret = 0; + + switch (value->ip_mask_len) { + case IPV4LEN: + nret = get_ipv4_mask(value, iplen, mask, err); + if (nret == 0) { + return 0; + } else if (nret < 0) { + goto free_out; + } + masklen = (size_t)nret; + break; + case IPV6LEN: + nret = get_ipv6_mask(value, iplen, mask, err); + if (nret == 0) { + return 0; + } else if (nret < 0) { + goto free_out; + } + masklen = (size_t)nret; + break; + default: + nret = asprintf(err, "Invalid mask len: %lu", value->ip_mask_len); + if (nret < 0) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + } + goto free_out; + } + return masklen; +free_out: + free(*mask); + *mask = NULL; + return 0; +} + +static char *do_generate_ip_with_mask(const uint8_t *mask, size_t masklen, const char *ip, char **err) +{ + char *tmp_mask = NULL; + char *result = NULL; + int nret = 0; + size_t res_len = 0; + + if (ip == NULL) { + return NULL; + } + tmp_mask = mask_hex_string(mask, masklen); + if (tmp_mask == NULL) { + *err = util_strdup_s("Mask toString failed"); + ERROR("Mask toString failed"); + goto free_out; + } + + if (strlen(ip) > ((SIZE_MAX - 2) - strlen(tmp_mask))) { + *err = util_strdup_s("Too long ips"); + ERROR("Too long ips"); + goto free_out; + } + + res_len = strlen(ip) + 1 + strlen(tmp_mask) + 1; + result = util_common_calloc_s(res_len); + if (result == NULL) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + goto free_out; + } + nret = sprintf_s(result, res_len, "%s/%s", ip, tmp_mask); + if (nret < 0) { + *err = util_strdup_s("Sprintf first type failed"); + ERROR("Sprintf failed"); + free(result); + result = NULL; + } +free_out: + free(tmp_mask); + return result; +} + +char *ipnet_to_string(const struct ipnet *value, char **err) +{ + char *result = NULL; + char *tmp_ip = NULL; + uint8_t *ip = NULL; + uint8_t *mask = NULL; + size_t iplen = 0; + size_t masklen = 0; + int slen = 0; + int nret = 0; + size_t res_len = 0; + + iplen = try_to_ipv4(value, &ip, err); + if (iplen == 0) { + goto free_out; + } + + masklen = try_get_mask(value, iplen, &mask, err); + if (masklen == 0) { + goto free_out; + } + + slen = simple_mask_len(mask, masklen); + tmp_ip = ip_to_string(ip, iplen); + if (tmp_ip == NULL) { + *err = util_strdup_s("IP toString failed"); + ERROR("IP toString failed"); + goto free_out; + } + if (slen == -1) { + result = do_generate_ip_with_mask(mask, masklen, tmp_ip, err); + goto free_out; + } + + if (strlen(tmp_ip) > (SIZE_MAX - 5)) { + *err = util_strdup_s("Too long ips"); + goto free_out; + } + + res_len = strlen(tmp_ip) + 1 + 3 + 1; + result = util_common_calloc_s(res_len); + if (result == NULL) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + goto free_out; + } + nret = sprintf_s(result, res_len, "%s/%d", tmp_ip, slen); + if (nret < 0) { + ERROR("Sprintf failed"); + *err = util_strdup_s("Sprintf second type failed"); + free(result); + result = NULL; + } + +free_out: + free(tmp_ip); + free(mask); + free(ip); + return result; +} + +static int get_ip_from_in6_addr(const struct in6_addr *ipv6, uint8_t **ip, size_t *len) +{ + uint8_t *result = NULL; + + if (ipv6 == NULL) { + return 0; + } + result = util_common_calloc_s(IPV6LEN * sizeof(uint8_t)); + if (result == NULL) { + ERROR("Out of memory"); + return -1; + } + if (memcpy_s(result, IPV6LEN * sizeof(uint8_t), ipv6->s6_addr, IPV6LEN * sizeof(uint8_t)) != EOK) { + ERROR("Memcpy failed"); + free(result); + return -1; + } + + *ip = result; + *len = IPV6LEN; + return 0; +} + +static int get_ip_from_in_addr(const struct in_addr *ipv4, uint8_t **ip, size_t *len) +{ + uint8_t *result = NULL; + size_t i = 0; + uint32_t work = 0; + + if (ipv4 == NULL) { + return 0; + } + result = util_common_calloc_s(IPV4LEN * sizeof(uint8_t)); + if (result == NULL) { + ERROR("Out of memory"); + return -1; + } + work = ipv4->s_addr; + for (i = 0; i < IPV4LEN; i++) { + result[i] = (uint8_t)(work & 0x0ff); + work >>= 8; + } + + *ip = result; + *len = IPV4LEN; + return 0; +} + +static int do_parse_ipv6_from_str(const char *addr, struct in6_addr *ipv6, uint8_t **ips, size_t *len, int *ret, + char **err) +{ + int nret = 0; + + nret = inet_pton(AF_INET6, addr, ipv6); + if (nret < 0) { + nret = asprintf(err, "ipv6 inet_pton %s", strerror(errno)); + if (nret < 0) { + ERROR("Sprintf failed"); + *ret = 1; + } + return -1; + } else if (nret == 0) { + nret = asprintf(err, "Invalid ip address: %s", addr); + if (nret < 0) { + ERROR("Sprintf failed"); + *ret = 1; + } + return -1; + } + + *ret = get_ip_from_in6_addr(ipv6, ips, len); + + return *ret; +} + +int parse_ip_from_str(const char *addr, uint8_t **ips, size_t *len, char **err) +{ + int nret = 0; + struct in_addr ipv4; + struct in6_addr ipv6; + int ret = -1; + + nret = inet_pton(AF_INET, addr, &ipv4); + if (nret < 0) { + nret = asprintf(err, "ipv4 inet_pton %s", strerror(errno)); + if (nret < 0) { + ERROR("Sprintf failed"); + ret = 1; + } + goto free_out; + } else if (nret == 0) { + /* check ipv6 */ + nret = do_parse_ipv6_from_str(addr, &ipv6, ips, len, &ret, err); + if (nret != 0) { + goto free_out; + } + } else { + nret = get_ip_from_in_addr(&ipv4, ips, len); + if (nret != 0) { + goto free_out; + } + } + ret = 0; +free_out: + return ret; +} + +static int do_parse_mask_in_cidr(unsigned int mask_num, struct ipnet *result, char **err) +{ + uint8_t full_mask = 0xff; + size_t j = 0; + size_t i = 0; + unsigned int mask_cnt = mask_num; + + j = result->ip_len; + + if (j >= (SIZE_MAX / sizeof(uint8_t))) { + *err = util_strdup_s("Too many ips"); + ERROR("Too many ips"); + return -1; + } + result->ip_mask = util_common_calloc_s(j * sizeof(uint8_t)); + if (result->ip_mask == NULL) { + *err = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + return -1; + } + result->ip_mask_len = j; + for (i = 0; i < j; i++) { + if (mask_cnt >= 8) { + result->ip_mask[i] = full_mask; + mask_cnt -= 8; + continue; + } + result->ip_mask[i] = ~(full_mask >> mask_cnt); + mask_cnt = 0; + } + return 0; +} + +int parse_cidr(const char *cidr_str, struct ipnet **ipnet_val, char **err) +{ + char *pos = NULL; + char *addr = NULL; + char *mask = NULL; + char *work_cidr = NULL; + int nret = 0; + unsigned int mask_num = 0; + int ret = -1; + struct ipnet *result = NULL; + + if (cidr_str == NULL) { + return -1; + } + + work_cidr = util_strdup_s(cidr_str); + + result = util_common_calloc_s(sizeof(struct ipnet)); + if (result == NULL) { + ERROR("Out of memory"); + goto free_out; + } + pos = strchr(work_cidr, '/'); + if (pos == NULL) { + nret = asprintf(err, "CIDR address %s", work_cidr); + if (nret < 0) { + ERROR("Sprintf failed"); + ret = 1; + } + goto free_out; + } + *pos = '\0'; + addr = work_cidr; + mask = pos + 1; + + nret = parse_ip_from_str(addr, &(result->ip), &(result->ip_len), err); + if (nret != 0) { + ret = -1; + goto free_out; + } + + nret = util_safe_uint(mask, &mask_num); + if (nret != 0 || (size_t)(mask_num >> 3) > result->ip_len) { + nret = asprintf(err, "Invalid CIDR address %s", cidr_str); + if (nret < 0) { + ERROR("Sprintf failed"); + *err = util_strdup_s("Asprintf cidr failed"); + ret = 1; + } + goto free_out; + } + + /* parse mask */ + if (do_parse_mask_in_cidr(mask_num, result, err) != 0) { + ret = -1; + goto free_out; + } + *ipnet_val = result; + result = NULL; + ret = 0; + +free_out: + free(work_cidr); + if (result != NULL) { + free(result->ip); + free(result->ip_mask); + free(result); + } + return ret; +} + diff --git a/src/types/types.h b/src/types/types.h new file mode 100644 index 0000000..2d93331 --- /dev/null +++ b/src/types/types.h @@ -0,0 +1,112 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. + * clibcni licensed under the Mulan PSL v1. + * You can use this software according to the terms and conditions of the Mulan PSL v1. + * You may obtain a copy of Mulan PSL v1 at: + * http://license.coscl.org.cn/MulanPSL + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR + * PURPOSE. + * See the Mulan PSL v1 for more details. + * Author: tanyifeng + * Create: 2019-04-25 + * Description: provide types function definition + *******************************************************************************/ +#ifndef CLIBCNI_TYPES_TYPES_H +#define CLIBCNI_TYPES_TYPES_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define IPV4LEN 4 + +#define IPV6LEN 16 + +/* define types for version */ +struct interface { + char *name; + char *mac; + char *sandbox; + }; + +struct ipnet { + uint8_t *ip; + size_t ip_len; + + uint8_t *ip_mask; + size_t ip_mask_len; +}; + +struct ipconfig { + char *version; + int32_t *interface; + struct ipnet *address; + + uint8_t *gateway; + size_t gateway_len; +}; + +struct route { + struct ipnet *dst; + + uint8_t *gw; + size_t gw_len; +}; + +struct dns { + char **name_servers; + size_t name_servers_len; + + char *domain; + + char **search; + size_t search_len; + + char **options; + size_t options_len; +}; + +struct result { + char *cniversion; + struct interface **interfaces; + size_t interfaces_len; + + struct ipconfig **ips; + size_t ips_len; + + struct route **routes; + size_t routes_len; + + struct dns *my_dns; +}; + +void free_ipnet_type(struct ipnet *val); + +void free_ipconfig_type(struct ipconfig *ipc); + +void free_route_type(struct route *val); + +void free_interface_type(struct interface *val); + +void free_dns_type(struct dns *val); + +void free_result(struct result *val); + +int parse_ip_from_str(const char *addr, uint8_t **ips, size_t *len, char **err); + +int parse_cidr(const char *cidr_str, struct ipnet **ipnet_val, char **err); + +/* common tool functions */ + +char *ipnet_to_string(const struct ipnet *value, char **err); + +char *ip_to_string(const uint8_t *ip, size_t len); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..2f198ca --- /dev/null +++ b/src/utils.c @@ -0,0 +1,647 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. + * clibcni licensed under the Mulan PSL v1. + * You can use this software according to the terms and conditions of the Mulan PSL v1. + * You may obtain a copy of Mulan PSL v1 at: + * http://license.coscl.org.cn/MulanPSL + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR + * PURPOSE. + * See the Mulan PSL v1 for more details. + * Author: tanyifeng + * Create: 2019-04-25 + * Description: provide util functions + *********************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "utils.h" +#include "log.h" + +#define ISSLASH(C) ((C) == '/') +#define IS_ABSOLUTE_FILE_NAME(F) (ISSLASH((F)[0])) + +char *util_strdup_s(const char *src) +{ + char *dst = NULL; + + if (src == NULL) { + return NULL; + } + + dst = strdup(src); + if (dst == NULL) { + abort(); + } + + return dst; +} + +static bool do_clean_path_continue(const char *endpos, const char *stpos, const char *respath, char **dst) +{ + bool check_dot = (endpos - stpos == 1 && stpos[0] == '.'); + bool check_dot_dot = (endpos - stpos == 2 && stpos[0] == '.' && stpos[1] == '.'); + + if (check_dot) { + return true; + } else if (check_dot_dot) { + char *dest = *dst; + if (dest <= respath + 1) { + return true; + } + for (--dest; dest > respath && !ISSLASH(dest[-1]); --dest) { + continue; + } + *dst = dest; + return true; + } + return false; +} + +static int do_clean_path(const char *respath, const char *limit_respath, const char *stpos, char **dst) +{ + char *dest = *dst; + const char *endpos = NULL; + errno_t ret; + + endpos = stpos; + + for (; *stpos; stpos = endpos) { + while (ISSLASH(*stpos)) { + ++stpos; + } + + for (endpos = stpos; *endpos && !ISSLASH(*endpos); ++endpos) { + } + + if (endpos - stpos == 0) { + break; + } else if (do_clean_path_continue(endpos, stpos, respath, &dest)) { + continue; + } + + if (!ISSLASH(dest[-1])) { + *dest++ = '/'; + } + + if (dest + (endpos - stpos) >= limit_respath) { + ERROR("Path is too long"); + if (dest > respath + 1) { + dest--; + } + *dest = '\0'; + return -1; + } + + ret = memcpy_s(dest, (size_t)(endpos - stpos), stpos, (size_t)(endpos - stpos)); + if (ret != EOK) { + ERROR("Failed at cleanpath memcpy"); + return -1; + } + dest += endpos - stpos; + *dest = '\0'; + } + *dst = dest; + return 0; +} + +static inline bool check_cleanpath_args(const char *path, const char *cleaned_path, size_t cleaned_path_len) +{ + return (path == NULL || path[0] == '\0' || cleaned_path == NULL || (cleaned_path_len < PATH_MAX)); +} + +char *cleanpath(const char *path, char *cleaned_path, size_t cleaned_path_len) +{ + char *respath = NULL; + char *dest = NULL; + const char *stpos = NULL; + const char *limit_respath = NULL; + errno_t ret; + + if (check_cleanpath_args(path, cleaned_path, cleaned_path_len)) { + return NULL; + } + + respath = cleaned_path; + + ret = memset_s(respath, cleaned_path_len, 0, cleaned_path_len); + if (ret != EOK) { + ERROR("Failed at cleanpath memset"); + goto error; + } + limit_respath = respath + PATH_MAX; + + if (!IS_ABSOLUTE_FILE_NAME(path)) { + if (!getcwd(respath, PATH_MAX)) { + ERROR("Failed to getcwd"); + respath[0] = '\0'; + goto error; + } + dest = strchr(respath, '\0'); + if (dest == NULL) { + ERROR("Failed to get the end of respath"); + goto error; + } + ret = strcat_s(respath, PATH_MAX, path); + if (ret != EOK) { + ERROR("Failed at cleanpath strcat"); + goto error; + } + stpos = path; + } else { + dest = respath; + *dest++ = '/'; + stpos = path; + } + + if (do_clean_path(respath, limit_respath, stpos, &dest)) { + goto error; + } + + if (dest > respath + 1 && ISSLASH(dest[-1])) { + --dest; + } + *dest = '\0'; + + return respath; + +error: + return NULL; +} + +bool is_null_or_empty(const char *str) +{ + return (str == NULL || strlen(str) == 0); +} + +void *util_common_calloc_s(size_t size) +{ + if (size == 0) { + return NULL; + } + + return calloc(1, size); +} + +size_t util_array_len(const char * const *array) +{ + const char * const *pos; + size_t len = 0; + + for (pos = array; pos != NULL && *pos != NULL; pos++) { + len++; + } + + return len; +} + +/* util free array */ +void util_free_array(char **array) +{ + char **p = NULL; + + if (array == NULL) { + return; + } + + for (p = array; p != NULL && *p != NULL; p++) { + free(*p); + *p = NULL; + } + free((void *)array); +} + +ssize_t util_write_nointr(int fd, const void *buf, size_t count) +{ + ssize_t n = 0; + bool empty_buf = (buf == NULL || count == 0); + + if (fd == -1) { + return -1; + } + if (empty_buf) { + return 0; + } + for (;;) { + n = write(fd, buf, count); + if (n < 0 && errno == EINTR) { + continue; + } else if (n < 0 && errno == EAGAIN) { + continue; + } else { + break; + } + } + return n; +} + +ssize_t util_read_nointr(int fd, void *buf, size_t count) +{ + ssize_t rn = 0; + bool empty_buf = (buf == NULL || count == 0); + + if (fd == -1) { + return -1; + } + if (empty_buf) { + return 0; + } + for (;;) { + rn = read(fd, buf, count); + if (rn < 0 && errno == EINTR) { + continue; + } + break; + } + + return rn; +} + +static char *do_string_join(const char *sep, const char * const *parts, size_t parts_len, size_t result_len) +{ + char *res_string = NULL; + size_t iter = 0; + + if (result_len > (SIZE_MAX - 1)) { + return NULL; + } + + res_string = util_common_calloc_s(result_len + 1); + if (res_string == NULL) { + return NULL; + } + + for (iter = 0; iter < parts_len - 1; iter++) { + if (strcat_s(res_string, result_len + 1, parts[iter]) != EOK) { + free(res_string); + return NULL; + } + if (strcat_s(res_string, result_len + 1, sep) != EOK) { + free(res_string); + return NULL; + } + } + if (strcat_s(res_string, result_len + 1, parts[parts_len - 1]) != EOK) { + free(res_string); + return NULL; + } + return res_string; +} + +static inline bool check_cni_util_string_join_args(const char *sep, const char * const *parts, size_t len) +{ + return (sep == NULL || strlen(sep) == 0 || len == 0 || parts == NULL); +} + +char *cni_util_string_join(const char *sep, const char * const *parts, size_t len) +{ + size_t sep_len = 0; + size_t result_len = 0; + size_t iter = 0; + + if (check_cni_util_string_join_args(sep, parts, len)) { + ERROR("Invalid arguments"); + return NULL; + } + + sep_len = strlen(sep); + if (len > SIZE_MAX / sep_len) { + ERROR("Large string"); + return NULL; + } + result_len = (len - 1) * sep_len; + for (iter = 0; iter < len; iter++) { + if (parts[iter] == NULL) { + return NULL; + } + result_len += strlen(parts[iter]); + } + return do_string_join(sep, parts, len, result_len); +} + +static char *do_uint8_join(const char *sep, const char *type, const uint8_t *parts, size_t parts_len, size_t result_len) +{ +#define MAX_UINT_LEN 3 + char *res_string = NULL; + size_t iter = 0; + char buffer[MAX_UINT_LEN + 1] = { 0 }; + int nret = 0; + + if (result_len > (SIZE_MAX - 1)) { + ERROR("Large string"); + return NULL; + } + + res_string = util_common_calloc_s(result_len + 1); + if (res_string == NULL) { + ERROR("Out of memory"); + return NULL; + } + + for (iter = 0; iter < parts_len - 1; iter++) { + nret = sprintf_s(buffer, sizeof(buffer), type, parts[iter]); + if (nret < 0) { + ERROR("Sprint failed"); + free(res_string); + return NULL; + } + if (strcat_s(res_string, result_len + 1, buffer) != EOK) { + ERROR("Strcat failed"); + free(res_string); + return NULL; + } + if (strcat_s(res_string, result_len + 1, sep) != EOK) { + ERROR("Strcat failed"); + free(res_string); + return NULL; + } + } + nret = sprintf_s(buffer, sizeof(buffer), type, parts[parts_len - 1]); + if (nret < 0) { + ERROR("Sprint failed"); + free(res_string); + return NULL; + } + if (strcat_s(res_string, result_len + 1, buffer) != EOK) { + free(res_string); + ERROR("Strcat failed"); + return NULL; + } + return res_string; +} + +static inline bool check_util_uint8_join_args(const char *sep, const uint8_t *parts, size_t len) +{ + return (sep == NULL || strlen(sep) == 0 || len == 0 || parts == NULL); +} + +char *util_uint8_join(const char *sep, const char *type, const uint8_t *parts, size_t len) +{ + size_t sep_len = 0; + size_t result_len = 0; + + if (check_util_uint8_join_args(sep, parts, len)) { + ERROR("Invalid arguments"); + return NULL; + } + + sep_len = strlen(sep); + if (len > SIZE_MAX / sep_len) { + ERROR("Large string"); + return NULL; + } + result_len = (len - 1) * sep_len; + + if (len > SIZE_MAX / MAX_UINT_LEN) { + ERROR("Large string"); + return NULL; + } + result_len += (MAX_UINT_LEN * len); + + return do_uint8_join(sep, type, parts, len, result_len); +} + +static inline bool check_do_util_safe_uint_args(const char *numstr, const char *err_str) +{ + return (err_str == NULL || err_str == numstr || *err_str != '\0'); +} + +static int do_util_safe_uint(const char *numstr, const char *err_str, unsigned long long ull, unsigned int *converted) +{ + if (check_do_util_safe_uint_args(numstr, err_str)) { + return -EINVAL; + } + + if (ull > UINT_MAX) { + return -ERANGE; + } + + *converted = (unsigned int)ull; + return 0; +} + +int util_safe_uint(const char *numstr, unsigned int *converted) +{ + char *err_str = NULL; + unsigned long long ull = 0; + + if (converted == NULL) { + return -1; + } + errno = 0; + ull = strtoull(numstr, &err_str, 0); + if (errno > 0) { + return -errno; + } + + return do_util_safe_uint(numstr, err_str, ull, converted); +} + +bool util_dir_exists(const char *path) +{ + struct stat s = { 0 }; + int nret = 0; + + if (path == NULL) { + return false; + } + nret = stat(path, &s); + if (nret < 0) { + return false; + } + + return S_ISDIR(s.st_mode); +} + +static int do_util_grow_array(char ***orig_array, size_t *orig_capacity, size_t size, size_t increment) +{ + size_t add_capacity = 0; + char **add_array = NULL; + + if (increment == 0) { + return 0; + } + add_capacity = *orig_capacity; + while (size + 1 > add_capacity) { + add_capacity += increment; + } + if (add_capacity != *orig_capacity) { + if (add_capacity > (SIZE_MAX / sizeof(void *))) { + return -1; + } + add_array = util_common_calloc_s(add_capacity * sizeof(void *)); + if (add_array == NULL) { + return -1; + } + if (*orig_array != NULL) { + if (memcpy_s(add_array, add_capacity * sizeof(void *), + *orig_array, *orig_capacity * sizeof(void *)) != EOK) { + free(add_array); + return -1; + } + free((void *)*orig_array); + } + + *orig_array = add_array; + *orig_capacity = add_capacity; + } + + return 0; +} + +int util_grow_array(char ***orig_array, size_t *orig_capacity, size_t size, size_t increment) +{ + if (orig_array == NULL || orig_capacity == NULL) { + return -1; + } + + if (*orig_array == NULL || *orig_capacity == 0) { + *orig_array = NULL; + *orig_capacity = 0; + } + + return do_util_grow_array(orig_array, orig_capacity, size, increment); +} + +static int do_util_validate_absolute_path(const char *path, regmatch_t *pregmatch) +{ + regex_t preg; + int nret = 0; + int status = 0; + + if (regcomp(&preg, "^(/[^/ ]*)+/?$", REG_NOSUB | REG_EXTENDED) != 0) { + nret = -1; + goto err_out; + } + + status = regexec(&preg, path, 1, pregmatch, 0); + regfree(&preg); + if (status != 0) { + nret = -1; + goto err_out; + } +err_out: + return nret; +} + +int util_validate_absolute_path(const char *path) +{ + regmatch_t regmatch; + + if (path == NULL) { + return -1; + } + + if (memset_s(®match, sizeof(regmatch_t), 0, sizeof(regmatch_t)) != EOK) { + return -1; + } + + return do_util_validate_absolute_path(path, ®match); +} + +static int do_util_validate_name(const char *name, regmatch_t *pregmatch) +{ + int nret = 0; + int status = 0; + regex_t preg; + + if (regcomp(&preg, "^([a-z0-9][-a-z0-9.]*)?[a-z0-9]$", REG_NOSUB | REG_EXTENDED) != 0) { + nret = -1; + goto err_out; + } + + status = regexec(&preg, name, 1, pregmatch, 0); + regfree(&preg); + if (status != 0) { + nret = -1; + goto err_out; + } +err_out: + return nret; +} + +static inline bool check_util_validate_name_args(const char *name) +{ +#define MAX_LEN_NAME 200 + return (name == NULL || strlen(name) > MAX_LEN_NAME); +} + +int util_validate_name(const char *name) +{ + regmatch_t regmatch; + + if (check_util_validate_name_args(name)) { + return -1; + } + + if (memset_s(®match, sizeof(regmatch_t), 0, sizeof(regmatch_t)) != EOK) { + return -1; + } + + return do_util_validate_name(name, ®match); +} + +static void set_char_to_terminator(char *p) +{ + *p = '\0'; +} + +/* + * @name is absolute path of this file. + * make all directory in this absolute path. + * */ +int util_build_dir(const char *name) +{ + char *n = NULL; // because we'll be modifying it + char *p = NULL; + char *e = NULL; + int nret = 0; + + if (name == NULL) { + ERROR("name is NULL"); + return -1; + } + n = util_strdup_s(name); + + e = &(n[strlen(n)]); + for (p = n + 1; p < e; p++) { + if (*p != '/') { + continue; + } + set_char_to_terminator(p); + nret = mkdir(n, DEFAULT_SECURE_DIRECTORY_MODE); + if (nret != 0 && (errno != EEXIST || !util_dir_exists(n))) { + SYSERROR("failed to create directory '%s'.", n); + free(n); + return -1; + } + *p = '/'; + } + free(n); + return 0; +} + +/* util open */ +int util_open(const char *filename, unsigned int flags, mode_t mode) +{ + char rpath[PATH_MAX] = { 0x00 }; + + if (cleanpath(filename, rpath, sizeof(rpath)) == NULL) { + return -1; + } + if (mode) { + return open(rpath, (int)(flags | O_CLOEXEC), (int)mode); + } else { + return open(rpath, (int)(flags | O_CLOEXEC)); + } +} + diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..619fe2e --- /dev/null +++ b/src/utils.h @@ -0,0 +1,62 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. + * clibcni licensed under the Mulan PSL v1. + * You can use this software according to the terms and conditions of the Mulan PSL v1. + * You may obtain a copy of Mulan PSL v1 at: + * http://license.coscl.org.cn/MulanPSL + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR + * PURPOSE. + * See the Mulan PSL v1 for more details. + * Author: tanyifeng + * Create: 2019-04-25 + * Description: provide util function definition + *********************************************************************************/ +#ifndef CLIBCNI_INVOKE_UTILS_H +#define CLIBCNI_INVOKE_UTILS_H + +#include +#include +#include +#include + +#define BUFFER_SIZE 4096 +#define MB (1 * 1024 * 1024) + +#define NUMSTRLEN64_MAXLEN 21 + +#define DEFAULT_SECURE_DIRECTORY_MODE 0750 + +bool is_null_or_empty(const char *str); + +size_t util_array_len(const char * const *array); + +void util_free_array(char **array); + +void *util_common_calloc_s(size_t size); + +ssize_t util_write_nointr(int fd, const void *buf, size_t count); + +ssize_t util_read_nointr(int fd, void *buf, size_t count); + +char *cni_util_string_join(const char *sep, const char * const *parts, size_t len); + +char *util_uint8_join(const char *sep, const char *type, const uint8_t *parts, size_t len); + +int util_safe_uint(const char *numstr, unsigned int *converted); + +bool util_dir_exists(const char *path); + +int util_grow_array(char ***orig_array, size_t *orig_capacity, size_t size, size_t increment); + +char *util_strdup_s(const char *src); + +int util_validate_absolute_path(const char *path); + +int util_validate_name(const char *name); + +int util_open(const char *filename, unsigned int flags, mode_t mode); + +int util_build_dir(const char *name); + +#endif diff --git a/src/version/CMakeLists.txt b/src/version/CMakeLists.txt new file mode 100644 index 0000000..6043fa4 --- /dev/null +++ b/src/version/CMakeLists.txt @@ -0,0 +1,3 @@ +# get current directory sources files +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} versionsrcs) +set(VERSION_SRCS ${versionsrcs} PARENT_SCOPE) diff --git a/src/version/version.c b/src/version/version.c new file mode 100644 index 0000000..10b9ad5 --- /dev/null +++ b/src/version/version.c @@ -0,0 +1,235 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. + * clibcni licensed under the Mulan PSL v1. + * You can use this software according to the terms and conditions of the Mulan PSL v1. + * You may obtain a copy of Mulan PSL v1 at: + * http://license.coscl.org.cn/MulanPSL + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR + * PURPOSE. + * See the Mulan PSL v1 for more details. + * Author: tanyifeng + * Create: 2019-04-25 + * Description: provide version functions + *********************************************************************************/ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include + +#include "version.h" +#include "utils.h" +#include "cni_version.h" +#include "inner_plugin_info.h" +#include "types.h" +#include "current.h" +#include "log.h" + +const char *g_curr_support_versions[3] = { "0.3.0", curr_implemented_spec_version, NULL }; + +void free_plugin_info(struct plugin_info *pinfo) +{ + if (pinfo != NULL) { + size_t i = 0; + free(pinfo->cniversion); + pinfo->cniversion = NULL; + for (i = 0; i < pinfo->supported_versions_len; i++) { + free(pinfo->supported_versions[i]); + pinfo->supported_versions[i] = NULL; + } + free(pinfo->supported_versions); + pinfo->supported_versions = NULL; + free(pinfo); + } +} + +static void convert_from_inner_plugin_info(inner_plugin_info *inner, struct plugin_info **result, char **errmsg) +{ + bool invalid_arg = (inner == NULL || result == NULL); + + if (invalid_arg) { + *errmsg = util_strdup_s("Invalid argument"); + ERROR("Invalid argument"); + return; + } + + *result = util_common_calloc_s(sizeof(struct plugin_info)); + if (*result == NULL) { + *errmsg = util_strdup_s("Out of memory"); + ERROR("Out of memory"); + return; + } + (*result)->cniversion = inner->cni_version; + inner->cni_version = NULL; + (*result)->supported_versions_len = inner->supported_versions_len; + inner->supported_versions_len = 0; + (*result)->supported_versions = inner->supported_versions; + inner->supported_versions = NULL; +} + +struct plugin_info *plugin_supports(const char * const *supported_versions, size_t len, char **errmsg) +{ + struct plugin_info *result = NULL; + size_t i = 0; + size_t size = 0; + bool invalid_arg = (supported_versions == NULL || len < 1); + + if (invalid_arg) { + *errmsg = util_strdup_s("Invalid version argument"); + return NULL; + } + + result = util_common_calloc_s(sizeof(struct plugin_info)); + if (result == NULL) { + ERROR("Out of memory"); + *errmsg = util_strdup_s("Out of memory"); + return NULL; + } + result->cniversion = util_strdup_s(current()); + + if (len > (SIZE_MAX / sizeof(char *) - 1)) { + *errmsg = util_strdup_s("Too many plugins"); + ERROR("Too many plugins"); + goto err_out; + } + + size = sizeof(char *) * (len + 1); + result->supported_versions = util_common_calloc_s(size); + if (result->supported_versions == NULL) { + ERROR("Out of memory"); + *errmsg = util_strdup_s("Out of memory"); + goto err_out; + } + + for (i = 0; i < len; i++) { + result->supported_versions[i] = util_strdup_s(supported_versions[i]); + result->supported_versions_len = i + 1; + } + + return result; +err_out: + free_plugin_info(result); + return NULL; +} + +struct plugin_info *plugin_info_decode(const char *jsonstr, char **errmsg) +{ + inner_plugin_info *pinfo = NULL; + struct plugin_info *result = NULL; + parser_error err = NULL; + const char *type020[] = { "0.1.0", "0.2.0" }; + int nret = 0; + + if (errmsg == NULL) { + return NULL; + } + if (jsonstr == NULL) { + *errmsg = util_strdup_s("empty argument"); + ERROR("Invalid arguments"); + goto out; + } + pinfo = inner_plugin_info_parse_data(jsonstr, NULL, &err); + if (pinfo == NULL) { + nret = asprintf(errmsg, "decoding version info: %s", err); + if (nret < 0) { + *errmsg = util_strdup_s("Out of memory"); + } + ERROR("decoding version info: %s", err); + goto out; + } + if (is_null_or_empty(pinfo->cni_version)) { + *errmsg = util_strdup_s("decoding version info: missing field cniVersion"); + goto out; + } + if (pinfo->supported_versions_len == 0) { + if (strcmp(pinfo->cni_version, "0.2.0") == 0) { + result = plugin_supports(type020, sizeof(type020) / sizeof(char *), errmsg); + goto out; + } + *errmsg = util_strdup_s("decoding version info: missing field supportedVersions"); + goto out; + } + + convert_from_inner_plugin_info(pinfo, &result, errmsg); +out: + free(err); + free_inner_plugin_info(pinfo); + return result; +} + +char *cniversion_decode(const char *jsonstr, char **errmsg) +{ + parser_error err = NULL; + cni_version *conf = NULL; + char *result = NULL; + int nret = 0; + + if (errmsg == NULL) { + return NULL; + } + conf = cni_version_parse_data(jsonstr, NULL, &err); + if (conf == NULL) { + nret = asprintf(errmsg, "decoding config \"%s\", failed: %s", jsonstr, err); + if (nret < 0) { + *errmsg = util_strdup_s("Out of memory"); + } + ERROR("decoding config \"%s\", failed: %s", jsonstr, err); + goto out; + } + if (conf->cni_version == NULL || strlen(conf->cni_version) == 0) { + result = util_strdup_s("0.1.0"); + goto out; + } + + result = util_strdup_s(conf->cni_version); +out: + free(err); + free_cni_version(conf); + return result; +} + +static bool check_raw(const char *version, const char **supports) +{ + const char **work = NULL; + bool invalid_arg = (version == NULL || supports == NULL); + + if (invalid_arg) { + return false; + } + + for (work = supports; *work != NULL; work++) { + if (strcmp(version, *work) == 0) { + return true; + } + } + return false; +} + +struct result_factories g_factories[1] = { + { + .supported_versions = g_curr_support_versions, + .new_result_op = &new_curr_result + } +}; + +struct result *new_result(const char *version, const char *jsonstr, char **err) +{ + size_t i = 0; + int ret = 0; + + if (err == NULL) { + return NULL; + } + for (i = 0; i < sizeof(g_factories) / sizeof(struct result_factories); i++) { + if (check_raw(version, g_factories[i].supported_versions)) { + return g_factories[i].new_result_op(jsonstr, err); + } + } + ret = asprintf(err, "unsupported CNI result version \"%s\"", version); + if (ret < 0) { + *err = util_strdup_s("Out of memory"); + } + ERROR("unsupported CNI result version \"%s\"", version); + return NULL; +} diff --git a/src/version/version.h b/src/version/version.h new file mode 100644 index 0000000..2c392a4 --- /dev/null +++ b/src/version/version.h @@ -0,0 +1,58 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. + * clibcni licensed under the Mulan PSL v1. + * You can use this software according to the terms and conditions of the Mulan PSL v1. + * You may obtain a copy of Mulan PSL v1 at: + * http://license.coscl.org.cn/MulanPSL + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR + * PURPOSE. + * See the Mulan PSL v1 for more details. + * Author: tanyifeng + * Create: 2019-04-25 + * Description: provide version function definition + *********************************************************************************/ +#ifndef CLIBCNI_VERSION_VERSION_H +#define CLIBCNI_VERSION_VERSION_H + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define CURRENT_VERSION "0.3.1" + +struct plugin_info { + char *cniversion; + + char **supported_versions; + size_t supported_versions_len; +}; + +void free_plugin_info(struct plugin_info *pinfo); + +struct plugin_info *plugin_supports(const char * const *supported_versions, size_t len, char **errmsg); + +struct plugin_info *plugin_info_decode(const char *jsonstr, char **errmsg); + +char *cniversion_decode(const char *jsonstr, char **errmsg); + +static inline const char *current() +{ + return CURRENT_VERSION; +} + +typedef struct result *(*new_result_t)(const char *json_data, char **err); + +struct result_factories { + const char **supported_versions; + new_result_t new_result_op; +}; + +struct result *new_result(const char *version, const char *jsonstr, char **err); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/tools/static_check b/tools/static_check new file mode 100755 index 0000000..9c32f93 --- /dev/null +++ b/tools/static_check @@ -0,0 +1,547 @@ +####################################################################### +##- @Copyright (C) Huawei Technologies Co., Ltd. 2019. All rights reserved. +# - clibcni licensed under the Mulan PSL v1. +# - You can use this software according to the terms and conditions of the Mulan PSL v1. +# - You may obtain a copy of Mulan PSL v1 at: +# - http://license.coscl.org.cn/MulanPSL +# - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR +# - IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR +# - PURPOSE. +# - See the Mulan PSL v1 for more details. +##- @Description: generate cetification +##- @Author: wujing +##- @Create: 2019-04-25 +####################################################################### +#!/bin/bash +# +# This script is the implementation portal for the iSulad project Personal level build static check. +# set -euxo pipefail + +CURRENT_PATH=$(pwd) +CI_TOOLS_PROJECT="/root/workspace/ci_tools" +export LOCAL_INCLUDE="/root/workspace/ci_tools/rule/include" +export EULER_CODE_PATH="$(realpath ${CURRENT_PATH}/..)" +LINT_RULE_FILE="/root/workspace/ci_tools/rule/pclint" +PCLINT_TOOL="/usr/local/bin/flint" +CODESTYLE_TOOL="/usr/local/bin/cpplint.py" +CMETRICS_TOOL="/root/cmetrics/cmetrics.py" + +function usage() { + echo -e "\ +=================================================================================================\033[1;37m + _____ ______ ___ ______ ____ ______ ______ __ __ ______ ______ __ __ + / ___//_ __// | /_ __// _// ____/ / ____// / / // ____// ____// //_/ + \__ \ / / / /| | / / / / / / / / / /_/ // __/ / / / ,< + ___/ / / / / ___ | / / _/ / / /___ / /___ / __ // /___ / /___ / /| | + /____/ /_/ /_/ |_|/_/ /___/ \____/ \____//_/ /_//_____/ \____//_/ |_| \033[0m +=================================================================================================" + echo "Usage: $0 [options]" + echo "Personal level build static check script for iSulad project" + echo "Options:" + echo " -u, --update-ci-tools Update ci tools project and replace header files with latest ones" + echo " -p, --pclint Perform pclint code static check" + echo " -s, --codestyle Perform codestyle(codedex) code static check" + echo " -c, --detail-cmetrics Detail code statistics analysis" + echo " -m, --simple-cmetrics Simple code statistics analysis" + echo " -a, --all Perform all checks and statistics" + echo " -i, --incremental-check Perform incremental check" + echo " -f, --quick-format Incremental format code by astyle/clang-format" + echo " -k, --style-check Check code style by astyle" + echo " --cpp-check Check code style by Cppcheck" + echo " -h, --help Script help information" +} + +function err() { + echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $@" >&2 +} + +function update_ci_tools() { + cd ${CI_TOOLS_PROJECT} + git fetch origin + git diff > static_check_backup.patch + git checkout . + git rebase origin/next_docker + cd ${CURRENT_PATH} + for file in $(find . -regextype posix-extended -regex ".*\.(h)") + do + cp $file $LOCAL_INCLUDE/docker/iSulad + done +} + +PCLINT_MASKED_RULE="679|826|726|322|571|522" + +function pclint_check() { + echo -e "\ +=================================================================================================\033[1;35m + ____ ______ __ ____ _ __ ______ ______ __ __ ______ ______ __ __ + / __ \ / ____// / / _// | / //_ __/ / ____// / / // ____// ____// //_/ + / /_/ // / / / / / / |/ / / / / / / /_/ // __/ / / / ,< + / ____// /___ / /___ _/ / / /| / / / / /___ / __ // /___ / /___ / /| | + /_/ \____//_____//___//_/ |_/ /_/ \____//_/ /_//_____/ \____//_/ |_| \033[0m +=================================================================================================" + local start_time=$(date +%s) + local files + if [[ ${1} == "all" ]]; then + files=$(find ./src -regextype posix-extended -regex ".*\.(c)") + else + files=$(git diff --name-only HEAD | grep -E "*.c$") + fi + files=(${files// / }) + local total=${#files[@]} + local failure_num=0 + local index=1 + for file in ${files[@]} + do + ${PCLINT_TOOL} -i ${LINT_RULE_FILE} std_clibcni.lnt $file 2>&1 | grep -E "Err|Warn|Info" | grep -vE ${PCLINT_MASKED_RULE} + if [[ $? -eq 0 ]];then + printf "[\033[1;36m%03d\033[0m\033[1;33m/\033[0m\033[1;34m%03d\033[0m]@%-80s \033[1;31m\033[5m%s\033[0m\n" \ + ${index} ${total} ${file} "[FAILED]" | sed -e 's/ /-/g' -e 's/@/ /' -e 's/-/ /' + failure_num=$((failure_num+1)) + else + printf "[\033[1;36m%03d\033[0m\033[1;33m/\033[0m\033[1;34m%03d\033[0m]@%-80s \033[1;32m%-5s\033[0m\n" \ + ${index} ${total} ${file} "[PASS]" | sed -e 's/ /-/g' -e 's/@/ /' -e 's/-/ /' + fi + index=$((index+1)) + done + printf "%0.s=" {1..96} + printf "\n" + local end_time=$(date +%s) + local duration=$((${end_time} - ${start_time})) + echo -e "\033[1;36mTotal files: ${total}\033[0m, \033[1;32msuccess: $((total-failure_num))\033[0m, \033[1;31mfailure: ${failure_num}\033[0m. \033[1;33mSpend time: ${duration} seconds\033[0m" + if [[ ${failure_num} -ne 0 ]]; then + exit -1 + fi +} + +CODESTYLE_MASKED_RULE=( + "Start-processing" + "Done-processing" + "Total-errors-found" + "\[build/header_guard\]-\[5\]" + "\[build/c++11\]-\[5\]" + "\[whitespace/indent\]-\[3\]" + "\[whitespace/braces\]-\[4\]" + "\[readability/condition\]-\[2\]" + "\[whitespace/braces\]-\[5\]" + "\[build/c\+\+11\]-\[5\]" + "\[build/include_order\]-\[4\]" + "\[readability/multiline_string\]-\[5\]" + "\[runtime/string\]-\[4\]" + "\[whitespace/semicolon\]-\[5\]" + "\[whitespace/comments\]-\[2\]" + "\[build/c\+\+11\]-\[3\]" + "\[whitespace/operators\]-\[4\]" + "\[runtime/threadsafe_fn\]-\[2\]" + "\[runtime/printf\]-\[4\]" + "\[readability/alt_tokens\]-\[2\]" +) +function codestyle_check() { + echo -e "\ +=================================================================================================\033[1;33m + ______ ____ ____ ____ _ __ ______ _____ ________ __ __ ______ + / ____// __ \ / __ \ / _// | / // ____// ___//_ __/\ \/ // / / ____/ + / / / / / // / / / / / / |/ // / __ \__ \ / / \ // / / __/ + / /___ / /_/ // /_/ /_/ / / /| // /_/ / ___/ / / / / // /___ / /___ + \____/ \____//_____//___//_/ |_/ \____/ /____/ /_/ /_//_____//_____/\033[0m +=================================================================================================" + local masked_rule=$(echo ${CODESTYLE_MASKED_RULE[@]} | sed -e "s/ /|/g" -e "s/-/ /g") + local start_time=$(date +%s) + local files + if [[ ${1} == "all" ]]; then + files=$(find ./src -regextype posix-extended -regex ".*\.(h|c|cc)") + else + files=$(git diff --name-only HEAD | grep -E "*.h$|*.c$|*.cc$") + fi + files=(${files// / }) + local total=${#files[@]} + local failure_num=0 + local index=1 + for file in ${files[@]} + do + python3 ${CODESTYLE_TOOL} $file 2>&1 | grep -vE "${masked_rule}" + if [[ $? -eq 0 ]];then + printf "[\033[1;36m%03d\033[0m\033[1;33m/\033[0m\033[1;34m%03d\033[0m]@%-80s \033[1;31m\033[5m%s\033[0m\n" \ + ${index} ${total} ${file} "[FAILED]" | sed -e 's/ /-/g' -e 's/@/ /' -e 's/-/ /' + failure_num=$((failure_num+1)) + else + printf "[\033[1;36m%03d\033[0m\033[1;33m/\033[0m\033[1;34m%03d\033[0m]@%-80s \033[1;32m%-5s\033[0m\n" \ + ${index} ${total} ${file} "[PASS]" | sed -e 's/ /-/g' -e 's/@/ /' -e 's/-/ /' + fi + index=$((index+1)) + done + printf "%0.s=" {1..96} + printf "\n" + local end_time=$(date +%s) + local duration=$((${end_time} - ${start_time})) + echo -e "\033[1;36mTotal files: ${total}\033[0m, \033[1;32msuccess: $((total-failure_num))\033[0m, \033[1;31mfailure: ${failure_num}\033[0m. \033[1;33mSpend time: ${duration} seconds\033[0m" + if [[ ${failure_num} -ne 0 ]]; then + exit -1 + fi +} + +CPPCHRECK_RULE=( + "information" + "warning" + "performance" + "style" + # "unusedFunction" + # "all" +) +CPPCHRCK_LOG="${CURRENT_PATH}/cppcheck.log" + +function cpp_check() { + echo -e "\ +=================================================================================================\033[1;33m + ______ ____ ____ ______ __ __ ______ ______ __ __ + / ____// __ \ / __ \ / ____// / / // ____// ____// //_/ + / / / /_/ // /_/ / / / / /_/ // __/ / / / ,< + / /___ / ____// ____/ / /___ / __ // /___ / /___ / /| | + \____//_/ /_/ \____//_/ /_//_____/ \____//_/ |_|\033[0m +=================================================================================================" + echo "cpp check is in progress, please wait a few seconds..." + printf "%0.s*" {1..97} + printf "\n" + local check_rule=$(echo ${CPPCHRECK_RULE[@]} | sed -e "s/ /,/g") + local start_time=$(date +%s) + result=$(cppcheck --enable=${check_rule} -j $(nproc) -i ./build ./ 2>&1 | grep -vE "^Checking|done$") + nums=$(echo "${result}" | wc -l) + echo "${result}" + local end_time=$(date +%s) + local duration=$((${end_time} - ${start_time})) + if [[ ${nums} -eq 0 ]] || [[ -z ${result} ]]; then + echo -e "\033[1;32mSuccess: clean code!\033[0m \033[1;33mSpend time: ${duration} seconds\033[0m" + else + printf "%0.s*" {1..97} + printf "\n" + echo -e "\033[1;31mFailure: There are ${nums} warnings that you need to handle\033[0m. \033[1;33mSpend time: ${duration} seconds\033[0m" + exit -1 + fi +} + +function clang_format() { + echo -e "\ +=================================================================================================\033[1;36m + ______ __ ___ _ __ ______ ______ ____ ____ __ ___ ___ ______ + / ____// / / | / | / // ____/ / ____// __ \ / __ \ / |/ // | /_ __/ + / / / / / /| | / |/ // / __ ______ / /_ / / / // /_/ // /|_/ // /| | / / + / /___ / /___ / ___ | / /| // /_/ //_____// __/ / /_/ // _, _// / / // ___ | / / + \____//_____//_/ |_|/_/ |_/ \____/ /_/ \____//_/ |_|/_/ /_//_/ |_|/_/ \033[0m] +=================================================================================================" + local start_time=$(date +%s) + local files=$(git diff --name-only HEAD | grep -E "*.h$|*.c$|*.cc$") + files=(${files// / }) + local total=${#files[@]} + local failure_num=0 + local index=1 + for file in ${files[@]} + do + clang-format -i ${file} + if [[ $? -ne 0 ]];then + printf "[\033[1;36m%03d\033[0m\033[1;33m/\033[0m\033[1;34m%03d\033[0m]@%-80s \033[1;31m\033[5m%s\033[0m\n" \ + ${index} ${total} ${file} "[FAILED]" | sed -e 's/ /-/g' -e 's/@/ /' -e 's/-/ /' + failure_num=$((failure_num+1)) + else + printf "[\033[1;36m%03d\033[0m\033[1;33m/\033[0m\033[1;34m%03d\033[0m]@%-80s \033[1;32m%-5s\033[0m\n" \ + ${index} ${total} ${file} "[PASS]" | sed -e 's/ /-/g' -e 's/@/ /' -e 's/-/ /' + fi + index=$((index+1)) + done + printf "%0.s=" {1..96} + printf "\n" + local end_time=$(date +%s) + local duration=$((${end_time} - ${start_time})) + echo -e "\033[1;36mTotal files: ${total}\033[0m, \033[1;32msuccess: $((total-failure_num))\033[0m, \033[1;31mfailure: ${failure_num}\033[0m. \033[1;33mSpend time: ${duration} seconds\033[0m" +} + +function do_astyle_fix() { + astyle --options=none --lineend=linux --mode=c \ + --style=kr \ + --add-braces \ + --indent=spaces=4 \ + --indent-preprocessor \ + --indent-col1-comments \ + --indent-switches \ + --indent-cases \ + --min-conditional-indent=0 \ + --max-instatement-indent=120 \ + --max-code-length=120 \ + --break-after-logical \ + --pad-oper \ + --pad-header \ + --unpad-paren \ + --pad-comma \ + --lineend=linux \ + --align-reference=name \ + --close-templates \ + --indent-preproc-define \ + --indent-cases \ + --indent-switches \ + --attach-namespaces \ + --attach-classes \ + --attach-extern-c \ + --attach-closing-while \ + --indent-col1-comments \ + --break-one-line-headers \ + --close-templates < "${1}" +} + +function astyle_fix() { + [[ -z "${1}" || ! -r "${1}" ]] && exit -1 + tmp="$(mktemp --tmpdir=$(dirname "${1}"))" + do_astyle_fix "${1}" > "${tmp}" + sed -i 's/\*const/\* const/g' "${tmp}" + mv "${tmp}" "${1}" +} + +function astyle_format() { + echo -e "\ +=================================================================================================\033[1;36m + ___ _____ ________ __ __ ______ ______ ____ ____ __ ___ ___ ______ + / | / ___//_ __/\ \/ // / / ____/ / ____// __ \ / __ \ / |/ // | /_ __/ + / /| | \__ \ / / \ // / / __/ ______ / /_ / / / // /_/ // /|_/ // /| | / / + / ___ | ___/ / / / / // /___ / /___/_____// __/ / /_/ // _, _// / / // ___ | / / + /_/ |_|/____/ /_/ /_//_____//_____/ /_/ \____//_/ |_|/_/ /_//_/ |_|/_/ \033[0m] +=================================================================================================" + local start_time=$(date +%s) + local files=$(find ./src -regextype posix-extended -regex ".*\.(h|c|cc)") + files=(${files// / }) + local total=${#files[@]} + local failure_num=0 + local index=1 + for file in ${files[@]} + do + astyle_fix ${file} + if [[ $? -ne 0 ]];then + printf "[\033[1;36m%03d\033[0m\033[1;33m/\033[0m\033[1;34m%03d\033[0m]@%-80s \033[1;31m\033[5m%s\033[0m\n" \ + ${index} ${total} ${file} "[FAILED]" | sed -e 's/ /-/g' -e 's/@/ /' -e 's/-/ /' + failure_num=$((failure_num+1)) + else + printf "[\033[1;36m%03d\033[0m\033[1;33m/\033[0m\033[1;34m%03d\033[0m]@%-80s \033[1;32m%-5s\033[0m\n" \ + ${index} ${total} ${file} "[PASS]" | sed -e 's/ /-/g' -e 's/@/ /' -e 's/-/ /' + fi + index=$((index+1)) + done + printf "%0.s=" {1..96} + printf "\n" + local end_time=$(date +%s) + local duration=$((${end_time} - ${start_time})) + echo -e "\033[1;36mTotal files: ${total}\033[0m, \033[1;32msuccess: $((total-failure_num))\033[0m, \033[1;31mfailure: ${failure_num}\033[0m. \033[1;33mSpend time: ${duration} seconds\033[0m" +} + +function quick_format() { + if [[ $1 == "clang-format" ]]; then + clang_format + else + astyle_format + fi +} + +function do_astyle_check() { + [[ -z "$1" || ! -r "$1" ]] && return -1 + + do_astyle_fix "$1" | diff -pu --label="$1.orig" "$1" --label="$1" - + if [[ $? -ne 0 ]]; then + return -1 + fi +} + +function style_check() { +echo -e "\ +================================================================================================= + ███████╗████████╗██╗ ██╗██╗ ███████╗ ██████╗██╗ ██╗███████╗ ██████╗██╗ ██╗ + ██╔════╝╚══██╔══╝╚██╗ ██╔╝██║ ██╔════╝ ██╔════╝██║ ██║██╔════╝██╔════╝██║ ██╔╝ + ███████╗ ██║ ╚████╔╝ ██║ █████╗ ██║ ███████║█████╗ ██║ █████╔╝ + ╚════██║ ██║ ╚██╔╝ ██║ ██╔══╝ ██║ ██╔══██║██╔══╝ ██║ ██╔═██╗ + ███████║ ██║ ██║ ███████╗███████╗ ╚██████╗██║ ██║███████╗╚██████╗██║ ██╗ + ╚══════╝ ╚═╝ ╚═╝ ╚══════╝╚══════╝ ╚═════╝╚═╝ ╚═╝╚══════╝ ╚═════╝╚═╝ ╚═╝ +=================================================================================================" + local start_time=$(date +%s) + local files + if [[ ${1} == "all" ]]; then + files=$(find ./src -regextype posix-extended -regex ".*\.(h|c|cc)") + else + files=$(git diff --name-only HEAD | grep -E "*.h$|*.c$|*.cc$") + fi + files=(${files// / }) + local total=${#files[@]} + local failure_num=0 + local index=1 + for file in ${files[@]} + do + do_astyle_check ${file} + if [[ $? -ne 0 ]];then + printf "[\033[1;36m%03d\033[0m\033[1;33m/\033[0m\033[1;34m%03d\033[0m]@%-80s \033[1;31m\033[5m%s\033[0m\n" \ + ${index} ${total} ${file} "[FAILED]" | sed -e 's/ /-/g' -e 's/@/ /' -e 's/-/ /' + failure_num=$((failure_num+1)) + else + printf "[\033[1;36m%03d\033[0m\033[1;33m/\033[0m\033[1;34m%03d\033[0m]@%-80s \033[1;32m%-5s\033[0m\n" \ + ${index} ${total} ${file} "[PASS]" | sed -e 's/ /-/g' -e 's/@/ /' -e 's/-/ /' + fi + index=$((index+1)) + done + printf "%0.s=" {1..96} + printf "\n" + local end_time=$(date +%s) + local duration=$((${end_time} - ${start_time})) + echo -e "\033[1;36mTotal files: ${total}\033[0m, \033[1;32msuccess: $((total-failure_num))\033[0m, \033[1;31mfailure: ${failure_num}\033[0m. \033[1;33mSpend time: ${duration} seconds\033[0m" + if [[ ${failure_num} -ne 0 ]]; then + exit -1 + fi +} + +function cmetrics_check() { + echo -e "\ +=================================================================================================\033[1;36m + ______ __ ___ ______ ______ ____ ____ ______ _____ ______ __ __ ______ ______ __ __ + / ____// |/ // ____//_ __// __ \ / _// ____// ___/ / ____// / / // ____// ____// //_/ + / / / /|_/ // __/ / / / /_/ / / / / / \__ \ / / / /_/ // __/ / / / ,< + / /___ / / / // /___ / / / _, _/_/ / / /___ ___/ / / /___ / __ // /___ / /___ / /| | + \____//_/ /_//_____/ /_/ /_/ |_|/___/ \____/ /____/ \____//_/ /_//_____/ \____//_/ |_|\033[0m +=================================================================================================" + if [[ ${1} == "simple" ]]; then + printf "%0.s*" {1..97} + printf "\n" + result=$(python3 ${CMETRICS_TOOL} -fp ./src) + echo "${result}" + printf "%0.s*" {1..97} + printf "\n" + CyclomaticComplexityPerMethod=$(echo "${result}" | grep '\[\*\] Cyclomatic Complexity per Method' | awk '{print $NF}') + if [[ $(echo "${CyclomaticComplexityPerMethod} > 5" | bc) -eq 1 ]]; then + echo -e "\033[1;31mFailure: cyclomatic complexity per method(${CyclomaticComplexityPerMethod}) greater then 5\033[0m." + exit 1 + else + echo -e "\033[1;32mSuccess: cyclomatic complexity per method(${CyclomaticComplexityPerMethod}) less then 5\033[0m." + exit 0 + fi + fi + + local start_time=$(date +%s) + local files + if [[ ${1} == "all" ]]; then + files=$(find ./src -regextype posix-extended -regex ".*\.(h|c|cc)") + else + files=$(git diff --name-only HEAD | grep -E "*.h$|*.c$|*.cc$") + fi + files=(${files// / }) + local total=${#files[@]} + local failure_num=0 + local index=1 + if [[ ${total} -eq 0 ]]; then + return 0 + fi + for file in ${files[@]} + do + result=$(python3 ${CMETRICS_TOOL} -fp ${file}) + CyclomaticComplexityperMethod=$(echo "${result}" | grep "\[\*\] Cyclomatic Complexity per Method:" | awk '{print $NF}') + CyclomaticComplexityperMethod=${CyclomaticComplexityperMethod:-0} + MaximumCyclomaticComplexity=$(echo "${result}" | grep "\[\*\] Maximum Cyclomatic Complexity:" | awk '{print $NF}') + MaximumCyclomaticComplexity=${MaximumCyclomaticComplexity:-0} + MaximumDepth=$(echo "${result}" | grep "\[\*\] Maximum Depth:" | awk '{print $NF}') + MaximumDepth=${MaximumDepth:-0} + RawLines=$(echo "${result}" | grep "\[\*\] Raw Lines:" | awk '{print $NF}') + RawLines=${RawLines:-0} + if [[ ${MaximumCyclomaticComplexity} -gt 10 ]] || [[ $(echo "${CyclomaticComplexityperMethod} > 5" | bc) -eq 1 ]] || \ + [[ ${MaximumDepth} -gt 5 ]] || [[ ${RawLines} -gt 2000 ]]; then + printf "[\033[1;36m%03d\033[0m\033[1;33m/\033[0m\033[1;34m%03d\033[0m]@%-80s \033[1;31m\033[5m%s\033[0m\n" \ + ${index} ${total} ${file} "[FAILED]" | sed -e 's/ /-/g' -e 's/@/ /' -e 's/-/ /' + failure_num=$((failure_num+1)) + else + printf "[\033[1;36m%03d\033[0m\033[1;33m/\033[0m\033[1;34m%03d\033[0m]@%-80s \033[1;32m%-5s\033[0m\n" \ + ${index} ${total} ${file} "[PASS]" | sed -e 's/ /-/g' -e 's/@/ /' -e 's/-/ /' + fi + printf "%s\n%s\n%s\n %-1.3f | %3d | %2d | %4d\n%s\n" \ +"-------------------------------------------------------------------------------------------------" \ +" Cyclomatic Complexity per Method | Maximum Cyclomatic Complexity | Maximum Depth | Raw Lines " \ +"-------------------------------------------------------------------------------------------------" \ + ${CyclomaticComplexityperMethod} ${MaximumCyclomaticComplexity} ${MaximumDepth} ${RawLines} \ +"-------------------------------------------------------------------------------------------------" + index=$((index+1)) + done + printf "%0.s=" {1..97} + printf "\n" + local end_time=$(date +%s) + local duration=$((${end_time} - ${start_time})) + echo -e "\033[1;36mTotal files: ${total}\033[0m, \033[1;32msuccess: $((total-failure_num))\033[0m, \033[1;31mfailure: ${failure_num}\033[0m. \033[1;33mSpend time: ${duration} seconds\033[0m" + printf "%0.s*" {1..97} + printf "\n" + if [[ ${1} == "all" ]]; then + result=$(python3 ${CMETRICS_TOOL} -fp ./src) + else + result=$(python3 ${CMETRICS_TOOL} -fp ${files[@]}) + fi + echo "${result}" + printf "%0.s*" {1..96} + printf "\n" + CyclomaticComplexityPerMethod=$(echo "${result}" | grep '\[\*\] Cyclomatic Complexity per Method' | awk '{print $NF}') + if [[ $(echo "${CyclomaticComplexityPerMethod} > 5" | bc) -eq 1 ]]; then + echo -e "\033[1;31mFailure: cyclomatic complexity per method(${CyclomaticComplexityPerMethod}) greater then 5\033[0m." + exit 1 + else + echo -e "\033[1;32mSuccess: cyclomatic complexity per method(${CyclomaticComplexityPerMethod}) less then 5\033[0m." + exit 0 + fi +} + +function incremental_check() { + style_check "incremental" + if [[ $? -ne 0 ]]; then + exit -1 + fi + pclint_check "incremental" + if [[ $? -ne 0 ]]; then + exit -1 + fi + codestyle_check "incremental" + if [[ $? -ne 0 ]]; then + exit -1 + fi + cpp_check + if [[ $? -ne 0 ]]; then + return -1 + fi + cmetrics_check "incremental" +} + +function static_check_all() { + style_check "all" + if [[ $? -ne 0 ]]; then + return -1 + fi + pclint_check "all" + if [[ $? -ne 0 ]]; then + return -1 + fi + codestyle_check "all" + if [[ $? -ne 0 ]]; then + return -1 + fi + cpp_check + if [[ $? -ne 0 ]]; then + return -1 + fi + + cmetrics_check "simple" +} + +args=`getopt -o upscmiaf:kh --long update-ci-tools,pclint,codestyle,detail-cmetrics,simple-cmetrics,incremental-check,all,quick-format:,style-check,cpp-check,help -- "$@"` +if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi +eval set -- "$args" + +while true; do + case "$1" in + -u|--update-ci-tools) update_ci_tools || (err "failed to update ci tools project" && exit -1); shift ;; + -p|--pclint) pclint_check "all" || (err "failed to perfrom pclint code static check" && exit -1); shift ;; + -s|--codestyle) codestyle_check "all" || (err "failed to perfrom codestyle(codedex) code static check" && exit -1); shift ;; + -c|--detail-cmetrics) cmetrics_check "all" || (err "failed to perform detail checks and statistics" && exit -1); shift ;; + -m|--simple-cmetrics) cmetrics_check "simple" || (err "failed to perform simple checks and statistics" && exit -1); shift ;; + -i|--incremental-check) incremental_check || (err "failed to perform incremental check" && exit -1); shift ;; + -a|--all) static_check_all || (err "failed to perform all checks and statistics" && exit -1); shift ;; + -f|--quick-format) quick_format $2 || (err "failed to format code" && exit -1); shift 2 ;; + -k|--style-check) style_check "all" || (err "failed to check code style" && exit -1); shift ;; + --cpp-check) cpp_check || (err "failed to check code style" && exit -1); shift ;; + -h|--help) usage ; exit 0 ;; + --) shift ; break ;; + *) err "invalid parameter" ; exit -1 ;; + esac +done + diff --git a/update-version.bash b/update-version.bash new file mode 100755 index 0000000..a71157a --- /dev/null +++ b/update-version.bash @@ -0,0 +1,56 @@ +####################################################################### +##- @Copyright (C) Huawei Technologies Co., Ltd. 2019. All rights reserved. +# - clibcni licensed under the Mulan PSL v1. +# - You can use this software according to the terms and conditions of the Mulan PSL v1. +# - You may obtain a copy of Mulan PSL v1 at: +# - http://license.coscl.org.cn/MulanPSL +# - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR +# - IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR +# - PURPOSE. +# - See the Mulan PSL v1 for more details. +##- @Description: generate cetification +##- @Author: wujing +##- @Create: 2019-04-25 +####################################################################### +#!/bin/bash +# +# usage +# ./update-version.bash +topDir=$(git rev-parse --show-toplevel) +specfile="${topDir}/clibcni.spec" +CMakefile="${topDir}/CMakeLists.txt" +old_version=$(cat ${specfile} | grep "%global" | grep "_version" | awk {'print $3'}) +first_old_version=$(cat ${specfile} | grep "%global" | grep "_version" | awk {'print $3'} | awk -F "." {'print $1'}) +second_old_version=$(cat ${specfile} | grep "%global" | grep "_version" | awk {'print $3'} | awk -F "." {'print $2'}) +third_old_version=$(cat ${specfile} | grep "%global" | grep "_version" | awk {'print $3'} | awk -F "." {'print $3'}) +read -p "Which level version do you want to upgrade?[1/2/3/d/N](default:N) select:" choice +if [[ ! -n "${choice}" || ${choice} == "N" ]]; then + echo "The version number has not been modified, it is still ${old_version}" + exit 0 +fi + +if [[ ${choice} -eq "1" ]]; then + first_old_version=$(($first_old_version+1)) + second_old_version="0" + third_old_version="0" +elif [[ ${choice} -eq "2" ]]; then + second_old_version=$(($second_old_version+1)) + third_old_version="0" +elif [[ ${choice} -eq "3" ]]; then + third_old_version=$(($third_old_version+1)) +fi + +new_version=${first_old_version}.${second_old_version}.${third_old_version} + +echo "The version number has been modified: ${old_version} => ${new_version}" + +old_release=$(cat ${specfile} | grep "%global" | grep "_release" | awk {'print $3'}) +commit_id_long=`git log --pretty=oneline -1 | awk {'print $1'}` +commit_id=${commit_id_long:0:8} +new_release=`date "+%Y%m%d"`.`date "+%H%M%S"`.git$commit_id +echo "The relase version has been modified, it is ${new_release}" +sed -i "s/set(CLIBCNI_VERSION \"${old_version}\")/set(CLIBCNI_VERSION \"${new_version}\")/g" ${CMakefile} +sed -i "s/^\%global _version ${old_version}$/\%global _version ${new_version}/g" ${specfile} +sed -i "s/^\%global _release ${old_release}$/\%global _release ${new_release}/g" ${specfile} + +echo "The release number has been modified: ${old_release} => ${new_release}"