]> Kevux Git Server - fll/commitdiff
Feature: Add f_process project.
authorKevin Day <Kevin@kevux.org>
Sun, 7 Dec 2025 22:32:48 +0000 (16:32 -0600)
committerKevin Day <Kevin@kevux.org>
Mon, 8 Dec 2025 00:07:47 +0000 (18:07 -0600)
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.

37 files changed:
build/disable/level_0/f_process.h [new file with mode: 0644]
build/disable/level_0/f_type.h
build/level_0/settings
build/monolithic/settings
build/scripts/bootstrap-example.sh
build/scripts/test.sh
level_0/f_process/c/process.c [new file with mode: 0644]
level_0/f_process/c/process.h [new file with mode: 0644]
level_0/f_process/c/process/common.h [new file with mode: 0644]
level_0/f_process/data/build/defines [new file with mode: 0644]
level_0/f_process/data/build/dependencies [new file with mode: 0644]
level_0/f_process/data/build/dependencies-tests [new file with mode: 0644]
level_0/f_process/data/build/fakefile [new file with mode: 0644]
level_0/f_process/data/build/settings [new file with mode: 0644]
level_0/f_process/data/build/settings-mocks [new file with mode: 0644]
level_0/f_process/data/build/settings-tests [new file with mode: 0644]
level_0/f_process/data/build/testfile [new file with mode: 0644]
level_0/f_process/tests/unit/c/mock-process.c [new file with mode: 0644]
level_0/f_process/tests/unit/c/mock-process.h [new file with mode: 0644]
level_0/f_process/tests/unit/c/test-process-descriptor_clone.c [new file with mode: 0644]
level_0/f_process/tests/unit/c/test-process-descriptor_clone.h [new file with mode: 0644]
level_0/f_process/tests/unit/c/test-process-descriptor_open.c [new file with mode: 0644]
level_0/f_process/tests/unit/c/test-process-descriptor_open.h [new file with mode: 0644]
level_0/f_process/tests/unit/c/test-process-descriptor_signal.c [new file with mode: 0644]
level_0/f_process/tests/unit/c/test-process-descriptor_signal.h [new file with mode: 0644]
level_0/f_process/tests/unit/c/test-process-handle_from_path_at.c [new file with mode: 0644]
level_0/f_process/tests/unit/c/test-process-handle_from_path_at.h [new file with mode: 0644]
level_0/f_process/tests/unit/c/test-process-handle_open_at.c [new file with mode: 0644]
level_0/f_process/tests/unit/c/test-process-handle_open_at.h [new file with mode: 0644]
level_0/f_process/tests/unit/c/test-process.c [new file with mode: 0644]
level_0/f_process/tests/unit/c/test-process.h [new file with mode: 0644]
level_0/f_process/tests/unit/c/test-process_stubs.c [new file with mode: 0644]
level_0/f_process/tests/unit/c/test-process_stubs.h [new file with mode: 0644]
level_0/f_type/c/type.h
level_0/f_type/c/type/handle.h [new file with mode: 0644]
level_0/f_type/data/build/defines
level_0/f_type/data/build/settings

diff --git a/build/disable/level_0/f_process.h b/build/disable/level_0/f_process.h
new file mode 100644 (file)
index 0000000..8da8e39
--- /dev/null
@@ -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_
index f67c90297dccc216bfa4afd6b9240a43197fed94..d71f03ab0cb3f5d1a4356912e14bdfeb339ede7c 100644 (file)
@@ -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_
index e39783a9e382fe9be4bce7e3fda7d33aea676253..f348b16624288c0363887f568ddd862e47a0103e 100644 (file)
@@ -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_
index e2e436e2c3e9b290053a87c506944869988d89e4..ebdf3d008ffee4d44df7de9a290a06b524c296dd 100644 (file)
@@ -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_
index bb24ee5db0da75f0553d189bce894a4e6b24c837..6a637126f89265b09728b22da3fcf282a5cf27b4 100644 (file)
@@ -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}." &&
 
index c51b903d0aaa7578682bc744578cb0e1403bdb56..c22109848c0edd2ac5b10a15f3877b82a0a1d5d6 100644 (file)
@@ -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 (file)
index 0000000..9ba3c81
--- /dev/null
@@ -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 (file)
index 0000000..19abc7c
--- /dev/null
@@ -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 <fcntl.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+// FLL-0 includes.
+#include <fll/level_0/type.h>
+#include <fll/level_0/status.h>
+#include <fll/level_0/memory.h>
+#include <fll/level_0/type_array.h>
+#include <fll/level_0/string.h>
+
+// FLL-0 process includes.
+#include <fll/level_0/process/common.h>
+
+#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 (file)
index 0000000..f5cf11b
--- /dev/null
@@ -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 (file)
index 0000000..bae0441
--- /dev/null
@@ -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 (file)
index 0000000..03d7727
--- /dev/null
@@ -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 (file)
index 0000000..dea3179
--- /dev/null
@@ -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 (file)
index 0000000..82cffd9
--- /dev/null
@@ -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 (file)
index 0000000..0141913
--- /dev/null
@@ -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 (file)
index 0000000..337f32e
--- /dev/null
@@ -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 (file)
index 0000000..b9d9ac3
--- /dev/null
@@ -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 (file)
index 0000000..e1637fe
--- /dev/null
@@ -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 (file)
index 0000000..a591696
--- /dev/null
@@ -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 (file)
index 0000000..9d4228b
--- /dev/null
@@ -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 <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <stdint.h>
+
+// cmocka includes.
+#include <cmocka.h>
+
+// FLL-0 includes.
+#include <fll/level_0/process.h>
+
+#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 (file)
index 0000000..a382b5a
--- /dev/null
@@ -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 (file)
index 0000000..2f67357
--- /dev/null
@@ -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 (file)
index 0000000..b10ad74
--- /dev/null
@@ -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 (file)
index 0000000..8fcf669
--- /dev/null
@@ -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 (file)
index 0000000..86afedb
--- /dev/null
@@ -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 (file)
index 0000000..8317ae5
--- /dev/null
@@ -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 (file)
index 0000000..f536295
--- /dev/null
@@ -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 (file)
index 0000000..09af9f5
--- /dev/null
@@ -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 (file)
index 0000000..f54e8d3
--- /dev/null
@@ -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 (file)
index 0000000..96707a9
--- /dev/null
@@ -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 (file)
index 0000000..73cbc4c
--- /dev/null
@@ -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 (file)
index 0000000..dff52b2
--- /dev/null
@@ -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 <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <stdint.h>
+#include <stdio.h>
+
+// cmocka includes.
+#include <cmocka.h>
+
+// FLL-0 includes.
+#include <fll/level_0/process.h>
+
+// 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 (file)
index 0000000..78f33de
--- /dev/null
@@ -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 (file)
index 0000000..7975991
--- /dev/null
@@ -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
index a8ad489d089d63e4993ee2e409432d0f25bacc27..25be31b1df7f2a452e5cf135a64561e1d80f76f7 100644 (file)
 #endif // _GNU_SOURCE
 
 // Libc includes.
+#include <fcntl.h>
 #include <stdbool.h>
 #include <stdint.h>
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <sys/types.h>
-#include <fcntl.h> // Must be defined after the sys/* includes.
 #include <time.h>
 #include <unistd.h>
 
diff --git a/level_0/f_type/c/type/handle.h b/level_0/f_type/c/type/handle.h
new file mode 100644 (file)
index 0000000..42febde
--- /dev/null
@@ -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
index c6653172ef1e71eb1aff738b1cbad4300feeacd6..4f130804d83cb5bf412c3e349a4ac790b2b825ca 100644 (file)
@@ -1,2 +1 @@
 # fss-0000
-
index e956727a1161528a659b8fab5bfdf6e46d6bdd85..31fc71f46648c0afd3d2b598ddc4e8fad8327f4a 100644 (file)
@@ -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