├── src ├── yajl ├── ocispec │ ├── read-file.h │ ├── validate.c │ ├── read-file.c │ ├── json_common.h │ ├── helpers.py │ └── headers.py ├── lib.rs ├── serialize.rs ├── image │ └── mod.rs └── runtime │ └── test │ └── config.test.json ├── tests ├── data │ ├── top_array_int.json │ ├── image_layout_config.json │ ├── top_array_string.json │ ├── top_double_array_int.json │ ├── top_double_array_string.json │ ├── null_value_config.json │ ├── top_double_array_refobj.json │ ├── top_double_array_obj.json │ ├── residual_image_layout_config.json │ ├── image_manifest_item.json │ ├── image_index_config.json │ ├── image_manifest.json │ ├── image_config_mapstringobject.json │ ├── image_config.json │ ├── doublearray.json │ ├── config.nocwd.json │ └── config.json ├── test-spec │ ├── basic │ │ ├── test_top_array_int.json │ │ ├── test_top_array_string.json │ │ ├── test_top_double_array_int.json │ │ ├── test_top_double_array_string.json │ │ ├── test_top_double_array_refobj.json │ │ ├── test_double_array_item.json │ │ ├── test_top_double_array_obj.json │ │ └── test_double_array.json │ └── imageManifestItems │ │ └── image-manifest-items-schema.json ├── test-2.c ├── test-5.c ├── test-7.c ├── test-11.c ├── test-6.c ├── test-3.c ├── test-8.c ├── test-9.c ├── test-4.c ├── test-1.c ├── test-12.c ├── test-13.c └── test-10.c ├── CODE-OF-CONDUCT.md ├── SECURITY.md ├── .gitmodules ├── ocispec.pc.in ├── autogen.sh ├── Cargo.toml ├── cfg.mk ├── .gitignore ├── rust-gen.js ├── configure.ac ├── .github └── workflows │ └── test.yaml ├── README.md ├── GNUmakefile ├── Cargo.lock └── Makefile.am /src/yajl: -------------------------------------------------------------------------------- 1 | ../yajl/src/api/ -------------------------------------------------------------------------------- /tests/data/top_array_int.json: -------------------------------------------------------------------------------- 1 | [1, 2, 3] 2 | -------------------------------------------------------------------------------- /tests/data/image_layout_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "imageLayoutVersion": "1.0.0" 3 | } 4 | -------------------------------------------------------------------------------- /tests/data/top_array_string.json: -------------------------------------------------------------------------------- 1 | [ 2 | "topstr1", 3 | "topstr2" 4 | ] 5 | -------------------------------------------------------------------------------- /tests/data/top_double_array_int.json: -------------------------------------------------------------------------------- 1 | [ 2 | [1, 2, 3], 3 | [1, 2], 4 | [1] 5 | ] 6 | -------------------------------------------------------------------------------- /tests/data/top_double_array_string.json: -------------------------------------------------------------------------------- 1 | [ 2 | ["doublestr11"], 3 | ["doublestr21", "doublestr22"], 4 | ["doublestr31", "doublestr32"] 5 | ] 6 | -------------------------------------------------------------------------------- /tests/data/null_value_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "imageLayoutVersion": "1.0.0", 3 | "just-key": null, 4 | "obj-item": { 5 | "key1": null 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /CODE-OF-CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## The libocispec Project Community Code of Conduct 2 | 3 | The libocispec project follows the [Containers Community Code of Conduct](https://github.com/containers/common/blob/master/CODE-OF-CONDUCT.md). 4 | -------------------------------------------------------------------------------- /tests/test-spec/basic/test_top_array_int.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "title": "top array of int", 4 | "type": "array", 5 | "items": { 6 | "type": "int32" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tests/test-spec/basic/test_top_array_string.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "title": "top array of string", 4 | "type": "array", 5 | "items": { 6 | "type": "string" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | ## Reporting a Vulnerability 2 | 3 | If you discover a potential security issue in the libocispec project we ask that 4 | you notify us directly via email to security@oci.run (not affiliated 5 | with opencontainers.org). 6 | 7 | Please do **not** create a public issue. 8 | -------------------------------------------------------------------------------- /tests/test-spec/basic/test_top_double_array_int.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "title": "top double array of string", 4 | "type": "array", 5 | "items": { 6 | "type": "array", 7 | "items": { 8 | "type": "int32" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/test-spec/basic/test_top_double_array_string.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "title": "top double array of string", 4 | "type": "array", 5 | "items": { 6 | "type": "array", 7 | "items": { 8 | "type": "string" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "runtime-spec"] 2 | path = runtime-spec 3 | url = https://github.com/opencontainers/runtime-spec 4 | [submodule "image-spec"] 5 | path = image-spec 6 | url = https://github.com/opencontainers/image-spec 7 | [submodule "yajl"] 8 | path = yajl 9 | url = https://github.com/containers/yajl.git 10 | -------------------------------------------------------------------------------- /tests/data/top_double_array_refobj.json: -------------------------------------------------------------------------------- 1 | [ 2 | [ 3 | {"item1": "test11", "item2": 11, "item3": true}, 4 | {"item1": "test12", "item2": 12, "item3": false} 5 | ], 6 | [ 7 | {"item1": "test21", "item2": 21, "item3": false}, 8 | {"item1": "test22", "item2": 22, "item3": true} 9 | ] 10 | ] 11 | -------------------------------------------------------------------------------- /tests/test-spec/basic/test_top_double_array_refobj.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "title": "top double array of ref object", 4 | "type": "array", 5 | "items": { 6 | "type": "array", 7 | "items": { 8 | "$ref": "test_double_array_item.json" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/data/top_double_array_obj.json: -------------------------------------------------------------------------------- 1 | [ 2 | [ 3 | {"first": true, "second": 11, "third": "doubleobj11"}, 4 | {"first": false, "second": 12, "third": "doubleobj12"} 5 | ], 6 | [ 7 | {"first": false, "second": 21, "third": "doubleobj21"}, 8 | {"first": true, "second": 22, "third": "doubleobj22"} 9 | ] 10 | ] 11 | -------------------------------------------------------------------------------- /tests/test-spec/basic/test_double_array_item.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "description array item object, use by double array", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "type": "object", 5 | "properties": { 6 | "item1": { 7 | "type": "string" 8 | }, 9 | "item2": { 10 | "type": "int32" 11 | }, 12 | "item3": { 13 | "type": "boolean" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ocispec.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@ 5 | 6 | Name: @PACKAGE_NAME@ 7 | Description: A library for easily parsing [OCI runtime](https://github.com/opencontainers/runtime-spec) and [OCI image](https://github.com/opencontainers/image-spec) files. 8 | Requires: yajl 9 | Version: @PACKAGE_VERSION@ 10 | Libs: -L${libdir} -locispec 11 | Cflags: -I${includedir}/ocispec 12 | -------------------------------------------------------------------------------- /tests/data/residual_image_layout_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "imageLayoutVersion": "1.0.0", 3 | "residual_int": 1, 4 | "residual_float": 1.1, 5 | "residual_string": "residual", 6 | "residual_true": true, 7 | "residual_false": false, 8 | "residual_array": [ 9 | 1, 2, 3, 4, 5, 6 10 | ], 11 | "residual_obj": { 12 | "key1": "value1", 13 | "key2": "value2", 14 | "key3": "value3" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | (cd yajl; ./autogen.sh) 4 | 5 | git submodule update --init --recursive 6 | 7 | test -n "$srcdir" || srcdir=`dirname "$0"` 8 | test -n "$srcdir" || srcdir=. 9 | 10 | olddir=`pwd` 11 | cd $srcdir 12 | 13 | if ! (autoreconf --version >/dev/null 2>&1); then 14 | echo "*** No autoreconf found, please install it ***" 15 | exit 1 16 | fi 17 | 18 | mkdir -p m4 19 | 20 | autoreconf --force --install --verbose 21 | 22 | cd $olddir 23 | test -n "$NOCONFIGURE" || "$srcdir/configure" "$@" 24 | -------------------------------------------------------------------------------- /tests/test-spec/basic/test_top_double_array_obj.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "title": "top double array of string", 4 | "type": "array", 5 | "items": { 6 | "type": "array", 7 | "items": { 8 | "type": "object", 9 | "properties": { 10 | "first": { 11 | "type": "boolean" 12 | }, 13 | "second": { 14 | "type": "int32" 15 | }, 16 | "third": { 17 | "type": "string" 18 | } 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libocispec" 3 | version = "0.1.0" 4 | authors = ["@containers "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | [features] 9 | default = ["serde", "deps-serde"] 10 | deps-serde = ["chrono/serde", "url/serde"] 11 | 12 | [dependencies] 13 | chrono = "0.4.7" 14 | serde = { version = "1.0.124", features = ["derive"], optional = true } 15 | url = "2.1.0" 16 | serde-value = "0.7.0" 17 | serde_json = "1.0.64" 18 | serde_derive = "1.0.125" 19 | -------------------------------------------------------------------------------- /tests/data/image_manifest_item.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Config":"5b117edd0b767986092e9f721ba2364951b0a271f53f1f41aff9dd1861c2d4fe.json", 4 | "RepoTags":null, 5 | "Layers":[ 6 | "e20c5c3444dd86d3e7ce9ddbc1e0cdc0ed02c08ea110612490578d05c153f1aa/layer.tar", 7 | "450bfc2e8e428446718353669503dc0d4337508d0704a28c0a06973cdf90f18b/layer.tar", 8 | "e5ffeddba503ff2220cf4587030131c2cee5aef6083df1d2559e3d576bf04c99/layer.tar", 9 | "6de415c70d8eb466e8c4db907c7c151882632fd851f38ae90a89226c3ecf21f6/layer.tar", 10 | "080c8e26809e5a3b25826ba37fff877cdd76d900857018e4b28bb0f19ab83325/layer.tar" 11 | ] 12 | } 13 | ] 14 | -------------------------------------------------------------------------------- /cfg.mk: -------------------------------------------------------------------------------- 1 | export VC_LIST_EXCEPT_DEFAULT=^(lib/.*|m4/.*|md5/.*|build-aux/.*|src/gettext\.h|.*ChangeLog)$$ 2 | 3 | local-checks-to-skip = \ 4 | sc_immutable_NEWS \ 5 | sc_copyright_check \ 6 | \ 7 | sc_program_name \ 8 | sc_bindtextdomain \ 9 | sc_error_message_period \ 10 | sc_unmarked_diagnostics \ 11 | sc_GPL_version \ 12 | sc_prohibit_always_true_header_tests \ 13 | sc_prohibit_intprops_without_use \ 14 | sc_prohibit_strcmp \ 15 | 16 | #SHELL=bash -x 17 | show-vc-list-except: 18 | @$(VC_LIST_EXCEPT) 19 | 20 | VC_LIST_ALWAYS_EXCLUDE_REGEX = ^ABOUT-NLS|maint.mk|git.mk|tests.*|COPYING$$ 21 | -------------------------------------------------------------------------------- /tests/test-spec/imageManifestItems/image-manifest-items-schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "title": "manifest item set", 4 | "type": "array", 5 | "items": { 6 | "title": "manifest item", 7 | "type": "object", 8 | "description": "manifest item for image save and load", 9 | "properties": { 10 | "Config": { 11 | "description": "", 12 | "id": "", 13 | "type": "string" 14 | }, 15 | "Layers": { 16 | "type": "array", 17 | "minItems": 1, 18 | "items": { 19 | "type": "string" 20 | } 21 | }, 22 | "RepoTags": { 23 | "type": "array", 24 | "minItems": 1, 25 | "items": { 26 | "type": "string" 27 | } 28 | }, 29 | "Parent": { 30 | "description": "", 31 | "id": "", 32 | "type": "string" 33 | } 34 | }, 35 | "required": [ 36 | "Config", 37 | "Layers" 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/ocispec/read-file.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Giuseppe Scrivano 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef READ_FILE_H 18 | #define READ_FILE_H 19 | 20 | #include 21 | #include 22 | 23 | extern char *fread_file (FILE *stream, size_t *length); 24 | 25 | extern char *read_file (const char *path, size_t *length); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /tests/data/image_index_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "manifests": [ 4 | { 5 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 6 | "size": 7143, 7 | "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f", 8 | "platform": { 9 | "architecture": "ppc64le", 10 | "os": "linux", 11 | "os.version": "1.0.0", 12 | "os.features": [ 13 | "fast", 14 | "simple" 15 | ] 16 | } 17 | }, 18 | { 19 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 20 | "size": 7682, 21 | "digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270", 22 | "platform": { 23 | "architecture": "amd64", 24 | "os": "linux" 25 | } 26 | } 27 | ], 28 | "annotations": { 29 | "com.example.key1": "value1", 30 | "com.example.key2": "value2" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/data/image_manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "config": { 4 | "mediaType": "application/vnd.oci.image.config.v1+json", 5 | "size": 1470, 6 | "digest": "sha256:c86f7763873b6c0aae22d963bab59b4f5debbed6685761b5951584f6efb0633b" 7 | }, 8 | "layers": [ 9 | { 10 | "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip", 11 | "size": 675598, 12 | "digest": "sha256:9d3dd9504c685a304985025df4ed0283e47ac9ffa9bd0326fddf4d59513f0827" 13 | }, 14 | { 15 | "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip", 16 | "size": 156, 17 | "digest": "sha256:2b689805fbd00b2db1df73fae47562faac1a626d5f61744bfe29946ecff5d73d" 18 | }, 19 | { 20 | "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip", 21 | "size": 148, 22 | "digest": "sha256:c57089565e894899735d458f0fd4bb17a0f1e0df8d72da392b85c9b35ee777cd" 23 | } 24 | ], 25 | "annotations": { 26 | "key1": "value1", 27 | "key2": "value2" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/test-2.c: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2017, 2019 Giuseppe Scrivano 2 | 3 | libocispec is free software; you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation; either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | libocispec is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with libocispec. If not, see . 15 | 16 | */ 17 | 18 | #include "config.h" 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "ocispec/runtime_spec_schema_config_schema.h" 24 | 25 | int 26 | main () 27 | { 28 | parser_error err; 29 | runtime_spec_schema_config_schema *container = runtime_spec_schema_config_schema_parse_file ("tests/data/config.nocwd.json", 0, &err); 30 | if (container != NULL) { 31 | exit (4); 32 | } 33 | exit (0); 34 | } 35 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | 3 | extern crate serde; 4 | extern crate serde_derive; 5 | extern crate serde_json; 6 | pub mod serialize; 7 | pub mod image; 8 | pub mod runtime; 9 | 10 | impl runtime::Spec { 11 | pub fn load(path: &str) -> Result { 12 | serialize::deserialize(path) 13 | } 14 | pub fn save(&self, path: &str) -> Result<(), serialize::SerializeError> { 15 | serialize::serialize(self, path) 16 | } 17 | } 18 | 19 | impl image::ImageSpec { 20 | pub fn load(path: &str) -> Result { 21 | serialize::deserialize(path) 22 | } 23 | pub fn save(&self, path: &str) -> Result<(), serialize::SerializeError> { 24 | serialize::serialize(self, path) 25 | } 26 | } 27 | 28 | #[cfg(test)] 29 | mod tests { 30 | use crate::runtime; 31 | #[test] 32 | fn test_runtime_load(){ 33 | match runtime::Spec::load("src/runtime/test/config.test.json") { 34 | Ok(_) => {}, 35 | Err(e) => panic!("{}", e), 36 | } 37 | } 38 | 39 | #[test] 40 | fn test_runtime_assert_spec(){ 41 | match runtime::Spec::load("src/runtime/test/config.test.json") { 42 | Ok(spec) => {assert_eq!(spec.oci_version, "0.5.0-dev")}, 43 | Err(e) => panic!("{}", e), 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tests/test-spec/basic/test_double_array.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "title": "manifest item set", 4 | "type": "object", 5 | "properties": { 6 | "strarrays": { 7 | "type": "array", 8 | "items": { 9 | "type": "array", 10 | "items": { 11 | "type": "string" 12 | } 13 | } 14 | }, 15 | "intarrays": { 16 | "type": "array", 17 | "items": { 18 | "type": "array", 19 | "items": { 20 | "type": "int32" 21 | } 22 | } 23 | }, 24 | "boolarrays": { 25 | "type": "array", 26 | "items": { 27 | "type": "array", 28 | "items": { 29 | "type": "boolean" 30 | } 31 | } 32 | }, 33 | "objectarrays": { 34 | "type": "array", 35 | "items": { 36 | "type": "array", 37 | "items": { 38 | "type": "object", 39 | "properties": { 40 | "first": { 41 | "type": "boolean" 42 | }, 43 | "second": { 44 | "type": "string" 45 | } 46 | } 47 | } 48 | } 49 | }, 50 | "refobjarrays": { 51 | "type": "array", 52 | "items": { 53 | "type": "array", 54 | "items": { 55 | "$ref": "test_double_array_item.json" 56 | } 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.a 2 | aclocal.m4 3 | autom4te.cache/ 4 | 5 | # Generated files in root directory 6 | /config_*.c 7 | /config_*.h 8 | /defs*.c 9 | /defs*.h 10 | /features_*.c 11 | /features_*.h 12 | /state_*.c 13 | /state_*.h 14 | /basic_test_* 15 | /test_* 16 | /*_schema.c 17 | /*_schema.h 18 | /libocispec-*.tar.* 19 | /libocispec-*/ 20 | /target/ 21 | build-aux/compile 22 | build-aux/config.guess 23 | build-aux/config.sub 24 | build-aux/depcomp 25 | build-aux/install-sh 26 | build-aux/ltmain.sh 27 | build-aux/missing 28 | build-aux/test-driver 29 | .clang-format 30 | config.h 31 | config.h.in 32 | config.h.in~ 33 | config.log 34 | config.status 35 | configure 36 | .deps/ 37 | libocispec.la 38 | libtool 39 | m4/* 40 | Makefile 41 | Makefile.in 42 | *.o 43 | ocispec.pc 44 | stamp-h1 45 | *_stamp 46 | 47 | # Generated files in src/ocispec/ 48 | src/ocispec/.dirstamp 49 | src/ocispec/.deps/ 50 | src/ocispec/__pycache__/ 51 | src/ocispec/*.lo 52 | src/ocispec/*.o 53 | src/ocispec/*_stamp 54 | src/ocispec/basic_test_* 55 | src/ocispec/test_* 56 | src/ocispec/image_manifest_* 57 | src/ocispec/image_spec_* 58 | src/ocispec/runtime_spec_* 59 | src/ocispec/*_schema.* 60 | src/ocispec/config_* 61 | src/ocispec/defs_* 62 | src/ocispec/defs.* 63 | src/ocispec/features_* 64 | src/ocispec/state_* 65 | src/ocispec/validate 66 | 67 | tests/.deps/ 68 | tests/.dirstamp 69 | tests/test-[0-9] 70 | tests/test-[0-9][0-9] 71 | tests/*.log 72 | tests/*.trs 73 | test-suite.log 74 | build-aux/config.guess~ 75 | build-aux/config.sub~ 76 | configure~ 77 | -------------------------------------------------------------------------------- /tests/data/image_config_mapstringobject.json: -------------------------------------------------------------------------------- 1 | { 2 | "created": "2015-10-31T22:22:56.015925234Z", 3 | "author": "Alyssa P. Hacker ", 4 | "architecture": "amd64", 5 | "os": "linux", 6 | "config": { 7 | "User": "1:1", 8 | "Env": [ 9 | "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 10 | "FOO=docker_is_a_really", 11 | "BAR=great_tool_you_know" 12 | ], 13 | "Entrypoint": [ 14 | "/bin/sh" 15 | ], 16 | "Cmd": [ 17 | "--foreground", 18 | "--config", 19 | "/etc/my-app.d/default.cfg" 20 | ], 21 | "Volumes": null, 22 | "ExposedPorts": { 23 | "8080/tcp": {} 24 | }, 25 | "StopSignal": "SIGKILL", 26 | "WorkingDir": "/home/alice", 27 | "Labels": { 28 | "com.example.project.git.url": "https://example.com/project.git", 29 | "com.example.project.git.commit": "45a939b2999782a3f005621a8d0f29aa387e1d6b" 30 | } 31 | }, 32 | "rootfs": { 33 | "diff_ids": [ 34 | "sha256:9d3dd9504c685a304985025df4ed0283e47ac9ffa9bd0326fddf4d59513f0827", 35 | "sha256:2b689805fbd00b2db1df73fae47562faac1a626d5f61744bfe29946ecff5d73d" 36 | ], 37 | "type": "layers" 38 | }, 39 | "history": [ 40 | { 41 | "created": "2015-10-31T22:22:54.690851953Z", 42 | "created_by": "/bin/sh -c #(nop) ADD file:a3bc1e842b69636f9df5256c49c5374fb4eef1e281fe3f282c65fb853ee171c5 in /" 43 | }, 44 | { 45 | "created": "2015-10-31T22:22:55.613815829Z", 46 | "created_by": "/bin/sh -c #(nop) CMD [\"sh\"]", 47 | "empty_layer": true 48 | } 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /tests/data/image_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "created": "2015-10-31T22:22:56.015925234Z", 3 | "author": "Alyssa P. Hacker ", 4 | "architecture": "amd64", 5 | "os": "linux", 6 | "config": { 7 | "User": "1:1", 8 | "ExposedPorts": { 9 | "8080/tcp": {} 10 | }, 11 | "Env": [ 12 | "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 13 | "FOO=docker_is_a_really", 14 | "BAR=great_tool_you_know" 15 | ], 16 | "Entrypoint": [ 17 | "/bin/sh" 18 | ], 19 | "Cmd": [ 20 | "--foreground", 21 | "--config", 22 | "/etc/my-app.d/default.cfg" 23 | ], 24 | "Volumes": { 25 | "/var/job-result-data": {}, 26 | "/var/log/my-app-logs": {} 27 | }, 28 | "StopSignal": "SIGKILL", 29 | "WorkingDir": "/home/alice", 30 | "Labels": { 31 | "com.example.project.git.url": "https://example.com/project.git", 32 | "com.example.project.git.commit": "45a939b2999782a3f005621a8d0f29aa387e1d6b" 33 | } 34 | }, 35 | "rootfs": { 36 | "diff_ids": [ 37 | "sha256:9d3dd9504c685a304985025df4ed0283e47ac9ffa9bd0326fddf4d59513f0827", 38 | "sha256:2b689805fbd00b2db1df73fae47562faac1a626d5f61744bfe29946ecff5d73d" 39 | ], 40 | "type": "layers" 41 | }, 42 | "history": [ 43 | { 44 | "created": "2015-10-31T22:22:54.690851953Z", 45 | "created_by": "/bin/sh -c #(nop) ADD file:a3bc1e842b69636f9df5256c49c5374fb4eef1e281fe3f282c65fb853ee171c5 in /" 46 | }, 47 | { 48 | "created": "2015-10-31T22:22:55.613815829Z", 49 | "created_by": "/bin/sh -c #(nop) CMD [\"sh\"]", 50 | "empty_layer": true 51 | } 52 | ] 53 | } 54 | -------------------------------------------------------------------------------- /tests/data/doublearray.json: -------------------------------------------------------------------------------- 1 | { 2 | "strarrays": [ 3 | [ 4 | "stra", 5 | "strb", 6 | "strc" 7 | ], 8 | [ 9 | "str2a", 10 | "str2b" 11 | ], 12 | [ 13 | "str3a" 14 | ] 15 | ], 16 | "intarrays": [ 17 | [ 18 | 1 19 | ], 20 | [ 21 | 1, 22 | 2 23 | ], 24 | [ 25 | 1, 26 | 2, 27 | 3 28 | ] 29 | ], 30 | "boolarrays": [ 31 | [ 32 | true, 33 | false 34 | ], 35 | [ 36 | false, 37 | true 38 | ], 39 | [ 40 | true 41 | ], 42 | [ 43 | false 44 | ] 45 | ], 46 | "objectarrays": [ 47 | [ 48 | { 49 | "first": true, 50 | "second": "item1" 51 | }, 52 | { 53 | "first": false, 54 | "second": "item2" 55 | }, 56 | { 57 | "first": true, 58 | "second": "item3" 59 | } 60 | ], 61 | [ 62 | { 63 | "first": false, 64 | "second": "item11" 65 | }, 66 | { 67 | "first": true, 68 | "second": "item12" 69 | }, 70 | { 71 | "first": false, 72 | "second": "item13" 73 | } 74 | ] 75 | ], 76 | "refobjarrays": [ 77 | [ 78 | { 79 | "item1": "first1", 80 | "item2": 1, 81 | "item3": false 82 | }, 83 | { 84 | "item1": "first2", 85 | "item2": 2, 86 | "item3": true 87 | }, 88 | { 89 | "item1": "first3", 90 | "item2": 3, 91 | "item3": false 92 | } 93 | ], 94 | [ 95 | { 96 | "item1": "second1", 97 | "item2": 11, 98 | "item3": true 99 | }, 100 | { 101 | "item1": "second2", 102 | "item2": 12, 103 | "item3": false 104 | }, 105 | { 106 | "item1": "second3", 107 | "item2": 13, 108 | "item3": true 109 | } 110 | ] 111 | ] 112 | } 113 | -------------------------------------------------------------------------------- /tests/test-5.c: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2017 Wang Long 2 | 3 | libocispec is free software; you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation; either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | libocispec is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with libocispec. If not, see . 15 | 16 | */ 17 | 18 | #include "config.h" 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "ocispec/image_spec_schema_image_layout_schema.h" 24 | 25 | int 26 | main () 27 | { 28 | parser_error err; 29 | image_spec_schema_image_layout_schema *image_layout = image_spec_schema_image_layout_schema_parse_file ("tests/data/image_layout_config.json", 0, &err); 30 | image_spec_schema_image_layout_schema *image_layout_gen = NULL; 31 | char *json_buf = NULL; 32 | 33 | if (image_layout == NULL) { 34 | printf ("error %s\n", err); 35 | exit (1); 36 | } 37 | json_buf = image_spec_schema_image_layout_schema_generate_json(image_layout, 0, &err); 38 | if (json_buf == NULL) { 39 | printf("gen error %s\n", err); 40 | exit (1); 41 | } 42 | image_layout_gen = image_spec_schema_image_layout_schema_parse_data(json_buf, 0, &err); 43 | if (image_layout_gen == NULL) { 44 | printf("parse error %s\n", err); 45 | exit(1); 46 | } 47 | 48 | if (strcmp (image_layout->image_layout_version, "1.0.0") && strcmp (image_layout->image_layout_version, image_layout_gen->image_layout_version)) 49 | exit (5); 50 | 51 | free(json_buf); 52 | free_image_spec_schema_image_layout_schema (image_layout); 53 | free_image_spec_schema_image_layout_schema (image_layout_gen); 54 | exit (0); 55 | } 56 | -------------------------------------------------------------------------------- /src/serialize.rs: -------------------------------------------------------------------------------- 1 | use serde; 2 | use serde_json; 3 | 4 | use std::{fmt, io, error::Error, fs::File}; 5 | 6 | #[derive(Debug)] 7 | pub enum SerializeError { 8 | Io(io::Error), 9 | Json(serde_json::Error), 10 | } 11 | 12 | impl fmt::Display for SerializeError { 13 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 14 | match *self { 15 | SerializeError::Io(ref err) => err.fmt(f), 16 | SerializeError::Json(ref err) => err.fmt(f), 17 | } 18 | } 19 | } 20 | 21 | #[allow(deprecated)] 22 | impl Error for SerializeError { 23 | fn description(&self) -> &str { 24 | match *self { 25 | SerializeError::Io(ref err) => err.description(), 26 | SerializeError::Json(ref err) => err.description(), 27 | } 28 | } 29 | fn cause(&self) -> Option<&dyn Error> { 30 | match *self { 31 | SerializeError::Io(ref err) => Some(err), 32 | SerializeError::Json(ref err) => Some(err), 33 | } 34 | } 35 | } 36 | 37 | impl From for SerializeError { 38 | fn from(err: io::Error) -> SerializeError { 39 | SerializeError::Io(err) 40 | } 41 | } 42 | 43 | impl From for SerializeError { 44 | fn from(err: serde_json::Error) -> SerializeError { 45 | SerializeError::Json(err) 46 | } 47 | } 48 | 49 | pub fn serialize(obj: &T, path: &str) -> Result<(), SerializeError> { 50 | let mut file = File::create(path)?; 51 | Ok(serde_json::to_writer_pretty(&mut file, &obj)?) 52 | } 53 | 54 | pub fn deserialize(path: &str) -> Result 55 | where for<'de> T: serde::Deserialize<'de> 56 | { 57 | let file = File::open(path)?; 58 | Ok(serde_json::from_reader(&file)?) 59 | } 60 | 61 | pub fn to_writer(obj: &T, mut writer: W) -> Result<(), SerializeError> { 62 | Ok(serde_json::to_writer(&mut writer, &obj)?) 63 | } 64 | 65 | pub fn to_string(obj: &T) -> Result { 66 | Ok(serde_json::to_string(&obj)?) 67 | } 68 | 69 | -------------------------------------------------------------------------------- /tests/test-7.c: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2017 Wang Long 2 | 3 | libocispec is free software; you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation; either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | libocispec is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with libocispec. If not, see . 15 | 16 | */ 17 | 18 | #include "config.h" 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "ocispec/image_spec_schema_config_schema.h" 24 | 25 | int 26 | main () 27 | { 28 | parser_error err; 29 | image_spec_schema_config_schema *image = image_spec_schema_config_schema_parse_file ("tests/data/image_config_mapstringobject.json", 0, &err); 30 | image_spec_schema_config_schema *image_gen = NULL; 31 | char *json_buf = NULL; 32 | 33 | if (image == NULL) { 34 | printf ("error %s\n", err); 35 | exit (1); 36 | } 37 | json_buf = image_spec_schema_config_schema_generate_json(image, 0, &err); 38 | if (json_buf == NULL) { 39 | printf("gen error %s\n", err); 40 | exit (1); 41 | } 42 | image_gen = image_spec_schema_config_schema_parse_data(json_buf, 0, &err); 43 | if (image_gen == NULL) { 44 | printf("parse error %s\n", err); 45 | exit(1); 46 | } 47 | 48 | if (image->config->volumes != NULL || image_gen->config->volumes != NULL) 49 | exit (5); 50 | if (image->config->exposed_ports->len != 1 || image_gen->config->exposed_ports->len != 1) 51 | exit (5); 52 | if (strcmp (image->config->exposed_ports->keys[0], "8080/tcp") && strcmp (image->config->exposed_ports->keys[0], image_gen->config->exposed_ports->keys[0])) 53 | exit (5); 54 | 55 | free(json_buf); 56 | free_image_spec_schema_config_schema (image); 57 | free_image_spec_schema_config_schema (image_gen); 58 | exit (0); 59 | } 60 | -------------------------------------------------------------------------------- /rust-gen.js: -------------------------------------------------------------------------------- 1 | const { 2 | quicktype, 3 | InputData, 4 | jsonInputForTargetLanguage, 5 | JSONSchemaInput, 6 | JSONSchemaStore, 7 | } = require("quicktype-core"); 8 | const $RefParser = require("@apidevtools/json-schema-ref-parser"); 9 | 10 | const BREAKLINE = "\n"; 11 | const SCHEMA_ENTRYPOINT = "config-schema.json"; 12 | const BASE_STRUCT_RUNTIME_SPEC = "Spec"; 13 | const BASE_STRUCT_IMAGE_SPEC = "ImageSpec"; 14 | 15 | var fs = require('fs'); 16 | 17 | function writeFile(fileName, data){ 18 | fs.writeFile(fileName, data, function (err) { 19 | if (err) throw err; 20 | console.log('Successfully generated '+fileName); 21 | }); 22 | } 23 | 24 | async function quicktypeJSONSchema(targetLanguage, typeName, jsonSchemaString) { 25 | const schemaInput = new JSONSchemaInput(new JSONSchemaStore()); 26 | 27 | await schemaInput.addSource({ name: typeName, schema: jsonSchemaString }); 28 | const inputData = new InputData(); 29 | inputData.addInput(schemaInput); 30 | return await quicktype({ 31 | inputData, 32 | rendererOptions: { visibility: "public" }, 33 | lang: targetLanguage, 34 | }); 35 | } 36 | 37 | function dereference(parentStructName, mySchema, fileName){ 38 | $RefParser.dereference(mySchema, (err, schema) => { 39 | if (err) { 40 | console.error(err); 41 | } 42 | else { 43 | schemaGen = JSON.stringify(schema); 44 | generateSchema(parentStructName, JSON.stringify(schema), fileName); 45 | } 46 | }) 47 | } 48 | 49 | async function generateSchema(parentStructName ,schema, fileName) { 50 | const { lines: rustSpec } = await quicktypeJSONSchema( 51 | "rust", 52 | parentStructName, 53 | schema 54 | ); 55 | writeFile(fileName, rustSpec.join(BREAKLINE)); 56 | } 57 | 58 | function generator(parentStructName ,base, entryPoint, outputPath){ 59 | var rootPath = process.cwd(); 60 | process.chdir(base); 61 | const myschema = require(base+"/"+entryPoint); 62 | dereference(parentStructName ,myschema, rootPath+"/"+outputPath); 63 | process.chdir(rootPath); 64 | } 65 | 66 | generator(BASE_STRUCT_RUNTIME_SPEC,'./runtime-spec/schema', SCHEMA_ENTRYPOINT, 'src/runtime/mod.rs'); 67 | generator(BASE_STRUCT_IMAGE_SPEC,'./image-spec/schema', SCHEMA_ENTRYPOINT, 'src/image/mod.rs'); 68 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_PREREQ([2.63]) 2 | AC_INIT([libocispec], [0.3], [giuseppe@scrivano.org]) 3 | AC_CONFIG_HEADER([config.h]) 4 | AC_CONFIG_MACRO_DIR([m4]) 5 | AC_CONFIG_AUX_DIR([build-aux]) 6 | 7 | AC_USE_SYSTEM_EXTENSIONS 8 | 9 | LT_INIT([disable-shared]) 10 | 11 | AM_INIT_AUTOMAKE([1.11 -Wno-portability foreign tar-ustar no-dist-gzip dist-xz subdir-objects]) 12 | AM_MAINTAINER_MODE([enable]) 13 | AM_SILENT_RULES([yes]) 14 | 15 | AM_EXTRA_RECURSIVE_TARGETS([yajl]) 16 | AC_CONFIG_SUBDIRS([yajl]) 17 | 18 | AC_ARG_ENABLE(embedded-yajl, 19 | AS_HELP_STRING([--enable-embedded-yajl], [Statically link a modified yajl version]), 20 | [ 21 | case "${enableval}" in 22 | yes) embedded_yajl=true ;; 23 | no) embedded_yajl=false ;; 24 | *) AC_MSG_ERROR(bad value ${enableval} for --enable-embedded-yajl) ;; 25 | esac],[embedded_yajl=false]) 26 | 27 | AM_CONDITIONAL([HAVE_EMBEDDED_YAJL], [test x"$embedded_yajl" = xtrue]) 28 | AM_COND_IF([HAVE_EMBEDDED_YAJL], [], [ 29 | AC_SEARCH_LIBS(yajl_tree_get, [yajl], [AC_DEFINE([HAVE_YAJL], 1, [Define if libyajl is available])], [AC_MSG_ERROR([*** libyajl headers not found])]) 30 | PKG_CHECK_MODULES([YAJL], [yajl >= 2.0.0]) 31 | ]) 32 | 33 | # Optionally install the library. 34 | AC_ARG_ENABLE(libocispec-install, 35 | AS_HELP_STRING([--enable-libocispec-install], [Enable libocispec installation]), 36 | [ 37 | case "${enableval}" in 38 | yes) libocispec_install=true ;; 39 | no) libocispec_install=false ;; 40 | *) AC_MSG_ERROR(bad value ${enableval} for --enable-libocispec-install) ;; 41 | esac],[libocispec_install=false]) 42 | 43 | AM_CONDITIONAL([ENABLE_LIBOCISPEC_INSTALL], [test x"$libocispec_install" = xtrue]) 44 | 45 | AC_ARG_VAR(OCI_RUNTIME_EXTENSIONS, [extensions for the OCI runtime parser]) 46 | AC_ARG_VAR(OCI_IMAGE_EXTENSIONS, [extensions for the OCI image parser]) 47 | AC_ARG_VAR(OCI_IMAGE_INDEX_EXTENSIONS, [extensions for the OCI image index parser]) 48 | AC_ARG_VAR(OCI_IMAGE_LAYOUT_EXTENSIONS, [extensions for the OCI image layout parser]) 49 | AC_ARG_VAR(OCI_IMAGE_MANIFEST_EXTENSIONS, [extensions for the OCI image manifest parser]) 50 | AC_ARG_VAR(OCI_IMAGE_MANIFEST_ITEMS_EXTENSIONS, [extensions for the OCI image manifest items parser]) 51 | 52 | AC_PROG_SED 53 | AC_PROG_CC 54 | AM_PROG_CC_C_O 55 | 56 | AM_PATH_PYTHON([3]) 57 | 58 | AC_CONFIG_FILES([ 59 | Makefile 60 | ocispec.pc 61 | ]) 62 | AC_OUTPUT 63 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build_job: 7 | runs-on: ubuntu-latest 8 | name: Build on ${{ matrix.arch }} 9 | 10 | strategy: 11 | matrix: 12 | include: 13 | - arch: armv7 14 | distro: ubuntu_latest 15 | - arch: aarch64 16 | distro: ubuntu_latest 17 | - arch: s390x 18 | distro: ubuntu_latest 19 | - arch: ppc64le 20 | distro: ubuntu_latest 21 | steps: 22 | - uses: actions/checkout@v3.0.2 23 | with: 24 | submodules: true 25 | set-safe-directory: true 26 | 27 | - uses: uraimo/run-on-arch-action@v3.0.1 28 | name: Build 29 | id: build 30 | with: 31 | arch: ${{ matrix.arch }} 32 | distro: ${{ matrix.distro }} 33 | 34 | githubToken: ${{ github.token }} 35 | 36 | setup: | 37 | git submodule update --init --recursive 38 | 39 | install: | 40 | apt-get update -q -y 41 | apt-get install -q -y python3 automake libtool autotools-dev git make cmake pkg-config gcc wget xz-utils 42 | 43 | run: | 44 | find $(pwd) -name '.git' -exec bash -c 'git config --global --add safe.directory ${0%/.git}' {} \; 45 | ./autogen.sh --enable-embedded-yajl 46 | ./configure --enable-embedded-yajl CFLAGS='-Wall -Wextra -Werror' 47 | # Run make check first to catch test failures with accessible logs 48 | make -j $(nproc) 49 | make check || (cat test-suite.log 2>/dev/null; exit 1) 50 | # Now run distcheck for distribution validation 51 | make -j $(nproc) distcheck DISTCHECK_CONFIGURE_FLAGS="--enable-embedded-yajl" 52 | # check that the working dir is clean 53 | git describe --broken --dirty --all | grep -qv dirty 54 | make clean 55 | 56 | test_and_build_rust_bindings: 57 | name: test and build rust bindings 58 | runs-on: ubuntu-latest 59 | steps: 60 | - uses: actions/checkout@v2 61 | - uses: actions-rs/toolchain@v1 62 | with: 63 | toolchain: stable 64 | - uses: actions-rs/cargo@v1 65 | with: 66 | command: test 67 | args: --lib 68 | - uses: actions-rs/cargo@v1 69 | with: 70 | command: build 71 | args: --release --all-features 72 | -------------------------------------------------------------------------------- /tests/test-11.c: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 duguhaotian 2 | 3 | libocispec is free software; you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation; either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | libocispec is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with libocispec. If not, see . 15 | 16 | */ 17 | 18 | #include "config.h" 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "ocispec/image_spec_schema_image_layout_schema.h" 24 | 25 | #ifndef OPT_PARSE_FULLKEY 26 | # define OPT_PARSE_FULLKEY 0x08 27 | #endif 28 | 29 | int 30 | main () 31 | { 32 | parser_error err; 33 | struct parser_context ctx; 34 | ctx.options = OPT_PARSE_FULLKEY; 35 | 36 | image_spec_schema_image_layout_schema *image_layout = image_spec_schema_image_layout_schema_parse_file ("tests/data/null_value_config.json", &ctx, &err); 37 | image_spec_schema_image_layout_schema *image_layout_gen = NULL; 38 | char *json_buf = NULL; 39 | 40 | if (image_layout == NULL) { 41 | printf ("error %s\n", err); 42 | exit (1); 43 | } 44 | json_buf = image_spec_schema_image_layout_schema_generate_json(image_layout, &ctx, &err); 45 | if (json_buf == NULL) { 46 | printf("gen error %s\n", err); 47 | free(err); 48 | exit (1); 49 | } 50 | image_layout_gen = image_spec_schema_image_layout_schema_parse_data(json_buf, 0, &err); 51 | if (image_layout_gen == NULL) { 52 | printf("parse error %s\n", err); 53 | free(err); 54 | exit(1); 55 | } 56 | 57 | if (strcmp (image_layout->image_layout_version, "1.0.0") && strcmp (image_layout->image_layout_version, image_layout_gen->image_layout_version)) 58 | exit (5); 59 | 60 | printf("%s\n", json_buf); 61 | 62 | // origin json str should same with generate new json str, 63 | if (strstr(json_buf, "just-key\": null") == NULL) 64 | exit (51); 65 | if (strstr(json_buf, "key1\": null") == NULL) 66 | exit (52); 67 | 68 | free(json_buf); 69 | free_image_spec_schema_image_layout_schema (image_layout); 70 | free_image_spec_schema_image_layout_schema (image_layout_gen); 71 | exit (0); 72 | } 73 | -------------------------------------------------------------------------------- /src/ocispec/validate.c: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2017, 2019 Giuseppe Scrivano 2 | 3 | libocispec is free software; you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation; either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | libocispec is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with libocispec. If not, see . 15 | 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "ocispec/runtime_spec_schema_config_schema.h" 24 | 25 | #ifdef FUZZER 26 | int 27 | LLVMFuzzerInitialize (int *argc, char ***argv) 28 | { 29 | return 0; 30 | } 31 | 32 | int 33 | LLVMFuzzerTestOneInput (uint8_t *buf, size_t len) 34 | { 35 | runtime_spec_schema_config_schema *container; 36 | struct parser_context ctx; 37 | parser_error err; 38 | FILE *s; 39 | 40 | if (len == 0) 41 | return 0; 42 | 43 | s = fmemopen (buf, len, "r"); 44 | if (s == NULL) 45 | return 0; 46 | 47 | ctx.options = OPT_PARSE_STRICT; 48 | ctx.errfile = stderr; 49 | container = runtime_spec_schema_config_schema_parse_file_stream (s, &ctx, &err); 50 | fclose (s); 51 | if (container) 52 | { 53 | free_runtime_spec_schema_config_schema (container); 54 | return 0; 55 | } 56 | if (err) 57 | { 58 | fprintf (stderr, "error: %s\n", err); 59 | free (err); 60 | } 61 | return 0; 62 | } 63 | #endif 64 | 65 | int 66 | main (int argc, char *argv[]) 67 | { 68 | parser_error err; 69 | runtime_spec_schema_config_schema *container; 70 | const char *file = "config.json"; 71 | struct parser_context ctx; 72 | 73 | #ifdef FUZZER 74 | if (getenv ("VALIDATE_FUZZ")) 75 | { 76 | extern void HF_ITER (uint8_t** buf, size_t* len); 77 | for (;;) 78 | { 79 | size_t len; 80 | uint8_t *buf; 81 | 82 | HF_ITER (&buf, &len); 83 | 84 | LLVMFuzzerTestOneInput (buf, len); 85 | } 86 | } 87 | #endif 88 | 89 | if (argc > 1) 90 | file = argv[1]; 91 | 92 | ctx.options = OPT_PARSE_STRICT; 93 | ctx.errfile = stderr; 94 | 95 | container = runtime_spec_schema_config_schema_parse_file (file, &ctx, &err); 96 | if (container) 97 | free_runtime_spec_schema_config_schema (container); 98 | 99 | if (err) 100 | { 101 | fprintf (stderr, "error in %s: %s\n", file, err); 102 | free (err); 103 | exit (EXIT_FAILURE); 104 | } 105 | 106 | exit (EXIT_SUCCESS); 107 | } 108 | -------------------------------------------------------------------------------- /tests/test-6.c: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2017 Yifeng Tan 2 | 3 | libocispec is free software; you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation; either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | libocispec is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with libocispec. If not, see . 15 | 16 | */ 17 | 18 | #include "config.h" 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "ocispec/image_spec_schema_image_manifest_schema.h" 24 | 25 | int 26 | main () 27 | { 28 | parser_error err; 29 | image_spec_schema_image_manifest_schema *manifest = image_spec_schema_image_manifest_schema_parse_file ("tests/data/image_manifest.json", 0, &err); 30 | image_spec_schema_image_manifest_schema *manifest_gen = NULL; 31 | char *json_buf = NULL; 32 | 33 | if (manifest == NULL) { 34 | printf ("error %s\n", err); 35 | exit (1); 36 | } 37 | json_buf = image_spec_schema_image_manifest_schema_generate_json(manifest, 0, &err); 38 | if (json_buf == NULL) { 39 | printf("gen error %s\n", err); 40 | exit (1); 41 | } 42 | manifest_gen = image_spec_schema_image_manifest_schema_parse_data(json_buf, 0, &err); 43 | if (manifest_gen == NULL) { 44 | printf("parse error %s\n", err); 45 | exit(1); 46 | } 47 | 48 | if (manifest->schema_version != 2 || manifest_gen->schema_version != 2) 49 | exit (5); 50 | if (strcmp (manifest->config->media_type, "application/vnd.oci.image.config.v1+json") && strcmp (manifest->config->media_type, manifest_gen->config->media_type)) 51 | exit (5); 52 | if (manifest->config->size != 1470 || manifest_gen->config->size != 1470) 53 | exit (5); 54 | if (manifest->layers_len != 3 || manifest_gen->layers_len != 3) 55 | exit (5); 56 | if (strcmp (manifest->layers[1]->digest, "sha256:2b689805fbd00b2db1df73fae47562faac1a626d5f61744bfe29946ecff5d73d") || \ 57 | strcmp (manifest->layers[1]->digest, manifest_gen->layers[1]->digest)) 58 | exit (5); 59 | if (manifest->annotations->len != 2 || manifest_gen->annotations->len != 2) 60 | exit (5); 61 | if (strcmp(manifest->annotations->keys[1], "key2") && strcmp(manifest->annotations->keys[1], manifest_gen->annotations->keys[1])) 62 | exit (5); 63 | if (strcmp(manifest->annotations->values[1], "value2") && strcmp(manifest->annotations->values[1], manifest_gen->annotations->values[1])) 64 | exit (5); 65 | 66 | free(json_buf); 67 | free_image_spec_schema_image_manifest_schema (manifest); 68 | free_image_spec_schema_image_manifest_schema (manifest_gen); 69 | exit (0); 70 | } 71 | -------------------------------------------------------------------------------- /tests/test-3.c: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2017 Wang Long 2 | 3 | libocispec is free software; you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation; either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | libocispec is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with libocispec. If not, see . 15 | 16 | */ 17 | 18 | #include "config.h" 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "ocispec/image_spec_schema_config_schema.h" 24 | 25 | int 26 | main () 27 | { 28 | parser_error err; 29 | image_spec_schema_config_schema *image = image_spec_schema_config_schema_parse_file ("tests/data/image_config.json", 0, &err); 30 | image_spec_schema_config_schema *image_gen = NULL; 31 | char *json_buf = NULL; 32 | 33 | if (image == NULL) { 34 | printf ("error %s\n", err); 35 | exit (1); 36 | } 37 | json_buf = image_spec_schema_config_schema_generate_json(image, 0, &err); 38 | if (json_buf == NULL) { 39 | printf("gen error %s\n", err); 40 | exit (1); 41 | } 42 | image_gen = image_spec_schema_config_schema_parse_data(json_buf, 0, &err); 43 | if (image_gen == NULL) { 44 | printf("parse error %s\n", err); 45 | exit(1); 46 | } 47 | 48 | if (strcmp (image->author, "Alyssa P. Hacker ") && strcmp (image->author, image_gen->author)) 49 | exit (5); 50 | if (strcmp (image->created, "2015-10-31T22:22:56.015925234Z") && strcmp (image->created, image_gen->created)) 51 | exit (5); 52 | if (strcmp (image->os, "linux") && strcmp (image->os, image_gen->os)) 53 | exit (5); 54 | if (strcmp (image->architecture, "amd64") && strcmp (image->architecture, image_gen->architecture)) 55 | exit (5); 56 | if (strcmp (image->config->user, "1:1") && strcmp (image->config->user, image_gen->config->user)) 57 | exit (5); 58 | if (strcmp (image->config->env[1], "FOO=docker_is_a_really") && strcmp (image->config->env[1], image_gen->config->env[1])) 59 | exit (5); 60 | if (strcmp (image->config->entrypoint[0], "/bin/sh") && strcmp (image->config->entrypoint[0], image_gen->config->entrypoint[0])) 61 | exit (5); 62 | if (image->config->volumes->len != 2 || image_gen->config->volumes->len != 2) 63 | exit (5); 64 | if (strcmp (image->config->volumes->keys[0], "/var/job-result-data") && strcmp (image->config->volumes->keys[0], image_gen->config->volumes->keys[0])) 65 | exit (5); 66 | if (strcmp (image->config->volumes->keys[1], "/var/log/my-app-logs") && strcmp (image->config->volumes->keys[1], image_gen->config->volumes->keys[1])) 67 | exit (5); 68 | free(json_buf); 69 | free_image_spec_schema_config_schema (image); 70 | free_image_spec_schema_config_schema(image_gen); 71 | exit (0); 72 | } 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | libocispec 2 | ========== 3 | 4 | [![Build Status](https://travis-ci.org/containers/libocispec.svg?branch=master)](https://travis-ci.org/containers/libocispec) 5 | 6 | A library for easily parsing 7 | of [OCI runtime](https://github.com/opencontainers/runtime-spec) 8 | and [OCI image](https://github.com/opencontainers/image-spec) files 9 | from C, and generate json string from corresponding struct. 10 | 11 | The parser is generated directly from the JSON schema in the source repository. 12 | 13 | ## Installation 14 | Expects [yajl](https://github.com/containers/yajl) to be installed and linkable. 15 | ```sh 16 | $ ./autogen.sh 17 | $ ./configure 18 | $ make 19 | $ sudo make install 20 | ``` 21 | 22 | Parsing an OCI configuration file is easy as: 23 | 24 | ```c 25 | #include 26 | #include 27 | 28 | runtime_spec_schema_config_schema *container = runtime_spec_schema_config_schema_parse_file ("config.json", NULL, &err); 29 | 30 | if (container == NULL) 31 | exit (EXIT_FAILURE); 32 | 33 | /* Print the container hostname. */ 34 | if (container->hostname) 35 | printf ("The specified hostname is %s\n", container->hostname); 36 | 37 | for (size_t i; i < container->mounts_len; i++) 38 | printf ("Mounting to %s\n", container->mounts[i]->destination); 39 | 40 | printf ("Running as user ID and GID %d %d\n", container->process->user->uid, container->process->user->gid); 41 | 42 | ``` 43 | 44 | Generating an OCI configuration json string is also easy as: 45 | 46 | ```c 47 | #include 48 | #include 49 | 50 | runtime_spec_schema_config_schema container; 51 | char *json_buf = NULL; 52 | 53 | memset (&container, 0, sizeof (runtime_spec_schema_config_schema)); 54 | 55 | container.oci_version = "2"; 56 | container.hostname = "ubuntu"; 57 | /* Add other configuration. */ 58 | /* ... ... */ 59 | 60 | json_buf = runtime_spec_schema_config_schema_generate_json (&container, NULL, &err); 61 | if (json_buf == NULL) 62 | exit (EXIT_FAILURE); 63 | 64 | printf ("The generated json string is:\n%s\n", json_buf); 65 | 66 | ``` 67 | ## Rust Bindings 68 | libocispec supports rust bindings as well. You can use it directly by adding it as dependency to `Cargo.toml` or generate fresh types using `make generate-rust` 69 | ```toml 70 | [dependencies] 71 | libocispec = { git = "https://github.com/containers/libocispec" } 72 | ``` 73 | for Cargo version older than `0.51.0` specify branch explicitly 74 | ```toml 75 | [dependencies] 76 | libocispec = { git = "https://github.com/containers/libocispec", branch = "main" } 77 | ``` 78 | Example usage 79 | ```rust 80 | extern crate libocispec; 81 | use libocispec::runtime; 82 | use libocispec::image; 83 | 84 | fn main() { 85 | let runtime_spec = match runtime::Spec::load("path/to/spec") { 86 | Ok(spec) => spec, 87 | Err(e) => panic!("{}", e), 88 | } 89 | let image_spec = match image::ImageConfig::load("path/to/spec") { 90 | Ok(spec) => spec, 91 | Err(e) => panic!("{}", e), 92 | } 93 | } 94 | ``` 95 | -------------------------------------------------------------------------------- /tests/test-8.c: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2017 YiFeng Tan 2 | 3 | libocispec is free software; you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation; either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | libocispec is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with libocispec. If not, see . 15 | 16 | */ 17 | 18 | #include "config.h" 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "ocispec/image_manifest_items_image_manifest_items_schema.h" 24 | 25 | int 26 | main () 27 | { 28 | parser_error err = NULL; 29 | image_manifest_items_image_manifest_items_schema_container *image_items = 30 | image_manifest_items_image_manifest_items_schema_container_parse_file ("tests/data/image_manifest_item.json", 0, &err); 31 | image_manifest_items_image_manifest_items_schema_container *image_items_gen = NULL; 32 | char *json_buf = NULL; 33 | 34 | if (image_items == NULL) { 35 | printf ("error %s\n", err); 36 | exit (1); 37 | } 38 | json_buf = image_manifest_items_image_manifest_items_schema_container_generate_json ( 39 | (const image_manifest_items_image_manifest_items_schema_container*)image_items, 0, &err); 40 | if (json_buf == NULL) { 41 | printf ("gen error %s\n", err); 42 | exit (1); 43 | } 44 | printf("%s\n", json_buf); 45 | 46 | image_items_gen = image_manifest_items_image_manifest_items_schema_container_parse_data ( 47 | json_buf, 0, &err); 48 | if (image_items_gen == NULL) { 49 | printf ("parse error %s\n", err); 50 | exit (1); 51 | } 52 | 53 | if (image_items->len != 1 || image_items->len != image_items_gen->len) 54 | exit (5); 55 | if (!image_items->items[0]->config || strcmp (image_items->items[0]->config, image_items_gen->items[0]->config) || \ 56 | strcmp (image_items_gen->items[0]->config, "5b117edd0b767986092e9f721ba2364951b0a271f53f1f41aff9dd1861c2d4fe.json")) 57 | exit (6); 58 | if (image_items->items[0]->layers_len != 5 || image_items->items[0]->layers_len != image_items_gen->items[0]->layers_len || \ 59 | strcmp (image_items->items[0]->layers[2], image_items_gen->items[0]->layers[2]) || \ 60 | strcmp (image_items_gen->items[0]->layers[2], "e5ffeddba503ff2220cf4587030131c2cee5aef6083df1d2559e3d576bf04c99/layer.tar")) 61 | exit (7); 62 | if (image_items->items[0]->repo_tags_len != 0 || image_items->items[0]->repo_tags_len != image_items_gen->items[0]->repo_tags_len || \ 63 | image_items->items[0]->repo_tags != NULL) 64 | exit (8); 65 | 66 | free (json_buf); 67 | free_image_manifest_items_image_manifest_items_schema_container (image_items); 68 | free_image_manifest_items_image_manifest_items_schema_container (image_items_gen); 69 | exit (0); 70 | } 71 | -------------------------------------------------------------------------------- /tests/test-9.c: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2020 duguhaotian 2 | 3 | libocispec is free software; you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation; either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | libocispec is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with libocispec. If not, see . 15 | 16 | */ 17 | 18 | #include "config.h" 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "ocispec/image_spec_schema_image_layout_schema.h" 24 | 25 | #ifndef OPT_PARSE_FULLKEY 26 | # define OPT_PARSE_FULLKEY 0x08 27 | #endif 28 | 29 | int 30 | main () 31 | { 32 | parser_error err; 33 | struct parser_context ctx; 34 | ctx.options = OPT_PARSE_FULLKEY; 35 | 36 | image_spec_schema_image_layout_schema *image_layout = image_spec_schema_image_layout_schema_parse_file ("tests/data/residual_image_layout_config.json", &ctx, &err); 37 | image_spec_schema_image_layout_schema *image_layout_gen = NULL; 38 | char *json_buf = NULL; 39 | 40 | if (image_layout == NULL) { 41 | printf ("error %s\n", err); 42 | exit (1); 43 | } 44 | json_buf = image_spec_schema_image_layout_schema_generate_json(image_layout, &ctx, &err); 45 | if (json_buf == NULL) { 46 | printf("gen error %s\n", err); 47 | free(err); 48 | exit (1); 49 | } 50 | image_layout_gen = image_spec_schema_image_layout_schema_parse_data(json_buf, 0, &err); 51 | if (image_layout_gen == NULL) { 52 | printf("parse error %s\n", err); 53 | free(err); 54 | exit(1); 55 | } 56 | 57 | if (strcmp (image_layout->image_layout_version, "1.0.0") && strcmp (image_layout->image_layout_version, image_layout_gen->image_layout_version)) 58 | exit (5); 59 | 60 | printf("%s\n", json_buf); 61 | 62 | // origin json str should same with generate new json str, 63 | if (strstr(json_buf, "residual_int") == NULL) 64 | exit (51); 65 | if (strstr(json_buf, "residual_float") == NULL) 66 | exit (52); 67 | if (strstr(json_buf, "residual_string") == NULL) 68 | exit (53); 69 | if (strstr(json_buf, "residual_true") == NULL) 70 | exit (54); 71 | if (strstr(json_buf, "residual_false") == NULL) 72 | exit (55); 73 | if (strstr(json_buf, "residual_array") == NULL) 74 | exit (56); 75 | if (strstr(json_buf, "residual_obj") == NULL) 76 | exit (57); 77 | if (strstr(json_buf, "key1") == NULL || 78 | strstr(json_buf, "key2") == NULL || 79 | strstr(json_buf, "key3") == NULL) 80 | exit (58); 81 | if (strstr(json_buf, "value1") == NULL || 82 | strstr(json_buf, "value2") == NULL || 83 | strstr(json_buf, "value3") == NULL) 84 | exit (59); 85 | 86 | free(json_buf); 87 | free_image_spec_schema_image_layout_schema (image_layout); 88 | free_image_spec_schema_image_layout_schema (image_layout_gen); 89 | exit (0); 90 | } 91 | -------------------------------------------------------------------------------- /src/image/mod.rs: -------------------------------------------------------------------------------- 1 | // Example code that deserializes and serializes the model. 2 | // extern crate serde; 3 | // #[macro_use] 4 | // extern crate serde_derive; 5 | // extern crate serde_json; 6 | // 7 | // use generated_module::ImageSpec; 8 | // 9 | // fn main() { 10 | // let json = r#"{"answer": 42}"#; 11 | // let model: ImageSpec = serde_json::from_str(&json).unwrap(); 12 | // } 13 | 14 | use serde::{Serialize, Deserialize}; 15 | use std::collections::HashMap; 16 | 17 | /// OpenContainer Config Specification 18 | #[derive(Serialize, Deserialize)] 19 | pub struct ImageSpec { 20 | #[serde(rename = "architecture")] 21 | pub architecture: String, 22 | 23 | #[serde(rename = "author")] 24 | pub author: Option, 25 | 26 | #[serde(rename = "config")] 27 | pub config: Option, 28 | 29 | #[serde(rename = "created")] 30 | pub created: Option, 31 | 32 | #[serde(rename = "history")] 33 | pub history: Option>, 34 | 35 | #[serde(rename = "os")] 36 | pub os: String, 37 | 38 | #[serde(rename = "os.features")] 39 | pub os_features: Option>, 40 | 41 | #[serde(rename = "os.version")] 42 | pub os_version: Option, 43 | 44 | #[serde(rename = "rootfs")] 45 | pub rootfs: Rootfs, 46 | 47 | #[serde(rename = "variant")] 48 | pub variant: Option, 49 | } 50 | 51 | #[derive(Serialize, Deserialize)] 52 | pub struct Config { 53 | #[serde(rename = "ArgsEscaped")] 54 | pub args_escaped: Option, 55 | 56 | #[serde(rename = "Cmd")] 57 | pub cmd: Option>, 58 | 59 | #[serde(rename = "Entrypoint")] 60 | pub entrypoint: Option>, 61 | 62 | #[serde(rename = "Env")] 63 | pub env: Option>, 64 | 65 | #[serde(rename = "ExposedPorts")] 66 | pub exposed_ports: Option>>, 67 | 68 | #[serde(rename = "Labels")] 69 | pub labels: Option>>, 70 | 71 | #[serde(rename = "StopSignal")] 72 | pub stop_signal: Option, 73 | 74 | #[serde(rename = "User")] 75 | pub user: Option, 76 | 77 | #[serde(rename = "Volumes")] 78 | pub volumes: Option>>, 79 | 80 | #[serde(rename = "WorkingDir")] 81 | pub working_dir: Option, 82 | } 83 | 84 | #[derive(Serialize, Deserialize)] 85 | pub struct History { 86 | #[serde(rename = "author")] 87 | pub author: Option, 88 | 89 | #[serde(rename = "comment")] 90 | pub comment: Option, 91 | 92 | #[serde(rename = "created")] 93 | pub created: Option, 94 | 95 | #[serde(rename = "created_by")] 96 | pub created_by: Option, 97 | 98 | #[serde(rename = "empty_layer")] 99 | pub empty_layer: Option, 100 | } 101 | 102 | #[derive(Serialize, Deserialize)] 103 | pub struct Rootfs { 104 | #[serde(rename = "diff_ids")] 105 | pub diff_ids: Vec, 106 | 107 | #[serde(rename = "type")] 108 | pub rootfs_type: Type, 109 | } 110 | 111 | #[derive(Serialize, Deserialize)] 112 | pub enum Type { 113 | #[serde(rename = "layers")] 114 | Layers, 115 | } 116 | -------------------------------------------------------------------------------- /src/ocispec/read-file.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Giuseppe Scrivano 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | 19 | #include "ocispec/read-file.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | char * 30 | fread_file (FILE *stream, size_t *length) 31 | { 32 | char *buf = NULL; 33 | size_t alloc = BUFSIZ; 34 | { 35 | struct stat st; 36 | 37 | if (fstat (fileno (stream), &st) >= 0 && S_ISREG (st.st_mode)) 38 | { 39 | off_t pos = ftello (stream); 40 | 41 | if (pos >= 0 && pos < st.st_size) 42 | { 43 | off_t alloc_off = st.st_size - pos; 44 | if (SIZE_MAX - 1 < (uintmax_t)(alloc_off)) 45 | { 46 | errno = ENOMEM; 47 | return NULL; 48 | } 49 | 50 | alloc = alloc_off + 1; 51 | } 52 | } 53 | } 54 | 55 | if (!(buf = malloc (alloc))) 56 | return NULL; 57 | 58 | { 59 | size_t size = 0; 60 | int save_errno; 61 | 62 | for (;;) 63 | { 64 | size_t requested = alloc - size; 65 | size_t count = fread (buf + size, 1, requested, stream); 66 | size += count; 67 | 68 | if (count != requested) 69 | { 70 | save_errno = errno; 71 | if (ferror (stream)) 72 | break; 73 | 74 | if (size < alloc - 1) 75 | { 76 | char *reduce_buf = realloc (buf, size + 1); 77 | if (reduce_buf != NULL) 78 | buf = reduce_buf; 79 | } 80 | 81 | buf[size] = '\0'; 82 | *length = size; 83 | return buf; 84 | } 85 | 86 | { 87 | char *temp_buf; 88 | 89 | if (alloc == SIZE_MAX) 90 | { 91 | save_errno = ENOMEM; 92 | break; 93 | } 94 | 95 | if (alloc < SIZE_MAX - alloc / 2) 96 | alloc = alloc + alloc / 2; 97 | else 98 | alloc = SIZE_MAX; 99 | 100 | if (!(temp_buf = realloc (buf, alloc))) 101 | { 102 | save_errno = errno; 103 | break; 104 | } 105 | 106 | buf = temp_buf; 107 | } 108 | } 109 | 110 | free (buf); 111 | errno = save_errno; 112 | return NULL; 113 | } 114 | } 115 | 116 | char * 117 | read_file (const char *path, size_t *length) 118 | { 119 | FILE *f = fopen (path, "r"); 120 | char *buf; 121 | int save_errno; 122 | 123 | if (!f) 124 | return NULL; 125 | 126 | buf = fread_file (f, length); 127 | 128 | save_errno = errno; 129 | 130 | if (fclose (f) != 0) 131 | { 132 | if (buf) 133 | { 134 | save_errno = errno; 135 | free (buf); 136 | } 137 | errno = save_errno; 138 | return NULL; 139 | } 140 | 141 | return buf; 142 | } 143 | -------------------------------------------------------------------------------- /tests/data/config.nocwd.json: -------------------------------------------------------------------------------- 1 | { 2 | "ociVersion": "1.0.0-rc2-dev", 3 | "platform": { 4 | "os": "linux", 5 | "arch": "amd64" 6 | }, 7 | "process": { 8 | "terminal": true, 9 | "consoleSize": { 10 | "height": 0, 11 | "width": 0 12 | }, 13 | "user": { 14 | "uid": 101, 15 | "gid": 0 16 | }, 17 | "args": [ 18 | "ARGS1", 19 | "sh" 20 | ], 21 | "env": [ 22 | "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 23 | "TERM=xterm" 24 | ], 25 | "capabilities": [ 26 | "CAP_AUDIT_WRITE", 27 | "CAP_KILL", 28 | "CAP_NET_BIND_SERVICE" 29 | ], 30 | "rlimits": [ 31 | { 32 | "type": "RLIMIT_NOFILE", 33 | "hard": 1024, 34 | "soft": 1024 35 | } 36 | ], 37 | "noNewPrivileges": true 38 | }, 39 | "root": { 40 | "path": "rootfs", 41 | "readonly": true 42 | }, 43 | "hostname": "runc", 44 | "mounts": [ 45 | { 46 | "destination": "/proc", 47 | "type": "proc", 48 | "source": "proc" 49 | }, 50 | { 51 | "destination": "/dev", 52 | "type": "tmpfs", 53 | "source": "tmpfs", 54 | "options": [ 55 | "nosuid", 56 | "strictatime", 57 | "mode=755", 58 | "size=65536k" 59 | ] 60 | }, 61 | { 62 | "destination": "/dev/pts", 63 | "type": "devpts", 64 | "source": "devpts", 65 | "options": [ 66 | "nosuid", 67 | "noexec", 68 | "newinstance", 69 | "ptmxmode=0666", 70 | "mode=0620", 71 | "gid=5" 72 | ] 73 | }, 74 | { 75 | "destination": "/dev/shm", 76 | "type": "tmpfs", 77 | "source": "shm", 78 | "options": [ 79 | "nosuid", 80 | "noexec", 81 | "nodev", 82 | "mode=1777", 83 | "size=65536k" 84 | ] 85 | }, 86 | { 87 | "destination": "/dev/mqueue", 88 | "type": "mqueue", 89 | "source": "mqueue", 90 | "options": [ 91 | "nosuid", 92 | "noexec", 93 | "nodev" 94 | ] 95 | }, 96 | { 97 | "destination": "/sys", 98 | "type": "sysfs", 99 | "source": "sysfs", 100 | "options": [ 101 | "nosuid", 102 | "noexec", 103 | "nodev", 104 | "ro" 105 | ] 106 | }, 107 | { 108 | "destination": "/sys/fs/cgroup", 109 | "type": "cgroup", 110 | "source": "cgroup", 111 | "options": [ 112 | "nosuid", 113 | "noexec", 114 | "nodev", 115 | "relatime", 116 | "ro" 117 | ] 118 | } 119 | ], 120 | "hooks": {}, 121 | "solaris": { 122 | "anet": [ 123 | { 124 | "allowedAddress": "172.17.0.2/16", 125 | "configureAllowedAddress": "true", 126 | "defrouter": "172.17.0.1/16", 127 | "linkProtection": "mac-nospoof, ip-nospoof", 128 | "linkname": "net0", 129 | "lowerLink": "net2", 130 | "macAddress": "02:42:f8:52:c7:16" 131 | } 132 | ] 133 | }, 134 | "linux": { 135 | "sysctl": { 136 | "net.ipv4.ip_forward": "1", 137 | "net.core.somaxconn": "256" 138 | }, 139 | "resources": { 140 | "devices": [ 141 | { 142 | "allow": false, 143 | "access": "rwm" 144 | } 145 | ] 146 | }, 147 | "namespaces": [ 148 | { 149 | "type": "pid" 150 | }, 151 | { 152 | "type": "network" 153 | }, 154 | { 155 | "type": "ipc" 156 | }, 157 | { 158 | "type": "uts" 159 | }, 160 | { 161 | "type": "mount" 162 | } 163 | ], 164 | "maskedPaths": [ 165 | "/proc/kcore", 166 | "/proc/latency_stats", 167 | "/proc/timer_list", 168 | "/proc/timer_stats", 169 | "/proc/sched_debug", 170 | "/sys/firmware" 171 | ], 172 | "readonlyPaths": [ 173 | "/proc/asound", 174 | "/proc/bus", 175 | "/proc/fs", 176 | "/proc/irq", 177 | "/proc/sys", 178 | "/proc/sysrq-trigger" 179 | ] 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /tests/test-4.c: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2017 Wang Long 2 | 3 | libocispec is free software; you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation; either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | libocispec is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with libocispec. If not, see . 15 | 16 | */ 17 | 18 | #include "config.h" 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "ocispec/image_spec_schema_image_index_schema.h" 24 | 25 | int 26 | main () 27 | { 28 | parser_error err; 29 | image_spec_schema_image_index_schema *image_index = image_spec_schema_image_index_schema_parse_file ("tests/data/image_index_config.json", 0, &err); 30 | image_spec_schema_image_index_schema *image_index_gen = NULL; 31 | char *json_buf = NULL; 32 | 33 | if (image_index == NULL) { 34 | printf ("error %s\n", err); 35 | exit (1); 36 | } 37 | json_buf = image_spec_schema_image_index_schema_generate_json(image_index, 0, &err); 38 | if (json_buf == NULL) { 39 | printf("gen error %s\n", err); 40 | exit (1); 41 | } 42 | image_index_gen = image_spec_schema_image_index_schema_parse_data(json_buf, 0, &err); 43 | if (image_index_gen == NULL) { 44 | printf("parse error %s\n", err); 45 | exit(1); 46 | } 47 | 48 | if (image_index->schema_version != 2 || image_index_gen->schema_version != 2) 49 | exit (5); 50 | if (image_index->annotations->len != 2 || image_index_gen->annotations->len != 2) 51 | exit (5); 52 | if (image_index->annotations->len != 2 || image_index_gen->annotations->len != 2) 53 | exit (5); 54 | if (strcmp (image_index->annotations->keys[0], "com.example.key1") && \ 55 | strcmp (image_index->annotations->keys[0], image_index_gen->annotations->keys[0])) 56 | exit (5); 57 | if (strcmp (image_index->annotations->values[1], "value2") && \ 58 | strcmp (image_index->annotations->values[1], image_index_gen->annotations->values[1])) 59 | exit (5); 60 | if (strcmp (image_index->manifests[0]->media_type, "application/vnd.oci.image.manifest.v1+json") && \ 61 | strcmp (image_index->manifests[0]->media_type, image_index_gen->manifests[0]->media_type)) 62 | exit (5); 63 | if (image_index->manifests[0]->size != 7143 || image_index_gen->manifests[0]->size != 7143) 64 | exit (5); 65 | if (strcmp (image_index->manifests[0]->digest, "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f") && \ 66 | strcmp (image_index->manifests[0]->digest, image_index_gen->manifests[0]->digest)) 67 | exit (5); 68 | if (strcmp (image_index->manifests[0]->platform->os, "linux") && \ 69 | strcmp (image_index->manifests[0]->platform->os, image_index_gen->manifests[0]->platform->os)) 70 | exit (5); 71 | if (strcmp (image_index->manifests[0]->platform->os_version, "1.0.0") && \ 72 | strcmp (image_index->manifests[0]->platform->os_version, image_index_gen->manifests[0]->platform->os_version)) 73 | exit (5); 74 | if (image_index->manifests[0]->platform->os_features_len != 2 || image_index_gen->manifests[0]->platform->os_features_len != 2) 75 | exit (5); 76 | if (strcmp (image_index->manifests[0]->platform->os_features[1], "simple") && \ 77 | strcmp (image_index->manifests[0]->platform->os_features[1], image_index_gen->manifests[0]->platform->os_features[1])) 78 | exit (5); 79 | if (strcmp (image_index->manifests[0]->platform->architecture, "ppc64le") && \ 80 | strcmp (image_index->manifests[0]->platform->architecture, image_index_gen->manifests[0]->platform->architecture)) 81 | exit (5); 82 | 83 | free(json_buf); 84 | free_image_spec_schema_image_index_schema (image_index); 85 | free_image_spec_schema_image_index_schema (image_index_gen); 86 | exit (0); 87 | } 88 | -------------------------------------------------------------------------------- /GNUmakefile: -------------------------------------------------------------------------------- 1 | # Having a separate GNUmakefile lets me 'include' the dynamically 2 | # generated rules created via cfg.mk (package-local configuration) 3 | # as well as maint.mk (generic maintainer rules). 4 | # This makefile is used only if you run GNU Make. 5 | # It is necessary if you want to build targets usually of interest 6 | # only to the maintainer. 7 | 8 | # Copyright (C) 2001, 2003, 2006-2014 Free Software Foundation, Inc. 9 | 10 | # This program is free software: you can redistribute it and/or modify 11 | # it under the terms of the GNU General Public License as published by 12 | # the Free Software Foundation, either version 3 of the License, or 13 | # (at your option) any later version. 14 | 15 | # This program is distributed in the hope that it will be useful, 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | # GNU General Public License for more details. 19 | 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | 23 | # If the user runs GNU make but has not yet run ./configure, 24 | # give them a diagnostic. 25 | _gl-Makefile := $(wildcard [M]akefile) 26 | ifneq ($(_gl-Makefile),) 27 | 28 | # Make tar archive easier to reproduce. 29 | export TAR_OPTIONS = --owner=0 --group=0 --numeric-owner 30 | 31 | # Allow the user to add to this in the Makefile. 32 | ALL_RECURSIVE_TARGETS = 33 | 34 | include Makefile 35 | 36 | # Some projects override e.g., _autoreconf here. 37 | -include $(srcdir)/cfg.mk 38 | 39 | # Allow cfg.mk to override these. 40 | _build-aux ?= build-aux 41 | _autoreconf ?= autoreconf -v 42 | 43 | include $(srcdir)/maint.mk 44 | 45 | # Ensure that $(VERSION) is up to date for dist-related targets, but not 46 | # for others: rerunning autoreconf and recompiling everything isn't cheap. 47 | _have-git-version-gen := \ 48 | $(shell test -f $(srcdir)/$(_build-aux)/git-version-gen && echo yes) 49 | ifeq ($(_have-git-version-gen)0,yes$(MAKELEVEL)) 50 | _is-dist-target ?= $(filter-out %clean, \ 51 | $(filter maintainer-% dist% alpha beta stable,$(MAKECMDGOALS))) 52 | _is-install-target ?= $(filter-out %check, $(filter install%,$(MAKECMDGOALS))) 53 | ifneq (,$(_is-dist-target)$(_is-install-target)) 54 | _curr-ver := $(shell cd $(srcdir) \ 55 | && $(_build-aux)/git-version-gen \ 56 | .tarball-version \ 57 | $(git-version-gen-tag-sed-script)) 58 | ifneq ($(_curr-ver),$(VERSION)) 59 | ifeq ($(_curr-ver),UNKNOWN) 60 | $(info WARNING: unable to verify if $(VERSION) is the correct version) 61 | else 62 | ifneq (,$(_is-install-target)) 63 | # GNU Coding Standards state that 'make install' should not cause 64 | # recompilation after 'make all'. But as long as changing the version 65 | # string alters config.h, the cost of having 'make all' always have an 66 | # up-to-date version is prohibitive. So, as a compromise, we merely 67 | # warn when installing a version string that is out of date; the user 68 | # should run 'autoreconf' (or something like 'make distcheck') to 69 | # fix the version, 'make all' to propagate it, then 'make install'. 70 | $(info WARNING: version string $(VERSION) is out of date;) 71 | $(info run '$(MAKE) _version' to fix it) 72 | else 73 | $(info INFO: running autoreconf for new version string: $(_curr-ver)) 74 | GNUmakefile: _version 75 | touch GNUmakefile 76 | endif 77 | endif 78 | endif 79 | endif 80 | endif 81 | 82 | .PHONY: _version 83 | _version: 84 | cd $(srcdir) && rm -rf autom4te.cache .version && $(_autoreconf) 85 | $(MAKE) $(AM_MAKEFLAGS) Makefile 86 | 87 | else 88 | 89 | .DEFAULT_GOAL := abort-due-to-no-makefile 90 | srcdir = . 91 | 92 | # The package can override .DEFAULT_GOAL to run actions like autoreconf. 93 | -include ./cfg.mk 94 | 95 | # Allow cfg.mk to override these. 96 | _build-aux ?= build-aux 97 | _autoreconf ?= autoreconf -v 98 | 99 | include ./maint.mk 100 | 101 | ifeq ($(.DEFAULT_GOAL),abort-due-to-no-makefile) 102 | $(MAKECMDGOALS): abort-due-to-no-makefile 103 | endif 104 | 105 | abort-due-to-no-makefile: 106 | @echo There seems to be no Makefile in this directory. 1>&2 107 | @echo "You must run ./configure before running 'make'." 1>&2 108 | @exit 1 109 | 110 | endif 111 | 112 | # Tell version 3.79 and up of GNU make to not build goals in this 113 | # directory in parallel, in case someone tries to build multiple 114 | # targets, and one of them can cause a recursive target to be invoked. 115 | 116 | # Only set this if Automake doesn't provide it. 117 | AM_RECURSIVE_TARGETS ?= $(RECURSIVE_TARGETS:-recursive=) \ 118 | $(RECURSIVE_CLEAN_TARGETS:-recursive=) \ 119 | dist distcheck tags ctags 120 | 121 | ALL_RECURSIVE_TARGETS += $(AM_RECURSIVE_TARGETS) 122 | 123 | ifneq ($(word 2, $(MAKECMDGOALS)), ) 124 | ifneq ($(filter $(ALL_RECURSIVE_TARGETS), $(MAKECMDGOALS)), ) 125 | .NOTPARALLEL: 126 | endif 127 | endif 128 | -------------------------------------------------------------------------------- /tests/test-1.c: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2017, 2019 Giuseppe Scrivano 2 | 3 | libocispec is free software; you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation; either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | libocispec is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with libocispec. If not, see . 15 | 16 | */ 17 | 18 | #include "config.h" 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "ocispec/runtime_spec_schema_config_schema.h" 24 | 25 | int 26 | main () 27 | { 28 | parser_error err; 29 | runtime_spec_schema_config_schema *container = runtime_spec_schema_config_schema_parse_file ("tests/data/config.json", 0, &err); 30 | runtime_spec_schema_config_schema *container_gen = NULL; 31 | char *json_buf = NULL; 32 | 33 | if (container == NULL) { 34 | printf ("error %s\n", err); 35 | exit (1); 36 | } 37 | json_buf = runtime_spec_schema_config_schema_generate_json(container, 0, &err); 38 | if (json_buf == NULL) { 39 | printf("gen error %s\n", err); 40 | exit (1); 41 | } 42 | container_gen = runtime_spec_schema_config_schema_parse_data(json_buf, 0, &err); 43 | if (container == NULL) { 44 | printf ("parse error %s\n", err); 45 | exit (1); 46 | } 47 | 48 | if (strcmp (container->hostname, "runc") && strcmp(container->hostname, container_gen->hostname)) 49 | exit (5); 50 | if (strcmp (container->process->cwd, "/cwd") && strcmp (container->process->cwd, container_gen->process->cwd)) 51 | exit (51); 52 | if (container->process->user->uid != 101 || container_gen->process->user->uid != 101) 53 | exit (52); 54 | if (!container->process->terminal_present) 55 | exit (53); 56 | if (!container->process->user->uid_present || container_gen->process->user->gid_present) 57 | exit (6); 58 | if (strcmp (container->process->args[0], "ARGS1") && strcmp (container->process->args[0], container_gen->process->args[0])) 59 | exit (61); 60 | if (strcmp (container->mounts[0]->destination, "/proc") && strcmp (container->mounts[0]->destination, container_gen->mounts[0]->destination)) 61 | exit (62); 62 | if (container->linux->resources->block_io->weight_device[0]->major != 8 || container_gen->linux->resources->block_io->weight_device[0]->major != 8) 63 | exit (5); 64 | if (container->linux->resources->block_io->weight_device[0]->minor != 0 || container_gen->linux->resources->block_io->weight_device[0]->minor != 0) 65 | exit (5); 66 | if (container->linux->resources->block_io->weight_device[0]->weight != 500 || container_gen->linux->resources->block_io->weight_device[0]->weight != 500) 67 | exit (5); 68 | if (container->linux->resources->block_io->weight_device[0]->leaf_weight != 300 || container_gen->linux->resources->block_io->weight_device[0]->leaf_weight != 300) 69 | exit (5); 70 | if (container->linux->resources->block_io->throttle_read_bps_device[0]->major != 8 || container_gen->linux->resources->block_io->throttle_read_bps_device[0]->major != 8) 71 | exit (5); 72 | if (container->linux->resources->block_io->throttle_read_bps_device[0]->minor != 0 || container_gen->linux->resources->block_io->throttle_read_bps_device[0]->minor != 0) 73 | exit (5); 74 | if (container->linux->resources->block_io->throttle_read_bps_device[0]->rate != 600 || container_gen->linux->resources->block_io->throttle_read_bps_device[0]->rate != 600) 75 | exit (5); 76 | if (container->linux->resources->block_io->throttle_write_iops_device[0]->major != 8 || container_gen->linux->resources->block_io->throttle_write_iops_device[0]->major != 8) 77 | exit (5); 78 | if (container->linux->resources->block_io->throttle_write_iops_device[0]->minor != 16 || container_gen->linux->resources->block_io->throttle_write_iops_device[0]->minor != 16) 79 | exit (5); 80 | if (container->linux->resources->block_io->throttle_write_iops_device[0]->rate != 300 || container_gen->linux->resources->block_io->throttle_write_iops_device[0]->rate != 300) 81 | exit (5); 82 | if (container->linux->namespaces_len != 5 || container_gen->linux->namespaces_len != 5) 83 | exit (5); 84 | if (strcmp(container->linux->namespaces[2]->type, "ipc") && strcmp(container->linux->namespaces[2]->type, container_gen->linux->namespaces[2]->type)) 85 | exit (5); 86 | if (container->linux->seccomp == NULL || container->linux->seccomp->flags == NULL || container->linux->seccomp->flags_len != 0) 87 | exit (5); 88 | 89 | free_runtime_spec_schema_config_schema (clone_runtime_spec_schema_config_schema (container)); 90 | free_runtime_spec_schema_config_schema_process (clone_runtime_spec_schema_config_schema_process (container->process)); 91 | 92 | free(json_buf); 93 | free_runtime_spec_schema_config_schema (container); 94 | free_runtime_spec_schema_config_schema (container_gen); 95 | exit (0); 96 | } 97 | -------------------------------------------------------------------------------- /tests/data/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "ociVersion": "1.0.0-rc5", 3 | "platform": { 4 | "os": "linux", 5 | "arch": "amd64" 6 | }, 7 | "process": { 8 | "terminal": true, 9 | "consoleSize": { 10 | "height": 0, 11 | "width": 0 12 | }, 13 | "user": { 14 | "uid": 101 15 | }, 16 | "args": [ 17 | "ARGS1", 18 | "sh" 19 | ], 20 | "env": [ 21 | "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 22 | "TERM=xterm" 23 | ], 24 | "cwd": "/cwd", 25 | "capabilities": { 26 | "bounding": [ 27 | "CAP_AUDIT_WRITE", 28 | "CAP_KILL", 29 | "CAP_NET_BIND_SERVICE" 30 | ], 31 | "effective": [ 32 | "CAP_AUDIT_WRITE", 33 | "CAP_KILL", 34 | "CAP_NET_BIND_SERVICE" 35 | ], 36 | "inheritable": [ 37 | "CAP_AUDIT_WRITE", 38 | "CAP_KILL", 39 | "CAP_NET_BIND_SERVICE" 40 | ], 41 | "permitted": [ 42 | "CAP_AUDIT_WRITE", 43 | "CAP_KILL", 44 | "CAP_NET_BIND_SERVICE" 45 | ], 46 | "ambient": [ 47 | "CAP_AUDIT_WRITE", 48 | "CAP_KILL", 49 | "CAP_NET_BIND_SERVICE" 50 | ] 51 | }, 52 | "rlimits": [ 53 | { 54 | "type": "RLIMIT_NOFILE", 55 | "hard": 1024, 56 | "soft": 1024 57 | } 58 | ], 59 | "noNewPrivileges": true 60 | }, 61 | "root": { 62 | "path": "rootfs", 63 | "readonly": true 64 | }, 65 | "hostname": "runc", 66 | "mounts": [ 67 | { 68 | "destination": "/proc", 69 | "type": "proc", 70 | "source": "proc" 71 | }, 72 | { 73 | "destination": "/dev", 74 | "type": "tmpfs", 75 | "source": "tmpfs", 76 | "options": [ 77 | "nosuid", 78 | "strictatime", 79 | "mode=755", 80 | "size=65536k" 81 | ] 82 | }, 83 | { 84 | "destination": "/dev/pts", 85 | "type": "devpts", 86 | "source": "devpts", 87 | "options": [ 88 | "nosuid", 89 | "noexec", 90 | "newinstance", 91 | "ptmxmode=0666", 92 | "mode=0620", 93 | "gid=5" 94 | ] 95 | }, 96 | { 97 | "destination": "/dev/shm", 98 | "type": "tmpfs", 99 | "source": "shm", 100 | "options": [ 101 | "nosuid", 102 | "noexec", 103 | "nodev", 104 | "mode=1777", 105 | "size=65536k" 106 | ] 107 | }, 108 | { 109 | "destination": "/dev/mqueue", 110 | "type": "mqueue", 111 | "source": "mqueue", 112 | "options": [ 113 | "nosuid", 114 | "noexec", 115 | "nodev" 116 | ] 117 | }, 118 | { 119 | "destination": "/sys", 120 | "type": "sysfs", 121 | "source": "sysfs", 122 | "options": [ 123 | "nosuid", 124 | "noexec", 125 | "nodev", 126 | "ro" 127 | ] 128 | }, 129 | { 130 | "destination": "/sys/fs/cgroup", 131 | "type": "cgroup", 132 | "source": "cgroup", 133 | "options": [ 134 | "nosuid", 135 | "noexec", 136 | "nodev", 137 | "relatime", 138 | "ro" 139 | ] 140 | } 141 | ], 142 | "hooks": {}, 143 | "solaris": { 144 | "anet": [ 145 | { 146 | "allowedAddress": "172.17.0.2/16", 147 | "configureAllowedAddress": "true", 148 | "defrouter": "172.17.0.1/16", 149 | "linkProtection": "mac-nospoof, ip-nospoof", 150 | "linkname": "net0", 151 | "lowerLink": "net2", 152 | "macAddress": "02:42:f8:52:c7:16" 153 | } 154 | ] 155 | }, 156 | "linux": { 157 | "seccomp" : { 158 | "defaultAction": "SCMP_ACT_ALLOW", 159 | "flags" : [] 160 | }, 161 | "sysctl": { 162 | "net.ipv4.ip_forward": "1", 163 | "net.core.somaxconn": "256" 164 | }, 165 | "resources": { 166 | "blockIO": { 167 | "weight": 10, 168 | "leafWeight": 10, 169 | "weightDevice": [ 170 | { 171 | "major": 8, 172 | "minor": 0, 173 | "weight": 500, 174 | "leafWeight": 300 175 | }, 176 | { 177 | "major": 8, 178 | "minor": 16, 179 | "weight": 500 180 | } 181 | ], 182 | "throttleReadBpsDevice": [ 183 | { 184 | "major": 8, 185 | "minor": 0, 186 | "rate": 600 187 | } 188 | ], 189 | "throttleWriteIOPSDevice": [ 190 | { 191 | "major": 8, 192 | "minor": 16, 193 | "rate": 300 194 | } 195 | ] 196 | }, 197 | "devices": [ 198 | { 199 | "allow": false, 200 | "access": "rwm" 201 | } 202 | ] 203 | }, 204 | "namespaces": [ 205 | { 206 | "type": "pid" 207 | }, 208 | { 209 | "type": "network" 210 | }, 211 | { 212 | "type": "ipc" 213 | }, 214 | { 215 | "type": "uts" 216 | }, 217 | { 218 | "type": "mount" 219 | } 220 | ], 221 | "maskedPaths": [ 222 | "/proc/kcore", 223 | "/proc/latency_stats", 224 | "/proc/timer_list", 225 | "/proc/timer_stats", 226 | "/proc/sched_debug", 227 | "/sys/firmware" 228 | ], 229 | "readonlyPaths": [ 230 | "/proc/asound", 231 | "/proc/bus", 232 | "/proc/fs", 233 | "/proc/irq", 234 | "/proc/sys", 235 | "/proc/sysrq-trigger" 236 | ] 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "autocfg" 5 | version = "1.0.1" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 8 | 9 | [[package]] 10 | name = "chrono" 11 | version = "0.4.19" 12 | source = "registry+https://github.com/rust-lang/crates.io-index" 13 | checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" 14 | dependencies = [ 15 | "libc", 16 | "num-integer", 17 | "num-traits", 18 | "serde", 19 | "time", 20 | "winapi", 21 | ] 22 | 23 | [[package]] 24 | name = "form_urlencoded" 25 | version = "1.0.1" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" 28 | dependencies = [ 29 | "matches", 30 | "percent-encoding", 31 | ] 32 | 33 | [[package]] 34 | name = "idna" 35 | version = "0.2.2" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | checksum = "89829a5d69c23d348314a7ac337fe39173b61149a9864deabd260983aed48c21" 38 | dependencies = [ 39 | "matches", 40 | "unicode-bidi", 41 | "unicode-normalization", 42 | ] 43 | 44 | [[package]] 45 | name = "itoa" 46 | version = "0.4.7" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" 49 | 50 | [[package]] 51 | name = "libc" 52 | version = "0.2.93" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41" 55 | 56 | [[package]] 57 | name = "libocispec" 58 | version = "0.1.0" 59 | dependencies = [ 60 | "chrono", 61 | "serde", 62 | "serde-value", 63 | "serde_derive", 64 | "serde_json", 65 | "url", 66 | ] 67 | 68 | [[package]] 69 | name = "matches" 70 | version = "0.1.8" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" 73 | 74 | [[package]] 75 | name = "num-integer" 76 | version = "0.1.44" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" 79 | dependencies = [ 80 | "autocfg", 81 | "num-traits", 82 | ] 83 | 84 | [[package]] 85 | name = "num-traits" 86 | version = "0.2.14" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 89 | dependencies = [ 90 | "autocfg", 91 | ] 92 | 93 | [[package]] 94 | name = "ordered-float" 95 | version = "2.1.1" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "766f840da25490628d8e63e529cd21c014f6600c6b8517add12a6fa6167a6218" 98 | dependencies = [ 99 | "num-traits", 100 | ] 101 | 102 | [[package]] 103 | name = "percent-encoding" 104 | version = "2.1.0" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 107 | 108 | [[package]] 109 | name = "proc-macro2" 110 | version = "1.0.26" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" 113 | dependencies = [ 114 | "unicode-xid", 115 | ] 116 | 117 | [[package]] 118 | name = "quote" 119 | version = "1.0.9" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" 122 | dependencies = [ 123 | "proc-macro2", 124 | ] 125 | 126 | [[package]] 127 | name = "ryu" 128 | version = "1.0.5" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 131 | 132 | [[package]] 133 | name = "serde" 134 | version = "1.0.125" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" 137 | dependencies = [ 138 | "serde_derive", 139 | ] 140 | 141 | [[package]] 142 | name = "serde-value" 143 | version = "0.7.0" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" 146 | dependencies = [ 147 | "ordered-float", 148 | "serde", 149 | ] 150 | 151 | [[package]] 152 | name = "serde_derive" 153 | version = "1.0.125" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" 156 | dependencies = [ 157 | "proc-macro2", 158 | "quote", 159 | "syn", 160 | ] 161 | 162 | [[package]] 163 | name = "serde_json" 164 | version = "1.0.64" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" 167 | dependencies = [ 168 | "itoa", 169 | "ryu", 170 | "serde", 171 | ] 172 | 173 | [[package]] 174 | name = "syn" 175 | version = "1.0.69" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb" 178 | dependencies = [ 179 | "proc-macro2", 180 | "quote", 181 | "unicode-xid", 182 | ] 183 | 184 | [[package]] 185 | name = "time" 186 | version = "0.1.44" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" 189 | dependencies = [ 190 | "libc", 191 | "wasi", 192 | "winapi", 193 | ] 194 | 195 | [[package]] 196 | name = "tinyvec" 197 | version = "1.2.0" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342" 200 | dependencies = [ 201 | "tinyvec_macros", 202 | ] 203 | 204 | [[package]] 205 | name = "tinyvec_macros" 206 | version = "0.1.0" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" 209 | 210 | [[package]] 211 | name = "unicode-bidi" 212 | version = "0.3.5" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0" 215 | dependencies = [ 216 | "matches", 217 | ] 218 | 219 | [[package]] 220 | name = "unicode-normalization" 221 | version = "0.1.17" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "07fbfce1c8a97d547e8b5334978438d9d6ec8c20e38f56d4a4374d181493eaef" 224 | dependencies = [ 225 | "tinyvec", 226 | ] 227 | 228 | [[package]] 229 | name = "unicode-xid" 230 | version = "0.2.1" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 233 | 234 | [[package]] 235 | name = "url" 236 | version = "2.2.1" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "9ccd964113622c8e9322cfac19eb1004a07e636c545f325da085d5cdde6f1f8b" 239 | dependencies = [ 240 | "form_urlencoded", 241 | "idna", 242 | "matches", 243 | "percent-encoding", 244 | "serde", 245 | ] 246 | 247 | [[package]] 248 | name = "wasi" 249 | version = "0.10.0+wasi-snapshot-preview1" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" 252 | 253 | [[package]] 254 | name = "winapi" 255 | version = "0.3.9" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 258 | dependencies = [ 259 | "winapi-i686-pc-windows-gnu", 260 | "winapi-x86_64-pc-windows-gnu", 261 | ] 262 | 263 | [[package]] 264 | name = "winapi-i686-pc-windows-gnu" 265 | version = "0.4.0" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 268 | 269 | [[package]] 270 | name = "winapi-x86_64-pc-windows-gnu" 271 | version = "0.4.0" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 274 | -------------------------------------------------------------------------------- /src/ocispec/json_common.h: -------------------------------------------------------------------------------- 1 | #ifndef _JSON_COMMON_H 2 | #define _JSON_COMMON_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | #undef linux 17 | 18 | #ifdef __MUSL__ 19 | #undef stdin 20 | #undef stdout 21 | #undef stderr 22 | #define stdin stdin 23 | #define stdout stdout 24 | #define stderr stderr 25 | #endif 26 | 27 | // options to report error if there is unknown key found in json 28 | #define OPT_PARSE_STRICT 0x01 29 | // options to generate all key and value 30 | #define OPT_GEN_KEY_VALUE 0x02 31 | // options to generate simplify(no indent) json string 32 | #define OPT_GEN_SIMPLIFY 0x04 33 | // options to keep all keys and values, even do not known 34 | #define OPT_PARSE_FULLKEY 0x08 35 | // options not to validate utf8 data 36 | #define OPT_GEN_NO_VALIDATE_UTF8 0x10 37 | 38 | #define define_cleaner_function(type, cleaner) \ 39 | static inline void cleaner##_function (type *ptr) \ 40 | { \ 41 | if (*ptr) \ 42 | cleaner (*ptr); \ 43 | } 44 | 45 | #define __auto_cleanup(cleaner) __attribute__ ((__cleanup__ (cleaner##_function))) 46 | 47 | static inline void 48 | ptr_free_function (void *p) 49 | { 50 | free (*(void **) p); 51 | } 52 | 53 | #define __auto_free __auto_cleanup (ptr_free) 54 | 55 | #define move_ptr(ptr) \ 56 | ({ \ 57 | typeof (ptr) moved_ptr = (ptr); \ 58 | (ptr) = NULL; \ 59 | moved_ptr; \ 60 | }) 61 | 62 | #define GEN_SET_ERROR_AND_RETURN(stat, err) \ 63 | { \ 64 | if (*(err) == NULL) \ 65 | { \ 66 | if (asprintf (err, "%s: %s: %d: error generating json, errcode: %u", __FILE__, __func__, __LINE__, stat) < 0) \ 67 | { \ 68 | *(err) = strdup ("error allocating memory"); \ 69 | } \ 70 | } \ 71 | return stat; \ 72 | } 73 | 74 | typedef char *parser_error; 75 | 76 | struct parser_context 77 | { 78 | unsigned int options; 79 | FILE *errfile; 80 | }; 81 | 82 | yajl_gen_status gen_yajl_object_residual (yajl_val obj, yajl_gen g, parser_error *err); 83 | 84 | yajl_gen_status map_uint (void *ctx, long long unsigned int num); 85 | 86 | yajl_gen_status map_int (void *ctx, long long int num); 87 | 88 | bool json_gen_init (yajl_gen *g, const struct parser_context *ctx); 89 | 90 | yajl_val get_val (yajl_val tree, const char *name, yajl_type type); 91 | 92 | char *safe_strdup (const char *src); 93 | 94 | void *safe_malloc (size_t size); 95 | 96 | int common_safe_double (const char *numstr, double *converted); 97 | 98 | int common_safe_uint8 (const char *numstr, uint8_t *converted); 99 | 100 | int common_safe_uint16 (const char *numstr, uint16_t *converted); 101 | 102 | int common_safe_uint32 (const char *numstr, uint32_t *converted); 103 | 104 | int common_safe_uint64 (const char *numstr, uint64_t *converted); 105 | 106 | int common_safe_uint (const char *numstr, unsigned int *converted); 107 | 108 | int common_safe_int8 (const char *numstr, int8_t *converted); 109 | 110 | int common_safe_int16 (const char *numstr, int16_t *converted); 111 | 112 | int common_safe_int32 (const char *numstr, int32_t *converted); 113 | 114 | int common_safe_int64 (const char *numstr, int64_t *converted); 115 | 116 | int common_safe_int (const char *numstr, int *converted); 117 | 118 | typedef struct 119 | { 120 | int *keys; 121 | int *values; 122 | size_t len; 123 | } json_map_int_int; 124 | 125 | void free_json_map_int_int (json_map_int_int *map); 126 | 127 | json_map_int_int *make_json_map_int_int (yajl_val src, const struct parser_context *ctx, parser_error *err); 128 | 129 | yajl_gen_status gen_json_map_int_int (void *ctx, const json_map_int_int *map, const struct parser_context *ptx, 130 | parser_error *err); 131 | 132 | int append_json_map_int_int (json_map_int_int *map, int key, int val); 133 | 134 | typedef struct 135 | { 136 | int *keys; 137 | bool *values; 138 | size_t len; 139 | } json_map_int_bool; 140 | 141 | void free_json_map_int_bool (json_map_int_bool *map); 142 | 143 | json_map_int_bool *make_json_map_int_bool (yajl_val src, const struct parser_context *ctx, parser_error *err); 144 | 145 | yajl_gen_status gen_json_map_int_bool (void *ctx, const json_map_int_bool *map, const struct parser_context *ptx, 146 | parser_error *err); 147 | 148 | int append_json_map_int_bool (json_map_int_bool *map, int key, bool val); 149 | 150 | typedef struct 151 | { 152 | int *keys; 153 | char **values; 154 | size_t len; 155 | } json_map_int_string; 156 | 157 | void free_json_map_int_string (json_map_int_string *map); 158 | 159 | json_map_int_string *make_json_map_int_string (yajl_val src, const struct parser_context *ctx, parser_error *err); 160 | 161 | yajl_gen_status gen_json_map_int_string (void *ctx, const json_map_int_string *map, const struct parser_context *ptx, 162 | parser_error *err); 163 | 164 | int append_json_map_int_string (json_map_int_string *map, int key, const char *val); 165 | 166 | typedef struct 167 | { 168 | char **keys; 169 | int *values; 170 | size_t len; 171 | } json_map_string_int; 172 | 173 | void free_json_map_string_int (json_map_string_int *map); 174 | 175 | json_map_string_int *make_json_map_string_int (yajl_val src, const struct parser_context *ctx, parser_error *err); 176 | 177 | yajl_gen_status gen_json_map_string_int (void *ctx, const json_map_string_int *map, const struct parser_context *ptx, 178 | parser_error *err); 179 | 180 | int append_json_map_string_int (json_map_string_int *map, const char *key, int val); 181 | 182 | typedef struct 183 | { 184 | char **keys; 185 | bool *values; 186 | size_t len; 187 | } json_map_string_bool; 188 | 189 | void free_json_map_string_bool (json_map_string_bool *map); 190 | 191 | json_map_string_bool *make_json_map_string_bool (yajl_val src, const struct parser_context *ctx, parser_error *err); 192 | 193 | typedef struct 194 | { 195 | char **keys; 196 | int64_t *values; 197 | size_t len; 198 | } json_map_string_int64; 199 | 200 | void free_json_map_string_int64 (json_map_string_int64 *map); 201 | 202 | json_map_string_int64 *make_json_map_string_int64 (yajl_val src, const struct parser_context *ctx, parser_error *err); 203 | 204 | yajl_gen_status gen_json_map_string_int64 (void *ctx, const json_map_string_int64 *map, 205 | const struct parser_context *ptx, parser_error *err); 206 | 207 | int append_json_map_string_int64 (json_map_string_int64 *map, const char *key, int64_t val); 208 | 209 | yajl_gen_status gen_json_map_string_bool (void *ctx, const json_map_string_bool *map, const struct parser_context *ptx, 210 | parser_error *err); 211 | 212 | int append_json_map_string_bool (json_map_string_bool *map, const char *key, bool val); 213 | 214 | typedef struct 215 | { 216 | char **keys; 217 | char **values; 218 | size_t len; 219 | } json_map_string_string; 220 | 221 | void free_json_map_string_string (json_map_string_string *map); 222 | 223 | json_map_string_string *clone_map_string_string (json_map_string_string *src); 224 | 225 | json_map_string_string *make_json_map_string_string (yajl_val src, const struct parser_context *ctx, parser_error *err); 226 | 227 | yajl_gen_status gen_json_map_string_string (void *ctx, const json_map_string_string *map, 228 | const struct parser_context *ptx, parser_error *err); 229 | 230 | int append_json_map_string_string (json_map_string_string *map, const char *key, const char *val); 231 | 232 | char *json_marshal_string (const char *str, size_t length, const struct parser_context *ctx, parser_error *err); 233 | 234 | #ifdef __cplusplus 235 | } 236 | #endif 237 | 238 | #endif 239 | -------------------------------------------------------------------------------- /tests/test-12.c: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2025 Giuseppe Scrivano 2 | 3 | libocispec is free software; you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation; either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | libocispec is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with libocispec. If not, see . 15 | 16 | */ 17 | 18 | /* Test clone round-trip: parse -> clone -> generate -> parse -> compare */ 19 | 20 | #include "config.h" 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "ocispec/runtime_spec_schema_config_schema.h" 26 | 27 | static int 28 | test_clone_roundtrip (void) 29 | { 30 | parser_error err = NULL; 31 | runtime_spec_schema_config_schema *original = NULL; 32 | runtime_spec_schema_config_schema *cloned = NULL; 33 | runtime_spec_schema_config_schema *reparsed = NULL; 34 | char *json_from_original = NULL; 35 | char *json_from_clone = NULL; 36 | int ret = 0; 37 | 38 | /* Parse original */ 39 | original = runtime_spec_schema_config_schema_parse_file ("tests/data/config.json", 0, &err); 40 | if (original == NULL) 41 | { 42 | printf ("parse error: %s\n", err); 43 | ret = 1; 44 | goto out; 45 | } 46 | 47 | /* Clone it */ 48 | cloned = clone_runtime_spec_schema_config_schema (original); 49 | if (cloned == NULL) 50 | { 51 | printf ("clone failed (original hostname=%s, ociVersion=%s)\n", 52 | original->hostname ? original->hostname : "(null)", 53 | original->oci_version ? original->oci_version : "(null)"); 54 | ret = 2; 55 | goto out; 56 | } 57 | 58 | /* Generate JSON from both */ 59 | json_from_original = runtime_spec_schema_config_schema_generate_json (original, 0, &err); 60 | if (json_from_original == NULL) 61 | { 62 | printf ("generate from original error: %s\n", err); 63 | ret = 3; 64 | goto out; 65 | } 66 | 67 | json_from_clone = runtime_spec_schema_config_schema_generate_json (cloned, 0, &err); 68 | if (json_from_clone == NULL) 69 | { 70 | printf ("generate from clone error: %s\n", err); 71 | ret = 4; 72 | goto out; 73 | } 74 | 75 | /* The generated JSON should be identical */ 76 | if (strcmp (json_from_original, json_from_clone) != 0) 77 | { 78 | printf ("JSON mismatch between original and clone\n"); 79 | printf ("Original:\n%s\n", json_from_original); 80 | printf ("Clone:\n%s\n", json_from_clone); 81 | ret = 5; 82 | goto out; 83 | } 84 | 85 | /* Verify we can parse the cloned output */ 86 | reparsed = runtime_spec_schema_config_schema_parse_data (json_from_clone, 0, &err); 87 | if (reparsed == NULL) 88 | { 89 | printf ("reparse error: %s\n", err); 90 | ret = 6; 91 | goto out; 92 | } 93 | 94 | /* Verify key fields match */ 95 | if (strcmp (original->hostname, cloned->hostname) != 0) 96 | { 97 | printf ("hostname mismatch\n"); 98 | ret = 7; 99 | goto out; 100 | } 101 | 102 | if (strcmp (original->process->cwd, cloned->process->cwd) != 0) 103 | { 104 | printf ("cwd mismatch\n"); 105 | ret = 8; 106 | goto out; 107 | } 108 | 109 | if (original->process->user->uid != cloned->process->user->uid) 110 | { 111 | printf ("uid mismatch\n"); 112 | ret = 9; 113 | goto out; 114 | } 115 | 116 | if (original->linux->namespaces_len != cloned->linux->namespaces_len) 117 | { 118 | printf ("namespaces_len mismatch\n"); 119 | ret = 10; 120 | goto out; 121 | } 122 | 123 | printf ("clone roundtrip test passed\n"); 124 | 125 | out: 126 | free (err); 127 | free (json_from_original); 128 | free (json_from_clone); 129 | free_runtime_spec_schema_config_schema (original); 130 | free_runtime_spec_schema_config_schema (cloned); 131 | free_runtime_spec_schema_config_schema (reparsed); 132 | return ret; 133 | } 134 | 135 | static int 136 | test_modify_clone_independence (void) 137 | { 138 | parser_error err = NULL; 139 | runtime_spec_schema_config_schema *original = NULL; 140 | runtime_spec_schema_config_schema *cloned = NULL; 141 | int ret = 0; 142 | const char *original_hostname; 143 | 144 | original = runtime_spec_schema_config_schema_parse_file ("tests/data/config.json", 0, &err); 145 | if (original == NULL) 146 | { 147 | printf ("parse error: %s\n", err); 148 | ret = 1; 149 | goto out; 150 | } 151 | 152 | original_hostname = original->hostname; 153 | 154 | cloned = clone_runtime_spec_schema_config_schema (original); 155 | if (cloned == NULL) 156 | { 157 | printf ("clone failed\n"); 158 | ret = 2; 159 | goto out; 160 | } 161 | 162 | /* Modify the clone */ 163 | free (cloned->hostname); 164 | cloned->hostname = strdup ("modified-hostname"); 165 | if (cloned->hostname == NULL) 166 | { 167 | printf ("strdup failed\n"); 168 | ret = 3; 169 | goto out; 170 | } 171 | 172 | /* Original should be unchanged */ 173 | if (strcmp (original->hostname, original_hostname) != 0) 174 | { 175 | printf ("original was modified when clone was changed\n"); 176 | ret = 4; 177 | goto out; 178 | } 179 | 180 | if (strcmp (cloned->hostname, "modified-hostname") != 0) 181 | { 182 | printf ("clone modification failed\n"); 183 | ret = 5; 184 | goto out; 185 | } 186 | 187 | printf ("clone independence test passed\n"); 188 | 189 | out: 190 | free (err); 191 | free_runtime_spec_schema_config_schema (original); 192 | free_runtime_spec_schema_config_schema (cloned); 193 | return ret; 194 | } 195 | 196 | static int 197 | test_present_flags (void) 198 | { 199 | parser_error err = NULL; 200 | runtime_spec_schema_config_schema *config = NULL; 201 | int ret = 0; 202 | 203 | config = runtime_spec_schema_config_schema_parse_file ("tests/data/config.json", 0, &err); 204 | if (config == NULL) 205 | { 206 | printf ("parse error: %s\n", err); 207 | ret = 1; 208 | goto out; 209 | } 210 | 211 | /* Check that present flags are set correctly */ 212 | if (!config->process->terminal_present) 213 | { 214 | printf ("terminal_present should be true\n"); 215 | ret = 2; 216 | goto out; 217 | } 218 | 219 | if (!config->process->user->uid_present) 220 | { 221 | printf ("uid_present should be true\n"); 222 | ret = 3; 223 | goto out; 224 | } 225 | 226 | /* gid is not set in the test data */ 227 | if (config->process->user->gid_present) 228 | { 229 | printf ("gid_present should be false\n"); 230 | ret = 4; 231 | goto out; 232 | } 233 | 234 | /* Verify numeric values */ 235 | if (config->process->user->uid != 101) 236 | { 237 | printf ("uid should be 101, got %u\n", config->process->user->uid); 238 | ret = 5; 239 | goto out; 240 | } 241 | 242 | printf ("present flags test passed\n"); 243 | 244 | out: 245 | free (err); 246 | free_runtime_spec_schema_config_schema (config); 247 | return ret; 248 | } 249 | 250 | static int 251 | test_array_cloning (void) 252 | { 253 | parser_error err = NULL; 254 | runtime_spec_schema_config_schema *original = NULL; 255 | runtime_spec_schema_config_schema *cloned = NULL; 256 | int ret = 0; 257 | size_t i; 258 | 259 | original = runtime_spec_schema_config_schema_parse_file ("tests/data/config.json", 0, &err); 260 | if (original == NULL) 261 | { 262 | printf ("parse error: %s\n", err); 263 | ret = 1; 264 | goto out; 265 | } 266 | 267 | cloned = clone_runtime_spec_schema_config_schema (original); 268 | if (cloned == NULL) 269 | { 270 | printf ("clone failed\n"); 271 | ret = 2; 272 | goto out; 273 | } 274 | 275 | /* Verify array lengths match */ 276 | if (original->process->args_len != cloned->process->args_len) 277 | { 278 | printf ("args_len mismatch\n"); 279 | ret = 3; 280 | goto out; 281 | } 282 | 283 | /* Verify array contents match */ 284 | for (i = 0; i < original->process->args_len; i++) 285 | { 286 | if (strcmp (original->process->args[i], cloned->process->args[i]) != 0) 287 | { 288 | printf ("args[%zu] mismatch\n", i); 289 | ret = 4; 290 | goto out; 291 | } 292 | } 293 | 294 | /* Verify mounts array */ 295 | if (original->mounts_len != cloned->mounts_len) 296 | { 297 | printf ("mounts_len mismatch\n"); 298 | ret = 5; 299 | goto out; 300 | } 301 | 302 | for (i = 0; i < original->mounts_len; i++) 303 | { 304 | if (strcmp (original->mounts[i]->destination, cloned->mounts[i]->destination) != 0) 305 | { 306 | printf ("mounts[%zu]->destination mismatch\n", i); 307 | ret = 6; 308 | goto out; 309 | } 310 | } 311 | 312 | printf ("array cloning test passed\n"); 313 | 314 | out: 315 | free (err); 316 | free_runtime_spec_schema_config_schema (original); 317 | free_runtime_spec_schema_config_schema (cloned); 318 | return ret; 319 | } 320 | 321 | int 322 | main (void) 323 | { 324 | int ret; 325 | 326 | ret = test_clone_roundtrip (); 327 | if (ret != 0) 328 | { 329 | printf ("test_clone_roundtrip failed: %d\n", ret); 330 | return ret; 331 | } 332 | 333 | ret = test_modify_clone_independence (); 334 | if (ret != 0) 335 | { 336 | printf ("test_modify_clone_independence failed: %d\n", ret); 337 | return ret; 338 | } 339 | 340 | ret = test_present_flags (); 341 | if (ret != 0) 342 | { 343 | printf ("test_present_flags failed: %d\n", ret); 344 | return ret; 345 | } 346 | 347 | ret = test_array_cloning (); 348 | if (ret != 0) 349 | { 350 | printf ("test_array_cloning failed: %d\n", ret); 351 | return ret; 352 | } 353 | 354 | return 0; 355 | } 356 | -------------------------------------------------------------------------------- /src/runtime/test/config.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "ociVersion": "0.5.0-dev", 3 | "process": { 4 | "terminal": true, 5 | "user": { 6 | "uid": 1, 7 | "gid": 1, 8 | "additionalGids": [ 9 | 5, 10 | 6 11 | ] 12 | }, 13 | "args": [ 14 | "sh" 15 | ], 16 | "env": [ 17 | "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 18 | "TERM=xterm" 19 | ], 20 | "cwd": "/", 21 | "capabilities": { 22 | "bounding": [ 23 | "CAP_AUDIT_WRITE", 24 | "CAP_KILL", 25 | "CAP_NET_BIND_SERVICE" 26 | ], 27 | "permitted": [ 28 | "CAP_AUDIT_WRITE", 29 | "CAP_KILL", 30 | "CAP_NET_BIND_SERVICE" 31 | ], 32 | "inheritable": [ 33 | "CAP_AUDIT_WRITE", 34 | "CAP_KILL", 35 | "CAP_NET_BIND_SERVICE" 36 | ], 37 | "effective": [ 38 | "CAP_AUDIT_WRITE", 39 | "CAP_KILL" 40 | ], 41 | "ambient": [ 42 | "CAP_NET_BIND_SERVICE" 43 | ] 44 | }, 45 | "rlimits": [ 46 | { 47 | "type": "RLIMIT_CORE", 48 | "hard": 1024, 49 | "soft": 1024 50 | }, 51 | { 52 | "type": "RLIMIT_NOFILE", 53 | "hard": 1024, 54 | "soft": 1024 55 | } 56 | ], 57 | "apparmorProfile": "acme_secure_profile", 58 | "oomScoreAdj": 100, 59 | "selinuxLabel": "system_u:system_r:svirt_lxc_net_t:s0:c124,c675", 60 | "noNewPrivileges": true 61 | }, 62 | "root": { 63 | "path": "rootfs", 64 | "readonly": true 65 | }, 66 | "hostname": "slartibartfast", 67 | "mounts": [ 68 | { 69 | "destination": "/proc", 70 | "type": "proc", 71 | "source": "proc" 72 | }, 73 | { 74 | "destination": "/dev", 75 | "type": "tmpfs", 76 | "source": "tmpfs", 77 | "options": [ 78 | "nosuid", 79 | "strictatime", 80 | "mode=755", 81 | "size=65536k" 82 | ] 83 | }, 84 | { 85 | "destination": "/dev/pts", 86 | "type": "devpts", 87 | "source": "devpts", 88 | "options": [ 89 | "nosuid", 90 | "noexec", 91 | "newinstance", 92 | "ptmxmode=0666", 93 | "mode=0620", 94 | "gid=5" 95 | ] 96 | }, 97 | { 98 | "destination": "/dev/shm", 99 | "type": "tmpfs", 100 | "source": "shm", 101 | "options": [ 102 | "nosuid", 103 | "noexec", 104 | "nodev", 105 | "mode=1777", 106 | "size=65536k" 107 | ] 108 | }, 109 | { 110 | "destination": "/dev/mqueue", 111 | "type": "mqueue", 112 | "source": "mqueue", 113 | "options": [ 114 | "nosuid", 115 | "noexec", 116 | "nodev" 117 | ] 118 | }, 119 | { 120 | "destination": "/sys", 121 | "type": "sysfs", 122 | "source": "sysfs", 123 | "options": [ 124 | "nosuid", 125 | "noexec", 126 | "nodev" 127 | ] 128 | }, 129 | { 130 | "destination": "/sys/fs/cgroup", 131 | "type": "cgroup", 132 | "source": "cgroup", 133 | "options": [ 134 | "nosuid", 135 | "noexec", 136 | "nodev", 137 | "relatime", 138 | "ro" 139 | ] 140 | } 141 | ], 142 | "hooks": { 143 | "prestart": [ 144 | { 145 | "path": "/usr/bin/fix-mounts", 146 | "args": [ 147 | "fix-mounts", 148 | "arg1", 149 | "arg2" 150 | ], 151 | "env": [ 152 | "key1=value1" 153 | ] 154 | }, 155 | { 156 | "path": "/usr/bin/setup-network" 157 | } 158 | ], 159 | "poststart": [ 160 | { 161 | "path": "/usr/bin/notify-start", 162 | "timeout": 5 163 | } 164 | ], 165 | "poststop": [ 166 | { 167 | "path": "/usr/sbin/cleanup.sh", 168 | "args": [ 169 | "cleanup.sh", 170 | "-f" 171 | ] 172 | } 173 | ] 174 | }, 175 | "linux": { 176 | "devices": [ 177 | { 178 | "path": "/dev/fuse", 179 | "type": "c", 180 | "major": 10, 181 | "minor": 229, 182 | "fileMode": 438, 183 | "uid": 0, 184 | "gid": 0 185 | }, 186 | { 187 | "path": "/dev/sda", 188 | "type": "b", 189 | "major": 8, 190 | "minor": 0, 191 | "fileMode": 432, 192 | "uid": 0, 193 | "gid": 0 194 | } 195 | ], 196 | "uidMappings": [ 197 | { 198 | "containerID": 0, 199 | "hostID": 1000, 200 | "size": 32000 201 | } 202 | ], 203 | "gidMappings": [ 204 | { 205 | "containerID": 0, 206 | "hostID": 1000, 207 | "size": 32000 208 | } 209 | ], 210 | "sysctl": { 211 | "net.ipv4.ip_forward": "1", 212 | "net.core.somaxconn": "256" 213 | }, 214 | "cgroupsPath": "/myRuntime/myContainer", 215 | "resources": { 216 | "network": { 217 | "classID": 1048577, 218 | "priorities": [ 219 | { 220 | "name": "eth0", 221 | "priority": 500 222 | }, 223 | { 224 | "name": "eth1", 225 | "priority": 1000 226 | } 227 | ] 228 | }, 229 | "pids": { 230 | "limit": 32771 231 | }, 232 | "hugepageLimits": [ 233 | { 234 | "pageSize": "2MB", 235 | "limit": 9223372036854772000 236 | } 237 | ], 238 | "memory": { 239 | "limit": 536870912, 240 | "reservation": 536870912, 241 | "swap": 536870912, 242 | "kernel": -1, 243 | "kernelTCP": -1, 244 | "swappiness": 0, 245 | "disableOOMKiller": false 246 | }, 247 | "cpu": { 248 | "shares": 1024, 249 | "quota": 1000000, 250 | "period": 500000, 251 | "realtimeRuntime": 950000, 252 | "realtimePeriod": 1000000, 253 | "cpus": "2-3", 254 | "mems": "0-7" 255 | }, 256 | "devices": [ 257 | { 258 | "allow": false, 259 | "access": "rwm" 260 | }, 261 | { 262 | "allow": true, 263 | "type": "c", 264 | "major": 10, 265 | "minor": 229, 266 | "access": "rw" 267 | }, 268 | { 269 | "allow": true, 270 | "type": "b", 271 | "major": 8, 272 | "minor": 0, 273 | "access": "r" 274 | } 275 | ], 276 | "blockIO": { 277 | "weight": 10, 278 | "leafWeight": 10, 279 | "weightDevice": [ 280 | { 281 | "major": 8, 282 | "minor": 0, 283 | "weight": 500, 284 | "leafWeight": 300 285 | }, 286 | { 287 | "major": 8, 288 | "minor": 16, 289 | "weight": 500 290 | } 291 | ], 292 | "throttleReadBpsDevice": [ 293 | { 294 | "major": 8, 295 | "minor": 0, 296 | "rate": 600 297 | } 298 | ], 299 | "throttleWriteIOPSDevice": [ 300 | { 301 | "major": 8, 302 | "minor": 16, 303 | "rate": 300 304 | } 305 | ] 306 | } 307 | }, 308 | "rootfsPropagation": "slave", 309 | "seccomp": { 310 | "defaultAction": "SCMP_ACT_ALLOW", 311 | "architectures": [ 312 | "SCMP_ARCH_X86", 313 | "SCMP_ARCH_X32" 314 | ], 315 | "syscalls": [ 316 | { 317 | "names": [ 318 | "getcwd", 319 | "chmod" 320 | ], 321 | "action": "SCMP_ACT_ERRNO" 322 | } 323 | ] 324 | }, 325 | "namespaces": [ 326 | { 327 | "type": "pid" 328 | }, 329 | { 330 | "type": "network" 331 | }, 332 | { 333 | "type": "ipc" 334 | }, 335 | { 336 | "type": "uts" 337 | }, 338 | { 339 | "type": "mount" 340 | }, 341 | { 342 | "type": "user" 343 | }, 344 | { 345 | "type": "cgroup" 346 | } 347 | ], 348 | "maskedPaths": [ 349 | "/proc/kcore", 350 | "/proc/latency_stats", 351 | "/proc/timer_stats", 352 | "/proc/sched_debug" 353 | ], 354 | "readonlyPaths": [ 355 | "/proc/asound", 356 | "/proc/bus", 357 | "/proc/fs", 358 | "/proc/irq", 359 | "/proc/sys", 360 | "/proc/sysrq-trigger" 361 | ], 362 | "mountLabel": "system_u:object_r:svirt_sandbox_file_t:s0:c715,c811" 363 | }, 364 | "annotations": { 365 | "com.example.key1": "value1", 366 | "com.example.key2": "value2" 367 | } 368 | } 369 | -------------------------------------------------------------------------------- /src/ocispec/helpers.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # libocispec - a C library for parsing OCI spec files. 4 | # 5 | # Copyright (C) Huawei Technologies., Ltd. 2018-2020. All rights reserved. 6 | # libocispec is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # libocispec is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with libocispec. If not, see . 18 | # 19 | # As a special exception, you may create a larger work that contains 20 | # part or all of the libocispec parser skeleton and distribute that work 21 | # under terms of your choice, so long as that work isn't itself a 22 | # parser generator using the skeleton or a modified version thereof 23 | # as a parser skeleton. Alternatively, if you modify or redistribute 24 | # the parser skeleton itself, you may (at your option) remove this 25 | # special exception, which will cause the skeleton and the resulting 26 | # libocispec output files to be licensed under the GNU General Public 27 | # License without this special exception. 28 | import os 29 | import sys 30 | 31 | cxx_reserved_keywords = ["class", "delete", "explicit", "friend", "mutable", "new", 32 | "operator", "private", "protected", "public", "throw", "try", "virtual"] 33 | 34 | def append_separator(substr): 35 | ''' 36 | Description: append only '_' at last position of subStr 37 | Interface: None 38 | History: 2019-09-20 39 | ''' 40 | if substr and substr[-1] != '_': 41 | substr.append('_') 42 | 43 | def conv_to_c_style(name): 44 | ''' 45 | Description: convert name to linux c format 46 | Interface: None 47 | History: 2019-06-17 48 | ''' 49 | if name is None or name == "": 50 | return "" 51 | # replace C++ reserved keywords (the generated C headers can be included in C++ applications) 52 | if name in cxx_reserved_keywords: 53 | name = '_' + name 54 | name = name.replace('.', '_').replace('-', '_').replace('/', '_') 55 | substr = [] 56 | preindex = 0 57 | index = 0 58 | for index, char in enumerate(name): 59 | if char == '_': 60 | append_separator(substr) 61 | substr.append(name[preindex:index].lower()) 62 | preindex = index + 1 63 | if not char.isupper() and name[preindex].isupper() and \ 64 | name[preindex + 1].isupper(): 65 | append_separator(substr) 66 | substr.append(name[preindex:index - 1].lower()) 67 | preindex = index - 1 68 | continue 69 | if char.isupper() and index > 0 and name[index - 1].islower(): 70 | append_separator(substr) 71 | substr.append(name[preindex:index].lower()) 72 | preindex = index 73 | 74 | if preindex <= index and index >= 0 and name[index] != '_' and \ 75 | preindex != 0: 76 | append_separator(substr) 77 | substr.append(name[preindex:index + 1].lower()) 78 | result = ''.join(substr) 79 | return result 80 | 81 | def get_map_c_types(typ): 82 | ''' 83 | Description: Get map c types 84 | Interface: None 85 | History: 2019-06-17 86 | ''' 87 | map_c_types = { 88 | 'byte': 'uint8_t', 89 | 'string': 'char *', 90 | 'integer': 'int', 91 | 'boolean': 'bool', 92 | 'double': 'double', 93 | 'int8': 'int8_t', 94 | "int16": 'int16_t', 95 | "int32": "int32_t", 96 | "int64": "int64_t", 97 | 'uint8': 'uint8_t', 98 | "uint16": 'uint16_t', 99 | "uint32": "uint32_t", 100 | "uint64": "uint64_t", 101 | "UID": "uid_t", 102 | "GID": "gid_t", 103 | "booleanPointer": "bool *", 104 | 'bytePointer': 'uint8_t *', 105 | 'integerPointer': 'int *', 106 | 'doublePointer': 'double *', 107 | 'int8Pointer': 'int8_t *', 108 | "int16Pointer": 'int16_t *', 109 | "int32Pointer": "int32_t *", 110 | "int64Pointer": "int64_t *", 111 | 'uint8Pointer': 'uint8_t *', 112 | "uint16Pointer": 'uint16_t *', 113 | "uint32Pointer": "uint32_t *", 114 | "uint64Pointer": "uint64_t *", 115 | } 116 | if typ in map_c_types: 117 | return map_c_types[typ] 118 | return "" 119 | 120 | def valid_basic_map_name(typ): 121 | ''' 122 | Description: Valid basic map name 123 | Interface: None 124 | History: 2019-06-17 125 | ''' 126 | return typ != 'mapStringObject' and hasattr(typ, 'startswith') and \ 127 | typ.startswith('map') 128 | 129 | def make_basic_map_name(mapname): 130 | ''' 131 | Description: Make basic map name 132 | Interface: None 133 | History: 2019-06-17 134 | ''' 135 | basic_map_types = ('string', 'int', 'bool', 'int64') 136 | parts = conv_to_c_style(mapname).split('_') 137 | if len(parts) != 3 or parts[0] != 'map' or \ 138 | (parts[1] not in basic_map_types) or \ 139 | (parts[2] not in basic_map_types): 140 | print('Invalid map name: %s' % mapname) 141 | sys.exit(1) 142 | return "json_map_%s_%s" % (parts[1], parts[2]) 143 | 144 | 145 | def get_top_array_type_name(name, prefix): 146 | ''' 147 | Description: Make top array type to contain subtype and length 148 | Interface: None 149 | History: 2020-10-24 150 | ''' 151 | return "%s_container" % prefix if name is None or name == "" or prefix == name \ 152 | else "%s_%s_container" % (prefix, name) 153 | 154 | def get_name_substr(name, prefix): 155 | ''' 156 | Description: Make array name 157 | Interface: None 158 | History: 2019-06-17 159 | ''' 160 | return "%s_element" % prefix if name is None or name == "" or prefix == name \ 161 | else "%s_%s_element" % (prefix, name) 162 | 163 | def get_prefixed_name(name, prefix): 164 | ''' 165 | Description: Make name 166 | Interface: None 167 | History: 2019-06-17 168 | ''' 169 | if name is None or name == "" or prefix.endswith(name): 170 | return "%s" % prefix 171 | if prefix is None or prefix == "" or prefix == name or name.endswith(prefix): 172 | return "%s" % name 173 | return "%s_%s" % (prefix, name) 174 | 175 | def get_prefixed_pointer(name, typ, prefix): 176 | ''' 177 | Description: Make pointer name 178 | Interface: None 179 | History: 2019-06-17 180 | ''' 181 | if typ != 'array' and typ != 'object' and typ != 'mapStringObject' and \ 182 | not valid_basic_map_name(typ): 183 | return "" 184 | return '%s *' % make_basic_map_name(typ) if valid_basic_map_name(typ) \ 185 | else "%s *" % get_prefixed_name(name, prefix) 186 | 187 | def is_compound_type(typ): 188 | ''' 189 | Description: Check compound object 190 | Interface: None 191 | History: 2019-06-17 192 | ''' 193 | return typ in ('object', 'array', 'mapStringObject') 194 | 195 | def is_numeric_type(typ): 196 | '''Check if typ is a numeric type (int, uint, double, etc.).''' 197 | if (typ.startswith("int") or typ.startswith("uint")) and \ 198 | "Pointer" not in typ: 199 | return True 200 | return typ in ("integer", "UID", "GID", "double") 201 | 202 | def is_numeric_pointer_type(typ): 203 | '''Check if typ is a pointer to a numeric type.''' 204 | if (typ.startswith("int") or typ.startswith("uint")) and "Pointer" in typ: 205 | return True 206 | return False 207 | 208 | def get_pointer_base_type(typ): 209 | '''Extract the base type from a pointer type (e.g., "int64Pointer" -> "int64").''' 210 | index = typ.find("Pointer") 211 | return typ[0:index] if index != -1 else "" 212 | 213 | def obtain_pointer(name, typ, prefix): 214 | ''' 215 | Description: Obtain pointer string 216 | Interface: None 217 | History: 2019-06-17 218 | ''' 219 | ptr = get_prefixed_pointer(name, typ, prefix) 220 | if ptr != "": 221 | return ptr 222 | 223 | return "char *" if typ == "string" else \ 224 | ("%s *" % typ if typ == "ArrayOfStrings" else "") 225 | 226 | class HierarchicalName: 227 | ''' 228 | Description: Store HierarchicalName information 229 | Interface: None 230 | History: 2019-06-17 231 | ''' 232 | 233 | def __init__(self, name, leaf=None): 234 | self.name = name 235 | self.leaf = leaf 236 | 237 | def __repr__(self): 238 | return self.name 239 | 240 | def append(self, leaf): 241 | ''' 242 | Description: append name 243 | Interface: None 244 | History: 2019-06-17 245 | ''' 246 | prefix_name = self.name + '_' if self.name != "" else "" 247 | return HierarchicalName(prefix_name + leaf, leaf) 248 | 249 | 250 | class SchemaNode: 251 | ''' 252 | Description: Store SchemaNode information 253 | Interface: None 254 | History: 2019-06-17 255 | ''' 256 | def __init__(self, name, typ, children, subtyp=None, subtypobj=None, subtypname=None, \ 257 | required=None, nested_array=False): 258 | self.typ = typ 259 | self.children = children 260 | self.subtyp = subtyp 261 | self.subtypobj = subtypobj 262 | self.subtypname = subtypname 263 | self.required = required 264 | self.name = conv_to_c_style(name.name.replace('.', '_')) 265 | self.origname = name.leaf or name.name 266 | self.fixname = conv_to_c_style(self.origname.replace('.', '_')) 267 | self.nested_array = nested_array 268 | 269 | 270 | 271 | def __repr__(self): 272 | if self.subtyp is not None: 273 | return "name:(%s) type:(%s -> %s)" \ 274 | % (self.name, self.typ, self.subtyp) 275 | return "name:(%s) type:(%s)" % (self.name, self.typ) 276 | 277 | 278 | class FilePath: 279 | ''' 280 | Description: Store filepath information 281 | Interface: None 282 | History: 2019-06-17 283 | ''' 284 | def __init__(self, name): 285 | self.name = os.path.realpath(name) 286 | self.dirname = os.path.dirname(self.name) 287 | self.basename = os.path.basename(self.name) 288 | 289 | def __repr__(self): 290 | return "{name:(%s) dirname:(%s) basename:(%s)}" \ 291 | % (self.name, self.dirname, self.basename) 292 | 293 | 294 | class SchemaInfo: 295 | ''' 296 | Description: Store schema information 297 | Interface: None 298 | History: 2019-06-17 299 | ''' 300 | 301 | def __init__(self, name, header, source, prefix, filesdir, refs=None): 302 | self.name = name 303 | self.fileprefix = conv_to_c_style( \ 304 | name.basename.replace('.', '_').replace('-', '_')) 305 | self.header = header 306 | self.source = source 307 | self.prefix = prefix 308 | self.refs = refs 309 | self.filesdir = os.path.realpath(filesdir) 310 | 311 | def __repr__(self): 312 | return "{name:(%s) header:(%s) source:(%s) prefix:(%s)}" \ 313 | % (self.name, self.header, self.source, self.prefix) 314 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | DIST_SUBDIRS = yajl 2 | SUBDIRS = yajl 3 | 4 | AM_CFLAGS = $(WARN_CFLAGS) -I$(top_srcdir)/src -I$(top_builddir)/src 5 | 6 | if HAVE_EMBEDDED_YAJL 7 | AM_CFLAGS += -I$(top_srcdir)/yajl/src/headers 8 | endif HAVE_EMBEDDED_YAJL 9 | 10 | CLEANFILES = $(man_MANS) src/runtime_spec_stamp src/image_spec_stamp src/image_manifest_stamp src/basic-test_stamp 11 | 12 | GITIGNOREFILES = build-aux/ gtk-doc.make config.h.in aclocal.m4 13 | 14 | 15 | if ENABLE_LIBOCISPEC_INSTALL 16 | lib_LTLIBRARIES = libocispec.la 17 | lib_LIBRARIES = libocispec.a 18 | else 19 | noinst_LTLIBRARIES = libocispec.la 20 | noinst_LIBRARIES = libocispec.a 21 | endif 22 | 23 | SOURCE_FILES = \ 24 | src/ocispec/image_spec_schema_config_schema.c \ 25 | src/ocispec/image_spec_schema_content_descriptor.c \ 26 | src/ocispec/image_spec_schema_defs.c \ 27 | src/ocispec/image_spec_schema_defs_descriptor.c \ 28 | src/ocispec/image_spec_schema_image_index_schema.c \ 29 | src/ocispec/image_spec_schema_image_layout_schema.c \ 30 | src/ocispec/image_spec_schema_image_manifest_schema.c \ 31 | src/ocispec/runtime_spec_schema_config_freebsd.c \ 32 | src/ocispec/runtime_spec_schema_config_linux.c \ 33 | src/ocispec/runtime_spec_schema_config_zos.c \ 34 | src/ocispec/runtime_spec_schema_config_schema.c \ 35 | src/ocispec/runtime_spec_schema_config_solaris.c \ 36 | src/ocispec/runtime_spec_schema_config_vm.c \ 37 | src/ocispec/runtime_spec_schema_config_windows.c \ 38 | src/ocispec/runtime_spec_schema_defs.c \ 39 | src/ocispec/runtime_spec_schema_defs_freebsd.c \ 40 | src/ocispec/runtime_spec_schema_defs_linux.c \ 41 | src/ocispec/runtime_spec_schema_defs_zos.c \ 42 | src/ocispec/runtime_spec_schema_defs_vm.c \ 43 | src/ocispec/runtime_spec_schema_defs_windows.c \ 44 | src/ocispec/runtime_spec_schema_state_schema.c \ 45 | src/ocispec/runtime_spec_schema_features_linux.c \ 46 | src/ocispec/runtime_spec_schema_features_schema.c \ 47 | src/ocispec/image_manifest_items_image_manifest_items_schema.c \ 48 | src/ocispec/basic_test_double_array_item.c \ 49 | src/ocispec/basic_test_double_array.c \ 50 | src/ocispec/basic_test_top_array_int.c \ 51 | src/ocispec/basic_test_top_array_string.c \ 52 | src/ocispec/basic_test_top_double_array_int.c \ 53 | src/ocispec/basic_test_top_double_array_obj.c \ 54 | src/ocispec/basic_test_top_double_array_refobj.c \ 55 | src/ocispec/basic_test_top_double_array_string.c 56 | 57 | HEADER_FILES = $(SOURCE_FILES:.c=.h) 58 | 59 | if ENABLE_LIBOCISPEC_INSTALL 60 | ocispec_includedir = $(includedir)/ocispec 61 | ocispec_include_HEADERS = $(HEADER_FILES) \ 62 | src/ocispec/json_common.h \ 63 | src/ocispec/read-file.h 64 | endif 65 | 66 | src/runtime_spec_stamp: src/ocispec/json_common.h src/ocispec/json_common.c 67 | $(PYTHON) $(srcdir)/src/ocispec/generate.py --gen-ref --root=${srcdir} --out=${builddir}/src/ocispec ${srcdir}/runtime-spec/schema 68 | @touch $@ 69 | 70 | src/image_spec_stamp: src/ocispec/json_common.h src/ocispec/json_common.c 71 | $(PYTHON) $(srcdir)/src/ocispec/generate.py --gen-ref --root=${srcdir} --out=${builddir}/src/ocispec ${srcdir}/image-spec/schema 72 | @touch $@ 73 | 74 | src/image_manifest_stamp: src/ocispec/json_common.h src/ocispec/json_common.c 75 | $(PYTHON) $(srcdir)/src/ocispec/generate.py --gen-ref --root=${srcdir}/tests/test-spec --out=${builddir}/src/ocispec ${srcdir}/tests/test-spec/imageManifestItems 76 | @touch $@ 77 | 78 | src/basic-test_stamp: src/ocispec/json_common.h src/ocispec/json_common.c 79 | $(PYTHON) $(srcdir)/src/ocispec/generate.py --gen-ref --root=${srcdir}/tests/test-spec --out=${builddir}/src/ocispec ${srcdir}/tests/test-spec/basic 80 | @touch $@ 81 | 82 | src/ocispec/image_spec_schema_config_schema.c \ 83 | src/ocispec/image_spec_schema_content_descriptor.c \ 84 | src/ocispec/image_spec_schema_defs.c \ 85 | src/ocispec/image_spec_schema_defs_descriptor.c \ 86 | src/ocispec/image_spec_schema_image_index_schema.c \ 87 | src/ocispec/image_spec_schema_image_layout_schema.c \ 88 | src/ocispec/image_spec_schema_image_manifest_schema.c \ 89 | src/ocispec/image_spec_schema_config_schema.h \ 90 | src/ocispec/image_spec_schema_content_descriptor.h \ 91 | src/ocispec/image_spec_schema_defs.h \ 92 | src/ocispec/image_spec_schema_defs_descriptor.h \ 93 | src/ocispec/image_spec_schema_image_index_schema.h \ 94 | src/ocispec/image_spec_schema_image_layout_schema.h \ 95 | src/ocispec/image_spec_schema_image_manifest_schema.h: src/image_spec_stamp 96 | 97 | src/ocispec/runtime_spec_schema_config_linux.h \ 98 | src/ocispec/runtime_spec_schema_config_schema.h \ 99 | src/ocispec/runtime_spec_schema_config_solaris.h \ 100 | src/ocispec/runtime_spec_schema_config_vm.h \ 101 | src/ocispec/runtime_spec_schema_config_windows.h \ 102 | src/ocispec/runtime_spec_schema_defs.h \ 103 | src/ocispec/runtime_spec_schema_defs_linux.h \ 104 | src/ocispec/runtime_spec_schema_defs_zos.h \ 105 | src/ocispec/runtime_spec_schema_features_linux.h \ 106 | src/ocispec/runtime_spec_schema_features_schema.h \ 107 | src/ocispec/runtime_spec_schema_features_linux.c \ 108 | src/ocispec/runtime_spec_schema_features_schema.c \ 109 | src/ocispec/runtime_spec_schema_defs_vm.h \ 110 | src/ocispec/runtime_spec_schema_defs_windows.h \ 111 | src/ocispec/runtime_spec_schema_state_schema.h \ 112 | src/ocispec/runtime_spec_schema_config_linux.c \ 113 | src/ocispec/runtime_spec_schema_config_zos.c \ 114 | src/ocispec/runtime_spec_schema_config_schema.c \ 115 | src/ocispec/runtime_spec_schema_config_solaris.c \ 116 | src/ocispec/runtime_spec_schema_config_vm.c \ 117 | src/ocispec/runtime_spec_schema_config_windows.c \ 118 | src/ocispec/runtime_spec_schema_defs.c \ 119 | src/ocispec/runtime_spec_schema_defs_linux.c \ 120 | src/ocispec/runtime_spec_schema_defs_vm.c \ 121 | src/ocispec/runtime_spec_schema_defs_zos.c \ 122 | src/ocispec/runtime_spec_schema_defs_windows.c \ 123 | src/ocispec/runtime_spec_schema_config_freebsd.h \ 124 | src/ocispec/runtime_spec_schema_defs_freebsd.h \ 125 | src/ocispec/runtime_spec_schema_config_freebsd.c \ 126 | src/ocispec/runtime_spec_schema_defs_freebsd.c \ 127 | src/ocispec/runtime_spec_schema_state_schema.c: src/runtime_spec_stamp 128 | 129 | src/ocispec/image_manifest_items_image_manifest_items_schema.h \ 130 | src/ocispec/image_manifest_items_image_manifest_items_schema.c: src/image_manifest_stamp 131 | 132 | src/ocispec/basic_test_double_array_item.h \ 133 | src/ocispec/basic_test_double_array.h \ 134 | src/ocispec/basic_test_top_array_int.h \ 135 | src/ocispec/basic_test_top_array_string.h \ 136 | src/ocispec/basic_test_top_double_array_int.h \ 137 | src/ocispec/basic_test_top_double_array_obj.h \ 138 | src/ocispec/basic_test_top_double_array_refobj.h \ 139 | src/ocispec/basic_test_top_double_array_string.h \ 140 | src/ocispec/basic_test_double_array_item.c \ 141 | src/ocispec/basic_test_double_array.c \ 142 | src/ocispec/basic_test_top_array_int.c \ 143 | src/ocispec/basic_test_top_array_string.c \ 144 | src/ocispec/basic_test_top_double_array_int.c \ 145 | src/ocispec/basic_test_top_double_array_obj.c \ 146 | src/ocispec/basic_test_top_double_array_refobj.c \ 147 | src/ocispec/basic_test_top_double_array_string.c: src/basic-test_stamp 148 | 149 | $(HEADER_FILES): %.h: %.c src/ocispec/generate.py 150 | 151 | BUILT_SOURCES = $(HEADER_FILES) $(SOURCE_FILES) 152 | 153 | libocispec_la_SOURCES = $(BUILT_SOURCES) \ 154 | src/ocispec/read-file.c \ 155 | src/ocispec/json_common.c 156 | 157 | TMP_H_FILES = $(HEADER_FILES:.h=.h.tmp) 158 | TMP_C_FILES = $(SOURCE_FILES:.c=.c.tmp) 159 | 160 | CLEANFILES += $(HEADER_FILES) $(SOURCE_FILES) $(TMP_H_FILES) $(TMP_C_FILES) 161 | 162 | TESTS_LDADD = libocispec.la $(SELINUX_LIBS) 163 | 164 | if HAVE_EMBEDDED_YAJL 165 | TESTS_LDADD += yajl/libyajl.la 166 | else 167 | TESTS_LDADD += $(YAJL_LIBS) 168 | endif 169 | 170 | libocispec_a_SOURCES = 171 | 172 | libocispec.a: libocispec.la $(BUILT_SOURCES) src/runtime_spec_stamp src/image_spec_stamp src/image_manifest_stamp src/basic-test_stamp 173 | $(LIBTOOL) --mode=link $(GCC) libocispec.la -o libocispec.a 174 | 175 | if ENABLE_LIBOCISPEC_INSTALL 176 | pkgconfigdir = $(libdir)/pkgconfig 177 | pkgconfig_DATA = ocispec.pc 178 | endif 179 | 180 | tests_test_1_SOURCES = tests/test-1.c 181 | tests_test_1_LDADD = $(TESTS_LDADD) 182 | 183 | tests_test_2_SOURCES = tests/test-2.c 184 | tests_test_2_LDADD = $(TESTS_LDADD) 185 | 186 | tests_test_3_SOURCES = tests/test-3.c 187 | tests_test_3_LDADD = $(TESTS_LDADD) 188 | 189 | tests_test_4_SOURCES = tests/test-4.c 190 | tests_test_4_LDADD = $(TESTS_LDADD) 191 | 192 | tests_test_5_SOURCES = tests/test-5.c 193 | tests_test_5_LDADD = $(TESTS_LDADD) 194 | 195 | tests_test_6_SOURCES = tests/test-6.c 196 | tests_test_6_LDADD = $(TESTS_LDADD) 197 | 198 | tests_test_7_SOURCES = tests/test-7.c 199 | tests_test_7_LDADD = $(TESTS_LDADD) 200 | 201 | tests_test_8_SOURCES = tests/test-8.c 202 | tests_test_8_LDADD = $(TESTS_LDADD) 203 | 204 | tests_test_9_SOURCES = tests/test-9.c 205 | tests_test_9_LDADD = $(TESTS_LDADD) 206 | 207 | tests_test_10_SOURCES = tests/test-10.c 208 | tests_test_10_LDADD = $(TESTS_LDADD) 209 | 210 | tests_test_11_SOURCES = tests/test-11.c 211 | tests_test_11_LDADD = $(TESTS_LDADD) 212 | 213 | tests_test_12_SOURCES = tests/test-12.c 214 | tests_test_12_LDADD = $(TESTS_LDADD) 215 | 216 | tests_test_13_SOURCES = tests/test-13.c 217 | tests_test_13_LDADD = $(TESTS_LDADD) 218 | 219 | src_ocispec_validate_SOURCES = src/ocispec/validate.c 220 | src_ocispec_validate_LDADD = $(TESTS_LDADD) 221 | 222 | TESTS = tests/test-1 \ 223 | tests/test-2 \ 224 | tests/test-3 \ 225 | tests/test-4 \ 226 | tests/test-5 \ 227 | tests/test-6 \ 228 | tests/test-7 \ 229 | tests/test-8 \ 230 | tests/test-9 \ 231 | tests/test-10 \ 232 | tests/test-11 \ 233 | tests/test-12 \ 234 | tests/test-13 235 | 236 | noinst_PROGRAMS = src/ocispec/validate $(TESTS) 237 | 238 | $(abs_top_builddir)/tests/data: $(abs_top_srcdir)/tests/data 239 | if test $(abs_top_srcdir) != $(abs_top_builddir) && test ! -d $@; then rm -f $@; ln -s $< $@; fi 240 | 241 | distcheck check: $(abs_top_builddir)/tests/data 242 | 243 | -include $(top_srcdir)/git.mk 244 | 245 | EXTRA_DIST = autogen.sh \ 246 | tests/data/image_index_config.json \ 247 | tests/data/image_layout_config.json \ 248 | tests/data/image_config_mapstringobject.json \ 249 | tests/data/config.json \ 250 | tests/data/image_manifest.json \ 251 | tests/data/image_config.json \ 252 | tests/data/config.nocwd.json \ 253 | tests/data/image_manifest_item.json \ 254 | tests/data/residual_image_layout_config.json \ 255 | tests/data/null_value_config.json \ 256 | tests/data/doublearray.json \ 257 | tests/data/top_array_int.json \ 258 | tests/data/top_array_string.json \ 259 | tests/data/top_double_array_int.json \ 260 | tests/data/top_double_array_obj.json \ 261 | tests/data/top_double_array_refobj.json \ 262 | tests/data/top_double_array_string.json \ 263 | tests/test-spec \ 264 | src/ocispec/generate.py \ 265 | src/ocispec/headers.py \ 266 | src/ocispec/helpers.py \ 267 | src/ocispec/sources.py \ 268 | $(HEADER_FILES) \ 269 | src/ocispec/read-file.h \ 270 | runtime-spec \ 271 | image-spec \ 272 | src/ocispec/json_common.h \ 273 | src/ocispec/json_common.c \ 274 | src/yajl 275 | 276 | sync: 277 | (cd image-spec; git pull https://github.com/opencontainers/image-spec) 278 | (cd runtime-spec; git pull https://github.com/opencontainers/runtime-spec) 279 | (cd yajl; git pull https://github.com/containers/yajl main) 280 | 281 | generate: src/runtime_spec_stamp src/image_spec_stamp src/image_manifest_stamp src/basic-test_stamp 282 | 283 | #Needed by phony: generate-rust 284 | install-node-deps: 285 | (npm install @apidevtools/json-schema-ref-parser) 286 | (npm install quicktype-core) 287 | 288 | generate-rust: 289 | (node rust-gen.js) 290 | -------------------------------------------------------------------------------- /src/ocispec/headers.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # libocispec - a C library for parsing OCI spec files. 4 | # 5 | # Copyright (C) Huawei Technologies., Ltd. 2018-2020. 6 | # Copyright (C) 2017, 2019 Giuseppe Scrivano 7 | # libocispec is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # libocispec is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with libocispec. If not, see . 19 | # 20 | # As a special exception, you may create a larger work that contains 21 | # part or all of the libocispec parser skeleton and distribute that work 22 | # under terms of your choice, so long as that work isn't itself a 23 | # parser generator using the skeleton or a modified version thereof 24 | # as a parser skeleton. Alternatively, if you modify or redistribute 25 | # the parser skeleton itself, you may (at your option) remove this 26 | # special exception, which will cause the skeleton and the resulting 27 | # libocispec output files to be licensed under the GNU General Public 28 | # License without this special exception. 29 | import helpers 30 | 31 | def append_header_arr(obj, header, prefix): 32 | ''' 33 | Description: Write c header file of array 34 | Interface: None 35 | History: 2019-06-17 36 | ''' 37 | if not obj.subtypobj or obj.subtypname: 38 | return 39 | 40 | header.append("typedef struct {\n") 41 | for i in obj.subtypobj: 42 | if i.typ == 'array': 43 | c_typ = helpers.get_prefixed_pointer(i.name, i.subtyp, prefix) or \ 44 | helpers.get_map_c_types(i.subtyp) 45 | if i.subtypobj is not None: 46 | c_typ = helpers.get_name_substr(i.name, prefix) 47 | 48 | if not helpers.is_compound_type(i.subtyp): 49 | header.append(f" {c_typ}{' ' if '*' not in c_typ else ''}*{i.fixname};\n") 50 | else: 51 | header.append(f" {c_typ} **{i.fixname};\n") 52 | header.append(f" size_t {i.fixname + '_len'};\n\n") 53 | else: 54 | c_typ = helpers.get_prefixed_pointer(i.name, i.typ, prefix) or \ 55 | helpers.get_map_c_types(i.typ) 56 | header.append(f" {c_typ}{' ' if '*' not in c_typ else ''}{i.fixname};\n") 57 | for i in obj.subtypobj: 58 | if helpers.is_numeric_type(i.typ) or i.typ == 'boolean': 59 | header.append(f" unsigned int {i.fixname}_present : 1;\n") 60 | typename = helpers.get_name_substr(obj.name, prefix) 61 | header.append(f"}}\n{typename};\n\n") 62 | header.append(f"void free_{typename} ({typename} *ptr);\n\n") 63 | header.append(f"{typename} *make_{typename} (yajl_val tree, const struct parser_context *ctx, parser_error *err);\n\n") 64 | 65 | 66 | def append_header_map_str_obj(obj, header, prefix): 67 | ''' 68 | Description: Write c header file of mapStringObject 69 | Interface: None 70 | History: 2019-06-17 71 | ''' 72 | child = obj.children[0] 73 | header.append("typedef struct {\n") 74 | header.append(" char **keys;\n") 75 | if helpers.valid_basic_map_name(child.typ): 76 | c_typ = helpers.get_prefixed_pointer("", child.typ, "") 77 | elif child.subtypname: 78 | c_typ = child.subtypname + " *" 79 | else: 80 | c_typ = helpers.get_prefixed_pointer(child.name, child.typ, prefix) 81 | header.append(f" {c_typ}{' ' if '*' not in c_typ else ''}*{child.fixname};\n") 82 | header.append(" size_t len;\n") 83 | 84 | 85 | def append_header_child_arr(child, header, prefix): 86 | ''' 87 | Description: Write c header file of array of child 88 | Interface: None 89 | History: 2019-06-17 90 | ''' 91 | if helpers.get_map_c_types(child.subtyp) != "": 92 | c_typ = helpers.get_map_c_types(child.subtyp) 93 | elif helpers.valid_basic_map_name(child.subtyp): 94 | c_typ = f'{helpers.make_basic_map_name(child.subtyp)} *' 95 | elif child.subtypname is not None: 96 | c_typ = child.subtypname 97 | elif child.subtypobj is not None: 98 | c_typ = helpers.get_name_substr(child.name, prefix) 99 | else: 100 | c_typ = helpers.get_prefixed_pointer(child.name, child.subtyp, prefix) 101 | 102 | dflag = "" 103 | if child.nested_array: 104 | dflag = "*" 105 | 106 | if helpers.valid_basic_map_name(child.subtyp): 107 | header.append(f" {helpers.make_basic_map_name(child.subtyp)} **{child.fixname};\n") 108 | elif not helpers.is_compound_type(child.subtyp): 109 | header.append(f" {c_typ}{' ' if '*' not in c_typ else ''}*{dflag}{child.fixname};\n") 110 | else: 111 | header.append(f" {c_typ}{' ' if '*' not in c_typ else ''}**{dflag}{child.fixname};\n") 112 | 113 | if child.nested_array and not helpers.valid_basic_map_name(child.subtyp): 114 | header.append(f" size_t *{child.fixname + '_item_lens'};\n") 115 | 116 | header.append(f" size_t {child.fixname + '_len'};\n\n") 117 | 118 | def append_header_child_others(child, header, prefix): 119 | ''' 120 | Description: Write c header file of others of child 121 | Interface: None 122 | History: 2019-06-17 123 | ''' 124 | if helpers.get_map_c_types(child.typ) != "": 125 | c_typ = helpers.get_map_c_types(child.typ) 126 | elif helpers.valid_basic_map_name(child.typ): 127 | c_typ = f'{helpers.make_basic_map_name(child.typ)} *' 128 | elif child.subtypname: 129 | c_typ = helpers.get_prefixed_pointer(child.subtypname, child.typ, "") 130 | else: 131 | c_typ = helpers.get_prefixed_pointer(child.name, child.typ, prefix) 132 | header.append(f" {c_typ}{' ' if '*' not in c_typ else ''}{child.fixname};\n\n") 133 | 134 | 135 | def append_type_c_header(obj, header, prefix): 136 | ''' 137 | Description: Write c header file 138 | Interface: None 139 | History: 2019-06-17 140 | ''' 141 | if not helpers.is_compound_type(obj.typ): 142 | return 143 | 144 | if obj.typ == 'array': 145 | append_header_arr(obj, header, prefix) 146 | return 147 | 148 | if obj.typ == 'mapStringObject': 149 | if obj.subtypname is not None: 150 | return 151 | append_header_map_str_obj(obj, header, prefix) 152 | elif obj.typ == 'object': 153 | if obj.subtypname is not None: 154 | return 155 | header.append("typedef struct {\n") 156 | if obj.children is None: 157 | header.append(" char unuseful; // unuseful definition to avoid empty struct\n") 158 | present_tags = [] 159 | for i in obj.children or []: 160 | if helpers.is_numeric_type(i.typ) or i.typ == 'boolean': 161 | present_tags.append(f" unsigned int {i.fixname}_present : 1;\n") 162 | if i.typ == 'array': 163 | append_header_child_arr(i, header, prefix) 164 | else: 165 | append_header_child_others(i, header, prefix) 166 | if obj.children is not None: 167 | header.append(" yajl_val _residual;\n") 168 | if len(present_tags) > 0: 169 | header.append("\n") 170 | for tag in present_tags: 171 | header.append(tag) 172 | typename = helpers.get_prefixed_name(obj.name, prefix) 173 | header.append(f"}}\n{typename};\n\n") 174 | header.append(f"void free_{typename} ({typename} *ptr);\n\n") 175 | header.append(f"{typename} *clone_{typename} ({typename} *src);\n") 176 | header.append(f"{typename} *make_{typename} (yajl_val tree, const struct parser_context *ctx, parser_error *err);\n\n") 177 | header.append(f"yajl_gen_status gen_{typename} (yajl_gen g, const {typename} *ptr, const struct parser_context *ctx, parser_error *err);\n\n") 178 | 179 | def header_reflect_top_array(obj, prefix, header): 180 | c_typ = helpers.get_prefixed_pointer(obj.name, obj.subtyp, prefix) or \ 181 | helpers.get_map_c_types(obj.subtyp) 182 | if obj.subtypobj is not None: 183 | if obj.nested_array and obj.subtypname is not None: 184 | c_typ = obj.subtypname + " *" 185 | else: 186 | c_typ = helpers.get_name_substr(obj.name, prefix) + " *" 187 | if c_typ == "": 188 | return 189 | 190 | typename = helpers.get_top_array_type_name(obj.name, prefix) 191 | header.append("typedef struct {\n") 192 | if obj.nested_array: 193 | header.append(f" {c_typ}{' ' if '*' not in c_typ else ''}**items;\n") 194 | header.append(" size_t *subitem_lens;\n\n") 195 | else: 196 | header.append(f" {c_typ}{' ' if '*' not in c_typ else ''}*items;\n") 197 | header.append(" size_t len;\n\n") 198 | header.append(f"}}\n{typename};\n\n") 199 | 200 | 201 | header.append(f"void free_{typename} ({typename} *ptr);\n\n") 202 | header.append(f"{typename} *{typename}_parse_file(const char *filename, const struct "\ 203 | "parser_context *ctx, parser_error *err);\n\n") 204 | header.append(f"{typename} *{typename}_parse_file_stream(FILE *stream, const struct "\ 205 | "parser_context *ctx, parser_error *err);\n\n") 206 | header.append(f"{typename} *{typename}_parse_data(const char *jsondata, const struct "\ 207 | "parser_context *ctx, parser_error *err);\n\n") 208 | header.append(f"char *{typename}_generate_json(const {typename} *ptr, "\ 209 | "const struct parser_context *ctx, parser_error *err);\n\n") 210 | 211 | def header_reflect(structs, schema_info, header): 212 | ''' 213 | Description: Reflection header files 214 | Interface: None 215 | History: 2019-06-17 216 | ''' 217 | prefix = schema_info.prefix 218 | header.append(f"// Generated from {schema_info.name.basename}. Do not edit!\n") 219 | header.append(f"#ifndef {prefix.upper()}_SCHEMA_H\n") 220 | header.append(f"#define {prefix.upper()}_SCHEMA_H\n\n") 221 | header.append("#include \n") 222 | header.append("#include \n") 223 | header.append("#include \"ocispec/json_common.h\"\n") 224 | if schema_info.refs: 225 | for ref in schema_info.refs.keys(): 226 | header.append(f"#include \"ocispec/{ref}\"\n") 227 | header.append("\n#ifdef __cplusplus\n") 228 | header.append("extern \"C\" {\n") 229 | header.append("#endif\n\n") 230 | 231 | for i in structs: 232 | append_type_c_header(i, header, prefix) 233 | length = len(structs) 234 | toptype = structs[length - 1].typ if length != 0 else "" 235 | if toptype == 'object': 236 | header.append(f"{prefix} *{prefix}_parse_file (const char *filename, const struct parser_context *ctx, "\ 237 | "parser_error *err);\n\n") 238 | header.append(f"{prefix} *{prefix}_parse_file_stream (FILE *stream, const struct parser_context *ctx, "\ 239 | "parser_error *err);\n\n") 240 | header.append(f"{prefix} *{prefix}_parse_data (const char *jsondata, const struct parser_context *ctx, "\ 241 | "parser_error *err);\n\n") 242 | header.append(f"char *{prefix}_generate_json (const {prefix} *ptr, const struct parser_context *ctx, "\ 243 | "parser_error *err);\n\n") 244 | elif toptype == 'array': 245 | header_reflect_top_array(structs[length - 1], prefix, header) 246 | 247 | header.append("#ifdef __cplusplus\n") 248 | header.append("}\n") 249 | header.append("#endif\n\n") 250 | header.append("#endif\n\n") 251 | -------------------------------------------------------------------------------- /tests/test-13.c: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2025 Giuseppe Scrivano 2 | 3 | libocispec is free software; you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation; either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | libocispec is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with libocispec. If not, see . 15 | 16 | */ 17 | 18 | /* Test error handling and edge cases */ 19 | 20 | #include "config.h" 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "ocispec/runtime_spec_schema_config_schema.h" 26 | #include "ocispec/image_spec_schema_config_schema.h" 27 | 28 | static int 29 | test_parse_invalid_json (void) 30 | { 31 | parser_error err = NULL; 32 | runtime_spec_schema_config_schema *config = NULL; 33 | int ret = 0; 34 | 35 | /* Parse invalid JSON */ 36 | config = runtime_spec_schema_config_schema_parse_data ("{invalid json", 0, &err); 37 | if (config != NULL) 38 | { 39 | printf ("expected parse to fail for invalid JSON\n"); 40 | free_runtime_spec_schema_config_schema (config); 41 | ret = 1; 42 | goto out; 43 | } 44 | 45 | /* Error should be set */ 46 | if (err == NULL) 47 | { 48 | printf ("expected error to be set\n"); 49 | ret = 2; 50 | goto out; 51 | } 52 | 53 | printf ("parse invalid json test passed (error: %s)\n", err); 54 | 55 | out: 56 | free (err); 57 | return ret; 58 | } 59 | 60 | static int 61 | test_parse_minimal_json (void) 62 | { 63 | parser_error err = NULL; 64 | runtime_spec_schema_config_schema *config = NULL; 65 | char *json_buf = NULL; 66 | int ret = 0; 67 | /* Minimal valid config - ociVersion is required */ 68 | const char *minimal_json = "{\"ociVersion\": \"1.0.0\"}"; 69 | 70 | config = runtime_spec_schema_config_schema_parse_data (minimal_json, 0, &err); 71 | if (config == NULL) 72 | { 73 | printf ("parse minimal object failed: %s\n", err); 74 | ret = 1; 75 | goto out; 76 | } 77 | 78 | if (config->oci_version == NULL || strcmp (config->oci_version, "1.0.0") != 0) 79 | { 80 | printf ("ociVersion mismatch\n"); 81 | ret = 2; 82 | goto out; 83 | } 84 | 85 | /* Generate JSON from minimal object */ 86 | json_buf = runtime_spec_schema_config_schema_generate_json (config, 0, &err); 87 | if (json_buf == NULL) 88 | { 89 | printf ("generate from minimal object failed: %s\n", err); 90 | ret = 3; 91 | goto out; 92 | } 93 | 94 | printf ("parse minimal json test passed\n"); 95 | 96 | out: 97 | free (err); 98 | free (json_buf); 99 | free_runtime_spec_schema_config_schema (config); 100 | return ret; 101 | } 102 | 103 | static int 104 | test_parse_nonexistent_file (void) 105 | { 106 | parser_error err = NULL; 107 | runtime_spec_schema_config_schema *config = NULL; 108 | int ret = 0; 109 | 110 | config = runtime_spec_schema_config_schema_parse_file ("/nonexistent/path/config.json", 0, &err); 111 | if (config != NULL) 112 | { 113 | printf ("expected parse to fail for nonexistent file\n"); 114 | free_runtime_spec_schema_config_schema (config); 115 | ret = 1; 116 | goto out; 117 | } 118 | 119 | if (err == NULL) 120 | { 121 | printf ("expected error to be set\n"); 122 | ret = 2; 123 | goto out; 124 | } 125 | 126 | printf ("parse nonexistent file test passed (error: %s)\n", err); 127 | 128 | out: 129 | free (err); 130 | return ret; 131 | } 132 | 133 | static int 134 | test_null_input (void) 135 | { 136 | parser_error err = NULL; 137 | runtime_spec_schema_config_schema *config = NULL; 138 | int ret = 0; 139 | 140 | /* Parse NULL data should fail gracefully */ 141 | config = runtime_spec_schema_config_schema_parse_data (NULL, 0, &err); 142 | if (config != NULL) 143 | { 144 | printf ("expected parse to fail for NULL input\n"); 145 | free_runtime_spec_schema_config_schema (config); 146 | ret = 1; 147 | goto out; 148 | } 149 | 150 | printf ("null input test passed\n"); 151 | 152 | out: 153 | free (err); 154 | return ret; 155 | } 156 | 157 | static int 158 | test_empty_arrays (void) 159 | { 160 | parser_error err = NULL; 161 | runtime_spec_schema_config_schema *config = NULL; 162 | char *json_buf = NULL; 163 | int ret = 0; 164 | const char *json_with_empty_arrays = "{" 165 | "\"ociVersion\": \"1.0.0\"," 166 | "\"process\": {" 167 | " \"args\": []," 168 | " \"env\": []," 169 | " \"cwd\": \"/\"" 170 | "}," 171 | "\"root\": {\"path\": \"rootfs\"}" 172 | "}"; 173 | 174 | config = runtime_spec_schema_config_schema_parse_data (json_with_empty_arrays, 0, &err); 175 | if (config == NULL) 176 | { 177 | printf ("parse with empty arrays failed: %s\n", err); 178 | ret = 1; 179 | goto out; 180 | } 181 | 182 | /* Verify empty arrays are handled correctly */ 183 | if (config->process->args_len != 0) 184 | { 185 | printf ("expected args_len to be 0, got %zu\n", config->process->args_len); 186 | ret = 2; 187 | goto out; 188 | } 189 | 190 | if (config->process->env_len != 0) 191 | { 192 | printf ("expected env_len to be 0, got %zu\n", config->process->env_len); 193 | ret = 3; 194 | goto out; 195 | } 196 | 197 | /* Generate JSON should work */ 198 | json_buf = runtime_spec_schema_config_schema_generate_json (config, 0, &err); 199 | if (json_buf == NULL) 200 | { 201 | printf ("generate with empty arrays failed: %s\n", err); 202 | ret = 4; 203 | goto out; 204 | } 205 | 206 | printf ("empty arrays test passed\n"); 207 | 208 | out: 209 | free (err); 210 | free (json_buf); 211 | free_runtime_spec_schema_config_schema (config); 212 | return ret; 213 | } 214 | 215 | static int 216 | test_string_with_special_chars (void) 217 | { 218 | parser_error err = NULL; 219 | runtime_spec_schema_config_schema *config = NULL; 220 | runtime_spec_schema_config_schema *reparsed = NULL; 221 | char *json_buf = NULL; 222 | int ret = 0; 223 | const char *json_with_special = "{" 224 | "\"ociVersion\": \"1.0.0\"," 225 | "\"hostname\": \"host\\nwith\\nnewlines\"," 226 | "\"process\": {" 227 | " \"args\": [\"echo\", \"hello\\tworld\"]," 228 | " \"cwd\": \"/path/with spaces/and\\\"quotes\\\"\"" 229 | "}," 230 | "\"root\": {\"path\": \"rootfs\"}" 231 | "}"; 232 | 233 | config = runtime_spec_schema_config_schema_parse_data (json_with_special, 0, &err); 234 | if (config == NULL) 235 | { 236 | printf ("parse with special chars failed: %s\n", err); 237 | ret = 1; 238 | goto out; 239 | } 240 | 241 | /* Verify special characters are preserved */ 242 | if (strstr (config->hostname, "\n") == NULL) 243 | { 244 | printf ("newline not preserved in hostname\n"); 245 | ret = 2; 246 | goto out; 247 | } 248 | 249 | /* Generate and reparse to verify round-trip */ 250 | json_buf = runtime_spec_schema_config_schema_generate_json (config, 0, &err); 251 | if (json_buf == NULL) 252 | { 253 | printf ("generate with special chars failed: %s\n", err); 254 | ret = 3; 255 | goto out; 256 | } 257 | 258 | reparsed = runtime_spec_schema_config_schema_parse_data (json_buf, 0, &err); 259 | if (reparsed == NULL) 260 | { 261 | printf ("reparse with special chars failed: %s\n", err); 262 | ret = 4; 263 | goto out; 264 | } 265 | 266 | if (strcmp (config->hostname, reparsed->hostname) != 0) 267 | { 268 | printf ("hostname mismatch after round-trip\n"); 269 | ret = 5; 270 | goto out; 271 | } 272 | 273 | printf ("string with special chars test passed\n"); 274 | 275 | out: 276 | free (err); 277 | free (json_buf); 278 | free_runtime_spec_schema_config_schema (config); 279 | free_runtime_spec_schema_config_schema (reparsed); 280 | return ret; 281 | } 282 | 283 | static int 284 | test_large_numbers (void) 285 | { 286 | parser_error err = NULL; 287 | runtime_spec_schema_config_schema *config = NULL; 288 | runtime_spec_schema_config_schema *reparsed = NULL; 289 | char *json_buf = NULL; 290 | int ret = 0; 291 | const char *json_with_large_nums = "{" 292 | "\"ociVersion\": \"1.0.0\"," 293 | "\"process\": {" 294 | " \"args\": [\"test\"]," 295 | " \"cwd\": \"/\"," 296 | " \"user\": {\"uid\": 4294967295, \"gid\": 4294967295}" 297 | "}," 298 | "\"root\": {\"path\": \"rootfs\"}" 299 | "}"; 300 | 301 | config = runtime_spec_schema_config_schema_parse_data (json_with_large_nums, 0, &err); 302 | if (config == NULL) 303 | { 304 | printf ("parse with large numbers failed: %s\n", err); 305 | ret = 1; 306 | goto out; 307 | } 308 | 309 | /* Generate and reparse */ 310 | json_buf = runtime_spec_schema_config_schema_generate_json (config, 0, &err); 311 | if (json_buf == NULL) 312 | { 313 | printf ("generate with large numbers failed: %s\n", err); 314 | ret = 2; 315 | goto out; 316 | } 317 | 318 | reparsed = runtime_spec_schema_config_schema_parse_data (json_buf, 0, &err); 319 | if (reparsed == NULL) 320 | { 321 | printf ("reparse with large numbers failed: %s\n", err); 322 | ret = 3; 323 | goto out; 324 | } 325 | 326 | if (config->process->user->uid != reparsed->process->user->uid) 327 | { 328 | printf ("uid mismatch after round-trip\n"); 329 | ret = 4; 330 | goto out; 331 | } 332 | 333 | printf ("large numbers test passed\n"); 334 | 335 | out: 336 | free (err); 337 | free (json_buf); 338 | free_runtime_spec_schema_config_schema (config); 339 | free_runtime_spec_schema_config_schema (reparsed); 340 | return ret; 341 | } 342 | 343 | static int 344 | test_clone_null (void) 345 | { 346 | runtime_spec_schema_config_schema *cloned = NULL; 347 | int ret = 0; 348 | 349 | /* Clone NULL should return NULL */ 350 | cloned = clone_runtime_spec_schema_config_schema (NULL); 351 | if (cloned != NULL) 352 | { 353 | printf ("expected clone of NULL to return NULL\n"); 354 | free_runtime_spec_schema_config_schema (cloned); 355 | ret = 1; 356 | goto out; 357 | } 358 | 359 | printf ("clone null test passed\n"); 360 | 361 | out: 362 | return ret; 363 | } 364 | 365 | static int 366 | test_free_null (void) 367 | { 368 | /* Free NULL should not crash */ 369 | free_runtime_spec_schema_config_schema (NULL); 370 | free_image_spec_schema_config_schema (NULL); 371 | 372 | printf ("free null test passed\n"); 373 | return 0; 374 | } 375 | 376 | int 377 | main (void) 378 | { 379 | int ret; 380 | 381 | ret = test_parse_invalid_json (); 382 | if (ret != 0) 383 | { 384 | printf ("test_parse_invalid_json failed: %d\n", ret); 385 | return ret; 386 | } 387 | 388 | ret = test_parse_minimal_json (); 389 | if (ret != 0) 390 | { 391 | printf ("test_parse_minimal_json failed: %d\n", ret); 392 | return ret; 393 | } 394 | 395 | ret = test_parse_nonexistent_file (); 396 | if (ret != 0) 397 | { 398 | printf ("test_parse_nonexistent_file failed: %d\n", ret); 399 | return ret; 400 | } 401 | 402 | ret = test_null_input (); 403 | if (ret != 0) 404 | { 405 | printf ("test_null_input failed: %d\n", ret); 406 | return ret; 407 | } 408 | 409 | ret = test_empty_arrays (); 410 | if (ret != 0) 411 | { 412 | printf ("test_empty_arrays failed: %d\n", ret); 413 | return ret; 414 | } 415 | 416 | ret = test_string_with_special_chars (); 417 | if (ret != 0) 418 | { 419 | printf ("test_string_with_special_chars failed: %d\n", ret); 420 | return ret; 421 | } 422 | 423 | ret = test_large_numbers (); 424 | if (ret != 0) 425 | { 426 | printf ("test_large_numbers failed: %d\n", ret); 427 | return ret; 428 | } 429 | 430 | ret = test_clone_null (); 431 | if (ret != 0) 432 | { 433 | printf ("test_clone_null failed: %d\n", ret); 434 | return ret; 435 | } 436 | 437 | ret = test_free_null (); 438 | if (ret != 0) 439 | { 440 | printf ("test_free_null failed: %d\n", ret); 441 | return ret; 442 | } 443 | 444 | return 0; 445 | } 446 | -------------------------------------------------------------------------------- /tests/test-10.c: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2020 duguhaotian 2 | 3 | libocispec is free software; you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation; either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | libocispec is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with libocispec. If not, see . 15 | 16 | */ 17 | 18 | #include "config.h" 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "ocispec/basic_test_double_array.h" 24 | #include "ocispec/basic_test_top_array_int.h" 25 | #include "ocispec/basic_test_top_array_string.h" 26 | #include "ocispec/basic_test_top_double_array_int.h" 27 | #include "ocispec/basic_test_top_double_array_obj.h" 28 | #include "ocispec/basic_test_top_double_array_string.h" 29 | #include "ocispec/basic_test_top_double_array_refobj.h" 30 | 31 | int 32 | do_test_object_double_array() 33 | { 34 | parser_error err = NULL; 35 | struct parser_context ctx = { 0 }; 36 | int ret = 0; 37 | 38 | basic_test_double_array *test_data = basic_test_double_array_parse_file( 39 | "tests/data/doublearray.json", &ctx, &err); 40 | char *json_buf = NULL; 41 | size_t i, j; 42 | 43 | if (test_data == NULL) { 44 | printf ("error %s\n", err); 45 | free(err); 46 | return 1; 47 | } 48 | 49 | // check double array of string 50 | if (test_data->strarrays_len != 3) { 51 | printf("invalid strarrays len\n"); 52 | ret = 1; 53 | goto out; 54 | } 55 | char *expect_strs[3][4] = { 56 | {"stra", "strb", "strc", NULL}, 57 | {"str2a", "str2b", NULL}, 58 | {"str3a", NULL}, 59 | }; 60 | size_t str_lens[3] = {3, 2, 1}; 61 | 62 | for (i = 0; i < 3; i++) { 63 | if (test_data->strarrays[i] == NULL || test_data->strarrays_item_lens == NULL) { 64 | printf("item %zu is null\n", i); 65 | ret = 1; 66 | goto out; 67 | } 68 | if (test_data->strarrays_item_lens[i] != str_lens[i]) { 69 | printf("double array str item %zu is expect len: %zu, get: %zu\n", i, str_lens[i], test_data->strarrays_item_lens[i]); 70 | ret = 1; 71 | goto out; 72 | } 73 | for (j = 0; j < 4 && expect_strs[i][j] != NULL; j++) { 74 | if (test_data->strarrays[i][j] == NULL || strcmp(test_data->strarrays[i][j], expect_strs[i][j]) != 0) { 75 | printf("item %zu: expect: %s, get: %s\n", i, expect_strs[i][j], test_data->strarrays[i][j]); 76 | ret = 1; 77 | goto out; 78 | } 79 | } 80 | } 81 | // check double array of int32 82 | if (test_data->intarrays_len != 3) { 83 | printf("invalid intarrays len\n"); 84 | ret = 1; 85 | goto out; 86 | } 87 | int32_t expect_ints[3][4] = { 88 | {1}, 89 | {1, 2}, 90 | {1, 2, 3}, 91 | }; 92 | size_t int_lens[3] = {1, 2, 3}; 93 | for (i = 0; i < 3; i++) { 94 | if (test_data->intarrays[i] == NULL || test_data->intarrays_item_lens == NULL) { 95 | printf("item %zu is null\n", i); 96 | ret = 1; 97 | goto out; 98 | } 99 | if (test_data->intarrays_item_lens[i] != int_lens[i]) { 100 | printf("double array int item %zu is expect len: %zu, get: %zu\n", i, int_lens[i], test_data->intarrays_item_lens[i]); 101 | ret = 1; 102 | goto out; 103 | } 104 | for (j = 0; j < 4 && expect_ints[i][j] != 0; j++) { 105 | if (test_data->intarrays[i][j] != expect_ints[i][j]) { 106 | printf("item %zu: expect: %d, get: %d\n", i, expect_ints[i][j], test_data->intarrays[i][j]); 107 | ret = 1; 108 | goto out; 109 | } 110 | } 111 | } 112 | 113 | // check bool array of int32 114 | if (test_data->boolarrays_len != 4) { 115 | printf("invalid bool arrays len\n"); 116 | ret = 1; 117 | goto out; 118 | } 119 | int32_t expect_bools[4][2] = { 120 | {true, false}, 121 | {false, true}, 122 | {true}, 123 | {false}, 124 | }; 125 | size_t bool_lens[4] = {2, 2, 1, 1}; 126 | 127 | for (i = 0; i < 4; i++) { 128 | if (test_data->boolarrays[i] == NULL || test_data->boolarrays_item_lens == NULL) { 129 | printf("item %zu is null\n", i); 130 | ret = 1; 131 | goto out; 132 | } 133 | if (test_data->boolarrays_item_lens[i] != bool_lens[i]) { 134 | printf("double array bool item %zu is expect len: %zu, get: %zu\n", i, bool_lens[i], test_data->boolarrays_item_lens[i]); 135 | ret = 1; 136 | goto out; 137 | } 138 | for (j = 0; j < bool_lens[i]; j++) { 139 | if (test_data->boolarrays[i][j] != expect_bools[i][j]) { 140 | printf("item %zu: expect: %d, get: %d\n", i, expect_bools[i][j], test_data->boolarrays[i][j]); 141 | ret = 1; 142 | goto out; 143 | } 144 | } 145 | } 146 | 147 | 148 | // check object array of int32 149 | if (test_data->objectarrays_len != 2) { 150 | printf("invalid object arrays len\n"); 151 | ret = 1; 152 | goto out; 153 | } 154 | bool obj_firsts[2][3] = { 155 | {true, false, true}, 156 | {false, true, false}, 157 | }; 158 | char *obj_seconds[2][3] = { 159 | {"item1", "item2", "item3"}, 160 | {"item11", "item12", "item13"}, 161 | }; 162 | for (i = 0; i < 2; i++) { 163 | if (test_data->objectarrays[i] == NULL || test_data->objectarrays_item_lens == NULL) { 164 | printf("object array item %zu: is null\n", i); 165 | ret = 1; 166 | goto out; 167 | } 168 | for (j = 0; j < 3; j++) { 169 | if (obj_firsts[i][j] != test_data->objectarrays[i][j]->first) { 170 | printf("item %zu -> %zu: expect: %d, get: %d\n", 171 | i, j, obj_firsts[i][j], test_data->objectarrays[i][j]->first); 172 | ret = 1; 173 | goto out; 174 | } 175 | if (test_data->objectarrays[i][j]->second == NULL || 176 | strcmp(obj_seconds[i][j], test_data->objectarrays[i][j]->second) != 0) { 177 | printf("item %zu -> %zu: expect: %s, get: %s\n", 178 | i, j, obj_seconds[i][j], test_data->objectarrays[i][j]->second); 179 | ret = 1; 180 | goto out; 181 | } 182 | } 183 | } 184 | 185 | // check ref object array of int32 186 | if (test_data->refobjarrays_len != 2) { 187 | printf("invalid ref object arrays len\n"); 188 | ret = 1; 189 | goto out; 190 | } 191 | char *refobj_item1[2][3] = { 192 | {"first1", "first2", "first3"}, 193 | {"second1", "second2", "second3"}, 194 | }; 195 | int32_t refobj_item2[2][3] = { 196 | {1, 2, 3}, 197 | {11, 12, 13}, 198 | }; 199 | bool refobj_item3[2][3] = { 200 | {false, true, false}, 201 | {true, false, true}, 202 | }; 203 | for (i = 0; i < 2; i++) { 204 | if (test_data->refobjarrays[i] == NULL || test_data->refobjarrays_item_lens == NULL) { 205 | printf("object array item %zu: is null\n", i); 206 | ret = 1; 207 | goto out; 208 | } 209 | for (j = 0; j < 3; j++) { 210 | if (refobj_item3[i][j] != test_data->refobjarrays[i][j]->item3) { 211 | printf("item %zu -> %zu: expect: %d, get: %d\n", 212 | i, j, refobj_item3[i][j], test_data->refobjarrays[i][j]->item3); 213 | ret = 1; 214 | goto out; 215 | } 216 | if (refobj_item2[i][j] != test_data->refobjarrays[i][j]->item2) { 217 | printf("item %zu -> %zu: expect: %d, get: %d\n", 218 | i, j, refobj_item2[i][j], test_data->refobjarrays[i][j]->item2); 219 | ret = 1; 220 | goto out; 221 | } 222 | if (test_data->refobjarrays[i][j]->item1 == NULL || 223 | strcmp(refobj_item1[i][j], test_data->refobjarrays[i][j]->item1) != 0) { 224 | printf("item %zu -> %zu: expect: %s, get: %s\n", 225 | i, j, refobj_item1[i][j], test_data->refobjarrays[i][j]->item1); 226 | ret = 1; 227 | goto out; 228 | } 229 | } 230 | } 231 | printf("double array of object check parse sucess\n"); 232 | 233 | // update test data, and check generate json 234 | free(test_data->strarrays[0][0]); 235 | test_data->strarrays[0][0] = strdup("stringtestflag"); 236 | test_data->intarrays[0][0] = 8888; 237 | free(test_data->objectarrays[0][0]->second); 238 | test_data->objectarrays[0][0]->second = strdup("objectteststr"); 239 | free(test_data->refobjarrays[0][0]->item1); 240 | test_data->refobjarrays[0][0]->item1 = strdup("objectrefstr"); 241 | 242 | json_buf = basic_test_double_array_generate_json(test_data, &ctx, &err); 243 | if (json_buf == NULL) { 244 | printf("gen error %s\n", err); 245 | ret = 1; 246 | goto out; 247 | } 248 | 249 | printf("%s\n", json_buf); 250 | 251 | // origin json str should same with generate new json str, 252 | if (strstr(json_buf, "stringtestflag") == NULL) 253 | { 254 | ret = 51; 255 | goto out; 256 | } 257 | if (strstr(json_buf, "objectteststr") == NULL) 258 | { 259 | ret = 52; 260 | goto out; 261 | } 262 | if (strstr(json_buf, "objectrefstr") == NULL) 263 | { 264 | ret = 53; 265 | goto out; 266 | } 267 | if (strstr(json_buf, "8888") == NULL) 268 | { 269 | ret = 54; 270 | goto out; 271 | } 272 | 273 | out: 274 | free(err); 275 | free(json_buf); 276 | free_basic_test_double_array(test_data); 277 | return ret; 278 | } 279 | 280 | int do_test_top_array_of_int() 281 | { 282 | parser_error err = NULL; 283 | struct parser_context ctx = { 0 }; 284 | int ret = 0; 285 | 286 | basic_test_top_array_int_container *test_data = basic_test_top_array_int_container_parse_file( 287 | "tests/data/top_array_int.json", &ctx, &err); 288 | char *json_buf = NULL; 289 | 290 | if (test_data == NULL) 291 | { 292 | printf("top int array parse error %s\n", err); 293 | free(err); 294 | return 1; 295 | } 296 | size_t i; 297 | uint8_t expect[3] = {1, 2, 3}; 298 | if (test_data->len != 3) 299 | { 300 | printf("top int array expect len: 3, get: %zu\n", test_data->len); 301 | ret = 1; 302 | goto out; 303 | } 304 | 305 | for (i = 0; i < 3; i++) 306 | { 307 | if (test_data->items[i] != expect[i]) 308 | { 309 | printf("item %zu: top int array expect: %u, get: %u\n", i, expect[i], test_data->items[i]); 310 | ret = 1; 311 | goto out; 312 | } 313 | } 314 | test_data->items[0] = 111; 315 | json_buf = basic_test_top_array_int_container_generate_json(test_data, &ctx, &err); 316 | if (json_buf == NULL) 317 | { 318 | printf("gen error %s\n", err); 319 | ret = 1; 320 | goto out; 321 | } 322 | if (strstr(json_buf, "111") == NULL) 323 | { 324 | printf("change not work %s\n", err); 325 | ret = 1; 326 | goto out; 327 | } 328 | 329 | out: 330 | free(err); 331 | free_basic_test_top_array_int_container(test_data); 332 | free(json_buf); 333 | return ret; 334 | } 335 | 336 | int do_test_top_array_of_string() 337 | { 338 | parser_error err = NULL; 339 | struct parser_context ctx = { 0 }; 340 | int ret = 0; 341 | 342 | basic_test_top_array_string_container *test_data = basic_test_top_array_string_container_parse_file( 343 | "tests/data/top_array_string.json", &ctx, &err); 344 | char *json_buf = NULL; 345 | 346 | if (test_data == NULL) 347 | { 348 | printf("top int array parse error %s\n", err); 349 | free(err); 350 | return 1; 351 | } 352 | size_t i; 353 | char *expect[3] = {"topstr1", "topstr2"}; 354 | if (test_data->len != 2) 355 | { 356 | printf("top int array expect len: 3, get: %zu\n", test_data->len); 357 | ret = 1; 358 | goto out; 359 | } 360 | 361 | for (i = 0; i < 2; i++) 362 | { 363 | if (test_data->items[i] == NULL || strcmp(test_data->items[i], expect[i]) != 0) 364 | { 365 | printf("item %zu: top int array expect: %s, get: %s\n", i, expect[i], test_data->items[i]); 366 | ret = 1; 367 | goto out; 368 | } 369 | } 370 | free(test_data->items[0]); 371 | test_data->items[0] = strdup("hello"); 372 | json_buf = basic_test_top_array_string_container_generate_json(test_data, &ctx, &err); 373 | if (json_buf == NULL) 374 | { 375 | printf("gen error %s\n", err); 376 | ret = 1; 377 | goto out; 378 | } 379 | if (strstr(json_buf, "hello") == NULL) 380 | { 381 | printf("change not work %s\n", err); 382 | ret = 1; 383 | goto out; 384 | } 385 | 386 | out: 387 | free(err); 388 | free_basic_test_top_array_string_container(test_data); 389 | free(json_buf); 390 | return ret; 391 | } 392 | 393 | int do_test_top_double_array_of_string() 394 | { 395 | parser_error err = NULL; 396 | struct parser_context ctx = { 0 }; 397 | int ret = 0; 398 | 399 | basic_test_top_double_array_string_container *test_data = basic_test_top_double_array_string_container_parse_file( 400 | "tests/data/top_double_array_string.json", &ctx, &err); 401 | char *json_buf = NULL; 402 | 403 | if (test_data == NULL) 404 | { 405 | printf("top string double array parse error %s\n", err); 406 | free(err); 407 | return 1; 408 | } 409 | size_t i; 410 | char *expect[3][2] = {{"doublestr11"}, {"doublestr21", "doublestr22"}, {"doublestr31", "doublestr32"}}; 411 | size_t expect_lens[3] = {1, 2, 2}; 412 | if (test_data->len != 3) 413 | { 414 | printf("top string double array expect len: 3, get: %zu\n", test_data->len); 415 | ret = 1; 416 | goto out; 417 | } 418 | 419 | for (i = 0; i < 3; i++) 420 | { 421 | size_t j; 422 | if (test_data->subitem_lens[i] != expect_lens[i]) 423 | { 424 | printf("item %zu: top string double array expect len: %zu, get: %zu\n", i, expect_lens[i], test_data->subitem_lens[i]); 425 | ret = 1; 426 | goto out; 427 | } 428 | for (j = 0; j < expect_lens[i]; j++) 429 | { 430 | if (test_data->items[i][j] == NULL || strcmp(test_data->items[i][j], expect[i][j]) != 0) 431 | { 432 | printf("item %zu: top int array expect: %s, get: %s\n", i, expect[i][j], test_data->items[i][j]); 433 | ret = 1; 434 | goto out; 435 | } 436 | } 437 | } 438 | free(test_data->items[0][0]); 439 | test_data->items[0][0] = strdup("hello"); 440 | json_buf = basic_test_top_double_array_string_container_generate_json(test_data, &ctx, &err); 441 | if (json_buf == NULL) 442 | { 443 | printf("gen error %s\n", err); 444 | ret = 1; 445 | goto out; 446 | } 447 | if (strstr(json_buf, "hello") == NULL) 448 | { 449 | printf("change not work %s\n", err); 450 | ret = 1; 451 | goto out; 452 | } 453 | 454 | out: 455 | free_basic_test_top_double_array_string_container(test_data); 456 | free(err); 457 | free(json_buf); 458 | return ret; 459 | } 460 | 461 | int do_test_top_double_array_of_int() 462 | { 463 | parser_error err = NULL; 464 | struct parser_context ctx = { 0 }; 465 | int ret = 0; 466 | 467 | basic_test_top_double_array_int_container *test_data = basic_test_top_double_array_int_container_parse_file( 468 | "tests/data/top_double_array_int.json", &ctx, &err); 469 | char *json_buf = NULL; 470 | 471 | if (test_data == NULL) 472 | { 473 | printf("top int double array parse error %s\n", err); 474 | free(err); 475 | return 1; 476 | } 477 | size_t i; 478 | int32_t expect[3][3] = {{1, 2, 3}, {1, 2}, {1}}; 479 | size_t expect_lens[3] = {3, 2, 1}; 480 | if (test_data->len != 3) 481 | { 482 | printf("top int double array expect len: 3, get: %zu\n", test_data->len); 483 | ret = 1; 484 | goto out; 485 | } 486 | 487 | for (i = 0; i < 3; i++) 488 | { 489 | size_t j; 490 | if (test_data->subitem_lens[i] != expect_lens[i]) 491 | { 492 | printf("item %zu: top int double array expect len: %zu, get: %zu\n", i, expect_lens[i], test_data->subitem_lens[i]); 493 | ret = 1; 494 | goto out; 495 | } 496 | for (j = 0; j < expect_lens[i]; j++) 497 | { 498 | if (test_data->items[i][j] != expect[i][j]) 499 | { 500 | printf("item %zu: top int array expect: %d, get: %d\n", i, expect[i][j], test_data->items[i][j]); 501 | ret = 1; 502 | goto out; 503 | } 504 | } 505 | } 506 | test_data->items[0][0] = 888; 507 | json_buf = basic_test_top_double_array_int_container_generate_json(test_data, &ctx, &err); 508 | if (json_buf == NULL) 509 | { 510 | printf("gen error %s\n", err); 511 | ret = 1; 512 | goto out; 513 | } 514 | if (strstr(json_buf, "888") == NULL) 515 | { 516 | printf("change not work %s\n", err); 517 | ret = 1; 518 | goto out; 519 | } 520 | 521 | out: 522 | free(err); 523 | free_basic_test_top_double_array_int_container(test_data); 524 | free(json_buf); 525 | return ret; 526 | } 527 | 528 | int do_test_top_double_array_of_obj() 529 | { 530 | parser_error err = NULL; 531 | struct parser_context ctx = { 0 }; 532 | int ret = 0; 533 | 534 | basic_test_top_double_array_obj_container *test_data = basic_test_top_double_array_obj_container_parse_file( 535 | "tests/data/top_double_array_obj.json", &ctx, &err); 536 | char *json_buf = NULL; 537 | 538 | if (test_data == NULL) 539 | { 540 | printf("top int double array parse error %s\n", err); 541 | free(err); 542 | return 1; 543 | } 544 | size_t i; 545 | bool expect_bools[2][2] = {{true, false}, {false, true}}; 546 | int32_t expect_ints[2][2] = {{11, 12}, {21, 22}}; 547 | char *expect_strs[2][2] = {{"doubleobj11", "doubleobj12"}, {"doubleobj21", "doubleobj22"}}; 548 | size_t expect_lens[3] = {2, 2}; 549 | if (test_data->len != 2) 550 | { 551 | printf("top obj double array expect len: 2, get: %zu\n", test_data->len); 552 | ret = 1; 553 | goto out; 554 | } 555 | 556 | for (i = 0; i < 2; i++) 557 | { 558 | size_t j; 559 | if (test_data->subitem_lens[i] != expect_lens[i]) 560 | { 561 | printf("item %zu: top obj double array expect len: %zu, get: %zu\n", i, expect_lens[i], test_data->subitem_lens[i]); 562 | ret = 1; 563 | goto out; 564 | } 565 | for (j = 0; j < expect_lens[i]; j++) 566 | { 567 | if (test_data->items[i][j]->first != expect_bools[i][j]) 568 | { 569 | printf("item %zu: top int array first expect: %d, get: %d\n", i, expect_bools[i][j], test_data->items[i][j]->first); 570 | ret = 1; 571 | goto out; 572 | } 573 | if (test_data->items[i][j]->second != expect_ints[i][j]) 574 | { 575 | printf("item %zu: top int array second expect: %d, get: %d\n", i, expect_ints[i][j], test_data->items[i][j]->second); 576 | ret = 1; 577 | goto out; 578 | } 579 | if (test_data->items[i][j]->third == NULL || strcmp(test_data->items[i][j]->third, expect_strs[i][j]) != 0) 580 | { 581 | printf("item %zu: top int array third expect: %s, get: %s\n", i, expect_strs[i][j], test_data->items[i][j]->third); 582 | ret = 1; 583 | goto out; 584 | } 585 | } 586 | } 587 | test_data->items[0][0]->second = 999; 588 | free(test_data->items[0][1]->third); 589 | test_data->items[0][1]->third = strdup("hello"); 590 | json_buf = basic_test_top_double_array_obj_container_generate_json(test_data, &ctx, &err); 591 | if (json_buf == NULL) 592 | { 593 | printf("gen error %s\n", err); 594 | ret = 1; 595 | goto out; 596 | } 597 | if (strstr(json_buf, "999") == NULL) 598 | { 599 | printf("change second not work %s\n", err); 600 | ret = 1; 601 | goto out; 602 | } 603 | if (strstr(json_buf, "hello") == NULL) 604 | { 605 | printf("change third not work %s\n", err); 606 | ret = 1; 607 | goto out; 608 | } 609 | 610 | out: 611 | free(err); 612 | free_basic_test_top_double_array_obj_container(test_data); 613 | free(json_buf); 614 | return ret; 615 | } 616 | 617 | int do_test_top_double_array_of_refobj() 618 | { 619 | parser_error err = NULL; 620 | struct parser_context ctx = { 0 }; 621 | int ret = 0; 622 | 623 | basic_test_top_double_array_refobj_container *test_data = basic_test_top_double_array_refobj_container_parse_file( 624 | "tests/data/top_double_array_refobj.json", &ctx, &err); 625 | char *json_buf = NULL; 626 | 627 | if (test_data == NULL) 628 | { 629 | printf("top int double array parse error %s\n", err); 630 | free(err); 631 | return 1; 632 | } 633 | size_t i; 634 | bool expect_bools[2][2] = {{true, false}, {false, true}}; 635 | int32_t expect_ints[2][2] = {{11, 12}, {21, 22}}; 636 | char *expect_strs[2][2] = {{"test11", "test12"}, {"test21", "test22"}}; 637 | size_t expect_lens[3] = {2, 2}; 638 | if (test_data->len != 2) 639 | { 640 | printf("top obj double array expect len: 2, get: %zu\n", test_data->len); 641 | ret = 1; 642 | goto out; 643 | } 644 | 645 | for (i = 0; i < 2; i++) 646 | { 647 | size_t j; 648 | if (test_data->subitem_lens[i] != expect_lens[i]) 649 | { 650 | printf("item %zu: top obj double array expect len: %zu, get: %zu\n", i, expect_lens[i], test_data->subitem_lens[i]); 651 | ret = 1; 652 | goto out; 653 | } 654 | for (j = 0; j < expect_lens[i]; j++) 655 | { 656 | if (test_data->items[i][j]->item3 != expect_bools[i][j]) 657 | { 658 | printf("item %zu: top int array first expect: %d, get: %d\n", i, expect_bools[i][j], test_data->items[i][j]->item3); 659 | ret = 1; 660 | goto out; 661 | } 662 | if (test_data->items[i][j]->item2 != expect_ints[i][j]) 663 | { 664 | printf("item %zu: top int array second expect: %d, get: %d\n", i, expect_ints[i][j], test_data->items[i][j]->item2); 665 | ret = 1; 666 | goto out; 667 | } 668 | if (test_data->items[i][j]->item1 == NULL || strcmp(test_data->items[i][j]->item1, expect_strs[i][j]) != 0) 669 | { 670 | printf("item %zu: top int array third expect: %s, get: %s\n", i, expect_strs[i][j], test_data->items[i][j]->item1); 671 | ret = 1; 672 | goto out; 673 | } 674 | } 675 | } 676 | test_data->items[0][0]->item2 = 999; 677 | free(test_data->items[0][1]->item1); 678 | test_data->items[0][1]->item1 = strdup("hello"); 679 | json_buf = basic_test_top_double_array_refobj_container_generate_json(test_data, &ctx, &err); 680 | if (json_buf == NULL) 681 | { 682 | printf("gen error %s\n", err); 683 | ret = 1; 684 | goto out; 685 | } 686 | if (strstr(json_buf, "999") == NULL) 687 | { 688 | printf("change second not work %s\n", err); 689 | ret = 1; 690 | goto out; 691 | } 692 | if (strstr(json_buf, "hello") == NULL) 693 | { 694 | printf("change third not work %s\n", err); 695 | ret = 1; 696 | goto out; 697 | } 698 | 699 | out: 700 | free(err); 701 | free_basic_test_top_double_array_refobj_container(test_data); 702 | free(json_buf); 703 | return ret; 704 | } 705 | 706 | int 707 | main () 708 | { 709 | int ret; 710 | 711 | ret = do_test_object_double_array(); 712 | if (ret != 0) { 713 | printf("do_test_object_double_array failed with: %d\n", ret); 714 | exit(ret); 715 | } 716 | 717 | ret = do_test_top_array_of_int(); 718 | if (ret != 0) { 719 | printf("do_test_top_array_of_int failed with: %d\n", ret); 720 | exit(ret); 721 | } 722 | ret = do_test_top_array_of_string(); 723 | if (ret != 0) { 724 | printf("do_test_top_array_of_string failed with: %d\n", ret); 725 | exit(ret); 726 | } 727 | 728 | ret = do_test_top_double_array_of_string(); 729 | if (ret != 0) { 730 | printf("do_test_top_double_array_of_string failed with: %d\n", ret); 731 | exit(ret); 732 | } 733 | ret = do_test_top_double_array_of_int(); 734 | if (ret != 0) { 735 | printf("do_test_top_double_array_of_int failed with: %d\n", ret); 736 | exit(ret); 737 | } 738 | ret = do_test_top_double_array_of_obj(); 739 | if (ret != 0) { 740 | printf("do_test_top_double_array_of_obj failed with: %d\n", ret); 741 | exit(ret); 742 | } 743 | ret = do_test_top_double_array_of_refobj(); 744 | if (ret != 0) { 745 | printf("do_test_top_double_array_of_refobj failed with: %d\n", ret); 746 | exit(ret); 747 | } 748 | 749 | return 0; 750 | } 751 | --------------------------------------------------------------------------------