From: Kevin Day Date: Sun, 17 Jan 2016 20:29:41 +0000 (-0600) Subject: Progress: add the current (draft) of the custom init program X-Git-Tag: 0.5.0~534 X-Git-Url: https://git.kevux.org/?a=commitdiff_plain;h=f252ad7f34050945bddb20fee70312a2373c0d95;p=fll Progress: add the current (draft) of the custom init program This is effectively a draft of my ideas and is entirely experimental and incomplete. I am adding it now because of numerous hard-drive failures and data loss of late. --- diff --git a/level_3/init/c/init.c b/level_3/init/c/init.c new file mode 100644 index 0000000..81496e9 --- /dev/null +++ b/level_3/init/c/init.c @@ -0,0 +1,280 @@ +/* FLL - Level 3 + * Project: Firewall + * Version: 0.5.0 + * Licenses: lgplv2.1 + * Programmers: Kevin Day + */ +#include +#include "private-init.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// version printed may be used by scripts, so this will only print the version number and a newline, no extra information or colors +#ifndef _di_init_print_version_ + f_return_status init_print_version(f_const init_argument argument) { + printf("%s\n", init_version); + + return f_none; + } +#endif // _di_init_print_version_ + +#ifndef _di_init_print_help_ + f_return_status init_print_help(f_const init_argument argument) { + printf("\n"); + fl_print_color(f_standard_output, argument.context.title, argument.context.reset, " %s", init_name_long); + + printf("\n"); + fl_print_color(f_standard_output, argument.context.notable, argument.context.reset, " Version %s", init_version); + + printf("\n\n"); + fl_print_color(f_standard_output, argument.context.important, argument.context.reset, " Available Options: "); + + printf("\n %s", f_console_symbol_short_enable); + fl_print_color(f_standard_output, argument.context.standout, argument.context.reset, f_console_standard_short_help); + + printf(", %s", f_console_symbol_long_enable); + fl_print_color(f_standard_output, argument.context.standout, argument.context.reset, f_console_standard_long_help); + printf(" Print this help message"); + + printf("\n %s", f_console_symbol_short_disable); + fl_print_color(f_standard_output, argument.context.standout, argument.context.reset, f_console_standard_short_light); + + printf(", %s", f_console_symbol_long_disable); + fl_print_color(f_standard_output, argument.context.standout, argument.context.reset, f_console_standard_long_light); + printf(" Output using colors that show up better on light backgrounds"); + + printf("\n %s", f_console_symbol_short_disable); + fl_print_color(f_standard_output, argument.context.standout, argument.context.reset, f_console_standard_short_no_color); + + printf(", %s", f_console_symbol_long_disable); + fl_print_color(f_standard_output, argument.context.standout, argument.context.reset, f_console_standard_long_no_color); + printf(" Do not output in color"); + + printf("\n %s", f_console_symbol_short_disable); + fl_print_color(f_standard_output, argument.context.standout, argument.context.reset, f_console_standard_short_version); + + printf(", %s", f_console_symbol_long_disable); + fl_print_color(f_standard_output, argument.context.standout, argument.context.reset, f_console_standard_long_version); + printf(" Print only the version number"); + + printf("\n %s", f_console_symbol_short_disable); + fl_print_color(f_standard_output, argument.context.standout, argument.context.reset, f_console_standard_short_debug); + + printf(", %s", f_console_symbol_long_disable); + fl_print_color(f_standard_output, argument.context.standout, argument.context.reset, f_console_standard_long_debug); + printf(" Enable debugging"); + + printf("\n %s", init_parameter_no_prepare_short_name); + fl_print_color(f_standard_output, argument.context.standout, argument.context.reset, f_console_standard_short_debug); + + printf(", %s", init_parameter_no_prepare_long_name); + fl_print_color(f_standard_output, argument.context.standout, argument.context.reset, f_console_standard_long_debug); + printf(" Do not attempt to process kernel command line or perform any boot-time specific preparations."); + + printf("\n %s", init_parameter_runlevel_short_name); + fl_print_color(f_standard_output, argument.context.standout, argument.context.reset, f_console_standard_short_debug); + + printf(", %s", init_parameter_runlevel_long_name); + fl_print_color(f_standard_output, argument.context.standout, argument.context.reset, f_console_standard_long_debug); + printf(" Specify a custom run level, ignoring the kernel command line runlevel argument."); + + printf("\n\n"); + fl_print_color(f_standard_output, argument.context.important, argument.context.reset, " Usage: "); + + printf("\n "); + fl_print_color(f_standard_output, argument.context.standout, argument.context.reset, init_name); + + printf(" "); + fl_print_color(f_standard_output, argument.context.notable, argument.context.reset, "["); + + printf(" options "); + fl_print_color(f_standard_output, argument.context.notable, argument.context.reset, "]"); + + printf("\n\n"); + + return f_none; + } +#endif // _di_init_print_help_ + +#ifndef _di_init_main_ + f_return_status init_main(f_const f_s_int argc, f_const f_string argv[], init_argument *argument) { + f_status status = f_none; + f_status status2 = f_none; + f_autochar run_level[init_kernel_runlevel_buffer]; + + memset(run_level, 0, sizeof(f_autochar) * init_kernel_runlevel_buffer); + + f_u_short do_socket_file = f_true; + f_u_short do_socket_port = f_false; + + status = fl_process_parameters(argc, argv, argument->parameters, init_total_parameters, &argument->remaining); + + // load colors when not told to show no colors + if (argument->parameters[init_parameter_no_color].result == f_console_result_none) { + fl_new_color_context(status2, argument->context); + + if (status2 == f_none) { + fll_colors_load_context(&argument->context, argument->parameters[init_parameter_light].result == f_console_result_found); + } else { + fprintf(f_standard_error, "Critical Error: unable to allocate memory\n"); + init_delete_argument((*argument)); + return status2; + } + } + + if (f_error_is_error(status)) { + status = f_error_set_fine(status); + + if (status == f_no_data) { + fl_print_color_line(f_standard_error, argument->context.error, argument->context.reset, "ERROR: One of the parameters you passed requires an additional parameter that you did not pass."); + // TODO: there is a way to identify which parameter is incorrect + // to do this, one must look for any "has_additional" and then see if the "additional" location is set to 0 + // nothing can be 0 as that represents the program name, unless argv[] is improperly created + } else if (f_macro_test_for_allocation_errors(status)) { + fl_print_color_line(f_standard_error, argument->context.error, argument->context.reset, "CRITICAL ERROR: unable to allocate memory"); + } else if (f_error_set_fine(status) == f_invalid_parameter) { + fl_print_color_line(f_standard_error, argument->context.error, argument->context.reset, "INTERNAL ERROR: Invalid parameter when calling fl_process_parameters()"); + } else { + fl_print_color_line(f_standard_error, argument->context.error, argument->context.reset, "INTERNAL ERROR: An unhandled error (%u) has occured while calling fl_process_parameters()", f_error_set_error(status)); + } + + init_delete_argument((*argument)); + return f_error_set_error(status); + } + + + if (argument->parameters[init_parameter_runlevel].result == f_console_result_found) { + const f_u_int parameter_length = strlen(argv[argument->parameters[init_parameter_runlevel].additional.array[0]]); + + // if the run_level value is greater than the static buffer size, ignore the entire string rather than process a cut off value. + if (parameter_length > 0 && parameter_length < init_kernel_runlevel_buffer) { + strncpy(&run_level, argv[argument->parameters[init_parameter_runlevel].additional.array[0]], parameter_length); + } + } + + + // before doing anything make sure that the rootfs has been properly setup. + if (argument->parameters[init_parameter_no_prepare].result == f_console_result_none) { + init_prepare_system(&run_level); + } + + // make sure appropriate required directories exist. + init_prepare_init(); + + + // attempt to load the main rule before forking and starting child processes. + { + // @todo: should this pre-scan the main rule file to see if there is a custom chroot command, if so, change behavior to process rules and then chroot into a new system (cleaning up resources as much as possible before doing chroot) (don't fork child processes?)? + // 1) check the last object for a chroot command in reverse order. + // - if the chroot fails then additional rules may apply. the chroot command does not have to be the last command. + // 2) if chroot is not specified, then continue onto code below. + // 3) if chroot is specified, then execute rules directly and exit this process on success (via chroot command). + // 4) if chroot is specified, but chroot fails, then continue processing rules and then exit this process (dropping to bash shell). + // effectively use my initrd_init and pre_init code here. + + // @todo: init_process_main_rule(); + } + + + // commented out code is an alternative method to avoid using malloc for stacks passed to clone. + //f_char stack_services[init_stack_size_small_services]; + //f_char stack_control_file[init_stack_size_small_control_file]; + init_stack_memory stack_memory = init_stack_memory_initialize; + + status = init_initialize_stack_memory(&stack_memory); + if (f_error_is_error(status)) { + init_delete_argument((*argument)); + init_delete_stack_memory(&stack_memory); + return status; + } + + { + f_pid_t pid_services = clone(init_handler_child_services, stack_memory.services + init_stack_size_services, init_flags_clone, stack_memory.services); + + if (pid_services < 0) { + fl_print_color_line(f_standard_error, data->context.error, data->context.reset, "ERROR: Failed to clone services process (errno = %i).", errno); + } + + f_pid_t pid_control_file = clone(init_handler_child_control_file, stack_memory.control_file + init_stack_size_control_file, init_flags_clone, stack_memory.control_file); + + if (pid_control_file < 0) { + fl_print_color_line(f_standard_error, data->context.error, data->context.reset, "ERROR: Failed to clone control via file process (errno = %i).", errno); + } + + + // block signals + f_sigset_t signal_mask; + f_siginfo_t signal_information; + + memset(&signal_mask, 0, sizeof(f_sigset_t)); + memset(&signal_information_parent, 0, sizeof(f_siginfo_t)); + + // block all signals. + sigfillset(&signal_mask); + sigprocmask(SIG_BLOCK, &signal_mask, 0); + + + // sit and wait for signals. + while (1) { + signal_result = sigwaitinfo(&signal_mask, &signal_information); + + if (signal_result < 0) { + if (errno == EAGAIN) { + // do nothing. + continue; + } + else if (errno != EINTR) { + fl_print_color_line(f_standard_error, data->context.error, data->context.reset, "ERROR: sigwaitinfo() failed (errno = %i).", errno); + + signal_problem_count++; + if (signal_problem_count > PROBLEM_COUNT_MAX_SIGNAL_SIZE) { + fl_print_color_line(f_standard_error, data->context.error, data->context.reset, "ERROR: max signal problem count has been reached, sleeping for a period of time.", errno); + sleep(init_panic_signal_sleep_seconds); + signal_problem_count = 0; + } + + continue; + } + } + + signal_problem_count = 0; + + if (signal_information_parent.si_signo == SIGHUP) { + // @todo: close all child process connections? try using small initial memory instead? + } + else if (signal_information_parent.si_signo == SIGINT { + // check the status of processes to see if they are still running, if not, then restart them. + } + else if (signal_information_parent.si_signo == SIGQUIT || signal_information_parent.si_signo == SIGTERM) { + // @todo: block these or attempt to respawn init process? try using small initial memory instead? + break; + } + else if (signal_information_parent.si_signo == SIGSEGV || signal_information_parent.si_signo == SIGBUS || signal_information_parent.si_signo == SIGILL || signal_information_parent.si_signo == SIGFPE) { + // @todo: block these or attempt to respawn init process? + } + else if (signal_information_parent.si_signo == SIGPWR) { + // @todo: shutdown process? + } + else if (signal_information_parent.si_signo == SIGABRT || signal_information_parent.si_signo == SIGIOT || signal_information_parent.si_signo == SIGXCPU) { + // do nothing. + } + else if (signal_information_parent.si_signo == SIGCHLD) { + // @todo: restart child processes? try using large initial memory instead? + } + + memset(&signal_information, 0, sizeof(siginfo_t)); + continue; + } + } + + init_delete_argument((*argument)); + init_delete_stack_memory(&stack_memory); + return status; + } +#endif // _di_init_main_ + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/level_3/init/c/init.h b/level_3/init/c/init.h new file mode 100644 index 0000000..ecbcb65 --- /dev/null +++ b/level_3/init/c/init.h @@ -0,0 +1,230 @@ +/* FLL - Level 3 + * Project: Firewall + * Version: 0.1.0 + * Licenses: lgplv2.1 + * Programmers: Kevin Day + * Documentation: + * + * This is the Kevux Operating System Init program. + * This program utilizes the Featureless Linux Library. + * + * This init program is designed to run with certain paths and if they do not exist, it will attemp to create them. + * This expects the kevux path permissions structure and naming, so a valid /etc/group file should exist. + * + * This is designed to have different threads handling the following actions: + * Thread Group 1 (Signals and Output) + * - (parent process) handles/blocks all signals for init program and handles children accordingly. + * + * Thread Group 2 (Services) + * - Handles starting and stopping of individual services. + * - New thread created for each user. + * + * Thread Group 3 (Reporting) + * - Handles output to the terminal or any other supported devices. + * - No child threads. + * + * Thread Group 4 (Commands: Time) + * - Handles time based processing. + * - This is a cron system. + * - 2 Child threads: + * 1) Cron-style behavior. + * 2) Anacron-style behavior. + * + * Thread Group 5 (Commands: Socket File) + * - If socket file is enabled, then this thread is created to listen on the socket file and process received commands. + * - No child threads. + * + * Thread Group 6 (Commands: Socket Port) + * - If socket port is enabled, then this thread is created to listen on the socket port and process received commands. + * - No child threads. + * + * + * Time based (aka: cron) functionality is being implemented here because cron jobs often need to run as specific users. + * The init program is already root and can already switch users, so give it control. + * This avoids having yet another program that must have root privileges. + * + * It would be nice to be able to start the init program as a non-root user who has elevated privileges similar to root. + * - That is, have ability to su to any user (except root). + * - While this will still be a security concern, not running as root will significantly reduce the potential problems. + * - This design would not allow for root-specific cron jobs. + */ +#ifndef _init_h + +// libc includes +#include +#include +#include +#include +#include // defines PATH_MAX +#include +#include +#include +#include +#include + +// private includes +#include "private-init.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _di_init_version_ + #define init_major_version "0" + #define init_minor_version "5" + #define init_micro_version "0" + #define init_version init_major_version "." init_minor_version "." init_micro_version +#endif // _di_init_version_ + +#ifndef _di_init_name_ + #define init_name "init" + #define init_name_long "Kevux Init Program" +#endif // _di_init_name_ + +#ifndef _di_init_default_allocation_step_ + #define init_default_allocation_step f_memory_default_allocation_step +#endif // _di_init_default_allocation_step_ + +#ifndef _di_init_paths_ + // @todo: this will eventually be written to use the f_paths project. + #define init_paths_rootfs "/" + #define init_paths_devices "/dev/" + #define init_paths_devices_pts "/dev/pts/" + #define init_paths_system "/sys/" + #define init_paths_processes "/proc/" + #define init_paths_temporary "/tmp/" + #define init_paths_mount "/mnt/" + #define init_paths_run "/run/" + #define init_paths_init_run "/run/init/" + #define init_paths_init_settings "/etc/init/" + #define init_paths_init_socket "/run/init/socket/" + #define init_paths_init_process "/run/init/process/" + #define init_paths_init_log "/log/init/" + #define init_paths_log "/log/" +#endif // _di_init_paths_ + +#ifndef _di_init_extension_ + #define init_extension_settings "settings" + #define init_extension_rule "rule" + #define init_extension_socket "socket" + #define init_extension_process "pid" +#endif // _di_init_extension_ + +#ifndef _di_init_settings_ + #define init_rule_core "main" + #define init_rule_core_file init_paths_init_settings init_rule_core "." init_extension_rule + + #define init_kernel_command_line init_paths_processes "cmdline" + #define init_kernel_runlevel "\\ + +int main(f_const f_s_int argc, f_const f_string argv[]) { + init_argument data = init_argument_initialize; + + return init_main(argc, argv, &data); +} diff --git a/level_3/init/c/private-init.c b/level_3/init/c/private-init.c new file mode 100644 index 0000000..648275d --- /dev/null +++ b/level_3/init/c/private-init.c @@ -0,0 +1,587 @@ +/** + * Private source file for firewall.c. + */ +#include +#include "private-init.h" + +#ifndef _di_init_rule_buffer_ + f_return_status init_rule_buffer(f_const f_string filename, f_dynamic_string *buffer, f_fss_objects *objects, f_fss_contents *contents) { + f_file file = f_file_initialize; + f_status status = f_none; + f_file_position file_position = f_file_position_initialize; + + status = f_file_open(&file, filename); + + if (f_error_is_error(status)) { + status = f_error_set_fine(status); + + if (optional) { + if (status == f_invalid_parameter) { + fl_print_color_line(f_standard_error, data->context.error, data->context.reset, "INTERNAL ERROR: Invalid parameter when calling f_file_open()."); + } else if (status != f_file_not_found && status != f_file_open_error && status != f_file_descriptor_error) { + fl_print_color_line(f_standard_error, data->context.error, data->context.reset, "INTERNAL ERROR: An unhandled error (%u) has occured while calling f_file_open().", f_error_set_error(status)); + } + } else { + if (status == f_invalid_parameter) { + fl_print_color_line(f_standard_error, data->context.error, data->context.reset, "INTERNAL ERROR: Invalid parameter when calling f_file_open()."); + } else if (status == f_file_not_found) { + fl_print_color_line(f_standard_error, data->context.error, data->context.reset, "ERROR: Unable to find the file '%s'.", filename); + } else if (status == f_file_open_error) { + fl_print_color_line(f_standard_error, data->context.error, data->context.reset, "ERROR: Unable to open the file '%s'.", filename); + } else if (status == f_file_descriptor_error) { + fl_print_color_line(f_standard_error, data->context.error, data->context.reset, "ERROR: File descriptor error while trying to open the file '%s'.", filename); + } else { + fl_print_color_line(f_standard_error, data->context.error, data->context.reset, "INTERNAL ERROR: An unhandled error (%u) has occured while calling f_file_open().", f_error_set_error(status)); + } + } + + return f_error_set_error(status); + } + + f_macro_file_reset_position(file_position, file) + + fflush(stdout); + status = fl_file_read(file, file_position, buffer); + + f_file_close(&file); + + if (f_error_is_error(status)) { + status = f_error_set_fine(status); + + if (status == f_invalid_parameter) { + fl_print_color_line(f_standard_error, data->context.error, data->context.reset, "INTERNAL ERROR: Invalid parameter when calling fl_file_read()."); + } else if (status == f_overflow) { + fl_print_color_line(f_standard_error, data->context.error, data->context.reset, "ERROR: Integer overflow while trying to buffer the file '%s'.", filename); + } else if (status == f_file_not_open) { + fl_print_color_line(f_standard_error, data->context.error, data->context.reset, "INTERNAL ERROR: The file '%s' is no longer open.", filename); + } else if (status == f_file_seek_error) { + fl_print_color_line(f_standard_error, data->context.error, data->context.reset, "ERROR: A seek error occurred while accessing the file '%s'.", filename); + } else if (status == f_file_read_error) { + fl_print_color_line(f_standard_error, data->context.error, data->context.reset, "ERROR: A read error occurred while accessing the file '%s'.", filename); + } else if (f_macro_test_for_allocation_errors(status)) { + fl_print_color_line(f_standard_error, data->context.error, data->context.reset, "CRITICAL ERROR: unable to allocate memory."); + } else { + fl_print_color_line(f_standard_error, data->context.error, data->context.reset, "INTERNAL ERROR: An unhandled error (%u) has occured while calling fl_file_read().", f_error_set_error(status)); + } + + return f_error_set_error(status); + } else { + f_string_location input = f_string_location_initialize; + + input.stop = buffer->used - 1; + + status = fll_fss_basic_list_read(buffer, &input, objects, contents); + } + + if (f_error_is_error(status)) { + status = f_error_set_fine(status); + + if (status == f_invalid_parameter) { + fl_print_color_line(f_standard_error, data->context.error, data->context.reset, "INTERNAL ERROR: Invalid parameter when calling fll_fss_basic_list_read() for the file '%s'.", filename); + } else if (status == f_no_data_on_eos || status == f_no_data || status == f_no_data_on_stop) { + fl_print_color_line(f_standard_error, data->context.error, data->context.reset, "ERROR: No relevant data was found within the file '%s'.", filename); + } else if (f_macro_test_for_allocation_errors(status)) { + fl_print_color_line(f_standard_error, data->context.error, data->context.reset, "CRITICAL ERROR: unable to allocate memory."); + } else { + fl_print_color_line(f_standard_error, data->context.error, data->context.reset, "INTERNAL ERROR: An unhandled error (%u) has occured while calling fll_fss_basic_list_read() for the file '%s'.", f_error_set_error(status), filename); + } + + return f_error_set_error(status); + } + + return status; + } +#endif // _di_init_rule_buffer_ + +#ifndef _di_init_rules_process_main_ + f_return_status init_rules_process_main(f_const init_data) { + f_status status = f_none; + f_status status2 = f_none; + + // @todo: resume replacing code below. + status = fll_fss_extended_read(&buffer, input, &local->rule_objects, &local->rule_contents); + + if (f_error_is_not_error(status)) { + status = firewall_perform_commands(*local, *data); + + if (f_error_is_error(status)) { + status = f_error_set_fine(status); + + if (f_macro_test_for_allocation_errors(status)) { + fl_print_color_line(f_standard_error, data->context.error, data->context.reset, "CRITICAL ERROR: unable to allocate memory."); + } else if (status == f_failure) { + // the error message has already been displayed. + } else { + fl_print_color_line(f_standard_error, data->context.error, data->context.reset, "INTERNAL ERROR: An unhandled error (%u) has occured while calling firewall_perform_commands().", f_error_set_error(status)); + } + + f_delete_fss_objects(status2, local->rule_objects); + f_delete_fss_contents(status2, local->rule_contents); + return f_error_set_error(status); + } + } + + f_delete_fss_objects(status2, local->rule_objects); + f_delete_fss_contents(status2, local->rule_contents); + return status; + } +#endif // _init_rules_process_main_ + +#ifndef _di_init_handler_child_services_ + f_return_status init_handler_child_services(f_void_p argument) { + init_local_data *local_data = (init_local_data *) argument; + + // load and process rules. + + return f_none; + } +#endif // _di_init_handler_child_services_ + +#ifndef _di_init_handler_child_control_file_ + f_return_status init_handler_child_control_file(f_void_p argument) { + init_local_data *local_data = (init_local_data *) argument; + + return f_none; + } +#endif // _di_init_handler_child_control_file_ + +#ifndef _di_init_initialize_stack_memory_ + f_return_status init_initialize_stack_memory(init_stack_memory *stack_memory) { + stack_memory->services = mmap(0, init_stack_size_small_services, init_stack_protections, init_stack_flags, -1, 0); + stack_memory->control_file = mmap(0, init_stack_size_small_control_file, init_stack_protections, init_stack_flags, -1, 0); + + if (stack_memory->services == (f_void_p) -1) { + return f_failure; + } + + if (stack_memory->control_file == (f_void_p) -1) { + return f_failure; + } + + return f_none; + } +#endif // _di_init_initialize_stack_memory_ + +#ifndef _di_init_delete_stack_memory_ + f_return_status init_delete_stack_memory(init_stack_memory *stack_memory) { + f_status status = f_none; + + if (stack_memory->services > 0) { + if (munmap(stack_memory->services, 0) >= 0) { + stack_memory->services = 0; + } + else { + status = f_failure; + } + } + + if (stack_memory->control_file > 0) { + if (munmap(stack_memory->control_file, 0) >= 0) { + stack_memory->control_file = 0; + } + else { + status = f_failure; + } + } + + return status; + } +#endif // _di_init_delete_stack_memory_ + +#ifndef _di_init_prepare_system_ + f_return_status init_prepare_system(f_autochar *run_level) { + f_status status = f_none; + f_stat stat; + + memset(&stat, 0, sizeof(f_stat)); + + + // create the required directories if they do not already exist and then perform appropriate mount. + status = f_file_stat(init_paths_devices, &stat); + + if (status == f_file_not_found || status == f_error_set_error(f_invalid_directory)) { + system(init_program_mkdir " -p " init_paths_devices); + memset(&stat, 0, sizeof(f_stat)); + } + + system(init_program_mount " " init_paths_devices); + + status = f_file_stat(init_paths_system, &stat); + + if (status == f_file_not_found || status == f_error_set_error(f_invalid_directory)) { + system(init_program_mkdir " -p " init_paths_system); + memset(&stat, 0, sizeof(f_stat)); + } + + system(init_program_mount " -p " init_paths_system); + + status = f_file_stat(init_paths_devices_pts, &stat); + + if (status == f_file_not_found || status == f_error_set_error(f_invalid_directory)) { + system(init_program_mkdir " -p " init_paths_devices_pts); + memset(&stat, 0, sizeof(f_stat)); + } + + system(init_program_mount " -t " init_paths_devices_pts); + + status = f_file_stat(init_paths_log, &stat); + + if (status == f_file_not_found || status == f_error_set_error(f_invalid_directory)) { + system(init_program_mkdir " -p " init_paths_log); + memset(&stat, 0, sizeof(f_stat)); + } + + system(init_program_mount " " init_paths_log); + + status = f_file_stat(var_run_path, &stat); + + if (status == f_file_not_found || status == f_error_set_error(f_invalid_directory)) { + system(init_program_mkdir " -p " var_run_path); + memset(&stat, 0, sizeof(f_stat)); + } + + system(init_program_mount " " var_run_path); + + status = f_file_stat(mnt_path, &stat); + + if (status == f_file_not_found || status == f_error_set_error(f_invalid_directory)) { + system(init_program_mkdir " -p " mnt_path); + memset(&stat, 0, sizeof(f_stat)); + } + + system(init_program_mount " " mnt_path); + + status = f_file_stat(tmp_path, &stat); + + if (status == f_file_not_found || status == f_error_set_error(f_invalid_directory)) { + system(init_program_mkdir " -p " tmp_path); + memset(&stat, 0, sizeof(f_stat)); + } + + system(init_program_mount " " tmp_path); + + status = f_file_stat(init_paths_processes, &stat); + + if (status == f_file_not_found || status == f_error_set_error(f_invalid_directory)) { + system(init_program_mkdir " -p " init_paths_processes); + memset(&stat, 0, sizeof(f_stat)); + } + + // Look for the kernel command line to determine whether or not the proc is mounted. + // doing this also allows for unusual circumstances where the kernel command line is not mounted but a custom kernel command line is statically provided. + status = f_file_stat(init_kernel_command_line, &stat); + + if (status == f_file_not_found) { + system(init_program_mount " " init_paths_processes); + } + + + // create the required devices + system(init_program_mknod " -m 0660 " init_path_device_null " c 1 3"); + system(init_program_mknod " -m 0660 " init_path_device_zero " c 1 5"); + system(init_program_mknod " -m 0660 " init_path_device_console " c 5 1"); + system(init_program_mknod " -m 0440 " init_path_device_random " c 1 8"); + system(init_program_mknod " -m 0440 " init_path_device_urandom " c 1 9"); + system(init_program_chgrp " " init_group_process_null " " init_path_device_null); + system(init_program_chgrp " " init_group_process_zero " " init_path_device_zero); + system(init_program_chgrp " " init_group_process_console " " init_path_device_console); + system(init_program_chgrp " " init_group_process_random " " init_path_device_random); + system(init_program_chgrp " " init_group_process_urandom " " init_path_device_urandom); + + + // attempt to load kernel command line, but do not stop on failure. + if (run_level > 0 && run_level[0] != 0) { + f_file_p kernel_command_line_file = 0; + f_string kernel_command_line_string = f_string_initialize; + size_t kernel_command_line_length = 0; + + kernel_command_line_file = fopen(init_kernel_command_line, "ro"); + + if (kernel_command_line_file > 0) { + getline(&kernel_command_line_string, &kernel_command_line_length, kernel_command_line_file); + + if (kernel_command_line_file) { + fclose(kernel_command_line_file); + } + } + + if (kernel_command_line_length > 0) { + regex_t expression; + regmatch_t match; + f_u_int reg_result = 0; + f_u_int string_length = 0; + + reg_result = do_regex_match(&expression, &match, kernel_command_line_string, init_kernel_runlevel); + + if (reg_result == REG_OK) { + string_length = match.rm_eo - match.rm_so - init_kernel_runlevel_ignore; + + // if the run_level value is greater than the static buffer size, ignore the entire string rather than process a cut off value. + if (string_length > 0 && string_length < init_kernel_runlevel_buffer) { + strncpy(run_level, kernel_command_line_string + match.rm_so + init_kernel_runlevel_ignore, string_length); + } + } + } + + if (kernel_command_line_string) { + f_status status_free = f_none; + f_delete_string(status_free, kernel_command_line_string, string_length); + } + } + + return f_none; + } +#endif // _di_init_prepare_system_ + +#ifndef _di_init_prepare_system_ + f_return_status init_prepare_system() { + f_status status = f_none; + f_stat stat; + + memset(&stat, 0, sizeof(f_stat)); + + // @fixme: this is implemented using user space tools. + // such a design allows for quick implementation but I consider this bad practice. + // work towards replacing this with custom functions. + + // create the required directories if they do not already exist and then perform appropriate mount. + status = f_file_stat(init_paths_devices, &stat); + if (status == f_file_not_found || status == f_error_set_error(f_invalid_directory)) { + system(init_program_mkdir " -p " init_paths_devices); + memset(&stat, 0, sizeof(f_stat)); + } + + system(init_program_mount " " init_paths_devices); + + status = f_file_stat(init_paths_system, &stat); + + if (status == f_file_not_found || status == f_error_set_error(f_invalid_directory)) { + system(init_program_mkdir " -p " init_paths_system); + memset(&stat, 0, sizeof(f_stat)); + } + + system(init_program_mount " -p " init_paths_system); + + status = f_file_stat(init_paths_devices_pts, &stat); + + if (status == f_file_not_found || status == f_error_set_error(f_invalid_directory)) { + system(init_program_mkdir " -p " init_paths_devices_pts); + memset(&stat, 0, sizeof(f_stat)); + } + + system(init_program_mount " -t " init_paths_devices_pts); + + status = f_file_stat(init_paths_log, &stat); + + if (status == f_file_not_found || status == f_error_set_error(f_invalid_directory)) { + system(init_program_mkdir " -p " init_paths_log); + memset(&stat, 0, sizeof(f_stat)); + } + + system(init_program_mount " " init_paths_log); + + status = f_file_stat(var_run_path, &stat); + + if (status == f_file_not_found || status == f_error_set_error(f_invalid_directory)) { + system(init_program_mkdir " -p " var_run_path); + memset(&stat, 0, sizeof(f_stat)); + } + + system(init_program_mount " " var_run_path); + + status = f_file_stat(mnt_path, &stat); + + if (status == f_file_not_found || status == f_error_set_error(f_invalid_directory)) { + system(init_program_mkdir " -p " mnt_path); + memset(&stat, 0, sizeof(f_stat)); + } + + system(init_program_mount " " mnt_path); + + status = f_file_stat(tmp_path, &stat); + + if (status == f_file_not_found || status == f_error_set_error(f_invalid_directory)) { + system(init_program_mkdir " -p " tmp_path); + memset(&stat, 0, sizeof(f_stat)); + } + + system(init_program_mount " " tmp_path); + + + // create the required devices + system(init_program_mknod " -m 0660 " init_path_device_null " c 1 3"); + system(init_program_mknod " -m 0660 " init_path_device_zero " c 1 5"); + system(init_program_mknod " -m 0660 " init_path_device_console " c 5 1"); + system(init_program_mknod " -m 0440 " init_path_device_random " c 1 8"); + system(init_program_mknod " -m 0440 " init_path_device_urandom " c 1 9"); + system(init_program_chgrp " " init_group_process_null " " init_path_device_null); + system(init_program_chgrp " " init_group_process_zero " " init_path_device_zero); + system(init_program_chgrp " " init_group_process_console " " init_path_device_console); + system(init_program_chgrp " " init_group_process_random " " init_path_device_random); + system(init_program_chgrp " " init_group_process_urandom " " init_path_device_urandom); + + return f_none; + } +#endif // _di_init_prepare_system_ + +#ifndef _di_init_prepare_init_ + f_return_status init_prepare_init() { + f_stat stat; + + memset(&stat, 0, sizeof(f_stat)); + + // @fixme: this is implemented using user space tools. + // such a design allows for quick implementation but I consider this bad practice. + // work towards replacing this with custom functions. + + status = f_file_stat(init_paths_init_run, &stat); + + if (status == f_file_not_found || status == f_error_set_error(f_invalid_directory)) { + system(init_program_mkdir " -p " init_paths_init_run); + system(init_program_chgrp " " init_group_init_run " " init_paths_init_run); + memset(&stat, 0, sizeof(f_stat)); + } + + status = f_file_stat(init_paths_init_settings, &stat); + + if (status == f_file_not_found || status == f_error_set_error(f_invalid_directory)) { + system(init_program_mkdir " -p " init_paths_init_settings); + system(init_program_chgrp " " init_group_init_settings " " init_paths_init_settings); + memset(&stat, 0, sizeof(f_stat)); + } + + status = f_file_stat(init_paths_init_socket, &stat); + + if (status == f_file_not_found || status == f_error_set_error(f_invalid_directory)) { + system(init_program_mkdir " -p " init_paths_init_socket); + system(init_program_chgrp " " init_group_init_socket " " init_paths_init_socket); + memset(&stat, 0, sizeof(f_stat)); + } + + status = f_file_stat(init_paths_init_process, &stat); + + if (status == f_file_not_found || status == f_error_set_error(f_invalid_directory)) { + system(init_program_mkdir " -p " init_paths_init_process); + system(init_program_chgrp " " init_group_init_process " " init_paths_init_process); + memset(&stat, 0, sizeof(f_stat)); + } + + status = f_file_stat(init_paths_init_log, &stat); + + if (status == f_file_not_found || status == f_error_set_error(f_invalid_directory)) { + system(init_program_mkdir " -p " init_paths_init_log); + system(init_program_chgrp " " init_group_init_log " " init_paths_init_log); + memset(&stat, 0, sizeof(f_stat)); + } + + memset(&stat, 0, sizeof(f_stat)); + + return f_none; + } +#endif // _di_init_prepare_init_ + +#ifndef _di_init_process_main_rule_ + f_return_status init_process_main_rule(const init_argument argument, f_dynamic_string *buffer, init_data *data) { + f_status status = f_none; + f_dynamic_string buffer = f_dynamic_string_initialize; + f_string_location location = f_string_location_initialize; + f_fss_objects objects = f_fss_objects_initialize; + f_fss_contents contents = f_fss_contents_initialize; + f_string_length position = f_string_length_initialize; + + // load the main file into memory. + status = init_rule_buffer(init_rule_core_file, &buffer, &objects, &contents); + + if (f_error_is_error(status)) { + status = f_error_set_fine(status); + + if (status == f_invalid_parameter) { + fl_print_color_line(f_standard_error, argument.context.error, argument.context.reset, "INTERNAL ERROR: Invalid parameter when calling fll_fss_basic_list_read() for the file '%s'.", init_rule_core_file); + } else if (status == f_no_data_on_eos || status == f_no_data || status == f_no_data_on_stop) { + fl_print_color_line(f_standard_error, argument.context.error, argument.context.reset, "ERROR: No relevant data was found within the file '%s'.", init_rule_core_file); + } else if (f_macro_test_for_allocation_errors(status)) { + fl_print_color_line(f_standard_error, argument.context.error, argument.context.reset, "CRITICAL ERROR: unable to allocate memory."); + } else { + fl_print_color_line(f_standard_error, argument.context.error, argument.context.reset, "INTERNAL ERROR: An unhandled error (%u) has occured while calling fll_fss_basic_list_read() for the file '%s'.", f_error_set_error(status), init_rule_core_file); + } + + f_delete_dynamic_string(buffer); + f_delete_fss_objects(objects); + f_delete_fss_contents(contents); + return status; + } + + while (position < objects.used) { + location.start = objects.array[position].start; + location.stop = objects.array[position].stop; + + status = fll_fss_extended_read(&buffer, &location, &objects, &contents); + + position++; + } // while + + // @fixme: resume here, below is just notes and copy&pasted code as examples/reminders. + + /* + status = fll_fss_extended_read(&buffer, &location, &objects, &contents); + + if (f_error_is_error(status_process)) { + if (f_macro_test_for_allocation_errors(status)) { + fl_print_color_line(f_standard_error, argument->context.error, argument->context.reset, "CRITICAL ERROR: unable to allocate memory."); + } + else { + fl_print_color_line(f_standard_error, argument->context.error, argument->context.reset, "INTERNAL ERROR: An unhandled error (%u) has occured while calling init_load_main_rule().", f_error_set_error(status)); + } + + init_delete_argument((*argument)); + init_delete_stack_memory(&stack_memory); + return status_process; + } + */ + + + /* + f_status status = f_none; + f_status status2 = f_none; + + status = fll_fss_extended_read(buffer, location, objects, contents); + + if (f_error_is_not_error(status)) { + // @todo: process objects and contents. + // execute individual processes. + + if (f_error_is_error(status)) { + status = f_error_set_fine(status); + + if (f_macro_test_for_allocation_errors(status)) { + fl_print_color_line(f_standard_error, data->context.error, context.reset, "CRITICAL ERROR: unable to allocate memory."); + } else if (status == f_failure) { + // the error message has already been displayed. + } else { + fl_print_color_line(f_standard_error, data->context.error, data->context.reset, "INTERNAL ERROR: An unhandled error (%u) has occured while calling firewall_perform_commands().", f_error_set_error(status)); + } + + f_delete_fss_objects(status2, (*rule_objects)); + f_delete_fss_contents(status2, (*rule_contents)); + return f_error_set_error(status); + } + } + else { + if (f_macro_test_for_allocation_errors(status)) { + fl_print_color_line(f_standard_error, context.error, context.reset, "CRITICAL ERROR: unable to allocate memory."); + } + else { + fl_print_color_line(f_standard_error, context.error, context.reset, "INTERNAL ERROR: An unhandled error (%u) has occured while calling init_load_main_rule().", f_error_set_error(status)); + } + } + + f_delete_fss_objects(status2, (*rule_objects)); + f_delete_fss_contents(status2, (*rule_contents)); + */ + + f_delete_dynamic_string(buffer); + f_delete_fss_objects(objects); + f_delete_fss_contents(contents); + return status; + } +#endif // _di_init_process_main_rule_ diff --git a/level_3/init/c/private-init.h b/level_3/init/c/private-init.h new file mode 100644 index 0000000..745c8cf --- /dev/null +++ b/level_3/init/c/private-init.h @@ -0,0 +1,364 @@ +/** + * Private include file for init.c. + */ + +#ifndef _PRIVATE_init_h +#define _PRIVATE_init_h + +#ifdef __cplusplus +extern "C" { +#endif + +// define a custom stack size for each handler. +// consider mmap(): +// - http://www.evanjones.ca/software/threading.html +// - http://stackoverflow.com/questions/1083172/how-to-mmap-the-stack-for-the-clone-system-call-on-linux +// - f_void_p stack = mmap(0,initial_stacksize,PROT_WRITE|PROT_READ,MAP_PRIVATE|MAP_GROWSDOWN|MAP_ANONYMOUS,-1,0); +// - http://lwn.net/Articles/294001/ +// - http://tiku.io/questions/659065/how-to-mmap-the-stack-for-the-clone-system-call-on-linux +#define init_stack_size_small_services 6144 +#define init_stack_size_small_control_file 4096 +#define init_stack_size_large_services 12288 +#define init_stack_size_large_control_file 8192 + +#define init_stack_protections PROT_WRITE | PROT_READ +#define init_stack_flags MAP_PRIVATE | MAP_GROWSDOWN | MAP_ANONYMOUS + +#define init_clone_flags CLONE_FILES | CLONE_FS | CLONE_IO | CLONE_VM | CLONE_SIGHAND | CLONE_THREAD + +#define init_panic_signal_sleep_seconds 9 + +#ifndef _di_init_rule_ + // rule [directory] [filename (no-extension)] [require] [last] [asynchronous] = execute a rule located in [directory][filename].rule. + typedef struct { + f_dynamic_string name; + f_dynamic_string directory; + f_dynamic_string file; + f_bool require; + f_bool asynchronous; + } init_rule; + + #define init_rule_initialize \ + { \ + f_dynamic_string_initialize, + f_dynamic_string_initialize, + f_dynamic_string_initialize, + f_bool_initialize, + f_bool_initialize, + } + + #define delete_init_rule(status, rule) \ + f_delete_dynamic_string(status, rule.name); \ + if (status == f_none) { \ + f_delete_dynamic_string(status, rule.directory); \ + } \ + if (status == f_none) { \ + f_delete_dynamic_string(status, rule.file); \ + } \ + if (status == f_none) { \ + rule.require = 0; \ + rule.asynchronous = 0; \ + } + + #define destroy_init_rule(status, rule) \ + f_destroy_dynamic_string(status, rule.name); \ + if (status == f_none) { \ + f_destroy_dynamic_string(status, rule.directory); \ + } \ + if (status == f_none) { \ + f_destroy_dynamic_string(status, rule.file); \ + } \ + if (status == f_none) { \ + rule.require = 0; \ + rule.asynchronous = 0; \ + } +#endif // _di_init_data_ + +// an array of dynamic strings +#ifndef _di_init_rules_ + typedef struct { + init_rule *array; // the array of init_rule + f_string_length size; // total amount of allocated space + f_string_length used; // total number of allocated spaces used + } init_rules; + + #define init_rules_initialize { 0, 0, 0 } + + #define f_delete_init_rules(status, rules) \ + status = f_none; \ + while (rules.size > 0) { \ + --rules.size; \ + delete_init_rule(status, rules.array[rules.size]); \ + if (status != f_none) break; \ + } \ + if (status == f_none) status = f_delete((f_void_p *) & rules.array, sizeof(init_rule), rules.size); \ + if (status == f_none) rules.used = 0; + + #define f_destroy_init_rules(status, rules) \ + status = f_none; \ + while (rules.size > 0) { \ + --rules.size; \ + destroy_init_rule(status, rules.array[rules.size]); \ + if (status != f_none) break; \ + } \ + if (status == f_none) status = f_destroy((f_void_p *) & rules.array, sizeof(init_rule), rules.size); \ + if (status == f_none) rules.used = 0; + + #define f_resize_init_rules(status, rules, new_length) \ + status = f_none; \ + if (new_length < rules.size) { \ + f_string_length i = rules.size - new_length; \ + for (; i < rules.size; ++i) { \ + delete_init_rule(status, rules.array[i]); \ + if (status != f_none) break; \ + } \ + } \ + if (status == f_none) status = f_resize((f_void_p *) & rules.array, sizeof(init_rule), rules.size, new_length); \ + if (status == f_none) { \ + if (new_length > rules.size) { \ + f_string_length i = rules.size; \ + for (; i < new_length; ++i) { \ + memset(&rules.array[i], 0, sizeof(f_string)); \ + } \ + } \ + rules.size = new_length; \ + if (rules.used > rules.size) rules.used = new_length; \ + } + + #define f_adjust_init_rules(status, rules, new_length) \ + status = f_none; \ + if (new_length < rules.size) { \ + f_string_length i = rules.size - new_length; \ + for (; i < rules.size; ++i) { \ + destroy_init_rule(status, rules.array[i]); \ + if (status != f_none) break; \ + } \ + } \ + if (status == f_none) status = f_adjust((f_void_p *) & rules.array, sizeof(init_rule), rules.size, new_length); \ + if (status == f_none) { \ + if (new_length > rules.size) { \ + f_string_length i = rules.size; \ + for (; i < new_length; ++i) { \ + memset(&rules.array[i], 0, sizeof(init_rule)); \ + } \ + } \ + rules.size = new_length; \ + if (rules.used > rules.size) rules.used = new_length; \ + } +#endif // _di_init_rules_ + +#ifndef _di_init_category_ + // category [name] = execute rules in the specified list called [name]. + typedef struct { + f_dynamic_string name; + init_rule last; + } init_category; + + #define init_rule_initialize \ + { \ + f_dynamic_string_initialize, + init_rule_initialize, + } + + #define delete_init_category(status, category) \ + f_delete_dynamic_string(status, category.name); \ + if (status == f_none) { \ + delete_init_rule(status, category.last); \ + } + + #define destroy_init_category(status, category) \ + f_destroy_dynamic_string(status, category.name); \ + if (status == f_none) { \ + destroy_init_rule(status, category.last); \ + } +#endif // _di_init_category_ + +// an array of dynamic strings +#ifndef _di_init_categorys_ + typedef struct { + init_category *array; // the array of init_category + + f_string_length size; // total amount of allocated space + f_string_length used; // total number of allocated spaces used + } init_categorys; + + #define init_categorys_initialize { 0, 0, 0 } + + #define f_delete_init_categorys(status, categorys) \ + status = f_none; \ + while (categorys.size > 0) { \ + --categorys.size; \ + delete_init_category(status, categorys.array[categorys.size]); \ + if (status != f_none) break; \ + } \ + if (status == f_none) status = f_delete((f_void_p *) & categorys.array, sizeof(init_category), categorys.size); \ + if (status == f_none) categorys.used = 0; + + #define f_destroy_init_categorys(status, categorys) \ + status = f_none; \ + while (categorys.size > 0) { \ + --categorys.size; \ + destroy_init_category(status, categorys.array[categorys.size]); \ + if (status != f_none) break; \ + } \ + if (status == f_none) status = f_destroy((f_void_p *) & categorys.array, sizeof(init_category), categorys.size); \ + if (status == f_none) categorys.used = 0; + + #define f_resize_init_categorys(status, categorys, new_length) \ + status = f_none; \ + if (new_length < categorys.size) { \ + f_string_length i = categorys.size - new_length; \ + for (; i < categorys.size; ++i) { \ + delete_init_category(status, categorys.array[i]); \ + if (status != f_none) break; \ + } \ + } \ + if (status == f_none) status = f_resize((f_void_p *) & categorys.array, sizeof(init_category), categorys.size, new_length); \ + if (status == f_none) { \ + if (new_length > categorys.size) { \ + f_string_length i = categorys.size; \ + for (; i < new_length; ++i) { \ + memset(&categorys.array[i], 0, sizeof(f_string)); \ + } \ + } \ + categorys.size = new_length; \ + if (categorys.used > categorys.size) categorys.used = new_length; \ + } + + #define f_adjust_init_categorys(status, categorys, new_length) \ + status = f_none; \ + if (new_length < categorys.size) { \ + f_string_length i = categorys.size - new_length; \ + for (; i < categorys.size; ++i) { \ + destroy_init_category(status, categorys.array[i]); \ + if (status != f_none) break; \ + } \ + } \ + if (status == f_none) status = f_adjust((f_void_p *) & categorys.array, sizeof(init_category), categorys.size, new_length); \ + if (status == f_none) { \ + if (new_length > categorys.size) { \ + f_string_length i = categorys.size; \ + for (; i < new_length; ++i) { \ + memset(&categorys.array[i], 0, sizeof(init_category)); \ + } \ + } \ + categorys.size = new_length; \ + if (categorys.used > categorys.size) categorys.used = new_length; \ + } +#endif // _di_init_categorys_ + +#ifndef _di_init_data_ + typedef struct { + f_string socket_file; + f_u_int socket_port; + f_u_int socket_id_target; + f_u_int socket_id_client; + + f_u_short timeout_start; + f_u_short timeout_stop; + f_u_short timeout_kill; + + init_rules main_rules; + init_categorys main_categorys; + init_rule main_failsafe; + } init_data; + + #define init_data_initialize \ + { \ + f_string_initialize, \ + 0, \ + 0, \ + 0, \ + 2, \ + 2, \ + 1, \ + init_rules_initialize, \ + init_categorys_initialize, \ + init_rule_initialize, \ + } +#endif // _di_init_data_ + +typedef struct { + f_void_p services; + f_void_p reporting; + f_void_p time; + f_void_p control_file; + f_void_p control_port; + f_void_p login_file; + f_void_p login_port; + + init_argument argument; + init_data data; +} init_stack_memory; + +#define init_stack_memory_initialize \ + { \ + 0, \ + 0, \ + 0, \ + 0, \ + 0, \ + 0, \ + 0, \ + init_argument_initialize, \ + init_data_initialize, \ + } + +// custom settings to prepare the init process. +// this is not self-contained and depends on user space applications to exist. +#define init_program_umount "umount" +#define init_program_mount "mount" +#define init_program_mkdir "mkdir" +#define init_program_chmod "chmod" +#define init_program_chgrp "chgrp" +#define init_program_mknod "mknod" +#define init_program_ln "ln" + +#define init_path_device_console init_paths_devices "console" +#define init_path_device_zero init_paths_devices "zero" +#define init_path_device_null init_paths_devices "null" +#define init_path_device_random init_paths_devices "random" +#define init_path_device_urandom init_paths_devices "urandom" + +#define init_path_processes_cmdline init_paths_processes "cmdline" +#define init_path_processes_mounts init_paths_processes "mounts" + +#ifndef _di_init_rule_buffer_ + f_return_status init_rule_buffer(f_const f_string filename, f_dynamic_string *buffer, f_fss_objects *objects, f_fss_contents *contents) f_gcc_attribute_visibility_internal; +#endif // _di_init_rule_buffer_ + +#ifndef _di_init_handler_child_services_ + // start, stop, and handle signals to services. + f_return_status init_handler_child_services(f_void_p argument) f_gcc_attribute_visibility_internal; +#endif // _di_init_handler_child_services_ + +#ifndef _di_init_handler_child_control_file_ + // listens on a socket file and accepts control commands. + f_return_status init_handler_child_control_file(f_void_p argument) f_gcc_attribute_visibility_internal; +#endif // _di_init_handler_child_socket_file_ + +#ifndef _di_init_initialize_stack_memory_ + f_return_status init_initialize_stack_memory(init_stack_memory *stack_memory) f_gcc_attribute_visibility_internal; +#endif // _di_init_initialize_stack_memory_ + +#ifndef _di_init_delete_stack_memory_ + f_return_status init_delete_stack_memory(init_stack_memory *stack_memory) f_gcc_attribute_visibility_internal; +#endif // _di_init_delete_stack_memory_ + +#ifndef _di_init_prepare_system_ + f_return_status init_prepare_system() f_gcc_attribute_visibility_internal; +#endif // _di_init_prepare_system_ + +#ifndef _di_init_prepare_init_ + f_return_status init_prepare_init() f_gcc_attribute_visibility_internal; +#endif // _di_init_prepare_init_ + +#ifndef _di_init_process_main_rule_ + f_return_status init_process_main_rule(const init_argument argument, f_dynamic_string *buffer, init_data *data) f_gcc_attribute_visibility_internal; +#endif // _di_init_process_main_rule_ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _PRIVATE_init_h diff --git a/level_3/init/data/build/dependencies b/level_3/init/data/build/dependencies new file mode 100644 index 0000000..3d9fca4 --- /dev/null +++ b/level_3/init/data/build/dependencies @@ -0,0 +1,17 @@ +f_types +f_errors +f_strings +f_memory +f_pipe +f_print +f_console +f_fss +fl_console +fl_file +fl_strings +fl_colors +fl_directory +fl_fss +fll_colors +fll_fss +fll_execute diff --git a/level_3/init/data/settings/core.rule b/level_3/init/data/settings/core.rule new file mode 100644 index 0000000..51207d7 --- /dev/null +++ b/level_3/init/data/settings/core.rule @@ -0,0 +1,46 @@ +# fss-0002 +# category [name] = execute rules in the specified list called [name]. +# failsafe [name] = a list whose rules get executed when required rules fail. +# rule [directory] [filename (no-extension)] [require] [last] [asynchronous] = execute a rule located in [directory][filename].rule. +# If [require] is specified, then execute failsafe list. +# If [last] is specified, then execution ceases with this list or rule. +# If [asynchronous] is specified, then execution of this rule will be run asynchronously. +# timeout [start|stop|kill] [seconds] provide a default time setting in seconds. +# +# this file is only used for booting. + +main: + timeout start 7 + timeout stop 7 + timeout kill 3 + + category boot + category net + category time + category console + + failsafe maintenance last + +maintenance: + timeout start 2 + timeout stop 2 + timeout kill 1 + + rule maintenance console + +boot: + rule boot filesystem require + rule boot modules require + rule boot devices require + rule target logger + rule target dbus + +net: + rule net all + +time: + rule service clock + +console: + rule program terminal require + rule target mouse diff --git a/level_3/init/data/settings/rules/boot/devices.rule b/level_3/init/data/settings/rules/boot/devices.rule new file mode 100644 index 0000000..b62bdb2 --- /dev/null +++ b/level_3/init/data/settings/rules/boot/devices.rule @@ -0,0 +1,14 @@ +# fss-0002 + +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; + } diff --git a/level_3/init/data/settings/rules/boot/filesystem.rule b/level_3/init/data/settings/rules/boot/filesystem.rule new file mode 100644 index 0000000..ec18c8b --- /dev/null +++ b/level_3/init/data/settings/rules/boot/filesystem.rule @@ -0,0 +1,52 @@ +# fss-0002 +# the first list to be executed, will be called 'main' and is broken up into the following fss-0000 or fss-0001 content. +# name = an identifier that this rule represents. The first rule with this identifier name to be successfully executed will cease all other uses of this list. +# need = all of the rule identifiers that represent rules required to be executed (and succeed) before this starts. +# want = all of the rule identifiers that represent rules required to be executed (but not succeed) before this starts. +# wish = all of the rule identifiers that represents rules that would be prefered to be executed before this starts. +# variable [variable name] [filename] [file format] [count number] = a variable to be loaded and used by lists in this rule. +# [variable name] = name of the variable in both the file and in the rule +# [filename] = name of the file stored in. +# [file format] = the format to read the file and load the variable. Supported formats: raw, fss-0000, fss-0001, fss-0002, fss-0003. +# [count number] = select a specific occurance of the [variable name] within the [filename]. +# +# each list, following the 'main' list will have a name that represents how it gets executed. +# 'command' represents a list with the single-line fss-0000 rules 'start', 'stop', 'restart', and 'reload' that get directly passed. +# not providing restart means that a 'start' and then 'stop' is used. +# not providing reload makes reload do nothing. +# providing 'start', 'stop', 'restart', or 'reload' but having no values means that the command will not be provided. +# +# 'script' represents a list broken up into multiple fss-0003 lists: 'start', 'stop', 'restart', and 'reload'. +# not providing restart means thata 'start' and then 'stop' is used. +# not providing reload makes reload do nothing. +# providing 'start', 'stop', 'restart', or 'reload' but having no values means that the command will not be provided. +# +# 'target' represents a 'command' list that expects a given pid file to be created by the program or created by this program. +# example pid line: 'pid program /run/program/name.pid' or: 'pid init /run/program/name.pid', such that 'program' tells the init program to expect the target to create the pid file and 'init' to expect this program to create the pid file. +# not providing stop will result in the init program reading the pid file and terminating the process by the pid. +# not providing restart means that a 'start' and then 'stop' is used. +# not providing reload makes reload do nothing. +# not providing start will result in no action on start. +# providing a 'stop' that is blank will result in no action on stop. +# +# 'timeout' [start|stop|kill] represents an amount of time to wait for pid file before considering the start, stop, or kill as problematic. +# providing a blank start or stop will result in an indefinite wait. +# kill will be called after stop fails and process is still running. +# providing a blank kill will result in kill not being executed when stop fails. +# +# each list, except for main, will be executed in a top down order (synchronously). +# +# each script, command, and target will support the following settings: +# 'user' the user id to switch to before executing/processing. +# 'group' the group id to switch to before executing/processing. + +main: + name boot-filesystem + +command: + start mount -a -O no_netdev + stop umount -arf -O no_netdev + +command: + start swapon -a + stop swapoff -a diff --git a/level_3/init/data/settings/rules/boot/modules.rule b/level_3/init/data/settings/rules/boot/modules.rule new file mode 100644 index 0000000..468635d --- /dev/null +++ b/level_3/init/data/settings/rules/boot/modules.rule @@ -0,0 +1,88 @@ +# fss-0002 + +default: + name boot-modules + need boot-filesystem + +script: + start { + if [[ ! -f /proc/modules ]] ; then + exit 0; + fi + + if [[ ! -e /lib/modules/$(uname -r)/modules.dep ]] ; then + depmod; + fi + + return 0; + } + + stop { + video_line=$(dmesg | grep -s -o '^\[drm] [[:alpha:]]* defaulting to [[:alpha:]]* modesetting.$') + + if [[ $(echo $video_line | grep -s -o '\') != "" ]] ; then + modesetting=kernel + else + modesetting=user + fi + + video_card=$(echo $video_line | grep -s -o '^\[drm] [[:alpha:]]* defaulting' | sed -e 's|^\[drm] ||' -e 's| defaulting$||') + + # handle nouveau + if [[ $video_card == "" ]] ; then + video_line=$(dmesg | grep -s -o '^\[drm] Initialized nouveau .*$') + + if [[ $video_line != "" ]] ; then + video_line=$(dmesg | grep -s -o '^fbcon: nouveaufb (fb0) is primary device$') + + if [[ $video_line != "" ]] ; then + modesetting=kernel + video_card=nouveau + fi + fi + fi + + if [[ $modesetting == "user" ]] ; then + video_line=$(dmesg | grep -s -o '^fbcon: NV.* (fb0) is primary device$') + + if [[ $video_line != "" ]] ; then + video_card=nv + fi + + if [[ $video_card == "" ]] ; then + video_line=$(dmesg | grep -s -o '^fbcon: radeon.*fb (fb0) is primary device$') + + if [[ $video_line != "" ]] ; then + video_card=radeon + fi + fi + + if [[ $video_card == "" ]] ; then + video_line=$(dmesg | grep -s -o '^fbcon: .* Radeon .* (fb0) is primary device$') + + if [[ $video_line != "" ]] ; then + video_card=radeon + fi + fi + + if [[ $video_card == "" ]] ; then + video_line=$(dmesg | grep -s -o '^fbcon: intel.*fb (fb0) is primary device$') + + if [[ $video_line != "" ]] ; then + video_card=intel + fi + fi + fi + + if [[ ! -f /dev/modesetting ]] ; then + echo $modesetting > /dev/modesetting + chmod u+rw-x,g+r-wx,o-rwx /dev/modesetting + chgrp e_xorg /dev/modesetting + fi + + if [[ ! -f /dev/video_card ]] ; then + echo $video_card > /dev/video_card + chmod u+rw-x,g+r-wx,o-rwx /dev/video_card + chgrp e_xorg /dev/video_card + fi + } diff --git a/level_3/init/data/settings/rules/net/all.rule b/level_3/init/data/settings/rules/net/all.rule new file mode 100644 index 0000000..b09acbc --- /dev/null +++ b/level_3/init/data/settings/rules/net/all.rule @@ -0,0 +1,9 @@ +# fss-0002 + +main: + name net-all + +command: + start network start + stop network stop + restart network restart diff --git a/level_3/init/data/settings/rules/net/loopback.rule b/level_3/init/data/settings/rules/net/loopback.rule new file mode 100644 index 0000000..aa6b169 --- /dev/null +++ b/level_3/init/data/settings/rules/net/loopback.rule @@ -0,0 +1,14 @@ +# fss-0002 + +main: + name net-loopback + +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; + } diff --git a/level_3/init/data/settings/rules/program/terminal.rule b/level_3/init/data/settings/rules/program/terminal.rule new file mode 100644 index 0000000..766fde1 --- /dev/null +++ b/level_3/init/data/settings/rules/program/terminal.rule @@ -0,0 +1,21 @@ +# fss-0002 + +main: + name program-terminal + want boot-modules + +target: + start qingy tty1 -d -l -n -t + pid init /run/tty/tty1.pid + +target: + start qingy tty2 -d -l -n -t + pid init /run/tty/tty2.pid + +target: + start qingy tty3 -d -l -n -t + pid init /run/tty/tty3.pid + +target: + start qingy tty4 -d -l -n -t + pid init /run/tty/tty4.pid diff --git a/level_3/init/data/settings/rules/service/clock.rule b/level_3/init/data/settings/rules/service/clock.rule new file mode 100644 index 0000000..1edf634 --- /dev/null +++ b/level_3/init/data/settings/rules/service/clock.rule @@ -0,0 +1,23 @@ +# fss-0002 + +main: + name + +script: + start { + clock_mode=$(fss_basic_read -c 0 -n mode /etc/clock); + + if [[ $clock_mode == "local" ]] ; then + hwclock --hctosys; + elif [[ $clock_mode == "ntpdate" ]] ; then + ntpdate $(fss_basic_read -c 0 -n server /etc/clock) && + hwclock --systohc --utc + elif [[ $clock_mode == "ntp" ]] ; then + if [[ $(fss_basic_read -c 0 -n ntpdate /etc/clock) == "yes" ]] ; then + ntpdate $(fss_basic_read -c 0 -n server /etc/clock) && + hwclock --systohc --utc + fi + elif [[ $clock_mode == "utc" ]] ; then + hwclock --hctosys --utc; + fi + } diff --git a/level_3/init/data/settings/rules/target/dbus.rule b/level_3/init/data/settings/rules/target/dbus.rule new file mode 100644 index 0000000..369ab3f --- /dev/null +++ b/level_3/init/data/settings/rules/target/dbus.rule @@ -0,0 +1,8 @@ +# fss-0002 + +main: + name target-dbus + +target: + start dbus-daemon --system --fork + pid target /run/dbus/dbus.pid diff --git a/level_3/init/data/settings/rules/target/logger.rule b/level_3/init/data/settings/rules/target/logger.rule new file mode 100644 index 0000000..eaa1d51 --- /dev/null +++ b/level_3/init/data/settings/rules/target/logger.rule @@ -0,0 +1,9 @@ +# fss-0002 + +main: + name target/logger + +target: + start metalog -B -p /run/logger/logger.pid -C /etc/logger.conf + pid target /run/logger/logger.pid + diff --git a/level_3/init/data/settings/rules/target/mouse.rule b/level_3/init/data/settings/rules/target/mouse.rule new file mode 100644 index 0000000..40aa768 --- /dev/null +++ b/level_3/init/data/settings/rules/target/mouse.rule @@ -0,0 +1,12 @@ +# fss-0002 + +main: + name target-mouse + want boot/modules + variable device /etc/mouse fss-0000 + variable protocol /etc/mouse fss-0000 + variable options /etc/mouse fss-0000 + +target: + start gpm -m [device] -t [protocol] [options] + pid /run/mouse/mouse.pid