From: Kevin Day Date: Sun, 7 Dec 2025 22:32:48 +0000 (-0600) Subject: Feature: Add f_process project. X-Git-Tag: 0.8.0~51 X-Git-Url: https://www.git.kevux.org/?a=commitdiff_plain;h=af54a088a6076db853bacb21ae950cf05479b850;p=fll Feature: Add f_process project. This provides pidfd functionality that I intend to utilize in future work. Provide `f_handle_t` for represetning `struct file_handle`. Several of the functions being represented are not in some libc implementations. Use `syscall()` to handle these. Provide defines for enabling/disabling the `syscall()` behavior. Provide stubs for mocking such functions in unit tests so as to avoid trying to mock `syscall()` itself. --- diff --git a/build/disable/level_0/f_process.h b/build/disable/level_0/f_process.h new file mode 100644 index 0000000..8da8e39 --- /dev/null +++ b/build/disable/level_0/f_process.h @@ -0,0 +1,9 @@ +#define _di_f_process_descriptor_clone_ +#define _di_f_process_descriptor_open_ +#define _di_f_process_descriptor_signal_ +#define _di_f_process_handle_from_path_at_ +#define _di_f_process_handle_open_at_ +#define _di_f_process_open_flag_d_ +#define _di_f_process_test_process_stub_pidfd_getfd_ +#define _di_f_process_test_process_stub_pidfd_open_ +#define _di_f_process_test_process_stub_pidfd_send_signal_ diff --git a/build/disable/level_0/f_type.h b/build/disable/level_0/f_type.h index f67c902..d71f03a 100644 --- a/build/disable/level_0/f_type.h +++ b/build/disable/level_0/f_type.h @@ -17,6 +17,7 @@ #define _di_f_gid_t_ #define _di_f_gids_t_ #define _di_f_gidss_t_ +#define _di_f_handle_t_ #define _di_f_id_t_ #define _di_f_ids_t_ #define _di_f_idss_t_ diff --git a/build/level_0/settings b/build/level_0/settings index e39783a..f348b16 100644 --- a/build/level_0/settings +++ b/build/level_0/settings @@ -136,7 +136,7 @@ build_sources_headers string/map_multi.h string/map_multis.h string/map_multiss. build_sources_headers string/static.h string/statics.h string/staticss.h build_sources_headers string/triple.h string/triples.h string/tripless.h build_sources_headers time.h time/common.h -build_sources_headers type.h type/cell.h type/date.h type/file.h type/fll.h type/mode.h type/number.h type/pid.h type/quantity.h type/range.h type/range_double.h type/state.h type/status.h type/time.h type/void.h +build_sources_headers type.h type/cell.h type/date.h type/file.h type/fll.h type/handle.h type/mode.h type/number.h type/pid.h type/quantity.h type/range.h type/range_double.h type/state.h type/status.h type/time.h type/void.h build_sources_headers type_array.h type_array_file.h type_array/common.h type_array/cell.h type_array/date.h type_array/file.h type_array/fll_id.h type_array/int8.h type_array/int16.h type_array/int32.h type_array/int64.h type_array/int128.h type_array/number_signed.h type_array/number_unsigned.h type_array/pid.h type_array/poll.h type_array/quantity.h type_array/quantitys.h type_array/quantityss.h type_array/range.h type_array/ranges.h type_array/rangess.h type_array/range_double.h type_array/range_doubles.h type_array/range_doubless.h type_array/state.h type_array/status.h type_array/time.h type_array/uint8.h type_array/uint16.h type_array/uint32.h type_array/uint64.h type_array/uint128.h build_sources_headers utf.h utf/common.h utf/convert.h utf/dynamic.h utf/dynamics.h utf/dynamicss.h utf/is.h utf/is_character.h utf/map.h utf/maps.h utf/mapss.h utf/map_multi.h utf/map_multis.h utf/map_multiss.h utf/static.h utf/statics.h utf/staticss.h utf/string.h utf/triple.h utf/triples.h utf/tripless.h @@ -164,6 +164,7 @@ environment LANG LC_ALL LC_COLLATE LC_CTYPE LC_FASTMSG LC_MESSAGES LC_MONETARY L #defines -D_f_file_rename_use_renameat2_ #defines -D_pthread_getname_np_unsupported_ -D_pthread_setname_np_unsupported_ defines -D_libcap_legacy_only_ +defines -D_en_use_syscall_pidfd_getfd_ -D_en_use_syscall_pidfd_open_ -D_en_use_syscall_pidfd_send_signal_ defines-android -D_di_f_thread_attribute_affinity_get_ -D_di_f_thread_attribute_affinity_set_ -D_di_f_thread_attribute_concurrency_get_ -D_di_f_thread_attribute_concurrency_set_ -D_di_f_thread_attribute_default_get_ -D_di_f_thread_attribute_default_set_ -D_di_f_thread_cancel_ -D_di_f_thread_cancel_state_set_ -D_di_f_thread_cancel_test_ -D_di_f_thread_join_try_ -D_di_f_thread_join_timed_ -D_pthread_mutex_prioceiling_unsupported_ -D_di_f_thread_semaphore_file_close_ -D_di_f_thread_semaphore_file_open_ -D_di_f_thread_semaphore_file_delete_ -D_di_f_thread_cancel_type_set_ defines-clang -D_clang_not_a_compile_time_constant_workaround_ defines-debug -D_en_f_status_debug_ diff --git a/build/monolithic/settings b/build/monolithic/settings index e2e436e..ebdf3d0 100644 --- a/build/monolithic/settings +++ b/build/monolithic/settings @@ -157,7 +157,7 @@ build_sources_headers level_0/string/map_multi.h level_0/string/map_multis.h lev build_sources_headers level_0/string/static.h level_0/string/statics.h level_0/string/staticss.h build_sources_headers level_0/string/triple.h level_0/string/triples.h level_0/string/tripless.h build_sources_headers level_0/time.h level_0/time/common.h -build_sources_headers level_0/type.h level_0/type/cell.h level_0/type/date.h level_0/type/file.h level_0/type/fll.h level_0/type/mode.h level_0/type/number.h level_0/type/pid.h level_0/type/quantity.h level_0/type/range.h level_0/type/range_double.h level_0/type/state.h level_0/type/status.h level_0/type/time.h level_0/type/void.h +build_sources_headers level_0/type.h level_0/type/cell.h level_0/type/date.h level_0/type/file.h level_0/type/fll.h level_0/type/handle.h level_0/type/mode.h level_0/type/number.h level_0/type/pid.h level_0/type/quantity.h level_0/type/range.h level_0/type/range_double.h level_0/type/state.h level_0/type/status.h level_0/type/time.h level_0/type/void.h build_sources_headers level_0/type_array.h level_0/type_array_file.h level_0/type_array/common.h level_0/type_array/cell.h level_0/type_array/date.h level_0/type_array/file.h level_0/type_array/fll_id.h level_0/type_array/int8.h level_0/type_array/int16.h level_0/type_array/int32.h level_0/type_array/int64.h level_0/type_array/int128.h level_0/type_array/number_signed.h level_0/type_array/number_unsigned.h level_0/type_array/pid.h level_0/type_array/poll.h level_0/type_array/quantity.h level_0/type_array/quantitys.h level_0/type_array/quantityss.h level_0/type_array/range.h level_0/type_array/ranges.h level_0/type_array/rangess.h level_0/type_array/range_double.h level_0/type_array/range_doubles.h level_0/type_array/range_doubless.h level_0/type_array/state.h level_0/type_array/status.h level_0/type_array/time.h level_0/type_array/uint8.h level_0/type_array/uint16.h level_0/type_array/uint32.h level_0/type_array/uint64.h level_0/type_array/uint128.h build_sources_headers level_0/utf.h level_0/utf/common.h level_0/utf/convert.h level_0/utf/dynamic.h level_0/utf/dynamics.h level_0/utf/dynamicss.h level_0/utf/is.h level_0/utf/is_character.h level_0/utf/map.h level_0/utf/maps.h level_0/utf/mapss.h level_0/utf/map_multi.h level_0/utf/map_multis.h level_0/utf/map_multiss.h level_0/utf/static.h level_0/utf/statics.h level_0/utf/staticss.h level_0/utf/string.h level_0/utf/triple.h level_0/utf/triples.h level_0/utf/tripless.h @@ -207,6 +207,7 @@ environment LANG LC_ALL LC_COLLATE LC_CTYPE LC_FASTMSG LC_MESSAGES LC_MONETARY L #defines -D_f_file_rename_use_renameat2_ #defines -D_pthread_getname_np_unsupported_ -D_pthread_setname_np_unsupported_ defines -D_libcap_legacy_only_ +defines -D_en_use_syscall_pidfd_getfd_ -D_en_use_syscall_pidfd_open_ -D_en_use_syscall_pidfd_send_signal_ defines-android -D_di_f_thread_attribute_affinity_get_ -D_di_f_thread_attribute_affinity_set_ -D_di_f_thread_attribute_concurrency_get_ -D_di_f_thread_attribute_concurrency_set_ -D_di_f_thread_attribute_default_get_ -D_di_f_thread_attribute_default_set_ -D_di_f_thread_cancel_ -D_di_f_thread_cancel_state_set_ -D_di_f_thread_cancel_test_ -D_di_f_thread_join_try_ -D_di_f_thread_join_timed_ -D_pthread_mutex_prioceiling_unsupported_ -D_di_f_thread_semaphore_file_close_ -D_di_f_thread_semaphore_file_open_ -D_di_f_thread_semaphore_file_delete_ -D_di_f_thread_cancel_type_set_ defines-clang -D_clang_not_a_compile_time_constant_workaround_ defines-debug -D_en_f_status_debug_ diff --git a/build/scripts/bootstrap-example.sh b/build/scripts/bootstrap-example.sh index bb24ee5..6a63712 100644 --- a/build/scripts/bootstrap-example.sh +++ b/build/scripts/bootstrap-example.sh @@ -160,7 +160,7 @@ boostrap_process() { ${shell_command} build/scripts/package.sh ${verbose} ${color} rebuild -i if [[ ${?} -eq 0 ]] ; then - for i in f_type f_status f_memory f_type_array f_string f_utf f_abstruse f_account f_capability f_color f_compare f_console f_control_group f_conversion f_directory f_environment f_execute f_file f_fss f_iki f_limit f_network f_parse f_path f_pipe f_print f_random f_rip f_schedule f_serialize f_signal f_socket f_status_string f_thread f_time fl_control_group fl_conversion fl_directory fl_environment fl_execute fl_fss fl_iki fl_path fl_print fl_status_string fl_utf_file fll_control_group fll_error fll_execute fll_file fll_fss fll_fss_status_string fll_iki fll_print fll_program ; do + for i in f_type f_status f_memory f_type_array f_string f_utf f_abstruse f_account f_capability f_color f_compare f_console f_control_group f_conversion f_directory f_environment f_execute f_file f_fss f_iki f_limit f_network f_parse f_path f_pipe f_print f_process f_random f_rip f_schedule f_serialize f_signal f_socket f_status_string f_thread f_time fl_control_group fl_conversion fl_directory fl_environment fl_execute fl_fss fl_iki fl_path fl_print fl_status_string fl_utf_file fll_control_group fll_error fll_execute fll_file fll_fss fll_fss_status_string fll_iki fll_print fll_program ; do echo && echo "Processing ${i}." && diff --git a/build/scripts/test.sh b/build/scripts/test.sh index c51b903..c221098 100644 --- a/build/scripts/test.sh +++ b/build/scripts/test.sh @@ -72,7 +72,7 @@ test_main() { local verbose= local verbose_common= - local projects="f_type f_status f_memory f_type_array f_string f_utf f_abstruse f_account f_capability f_color f_compare f_console f_control_group f_conversion f_directory f_environment f_execute f_file f_fss f_iki f_limit f_network f_parse f_path f_pipe f_print f_random f_rip f_schedule f_serialize f_signal f_socket f_status_string f_thread f_time fl_control_group fl_conversion fl_directory fl_environment fl_execute fl_fss fl_iki fl_path fl_print fl_status_string fl_utf_file fll_control_group fll_error fll_execute fll_file fll_fss fll_fss_status_string fll_iki fll_print fll_program" + local projects="f_type f_status f_memory f_type_array f_string f_utf f_abstruse f_account f_capability f_color f_compare f_console f_control_group f_conversion f_directory f_environment f_execute f_file f_fss f_iki f_limit f_network f_parse f_path f_pipe f_print f_process f_random f_rip f_schedule f_serialize f_signal f_socket f_status_string f_thread f_time fl_control_group fl_conversion fl_directory fl_environment fl_execute fl_fss fl_iki fl_path fl_print fl_status_string fl_utf_file fll_control_group fll_error fll_execute fll_file fll_fss fll_fss_status_string fll_iki fll_print fll_program" local projects_no_tests="f_type" local programs="fss_read" diff --git a/level_0/f_process/c/process.c b/level_0/f_process/c/process.c new file mode 100644 index 0000000..9ba3c81 --- /dev/null +++ b/level_0/f_process/c/process.c @@ -0,0 +1,155 @@ +#include "process.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _di_f_process_descriptor_clone_ + f_status_t f_process_descriptor_clone(const pid_t pid, const int clone, const unsigned int flags, int * const id) { + #ifndef _di_level_0_parameter_checking_ + if (!id) return F_status_set_error(F_parameter); + #endif // _di_level_0_parameter_checking_ + + #ifdef _en_use_syscall_pidfd_getfd_ + const int result = syscall(SYS_pidfd_getfd, pid, clone, flags); + #else + const int result = pidfd_getfd(pid, clone, flags); + #endif //_en_use_syscall_pidfd_getfd_ + + if (result == -1) { + if (errno == EBADF) return F_status_set_error(F_file_descriptor_not); + if (errno == EINVAL) return F_status_set_error(F_parameter); + if (errno == EMFILE) return F_status_set_error(F_file_descriptor_max); + if (errno == ENFILE) return F_status_set_error(F_file_open_max); + if (errno == EPERM) return F_status_set_error(F_prohibited); + if (errno == ESRCH) return F_status_set_error(F_search); + + return F_status_set_error(F_failure); + } + + *id = result; + + return F_okay; + } +#endif // _di_f_process_descriptor_clone_ + +#ifndef _di_f_process_descriptor_open_ + f_status_t f_process_descriptor_open(const pid_t pid, const unsigned int flags, int * const id) { + #ifndef _di_level_0_parameter_checking_ + if (!id) return F_status_set_error(F_parameter); + #endif // _di_level_0_parameter_checking_ + + #ifdef _en_use_syscall_pidfd_open_ + const int result = syscall(SYS_pidfd_open, pid, flags); + #else + const int result = pidfd_open(pid, flags); + #endif //_en_use_syscall_pidfd_open_ + + if (result == -1) { + if (errno == EINVAL) return F_status_set_error(F_parameter); + if (errno == EMFILE) return F_status_set_error(F_file_descriptor_max); + if (errno == ENFILE) return F_status_set_error(F_file_open_max); + if (errno == ENODEV) return F_status_set_error(F_device_not); + if (errno == ENOMEM) return F_status_set_error(F_memory_not); + if (errno == ESRCH) return F_status_set_error(F_search); + + return F_status_set_error(F_failure); + } + + *id = result; + + return F_okay; + } +#endif // _di_f_process_descriptor_open_ + +#ifndef _di_f_process_descriptor_signal_ + f_status_t f_process_descriptor_signal(const int id, const int signal, const unsigned int flags, siginfo_t * const information) { + + #ifdef _en_use_syscall_pidfd_send_signal_ + const int result = syscall(SYS_pidfd_send_signal, id, signal, information, flags); + #else + const int result = pidfd_send_signal(id, signal, information, flags); + #endif //_en_use_syscall_pidfd_send_signal_ + + if (result == -1) { + if (errno == EBADF) return F_status_set_error(F_file_descriptor_not); + if (errno == EINVAL) return F_status_set_error(F_parameter); + if (errno == EPERM) return F_status_set_error(F_prohibited); + if (errno == ESRCH) return F_status_set_error(F_search); + + return F_status_set_error(F_failure); + } + + return F_okay; + } +#endif // _di_f_process_descriptor_signal_ + +#ifndef _di_f_process_handle_from_path_at_ + f_status_t f_process_handle_from_path_at(const int at_id, const f_string_static_t path, const int flags, f_handle_t * const handle, int * const id) { + #ifndef _di_level_0_parameter_checking_ + if (!handle || !id) return F_status_set_error(F_parameter); + #endif // _di_level_0_parameter_checking_ + + const int result = name_to_handle_at(at_id, path.string, handle, id, flags); + + if (result == -1) { + if (errno == EACCES) return F_status_set_error(F_access_denied); + if (errno == EBADF) return F_status_set_error(F_directory_descriptor_not); + if (errno == EFAULT) return F_status_set_error(F_buffer); + if (errno == EFBIG || errno == EOVERFLOW) return F_status_set_error(F_number_overflow); + if (errno == EINTR) return F_status_set_error(F_interrupt); + if (errno == EINVAL) return F_status_set_error(F_parameter); + if (errno == ELOOP) return F_status_set_error(F_loop); + if (errno == ENAMETOOLONG) return F_status_set_error(F_name); + if (errno == ENFILE) return F_status_set_error(F_file_open_max); + if (errno == ENOENT) return F_status_set_error(F_directory_found_not); + if (errno == ENOMEM) return F_status_set_error(F_memory_not); + if (errno == ENOTDIR) return F_status_set_error(F_directory_not); + if (errno == ENOSPC) return F_status_set_error(F_space_not); + if (errno == EOPNOTSUPP) return F_status_set_error(F_support_not); + if (errno == EPERM) return F_status_set_error(F_prohibited); + if (errno == EROFS) return F_status_set_error(F_read_only); + + return F_status_set_error(F_failure); + } + + return F_okay; + } +#endif // _di_f_process_handle_from_path_at_ + +#ifndef _di_f_process_handle_open_at_ + f_status_t f_process_handle_open_at(const int at_id, const unsigned int flags, f_handle_t * const handle) { + #ifndef _di_level_0_parameter_checking_ + if (!handle) return F_status_set_error(F_parameter); + #endif // _di_level_0_parameter_checking_ + + const int result = open_by_handle_at(at_id, handle, flags); + + if (result == -1) { + if (errno == EACCES) return F_status_set_error(F_access_denied); + if (errno == EBADF) return F_status_set_error(F_directory_descriptor_not); + if (errno == EFAULT) return F_status_set_error(F_buffer); + if (errno == EFBIG || errno == EOVERFLOW) return F_status_set_error(F_number_overflow); + if (errno == EINTR) return F_status_set_error(F_interrupt); + if (errno == EINVAL) return F_status_set_error(F_parameter); + if (errno == ELOOP) return F_status_set_error(F_loop); + if (errno == ENAMETOOLONG) return F_status_set_error(F_name); + if (errno == ENFILE) return F_status_set_error(F_file_open_max); + if (errno == ENOENT) return F_status_set_error(F_directory_found_not); + if (errno == ENOMEM) return F_status_set_error(F_memory_not); + if (errno == ENOTDIR) return F_status_set_error(F_directory_not); + if (errno == ENOSPC) return F_status_set_error(F_space_not); + if (errno == EPERM) return F_status_set_error(F_prohibited); + if (errno == EROFS) return F_status_set_error(F_read_only); + if (errno == ESTALE) return F_status_set_error(F_stale); + + return F_status_set_error(F_failure); + } + + return F_okay; + } +#endif // _di_f_process_handle_open_at_ + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/level_0/f_process/c/process.h b/level_0/f_process/c/process.h new file mode 100644 index 0000000..19abc7c --- /dev/null +++ b/level_0/f_process/c/process.h @@ -0,0 +1,223 @@ +/** + * FLL - Level 0 + * + * Project: Process + * API Version: 0.8 + * Licenses: lgpl-2.1-or-later + * + * Provides process related functionality. + */ +#ifndef _F_process_h +#define _F_process_h + +// For fcntl.h. +#ifndef _GNU_SOURCE + #define _GNU_SOURCE +#endif // _GNU_SOURCE + +// Libc includes. +#include +#include +#include +#include +#include +#include + +// FLL-0 includes. +#include +#include +#include +#include +#include + +// FLL-0 process includes. +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Clone a file descriptor from some process. + * + * @param pid + * The process ID to get the descriptor of. + * @param clone + * The process file descriptor to clone. + * @param flags + * Flags to use. + * @param id + * The file descriptor. + * A value of -1 designates that this is not set. + * + * Must not be NULL. + * + * @return + * F_okay on success. + * + * F_file_descriptor_max (with error bit) if max file descriptors is reached. + * F_file_descriptor_not (with error bit) if the file descriptor is invalid. + * F_file_open_max (with error bit) when system-wide max open files is reached. + * F_parameter (with error bit) if a parameter is invalid. + * F_prohibited (with error bit) if the current process is not allowed to clone the requested file descriptor. + * F_search (with error bit) if the process could not be found. + * + * F_failure (with error bit) on any other error. + * + * @see pidfd_getfd() + */ +#ifndef _di_f_process_descriptor_clone_ + extern f_status_t f_process_descriptor_clone(const pid_t pid, const int clone, const unsigned int flags, int * const id); +#endif // _di_f_process_descriptor_clone_ + +/** + * Open a file descriptor of some process. + * + * @param pid + * The process ID to get the descriptor of. + * @param flags + * Flags to use when opening (see f_process_open_flag_*_d). + * @param id + * The file descriptor. + * A value of -1 designates that this is not set. + * + * Must not be NULL. + * + * @return + * F_okay on success. + * + * F_device_not (with error bit) if the device does not exist. + * F_file_descriptor_max (with error bit) if max file descriptors is reached. + * F_file_open_max (with error bit) when system-wide max open files is reached. + * F_memory_not (with error bit) if out of memory. + * F_parameter (with error bit) if a parameter is invalid. + * F_search (with error bit) if the process could not be found. + * + * F_failure (with error bit) on any other error. + * + * @see pidfd_open() + */ +#ifndef _di_f_process_descriptor_open_ + extern f_status_t f_process_descriptor_open(const pid_t pid, const unsigned int flags, int * const id); +#endif // _di_f_process_descriptor_open_ + +/** + * Send a signal to a process file descriptor. + * + * @param id + * The process file descriptor to send the signal to. + * @param signal + * The signal to send. + * @param flags + * Flags to use. + * @param information + * (optional) The signal information. + * + * @return + * F_okay on success. + * + * F_file_descriptor_not (with error bit) if the file descriptor is invalid. + * F_parameter (with error bit) if a parameter is invalid. + * F_prohibited (with error bit) if the current process is not allowed to signal the requested file descriptor. + * F_search (with error bit) if the process could not be found. + * + * F_failure (with error bit) on any other error. + * + * @see pidfd_send_signal() + */ +#ifndef _di_f_process_descriptor_signal_ + extern f_status_t f_process_descriptor_signal(const int id, const int signal, const unsigned int flags, siginfo_t * const information); +#endif // _di_f_process_descriptor_signal_ + +/** + * Get a file handle and mount ID from some file path. + * + * @param at_id + * The parent directory, as an open directory file descriptor, in which path is relative to. + * @param path + * The file path to the file or directory. + * @param flags + * Flags to use. + * @param handle + * The retrieved file handle data. + * + * Must not be NULL. + * @param id + * The retrieved mount ID. + * + * Must not be NULL. + * + * @return + * F_okay on success. + * + * F_access_denied (with error bit) on access denied. + * F_buffer (with error bit) if the buffer is invalid. + * F_directory_found_not (with error bit) if directory was not found. + * F_directory_not (with error bit) file is not a directory. + * F_directory_descriptor_not (with error bit) for bad directory descriptor for at_id. + * F_file_open_max (with error bit) too many open files. + * F_interrupt (with error bit) when program received an interrupt signal, halting operation. + * F_loop (with error bit) on loop error. + * F_memory_not (with error bit) if out of memory. + * F_name (with error bit) on path name error. + * F_number_overflow (with error bit) on integer overflow. + * F_parameter (with error bit) if a parameter is invalid. + * F_prohibited (with error bit) if file system does not allow for removing. + * F_read_only (with error bit) if file is read-only. + * F_space_not (with error bit) if file system is out of space (or file system quota is reached). + * F_support_not (with error bit) if the file system does not suppot converting a path to a file handle. + * + * F_failure (with error bit) on any other error. + * + * @see name_to_handle_at() + */ +#ifndef _di_f_process_handle_from_path_at_ + extern f_status_t f_process_handle_from_path_at(const int at_id, const f_string_static_t path, const int flags, f_handle_t * const handle, int * const id); +#endif // _di_f_process_handle_from_path_at_ + + +/** + * Open a file represented by the given handle. + * + * @param at_id + * The mount directory, as an open directory file descriptor, in which path is relative to. + * Such as from f_process_handle_from_path_at(). + * @param flags + * Flags to use. + * @param handle + * The file handle data. + * + * Must not be NULL. + * + * @return + * F_okay on success. + * + * F_access_denied (with error bit) on access denied. + * F_buffer (with error bit) if the buffer is invalid. + * F_directory_found_not (with error bit) if directory was not found. + * F_directory_not (with error bit) file is not a directory. + * F_directory_descriptor_not (with error bit) for bad directory descriptor for at_id. + * F_file_open_max (with error bit) too many open files. + * F_interrupt (with error bit) when program received an interrupt signal, halting operation. + * F_loop (with error bit) on loop error. + * F_memory_not (with error bit) if out of memory. + * F_name (with error bit) on path name error. + * F_number_overflow (with error bit) on integer overflow. + * F_parameter (with error bit) if a parameter is invalid. + * F_prohibited (with error bit) if file system does not allow for removing. + * F_read_only (with error bit) if file is read-only. + * F_space_not (with error bit) if file system is out of space (or file system quota is reached). + * + * F_failure (with error bit) on any other error. + * + * @see open_by_handle_at() + */ +#ifndef _di_f_process_handle_open_at_ + extern f_status_t f_process_handle_open_at(const int at_id, const unsigned int flags, f_handle_t * const handle); +#endif // _di_f_process_handle_open_at_ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _F_process_h diff --git a/level_0/f_process/c/process/common.h b/level_0/f_process/c/process/common.h new file mode 100644 index 0000000..f5cf11b --- /dev/null +++ b/level_0/f_process/c/process/common.h @@ -0,0 +1,33 @@ +/** + * FLL - Level 0 + * + * Project: Process + * API Version: 0.8 + * Licenses: lgpl-2.1-or-later + * + * Defines common data to be used for/by process related functionality. + * + * This is auto-included by process.h and should not need to be explicitly included. + */ +#ifndef _F_process_common_h +#define _F_process_common_h + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Provide process descriptor open flags. + * + * f_process_open_flag*_d: + * - block_not: Get a non-blocking file descriptor. + */ +#ifndef _di_f_process_open_flag_d_ + #define F_process_open_flag_block_not_d PIDFD_NONBLOCK +#endif // _di_f_process_open_flag_d_ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _F_process_common_h diff --git a/level_0/f_process/data/build/defines b/level_0/f_process/data/build/defines new file mode 100644 index 0000000..bae0441 --- /dev/null +++ b/level_0/f_process/data/build/defines @@ -0,0 +1,5 @@ +# fss-0000 + +_en_use_syscall_pidfd_getfd_ Use syscall() for the pidfd_getfd() (required on systems where the libc does not provide pidfd_getfd(). +_en_use_syscall_pidfd_open_ Use syscall() for the pidfd_open() (required on systems where the libc does not provide pidfd_open(). +_en_use_syscall_pidfd_send_signal_ Use syscall() for the pidfd_send_signal() (required on systems where the libc does not provide pidfd_send_signal(). diff --git a/level_0/f_process/data/build/dependencies b/level_0/f_process/data/build/dependencies new file mode 100644 index 0000000..03d7727 --- /dev/null +++ b/level_0/f_process/data/build/dependencies @@ -0,0 +1,7 @@ +# fss-0000 + +f_type +f_status +f_memory +f_type_array +f_string diff --git a/level_0/f_process/data/build/dependencies-tests b/level_0/f_process/data/build/dependencies-tests new file mode 100644 index 0000000..dea3179 --- /dev/null +++ b/level_0/f_process/data/build/dependencies-tests @@ -0,0 +1,3 @@ +# fss-0001 + +cmocka 1.* diff --git a/level_0/f_process/data/build/fakefile b/level_0/f_process/data/build/fakefile new file mode 100644 index 0000000..82cffd9 --- /dev/null +++ b/level_0/f_process/data/build/fakefile @@ -0,0 +1,20 @@ +# fss-0005 iki-0002 + +settings: + fail exit + modes individual individual_thread level monolithic clang coverage fanalyzer gcc gcc_13 test thread threadless + + environment PATH LD_LIBRARY_PATH + environment LANG LC_ALL LC_COLLATE LC_CTYPE LC_FASTMSG LC_MESSAGES LC_MONETARY LC_NUMERIC LC_TIME LOCPATH NLSPATH + +main: + build + +help: + print + print context:'title'Fakefile Options for FLL Software.context:'reset' + + print + print The following operations are available\: + print " - context:'notable'help:context:'reset' Perform the help operation, printing this message." + print " - context:'notable'main:context:'reset' The default compilation using the build settings mode." diff --git a/level_0/f_process/data/build/settings b/level_0/f_process/data/build/settings new file mode 100644 index 0000000..0141913 --- /dev/null +++ b/level_0/f_process/data/build/settings @@ -0,0 +1,73 @@ +# fss-0001 +# +# Modes: +# - android: Compile on an android system (using Termux; may need modification depending on the android system). +# - clang: Use CLang rather than the default, which is generally GCC. +# - coverage: Compile for building coverage. +# - debug: Enable debugging, such as compile time debug options. +# - fanalyzer: Compile using GCC's -fanalyzer compile time option. +# - gcc: Use GCC specific settings. +# - gcc_13: Use GCC version 13 or greater specific settings. +# - individual: Compile using per project (individual) libraries, does not handle thread or threadless cases. +# - individual_thread: This is required when compiling in individual mode with "thread" mode. +# - level: Compile using per level libraries. +# - monolithic: Compile using per monolithic libraries. +# - test: Compile for a test, such as unit testing. +# - thread: Compile with thread support. +# - threadless: Compile without thread support. +# + +build_name f_process + +version_major 0 +version_minor 8 +version_micro 0 +version_file micro +version_target minor + +modes android clang coverage debug fanalyzer gcc gcc_13 individual individual_thread level monolithic test thread threadless +modes_default debug gcc individual individual_thread thread + +build_compiler gcc +build_compiler-clang clang +build_indexer ar +build_indexer_arguments rcs +build_language c + +build_libraries_shared -lc -lcap +build_libraries_shared-individual -lf_memory -lf_string -lf_type_array + +build_libraries_static -l:libc.a -l:libcap.a +build_libraries_static-individual -l:libf_memory.a -l:libf_string.a -l:libf_type_array.a + +build_sources_library process.c + +build_sources_headers process.h process/common.h + +build_static no + +path_headers fll/level_0 + +environment PATH LD_LIBRARY_PATH +environment LANG LC_ALL LC_COLLATE LC_CTYPE LC_FASTMSG LC_MESSAGES LC_MONETARY LC_NUMERIC LC_TIME LOCPATH NLSPATH + +defines -D_en_use_syscall_pidfd_getfd_ -D_en_use_syscall_pidfd_open_ -D_en_use_syscall_pidfd_send_signal_ +defines-android -D_di_f_thread_attribute_affinity_get_ -D_di_f_thread_attribute_affinity_set_ -D_di_f_thread_attribute_concurrency_get_ -D_di_f_thread_attribute_concurrency_set_ -D_di_f_thread_attribute_default_get_ -D_di_f_thread_attribute_default_set_ -D_di_f_thread_cancel_ -D_di_f_thread_cancel_state_set_ -D_di_f_thread_cancel_test_ -D_di_f_thread_join_try_ -D_di_f_thread_join_timed_ -D_pthread_mutex_prioceiling_unsupported_ -D_di_f_thread_semaphore_file_close_ -D_di_f_thread_semaphore_file_open_ -D_di_f_thread_semaphore_file_delete_ -D_di_f_thread_cancel_type_set_ +defines-debug -D_en_f_status_debug_ +defines-thread -D_pthread_attr_unsupported_ -D_pthread_sigqueue_unsupported_ +defines-threadless -D_di_thread_support_ + +flags -O2 -g -fdiagnostics-color=always -Wno-logical-not-parentheses -Wno-parentheses -Wno-missing-braces +flags -fstack-clash-protection -fno-delete-null-pointer-checks +flags -Wl,-z,nodlopen -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now +flags-android -Wno-implicit-function-declaration -Wl,-z,norelro +flags-clang -Wno-logical-op-parentheses +flags-coverage -O0 --coverage -fprofile-abs-path -fprofile-dir=build/coverage/ +flags-gcc_13 -fstrict-flex-arrays=3 +flags-test -O0 -fstack-protector-strong -Wall +flags-thread -pthread + +flags_library -fPIC +flags_object -fPIC +flags_program -fPIE +flags_program-android -fPIE -Wl,-z,relro diff --git a/level_0/f_process/data/build/settings-mocks b/level_0/f_process/data/build/settings-mocks new file mode 100644 index 0000000..337f32e --- /dev/null +++ b/level_0/f_process/data/build/settings-mocks @@ -0,0 +1,64 @@ +# fss-0001 +# +# Build the project with appropriate mocks linked in via the dynamic linker's "--wrap" functionality. +# +# The -Wl,--wrap does not work across shared files. +# Therefore, this file is a work-around to inject the mocks into the library for testing purposes. +# This should exactly match the "settings" file, except for the additional "-Wl,--wrap" parts and the additional mock source file. +# +# The flags -o0 must be passed to prevent the compiler from optimizing away any functions being mocked (which results in the mock not happening and a real function being called). +# Alternatively, figure out which optimization that is disabled by -o0 and have that specific optimization disabled. +# + +build_name f_process + +version_major 0 +version_minor 8 +version_micro 0 +version_file micro +version_target minor + +modes android clang coverage fanalyzer gcc gcc_13 individual individual_thread level monolithic test thread threadless +modes_default debug gcc individual individual_thread test thread + +build_compiler gcc +build_compiler-clang clang +build_indexer ar +build_indexer_arguments rcs +build_language c + +build_libraries -lc -lcap +build_libraries-individual -lf_memory -lf_string -lf_type_array + +build_sources_library process.c ../../tests/unit/c/mock-process.c ../../tests/unit/c/test-process_stubs.c + +build_sources_headers process.h process/common.h + +build_static no + +path_headers fll/level_0 + +environment PATH LD_LIBRARY_PATH +environment LANG LC_ALL LC_COLLATE LC_CTYPE LC_FASTMSG LC_MESSAGES LC_MONETARY LC_NUMERIC LC_TIME LOCPATH NLSPATH + +# Disable warning because the functions might not actually exist and a stub is linked in separately. +defines -Wno-implicit-function-declaration + +defines-debug -D_en_f_status_debug_ + +flags -O0 -g -fdiagnostics-color=always -Wno-logical-not-parentheses -Wno-parentheses -Wno-missing-braces +flags -fstack-clash-protection -fno-delete-null-pointer-checks +flags -Wl,-z,nodlopen -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now +flags-clang -Wno-logical-op-parentheses +flags-coverage --coverage -fprofile-abs-path -fprofile-dir=build/coverage/ +flags-gcc_13 -fstrict-flex-arrays=3 +flags-test -fstack-protector-strong -Wall + +flags_library -fPIC + +# Inject mocks. +flags -Wl,--wrap=name_to_handle_at +flags -Wl,--wrap=open_by_handle_at +flags -Wl,--wrap=pidfd_getfd +flags -Wl,--wrap=pidfd_open +flags -Wl,--wrap=pidfd_send_signal diff --git a/level_0/f_process/data/build/settings-tests b/level_0/f_process/data/build/settings-tests new file mode 100644 index 0000000..b9d9ac3 --- /dev/null +++ b/level_0/f_process/data/build/settings-tests @@ -0,0 +1,56 @@ +# fss-0001 +# +# Builds a program that is links to the generated library and is executed to perform tests. +# +# Memory leaks in the test program can be checked for by running valgrind with this executable. +# + +build_name test-f_process + +version_major 0 +version_minor 8 +version_micro 0 +version_file major +version_target major + +modes android clang coverage fanalyzer gcc gcc_13 individual individual_thread level monolithic test thread threadless +modes_default debug gcc individual individual_thread thread + +build_compiler gcc +build_compiler-clang clang +build_indexer ar +build_indexer_arguments rcs +build_language c + +build_libraries -lc -lcmocka +build_libraries-individual -lf_memory -lf_string -lf_type_array -lf_process + +build_sources_program test-process-descriptor_clone.c test-process-descriptor_open.c test-process-descriptor_signal.c test-process-handle_from_path_at.c test-process-handle_open_at.c +build_sources_program test-process.c + +build_script no +build_shared yes +build_static no + +path_headers tests/unit/c +path_sources tests/unit/c + +has_path_standard no + +environment PATH LD_LIBRARY_PATH +environment LANG LC_ALL LC_COLLATE LC_CTYPE LC_FASTMSG LC_MESSAGES LC_MONETARY LC_NUMERIC LC_TIME LOCPATH NLSPATH + +defines -Ibuild/includes +defines-debug -D_en_f_status_debug_ +defines_static -Lbuild/libraries/static +defines_shared -Lbuild/libraries/shared + +flags -O2 -g -fdiagnostics-color=always -Wno-logical-not-parentheses -Wno-parentheses -Wno-missing-braces +flags -fstack-clash-protection -fno-delete-null-pointer-checks +flags -Wl,-z,nodlopen -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now +flags-clang -Wno-logical-op-parentheses +flags-coverage -O0 --coverage -fprofile-abs-path -fprofile-dir=build/coverage/ +flags-gcc_13 -fstrict-flex-arrays=3 +flags-test -fstack-protector-strong -Wall + +flags_program -fPIE diff --git a/level_0/f_process/data/build/testfile b/level_0/f_process/data/build/testfile new file mode 100644 index 0000000..e1637fe --- /dev/null +++ b/level_0/f_process/data/build/testfile @@ -0,0 +1,73 @@ +# fss-0005 iki-0002 + +settings: + load_build yes + fail exit + + environment PATH LD_LIBRARY_PATH + environment LANG LC_ALL LC_COLLATE LC_CTYPE LC_FASTMSG LC_MESSAGES LC_MONETARY LC_NUMERIC LC_TIME LOCPATH NLSPATH + environment CMOCKA_XML_FILE CMOCKA_MESSAGE_OUTPUT CMOCKA_TEST_ABORT + + # Cmocka is not fully thread-safe, set this to "1" to have cmocka call abort() on a test failure. + #define CMOCKA_TEST_ABORT 1 + + # One of: STDOUT, SUBUNIT, TAP, or XML. + #define CMOCKA_MESSAGE_OUTPUT STDOUT + + # When in "XML" output mode, output to this file rather than stdout. + #define CMOCKA_XML_FILE ./out.xml + +main: + build settings-mocks individual test + build settings-tests individual test + + operate build_path + operate ld_library_path + + if exist parameter:"build_path"programs/shared/test-f_process + shell parameter:"build_path"programs/shared/test-f_process + + if exist parameter:"build_path"programs/static/test-f_process + shell parameter:"build_path"programs/static/test-f_process + + if not exist parameter:"build_path"programs/shared/test-f_process + and not exist parameter:"build_path"programs/static/test-f_process + operate not_created + +not_created: + print + print 'context:"error"Failed to test due to being unable to find either a shared or static test binary to perform tests. context:"reset"' + + exit failure + +build_path: + parameter build_path build/ + + if parameter build:value + parameter build_path parameter:"build:value" + +ld_library_path: + if define LD_LIBRARY_PATH + and parameter work:value + define LD_LIBRARY_PATH 'parameter:"build_path"libraries/shared:parameter:"work:value"libraries/shared:define:"LD_LIBRARY_PATH"' + + else + if define LD_LIBRARY_PATH + define LD_LIBRARY_PATH 'parameter:"build_path"libraries/shared:define:"LD_LIBRARY_PATH"' + + else + if parameter work:value + define LD_LIBRARY_PATH 'parameter:"build_path"libraries/shared:parameter:"work:value"libraries/shared' + + else + define LD_LIBRARY_PATH 'parameter:"build_path"libraries/shared' + +help: + print + print context:'title'Fakefile Options for FLL Software Testing.context:'reset' + print + + print + print The following operations are available\: + print " - context:'notable'help:context:'reset' Perform the help operation, printing this message." + print " - context:'notable'main:context:'reset' Build and run the tests." diff --git a/level_0/f_process/tests/unit/c/mock-process.c b/level_0/f_process/tests/unit/c/mock-process.c new file mode 100644 index 0000000..a591696 --- /dev/null +++ b/level_0/f_process/tests/unit/c/mock-process.c @@ -0,0 +1,80 @@ +#include "mock-process.h" +#include "test-process_stubs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int __wrap_name_to_handle_at(int dirfd, const char *path, struct file_handle *handle, int *mount_id, int flags) { + + const uint8_t failure = mock_type(uint8_t); + + if (failure) { + errno = mock_type(int); + return -1; + } + + struct file_handle *mock_handle = mock_ptr_type(struct file_handle *); + + handle = mock_handle; + *mount_id = mock_type(int); + + return 1; +} + +int __wrap_open_by_handle_at(int mount_fd, struct file_handle *handle, int flags) { + + const uint8_t failure = mock_type(uint8_t); + + if (failure) { + errno = mock_type(int); + return -1; + } + + struct file_handle *mock_handle = mock_ptr_type(struct file_handle *); + + handle = mock_handle; + + return 1; +} + +int __wrap_pidfd_getfd(int pidfd, int targetfd, unsigned int flags) { + + const uint8_t failure = mock_type(uint8_t); + + if (failure) { + errno = mock_type(int); + return -1; + } + + return mock_type(int); +} + +int __wrap_pidfd_open(pid_t pid, unsigned int flags) { + + const uint8_t failure = mock_type(uint8_t); + + if (failure) { + errno = mock_type(int); + return -1; + } + + return mock_type(int); +} + +int __wrap_pidfd_send_signal(int pidfd, int sig, siginfo_t * info, unsigned int flags) { + + const uint8_t failure = mock_type(uint8_t); + + if (failure) { + errno = mock_type(int); + + return -1; + } + + return 1; +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/level_0/f_process/tests/unit/c/mock-process.h b/level_0/f_process/tests/unit/c/mock-process.h new file mode 100644 index 0000000..9d4228b --- /dev/null +++ b/level_0/f_process/tests/unit/c/mock-process.h @@ -0,0 +1,46 @@ +/** + * FLL - Level 0 + * + * Project: Process + * API Version: 0.8 + * Licenses: lgpl-2.1-or-later + * + * Test the process project. + */ +#ifndef _MOCK__process_h +#define _MOCK__process_h + +// For fcntl.h. +#ifndef _GNU_SOURCE + #define _GNU_SOURCE +#endif // _GNU_SOURCE + +// Libc includes. +#include +#include +#include +#include + +// cmocka includes. +#include + +// FLL-0 includes. +#include + +#ifdef __cplusplus +extern "C" { +#endif + +const static int mock_errno_generic = 32767; + +extern int __wrap_name_to_handle_at(int dirfd, const char *path, struct file_handle *handle, int *mount_id, int flags); +extern int __wrap_open_by_handle_at(int mount_fd, struct file_handle *handle, int flags); +extern int __wrap_pidfd_getfd(int pidfd, int targetfd, unsigned int flags); +extern int __wrap_pidfd_open(pid_t pid, unsigned int flags); +extern int __wrap_pidfd_send_signal(int pidfd, int sig, siginfo_t * info, unsigned int flags); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _MOCK__process_h diff --git a/level_0/f_process/tests/unit/c/test-process-descriptor_clone.c b/level_0/f_process/tests/unit/c/test-process-descriptor_clone.c new file mode 100644 index 0000000..a382b5a --- /dev/null +++ b/level_0/f_process/tests/unit/c/test-process-descriptor_clone.c @@ -0,0 +1,79 @@ +#include "test-process.h" +#include "test-process-descriptor_clone.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void test__f_process_descriptor_clone__fails(void **state) { + + const pid_t pid = 1; + const int clone = 2; + + int errnos[] = { + EBADF, + EINVAL, + EMFILE, + ENFILE, + EPERM, + ESRCH, + mock_errno_generic, + }; + + f_status_t statuss[] = { + F_file_descriptor_not, + F_parameter, + F_file_descriptor_max, + F_file_open_max, + F_prohibited, + F_search, + F_failure, + }; + + for (int i = 0; i < 7; ++i) { + + int id = -1; + + will_return(__wrap_pidfd_getfd, true); + will_return(__wrap_pidfd_getfd, errnos[i]); + + const f_status_t status = f_process_descriptor_clone(pid, clone, 0, &id); + + assert_int_equal(status, F_status_set_error(statuss[i])); + } // for +} + +void test__f_process_descriptor_clone__parameter_checking(void **state) { + + const pid_t pid = 1; + const int clone = 2; + + { + const f_status_t status = f_process_descriptor_clone(pid, clone, 0, 0); + + assert_int_equal(status, F_status_set_error(F_parameter)); + } +} + +void test__f_process_descriptor_clone__works(void **state) { + + const pid_t pid = 1; + const int clone = 2; + const int expect = 3; + + { + int id = -1; + + will_return(__wrap_pidfd_getfd, false); + will_return(__wrap_pidfd_getfd, expect); + + const f_status_t status = f_process_descriptor_clone(pid, clone, 0, &id); + + assert_int_equal(status, F_okay); + assert_int_equal(id, expect); + } +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/level_0/f_process/tests/unit/c/test-process-descriptor_clone.h b/level_0/f_process/tests/unit/c/test-process-descriptor_clone.h new file mode 100644 index 0000000..2f67357 --- /dev/null +++ b/level_0/f_process/tests/unit/c/test-process-descriptor_clone.h @@ -0,0 +1,34 @@ +/** + * FLL - Level 0 + * + * Project: Process + * API Version: 0.8 + * Licenses: lgpl-2.1-or-later + * + * Test the process project. + */ +#ifndef _TEST__F_process__descriptor_clone_h +#define _TEST__F_process__descriptor_clone_h + +/** + * Test that function fails. + * + * @see f_process_descriptor_clone() + */ +extern void test__f_process_descriptor_clone__fails(void **state); + +/** + * Test that parameter checking works as expected. + * + * @see f_process_descriptor_clone() + */ +extern void test__f_process_descriptor_clone__parameter_checking(void **state); + +/** + * Test that function works. + * + * @see f_process_descriptor_clone() + */ +extern void test__f_process_descriptor_clone__works(void **state); + +#endif // _TEST__F_process_descriptor_clone_h diff --git a/level_0/f_process/tests/unit/c/test-process-descriptor_open.c b/level_0/f_process/tests/unit/c/test-process-descriptor_open.c new file mode 100644 index 0000000..b10ad74 --- /dev/null +++ b/level_0/f_process/tests/unit/c/test-process-descriptor_open.c @@ -0,0 +1,76 @@ +#include "test-process.h" +#include "test-process-descriptor_open.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void test__f_process_descriptor_open__fails(void **state) { + + const pid_t pid = 1; + + int errnos[] = { + EINVAL, + EMFILE, + ENFILE, + ENODEV, + ENOMEM, + ESRCH, + mock_errno_generic, + }; + + f_status_t statuss[] = { + F_parameter, + F_file_descriptor_max, + F_file_open_max, + F_device_not, + F_memory_not, + F_search, + F_failure, + }; + + for (int i = 0; i < 7; ++i) { + + int id = -1; + + will_return(__wrap_pidfd_open, true); + will_return(__wrap_pidfd_open, errnos[i]); + + const f_status_t status = f_process_descriptor_open(pid, 0, &id); + + assert_int_equal(status, F_status_set_error(statuss[i])); + } // for +} + +void test__f_process_descriptor_open__parameter_checking(void **state) { + + const pid_t pid = 1; + + { + const f_status_t status = f_process_descriptor_open(pid, 0, 0); + + assert_int_equal(status, F_status_set_error(F_parameter)); + } +} + +void test__f_process_descriptor_open__works(void **state) { + + const pid_t pid = 1; + const int expect = 3; + + { + int id = -1; + + will_return(__wrap_pidfd_open, false); + will_return(__wrap_pidfd_open, expect); + + const f_status_t status = f_process_descriptor_open(pid, 0, &id); + + assert_int_equal(status, F_okay); + assert_int_equal(id, expect); + } +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/level_0/f_process/tests/unit/c/test-process-descriptor_open.h b/level_0/f_process/tests/unit/c/test-process-descriptor_open.h new file mode 100644 index 0000000..8fcf669 --- /dev/null +++ b/level_0/f_process/tests/unit/c/test-process-descriptor_open.h @@ -0,0 +1,34 @@ +/** + * FLL - Level 0 + * + * Project: Process + * API Version: 0.8 + * Licenses: lgpl-2.1-or-later + * + * Test the process project. + */ +#ifndef _TEST__F_process__descriptor_open_h +#define _TEST__F_process__descriptor_open_h + +/** + * Test that function fails. + * + * @see f_process_descriptor_open() + */ +extern void test__f_process_descriptor_open__fails(void **state); + +/** + * Test that parameter checking works as expected. + * + * @see f_process_descriptor_open() + */ +extern void test__f_process_descriptor_open__parameter_checking(void **state); + +/** + * Test that function works. + * + * @see f_process_descriptor_open() + */ +extern void test__f_process_descriptor_open__works(void **state); + +#endif // _TEST__F_process__descriptor_open_h diff --git a/level_0/f_process/tests/unit/c/test-process-descriptor_signal.c b/level_0/f_process/tests/unit/c/test-process-descriptor_signal.c new file mode 100644 index 0000000..86afedb --- /dev/null +++ b/level_0/f_process/tests/unit/c/test-process-descriptor_signal.c @@ -0,0 +1,74 @@ +#include "test-process.h" +#include "test-process-descriptor_signal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void test__f_process_descriptor_signal__fails(void **state) { + + const int id = 1; + const int signal = 2; + siginfo_t info; + + int errnos[] = { + EBADF, + EINVAL, + EPERM, + ESRCH, + mock_errno_generic, + }; + + f_status_t statuss[] = { + F_file_descriptor_not, + F_parameter, + F_prohibited, + F_search, + F_failure, + }; + + for (int i = 0; i < 5; ++i) { + + memset(&info, 0, sizeof(siginfo_t)); + + will_return(__wrap_pidfd_send_signal, true); + will_return(__wrap_pidfd_send_signal, errnos[i]); + + const f_status_t status = f_process_descriptor_signal(id, signal, 0, &info); + + assert_int_equal(status, F_status_set_error(statuss[i])); + } // for + + for (int i = 0; i < 5; ++i) { + + memset(&info, 0, sizeof(siginfo_t)); + + will_return(__wrap_pidfd_send_signal, true); + will_return(__wrap_pidfd_send_signal, errnos[i]); + + const f_status_t status = f_process_descriptor_signal(id, signal, 0, 0); + + assert_int_equal(status, F_status_set_error(statuss[i])); + } // for +} + +void test__f_process_descriptor_signal__works(void **state) { + + const int id = 1; + const int signal = 2; + siginfo_t info; + + { + memset(&info, 0, sizeof(siginfo_t)); + + will_return(__wrap_pidfd_send_signal, false); + + const f_status_t status = f_process_descriptor_signal(id, signal, 0, &info); + + assert_int_equal(status, F_okay); + } +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/level_0/f_process/tests/unit/c/test-process-descriptor_signal.h b/level_0/f_process/tests/unit/c/test-process-descriptor_signal.h new file mode 100644 index 0000000..8317ae5 --- /dev/null +++ b/level_0/f_process/tests/unit/c/test-process-descriptor_signal.h @@ -0,0 +1,27 @@ +/** + * FLL - Level 0 + * + * Project: Process + * API Version: 0.8 + * Licenses: lgpl-2.1-or-later + * + * Test the process project. + */ +#ifndef _TEST__F_process__descriptor_signal_h +#define _TEST__F_process__descriptor_signal_h + +/** + * Test that function fails. + * + * @see f_process_descriptor_signal() + */ +extern void test__f_process_descriptor_signal__fails(void **state); + +/** + * Test that function works. + * + * @see f_process_descriptor_signal() + */ +extern void test__f_process_descriptor_signal__works(void **state); + +#endif // _TEST__F_process__descriptor_signal_h diff --git a/level_0/f_process/tests/unit/c/test-process-handle_from_path_at.c b/level_0/f_process/tests/unit/c/test-process-handle_from_path_at.c new file mode 100644 index 0000000..f536295 --- /dev/null +++ b/level_0/f_process/tests/unit/c/test-process-handle_from_path_at.c @@ -0,0 +1,122 @@ +#include "test-process.h" +#include "test-process-handle_from_path_at.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void test__f_process_handle_from_path_at__fails(void **state) { + + const int at_id = 0; + const f_string_static_t path = macro_f_string_static_t_initialize_2("path", 4); + + int errnos[] = { + EACCES, + EBADF, + EFAULT, + EFBIG, + EINTR, + EINVAL, + ELOOP, + ENAMETOOLONG, + ENFILE, + ENOENT, + ENOMEM, + ENOTDIR, + ENOSPC, + EOPNOTSUPP, + EOVERFLOW, + EPERM, + EROFS, + mock_errno_generic, + }; + + f_status_t statuss[] = { + F_access_denied, + F_directory_descriptor_not, + F_buffer, + F_number_overflow, + F_interrupt, + F_parameter, + F_loop, + F_name, + F_file_open_max, + F_directory_found_not, + F_memory_not, + F_directory_not, + F_space_not, + F_support_not, + F_number_overflow, + F_prohibited, + F_read_only, + F_failure, + }; + + for (int i = 0; i < 18; ++i) { + + f_handle_t handle = f_handle_t_initialize; + int id = -1; + + will_return(__wrap_name_to_handle_at, true); + will_return(__wrap_name_to_handle_at, errnos[i]); + + const f_status_t status = f_process_handle_from_path_at(at_id, path, 0, &handle, &id); + + assert_int_equal(status, F_status_set_error(statuss[i])); + } // for +} + +void test__f_process_handle_from_path_at__parameter_checking(void **state) { + + const int at_id = 0; + const f_string_static_t path = macro_f_string_static_t_initialize_2("path", 4); + + { + const f_status_t status = f_process_handle_from_path_at(at_id, path, 0, 0, 0); + + assert_int_equal(status, F_status_set_error(F_parameter)); + } + + { + f_handle_t handle = f_handle_t_initialize; + + const f_status_t status = f_process_handle_from_path_at(at_id, path, 0, &handle, 0); + + assert_int_equal(status, F_status_set_error(F_parameter)); + } + + { + int id = -1; + + const f_status_t status = f_process_handle_from_path_at(at_id, path, 0, 0, &id); + + assert_int_equal(status, F_status_set_error(F_parameter)); + } +} + +void test__f_process_handle_from_path_at__works(void **state) { + + const int at_id = 0; + const int expect = 3; + const f_string_static_t path = macro_f_string_static_t_initialize_2("path", 4); + + f_handle_t expect_handle = f_handle_t_initialize; + + { + f_handle_t handle = f_handle_t_initialize; + int id = -1; + + will_return(__wrap_name_to_handle_at, false); + will_return(__wrap_name_to_handle_at, &expect_handle); + will_return(__wrap_name_to_handle_at, expect); + + const f_status_t status = f_process_handle_from_path_at(at_id, path, 0, &handle, &id); + + assert_int_equal(status, F_okay); + assert_int_equal(id, expect); + } +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/level_0/f_process/tests/unit/c/test-process-handle_from_path_at.h b/level_0/f_process/tests/unit/c/test-process-handle_from_path_at.h new file mode 100644 index 0000000..09af9f5 --- /dev/null +++ b/level_0/f_process/tests/unit/c/test-process-handle_from_path_at.h @@ -0,0 +1,34 @@ +/** + * FLL - Level 0 + * + * Project: Process + * API Version: 0.8 + * Licenses: lgpl-2.1-or-later + * + * Test the process project. + */ +#ifndef _TEST__F_process__handle_from_path_at_h +#define _TEST__F_process__handle_from_path_at_h + +/** + * Test that function fails. + * + * @see f_process_handle_from_path_at() + */ +extern void test__f_process_handle_from_path_at__fails(void **state); + +/** + * Test that parameter checking works as expected. + * + * @see f_process_handle_from_path_at() + */ +extern void test__f_process_handle_from_path_at__parameter_checking(void **state); + +/** + * Test that function works. + * + * @see f_process_handle_from_path_at() + */ +extern void test__f_process_handle_from_path_at__works(void **state); + +#endif // _TEST__F_process__handle_from_path_at_h diff --git a/level_0/f_process/tests/unit/c/test-process-handle_open_at.c b/level_0/f_process/tests/unit/c/test-process-handle_open_at.c new file mode 100644 index 0000000..f54e8d3 --- /dev/null +++ b/level_0/f_process/tests/unit/c/test-process-handle_open_at.c @@ -0,0 +1,94 @@ +#include "test-process.h" +#include "test-process-handle_open_at.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void test__f_process_handle_open_at__fails(void **state) { + + const int id = 0; + + int errnos[] = { + EACCES, + EBADF, + EFAULT, + EFBIG, + EINTR, + EINVAL, + ELOOP, + ENAMETOOLONG, + ENFILE, + ENOENT, + ENOMEM, + ENOTDIR, + ENOSPC, + EPERM, + EROFS, + ESTALE, + mock_errno_generic, + }; + + f_status_t statuss[] = { + F_access_denied, + F_directory_descriptor_not, + F_buffer, + F_number_overflow, + F_interrupt, + F_parameter, + F_loop, + F_name, + F_file_open_max, + F_directory_found_not, + F_memory_not, + F_directory_not, + F_space_not, + F_prohibited, + F_read_only, + F_stale, + F_failure, + }; + + for (int i = 0; i < 17; ++i) { + + f_handle_t handle = f_handle_t_initialize; + + will_return(__wrap_open_by_handle_at, true); + will_return(__wrap_open_by_handle_at, errnos[i]); + + const f_status_t status = f_process_handle_open_at(id, 0, &handle); + + assert_int_equal(status, F_status_set_error(statuss[i])); + } // for +} + +void test__f_process_handle_open_at__parameter_checking(void **state) { + + const int id = 0; + + { + const f_status_t status = f_process_handle_open_at(id, 0, 0); + + assert_int_equal(status, F_status_set_error(F_parameter)); + } +} + +void test__f_process_handle_open_at__works(void **state) { + + const int id = 0; + + { + f_handle_t handle = f_handle_t_initialize; + + will_return(__wrap_open_by_handle_at, false); + will_return(__wrap_open_by_handle_at, &handle); + + const f_status_t status = f_process_handle_open_at(id, 0, &handle); + + assert_int_equal(status, F_okay); + } +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/level_0/f_process/tests/unit/c/test-process-handle_open_at.h b/level_0/f_process/tests/unit/c/test-process-handle_open_at.h new file mode 100644 index 0000000..96707a9 --- /dev/null +++ b/level_0/f_process/tests/unit/c/test-process-handle_open_at.h @@ -0,0 +1,34 @@ +/** + * FLL - Level 0 + * + * Project: Process + * API Version: 0.8 + * Licenses: lgpl-2.1-or-later + * + * Test the process project. + */ +#ifndef _TEST__F_process__handle_open_at_h +#define _TEST__F_process__handle_open_at_h + +/** + * Test that function fails. + * + * @see f_process_handle_open_at() + */ +extern void test__f_process_handle_open_at__fails(void **state); + +/** + * Test that parameter checking works as expected. + * + * @see f_process_handle_open_at() + */ +extern void test__f_process_handle_open_at__parameter_checking(void **state); + +/** + * Test that function works. + * + * @see f_process_handle_open_at() + */ +extern void test__f_process_handle_open_at__works(void **state); + +#endif // _TEST__F_process__handle_open_at_h diff --git a/level_0/f_process/tests/unit/c/test-process.c b/level_0/f_process/tests/unit/c/test-process.c new file mode 100644 index 0000000..73cbc4c --- /dev/null +++ b/level_0/f_process/tests/unit/c/test-process.c @@ -0,0 +1,51 @@ +#include "test-process.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int setup(void **state) { + + return 0; +} + +int setdown(void **state) { + + errno = 0; + + return 0; +} + +int main(void) { + + const struct CMUnitTest tests[] = { + cmocka_unit_test(test__f_process_descriptor_clone__fails), + cmocka_unit_test(test__f_process_descriptor_clone__works), + + cmocka_unit_test(test__f_process_descriptor_open__fails), + cmocka_unit_test(test__f_process_descriptor_open__works), + + cmocka_unit_test(test__f_process_descriptor_signal__fails), + cmocka_unit_test(test__f_process_descriptor_signal__works), + + cmocka_unit_test(test__f_process_handle_from_path_at__fails), + cmocka_unit_test(test__f_process_handle_from_path_at__works), + + cmocka_unit_test(test__f_process_handle_open_at__fails), + cmocka_unit_test(test__f_process_handle_open_at__works), + + #ifndef _di_level_0_parameter_checking_ + cmocka_unit_test(test__f_process_descriptor_clone__parameter_checking), + cmocka_unit_test(test__f_process_descriptor_open__parameter_checking), + // f_process_f_process_descriptor_signal() doesn't use parameter checking. + cmocka_unit_test(test__f_process_handle_from_path_at__parameter_checking), + cmocka_unit_test(test__f_process_handle_open_at__parameter_checking), + #endif // _di_level_0_parameter_checking_ + }; + + return cmocka_run_group_tests(tests, setup, setdown); +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/level_0/f_process/tests/unit/c/test-process.h b/level_0/f_process/tests/unit/c/test-process.h new file mode 100644 index 0000000..dff52b2 --- /dev/null +++ b/level_0/f_process/tests/unit/c/test-process.h @@ -0,0 +1,85 @@ +/** + * FLL - Level 0 + * + * Project: Process + * API Version: 0.8 + * Licenses: lgpl-2.1-or-later + * + * Test the process project. + */ +#ifndef _TEST__F_process_h +#define _TEST__F_process_h + +// For fcntl.h. +#ifndef _GNU_SOURCE + #define _GNU_SOURCE +#endif // _GNU_SOURCE + +// Libc includes. +#include +#include +#include +#include +#include + +// cmocka includes. +#include + +// FLL-0 includes. +#include + +// Mock includes. +#include "mock-process.h" + +// Stub includes. +#include "test-process_stubs.h" + +// Test includes. +#include "test-process-descriptor_clone.h" +#include "test-process-descriptor_open.h" +#include "test-process-descriptor_signal.h" +#include "test-process-handle_from_path_at.h" +#include "test-process-handle_open_at.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Perform any setup operations. + * + * @param state + * The test state. + * + * @return + * The status of this function, where 0 means success. + */ +extern int setup(void **state); + +/** + * Peform any setdown operations. + * + * @param state + * The test state. + * + * @return + * The status of this function, where 0 means success. + */ +extern int setdown(void **state); + +/** + * Run all tests. + * + * @return + * The final result of the tests. + * + * @see cmocka_run_group_tests() + * @see cmocka_unit_test() + */ +extern int main(void); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _TEST__F_process_h diff --git a/level_0/f_process/tests/unit/c/test-process_stubs.c b/level_0/f_process/tests/unit/c/test-process_stubs.c new file mode 100644 index 0000000..78f33de --- /dev/null +++ b/level_0/f_process/tests/unit/c/test-process_stubs.c @@ -0,0 +1,33 @@ +#include "test-process.h" +#include "test-process_stubs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* +#ifndef _di_f_process_test_process_stub_pidfd_getfd_ + int pidfd_getfd(int pidfd, int targetfd, unsigned int flags) { + + return -1; + } +#endif // _di_f_process_test_process_stub_pidfd_getfd_ + +#ifndef _di_f_process_test_process_stub_pidfd_open_ + int pidfd_open(pid_t pid, unsigned int flags) { + + return -1; + } +#endif // _di_f_process_test_process_stub_pidfd_open_ + +#ifndef _di_f_process_test_process_stub_pidfd_send_signal_ + int pidfd_send_signal(int pidfd, int sig, siginfo_t * info, unsigned int flags) { + + return -1; + } +#endif // _di_f_process_test_process_stub_pidfd_send_signal_ +*/ + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/level_0/f_process/tests/unit/c/test-process_stubs.h b/level_0/f_process/tests/unit/c/test-process_stubs.h new file mode 100644 index 0000000..7975991 --- /dev/null +++ b/level_0/f_process/tests/unit/c/test-process_stubs.h @@ -0,0 +1,36 @@ +/** + * FLL - Level 0 + * + * Project: Process + * API Version: 0.8 + * Licenses: lgpl-2.1-or-later + * + * Test the process project. + * + * These are stubs for functions that are not commonly available in some libc implementations. + * Disable any stub functions for systems where they are provided. + */ +#ifndef _TEST__F_process_stubs_h +#define _TEST__F_process_stubs_h + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _di_f_process_test_process_stub_pidfd_getfd_ + extern int pidfd_getfd(int pidfd, int targetfd, unsigned int flags); +#endif // _di_f_process_test_process_stub_pidfd_getfd_ + +#ifndef _di_f_process_test_process_stub_pidfd_open_ + extern int pidfd_open(pid_t pid, unsigned int flags); +#endif // _di_f_process_test_process_stub_pidfd_open_ + +#ifndef _di_f_process_test_process_stub_pidfd_send_signal_ + extern int pidfd_send_signal(int pidfd, int sig, siginfo_t * info, unsigned int flags); +#endif // _di_f_process_test_process_stub_pidfd_send_signal_ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _TEST__F_process_stubs_h diff --git a/level_0/f_type/c/type.h b/level_0/f_type/c/type.h index a8ad489..25be31b 100644 --- a/level_0/f_type/c/type.h +++ b/level_0/f_type/c/type.h @@ -18,12 +18,12 @@ #endif // _GNU_SOURCE // Libc includes. +#include #include #include #include #include #include -#include // Must be defined after the sys/* includes. #include #include diff --git a/level_0/f_type/c/type/handle.h b/level_0/f_type/c/type/handle.h new file mode 100644 index 0000000..42febde --- /dev/null +++ b/level_0/f_type/c/type/handle.h @@ -0,0 +1,48 @@ +/** + * FLL - Level 0 + * + * Project: Type + * API Version: 0.8 + * Licenses: lgpl-2.1-or-later + * + * Defines file handle type data. + * + * This is auto-included by type.h and should not need to be explicitly included. + */ +#ifndef _F_type_handle_h +#define _F_type_handle_h + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * A file handle. + */ +#ifndef _di_f_handle_t_ + typedef struct file_handle f_handle_t; + + #define f_handle_t_initialize { \ + .handle_bytes = 0, \ + .handle_type = 0, \ + .f_handle = {}, \ + } + + #define macrro_f_handle_t_initialize_1(handle_bytes_value, handle_type_value, f_handle_value) { \ + .handle_bytes = handle_bytes_value, \ + .handle_type = handle_type_value, \ + .f_handle = f_handle_value, \ + } + + #define macrro_f_handle_t_initialize_2(handle_bytes_value, handle_type_value) { \ + .handle_bytes = handle_bytes_value, \ + .handle_type = handle_type_value, \ + .f_handle = {}, \ + } +#endif // _di_f_handle_t_ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _F_type_handle_h diff --git a/level_0/f_type/data/build/defines b/level_0/f_type/data/build/defines index c665317..4f13080 100644 --- a/level_0/f_type/data/build/defines +++ b/level_0/f_type/data/build/defines @@ -1,2 +1 @@ # fss-0000 - diff --git a/level_0/f_type/data/build/settings b/level_0/f_type/data/build/settings index e956727..31fc71f 100644 --- a/level_0/f_type/data/build/settings +++ b/level_0/f_type/data/build/settings @@ -38,7 +38,7 @@ build_libraries_shared -lc build_libraries_static -l:libc.a -build_sources_headers type.h type/cell.h type/date.h type/file.h type/fll.h type/mode.h type/number.h type/pid.h type/quantity.h type/range.h type/range_double.h type/state.h type/status.h type/time.h type/void.h +build_sources_headers type.h type/cell.h type/date.h type/file.h type/fll.h type/handle.h type/mode.h type/number.h type/pid.h type/quantity.h type/range.h type/range_double.h type/state.h type/status.h type/time.h type/void.h build_static no