]> Kevux Git Server - controller/commitdiff
Progress: Begin implementing controlfile and initfile support.
authorKevin Day <Kevin@kevux.org>
Mon, 17 Nov 2025 00:16:18 +0000 (18:16 -0600)
committerKevin Day <Kevin@kevux.org>
Mon, 17 Nov 2025 00:30:23 +0000 (18:30 -0600)
The specifications are now implemented.
Provide examples of the  `controlfile` and the `initfile`.

Comments some TODO's that I am thinking about and will set aside for future consideration.
These TODO's will be removed once I wrap up the `controlfile` support regardless of whether or not I implement the TODOs.

The `controller` and `init` programs have the basic file support setup, but no function to actually process these are implemented yet.

My goal is to pre-process these files and then pass them along to the normal load functions.
Ideally, the file should need only be loaded in memory once.

I broke out some of the `controller_process()` functionality into another functions `controller_process_prepare()` and `controller_process_run()` for organizational purposes.

There are potentially some stale TODO/`@todo` comments that will be addressed and removed as I complete this functionality.

20 files changed:
data/build/defines
data/build/settings
data/build/settings.controller
data/build/settings.init
data/data/controller/example/controlfile/controlfile [new file with mode: 0644]
data/data/controller/example/controlfile/initfile [new file with mode: 0644]
documents/controlfile.txt [new file with mode: 0644]
documents/initfile.txt [new file with mode: 0644]
sources/c/program/controller/controller/string.c
sources/c/program/controller/controller/string.h
sources/c/program/controller/init/string.c
sources/c/program/controller/init/string.h
sources/c/program/controller/main/common/define.h
sources/c/program/controller/main/common/string.c
sources/c/program/controller/main/common/string.h
sources/c/program/controller/main/entry.c
sources/c/program/controller/main/process.c
sources/c/program/controller/main/process.h
specifications/controlfile.txt [new file with mode: 0644]
specifications/initfile.txt [new file with mode: 0644]

index f282d0fd2c18a98b66378bf3304f41246e6ce24c..7fccb9f21afecb3a9b8ab2b71bbb2511a85b6e41 100644 (file)
@@ -5,6 +5,9 @@ _di_thread_support_ Disables thread support.
 
 _libcap_legacy_only_ Disable functionality provided by later versions of libcap (2.43 and later).
 
+_support_controller_controlfile_ Enable support for the controlfile by the controller program and library.
+_support_controller_initfile_    Enable support for the initfile by the init program (requires _support_controller_controlfile_).
+
 _override_controller_default_engine_     Provide a custom scripting engine name string to execute (such as php).
 _override_controller_path_pid_           Use this as the default custom directory path representing the location of the controller program pid.
 _override_controller_path_pid_prefix_    Use this as the default custom prefix prepended to the file name of the file representing the controller program pid.
index dd8273d042c2a90cc4ce0011ec3cb8e90647059c..1ceef883a22b05c176b1217c3ff745f17f2f3845 100644 (file)
@@ -127,6 +127,7 @@ environment LANG LC_ALL LC_COLLATE LC_CTYPE LC_FASTMSG LC_MESSAGES LC_MONETARY L
 
 #defines -D_di_libcap_
 defines -D_libcap_legacy_only_
+defines -D_support_controller_controlfile_
 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_
 F_status_debug_source_d
index f6f615653aff4b8e8d7ff2d5b8ab203c1a81f8bd..674be424dd8a71f45f3c6ee6d461956d84f1703c 100644 (file)
@@ -89,6 +89,7 @@ environment LANG LC_ALL LC_COLLATE LC_CTYPE LC_FASTMSG LC_MESSAGES LC_MONETARY L
 
 #defines -D_di_libcap_
 defines -D_libcap_legacy_only_
+defines -D_support_controller_controlfile_
 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_
index 2f84a39d88580f28a0e190b67c0e2ecf9e6e34f5..78f273bb86950d29a6aae218852743859e994a0a 100644 (file)
@@ -89,6 +89,7 @@ environment LANG LC_ALL LC_COLLATE LC_CTYPE LC_FASTMSG LC_MESSAGES LC_MONETARY L
 
 #defines -D_di_libcap_
 defines -D_libcap_legacy_only_
+defines -D_support_controller_controlfile_ -D_support_controller_initfile_
 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_
diff --git a/data/data/controller/example/controlfile/controlfile b/data/data/controller/example/controlfile/controlfile
new file mode 100644 (file)
index 0000000..bdec963
--- /dev/null
@@ -0,0 +1,64 @@
+# fss-0005 fss-000d iki-0000
+#
+# Example controlfile based on the example files:
+#   - miscellaneous/entries/serial.entry.
+#   - miscellaneous/exits/serial.exit.
+#   - miscellaneous/rules/serial/s_1.rule
+#   - miscellaneous/rules/serial/s_2.rule
+#   - miscellaneous/rules/serial/s_3.rule
+#   - miscellaneous/rules/serial/s_4.rule
+#   - miscellaneous/rules/serial/s_5.rule
+#   - miscellaneous/rules/serial/s_6.rule
+#
+
+entry_main:
+  consider serial s_1
+  consider serial s_2
+  consider serial s_3
+  consider serial s_4
+  consider serial s_5
+
+  start serial s_6
+
+  ready
+
+exit_main:
+  consider serial s_1
+  consider serial s_2
+  consider serial s_3
+  consider serial s_4
+  consider serial s_5
+  consider serial s_6
+
+  stop serial s_1
+
+  ready
+
+rule_serial_s_1_settings:
+  name "Serial 1"
+  on stop need serial s_2
+
+rule_serial_s_1_script:
+  start {
+    main() {
+      local IFS=$' \t\n' # Prevent IFS exploits by overriding with a local scope.
+
+      echo "Serial 1: sleeping $(date -u)"
+      sleep 1
+      echo "Serial 1: slept    $(date -u)"
+    \}
+
+    main ${*}
+  }
+
+  stop {
+    main() {
+      local IFS=$' \t\n' # Prevent IFS exploits by overriding with a local scope.
+
+      echo "Serial 1: stopping, sleeping $(date -u)"
+      sleep 1
+      echo "Serial 1: stopping, slept    $(date -u)"
+    \}
+
+    main ${*}
+  }
diff --git a/data/data/controller/example/controlfile/initfile b/data/data/controller/example/controlfile/initfile
new file mode 100644 (file)
index 0000000..465d429
--- /dev/null
@@ -0,0 +1,259 @@
+# fss-0005 fss-000d iki-0000
+#
+# Example initfile based on the init/entries/default.entry and many of its related files.
+#
+
+entry_settings:
+  pid ready
+  show init
+
+  control init.socket
+  control_user 0
+  control_group 0
+  control_mode ug+rwx,o-rwx
+
+entry_main:
+  timeout start 7
+  timeout stop 7
+  timeout kill 3
+
+  failsafe maintenance
+
+  item boot
+  item console
+
+entry_boot:
+  start boot root require
+  start boot proc asynchronous require
+  start boot devices asynchronous require
+  start boot file_system asynchronous
+  start boot modules wait
+
+  ready
+
+entry_console:
+  start service mouse asynchronous
+
+  start terminal two asynchronous
+  start terminal three asynchronous
+  start terminal four asynchronous
+  start terminal one require wait
+
+entry_maintenance:
+  # TODO: Support IKI variables for certain env data, such as an architecture path (thus '/bin[environment:"architecture_path"]/bash'). with some compilation built ins, or maybe have settings support calling programs to populate variables, like '/bin[variable:"architecture_path"]/bash'.
+  # TODO: There could even be settings, entries, and exits to support for calling and processing custom, named, functions built into the source code.
+  execute /bin/bash --login
+
+rule_boot_devices_settings:
+  name "Setup /dev file system"
+
+  on start need boot root
+
+rule_boot_devices_script:
+  start {
+    main() {
+      local IFS=$' \t\n' # Prevent IFS exploits by overriding with a local scope.
+
+      if [[ ! -d /dev/pts ]] ; then
+        mkdir /dev/pts
+      fi
+
+      if [[ ! -d /dev/shm ]] ; then
+        mkdir /dev/shm
+      fi
+
+      return 0
+    \}
+
+    main ${*}
+  }
+
+rule_boot_devices_command:
+  start mount /dev/pts
+  stop umount -l /dev/pts
+
+rule_boot_devices_command:
+  start mount /dev/shm
+  stop umount -l /dev/shm
+
+rule_boot_file_system_settings:
+  name "Setup Filesystem"
+
+  on start need boot root
+  on start need boot proc
+  on start need boot devices
+
+  on stop need boot proc
+  on stop need boot devices
+
+rule_boot_file_system_command:
+  start mount -n -a -O no_netdev
+  stop umount -n -arf -O no_netdev
+
+rule_boot_file_system_command:
+  start swapon -a
+  stop swapoff -a
+
+rule_boot_file_system_script:
+  start {
+    main() {
+      local IFS=$' \t\n' # Prevent IFS exploits by overriding with a local scope.
+
+      if [[ ! -d /var/run/init ]] ; then
+        mkdir /var/run/init
+      fi
+    \}
+
+    main ${*}
+  }
+
+rule_boot_modules_settings:
+  name "Setup Kernel Modules"
+
+  on start need boot root
+  on start need boot proc
+  on start want boot filesystem
+
+rule_boot_modules_script:
+  start {
+    main() {
+      local IFS=$' \t\n' # Prevent IFS exploits by overriding with a local scope.
+
+      if [[ ! -f /proc/modules ]] ; then
+        exit 0
+      fi
+
+      if [[ -d /modules ]] ; then
+        if [[ ! -e /modules/$(uname -r)/modules.dep ]] ; then
+          depmod
+        else
+          depmod -A
+        fi
+      fi
+
+      return 0
+    \}
+
+    main ${*}
+  }
+
+rule_boot_proc_settings:
+  name "Setup /proc Filesystem"
+
+  on start need boot root
+
+rule_boot_proc_command:
+  start mount /proc
+
+rule_boot_proc_script:
+  start {
+    main() {
+      local IFS=$' \t\n' # Prevent IFS exploits by overriding with a local scope.
+
+      if [[ -d /proc/bus/usb ]] ; then
+        mount /proc/bus/usb
+      fi
+
+      return 0
+    \}
+
+    main ${*}
+  }
+
+  stop {
+    main() {
+      local IFS=$' \t\n' # Prevent IFS exploits by overriding with a local scope.
+
+      if [[ -d /proc/bus/usb ]] ; then
+        umount -l /proc/bus/usb
+      fi
+
+      return 0
+    \}
+
+    main ${*}
+  }
+
+rule_boot_root_settings:
+  name "Setup Root Filesystem"
+
+rule_boot_root_command:
+  start mount -o remount,rw /
+
+rule_boot_root_script:
+  start {
+    main() {
+      local IFS=$' \t\n' # Prevent IFS exploits by overriding with a local scope.
+      local i=
+
+      for i in /dev /dev/pts /dev/shm /firmware /mnt /modules /proc /sys /tmp /var /var/log /var/run /var/tmp ; do
+        if [[ ! -d ${i} ]] ; then
+          mkdir ${i}
+        fi
+      done
+
+      return 0
+    \}
+
+    main ${*}
+  }
+
+rule_service_mouse_settings:
+  name "Console Mouse"
+  capability all=
+  nice 15
+
+rule_service_mouse_script:
+  start {
+    main() {
+      local IFS=$' \t\n' # Prevent IFS exploits by overriding with a local scope.
+
+      # This works if gpm service is run as root, but if not then this should be in a separate rule file with appropriate access to write to /var/run (don't forget to chown!).
+      if [[ ! -d /var/run/mouse/ && -d /var/run ]] ; then
+        mkdir /var/run/mouse/
+      fi
+    \}
+
+    main ${*}
+  }
+
+rule_service_mouse_service:
+  pid_file /var/run/mouse/mouse.pid
+
+  start gpm -m variable:"device" -t variable:"protocol" variable:"options"
+
+rule_terminal_one_settings:
+  name "System Terminal 1"
+
+rule_terminal_one_command:
+  start agetty -8 -i -J - linux
+
+  rerun start success delay 1000 reset
+  rerun start failure delay 5000
+
+rule_terminal_two_settings:
+  name "System Terminal 2"
+
+rule_terminal_two_command:
+  start agetty -8 tty2 linux
+
+  rerun start success delay 1000 reset
+  rerun start failure delay 5000 max 100
+
+rule_terminal_three_settings:
+  name "System Terminal 3"
+
+rule_terminal_three_command:
+  start agetty -8 tty3 linux
+
+  rerun start success delay 1000 reset
+  rerun start failure delay 5000 max 100
+
+rule_terminal_four_settings:
+  name "System Terminal 4"
+
+rule_terminal_four_command:
+  start agetty -8 tty4 linux
+
+  rerun start success delay 1000 reset
+  rerun start failure delay 5000 max 100
diff --git a/documents/controlfile.txt b/documents/controlfile.txt
new file mode 100644 (file)
index 0000000..a62a6dd
--- /dev/null
@@ -0,0 +1,24 @@
+# fss-0002 iki-0000
+#
+# license: open-standard-license-1.0-or-later
+# version 2025/11/17
+#
+# This file (assumed to be named controlfile.txt) can be more easily read using the following iki_read commands:
+#   iki_read controlfile.txt +Q -w -WW code '"' '"' file '"' '"'
+#
+# To read the "Control File Documentation" section of this file, use this command sequence:
+#   fss_basic_list_read controlfile.txt +Q -cn "Control File Documentation" | iki_read +Q -w -WW code '"' '"' file '"' '"'
+#
+
+Control File Documentation:
+
+  A control file is intended to be used by controller programs similar to how the code:"fakefile" is used by the bold:"Featureless Make" program.
+
+  A control file consists of the file:"entry.txt", file:"exit.txt", and file:"rule.txt" all merged into a single file.
+  The goal of of a control file is to collapse the directory hierarchy structure into a single file.
+  This imposes no new Objects or other structures.
+
+  Any modifications, or deviations, from this standard that add additional things beyond the entry, exit, and rule are encouraged to follow the naming prefix pattern.
+  The naming pattern is the simple standard name, like code:"entry", code:"exit", or code:"rule", followed by an underscore as a prefix.
+  Then all Object names would then have that prefix.
+  For example if there were a specification called file:"system" with an Object called code:"power_on" at the top level, then the control file representation would be code:"system_power_on".
diff --git a/documents/initfile.txt b/documents/initfile.txt
new file mode 100644 (file)
index 0000000..816eef5
--- /dev/null
@@ -0,0 +1,16 @@
+# fss-0002 iki-0000
+#
+# license: open-standard-license-1.0-or-later
+# version 2025/11/17
+#
+# This file (assumed to be named initfile.txt) can be more easily read using the following iki_read commands:
+#   iki_read initfile.txt +Q -w -W file '"' '"'
+#
+# To read the "Init File Documentation" section of this file, use this command sequence:
+#   fss_basic_list_read initfile.txt +Q -cn "Init File Documentation" | iki_read +Q -w -W file '"' '"'
+#
+
+Init File Documentation:
+
+  An init file is the init program specific version of the file:"controlfile".
+  The init program should use an file:"initfile" instead of a file:"controlfile", if supported.
index b14998d09c93cea7defa2a608a5d23a7b161b5eb..6c76ad550476433e05bdf136a340d235355631c1 100644 (file)
@@ -9,6 +9,10 @@ extern "C" {
   const f_string_static_t controller_program_name_long_s = macro_f_string_static_t_initialize_1(CONTROLLER_program_name_long_s, 0, CONTROLLER_program_name_long_s_length);
 #endif // _di_controller_program_name_s_
 
+#if !defined(_di_controller_controlfile_s_) && defined(_support_controller_controlfile_)
+  const f_string_static_t controller_controlfile_s = macro_f_string_static_t_initialize_1(CONTROLLER_controlfile_s, 0, CONTROLLER_controlfile_s_length);
+#endif // !defined(_di_controller_controlfile_s_) && defined(_support_controller_controlfile_)
+
 #ifndef _di_controller_default_s_
   const f_string_static_t controller_default_engine_s = macro_f_string_static_t_initialize_1(CONTROLLER_default_engine_s, 0, CONTROLLER_default_engine_s_length);
   const f_string_static_t controller_default_path_pid_s = macro_f_string_static_t_initialize_1(CONTROLLER_default_path_pid_s, 0, CONTROLLER_default_path_pid_s_length);
index 4870bc01f3eedeb71ff2c01d55b1597e5aab25c4..3c54dd58934b0a5f940bf02bfae6d10a03a5ab93 100644 (file)
@@ -28,6 +28,15 @@ extern "C" {
 #endif // _di_controller_program_name_s_
 
 /**
+ * Controlfile strings.
+ */
+#if !defined(_di_controller_controlfile_s_) && defined(_support_controller_controlfile_)
+  #define CONTROLLER_controlfile_s "controlfile"
+
+  #define CONTROLLER_controlfile_s_length 11
+#endif // !defined(_di_controller_controlfile_s_) && defined(_support_controller_controlfile_)
+
+/**
  * The program defaults.
  */
 #ifndef _di_controller_default_s_
index c34bf3e58caa8d4bf773ceb582ede6ebac766751..bdc0874c337edc3aafccca60e826c4e29bed1b9a 100644 (file)
@@ -9,6 +9,10 @@ extern "C" {
   const f_string_static_t controller_program_name_long_s = macro_f_string_static_t_initialize_1(CONTROLLER_program_name_long_s, 0, CONTROLLER_program_name_long_s_length);
 #endif // _di_controller_program_name_s_
 
+#if !defined(_di_controller_controlfile_s_) && defined(_support_controller_controlfile_)
+  const f_string_static_t controller_controlfile_s = macro_f_string_static_t_initialize_1(CONTROLLER_controlfile_s, 0, CONTROLLER_controlfile_s_length);
+#endif // !defined(_di_controller_controlfile_s_) && defined(_support_controller_controlfile_)
+
 #ifndef _di_controller_default_s_
   const f_string_static_t controller_default_engine_s = macro_f_string_static_t_initialize_1(CONTROLLER_default_engine_s, 0, CONTROLLER_default_engine_s_length);
   const f_string_static_t controller_default_path_pid_s = macro_f_string_static_t_initialize_1(CONTROLLER_init_default_path_pid_s, 0, CONTROLLER_init_default_path_pid_s_length);
index 3e3aac5c48ded76300bef27cc6c25fdda74c0750..eba5edb9c5f3733dd2392e7368c7464fc7ecad9b 100644 (file)
@@ -28,6 +28,15 @@ extern "C" {
 #endif // _di_controller_program_name_s_
 
 /**
+ * Controlfile strings.
+ */
+#if !defined(_di_controller_controlfile_s_) && defined(_support_controller_controlfile_)
+  #define CONTROLLER_controlfile_s "initfile"
+
+  #define CONTROLLER_controlfile_s_length 8
+#endif // !defined(_di_controller_controlfile_s_) && defined(_support_controller_controlfile_)
+
+/**
  * The program defaults.
  */
 #ifndef _di_controller_default_s_
index 01825f6f2fb29e1ac166d6b5b26851577d43b4a6..144aa7919b4c3d59762cc3c2fa3d3155c924a5cc 100644 (file)
@@ -87,6 +87,7 @@ extern "C" {
  *   - fine:                   Check if status is "fine".
  *   - help:                   Print help.
  *   - interruptible:          The process is interruptible.
+ *   - loaded:                 Designate that the files are all loaded (generally only set when a controlfile exists and is loaded).
  *   - pid:                    Designate that a custom PID is specified.
  *   - pipe:                   Use the input pipe.
  *   - simulate:               Perform simulation of rules rather than execution.
@@ -102,12 +103,13 @@ extern "C" {
   #define controller_main_flag_fine_d                   0x8
   #define controller_main_flag_help_d                   0x10
   #define controller_main_flag_interruptible_d          0x20
-  #define controller_main_flag_pid_d                    0x40
-  #define controller_main_flag_pipe_d                   0x80
-  #define controller_main_flag_simulate_d               0x100
-  #define controller_main_flag_validate_d               0x200
-  #define controller_main_flag_version_d                0x400
-  #define controller_main_flag_version_copyright_help_d 0x411
+  #define controller_main_flag_loaded_d                 0x40
+  #define controller_main_flag_pid_d                    0x80
+  #define controller_main_flag_pipe_d                   0x100
+  #define controller_main_flag_simulate_d               0x200
+  #define controller_main_flag_validate_d               0x400
+  #define controller_main_flag_version_d                0x800
+  #define controller_main_flag_version_copyright_help_d 0x811
 #endif // _di_controller_main_flag_d_
 
 /**
index e7b9f95557d8d4d5e6ef47b0c83d64fba4b2a420..fb1d83f70892cc1eaf9432e91baf573ce292ef2f 100644 (file)
@@ -8,6 +8,12 @@ extern "C" {
   const f_string_static_t controller_program_version_s = macro_f_string_static_t_initialize_1(CONTROLLER_program_version_s, 0, CONTROLLER_program_version_s_length);
 #endif // _di_controller_program_version_s_
 
+#if !defined(_di_controller_controlfile_s_) && defined(_support_controller_controlfile_)
+  const f_string_static_t controller_prefix_entry_s = macro_f_string_static_t_initialize_1(CONTROLLER_prefix_entry_s, 0, CONTROLLER_prefix_entry_s_length);
+  const f_string_static_t controller_prefix_exit_s = macro_f_string_static_t_initialize_1(CONTROLLER_prefix_exit_s, 0, CONTROLLER_prefix_exit_s_length);
+  const f_string_static_t controller_prefix_rule_s = macro_f_string_static_t_initialize_1(CONTROLLER_prefix_rule_s, 0, CONTROLLER_prefix_rule_s_length);
+#endif // !defined(_di_controller_controlfile_s_) && defined(_support_controller_controlfile_)
+
 #ifndef _di_controller_parameter_s_
   const f_string_static_t controller_short_cgroup_s = macro_f_string_static_t_initialize_1(CONTROLLER_short_cgroup_s, 0, CONTROLLER_short_cgroup_s_length);
   const f_string_static_t controller_short_daemon_s = macro_f_string_static_t_initialize_1(CONTROLLER_short_daemon_s, 0, CONTROLLER_short_daemon_s_length);
index 413c7a97e59acd9e835bc8d82c71e307606e9a29..dd3438594b6b9961324fbd393de115130343bae0 100644 (file)
@@ -56,6 +56,27 @@ extern "C" {
 #endif // _di_controller_program_name_s_
 
 /**
+ * Controlfile strings.
+ *
+ * The controller_controlfile_s implementation is defined within the individual programs.
+ */
+#if !defined(_di_controller_controlfile_s_) && defined(_support_controller_controlfile_)
+  #define CONTROLLER_prefix_entry_s "entry_"
+  #define CONTROLLER_prefix_exit_s  "exit_"
+  #define CONTROLLER_prefix_rule_s  "rule_"
+
+  #define CONTROLLER_prefix_entry_s_length 6
+  #define CONTROLLER_prefix_exit_s_length  5
+  #define CONTROLLER_prefix_rule_s_length  5
+
+  extern const f_string_static_t controller_controlfile_s;
+
+  extern const f_string_static_t controller_prefix_entry_s_length;
+  extern const f_string_static_t controller_prefix_exit_s_length;
+  extern const f_string_static_t controller_prefix_rule_s_length;
+#endif // !defined(_di_controller_controlfile_s_) && defined(_support_controller_controlfile_)
+
+/**
  * The main program parameters.
  */
 #ifndef _di_controller_parameter_s_
index 4573abc311652ad00affeed120fbc95fdd43b59a..851254fa71bc984e2a2fa7d4530489d4348dce03 100644 (file)
@@ -72,6 +72,7 @@ extern "C" {
     main->thread.cache.action.name_action.used = 0;
     main->thread.cache.action.name_item.used = 0;
 
+    // @todo Check if controlfile, then load that using the prefix, otherwise, operate normally. The full controlfile will be loaded once. Maybe make the file load a callback?
     if (F_status_is_error_not(state.status)) {
       if (is_entry) {
         state.status = controller_file_load(main, F_true, controller_entries_s, main->process.name_entry, controller_entry_s);
index 639fd5e7b79e9399fca4ac74403dc6c310a69dd5..f2f2b6af8d9e76aff691a39468f5cbf497d91cac 100644 (file)
@@ -56,69 +56,25 @@ extern "C" {
     else {
       status = controller_thread_signal_create(main, &controller_thread_signal_normal);
 
-      if (F_status_is_error_not(status)) {
-
-        // Start the clean up thread so that it can handle any zombie reaping.
-        if (!(main->setting.flag & controller_main_flag_validate_d) && main->process.mode == controller_process_mode_service_e) {
-          if (!main->thread.id_cleanup) {
-            status = f_thread_create(0, &main->thread.id_cleanup, &controller_thread_cleanup, (void *) main);
-          }
-        }
-      }
-
       if (F_status_is_error(status)) {
         main->thread.id_signal = 0;
-        main->thread.id_cleanup = 0;
 
         controller_print_error_status(&main->program.error, F_status_debug_source_d, status);
       }
       else {
-        if (main->setting.flag & controller_main_flag_daemon_d) {
-          main->process.ready = controller_process_ready_done_e;
-
-          if (f_file_exists(main->process.path_pid, F_true) == F_true) {
-            status = F_status_set_error(F_available_not);
-            main->process.ready = controller_process_ready_abort_e;
-
-            controller_print_error_file_pid_exists(&main->program.error, &main->thread, main->process.path_pid);
-          }
-        }
-        else if (main->process.name_entry.used) {
-          status = f_thread_create(0, &main->thread.id_entry, &controller_thread_entry, (void *) main);
-
-          if (F_status_is_error(status)) {
-            controller_print_error_status(&main->program.error, F_status_debug_source_d, status);
-          }
-          else {
-            if (main->thread.id_entry) {
-              controller_thread_join(&main->thread.id_entry);
-            }
+        #ifdef _support_controller_controlfile_
+          // @todo load the controlfile/initfile and start the thread. controller_main_flag_loaded_d
+        #endif // _support_controller_controlfile_
 
-            status = main->thread.status;
-          }
+        if (F_status_is_error_not(status)) {
+          status = controller_process_prepare(main);
         }
       }
     }
 
     // Only make the Rule and control threads available once any/all pre-processing is complete.
     if (F_status_is_error_not(status) && status != F_failure && status != F_child && controller_thread_enable_get(&main->thread) == controller_thread_enable_e) {
-      if (!(main->setting.flag & controller_main_flag_validate_d)) {
-
-        // Wait for the Entry thread to complete before starting the Rule thread.
-        if (main->thread.id_rule) {
-          controller_thread_join(&main->thread.id_rule);
-        }
-
-        if (controller_thread_enable_get(&main->thread) > controller_thread_enable_not_e && main->process.mode == controller_process_mode_service_e) {
-          status = f_thread_create(0, &main->thread.id_rule, &controller_thread_rule, (void *) main);
-
-          if (F_status_is_error(status)) {
-            main->thread.id_rule = 0;
-
-            controller_print_error_status(&main->program.error, F_status_debug_source_d, status);
-          }
-        }
-      }
+      status = controller_process_run(main);
     }
 
     if (status == F_child) {
@@ -175,6 +131,90 @@ extern "C" {
   }
 #endif // _di_controller_process_
 
+#ifndef _di_controller_process_prepare_
+  f_status_t controller_process_prepare(controller_t * const main) {
+
+    if (!main) return F_status_set_error(F_parameter);
+
+    f_status_t status = F_okay;
+
+    // Start the clean up thread so that it can handle any zombie reaping.
+    if (!(main->setting.flag & controller_main_flag_validate_d) && main->process.mode == controller_process_mode_service_e) {
+      if (!main->thread.id_cleanup) {
+        status = f_thread_create(0, &main->thread.id_cleanup, &controller_thread_cleanup, (void *) main);
+
+        if (F_status_is_error(status)) {
+          main->thread.id_cleanup = 0;
+
+          controller_print_error_status(&main->program.error, F_status_debug_source_d, status);
+        }
+      }
+    }
+
+    if (F_status_is_error_not(status)) {
+      if (main->setting.flag & controller_main_flag_daemon_d) {
+        status = F_okay;
+        main->process.ready = controller_process_ready_done_e;
+
+        if (f_file_exists(main->process.path_pid, F_true) == F_true) {
+          status = F_status_set_error(F_available_not);
+          main->process.ready = controller_process_ready_abort_e;
+
+          controller_print_error_file_pid_exists(&main->program.error, &main->thread, main->process.path_pid);
+        }
+      }
+      else if (main->process.name_entry.used) {
+        status = f_thread_create(0, &main->thread.id_entry, &controller_thread_entry, (void *) main);
+
+        if (F_status_is_error(status)) {
+          controller_print_error_status(&main->program.error, F_status_debug_source_d, status);
+        }
+        else {
+          if (main->thread.id_entry) {
+            controller_thread_join(&main->thread.id_entry);
+          }
+
+          status = main->thread.status;
+        }
+      }
+    }
+
+    return status;
+  }
+#endif // _di_controller_process_prepare_
+
+#ifndef _di_controller_process_run_
+  f_status_t controller_process_run(controller_t * const main) {
+
+    if (!main) return F_status_set_error(F_parameter);
+
+    f_status_t status = F_okay;
+
+    if (!(main->setting.flag & controller_main_flag_validate_d)) {
+
+      // Wait for the Entry thread to complete before starting the Rule thread.
+      if (main->thread.id_rule) {
+        controller_thread_join(&main->thread.id_rule);
+      }
+
+      if (controller_thread_enable_get(&main->thread) > controller_thread_enable_not_e && main->process.mode == controller_process_mode_service_e) {
+        status = f_thread_create(0, &main->thread.id_rule, &controller_thread_rule, (void *) main);
+
+        if (F_status_is_error(status)) {
+          main->thread.id_rule = 0;
+
+          controller_print_error_status(&main->program.error, F_status_debug_source_d, status);
+        }
+        else {
+          status = F_okay;
+        }
+      }
+    }
+
+    return status;
+  }
+#endif // _di_controller_process_run_
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
index cc27edd37b370a3116eec41cf3b6c698b3c908dd..2f310535e43bb5b5cae76c154561e5b14ed2aaf9 100644 (file)
@@ -33,7 +33,7 @@ extern "C" {
  *
  *   Must not be NULL.
  *
- *   This alters main.setting.state.status:
+ *   This does not alter main.setting.state.status.
  *
  * @return
  *   F_child on child process exiting.
@@ -43,11 +43,71 @@ extern "C" {
  *   F_parameter (with error bit) if a parameter is invalid.
  *
  *   F_failure (with error bit) on any other error.
+ *
+ *   Errors (with error bit) from: controller_process_prepare()
+ *
+ * @see controller_process_prepare()
  */
 #ifndef controller_process
   extern f_status_t controller_process(controller_t * const main);
 #endif // controller_process
 
+/**
+ * Perform the normal process preparation operations.
+ *
+ * This prints on error.
+ *
+ * @param main
+ *   The main program data and settings.
+ *
+ *   Must not be NULL.
+ *
+ *   This does not alter main.setting.state.status.
+ *
+ * @return
+ *   F_okay on success.
+ *
+ *   F_available_not (with error bit) if the path PID file already exists while operaeting as controller_main_flag_daemon_d.
+ *   F_parameter (with error bit) if a parameter is invalid.
+ *
+ *   The value of main->thread.status may be returned if the entry thread runs and completes.
+ *
+ *   Errors (with error bit) from: f_thread_create()
+ *
+ * @see f_thread_create()
+ */
+#ifndef _di_controller_process_prepare_
+  extern f_status_t controller_process_prepare(controller_t * const main);
+#endif // _di_controller_process_prepare_
+
+/**
+ * Perform the normal process run operations.
+ *
+ * This prints on error.
+ *
+ * @param main
+ *   The main program data and settings.
+ *
+ *   Must not be NULL.
+ *
+ *   This does not alter main.setting.state.status.
+ *
+ * @return
+ *   F_okay on success.
+ *
+ *   F_available_not (with error bit) if the path PID file already exists while operaeting as controller_main_flag_daemon_d.
+ *   F_parameter (with error bit) if a parameter is invalid.
+ *
+ *   The value of main->thread.status may be returned if the entry thread runs and completes.
+ *
+ *   Errors (with error bit) from: f_thread_create()
+ *
+ * @see f_thread_create()
+ */
+#ifndef _di_controller_process_run_
+  extern f_status_t controller_process_run(controller_t * const main);
+#endif // _di_controller_process_run_
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
diff --git a/specifications/controlfile.txt b/specifications/controlfile.txt
new file mode 100644 (file)
index 0000000..ab904bd
--- /dev/null
@@ -0,0 +1,38 @@
+# fss-0002 iki-0000
+#
+# license: open-standard-license-1.0-or-later
+# version 2025/11/17
+#
+# This file (assumed to be named controlfile.txt) can be more easily read using the following iki_read commands:
+#   iki_read controlfile.txt +Q -w -WW code '"' '"' file '"' '"'
+#
+# To read the "Controlfile Specification" section of this file, use this command sequence:
+#   fss_basic_list_read controlfile.txt +Q -cn "Controlfile Specification" | iki_read +Q -w -WW code '"' '"' file '"' '"'
+#
+
+Controlfile Specification:
+  A control file follows the code:"FSS-0005 (Somewhat Basic List)" and the code:"FSS-000D (Basic Rule)" format with bold:"IKI-0000 (Unrestricted)" format.
+
+  A control file name is expected to have the exact name of file:"controlfile".
+  The file:"controlfile" is all lower case and has no file extension.
+
+  A control file consists of the file:"entry.txt", file:"exit.txt", and file:"rule.txt" all merged into a single file.
+  All of the specifications apply, except for the name of the Objects.
+  The names of the Objects are as follows.
+
+  For file:"entry.txt":
+    - The code:"main" is now called code:"entry_main".
+    - The code:"settings" is now called code:"entry_settings".
+
+  For file:"exit.txt":
+    - The code:"main" is now called code:"exit_main".
+    - The code:"settings" is now called code:"exit_settings".
+
+  For file:"rule.txt":
+    - The code:"command" is now called code:"rule_command".
+    - The code:"script" is now called code:"rule_script".
+    - The code:"service" is now called code:"rule_service".
+    - The code:"settings" is now called code:"rule_settings".
+    - The code:"utility" is now called code:"rule_utility".
+
+  Any additional specifications brought in are be expected to follow the same pattern for combining.
diff --git a/specifications/initfile.txt b/specifications/initfile.txt
new file mode 100644 (file)
index 0000000..00707ed
--- /dev/null
@@ -0,0 +1,17 @@
+# fss-0002 iki-0000
+#
+# license: open-standard-license-1.0-or-later
+# version 2025/11/17
+#
+# This file (assumed to be named initfile.txt) can be more easily read using the following iki_read commands:
+#   iki_read initfile.txt +Q -w -W file '"' '"'
+#
+# To read the "Init File Specification" section of this file, use this command sequence:
+#   fss_basic_list_read initfile.txt +Q -cn "Init File Specification" | iki_read +Q -w -W file '"' '"'
+#
+
+Init File Specification:
+  An init file is identical to a control file in all but name.
+
+  An Initfile file name is expected to have the exact name of file:"initfile".
+  The file:"initfile" is all lower case and has no file extension.