Change "timeout" property to the more generic "number".
This can then be used for types "timeout" and "item", whereas the "item" number represents the location within the items list.
This is not known until all entry items are loaded and is therefore updated during the pre-process phase.
This implements the pre-process phase after the load phase.
The pre-process will perform additional checks, such as determining if "ready" is set somewhere in the active chain.
This makes sure recursion to an item already on the stack is not allowed.
Do some cleanups, reducing the if/then nesting by adding "do nothing" comment and letting the "else" condition handle the case appropriately.
Internally, line counting starts at 0, but when dealing with presenting the lines to users, the lines start at 1.
Change the behavior to increase the line number appropriately for entries.
The same will likely need to be done for rules.
Have the default.entry provide "ready".
}
}
- // @todo create pid file but not until "ready", so be sure to add this after pre-processing the entry file.
- if (setting.ready) {
- controller_file_pid_create(*data, setting.path_pid);
- }
-
if (F_status_is_error_not(status)) {
- status = controller_preprocess_rules(*data, &setting, &cache);
+ status = controller_preprocess_items(*data, &setting, &cache);
}
if (F_status_is_error_not(status)) {
+
if (data->parameters[controller_parameter_test].result == f_console_result_found || data->parameters[controller_parameter_validate].result == f_console_result_found) {
// @todo validate happens first, report and handle validation problems or success.
else {
// @todo check to see if the standard pid file exists before attempting to start (when in normal operation mode).
// @todo real execution.
+
+ // @todo create pid file but not until "ready", so be sure to add this after pre-processing the entry file.
+ if (setting.ready) {
+ controller_file_pid_create(*data, setting.path_pid);
+ }
+
+ // @todo when everything is ready, wait here until told to quit.
+ // @todo clear cache periodically while waiting and no commands.
+ // controller_macro_cache_t_delete_simple(cache);
+
+ // @todo on failures that prevent normal execution: trigger failsafe/fallback execution, if defined
+ // @todo otherwise, set ready to controller_setting_ready_done.
}
}
#include <level_1/console.h>
#include <level_1/fss.h>
#include <level_1/string.h>
+#include <level_1/type.h>
// fll-2 includes
#include <level_2/error.h>
#ifndef _di_controller_defines_
- // must be at least 2.
+ // This specifically must be at least 2 for this project.
#define controller_default_allocation_step 4
#define controller_path_pid "/var/run/controller/controller.pid"
uint8_t code;
f_string_length_t line;
- f_number_unsigned_t timeout;
+ f_number_unsigned_t number;
f_status_t status;
controller_setting_ready_no = 0,
controller_setting_ready_wait,
controller_setting_ready_yes,
+ controller_setting_ready_done,
};
typedef struct {
f_string_range_t range_action;
+ f_array_lengths_t ats;
+
f_fss_comments_t comments;
f_fss_delimits_t delimits;
0, \
0, \
f_string_range_t_initialize, \
+ f_array_lengths_t_initialize, \
f_fss_comments_t_initialize, \
f_fss_delimits_t_initialize, \
f_fss_content_t_initialize, \
}
#define controller_macro_cache_t_delete_simple(cache) \
+ f_macro_array_lengths_t_delete_simple(cache.ats) \
f_macro_fss_comments_t_delete_simple(cache.comments) \
f_macro_fss_delimits_t_delete_simple(cache.delimits) \
f_macro_fss_content_t_delete_simple(cache.content_action) \
extern "C" {
#endif
+#ifndef _di_controller_entry_action_type_name_
+ f_string_static_t controller_entry_action_type_name(const uint8_t type) {
+
+ f_string_static_t buffer = f_string_static_t_initialize;
+
+ switch (type) {
+ case controller_entry_action_type_consider:
+ buffer.string = controller_string_consider;
+ buffer.used = controller_string_consider_length;
+ break;
+
+ case controller_entry_action_type_failsafe:
+ buffer.string = controller_string_failsafe;
+ buffer.used = controller_string_failsafe_length;
+ break;
+
+ case controller_entry_action_type_item:
+ buffer.string = controller_string_item;
+ buffer.used = controller_string_item_length;
+ break;
+
+ case controller_entry_action_type_ready:
+ buffer.string = controller_string_ready;
+ buffer.used = controller_string_ready_length;
+ break;
+
+ case controller_entry_action_type_rule:
+ buffer.string = controller_string_rule;
+ buffer.used = controller_string_rule_length;
+ break;
+
+ case controller_entry_action_type_timeout:
+ buffer.string = controller_string_timeout;
+ buffer.used = controller_string_timeout_length;
+ break;
+ }
+
+ buffer.size = buffer.used;
+
+ return buffer;
+ }
+#endif // _di_controller_entry_action_type_name_
+
#ifndef _di_controller_file_load_
f_return_status controller_file_load(const controller_data_t data, const controller_setting_t setting, const f_string_t path_prefix, const f_string_static_t path_name, const f_string_t path_suffix, const f_string_length_t path_prefix_length, const f_string_length_t path_suffix_length, f_string_dynamic_t *path_file, f_string_dynamic_t *buffer) {
f_status_t status = F_none;
}
#endif // _di_controller_file_pid_delete_
-#ifndef _di_controller_preprocess_rules_
- f_return_status controller_preprocess_rules(const controller_data_t data, controller_setting_t *setting, controller_cache_t *cache) {
- // @todo
- return F_none;
+#ifndef _di_controller_preprocess_items_
+ f_return_status controller_preprocess_items(const controller_data_t data, controller_setting_t *setting, controller_cache_t *cache) {
+ f_status_t status = F_none;
+ f_status_t status2 = F_none;
+
+ f_array_length_t i = 0;
+ f_array_length_t j = 0;
+
+ f_array_length_t at_i = 0;
+ f_array_length_t at_j = 1;
+
+ controller_entry_actions_t *actions = 0;
+
+ uint8_t error_has = F_false;
+
+ setting->ready = controller_setting_ready_no;
+
+ cache->ats.used = 0;
+
+ status = fl_type_array_lengths_increase_by(controller_default_allocation_step, &cache->ats);
+
+ if (F_status_is_error(status)) {
+ fll_error_print(data.error, F_status_set_fine(status), "fl_type_array_lengths_increase_by", F_true);
+ return status;
+ }
+
+ cache->ats.array[0] = 0;
+ cache->ats.array[1] = 0;
+ cache->ats.used = 2;
+
+ cache->line_item = setting->entry.items.array[0].line;
+ cache->name_item.used = 0;
+
+ status = fl_string_dynamic_append(setting->entry.items.array[0].name, &cache->name_item);
+
+ if (F_status_is_error(status)) {
+ fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_append", F_true);
+ controller_entry_error_print(data.error, *cache);
+
+ return status;
+ }
+
+ status = fl_string_dynamic_terminate_after(&cache->name_item);
+
+ if (F_status_is_error(status)) {
+ fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_terminate_after", F_true);
+ controller_entry_error_print(data.error, *cache);
+
+ return status;
+ }
+
+ for (;;) {
+
+ actions = &setting->entry.items.array[cache->ats.array[at_i]].actions;
+
+ for (; cache->ats.array[at_j] < actions->used; ++cache->ats.array[at_j]) {
+
+ cache->line_action = actions->array[cache->ats.array[at_j]].line;
+ cache->name_action.used = 0;
+
+ status2 = fl_string_dynamic_append(controller_entry_action_type_name(actions->array[cache->ats.array[at_j]].type), &cache->name_action);
+
+ if (F_status_is_error(status2)) {
+ fll_error_print(data.error, F_status_set_fine(status2), "fl_string_dynamic_append", F_true);
+ controller_entry_error_print(data.error, *cache);
+
+ return status2;
+ }
+
+ status2 = fl_string_dynamic_terminate_after(&cache->name_action);
+
+ if (F_status_is_error(status2)) {
+ fll_error_print(data.error, F_status_set_fine(status2), "fl_string_dynamic_terminate_after", F_true);
+ controller_entry_error_print(data.error, *cache);
+
+ return status2;
+ }
+
+ if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_ready) {
+
+ if (setting->ready == controller_setting_ready_wait) {
+ if (data.warning.verbosity == f_console_verbosity_debug) {
+ fprintf(data.warning.to.stream, "%c", f_string_eol[0]);
+ fprintf(data.warning.to.stream, "%s%sMultiple '", data.warning.context.before->string, data.warning.prefix ? data.warning.prefix : "");
+ fprintf(data.warning.to.stream, "%s%s%s%s", data.warning.context.after->string, data.warning.notable.before->string, controller_string_ready, data.warning.notable.after->string);
+ fprintf(data.warning.to.stream, "%s' entry item actions detected; only the first will be used.%s%c", data.warning.context.before->string, data.warning.context.after->string, f_string_eol[0]);
+
+ controller_entry_error_print(data.warning, *cache);
+ }
+ }
+
+ // the pre-process currently only looks for "ready", so once found, pre-process is complete.
+ 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;
+
+ if (fl_string_dynamic_compare_string(controller_string_main, actions->array[cache->ats.array[at_j]].parameters.array[0], controller_string_main_length) == F_equal_to) {
+ continue;
+ }
+
+ // walk though each items and check to see if the item actually exists (skipping main).
+ for (i = 1; i < setting->entry.items.used; ++i) {
+
+ if (fl_string_dynamic_compare(setting->entry.items.array[i].name, actions->array[cache->ats.array[at_j]].parameters.array[0]) == F_equal_to) {
+
+ // check to see if "i" is already in the stack (to prevent recursion) (skipping main).
+ for (j = 2; j < cache->ats.used; j += 2) {
+
+ if (cache->ats.array[j] == i) {
+ if (data.error.verbosity != f_console_verbosity_quiet) {
+ fprintf(data.error.to.stream, "%c", f_string_eol[0]);
+ fprintf(data.error.to.stream, "%s%sThe entry item named '", 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, setting->entry.items.array[i].name.string, data.error.notable.after->string);
+ fprintf(data.error.to.stream, "%s' cannot be executed because recursion is not allowed.%s%c", data.error.context.before->string, data.error.context.after->string, f_string_eol[0]);
+ }
+
+ controller_entry_error_print(data.error, *cache);
+
+ if (F_status_is_error_not(status)) {
+ status = F_status_set_error(F_recurse);
+ }
+
+ error_has = F_true;
+ break;
+ }
+ } // for
+
+ if (error_has) break;
+
+ status2 = fl_type_array_lengths_increase_by(controller_default_allocation_step, &cache->ats);
+
+ if (F_status_is_error(status2)) {
+ fll_error_print(data.error, F_status_set_fine(status2), "fl_type_array_lengths_increase_by", F_true);
+ controller_entry_error_print(data.error, *cache);
+
+ return status2;
+ }
+
+ // save the value so to avoid string comparison during normal operation.
+ actions->array[cache->ats.array[at_j]].number = i;
+
+ // continue into the requested item.
+ at_i = cache->ats.used;
+ at_j = cache->ats.used + 1;
+
+ cache->ats.array[at_i] = i;
+ cache->ats.array[at_j] = 0;
+ cache->ats.used += 2;
+
+ cache->name_action.used = 0;
+ cache->line_action = 0;
+
+ cache->name_item.used = 0;
+ cache->line_item = setting->entry.items.array[i].line;
+
+ status2 = fl_string_dynamic_append(setting->entry.items.array[i].name, &cache->name_item);
+
+ if (F_status_is_error(status2)) {
+ fll_error_print(data.error, F_status_set_fine(status2), "fl_string_dynamic_append", F_true);
+ controller_entry_error_print(data.error, *cache);
+
+ return status2;
+ }
+
+ status2 = fl_string_dynamic_terminate_after(&cache->name_item);
+
+ if (F_status_is_error(status2)) {
+ fll_error_print(data.error, F_status_set_fine(status2), "fl_string_dynamic_terminate_after", F_true);
+ controller_entry_error_print(data.error, *cache);
+
+ return status2;
+ }
+
+ break;
+ }
+ } // for
+
+ if (error_has || i >= setting->entry.items.used) {
+ if (i >= setting->entry.items.used) {
+ if (data.error.verbosity != f_console_verbosity_quiet) {
+ fprintf(data.error.to.stream, "%c", f_string_eol[0]);
+ fprintf(data.error.to.stream, "%s%sThe entry item named '", 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, actions->array[cache->ats.array[at_j]].parameters.array[0].string, data.error.notable.after->string);
+ fprintf(data.error.to.stream, "%s' does not exist.%s%c", data.error.context.before->string, data.error.context.after->string, f_string_eol[0]);
+ }
+
+ controller_entry_error_print(data.error, *cache);
+
+ if (F_status_is_error_not(status)) {
+ status = F_status_set_error(F_valid_not);
+ }
+ }
+ }
+ else {
+ break;
+ }
+ }
+ } // for
+
+ cache->line_action = 0;
+ cache->name_action.used = 0;
+
+ // end of actions found, so drop to previous loop in stack.
+ if (cache->ats.array[at_j] == actions->used) {
+
+ // all actions for "main" are processed so there is nothing left to do.
+ if (at_i == 0) break;
+
+ at_i -= 2;
+ at_j -= 2;
+
+ cache->ats.used -= 2;
+ cache->ats.array[at_j]++;
+
+ cache->line_item = setting->entry.items.array[cache->ats.array[at_i]].line;
+ cache->name_item.used = 0;
+
+ status2 = fl_string_dynamic_append(setting->entry.items.array[cache->ats.array[at_i]].name, &cache->name_item);
+
+ if (F_status_is_error(status2)) {
+ fll_error_print(data.error, F_status_set_fine(status2), "fl_string_dynamic_append", F_true);
+ controller_entry_error_print(data.error, *cache);
+
+ return status2;
+ }
+
+ status2 = fl_string_dynamic_terminate_after(&cache->name_item);
+
+ if (F_status_is_error(status2)) {
+ fll_error_print(data.error, F_status_set_fine(status2), "fl_string_dynamic_terminate_after", F_true);
+ controller_entry_error_print(data.error, *cache);
+
+ return status2;
+ }
+ }
+ } // for
+
+ // if ready was never found in the entry, then default to always ready.
+ if (setting->ready == controller_setting_ready_no) {
+ setting->ready = controller_setting_ready_yes;
+ }
+
+ cache->ats.used = 0;
+ cache->line_action = 0;
+ cache->line_item = 0;
+ cache->name_action.used = 0;
+ cache->name_item.used = 0;
+
+ return status;
}
-#endif // _di_controller_preprocess_rules_
+#endif // _di_controller_preprocess_items_
#ifndef _di_controller_status_simplify_
f_return_status controller_status_simplify(const f_status_t status) {
return F_status_set_error(F_number);
}
- if (status == F_parameter || status == F_found_not || status == F_interrupt) {
+ if (status == F_parameter || status == F_found_not || status == F_interrupt || status == F_supported_not) {
return F_status_set_error(status);
}
#ifdef __cplusplus
extern "C" {
#endif
+/**
+ * Get a string representing the entry action type.
+ *
+ * @param type
+ * The entry action type code.
+ *
+ * @return
+ * The string with used > 0 on success.
+ * The string with used == 0 if no match was found.
+ */
+#ifndef _di_controller_entry_action_type_name_
+ extern f_string_static_t controller_entry_action_type_name(const uint8_t type) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_entry_action_type_name_
/**
* Load a file from the controller settings directory.
* @return
* F_none on success.
*/
-#ifndef _di_controller_preprocess_rules_
- extern f_return_status controller_preprocess_rules(const controller_data_t data, controller_setting_t *setting, controller_cache_t *cache) f_gcc_attribute_visibility_internal;
-#endif // _di_controller_preprocess_rules_
+#ifndef _di_controller_preprocess_items_
+ extern f_return_status controller_preprocess_items(const controller_data_t data, controller_setting_t *setting, controller_cache_t *cache) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_preprocess_items_
/**
* Given a wide range of status codes, simplify them down to a small subset.
action = &actions->array[actions->used];
action->type = 0;
action->code = 0;
- action->line = cache->line_action;
- action->timeout = 0;
+ action->line = 0;
+ action->number = 0;
action->status = F_known_not;
action->parameters.used = 0;
break;
}
+ cache->line_action++;
+ action->line = cache->line_action;
+
status = fl_string_dynamic_rip_nulless(cache->buffer_file, cache->object_actions.array[i], &cache->name_action);
if (F_status_is_error(status)) {
}
} // for
}
+ else if (action->type == controller_entry_action_type_item) {
+ if (fl_string_dynamic_compare_string(controller_string_main, action->parameters.array[0], controller_string_main_length) == F_equal_to) {
+ action->status = F_status_set_error(F_supported_not);
+
+ if (F_status_is_error_not(status_action)) {
+ status_action = action->status;
+ }
+
+ if (data.error.verbosity != f_console_verbosity_quiet) {
+ fprintf(data.error.to.stream, "%c", f_string_eol[0]);
+ fprintf(data.error.to.stream, "%s%sThe entry item action may not specify the reserved item '", 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, controller_string_main, 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]);
+ }
+ }
+ }
else if (action->type == controller_entry_action_type_timeout) {
- if (fl_string_dynamic_compare_string(controller_string_kill, action->parameters.array[0], controller_string_kill_length) == F_equal_to_not) {
- if (fl_string_dynamic_compare_string(controller_string_start, action->parameters.array[0], controller_string_start_length) == F_equal_to_not) {
- if (fl_string_dynamic_compare_string(controller_string_stop, action->parameters.array[0], controller_string_stop_length) == F_equal_to_not) {
- action->status = F_status_set_error(F_supported_not);
+ if (fl_string_dynamic_compare_string(controller_string_kill, action->parameters.array[0], controller_string_kill_length) == F_equal_to) {
+ // do nothing
+ }
+ else if (fl_string_dynamic_compare_string(controller_string_start, action->parameters.array[0], controller_string_start_length) == F_equal_to) {
+ // do nothing
+ }
+ else if (fl_string_dynamic_compare_string(controller_string_stop, action->parameters.array[0], controller_string_stop_length) == F_equal_to) {
+ // do nothing
+ }
+ else {
+ action->status = F_status_set_error(F_supported_not);
- if (F_status_is_error_not(status_action)) {
- status_action = action->status;
- }
+ if (F_status_is_error_not(status_action)) {
+ status_action = action->status;
+ }
- if (data.error.verbosity != f_console_verbosity_quiet) {
- fprintf(data.error.to.stream, "%c", f_string_eol[0]);
- fprintf(data.error.to.stream, "%s%sThe entry item action must have one of '", 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, controller_string_kill, data.error.notable.after->string);
- fprintf(data.error.to.stream, "%s', '", data.error.context.before->string);
- fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, controller_string_start, data.error.notable.after->string);
- fprintf(data.error.to.stream, "%s', or '", data.error.context.before->string);
- fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, controller_string_stop, data.error.notable.after->string);
- fprintf(data.error.to.stream, "%s' but instead has '", data.error.context.before->string);
- fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, action->parameters.array[0].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]);
- }
- }
+ if (data.error.verbosity != f_console_verbosity_quiet) {
+ fprintf(data.error.to.stream, "%c", f_string_eol[0]);
+ fprintf(data.error.to.stream, "%s%sThe entry item action must have one of '", 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, controller_string_kill, data.error.notable.after->string);
+ fprintf(data.error.to.stream, "%s', '", data.error.context.before->string);
+ fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, controller_string_start, data.error.notable.after->string);
+ fprintf(data.error.to.stream, "%s', or '", data.error.context.before->string);
+ fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, controller_string_stop, data.error.notable.after->string);
+ fprintf(data.error.to.stream, "%s' but instead has '", data.error.context.before->string);
+ fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, action->parameters.array[0].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]);
}
}
if (action->status == F_none) {
const f_string_range_t range = f_macro_string_range_t_initialize(action->parameters.array[1].used);
- f_number_unsigned_t number = 0;
- status = fl_conversion_string_to_number_unsigned(action->parameters.array[1].string, &number, range);
+ status = fl_conversion_string_to_number_unsigned(action->parameters.array[1].string, &action->number, range);
if (F_status_is_error(status) || status == F_data_not) {
+ action->number = 0;
+
if (status == F_data_not) {
action->status = F_status_set_error(F_number);
}
fll_error_print(data.error, F_status_set_fine(status), "controller_entry_items_increase_by", F_true);
}
else {
+
// 0x1 = main found, 0x2 = found existing.
uint8_t code = 0;
break;
}
+ cache->line_item++;
+
for (j = (code & 0x1) ? 1 : 0; j < entry->items.used; ++j) {
if (fl_string_dynamic_compare(entry->items.array[j].name, cache->name_item) == F_equal_to) {
- if (data.warning.verbosity != f_console_verbosity_quiet) {
+ if (data.warning.verbosity == f_console_verbosity_debug) {
fprintf(data.warning.to.stream, "%c", f_string_eol[0]);
fprintf(data.warning.to.stream, "%s%sIgnoring duplicate entry item '", data.warning.context.before->string, data.warning.prefix ? data.warning.prefix : "");
fprintf(data.warning.to.stream, "%s%s%s%s", data.warning.context.after->string, data.warning.notable.before->string, cache->name_file.string, data.warning.notable.after->string);
* The range in the list buffer representing the content.
* @param cache
* A structure for containing and caching relevant data.
- * @param list
- * The processed list.
+ * @param actions
+ * The processed actions.
*
* @return
* F_none on success.
* @see fll_fss_extended_read()
*/
#ifndef _di_controller_entry_actions_read_
- extern f_return_status controller_entry_actions_read(const controller_data_t data, const controller_setting_t setting, const f_string_range_t content_range, controller_cache_t *cache, controller_entry_actions_t *items) f_gcc_attribute_visibility_internal;
+ extern f_return_status controller_entry_actions_read(const controller_data_t data, const controller_setting_t setting, const f_string_range_t content_range, controller_cache_t *cache, controller_entry_actions_t *actions) f_gcc_attribute_visibility_internal;
#endif // _di_controller_entry_actions_read_
/**
fl_fss
fl_iki
fl_string
+fl_type
fll_error
fll_fss
fll_path
build_indexer ar
build_language c
build_libraries -lc
-build_libraries-individual -lfll_error -lfll_fss -lfll_path -lfll_program -lfll_status -lfl_color -lfl_console -lfl_conversion -lfl_fss -lfl_iki -lfl_status -lfl_string -lf_console -lf_conversion -lf_directory -lf_file -lf_fss -lf_iki -lf_memory -lf_path -lf_pipe -lf_print -lf_utf
+build_libraries-individual -lfll_error -lfll_fss -lfll_path -lfll_program -lfll_status -lfl_color -lfl_console -lfl_conversion -lfl_fss -lfl_iki -lfl_status -lfl_string -lfl_type -lf_console -lf_conversion -lf_directory -lf_file -lf_fss -lf_iki -lf_memory -lf_path -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-control.c private-controller.c private-entry.c private-rule.c
rule service logger wait
rule service dbus asynchronous
+ ready
+
net:
rule net all asynchronous