Reimplemented most functions
This commit is contained in:
parent
1cd34dd514
commit
487982a182
@ -1,9 +0,0 @@
|
|||||||
#include "backing.h"
|
|
||||||
|
|
||||||
#include "entry.h"
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/stat.h>
|
|
@ -1,10 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "utils.h"
|
|
||||||
#include "disk.h"
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#define BACKING_POOL_DIR "/var/lib/sandbox/backings"
|
|
||||||
#define MAX_BACKING_LENGTH 256
|
|
320
src/disk.c
320
src/disk.c
@ -1,56 +1,44 @@
|
|||||||
#include "disk.h"
|
#include "disk.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include <json-c/json.h>
|
#include <json-c/json.h>
|
||||||
|
|
||||||
Result create_root_disk(const char* path, uint64_t size, mode_t permissions) {
|
Status CreateRootDisk(const char* path, uint64_t disk_size) {
|
||||||
// Convert the size to a string
|
// Convert the size to a string
|
||||||
char* size_str;
|
char* size_str;
|
||||||
Result result = format(&size_str, "%lu", size);
|
Status status = Format(&size_str, "%lu", disk_size);
|
||||||
if (result != SUCCESS)
|
if (status != SUCCESS)
|
||||||
return result;
|
return status;
|
||||||
|
|
||||||
// Create the disk
|
// Create the disk
|
||||||
int exit_code;
|
int exit_code;
|
||||||
char* stderr_buffer = NULL;
|
// char* stdout;
|
||||||
result = run_executable(&exit_code, NULL, &stderr_buffer, "qemu-img", "create", "-f", "qcow2", path, size_str, NULL);
|
char* stderrb;
|
||||||
|
|
||||||
|
status = RunExecutable(&exit_code, NULL, &stderrb, "/usr/bin/qemu-img", "create", "-f", "qcow2", path, size_str, NULL);
|
||||||
|
|
||||||
// Free the size string
|
|
||||||
free(size_str);
|
free(size_str);
|
||||||
|
|
||||||
if (result != SUCCESS) {
|
// Check if the disk was created successfully
|
||||||
free(stderr_buffer);
|
if (status != SUCCESS)
|
||||||
return result;
|
return status;
|
||||||
}
|
|
||||||
|
|
||||||
// Check the exit code
|
if (exit_code != 0) {
|
||||||
if (exit_code != EXIT_SUCCESS) {
|
if (stderrb != NULL) {
|
||||||
if (stderr_buffer == NULL)
|
// Remove newlines from the stderr buffer
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to create the disk '%s'.", path);
|
for (int i = 0; stderrb[i] != '\0'; i++)
|
||||||
else {
|
if (stderrb[i] == '\n')
|
||||||
// Remove newlines from the error message
|
stderrb[i] = ' ';
|
||||||
for (size_t i = 0; i < strlen(stderr_buffer); i++)
|
|
||||||
if (stderr_buffer[i] == '\n')
|
|
||||||
stderr_buffer[i] = ' ';
|
|
||||||
|
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to create the disk '%s' (%s).", path, stderr_buffer);
|
Log(LOG_LEVEL_ERROR, "Failed to create disk at '%s' (%s).", path, stderrb);
|
||||||
free(stderr_buffer);
|
free(stderrb);
|
||||||
}
|
} else
|
||||||
|
Log(LOG_LEVEL_ERROR, "Failed to create disk");
|
||||||
return FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the permissions of the disk
|
|
||||||
if (chmod(path, permissions) != 0) {
|
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to set the permissions of the disk '%s' (%s).", path, strerror(errno));
|
|
||||||
|
|
||||||
// Try to remove the disk if the permissions could not be set
|
|
||||||
unlink(path);
|
|
||||||
|
|
||||||
return FAILURE;
|
return FAILURE;
|
||||||
}
|
}
|
||||||
@ -58,40 +46,29 @@ Result create_root_disk(const char* path, uint64_t size, mode_t permissions) {
|
|||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result create_backed_disk(const char* path, const char* backing_disk, mode_t permissions) {
|
Status CreateBackedDisk(const char* path, const char* backing_file) {
|
||||||
// Create the disk
|
// Create the disk
|
||||||
int exit_code;
|
int exit_code;
|
||||||
char* stderr_buffer = NULL;
|
// char* stdout;
|
||||||
Result result = run_executable(&exit_code, NULL, &stderr_buffer, "qemu-img", "create", "-f", "qcow2", "-F", "qcow2", "-b", backing_disk, path, NULL);
|
char* stderrb;
|
||||||
|
|
||||||
if (result != SUCCESS) {
|
Status status = RunExecutable(&exit_code, NULL, &stderrb, "/usr/bin/qemu-img", "create", "-f", "qcow2", "-F", "qcow2", "-b", backing_file, path, NULL);
|
||||||
free(stderr_buffer);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check the exit code
|
// Check if the disk was created successfully
|
||||||
if (exit_code != EXIT_SUCCESS) {
|
if (status != SUCCESS)
|
||||||
if (stderr_buffer == NULL)
|
return status;
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to create the disk '%s'.", path);
|
|
||||||
else {
|
|
||||||
// Remove newlines from the error message
|
|
||||||
for (size_t i = 0; i < strlen(stderr_buffer); i++)
|
|
||||||
if (stderr_buffer[i] == '\n')
|
|
||||||
stderr_buffer[i] = ' ';
|
|
||||||
|
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to create the disk '%s' (%s).", path, stderr_buffer);
|
if (exit_code != 0) {
|
||||||
free(stderr_buffer);
|
if (stderrb != NULL) {
|
||||||
}
|
// Remove newlines from the stderr buffer
|
||||||
|
for (int i = 0; stderrb[i] != '\0'; i++)
|
||||||
|
if (stderrb[i] == '\n')
|
||||||
|
stderrb[i] = ' ';
|
||||||
|
|
||||||
return FAILURE;
|
Log(LOG_LEVEL_ERROR, "Failed to create disk at '%s' (%s).", path, stderrb);
|
||||||
}
|
free(stderrb);
|
||||||
|
} else
|
||||||
// Set the permissions of the disk
|
Log(LOG_LEVEL_ERROR, "Failed to create disk");
|
||||||
if (chmod(path, permissions) != 0) {
|
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to set the permissions of the disk '%s' (%s).", path, strerror(errno));
|
|
||||||
|
|
||||||
// Try to remove the disk if the permissions could not be set
|
|
||||||
unlink(path);
|
|
||||||
|
|
||||||
return FAILURE;
|
return FAILURE;
|
||||||
}
|
}
|
||||||
@ -99,106 +76,101 @@ Result create_backed_disk(const char* path, const char* backing_disk, mode_t per
|
|||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result trim_disk(const char* path) {
|
Status TrimDisk(const char* path) {
|
||||||
char* tmp_path;
|
char* tmp_path = NULL;
|
||||||
Result result = format(&tmp_path, "%s.tmp", path);
|
Status status = Format(&tmp_path, "%s.tmp", path);
|
||||||
if (result != SUCCESS)
|
if (status != SUCCESS)
|
||||||
return result;
|
return status;
|
||||||
|
|
||||||
DiskInfo info;
|
DiskInfo info;
|
||||||
result = get_disk_info(path, &info);
|
status = GetDiskInfo(path, &info);
|
||||||
if (result != SUCCESS) {
|
if (status != SUCCESS) {
|
||||||
free(tmp_path);
|
free(tmp_path);
|
||||||
return result;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the trimmed disk
|
// Create the temporary disk
|
||||||
int exit_code;
|
int exit_code;
|
||||||
char* stderr_buffer = NULL;
|
// char* stdout;
|
||||||
|
char* stderrb;
|
||||||
|
|
||||||
if (info.backing_file == NULL) {
|
if (info.backing_file != NULL) {
|
||||||
// If the disk is not backed, we can simply convert it to a new disk
|
char* backing_file_opt = NULL;
|
||||||
result = run_executable(&exit_code, NULL, &stderr_buffer, "qemu-img", "convert", "-f", "qcow2", "-O", "qcow2", path, tmp_path, NULL);
|
status = Format(&backing_file_opt, "backing_file=%s", info.backing_file);
|
||||||
} else {
|
if (status != SUCCESS) {
|
||||||
// If the disk is backed, we need to specify the backing file
|
|
||||||
char* backing_disk_arg;
|
|
||||||
result = format(&backing_disk_arg, "backing_file=%s", info.backing_file);
|
|
||||||
if (result != SUCCESS) {
|
|
||||||
free(tmp_path);
|
free(tmp_path);
|
||||||
free_disk_info(&info);
|
FreeDiskInfo(&info);
|
||||||
return result;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = run_executable(&exit_code, NULL, &stderr_buffer, "qemu-img", "convert", "-f", "qcow2", "-F", "qcow2", "-O", "qcow2", "-o", backing_disk_arg, path, tmp_path, NULL);
|
status = RunExecutable(&exit_code, NULL, &stderrb, "/usr/bin/qemu-img", "create", "-f", "qcow2", "-F", "qcow2", "-O", "qcow2", "-o", backing_file_opt, tmp_path, NULL);
|
||||||
|
|
||||||
free(backing_disk_arg);
|
free(backing_file_opt);
|
||||||
}
|
} else
|
||||||
|
status = RunExecutable(&exit_code, NULL, &stderrb, "/usr/bin/qemu-img", "create", "-f", "qcow2", "-O", "qcow2", tmp_path, NULL);
|
||||||
|
|
||||||
// Free the disk info as it is no longer needed
|
FreeDiskInfo(&info);
|
||||||
free_disk_info(&info);
|
|
||||||
|
|
||||||
if (result != SUCCESS) {
|
// Check if the disk was created successfully
|
||||||
|
if (status != SUCCESS) {
|
||||||
free(tmp_path);
|
free(tmp_path);
|
||||||
free(stderr_buffer);
|
return status;
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the exit code
|
if (exit_code != 0) {
|
||||||
if (exit_code != EXIT_SUCCESS) {
|
if (stderrb != NULL) {
|
||||||
if (stderr_buffer == NULL)
|
// Remove newlines from the stderr buffer
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to trim the disk '%s'.", path);
|
for (int i = 0; stderrb[i] != '\0'; i++)
|
||||||
else {
|
if (stderrb[i] == '\n')
|
||||||
// Remove newlines from the error message
|
stderrb[i] = ' ';
|
||||||
for (size_t i = 0; i < strlen(stderr_buffer); i++)
|
|
||||||
if (stderr_buffer[i] == '\n')
|
|
||||||
stderr_buffer[i] = ' ';
|
|
||||||
|
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to trim the disk '%s' (%s).", path, stderr_buffer);
|
Log(LOG_LEVEL_ERROR, "Failed to create temporary disk at '%s' (%s).", tmp_path, stderrb);
|
||||||
free(stderr_buffer);
|
free(stderrb);
|
||||||
}
|
} else
|
||||||
|
Log(LOG_LEVEL_ERROR, "Failed to create temporary disk");
|
||||||
|
|
||||||
free(tmp_path);
|
free(tmp_path);
|
||||||
return FAILURE;
|
return FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace the original disk with the trimmed disk
|
// Try to move the temporary disk to the original path
|
||||||
if (rename(tmp_path, path) != 0) {
|
if (rename(tmp_path, path) != 0) {
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to replace the disk '%s' with the trimmed disk '%s' (%s).", path, tmp_path, strerror(errno));
|
Log(LOG_LEVEL_ERROR, "Failed to move the temporary disk to the original path '%s' (%s).", path, strerror(errno));
|
||||||
|
|
||||||
unlink(tmp_path);
|
unlink(tmp_path);
|
||||||
|
|
||||||
free(tmp_path);
|
free(tmp_path);
|
||||||
return FAILURE;
|
return FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free the temporary path
|
|
||||||
free(tmp_path);
|
free(tmp_path);
|
||||||
|
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result rebase_disk(const char* path, const char* backing_disk) {
|
Status RebaseDisk(const char* path, const char* backing_file) {
|
||||||
// Rebase the disk
|
// Create the disk
|
||||||
int exit_code;
|
int exit_code;
|
||||||
char* stderr_buffer = NULL;
|
// char* stdout;
|
||||||
Result result = run_executable(&exit_code, NULL, &stderr_buffer, "qemu-img", "rebase", "-u", "-b", backing_disk, path, NULL);
|
char* stderrb;
|
||||||
|
|
||||||
if (result != SUCCESS) {
|
Status status = RunExecutable(&exit_code, NULL, &stderrb, "/usr/bin/qemu-img", "rebase", "-u", "-b", backing_file, path, NULL);
|
||||||
free(stderr_buffer);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check the exit code
|
// Check if the disk was created successfully
|
||||||
if (exit_code != EXIT_SUCCESS) {
|
if (status != SUCCESS)
|
||||||
if (stderr_buffer == NULL)
|
return status;
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to rebase the disk '%s'.", path);
|
|
||||||
else {
|
|
||||||
// Remove newlines from the error message
|
|
||||||
for (size_t i = 0; i < strlen(stderr_buffer); i++)
|
|
||||||
if (stderr_buffer[i] == '\n')
|
|
||||||
stderr_buffer[i] = ' ';
|
|
||||||
|
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to rebase the disk '%s' (%s).", path, stderr_buffer);
|
if (exit_code != 0) {
|
||||||
free(stderr_buffer);
|
if (stderrb != NULL) {
|
||||||
}
|
// Remove newlines from the stderr buffer
|
||||||
|
for (int i = 0; stderrb[i] != '\0'; i++)
|
||||||
|
if (stderrb[i] == '\n')
|
||||||
|
stderrb[i] = ' ';
|
||||||
|
|
||||||
|
Log(LOG_LEVEL_ERROR, "Failed to rebase disk at '%s' (%s).", path, stderrb);
|
||||||
|
free(stderrb);
|
||||||
|
} else
|
||||||
|
Log(LOG_LEVEL_ERROR, "Failed to rebase disk");
|
||||||
|
|
||||||
return FAILURE;
|
return FAILURE;
|
||||||
}
|
}
|
||||||
@ -206,88 +178,64 @@ Result rebase_disk(const char* path, const char* backing_disk) {
|
|||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result get_disk_info(const char* path, DiskInfo* out_info) {
|
Status GetDiskInfo(const char* path, DiskInfo* _info) {
|
||||||
// Initialize the output
|
*_info = (DiskInfo){0};
|
||||||
out_info->backing_file = NULL;
|
|
||||||
out_info->actual_size = 0;
|
|
||||||
out_info->virtual_size = 0;
|
|
||||||
|
|
||||||
// Get the information about the disk
|
// Get the disk info
|
||||||
int exit_code;
|
int exit_code;
|
||||||
char* stdout_buffer = NULL;
|
char* stdoutb;
|
||||||
char* stderr_buffer = NULL;
|
char* stderrb;
|
||||||
Result result = run_executable(&exit_code, &stdout_buffer, &stderr_buffer, "qemu-img", "info", "--output", "json", path, NULL);
|
|
||||||
|
|
||||||
if (result != SUCCESS) {
|
Status status = RunExecutable(&exit_code, &stdoutb, &stderrb, "/usr/bin/qemu-img", "info", "--output", "json", path, NULL);
|
||||||
free(stdout_buffer);
|
if (status != SUCCESS)
|
||||||
free(stderr_buffer);
|
return status;
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check the exit code
|
if (exit_code != 0) {
|
||||||
if (exit_code != EXIT_SUCCESS) {
|
if (stderrb != NULL) {
|
||||||
if (stderr_buffer == NULL)
|
// Remove newlines from the stderr buffer
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to get the information of the disk '%s'.", path);
|
for (int i = 0; stderrb[i] != '\0'; i++)
|
||||||
else {
|
if (stderrb[i] == '\n')
|
||||||
// Remove newlines from the error message
|
stderrb[i] = ' ';
|
||||||
for (size_t i = 0; i < strlen(stderr_buffer); i++)
|
|
||||||
if (stderr_buffer[i] == '\n')
|
|
||||||
stderr_buffer[i] = ' ';
|
|
||||||
|
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to get the information of the disk '%s' (%s).", path, stderr_buffer);
|
Log(LOG_LEVEL_ERROR, "Failed to get disk info at '%s' (%s).", path, stderrb);
|
||||||
free(stderr_buffer);
|
free(stderrb);
|
||||||
}
|
} else
|
||||||
|
Log(LOG_LEVEL_ERROR, "Failed to get disk info");
|
||||||
|
|
||||||
free(stdout_buffer);
|
free(stdoutb);
|
||||||
return FAILURE;
|
return FAILURE;
|
||||||
}
|
}
|
||||||
|
free(stderrb);
|
||||||
// Free the stderr buffer, as it is no longer needed
|
|
||||||
free(stderr_buffer);
|
|
||||||
|
|
||||||
// Parse the JSON output
|
// Parse the JSON output
|
||||||
json_object* root = json_tokener_parse(stdout_buffer);
|
json_object* root = json_tokener_parse(stdoutb);
|
||||||
|
free(stdoutb);
|
||||||
|
|
||||||
if (root == NULL) {
|
if (root == NULL) {
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to parse the JSON output of 'qemu-img info'.");
|
Log(LOG_LEVEL_ERROR, "Failed to parse the JSON output");
|
||||||
free(stdout_buffer);
|
|
||||||
return FAILURE;
|
return FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free the stdout buffer, as it is no longer needed
|
json_object* virtual_size = NULL;
|
||||||
free(stdout_buffer);
|
json_object* actual_size = NULL;
|
||||||
|
json_object* backing_file = NULL;
|
||||||
|
|
||||||
// Get the virtual size
|
json_object_object_get_ex(root, "virtual-size", &virtual_size);
|
||||||
json_object* virtual_size_obj;
|
json_object_object_get_ex(root, "actual-size", &actual_size);
|
||||||
if (json_object_object_get_ex(root, "virtual-size", &virtual_size_obj))
|
json_object_object_get_ex(root, "backing-filename", &backing_file);
|
||||||
out_info->virtual_size = json_object_get_int64(virtual_size_obj);
|
|
||||||
|
|
||||||
// Get the actual size
|
if (virtual_size != NULL)
|
||||||
json_object* actual_size_obj;
|
_info->size = json_object_get_int64(virtual_size);
|
||||||
if (json_object_object_get_ex(root, "actual-size", &actual_size_obj))
|
if (actual_size != NULL)
|
||||||
out_info->actual_size = json_object_get_int64(actual_size_obj);
|
_info->allocated = json_object_get_int64(actual_size);
|
||||||
|
if (backing_file != NULL)
|
||||||
|
_info->backing_file = strdup(json_object_get_string(backing_file));
|
||||||
|
|
||||||
// Get the backing file
|
|
||||||
json_object* backing_file_obj;
|
|
||||||
if (json_object_object_get_ex(root, "backing-filename", &backing_file_obj)) {
|
|
||||||
out_info->backing_file = strdup(json_object_get_string(backing_file_obj));
|
|
||||||
|
|
||||||
if (out_info->backing_file == NULL) {
|
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to allocate memory for the backing file path.");
|
|
||||||
json_object_put(root);
|
|
||||||
return OUT_OF_MEMORY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free the JSON object
|
|
||||||
json_object_put(root);
|
json_object_put(root);
|
||||||
|
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void free_disk_info(DiskInfo* info) {
|
void FreeDiskInfo(DiskInfo* info) {
|
||||||
free(info->backing_file);
|
free(info->backing_file);
|
||||||
|
|
||||||
info->backing_file = NULL;
|
|
||||||
info->actual_size = 0;
|
|
||||||
info->virtual_size = 0;
|
|
||||||
}
|
}
|
||||||
|
52
src/disk.h
52
src/disk.h
@ -2,53 +2,17 @@
|
|||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
/// @brief The information about a disk.
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/// @brief The virtual size of the disk.
|
uint64_t size;
|
||||||
uint64_t virtual_size;
|
uint64_t allocated;
|
||||||
|
|
||||||
/// @brief The actual size of the disk.
|
|
||||||
uint64_t actual_size;
|
|
||||||
|
|
||||||
/// @brief The path to the backing file of the disk. This is NULL if the disk is not backed.
|
|
||||||
char* backing_file;
|
char* backing_file;
|
||||||
} DiskInfo;
|
} DiskInfo;
|
||||||
|
|
||||||
/// @brief Creates a root disk at the given path, with the given size and with the given permissions.
|
Status CreateRootDisk(const char* path, uint64_t disk_size);
|
||||||
/// @param path The path to the disk to create.
|
Status CreateBackedDisk(const char* path, const char* backing_file);
|
||||||
/// @param size The size of the disk to create.
|
|
||||||
/// @param permissions The permissions to set on the disk.
|
|
||||||
/// @return The result of the operation.
|
|
||||||
Result create_root_disk(const char* path, uint64_t size, mode_t permissions);
|
|
||||||
|
|
||||||
/// @brief Creates a backed disk at the given path, with the given backing disk and with the given permissions.
|
Status TrimDisk(const char* path);
|
||||||
/// @param path The path to the disk to create.
|
Status RebaseDisk(const char* path, const char* backing_file);
|
||||||
/// @param backing_disk The disk to back the new disk with.
|
|
||||||
/// @param permissions The permissions to set on the disk.
|
|
||||||
/// @return The result of the operation.
|
|
||||||
Result create_backed_disk(const char* path, const char* backing_disk, mode_t permissions);
|
|
||||||
|
|
||||||
/// @brief Trims a disk to remove any unused space.
|
Status GetDiskInfo(const char* path, DiskInfo* _info);
|
||||||
/// @param path The path to the disk to trim.
|
void FreeDiskInfo(DiskInfo* info);
|
||||||
/// @return The result of the operation.
|
|
||||||
Result trim_disk(const char* path);
|
|
||||||
|
|
||||||
/// @brief Moves a disk's backing file to the given path.
|
|
||||||
/// @param path The path to the disk to move.
|
|
||||||
/// @param backing_disk The new path to the backing disk.
|
|
||||||
/// @return The result of the operation.
|
|
||||||
Result rebase_disk(const char* path, const char* backing_disk);
|
|
||||||
|
|
||||||
/// @brief Gathers information about a disk.
|
|
||||||
/// @param path The path to the disk to gather information about.
|
|
||||||
/// @param out_info The information about the disk.
|
|
||||||
/// @return The result of the operation.
|
|
||||||
Result get_disk_info(const char* path, DiskInfo* out_info);
|
|
||||||
|
|
||||||
/// @brief Frees the resources used by the given disk information.
|
|
||||||
/// @param info The disk information to free.
|
|
||||||
void free_disk_info(DiskInfo* info);
|
|
629
src/entry.c
629
src/entry.c
File diff suppressed because it is too large
Load Diff
105
src/entry.h
105
src/entry.h
@ -1,99 +1,24 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "disk.h"
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
bool IsEntryIdentifierValid(const char* entry_identifier);
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#define ENTRY_POOL_DIR "/var/lib/sandbox/entries"
|
Status GetEntryPoolPath(char** _entry_pool_path);
|
||||||
#define MAX_ENTRY_LENGTH 256
|
Status GetEntryPath(const char* entry_identifier, char** _entry_path);
|
||||||
|
Status GetEntryDiskPath(const char* entry_identifier, char** _entry_disk_path);
|
||||||
|
|
||||||
#define ENTRY_TYPE_ROOT_STRING "ROOT"
|
Status DoesEntryExist(const char* entry_identifier, bool* _result);
|
||||||
#define ENTRY_TYPE_BACKED_STRING "BACKED"
|
Status DoesEntryDiskExist(const char* entry_identifier, bool* _result);
|
||||||
#define ENTRY_TYPE_AUTOMATIC_STRING "AUTOMATIC"
|
|
||||||
|
|
||||||
typedef enum {
|
Status AddEntry(const char* entry_identifier);
|
||||||
ENTRY_TYPE_UNKNOWN,
|
Status RemoveEntry(const char* entry_identifier);
|
||||||
ENTRY_TYPE_ROOT,
|
|
||||||
ENTRY_TYPE_BACKED,
|
|
||||||
ENTRY_TYPE_AUTOMATIC
|
|
||||||
} EntryType;
|
|
||||||
|
|
||||||
/// @brief Checks whether the given entry id is valid.
|
Status ListEntries(char*** _entries);
|
||||||
/// @param entry_id The entry id to check.
|
Status ClearEntries();
|
||||||
/// @return True if the entry id is valid, false otherwise.
|
|
||||||
bool is_entry_id_valid(const char* entry_id);
|
|
||||||
|
|
||||||
/// @brief Gets the path of the given entry.
|
Status AddRootEntryDisk(const char* entry_identifier, uint64_t disk_size);
|
||||||
/// @param entry_id The entry id.
|
Status AddBackedEntryDisk(const char* entry_identifier, const char* backing_identifier);
|
||||||
/// @param out_path The pointer to the output path string. The caller is responsible for freeing the memory.
|
Status RemoveEntryDisk(const char* entry_identifier);
|
||||||
/// @return The result of the operation.
|
Status ResetEntryDisk(const char* entry_identifier, const char* backing_identifier);
|
||||||
Result get_entry_path(const char* entry_id, char** out_path);
|
Status TrimEntryDisk(const char* entry_identifier);
|
||||||
|
|
||||||
/// @brief Gets the path of the disk of the given entry.
|
|
||||||
/// @param entry_id The entry id.
|
|
||||||
/// @param out_path The pointer to the output path string. The caller is responsible for freeing the memory.
|
|
||||||
/// @return The result of the operation.
|
|
||||||
Result get_entry_disk_path(const char* entry_id, char** out_path);
|
|
||||||
|
|
||||||
/// @brief Gets the backing id of the given entry.
|
|
||||||
/// @param entry_id The entry id.
|
|
||||||
/// @param out_backing_id The pointer to the output backing id string. The caller is responsible for freeing the memory.
|
|
||||||
/// @return The result of the operation.
|
|
||||||
Result get_entry_backing_id(const char* entry_id, char** out_backing_id);
|
|
||||||
|
|
||||||
/// @brief Gets the path of the type file of the given entry.
|
|
||||||
/// @param entry_id The entry id.
|
|
||||||
/// @param out_path The pointer to the output path string. The caller is responsible for freeing the memory.
|
|
||||||
/// @return The result of the operation.
|
|
||||||
Result get_entry_type_path(const char* entry_id, char** out_path);
|
|
||||||
|
|
||||||
/// @brief Gets the type of the given entry.
|
|
||||||
/// @param entry_id The entry id.
|
|
||||||
/// @param out_type The pointer to the output entry type.
|
|
||||||
/// @return The result of the operation.
|
|
||||||
Result get_entry_type(const char* entry_id, EntryType* out_type);
|
|
||||||
|
|
||||||
/// @brief Checks whether the given entry exists in the pool.
|
|
||||||
/// @param entry_id The entry id.
|
|
||||||
/// @param out_exists The pointer to the output boolean.
|
|
||||||
/// @return The result of the operation.
|
|
||||||
Result entry_exists(const char* entry_id, bool* out_exists);
|
|
||||||
|
|
||||||
/// @brief Adds an entry directory to the pool.
|
|
||||||
/// @param entry_id The entry id.
|
|
||||||
/// @param type The type of the entry.
|
|
||||||
/// @return The result of the operation.
|
|
||||||
Result add_entry(const char* entry_id, EntryType type);
|
|
||||||
|
|
||||||
/// @brief Adds a root entry to the pool.
|
|
||||||
/// @param entry_id The entry id.
|
|
||||||
/// @param size The size of the entry.
|
|
||||||
/// @return The result of the operation.
|
|
||||||
Result add_root_entry(const char* entry_id, uint64_t size);
|
|
||||||
|
|
||||||
/// @brief Adds a backed entry to the pool.
|
|
||||||
/// @param entry_id The entry id.
|
|
||||||
/// @param backing_id The backing id.
|
|
||||||
/// @return The result of the operation.
|
|
||||||
Result add_backed_entry(const char* entry_id, const char* backing_id);
|
|
||||||
|
|
||||||
/// @brief Adds an automatic entry to the pool. Automatically selects the latest backing disk available.
|
|
||||||
/// @param entry_id The entry id.
|
|
||||||
/// @return The result of the operation.
|
|
||||||
Result add_automatic_entry(const char* entry_id);
|
|
||||||
|
|
||||||
/// @brief Removes the given entry from the pool.
|
|
||||||
/// @param entry_id The entry id.
|
|
||||||
/// @return The result of the operation.
|
|
||||||
Result remove_entry(const char* entry_id);
|
|
||||||
|
|
||||||
/// @brief Lists the entries in the pool.
|
|
||||||
/// @param out_entries The pointer to the null-terminated array of entry ids. The caller is responsible for freeing the memory of the array and its elements.
|
|
||||||
/// @return The result of the operation.
|
|
||||||
Result list_entries(char*** out_entries);
|
|
||||||
|
|
||||||
/// @brief Clears all the entries from the pool.
|
|
||||||
/// @return The result of the operation.
|
|
||||||
Result clear_entries(void);
|
|
239
src/sandbox.c
239
src/sandbox.c
@ -1,85 +1,65 @@
|
|||||||
#include "sandbox.h"
|
#include "sandbox.h"
|
||||||
|
|
||||||
#include "utils.h"
|
|
||||||
#include "backing.h"
|
|
||||||
#include "entry.h"
|
#include "entry.h"
|
||||||
#include "disk.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <time.h>
|
|
||||||
#include <sys/resource.h>
|
|
||||||
|
|
||||||
const Command COMMANDS[] = {
|
Command COMMANDS[] = {
|
||||||
{command_help, "help", "[command]", "Prints the help message.",
|
{CommandHelp, "help", "[command]", "Prints this help message.",
|
||||||
"Prints the help message for the given command. If no command is given, prints the help message for all commands."},
|
"TODO: Add details."},
|
||||||
{command_version, "version", "", "Prints the version.",
|
{CommandVersion, "version", NULL, "Prints the version of the program.",
|
||||||
"Prints the version of the program."},
|
"TODO: Add details."},
|
||||||
{},
|
|
||||||
{command_add_entry, "add-entry", "<entry id> [--root|-r <size>] [--backed|-b <backing id>]", "Adds an entry to the entry pool.",
|
|
||||||
"Adds an entry to the entry pool with the given id. The entry can either be a root entry or a backed entry. If the entry is a root entry, the size of the entry must be specified. If the entry is a backed entry, the backing id must be specified. By default, the entry will be an automatic entry, which will be backed by the latest backing disk available and will automatically reset to its initial state when a new backing disk is added."},
|
|
||||||
{command_remove_entry, "remove-entry", "<entry id>", "Removes an entry from the entry pool.",
|
|
||||||
"Removes the entry with the given id from the entry pool."},
|
|
||||||
{command_list_entries, "entries", "", "Lists the entries in the entry pool.",
|
|
||||||
"Lists the entries in the entry pool, as well as their types and backing disks."},
|
|
||||||
{command_clear_entries, "clear-entries", "", "Removes all the entries from the entry pool.",
|
|
||||||
"Removes all the entries from the entry pool."},
|
|
||||||
{command_reset_entry, "reset-entry", "<entry id>", "Resets an entry to its initial state.",
|
|
||||||
"TODO"},
|
|
||||||
{command_update_entry, "update-entry", "<entry id>", "Updates the backing disk of the given entry to the latest available.",
|
|
||||||
"TODO"},
|
|
||||||
{},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
// Check that the user 'sandbox' exists
|
struct passwd* pw = getpwnam(SANDBOX_USER);
|
||||||
struct passwd* user = getpwnam(SANDBOX_USER);
|
if (pw == NULL) {
|
||||||
if (user == NULL) {
|
Log(LOG_LEVEL_ERROR, "Failed to get the 'sandbox' user (%s).", strerror(errno));
|
||||||
log_message(LOG_LEVEL_ERROR, "User '%s' does not exist. Please check the installation.", SANDBOX_USER);
|
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that the program is either run as root or as the user 'sandbox'
|
// Check if the current user is root or the 'sandbox' user
|
||||||
if (geteuid() != 0 && geteuid() != user->pw_uid) {
|
if (getuid() != 0 && getuid() != pw->pw_uid) {
|
||||||
log_message(LOG_LEVEL_ERROR, "This program must be run as root or as the user '%s'.", SANDBOX_USER);
|
Log(LOG_LEVEL_ERROR, "You must be root or the 'sandbox' user to use this program.");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the program is run as root, switch to the user 'sandbox'
|
// Try and switch to the 'sandbox' user if we are root
|
||||||
if (geteuid() == 0) {
|
if (geteuid() == 0) {
|
||||||
if (setregid(user->pw_gid, user->pw_gid) != 0) {
|
if (setregid(pw->pw_gid, pw->pw_gid) == -1) {
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to switch to the user '%s'.", SANDBOX_USER);
|
Log(LOG_LEVEL_ERROR, "Failed to set the real and effective group ID to the 'sandbox' user (%s).", strerror(errno));
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setreuid(user->pw_uid, user->pw_uid) != 0) {
|
if (setreuid(pw->pw_uid, pw->pw_uid) == -1) {
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to switch to the user '%s'.", SANDBOX_USER);
|
Log(LOG_LEVEL_ERROR, "Failed to set the real and effective user ID to the 'sandbox' user (%s).", strerror(errno));
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argc < 2)
|
// If there are no arguments, print the help message
|
||||||
return command_help(0, NULL);
|
if (argc == 1)
|
||||||
|
return CommandHelp(0, NULL);
|
||||||
|
|
||||||
size_t input_length = strlen(argv[1]);
|
const char* input = argv[1];
|
||||||
|
size_t input_length = strlen(input);
|
||||||
|
|
||||||
const Command* command = NULL;
|
const Command* command = NULL;
|
||||||
for (int i = 0; i < ARRAY_SIZE(COMMANDS); i++) {
|
for (size_t i = 0; i < sizeof(COMMANDS) / sizeof(COMMANDS[0]); i++) {
|
||||||
if (COMMANDS[i].name == NULL)
|
if (COMMANDS[i].name == NULL)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Check that the length of the input is equal or less than the length of the command name
|
|
||||||
if (input_length > strlen(COMMANDS[i].name))
|
if (input_length > strlen(COMMANDS[i].name))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Check that the input matches the command name
|
if (strncmp(input, COMMANDS[i].name, input_length) == 0) {
|
||||||
if (strncmp(argv[1], COMMANDS[i].name, input_length) == 0) {
|
|
||||||
// Check for multiple matches
|
|
||||||
if (command != NULL) {
|
if (command != NULL) {
|
||||||
log_message(LOG_LEVEL_ERROR, "Ambiguous command '%s'.", argv[1]);
|
Log(LOG_LEVEL_ERROR, "Ambiguous command '%s'.", input);
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,167 +67,66 @@ int main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the command is NULL (no matches)
|
|
||||||
if (command == NULL) {
|
if (command == NULL) {
|
||||||
log_message(LOG_LEVEL_ERROR, "Unknown command '%s'.", argv[1]);
|
Log(LOG_LEVEL_ERROR, "Unknown command '%s'.", input);
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return command->handler(argc - 2, argv + 2);
|
return command->handler(argc - 2, argv + 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
int command_help(int argc, char* argv[]) {
|
int CommandHelp(int argc, char* argv[]) {
|
||||||
// Check the number of arguments
|
|
||||||
if (argc == 0) {
|
if (argc == 0) {
|
||||||
fprintf(stdout, "Usage: sandbox [command] [arguments]\n\n");
|
fprintf(stdout, "Usage: sandbox <command> [arguments] [options]\n");
|
||||||
|
fprintf(stdout, "\n");
|
||||||
fprintf(stdout, "Commands:\n");
|
fprintf(stdout, "Commands:\n");
|
||||||
|
for (size_t i = 0; i < sizeof(COMMANDS) / sizeof(COMMANDS[0]); i++) {
|
||||||
|
if (COMMANDS[i].name == NULL) {
|
||||||
|
fprintf(stdout, "\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < ARRAY_SIZE(COMMANDS); i++)
|
fprintf(stdout, " %s", COMMANDS[i].name);
|
||||||
if (COMMANDS[i].name == NULL)
|
if (COMMANDS[i].arguments != NULL)
|
||||||
printf("\n");
|
fprintf(stdout, " %s", COMMANDS[i].arguments);
|
||||||
else
|
fprintf(stdout, " - %s\n", COMMANDS[i].description);
|
||||||
fprintf(stdout, " %s %s - %s\n", COMMANDS[i].name, COMMANDS[i].arguments, COMMANDS[i].description);
|
}
|
||||||
|
|
||||||
|
fprintf(stdout, "\nFor more information, run 'sandbox help <command>'.\n");
|
||||||
|
|
||||||
fprintf(stdout, "\nRun 'sandbox help [command]' for more information on a command.\n");
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
} else if (argc == 1) {
|
} else if (argc == 1) {
|
||||||
// Find the command that matches the given name
|
const char* input = argv[0];
|
||||||
const Command* command = NULL;
|
|
||||||
|
|
||||||
for (int i = 0; i < ARRAY_SIZE(COMMANDS); i++) {
|
for (size_t i = 0; i < sizeof(COMMANDS) / sizeof(COMMANDS[0]); i++) {
|
||||||
if (COMMANDS[i].name == NULL)
|
if (COMMANDS[i].name == NULL)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (strcmp(argv[0], COMMANDS[i].name) == 0) {
|
if (strcmp(input, COMMANDS[i].name) == 0) {
|
||||||
command = &COMMANDS[i];
|
fprintf(stdout, "Usage: sandbox %s", COMMANDS[i].name);
|
||||||
break;
|
if (COMMANDS[i].arguments != NULL)
|
||||||
|
fprintf(stdout, " %s", COMMANDS[i].arguments);
|
||||||
|
fprintf(stdout, "\n");
|
||||||
|
fprintf(stdout, "\n");
|
||||||
|
fprintf(stdout, "%s\n", COMMANDS[i].description);
|
||||||
|
fprintf(stdout, "\n");
|
||||||
|
fprintf(stdout, "%s\n", COMMANDS[i].details);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the command is NULL (no matches)
|
Log(LOG_LEVEL_ERROR, "Unknown command '%s'.", input);
|
||||||
if (command == NULL) {
|
return EXIT_FAILURE;
|
||||||
log_message(LOG_LEVEL_ERROR, "Unknown command '%s'.", argv[0]);
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print the help message for the command
|
|
||||||
fprintf(stdout, "Usage: sandbox %s %s\n\n", command->name, command->arguments);
|
|
||||||
fprintf(stdout, " %s\n", command->details);
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
} else {
|
} else {
|
||||||
log_message(LOG_LEVEL_ERROR, "Too many arguments for 'help' command.");
|
Log(LOG_LEVEL_ERROR, "Too many arguments supplied to 'help'.");
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int command_version(int argc, char* argv[]) {
|
|
||||||
fprintf(stdout, "Sandbox manager v%s\n", VERSION);
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
int command_add_entry(int argc, char* argv[]) {
|
|
||||||
// Extract the primary arguments
|
|
||||||
if (argc < 1) {
|
|
||||||
log_message(LOG_LEVEL_ERROR, "Missing entry id.");
|
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* entry_id = argv[0];
|
|
||||||
|
|
||||||
// Extract the options
|
|
||||||
bool is_root = false;
|
|
||||||
uint64_t size = 0;
|
|
||||||
|
|
||||||
bool is_backed = false;
|
|
||||||
const char* backing_id = NULL;
|
|
||||||
|
|
||||||
for (int i = 1; i < argc; i++) {
|
|
||||||
if (strcmp(argv[i], "--root") == 0 || strcmp(argv[i], "-r") == 0) {
|
|
||||||
if (is_backed) {
|
|
||||||
log_message(LOG_LEVEL_ERROR, "Cannot specify both --root and --backed options.");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
is_root = true;
|
|
||||||
|
|
||||||
if (i + 1 >= argc) {
|
|
||||||
log_message(LOG_LEVEL_ERROR, "Missing size for root entry.");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result result = parse_size(argv[i + 1], &size);
|
|
||||||
if (result != SUCCESS)
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
|
|
||||||
i++; // Consume the next argument
|
|
||||||
} else if (strcmp(argv[i], "--backed") == 0 || strcmp(argv[i], "-b") == 0) {
|
|
||||||
if (is_root) {
|
|
||||||
log_message(LOG_LEVEL_ERROR, "Cannot specify both --root and --backed options.");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
is_backed = true;
|
|
||||||
|
|
||||||
if (i + 1 >= argc) {
|
|
||||||
log_message(LOG_LEVEL_ERROR, "Missing backing id for backed entry.");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
backing_id = argv[i + 1];
|
|
||||||
i++; // Consume the next argument
|
|
||||||
} else {
|
|
||||||
log_message(LOG_LEVEL_ERROR, "Too many arguments for 'add-entry' command.");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Result result;
|
|
||||||
if (is_root)
|
|
||||||
result = add_root_entry(entry_id, size);
|
|
||||||
else if (is_backed)
|
|
||||||
result = add_backed_entry(entry_id, backing_id);
|
|
||||||
else
|
|
||||||
result = add_automatic_entry(entry_id);
|
|
||||||
|
|
||||||
if (result != SUCCESS)
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
int command_remove_entry(int argc, char* argv[]) {
|
int CommandVersion(int argc, char* argv[]) {
|
||||||
if (argc < 1) {
|
fprintf(stdout, "Sandbox utility v%s\n", VERSION);
|
||||||
log_message(LOG_LEVEL_ERROR, "Missing entry id.");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* entry_id = argv[0];
|
|
||||||
|
|
||||||
if (argc > 1) {
|
|
||||||
log_message(LOG_LEVEL_ERROR, "Too many arguments for 'remove-entry' command.");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result result = remove_entry(entry_id);
|
|
||||||
if (result != SUCCESS)
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
int command_list_entries(int argc, char* argv[]) {
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
int command_clear_entries(int argc, char* argv[]) {
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
int command_reset_entry(int argc, char* argv[]) {
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
int command_update_entry(int argc, char* argv[]) {
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define VERSION "0.0.6"
|
#define VERSION "0.0.9"
|
||||||
|
|
||||||
#define SANDBOX_USER "sandbox"
|
#define SANDBOX_USER "sandbox"
|
||||||
|
|
||||||
@ -12,16 +12,7 @@ typedef struct {
|
|||||||
const char* details;
|
const char* details;
|
||||||
} Command;
|
} Command;
|
||||||
|
|
||||||
extern const Command COMMANDS[];
|
|
||||||
|
|
||||||
int main(int argc, char* argv[]);
|
int main(int argc, char* argv[]);
|
||||||
|
|
||||||
int command_help(int argc, char* argv[]);
|
int CommandHelp(int argc, char* argv[]);
|
||||||
int command_version(int argc, char* argv[]);
|
int CommandVersion(int argc, char* argv[]);
|
||||||
|
|
||||||
int command_add_entry(int argc, char* argv[]);
|
|
||||||
int command_remove_entry(int argc, char* argv[]);
|
|
||||||
int command_list_entries(int argc, char* argv[]);
|
|
||||||
int command_clear_entries(int argc, char* argv[]);
|
|
||||||
int command_reset_entry(int argc, char* argv[]);
|
|
||||||
int command_update_entry(int argc, char* argv[]);
|
|
321
src/utils.c
321
src/utils.c
@ -3,21 +3,21 @@
|
|||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
LogLevel log_level = LOG_LEVEL_WARNING;
|
LogLevel log_level = LOG_LEVEL_INFO;
|
||||||
|
|
||||||
void set_log_level(LogLevel level) {
|
void SetLogLevel(LogLevel level) {
|
||||||
log_level = level;
|
log_level = level;
|
||||||
}
|
}
|
||||||
|
|
||||||
void log_message(LogLevel level, const char* format, ...) {
|
void Log(LogLevel level, const char* format, ...) {
|
||||||
if (level < log_level)
|
if (level < log_level)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -26,13 +26,15 @@ void log_message(LogLevel level, const char* format, ...) {
|
|||||||
|
|
||||||
const char* color;
|
const char* color;
|
||||||
const char* level_str;
|
const char* level_str;
|
||||||
|
|
||||||
// Set the color and level_str based on the log level
|
// Set the color and level_str based on the log level
|
||||||
switch (level) {
|
switch (level) {
|
||||||
case LOG_LEVEL_DEBUG:
|
case LOG_LEVEL_DEBUG:
|
||||||
color = "\033[0;90m";
|
color = "\033[0;90m";
|
||||||
level_str = "DEBUG";
|
level_str = "DEBUG";
|
||||||
break;
|
break;
|
||||||
|
case LOG_LEVEL_INFO:
|
||||||
|
color = "\033[0;36m";
|
||||||
|
level_str = "INFO";
|
||||||
case LOG_LEVEL_WARNING:
|
case LOG_LEVEL_WARNING:
|
||||||
color = "\033[0;33m";
|
color = "\033[0;33m";
|
||||||
level_str = "WARNING";
|
level_str = "WARNING";
|
||||||
@ -58,48 +60,44 @@ void log_message(LogLevel level, const char* format, ...) {
|
|||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result format(char** out_string, const char* fmt, ...) {
|
Status Format(char** _string, const char* fmt, ...) {
|
||||||
*out_string = NULL;
|
*_string = NULL;
|
||||||
|
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
|
|
||||||
// Calculate the length of the formatted string
|
// Calculate the length of the formatted string
|
||||||
size_t length = vsnprintf(NULL, 0, fmt, args) + 1;
|
size_t length = vsnprintf(NULL, 0, fmt, args) + 1;
|
||||||
|
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
if (length <= 0) {
|
if (length <= 0) {
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to calculate the length of the formatted string (%s).", strerror(errno));
|
Log(LOG_LEVEL_ERROR, "Failed to calculate the length of the formatted string.");
|
||||||
return FAILURE;
|
return FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate a buffer for the formatted string
|
// Allocate a buffer for the formatted string
|
||||||
char* buffer = calloc(length, sizeof(char));
|
char* buffer = calloc(length, sizeof(char));
|
||||||
if (buffer == NULL) {
|
if (buffer == NULL) {
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to allocate memory for the formatted string (%s).", strerror(errno));
|
Log(LOG_LEVEL_ERROR, "Failed to allocate memory for the formatted string (%s).", strerror(errno));
|
||||||
return OUT_OF_MEMORY;
|
return FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
|
|
||||||
// Format the string
|
|
||||||
vsnprintf(buffer, length, fmt, args);
|
vsnprintf(buffer, length, fmt, args);
|
||||||
|
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
*out_string = buffer;
|
*_string = buffer;
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result run_executable(int* out_exit_code, char** out_stdout, char** out_stderr, const char* executable, ...) {
|
Status RunExecutable(int* _exit_code, char** _stdout, char** _stderr, const char* executable, ...) {
|
||||||
if (out_exit_code != NULL)
|
if (_exit_code != NULL)
|
||||||
*out_exit_code = -1;
|
*_exit_code = -1;
|
||||||
|
if (_stdout != NULL)
|
||||||
if (out_stdout != NULL)
|
*_stdout = NULL;
|
||||||
*out_stdout = NULL;
|
if (_stderr != NULL)
|
||||||
if (out_stderr != NULL)
|
*_stderr = NULL;
|
||||||
*out_stderr = NULL;
|
|
||||||
|
|
||||||
// Count the number of arguments
|
// Count the number of arguments
|
||||||
int argc = 1; // The first argument is the executable itself
|
int argc = 1; // The first argument is the executable itself
|
||||||
@ -113,48 +111,44 @@ Result run_executable(int* out_exit_code, char** out_stdout, char** out_stderr,
|
|||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
// Allocate an array for the arguments
|
// Allocate an array for the arguments
|
||||||
char** argv = calloc(argc + 1, sizeof(char*));
|
char** argv = calloc(argc + 1, sizeof(char*)); // +1 for the NULL terminator
|
||||||
if (argv == NULL) {
|
if (argv == NULL) {
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to allocate memory for the arguments (%s).", strerror(errno));
|
Log(LOG_LEVEL_ERROR, "Failed to allocate memory for the arguments array (%s).", strerror(errno));
|
||||||
return OUT_OF_MEMORY;
|
return FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill the array with the arguments
|
// Fill the arguments array
|
||||||
argv[0] = strdup(executable);
|
argv[0] = strdup(executable);
|
||||||
if (argv[0] == NULL) {
|
if (argv[0] == NULL) {
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to duplicate the executable string (%s).", strerror(errno));
|
Log(LOG_LEVEL_ERROR, "Failed to duplicate the executable path (%s).", strerror(errno));
|
||||||
|
|
||||||
free(argv);
|
free(argv);
|
||||||
|
return FAILURE;
|
||||||
return OUT_OF_MEMORY;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
va_start(args, executable);
|
va_start(args, executable);
|
||||||
|
|
||||||
|
// Duplicate the arguments and store them in the array
|
||||||
for (int i = 1; i < argc; i++) {
|
for (int i = 1; i < argc; i++) {
|
||||||
argv[i] = strdup(va_arg(args, const char*));
|
argv[i] = strdup(va_arg(args, const char*));
|
||||||
if (argv[i] == NULL) {
|
if (argv[i] == NULL) {
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to duplicate the argument string (%s).", strerror(errno));
|
Log(LOG_LEVEL_ERROR, "Failed to duplicate the argument n°%d (%s).", i, strerror(errno));
|
||||||
|
|
||||||
for (int j = 0; j < i; j++)
|
for (int j = 0; j < i; j++)
|
||||||
free(argv[j]);
|
free(argv[j]);
|
||||||
free(argv);
|
free(argv);
|
||||||
|
|
||||||
va_end(args);
|
return FAILURE;
|
||||||
|
|
||||||
return OUT_OF_MEMORY;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
// Set the last element of the array to NULL
|
|
||||||
argv[argc] = NULL;
|
argv[argc] = NULL;
|
||||||
|
|
||||||
// Create pipes for the stdout and stderr
|
// Create pipes for the standard output and error
|
||||||
int stdout_pipe[2];
|
int stdout_pipe[2];
|
||||||
if (pipe(stdout_pipe) == -1) {
|
if (pipe(stdout_pipe) == -1) {
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to create the stdout pipe (%s).", strerror(errno));
|
Log(LOG_LEVEL_ERROR, "Failed to create the standard output pipe (%s).", strerror(errno));
|
||||||
|
|
||||||
for (int i = 0; i < argc; i++)
|
for (int i = 0; i < argc; i++)
|
||||||
free(argv[i]);
|
free(argv[i]);
|
||||||
@ -165,7 +159,7 @@ Result run_executable(int* out_exit_code, char** out_stdout, char** out_stderr,
|
|||||||
|
|
||||||
int stderr_pipe[2];
|
int stderr_pipe[2];
|
||||||
if (pipe(stderr_pipe) == -1) {
|
if (pipe(stderr_pipe) == -1) {
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to create the stderr pipe (%s).", strerror(errno));
|
Log(LOG_LEVEL_ERROR, "Failed to create the standard error pipe (%s).", strerror(errno));
|
||||||
|
|
||||||
for (int i = 0; i < argc; i++)
|
for (int i = 0; i < argc; i++)
|
||||||
free(argv[i]);
|
free(argv[i]);
|
||||||
@ -181,7 +175,7 @@ Result run_executable(int* out_exit_code, char** out_stdout, char** out_stderr,
|
|||||||
pid_t pid = fork();
|
pid_t pid = fork();
|
||||||
|
|
||||||
if (pid == -1) {
|
if (pid == -1) {
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to fork the process (%s).", strerror(errno));
|
Log(LOG_LEVEL_ERROR, "Failed to fork the process (%s).", strerror(errno));
|
||||||
|
|
||||||
for (int i = 0; i < argc; i++)
|
for (int i = 0; i < argc; i++)
|
||||||
free(argv[i]);
|
free(argv[i]);
|
||||||
@ -196,7 +190,7 @@ Result run_executable(int* out_exit_code, char** out_stdout, char** out_stderr,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
// Redirect the stdout and stderr to the pipes
|
// Redirect the standard output and error to the pipes
|
||||||
dup2(stdout_pipe[1], STDOUT_FILENO);
|
dup2(stdout_pipe[1], STDOUT_FILENO);
|
||||||
dup2(stderr_pipe[1], STDERR_FILENO);
|
dup2(stderr_pipe[1], STDERR_FILENO);
|
||||||
|
|
||||||
@ -207,116 +201,84 @@ Result run_executable(int* out_exit_code, char** out_stdout, char** out_stderr,
|
|||||||
close(stderr_pipe[1]);
|
close(stderr_pipe[1]);
|
||||||
|
|
||||||
// Execute the command
|
// Execute the command
|
||||||
execvp(executable, argv);
|
execv(executable, argv);
|
||||||
|
|
||||||
// If the command failed to execute, print an error message and exit
|
// If execv returns, it failed
|
||||||
fprintf(stderr, "Failed to execute the command '%s' (%s).\n", executable, strerror(errno));
|
fprintf(stderr, "Failed to execute the command (%s).\n", strerror(errno));
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free the arguments array
|
|
||||||
for (int i = 0; i < argc; i++)
|
|
||||||
free(argv[i]);
|
|
||||||
free(argv);
|
|
||||||
|
|
||||||
// Close the unused ends of the pipes
|
// Close the unused ends of the pipes
|
||||||
close(stdout_pipe[1]);
|
close(stdout_pipe[1]);
|
||||||
close(stderr_pipe[1]);
|
close(stderr_pipe[1]);
|
||||||
|
|
||||||
// Wait for the child process to finish
|
// Free the arguments
|
||||||
|
for (int i = 0; i < argc; i++)
|
||||||
|
free(argv[i]);
|
||||||
|
free(argv);
|
||||||
|
|
||||||
|
// Wait for the child process to terminate
|
||||||
int status;
|
int status;
|
||||||
waitpid(pid, &status, 0);
|
waitpid(pid, &status, 0);
|
||||||
|
|
||||||
// Read the output from the pipes
|
// Read the standard output and error
|
||||||
if (out_stdout != NULL)
|
if (_stdout != NULL)
|
||||||
read_fd(stdout_pipe[0], out_stdout);
|
ReadFileDescriptor(stdout_pipe[0], _stdout);
|
||||||
if (out_stderr != NULL)
|
|
||||||
read_fd(stderr_pipe[0], out_stderr);
|
if (_stderr != NULL)
|
||||||
|
ReadFileDescriptor(stderr_pipe[0], _stderr);
|
||||||
|
|
||||||
// Close the pipes
|
// Close the pipes
|
||||||
close(stdout_pipe[0]);
|
close(stdout_pipe[0]);
|
||||||
close(stderr_pipe[0]);
|
close(stderr_pipe[0]);
|
||||||
|
|
||||||
// Set the exit code
|
// Set the exit code
|
||||||
if (out_exit_code != NULL)
|
if (_exit_code != NULL)
|
||||||
*out_exit_code = WEXITSTATUS(status);
|
*_exit_code = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
|
||||||
|
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result read_fd(int fd, char** out_string) {
|
Status ReadFileDescriptor(int fd, char** _content) {
|
||||||
*out_string = NULL;
|
*_content = NULL;
|
||||||
|
|
||||||
// Create a buffer for the output
|
// Allocate a buffer for the content
|
||||||
char buffer[4096];
|
char buffer[4096];
|
||||||
size_t length = 0;
|
size_t length = 0;
|
||||||
|
|
||||||
// Read the output from the file descriptor
|
|
||||||
ssize_t bytes_read;
|
ssize_t bytes_read;
|
||||||
while ((bytes_read = read(fd, buffer, sizeof(buffer))) > 0) {
|
while ((bytes_read = read(fd, buffer, sizeof(buffer))) > 0) {
|
||||||
// Reallocate the buffer
|
char* new_content = realloc(*_content, length + bytes_read + 1); // +1 for the NULL terminator
|
||||||
char* new_buffer = realloc(*out_string, length + bytes_read + 1);
|
if (new_content == NULL) {
|
||||||
if (new_buffer == NULL) {
|
Log(LOG_LEVEL_ERROR, "Failed to reallocate memory for the file content (%s).", strerror(errno));
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to reallocate memory for the output string (%s).", strerror(errno));
|
free(*_content);
|
||||||
|
return FAILURE;
|
||||||
free(*out_string);
|
|
||||||
*out_string = NULL;
|
|
||||||
|
|
||||||
return OUT_OF_MEMORY;
|
|
||||||
}
|
}
|
||||||
|
*_content = new_content;
|
||||||
|
|
||||||
*out_string = new_buffer;
|
memcpy(*_content + length, buffer, bytes_read);
|
||||||
|
|
||||||
// Copy the new data to the buffer
|
|
||||||
memcpy(*out_string + length, buffer, bytes_read);
|
|
||||||
length += bytes_read;
|
length += bytes_read;
|
||||||
|
|
||||||
|
(*_content)[length] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if an error occurred
|
|
||||||
if (bytes_read == -1) {
|
if (bytes_read == -1) {
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to read from the file descriptor (%s).", strerror(errno));
|
Log(LOG_LEVEL_ERROR, "Failed to read from the file descriptor (%s).", strerror(errno));
|
||||||
|
free(*_content);
|
||||||
free(*out_string);
|
|
||||||
*out_string = NULL;
|
|
||||||
|
|
||||||
return FAILURE;
|
return FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Null-terminate the string
|
|
||||||
if (length > 0)
|
|
||||||
(*out_string)[length] = '\0';
|
|
||||||
|
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result read_file(const char* path, char** out_string) {
|
Status WriteFileDescriptor(int fd, const char* content) {
|
||||||
*out_string = NULL;
|
size_t length = strlen(content);
|
||||||
|
|
||||||
// Open the file
|
|
||||||
int fd = open(path, O_RDONLY);
|
|
||||||
if (fd == -1) {
|
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to open the file '%s' (%s).", path, strerror(errno));
|
|
||||||
return FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the file
|
|
||||||
Result result = read_fd(fd, out_string);
|
|
||||||
|
|
||||||
// Close the file
|
|
||||||
close(fd);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result write_fd(int fd, const char* string) {
|
|
||||||
// Write the string to the file descriptor
|
|
||||||
size_t length = strlen(string);
|
|
||||||
ssize_t bytes_written = 0;
|
ssize_t bytes_written = 0;
|
||||||
|
|
||||||
while (bytes_written < length) {
|
while (bytes_written < length) {
|
||||||
ssize_t result = write(fd, string + bytes_written, length - bytes_written);
|
ssize_t result = write(fd, content + bytes_written, length - bytes_written);
|
||||||
if (result == -1) {
|
if (result == -1) {
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to write to the file descriptor (%s).", strerror(errno));
|
Log(LOG_LEVEL_ERROR, "Failed to write to the file descriptor (%s).", strerror(errno));
|
||||||
return FAILURE;
|
return FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -326,141 +288,38 @@ Result write_fd(int fd, const char* string) {
|
|||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result write_file(const char* path, const char* string) {
|
Status ReadFile(const char* path, char** _content) {
|
||||||
|
*_content = NULL;
|
||||||
|
|
||||||
// Open the file
|
// Open the file
|
||||||
int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
int file = open(path, O_RDONLY);
|
||||||
if (fd == -1) {
|
if (file == -1) {
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to open the file '%s' (%s).", path, strerror(errno));
|
Log(LOG_LEVEL_ERROR, "Failed to open the file %s (%s).", path, strerror(errno));
|
||||||
return FAILURE;
|
return FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the string to the file
|
// Read the file
|
||||||
Result result = write_fd(fd, string);
|
Status status = ReadFileDescriptor(file, _content);
|
||||||
|
|
||||||
// Close the file
|
// Close the file
|
||||||
close(fd);
|
close(file);
|
||||||
|
|
||||||
return result;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result copy_file(const char* src_path, const char* dst_path, mode_t mode) {
|
Status WriteFile(const char* path, const char* content) {
|
||||||
// Open the source file
|
// Open the file
|
||||||
int src_fd = open(src_path, O_RDONLY);
|
int file = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||||
if (src_fd == -1) {
|
if (file == -1) {
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to open the source file '%s' (%s).", src_path, strerror(errno));
|
Log(LOG_LEVEL_ERROR, "Failed to open the file %s (%s).", path, strerror(errno));
|
||||||
return FAILURE;
|
return FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open the destination file
|
// Write the content
|
||||||
int dst_fd = open(dst_path, O_WRONLY | O_CREAT | O_TRUNC, mode);
|
Status status = WriteFileDescriptor(file, content);
|
||||||
if (dst_fd == -1) {
|
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to open the destination file '%s' (%s).", dst_path, strerror(errno));
|
|
||||||
|
|
||||||
close(src_fd);
|
// Close the file
|
||||||
return FAILURE;
|
close(file);
|
||||||
}
|
|
||||||
|
|
||||||
// Copy the file
|
return status;
|
||||||
char buffer[4096];
|
|
||||||
ssize_t bytes_read;
|
|
||||||
|
|
||||||
while ((bytes_read = read(src_fd, buffer, sizeof(buffer))) > 0) {
|
|
||||||
ssize_t bytes_written = 0;
|
|
||||||
|
|
||||||
while (bytes_written < bytes_read) {
|
|
||||||
ssize_t result = write(dst_fd, buffer + bytes_written, bytes_read - bytes_written);
|
|
||||||
if (result == -1) {
|
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to write to the destination file '%s' (%s).", dst_path, strerror(errno));
|
|
||||||
|
|
||||||
close(src_fd);
|
|
||||||
close(dst_fd);
|
|
||||||
return FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes_written += result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if an error occurred
|
|
||||||
if (bytes_read == -1) {
|
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to read from the source file '%s' (%s).", src_path, strerror(errno));
|
|
||||||
|
|
||||||
close(src_fd);
|
|
||||||
close(dst_fd);
|
|
||||||
return FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close the files
|
|
||||||
close(src_fd);
|
|
||||||
close(dst_fd);
|
|
||||||
|
|
||||||
return SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result format_size(uint64_t size, char** out_string) {
|
|
||||||
*out_string = NULL;
|
|
||||||
|
|
||||||
// Determine the unit
|
|
||||||
const char* unit;
|
|
||||||
double value;
|
|
||||||
|
|
||||||
if (size < 1024ULL) {
|
|
||||||
unit = "B";
|
|
||||||
value = size;
|
|
||||||
} else if (size < 1024ULL * 1024) {
|
|
||||||
unit = "KiB";
|
|
||||||
value = (double)size / 1024;
|
|
||||||
} else if (size < 1024ULL * 1024 * 1024) {
|
|
||||||
unit = "MiB";
|
|
||||||
value = (double)size / (1024 * 1024);
|
|
||||||
} else if (size < 1024ULL * 1024 * 1024 * 1024) {
|
|
||||||
unit = "GiB";
|
|
||||||
value = (double)size / (1024 * 1024 * 1024);
|
|
||||||
} else if (size < 1024ULL * 1024 * 1024 * 1024 * 1024) {
|
|
||||||
unit = "TiB";
|
|
||||||
value = (double)size / (1024 * 1024 * 1024 * 1024);
|
|
||||||
} else {
|
|
||||||
unit = "PiB";
|
|
||||||
value = (double)size / (1024 * 1024 * 1024 * 1024 * 1024);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format the string
|
|
||||||
return format(out_string, "%.2f%s", value, unit);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result parse_size(const char* string, uint64_t* out_size) {
|
|
||||||
// Parse the size
|
|
||||||
char* endptr;
|
|
||||||
uint64_t size = strtoull(string, &endptr, 10);
|
|
||||||
|
|
||||||
if (endptr == string) {
|
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to parse the size '%s'.", string);
|
|
||||||
return FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine the unit
|
|
||||||
if (*endptr == '\0') {
|
|
||||||
// No unit
|
|
||||||
*out_size = size;
|
|
||||||
} else if (strcmp(endptr, "K") == 0 || strcmp(endptr, "KB") == 0 || strcmp(endptr, "KiB") == 0) {
|
|
||||||
// Kilobytes
|
|
||||||
*out_size = size * 1024;
|
|
||||||
} else if (strcmp(endptr, "M") == 0 || strcmp(endptr, "MB") == 0 || strcmp(endptr, "MiB") == 0) {
|
|
||||||
// Megabytes
|
|
||||||
*out_size = size * 1024 * 1024;
|
|
||||||
} else if (strcmp(endptr, "G") == 0 || strcmp(endptr, "GB") == 0 || strcmp(endptr, "GiB") == 0) {
|
|
||||||
// Gigabytes
|
|
||||||
*out_size = size * 1024 * 1024 * 1024;
|
|
||||||
} else if (strcmp(endptr, "T") == 0 || strcmp(endptr, "TB") == 0 || strcmp(endptr, "TiB") == 0) {
|
|
||||||
// Terabytes
|
|
||||||
*out_size = size * 1024 * 1024 * 1024 * 1024;
|
|
||||||
} else if (strcmp(endptr, "P") == 0 || strcmp(endptr, "PB") == 0 || strcmp(endptr, "PiB") == 0) {
|
|
||||||
// Petabytes
|
|
||||||
*out_size = size * 1024 * 1024 * 1024 * 1024 * 1024;
|
|
||||||
} else {
|
|
||||||
log_message(LOG_LEVEL_ERROR, "Unknown unit '%s'.", endptr);
|
|
||||||
return FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
84
src/utils.h
84
src/utils.h
@ -1,90 +1,30 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <sys/types.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SUCCESS,
|
SUCCESS,
|
||||||
FAILURE,
|
FAILURE
|
||||||
OUT_OF_MEMORY,
|
} Status;
|
||||||
} Result;
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
LOG_LEVEL_DEBUG,
|
LOG_LEVEL_DEBUG,
|
||||||
|
LOG_LEVEL_INFO,
|
||||||
LOG_LEVEL_WARNING,
|
LOG_LEVEL_WARNING,
|
||||||
LOG_LEVEL_ERROR,
|
LOG_LEVEL_ERROR,
|
||||||
} LogLevel;
|
} LogLevel;
|
||||||
|
|
||||||
extern LogLevel log_level;
|
extern LogLevel log_level;
|
||||||
|
|
||||||
/// @brief Sets the log level.
|
void SetLogLevel(LogLevel level);
|
||||||
/// @param level The log level to set.
|
void Log(LogLevel level, const char* format, ...);
|
||||||
void set_log_level(LogLevel level);
|
|
||||||
|
|
||||||
/// @brief Logs a message.
|
Status Format(char** _string, const char* fmt, ...);
|
||||||
/// @param level The log level.
|
|
||||||
/// @param format The format string.
|
|
||||||
/// @param ... The format arguments.
|
|
||||||
void log_message(LogLevel level, const char* format, ...);
|
|
||||||
|
|
||||||
/// @brief Formats a string.
|
Status RunExecutable(int* _exit_code, char** _stdout, char** _stderr, const char* executable, ...);
|
||||||
/// @param out_string The pointer to the output string. The caller is responsible for freeing the memory.
|
|
||||||
/// @param fmt The format string.
|
|
||||||
/// @param ... The format arguments.
|
|
||||||
/// @return The result of the operation.
|
|
||||||
Result format(char** out_string, const char* fmt, ...);
|
|
||||||
|
|
||||||
/// @brief Runs an external executable with the given arguments, and waits for it to finish, then returns the exit code and the output.
|
Status ReadFileDescriptor(int fd, char** _content);
|
||||||
/// @param out_exit_code The pointer to the output exit code. This argument can be NULL if the exit code is not needed.
|
Status WriteFileDescriptor(int fd, const char* content);
|
||||||
/// @param out_stdout The pointer to the output stdout string. This argument can be NULL if the output is not needed. The caller is responsible for freeing the memory.
|
Status ReadFile(const char* path, char** _content);
|
||||||
/// @param out_stderr The pointer to the output stderr string. This argument can be NULL if the output is not needed. The caller is responsible for freeing the memory.
|
Status WriteFile(const char* path, const char* content);
|
||||||
/// @param executable The path of the executable to run.
|
|
||||||
/// @param ... The arguments of the executable. The last argument must be NULL. Note: No need to include the executable path in the arguments.
|
|
||||||
/// @return The result of the operation.
|
|
||||||
Result run_executable(int* out_exit_code, char** out_stdout, char** out_stderr, const char* executable, ...);
|
|
||||||
|
|
||||||
/// @brief Reads the contents of a file descriptor into a string.
|
|
||||||
/// @param fd The file descriptor to read from.
|
|
||||||
/// @param out_string The pointer to the output string. The caller is responsible for freeing the memory.
|
|
||||||
/// @return The result of the operation.
|
|
||||||
Result read_fd(int fd, char** out_string);
|
|
||||||
|
|
||||||
/// @brief Reads the contents of a file into a string.
|
|
||||||
/// @param path The path of the file to read.
|
|
||||||
/// @param out_string The pointer to the output string. The caller is responsible for freeing the memory.
|
|
||||||
/// @return The result of the operation.
|
|
||||||
Result read_file(const char* path, char** out_string);
|
|
||||||
|
|
||||||
/// @brief Writes the given string to a file descriptor.
|
|
||||||
/// @param fd The file descriptor to write to.
|
|
||||||
/// @param string The string to write.
|
|
||||||
/// @return The result of the operation.
|
|
||||||
Result write_fd(int fd, const char* string);
|
|
||||||
|
|
||||||
/// @brief Writes the given string to a file.
|
|
||||||
/// @param path The path of the file to write.
|
|
||||||
/// @param string The string to write.
|
|
||||||
/// @return The result of the operation.
|
|
||||||
Result write_file(const char* path, const char* string);
|
|
||||||
|
|
||||||
/// @brief Copies a file.
|
|
||||||
/// @param src_path The path of the source file.
|
|
||||||
/// @param dst_path The path of the destination file.
|
|
||||||
/// @param mode The mode of the destination file.
|
|
||||||
/// @return The result of the operation.
|
|
||||||
Result copy_file(const char* src_path, const char* dst_path, mode_t mode);
|
|
||||||
|
|
||||||
/// @brief Converts a size in bytes to a human-readable string.
|
|
||||||
/// @param size The size in bytes.
|
|
||||||
/// @param out_string The pointer to the output string. The caller is responsible for freeing the memory.
|
|
||||||
/// @return The result of the operation.
|
|
||||||
Result format_size(uint64_t size, char** out_string);
|
|
||||||
|
|
||||||
/// @brief Parses a size from a human-readable string.
|
|
||||||
/// @param string The string to parse.
|
|
||||||
/// @param out_size The pointer to the output size.
|
|
||||||
/// @return The result of the operation.
|
|
||||||
Result parse_size(const char* string, uint64_t* out_size);
|
|
||||||
|
Loading…
Reference in New Issue
Block a user