From fe268326b1c2bf2a558ba7a86ee6da0c53ace21e Mon Sep 17 00:00:00 2001 From: Kevin Day Date: Wed, 24 Jul 2024 22:49:29 -0500 Subject: [PATCH] Update: Initial import of the project, migrating from the FLL 0.7 level 3 project. I also copied several files from the controller project to help kick start setting this up. I have not yet configured anything to compile or work. There is still quite a lot of work to complete before the migration is complete. --- .gitignore | 9 + data/build/defines | 18 + data/build/dependencies | 31 + data/build/fakefile | 29 + data/build/settings | 94 +++ data/build/settings.control | 94 +++ data/documentation/man/man1/control.1 | 77 ++ data/settings/settings | 6 + documents/packet_request.txt | 38 + documents/packet_response.txt | 53 ++ documents/readme.build.txt | 335 ++++++++ documents/readme.txt | 27 + documents/settings.txt | 65 ++ install.sh | 880 +++++++++++++++++++++ licenses/cc-by-sa-4.0 | 4 + licenses/copyrights.txt | 5 + licenses/lgpl-2.1-or-later | 504 ++++++++++++ licenses/open-standard-license-1.0-or-later | 44 ++ sources/c/program/control/control/config.c | 1 + sources/c/program/control/control/config.h | 0 sources/c/program/control/control/control.c | 9 + sources/c/program/control/control/control.h | 25 + sources/c/program/control/control/main.c | 51 ++ sources/c/program/control/control/main.h | 38 + sources/c/program/control/control/string.c | 25 + sources/c/program/control/control/string.h | 41 + sources/c/program/control/main/action.c | 233 ++++++ sources/c/program/control/main/action.h | 66 ++ sources/c/program/control/main/common.c | 401 ++++++++++ sources/c/program/control/main/common.h | 48 ++ sources/c/program/control/main/common/define.c | 9 + sources/c/program/control/main/common/define.h | 67 ++ .../c/program/control/main/common/enumeration.c | 9 + .../c/program/control/main/common/enumeration.h | 159 ++++ sources/c/program/control/main/common/print.c | 34 + sources/c/program/control/main/common/print.h | 69 ++ sources/c/program/control/main/common/string.c | 71 ++ sources/c/program/control/main/common/string.h | 273 +++++++ sources/c/program/control/main/common/type.c | 47 ++ sources/c/program/control/main/common/type.h | 216 +++++ sources/c/program/control/main/control.c | 110 +++ sources/c/program/control/main/control.h | 102 +++ sources/c/program/control/main/main.c | 77 ++ sources/c/program/control/main/main.h | 38 + sources/c/program/control/main/packet.c | 559 +++++++++++++ sources/c/program/control/main/packet.h | 155 ++++ sources/c/program/control/main/payload.c | 38 + sources/c/program/control/main/payload.h | 51 ++ sources/c/program/control/main/print/data.c | 9 + sources/c/program/control/main/print/data.h | 23 + sources/c/program/control/main/print/debug.c | 59 ++ sources/c/program/control/main/print/debug.h | 79 ++ sources/c/program/control/main/print/error.c | 419 ++++++++++ sources/c/program/control/main/print/error.h | 494 ++++++++++++ sources/c/program/control/main/print/message.c | 82 ++ sources/c/program/control/main/print/message.h | 74 ++ sources/c/program/control/main/print/warning.c | 65 ++ sources/c/program/control/main/print/warning.h | 92 +++ sources/c/program/control/main/process.c | 9 + sources/c/program/control/main/process.h | 23 + sources/c/program/control/main/signal.c | 111 +++ sources/c/program/control/main/signal.h | 86 ++ sources/c/program/control/main/thread.c | 22 + sources/c/program/control/main/thread.h | 46 ++ specifications/settings.txt | 26 + 65 files changed, 7054 insertions(+) create mode 100644 .gitignore create mode 100644 data/build/defines create mode 100644 data/build/dependencies create mode 100644 data/build/fakefile create mode 100644 data/build/settings create mode 100644 data/build/settings.control create mode 100644 data/documentation/man/man1/control.1 create mode 100644 data/settings/settings create mode 100644 documents/packet_request.txt create mode 100644 documents/packet_response.txt create mode 100644 documents/readme.build.txt create mode 100644 documents/readme.txt create mode 100644 documents/settings.txt create mode 100755 install.sh create mode 100644 licenses/cc-by-sa-4.0 create mode 100644 licenses/copyrights.txt create mode 100644 licenses/lgpl-2.1-or-later create mode 100644 licenses/open-standard-license-1.0-or-later create mode 100644 sources/c/program/control/control/config.c create mode 100644 sources/c/program/control/control/config.h create mode 100644 sources/c/program/control/control/control.c create mode 100644 sources/c/program/control/control/control.h create mode 100644 sources/c/program/control/control/main.c create mode 100644 sources/c/program/control/control/main.h create mode 100644 sources/c/program/control/control/string.c create mode 100644 sources/c/program/control/control/string.h create mode 100644 sources/c/program/control/main/action.c create mode 100644 sources/c/program/control/main/action.h create mode 100644 sources/c/program/control/main/common.c create mode 100644 sources/c/program/control/main/common.h create mode 100644 sources/c/program/control/main/common/define.c create mode 100644 sources/c/program/control/main/common/define.h create mode 100644 sources/c/program/control/main/common/enumeration.c create mode 100644 sources/c/program/control/main/common/enumeration.h create mode 100644 sources/c/program/control/main/common/print.c create mode 100644 sources/c/program/control/main/common/print.h create mode 100644 sources/c/program/control/main/common/string.c create mode 100644 sources/c/program/control/main/common/string.h create mode 100644 sources/c/program/control/main/common/type.c create mode 100644 sources/c/program/control/main/common/type.h create mode 100644 sources/c/program/control/main/control.c create mode 100644 sources/c/program/control/main/control.h create mode 100644 sources/c/program/control/main/main.c create mode 100644 sources/c/program/control/main/main.h create mode 100644 sources/c/program/control/main/packet.c create mode 100644 sources/c/program/control/main/packet.h create mode 100644 sources/c/program/control/main/payload.c create mode 100644 sources/c/program/control/main/payload.h create mode 100644 sources/c/program/control/main/print/data.c create mode 100644 sources/c/program/control/main/print/data.h create mode 100644 sources/c/program/control/main/print/debug.c create mode 100644 sources/c/program/control/main/print/debug.h create mode 100644 sources/c/program/control/main/print/error.c create mode 100644 sources/c/program/control/main/print/error.h create mode 100644 sources/c/program/control/main/print/message.c create mode 100644 sources/c/program/control/main/print/message.h create mode 100644 sources/c/program/control/main/print/warning.c create mode 100644 sources/c/program/control/main/print/warning.h create mode 100644 sources/c/program/control/main/process.c create mode 100644 sources/c/program/control/main/process.h create mode 100644 sources/c/program/control/main/signal.c create mode 100644 sources/c/program/control/main/signal.h create mode 100644 sources/c/program/control/main/thread.c create mode 100644 sources/c/program/control/main/thread.h create mode 100644 specifications/settings.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ebdab6f --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +/build/ +/build/* + +# Disclude FLL sources dropped for stand alone building. +sources/c/fll + +# Do not include the local configuration sources. +sources/c/config.c +sources/c/config.h diff --git a/data/build/defines b/data/build/defines new file mode 100644 index 0000000..822a9b6 --- /dev/null +++ b/data/build/defines @@ -0,0 +1,18 @@ +# fss-0000 + +_di_thread_support_ Disables thread support. + +_controller_as_init_ The controller program is compiled as an init replacement and this control program should treat it as such. +_override_controller_name_socket_ Use this as the default custom file name representing the controller program socket. +_override_controller_name_socket_length_ The number of bytes representing the string in _override_controller_name_socket_ (not including the terminating NULL). +_override_controller_path_socket_ Use this as the default custom directory path representing the location of the controller program socket. +_override_controller_path_socket_length_ The number of bytes representing the string in _override_controller_path_socket_ (not including the terminating NULL). +_override_controller_path_socket_prefix_ Use this as the default custom prefix prepended to the file name of the file representing the controller program socket. +_override_controller_path_socket_prefix_length_ The number of bytes representing the string in _override_controller_path_socket_prefix_ (not including the terminating NULL). +_override_controller_path_socket_suffix_ Use this as the default custom prefix prepended to the file name of the file representing the controller program socket. +_override_controller_path_socket_suffix_length_ The number of bytes representing the string in _override_controller_path_socket_suffix_ (not including the terminating NULL). +_override_control_path_settings_ Use this as the default full path to the controlsettings. +_override_control_path_settings_length_ The number of bytes representing the string in _override_controller_path_settings_length_ (not including the terminating NULL). + +_pthread_attr_unsupported_ Disable non-portable functionality associated with pthread_attr. +_pthread_sigqueue_unsupported_ Disable GNU specific sigqueue(). diff --git a/data/build/dependencies b/data/build/dependencies new file mode 100644 index 0000000..b30996e --- /dev/null +++ b/data/build/dependencies @@ -0,0 +1,31 @@ +# fss-0000 + +f_type +f_status +f_memory +f_type_array +f_string +f_utf +f_color +f_compare +f_console +f_file +f_fss +f_parse +f_path +f_pipe +f_print +f_rip +f_signal +f_socket +f_status_string +f_thread + +fl_fss +fl_print + +fll_error +fll_fss +fll_print +fll_program +fll_status_string diff --git a/data/build/fakefile b/data/build/fakefile new file mode 100644 index 0000000..affda72 --- /dev/null +++ b/data/build/fakefile @@ -0,0 +1,29 @@ +# fss-0005 iki-0002 + +settings: + fail exit + modes individual individual_thread level monolithic clang test fanalyzer coverage thread threadlesss + + 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 settings + build settings.control + +install: + shell ./install.sh parameter:'work' parameter:'verbosity' parameter:'color' + shell ./install.sh parameter:'work' parameter:'verbosity' parameter:'color' -s data/build/settings.control + +help: + print + print context:'title'Fakefile Options for the Control Program.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'install:context:'reset' A helper operation that calls the ./install.sh script for the control program." + print " - context:'notable'main:context:'reset' Compilation using the build settings mode for the control program (default)." + + print + print The context:'notable'install context:'reset'operation supports the context:'notable'work,context:'reset' context:'notable'verbosity,context:'reset' and context:'notable'color context:'reset'parameters. diff --git a/data/build/settings b/data/build/settings new file mode 100644 index 0000000..5dfb9e9 --- /dev/null +++ b/data/build/settings @@ -0,0 +1,94 @@ +# fss-0001 +# +# Modes: +# - 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. +# - clang: Use clang rather than the default, which is generally gcc. +# - gcc: Use gcc specific settings. +# - test: Compile for a test, such as unit testing. +# - fanalyzer: Compile using GCC's -fanalyzer compile time option. +# - coverage: Compile for building coverage. +# - thread: Compile with thread support. +# - threadless: Compile without thread support. +# + +build_name control + +version_major 0 +version_minor 7 +version_micro 0 +version_file micro +version_target minor + +modes individual individual_thread level monolithic clang gcc test fanalyzer coverage thread threadless +modes_default monolithic thread gcc + +build_compiler gcc +build_compiler-clang clang +build_indexer ar +build_indexer_arguments rcs +build_language c + +build_libraries -lc +build_libraries-individual -lfll_error -lfll_fss -lfll_print -lfll_program +build_libraries-individual -lfl_conversion -lfl_fss -lfl_print -lfl_status_string +build_libraries-individual -lf_color -lf_compare -lf_console -lf_conversion -lf_file -lf_fss -lf_memory -lf_parse -lf_path -lf_pipe -lf_print -lf_rip -lf_signal -lf_socket -lf_status_string -lf_string -lf_type_array -lf_utf +build_libraries-individual_thread -lf_thread +build_libraries-level -lfll_2 -lfll_1 -lfll_0 +build_libraries-monolithic -lfll + +build_sources_library main/common.c main/common/define.c main/common/enumeration.c main/common/print.c main/common/string.c main/common/type.c +build_sources_library main/print/data.c main/print/debug.c main/print/error.c main/print/message.c main/print/warning.c +build_sources_library main/action.c main/control.c main/packet.c main/payload.c main/signal.c main/thread.c + +build_sources_headers main/common.h main/common/define.h main/common/enumeration.h main/common/print.h main/common/string.h main/common/type.h +build_sources_headers main/print/data.h main/print/debug.h main/print/error.h main/print/message.h main/print/warning.h +build_sources_headers main/action.h main/control.h main/packet.h main/payload.h main/signal.h main/thread.h + +build_sources_documentation man + +build_script yes +build_shared yes +build_static no + +path_headers program/control/main +path_library_script script +path_library_shared shared +path_library_static static +path_object_script script +path_object_shared shared +path_object_static static +path_program_script script +path_program_shared shared +path_program_static static + +has_path_standard no +preserve_path_headers yes + +search_exclusive yes +search_shared yes +search_static yes + +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_di_libcap_ +defines -D_libcap_legacy_only_ +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-clang -Wno-logical-op-parentheses +flags-coverage -O0 --coverage -fprofile-abs-path -fprofile-dir=build/coverage/ +flags-fanalyzer -fanalyzer +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 diff --git a/data/build/settings.control b/data/build/settings.control new file mode 100644 index 0000000..b08422b --- /dev/null +++ b/data/build/settings.control @@ -0,0 +1,94 @@ +# fss-0001 +# +# Modes: +# - 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. +# - clang: Use CLang rather than the default, which is generally GCC. +# - gcc: Use GCC specific settings. +# - test: Compile for a test, such as unit testing. +# - fanalyzer: Compile using GCC's -fanalyzer compile time option. +# - coverage: Compile for building coverage. +# - thread: Compile with thread support. +# - threadless: Compile without thread support. +# + +build_name control + +version_major 0 +version_minor 7 +version_micro 0 +version_file micro +version_target minor + +modes individual individual_thread level monolithic clang gcc test fanalyzer coverage thread threadless +modes_default monolithic thread gcc + +build_compiler gcc +build_compiler-clang clang +build_indexer ar +build_indexer_arguments rcs +build_language c + +build_libraries -lc -lcap -lcontrol +build_libraries-individual -lfll_control_group -lfll_error -lfll_execute -lfll_fss -lfll_print -lfll_program +build_libraries-individual_thread -lf_thread +build_libraries-individual -lfl_control_group -lfl_conversion -lfl_directory -lfl_environment -lfl_execute -lfl_fss -lfl_iki -lfl_path -lfl_print +build_libraries-individual -lf_abstruse -lf_account -lf_capability -lf_color -lf_compare -lf_console -lf_control_group -lf_conversion -lf_directory -lf_environment -lf_execute -lf_file -lf_fss -lf_iki -lf_limit -lf_memory -lf_parse -lf_path -lf_pipe -lf_print -lf_rip -lf_signal -lf_socket -lf_string -lf_time -lf_type_array -lf_utf +build_libraries-individual_thread -lf_thread +build_libraries-level -lfll_2 -lfll_1 -lfll_0 +build_libraries-monolithic -lfll + +build_sources_program config.c main.c control.c string.c + +build_sources_headers control.h string.h + +build_sources_documentation man + +build_sources_setting control + +build_script yes +build_shared yes +build_static no + +path_headers program/control/control +path_library_script script +path_library_shared shared +path_library_static static +path_object_script script +path_object_shared shared +path_object_static static +path_program_script script +path_program_shared shared +path_program_static static +path_sources sources/c/program/control/control + +has_path_standard no +preserve_path_headers yes + +search_exclusive yes +search_shared yes +search_static yes + +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_di_libcap_ +defines -D_libcap_legacy_only_ +defines-threadless -D_di_thread_support_ +defines-thread -D_pthread_attr_unsupported_ -D_pthread_sigqueue_unsupported_ + +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-thread -pthread +flags-clang -Wno-logical-op-parentheses +flags-gcc_13 -fstrict-flex-arrays=3 +flags-test -O0 -fstack-protector-strong -Wall +flags-fanalyzer -fanalyzer +flags-coverage -O0 --coverage -fprofile-abs-path -fprofile-dir=build/coverage/ + +flags_library -fPIC +flags_object -fPIC +flags_program -fPIE diff --git a/data/documentation/man/man1/control.1 b/data/documentation/man/man1/control.1 new file mode 100644 index 0000000..b1fd5c5 --- /dev/null +++ b/data/documentation/man/man1/control.1 @@ -0,0 +1,77 @@ +.TH CONTROL "1" "March 2023" "FLL - Control 0.7.0" "User Commands" +.SH NAME +control \- Give commands or make requests to the \fBcontroller\fR program. +.SH SYNOPSIS +.B control +[\fI\,OPTIONS\/\fR] [\fI\,ACTION\/\fR] +.SH DESCRIPTION +.PP +When the \fB\-\-socket\fR parameter represents a directory path then the file name is generated from either the \fB\-\-name\fR parameter or from the control settings file. + +A rule action allows for either the full rule path, such as '\fBboot/root\fR' as a single parameter or two parameters with the first representing the rule directory path '\fBboot\fR' and the second representing the rule base name '\fBroot\fR'. + +The \fB\-\-return\fR parameter is intended to be used for scripting and is of the form "\fBresponse [type] [action] [status]\fR". +Be sure to use the \fB++quiet\fR parameter to suppress output when using this in scripting. +No response is returned on program errors, especially those errors that prevent communicating to the controller. +.SH OPTIONS +.TP +\fB\{\-h, \-\-help\fR +Print the help message. +.TP +\fB+C, ++copyright\fR +Print the copyright. +.TP +\fB+d, ++dark\fR +Output using colors that show up better on dark backgrounds. +.TP +\fB+l, ++light\fR +Output using colors that show up better on light backgrounds. +.TP +\fB+n, ++no_color\fR +Do not print using color. +.TP +\fB+Q, ++quiet\fR +Decrease verbosity, silencing most output. +.TP +\fB+E, ++error\fR +Decrease verbosity, using only error output. +.TP +\fB+N, ++normal\fR +Set verbosity to normal. +.TP +\fB+V, ++verbose\fR +Increase verbosity beyond normal output. +.TP +\fB+D, ++debug\fR +Enable debugging, significantly increasing verbosity beyond normal output. +.TP +\fB+v, ++version\fR +Print only the version number. +.TP +\fB\-n, \-\-name\fR +Specify the name of the controller socket file. +.TP +\fB\-R, \-\-return\fR +Print a message about the response packet. +.TP +\fB\-s, \-\-settings\fR +Specify a directory path or a full path to the control settings file. +.TP +\fB\-k, \-\-socket\fR +Specify a directory path or a full path to the controller socket file. +.SH ACTION +.TP +The action to perform. +.SH SEE ALSO +.PP +\fBcontroller\fR(1), +\fBcontroller\-actions\fR(5), +\fBcontroller\-entry\fR(5), +\fBcontroller\-exit\fR(5), +\fBcontroller\-packet\fR(5), +\fBcontroller\-rule\fR(5) +.SH AUTHOR +Written by Kevin Day. +.SH COPYRIGHT +.PP +Copyright \(co 2007-2024 Kevin Day, GNU LGPL Version 2.1 or later. diff --git a/data/settings/settings b/data/settings/settings new file mode 100644 index 0000000..c2697e8 --- /dev/null +++ b/data/settings/settings @@ -0,0 +1,6 @@ +# fss-0001 + +name_socket default +path_socket /var/run/controller +path_socket_prefix controller- +path_socket_suffix .socket diff --git a/documents/packet_request.txt b/documents/packet_request.txt new file mode 100644 index 0000000..6f7e8c3 --- /dev/null +++ b/documents/packet_request.txt @@ -0,0 +1,38 @@ +# fss-0002 +# +# license: open-standard-license-1.0-or-later +# version 2024/07/02 +# + +Packet Request: + The Control program sends packets and receives packet responses from the Controller program over the Controller program's Control socket. + This documentation describes the request packets sent by this program. + + The purpose of these request packets are to ask the Controller program to perform a requested action. + + The communications to/from the Controller program utilize the FSS-000F (Simple Packet). + This Simple Packet is expected to contain within it the FSS-000E (Payload) format. + + The Payload contains within it a Header and a Payload. + Note the potential confusion here between the Payload when referring to the Packet format and the Payload referring to the Content within the Payload Packet. + To avoid this, henceforth "Payload Packet" refers to the FSS-000E (Payload) used within the FSS-000F (Simple Packet). + The "Payload Header" refers to either the Object ("header:") or Content within the Payload Packet representing the header. + The "Payload Object" refers to the Object within the Payload Packet (ie: "payload:"). + The "Payload Content" refers to the Content within the Payload Packet. + + The FSS-000E (Payload Packet) is very flexible in what it allows within the Header of the Payload Packet. + The Control program is more restrictive and supports only a subset of the possibilities (which this is explicitly allowed by the Specification). + + The Control program only supports the following Payload Packet header Objects for the request\: + - type: Allows only a single type header Object and may only be one of: "controller", "error", or "init". + - length: Allows only a single length header Object and must properly describe the length of the entire Payload Packet as per the referenced Specifications. + - action: Allows only a single action header Object and whose first Content must only be one of: "freeze", "kexec", "kill", "pause", "reboot", "reload", "rerun", "restart", "resume", "shutdown", "start", "stop", and "thaw". + + The number of Content associated with a action are specific to the requirements of that action. + + The Payload Content is not used and should always be of length 0. + + The Payload Content represents the rule being started in FSS-0001 (Extended) format, such as "service sshd". + + For example, given the command: "control start service sshd in 5 minutes". + The "action" Object would be "start service sshd in 5 minutes". diff --git a/documents/packet_response.txt b/documents/packet_response.txt new file mode 100644 index 0000000..8c12c48 --- /dev/null +++ b/documents/packet_response.txt @@ -0,0 +1,53 @@ +# fss-0002 +# +# license: open-standard-license-1.0-or-later +# version 2024/07/02 +# + +Packet Response: + The control program sends packets and receives packet responses from the controller program over the controller program's control socket. + This documentation describes the expected response packets and how these response packets are handled. + + The communications to/from the controller program utilize the FSS-000F (Simple Packet). + This Simple Packet is expected to contain within it the FSS-000E (Payload) format. + + The Payload contains within it a Header and a Payload. + Note the potential confusion here between the Payload when referring to the Packet format and the Payload referring to the Content within the Payload Packet. + The "Payload Header" refers to either the Object ("header:") or Content within the Payload Packet representing the header. + The "Payload Object" refers to the Object within the Payload Packet (ie: "payload:"). + The "Payload Content" refers to the Content within the Payload Packet. + + The Payload Packet (FSS-000E) is very flexible in what it allows within the Header of the Payload Packet. + The Control program is more restrictive and supports only a subset of the possibilities (which this is explicitly allowed by the Specification). + + The Control program only supports the following Payload Packet header Objects for the response\: + - status: Allows only a single status header Object. + - type: Allows only a single type header Object and may only be one of: "controller", "error", or "init". + - action: Allows only a single action header Object and must only be one of: "freeze", "kexec", "kill", "pause", "reboot", "reload", "rerun", "restart", "resume", "shutdown", "start", "stop", and "thaw". + - length: Allows only a single length header Object and must properly describe the length of the entire Payload Packet as per the referenced Specifications. + + The "error" type response Payload Packet is treated as an error response. + For any error responses that are associated with an action, then the action header is provided (otherwise it is not expected and is ignored). + For any error responses the Payload Content may be empty but when it is not, then the Payload Content is a single string representing a message further describing the error or the reason for the error. + These error responses represents errors in attempting to perform some action. + These responses are not used for actions that return an error as the result of a proper execution or performing of that action. + + The "controller" type response Payload Packet represents a response regarding the result of performing a requested action. + There must always be an "action" designating the action this response is in regards to. + Only the following are (currently) supported as a status\: + - F_busy: When unable to perform the action as because the service is too busy or the + - F_done: When successfully performed the action but the action does not return success or failure. + - F_failure: When successfully performed the action and the action returned some sort of failure or error. + - F_success: When successfully performed the action and the action returned some sort of success. + + When using the -R/--return parameter, the program prints specially formatted messages to help make the output more scriptable. + + The form is "response [type] [action] [status]". + Examples\: + - A response that designates the action failed might look like: "response controller my_action F_failure". + - A response that designates the controller ran out of memory, preventing the action from being run: "response error my_action F_memory_not". + - A response that designates the controller ran successfully: "response controller my_action F_success". + + When the control program fails to function due to some error, there will be no specially formatted message printed. + When the controller program fails before it can perform the requested action, then a "response error [action] [status]" formatted message is printed. + When the controller program successfully performs the action but the result of that action is an error, then a "response controller [action] F_failure" formatted message is printed. diff --git a/documents/readme.build.txt b/documents/readme.build.txt new file mode 100644 index 0000000..db613c4 --- /dev/null +++ b/documents/readme.build.txt @@ -0,0 +1,335 @@ +# fss-0002 iki-0000 +# +# license: cc-by-sa-4.0 +# version 2024/07/10 +# +# This file (assumed to be named readme.build.txt) can be more easily read using the following iki_read commands: +# iki_read readme.build.txt +Q -w -rr FLL FLL FSS FSS -WW character "'" "'" code '"' '"' +# +# To read the "Build Readme Documentation" section of this file, use this command sequence: +# fss_basic_list_read readme.build.txt +Q -cn "Build Readme Documentation" | iki_read +Q -w -rr FLL FLL FSS FSS -WW character "'" "'" code '"' '"' +# + +Build Readme Documentation: + The bold:"Featureless Make", or code:"fake", is a build system opposing the bold:"GNU Make" build (and install) system. + See the code:"fake" project for further details regarding that build system. + + Use the code:"make" operation from the code:"fake" program to build this project\: + code:"fake" + + If the project has already been built before, alternative consider performing both a code:"clean" and an explicit code:"make" operation\: + code:"fake clean make" + + After building, either run the helper file:"install.sh" script or manually install the build files. + + Install Example\: + code:"./install.sh" + + Manual Install Example\: + code:"cp -vR build/documentation/* /usr/share/" + code:"cp -vR build/includes/* /usr/include/" + code:"cp -vR build/libaries/shared/* /usr/lib/" + code:"cp -vR build/programs/shared/* /usr/bin/" + code:"cp -vR build/settings/* /etc/" + + Build Tree Structure Example (using an early code:"controller-0.7.0" project structure)\: + block:" + build/ + ├── documentation + │   └── man + │   ├── man1 + │   │   └── controller.1 + │   └── man5 + │   ├── controller-actions.5 + │   ├── controller-entry.5 + │   ├── controller-exit.5 + │   ├── controller-packet.5 + │   └── controller-rule.5 + ├── documents + ├── includes + │   └── program + │   └── controller + │   ├── controller + │   │   ├── controller.h + │   │   └── string.h + │   ├── init + │   │   ├── init.h + │   │   └── string.h + │   └── main + │   ├── common + │   │   ├── define + │   │   │   ├── control.h + │   │   │   ├── entry.h + │   │   │   ├── rule.h + │   │   │   └── thread.h + │   │   ├── define.h + │   │   ├── enumeration + │   │   │   ├── control.h + │   │   │   ├── entry.h + │   │   │   ├── instance.h + │   │   │   ├── process.h + │   │   │   ├── rule.h + │   │   │   └── thread.h + │   │   ├── enumeration.h + │   │   ├── print.h + │   │   ├── string + │   │   │   ├── general.h + │   │   │   └── rule.h + │   │   ├── string.h + │   │   ├── type + │   │   │   ├── cache.h + │   │   │   ├── control.h + │   │   │   ├── defs.h + │   │   │   ├── entry.h + │   │   │   ├── execute.h + │   │   │   ├── instance.h + │   │   │   ├── interrupt.h + │   │   │   ├── lock.h + │   │   │   ├── process.h + │   │   │   ├── rule.h + │   │   │   └── thread.h + │   │   └── type.h + │   ├── common.h + │   ├── controller.h + │   ├── convert.h + │   ├── entry + │   │   ├── action.h + │   │   ├── preprocess.h + │   │   ├── process.h + │   │   └── setting.h + │   ├── entry.h + │   ├── file.h + │   ├── instance + │   │   ├── prepare.h + │   │   └── wait.h + │   ├── instance.h + │   ├── lock.h + │   ├── path.h + │   ├── perform.h + │   ├── print + │   │   ├── data.h + │   │   ├── debug + │   │   │   ├── perform + │   │   │   │   ├── control.h + │   │   │   │   └── pid.h + │   │   │   └── rule + │   │   │   ├── action.h + │   │   │   └── execute.h + │   │   ├── debug.h + │   │   ├── error + │   │   │   ├── entry + │   │   │   │   ├── action.h + │   │   │   │   ├── item.h + │   │   │   │   └── setting.h + │   │   │   ├── entry.h + │   │   │   ├── lock.h + │   │   │   ├── perform + │   │   │   │   └── pid.h + │   │   │   ├── rule + │   │   │   │   ├── action.h + │   │   │   │   ├── item.h + │   │   │   │   └── setting.h + │   │   │   └── rule.h + │   │   ├── error.h + │   │   ├── lock.h + │   │   ├── message + │   │   │   ├── entry + │   │   │   │   ├── action.h + │   │   │   │   └── item.h + │   │   │   └── entry.h + │   │   ├── message.h + │   │   ├── output + │   │   │   ├── entry + │   │   │   │   └── setting.h + │   │   │   └── rule + │   │   │   ├── execute.h + │   │   │   ├── setting.h + │   │   │   └── validate.h + │   │   ├── verbose.h + │   │   ├── warning + │   │   │   ├── entry + │   │   │   │   ├── action.h + │   │   │   │   ├── item.h + │   │   │   │   └── setting.h + │   │   │   └── rule + │   │   │   ├── action.h + │   │   │   ├── item.h + │   │   │   └── setting.h + │   │   └── warning.h + │   ├── process.h + │   ├── rule + │   │   ├── action.h + │   │   ├── execute.h + │   │   ├── expand.h + │   │   ├── instance.h + │   │   ├── is.h + │   │   ├── item.h + │   │   ├── parameter.h + │   │   ├── read.h + │   │   ├── setting.h + │   │   └── wait.h + │   ├── rule.h + │   ├── signal.h + │   ├── status.h + │   ├── thread + │   │   ├── cleanup.h + │   │   ├── control.h + │   │   ├── entry.h + │   │   ├── instance.h + │   │   ├── is.h + │   │   ├── rule.h + │   │   └── signal.h + │   ├── thread.h + │   ├── time.h + │   └── validate.h + ├── libraries + │   ├── script + │   ├── shared + │   │   ├── libcontroller.so -> libcontroller.so.0 + │   │   ├── libcontroller.so.0 -> libcontroller.so.0.7 + │   │   ├── libcontroller.so.0.7 -> libcontroller.so.0.7.0 + │   │   └── libcontroller.so.0.7.0 + │   └── static + ├── objects + │   ├── script + │   ├── shared + │   └── static + ├── programs + │   ├── script + │   ├── shared + │   │   ├── controller + │   │   └── init + │   └── static + ├── settings + │   └── controller + │   ├── entries + │   │   ├── default.entry + │   │   └── maintenance.entry + │   ├── example + │   │   ├── cgroup_example + │   │   │   ├── entries + │   │   │   │   ├── chromium.entry + │   │   │   │   ├── eclipse.entry + │   │   │   │   ├── firefox.entry + │   │   │   │   └── setup_cgroups.entry + │   │   │   └── rules + │   │   │   ├── program + │   │   │   │   ├── chromium.rule + │   │   │   │   ├── eclipse.rule + │   │   │   │   └── firefox.rule + │   │   │   └── setup + │   │   │   └── cgroups.rule + │   │   ├── entries + │   │   │   ├── asynchronous.entry + │   │   │   ├── asynchronous-serial.entry + │   │   │   ├── delay-program.entry + │   │   │   ├── delay-service.entry + │   │   │   ├── environment.entry + │   │   │   ├── htop-alternate.entry + │   │   │   ├── htop-command.entry + │   │   │   ├── htop.entry + │   │   │   ├── iki.entry + │   │   │   ├── serial-alternate.entry + │   │   │   ├── serial.entry + │   │   │   ├── sshd.entry + │   │   │   ├── test.entry + │   │   │   ├── up.entry + │   │   │   └── utility.entry + │   │   ├── exits + │   │   │   ├── htop-alternate.exit + │   │   │   ├── serial.exit + │   │   │   └── sshd.exit + │   │   └── rules + │   │   ├── asynchronous + │   │   │   ├── sleep_10.rule + │   │   │   ├── sleep_1.rule + │   │   │   ├── sleep_2.rule + │   │   │   ├── sleep_3.rule + │   │   │   ├── sleep_5.rule + │   │   │   └── sleep_8.rule + │   │   ├── command + │   │   │   ├── htop.rule + │   │   │   └── multiple.rule + │   │   ├── delay + │   │   │   ├── long.rule + │   │   │   └── short.rule + │   │   ├── environment + │   │   │   ├── default.rule + │   │   │   ├── empty.rule + │   │   │   ├── exported.rule + │   │   │   ├── exporting.rule + │   │   │   ├── fake-nothing.rule + │   │   │   └── fake-something.rule + │   │   ├── maintenance + │   │   │   └── boom.rule + │   │   ├── print + │   │   │   └── newline.rule + │   │   ├── script + │   │   │   ├── create_socket_path.rule + │   │   │   ├── fail.rule + │   │   │   ├── iki.rule + │   │   │   ├── php.rule + │   │   │   ├── python.rule + │   │   │   ├── require_me.rule + │   │   │   └── succeed.rule + │   │   ├── serial + │   │   │   ├── s_1.rule + │   │   │   ├── s_2.rule + │   │   │   ├── s_3.rule + │   │   │   ├── s_4.rule + │   │   │   ├── s_5.rule + │   │   │   └── s_6.rule + │   │   ├── service + │   │   │   └── sshd.rule + │   │   └── utility + │   │   ├── sleeper_1.rule + │   │   ├── sleeper_2.rule + │   │   └── sleeper_3.rule + │   └── rules + │   ├── boot + │   │   ├── devices.rule + │   │   ├── file_system.rule + │   │   ├── modules.rule + │   │   ├── proc.rule + │   │   └── root.rule + │   ├── maintenance + │   │   └── console.rule + │   ├── net + │   │   ├── all.rule + │   │   └── loopback.rule + │   ├── service + │   │   ├── dbus.rule + │   │   ├── logger.rule + │   │   └── mouse.rule + │   ├── task + │   │   ├── clock.rule + │   │   ├── keyboard.rule + │   │   └── ntpdate.rule + │   └── terminal + │   ├── four.rule + │   ├── one.rule + │   ├── three.rule + │   └── two.rule + └── stage + ├── library_shared-settings.built + ├── program_shared-settings.controller.built + ├── program_shared-settings.init.built + ├── skeleton-settings.built + ├── skeleton-settings.controller.built + ├── skeleton-settings.init.built + ├── sources_documentation-settings.built + ├── sources_documentation-settings.controller.built + ├── sources_documentation-settings.init.built + ├── sources_headers-settings.built + ├── sources_headers-settings.controller.built + ├── sources_headers-settings.init.built + ├── sources_script-settings.built + ├── sources_script-settings.controller.built + ├── sources_script-settings.init.built + ├── sources_settings-settings.built + ├── sources_settings-settings.controller.built + └── sources_settings-settings.init.built + " + + See: FLL:"Featureless Linux Library" code:"fake" project documents and specifications for further details on how to use the bold:"Featureless Make" system. + See: FLL:"Featureless Linux Library" project documentation and specifications for how to configure the bold:"Featureless Make" FSS:"Featureless Settings Specification" files. diff --git a/documents/readme.txt b/documents/readme.txt new file mode 100644 index 0000000..5a97291 --- /dev/null +++ b/documents/readme.txt @@ -0,0 +1,27 @@ +# fss-0002 iki-0000 +# +# license: cc-by-sa-4.0 +# version 2024/07/10 +# +# This file (assumed to be named readme.build.txt) can be more easily read using the following iki_read commands: +# iki_read readme.txt +Q -w -r FLL FLL +# +# To read the "Readme Documentation" section of this file, use this command sequence: +# fss_basic_list_read readme.txt +Q -cn "Readme Documentation" | iki_read +Q -w -r FLL FLL +# + +Readme Documentation: + The Control program utilizes the FLL:"Featureless Linux Library" and is designed to be used for bold:"Linux" systems. + + This project follows many of the FLL:"Featureless Linux Library" project practices and the documentation provided by that project should be consulted for further details in this regard. + + The purpose of this project is to provide a tool to communicate with the Controller program. + This can also interact with the bold:"init" program that is an alternative build variation of the Controller program. + + This project follows bold:"Specification Driven Development" and details of the configuration files are defined in the directory:"specifications/" and in the directory:"documents/". + + To facilitate building of this project, these build systems are provided\: + - The bold:"Featureless Make", which is a level 3 project provided by FLL:"Featureless Linux Library". + + See: file:"data/build/dependencies" for specific dependencies of this project. + See: file:"documents/readme.build.txt" for bold:"Featureless Make" compiling and notes on installing. diff --git a/documents/settings.txt b/documents/settings.txt new file mode 100644 index 0000000..9a14391 --- /dev/null +++ b/documents/settings.txt @@ -0,0 +1,65 @@ +# fss-0002 +# +# license: open-standard-license-1.0-or-later +# version 2024/07/02 +# + +Settings Documentation: + This describes intent and purposes of the control settings file. + + The settings file provides default or system-wide settings. + The system-wide settings file is loaded by default but a custom settings file may be alternatively specified. + Using this avoids the need to add additional paramters to the command line when calling the control program. + + When this file is not specified any hardcoded defaults compiled into the program are used. + + The location of the settings file is compile time specific and should be changed depending on the particular design of the system. + The default path for the control settings file is something like "/etc/control/settings". + + - name_socket\: + This represents the file name used to construct the full socket path. + The file name represents the name of the file but any file extensions, such as ".suffix", should likely use "path_socket_suffix" to specify the file extension. + + When not defined the compiled in default is used. + The default socket path directory is generally "default" but this could be changed during compile time. + + This is required to not be empty so when the Object "path_socket" is defined without any Content, then an error is expected to be thrown. + + This is used along with the "path_socket", "path_socket_prefix", and the "path_socket_suffix" to construct the socket file. + + - path_socket\: + This represents the directory path to the socket file provided by the controller or init service. + This directory path is separate from the file name part so that the name can be more dynamically constructed without having to specify a full directory path each time. + + When not defined the compiled in default is used. + The default socket path directory is generally "/var/run/controller" but this could be changed during compile time. + + When the Object "path_socket" is defined without any Content, then no path is added (resulting in the socket relative to the callers current working directory). + + This is used along with the "path_socket_prefix", the "path_socket_suffix", and the "name_socket" to construct the socket file. + A full socket path might look something like "/var/run/controller/controller-default.socket". + + It is common for the controller program to be compiled as an init program. + In this case it may be common for the full socket path to instead be something more like "/var/run/init/init-default.socket". + + - path_socket_prefix\: + This represents a prefix used to construct the full socket path. + This prefix is prepended to the socket file name. + + When not defined the compiled in default is used. + The default path prefix is generally "controller-" but this could be changed during compile time. + + When the Object "path_socket_prefix" is defined without any Content, then no prefix is prepended. + + This is used along with the "path_socket", the "path_socket_suffix", and the "name_socket" to construct the socket file. + + - path_socket_suffix\: + This represents a suffix used to construct the full socket path. + This suffix is appended to the socket file name. + + When not defined the compiled in default is used. + The default path suffix is generally ".socket" but this could be changed during compile time. + + When the Object "path_socket_suffix" is defined without any Content, then no suffix is appended. + + This is used along with the "path_socket", the "path_socket_prefix", and the "name_socket" to construct the socket file. diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..7e4db0c --- /dev/null +++ b/install.sh @@ -0,0 +1,880 @@ +#!/bin/bash +# license: lgpl-2.1-or-later +# programmer: Kevin Day +# +# The purpose of this script is to provide a simple installer tool to copy any part of the FLL project onto a system. +# This assumes the destination directories already exist and does not attempt to create them. +# Ideally, the package manager of the system should be used, but this is provided as a guide or a fallback. +# Settings files are not copied over, but a warning will be printed to inform the installer of their existence. +# +# The dependencies of this script are: bash, grep, and sed. +# +# This script can also be run under zsh rather than bash by setting the environment variable SHELL_ENGINE to "zsh", such as: +# SHELL_ENGINE="zsh" zsh ./install.sh --help +# + +install_main() { + + if [[ ${SHELL_ENGINE} == "zsh" ]] ; then + emulate ksh + fi + + local public_name="Simple FLL Project Install Script" + local system_name=install + local called_name=$(basename ${0}) + local version=0.7.0 + + local grab_next= + local do_color=dark + local do_help= + local do_copyright= + local i=0 + local p= + local t=0 + local key= + + local c_reset="\\033[0m" + local c_title="\\033[1;33m" + local c_error="\\033[1;31m" + local c_warning="\\033[0;33m" + local c_highlight="\\033[1;32m" + local c_notice="\\033[0;01m" + local c_important="\\033[0;32m" + local c_subtle="\\033[1;30m" + local c_prefix="\\" + + local failure=0 + local operation= + local operation_failure= + local verbosity=normal + local verbose= + local verbose_common= + + local path_build=build/ + local path_documentation=documentation/ + local path_programs=programs/ + local path_includes=includes/ + local path_libraries=libraries/ + local path_settings=settings/ + local path_static=static/ + local path_shared=shared/ + + local destination_documentation=share/ + local destination_prefix=/usr/local/ + local destination_programs=bin/ + local destination_includes=include/ + local destination_libraries=lib/ + local destination_libraries_static= + local destination_libraries_shared= + local destination_programs_static= + local destination_programs_shared= + local destination_settings=etc/ + + local work= + + local enable_documentation="yes" + local enable_settings="yes" + local enable_shared="yes" + local enable_shared_programs="yes" + local enable_shared_libraries="yes" + local enable_static="yes" + local enable_static_programs="yes" + local enable_static_libraries="yes" + local enable_includes="yes" + + if [[ $# -gt 0 ]] ; then + t=$# + + while [[ ${i} -lt ${t} ]] ; do + let i=${i}+1 + + if [[ ${SHELL_ENGINE} == "zsh" ]] ; then + p=${(P)i} + else + p=${!i} + fi + + if [[ ${grab_next} == "" ]] ; then + if [[ ${p} == "-h" || ${p} == "--help" ]] ; then + do_help=yes + elif [[ ${p} == "+C" || ${p} == "++copyright" ]] ; then + do_copyright="yes" + elif [[ ${p} == "+d" || ${p} == "++dark" ]] ; then + do_color="dark" + context="+d" + elif [[ ${p} == "+l" || ${p} == "++light" ]] ; then + do_color="light" + context="+l" + elif [[ ${p} == "+n" || ${p} == "++no_color" ]] ; then + do_color=none + context="+n" + elif [[ ${p} == "+Q" || ${p} == "++quiet" ]] ; then + verbosity="quiet" + verbose="+Q" + verbose_common= + elif [[ ${p} == "+N" || ${p} == "++normal" ]] ; then + verbosity= + verbose="+N" + verbose_common= + elif [[ ${p} == "+E" || ${p} == "++error" ]] ; then + verbosity="error" + verbose="+E" + verbose_common= + elif [[ ${p} == "+V" || ${p} == "++verbose" ]] ; then + verbosity="verbose" + verbose="+V" + verbose_common="-v" + elif [[ ${p} == "+D" || ${p} == "++debug" ]] ; then + verbosity="debug" + verbose="+D" + verbose_common="-v" + elif [[ ${p} == "+v" || ${p} == "++version" ]] ; then + echo ${version} + return 0 + elif [[ ${p} == "-b" || ${p} == "--build" ]] ; then + grab_next=path_build + elif [[ ${p} == "-P" || ${p} == "--prefix" ]] ; then + grab_next=prefix + elif [[ ${p} == "-B" || ${p} == "--bindir" ]] ; then + grab_next=bindir + elif [[ ${p} == "-D" || ${p} == "--docdir" ]] ; then + grab_next=docdir + elif [[ ${p} == "-E" || ${p} == "--etcdir" ]] ; then + grab_next=etcdir + elif [[ ${p} == "-I" || ${p} == "--includedir" ]] ; then + grab_next=includedir + elif [[ ${p} == "-L" || ${p} == "--libdir" ]] ; then + grab_next=libdir + elif [[ ${p} == "-w" || ${p} == "--work" ]] ; then + grab_next=work + elif [[ ${p} == "--enable-doc" ]] ; then + enable_documentation="yes" + elif [[ ${p} == "--disable-doc" ]] ; then + enable_documentation="no" + elif [[ ${p} == "--enable-settings" ]] ; then + enable_settings="yes" + elif [[ ${p} == "--disable-settings" ]] ; then + enable_settings="no" + elif [[ ${p} == "--enable-shared" ]] ; then + enable_shared="yes" + elif [[ ${p} == "--disable-shared" ]] ; then + enable_shared="no" + elif [[ ${p} == "--disable-shared-programs" ]] ; then + enable_shared_programs="no" + elif [[ ${p} == "--disable-shared-libraries" ]] ; then + enable_shared_libraries="no" + elif [[ ${p} == "--disable-static-programs" ]] ; then + enable_static_programs="no" + elif [[ ${p} == "--disable-static-libraries" ]] ; then + enable_static_libraries="no" + elif [[ ${p} == "--enable-static" ]] ; then + enable_static="yes" + elif [[ ${p} == "--disable-static" ]] ; then + enable_static="no" + elif [[ ${p} == "--enable-includes" ]] ; then + enable_includes="yes" + elif [[ ${p} == "--disable-includes" ]] ; then + enable_includes="no" + elif [[ ${p} == "--libraries-static" ]] ; then + grab_next="destination_libraries_static" + elif [[ ${p} == "--libraries-shared" ]] ; then + grab_next="destination_libraries_shared" + elif [[ ${p} == "--programs-static" ]] ; then + grab_next="destination_programs_static" + elif [[ ${p} == "--programs-shared" ]] ; then + grab_next="destination_programs_shared" + elif [[ ${operation_failure} == "" ]] ; then + operation="${p}" + operation_failure=fail-unsupported + fi + else + if [[ ${grab_next} == "path_build" ]] ; then + path_build=$(echo ${p} | sed -e 's|^//*|/|' -e 's|/*$|/|') + elif [[ ${grab_next} == "prefix" ]] ; then + destination_prefix=$(echo ${p} | sed -e 's|^//*|/|' -e 's|/*$|/|') + elif [[ ${grab_next} == "bindir" ]] ; then + destination_programs=$(echo ${p} | sed -e 's|^//*|/|' -e 's|/*$|/|') + elif [[ ${grab_next} == "docdir" ]] ; then + destination_documentation=$(echo ${p} | sed -e 's|^//*|/|' -e 's|/*$|/|') + elif [[ ${grab_next} == "etcdir" ]] ; then + destination_settings=$(echo ${p} | sed -e 's|^//*|/|' -e 's|/*$|/|') + elif [[ ${grab_next} == "includedir" ]] ; then + destination_includes=$(echo ${p} | sed -e 's|^//*|/|' -e 's|/*$|/|') + elif [[ ${grab_next} == "libdir" ]] ; then + destination_libraries=$(echo ${p} | sed -e 's|^//*|/|' -e 's|/*$|/|') + elif [[ ${grab_next} == "work" ]] ; then + work=$(echo ${p} | sed -e 's|^//*|/|' -e 's|/*$|/|') + elif [[ ${grab_next} == "destination_libraries_static" ]] ; then + destination_libraries_static=$(echo ${p} | sed -e 's|^//*|/|' -e 's|/*$|/|') + elif [[ ${grab_next} == "destination_libraries_shared" ]] ; then + destination_libraries_shared=$(echo ${p} | sed -e 's|^//*|/|' -e 's|/*$|/|') + elif [[ ${grab_next} == "destination_programs_static" ]] ; then + destination_programs_static=$(echo ${p} | sed -e 's|^//*|/|' -e 's|/*$|/|') + elif [[ ${grab_next} == "destination_programs_shared" ]] ; then + destination_programs_shared=$(echo ${p} | sed -e 's|^//*|/|' -e 's|/*$|/|') + fi + + grab_next= + fi + done + + p= + fi + + install_handle_colors + + if [[ ${do_help} == "yes" ]] ; then + install_help + install_cleanup + + return 0 + fi + + if [[ ${do_copyright} == "yes" ]] ; then + install_copyright + install_cleanup + + return 0 + fi + + if [[ ${operation_failure} == "fail-unsupported" ]] ; then + if [[ ${verbosity} != "quiet" ]] ; then + echo -e "${c_error}ERROR: The operation ${c_notice}${operation}${c_error} was not recognized.${c_reset}" + fi + + install_cleanup + + return 1 + fi + + if [[ ! -d ${path_build} ]] ; then + if [[ ${verbosity} != "quiet" ]] ; then + echo -e "${c_error}ERROR: The build path ${c_notice}${path_build}${c_error} is not a valid directory.${c_reset}" + fi + + install_cleanup + + return 1 + fi + + if [[ ${work} == "" && ${destination_prefix} != "" && ! -d ${destination_prefix} ]] ; then + if [[ ${verbosity} != "quiet" ]] ; then + echo -e "${c_error}ERROR: The destination prefix ${c_notice}${destination_prefix}${c_error} is not a valid directory.${c_reset}" + fi + + install_cleanup + + return 1 + fi + + if [[ ${destination_prefix} != "" ]] ; then + if [[ $(echo ${destination_documentation} | grep -o '^/') == "" ]] ; then + destination_documentation="${destination_prefix}${destination_documentation}" + fi + + if [[ $(echo ${destination_programs} | grep -o '^/') == "" ]] ; then + destination_programs="${destination_prefix}${destination_programs}" + fi + + if [[ $(echo ${destination_includes} | grep -o '^/') == "" ]] ; then + destination_includes="${destination_prefix}${destination_includes}" + fi + + if [[ $(echo ${destination_libraries} | grep -o '^/') == "" ]] ; then + destination_libraries="${destination_prefix}${destination_libraries}" + fi + + if [[ $(echo ${destination_settings} | grep -o '^/') == "" ]] ; then + destination_settings="${destination_prefix}${destination_settings}" + fi + fi + + if [[ ${destination_libraries_static} != "" ]] ; then + if [[ $(echo ${destination_libraries_static} | grep -o '^/') == "" ]] ; then + destination_libraries_static=${destination_libraries}${destination_libraries_static} + fi + else + destination_libraries_static=${destination_libraries} + fi + + if [[ ${destination_libraries_shared} != "" ]] ; then + if [[ $(echo ${destination_libraries_shared} | grep -o '^/') == "" ]] ; then + destination_libraries_shared=${destination_libraries}${destination_libraries_shared} + fi + else + destination_libraries_shared=${destination_libraries} + fi + + if [[ ${destination_programs_static} != "" ]] ; then + if [[ $(echo ${destination_programs_static} | grep -o '^/') == "" ]] ; then + destination_programs_static=${destination_programs}${destination_programs_static} + fi + else + destination_programs_static=${destination_programs} + fi + + if [[ ${destination_programs_shared} != "" ]] ; then + if [[ $(echo ${destination_programs_shared} | grep -o '^/') == "" ]] ; then + destination_programs_shared=${destination_programs}${destination_programs_shared} + fi + else + destination_programs_shared=${destination_programs} + fi + + if [[ ${work} != "" && ! -d ${work} ]] ; then + if [[ ${verbosity} != "quiet" ]] ; then + echo -e "${c_error}ERROR: The work directory ${c_notice}${work}${c_error} is not a valid directory.${c_reset}" + fi + + install_cleanup + + return 1 + fi + + if [[ ${work} == "" && -e ${destination_programs} && ! -d ${destination_programs} ]] ; then + if [[ ${verbosity} != "quiet" ]] ; then + echo -e "${c_error}ERROR: The destination bindir ${c_notice}${destination_programs}${c_error} is not a valid directory.${c_reset}" + fi + + install_cleanup + + return 1 + fi + + if [[ ${work} == "" && -e ${destination_programs_static} && ! -d ${destination_programs_static} ]] ; then + if [[ ${verbosity} != "quiet" ]] ; then + echo -e "${c_error}ERROR: The destination (${c_notice}static${c_error}) bindir ${c_notice}${destination_programs_static}${c_error} is not a valid directory.${c_reset}" + fi + + install_cleanup + + return 1 + fi + + if [[ ${work} == "" && -e ${destination_programs_shared} && ! -d ${destination_programs_shared} ]] ; then + if [[ ${verbosity} != "quiet" ]] ; then + echo -e "${c_error}ERROR: The destination (${c_notice}shared${c_error}) bindir ${c_notice}${destination_programs_shared}${c_error} is not a valid directory.${c_reset}" + fi + + install_cleanup + + return 1 + fi + + if [[ ${work} == "" && -e ${destination_includes} && ! -d ${destination_includes} ]] ; then + if [[ ${verbosity} != "quiet" ]] ; then + echo -e "${c_error}ERROR: The destination incluedir ${c_notice}${destination_includes}${c_error} is not a valid directory.${c_reset}" + fi + + install_cleanup + + return 1 + fi + + if [[ ${work} == "" && -e ${destination_libraries_static} && ! -d ${destination_libraries_static} ]] ; then + if [[ ${verbosity} != "quiet" ]] ; then + echo -e "${c_error}ERROR: The destination (${c_notice}static${c_error}) libdir ${c_notice}${destination_libraries_static}${c_error} is not a valid directory.${c_reset}" + fi + + install_cleanup + + return 1 + fi + + if [[ ${work} == "" && -e ${destination_libraries_shared} && ! -d ${destination_libraries_shared} ]] ; then + if [[ ${verbosity} != "quiet" ]] ; then + echo -e "${c_error}ERROR: The destination (${c_notice}shared${c_error}) libdir ${c_notice}${destination_libraries_shared}${c_error} is not a valid directory.${c_reset}" + fi + + install_cleanup + + return 1 + fi + + install_perform_install + + install_cleanup + + if [[ ${failure} -eq 1 ]] ; then + return 1 + fi + + return 0 +} + +install_handle_colors() { + + if [[ ${do_color} == "light" ]] ; then + c_error="\\033[1;31m" + c_warning="\\033[0;31m" + c_title="\\033[1;34m" + c_highlight="\\033[0;34m" + c_notice="\\033[0;01m" + c_important="\\033[0;35m" + elif [[ ${do_color} == "none" ]] ; then + c_reset= + c_title= + c_error= + c_warning= + c_highlight= + c_notice= + c_important= + c_subtle= + c_prefix= + fi +} + +install_help() { + + echo -e "${c_title}${public_name}${c_reset}" + echo -e " ${c_notice}Version ${version}${c_reset}" + echo + echo -e "${c_highlight}${system_name}${c_reset} ${c_notice}[${c_reset} options ${c_notice}]${c_reset}" + echo + echo -e "${c_highlight}Options:${c_reset}" + echo -e " -${c_important}h${c_reset}, --${c_important}help${c_reset} Print this help message." + echo -e " +${c_important}C${c_reset}, ++${c_important}copyright${c_reset} Print the copyright." + echo -e " +${c_important}d${c_reset}, ++${c_important}dark${c_reset} Output using colors that show up better on dark backgrounds." + echo -e " +${c_important}l${c_reset}, ++${c_important}light${c_reset} Output using colors that show up better on light backgrounds." + echo -e " +${c_important}n${c_reset}, ++${c_important}no_color${c_reset} Do not print using color." + echo -e " +${c_important}Q${c_reset}, ++${c_important}quiet${c_reset} Decrease verbosity, silencing most print.to." + echo -e " +${c_important}E${c_reset}, ++${c_important}error${c_reset} Decrease verbosity, using only error print.to." + echo -e " +${c_important}N${c_reset}, ++${c_important}normal${c_reset} Set verbosity to normal." + echo -e " +${c_important}V${c_reset}, ++${c_important}verbose${c_reset} Increase verbosity beyond normal print.to." + echo -e " +${c_important}D${c_reset}, ++${c_important}debug${c_reset} Enable debugging, significantly increasing verbosity beyond normal print.to." + echo -e " +${c_important}v${c_reset}, ++${c_important}version${c_reset} Print only the version number." + echo + echo -e "${c_highlight}Install Options:${c_reset}" + echo -e " -${c_important}b${c_reset}, --${c_important}build${c_reset} Custom build directory." + echo -e " -${c_important}P${c_reset}, --${c_important}prefix${c_reset} Custom destination prefix." + echo -e " -${c_important}B${c_reset}, --${c_important}bindir${c_reset} Custom destination bin/ directory." + echo -e " -${c_important}D${c_reset}, --${c_important}docdir${c_reset} Custom destination share/ directory (documentation directory)." + echo -e " -${c_important}E${c_reset}, --${c_important}etcdir${c_reset} Custom destination etc/ directory (settings directory)." + echo -e " -${c_important}I${c_reset}, --${c_important}includedir${c_reset} Custom destination include/ directory." + echo -e " -${c_important}L${c_reset}, --${c_important}libdir${c_reset} Custom destination lib/ directory." + echo -e " -${c_important}w${c_reset}, --${c_important}work${c_reset} Install to this work directory using a 'working' directory structure." + echo + echo -e "${c_highlight}Special Options:${c_reset}" + echo -e " --${c_important}enable-doc${c_reset} Forcibly do install documentation files." + echo -e " --${c_important}disable-doc${c_reset} Forcibly do not install documentation files." + echo -e " --${c_important}enable-settings${c_reset} Forcibly do install settings files." + echo -e " --${c_important}disable-settings${c_reset} Forcibly do not install settings files." + echo -e " --${c_important}enable-shared${c_reset} Forcibly do install shared files." + echo -e " --${c_important}disable-shared${c_reset} Forcibly do not install shared files." + echo -e " --${c_important}disable-shared-programs${c_reset} Forcibly do not install shared programs." + echo -e " --${c_important}disable-shared-libraries${c_reset} Forcibly do not install shared libraries." + echo -e " --${c_important}enable-static${c_reset} Forcibly do install static files." + echo -e " --${c_important}disable-static${c_reset} Forcibly do not install static files." + echo -e " --${c_important}disable-static-programs${c_reset} Forcibly do not install shared programs." + echo -e " --${c_important}disable-static-libraries${c_reset} Forcibly do not install shared libraries." + echo -e " --${c_important}enable-includes${c_reset} Forcibly do not install include files." + echo -e " --${c_important}disable-includes${c_reset} Forcibly do not install include files." + echo -e " --${c_important}libraries-static${c_reset} Custom destination for static libraries." + echo -e " --${c_important}libraries-shared${c_reset} Custom destination for shared libraries." + echo -e " --${c_important}programs-static${c_reset} Custom destination for static programs." + echo -e " --${c_important}programs-shared${c_reset} Custom destination for shared programs." +} + +install_copyright() { + + echo "Copyright © 2007-2024 Kevin Day." + echo + echo "Source code license lgpl-2.1-or-later." + echo "Standard and specification license open-standard-license-1.0-or-later." + echo "Documentation license cc-by-sa-4.0." +} + +install_perform_install() { + local key= + local i= + local path= + local message= + + if [[ ${enable_shared} == "no" ]] ; then + enable_shared_programs="no" + enable_shared_libraries="no" + fi + + if [[ ${enable_static} == "no" ]] ; then + enable_static_programs="no" + enable_static_libraries="no" + fi + + if [[ ${work} == "" ]] ; then + message="install destination directory" + else + message="work directory" + destination_prefix=${work} + destination_documentation=${work}documentation/ + destination_programs=${work}programs/ + destination_programs_static=${destination_programs}static/ + destination_programs_shared=${destination_programs}shared/ + destination_includes=${work}includes/ + destination_libraries=${work}libraries/ + destination_libraries_static=${destination_libraries}static/ + destination_libraries_shared=${destination_libraries}shared/ + destination_settings=${work}settings/ + fi + + if [[ ! -d ${destination_prefix} ]] ; then + mkdir ${verbose_common} ${destination_prefix} + + if [[ ${?} -ne 0 ]] ; then + if [[ ${verbosity} != "quiet" ]] ; then + echo -e "${c_error}ERROR: Failed to create install ${message} ${c_notice}${destination_prefix}${c_error}.${c_reset}" + fi + + let failure=1 + fi + fi + + if [[ ${enable_shared_programs} == "yes" || ${enable_static_programs} == "yes" ]] ; then + if [[ -d ${path_build}${path_programs} && ! -d ${destination_programs} ]] ; then + mkdir ${verbose_common} ${destination_programs} + + if [[ ${?} -ne 0 ]] ; then + if [[ ${verbosity} != "quiet" ]] ; then + echo -e "${c_error}ERROR: Failed to create install ${message} ${c_notice}${destination_programs}${c_error}.${c_reset}" + fi + + let failure=1 + fi + fi + + if [[ ${enable_shared_programs} == "yes" && -d ${path_build}${path_programs}${path_shared} && ! -d ${destination_programs_shared} ]] ; then + mkdir ${verbose_common} ${destination_programs_shared} + + if [[ ${?} -ne 0 ]] ; then + if [[ ${verbosity} != "quiet" ]] ; then + echo -e "${c_error}ERROR: Failed to create install ${message} ${c_notice}${destination_programs_shared}${c_error}.${c_reset}" + fi + + let failure=1 + fi + fi + + if [[ ${enable_static_programs} == "yes" && -d ${path_build}${path_programs}${path_static} && ! -d ${destination_programs_static} ]] ; then + mkdir ${verbose_common} ${destination_programs_static} + + if [[ ${?} -ne 0 ]] ; then + if [[ ${verbosity} != "quiet" ]] ; then + echo -e "${c_error}ERROR: Failed to create install ${message} ${c_notice}${destination_programs_static}${c_error}.${c_reset}" + fi + + let failure=1 + fi + fi + fi + + if [[ ${enable_shared_libraries} == "yes" || ${enable_static_libraries} == "yes" ]] ; then + if [[ -d ${path_build}${path_libraries} && ! -d ${destination_libraries} ]] ; then + mkdir ${verbose_common} ${destination_libraries} + + if [[ ${?} -ne 0 ]] ; then + if [[ ${verbosity} != "quiet" ]] ; then + echo -e "${c_error}ERROR: Failed to create install ${message} ${c_notice}${destination_libraries}${c_error}.${c_reset}" + fi + + let failure=1 + fi + fi + + if [[ ${enable_shared_libraries} == "yes" && -d ${path_build}${path_libraries}${path_shared} && ! -d ${destination_libraries_shared} ]] ; then + mkdir ${verbose_common} ${destination_libraries_shared} + + if [[ ${?} -ne 0 ]] ; then + if [[ ${verbosity} != "quiet" ]] ; then + echo -e "${c_error}ERROR: Failed to create ${message} ${c_notice}${destination_libraries_shared}${c_error}.${c_reset}" + fi + + let failure=1 + fi + fi + + if [[ ${enable_static_libraries} == "yes" && -d ${path_build}${path_libraries}${path_static} && ! -d ${destination_libraries_static} ]] ; then + mkdir ${verbose_common} ${destination_libraries_static} + + if [[ ${?} -ne 0 ]] ; then + if [[ ${verbosity} != "quiet" ]] ; then + echo -e "${c_error}ERROR: Failed to create ${message} ${c_notice}${destination_libraries_static}${c_error}.${c_reset}" + fi + + let failure=1 + fi + fi + fi + + if [[ ${enable_includes} == "yes" ]] ; then + if [[ -d ${path_build}${path_includes} && ! -d ${destination_includes} ]] ; then + mkdir ${verbose_common} ${destination_includes} + + if [[ ${?} -ne 0 ]] ; then + if [[ ${verbosity} != "quiet" ]] ; then + echo -e "${c_error}ERROR: Failed to create ${message} ${c_notice}${destination_includes}${c_error}.${c_reset}" + fi + + let failure=1 + fi + fi + fi + + if [[ ${enable_documentation} == "yes" ]] ; then + if [[ -d ${path_build}${path_documentation} && ! -d ${destination_documentation} ]] ; then + mkdir ${verbose_common} ${destination_documentation} + + if [[ ${?} -ne 0 ]] ; then + if [[ ${verbosity} != "quiet" ]] ; then + echo -e "${c_error}ERROR: Failed to create ${message} ${c_notice}${destination_documentation}${c_error}.${c_reset}" + fi + + let failure=1 + fi + fi + fi + + if [[ ${enable_settings} == "yes" ]] ; then + if [[ -d ${path_build}${path_settings} && ! -d ${destination_settings} ]] ; then + mkdir ${verbose_common} ${destination_settings} + + if [[ ${?} -ne 0 ]] ; then + if [[ ${verbosity} != "quiet" ]] ; then + echo -e "${c_error}ERROR: Failed to create ${message} ${c_notice}${destination_settings}${c_error}.${c_reset}" + fi + + let failure=1 + fi + fi + fi + + if [[ ${failure} -eq 0 && -d ${path_build}${path_includes} && ${enable_includes} == "yes" ]] ; then + for i in ${path_build}${path_includes}* ; do + + file=$(echo ${i} | sed -e "s|^${path_build}${path_includes}||") + + break + done + + if [[ ${file} == "*" && ! -f "${path_build}${path_includes}*" ]] ; then + file= + fi + + if [[ ${file} != "" ]] ; then + if [[ ${verbosity} != "quiet" && ${verbosity} != "error" ]] ; then + echo + echo -e "${c_highlight}Installing Includes to: ${c_reset}${c_notice}${destination_includes}${c_reset}${c_highlight}.${c_reset}" + fi + + cp ${verbose_common} -R ${path_build}${path_includes}* ${destination_includes} + + if [[ ${?} -ne 0 ]] ; then + if [[ ${verbosity} != "quiet" ]] ; then + echo -e "${c_error}ERROR: Failed to copy include files from ${c_notice}${path_build}${path_includes}${c_error} to ${c_notice}${destination_includes}${c_error}.${c_reset}" + fi + + let failure=1 + fi + fi + fi + + if [[ ${failure} -eq 0 && -d ${path_build}${path_libraries} && ( ${enable_shared_libraries} == "yes" || ${enable_static_libraries} == "yes" ) ]] ; then + if [[ -d ${path_build}${path_libraries}${path_static} && ${enable_static_libraries} == "yes" ]] ; then + for i in ${path_build}${path_libraries}${path_static}* ; do + + file=$(echo ${i} | sed -e "s|^${path_build}${path_libraries}${path_static}||") + + break + done + + if [[ ${file} == "*" && ! -f "${path_build}${path_libraries}${path_static}*" ]] ; then + file= + fi + + if [[ ${file} != "" ]] ; then + if [[ ${verbosity} != "quiet" && ${verbosity} != "error" ]] ; then + echo + echo -e "${c_highlight}Installing (${c_notice}static${c_highlight}) Libraries to: ${c_reset}${c_notice}${destination_libraries_static}${c_reset}${c_highlight}.${c_reset}" + fi + + cp ${verbose_common} -R ${path_build}${path_libraries}${path_static}* ${destination_libraries_static} + + if [[ ${?} -ne 0 ]] ; then + if [[ ${verbosity} != "quiet" ]] ; then + echo -e "${c_error}ERROR: Failed to copy (${c_notice}static${c_error}) library files from ${c_notice}${path_build}${path_libraries}${path_static}${c_error} to ${c_notice}${destination_libraries_static}${c_error}.${c_reset}" + fi + + let failure=1 + fi + fi + fi + + if [[ ${failure} -eq 0 && -d ${path_build}${path_libraries}${path_shared} && ${enable_shared_libraries} == "yes" ]] ; then + for i in ${path_build}${path_libraries}${path_shared}* ; do + + file=$(echo ${i} | sed -e "s|^${path_build}${path_libraries}${path_shared}||") + + break + done + + if [[ ${file} == "*" && ! -f "${path_build}${path_libraries}${path_shared}*" ]] ; then + file= + fi + + if [[ ${file} != "" ]] ; then + if [[ ${verbosity} != "quiet" && ${verbosity} != "error" ]] ; then + echo + echo -e "${c_highlight}Installing (${c_notice}shared${c_highlight}) Libraries to: ${c_reset}${c_notice}${destination_libraries_shared}${c_reset}${c_highlight}.${c_reset}" + fi + + cp ${verbose_common} -R ${path_build}${path_libraries}${path_shared}* ${destination_libraries_shared} + + if [[ ${?} -ne 0 ]] ; then + if [[ ${verbosity} != "quiet" ]] ; then + echo -e "${c_error}ERROR: Failed to copy (${c_notice}shared${c_error}) library files from ${c_notice}${path_build}${path_libraries}${path_shared}${c_error} to ${c_notice}${destination_libraries_shared}${c_error}.${c_reset}" + fi + + let failure=1 + fi + fi + fi + fi + + if [[ ${failure} -eq 0 && -d ${path_build}${path_programs} && ( ${enable_shared_programs} == "yes" || ${enable_static_programs} == "yes" ) ]] ; then + if [[ -d ${path_build}${path_programs}${path_static} && ${enable_static_programs} == "yes" ]] ; then + for i in ${path_build}${path_programs}${path_static}* ; do + + file=$(echo ${i} | sed -e "s|^${path_build}${path_programs}${path_static}||") + + break + done + + if [[ ${file} == "*" && ! -f "${path_build}${path_programs}${path_static}*" ]] ; then + file= + fi + + if [[ ${file} != "" && ${enable_static_programs} == "yes" ]] ; then + if [[ ${verbosity} != "quiet" && ${verbosity} != "error" ]] ; then + echo + echo -e "${c_highlight}Installing (${c_notice}static${c_highlight}) Programs to: ${c_reset}${c_notice}${destination_programs_static}${c_reset}${c_highlight}.${c_reset}" + fi + + cp ${verbose_common} -R ${path_build}${path_programs}${path_static}* ${destination_programs_static} + + if [[ ${?} -ne 0 ]] ; then + if [[ ${verbosity} != "quiet" ]] ; then + echo -e "${c_error}ERROR: failed to copy (${c_notice}static${c_error}) program files from ${c_notice}${path_build}${path_programs}${path_static}${c_error} to ${c_notice}${destination_programs_static}${c_error}.${c_reset}" + fi + + let failure=1 + fi + fi + fi + + if [[ ${failure} -eq 0 && -d ${path_build}${path_programs}${path_shared} && ${enable_shared_programs} == "yes" ]] ; then + for i in ${path_build}${path_programs}${path_shared}* ; do + + file=$(echo ${i} | sed -e "s|^${path_build}${path_programs}${path_shared}||") + + break + done + + if [[ ${file} == "*" && ! -f "${path_build}${path_programs}${path_shared}*" ]] ; then + file= + fi + + if [[ ${file} != "" ]] ; then + if [[ ${verbosity} != "quiet" && ${verbosity} != "error" ]] ; then + echo + echo -e "${c_highlight}Installing (${c_notice}shared${c_highlight}) Programs to: ${c_reset}${c_notice}${destination_programs_shared}${c_reset}${c_highlight}.${c_reset}" + fi + + cp ${verbose_common} -R ${path_build}${path_programs}${path_shared}* ${destination_programs_shared} + + if [[ ${?} -ne 0 ]] ; then + if [[ ${verbosity} != "quiet" ]] ; then + echo -e "${c_error}ERROR: failed to copy (${c_notice}shared${c_error}) program files from ${c_notice}${path_build}${path_programs}${path_shared}${c_error} to ${c_notice}${destination_programs_shared}${c_error}.${c_reset}" + fi + + let failure=1 + fi + fi + fi + fi + + if [[ ${failure} -eq 0 && -d ${path_build}${path_settings} && ${enable_settings} == "yes" ]] ; then + for i in ${path_build}${path_settings}* ; do + + file=$(echo ${i} | sed -e "s|^${path_build}${path_settings}||") + + break + done + + if [[ ${file} == "*" && ! -f "${path_build}${path_settings}*" ]] ; then + file= + fi + + if [[ ${file} != "" ]] ; then + if [[ ${verbosity} != "quiet" && ${verbosity} != "error" ]] ; then + echo + echo -e "${c_highlight}Installing Settings to: ${c_reset}${c_notice}${destination_settings}${c_reset}${c_highlight}.${c_reset}" + fi + + cp ${verbose_common} -R ${path_build}${path_settings}* ${destination_settings} + + if [[ ${?} -ne 0 ]] ; then + if [[ ${verbosity} != "quiet" ]] ; then + echo -e "${c_error}ERROR: failed to copy settings files from ${c_notice}${path_build}${path_settings}${c_error} to ${c_notice}${destination_settings}${c_error}.${c_reset}" + fi + + let failure=1 + fi + fi + fi + + if [[ ${failure} -eq 0 && -d ${path_build}${path_documentation} && ${enable_documentation} == "yes" ]] ; then + for i in ${path_build}${path_documentation}* ; do + + file=$(echo ${i} | sed -e "s|^${path_build}${path_documentation}||") + + break + done + + if [[ ${file} == "*" && ! -f "${path_build}${path_documentation}*" ]] ; then + file= + fi + + if [[ ${file} != "" ]] ; then + if [[ ${verbosity} != "quiet" && ${verbosity} != "error" ]] ; then + echo + echo -e "${c_highlight}Installing Documentation to: ${c_reset}${c_notice}${destination_documentation}${c_reset}${c_highlight}.${c_reset}" + fi + + cp ${verbose_common} -R ${path_build}${path_documentation}* ${destination_documentation} + + if [[ ${?} -ne 0 ]] ; then + if [[ ${verbosity} != "quiet" ]] ; then + echo -e "${c_error}ERROR: failed to copy documentation files from ${c_notice}${path_build}${path_documentation}${c_error} to ${c_notice}${destination_documentation}${c_error}.${c_reset}" + fi + + let failure=1 + fi + fi + fi + + if [[ ${failure} -eq 1 ]] ; then + return 1 + fi + + return 0 +} + +install_cleanup() { + + unset install_copyright + unset install_main + unset install_handle_colors + unset install_help + unset install_perform_install + unset install_cleanup +} + +install_main $* diff --git a/licenses/cc-by-sa-4.0 b/licenses/cc-by-sa-4.0 new file mode 100644 index 0000000..e672f4e --- /dev/null +++ b/licenses/cc-by-sa-4.0 @@ -0,0 +1,4 @@ +Creative Commons Attribution Share Alike 4.0 International + +see: https://creativecommons.org/licenses/by-sa/4.0/ +see: https://creativecommons.org/licenses/by-sa/4.0/legalcode diff --git a/licenses/copyrights.txt b/licenses/copyrights.txt new file mode 100644 index 0000000..eec3295 --- /dev/null +++ b/licenses/copyrights.txt @@ -0,0 +1,5 @@ +ALl files within this project unless otherwise specified are Copyright © 2007-2024 Kevin Day. + +Source code and related files are under lgpl-2.1-or-later. +Specifications and related files are under open-standard-license-1.0-or-later. +Documentation and related files are under cc-by-sa-4.0. diff --git a/licenses/lgpl-2.1-or-later b/licenses/lgpl-2.1-or-later new file mode 100644 index 0000000..602bfc9 --- /dev/null +++ b/licenses/lgpl-2.1-or-later @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/licenses/open-standard-license-1.0-or-later b/licenses/open-standard-license-1.0-or-later new file mode 100644 index 0000000..d82c79d --- /dev/null +++ b/licenses/open-standard-license-1.0-or-later @@ -0,0 +1,44 @@ +Open Standard License 1.0. + February 27, 2024. + +This license applies to the Standards and their Specifications and designates legal requirements on Implementations of the Standard and their respective Specifications. + +Terminology: + - Standard: A set of rules and guidelines. + - Specification: A specific interpretation or clarification of a Standard, such as the documentation that describes how to use or follow a Standard. + - Implementation: The applying of some Standard or Specification. + - API: Application Programming Interface*. + - ABI: Application Binary Interface**. + - Service: Any action or labor performed by one party for another party (such as one person helping another person). + - Protocol: In the context of computers and software, this is a Standard focused on communication between at least two parties (often referring to Internet communication) and is at the most basic level an agreement of rules between two (or more) parties. + - Provider: A party who provides a Service of any kind (or equivalent functionality) that utilizes a Protocol or provides a Service that Implements or follows a Standard or Specification to another party. + +* The API term is commonly mis-represented as Services or Protocols (such as "Web API" which in actuality should be called "Web Service" or "Web Protocol"). + An API instructs a party, usually a programmer, on how to use a dependency when programming some software that utilizes said dependency. + An API is a Specification of some Implementation of a Standard be it a formally defined Standard or an informally defined Standard. + An API may be an Implementation of a Standard or a part of an Implementation of a Standard. + An API is, in effect, documentation. + +** The ABI term refers to an Application Binary Interface and represents the compiled Implementation of some API. + An ABI may not always exist for some API, such as for pure scripting languages. + An ABI is neither a Specification nor a Standard. + An ABI is an Implementation of an API making it an Implementation of a Standard or Specification. + +01) Principles of this license: + 01) The Standard or Specification is and must be freely and publicly available to use and implement irrespective of any license, patent, or other restriction of any kind for any reason of some Implementation or Provider. + 02) All patents associated are and must be royalty-free for unrestricted use and must not impose any restrictions on any third party's Implementation of this Standard in any way for any reason beyond those described in this license. + 03) There are not and there must not be any agreements or requirements for the execution of this license grant, including but not limited to: NDA, grant, click-through, or any form of paperwork (including but not limited to all non-paper forms of paperwork, such as digital forms). + 04) There are not and must not be any restrictions on the form of an Implementation of a Standard or Specification. + 05) Implementations of a Standard or Specification may be under any license so long as that license: + a) Does not restrict, alter, or invalidate this license in any manner. + b) Does not impose any form of restrictions to access, to use, to implement, to extend, or to deviate from anything allowed or otherwise granted by this license. + 06) This license shall prohibit any form of restricting any parties to access, to use, to implement, to extend, or o deviate from this Standard unless: + a) Restricted by this license. + b) Unless there is a breach of license conditions. + 07) This license is irrevocable unless there is a breach of the license conditions. + 08) This license does not grant any kind of warranty or liability under any circumstances to any party for any reason, be it direct, indirect, consequential, incidental, or in any other form. + 09) This license does not restrict any party from optionally providing their own warranty or liability on any Implementation but such warranties or liabilities are completely separate and independent of this license in all circumstances for any reason, be it direct, indirect, consequential, incidental, or for any other reason. + 10) The final and absolute determination of any terminology and intent of this license is by the original owner of this license or a party explicitly authorized by the original license owner or party, and is not in any way subject to re-interpretation or re-definition in any way for any reason by any party including but not limited to judges, juries, lawyers, attorneys, technicians, experts, or governments. + 11) Any restriction, alteration, removal, invalidation, making illegal, or making unlawful of or against any part of this license by any party, such as but not limited to a government or judge, shall not permanently alter this license in any way for any reason. That is for example, if some court deems some part of this unlawful and then later makes it lawful, then the now re-lawfulized parts do and must immediately apply once more as if it were never made unlawful. + 12) When at any point in time under any government any parts of this license is restricted, altered, removed, invalidated or otherwise made illegal, unlawful, or unenforceable is reversed, restored, or otherwise made legal, lawful, and enforceable again, then these parts are immediately in affect again. + 13) If at any point in time some party, such as but not limited to a government, makes any change to this license (like those described in [01.11]) and the licensee for any reason at any time is no longer subject to a given jurisdiction, then those parts of the original license are immediately in effect as if they were never altered or restricted in the first place. For example, if a licensee leaves the jurisdiction of some government that otherwise restricted some part of this license then that otherwise restricted part is immediately in affect again now that the licensee is out of the given jurisdiction. diff --git a/sources/c/program/control/control/config.c b/sources/c/program/control/control/config.c new file mode 100644 index 0000000..1ac2545 --- /dev/null +++ b/sources/c/program/control/control/config.c @@ -0,0 +1 @@ +#include "config.h" diff --git a/sources/c/program/control/control/config.h b/sources/c/program/control/control/config.h new file mode 100644 index 0000000..e69de29 diff --git a/sources/c/program/control/control/control.c b/sources/c/program/control/control/control.c new file mode 100644 index 0000000..0c95daf --- /dev/null +++ b/sources/c/program/control/control/control.c @@ -0,0 +1,9 @@ +#include "control.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/sources/c/program/control/control/control.h b/sources/c/program/control/control/control.h new file mode 100644 index 0000000..0bed44e --- /dev/null +++ b/sources/c/program/control/control/control.h @@ -0,0 +1,25 @@ +/** + * FLL - Level 3 + * + * Project: Control + * API Version: 0.7 + * Licenses: lgpl-2.1-or-later + * + * This program provides the base include for the control program. + */ +#ifndef _control_control_h +#define _control_control_h + +// Control includes. +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _control_control_h diff --git a/sources/c/program/control/control/main.c b/sources/c/program/control/control/main.c new file mode 100644 index 0000000..a39d0e3 --- /dev/null +++ b/sources/c/program/control/control/main.c @@ -0,0 +1,51 @@ +#include "control.h" + +int main(const int argc, const f_string_t *argv, const f_string_t *envp) { + + control_t data = control_t_initialize; + + data.program.debug.flag |= control_print_flag_debug_e | control_print_flag_out_e; + data.program.error.flag |= control_print_flag_error_e | control_print_flag_out_e; + data.program.output.flag |= control_print_flag_out_e; + data.program.message.flag |= control_print_flag_message_e | control_print_flag_out_e; + data.program.warning.flag |= control_print_flag_warning_e | control_print_flag_out_e; + data.program.error.custom = (void *) &data; + data.program.debug.custom = (void *) &data; + data.program.message.custom = (void *) &data; + data.program.output.custom = (void *) &data; + data.program.warning.custom = (void *) &data; + + f_console_parameter_t parameters[] = control_console_parameter_t_initialize; + + data.program.parameters.array = parameters; + data.program.parameters.used = control_parameter_total_d; + data.program.environment = envp; + + if (f_pipe_input_exists()) { + data.program.pipe = fll_program_data_pipe_input_e; + } + + data.setting.flag |= control_main_flag_interruptible_e; + + fll_program_standard_set_up(&data.program); + + f_file_umask_get(&data.program.umask); + + { + const f_console_arguments_t arguments = macro_f_console_arguments_t_initialize_1(argc, argv, envp); + + control_setting_load(arguments, &data); + } + + control_process(&data); + + control_delete(&data); + + fll_program_standard_set_down(&data.program); + + if (data.setting.state.status == F_child) { + exit(data.program.child); + } + + return (F_status_is_error(data.setting.state.status) || data.setting.state.status == F_false) ? 1 : 0; +} diff --git a/sources/c/program/control/control/main.h b/sources/c/program/control/control/main.h new file mode 100644 index 0000000..18aef0c --- /dev/null +++ b/sources/c/program/control/control/main.h @@ -0,0 +1,38 @@ +/** + * FLL - Level 3 + * + * Project: Control + * API Version: 0.7 + * Licenses: lgpl-2.1-or-later + * + * This file is only ever included by program/control/control/main.c and should not normally be included anywhere else. + * Anything that wants to include this should be providing the "control" program functionality in some manner. + */ +#ifndef _control_control_h +#define _control_control_h + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Standard program Entry point. + * + * @param argc + * The number of arguments. + * @param argv + * The array of arguments. + * @param envp + * The array of all environment variables on program start. + * + * @return + * 0 on success. + * 1 on error. + */ +extern int main(const int argc, const f_string_t *argv, const f_string_t *envp); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _control_control_h diff --git a/sources/c/program/control/control/string.c b/sources/c/program/control/control/string.c new file mode 100644 index 0000000..5c33470 --- /dev/null +++ b/sources/c/program/control/control/string.c @@ -0,0 +1,25 @@ +#include "control.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _di_control_program_name_s_ + const f_string_static_t control_program_name_s = macro_f_string_static_t_initialize_1(CONTROL_program_name_s, 0, CONTROL_program_name_s_length); + const f_string_static_t control_program_name_long_s = macro_f_string_static_t_initialize_1(CONTROL_program_name_long_s, 0, CONTROL_program_name_long_s_length); +#endif // _di_control_program_name_s_ + +#ifndef _di_control_default_s_ + const f_string_static_t control_default_engine_s = macro_f_string_static_t_initialize_1(CONTROL_default_engine_s, 0, CONTROL_default_engine_s_length); + const f_string_static_t control_default_path_pid_s = macro_f_string_static_t_initialize_1(CONTROL_default_path_pid_s, 0, CONTROL_default_path_pid_s_length); + const f_string_static_t control_default_path_pid_prefix_s = macro_f_string_static_t_initialize_1(CONTROL_default_path_pid_prefix_s, 0, CONTROL_default_path_pid_prefix_s_length); + const f_string_static_t control_default_path_pid_suffix_s = macro_f_string_static_t_initialize_1(CONTROL_default_path_pid_suffix_s, 0, CONTROL_default_path_pid_suffix_s_length); + const f_string_static_t control_default_path_settings_s = macro_f_string_static_t_initialize_1(CONTROL_default_path_settings_s, 0, CONTROL_default_path_settings_s_length); + const f_string_static_t control_default_path_socket_s = macro_f_string_static_t_initialize_1(CONTROL_default_path_socket_s, 0, CONTROL_default_path_socket_s_length); + const f_string_static_t control_default_path_socket_prefix_s = macro_f_string_static_t_initialize_1(CONTROL_default_path_socket_prefix_s, 0, CONTROL_default_path_socket_prefix_s_length); + const f_string_static_t control_default_path_socket_suffix_s = macro_f_string_static_t_initialize_1(CONTROL_default_path_socket_suffix_s, 0, CONTROL_default_path_socket_suffix_s_length); +#endif // _di_control_default_s_ + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/sources/c/program/control/control/string.h b/sources/c/program/control/control/string.h new file mode 100644 index 0000000..513e234 --- /dev/null +++ b/sources/c/program/control/control/string.h @@ -0,0 +1,41 @@ +/** + * FLL - Level 3 + * + * Project: Control + * API Version: 0.7 + * Licenses: lgpl-2.1-or-later + * + * Provides the common string structures for the control program. + * + * This is auto-included and should not need to be explicitly included. + */ +#ifndef _control_control_string_h +#define _control_control_string_h + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The program name. + */ +#ifndef _di_control_program_name_s_ + #define CONTROL_program_name_s "control" + #define CONTROL_program_name_long_s "Control" + + #define CONTROL_program_name_s_length 7 + #define CONTROL_program_name_long_s_length 7 +#endif // _di_control_program_name_s_ + +/** + * The program defaults. + */ +#ifndef _di_control_default_s_ + // No custom program defaults. +#endif // _di_control_default_s_ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _control_control_string_h diff --git a/sources/c/program/control/main/action.c b/sources/c/program/control/main/action.c new file mode 100644 index 0000000..f9d818d --- /dev/null +++ b/sources/c/program/control/main/action.c @@ -0,0 +1,233 @@ +#include "control.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _di_control_action_type_identify_ + const uint8_t control_action_type_identify(const f_string_static_t action) { + + if (f_compare_dynamic(action, control_freeze_s) == F_equal_to) return control_action_type_freeze_e; + if (f_compare_dynamic(action, control_kexec_s) == F_equal_to) return control_action_type_kexec_e; + if (f_compare_dynamic(action, control_kill_s) == F_equal_to) return control_action_type_kill_e; + if (f_compare_dynamic(action, control_pause_s) == F_equal_to) return control_action_type_pause_e; + if (f_compare_dynamic(action, control_reboot_s) == F_equal_to) return control_action_type_reboot_e; + if (f_compare_dynamic(action, control_reload_s) == F_equal_to) return control_action_type_reload_e; + if (f_compare_dynamic(action, control_rerun_s) == F_equal_to) return control_action_type_rerun_e; + if (f_compare_dynamic(action, control_restart_s) == F_equal_to) return control_action_type_restart_e; + if (f_compare_dynamic(action, control_resume_s) == F_equal_to) return control_action_type_resume_e; + if (f_compare_dynamic(action, control_shutdown_s) == F_equal_to) return control_action_type_shutdown_e; + if (f_compare_dynamic(action, control_start_s) == F_equal_to) return control_action_type_start_e; + if (f_compare_dynamic(action, control_stop_s) == F_equal_to) return control_action_type_stop_e; + if (f_compare_dynamic(action, control_thaw_s) == F_equal_to) return control_action_type_thaw_e; + + return 0; + } +#endif // _di_control_action_type_identify_ + +#ifndef _di_control_action_type_name_ + const f_string_static_t control_action_type_name(const uint8_t type) { + + switch (type) { + case control_action_type_freeze_e: + return control_freeze_s; + + case control_action_type_kexec_e: + return control_kexec_s; + + case control_action_type_kill_e: + return control_kill_s; + + case control_action_type_pause_e: + return control_pause_s; + + case control_action_type_reboot_e: + return control_reboot_s; + + case control_action_type_reload_e: + return control_reload_s; + + case control_action_type_rerun_e: + return control_rerun_s; + + case control_action_type_restart_e: + return control_restart_s; + + case control_action_type_resume_e: + return control_resume_s; + + case control_action_type_shutdown_e: + return control_shutdown_s; + + case control_action_type_start_e: + return control_start_s; + + case control_action_type_stop_e: + return control_stop_s; + + case control_action_type_thaw_e: + return control_thaw_s; + } + + return f_string_empty_s; + } +#endif // _di_control_action_type_name_ + +#ifndef _di_control_action_verify_ + void control_action_verify(control_main_t * const main) { + + if (!main) return; + + switch (main->setting.action) { + case control_action_type_freeze_e: + case control_action_type_kill_e: + case control_action_type_pause_e: + case control_action_type_reload_e: + case control_action_type_rerun_e: + case control_action_type_restart_e: + case control_action_type_resume_e: + case control_action_type_start_e: + case control_action_type_stop_e: + case control_action_type_thaw_e: + if (main->setting.actions.used < 3) { + control_print_error_parameter_action_rule_not(&main->program.error, main->setting.actions.array[0]); + + main->setting.state.status = F_status_set_error(F_parameter); + + return; + } + + if (main->setting.actions.used > 3) { + if (f_compare_dynamic(control_at_s, main->setting.actions.array[3]) == F_equal_to) { + if (main->setting.actions.used < 5) { + control_print_error_parameter_action_rule_too_few_with(&main->program.error, main->setting.actions.array[0], control_at_s); + + main->setting.state.status = F_status_set_error(F_parameter); + + return; + } + + if (main->setting.actions.used > 5) { + control_print_error_parameter_action_rule_too_many_with(&main->program.error, main->setting.actions.array[0], control_at_s); + + main->setting.state.status = F_status_set_error(F_parameter); + + return; + } + } + else if (f_compare_dynamic(control_in_s, main->setting.actions.array[3]) == F_equal_to) { + if (main->setting.actions.used < 6) { + control_print_error_parameter_action_rule_too_few_with(&main->program.error, main->setting.actions.array[0], control_in_s); + + main->setting.state.status = F_status_set_error(F_parameter); + + return; + } + } + else if (f_compare_dynamic(control_now_s, main->setting.actions.array[3]) == F_equal_to) { + if (main->setting.actions.used > 4) { + control_print_error_parameter_action_rule_too_many_with(&main->program.error, main->setting.actions.array[0], control_now_s); + + main->setting.state.status = F_status_set_error(F_parameter); + + return; + } + } + else { + control_print_error_parameter_action_rule_with_unknown(&main->program.error, main->setting.actions.array[0], main->setting.actions.array[2]); + + main->setting.state.status = F_status_set_error(F_parameter); + + return; + } + } + + if (!main->setting.actions.array[1].used) { + if (main->setting.actions.used == 3) { + control_print_error_parameter_action_rule_empty(&main->program.error, main->setting.actions.array[0]); + } + else { + control_print_error_parameter_action_rule_directory_empty(&main->program.error, main->setting.actions.array[0]); + } + + main->setting.state.status = F_status_set_error(F_parameter); + + return; + } + + if (main->setting.actions.used == 3) { + if (!main->setting.actions.array[2].used) { + control_print_error_parameter_action_rule_basename_empty(&main->program.error, main->setting.actions.array[0]); + + main->setting.state.status = F_status_set_error(F_parameter); + + return; + } + } + + main->setting.state.status = F_okay; + + return; + + case control_action_type_kexec_e: + case control_action_type_reboot_e: + case control_action_type_shutdown_e: + if (main->setting.actions.used < 2) { + control_print_error_parameter_action_rule_too_few(&main->program.error, main->setting.actions.array[0]); + + main->setting.state.status = F_status_set_error(F_parameter); + + return; + } + + if (f_compare_dynamic(control_at_s, main->setting.actions.array[1]) == F_equal_to) { + if (main->setting.actions.used < 3) { + control_print_error_parameter_action_rule_too_few_with(&main->program.error, main->setting.actions.array[0], control_at_s); + + main->setting.state.status = F_status_set_error(F_parameter); + + return; + } + + if (main->setting.actions.used > 3) { + control_print_error_parameter_action_rule_too_many_with(&main->program.error, main->setting.actions.array[0], control_at_s); + + main->setting.state.status = F_status_set_error(F_parameter); + + return; + } + } + else if (f_compare_dynamic(control_in_s, main->setting.actions.array[1]) == F_equal_to) { + if (main->setting.actions.used < 4) { + control_print_error_parameter_action_rule_too_few_with(&main->program.error, main->setting.actions.array[0], control_in_s); + + main->setting.state.status = F_status_set_error(F_parameter); + + return; + } + } + else if (f_compare_dynamic(control_now_s, main->setting.actions.array[1]) == F_equal_to) { + if (main->setting.actions.used > 2) { + control_print_error_parameter_action_rule_too_many_with(&main->program.error, main->setting.actions.array[0], control_now_s); + + main->setting.state.status = F_status_set_error(F_parameter); + + return; + } + } + else { + control_print_error_parameter_action_rule_with_unknown(&main->program.error, main->setting.actions.array[0], main->setting.actions.array[1]); + + main->setting.state.status = F_status_set_error(F_parameter); + + return; + } + } + + main->setting.state.status = F_okay; + } +#endif // _di_control_action_verify_ + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/sources/c/program/control/main/action.h b/sources/c/program/control/main/action.h new file mode 100644 index 0000000..b0d401a --- /dev/null +++ b/sources/c/program/control/main/action.h @@ -0,0 +1,66 @@ +/** + * FLL - Level 3 + * + * Project: Control + * API Version: 0.7 + * Licenses: lgpl-2.1-or-later + * + * Provides the action functionality. + * + * This is auto-included and should not need to be explicitly included. + */ +#ifndef _control_main_action_h +#define _control_main_action_h + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Identify the action code the given name represents. + * + * @param action + * The string representing an action. + * + * @return + * The action type code on success. + * 0 if name is unknown. + */ +#ifndef _di_control_action_type_identify_ + extern const uint8_t control_action_type_identify(const f_string_static_t action); +#endif // _di_control_action_type_identify_ + +/** + * Get a string representing the action type. + * + * @param type + * The action type id. + * + * @return + * The string with used > 0 on success. + * The string with used == 0 if no match was found. + */ +#ifndef _di_control_action_type_name_ + extern const f_string_static_t control_action_type_name(const uint8_t type); +#endif // _di_control_action_type_name_ + +/** + * Verify that the additional parameters are reasonably correct for the identified action. + * + * @param main + * The main program data. + * + * This alters main.setting.state.status: + * F_okay on success. + * + * F_parameter (with error bit) on parameter validation/verification failure. + */ +#ifndef _di_control_action_verify_ + extern void control_action_verify(control_main_t * const main); +#endif // _di_control_action_verify_ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _control_main_action_h diff --git a/sources/c/program/control/main/common.c b/sources/c/program/control/main/common.c new file mode 100644 index 0000000..1ef8754 --- /dev/null +++ b/sources/c/program/control/main/common.c @@ -0,0 +1,401 @@ +#include "control.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _di_control_setting_load_ + void control_setting_load(const f_console_arguments_t arguments, control_main_t * const main) { + + if (!main) return; + + main->setting.state.step_small = control_allocation_console_d; + + f_console_parameter_process(arguments, &main->program.parameters, &main->setting.state, 0); + + main->setting.state.step_small = control_allocation_small_d; + + if (F_status_is_error(main->setting.state.status)) { + control_print_error(&main->program.error, macro_control_f(f_console_parameter_process)); + + return; + } + + main->setting.state.status = fll_program_parameter_process_context_standard(F_true, &main->program); + + if (F_status_is_error(main->setting.state.status)) { + control_print_error(&main->program.error, macro_control_f(fll_program_parameter_process_context_standard)); + + return; + } + + main->setting.state.status = fll_program_parameter_process_verbosity_standard(F_true, &main->program); + + if (F_status_is_error(main->setting.state.status)) { + control_print_error(&main->program.error, macro_control_f(fll_program_parameter_process_verbosity_standard)); + + return; + } + + if (main->program.parameters.array[f_console_standard_parameter_help_e].result & f_console_result_found_e) { + main->setting.flag |= control_main_flag_help_e; + + return; + } + + if (main->program.parameters.array[f_console_standard_parameter_version_e].result & f_console_result_found_e) { + main->setting.flag |= control_main_flag_version_e; + + return; + } + + if (main->program.parameters.array[f_console_standard_parameter_copyright_e].result & f_console_result_found_e) { + main->setting.flag |= control_main_flag_copyright_e; + + return; + } + + if (main->program.parameters.array[control_parameter_return_e].result & f_console_result_found_e) { + main->setting.flag |= control_main_flag_return_e; + + return; + } + + f_number_unsigned_t index = 0; + + if (main->program.pipe & fll_program_data_pipe_input_e) { + main->setting.flag |= control_main_flag_pipe_e; + } + else { + main->setting.flag &= ~control_main_flag_pipe_e; + } + + // The settings path is statically allocated. + if (main->program.parameters.array[control_parameter_settings_e].result & f_console_result_value_e) { + index = main->program.parameters.array[control_parameter_settings_e].values.array[main->program.parameters.array[control_parameter_settings_e].values.used - 1]; + + main->setting.path_settings.string = main->program.parameters.arguments.array[index].string; + main->setting.path_settings.used = main->program.parameters.arguments.array[index].used; + main->setting.path_settings.size = 0; + } + else { + main->setting.path_settings.string = control_path_settings_s.string; + main->setting.path_settings.used = control_path_settings_s.used; + main->setting.path_settings.size = 0; + } + + { + f_file_t file = f_file_t_initialize; + + main->setting.state.status = f_file_stream_open(main->setting.path_settings, f_file_open_mode_read_s, &file); + + if (F_status_is_error(main->setting.state.status)) { + control_print_error_file(&main->program.error, macro_control_f(f_file_stream_open), main->setting.path_settings, f_file_operation_open_s, fll_error_file_type_file_e); + + return; + } + + main->setting.state.status = f_file_stream_read(file, &main->cache.large); + + f_file_stream_flush(file); + f_file_stream_close(&file); + + if (F_status_is_error(main->setting.state.status)) { + control_print_error_file(&main->program.error, macro_control_f(f_file_stream_read), main->setting.path_settings, f_file_operation_read_s, fll_error_file_type_file_e); + + return; + } + } + + if (main->cache.large.used) { + main->setting.range.start = 0; + main->setting.range.stop = main->cache.large.used - 1; + } + else { + main->setting.range.start = 1; + main->setting.range.stop = 0; + } + + fll_fss_extended_read(main->cache.large, &main->setting.range, &main->cache.objects, &main->cache.contents, 0, 0, &main->cache.delimits, 0, &main->setting.state); + + if (F_status_is_error(main->setting.state.status)) { + control_print_error_file(&main->program.error, macro_control_f(fll_fss_extended_read), main->setting.path_settings, f_file_operation_process_s, fll_error_file_type_file_e); + + return; + } + + f_fss_apply_delimit(main->cache.delimits, &main->cache.large, &main->setting.state); + + if (F_status_is_error(main->setting.state.status)) { + control_print_error_file(&main->program.error, macro_control_f(f_fss_apply_delimit), main->setting.path_settings, f_file_operation_process_s, fll_error_file_type_file_e); + + return; + } + + uint8_t i = 0; + uint8_t parameter_hass[] = { 0, 0, 0, 0 }; + f_number_unsigned_t parameter_ats[] = { 0, 0, 0, 0 }; + + { + const f_string_static_t parameter_names[] = { + control_name_socket_s, + control_path_socket_s, + control_path_socket_prefix_s, + control_path_socket_suffix_s, + }; + + f_number_unsigned_t j = 0; + uint8_t k = 0; + + for (; j < main->cache.objects.used; ++j) { + + for (k = 0; k < 4; ++k) { + + if (!parameter_names[k].used) continue; + + main->setting.range.start = 0; + main->setting.range.stop = parameter_names[k].used - 1; + + if (f_compare_dynamic_partial(parameter_names[k], main->cache.large, main->setting.range, main->cache.objects.array[j]) == F_equal_to) { + parameter_hass[k] = F_true; + parameter_ats[k] = j; + + break; + } + } // for + } // for + } + + // The settings path is statically allocated, except when read from the settings file. + if (main->program.parameters.array[control_parameter_socket_e].result & f_console_result_value_e) { + index = main->program.parameters.array[control_parameter_settings_e].values.array[main->program.parameters.array[control_parameter_settings_e].values.used - 1]; + + main->setting.path_socket.string = main->program.parameters.arguments.array[index].string; + main->setting.path_socket.used = main->program.parameters.arguments.array[index].used; + main->setting.path_socket.size = 0; + } + else if (parameter_hass[1]) { + main->setting.state.status = f_string_dynamic_partial_append_nulless(main->cache.large, main->cache.objects.array[parameter_ats[1]], &main->setting.path_socket); + + if (F_status_is_error(main->setting.state.status)) { + control_print_error_file(&main->program.error, macro_control_f(f_string_dynamic_partial_append_nulless), main->setting.path_socket, f_file_operation_process_s, fll_error_file_type_file_e); + + return; + } + } + else { + main->setting.path_socket.string = controller_path_socket_s.string; + main->setting.path_socket.used = controller_path_socket_s.used; + main->setting.path_socket.size = 0; + } + + main->setting.state.status = f_file_exists(main->setting.path_socket, F_true); + + if (F_status_is_error(main->setting.state.status) || main->setting.state.status == F_false) { + if (F_status_is_error(main->setting.state.status)) { + control_print_error_file(&main->program.error, macro_control_f(f_file_exists), main->setting.path_socket, f_file_operation_find_s, fll_error_file_type_directory_e); + + if (main->program.error.verbosity > f_console_verbosity_quiet_e) { + fll_print_dynamic_raw(f_string_eol_s, main->program.error.to); + } + } + + main->setting.state.status = F_status_set_error(F_socket_not); + + control_print_error_socket_file_missing(&main->program.error, main->setting.path_socket); + + return; + } + + // Construct the file name when the socket path is a directory. + if (f_file_is(main->setting.path_socket, F_file_type_directory_d, F_true) == F_true) { + main->setting.state.status = f_string_dynamic_append_assure(f_path_separator_s, &main->setting.path_socket); + + if (F_status_is_error(main->setting.state.status)) { + control_print_error(&main->program.error, macro_control_f(f_string_dynamic_append_assure)); + + return; + } + + { + const uint8_t append_ids[] = { + 0, + control_parameter_name_e, + 0, + }; + + const uint8_t append_hass[] = { + parameter_hass[2], + parameter_hass[0], + parameter_hass[3], + }; + + const f_string_static_t append_defaults[] = { + controller_path_socket_prefix_s, + controller_name_socket_s, + controller_path_socket_suffix_s, + }; + + for (i = 0; i < 3; ++i) { + + if (append_ids[i] && main->program.parameters.array[append_ids[i]].result & f_console_result_value_e) { + index = main->program.parameters.array[append_ids[i]].values.array[main->program.parameters.array[append_ids[i]].values.used - 1]; + + main->setting.state.status = f_string_dynamic_append(main->program.parameters.arguments.array[index], &main->setting.path_socket); + } + else if (append_hass[i]) { + main->setting.state.status = f_string_dynamic_partial_append_nulless(main->cache.large, main->cache.objects.array[append_hass[i]], &main->setting.path_socket); + } + else { + main->setting.state.status = f_string_dynamic_append_nulless(append_defaults[i], &main->setting.path_socket); + } + + if (F_status_is_error(main->setting.state.status)) { + control_print_error( + &main->program.error, + ((append_ids[i] && main->program.parameters.array[append_ids[i]].result & f_console_result_value_e) || !append_hass[i]) + ? macro_control_f(f_string_dynamic_append) + : macro_control_f(f_string_dynamic_partial_append_nulless) + ); + + return; + } + } // for + + main->setting.state.status = f_file_exists(main->setting.path_socket, F_true); + + if (F_status_is_error(main->setting.state.status) || main->setting.state.status == F_false) { + if (F_status_is_error(main->setting.state.status)) { + control_print_error_file(&main->program.error, macro_control_f(f_file_exists), main->setting.path_socket, f_file_operation_find_s, fll_error_file_type_directory_e); + + if (main->program.error.verbosity > f_console_verbosity_quiet_e) { + fll_print_dynamic_raw(f_string_eol_s, main->program.error.to); + } + } + + main->setting.state.status = F_status_set_error(F_socket_not); + + control_print_error_socket_file_missing(&main->program.error, main->setting.path_socket); + + return; + } + } + } + + if (f_file_is(main->setting.path_socket, F_file_type_socket_d, F_true) == F_false) { + main->setting.state.status = F_status_set_error(F_socket_not); + + control_print_error_socket_file_not(&main->program.error, main->setting.path_socket); + + return; + } + + main->setting.state.status = f_socket_create(&main->setting.socket); + + if (F_status_is_error(main->setting.state.status)) { + control_print_error(&main->program.error, macro_control_f(f_socket_create)); + + control_print_error_socket_file_failed(&main->program.error, main->setting.path_socket); + + return; + } + + main->setting.state.status = f_socket_connect(&main->setting.socket); + + if (F_status_is_error(main->setting.state.status)) { + control_print_error(&main->program.error, macro_control_f(f_socket_connect)); + + control_print_error_socket_file_failed(&main->program.error, main->setting.path_socket); + + return; + } + + f_memory_array_resize(0, sizeof(f_range_t), (void **) &main->cache.objects.array, &main->cache.objects.used, &main->cache.objects.size); + f_memory_arrays_resize(0, sizeof(f_ranges_t), (void **) &main->cache.contents.array, &main->cache.contents.used, &main->cache.contents.size, &f_rangess_delete_callback); + f_memory_array_resize(0, sizeof(f_number_unsigned_t), (void **) &main->cache.delimits.array, &main->cache.delimits.used, &main->cache.delimits.size); + + { + const uint8_t ids[] = { + control_parameter_name_e, + control_parameter_settings_e, + control_parameter_socket_e + }; + + const f_string_static_t names[] = { + control_long_name_s, + control_long_settings_s, + control_long_socket_s + }; + + f_status_t error_printed_not = F_false; + + for (i = 0; i < 3; ++i) { + + if (main->program.parameters.array[ids[i]].result & f_console_result_found_e) { + main->setting.state.status = F_status_set_error(F_parameter); + + if (error_printed_not) { + error_printed_not = F_true; + } + + control_print_error_parameter_value_not(&main->program.error, names[i]); + } + else if (main->program.parameters.array[ids[i]].result & f_console_result_value_e) { + index = main->program.parameters.array[ids[i]].values.array[main->program.parameters.array[ids[i]].values.used - 1]; + + if (!main->program.parameters.arguments.array[index].used) { + main->setting.state.status = F_status_set_error(F_parameter); + + if (error_printed_not) { + error_printed_not = F_true; + } + + control_print_error_parameter_value_empty(&main->program.error, names[i]); + } + } + } // for + } + + main->cache.large.used = 0; + + if (main->cache.large.size > control_default_buffer_limit_soft_large_d) { + main->setting.state.status = f_memory_array_resize(control_default_buffer_limit_soft_large_d, sizeof(f_char_t), (void **) &main->cache.large.string, &main->cache.large.used, &main->cache.large.size); + + if (F_status_is_error(main->setting.state.status)) { + control_print_error(&main->program.error, macro_control_f(f_memory_array_resize)); + + return; + } + } + + if (main->program.parameters.remaining.used) { + main->setting.state.status = f_memory_array_increase_by(main->program.parameters.remaining.used, sizeof(f_string_dynamic_t), (void **) &main->setting.actions.array, &main->setting.actions.used, &main->setting.actions.size); + + if (F_status_is_error(main->setting.state.status)) { + control_print_error(&main->program.error, macro_control_f(f_memory_array_increase_by)); + + return; + } + + index = 0; + + for (f_number_unsigned_t number = 0; index < main->program.parameters.remaining.used; ++index) { + + if (control_signal_check(main)) return; + + number = main->program.parameters.remaining.array[index]; + + // Statically allocate the inner strings. + main->setting.actions.array[main->setting.actions.used].string = main->program.parameters.arguments.array[number].string; + main->setting.actions.array[main->setting.actions.used].used = main->program.parameters.arguments.array[number].used; + main->setting.actions.array[main->setting.actions.used++].size = 0; + } // for + } + + main->setting.state.status = F_okay; + } +#endif // _di_control_setting_load_ + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/sources/c/program/control/main/common.h b/sources/c/program/control/main/common.h new file mode 100644 index 0000000..c6e8f21 --- /dev/null +++ b/sources/c/program/control/main/common.h @@ -0,0 +1,48 @@ +/** + * FLL - Level 3 + * + * Project: Control + * API Version: 0.7 + * Licenses: lgpl-2.1-or-later + * + * Provides the common data structures. + * + * This is auto-included and should not need to be explicitly included. + */ +#ifndef _control_main_common_h +#define _control_main_common_h + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Perform the standard program setting load process. + * + * This prints error messages as appropriate. + * + * If either main or setting is NULL, then this immediately returns without doing anything. + * + * @param arguments + * The parameters passed to the process (often referred to as command line arguments). + * @param main + * The main program data and settings. + * + * This alters main.setting.state.status: + * F_okay on success. + * + * Errors (with error bit) from: f_console_parameter_process(). + * Errors (with error bit) from: fll_program_parameter_process_context(). + * + * @see f_console_parameter_process() + * @see fll_program_parameter_process_context() + */ +#ifndef _di_control_setting_load_ + extern void control_setting_load(const f_console_arguments_t arguments, control_main_t * const main); +#endif // _di_control_setting_load_ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _control_main_common_h diff --git a/sources/c/program/control/main/common/define.c b/sources/c/program/control/main/common/define.c new file mode 100644 index 0000000..39790a2 --- /dev/null +++ b/sources/c/program/control/main/common/define.c @@ -0,0 +1,9 @@ +#include "../control.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/sources/c/program/control/main/common/define.h b/sources/c/program/control/main/common/define.h new file mode 100644 index 0000000..608f3b3 --- /dev/null +++ b/sources/c/program/control/main/common/define.h @@ -0,0 +1,67 @@ +/** + * FLL - Level 3 + * + * Project: Control + * API Version: 0.7 + * Licenses: lgpl-2.1-or-later + * + * Provides the common define types. + * + * This is auto-included and should not need to be explicitly included. + */ +#ifndef _control_main_common_define_h +#define _control_main_common_define_h + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The program allocation defines. + * + * control_allocation_*_d: + * - console: An allocation step used for small buffers specifically for console parameter. + * - large: An allocation step used for buffers that are anticipated to have large buffers. + * - pipe: A buffer size used for processing piped data. + * - small: An allocation step used for buffers that are anticipated to have small buffers. + */ +#ifndef _di_control_allocation_d_ + #define control_allocation_console_d 4 + #define control_allocation_large_d 256 + #define control_allocation_pipe_d 16384 + #define control_allocation_small_d 4 +#endif // _di_control_allocation_d_ + +/** + * General defines used throughout the program. + * + * control_default_*_d: + * - buffer_limit_soft_large: The preferred maximum size of buffers intended for large data sets such that sizes exceeding this will be shrunk when operations are complete (aka: a soft limit). + * - buffer_limit_soft_small: The preferred maximum size of buffers intended for small data sets such that sizes exceeding this will be shrunk when operations are complete (aka: a soft limit). + */ +#ifndef _di_control_default_d_ + #define control_default_buffer_limit_soft_large_d 2048 + #define control_default_buffer_limit_soft_small_d 64 +#endif // _di_control_default_d_ + +/** + * The program signal defines. + * + * control_signal_*_d: + * - check: Number of iterations before performing signal check in non-threaded signal handling. + * - check_failsafe: When using threads, how many consecutive failures to check signal before aborting (as a recursion failsafe). + * - check_tiny: The tiny check. + * - check_short: The short signal check. + */ +#ifndef _di_control_signal_d_ + #define control_signal_check_d 500000 + #define control_signal_check_failsafe_d 20000 + #define control_signal_check_tiny_d 4 + #define control_signal_check_short_d 16 +#endif // _di_control_signal_d_ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _control_main_common_define_h diff --git a/sources/c/program/control/main/common/enumeration.c b/sources/c/program/control/main/common/enumeration.c new file mode 100644 index 0000000..39790a2 --- /dev/null +++ b/sources/c/program/control/main/common/enumeration.c @@ -0,0 +1,9 @@ +#include "../control.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/sources/c/program/control/main/common/enumeration.h b/sources/c/program/control/main/common/enumeration.h new file mode 100644 index 0000000..8770f67 --- /dev/null +++ b/sources/c/program/control/main/common/enumeration.h @@ -0,0 +1,159 @@ +/** + * FLL - Level 3 + * + * Project: Control + * API Version: 0.7 + * Licenses: lgpl-2.1-or-later + * + * Provides the common enumeration types. + * + * This is auto-included and should not need to be explicitly included. + */ +#ifndef _control_main_common_enumeration_h +#define _control_main_common_enumeration_h + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Codes representing supported actions. + * + * freeze: Perform the freeze controller operation. + * kexec: Perform the kexec controller operation (only for init mode). + * kill: Perform the kill controller operation. + * pause: Perform the pause controller operation. + * reboot: Perform the reboot controller operation (only for init mode). + * reload: Perform the reload controller operation. + * rerun: Perform the rerun controller operation. + * restart: Perform the restart controller operation. + * resume: Perform the resume controller operation. + * shutdown: Perform the shutdown controller operation (only for init mode). + * start: Perform the start controller operation. + * stop: Perform the stop controller operation. + * thaw: Perform the thaw controller operation. + */ +#ifndef _di_control_action_type_e_ + enum { + control_action_type_freeze_e = 1, + control_action_type_kexec_e, + control_action_type_kill_e, + control_action_type_pause_e, + control_action_type_reboot_e, + control_action_type_reload_e, + control_action_type_rerun_e, + control_action_type_restart_e, + control_action_type_resume_e, + control_action_type_shutdown_e, + control_action_type_start_e, + control_action_type_stop_e, + control_action_type_thaw_e, + }; // enum +#endif // _di_control_action_type_e_ + +/** + * Supported payload types. + * + * controller: The payload is a controller payload. + * error: The payload is an error payload. + * init: The payload is an init payload (only available when operating in "init" mode). + */ +#ifndef _di_control_payload_type_e_ + enum { + control_payload_type_controller_e = 1, + control_payload_type_error_e, + control_payload_type_init_e, + }; // enum +#endif // _di_control_payload_type_e_ + +/** + * A codes repesent different flags associated with a packet. + * + * control_packet_flag_*: + * - binary: Designate that the packet is in binary mode (when not set then packet is in string mode). + * - endian_big: Designate that the packet is in big endian order (when not set then packet is in little endian order). + */ +#ifndef _di_control_packet_flag_e_ + #define control_packet_flag_binary_d 0x80 + #define control_packet_flag_endian_big_d 0x40 +#endif // _di_control_packet_flag_e_ + +/** + * Flags passed to the main function or program. + * + * control_main_flag_*_e: + * - none: No flags set. + * - copyright: Print copyright. + * - header: Enable printing of headers. + * - help: Print help. + * - pipe: Use the input pipe. + * - return: The parameter is specified. + * - version: Print version. + * - version_copyright_help: A helper flag representing version, copyright, and help flag bits being set. + */ +#ifndef _di_control_main_flag_e_ + enum { + control_main_flag_none_e = 0x0, + control_main_flag_copyright_e = 0x1, + control_main_flag_help_e = 0x2, + control_main_flag_pipe_e = 0x4, + control_main_flag_return_e = 0x8, + control_main_flag_version_e = 0x10, + control_main_flag_version_copyright_help_e = 0x13, + }; // enum +#endif // _di_control_main_flag_e_ + +/** + * The main program parameters. + */ +#ifndef _di_control_parameter_d_ + enum { + control_parameter_name_e = f_console_standard_parameter_last_e, + control_parameter_return_e, + control_parameter_settings_e, + control_parameter_socket_e, + }; // enum + + #define control_console_parameter_t_initialize \ + { \ + macro_fll_program_console_parameter_standard_initialize, \ + \ + macro_f_console_parameter_t_initialize_3(control_short_name_s, control_long_name_s, 1, f_console_flag_normal_e), \ + macro_f_console_parameter_t_initialize_3(control_short_return_s, control_long_return_s, 1, f_console_flag_normal_e), \ + macro_f_console_parameter_t_initialize_3(control_short_settings_s, control_long_settings_s, 1, f_console_flag_normal_e), \ + macro_f_console_parameter_t_initialize_3(control_short_socket_s, control_long_socket_s, 1, f_console_flag_normal_e), \ + } + + #define control_parameter_total_d (f_console_parameter_state_type_total_d + 4) +#endif // _di_control_parameter_d_ + +/** + * Flags for fine-tuned print control. + * + * control_print_flag_*_e: + * - none: No flags set. + * - debug: Stream is for debug printing. + * - error: Stream is for error printing. + * - in: Stream is a source file. + * - message: Stream is for message printing. + * - out: Stream is a destination file. + * - warning: Stream is for warning printing. + */ +#ifndef _di_control_print_flag_e_ + enum { + control_print_flag_none_e = 0x0, + control_print_flag_debug_e = 0x1, + control_print_flag_error_e = 0x2, + control_print_flag_file_e = 0x4, + control_print_flag_in_e = 0x8, + control_print_flag_out_e = 0x10, + control_print_flag_message_e = 0x20, + control_print_flag_warning_e = 0x40, + }; // enum +#endif // _di_control_print_flag_e_ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _control_main_common_enumeration_h diff --git a/sources/c/program/control/main/common/print.c b/sources/c/program/control/main/common/print.c new file mode 100644 index 0000000..046aa49 --- /dev/null +++ b/sources/c/program/control/main/common/print.c @@ -0,0 +1,34 @@ +#include "../control.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _di_control_f_a_ + const f_string_t control_f_a[] = { + "control_packet_build", + "control_packet_process", + "control_packet_receive", + "control_packet_send", + "f_console_parameter_process", + "f_file_exists", + "f_file_stream_open", + "f_file_stream_read", + "f_fss_apply_delimit", + "f_memory_array_increase_by", + "f_memory_array_resize", + "f_socket_connect", + "f_socket_create", + "f_string_dynamic_append", + "f_string_dynamic_append_assure", + "f_string_dynamic_partial_append_nulless", + "f_thread_create", + "fll_fss_extended_read", + "fll_program_parameter_process_context_standard", + "fll_program_parameter_process_verbosity_standard", + }; +#endif // _di_control_f_a_ + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/sources/c/program/control/main/common/print.h b/sources/c/program/control/main/common/print.h new file mode 100644 index 0000000..900e4fa --- /dev/null +++ b/sources/c/program/control/main/common/print.h @@ -0,0 +1,69 @@ +/** + * FLL - Level 3 + * + * Project: Control + * API Version: 0.7 + * Licenses: lgpl-2.1-or-later + * + * Provides the common print functionality. + * + * This is auto-included and should not need to be explicitly included. + */ +#ifndef _control_main_common_print_h +#define _control_main_common_print_h + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * A special array of strings intended for representing funciton names. + * + * These are primarily used for printing errors with the function names. + * + * The macro macro_control_f() is used to reference the array index by the enum name. + * + * macro_control_f(): + * - name: The name of the function. + */ +#ifndef _di_control_f_a_ + extern const f_string_t control_f_a[]; + + #define macro_control_f(name) control_f_a[control_f_##name##_e] +#endif // _di_control_f_a_ + +/** + * An enum representing specific indexes within the above array. + * + * This is a convenience enum used to make code slightly more readable. + */ +#ifndef _di_control_f_e_ + enum { + control_f_control_packet_build_e, + control_f_control_packet_process_e, + control_f_control_packet_receive_e, + control_f_control_packet_send_e, + control_f_f_console_parameter_process_e, + control_f_f_file_exists_e, + control_f_f_file_stream_open_e, + control_f_f_file_stream_read_e, + control_f_f_fss_apply_delimit_e, + control_f_f_memory_array_increase_by_e, + control_f_f_memory_array_resize_e, + control_f_f_socket_connect_e, + control_f_f_socket_create_e, + control_f_f_string_dynamic_append_e, + control_f_f_string_dynamic_append_assure_e, + control_f_f_string_dynamic_partial_append_nulless_e, + control_f_f_thread_create_e, + control_f_fll_fss_extended_read_e, + control_f_fll_program_parameter_process_context_standard_e, + control_f_fll_program_parameter_process_verbosity_standard_e, + }; // enum +#endif // _di_control_f_e_ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _control_main_common_print_h diff --git a/sources/c/program/control/main/common/string.c b/sources/c/program/control/main/common/string.c new file mode 100644 index 0000000..a9ab36b --- /dev/null +++ b/sources/c/program/control/main/common/string.c @@ -0,0 +1,71 @@ +#include "../control.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _di_control_program_version_s_ + const f_string_static_t control_program_version_s = macro_f_string_static_t_initialize_1(CONTROL_program_version_s, 0, CONTROL_program_version_s_length); +#endif // _di_control_program_version_s_ + +#ifndef _di_control_program_name_s_ + const f_string_static_t control_program_name_s = macro_f_string_static_t_initialize_1(CONTROL_program_name_s, 0, CONTROL_program_name_s_length); + const f_string_static_t control_program_name_long_s = macro_f_string_static_t_initialize_1(CONTROL_program_name_long_s, 0, CONTROL_program_name_long_s_length); +#endif // _di_control_program_name_s_ + +#ifndef _di_control_parameter_s_ + const f_string_static_t control_short_name_s = macro_f_string_static_t_initialize_1(CONTROL_short_name_s, 0, CONTROL_short_name_s_length); + const f_string_static_t control_short_return_s = macro_f_string_static_t_initialize_1(CONTROL_short_return_s, 0, CONTROL_short_return_s_length); + const f_string_static_t control_short_settings_s = macro_f_string_static_t_initialize_1(CONTROL_short_settings_s, 0, CONTROL_short_settings_s_length); + const f_string_static_t control_short_socket_s = macro_f_string_static_t_initialize_1(CONTROL_short_socket_s, 0, CONTROL_short_socket_s_length); + + const f_string_static_t control_long_name_s = macro_f_string_static_t_initialize_1(CONTROL_long_name_s, 0, CONTROL_long_name_s_length); + const f_string_static_t control_long_return_s = macro_f_string_static_t_initialize_1(CONTROL_long_return_s, 0, CONTROL_long_return_s_length); + const f_string_static_t control_long_settings_s = macro_f_string_static_t_initialize_1(CONTROL_long_settings_s, 0, CONTROL_long_settings_s_length); + const f_string_static_t control_long_socket_s = macro_f_string_static_t_initialize_1(CONTROL_long_socket_s, 0, CONTROL_long_socket_s_length); +#endif // _di_control_parameter_s_ + +#ifndef _di_control_strings_s_ + const f_string_static_t control_path_settings_s = macro_f_string_static_t_initialize_1(CONTROL_path_settings_s, 0, CONTROL_path_settings_s_length); + + const f_string_static_t control_action_s = macro_f_string_static_t_initialize_1(CONTROL_action_s, 0, CONTROL_action_s_length); + const f_string_static_t control_at_s = macro_f_string_static_t_initialize_1(CONTROL_at_s, 0, CONTROL_at_s_length); + const f_string_static_t control_controller_s = macro_f_string_static_t_initialize_1(CONTROL_controller_s, 0, CONTROL_controller_s_length); + const f_string_static_t control_default_s = macro_f_string_static_t_initialize_1(CONTROL_default_s, 0, CONTROL_default_s_length); + const f_string_static_t control_error_s = macro_f_string_static_t_initialize_1(CONTROL_error_s, 0, CONTROL_error_s_length); + const f_string_static_t control_in_s = macro_f_string_static_t_initialize_1(CONTROL_in_s, 0, CONTROL_in_s_length); + const f_string_static_t control_init_s = macro_f_string_static_t_initialize_1(CONTROL_init_s, 0, CONTROL_init_s_length); + const f_string_static_t control_kexec_s = macro_f_string_static_t_initialize_1(CONTROL_kexec_s, 0, CONTROL_kexec_s_length); + const f_string_static_t control_length_s = macro_f_string_static_t_initialize_1(CONTROL_length_s, 0, CONTROL_length_s_length); + const f_string_static_t control_name_socket_s = macro_f_string_static_t_initialize_1(CONTROL_name_socket_s, 0, CONTROL_name_socket_s_length); + const f_string_static_t control_now_s = macro_f_string_static_t_initialize_1(CONTROL_now_s, 0, CONTROL_now_s_length); + const f_string_static_t control_path_socket_s = macro_f_string_static_t_initialize_1(CONTROL_path_socket_s, 0, CONTROL_path_socket_s_length); + const f_string_static_t control_path_socket_prefix_s = macro_f_string_static_t_initialize_1(CONTROL_path_socket_prefix_s, 0, CONTROL_path_socket_prefix_s_length); + const f_string_static_t control_path_socket_suffix_s = macro_f_string_static_t_initialize_1(CONTROL_path_socket_suffix_s, 0, CONTROL_path_socket_suffix_s_length); + const f_string_static_t control_status_s = macro_f_string_static_t_initialize_1(CONTROL_status_s, 0, CONTROL_status_s_length); + const f_string_static_t control_type_s = macro_f_string_static_t_initialize_1(CONTROL_type_s, 0, CONTROL_type_s_length); + + const f_string_static_t control_freeze_s = macro_f_string_static_t_initialize_1(CONTROL_freeze_s, 0, CONTROL_freeze_s_length); + const f_string_static_t control_kill_s = macro_f_string_static_t_initialize_1(CONTROL_kill_s, 0, CONTROL_kill_s_length); + const f_string_static_t control_pause_s = macro_f_string_static_t_initialize_1(CONTROL_pause_s, 0, CONTROL_pause_s_length); + const f_string_static_t control_reboot_s = macro_f_string_static_t_initialize_1(CONTROL_reboot_s, 0, CONTROL_reboot_s_length); + const f_string_static_t control_reload_s = macro_f_string_static_t_initialize_1(CONTROL_reload_s, 0, CONTROL_reload_s_length); + const f_string_static_t control_rerun_s = macro_f_string_static_t_initialize_1(CONTROL_rerun_s, 0, CONTROL_rerun_s_length); + const f_string_static_t control_restart_s = macro_f_string_static_t_initialize_1(CONTROL_restart_s, 0, CONTROL_restart_s_length); + const f_string_static_t control_resume_s = macro_f_string_static_t_initialize_1(CONTROL_resume_s, 0, CONTROL_resume_s_length); + const f_string_static_t control_shutdown_s = macro_f_string_static_t_initialize_1(CONTROL_shutdown_s, 0, CONTROL_shutdown_s_length); + const f_string_static_t control_start_s = macro_f_string_static_t_initialize_1(CONTROL_start_s, 0, CONTROL_start_s_length); + const f_string_static_t control_stop_s = macro_f_string_static_t_initialize_1(CONTROL_stop_s, 0, CONTROL_stop_s_length); + const f_string_static_t control_thaw_s = macro_f_string_static_t_initialize_1(CONTROL_thaw_s, 0, CONTROL_thaw_s_length); +#endif // _di_control_strings_s_ + +#ifndef _di_controller_strings_s_ + const f_string_static_t controller_name_socket_s = macro_f_string_static_t_initialize_1(CONTROLLER_name_socket_s, 0, CONTROLLER_name_socket_s_length); + const f_string_static_t controller_path_socket_s = macro_f_string_static_t_initialize_1(CONTROLLER_path_socket_s, 0, CONTROLLER_path_socket_s_length); + const f_string_static_t controller_path_socket_prefix_s = macro_f_string_static_t_initialize_1(CONTROLLER_path_socket_prefix_s, 0, CONTROLLER_path_socket_prefix_s_length); + const f_string_static_t controller_path_socket_suffix_s = macro_f_string_static_t_initialize_1(CONTROLLER_path_socket_suffix_s, 0, CONTROLLER_path_socket_suffix_s_length); +#endif // _di_controller_strings_s_ + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/sources/c/program/control/main/common/string.h b/sources/c/program/control/main/common/string.h new file mode 100644 index 0000000..c7c532b --- /dev/null +++ b/sources/c/program/control/main/common/string.h @@ -0,0 +1,273 @@ +/** + * FLL - Level 3 + * + * Project: Control + * API Version: 0.7 + * Licenses: lgpl-2.1-or-later + * + * Provides the common string structures. + * + * This is auto-included and should not need to be explicitly included. + */ +#ifndef _control_main_common_string_h +#define _control_main_common_string_h + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The program version. + */ +#ifndef _di_control_program_version_s_ + #define CONTROL_program_version_major_s F_string_ascii_0_s + #define CONTROL_program_version_minor_s F_string_ascii_7_s + #define CONTROL_program_version_micro_s F_string_ascii_0_s + + #define CONTROL_program_version_major_s_length F_string_ascii_0_s_length + #define CONTROL_program_version_minor_s_length F_string_ascii_7_s_length + #define CONTROL_program_version_micro_s_length F_string_ascii_0_s_length + + #if !(defined(CONTROL_program_version_nano_prefix_s) && defined(CONTROL_program_version_nano_prefix_s_length)) + #define CONTROL_program_version_nano_prefix_s + #define CONTROL_program_version_nano_prefix_s_length 0 + #endif // !(defined(CONTROL_program_version_nano_prefix_s) && defined(CONTROL_program_version_nano_prefix_s_length)) + + #if !(defined(CONTROL_program_version_nano_s) && defined(CONTROL_program_version_nano_s_length)) + #define CONTROL_program_version_nano_s + #define CONTROL_program_version_nano_s_length 0 + #endif // !(defined(CONTROL_program_version_nano_s) && defined(CONTROL_program_version_nano_s_length)) + + #define CONTROL_program_version_s CONTROL_program_version_major_s F_string_ascii_period_s CONTROL_program_version_minor_s F_string_ascii_period_s CONTROL_program_version_micro_s CONTROL_program_version_nano_prefix_s CONTROL_program_version_nano_s + + #define CONTROL_program_version_s_length CONTROL_program_version_major_s_length + F_string_ascii_period_s_length + CONTROL_program_version_minor_s_length + F_string_ascii_period_s_length + CONTROL_program_version_micro_s_length + CONTROL_program_version_nano_prefix_s_length + CONTROL_program_version_nano_s_length + + extern const f_string_static_t control_program_version_s; +#endif // _di_control_program_version_s_ + +/** + * The program name. + */ +#ifndef _di_control_program_name_s_ + #define CONTROL_program_name_s "control" + #define CONTROL_program_name_long_s "Control Program" + + #define CONTROL_program_name_s_length 7 + #define CONTROL_program_name_long_s_length 15 + + extern const f_string_static_t control_program_name_s; + extern const f_string_static_t control_program_name_long_s; +#endif // _di_control_program_name_s_ + +/** + * The main program parameters. + */ +#ifndef _di_control_parameter_s_ + #define CONTROL_short_name_s "n" + #define CONTROL_short_return_s "R" + #define CONTROL_short_settings_s "s" + #define CONTROL_short_socket_s "k" + + #define CONTROL_long_name_s "name" + #define CONTROL_long_return_s "return" + #define CONTROL_long_settings_s "settings" + #define CONTROL_long_socket_s "socket" + + #define CONTROL_short_name_s_length 1 + #define CONTROL_short_return_s_length 1 + #define CONTROL_short_settings_s_length 1 + #define CONTROL_short_socket_s_length 1 + + #define CONTROL_long_name_s_length 4 + #define CONTROL_long_return_s_length 6 + #define CONTROL_long_settings_s_length 8 + #define CONTROL_long_socket_s_length 6 + + extern const f_string_static_t control_short_name_s; + extern const f_string_static_t control_short_return_s; + extern const f_string_static_t control_short_settings_s; + extern const f_string_static_t control_short_socket_s; + + extern const f_string_static_t control_long_name_s; + extern const f_string_static_t control_long_return_s; + extern const f_string_static_t control_long_settings_s; + extern const f_string_static_t control_long_socket_s; +#endif // _di_control_parameter_s_ + +/** + * All special strings used within this program. + */ +#ifndef _di_control_strings_s_ + #if defined(_override_control_path_settings_) && defined(_override_control_path_settings_length_) + #define CONTROL_path_settings_s _override_control_path_settings_ + + #define CONTROL_path_settings_s_length _override_control_path_settings_length_ + #else + #define CONTROL_path_settings_s "/etc/control/settings" + + #define CONTROL_path_settings_s_length 21 + #endif // defined(_override_control_path_settings_) && defined(_override_control_path_settings_length_) + + #define CONTROL_action_s "action" + #define CONTROL_at_s "at" + #define CONTROL_controller_s "controller" + #define CONTROL_default_s "default" + #define CONTROL_error_s "error" + #define CONTROL_kexec_s "kexec" + #define CONTROL_in_s "in" + #define CONTROL_init_s "init" + #define CONTROL_length_s "length" + #define CONTROL_name_socket_s "name_socket" + #define CONTROL_now_s "now" + #define CONTROL_path_socket_s "path_socket" + #define CONTROL_path_socket_prefix_s "path_socket_prefix" + #define CONTROL_path_socket_suffix_s "path_socket_suffix" + #define CONTROL_status_s "status" + #define CONTROL_type_s "type" + + #define CONTROL_freeze_s "freeze" + #define CONTROL_kill_s "kill" + #define CONTROL_pause_s "pause" + #define CONTROL_reboot_s "reboot" + #define CONTROL_reload_s "reload" + #define CONTROL_rerun_s "rerun" + #define CONTROL_restart_s "restart" + #define CONTROL_resume_s "resume" + #define CONTROL_shutdown_s "shutdown" + #define CONTROL_start_s "start" + #define CONTROL_stop_s "stop" + #define CONTROL_thaw_s "thaw" + + #define CONTROL_action_s_length 6 + #define CONTROL_at_s_length 2 + #define CONTROL_controller_s_length 10 + #define CONTROL_default_s_length 7 + #define CONTROL_error_s_length 5 + #define CONTROL_in_s_length 2 + #define CONTROL_init_s_length 4 + #define CONTROL_kexec_s_length 5 + #define CONTROL_length_s_length 6 + #define CONTROL_name_socket_s_length 11 + #define CONTROL_now_s_length 3 + #define CONTROL_path_socket_s_length 11 + #define CONTROL_path_socket_prefix_s_length 18 + #define CONTROL_path_socket_suffix_s_length 18 + #define CONTROL_status_s_length 6 + #define CONTROL_type_s_length 4 + + #define CONTROL_freeze_s_length 6 + #define CONTROL_kill_s_length 4 + #define CONTROL_pause_s_length 5 + #define CONTROL_reboot_s_length 6 + #define CONTROL_reload_s_length 6 + #define CONTROL_rerun_s_length 5 + #define CONTROL_restart_s_length 7 + #define CONTROL_resume_s_length 6 + #define CONTROL_shutdown_s_length 8 + #define CONTROL_start_s_length 5 + #define CONTROL_stop_s_length 4 + #define CONTROL_thaw_s_length 4 + + extern const f_string_static_t control_path_settings_s; + + extern const f_string_static_t control_action_s; + extern const f_string_static_t control_at_s; + extern const f_string_static_t control_controller_s; + extern const f_string_static_t control_default_s; + extern const f_string_static_t control_error_s; + extern const f_string_static_t control_in_s; + extern const f_string_static_t control_init_s; + extern const f_string_static_t control_kexec_s; + extern const f_string_static_t control_length_s; + extern const f_string_static_t control_name_socket_s; + extern const f_string_static_t control_now_s; + extern const f_string_static_t control_path_socket_s; + extern const f_string_static_t control_path_socket_prefix_s; + extern const f_string_static_t control_path_socket_suffix_s; + extern const f_string_static_t control_status_s; + extern const f_string_static_t control_type_s; + + extern const f_string_static_t control_freeze_s; + extern const f_string_static_t control_kill_s; + extern const f_string_static_t control_pause_s; + extern const f_string_static_t control_reboot_s; + extern const f_string_static_t control_reload_s; + extern const f_string_static_t control_rerun_s; + extern const f_string_static_t control_restart_s; + extern const f_string_static_t control_resume_s; + extern const f_string_static_t control_shutdown_s; + extern const f_string_static_t control_start_s; + extern const f_string_static_t control_stop_s; + extern const f_string_static_t control_thaw_s; +#endif // _di_control_strings_s_ + +/** + * Controller defines that the control program utilizes. + * + * These are intended to exactly match the relevant controller string defines. + */ +#ifndef _di_controller_strings_s_ + + // The name_socket is a system-specific path and should match the path compiled into the controller program. + #if defined(_override_controller_name_socket_) && defined(_override_controller_name_socket_length_) + #define CONTROLLER_name_socket_s _override_controller_name_socket_ + + #define CONTROLLER_name_socket_s_length _override_controller_name_socket_length_ + #else + #define CONTROLLER_name_socket_s "default" + + #define CONTROLLER_name_socket_s_length 7 + #endif // defined(_override_controller_name_socket_) && defined(_override_controller_name_socket_length_) + + // The path_socket is a system-specific path and should match the path compiled into the controller program. + #if defined(_override_controller_path_socket_) && defined(_override_controller_path_socket_length_) + #define CONTROLLER_path_socket_s _override_controller_path_socket_ + + #define CONTROLLER_path_socket_s_length _override_controller_path_socket_length_ + #elif defined(_controller_as_init_) + #define CONTROLLER_path_socket_s "/var/run/init" + + #define CONTROLLER_path_socket_s_length 13 + #else + #define CONTROLLER_path_socket_s "/var/run/controller" + + #define CONTROLLER_path_socket_s_length 19 + #endif // defined(_override_controller_path_socket_) && defined(_override_controller_path_socket_length_) + + // The name_socket_prefix is a system-specific path and should match the path compiled into the controller program. + #if defined(_override_controller_path_socket_prefix_) && defined(_override_controller_path_socket_prefix_length_) + #define CONTROLLER_path_socket_prefix_s _override_controller_path_socket_prefix_ + + #define CONTROLLER_path_socket_prefix_s_length _override_controller_path_socket_prefix_length_ + #elif defined(_controller_as_init_) + #define CONTROLLER_path_socket_prefix_s "init-" + + #define CONTROLLER_path_socket_prefix_s_length 5 + #else + #define CONTROLLER_path_socket_prefix_s "" + + #define CONTROLLER_path_socket_prefix_s_length 0 + #endif // defined(_override_controller_name_socket_prefix_) && defined(_override_controller_name_socket_prefix_length_) + + // The name_socket_suffix is a system-specific path and should match the path compiled into the controller program. + #if defined(_override_controller_path_socket_suffix_) && defined(_override_controller_path_socket_suffix_length_) + #define CONTROLLER_path_socket_suffix_s _override_controller_path_socket_suffix_ + + #define CONTROLLER_path_socket_suffix_s_length _override_controller_path_socket_suffix_length_ + #else + #define CONTROLLER_path_socket_suffix_s ".socket" + + #define CONTROLLER_path_socket_suffix_s_length 7 + #endif // defined(_override_controller_name_socket_suffix_) && defined(_override_controller_name_socket_suffix_length_) + + extern const f_string_static_t controller_name_socket_s; + extern const f_string_static_t controller_path_socket_s; + extern const f_string_static_t controller_path_socket_prefix_s; + extern const f_string_static_t controller_path_socket_suffix_s; +#endif // _di_controller_strings_s_ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _control_main_common_string_h diff --git a/sources/c/program/control/main/common/type.c b/sources/c/program/control/main/common/type.c new file mode 100644 index 0000000..c688158 --- /dev/null +++ b/sources/c/program/control/main/common/type.c @@ -0,0 +1,47 @@ +#include "../control.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _di_control_main_delete_ + void control_main_delete(control_main_t * const main) { + + if (!main) return; + + fll_program_data_delete(&main->program); + control_setting_delete(&main->setting); + + f_memory_array_resize(0, sizeof(f_char_t), (void **) &main->cache.large.string, &main->cache.large.used, &main->cache.large.size); + f_memory_array_resize(0, sizeof(f_char_t), (void **) &main->cache.small.string, &main->cache.small.used, &main->cache.small.size); + f_memory_array_resize(0, sizeof(f_char_t), (void **) &main->cache.packet.string, &main->cache.packet.used, &main->cache.packet.size); + + f_memory_array_resize(0, sizeof(f_range_t), (void **) &main->cache.objects.array, &main->cache.objects.used, &main->cache.objects.size); + f_memory_arrays_resize(0, sizeof(f_ranges_t), (void **) &main->cache.contents.array, &main->cache.contents.used, &main->cache.contents.size, &f_rangess_delete_callback); + + f_memory_array_resize(0, sizeof(f_range_t), (void **) &main->cache.packet_objects.array, &main->cache.packet_objects.used, &main->cache.packet_objects.size); + f_memory_arrays_resize(0, sizeof(f_ranges_t), (void **) &main->cache.packet_contents.array, &main->cache.packet_contents.used, &main->cache.packet_contents.size, &f_rangess_delete_callback); + + f_memory_array_resize(0, sizeof(f_range_t), (void **) &main->cache.header_objects.array, &main->cache.header_objects.used, &main->cache.header_objects.size); + f_memory_arrays_resize(0, sizeof(f_ranges_t), (void **) &main->cache.header_contents.array, &main->cache.header_contents.used, &main->cache.header_contents.size, &f_rangess_delete_callback); + + f_memory_array_resize(0, sizeof(f_number_unsigned_t), (void **) &main->cache.delimits.array, &main->cache.delimits.used, &main->cache.delimits.size); + } +#endif // _di_control_main_delete_ + +#ifndef _di_control_setting_delete_ + void control_setting_delete(control_setting_t * const setting) { + + if (!setting) return; + + f_memory_array_resize(0, sizeof(f_char_t), (void **) &setting->name_socket.string, &setting->name_socket.used, &setting->name_socket.size); + f_memory_array_resize(0, sizeof(f_char_t), (void **) &setting->path_settings.string, &setting->path_settings.used, &setting->path_settings.size); + f_memory_array_resize(0, sizeof(f_char_t), (void **) &setting->path_socket.string, &setting->path_socket.used, &setting->path_socket.size); + + f_memory_arrays_resize(0, sizeof(f_string_dynamic_t), (void **) &setting->actions.array, &setting->actions.used, &setting->actions.size, &f_string_dynamics_delete_callback); + } +#endif // _di_control_setting_delete_ + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/sources/c/program/control/main/common/type.h b/sources/c/program/control/main/common/type.h new file mode 100644 index 0000000..fe623d4 --- /dev/null +++ b/sources/c/program/control/main/common/type.h @@ -0,0 +1,216 @@ +/** + * FLL - Level 3 + * + * Project: Control + * API Version: 0.7 + * Licenses: lgpl-2.1-or-later + * + * Provides the common type structures. + * + * This is auto-included and should not need to be explicitly included. + */ +#ifndef _control_main_common_type_h +#define _control_main_common_type_h + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The control cache. + * + * large: A buffer for storing large sets of data. + * small: A buffer for storing small sets of data. + * packet: A buffer for storing the send packet or the response payload. + * + * objects: An array of ranges representing objects. + * contents: An array of ranges representing contents. + * delimits: An array of ranges representing delimits. + * + * packet_objects: The FSS Objects for a packet. + * packet_contents: The FSS Contents for a packet. + * + * header_objects: The FSS Objects for a packet payload header. + * header_contents: The FSS Contents for a packet payload header. + * + * delimits: The delimits cache. + */ +#ifndef _di_control_cache_t_ + typedef struct { + f_string_dynamic_t large; + f_string_dynamic_t small; + f_string_dynamic_t packet; + + f_ranges_t objects; + f_rangess_t contents; + f_number_unsigneds_t delimits; + + f_ranges_t packet_objects; + f_rangess_t packet_contents; + + f_ranges_t header_objects; + f_rangess_t header_contents; + } control_cache_t; + + #define control_cache_t_initialize \ + { \ + f_string_dynamic_t_initialize, \ + f_string_dynamic_t_initialize, \ + f_string_dynamic_t_initialize, \ + f_ranges_t_initialize, \ + f_rangess_t_initialize, \ + f_number_unsigneds_t_initialize, \ + f_ranges_t_initialize, \ + f_rangess_t_initialize, \ + f_ranges_t_initialize, \ + f_rangess_t_initialize, \ + } +#endif // _di_control_cache_t_ + +/** + * The packet payload header data. + * + * The FSS-000E (Payload) supports multiple objects, but the Control packet does not support this, yet. + * + * action: The action type code, for any valid action (see: control_action_types enumeration). + * type: The packet type represented by the payload packet. + * status: The status code represented by the payload packet. + * length: The length of the payload content within the payload packet. + */ +#ifndef _di_control_payload_header_t_ + typedef struct { + uint8_t action; + uint8_t type; + f_status_t status; + uint16_t length; + } control_payload_header_t; + + #define control_payload_header_t_initialize \ + { \ + 0, \ + 0, \ + f_status_t_initialize, \ + f_number_unsigned_t_initialize, \ + } +#endif // _di_control_payload_header_t_ + +/** + * The control main program settings. + * + * This is passed to the program-specific main entry point to designate program settings. + * These program settings are often processed from the program arguments (often called the command line arguments). + * + * Properties: + * - flag: Flags passed to the main function. + * - action: The action type code. + * - size_write: The write size of a packet request or response. + * + * - status_signal: A status used eclusively by the threaded signal handler. + * - state: The state data used when processing the data. + * + * - range: A range for any particular use. + * - socket: A socket used to connect to the controller. + * + * - name_socket: The name of the socket file. + * - path_settings: The path to the settings file. + * - path_socket: The path to the socket file. + * - actions: The requested actions. + */ +#ifndef _di_control_setting_t_ + typedef struct { + uint16_t flag; + uint8_t action; + f_number_unsigned_t size_write; + + f_status_t status_signal; + f_state_t state; + + f_range_t range; + f_socket_t socket; + + f_string_dynamic_t name_socket; + f_string_dynamic_t path_settings; + f_string_dynamic_t path_socket; + f_string_dynamics_t actions; + } control_setting_t; + + #define control_setting_t_initialize \ + { \ + control_main_flag_none_e, \ + 0, \ + 0, \ + F_okay, \ + f_state_t_initialize, \ + f_range_t_initialize, \ + f_socket_t_initialize, \ + f_string_dynamic_t_initialize, \ + f_string_dynamic_t_initialize, \ + f_string_dynamic_t_initialize, \ + f_string_dynamics_t_initialize, \ + } +#endif // _di_control_setting_t_ + +/** + * The main program data as a single structure. + * + * Properties: + * - program: The main program data. + * - setting: The settings data. + * - cache: The cache data. + */ +#ifndef _di_control_main_t_ + typedef struct { + fll_program_data_t program; + control_setting_t setting; + control_cache_t cache; + } control_main_t; + + #define control_main_t_initialize \ + { \ + fll_program_data_t_initialize, \ + control_setting_t_initialize, \ + control_cache_t_initialize, \ + } +#endif // _di_control_main_t_ + +/** + * Deallocate main program data. + * + * @param main + * The main program data. + * + * Must not be NULL. + * + * This does not alter main.setting.state.status. + * + * @see f_memory_array_resize() + * @see f_memory_arrays_resize() + * @see fll_program_data_delete() + * @see control_setting_delete() + */ +#ifndef _di_control_main_delete_ + extern void control_main_delete(control_main_t * const main); +#endif // _di_control_main_delete_ + +/** + * Delete the program main setting data. + * + * @param setting + * The program main setting data. + * + * Must not be NULL. + * + * This does not alter setting.state.status. + * + * @see f_memory_array_resize() + * @see f_memory_arrays_resize() + */ +#ifndef _di_control_setting_delete_ + extern void control_setting_delete(control_setting_t * const setting); +#endif // _di_control_setting_delete_ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _control_main_common_type_h diff --git a/sources/c/program/control/main/control.c b/sources/c/program/control/main/control.c new file mode 100644 index 0000000..ee0540c --- /dev/null +++ b/sources/c/program/control/main/control.c @@ -0,0 +1,110 @@ +#include "control.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _di_control_main_ + void control_main(control_main_t * const main) { + + if (!main) return; + if (F_status_is_error(main->setting.state.status)) return; + + main->setting.state.status = F_okay; + + if (main->setting.flag & control_main_flag_version_copyright_help_e) { + if (main->setting.flag & control_main_flag_help_e) { + control_print_message_help(&main->program.message); + } + else if (main->setting.flag & control_main_flag_version_e) { + fll_program_print_version(&main->program.message, control_program_version_s); + } + else if (main->setting.flag & control_main_flag_copyright_e) { + fll_program_print_copyright(&main->program.message, fll_program_copyright_year_author_s); + } + + return; + } + + if (main->setting.flag & control_main_flag_pipe_e) { + control_print_error_pipe_supported_not(&main->program.error); + + main->setting.state.status = F_status_set_error(F_support_not); + } + else if (main->setting.actions.used) { + main->setting.action = control_action_type_identify(main->setting.actions.array[0]); + + if (main->setting.action) { + control_action_verify(main); + } + else { + control_print_error_parameter_action_not(&main->program.error, main->setting.actions.array[0]); + + main->setting.state.status = F_status_set_error(F_parameter); + } + + if (F_status_is_error_not(main->setting.state.status)) { + control_packet_build(main); + + if (F_status_is_error(main->setting.state.status)) { + if (F_status_set_fine(main->setting.state.status) == F_too_large) { + control_print_error_request_packet_too_large(&main->program.error); + } + else { + control_print_error(&main->program.error, macro_control_f(control_packet_build)); + } + } + + if (F_status_is_error_not(main->setting.state.status)) { + control_packet_send(main); + + if (F_status_is_error(main->setting.state.status)) { + control_print_error(&main->program.error, macro_control_f(control_packet_send)); + } + } + + if (F_status_is_error_not(main->setting.state.status)) { + control_payload_header_t header = control_payload_header_t_initialize; + + control_packet_receive(main, &header); + + if (F_status_is_error(main->setting.state.status)) { + if (F_status_set_fine(main->setting.state.status) == F_too_large) { + control_print_error_response_packet_valid_not(&main->program.error); + } + else { + control_print_error(&main->program.error, macro_control_f(control_packet_receive)); + } + } + else { + control_packet_process(main, &header); + + // Print the error message only if the error message is not already printed. + if (F_status_is_error(main->setting.state.status)) { + if (header.type != control_payload_type_error_e && (header.type != control_payload_type_controller_e || F_status_set_fine(main->setting.state.status) != header.status || (header.status != F_failure && header.status != F_busy))) { + control_print_error(&main->program.error, macro_control_f(control_packet_process)); + } + } + } + } + } + + if (main->setting.socket.id != -1) { + f_socket_disconnect(&main->setting.socket, f_socket_close_fast_e); + } + } + else { + control_print_error_parameter_actions_none(&main->program.error); + + main->setting.state.status = F_status_set_error(F_data_not); + } + + if (F_status_is_error(main->setting.state.status)) return; + + main->setting.state.status = F_okay; + } +#endif // _di_control_main_ + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/sources/c/program/control/main/control.h b/sources/c/program/control/main/control.h new file mode 100644 index 0000000..82e5f2e --- /dev/null +++ b/sources/c/program/control/main/control.h @@ -0,0 +1,102 @@ +/** + * FLL - Level 3 + * + * Project: Control + * API Version: 0.7 + * Licenses: lgpl-2.1-or-later + */ +#ifndef _control_main_control_h +#define _control_main_control_h + +// Libc includes. +#include + +// FLL-0 includes. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// FLL-1 includes. +#include +#include +#include +#include + +// FLL-2 includes. +#include +#include +#include +#include +#include +#include + +// Control includes. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Execute main program. + * + * If main.signal is non-zero, then this blocks and handles the following signals: + * - F_signal_abort + * - F_signal_broken_pipe + * - F_signal_hangup + * - F_signal_interrupt + * - F_signal_quit + * - F_signal_termination + * + * @param main + * The main program data and settings. + * + * This alters main.setting.state.status: + * F_okay on success. + * F_true on success when performing verification and verify passed. + * F_false on success when performing verification and verify failed. + * + * F_interrupt (with error bit) on (exit) signal received. + * F_parameter (with error bit) if main is NULL or setting is NULL. + */ +#ifndef _di_control_main_ + extern void control_main(control_main_t * const main); +#endif // _di_control_main_ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _control_main_control_h diff --git a/sources/c/program/control/main/main.c b/sources/c/program/control/main/main.c new file mode 100644 index 0000000..bfde226 --- /dev/null +++ b/sources/c/program/control/main/main.c @@ -0,0 +1,77 @@ +#include "control.h" + +int main(const int argc, const f_string_t *argv, const f_string_t *envp) { + + control_main_t data = control_main_t_initialize; + + data.program.debug.flag |= control_print_flag_debug_e | control_print_flag_out_e; + data.program.error.flag |= control_print_flag_error_e | control_print_flag_out_e; + data.program.output.flag |= control_print_flag_out_e; + data.program.message.flag |= control_print_flag_message_e | control_print_flag_out_e; + data.program.warning.flag |= control_print_flag_warning_e | control_print_flag_out_e; + data.program.error.custom = (void *) &data; + data.program.debug.custom = (void *) &data; + data.program.message.custom = (void *) &data; + data.program.output.custom = (void *) &data; + data.program.warning.custom = (void *) &data; + + f_console_parameter_t parameters[] = control_console_parameter_t_initialize; + + data.program.parameters.array = parameters; + data.program.parameters.used = control_parameter_total_d; + data.program.environment = envp; + + data.setting.socket.domain = f_socket_protocol_family_local_e; + data.setting.socket.type = f_socket_type_stream_e; + data.setting.socket.length = sizeof(struct sockaddr_un); + + if (f_pipe_input_exists()) { + data.program.pipe = fll_program_data_pipe_input_e; + } + + fll_program_standard_set_up(&data.program); + + f_file_umask_get(&data.program.umask); + + #ifdef _di_thread_support_ + { + const f_console_arguments_t arguments = macro_f_console_arguments_t_initialize_1(argc, argv, envp); + + control_setting_load(arguments, &data); + } + + control_main(&data); + #else + { + f_thread_id_t id_signal; + + memset(&id_signal, 0, sizeof(f_thread_id_t)); + + data.setting.state.status = f_thread_create(0, &id_signal, &control_thread_signal, (void *) &data); + + if (F_status_is_error(data.setting.state.status)) { + control_print_error(&data.program.error, macro_control_f(f_thread_create)); + } + else { + { + const f_console_arguments_t arguments = macro_f_console_arguments_t_initialize_1(argc, argv, envp); + + control_setting_load(arguments, &data); + } + + if (!control_signal_check(&data)) { + control_main(&data); + } + + f_thread_cancel(id_signal); + f_thread_join(id_signal, 0); + } + } + #endif // _di_thread_support_ + + control_main_delete(&data); + + fll_program_standard_set_down(&data.program); + + return (F_status_is_error(data.setting.state.status) || data.setting.state.status == F_false) ? 1 : 0; +} diff --git a/sources/c/program/control/main/main.h b/sources/c/program/control/main/main.h new file mode 100644 index 0000000..f66d83a --- /dev/null +++ b/sources/c/program/control/main/main.h @@ -0,0 +1,38 @@ +/** + * FLL - Level 3 + * + * Project: Control + * API Version: 0.7 + * Licenses: lgpl-2.1-or-later + * + * This file is only ever included by main/main.c and should not normally be included anywhere else. + * Anything that wants to include this should be providing the "control" program functionality in some manner. + */ +#ifndef _control_main_main_h +#define _control_main_main_h + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Standard program entry point. + * + * @param argc + * The number of arguments. + * @param argv + * The array of arguments. + * @param envp + * The array of all environment variables on program start. + * + * @return + * 0 on success. + * 1 on error. + */ +extern int main(const int argc, const f_string_t *argv, const f_string_t *envp); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _control_main_main_h diff --git a/sources/c/program/control/main/packet.c b/sources/c/program/control/main/packet.c new file mode 100644 index 0000000..ddb4ca5 --- /dev/null +++ b/sources/c/program/control/main/packet.c @@ -0,0 +1,559 @@ +#include "control.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _di_control_packet_build_ + void control_packet_build(control_main_t * const main) { + + if (!main) return; + + main->cache.packet.used = 0; + main->cache.large.used = 0; + main->cache.small.used = 0; + + f_number_unsigned_t i = 0; + + { + f_number_unsigned_t length = 5 + f_fss_header_s.used + f_fss_payload_s.used; + length += control_action_s.used + control_length_s.used + control_type_s.used; + length += (f_fss_payload_list_open_s.used + f_fss_payload_list_close_s.used) * 2; + length += (f_fss_payload_header_open_s.used + f_fss_payload_header_close_s.used) * 3; + length += main->setting.actions.array[0].used + f_string_ascii_0_s.used; + + // @todo This should properly handle escaping the FSS-0001 (Extended) Content and then count that length. + for (i = 1; i < main->setting.actions.used; ++i) { + length += f_fss_payload_header_next_s.used + main->setting.actions.array[i].used; + } // for + + if (main->setting.action == control_action_type_kexec_e || main->setting.action == control_action_type_reboot_e || main->setting.action == control_action_type_shutdown_e) { + length += control_init_s.used; + } + else { + length += control_controller_s.used; + } + + if (length > 0xffffffffu) { + main->setting.state.status = F_status_set_error(F_too_large); + + return; + } + + main->setting.state.status = f_memory_array_resize(length, sizeof(f_char_t), (void **) &main->cache.packet.string, &main->cache.packet.used, &main->cache.packet.size); + if (F_status_is_error(main->setting.state.status)) return; + } + + f_state_t state = macro_f_state_t_initialize_1(control_allocation_large_d, control_allocation_small_d, F_okay, 0, 0, 0, &fll_program_standard_signal_handle, 0, (void *) main, 0); + + f_string_static_t contents_array[main->setting.actions.used]; + f_string_statics_t contents = macro_f_string_statics_t_initialize_1(contents_array, 0, main->setting.actions.used); + + // The Packet Control Block. + { + f_char_t block_control = (f_char_t) control_packet_flag_binary_d; + + #ifdef _is_F_endian_big + block_control |= (f_char_t) control_packet_flag_endian_big_d; + #endif // _is_F_endian_big + + main->setting.state.status = f_string_append(&block_control, 1, &main->cache.packet); + if (F_status_is_error(main->setting.state.status)) return; + } + + // Reserve the Packet Size Block to be calculated later. + main->cache.packet.used = 5; + + // Payload Header: type. + if (main->setting.action == control_action_type_kexec_e || main->setting.action == control_action_type_reboot_e || main->setting.action == control_action_type_shutdown_e) { + contents_array[0] = control_init_s; + } + else { + contents_array[0] = control_controller_s; + } + + contents.used = 1; + + fll_fss_extended_write(control_type_s, contents, 0, &main->cache.large, &state); + + // Payload Header: action. + if (F_status_is_error_not(state.status)) { + for (contents.used = 0; contents.used < main->setting.actions.used; ++contents.used) { + contents_array[contents.used] = main->setting.actions.array[contents.used]; + } // for + + fll_fss_extended_write(control_action_s, contents, 0, &main->cache.large, &state); + } + + // Payload Header: length. + if (F_status_is_error_not(state.status)) { + contents_array[0] = f_string_ascii_0_s; + contents.used = 1; + + fll_fss_extended_write(control_length_s, contents, 0, &main->cache.large, &state); + } + + // Payload Packet: Header. + if (F_status_is_error_not(state.status)) { + fll_fss_payload_write(f_fss_header_s, main->cache.large, F_false, 0, &main->cache.packet, &state); + } + + // Payload Packet: Payload. + if (F_status_is_error_not(state.status)) { + main->cache.large.used = 0; + + fll_fss_payload_write(f_fss_payload_s, main->cache.large, F_false, 0, &main->cache.packet, &state); + } + + if (F_status_is_error(state.status)) { + main->setting.state.status = state.status; + + return; + } + + // Construct Packet Size Block. + #ifdef _is_F_endian_big + main->cache.packet.string[1] = main->cache.packet.used & 0xffu; + main->cache.packet.string[2] = main->cache.packet.used & 0xff00u; + main->cache.packet.string[3] = main->cache.packet.used & 0xff0000u; + main->cache.packet.string[4] = main->cache.packet.used & 0xff000000u; + #else + main->cache.packet.string[1] = main->cache.packet.used & 0xff000000u; + main->cache.packet.string[2] = main->cache.packet.used & 0xff0000u; + main->cache.packet.string[3] = main->cache.packet.used & 0xff00u; + main->cache.packet.string[4] = main->cache.packet.used & 0xffu; + #endif // _is_F_endian_big + + main->setting.state.status = F_okay; + } +#endif // _di_control_packet_build_ + +#ifndef _di_control_packet_header_flag_ + uint8_t control_packet_header_flag(const uint8_t buffer[]) { + return (uint8_t) (((buffer[0] & 0x8u) ? control_packet_flag_binary_d : 0) | ((buffer[0] & 0x4u) ? control_packet_flag_endian_big_d : 0)); + } +#endif // _di_control_packet_header_flag_ + +#ifndef _di_control_packet_header_length_ + uint32_t control_packet_header_length(const bool is_big, const uint8_t buffer[]) { + + #ifdef _is_F_endian_big + if (is_big) return (buffer[1] << 24u) | (buffer[2] << 16u) | (buffer[3] << 8u) | buffer[4]; + #else + if (!is_big) return (buffer[1] << 24u) | (buffer[2] << 16u) | (buffer[3] << 8u) | buffer[4]; + #endif // _is_F_endian_big + + return (buffer[4] << 24u) | (buffer[3] << 16u) | (buffer[2] << 8u) | buffer[1]; + } +#endif // _di_control_packet_header_length_ + +#ifndef _di_control_packet_receive_ + void control_packet_receive(control_main_t * const main, control_payload_header_t * const header) { + + if (!main || !header) return; + + main->cache.large.used = 0; + main->cache.small.used = 0; + main->cache.packet.used = 0; + main->cache.packet_objects.used = 0; + main->cache.packet_contents.used = 0; + main->cache.header_objects.used = 0; + main->cache.header_contents.used = 0; + main->cache.delimits.used = 0; + + header->action = 0; + header->type = 0; + header->status = F_okay; + header->length = 0; + + f_number_unsigned_t i = 0; + f_range_t range_header = f_range_t_initialize; + + { + f_number_unsigned_t length = 5; + uint8_t head[length]; + + memset(head, 0, sizeof(uint8_t) * length); + + main->setting.state.status = f_socket_read(&main->setting.socket, f_socket_flag_peek_e, (void *) head, &length); + if (F_status_is_error(main->setting.state.status)) return; + + if (length < 5) { + main->setting.state.status = F_status_set_error(F_packet_not); + + return; + } + + // Only the first two bits of the 8 Control bits are allowed to be set to 1 for this Packet. + if (head[0] & (~(control_packet_flag_binary_d | control_packet_flag_endian_big_d))) { + main->setting.state.status = F_status_set_error(F_packet_not); + + return; + } + + length = control_packet_header_length(head[0] & control_packet_flag_endian_big_d, head); + + if (length > 0xffffffffu) { + main->setting.state.status = F_status_set_error(F_too_large); + + return; + } + + main->setting.state.status = f_memory_array_increase_by(length, sizeof(f_char_t), (void **) &main->cache.large.string, &main->cache.large.used, &main->cache.large.size); + if (F_status_is_error(main->setting.state.status)) return; + + main->setting.state.status = f_socket_read(&main->setting.socket, f_socket_flag_wait_all_e, (void *) head, &length); + if (F_status_is_error(main->setting.state.status)) return; + + if (length < main->cache.large.used) { + main->setting.state.status = F_status_set_error(F_too_small); + + return; + } + + if (length > main->cache.large.used) { + main->setting.state.status = F_status_set_error(F_too_large); + + return; + } + } + + { + f_state_t state = macro_f_state_t_initialize_1(control_allocation_large_d, control_allocation_small_d, F_okay, 0, 0, 0, &fll_program_standard_signal_handle, 0, (void *) main, 0); + f_range_t range_packet = macro_f_range_t_initialize_2(main->cache.large.used); + + fll_fss_basic_list_read(main->cache.large, &range_packet, &main->cache.packet_objects, &main->cache.packet_contents, &main->cache.delimits, 0, 0, &state); + + if (F_status_is_error(main->setting.state.status)) { + control_print_debug_packet_message(&main->program.debug, "Failure while reading FSS Basic List in the response packet", 0, 0); + + if (F_status_set_fine(main->setting.state.status) == F_memory_not) return; + + main->setting.state.status = F_status_set_error(F_header); + + return; + } + + f_fss_apply_delimit(main->cache.delimits, &main->cache.large, &state); + + if (F_status_is_error(state.status)) { + main->setting.state.status = state.status; + + control_print_debug_packet_message(&main->program.debug, "Failure while processing delimits for the FSS Basic List in the response packet", 0, 0); + + main->setting.state.status = F_status_set_error(F_header); + + return; + } + + main->cache.delimits.used = 0; + + { + f_ranges_t *content_header = 0; + f_ranges_t *content_payload = 0; + + for (; i < main->cache.packet_objects.used; ++i) { + + if (f_compare_dynamic_partial_string(f_fss_header_s.string, main->cache.large, f_fss_header_s.used, main->cache.packet_objects.array[i]) == F_equal_to) { + + // The FSS-000E (Payload) standard does not prohibit multiple "header", but such cases are not supported by the controller and the control programs. + if (content_header) { + control_print_debug_packet_message(&main->program.debug, "Multiple %[" F_fss_header_s "%] found in response packet", 0, 0); + + main->setting.state.status = F_status_set_error(F_payload_not); + + return; + } + + content_header = &main->cache.packet_contents.array[i]; + } + else if (f_compare_dynamic_partial_string(f_fss_payload_s.string, main->cache.large, f_fss_payload_s.used, main->cache.packet_objects.array[i]) == F_equal_to) { + + // Only a single "payload" is supported by the FSS-000E (Payload) standard. + if (content_payload) { + control_print_debug_packet_message(&main->program.debug, "Multiple %[" F_fss_payload_s "%] found in response packet", 0, 0); + + main->setting.state.status = F_status_set_error(F_payload_not); + + return; + } + + if (i + 1 < main->cache.packet_contents.used) { + control_print_debug_packet_message(&main->program.debug, "Invalid FSS Payload format, the %[" F_fss_payload_s "%] is required to be the last FSS Basic List Object", 0, 0); + + main->setting.state.status = F_status_set_error(F_payload_not); + + return; + } + + content_payload = &main->cache.packet_contents.array[i]; + } + } // for + + if (!content_header) { + control_print_debug_packet_message(&main->program.debug, "Did not find a %[" F_fss_header_s "%] in the response packet", 0, 0); + + main->setting.state.status = F_status_set_error(F_payload_not); + + return; + } + + if (!content_payload) { + control_print_debug_packet_message(&main->program.debug, "Did not find a %[" F_fss_payload_s "%] in the response packet", 0, 0); + + main->setting.state.status = F_status_set_error(F_payload_not); + + return; + } + + range_header = content_header->array[0]; + } + + { + // 0x1 = found action, 0x2 = found length, 0x4 = found status, 0x8 = found_type. + uint8_t found = 0; + f_number_unsigned_t number = 0; + f_range_t range = range_header; + + fll_fss_extended_read(main->cache.large, &range, &main->cache.header_objects, &main->cache.header_contents, 0, 0, &main->cache.delimits, 0, &state); + + if (F_status_is_error(main->setting.state.status)) { + control_print_debug_packet_message(&main->program.debug, "Failure while reading FSS Extended in the response packet", 0, 0); + + if (F_status_set_fine(main->setting.state.status) == F_memory_not) return; + + main->setting.state.status = F_status_set_error(F_header_not); + + return; + } + + f_fss_apply_delimit(main->cache.delimits, &main->cache.large, &state); + + if (F_status_is_error(state.status)) { + main->setting.state.status = state.status; + + control_print_debug_packet_message(&main->program.debug, "Failure while processing delimits for the FSS Basic List in the response packet", 0, 0); + + main->setting.state.status = F_status_set_error(F_header_not); + + return; + } + + if (!main->cache.header_contents.used) { + control_print_debug_packet_message(&main->program.debug, "Did not find any Content within the %[" F_fss_header_s "%]", 0, 0); + + main->setting.state.status = F_status_set_error(F_header_not); + + return; + } + + for (i = 0; i < main->cache.header_objects.used; ++i) { + + if (f_compare_dynamic_partial_string(control_action_s.string, main->cache.large, control_action_s.used, main->cache.header_objects.array[i]) == F_equal_to) { + if (!(found & 0x1)) { + const f_number_unsigned_t action_length = (main->cache.header_contents.array[i].array[0].stop - main->cache.header_contents.array[i].array[0].start) + 1; + char action_string[action_length + 1]; + const f_string_static_t action = macro_f_string_static_t_initialize_1(action_string, 0, action_length); + + memcpy(action_string, main->cache.large.string + main->cache.header_contents.array[i].array[0].start, action_length); + action_string[action_length] = 0; + + found |= 0x1; + + control_print_debug_packet_header_object_and_content(&main->program.debug, control_action_s, main->cache.large, main->cache.header_contents.array[i].array[0]); + + header->action = control_action_type_identify(action); + + if (!header->action) { + control_print_debug_packet_message(&main->program.debug, "Failed to identify %[" CONTROL_action_s "%] from: ", &main->cache.large, &main->cache.header_contents.array[i].array[0]); + + main->setting.state.status = F_status_set_error(F_header_not); + + return; + } + } + else { + control_print_warning_packet_header_duplicate_object(&main->program.warning, control_action_s); + } + } + else if (f_compare_dynamic_partial_string(control_length_s.string, main->cache.large, control_length_s.used, main->cache.header_objects.array[i]) == F_equal_to) { + if (!(found & 0x2)) { + found |= 0x2; + number = 0; + + control_print_debug_packet_header_object_and_content(&main->program.debug, control_length_s, main->cache.large, main->cache.header_contents.array[i].array[0]); + + main->setting.state.status = fl_conversion_dynamic_partial_to_unsigned_detect(fl_conversion_data_base_10_c, main->cache.large, main->cache.header_contents.array[i].array[0], &number); + + if (F_status_is_error(main->setting.state.status)) { + control_print_debug_packet_message(&main->program.debug, "Failed to process number for %[" CONTROL_length_s "%] in the response packet, number is:", &main->cache.large, &main->cache.header_contents.array[i].array[0]); + + main->setting.state.status = F_status_set_error(F_header_not); + + return; + } + + if (number > F_type_size_max_32_unsigned_d) { + control_print_debug_packet_message(&main->program.debug, "Processed number for %[" CONTROL_length_s "%] exceeds allowed size in the response packet, number is:", &main->cache.large, &main->cache.header_contents.array[i].array[0]); + + main->setting.state.status = F_status_set_error(F_header_not); + + return; + } + + header->length = (uint16_t) number; + } + else { + control_print_warning_packet_header_duplicate_object(&main->program.warning, control_length_s); + } + } + else if (f_compare_dynamic_partial_string(control_status_s.string, main->cache.large, control_status_s.used, main->cache.header_objects.array[i]) == F_equal_to) { + if (!(found & 0x4)) { + found |= 0x4; + number = 0; + + control_print_debug_packet_header_object_and_content(&main->program.debug, control_status_s, main->cache.large, main->cache.header_contents.array[i].array[0]); + + // Attempt to get packet status as a number. + main->setting.state.status = fl_conversion_dynamic_partial_to_unsigned_detect(fl_conversion_data_base_10_c, main->cache.large, main->cache.header_contents.array[i].array[0], &number); + + if (F_status_set_fine(main->setting.state.status) == F_number) { + + // Not a number, so attempt get by packet status string name. + const f_number_unsigned_t name_length = (main->cache.header_contents.array[i].array[0].stop - main->cache.header_contents.array[i].array[0].start) + 1; + char name_string[name_length + 1]; + const f_string_static_t name = macro_f_string_static_t_initialize_1(name_string, 0, name_length); + + memcpy(name_string, main->cache.large.string + main->cache.header_contents.array[i].array[0].start, name_length); + name_string[name_length] = 0; + + main->setting.state.status = fl_status_string_from(name, &header->status); + + if (F_status_is_error(main->setting.state.status)) { + control_print_debug_packet_message(&main->program.debug, "Failed to process %[" CONTROL_status_s "%] in the response packet, Content is:", &main->cache.large, &main->cache.header_contents.array[i].array[0]); + + main->setting.state.status = F_status_set_error(F_header_not); + + return; + } + } + else if (F_status_is_error(main->setting.state.status)) { + control_print_debug_packet_message(&main->program.debug, "Failed to process number for %[" CONTROL_status_s "%] in the response packet, number is:", &main->cache.large, &main->cache.header_contents.array[i].array[0]); + + if (F_status_set_fine(main->setting.state.status) == F_memory_not) return; + + main->setting.state.status = F_status_set_error(F_header_not); + + return; + } + else { + if (number > F_status_size_max_with_bits_d) { + control_print_debug_packet_message(&main->program.debug, "Processed number for %[" CONTROL_status_s "%] exceeds allowed size in the response packet, number is:", &main->cache.large, &main->cache.header_contents.array[i].array[0]); + + main->setting.state.status = F_status_set_error(F_header_not); + + return; + } + } + } + else { + control_print_warning_packet_header_duplicate_object(&main->program.warning, control_length_s); + } + } + else if (f_compare_dynamic_partial_string(control_type_s.string, main->cache.large, control_type_s.used, main->cache.header_objects.array[i]) == F_equal_to) { + if (!(found & 0x8)) { + found |= 0x8; + + control_print_debug_packet_header_object_and_content(&main->program.debug, control_type_s, main->cache.large, main->cache.header_contents.array[i].array[0]); + + if (f_compare_dynamic_partial_string(control_controller_s.string, main->cache.large, control_controller_s.used, main->cache.header_contents.array[i].array[0]) == F_equal_to) { + header->type = control_payload_type_controller_e; + } + else if (f_compare_dynamic_partial_string(control_error_s.string, main->cache.large, control_error_s.used, main->cache.header_contents.array[i].array[0]) == F_equal_to) { + header->type = control_payload_type_error_e; + } + else { + control_print_debug_packet_message(&main->program.debug, "Unknown %[" CONTROL_type_s "%] in response packet, Content is:", &main->cache.large, &main->cache.header_contents.array[i].array[0]); + + main->setting.state.status = F_status_set_error(F_header_not); + + return; + } + } + else { + control_print_warning_packet_header_duplicate_object(&main->program.warning, control_type_s); + } + } + } + } + } + + main->setting.state.status = F_okay; + } +#endif // _di_control_packet_receive_ + +#ifndef _di_control_packet_process_ + void control_packet_process(control_main_t * const main, control_payload_header_t * const header) { + + if (!main || !header) return; + + f_string_static_t string_status = f_string_static_t_initialize; + + { + main->setting.state.status = f_status_string_to(header->status, &string_status); + + if (F_status_is_error(main->setting.state.status)) { + control_print_warning_packet_process_string_to_failed(&main->program.warning, header->status, main->setting.state.status); + + return; + } + } + + if (main->setting.flag & control_main_flag_return_e) { + fll_print_format("response %q %q %q%r", main->program.output.to, control_payload_type_name(header->type), control_action_type_name(header->action), string_status, f_string_eol_s); + } + else if (header->type == control_payload_type_error_e) { + control_print_error_packet_response(&main->program.error, *header, string_status); + } + else if (header->status == F_failure) { + control_print_error_packet_response_failure(&main->program.error, *header, string_status); + } + else if (header->status == F_busy) { + control_print_warning_packet_response_busy(&main->program.warning, *header, string_status); + } + else if (header->status == F_done || header->status == F_success) { + control_print_message_packet_response(&main->program.output, *header, string_status); + } + else { + + // Set type to 0 to inform the caller to handle this error. + header->type = 0; + + main->setting.state.status = F_status_set_error(F_known_not); + + return; + } + + if (header->type == control_payload_type_error_e || header->status == F_failure || header->status == F_busy) { + main->setting.state.status = F_status_set_error(header->status); + } + else { + main->setting.state.status = header->status; + } + } +#endif // _di_control_packet_process_ + +#ifndef _di_control_packet_send_ + void control_packet_send(control_main_t * const main) { + + if (!main) return; + + main->setting.size_write = main->cache.packet.used; + + main->setting.state.status = f_socket_write(&main->setting.socket, 0, (void *) &main->cache.packet, 0); + } +#endif // _di_control_packet_send_ + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/sources/c/program/control/main/packet.h b/sources/c/program/control/main/packet.h new file mode 100644 index 0000000..f5fdefb --- /dev/null +++ b/sources/c/program/control/main/packet.h @@ -0,0 +1,155 @@ +/** + * FLL - Level 3 + * + * Project: Control + * API Version: 0.7 + * Licenses: lgpl-2.1-or-later + * + * Provides the packet functionality. + * + * This is auto-included and should not need to be explicitly included. + */ +#ifndef _control_main_packet_h +#define _control_main_packet_h + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Build the payload, storing it in the large cache. + * + * @param main + * The main program data. + * + * @return + * F_okay on success. + * + * F_too_large (with error bit) If the message is too large for the packet format to transmit. + * + * Errors (with error bit) from: f_memory_array_resize(). + * Errors (with error bit) from: f_string_append(). + * Errors (with error bit) from: f_string_dynamic_append(). + * Errors (with error bit) from: fll_fss_extended_write(). + * Errors (with error bit) from: fll_fss_payload_write(). + * + * @see f_memory_array_resize() + * @see f_string_append() + * @see f_string_dynamic_append() + */ +#ifndef _di_control_packet_build_ + extern void control_packet_build(control_main_t * const main); +#endif // _di_control_packet_build_ + +/** + * Given the header buffer, get the flag bits. + * + * @param buffer + * The buffer to read the length of and get the + * + * @return + * The 8-bit number representing the flags. + */ +#ifndef _di_control_packet_header_flag_ + extern uint8_t control_packet_header_flag(const uint8_t buffer[]); +#endif // _di_control_packet_header_flag_ + +/** + * Given the header buffer, get the length bits. + * + * The endianness is automatically detected and swapped by this function to guarantee host order bytes. + * + * @param is_big + * If TRUE, then the length in the buffer is in big endian format. + * If FALSE, then the length in the buffer is in little endian format. + * @param buffer + * The buffer to read the length of and get the + * + * @return + * The 32-bit number representing the length. + */ +#ifndef _di_control_packet_header_length_ + extern uint32_t control_packet_header_length(const bool is_big, const uint8_t buffer[]); +#endif // _di_control_packet_header_length_ + +/** + * Receive the response from the remote socket, storing it in the large cache. + * + * @todo consider returning F_header (with error bit) fo most header processing errors rather than individual status codes. + * + * @param main + * The main program data. + * + * This alters main.setting.state.status: + * F_okay on success. + * + * F_header_not (with error bit) If there is a problem processing the packet header. + * F_memory_not (with error bit) On out of memory issues (this is passed through from called functions). + * F_packet_not (with error bit) If the received packet is not a valid packet or not a supported packet structure. + * F_payload_not (with error bit) If there is a problem processing the packet payload. + * F_too_large (with error bit) If the received packet specifies a size that is too large or the actual size is larger than the specified size. + * F_too_small (with error bit) If the received packet actual size is smaller than the specified size. + * + * Errors (with error bit) from: f_memory_array_increase_by(). + * Errors (with error bit) from: f_socket_read(). + * Errors (with error bit) from: fl_conversion_dynamic_partial_to_unsigned_detect(). + * Errors (with error bit) from: fll_fss_extended_read(). + * Errors (with error bit) from: fll_fss_basic_list_read(). + * @param header + * The control payload packet header data. + * + * @see f_memory_array_increase_by() + * @see f_socket_read() + * @see fl_conversion_dynamic_partial_to_unsigned_detect() + * @see f_fss_apply_delimit() + * @see fl_status_string_from() + * @see fll_fss_extended_read() + * @see fll_fss_basic_list_read() + */ +#ifndef _di_control_packet_receive_ + extern void control_packet_receive(control_main_t * const main, control_payload_header_t * const header); +#endif // _di_control_packet_receive_ + +/** + * Process the received and loaded packet. + * + * @param main + * The main program data. + * + * This alters main.setting.state.status: + * F_done on success but action has no success or failure states. + * F_success on success. + * + * F_busy (with error bit) + * F_failure (with error bit) on success but controller returned failure for action. + * F_known_not (with error bit) if the Payload type is unknown. + * Any error (with error bit) on failure where the error is defined by the controller service. + * @param header + * The control payload packet header data. + */ +#ifndef _di_control_packet_process_ + extern void control_packet_process(control_main_t * const main, control_payload_header_t * const header); +#endif // _di_control_packet_process_ + +/** + * Send the payload to the remote socket, getting the payload from the large cache. + * + * @param main + * The main program data. + * + * This alters main.setting.state.status: + * F_okay on success. + * + * Errors (with error bit) from: f_socket_write(). + * + * @see f_socket_write() + */ +#ifndef _di_control_packet_send_ + extern void control_packet_send(control_main_t * const main); +#endif // _di_control_packet_send_ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _control_main_packet_h diff --git a/sources/c/program/control/main/payload.c b/sources/c/program/control/main/payload.c new file mode 100644 index 0000000..aa20e06 --- /dev/null +++ b/sources/c/program/control/main/payload.c @@ -0,0 +1,38 @@ +#include "control.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _di_control_payload_type_identify_ + const uint8_t control_payload_type_identify(const f_string_static_t payload) { + + if (f_compare_dynamic(payload, control_controller_s) == F_equal_to) return control_payload_type_controller_e; + if (f_compare_dynamic(payload, control_error_s) == F_equal_to) return control_payload_type_error_e; + if (f_compare_dynamic(payload, control_init_s) == F_equal_to) return control_payload_type_init_e; + + return 0; + } +#endif // _di_control_payload_type_identify_ + +#ifndef _di_control_payload_type_name_ + const f_string_static_t control_payload_type_name(const uint8_t type) { + + switch (type) { + case control_payload_type_controller_e: + return control_controller_s; + + case control_payload_type_error_e: + return control_error_s; + + case control_payload_type_init_e: + return control_init_s; + } + + return f_string_empty_s; + } +#endif // _di_control_payload_type_name_ + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/sources/c/program/control/main/payload.h b/sources/c/program/control/main/payload.h new file mode 100644 index 0000000..1ecba36 --- /dev/null +++ b/sources/c/program/control/main/payload.h @@ -0,0 +1,51 @@ +/** + * FLL - Level 3 + * + * Project: Control + * API Version: 0.7 + * Licenses: lgpl-2.1-or-later + * + * Provides the payload functionality. + * + * This is auto-included and should not need to be explicitly included. + */ +#ifndef _control_main_payload_h +#define _control_main_payload_h + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Identify the payload code the given name represents. + * + * @param payload + * The string representing a payload. + * + * @return + * The payload type code on success. + * 0 if name is unknown. + */ +#ifndef _di_control_payload_type_identify_ + extern const uint8_t control_payload_type_identify(const f_string_static_t payload); +#endif // _di_control_payload_type_identify_ + +/** + * Get a string representing the payload type. + * + * @param type + * The payload type id. + * + * @return + * The string with used > 0 on success. + * The string with used == 0 if no match was found. + */ +#ifndef _di_control_payload_type_name_ + extern const f_string_static_t control_payload_type_name(const uint8_t type); +#endif // _di_control_payload_type_name_ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _control_main_payload_h diff --git a/sources/c/program/control/main/print/data.c b/sources/c/program/control/main/print/data.c new file mode 100644 index 0000000..39790a2 --- /dev/null +++ b/sources/c/program/control/main/print/data.c @@ -0,0 +1,9 @@ +#include "../control.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/sources/c/program/control/main/print/data.h b/sources/c/program/control/main/print/data.h new file mode 100644 index 0000000..e032301 --- /dev/null +++ b/sources/c/program/control/main/print/data.h @@ -0,0 +1,23 @@ +/** + * FLL - Level 3 + * + * Project: Control + * API Version: 0.7 + * Licenses: lgpl-2.1-or-later + * + * Provides the print data functionality. + * + * This is auto-included and should not need to be explicitly included. + */ +#ifndef _control_main_print_data_h +#define _control_main_print_data_h + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _control_main_print_data_h diff --git a/sources/c/program/control/main/print/debug.c b/sources/c/program/control/main/print/debug.c new file mode 100644 index 0000000..aefb4fd --- /dev/null +++ b/sources/c/program/control/main/print/debug.c @@ -0,0 +1,59 @@ +#include "../control.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _di_control_print_debug_packet_header_object_and_content_ + f_status_t control_print_debug_packet_header_object_and_content(fl_print_t * const print, const f_string_static_t object, const f_string_static_t content, const f_range_t content_range) { + + if (!print) return F_status_set_error(F_output_not); + if (print->verbosity < f_console_verbosity_debug_e) return F_output_not; + + f_file_stream_lock(print->to); + + fl_print_format("Packet header Object '%[%Q%]", print->to, print->set->notable, object, print->set->notable); + fl_print_format("' has value '%[%/Q%]'.%r", print->to, print->set->notable, content, content_range, print->set->notable, f_string_eol_s); + + f_file_stream_unlock(print->to); + + return F_okay; + } +#endif // _di_control_print_debug_packet_header_object_and_content_ + +#ifndef _di_control_print_debug_packet_message_ + f_status_t control_print_debug_packet_message(fl_print_t * const print, const f_string_t message, const f_string_static_t *buffer, const f_range_t *range) { + + if (!print || !print->custom) return F_status_set_error(F_output_not); + if (print->verbosity < f_console_verbosity_debug_e) return F_output_not; + + control_main_t * const main = (control_main_t *) print->custom; + + f_file_stream_lock(print->to); + + fl_print_format("%s", print->to, message); + + if (buffer) { + if (range) { + fl_print_format("'%[%/Q%]'", print->to, print->set->notable, *buffer, *range, print->set->notable); + } + else { + fl_print_format("'%[%/Q%]'", print->to, print->set->notable, *buffer, print->set->notable); + } + } + + if (main->setting.state.status) { + fl_print_format(", with status code %[%ui%]'", print->to, print->set->notable, main->setting.state.status, print->set->notable); + } + + fl_print_format(".%r", print->to, f_string_eol_s); + + f_file_stream_unlock(print->to); + + return F_okay; + } +#endif // _di_control_print_debug_packet_message_ + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/sources/c/program/control/main/print/debug.h b/sources/c/program/control/main/print/debug.h new file mode 100644 index 0000000..5235bae --- /dev/null +++ b/sources/c/program/control/main/print/debug.h @@ -0,0 +1,79 @@ +/** + * FLL - Level 3 + * + * Project: Control + * API Version: 0.7 + * Licenses: lgpl-2.1-or-later + * + * Provides the print functionality. + * + * This is auto-included and should not need to be explicitly included. + */ +#ifndef _control_main_print_debug_h +#define _control_main_print_debug_h + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Print a message displaying the object and content for some packet header. + * + * @param print + * The output structure to print to. + * + * This does not alter print.custom.setting.state.status. + * @param object + * The object string. + * @param content + * The content string. + * @param content_range + * The range representing the content where the content is found within the content string. + * + * @return + * F_okay on success. + * F_output_not on success, but no printing is performed. + * + * F_output_not (with error bit) if setting is NULL. + */ +#ifndef _di_control_print_debug_packet_header_object_and_content_ + extern f_status_t control_print_debug_packet_header_object_and_content(fl_print_t * const print, const f_string_static_t object, const f_string_static_t content, const f_range_t content_range); +#endif // _di_control_print_debug_packet_header_object_and_content_ + +/** + * Print a debug message regarding some packet. + * + * @param print + * The output structure to print to. + * + * This does not alter print.custom.setting.state.status. + * @param message + * The message to print. + * + * A single "%[" followed by a single "%]" is supported in the message and is replaced with "notable" context. + * @param buffer + * (optional) An additonal message to print (this is syntax highlighted). + * Set to NULL to not use. + * @param range + * (optional) The range within the buffer to print rather than printing the entire buffer. + * Set to NULL to not use. + * This is ignored when buffer is NULL. + * @param status + * (optional) A status code that triggered the failure (this is syntax highlighted). + * Set to NULL to not use. + * + * @return + * F_okay on success. + * F_output_not on success, but no printing is performed. + * + * F_output_not (with error bit) if setting is NULL. + */ +#ifndef _di_control_print_debug_packet_message_ + extern f_status_t control_print_debug_packet_message(fl_print_t * const print, const f_string_t message, const f_string_static_t *buffer, const f_range_t *range); +#endif // _di_control_print_debug_packet_message_ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _control_main_print_debug_h diff --git a/sources/c/program/control/main/print/error.c b/sources/c/program/control/main/print/error.c new file mode 100644 index 0000000..18c9ddb --- /dev/null +++ b/sources/c/program/control/main/print/error.c @@ -0,0 +1,419 @@ +#include "../control.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _di_control_print_error_ + f_status_t control_print_error(fl_print_t * const print, const f_string_t function) { + + if (!print || !print->custom) return F_status_set_error(F_output_not); + if (print->verbosity < f_console_verbosity_error_e) return F_output_not; + + fll_error_print(print, F_status_set_fine(((control_main_t *) print->custom)->setting.state.status), function, fll_error_file_flag_fallback_e); + + return F_okay; + } +#endif // _di_control_print_error_ + +#ifndef _di_control_print_error_file_ + f_status_t control_print_error_file(fl_print_t * const print, const f_string_t function, const f_string_static_t name, const f_string_static_t operation, const uint8_t type) { + + if (!print || !print->custom) return F_status_set_error(F_output_not); + if (print->verbosity < f_console_verbosity_error_e) return F_output_not; + + control_main_t * const main = (control_main_t *) print->custom; + + fll_error_file_print(print, F_status_set_fine(main->setting.state.status), function, fll_error_file_flag_fallback_e, name, operation, type); + + return F_okay; + } +#endif // _di_control_print_error_file_ + +#ifndef _di_control_print_error_packet_response_ + f_status_t control_print_error_packet_response(fl_print_t * const print, const control_payload_header_t header, const f_string_static_t string_status) { + + if (!print || !print->custom) return F_status_set_error(F_output_not); + if (print->verbosity < f_console_verbosity_error_e) return F_output_not; + + control_main_t * const main = (control_main_t *) print->custom; + + f_file_stream_lock(print->to); + + fl_print_format("%[%QReceived error response for " CONTROL_action_s " '%]", print->to, print->set->error, print->prefix, print->set->error); + fl_print_format(f_string_format_Q_single_s.string, print->to, print->set->notable, control_action_type_name(header.action), print->set->notable); + fl_print_format("%[' with status '%]", print->to, print->set->error, print->set->error); + fl_print_format(f_string_format_Q_single_s.string, print->to, print->set->notable, string_status, print->set->notable); + fl_print_format("%[' (%]", print->to, print->set->error, print->set->error); + fl_print_format(f_string_format_ui_single_s.string, print->to, print->set->notable, header.status, print->set->notable); + + if (header.length) { + fl_print_format("%[): %/Q%]%r", print->to, print->set->error, print->set->error, main->cache.large, main->cache.packet_contents.array[main->cache.packet_contents.used - 1].array[0], f_string_eol_s); + } + else { + fl_print_format("%[).%]%r", print->to, print->set->error, print->set->error, f_string_eol_s); + } + + f_file_stream_unlock(print->to); + + return F_okay; + } +#endif // _di_control_print_error_packet_response_ + +#ifndef _di_control_print_error_packet_response_failure_ + f_status_t control_print_error_packet_response_failure(fl_print_t * const print, const control_payload_header_t header, const f_string_static_t string_status) { + + if (!print || !print->custom) return F_status_set_error(F_output_not); + if (print->verbosity < f_console_verbosity_error_e) return F_output_not; + + control_main_t * const main = (control_main_t *) print->custom; + + f_file_stream_lock(print->to); + + fl_print_format("%[%QThe action '%]", print->to, print->set->error, print->prefix, print->set->error); + fl_print_format(f_string_format_Q_single_s.string, print->to, print->set->notable, control_action_type_name(header.action), print->set->notable); + fl_print_format("%[' failed with status '%]", print->to, print->set->error, print->set->error); + fl_print_format(f_string_format_Q_single_s.string, print->to, print->set->notable, string_status, print->set->notable); + fl_print_format("%[' (%]", print->to, print->set->error, print->set->error); + fl_print_format(f_string_format_ui_single_s.string, print->to, print->set->notable, header.status, print->set->notable); + + if (header.length) { + fl_print_format("%[): %/Q%]%r", print->to, print->set->error, print->set->error, main->cache.large, main->cache.packet_contents.array[main->cache.packet_contents.used - 1].array[0], f_string_eol_s); + } + else { + fl_print_format("%[).%]%r", print->to, print->set->error, print->set->error, f_string_eol_s); + } + + f_file_stream_unlock(print->to); + + return F_okay; + } +#endif // _di_control_print_error_packet_response_failure_ + +#ifndef _di_control_print_error_parameter_actions_none_ + f_status_t control_print_error_parameter_actions_none(fl_print_t * const print) { + + if (!print) return F_status_set_error(F_output_not); + if (print->verbosity < f_console_verbosity_error_e) return F_output_not; + + fll_print_format("%[%QNo actions provided.%]%r", print->to, print->set->error, print->prefix, print->set->error, f_string_eol_s); + + return F_okay; + } +#endif // _di_control_print_error_parameter_actions_none_ + +#ifndef _di_control_print_error_parameter_action_not_ + f_status_t control_print_error_parameter_action_not(fl_print_t * const print, const f_string_static_t action) { + + if (!print) return F_status_set_error(F_output_not); + if (print->verbosity < f_console_verbosity_error_e) return F_output_not; + + f_file_stream_lock(print->to); + + fl_print_format("%[%QThe parameter '%]", print->to, print->set->error, print->prefix, print->set->error); + fl_print_format(f_string_format_Q_single_s.string, print->to, print->set->notable, action, print->set->notable); + fl_print_format("%[' is not a known controller action.%]%r", print->to, print->set->error, print->set->error, f_string_eol_s); + + f_file_stream_unlock(print->to); + + return F_okay; + } +#endif // _di_control_print_error_parameter_action_not_ + +#ifndef _di_control_print_error_parameter_action_rule_basename_empty_ + f_status_t control_print_error_parameter_action_rule_basename_empty(fl_print_t * const print, const f_string_static_t action) { + + if (!print) return F_status_set_error(F_output_not); + if (print->verbosity < f_console_verbosity_error_e) return F_output_not; + + f_file_stream_lock(print->to); + + fl_print_format("%[%QThe action parameter '%]", print->to, print->set->error, print->prefix, print->set->error); + fl_print_format(f_string_format_Q_single_s.string, print->to, print->set->notable, action, print->set->notable); + fl_print_format("%[' a rule base name cannot be an empty string.%]%r", print->to, print->set->error, print->set->error, f_string_eol_s); + + f_file_stream_unlock(print->to); + + return F_okay; + } +#endif // _di_control_print_error_parameter_action_rule_basename_empty_ + +#ifndef _di_control_print_error_parameter_action_rule_directory_empty_ + f_status_t control_print_error_parameter_action_rule_directory_empty(fl_print_t * const print, const f_string_static_t action) { + + if (!print) return F_status_set_error(F_output_not); + if (print->verbosity < f_console_verbosity_error_e) return F_output_not; + + f_file_stream_lock(print->to); + + fl_print_format("%[%QThe action parameter '%]", print->to, print->set->error, print->prefix, print->set->error); + fl_print_format(f_string_format_Q_single_s.string, print->to, print->set->notable, action, print->set->notable); + fl_print_format("%[' a rule directory path cannot be an empty string.%]%r", print->to, print->set->error, print->set->error, f_string_eol_s); + + f_file_stream_unlock(print->to); + + return F_okay; + } +#endif // _di_control_print_error_parameter_action_rule_directory_empty_ + +#ifndef _di_control_print_error_parameter_action_rule_empty_ + f_status_t control_print_error_parameter_action_rule_empty(fl_print_t * const print, const f_string_static_t action) { + + if (!print) return F_status_set_error(F_output_not); + if (print->verbosity < f_console_verbosity_error_e) return F_output_not; + + f_file_stream_lock(print->to); + + fl_print_format("%[%QThe action parameter '%]", print->to, print->set->error, print->prefix, print->set->error); + fl_print_format(f_string_format_Q_single_s.string, print->to, print->set->notable, action, print->set->notable); + fl_print_format("%[' a rule name cannot be an empty string.%]%r", print->to, print->set->error, print->set->error, f_string_eol_s); + + f_file_stream_unlock(print->to); + + return F_okay; + } +#endif // _di_control_print_error_parameter_action_rule_empty_ + +#ifndef _di_control_print_error_parameter_action_rule_not_ + f_status_t control_print_error_parameter_action_rule_not(fl_print_t * const print, const f_string_static_t action) { + + if (!print) return F_status_set_error(F_output_not); + if (print->verbosity < f_console_verbosity_error_e) return F_output_not; + + f_file_stream_lock(print->to); + + fl_print_format("%[%QThe action parameter '%]", print->to, print->set->error, print->prefix, print->set->error); + fl_print_format(f_string_format_Q_single_s.string, print->to, print->set->notable, action, print->set->notable); + fl_print_format("%[' requires either a full rule name or a rule directory path along with the rule base name.%]%r", print->to, print->set->error, print->set->error, f_string_eol_s); + + f_file_stream_unlock(print->to); + + return F_okay; + } +#endif // _di_control_print_error_parameter_action_rule_not_ + +#ifndef _di_control_print_error_parameter_action_rule_too_few_ + f_status_t control_print_error_parameter_action_rule_too_few(fl_print_t * const print, const f_string_static_t action) { + + if (!print) return F_status_set_error(F_output_not); + if (print->verbosity < f_console_verbosity_error_e) return F_output_not; + + f_file_stream_lock(print->to); + + fl_print_format("%[%QThe action parameter '%]", print->to, print->set->error, print->prefix, print->set->error); + fl_print_format(f_string_format_Q_single_s.string, print->to, print->set->notable, action, print->set->notable); + fl_print_format("%[' has too few arguments.%]%r", print->to, print->set->error, print->set->error, f_string_eol_s); + + f_file_stream_unlock(print->to); + + return F_okay; + } +#endif // _di_control_print_error_parameter_action_rule_too_few_ + +#ifndef _di_control_print_error_parameter_action_rule_too_few_with_ + f_status_t control_print_error_parameter_action_rule_too_few_with(fl_print_t * const print, const f_string_static_t action, const f_string_static_t with) { + + if (!print) return F_status_set_error(F_output_not); + if (print->verbosity < f_console_verbosity_error_e) return F_output_not; + + f_file_stream_lock(print->to); + + fl_print_format("%[%QThe action parameter '%]", print->to, print->set->error, print->prefix, print->set->error); + fl_print_format(f_string_format_Q_single_s.string, print->to, print->set->notable, action, print->set->notable); + fl_print_format("%[' when used with '%]", print->to, print->set->error, print->set->error, f_string_eol_s); + fl_print_format(f_string_format_Q_single_s.string, print->to, print->set->notable, with, print->set->notable); + fl_print_format("%[' has too few arguments.%]%r", print->to, print->set->error, print->set->error, f_string_eol_s); + + f_file_stream_unlock(print->to); + + return F_okay; + } +#endif // _di_control_print_error_parameter_action_rule_too_few_with_ + +#ifndef _di_control_print_error_parameter_action_rule_too_many_ + f_status_t control_print_error_parameter_action_rule_too_many(fl_print_t * const print, const f_string_static_t action) { + + if (!print) return F_status_set_error(F_output_not); + if (print->verbosity < f_console_verbosity_error_e) return F_output_not; + + f_file_stream_lock(print->to); + + fl_print_format("%[%QThe action parameter '%]", print->to, print->set->error, print->prefix, print->set->error); + fl_print_format(f_string_format_Q_single_s.string, print->to, print->set->notable, action, print->set->notable); + fl_print_format("%[' has too many arguments.%]%r", print->to, print->set->error, print->set->error, f_string_eol_s); + + f_file_stream_unlock(print->to); + + return F_okay; + } +#endif // _di_control_print_error_parameter_action_rule_too_many_ + +#ifndef _di_control_print_error_parameter_action_rule_too_many_with_ + f_status_t control_print_error_parameter_action_rule_too_many_with(fl_print_t * const print, const f_string_static_t action, const f_string_static_t with) { + + if (!print) return F_status_set_error(F_output_not); + if (print->verbosity < f_console_verbosity_error_e) return F_output_not; + + f_file_stream_lock(print->to); + + fl_print_format("%[%QThe action parameter '%]", print->to, print->set->error, print->prefix, print->set->error); + fl_print_format(f_string_format_Q_single_s.string, print->to, print->set->notable, action, print->set->notable); + fl_print_format("%[' when used with '%]", print->to, print->set->error, print->set->error, f_string_eol_s); + fl_print_format(f_string_format_Q_single_s.string, print->to, print->set->notable, with, print->set->notable); + fl_print_format("%[' has too many arguments.%]%r", print->to, print->set->error, print->set->error, f_string_eol_s); + + f_file_stream_unlock(print->to); + + return F_okay; + } +#endif // _di_control_print_error_parameter_action_rule_too_many_with_ + +#ifndef _di_control_print_error_parameter_action_rule_with_unknown_ + f_status_t control_print_error_parameter_action_rule_with_unknown(fl_print_t * const print, const f_string_static_t action, const f_string_static_t with) { + + if (!print) return F_status_set_error(F_output_not); + if (print->verbosity < f_console_verbosity_error_e) return F_output_not; + + f_file_stream_lock(print->to); + + fl_print_format("%[%QThe action parameter '%]", print->to, print->set->error, print->prefix, print->set->error); + fl_print_format(f_string_format_Q_single_s.string, print->to, print->set->notable, action, print->set->notable); + fl_print_format("%[' does not know the argument '%]", print->to, print->set->error, print->set->error, f_string_eol_s); + fl_print_format(f_string_format_Q_single_s.string, print->to, print->set->notable, with, print->set->notable); + fl_print_format(f_string_format_sentence_end_quote_s.string, print->to, print->set->error, print->set->error, f_string_eol_s); + + f_file_stream_unlock(print->to); + + return F_okay; + } +#endif // _di_control_print_error_parameter_action_rule_with_unknown_ + +#ifndef _di_control_print_error_parameter_value_empty_ + f_status_t control_print_error_parameter_value_empty(fl_print_t * const print, const f_string_static_t parameter) { + + if (!print) return F_status_set_error(F_output_not); + if (print->verbosity < f_console_verbosity_error_e) return F_output_not; + + f_file_stream_lock(print->to); + + fl_print_format("%[%QThe value for the parameter '%]", print->to, print->set->error, print->prefix, print->set->error); + fl_print_format(f_string_format_rr_single_s.string, print->to, print->set->notable, f_console_symbol_long_normal_s, parameter, print->set->notable); + fl_print_format("%[' must not be an empty string.%]%r", print->to, print->set->error, print->set->error, f_string_eol_s); + + f_file_stream_unlock(print->to); + + return F_okay; + } +#endif // _di_control_print_error_parameter_value_empty_ + +#ifndef _di_control_print_error_parameter_value_not_ + f_status_t control_print_error_parameter_value_not(fl_print_t * const print, const f_string_static_t parameter) { + + if (!print) return F_status_set_error(F_output_not); + if (print->verbosity < f_console_verbosity_error_e) return F_output_not; + + f_file_stream_lock(print->to); + + fl_print_format("%[%QThe parameter '%]", print->to, print->set->error, print->prefix, print->set->error); + fl_print_format(f_string_format_rr_single_s.string, print->to, print->set->notable, f_console_symbol_long_normal_s, parameter, print->set->notable); + fl_print_format("%[' is specified, but no value is given.%]%r", print->to, print->set->error, print->set->error, f_string_eol_s); + + f_file_stream_unlock(print->to); + + return F_okay; + } +#endif // _di_control_print_error_parameter_value_not_ + +#ifndef _di_control_print_error_pipe_supported_not_ + f_status_t control_print_error_pipe_supported_not(fl_print_t * const print) { + + if (!print) return F_status_set_error(F_output_not); + if (print->verbosity < f_console_verbosity_error_e) return F_output_not; + + fll_print_format("%[%QPipe input is not supported by this program.%]%r", print->to, print->set->error, print->prefix, print->set->error, f_string_eol_s); + + return F_okay; + } +#endif // _di_control_print_error_pipe_supported_not_ + +#ifndef _di_control_print_error_response_packet_valid_not_ + f_status_t control_print_error_response_packet_valid_not(fl_print_t * const print) { + + if (!print) return F_status_set_error(F_output_not); + if (print->verbosity < f_console_verbosity_error_e) return F_output_not; + + fll_print_format("%[%QThe received response is not a valid or supported packet.%]%r", print->to, print->set->error, print->prefix, print->set->error, f_string_eol_s); + + return F_okay; + } +#endif // _di_control_print_error_response_packet_valid_not_ + +#ifndef _di_control_print_error_request_packet_too_large_ + f_status_t control_print_error_request_packet_too_large(fl_print_t * const print) { + + if (!print) return F_status_set_error(F_output_not); + if (print->verbosity < f_console_verbosity_error_e) return F_output_not; + + fll_print_format("%[%QThe generated packet is too large, cannot send packet.%]%r", print->to, print->set->error, print->prefix, print->set->error, f_string_eol_s); + + return F_okay; + } +#endif // _di_control_print_error_request_packet_too_large_ + +#ifndef _di_control_print_error_socket_file_failed_ + f_status_t control_print_error_socket_file_failed(fl_print_t * const print, const f_string_static_t path_socket) { + + if (!print) return F_status_set_error(F_output_not); + if (print->verbosity < f_console_verbosity_error_e) return F_output_not; + + f_file_stream_lock(print->to); + + fl_print_format("%[%QFailed to connect to the socket file '%]", print->to, print->set->error, print->prefix, print->set->error); + fl_print_format(f_string_format_Q_single_s.string, print->to, print->set->notable, path_socket, print->set->notable); + fl_print_format(f_string_format_sentence_end_quote_s.string, print->to, print->set->error, print->set->error, f_string_eol_s); + + f_file_stream_unlock(print->to); + + return F_okay; + } +#endif // _di_control_print_error_socket_file_failed_ + +#ifndef _di_control_print_error_socket_file_missing_ + f_status_t control_print_error_socket_file_missing(fl_print_t * const print, const f_string_static_t path_socket) { + + if (!print) return F_status_set_error(F_output_not); + if (print->verbosity < f_console_verbosity_error_e) return F_output_not; + + f_file_stream_lock(print->to); + + fl_print_format("%[%QThe controller socket file '%]", print->to, print->set->error, print->prefix, print->set->error); + fl_print_format(f_string_format_Q_single_s.string, print->to, print->set->notable, path_socket, print->set->notable); + fl_print_format("%[' could not be found and is required.%]%r", print->to, print->set->error, print->set->error, f_string_eol_s); + + f_file_stream_unlock(print->to); + + return F_okay; + } +#endif // _di_control_print_error_socket_file_missing_ + +#ifndef _di_control_print_error_socket_file_not_ + f_status_t control_print_error_socket_file_not(fl_print_t * const print, const f_string_static_t path_socket) { + + if (!print) return F_status_set_error(F_output_not); + if (print->verbosity < f_console_verbosity_error_e) return F_output_not; + + f_file_stream_lock(print->to); + + fl_print_format("%[%QThe controller socket file '%]", print->to, print->set->error, print->prefix, print->set->error); + fl_print_format(f_string_format_Q_single_s.string, print->to, print->set->notable, path_socket, print->set->notable); + fl_print_format("%[' is not a socket file.%]%r", print->to, print->set->error, print->set->error, f_string_eol_s); + + f_file_stream_unlock(print->to); + + return F_okay; + } +#endif // _di_control_print_error_socket_file_not_ + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/sources/c/program/control/main/print/error.h b/sources/c/program/control/main/print/error.h new file mode 100644 index 0000000..1e8e6b4 --- /dev/null +++ b/sources/c/program/control/main/print/error.h @@ -0,0 +1,494 @@ +/** + * FLL - Level 3 + * + * Project: Control + * API Version: 0.7 + * Licenses: lgpl-2.1-or-later + * + * Provides the print error functionality. + * + * This is auto-included and should not need to be explicitly included. + */ +#ifndef _control_main_print_error_h +#define _control_main_print_error_h + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Print generic error message regarding a function failing in some way. + * + * @param print + * The output structure to print to. + * + * This does not alter print.custom.setting.state.status. + * @param function + * The name of the function associated with the error. + * + * @return + * F_okay on success. + * F_output_not on success, but no printing is performed. + * + * F_output_not (with error bit) if setting is NULL. + * + * @see fll_error_print() + */ +#ifndef _di_control_print_error_ + extern f_status_t control_print_error(fl_print_t * const print, const f_string_t function); +#endif // _di_control_print_error_ + +/** + * Print file related error or warning messages. + * + * @param print + * The output structure to print to. + * + * This does not alter print.custom.setting.state.status. + * @param function + * The name of the function where the error happened. + * Set to 0 to disable. + * @param name + * The name of the file or directory. + * @param operation + * The operation that fails, such as 'create' or 'access'. + * @param type + * A valid file type code from the fll_error_file_type enum. + * + * @return + * F_okay on success. + * F_output_not on success, but no printing is performed. + * + * F_output_not (with error bit) if setting is NULL. + * + * @see fll_error_file_print() + */ +#ifndef _di_control_print_error_file_ + extern f_status_t control_print_error_file(fl_print_t * const print, const f_string_t function, const f_string_static_t name, const f_string_static_t operation, const uint8_t type); +#endif // _di_control_print_error_file_ + +/** + * Print an error from the packet response. + * + * @param print + * The output structure to print to. + * + * This does not alter print.custom.setting.state.status. + * @param header + * The control payload packet header data. + * @param status + * A string representing the name of the status code from header.status. + * + * @return + * F_okay on success. + * F_output_not on success, but no printing is performed. + * + * F_output_not (with error bit) if setting is NULL. + */ +#ifndef _di_control_print_error_packet_response_ + extern f_status_t control_print_error_packet_response(fl_print_t * const print, const control_payload_header_t header, const f_string_static_t status); +#endif // _di_control_print_error_packet_response_ + +/** + * Print a error about a packet response failure. + * + * @param print + * The output structure to print to. + * + * This does not alter print.custom.setting.state.status. + * @param header + * The control payload packet header data. + * @param status + * A string representing the name of the status code from header.status. + * + * @return + * F_okay on success. + * F_output_not on success, but no printing is performed. + * + * F_output_not (with error bit) if setting is NULL. + */ +#ifndef _di_control_print_error_packet_response_failure_ + extern f_status_t control_print_error_packet_response_failure(fl_print_t * const print, const control_payload_header_t header, const f_string_static_t string_status); +#endif // _di_control_print_error_packet_response_failure_ +/** + * Print an error message about no actions being provided. + * + * @param print + * The output structure to print to. + * + * This does not alter print.custom.setting.state.status. + * + * @return + * F_okay on success. + * F_output_not on success, but no printing is performed. + * + * F_output_not (with error bit) if setting is NULL. + */ +#ifndef _di_control_print_error_parameter_actions_none_ + extern f_status_t control_print_error_parameter_actions_none(fl_print_t * const print); +#endif // _di_control_print_error_parameter_actions_none_ + +/** + * Print an error message about the given parameter not matching the known set of controller actions. + * + * @param print + * The output structure to print to. + * + * This does not alter print.custom.setting.state.status. + * @param action + * The parameter representing an action. + * + * @return + * F_okay on success. + * F_output_not on success, but no printing is performed. + * + * F_output_not (with error bit) if setting is NULL. + */ +#ifndef _di_control_print_error_parameter_action_not_ + extern f_status_t control_print_error_parameter_action_not(fl_print_t * const print, const f_string_static_t action); +#endif // _di_control_print_error_parameter_action_not_ + +/** + * Print an error message about the given parameter being a rule action having an empty rule basename. + * + * @param print + * The output structure to print to. + * + * This does not alter print.custom.setting.state.status. + * @param action + * The parameter representing an action. + * + * @return + * F_okay on success. + * F_output_not on success, but no printing is performed. + * + * F_output_not (with error bit) if setting is NULL. + */ +#ifndef _di_control_print_error_parameter_action_rule_basename_empty_ + extern f_status_t control_print_error_parameter_action_rule_basename_empty(fl_print_t * const print, const f_string_static_t action); +#endif // _di_control_print_error_parameter_action_rule_basename_empty_ + +/** + * Print an error message about the given parameter being a rule action having an empty rule directory path. + * + * @param print + * The output structure to print to. + * + * This does not alter print.custom.setting.state.status. + * @param action + * The parameter representing an action. + * + * @return + * F_okay on success. + * F_output_not on success, but no printing is performed. + * + * F_output_not (with error bit) if setting is NULL. + */ +#ifndef _di_control_print_error_parameter_action_rule_directory_empty_ + extern f_status_t control_print_error_parameter_action_rule_directory_empty(fl_print_t * const print, const f_string_static_t action); +#endif // _di_control_print_error_parameter_action_rule_directory_empty_ + +/** + * Print an error message about the given parameter being a rule action having an empty rule name. + * + * @param print + * The output structure to print to. + * + * This does not alter print.custom.setting.state.status. + * @param action + * The parameter representing an action. + * + * @return + * F_okay on success. + * F_output_not on success, but no printing is performed. + * + * F_output_not (with error bit) if setting is NULL. + */ +#ifndef _di_control_print_error_parameter_action_rule_empty_ + extern f_status_t control_print_error_parameter_action_rule_empty(fl_print_t * const print, const f_string_static_t action); +#endif // _di_control_print_error_parameter_action_rule_empty_ + +/** + * Print an error message about the given parameter being a rule action but no rule name is specified. + * + * @param print + * The output structure to print to. + * + * This does not alter print.custom.setting.state.status. + * @param action + * The parameter representing an action. + * + * @return + * F_okay on success. + * F_output_not on success, but no printing is performed. + * + * F_output_not (with error bit) if setting is NULL. + */ +#ifndef _di_control_print_error_parameter_action_rule_not_ + extern f_status_t control_print_error_parameter_action_rule_not(fl_print_t * const print, const f_string_static_t action); +#endif // _di_control_print_error_parameter_action_rule_not_ + +/** + * Print an error message about the given parameter being a rule action having too few arguments passed. + * + * @param print + * The output structure to print to. + * + * This does not alter print.custom.setting.state.status. + * @param action + * The parameter representing an action. + * + * @return + * F_okay on success. + * F_output_not on success, but no printing is performed. + * + * F_output_not (with error bit) if setting is NULL. + */ +#ifndef _di_control_print_error_parameter_action_rule_too_few_ + extern f_status_t control_print_error_parameter_action_rule_too_few(fl_print_t * const print, const f_string_static_t action); +#endif // _di_control_print_error_parameter_action_rule_too_few_ + +/** + * Print an error message about the given parameter being a rule action having few many arguments passed for a given "with". + * + * @param print + * The output structure to print to. + * + * This does not alter print.custom.setting.state.status. + * @param action + * The parameter representing an action. + * @param with + * The additional parameter in which is requiring additional arguments that are not met. + * + * @return + * F_okay on success. + * F_output_not on success, but no printing is performed. + * + * F_output_not (with error bit) if setting is NULL. + */ +#ifndef _di_control_print_error_parameter_action_rule_too_few_with_ + extern f_status_t control_print_error_parameter_action_rule_too_few_with(fl_print_t * const print, const f_string_static_t action, const f_string_static_t with); +#endif // _di_control_print_error_parameter_action_rule_too_few_with_ + +/** + * Print an error message about the given parameter being a rule action having too many arguments passed. + * + * @param print + * The output structure to print to. + * + * This does not alter print.custom.setting.state.status. + * @param action + * The parameter representing an action. + * + * @return + * F_okay on success. + * F_output_not on success, but no printing is performed. + * + * F_output_not (with error bit) if setting is NULL. + */ +#ifndef _di_control_print_error_parameter_action_rule_too_many_ + extern f_status_t control_print_error_parameter_action_rule_too_many(fl_print_t * const print, const f_string_static_t action); +#endif // _di_control_print_error_parameter_action_rule_too_many_ + +/** + * Print an error message about the given parameter being a rule action having too many arguments passed for a given "with". + * + * @param print + * The output structure to print to. + * + * This does not alter print.custom.setting.state.status. + * @param action + * The parameter representing an action. + * @param with + * The additional parameter in which is requiring additional arguments that are not met. + * + * @return + * F_okay on success. + * F_output_not on success, but no printing is performed. + * + * F_output_not (with error bit) if setting is NULL. + */ +#ifndef _di_control_print_error_parameter_action_rule_too_many_with_ + extern f_status_t control_print_error_parameter_action_rule_too_many_with(fl_print_t * const print, const f_string_static_t action, const f_string_static_t with); +#endif // _di_control_print_error_parameter_action_rule_too_many_with_ +/** + * Print an error message about the given parameter being unknown for the use with the given action. + * + * @param print + * The output structure to print to. + * + * This does not alter print.custom.setting.state.status. + * @param action + * The parameter representing an action. + * @param with + * The additional parameter in which is requiring additional arguments that are not met. + * + * @return + * F_okay on success. + * F_output_not on success, but no printing is performed. + * + * F_output_not (with error bit) if setting is NULL. + */ +#ifndef _di_control_print_error_parameter_action_rule_with_unknown_ + extern f_status_t control_print_error_parameter_action_rule_with_unknown(fl_print_t * const print, const f_string_static_t action, const f_string_static_t with); +#endif // _di_control_print_error_parameter_action_rule_with_unknown_ + +/** + * Print an error message about the parameter's associated value being an empty string. + * + * @param print + * The output structure to print to. + * + * This does not alter print.custom.setting.state.status. + * @param parameter + * The parameter name. + * + * @return + * F_okay on success. + * F_output_not on success, but no printing is performed. + * + * F_output_not (with error bit) if setting is NULL. + */ +#ifndef _di_control_print_error_parameter_value_empty_ + extern f_status_t control_print_error_parameter_value_empty(fl_print_t * const print, const f_string_static_t parameter); +#endif // _di_control_print_error_parameter_value_empty_ + +/** + * Print an error message about the parameter missings its associated value. + * + * @param print + * The output structure to print to. + * + * This does not alter print.custom.setting.state.status. + * @param parameter + * The parameter name. + * + * @return + * F_okay on success. + * F_output_not on success, but no printing is performed. + * + * F_output_not (with error bit) if setting is NULL. + */ +#ifndef _di_control_print_error_parameter_value_not_ + extern f_status_t control_print_error_parameter_value_not(fl_print_t * const print, const f_string_static_t parameter); +#endif // _di_control_print_error_parameter_value_not_ + +/** + * Print an error message about a pipe input being unsupported. + * + * @param print + * The output structure to print to. + * + * This does not alter print.custom.setting.state.status. + * + * @return + * F_okay on success. + * F_output_not on success, but no printing is performed. + * + * F_output_not (with error bit) if setting is NULL. + */ +#ifndef _di_control_print_error_pipe_supported_not_ + extern f_status_t control_print_error_pipe_supported_not(fl_print_t * const print); +#endif // _di_control_print_error_pipe_supported_not_ + +/** + * Print an error message about the response packet format either being invalid or not supported. + * + * @param print + * The output structure to print to. + * + * This does not alter print.custom.setting.state.status. + * + * @return + * F_okay on success. + * F_output_not on success, but no printing is performed. + * + * F_output_not (with error bit) if setting is NULL. + */ +#ifndef _di_control_print_error_response_packet_valid_not_ + extern f_status_t control_print_error_response_packet_valid_not(fl_print_t * const print); +#endif // _di_control_print_error_response_packet_valid_not_ + +/** + * Print an error message about the request packet being too large. + * + * @param print + * The output structure to print to. + * + * This does not alter print.custom.setting.state.status. + * + * @return + * F_okay on success. + * F_output_not on success, but no printing is performed. + * + * F_output_not (with error bit) if setting is NULL. + */ +#ifndef _di_control_print_error_request_packet_too_large_ + extern f_status_t control_print_error_request_packet_too_large(fl_print_t * const print); +#endif // _di_control_print_error_request_packet_too_large_ + +/** + * Print an error message about failure to connect to the socket file. + * + * @param print + * The output structure to print to. + * + * This does not alter print.custom.setting.state.status. + * @param path_socket + * The socket file path. + * + * @return + * F_okay on success. + * F_output_not on success, but no printing is performed. + * + * F_output_not (with error bit) if setting is NULL. + */ +#ifndef _di_control_print_error_socket_file_failed_ + extern f_status_t control_print_error_socket_file_failed(fl_print_t * const print, const f_string_static_t path_socket); +#endif // _di_control_print_error_socket_file_failed_ + +/** + * Print an error message about the socket file not being found. + * + * @param print + * The output structure to print to. + * + * This does not alter print.custom.setting.state.status. + * @param path_socket + * The socket file path. + * + * @return + * F_okay on success. + * F_output_not on success, but no printing is performed. + * + * F_output_not (with error bit) if setting is NULL. + */ +#ifndef _di_control_print_error_socket_file_missing_ + extern f_status_t control_print_error_socket_file_missing(fl_print_t * const print, const f_string_static_t path_socket); +#endif // _di_control_print_error_socket_file_missing_ + +/** + * Print an error message about the socket file not actually being a socket file. + * + * @param print + * The output structure to print to. + * + * This does not alter print.custom.setting.state.status. + * @param path_socket + * The socket file path. + * + * @return + * F_okay on success. + * F_output_not on success, but no printing is performed. + * + * F_output_not (with error bit) if setting is NULL. + */ +#ifndef _di_control_print_error_socket_file_not_ + extern f_status_t control_print_error_socket_file_not(fl_print_t * const print, const f_string_static_t path_socket); +#endif // _di_control_print_error_socket_file_not_ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _control_main_print_error_h diff --git a/sources/c/program/control/main/print/message.c b/sources/c/program/control/main/print/message.c new file mode 100644 index 0000000..ed68aff --- /dev/null +++ b/sources/c/program/control/main/print/message.c @@ -0,0 +1,82 @@ +#include "../control.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _di_control_print_message_help_ + f_status_t control_print_message_help(fl_print_t * const print) { + + if (!print) return F_status_set_error(F_output_not); + + f_file_stream_lock(print->to); + + fll_program_print_help_header(print, control_program_name_long_s, control_program_version_s); + + fll_program_print_help_option_standard(print); + + f_print_dynamic_raw(f_string_eol_s, print->to); + + fll_program_print_help_option(print, control_short_name_s, control_long_name_s, f_console_symbol_short_normal_s, f_console_symbol_long_normal_s, " Specify the name of the controller socket file."); + fll_program_print_help_option(print, control_short_return_s, control_long_return_s, f_console_symbol_short_normal_s, f_console_symbol_long_normal_s, " Print a message about the response packet."); + fll_program_print_help_option(print, control_short_settings_s, control_long_settings_s, f_console_symbol_short_normal_s, f_console_symbol_long_normal_s, "Specify a directory path or a full path to the control settings file."); + fll_program_print_help_option(print, control_short_socket_s, control_long_socket_s, f_console_symbol_short_normal_s, f_console_symbol_long_normal_s, " Specify a directory path or a full path to the controller socket file."); + + f_print_dynamic_raw(f_string_eol_s, print->to); + + fll_program_print_help_usage(print, control_program_name_s, control_action_s); + + fl_print_format("%r When the %[%r%r%] parameter represents a directory path then the file name is generated from either the", print->to, f_string_eol_s, print->set->notable, f_console_symbol_long_normal_s, control_long_socket_s, print->set->notable); + fl_print_format(" %[%r%r%] parameter or from the control settings file.%r%r", print->to, print->set->notable, f_console_symbol_long_normal_s, control_long_name_s, print->set->notable, f_string_eol_s, f_string_eol_s); + + fl_print_format(" A rule action allows for either the full rule path, such as '%[boot/root%]'", print->to, print->set->notable, print->set->notable); + fl_print_format(" as a single parameter or two parameters with the first representing the rule directory path '%[boot%]'", print->to, print->set->notable, print->set->notable); + fl_print_format(" and the second representing the rule base name '%[root%]'.%r%r", print->to, print->set->notable, print->set->notable, f_string_eol_s, f_string_eol_s); + + fl_print_format(" The %[%r%r%] parameter is intended to be used for scripting and is of the form \"response [type] [action] [status]\".%r", print->to, print->set->notable, f_console_symbol_long_normal_s, control_long_return_s, print->set->notable, f_string_eol_s); + fl_print_format(" Be sure to use the %[%r%r%] parameter to suppress output when using this in scripting.%r", print->to, print->set->notable, f_console_symbol_long_inverse_s, f_console_standard_long_quiet_s, print->set->notable, f_string_eol_s); + fl_print_format(" No response is returned on program errors, especially those errors that prevent communicating to the controller.%r", print->to, f_string_eol_s); + + f_file_stream_flush(print->to); + f_file_stream_unlock(print->to); + + return F_okay; + } +#endif // _di_control_print_message_help_ + +#ifndef _di_control_print_message_packet_response_ + f_status_t control_print_message_packet_response(fl_print_t * const print, const control_payload_header_t header, const f_string_static_t string_status) { + + if (!print || !print->custom) return F_status_set_error(F_output_not); + if (print->verbosity < f_console_verbosity_normal_e) return F_output_not; + + control_main_t * const main = (control_main_t *) print->custom; + + f_file_stream_lock(print->to); + + fl_print_format("The action '", print->to); + fl_print_format(f_string_format_Q_single_s.string, print->to, print->set->notable, control_action_type_name(header.action), print->set->notable); + + if (header.status == F_done) { + fl_print_format("' is performed", print->to); + } + else { + fl_print_format("' is successfully performed", print->to); + } + + if (header.length) { + fl_print_format(": %/Q%r", print->to, main->cache.large, main->cache.packet_contents.array[main->cache.packet_contents.used - 1].array[0], f_string_eol_s); + } + else { + fl_print_format(".%r", print->to, f_string_eol_s); + } + + f_file_stream_unlock(print->to); + + return F_okay; + } +#endif // _di_control_print_message_packet_response_ + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/sources/c/program/control/main/print/message.h b/sources/c/program/control/main/print/message.h new file mode 100644 index 0000000..187c7ab --- /dev/null +++ b/sources/c/program/control/main/print/message.h @@ -0,0 +1,74 @@ +/** + * FLL - Level 3 + * + * Project: Control + * API Version: 0.7 + * Licenses: lgpl-2.1-or-later + * + * Provides the print functionality. + * + * This is auto-included and should not need to be explicitly included. + */ +#ifndef _control_main_print_message_h +#define _control_main_print_message_h + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Print help. + * + * @param print + * The output structure to print to. + * + * This does not alter print.custom.setting.state.status. + * + * @return + * F_okay on success. + * F_output_not on success, but no printing is performed. + * + * F_output_not (with error bit) if setting is NULL. + * + * @see f_file_stream_flush() + * @see f_file_stream_lock() + * @see f_file_stream_unlock() + * @see f_print_dynamic_raw() + * @see fl_print_format() + * + * @see fll_program_print_help_header() + * @see fll_program_print_help_option() + * @see fll_program_print_help_option_standard() + * @see fll_program_print_help_usage() + */ +#ifndef _di_control_print_message_help_ + extern f_status_t control_print_message_help(fl_print_t * const print); +#endif // _di_control_print_message_help_ + +/** + * Print a message about a packet response. + * + * @param print + * The output structure to print to. + * + * This does not alter print.custom.setting.state.status. + * @param header + * The control payload packet header data. + * @param status + * A string representing the name of the status code from header.status. + * + * @return + * F_okay on success. + * F_output_not on success, but no printing is performed. + * + * F_output_not (with error bit) if setting is NULL. + */ +#ifndef _di_control_print_message_packet_response_ + extern f_status_t control_print_message_packet_response(fl_print_t * const print, const control_payload_header_t header, const f_string_static_t string_status); +#endif // _di_control_print_message_packet_response_ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _control_main_print_message_h diff --git a/sources/c/program/control/main/print/warning.c b/sources/c/program/control/main/print/warning.c new file mode 100644 index 0000000..fc34f4a --- /dev/null +++ b/sources/c/program/control/main/print/warning.c @@ -0,0 +1,65 @@ +#include "../control.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _di_control_print_warning_packet_header_duplicate_object_ + f_status_t control_print_warning_packet_header_duplicate_object(fl_print_t * const print, const f_string_static_t response_header) { + + if (!print) return F_status_set_error(F_output_not); + if (print->verbosity < f_console_verbosity_verbose_e) return F_output_not; + + f_file_stream_lock(print->to); + + fl_print_format("%[%QThe received response header '%]", print->to, print->set->warning, print->prefix, print->set->warning); + fl_print_format(f_string_format_Q_single_s.string, print->to, print->set->notable, response_header, print->set->notable); + fl_print_format("%[' is repeated.%]%r", print->to, print->set->warning, print->set->warning, f_string_eol_s); + + f_file_stream_unlock(print->to); + + return F_okay; + } +#endif // _di_control_print_warning_packet_header_duplicate_object_ + +#ifndef _di_control_print_warning_packet_process_string_to_failed_ + f_status_t control_print_warning_packet_process_string_to_failed(fl_print_t * const print, const f_status_t status_of, const f_status_t status_error) { + + if (!print) return F_status_set_error(F_output_not); + if (print->verbosity < f_console_verbosity_verbose_e) return F_output_not; + + f_file_stream_lock(print->to); + + fl_print_format("%[%QFailed while calling f_status_string_to() for status%] ", print->to, print->set->warning, print->set->warning, print->set->warning); + fl_print_format(f_string_format_ui_single_s.string, print->to, print->set->notable, status_of, print->set->notable); + fl_print_format("%[, failing with status code%] ", print->to, print->set->warning, status_error, print->set->warning); + fl_print_format(f_string_format_ui_single_s.string, print->to, print->set->notable, status_error, print->set->notable); + fl_print_format(f_string_format_sentence_end_s.string, print->to, print->set->warning, print->set->warning, f_string_eol_s); + + f_file_stream_unlock(print->to); + + return F_okay; + } +#endif // _di_control_print_warning_packet_process_string_to_failed_ + +#ifndef _di_control_print_warning_packet_response_busy_ + f_status_t control_print_warning_packet_response_busy(fl_print_t * const print, const control_payload_header_t header, const f_string_static_t string_status) { + + if (!print) return F_status_set_error(F_output_not); + if (print->verbosity < f_console_verbosity_verbose_e) return F_output_not; + + f_file_stream_lock(print->to); + + fl_print_format("%[%QThe action '%]", print->to, print->set->warning, print->prefix, print->set->warning); + fl_print_format(f_string_format_Q_single_s.string, print->to, print->set->notable, control_action_type_name(header.action), print->set->notable); + fl_print_format("%[' could not be performed because the service is busy.%]%r", print->to, print->set->warning, print->set->warning, f_string_eol_s); + + f_file_stream_unlock(print->to); + + return F_okay; + } +#endif // _di_control_print_warning_packet_response_busy_ + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/sources/c/program/control/main/print/warning.h b/sources/c/program/control/main/print/warning.h new file mode 100644 index 0000000..d1bdcb7 --- /dev/null +++ b/sources/c/program/control/main/print/warning.h @@ -0,0 +1,92 @@ +/** + * FLL - Level 3 + * + * Project: Control + * API Version: 0.7 + * Licenses: lgpl-2.1-or-later + * + * Provides the print functionality. + * + * This is auto-included and should not need to be explicitly included. + */ +#ifndef _control_main_print_warning_h +#define _control_main_print_warning_h + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Print a warning message about a response header being repeated (when debugging). + * + * This program currently does not support multiple headers for any given valid header Object. + * + * @param print + * The output structure to print to. + * + * This does not alter print.custom.setting.state.status. + * @param response_header + * The repeated response header. + * + * @return + * F_okay on success. + * F_output_not on success, but no printing is performed. + * + * F_output_not (with error bit) if setting is NULL. + */ +#ifndef _di_control_print_warning_packet_header_duplicate_object_ + extern f_status_t control_print_warning_packet_header_duplicate_object(fl_print_t * const print, const f_string_static_t response_header); +#endif // _di_control_print_warning_packet_header_duplicate_object_ + +/** + * Print a warning message about a failure when calling f_status_string_to(). + * + * + * @param print + * The output structure to print to. + * + * This does not alter print.custom.setting.state.status. + * @param status_of + * The status code to be translating to a string. + * @param status_error + * The status code representing the failure status returned by f_status_string_to(). + * + * @return + * F_okay on success. + * F_output_not on success, but no printing is performed. + * + * F_output_not (with error bit) if setting is NULL. + * + * @see f_status_string_to() + */ +#ifndef _di_control_print_warning_packet_process_string_to_failed_ + extern f_status_t control_print_warning_packet_process_string_to_failed(fl_print_t * const print, const f_status_t status_of, const f_status_t status_error); +#endif // _di_control_print_warning_packet_process_string_to_failed_ + +/** + * Print a warning about a packet response returning as busy. + * + * @param print + * The output structure to print to. + * + * This does not alter print.custom.setting.state.status. + * @param header + * The control payload packet header data. + * @param status + * A string representing the name of the status code from header.status. + * + * @return + * F_okay on success. + * F_output_not on success, but no printing is performed. + * + * F_output_not (with error bit) if setting is NULL. + */ +#ifndef _di_control_print_warning_packet_response_busy_ + extern f_status_t control_print_warning_packet_response_busy(fl_print_t * const print, const control_payload_header_t header, const f_string_static_t string_status); +#endif // _di_control_print_warning_packet_response_busy_ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _control_main_print_warning_h diff --git a/sources/c/program/control/main/process.c b/sources/c/program/control/main/process.c new file mode 100644 index 0000000..0c95daf --- /dev/null +++ b/sources/c/program/control/main/process.c @@ -0,0 +1,9 @@ +#include "control.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/sources/c/program/control/main/process.h b/sources/c/program/control/main/process.h new file mode 100644 index 0000000..57a80d3 --- /dev/null +++ b/sources/c/program/control/main/process.h @@ -0,0 +1,23 @@ +/** + * FLL - Level 3 + * + * Project: Control + * API Version: 0.7 + * Licenses: lgpl-2.1-or-later + * + * Provides the process functionality. + * + * This is auto-included and should not need to be explicitly included. + */ +#ifndef _control_main_process_h +#define _control_main_process_h + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _control_main_process_h diff --git a/sources/c/program/control/main/signal.c b/sources/c/program/control/main/signal.c new file mode 100644 index 0000000..7daac73 --- /dev/null +++ b/sources/c/program/control/main/signal.c @@ -0,0 +1,111 @@ +#include "control.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(_di_control_signal_check_) && defined(_di_thread_support_) + f_status_t control_signal_check(control_main_t * const main) { + + if (!main || main->program.signal.id == -1) return F_false; + + if (!((++main->program.signal_check) % control_signal_check_d)) { + if (fll_program_standard_signal_received(&main->program)) { + fll_program_print_signal_received(&main->program.warning, main->program.signal_received); + + main->setting.state.status = F_status_set_error(F_interrupt); + + return F_true; + } + + main->program.signal_check = 0; + } + + return F_false; + } +#endif // !defined(_di_control_signal_check_) && defined(_di_thread_support_) + +#if !defined(_di_control_signal_check_) && !defined(_di_thread_support_) + f_status_t control_signal_check(control_main_t * const main) { + + if (!main || main->program.signal.id == -1) return F_false; + + if (main->program.signal_received) { + fll_program_print_signal_received(&main->program.warning, main->program.signal_received); + + main->setting.state.status = F_status_set_error(F_interrupt); + + return F_true; + } + + return F_false; + } +#endif // !defined(_di_control_signal_check_) && !defined(_di_thread_support_) + +#if !defined(_di_control_signal_handler_) && !defined(_di_thread_support_) + void control_signal_handler(control_main_t * const main) { + + if (!main) return; + + siginfo_t information; + f_number_unsigned_t failsafe = 0; + + memset(&information, 0, sizeof(siginfo_t)); + + main->program.signal_received = 0; + + f_signal_set_empty(&main->program.signal.set); + f_signal_set_add(F_signal_abort, &main->program.signal.set); + f_signal_set_add(F_signal_broken_pipe, &main->program.signal.set); + f_signal_set_add(F_signal_hangup, &main->program.signal.set); + f_signal_set_add(F_signal_interrupt, &main->program.signal.set); + f_signal_set_add(F_signal_quit, &main->program.signal.set); + f_signal_set_add(F_signal_termination, &main->program.signal.set); + + if (main->program.signal.id == -1) { + main->setting.status_signal = f_signal_open(&main->program.signal); + + if (F_status_is_error(main->setting.status_signal)) { + main->program.signal_received = F_signal_abort; + + return; + } + } + + do { + memset(&information, 0, sizeof(siginfo_t)); + + main->setting.status_signal = f_signal_wait(&main->program.signal.set, &information); + + if (F_status_is_error(main->setting.status_signal) && F_status_set_fine(main->setting.status_signal) != F_interrupt) { + if (++failsafe >= control_signal_check_failsafe_d) break; + } + + switch (information.si_signo) { + case F_signal_abort: + case F_signal_broken_pipe: + case F_signal_hangup: + case F_signal_interrupt: + case F_signal_quit: + case F_signal_termination: + main->program.signal_received = information.si_signo; + + break; + } + + failsafe = 0; + main->setting.status_signal = F_okay; + + } while (!main->program.signal_received); + + f_signal_close(&main->program.signal); + + if (F_status_is_error(main->setting.status_signal)) { + main->program.signal_received = F_signal_abort; + } + } +#endif // !defined(_di_control_signal_handler_) && !defined(_di_thread_support_) + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/sources/c/program/control/main/signal.h b/sources/c/program/control/main/signal.h new file mode 100644 index 0000000..26914d9 --- /dev/null +++ b/sources/c/program/control/main/signal.h @@ -0,0 +1,86 @@ +/** + * FLL - Level 3 + * + * Project: Control + * API Version: 0.7 + * Licenses: lgpl-2.1-or-later + * + * Provides signal functionality. + * + * This is auto-included and should not need to be explicitly included. + */ +#ifndef _control_main_signal_h +#define _control_main_signal_h + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Check to see if a signal is received. + * + * If main.signal is non-zero, then this handles the following signals: + * - F_signal_abort + * - F_signal_broken_pipe + * - F_signal_hangup + * - F_signal_interrupt + * - F_signal_quit + * - F_signal_termination + * + * There is a threaded and a non-threaded version of this. + * The non-threaded version checks periodically using control_signal_check_d and updates main->signal_check as needed. + * The threaded version checks the flag state which is set by a separate thread that is blocking until signal is received. + * + * @param main + * The main program and settings data. + * + * This does not alter main.setting.state.status. + * + * @return + * F_true on signal received. + * F_false otherwise. + * + * @see control_signal_handler() + * + * @see fll_program_standard_signal_received() + */ +#ifndef _di_control_signal_check_ + extern f_status_t control_signal_check(control_main_t * const main); +#endif // _di_control_signal_check_ + +/** + * Signal handler for signals/interrupts. + * + * This blocks until an expected signal is recieved. + * When an expected signal is received it then sets the + * + * If main.signal is non-zero, then this handles the following signals: + * - F_signal_abort + * - F_signal_broken_pipe + * - F_signal_hangup + * - F_signal_interrupt + * - F_signal_quit + * - F_signal_termination + * + * @param main + * The main program and settings data. + * + * This alters main.program.signal_received, setting it to a received signal. + * + * This alters main.setting.state.status: + * Errors (with error bit) from: f_signal_open() + * Errors (with error bit) from: f_signal_wait() + * + * @see f_signal_close() + * @see f_signal_open() + * @see f_signal_wait() + */ +#if !defined(_di_control_signal_handler_) && !defined(_di_thread_support_) + extern void control_signal_handler(control_main_t * const main); +#endif // !defined(_di_control_signal_handler_) && !defined(_di_thread_support_) + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _control_main_signal_h diff --git a/sources/c/program/control/main/thread.c b/sources/c/program/control/main/thread.c new file mode 100644 index 0000000..c9a44e2 --- /dev/null +++ b/sources/c/program/control/main/thread.c @@ -0,0 +1,22 @@ +#include "control.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(_di_control_thread_signal_) && !defined(_di_thread_support_) + void * control_thread_signal(void * const main) { + + f_thread_cancel_state_set(PTHREAD_CANCEL_DEFERRED, 0); + + if (main) { + control_signal_handler((control_main_t *) main); + } + + return 0; + } +#endif // !defined(_di_control_thread_signal_) && !defined(_di_thread_support_) + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/sources/c/program/control/main/thread.h b/sources/c/program/control/main/thread.h new file mode 100644 index 0000000..f59145c --- /dev/null +++ b/sources/c/program/control/main/thread.h @@ -0,0 +1,46 @@ +/** + * FLL - Level 3 + * + * Project: Control + * API Version: 0.7 + * Licenses: lgpl-2.1-or-later + * + * Provides thread functionality. + * + * This is auto-included and should not need to be explicitly included. + */ +#ifndef _control_main_thread_h +#define _control_main_thread_h + +/** + * Thread handler for signals/interrupts. + * + * If main.signal is non-zero, then this handles the following signals: + * - F_signal_abort + * - F_signal_broken_pipe + * - F_signal_hangup + * - F_signal_interrupt + * - F_signal_quit + * - F_signal_termination + * + * @param main + * The program and settings data. + * + * Must be of type control_main_t. + * + * @return + * 0, always. + * + * @see f_thread_cancel_state_set() + * + * @see control_signal_handler() + */ +#if !defined(_di_control_thread_signal_) && !defined(_di_thread_support_) + extern void * control_thread_signal(void * const main); +#endif // !defined(_di_control_thread_signal_) && !defined(_di_thread_support_) + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _control_main_thread_h diff --git a/specifications/settings.txt b/specifications/settings.txt new file mode 100644 index 0000000..d824231 --- /dev/null +++ b/specifications/settings.txt @@ -0,0 +1,26 @@ +# fss-0002 iki-0000 +# +# license: open-standard-license-1.0-or-later +# version 2024/07/02 +# +# This file (assumed to be named settings.txt) can be more easily read using the following iki_read commands: +# iki_read settings.txt +Q -w -W code '"' '"' +# +# To read the "Entry Specification" section of this file, use this command sequence: +# fss_basic_list_read settings.txt +Q -cn "Settings Specification" | iki_read +Q -w -W code '"' '"' +# + +Settings Specification: + The control settings "settings" file follows the FSS-0001 (Extended) format. + + Each Object represents a settings property name. + There is only a distinct set of setting property names (see below). + + Each Content represents the values associated with that property. + Additional restrictions are applied to each Content depending on each specific Object name (see below). + + Object Names and their respective Content purpose/restrictions\: + - name_socket: Must only be a single valid filename, without the directory. + - path_socket: Must only be a single valid directory. + - path_socket_prefix: Zero or one string representing a file name. + - path_socket_suffix: Zero or one string representing a file name. -- 1.8.3.1