From c2b3091d6eb70d908d98ac7c5ecaa837058d68d5 Mon Sep 17 00:00:00 2001 From: Okerew <93822247+Okerew@users.noreply.github.com> Date: Thu, 22 Aug 2024 17:15:42 +0200 Subject: [PATCH 01/23] Update osxiec.c Added stoppping, starting functionality, removed -execute as run servers it's purpose well, added check-for-uptade which checks for uptades --- osxiec.c | 423 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 381 insertions(+), 42 deletions(-) diff --git a/osxiec.c b/osxiec.c index b0e706a..6afb9df 100644 --- a/osxiec.c +++ b/osxiec.c @@ -37,6 +37,7 @@ #define MAX_MEMORY_LIMIT 2147483648 // 2 GB max memory limit #define MAX_HISTORY_LEN 100 #define MAX_LINE_LEN 1024 +#define VERSION "0.7" int port = PORT; @@ -1350,7 +1351,7 @@ void create_isolated_environment(FILE *bin_file, const char *bin_file_path, Cont snprintf(disk_image_path, sizeof(disk_image_path), "/tmp/container_disk_%d.dmg", getpid()); char create_disk_command[MAX_COMMAND_LEN]; - snprintf(create_disk_command, sizeof(create_disk_command), "hdiutil create -size 1g -fs HFS+ -volname Container %s", disk_image_path); + snprintf(create_disk_command, sizeof(create_disk_command), "hdiutil create -size 1g -fs HFS+ -volname \"%s\" %s", bin_file_path, disk_image_path); system(create_disk_command); chmod(disk_image_path, 0644); // rw-r--r-- @@ -1359,7 +1360,8 @@ void create_isolated_environment(FILE *bin_file, const char *bin_file_path, Cont snprintf(mount_command, sizeof(mount_command), "hdiutil attach %s", disk_image_path); system(mount_command); - char container_root[] = "/Volumes/Container"; + char container_root[MAX_PATH_LEN]; + snprintf(container_root, sizeof(container_root), "/Volumes/%s", bin_file_path); // Create a symbolic link to the shared folder char shared_mount_point[MAX_PATH_LEN]; @@ -1454,8 +1456,9 @@ void create_isolated_environment(FILE *bin_file, const char *bin_file_path, Cont exit(1); } - printf("\n=== Container Terminal ===\n"); + printf("\n=== Container %s Terminal ===\n", bin_file_path); printf("Enter commands (type 'exit' to quit, help for help):\n"); + printf("If you just ran the container ignore the first log file error"); // Start the network listener in a separate thread pthread_t network_thread; @@ -1555,13 +1558,41 @@ void create_isolated_environment(FILE *bin_file, const char *bin_file_path, Cont printf(" autoscale: Start automatic resource scaling\n"); printf(" status: Print current resource usage\n"); printf(" help: Print this help message\n"); + printf(" stop: Stops the container and save its state\n"); + } else if (strcmp(command, "stop") == 0) { + printf("Stopping container...\n"); + + // Save the container state + char state_file_path[MAX_PATH_LEN]; + time_t now = time(NULL); + + // Construct the state file path using the Unix timestamp + snprintf(state_file_path, sizeof(state_file_path), "%s/%ld_%s", container_root, now, bin_file_path); + + // state_file_path now includes the date + FILE *state_file = fopen(state_file_path, "wb"); + if (state_file == NULL) { + perror("Error creating container state file"); + } else { + // Save the container configuration + fwrite(&config, sizeof(ContainerConfig), 1, state_file); + + // Save the environment variables + fwrite(&container_state, sizeof(ContainerState), 1, state_file); + fclose(state_file); + printf("Container state saved to %s\n", state_file_path); + } + + goto stop_loop; } else { execute_command(command); } printf("\n"); add_to_history(command); - FILE *log_file = fopen("/Volumes/Container/log.txt", "a"); + char log_file_path[MAX_PATH_LEN]; + snprintf(log_file_path, sizeof(log_file_path), "%s/log.txt", container_root); + FILE *log_file = fopen(log_file_path, "a"); if (log_file == NULL) { perror("Failed to open log file"); } else { @@ -1605,8 +1636,24 @@ void create_isolated_environment(FILE *bin_file, const char *bin_file_path, Cont free(container_state.environment_variables[i]); } free(container_state.environment_variables); + +stop_loop: + set_terminal_canonical_mode(); + printf("Container stopped.\n"); + + pthread_cancel(network_thread); + pthread_join(network_thread, NULL); + + pthread_cancel(logger); + pthread_join(logger, NULL); + + // Preserve the environment variables + for (int i = 0; i < container_state.num_env_vars; i++) { + setenv(container_state.environment_variables[i], NULL, 1); + } } + void ocreate_isolated_environment(FILE *bin_file, const char *bin_file_path) { signal(SIGTERM, handle_signal); signal(SIGINT, handle_signal); @@ -1623,8 +1670,9 @@ void ocreate_isolated_environment(FILE *bin_file, const char *bin_file_path) { char disk_image_path[MAX_PATH_LEN]; snprintf(disk_image_path, sizeof(disk_image_path), "/tmp/container_disk_%d.dmg", getpid()); + // Assign the volume name as the bin_file_path char create_disk_command[MAX_COMMAND_LEN]; - snprintf(create_disk_command, sizeof(create_disk_command), "hdiutil create -size 1g -fs HFS+ -volname Container %s", disk_image_path); + snprintf(create_disk_command, sizeof(create_disk_command), "hdiutil create -size 1g -fs HFS+ -volname \"%s\" %s", bin_file_path, disk_image_path); system(create_disk_command); chmod(disk_image_path, 0644); @@ -1633,7 +1681,9 @@ void ocreate_isolated_environment(FILE *bin_file, const char *bin_file_path) { snprintf(mount_command, sizeof(mount_command), "hdiutil attach %s", disk_image_path); system(mount_command); - char container_root[] = "/Volumes/Container"; + // Use bin_file_path as the root volume name + char container_root[MAX_PATH_LEN]; + snprintf(container_root, sizeof(container_root), "/Volumes/%s", bin_file_path); char shared_mount_point[MAX_PATH_LEN]; snprintf(shared_mount_point, sizeof(shared_mount_point), "%s/shared", container_root); @@ -1724,8 +1774,9 @@ void ocreate_isolated_environment(FILE *bin_file, const char *bin_file_path) { exit(1); } - printf("\n=== Container Terminal ===\n"); + printf("\n=== Container %s Terminal ===\n", bin_file_path); printf("Enter commands (type 'exit' to quit, help for help):\n"); + printf("If you just ran the container ignore the first log file error"); if (config.start_config[0] != '\0') { char start_config_path[MAX_PATH_LEN]; @@ -1820,15 +1871,39 @@ void ocreate_isolated_environment(FILE *bin_file, const char *bin_file_path) { printf(" autoscale: Start automatic resource scaling\n"); printf(" status: Print current resource usage\n"); printf(" help: Print this help message\n"); + printf(" stop: Stops the container and saves its state\n"); + } else if (strcmp(command, "stop") == 0) { + printf("Stopping container...\n"); + + // Save the container state + char state_file_path[MAX_PATH_LEN]; + snprintf(state_file_path, sizeof(state_file_path), "%s/%s", container_root, bin_file_path); + FILE *state_file = fopen(state_file_path, "wb"); + if (state_file == NULL) { + perror("Error creating container state file"); + } else { + // Save the container configuration + fwrite(&config, sizeof(ContainerConfig), 1, state_file); + + // Save the environment variables + fwrite(&container_state, sizeof(ContainerState), 1, state_file); + fclose(state_file); + printf("Container state saved to %s\n", state_file_path); + } + + goto stop_loop; } else { execute_command(command); } printf("\n"); add_to_history(command); - FILE *log_file = fopen("/Volumes/Container/log.txt", "a"); + char log_file_path[MAX_PATH_LEN]; + snprintf(log_file_path, sizeof(log_file_path), "%s/log.txt", container_root); + FILE *log_file = fopen(log_file_path, "a"); if (log_file == NULL) { perror("Failed to open log file"); + printf("If you just ran the container ignore this"); } else { fprintf(log_file, "\nCommand History:\n"); for (int i = 0; i < command_index; i++) { @@ -1865,6 +1940,18 @@ void ocreate_isolated_environment(FILE *bin_file, const char *bin_file_path) { free(container_state.environment_variables[i]); } free(container_state.environment_variables); + exit(0); + +stop_loop: + set_terminal_canonical_mode(); + printf("Container stopped.\n"); + pthread_cancel(logger); + pthread_join(logger, NULL); + + // Preserve the environment variables + for (int i = 0; i < container_state.num_env_vars; i++) { + setenv(container_state.environment_variables[i], NULL, 1); + } } size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) { @@ -2227,13 +2314,15 @@ void deploy_container(const char *config_file, int deploy_port) { } -void detach_container_images() { - printf("Detaching all Containes\n"); - int result = system("hdiutil detach -force /Volumes/Container*"); +void detach_container_images(const char *volume_name) { + printf("Detaching %s Containers\n", volume_name); + char command[MAX_PATH_LEN]; + snprintf(command, sizeof(command), "hdiutil detach -force /Volumes/%s", volume_name); + int result = system(command); if (result == 0) { - printf("All Container disk images detached successfully.\n"); + printf("Container disk image detached.\n"); } else { - fprintf(stderr, "Failed to detach some or all 'Container' disk images.\n"); + fprintf(stderr, "Failed to detach Container disk image.\n"); } } @@ -2517,6 +2606,161 @@ void convert_to_oci(const char *osxiec_file, const char *output_dir, const char printf("OCI container structure created in %s\n", output_dir); } +char* find_latest_bin_file(const char *volume_name) { + DIR *dir; + struct dirent *entry; + char *latest_file_path = NULL; + time_t latest_timestamp = 0; + char search_directory[MAX_PATH_LEN]; + + // Format the search directory path + snprintf(search_directory, sizeof(search_directory), "/Volumes/%s", volume_name); + + if ((dir = opendir(search_directory)) == NULL) { + perror("Unable to open directory"); + return NULL; + } + + while ((entry = readdir(dir)) != NULL) { + // Check if the file ends with ".bin" + if (strstr(entry->d_name, ".bin") != NULL) { + // Extract Unix timestamp from the beginning of the filename + time_t timestamp = atol(entry->d_name); + + if (timestamp > latest_timestamp) { + latest_timestamp = timestamp; + + // Free the previous path and allocate for the new one + free(latest_file_path); + latest_file_path = malloc(MAX_PATH_LEN); + snprintf(latest_file_path, MAX_PATH_LEN, "%s/%s", search_directory, entry->d_name); + } + } + } + closedir(dir); + + return latest_file_path; +} + +int copy_file(const char *source, const char *destination_folder, const char *new_file_name) { + // Construct the full path for the destination file + char destination[MAX_PATH_LEN]; + snprintf(destination, sizeof(destination), "%s/%s", destination_folder, new_file_name); + + FILE *src = fopen(source, "rb"); + if (src == NULL) { + perror("Error opening source file"); + return -1; + } + + FILE *dst = fopen(destination, "wb"); + if (dst == NULL) { + perror("Error opening destination file"); + fclose(src); + return -1; + } + + char buffer[BUFSIZ]; + size_t n; + while ((n = fread(buffer, 1, sizeof(buffer), src)) > 0) { + if (fwrite(buffer, 1, n, dst) != n) { + perror("Error writing to destination file"); + fclose(src); + fclose(dst); + return -1; + } + } + + fclose(src); + fclose(dst); + return 0; +} + +size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp) { + size_t total_size = size * nmemb; + char **response_ptr = (char **)userp; + + // Allocate or reallocate memory for the response + char *temp = realloc(*response_ptr, total_size + ( *response_ptr ? strlen(*response_ptr) : 0 ) + 1); + if (temp == NULL) { + fprintf(stderr, "Failed to allocate memory.\n"); + return 0; // Abort the transfer + } + *response_ptr = temp; + + // Append new data to the response buffer + if (*response_ptr) { + memcpy(*response_ptr + ( *response_ptr ? strlen(*response_ptr) : 0 ), contents, total_size); + (*response_ptr)[total_size + ( *response_ptr ? strlen(*response_ptr) : 0 )] = '\0'; // Null-terminate + } + + return total_size; +} + + +char* fetch_latest_version(void) { + CURL *curl; + CURLcode res; + char *latest_version = NULL; + + curl = curl_easy_init(); + if(curl) { + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "User-Agent: osxiec-update-checker"); + + curl_easy_setopt(curl, CURLOPT_URL, "https://api.github.com/repos/Okerew/osxiec/releases/latest"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + + char *response = NULL; + + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); + + res = curl_easy_perform(curl); + + if(res == CURLE_OK) { + if (response) { + struct json_object *parsed_json = json_tokener_parse(response); + if (parsed_json == NULL) { + fprintf(stderr, "Failed to parse JSON.\n"); + } else { + struct json_object *tag_name; + if (json_object_object_get_ex(parsed_json, "tag_name", &tag_name)) { + const char *version = json_object_get_string(tag_name); + latest_version = strdup(version); + } else { + fprintf(stderr, "JSON does not contain 'tag_name' field.\n"); + } + + json_object_put(parsed_json); + } + } else { + fprintf(stderr, "No response data received.\n"); + } + } else { + fprintf(stderr, "CURL request failed: %s\n", curl_easy_strerror(res)); + } + + curl_easy_cleanup(curl); + curl_slist_free_all(headers); + free(response); + } else { + fprintf(stderr, "Failed to initialize CURL.\n"); + } + + return latest_version; +} + +// Add this function to compare version strings +int compare_versions(const char* v1, const char* v2) { + int a, b, c, d, e, f; + sscanf(v1, "%d.%d.%d", &a, &b, &c); + sscanf(v2, "%d.%d.%d", &d, &e, &f); + if (a != d) return a - d; + if (b != e) return b - e; + return c - f; +} + int main(int argc, char *argv[]) { PluginManager plugin_manager; plugin_manager_init(&plugin_manager); @@ -2602,29 +2846,6 @@ int main(int argc, char *argv[]) { const char *container_config_file = (argc > 6) ? argv[6] : NULL; containerize_directory_with_bin_file(argv[2], argv[3], argv[4], start_config_file, container_config_file); printf("Directory contents containerized into '%s'.\n", argv[4]); - } else if (strcmp(argv[1], "-execute") == 0) { - if (argc < 3) { - fprintf(stderr, "Usage for execute: %s -execute [-port ]\n", argv[0]); - return EXIT_FAILURE; - } - - // Parse port if provided - for (int i = 3; i < argc; i++) { - if (strcmp(argv[i], "-port") == 0 && i + 1 < argc) { - port = atoi(argv[i + 1]); - break; - } - } - - FILE *bin_file = fopen(argv[2], "rb"); - if (bin_file == NULL) { - perror("Error opening binary file"); - return EXIT_FAILURE; - } - - ContainerNetwork dummy_network = {0}; - create_isolated_environment(bin_file, argv[2], &dummy_network); - fclose(bin_file); } else if (strcmp(argv[1], "-oexec") == 0) { if (argc < 3) { fprintf(stderr, "Usage for execute: %s -execute [-port ]\n", argv[0]); @@ -2690,8 +2911,107 @@ int main(int argc, char *argv[]) { } create_isolated_environment(bin_file, argv[2], &network); fclose(bin_file); + } else if (strcmp(argv[1], "-start") == 0) { + if (argc < 4) { + fprintf(stderr, "Usage: %s -start [-port ]\n", argv[0]); + return EXIT_FAILURE; + } + + for (int i = 4; i < argc; i++) { + if (strcmp(argv[i], "-port") == 0 && i + 1 < argc) { + port = atoi(argv[i + 1]); + break; + } + } + + // Load network configuration + ContainerNetwork network = load_container_network(argv[3]); + + if (network.vlan_id == 0) { + fprintf(stderr, "Failed to load network configuration for %s\n", argv[3]); + return EXIT_FAILURE; + } + + // Find the latest binary file in the specified volume + const char *volume_name = argv[2]; + char *latest_bin_file_path = find_latest_bin_file(volume_name); + + if (latest_bin_file_path == NULL) { + fprintf(stderr, "No valid .bin file found for volume %s\n", volume_name); + return EXIT_FAILURE; + } + + // Copy the latest file to the root of the volume with just the volume name + char dest_folder[MAX_PATH_LEN]; + snprintf(dest_folder, sizeof(dest_folder), "/Volumes/%s", volume_name); + + if (copy_file(latest_bin_file_path, dest_folder, volume_name) != 0) { + fprintf(stderr, "Failed to copy binary file to volume root\n"); + free(latest_bin_file_path); + return EXIT_FAILURE; + } + + // Open the copied binary file + char full_dest_path[MAX_PATH_LEN]; + snprintf(full_dest_path, sizeof(full_dest_path), "%s/%s", dest_folder, volume_name); + + printf("Opening binary file: %s\n", full_dest_path); + FILE *bin_file = fopen(full_dest_path, "rb"); + if (bin_file == NULL) { + perror("Error opening binary file"); + fprintf(stderr, "Failed to open: %s\n", full_dest_path); + free(latest_bin_file_path); + return EXIT_FAILURE; + } + + create_isolated_environment(bin_file, volume_name, &network); + + fclose(bin_file); + free(latest_bin_file_path); + } else if (strcmp(argv[1], "-ostart") == 0) { + if (argc < 2) { + fprintf(stderr, "Usage: %s -ostart \n", argv[0]); + return EXIT_FAILURE; + } + + // Find the latest binary file in the specified volume + const char *volume_name = argv[2]; + char *latest_bin_file_path = find_latest_bin_file(volume_name); + + if (latest_bin_file_path == NULL) { + fprintf(stderr, "No valid .bin file found for volume %s\n", volume_name); + return EXIT_FAILURE; + } + + // Copy the latest file to the root of the volume with just the volume name + char dest_folder[MAX_PATH_LEN]; + snprintf(dest_folder, sizeof(dest_folder), "/Volumes/%s", volume_name); + + if (copy_file(latest_bin_file_path, dest_folder, volume_name) != 0) { + fprintf(stderr, "Failed to copy binary file to volume root\n"); + free(latest_bin_file_path); + return EXIT_FAILURE; + } + + // Open the copied binary file + char full_dest_path[MAX_PATH_LEN]; + snprintf(full_dest_path, sizeof(full_dest_path), "%s/%s", dest_folder, volume_name); + + printf("Opening binary file: %s\n", full_dest_path); + FILE *bin_file = fopen(full_dest_path, "rb"); + if (bin_file == NULL) { + perror("Error opening binary file"); + fprintf(stderr, "Failed to open: %s\n", full_dest_path); + free(latest_bin_file_path); + return EXIT_FAILURE; + } + + ocreate_isolated_environment(bin_file, volume_name); + + fclose(bin_file); + free(latest_bin_file_path); } else if (strcmp(argv[1], "--version") == 0) { - printf("Osxiec version 0.69\n"); + printf("Osxiec version %s\n", VERSION); } else if (strcmp(argv[1], "-pull") == 0) { if (argc != 3) { printf("Usage: %s -pull \n", argv[0]); @@ -2758,11 +3078,11 @@ int main(int argc, char *argv[]) { system(command); } else if (strcmp(argv[1], "-detach") == 0) { - if (argc > 2) { - fprintf(stderr, "Usage: %s -detach\n", argv[0]); + if (argc != 3) { + fprintf(stderr, "Usage: %s -detach \n", argv[0]); return EXIT_FAILURE; } - detach_container_images(); + detach_container_images(argv[2]); } else if (strcmp(argv[1], "-extract") == 0) { if (argc != 4) { fprintf(stderr, "Usage: %s -extract \n", argv[0]); @@ -2775,10 +3095,12 @@ int main(int argc, char *argv[]) { printf("Contains a directory into a container file\n"); printf(" -craft \n"); printf("Crafts a container file from a directory and a bin file\n"); - printf(" -execute [-port ]\n"); - printf("Executes a container file\n"); printf(" -oexec \n"); printf("Executes a container file in offline mode\n"); + printf(" -start [-port ]\n"); + printf("Starts a stopped container"); + printf(" -ostart [-port ]\n"); + printf("Starts a stopped container in offline mode"); printf(" -network [vlan_id>\n"); printf("Manages the vlan network\n"); printf(" -run [-port ]\n"); @@ -2809,6 +3131,23 @@ int main(int argc, char *argv[]) { printf("Prints the version of osxiec\n"); printf(" -help\n"); printf("Prints this help message\n"); + printf(" -check-for-update\n"); + printf("Checks for updates\n"); + } else if (strcmp(argv[1], "-check-for-update") == 0) { + char* latest_version = fetch_latest_version(); + if (latest_version) { + int comparison = compare_versions(VERSION, latest_version); + if (comparison < 0) { + printf("An update is available. Latest version: %s\n", latest_version); + printf("Your current version: %s\n", VERSION); + printf("Please visit https://github.com/Okerew/osxiec/releases/latest to update.\n"); + } else if (comparison == 0) { + printf("You are running the latest version (%s).\n", VERSION); + } + free(latest_version); + } else { + printf("Failed to check for updates. Please check your internet connection.\n"); + } } else { fprintf(stderr, "Unknown command: %s\n", argv[1]); return EXIT_FAILURE; From e7d2f82ef6e35fc66d89d2964e9544126df96d66 Mon Sep 17 00:00:00 2001 From: Okerew <93822247+Okerew@users.noreply.github.com> Date: Thu, 22 Aug 2024 17:16:08 +0200 Subject: [PATCH 02/23] Update README.md --- README.md | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index a5667e5..c13746b 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,6 @@ https://apps.apple.com/us/app/xcode/id497799835?mt=12 1. **Download the Release**: Download the `osxiec_cli.tar.gz` and `osxiec_gui.tar.gz` if you want a gui app file from the releases section. - 2. **Extract the Archive**: ```sh tar -xvzf osxiec_cli.tar.gz @@ -70,15 +69,6 @@ To update to a new release, repeat steps 1, 2, and 3 if using osxiec_gui also st sudo osxiec -contain {directory_path} {some_name}.bin {path_to_config_file_in_directory_path} {container_config_file} ``` -**Execute a Container**: executes container -```sh -sudo osxiec -execute {some_name}.bin -``` - -**Execute with Port Argument**: execute with port -```sh -sudo osxiec -execute {some_name} -port {PORT_NUMBER} -``` **Create a cluster ( virtualized network )** create a cluster ```sh sudo osxiec -network create {network_name} {vlan_id} @@ -89,8 +79,10 @@ sudo osxiec -network remove {network_name} {vlan_id} ``` **Run with vlan config** run with vlan config ``` sh -sudo osxiec -run {some_name} {network_name} -port {PORT_NUMBER} +sudo osxiec -run {some_name.bin} {network_name} -port {PORT_NUMBER} ``` +port argument is optional + **Version** checks the current version ```sh osxiec --version @@ -161,6 +153,22 @@ osxiec -convert-to-oci {bin_file_path} {output_path} {arch} {author} sudo osxiec -craft {directory_path} {bin_input_file} {output_file} {start_config_file} {container_config_file} ``` +**Check for update** checks for update +```sh +osxiec -check-for-update +``` + +**Start** starts a container a stopped container +```sh +osxiec -start {volume_name} {network} -port {PORT_NUMBER} +``` +port is optional + +**Ostart** starts a container a stopped container in offline mode +```sh +osxiec -ostart {volume_name} +``` + ## Creating a container Make sure to include any dependencies or executables you can obtain these by searching for where a dependency or executable is located and copying it along with it's dependencies. @@ -204,9 +212,9 @@ This will also scan the container for security vulnerabilities. After creating a container or downloading one. **You can execute one with** -`osxiec -execute {container_name}` +`osxiec -oexec {container_name}` -If you want you can also use a custom port with `-port {PORT_NUMBER}` +This will execute it in offline mode. If you want to execute it in online mode see the next point. **Run with vlan** @@ -233,6 +241,7 @@ If you want to access the container terminal just press enter. 5. **xs** Execute an osxs script command 6. **autoscale** automatically scales the resources of the container 7. **status** shows the status of the container +8. **stop** stops the container. ## Creating a vlan network To create a network you can run `osxiec -network create {network_name} {vlan_id}` From 03aa3df9889939b9cbaea38fda0c278f68b1aacd Mon Sep 17 00:00:00 2001 From: Okerew <93822247+Okerew@users.noreply.github.com> Date: Tue, 27 Aug 2024 12:00:36 +0200 Subject: [PATCH 03/23] Update osxiec.c Added dynamic ip creation, updating, some small improvements, bug fixes, an api that allows to use some of the functions. --- osxiec.c | 198 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 170 insertions(+), 28 deletions(-) diff --git a/osxiec.c b/osxiec.c index 6afb9df..a439091 100644 --- a/osxiec.c +++ b/osxiec.c @@ -37,7 +37,8 @@ #define MAX_MEMORY_LIMIT 2147483648 // 2 GB max memory limit #define MAX_HISTORY_LEN 100 #define MAX_LINE_LEN 1024 -#define VERSION "0.7" +#define VERSION "v0.71" +#define OSXIEC_ARCHITECTURE "arm64" int port = PORT; @@ -70,6 +71,7 @@ typedef struct { int vlan_id; int num_containers; char container_names[MAX_CLIENTS][MAX_PATH_LEN]; + char container_ips[MAX_CLIENTS][16]; } ContainerNetwork; int debug_mode = DEBUG_NONE; @@ -278,7 +280,7 @@ int is_subpath(const char *path, const char *base) { } -void execute_command(const char *command) { +void execute_command(const char *command, const char *container_root) { if (command == NULL || strlen(command) == 0) { fprintf(stderr, "Error: Empty command\n"); return; @@ -302,7 +304,6 @@ void execute_command(const char *command) { // Update current directory if it's a cd command if (strncmp(command, "cd ", 3) == 0) { const char *new_dir = command + 3; - const char container_root[] = "/Volumes/Container"; const char shared_folder_path[] = "/Volumes/SharedContainer"; char current_path[PATH_MAX]; @@ -381,7 +382,7 @@ void execute_command(const char *command) { free(command_copy); } -void execute_start_config(const char *config_file) { +void execute_start_config(const char *config_file, const char *container_root) { FILE *file = fopen(config_file, "r"); if (file == NULL) { perror("Error opening start configuration file"); @@ -399,7 +400,7 @@ void execute_start_config(const char *config_file) { } printf("Executing start command: %s\n", line); - execute_command(line); + execute_command(line, container_root); } fclose(file); @@ -601,7 +602,7 @@ void containerize_directory(const char *dir_path, const char *output_file, const .memory_soft_limit = 384 * 1024 * 1024, .memory_hard_limit = 512 * 1024 * 1024, .cpu_priority = 20, - .network_mode = "host", + .network_mode = "bridge", .container_uid = 1000, .container_gid = 1000, .start_config = "" @@ -683,7 +684,7 @@ void containerize_directory_with_bin_file(const char *dir_path, const char *inpu .memory_soft_limit = 384 * 1024 * 1024, .memory_hard_limit = 512 * 1024 * 1024, .cpu_priority = 20, - .network_mode = "host", + .network_mode = "bridge", .container_uid = 1000, .container_gid = 1000, .start_config = "" @@ -838,6 +839,13 @@ ContainerNetwork load_container_network(const char *name) { if (sscanf(line, "vlan_id=%d", &network.vlan_id) == 1) { continue; } + if (sscanf(line, "container_name=%s", network.container_names[network.num_containers]) == 1) { + network.num_containers++; + continue; + } + if (sscanf(line, "container_ip=%s", network.container_ips[network.num_containers - 1]) == 1) { + continue; + } } fclose(file); @@ -881,10 +889,31 @@ void remove_container_network(const char *name) { void add_container_to_network(ContainerNetwork *network, const char *container_name) { if (network->num_containers < MAX_CLIENTS) { strncpy(network->container_names[network->num_containers], container_name, MAX_PATH_LEN - 1); + + // Dynamically assign IP address based on the number of containers + char container_ip[16]; + snprintf(container_ip, sizeof(container_ip), "192.168.%d.%d", network->vlan_id, network->num_containers + 2); + strncpy(network->container_ips[network->num_containers], container_ip, 15); + network->num_containers++; + + // Save the updated network configuration to a file + char filename[MAX_PATH_LEN]; + snprintf(filename, sizeof(filename), "/tmp/network_%s.conf", network->name); + + FILE *file = fopen(filename, "a"); + if (file == NULL) { + perror("Failed to save network configuration"); + return; + } + + fprintf(file, "container_name=%s\n", container_name); + fprintf(file, "container_ip=%s\n", container_ip); + fclose(file); } } + char *get_ip_address() { int sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock == -1) { @@ -975,20 +1004,23 @@ void setup_pf_rules(ContainerNetwork *network) { free(ip_address); } - void setup_network_isolation(ContainerConfig *config, ContainerNetwork *network) { if (strcmp(config->network_mode, "bridge") == 0) { config->vlan_id = network->vlan_id; + + // Dynamically assign IP address based on the number of containers + char container_ip[16]; + snprintf(container_ip, sizeof(container_ip), "192.168.%d.%d", network->vlan_id, network->num_containers + 2); add_container_to_network(network, config->name); - printf("Setting up bridge network. Container %s on VLAN %d\n", config->name, config->vlan_id); + printf("Setting up bridge network. Container %s on VLAN %d with IP %s\n", config->name, config->vlan_id, container_ip); char vlan_cmd[256]; snprintf(vlan_cmd, sizeof(vlan_cmd), "ifconfig vlan%d create vlan %d vlandev en0", config->vlan_id, config->vlan_id); system(vlan_cmd); - snprintf(vlan_cmd, sizeof(vlan_cmd), "ifconfig vlan%d up", config->vlan_id); + snprintf(vlan_cmd, sizeof(vlan_cmd), "ifconfig vlan%d inet %s/24 up", config->vlan_id, container_ip); system(vlan_cmd); } else if (strcmp(config->network_mode, "host") == 0) { printf("Using host network mode\n"); @@ -999,6 +1031,7 @@ void setup_network_isolation(ContainerConfig *config, ContainerNetwork *network) } } + void enable_container_communication(ContainerNetwork *network) { char pf_rule[256]; snprintf(pf_rule, sizeof(pf_rule), @@ -1018,7 +1051,7 @@ void enable_container_communication(ContainerNetwork *network) { system("pfctl -f /etc/pf.conf"); } -void handle_client(int client_socket) { +void handle_client(int client_socket, const char *container_root) { char command[MAX_COMMAND_LEN]; ssize_t bytes_received; @@ -1030,7 +1063,7 @@ void handle_client(int client_socket) { } // Execute the command - execute_command(command); + execute_command(command, container_root); // Send a response back to the client const char *response = "Command executed.\n"; @@ -1040,7 +1073,7 @@ void handle_client(int client_socket) { close(client_socket); } -void start_network_listener() { +void start_network_listener(const char *container_root) { int server_fd, client_socket; struct sockaddr_in address; int opt = 1; @@ -1082,7 +1115,7 @@ void start_network_listener() { } printf("New client connected\n"); - handle_client(client_socket); + handle_client(client_socket, container_root); } } @@ -1350,8 +1383,24 @@ void create_isolated_environment(FILE *bin_file, const char *bin_file_path, Cont char disk_image_path[MAX_PATH_LEN]; snprintf(disk_image_path, sizeof(disk_image_path), "/tmp/container_disk_%d.dmg", getpid()); + // Assign the volume name as the bin_file_path char create_disk_command[MAX_COMMAND_LEN]; - snprintf(create_disk_command, sizeof(create_disk_command), "hdiutil create -size 1g -fs HFS+ -volname \"%s\" %s", bin_file_path, disk_image_path); + struct stat st; + if (stat(bin_file_path, &st) == -1) { + perror("stat"); + } + + // Get the size of the file in bytes + off_t file_size = st.st_size; + + // Convert the size to gigabytes and add 1 GB + double size_in_gb = (double)file_size / (1024 * 1024 * 1024) + 1.0; + + // Format the size to two decimal places + snprintf(create_disk_command, sizeof(create_disk_command), + "hdiutil create -size %.2fg -fs HFS+ -volname \"%s\" %s", + size_in_gb, bin_file_path, disk_image_path); + system(create_disk_command); chmod(disk_image_path, 0644); // rw-r--r-- @@ -1469,7 +1518,7 @@ void create_isolated_environment(FILE *bin_file, const char *bin_file_path, Cont if (config.start_config[0] != '\0') { char start_config_path[MAX_PATH_LEN]; snprintf(start_config_path, sizeof(start_config_path), "%s/%s", container_root, config.start_config); - execute_start_config(start_config_path); + execute_start_config(start_config_path, container_root); } char command[MAX_COMMAND_LEN]; @@ -1585,7 +1634,7 @@ void create_isolated_environment(FILE *bin_file, const char *bin_file_path, Cont goto stop_loop; } else { - execute_command(command); + execute_command(command, container_root); } printf("\n"); @@ -1672,7 +1721,22 @@ void ocreate_isolated_environment(FILE *bin_file, const char *bin_file_path) { // Assign the volume name as the bin_file_path char create_disk_command[MAX_COMMAND_LEN]; - snprintf(create_disk_command, sizeof(create_disk_command), "hdiutil create -size 1g -fs HFS+ -volname \"%s\" %s", bin_file_path, disk_image_path); + struct stat st; + if (stat(bin_file_path, &st) == -1) { + perror("stat"); + } + + // Get the size of the file in bytes + off_t file_size = st.st_size; + + // Convert the size to gigabytes and add 1 GB + double size_in_gb = (double)file_size / (1024 * 1024 * 1024) + 1.0; + + // Format the size to two decimal places + snprintf(create_disk_command, sizeof(create_disk_command), + "hdiutil create -size %.2fg -fs HFS+ -volname \"%s\" %s", + size_in_gb, bin_file_path, disk_image_path); + system(create_disk_command); chmod(disk_image_path, 0644); @@ -1781,7 +1845,7 @@ void ocreate_isolated_environment(FILE *bin_file, const char *bin_file_path) { if (config.start_config[0] != '\0') { char start_config_path[MAX_PATH_LEN]; snprintf(start_config_path, sizeof(start_config_path), "%s/%s", container_root, config.start_config); - execute_start_config(start_config_path); + execute_start_config(start_config_path, container_root); } char command[MAX_COMMAND_LEN]; @@ -1893,7 +1957,7 @@ void ocreate_isolated_environment(FILE *bin_file, const char *bin_file_path) { goto stop_loop; } else { - execute_command(command); + execute_command(command, container_root); } printf("\n"); @@ -2751,7 +2815,6 @@ char* fetch_latest_version(void) { return latest_version; } -// Add this function to compare version strings int compare_versions(const char* v1, const char* v2) { int a, b, c, d, e, f; sscanf(v1, "%d.%d.%d", &a, &b, &c); @@ -3010,8 +3073,6 @@ int main(int argc, char *argv[]) { fclose(bin_file); free(latest_bin_file_path); - } else if (strcmp(argv[1], "--version") == 0) { - printf("Osxiec version %s\n", VERSION); } else if (strcmp(argv[1], "-pull") == 0) { if (argc != 3) { printf("Usage: %s -pull \n", argv[0]); @@ -3127,13 +3188,13 @@ int main(int argc, char *argv[]) { printf("Detaches container from /Volumes\n"); printf(" -extract \n"); printf("Extracts a container file\n"); - printf(" --version\n"); - printf("Prints the version of osxiec\n"); printf(" -help\n"); printf("Prints this help message\n"); - printf(" -check-for-update\n"); - printf("Checks for updates\n"); - } else if (strcmp(argv[1], "-check-for-update") == 0) { + printf(" --version\n"); + printf("Checks for updates and the current version\n"); + printf(" -update\n"); + printf("Checks for updates and updates the current version\n"); + } else if (strcmp(argv[1], "--version") == 0) { char* latest_version = fetch_latest_version(); if (latest_version) { int comparison = compare_versions(VERSION, latest_version); @@ -3147,6 +3208,87 @@ int main(int argc, char *argv[]) { free(latest_version); } else { printf("Failed to check for updates. Please check your internet connection.\n"); + printf("Your current version: %s\n", VERSION); + } + } else if (strcmp(argv[1], "-update") == 0) { + char* latest_version = fetch_latest_version(); + if (latest_version) { + int comparison = compare_versions(VERSION, latest_version); + if (comparison < 0) { + printf("An update is available. Latest version: %s\n", latest_version); + printf("Your current version: %s\n", VERSION); + if (strcmp(OSXIEC_ARCHITECTURE, "arm64") == 0) { + char update_command[MAX_COMMAND_LEN]; + sprintf(update_command, "curl -L -o osxiec_cli.tar.gz https://github.com/Okerew/osxiec/releases/download/%s/osxiec_cli.tar.gz", latest_version); + system(update_command); + system("tar -xvzf osxiec_cli.tar.gz"); + const char *path = "osxiec_cli"; + + if (chdir(path) != 0) { + perror("chdir() to 'osxiec_cli' failed"); + return 1; + } + + system("sudo sh install.sh"); + } + if (strcmp(OSXIEC_ARCHITECTURE, "86_64") == 0) { + char update_command[MAX_COMMAND_LEN]; + sprintf(update_command, "curl -L -o osxiec_cli_86_64.tar.gz https://github.com/Okerew/osxiec/releases/download/%s/osxiec_cli.tar.gz", latest_version); + system(update_command); + system("tar -xvzf osxiec_cli_86_64.tar.gz"); + const char *path = "osxiec_cli_86_64"; + + if (chdir(path) != 0) { + perror("chdir() to 'osxiec_cli' failed"); + return 1; + } + + system("sudo sh install.sh"); + } + else { + printf("There was some error while updating. Please visit https://github.com/Okerew/osxiec/releases/latest to update.\n"); + } + } else if (comparison == 0) { + printf("You are running the latest version (%s).\n", VERSION); + } + free(latest_version); + } else { + printf("Failed to check for updates. Please check your internet connection.\n"); + printf("Your current version: %s\n", VERSION); + } + } else if (strcmp(argv[1], "-api") == 0) { + if (strcmp(argv[2], "execute_command") == 0) { + execute_command(argv[3], argv[4]); + } + else if(strcmp(argv[2], "copy_file") == 0) { + copy_file(argv[3], argv[4], argv[5]); + } + else if(strcmp(argv[2], "execute_script_file") == 0) { + execute_script_file(argv[3]); + } + else if (strcmp(argv[2], "get_ip_address") == 0) { + char* ip_address = get_ip_address(); + printf("%s\n", ip_address); + } + else if (strcmp(argv[2], "isbase64") == 0) { + int value_is_base64 = is_base64(argv[3]); + printf("%d\n", value_is_base64); + } + else if (strcmp(argv[2], "find_latest_bin_file") == 0) { + char* latest_bin_file = find_latest_bin_file(argv[3]); + printf("%s\n", latest_bin_file); + } + else if (strcmp(argv[2], "create_directories") == 0) { + create_directories(argv[3]); + } + else if (strcmp(argv[2], "execute_start_config") == 0) { + execute_start_config(argv[3], argv[4]); + } + else if (strcmp(argv[2], "start_network_listener") == 0) { + start_network_listener(argv[1]); + } + else { + printf("This futures is not accessible in the api\n"); } } else { fprintf(stderr, "Unknown command: %s\n", argv[1]); From 384b2df1e009a4402d8f888792e0e21f37f921ea Mon Sep 17 00:00:00 2001 From: Okerew <93822247+Okerew@users.noreply.github.com> Date: Tue, 27 Aug 2024 12:01:48 +0200 Subject: [PATCH 04/23] Update README.md --- README.md | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index c13746b..2e00e89 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,10 @@ brew install readline ```sh brew install json-c@0.17 ``` + +**Xcode** +https://apps.apple.com/us/app/xcode/id497799835?mt=12 + ### Build Dependencies **Ninja for building** ```sh @@ -37,10 +41,30 @@ brew install ninja ``` sh brew install cmake ``` -**Xcode** -https://apps.apple.com/us/app/xcode/id497799835?mt=12 ## Installation +**Quick way with command line** + +Arm architecture +```sh +curl -L -o osxiec_cli.tar.gz https://github.com/Okerew/osxiec/releases/download/%s/osxiec_cli.tar.gz && tar -xvzf osxiec_cli.tar.gz && sudo sh install.sh +``` +replace %s with the latest version +______ + +86_64 architecture +```sh +curl -L -o osxiec_cli_86_64.tar.gz https://github.com/Okerew/osxiec/releases/download/%s/osxiec_cli_86_64.tar.gz && tar -xvzf osxiec_cli_86_64.tar.gz && sudo sh install.sh +``` +replace %s with the latest version +____ +Gui +```sh +curl -L -o osxiec_gui.tar.gz https://github.com/Okerew/osxiec/releases/download/%s/osxiec_gui.tar.gz && sudo cp osxiec_gui.tar.gz /Applications +``` +replace %s with the latest version + +______ 1. **Download the Release**: Download the `osxiec_cli.tar.gz` and `osxiec_gui.tar.gz` if you want a gui app file from the releases section. 2. **Extract the Archive**: @@ -83,7 +107,7 @@ sudo osxiec -run {some_name.bin} {network_name} -port {PORT_NUMBER} ``` port argument is optional -**Version** checks the current version +**Version** checks the current version, updates ```sh osxiec --version ``` @@ -153,11 +177,6 @@ osxiec -convert-to-oci {bin_file_path} {output_path} {arch} {author} sudo osxiec -craft {directory_path} {bin_input_file} {output_file} {start_config_file} {container_config_file} ``` -**Check for update** checks for update -```sh -osxiec -check-for-update -``` - **Start** starts a container a stopped container ```sh osxiec -start {volume_name} {network} -port {PORT_NUMBER} @@ -169,6 +188,15 @@ port is optional osxiec -ostart {volume_name} ``` +**Api** an api that exposes some more functions of the cli +```sh +osxiec -api {argument} +``` + +**Update** checks for updates and updates +```sh +sudo osxiec -update +``` ## Creating a container Make sure to include any dependencies or executables you can obtain these by searching for where a dependency or executable is located and copying it along with it's dependencies. From 0e3a9fa8d0d8672f90c9c4299b722a4038d63f22 Mon Sep 17 00:00:00 2001 From: Okerew <93822247+Okerew@users.noreply.github.com> Date: Tue, 27 Aug 2024 12:03:55 +0200 Subject: [PATCH 05/23] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2e00e89..f6d1a30 100644 --- a/README.md +++ b/README.md @@ -47,14 +47,14 @@ brew install cmake Arm architecture ```sh -curl -L -o osxiec_cli.tar.gz https://github.com/Okerew/osxiec/releases/download/%s/osxiec_cli.tar.gz && tar -xvzf osxiec_cli.tar.gz && sudo sh install.sh +curl -L -o osxiec_cli.tar.gz https://github.com/Okerew/osxiec/releases/download/%s/osxiec_cli.tar.gz && tar -xvzf osxiec_cli.tar.gz && cd osxiec_cli && sudo sh install.sh ``` replace %s with the latest version ______ 86_64 architecture ```sh -curl -L -o osxiec_cli_86_64.tar.gz https://github.com/Okerew/osxiec/releases/download/%s/osxiec_cli_86_64.tar.gz && tar -xvzf osxiec_cli_86_64.tar.gz && sudo sh install.sh +curl -L -o osxiec_cli_86_64.tar.gz https://github.com/Okerew/osxiec/releases/download/%s/osxiec_cli_86_64.tar.gz && tar -xvzf osxiec_cli_86_64.tar.gz && cd osxiec_cli_86_64 && sudo sh install.sh ``` replace %s with the latest version ____ From 2511242ca661c70ee9c1dd5dcdb5daec110b1b9f Mon Sep 17 00:00:00 2001 From: Okerew <93822247+Okerew@users.noreply.github.com> Date: Sat, 21 Sep 2024 12:48:56 +0200 Subject: [PATCH 06/23] Update README.md --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f6d1a30..6372c41 100644 --- a/README.md +++ b/README.md @@ -179,13 +179,13 @@ sudo osxiec -craft {directory_path} {bin_input_file} {output_file} {start_config **Start** starts a container a stopped container ```sh -osxiec -start {volume_name} {network} -port {PORT_NUMBER} +sudo osxiec -start {volume_name} {network} -port {PORT_NUMBER} ``` port is optional **Ostart** starts a container a stopped container in offline mode ```sh -osxiec -ostart {volume_name} +sudo osxiec -ostart {volume_name} ``` **Api** an api that exposes some more functions of the cli @@ -197,6 +197,11 @@ osxiec -api {argument} ```sh sudo osxiec -update ``` + +**Add plugin** adds a plugin +```sh +sudo osxiec -add_plugin {path_to_source.c} +``` ## Creating a container Make sure to include any dependencies or executables you can obtain these by searching for where a dependency or executable is located and copying it along with it's dependencies. From 7ddde7e2c4b4e06a04353ebab0114c7d2abbbf1b Mon Sep 17 00:00:00 2001 From: Okerew <93822247+Okerew@users.noreply.github.com> Date: Sat, 21 Sep 2024 12:49:42 +0200 Subject: [PATCH 07/23] Update osxiec.c --- osxiec.c | 147 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 134 insertions(+), 13 deletions(-) diff --git a/osxiec.c b/osxiec.c index a439091..f2e175f 100644 --- a/osxiec.c +++ b/osxiec.c @@ -18,13 +18,15 @@ #include #include "/opt/homebrew/Cellar/json-c/0.17/include/json-c/json.h" #include +#include +#define OSXIEC_ARCHITECTURE "arm64" #define MAX_COMMAND_LEN 1024 #define MAX_PATH_LEN 256 #define MAX_FILE_SIZE 1024*1024*1024 // 1 GB #define MAX_FILES 2147483648 #define PORT 3000 -#define MAX_CLIENTS 5 +#define MAX_CLIENTS 15 #define DEBUG_NONE 0 #define DEBUG_STEP 1 #define DEBUG_BREAK 2 @@ -37,8 +39,7 @@ #define MAX_MEMORY_LIMIT 2147483648 // 2 GB max memory limit #define MAX_HISTORY_LEN 100 #define MAX_LINE_LEN 1024 -#define VERSION "v0.71" -#define OSXIEC_ARCHITECTURE "arm64" +#define VERSION "v0.72" int port = PORT; @@ -1321,7 +1322,7 @@ void navigate_history(char *command, int *command_index, int *cursor_pos, int di volatile sig_atomic_t stop_thread = 0; -void signal_handler(int signum) { +void signal_handler() { stop_thread = 1; } @@ -2816,12 +2817,83 @@ char* fetch_latest_version(void) { } int compare_versions(const char* v1, const char* v2) { - int a, b, c, d, e, f; - sscanf(v1, "%d.%d.%d", &a, &b, &c); - sscanf(v2, "%d.%d.%d", &d, &e, &f); - if (a != d) return a - d; - if (b != e) return b - e; - return c - f; + return strcmp(v1, v2) == 0 ? 0 : -1; +} + + +int add_plugin(const char* plugin_source) { + // Get the user's home directory + const char* home_dir = getenv("HOME"); + if (home_dir == NULL) { + struct passwd* pwd = getpwuid(getuid()); + if (pwd == NULL) { + fprintf(stderr, "Unable to determine home directory\n"); + return EXIT_FAILURE; + } + home_dir = pwd->pw_dir; + } + + // Define the plugin directory in the user's home + char plugin_dir[MAX_PATH_LEN]; + snprintf(plugin_dir, sizeof(plugin_dir), "%s/.osxiec/plugins", home_dir); + char compile_command[1024]; + char link_command[1024]; + char plugin_name[256]; + char* dot_pos = strrchr(plugin_source, '.'); + + if (dot_pos == NULL) { + fprintf(stderr, "Invalid plugin source file name\n"); + return -1; + } + + // Extract plugin name without extension + strncpy(plugin_name, plugin_source, dot_pos - plugin_source); + plugin_name[dot_pos - plugin_source] = '\0'; + + // Compile the plugin as an object file + snprintf(compile_command, sizeof(compile_command), + "gcc -c -fPIC -o %s.o %s", plugin_name, plugin_source); + + if (system(compile_command) != 0) { + fprintf(stderr, "Failed to compile plugin\n"); + return -1; + } + + // Get the path of the current executable + char executable_path[PATH_MAX]; + uint32_t size = sizeof(executable_path); + if (_NSGetExecutablePath(executable_path, &size) != 0) { + fprintf(stderr, "Failed to get executable path\n"); + return -1; + } + + // Link the plugin with the main executable + snprintf(link_command, sizeof(link_command), + "gcc -dynamiclib -o lib%s.dylib %s.o -undefined dynamic_lookup", + plugin_name, plugin_name); + + if (system(link_command) != 0) { + fprintf(stderr, "Failed to create dynamic library from plugin\n"); + return -1; + } + + // Move the dynamic library to the plugin directory + char move_command[1024]; + snprintf(move_command, sizeof(move_command), + "mv lib%s.dylib %s", plugin_name, plugin_dir); + + if (system(move_command) != 0) { + fprintf(stderr, "Failed to move plugin to plugin directory\n"); + return -1; + } + + // Clean up the object file + remove(plugin_name); + + printf("Plugin '%s' has been successfully compiled and moved to the plugin directory.\n", plugin_name); + printf("To use the plugin, you need to restart the program.\n"); + + return 0; } int main(int argc, char *argv[]) { @@ -2904,6 +2976,10 @@ int main(int argc, char *argv[]) { fprintf(stderr, "This program must be run as root. Try using sudo.\n"); return EXIT_FAILURE; } + if (geteuid() != 0) { + fprintf(stderr, "This program must be run as root. Try using sudo.\n"); + return EXIT_FAILURE; + } const char *start_config_file = (argc > 5) ? argv[5] : NULL; const char *container_config_file = (argc > 6) ? argv[6] : NULL; @@ -2911,7 +2987,11 @@ int main(int argc, char *argv[]) { printf("Directory contents containerized into '%s'.\n", argv[4]); } else if (strcmp(argv[1], "-oexec") == 0) { if (argc < 3) { - fprintf(stderr, "Usage for execute: %s -execute [-port ]\n", argv[0]); + fprintf(stderr, "Usage for oexec: %s -oexec \n", argv[0]); + return EXIT_FAILURE; + } + if (geteuid() != 0) { + fprintf(stderr, "This program must be run as root. Try using sudo.\n"); return EXIT_FAILURE; } @@ -2928,6 +3008,10 @@ int main(int argc, char *argv[]) { fprintf(stderr, "Usage: %s -network [vlan_id]\n", argv[0]); return EXIT_FAILURE; } + if (geteuid() != 0) { + fprintf(stderr, "This program must be run as root. Try using sudo.\n"); + return EXIT_FAILURE; + } if (strcmp(argv[2], "create") == 0) { if (argc < 5) { @@ -2951,6 +3035,10 @@ int main(int argc, char *argv[]) { fprintf(stderr, "Usage: %s -run [-port ]\n", argv[0]); return EXIT_FAILURE; } + if (geteuid() != 0) { + fprintf(stderr, "This program must be run as root. Try using sudo.\n"); + return EXIT_FAILURE; + } for (int i = 4; i < argc; i++) { if (strcmp(argv[i], "-port") == 0 && i + 1 < argc) { @@ -2979,6 +3067,10 @@ int main(int argc, char *argv[]) { fprintf(stderr, "Usage: %s -start [-port ]\n", argv[0]); return EXIT_FAILURE; } + if (geteuid() != 0) { + fprintf(stderr, "This program must be run as root. Try using sudo.\n"); + return EXIT_FAILURE; + } for (int i = 4; i < argc; i++) { if (strcmp(argv[i], "-port") == 0 && i + 1 < argc) { @@ -3036,6 +3128,10 @@ int main(int argc, char *argv[]) { fprintf(stderr, "Usage: %s -ostart \n", argv[0]); return EXIT_FAILURE; } + if (geteuid() != 0) { + fprintf(stderr, "This program must be run as root. Try using sudo.\n"); + return EXIT_FAILURE; + } // Find the latest binary file in the specified volume const char *volume_name = argv[2]; @@ -3110,6 +3206,10 @@ int main(int argc, char *argv[]) { fprintf(stderr, "Usage: %s -deploy [-port ]\n", argv[0]); return EXIT_FAILURE; } + if (geteuid() != 0) { + fprintf(stderr, "This program must be run as root. Try using sudo.\n"); + return EXIT_FAILURE; + } int deploy_port = 0; for (int i = 3; i < argc; i++) { if (strcmp(argv[i], "-port") == 0 && i + 1 < argc) { @@ -3150,6 +3250,23 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } extract_container(argv[2], argv[3]); + } else if (strcmp(argv[1], "-add_plugin") == 0) { + if (argc != 3) { + fprintf(stderr, "Usage: %s -add_plugin \n", argv[0]); + return EXIT_FAILURE; + } + if (geteuid() != 0) { + fprintf(stderr, "This program must be run as root. Try using sudo.\n"); + return EXIT_FAILURE; + } + + const char* plugin_source = argv[2]; + if (add_plugin(plugin_source) != 0) { + fprintf(stderr, "Failed to add plugin: %s\n", plugin_source); + return EXIT_FAILURE; + } + printf("Plugin added successfully. Please restart the program.\n"); + return EXIT_SUCCESS; // Exit after adding plugin } else if (strcmp(argv[1], "-help") == 0) { printf("Available commands:\n"); printf(" -contain \n"); @@ -3194,7 +3311,9 @@ int main(int argc, char *argv[]) { printf("Checks for updates and the current version\n"); printf(" -update\n"); printf("Checks for updates and updates the current version\n"); - } else if (strcmp(argv[1], "--version") == 0) { + printf(" -add_plugin \n"); + printf("Adds a plugin\n"); + } else if (argc > 1 && strcmp(argv[1], "--version") == 0) { char* latest_version = fetch_latest_version(); if (latest_version) { int comparison = compare_versions(VERSION, latest_version); @@ -3204,6 +3323,8 @@ int main(int argc, char *argv[]) { printf("Please visit https://github.com/Okerew/osxiec/releases/latest to update.\n"); } else if (comparison == 0) { printf("You are running the latest version (%s).\n", VERSION); + } else { + printf("Your version (%s) is newer than the latest known version (%s).\n", VERSION, latest_version); } free(latest_version); } else { @@ -3288,7 +3409,7 @@ int main(int argc, char *argv[]) { start_network_listener(argv[1]); } else { - printf("This futures is not accessible in the api\n"); + printf("This feature is not accessible in the api\n"); } } else { fprintf(stderr, "Unknown command: %s\n", argv[1]); From efa5c7a538091c5f054cb7f1ada7d0f5695048c2 Mon Sep 17 00:00:00 2001 From: Okerew <93822247+Okerew@users.noreply.github.com> Date: Sat, 21 Sep 2024 12:49:56 +0200 Subject: [PATCH 08/23] Update CMakeLists.txt --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 36f078b..24130c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ add_executable(osxiec plugin_manager/plugin_manager.c osxiec_script/osxiec_script.h osxiec_script/osxiec_script.c - osxiec.h + api_for_osxiec_script.h ) # Find and link CURL From fe8f26f4e0833af6f1f1bcc8ccfc09764e953594 Mon Sep 17 00:00:00 2001 From: Okerew <93822247+Okerew@users.noreply.github.com> Date: Sat, 21 Sep 2024 12:50:16 +0200 Subject: [PATCH 09/23] Create api_for_osxiec_script.h --- api_for_osxiec_script.h | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 api_for_osxiec_script.h diff --git a/api_for_osxiec_script.h b/api_for_osxiec_script.h new file mode 100644 index 0000000..8e2fd3d --- /dev/null +++ b/api_for_osxiec_script.h @@ -0,0 +1,8 @@ +#ifndef OSXIEC_H +#define OSXIEC_H + +void execute_command(const char *command); + +void scale_container_resources(int soft_limit, int hard_limit, int cpu_priority); + +#endif //OSXIEC_H From 2284b42d952a5ef0ca910d759e1319de511fad82 Mon Sep 17 00:00:00 2001 From: Okerew <93822247+Okerew@users.noreply.github.com> Date: Sat, 21 Sep 2024 12:50:27 +0200 Subject: [PATCH 10/23] Update osxiec.h --- osxiec.h | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 2 deletions(-) diff --git a/osxiec.h b/osxiec.h index b063617..84750da 100644 --- a/osxiec.h +++ b/osxiec.h @@ -1,7 +1,99 @@ #ifndef OSXIEC_H #define OSXIEC_H +#include +#include +#include -void execute_command(const char *command); +#define MAX_PATH_LEN 256 +typedef struct { + char name[MAX_PATH_LEN]; + size_t size; + char *data; +} File; -void scale_container_resources(int soft_limit, int hard_limit, int cpu_priority); +#define MAX_CLIENTS 15 + +typedef struct { + char name[MAX_PATH_LEN]; + long memory_soft_limit; + long memory_hard_limit; + int cpu_priority; + char network_mode[20]; + uid_t container_uid; + gid_t container_gid; + char network_name[MAX_PATH_LEN]; + int vlan_id; + char start_config[MAX_PATH_LEN]; +} ContainerConfig; + +typedef struct { + char name[MAX_PATH_LEN]; + int vlan_id; + int num_containers; + char container_names[MAX_CLIENTS][MAX_PATH_LEN]; + char container_ips[MAX_CLIENTS][16]; +} ContainerNetwork; + +void execute_command(const char *command, const char *container_root); +void containerize_directory_with_bin_file(const char *dir_path, const char *input_bin_file, const char *output_file, const char *start_config_file, const char *container_config_file); +void containerize_directory(const char *dir_path, const char *output_file, const char *start_config_file, const char *container_config_file); +void read_config_file(const char *filename, ContainerConfig *config); +void extract_container(const char *osxiec_file, const char *output_dir); +void security_scan(const char *bin_file); +ContainerNetwork load_container_network(const char *name); +void deploy_container(const char *config_file, int deploy_port); +void apply_resource_limits(const ContainerConfig *config); +void *monitor_memory_usage(void *arg); +void setup_pf_rules(ContainerNetwork *network); +void create_and_save_container_network(const char *name, int vlan_id); +void remove_container_network(const char *name); +void auto_scale_resources(const ContainerConfig *config); +void start_auto_scaling(ContainerConfig *config); +void handle_client(int client_socket, const char *container_root); +void start_network_listener(const char *container_root); +void create_isolated_environment(FILE *bin_file, const char *bin_file_path, ContainerNetwork *network); +void ocreate_isolated_environment(FILE *bin_file, const char *bin_file_path); +static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp); +void search(const char *term); +void download_file(const char* file_name); +void upload_file(const char *filename, const char *username, const char *password, const char *description); +void convert_to_docker(const char *osxiec_file, const char *output_dir, const char *base_image, const char *custom_dockerfile); +void clean_container_dmgs(); +void convert_to_oci(const char *osxiec_file, const char *output_dir, const char *arch, const char *author, const char *created); +char* find_latest_bin_file(const char *volume_name); +int copy_file(const char *source, const char *destination_folder, const char *new_file_name); +size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp); +char* fetch_latest_version(void); +int compare_versions(const char* v1, const char* v2); +int add_plugin(const char* plugin_source); +int copy_file(const char *source, const char *destination_folder, const char *new_file_name); +char* find_latest_bin_file(const char *volume_name); +void create_directory_if_needed(const char *path); +void handle_signal(int sig); +void *logger_thread(void *arg); +void signal_handler(); +void navigate_history(char *command, int *command_index, int *cursor_pos, int direction); +void add_to_history(const char *command); +void clear_line(); +void move_cursor_right(int n); +void move_cursor_left(int n); +void set_terminal_canonical_mode(); +void set_terminal_raw_mode(); +void detach_container_images(const char *volume_name); +void print_current_resource_usage(ContainerConfig *config); +void start_auto_scaling(ContainerConfig *config); +double get_cpu_usage(); +void create_directories(const char *file_path); +void create_shared_folder(); +void handle_script_file(const char *filename); +void handle_script_command(const char *script_content); +void scale_container_resources(long memory_soft_limit, long memory_hard_limit, int cpu_priority); +void enable_container_communication(ContainerNetwork *network); +void setup_network_isolation(ContainerConfig *config, ContainerNetwork *network); +char *get_ip_address(); +void execute_start_config(const char *config_file, const char *container_root); +int is_subpath(const char *path, const char *base); +void handle_debug_command(char *command); +void print_container_state(); +void update_container_state(); #endif //OSXIEC_H From 9a7861fb7d452b693e377720444ecb4616c51790 Mon Sep 17 00:00:00 2001 From: Okerew <93822247+Okerew@users.noreply.github.com> Date: Sat, 21 Sep 2024 12:51:57 +0200 Subject: [PATCH 11/23] Update sample_plugin.c --- samples/sample_plugin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/sample_plugin.c b/samples/sample_plugin.c index aeda048..169c996 100644 --- a/samples/sample_plugin.c +++ b/samples/sample_plugin.c @@ -27,4 +27,4 @@ OsxiecPlugin osxiec_plugin = { .initialize = sample_initialize, .execute = sample_execute, .cleanup = sample_cleanup -}; \ No newline at end of file +}; From b23d3a9392a0727929bfc7ee675054cbb2e165d2 Mon Sep 17 00:00:00 2001 From: Okerew <93822247+Okerew@users.noreply.github.com> Date: Sat, 21 Sep 2024 12:52:45 +0200 Subject: [PATCH 12/23] Update sample_plugin.c --- samples/sample_plugin.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/samples/sample_plugin.c b/samples/sample_plugin.c index 169c996..26f9be1 100644 --- a/samples/sample_plugin.c +++ b/samples/sample_plugin.c @@ -1,10 +1,12 @@ // sample_plugin.c -#include "plugin.h" +#include "plugin_manager/plugin.h" #include #include +#include "osxiec.h" static int sample_initialize(void) { printf("Sample plugin initialized\n"); + execute_command("echo hello", ""); return 0; } From a996ff1b068d8969b1ccf91a4dc7a3eec71e07a3 Mon Sep 17 00:00:00 2001 From: Okerew <93822247+Okerew@users.noreply.github.com> Date: Sat, 21 Sep 2024 12:57:39 +0200 Subject: [PATCH 13/23] Update install.sh --- scripts/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/install.sh b/scripts/install.sh index a2225ff..33168e8 100644 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -6,7 +6,7 @@ if [ "$EUID" -ne 0 ] exit fi -files_to_copy=("osxiec" "osxiec_deploy_multiple.sh") +files_to_copy=("osxiec" "osxiec_deploy_multiple.sh" "osxiec.h") # Delete existing files and copy the new ones to /usr/local/bin for file in "${files_to_copy[@]}" From 3d4b5987da4dc9175b5515206ea759bc3c468ad1 Mon Sep 17 00:00:00 2001 From: Okerew <93822247+Okerew@users.noreply.github.com> Date: Sat, 4 Jan 2025 22:20:33 +0100 Subject: [PATCH 14/23] Update osxiec.c --- osxiec.c | 461 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 458 insertions(+), 3 deletions(-) diff --git a/osxiec.c b/osxiec.c index f2e175f..817e85f 100644 --- a/osxiec.c +++ b/osxiec.c @@ -39,7 +39,7 @@ #define MAX_MEMORY_LIMIT 2147483648 // 2 GB max memory limit #define MAX_HISTORY_LEN 100 #define MAX_LINE_LEN 1024 -#define VERSION "v0.72" +#define VERSION "v0.73" int port = PORT; @@ -2019,6 +2019,346 @@ void ocreate_isolated_environment(FILE *bin_file, const char *bin_file_path) { } } +void gcreate_isolated_environment(FILE *bin_file, const char *bin_file_path) { + signal(SIGTERM, handle_signal); + signal(SIGINT, handle_signal); + signal(SIGSEGV, handle_signal); + ContainerConfig config; + fread(&config, sizeof(ContainerConfig), 1, bin_file); + + int num_files; + fread(&num_files, sizeof(int), 1, bin_file); + + char shared_folder_path[] = "/Volumes/SharedContainer"; + mkdir(shared_folder_path, 0755); + + char disk_image_path[MAX_PATH_LEN]; + snprintf(disk_image_path, sizeof(disk_image_path), "/tmp/container_disk_%d.dmg", getpid()); + + // Assign the volume name as the bin_file_path + char create_disk_command[MAX_COMMAND_LEN]; + struct stat st; + if (stat(bin_file_path, &st) == -1) { + perror("stat"); + } + + // Get the size of the file in bytes + off_t file_size = st.st_size; + + // Convert the size to gigabytes and add 1 GB + double size_in_gb = (double)file_size / (1024 * 1024 * 1024) + 1.0; + + // Format the size to two decimal places + snprintf(create_disk_command, sizeof(create_disk_command), + "hdiutil create -size %.2fg -fs HFS+ -volname \"%s\" %s", + size_in_gb, bin_file_path, disk_image_path); + + system(create_disk_command); + + chmod(disk_image_path, 0644); + + char mount_command[MAX_COMMAND_LEN]; + snprintf(mount_command, sizeof(mount_command), "hdiutil attach %s", disk_image_path); + system(mount_command); + + // Use bin_file_path as the root volume name + char container_root[MAX_PATH_LEN]; + snprintf(container_root, sizeof(container_root), "/Volumes/%s", bin_file_path); + + char shared_mount_point[MAX_PATH_LEN]; + snprintf(shared_mount_point, sizeof(shared_mount_point), "%s/shared", container_root); + symlink(shared_folder_path, shared_mount_point); + + for (int i = 0; i < num_files; i++) { + File file; + fread(file.name, sizeof(char), MAX_PATH_LEN, bin_file); + fread(&file.size, sizeof(size_t), 1, bin_file); + + file.data = malloc(file.size); + if (file.data == NULL) { + perror("Error allocating memory for file data"); + exit(EXIT_FAILURE); + } + + fread(file.data, 1, file.size, bin_file); + + char file_path[MAX_PATH_LEN]; + snprintf(file_path, sizeof(file_path), "%s/%s", container_root, file.name); + + create_directories(file_path); + + FILE *out_file = fopen(file_path, "wb"); + if (out_file == NULL) { + perror("Error creating file in container"); + exit(EXIT_FAILURE); + } + + fwrite(file.data, 1, file.size, out_file); + fclose(out_file); + free(file.data); + + chmod(file_path, 0755); + } + + chmod(container_root, 0755); + + if (chdir(container_root) != 0) { + perror("Failed to change to container root directory"); + exit(1); + } + + if (setgid(config.container_gid) != 0) { + perror("Failed to set group ID"); + exit(1); + } + + if (setuid(config.container_uid) != 0) { + perror("Failed to set user ID"); + exit(1); + } + + apply_resource_limits(&config); + + char sandbox_profile[2048]; + snprintf(sandbox_profile, sizeof(sandbox_profile), + "(version 1)" + "(allow default)" // Change default policy to allow + "(deny file-read* (subpath \"/Applications\"))" // Deny access to /Applications + "(deny file-read* (subpath \"/Users\"))" // Deny access to /Users + "(deny file-read* (subpath \"/sbin\"))" // Deny acces to sbin + "(allow process-fork)" + "(allow file-read*)" + "(allow file-write* (subpath \"%s\"))" + "(allow file-read* (subpath \"%s\"))" + "(allow file-read* (literal \"%s\"))" + "(allow file-read* (subpath \"/usr/lib\"))" + "(allow file-read* (subpath \"/usr/bin\"))" + "(allow file-read* (subpath \"/bin\"))" + "(allow file-read* (subpath \"/System\"))" + "(allow file-read* (subpath \"%s\"))" + "(allow file-read* (subpath \"/Applications/Xcode.app\"))" + "(allow file-write* (subpath \"%s\"))" + "(allow sysctl-read)" + "(allow mach-lookup)" + "(allow network-outbound (remote ip))" + "(allow network-inbound (local ip))" + "(allow process-exec (subpath \"/usr/bin\"))" + "(allow process-exec (subpath \"/Applications/Xcode.app\"))" + "(allow process-exec (subpath \"/bin\"))" + "(deny file-read* (subpath \"/Library\"))" // Deny access to /Library + "(allow file-read* (subpath \"/Library/Audio\"))" + "(allow file-read* (subpath \"/Library/Caches\"))" + "(allow file-read* (subpath \"/Library/Developer\"))" + "(allow file-read* (subpath \"/Library/DriverExtensions\"))" + "(allow file-read* (subpath \"/Library/Extensions\"))" + "(allow file-read* (subpath \"/Library/Fonts\"))" + "(allow file-read* (subpath \"/Library/Frameworks\"))" + "(allow file-read* (subpath \"/Library/Graphics\"))" + "(allow file-read* (subpath \"/Library/GPUBundles\"))" + "(allow file-read* (subpath \"/Library/Image Capture\"))" + "(allow file-read* (subpath \"/Library/Input Methods\"))" + "(allow file-read* (subpath \"/Library/LaunchAgents\"))" + "(allow file-read* (subpath \"/Library/KernelCollections\"))" + "(allow file-read* (subpath \"/Library/LaunchDaemons\"))" + "(allow file-read* (subpath \"/Library/Logs\"))" + "(allow file-read* (subpath \"/Library/OSAnalytics\"))" + "(allow file-read* (subpath \"/Library/Preferences\"))" + "(allow file-read* (subpath \"/Library/Scripts\"))" + "(allow file-read* (subpath \"/Library/Speech\"))" + "(allow file-read* (subpath \"/Library/WebServer\"))" + "(allow process-exec (subpath \"%s\"))", + container_root, container_root, bin_file_path, + shared_mount_point, shared_mount_point, container_root + ); + + + char *error; + if (sandbox_init(sandbox_profile, 0, &error) != 0) { + fprintf(stderr, "sandbox_init failed: %s\n", error); + sandbox_free_error(error); + exit(1); + } + + printf("\n=== Container %s Terminal ===\n", bin_file_path); + printf("Enter commands (type 'exit' to quit, help for help):\n"); + printf("If you just ran the container ignore the first log file error"); + + if (config.start_config[0] != '\0') { + char start_config_path[MAX_PATH_LEN]; + snprintf(start_config_path, sizeof(start_config_path), "%s/%s", container_root, config.start_config); + execute_start_config(start_config_path, container_root); + } + + char command[MAX_COMMAND_LEN]; + int command_index = 0; + int cursor_pos = 0; + + set_terminal_raw_mode(); + + pthread_t logger; + pthread_create(&logger, NULL, logger_thread, &config); + + while (1) { + if (should_exit) { + break; + } + + printf("> "); + fflush(stdout); + + int ch; + while ((ch = getchar()) != EOF) { + if (ch == 27) { // ESC key + getchar(); // Skip the next character + ch = getchar(); // Get the actual key code + if (ch == 'A') { // Up arrow + navigate_history(command, &command_index, &cursor_pos, -1); + } else if (ch == 'B') { // Down arrow + navigate_history(command, &command_index, &cursor_pos, 1); + } else if (ch == 'C') { // Right arrow + if (cursor_pos < command_index) { + move_cursor_right(1); + cursor_pos++; + } + } else if (ch == 'D') { // Left arrow + if (cursor_pos > 0) { + move_cursor_left(1); + cursor_pos--; + } + } + } else if (ch == 127 || ch == 8) { // Backspace or Delete + if (cursor_pos > 0) { + move_cursor_left(1); + clear_line(); + memmove(&command[cursor_pos - 1], &command[cursor_pos], command_index - cursor_pos + 1); + command_index--; + cursor_pos--; + printf("%s", &command[cursor_pos]); + move_cursor_left(command_index - cursor_pos); + } + } else if (ch == '\n' || ch == '\r') { // Enter key + command[command_index] = '\0'; + set_terminal_canonical_mode(); + usleep(10000); // 10ms delay + + if (strcmp(command, "exit") == 0) goto exit_loop; + if (strcmp(command, "debug") == 0) { + debug_mode = DEBUG_STEP; + printf("Entered debug mode. Type 'help' for debug commands.\n"); + } else if (strncmp(command, "scale", 5) == 0) { + long memory_soft_limit, memory_hard_limit; + int cpu_priority; + if (sscanf(command, "scale %ld %ld %d", &memory_soft_limit, &memory_hard_limit, &cpu_priority) == 3) { + scale_container_resources(memory_soft_limit, memory_hard_limit, cpu_priority); + } else { + printf("Usage: scale \n"); + } + } else if (strncmp(command, "xs", 6) == 0) { + char *script_content = command + 7; + handle_script_command(script_content); + } else if (strncmp(command, "osxs", 4) == 0) { + char *filename = command + 5; + while (isspace(*filename)) { + filename++; + } + handle_script_file(filename); + } else if (strcmp(command, "autoscale") == 0) { + start_auto_scaling(&config); + } else if (strcmp(command, "status") == 0) { + print_current_resource_usage(&config); + } else if (strcmp(command, "help") == 0) { + printf("Commands:\n"); + printf(" exit: Exit the container\n"); + printf(" debug: Enter debug mode\n"); + printf(" scale : Set memory limits and CPU priority\n"); + printf(" xs : Execute a script in the container\n"); + printf(" osxs : Execute a script file in the container\n"); + printf(" autoscale: Start automatic resource scaling\n"); + printf(" status: Print current resource usage\n"); + printf(" help: Print this help message\n"); + printf(" stop: Stops the container and saves its state\n"); + } else if (strcmp(command, "stop") == 0) { + printf("Stopping container...\n"); + + // Save the container state + char state_file_path[MAX_PATH_LEN]; + snprintf(state_file_path, sizeof(state_file_path), "%s/%s", container_root, bin_file_path); + FILE *state_file = fopen(state_file_path, "wb"); + if (state_file == NULL) { + perror("Error creating container state file"); + } else { + // Save the container configuration + fwrite(&config, sizeof(ContainerConfig), 1, state_file); + + // Save the environment variables + fwrite(&container_state, sizeof(ContainerState), 1, state_file); + fclose(state_file); + printf("Container state saved to %s\n", state_file_path); + } + + goto stop_loop; + } else { + execute_command(command, container_root); + } + + printf("\n"); + add_to_history(command); + char log_file_path[MAX_PATH_LEN]; + snprintf(log_file_path, sizeof(log_file_path), "%s/log.txt", container_root); + FILE *log_file = fopen(log_file_path, "a"); + if (log_file == NULL) { + perror("Failed to open log file"); + printf("If you just ran the container ignore this"); + } else { + fprintf(log_file, "\nCommand History:\n"); + for (int i = 0; i < command_index; i++) { + fprintf(log_file, "Command %d: %s\n", i + 1, history.commands[i]); + } + fclose(log_file); + } + command_index = 0; + cursor_pos = 0; + set_terminal_raw_mode(); + printf("> "); + fflush(stdout); + } else { + if (command_index < MAX_COMMAND_LEN - 1) { + memmove(&command[cursor_pos + 1], &command[cursor_pos], command_index - cursor_pos + 1); + command[cursor_pos] = ch; + command_index++; + cursor_pos++; + printf("%s", &command[cursor_pos - 1]); + move_cursor_left(command_index - cursor_pos); + } + } + } + } + +exit_loop: + set_terminal_canonical_mode(); + printf("Container terminated.\n"); + + pthread_cancel(logger); + pthread_join(logger, NULL); + + for (int i = 0; i < container_state.num_env_vars; i++) { + free(container_state.environment_variables[i]); + } + free(container_state.environment_variables); + exit(0); + +stop_loop: + set_terminal_canonical_mode(); + printf("Container stopped.\n"); + pthread_cancel(logger); + pthread_join(logger, NULL); + + // Preserve the environment variables + for (int i = 0; i < container_state.num_env_vars; i++) { + setenv(container_state.environment_variables[i], NULL, 1); + } +} + size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) { size_t written = fwrite(ptr, size, nmemb, stream); return written; @@ -2896,6 +3236,36 @@ int add_plugin(const char* plugin_source) { return 0; } +int remove_plugin(const char* plugin_name) { + // Get the user's home directory + const char* home_dir = getenv("HOME"); + if (home_dir == NULL) { + struct passwd* pwd = getpwuid(getuid()); + if (pwd == NULL) { + fprintf(stderr, "Unable to determine home directory\n"); + return EXIT_FAILURE; + } + home_dir = pwd->pw_dir; + } + + // Define the plugin directory in the user's home + char plugin_dir[MAX_PATH_LEN]; + snprintf(plugin_dir, sizeof(plugin_dir), "%s/.osxiec/plugins", home_dir); + + // Remove the plugin from the plugin directory + char remove_command[1024]; + snprintf(remove_command, sizeof(remove_command), + "rm -f %s/%s.dylib", plugin_dir, plugin_name); + + if (system(remove_command) != 0) { + fprintf(stderr, "Failed to remove plugin\n"); + return -1; + } + + printf("Plugin '%s' has been successfully removed from the plugin directory.\n", plugin_name); + + return 0; +} int main(int argc, char *argv[]) { PluginManager plugin_manager; plugin_manager_init(&plugin_manager); @@ -3003,6 +3373,24 @@ int main(int argc, char *argv[]) { ocreate_isolated_environment(bin_file, argv[2]); fclose(bin_file); + } else if(strcmp(argv[1], "-gexec") == 0) { + if (argc < 3) { + fprintf(stderr, "Usage for gexec: %s -gexec \n", argv[0]); + return EXIT_FAILURE; + } + if (geteuid() != 0) { + fprintf(stderr, "This program must be run as root. Try using sudo.\n"); + return EXIT_FAILURE; + } + + FILE *bin_file = fopen(argv[2], "rb"); + if (bin_file == NULL) { + perror("Error opening binary file"); + return EXIT_FAILURE; + } + + gcreate_isolated_environment(bin_file, argv[2]); + fclose(bin_file); } else if (strcmp(argv[1], "-network") == 0) { if (argc < 4) { fprintf(stderr, "Usage: %s -network [vlan_id]\n", argv[0]); @@ -3167,6 +3555,52 @@ int main(int argc, char *argv[]) { ocreate_isolated_environment(bin_file, volume_name); + fclose(bin_file); + free(latest_bin_file_path); + } else if (strcmp(argv[1], "-gstart") == 0) { + if (argc < 2) { + fprintf(stderr, "Usage: %s -gstart \n", argv[0]); + return EXIT_FAILURE; + } + if (geteuid() != 0) { + fprintf(stderr, "This program must be run as root. Try using sudo.\n"); + return EXIT_FAILURE; + } + + // Find the latest binary file in the specified volume + const char *volume_name = argv[2]; + char *latest_bin_file_path = find_latest_bin_file(volume_name); + + if (latest_bin_file_path == NULL) { + fprintf(stderr, "No valid .bin file found for volume %s\n", volume_name); + return EXIT_FAILURE; + } + + // Copy the latest file to the root of the volume with just the volume name + char dest_folder[MAX_PATH_LEN]; + snprintf(dest_folder, sizeof(dest_folder), "/Volumes/%s", volume_name); + + if (copy_file(latest_bin_file_path, dest_folder, volume_name) != 0) { + fprintf(stderr, "Failed to copy binary file to volume root\n"); + free(latest_bin_file_path); + return EXIT_FAILURE; + } + + // Open the copied binary file + char full_dest_path[MAX_PATH_LEN]; + snprintf(full_dest_path, sizeof(full_dest_path), "%s/%s", dest_folder, volume_name); + + printf("Opening binary file: %s\n", full_dest_path); + FILE *bin_file = fopen(full_dest_path, "rb"); + if (bin_file == NULL) { + perror("Error opening binary file"); + fprintf(stderr, "Failed to open: %s\n", full_dest_path); + free(latest_bin_file_path); + return EXIT_FAILURE; + } + + gcreate_isolated_environment(bin_file, volume_name); + fclose(bin_file); free(latest_bin_file_path); } else if (strcmp(argv[1], "-pull") == 0) { @@ -3267,6 +3701,23 @@ int main(int argc, char *argv[]) { } printf("Plugin added successfully. Please restart the program.\n"); return EXIT_SUCCESS; // Exit after adding plugin + } else if (strcmp(argv[1], "-remove_plugin") == 0) { + if (argc != 3) { + fprintf(stderr, "Usage: %s -remove_plugin \n", argv[0]); + return EXIT_FAILURE; + } + if (geteuid() != 0) { + fprintf(stderr, "This program must be run as root. Try using sudo.\n"); + return EXIT_FAILURE; + } + + const char* plugin_name = argv[2]; + if (remove_plugin(plugin_name) != 0) { + fprintf(stderr, "Failed to remove plugin: %s\n", plugin_name); + return EXIT_FAILURE; + } + printf("Plugin removed successfully. Please restart the program.\n"); + return EXIT_SUCCESS; // Exit after removing plugin } else if (strcmp(argv[1], "-help") == 0) { printf("Available commands:\n"); printf(" -contain \n"); @@ -3275,9 +3726,11 @@ int main(int argc, char *argv[]) { printf("Crafts a container file from a directory and a bin file\n"); printf(" -oexec \n"); printf("Executes a container file in offline mode\n"); - printf(" -start [-port ]\n"); + printf(" -gexec \n"); + printf("Executes a container file wile allowing gui applications (note this is doesn't support online mode and has far limited isolation compared to other modes)\n"); + printf(" -start \n"); printf("Starts a stopped container"); - printf(" -ostart [-port ]\n"); + printf(" -ostart \n"); printf("Starts a stopped container in offline mode"); printf(" -network [vlan_id>\n"); printf("Manages the vlan network\n"); @@ -3313,6 +3766,8 @@ int main(int argc, char *argv[]) { printf("Checks for updates and updates the current version\n"); printf(" -add_plugin \n"); printf("Adds a plugin\n"); + printf(" -remove_plugin \n"); + printf("Removes a plugin\n"); } else if (argc > 1 && strcmp(argv[1], "--version") == 0) { char* latest_version = fetch_latest_version(); if (latest_version) { From 5bd1912fa69336ca4e6e4560c83bc503edd115b8 Mon Sep 17 00:00:00 2001 From: Okerew <93822247+Okerew@users.noreply.github.com> Date: Sat, 4 Jan 2025 22:21:17 +0100 Subject: [PATCH 15/23] Update osxiec.h --- osxiec.h | 1 + 1 file changed, 1 insertion(+) diff --git a/osxiec.h b/osxiec.h index 84750da..b0c8d60 100644 --- a/osxiec.h +++ b/osxiec.h @@ -53,6 +53,7 @@ void handle_client(int client_socket, const char *container_root); void start_network_listener(const char *container_root); void create_isolated_environment(FILE *bin_file, const char *bin_file_path, ContainerNetwork *network); void ocreate_isolated_environment(FILE *bin_file, const char *bin_file_path); +void gcreate_isolated_environment(FILE *bin_file, const char *bin_file_path); static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp); void search(const char *term); void download_file(const char* file_name); From 48fa8c5d47438d944d88723af7ea353a267342f5 Mon Sep 17 00:00:00 2001 From: Okerew <93822247+Okerew@users.noreply.github.com> Date: Sat, 4 Jan 2025 22:24:34 +0100 Subject: [PATCH 16/23] Update README.md --- README.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 6372c41..d4bdb45 100644 --- a/README.md +++ b/README.md @@ -161,7 +161,10 @@ sudo osxiec -deploym {config_file} ```sh sudo osxiec -oexec {bin_file_path} ``` - +**Gexec** executes a container in gui mode +```sh +sudo osxiec -gexec {bin_file_path} +``` **Extract** extracts files and folders from a container ```sh sudo osxiec -extract {bin_file_path} @@ -187,7 +190,10 @@ port is optional ```sh sudo osxiec -ostart {volume_name} ``` - +**Gstart** starts a container a stopped container in gui mode +```sh +sudo osxiec -gstart {volume_name} +``` **Api** an api that exposes some more functions of the cli ```sh osxiec -api {argument} @@ -197,11 +203,6 @@ osxiec -api {argument} ```sh sudo osxiec -update ``` - -**Add plugin** adds a plugin -```sh -sudo osxiec -add_plugin {path_to_source.c} -``` ## Creating a container Make sure to include any dependencies or executables you can obtain these by searching for where a dependency or executable is located and copying it along with it's dependencies. From 1a972018c7b7853cbf42e7ad06b8ab59f62f0bbd Mon Sep 17 00:00:00 2001 From: Okerew <93822247+Okerew@users.noreply.github.com> Date: Tue, 4 Mar 2025 18:43:57 +0100 Subject: [PATCH 17/23] Update osxiec.c --- osxiec.c | 7381 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 4073 insertions(+), 3308 deletions(-) diff --git a/osxiec.c b/osxiec.c index 817e85f..d5e845c 100644 --- a/osxiec.c +++ b/osxiec.c @@ -1,3875 +1,4640 @@ -#include -#include -#include +#include "/opt/homebrew/Cellar/json-c/0.17/include/json-c/json.h" +#include "osxiec_script/osxiec_script.h" +#include "plugin_manager/plugin_manager.h" +#include +#include #include #include -#include +#include +#include #include #include -#include -#include -#include -#include -#include "plugin_manager/plugin_manager.h" #include +#include #include +#include +#include #include -#include "osxiec_script/osxiec_script.h" -#include -#include "/opt/homebrew/Cellar/json-c/0.17/include/json-c/json.h" +#include +#include #include -#include +#include #define OSXIEC_ARCHITECTURE "arm64" #define MAX_COMMAND_LEN 1024 #define MAX_PATH_LEN 256 -#define MAX_FILE_SIZE 1024*1024*1024 // 1 GB +#define MAX_FILE_SIZE 1024 * 1024 * 1024 // 1 GB #define MAX_FILES 2147483648 #define PORT 3000 #define MAX_CLIENTS 15 #define DEBUG_NONE 0 #define DEBUG_STEP 1 #define DEBUG_BREAK 2 -#define CHUNK_SIZE 8192 // 8 KB chunks +#define CHUNK_SIZE 8192 // 8 KB chunks #define SHARED_FOLDER_PATH "/Volumes/SharedContainer" -#define CPU_USAGE_THRESHOLD 80.0 // 80% CPU usage +#define CPU_USAGE_THRESHOLD 80.0 // 80% CPU usage #define MEMORY_USAGE_THRESHOLD 80.0 // 80% of soft limit -#define MAX_CPU_PRIORITY 39 // Maximum nice value -#define MIN_CPU_PRIORITY -20 // Minimum nice value +#define MAX_CPU_PRIORITY 39 // Maximum nice value +#define MIN_CPU_PRIORITY -20 // Minimum nice value #define MAX_MEMORY_LIMIT 2147483648 // 2 GB max memory limit #define MAX_HISTORY_LEN 100 #define MAX_LINE_LEN 1024 #define VERSION "v0.73" +#define MAX_BACKGROUND_THREADS 32 int port = PORT; typedef struct { - char name[MAX_PATH_LEN]; - size_t size; - char *data; + char name[MAX_PATH_LEN]; + size_t size; + char *data; } File; typedef struct { - char source[MAX_PATH_LEN]; - char target[MAX_PATH_LEN]; + char source[MAX_PATH_LEN]; + char target[MAX_PATH_LEN]; } Mount; typedef struct { - char name[MAX_PATH_LEN]; - long memory_soft_limit; - long memory_hard_limit; - int cpu_priority; - char network_mode[20]; - uid_t container_uid; - gid_t container_gid; - char network_name[MAX_PATH_LEN]; - int vlan_id; - char start_config[MAX_PATH_LEN]; + char name[MAX_PATH_LEN]; + long memory_soft_limit; + long memory_hard_limit; + int cpu_priority; + char network_mode[20]; + uid_t container_uid; + gid_t container_gid; + char network_name[MAX_PATH_LEN]; + int vlan_id; + char start_config[MAX_PATH_LEN]; } ContainerConfig; typedef struct { - char name[MAX_PATH_LEN]; - int vlan_id; - int num_containers; - char container_names[MAX_CLIENTS][MAX_PATH_LEN]; - char container_ips[MAX_CLIENTS][16]; + char name[MAX_PATH_LEN]; + int vlan_id; + int num_containers; + char container_names[MAX_CLIENTS][MAX_PATH_LEN]; + char container_ips[MAX_CLIENTS][16]; } ContainerNetwork; int debug_mode = DEBUG_NONE; char *breakpoint = NULL; typedef struct { - char current_directory[MAX_PATH_LEN]; - char last_executed_command[MAX_COMMAND_LEN]; - int num_processes; - long memory_usage; - char network_status[50]; - char **environment_variables; - int num_env_vars; + char current_directory[MAX_PATH_LEN]; + char last_executed_command[MAX_COMMAND_LEN]; + int num_processes; + long memory_usage; + char network_status[50]; + char **environment_variables; + int num_env_vars; } ContainerState; ContainerState container_state = {0}; typedef struct { - char commands[MAX_HISTORY_LEN][MAX_COMMAND_LEN]; - int count; - int current; + char commands[MAX_HISTORY_LEN][MAX_COMMAND_LEN]; + int count; + int current; } CommandHistory; -CommandHistory history = { .count = 0, .current = 0 }; +CommandHistory history = {.count = 0, .current = 0}; int read_files(const char *dir_path, File *files, uid_t uid, gid_t gid) { - DIR *dir; - struct dirent *entry; - char file_path[MAX_PATH_LEN]; - int num_files = 0; - struct stat st; + DIR *dir; + struct dirent *entry; + char file_path[MAX_PATH_LEN]; + int num_files = 0; + struct stat st; + + dir = opendir(dir_path); + if (dir == NULL) { + perror("Error opening directory"); + return -1; + } + + while ((entry = readdir(dir)) != NULL) { + snprintf(file_path, sizeof(file_path), "%s/%s", dir_path, entry->d_name); + + if (stat(file_path, &st) == 0) { + if (S_ISDIR(st.st_mode)) { + // Skip "." and ".." + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + // Recursively read subdirectories + int sub_num_files = read_files(file_path, &files[num_files], uid, gid); + if (sub_num_files < 0) { + closedir(dir); + return -1; + } + num_files += sub_num_files; + } else if (S_ISREG(st.st_mode)) { + if (st.st_size > MAX_FILE_SIZE) { + fprintf(stderr, "File %s is too large (max %d bytes)\n", + entry->d_name, MAX_FILE_SIZE); + continue; + } + + strncpy(files[num_files].name, file_path, MAX_PATH_LEN - 1); + files[num_files].name[MAX_PATH_LEN - 1] = '\0'; + files[num_files].size = st.st_size; + + files[num_files].data = malloc(st.st_size); + if (files[num_files].data == NULL) { + perror("Error allocating memory for file data"); + closedir(dir); + return -1; + } + + FILE *file = fopen(file_path, "rb"); + if (file == NULL) { + perror("Error opening file"); + free(files[num_files].data); + closedir(dir); + return -1; + } - dir = opendir(dir_path); - if (dir == NULL) { - perror("Error opening directory"); - return -1; - } + if (fread(files[num_files].data, 1, st.st_size, file) != st.st_size) { + perror("Error reading file"); + fclose(file); + free(files[num_files].data); + closedir(dir); + return -1; + } - while ((entry = readdir(dir)) != NULL) { - snprintf(file_path, sizeof(file_path), "%s/%s", dir_path, entry->d_name); - - if (stat(file_path, &st) == 0) { - if (S_ISDIR(st.st_mode)) { - // Skip "." and ".." - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) - continue; - // Recursively read subdirectories - int sub_num_files = read_files(file_path, &files[num_files], uid, gid); - if (sub_num_files < 0) { - closedir(dir); - return -1; - } - num_files += sub_num_files; - } else if (S_ISREG(st.st_mode)) { - if (st.st_size > MAX_FILE_SIZE) { - fprintf(stderr, "File %s is too large (max %d bytes)\n", entry->d_name, MAX_FILE_SIZE); - continue; - } - - strncpy(files[num_files].name, file_path, MAX_PATH_LEN - 1); - files[num_files].name[MAX_PATH_LEN - 1] = '\0'; - files[num_files].size = st.st_size; - - files[num_files].data = malloc(st.st_size); - if (files[num_files].data == NULL) { - perror("Error allocating memory for file data"); - closedir(dir); - return -1; - } - - FILE *file = fopen(file_path, "rb"); - if (file == NULL) { - perror("Error opening file"); - free(files[num_files].data); - closedir(dir); - return -1; - } - - if (fread(files[num_files].data, 1, st.st_size, file) != st.st_size) { - perror("Error reading file"); - fclose(file); - free(files[num_files].data); - closedir(dir); - return -1; - } - - fclose(file); - - // Set appropriate permissions - if (chmod(file_path, 0755) != 0) { - perror("Error setting file permissions"); - free(files[num_files].data); - closedir(dir); - return -1; - } - - // Change ownership of the file - if (chown(file_path, uid, gid) != 0) { - perror("Error changing file ownership"); - free(files[num_files].data); - closedir(dir); - return -1; - } - - num_files++; - } + fclose(file); + + // Set appropriate permissions + if (chmod(file_path, 0755) != 0) { + perror("Error setting file permissions"); + free(files[num_files].data); + closedir(dir); + return -1; } + + // Change ownership of the file + if (chown(file_path, uid, gid) != 0) { + perror("Error changing file ownership"); + free(files[num_files].data); + closedir(dir); + return -1; + } + + num_files++; + } } + } - closedir(dir); - return num_files; + closedir(dir); + return num_files; } extern char **environ; void update_container_state() { - // Update current directory - getcwd(container_state.current_directory, MAX_PATH_LEN); - - // Update number of processes - container_state.num_processes = 1; - - // Update memory usage - FILE* file = fopen("/proc/self/status", "r"); - if (file) { - char line[128]; - while (fgets(line, sizeof(line), file)) { - if (strncmp(line, "VmRSS:", 6) == 0) { - sscanf(line + 6, "%ld", &container_state.memory_usage); - break; - } - } - fclose(file); - } + // Update current directory + getcwd(container_state.current_directory, MAX_PATH_LEN); - strcpy(container_state.network_status, "Connected"); + // Update number of processes + container_state.num_processes = 1; - // Update environment variables - for (int i = 0; environ[i] != NULL; i++) { - if (i >= container_state.num_env_vars) { - container_state.environment_variables = realloc(container_state.environment_variables, (i + 1) * sizeof(char*)); - container_state.environment_variables[i] = strdup(environ[i]); - container_state.num_env_vars++; - } else if (strcmp(container_state.environment_variables[i], environ[i]) != 0) { - free(container_state.environment_variables[i]); - container_state.environment_variables[i] = strdup(environ[i]); - } + // Update memory usage + FILE *file = fopen("/proc/self/status", "r"); + if (file) { + char line[128]; + while (fgets(line, sizeof(line), file)) { + if (strncmp(line, "VmRSS:", 6) == 0) { + sscanf(line + 6, "%ld", &container_state.memory_usage); + break; + } } + fclose(file); + } + + strcpy(container_state.network_status, "Connected"); + + // Update environment variables + for (int i = 0; environ[i] != NULL; i++) { + if (i >= container_state.num_env_vars) { + container_state.environment_variables = realloc( + container_state.environment_variables, (i + 1) * sizeof(char *)); + container_state.environment_variables[i] = strdup(environ[i]); + container_state.num_env_vars++; + } else if (strcmp(container_state.environment_variables[i], environ[i]) != + 0) { + free(container_state.environment_variables[i]); + container_state.environment_variables[i] = strdup(environ[i]); + } + } } void print_container_state() { - update_container_state(); - - printf("Container State:\n"); - printf(" Current Directory: %s\n", container_state.current_directory); - printf(" Last Executed Command: %s\n", container_state.last_executed_command); - printf(" Number of Processes: %d\n", container_state.num_processes); - printf(" Network Status: %s\n", container_state.network_status); - printf(" Environment Variables:\n"); - for (int i = 0; i < container_state.num_env_vars; i++) { - printf(" %s\n", container_state.environment_variables[i]); - } + update_container_state(); + + printf("Container State:\n"); + printf(" Current Directory: %s\n", container_state.current_directory); + printf(" Last Executed Command: %s\n", + container_state.last_executed_command); + printf(" Number of Processes: %d\n", container_state.num_processes); + printf(" Network Status: %s\n", container_state.network_status); + printf(" Environment Variables:\n"); + for (int i = 0; i < container_state.num_env_vars; i++) { + printf(" %s\n", container_state.environment_variables[i]); + } } void handle_debug_command(char *command) { - if (strcmp(command, "continue") == 0 || strcmp(command, "c") == 0) { - debug_mode = DEBUG_NONE; - } else if (strcmp(command, "step") == 0 || strcmp(command, "s") == 0) { - debug_mode = DEBUG_STEP; - } else if (strncmp(command, "break ", 6) == 0) { - if (breakpoint) free(breakpoint); - breakpoint = strdup(command + 6); - debug_mode = DEBUG_BREAK; - } else if (strcmp(command, "print") == 0 || strcmp(command, "p") == 0) { - print_container_state(); - } else if (strncmp(command, "print ", 6) == 0 || strncmp(command, "p ", 2) == 0) { - char *var_name = command + (command[1] == ' ' ? 2 : 6); - char *var_value = getenv(var_name); - if (var_value) { - printf("%s = %s\n", var_name, var_value); - } else { - printf("Variable %s not found\n", var_name); - } - } else if (strcmp(command, "help") == 0 || strcmp(command, "h") == 0) { - printf("Debug commands:\n"); - printf(" continue (c) - Continue execution\n"); - printf(" step (s) - Step to next command\n"); - printf(" break - Set breakpoint at command\n"); - printf(" print (p) - Print container state\n"); - printf(" print (p ) - Print value of environment variable\n"); - printf(" help (h) - Show this help message\n"); + if (strcmp(command, "continue") == 0 || strcmp(command, "c") == 0) { + debug_mode = DEBUG_NONE; + } else if (strcmp(command, "step") == 0 || strcmp(command, "s") == 0) { + debug_mode = DEBUG_STEP; + } else if (strncmp(command, "break ", 6) == 0) { + if (breakpoint) + free(breakpoint); + breakpoint = strdup(command + 6); + debug_mode = DEBUG_BREAK; + } else if (strcmp(command, "print") == 0 || strcmp(command, "p") == 0) { + print_container_state(); + } else if (strncmp(command, "print ", 6) == 0 || + strncmp(command, "p ", 2) == 0) { + char *var_name = command + (command[1] == ' ' ? 2 : 6); + char *var_value = getenv(var_name); + if (var_value) { + printf("%s = %s\n", var_name, var_value); } else { - printf("Unknown debug command. Type 'help' for a list of commands.\n"); - } + printf("Variable %s not found\n", var_name); + } + } else if (strcmp(command, "help") == 0 || strcmp(command, "h") == 0) { + printf("Debug commands:\n"); + printf(" continue (c) - Continue execution\n"); + printf(" step (s) - Step to next command\n"); + printf(" break - Set breakpoint at command\n"); + printf(" print (p) - Print container state\n"); + printf(" print (p ) - Print value of environment variable\n"); + printf(" help (h) - Show this help message\n"); + } else { + printf("Unknown debug command. Type 'help' for a list of commands.\n"); + } } int is_subpath(const char *path, const char *base) { - char resolved_path[PATH_MAX]; - char resolved_base[PATH_MAX]; + char resolved_path[PATH_MAX]; + char resolved_base[PATH_MAX]; - if (realpath(path, resolved_path) == NULL || realpath(base, resolved_base) == NULL) { - return 0; - } + if (realpath(path, resolved_path) == NULL || + realpath(base, resolved_base) == NULL) { + return 0; + } - return strncmp(resolved_path, resolved_base, strlen(resolved_base)) == 0; + return strncmp(resolved_path, resolved_base, strlen(resolved_base)) == 0; } - void execute_command(const char *command, const char *container_root) { - if (command == NULL || strlen(command) == 0) { - fprintf(stderr, "Error: Empty command\n"); - return; - } - - if (debug_mode == DEBUG_STEP || (debug_mode == DEBUG_BREAK && breakpoint && strstr(command, breakpoint))) { - printf("Debugger: Paused at command: %s\n", command); - char *debug_cmd; - while ((debug_cmd = readline("debug> ")) != NULL) { - handle_debug_command(debug_cmd); - free(debug_cmd); - if (debug_mode == DEBUG_NONE) break; - } - } - - printf("Executing: %s\n", command); - - strncpy(container_state.last_executed_command, command, MAX_COMMAND_LEN - 1); - container_state.last_executed_command[MAX_COMMAND_LEN - 1] = '\0'; - - // Update current directory if it's a cd command - if (strncmp(command, "cd ", 3) == 0) { - const char *new_dir = command + 3; - const char shared_folder_path[] = "/Volumes/SharedContainer"; - - char current_path[PATH_MAX]; - if (getcwd(current_path, sizeof(current_path)) == NULL) { - perror("Failed to get current directory"); - return; - } - - if (is_subpath(new_dir, container_root) || - is_subpath(new_dir, shared_folder_path) || - (is_subpath(current_path, shared_folder_path) && strcmp(new_dir, container_root) == 0)) { - if (chdir(new_dir) == 0) { - getcwd(container_state.current_directory, MAX_PATH_LEN); - printf("Changed directory to: %s\n", container_state.current_directory); - } else { - perror("Failed to change directory"); - } - } else { - fprintf(stderr, "Error: Cannot change directory outside of the container or shared folder.\n"); - } - return; - } - - char *args[MAX_COMMAND_LEN / 2 + 1]; - char *command_copy = strdup(command); - if (command_copy == NULL) { - perror("Failed to allocate memory for command"); - return; - } - - char *token = strtok(command_copy, " "); - int i = 0; - - while (token != NULL && i < MAX_COMMAND_LEN / 2) { - args[i++] = token; - token = strtok(NULL, " "); - } - args[i] = NULL; - - pid_t pid; - int status; - posix_spawn_file_actions_t actions; - posix_spawnattr_t attr; - - if (posix_spawn_file_actions_init(&actions) != 0) { - perror("posix_spawn_file_actions_init failed"); - free(command_copy); - return; - } - - if (posix_spawnattr_init(&attr) != 0) { - perror("posix_spawnattr_init failed"); - posix_spawn_file_actions_destroy(&actions); - free(command_copy); - return; - } - - int ret = posix_spawnp(&pid, args[0], &actions, &attr, args, environ); - - if (ret == 0) { - if (waitpid(pid, &status, 0) != -1) { - if (WIFEXITED(status)) { - printf("Child process exited with status %d\n", WEXITSTATUS(status)); - } else if (WIFSIGNALED(status)) { - printf("Child process terminated by signal %d\n", WTERMSIG(status)); - } - } else { - perror("Error waiting for child process"); - } + if (command == NULL || strlen(command) == 0) { + fprintf(stderr, "Error: Empty command\n"); + return; + } + + if (debug_mode == DEBUG_STEP || (debug_mode == DEBUG_BREAK && breakpoint && + strstr(command, breakpoint))) { + printf("Debugger: Paused at command: %s\n", command); + char *debug_cmd; + while ((debug_cmd = readline("debug> ")) != NULL) { + handle_debug_command(debug_cmd); + free(debug_cmd); + if (debug_mode == DEBUG_NONE) + break; + } + } + + printf("Executing: %s\n", command); + + strncpy(container_state.last_executed_command, command, MAX_COMMAND_LEN - 1); + container_state.last_executed_command[MAX_COMMAND_LEN - 1] = '\0'; + + // Update current directory if it's a cd command + if (strncmp(command, "cd ", 3) == 0) { + const char *new_dir = command + 3; + const char shared_folder_path[] = "/Volumes/SharedContainer"; + + char current_path[PATH_MAX]; + if (getcwd(current_path, sizeof(current_path)) == NULL) { + perror("Failed to get current directory"); + return; + } + + if (is_subpath(new_dir, container_root) || + is_subpath(new_dir, shared_folder_path) || + (is_subpath(current_path, shared_folder_path) && + strcmp(new_dir, container_root) == 0)) { + if (chdir(new_dir) == 0) { + getcwd(container_state.current_directory, MAX_PATH_LEN); + printf("Changed directory to: %s\n", container_state.current_directory); + } else { + perror("Failed to change directory"); + } } else { - fprintf(stderr, "posix_spawnp failed: %s\n", strerror(ret)); - } + fprintf(stderr, "Error: Cannot change directory outside of the container " + "or shared folder.\n"); + } + return; + } + + char *args[MAX_COMMAND_LEN / 2 + 1]; + char *command_copy = strdup(command); + if (command_copy == NULL) { + perror("Failed to allocate memory for command"); + return; + } + + char *token = strtok(command_copy, " "); + int i = 0; + + while (token != NULL && i < MAX_COMMAND_LEN / 2) { + args[i++] = token; + token = strtok(NULL, " "); + } + args[i] = NULL; + + pid_t pid; + int status; + posix_spawn_file_actions_t actions; + posix_spawnattr_t attr; + + if (posix_spawn_file_actions_init(&actions) != 0) { + perror("posix_spawn_file_actions_init failed"); + free(command_copy); + return; + } + if (posix_spawnattr_init(&attr) != 0) { + perror("posix_spawnattr_init failed"); posix_spawn_file_actions_destroy(&actions); - posix_spawnattr_destroy(&attr); free(command_copy); + return; + } + + int ret = posix_spawnp(&pid, args[0], &actions, &attr, args, environ); + + if (ret == 0) { + if (waitpid(pid, &status, 0) != -1) { + if (WIFEXITED(status)) { + printf("Child process exited with status %d\n", WEXITSTATUS(status)); + } else if (WIFSIGNALED(status)) { + printf("Child process terminated by signal %d\n", WTERMSIG(status)); + } + } else { + perror("Error waiting for child process"); + } + } else { + fprintf(stderr, "posix_spawnp failed: %s\n", strerror(ret)); + } + + posix_spawn_file_actions_destroy(&actions); + posix_spawnattr_destroy(&attr); + free(command_copy); } void execute_start_config(const char *config_file, const char *container_root) { - FILE *file = fopen(config_file, "r"); - if (file == NULL) { - perror("Error opening start configuration file"); - return; - } - - char line[MAX_COMMAND_LEN]; - while (fgets(line, sizeof(line), file)) { - // Remove newline character if present - line[strcspn(line, "\n")] = 0; + FILE *file = fopen(config_file, "r"); + if (file == NULL) { + perror("Error opening start configuration file"); + return; + } - // Skip empty lines and comments - if (line[0] == '\0' || line[0] == '#') { - continue; - } + char line[MAX_COMMAND_LEN]; + while (fgets(line, sizeof(line), file)) { + // Remove newline character if present + line[strcspn(line, "\n")] = 0; - printf("Executing start command: %s\n", line); - execute_command(line, container_root); + // Skip empty lines and comments + if (line[0] == '\0' || line[0] == '#') { + continue; } - fclose(file); + printf("Executing start command: %s\n", line); + execute_command(line, container_root); + } + + fclose(file); } int is_base64(const char *str) { - regex_t regex; - int reti = regcomp(®ex, "^[A-Za-z0-9+/]+={0,2}$", REG_EXTENDED); - if (reti) { - return 0; - } - reti = regexec(®ex, str, 0, NULL, 0); - regfree(®ex); - return (reti == 0); + regex_t regex; + int reti = regcomp(®ex, "^[A-Za-z0-9+/]+={0,2}$", REG_EXTENDED); + if (reti) { + return 0; + } + reti = regexec(®ex, str, 0, NULL, 0); + regfree(®ex); + return (reti == 0); } void security_scan(const char *bin_file) { - // Note this is a work in progress, so it make not always provide correct results - FILE *file = fopen(bin_file, "rb"); - if (file == NULL) { - perror("Error opening binary file for security scan"); - return; - } - - ContainerConfig config; - if (fread(&config, sizeof(ContainerConfig), 1, file) != 1) { - perror("Error reading container config during security scan"); - fclose(file); - return; - } - - printf("Performing security scan on %s\n", bin_file); - - // Check container configuration - if (config.container_uid == 0 || config.container_gid == 0) { - printf("HIGH RISK: Container is running as root. This is a significant security risk.\n"); - } - - if (strcmp(config.network_mode, "host") == 0) { - printf("HIGH RISK: Container is using host network mode. This can be a significant security risk.\n"); - printf("The host network error will not be revelenant if you use the vlan network.\n"); - } - - // Check resource limits - if (config.memory_hard_limit == 0) { - printf("MEDIUM RISK: No hard memory limit set. This could lead to resource exhaustion.\n"); - } - - if (config.cpu_priority == 0) { - printf("LOW RISK: No CPU priority set. This could lead to resource contention.\n"); - } - - int num_files; - fread(&num_files, sizeof(int), 1, file); - - regex_t regex; - regcomp(®ex, "^[a-zA-Z0-9._-]+$", REG_EXTENDED); - - for (int i = 0; i < num_files; i++) { - char file_name[MAX_PATH_LEN]; - size_t file_size; - - fread(file_name, sizeof(char), MAX_PATH_LEN, file); - fread(&file_size, sizeof(size_t), 1, file); - - // Check for potentially dangerous file names - if (strstr(file_name, "..") != NULL) { - printf("HIGH RISK: File '%s' contains potentially dangerous '..' in its path.\n", file_name); - } - - if (regexec(®ex, file_name, 0, NULL, 0) != 0) { - printf("MEDIUM RISK: File '%s' has a potentially unsafe name.\n", file_name); - } - - // Check for overly permissive file permissions - if (strstr(file_name, ".sh") != NULL || strstr(file_name, ".py") || strstr(file_name, ".lua") != NULL) { - printf("LOW RISK: Script file detected: '%s'. Ensure it has appropriate permissions.\n", file_name); - } - - // Check for sensitive files - if (strstr(file_name, "id_rsa") != NULL || strstr(file_name, ".pem") != NULL) { - printf("HIGH RISK: Potential private key file detected: '%s'. Ensure it's properly secured.\n", file_name); - } + // Note this is a work in progress, so it make not always provide correct + // results + FILE *file = fopen(bin_file, "rb"); + if (file == NULL) { + perror("Error opening binary file for security scan"); + return; + } + + ContainerConfig config; + if (fread(&config, sizeof(ContainerConfig), 1, file) != 1) { + perror("Error reading container config during security scan"); + fclose(file); + return; + } - if (strstr(file_name, "password") != NULL || strstr(file_name, "secret") != NULL) { - printf("HIGH RISK: Potential sensitive file detected: '%s'. Ensure it's properly secured.\n", file_name); - } + printf("Performing security scan on %s\n", bin_file); - // Scan file contents - char *buffer = malloc(file_size + 1); - if (buffer == NULL) { - perror("Failed to allocate memory for file content"); - continue; - } + // Check container configuration + if (config.container_uid == 0 || config.container_gid == 0) { + printf("HIGH RISK: Container is running as root. This is a significant " + "security risk.\n"); + } - fread(buffer, 1, file_size, file); - buffer[file_size] = '\0'; + if (strcmp(config.network_mode, "host") == 0) { + printf("HIGH RISK: Container is using host network mode. This can be a " + "significant security risk.\n"); + printf("The host network error will not be revelenant if you use the vlan " + "network.\n"); + } - // Check for insecure environment variables - if (strstr(buffer, "ENV_VAR_WITH_SENSITIVE_INFO") != NULL) { - printf("MEDIUM RISK: Insecure environment variable detected in file '%s'.\n", file_name); - } + // Check resource limits + if (config.memory_hard_limit == 0) { + printf("MEDIUM RISK: No hard memory limit set. This could lead to resource " + "exhaustion.\n"); + } - // Check for insecure capabilities - if (strstr(buffer, "CAP_SYS_ADMIN") != NULL) { - printf("HIGH RISK: Insecure capability detected in file '%s'.\n", file_name); - } + if (config.cpu_priority == 0) { + printf("LOW RISK: No CPU priority set. This could lead to resource " + "contention.\n"); + } - // Check for insecure file permissions - if (strstr(buffer, "chmod 777") != NULL) { - printf("HIGH RISK: Insecure file permissions detected in file '%s'.\n", file_name); - } + int num_files; + fread(&num_files, sizeof(int), 1, file); - // Check for hardcoded credentials - regex_t pwd_regex; - if (regcomp(&pwd_regex, "(password|api_key|secret)\\s*=\\s*['\"][^'\"]+['\"]", REG_EXTENDED | REG_ICASE) == 0) { - if (regexec(&pwd_regex, buffer, 0, NULL, 0) == 0) { - printf("HIGH RISK: Potential hardcoded credentials detected in file '%s'.\n", file_name); - } - regfree(&pwd_regex); - } + regex_t regex; + regcomp(®ex, "^[a-zA-Z0-9._-]+$", REG_EXTENDED); - // Check for potential SQL injection vulnerabilities - if (strstr(buffer, "SELECT") != NULL && strstr(buffer, "WHERE") != NULL && strstr(buffer, "+") != NULL) { - printf("HIGH RISK: Potential SQL injection vulnerability detected in file '%s'.\n", file_name); - } + for (int i = 0; i < num_files; i++) { + char file_name[MAX_PATH_LEN]; + size_t file_size; - // Check for base64 encoded strings (potential hidden data) - char *token = strtok(buffer, " \t\n"); - while (token != NULL) { - if (strlen(token) > 20 && is_base64(token)) { - printf("LOW RISK: Potential base64 encoded data detected in file '%s'. Verify if it contains sensitive information.\n", file_name); - break; - } - token = strtok(NULL, " \t\n"); - } + fread(file_name, sizeof(char), MAX_PATH_LEN, file); + fread(&file_size, sizeof(size_t), 1, file); - free(buffer); + // Check for potentially dangerous file names + if (strstr(file_name, "..") != NULL) { + printf("HIGH RISK: File '%s' contains potentially dangerous '..' in its " + "path.\n", + file_name); } - regfree(®ex); - - printf("Security scan completed.\n"); - fclose(file); -} - -void read_config_file(const char *filename, ContainerConfig *config) { - FILE *file = fopen(filename, "r"); - if (file == NULL) { - perror("Error opening config file"); - exit(EXIT_FAILURE); + if (regexec(®ex, file_name, 0, NULL, 0) != 0) { + printf("MEDIUM RISK: File '%s' has a potentially unsafe name.\n", + file_name); } - char line[MAX_LINE_LEN]; - while (fgets(line, sizeof(line), file)) { - char key[64], value[MAX_LINE_LEN]; - if (sscanf(line, "%63[^=]=%[^\n]", key, value) == 2) { - if (strcmp(key, "name") == 0) { - strncpy(config->name, value, sizeof(config->name) - 1); - } else if (strcmp(key, "memory_soft_limit") == 0) { - config->memory_soft_limit = strtoul(value, NULL, 10); - } else if (strcmp(key, "memory_hard_limit") == 0) { - config->memory_hard_limit = strtoul(value, NULL, 10); - } else if (strcmp(key, "cpu_priority") == 0) { - config->cpu_priority = atoi(value); - } else if (strcmp(key, "network_mode") == 0) { - strncpy(config->network_mode, value, sizeof(config->network_mode) - 1); - } else if (strcmp(key, "container_uid") == 0) { - config->container_uid = atoi(value); - } else if (strcmp(key, "container_gid") == 0) { - config->container_gid = atoi(value); - } - } + // Check for overly permissive file permissions + if (strstr(file_name, ".sh") != NULL || strstr(file_name, ".py") || + strstr(file_name, ".lua") != NULL) { + printf("LOW RISK: Script file detected: '%s'. Ensure it has appropriate " + "permissions.\n", + file_name); } - fclose(file); -} - - -void containerize_directory(const char *dir_path, const char *output_file, const char *start_config_file, const char *container_config_file) { - FILE *bin_file = fopen(output_file, "wb"); - if (bin_file == NULL) { - perror("Error opening output file"); - exit(EXIT_FAILURE); + // Check for sensitive files + if (strstr(file_name, "id_rsa") != NULL || + strstr(file_name, ".pem") != NULL) { + printf("HIGH RISK: Potential private key file detected: '%s'. Ensure " + "it's properly secured.\n", + file_name); } - File *files = malloc(sizeof(File) * MAX_FILES); - if (files == NULL) { - perror("Error allocating memory for files"); - fclose(bin_file); - exit(EXIT_FAILURE); + if (strstr(file_name, "password") != NULL || + strstr(file_name, "secret") != NULL) { + printf("HIGH RISK: Potential sensitive file detected: '%s'. Ensure it's " + "properly secured.\n", + file_name); } - int num_files; + // Scan file contents + char *buffer = malloc(file_size + 1); + if (buffer == NULL) { + perror("Failed to allocate memory for file content"); + continue; + } - // Initialize default config - ContainerConfig config = { - .name = "default_container", - .memory_soft_limit = 384 * 1024 * 1024, - .memory_hard_limit = 512 * 1024 * 1024, - .cpu_priority = 20, - .network_mode = "bridge", - .container_uid = 1000, - .container_gid = 1000, - .start_config = "" - }; + fread(buffer, 1, file_size, file); + buffer[file_size] = '\0'; - if (container_config_file) { - read_config_file(container_config_file, &config); + // Check for insecure environment variables + if (strstr(buffer, "ENV_VAR_WITH_SENSITIVE_INFO") != NULL) { + printf( + "MEDIUM RISK: Insecure environment variable detected in file '%s'.\n", + file_name); } - if (start_config_file) { - strncpy(config.start_config, start_config_file, MAX_PATH_LEN - 1); - config.start_config[MAX_PATH_LEN - 1] = '\0'; - } else { - config.start_config[0] = '\0'; + // Check for insecure capabilities + if (strstr(buffer, "CAP_SYS_ADMIN") != NULL) { + printf("HIGH RISK: Insecure capability detected in file '%s'.\n", + file_name); } - num_files = read_files(dir_path, files, config.container_uid, config.container_gid); - if (num_files < 0) { - free(files); - fclose(bin_file); - exit(EXIT_FAILURE); + // Check for insecure file permissions + if (strstr(buffer, "chmod 777") != NULL) { + printf("HIGH RISK: Insecure file permissions detected in file '%s'.\n", + file_name); } - fwrite(&config, sizeof(ContainerConfig), 1, bin_file); - fwrite(&num_files, sizeof(int), 1, bin_file); + // Check for hardcoded credentials + regex_t pwd_regex; + if (regcomp(&pwd_regex, + "(password|api_key|secret)\\s*=\\s*['\"][^'\"]+['\"]", + REG_EXTENDED | REG_ICASE) == 0) { + if (regexec(&pwd_regex, buffer, 0, NULL, 0) == 0) { + printf("HIGH RISK: Potential hardcoded credentials detected in file " + "'%s'.\n", + file_name); + } + regfree(&pwd_regex); + } - // Display the progress bar - int progress_bar_width = 50; - printf("Containerizing ["); - fflush(stdout); + // Check for potential SQL injection vulnerabilities + if (strstr(buffer, "SELECT") != NULL && strstr(buffer, "WHERE") != NULL && + strstr(buffer, "+") != NULL) { + printf("HIGH RISK: Potential SQL injection vulnerability detected in " + "file '%s'.\n", + file_name); + } - for (int i = 0; i < num_files; i++) { - fwrite(files[i].name, sizeof(char), MAX_PATH_LEN, bin_file); - fwrite(&files[i].size, sizeof(size_t), 1, bin_file); - fwrite(files[i].data, 1, files[i].size, bin_file); - free(files[i].data); - - // Update the progress bar - int progress = (i + 1) * progress_bar_width / num_files; - for (int j = 0; j < progress; j++) { - printf("#"); - fflush(stdout); - } - for (int j = progress; j < progress_bar_width; j++) { - printf(" "); - fflush(stdout); - } - printf("] %d%%\r", (i + 1) * 100 / num_files); - fflush(stdout); + // Check for base64 encoded strings (potential hidden data) + char *token = strtok(buffer, " \t\n"); + while (token != NULL) { + if (strlen(token) > 20 && is_base64(token)) { + printf("LOW RISK: Potential base64 encoded data detected in file '%s'. " + "Verify if it contains sensitive information.\n", + file_name); + break; + } + token = strtok(NULL, " \t\n"); } - printf("\n"); + free(buffer); + } - free(files); - fclose(bin_file); + regfree(®ex); - security_scan(output_file); + printf("Security scan completed.\n"); + fclose(file); } -void containerize_directory_with_bin_file(const char *dir_path, const char *input_bin_file, const char *output_file, const char *start_config_file, const char *container_config_file) { - FILE *bin_file = fopen(output_file, "wb"); - if (bin_file == NULL) { - perror("Error opening output file"); - exit(EXIT_FAILURE); - } - - File *files = malloc(sizeof(File) * MAX_FILES); - if (files == NULL) { - perror("Error allocating memory for files"); - fclose(bin_file); - exit(EXIT_FAILURE); - } +void read_config_file(const char *filename, ContainerConfig *config) { + FILE *file = fopen(filename, "r"); + if (file == NULL) { + perror("Error opening config file"); + exit(EXIT_FAILURE); + } + + char line[MAX_LINE_LEN]; + while (fgets(line, sizeof(line), file)) { + char key[64], value[MAX_LINE_LEN]; + if (sscanf(line, "%63[^=]=%[^\n]", key, value) == 2) { + if (strcmp(key, "name") == 0) { + strncpy(config->name, value, sizeof(config->name) - 1); + } else if (strcmp(key, "memory_soft_limit") == 0) { + config->memory_soft_limit = strtoul(value, NULL, 10); + } else if (strcmp(key, "memory_hard_limit") == 0) { + config->memory_hard_limit = strtoul(value, NULL, 10); + } else if (strcmp(key, "cpu_priority") == 0) { + config->cpu_priority = atoi(value); + } else if (strcmp(key, "network_mode") == 0) { + strncpy(config->network_mode, value, sizeof(config->network_mode) - 1); + } else if (strcmp(key, "container_uid") == 0) { + config->container_uid = atoi(value); + } else if (strcmp(key, "container_gid") == 0) { + config->container_gid = atoi(value); + } + } + } + + fclose(file); +} - int num_files = 0; +void containerize_directory(const char *dir_path, const char *output_file, + const char *start_config_file, + const char *container_config_file) { + FILE *bin_file = fopen(output_file, "wb"); + if (bin_file == NULL) { + perror("Error opening output file"); + exit(EXIT_FAILURE); + } + + File *files = malloc(sizeof(File) * MAX_FILES); + if (files == NULL) { + perror("Error allocating memory for files"); + fclose(bin_file); + exit(EXIT_FAILURE); + } + + int num_files; + + // Initialize default config + ContainerConfig config = {.name = "default_container", + .memory_soft_limit = 384 * 1024 * 1024, + .memory_hard_limit = 512 * 1024 * 1024, + .cpu_priority = 20, + .network_mode = "bridge", + .container_uid = 1000, + .container_gid = 1000, + .start_config = ""}; + + if (container_config_file) { + read_config_file(container_config_file, &config); + } + + if (start_config_file) { + strncpy(config.start_config, start_config_file, MAX_PATH_LEN - 1); + config.start_config[MAX_PATH_LEN - 1] = '\0'; + } else { + config.start_config[0] = '\0'; + } + + num_files = + read_files(dir_path, files, config.container_uid, config.container_gid); + if (num_files < 0) { + free(files); + fclose(bin_file); + exit(EXIT_FAILURE); + } + + fwrite(&config, sizeof(ContainerConfig), 1, bin_file); + fwrite(&num_files, sizeof(int), 1, bin_file); + + // Display the progress bar + int progress_bar_width = 50; + printf("Containerizing ["); + fflush(stdout); + + for (int i = 0; i < num_files; i++) { + fwrite(files[i].name, sizeof(char), MAX_PATH_LEN, bin_file); + fwrite(&files[i].size, sizeof(size_t), 1, bin_file); + fwrite(files[i].data, 1, files[i].size, bin_file); + free(files[i].data); + + // Update the progress bar + int progress = (i + 1) * progress_bar_width / num_files; + for (int j = 0; j < progress; j++) { + printf("#"); + fflush(stdout); + } + for (int j = progress; j < progress_bar_width; j++) { + printf(" "); + fflush(stdout); + } + printf("] %d%%\r", (i + 1) * 100 / num_files); + fflush(stdout); + } - // Initialize default config - ContainerConfig config = { - .name = "default_container", - .memory_soft_limit = 384 * 1024 * 1024, - .memory_hard_limit = 512 * 1024 * 1024, - .cpu_priority = 20, - .network_mode = "bridge", - .container_uid = 1000, - .container_gid = 1000, - .start_config = "" - }; + printf("\n"); - // Load config from file if provided - if (container_config_file) { - read_config_file(container_config_file, &config); - } + free(files); + fclose(bin_file); - if (start_config_file) { - strncpy(config.start_config, start_config_file, MAX_PATH_LEN - 1); - config.start_config[MAX_PATH_LEN - 1] = '\0'; - } + security_scan(output_file); +} - // Read files from directory - int dir_files = read_files(dir_path, files, config.container_uid, config.container_gid); - if (dir_files < 0) { +void containerize_directory_with_bin_file(const char *dir_path, + const char *input_bin_file, + const char *output_file, + const char *start_config_file, + const char *container_config_file) { + FILE *bin_file = fopen(output_file, "wb"); + if (bin_file == NULL) { + perror("Error opening output file"); + exit(EXIT_FAILURE); + } + + File *files = malloc(sizeof(File) * MAX_FILES); + if (files == NULL) { + perror("Error allocating memory for files"); + fclose(bin_file); + exit(EXIT_FAILURE); + } + + int num_files = 0; + + // Initialize default config + ContainerConfig config = {.name = "default_container", + .memory_soft_limit = 384 * 1024 * 1024, + .memory_hard_limit = 512 * 1024 * 1024, + .cpu_priority = 20, + .network_mode = "bridge", + .container_uid = 1000, + .container_gid = 1000, + .start_config = ""}; + + // Load config from file if provided + if (container_config_file) { + read_config_file(container_config_file, &config); + } + + if (start_config_file) { + strncpy(config.start_config, start_config_file, MAX_PATH_LEN - 1); + config.start_config[MAX_PATH_LEN - 1] = '\0'; + } + + // Read files from directory + int dir_files = + read_files(dir_path, files, config.container_uid, config.container_gid); + if (dir_files < 0) { + free(files); + fclose(bin_file); + exit(EXIT_FAILURE); + } + num_files += dir_files; + + // Read files from input bin file + if (input_bin_file) { + FILE *input_bin = fopen(input_bin_file, "rb"); + if (input_bin == NULL) { + perror("Error opening input bin file"); + free(files); + fclose(bin_file); + exit(EXIT_FAILURE); + } + + ContainerConfig input_config; + fread(&input_config, sizeof(ContainerConfig), 1, input_bin); + + int input_num_files; + fread(&input_num_files, sizeof(int), 1, input_bin); + + for (int i = 0; i < input_num_files; i++) { + File *file = &files[num_files + i]; + fread(file->name, sizeof(char), MAX_PATH_LEN, input_bin); + fread(&file->size, sizeof(size_t), 1, input_bin); + file->data = malloc(file->size); + if (file->data == NULL) { + perror("Error allocating memory for file data"); + fclose(input_bin); free(files); fclose(bin_file); exit(EXIT_FAILURE); + } + fread(file->data, 1, file->size, input_bin); } - num_files += dir_files; - - // Read files from input bin file - if (input_bin_file) { - FILE *input_bin = fopen(input_bin_file, "rb"); - if (input_bin == NULL) { - perror("Error opening input bin file"); - free(files); - fclose(bin_file); - exit(EXIT_FAILURE); - } - ContainerConfig input_config; - fread(&input_config, sizeof(ContainerConfig), 1, input_bin); - - int input_num_files; - fread(&input_num_files, sizeof(int), 1, input_bin); - - for (int i = 0; i < input_num_files; i++) { - File *file = &files[num_files + i]; - fread(file->name, sizeof(char), MAX_PATH_LEN, input_bin); - fread(&file->size, sizeof(size_t), 1, input_bin); - file->data = malloc(file->size); - if (file->data == NULL) { - perror("Error allocating memory for file data"); - fclose(input_bin); - free(files); - fclose(bin_file); - exit(EXIT_FAILURE); - } - fread(file->data, 1, file->size, input_bin); - } + num_files += input_num_files; + fclose(input_bin); + } - num_files += input_num_files; - fclose(input_bin); - } + fwrite(&config, sizeof(ContainerConfig), 1, bin_file); + fwrite(&num_files, sizeof(int), 1, bin_file); - fwrite(&config, sizeof(ContainerConfig), 1, bin_file); - fwrite(&num_files, sizeof(int), 1, bin_file); + // Display the progress bar + int progress_bar_width = 50; + printf("Containerizing ["); + fflush(stdout); - // Display the progress bar - int progress_bar_width = 50; - printf("Containerizing ["); - fflush(stdout); + for (int i = 0; i < num_files; i++) { + fwrite(files[i].name, sizeof(char), MAX_PATH_LEN, bin_file); + fwrite(&files[i].size, sizeof(size_t), 1, bin_file); + fwrite(files[i].data, 1, files[i].size, bin_file); + free(files[i].data); - for (int i = 0; i < num_files; i++) { - fwrite(files[i].name, sizeof(char), MAX_PATH_LEN, bin_file); - fwrite(&files[i].size, sizeof(size_t), 1, bin_file); - fwrite(files[i].data, 1, files[i].size, bin_file); - free(files[i].data); - - // Update the progress bar - int progress = (i + 1) * progress_bar_width / num_files; - for (int j = 0; j < progress; j++) { - printf("#"); - fflush(stdout); - } - for (int j = progress; j < progress_bar_width; j++) { - printf(" "); - fflush(stdout); - } - printf("] %d%%\r", (i + 1) * 100 / num_files); - fflush(stdout); + // Update the progress bar + int progress = (i + 1) * progress_bar_width / num_files; + for (int j = 0; j < progress; j++) { + printf("#"); + fflush(stdout); } + for (int j = progress; j < progress_bar_width; j++) { + printf(" "); + fflush(stdout); + } + printf("] %d%%\r", (i + 1) * 100 / num_files); + fflush(stdout); + } - printf("\n"); + printf("\n"); - free(files); - fclose(bin_file); + free(files); + fclose(bin_file); - security_scan(output_file); + security_scan(output_file); } void *monitor_memory_usage(void *arg) { - ContainerConfig *config = (ContainerConfig *)arg; - mach_port_t task = mach_task_self(); - - while (1) { - task_vm_info_data_t vm_info; - mach_msg_type_number_t count = TASK_VM_INFO_COUNT; - - if (task_info(task, TASK_VM_INFO, (task_info_t)&vm_info, &count) == KERN_SUCCESS) { - vm_size_t used_memory = vm_info.internal + vm_info.compressed; - - if (used_memory > config->memory_hard_limit) { - fprintf(stderr, "Memory usage exceeded hard limit. Terminating process.\n"); - exit(1); - } else if (used_memory > config->memory_soft_limit) { - fprintf(stderr, "Warning: Memory usage exceeded soft limit. Clearing cache memory.\n"); - system("purge"); - } - } + ContainerConfig *config = (ContainerConfig *)arg; + mach_port_t task = mach_task_self(); + + while (1) { + task_vm_info_data_t vm_info; + mach_msg_type_number_t count = TASK_VM_INFO_COUNT; + + if (task_info(task, TASK_VM_INFO, (task_info_t)&vm_info, &count) == + KERN_SUCCESS) { + vm_size_t used_memory = vm_info.internal + vm_info.compressed; - sleep(1); + if (used_memory > config->memory_hard_limit) { + fprintf(stderr, + "Memory usage exceeded hard limit. Terminating process.\n"); + exit(1); + } else if (used_memory > config->memory_soft_limit) { + fprintf(stderr, "Warning: Memory usage exceeded soft limit. Clearing " + "cache memory.\n"); + system("purge"); + } } + + sleep(1); + } } void apply_resource_limits(const ContainerConfig *config) { - // Note it applies basic limits, as it doesn't fully use the kernel - // The reason why is that it would cause many permission issues, pottentially allow the containers to acces parts of the kernel they shouldn't be able to access - setpriority(PRIO_PROCESS, 0, config->cpu_priority); - - // Start memory monitoring thread - pthread_t memory_thread; - if (pthread_create(&memory_thread, NULL, monitor_memory_usage, (void *)config) != 0) { - perror("Failed to create memory monitoring thread"); - } else { - printf("Memory monitoring started with soft limit %ld bytes and hard limit %ld bytes\n", - config->memory_soft_limit, config->memory_hard_limit); - } + // Note it applies basic limits, as it doesn't fully use the kernel + // The reason why is that it would cause many permission issues, pottentially + // allow the containers to acces parts of the kernel they shouldn't be able to + // access + setpriority(PRIO_PROCESS, 0, config->cpu_priority); + + // Start memory monitoring thread + pthread_t memory_thread; + if (pthread_create(&memory_thread, NULL, monitor_memory_usage, + (void *)config) != 0) { + perror("Failed to create memory monitoring thread"); + } else { + printf("Memory monitoring started with soft limit %ld bytes and hard limit " + "%ld bytes\n", + config->memory_soft_limit, config->memory_hard_limit); + } } ContainerNetwork load_container_network(const char *name) { - ContainerNetwork network = {0}; + ContainerNetwork network = {0}; - char filename[MAX_PATH_LEN]; - snprintf(filename, sizeof(filename), "/tmp/network_%s.conf", name); + char filename[MAX_PATH_LEN]; + snprintf(filename, sizeof(filename), "/tmp/network_%s.conf", name); - FILE *file = fopen(filename, "r"); - if (file == NULL) { - fprintf(stderr, "Failed to load network configuration for %s\n", name); - return network; - } + FILE *file = fopen(filename, "r"); + if (file == NULL) { + fprintf(stderr, "Failed to load network configuration for %s\n", name); + return network; + } - char line[256]; - while (fgets(line, sizeof(line), file)) { - if (sscanf(line, "name=%s", network.name) == 1) { - continue; - } - if (sscanf(line, "vlan_id=%d", &network.vlan_id) == 1) { - continue; - } - if (sscanf(line, "container_name=%s", network.container_names[network.num_containers]) == 1) { - network.num_containers++; - continue; - } - if (sscanf(line, "container_ip=%s", network.container_ips[network.num_containers - 1]) == 1) { - continue; - } + char line[256]; + while (fgets(line, sizeof(line), file)) { + if (sscanf(line, "name=%s", network.name) == 1) { + continue; + } + if (sscanf(line, "vlan_id=%d", &network.vlan_id) == 1) { + continue; + } + if (sscanf(line, "container_name=%s", + network.container_names[network.num_containers]) == 1) { + network.num_containers++; + continue; + } + if (sscanf(line, "container_ip=%s", + network.container_ips[network.num_containers - 1]) == 1) { + continue; } + } - fclose(file); - return network; + fclose(file); + return network; } void create_and_save_container_network(const char *name, int vlan_id) { - ContainerNetwork network; - strncpy(network.name, name, MAX_PATH_LEN - 1); - network.vlan_id = vlan_id; - network.num_containers = 0; - - // Save the network configuration to a file - char filename[MAX_PATH_LEN]; - snprintf(filename, sizeof(filename), "/tmp/network_%s.conf", name); - - FILE *file = fopen(filename, "w"); - if (file == NULL) { - perror("Failed to save network configuration"); - return; - } - - fprintf(file, "name=%s\n", network.name); - fprintf(file, "vlan_id=%d\n", network.vlan_id); - fclose(file); - - printf("Created and saved network %s with VLAN ID %d\n", network.name, network.vlan_id); + ContainerNetwork network; + strncpy(network.name, name, MAX_PATH_LEN - 1); + network.vlan_id = vlan_id; + network.num_containers = 0; + + // Save the network configuration to a file + char filename[MAX_PATH_LEN]; + snprintf(filename, sizeof(filename), "/tmp/network_%s.conf", name); + + FILE *file = fopen(filename, "w"); + if (file == NULL) { + perror("Failed to save network configuration"); + return; + } + + fprintf(file, "name=%s\n", network.name); + fprintf(file, "vlan_id=%d\n", network.vlan_id); + fclose(file); + + printf("Created and saved network %s with VLAN ID %d\n", network.name, + network.vlan_id); } void remove_container_network(const char *name) { - char filename[MAX_PATH_LEN]; - snprintf(filename, sizeof(filename), "/tmp/network_%s.conf", name); - - if (remove(filename) == 0) { - printf("Removed network %s\n", name); - } else { - perror("Failed to remove network"); - } + char filename[MAX_PATH_LEN]; + snprintf(filename, sizeof(filename), "/tmp/network_%s.conf", name); + + if (remove(filename) == 0) { + printf("Removed network %s\n", name); + } else { + perror("Failed to remove network"); + } } -void add_container_to_network(ContainerNetwork *network, const char *container_name) { - if (network->num_containers < MAX_CLIENTS) { - strncpy(network->container_names[network->num_containers], container_name, MAX_PATH_LEN - 1); +void add_container_to_network(ContainerNetwork *network, + const char *container_name) { + if (network->num_containers < MAX_CLIENTS) { + strncpy(network->container_names[network->num_containers], container_name, + MAX_PATH_LEN - 1); - // Dynamically assign IP address based on the number of containers - char container_ip[16]; - snprintf(container_ip, sizeof(container_ip), "192.168.%d.%d", network->vlan_id, network->num_containers + 2); - strncpy(network->container_ips[network->num_containers], container_ip, 15); + // Dynamically assign IP address based on the number of containers + char container_ip[16]; + snprintf(container_ip, sizeof(container_ip), "192.168.%d.%d", + network->vlan_id, network->num_containers + 2); + strncpy(network->container_ips[network->num_containers], container_ip, 15); - network->num_containers++; + network->num_containers++; - // Save the updated network configuration to a file - char filename[MAX_PATH_LEN]; - snprintf(filename, sizeof(filename), "/tmp/network_%s.conf", network->name); - - FILE *file = fopen(filename, "a"); - if (file == NULL) { - perror("Failed to save network configuration"); - return; - } + // Save the updated network configuration to a file + char filename[MAX_PATH_LEN]; + snprintf(filename, sizeof(filename), "/tmp/network_%s.conf", network->name); - fprintf(file, "container_name=%s\n", container_name); - fprintf(file, "container_ip=%s\n", container_ip); - fclose(file); + FILE *file = fopen(filename, "a"); + if (file == NULL) { + perror("Failed to save network configuration"); + return; } -} + fprintf(file, "container_name=%s\n", container_name); + fprintf(file, "container_ip=%s\n", container_ip); + fclose(file); + } +} char *get_ip_address() { - int sock = socket(AF_INET, SOCK_DGRAM, 0); - if (sock == -1) { - perror("socket"); - return NULL; - } - - struct sockaddr_in loopback; - memset(&loopback, 0, sizeof(loopback)); - loopback.sin_family = AF_INET; - // Ping Google's DNS server - loopback.sin_addr.s_addr = inet_addr("8.8.8.8"); - loopback.sin_port = htons(53); + int sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock == -1) { + perror("socket"); + return NULL; + } - if (connect(sock, (struct sockaddr *)&loopback, sizeof(loopback)) == -1) { - perror("connect"); - close(sock); - return NULL; - } + struct sockaddr_in loopback; + memset(&loopback, 0, sizeof(loopback)); + loopback.sin_family = AF_INET; + // Ping Google's DNS server + loopback.sin_addr.s_addr = inet_addr("8.8.8.8"); + loopback.sin_port = htons(53); - struct sockaddr_in addr; - socklen_t addr_len = sizeof(addr); - if (getsockname(sock, (struct sockaddr *)&addr, &addr_len) == -1) { - perror("getsockname"); - close(sock); - return NULL; - } + if (connect(sock, (struct sockaddr *)&loopback, sizeof(loopback)) == -1) { + perror("connect"); + close(sock); + return NULL; + } + struct sockaddr_in addr; + socklen_t addr_len = sizeof(addr); + if (getsockname(sock, (struct sockaddr *)&addr, &addr_len) == -1) { + perror("getsockname"); close(sock); - char *ip = strdup(inet_ntoa(addr.sin_addr)); - return ip; + return NULL; + } + + close(sock); + char *ip = strdup(inet_ntoa(addr.sin_addr)); + return ip; } void setup_pf_rules(ContainerNetwork *network) { - char *ip_address = get_ip_address(); - if (ip_address == NULL) { - fprintf(stderr, "Failed to get IP address\n"); - return; - } - - // Assign IP address to the VLAN interface - char ip_cmd[256]; - snprintf(ip_cmd, sizeof(ip_cmd), "ifconfig vlan%d create vlan %d vlandev en0 && ifconfig vlan%d inet %s/24 up", - network->vlan_id, network->vlan_id, network->vlan_id, ip_address); - system(ip_cmd); - - // Create a new file for VLAN rules - char vlan_rules_file[64]; - snprintf(vlan_rules_file, sizeof(vlan_rules_file), "/etc/pf.vlan%d.conf", network->vlan_id); - - FILE *vlan_pf_conf = fopen(vlan_rules_file, "w"); - if (vlan_pf_conf == NULL) { - perror("Failed to create VLAN rules file"); - free(ip_address); - return; - } - - // Write the VLAN rules to the new file - fprintf(vlan_pf_conf, - "# VLAN %d rules\n" - "nat on en0 from %s/24 to any -> (en0)\n" - "pass on vlan%d all\n" - "pass in on vlan%d all\n" - "pass out on vlan%d all\n", - network->vlan_id, - ip_address, - network->vlan_id, - network->vlan_id, - network->vlan_id - ); - fclose(vlan_pf_conf); - - // Add include statement to main pf.conf if not already present - char include_cmd[256]; - snprintf(include_cmd, sizeof(include_cmd), - "grep -q 'include \"%s\"' /etc/pf.conf || echo 'include \"%s\"' >> /etc/pf.conf", - vlan_rules_file, vlan_rules_file); - system(include_cmd); - - // Reload only the VLAN rules - char reload_cmd[256]; - snprintf(reload_cmd, sizeof(reload_cmd), "pfctl -f %s", vlan_rules_file); - system(reload_cmd); - - // Enable pf if it's not already enabled - system("pfctl -e"); - + char *ip_address = get_ip_address(); + if (ip_address == NULL) { + fprintf(stderr, "Failed to get IP address\n"); + return; + } + + // Assign IP address to the VLAN interface + char ip_cmd[256]; + snprintf(ip_cmd, sizeof(ip_cmd), + "ifconfig vlan%d create vlan %d vlandev en0 && ifconfig vlan%d inet " + "%s/24 up", + network->vlan_id, network->vlan_id, network->vlan_id, ip_address); + system(ip_cmd); + + // Create a new file for VLAN rules + char vlan_rules_file[64]; + snprintf(vlan_rules_file, sizeof(vlan_rules_file), "/etc/pf.vlan%d.conf", + network->vlan_id); + + FILE *vlan_pf_conf = fopen(vlan_rules_file, "w"); + if (vlan_pf_conf == NULL) { + perror("Failed to create VLAN rules file"); free(ip_address); + return; + } + + // Write the VLAN rules to the new file + fprintf(vlan_pf_conf, + "# VLAN %d rules\n" + "nat on en0 from %s/24 to any -> (en0)\n" + "pass on vlan%d all\n" + "pass in on vlan%d all\n" + "pass out on vlan%d all\n", + network->vlan_id, ip_address, network->vlan_id, network->vlan_id, + network->vlan_id); + fclose(vlan_pf_conf); + + // Add include statement to main pf.conf if not already present + char include_cmd[256]; + snprintf(include_cmd, sizeof(include_cmd), + "grep -q 'include \"%s\"' /etc/pf.conf || echo 'include \"%s\"' >> " + "/etc/pf.conf", + vlan_rules_file, vlan_rules_file); + system(include_cmd); + + // Reload only the VLAN rules + char reload_cmd[256]; + snprintf(reload_cmd, sizeof(reload_cmd), "pfctl -f %s", vlan_rules_file); + system(reload_cmd); + + // Enable pf if it's not already enabled + system("pfctl -e"); + + free(ip_address); } -void setup_network_isolation(ContainerConfig *config, ContainerNetwork *network) { - if (strcmp(config->network_mode, "bridge") == 0) { - config->vlan_id = network->vlan_id; +void setup_network_isolation(ContainerConfig *config, + ContainerNetwork *network) { + if (strcmp(config->network_mode, "bridge") == 0) { + config->vlan_id = network->vlan_id; + + // Dynamically assign IP address based on the number of containers + char container_ip[16]; + snprintf(container_ip, sizeof(container_ip), "192.168.%d.%d", + network->vlan_id, network->num_containers + 2); + add_container_to_network(network, config->name); + + printf("Setting up bridge network. Container %s on VLAN %d with IP %s\n", + config->name, config->vlan_id, container_ip); + + char vlan_cmd[256]; + snprintf(vlan_cmd, sizeof(vlan_cmd), + "ifconfig vlan%d create vlan %d vlandev en0", config->vlan_id, + config->vlan_id); + system(vlan_cmd); + + snprintf(vlan_cmd, sizeof(vlan_cmd), "ifconfig vlan%d inet %s/24 up", + config->vlan_id, container_ip); + system(vlan_cmd); + } else if (strcmp(config->network_mode, "host") == 0) { + printf("Using host network mode\n"); + } else if (strcmp(config->network_mode, "none") == 0) { + printf("Network isolation set to none\n"); + } else { + printf("Unsupported network mode\n"); + } +} - // Dynamically assign IP address based on the number of containers - char container_ip[16]; - snprintf(container_ip, sizeof(container_ip), "192.168.%d.%d", network->vlan_id, network->num_containers + 2); - add_container_to_network(network, config->name); - - printf("Setting up bridge network. Container %s on VLAN %d with IP %s\n", config->name, config->vlan_id, container_ip); - - char vlan_cmd[256]; - snprintf(vlan_cmd, sizeof(vlan_cmd), "ifconfig vlan%d create vlan %d vlandev en0", - config->vlan_id, config->vlan_id); - system(vlan_cmd); - - snprintf(vlan_cmd, sizeof(vlan_cmd), "ifconfig vlan%d inet %s/24 up", config->vlan_id, container_ip); - system(vlan_cmd); - } else if (strcmp(config->network_mode, "host") == 0) { - printf("Using host network mode\n"); - } else if (strcmp(config->network_mode, "none") == 0) { - printf("Network isolation set to none\n"); - } else { - printf("Unsupported network mode\n"); - } -} - - -void enable_container_communication(ContainerNetwork *network) { - char pf_rule[256]; - snprintf(pf_rule, sizeof(pf_rule), - "pass on vlan%d all\n", - network->vlan_id); - - // Append the rule to pf.conf - FILE *pf_conf = fopen("/etc/pf.conf", "a"); - if (pf_conf == NULL) { - perror("Failed to open /etc/pf.conf"); - return; - } - fprintf(pf_conf, "%s", pf_rule); - fclose(pf_conf); - - // Reload pf rules - system("pfctl -f /etc/pf.conf"); -} +void enable_container_communication(ContainerNetwork *network) { + char pf_rule[256]; + snprintf(pf_rule, sizeof(pf_rule), "pass on vlan%d all\n", network->vlan_id); + + // Append the rule to pf.conf + FILE *pf_conf = fopen("/etc/pf.conf", "a"); + if (pf_conf == NULL) { + perror("Failed to open /etc/pf.conf"); + return; + } + fprintf(pf_conf, "%s", pf_rule); + fclose(pf_conf); + + // Reload pf rules + system("pfctl -f /etc/pf.conf"); +} void handle_client(int client_socket, const char *container_root) { - char command[MAX_COMMAND_LEN]; - ssize_t bytes_received; + char command[MAX_COMMAND_LEN]; + ssize_t bytes_received; - while ((bytes_received = recv(client_socket, command, sizeof(command) - 1, 0)) > 0) { - command[bytes_received] = '\0'; + while ((bytes_received = + recv(client_socket, command, sizeof(command) - 1, 0)) > 0) { + command[bytes_received] = '\0'; - if (strcmp(command, "exit") == 0) { - break; - } + if (strcmp(command, "exit") == 0) { + break; + } - // Execute the command - execute_command(command, container_root); + // Execute the command + execute_command(command, container_root); - // Send a response back to the client - const char *response = "Command executed.\n"; - send(client_socket, response, strlen(response), 0); - } + // Send a response back to the client + const char *response = "Command executed.\n"; + send(client_socket, response, strlen(response), 0); + } - close(client_socket); + close(client_socket); } void start_network_listener(const char *container_root) { - int server_fd, client_socket; - struct sockaddr_in address; - int opt = 1; - int addrlen = sizeof(address); - - // Create socket file descriptor - if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { - perror("socket failed"); - exit(EXIT_FAILURE); - } - - // Allow reuse of local addresses (SO_REUSEADDR) - this is required on macOS - if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { - perror("setsockopt(SO_REUSEADDR) failed"); - exit(EXIT_FAILURE); - } - - // Bind the socket to the network address and port - address.sin_family = AF_INET; - address.sin_addr.s_addr = INADDR_ANY; - address.sin_port = htons(port); - if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { - perror("bind failed"); - exit(EXIT_FAILURE); - } - - // Start listening for incoming connections - if (listen(server_fd, MAX_CLIENTS) < 0) { - perror("listen"); - exit(EXIT_FAILURE); - } - - printf("Server listening on port %d\n", port); - - while (1) { - if ((client_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) { - perror("accept"); - continue; - } - - printf("New client connected\n"); - handle_client(client_socket, container_root); - } + int server_fd, client_socket; + struct sockaddr_in address; + int opt = 1; + int addrlen = sizeof(address); + + // Create socket file descriptor + if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { + perror("socket failed"); + exit(EXIT_FAILURE); + } + + // Allow reuse of local addresses (SO_REUSEADDR) - this is required on macOS + if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { + perror("setsockopt(SO_REUSEADDR) failed"); + exit(EXIT_FAILURE); + } + + // Bind the socket to the network address and port + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = htons(port); + if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { + perror("bind failed"); + exit(EXIT_FAILURE); + } + + // Start listening for incoming connections + if (listen(server_fd, MAX_CLIENTS) < 0) { + perror("listen"); + exit(EXIT_FAILURE); + } + + printf("Server listening on port %d\n", port); + + while (1) { + if ((client_socket = accept(server_fd, (struct sockaddr *)&address, + (socklen_t *)&addrlen)) < 0) { + perror("accept"); + continue; + } + + printf("New client connected\n"); + handle_client(client_socket, container_root); + } } -void scale_container_resources(long memory_soft_limit, long memory_hard_limit, int cpu_priority) { - ContainerConfig config; - // Update the container configuration - config.memory_soft_limit = memory_soft_limit; - config.memory_hard_limit = memory_hard_limit; - config.cpu_priority = cpu_priority; - - // Apply the new resource limits - apply_resource_limits(&config); - - printf("Container resources.sh scaled:\n"); - printf("Memory Soft Limit: %ld bytes\n", memory_soft_limit); - printf("Memory Hard Limit: %ld bytes\n", memory_hard_limit); - printf("CPU Priority: %d\n", cpu_priority); +void scale_container_resources(long memory_soft_limit, long memory_hard_limit, + int cpu_priority) { + ContainerConfig config; + // Update the container configuration + config.memory_soft_limit = memory_soft_limit; + config.memory_hard_limit = memory_hard_limit; + config.cpu_priority = cpu_priority; + + // Apply the new resource limits + apply_resource_limits(&config); + + printf("Container resources.sh scaled:\n"); + printf("Memory Soft Limit: %ld bytes\n", memory_soft_limit); + printf("Memory Hard Limit: %ld bytes\n", memory_hard_limit); + printf("CPU Priority: %d\n", cpu_priority); } void handle_script_command(const char *script_content) { - execute_script(script_content); + execute_script(script_content); } -void handle_script_file(const char *filename) { - execute_script_file(filename); -} +void handle_script_file(const char *filename) { execute_script_file(filename); } void create_shared_folder() { - struct stat st = {0}; - if (stat(SHARED_FOLDER_PATH, &st) == -1) { - mkdir(SHARED_FOLDER_PATH, 0755); - } + struct stat st = {0}; + if (stat(SHARED_FOLDER_PATH, &st) == -1) { + mkdir(SHARED_FOLDER_PATH, 0755); + } } void create_directories(const char *file_path) { - char path[MAX_PATH_LEN]; - strncpy(path, file_path, MAX_PATH_LEN); + char path[MAX_PATH_LEN]; + strncpy(path, file_path, MAX_PATH_LEN); - for (char *p = path + 1; *p; p++) { - if (*p == '/') { - *p = '\0'; - mkdir(path, 0755); - *p = '/'; - } + for (char *p = path + 1; *p; p++) { + if (*p == '/') { + *p = '\0'; + mkdir(path, 0755); + *p = '/'; } + } } double get_cpu_usage() { - static clock_t last_cpu_time = 0; - static struct timeval last_wall_time = {0}; + static clock_t last_cpu_time = 0; + static struct timeval last_wall_time = {0}; - struct rusage usage; - struct timeval current_wall_time; + struct rusage usage; + struct timeval current_wall_time; - getrusage(RUSAGE_SELF, &usage); - gettimeofday(¤t_wall_time, NULL); + getrusage(RUSAGE_SELF, &usage); + gettimeofday(¤t_wall_time, NULL); - clock_t current_cpu_time = usage.ru_utime.tv_sec * 1000000 + usage.ru_utime.tv_usec + - usage.ru_stime.tv_sec * 1000000 + usage.ru_stime.tv_usec; + clock_t current_cpu_time = + usage.ru_utime.tv_sec * 1000000 + usage.ru_utime.tv_usec + + usage.ru_stime.tv_sec * 1000000 + usage.ru_stime.tv_usec; - double cpu_usage = 0.0; - if (last_cpu_time != 0) { - long wall_time_diff = (current_wall_time.tv_sec - last_wall_time.tv_sec) * 1000000 + - (current_wall_time.tv_usec - last_wall_time.tv_usec); + double cpu_usage = 0.0; + if (last_cpu_time != 0) { + long wall_time_diff = + (current_wall_time.tv_sec - last_wall_time.tv_sec) * 1000000 + + (current_wall_time.tv_usec - last_wall_time.tv_usec); - long cpu_time_diff = current_cpu_time - last_cpu_time; + long cpu_time_diff = current_cpu_time - last_cpu_time; - cpu_usage = (cpu_time_diff * 100.0) / wall_time_diff; - } + cpu_usage = (cpu_time_diff * 100.0) / wall_time_diff; + } - last_cpu_time = current_cpu_time; - last_wall_time = current_wall_time; + last_cpu_time = current_cpu_time; + last_wall_time = current_wall_time; - return cpu_usage; + return cpu_usage; } void *auto_scale_resources(void *arg) { - ContainerConfig *config = (ContainerConfig *)arg; - struct rusage usage; - long memory_increment = 100 * 1024 * 1024; // 100 MB - int cpu_priority_increment = 1; - int scale_count = 0; - - while (1) { - if (getrusage(RUSAGE_SELF, &usage) == 0) { - long memory_used = usage.ru_maxrss; - double memory_usage_percent = (memory_used * 100.0) / config->memory_soft_limit; - double cpu_usage = get_cpu_usage(); - - bool should_scale = false; - - if (memory_usage_percent > MEMORY_USAGE_THRESHOLD && config->memory_soft_limit < MAX_MEMORY_LIMIT) { - config->memory_soft_limit += memory_increment; - config->memory_hard_limit += memory_increment; - should_scale = true; - } - - if (cpu_usage > CPU_USAGE_THRESHOLD && config->cpu_priority > MIN_CPU_PRIORITY) { - config->cpu_priority = (config->cpu_priority > MIN_CPU_PRIORITY + cpu_priority_increment) - ? config->cpu_priority - cpu_priority_increment - : MIN_CPU_PRIORITY; - should_scale = true; - } + ContainerConfig *config = (ContainerConfig *)arg; + struct rusage usage; + long memory_increment = 100 * 1024 * 1024; // 100 MB + int cpu_priority_increment = 1; + int scale_count = 0; - if (should_scale) { - apply_resource_limits(config); - scale_count++; - - printf("Auto-scaled resources (Count: %d):\n", scale_count); - printf("Memory Usage: %.2f%% (%ld / %ld bytes)\n", - memory_usage_percent, memory_used, config->memory_soft_limit); - printf("CPU Usage: %.2f%%\n", cpu_usage); - printf("New Memory Soft Limit: %ld bytes\n", config->memory_soft_limit); - printf("New Memory Hard Limit: %ld bytes\n", config->memory_hard_limit); - printf("New CPU Priority: %d\n", config->cpu_priority); - } - } - - sleep(5); + while (1) { + if (getrusage(RUSAGE_SELF, &usage) == 0) { + long memory_used = usage.ru_maxrss; + double memory_usage_percent = + (memory_used * 100.0) / config->memory_soft_limit; + double cpu_usage = get_cpu_usage(); + + bool should_scale = false; + + if (memory_usage_percent > MEMORY_USAGE_THRESHOLD && + config->memory_soft_limit < MAX_MEMORY_LIMIT) { + config->memory_soft_limit += memory_increment; + config->memory_hard_limit += memory_increment; + should_scale = true; + } + + if (cpu_usage > CPU_USAGE_THRESHOLD && + config->cpu_priority > MIN_CPU_PRIORITY) { + config->cpu_priority = + (config->cpu_priority > MIN_CPU_PRIORITY + cpu_priority_increment) + ? config->cpu_priority - cpu_priority_increment + : MIN_CPU_PRIORITY; + should_scale = true; + } + + if (should_scale) { + apply_resource_limits(config); + scale_count++; + + printf("Auto-scaled resources (Count: %d):\n", scale_count); + printf("Memory Usage: %.2f%% (%ld / %ld bytes)\n", memory_usage_percent, + memory_used, config->memory_soft_limit); + printf("CPU Usage: %.2f%%\n", cpu_usage); + printf("New Memory Soft Limit: %ld bytes\n", config->memory_soft_limit); + printf("New Memory Hard Limit: %ld bytes\n", config->memory_hard_limit); + printf("New CPU Priority: %d\n", config->cpu_priority); + } } + + sleep(5); + } } void start_auto_scaling(ContainerConfig *config) { - pthread_t auto_scale_thread; - if (pthread_create(&auto_scale_thread, NULL, auto_scale_resources, (void *)config) != 0) { - perror("Failed to create auto-scaling thread"); - } else { - printf("Auto-scaling started\n"); - } + pthread_t auto_scale_thread; + if (pthread_create(&auto_scale_thread, NULL, auto_scale_resources, + (void *)config) != 0) { + perror("Failed to create auto-scaling thread"); + } else { + printf("Auto-scaling started\n"); + } } void print_current_resource_usage(ContainerConfig *config) { - struct rusage usage; - if (getrusage(RUSAGE_SELF, &usage) == 0) { - long memory_used = usage.ru_maxrss; - double memory_usage_percent = (memory_used * 100.0) / config->memory_soft_limit; - double cpu_usage = get_cpu_usage(); - - printf("Current Resource Usage:\n"); - printf("Memory Usage: %.2f%% (%ld / %ld bytes)\n", - memory_usage_percent, memory_used, config->memory_soft_limit); - printf("CPU Usage: %.2f%%\n", cpu_usage); - printf("CPU Priority: %d\n", config->cpu_priority); - } else { - perror("Failed to get resource usage"); - } + struct rusage usage; + if (getrusage(RUSAGE_SELF, &usage) == 0) { + long memory_used = usage.ru_maxrss; + double memory_usage_percent = + (memory_used * 100.0) / config->memory_soft_limit; + double cpu_usage = get_cpu_usage(); + + printf("Current Resource Usage:\n"); + printf("Memory Usage: %.2f%% (%ld / %ld bytes)\n", memory_usage_percent, + memory_used, config->memory_soft_limit); + printf("CPU Usage: %.2f%%\n", cpu_usage); + printf("CPU Priority: %d\n", config->cpu_priority); + } else { + perror("Failed to get resource usage"); + } } void set_terminal_raw_mode() { - struct termios raw; - tcgetattr(STDIN_FILENO, &raw); - raw.c_lflag &= ~(ICANON | ECHO); - tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw); + struct termios raw; + tcgetattr(STDIN_FILENO, &raw); + raw.c_lflag &= ~(ICANON | ECHO); + tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw); } void set_terminal_canonical_mode() { - struct termios canonical; - tcgetattr(STDIN_FILENO, &canonical); - canonical.c_lflag |= (ICANON | ECHO); - tcsetattr(STDIN_FILENO, TCSAFLUSH, &canonical); + struct termios canonical; + tcgetattr(STDIN_FILENO, &canonical); + canonical.c_lflag |= (ICANON | ECHO); + tcsetattr(STDIN_FILENO, TCSAFLUSH, &canonical); } void move_cursor_left(int n) { - printf("\033[%dD", n); - fflush(stdout); + printf("\033[%dD", n); + fflush(stdout); } void move_cursor_right(int n) { - printf("\033[%dC", n); - fflush(stdout); + printf("\033[%dC", n); + fflush(stdout); } void clear_line() { - printf("\033[2K"); - fflush(stdout); + printf("\033[2K"); + fflush(stdout); } void add_to_history(const char *command) { - if (history.count < MAX_HISTORY_LEN) { - strncpy(history.commands[history.count], command, MAX_COMMAND_LEN); - history.count++; - history.current = history.count; - } else { - // Shift the history to make room for the new command - memmove(history.commands, history.commands + 1, (MAX_HISTORY_LEN - 1) * MAX_COMMAND_LEN); - strncpy(history.commands[MAX_HISTORY_LEN - 1], command, MAX_COMMAND_LEN); - } + if (history.count < MAX_HISTORY_LEN) { + strncpy(history.commands[history.count], command, MAX_COMMAND_LEN); + history.count++; + history.current = history.count; + } else { + // Shift the history to make room for the new command + memmove(history.commands, history.commands + 1, + (MAX_HISTORY_LEN - 1) * MAX_COMMAND_LEN); + strncpy(history.commands[MAX_HISTORY_LEN - 1], command, MAX_COMMAND_LEN); + } } -void navigate_history(char *command, int *command_index, int *cursor_pos, int direction) { - if (direction == -1 && history.current > 0) { - history.current--; - strncpy(command, history.commands[history.current], MAX_COMMAND_LEN); - *command_index = strlen(command); - *cursor_pos = *command_index; - } else if (direction == 1 && history.current < history.count - 1) { - history.current++; - strncpy(command, history.commands[history.current], MAX_COMMAND_LEN); - *command_index = strlen(command); - *cursor_pos = *command_index; - } +void navigate_history(char *command, int *command_index, int *cursor_pos, + int direction) { + if (direction == -1 && history.current > 0) { + history.current--; + strncpy(command, history.commands[history.current], MAX_COMMAND_LEN); + *command_index = strlen(command); + *cursor_pos = *command_index; + } else if (direction == 1 && history.current < history.count - 1) { + history.current++; + strncpy(command, history.commands[history.current], MAX_COMMAND_LEN); + *command_index = strlen(command); + *cursor_pos = *command_index; + } } volatile sig_atomic_t stop_thread = 0; -void signal_handler() { - stop_thread = 1; -} +void signal_handler() { stop_thread = 1; } void *logger_thread(void *arg) { - FILE *log_file = fopen("/Volumes/Container/log.txt", "w"); - if (log_file == NULL) { - perror("Failed to open log file"); - return NULL; - } - - while (!stop_thread) { - struct rusage usage; - if (getrusage(RUSAGE_SELF, &usage) == 0) { - long memory_used = usage.ru_maxrss; - double cpu_usage = get_cpu_usage(); + FILE *log_file = fopen("/Volumes/Container/log.txt", "w"); + if (log_file == NULL) { + perror("Failed to open log file"); + return NULL; + } - fprintf(log_file, "Memory Usage: %ld bytes\n", memory_used); - fprintf(log_file, "CPU Usage: %.2f%%\n", cpu_usage); - fprintf(log_file, "CPU Priority: %d\n", ((ContainerConfig *)arg)->cpu_priority); + while (!stop_thread) { + struct rusage usage; + if (getrusage(RUSAGE_SELF, &usage) == 0) { + long memory_used = usage.ru_maxrss; + double cpu_usage = get_cpu_usage(); - } else { - perror("Failed to get resource usage"); - } + fprintf(log_file, "Memory Usage: %ld bytes\n", memory_used); + fprintf(log_file, "CPU Usage: %.2f%%\n", cpu_usage); + fprintf(log_file, "CPU Priority: %d\n", + ((ContainerConfig *)arg)->cpu_priority); - sleep(5); + } else { + perror("Failed to get resource usage"); } - fclose(log_file); - return NULL; + sleep(5); + } + + fclose(log_file); + return NULL; } volatile sig_atomic_t should_exit = 0; void handle_signal(int sig) { - // Set the should_exit flag when a signal is received - if (sig == SIGTERM || sig == SIGINT || sig == SIGSEGV) { - should_exit = 1; - } + // Set the should_exit flag when a signal is received + if (sig == SIGTERM || sig == SIGINT || sig == SIGSEGV) { + should_exit = 1; + } } -void create_isolated_environment(FILE *bin_file, const char *bin_file_path, ContainerNetwork *network) { - signal(SIGTERM, handle_signal); - signal(SIGINT, handle_signal); - signal(SIGSEGV, handle_signal); - ContainerConfig config; - fread(&config, sizeof(ContainerConfig), 1, bin_file); +typedef struct { + pthread_t thread; + pid_t pid; // Add process ID + char command[MAX_COMMAND_LEN]; + int is_running; + int is_paused; + int exit_code; + char output[4096]; + pthread_mutex_t output_mutex; + pthread_cond_t pause_cond; + char container_root[MAX_PATH_LEN]; + time_t start_time; // Add start time +} BackgroundTask; - setup_network_isolation(&config, network); - enable_container_communication(network); +typedef struct { + BackgroundTask tasks[MAX_BACKGROUND_THREADS]; + int task_count; + pthread_mutex_t tasks_mutex; +} BackgroundTaskManager; + +BackgroundTaskManager bg_manager = {.task_count = 0, + .tasks_mutex = PTHREAD_MUTEX_INITIALIZER}; + +// Modify the thread function to store the process ID +void *background_command_thread(void *arg) { + BackgroundTask *task = (BackgroundTask *)arg; + + if (chdir(task->container_root) != 0) { + snprintf(task->output, sizeof(task->output), + "Failed to change to container directory\n"); + task->is_running = 0; + return NULL; + } - int num_files; - fread(&num_files, sizeof(int), 1, bin_file); + int pipefd[2]; + if (pipe(pipefd) == -1) { + snprintf(task->output, sizeof(task->output), "Failed to create pipe\n"); + task->is_running = 0; + return NULL; + } - // Create shared folder if it doesn't exist - char shared_folder_path[] = "/Volumes/SharedContainer"; - mkdir(shared_folder_path, 0755); + pid_t pid = fork(); + if (pid == -1) { + snprintf(task->output, sizeof(task->output), "Fork failed\n"); + task->is_running = 0; + return NULL; + } - // Create and mount disk image for file system isolation - char disk_image_path[MAX_PATH_LEN]; - snprintf(disk_image_path, sizeof(disk_image_path), "/tmp/container_disk_%d.dmg", getpid()); + if (pid == 0) { // Child process + close(pipefd[0]); + dup2(pipefd[1], STDOUT_FILENO); + dup2(pipefd[1], STDERR_FILENO); + close(pipefd[1]); - // Assign the volume name as the bin_file_path - char create_disk_command[MAX_COMMAND_LEN]; - struct stat st; - if (stat(bin_file_path, &st) == -1) { - perror("stat"); - } + char *args[] = {"/bin/sh", "-c", task->command, NULL}; + execvp(args[0], args); + exit(127); + } - // Get the size of the file in bytes - off_t file_size = st.st_size; + // Store the process ID in the task structure + task->pid = pid; - // Convert the size to gigabytes and add 1 GB - double size_in_gb = (double)file_size / (1024 * 1024 * 1024) + 1.0; + close(pipefd[1]); - // Format the size to two decimal places - snprintf(create_disk_command, sizeof(create_disk_command), - "hdiutil create -size %.2fg -fs HFS+ -volname \"%s\" %s", - size_in_gb, bin_file_path, disk_image_path); + char buffer[1024]; + ssize_t n; - system(create_disk_command); + while ((n = read(pipefd[0], buffer, sizeof(buffer) - 1)) > 0) { + buffer[n] = '\0'; + pthread_mutex_lock(&task->output_mutex); + while (task->is_paused) { + pthread_cond_wait(&task->pause_cond, &task->output_mutex); + } + strncat(task->output, buffer, + sizeof(task->output) - strlen(task->output) - 1); + pthread_mutex_unlock(&task->output_mutex); + } - chmod(disk_image_path, 0644); // rw-r--r-- + close(pipefd[0]); - char mount_command[MAX_COMMAND_LEN]; - snprintf(mount_command, sizeof(mount_command), "hdiutil attach %s", disk_image_path); - system(mount_command); + int status; + waitpid(pid, &status, 0); + task->exit_code = WEXITSTATUS(status); + task->is_running = 0; + return NULL; +} - char container_root[MAX_PATH_LEN]; - snprintf(container_root, sizeof(container_root), "/Volumes/%s", bin_file_path); +// Modify start_background_task to initialize new fields +int start_background_task(const char *command, const char *container_root) { + pthread_mutex_lock(&bg_manager.tasks_mutex); + + if (bg_manager.task_count >= MAX_BACKGROUND_THREADS) { + pthread_mutex_unlock(&bg_manager.tasks_mutex); + return -1; + } + + int task_id = bg_manager.task_count++; + BackgroundTask *task = &bg_manager.tasks[task_id]; + + memset(task, 0, sizeof(BackgroundTask)); + strncpy(task->command, command, MAX_COMMAND_LEN - 1); + strncpy(task->container_root, container_root, MAX_PATH_LEN - 1); + task->is_running = 1; + task->is_paused = 0; + task->pid = 0; + task->start_time = time(NULL); + pthread_mutex_init(&task->output_mutex, NULL); + pthread_cond_init(&task->pause_cond, NULL); + + if (pthread_create(&task->thread, NULL, background_command_thread, task) != + 0) { + bg_manager.task_count--; + pthread_mutex_unlock(&bg_manager.tasks_mutex); + return -1; + } + + pthread_mutex_unlock(&bg_manager.tasks_mutex); + return task_id; +} - // Create a symbolic link to the shared folder - char shared_mount_point[MAX_PATH_LEN]; - snprintf(shared_mount_point, sizeof(shared_mount_point), "%s/shared", container_root); - symlink(shared_folder_path, shared_mount_point); +void pause_background_tasks() { + pthread_mutex_lock(&bg_manager.tasks_mutex); + for (int i = 0; i < bg_manager.task_count; i++) { + BackgroundTask *task = &bg_manager.tasks[i]; + if (task->is_running) { + pthread_mutex_lock(&task->output_mutex); + task->is_paused = 1; + pthread_mutex_unlock(&task->output_mutex); + } + } + pthread_mutex_unlock(&bg_manager.tasks_mutex); +} - // Extract files - for (int i = 0; i < num_files; i++) { - File file; - fread(file.name, sizeof(char), MAX_PATH_LEN, bin_file); - fread(&file.size, sizeof(size_t), 1, bin_file); +void unpause_background_tasks() { + pthread_mutex_lock(&bg_manager.tasks_mutex); + for (int i = 0; i < bg_manager.task_count; i++) { + BackgroundTask *task = &bg_manager.tasks[i]; + if (task->is_running) { + pthread_mutex_lock(&task->output_mutex); + task->is_paused = 0; + pthread_cond_signal(&task->pause_cond); + pthread_mutex_unlock(&task->output_mutex); + } + } + pthread_mutex_unlock(&bg_manager.tasks_mutex); +} - file.data = malloc(file.size); - if (file.data == NULL) { - perror("Error allocating memory for file data"); - exit(EXIT_FAILURE); - } +void wait_background_task(int task_id) { + if (task_id < 0 || task_id >= bg_manager.task_count) { + printf("Invalid task ID\n"); + return; + } - fread(file.data, 1, file.size, bin_file); + BackgroundTask *task = &bg_manager.tasks[task_id]; + pthread_join(task->thread, NULL); - char file_path[MAX_PATH_LEN]; - snprintf(file_path, sizeof(file_path), "%s/%s", container_root, file.name); + printf("\nBackground task %d completed\n", task_id); + printf("Command: %s\n", task->command); + printf("Exit code: %d\n", task->exit_code); + printf("Output:\n%s\n", task->output); +} - // Ensure all necessary directories exist - create_directories(file_path); +void show_background_tasks() { + pthread_mutex_lock(&bg_manager.tasks_mutex); - FILE *out_file = fopen(file_path, "wb"); - if (out_file == NULL) { - perror("Error creating file in container"); - exit(EXIT_FAILURE); - } + printf("\nBackground Tasks:\n"); + printf("%-4s %-8s %-10s %-8s %-20s %s\n", "ID", "PID", "STATUS", "RUNTIME", + "STARTED", "COMMAND"); + printf("---------------------------------------------------------------------" + "-----------\n"); - fwrite(file.data, 1, file.size, out_file); - fclose(out_file); - free(file.data); + time_t current_time = time(NULL); + int found = 0; - chmod(file_path, 0755); - } + for (int i = 0; i < bg_manager.task_count; i++) { + BackgroundTask *task = &bg_manager.tasks[i]; + if (task->is_running) { + found = 1; + const char *status = task->is_paused ? "PAUSED" : "RUNNING"; - chmod(container_root, 0755); + // Calculate runtime + time_t runtime = current_time - task->start_time; + char runtime_str[32]; + snprintf(runtime_str, sizeof(runtime_str), "%02ld:%02ld:%02ld", + runtime / 3600, (runtime % 3600) / 60, runtime % 60); - if (chdir(container_root) != 0) { - perror("Failed to change to container root directory"); - exit(1); - } + // Format start time + char start_time_str[32]; + strftime(start_time_str, sizeof(start_time_str), "%H:%M:%S", + localtime(&task->start_time)); - if (setgid(config.container_gid) != 0) { - perror("Failed to set group ID"); - exit(1); + printf("%-4d %-8d %-10s %-8s %-20s %s\n", i, task->pid, status, + runtime_str, start_time_str, task->command); } + } - if (setuid(config.container_uid) != 0) { - perror("Failed to set user ID"); - exit(1); - } + if (!found) { + printf("No running background tasks\n"); + } + printf("---------------------------------------------------------------------" + "-----------\n"); - apply_resource_limits(&config); - - // Updated sandbox profile - char sandbox_profile[1024]; - snprintf(sandbox_profile, sizeof(sandbox_profile), - "(version 1)" - "(deny default)" - "(allow process-fork)" - "(allow file-read*)" - "(allow file-write* (subpath \"%s\"))" - "(allow file-read* (subpath \"%s\"))" - "(allow file-read* (literal \"%s\"))" - "(allow file-read* (subpath \"/usr/lib\"))" - "(allow file-read* (subpath \"/usr/bin\"))" - "(allow file-read* (subpath \"/bin\"))" - "(allow file-read* (subpath \"/System\"))" - "(allow file-read* (subpath \"%s\"))" - "(allow file-read* (subpath \"/Applications/Xcode.app\"))" - "(allow file-write* (subpath \"%s\"))" - "(allow sysctl-read)" - "(allow mach-lookup)" - "(allow network-outbound (remote ip))" - "(allow network-inbound (local ip))" - "(allow process-exec (subpath \"/usr/bin\"))" - "(allow process-exec (subpath \"/Applications/Xcode.app\"))" - "(allow process-exec (subpath \"/bin\"))" - "(allow process-exec (subpath \"%s\"))", - container_root, container_root, bin_file_path, - shared_mount_point, shared_mount_point, container_root - ); - - char *error; - if (sandbox_init(sandbox_profile, 0, &error) != 0) { - fprintf(stderr, "sandbox_init failed: %s\n", error); - sandbox_free_error(error); - exit(1); - } + pthread_mutex_unlock(&bg_manager.tasks_mutex); +} - printf("\n=== Container %s Terminal ===\n", bin_file_path); - printf("Enter commands (type 'exit' to quit, help for help):\n"); - printf("If you just ran the container ignore the first log file error"); +void start_network_thread(pthread_t network_thread, int network_thread_active) { + if (network_thread_active) { + printf("Network listener is already running\n"); + return; + } + + if (pthread_create(&network_thread, NULL, + (void *(*)(void *))start_network_listener, NULL) != 0) { + perror("Failed to create network listener thread"); + } else { + network_thread_active = 1; + printf("Network listener started successfully\n"); + } +} - // Start the network listener in a separate thread - pthread_t network_thread; - if (pthread_create(&network_thread, NULL, (void *(*)(void *))start_network_listener, NULL) != 0) { - perror("Failed to create network listener thread"); - } +void stop_network_thread(pthread_t network_thread, int network_thread_active) { + if (!network_thread_active) { + printf("Network listener is not running\n"); + return; + } + + pthread_cancel(network_thread); + pthread_join(network_thread, NULL); + network_thread_active = 0; + printf("Network listener stopped successfully\n"); +} - if (config.start_config[0] != '\0') { - char start_config_path[MAX_PATH_LEN]; - snprintf(start_config_path, sizeof(start_config_path), "%s/%s", container_root, config.start_config); - execute_start_config(start_config_path, container_root); - } +void create_isolated_environment(FILE *bin_file, const char *bin_file_path, + ContainerNetwork *network) { + signal(SIGTERM, handle_signal); + signal(SIGINT, handle_signal); + signal(SIGSEGV, handle_signal); + ContainerConfig config; + fread(&config, sizeof(ContainerConfig), 1, bin_file); - char command[MAX_COMMAND_LEN]; - int command_index = 0; - int cursor_pos = 0; + setup_network_isolation(&config, network); + enable_container_communication(network); - set_terminal_raw_mode(); + int num_files; + fread(&num_files, sizeof(int), 1, bin_file); - pthread_t logger; - pthread_create(&logger, NULL, logger_thread, &config); - while (1) { - if (should_exit) { - break; - } + // Create shared folder if it doesn't exist + char shared_folder_path[] = "/Volumes/SharedContainer"; + mkdir(shared_folder_path, 0755); - printf("> "); - fflush(stdout); + // Create and mount disk image for file system isolation + char disk_image_path[MAX_PATH_LEN]; + snprintf(disk_image_path, sizeof(disk_image_path), + "/tmp/container_disk_%d.dmg", getpid()); - int ch; - while ((ch = getchar()) != EOF) { - if (ch == 27) { // ESC key - getchar(); // Skip the next character - ch = getchar(); // Get the actual key code - if (ch == 'A') { // Up arrow - navigate_history(command, &command_index, &cursor_pos, -1); - } else if (ch == 'B') { // Down arrow - navigate_history(command, &command_index, &cursor_pos, 1); - } else if (ch == 'C') { // Right arrow - if (cursor_pos < command_index) { - move_cursor_right(1); - cursor_pos++; - } - } else if (ch == 'D') { // Left arrow - if (cursor_pos > 0) { - move_cursor_left(1); - cursor_pos--; - } - } - } else if (ch == 127 || ch == 8) { // Backspace or Delete - if (cursor_pos > 0) { - move_cursor_left(1); - clear_line(); - memmove(&command[cursor_pos - 1], &command[cursor_pos], command_index - cursor_pos + 1); - command_index--; - cursor_pos--; - printf("%s", &command[cursor_pos]); - move_cursor_left(command_index - cursor_pos); - } - } else if (ch == '\n' || ch == '\r') { // Enter key - command[command_index] = '\0'; - set_terminal_canonical_mode(); - usleep(10000); // 10ms delay - - if (strcmp(command, "exit") == 0) goto exit_loop; - if (strcmp(command, "debug") == 0) { - debug_mode = DEBUG_STEP; - printf("Entered debug mode. Type 'help' for debug commands.\n"); - } else if (strncmp(command, "scale", 5) == 0) { - long memory_soft_limit, memory_hard_limit; - int cpu_priority; - if (sscanf(command, "scale %ld %ld %d", &memory_soft_limit, &memory_hard_limit, &cpu_priority) == 3) { - scale_container_resources(memory_soft_limit, memory_hard_limit, cpu_priority); - } else { - printf("Usage: scale \n"); - } - } else if (strncmp(command, "xs", 6) == 0) { - char *script_content = command + 7; - handle_script_command(script_content); - } else if (strncmp(command, "osxs", 4) == 0) { - char *filename = command + 5; - while (isspace(*filename)) { - filename++; - } - handle_script_file(filename); - } else if (strcmp(command, "autoscale") == 0) { - start_auto_scaling(&config); - } else if (strcmp(command, "status") == 0) { - print_current_resource_usage(&config); - } else if (strcmp(command, "help") == 0) { - printf("Commands:\n"); - printf(" exit: Exit the container\n"); - printf(" debug: Enter debug mode\n"); - printf(" scale : Set memory limits and CPU priority\n"); - printf(" xs : Execute a script in the container\n"); - printf(" osxs : Execute a script file in the container\n"); - printf(" autoscale: Start automatic resource scaling\n"); - printf(" status: Print current resource usage\n"); - printf(" help: Print this help message\n"); - printf(" stop: Stops the container and save its state\n"); - } else if (strcmp(command, "stop") == 0) { - printf("Stopping container...\n"); - - // Save the container state - char state_file_path[MAX_PATH_LEN]; - time_t now = time(NULL); - - // Construct the state file path using the Unix timestamp - snprintf(state_file_path, sizeof(state_file_path), "%s/%ld_%s", container_root, now, bin_file_path); - - // state_file_path now includes the date - FILE *state_file = fopen(state_file_path, "wb"); - if (state_file == NULL) { - perror("Error creating container state file"); - } else { - // Save the container configuration - fwrite(&config, sizeof(ContainerConfig), 1, state_file); - - // Save the environment variables - fwrite(&container_state, sizeof(ContainerState), 1, state_file); - fclose(state_file); - printf("Container state saved to %s\n", state_file_path); - } - - goto stop_loop; - } else { - execute_command(command, container_root); - } - - printf("\n"); - add_to_history(command); - char log_file_path[MAX_PATH_LEN]; - snprintf(log_file_path, sizeof(log_file_path), "%s/log.txt", container_root); - FILE *log_file = fopen(log_file_path, "a"); - if (log_file == NULL) { - perror("Failed to open log file"); - } else { - fprintf(log_file, "\nCommand History:\n"); - for (int i = 0; i < command_index; i++) { - fprintf(log_file, "Command %d: %s\n", i + 1, history.commands[i]); - } - fclose(log_file); - } - command_index = 0; - cursor_pos = 0; - set_terminal_raw_mode(); - printf("> "); - fflush(stdout); - } else { - if (command_index < MAX_COMMAND_LEN - 1) { - memmove(&command[cursor_pos + 1], &command[cursor_pos], command_index - cursor_pos + 1); - command[cursor_pos] = ch; - command_index++; - cursor_pos++; - printf("%s", &command[cursor_pos - 1]); - move_cursor_left(command_index - cursor_pos); - } - } - } - } + // Assign the volume name as the bin_file_path + char create_disk_command[MAX_COMMAND_LEN]; + struct stat st; + if (stat(bin_file_path, &st) == -1) { + perror("stat"); + } -exit_loop: - set_terminal_canonical_mode(); - printf("Container terminated.\n"); + // Get the size of the file in bytes + off_t file_size = st.st_size; - // Clean up the threads - pthread_cancel(network_thread); - pthread_join(network_thread, NULL); + // Convert the size to gigabytes and add 1 GB + double size_in_gb = (double)file_size / (1024 * 1024 * 1024) + 1.0; - pthread_cancel(logger); - pthread_join(logger, NULL); + // Format the size to two decimal places + snprintf(create_disk_command, sizeof(create_disk_command), + "hdiutil create -size %.2fg -fs HFS+ -volname \"%s\" %s", size_in_gb, + bin_file_path, disk_image_path); - // Cleanup container state - for (int i = 0; i < container_state.num_env_vars; i++) { - free(container_state.environment_variables[i]); - } - free(container_state.environment_variables); + system(create_disk_command); -stop_loop: - set_terminal_canonical_mode(); - printf("Container stopped.\n"); + chmod(disk_image_path, 0644); // rw-r--r-- - pthread_cancel(network_thread); - pthread_join(network_thread, NULL); + char mount_command[MAX_COMMAND_LEN]; + snprintf(mount_command, sizeof(mount_command), "hdiutil attach %s", + disk_image_path); + system(mount_command); - pthread_cancel(logger); - pthread_join(logger, NULL); + char container_root[MAX_PATH_LEN]; + snprintf(container_root, sizeof(container_root), "/Volumes/%s", + bin_file_path); - // Preserve the environment variables - for (int i = 0; i < container_state.num_env_vars; i++) { - setenv(container_state.environment_variables[i], NULL, 1); - } -} + // Create a symbolic link to the shared folder + char shared_mount_point[MAX_PATH_LEN]; + snprintf(shared_mount_point, sizeof(shared_mount_point), "%s/shared", + container_root); + symlink(shared_folder_path, shared_mount_point); + // Extract files + for (int i = 0; i < num_files; i++) { + File file; + fread(file.name, sizeof(char), MAX_PATH_LEN, bin_file); + fread(&file.size, sizeof(size_t), 1, bin_file); -void ocreate_isolated_environment(FILE *bin_file, const char *bin_file_path) { - signal(SIGTERM, handle_signal); - signal(SIGINT, handle_signal); - signal(SIGSEGV, handle_signal); - ContainerConfig config; - fread(&config, sizeof(ContainerConfig), 1, bin_file); + file.data = malloc(file.size); + if (file.data == NULL) { + perror("Error allocating memory for file data"); + exit(EXIT_FAILURE); + } - int num_files; - fread(&num_files, sizeof(int), 1, bin_file); + fread(file.data, 1, file.size, bin_file); - char shared_folder_path[] = "/Volumes/SharedContainer"; - mkdir(shared_folder_path, 0755); + char file_path[MAX_PATH_LEN]; + snprintf(file_path, sizeof(file_path), "%s/%s", container_root, file.name); + + // Ensure all necessary directories exist + create_directories(file_path); + + FILE *out_file = fopen(file_path, "wb"); + if (out_file == NULL) { + perror("Error creating file in container"); + exit(EXIT_FAILURE); + } + + fwrite(file.data, 1, file.size, out_file); + fclose(out_file); + free(file.data); + + chmod(file_path, 0755); + } + + chmod(container_root, 0755); + + if (chdir(container_root) != 0) { + perror("Failed to change to container root directory"); + exit(1); + } + + if (setgid(config.container_gid) != 0) { + perror("Failed to set group ID"); + exit(1); + } + + if (setuid(config.container_uid) != 0) { + perror("Failed to set user ID"); + exit(1); + } + + apply_resource_limits(&config); + + // Updated sandbox profile + char sandbox_profile[1024]; + snprintf(sandbox_profile, sizeof(sandbox_profile), + "(version 1)" + "(deny default)" + "(allow process-fork)" + "(allow file-read*)" + "(allow file-write* (subpath \"%s\"))" + "(allow file-read* (subpath \"%s\"))" + "(allow file-read* (literal \"%s\"))" + "(allow file-read* (subpath \"/usr/lib\"))" + "(allow file-read* (subpath \"/usr/bin\"))" + "(allow file-read* (subpath \"/bin\"))" + "(allow file-read* (subpath \"/System\"))" + "(allow file-read* (subpath \"%s\"))" + "(allow file-read* (subpath \"/Applications/Xcode.app\"))" + "(allow file-write* (subpath \"%s\"))" + "(allow sysctl-read)" + "(allow mach-lookup)" + "(allow network-outbound (remote ip))" + "(allow network-inbound (local ip))" + "(allow process-exec (subpath \"/usr/bin\"))" + "(allow process-exec (subpath \"/Applications/Xcode.app\"))" + "(allow process-exec (subpath \"/bin\"))" + "(allow process-exec (subpath \"%s\"))", + container_root, container_root, bin_file_path, shared_mount_point, + shared_mount_point, container_root); + + char *error; + if (sandbox_init(sandbox_profile, 0, &error) != 0) { + fprintf(stderr, "sandbox_init failed: %s\n", error); + sandbox_free_error(error); + exit(1); + } + + printf("\n=== Container %s Terminal ===\n", bin_file_path); + printf("Enter commands (type 'exit' to quit, help for help):\n"); + printf("If you just ran the container ignore the first log file error"); + + // Start the network listener in a separate thread + pthread_t network_thread; + if (pthread_create(&network_thread, NULL, + (void *(*)(void *))start_network_listener, NULL) != 0) { + perror("Failed to create network listener thread"); + } + + if (config.start_config[0] != '\0') { + char start_config_path[MAX_PATH_LEN]; + snprintf(start_config_path, sizeof(start_config_path), "%s/%s", + container_root, config.start_config); + execute_start_config(start_config_path, container_root); + } + + char command[MAX_COMMAND_LEN]; + int command_index = 0; + int cursor_pos = 0; + + set_terminal_raw_mode(); + + pthread_t logger; + pthread_create(&logger, NULL, logger_thread, &config); + int network_thread_active = 1; + while (1) { + if (should_exit) { + break; + } + + printf("> "); + fflush(stdout); - char disk_image_path[MAX_PATH_LEN]; - snprintf(disk_image_path, sizeof(disk_image_path), "/tmp/container_disk_%d.dmg", getpid()); + int ch; + while ((ch = getchar()) != EOF) { + if (ch == 27) { // ESC key + getchar(); // Skip the next character + ch = getchar(); // Get the actual key code + if (ch == 'A') { // Up arrow + navigate_history(command, &command_index, &cursor_pos, -1); + } else if (ch == 'B') { // Down arrow + navigate_history(command, &command_index, &cursor_pos, 1); + } else if (ch == 'C') { // Right arrow + if (cursor_pos < command_index) { + move_cursor_right(1); + cursor_pos++; + } + } else if (ch == 'D') { // Left arrow + if (cursor_pos > 0) { + move_cursor_left(1); + cursor_pos--; + } + } + } else if (ch == 127 || ch == 8) { // Backspace or Delete + if (cursor_pos > 0) { + move_cursor_left(1); + clear_line(); + memmove(&command[cursor_pos - 1], &command[cursor_pos], + command_index - cursor_pos + 1); + command_index--; + cursor_pos--; + printf("%s", &command[cursor_pos]); + move_cursor_left(command_index - cursor_pos); + } + } else if (ch == '\n' || ch == '\r') { // Enter key + command[command_index] = '\0'; + set_terminal_canonical_mode(); + usleep(10000); // 10ms delay + + if (strcmp(command, "exit") == 0) + goto exit_loop; + if (strcmp(command, "debug") == 0) { + debug_mode = DEBUG_STEP; + printf("Entered debug mode. Type 'help' for debug commands.\n"); + } else if (strncmp(command, "scale", 5) == 0) { + long memory_soft_limit, memory_hard_limit; + int cpu_priority; + if (sscanf(command, "scale %ld %ld %d", &memory_soft_limit, + &memory_hard_limit, &cpu_priority) == 3) { + scale_container_resources(memory_soft_limit, memory_hard_limit, + cpu_priority); + } else { + printf("Usage: scale " + "\n"); + } + } else if (strncmp(command, "xs", 6) == 0) { + char *script_content = command + 7; + handle_script_command(script_content); + } else if (strncmp(command, "osxs", 4) == 0) { + char *filename = command + 5; + while (isspace(*filename)) { + filename++; + } + handle_script_file(filename); + } else if (strcmp(command, "autoscale") == 0) { + start_auto_scaling(&config); + } else if (strcmp(command, "status") == 0) { + print_current_resource_usage(&config); + } else if (strncmp(command, "br ", 3) == + 0) { // Note the space after 'br' + char *cmd = command + 3; // Skip "br " prefix + while (isspace(*cmd)) + cmd++; // Skip any additional whitespace + if (*cmd) { + int task_id = start_background_task(cmd, container_root); + if (task_id >= 0) { + printf("Started background task %d: %s\n", task_id, cmd); + } else { + printf("Failed to start background task\n"); + } + } else { + printf("Usage: br \n"); + } + } else if (strcmp(command, "pause") == 0) { + pause_background_tasks(); + printf("All background tasks paused\n"); + } else if (strcmp(command, "unpause") == 0) { + unpause_background_tasks(); + printf("All background tasks unpaused\n"); + } else if (strncmp(command, "wait ", 5) == 0) { + int task_id; + if (sscanf(command + 5, "%d", &task_id) == 1) { + wait_background_task(task_id); + } else { + printf("Usage: wait \n"); + } + } else if (strncmp(command, "wait", 4) == 0) { + int task_id; + if (sscanf(command + 5, "%d", &task_id) == 1) { + wait_background_task(task_id); + } else { + printf("Usage: wait \n"); + } + } else if (strcmp(command, "ps") == 0) { + show_background_tasks(); + } else if (strcmp(command, "network restart") == 0) { + stop_network_thread(network_thread, network_thread_active); + start_network_thread(network_thread, network_thread_active); + } else if (strcmp(command, "network start") == 0) { + start_network_thread(network_thread, network_thread_active); + } else if (strcmp(command, "network stop") == 0) { + stop_network_thread(network_thread, network_thread_active); + } else if (strcmp(command, "network status") == 0) { + printf("Network listener status: %s\n", + network_thread_active ? "running" : "stopped"); + } else if (strcmp(command, "help") == 0) { + printf("Commands:\n"); + printf(" exit: Exit the container\n"); + printf(" debug: Enter debug mode\n"); + printf(" scale " + ": Set memory limits and CPU priority\n"); + printf(" xs : Execute a script in the container\n"); + printf(" osxs : Execute a script file in the container\n"); + printf(" autoscale: Start automatic resource scaling\n"); + printf(" status: Print current resource usage\n"); + printf(" br : Start a background task\n"); + printf(" pause: Pause all background tasks\n"); + printf(" unpause: Unpause all background tasks\n"); + printf(" wait : Wait for a background task to finish\n"); + printf(" ps: List all background tasks\n"); + printf(" help: Print this help message\n"); + printf(" stop: Stops the container and saves its state\n"); + printf(" network restart: Restart the network listener\n"); + printf(" network start: Start the network listener note it is already started by default\n"); + printf(" network stop: Stop the network listener\n"); + printf(" network status: Print the status of the network listener\n"); + } else if (strcmp(command, "stop") == 0) { + printf("Stopping container...\n"); + + // Save the container state + char state_file_path[MAX_PATH_LEN]; + time_t now = time(NULL); + + // Construct the state file path using the Unix timestamp + snprintf(state_file_path, sizeof(state_file_path), "%s/%ld_%s", + container_root, now, bin_file_path); + + // state_file_path now includes the date + FILE *state_file = fopen(state_file_path, "wb"); + if (state_file == NULL) { + perror("Error creating container state file"); + } else { + // Save the container configuration + fwrite(&config, sizeof(ContainerConfig), 1, state_file); + + // Save the environment variables + fwrite(&container_state, sizeof(ContainerState), 1, state_file); + fclose(state_file); + printf("Container state saved to %s\n", state_file_path); + } + + goto stop_loop; + } else { + execute_command(command, container_root); + } - // Assign the volume name as the bin_file_path - char create_disk_command[MAX_COMMAND_LEN]; - struct stat st; - if (stat(bin_file_path, &st) == -1) { - perror("stat"); + printf("\n"); + add_to_history(command); + char log_file_path[MAX_PATH_LEN]; + snprintf(log_file_path, sizeof(log_file_path), "%s/log.txt", + container_root); + FILE *log_file = fopen(log_file_path, "a"); + if (log_file == NULL) { + perror("Failed to open log file"); + } else { + fprintf(log_file, "\nCommand History:\n"); + for (int i = 0; i < command_index; i++) { + fprintf(log_file, "Command %d: %s\n", i + 1, history.commands[i]); + } + fclose(log_file); + } + command_index = 0; + cursor_pos = 0; + set_terminal_raw_mode(); + printf("> "); + fflush(stdout); + } else { + if (command_index < MAX_COMMAND_LEN - 1) { + memmove(&command[cursor_pos + 1], &command[cursor_pos], + command_index - cursor_pos + 1); + command[cursor_pos] = ch; + command_index++; + cursor_pos++; + printf("%s", &command[cursor_pos - 1]); + move_cursor_left(command_index - cursor_pos); + } + } } + } - // Get the size of the file in bytes - off_t file_size = st.st_size; - - // Convert the size to gigabytes and add 1 GB - double size_in_gb = (double)file_size / (1024 * 1024 * 1024) + 1.0; - - // Format the size to two decimal places - snprintf(create_disk_command, sizeof(create_disk_command), - "hdiutil create -size %.2fg -fs HFS+ -volname \"%s\" %s", - size_in_gb, bin_file_path, disk_image_path); - - system(create_disk_command); +exit_loop: + set_terminal_canonical_mode(); + printf("Container terminated.\n"); + + // Clean up the threads + pthread_cancel(network_thread); + pthread_join(network_thread, NULL); + + pthread_cancel(logger); + pthread_join(logger, NULL); + + // Cleanup container state + for (int i = 0; i < container_state.num_env_vars; i++) { + free(container_state.environment_variables[i]); + } + free(container_state.environment_variables); - chmod(disk_image_path, 0644); +stop_loop: + set_terminal_canonical_mode(); + printf("Container stopped.\n"); - char mount_command[MAX_COMMAND_LEN]; - snprintf(mount_command, sizeof(mount_command), "hdiutil attach %s", disk_image_path); - system(mount_command); + pthread_cancel(network_thread); + pthread_join(network_thread, NULL); - // Use bin_file_path as the root volume name - char container_root[MAX_PATH_LEN]; - snprintf(container_root, sizeof(container_root), "/Volumes/%s", bin_file_path); + pthread_cancel(logger); + pthread_join(logger, NULL); - char shared_mount_point[MAX_PATH_LEN]; - snprintf(shared_mount_point, sizeof(shared_mount_point), "%s/shared", container_root); - symlink(shared_folder_path, shared_mount_point); + // Preserve the environment variables + for (int i = 0; i < container_state.num_env_vars; i++) { + setenv(container_state.environment_variables[i], NULL, 1); + } +} - for (int i = 0; i < num_files; i++) { - File file; - fread(file.name, sizeof(char), MAX_PATH_LEN, bin_file); - fread(&file.size, sizeof(size_t), 1, bin_file); +void ocreate_isolated_environment(FILE *bin_file, const char *bin_file_path) { + signal(SIGTERM, handle_signal); + signal(SIGINT, handle_signal); + signal(SIGSEGV, handle_signal); + ContainerConfig config; + fread(&config, sizeof(ContainerConfig), 1, bin_file); - file.data = malloc(file.size); - if (file.data == NULL) { - perror("Error allocating memory for file data"); - exit(EXIT_FAILURE); - } + int num_files; + fread(&num_files, sizeof(int), 1, bin_file); - fread(file.data, 1, file.size, bin_file); + char shared_folder_path[] = "/Volumes/SharedContainer"; + mkdir(shared_folder_path, 0755); - char file_path[MAX_PATH_LEN]; - snprintf(file_path, sizeof(file_path), "%s/%s", container_root, file.name); + char disk_image_path[MAX_PATH_LEN]; + snprintf(disk_image_path, sizeof(disk_image_path), + "/tmp/container_disk_%d.dmg", getpid()); - create_directories(file_path); + // Assign the volume name as the bin_file_path + char create_disk_command[MAX_COMMAND_LEN]; + struct stat st; + if (stat(bin_file_path, &st) == -1) { + perror("stat"); + } - FILE *out_file = fopen(file_path, "wb"); - if (out_file == NULL) { - perror("Error creating file in container"); - exit(EXIT_FAILURE); - } + // Get the size of the file in bytes + off_t file_size = st.st_size; - fwrite(file.data, 1, file.size, out_file); - fclose(out_file); - free(file.data); + // Convert the size to gigabytes and add 1 GB + double size_in_gb = (double)file_size / (1024 * 1024 * 1024) + 1.0; - chmod(file_path, 0755); - } + // Format the size to two decimal places + snprintf(create_disk_command, sizeof(create_disk_command), + "hdiutil create -size %.2fg -fs HFS+ -volname \"%s\" %s", size_in_gb, + bin_file_path, disk_image_path); - chmod(container_root, 0755); + system(create_disk_command); - if (chdir(container_root) != 0) { - perror("Failed to change to container root directory"); - exit(1); - } + chmod(disk_image_path, 0644); - if (setgid(config.container_gid) != 0) { - perror("Failed to set group ID"); - exit(1); - } + char mount_command[MAX_COMMAND_LEN]; + snprintf(mount_command, sizeof(mount_command), "hdiutil attach %s", + disk_image_path); + system(mount_command); - if (setuid(config.container_uid) != 0) { - perror("Failed to set user ID"); - exit(1); - } + // Use bin_file_path as the root volume name + char container_root[MAX_PATH_LEN]; + snprintf(container_root, sizeof(container_root), "/Volumes/%s", + bin_file_path); - apply_resource_limits(&config); - - char sandbox_profile[1024]; - snprintf(sandbox_profile, sizeof(sandbox_profile), - "(version 1)" - "(deny default)" - "(allow process-fork)" - "(allow file-read*)" - "(allow file-write* (subpath \"%s\"))" - "(allow file-read* (subpath \"%s\"))" - "(allow file-read* (literal \"%s\"))" - "(allow file-read* (subpath \"/usr/lib\"))" - "(allow file-read* (subpath \"/usr/bin\"))" - "(allow file-read* (subpath \"/bin\"))" - "(allow file-read* (subpath \"/System\"))" - "(allow file-read* (subpath \"%s\"))" - "(allow file-read* (subpath \"/Applications/Xcode.app\"))" - "(allow file-write* (subpath \"%s\"))" - "(allow sysctl-read)" - "(allow mach-lookup)" - "(allow network-outbound (remote ip))" - "(allow network-inbound (local ip))" - "(allow process-exec (subpath \"/usr/bin\"))" - "(allow process-exec (subpath \"/Applications/Xcode.app\"))" - "(allow process-exec (subpath \"/bin\"))" - "(allow process-exec (subpath \"%s\"))", - container_root, container_root, bin_file_path, - shared_mount_point, shared_mount_point, container_root - ); - - char *error; - if (sandbox_init(sandbox_profile, 0, &error) != 0) { - fprintf(stderr, "sandbox_init failed: %s\n", error); - sandbox_free_error(error); - exit(1); - } + char shared_mount_point[MAX_PATH_LEN]; + snprintf(shared_mount_point, sizeof(shared_mount_point), "%s/shared", + container_root); + symlink(shared_folder_path, shared_mount_point); - printf("\n=== Container %s Terminal ===\n", bin_file_path); - printf("Enter commands (type 'exit' to quit, help for help):\n"); - printf("If you just ran the container ignore the first log file error"); + for (int i = 0; i < num_files; i++) { + File file; + fread(file.name, sizeof(char), MAX_PATH_LEN, bin_file); + fread(&file.size, sizeof(size_t), 1, bin_file); - if (config.start_config[0] != '\0') { - char start_config_path[MAX_PATH_LEN]; - snprintf(start_config_path, sizeof(start_config_path), "%s/%s", container_root, config.start_config); - execute_start_config(start_config_path, container_root); + file.data = malloc(file.size); + if (file.data == NULL) { + perror("Error allocating memory for file data"); + exit(EXIT_FAILURE); } - char command[MAX_COMMAND_LEN]; - int command_index = 0; - int cursor_pos = 0; - - set_terminal_raw_mode(); + fread(file.data, 1, file.size, bin_file); - pthread_t logger; - pthread_create(&logger, NULL, logger_thread, &config); - - while (1) { - if (should_exit) { - break; - } - - printf("> "); - fflush(stdout); + char file_path[MAX_PATH_LEN]; + snprintf(file_path, sizeof(file_path), "%s/%s", container_root, file.name); + + create_directories(file_path); + + FILE *out_file = fopen(file_path, "wb"); + if (out_file == NULL) { + perror("Error creating file in container"); + exit(EXIT_FAILURE); + } + + fwrite(file.data, 1, file.size, out_file); + fclose(out_file); + free(file.data); + + chmod(file_path, 0755); + } + + chmod(container_root, 0755); + + if (chdir(container_root) != 0) { + perror("Failed to change to container root directory"); + exit(1); + } + + if (setgid(config.container_gid) != 0) { + perror("Failed to set group ID"); + exit(1); + } + + if (setuid(config.container_uid) != 0) { + perror("Failed to set user ID"); + exit(1); + } + + apply_resource_limits(&config); + + char sandbox_profile[1024]; + snprintf(sandbox_profile, sizeof(sandbox_profile), + "(version 1)" + "(deny default)" + "(allow process-fork)" + "(allow file-read*)" + "(allow file-write* (subpath \"%s\"))" + "(allow file-read* (subpath \"%s\"))" + "(allow file-read* (literal \"%s\"))" + "(allow file-read* (subpath \"/usr/lib\"))" + "(allow file-read* (subpath \"/usr/bin\"))" + "(allow file-read* (subpath \"/bin\"))" + "(allow file-read* (subpath \"/System\"))" + "(allow file-read* (subpath \"%s\"))" + "(allow file-read* (subpath \"/Applications/Xcode.app\"))" + "(allow file-write* (subpath \"%s\"))" + "(allow sysctl-read)" + "(allow mach-lookup)" + "(allow network-outbound (remote ip))" + "(allow network-inbound (local ip))" + "(allow process-exec (subpath \"/usr/bin\"))" + "(allow process-exec (subpath \"/Applications/Xcode.app\"))" + "(allow process-exec (subpath \"/bin\"))" + "(allow process-exec (subpath \"%s\"))", + container_root, container_root, bin_file_path, shared_mount_point, + shared_mount_point, container_root); + + char *error; + if (sandbox_init(sandbox_profile, 0, &error) != 0) { + fprintf(stderr, "sandbox_init failed: %s\n", error); + sandbox_free_error(error); + exit(1); + } + + printf("\n=== Container %s Terminal ===\n", bin_file_path); + printf("Enter commands (type 'exit' to quit, help for help):\n"); + printf("If you just ran the container ignore the first log file error"); + + if (config.start_config[0] != '\0') { + char start_config_path[MAX_PATH_LEN]; + snprintf(start_config_path, sizeof(start_config_path), "%s/%s", + container_root, config.start_config); + execute_start_config(start_config_path, container_root); + } + + char command[MAX_COMMAND_LEN]; + int command_index = 0; + int cursor_pos = 0; + + set_terminal_raw_mode(); + + pthread_t logger; + pthread_create(&logger, NULL, logger_thread, &config); + + while (1) { + if (should_exit) { + break; + } + + printf("> "); + fflush(stdout); - int ch; - while ((ch = getchar()) != EOF) { - if (ch == 27) { // ESC key - getchar(); // Skip the next character - ch = getchar(); // Get the actual key code - if (ch == 'A') { // Up arrow - navigate_history(command, &command_index, &cursor_pos, -1); - } else if (ch == 'B') { // Down arrow - navigate_history(command, &command_index, &cursor_pos, 1); - } else if (ch == 'C') { // Right arrow - if (cursor_pos < command_index) { - move_cursor_right(1); - cursor_pos++; - } - } else if (ch == 'D') { // Left arrow - if (cursor_pos > 0) { - move_cursor_left(1); - cursor_pos--; - } - } - } else if (ch == 127 || ch == 8) { // Backspace or Delete - if (cursor_pos > 0) { - move_cursor_left(1); - clear_line(); - memmove(&command[cursor_pos - 1], &command[cursor_pos], command_index - cursor_pos + 1); - command_index--; - cursor_pos--; - printf("%s", &command[cursor_pos]); - move_cursor_left(command_index - cursor_pos); - } - } else if (ch == '\n' || ch == '\r') { // Enter key - command[command_index] = '\0'; - set_terminal_canonical_mode(); - usleep(10000); // 10ms delay - - if (strcmp(command, "exit") == 0) goto exit_loop; - if (strcmp(command, "debug") == 0) { - debug_mode = DEBUG_STEP; - printf("Entered debug mode. Type 'help' for debug commands.\n"); - } else if (strncmp(command, "scale", 5) == 0) { - long memory_soft_limit, memory_hard_limit; - int cpu_priority; - if (sscanf(command, "scale %ld %ld %d", &memory_soft_limit, &memory_hard_limit, &cpu_priority) == 3) { - scale_container_resources(memory_soft_limit, memory_hard_limit, cpu_priority); - } else { - printf("Usage: scale \n"); - } - } else if (strncmp(command, "xs", 6) == 0) { - char *script_content = command + 7; - handle_script_command(script_content); - } else if (strncmp(command, "osxs", 4) == 0) { - char *filename = command + 5; - while (isspace(*filename)) { - filename++; - } - handle_script_file(filename); - } else if (strcmp(command, "autoscale") == 0) { - start_auto_scaling(&config); - } else if (strcmp(command, "status") == 0) { - print_current_resource_usage(&config); - } else if (strcmp(command, "help") == 0) { - printf("Commands:\n"); - printf(" exit: Exit the container\n"); - printf(" debug: Enter debug mode\n"); - printf(" scale : Set memory limits and CPU priority\n"); - printf(" xs : Execute a script in the container\n"); - printf(" osxs : Execute a script file in the container\n"); - printf(" autoscale: Start automatic resource scaling\n"); - printf(" status: Print current resource usage\n"); - printf(" help: Print this help message\n"); - printf(" stop: Stops the container and saves its state\n"); - } else if (strcmp(command, "stop") == 0) { - printf("Stopping container...\n"); - - // Save the container state - char state_file_path[MAX_PATH_LEN]; - snprintf(state_file_path, sizeof(state_file_path), "%s/%s", container_root, bin_file_path); - FILE *state_file = fopen(state_file_path, "wb"); - if (state_file == NULL) { - perror("Error creating container state file"); - } else { - // Save the container configuration - fwrite(&config, sizeof(ContainerConfig), 1, state_file); - - // Save the environment variables - fwrite(&container_state, sizeof(ContainerState), 1, state_file); - fclose(state_file); - printf("Container state saved to %s\n", state_file_path); - } - - goto stop_loop; - } else { - execute_command(command, container_root); - } - - printf("\n"); - add_to_history(command); - char log_file_path[MAX_PATH_LEN]; - snprintf(log_file_path, sizeof(log_file_path), "%s/log.txt", container_root); - FILE *log_file = fopen(log_file_path, "a"); - if (log_file == NULL) { - perror("Failed to open log file"); - printf("If you just ran the container ignore this"); - } else { - fprintf(log_file, "\nCommand History:\n"); - for (int i = 0; i < command_index; i++) { - fprintf(log_file, "Command %d: %s\n", i + 1, history.commands[i]); - } - fclose(log_file); - } - command_index = 0; - cursor_pos = 0; - set_terminal_raw_mode(); - printf("> "); - fflush(stdout); + int ch; + while ((ch = getchar()) != EOF) { + if (ch == 27) { // ESC key + getchar(); // Skip the next character + ch = getchar(); // Get the actual key code + if (ch == 'A') { // Up arrow + navigate_history(command, &command_index, &cursor_pos, -1); + } else if (ch == 'B') { // Down arrow + navigate_history(command, &command_index, &cursor_pos, 1); + } else if (ch == 'C') { // Right arrow + if (cursor_pos < command_index) { + move_cursor_right(1); + cursor_pos++; + } + } else if (ch == 'D') { // Left arrow + if (cursor_pos > 0) { + move_cursor_left(1); + cursor_pos--; + } + } + } else if (ch == 127 || ch == 8) { // Backspace or Delete + if (cursor_pos > 0) { + move_cursor_left(1); + clear_line(); + memmove(&command[cursor_pos - 1], &command[cursor_pos], + command_index - cursor_pos + 1); + command_index--; + cursor_pos--; + printf("%s", &command[cursor_pos]); + move_cursor_left(command_index - cursor_pos); + } + } else if (ch == '\n' || ch == '\r') { // Enter key + command[command_index] = '\0'; + set_terminal_canonical_mode(); + usleep(10000); // 10ms delay + + if (strcmp(command, "exit") == 0) + goto exit_loop; + if (strcmp(command, "debug") == 0) { + debug_mode = DEBUG_STEP; + printf("Entered debug mode. Type 'help' for debug commands.\n"); + } else if (strncmp(command, "scale", 5) == 0) { + long memory_soft_limit, memory_hard_limit; + int cpu_priority; + if (sscanf(command, "scale %ld %ld %d", &memory_soft_limit, + &memory_hard_limit, &cpu_priority) == 3) { + scale_container_resources(memory_soft_limit, memory_hard_limit, + cpu_priority); + } else { + printf("Usage: scale " + "\n"); + } + } else if (strncmp(command, "xs", 6) == 0) { + char *script_content = command + 7; + handle_script_command(script_content); + } else if (strncmp(command, "osxs", 4) == 0) { + char *filename = command + 5; + while (isspace(*filename)) { + filename++; + } + handle_script_file(filename); + } else if (strcmp(command, "autoscale") == 0) { + start_auto_scaling(&config); + } else if (strcmp(command, "status") == 0) { + print_current_resource_usage(&config); + } else if (strncmp(command, "br ", 3) == + 0) { // Note the space after 'br' + char *cmd = command + 3; // Skip "br " prefix + while (isspace(*cmd)) + cmd++; // Skip any additional whitespace + if (*cmd) { + int task_id = start_background_task(cmd, container_root); + if (task_id >= 0) { + printf("Started background task %d: %s\n", task_id, cmd); } else { - if (command_index < MAX_COMMAND_LEN - 1) { - memmove(&command[cursor_pos + 1], &command[cursor_pos], command_index - cursor_pos + 1); - command[cursor_pos] = ch; - command_index++; - cursor_pos++; - printf("%s", &command[cursor_pos - 1]); - move_cursor_left(command_index - cursor_pos); - } + printf("Failed to start background task\n"); } + } else { + printf("Usage: br \n"); + } + } else if (strcmp(command, "pause") == 0) { + pause_background_tasks(); + printf("All background tasks paused\n"); + } else if (strcmp(command, "unpause") == 0) { + unpause_background_tasks(); + printf("All background tasks unpaused\n"); + } else if (strncmp(command, "wait ", 5) == 0) { + int task_id; + if (sscanf(command + 5, "%d", &task_id) == 1) { + wait_background_task(task_id); + } else { + printf("Usage: wait \n"); + } + } else if (strncmp(command, "wait", 4) == 0) { + int task_id; + if (sscanf(command + 5, "%d", &task_id) == 1) { + wait_background_task(task_id); + } else { + printf("Usage: wait \n"); + } + } else if (strcmp(command, "ps") == 0) { + show_background_tasks(); + } else if (strcmp(command, "help") == 0) { + printf("Commands:\n"); + printf(" exit: Exit the container\n"); + printf(" debug: Enter debug mode\n"); + printf(" scale " + ": Set memory limits and CPU priority\n"); + printf(" xs : Execute a script in the container\n"); + printf(" osxs : Execute a script file in the container\n"); + printf(" autoscale: Start automatic resource scaling\n"); + printf(" status: Print current resource usage\n"); + printf(" br : Start a background task\n"); + printf(" pause: Pause all background tasks\n"); + printf(" unpause: Unpause all background tasks\n"); + printf(" wait : Wait for a background task to finish\n"); + printf(" ps: List all background tasks\n"); + printf(" help: Print this help message\n"); + printf(" stop: Stops the container and saves its state\n"); + } else if (strcmp(command, "stop") == 0) { + printf("Stopping container...\n"); + + // Save the container state + char state_file_path[MAX_PATH_LEN]; + snprintf(state_file_path, sizeof(state_file_path), "%s/%s", + container_root, bin_file_path); + FILE *state_file = fopen(state_file_path, "wb"); + if (state_file == NULL) { + perror("Error creating container state file"); + } else { + // Save the container configuration + fwrite(&config, sizeof(ContainerConfig), 1, state_file); + + // Save the environment variables + fwrite(&container_state, sizeof(ContainerState), 1, state_file); + fclose(state_file); + printf("Container state saved to %s\n", state_file_path); + } + + goto stop_loop; + } else { + execute_command(command, container_root); + } + + printf("\n"); + add_to_history(command); + char log_file_path[MAX_PATH_LEN]; + snprintf(log_file_path, sizeof(log_file_path), "%s/log.txt", + container_root); + FILE *log_file = fopen(log_file_path, "a"); + if (log_file == NULL) { + perror("Failed to open log file"); + printf("If you just ran the container ignore this"); + } else { + fprintf(log_file, "\nCommand History:\n"); + for (int i = 0; i < command_index; i++) { + fprintf(log_file, "Command %d: %s\n", i + 1, history.commands[i]); + } + fclose(log_file); + } + command_index = 0; + cursor_pos = 0; + set_terminal_raw_mode(); + printf("> "); + fflush(stdout); + } else { + if (command_index < MAX_COMMAND_LEN - 1) { + memmove(&command[cursor_pos + 1], &command[cursor_pos], + command_index - cursor_pos + 1); + command[cursor_pos] = ch; + command_index++; + cursor_pos++; + printf("%s", &command[cursor_pos - 1]); + move_cursor_left(command_index - cursor_pos); } + } } + } exit_loop: - set_terminal_canonical_mode(); - printf("Container terminated.\n"); + set_terminal_canonical_mode(); + printf("Container terminated.\n"); - pthread_cancel(logger); - pthread_join(logger, NULL); + pthread_cancel(logger); + pthread_join(logger, NULL); - for (int i = 0; i < container_state.num_env_vars; i++) { - free(container_state.environment_variables[i]); - } - free(container_state.environment_variables); - exit(0); + for (int i = 0; i < container_state.num_env_vars; i++) { + free(container_state.environment_variables[i]); + } + free(container_state.environment_variables); + exit(0); stop_loop: - set_terminal_canonical_mode(); - printf("Container stopped.\n"); - pthread_cancel(logger); - pthread_join(logger, NULL); - - // Preserve the environment variables - for (int i = 0; i < container_state.num_env_vars; i++) { - setenv(container_state.environment_variables[i], NULL, 1); - } + set_terminal_canonical_mode(); + printf("Container stopped.\n"); + pthread_cancel(logger); + pthread_join(logger, NULL); + + // Preserve the environment variables + for (int i = 0; i < container_state.num_env_vars; i++) { + setenv(container_state.environment_variables[i], NULL, 1); + } } void gcreate_isolated_environment(FILE *bin_file, const char *bin_file_path) { - signal(SIGTERM, handle_signal); - signal(SIGINT, handle_signal); - signal(SIGSEGV, handle_signal); - ContainerConfig config; - fread(&config, sizeof(ContainerConfig), 1, bin_file); - - int num_files; - fread(&num_files, sizeof(int), 1, bin_file); - - char shared_folder_path[] = "/Volumes/SharedContainer"; - mkdir(shared_folder_path, 0755); - - char disk_image_path[MAX_PATH_LEN]; - snprintf(disk_image_path, sizeof(disk_image_path), "/tmp/container_disk_%d.dmg", getpid()); - - // Assign the volume name as the bin_file_path - char create_disk_command[MAX_COMMAND_LEN]; - struct stat st; - if (stat(bin_file_path, &st) == -1) { - perror("stat"); - } - - // Get the size of the file in bytes - off_t file_size = st.st_size; - - // Convert the size to gigabytes and add 1 GB - double size_in_gb = (double)file_size / (1024 * 1024 * 1024) + 1.0; - - // Format the size to two decimal places - snprintf(create_disk_command, sizeof(create_disk_command), - "hdiutil create -size %.2fg -fs HFS+ -volname \"%s\" %s", - size_in_gb, bin_file_path, disk_image_path); - - system(create_disk_command); - - chmod(disk_image_path, 0644); - - char mount_command[MAX_COMMAND_LEN]; - snprintf(mount_command, sizeof(mount_command), "hdiutil attach %s", disk_image_path); - system(mount_command); - - // Use bin_file_path as the root volume name - char container_root[MAX_PATH_LEN]; - snprintf(container_root, sizeof(container_root), "/Volumes/%s", bin_file_path); - - char shared_mount_point[MAX_PATH_LEN]; - snprintf(shared_mount_point, sizeof(shared_mount_point), "%s/shared", container_root); - symlink(shared_folder_path, shared_mount_point); - - for (int i = 0; i < num_files; i++) { - File file; - fread(file.name, sizeof(char), MAX_PATH_LEN, bin_file); - fread(&file.size, sizeof(size_t), 1, bin_file); + signal(SIGTERM, handle_signal); + signal(SIGINT, handle_signal); + signal(SIGSEGV, handle_signal); + ContainerConfig config; + fread(&config, sizeof(ContainerConfig), 1, bin_file); - file.data = malloc(file.size); - if (file.data == NULL) { - perror("Error allocating memory for file data"); - exit(EXIT_FAILURE); - } + int num_files; + fread(&num_files, sizeof(int), 1, bin_file); - fread(file.data, 1, file.size, bin_file); + char shared_folder_path[] = "/Volumes/SharedContainer"; + mkdir(shared_folder_path, 0755); - char file_path[MAX_PATH_LEN]; - snprintf(file_path, sizeof(file_path), "%s/%s", container_root, file.name); + char disk_image_path[MAX_PATH_LEN]; + snprintf(disk_image_path, sizeof(disk_image_path), + "/tmp/container_disk_%d.dmg", getpid()); - create_directories(file_path); + // Assign the volume name as the bin_file_path + char create_disk_command[MAX_COMMAND_LEN]; + struct stat st; + if (stat(bin_file_path, &st) == -1) { + perror("stat"); + } - FILE *out_file = fopen(file_path, "wb"); - if (out_file == NULL) { - perror("Error creating file in container"); - exit(EXIT_FAILURE); - } + // Get the size of the file in bytes + off_t file_size = st.st_size; - fwrite(file.data, 1, file.size, out_file); - fclose(out_file); - free(file.data); + // Convert the size to gigabytes and add 1 GB + double size_in_gb = (double)file_size / (1024 * 1024 * 1024) + 1.0; - chmod(file_path, 0755); - } + // Format the size to two decimal places + snprintf(create_disk_command, sizeof(create_disk_command), + "hdiutil create -size %.2fg -fs HFS+ -volname \"%s\" %s", size_in_gb, + bin_file_path, disk_image_path); - chmod(container_root, 0755); + system(create_disk_command); - if (chdir(container_root) != 0) { - perror("Failed to change to container root directory"); - exit(1); - } + chmod(disk_image_path, 0644); - if (setgid(config.container_gid) != 0) { - perror("Failed to set group ID"); - exit(1); - } + char mount_command[MAX_COMMAND_LEN]; + snprintf(mount_command, sizeof(mount_command), "hdiutil attach %s", + disk_image_path); + system(mount_command); - if (setuid(config.container_uid) != 0) { - perror("Failed to set user ID"); - exit(1); - } + // Use bin_file_path as the root volume name + char container_root[MAX_PATH_LEN]; + snprintf(container_root, sizeof(container_root), "/Volumes/%s", + bin_file_path); - apply_resource_limits(&config); - - char sandbox_profile[2048]; - snprintf(sandbox_profile, sizeof(sandbox_profile), - "(version 1)" - "(allow default)" // Change default policy to allow - "(deny file-read* (subpath \"/Applications\"))" // Deny access to /Applications - "(deny file-read* (subpath \"/Users\"))" // Deny access to /Users - "(deny file-read* (subpath \"/sbin\"))" // Deny acces to sbin - "(allow process-fork)" - "(allow file-read*)" - "(allow file-write* (subpath \"%s\"))" - "(allow file-read* (subpath \"%s\"))" - "(allow file-read* (literal \"%s\"))" - "(allow file-read* (subpath \"/usr/lib\"))" - "(allow file-read* (subpath \"/usr/bin\"))" - "(allow file-read* (subpath \"/bin\"))" - "(allow file-read* (subpath \"/System\"))" - "(allow file-read* (subpath \"%s\"))" - "(allow file-read* (subpath \"/Applications/Xcode.app\"))" - "(allow file-write* (subpath \"%s\"))" - "(allow sysctl-read)" - "(allow mach-lookup)" - "(allow network-outbound (remote ip))" - "(allow network-inbound (local ip))" - "(allow process-exec (subpath \"/usr/bin\"))" - "(allow process-exec (subpath \"/Applications/Xcode.app\"))" - "(allow process-exec (subpath \"/bin\"))" - "(deny file-read* (subpath \"/Library\"))" // Deny access to /Library - "(allow file-read* (subpath \"/Library/Audio\"))" - "(allow file-read* (subpath \"/Library/Caches\"))" - "(allow file-read* (subpath \"/Library/Developer\"))" - "(allow file-read* (subpath \"/Library/DriverExtensions\"))" - "(allow file-read* (subpath \"/Library/Extensions\"))" - "(allow file-read* (subpath \"/Library/Fonts\"))" - "(allow file-read* (subpath \"/Library/Frameworks\"))" - "(allow file-read* (subpath \"/Library/Graphics\"))" - "(allow file-read* (subpath \"/Library/GPUBundles\"))" - "(allow file-read* (subpath \"/Library/Image Capture\"))" - "(allow file-read* (subpath \"/Library/Input Methods\"))" - "(allow file-read* (subpath \"/Library/LaunchAgents\"))" - "(allow file-read* (subpath \"/Library/KernelCollections\"))" - "(allow file-read* (subpath \"/Library/LaunchDaemons\"))" - "(allow file-read* (subpath \"/Library/Logs\"))" - "(allow file-read* (subpath \"/Library/OSAnalytics\"))" - "(allow file-read* (subpath \"/Library/Preferences\"))" - "(allow file-read* (subpath \"/Library/Scripts\"))" - "(allow file-read* (subpath \"/Library/Speech\"))" - "(allow file-read* (subpath \"/Library/WebServer\"))" - "(allow process-exec (subpath \"%s\"))", - container_root, container_root, bin_file_path, - shared_mount_point, shared_mount_point, container_root - ); - - - char *error; - if (sandbox_init(sandbox_profile, 0, &error) != 0) { - fprintf(stderr, "sandbox_init failed: %s\n", error); - sandbox_free_error(error); - exit(1); - } + char shared_mount_point[MAX_PATH_LEN]; + snprintf(shared_mount_point, sizeof(shared_mount_point), "%s/shared", + container_root); + symlink(shared_folder_path, shared_mount_point); - printf("\n=== Container %s Terminal ===\n", bin_file_path); - printf("Enter commands (type 'exit' to quit, help for help):\n"); - printf("If you just ran the container ignore the first log file error"); + for (int i = 0; i < num_files; i++) { + File file; + fread(file.name, sizeof(char), MAX_PATH_LEN, bin_file); + fread(&file.size, sizeof(size_t), 1, bin_file); - if (config.start_config[0] != '\0') { - char start_config_path[MAX_PATH_LEN]; - snprintf(start_config_path, sizeof(start_config_path), "%s/%s", container_root, config.start_config); - execute_start_config(start_config_path, container_root); + file.data = malloc(file.size); + if (file.data == NULL) { + perror("Error allocating memory for file data"); + exit(EXIT_FAILURE); } - char command[MAX_COMMAND_LEN]; - int command_index = 0; - int cursor_pos = 0; - - set_terminal_raw_mode(); - - pthread_t logger; - pthread_create(&logger, NULL, logger_thread, &config); - - while (1) { - if (should_exit) { - break; - } + fread(file.data, 1, file.size, bin_file); - printf("> "); - fflush(stdout); + char file_path[MAX_PATH_LEN]; + snprintf(file_path, sizeof(file_path), "%s/%s", container_root, file.name); + + create_directories(file_path); + + FILE *out_file = fopen(file_path, "wb"); + if (out_file == NULL) { + perror("Error creating file in container"); + exit(EXIT_FAILURE); + } + + fwrite(file.data, 1, file.size, out_file); + fclose(out_file); + free(file.data); + + chmod(file_path, 0755); + } + + chmod(container_root, 0755); + + if (chdir(container_root) != 0) { + perror("Failed to change to container root directory"); + exit(1); + } + + if (setgid(config.container_gid) != 0) { + perror("Failed to set group ID"); + exit(1); + } + + if (setuid(config.container_uid) != 0) { + perror("Failed to set user ID"); + exit(1); + } + + apply_resource_limits(&config); + + char sandbox_profile[2048]; + snprintf(sandbox_profile, sizeof(sandbox_profile), + "(version 1)" + "(allow default)" // Change default policy to allow + "(deny file-read* (subpath \"/Applications\"))" // Deny access to + // /Applications + "(deny file-read* (subpath \"/Users\"))" // Deny access to /Users + "(deny file-read* (subpath \"/sbin\"))" // Deny acces to sbin + "(allow process-fork)" + "(allow file-read*)" + "(allow file-write* (subpath \"%s\"))" + "(allow file-read* (subpath \"%s\"))" + "(allow file-read* (literal \"%s\"))" + "(allow file-read* (subpath \"/usr/lib\"))" + "(allow file-read* (subpath \"/usr/bin\"))" + "(allow file-read* (subpath \"/bin\"))" + "(allow file-read* (subpath \"/System\"))" + "(allow file-read* (subpath \"%s\"))" + "(allow file-read* (subpath \"/Applications/Xcode.app\"))" + "(allow file-write* (subpath \"%s\"))" + "(allow sysctl-read)" + "(allow mach-lookup)" + "(allow network-outbound (remote ip))" + "(allow network-inbound (local ip))" + "(allow process-exec (subpath \"/usr/bin\"))" + "(allow process-exec (subpath \"/Applications/Xcode.app\"))" + "(allow process-exec (subpath \"/bin\"))" + "(deny file-read* (subpath \"/Library\"))" // Deny access to /Library + "(allow file-read* (subpath \"/Library/Audio\"))" + "(allow file-read* (subpath \"/Library/Caches\"))" + "(allow file-read* (subpath \"/Library/Developer\"))" + "(allow file-read* (subpath \"/Library/DriverExtensions\"))" + "(allow file-read* (subpath \"/Library/Extensions\"))" + "(allow file-read* (subpath \"/Library/Fonts\"))" + "(allow file-read* (subpath \"/Library/Frameworks\"))" + "(allow file-read* (subpath \"/Library/Graphics\"))" + "(allow file-read* (subpath \"/Library/GPUBundles\"))" + "(allow file-read* (subpath \"/Library/Image Capture\"))" + "(allow file-read* (subpath \"/Library/Input Methods\"))" + "(allow file-read* (subpath \"/Library/LaunchAgents\"))" + "(allow file-read* (subpath \"/Library/KernelCollections\"))" + "(allow file-read* (subpath \"/Library/LaunchDaemons\"))" + "(allow file-read* (subpath \"/Library/Logs\"))" + "(allow file-read* (subpath \"/Library/OSAnalytics\"))" + "(allow file-read* (subpath \"/Library/Preferences\"))" + "(allow file-read* (subpath \"/Library/Scripts\"))" + "(allow file-read* (subpath \"/Library/Speech\"))" + "(allow file-read* (subpath \"/Library/WebServer\"))" + "(allow process-exec (subpath \"%s\"))", + container_root, container_root, bin_file_path, shared_mount_point, + shared_mount_point, container_root); + + char *error; + if (sandbox_init(sandbox_profile, 0, &error) != 0) { + fprintf(stderr, "sandbox_init failed: %s\n", error); + sandbox_free_error(error); + exit(1); + } + + printf("\n=== Container %s Terminal ===\n", bin_file_path); + printf("Enter commands (type 'exit' to quit, help for help):\n"); + printf("If you just ran the container ignore the first log file error"); + + if (config.start_config[0] != '\0') { + char start_config_path[MAX_PATH_LEN]; + snprintf(start_config_path, sizeof(start_config_path), "%s/%s", + container_root, config.start_config); + execute_start_config(start_config_path, container_root); + } + + char command[MAX_COMMAND_LEN]; + int command_index = 0; + int cursor_pos = 0; + + set_terminal_raw_mode(); + + pthread_t logger; + pthread_create(&logger, NULL, logger_thread, &config); + + while (1) { + if (should_exit) { + break; + } + + printf("> "); + fflush(stdout); - int ch; - while ((ch = getchar()) != EOF) { - if (ch == 27) { // ESC key - getchar(); // Skip the next character - ch = getchar(); // Get the actual key code - if (ch == 'A') { // Up arrow - navigate_history(command, &command_index, &cursor_pos, -1); - } else if (ch == 'B') { // Down arrow - navigate_history(command, &command_index, &cursor_pos, 1); - } else if (ch == 'C') { // Right arrow - if (cursor_pos < command_index) { - move_cursor_right(1); - cursor_pos++; - } - } else if (ch == 'D') { // Left arrow - if (cursor_pos > 0) { - move_cursor_left(1); - cursor_pos--; - } - } - } else if (ch == 127 || ch == 8) { // Backspace or Delete - if (cursor_pos > 0) { - move_cursor_left(1); - clear_line(); - memmove(&command[cursor_pos - 1], &command[cursor_pos], command_index - cursor_pos + 1); - command_index--; - cursor_pos--; - printf("%s", &command[cursor_pos]); - move_cursor_left(command_index - cursor_pos); - } - } else if (ch == '\n' || ch == '\r') { // Enter key - command[command_index] = '\0'; - set_terminal_canonical_mode(); - usleep(10000); // 10ms delay - - if (strcmp(command, "exit") == 0) goto exit_loop; - if (strcmp(command, "debug") == 0) { - debug_mode = DEBUG_STEP; - printf("Entered debug mode. Type 'help' for debug commands.\n"); - } else if (strncmp(command, "scale", 5) == 0) { - long memory_soft_limit, memory_hard_limit; - int cpu_priority; - if (sscanf(command, "scale %ld %ld %d", &memory_soft_limit, &memory_hard_limit, &cpu_priority) == 3) { - scale_container_resources(memory_soft_limit, memory_hard_limit, cpu_priority); - } else { - printf("Usage: scale \n"); - } - } else if (strncmp(command, "xs", 6) == 0) { - char *script_content = command + 7; - handle_script_command(script_content); - } else if (strncmp(command, "osxs", 4) == 0) { - char *filename = command + 5; - while (isspace(*filename)) { - filename++; - } - handle_script_file(filename); - } else if (strcmp(command, "autoscale") == 0) { - start_auto_scaling(&config); - } else if (strcmp(command, "status") == 0) { - print_current_resource_usage(&config); - } else if (strcmp(command, "help") == 0) { - printf("Commands:\n"); - printf(" exit: Exit the container\n"); - printf(" debug: Enter debug mode\n"); - printf(" scale : Set memory limits and CPU priority\n"); - printf(" xs : Execute a script in the container\n"); - printf(" osxs : Execute a script file in the container\n"); - printf(" autoscale: Start automatic resource scaling\n"); - printf(" status: Print current resource usage\n"); - printf(" help: Print this help message\n"); - printf(" stop: Stops the container and saves its state\n"); - } else if (strcmp(command, "stop") == 0) { - printf("Stopping container...\n"); - - // Save the container state - char state_file_path[MAX_PATH_LEN]; - snprintf(state_file_path, sizeof(state_file_path), "%s/%s", container_root, bin_file_path); - FILE *state_file = fopen(state_file_path, "wb"); - if (state_file == NULL) { - perror("Error creating container state file"); - } else { - // Save the container configuration - fwrite(&config, sizeof(ContainerConfig), 1, state_file); - - // Save the environment variables - fwrite(&container_state, sizeof(ContainerState), 1, state_file); - fclose(state_file); - printf("Container state saved to %s\n", state_file_path); - } - - goto stop_loop; - } else { - execute_command(command, container_root); - } - - printf("\n"); - add_to_history(command); - char log_file_path[MAX_PATH_LEN]; - snprintf(log_file_path, sizeof(log_file_path), "%s/log.txt", container_root); - FILE *log_file = fopen(log_file_path, "a"); - if (log_file == NULL) { - perror("Failed to open log file"); - printf("If you just ran the container ignore this"); - } else { - fprintf(log_file, "\nCommand History:\n"); - for (int i = 0; i < command_index; i++) { - fprintf(log_file, "Command %d: %s\n", i + 1, history.commands[i]); - } - fclose(log_file); - } - command_index = 0; - cursor_pos = 0; - set_terminal_raw_mode(); - printf("> "); - fflush(stdout); + int ch; + while ((ch = getchar()) != EOF) { + if (ch == 27) { // ESC key + getchar(); // Skip the next character + ch = getchar(); // Get the actual key code + if (ch == 'A') { // Up arrow + navigate_history(command, &command_index, &cursor_pos, -1); + } else if (ch == 'B') { // Down arrow + navigate_history(command, &command_index, &cursor_pos, 1); + } else if (ch == 'C') { // Right arrow + if (cursor_pos < command_index) { + move_cursor_right(1); + cursor_pos++; + } + } else if (ch == 'D') { // Left arrow + if (cursor_pos > 0) { + move_cursor_left(1); + cursor_pos--; + } + } + } else if (ch == 127 || ch == 8) { // Backspace or Delete + if (cursor_pos > 0) { + move_cursor_left(1); + clear_line(); + memmove(&command[cursor_pos - 1], &command[cursor_pos], + command_index - cursor_pos + 1); + command_index--; + cursor_pos--; + printf("%s", &command[cursor_pos]); + move_cursor_left(command_index - cursor_pos); + } + } else if (ch == '\n' || ch == '\r') { // Enter key + command[command_index] = '\0'; + set_terminal_canonical_mode(); + usleep(10000); // 10ms delay + + if (strcmp(command, "exit") == 0) + goto exit_loop; + if (strcmp(command, "debug") == 0) { + debug_mode = DEBUG_STEP; + printf("Entered debug mode. Type 'help' for debug commands.\n"); + } else if (strncmp(command, "scale", 5) == 0) { + long memory_soft_limit, memory_hard_limit; + int cpu_priority; + if (sscanf(command, "scale %ld %ld %d", &memory_soft_limit, + &memory_hard_limit, &cpu_priority) == 3) { + scale_container_resources(memory_soft_limit, memory_hard_limit, + cpu_priority); + } else { + printf("Usage: scale " + "\n"); + } + } else if (strncmp(command, "xs", 6) == 0) { + char *script_content = command + 7; + handle_script_command(script_content); + } else if (strncmp(command, "osxs", 4) == 0) { + char *filename = command + 5; + while (isspace(*filename)) { + filename++; + } + handle_script_file(filename); + } else if (strcmp(command, "autoscale") == 0) { + start_auto_scaling(&config); + } else if (strcmp(command, "status") == 0) { + print_current_resource_usage(&config); + } else if (strncmp(command, "br ", 3) == + 0) { // Note the space after 'br' + char *cmd = command + 3; // Skip "br " prefix + while (isspace(*cmd)) + cmd++; // Skip any additional whitespace + if (*cmd) { + int task_id = start_background_task(cmd, container_root); + if (task_id >= 0) { + printf("Started background task %d: %s\n", task_id, cmd); } else { - if (command_index < MAX_COMMAND_LEN - 1) { - memmove(&command[cursor_pos + 1], &command[cursor_pos], command_index - cursor_pos + 1); - command[cursor_pos] = ch; - command_index++; - cursor_pos++; - printf("%s", &command[cursor_pos - 1]); - move_cursor_left(command_index - cursor_pos); - } + printf("Failed to start background task\n"); } + } else { + printf("Usage: br \n"); + } + } else if (strcmp(command, "pause") == 0) { + pause_background_tasks(); + printf("All background tasks paused\n"); + } else if (strcmp(command, "unpause") == 0) { + unpause_background_tasks(); + printf("All background tasks unpaused\n"); + } else if (strncmp(command, "wait ", 5) == 0) { + int task_id; + if (sscanf(command + 5, "%d", &task_id) == 1) { + wait_background_task(task_id); + } else { + printf("Usage: wait \n"); + } + } else if (strncmp(command, "wait", 4) == 0) { + int task_id; + if (sscanf(command + 5, "%d", &task_id) == 1) { + wait_background_task(task_id); + } else { + printf("Usage: wait \n"); + } + } else if (strcmp(command, "ps") == 0) { + show_background_tasks(); + } else if (strcmp(command, "help") == 0) { + printf("Commands:\n"); + printf(" exit: Exit the container\n"); + printf(" debug: Enter debug mode\n"); + printf(" scale " + ": Set memory limits and CPU priority\n"); + printf(" xs : Execute a script in the container\n"); + printf(" osxs : Execute a script file in the container\n"); + printf(" autoscale: Start automatic resource scaling\n"); + printf(" status: Print current resource usage\n"); + printf(" br : Start a background task\n"); + printf(" pause: Pause all background tasks\n"); + printf(" unpause: Unpause all background tasks\n"); + printf(" wait : Wait for a background task to finish\n"); + printf(" ps: List all background tasks\n"); + printf(" help: Print this help message\n"); + printf(" stop: Stops the container and saves its state\n"); + } else if (strcmp(command, "stop") == 0) { + printf("Stopping container...\n"); + + // Save the container state + char state_file_path[MAX_PATH_LEN]; + snprintf(state_file_path, sizeof(state_file_path), "%s/%s", + container_root, bin_file_path); + FILE *state_file = fopen(state_file_path, "wb"); + if (state_file == NULL) { + perror("Error creating container state file"); + } else { + // Save the container configuration + fwrite(&config, sizeof(ContainerConfig), 1, state_file); + + // Save the environment variables + fwrite(&container_state, sizeof(ContainerState), 1, state_file); + fclose(state_file); + printf("Container state saved to %s\n", state_file_path); + } + + goto stop_loop; + } else { + execute_command(command, container_root); + } + + printf("\n"); + add_to_history(command); + char log_file_path[MAX_PATH_LEN]; + snprintf(log_file_path, sizeof(log_file_path), "%s/log.txt", + container_root); + FILE *log_file = fopen(log_file_path, "a"); + if (log_file == NULL) { + perror("Failed to open log file"); + printf("If you just ran the container ignore this"); + } else { + fprintf(log_file, "\nCommand History:\n"); + for (int i = 0; i < command_index; i++) { + fprintf(log_file, "Command %d: %s\n", i + 1, history.commands[i]); + } + fclose(log_file); + } + command_index = 0; + cursor_pos = 0; + set_terminal_raw_mode(); + printf("> "); + fflush(stdout); + } else { + if (command_index < MAX_COMMAND_LEN - 1) { + memmove(&command[cursor_pos + 1], &command[cursor_pos], + command_index - cursor_pos + 1); + command[cursor_pos] = ch; + command_index++; + cursor_pos++; + printf("%s", &command[cursor_pos - 1]); + move_cursor_left(command_index - cursor_pos); } + } } + } exit_loop: - set_terminal_canonical_mode(); - printf("Container terminated.\n"); + set_terminal_canonical_mode(); + printf("Container terminated.\n"); - pthread_cancel(logger); - pthread_join(logger, NULL); + pthread_cancel(logger); + pthread_join(logger, NULL); - for (int i = 0; i < container_state.num_env_vars; i++) { - free(container_state.environment_variables[i]); - } - free(container_state.environment_variables); - exit(0); + for (int i = 0; i < container_state.num_env_vars; i++) { + free(container_state.environment_variables[i]); + } + free(container_state.environment_variables); + exit(0); stop_loop: - set_terminal_canonical_mode(); - printf("Container stopped.\n"); - pthread_cancel(logger); - pthread_join(logger, NULL); - - // Preserve the environment variables - for (int i = 0; i < container_state.num_env_vars; i++) { - setenv(container_state.environment_variables[i], NULL, 1); - } + set_terminal_canonical_mode(); + printf("Container stopped.\n"); + pthread_cancel(logger); + pthread_join(logger, NULL); + + // Preserve the environment variables + for (int i = 0; i < container_state.num_env_vars; i++) { + setenv(container_state.environment_variables[i], NULL, 1); + } } size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) { - size_t written = fwrite(ptr, size, nmemb, stream); - return written; -} - -void download_file(const char* file_name) { - CURL *curl; - FILE *fp; - CURLcode res; - curl = curl_easy_init(); - if(curl) { - char save_path[256]; - snprintf(save_path, sizeof(save_path), "./%s", file_name); - fp = fopen(save_path, "wb"); - char url[256]; - snprintf(url, sizeof(url), "https://osxiec-file-server-1.onrender.com/files/%s", file_name); - curl_easy_setopt(curl, CURLOPT_URL, url); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); - res = curl_easy_perform(curl); - if(res != CURLE_OK) - fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); - fclose(fp); - curl_easy_cleanup(curl); - } + size_t written = fwrite(ptr, size, nmemb, stream); + return written; +} + +void download_file(const char *file_name) { + CURL *curl; + FILE *fp; + CURLcode res; + curl = curl_easy_init(); + if (curl) { + char save_path[256]; + snprintf(save_path, sizeof(save_path), "./%s", file_name); + fp = fopen(save_path, "wb"); + char url[256]; + snprintf(url, sizeof(url), + "https://bristle-sideways-blob.glitch.me/uploads/%s", file_name); + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); + res = curl_easy_perform(curl); + if (res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", + curl_easy_strerror(res)); + fclose(fp); + curl_easy_cleanup(curl); + } } struct Memory { - char *response; - size_t size; + char *response; + size_t size; }; -static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) { - size_t realsize = size * nmemb; - struct Memory *mem = (struct Memory *)userp; +static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, + void *userp) { + size_t realsize = size * nmemb; + struct Memory *mem = (struct Memory *)userp; - char *ptr = realloc(mem->response, mem->size + realsize + 1); - if (ptr == NULL) { - printf("Not enough memory (realloc returned NULL)\n"); - return 0; - } + char *ptr = realloc(mem->response, mem->size + realsize + 1); + if (ptr == NULL) { + printf("Not enough memory (realloc returned NULL)\n"); + return 0; + } - mem->response = ptr; - memcpy(&(mem->response[mem->size]), contents, realsize); - mem->size += realsize; - mem->response[mem->size] = 0; + mem->response = ptr; + memcpy(&(mem->response[mem->size]), contents, realsize); + mem->size += realsize; + mem->response[mem->size] = 0; - return realsize; + return realsize; } void search(const char *term) { - CURL *curl; - CURLcode res; - struct Memory chunk = {0}; + CURL *curl; + CURLcode res; + struct Memory chunk = {0}; - curl_global_init(CURL_GLOBAL_DEFAULT); - curl = curl_easy_init(); + curl_global_init(CURL_GLOBAL_DEFAULT); + curl = curl_easy_init(); - if (curl) { - char url[256]; - snprintf(url, sizeof(url), "https://osxiec-file-server-1.onrender.com/search?term=%s", term); + if (curl) { + char url[256]; + snprintf(url, sizeof(url), + "https://osxiec-file-server-1.onrender.com/search?term=%s", term); - curl_easy_setopt(curl, CURLOPT_URL, url); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); - res = curl_easy_perform(curl); + res = curl_easy_perform(curl); - if (res != CURLE_OK) { - fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); - } else { - printf("Search results:\n%s\n", chunk.response); - } - - curl_easy_cleanup(curl); + if (res != CURLE_OK) { + fprintf(stderr, "curl_easy_perform() failed: %s\n", + curl_easy_strerror(res)); + } else { + printf("Search results:\n%s\n", chunk.response); } - free(chunk.response); - curl_global_cleanup(); -} + curl_easy_cleanup(curl); + } -void upload_file(const char *filename, const char *username, const char *password, const char *description) { - CURL *curl; - CURLcode res; - struct curl_httppost *formpost = NULL; - struct curl_httppost *lastptr = NULL; + free(chunk.response); + curl_global_cleanup(); +} - curl_global_init(CURL_GLOBAL_ALL); +void upload_file(const char *filename, const char *username, + const char *password, const char *description) { + CURL *curl; + CURLcode res; + struct curl_httppost *formpost = NULL; + struct curl_httppost *lastptr = NULL; - // Create the form - curl_formadd(&formpost, &lastptr, - CURLFORM_COPYNAME, "file", - CURLFORM_FILE, filename, - CURLFORM_END); + curl_global_init(CURL_GLOBAL_ALL); - curl_formadd(&formpost, &lastptr, - CURLFORM_COPYNAME, "username", - CURLFORM_COPYCONTENTS, username, - CURLFORM_END); + // Create the form + curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "file", CURLFORM_FILE, + filename, CURLFORM_END); - curl_formadd(&formpost, &lastptr, - CURLFORM_COPYNAME, "password", - CURLFORM_COPYCONTENTS, password, - CURLFORM_END); + curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "username", + CURLFORM_COPYCONTENTS, username, CURLFORM_END); - curl_formadd(&formpost, &lastptr, - CURLFORM_COPYNAME, "description", - CURLFORM_COPYCONTENTS, description, - CURLFORM_END); + curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "password", + CURLFORM_COPYCONTENTS, password, CURLFORM_END); - curl = curl_easy_init(); - if (curl) { - curl_easy_setopt(curl, CURLOPT_URL, "https://osxiec-file-server-1.onrender.com/upload"); - curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost); + curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "description", + CURLFORM_COPYCONTENTS, description, CURLFORM_END); - // Perform the request - res = curl_easy_perform(curl); + curl = curl_easy_init(); + if (curl) { + curl_easy_setopt(curl, CURLOPT_URL, + "https://osxiec-file-server-1.onrender.com/upload"); + curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost); - // Check for errors - if (res != CURLE_OK) { - fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); - } else { - printf("File uploaded successfully.\n"); - } + // Perform the request + res = curl_easy_perform(curl); - // Clean up - curl_easy_cleanup(curl); + // Check for errors + if (res != CURLE_OK) { + fprintf(stderr, "curl_easy_perform() failed: %s\n", + curl_easy_strerror(res)); + } else { + printf("File uploaded successfully.\n"); } - curl_formfree(formpost); - curl_global_cleanup(); + // Clean up + curl_easy_cleanup(curl); + } + + curl_formfree(formpost); + curl_global_cleanup(); } -void convert_to_docker(const char *osxiec_file, const char *output_dir, const char *base_image, const char *custom_dockerfile) { - FILE *bin_file = fopen(osxiec_file, "rb"); - if (bin_file == NULL) { - perror("Error opening osxiec container file"); - return; +void convert_to_docker(const char *osxiec_file, const char *output_dir, + const char *base_image, const char *custom_dockerfile) { + FILE *bin_file = fopen(osxiec_file, "rb"); + if (bin_file == NULL) { + perror("Error opening osxiec container file"); + return; + } + + // Read container config + ContainerConfig config; + if (fread(&config, sizeof(ContainerConfig), 1, bin_file) != 1) { + perror("Error reading container config"); + fclose(bin_file); + return; + } + + // Create output directory + if (mkdir(output_dir, 0755) != 0 && errno != EEXIST) { + perror("Error creating output directory"); + fclose(bin_file); + return; + } + + FILE *dockerfile = NULL; + char dockerfile_path[MAX_PATH_LEN]; + + if (custom_dockerfile != NULL) { + char cmd[MAX_PATH_LEN * 2]; + snprintf(cmd, sizeof(cmd), "cp %s %s/Dockerfile", custom_dockerfile, + output_dir); + if (system(cmd) != 0) { + perror("Error copying custom Dockerfile"); + fclose(bin_file); + return; + } + } else { + snprintf(dockerfile_path, sizeof(dockerfile_path), "%s/Dockerfile", + output_dir); + dockerfile = fopen(dockerfile_path, "w"); + if (dockerfile == NULL) { + perror("Error creating Dockerfile"); + fclose(bin_file); + return; + } + + fprintf(dockerfile, "FROM %s\n\nWORKDIR /app\n\n", base_image); + } + + // Read number of files + int num_files; + if (fread(&num_files, sizeof(int), 1, bin_file) != 1) { + perror("Error reading number of files"); + if (dockerfile) + fclose(dockerfile); + fclose(bin_file); + return; + } + + char buffer[CHUNK_SIZE]; + for (int i = 0; i < num_files; i++) { + char file_name[MAX_PATH_LEN]; + size_t file_size; + + if (fread(file_name, sizeof(char), MAX_PATH_LEN, bin_file) != + MAX_PATH_LEN || + fread(&file_size, sizeof(size_t), 1, bin_file) != 1) { + perror("Error reading file metadata"); + if (dockerfile) + fclose(dockerfile); + fclose(bin_file); + return; } - // Read container config - ContainerConfig config; - if (fread(&config, sizeof(ContainerConfig), 1, bin_file) != 1) { - perror("Error reading container config"); - fclose(bin_file); - return; + // Create the directory structure + char *dir_name = dirname(file_name); + char dir_path[MAX_PATH_LEN]; + snprintf(dir_path, sizeof(dir_path), "%s/%s", output_dir, dir_name); + if (mkdir(dir_path, 0755) != 0 && errno != EEXIST) { + perror("Error creating directory"); + continue; } - // Create output directory - if (mkdir(output_dir, 0755) != 0 && errno != EEXIST) { - perror("Error creating output directory"); - fclose(bin_file); - return; + char file_path[MAX_PATH_LEN]; + snprintf(file_path, sizeof(file_path), "%s/%s", output_dir, file_name); + FILE *out_file = fopen(file_path, "wb"); + if (out_file == NULL) { + perror("Error creating file in output directory"); + continue; + } + + size_t remaining = file_size; + while (remaining > 0) { + size_t to_read = (remaining < CHUNK_SIZE) ? remaining : CHUNK_SIZE; + size_t bytes_read = fread(buffer, 1, to_read, bin_file); + if (bytes_read == 0) { + if (feof(bin_file)) { + fprintf(stderr, "Unexpected end of file\n"); + } else { + perror("Error reading file data"); + } + break; + } + fwrite(buffer, 1, bytes_read, out_file); + remaining -= bytes_read; } - FILE *dockerfile = NULL; - char dockerfile_path[MAX_PATH_LEN]; + fclose(out_file); - if (custom_dockerfile != NULL) { - char cmd[MAX_PATH_LEN * 2]; - snprintf(cmd, sizeof(cmd), "cp %s %s/Dockerfile", custom_dockerfile, output_dir); - if (system(cmd) != 0) { - perror("Error copying custom Dockerfile"); - fclose(bin_file); - return; - } - } else { - snprintf(dockerfile_path, sizeof(dockerfile_path), "%s/Dockerfile", output_dir); - dockerfile = fopen(dockerfile_path, "w"); - if (dockerfile == NULL) { - perror("Error creating Dockerfile"); - fclose(bin_file); - return; - } + if (custom_dockerfile == NULL) { + char relative_path[MAX_PATH_LEN]; + snprintf(relative_path, sizeof(relative_path), "%s", file_name); + fprintf(dockerfile, "COPY %s /app/%s\n", relative_path, relative_path); + } + } - fprintf(dockerfile, "FROM %s\n\nWORKDIR /app\n\n", base_image); + if (custom_dockerfile == NULL) { + fprintf(dockerfile, "\nENV MEMORY_SOFT_LIMIT=%ld\n", + config.memory_soft_limit); + fprintf(dockerfile, "ENV MEMORY_HARD_LIMIT=%ld\n", + config.memory_hard_limit); + fprintf(dockerfile, "ENV CPU_PRIORITY=%d\n", config.cpu_priority); + + if (strcmp(config.network_mode, "host") == 0) { + fprintf(dockerfile, "\n# Using host network mode\n"); + } else if (strcmp(config.network_mode, "bridge") == 0) { + fprintf(dockerfile, "\n# Using bridge network mode\n"); } - // Read number of files - int num_files; - if (fread(&num_files, sizeof(int), 1, bin_file) != 1) { - perror("Error reading number of files"); - if (dockerfile) fclose(dockerfile); - fclose(bin_file); - return; + if (config.start_config[0] != '\0') { + fprintf(dockerfile, + "\nCMD [\"/bin/sh\", \"-c\", \"while read cmd; do $cmd; done < " + "%s\"]\n", + config.start_config); + } else { + fprintf(dockerfile, "\nCMD [\"/bin/sh\"]\n"); } - char buffer[CHUNK_SIZE]; - for (int i = 0; i < num_files; i++) { - char file_name[MAX_PATH_LEN]; - size_t file_size; + fclose(dockerfile); + } - if (fread(file_name, sizeof(char), MAX_PATH_LEN, bin_file) != MAX_PATH_LEN || - fread(&file_size, sizeof(size_t), 1, bin_file) != 1) { - perror("Error reading file metadata"); - if (dockerfile) fclose(dockerfile); - fclose(bin_file); - return; - } + fclose(bin_file); - // Create the directory structure - char *dir_name = dirname(file_name); - char dir_path[MAX_PATH_LEN]; - snprintf(dir_path, sizeof(dir_path), "%s/%s", output_dir, dir_name); - if (mkdir(dir_path, 0755) != 0 && errno != EEXIST) { - perror("Error creating directory"); - continue; - } + printf("Docker container created in %s\n", output_dir); + printf("To build: docker build -t {container-name} %s\n", output_dir); + printf("To run: docker run -it {container-name}\n"); +} - char file_path[MAX_PATH_LEN]; - snprintf(file_path, sizeof(file_path), "%s/%s", output_dir, file_name); - FILE *out_file = fopen(file_path, "wb"); - if (out_file == NULL) { - perror("Error creating file in output directory"); - continue; - } +void clean_container_dmgs() { + DIR *dir; + struct dirent *entry; + char file_path[MAX_PATH_LEN]; + + dir = opendir("/tmp"); + if (dir == NULL) { + perror("Error opening /tmp directory"); + return; + } + + while ((entry = readdir(dir)) != NULL) { + if (strstr(entry->d_name, "container_") && strstr(entry->d_name, ".dmg")) { + snprintf(file_path, sizeof(file_path), "/tmp/%s", entry->d_name); + + // Remove the file + if (remove(file_path) == 0) { + printf("Removed: %s\n", file_path); + } else { + perror("Error removing file"); + } + } + } + + closedir(dir); +} - size_t remaining = file_size; - while (remaining > 0) { - size_t to_read = (remaining < CHUNK_SIZE) ? remaining : CHUNK_SIZE; - size_t bytes_read = fread(buffer, 1, to_read, bin_file); - if (bytes_read == 0) { - if (feof(bin_file)) { - fprintf(stderr, "Unexpected end of file\n"); - } else { - perror("Error reading file data"); - } - break; - } - fwrite(buffer, 1, bytes_read, out_file); - remaining -= bytes_read; - } +void deploy_container(const char *config_file, int deploy_port) { + FILE *file = fopen(config_file, "r"); + if (file == NULL) { + perror("Error opening config file"); + return; + } + + char source_dir[MAX_PATH_LEN] = {0}; + char container_file[MAX_PATH_LEN] = {0}; + char network_name[MAX_PATH_LEN] = {0}; + char start_config[MAX_PATH_LEN] = {0}; + char container_config[MAX_PATH_LEN] = {0}; + + char line[MAX_COMMAND_LEN]; + while (fgets(line, sizeof(line), file)) { + char *key = strtok(line, "="); + char *value = strtok(NULL, "\n"); + if (key && value) { + if (strcmp(key, "source_dir") == 0) { + strncpy(source_dir, value, MAX_PATH_LEN - 1); + } else if (strcmp(key, "container_file") == 0) { + strncpy(container_file, value, MAX_PATH_LEN - 1); + } else if (strcmp(key, "network_name") == 0) { + strncpy(network_name, value, MAX_PATH_LEN - 1); + } else if (strcmp(key, "start_config") == 0) { + strncpy(start_config, value, MAX_PATH_LEN - 1); + } + } + } + fclose(file); + + if (source_dir[0] == '\0' || container_file[0] == '\0' || + network_name[0] == '\0') { + fprintf(stderr, "Error: Missing required configuration in config file\n"); + return; + } + + // Contain the directory + containerize_directory(source_dir, container_file, + start_config[0] != '\0' ? start_config : NULL, + container_config[0] != '\0' ? container_config : NULL); + printf("Directory contents containerized into '%s'.\n", container_file); + + // Load network configuration + ContainerNetwork network = load_container_network(network_name); + + if (network.vlan_id == 0) { + fprintf(stderr, "Failed to load network configuration for %s\n", + network_name); + return; + } + + if (deploy_port != 0) { + // Set the global port variable + port = deploy_port; + } + + // Run the container + FILE *bin_file = fopen(container_file, "rb"); + if (bin_file == NULL) { + perror("Error opening binary file"); + return; + } + create_isolated_environment(bin_file, container_file, &network); + fclose(bin_file); +} - fclose(out_file); +void detach_container_images(const char *volume_name) { + printf("Detaching %s Containers\n", volume_name); + char command[MAX_PATH_LEN]; + snprintf(command, sizeof(command), "hdiutil detach -force /Volumes/%s", + volume_name); + int result = system(command); + if (result == 0) { + printf("Container disk image detached.\n"); + } else { + fprintf(stderr, "Failed to detach Container disk image.\n"); + } +} - if (custom_dockerfile == NULL) { - char relative_path[MAX_PATH_LEN]; - snprintf(relative_path, sizeof(relative_path), "%s", file_name); - fprintf(dockerfile, "COPY %s /app/%s\n", relative_path, relative_path); - } - } +void extract_container(const char *osxiec_file, const char *output_dir) { + FILE *bin_file = fopen(osxiec_file, "rb"); + if (bin_file == NULL) { + perror("Error opening osxiec container file"); + return; + } + + // Read container config + ContainerConfig config; + if (fread(&config, sizeof(ContainerConfig), 1, bin_file) != 1) { + perror("Error reading container config"); + fclose(bin_file); + return; + } - if (custom_dockerfile == NULL) { - fprintf(dockerfile, "\nENV MEMORY_SOFT_LIMIT=%ld\n", config.memory_soft_limit); - fprintf(dockerfile, "ENV MEMORY_HARD_LIMIT=%ld\n", config.memory_hard_limit); - fprintf(dockerfile, "ENV CPU_PRIORITY=%d\n", config.cpu_priority); - - if (strcmp(config.network_mode, "host") == 0) { - fprintf(dockerfile, "\n# Using host network mode\n"); - } else if (strcmp(config.network_mode, "bridge") == 0) { - fprintf(dockerfile, "\n# Using bridge network mode\n"); - } - - if (config.start_config[0] != '\0') { - fprintf(dockerfile, "\nCMD [\"/bin/sh\", \"-c\", \"while read cmd; do $cmd; done < %s\"]\n", config.start_config); - } else { - fprintf(dockerfile, "\nCMD [\"/bin/sh\"]\n"); - } - - fclose(dockerfile); - } + // Create output directory + if (mkdir(output_dir, 0755) != 0 && errno != EEXIST) { + perror("Error creating output directory"); + fclose(bin_file); + return; + } + // Read number of files + int num_files; + if (fread(&num_files, sizeof(int), 1, bin_file) != 1) { + perror("Error reading number of files"); fclose(bin_file); + return; + } - printf("Docker container created in %s\n", output_dir); - printf("To build: docker build -t {container-name} %s\n", output_dir); - printf("To run: docker run -it {container-name}\n"); -} + char buffer[CHUNK_SIZE]; + for (int i = 0; i < num_files; i++) { + char file_name[MAX_PATH_LEN]; + size_t file_size; -void clean_container_dmgs() { - DIR *dir; - struct dirent *entry; - char file_path[MAX_PATH_LEN]; - - dir = opendir("/tmp"); - if (dir == NULL) { - perror("Error opening /tmp directory"); - return; + if (fread(file_name, sizeof(char), MAX_PATH_LEN, bin_file) != + MAX_PATH_LEN || + fread(&file_size, sizeof(size_t), 1, bin_file) != 1) { + perror("Error reading file metadata"); + fclose(bin_file); + return; } - while ((entry = readdir(dir)) != NULL) { - if (strstr(entry->d_name, "container_") && strstr(entry->d_name, ".dmg")) { - snprintf(file_path, sizeof(file_path), "/tmp/%s", entry->d_name); + // Create the directory structure + char *dir_name = dirname(file_name); + char dir_path[MAX_PATH_LEN]; + snprintf(dir_path, sizeof(dir_path), "%s/%s", output_dir, dir_name); + if (mkdir(dir_path, 0755) != 0 && errno != EEXIST) { + perror("Error creating directory"); + continue; + } - // Remove the file - if (remove(file_path) == 0) { - printf("Removed: %s\n", file_path); - } else { - perror("Error removing file"); - } + char file_path[MAX_PATH_LEN]; + snprintf(file_path, sizeof(file_path), "%s/%s", output_dir, file_name); + FILE *out_file = fopen(file_path, "wb"); + if (out_file == NULL) { + perror("Error creating file in output directory"); + continue; + } + + size_t remaining = file_size; + while (remaining > 0) { + size_t to_read = (remaining < CHUNK_SIZE) ? remaining : CHUNK_SIZE; + size_t bytes_read = fread(buffer, 1, to_read, bin_file); + if (bytes_read == 0) { + if (feof(bin_file)) { + fprintf(stderr, "Unexpected end of file\n"); + } else { + perror("Error reading file data"); } + break; + } + fwrite(buffer, 1, bytes_read, out_file); + remaining -= bytes_read; } - closedir(dir); + fclose(out_file); + } + + fclose(bin_file); + + printf("Container extracted to %s\n", output_dir); } -void deploy_container(const char *config_file, int deploy_port) { - FILE *file = fopen(config_file, "r"); - if (file == NULL) { - perror("Error opening config file"); - return; - } +typedef enum { ENTRY_FILE, ENTRY_DIR } EntryType; - char source_dir[MAX_PATH_LEN] = {0}; - char container_file[MAX_PATH_LEN] = {0}; - char network_name[MAX_PATH_LEN] = {0}; - char start_config[MAX_PATH_LEN] = {0}; - char container_config[MAX_PATH_LEN] = {0}; +void create_directory_if_needed(const char *path) { + char dir[MAX_PATH_LEN]; + snprintf(dir, sizeof(dir), "%s", path); - char line[MAX_COMMAND_LEN]; - while (fgets(line, sizeof(line), file)) { - char *key = strtok(line, "="); - char *value = strtok(NULL, "\n"); - if (key && value) { - if (strcmp(key, "source_dir") == 0) { - strncpy(source_dir, value, MAX_PATH_LEN - 1); - } else if (strcmp(key, "container_file") == 0) { - strncpy(container_file, value, MAX_PATH_LEN - 1); - } else if (strcmp(key, "network_name") == 0) { - strncpy(network_name, value, MAX_PATH_LEN - 1); - } else if (strcmp(key, "start_config") == 0) { - strncpy(start_config, value, MAX_PATH_LEN - 1); - } + for (char *p = dir + 1; *p; p++) { + if (*p == '/') { + *p = '\0'; + if (access(dir, F_OK) != 0) { // Check if the directory exists + if (mkdir(dir, 0777) != 0 && errno != EEXIST) { + perror("Error creating directory"); + exit(EXIT_FAILURE); } + } + *p = '/'; } - fclose(file); - - if (source_dir[0] == '\0' || container_file[0] == '\0' || network_name[0] == '\0') { - fprintf(stderr, "Error: Missing required configuration in config file\n"); - return; + } + if (access(dir, F_OK) != 0) { // Check if the directory exists + if (mkdir(dir, 0777) != 0 && errno != EEXIST) { + perror("Error creating directory"); + exit(EXIT_FAILURE); } + } +} - // Contain the directory - containerize_directory(source_dir, container_file, start_config[0] != '\0' ? start_config : NULL, container_config[0] != '\0' ? container_config : NULL); - printf("Directory contents containerized into '%s'.\n", container_file); - - // Load network configuration - ContainerNetwork network = load_container_network(network_name); +void convert_to_oci(const char *osxiec_file, const char *output_dir, + const char *arch, const char *author, const char *created) { + if (output_dir == NULL) { + perror("Output directory is NULL"); + return; + } + + FILE *bin_file = fopen(osxiec_file, "rb"); + if (bin_file == NULL) { + perror("Error opening osxiec container file"); + return; + } + + // Read ContainerConfig + ContainerConfig config; + if (fread(&config, sizeof(ContainerConfig), 1, bin_file) != 1) { + perror("Error reading ContainerConfig"); + fclose(bin_file); + return; + } - if (network.vlan_id == 0) { - fprintf(stderr, "Failed to load network configuration for %s\n", network_name); - return; - } + // Read number of entries + int num_entries; + if (fread(&num_entries, sizeof(int), 1, bin_file) != 1) { + perror("Error reading number of entries"); + fclose(bin_file); + return; + } - if (deploy_port != 0) { - // Set the global port variable - port = deploy_port; - } + printf("Number of entries: %d\n", num_entries); - // Run the container - FILE *bin_file = fopen(container_file, "rb"); - if (bin_file == NULL) { - perror("Error opening binary file"); - return; - } - create_isolated_environment(bin_file, container_file, &network); + // Create OCI layout file + char layout_path[MAX_PATH_LEN]; + snprintf(layout_path, sizeof(layout_path), "%s/oci-layout", output_dir); + FILE *layout_file = fopen(layout_path, "w"); + if (layout_file == NULL) { + perror("Error creating OCI layout file"); fclose(bin_file); - + return; + } + fprintf(layout_file, "{\"imageLayoutVersion\": \"1.0.0\"}"); + fclose(layout_file); + + // Create blobs directory + char blobs_dir[MAX_PATH_LEN]; + snprintf(blobs_dir, sizeof(blobs_dir), "%s/blobs/sha256", output_dir); + create_directory_if_needed(blobs_dir); + + // Prepare config JSON + json_object *config_json = json_object_new_object(); + json_object_object_add(config_json, "os", json_object_new_string("macOS")); + json_object_object_add(config_json, "architecture", + json_object_new_string(arch)); + json_object_object_add(config_json, "author", json_object_new_string(author)); + json_object_object_add(config_json, "created", + json_object_new_string(created)); + json_object_object_add(config_json, "ociVersion", + json_object_new_string("1.0.0")); + json_object_object_add(config_json, "root", json_object_new_object()); + struct json_object *root_object = json_object_new_object(); + struct json_object *path_object = json_object_new_string(""); + json_object_object_add(root_object, "path", path_object); + json_object_object_add(config_json, "root", root_object); + + // Create config blob + char config_blob_path[MAX_PATH_LEN]; + snprintf(config_blob_path, sizeof(config_blob_path), "%s/config.json", + blobs_dir); + FILE *config_blob = fopen(config_blob_path, "w"); + if (config_blob == NULL) { + perror("Error creating config blob"); + fclose(bin_file); + json_object_put(config_json); + return; + } + fprintf(config_blob, "%s", + json_object_to_json_string_ext(config_json, JSON_C_TO_STRING_PRETTY)); + fclose(config_blob); + + // Prepare manifest JSON + json_object *manifest_json = json_object_new_object(); + json_object_object_add(manifest_json, "schemaVersion", + json_object_new_int(2)); + json_object_object_add( + manifest_json, "mediaType", + json_object_new_string("application/vnd.oci.image.manifest.v1+json")); + json_object_object_add(manifest_json, "config", json_object_new_object()); + json_object_object_add( + json_object_object_get(manifest_json, "config"), "mediaType", + json_object_new_string("application/vnd.oci.image.config.v1+json")); + json_object_object_add( + json_object_object_get(manifest_json, "config"), "size", + json_object_new_int64(strlen(json_object_to_json_string(config_json)))); + json_object_object_add( + json_object_object_get(manifest_json, "config"), "digest", + json_object_new_string( + "sha256:configdigest")); // Replace with actual digest + + json_object *layers_array = json_object_new_array(); + json_object_object_add(manifest_json, "layers", layers_array); + + char buffer[CHUNK_SIZE]; + for (int i = 0; i < num_entries; i++) { + char path[MAX_PATH_LEN]; + if (fread(path, sizeof(char), MAX_PATH_LEN, bin_file) != MAX_PATH_LEN) { + perror("Error reading entry path"); + fclose(bin_file); + json_object_put(config_json); + json_object_put(manifest_json); + return; + } + + printf("Entry %d: path=%s\n", i, path); + + // For files + size_t file_size; + if (fread(&file_size, sizeof(size_t), 1, bin_file) != 1) { + perror("Error reading file size"); + fclose(bin_file); + json_object_put(config_json); + json_object_put(manifest_json); + return; + } + + // Create blob for each file + char blob_path[MAX_PATH_LEN]; + snprintf(blob_path, sizeof(blob_path), "%s/%s", blobs_dir, path); + // Ensure the directory exists before creating the file + char *last_slash = strrchr(blob_path, '/'); + if (last_slash != NULL) { + *last_slash = + '\0'; // Temporarily null-terminate the path to create directories + create_directory_if_needed(blob_path); + *last_slash = '/'; // Restore the path + } + + FILE *blob_file = fopen(blob_path, "wb"); + if (blob_file == NULL) { + perror("Error creating blob file"); + continue; + } + + size_t remaining = file_size; + while (remaining > 0) { + size_t to_read = (remaining < CHUNK_SIZE) ? remaining : CHUNK_SIZE; + size_t bytes_read = fread(buffer, 1, to_read, bin_file); + if (bytes_read == 0) { + if (feof(bin_file)) { + fprintf(stderr, "Unexpected end of file while reading file data\n"); + } else { + perror("Error reading file data"); + } + break; + } + fwrite(buffer, 1, bytes_read, blob_file); + remaining -= bytes_read; + } + fclose(blob_file); + + // Add layer to manifest + json_object *layer = json_object_new_object(); + json_object_object_add( + layer, "mediaType", + json_object_new_string("application/vnd.oci.image.layer.v1.tar")); + json_object_object_add(layer, "size", json_object_new_int64(file_size)); + json_object_object_add( + layer, "digest", + json_object_new_string( + "sha256:layerdigest")); // Replace with actual digest + json_object_array_add(layers_array, layer); + } + + // Write manifest + char manifest_path[MAX_PATH_LEN]; + snprintf(manifest_path, sizeof(manifest_path), "%s/manifest.json", + output_dir); + FILE *manifest_file = fopen(manifest_path, "w"); + if (manifest_file == NULL) { + perror("Error creating manifest file"); + fclose(bin_file); + json_object_put(config_json); + json_object_put(manifest_json); + return; + } + fprintf( + manifest_file, "%s", + json_object_to_json_string_ext(manifest_json, JSON_C_TO_STRING_PRETTY)); + fclose(manifest_file); + + fclose(bin_file); + json_object_put(config_json); + json_object_put(manifest_json); + printf("OCI container structure created in %s\n", output_dir); } -void detach_container_images(const char *volume_name) { - printf("Detaching %s Containers\n", volume_name); - char command[MAX_PATH_LEN]; - snprintf(command, sizeof(command), "hdiutil detach -force /Volumes/%s", volume_name); - int result = system(command); - if (result == 0) { - printf("Container disk image detached.\n"); - } else { - fprintf(stderr, "Failed to detach Container disk image.\n"); - } -} +char *find_latest_bin_file(const char *volume_name) { + DIR *dir; + struct dirent *entry; + char *latest_file_path = NULL; + time_t latest_timestamp = 0; + char search_directory[MAX_PATH_LEN]; -void extract_container(const char *osxiec_file, const char *output_dir) { - FILE *bin_file = fopen(osxiec_file, "rb"); - if (bin_file == NULL) { - perror("Error opening osxiec container file"); - return; - } + // Format the search directory path + snprintf(search_directory, sizeof(search_directory), "/Volumes/%s", + volume_name); - // Read container config - ContainerConfig config; - if (fread(&config, sizeof(ContainerConfig), 1, bin_file) != 1) { - perror("Error reading container config"); - fclose(bin_file); - return; - } + if ((dir = opendir(search_directory)) == NULL) { + perror("Unable to open directory"); + return NULL; + } - // Create output directory - if (mkdir(output_dir, 0755) != 0 && errno != EEXIST) { - perror("Error creating output directory"); - fclose(bin_file); - return; - } + while ((entry = readdir(dir)) != NULL) { + // Check if the file ends with ".bin" + if (strstr(entry->d_name, ".bin") != NULL) { + // Extract Unix timestamp from the beginning of the filename + time_t timestamp = atol(entry->d_name); - // Read number of files - int num_files; - if (fread(&num_files, sizeof(int), 1, bin_file) != 1) { - perror("Error reading number of files"); - fclose(bin_file); - return; + if (timestamp > latest_timestamp) { + latest_timestamp = timestamp; + + // Free the previous path and allocate for the new one + free(latest_file_path); + latest_file_path = malloc(MAX_PATH_LEN); + snprintf(latest_file_path, MAX_PATH_LEN, "%s/%s", search_directory, + entry->d_name); + } } + } + closedir(dir); - char buffer[CHUNK_SIZE]; - for (int i = 0; i < num_files; i++) { - char file_name[MAX_PATH_LEN]; - size_t file_size; + return latest_file_path; +} - if (fread(file_name, sizeof(char), MAX_PATH_LEN, bin_file) != MAX_PATH_LEN || - fread(&file_size, sizeof(size_t), 1, bin_file) != 1) { - perror("Error reading file metadata"); - fclose(bin_file); - return; - } +int copy_file(const char *source, const char *destination_folder, + const char *new_file_name) { + // Construct the full path for the destination file + char destination[MAX_PATH_LEN]; + snprintf(destination, sizeof(destination), "%s/%s", destination_folder, + new_file_name); + + FILE *src = fopen(source, "rb"); + if (src == NULL) { + perror("Error opening source file"); + return -1; + } + + FILE *dst = fopen(destination, "wb"); + if (dst == NULL) { + perror("Error opening destination file"); + fclose(src); + return -1; + } + + char buffer[BUFSIZ]; + size_t n; + while ((n = fread(buffer, 1, sizeof(buffer), src)) > 0) { + if (fwrite(buffer, 1, n, dst) != n) { + perror("Error writing to destination file"); + fclose(src); + fclose(dst); + return -1; + } + } + + fclose(src); + fclose(dst); + return 0; +} - // Create the directory structure - char *dir_name = dirname(file_name); - char dir_path[MAX_PATH_LEN]; - snprintf(dir_path, sizeof(dir_path), "%s/%s", output_dir, dir_name); - if (mkdir(dir_path, 0755) != 0 && errno != EEXIST) { - perror("Error creating directory"); - continue; - } +size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp) { + size_t total_size = size * nmemb; + char **response_ptr = (char **)userp; + + // Allocate or reallocate memory for the response + char *temp = + realloc(*response_ptr, + total_size + (*response_ptr ? strlen(*response_ptr) : 0) + 1); + if (temp == NULL) { + fprintf(stderr, "Failed to allocate memory.\n"); + return 0; // Abort the transfer + } + *response_ptr = temp; + + // Append new data to the response buffer + if (*response_ptr) { + memcpy(*response_ptr + (*response_ptr ? strlen(*response_ptr) : 0), + contents, total_size); + (*response_ptr)[total_size + (*response_ptr ? strlen(*response_ptr) : 0)] = + '\0'; // Null-terminate + } + + return total_size; +} - char file_path[MAX_PATH_LEN]; - snprintf(file_path, sizeof(file_path), "%s/%s", output_dir, file_name); - FILE *out_file = fopen(file_path, "wb"); - if (out_file == NULL) { - perror("Error creating file in output directory"); - continue; - } +char *fetch_latest_version(void) { + CURL *curl; + CURLcode res; + char *latest_version = NULL; - size_t remaining = file_size; - while (remaining > 0) { - size_t to_read = (remaining < CHUNK_SIZE) ? remaining : CHUNK_SIZE; - size_t bytes_read = fread(buffer, 1, to_read, bin_file); - if (bytes_read == 0) { - if (feof(bin_file)) { - fprintf(stderr, "Unexpected end of file\n"); - } else { - perror("Error reading file data"); - } - break; - } - fwrite(buffer, 1, bytes_read, out_file); - remaining -= bytes_read; - } + curl = curl_easy_init(); + if (curl) { + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "User-Agent: osxiec-update-checker"); - fclose(out_file); - } + curl_easy_setopt( + curl, CURLOPT_URL, + "https://api.github.com/repos/Okerew/osxiec/releases/latest"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); - fclose(bin_file); + char *response = NULL; - printf("Container extracted to %s\n", output_dir); -} + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); -typedef enum { ENTRY_FILE, ENTRY_DIR } EntryType; + res = curl_easy_perform(curl); -void create_directory_if_needed(const char *path) { - char dir[MAX_PATH_LEN]; - snprintf(dir, sizeof(dir), "%s", path); - - for (char *p = dir + 1; *p; p++) { - if (*p == '/') { - *p = '\0'; - if (access(dir, F_OK) != 0) { // Check if the directory exists - if (mkdir(dir, 0777) != 0 && errno != EEXIST) { - perror("Error creating directory"); - exit(EXIT_FAILURE); - } - } - *p = '/'; - } - } - if (access(dir, F_OK) != 0) { // Check if the directory exists - if (mkdir(dir, 0777) != 0 && errno != EEXIST) { - perror("Error creating directory"); - exit(EXIT_FAILURE); - } - } -} - -void convert_to_oci(const char *osxiec_file, const char *output_dir, const char *arch, const char *author, const char *created) { - if (output_dir == NULL) { - perror("Output directory is NULL"); - return; - } - - FILE *bin_file = fopen(osxiec_file, "rb"); - if (bin_file == NULL) { - perror("Error opening osxiec container file"); - return; + if (res == CURLE_OK) { + if (response) { + struct json_object *parsed_json = json_tokener_parse(response); + if (parsed_json == NULL) { + fprintf(stderr, "Failed to parse JSON.\n"); + } else { + struct json_object *tag_name; + if (json_object_object_get_ex(parsed_json, "tag_name", &tag_name)) { + const char *version = json_object_get_string(tag_name); + latest_version = strdup(version); + } else { + fprintf(stderr, "JSON does not contain 'tag_name' field.\n"); + } + + json_object_put(parsed_json); + } + } else { + fprintf(stderr, "No response data received.\n"); + } + } else { + fprintf(stderr, "CURL request failed: %s\n", curl_easy_strerror(res)); } - // Read ContainerConfig - ContainerConfig config; - if (fread(&config, sizeof(ContainerConfig), 1, bin_file) != 1) { - perror("Error reading ContainerConfig"); - fclose(bin_file); - return; - } + curl_easy_cleanup(curl); + curl_slist_free_all(headers); + free(response); + } else { + fprintf(stderr, "Failed to initialize CURL.\n"); + } - // Read number of entries - int num_entries; - if (fread(&num_entries, sizeof(int), 1, bin_file) != 1) { - perror("Error reading number of entries"); - fclose(bin_file); - return; - } + return latest_version; +} - printf("Number of entries: %d\n", num_entries); +int compare_versions(const char *v1, const char *v2) { + return strcmp(v1, v2) == 0 ? 0 : -1; +} - // Create OCI layout file - char layout_path[MAX_PATH_LEN]; - snprintf(layout_path, sizeof(layout_path), "%s/oci-layout", output_dir); - FILE *layout_file = fopen(layout_path, "w"); - if (layout_file == NULL) { - perror("Error creating OCI layout file"); - fclose(bin_file); - return; - } - fprintf(layout_file, "{\"imageLayoutVersion\": \"1.0.0\"}"); - fclose(layout_file); - - // Create blobs directory - char blobs_dir[MAX_PATH_LEN]; - snprintf(blobs_dir, sizeof(blobs_dir), "%s/blobs/sha256", output_dir); - create_directory_if_needed(blobs_dir); - - // Prepare config JSON - json_object *config_json = json_object_new_object(); - json_object_object_add(config_json, "os", json_object_new_string("macOS")); - json_object_object_add(config_json, "architecture", json_object_new_string(arch)); - json_object_object_add(config_json, "author", json_object_new_string(author)); - json_object_object_add(config_json, "created", json_object_new_string(created)); - json_object_object_add(config_json, "ociVersion", json_object_new_string("1.0.0")); - json_object_object_add(config_json, "root", json_object_new_object()); - struct json_object *root_object = json_object_new_object(); - struct json_object *path_object = json_object_new_string(""); - json_object_object_add(root_object, "path", path_object); - json_object_object_add(config_json, "root", root_object); - - - // Create config blob - char config_blob_path[MAX_PATH_LEN]; - snprintf(config_blob_path, sizeof(config_blob_path), "%s/config.json", blobs_dir); - FILE *config_blob = fopen(config_blob_path, "w"); - if (config_blob == NULL) { - perror("Error creating config blob"); - fclose(bin_file); - json_object_put(config_json); - return; - } - fprintf(config_blob, "%s", json_object_to_json_string_ext(config_json, JSON_C_TO_STRING_PRETTY)); - fclose(config_blob); - - // Prepare manifest JSON - json_object *manifest_json = json_object_new_object(); - json_object_object_add(manifest_json, "schemaVersion", json_object_new_int(2)); - json_object_object_add(manifest_json, "mediaType", json_object_new_string("application/vnd.oci.image.manifest.v1+json")); - json_object_object_add(manifest_json, "config", json_object_new_object()); - json_object_object_add(json_object_object_get(manifest_json, "config"), "mediaType", json_object_new_string("application/vnd.oci.image.config.v1+json")); - json_object_object_add(json_object_object_get(manifest_json, "config"), "size", json_object_new_int64(strlen(json_object_to_json_string(config_json)))); - json_object_object_add(json_object_object_get(manifest_json, "config"), "digest", json_object_new_string("sha256:configdigest")); // Replace with actual digest - - json_object *layers_array = json_object_new_array(); - json_object_object_add(manifest_json, "layers", layers_array); - - char buffer[CHUNK_SIZE]; - for (int i = 0; i < num_entries; i++) { - char path[MAX_PATH_LEN]; - if (fread(path, sizeof(char), MAX_PATH_LEN, bin_file) != MAX_PATH_LEN) { - perror("Error reading entry path"); - fclose(bin_file); - json_object_put(config_json); - json_object_put(manifest_json); - return; - } +int add_plugin(const char *plugin_source) { + // Get the user's home directory + const char *home_dir = getenv("HOME"); + if (home_dir == NULL) { + struct passwd *pwd = getpwuid(getuid()); + if (pwd == NULL) { + fprintf(stderr, "Unable to determine home directory\n"); + return EXIT_FAILURE; + } + home_dir = pwd->pw_dir; + } + + // Define the plugin directory in the user's home + char plugin_dir[MAX_PATH_LEN]; + snprintf(plugin_dir, sizeof(plugin_dir), "%s/.osxiec/plugins", home_dir); + char compile_command[1024]; + char link_command[1024]; + char plugin_name[256]; + char *dot_pos = strrchr(plugin_source, '.'); + + if (dot_pos == NULL) { + fprintf(stderr, "Invalid plugin source file name\n"); + return -1; + } + + // Extract plugin name without extension + strncpy(plugin_name, plugin_source, dot_pos - plugin_source); + plugin_name[dot_pos - plugin_source] = '\0'; + + // Compile the plugin as an object file + snprintf(compile_command, sizeof(compile_command), "gcc -c -fPIC -o %s.o %s", + plugin_name, plugin_source); + + if (system(compile_command) != 0) { + fprintf(stderr, "Failed to compile plugin\n"); + return -1; + } + + // Get the path of the current executable + char executable_path[PATH_MAX]; + uint32_t size = sizeof(executable_path); + if (_NSGetExecutablePath(executable_path, &size) != 0) { + fprintf(stderr, "Failed to get executable path\n"); + return -1; + } + + // Link the plugin with the main executable + snprintf(link_command, sizeof(link_command), + "gcc -dynamiclib -o lib%s.dylib %s.o -undefined dynamic_lookup", + plugin_name, plugin_name); + + if (system(link_command) != 0) { + fprintf(stderr, "Failed to create dynamic library from plugin\n"); + return -1; + } + + // Move the dynamic library to the plugin directory + char move_command[1024]; + snprintf(move_command, sizeof(move_command), "mv lib%s.dylib %s", plugin_name, + plugin_dir); + + if (system(move_command) != 0) { + fprintf(stderr, "Failed to move plugin to plugin directory\n"); + return -1; + } + + // Clean up the object file + remove(plugin_name); + + printf("Plugin '%s' has been successfully compiled and moved to the plugin " + "directory.\n", + plugin_name); + printf("To use the plugin, you need to restart the program.\n"); + + return 0; +} - printf("Entry %d: path=%s\n", i, path); +int remove_plugin(const char *plugin_name) { + // Get the user's home directory + const char *home_dir = getenv("HOME"); + if (home_dir == NULL) { + struct passwd *pwd = getpwuid(getuid()); + if (pwd == NULL) { + fprintf(stderr, "Unable to determine home directory\n"); + return EXIT_FAILURE; + } + home_dir = pwd->pw_dir; + } + + // Define the plugin directory in the user's home + char plugin_dir[MAX_PATH_LEN]; + snprintf(plugin_dir, sizeof(plugin_dir), "%s/.osxiec/plugins", home_dir); + + // Remove the plugin from the plugin directory + char remove_command[1024]; + snprintf(remove_command, sizeof(remove_command), "rm -f %s/%s.dylib", + plugin_dir, plugin_name); + + if (system(remove_command) != 0) { + fprintf(stderr, "Failed to remove plugin\n"); + return -1; + } + + printf( + "Plugin '%s' has been successfully removed from the plugin directory.\n", + plugin_name); + + return 0; +} - // For files - size_t file_size; - if (fread(&file_size, sizeof(size_t), 1, bin_file) != 1) { - perror("Error reading file size"); - fclose(bin_file); - json_object_put(config_json); - json_object_put(manifest_json); - return; - } +void update_container_config(const char *container_file, + const char *new_config_file) { + // Open the container file in read mode + FILE *bin_file = fopen(container_file, "rb+"); + if (bin_file == NULL) { + perror("Error opening container file"); + exit(EXIT_FAILURE); + } + + // Read the current configuration + ContainerConfig current_config; + if (fread(¤t_config, sizeof(ContainerConfig), 1, bin_file) != 1) { + perror("Error reading current configuration"); + fclose(bin_file); + exit(EXIT_FAILURE); + } - // Create blob for each file - char blob_path[MAX_PATH_LEN]; - snprintf(blob_path, sizeof(blob_path), "%s/%s", blobs_dir, path); - // Ensure the directory exists before creating the file - char *last_slash = strrchr(blob_path, '/'); - if (last_slash != NULL) { - *last_slash = '\0'; // Temporarily null-terminate the path to create directories - create_directory_if_needed(blob_path); - *last_slash = '/'; // Restore the path - } + // Initialize new configuration with current values + ContainerConfig new_config = current_config; - FILE *blob_file = fopen(blob_path, "wb"); - if (blob_file == NULL) { - perror("Error creating blob file"); - continue; - } + // Read and apply new configuration + if (new_config_file) { + read_config_file(new_config_file, &new_config); + } - size_t remaining = file_size; - while (remaining > 0) { - size_t to_read = (remaining < CHUNK_SIZE) ? remaining : CHUNK_SIZE; - size_t bytes_read = fread(buffer, 1, to_read, bin_file); - if (bytes_read == 0) { - if (feof(bin_file)) { - fprintf(stderr, "Unexpected end of file while reading file data\n"); - } else { - perror("Error reading file data"); - } - break; - } - fwrite(buffer, 1, bytes_read, blob_file); - remaining -= bytes_read; - } - fclose(blob_file); - - // Add layer to manifest - json_object *layer = json_object_new_object(); - json_object_object_add(layer, "mediaType", json_object_new_string("application/vnd.oci.image.layer.v1.tar")); - json_object_object_add(layer, "size", json_object_new_int64(file_size)); - json_object_object_add(layer, "digest", json_object_new_string("sha256:layerdigest")); // Replace with actual digest - json_object_array_add(layers_array, layer); - } - - // Write manifest - char manifest_path[MAX_PATH_LEN]; - snprintf(manifest_path, sizeof(manifest_path), "%s/manifest.json", output_dir); - FILE *manifest_file = fopen(manifest_path, "w"); - if (manifest_file == NULL) { - perror("Error creating manifest file"); - fclose(bin_file); - json_object_put(config_json); - json_object_put(manifest_json); - return; - } - fprintf(manifest_file, "%s", json_object_to_json_string_ext(manifest_json, JSON_C_TO_STRING_PRETTY)); - fclose(manifest_file); + // Seek back to the start of the file + if (fseek(bin_file, 0, SEEK_SET) != 0) { + perror("Error seeking to start of file"); + fclose(bin_file); + exit(EXIT_FAILURE); + } + // Write the new configuration + if (fwrite(&new_config, sizeof(ContainerConfig), 1, bin_file) != 1) { + perror("Error writing new configuration"); fclose(bin_file); - json_object_put(config_json); - json_object_put(manifest_json); - printf("OCI container structure created in %s\n", output_dir); + exit(EXIT_FAILURE); + } + + fclose(bin_file); + printf("Container configuration updated successfully.\n"); } -char* find_latest_bin_file(const char *volume_name) { - DIR *dir; - struct dirent *entry; - char *latest_file_path = NULL; - time_t latest_timestamp = 0; - char search_directory[MAX_PATH_LEN]; +int copy_volume_to_directory(const char *volume_name, const char *target_dir) { + char volume_path[MAX_PATH_LEN]; + snprintf(volume_path, sizeof(volume_path), "/Volumes/%s", volume_name); + + // Check if volume exists + struct stat st = {0}; + if (stat(volume_path, &st) == -1) { + fprintf(stderr, "Volume %s does not exist\n", volume_name); + return EXIT_FAILURE; + } + + // Create target directory if it doesn't exist + if (stat(target_dir, &st) == -1) { + if (mkdir(target_dir, 0755) == -1) { + fprintf(stderr, "Error creating directory %s: %s\n", target_dir, + strerror(errno)); + return EXIT_FAILURE; + } + printf("Created directory: %s\n", target_dir); + } + + // Command to copy all files (using cp -R for recursive copy) + char command[MAX_PATH_LEN * 3]; + snprintf(command, sizeof(command), "cp -R %s/* %s/", volume_path, target_dir); + + int result = system(command); + if (result == 0) { + printf("Successfully copied contents from %s to %s\n", volume_path, + target_dir); + return EXIT_SUCCESS; + } else { + fprintf(stderr, "Failed to copy contents from %s to %s\n", volume_path, + target_dir); + return EXIT_FAILURE; + } +} - // Format the search directory path - snprintf(search_directory, sizeof(search_directory), "/Volumes/%s", volume_name); +void broadcast_command_to_network(const char* network_name, const char* command, int port) { + ContainerNetwork network = load_container_network(network_name); + if (network.vlan_id == 0) { + fprintf(stderr, "Failed to load network configuration for %s\n", network_name); + return; + } + + // Set up socket options + int enable_socket_reuse = 1; + struct timeval timeout = { + .tv_sec = 5, // 5 seconds timeout + .tv_usec = 0 + }; + + for (int i = 0; i < network.num_containers; i++) { + // Create a new socket for each container + int broadcast_socket = socket(AF_INET, SOCK_STREAM, 0); + if (broadcast_socket < 0) { + perror("Socket creation failed"); + continue; + } + + // Configure socket options + setsockopt(broadcast_socket, SOL_SOCKET, SO_REUSEADDR, + &enable_socket_reuse, sizeof(enable_socket_reuse)); + setsockopt(broadcast_socket, SOL_SOCKET, SO_RCVTIMEO, + &timeout, sizeof(timeout)); + setsockopt(broadcast_socket, SOL_SOCKET, SO_SNDTIMEO, + &timeout, sizeof(timeout)); + + // Set up container address + struct sockaddr_in container_addr; + memset(&container_addr, 0, sizeof(container_addr)); + container_addr.sin_family = AF_INET; + container_addr.sin_port = htons(port); // Using the port parameter + + // Convert IP address string to network format + if (inet_pton(AF_INET, network.container_ips[i], &container_addr.sin_addr) <= 0) { + fprintf(stderr, "Invalid container IP address: %s\n", network.container_ips[i]); + close(broadcast_socket); + continue; + } + + // Try to connect to the container + if (connect(broadcast_socket, (struct sockaddr*)&container_addr, + sizeof(container_addr)) < 0) { + fprintf(stderr, "Failed to connect to container %s at %s:%d\n", + network.container_names[i], network.container_ips[i], port); + close(broadcast_socket); + continue; + } + + // Send the command + ssize_t sent_bytes = send(broadcast_socket, command, strlen(command), 0); + if (sent_bytes < 0) { + fprintf(stderr, "Failed to send command to container %s\n", + network.container_names[i]); + } else { + printf("Command sent to container %s at %s:%d (%zd bytes)\n", + network.container_names[i], network.container_ips[i], port, sent_bytes); + + // Wait for response + char response[1024] = {0}; + ssize_t received_bytes = recv(broadcast_socket, response, sizeof(response)-1, 0); + if (received_bytes > 0) { + printf("Response from %s (%zd bytes): %s", + network.container_names[i], received_bytes, response); + } else if (received_bytes == 0) { + printf("Connection closed by container %s\n", network.container_names[i]); + } else { + perror("Error receiving response"); + } + } + + close(broadcast_socket); + } +} - if ((dir = opendir(search_directory)) == NULL) { - perror("Unable to open directory"); - return NULL; +int main(int argc, char *argv[]) { + PluginManager plugin_manager; + plugin_manager_init(&plugin_manager); + + // Get the user's home directory + const char *home_dir = getenv("HOME"); + if (home_dir == NULL) { + struct passwd *pwd = getpwuid(getuid()); + if (pwd == NULL) { + fprintf(stderr, "Unable to determine home directory\n"); + return EXIT_FAILURE; + } + home_dir = pwd->pw_dir; + } + + // Define the plugin directory in the user's home + char plugin_dir[MAX_PATH_LEN]; + snprintf(plugin_dir, sizeof(plugin_dir), "%s/.osxiec/plugins", home_dir); + + // Check if the directory exists, if not, create it + struct stat st = {0}; + if (stat(plugin_dir, &st) == -1) { + // Create the .osxiec directory first + char osxiec_dir[MAX_PATH_LEN]; + snprintf(osxiec_dir, sizeof(osxiec_dir), "%s/.osxiec", home_dir); + if (mkdir(osxiec_dir, 0755) == -1 && errno != EEXIST) { + fprintf(stderr, "Error creating .osxiec directory: %s\n", + strerror(errno)); + // Continue execution, as the program can still function without plugins + } + + // Now create the plugins directory + if (mkdir(plugin_dir, 0755) == -1) { + fprintf(stderr, "Error creating plugin directory %s: %s\n", plugin_dir, + strerror(errno)); + // Continue execution, as the program can still function without plugins + } else { + printf("Created plugin directory: %s\n", plugin_dir); } + } + // Load plugins from the directory + DIR *dir = opendir(plugin_dir); + if (dir) { + struct dirent *entry; while ((entry = readdir(dir)) != NULL) { - // Check if the file ends with ".bin" - if (strstr(entry->d_name, ".bin") != NULL) { - // Extract Unix timestamp from the beginning of the filename - time_t timestamp = atol(entry->d_name); - - if (timestamp > latest_timestamp) { - latest_timestamp = timestamp; - - // Free the previous path and allocate for the new one - free(latest_file_path); - latest_file_path = malloc(MAX_PATH_LEN); - snprintf(latest_file_path, MAX_PATH_LEN, "%s/%s", search_directory, entry->d_name); - } - } + if (entry->d_type == DT_REG) { // Regular file + char plugin_path[MAX_PATH_LEN]; + snprintf(plugin_path, sizeof(plugin_path), "%s/%s", plugin_dir, + entry->d_name); + plugin_manager_load(&plugin_manager, plugin_path); + } } closedir(dir); - - return latest_file_path; -} - -int copy_file(const char *source, const char *destination_folder, const char *new_file_name) { - // Construct the full path for the destination file - char destination[MAX_PATH_LEN]; - snprintf(destination, sizeof(destination), "%s/%s", destination_folder, new_file_name); - - FILE *src = fopen(source, "rb"); - if (src == NULL) { - perror("Error opening source file"); - return -1; + } + if (argc < 2) { + fprintf(stderr, "Unknown command: %s\n", argv[0]); + return EXIT_FAILURE; + } + + if (strcmp(argv[1], "-contain") == 0) { + if (argc < 4) { + fprintf(stderr, + "Usage for containerize: %s -contain " + " [start_config_file] [container_config_file]\n", + argv[0]); + return EXIT_FAILURE; + } + if (geteuid() != 0) { + fprintf(stderr, "This program must be run as root. Try using sudo.\n"); + return EXIT_FAILURE; + } + + const char *start_config_file = (argc > 4) ? argv[4] : NULL; + const char *container_config_file = (argc > 5) ? argv[5] : NULL; + containerize_directory(argv[2], argv[3], start_config_file, + container_config_file); + printf("Directory contents containerized into '%s'.\n", argv[3]); + } else if (strcmp(argv[1], "-craft") == 0) { + if (argc < 5) { + fprintf(stderr, + "Usage for craft: %s -craft " + " \n", + argv[0]); + return EXIT_FAILURE; + } + if (geteuid() != 0) { + fprintf(stderr, "This program must be run as root. Try using sudo.\n"); + return EXIT_FAILURE; + } + if (geteuid() != 0) { + fprintf(stderr, "This program must be run as root. Try using sudo.\n"); + return EXIT_FAILURE; + } + + const char *start_config_file = (argc > 5) ? argv[5] : NULL; + const char *container_config_file = (argc > 6) ? argv[6] : NULL; + containerize_directory_with_bin_file( + argv[2], argv[3], argv[4], start_config_file, container_config_file); + printf("Directory contents containerized into '%s'.\n", argv[4]); + } else if (strcmp(argv[1], "-oexec") == 0) { + if (argc < 3) { + fprintf(stderr, "Usage for oexec: %s -oexec \n", argv[0]); + return EXIT_FAILURE; + } + if (geteuid() != 0) { + fprintf(stderr, "This program must be run as root. Try using sudo.\n"); + return EXIT_FAILURE; + } + + FILE *bin_file = fopen(argv[2], "rb"); + if (bin_file == NULL) { + perror("Error opening binary file"); + return EXIT_FAILURE; } - FILE *dst = fopen(destination, "wb"); - if (dst == NULL) { - perror("Error opening destination file"); - fclose(src); - return -1; + ocreate_isolated_environment(bin_file, argv[2]); + fclose(bin_file); + } else if (strcmp(argv[1], "-gexec") == 0) { + if (argc < 3) { + fprintf(stderr, "Usage for gexec: %s -gexec \n", argv[0]); + return EXIT_FAILURE; } - - char buffer[BUFSIZ]; - size_t n; - while ((n = fread(buffer, 1, sizeof(buffer), src)) > 0) { - if (fwrite(buffer, 1, n, dst) != n) { - perror("Error writing to destination file"); - fclose(src); - fclose(dst); - return -1; - } + if (geteuid() != 0) { + fprintf(stderr, "This program must be run as root. Try using sudo.\n"); + return EXIT_FAILURE; } - fclose(src); - fclose(dst); - return 0; -} - -size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp) { - size_t total_size = size * nmemb; - char **response_ptr = (char **)userp; - - // Allocate or reallocate memory for the response - char *temp = realloc(*response_ptr, total_size + ( *response_ptr ? strlen(*response_ptr) : 0 ) + 1); - if (temp == NULL) { - fprintf(stderr, "Failed to allocate memory.\n"); - return 0; // Abort the transfer + FILE *bin_file = fopen(argv[2], "rb"); + if (bin_file == NULL) { + perror("Error opening binary file"); + return EXIT_FAILURE; } - *response_ptr = temp; - // Append new data to the response buffer - if (*response_ptr) { - memcpy(*response_ptr + ( *response_ptr ? strlen(*response_ptr) : 0 ), contents, total_size); - (*response_ptr)[total_size + ( *response_ptr ? strlen(*response_ptr) : 0 )] = '\0'; // Null-terminate + gcreate_isolated_environment(bin_file, argv[2]); + fclose(bin_file); + } else if (strcmp(argv[1], "-network") == 0) { + if (argc < 4) { + fprintf(stderr, "Usage: %s -network [vlan_id]\n", + argv[0]); + return EXIT_FAILURE; + } + if (geteuid() != 0) { + fprintf(stderr, "This program must be run as root. Try using sudo.\n"); + return EXIT_FAILURE; } - return total_size; -} - - -char* fetch_latest_version(void) { - CURL *curl; - CURLcode res; - char *latest_version = NULL; - - curl = curl_easy_init(); - if(curl) { - struct curl_slist *headers = NULL; - headers = curl_slist_append(headers, "User-Agent: osxiec-update-checker"); - - curl_easy_setopt(curl, CURLOPT_URL, "https://api.github.com/repos/Okerew/osxiec/releases/latest"); - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); - - char *response = NULL; - - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); - - res = curl_easy_perform(curl); - - if(res == CURLE_OK) { - if (response) { - struct json_object *parsed_json = json_tokener_parse(response); - if (parsed_json == NULL) { - fprintf(stderr, "Failed to parse JSON.\n"); - } else { - struct json_object *tag_name; - if (json_object_object_get_ex(parsed_json, "tag_name", &tag_name)) { - const char *version = json_object_get_string(tag_name); - latest_version = strdup(version); - } else { - fprintf(stderr, "JSON does not contain 'tag_name' field.\n"); - } - - json_object_put(parsed_json); - } - } else { - fprintf(stderr, "No response data received.\n"); - } - } else { - fprintf(stderr, "CURL request failed: %s\n", curl_easy_strerror(res)); - } - - curl_easy_cleanup(curl); - curl_slist_free_all(headers); - free(response); + if (strcmp(argv[2], "create") == 0) { + if (argc < 5) { + fprintf(stderr, "Usage: %s -network create \n", + argv[0]); + return EXIT_FAILURE; + } + create_and_save_container_network(argv[3], atoi(argv[4])); + ContainerNetwork network = load_container_network(argv[3]); + setup_pf_rules(&network); + } else if (strcmp(argv[2], "remove") == 0) { + remove_container_network(argv[3]); } else { - fprintf(stderr, "Failed to initialize CURL.\n"); + fprintf(stderr, "Unknown network command: %s\n", argv[2]); + return EXIT_FAILURE; } - - return latest_version; -} - -int compare_versions(const char* v1, const char* v2) { - return strcmp(v1, v2) == 0 ? 0 : -1; -} - - -int add_plugin(const char* plugin_source) { - // Get the user's home directory - const char* home_dir = getenv("HOME"); - if (home_dir == NULL) { - struct passwd* pwd = getpwuid(getuid()); - if (pwd == NULL) { - fprintf(stderr, "Unable to determine home directory\n"); - return EXIT_FAILURE; - } - home_dir = pwd->pw_dir; + } else if (strcmp(argv[1], "-run") == 0) { + if (argc < 4) { + fprintf(stderr, + "Usage: %s -run [-port ]\n", + argv[0]); + return EXIT_FAILURE; } - - // Define the plugin directory in the user's home - char plugin_dir[MAX_PATH_LEN]; - snprintf(plugin_dir, sizeof(plugin_dir), "%s/.osxiec/plugins", home_dir); - char compile_command[1024]; - char link_command[1024]; - char plugin_name[256]; - char* dot_pos = strrchr(plugin_source, '.'); - - if (dot_pos == NULL) { - fprintf(stderr, "Invalid plugin source file name\n"); - return -1; + if (geteuid() != 0) { + fprintf(stderr, "This program must be run as root. Try using sudo.\n"); + return EXIT_FAILURE; } - // Extract plugin name without extension - strncpy(plugin_name, plugin_source, dot_pos - plugin_source); - plugin_name[dot_pos - plugin_source] = '\0'; + for (int i = 4; i < argc; i++) { + if (strcmp(argv[i], "-port") == 0 && i + 1 < argc) { + port = atoi(argv[i + 1]); + break; + } + } - // Compile the plugin as an object file - snprintf(compile_command, sizeof(compile_command), - "gcc -c -fPIC -o %s.o %s", plugin_name, plugin_source); + // Load network configuration + ContainerNetwork network = load_container_network(argv[3]); - if (system(compile_command) != 0) { - fprintf(stderr, "Failed to compile plugin\n"); - return -1; + if (network.vlan_id == 0) { + fprintf(stderr, "Failed to load network configuration for %s\n", argv[3]); + return EXIT_FAILURE; } - // Get the path of the current executable - char executable_path[PATH_MAX]; - uint32_t size = sizeof(executable_path); - if (_NSGetExecutablePath(executable_path, &size) != 0) { - fprintf(stderr, "Failed to get executable path\n"); - return -1; + FILE *bin_file = fopen(argv[2], "rb"); + if (bin_file == NULL) { + perror("Error opening binary file"); + return EXIT_FAILURE; } - - // Link the plugin with the main executable - snprintf(link_command, sizeof(link_command), - "gcc -dynamiclib -o lib%s.dylib %s.o -undefined dynamic_lookup", - plugin_name, plugin_name); - - if (system(link_command) != 0) { - fprintf(stderr, "Failed to create dynamic library from plugin\n"); - return -1; + create_isolated_environment(bin_file, argv[2], &network); + fclose(bin_file); + } else if (strcmp(argv[1], "-start") == 0) { + if (argc < 4) { + fprintf(stderr, + "Usage: %s -start [-port ]\n", + argv[0]); + return EXIT_FAILURE; + } + if (geteuid() != 0) { + fprintf(stderr, "This program must be run as root. Try using sudo.\n"); + return EXIT_FAILURE; } - // Move the dynamic library to the plugin directory - char move_command[1024]; - snprintf(move_command, sizeof(move_command), - "mv lib%s.dylib %s", plugin_name, plugin_dir); - - if (system(move_command) != 0) { - fprintf(stderr, "Failed to move plugin to plugin directory\n"); - return -1; + for (int i = 4; i < argc; i++) { + if (strcmp(argv[i], "-port") == 0 && i + 1 < argc) { + port = atoi(argv[i + 1]); + break; + } } - // Clean up the object file - remove(plugin_name); + // Load network configuration + ContainerNetwork network = load_container_network(argv[3]); - printf("Plugin '%s' has been successfully compiled and moved to the plugin directory.\n", plugin_name); - printf("To use the plugin, you need to restart the program.\n"); + if (network.vlan_id == 0) { + fprintf(stderr, "Failed to load network configuration for %s\n", argv[3]); + return EXIT_FAILURE; + } - return 0; -} + // Find the latest binary file in the specified volume + const char *volume_name = argv[2]; + char *latest_bin_file_path = find_latest_bin_file(volume_name); -int remove_plugin(const char* plugin_name) { - // Get the user's home directory - const char* home_dir = getenv("HOME"); - if (home_dir == NULL) { - struct passwd* pwd = getpwuid(getuid()); - if (pwd == NULL) { - fprintf(stderr, "Unable to determine home directory\n"); - return EXIT_FAILURE; - } - home_dir = pwd->pw_dir; + if (latest_bin_file_path == NULL) { + fprintf(stderr, "No valid .bin file found for volume %s\n", volume_name); + return EXIT_FAILURE; } - // Define the plugin directory in the user's home - char plugin_dir[MAX_PATH_LEN]; - snprintf(plugin_dir, sizeof(plugin_dir), "%s/.osxiec/plugins", home_dir); + // Copy the latest file to the root of the volume with just the volume name + char dest_folder[MAX_PATH_LEN]; + snprintf(dest_folder, sizeof(dest_folder), "/Volumes/%s", volume_name); - // Remove the plugin from the plugin directory - char remove_command[1024]; - snprintf(remove_command, sizeof(remove_command), - "rm -f %s/%s.dylib", plugin_dir, plugin_name); - - if (system(remove_command) != 0) { - fprintf(stderr, "Failed to remove plugin\n"); - return -1; + if (copy_file(latest_bin_file_path, dest_folder, volume_name) != 0) { + fprintf(stderr, "Failed to copy binary file to volume root\n"); + free(latest_bin_file_path); + return EXIT_FAILURE; } - printf("Plugin '%s' has been successfully removed from the plugin directory.\n", plugin_name); - - return 0; -} -int main(int argc, char *argv[]) { - PluginManager plugin_manager; - plugin_manager_init(&plugin_manager); - - // Get the user's home directory - const char* home_dir = getenv("HOME"); - if (home_dir == NULL) { - struct passwd* pwd = getpwuid(getuid()); - if (pwd == NULL) { - fprintf(stderr, "Unable to determine home directory\n"); - return EXIT_FAILURE; - } - home_dir = pwd->pw_dir; - } - - // Define the plugin directory in the user's home - char plugin_dir[MAX_PATH_LEN]; - snprintf(plugin_dir, sizeof(plugin_dir), "%s/.osxiec/plugins", home_dir); - - // Check if the directory exists, if not, create it - struct stat st = {0}; - if (stat(plugin_dir, &st) == -1) { - // Create the .osxiec directory first - char osxiec_dir[MAX_PATH_LEN]; - snprintf(osxiec_dir, sizeof(osxiec_dir), "%s/.osxiec", home_dir); - if (mkdir(osxiec_dir, 0755) == -1 && errno != EEXIST) { - fprintf(stderr, "Error creating .osxiec directory: %s\n", strerror(errno)); - // Continue execution, as the program can still function without plugins - } + // Open the copied binary file + char full_dest_path[MAX_PATH_LEN]; + snprintf(full_dest_path, sizeof(full_dest_path), "%s/%s", dest_folder, + volume_name); - // Now create the plugins directory - if (mkdir(plugin_dir, 0755) == -1) { - fprintf(stderr, "Error creating plugin directory %s: %s\n", plugin_dir, strerror(errno)); - // Continue execution, as the program can still function without plugins - } else { - printf("Created plugin directory: %s\n", plugin_dir); - } + printf("Opening binary file: %s\n", full_dest_path); + FILE *bin_file = fopen(full_dest_path, "rb"); + if (bin_file == NULL) { + perror("Error opening binary file"); + fprintf(stderr, "Failed to open: %s\n", full_dest_path); + free(latest_bin_file_path); + return EXIT_FAILURE; } - // Load plugins from the directory - DIR* dir = opendir(plugin_dir); - if (dir) { - struct dirent* entry; - while ((entry = readdir(dir)) != NULL) { - if (entry->d_type == DT_REG) { // Regular file - char plugin_path[MAX_PATH_LEN]; - snprintf(plugin_path, sizeof(plugin_path), "%s/%s", plugin_dir, entry->d_name); - plugin_manager_load(&plugin_manager, plugin_path); - } - } - closedir(dir); - } + create_isolated_environment(bin_file, volume_name, &network); + + fclose(bin_file); + free(latest_bin_file_path); + } else if (strcmp(argv[1], "-ostart") == 0) { if (argc < 2) { - fprintf(stderr, "Unknown command: %s\n" , argv[0]); - return EXIT_FAILURE; + fprintf(stderr, "Usage: %s -ostart \n", argv[0]); + return EXIT_FAILURE; } - - if (strcmp(argv[1], "-contain") == 0) { - if (argc < 4) { - fprintf(stderr, "Usage for containerize: %s -contain [start_config_file] [container_config_file]\n", argv[0]); - return EXIT_FAILURE; - } - if (geteuid() != 0) { - fprintf(stderr, "This program must be run as root. Try using sudo.\n"); - return EXIT_FAILURE; - } - - const char *start_config_file = (argc > 4) ? argv[4] : NULL; - const char *container_config_file = (argc > 5) ? argv[5] : NULL; - containerize_directory(argv[2], argv[3], start_config_file, container_config_file); - printf("Directory contents containerized into '%s'.\n", argv[3]); - } else if (strcmp(argv[1], "-craft") == 0) { - if (argc < 5) { - fprintf(stderr, "Usage for craft: %s -craft \n", argv[0]); - return EXIT_FAILURE; - } - if (geteuid() != 0) { - fprintf(stderr, "This program must be run as root. Try using sudo.\n"); - return EXIT_FAILURE; - } - if (geteuid() != 0) { - fprintf(stderr, "This program must be run as root. Try using sudo.\n"); - return EXIT_FAILURE; - } - - const char *start_config_file = (argc > 5) ? argv[5] : NULL; - const char *container_config_file = (argc > 6) ? argv[6] : NULL; - containerize_directory_with_bin_file(argv[2], argv[3], argv[4], start_config_file, container_config_file); - printf("Directory contents containerized into '%s'.\n", argv[4]); - } else if (strcmp(argv[1], "-oexec") == 0) { - if (argc < 3) { - fprintf(stderr, "Usage for oexec: %s -oexec \n", argv[0]); - return EXIT_FAILURE; - } - if (geteuid() != 0) { - fprintf(stderr, "This program must be run as root. Try using sudo.\n"); - return EXIT_FAILURE; - } - - FILE *bin_file = fopen(argv[2], "rb"); - if (bin_file == NULL) { - perror("Error opening binary file"); - return EXIT_FAILURE; - } - - ocreate_isolated_environment(bin_file, argv[2]); - fclose(bin_file); - } else if(strcmp(argv[1], "-gexec") == 0) { - if (argc < 3) { - fprintf(stderr, "Usage for gexec: %s -gexec \n", argv[0]); - return EXIT_FAILURE; - } - if (geteuid() != 0) { - fprintf(stderr, "This program must be run as root. Try using sudo.\n"); - return EXIT_FAILURE; - } - - FILE *bin_file = fopen(argv[2], "rb"); - if (bin_file == NULL) { - perror("Error opening binary file"); - return EXIT_FAILURE; - } - - gcreate_isolated_environment(bin_file, argv[2]); - fclose(bin_file); - } else if (strcmp(argv[1], "-network") == 0) { - if (argc < 4) { - fprintf(stderr, "Usage: %s -network [vlan_id]\n", argv[0]); - return EXIT_FAILURE; - } - if (geteuid() != 0) { - fprintf(stderr, "This program must be run as root. Try using sudo.\n"); - return EXIT_FAILURE; - } - - if (strcmp(argv[2], "create") == 0) { - if (argc < 5) { - fprintf(stderr, "Usage: %s -network create \n", argv[0]); - return EXIT_FAILURE; - } - create_and_save_container_network(argv[3], atoi(argv[4])); - ContainerNetwork network = load_container_network(argv[3]); - setup_pf_rules(&network); - } - else if (strcmp(argv[2], "remove") == 0) { - remove_container_network(argv[3]); - } - else { - fprintf(stderr, "Unknown network command: %s\n", argv[2]); - return EXIT_FAILURE; - } + if (geteuid() != 0) { + fprintf(stderr, "This program must be run as root. Try using sudo.\n"); + return EXIT_FAILURE; } - else if (strcmp(argv[1], "-run") == 0) { - if (argc < 4) { - fprintf(stderr, "Usage: %s -run [-port ]\n", argv[0]); - return EXIT_FAILURE; - } - if (geteuid() != 0) { - fprintf(stderr, "This program must be run as root. Try using sudo.\n"); - return EXIT_FAILURE; - } - - for (int i = 4; i < argc; i++) { - if (strcmp(argv[i], "-port") == 0 && i + 1 < argc) { - port = atoi(argv[i + 1]); - break; - } - } - - // Load network configuration - ContainerNetwork network = load_container_network(argv[3]); - - if (network.vlan_id == 0) { - fprintf(stderr, "Failed to load network configuration for %s\n", argv[3]); - return EXIT_FAILURE; - } - - FILE *bin_file = fopen(argv[2], "rb"); - if (bin_file == NULL) { - perror("Error opening binary file"); - return EXIT_FAILURE; - } - create_isolated_environment(bin_file, argv[2], &network); - fclose(bin_file); - } else if (strcmp(argv[1], "-start") == 0) { - if (argc < 4) { - fprintf(stderr, "Usage: %s -start [-port ]\n", argv[0]); - return EXIT_FAILURE; - } - if (geteuid() != 0) { - fprintf(stderr, "This program must be run as root. Try using sudo.\n"); - return EXIT_FAILURE; - } - - for (int i = 4; i < argc; i++) { - if (strcmp(argv[i], "-port") == 0 && i + 1 < argc) { - port = atoi(argv[i + 1]); - break; - } - } - - // Load network configuration - ContainerNetwork network = load_container_network(argv[3]); - if (network.vlan_id == 0) { - fprintf(stderr, "Failed to load network configuration for %s\n", argv[3]); - return EXIT_FAILURE; - } - - // Find the latest binary file in the specified volume - const char *volume_name = argv[2]; - char *latest_bin_file_path = find_latest_bin_file(volume_name); - - if (latest_bin_file_path == NULL) { - fprintf(stderr, "No valid .bin file found for volume %s\n", volume_name); - return EXIT_FAILURE; - } - - // Copy the latest file to the root of the volume with just the volume name - char dest_folder[MAX_PATH_LEN]; - snprintf(dest_folder, sizeof(dest_folder), "/Volumes/%s", volume_name); - - if (copy_file(latest_bin_file_path, dest_folder, volume_name) != 0) { - fprintf(stderr, "Failed to copy binary file to volume root\n"); - free(latest_bin_file_path); - return EXIT_FAILURE; - } - - // Open the copied binary file - char full_dest_path[MAX_PATH_LEN]; - snprintf(full_dest_path, sizeof(full_dest_path), "%s/%s", dest_folder, volume_name); - - printf("Opening binary file: %s\n", full_dest_path); - FILE *bin_file = fopen(full_dest_path, "rb"); - if (bin_file == NULL) { - perror("Error opening binary file"); - fprintf(stderr, "Failed to open: %s\n", full_dest_path); - free(latest_bin_file_path); - return EXIT_FAILURE; - } - - create_isolated_environment(bin_file, volume_name, &network); + // Find the latest binary file in the specified volume + const char *volume_name = argv[2]; + char *latest_bin_file_path = find_latest_bin_file(volume_name); - fclose(bin_file); - free(latest_bin_file_path); - } else if (strcmp(argv[1], "-ostart") == 0) { - if (argc < 2) { - fprintf(stderr, "Usage: %s -ostart \n", argv[0]); - return EXIT_FAILURE; - } - if (geteuid() != 0) { - fprintf(stderr, "This program must be run as root. Try using sudo.\n"); - return EXIT_FAILURE; - } + if (latest_bin_file_path == NULL) { + fprintf(stderr, "No valid .bin file found for volume %s\n", volume_name); + return EXIT_FAILURE; + } - // Find the latest binary file in the specified volume - const char *volume_name = argv[2]; - char *latest_bin_file_path = find_latest_bin_file(volume_name); + // Copy the latest file to the root of the volume with just the volume name + char dest_folder[MAX_PATH_LEN]; + snprintf(dest_folder, sizeof(dest_folder), "/Volumes/%s", volume_name); - if (latest_bin_file_path == NULL) { - fprintf(stderr, "No valid .bin file found for volume %s\n", volume_name); - return EXIT_FAILURE; - } + if (copy_file(latest_bin_file_path, dest_folder, volume_name) != 0) { + fprintf(stderr, "Failed to copy binary file to volume root\n"); + free(latest_bin_file_path); + return EXIT_FAILURE; + } - // Copy the latest file to the root of the volume with just the volume name - char dest_folder[MAX_PATH_LEN]; - snprintf(dest_folder, sizeof(dest_folder), "/Volumes/%s", volume_name); + // Open the copied binary file + char full_dest_path[MAX_PATH_LEN]; + snprintf(full_dest_path, sizeof(full_dest_path), "%s/%s", dest_folder, + volume_name); - if (copy_file(latest_bin_file_path, dest_folder, volume_name) != 0) { - fprintf(stderr, "Failed to copy binary file to volume root\n"); - free(latest_bin_file_path); - return EXIT_FAILURE; - } + printf("Opening binary file: %s\n", full_dest_path); + FILE *bin_file = fopen(full_dest_path, "rb"); + if (bin_file == NULL) { + perror("Error opening binary file"); + fprintf(stderr, "Failed to open: %s\n", full_dest_path); + free(latest_bin_file_path); + return EXIT_FAILURE; + } - // Open the copied binary file - char full_dest_path[MAX_PATH_LEN]; - snprintf(full_dest_path, sizeof(full_dest_path), "%s/%s", dest_folder, volume_name); - - printf("Opening binary file: %s\n", full_dest_path); - FILE *bin_file = fopen(full_dest_path, "rb"); - if (bin_file == NULL) { - perror("Error opening binary file"); - fprintf(stderr, "Failed to open: %s\n", full_dest_path); - free(latest_bin_file_path); - return EXIT_FAILURE; - } + ocreate_isolated_environment(bin_file, volume_name); - ocreate_isolated_environment(bin_file, volume_name); + fclose(bin_file); + free(latest_bin_file_path); + } else if (strcmp(argv[1], "-gstart") == 0) { + if (argc < 2) { + fprintf(stderr, "Usage: %s -gstart \n", argv[0]); + return EXIT_FAILURE; + } + if (geteuid() != 0) { + fprintf(stderr, "This program must be run as root. Try using sudo.\n"); + return EXIT_FAILURE; + } - fclose(bin_file); - free(latest_bin_file_path); - } else if (strcmp(argv[1], "-gstart") == 0) { - if (argc < 2) { - fprintf(stderr, "Usage: %s -gstart \n", argv[0]); - return EXIT_FAILURE; - } - if (geteuid() != 0) { - fprintf(stderr, "This program must be run as root. Try using sudo.\n"); - return EXIT_FAILURE; - } + // Find the latest binary file in the specified volume + const char *volume_name = argv[2]; + char *latest_bin_file_path = find_latest_bin_file(volume_name); - // Find the latest binary file in the specified volume - const char *volume_name = argv[2]; - char *latest_bin_file_path = find_latest_bin_file(volume_name); + if (latest_bin_file_path == NULL) { + fprintf(stderr, "No valid .bin file found for volume %s\n", volume_name); + return EXIT_FAILURE; + } - if (latest_bin_file_path == NULL) { - fprintf(stderr, "No valid .bin file found for volume %s\n", volume_name); - return EXIT_FAILURE; - } + // Copy the latest file to the root of the volume with just the volume name + char dest_folder[MAX_PATH_LEN]; + snprintf(dest_folder, sizeof(dest_folder), "/Volumes/%s", volume_name); - // Copy the latest file to the root of the volume with just the volume name - char dest_folder[MAX_PATH_LEN]; - snprintf(dest_folder, sizeof(dest_folder), "/Volumes/%s", volume_name); + if (copy_file(latest_bin_file_path, dest_folder, volume_name) != 0) { + fprintf(stderr, "Failed to copy binary file to volume root\n"); + free(latest_bin_file_path); + return EXIT_FAILURE; + } - if (copy_file(latest_bin_file_path, dest_folder, volume_name) != 0) { - fprintf(stderr, "Failed to copy binary file to volume root\n"); - free(latest_bin_file_path); - return EXIT_FAILURE; - } + // Open the copied binary file + char full_dest_path[MAX_PATH_LEN]; + snprintf(full_dest_path, sizeof(full_dest_path), "%s/%s", dest_folder, + volume_name); - // Open the copied binary file - char full_dest_path[MAX_PATH_LEN]; - snprintf(full_dest_path, sizeof(full_dest_path), "%s/%s", dest_folder, volume_name); - - printf("Opening binary file: %s\n", full_dest_path); - FILE *bin_file = fopen(full_dest_path, "rb"); - if (bin_file == NULL) { - perror("Error opening binary file"); - fprintf(stderr, "Failed to open: %s\n", full_dest_path); - free(latest_bin_file_path); - return EXIT_FAILURE; - } + printf("Opening binary file: %s\n", full_dest_path); + FILE *bin_file = fopen(full_dest_path, "rb"); + if (bin_file == NULL) { + perror("Error opening binary file"); + fprintf(stderr, "Failed to open: %s\n", full_dest_path); + free(latest_bin_file_path); + return EXIT_FAILURE; + } - gcreate_isolated_environment(bin_file, volume_name); + gcreate_isolated_environment(bin_file, volume_name); - fclose(bin_file); - free(latest_bin_file_path); - } else if (strcmp(argv[1], "-pull") == 0) { - if (argc != 3) { - printf("Usage: %s -pull \n", argv[0]); + fclose(bin_file); + free(latest_bin_file_path); + } else if (strcmp(argv[1], "-pull") == 0) { + if (argc != 3) { + printf("Usage: %s -pull \n", argv[0]); + return 1; + } + download_file(argv[2]); + + } else if (strcmp(argv[1], "-search") == 0) { + if (argc != 3) { + fprintf(stderr, "Usage: %s -search \n", argv[0]); + return EXIT_FAILURE; + } + search(argv[2]); + } else if (strcmp(argv[1], "-upload") == 0) { + if (argc != 6) { + fprintf( + stderr, + "Usage: %s -upload \n", + argv[0]); + return EXIT_FAILURE; + } + upload_file(argv[2], argv[3], argv[4], argv[5]); + } else if (strcmp(argv[1], "-convert-to-docker") == 0) { + if (argc < 5 || argc > 6) { + fprintf(stderr, + "Usage: %s -convert-to-docker " + " [custom_dockerfile]\n", + argv[0]); + return EXIT_FAILURE; + } + const char *custom_dockerfile = (argc == 6) ? argv[5] : NULL; + convert_to_docker(argv[2], argv[3], argv[4], custom_dockerfile); + } else if (strcmp(argv[1], "-convert-to-oci") == 0) { + if (argc < 6 || argc > 7) { + fprintf(stderr, + "Usage: %s -convert-to-oci " + " \n", + argv[0]); + return EXIT_FAILURE; + } + convert_to_oci(argv[2], argv[3], argv[4], argv[5], argv[6]); + } else if (strcmp(argv[1], "-deploy") == 0) { + if (argc < 3) { + fprintf(stderr, "Usage: %s -deploy [-port ]\n", + argv[0]); + return EXIT_FAILURE; + } + if (geteuid() != 0) { + fprintf(stderr, "This program must be run as root. Try using sudo.\n"); + return EXIT_FAILURE; + } + int deploy_port = 0; + for (int i = 3; i < argc; i++) { + if (strcmp(argv[i], "-port") == 0 && i + 1 < argc) { + deploy_port = atoi(argv[i + 1]); + break; + } + } + deploy_container(argv[2], deploy_port); + } else if (strcmp(argv[1], "-clean") == 0) { + clean_container_dmgs(); + printf("Cleaned up container disk images from /tmp directory.\n"); + } else if (strcmp(argv[1], "-scan") == 0) { + if (argc != 3) { + fprintf(stderr, "Usage: %s -scan \n", argv[0]); + return EXIT_FAILURE; + } + security_scan(argv[2]); + } else if (strcmp(argv[1], "-deploym") == 0) { + char command[100] = "osxiec_deploy_multiple.sh"; + + if (argc > 2) { + for (int i = 2; i < argc; i++) { + strcat(command, " "); + strcat(command, argv[i]); + } + } + + system(command); + } else if (strcmp(argv[1], "-detach") == 0) { + if (argc != 3) { + fprintf(stderr, "Usage: %s -detach \n", argv[0]); + return EXIT_FAILURE; + } + detach_container_images(argv[2]); + } else if (strcmp(argv[1], "-extract") == 0) { + if (argc != 4) { + fprintf(stderr, + "Usage: %s -extract \n", + argv[0]); + return EXIT_FAILURE; + } + extract_container(argv[2], argv[3]); + } else if (strcmp(argv[1], "-add_plugin") == 0) { + if (argc != 3) { + fprintf(stderr, "Usage: %s -add_plugin \n", argv[0]); + return EXIT_FAILURE; + } + if (geteuid() != 0) { + fprintf(stderr, "This program must be run as root. Try using sudo.\n"); + return EXIT_FAILURE; + } + + const char *plugin_source = argv[2]; + if (add_plugin(plugin_source) != 0) { + fprintf(stderr, "Failed to add plugin: %s\n", plugin_source); + return EXIT_FAILURE; + } + printf("Plugin added successfully. Please restart the program.\n"); + return EXIT_SUCCESS; // Exit after adding plugin + } else if (strcmp(argv[1], "-remove_plugin") == 0) { + if (argc != 3) { + fprintf(stderr, "Usage: %s -remove_plugin \n", argv[0]); + return EXIT_FAILURE; + } + if (geteuid() != 0) { + fprintf(stderr, "This program must be run as root. Try using sudo.\n"); + return EXIT_FAILURE; + } + + const char *plugin_name = argv[2]; + if (remove_plugin(plugin_name) != 0) { + fprintf(stderr, "Failed to remove plugin: %s\n", plugin_name); + return EXIT_FAILURE; + } + printf("Plugin removed successfully. Please restart the program.\n"); + return EXIT_SUCCESS; // Exit after removing plugin + } else if (strcmp(argv[1], "-bcn") == 0) { + // broadcast_command_to_network + if (argc != 5) { + fprintf(stderr, "Usage: %s -bcn , , PORT\n", argv[0]); + return EXIT_FAILURE; + } + broadcast_command_to_network(argv[2], argv[3], atoi(argv[4])); + } else if (strcmp(argv[1], "-help") == 0) { + printf("Available commands:\n"); + printf(" -contain " + " \n"); + printf("Contains a directory into a container file\n"); + printf(" -craft " + " \n"); + printf("Crafts a container file from a directory and a bin file\n"); + printf(" -oexec \n"); + printf("Executes a container file in offline mode\n"); + printf(" -gexec \n"); + printf("Executes a container file wile allowing gui applications (note " + "this is doesn't support online mode and has far limited isolation " + "compared to other modes)\n"); + printf(" -start \n"); + printf("Starts a stopped container"); + printf(" -ostart \n"); + printf("Starts a stopped container in offline mode"); + printf(" -network [vlan_id>\n"); + printf("Manages the vlan network\n"); + printf(" -run [-port ]\n"); + printf("Runs a container file with a vlan network\n"); + printf(" -pull \n"); + printf("Pulls a container from Osxiec Hub\n"); + printf(" -search \n"); + printf("Searches for a container in Osxiec Hub\n"); + printf(" -upload \n"); + printf("Uploads a file to Osxiec Hub\n"); + printf(" -convert-to-docker " + "[custom_dockerfile]\n"); + printf("Converts a binary file to a docker image\n"); + printf(" -convert-to-oci " + "\n"); + printf("Converts a binary file to an oci image\n"); + printf(" -clean\n"); + printf("Cleans up container disk images from /tmp directory.\n"); + printf(" -deploy [-port ]\n"); + printf("Deploys a container from a config file\n"); + printf(" -scan \n"); + printf("Scans a binary file for vulnerabilities\n"); + printf(" -deploym\n"); + printf("Deploys multiple containers from a config file\n"); + printf(" -detach\n"); + printf("Detaches container from /Volumes\n"); + printf(" -extract \n"); + printf("Extracts a container file\n"); + printf(" -help\n"); + printf("Prints this help message\n"); + printf(" --version\n"); + printf("Checks for updates and the current version\n"); + printf(" -check_for_update\n"); + printf("Checks for updates and updates the current version\n"); + printf(" -add_plugin \n"); + printf("Adds a plugin\n"); + printf(" -remove_plugin \n"); + printf("Removes a plugin\n"); + printf(" -update \n"); + printf("Updates a container file with a new config\n"); + printf(" -copy-volume \n"); + printf("Copies volume files to a directory\n"); + printf(" -bcn , , PORT\n"); + printf("Broadcasts a command to a network\n"); + } else if (argc > 1 && strcmp(argv[1], "--version") == 0) { + char *latest_version = fetch_latest_version(); + if (latest_version) { + int comparison = compare_versions(VERSION, latest_version); + if (comparison < 0) { + printf("An update is available. Latest version: %s\n", latest_version); + printf("Your current version: %s\n", VERSION); + printf("Please visit https://github.com/Okerew/osxiec/releases/latest " + "to update.\n"); + } else if (comparison == 0) { + printf("You are running the latest version (%s).\n", VERSION); + } else { + printf( + "Your version (%s) is newer than the latest known version (%s).\n", + VERSION, latest_version); + } + free(latest_version); + } else { + printf("Failed to check for updates. Please check your internet " + "connection.\n"); + printf("Your current version: %s\n", VERSION); + } + } else if (strcmp(argv[1], "-update") == 0) { + if (argc != 4) { + fprintf(stderr, "Usage: %s -update \n", + argv[0]); + return EXIT_FAILURE; + } + if (geteuid() != 0) { + fprintf(stderr, "This program must be run as root. Try using sudo.\n"); + return EXIT_FAILURE; + } + update_container_config(argv[2], argv[3]); + } else if (strcmp(argv[1], "-copy-volume") == 0) { + if (argc != 4) { + fprintf(stderr, + "Usage: %s -copy-volume \n", + argv[0]); + return EXIT_FAILURE; + } + return copy_volume_to_directory(argv[2], argv[3]); + } else if (strcmp(argv[1], "-check_for_update") == 0) { + char *latest_version = fetch_latest_version(); + if (latest_version) { + int comparison = compare_versions(VERSION, latest_version); + if (comparison < 0) { + printf("An update is available. Latest version: %s\n", latest_version); + printf("Your current version: %s\n", VERSION); + if (strcmp(OSXIEC_ARCHITECTURE, "arm64") == 0) { + char update_command[MAX_COMMAND_LEN]; + sprintf(update_command, + "curl -L -o osxiec_cli.tar.gz " + "https://github.com/Okerew/osxiec/releases/download/%s/" + "osxiec_cli.tar.gz", + latest_version); + system(update_command); + system("tar -xvzf osxiec_cli.tar.gz"); + const char *path = "osxiec_cli"; + + if (chdir(path) != 0) { + perror("chdir() to 'osxiec_cli' failed"); return 1; - } - download_file(argv[2]); - - } else if (strcmp(argv[1], "-search") == 0) { - if (argc != 3) { - fprintf(stderr, "Usage: %s -search \n", argv[0]); - return EXIT_FAILURE; - } - search(argv[2]); - } else if (strcmp(argv[1], "-upload") == 0) { - if (argc != 6) { - fprintf(stderr, "Usage: %s -upload \n", argv[0]); - return EXIT_FAILURE; - } - upload_file(argv[2], argv[3], argv[4], argv[5]); - } else if (strcmp(argv[1], "-convert-to-docker") == 0) { - if (argc < 5 || argc > 6) { - fprintf(stderr, "Usage: %s -convert-to-docker [custom_dockerfile]\n", argv[0]); - return EXIT_FAILURE; - } - const char *custom_dockerfile = (argc == 6) ? argv[5] : NULL; - convert_to_docker(argv[2], argv[3], argv[4], custom_dockerfile); - } else if (strcmp(argv[1], "-convert-to-oci") == 0) { - if (argc < 6 || argc > 7) { - fprintf(stderr, "Usage: %s -convert-to-oci \n", argv[0]); - return EXIT_FAILURE; - } - convert_to_oci(argv[2], argv[3], argv[4], argv[5], argv[6]); - } else if (strcmp(argv[1], "-deploy") == 0) { - if (argc < 3) { - fprintf(stderr, "Usage: %s -deploy [-port ]\n", argv[0]); - return EXIT_FAILURE; - } - if (geteuid() != 0) { - fprintf(stderr, "This program must be run as root. Try using sudo.\n"); - return EXIT_FAILURE; - } - int deploy_port = 0; - for (int i = 3; i < argc; i++) { - if (strcmp(argv[i], "-port") == 0 && i + 1 < argc) { - deploy_port = atoi(argv[i + 1]); - break; - } - } - deploy_container(argv[2], deploy_port); - } else if (strcmp(argv[1], "-clean") == 0) { - clean_container_dmgs(); - printf("Cleaned up container disk images from /tmp directory.\n"); - } else if (strcmp(argv[1], "-scan") == 0) { - if (argc != 3) { - fprintf(stderr, "Usage: %s -scan \n", argv[0]); - return EXIT_FAILURE; - } - security_scan(argv[2]); - } else if (strcmp(argv[1], "-deploym") == 0) { - char command[100] = "osxiec_deploy_multiple.sh"; - - if (argc > 2) { - for (int i = 2; i < argc; i++) { - strcat(command, " "); - strcat(command, argv[i]); - } - } - - system(command); - } else if (strcmp(argv[1], "-detach") == 0) { - if (argc != 3) { - fprintf(stderr, "Usage: %s -detach \n", argv[0]); - return EXIT_FAILURE; - } - detach_container_images(argv[2]); - } else if (strcmp(argv[1], "-extract") == 0) { - if (argc != 4) { - fprintf(stderr, "Usage: %s -extract \n", argv[0]); - return EXIT_FAILURE; - } - extract_container(argv[2], argv[3]); - } else if (strcmp(argv[1], "-add_plugin") == 0) { - if (argc != 3) { - fprintf(stderr, "Usage: %s -add_plugin \n", argv[0]); - return EXIT_FAILURE; - } - if (geteuid() != 0) { - fprintf(stderr, "This program must be run as root. Try using sudo.\n"); - return EXIT_FAILURE; - } - - const char* plugin_source = argv[2]; - if (add_plugin(plugin_source) != 0) { - fprintf(stderr, "Failed to add plugin: %s\n", plugin_source); - return EXIT_FAILURE; - } - printf("Plugin added successfully. Please restart the program.\n"); - return EXIT_SUCCESS; // Exit after adding plugin - } else if (strcmp(argv[1], "-remove_plugin") == 0) { - if (argc != 3) { - fprintf(stderr, "Usage: %s -remove_plugin \n", argv[0]); - return EXIT_FAILURE; - } - if (geteuid() != 0) { - fprintf(stderr, "This program must be run as root. Try using sudo.\n"); - return EXIT_FAILURE; - } + } + + system("sudo sh install.sh"); + } + if (strcmp(OSXIEC_ARCHITECTURE, "86_64") == 0) { + char update_command[MAX_COMMAND_LEN]; + sprintf(update_command, + "curl -L -o osxiec_cli_86_64.tar.gz " + "https://github.com/Okerew/osxiec/releases/download/%s/" + "osxiec_cli.tar.gz", + latest_version); + system(update_command); + system("tar -xvzf osxiec_cli_86_64.tar.gz"); + const char *path = "osxiec_cli_86_64"; + + if (chdir(path) != 0) { + perror("chdir() to 'osxiec_cli' failed"); + return 1; + } - const char* plugin_name = argv[2]; - if (remove_plugin(plugin_name) != 0) { - fprintf(stderr, "Failed to remove plugin: %s\n", plugin_name); - return EXIT_FAILURE; - } - printf("Plugin removed successfully. Please restart the program.\n"); - return EXIT_SUCCESS; // Exit after removing plugin - } else if (strcmp(argv[1], "-help") == 0) { - printf("Available commands:\n"); - printf(" -contain \n"); - printf("Contains a directory into a container file\n"); - printf(" -craft \n"); - printf("Crafts a container file from a directory and a bin file\n"); - printf(" -oexec \n"); - printf("Executes a container file in offline mode\n"); - printf(" -gexec \n"); - printf("Executes a container file wile allowing gui applications (note this is doesn't support online mode and has far limited isolation compared to other modes)\n"); - printf(" -start \n"); - printf("Starts a stopped container"); - printf(" -ostart \n"); - printf("Starts a stopped container in offline mode"); - printf(" -network [vlan_id>\n"); - printf("Manages the vlan network\n"); - printf(" -run [-port ]\n"); - printf("Runs a container file with a vlan network\n"); - printf(" -pull \n"); - printf("Pulls a container from Osxiec Hub\n"); - printf(" -search \n"); - printf("Searches for a container in Osxiec Hub\n"); - printf(" -upload \n"); - printf("Uploads a file to Osxiec Hub\n"); - printf(" -convert-to-docker [custom_dockerfile]\n"); - printf("Converts a binary file to a docker image\n"); - printf(" -convert-to-oci \n"); - printf("Converts a binary file to an oci image\n"); - printf(" -clean\n"); - printf("Cleans up container disk images from /tmp directory.\n"); - printf(" -deploy [-port ]\n"); - printf("Deploys a container from a config file\n"); - printf(" -scan \n"); - printf("Scans a binary file for vulnerabilities\n"); - printf(" -deploym\n"); - printf("Deploys multiple containers from a config file\n"); - printf(" -detach\n"); - printf("Detaches container from /Volumes\n"); - printf(" -extract \n"); - printf("Extracts a container file\n"); - printf(" -help\n"); - printf("Prints this help message\n"); - printf(" --version\n"); - printf("Checks for updates and the current version\n"); - printf(" -update\n"); - printf("Checks for updates and updates the current version\n"); - printf(" -add_plugin \n"); - printf("Adds a plugin\n"); - printf(" -remove_plugin \n"); - printf("Removes a plugin\n"); - } else if (argc > 1 && strcmp(argv[1], "--version") == 0) { - char* latest_version = fetch_latest_version(); - if (latest_version) { - int comparison = compare_versions(VERSION, latest_version); - if (comparison < 0) { - printf("An update is available. Latest version: %s\n", latest_version); - printf("Your current version: %s\n", VERSION); - printf("Please visit https://github.com/Okerew/osxiec/releases/latest to update.\n"); - } else if (comparison == 0) { - printf("You are running the latest version (%s).\n", VERSION); - } else { - printf("Your version (%s) is newer than the latest known version (%s).\n", VERSION, latest_version); - } - free(latest_version); - } else { - printf("Failed to check for updates. Please check your internet connection.\n"); - printf("Your current version: %s\n", VERSION); - } - } else if (strcmp(argv[1], "-update") == 0) { - char* latest_version = fetch_latest_version(); - if (latest_version) { - int comparison = compare_versions(VERSION, latest_version); - if (comparison < 0) { - printf("An update is available. Latest version: %s\n", latest_version); - printf("Your current version: %s\n", VERSION); - if (strcmp(OSXIEC_ARCHITECTURE, "arm64") == 0) { - char update_command[MAX_COMMAND_LEN]; - sprintf(update_command, "curl -L -o osxiec_cli.tar.gz https://github.com/Okerew/osxiec/releases/download/%s/osxiec_cli.tar.gz", latest_version); - system(update_command); - system("tar -xvzf osxiec_cli.tar.gz"); - const char *path = "osxiec_cli"; - - if (chdir(path) != 0) { - perror("chdir() to 'osxiec_cli' failed"); - return 1; - } - - system("sudo sh install.sh"); - } - if (strcmp(OSXIEC_ARCHITECTURE, "86_64") == 0) { - char update_command[MAX_COMMAND_LEN]; - sprintf(update_command, "curl -L -o osxiec_cli_86_64.tar.gz https://github.com/Okerew/osxiec/releases/download/%s/osxiec_cli.tar.gz", latest_version); - system(update_command); - system("tar -xvzf osxiec_cli_86_64.tar.gz"); - const char *path = "osxiec_cli_86_64"; - - if (chdir(path) != 0) { - perror("chdir() to 'osxiec_cli' failed"); - return 1; - } - - system("sudo sh install.sh"); - } - else { - printf("There was some error while updating. Please visit https://github.com/Okerew/osxiec/releases/latest to update.\n"); - } - } else if (comparison == 0) { - printf("You are running the latest version (%s).\n", VERSION); - } - free(latest_version); + system("sudo sh install.sh"); } else { - printf("Failed to check for updates. Please check your internet connection.\n"); - printf("Your current version: %s\n", VERSION); - } - } else if (strcmp(argv[1], "-api") == 0) { - if (strcmp(argv[2], "execute_command") == 0) { - execute_command(argv[3], argv[4]); - } - else if(strcmp(argv[2], "copy_file") == 0) { - copy_file(argv[3], argv[4], argv[5]); - } - else if(strcmp(argv[2], "execute_script_file") == 0) { - execute_script_file(argv[3]); - } - else if (strcmp(argv[2], "get_ip_address") == 0) { - char* ip_address = get_ip_address(); - printf("%s\n", ip_address); - } - else if (strcmp(argv[2], "isbase64") == 0) { - int value_is_base64 = is_base64(argv[3]); - printf("%d\n", value_is_base64); - } - else if (strcmp(argv[2], "find_latest_bin_file") == 0) { - char* latest_bin_file = find_latest_bin_file(argv[3]); - printf("%s\n", latest_bin_file); - } - else if (strcmp(argv[2], "create_directories") == 0) { - create_directories(argv[3]); - } - else if (strcmp(argv[2], "execute_start_config") == 0) { - execute_start_config(argv[3], argv[4]); - } - else if (strcmp(argv[2], "start_network_listener") == 0) { - start_network_listener(argv[1]); - } - else { - printf("This feature is not accessible in the api\n"); - } + printf( + "There was some error while updating. Please visit " + "https://github.com/Okerew/osxiec/releases/latest to update.\n"); + } + } else if (comparison == 0) { + printf("You are running the latest version (%s).\n", VERSION); + } + free(latest_version); } else { - fprintf(stderr, "Unknown command: %s\n", argv[1]); - return EXIT_FAILURE; + printf("Failed to check for updates. Please check your internet " + "connection.\n"); + printf("Your current version: %s\n", VERSION); + } + } else if (strcmp(argv[1], "-api") == 0) { + if (strcmp(argv[2], "execute_command") == 0) { + execute_command(argv[3], argv[4]); + } else if (strcmp(argv[2], "copy_file") == 0) { + copy_file(argv[3], argv[4], argv[5]); + } else if (strcmp(argv[2], "execute_script_file") == 0) { + execute_script_file(argv[3]); + } else if (strcmp(argv[2], "get_ip_address") == 0) { + char *ip_address = get_ip_address(); + printf("%s\n", ip_address); + } else if (strcmp(argv[2], "isbase64") == 0) { + int value_is_base64 = is_base64(argv[3]); + printf("%d\n", value_is_base64); + } else if (strcmp(argv[2], "find_latest_bin_file") == 0) { + char *latest_bin_file = find_latest_bin_file(argv[3]); + printf("%s\n", latest_bin_file); + } else if (strcmp(argv[2], "create_directories") == 0) { + create_directories(argv[3]); + } else if (strcmp(argv[2], "execute_start_config") == 0) { + execute_start_config(argv[3], argv[4]); + } else if (strcmp(argv[2], "start_network_listener") == 0) { + start_network_listener(argv[1]); + } else { + printf("This feature is not accessible in the api\n"); } + } else { + fprintf(stderr, "Unknown command: %s\n", argv[1]); + return EXIT_FAILURE; + } - return EXIT_SUCCESS; + return EXIT_SUCCESS; } From 81ca8d7ebd308ee3af6392fa86d13095a93e7db4 Mon Sep 17 00:00:00 2001 From: Okerew <93822247+Okerew@users.noreply.github.com> Date: Tue, 4 Mar 2025 18:44:08 +0100 Subject: [PATCH 18/23] Update osxiec.h --- osxiec.h | 96 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 59 insertions(+), 37 deletions(-) diff --git a/osxiec.h b/osxiec.h index b0c8d60..3e33723 100644 --- a/osxiec.h +++ b/osxiec.h @@ -6,37 +6,43 @@ #define MAX_PATH_LEN 256 typedef struct { - char name[MAX_PATH_LEN]; - size_t size; - char *data; + char name[MAX_PATH_LEN]; + size_t size; + char *data; } File; #define MAX_CLIENTS 15 typedef struct { - char name[MAX_PATH_LEN]; - long memory_soft_limit; - long memory_hard_limit; - int cpu_priority; - char network_mode[20]; - uid_t container_uid; - gid_t container_gid; - char network_name[MAX_PATH_LEN]; - int vlan_id; - char start_config[MAX_PATH_LEN]; + char name[MAX_PATH_LEN]; + long memory_soft_limit; + long memory_hard_limit; + int cpu_priority; + char network_mode[20]; + uid_t container_uid; + gid_t container_gid; + char network_name[MAX_PATH_LEN]; + int vlan_id; + char start_config[MAX_PATH_LEN]; } ContainerConfig; typedef struct { - char name[MAX_PATH_LEN]; - int vlan_id; - int num_containers; - char container_names[MAX_CLIENTS][MAX_PATH_LEN]; - char container_ips[MAX_CLIENTS][16]; + char name[MAX_PATH_LEN]; + int vlan_id; + int num_containers; + char container_names[MAX_CLIENTS][MAX_PATH_LEN]; + char container_ips[MAX_CLIENTS][16]; } ContainerNetwork; void execute_command(const char *command, const char *container_root); -void containerize_directory_with_bin_file(const char *dir_path, const char *input_bin_file, const char *output_file, const char *start_config_file, const char *container_config_file); -void containerize_directory(const char *dir_path, const char *output_file, const char *start_config_file, const char *container_config_file); +void containerize_directory_with_bin_file(const char *dir_path, + const char *input_bin_file, + const char *output_file, + const char *start_config_file, + const char *container_config_file); +void containerize_directory(const char *dir_path, const char *output_file, + const char *start_config_file, + const char *container_config_file); void read_config_file(const char *filename, ContainerConfig *config); void extract_container(const char *osxiec_file, const char *output_dir); void security_scan(const char *bin_file); @@ -51,29 +57,37 @@ void auto_scale_resources(const ContainerConfig *config); void start_auto_scaling(ContainerConfig *config); void handle_client(int client_socket, const char *container_root); void start_network_listener(const char *container_root); -void create_isolated_environment(FILE *bin_file, const char *bin_file_path, ContainerNetwork *network); +void create_isolated_environment(FILE *bin_file, const char *bin_file_path, + ContainerNetwork *network); void ocreate_isolated_environment(FILE *bin_file, const char *bin_file_path); void gcreate_isolated_environment(FILE *bin_file, const char *bin_file_path); -static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp); +static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, + void *userp); void search(const char *term); -void download_file(const char* file_name); -void upload_file(const char *filename, const char *username, const char *password, const char *description); -void convert_to_docker(const char *osxiec_file, const char *output_dir, const char *base_image, const char *custom_dockerfile); +void download_file(const char *file_name); +void upload_file(const char *filename, const char *username, + const char *password, const char *description); +void convert_to_docker(const char *osxiec_file, const char *output_dir, + const char *base_image, const char *custom_dockerfile); void clean_container_dmgs(); -void convert_to_oci(const char *osxiec_file, const char *output_dir, const char *arch, const char *author, const char *created); -char* find_latest_bin_file(const char *volume_name); -int copy_file(const char *source, const char *destination_folder, const char *new_file_name); +void convert_to_oci(const char *osxiec_file, const char *output_dir, + const char *arch, const char *author, const char *created); +char *find_latest_bin_file(const char *volume_name); +int copy_file(const char *source, const char *destination_folder, + const char *new_file_name); size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp); -char* fetch_latest_version(void); -int compare_versions(const char* v1, const char* v2); -int add_plugin(const char* plugin_source); -int copy_file(const char *source, const char *destination_folder, const char *new_file_name); -char* find_latest_bin_file(const char *volume_name); +char *fetch_latest_version(void); +int compare_versions(const char *v1, const char *v2); +int add_plugin(const char *plugin_source); +int copy_file(const char *source, const char *destination_folder, + const char *new_file_name); +char *find_latest_bin_file(const char *volume_name); void create_directory_if_needed(const char *path); void handle_signal(int sig); void *logger_thread(void *arg); void signal_handler(); -void navigate_history(char *command, int *command_index, int *cursor_pos, int direction); +void navigate_history(char *command, int *command_index, int *cursor_pos, + int direction); void add_to_history(const char *command); void clear_line(); void move_cursor_right(int n); @@ -88,13 +102,21 @@ void create_directories(const char *file_path); void create_shared_folder(); void handle_script_file(const char *filename); void handle_script_command(const char *script_content); -void scale_container_resources(long memory_soft_limit, long memory_hard_limit, int cpu_priority); +void scale_container_resources(long memory_soft_limit, long memory_hard_limit, + int cpu_priority); void enable_container_communication(ContainerNetwork *network); -void setup_network_isolation(ContainerConfig *config, ContainerNetwork *network); +void setup_network_isolation(ContainerConfig *config, + ContainerNetwork *network); char *get_ip_address(); void execute_start_config(const char *config_file, const char *container_root); int is_subpath(const char *path, const char *base); void handle_debug_command(char *command); void print_container_state(); void update_container_state(); -#endif //OSXIEC_H +void broadcast_command_to_network(const char *network_name, const char *command, + int port); +int copy_volume_to_directory(const char *volume_name, const char *target_dir); +void update_container_config(const char *container_file, + const char *new_config_file); +int remove_plugin(const char *plugin_name); +#endif // OSXIEC_H From 08e3ecb1249690a911886cfeaa5e5a90f7c20074 Mon Sep 17 00:00:00 2001 From: Okerew <93822247+Okerew@users.noreply.github.com> Date: Tue, 4 Mar 2025 18:44:29 +0100 Subject: [PATCH 19/23] Update README.md --- README.md | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d4bdb45..9a33742 100644 --- a/README.md +++ b/README.md @@ -199,10 +199,25 @@ sudo osxiec -gstart {volume_name} osxiec -api {argument} ``` -**Update** checks for updates and updates +**Update** updates configuration of a container ```sh sudo osxiec -update ``` + +**Check for update** checks for update +```sh +sudo osxiec -check-for-update +``` +**copy-volume** Copies files form a container volume to a directory +```sh +sudo osxiec -copy-volume {volume_name} {target_directory} +``` + +**bcn** Broadcasts a command to the container network +```sh +sudo osxiec -bcn {network_name} {command} +``` + ## Creating a container Make sure to include any dependencies or executables you can obtain these by searching for where a dependency or executable is located and copying it along with it's dependencies. @@ -582,4 +597,5 @@ After this on the execution of osxiec command the plugin will be loaded. Chroot requires for SIP to be disabled, which causes many security risks, chroot can be easily exited by any process, using the normal macOS restrictions is way more secure, reliable, having it disabled causes many permission issues. - **Sandbox deprecation error** yes I know that sandbox innit is deprecated but there isn't really an alternative for it unless I would use xcode and there is no way I am using it to rebuild this. ---- + +- **If you want to remove ips from the network do it by editing the file in private/etc/network_{network_name}.conf file** From 8f5c4b621cfb36a9ff694b162bde23f5a9b08e09 Mon Sep 17 00:00:00 2001 From: Okerew <93822247+Okerew@users.noreply.github.com> Date: Tue, 4 Mar 2025 18:48:53 +0100 Subject: [PATCH 20/23] Update README.md --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 9a33742..bca1367 100644 --- a/README.md +++ b/README.md @@ -291,6 +291,12 @@ If you want to access the container terminal just press enter. 6. **autoscale** automatically scales the resources of the container 7. **status** shows the status of the container 8. **stop** stops the container. +9. **wait** waits for a command(background task) in a container to stop. +10. **ps** shows all the running commands(background tasks) in the container. +11. **pause** pauses all background tasks in the container +12. **unpause** unpauses all background tasks in the container +13. **br** runs a command in background + ## Creating a vlan network To create a network you can run `osxiec -network create {network_name} {vlan_id}` From 7f7a1f26c61f625dd2e7a4718940d1a7e6aaff9f Mon Sep 17 00:00:00 2001 From: Okerew <93822247+Okerew@users.noreply.github.com> Date: Sun, 23 Mar 2025 18:55:19 +0100 Subject: [PATCH 21/23] Update osxiec.c --- osxiec.c | 867 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 788 insertions(+), 79 deletions(-) diff --git a/osxiec.c b/osxiec.c index d5e845c..9915d73 100644 --- a/osxiec.c +++ b/osxiec.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -39,8 +40,11 @@ #define MAX_MEMORY_LIMIT 2147483648 // 2 GB max memory limit #define MAX_HISTORY_LEN 100 #define MAX_LINE_LEN 1024 -#define VERSION "v0.73" +#define VERSION "v0.9" #define MAX_BACKGROUND_THREADS 32 +#define EVENT_CONTAINER_STOP 0 +#define EVENT_CONTAINER_START 1 +#define MAX_SCHEDULED_TASKS 32 int port = PORT; @@ -907,16 +911,20 @@ ContainerNetwork load_container_network(const char *name) { return network; } -void create_and_save_container_network(const char *name, int vlan_id) { +void create_and_save_container_network(const char *name, int vlan_id, + const char *allowed_ip) { ContainerNetwork network; strncpy(network.name, name, MAX_PATH_LEN - 1); + network.name[MAX_PATH_LEN - 1] = '\0'; // Ensure null termination network.vlan_id = vlan_id; network.num_containers = 0; + // Set allowed IP if provided, otherwise set to NULL or empty string + char has_allowed_ip = (allowed_ip != NULL && strlen(allowed_ip) > 0); + // Save the network configuration to a file char filename[MAX_PATH_LEN]; snprintf(filename, sizeof(filename), "/tmp/network_%s.conf", name); - FILE *file = fopen(filename, "w"); if (file == NULL) { perror("Failed to save network configuration"); @@ -925,10 +933,20 @@ void create_and_save_container_network(const char *name, int vlan_id) { fprintf(file, "name=%s\n", network.name); fprintf(file, "vlan_id=%d\n", network.vlan_id); - fclose(file); - printf("Created and saved network %s with VLAN ID %d\n", network.name, - network.vlan_id); + // Add the allowed IP to the configuration if specified + if (has_allowed_ip) { + fprintf(file, "allowed_ip=%s\n", allowed_ip); + printf("Created and saved network %s with VLAN ID %d and restricted to IP " + "%s\n", + network.name, network.vlan_id, allowed_ip); + } else { + printf("Created and saved network %s with VLAN ID %d with no IP " + "restrictions\n", + network.name, network.vlan_id); + } + + fclose(file); } void remove_container_network(const char *name) { @@ -1642,9 +1660,9 @@ void start_network_thread(pthread_t network_thread, int network_thread_active) { printf("Network listener is already running\n"); return; } - + if (pthread_create(&network_thread, NULL, - (void *(*)(void *))start_network_listener, NULL) != 0) { + (void *(*)(void *))start_network_listener, NULL) != 0) { perror("Failed to create network listener thread"); } else { network_thread_active = 1; @@ -1657,13 +1675,513 @@ void stop_network_thread(pthread_t network_thread, int network_thread_active) { printf("Network listener is not running\n"); return; } - + pthread_cancel(network_thread); pthread_join(network_thread, NULL); network_thread_active = 0; printf("Network listener stopped successfully\n"); } +void trace_command(const char *command, const char *container_root) { + printf("Starting command tracing for: %s\n", command); + char trace_log_path[MAX_PATH_LEN]; + snprintf(trace_log_path, sizeof(trace_log_path), "%s/trace_log.txt", + container_root); + + // Create a unique wrapper script + char wrapper_path[MAX_PATH_LEN]; + snprintf(wrapper_path, sizeof(wrapper_path), "%s/trace_wrapper_%d.sh", + container_root, (int)time(NULL)); + + FILE *wrapper = fopen(wrapper_path, "w"); + if (!wrapper) { + perror("Failed to create wrapper script"); + return; + } + + // Create a script that will record execution info without requiring + // privileges + fprintf(wrapper, "#!/bin/sh\n"); + fprintf(wrapper, "echo \"=== Command Trace: %s ===\" > %s\n", command, + trace_log_path); + fprintf(wrapper, "echo \"Started at $(date)\" >> %s\n", trace_log_path); + fprintf(wrapper, "echo \"Current directory: $(pwd)\" >> %s\n", + trace_log_path); + fprintf(wrapper, "echo \"Environment variables:\" >> %s\n", trace_log_path); + fprintf(wrapper, "env | sort >> %s\n", trace_log_path); + fprintf(wrapper, "echo \"\\nCommand output:\" >> %s\n", trace_log_path); + fprintf(wrapper, "echo \"-------------------\" >> %s\n", trace_log_path); + fprintf(wrapper, "# Run the command and capture timing info\n"); + fprintf(wrapper, "START=$(date +%%s.%%N)\n"); + fprintf(wrapper, "{ time %s ; } 2>&1 | tee -a %s\n", command, trace_log_path); + fprintf(wrapper, "END=$(date +%%s.%%N)\n"); + fprintf(wrapper, + "echo \"\\nExecution time: $(echo \"$END - $START\" | bc) seconds\" " + ">> %s\n", + trace_log_path); + fprintf(wrapper, "echo \"Finished at $(date)\" >> %s\n", trace_log_path); + fprintf(wrapper, "echo \"Exit status: $?\" >> %s\n", trace_log_path); + fprintf(wrapper, "echo \"-------------------\" >> %s\n", trace_log_path); + + // Replace ps command with /proc inspection for memory usage + fprintf(wrapper, "echo \"Memory usage after execution:\" >> %s\n", + trace_log_path); + fprintf(wrapper, "echo \"PID COMMAND RSS VSZ\" >> %s\n", + trace_log_path); + fprintf(wrapper, "for pid in $(pgrep -f \"%s\"); do\n", command); + fprintf(wrapper, " if [ -d \"/proc/$pid\" ]; then\n"); + fprintf(wrapper, + " cmd=$(cat /proc/$pid/cmdline | tr '\\0' ' ' | head -c 30)\n"); + fprintf(wrapper, " if [ -f \"/proc/$pid/status\" ]; then\n"); + fprintf(wrapper, + " rss=$(grep VmRSS /proc/$pid/status | awk '{print $2}')\n"); + fprintf(wrapper, + " vsz=$(grep VmSize /proc/$pid/status | awk '{print $2}')\n"); + fprintf(wrapper, + " echo \"$pid $cmd ${rss:-0} ${vsz:-0}\" >> %s\n", + trace_log_path); + fprintf(wrapper, " fi\n"); + fprintf(wrapper, " fi\n"); + fprintf(wrapper, "done\n"); + + // Use lsof for file descriptors since it works + fprintf(wrapper, "echo \"File descriptors after execution:\" >> %s\n", + trace_log_path); + fprintf(wrapper, "for pid in $(pgrep -f \"%s\"); do\n", command); + fprintf(wrapper, " echo \"File descriptors for PID $pid:\" >> %s\n", + trace_log_path); + fprintf(wrapper, + " lsof -p $pid 2>/dev/null | head -20 >> %s 2>&1 || echo \"No file " + "descriptor info available\" >> %s\n", + trace_log_path, trace_log_path); + fprintf(wrapper, "done\n"); + + fclose(wrapper); + chmod(wrapper_path, 0755); + + printf("Executing command with tracing...\n"); + printf("----------------\n"); + + // Execute the wrapper script + system(wrapper_path); + + // Read and display the log + FILE *log = fopen(trace_log_path, "r"); + if (log) { + char buffer[1024]; + while (fgets(buffer, sizeof(buffer), log)) { + printf("%s", buffer); + } + fclose(log); + } + + // Clean up the wrapper script + unlink(wrapper_path); + + printf("----------------\n"); + printf("Trace completed. Log saved to %s\n", trace_log_path); +} + +void trace_background_process(int process_id, const char *container_root) { + // First, check if the process ID exists and is a running background task + int valid_task = 0; + // Iterate through the background tasks + for (int i = 0; i < bg_manager.task_count; i++) { + BackgroundTask *task = &bg_manager.tasks[i]; + if (task->pid == process_id && task->is_running == 1) { + valid_task = 1; + break; + } + } + if (!valid_task) { + printf("Error: Process ID %d is not a valid running background task\n", + process_id); + return; + } + + printf("Starting trace for background process ID: %d\n", process_id); + char trace_log_path[MAX_PATH_LEN]; + snprintf(trace_log_path, sizeof(trace_log_path), "%s/proc_trace_%d.txt", + container_root, process_id); + + // Create a monitor script that samples the process periodically + char monitor_path[MAX_PATH_LEN]; + snprintf(monitor_path, sizeof(monitor_path), "%s/proc_monitor_%d.sh", + container_root, process_id); + + FILE *monitor = fopen(monitor_path, "w"); + if (!monitor) { + perror("Failed to create monitor script"); + return; + } + + // Create a script that will monitor the process without requiring privileges + fprintf(monitor, "#!/bin/sh\n"); + fprintf(monitor, "echo \"=== Process Trace: PID %d ===\" > %s\n", process_id, + trace_log_path); + fprintf(monitor, "echo \"Started monitoring at $(date)\" >> %s\n", + trace_log_path); + + // Use /proc filesystem instead of ps for process info + fprintf(monitor, "echo \"\\nInitial process info:\" >> %s\n", trace_log_path); + fprintf(monitor, + "echo \"PID PPID COMMAND CPU MEM RSS VSZ STATE START TIME\" >> %s\n", + trace_log_path); + fprintf(monitor, "if [ -d \"/proc/%d\" ]; then\n", process_id); + fprintf( + monitor, + " cmd=$(cat /proc/%d/cmdline 2>/dev/null | tr '\\0' ' ' | head -c 30)\n", + process_id); + fprintf(monitor, + " ppid=$(cat /proc/%d/stat 2>/dev/null | awk '{print $4}')\n", + process_id); + fprintf(monitor, + " state=$(cat /proc/%d/stat 2>/dev/null | awk '{print $3}')\n", + process_id); + fprintf(monitor, " if [ -f \"/proc/%d/status\" ]; then\n", process_id); + fprintf( + monitor, + " rss=$(grep VmRSS /proc/%d/status 2>/dev/null | awk '{print $2}')\n", + process_id); + fprintf( + monitor, + " vsz=$(grep VmSize /proc/%d/status 2>/dev/null | awk '{print $2}')\n", + process_id); + fprintf(monitor, " fi\n"); + fprintf(monitor, " start=$(stat -c %%Y /proc/%d 2>/dev/null)\n", process_id); + fprintf(monitor, " if [ -n \"$start\" ]; then\n"); + fprintf(monitor, " start_time=$(date -d @$start '+%%H:%%M' 2>/dev/null || " + "date -r $start '+%%H:%%M' 2>/dev/null)\n"); + fprintf(monitor, " else\n"); + fprintf(monitor, " start_time=\"unknown\"\n"); + fprintf(monitor, " fi\n"); + fprintf(monitor, " uptime=$(cut -d' ' -f1 /proc/uptime 2>/dev/null)\n"); + fprintf(monitor, " if [ -f \"/proc/%d/stat\" ]; then\n", process_id); + fprintf(monitor, + " utime=$(cat /proc/%d/stat 2>/dev/null | awk '{print $14}')\n", + process_id); + fprintf(monitor, + " stime=$(cat /proc/%d/stat 2>/dev/null | awk '{print $15}')\n", + process_id); + fprintf(monitor, " if [ -n \"$utime\" ] && [ -n \"$stime\" ]; then\n"); + fprintf(monitor, " total_time=$((utime + stime))\n"); + fprintf(monitor, + " clock_ticks=$(getconf CLK_TCK 2>/dev/null || echo 100)\n"); + fprintf(monitor, " total_time_sec=$(echo \"scale=2; $total_time / " + "$clock_ticks\" | bc 2>/dev/null)\n"); + fprintf(monitor, " echo \"$total_time_sec seconds CPU time\" >> %s\n", + trace_log_path); + fprintf(monitor, " fi\n"); + fprintf(monitor, " fi\n"); + fprintf( + monitor, + " echo \"%d ${ppid:-?} ${cmd:-unknown} ${cpu:-?} ${mem:-?} ${rss:-0} " + "${vsz:-0} ${state:-?} ${start_time:-?} ${total_time_sec:-?}\" >> %s\n", + process_id, trace_log_path); + fprintf(monitor, "else\n"); + fprintf(monitor, + " echo \"Process %d not found in /proc filesystem\" >> %s\n", + process_id, trace_log_path); + fprintf(monitor, "fi\n"); + + fprintf( + monitor, + "echo \"\\nMonitoring process activity (Press Ctrl+C to stop):\" >> %s\n", + trace_log_path); + fprintf(monitor, "echo \"-------------------\" >> %s\n", trace_log_path); + fprintf(monitor, "while kill -0 %d 2>/dev/null; do\n", process_id); + fprintf(monitor, " echo \"\\n[$(date)] Process snapshot:\" >> %s\n", + trace_log_path); + + // Periodic process monitoring without ps + fprintf(monitor, " if [ -d \"/proc/%d\" ]; then\n", process_id); + fprintf(monitor, " echo \"Process status:\" >> %s\n", trace_log_path); + fprintf(monitor, + " cat /proc/%d/status 2>/dev/null | grep -E " + "'Name|State|Pid|PPid|VmRSS|VmSize|Threads' >> %s 2>&1 || echo \"No " + "status info available\" >> %s\n", + process_id, trace_log_path, trace_log_path); + fprintf(monitor, " fi\n"); + + // Use lsof for file and socket information + fprintf(monitor, " echo \"Open files and sockets:\" >> %s\n", + trace_log_path); + fprintf(monitor, + " lsof -p %d 2>/dev/null | head -20 >> %s 2>&1 || echo \"No file " + "descriptor info available\" >> %s\n", + process_id, trace_log_path, trace_log_path); + + // Check for child processes using /proc + fprintf(monitor, " echo \"Child processes:\" >> %s\n", trace_log_path); + fprintf(monitor, " for cpid in /proc/[0-9]*/stat; do\n"); + fprintf(monitor, " if [ -f \"$cpid\" ]; then\n"); + fprintf(monitor, " ppid=$(cat $cpid 2>/dev/null | awk '{print $4}')\n"); + fprintf(monitor, " if [ \"$ppid\" = \"%d\" ]; then\n", process_id); + fprintf(monitor, " pid=$(basename $(dirname $cpid))\n"); + fprintf(monitor, " cmd=$(cat /proc/$pid/cmdline 2>/dev/null | tr " + "'\\0' ' ' | head -c 30)\n"); + fprintf(monitor, " echo \"$pid $ppid ${cmd:-unknown}\" >> %s\n", + trace_log_path); + fprintf(monitor, " fi\n"); + fprintf(monitor, " fi\n"); + fprintf(monitor, " done\n"); + + fprintf(monitor, " sleep 1\n"); + fprintf(monitor, "done\n"); + fprintf( + monitor, + "echo \"\\nProcess %d has terminated or is no longer visible\" >> %s\n", + process_id, trace_log_path); + fprintf(monitor, "echo \"Monitoring stopped at $(date)\" >> %s\n", + trace_log_path); + + fclose(monitor); + chmod(monitor_path, 0755); + + printf("Starting process monitoring. Press Ctrl+C to stop.\n"); + printf("The log will be saved to %s\n", trace_log_path); + + // Run the monitor script + char monitor_cmd[MAX_COMMAND_LEN]; + snprintf(monitor_cmd, sizeof(monitor_cmd), "%s &", monitor_path); + system(monitor_cmd); + + printf("Monitoring started in background. Use 'ls -la %s' to see the log " + "file.\n", + trace_log_path); + printf("You can view the log file at any time with 'cat %s'\n", + trace_log_path); +} + +// Global variable declaration for the attach interrupt flag +volatile sig_atomic_t attach_interrupted = 0; + +// Signal handler for attaching to processes +void handle_attach_interrupt(int sig) { attach_interrupted = 1; } + +void live_process_inspection(const char *container_root) { + printf("\033[2J\033[H"); // Clear screen and move cursor to top + printf("Live Process Inspection (Press 'q' to exit)\n"); + printf("%-6s %-10s %-5s %-5s %-10s %-10s %s\n", "PID", "USER", "CPU%", "MEM%", + "VSZ", "RSS", "COMMAND"); + + set_terminal_raw_mode(); + + int running = 1; + while (running) { + char ps_command[MAX_COMMAND_LEN]; + snprintf(ps_command, sizeof(ps_command), + "ps -eo pid,user,pcpu,pmem,vsz,rss,comm | grep -v grep"); + + FILE *pipe = popen(ps_command, "r"); + if (!pipe) { + perror("Failed to run ps command"); + break; + } + + // Skip header line from ps output + char buffer[1024]; + fgets(buffer, sizeof(buffer), pipe); + + printf("\033[3;1H"); // Move cursor to line 3 + printf("\033[J"); // Clear from cursor to end of screen + + int line = 0; + while (fgets(buffer, sizeof(buffer), pipe) != NULL) { + // Filter processes that belong to the container namespace + if (strstr(buffer, container_root) || + 1) { // Always true for now, implement proper filtering if needed + printf("%-80s\n", buffer); + line++; + if (line > 20) + break; // Limit display to 20 processes + } + } + + pclose(pipe); + + // Poll for keypress + fd_set fds; + struct timeval tv; + FD_ZERO(&fds); + FD_SET(STDIN_FILENO, &fds); + tv.tv_sec = 1; + tv.tv_usec = 0; + + int result = select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv); + if (result > 0) { + char c = getchar(); + if (c == 'q' || c == 'Q') { + running = 0; + } + } + } + + set_terminal_canonical_mode(); + printf("\nExiting process inspector\n"); +} + +void attach_to_background_task(int task_id) { + if (task_id < 0 || task_id >= MAX_BACKGROUND_THREADS) { + printf("Invalid task ID\n"); + return; + } + + int valid_task = 0; + for (int i = 0; i < bg_manager.task_count; i++) { + if (i == task_id && bg_manager.tasks[i].is_running == 1) { + valid_task = 1; + break; + } + } + + if (!valid_task) { + printf("No running task with ID %d\n", task_id); + return; + } + + BackgroundTask *task = &bg_manager.tasks[task_id]; + printf("Attaching to task %d (PID %d): %s\n", task_id, task->pid, + task->command); + printf("Press Ctrl+C to detach (this will NOT terminate the process)\n"); + + // Set up signal handler for Ctrl+C + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = handle_attach_interrupt; + sigaction(SIGINT, &sa, NULL); + + set_terminal_raw_mode(); + + // Print current output + pthread_mutex_lock(&task->output_mutex); + printf("%s", task->output); + pthread_mutex_unlock(&task->output_mutex); + + int running = 1; + char last_seen[4096] = {0}; + strncpy(last_seen, task->output, sizeof(last_seen) - 1); + + while (running && task->is_running == 1) { + // Check if there's new content in the task's output buffer + pthread_mutex_lock(&task->output_mutex); + + // Check if output has changed + if (strcmp(last_seen, task->output) != 0) { + // Find the new content by comparing with what we've seen + size_t match_len = 0; + while (match_len < strlen(last_seen) && + match_len < strlen(task->output) && + last_seen[match_len] == task->output[match_len]) { + match_len++; + } + + // Print only the new content + if (strlen(task->output) > match_len) { + printf("%s", task->output + match_len); + fflush(stdout); + } + + // Update last seen content + strncpy(last_seen, task->output, sizeof(last_seen) - 1); + } + + pthread_mutex_unlock(&task->output_mutex); + + // Check task status + if (kill(task->pid, 0) != 0) { + // Process no longer exists + task->is_running = 0; + break; + } + + // Check for detach signal + if (attach_interrupted) { + running = 0; + attach_interrupted = 0; + printf("\nDetached from task %d (process continuing in background)\n", + task_id); + } + + usleep(100000); // Sleep for 100ms to reduce CPU usage + } + + // Restore default signal handler + sa.sa_handler = SIG_DFL; + sigaction(SIGINT, &sa, NULL); + + set_terminal_canonical_mode(); + + if (task->is_running == 0) { + printf("\nTask %d has completed\n", task_id); + } +} + +typedef struct { + char command[MAX_COMMAND_LEN]; + time_t scheduled_time; // Time at which the command should be executed +} ScheduledTask; + +ScheduledTask scheduled_tasks[MAX_SCHEDULED_TASKS]; +int num_scheduled_tasks = 0; + +void schedule_command(const char *command, time_t scheduled_time) { + if (num_scheduled_tasks >= MAX_SCHEDULED_TASKS) { + printf("Cannot schedule more tasks. Maximum limit reached.\n"); + return; + } + + ScheduledTask *task = &scheduled_tasks[num_scheduled_tasks++]; + strncpy(task->command, command, MAX_COMMAND_LEN); + task->scheduled_time = scheduled_time; + printf("Task scheduled: %s\n", command); +} + +void check_scheduled_tasks(char *container_root) { + time_t current_time = time(NULL); + + for (int i = 0; i < num_scheduled_tasks; i++) { + ScheduledTask *task = &scheduled_tasks[i]; + + if (task->scheduled_time <= current_time) { + printf("Executing scheduled task: %s\n", task->command); + execute_command(task->command, container_root); + + // Remove the task from the list + memmove(&scheduled_tasks[i], &scheduled_tasks[i + 1], + (num_scheduled_tasks - i - 1) * sizeof(ScheduledTask)); + num_scheduled_tasks--; + i--; // Adjust the index after removal + } + } +} + +void list_scheduled_tasks() { + printf("Scheduled tasks:\n"); + for (int i = 0; i < num_scheduled_tasks; i++) { + ScheduledTask *task = &scheduled_tasks[i]; + printf("%d. %s\n", i + 1, task->command); + } +} + +time_t parse_time(const char *time_str) { + struct tm time_struct; + char *format; + + format = "%H:%M"; + if (strptime(time_str, format, &time_struct) != NULL) { + return mktime(&time_struct); + } + + format = "%Y-%m-%d %H:%M"; + if (strptime(time_str, format, &time_struct) != NULL) { + return mktime(&time_struct); + } + + // If none of the formats match, return -1 + return -1; +} + void create_isolated_environment(FILE *bin_file, const char *bin_file_path, ContainerNetwork *network) { signal(SIGTERM, handle_signal); @@ -1956,8 +2474,57 @@ void create_isolated_environment(FILE *bin_file, const char *bin_file_path, } else if (strcmp(command, "network stop") == 0) { stop_network_thread(network_thread, network_thread_active); } else if (strcmp(command, "network status") == 0) { - printf("Network listener status: %s\n", + printf("Network listener status: %s\n", network_thread_active ? "running" : "stopped"); + } else if (strncmp(command, "trace ", 6) == 0) { + char *cmd = command + 6; // Skip "trace " prefix + while (isspace(*cmd)) + cmd++; // Skip any additional whitespace + if (*cmd) { + trace_command(cmd, container_root); + } else { + printf("Usage: trace \n"); + } + } else if (strncmp(command, "proctrace ", 10) == 0) { + int process_id; + if (sscanf(command + 10, "%d", &process_id) == 1) { + // Pass the background_tasks array and MAX_BACKGROUND_TASKS constant + trace_background_process(process_id, container_root); + } else { + printf("Usage: proctrace \n"); + } + } else if (strcmp(command, "cps") == 0) { + // Live process inspection - like top/htop + live_process_inspection(container_root); + } else if (strncmp(command, "attach ", 7) == 0) { + int task_id; + if (sscanf(command + 7, "%d", &task_id) == 1) { + attach_to_background_task(task_id); + } else { + printf("Usage: attach \n"); + } + } else if (strncmp(command, "schedule", 8) == 0) { + char *cmd = command + 9; // Skip "schedule " prefix + while (isspace(*cmd)) + cmd++; // Skip any additional whitespace + + char *time_str = strtok(cmd, " "); + char *event_str = strtok(NULL, " "); + char *task_command = strtok(NULL, ""); + + if (time_str && event_str && task_command) { + time_t scheduled_time = parse_time(time_str); + + if (scheduled_time != -1) { + schedule_command(task_command, scheduled_time); + } else { + printf("Invalid time or event type.\n"); + } + } else { + printf("Usage: schedule