Rewrote a cleaner version of container / backing systems

This commit is contained in:
Alexei KADIR 2024-02-28 17:50:27 +01:00
parent 788d46d071
commit a02cd9fe20
21 changed files with 1453 additions and 2489 deletions

View File

@ -1,22 +1,22 @@
# ---- ---- #
CC = gcc
CF = -Wall -lkrb5 -lvirt -ljson-c -lcrypto -g
CF = -Wall -lkrb5 -lvirt -ljson-c -lcrypto -g -pedantic
# ---- ---- #
build: $(shell find . -name "*.c") $(shell find . -name "*.h")
@mkdir -p bin
@$(CC) $(CF) -o ./bin/sandbox $(shell find . -name "*.c")
$(CC) $(CF) -o ./bin/sandbox $(shell find . -name "*.c")
run: build
@./bin/sandbox
./bin/sandbox
debug: build
@gdb -q ./bin/sandbox
gdb -q ./bin/sandbox
clean:
@rm -rf ./bin
rm -rf ./bin
.PHONY: build run clean

4
src/auth.h Normal file
View File

@ -0,0 +1,4 @@
#pragma once
#include "utils.h"

646
src/backing.c Executable file → Normal file

File diff suppressed because it is too large Load Diff

114
src/backing.h Executable file → Normal file
View File

@ -1,88 +1,76 @@
#pragma once
#include "utils.h"
#include "disk.h"
#include "config.h"
/// @brief Checks whether the given string is a valid backing identifier. If the string is not a valid backing identifier, the call will return a failure result with an error message.
/// @param backing The string to check.
/// @brief Checks whether the specified backing identifier is valid. This call returns a failure if the identifier is invalid.
/// @param backing The backing identifier to check.
/// @return The result of the operation.
result_t check_backing_identifier(const char* backing);
/// @brief Checks whether the given backing exists. If the backing does not exist, the call will return a failure result with an error message.
/// @param backing The backing to check.
/// @brief Returns the path of the backing pool.
/// @param _path The pointer to where the path should be stored. The caller is responsible for freeing the path.
/// @param config The program configuration to use.
/// @return The result of the operation.
result_t check_backing_exists(const char* backing);
result_t get_backing_pool_path(char** _path, const config_t* config);
/// @brief Gets the backing pool path.
/// @param _path The string pointer to store the resulting path in. The caller is responsible for freeing the string.
/// @brief Returns the path of the specified backing.
/// @param _path The pointer to where the path should be stored. The caller is responsible for freeing the path.
/// @param config The program configuration to use.
/// @param backing The identifier of the backing to get the path of.
/// @return The result of the operation.
result_t get_backing_pool_path(char** _path);
result_t get_backing_path(char** _path, const config_t* config, const char* backing);
/// @brief Gets the path of the given backing.
/// @param _path The string pointer to store the resulting path in. The caller is responsible for freeing the string.
/// @param backing The backing to get the path of.
/// @brief Returns the path of the default disk file.
/// @param _path The pointer to where the path should be stored. The caller is responsible for freeing the path.
/// @param config The program configuration to use.
/// @return The result of the operation.
result_t get_backing_path(char** _path, const char* backing);
result_t get_backing_default_path(char** _path, const config_t* config);
/// @brief Gets the path of the file containing the default backing identifier.
/// @param _path The string pointer to store the resulting path in. The caller is responsible for freeing the string.
/// @brief Returns the path of the temporary disk file.
/// @param _path The pointer to where the path should be stored. The caller is responsible for freeing the path.
/// @param config The program configuration to use.
/// @return The result of the operation.
result_t get_backing_default_path(char** _path);
result_t get_backing_temporary_path(char** _path, const config_t* config);
/// @brief Gets the path of the temporary path to use for backing files.
/// @param _path The string pointer to store the resulting path in. The caller is responsible for freeing the string.
/// @brief Checks whether the specified backing exists. This call returns a failure if the backing does not exist.
/// @param config The program configuration to use.
/// @param backing The identifier of the backing to check.
/// @return Whether the backing exists.
result_t check_backing_exists(const config_t* config, const char* backing);
/// @brief Returns the default backing identifier.
/// @param _backing The pointer to where the backing should be stored. The caller is responsible for freeing the backing.
/// @param config The program configuration to use.
/// @return The result of the operation.
result_t get_backing_temp_path(char** _path);
/// @brief Adds a backing to the pool, from the given container.
/// @param _backing The string pointer to store the resulting backing identifier in. The caller is responsible for freeing the string.
/// @param container The container to use as the source for the backing.
/// @return The result of the operation.
result_t add_backing(char** _backing, const char* container);
/// @brief Imports a backing from the given disk.
/// @param _backing The string pointer to store the resulting backing identifier in. The caller is responsible for freeing the string.
/// @param disk The disk to use as the source for the backing.
/// @return The result of the operation.
result_t import_backing(char** _backing, const char* disk);
/// @brief Removes the given backing from the pool. If the backing does not exist, the call will return a failure result with an error message.
/// @param backing The backing to remove.
/// @return The result of the operation.
result_t remove_backing(const char* backing);
/// @brief Checks that the given file is a backing. This function is used as a filter for listing backings.
/// @param file The file to check.
/// @return Whether the file is a backing.
bool backing_filter(const char* file);
/// @brief Lists the backings in the pool.
/// @param _backings The string array pointer to store the resulting array in. The caller is responsible for freeing the strings and the array.
/// @return The result of the operation.
result_t list_backings(char*** _backings);
/// @brief Gets the default backing identifier.
/// @param _backing The string pointer to store the resulting backing identifier in. The caller is responsible for freeing the string.
/// @return The result of the operation.
result_t get_default_backing(char** _backing);
result_t get_default_backing(char** _backing, const config_t* config);
/// @brief Sets the default backing identifier.
/// @param backing The backing identifier to set as the default. This parameter can be NULL to remove the default backing.
/// @param config The program configuration to use.
/// @param backing The identifier of the backing to set as default.
/// @return The result of the operation.
result_t set_default_backing(const char* backing);
result_t set_default_backing(const config_t* config, const char* backing);
/// @brief Gets the backing parent of the given backing.
/// @param _parent The string pointer to store the resulting backing parent identifier in. The caller is responsible for freeing the string.
/// @param backing The backing to get the parent of.
/// @brief Adds a new backing to the backing pool from the specified container.
/// @param _backing The pointer to where the backing identifier should be stored. The caller is responsible for freeing the backing.
/// @param config The program configuration to use.
/// @param container The identifier of the container to add the backing from.
/// @return The result of the operation.
result_t get_backing_parent(char** _parent, const char* backing);
result_t add_backing(char** _backing, const config_t* config, const char* container);
/// @brief Gets information about the given backing. If the backing does not exist, the call will return a failure result with an error message.
/// @param _info The string pointer to store the resulting disk information in. The caller is responsible for freeing the string.
/// @param backing The backing to get information about.
/// @brief Removes the specified backing from the backing pool.
/// @param config The program configuration to use.
/// @param backing The identifier of the backing to remove.
/// @return The result of the operation.
result_t get_backing_info(disk_info_t* _info, const char* backing);
result_t remove_backing(const config_t* config, const char* backing);
/// @brief Executes /etc/sandbox.d/sync to synchronize the backing pool.
/// @brief Lists the backings in the backing pool, and returns the result in a null-terminated array of strings.
/// @param _backings The pointer to where the backings array should be stored. The caller is responsible for freeing the backings array as well as the strings in the array.
/// @param config The program configuration to use.
/// @return The result of the operation.
result_t sync_backing_pool(void);
result_t list_backings(char*** _backings, const config_t* config);
/// @brief Executes the syncronization script.
/// @param config The program configuration to use.
/// @return The result of the operation.
result_t sync_backing(const config_t* config);

4
src/cache.h Normal file
View File

@ -0,0 +1,4 @@
#pragma once
#include "utils.h"

31
src/config.c Normal file
View File

@ -0,0 +1,31 @@
#include "config.h"
#include "sandbox.h"
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <json-c/json.h>
result_t init_config(config_t** _config) {
// Create the configuration.
config_t* config = malloc(sizeof(config_t));
if (config == NULL)
return failure("Failed to allocate memory for the configuration.");
// Initialize the configuration.
config->container_pool = strdup("/var/lib/sandbox/containers");
config->backing_pool = strdup("/var/lib/sandbox/backings");
// Output the configuration.
*_config = config;
return success();
}
void free_config(config_t* config) {
free(config->container_pool);
free(config);
}

21
src/config.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include "utils.h"
/// @brief Structure used to store the program configuration.
typedef struct {
/// @brief The path of the directory where the containers are stored.
char* container_pool;
/// @brief The path of the directory where the backings are stored.
char* backing_pool;
} config_t;
/// @brief Initializes the program configuration.
/// @param _config The pointer to where the configuration should be stored.
/// @return The result of the operation.
result_t init_config(config_t** _config);
/// @brief Frees the program configuration.
/// @param config The configuration to free.
void free_config(config_t* config);

465
src/container.c Executable file → Normal file

File diff suppressed because it is too large Load Diff

114
src/container.h Executable file → Normal file
View File

@ -1,91 +1,87 @@
#pragma once
#include "utils.h"
#include "disk.h"
#include "config.h"
#define CONTAINER_IDENTIFIER_MAX_LENGTH 64
/// @brief The maximum length of a container identifier.
#define MAX_CONTAINER_IDENTIFIER_LENGTH 64
/// @brief Checks whether the given string is a valid container identifier. If the string is not a valid container identifier, the call will return a failure result with an error message.
/// @param container The string to check.
/// @brief Checks whether the specified container identifier is valid. This call returns a failure if the identifier is invalid.
/// @param container The container identifier to check.
/// @return The result of the operation.
result_t check_container_identifier(const char* container);
/// @brief Checks whether the given container exists. If the container does not exist, the call will return a failure result with an error message.
/// @param container The container to check.
/// @brief Returns the path of the container pool.
/// @param _path The pointer to where the path should be stored. The caller is responsible for freeing the path.
/// @param config The program configuration to use.
/// @return The result of the operation.
result_t check_container_exists(const char* container);
result_t get_container_pool_path(char** _path, const config_t* config);
/// @brief Gets the container pool path.
/// @param _path The string pointer to store the resulting path in. The caller is responsible for freeing the string.
/// @brief Returns the path of the specified container.
/// @param _path The pointer to where the path should be stored. The caller is responsible for freeing the path.
/// @param config The program configuration to use.
/// @param container The identifier of the container to get the path of.
/// @return The result of the operation.
result_t get_container_pool_path(char** _path);
result_t get_container_path(char** _path, const config_t* config, const char* container);
/// @brief Gets the container path.
/// @param _path The string pointer to store the resulting path in. The caller is responsible for freeing the string.
/// @param container The container to get the path of.
/// @return The result of the operation.
result_t get_container_path(char** _path, const char* container);
/// @brief Checks whether the specified container exists. This call returns a failure if the container does not exist.
/// @param config The program configuration to use.
/// @param container The identifier of the container to check.
/// @return Whether the container exists.
result_t check_container_exists(const config_t* config, const char* container);
/// @brief Adds a root (empty) container to the pool, with the given size. If the container already exists, the call will return a failure result with an error message.
/// @param container The container to add.
/// @brief Adds a new root container to the container pool, with the specified identifier and size.
/// @param config The program configuration to use.
/// @param container The identifier of the container to add.
/// @param size The size of the container to add, in bytes.
/// @return The result of the operation.
result_t add_root_container(const char* container, uint64_t size);
result_t add_root_container(const config_t* config, const char* container, uint64_t size);
/// @brief Adds a backed container to the pool, with the given backing. If the container already exists, or the backing does not, the call will return a failure result with an error message.
/// @param container The container to add.
/// @param backing The backing to use for the container.
/// @brief Adds a new backed container to the container pool, with the specified identifier and backing.
/// @param config The program configuration to use.
/// @param container The identifier of the container to add.
/// @param backing The identifier of the backing to use.
/// @return The result of the operation.
result_t add_backed_container(const char* container, const char* backing);
result_t add_backed_container(const config_t* config, const char* container, const char* backing);
/// @brief Removes the given container from the pool. If the container does not exist, the call will return a failure result with an error message.
/// @param container The container to remove.
/// @brief Removes the specified container from the container pool.
/// @param config The program configuration to use.
/// @param container The identifier of the container to remove.
/// @return The result of the operation.
result_t remove_container(const char* container);
result_t remove_container(const config_t* config, const char* container);
/// @brief Trims (removes unused space) the given container.
/// @param container The container to trim.
/// @brief Resets the specified container to its original state.
/// @param config The program configuration to use.
/// @param container The identifier of the container to reset.
/// @return The result of the operation.
result_t trim_container(const char* container);
result_t reset_container(const config_t* config, const char* container);
/// @brief Resets the given container to its initial state.
/// @param container The container to reset.
/// @brief Trims the specified container.
/// @param config The program configuration to use.
/// @param container The identifier of the container to trim.
/// @return The result of the operation.
result_t reset_container(const char* container);
result_t trim_container(const config_t* config, const char* container);
/// @brief Checks that the given file is a container. This function is used as a filter for listing containers.
/// @param file The file to check.
/// @return Whether the file is a container.
bool container_filter(const char* file);
/// @brief Lists the containers in the pool.
/// @param _containers The string array pointer to store the resulting array in. The caller is responsible for freeing the strings and the array.
/// @brief Lists the containers in the container pool, and returns the result in a null-terminated array of strings.
/// @param _containers The pointer to where the containers array should be stored. The caller is responsible for freeing the containers array as well as the strings in the array.
/// @param config The program configuration to use.
/// @return The result of the operation.
result_t list_containers(char*** _containers);
result_t list_containers(char*** _containers, const config_t* config);
/// @brief Gets the backing of the given container. If the container does not exist, the call will return a failure result with an error message.
/// @param _backing The string pointer to store the resulting backing in.
/// @param container The container to get the backing of.
/// @brief Returns the oldest container in the container pool.
/// @param _container The pointer to where the container should be stored. The caller is responsible for freeing the container.
/// @param config The program configuration to use.
/// @return The result of the operation.
result_t get_container_backing(char** _backing, const char* container);
result_t get_oldest_container(char** _container, const config_t* config);
/// @brief Gets information about the given container. If the container does not exist, the call will return a failure result with an error message.
/// @param _info The string pointer to store the resulting disk information in. The caller is responsible for freeing the string.
/// @param container The container to get information about.
/// @brief Calculates the total available space in the container pool.
/// @param _size The pointer to where the size should be stored.
/// @param config The program configuration to use.
/// @return The result of the operation.
result_t get_container_info(disk_info_t* _info, const char* container);
result_t get_available_space(uint64_t* _size, const config_t* config);
/// @brief Gets the oldest container in the pool.
/// @param _container The string pointer to store the resulting container in. The caller is responsible for freeing the string.
/// @return The result of the operation.
result_t get_oldest_container(char** _container);
/// @brief Gets the available space in the pool.
/// @param _space The string pointer to store the resulting space in, in bytes. The caller is responsible for freeing the string.
/// @return The result of the operation.
result_t get_container_pool_space(uint64_t* _space);
/// @brief Reserves the given space in the pool. If the pool does not have enough space, the oldest containers will be removed to make space.
/// @brief Reserves the specified space in the container pool, by removing the oldest containers.
/// @param config The program configuration to use.
/// @param size The size to reserve, in bytes.
/// @return The result of the operation.
result_t reserve_container_pool_space(uint64_t size);
result_t reserve_space(const config_t* config, uint64_t size);

578
src/disk.c Executable file → Normal file

File diff suppressed because it is too large Load Diff

64
src/disk.h Executable file → Normal file
View File

@ -2,50 +2,52 @@
#include "utils.h"
/// @brief The disk information structure.
/// @brief Structure used to store information about a qcow2 disk.
typedef struct {
/// @brief The virtual size of the disk, in bytes.
uint64_t size;
uint64_t virtual_size;
/// @brief The actual size of the disk, in bytes.
uint64_t allocated;
/// @brief The path to the backing file of the disk, or NULL if the disk is not backed.
uint64_t actual_size;
/// @brief The path of the backing disk of this disk. If this disk is not a backing disk, this field is NULL.
char* backing_path;
/// @brief The name of the backing disk of this disk. If this disk is not a backing disk, this field is NULL.
char* backing_name;
} disk_info_t;
/// @brief Creates a root disk (empty disk) at the given path with the given size.
/// @param path The path to create the disk at. Any existing file at this path will be overwritten.
/// @brief Reads the information of the specified qcow2 disk.
/// @param _info The pointer to where the disk information should be stored. The caller is responsible for freeing the disk information.
/// @param disk The disk to read the information of.
/// @return The result of the operation.
result_t read_disk_info(disk_info_t** _info, const char* disk);
/// @brief Frees the specified disk information.
/// @param info The disk information to free.
void free_disk_info(disk_info_t* info);
/// @brief Creates a new root (empty) qcow2 disk with the specified size.
/// @param disk The path of the disk to create. Any existing file at this path will be overwritten.
/// @param size The size of the disk to create, in bytes.
/// @return The result of the operation.
result_t create_root_disk(const char* path, uint64_t size);
result_t create_root_disk(const char* disk, uint64_t size);
/// @brief Creates a backed disk at the given path with the given backing path.
/// @param path The path to create the disk at. Any existing file at this path will be overwritten.
/// @param backing_path The path to the backing file to use for the disk.
/// @brief Creates a new backed qcow2 disk with the specified backing disk.
/// @param disk The path of the disk to create. Any existing file at this path will be overwritten.
/// @param backing The backing disk to use.
/// @return The result of the operation.
result_t create_backed_disk(const char* path, const char* backing_path);
result_t create_backed_disk(const char* disk, const char* backing);
/// @brief Trims (removes unused space) the given disk.
/// @param path The path to the disk to trim.
/// @brief Rebacks the specified qcow2 disk to the specified backing disk.
/// @param disk The disk to reback.
/// @param backing The backing disk to use.
/// @return The result of the operation.
result_t trim_disk(const char* path);
result_t reback_disk(const char* disk, const char* backing);
/// @brief Resets the given disk to its initial state.
/// @param path The path to the disk to reset.
/// @brief Resets the specified qcow2 disk to its original state.
/// @param disk The disk to reset.
/// @return The result of the operation.
result_t reset_disk(const char* path);
result_t reset_disk(const char* disk);
/// @brief Changes the backing of the given disk to the given backing path. This call does not change the contents of the disk, but only the backing file path.
/// @param path The path to the disk to reback.
/// @param backing_path The path to the new backing file to use for the disk.
/// @brief Trims (removes unused sparse space) the specified qcow2 disk.
/// @param disk The disk to trim.
/// @return The result of the operation.
result_t reback_disk(const char* path, const char* backing_path);
/// @brief Gets the disk information of the disk at the given path.
/// @param _info The disk information pointer to store the resulting disk information in. The caller is responsible for freeing the disk information.
/// @param path The path to the disk to get the information of.
/// @return The result of the operation.
result_t get_disk_info(disk_info_t* _info, const char* path);
/// @brief Frees the given disk information.
/// @param _info The disk information to free.
void free_disk_info(disk_info_t* _info);
result_t trim_disk(const char* disk);

View File

@ -1,38 +0,0 @@
#include "domain.h"
#include "sandbox.h"
#include <string.h>
result_t container_to_domain(char** _domain, const char* container) {
// Initialize the output parameter
*_domain = NULL;
// Convert the container identifier to a domain identifier
result_t result = format(_domain, "%s%s", DOMAIN_IDENTIFIER_PREFIX, container);
if (result != success())
return result;
return success();
}
result_t domain_to_container(char** _container, const char* domain) {
// Initialize the output parameter
*_container = NULL;
// Check that the domain identifier is not too short
size_t prefix_length = strlen(DOMAIN_IDENTIFIER_PREFIX);
if (strlen(domain) <= prefix_length)
return failure("Invalid domain identifier. The domain identifier is too short. A domain identifier must be prefixed with '%s'.", DOMAIN_IDENTIFIER_PREFIX);
// Check that the domain identifier has the correct prefix
if (strncmp(domain, DOMAIN_IDENTIFIER_PREFIX, prefix_length) != 0)
return failure("Invalid domain identifier. The domain identifier must be prefixed with '%s'.", DOMAIN_IDENTIFIER_PREFIX);
// Convert the domain identifier to a container identifier
result_t result = format(_container, "%s", domain + prefix_length);
if (result != success())
return result;
return success();
}

View File

@ -1,15 +0,0 @@
#pragma once
#include "utils.h"
/// @brief Converts a container identifier to a domain identifier.
/// @param _domain The string pointer to store the resulting domain identifier in. The caller is responsible for freeing the string.
/// @param container The container identifier to convert.
/// @return The result of the operation.
result_t container_to_domain(char** _domain, const char* container);
/// @brief Converts a domain identifier to a container identifier.
/// @param _container The string pointer to store the resulting container identifier in. The caller is responsible for freeing the string.
/// @param domain The domain identifier to convert.
/// @return The result of the operation.
result_t domain_to_container(char** _container, const char* domain);

239
src/pci.c
View File

@ -1,239 +0,0 @@
#include "pci.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <libgen.h>
#include <sys/stat.h>
result_t check_pci_address(const char* pci_address) {
// Check the length of the string (should be 12 characters, eg. 0000:00:00.0)
if (strlen(pci_address) != 12)
return failure("Invalid PCI address '%s'. Valid PCI addresses are in the format '0000:00:00.0'.", pci_address);
// Check the format of the string
for (int i = 0; i < 12; i++)
if (i == 4 || i == 7) {
if (pci_address[i] != ':')
return failure("Invalid PCI address '%s'. Missing colon at position %d. Valid PCI addresses are in the format '0000:00:00.0'.", pci_address, i);
} else if (i == 10) {
if (pci_address[i] != '.')
return failure("Invalid PCI address '%s'. Missing dot at position %d. Valid PCI addresses are in the format '0000:00:00.0'.", pci_address, i);
} else {
if (!isxdigit(pci_address[i]))
return failure("Invalid PCI address '%s'. Non-hexadecimal character '%c' at position %d. Valid PCI addresses are in the format '0000:00:00.0'.", pci_address, pci_address[i], i);
}
return success();
}
result_t check_pci_exists(const char* pci_address) {
// Check the PCI address format
result_t result = check_pci_address(pci_address);
if (result != success())
return result;
// Check if the PCI address exists by checking if the sysfs directory exists
char* path;
result = format(&path, "/sys/bus/pci/devices/%s", pci_address);
if (result != success())
return result;
// Check that the directory exists
struct stat st;
if (stat(path, &st) != 0 || !S_ISDIR(st.st_mode)) {
free(path);
return failure("PCI address '%s' does not exist.", pci_address);
}
// Free the path
free(path);
return success();
}
result_t get_iommu_group(int* _group, const char* pci_address) {
// Initialize the output parameter
*_group = -1;
// Check that the PCI address exists
result_t result = check_pci_exists(pci_address);
if (result != success())
return result;
// Get the IOMMU group if the PCI device has one
char* path;
result = format(&path, "/sys/bus/pci/devices/%s/iommu_group", pci_address);
if (result != success())
return result;
// Check that the file exists and is a symlink
struct stat st;
if (lstat(path, &st) != 0 || !S_ISLNK(st.st_mode)) {
free(path);
return failure("PCI address '%s' does not have an IOMMU group. Please ensure that the IOMMU is enabled in the kernel.", pci_address);
}
// Read the IOMMU group by getting the path of the symlink, and getting the basename of the path
char iommu_group_path[256]; // 256 should be enough for the path
ssize_t len = readlink(path, iommu_group_path, sizeof(iommu_group_path) - 1);
free(path);
// Check for errors during the readlink call
if (len == -1)
return failure("Failed to read IOMMU group of PCI address '%s'.", pci_address);
// Null-terminate the path
iommu_group_path[len] = '\0';
// Get the basename of the path, and try to parse it as an integer
*_group = atoi(basename(iommu_group_path));
return success();
}
result_t get_iommu_groups(int** _groups, int* _count, const char** pci_addresses) {
// Initialize the output parameters
*_groups = NULL;
*_count = 0;
// Count the number of PCI devices
int count = 0;
while (pci_addresses[count] != NULL)
count++;
// Allocate memory for the groups
int* groups = malloc(count * sizeof(int));
if (groups == NULL)
return failure("Failed to allocate memory for IOMMU groups.");
// Get the IOMMU groups of the PCI devices
for (int i = 0; i < count; i++) {
// Get the IOMMU group of the PCI device
result_t result = get_iommu_group(&groups[i], pci_addresses[i]);
if (result != success()) {
free(groups);
return result;
}
}
// Remove duplicates from the groups
for (int i = 0; i < count; i++) {
for (int j = i + 1; j < count; j++) {
if (groups[i] == groups[j]) {
// Shift the elements to the left and decrement the count
for (int k = j; k < count - 1; k++)
groups[k] = groups[k + 1];
count--;
j--;
}
}
}
*_groups = groups;
*_count = count;
return success();
}
bool pci_filter(const char* file) {
// Check that the PCI device exists
return check_pci_exists(file) == success();
}
result_t get_iommu_group_devices(char*** _devices, int group) {
// Initialize the output parameters
*_devices = NULL;
// Get the path of the IOMMU group
char* path;
result_t result = format(&path, "/sys/kernel/iommu_groups/%d/devices", group);
if (result != success())
return result;
// List the devices in the IOMMU group
result = list_files(_devices, path, pci_filter);
// Free the path
free(path);
return result;
}
result_t get_iommu_groups_devices(char*** _devices, const int* groups, int groups_count) {
// Initialize the output parameters
*_devices = NULL;
char*** devices = malloc(groups_count * sizeof(char**));
if (devices == NULL)
return failure("Failed to allocate memory for IOMMU group devices.");
// Get the devices of each IOMMU group
for (int i = 0; i < groups_count; i++) {
result_t result = get_iommu_group_devices(&devices[i], groups[i]);
if (result != success()) {
// Free every individual device
for (int j = 0; j < i; j++) {
char** group_devices = devices[j];
for (int k = 0; group_devices[k] != NULL; k++)
free(group_devices[k]);
free(devices[j]);
}
// Free the devices array
free(devices);
return result;
}
}
// Count the number of devices
int count = 0;
for (int i = 0; i < groups_count; i++) {
char** group_devices = devices[i];
for (int j = 0; group_devices[j] != NULL; j++)
count++;
}
// Allocate memory for the output
char** output_devices = malloc((count + 1) * sizeof(char*));
if (output_devices == NULL) {
// Free every individual device
for (int i = 0; i < groups_count; i++) {
char** group_devices = devices[i];
for (int j = 0; group_devices[j] != NULL; j++)
free(group_devices[j]);
free(devices[i]);
}
// Free the devices array
free(devices);
return failure("Failed to allocate memory for IOMMU group devices.");
}
// Move the devices to the output
int index = 0;
for (int i = 0; i < groups_count; i++) {
char** group_devices = devices[i];
for (int j = 0; group_devices[j] != NULL; j++) {
output_devices[index] = group_devices[j];
group_devices[j] = NULL;
index++;
}
// Free the group devices
free(group_devices);
}
// Null-terminate the output
output_devices[count] = NULL;
// Free the devices array
free(devices);
*_devices = output_devices;
return success();
}

View File

@ -1,44 +0,0 @@
#pragma once
#include "utils.h"
/// @brief Checks whether the given string is a valid PCI address. If the string is not a valid PCI address, the call will return a failure result with an error message.
/// @param pci_address The string to check.
/// @return The result of the operation.
result_t check_pci_address(const char* pci_address);
/// @brief Checks whether the given PCI address exists. If the PCI address does not exist, the call will return a failure result with an error message.
/// @param pci_address The PCI address to check.
/// @return The result of the operation.
result_t check_pci_exists(const char* pci_address);
/// @brief Gets the IOMMU group of the given PCI address.
/// @param _group The integer pointer to store the resulting IOMMU group in.
/// @param pci_address The PCI address to get the IOMMU group of.
/// @return The result of the operation.
result_t get_iommu_group(int* _group, const char* pci_address);
/// @brief Gets the IOMMU groups of the given PCI addresses.
/// @param _groups The integer array pointer to store the resulting IOMMU groups in. The caller is responsible for freeing the array.
/// @param _count The integer pointer to store the number of IOMMU groups in.
/// @param pci_addresses The PCI addresses to get the IOMMU groups of.
/// @return The result of the operation.
result_t get_iommu_groups(int** _groups, int* _count, const char** pci_addresses);
/// @brief Checks that the given file is a PCI device. This function is used as a filter for listing PCI devices.
/// @param file The file to check.
/// @return Whether the file is a PCI device.
bool pci_filter(const char* file);
/// @brief Get the PCI devices in the given IOMMU group.
/// @param _devices The string array pointer to store the resulting devices in. The caller is responsible for freeing the strings and the array.
/// @param group The IOMMU group to get the devices of.
/// @return The result of the operation.
result_t get_iommu_group_devices(char*** _devices, int group);
/// @brief Get the PCI devices in the given IOMMU groups.
/// @param _devices The string array pointer to store the resulting devices in. The caller is responsible for freeing the strings and the array.
/// @param groups The IOMMU groups to get the devices of.
/// @param groups_count The number of IOMMU groups.
/// @return The result of the operation.
result_t get_iommu_groups_devices(char*** _devices, const int* groups, int groups_count);

121
src/sandbox.c Executable file → Normal file
View File

@ -1,115 +1,22 @@
#include "sandbox.h"
#include "utils.h"
#include "backing.h"
#include "disk.h"
#include "config.h"
#include "container.h"
#include "backing.h"
#include <stdio.h>
#include <stdbool.h>
#include <pwd.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define ALIAS(...) \
(const char*[]) { \
__VA_ARGS__, NULL \
int main(int argc, char* argv[]) {
config_t* config;
result_t result = init_config(&config);
if (result != success()) {
fprintf(stderr, "Failed to initialize the configuration: %s\n", last_error());
return 1;
}
#define ALIASES(...) \
(const char**[]) { \
__VA_ARGS__, NULL \
}
#define ARGUMENTS(...) \
(const Argument[]) { \
__VA_ARGS__, {} \
}
#define OPTIONS(...) \
(const Option[]) { \
__VA_ARGS__, {} \
}
#define OPTION_ALIASES(...) \
(const char*[]) { \
__VA_ARGS__, NULL \
}
const Command COMMANDS[] = {
{
.handler = command_help,
.description = "Display help information",
.details = "Display help information for a specific command",
.aliases = ALIASES(ALIAS("h"), ALIAS("help", "me")),
.arguments = ARGUMENTS({
.name = "command",
.description = "The command to display help information for",
.optional = true,
}),
.options = OPTIONS({
.aliases = OPTION_ALIASES("verbose", "v"),
.description = "Display verbose help information",
.arguments = ARGUMENTS({
.name = "level",
.description = "The level of verbosity to display",
}),
}),
},
{}};
int main(int argc, char** argv) {
if (argc == 1) {
// Handle no command
fprintf(stderr, "No command specified\n");
return EXIT_FAILURE;
}
// For each command
for (int i = 0; COMMANDS[i].handler != NULL; i++) {
const Command* command = &COMMANDS[i];
// For each alias
bool command_matched = false;
int k = 0;
for (int j = 0; command->aliases[j] != NULL; j++) {
const char** alias = command->aliases[j];
// For each string in the alias
bool alias_matched = true;
for (k = 0; alias[k] != NULL; k++)
if (argc <= 1 + k || strcmp(argv[1 + k], alias[k]) != 0) {
alias_matched = false;
break;
}
// Check if the alias matched
if (alias_matched) {
command_matched = true;
break;
}
}
// If the command matched
if (command_matched) {
// Call the command handler
return command->handler(argc - 1 - k, argv + 1 + k);
}
}
fprintf(stderr, "Unknown command: ");
for (int i = 1; i < argc; i++)
fprintf(stderr, "%s ", argv[i]);
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
int command_help(int argc, char* argv[]) {
// Print argv
printf("argc: %d\n", argc);
for (int i = 0; i < argc; i++)
printf("%s\n", argv[i]);
return EXIT_SUCCESS;
free_config(config);
}

40
src/sandbox.h Executable file → Normal file
View File

@ -1,40 +1,4 @@
#pragma once
#include <stdbool.h>
#define SANDBOX_VERSION "0.1.4"
#define SANDBOX_USER "sandbox"
#define DOMAIN_IDENTIFIER_PREFIX "sandbox-"
#define LIBVIRT_DRIVER "qemu:///system"
#define CONFIG_FILE "/etc/sandbox.d/sandbox.conf"
#define SYNC_FILE "/etc/sandbox.d/sync"
typedef struct {
const char* name;
const char* description;
const bool optional;
} Argument;
typedef struct {
const char** aliases;
const char* description;
const Argument* arguments;
} Option;
typedef struct {
int (*handler)(int argc, char* argv[]);
const char* description;
const char* details;
const char*** aliases;
const Argument* arguments;
const Option* options;
} Command;
extern const Command COMMANDS[];
int main(int argc, char** argv);
int command_help(int argc, char* argv[]);
#define CONFIGURATION_FILE "/etc/sandbox.d/config.json"
#define SYNCRONIZATION_FILE "/etc/sandbox.d/sync"

887
src/utils.c Executable file → Normal file

File diff suppressed because it is too large Load Diff

194
src/utils.h Executable file → Normal file
View File

@ -1,135 +1,117 @@
#pragma once
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <inttypes.h>
#include <sys/types.h>
#define ERROR_BUFFER_SIZE 4096
/// @brief The global error buffer. It can be accessed using the error() function.
extern char _ERROR_BUFFER[];
/// @brief The level of importance of a log message.
typedef enum {
/// @brief A debug message. These messages are used for debugging purposes, and are not meant to be seen by the user.
LOG_LEVEL_DEBUG,
/// @brief An info message. These messages are used to inform the user about the progress of the program.
LOG_LEVEL_INFO,
/// @brief A warning message. These messages are used to warn the user about potential issues.
LOG_LEVEL_WARNING,
/// @brief An error message. These messages are used to inform the user that a critical error has occurred.
LOG_LEVEL_ERROR,
} log_level_t;
/// @brief The minimum log level of the program.
extern log_level_t _LOG_LEVEL;
/// @brief The result of an operation.
/// @brief Type representing a result of an operation, 0 for success, non-zero for failure.
typedef int result_t;
/// @brief Sets the log level of the program.
/// @param level The log level to set.
void set_log_level(log_level_t level);
/// @brief The size of the error buffer.
#define ERROR_BUFFER_SIZE 4096
/// @brief Logs a message with the given level, and the given format string and arguments.
/// @param level The level of the message.
/// @param format The format string to use for the message.
/// @param ... The arguments to use for the format string.
void log_message(log_level_t level, const char* format, ...);
/// @brief The error buffer, containing the last error message.
extern char ERROR_BUFFER[];
/// @brief Returns a generic success result.
/// @return The success result.
result_t success(void);
/// @brief Returns a result_t representing a successful operation.
/// @return The successful result_t.
result_t success();
/// @brief Returns a generic failure result, with an error message formatted using the given format string and arguments. The error message is stored in a global buffer, and can be retrieved using the error() function.
/// @param format The format string to use for the error message.
/// @param ... The arguments to use for the format string.
/// @return The failure result.
/// @brief Returns a result_t representing a failed operation, and writes the formatted error message to the error buffer, which can be accessed using last_error().
/// @param format The format string for the error message.
/// @param ... The arguments for the format string.
/// @return The failed result_t.
result_t failure(const char* format, ...);
/// @brief Returns the error message from the last failure result.
/// @return The error message from the last failure result.
const char* error(void);
/// @brief Returns the last error message.
/// @return The last error message.
const char* last_error();
/// @brief Formats a string using the given format string and arguments, and stores the result in the given string pointer.
/// @param _str The string pointer to store the resulting string in. The caller is responsible for freeing the string.
/// @param format The format string to use for the string.
/// @param ... The arguments to use for the format string.
/// @brief Executes the specified file using the given arguments. The standard output and error streams are captured into buffers, as well as the exit code.
/// @param _exit_code The pointer to where the exit code should be stored. This output parameter is optional, and can be NULL.
/// @param _stdout_buffer The pointer to where the standard output buffer should be stored. This output parameter is optional, and can be NULL. The caller is responsible for freeing the buffer.
/// @param _stdout_size The pointer to where the size of the standard output buffer should be stored. This output parameter is optional, and can be NULL.
/// @param _stderr_buffer The pointer to where the standard error buffer should be stored. This output parameter is optional, and can be NULL. The caller is responsible for freeing the buffer.
/// @param _stderr_size The pointer to where the size of the standard error buffer should be stored. This output parameter is optional, and can be NULL.
/// @param working_directory The working directory for the process, or NULL to use the current working directory.
/// @param file The file to execute.
/// @param ... The null-terminated list of arguments for the file.
/// @return The result of the operation.
result_t format(char** _str, const char* format, ...);
result_t execute_file(int* _exit_code, char** _stdout_buffer, size_t* _stdout_size, char** _stderr_buffer, size_t* _stderr_size, const char* working_directory, const char* file, ...);
/// @brief Extracts a substring from the given string, starting at the given index and with the given length, and stores the resulting string in the given string pointer.
/// @param _str The string pointer to store the resulting string in. The caller is responsible for freeing the string.
/// @param str The string to extract the substring from.
/// @param start The index to start the substring at.
/// @param length The length of the substring. If the length goes beyond the end of the string, the substring will be truncated to the end of the string.
/// @brief Reads the specified file descriptor into a buffer.
/// @param _buffer The pointer to where the buffer should be stored. The caller is responsible for freeing the buffer.
/// @param _size The pointer to where the size of the buffer should be stored. This output parameter is optional, and can be NULL.
/// @param fd The file descriptor to read.
/// @return The result of the operation.
result_t substring(char** _str, const char* str, size_t start, size_t length);
result_t read_fd(char** _buffer, size_t* _size, int fd);
/// @brief Formats a size in bytes to a human-readable string, and stores the resulting string in the given string pointer.
/// @param _str The string pointer to store the resulting string in. The caller is responsible for freeing the string.
/// @param size The size in bytes to format.
/// @brief Writes the specified buffer to the specified file descriptor.
/// @param fd The file descriptor to write to.
/// @param buffer The buffer to write.
/// @param size The size of the buffer.
/// @return The result of the operation.
result_t format_size(char** _str, uint64_t size);
result_t write_fd(int fd, const char* buffer, size_t size);
/// @brief Parses a size from the given string, and stores the resulting integer in the given size pointer.
/// @param _size The size pointer to store the resulting integer in.
/// @param str The string to parse the size from.
/// @brief Reads the specified file into a buffer.
/// @param _buffer The pointer to where the buffer should be stored. The caller is responsible for freeing the buffer.
/// @param _size The pointer to where the size of the buffer should be stored. This output parameter is optional, and can be NULL.
/// @param file The file to read.
/// @return The result of the operation.
result_t parse_size(uint64_t* _size, const char* str);
result_t read_file(char** _buffer, size_t* _size, const char* file);
/// @brief Executes the given command, and stores the resulting exit code in the given exit code pointer, and the standard output and standard error in the given string pointers.
/// @param _exit_code The exit code pointer to store the resulting exit code in.
/// @param _stdoutbuf The standard output string pointer to store the resulting string in. The caller is responsible for freeing the string. This parameter can be NULL if the standard output is not needed.
/// @param _stderrbuf The standard error string pointer to store the resulting string in. The caller is responsible for freeing the string. This parameter can be NULL if the standard error is not needed.
/// @param working_directory The working directory to execute the command in. If this parameter is NULL, the command will be executed in the current working directory.
/// @param executable The command to execute.
/// @param ... The arguments to pass to the command. The last argument must be NULL. No need to pass the executable as the first argument.
/// @brief Writes the specified buffer to the specified file.
/// @param file The file to write to.
/// @param buffer The buffer to write.
/// @param size The size of the buffer.
/// @param mode The permissions to set on the file.
/// @return The result of the operation.
result_t execute(int* _exit_code, char** _stdoutbuf, char** _stderrbuf, const char* working_directory, const char* executable, ...);
result_t write_file(const char* file, const char* buffer, size_t size, mode_t mode);
// @brief Reads the given file descriptor, and stores the resulting string in the given string pointer. The string can contain null characters.
// @param _str The string pointer to store the resulting string in. The caller is responsible for freeing the string.
// @param fd The file descriptor to read from.
// @return The result of the operation.
result_t read_fd(char** _str, int fd);
// @brief Writes the given string to the given file descriptor.
// @param fd The file descriptor to write to.
// @param str The string to write.
// @return The result of the operation.
result_t write_fd(int fd, const char* str);
/// @brief Reads the given file, and stores the resulting string in the given string pointer. The string can contain null characters.
/// @param _str The string pointer to store the resulting string in. The caller is responsible for freeing the string.
/// @param path The path to the file to read.
/// @brief Copies the contents of the specified source file descriptor to the specified destination file descriptor.
/// @param source_fd The source file descriptor to copy.
/// @param destination_fd The destination file descriptor to copy to.
/// @return The result of the operation.
result_t read_file(char** _str, const char* path);
result_t copy_fd(int source_fd, int destination_fd);
/// @brief Writes the given string to the given file.
/// @param path The path to the file to write to.
/// @param str The string to write.
/// @brief Copies the contents of the specified source file to the specified destination file.
/// @param source The source file to copy.
/// @param destination The destination to copy the file to.
/// @param mode The permissions to set on the destination file.
/// @return The result of the operation.
result_t write_file(const char* path, const char* str);
result_t copy_file(const char* source, const char* destination, mode_t mode);
/// @brief Copies the given file to the given destination.
/// @param src The path to the file to copy.
/// @param dst The path to the destination to copy the file to.
/// @return The result of the operation.
result_t copy_file(const char* src, const char* dst);
/// @brief Trims whitespace from the beginning and end of the specified string.
/// @param string The string to trim.
/// @return The trimmed string. The caller is responsible for freeing the string.
char* trim(const char* string);
/// @brief Lists the files in the given directory, and stores the resulting array in the given string array pointer. A filter can be provided to only include files that match the filter.
/// @param _files The string array pointer to store the resulting array in. The caller is responsible for freeing the strings and the array.
/// @param path The path to the directory to list the files in.
/// @param filter The filter to use to only include files that match the filter. This parameter can be NULL if no filter is needed.
/// @return The result of the operation.
result_t list_files(char*** _files, const char* path, bool (*filter)(const char*));
/// @brief Extracts the file name from the specified path.
/// @param path The path to extract the file name from.
/// @return The file name. The caller is responsible for freeing the string.
char* file_name(const char* path);
/// @brief Calculates the MD5 checksum of the given file, and stores the resulting hash in the given string pointer.
/// @param _md5 The string pointer to store the resulting hash in. The caller is responsible for freeing the string.
/// @param path The path to the file to calculate the MD5 checksum of.
/// @brief Extracts the directory name from the specified path.
/// @param path The path to extract the directory name from.
/// @return The directory name. The caller is responsible for freeing the string.
char* directory_name(const char* path);
/// @brief Lists the files in the specified directory, and returns the result in a null-terminated array of strings. A filter function can be provided to filter the files.
/// @param _files The pointer to where the files should be stored. The caller is responsible for freeing the files.
/// @param directory The directory to list the files of.
/// @param filter The filter function to use, or NULL to list all files. The filter function should return a success result if the file should be included in the list.
/// @return The result of the operation.
result_t md5sum(char** _md5, const char* path);
result_t list_files(char*** _files, const char* directory, result_t (*filter)(const char*));
/// @brief Reads the specified file descriptor, and calculates the MD5 hash of the contents.
/// @param _hash The pointer to where the hash should be stored. The caller is responsible for freeing the hash.
/// @param fd The file descriptor to read.
/// @return The result of the operation.
result_t md5_fd(char** _hash, int fd);
/// @brief Reads the specified file, and calculates the MD5 hash of the contents.
/// @param _hash The pointer to where the hash should be stored. The caller is responsible for freeing the hash.
/// @param file The file to read.
/// @return The result of the operation.
result_t md5_file(char** _hash, const char* file);

275
src/xml.c
View File

@ -1,275 +0,0 @@
#include "xml.h"
#include "pci.h"
#include "container.h"
#include "domain.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
result_t generate_domain_xml(char** _xml, const char* container, uint64_t memory, int vcpus, const char** pci_addresses, const char** iso_paths, int vnc_port, const char* vnc_password) {
// Initialize the output parameters
*_xml = NULL;
// Generate the XML configuration for the PCI devices
char* pci_xml;
result_t result = generate_pci_xml(&pci_xml, pci_addresses);
if (result != success())
return result;
// Generate the XML configuration for the ISO images
char* iso_xml;
result = generate_iso_xml(&iso_xml, iso_paths);
if (result != success()) {
free(pci_xml);
return result;
}
// Generate the XML configuration for the VNC server
char* vnc_xml;
result = generate_vnc_xml(&vnc_xml, vnc_port, vnc_password);
if (result != success()) {
free(pci_xml);
free(iso_xml);
return result;
}
// Get the container path
char* container_path;
result = get_container_path(&container_path, container);
if (result != success()) {
free(pci_xml);
free(iso_xml);
free(vnc_xml);
return result;
}
// Get the domain identifier
char* domain;
result = container_to_domain(&domain, container);
if (result != success()) {
free(pci_xml);
free(iso_xml);
free(vnc_xml);
free(container_path);
return result;
}
// Generate the XML configuration for the domain
char* xml;
result = format(&xml, "<domain type='kvm'>\n"
"<name>%s</name>\n"
"\n<!-- Resources -->\n"
"<memory unit='B'>%lu</memory>\n"
"<vcpu>%d</vcpu>\n"
"<cpu mode='host-passthrough'/>\n"
"\n<!-- OS -->\n"
"<os>\n"
"<type arch='x86_64' machine='q35'>hvm</type>\n"
"<bootmenu enable='no'/>\n"
"</os>\n"
"\n<!-- Features -->\n"
"<features>\n"
"<acpi/>\n"
"<apic/>\n"
"</features>\n"
"\n<!-- Clock -->\n"
"<clock offset='utc'>\n"
"<timer name='rtc' tickpolicy='catchup'/>\n"
"<timer name='pit' tickpolicy='delay'/>\n"
"<timer name='hpet' present='no'/>\n"
"</clock>\n"
"\n<!-- Behavior -->\n"
"<on_poweroff>destroy</on_poweroff>\n"
"<on_reboot>destroy</on_reboot>\n"
"<on_crash>destroy</on_crash>\n"
"<pm>\n"
"<suspend-to-mem enabled='yes'/>\n"
"<suspend-to-disk enabled='yes'/>\n"
"</pm>\n"
"\n<!-- Devices -->\n"
"<devices>\n"
"<emulator>/usr/bin/qemu-system-x86_64</emulator>\n"
"\n<!-- Disks -->\n"
"<disk type='file' device='disk'>\n"
"<driver name='qemu' type='qcow2'/>\n"
"<source file='%s'/>\n"
"<target dev='hda' bus='sata'/>\n"
"<boot order='1'/>\n"
"</disk>\n"
"\n<!-- PCI devices -->\n"
"%s"
"\n<!-- ISO images -->\n"
"%s"
"\n<!-- VNC server -->\n"
"%s"
"\n<!-- Misc -->\n"
"<watchdog model='itco' action='poweroff'/>\n"
"<memballoon model='none'/>\n"
"</devices>\n"
"</domain>\n",
domain, memory, vcpus, container_path, pci_xml, iso_xml, vnc_xml);
free(pci_xml);
free(iso_xml);
free(vnc_xml);
free(container_path);
free(domain);
if (result != success())
return result;
// Return the result
*_xml = xml;
return success();
}
result_t generate_pci_xml(char** _xml, const char** pci_addresses) {
// Initialize the output parameters
*_xml = NULL;
// Get the IOMMU group for each PCI address
int* iommu_groups;
int iommu_groups_count;
result_t result = get_iommu_groups(&iommu_groups, &iommu_groups_count, pci_addresses);
if (result != success())
return result;
// Get the PCI devices for each IOMMU group (the devices that should be passed through)
char** pci_devices;
result = get_iommu_groups_devices(&pci_devices, iommu_groups, iommu_groups_count);
// Free the IOMMU groups as they are no longer needed
free(iommu_groups);
if (result != success())
return result;
// Generate the XML configuration for the PCI devices
errno = 0;
char* xml = strdup("");
if (xml == NULL) {
for (int i = 0; pci_devices[i] != NULL; i++)
free(pci_devices[i]);
free(pci_devices);
return failure("Failed to allocate memory for the initial XML configuration (%s).", strerror(errno));
}
// For each PCI device, generate the XML configuration, and append it to the result
for (int i = 0; pci_devices[i] != NULL; i++) {
// Split the PCI address into domain, bus, device, and function
int domain, bus, device, function;
if (sscanf(pci_devices[i], "%04x:%02x:%02x.%x", &domain, &bus, &device, &function) != 4) {
free(xml);
for (int j = 0; pci_devices[j] != NULL; j++)
free(pci_devices[j]);
free(pci_devices);
return failure("Failed to parse PCI address.");
}
// Generate the XML configuration for the PCI device
char* device_xml;
result = format(&device_xml, "<hostdev mode='subsystem' type='pci' managed='yes'>\n"
"<source>\n"
"<address domain='0x%04x' bus='0x%02x' slot='0x%02x' function='0x%x'/>\n"
"</source>\n"
"</hostdev>\n",
domain, bus, device, function);
if (result != success()) {
free(xml);
for (int j = 0; pci_devices[j] != NULL; j++)
free(pci_devices[j]);
free(pci_devices);
return result;
}
// Append the new XML configuration to the result
char* new_xml;
result = format(&new_xml, "%s%s", xml, device_xml);
free(device_xml);
if (result != success()) {
free(xml);
for (int j = 0; pci_devices[j] != NULL; j++)
free(pci_devices[j]);
free(pci_devices);
return result;
}
// Swap the old and new XML configurations
free(xml);
xml = new_xml;
}
// Free the PCI devices as they are no longer needed
for (int i = 0; pci_devices[i] != NULL; i++)
free(pci_devices[i]);
free(pci_devices);
// Return the result
*_xml = xml;
return success();
}
result_t generate_iso_xml(char** _xml, const char** iso_paths) {
// Initialize the output parameters
*_xml = NULL;
// Generate the XML configuration for the ISO images
errno = 0;
char* xml = strdup("");
if (xml == NULL)
return failure("Failed to allocate memory for the initial XML configuration (%s).", strerror(errno));
// For each ISO image, generate the XML configuration, and append it to the result
for (int i = 0; iso_paths[i] != NULL; i++) {
if (i > 25) {
free(xml);
return failure("Too many ISO images.");
}
char* iso_xml;
result_t result = format(&iso_xml, "<disk type='file' device='cdrom'>\n"
"<driver name='qemu' type='raw'/>\n"
"<source file='%s'/>\n"
"<target dev='hd%c' bus='sata'/>\n"
"<readonly/>\n"
"<boot order='%d'/>\n"
"</disk>\n",
iso_paths[i], 'b' + i, 2 + i); // hda is reserved for the main disk
if (result != success()) {
free(xml);
return result;
}
// Append the new XML configuration to the result
char* new_xml;
result = format(&new_xml, "%s%s", xml, iso_xml);
free(iso_xml);
if (result != success()) {
free(xml);
return result;
}
// Swap the old and new XML configurations
free(xml);
xml = new_xml;
}
// Return the result
*_xml = xml;
return success();
}
result_t generate_vnc_xml(char** _xml, int vnc_port, const char* vnc_password) {
// Initialize the output parameters
*_xml = NULL;
// Generate the XML configuration for the VNC server (if enabled)
if (vnc_port != 0)
return format(_xml, "<graphics type='vnc' port='%d' autoport='no' listen='0.0.0.0' passwd='%s'/>", vnc_port, vnc_password);
return format(_xml, "");
}

View File

@ -1,34 +0,0 @@
#pragma once
#include "utils.h"
/// @brief Generates the XML configuration for a sandbox domain, using the given parameters.
/// @param _xml The string pointer to store the resulting XML configuration in. The caller is responsible for freeing the string.
/// @param container The container to generate the XML configuration for.
/// @param memory The memory size of the domain, in bytes.
/// @param vcpus The number of virtual CPUs of the domain.
/// @param pci_addresses The PCI addresses of the devices to pass through to the domain.
/// @param iso_paths The paths to the ISO images to pass through to the domain.
/// @param vnc_port The VNC port to use for the domain, or 0 to disable VNC.
/// @param vnc_password The VNC password to use for the domain. If the VNC server is disabled, this parameter is ignored.
/// @return The result of the operation.
result_t generate_domain_xml(char** _xml, const char* container, uint64_t memory, int vcpus, const char** pci_addresses, const char** iso_paths, int vnc_port, const char* vnc_password);
/// @brief Generates the XML configuration for passing through the given PCI devices.
/// @param _xml The string pointer to store the resulting XML configuration in. The caller is responsible for freeing the string.
/// @param pci_addresses The PCI addresses of the devices to pass through.
/// @return The result of the operation.
result_t generate_pci_xml(char** _xml, const char** pci_addresses);
/// @brief Generates the XML configuration for attaching the given ISO images.
/// @param _xml The string pointer to store the resulting XML configuration in. The caller is responsible for freeing the string.
/// @param iso_paths The paths to the ISO images to attach.
/// @return The result of the operation.
result_t generate_iso_xml(char** _xml, const char** iso_paths);
/// @brief Generates the XML configuration for a VNC server.
/// @param _xml The string pointer to store the resulting XML configuration in. The caller is responsible for freeing the string.
/// @param vnc_port The VNC port to use. If 0, the VNC server will be disabled.
/// @param vnc_password The VNC password to use. If the VNC server is disabled, this parameter is ignored.
/// @return The result of the operation.
result_t generate_vnc_xml(char** _xml, int vnc_port, const char* vnc_password);