diff --git a/files/build_templates/sonic_debian_extension.j2 b/files/build_templates/sonic_debian_extension.j2 index 0d5b895cacfe..45286629f38d 100644 --- a/files/build_templates/sonic_debian_extension.j2 +++ b/files/build_templates/sonic_debian_extension.j2 @@ -265,6 +265,10 @@ sudo rm -rf $FILESYSTEM_ROOT/$SONIC_UTILITIES_PY3_WHEEL_NAME sudo dpkg --root=$FILESYSTEM_ROOT -i $debs_path/sonic-utilities-data_*.deb || \ sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y install -f +# Install customized bash version to patch bash plugin support. +sudo dpkg --root=$FILESYSTEM_ROOT -i target/debs/bash_*.deb || \ + sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y install -f + # sonic-utilities-data installs bash-completion as a dependency. However, it is disabled by default # in bash.bashrc, so we copy a version of the file with it enabled here. sudo cp -f $IMAGE_CONFIGS/bash/bash.bashrc $FILESYSTEM_ROOT/etc/ diff --git a/rules/bash.mk b/rules/bash.mk index 9c0d0c1e86d1..48c089c49e0d 100644 --- a/rules/bash.mk +++ b/rules/bash.mk @@ -1,14 +1,12 @@ # bash package # -# Created to patch memory-leak issue in the bash-package included in Debian-8 (Jessie) -# release. This rule file, and the associated building-infra created to solve this -# bug (src/bash/), should be eliminated once the migration to Debian-9 (Stretch) is -# completed. +# Created to patch plugin support in the bash-package included in Debian-11 (Bullseye) +# release. -# Bash major release-number corresponding to Debian-8 (Jessie) -BASH_VERSION_MAJOR = 4.3 -# Bash complete release-number. This image contains all 4.3 fixes up to patch '42'. -BASH_VERSION_FULL = $(BASH_VERSION_MAJOR)-14 +# Bash major release-number corresponding to Debian-11 (Bullseye) +BASH_VERSION_MAJOR = 5.1 +# Bash complete release-number. This image contains all 5.1 fixes up to patch '2'. +BASH_VERSION_FULL = $(BASH_VERSION_MAJOR)-2 export BASH_VERSION_MAJOR BASH_VERSION_FULL diff --git a/slave.mk b/slave.mk index 9e4bf532bfda..8c1a1699a06d 100644 --- a/slave.mk +++ b/slave.mk @@ -910,7 +910,8 @@ $(addprefix $(TARGET_PATH)/, $(SONIC_INSTALLERS)) : $(TARGET_PATH)/% : \ $(PYTHON_SWSSCOMMON) \ $(PYTHON3_SWSSCOMMON) \ $(SONIC_UTILITIES_DATA) \ - $(SONIC_HOST_SERVICES_DATA)) \ + $(SONIC_HOST_SERVICES_DATA) \ + $(BASH)) \ $$(addprefix $(TARGET_PATH)/,$$($$*_DOCKERS)) \ $$(addprefix $(TARGET_PATH)/,$$(SONIC_PACKAGES_LOCAL)) \ $$(addprefix $(FILES_PATH)/,$$($$*_FILES)) \ diff --git a/sonic-slave-bullseye/Dockerfile.j2 b/sonic-slave-bullseye/Dockerfile.j2 index fac3bbe2b4aa..8a6e68b0546a 100644 --- a/sonic-slave-bullseye/Dockerfile.j2 +++ b/sonic-slave-bullseye/Dockerfile.j2 @@ -250,6 +250,12 @@ RUN apt-get update && apt-get install -y \ iproute2 \ # For bash texi2html \ + sharutils \ + locales \ + time \ + man2html-base \ + libcunit1 \ + libcunit1-dev \ # For initramfs shellcheck \ bash-completion \ diff --git a/sonic-slave-buster/Dockerfile.j2 b/sonic-slave-buster/Dockerfile.j2 index bbd68a31396e..8033f8d0a7c9 100644 --- a/sonic-slave-buster/Dockerfile.j2 +++ b/sonic-slave-buster/Dockerfile.j2 @@ -258,6 +258,12 @@ RUN apt-get update && apt-get install -y \ iproute2 \ # For bash texi2html \ + sharutils \ + locales \ + time \ + man2html-base \ + libcunit1 \ + libcunit1-dev \ # For initramfs shellcheck \ bash-completion \ diff --git a/sonic-slave-jessie/Dockerfile.j2 b/sonic-slave-jessie/Dockerfile.j2 index db8f0a49650d..b23f06880344 100644 --- a/sonic-slave-jessie/Dockerfile.j2 +++ b/sonic-slave-jessie/Dockerfile.j2 @@ -231,6 +231,12 @@ RUN apt-get update && apt-get install -y \ texlive-latex-recommended \ # For bash texi2html \ + sharutils \ + locales \ + time \ + man2html-base \ + libcunit1 \ + libcunit1-dev \ # For initramfs bash-completion \ {% if CONFIGURED_ARCH == "amd64" -%} diff --git a/sonic-slave-stretch/Dockerfile.j2 b/sonic-slave-stretch/Dockerfile.j2 index cc45afbc6f82..ed37d506cf41 100644 --- a/sonic-slave-stretch/Dockerfile.j2 +++ b/sonic-slave-stretch/Dockerfile.j2 @@ -254,6 +254,12 @@ RUN apt-get update && apt-get install -y \ iproute2 \ # For bash texi2html \ + sharutils \ + locales \ + time \ + man2html-base \ + libcunit1 \ + libcunit1-dev \ # For initramfs bash-completion \ {%- if CONFIGURED_ARCH == "amd64" %} diff --git a/src/bash/.gitignore b/src/bash/.gitignore index a0991ff4402b..4d73f1b0d38e 100644 --- a/src/bash/.gitignore +++ b/src/bash/.gitignore @@ -1,3 +1,7 @@ * !.gitignore !Makefile +!Files/ +!Files/* +!patches/ +!patches/* diff --git a/src/bash/Files/unittest/Makefile b/src/bash/Files/unittest/Makefile new file mode 100644 index 000000000000..8eec2b07ab45 --- /dev/null +++ b/src/bash/Files/unittest/Makefile @@ -0,0 +1,18 @@ +#disable some warning because UT need test functions not in header file. +CFLAGS = -Wno-parentheses -Wno-format-security -Wno-implicit-function-declaration -c +IFLAGS = -I.. -I../include -I../lib +MFLAG = -DDEBUG -DBASH_PLUGIN_UT + +all: + gcc plugin_test.c $(IFLAGS) $(CFLAGS) -o plugin_test.o + gcc mock_helper.c $(IFLAGS) $(CFLAGS) -o mock_helper.o + gcc ../plugin.c $(IFLAGS) $(CFLAGS) $(MFLAG) -o plugin.o + gcc plugin_test.o mock_helper.o plugin.o -o plugin_test -lc -lcunit + +test: + # run unit test, if UT failed, build will break + ./plugin_test + +clean: + rm *.o + rm plugin_test diff --git a/src/bash/Files/unittest/bash_plugins.conf b/src/bash/Files/unittest/bash_plugins.conf new file mode 100644 index 000000000000..568de35efd37 --- /dev/null +++ b/src/bash/Files/unittest/bash_plugins.conf @@ -0,0 +1,6 @@ +# tacacs authorization plugin +plugin=/home/liuh/tacacs-bash-plugin/tacacs-authorization.so +plugin=/usr/lib/bash-plugins/another_test_plugin.so # test comments + + +# test line \ No newline at end of file diff --git a/src/bash/Files/unittest/mock_helper.c b/src/bash/Files/unittest/mock_helper.c new file mode 100644 index 000000000000..dfbf3a6444aa --- /dev/null +++ b/src/bash/Files/unittest/mock_helper.c @@ -0,0 +1,218 @@ +/* mock_helper.c -- mock helper for bash plugin UT. */ +#include +#include +#include +#include +#include +#include +#include "mock_helper.h" + +// define BASH_PLUGIN_UT_DEBUG to output UT debug message. +//#define BASH_PLUGIN_UT_DEBUG +#if defined (BASH_PLUGIN_UT_DEBUG) +# define debug_printf printf +#else +# define debug_printf +#endif + +/* itrace buffer */ +char mock_itrace_message_buffer[1024]; + +/* bash run command buffer */ +char mock_onshell_execve_command_buffer[1024]; + +/* plugin handles. */ +void* mock_plugin_handle = (void*)TEST_MOCK_PLUGIN_HANDLE; +void* mock_plugin_default_function_handle = (void*)0x2234; +void* mock_plugin_on_shell_execve_handle = (void*)0x3234; +char* mock_dlerror_failed = "MOCK error"; +char* mock_dlerror = NULL; + +/* define test scenarios for mock functions return different value by scenario. */ +int test_scenario; + +/* define test scenarios for different return value. */ +int plugin_init_status; + +/* define memory allocate counter. */ +int memory_allocate_count; + +/* Set test scenario for test*/ +void set_test_scenario(int scenario) +{ + test_scenario = scenario; +} + +/* Get test scenario for test*/ +int get_test_scenario() +{ + return test_scenario; +} + +/* Set plugin init status for test*/ +void set_plugin_init_status(int status) +{ + plugin_init_status = status; +} + +/* Get plugin init status for test*/ +int get_plugin_init_status() +{ + return plugin_init_status; +} + +/* Set memory allocate count for test*/ +void set_memory_allocate_count(int count) +{ + memory_allocate_count = count; +} + +/* Get memory allocate count for test*/ +int get_memory_allocate_count() +{ + return memory_allocate_count; +} + +/* MOCK plugin_init method*/ +int mock_plugin_init() +{ + set_plugin_init_status(PLUGIN_INITIALIZED); +} + +/* MOCK plugin_init method*/ +int mock_plugin_uninit() +{ + set_plugin_init_status(PLUGIN_NOT_INITIALIZE); +} + +/* MOCK on_shell_execve method*/ +int mock_on_shell_execve (char *user, int shell_level, char *cmd, char **argv) +{ + // set mock command data to buffer for UT. + memset(mock_onshell_execve_command_buffer, 0, sizeof(mock_onshell_execve_command_buffer)); + + snprintf(mock_onshell_execve_command_buffer, sizeof(mock_onshell_execve_command_buffer), "on_shell_execve: user: %s, level: %d, command: %s, argv: %p\n", user, shell_level, cmd, argv); + + debug_printf("MOCK: mock_on_shell_execve: %s\n", mock_onshell_execve_command_buffer); +} + +/* MOCK dlopen*/ +void *dlopen(const char *filename, int flags) +{ + debug_printf("MOCK: dlopen: %s\n", filename); + if (TEST_SCEANRIO_PLUGIN_NOT_EXIT == test_scenario) + { + // return null when plugin not exist + mock_dlerror = mock_dlerror_failed; + return NULL; + } + + // all other case return mock handle + mock_dlerror = NULL; + return mock_plugin_handle; +} + +/* MOCK dlclose*/ +int dlclose(void *handle) +{ + debug_printf("MOCK: dlclose: %p\n", handle); + // check if the close handle match the opened handle + CU_ASSERT_EQUAL(handle, mock_plugin_handle); +} + +/* MOCK dlsym*/ +void *dlsym(void *restrict handle, const char *restrict symbol) +{ + debug_printf("MOCK: dlsym: %p, %s\n", handle, symbol); + mock_dlerror = NULL; + switch (test_scenario) + { + case TEST_SCEANRIO_PLUGIN_EXECVE_NOT_EXIT: + if (strcmp(symbol, "on_shell_execve") == 0) + { + mock_dlerror = mock_dlerror_failed; + return NULL; + } + + case TEST_SCEANRIO_PLUGIN_UNINIT_NOT_EXIT: + if (strcmp(symbol, "plugin_uninit") == 0) + { + mock_dlerror = mock_dlerror_failed; + return NULL; + } + + case TEST_SCEANRIO_PLUGIN_INIT_NOT_EXIT: + if (strcmp(symbol, "plugin_init") == 0) + { + mock_dlerror = mock_dlerror_failed; + return NULL; + } + + case TEST_SCEANRIO_PLUGIN_INIT_SUCCESS: + if (strcmp(symbol, "plugin_init") == 0) + { + // return mock method handle so plugin framework will call it to initialize + return mock_plugin_init; + } + else if (strcmp(symbol, "plugin_uninit") == 0) + { + // return mock method handle so plugin framework will call it to initialize + return mock_plugin_uninit; + } + else if (strcmp(symbol, "on_shell_execve") == 0) + { + // return mock method handle so plugin framework will call it to initialize + return mock_on_shell_execve; + } + } + + return mock_plugin_default_function_handle; +} + +/* MOCK dlerror*/ +char *dlerror(void) +{ + return mock_dlerror; +} + +/* MOCK get_string_value*/ +char *get_string_value(const char * str) +{ + return "1"; +} + +/* MOCK absolute_program*/ +int absolute_program (const char * str) +{ + return 0; +} + +/* MOCK itrace*/ +void itrace (const char * format, ...) +{ + // set mock message data to buffer for UT. + memset(mock_itrace_message_buffer, 0, sizeof(mock_itrace_message_buffer)); + + va_list args; + va_start(args, format); + // save message to buffer to UT check later + vsnprintf(mock_itrace_message_buffer, sizeof(mock_itrace_message_buffer), format, args); + va_end(args); + debug_printf("MOCK: itrace: %s\n", mock_itrace_message_buffer); +} + +/* MOCK malloc method*/ +void* mock_malloc (size_t size) +{ + memory_allocate_count++; + debug_printf("MOCK: malloc memory count: %d\n", memory_allocate_count); + return malloc(size); +} + +/* MOCK free method*/ +void mock_free (void* ptr) +{ + memory_allocate_count--; + debug_printf("MOCK: free memory count: %d\n", memory_allocate_count); + free(ptr); +} \ No newline at end of file diff --git a/src/bash/Files/unittest/mock_helper.h b/src/bash/Files/unittest/mock_helper.h new file mode 100644 index 000000000000..1fcfeb39be9b --- /dev/null +++ b/src/bash/Files/unittest/mock_helper.h @@ -0,0 +1,65 @@ +/* plugin.h - functions from plugin.c. */ + +/* Copyright (C) 1993-2015 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see . +*/ + +#if !defined (_MOCK_HELPER_H_) +#define _MOCK_HELPER_H_ + +#include "plugin.h" + +#define TEST_MOCK_PLUGIN_HANDLE 0x1234 + +#define TEST_SCEANRIO_PLUGIN_NOT_EXIT 1 +#define TEST_SCEANRIO_PLUGIN_EXECVE_NOT_EXIT 2 +#define TEST_SCEANRIO_PLUGIN_UNINIT_NOT_EXIT 3 +#define TEST_SCEANRIO_PLUGIN_INIT_NOT_EXIT 4 +#define TEST_SCEANRIO_PLUGIN_INIT_SUCCESS 5 + +#define PLUGIN_NOT_INITIALIZE -1 +#define PLUGIN_INITIALIZED 1 + +/* The global plugin list */ +extern PLUGIN_NODE *global_plugin_list; + +/* itrace buffer */ +extern char mock_itrace_message_buffer[1024]; + +/* bash run command buffer */ +extern char mock_onshell_execve_command_buffer[1024]; + +/* Set test scenario for test*/ +void set_test_scenario(int scenario); + +/* Get test scenario for test*/ +int get_test_scenario(); + +/* Set plugin init status for test*/ +void set_plugin_init_status(int status); + +/* Get plugin init status for test*/ +int get_plugin_init_status(); + +/* Set memory allocate count for test*/ +void set_memory_allocate_count(int count); + +/* Get memory allocate count for test*/ +int get_memory_allocate_count(); + + +#endif /* _MOCK_HELPER_H_ */ \ No newline at end of file diff --git a/src/bash/Files/unittest/plugin_test.c b/src/bash/Files/unittest/plugin_test.c new file mode 100644 index 000000000000..83439f93a03f --- /dev/null +++ b/src/bash/Files/unittest/plugin_test.c @@ -0,0 +1,217 @@ +#include +#include +#include +#include +#include "plugin.h" +#include "mock_helper.h" + +int clean_up() { + return 0; +} + +int start_up() { + return 0; +} + +/* Test plugin not exist scenario */ +void testcase_try_load_plugin_by_path_not_exist() { + set_test_scenario(TEST_SCEANRIO_PLUGIN_NOT_EXIT); + + try_load_plugin_by_path("./testplugin.so"); + + CU_ASSERT_STRING_EQUAL(mock_itrace_message_buffer, "Plugin: can't load plugin ./testplugin.so: MOCK error\n"); +} + +/* Test plugin exist but not support shell_execve scenario */ +void testcase_try_load_plugin_by_path_execve_not_exist() { + set_test_scenario(TEST_SCEANRIO_PLUGIN_EXECVE_NOT_EXIT); + + try_load_plugin_by_path("./testplugin.so"); + + CU_ASSERT_STRING_EQUAL(mock_itrace_message_buffer, "Plugin: can't find on_shell_execve function ./testplugin.so: MOCK error\n"); +} + +/* Test plugin exist but not support plugin_uninit scenario */ +void testcase_try_load_plugin_by_path_plugin_uninit_not_exist() { + set_test_scenario(TEST_SCEANRIO_PLUGIN_UNINIT_NOT_EXIT); + + try_load_plugin_by_path("./testplugin.so"); + + CU_ASSERT_STRING_EQUAL(mock_itrace_message_buffer, "Plugin: can't find plugin_uninit function ./testplugin.so: MOCK error\n"); +} + +/* Test plugin exist but not support plugin_init scenario */ +void testcase_try_load_plugin_by_path_plugin_init_not_exist() { + set_test_scenario(TEST_SCEANRIO_PLUGIN_INIT_NOT_EXIT); + + try_load_plugin_by_path("./testplugin.so"); + + CU_ASSERT_STRING_EQUAL(mock_itrace_message_buffer, "Plugin: can't find plugin_init function ./testplugin.so: MOCK error\n"); +} + +/* Test plugin exist but not support plugin_init scenario */ +void testcase_try_load_plugin_by_path_plugin_init_success() { + set_test_scenario(TEST_SCEANRIO_PLUGIN_INIT_SUCCESS); + set_memory_allocate_count(0); + set_plugin_init_status(PLUGIN_NOT_INITIALIZE); + + try_load_plugin_by_path("./testplugin.so"); + + // check plugin init success + CU_ASSERT_EQUAL(get_plugin_init_status(), PLUGIN_INITIALIZED); + + // check API success + CU_ASSERT_STRING_EQUAL(mock_itrace_message_buffer, "Plugin: plugin ./testplugin.so loaded\n"); + + // check global plugin list not empty and contains correct pluginglobal_plugin_list + CU_ASSERT_NOT_EQUAL(global_plugin_list, NULL); + CU_ASSERT_EQUAL(global_plugin_list->plugin_handle, TEST_MOCK_PLUGIN_HANDLE); + + // release all loaded plugins + free_loaded_plugins(); + + // check if memory fully released + CU_ASSERT_EQUAL(global_plugin_list, NULL); + CU_ASSERT_EQUAL(get_memory_allocate_count(), 0); +} + +/* Test free loaded plugins */ +void testcase_release_loaded_plugin() { + set_test_scenario(TEST_SCEANRIO_PLUGIN_INIT_SUCCESS); + set_memory_allocate_count(0); + try_load_plugin_by_path("./testplugin.so"); + + // check memory allocated + CU_ASSERT_NOT_EQUAL(get_memory_allocate_count(), 0); + + // check plugin init success + CU_ASSERT_EQUAL(get_plugin_init_status(), PLUGIN_INITIALIZED); + + // release all loaded plugins + free_loaded_plugins(); + + // check if memory fully released + CU_ASSERT_EQUAL(global_plugin_list, NULL); + CU_ASSERT_EQUAL(get_memory_allocate_count(), 0); +} + +/* Test load plugin by config */ +void testcase_load_plugin_by_config() { + set_test_scenario(TEST_SCEANRIO_PLUGIN_INIT_SUCCESS); + set_memory_allocate_count(0); + load_plugin_by_config("./bash_plugins.conf"); + + // check memory allocated + CU_ASSERT_NOT_EQUAL(get_memory_allocate_count(), 0); + + // check plugin init success + CU_ASSERT_EQUAL(get_plugin_init_status(), PLUGIN_INITIALIZED); + + // check target plugin in config file loaded + CU_ASSERT_STRING_EQUAL(mock_itrace_message_buffer, "Plugin: plugin /usr/lib/bash-plugins/another_test_plugin.so loaded\n"); + + // check there are 2 plugins loaded + CU_ASSERT_EQUAL(get_memory_allocate_count(), 2); + + // release all loaded plugins + free_loaded_plugins(); + + // check if memory fully released + CU_ASSERT_EQUAL(global_plugin_list, NULL); + printf("Count %d\n", get_memory_allocate_count()); + CU_ASSERT_EQUAL(get_memory_allocate_count(), 0); +} + +/* Test invoke on_shell_execve plugin method */ +void testcase_invoke_plugin_on_shell_execve() { + set_test_scenario(TEST_SCEANRIO_PLUGIN_INIT_SUCCESS); + set_memory_allocate_count(0); + load_plugin_by_config("./bash_plugins.conf"); + + // invoke plugin method + char** pargv = (char**)0x5234; + invoke_plugin_on_shell_execve("testuser", "testcommand", pargv); + printf(mock_onshell_execve_command_buffer); + CU_ASSERT_STRING_EQUAL(mock_onshell_execve_command_buffer, "on_shell_execve: user: testuser, level: 1, command: testcommand, argv: 0x5234\n"); + + // release all loaded plugins + free_loaded_plugins(); + + // check if memory fully released + CU_ASSERT_EQUAL(global_plugin_list, NULL); + printf("Count %d\n", get_memory_allocate_count()); + CU_ASSERT_EQUAL(get_memory_allocate_count(), 0); +} + +int main(void) { + if (CUE_SUCCESS != CU_initialize_registry()) { + return CU_get_error(); + } + + CU_pSuite ste = CU_add_suite("plugin_test", start_up, clean_up); + if (NULL == ste) { + CU_cleanup_registry(); + return CU_get_error(); + } + + if (CU_get_error() != CUE_SUCCESS) { + fprintf(stderr, "Error creating suite: (%d)%s\n", CU_get_error(), CU_get_error_msg()); + return CU_get_error(); + } + + if (!CU_add_test(ste, "Test testcase_try_load_plugin_by_path_not_exist()...\n", testcase_try_load_plugin_by_path_not_exist)) { + CU_cleanup_registry(); + return CU_get_error(); + } + + if (!CU_add_test(ste, "Test testcase_try_load_plugin_by_path_execve_not_exist()...\n", testcase_try_load_plugin_by_path_execve_not_exist)) { + CU_cleanup_registry(); + return CU_get_error(); + } + + if (!CU_add_test(ste, "Test testcase_try_load_plugin_by_path_plugin_uninit_not_exist()...\n", testcase_try_load_plugin_by_path_plugin_uninit_not_exist)) { + CU_cleanup_registry(); + return CU_get_error(); + } + + if (!CU_add_test(ste, "Test testcase_try_load_plugin_by_path_plugin_init_not_exist()...\n", testcase_try_load_plugin_by_path_plugin_init_not_exist)) { + CU_cleanup_registry(); + return CU_get_error(); + } + + if (!CU_add_test(ste, "Test testcase_try_load_plugin_by_path_plugin_init_success()...\n", testcase_try_load_plugin_by_path_plugin_init_success)) { + CU_cleanup_registry(); + return CU_get_error(); + } + + if (!CU_add_test(ste, "Test testcase_release_loaded_plugin()...\n", testcase_release_loaded_plugin)) { + CU_cleanup_registry(); + return CU_get_error(); + } + + if (!CU_add_test(ste, "Test testcase_load_plugin_by_config()...\n", testcase_load_plugin_by_config)) { + CU_cleanup_registry(); + return CU_get_error(); + } + + if (!CU_add_test(ste, "Test testcase_invoke_plugin_on_shell_execve()...\n", testcase_invoke_plugin_on_shell_execve)) { + CU_cleanup_registry(); + return CU_get_error(); + } + + if (CU_get_error() != CUE_SUCCESS) { + fprintf(stderr, "Error adding test: (%d)%s\n", CU_get_error(), CU_get_error_msg()); + } + + // run all test + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_ErrorCode run_errors = CU_basic_run_suite(ste); + if (run_errors != CUE_SUCCESS) { + fprintf(stderr, "Error running tests: (%d)%s\n", run_errors, CU_get_error_msg()); + } + + CU_basic_show_failures(CU_get_failure_list()); + + // use failed UT count as return value + return CU_get_number_of_failure_records(); +} diff --git a/src/bash/Makefile b/src/bash/Makefile index 6576ff92e74a..b294e6e1c40d 100644 --- a/src/bash/Makefile +++ b/src/bash/Makefile @@ -9,8 +9,26 @@ $(addprefix $(DEST)/, $(MAIN_TARGET)): $(DEST)/% : dget -u https://launchpad.net/debian/+archive/primary/+sourcefiles/bash/$(BASH_VERSION_FULL)/bash_$(BASH_VERSION_FULL).dsc + # Apply plugin suport patches + quilt push -a + pushd bash-$(BASH_VERSION_MAJOR) + + # build package DEB_BUILD_OPTIONS=nocheck dpkg-buildpackage -us -uc -b -j$(SONIC_CONFIG_MAKE_JOBS) --admindir $(SONIC_DPKG_ADMINDIR) + + # copy UT code. + cp -a ../Files/. ./ + + # generate config.h which need by UT + ./configure + + # generate 'pathnames.h' by make bash again, which is needed by UT. + make + + # run UT after config.h ready. + make -C unittest && make test -C unittest + popd mv $* $(DEST)/ diff --git a/src/bash/patches/0001-Add-plugin-support-to-bash.patch b/src/bash/patches/0001-Add-plugin-support-to-bash.patch new file mode 100644 index 000000000000..bec53ba8b5df --- /dev/null +++ b/src/bash/patches/0001-Add-plugin-support-to-bash.patch @@ -0,0 +1,805 @@ +From 79b3c4f7e8589afae4b048d662a56b055436e9ab Mon Sep 17 00:00:00 2001 +From: liuh-80 <58683130+liuh-80@users.noreply.github.com> +Date: Fri, 8 Oct 2021 16:36:34 +0800 +Subject: [PATCH] Add plugin support to bash. + +--- + bash-5.1/Makefile.in | 14 +- + bash-5.1/config.h.in | 3 + + bash-5.1/configure | 18 +- + bash-5.1/configure.ac | 10 + + bash-5.1/execute_cmd.c | 16 ++ + bash-5.1/plugin.c | 428 +++++++++++++++++++++++++++++++++++++++++ + bash-5.1/plugin.h | 79 ++++++++ + bash-5.1/shell.c | 12 ++ + 8 files changed, 571 insertions(+), 9 deletions(-) + create mode 100644 bash-5.1/plugin.c + create mode 100644 bash-5.1/plugin.h + +diff --git a/bash-5.1/Makefile.in b/bash-5.1/Makefile.in +index 3e3a5d4..16169cd 100644 +--- a/bash-5.1/Makefile.in ++++ b/bash-5.1/Makefile.in +@@ -380,6 +380,9 @@ LTLIBINTL = @LTLIBINTL@ + INTLLIBS = @INTLLIBS@ + INTLOBJS = @INTLOBJS@ + ++# Dynamic load library. ++DYNAMICLOAD_LIB = @DYNAMICLOAD_LIB@ ++ + # Our malloc. + MALLOC_TARGET = @MALLOC_TARGET@ + +@@ -421,7 +424,7 @@ BASHINCFILES = $(BASHINCDIR)/posixstat.h $(BASHINCDIR)/ansi_stdlib.h \ + $(BASHINCDIR)/ocache.h + + LIBRARIES = $(GLOB_LIB) $(SHLIB_LIB) $(READLINE_LIB) $(HISTORY_LIB) $(TERMCAP_LIB) \ +- $(TILDE_LIB) $(MALLOC_LIB) $(INTL_LIB) $(LIBICONV) $(LOCAL_LIBS) ++ $(TILDE_LIB) $(MALLOC_LIB) $(INTL_LIB) $(LIBICONV) $(LOCAL_LIBS) $(DYNAMICLOAD_LIB) + + LIBDEP = $(GLOB_DEP) $(SHLIB_DEP) $(INTL_DEP) $(READLINE_DEP) $(HISTORY_DEP) $(TERMCAP_DEP) \ + $(TILDE_DEP) $(MALLOC_DEP) +@@ -441,7 +444,7 @@ CSOURCES = shell.c eval.c parse.y general.c make_cmd.c print_cmd.c y.tab.c \ + input.c bashhist.c array.c arrayfunc.c assoc.c sig.c pathexp.c \ + unwind_prot.c siglist.c bashline.c bracecomp.c error.c \ + list.c stringlib.c locale.c findcmd.c redir.c \ +- pcomplete.c pcomplib.c syntax.c xmalloc.c ++ pcomplete.c pcomplib.c syntax.c xmalloc.c plugin.c + + HSOURCES = shell.h flags.h trap.h hashcmd.h hashlib.h jobs.h builtins.h \ + general.h variables.h config.h $(ALLOC_HEADERS) alias.h \ +@@ -449,7 +452,7 @@ HSOURCES = shell.h flags.h trap.h hashcmd.h hashlib.h jobs.h builtins.h \ + command.h input.h error.h bashansi.h dispose_cmd.h make_cmd.h \ + subst.h externs.h siglist.h bashhist.h bashline.h bashtypes.h \ + array.h arrayfunc.h sig.h mailcheck.h bashintl.h bashjmp.h \ +- execute_cmd.h parser.h pathexp.h pathnames.h pcomplete.h assoc.h \ ++ execute_cmd.h parser.h pathexp.h pathnames.h pcomplete.h assoc.h plugin.h \ + $(BASHINCFILES) + + SOURCES = $(CSOURCES) $(HSOURCES) $(BUILTIN_DEFS) +@@ -482,7 +485,7 @@ OBJECTS = shell.o eval.o y.tab.o general.o make_cmd.o print_cmd.o $(GLOBO) \ + trap.o input.o unwind_prot.o pathexp.o sig.o test.o version.o \ + alias.o array.o arrayfunc.o assoc.o braces.o bracecomp.o bashhist.o \ + bashline.o $(SIGLIST_O) list.o stringlib.o locale.o findcmd.o redir.o \ +- pcomplete.o pcomplib.o syntax.o xmalloc.o $(SIGNAMES_O) ++ pcomplete.o pcomplib.o syntax.o xmalloc.o plugin.o $(SIGNAMES_O) + + # Where the source code of the shell builtins resides. + BUILTIN_SRCDIR=$(srcdir)/builtins +@@ -1039,7 +1042,7 @@ eval.o: make_cmd.h subst.h sig.h pathnames.h externs.h parser.h + eval.o: input.h execute_cmd.h + eval.o: bashhist.h assoc.h ${BASHINCDIR}/ocache.h ${BASHINCDIR}/chartypes.h + execute_cmd.o: config.h bashtypes.h ${BASHINCDIR}/filecntl.h ${BASHINCDIR}/posixstat.h bashansi.h ${BASHINCDIR}/ansi_stdlib.h +-execute_cmd.o: shell.h syntax.h config.h bashjmp.h ${BASHINCDIR}/posixjmp.h command.h ${BASHINCDIR}/stdc.h error.h ++execute_cmd.o: shell.h syntax.h config.h bashjmp.h ${BASHINCDIR}/posixjmp.h command.h ${BASHINCDIR}/stdc.h error.h plugin.h + execute_cmd.o: general.h xmalloc.h bashtypes.h variables.h arrayfunc.h conftypes.h array.h hashlib.h + execute_cmd.o: quit.h ${BASHINCDIR}/maxpath.h unwind_prot.h dispose_cmd.h + execute_cmd.o: make_cmd.h subst.h sig.h pathnames.h externs.h parser.h +@@ -1050,6 +1053,7 @@ execute_cmd.o: ${BASHINCDIR}/posixtime.h ${BASHINCDIR}/chartypes.h + execute_cmd.o: $(DEFSRC)/getopt.h + execute_cmd.o: bashhist.h input.h ${GRAM_H} assoc.h hashcmd.h alias.h + execute_cmd.o: ${BASHINCDIR}/ocache.h ${BASHINCDIR}/posixwait.h ++plugin.o: plugin.h + expr.o: config.h bashansi.h ${BASHINCDIR}/ansi_stdlib.h + expr.o: shell.h syntax.h config.h bashjmp.h ${BASHINCDIR}/posixjmp.h command.h ${BASHINCDIR}/stdc.h error.h + expr.o: general.h xmalloc.h bashtypes.h variables.h arrayfunc.h conftypes.h array.h hashlib.h +diff --git a/bash-5.1/config.h.in b/bash-5.1/config.h.in +index ab316d4..ab5634f 100644 +--- a/bash-5.1/config.h.in ++++ b/bash-5.1/config.h.in +@@ -38,6 +38,9 @@ + BSD-like job control. */ + #undef JOB_CONTROL + ++/* Define BASH_SHELL_EXECVE_PLUGIN if need plugin support. */ ++#undef BASH_SHELL_EXECVE_PLUGIN ++ + /* Define ALIAS if you want the alias features. */ + #undef ALIAS + +diff --git a/bash-5.1/configure b/bash-5.1/configure +index 0f1d3ed..c462d55 100644 +--- a/bash-5.1/configure ++++ b/bash-5.1/configure +@@ -632,6 +632,7 @@ LOCAL_DEFS + LOCAL_LDFLAGS + LOCAL_CFLAGS + LOCAL_LIBS ++DYNAMICLOAD_LIB + MALLOC_DEBUG + DEBUG + RELSTATUS +@@ -858,6 +859,7 @@ enable_single_help_strings + enable_strict_posix_default + enable_usg_echo_default + enable_xpg_echo_default ++enable_bash_shell_execve_plugin + enable_mem_scramble + enable_profiling + enable_static_link +@@ -1568,6 +1570,7 @@ Optional Features: + --enable-xpg-echo-default + make the echo builtin expand escape sequences by + default ++ --enable-bash-plugin enable bash plugin features + --enable-mem-scramble scramble memory on calls to malloc and free + --enable-profiling allow profiling with gprof + --enable-static-link link bash statically, for use as a root shell +@@ -3027,6 +3030,7 @@ opt_dircomplete_expand_default=no + opt_globascii_default=yes + opt_function_import=yes + opt_dev_fd_stat_broken=no ++opt_bash_shell_execve_plugin=yes + + opt_static_link=no + opt_profiling=no +@@ -3048,6 +3052,7 @@ if test $opt_minimal_config = yes; then + opt_multibyte=yes opt_cond_regexp=no opt_coproc=no + opt_casemod_attrs=no opt_casemod_expansions=no opt_extglob_default=no + opt_globascii_default=yes ++ opt_bash_shell_execve_plugin=no + fi + + # Check whether --enable-alias was given. +@@ -3235,6 +3240,10 @@ if test "${enable_xpg_echo_default+set}" = set; then : + enableval=$enable_xpg_echo_default; opt_xpg_echo=$enableval + fi + ++# Check whether --enable-bash-shell-execve-plugin was given. ++if test "${enable_bash_shell_execve_plugin+set}" = set; then : ++ enableval=$enable_bash_shell_execve_plugin; opt_bash_shell_execve_plugin=$enableval ++fi + + # Check whether --enable-mem-scramble was given. + if test "${enable_mem_scramble+set}" = set; then : +@@ -3254,10 +3263,11 @@ fi + + + +- +- +- +- ++DYNAMICLOAD_LIB= ++if test $opt_bash_shell_execve_plugin = yes; then ++$as_echo "#define BASH_SHELL_EXECVE_PLUGIN 1" >>confdefs.h ++DYNAMICLOAD_LIB=-ldl ++fi + + if test $opt_alias = yes; then + $as_echo "#define ALIAS 1" >>confdefs.h +diff --git a/bash-5.1/configure.ac b/bash-5.1/configure.ac +index 2fe3e7d..0064683 100644 +--- a/bash-5.1/configure.ac ++++ b/bash-5.1/configure.ac +@@ -182,6 +182,7 @@ opt_dircomplete_expand_default=no + opt_globascii_default=yes + opt_function_import=yes + opt_dev_fd_stat_broken=no ++opt_bash_shell_execve_plugin=yes + + dnl options that affect how bash is compiled and linked + opt_static_link=no +@@ -203,6 +204,7 @@ if test $opt_minimal_config = yes; then + opt_multibyte=yes opt_cond_regexp=no opt_coproc=no + opt_casemod_attrs=no opt_casemod_expansions=no opt_extglob_default=no + opt_globascii_default=yes ++ opt_bash_shell_execve_plugin=no + fi + + AC_ARG_ENABLE(alias, AC_HELP_STRING([--enable-alias], [enable shell aliases]), opt_alias=$enableval) +@@ -242,6 +244,7 @@ AC_ARG_ENABLE(single-help-strings, AC_HELP_STRING([--enable-single-help-strings] + AC_ARG_ENABLE(strict-posix-default, AC_HELP_STRING([--enable-strict-posix-default], [configure bash to be posix-conformant by default]), opt_strict_posix=$enableval) + AC_ARG_ENABLE(usg-echo-default, AC_HELP_STRING([--enable-usg-echo-default], [a synonym for --enable-xpg-echo-default]), opt_xpg_echo=$enableval) + AC_ARG_ENABLE(xpg-echo-default, AC_HELP_STRING([--enable-xpg-echo-default], [make the echo builtin expand escape sequences by default]), opt_xpg_echo=$enableval) ++AC_ARG_ENABLE(bash-shell-execve-plugin, AC_HELP_STRING([--enable-bash-shell-execve-plugin], [enable bash shell execve plugin features]), opt_bash_shell_execve_plugin=$enableval) + + dnl options that alter how bash is compiled and linked + AC_ARG_ENABLE(mem-scramble, AC_HELP_STRING([--enable-mem-scramble], [scramble memory on calls to malloc and free]), opt_memscramble=$enableval) +@@ -260,6 +263,13 @@ dnl opt_readline and opt_history are handled later, because AC_PROG_CC needs + dnl to be run before we can check the version of an already-installed readline + dnl library + ++DYNAMICLOAD_LIB= ++if test $opt_bash_shell_execve_plugin = yes; then ++AC_DEFINE(BASH_SHELL_EXECVE_PLUGIN) ++DYNAMICLOAD_LIB=-ldl ++fi ++AC_SUBST(DYNAMICLOAD_LIB) ++ + if test $opt_alias = yes; then + AC_DEFINE(ALIAS) + fi +diff --git a/bash-5.1/execute_cmd.c b/bash-5.1/execute_cmd.c +index d2a0dd7..fb05489 100644 +--- a/bash-5.1/execute_cmd.c ++++ b/bash-5.1/execute_cmd.c +@@ -82,6 +82,10 @@ extern int errno; + # include "test.h" + #endif + ++#if defined (BASH_SHELL_EXECVE_PLUGIN) ++#include "plugin.h" ++#endif /* BASH_SHELL_EXECVE_PLUGIN */ ++ + #include "builtins/common.h" + #include "builtins/builtext.h" /* list of builtins */ + +@@ -5592,6 +5596,18 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out, + leave it there, in the same format that the user used to + type it in. */ + args = strvec_from_word_list (words, 0, 0, (int *)NULL); ++ ++#if defined (BASH_SHELL_EXECVE_PLUGIN) ++ result = invoke_plugin_on_shell_execve (current_user.user_name, command, args); ++ ++#if defined (DEBUG) ++ itrace("invoke_plugin_on_shell_execve: failed invoke plugin with user:%s, command:%s, result: %d", current_user.user_name, command, result); ++#endif ++ if (result) { ++ exit (EXECUTION_FAILURE); ++ } ++#endif /* BASH_SHELL_EXECVE_PLUGIN */ ++ + exit (shell_execve (command, args, export_env)); + } + else +diff --git a/bash-5.1/plugin.c b/bash-5.1/plugin.c +new file mode 100644 +index 0000000..df72830 +--- /dev/null ++++ b/bash-5.1/plugin.c +@@ -0,0 +1,428 @@ ++/* plugin.c -- Bash plugin support. */ ++ ++/* Copyright (C) 1987-2016 Free Software Foundation, Inc. ++ ++ This file is part of GNU Bash, the Bourne Again SHell. ++ ++ Bash is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ ++ Bash is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with Bash. If not, see . ++*/ ++ ++#include "config.h" ++ ++#if !defined (__GNUC__) && !defined (HAVE_ALLOCA_H) && defined (_AIX) ++ #pragma alloca ++#endif /* _AIX && RISC6000 && !__GNUC__ */ ++ ++// disable bash memory management when build for UT, PTR_T defined in xmalloc.h, define here for disable warning ++#if defined (BASH_PLUGIN_UT) ++# define _XMALLOC_H_ ++# define PTR_T void * ++# define malloc mock_malloc ++# define free mock_free ++#else ++#endif ++ ++#include ++#include ++#include "chartypes.h" ++#include "bashtypes.h" ++#if !defined (_MINIX) && defined (HAVE_SYS_FILE_H) ++# include ++#endif ++#include "filecntl.h" ++#include "posixstat.h" ++#include ++#include ++#if defined (HAVE_SYS_PARAM_H) ++# include ++#endif ++ ++#if defined (HAVE_UNISTD_H) ++# include ++#endif ++ ++#include "posixtime.h" ++ ++#if defined (HAVE_SYS_RESOURCE_H) && !defined (RLIMTYPE) ++# include ++#endif ++ ++#if defined (HAVE_SYS_TIMES_H) && defined (HAVE_TIMES) ++# include ++#endif ++ ++#include ++ ++#if !defined (errno) ++extern int errno; ++#endif ++ ++#define NEED_FPURGE_DECL ++#define NEED_SH_SETLINEBUF_DECL ++ ++#include "bashansi.h" ++#include "bashintl.h" ++ ++#include "shell.h" ++#include /* use <...> so we pick it up from the build directory */ ++#include "error.h" ++#include "flags.h" ++#include "builtins.h" ++#include "hashlib.h" ++#include "jobs.h" ++#include "execute_cmd.h" ++#include "findcmd.h" ++#include "redir.h" ++#include "trap.h" ++#include "pathexp.h" ++#include "hashcmd.h" ++ ++ ++#if defined (BASH_SHELL_EXECVE_PLUGIN) ++#include "plugin.h" ++#endif /* BASH_SHELL_EXECVE_PLUGIN */ ++ ++#if defined (COND_COMMAND) ++# include "test.h" ++#endif ++ ++#include "builtins/common.h" ++ ++#include "builtins/getopt.h" ++ ++#include ++#include ++ ++#if defined (BUFFERED_INPUT) ++# include "input.h" ++#endif ++ ++#if defined (ALIAS) ++# include "alias.h" ++#endif ++ ++#if defined (HISTORY) ++# include "bashhist.h" ++#endif ++ ++#if defined (HAVE_MBSTR_H) && defined (HAVE_MBSCHR) ++# include /* mbschr */ ++#endif ++ ++/* configuration buffer size */ ++#define CONFIG_BUFFER_SIZE 256 ++ ++/* configuration plugin setting key */ ++#define CONFIG_PLUGIN_SETTING_KEY "plugin=" ++ ++/* configuration termination characters */ ++#define CONFIG_SETTING_TERMINATIONS " \t\n\r\f" ++ ++/* configuration comments start character */ ++#define CONFIG_SETTING_COMMENTS_START_CHAR '#' ++ ++/* plugin configration file */ ++const char *plugin_config_file = "/etc/bash_plugins.conf"; ++ ++/* plugin on_shell_execve function name */ ++static const char *on_shell_execve_function_name = "on_shell_execve"; ++ ++/* plugin plugin_init function name */ ++static const char *plugin_init_function_name = "plugin_init"; ++ ++/* plugin plugin_uninit function name */ ++static const char *plugin_uninit_function_name = "plugin_uninit"; ++ ++/* plugin handle for test */ ++PLUGIN_NODE *global_plugin_list = NULL; ++ ++/* Load plugin by plugin path */ ++int ++append_plugin( ++ plugin_handle, ++ on_shell_execve, ++ plugin_init, ++ plugin_uninit) ++ void *plugin_handle; ++ on_shell_execve_t *on_shell_execve; ++ plugin_init_t *plugin_init; ++ plugin_uninit_t *plugin_uninit; ++{ ++ /* Create and initialize new plugin */ ++ PLUGIN_NODE *new_plugin_node = (PLUGIN_NODE*)malloc(sizeof(PLUGIN_NODE)); ++ if (new_plugin_node == NULL) ++ { ++ /* When allocate memory failed, stop and return. also output log to both syslog and stderr with LOG_PERROR*/ ++ syslog(LOG_PERROR, "Plugin: failed to allocate memory for plugin node.\n"); ++ return PLUGIN_LOAD_FAILED; ++ } ++ ++ new_plugin_node->next = NULL; ++ new_plugin_node->plugin_handle = plugin_handle; ++ new_plugin_node->on_shell_execve = on_shell_execve; ++ new_plugin_node->plugin_init = plugin_init; ++ new_plugin_node->plugin_uninit = plugin_uninit; ++ ++#ifdef DEBUG ++ itrace("Plugin: append plugin node %p to global list %p\n", new_plugin_node, global_plugin_list); ++#endif ++ ++ /* Find the pointer to the latest plugin node's 'next' field */ ++ PLUGIN_NODE **current_plugin_node = &global_plugin_list; ++ while (*current_plugin_node != NULL) { ++ current_plugin_node = &((*current_plugin_node)->next); ++ ++#ifdef DEBUG ++ itrace("Plugin: founded next plugin node: %p\n", *current_plugin_node); ++#endif ++ } ++ ++ /* append new plugin to tail node */ ++ *current_plugin_node = new_plugin_node; ++ ++#ifdef DEBUG ++ itrace("Plugin: append new plugin node %p to %p\n", new_plugin_node, current_plugin_node); ++#endif ++ ++ return PLUGIN_LOAD_SUCCESS; ++} ++ ++ ++/* Load plugin by plugin path */ ++void ++try_load_plugin_by_path(plugin_path) ++ const char *plugin_path; ++{ ++ /* Plugin handle */ ++ void *plugin_handle; ++ if ( (plugin_handle = dlopen(plugin_path, RTLD_LAZY)) == NULL) { ++#ifdef DEBUG ++ itrace("Plugin: can't load plugin %s: %s\n", plugin_path, dlerror()); ++#endif ++ return; ++ } ++ ++ /* Check if plugin support shell execve method */ ++ on_shell_execve_t* plugin_on_shell_execve_handle = dlsym(plugin_handle, on_shell_execve_function_name); ++ if (dlerror() != NULL) { ++ dlclose(plugin_handle); ++ ++#ifdef DEBUG ++ itrace("Plugin: can't find on_shell_execve function %s: %s\n", plugin_path, dlerror()); ++#endif ++ return; ++ } ++ ++ ++ /* Check if plugin support un-initialization method */ ++ plugin_uninit_t* plugin_uninit_handle = dlsym(plugin_handle, plugin_uninit_function_name); ++ if (dlerror() != NULL) { ++ dlclose(plugin_handle); ++ ++#ifdef DEBUG ++ itrace("Plugin: can't find plugin_uninit function %s: %s\n", plugin_path, dlerror()); ++#endif ++ return; ++ } ++ ++ /* Check if plugin support initialization method */ ++ plugin_init_t* plugin_init_handle = dlsym(plugin_handle, plugin_init_function_name); ++ if (dlerror() != NULL) { ++ dlclose(plugin_handle); ++ ++#ifdef DEBUG ++ itrace("Plugin: can't find plugin_init function %s: %s\n", plugin_path, dlerror()); ++#endif ++ return; ++ } ++ else { ++ /* Initialize plugin */ ++ plugin_init_handle(); ++ } ++ ++ /* Add plugin to plugin list */ ++ int plugin_load_result = append_plugin(plugin_handle, ++ plugin_on_shell_execve_handle, ++ plugin_init_handle, ++ plugin_uninit_handle); ++ ++ if (plugin_load_result == PLUGIN_LOAD_SUCCESS) { ++#ifdef DEBUG ++ itrace("Plugin: plugin %s loaded\n", plugin_path); ++#endif ++ } ++ else { ++ /* Output plugin load error message, also output log to both syslog and stderr with LOG_PERROR*/ ++ syslog(LOG_PERROR,"Plugin: plugin %s load failed, result: %d\n", plugin_path, plugin_load_result); ++ } ++} ++ ++/* Load plugin by config file */ ++void ++load_plugin_by_config(config_filename) ++ const char *config_filename; ++{ ++ FILE *config_file; ++ char buffer[CONFIG_BUFFER_SIZE]; ++ ++ config_file = fopen(config_filename, "r"); ++ if(config_file == NULL) { ++#ifdef DEBUG ++ itrace("Plugin: can't open plugin config file %s: %s\n", config_filename, strerror(errno)); ++#endif ++ return; ++ } ++ ++ while(fgets(buffer, sizeof buffer, config_file)) { ++ if(*buffer == CONFIG_SETTING_COMMENTS_START_CHAR || isspace(*buffer)) { ++ /* ignore comments or white space. */ ++ continue; ++ } ++ ++ /* read to first whitespace. */ ++ strtok(buffer, CONFIG_SETTING_TERMINATIONS); ++ ++ if(!strncmp(buffer, CONFIG_PLUGIN_SETTING_KEY, strlen(CONFIG_PLUGIN_SETTING_KEY))) { ++ /* read plugin path. */ ++ char* plugin_path = strtok(buffer + strlen(CONFIG_PLUGIN_SETTING_KEY), CONFIG_SETTING_TERMINATIONS); ++#ifdef DEBUG ++ itrace("Plugin: load plugin: %s\n", plugin_path); ++#endif ++ try_load_plugin_by_path(plugin_path); ++ } ++#ifdef DEBUG ++ else { ++ /* output debug message. */ ++ itrace("Plugin: unrecognized parameter: %s\n", buffer); ++ } ++#endif ++ } ++ ++ fclose(config_file); ++} ++ ++/* Free loaded plugins */ ++void ++free_loaded_plugins() ++{ ++ if (global_plugin_list == NULL) { ++ return; ++ } ++ ++#ifdef DEBUG ++ itrace("Plugin: start free plugin from global list %p\n", global_plugin_list); ++#endif ++ ++ /* Walk to last plugin */ ++ PLUGIN_NODE *next_plugin_node = global_plugin_list; ++ while (next_plugin_node != NULL) { ++ ++ /* Unload plugin */ ++ next_plugin_node->plugin_uninit(); ++ dlclose(next_plugin_node->plugin_handle); ++ ++ /* Continue with next pligin */ ++ PLUGIN_NODE* current_plugin_node_memory = next_plugin_node; ++ next_plugin_node = next_plugin_node->next; ++ ++#ifdef DEBUG ++ itrace("Plugin: next plugin address %p\n", next_plugin_node); ++#endif ++ ++ /* Free plugin node memory, this may also reset all allocated memory depends on c lib implementation */ ++ free(current_plugin_node_memory); ++ } ++ ++ /* Reset plugin list */ ++ global_plugin_list = NULL; ++} ++ ++/* Invoke loaded plugins */ ++int ++invoke_loaded_plugins (user, shell_level, cmd, argv) ++ char *user; ++ int shell_level; ++ char *cmd; ++ char **argv; ++{ ++ if (global_plugin_list == NULL) { ++ return 0; ++ } ++ ++#ifdef DEBUG ++ itrace("Plugin: start invoke plugin from global list %p\n", global_plugin_list); ++#endif ++ ++ /* Walk to last plugin */ ++ PLUGIN_NODE *next_plugin_node = global_plugin_list; ++ while (next_plugin_node != NULL) { ++ ++ /* Call plugin method */ ++ int plugin_error_code = next_plugin_node->on_shell_execve(user, shell_level, cmd, argv); ++ if (plugin_error_code != 0) { ++#ifdef DEBUG ++ itrace("Plugin: on_execve return error: %d\n", plugin_error_code); ++#endif ++ /* Exit when plugin failed */ ++ return plugin_error_code; ++ } ++ ++ /* Continue with next pligin */ ++ next_plugin_node = next_plugin_node->next; ++ ++#ifdef DEBUG ++ itrace("Plugin: next plugin address %p\n", next_plugin_node); ++#endif ++ } ++ ++ return 0; ++} ++ ++/* Load all plugins。 */ ++void ++load_plugins () ++{ ++ load_plugin_by_config(plugin_config_file); ++} ++ ++/* Free all plugins */ ++void ++free_plugins () ++{ ++ free_loaded_plugins(); ++} ++ ++/* Invoke plugins before shell execve */ ++int ++invoke_plugin_on_shell_execve (user, cmd, argv) ++ char *user; ++ char *cmd; ++ char **argv; ++{ ++ const char* shell_level_str = get_string_value ("SHLVL"); ++ const int shell_level = atoi (shell_level_str); ++ ++ if (absolute_program (cmd)) { ++ // find real path for relative path command ++ char resolved_path[PATH_MAX]; ++ ++ // real_path_buffer should not free here because we pass resolved_path as parameter. ++ char* real_path_buffer = realpath(cmd, resolved_path); ++ ++ return invoke_loaded_plugins(user, shell_level, resolved_path, argv); ++ } ++ else { ++ return invoke_loaded_plugins(user, shell_level, cmd, argv); ++ } ++} +diff --git a/bash-5.1/plugin.h b/bash-5.1/plugin.h +new file mode 100644 +index 0000000..116b2c5 +--- /dev/null ++++ b/bash-5.1/plugin.h +@@ -0,0 +1,79 @@ ++/* plugin.h - functions from plugin.c. */ ++ ++/* Copyright (C) 1993-2015 Free Software Foundation, Inc. ++ ++ This file is part of GNU Bash, the Bourne Again SHell. ++ ++ Bash is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ ++ Bash is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with Bash. If not, see . ++*/ ++ ++#if !defined (_PLUGIN_H_) ++#define _PLUGIN_H_ ++ ++#include "stdc.h" ++ ++/* System-wide bash plugin configuration. */ ++#define SYS_BASH_PLUGIN "/etc/bash.plugin" ++ ++/* Plugin load result. */ ++#define PLUGIN_LOAD_SUCCESS 0 ++#define PLUGIN_LOAD_FAILED 1 ++ ++typedef enum { T_COMMAND } plugin_type_t; ++ ++/* Bash plugin config. */ ++typedef struct bash_plugin_conf ++{ ++ const char *path; /* path to binary */ ++ char *name; /* Used to distinguish plugins */ ++} bash_plugin_conf_t; ++ ++/* plugin on_shell_execve function handle type */ ++typedef int on_shell_execve_t (char *user, int shell_level, char *cmd, char **argv); ++ ++/* plugin plugin_init function handle type */ ++typedef int plugin_init_t (); ++ ++/* plugin plugin_uninit function handle type */ ++typedef int plugin_uninit_t (); ++ ++/* Plugin list node. */ ++typedef struct plugin_node { ++ ++ /* Next plugin pointer. */ ++ struct plugin_node *next; ++ ++ /* Plugin library handle. */ ++ void *plugin_handle; ++ ++ /* Plugin on_shell_execve function handle. */ ++ on_shell_execve_t *on_shell_execve; ++ ++ /* Plugin plugin_init function handle. */ ++ plugin_init_t *plugin_init; ++ ++ /* Plugin plugin_uninit function handle. */ ++ plugin_uninit_t *plugin_uninit; ++} PLUGIN_NODE; ++ ++/* Load all plugins */ ++extern void load_plugins __P((void)); ++ ++/* Free all plugins */ ++extern void free_plugins __P((void)); ++ ++/* Invoke plugins before shell execve */ ++extern int invoke_plugin_on_shell_execve __P((char *, char *, char **)); ++ ++#endif /* _PLUGIN_H_ */ +diff --git a/bash-5.1/shell.c b/bash-5.1/shell.c +index ce8087f..6928208 100644 +--- a/bash-5.1/shell.c ++++ b/bash-5.1/shell.c +@@ -46,6 +46,10 @@ + # include + #endif + ++#if defined (BASH_SHELL_EXECVE_PLUGIN) ++#include "plugin.h" ++#endif /* BASH_SHELL_EXECVE_PLUGIN */ ++ + #include "bashintl.h" + + #define NEED_SH_SETLINEBUF_DECL /* used in externs.h */ +@@ -567,6 +571,10 @@ main (argc, argv, env) + if (shopt_alist) + run_shopt_alist (); + ++#if defined (BASH_SHELL_EXECVE_PLUGIN) ++ load_plugins (); ++#endif /* BASH_SHELL_EXECVE_PLUGIN */ ++ + /* From here on in, the shell must be a normal functioning shell. + Variables from the environment are expected to be set, etc. */ + shell_initialize (); +@@ -810,6 +818,10 @@ main (argc, argv, env) + /* Read commands until exit condition. */ + reader_loop (); + exit_shell (last_command_exit_value); ++ ++#if defined (BASH_SHELL_EXECVE_PLUGIN) ++ free_plugins (); ++#endif /* BASH_SHELL_EXECVE_PLUGIN */ + } + + static int +-- +2.17.1.windows.2 + diff --git a/src/bash/patches/series b/src/bash/patches/series new file mode 100644 index 000000000000..a27ad05ca28b --- /dev/null +++ b/src/bash/patches/series @@ -0,0 +1 @@ +0001-Add-plugin-support-to-bash.patch \ No newline at end of file