Begin prototyping and writing the controller code.
While working on this I suddenly realized that I accidentally treated FSS-0003 (Extended List) as FSS-0008 (Embedded List)!
This needs to be fixed so I am saving the progress as-is with all the note and other mess and will correct the FSS-0003 accident.
#include "controller.h"
#include "private-controller.h"
+#include "private-entry.h"
+#include "private-rule.h"
#ifdef __cplusplus
extern "C" {
#endif // _di_controller_name_
#ifndef _di_controller_defines_
+ #define controller_string_create "create"
+ #define controller_string_command "command"
+ #define controller_string_define "define"
+ #define controller_string_environment "environment"
+ #define controller_string_group "group"
+ #define controller_string_name "name"
+ #define controller_string_pid "pid"
+ #define controller_string_program "program"
+ #define controller_string_restart "restart"
+ #define controller_string_reload "reload"
+ #define controller_string_script "script"
+ #define controller_string_service "service"
+ #define controller_string_settings "settings"
+ #define controller_string_start "start"
+ #define controller_string_stop "stop"
+ #define controller_string_user "user"
+
+ #define controller_string_create_length 6
+ #define controller_string_command_length 7
+ #define controller_string_define_length 6
+ #define controller_string_environment_length 11
+ #define controller_string_group_length 5
+ #define controller_string_name_length 4
+ #define controller_string_pid_length 3
+ #define controller_string_program_length 7
+ #define controller_string_restart_length 7
+ #define controller_string_reload_length 6
+ #define controller_string_script_length 6
+ #define controller_string_service_length 7
+ #define controller_string_settings_length 8
+ #define controller_string_start_length 5
+ #define controller_string_stop_length 4
+ #define controller_string_user_length 4
enum {
controller_parameter_help,
}
#endif // _di_controller_data_t_
+#ifndef _di_controller_rule_item_t_
+ enum {
+ controller_rule_item_type_single = 1,
+ controller_rule_item_type_multiple,
+ };
+
+ enum {
+ controller_rule_item_intent_create = 1,
+ controller_rule_item_intent_program,
+ controller_rule_item_intent_group,
+ controller_rule_item_intent_restart,
+ controller_rule_item_intent_reload,
+ controller_rule_item_intent_start,
+ controller_rule_item_intent_stop,
+ controller_rule_item_intent_user,
+ };
+
+ // @fixme rule_item needs to contain a list of actions which is essentially what rule_item is currently acting as.
+ typedef struct {
+ uint8_t type;
+ uint8_t intent;
+
+ f_string_length_t line;
+
+ f_string_dynamic_t name;
+ f_string_dynamic_t content;
+ } controller_rule_item_t;
+
+ #define controller_rule_item_t_initialize \
+ { \
+ 0, \
+ 0, \
+ 0, \
+ f_string_dynamic_t_initialize, \
+ f_string_dynamic_t_initialize, \
+ }
+
+ #define f_macro_controller_rule_item_t_delete_simple(rule_item) \
+ f_macro_string_dynamic_t_delete_simple(rule_item.name) \
+ f_macro_string_dynamic_t_delete_simple(rule_item.content)
+#endif // _di_controller_rule_item_t_
+
+#ifndef _di_controller_rule_items_t_
+ typedef struct {
+ f_string_length_t line;
+ f_string_dynamic_t name;
+
+ controller_rule_item_t *array;
+
+ f_array_length_t size;
+ f_array_length_t used;
+ } controller_rule_items_t;
+
+ #define controller_rule_items_initialize \
+ { \
+ 0, \
+ f_string_dynamic_t_initialize, \
+ 0, \
+ 0, \
+ 0, \
+ }
+
+ #define f_macro_controller_rule_items_t_delete_simple(items) \
+ items.used = items.size; \
+ while (items.used > 0) { \
+ items.used--; \
+ f_macro_controller_rule_item_t_delete_simple(items.array[items.used]); \
+ if (!items.used) { \
+ if (f_memory_delete((void **) & items.array, sizeof(f_string_dynamic_t), items.size)) { \
+ items.size = 0; \
+ } \
+ } \
+ } \
+ f_macro_string_dynamic_t_delete_simple(items.name);
+#endif // _di_controller_rule_items_t_
+
/**
* Print help.
*
#include "controller.h"
#include "private-controller.h"
+#include "private-entry.h"
+#include "private-rule.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
--- /dev/null
+#include "controller.h"
+#include "private-entry.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
--- /dev/null
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.5
+ * Licenses: lgplv2.1
+ */
+#ifndef _PRIVATE_entry_h
+#define _PRIVATE_entry_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _PRIVATE_entry_h
--- /dev/null
+#include "controller.h"
+#include "private-rule.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_rule_items_increase_by_
+ f_return_status controller_rule_items_increase_by(const f_array_length_t amount, controller_rule_items_t *items) {
+ f_status_t status = F_none;
+ f_string_length_t size = items->size + amount;
+
+ if (size > f_array_length_t_size) {
+ if (items->size == f_array_length_t_size) {
+ return F_status_set_error(F_array_too_large);
+ }
+
+ size = items->size;
+ status = F_array_too_large;
+ }
+
+ const f_status_t status_resize = f_memory_resize((void **) & items.array, sizeof(controller_rule_items_t), items.size, size);
+ if (F_status_is_error(status_resize)) return status_resize;
+
+ items.size = size;
+ return status;
+ }
+#endif // _di_controller_rule_items_increase_by_
+
+#ifndef _di_controller_rule_read_
+ f_return_status controller_rule_read(const controller_data_t data, const f_string_static_t file_name, controller_rule_items_t *items) {
+ f_status_t status = F_none;
+ f_string_dynamic_t buffer = f_string_dynamic_t_initialize;
+
+ items->used = 0;
+
+ {
+ f_file_t file = f_file_t_initialize;
+
+ status = f_file_stream_open(arguments.argv[data->remaining.array[i]], 0, &file);
+
+ if (F_status_is_error(status)) {
+ fll_error_file_print(data->error, F_status_set_fine(status), "f_file_stream_open", F_true, file_name.string, "open", fll_error_file_type_file);
+ }
+ else {
+ status = f_file_stream_read(file, 1, &buffer);
+
+ if (F_status_is_error(status)) {
+ fll_error_file_print(data->error, F_status_set_fine(status), "f_file_stream_read", F_true, file_name.string, "read", fll_error_file_type_file);
+ }
+ }
+
+ f_file_stream_close(F_true, &file);
+
+ if (F_status_is_error(status)) {
+ f_macro_string_dynamic_t_delete_simple(buffer);
+ return F_false;
+ }
+ }
+
+ f_fss_objects_t objects = f_fss_objects_t_initialize;
+ f_fss_contents_t contents = f_fss_contents_t_initialize;
+
+ if (buffer.used) {
+ f_string_range_t range = f_macro_string_range_t_initialize(buffer.used);
+ f_fss_delimits_t delimits = f_fss_delimits_t_initialize;
+ f_fss_comments_t comments = f_fss_comments_t_initialize;
+
+ status = fll_fss_basic_list_read(&data_make->buffer, &range, &objects, &contents, &delimits, 0, &comments);
+
+ if (F_status_is_error(status)) {
+ fake_print_error_fss(data, status, "fll_fss_basic_list_read", data.file_data_build_fakefile.string, range, F_true);
+ }
+ else {
+ status = fl_fss_apply_delimit(delimits, &data_make->buffer);
+
+ if (F_status_is_error(status)) {
+ fll_error_print(data.error, F_status_set_fine(status), "fl_fss_apply_delimit", F_true);
+ }
+ }
+
+ f_macro_fss_delimits_t_delete_simple(delimits);
+ f_macro_fss_comments_t_delete_simple(comments);
+ }
+
+ if (F_status_is_error_not(status) && objects.used) {
+ status = controller_rule_items_increase_by(objects.used, items);
+
+ if (F_status_is_error(status)) {
+ fll_error_print(data.error, F_status_set_fine(status), "controller_rule_items_increase_by", F_true);
+ }
+ else {
+ f_string_dynamic_t content = f_string_dynamic_t_initialize;
+
+ for (f_array_length_t i = 0; i < objects.used; ++i) {
+
+ status = f_fss_count_lines(buffer, objects.array[i], &items->array[items->used].line);
+
+ if (F_status_is_error(status)) {
+ fll_error_print(data.error, F_status_set_fine(status), "f_fss_count_lines", F_true);
+ break;
+ }
+
+ status = fl_string_dynamic_partial_append_nulless(buffer, objects.array[i], items->array[items->used].name);
+
+ if (F_status_is_error(status)) {
+ fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_partial_append_nulless", F_true);
+ break;
+ }
+
+ status = fl_string_dynamic_terminate_after(items->array[items->used].name);
+
+ if (F_status_is_error(status)) {
+ fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_terminate_after", F_true);
+ break;
+ }
+
+ content.used = 0;
+ status = fl_string_dynamic_partial_append(buffer, contents.array[i].array[0], &content);
+
+ if (F_status_is_error(status)) {
+ fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_partial_append", F_true);
+ break;
+ }
+
+ status = controller_rule_read_content(data, file_name, items->array[items->used].line, &content, items);
+ if (F_status_is_error(status)) break;
+
+ items->used++;
+ } // for
+
+ f_macro_fss_content_t_delete_simple(content);
+ }
+ }
+
+ f_macro_fss_objects_t_delete_simple(objects);
+ f_macro_fss_contents_t_delete_simple(contents);
+ f_macro_string_dynamic_t_delete_simple(buffer);
+
+ return status;
+ }
+#endif // _di_controller_rule_read_
+
+#ifndef _di_controller_rule_read_content_
+ f_return_status controller_rule_read_content(const controller_data_t data, const f_string_static_t file_name, const f_string_length_t line, f_string_static_t *content, controller_rule_items_t *items) {
+ f_status_t status = F_none;
+
+ f_string_range_t range = f_macro_string_range_t_initialize(content->used);
+ f_string_length_t last = 0;
+
+ f_fss_delimits_t delimits = f_fss_delimits_t_initialize;
+ f_fss_comments_t comments = f_fss_comments_t_initialize;
+ f_fss_object_t child_object = f_fss_object_t_initialize;
+ f_fss_content_t child_content = f_fss_content_t_initialize;
+ f_fss_quote_t quote = f_fss_quote_t_initialize;
+
+ uint8_t type = 0;
+
+ for (range.start = 0; range.start < content->used; last = range.start, type = 0) {
+
+ delimits.used = 0;
+ status = fl_fss_extended_list_object_read(content, &range, &child_object, &delimits);
+
+ if (F_status_is_error(status)) {
+ fll_error_file_print(data->error, F_status_set_fine(status), "fl_fss_extended_list_object_read", F_true, file_name.string, "read", fll_error_file_type_file);
+ break;
+ }
+
+ if (range.start >= range.stop || range.start >= content->used) {
+ if (status == FL_fss_found_object || status == FL_fss_found_object_content_not) {
+ if (error.verbosity != f_console_verbosity_quiet) {
+ fprintf(data.error.to.stream, "%c", f_string_eol[0]);
+ fprintf(data.error.to.stream, "%s%sUnterminated FSS Extended List at end of file '", data.error.context.before->string, data.error.prefix ? data.error.prefix : "");
+ fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, file_name.string, data.error.notable.after->string);
+ fprintf(data.error.to.stream, "%s'.%s%c", data.error.context.before->string, data.error.context.after->string, f_string_eol[0]);
+ }
+
+ status = F_status_set_error(FL_fss_found_object_content_not);
+ }
+
+ break;
+ }
+
+ if (status == FL_fss_found_object) {
+ // @fixme I just noticed that Extended List wasn't intended to be recursive, this will be updated.
+ comments.used = 0;
+ status = fl_fss_extended_list_content_read(content, &range, &child_content, &delimits, &comments);
+
+ if (F_status_is_error(status)) {
+ fll_error_file_print(data->error, F_status_set_fine(status), "fl_fss_extended_list_content_read", F_true, file_name.string, "read", fll_error_file_type_file);
+ break;
+ }
+
+ if (status == FL_fss_found_content) {
+ type = controller_rule_item_type_multiple;
+ }
+ }
+ else {
+ status = fl_fss_basic_object_read(content, &range, &child_object, "e, &delimits);
+
+ if (F_status_is_error(status)) {
+ fll_error_file_print(data->error, F_status_set_fine(status), "fl_fss_basic_object_read", F_true, file_name.string, "read", fll_error_file_type_file);
+ break;
+ }
+
+ if (range.start >= range.stop || range.start >= content->used) {
+ // in this case, if status is FL_fss_found_object or FL_fss_found_object_content_not, there is no content so do not save this item.
+ break;
+ }
+
+ if (status == FL_fss_found_object) {
+ status = fl_fss_basic_content_read(content, &range, &child_content, &delimits);
+
+ if (F_status_is_error(status)) {
+ fll_error_file_print(data->error, F_status_set_fine(status), "fl_fss_basic_content_read", F_true, file_name.string, "read", fll_error_file_type_file);
+ break;
+ }
+
+ if (status == FL_fss_found_content) {
+ type = controller_rule_item_type_single;
+ }
+ }
+ }
+
+ if (type) {
+ status = fl_fss_apply_delimit(delimits, &data_make->buffer);
+
+ if (F_status_is_error(status)) {
+ fll_error_file_print(data->error, F_status_set_fine(status), "fl_fss_apply_delimit", F_true, file_name.string, "read", fll_error_file_type_file);
+ break;
+ }
+
+ status = f_fss_count_lines(buffer, contents.array[i], &items->array[items->used].line);
+
+ if (F_status_is_error(status)) {
+ fll_error_print(data.error, F_status_set_fine(status), "f_fss_count_lines", F_true);
+ break;
+ }
+
+ // @todo
+ //if (fl_string_dynamic_compare_string(x, items->array[i].name) == f_equal_to) {
+ //items->array[i].name
+ // @todo
+ //}
+ }
+ } // for
+
+ f_macro_fss_delimits_t_delete_simple(delimits);
+ f_macro_fss_comments_t_delete_simple(comments);
+ f_macro_fss_content_t_delete_simple(child_content);
+
+ if (F_status_is_error(status)) return status;
+
+ /*
+ uint8_t type;
+ uint8_t intent;
+
+ f_string_length_t line;
+
+ f_string_dynamic_t name;
+ f_string_dynamic_t content;
+ */
+ /*
+ status = fl_string_dynamic_partial_append_nulless(buffer, objects.array[i], items->array[i].name);
+
+ if (F_status_is_error(status)) {
+ fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_partial_append_nulless", F_true);
+ break;
+ }
+
+ status = fl_string_dynamic_terminate_after(items->array[i].name);
+
+ if (F_status_is_error(status)) {
+ fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_terminate_after", F_true);
+ break;
+ }
+
+
+ */
+ }
+#endif // _di_controller_rule_read_content_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
--- /dev/null
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.5
+ * Licenses: lgplv2.1
+ */
+#ifndef _PRIVATE_rule_h
+#define _PRIVATE_rule_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Increase the size of the rule items array by the specified amount, but only if necessary.
+ *
+ * This only increases size if the current used plus amount is greater than the currently allocated size.
+ *
+ * @param amount
+ * A positive number representing how much to increase the size by.
+ * @param items
+ * The items to resize.
+ *
+ * @return
+ * F_none on success.
+ * F_array_too_large on success, but requested size is too small (resize is smaller than requested length).
+ *
+ * Errors (with error bit) from: f_memory_resize().
+ */
+#ifndef _di_controller_rule_items_increase_by_
+ extern f_return_status controller_rule_items_increase_by(const f_array_length_t amount, controller_rule_items_t *items) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_rule_items_increase_by_
+
+/**
+ * Read the content within the buffer, extracting all valid items.
+ *
+ * @param data
+ * The program data.
+ * @param file_name
+ * The file name string.
+ * @param items
+ * An array of items associated with the rule.
+ *
+ * @return
+ * F_none on success.
+ *
+ * Errors (with error bit) from: controller_rule_items_increase_by().
+ * Errors (with error bit) from: controller_rule_read_content().
+ * Errors (with error bit) from: f_file_stream_open().
+ * Errors (with error bit) from: f_file_stream_read().
+ * Errors (with error bit) from: f_fss_count_lines().
+ * Errors (with error bit) from: fl_fss_apply_delimit().
+ * Errors (with error bit) from: fl_string_dynamic_partial_append().
+ * Errors (with error bit) from: fl_string_dynamic_partial_append_nulless().
+ * Errors (with error bit) from: fl_string_dynamic_terminate_after().
+ * Errors (with error bit) from: fll_fss_basic_list_read().
+ */
+#ifndef _di_controller_rule_read_
+ extern f_return_status controller_rule_read(const controller_data_t data, const f_string_static_t file_name, controller_rule_items_t *items) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_rule_read_
+
+/**
+ * Read the content within the buffer, extracting all valid items after determining their type for some rule file.
+ *
+ * This will perform additional FSS read functions as appropriate.
+ *
+ * @param data
+ * The program data.
+ * @param file_name
+ * The file name string.
+ * @param line
+ * The line number where the content begins.
+ * @param content
+ * The buffer containing the content.
+ * @param items
+ * An array of items associated with the rule.
+ *
+ * @return
+ * F_none on success.
+ *
+ * Errors (with error bit) from: f_fss_count_lines().
+ * Errors (with error bit) from: fl_string_dynamic_partial_append_nulless().
+ * Errors (with error bit) from: fl_string_dynamic_terminate_after().
+ */
+#ifndef _di_controller_rule_read_content_
+ extern f_return_status controller_rule_read_content(const controller_data_t data, const f_string_static_t file_name, const f_string_length_t line, f_string_static_t *content, controller_rule_items_t *items) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_rule_read_content_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _PRIVATE_rule_h
build_libraries-individual -lfll_error -lfll_program -lfll_status -lfl_color -lfl_console -lfl_status -lfl_string -lf_console -lf_conversion -lf_file -lf_memory -lf_pipe -lf_print -lf_utf
build_libraries-level -lfll_2 -lfll_1 -lfll_0
build_libraries-monolithic -lfll
-build_sources_library controller.c private-controller.c
+build_sources_library controller.c private-controller.c private-entry.c private-rule.c
build_sources_program main.c
build_sources_headers controller.h
build_sources_script
Rule Documentation:
This describes the intent and purpose of the rule file settings.
- A rule file, such as "ssh.rule", is intended to desginate what to execute.
+ A rule file, such as "ssh.rule", is intended to designate what to execute.
The rule file is read top-down, except for the outer most list "settings", which is intended to store settings data for this rule.
Multiple outer most list Objects may be specified and they are executed as provided, in a top-down manner.
The "settings" outer most list Object has the following FSS-0001 (Extended) Content:
+ "define": Define a custom environment variable with a given variable, and automatically expose it to processes executed within this rule.
+ "environment": A set of environment variables to expose to the processes executed within this rule (PATH is always exposed).
"name": A name used to represent this rule, which is printing to the user, screen, logs, etc...
"pid": A path to a directory where the PID file is expected to be stored in.
@todo: consider adding "path" to allow specifying a custom environment PATH variable (or even go a step further and instead provide "environment" or "variable").
The "reload" Content is performed whenever this rule is executed using the reload action.
When "restart" Content is not provided, then "start" and "stop" is called when the rule is executed using the restart action, if both "start" and "stop" are provided.
- When "reload", "start", or "stop" Content are not provided, then no respective action is peformed.
+ When "reload", "start", or "stop" Content are not provided, then no respective action is performed.
The "group" inner Content, an associated "command", "service", or "script" are executed with that group ID or group name.
The "user" inner Content, an associated "command", "service", or "script" are executed with that user ID or user name.
For the above Basic List Objects, "main" may be specified only once whereas the others may be specifed multiple times.
The "settings" outer most list Object has the following FSS-0001 (Extended) Content:
+ "define": Two Content, the first Content must be a case-sensitive valid environment variable name (alpha-numeric or underscore, but no leading digits).
+ "environment": Zero or more Content, each must be a case-sensitive valid environment variable name (alpha-numeric or underscore, but no leading digits).
"name": One Content, must have at least 1 non-whitespace printing character.
"pid": One Content representing the path to a PID file directory.
# fss-0002
Featureless Settings Specification: 000D - Basic Rule:
- @todo: document this.
- This should be a special case that has an FSS Basic List on the outside and conditional types on the inside.
- The conditional types are context-specific and not defined by this specification.
- This specification only defines that they exist and not when/how they exist.
+ This is a special case that follows FSS-0002 (Basic List), and different FSS formats inside this Basic List.
+ This Basic List is considered the "Outer List" and the Content of this Outer List is considered the "Inner Content".
+
+ The Inner Content may be any of the following FSS formats: FSS-0000 (Basic), FSS-0001 (Extended), and FSS-0003 (Extended List).
+ The way in which each format is determined is first to see if the Inner Content Object would be a valid Extended List Object.
+ If the Inner Content Object is not a valid Extended List Object, then check to see if it is an Basic/Extended Object.
+
+ The FSS Basic and FSS Extended Objects have the same format for Object names but the FSS Extended List Object is slightly different.
+ Anything that would match an FSS Extended List Object must therefore be an FSS Extended List Object.
+
+ Anything implementing this specification may impose its own restrictions on when to determine if the Inner Content is what FSS format, based on Object names.
+
+ See the appropriate specifications for the Key/Structure/Example documentation for the respective FSS-0000 (Basic), FSS-0001 (Extended), and FSS-0003 (Extended List).
+
+ Example\:
+
+ main:
+ name boot-devices
+
+ script:
+ start {
+ ip addr add 127.0.0.1/8 label lo dev lo;
+ ip link set lo up;
+ }
+
+ stop {
+ ip link set lo down;
+ }
+
+ command:
+ start mount -a -O no_netdev
+ stop umount -arf -O no_netdev
+
+ Outer List Objects would be\:
+ 1) main
+ 2) script
+ 3) command
+
+ Outer List Contents would be\:
+ 1.1) name boot-devices
+
+ 2.1) start {
+ ip addr add 127.0.0.1/8 label lo dev lo;
+ ip link set lo up;
+ }
+
+ stop {
+ ip link set lo down;
+ }
+
+ 3.1) begin mount -a -O no_netdev
+ end umount -arf -O no_netdev
+
+ Inner Content Objects would be\:
+ 1.1.1) name
+ 2.1.1) start
+ 2.1.2) stop
+ 3.1.1) begin
+ 3.1.2) end
+
+ Inner Content Contents would be\:
+ 1.1.1) boot-devices
+ 2.1.1) ip addr add 127.0.0.1/8 label lo dev lo;
+ ip link set lo up;
+ 2.1.2) ip link set lo down;
+ 3.1.1) mount -a -O no_netdev
+ 3.1.2) umount -arf -O no_netdev
+
+++ /dev/null
-# fss-0002
-
-Featureless Settings Specification: 000E - Extended Rule:
- @todo: document this.
- This should be a special case that has an FSS Extended List on the outside and conditional types on the inside.
- The conditional types are context-specific and not defined by this specification.
- This specification only defines that they exist and not when/how they exist.
- fss-000B: Simple List
- fss-000C: Iki Text
- fss-000D: Basic Rule
- - fss-000E: Extended Rule