Add documentation, specifications, and basic structural changes for the "exit" files.
The "entry" and "exit" files now reserve "setting" for designating settings.
Currently, only "entry" supports a setting and that setting is "mode".
The "mode" setting designates how the entry program is intended to behave.
When operating as a "service", the controller program will wait indefinitely for commands (via the not yet implemented "control" program or "control" socket).
When operating as a "program", the controller program will immediately exit after completion.
The "ready" Entry Action now supports "wait".
When "wait" is provided, the "ready" operation will wait for all current asynchronous processes to complete before operating.
Update documentation and specifications, adding the "exit" files.
Cleanup the existing documentation and specifications, fixing wording.
f_string_dynamic_resize(0, &setting->path_setting);
controller_entry_items_delete_simple(&setting->entry.items);
+ controller_entry_items_delete_simple(&setting->exit.items);
controller_rules_delete_simple(&setting->rules);
}
#endif // _di_controller_setting_delete_simple_
#define controller_string_entries "entries"
#define controller_string_environment "environment"
#define controller_string_existing "existing"
+ #define controller_string_exit "exit"
+ #define controller_string_exits "exits"
#define controller_string_fail "fail"
#define controller_string_failsafe "failsafe"
#define controller_string_fifo "fifo"
#define controller_string_main "main"
#define controller_string_memlock "memlock"
#define controller_string_method "method"
+ #define controller_string_mode "mode"
#define controller_string_msgqueue "msgqueue"
#define controller_string_name "name"
#define controller_string_need "need"
#define controller_string_entries_length 7
#define controller_string_environment_length 11
#define controller_string_existing_length 8
+ #define controller_string_exit_length 4
+ #define controller_string_exits_length 5
#define controller_string_fail_length 4
#define controller_string_failsafe_length 8
#define controller_string_fifo_length 4
#define controller_string_main_length 4
#define controller_string_memlock_length 7
#define controller_string_method_length 6
+ #define controller_string_mode_length 4
#define controller_string_msgqueue_length 8
#define controller_string_name_length 4
#define controller_string_need_length 4
const static f_string_t controller_string_entries_s = controller_string_entries;
const static f_string_t controller_string_environment_s = controller_string_environment;
const static f_string_t controller_string_existing_s = controller_string_existing;
+ const static f_string_t controller_string_exit_s = controller_string_exit;
+ const static f_string_t controller_string_exits_s = controller_string_exits;
const static f_string_t controller_string_fail_s = controller_string_fail;
const static f_string_t controller_string_failsafe_s = controller_string_failsafe;
const static f_string_t controller_string_fifo_s = controller_string_fifo;
const static f_string_t controller_string_main_s = controller_string_main;
const static f_string_t controller_string_memlock_s = controller_string_memlock;
const static f_string_t controller_string_method_s = controller_string_method;
+ const static f_string_t controller_string_mode_s = controller_string_mode;
const static f_string_t controller_string_msgqueue_s = controller_string_msgqueue;
const static f_string_t controller_string_name_s = controller_string_name;
const static f_string_t controller_string_need_s = controller_string_need;
controller_setting_ready_abort,
};
+ enum {
+ controller_setting_mode_service = 0,
+ controller_setting_mode_program,
+ };
+
typedef struct {
bool interruptable;
bool pid_created;
uint8_t ready;
+ uint8_t mode;
f_number_unsigned_t timeout_kill;
f_number_unsigned_t timeout_start;
f_string_dynamic_t path_setting;
controller_entry_t entry;
+ controller_entry_t exit;
controller_rules_t rules;
} controller_setting_t;
F_false, \
F_false, \
0, \
+ 0, \
3, \
3, \
3, \
f_string_dynamic_t_initialize, \
f_string_dynamic_t_initialize, \
controller_entry_t_initialize, \
+ controller_entry_t_initialize, \
controller_rules_t_initialize, \
}
#endif // _di_controller_setting_t
}
}
- // the pre-process currently only looks for "ready", so once found, pre-process is complete.
main.setting->ready = controller_setting_ready_wait;
}
else if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_item) {
error_has = F_false;
+ // "main" is not allowed to be used for an "item" and "setting" is not an executable "item".
if (fl_string_dynamic_compare_string(controller_string_main_s, actions->array[cache->ats.array[at_j]].parameters.array[0], controller_string_main_length) == F_equal_to) {
continue;
}
+ else if (fl_string_dynamic_compare_string(controller_string_setting_s, actions->array[cache->ats.array[at_j]].parameters.array[0], controller_string_setting_length) == F_equal_to) {
+ continue;
+ }
- // walk though each items and check to see if the item actually exists (skipping main).
+ // walk though each items and check to see if the item actually exists.
for (i = 1; i < main.setting->entry.items.used; ++i) {
if (fl_string_dynamic_compare(main.setting->entry.items.array[i].name, actions->array[cache->ats.array[at_j]].parameters.array[0]) == F_equal_to) {
if (entry_action->type == controller_entry_action_type_ready) {
- if (main->setting->ready == controller_setting_ready_wait) {
+ if (entry_action->code & controller_entry_rule_code_wait) {
+ if (simulate) {
+ if (main->data->error.verbosity != f_console_verbosity_quiet) {
+ f_thread_mutex_lock(&main->thread->lock.print);
+ fprintf(main->data->output.stream, "%c", f_string_eol_s[0]);
+ fprintf(main->data->output.stream, "Waiting before processing entry item action '");
+ fprintf(main->data->output.stream, "%s%s%s", main->data->context.set.title.before->string, controller_string_ready_s, main->data->context.set.title.after->string);
+ fprintf(main->data->output.stream, "'.%c", f_string_eol_s[0]);
+
+ controller_print_unlock_flush(main->data->output.stream, &main->thread->lock.print);
+ }
+ }
+
+ controller_rule_wait_all(*main, F_false, process);
+ }
+
+ if (main->setting->ready == controller_setting_ready_wait) {
if (simulate) {
if (main->data->error.verbosity != f_console_verbosity_quiet) {
f_thread_mutex_lock(&main->thread->lock.print);
at_least = 2;
at_most = 2;
}
- else {
- // for: controller_entry_action_type_ready.
- allocate = 0;
+ else if (action->type == controller_entry_action_type_ready) {
+ allocate = 1;
at_least = 0;
- at_most = 0;
+ at_most = 1;
}
if (cache->content_actions.array[i].used < at_least || cache->content_actions.array[i].used > at_most) {
fprintf(main.data->error.to.stream, "%s%s%s%s", main.data->error.context.after->string, main.data->error.notable.before->string, cache->action.name_action.string, main.data->error.notable.after->string);
fprintf(main.data->error.to.stream, "%s' requires ", main.data->error.context.before->string);
+ if (at_least == at_most) {
+ fprintf(main.data->error.to.stream, "exactly ", main.data->error.context.before->string);
+ }
+
+ fprintf(main.data->error.to.stream, "%s%s%llu%s", main.data->error.context.after->string, main.data->error.notable.before->string, at_least, main.data->error.notable.after->string);
+
if (action->type == controller_entry_action_type_consider || action->type == controller_entry_action_type_rule) {
- fprintf(main.data->error.to.stream, "%s%s%llu%s", main.data->error.context.after->string, main.data->error.notable.before->string, cache->action.line_action, main.data->error.notable.after->string);
fprintf(main.data->error.to.stream, "%s or more parameters.%s%c", main.data->error.context.before->string, main.data->error.context.after->string, f_string_eol_s[0]);
}
else {
- uint8_t parameters = 0;
-
- if (action->type == controller_entry_action_type_failsafe || action->type == controller_entry_action_type_item) {
- parameters = 1;
+ if (at_least == at_most) {
+ fprintf(main.data->error.to.stream, "%s parameters.%s%c", main.data->error.context.before->string, main.data->error.context.after->string, f_string_eol_s[0]);
}
- else if (action->type == controller_entry_action_type_timeout) {
- parameters = 2;
+ else {
+ fprintf(main.data->error.to.stream, "%s to ", main.data->error.context.before->string);
+ fprintf(main.data->error.to.stream, "%s%s%llu%s", main.data->error.context.after->string, main.data->error.notable.before->string, at_most, main.data->error.notable.after->string);
+ fprintf(main.data->error.to.stream, "%s parameters.%s%c", main.data->error.context.before->string, main.data->error.context.after->string, f_string_eol_s[0]);
}
-
- fprintf(main.data->error.to.stream, "exactly ", main.data->error.context.before->string);
- fprintf(main.data->error.to.stream, "%s%s%u%s", main.data->error.context.after->string, main.data->error.notable.before->string, parameters, main.data->error.notable.after->string);
- fprintf(main.data->error.to.stream, "%s parameters.%s%c", main.data->error.context.before->string, main.data->error.context.after->string, f_string_eol_s[0]);
}
}
}
break;
}
- for (j = 0; j < allocate; ++j) {
+ for (j = 0; j < allocate && j < cache->content_actions.array[i].used; ++j) {
action->parameters.array[j].used = 0;
}
}
}
+ else if (action->type == controller_entry_action_type_ready) {
+ if (action->parameters.used) {
+ if (fl_string_dynamic_compare_string(controller_string_wait_s, action->parameters.array[0], controller_string_wait_length) == F_equal_to) {
+ action->code |= controller_entry_rule_code_wait;
+ }
+ else {
+ action->status = F_status_set_error(F_supported_not);
+
+ if (main.data->error.verbosity != f_console_verbosity_quiet) {
+ fprintf(main.data->error.to.stream, "%c", f_string_eol_s[0]);
+ fprintf(main.data->error.to.stream, "%s%sThe entry item action may only have '", main.data->error.context.before->string, main.data->error.prefix ? main.data->error.prefix : f_string_empty_s);
+ fprintf(main.data->error.to.stream, "%s%s%s%s", main.data->error.context.after->string, main.data->error.notable.before->string, controller_string_wait_s, main.data->error.notable.after->string);
+ fprintf(main.data->error.to.stream, "%s' but instead has '", main.data->error.context.before->string);
+ fprintf(main.data->error.to.stream, "%s%s%s%s", main.data->error.context.after->string, main.data->error.notable.before->string, action->parameters.array[0].string, main.data->error.notable.after->string);
+ fprintf(main.data->error.to.stream, "%s'.%s%c", main.data->error.context.before->string, main.data->error.context.after->string, f_string_eol_s[0]);
+ }
+ }
+ }
+ }
}
}
main.setting->entry.items.used = 1;
}
}
+ else if (fl_string_dynamic_compare_string(controller_string_setting_s, cache->action.name_item, controller_string_setting_length) == F_equal_to) {
+ status = controller_entry_settings_read(*range, main, cache);
+
+ continue;
+ }
else if (main.setting->entry.items.used) {
at = main.setting->entry.items.used++;
}
if (F_status_is_error(status)) {
controller_error_print(main.data->error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true, main.thread);
+
break;
}
}
#endif // _di_controller_entry_read_
+#ifndef _di_controller_entry_settings_read_
+ f_status_t controller_entry_settings_read(const f_string_range_t content_range, controller_main_t main, controller_cache_t *cache) {
+ f_status_t status = F_none;
+
+ {
+ f_string_range_t range = content_range;
+
+ status = fll_fss_extended_read(cache->buffer_file, &range, &cache->object_actions, &cache->content_actions, 0, 0, &cache->delimits, 0);
+ }
+
+ if (F_status_is_error(status)) {
+ controller_entry_error_print(main.data->error, cache->action, F_status_set_fine(status), "fll_fss_extended_read", F_true, main.thread);
+
+ return status;
+ }
+
+ status = fl_fss_apply_delimit(cache->delimits, &cache->buffer_file);
+
+ if (F_status_is_error(status)) {
+ controller_entry_error_print(main.data->error, cache->action, F_status_set_fine(status), "fl_fss_apply_delimit", F_true, main.thread);
+
+ return status;
+ }
+
+ cache->delimits.used = 0;
+
+ f_array_length_t i = 0;
+ f_array_length_t j = 0;
+ f_array_length_t line = 0;
+
+ for (; i < cache->object_actions.used; ++i) {
+
+ status = f_fss_count_lines(cache->buffer_file, cache->object_actions.array[i].start, &cache->action.line_action);
+
+ if (F_status_is_error(status)) {
+ controller_entry_error_print(main.data->error, cache->action, F_status_set_fine(status), "f_fss_count_lines", F_true, main.thread);
+ break;
+ }
+
+ line = ++cache->action.line_action;
+
+ status = controller_string_dynamic_rip_nulless_terminated(cache->buffer_file, cache->object_actions.array[i], &cache->action.name_action);
+
+ if (F_status_is_error(status)) {
+ controller_entry_error_print(main.data->error, cache->action, F_status_set_fine(status), "controller_string_dynamic_rip_nulless_terminated", F_true, main.thread);
+ break;
+ }
+
+ if (fl_string_dynamic_compare_string(controller_string_mode_s, cache->action.name_action, controller_string_mode_length) == F_equal_to) {
+ if (cache->content_actions.array[i].used < 0 || cache->content_actions.array[i].used > 1) {
+ if (main.data->error.verbosity != f_console_verbosity_quiet) {
+ fprintf(main.data->error.to.stream, "%c", f_string_eol_s[0]);
+ fprintf(main.data->error.to.stream, "%s%sThe entry item setting '", main.data->error.context.before->string, main.data->error.prefix ? main.data->error.prefix : f_string_empty_s);
+ fprintf(main.data->error.to.stream, "%s%s%s%s", main.data->error.context.after->string, main.data->error.notable.before->string, cache->action.name_action.string, main.data->error.notable.after->string);
+ fprintf(main.data->error.to.stream, "%s' requires exactly ", main.data->error.context.before->string);
+ fprintf(main.data->error.to.stream, "%s%s%llu%s", main.data->error.context.after->string, main.data->error.notable.before->string, 1, main.data->error.notable.after->string);
+ fprintf(main.data->error.to.stream, "%s parameter.%s%c", main.data->error.context.before->string, main.data->error.context.after->string, f_string_eol_s[0]);
+ }
+
+ continue;
+ }
+
+ if (fl_string_dynamic_partial_compare_string(controller_string_service_s, cache->buffer_file, controller_string_service_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ main.setting->mode = controller_setting_mode_service;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_string_program_s, cache->buffer_file, controller_string_program_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ main.setting->mode = controller_setting_mode_program;
+ }
+ else {
+ if (main.data->warning.verbosity == f_console_verbosity_debug) {
+ fprintf(main.data->warning.to.stream, "%s%sUnknown entry item setting value '", main.data->warning.context.before->string, main.data->warning.prefix ? main.data->warning.prefix : f_string_empty_s);
+ fprintf(main.data->warning.to.stream, "%s%s", main.data->warning.context.after->string, main.data->warning.notable.before->string);
+ f_print_dynamic_partial(main.data->warning.to.stream, cache->buffer_file, cache->content_actions.array[i].array[0]);
+ fprintf(main.data->warning.to.stream, "%s%s for entry item setting '", main.data->warning.notable.after->string, main.data->warning.context.before->string);
+ fprintf(main.data->warning.to.stream, "%s%s", main.data->warning.context.after->string, main.data->warning.notable.before->string);
+ f_print_dynamic(main.data->warning.to.stream, cache->action.name_action);
+ fprintf(main.data->warning.to.stream, "%s", main.data->warning.notable.after->string);
+ fprintf(main.data->warning.to.stream, "%s'.%s%c", main.data->warning.context.before->string, main.data->warning.context.after->string, f_string_eol_s[0]);
+
+ controller_entry_error_print_cache(main.data->warning, cache->action);
+ }
+
+ continue;
+ }
+ }
+ else {
+ if (main.data->warning.verbosity == f_console_verbosity_debug) {
+ fprintf(main.data->warning.to.stream, "%s%sUnknown entry item setting '", main.data->warning.context.before->string, main.data->warning.prefix ? main.data->warning.prefix : f_string_empty_s);
+ fprintf(main.data->warning.to.stream, "%s%s", main.data->warning.context.after->string, main.data->warning.notable.before->string);
+ f_print_dynamic(main.data->warning.to.stream, cache->action.name_action);
+ fprintf(main.data->warning.to.stream, "%s", main.data->warning.notable.after->string);
+ fprintf(main.data->warning.to.stream, "%s'.%s%c", main.data->warning.context.before->string, main.data->warning.context.after->string, f_string_eol_s[0]);
+
+ controller_entry_error_print_cache(main.data->warning, cache->action);
+ }
+
+ continue;
+ }
+ } // for
+ }
+#endif // _di_controller_entry_settings_read_
+
#ifdef __cplusplus
} // extern "C"
#endif
extern f_status_t controller_entry_read(const f_string_static_t entry_name, controller_main_t main, controller_cache_t *cache) f_gcc_attribute_visibility_internal;
#endif // _di_controller_entry_read_
+/**
+ * Read the entry settings, loading all settings.
+ *
+ * @param content_range
+ * The range in the list buffer representing the content.
+ * @param main
+ * The main data.
+ * @param cache
+ * A structure for containing and caching relevant data.
+ */
+#ifndef _di_controller_entry_settings_read_
+ extern f_status_t controller_entry_settings_read(const f_string_range_t content_range, controller_main_t main, controller_cache_t *cache) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_entry_settings_read_
+
#ifdef __cplusplus
} // extern "C"
#endif
thread.id_rule = 0;
}
- if (thread.enabled) {
+ if (thread.enabled && setting->mode == controller_setting_mode_service) {
status = f_thread_create(0, &thread.id_rule, &controller_thread_rule, (void *) &main);
if (F_status_is_error(status)) {
f_thread_cancel(thread.id_signal);
}
}
- else if (data->parameters[controller_parameter_validate].result == f_console_result_none) {
+ else {
+ if (data->parameters[controller_parameter_validate].result == f_console_result_none && setting->mode == controller_setting_mode_service) {
- if (thread.id_signal) {
- f_thread_join(thread.id_signal, 0);
+ if (thread.id_signal) {
+ f_thread_join(thread.id_signal, 0);
- thread.id_signal = 0;
+ thread.id_signal = 0;
+ }
+ }
+ else if (data->parameters[controller_parameter_validate].result == f_console_result_none && setting->mode == controller_setting_mode_program) {
+ controller_rule_wait_all(main, F_false, 0);
+
+ f_thread_cancel(thread.id_signal);
+ }
+ else {
+ f_thread_cancel(thread.id_signal);
}
- }
- else {
- f_thread_cancel(thread.id_signal);
}
controller_thread_process_cancel(&main);
sigwait(&main->data->signal.set, &signal);
if (main->data->parameters[controller_parameter_interruptable].result == f_console_result_found) {
+
if (signal == F_signal_interrupt || signal == F_signal_abort || signal == F_signal_quit || signal == F_signal_termination) {
+
main->thread->signal = signal;
controller_thread_process_cancel(main);
+
break;
}
}
# fss-0005
main:
-
rule serial s_1 asynchronous
rule serial s_2 asynchronous
rule serial s_3 asynchronous
rule serial s_5 asynchronous
rule serial s_6 asynchronous
- ready
+ ready wait
# fss-0005
+setting:
+ mode program
+
main:
+ ready
+
consider asynchronous sleep_8 asynchronous
consider asynchronous sleep_10 asynchronous
rule asynchronous sleep_5 asynchronous
rule asynchronous sleep_8 asynchronous
rule asynchronous sleep_10 asynchronous
-
- ready
# fss-0005
+setting:
+ mode program
+
main:
rule serial s_1
rule serial s_2
This describes the intent and purpose of the Entry file settings.
An Entry file, such as "default.entry", is intended to store a set of rules in which the controller will process on execution.
- These are almost always meant for booting a system.
+ These are used to run some set of commands, such as booting a system.
The "main" Item Object is always executed first (Therefore "main" is both reserved and required).
All other Basic List Objects are not executed unless either an "item" or a "failsafe" specifies a valid Item name.
Execution of all Items are top-down.
+ The "setting" Item Object represents settings and is not an "item" that can be executed.
+ A number of settings are supported, but if this Item Object is not specified, then defaults are used.
+ The following settings are available: "mode".
+
+ The "mode" setting represents the mode in which the Entry is operating in.
+ The "program" mode designates that the Entry operates as a program and exits when complete (does not execute an "exit" unless one is provided).
+ The "service" mode (the default mode) designates that the Entry operates as a service and will sit and wait for control commands when complete.
+ The "service" mode will call the "exit" with the same name as this Entry, but with the extension "exit", such as "default.exit".
+
Each item supports the following Action Names: "consider", "failsafe", "item", "ready", "rule", and "timeout".
The "consider" Action is a special case of a "rule" Action.
If the pid file is created at program start, then the /var/run/controller.pid would be written before the /var/run/ directory is ready.
This could be a problem, such as on a read-only filesystem the pid creation fails and controller bails out on error.
Adding "ready" essentially specifies a point in time in the Entry in which things are expected to be safe for such basic operations.
+ When the optional "wait" is provided, then "ready" will wait for all currently started asynchronous processes to complete before operating.
The "rule" Action immediately executes a named rule file.
The first Action Parameter represents the rule directory, which is a relative directory path the rule file is to be found.
Until that execution succeeds and the daemon goes into the background the representing rule will block.
After the daemon goes into the background, then the representing rule will be fully executed.
- Any "rule" specified in this entry file is always executed using the "start" rule action type.
+ Any "rule" specified in this Entry file is always executed using the "start" rule action type.
The "timeout" Action provides default global settings for each of the three special situations: "start", "stop", and "kill".
Each of these may only have a single one exist at a time (one "start", one "stop", and one "kill").
For "kill", this represents the number of MegaTime to wait after stopping some rule and that rule has not yet stopped to forcefully stop the rule (aka kill the rule).
The timeouts are generally only valid for services such as daemon services.
A value of 0 disables this (prevents any action).
-
- This utilizes the unit of measurement called a "Time", represented with uppercase "T".
- For comparison, a unit of Time is equivalent to a nanosecond, or 10^-9 seconds.
- A MegaTime (MT) is therefore equivalent to a millisecond such that a millisecond is 10^-3 seconds.
- A unit of Time is intended to represent some unit of Time such that a single 64-bit integer may hold all units of Time for a single calendar year.
- This unit of Time does not and must not include Years (unlike Unixtime).
- To convert from Time to Unixtime, one must have a year (which could be assumed to be the current year) and then calculate all of those calendar oddities.
- A unit of Time by default is assumed to be in UTC.
- 1 (Earth) year ~= 31536000000000000 Time or 31536000 GT (GigaTime).
- 1 (Earth) day = 86400000000000 Time or 86400 GT (GigaTime).
- 1 (Earth) hour = 3600000000000 Time or 3600 GT (GigaTime).
- 1 (Earth) minute = 60000000000 Time or 60 GT (GigaTime).
- 1 (Earth) second = 1000000000 Time or 1 GT (GigaTime).
- Consequentially, 1 day in units of Time is easily represented as 86.4 TT (TeraTime).
- The Time may be stored in its "year string format".
- In this format, a Year may be prepended to the Time followed by a single colon ':' to associate a year with the Time.
- This Year has no minimum or maximum but may not have decimals.
- For example, "2020:86400000000000" would represent: January 02, 2020 0:00 UTC.
- For example, "2020:86.4 TT" would represent: January 02, 2020 0:00 UTC.
--- /dev/null
+# fss-0002
+
+Exit Documentation:
+ This describes the intent and purpose of the Exit file settings.
+
+ An Exit file, such as "default.exit", is intended to store a set of rules in which the controller will process on execution.
+ These are used to run some set of commands, such as shutting down a system.
+
+ The "main" Item Object is always executed first (Therefore "main" is both reserved and required).
+ All other Basic List Objects are not executed unless either an "item" or a "failsafe" specifies a valid Item name.
+ Execution of all Items are top-down.
+
+ The "setting" Item Object represents settings and is not an "item" that can be executed.
+ A number of settings are supported, but if this Item Object is not specified, then defaults are used.
+ There are no settings are available.
+
+ Each item supports the following Action Names: "consider", "failsafe", "item", "ready", "rule", and "timeout".
+
+ The "consider" Action is a special case of a "rule" Action.
+ All Action Parameters are the same as with the "rule" Action Parameters.
+ The difference is that "consider" is only processed (instead of being processed and executed) and when some "rule" Action designates that this consideration is required (via "need"), wanted (via "want"), or wished for (via "wish") from the within the Rule file.
+ If this is determined to be executed, then this is immediately executed when needed, wanted or wished for and applies all properties as appropriate (such as "asynchronous", for example).
+ If this is determined not to be executed, then this "consider" is ignored as if it was never there in the first place.
+
+ The "failsafe" Action accepts only a valid Item Name in which will be executed when a failure is detected.
+ Only a single "failsafe" Action may exist at a time.
+ Each successive "failsafe" Action specified replaces the previously defined "failsafe" Action (in a top-down manner).
+ When operating in "failsafe", the "require" Action is ignored given that it is meaningless once operating in failsafe.
+
+ The "item" Action accepts only a valid Item Name in which will be immediately executed.
+ Any valid Item Name, except for the reserved "main", may be used.
+
+ The "ready" Action instructs the controller program when it is safe to perform stop tasks.
+ A stop task is simply informing all controlled processes to stop.
+ When not specified, the state is always assumed to be ready (all controlled processes are immediately informed to stop).
+
+ The "rule" Action immediately executes a named rule file.
+ The first Action Parameter represents the rule directory, which is a relative directory path the rule file is to be found.
+ - Do not include leading or trailing slashes.
+ - This is relative to the settings rules directory.
+ The second Action Parameter represents the basename for the rule file, without the file extension.
+ - This must not have any directory paths.
+ The remaining Action Parameters may be specified in any order\:
+ - "asynchronous": Designates that execution will not block (wait).
+ - "require": Designates that this rule must succeed or trigger execution of failsafe.
+ - "wait": Designates that this rule will not execute until all other Actions before this (including "asynchronous" ones) finish executing (in a top-down manner).
+
+ The full path to the "rule" is relative to the settings, such that if the controller rule settings are found in "/etc/controller/rules/", then for a directory called "[directory]" and a rule basename of "[basename]", the resulting path would be: "/etc/controller/rules/[directory]/[basename].rule"
+
+ It is important to note that for any given "rule", execution within that "rule" may be internally asynchronous (even if the "rule" is synchronous).
+ For example, a service that is often called a daemon will execute in the background.
+ Until that execution succeeds and the daemon goes into the background the representing rule will block.
+ After the daemon goes into the background, then the representing rule will be fully executed.
+
+ Any "rule" specified in this Exit file is always executed using the "stop" rule action type.
+
+ The "timeout" Action provides default global settings for each of the three special situations: "start", "stop", and "kill".
+ Each of these may only have a single one exist at a time (one "start", one "stop", and one "kill").
+ Each successive "timeout" Action, specific to each Action Name (such as "start"), specified replaces the previously defined "timeout" Action (in a top-down manner).
+ Each of these accepts a single Action Parameter that is a 0 or greater whole number representing the number of MegaTime (MT) (equivalent to milliseconds).
+ For "start", this represents the number of MegaTime to wait after starting some rule before assuming something went wrong and the rule is returned as failed.
+ For "stop", this represents the number of MegaTime to wait after stopping some rule before assuming something went wrong and the rule is returned as failed.
+ For "kill", this represents the number of MegaTime to wait after stopping some rule and that rule has not yet stopped to forcefully stop the rule (aka kill the rule).
+ The timeouts are generally only valid for services such as daemon services.
+ A value of 0 disables this (prevents any action).
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.
+ The rule file is read top-down, except for the outer most list "setting", which is intended to store setting 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" Rule Type has the following FSS-0001 (Extended) Content\:
+ The "setting" Rule Type has the following FSS-0001 (Extended) Content\:
"affinity": Define one ore more processors to restrict this rule by with each number representing a specific processor by its id (starting at 0).
"capability": Define a set of capabilities in which to use, using the capability "text" format (such as "= cap_chown+ep").
"control_group": Define a control group (cgroup) in which everything within this rule executes under.
--- /dev/null
+# fss-0002
+
+Time Documentation:
+ The Controller program utilizes the unit of measurement called a "Time", represented with uppercase "T".
+ For comparison, a unit of Time is equivalent to a nanosecond, or 10^-9 seconds.
+ A MegaTime (MT) is therefore equivalent to a millisecond such that a millisecond is 10^-3 seconds.
+ A unit of Time is intended to represent some unit of Time such that a single 64-bit integer may hold all units of Time for a single calendar year.
+ This unit of Time does not and must not include Years (unlike Unixtime).
+ To convert from Time to Unixtime, one must have a year (which could be assumed to be the current year) and then calculate all of those calendar oddities.
+ A unit of Time by default is assumed to be in UTC.
+ 1 (Earth) year ~= 31536000000000000 Time or 31536000 GT (GigaTime).
+ 1 (Earth) day = 86400000000000 Time or 86400 GT (GigaTime).
+ 1 (Earth) hour = 3600000000000 Time or 3600 GT (GigaTime).
+ 1 (Earth) minute = 60000000000 Time or 60 GT (GigaTime).
+ 1 (Earth) second = 1000000000 Time or 1 GT (GigaTime).
+ Consequentially, 1 day in units of Time is easily represented as 86.4 TT (TeraTime).
+ The Time may be stored in its "year string format".
+ In this format, a Year may be prepended to the Time followed by a single colon ':' to associate a year with the Time.
+ This Year has no minimum or maximum but may not have decimals.
+ For example, "2020:86400000000000" would represent: January 02, 2020 0:00 UTC.
+ For example, "2020:86.4 TT" would represent: January 02, 2020 0:00 UTC.
An Entry file name is expected to have the file extension ".entry".
- For each entry file:
+ For each Entry file:
- The outer most part is a FSS-0002 (Basic List).
- The Basic List Object is considered the "Item".
- The Basic List Content are considered the "Actions".
The Items:
"main": required.
+ "setting": optional, Actions may be one of:
+ - "mode": either "program" or "service".
The Entry file may have any other valid Item Objects, but only the above are reserved.
The first Content that is the relative file path (without any leading/trailing slashes and without file extension).
The second Content that is the basename for a rule file.
The third and beyond may only be one of\:
- - asynchronous
- - require
- - wait
+ - "asynchronous"
+ - "require"
+ - "wait"
"failsafe": One Content that is a valid Object name, except for the reserved "main".
"item": One Content that is a valid Object name, except for the reserved "main".
- "ready": Zero Content.
+ "ready": Zero or One Content.
+ The first may only be one of\:
+ - "wait"
"rule": Two or more Content.
The first Content that is the relative directory path (without any leading/trailing slashes).
The second Content that is the basename for a rule file.
The third and beyond may only be one of\:
- - asynchronous
- - require
- - wait
+ - "asynchronous"
+ - "require"
+ - "wait"
"timeout": Two Content.
The first being one of\:
- - start
- - stop
- - kill
+ - "start"
+ - "stop"
+ - "kill"
The second being a positive whole number or 0.
--- /dev/null
+# fss-0002
+
+Exit Specification:
+ The Exit files follow the FSS-0005 (Somewhat Basic List) format.
+
+ An Exit file name is expected to have the file extension ".exit".
+
+ For each Exit file:
+ - The outer most part is a FSS-0002 (Basic List).
+ - The Basic List Object is considered the "Item".
+ - The Basic List Content are considered the "Actions".
+ - The "Actions" are FSS-0001 (Extended).
+ - Each Action Object is the "Action Name".
+ - Each Action Content are the "Action Parameters".
+
+ The Items:
+ "main": required.
+ "setting": optional, but not Actions are currently supported.
+
+ The Exit file may have any other valid Item Objects, but only the above are reserved.
+
+ The Actions\:
+ "consider": One or more Content.
+ The first Content that is the relative file path (without any leading/trailing slashes and without file extension).
+ The second Content that is the basename for a rule file.
+ The third and beyond may only be one of\:
+ - "asynchronous"
+ - "require"
+ - "wait"
+ "failsafe": One Content that is a valid Object name, except for the reserved "main".
+ "item": One Content that is a valid Object name, except for the reserved "main".
+ "ready": Zero or One Content.
+ The first may only be one of\:
+ - "wait"
+ "rule": Two or more Content.
+ The first Content that is the relative directory path (without any leading/trailing slashes).
+ The second Content that is the basename for a rule file.
+ The third and beyond may only be one of\:
+ - "asynchronous"
+ - "require"
+ - "wait"
+ "timeout": Two Content.
+ The first being one of\:
+ - "start"
+ - "stop"
+ - "kill"
+ The second being a positive whole number or 0.
"command": FSS-0003 (Extended List) or FSS-0001 (Extended).
"script": FSS-0003 (Extended List) or FSS-0001 (Extended).
"service": FSS-0003 (Extended List) or FSS-0001 (Extended).
- "settings": (Required) FSS-0001 (Extended).
+ "setting": (Required) FSS-0001 (Extended).
"utility": FSS-0003 (Extended List) or FSS-0001 (Extended).
- For the above Rule Types, "settings" may be specified only once whereas the others may be specifed multiple times.
- The "settings" Rule Type is always processed first, regardless of position.
+ For the above Rule Types, "setting" may be specified only once whereas the others may be specifed multiple times.
+ The "setting" Rule Type is always processed first, regardless of position.
The other Rule Types are processed top-down.
- The "settings" Rule Type has the following FSS-0001 (Extended)\:
+ The "setting" Rule Type has the following FSS-0001 (Extended)\:
"affinity": One or more Content, each must be a 0 or greater whole number.
"capability": One Content representing capabilities.
"control_group": Two or more Content, the first Content being either "existing" or "new" and the remaining representing a valid control group (cgroup) name, must have at least 1 graph character (non-whitespace printing character) (leading and trailing whitespace are trimmed off).