├── .gitignore ├── benchmarks └── async │ ├── .gitignore │ ├── go.mod │ ├── Makefile │ ├── go.sum │ ├── README.md │ ├── bench.go │ └── bench.cpp ├── examples ├── custom │ ├── .gitignore │ ├── go.mod │ ├── Makefile │ └── go.sum ├── full │ ├── .gitignore │ ├── go.mod │ ├── Makefile │ ├── full_rules.yaml │ └── go.sum ├── source │ ├── .gitignore │ ├── go.mod │ ├── Makefile │ ├── go.sum │ └── source.go └── extractor │ ├── .gitignore │ ├── go.mod │ ├── Makefile │ ├── go.sum │ └── extractor.go ├── .goreleaser.yml ├── OWNERS ├── pkg ├── sdk │ ├── plugin_types_include.patch │ ├── symbols │ │ ├── info │ │ │ ├── info.h │ │ │ ├── info.c │ │ │ └── info.go │ │ ├── extract │ │ │ ├── internal │ │ │ │ └── asyncbench │ │ │ │ │ ├── workload.go │ │ │ │ │ ├── bench.h │ │ │ │ │ ├── bench.c │ │ │ │ │ ├── async_test.go │ │ │ │ │ └── async.go │ │ │ ├── extract.h │ │ │ ├── extract.go │ │ │ ├── extract.c │ │ │ └── extract_test.go │ │ ├── fields │ │ │ ├── fields_test.go │ │ │ └── fields.go │ │ ├── evtstr │ │ │ ├── evtstr.go │ │ │ └── evtstr_test.go │ │ ├── initschema │ │ │ ├── initschema.go │ │ │ └── initschema_test.go │ │ ├── listopen │ │ │ ├── listopen.go │ │ │ └── listopen_test.go │ │ ├── progress │ │ │ ├── progress.go │ │ │ └── progress_test.go │ │ ├── lasterr │ │ │ ├── lasterr.go │ │ │ └── lasterr_test.go │ │ ├── nextbatch │ │ │ ├── nextbatch.go │ │ │ └── nextbatch_test.go │ │ ├── doc.go │ │ ├── open │ │ │ ├── open.go │ │ │ └── open_test.go │ │ └── initialize │ │ │ ├── initialize_test.go │ │ │ └── initialize.go │ ├── plugins │ │ ├── source │ │ │ ├── source_test.go │ │ │ └── source.go │ │ ├── doc.go │ │ ├── extractor │ │ │ ├── extractor_test.go │ │ │ └── extractor.go │ │ ├── plugins_test.go │ │ └── plugins.go │ ├── internal │ │ ├── hooks │ │ │ └── hooks.go │ │ └── sdk │ │ │ └── inmemory.go │ ├── buffers.go │ ├── doc.go │ ├── instance.go │ ├── plugin.go │ ├── sdk.go │ └── event_test.go ├── loader │ ├── plugin_api_include.patch │ ├── strlcat.patch │ ├── plugin_loader.h │ └── strlcpy.patch ├── ptr │ ├── string_test.go │ ├── string.go │ └── bytes.go └── cgo │ ├── handle_test.go │ └── handle.go ├── go.mod ├── .github ├── dependabot.yaml ├── workflows │ ├── release.yml │ ├── ci.yml │ └── codeql-analysis.yml └── PULL_REQUEST_TEMPLATE.md ├── release.md ├── Makefile ├── go.sum └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .vscode 3 | -------------------------------------------------------------------------------- /benchmarks/async/.gitignore: -------------------------------------------------------------------------------- 1 | build -------------------------------------------------------------------------------- /examples/custom/.gitignore: -------------------------------------------------------------------------------- 1 | *.so 2 | *.h -------------------------------------------------------------------------------- /examples/full/.gitignore: -------------------------------------------------------------------------------- 1 | *.so 2 | *.h -------------------------------------------------------------------------------- /examples/source/.gitignore: -------------------------------------------------------------------------------- 1 | *.so 2 | *.h -------------------------------------------------------------------------------- /examples/extractor/.gitignore: -------------------------------------------------------------------------------- 1 | *.so 2 | *.h -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | project_name: plugin-sdk-go 2 | version: 2 3 | 4 | builds: 5 | - skip: true 6 | 7 | release: 8 | github: {} 9 | prerelease: auto 10 | -------------------------------------------------------------------------------- /OWNERS: -------------------------------------------------------------------------------- 1 | approvers: 2 | - leogr 3 | - jasondellaluce 4 | emeritus_approvers: 5 | - leodido 6 | - fntlnz 7 | - kris-nova 8 | - mstemm 9 | - ldegio 10 | -------------------------------------------------------------------------------- /examples/custom/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/falcosecurity/plugin-sdk-go/examples/custom 2 | 3 | replace github.com/falcosecurity/plugin-sdk-go => ../../ 4 | 5 | go 1.15 6 | 7 | require github.com/falcosecurity/plugin-sdk-go v0.0.0-00010101000000-000000000000 8 | -------------------------------------------------------------------------------- /examples/source/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/falcosecurity/plugin-sdk-go/examples/source 2 | 3 | replace github.com/falcosecurity/plugin-sdk-go => ../../ 4 | 5 | go 1.15 6 | 7 | require github.com/falcosecurity/plugin-sdk-go v0.0.0-00010101000000-000000000000 8 | -------------------------------------------------------------------------------- /benchmarks/async/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/falcosecurity/plugin-sdk-go/benchmarks/async 2 | 3 | replace github.com/falcosecurity/plugin-sdk-go => ../../ 4 | 5 | go 1.15 6 | 7 | require github.com/falcosecurity/plugin-sdk-go v0.0.0-00010101000000-000000000000 8 | -------------------------------------------------------------------------------- /examples/extractor/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/falcosecurity/plugin-sdk-go/examples/extractor 2 | 3 | replace github.com/falcosecurity/plugin-sdk-go => ../../ 4 | 5 | go 1.15 6 | 7 | require github.com/falcosecurity/plugin-sdk-go v0.0.0-00010101000000-000000000000 8 | -------------------------------------------------------------------------------- /examples/full/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/falcosecurity/plugin-sdk-go/examples/full 2 | 3 | replace github.com/falcosecurity/plugin-sdk-go => ../../ 4 | 5 | go 1.15 6 | 7 | require ( 8 | github.com/alecthomas/jsonschema v0.0.0-20220216202328-9eeeec9d044b 9 | github.com/falcosecurity/plugin-sdk-go v0.0.0-00010101000000-000000000000 10 | ) 11 | -------------------------------------------------------------------------------- /pkg/sdk/plugin_types_include.patch: -------------------------------------------------------------------------------- 1 | diff --git a/pkg/sdk/plugin_api.h b/pkg/sdk/plugin_api.h 2 | index 0c877a2..54949b3 100644 3 | --- a/pkg/sdk/plugin_api.h 4 | +++ b/pkg/sdk/plugin_api.h 5 | @@ -18,7 +18,7 @@ limitations under the License. 6 | 7 | #pragma once 8 | 9 | -#include 10 | +#include "plugin_types.h" 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | -------------------------------------------------------------------------------- /pkg/loader/plugin_api_include.patch: -------------------------------------------------------------------------------- 1 | diff --git a/pkg/loader/plugin_loader.h b/pkg/loader/plugin_loader.h 2 | index e6e8333..8db0d70 100644 3 | --- a/pkg/loader/plugin_loader.h 4 | +++ b/pkg/loader/plugin_loader.h 5 | @@ -18,7 +18,7 @@ limitations under the License. 6 | 7 | #pragma once 8 | 9 | -#include 10 | +#include "plugin_api.h" 11 | 12 | #include 13 | 14 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/falcosecurity/plugin-sdk-go 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/stretchr/testify v1.8.2 7 | github.com/xeipuuv/gojsonschema v1.2.0 8 | ) 9 | 10 | require ( 11 | github.com/davecgh/go-spew v1.1.1 // indirect 12 | github.com/pmezard/go-difflib v1.0.0 // indirect 13 | github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect 14 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect 15 | gopkg.in/yaml.v3 v3.0.1 // indirect 16 | ) 17 | -------------------------------------------------------------------------------- /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gomod 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | open-pull-requests-limit: 10 8 | groups: 9 | gomod: 10 | update-types: 11 | - "patch" 12 | 13 | - package-ecosystem: "github-actions" 14 | directory: "/" 15 | schedule: 16 | interval: "weekly" 17 | open-pull-requests-limit: 10 18 | groups: 19 | actions: 20 | update-types: 21 | - "minor" 22 | - "patch" 23 | -------------------------------------------------------------------------------- /pkg/sdk/symbols/info/info.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | #pragma once 19 | 20 | const char* get_default_required_api_version(); 21 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v[0-9]+.[0-9]+.[0-9]+' 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | 12 | permissions: 13 | contents: write 14 | 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 18 | with: 19 | fetch-depth: 0 20 | 21 | - name: Setup Go 22 | uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 23 | with: 24 | go-version: 1.17 25 | 26 | - name: Run GoReleaser 27 | uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0 28 | with: 29 | args: release --clean --timeout 60m 30 | env: 31 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 32 | -------------------------------------------------------------------------------- /examples/full/Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # Copyright (C) 2023 The Falco Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 6 | # the License. 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 distributed under the License is distributed on an 11 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | # specific language governing permissions and limitations under the License. 13 | # 14 | 15 | SHELL=/bin/bash -o pipefail 16 | GO ?= go 17 | 18 | NAME := full 19 | OUTPUT := lib$(NAME).so 20 | 21 | all: $(OUTPUT) 22 | 23 | clean: 24 | @rm -f *.so *.h 25 | 26 | $(OUTPUT): *.go 27 | @GODEBUG=cgocheck=1 $(GO) build -buildmode=c-shared -o $(OUTPUT) 28 | -------------------------------------------------------------------------------- /examples/custom/Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # Copyright (C) 2023 The Falco Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 6 | # the License. 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 distributed under the License is distributed on an 11 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | # specific language governing permissions and limitations under the License. 13 | # 14 | 15 | SHELL=/bin/bash -o pipefail 16 | GO ?= go 17 | 18 | NAME := custom 19 | OUTPUT := lib$(NAME).so 20 | 21 | all: $(OUTPUT) 22 | 23 | clean: 24 | @rm -f *.so *.h 25 | 26 | $(OUTPUT): *.go 27 | @GODEBUG=cgocheck=1 $(GO) build -buildmode=c-shared -o $(OUTPUT) 28 | -------------------------------------------------------------------------------- /examples/extractor/Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # Copyright (C) 2023 The Falco Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 6 | # the License. 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 distributed under the License is distributed on an 11 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | # specific language governing permissions and limitations under the License. 13 | # 14 | 15 | SHELL=/bin/bash -o pipefail 16 | GO ?= go 17 | 18 | NAME := extractor 19 | OUTPUT := lib$(NAME).so 20 | 21 | all: $(OUTPUT) 22 | 23 | clean: 24 | @rm -f *.so *.h 25 | 26 | $(OUTPUT): *.go 27 | @GODEBUG=cgocheck=1 $(GO) build -buildmode=c-shared -o $(OUTPUT) 28 | -------------------------------------------------------------------------------- /examples/source/Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # Copyright (C) 2023 The Falco Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 6 | # the License. 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 distributed under the License is distributed on an 11 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | # specific language governing permissions and limitations under the License. 13 | # 14 | 15 | SHELL=/bin/bash -o pipefail 16 | GO ?= go 17 | 18 | NAME := source 19 | OUTPUT := lib$(NAME).so 20 | 21 | all: $(OUTPUT) 22 | 23 | clean: 24 | @rm -f *.so *.h 25 | 26 | $(OUTPUT): *.go 27 | @GODEBUG=cgocheck=1 $(GO) build -buildmode=c-shared -o $(OUTPUT) 28 | 29 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | run-tests: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout plugin-sdk-go 14 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 15 | 16 | - name: Setup Golang 17 | uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 18 | with: 19 | go-version: '^1.19' 20 | 21 | - name: Run tests 22 | run: go test ./... 23 | 24 | build-example-plugins: 25 | runs-on: ubuntu-latest 26 | 27 | steps: 28 | - name: Checkout plugin-sdk-go 29 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 30 | 31 | - name: Setup Golang 32 | uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 33 | with: 34 | go-version: '^1.19' 35 | 36 | - name: Build all example plugins 37 | run: make examples 38 | -------------------------------------------------------------------------------- /pkg/sdk/symbols/extract/internal/asyncbench/workload.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package asyncbench 19 | 20 | /* 21 | #include "bench.h" 22 | */ 23 | import "C" 24 | 25 | var inData, outData int 26 | 27 | var ( 28 | testInput = 1 29 | expectedOut = 2 30 | badOut = 3 31 | ) 32 | 33 | //export doWork 34 | func doWork(input int) int { 35 | return input + 1 36 | } 37 | 38 | func do_work_c(n int) int { 39 | return int(C.do_work_c(C.int(n))) 40 | } 41 | -------------------------------------------------------------------------------- /benchmarks/async/Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # Copyright (C) 2023 The Falco Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 6 | # the License. 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 distributed under the License is distributed on an 11 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | # specific language governing permissions and limitations under the License. 13 | # 14 | 15 | SHELL =/bin/bash -o pipefail 16 | GO ?= go 17 | NAME := bench 18 | OUTPUTDIR := build 19 | OUTPUT := $(OUTPUTDIR)/bench 20 | OUTPUTGO := $(OUTPUTDIR)/libgo$(NAME).a 21 | 22 | all: $(OUTPUT) 23 | 24 | clean: 25 | @rm -fr $(OUTPUTDIR) 26 | 27 | $(OUTPUT): bench.cpp bench.go 28 | mkdir -p $(OUTPUTDIR) 29 | GODEBUG=cgocheck=1 $(GO) build -buildmode=c-archive -o $(OUTPUTGO) bench.go 30 | $(CXX) bench.cpp $(OUTPUTGO) -std=c++11 -pthread -o $(OUTPUT) 31 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | schedule: 11 | - cron: '45 5 * * 2' 12 | 13 | jobs: 14 | analyze: 15 | name: Analyze 16 | runs-on: ubuntu-latest 17 | permissions: 18 | actions: read 19 | contents: read 20 | security-events: write 21 | 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | language: 26 | - 'go' 27 | 28 | steps: 29 | - name: Checkout repository 30 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 31 | 32 | # Initializes the CodeQL tools for scanning. 33 | - name: Initialize CodeQL 34 | uses: github/codeql-action/init@16140ae1a102900babc80a33c44059580f687047 # v4.30.9 35 | with: 36 | languages: ${{ matrix.language }} 37 | 38 | - name: Autobuild 39 | uses: github/codeql-action/autobuild@16140ae1a102900babc80a33c44059580f687047 # v4.30.9 40 | 41 | - name: Perform CodeQL Analysis 42 | uses: github/codeql-action/analyze@16140ae1a102900babc80a33c44059580f687047 # v4.30.9 43 | -------------------------------------------------------------------------------- /pkg/sdk/symbols/extract/internal/asyncbench/bench.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | #pragma once 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | enum worker_state 28 | { 29 | WAIT = 0, 30 | DATA_REQ = 1, 31 | EXIT_REQ = 2, 32 | EXIT_ACK = 3, 33 | }; 34 | 35 | void data_request(atomic_int_least32_t *lock); 36 | 37 | void async_benchmark(int32_t *lock, int n); 38 | int sync_benchmark(int n, int input); 39 | 40 | int do_work_c(int i); 41 | -------------------------------------------------------------------------------- /pkg/sdk/symbols/extract/extract.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | #pragma once 19 | 20 | #include 21 | #include "../../plugin_api.h" 22 | 23 | typedef struct async_extractor_info 24 | { 25 | // lock 26 | atomic_int_least32_t lock; 27 | 28 | // input data 29 | ss_plugin_t *s; 30 | const ss_plugin_event_input *evt; 31 | uint32_t num_fields; 32 | ss_plugin_extract_field *fields; 33 | ss_plugin_extract_value_offsets *value_offsets; 34 | 35 | // output data 36 | int32_t rc; 37 | } async_extractor_info; 38 | 39 | async_extractor_info *async_init(size_t size); 40 | void async_deinit(); 41 | -------------------------------------------------------------------------------- /pkg/sdk/symbols/info/info.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | #include "info.h" 19 | #include "../../plugin_types.h" 20 | #include "../../plugin_api.h" 21 | 22 | const char* get_default_required_api_version() 23 | { 24 | return PLUGIN_API_VERSION_STR; 25 | } 26 | 27 | // todo(jasondellaluce,therealbobo): support this for real when we decide to 28 | // deal with non-plugin events in the SDK Go 29 | uint16_t* plugin_get_extract_event_types(uint32_t* num_types) 30 | { 31 | static uint16_t types[] = { 322 }; // PPME_PLUGINEVENT_E 32 | *num_types = sizeof(types) / sizeof(uint16_t); 33 | return &types[0]; 34 | } 35 | -------------------------------------------------------------------------------- /release.md: -------------------------------------------------------------------------------- 1 | # Release Process 2 | 3 | When we release we do the following process: 4 | 5 | 1. We decide together (usually in the #falco channel in [slack](https://kubernetes.slack.com/messages/falco)) what's the [next version](#About-versioning) to tag 6 | 2. A person with repository rights does the tag 7 | 3. The same person runs commands in their machine following the [Release commands](#Release-commands) section below 8 | 4. The tag is live on [Github](https://github.com/falcosecurity/plugin-sdk-go/releases) with the changelog attached 9 | 10 | ## Release commands 11 | 12 | Just tag the [version](#About-versioning). For example: 13 | 14 | ```bash 15 | git tag -a v0.1.0-rc.0 -m "v0.1.0-rc.0" 16 | git push origin v0.1.0-rc.0 17 | ``` 18 | 19 | The [goreleaser](https://goreleaser.com/ci/) will run on CircleCI and do the magic :) 20 | 21 | ## About versioning 22 | 23 | This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 24 | 25 | Also, note that the `plugin-sdk-go` version is NOT paired with the Falco version nor with the Plugin API version. 26 | However, any backward-incompatible changes introduced by Plugin API represent a breaking change for the `plugin-sdk-go` too. 27 | In such a case, the major version (or the minor version when [major version is zero](https://semver.org/spec/v2.0.0.html#spec-item-4)) MUST be incremented. -------------------------------------------------------------------------------- /pkg/sdk/plugins/source/source_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package source 19 | 20 | import ( 21 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk" 22 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk/plugins" 23 | ) 24 | 25 | type testPlugin struct { 26 | plugins.BasePlugin 27 | } 28 | 29 | type testInstance struct { 30 | BaseInstance 31 | } 32 | 33 | func (m *testPlugin) Info() *plugins.Info { 34 | return &plugins.Info{ 35 | ID: 999, 36 | Name: "test", 37 | Description: "Source Test", 38 | Contact: "", 39 | Version: "", 40 | RequiredAPIVersion: "", 41 | EventSource: "test", 42 | } 43 | } 44 | 45 | func (m *testPlugin) Init(config string) error { 46 | return nil 47 | } 48 | 49 | func (m *testPlugin) Open(params string) (Instance, error) { 50 | return &testInstance{}, nil 51 | } 52 | 53 | func (m *testPlugin) String(evt sdk.EventReader) (string, error) { 54 | return "", nil 55 | } 56 | 57 | func (m *testInstance) NextBatch(pState sdk.PluginState, evts sdk.EventWriters) (int, error) { 58 | return 0, nil 59 | } 60 | -------------------------------------------------------------------------------- /pkg/sdk/symbols/extract/internal/asyncbench/bench.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | #include "bench.h" 19 | 20 | void data_request(atomic_int_least32_t *lock) 21 | { 22 | // We assume state_wait because no concurrent requests are supported 23 | enum worker_state old_val = WAIT; 24 | while (!atomic_compare_exchange_strong_explicit(lock, (uint32_t *)&old_val, DATA_REQ, memory_order_seq_cst, memory_order_seq_cst)) 25 | { 26 | old_val = WAIT; 27 | } 28 | 29 | // state_data_req acquired, wait for worker completation 30 | while (atomic_load_explicit(lock, memory_order_seq_cst) != WAIT); 31 | } 32 | 33 | void async_benchmark(int32_t *lock, int n) 34 | { 35 | for (int i = 0; i < n; i++) 36 | { 37 | data_request((atomic_int_least32_t *)lock); 38 | } 39 | } 40 | 41 | // Defined in async.go 42 | extern int doWork(int); 43 | 44 | int sync_benchmark(int n, int input) 45 | { 46 | int output; 47 | for (int i = 0; i < n; i++) 48 | { 49 | output = doWork(input); 50 | } 51 | return output; 52 | } 53 | 54 | int do_work_c(int i) 55 | { 56 | return i + 1; 57 | } 58 | -------------------------------------------------------------------------------- /examples/full/full_rules.yaml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # Copyright (C) 2023 The Falco Authors. 4 | # 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | - rule: Sample plugin rule 20 | desc: Some sample rule for testing 21 | condition: 22 | evt.num > 0 and evt.num < 5 23 | and example.count > 0 24 | and example.oddcount = true 25 | and example.initduration exists 26 | and example.evttime exists 27 | and example.ipv4addr = "224.0.0.1" 28 | and example.ipv6addr = "::1" 29 | and example.ipv4net = "192.0.3.1/16" 30 | and example.ipv6net = "2002::1234:abcd:ffff:c0a8:102:ffff/32" 31 | output: Some event ( 32 | example.count=%example.count, 33 | example.countstr=%example.countstr, 34 | example.oddcount=%example.oddcount, 35 | example.initduration=%example.initduration, 36 | example.evttime=%example.evttime, 37 | evt.time=%evt.rawtime, 38 | example.ipv4addr=%example.ipv4addr, 39 | example.ipv6addr=%example.ipv6addr, 40 | example.ipv4net=%example.ipv4net, 41 | example.ipv6net=%example.ipv6net 42 | info=%evt.plugininfo 43 | plugin=%evt.pluginname) 44 | priority: CRITICAL 45 | source: example 46 | -------------------------------------------------------------------------------- /pkg/sdk/symbols/fields/fields_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package fields 19 | 20 | import ( 21 | "encoding/json" 22 | "testing" 23 | "reflect" 24 | "unsafe" 25 | 26 | "github.com/falcosecurity/plugin-sdk-go/pkg/ptr" 27 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk" 28 | ) 29 | 30 | var sampleFields = []sdk.FieldEntry{ 31 | {Type: "uint64", Name: "test.field", Display: "Test Field", Desc: "Test Field"}, 32 | } 33 | 34 | func TestFields(t *testing.T) { 35 | // Test get/set 36 | if Fields() != nil { 37 | t.Errorf("expected nil") 38 | } 39 | SetFields(sampleFields) 40 | if len(Fields()) != len(sampleFields) { 41 | t.Errorf("expected %d, but found %d", len(sampleFields), len(Fields())) 42 | } 43 | for i, f := range Fields() { 44 | if ! reflect.DeepEqual(f, sampleFields[i]) { 45 | t.Errorf("wrong sample at index %d", i) 46 | } 47 | } 48 | 49 | // Test C symbol 50 | b, err := json.Marshal(&sampleFields) 51 | if err != nil { 52 | t.Error(err) 53 | } 54 | cStr := plugin_get_fields() 55 | str := ptr.GoString(unsafe.Pointer(cStr)) 56 | if str != string(b) { 57 | t.Errorf("expected %s, but found %s", string(b), str) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /pkg/sdk/symbols/fields/fields.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | // This package exports the following C function: 19 | // - char* plugin_get_fields() 20 | // 21 | // This function is part of the plugin_api interface as defined in plugin_api.h. 22 | // In almost all cases, your plugin should import this module, unless your 23 | // plugin exports those symbols by other means. 24 | package fields 25 | 26 | import "C" 27 | import ( 28 | "encoding/json" 29 | 30 | "github.com/falcosecurity/plugin-sdk-go/pkg/ptr" 31 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk" 32 | ) 33 | 34 | var ( 35 | fields []sdk.FieldEntry 36 | buf ptr.StringBuffer 37 | ) 38 | 39 | // SetFields sets a slice of sdk.FieldEntry representing the list of extractor 40 | // fields exported by this plugin. 41 | func SetFields(f []sdk.FieldEntry) { 42 | fields = f 43 | } 44 | 45 | // Fields returns the slice of sdk.FieldEntry set with SetFields(). 46 | func Fields() []sdk.FieldEntry { 47 | return fields 48 | } 49 | 50 | //export plugin_get_fields 51 | func plugin_get_fields() *C.char { 52 | b, err := json.Marshal(&fields) 53 | if err != nil { 54 | return nil 55 | } 56 | buf.Write(string(b)) 57 | return (*C.char)(buf.CharPtr()) 58 | } 59 | -------------------------------------------------------------------------------- /pkg/sdk/symbols/extract/internal/asyncbench/async_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package asyncbench 19 | 20 | import ( 21 | "testing" 22 | ) 23 | 24 | func TestWorker(t *testing.T) { 25 | var lock int32 26 | startWorker(&lock) 27 | 28 | inData = testInput 29 | dataRequest(&lock) 30 | if outData != expectedOut { 31 | t.Fatalf(`dataRequest failed: expected "%d", got "%d"`, expectedOut, outData) 32 | } 33 | 34 | exitRequest(&lock) 35 | } 36 | 37 | func BenchmarkCall_Go(b *testing.B) { 38 | for i := 0; i < b.N; i++ { 39 | _ = doWork(testInput) 40 | } 41 | } 42 | 43 | func BenchmarkCall_C_to_Go(b *testing.B) { 44 | benchmark_sync(b.N, testInput) 45 | } 46 | 47 | func BenchmarkCall_Go_to_C(b *testing.B) { 48 | for i := 0; i < b.N; i++ { 49 | _ = do_work_c(testInput) 50 | } 51 | } 52 | 53 | func BenchmarkWorker_GoCaller(b *testing.B) { 54 | var lock int32 55 | startWorker(&lock) 56 | b.ResetTimer() 57 | for i := 0; i < b.N; i++ { 58 | dataRequest(&lock) 59 | } 60 | b.StopTimer() 61 | exitRequest(&lock) 62 | } 63 | 64 | func BenchmarkWorker_CCaller(b *testing.B) { 65 | var lock int32 66 | startWorker(&lock) 67 | b.ResetTimer() 68 | benchmark_async(&lock, b.N) 69 | b.StopTimer() 70 | exitRequest(&lock) 71 | } 72 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | **What type of PR is this?** 10 | 11 | > Uncomment one (or more) `/kind <>` lines: 12 | 13 | > /kind bug 14 | 15 | > /kind cleanup 16 | 17 | > /kind design 18 | 19 | > /kind documentation 20 | 21 | > /kind failing-test 22 | 23 | > /kind feature 24 | 25 | **Any specific area of the project related to this PR?** 26 | 27 | > Uncomment one (or more) `/area <>` lines: 28 | 29 | > /area build 30 | 31 | > /area plugin-sdk 32 | 33 | > /area tests 34 | 35 | 38 | 39 | **What this PR does / why we need it**: 40 | 41 | **Which issue(s) this PR fixes**: 42 | 43 | 48 | 49 | Fixes # 50 | 51 | **Special notes for your reviewer**: 52 | 53 | **Does this PR introduce a user-facing change?**: 54 | 55 | 62 | 63 | ```release-note 64 | 65 | ``` 66 | -------------------------------------------------------------------------------- /pkg/sdk/internal/hooks/hooks.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | // Package hooks contains a set of hooks to be used internally in the SDK. 19 | package hooks 20 | 21 | import ( 22 | "github.com/falcosecurity/plugin-sdk-go/pkg/cgo" 23 | ) 24 | 25 | type OnBeforeDestroyFn func(handle cgo.Handle) 26 | type OnAfterInitFn func(handle cgo.Handle) 27 | 28 | var ( 29 | onBeforeDestroy OnBeforeDestroyFn = func(cgo.Handle) {} 30 | onAfterInit OnAfterInitFn = func(cgo.Handle) {} 31 | ) 32 | 33 | // SetOnBeforeDestroy sets a callback that is invoked before the Destroy() method. 34 | func SetOnBeforeDestroy(fn OnBeforeDestroyFn) { 35 | if fn == nil { 36 | panic("plugin-sdk-go/sdk/symbols/initialize.SetOnBeforeDestroy: fn must not be nil") 37 | } 38 | onBeforeDestroy = fn 39 | } 40 | 41 | // OnBeforeDestroy returns a callback that is invoked before the Destroy() method. 42 | func OnBeforeDestroy() OnBeforeDestroyFn { 43 | return onBeforeDestroy 44 | } 45 | 46 | // SetOnAfterInit sets a callback that is invoked after the Init() method. 47 | func SetOnAfterInit(fn OnAfterInitFn) { 48 | if fn == nil { 49 | panic("plugin-sdk-go/sdk/symbols/initialize.SetOnAfterInit: fn must not be nil") 50 | } 51 | onAfterInit = fn 52 | } 53 | 54 | // OnAfterInit returns a callback that is invoked after the Init() method. 55 | func OnAfterInit() OnAfterInitFn { 56 | return onAfterInit 57 | } 58 | -------------------------------------------------------------------------------- /pkg/sdk/plugins/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | // Package plugins and its subpackages provide high-level constructs 19 | // to easily develop plugins, abstracting all the low-level 20 | // details of the plugin framework. This bundles the main plugin developer 21 | // tools provided by the SDK. Plugin developers are encouraged to use the 22 | // constructs of the plugins package for their plugins. 23 | // 24 | // This packages depends on the lower-level prebuilt C symbols implementations 25 | // of the sdk/symbols package. For some use cases, developers can consider 26 | // using the the sdk/symbols package to customize their usage of the SDK. 27 | // This is meaningful only if developers wish to manually write part of the 28 | // low-level C details of the plugin framework, but still want to use some 29 | // parts of the SDK. This is discouraged if not for advanced use cases only, 30 | // and developers are instead encouraged to rely on the plugins package to 31 | // build their plugins. 32 | // 33 | // Most of the time, a plugin author only needs to import the following packages, 34 | // which provide the "default" streamlined interfaces to implementing plugins: 35 | // "github.com/falcosecurity/plugin-sdk-go/pkg/sdk" 36 | // "github.com/falcosecurity/plugin-sdk-go/pkg/sdk/plugins" 37 | // "github.com/falcosecurity/plugin-sdk-go/pkg/sdk/plugins/{source,extractor}" 38 | // 39 | package plugins 40 | -------------------------------------------------------------------------------- /pkg/sdk/symbols/evtstr/evtstr.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | // This package exports the following C function: 19 | // - char* plugin_event_to_string(ss_plugin_t *s, const ss_plugin_event *evt) 20 | // 21 | // The exported plugin_event_to_string requires that s to be a handle 22 | // of cgo.Handle from this SDK. The value of the s handle must implement 23 | // the sdk.Stringer and sdk.StringerBuffer interfaces. 24 | // 25 | // This function is part of the plugin_api interface as defined in plugin_api.h. 26 | // In almost all cases, your plugin should import this module, 27 | // unless your plugin exports those symbols by other means. 28 | package evtstr 29 | 30 | /* 31 | #include "../../plugin_types.h" 32 | */ 33 | import "C" 34 | import ( 35 | "unsafe" 36 | 37 | "github.com/falcosecurity/plugin-sdk-go/pkg/cgo" 38 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk" 39 | ) 40 | 41 | //export plugin_event_to_string 42 | func plugin_event_to_string(pState C.uintptr_t, evt *C.ss_plugin_event_input) *C.char { 43 | buf := cgo.Handle(pState).Value().(sdk.StringerBuffer).StringerBuffer() 44 | stringer, ok := cgo.Handle(pState).Value().(sdk.Stringer) 45 | if ok { 46 | if str, err := stringer.String(sdk.NewEventReader(unsafe.Pointer(evt))); err == nil { 47 | buf.Write(str) 48 | } else { 49 | buf.Write(err.Error()) 50 | } 51 | } else { 52 | buf.Write("") 53 | } 54 | return (*C.char)(buf.CharPtr()) 55 | } 56 | -------------------------------------------------------------------------------- /pkg/sdk/symbols/initschema/initschema.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | // This package exports the following C function: 19 | // - const char* get_init_schema(ss_plugin_schema_type* schema_type) 20 | // 21 | // This function is part of the plugin_api interface as defined in plugin_api.h. 22 | // In almost all cases, your plugin should import this module, unless your 23 | // plugin exports those symbols by other means. 24 | package initschema 25 | 26 | /* 27 | #include "../../plugin_types.h" 28 | */ 29 | import "C" 30 | import ( 31 | "github.com/falcosecurity/plugin-sdk-go/pkg/ptr" 32 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk" 33 | ) 34 | 35 | var ( 36 | schema *sdk.SchemaInfo 37 | buf ptr.StringBuffer 38 | ) 39 | 40 | // SetInitSchema sets a pointer to sdk.SchemaInfo representing the schema 41 | // of the configuration expected by the plugin during initialization. 42 | func SetInitSchema(s *sdk.SchemaInfo) { 43 | schema = s 44 | } 45 | 46 | // InitSchema returns the pointer to sdk.SchemaInfo set with SetInitSchema(). 47 | func InitSchema() *sdk.SchemaInfo { 48 | return schema 49 | } 50 | 51 | //export plugin_get_init_schema 52 | func plugin_get_init_schema(schema_type *C.ss_plugin_schema_type) *C.char { 53 | str := "" 54 | *schema_type = C.SS_PLUGIN_SCHEMA_NONE 55 | if schema != nil { 56 | str = schema.Schema 57 | *schema_type = C.SS_PLUGIN_SCHEMA_JSON 58 | } 59 | buf.Write(str) 60 | return (*C.char)(buf.CharPtr()) 61 | } 62 | -------------------------------------------------------------------------------- /pkg/sdk/symbols/listopen/listopen.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | // This package exports the following C function: 19 | // - char* plugin_list_open_params() 20 | // 21 | // This function is part of the plugin_api interface as defined in plugin_api.h. 22 | // In almost all cases, your plugin should import this module, unless your 23 | // plugin exports those symbols by other means. 24 | package listopen 25 | 26 | /* 27 | #include 28 | */ 29 | import "C" 30 | import ( 31 | "encoding/json" 32 | 33 | "github.com/falcosecurity/plugin-sdk-go/pkg/cgo" 34 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk" 35 | ) 36 | 37 | //export plugin_list_open_params 38 | func plugin_list_open_params(pState C.uintptr_t, rc *int32) *C.char { 39 | *rc = sdk.SSPluginSuccess 40 | if openParams, ok := cgo.Handle(pState).Value().(sdk.OpenParams); ok { 41 | if buf, ok := cgo.Handle(pState).Value().(sdk.OpenParamsBuffer); ok { 42 | list, err := openParams.OpenParams() 43 | if err != nil { 44 | cgo.Handle(pState).Value().(sdk.LastError).SetLastError(err) 45 | *rc = sdk.SSPluginFailure 46 | return nil 47 | } 48 | b, err := json.Marshal(&list) 49 | if err != nil { 50 | cgo.Handle(pState).Value().(sdk.LastError).SetLastError(err) 51 | *rc = sdk.SSPluginFailure 52 | return nil 53 | } 54 | buf.OpenParamsBuffer().Write(string(b)) 55 | return (*C.char)(buf.OpenParamsBuffer().CharPtr()) 56 | } 57 | } 58 | return nil 59 | } 60 | -------------------------------------------------------------------------------- /pkg/sdk/symbols/progress/progress.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | // This package exports the following C function: 19 | // - char* plugin_get_progress(ss_plugin_t* s, ss_instance_t* h, uint32_t* progress_pct) 20 | // 21 | // The exported plugin_get_progress requires that both s and h are handles 22 | // of cgo.Handle from this SDK. The value of the s handle must implement 23 | // the sdk.PluginState interface. The value of the h handle must implement 24 | // the sdk.Progresser and sdk.ProgressBuffer interfaces. 25 | // 26 | // This function is part of the plugin_api interface as defined in plugin_api.h. 27 | // In almost all cases, your plugin should import this module, 28 | // unless your plugin exports those symbols by other means. 29 | package progress 30 | 31 | /* 32 | #include 33 | */ 34 | import "C" 35 | import ( 36 | "github.com/falcosecurity/plugin-sdk-go/pkg/cgo" 37 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk" 38 | ) 39 | 40 | //export plugin_get_progress 41 | func plugin_get_progress(pState C.uintptr_t, iState C.uintptr_t, progress_pct *uint32) *C.char { 42 | buf := cgo.Handle(iState).Value().(sdk.ProgressBuffer).ProgressBuffer() 43 | progresser, ok := cgo.Handle(iState).Value().(sdk.Progresser) 44 | if ok { 45 | pct, str := progresser.Progress(cgo.Handle(pState).Value().(sdk.PluginState)) 46 | *progress_pct = uint32(pct * 10000) 47 | buf.Write(str) 48 | return (*C.char)(buf.CharPtr()) 49 | } 50 | *progress_pct = 0 51 | return (*C.char)(nil) 52 | } 53 | -------------------------------------------------------------------------------- /benchmarks/async/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 5 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 6 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 7 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 8 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 9 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 10 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 11 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 12 | github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= 13 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 14 | github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= 15 | github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= 16 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= 17 | github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= 18 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 19 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 20 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 21 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 22 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 23 | -------------------------------------------------------------------------------- /examples/custom/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 5 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 6 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 7 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 8 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 9 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 10 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 11 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 12 | github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= 13 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 14 | github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= 15 | github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= 16 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= 17 | github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= 18 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 19 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 20 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 21 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 22 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 23 | -------------------------------------------------------------------------------- /examples/source/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 5 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 6 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 7 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 8 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 9 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 10 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 11 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 12 | github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= 13 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 14 | github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= 15 | github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= 16 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= 17 | github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= 18 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 19 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 20 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 21 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 22 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 23 | -------------------------------------------------------------------------------- /pkg/sdk/plugins/extractor/extractor_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package extractor 19 | 20 | import ( 21 | "fmt" 22 | "testing" 23 | 24 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk" 25 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk/plugins" 26 | ) 27 | 28 | type testPlugin struct { 29 | plugins.BasePlugin 30 | } 31 | 32 | func (m *testPlugin) Info() *plugins.Info { 33 | return &plugins.Info{ 34 | ID: 999, 35 | Name: "test", 36 | Description: "Extractor Test", 37 | Contact: "", 38 | Version: "", 39 | RequiredAPIVersion: "", 40 | ExtractEventSources: []string{"test"}, 41 | } 42 | } 43 | 44 | func (m *testPlugin) Init(config string) error { 45 | return nil 46 | } 47 | 48 | func (m *testPlugin) Fields() []sdk.FieldEntry { 49 | return []sdk.FieldEntry{ 50 | {Type: "uint64", Name: "test.field", Display: "Test Field", Desc: "Test Field"}, 51 | } 52 | } 53 | 54 | func (m *testPlugin) Extract(req sdk.ExtractRequest, evt sdk.EventReader) error { 55 | switch req.FieldID() { 56 | case 0: 57 | req.SetValue(uint64(0)) 58 | if req.WantOffset() { 59 | req.SetValueOffset(sdk.PluginEventPayloadOffset, 8) 60 | } 61 | return nil 62 | default: 63 | return fmt.Errorf("unsupported field: %s", req.Field()) 64 | } 65 | } 66 | 67 | func assertPanic(t *testing.T, fun func()) { 68 | defer func() { 69 | if r := recover(); r == nil { 70 | t.Errorf("expected panic") 71 | } 72 | }() 73 | fun() 74 | } 75 | -------------------------------------------------------------------------------- /examples/extractor/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 5 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 6 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 7 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 8 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 9 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 10 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 11 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 12 | github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= 13 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 14 | github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= 15 | github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= 16 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= 17 | github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= 18 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 19 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 20 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 21 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 22 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 23 | -------------------------------------------------------------------------------- /pkg/sdk/symbols/initschema/initschema_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package initschema 19 | 20 | import ( 21 | "testing" 22 | "unsafe" 23 | 24 | "github.com/falcosecurity/plugin-sdk-go/pkg/ptr" 25 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk" 26 | ) 27 | 28 | var sampleSchema = &sdk.SchemaInfo{ 29 | Schema: "", 30 | } 31 | 32 | func TestInitSchema(t *testing.T) { 33 | var schemaType _Ctype_ss_plugin_schema_type 34 | var schemaStr *_Ctype_char 35 | 36 | // Test with nil schema 37 | if InitSchema() != nil { 38 | t.Errorf("expected nil") 39 | } 40 | schemaStr = plugin_get_init_schema(&schemaType) 41 | if len(ptr.GoString(unsafe.Pointer(schemaStr))) > 0 { 42 | t.Errorf("expected empty string, but found %s", ptr.GoString(unsafe.Pointer(schemaStr))) 43 | } 44 | if schemaType != _Ciconst_SS_PLUGIN_SCHEMA_NONE { 45 | t.Errorf("expected %d, but found %d", _Ciconst_SS_PLUGIN_SCHEMA_NONE, schemaType) 46 | } 47 | 48 | // Test with non-nil schema 49 | SetInitSchema(sampleSchema) 50 | if InitSchema() != sampleSchema { 51 | t.Errorf("expected %p, but found %p", InitSchema(), sampleSchema) 52 | } 53 | schemaStr = plugin_get_init_schema(&schemaType) 54 | if ptr.GoString(unsafe.Pointer(schemaStr)) != sampleSchema.Schema { 55 | t.Errorf("expected %s, but found %s", sampleSchema.Schema, ptr.GoString(unsafe.Pointer(schemaStr))) 56 | } 57 | if schemaType != _Ciconst_SS_PLUGIN_SCHEMA_JSON { 58 | t.Errorf("expected %d, but found %d", _Ciconst_SS_PLUGIN_SCHEMA_JSON, schemaType) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /pkg/sdk/symbols/lasterr/lasterr.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | // This package exports the following C function: 19 | // - char* plugin_get_last_error(ss_plugin_t* s) 20 | // 21 | // The exported plugin_get_last_error requires s to be a handle 22 | // of cgo.Handle from this SDK. The value of the s handle must implement 23 | // the sdk.LastErrorBuffer interface, and either the error or the sdk.LastError 24 | // interfaces. 25 | // 26 | // This function is part of the plugin_api interface as defined in plugin_api.h. 27 | // In almost all cases, your plugin should import this module, unless your 28 | // plugin exports those symbols by other means. 29 | package lasterr 30 | 31 | /* 32 | #include // for uintptr_t 33 | */ 34 | import "C" 35 | import ( 36 | "errors" 37 | 38 | "github.com/falcosecurity/plugin-sdk-go/pkg/cgo" 39 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk" 40 | ) 41 | 42 | var ( 43 | errNoErrorInterface = errors.New(`cannot get error message: plugin instance does not implement "error" interface`) 44 | ) 45 | 46 | //export plugin_get_last_error 47 | func plugin_get_last_error(pInstance C.uintptr_t) *C.char { 48 | buf := cgo.Handle(pInstance).Value().(sdk.LastErrorBuffer).LastErrorBuffer() 49 | err, ok := cgo.Handle(pInstance).Value().(error) 50 | if !ok { 51 | lastErr, ok := cgo.Handle(pInstance).Value().(sdk.LastError) 52 | if !ok { 53 | err = errNoErrorInterface 54 | } else { 55 | err = lastErr.LastError() 56 | } 57 | } 58 | buf.Write(err.Error()) 59 | return (*C.char)(buf.CharPtr()) 60 | } 61 | -------------------------------------------------------------------------------- /benchmarks/async/README.md: -------------------------------------------------------------------------------- 1 | # Async Extraction Benchmark 2 | 3 | This little program is a benchmarking tool to measure the performance of the async extraction optimization (see: [pkg/sdk/symbols/extract](https://github.com/falcosecurity/plugin-sdk-go/tree/main/pkg/sdk/symbols/extract)). 4 | 5 | ### Usage 6 | 7 | ``` 8 | Usage: bench [options] 9 | Options: 10 | -h, --help Print this usage snippet. 11 | -a, --async Run the benchmark by enabling the async extraction optimization (default: off). 12 | -n The number of extraction requests performed in the benchmark (default: 10000). 13 | -p The number of plugins that run the benchmark in parallel (default: 1). 14 | ``` 15 | 16 | ### Example 17 | ``` 18 | > ./build/bench -n 100000 -a 19 | plugin 1: 251.21 ns/extraction (elapsed time 25121098ns, extractions 100000) 20 | ``` 21 | 22 | ### Description 23 | 24 | The benchmark is implemented in C language, whereas the extraction function is implemented in Go by using the Plugin SDK Go. This is achieved by implementing a mock plugin using the SDK, then building it in `c-archive` mode, and then linking the resulting binary with the C code. The end result is a C executable that is able to call the symbols of the C plugin API, such as `plugin_init` and `plugin_extract_fields` (which are the ones we need to perform the benchmark in this case). 25 | 26 | The goal here is to have a real use case estimation of how costly the C -> Go function calls are when the async worker optimization is enabled or disabled. This can't be achieved with the Go benchmarking tools, because the way the Go runtime behaves when built as `c-archive` and `c-shared` might influence the performance results. You can find a Go benchmark for this in https://github.com/falcosecurity/plugin-sdk-go/tree/main/pkg/sdk/symbols/extract/internal/asyncbench. 27 | 28 | **NOTE**: this allows running multiple benchmarks in parallel by using the same shared Go code. This is unsafe with the current async extraction implementation, because it assumes a single-caller-single-worker execution model. However, this feature might become useful in the future one we support parallelized plugin code execution (see point **(B3)** of https://github.com/falcosecurity/falco/issues/2074). -------------------------------------------------------------------------------- /pkg/sdk/symbols/lasterr/lasterr_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package lasterr 19 | 20 | import ( 21 | "fmt" 22 | "testing" 23 | "unsafe" 24 | 25 | "github.com/falcosecurity/plugin-sdk-go/pkg/cgo" 26 | "github.com/falcosecurity/plugin-sdk-go/pkg/ptr" 27 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk" 28 | ) 29 | 30 | var errTest = fmt.Errorf("test") 31 | 32 | type sampleLastErr struct { 33 | lastErrBuf ptr.StringBuffer 34 | lastErr error 35 | } 36 | 37 | func (s *sampleLastErr) LastError() error { 38 | return s.lastErr 39 | } 40 | 41 | func (s *sampleLastErr) SetLastError(err error) { 42 | s.lastErr = err 43 | } 44 | 45 | func (s *sampleLastErr) LastErrorBuffer() sdk.StringBuffer { 46 | return &s.lastErrBuf 47 | } 48 | 49 | func assertPanic(t *testing.T, fun func()) { 50 | defer func() { 51 | if r := recover(); r == nil { 52 | t.Errorf("expected panic") 53 | } 54 | }() 55 | fun() 56 | } 57 | 58 | func TestLastErr(t *testing.T) { 59 | sample := &sampleLastErr{} 60 | defer sample.LastErrorBuffer().Free() 61 | sample.SetLastError(errTest) 62 | handle := cgo.NewHandle(sample) 63 | defer handle.Delete() 64 | 65 | cStr := plugin_get_last_error(_Ctype_uintptr_t(handle)) 66 | errStr := ptr.GoString(unsafe.Pointer(cStr)) 67 | 68 | if errTest.Error() != errStr { 69 | t.Fatalf("expected %s, but found %s", errTest.Error(), errStr) 70 | } 71 | } 72 | 73 | func TestLastErrPanic(t *testing.T) { 74 | handle := cgo.NewHandle(int64(0)) 75 | defer handle.Delete() 76 | assertPanic(t, func() { 77 | plugin_get_last_error(_Ctype_uintptr_t(handle)) 78 | }) 79 | } 80 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # Copyright (C) 2025 The Falco Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 6 | # the License. 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 distributed under the License is distributed on an 11 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | # specific language governing permissions and limitations under the License. 13 | # 14 | SHELL := /bin/bash 15 | GO ?= $(shell which go) 16 | CURL ?= $(shell which curl) 17 | PATCH ?= $(shell which patch) 18 | 19 | FALCOSECURITY_LIBS_REVISION ?= 0.21.0 20 | FALCOSECURITY_LIBS_REPO ?= falcosecurity/libs 21 | PLUGINLIB_URL=https://raw.githubusercontent.com/${FALCOSECURITY_LIBS_REPO}/${FALCOSECURITY_LIBS_REVISION}/userspace/plugin 22 | 23 | examples_dir = $(shell ls -d examples/*/ | cut -f2 -d'/' | xargs) 24 | examples_build = $(addprefix example-,$(examples_dir)) 25 | examples_clean = $(addprefix clean-example-,$(examples_dir)) 26 | 27 | .PHONY: all 28 | all: pluginlib examples 29 | 30 | .PHONY: clean 31 | clean: clean-pluginlib $(examples_clean) 32 | 33 | .PHONY: pluginlib 34 | pluginlib: 35 | $(CURL) -Lso pkg/sdk/plugin_types.h $(PLUGINLIB_URL)/plugin_types.h 36 | $(CURL) -Lso pkg/sdk/plugin_api.h $(PLUGINLIB_URL)/plugin_api.h 37 | $(CURL) -Lso pkg/loader/plugin_loader.h $(PLUGINLIB_URL)/plugin_loader.h 38 | $(CURL) -Lso pkg/loader/plugin_loader.c $(PLUGINLIB_URL)/plugin_loader.c 39 | $(PATCH) -p1 < pkg/loader/plugin_api_include.patch 40 | $(PATCH) -p1 < pkg/loader/strlcpy.patch 41 | $(PATCH) -p1 < pkg/loader/strlcat.patch 42 | $(PATCH) -p1 < pkg/sdk/plugin_types_include.patch 43 | 44 | clean-pluginlib: 45 | rm -f \ 46 | pkg/sdk/plugin_types.h \ 47 | pkg/sdk/plugin_api.h \ 48 | pkg/loader/plugin_loader.h \ 49 | pkg/loader/plugin_loader.c 50 | 51 | .PHONY: test 52 | test: 53 | @$(GO) test ./... -cover 54 | 55 | .PHONY: examples 56 | examples: $(examples_build) 57 | 58 | example-%: 59 | @cd examples/$* && make 60 | 61 | clean-example-%: 62 | @cd examples/$* && make clean 63 | -------------------------------------------------------------------------------- /benchmarks/async/bench.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | // This is not a real plugin. The goal of this is to implement few 19 | // plugin_* API symbols by leveraging the SDK Go, so that we can use 20 | // the to execute the benchmark. This code is compiled as a c-archive 21 | // and linked into a C executable so that each symbol will be a C -> Go call, 22 | // thus simulating what really happens in the plugin framework. 23 | package main 24 | 25 | import ( 26 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk" 27 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk/plugins" 28 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk/plugins/extractor" 29 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk/symbols/extract" 30 | ) 31 | 32 | type MockPlugin struct { 33 | plugins.BasePlugin 34 | } 35 | 36 | func init() { 37 | plugins.SetFactory(func() plugins.Plugin { 38 | p := &MockPlugin{} 39 | extractor.Register(p) 40 | return p 41 | }) 42 | } 43 | 44 | func (m *MockPlugin) Info() *plugins.Info { 45 | return &plugins.Info{} 46 | } 47 | 48 | func (m *MockPlugin) Fields() []sdk.FieldEntry { 49 | return []sdk.FieldEntry{} 50 | } 51 | 52 | // note: we enable/disable the async extraction optimization depending on the 53 | // passed-in config 54 | func (m *MockPlugin) Init(config string) error { 55 | extract.SetAsync(config == "async") 56 | return nil 57 | } 58 | 59 | // note: we do nothing here, we're just interested in measuring the cost of 60 | // calling this function from C 61 | func (m *MockPlugin) Extract(req sdk.ExtractRequest, evt sdk.EventReader) error { 62 | return nil 63 | } 64 | 65 | // not used but required to build this as a c-archive 66 | func main() {} 67 | -------------------------------------------------------------------------------- /pkg/sdk/symbols/nextbatch/nextbatch.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | // This package exports the following C function: 19 | // - ss_plugin_rc plugin_next_batch(ss_plugin_t* s, ss_instance_t* h, uint32_t *nevts, ss_plugin_event ***evts) 20 | // 21 | // The exported plugin_next_batch requires s and h to be a handles 22 | // of cgo.Handle from this SDK. The value of the s handle must implement 23 | // the sdk.PluginState interface. The value of the h handle must implement 24 | // the sdk.Events and the sdk.NextBatcher interfaces. 25 | // 26 | // This function is part of the plugin_api interface as defined in plugin_api.h. 27 | // In almost all cases, your plugin should import this module, unless your 28 | // plugin exports those symbols by other means. 29 | package nextbatch 30 | 31 | /* 32 | #include "../../plugin_types.h" 33 | */ 34 | import "C" 35 | import ( 36 | "github.com/falcosecurity/plugin-sdk-go/pkg/cgo" 37 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk" 38 | ) 39 | 40 | //export plugin_next_batch 41 | func plugin_next_batch(pState C.uintptr_t, iState C.uintptr_t, nevts *uint32, retEvts ***C.ss_plugin_event) int32 { 42 | events := cgo.Handle(iState).Value().(sdk.Events).Events() 43 | n, err := cgo.Handle(iState).Value().(sdk.NextBatcher).NextBatch(cgo.Handle(pState).Value().(sdk.PluginState), events) 44 | 45 | *nevts = uint32(n) 46 | *retEvts = (**C.ss_plugin_event)(events.ArrayPtr()) 47 | switch err { 48 | case nil: 49 | return sdk.SSPluginSuccess 50 | case sdk.ErrEOF: 51 | return sdk.SSPluginEOF 52 | case sdk.ErrTimeout: 53 | return sdk.SSPluginTimeout 54 | default: 55 | *nevts = uint32(0) 56 | *retEvts = nil 57 | cgo.Handle(pState).Value().(sdk.LastError).SetLastError(err) 58 | return sdk.SSPluginFailure 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /pkg/loader/strlcat.patch: -------------------------------------------------------------------------------- 1 | diff --git a/pkg/loader/plugin_loader.c b/pkg/loader/plugin_loader.c 2 | index 3aea70c..a68327a 100644 3 | --- a/pkg/loader/plugin_loader.c 4 | +++ b/pkg/loader/plugin_loader.c 5 | @@ -33,6 +33,7 @@ typedef void* library_handle_t; 6 | // note(jasondellaluce,therealbobo): implementation taken from falcosecurity/libs 7 | // note(leogr): to avoid clashing with `strlcpy` introduced by glibc 2.38, 8 | // the func has been renamed to plugin_loader_strlcpy. 9 | +// The same applies to `strlcat`. 10 | // N.B.: our building system here is not smart enough to detect if the function 11 | // was declared already. 12 | #include 13 | @@ -61,6 +62,37 @@ static inline size_t plugin_loader_strlcpy(char *dst, const char *src, size_t si 14 | return srcsize; 15 | } 16 | 17 | +/*! 18 | + \brief Append the NUL-terminated string src to the end of dst. It will append at most size − 19 | + strlen(dst) − 1 bytes, NUL-terminating the result. 20 | + 21 | + \return The initial length of dst plus the length of src 22 | +*/ 23 | + 24 | +static inline size_t plugin_loader_strlcat(char *dst, const char *src, size_t size) { 25 | + size_t srcsize = strlen(src); 26 | + size_t dstsize = strlen(dst); 27 | + 28 | + if(dstsize >= size) { 29 | + return size; 30 | + } 31 | + 32 | + if(srcsize == 0) { 33 | + return dstsize; 34 | + } 35 | + 36 | + size_t totalsize = srcsize + dstsize; 37 | + if(totalsize > size - 1) { 38 | + totalsize = size - 1; 39 | + } 40 | + 41 | + size_t copysize = totalsize - dstsize; 42 | + memcpy(dst + dstsize, src, copysize); 43 | + dst[totalsize] = '\0'; 44 | + 45 | + return dstsize + srcsize; 46 | +} 47 | + 48 | static inline void err_prepend(char* s, const char* prefix, const char* sep) { 49 | char tmp[PLUGIN_MAX_ERRLEN]; 50 | size_t prefix_len = plugin_loader_strlcpy(tmp, prefix, PLUGIN_MAX_ERRLEN); 51 | @@ -74,9 +106,9 @@ static inline void err_prepend(char* s, const char* prefix, const char* sep) { 52 | 53 | static inline void err_append(char* s, const char* suffix, const char* sep) { 54 | if(*s != '\0') { 55 | - strlcat(s, sep, PLUGIN_MAX_ERRLEN); 56 | + plugin_loader_strlcat(s, sep, PLUGIN_MAX_ERRLEN); 57 | } 58 | - strlcat(s, suffix, PLUGIN_MAX_ERRLEN); 59 | + plugin_loader_strlcat(s, suffix, PLUGIN_MAX_ERRLEN); 60 | } 61 | 62 | static void* getsym(library_handle_t handle, const char* name) { 63 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 5 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 6 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 7 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 8 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 9 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 10 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 11 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 12 | github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= 13 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 14 | github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= 15 | github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= 16 | github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= 17 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= 18 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= 19 | github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= 20 | github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= 21 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 22 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 23 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 24 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 25 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 26 | -------------------------------------------------------------------------------- /pkg/sdk/symbols/extract/internal/asyncbench/async.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package asyncbench 19 | 20 | import ( 21 | "sync/atomic" 22 | ) 23 | 24 | /* 25 | #include "bench.h" 26 | */ 27 | import "C" 28 | 29 | func benchmark_async(lock *int32, n int) { 30 | C.async_benchmark((*C.int32_t)(lock), C.int(n)) 31 | } 32 | 33 | func benchmark_sync(n int, input int) { 34 | C.sync_benchmark(C.int(n), C.int(input)) 35 | } 36 | 37 | const ( 38 | state_wait = iota 39 | state_data_req 40 | state_exit_req 41 | state_exit_ack 42 | ) 43 | 44 | func startWorker(lock *int32) { 45 | go func() { 46 | for { 47 | // Check for incoming request, if any, otherwise busy waits 48 | switch atomic.LoadInt32(lock) { 49 | 50 | case state_data_req: 51 | // Incoming data request. Process it... 52 | outData = doWork(inData) 53 | // Processing done, return back to waiting state 54 | atomic.StoreInt32(lock, state_wait) 55 | 56 | case state_exit_req: 57 | // Incoming exit request. Send ack and exit. 58 | atomic.StoreInt32(lock, state_exit_ack) 59 | return 60 | 61 | default: 62 | // busy wait 63 | } 64 | } 65 | }() 66 | } 67 | 68 | func dataRequest(lock *int32) { 69 | // We assume state_wait because no concurrent requests are supported 70 | for !atomic.CompareAndSwapInt32(lock, state_wait, state_data_req) { 71 | // spin 72 | } 73 | 74 | // state_data_req acquired, wait for worker completation 75 | for atomic.LoadInt32(lock) != state_wait { 76 | // spin 77 | } 78 | } 79 | 80 | func exitRequest(lock *int32) { 81 | // We assume state_wait because no concurrent requests are supported 82 | for !atomic.CompareAndSwapInt32(lock, state_wait, state_exit_req) { 83 | // spin 84 | } 85 | 86 | // state_exit_req acquired, wait for worker exiting 87 | for atomic.LoadInt32(lock) != state_exit_ack { 88 | // spin 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /pkg/sdk/plugins/plugins_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package plugins 19 | 20 | import ( 21 | "errors" 22 | "testing" 23 | 24 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk" 25 | ) 26 | 27 | func TestBaseEvents(t *testing.T) { 28 | b := BaseEvents{} 29 | value, err := sdk.NewEventWriters(10, 10) 30 | if err != nil { 31 | t.Error(err) 32 | } 33 | 34 | b.SetEvents(value) 35 | if b.Events() != value { 36 | t.Errorf("Events: value does not match") 37 | } 38 | value.Free() 39 | } 40 | 41 | func TestBaseExtractRequests(t *testing.T) { 42 | b := BaseExtractRequests{} 43 | value := sdk.NewExtractRequestPool() 44 | 45 | b.SetExtractRequests(value) 46 | if b.ExtractRequests() != value { 47 | t.Errorf("ExtractRequests: value does not match") 48 | } 49 | value.Free() 50 | } 51 | 52 | func TestBaseLastError(t *testing.T) { 53 | b := BaseLastError{} 54 | str := "test error" 55 | value := errors.New(str) 56 | 57 | b.SetLastError(value) 58 | if b.LastError() != value { 59 | t.Errorf("LastError: value does not match") 60 | } 61 | 62 | b.LastErrorBuffer().Write(str) 63 | if b.LastErrorBuffer().String() != str { 64 | t.Errorf("LastErrorBuffer: expected %s, but found %s", str, b.LastErrorBuffer().String()) 65 | } 66 | b.LastErrorBuffer().Free() 67 | } 68 | 69 | func TestBaseStringer(t *testing.T) { 70 | b := BaseStringer{} 71 | str := "test" 72 | 73 | b.StringerBuffer().Write(str) 74 | if b.StringerBuffer().String() != str { 75 | t.Errorf("StringerBuffer: expected %s, but found %s", str, b.StringerBuffer().String()) 76 | } 77 | b.StringerBuffer().Free() 78 | } 79 | 80 | func TestBaseProgress(t *testing.T) { 81 | b := BaseProgress{} 82 | str := "test" 83 | 84 | b.ProgressBuffer().Write(str) 85 | if b.ProgressBuffer().String() != str { 86 | t.Errorf("ProgressBuffer: expected %s, but found %s", str, b.ProgressBuffer().String()) 87 | } 88 | b.ProgressBuffer().Free() 89 | } 90 | -------------------------------------------------------------------------------- /pkg/ptr/string_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package ptr 19 | 20 | import ( 21 | "bytes" 22 | "reflect" 23 | "testing" 24 | "unsafe" 25 | ) 26 | 27 | const ( 28 | testString = "hello poiana" 29 | ) 30 | 31 | func TestGoStringPointer(t *testing.T) { 32 | // Allocate a buffer and encode a C-like string into it 33 | bytes := bytes.NewBufferString(testString + " ").Bytes() 34 | bytes[len(bytes)-1] = cStringNullTerminator 35 | 36 | // Use the sdk GoString to create a Go-friently string 37 | // view of the buffer above 38 | bytesPtr := unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(&bytes)).Data) 39 | str := GoString(bytesPtr) 40 | if len(str) != len(testString) || str != testString { 41 | t.Errorf("str=%s, len=%d", str, len(str)) 42 | } 43 | 44 | // Editing buffer should make the string change too, 45 | // because they point to the same memory location 46 | editPos := 0 47 | editByte := byte('X') 48 | bytes[editPos] = editByte 49 | if len(str) != len(testString) || str == testString || str[editPos] != editByte { 50 | t.Errorf("str=%s, len=%d", str, len(str)) 51 | } 52 | } 53 | 54 | func TestGoStringNull(t *testing.T) { 55 | str := GoString(nil) 56 | if len(str) > 0 { 57 | t.Errorf("expected empty string") 58 | } 59 | } 60 | 61 | func TestStringBuffer(t *testing.T) { 62 | str := "hello" 63 | buf := &StringBuffer{} 64 | 65 | if buf.CharPtr() != nil { 66 | t.Errorf("expected nil char pointer") 67 | } 68 | if len(buf.String()) > 0 { 69 | t.Errorf("expected empty string") 70 | } 71 | 72 | buf.Write(str) 73 | if buf.CharPtr() == nil { 74 | t.Errorf("expected non-nil char pointer") 75 | } 76 | if buf.String() != str { 77 | t.Errorf("string does not match: %s expected, but %s found", str, buf.String()) 78 | } 79 | 80 | // test reallocation 81 | str = str + " world" 82 | buf.Write(str) 83 | if buf.String() != str { 84 | t.Errorf("string does not match: %s expected, but %s found", str, buf.String()) 85 | } 86 | 87 | buf.Free() 88 | } 89 | -------------------------------------------------------------------------------- /pkg/sdk/symbols/progress/progress_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package progress 19 | 20 | import ( 21 | "fmt" 22 | "testing" 23 | "unsafe" 24 | 25 | "github.com/falcosecurity/plugin-sdk-go/pkg/cgo" 26 | "github.com/falcosecurity/plugin-sdk-go/pkg/ptr" 27 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk" 28 | ) 29 | 30 | var testPct = float64(0.45) 31 | 32 | type sampleProgress struct { 33 | strBuf ptr.StringBuffer 34 | pct float64 35 | } 36 | 37 | func (s *sampleProgress) ProgressBuffer() sdk.StringBuffer { 38 | return &s.strBuf 39 | } 40 | 41 | func formatPercent(pct float64) string { 42 | return fmt.Sprintf("%.2f", pct*100) 43 | } 44 | 45 | func (s *sampleProgress) Progress(pState sdk.PluginState) (float64, string) { 46 | return s.pct, formatPercent(s.pct) 47 | } 48 | 49 | func assertPanic(t *testing.T, fun func()) { 50 | defer func() { 51 | if r := recover(); r == nil { 52 | t.Errorf("expected panic") 53 | } 54 | }() 55 | fun() 56 | } 57 | 58 | func TestProgress(t *testing.T) { 59 | var resPct uint32 60 | var resStr string 61 | var expectedStr string 62 | 63 | sample := &sampleProgress{} 64 | defer sample.ProgressBuffer().Free() 65 | pHandle := cgo.NewHandle(1) 66 | iHandle := cgo.NewHandle(sample) 67 | defer pHandle.Delete() 68 | defer iHandle.Delete() 69 | 70 | // test success 71 | sample.pct = testPct 72 | expectedStr = formatPercent(testPct) 73 | resStr = ptr.GoString(unsafe.Pointer(plugin_get_progress(_Ctype_uintptr_t(pHandle), _Ctype_uintptr_t(iHandle), &resPct))) 74 | if resStr != expectedStr { 75 | t.Errorf("expected %s, but found %s", expectedStr, resStr) 76 | } 77 | if resPct != uint32(testPct*10000) { 78 | t.Errorf("expected %d, but found %d", uint32(testPct*10000), resPct) 79 | } 80 | } 81 | 82 | func TestProgressPanic(t *testing.T) { 83 | var resPct uint32 84 | handle := cgo.NewHandle(int64(0)) 85 | defer handle.Delete() 86 | assertPanic(t, func() { 87 | plugin_get_progress(_Ctype_uintptr_t(handle), _Ctype_uintptr_t(handle), &resPct) 88 | }) 89 | } 90 | -------------------------------------------------------------------------------- /examples/full/go.sum: -------------------------------------------------------------------------------- 1 | github.com/alecthomas/jsonschema v0.0.0-20220216202328-9eeeec9d044b h1:doCpXjVwui6HUN+xgNsNS3SZ0/jUZ68Eb+mJRNOZfog= 2 | github.com/alecthomas/jsonschema v0.0.0-20220216202328-9eeeec9d044b/go.mod h1:/n6+1/DWPltRLWL/VKyUxg6tzsl5kHUCcraimt4vr60= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 h1:i462o439ZjprVSFSZLZxcsoAe592sZB1rci2Z8j4wdk= 7 | github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA= 8 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 9 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 10 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 11 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 12 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 13 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 14 | github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 15 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 16 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 17 | github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= 18 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 19 | github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= 20 | github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= 21 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= 22 | github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= 23 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 24 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 25 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 26 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 27 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 28 | -------------------------------------------------------------------------------- /pkg/sdk/buffers.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package sdk 19 | 20 | import "unsafe" 21 | 22 | // StringBuffer represents a buffer for C-allocated null-terminated strings 23 | // in a Go-friendly way. Unlike the C.CString function, this interface helps 24 | // convert a Go string in a C-like one by always reusing the same buffer. 25 | // Implementations of this interface must take care of allocating the 26 | // underlying C buffer. 27 | type StringBuffer interface { 28 | // Write writes a Go string inside the buffer, converting it to a C-like 29 | // null-terminated string. Implementations of this interface must handle 30 | // the case in which the buffer is not large enough to host the converted 31 | // string. 32 | Write(string) 33 | // 34 | // String returns a Go string obtained by converting the C-like string 35 | // currently stored in the buffer. If the buffer is empty, an empty string 36 | // is returned. 37 | String() string 38 | // 39 | // CharPtr returns an unsafe pointer to the underlying C-allocated 40 | // char* buffer. Freeing the returned pointer by any sort of deallocator 41 | // (C.free or similars) can lead to undefined behavior. 42 | CharPtr() unsafe.Pointer 43 | // 44 | // Free deallocates the underlying C-allocated buffer. 45 | Free() 46 | } 47 | 48 | // LastErrorBuffer is an interface wrapping the basic LastErrorBuffer method. 49 | // LastErrorBuffer returns a StringBuffer meant to be used as buffer for 50 | // plugin_get_last_error(). 51 | type LastErrorBuffer interface { 52 | LastErrorBuffer() StringBuffer 53 | } 54 | 55 | // StringerBuffer is an interface wrapping the basic StringerBuffer method. 56 | // StringerBuffer returns a StringBuffer meant to be used as buffer for 57 | // plugin_event_to_string(). 58 | type StringerBuffer interface { 59 | StringerBuffer() StringBuffer 60 | } 61 | 62 | // ProgressBuffer is an interface wrapping the basic ProgressBuffer method. 63 | // ProgressBuffer returns a StringBuffer meant to be used as buffer for 64 | // plugin_get_progress(). 65 | type ProgressBuffer interface { 66 | ProgressBuffer() StringBuffer 67 | } 68 | 69 | // OpenParamsBuffer is an interface wrapping the basic OpenParamsBuffer method. 70 | // OpenParamsBuffer returns a StringBuffer meant to be used as buffer for 71 | // plugin_list_open_params(). 72 | type OpenParamsBuffer interface { 73 | OpenParamsBuffer() StringBuffer 74 | } 75 | -------------------------------------------------------------------------------- /pkg/sdk/symbols/extract/extract.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2025 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | // This package exports the following C function: 19 | // - ss_plugin_rc plugin_extract_fields(ss_plugin_t *s, const ss_plugin_event *evt, uint32_t num_fields, ss_plugin_extract_field *fields) 20 | // 21 | // The exported plugin_extract_fields requires s to be a handle 22 | // of cgo.Handle from this SDK. The value of the s handle must implement 23 | // the sdk.Extractor and sdk.ExtractRequests interfaces. 24 | // 25 | // This function is part of the plugin_api interface as defined in plugin_api.h. 26 | // In almost all cases, your plugin should import this module, unless your 27 | // plugin exports those symbols by other means. 28 | package extract 29 | 30 | /* 31 | #include 32 | #include "extract.h" 33 | */ 34 | import "C" 35 | import ( 36 | "unsafe" 37 | 38 | "github.com/falcosecurity/plugin-sdk-go/pkg/cgo" 39 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk" 40 | ) 41 | 42 | //export plugin_extract_fields_sync 43 | func plugin_extract_fields_sync(plgState C.uintptr_t, evt *C.ss_plugin_event_input, numFields uint32, fields *C.ss_plugin_extract_field, offsets *C.ss_plugin_extract_value_offsets) int32 { 44 | pHandle := cgo.Handle(plgState) 45 | extract := pHandle.Value().(sdk.Extractor) 46 | extrReqs := pHandle.Value().(sdk.ExtractRequests) 47 | 48 | // https://go.dev/wiki/cgo#turning-c-arrays-into-go-slices 49 | flds := (*[1 << 28]C.struct_ss_plugin_extract_field)(unsafe.Pointer(fields))[:numFields:numFields] 50 | var i uint32 51 | var extrReq sdk.ExtractRequest 52 | 53 | if offsets != nil { 54 | extrReqs.ExtractRequests().MakeOffsetArrayPtrs(unsafe.Pointer(offsets), numFields) 55 | } 56 | 57 | for i = 0; i < numFields; i++ { 58 | flds[i].res_len = (C.uint64_t)(0) 59 | extrReq = extrReqs.ExtractRequests().Get(int(flds[i].field_id)) 60 | extrReq.SetPtr(unsafe.Pointer(&flds[i])) 61 | 62 | if offsets == nil { 63 | extrReq.SetOffsetPtrs(nil, nil) 64 | } else { 65 | extrReq.SetOffsetPtrs( 66 | unsafe.Add(unsafe.Pointer(offsets.start), i*C.sizeof_uint32_t), 67 | unsafe.Add(unsafe.Pointer(offsets.length), i*C.sizeof_uint32_t), 68 | ) 69 | } 70 | 71 | err := extract.Extract(extrReq, sdk.NewEventReader(unsafe.Pointer(evt))) 72 | if err != nil { 73 | pHandle.Value().(sdk.LastError).SetLastError(err) 74 | return sdk.SSPluginFailure 75 | } 76 | } 77 | 78 | return sdk.SSPluginSuccess 79 | } 80 | -------------------------------------------------------------------------------- /pkg/sdk/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | // Package sdk provides definitions and constructs for developers that 19 | // would like to write Falcosecurity Plugins (https://falco.org/docs/plugins/) 20 | // in Go. 21 | // 22 | // Before using this package, review the developer's guide 23 | // (https://falco.org/docs/plugins/developers_guide/) which fully documents 24 | // the API and provides best practices for writing plugins. 25 | // The developer's guide includes a walkthrough 26 | // (https://falco.org/docs/plugins/developers_guide/#example-go-plugin-dummy) 27 | // of a plugin written in Go that uses this package. 28 | // 29 | // For a quick start, you can refer to the provided examples of plugins with 30 | // field extraction capability 31 | // (https://github.com/falcosecurity/plugin-sdk-go/tree/main/examples/extractor), 32 | // with event sourcing capability, 33 | // (https://github.com/falcosecurity/plugin-sdk-go/tree/main/examples/source), 34 | // and with both 35 | // (https://github.com/falcosecurity/plugin-sdk-go/tree/main/examples/full). 36 | // 37 | // This SDK is designed to be layered with different levels of abstraction: 38 | // 39 | // 1. The "sdk/plugins" package provide high-level constructs to easily develop 40 | // plugins in a Go-friendly way, by abstracting all the low-level details 41 | // of the plugin framework and by avoiding the need of useless boilerplate code. 42 | // This package is the way to go for developers to start building plugins. 43 | // 44 | // 2. The "sdk/symbols" package provide prebuilt implementations for all the C 45 | // symbols that need to be exported by the plugin in order to be accepted by the 46 | // framework. The prebuilt symbols handle all the complexity of bridging the C 47 | // symbols and the Go runtime. Each subpackage is not internal, and can be used 48 | // by advanced plugin developers to achieve a custom usage of the SDK symbols. 49 | // This option is a strongly discouraged, as plugins must generally be 50 | // developed using the more high-level constructs of the "sdk/plugins" package. 51 | // This package is also used internally, and may be subject to more frequent 52 | // breaking changes. 53 | // 54 | // 3. The "sdk" package provides basic definitions and constructs necessary to develop 55 | // plugins. The SDK describes the behavior of plugins as a set of minimal and 56 | // composable interfaces, to be used flexibly in other packages. 57 | // 58 | package sdk 59 | -------------------------------------------------------------------------------- /pkg/sdk/plugins/extractor/extractor.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | // Package extractor provides high-level constructs to easily build 19 | // plugins with field extraction capability. 20 | package extractor 21 | 22 | import ( 23 | "github.com/falcosecurity/plugin-sdk-go/pkg/cgo" 24 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk" 25 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk/internal/hooks" 26 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk/plugins" 27 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk/symbols/extract" 28 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk/symbols/fields" 29 | _ "github.com/falcosecurity/plugin-sdk-go/pkg/sdk/symbols/lasterr" 30 | ) 31 | 32 | // Plugin is an interface representing a plugin with field extraction capability. 33 | type Plugin interface { 34 | plugins.Plugin 35 | sdk.Extractor 36 | sdk.ExtractRequests 37 | // 38 | // Fields return the list of extractor fields exported by this plugin. 39 | Fields() []sdk.FieldEntry 40 | } 41 | 42 | // note: here the plugin Init method might have 43 | // called extract.SetAsync, which influences whether the async optimization 44 | // will be actually used or not. Potentially, extract.SetAsync might be 45 | // invoked with different boolean values at subsequent plugin initializations, 46 | // however this code is still safe since: 47 | // - Init methods are called in sequence and not concurrently. As such, 48 | // every plugin invoking extract.SetAsync influences behavior of 49 | // extract.StartAsync only for itself. 50 | // - The hooks.SetOnBeforeDestroy callback is idempotent, because 51 | // extract.StopAsync works without having context on whether the current 52 | // plugin actually used the async optimization. Hence, extract.StopAsync 53 | // is not influenced by the invocations of extract.SetAsync. 54 | func enableAsync(handle cgo.Handle) { 55 | extract.StartAsync(handle) 56 | hooks.SetOnBeforeDestroy(func(handle cgo.Handle) { 57 | extract.StopAsync(handle) 58 | }) 59 | } 60 | 61 | // Register registers the field extraction capability in the framework for the given Plugin. 62 | // 63 | // This function should be called from the provided plugins.FactoryFunc implementation. 64 | // See the parent package for more detail. This function is idempotent. 65 | func Register(p Plugin) { 66 | 67 | fields.SetFields(p.Fields()) 68 | 69 | // setup hooks for automatically start/stop async extraction 70 | hooks.SetOnAfterInit(enableAsync) 71 | } 72 | -------------------------------------------------------------------------------- /pkg/sdk/symbols/evtstr/evtstr_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package evtstr 19 | 20 | import ( 21 | "bytes" 22 | "errors" 23 | "io/ioutil" 24 | "testing" 25 | "time" 26 | "unsafe" 27 | 28 | "github.com/falcosecurity/plugin-sdk-go/pkg/cgo" 29 | "github.com/falcosecurity/plugin-sdk-go/pkg/ptr" 30 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk" 31 | ) 32 | 33 | var errDataMatch = errors.New("data does not match") 34 | var errTest = errors.New("test err") 35 | var strSuccess = "success" 36 | 37 | type sampleEvtStr struct { 38 | strBuf ptr.StringBuffer 39 | shouldError bool 40 | expectedData []byte 41 | } 42 | 43 | func allocSSPluginEvent(num, ts uint64, data []byte) (*_Ctype_struct_ss_plugin_event_input, func()) { 44 | ret := &_Ctype_struct_ss_plugin_event_input{} 45 | evts, _ := sdk.NewEventWriters(1, int64(len(data))) 46 | evt := evts.Get(0) 47 | evt.Writer().Write(data) 48 | ret.evt = *(**_Ctype_struct_ss_plugin_event)(evts.ArrayPtr()) 49 | ret.evtnum = _Ctype_uint64_t(num) 50 | 51 | return ret, func() { 52 | evts.Free() 53 | } 54 | } 55 | 56 | func (s *sampleEvtStr) StringerBuffer() sdk.StringBuffer { 57 | return &s.strBuf 58 | } 59 | 60 | func (s *sampleEvtStr) String(evt sdk.EventReader) (string, error) { 61 | if s.shouldError { 62 | return "", errTest 63 | } 64 | data, err := ioutil.ReadAll(evt.Reader()) 65 | if err != nil { 66 | return "", err 67 | } 68 | if !bytes.Equal(s.expectedData, data) { 69 | return "", errDataMatch 70 | } 71 | 72 | return strSuccess, nil 73 | } 74 | 75 | func TestEvtStr(t *testing.T) { 76 | sample := &sampleEvtStr{} 77 | defer sample.StringerBuffer().Free() 78 | handle := cgo.NewHandle(sample) 79 | defer handle.Delete() 80 | 81 | data := []byte{0, 1, 2, 3, 4, 5, 6} 82 | sample.expectedData = data 83 | 84 | // test success 85 | event, freeEvent := allocSSPluginEvent(1, uint64(time.Now().UnixNano()), data) 86 | defer freeEvent() 87 | cStr := plugin_event_to_string(_Ctype_uintptr_t(handle), event) 88 | str := ptr.GoString(unsafe.Pointer(cStr)) 89 | if str != strSuccess { 90 | t.Errorf("expected %s, but found %s", strSuccess, str) 91 | } 92 | 93 | // test forced error 94 | sample.shouldError = true 95 | cStr = plugin_event_to_string(_Ctype_uintptr_t(handle), event) 96 | str = ptr.GoString(unsafe.Pointer(cStr)) 97 | if str != errTest.Error() { 98 | t.Errorf("expected %s, but found %s", strSuccess, str) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /pkg/sdk/symbols/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | // Package symbols provides prebuilt implementations for all the C symbols 19 | // required to develop plugins as for the definitions of plugin_types.h. 20 | // 21 | // This package defines low-level constructs for plugin development meant 22 | // for advanced users that wish to use only a portion of the SDK internals. 23 | // The sdk/plugins package should normally be used instead for the most general 24 | // use cases, as it provides more high-level constructs. The symbols package 25 | // is also used internally, and may be subject to more frequent breaking 26 | // changes. 27 | // 28 | // The C symbol set is divided in different sub-packages to allow plugin 29 | // developers to import only the ones they need. Importing one of the 30 | // sub-packages automatically includes its prebuilt symbols in the plugin. 31 | // If one of the prebuilt symbols is imported it would not be possible 32 | // to re-define it in the plugin, as this would lead to a linking failure 33 | // due to multiple definitions of the same symbol. 34 | // 35 | // Each sub-package has been designed to only implement one or few symbols. 36 | // Plugin developers can either decide to implement all the symbols of the 37 | // sub-packages, or to import only a subset of them and decide to implement 38 | // some of the symbols manually. 39 | // 40 | // The mapping between the prebuilt C exported symbols and their sub-package 41 | // has been designed by grouping them depending on their use cases. 42 | // The mapping is designed as follows: 43 | // - info: get_type, get_id, get_description, get_contact, 44 | // get_version, get_required_api_version, 45 | // get_event_source, get_extract_event_sources 46 | // - fields: plugin_get_fields 47 | // - lasterr: plugin_get_last_error 48 | // - initialize: plugin_init, plugin_destroy 49 | // - open: plugin_open, plugin_close 50 | // - nextbatch: plugin_next_batch 51 | // - extract: plugin_extract_fields 52 | // - evtstr: plugin_event_to_string 53 | // - progress: plugin_get_progress 54 | // 55 | // There are no horizontal dependencies between the sub-packages, which means 56 | // that they are independent from one another. Each sub-package only depends 57 | // on the definitions of the base-level sdk package, and occasionally uses 58 | // constructs from the ptr and cgo packages. This makes each sub-package 59 | // composable, and developers can easily mix manually implemented C symbols 60 | // with the prebuilt ones, as long as the interface requirements are respected. 61 | // 62 | package symbols 63 | -------------------------------------------------------------------------------- /pkg/sdk/symbols/listopen/listopen_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 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 | http://www.apache.org/licenses/LICENSE-2.0 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | package listopen 16 | 17 | import ( 18 | "encoding/json" 19 | "errors" 20 | "testing" 21 | "unsafe" 22 | 23 | "github.com/falcosecurity/plugin-sdk-go/pkg/cgo" 24 | "github.com/falcosecurity/plugin-sdk-go/pkg/ptr" 25 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk" 26 | ) 27 | 28 | var errTest = errors.New("errTest") 29 | 30 | var sampleParams = []sdk.OpenParam{ 31 | { 32 | Value: "Res1", 33 | Desc: "Desc1", 34 | }, 35 | { 36 | Value: "Res2", 37 | }, 38 | } 39 | 40 | type sampleOpenParams struct { 41 | lastErr error 42 | params []sdk.OpenParam 43 | strBuf ptr.StringBuffer 44 | } 45 | 46 | func (b *sampleOpenParams) LastError() error { 47 | return b.lastErr 48 | } 49 | 50 | func (b *sampleOpenParams) SetLastError(err error) { 51 | b.lastErr = err 52 | } 53 | 54 | func (s *sampleOpenParams) OpenParamsBuffer() sdk.StringBuffer { 55 | return &s.strBuf 56 | } 57 | 58 | func (s *sampleOpenParams) OpenParams() ([]sdk.OpenParam, error) { 59 | return s.params, s.lastErr 60 | } 61 | 62 | func TestOpenParams(t *testing.T) { 63 | var err error 64 | var bytes []byte 65 | var res int32 66 | var str string 67 | var strExpected string 68 | 69 | // initilize 70 | pState := &sampleOpenParams{} 71 | pHandle := cgo.NewHandle(pState) 72 | defer pHandle.Delete() 73 | defer pState.strBuf.Free() 74 | 75 | // error 76 | pState.lastErr = errTest 77 | str = ptr.GoString(unsafe.Pointer(plugin_list_open_params((_Ctype_uintptr_t)(pHandle), &res))) 78 | if res != sdk.SSPluginFailure { 79 | t.Errorf("(res): expected %d, but found %d", sdk.SSPluginFailure, res) 80 | } else if pState.LastError() != errTest { 81 | t.Errorf("(err): expected %s, but found %s", errTest.Error(), pState.LastError().Error()) 82 | } else if str != "" { 83 | t.Errorf("(value): expected nil, but found %s", str) 84 | } 85 | 86 | // success 87 | pState.lastErr = nil 88 | pState.params = sampleParams 89 | bytes, err = json.Marshal(&sampleParams) 90 | if err != nil { 91 | t.Error(err) 92 | } 93 | strExpected = string(bytes) 94 | str = ptr.GoString(unsafe.Pointer(plugin_list_open_params((_Ctype_uintptr_t)(pHandle), &res))) 95 | if res != sdk.SSPluginSuccess { 96 | t.Errorf("(res): expected %d, but found %d", sdk.SSPluginSuccess, res) 97 | } else if pState.LastError() != nil { 98 | t.Errorf("(err): expected nil, but found %s", pState.LastError().Error()) 99 | } else if str != strExpected { 100 | t.Errorf("(value): expected %s, but found %s", strExpected, str) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /pkg/ptr/string.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package ptr 19 | 20 | /* 21 | #include 22 | */ 23 | import "C" 24 | import ( 25 | "reflect" 26 | "unsafe" 27 | ) 28 | 29 | const ( 30 | cStringNullTerminator = byte(0) 31 | ) 32 | 33 | // GoString converts a C string to a Go string. This is analoguous 34 | // to C.GoString, but avoids unnecessary memory allcations and copies. 35 | // The string length is determined by invoking strlen on the passed 36 | // memory pointer. 37 | // Note that the returned string is an aliased view of the underlying 38 | // C-allocated memory. As such, writing inside the memory will cause 39 | // the string contents to change. Accordingly, unsafe memory management, 40 | // such as unexpectedly free-ing the underlying C memory, can cause 41 | // non-deterministic behavior on the Go routines using the returned string. 42 | func GoString(charPtr unsafe.Pointer) string { 43 | if charPtr == nil { 44 | return "" 45 | } 46 | 47 | // We manually implement strlen to avoid an unnecessary Go -> C call. 48 | // See: https://github.com/torvalds/linux/blob/f6274b06e326d8471cdfb52595f989a90f5e888f/lib/string.c#L558 49 | var len int 50 | for len = 0; *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(charPtr)) + uintptr(len))) != cStringNullTerminator; len++ { 51 | // nothing 52 | } 53 | 54 | var res string 55 | (*reflect.StringHeader)(unsafe.Pointer(&res)).Data = uintptr(charPtr) 56 | (*reflect.StringHeader)(unsafe.Pointer(&res)).Len = len 57 | return res 58 | } 59 | 60 | // StringBuffer represents a buffer for C-allocated null-terminated strings 61 | // in a Go-friendly way. This is an implementation of the sdk.StringBuffer 62 | // interface. The underlying memory buffer is allocated and resized 63 | // automatically. The buffer allocation happens lazily at the first call 64 | // to Write. If during a call to Write the converted string is too large 65 | // to fit in the buffer, it gets resized automatically to a proper size. 66 | type StringBuffer struct { 67 | cPtr *C.char 68 | len int 69 | } 70 | 71 | func (s *StringBuffer) Write(str string) { 72 | if s.cPtr == nil || len(str) > s.len { 73 | if s.cPtr != nil { 74 | C.free(unsafe.Pointer(s.cPtr)) 75 | } 76 | s.cPtr = (*C.char)(C.malloc((C.size_t)(len(str) + 1))) 77 | } 78 | 79 | p := (*[1 << 30]byte)(unsafe.Pointer(s.cPtr)) 80 | copy(p[:], str) 81 | p[len(str)] = cStringNullTerminator 82 | s.len = len(str) 83 | } 84 | 85 | func (s *StringBuffer) CharPtr() unsafe.Pointer { 86 | return unsafe.Pointer(s.cPtr) 87 | } 88 | 89 | func (s *StringBuffer) String() string { 90 | return GoString(unsafe.Pointer(s.cPtr)) 91 | } 92 | 93 | func (s *StringBuffer) Free() { 94 | if s.cPtr != nil { 95 | C.free(unsafe.Pointer(s.cPtr)) 96 | s.cPtr = nil 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /pkg/sdk/symbols/nextbatch/nextbatch_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package nextbatch 19 | 20 | import ( 21 | "errors" 22 | "testing" 23 | "unsafe" 24 | 25 | "github.com/falcosecurity/plugin-sdk-go/pkg/cgo" 26 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk" 27 | ) 28 | 29 | var errTest = errors.New("testErr") 30 | 31 | type sampleNextBatch struct { 32 | events sdk.EventWriters 33 | n int 34 | err error 35 | lastErr error 36 | } 37 | 38 | func (s *sampleNextBatch) Events() sdk.EventWriters { 39 | return s.events 40 | } 41 | 42 | func (s *sampleNextBatch) SetEvents(evts sdk.EventWriters) { 43 | s.events = evts 44 | } 45 | 46 | func (s *sampleNextBatch) NextBatch(pState sdk.PluginState, evts sdk.EventWriters) (int, error) { 47 | return s.n, s.err 48 | } 49 | 50 | func (s *sampleNextBatch) SetLastError(err error) { 51 | s.lastErr = err 52 | } 53 | 54 | func (s *sampleNextBatch) LastError() error { 55 | return s.lastErr 56 | } 57 | 58 | func TestNextBatch(t *testing.T) { 59 | sample := &sampleNextBatch{} 60 | handle := cgo.NewHandle(sample) 61 | defer handle.Delete() 62 | events, err := sdk.NewEventWriters(10, 10) 63 | if err != nil { 64 | t.Error(err) 65 | } 66 | defer events.Free() 67 | sample.events = events 68 | 69 | // generic testing callback 70 | doTest := func(name string, res int32, num uint32, ptr unsafe.Pointer, err error) { 71 | var resNum uint32 72 | var resPtr **_Ctype_ss_plugin_event 73 | r := plugin_next_batch(_Ctype_uintptr_t(handle), _Ctype_uintptr_t(handle), &resNum, &resPtr) 74 | if r != res { 75 | t.Errorf("(%s - res): expected %d, but found %d", name, res, r) 76 | } else if resNum != num { 77 | t.Errorf("(%s - num): expected %d, but found %d", name, num, resNum) 78 | } else if unsafe.Pointer(resPtr) != ptr { 79 | t.Errorf("(%s - ptr): expected %d, but found %d", name, ptr, resPtr) 80 | } else if sample.lastErr != err { 81 | if sample.lastErr == nil && err != nil { 82 | t.Errorf("(%s - err): should not be nil", name) 83 | } else if err == nil { 84 | t.Errorf("(%s - err): should be nil", name) 85 | } else { 86 | t.Errorf("(%s - err): expected %s, but found %s", name, err.Error(), sample.lastErr.Error()) 87 | } 88 | } 89 | } 90 | 91 | // success 92 | sample.n = 5 93 | sample.err = nil 94 | doTest("success", sdk.SSPluginSuccess, uint32(sample.n), events.ArrayPtr(), nil) 95 | 96 | // timeout 97 | sample.n = 5 98 | sample.err = sdk.ErrTimeout 99 | doTest("timeout", sdk.SSPluginTimeout, uint32(sample.n), events.ArrayPtr(), nil) 100 | 101 | // eof 102 | sample.n = 5 103 | sample.err = sdk.ErrEOF 104 | doTest("timeout", sdk.SSPluginEOF, uint32(sample.n), events.ArrayPtr(), nil) 105 | 106 | // failure 107 | sample.n = 0 108 | sample.err = errTest 109 | doTest("failure", sdk.SSPluginFailure, uint32(sample.n), nil, errTest) 110 | 111 | } 112 | -------------------------------------------------------------------------------- /pkg/sdk/instance.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package sdk 19 | 20 | // InstanceState represents the state of a plugin instance created 21 | // with plugin_open(). 22 | type InstanceState interface { 23 | } 24 | 25 | // Closer is an interface wrapping the basic Destroy method. 26 | // Close deinitializes the resources opened or allocated by a plugin instance. 27 | // This is meant to be used in plugin_close() to release the resources owned 28 | // by a plugin instance. The behavior of Close after the first call 29 | // is undefined. 30 | type Closer interface { 31 | Close() 32 | } 33 | 34 | // Events is an interface wrapping the basic Events and SetEvents 35 | // methods. This is meant to be used as a standard container for a resusable 36 | // list of EventWriters to be used in plugin_next_batch(). 37 | type Events interface { 38 | // Events returns the list of reusable EventWriters. 39 | Events() EventWriters 40 | // 41 | // SetEvents sets the list of reusable EventWriters. 42 | SetEvents(events EventWriters) 43 | } 44 | 45 | // NextBatcher is an interface wrapping the basic NextBatch method. 46 | // NextBatch is meant to be used in plugin_next_batch() to create a batch of 47 | // new events altogether. 48 | // 49 | // The pState argument represents the plugin state, whereas the evt argument 50 | // is an EventWriters representing list the to-be created events. 51 | // The size of the event list dictates the expected size of the batch. 52 | // 53 | // NextBatch can set a timestamp for the to-be created events with the 54 | // SetTimestamp method of the EventWriter interface. 55 | // If not set manually, the framework will set a timestamp automatically. 56 | // NextBatch must be consistent in setting timestamps: either it sets it 57 | // for every event, or for none. 58 | // 59 | // NextBatch returns the number of events created in the batch, which is 60 | // always <= evts.Len(), and a nil error if the call is successful. 61 | // ErrTimeout can be returned to indicate that no new events are currently 62 | // available for the current batch, but that they can be available in future 63 | // calls to NextBatch. ErrEOF can be returned to indicate that no new events 64 | // will be available. After returning ErrEOF once, subsequent calls to 65 | // NextBatch must be idempotent and must keep returning ErrEOF. 66 | // If the returned error is non-nil, the batch of events is discarded. 67 | type NextBatcher interface { 68 | NextBatch(pState PluginState, evts EventWriters) (int, error) 69 | } 70 | 71 | // Progresser is an interface wrapping the basic Progress method. 72 | // Progress is meant to be used in plugin_get_progress() to optionally notify 73 | // the framework about the current event creation progress. 74 | // Progress returns a float64 representing the normalized progress percentage 75 | // such that 0 <= percentage <= 1, and a string representation of the same 76 | // percentage value. 77 | type Progresser interface { 78 | Progress(pState PluginState) (float64, string) 79 | } 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # plugin-sdk-go 2 | 3 | [![Falco Core Repository](https://github.com/falcosecurity/evolution/blob/main/repos/badges/falco-core-blue.svg)](https://github.com/falcosecurity/evolution/blob/main/REPOSITORIES.md#core-scope) [![Stable](https://img.shields.io/badge/status-stable-brightgreen?style=for-the-badge)](https://github.com/falcosecurity/evolution/blob/main/REPOSITORIES.md#stable) [![License](https://img.shields.io/github/license/falcosecurity/plugin-sdk-go?style=for-the-badge)](./LICENSE) 4 | 5 | [![Go Reference](https://pkg.go.dev/badge/github.com/falcosecurity/plugin-sdk-go/pkg/sdk.svg)](https://pkg.go.dev/github.com/falcosecurity/plugin-sdk-go/pkg/sdk) 6 | [![Release](https://img.shields.io/github/release/falcosecurity/plugin-sdk-go.svg?style=flat-square)](https://github.com/falcosecurity/plugin-sdk-go/releases/latest) 7 | [![Go Report Card](https://goreportcard.com/badge/github.com/falcosecurity/plugin-sdk-go?style=flat-square)](https://goreportcard.com/report/github.com/falcosecurity/plugin-sdk-go) 8 | 9 | ## Introduction 10 | 11 | This SDK facilitates writing [plugins](https://falco.org/docs/plugins) for [Falco](https://github.com/falcosecurity/falco) or application using [Falcosecurity's libs](https://github.com/falcosecurity/libs). 12 | 13 | ## Quick start 14 | 15 | Before using this SDK, review the [developer's guide](https://falco.org/docs/plugins/developers_guide/) which fully documents the API and provides best practices for writing plugins. The developer's guide includes a [walkthrough](https://falco.org/docs/plugins/go-sdk-walkthrough/#example-go-plugin-dummy) of a plugin written in Go that uses this package. 16 | 17 | For a quick start, you can refer to the provided examples: 18 | - [plugin with field extraction](https://github.com/falcosecurity/plugin-sdk-go/tree/main/examples/extractor) 19 | - [plugin with event sourcing](https://github.com/falcosecurity/plugin-sdk-go/tree/main/examples/source) 20 | - [plugin with both event sourcing and field extraction](https://github.com/falcosecurity/plugin-sdk-go/tree/main/examples/full) 21 | 22 | 23 | 24 | ## What's next 25 | 26 | When ready to release your plugin, make sure to register the plugin with the Falcosecurity organization by creating a PR to the [falcosecurity/plugins](https://github.com/falcosecurity/plugins) respository with details on the new plugin. This ensures that a given ID is used by exactly one plugin with event sourcing capability, and allows authors of plugins with field extraction capability to coordinate about event source formats. 27 | 28 | ## Join the Community 29 | 30 | To get involved with The Falco Project please visit [the community repository](https://github.com/falcosecurity/community) to find more. 31 | 32 | How to reach out? 33 | 34 | - Join the [#falco](https://kubernetes.slack.com/messages/falco) channel on the [Kubernetes Slack](https://slack.k8s.io) 35 | - [Join the Falco mailing list](https://lists.cncf.io/g/cncf-falco-dev) 36 | 37 | 38 | ## Contributing 39 | 40 | See the [CONTRIBUTING.md](https://github.com/falcosecurity/.github/blob/master/CONTRIBUTING.md). 41 | 42 | ## Security Audit 43 | 44 | A third party security audit was performed by Cure53, you can see the full report [here](https://github.com/falcosecurity/falco/blob/master/audits/SECURITY_AUDIT_2019_07.pdf). 45 | 46 | ## Reporting security vulnerabilities 47 | 48 | Please report security vulnerabilities following the community process documented [here](https://github.com/falcosecurity/.github/blob/master/SECURITY.md). 49 | 50 | ## License Terms 51 | 52 | This project is licensed to you under the [Apache 2.0](./LICENSE) open source license. 53 | 54 | 55 | -------------------------------------------------------------------------------- /pkg/sdk/plugin.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package sdk 19 | 20 | // PluginState represents the state of a plugin returned by plugin_init(). 21 | type PluginState interface { 22 | } 23 | 24 | type ExtractRequests interface { 25 | ExtractRequests() ExtractRequestPool 26 | SetExtractRequests(ExtractRequestPool) 27 | } 28 | 29 | // LastError is a compasable interface wrapping the basic LastError and 30 | // SetLastError methods. This is meant to be used as a standard 31 | // container for the last error catched during the execution of a plugin. 32 | type LastError interface { 33 | // LastError returns the last error occurred in the plugin. 34 | LastError() error 35 | // 36 | // SetLastError sets the last error occurred in the plugin. 37 | SetLastError(err error) 38 | } 39 | 40 | // Destroyer is an interface wrapping the basic Destroy method. 41 | // Destroy deinitializes the resources opened or allocated by a plugin. 42 | // This is meant to be used in plugin_destroy() to release the plugin's 43 | // resources. The behavior of Destroy after the first call is undefined. 44 | type Destroyer interface { 45 | Destroy() 46 | } 47 | 48 | // Stringer is an interface wrapping the basic String method. 49 | // String takes an EventReader and produces a string representation 50 | // describing its internal data. This is meant to be used in 51 | // plugin_event_to_string(), where the event is provided by the framework 52 | // and previouly produced by an invocation of plugin_next_batch() of this 53 | // plugin. 54 | type Stringer interface { 55 | String(evt EventReader) (string, error) 56 | } 57 | 58 | // Extractor is an interface wrapping the basic Extract method. 59 | // Extract is meant to be used in plugin_extract_fields() to extract the value 60 | // of a single field from a given event data. 61 | type Extractor interface { 62 | Extract(req ExtractRequest, evt EventReader) error 63 | } 64 | 65 | // OpenParams is an interface wrapping the basic OpenParams method. 66 | // OpenParams is meant to be used in plugin_list_open_params() to return a list 67 | // of suggested parameters that would be accepted as valid arguments 68 | // for plugin_open(). 69 | type OpenParams interface { 70 | OpenParams() ([]OpenParam, error) 71 | } 72 | 73 | // InitSchema is an interface wrapping the basic InitSchema method. 74 | // InitSchema is meant to be used in plugin_get_init_schema() to return a 75 | // schema describing the data expected to be passed as a configuration 76 | // during the plugin initialization. The schema must follow the JSON Schema 77 | // specific: https://json-schema.org/. A nil return value is interpreted 78 | // as the absence of a schema, and the init configuration will not be 79 | // pre-validated by the framework. If JSON Schema is returned, the 80 | // init configuration will be expected to be a json-formatted string. 81 | // If so, the init() function can assume the configuration to be well-formed 82 | // according to the returned schema, as the framework will perform a 83 | // pre-validation before initializing the plugin. 84 | type InitSchema interface { 85 | InitSchema() *SchemaInfo 86 | } 87 | -------------------------------------------------------------------------------- /pkg/sdk/symbols/extract/extract.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include "extract.h" 23 | 24 | // Possibly oversimplified version of https://gcc.gnu.org/wiki/Visibility 25 | #if defined _WIN32 || defined __CYGWIN__ 26 | #define FALCO_PLUGIN_SDK_PUBLIC __declspec(dllexport) 27 | #else 28 | #define FALCO_PLUGIN_SDK_PUBLIC 29 | #endif 30 | 31 | enum worker_state 32 | { 33 | UNUSED = 0, 34 | WAIT = 1, 35 | DATA_REQ = 2, 36 | EXIT_REQ = 3, 37 | EXIT_ACK = 4, 38 | }; 39 | 40 | static async_extractor_info *s_async_ctx_batch = NULL; 41 | 42 | async_extractor_info *async_init(size_t size) 43 | { 44 | s_async_ctx_batch = (async_extractor_info *)malloc(sizeof(async_extractor_info) * size); 45 | return s_async_ctx_batch; 46 | } 47 | 48 | void async_deinit() 49 | { 50 | free(s_async_ctx_batch); 51 | s_async_ctx_batch = NULL; 52 | } 53 | 54 | // Defined in extract.go 55 | extern int32_t plugin_extract_fields_sync(ss_plugin_t *s, 56 | const ss_plugin_event_input *evt, 57 | uint32_t num_fields, 58 | ss_plugin_extract_field *fields, 59 | ss_plugin_extract_value_offsets *offsets); 60 | 61 | // This is the plugin API function. If s_async_ctx_batch is 62 | // non-NULL, it calls the async extractor function. Otherwise, it 63 | // calls the synchronous extractor function. 64 | FALCO_PLUGIN_SDK_PUBLIC int32_t plugin_extract_fields(ss_plugin_t *s, 65 | const ss_plugin_event_input *evt, 66 | const ss_plugin_field_extract_input* in) 67 | { 68 | // note: concurrent requests are supported on the context batch, but each 69 | // slot with a different value of ss_plugin_t *s. As such, for each lock 70 | // we assume worker is already in WAIT state. This is possible because 71 | // ss_plugin_t *s is an integer number representing a cgo.Handle, and can 72 | // have values in the range of [1, cgo.MaxHandle] 73 | // 74 | // todo(jasondellaluce): this is dependent on the implementation of our 75 | // cgo.Handle to optimize performance, so change this if we ever change 76 | // how cgo.Handles are represented 77 | 78 | // if async optimization is not available, go with a simple C -> Go call 79 | if (s_async_ctx_batch == NULL 80 | || atomic_load_explicit(&s_async_ctx_batch[(size_t)s - 1].lock, memory_order_seq_cst) != WAIT) 81 | { 82 | return plugin_extract_fields_sync(s, evt, in->num_fields, in->fields, in->value_offsets); 83 | } 84 | 85 | // Set input data 86 | s_async_ctx_batch[(size_t)s - 1].s = s; 87 | s_async_ctx_batch[(size_t)s - 1].evt = evt; 88 | s_async_ctx_batch[(size_t)s - 1].num_fields = in->num_fields; 89 | s_async_ctx_batch[(size_t)s - 1].fields = in->fields; 90 | s_async_ctx_batch[(size_t)s - 1].value_offsets = in->value_offsets; 91 | 92 | // notify data request 93 | atomic_store_explicit(&s_async_ctx_batch[(size_t)s - 1].lock, DATA_REQ, memory_order_seq_cst); 94 | 95 | // busy-wait until worker completation 96 | while (atomic_load_explicit(&s_async_ctx_batch[(size_t)s - 1].lock, memory_order_seq_cst) != WAIT); 97 | 98 | return s_async_ctx_batch[(size_t)s - 1].rc; 99 | } 100 | -------------------------------------------------------------------------------- /pkg/sdk/sdk.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | 4 | Copyright (C) 2023 The Falco Authors. 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | package sdk 20 | 21 | import ( 22 | "errors" 23 | ) 24 | 25 | // ErrEOF is the error returned by next_batch when no new events 26 | // are available. 27 | var ErrEOF = errors.New("eof") 28 | 29 | // ErrTimeout is the error returned by next_batch when no new events 30 | // are available for the current batch, but may be available in the 31 | // next one. 32 | var ErrTimeout = errors.New("timeout") 33 | 34 | // Functions that return or update a rc (e.g. plugin_init, 35 | // plugin_open) should return one of these values. 36 | const ( 37 | SSPluginSuccess int32 = 0 38 | SSPluginFailure int32 = 1 39 | SSPluginTimeout int32 = -1 40 | SSPluginEOF int32 = 2 41 | SSPluginNotSupported int32 = 3 42 | ) 43 | 44 | // DefaultEvtSize is the default size for the data payload allocated 45 | // for each event in the EventWriters interface used by the SDK. 46 | const DefaultEvtSize uint32 = 256 * 1024 47 | 48 | // DefaultBatchSize is the default number of events in the EventWriters 49 | // interface used by the SDK. 50 | const DefaultBatchSize uint32 = 128 51 | 52 | // The full set of values that can be returned in the ftype 53 | // member of ss_plugin_extract_field structs (ppm_events_public.h). 54 | const ( 55 | // A 64bit unsigned integer. 56 | FieldTypeUint64 uint32 = 8 57 | // A printable buffer of bytes, NULL terminated. 58 | FieldTypeCharBuf uint32 = 9 59 | // A relative time. Seconds * 10^9 + nanoseconds. 64bit. 60 | FieldTypeRelTime uint32 = 20 61 | // An absolute time interval. Seconds from epoch * 10^9 + nanoseconds. 64bit. 62 | FieldTypeAbsTime uint32 = 21 63 | // A boolean value, 4 bytes. 64 | FieldTypeBool uint32 = 25 65 | // Either an IPv4 or IPv6 address. The length indicates which one it is. 66 | FieldTypeIPAddr uint32 = 40 67 | // Either an IPv4 or IPv6 network. The length indicates which one it is. 68 | FieldTypeIPNet uint32 = 41 69 | ) 70 | 71 | // FieldEntry represents a single field entry that a plugin with field extraction 72 | // capability can expose. 73 | // Should be used when implementing plugin_get_fields(). 74 | type FieldEntry struct { 75 | Name string `json:"name"` 76 | Type string `json:"type"` 77 | IsList bool `json:"isList"` 78 | Arg FieldEntryArg `json:"arg"` 79 | Display string `json:"display"` 80 | Desc string `json:"desc"` 81 | Properties []string `json:"properties"` 82 | } 83 | 84 | // FieldEntryArg describes the argument of a single field entry that 85 | // an plugin with field extraction capability can expose. 86 | // Should be used when implementing plugin_get_fields(). 87 | type FieldEntryArg struct { 88 | IsRequired bool `json:"isRequired"` 89 | IsIndex bool `json:"isIndex"` 90 | IsKey bool `json:"isKey"` 91 | } 92 | 93 | // OpenParam represents a valid parameter for plugin_open(). 94 | type OpenParam struct { 95 | Value string `json:"value"` 96 | Desc string `json:"desc"` 97 | Separator string `json:"separator"` 98 | } 99 | 100 | // SchemaInfo represent a schema describing a structured data type. 101 | // Should be used when implementing plugin_get_init_schema(). 102 | type SchemaInfo struct { 103 | Schema string 104 | } 105 | -------------------------------------------------------------------------------- /pkg/sdk/plugins/source/source.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | // Package source provides high-level constructs to easily build 19 | // plugins with event sourcing capability. 20 | package source 21 | 22 | import ( 23 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk" 24 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk/plugins" 25 | _ "github.com/falcosecurity/plugin-sdk-go/pkg/sdk/symbols/evtstr" 26 | _ "github.com/falcosecurity/plugin-sdk-go/pkg/sdk/symbols/lasterr" 27 | _ "github.com/falcosecurity/plugin-sdk-go/pkg/sdk/symbols/listopen" 28 | _ "github.com/falcosecurity/plugin-sdk-go/pkg/sdk/symbols/nextbatch" 29 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk/symbols/open" 30 | _ "github.com/falcosecurity/plugin-sdk-go/pkg/sdk/symbols/progress" 31 | ) 32 | 33 | // Plugin is an interface representing a plugin with event sourcing capability 34 | type Plugin interface { 35 | plugins.Plugin 36 | sdk.StringerBuffer 37 | sdk.OpenParamsBuffer 38 | // (optional) sdk.OpenParams 39 | // (optional) sdk.Stringer 40 | 41 | // 42 | // Open opens the source and starts a capture (e.g. stream of events). 43 | // 44 | // The argument string represents the user-defined parameters and 45 | // can be used to customize how the source is opened. 46 | // The return value is an Instance representing the source capture session. 47 | // There can be multiple instances of the same source open. 48 | // A successfull call to Open returns a nil error. 49 | // 50 | // The sdk.EventWriters event buffer, that is reused during each cycle 51 | // of new event creation, is initialized in automatic after the execution 52 | // of Open with the SetEvents method of the Instance interface. 53 | // Developers may override the default sdk.EventWriters by setting it 54 | // on the returned Instance with SetEvents, before returning from Open. 55 | // This can help specifying the data event size, the size of each 56 | // event batch, or just to use an implementation of the 57 | // sdk.EventWriters interface different from the SDK default one. 58 | Open(params string) (Instance, error) 59 | } 60 | 61 | // Instance is an interface representing a source capture session instance 62 | // returned by a call to Open of a plugin with event sourcing capability. 63 | // 64 | // Implementations of this interface must implement sdk.NextBatcher, and can 65 | // optionally implement sdk.Closer and sdk.Progresser. 66 | // If sdk.Closer is implemented, the Close method will be called while closing 67 | // the source capture session. 68 | type Instance interface { 69 | // (optional) sdk.Closer 70 | // (optional) sdk.Progresser 71 | sdk.Events 72 | sdk.NextBatcher 73 | sdk.ProgressBuffer 74 | } 75 | 76 | // BaseInstance is a base implementation of the Instance interface. 77 | // Developer-defined Instance implementations should be composed with BaseInstance 78 | // to have out-of-the-box compliance with all the required interfaces. 79 | type BaseInstance struct { 80 | plugins.BaseEvents 81 | plugins.BaseProgress 82 | } 83 | 84 | // Register registers the event sourcing capability in the framework for the given Plugin. 85 | // 86 | // This function should be called from the provided plugins.FactoryFunc implementation. 87 | // See the parent package for more detail. This function is idempotent. 88 | func Register(p Plugin) { 89 | open.SetOnOpen(func(c string) (sdk.InstanceState, error) { 90 | return p.Open(c) 91 | }) 92 | } 93 | -------------------------------------------------------------------------------- /pkg/sdk/symbols/open/open.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | // This package exports the following C functions: 19 | // - ss_instance_t* plugin_open(ss_plugin_t* s, char* params, ss_plugin_rc* rc) 20 | // - void plugin_close(ss_plugin_t* s, ss_instance_t* h) 21 | // 22 | // The exported plugin_open requires s to be a handle 23 | // of cgo.Handle from this SDK. The value of the s handle must implement 24 | // the sdk.LastError interface. plugin_open calls the function set with 25 | // SetOnOpen, which returns a sdk.InstanceState interface. If the return 26 | // value implements the sdk.Events interface, the function checks if an 27 | // instance of sdk.EventWriters has already been set. If not, a default 28 | // one is created on the fly and set with the SetEvents method. 29 | // 30 | // The exported plugin_close requires h to be a handle 31 | // of cgo.Handle from this SDK. If the value of the h handle implements 32 | // the sdk.Closer interface, the function calls its Close method. 33 | // If sdk.Events is implemented the function calls the Free method 34 | // on the returned sdk.EventWriters. Finally, the function deletes the 35 | // h cgo.Handle. 36 | // 37 | // This function is part of the plugin_api interface as defined in plugin_api.h. 38 | // In almost all cases, your plugin should import this module, unless your 39 | // plugin exports those symbols by other means. 40 | package open 41 | 42 | /* 43 | #include 44 | */ 45 | import "C" 46 | import ( 47 | "github.com/falcosecurity/plugin-sdk-go/pkg/cgo" 48 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk" 49 | ) 50 | 51 | var ( 52 | onOpenFn OnOpenFn 53 | ) 54 | 55 | // OnOpenFn is a callback used in plugin_open. 56 | type OnOpenFn func(config string) (sdk.InstanceState, error) 57 | 58 | // SetOnInit sets an initialization callback to be called in plugin_open to 59 | // create the plugin instance state. If never set, plugin_open will panic. 60 | func SetOnOpen(fn OnOpenFn) { 61 | if fn == nil { 62 | panic("plugin-sdk-go/sdk/symbols/open.SetOnOpen: fn must not be nil") 63 | } 64 | onOpenFn = fn 65 | } 66 | 67 | //export plugin_open 68 | func plugin_open(plgState C.uintptr_t, params *C.char, rc *int32) C.uintptr_t { 69 | if onOpenFn == nil { 70 | panic("plugin-sdk-go/sdk/symbols/open: SetOnOpen must be called") 71 | } 72 | 73 | iState, err := onOpenFn(C.GoString(params)) 74 | if err == nil { 75 | // this allows a nil iState 76 | iEvents, ok := iState.(sdk.Events) 77 | if ok && iEvents.Events() == nil { 78 | var events sdk.EventWriters 79 | events, err = sdk.NewEventWriters(int64(sdk.DefaultBatchSize), int64(sdk.DefaultEvtSize)) 80 | if err == nil { 81 | iEvents.SetEvents(events) 82 | } 83 | } 84 | } 85 | 86 | if err != nil { 87 | cgo.Handle(plgState).Value().(sdk.LastError).SetLastError(err) 88 | *rc = sdk.SSPluginFailure 89 | return 0 90 | } 91 | *rc = sdk.SSPluginSuccess 92 | return (C.uintptr_t)(cgo.NewHandle(iState)) 93 | } 94 | 95 | //export plugin_close 96 | func plugin_close(plgState C.uintptr_t, instanceState C.uintptr_t) { 97 | if instanceState != 0 { 98 | handle := cgo.Handle(instanceState) 99 | if state, ok := handle.Value().(sdk.Closer); ok { 100 | state.Close() 101 | } 102 | if state, ok := handle.Value().(sdk.Events); ok { 103 | state.Events().Free() 104 | state.SetEvents(nil) 105 | } 106 | handle.Delete() 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /pkg/sdk/symbols/open/open_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package open 19 | 20 | import ( 21 | "errors" 22 | "testing" 23 | "unsafe" 24 | 25 | "github.com/falcosecurity/plugin-sdk-go/pkg/cgo" 26 | "github.com/falcosecurity/plugin-sdk-go/pkg/ptr" 27 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk" 28 | ) 29 | 30 | var errTest = errors.New("errTest") 31 | 32 | type sampleOpen struct { 33 | lastErr error 34 | } 35 | 36 | func (b *sampleOpen) LastError() error { 37 | return b.lastErr 38 | } 39 | 40 | func (b *sampleOpen) SetLastError(err error) { 41 | b.lastErr = err 42 | } 43 | 44 | type sampleOpenInstance struct { 45 | closeCalled bool 46 | events sdk.EventWriters 47 | } 48 | 49 | func (s *sampleOpenInstance) Events() sdk.EventWriters { 50 | return s.events 51 | } 52 | 53 | func (s *sampleOpenInstance) SetEvents(evts sdk.EventWriters) { 54 | s.events = evts 55 | } 56 | 57 | func (s *sampleOpenInstance) Close() { 58 | s.closeCalled = true 59 | } 60 | 61 | func assertPanic(t *testing.T, fun func()) { 62 | defer func() { 63 | if r := recover(); r == nil { 64 | t.Errorf("expected panic") 65 | } 66 | }() 67 | fun() 68 | } 69 | 70 | func TestInitialize(t *testing.T) { 71 | var res int32 72 | var pHandle cgo.Handle 73 | var iHandle cgo.Handle 74 | var cStr ptr.StringBuffer 75 | 76 | // panics 77 | assertPanic(t, func() { 78 | SetOnOpen(nil) 79 | }) 80 | assertPanic(t, func() { 81 | plugin_open((_Ctype_uintptr_t)(pHandle), (*_Ctype_char)(cStr.CharPtr()), &res) 82 | }) 83 | 84 | // initilize 85 | pState := &sampleOpen{} 86 | pHandle = cgo.NewHandle(pState) 87 | cStr.Write("cStr") 88 | 89 | // nil state 90 | SetOnOpen(func(config string) (sdk.InstanceState, error) { 91 | return nil, nil 92 | }) 93 | iHandle = cgo.Handle(plugin_open((_Ctype_uintptr_t)(pHandle), (*_Ctype_char)(cStr.CharPtr()), &res)) 94 | if res != sdk.SSPluginSuccess { 95 | t.Errorf("(res): expected %d, but found %d", sdk.SSPluginSuccess, res) 96 | } else if iHandle.Value() != nil { 97 | t.Errorf("(value): expected %d, but found %d", unsafe.Pointer(nil), iHandle.Value()) 98 | } 99 | iHandle.Delete() 100 | 101 | // error 102 | SetOnOpen(func(config string) (sdk.InstanceState, error) { 103 | return nil, errTest 104 | }) 105 | iHandle = cgo.Handle(plugin_open((_Ctype_uintptr_t)(pHandle), (*_Ctype_char)(cStr.CharPtr()), &res)) 106 | if res != sdk.SSPluginFailure { 107 | t.Errorf("(res): expected %d, but found %d", sdk.SSPluginFailure, res) 108 | } else if pState.LastError() != errTest { 109 | t.Errorf("(err): expected %s, but found %s", errTest.Error(), pState.LastError().Error()) 110 | } 111 | 112 | // success 113 | iState := &sampleOpenInstance{} 114 | SetOnOpen(func(config string) (sdk.InstanceState, error) { 115 | return iState, nil 116 | }) 117 | iHandle = cgo.Handle(plugin_open((_Ctype_uintptr_t)(pHandle), (*_Ctype_char)(cStr.CharPtr()), &res)) 118 | if res != sdk.SSPluginSuccess { 119 | t.Errorf("(res): expected %d, but found %d", sdk.SSPluginSuccess, res) 120 | } else if iHandle.Value() != iState { 121 | t.Errorf("(value): expected %d, but found %d", unsafe.Pointer(iState), iHandle.Value()) 122 | } else if iState.Events() == nil { 123 | t.Errorf("expected SetEvents() to be called") 124 | } 125 | 126 | // close 127 | plugin_close(_Ctype_uintptr_t(pHandle), _Ctype_uintptr_t(iHandle)) 128 | if !iState.closeCalled { 129 | t.Errorf("expected Close() to be called") 130 | } 131 | pHandle.Delete() 132 | } 133 | -------------------------------------------------------------------------------- /pkg/cgo/handle_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package cgo 19 | 20 | import ( 21 | "reflect" 22 | "sync/atomic" 23 | "testing" 24 | ) 25 | 26 | // This test suite derivates from 27 | // https://cs.opensource.google/go/go/+/refs/tags/go1.17.2:src/runtime/cgo/handle_test.go 28 | 29 | func TestHandle(t *testing.T) { 30 | v := 42 31 | 32 | tests := []struct { 33 | v1 interface{} 34 | v2 interface{} 35 | }{ 36 | {v1: v, v2: v}, 37 | {v1: &v, v2: &v}, 38 | {v1: nil, v2: nil}, 39 | } 40 | 41 | for _, tt := range tests { 42 | h1 := NewHandle(tt.v1) 43 | h2 := NewHandle(tt.v2) 44 | 45 | if uintptr(h1) == 0 || uintptr(h2) == 0 { 46 | t.Fatalf("NewHandle returns zero") 47 | } 48 | 49 | if uintptr(h1) == uintptr(h2) { 50 | t.Fatalf("Duplicated Go values should have different handles, but got equal") 51 | } 52 | 53 | h1v := h1.Value() 54 | h2v := h2.Value() 55 | if !reflect.DeepEqual(h1v, h2v) || !reflect.DeepEqual(h1v, tt.v1) { 56 | t.Fatalf("Value of a Handle got wrong, got %+v %+v, want %+v", h1v, h2v, tt.v1) 57 | } 58 | 59 | h1.Delete() 60 | h2.Delete() 61 | } 62 | 63 | siz := 0 64 | for i := 0; i < MaxHandle; i++ { 65 | if atomic.LoadPointer(&handles[i]) != noHandle { 66 | siz++ 67 | } 68 | } 69 | 70 | if siz != 0 { 71 | t.Fatalf("handles are not cleared, got %d, want %d", siz, 0) 72 | } 73 | } 74 | 75 | func TestInvalidHandle(t *testing.T) { 76 | t.Run("zero", func(t *testing.T) { 77 | h := Handle(0) 78 | 79 | defer func() { 80 | if r := recover(); r != nil { 81 | return 82 | } 83 | t.Fatalf("Delete of zero handle did not trigger a panic") 84 | }() 85 | 86 | h.Delete() 87 | }) 88 | 89 | t.Run("zero-value", func(t *testing.T) { 90 | h := Handle(0) 91 | defer func() { 92 | if r := recover(); r != nil { 93 | return 94 | } 95 | t.Fatalf("Delete of zero handle did not trigger a panic") 96 | }() 97 | h.Value() 98 | }) 99 | 100 | t.Run("invalid", func(t *testing.T) { 101 | h := NewHandle(42) 102 | 103 | defer func() { 104 | if r := recover(); r != nil { 105 | h.Delete() 106 | return 107 | } 108 | t.Fatalf("Invalid handle did not trigger a panic") 109 | }() 110 | 111 | Handle(h + 1).Delete() 112 | }) 113 | } 114 | 115 | func TestMaxHandle(t *testing.T) { 116 | t.Run("non-max", func(t *testing.T) { 117 | defer func() { 118 | if r := recover(); r != nil { 119 | t.Fatalf("NewHandle with non-max handle count triggered a panic") 120 | } 121 | }() 122 | handles := make([]Handle, 0) 123 | for i := 1; i <= MaxHandle; i++ { 124 | v := i 125 | handles = append(handles, NewHandle(&v)) 126 | } 127 | for _, h := range handles { 128 | h.Delete() 129 | } 130 | }) 131 | 132 | t.Run("max", func(t *testing.T) { 133 | defer func() { 134 | if r := recover(); r != nil { 135 | return 136 | } 137 | t.Fatalf("NewHandle with max handle count did not triggered a panic") 138 | }() 139 | handles := make([]Handle, 0) 140 | for i := 1; i <= MaxHandle+1; i++ { 141 | v := i 142 | handles = append(handles, NewHandle(&v)) 143 | } 144 | for _, h := range handles { 145 | h.Delete() 146 | } 147 | }) 148 | } 149 | 150 | func BenchmarkHandle(b *testing.B) { 151 | b.Run("non-concurrent", func(b *testing.B) { 152 | for i := 0; i < b.N; i++ { 153 | h := NewHandle(i) 154 | _ = h.Value() 155 | h.Delete() 156 | 157 | // reset handle to avoid going out of handle space 158 | if i%(MaxHandle-1) == 0 { 159 | resetHandles() 160 | } 161 | } 162 | }) 163 | } 164 | -------------------------------------------------------------------------------- /pkg/loader/plugin_loader.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "plugin_api.h" 22 | 23 | #include 24 | 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif 28 | 29 | /*! 30 | \brief This enums the capabilities supported by plugins. 31 | Each plugin can support one or more of these, in which case the enum flags 32 | are or-ed with each other. 33 | Currently, the supported capabilities are: 34 | * ability to source events and provide them to the event loop 35 | * ability to extract fields from events created by other plugins 36 | * ability to parse events from the event loop (at most once) before 37 | the field extraction phase 38 | * ability to inject events asynchronously in the event loop 39 | */ 40 | typedef enum { 41 | CAP_NONE = 0, 42 | CAP_SOURCING = 1 << 0, 43 | CAP_EXTRACTION = 1 << 1, 44 | CAP_PARSING = 1 << 2, 45 | CAP_ASYNC = 1 << 3, 46 | CAP_CAPTURE_LISTENING = 1 << 4, 47 | CAP_BROKEN = 1 << 31, // used to report inconsistencies 48 | } plugin_caps_t; 49 | 50 | /*! 51 | \brief A handle to a loaded plugin dynamic library. 52 | Pointers to this struct must be obtained through the plugin_load() 53 | and released through plugin_unload(). 54 | */ 55 | typedef struct plugin_handle_t { 56 | #ifdef _WIN32 57 | HINSTANCE handle; ///< Handle of the dynamic library 58 | #else 59 | void* handle; ///< Handle of the dynamic library 60 | #endif 61 | plugin_api api; ///< The vtable method of the plugin that define its API 62 | } plugin_handle_t; 63 | 64 | /*! 65 | \brief Uses the given plugin api and returns a plugin_handle_t* 66 | representing the loaded plugin. In case of error, returns NULL and fills 67 | the err string up to PLUGIN_MAX_ERRLEN chars. 68 | */ 69 | plugin_handle_t* plugin_load_api(const plugin_api* api, char* err); 70 | 71 | /*! 72 | \brief Loads a dynamic library from the given path and returns a 73 | plugin_handle_t* representing the loaded plugin. In case of error, 74 | returns NULL and fills the err string up to PLUGIN_MAX_ERRLEN chars. 75 | */ 76 | plugin_handle_t* plugin_load(const char* path, char* err); 77 | 78 | /*! 79 | \brief Destroys a plugin_handle_t* previously allocated by 80 | invoking plugin_load(). 81 | */ 82 | void plugin_unload(plugin_handle_t* h); 83 | 84 | /*! 85 | \brief Returns true if the plugin at the given path is currently loaded. 86 | */ 87 | bool plugin_is_loaded(const char* path); 88 | 89 | /*! 90 | \brief Returns true the API version required by the given plugin is 91 | compatible with the API version of the loader. Otherwise, returns false 92 | and fills the err string up to PLUGIN_MAX_ERRLEN chars. 93 | */ 94 | bool plugin_check_required_api_version(const plugin_handle_t* h, char* err); 95 | 96 | /*! 97 | \brief Returns true if the given plugin handle implements all the 98 | minimum required function symbols for the current API version. Otherwise, 99 | returns false and fills the err string up to PLUGIN_MAX_ERRLEN chars. 100 | */ 101 | bool plugin_check_required_symbols(const plugin_handle_t* h, char* err); 102 | 103 | /*! 104 | \brief Returns the capabilities supported by the given plugin handle. 105 | In case of inconsistencies, the result will have the CAP_BROKEN bit set 106 | and the err string will be filled up to PLUGIN_MAX_ERRLEN chars representing 107 | the error encountered. 108 | */ 109 | plugin_caps_t plugin_get_capabilities(const plugin_handle_t* h, char* err); 110 | 111 | #ifdef __cplusplus 112 | } 113 | #endif 114 | -------------------------------------------------------------------------------- /pkg/sdk/event_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package sdk 19 | 20 | import ( 21 | "encoding/json" 22 | "io" 23 | "testing" 24 | "time" 25 | "unsafe" 26 | ) 27 | 28 | type sampleEvent struct { 29 | V1 int 30 | V2 int 31 | V3 string 32 | V4 float64 33 | } 34 | 35 | var sample = &sampleEvent{ 36 | V1: 0, 37 | V2: 2, 38 | V3: "hello world", 39 | V4: 5.0, 40 | } 41 | 42 | func nextCallback(pState unsafe.Pointer, iState unsafe.Pointer, evt EventWriter) error { 43 | encoder := json.NewEncoder(evt.Writer()) 44 | err := encoder.Encode(sample) 45 | if err != nil { 46 | return err 47 | } 48 | evt.SetTimestamp(uint64(time.Now().Unix()) * 1000000000) 49 | return nil 50 | } 51 | 52 | func BenchmarkEventWritersNext(b *testing.B) { 53 | writers, err := NewEventWriters(1, int64(DefaultEvtSize)) 54 | if err != nil { 55 | println(err.Error()) 56 | b.Fail() 57 | } 58 | 59 | b.ResetTimer() 60 | for i := 0; i < b.N; i++ { 61 | event := writers.Get(0) 62 | encoder := json.NewEncoder(event.Writer()) 63 | err = encoder.Encode(sample) 64 | if err != nil { 65 | println(err.Error()) 66 | b.Fail() 67 | } 68 | event.SetTimestamp(uint64(time.Now().Unix()) * 1000000000) 69 | } 70 | } 71 | 72 | func BenchmarkEventWritersNextBatch(b *testing.B) { 73 | writers, err := NewEventWriters(int64(DefaultBatchSize), int64(DefaultEvtSize)) 74 | if err != nil { 75 | println(err.Error()) 76 | b.Fail() 77 | } 78 | 79 | b.ResetTimer() 80 | for i := 0; i < b.N; i++ { 81 | for j := 0; j < writers.Len(); j++ { 82 | err := nextCallback(nil, nil, writers.Get(j)) 83 | if err != nil { 84 | println(err.Error()) 85 | b.Fail() 86 | } 87 | } 88 | } 89 | } 90 | 91 | func TestEventWritersNextBatch(t *testing.T) { 92 | events, err := NewEventWriters(int64(DefaultBatchSize), int64(DefaultEvtSize)) 93 | if err != nil { 94 | println(err.Error()) 95 | t.Fail() 96 | } 97 | for i := 0; i < events.Len(); i++ { 98 | err := nextCallback(nil, nil, events.Get(i)) 99 | if err != nil { 100 | println(err.Error()) 101 | t.Fail() 102 | } 103 | } 104 | } 105 | 106 | func TestEventWriterEventReader(t *testing.T) { 107 | tmp := []byte{0} 108 | evtNum := 1 109 | evtSize := DefaultEvtSize 110 | timestamp := time.Now() 111 | 112 | // Create event writer and write sample data 113 | writers, err := NewEventWriters(int64(evtNum), int64(evtSize)) 114 | if err != nil { 115 | t.Error(err) 116 | } 117 | evtWriter := writers.Get(0) 118 | writer := evtWriter.Writer() 119 | for i := 0; i < int(evtSize); i++ { 120 | n, err := writer.Write(tmp) 121 | if err != nil { 122 | t.Error(err) 123 | } else if n == 0 { 124 | t.Errorf("Failed writing byte #%d in event", i) 125 | } 126 | } 127 | evtWriter.SetTimestamp(uint64(timestamp.UnixNano())) 128 | 129 | // Create event reader and read all data 130 | evtReader := NewEventReader(writers.ArrayPtr()) 131 | reader := evtReader.Reader() 132 | _ = evtReader.EventNum() 133 | if evtReader.Timestamp() != uint64(timestamp.UnixNano()) { 134 | t.Errorf("timestamp does not match: expected %d, but found %d", uint64(timestamp.UnixNano()), evtReader.Timestamp()) 135 | } 136 | var i int 137 | for { 138 | n, err := reader.Read(tmp) 139 | if err == io.EOF { 140 | break 141 | } 142 | if err != nil { 143 | t.Error(err) 144 | } else if n == 0 || tmp[0] != byte(0) { 145 | t.Errorf("failed reading byte #%d in event", i) 146 | } 147 | i++ 148 | } 149 | if i != int(evtSize) { 150 | t.Errorf("expected reading %d bytes, but found %d", evtSize, i) 151 | } 152 | 153 | writers.Free() 154 | } 155 | -------------------------------------------------------------------------------- /pkg/sdk/symbols/initialize/initialize_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package initialize 19 | 20 | import ( 21 | "errors" 22 | "testing" 23 | "unsafe" 24 | 25 | "github.com/falcosecurity/plugin-sdk-go/pkg/cgo" 26 | "github.com/falcosecurity/plugin-sdk-go/pkg/ptr" 27 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk" 28 | ) 29 | 30 | var errTest = errors.New("errTest") 31 | 32 | type sampleInitialize struct { 33 | destroyCalled bool 34 | baseInit 35 | reqs sdk.ExtractRequestPool 36 | stringerBuf ptr.StringBuffer 37 | progressBuf ptr.StringBuffer 38 | } 39 | 40 | func (s *sampleInitialize) ExtractRequests() sdk.ExtractRequestPool { 41 | return s.reqs 42 | } 43 | 44 | func (s *sampleInitialize) SetExtractRequests(reqs sdk.ExtractRequestPool) { 45 | s.reqs = reqs 46 | } 47 | 48 | func (s *sampleInitialize) StringerBuffer() sdk.StringBuffer { 49 | return &s.stringerBuf 50 | } 51 | 52 | func (s *sampleInitialize) ProgressBuffer() sdk.StringBuffer { 53 | return &s.progressBuf 54 | } 55 | 56 | func (s *sampleInitialize) Destroy() { 57 | s.destroyCalled = true 58 | } 59 | 60 | func assertPanic(t *testing.T, fun func()) { 61 | defer func() { 62 | if r := recover(); r == nil { 63 | t.Errorf("expected panic") 64 | } 65 | }() 66 | fun() 67 | } 68 | 69 | func TestInitialize(t *testing.T) { 70 | var res int32 71 | var handle cgo.Handle 72 | var cStr ptr.StringBuffer 73 | cStr.Write("cStr") 74 | 75 | // panic 76 | assertPanic(t, func() { 77 | SetOnInit(nil) 78 | }) 79 | 80 | // nil state 81 | SetOnInit(func(config string) (sdk.PluginState, error) { 82 | return nil, nil 83 | }) 84 | 85 | // create an init input 86 | var in _Ctype_struct_ss_plugin_init_input 87 | in.config = nil 88 | in.owner = nil 89 | in.get_owner_last_error = nil 90 | in.tables = nil 91 | 92 | in.config = (*_Ctype_char)(cStr.CharPtr()) 93 | handle = cgo.Handle(plugin_init(&in, &res)) 94 | if res != sdk.SSPluginSuccess { 95 | t.Errorf("(res): expected %d, but found %d", sdk.SSPluginSuccess, res) 96 | } else if handle.Value() != nil { 97 | t.Errorf("(value): expected %d, but found %d", unsafe.Pointer(nil), handle.Value()) 98 | } 99 | handle.Delete() 100 | 101 | // error 102 | SetOnInit(func(config string) (sdk.PluginState, error) { 103 | return nil, errTest 104 | }) 105 | in.config = (*_Ctype_char)(cStr.CharPtr()) 106 | handle = cgo.Handle(plugin_init(&in, &res)) 107 | if res != sdk.SSPluginFailure { 108 | t.Errorf("(res): expected %d, but found %d", sdk.SSPluginFailure, res) 109 | } 110 | val, ok := handle.Value().(sdk.LastError) 111 | if !ok { 112 | t.Errorf("(value): should implement sdk.LastError") 113 | } else if val.LastError() != errTest { 114 | t.Errorf("(err): expected %s, but found %s", errTest.Error(), val.LastError().Error()) 115 | } 116 | handle.Delete() 117 | 118 | // success 119 | state := &sampleInitialize{} 120 | SetOnInit(func(config string) (sdk.PluginState, error) { 121 | return state, nil 122 | }) 123 | in.config = (*_Ctype_char)(cStr.CharPtr()) 124 | handle = cgo.Handle(plugin_init(&in, &res)) 125 | if res != sdk.SSPluginSuccess { 126 | t.Errorf("(res): expected %d, but found %d", sdk.SSPluginSuccess, res) 127 | } else if handle.Value() != state { 128 | t.Errorf("(value): expected %d, but found %d", unsafe.Pointer(state), handle.Value()) 129 | } else if state.ExtractRequests() == nil { 130 | t.Errorf("expected SetExtractRequests() to be called") 131 | } 132 | 133 | // destroy 134 | plugin_destroy(_Ctype_uintptr_t(handle)) 135 | if !state.destroyCalled { 136 | t.Errorf("expected Destroy() to be called") 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /pkg/sdk/internal/sdk/inmemory.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package sdk 19 | 20 | import ( 21 | "bytes" 22 | "io" 23 | "unsafe" 24 | 25 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk" 26 | ) 27 | 28 | // InMemoryExtractRequest is an in-memory implementation of 29 | // sdk.ExtractRequest that allows changing its internal values. 30 | type InMemoryExtractRequest struct { 31 | ValFieldID uint64 32 | ValFieldType uint32 33 | ValField string 34 | ValArgKey string 35 | ValArgIndex uint64 36 | ValArgPresent bool 37 | ValIsList bool 38 | ValValue interface{} 39 | ValPtr unsafe.Pointer 40 | } 41 | 42 | func (i *InMemoryExtractRequest) FieldID() uint64 { 43 | return i.ValFieldID 44 | } 45 | 46 | func (i *InMemoryExtractRequest) FieldType() uint32 { 47 | return i.ValFieldType 48 | } 49 | 50 | func (i *InMemoryExtractRequest) Field() string { 51 | return i.ValField 52 | } 53 | 54 | func (i *InMemoryExtractRequest) ArgKey() string { 55 | return i.ValArgKey 56 | } 57 | 58 | func (i *InMemoryExtractRequest) ArgIndex() uint64 { 59 | return i.ValArgIndex 60 | } 61 | 62 | func (i *InMemoryExtractRequest) ArgPresent() bool { 63 | return i.ValArgPresent 64 | } 65 | 66 | func (i *InMemoryExtractRequest) IsList() bool { 67 | return i.ValIsList 68 | } 69 | 70 | func (i *InMemoryExtractRequest) SetValue(v interface{}) { 71 | i.ValValue = v 72 | } 73 | 74 | func (i *InMemoryExtractRequest) SetPtr(ptr unsafe.Pointer) { 75 | i.ValPtr = ptr 76 | } 77 | 78 | // InMemoryExtractRequestPool is an in-memory implementation of 79 | // sdk.ExtractRequestPool that allows changing its internal values. 80 | type InMemoryExtractRequestPool struct { 81 | Requests map[int]sdk.ExtractRequest 82 | } 83 | 84 | func (i *InMemoryExtractRequestPool) Get(requestIndex int) sdk.ExtractRequest { 85 | return i.Requests[requestIndex] 86 | } 87 | 88 | func (i *InMemoryExtractRequest) Free() { 89 | // do nothing 90 | } 91 | 92 | // InMemoryEventWriter is an in-memory implementation of 93 | // sdk.EventWriter that allows changing its internal values. 94 | type InMemoryEventWriter struct { 95 | Buffer bytes.Buffer 96 | ValTimestamp uint64 97 | } 98 | 99 | func (i *InMemoryEventWriter) Writer() io.Writer { 100 | i.Buffer.Reset() 101 | return &i.Buffer 102 | } 103 | 104 | func (i *InMemoryEventWriter) SetTimestamp(value uint64) { 105 | i.ValTimestamp = value 106 | } 107 | 108 | // InMemoryEventWriters is an in-memory implementation of 109 | // sdk.EventWriters that allows changing its internal values. 110 | type InMemoryEventWriters struct { 111 | Writers []sdk.EventWriter 112 | ValArrayPtr unsafe.Pointer 113 | OnFree func() 114 | } 115 | 116 | func (i *InMemoryEventWriters) Get(eventIndex int) sdk.EventWriter { 117 | return i.Writers[eventIndex] 118 | } 119 | 120 | func (i *InMemoryEventWriters) Len() int { 121 | return len(i.Writers) 122 | } 123 | 124 | func (i *InMemoryEventWriters) ArrayPtr() unsafe.Pointer { 125 | return i.ValArrayPtr 126 | } 127 | 128 | func (i *InMemoryEventWriters) Free() { 129 | if i.OnFree != nil { 130 | i.OnFree() 131 | } 132 | } 133 | 134 | // InMemoryEventReader is an in-memory implementation of 135 | // sdk.EventReader that allows changing its internal values. 136 | type InMemoryEventReader struct { 137 | Buffer []byte 138 | ValEventNum uint64 139 | ValTimestamp uint64 140 | } 141 | 142 | func (i *InMemoryEventReader) EventNum() uint64 { 143 | return i.ValEventNum 144 | } 145 | 146 | func (i *InMemoryEventReader) Timestamp() uint64 { 147 | return i.ValTimestamp 148 | } 149 | 150 | func (i *InMemoryEventReader) Reader() io.ReadSeeker { 151 | return bytes.NewReader(i.Buffer) 152 | } 153 | -------------------------------------------------------------------------------- /examples/extractor/extractor.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | // This plugin is a simple example of plugin with field extraction capability. 19 | // The plugin extracts the "example.ts" field from the "example" event source, 20 | // which simply represents the timestamp of the extraction. 21 | package main 22 | 23 | import ( 24 | "fmt" 25 | "time" 26 | 27 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk" 28 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk/plugins" 29 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk/plugins/extractor" 30 | ) 31 | 32 | // Defining a type for the plugin. 33 | // Composing the struct with plugins.BasePlugin is the recommended practice 34 | // as it provides the boilerplate code that satisfies most of the interface 35 | // requirements of the SDK. 36 | // 37 | // State variables to store in the plugin must be defined here. 38 | // In this simple example, we don't need any state. 39 | type MyPlugin struct { 40 | plugins.BasePlugin 41 | } 42 | 43 | // The plugin must be registered to the SDK in the init() function. 44 | // The extractor.Register function initializes our plugin as an extractor 45 | // plugin. This requires our plugin to implement the extractor.Plugin 46 | // interface, so compilation will fail if the mandatory methods are not 47 | // implemented. 48 | func init() { 49 | plugins.SetFactory(func() plugins.Plugin { 50 | p := &MyPlugin{} 51 | extractor.Register(p) 52 | return p 53 | }) 54 | } 55 | 56 | // Info returns a pointer to a plugin.Info struct, containing all the 57 | // general information about this plugin. 58 | // This method is mandatory. 59 | func (m *MyPlugin) Info() *plugins.Info { 60 | return &plugins.Info{ 61 | ID: 999, 62 | Name: "extractor-example", 63 | Description: "An Extractor Plugin Example", 64 | Contact: "github.com/falcosecurity/plugin-sdk-go/", 65 | Version: "0.1.0", 66 | ExtractEventSources: []string{"example"}, 67 | } 68 | } 69 | 70 | // Init initializes this plugin with a given config string, which is unused 71 | // in this example. This method is mandatory. 72 | func (m *MyPlugin) Init(config string) error { 73 | return nil 74 | } 75 | 76 | // Fields return the list of extractor fields exported by this plugin. 77 | // This method is mandatory for the field extraction capability. 78 | func (m *MyPlugin) Fields() []sdk.FieldEntry { 79 | return []sdk.FieldEntry{ 80 | {Type: "uint64", Name: "example.ts", Display: "Current Timestamp", Desc: "The current timestamp"}, 81 | } 82 | } 83 | 84 | // Extract extracts the value of a single field from a given event data. 85 | // This method is mandatory for the field extraction capability. 86 | func (m *MyPlugin) Extract(req sdk.ExtractRequest, evt sdk.EventReader) error { 87 | switch req.FieldID() { 88 | case 0: 89 | req.SetValue(uint64(time.Now().UnixNano())) 90 | return nil 91 | default: 92 | return fmt.Errorf("unsupported field: %s", req.Field()) 93 | } 94 | } 95 | 96 | // Destroy is gets called by the SDK when the plugin gets deinitialized. 97 | // This is useful to release any open resource used by the plugin. 98 | // This method is optional. 99 | // func (m *MyPlugin) Destroy() { 100 | // 101 | // } 102 | 103 | // InitSchema is gets called by the SDK before initializing the plugin. 104 | // This returns a schema representing the configuration expected by the 105 | // plugin to be passed to the Init() method. Defining InitSchema() allows 106 | // the framework to automatically validate the configuration, so that the 107 | // plugin can assume that it to be always be well-formed when passed to Init(). 108 | // This is ignored if the return value is nil. The returned schema must follow 109 | // the JSON Schema specific. See: https://json-schema.org/ 110 | // This method is optional. 111 | // func (m *MyPlugin) InitSchema() *sdk.SchemaInfo { 112 | // 113 | // } 114 | 115 | func main() {} 116 | -------------------------------------------------------------------------------- /pkg/sdk/symbols/initialize/initialize.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | // This package exports the following C functions: 19 | // - ss_plugin_t* plugin_init(char* config, int32_t* rc) 20 | // - void* plugin_destroy(ss_plugin_t* s) 21 | // 22 | // The exported plugin_init calls the function set with SetOnInit, which 23 | // returns a sdk.PluginState interface. If the return value implements the 24 | // sdk.ExtractRequests interface, the function checks if an instance of 25 | // sdk.ExtractRequestPool has already been set. If not, a default 26 | // one is created on the fly and set with the SetExtractRequests method. 27 | // 28 | // The exported plugin_destroy requires s to be a handle 29 | // of cgo.Handle from this SDK. If the value of the s handle implements 30 | // the sdk.Destroyer interface, the function calls its Destroy method. 31 | // If any of sdk.ExtractRequests, sdk.LastErrorBuffer, sdk.StringerBuffer, 32 | // or sdk.ProgresserBuffer, are implemented, the function calls the Free method 33 | // on the returned sdk.StringBuffer. Finally, the function deletes the 34 | // s cgo.Handle. 35 | // 36 | // This function is part of the plugin_api interface as defined in plugin_api.h. 37 | // In almost all cases, your plugin should import this module, unless your 38 | // plugin exports those symbols by other means. 39 | package initialize 40 | 41 | /* 42 | #include "../../plugin_api.h" 43 | */ 44 | import "C" 45 | import ( 46 | "github.com/falcosecurity/plugin-sdk-go/pkg/cgo" 47 | "github.com/falcosecurity/plugin-sdk-go/pkg/ptr" 48 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk" 49 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk/internal/hooks" 50 | ) 51 | 52 | type baseInit struct { 53 | lastErr error 54 | lastErrBuf ptr.StringBuffer 55 | } 56 | 57 | func (b *baseInit) LastError() error { 58 | return b.lastErr 59 | } 60 | 61 | func (b *baseInit) SetLastError(err error) { 62 | b.lastErr = err 63 | } 64 | 65 | func (b *baseInit) LastErrorBuffer() sdk.StringBuffer { 66 | return &b.lastErrBuf 67 | } 68 | 69 | // OnInitFn is a callback used in plugin_init. 70 | type OnInitFn func(config string) (sdk.PluginState, error) 71 | 72 | var ( 73 | onInitFn OnInitFn = func(config string) (sdk.PluginState, error) { return &baseInit{}, nil } 74 | ) 75 | 76 | // SetOnInit sets an initialization callback to be called in plugin_init to 77 | // create the plugin state. If never set, a default one is provided internally. 78 | func SetOnInit(fn OnInitFn) { 79 | if fn == nil { 80 | panic("plugin-sdk-go/sdk/symbols/initialize.SetOnInit: fn must not be nil") 81 | } 82 | onInitFn = fn 83 | } 84 | 85 | //export plugin_init 86 | func plugin_init(in *C.ss_plugin_init_input, rc *int32) C.uintptr_t { 87 | var state sdk.PluginState 88 | var err error 89 | 90 | // todo(jasondellaluce,therealbobo): support table access and owner operations 91 | state, err = onInitFn(C.GoString(in.config)) 92 | if err != nil { 93 | state = &baseInit{} 94 | state.(sdk.LastError).SetLastError(err) 95 | *rc = sdk.SSPluginFailure 96 | } else { 97 | // this allows a nil state 98 | extrReqs, ok := state.(sdk.ExtractRequests) 99 | if ok && extrReqs.ExtractRequests() == nil { 100 | extrReqs.SetExtractRequests(sdk.NewExtractRequestPool()) 101 | } 102 | *rc = sdk.SSPluginSuccess 103 | } 104 | 105 | handle := cgo.NewHandle(state) 106 | if *rc == sdk.SSPluginSuccess { 107 | hooks.OnAfterInit()(handle) 108 | } 109 | 110 | return (C.uintptr_t)(handle) 111 | } 112 | 113 | //export plugin_destroy 114 | func plugin_destroy(pState C.uintptr_t) { 115 | if pState != 0 { 116 | handle := cgo.Handle(pState) 117 | hooks.OnBeforeDestroy()(handle) 118 | if state, ok := handle.Value().(sdk.Destroyer); ok { 119 | state.Destroy() 120 | } 121 | if state, ok := handle.Value().(sdk.ExtractRequests); ok { 122 | state.ExtractRequests().Free() 123 | state.SetExtractRequests(nil) 124 | } 125 | if state, ok := handle.Value().(sdk.LastErrorBuffer); ok { 126 | state.LastErrorBuffer().Free() 127 | } 128 | if state, ok := handle.Value().(sdk.StringerBuffer); ok { 129 | state.StringerBuffer().Free() 130 | } 131 | if state, ok := handle.Value().(sdk.ProgressBuffer); ok { 132 | state.ProgressBuffer().Free() 133 | } 134 | handle.Delete() 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /pkg/sdk/symbols/extract/extract_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package extract 19 | 20 | import ( 21 | "errors" 22 | "testing" 23 | "time" 24 | 25 | "github.com/falcosecurity/plugin-sdk-go/pkg/cgo" 26 | "github.com/falcosecurity/plugin-sdk-go/pkg/ptr" 27 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk" 28 | ) 29 | 30 | var errTest = errors.New("testErr") 31 | 32 | type sampleExtract struct { 33 | reqs sdk.ExtractRequestPool 34 | err error 35 | lastErr error 36 | } 37 | 38 | func (s *sampleExtract) ExtractRequests() sdk.ExtractRequestPool { 39 | return s.reqs 40 | } 41 | 42 | func (s *sampleExtract) SetExtractRequests(reqs sdk.ExtractRequestPool) { 43 | s.reqs = reqs 44 | } 45 | 46 | func (s *sampleExtract) Extract(req sdk.ExtractRequest, evt sdk.EventReader) error { 47 | return s.err 48 | } 49 | 50 | func (s *sampleExtract) SetLastError(err error) { 51 | s.lastErr = err 52 | } 53 | 54 | func (s *sampleExtract) LastError() error { 55 | return s.lastErr 56 | } 57 | 58 | func allocSSPluginExtractField(fid, ftype uint32, fname, farg string) (*_Ctype_ss_plugin_extract_field, func()) { 59 | ret := &_Ctype_ss_plugin_extract_field{} 60 | ret.field_id = _Ctype_uint32_t(fid) 61 | ret.ftype = _Ctype_uint32_t(ftype) 62 | 63 | argBuf := ptr.StringBuffer{} 64 | fnameBuf := ptr.StringBuffer{} 65 | fnameBuf.Write(fname) 66 | ret.field = (*_Ctype_char)(fnameBuf.CharPtr()) 67 | if len(farg) > 0 { 68 | argBuf.Write(farg) 69 | ret.arg_key = (*_Ctype_char)(argBuf.CharPtr()) 70 | } else { 71 | ret.arg_key = nil 72 | } 73 | 74 | return ret, func() { 75 | argBuf.Free() 76 | fnameBuf.Free() 77 | } 78 | } 79 | 80 | func allocSSPluginExtractValueOffsets(start *uint32, length *uint32) (*_Ctype_ss_plugin_extract_value_offsets) { 81 | ret := &_Ctype_ss_plugin_extract_value_offsets{} 82 | ret.start = (*_Ctype_uint32_t)(start) 83 | ret.length = (*_Ctype_uint32_t)(length) 84 | 85 | return ret 86 | } 87 | 88 | func allocSSPluginEvent(num, ts uint64, data []byte) (*_Ctype_struct_ss_plugin_event_input, func()) { 89 | ret := &_Ctype_struct_ss_plugin_event_input{} 90 | evts, _ := sdk.NewEventWriters(1, int64(len(data))) 91 | evt := evts.Get(0) 92 | evt.Writer().Write(data) 93 | ret.evt = *(**_Ctype_struct_ss_plugin_event)(evts.ArrayPtr()) 94 | ret.evtnum = _Ctype_uint64_t(num) 95 | 96 | return ret, func() { 97 | evts.Free() 98 | } 99 | } 100 | 101 | func assertPanic(t *testing.T, fun func()) { 102 | defer func() { 103 | if r := recover(); r == nil { 104 | t.Errorf("expected panic") 105 | } 106 | }() 107 | fun() 108 | } 109 | 110 | func TestExtract(t *testing.T) { 111 | var res int32 112 | sample := &sampleExtract{} 113 | handle := cgo.NewHandle(sample) 114 | defer handle.Delete() 115 | reqs := sdk.NewExtractRequestPool() 116 | defer reqs.Free() 117 | sample.reqs = reqs 118 | 119 | // Alloc c structs 120 | evtData := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9} 121 | event, freeEvent := allocSSPluginEvent(1, uint64(time.Now().UnixNano()), evtData) 122 | defer freeEvent() 123 | field, freeField := allocSSPluginExtractField(1, sdk.FieldTypeUint64, "test.field", "") 124 | defer freeField() 125 | 126 | // panic 127 | badHandle := cgo.NewHandle(1) 128 | assertPanic(t, func() { 129 | plugin_extract_fields_sync(_Ctype_uintptr_t(badHandle), event, 1, field, nil) 130 | }) 131 | 132 | // success 133 | res = plugin_extract_fields_sync(_Ctype_uintptr_t(handle), event, 1, field, nil) 134 | if res != sdk.SSPluginSuccess { 135 | t.Errorf("(res): expected %d, but found %d", sdk.SSPluginSuccess, res) 136 | } else if sample.lastErr != nil { 137 | t.Errorf("(lastErr): should be nil") 138 | } 139 | 140 | // success + offsets 141 | val_start := uint32(0) 142 | val_length := uint32(8) 143 | offsets := allocSSPluginExtractValueOffsets(&val_start, &val_length) 144 | res = plugin_extract_fields_sync(_Ctype_uintptr_t(handle), event, 1, field, offsets) 145 | if res != sdk.SSPluginSuccess { 146 | t.Errorf("(res): expected %d, but found %d", sdk.SSPluginSuccess, res) 147 | } else if sample.lastErr != nil { 148 | t.Errorf("(lastErr): should be nil") 149 | } 150 | 151 | // error 152 | sample.err = errTest 153 | res = plugin_extract_fields_sync(_Ctype_uintptr_t(handle), event, 1, field, nil) 154 | if res != sdk.SSPluginFailure { 155 | t.Errorf("(res): expected %d, but found %d", sdk.SSPluginFailure, res) 156 | } else if sample.lastErr != errTest { 157 | t.Errorf("(lastErr): expected %s, but found %s", errTest.Error(), sample.lastErr.Error()) 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /examples/source/source.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | // This plugin is a simple example of plugin with event sourcing capability. 19 | // The plugin produces events of the "example" data source containing 20 | // a single uint64 representing the incrementing value of a counter, 21 | // serialized using a encoding/gob encoder. 22 | // This plugin makes use of the SDK-provided "pull" source instance to 23 | // open the event source, so we'll not provide a type implementation of 24 | // the source.Instance interface here 25 | package main 26 | 27 | import ( 28 | "context" 29 | "encoding/gob" 30 | "fmt" 31 | "time" 32 | 33 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk" 34 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk/plugins" 35 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk/plugins/source" 36 | ) 37 | 38 | // Defining a type for the plugin. 39 | // Composing the struct with plugins.BasePlugin is the recommended practice 40 | // as it provides the boilerplate code that satisfies most of the interface 41 | // requirements of the SDK. 42 | // 43 | // State variables to store in the plugin must be defined here. 44 | // In this simple example, we store the configuration string passed by the 45 | // SDK during the plugin initialization. 46 | type MyPlugin struct { 47 | plugins.BasePlugin 48 | config string 49 | } 50 | 51 | // The plugin must be registered to the SDK in the init() function. 52 | // The source.Register function initializes our plugin as an source 53 | // plugin. This requires our plugin to implement the source.Plugin 54 | // interface, so compilation will fail if the mandatory methods are not 55 | // implemented. 56 | func init() { 57 | plugins.SetFactory(func() plugins.Plugin { 58 | p := &MyPlugin{} 59 | source.Register(p) 60 | return p 61 | }) 62 | } 63 | 64 | // Info returns a pointer to a plugin.Info struct, containing all the 65 | // general information about this plugin. 66 | // This method is mandatory. 67 | func (m *MyPlugin) Info() *plugins.Info { 68 | return &plugins.Info{ 69 | ID: 999, 70 | Name: "source-example", 71 | Description: "A Source Plugin Example", 72 | Contact: "github.com/falcosecurity/plugin-sdk-go/", 73 | Version: "0.1.0", 74 | EventSource: "example", 75 | } 76 | } 77 | 78 | // Init initializes this plugin with a given config string, which is unused 79 | // in this example. This method is mandatory. 80 | func (m *MyPlugin) Init(config string) error { 81 | m.config = config 82 | return nil 83 | } 84 | 85 | // Open opens the plugin source and starts a new capture session (e.g. stream 86 | // of events). This uses the SDK built-in source.NewPullInstance() function 87 | // that allows creating an event source by simply providing a event-generating 88 | // callback. This method is mandatory for the event sourcing capability. 89 | func (m *MyPlugin) Open(params string) (source.Instance, error) { 90 | counter := uint64(0) 91 | pull := func(ctx context.Context, evt sdk.EventWriter) error { 92 | counter++ 93 | if err := gob.NewEncoder(evt.Writer()).Encode(counter); err != nil { 94 | return err 95 | } 96 | evt.SetTimestamp(uint64(time.Now().UnixNano())) 97 | return nil 98 | } 99 | return source.NewPullInstance(pull) 100 | } 101 | 102 | // String produces a string representation of an event data produced by the 103 | // event source of this plugin. 104 | // This method is optional for the event sourcing capability. 105 | func (m *MyPlugin) String(evt sdk.EventReader) (string, error) { 106 | var value uint64 107 | encoder := gob.NewDecoder(evt.Reader()) 108 | if err := encoder.Decode(&value); err != nil { 109 | return "", err 110 | } 111 | return fmt.Sprintf("counter: %d", value), nil 112 | } 113 | 114 | // Destroy is gets called by the SDK when the plugin gets deinitialized. 115 | // This is useful to release any open resource used by the plugin. 116 | // This method is optional. 117 | // func (m *MyPlugin) Destroy() { 118 | // 119 | // } 120 | 121 | // InitSchema is gets called by the SDK before initializing the plugin. 122 | // This returns a schema representing the configuration expected by the 123 | // plugin to be passed to the Init() method. Defining InitSchema() allows 124 | // the framework to automatically validate the configuration, so that the 125 | // plugin can assume that it to be always be well-formed when passed to Init(). 126 | // This is ignored if the return value is nil. The returned schema must follow 127 | // the JSON Schema specific. See: https://json-schema.org/ 128 | // This method is optional. 129 | // func (m *MyPlugin) InitSchema() *sdk.SchemaInfo { 130 | // 131 | // } 132 | 133 | func main() {} 134 | -------------------------------------------------------------------------------- /pkg/cgo/handle.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package cgo 19 | 20 | import ( 21 | "fmt" 22 | "sync/atomic" 23 | "unsafe" 24 | ) 25 | 26 | // Handle is an alternative implementation of cgo.Handle introduced by 27 | // Go 1.17, see https://pkg.go.dev/runtime/cgo. This implementation 28 | // optimizes performance in use cases related to plugins. It is intended 29 | // to be used both as a replacement and as a polyfill for Go versions 30 | // that miss it. 31 | // 32 | // As the original implementation, this provides a way to pass values that 33 | // contain Go pointers between Go and C without breaking the cgo pointer 34 | // passing rules. The underlying type of Handle is guaranteed to fit in 35 | // an integer type that is large enough to hold the bit pattern of any pointer. 36 | // The zero value of a Handle is not valid and thus is safe to use as 37 | // a sentinel in C APIs. 38 | // 39 | // The performance optimization comes with a limitation: the maximum number 40 | // of valid handles is capped to a fixed value (see MaxHandle). 41 | // However, since the intended usage is to pass opaque pointers holding the 42 | // plugin states (usually at most two pointers per one instance of a plugin), 43 | // this hard limit is considered acceptable. 44 | // 45 | // The thread-safety guarantees have been dropped for further 46 | // performance improvements. The current version of the Plugin API does not 47 | // require thread safety. 48 | // 49 | // The usage in other contexts is discuraged. 50 | type Handle uintptr 51 | 52 | const ( 53 | // MaxHandle is the largest value that an Handle can hold 54 | MaxHandle = 256 - 1 55 | 56 | // max number of times we're willing to iterate over the vector of reusable 57 | // handles to do compare-and-swap before giving up 58 | maxNewHandleRounds = 20 59 | ) 60 | 61 | var ( 62 | handles [MaxHandle + 1]unsafe.Pointer // [int]*interface{} 63 | noHandle unsafe.Pointer = nil 64 | ) 65 | 66 | func init() { 67 | resetHandles() 68 | } 69 | 70 | // NewHandle returns a handle for a given value. 71 | // 72 | // The handle is valid until the program calls Delete on it. The handle 73 | // uses resources, and this package assumes that C code may hold on to 74 | // the handle, so a program must explicitly call Delete when the handle 75 | // is no longer needed. Programs must not retain deleted handles. 76 | // 77 | // The intended use is to pass the returned handle to C code, which 78 | // passes it back to Go, which calls Value. 79 | // 80 | // The simultaneous number of the valid handles cannot exceed MaxHandle. 81 | // This function panics if there are no more handles available. 82 | // Previously created handles may be made available again when 83 | // invalidated with Delete. 84 | // 85 | // This function is not thread-safe. 86 | func NewHandle(v interface{}) Handle { 87 | rounds := 0 88 | for h := uintptr(1); ; h++ { 89 | // we acquired ownership of an handle, return it 90 | // note: we attempt accessing slots 1..MaxHandle (included) 91 | if atomic.CompareAndSwapPointer(&handles[h], noHandle, (unsafe.Pointer)(&v)) { 92 | return Handle(h) 93 | } 94 | 95 | // we haven't acquired a handle, but we can try with the next one 96 | if h < MaxHandle { 97 | continue 98 | } 99 | 100 | // we iterated over the whole vector of handles, so we get back to start 101 | // and try again with another round. Once we do this too many times, 102 | // we have no choice if not panic-ing 103 | h = uintptr(0) // note: will be incremented when continuing 104 | if rounds < maxNewHandleRounds { 105 | rounds++ 106 | continue 107 | } 108 | 109 | panic(fmt.Sprintf("plugin-sdk-go/cgo: could not obtain a new handle after round #%d", rounds)) 110 | } 111 | } 112 | 113 | // Value returns the associated Go value for a valid handle. 114 | // 115 | // The method panics if the handle is invalid. 116 | // This function is not thread-safe. 117 | func (h Handle) Value() interface{} { 118 | if h > MaxHandle || atomic.LoadPointer(&handles[h]) == noHandle { 119 | panic(fmt.Sprintf("plugin-sdk-go/cgo: misuse (value) of an invalid Handle %d", h)) 120 | } 121 | return *(*interface{})(atomic.LoadPointer(&handles[h])) 122 | } 123 | 124 | // Delete invalidates a handle. This method should only be called once 125 | // the program no longer needs to pass the handle to C and the C code 126 | // no longer has a copy of the handle value. 127 | // 128 | // The method panics if the handle is invalid. 129 | // This function is not thread-safe. 130 | func (h Handle) Delete() { 131 | if h > MaxHandle || atomic.LoadPointer(&handles[h]) == noHandle { 132 | panic(fmt.Sprintf("plugin-sdk-go/cgo: misuse (delete) of an invalid Handle %d", h)) 133 | } 134 | atomic.StorePointer(&handles[h], noHandle) 135 | } 136 | 137 | func resetHandles() { 138 | for i := 0; i <= MaxHandle; i++ { 139 | atomic.StorePointer(&handles[i], noHandle) 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /pkg/sdk/symbols/info/info.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | // This package exports a set of C functions that provide general 19 | // information about the plugin. The exported functions are: 20 | // 21 | // uint32_t get_type(); 22 | // uint32_t get_id(); 23 | // char* get_name(); 24 | // char* get_description(); 25 | // char* get_contact(); 26 | // char* get_version(); 27 | // char* get_required_api_version(); 28 | // char* get_event_source(); 29 | // char* get_extract_event_sources(); 30 | // 31 | // In almost all cases, your plugin should import this module, unless 32 | // your plugin exports those symbols by other means. 33 | package info 34 | 35 | /* 36 | #include "info.h" 37 | */ 38 | import "C" 39 | import ( 40 | "encoding/json" 41 | "strings" 42 | 43 | "github.com/falcosecurity/plugin-sdk-go/pkg/ptr" 44 | ) 45 | 46 | var ( 47 | pType uint32 48 | pId uint32 49 | pName ptr.StringBuffer 50 | pDescription ptr.StringBuffer 51 | pContact ptr.StringBuffer 52 | pVersion ptr.StringBuffer 53 | pRequiredAPIVersion ptr.StringBuffer 54 | pEventSource ptr.StringBuffer 55 | pExtractEventSources ptr.StringBuffer 56 | ) 57 | 58 | //export plugin_get_id 59 | func plugin_get_id() uint32 { 60 | return pId 61 | } 62 | 63 | func SetId(id uint32) { 64 | pId = id 65 | } 66 | 67 | //export plugin_get_name 68 | func plugin_get_name() *C.char { 69 | return (*C.char)(pName.CharPtr()) 70 | } 71 | 72 | func SetName(name string) { 73 | pName.Write(name) 74 | } 75 | 76 | //export plugin_get_description 77 | func plugin_get_description() *C.char { 78 | return (*C.char)(pDescription.CharPtr()) 79 | } 80 | 81 | func SetDescription(desc string) { 82 | pDescription.Write(desc) 83 | } 84 | 85 | //export plugin_get_contact 86 | func plugin_get_contact() *C.char { 87 | return (*C.char)(pContact.CharPtr()) 88 | } 89 | 90 | func SetContact(contact string) { 91 | pContact.Write(contact) 92 | } 93 | 94 | //export plugin_get_version 95 | func plugin_get_version() *C.char { 96 | return (*C.char)(pVersion.CharPtr()) 97 | } 98 | 99 | func SetVersion(version string) { 100 | pVersion.Write(version) 101 | } 102 | 103 | //export plugin_get_required_api_version 104 | func plugin_get_required_api_version() *C.char { 105 | if pRequiredAPIVersion.String() == "" { 106 | return C.get_default_required_api_version() 107 | } 108 | return (*C.char)(pRequiredAPIVersion.CharPtr()) 109 | } 110 | 111 | func splitVersionString(version string) (string, string, string) { 112 | nums := strings.Split(version, ".") 113 | if len(nums) != 3 { 114 | panic("Incorrect format. Expected: Semantic Versioning: X.Y.Z") 115 | } 116 | return nums[0], nums[1], nums[2] 117 | } 118 | 119 | func SetRequiredAPIVersion(apiVer string) { 120 | if apiVer != "" { 121 | pluginRequiredMajor, pluginRequiredMinor, pluginRequiredPatch := splitVersionString(apiVer) 122 | sdkRequiredMajor, sdkRequiredMinor, sdkRequiredPatch := splitVersionString(C.GoString(C.get_default_required_api_version())) 123 | 124 | // The plugin should always require a version lower or equal to the one required by the SDK 125 | // because the SDK couldn't support features coming from new framework versions. 126 | // On the other side the plugin could require a lower version because maybe it doesn't 127 | // need all features provided by the framework. 128 | if sdkRequiredMajor != pluginRequiredMajor { 129 | panic("Incompatible required Major version between SDK and the plugin. Major SDK version is equal to " + sdkRequiredMajor + " but the plugin uses " + pluginRequiredMajor + ". The 2 Major versions should be equal.") 130 | } 131 | if sdkRequiredMinor < pluginRequiredMinor { 132 | panic("The plugin requires a Minor version greater than the SDK one. Minor SDK version is equal to " + sdkRequiredMinor + " but the plugin uses " + pluginRequiredMinor + ". The plugin should always require a Minor version lower or equal to the SDK one.") 133 | } 134 | if sdkRequiredMinor == pluginRequiredMinor && sdkRequiredPatch < pluginRequiredPatch { 135 | panic("The plugin requires a Patch version greater than the SDK one. Patch SDK version is equal to " + sdkRequiredPatch + " but the plugin uses " + pluginRequiredPatch + ". The plugin should always require a Patch version lower or equal to the SDK one.") 136 | } 137 | } 138 | pRequiredAPIVersion.Write(apiVer) 139 | } 140 | 141 | //export plugin_get_event_source 142 | func plugin_get_event_source() *C.char { 143 | return (*C.char)(pEventSource.CharPtr()) 144 | } 145 | 146 | func SetEventSource(source string) { 147 | pEventSource.Write(source) 148 | } 149 | 150 | //export plugin_get_extract_event_sources 151 | func plugin_get_extract_event_sources() *C.char { 152 | if pExtractEventSources.String() == "" { 153 | pExtractEventSources.Write("[]") 154 | } 155 | return (*C.char)(pExtractEventSources.CharPtr()) 156 | } 157 | 158 | func SetExtractEventSources(sources []string) { 159 | if len(sources) == 0 { 160 | pExtractEventSources.Write("[]") 161 | } else if b, err := json.Marshal(sources); err != nil { 162 | panic(err) 163 | } else { 164 | pExtractEventSources.Write(string(b)) 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /pkg/loader/strlcpy.patch: -------------------------------------------------------------------------------- 1 | diff --git a/pkg/loader/plugin_loader.c b/pkg/loader/plugin_loader.c 2 | index 2943335..7bebeeb 100644 3 | --- a/pkg/loader/plugin_loader.c 4 | +++ b/pkg/loader/plugin_loader.c 5 | @@ -24,22 +24,52 @@ typedef HINSTANCE library_handle_t; 6 | typedef void* library_handle_t; 7 | #endif 8 | 9 | -#include 10 | #include 11 | #include 12 | #include 13 | #include 14 | -#include 15 | +#include "plugin_loader.h" 16 | + 17 | +// note(jasondellaluce,therealbobo): implementation taken from falcosecurity/libs 18 | +// note(leogr): to avoid clashing with `strlcpy` introduced by glibc 2.38, 19 | +// the func has been renamed to plugin_loader_strlcpy. 20 | +// N.B.: our building system here is not smart enough to detect if the function 21 | +// was declared already. 22 | +#include 23 | +#include 24 | +/*! 25 | + \brief Copy up to size - 1 characters from the NUL-terminated string src to dst, NUL-terminating the result. 26 | + 27 | + \return The length of the source string. 28 | +*/ 29 | + 30 | +static inline size_t plugin_loader_strlcpy(char *dst, const char *src, size_t size) { 31 | + size_t srcsize = strlen(src); 32 | + if (size == 0) { 33 | + return srcsize; 34 | + } 35 | + 36 | + size_t copysize = srcsize; 37 | + 38 | + if (copysize > size - 1) { 39 | + copysize = size - 1; 40 | + } 41 | + 42 | + memcpy(dst, src, copysize); 43 | + dst[copysize] = '\0'; 44 | + 45 | + return srcsize; 46 | +} 47 | 48 | static inline void err_prepend(char* s, const char* prefix, const char* sep) { 49 | char tmp[PLUGIN_MAX_ERRLEN]; 50 | - size_t prefix_len = strlcpy(tmp, prefix, PLUGIN_MAX_ERRLEN); 51 | + size_t prefix_len = plugin_loader_strlcpy(tmp, prefix, PLUGIN_MAX_ERRLEN); 52 | if(*s != '\0') { 53 | - strlcpy(&tmp[prefix_len], sep, PLUGIN_MAX_ERRLEN - prefix_len); 54 | + plugin_loader_strlcpy(&tmp[prefix_len], sep, PLUGIN_MAX_ERRLEN - prefix_len); 55 | prefix_len += strlen(sep); 56 | } 57 | - strlcpy(&tmp[prefix_len], s, PLUGIN_MAX_ERRLEN - prefix_len); 58 | - strlcpy(s, tmp, PLUGIN_MAX_ERRLEN); 59 | + plugin_loader_strlcpy(&tmp[prefix_len], s, PLUGIN_MAX_ERRLEN - prefix_len); 60 | + plugin_loader_strlcpy(s, tmp, PLUGIN_MAX_ERRLEN); 61 | } 62 | 63 | static inline void err_append(char* s, const char* suffix, const char* sep) { 64 | @@ -65,7 +95,7 @@ plugin_handle_t* plugin_load(const char* path, char* err) { 65 | err[0] = '\0'; 66 | plugin_handle_t* ret = (plugin_handle_t*)calloc(1, sizeof(plugin_handle_t)); 67 | if(!ret) { 68 | - strlcpy(err, "error allocating plugin handle", PLUGIN_MAX_ERRLEN); 69 | + plugin_loader_strlcpy(err, "error allocating plugin handle", PLUGIN_MAX_ERRLEN); 70 | return NULL; 71 | } 72 | 73 | @@ -77,14 +107,19 @@ plugin_handle_t* plugin_load(const char* path, char* err) { 74 | FORMAT_MESSAGE_IGNORE_INSERTS; 75 | LPTSTR msg_buf = 0; 76 | if(FormatMessageA(flg, 0, GetLastError(), 0, (LPTSTR)&msg_buf, 0, NULL) && msg_buf) { 77 | - strlcpy(err, msg_buf, PLUGIN_MAX_ERRLEN); 78 | + plugin_loader_strlcpy(err, msg_buf, PLUGIN_MAX_ERRLEN); 79 | LocalFree(msg_buf); 80 | } 81 | } 82 | #else 83 | - ret->handle = dlopen(path, RTLD_LAZY); 84 | +#ifdef __linux__ 85 | + int dlopen_flags = RTLD_NOW|RTLD_DEEPBIND; 86 | +#else 87 | + int dlopen_flags = RTLD_NOW; 88 | +#endif 89 | + ret->handle = dlopen(path, dlopen_flags); 90 | if(ret->handle == NULL) { 91 | - strlcpy(err, (const char*)dlerror(), PLUGIN_MAX_ERRLEN); 92 | + plugin_loader_strlcpy(err, (const char*)dlerror(), PLUGIN_MAX_ERRLEN); 93 | } 94 | #endif 95 | 96 | @@ -135,13 +170,13 @@ plugin_handle_t* plugin_load_api(const plugin_api* api, char* err) { 97 | // alloc and init memory 98 | err[0] = '\0'; 99 | if(!api) { 100 | - strlcpy(err, "can't allocate plugin handle with invalid API table", PLUGIN_MAX_ERRLEN); 101 | + plugin_loader_strlcpy(err, "can't allocate plugin handle with invalid API table", PLUGIN_MAX_ERRLEN); 102 | return NULL; 103 | } 104 | 105 | plugin_handle_t* ret = (plugin_handle_t*)calloc(1, sizeof(plugin_handle_t)); 106 | if(!ret) { 107 | - strlcpy(err, "error allocating plugin handle", PLUGIN_MAX_ERRLEN); 108 | + plugin_loader_strlcpy(err, "error allocating plugin handle", PLUGIN_MAX_ERRLEN); 109 | return NULL; 110 | } 111 | ret->api = *api; 112 | @@ -150,7 +185,7 @@ plugin_handle_t* plugin_load_api(const plugin_api* api, char* err) { 113 | uint32_t major, minor, patch; 114 | const char* ver; 115 | if(api->get_required_api_version == NULL) { 116 | - strlcpy(err, "plugin_get_required_api_version symbol not implemented", PLUGIN_MAX_ERRLEN); 117 | + plugin_loader_strlcpy(err, "plugin_get_required_api_version symbol not implemented", PLUGIN_MAX_ERRLEN); 118 | return NULL; 119 | } 120 | 121 | @@ -217,7 +252,7 @@ bool plugin_check_required_api_version(const plugin_handle_t* h, char* err) { 122 | uint32_t major, minor, patch; 123 | const char *ver, *failmsg; 124 | if(h->api.get_required_api_version == NULL) { 125 | - strlcpy(err, "plugin_get_required_api_version symbol not implemented", PLUGIN_MAX_ERRLEN); 126 | + plugin_loader_strlcpy(err, "plugin_get_required_api_version symbol not implemented", PLUGIN_MAX_ERRLEN); 127 | return false; 128 | } 129 | 130 | @@ -256,7 +291,7 @@ bool plugin_check_required_api_version(const plugin_handle_t* h, char* err) { 131 | 132 | plugin_caps_t plugin_get_capabilities(const plugin_handle_t* h, char* err) { 133 | plugin_caps_t caps = CAP_NONE; 134 | - strlcpy(err, "", PLUGIN_MAX_ERRLEN); 135 | + plugin_loader_strlcpy(err, "", PLUGIN_MAX_ERRLEN); 136 | 137 | if(h->api.open != NULL && h->api.close != NULL && h->api.next_batch != NULL) { 138 | bool has_id = h->api.get_id != NULL && h->api.get_id() != 0; 139 | -------------------------------------------------------------------------------- /benchmarks/async/bench.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | #define __STDC_FORMAT_MACROS 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "../../pkg/sdk/plugin_api.h" 28 | 29 | // defined in Go and exported from bench.go 30 | extern "C" 31 | { 32 | void plugin_destroy(ss_plugin_t*); 33 | ss_plugin_t* plugin_init(const ss_plugin_init_input *input, ss_plugin_rc *rc); 34 | ss_plugin_rc plugin_extract_fields(ss_plugin_t*, const ss_plugin_event_input*, const ss_plugin_field_extract_input*); 35 | } 36 | 37 | // global benchmark options 38 | static int g_parallelism; 39 | static int g_niterations; 40 | static bool g_use_async; 41 | 42 | static void print_help() 43 | { 44 | printf( 45 | "Usage: bench [options]\n\n" 46 | "Options:\n" 47 | " -h, --help Print this usage snippet.\n" 48 | " -a, --async Run the benchmark by enabling the async extraction optimization (default: off).\n" 49 | " -n The number of extraction requests performed in the benchmark (default: 10000).\n" 50 | " -p The number of plugins that run the benchmark in parallel (default: 1).\n"); 51 | } 52 | 53 | static void parse_options(int argc, char** argv) 54 | { 55 | g_parallelism = 1; 56 | g_niterations = 10000; 57 | g_use_async = false; 58 | 59 | for (int i = 1; i < argc; i++) 60 | { 61 | auto arg = std::string(argv[i]); 62 | if (arg == "-h" || arg == "--help" ) 63 | { 64 | print_help(); 65 | exit(0); 66 | } 67 | else if (arg == "-a" || arg == "--async" ) 68 | { 69 | g_use_async = true; 70 | } 71 | else if (arg == "-p" || arg == "-n") 72 | { 73 | int tmp; 74 | i++; 75 | if (i >= argc) 76 | { 77 | fprintf(stderr, "option '%s' requires a parameter\n", arg.c_str()); 78 | exit(1); 79 | } 80 | 81 | tmp = atoi(argv[i]); 82 | if (tmp <= 0) 83 | { 84 | fprintf(stderr, "option '%s' parameter must be a positive integer\n", arg.c_str()); 85 | exit(1); 86 | } 87 | 88 | if (arg == "-p") 89 | { 90 | g_parallelism = tmp; 91 | } 92 | else 93 | { 94 | g_niterations = tmp; 95 | } 96 | } 97 | else 98 | { 99 | fprintf(stderr, "unrecognized option '%s'\n", argv[i]); 100 | print_help(); 101 | exit(1); 102 | } 103 | } 104 | } 105 | 106 | static void benchmark(ss_plugin_t *plugin) noexcept 107 | { 108 | // craft a mock extract request 109 | ss_plugin_extract_field e; 110 | e.field_id = 0; 111 | e.field = "sample.field"; 112 | e.arg_present = false; 113 | e.ftype = FTYPE_UINT64; 114 | e.flist = false; 115 | ss_plugin_field_extract_input in; 116 | in.fields = &e; 117 | in.num_fields = 1; 118 | 119 | // request multiple extractions and compute total execution time 120 | auto start = std::chrono::high_resolution_clock::now(); 121 | ss_plugin_rc rc = SS_PLUGIN_FAILURE; 122 | for (int i = 0; i < g_niterations; i++) 123 | { 124 | rc = plugin_extract_fields(plugin, NULL, &in); 125 | if (rc != SS_PLUGIN_SUCCESS) 126 | { 127 | fprintf(stderr, "plugin %" PRIu64 ": plugin_extract_fields failure: %d\n", (uint64_t) plugin, rc); 128 | return; 129 | } 130 | } 131 | auto end = std::chrono::high_resolution_clock::now(); 132 | 133 | // print stats summary 134 | auto time_ns = std::chrono::duration_cast(end - start); 135 | printf("plugin %" PRIu64 ": %.02f ns/extraction (elapsed time %" PRIu64 "ns, extractions %d)\n", 136 | (uint64_t) plugin, 137 | (double) time_ns.count() / (double) (g_niterations), 138 | (uint64_t) time_ns.count(), 139 | g_niterations); 140 | } 141 | 142 | int main(int argc, char** argv) 143 | { 144 | // parse user options 145 | parse_options(argc, argv); 146 | 147 | // initialize plugins and launch a benchmark for each of them in parallel 148 | std::vector threads; 149 | std::vector plugins; 150 | for (int i = 0; i < g_parallelism; ++i) 151 | { 152 | ss_plugin_rc rc = SS_PLUGIN_FAILURE; 153 | ss_plugin_init_input in; 154 | in.config = g_use_async ? "async" : ""; 155 | plugins.push_back(plugin_init(&in, &rc)); 156 | if (rc != SS_PLUGIN_SUCCESS) 157 | { 158 | fprintf(stderr, "can't initialize plugin"); 159 | exit(1); 160 | } 161 | threads.push_back(std::thread(benchmark, plugins[i])); 162 | } 163 | 164 | // wait for all banchmarks to finish and destroy plugins 165 | for (int i = 0; i < g_parallelism; ++i) 166 | { 167 | if (threads[i].joinable()) 168 | { 169 | threads[i].join(); 170 | } 171 | plugin_destroy(plugins[i]); 172 | } 173 | 174 | return 0; 175 | } 176 | -------------------------------------------------------------------------------- /pkg/ptr/bytes.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package ptr 19 | 20 | /* 21 | #include 22 | #include 23 | */ 24 | import "C" 25 | import ( 26 | "fmt" 27 | "io" 28 | "reflect" 29 | "unsafe" 30 | ) 31 | 32 | const ( 33 | offsetErrorFmt = "invalid offset value %d" 34 | lengthErrorFmt = "invalid length value %d" 35 | capacityErrorFmt = "invalid capacity value %d" 36 | whenceErrorFmt = "invalid whence value %d" 37 | bufferErrorFmt = "invalid buffer value" 38 | ) 39 | 40 | // Integer limit values. 41 | // todo: math.MaxInt was introduced by golang 1.17 (see https://golang.org/doc/go1.17) 42 | const ( 43 | intSize = 32 << (^uint(0) >> 63) // 32 or 64 44 | maxInt = 1<<(intSize-1) - 1 45 | ) 46 | 47 | // BytesReadWriter is an opaque wrapper for fixed-size memory buffers, that can safely be 48 | // used in the plugin framework in a Go-friendly way. The purpose is to allow safe memory 49 | // access through the read/write interface primitives, regardless of how the buffer is 50 | // physically allocated under the hood. For instance, this can be used to wrap C-allocated 51 | // buffers to hide both the type conversion magic and prevent illegal memory operations. 52 | // 53 | // The io.ReadWriteSeeker interface is leveraged to implement the safe random memory 54 | // access semantic. Note, read-only or write-only access to the memory buffer 55 | // can easily be accomplished by casting instances of this interface to either a io.Reader 56 | // or a io.Writer. 57 | type BytesReadWriter interface { 58 | io.ReadWriteSeeker 59 | // 60 | // BufferPtr returns an unsafe.Pointer that points to the underlying memory buffer. 61 | BufferPtr() unsafe.Pointer 62 | // 63 | // Len returns the total number of accessible bytes for reading and writing. 64 | Len() int64 65 | // 66 | // SetLen sets the total number of accessible bytes for reading and writing. 67 | // The new length value should not be larger than the underlying memory buffer capacity. 68 | // If a greater value is given, the length is set to be equal to the capacity. 69 | // If a value less than zero is given, the length is set to be zero. 70 | SetLen(len int64) 71 | // 72 | // Offset returns the current cursor position relatively to the underlying buffer. 73 | // The cursor position represents the index of the next byte in the buffer that will 74 | // be available for read\write operations. This value is altered through the usage of 75 | // Seek, Read, and Write. By definition, we have that 0 <= Offset() <= Len(). 76 | Offset() int64 77 | } 78 | 79 | // NewBytesReadWriter creates a new instance of BytesReadWriter by wrapping the memory pointed 80 | // by the buffer argument. The length argument is the total number of accessible bytes 81 | // for reading and writing. The capacity argument is the number of bytes in the given buffer. 82 | // 83 | // Note that the capacity cannot be changed after creation, and that the length cannot ever exceed 84 | // the capacity. 85 | func NewBytesReadWriter(buffer unsafe.Pointer, length, capacity int64) (BytesReadWriter, error) { 86 | if buffer == nil { 87 | return nil, fmt.Errorf(bufferErrorFmt) 88 | } 89 | if capacity < 0 || capacity > maxInt { 90 | return nil, fmt.Errorf(capacityErrorFmt, capacity) 91 | } 92 | if length < 0 || length > capacity { 93 | return nil, fmt.Errorf(lengthErrorFmt, length) 94 | } 95 | // Inspired by: https://stackoverflow.com/a/66218124 96 | var bytes []byte 97 | (*reflect.SliceHeader)(unsafe.Pointer(&bytes)).Data = uintptr(buffer) 98 | (*reflect.SliceHeader)(unsafe.Pointer(&bytes)).Len = int(capacity) 99 | (*reflect.SliceHeader)(unsafe.Pointer(&bytes)).Cap = int(capacity) 100 | return &bytesReadWriter{ 101 | buffer: buffer, 102 | bytesAlias: bytes, 103 | offset: 0, 104 | len: length, 105 | }, nil 106 | } 107 | 108 | type bytesReadWriter struct { 109 | offset int64 110 | len int64 111 | buffer unsafe.Pointer 112 | bytesAlias []byte 113 | } 114 | 115 | func (b *bytesReadWriter) Read(p []byte) (n int, err error) { 116 | n = 0 117 | pLen := len(p) 118 | for i := 0; i < pLen; i++ { 119 | if b.offset >= b.len { 120 | err = io.EOF 121 | return 122 | } 123 | p[i] = b.bytesAlias[b.offset] 124 | b.offset++ 125 | n++ 126 | } 127 | return 128 | } 129 | 130 | func (b *bytesReadWriter) Write(p []byte) (n int, err error) { 131 | n = 0 132 | for _, v := range p { 133 | if b.offset >= b.len { 134 | err = io.ErrShortWrite 135 | return 136 | } 137 | b.bytesAlias[b.offset] = v 138 | b.offset++ 139 | n++ 140 | } 141 | return 142 | } 143 | 144 | func (b *bytesReadWriter) Len() int64 { 145 | return b.len 146 | } 147 | 148 | func (b *bytesReadWriter) SetLen(len int64) { 149 | if len < 0 { 150 | b.len = 0 151 | } else if len > int64(cap(b.bytesAlias)) { 152 | b.len = int64(cap(b.bytesAlias)) 153 | } else { 154 | b.len = len 155 | } 156 | } 157 | 158 | func (b *bytesReadWriter) Offset() int64 { 159 | return b.offset 160 | } 161 | 162 | func (b *bytesReadWriter) Seek(offset int64, whence int) (int64, error) { 163 | if offset < 0 { 164 | return b.offset, fmt.Errorf(offsetErrorFmt, offset) 165 | } 166 | switch whence { 167 | case io.SeekStart: 168 | b.offset = offset 169 | if offset > b.len { 170 | return b.offset, fmt.Errorf(offsetErrorFmt, offset) 171 | } 172 | case io.SeekCurrent: 173 | if offset > b.len-b.offset { 174 | return b.offset, fmt.Errorf(offsetErrorFmt, offset) 175 | } 176 | b.offset = b.offset + offset 177 | case io.SeekEnd: 178 | if offset > b.len { 179 | return b.offset, fmt.Errorf(offsetErrorFmt, offset) 180 | } 181 | b.offset = b.len - offset 182 | default: 183 | return b.offset, fmt.Errorf(whenceErrorFmt, whence) 184 | } 185 | return b.offset, nil 186 | } 187 | 188 | func (b *bytesReadWriter) BufferPtr() unsafe.Pointer { 189 | return b.buffer 190 | } 191 | -------------------------------------------------------------------------------- /pkg/sdk/plugins/plugins.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | /* 3 | Copyright (C) 2023 The Falco Authors. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package plugins 19 | 20 | import ( 21 | "github.com/falcosecurity/plugin-sdk-go/pkg/ptr" 22 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk" 23 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk/symbols/info" 24 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk/symbols/initialize" 25 | "github.com/falcosecurity/plugin-sdk-go/pkg/sdk/symbols/initschema" 26 | ) 27 | 28 | // Info is a struct containing the general information about a plugin. 29 | type Info struct { 30 | ID uint32 31 | Name string 32 | Description string 33 | EventSource string 34 | Contact string 35 | Version string 36 | RequiredAPIVersion string 37 | ExtractEventSources []string 38 | } 39 | 40 | // Plugin is an interface representing a plugin. 41 | // Implementations of this interface can optionally implement the sdk.Destroy 42 | // interface to specify a Destroy method will be called during the 43 | // plugin deinitialization. 44 | type Plugin interface { 45 | // (optional): sdk.Destroyer 46 | // (optional): sdk.InitSchema 47 | sdk.LastError 48 | sdk.LastErrorBuffer 49 | // 50 | // Info returns a pointer to a Info struct, containing 51 | // all the general information about this plugin. 52 | Info() *Info 53 | // 54 | // Init initializes this plugin with a given config string. 55 | // A successful call to init returns a nil error. 56 | Init(config string) error 57 | } 58 | 59 | // BaseEvents is a base implementation of the sdk.Events interface. 60 | type BaseEvents struct { 61 | events sdk.EventWriters 62 | } 63 | 64 | func (b *BaseEvents) Events() sdk.EventWriters { 65 | return b.events 66 | } 67 | 68 | func (b *BaseEvents) SetEvents(events sdk.EventWriters) { 69 | b.events = events 70 | } 71 | 72 | // BaseExtractRequests is a base implementation of the sdk.ExtractRequests 73 | // interface. 74 | type BaseExtractRequests struct { 75 | extrReqPool sdk.ExtractRequestPool 76 | } 77 | 78 | func (b *BaseExtractRequests) ExtractRequests() sdk.ExtractRequestPool { 79 | return b.extrReqPool 80 | } 81 | 82 | func (b *BaseExtractRequests) SetExtractRequests(pool sdk.ExtractRequestPool) { 83 | b.extrReqPool = pool 84 | } 85 | 86 | // BaseLastError is a base implementation of the sdk.LastError interface. 87 | type BaseLastError struct { 88 | lastErr error 89 | lastErrBuf ptr.StringBuffer 90 | } 91 | 92 | func (b *BaseLastError) LastError() error { 93 | return b.lastErr 94 | } 95 | 96 | func (b *BaseLastError) SetLastError(err error) { 97 | b.lastErr = err 98 | } 99 | 100 | func (b *BaseLastError) LastErrorBuffer() sdk.StringBuffer { 101 | return &b.lastErrBuf 102 | } 103 | 104 | // BaseStringer is a base implementation of the sdk.StringerBuffer interface. 105 | type BaseStringer struct { 106 | stringerBuf ptr.StringBuffer 107 | } 108 | 109 | func (b *BaseStringer) StringerBuffer() sdk.StringBuffer { 110 | return &b.stringerBuf 111 | } 112 | 113 | // BaseProgress is a base implementation of the sdk.ProgressBuffer interface. 114 | type BaseProgress struct { 115 | progressBuf ptr.StringBuffer 116 | } 117 | 118 | func (b *BaseProgress) ProgressBuffer() sdk.StringBuffer { 119 | return &b.progressBuf 120 | } 121 | 122 | // BaseOpenParams is a base implementation of the sdk.OpenParamsBuffer interface. 123 | type BaseOpenParams struct { 124 | openParamsBuf ptr.StringBuffer 125 | } 126 | 127 | func (b *BaseOpenParams) OpenParamsBuffer() sdk.StringBuffer { 128 | return &b.openParamsBuf 129 | } 130 | 131 | // BasePlugin is a base implementation of the Plugin interface. 132 | // Developer-defined Plugin implementations should be composed with BasePlugin 133 | // to have out-of-the-box compliance with all the required interfaces. 134 | type BasePlugin struct { 135 | BaseLastError 136 | BaseStringer 137 | BaseExtractRequests 138 | BaseOpenParams 139 | } 140 | 141 | // FactoryFunc creates a new Plugin 142 | type FactoryFunc func() Plugin 143 | 144 | // SetFactory sets the FactoryFunc to be used by the SDK when creating a new Plugin 145 | // 146 | // SetFactory should be called in the Go init() function of the plugin main package. 147 | // It hooks the plugin framework initialization stage to create a new Plugin and 148 | // to set up common facilities provided by this SDK. The given FactoryFunc must create 149 | // a Plugin and can optionally enable plugin capabilities by using the Register functions 150 | // provided by sub-packages. This function is idempotent. 151 | // 152 | // Usage example: 153 | // 154 | // package main 155 | // 156 | // import ( 157 | // "github.com/falcosecurity/plugin-sdk-go/pkg/sdk/plugins" 158 | // "github.com/falcosecurity/plugin-sdk-go/pkg/sdk/plugins/extractor" 159 | // "github.com/falcosecurity/plugin-sdk-go/pkg/sdk/plugins/source" 160 | // ) 161 | // 162 | // func init() { 163 | // plugins.SetFactory(func() plugins.Plugin { 164 | // p := &MyPlugin{} // create a new Plugin 165 | // source.Register(p) // enable event sourcing capability 166 | // extractor.Register(p) // enable field extraction capability 167 | // return p 168 | // }) 169 | // } 170 | // 171 | func SetFactory(f FactoryFunc) { 172 | 173 | // Create a new plugin instance to get static plugin info 174 | p := f() 175 | 176 | // Set up plugin info 177 | i := p.Info() 178 | info.SetId(i.ID) 179 | info.SetName(i.Name) 180 | info.SetDescription(i.Description) 181 | info.SetEventSource(i.EventSource) 182 | info.SetExtractEventSources(i.ExtractEventSources) 183 | info.SetContact(i.Contact) 184 | info.SetVersion(i.Version) 185 | info.SetRequiredAPIVersion(i.RequiredAPIVersion) 186 | 187 | // Set up plugin init schema, if any 188 | if initSchema, ok := p.(sdk.InitSchema); ok { 189 | initschema.SetInitSchema(initSchema.InitSchema()) 190 | } 191 | 192 | initialize.SetOnInit(func(c string) (sdk.PluginState, error) { 193 | // Create a new plugin instance 194 | p := f() 195 | err := p.Init(c) 196 | return p, err 197 | }) 198 | } 199 | --------------------------------------------------------------------------------