Added support for CLI style commands

This commit is contained in:
Alexei KADIR 2024-02-16 17:13:52 +01:00
parent ba07fd7968
commit 2c0dd8d9ab
6 changed files with 168 additions and 51 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
[bB]in/
.vscode/

View File

@ -6,20 +6,16 @@ CF = -Wall -lkrb5 -lvirt -ljson-c -g
# ---- ---- #
build: $(shell find . -name "*.c") $(shell find . -name "*.h")
@echo "Building sandbox..."
@mkdir -p bin
@$(CC) $(CF) -o ./bin/sandbox $(shell find . -name "*.c")
run: build
@echo "Running sandbox..."
@./bin/sandbox
debug: build
@echo "Debugging sandbox..."
@gdb -q ./bin/sandbox
clean:
@echo "Cleaning sandbox..."
@rm -rf ./bin
.PHONY: build run clean

93
src/command.c Normal file
View File

@ -0,0 +1,93 @@
#include "command.h"
#include "sandbox.h"
#include <stdio.h>
#include <string.h>
Command COMMANDS[] = {
{"help", "[command]", "Shows help for all or a specific command.", CMD_HELP},
{"version", "", "Shows the version of the program.", CMD_VERSION},
{NULL},
{"add-entry", "<entry> [--root|-r <size>] [--backing|-b <backing>]", "Adds a new entry to the pool", CMD_ADD_ENTRY},
{"remove-entry", "<entry>", "Removes an entry from the pool", CMD_REMOVE_ENTRY},
{"list-entries", "", "Lists all entries in the pool", CMD_LIST_ENTRIES},
{"clear-entries", "", "Clears all entries from the pool", CMD_CLEAR_ENTRIES},
{NULL},
{"add-backing", "<entry>", "Adds a backing disk to the pool from the given entry", CMD_ADD_BACKING},
{"remove-backing", "<backing>", "Removes a backing disk from the pool", CMD_REMOVE_BACKING},
{"list-backings", "[--tree|-t]", "Lists all backing disks in the pool", CMD_LIST_BACKINGS},
{"clear-backings", "", "Clears all backing disks from the pool", CMD_CLEAR_BACKINGS},
{NULL},
{"start", "<entry> [--no-pci] [--vnc <port> <password>] [--iso <iso>]", "Starts the sandbox with the given entry", CMD_START},
{"power", "", "Sends a power signal to the sandbox", CMD_POWER},
{"stop", "[--force|-f] [--timeout|-t <timeout>]", "Stops the sandbox", CMD_STOP},
{"status", "", "Shows the status of the sandbox", CMD_STATUS},
};
Command* find_command(const char* name) {
Command* match = NULL;
// Find all matching commands (starting with the given name)
size_t length = strlen(name);
for (size_t i = 0; i < sizeof(COMMANDS) / sizeof(COMMANDS[0]); i++) {
if (COMMANDS[i].name == NULL)
continue;
// Check the sizes
if (strlen(COMMANDS[i].name) < length)
continue;
if (strncmp(name, COMMANDS[i].name, length) == 0) {
// Check for multiple matches
if (match != NULL)
return NULL;
match = &COMMANDS[i];
}
}
return match;
}
void show_help() {
printf("Usage: sandbox <command> [args]\n\n");
printf("Commands:\n");
for (size_t i = 0; i < sizeof(COMMANDS) / sizeof(COMMANDS[0]); i++) {
if (COMMANDS[i].name == NULL) {
printf("\n");
continue;
}
printf(" %s %s\n", COMMANDS[i].name, COMMANDS[i].args);
printf(" %s\n", COMMANDS[i].description);
}
printf("\n");
printf("For more information about a specific command, use 'sandbox help <command>'.\n");
}
void show_command_help(const Command* command) {
printf("Usage: sandbox %s %s\n", command->name, command->args);
printf(" %s\n", command->description);
}
int cmd_help(int argc, char** argv) {
if (argc == 0) {
show_help();
return 0;
}
Command* command = find_command(argv[0]);
if (command == NULL) {
printf("Unknown command '%s'.\n", argv[0]);
return 1;
}
show_command_help(command);
return 0;
}
int cmd_version(int argc, char** argv) {
printf("Sandbox version v" SANDBOX_VERSION "\n");
return 0;
}

50
src/command.h Normal file
View File

@ -0,0 +1,50 @@
#pragma once
#include <stdlib.h>
typedef enum {
CMD_UNKNOWN,
CMD_HELP,
CMD_VERSION,
CMD_ADD_ENTRY,
CMD_REMOVE_ENTRY,
CMD_LIST_ENTRIES,
CMD_CLEAR_ENTRIES,
CMD_ADD_BACKING,
CMD_REMOVE_BACKING,
CMD_LIST_BACKINGS,
CMD_CLEAR_BACKINGS,
CMD_START,
CMD_POWER,
CMD_STOP,
CMD_STATUS,
} CommandID;
typedef struct {
const char* name;
const char* args;
const char* description;
CommandID id;
} Command;
extern Command COMMANDS[];
/// @brief Finds the best matching command for the given command name.
/// @param name The name of the command to find.
/// @return The best matching command or NULL if no or multiple matches were found. No need to free the result.
Command* find_command(const char* name);
/// @brief Shows the help for all commands.
void show_help();
/// @brief Shows the help for a specific command.
/// @param command The command to show the help for.
void show_command_help(const Command* command);
int cmd_help(int argc, char** argv);
int cmd_version(int argc, char** argv);

View File

@ -1,28 +1,34 @@
#include "sandbox.h"
#include "utils.h"
#include "command.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char* argv[]) {
if (argc < 2) {
show_help();
return 0;
}
Command* find_command(const char* name) {
Command* match = NULL;
// Find all matching commands (starting with the given name)
size_t length = strlen(name);
for (size_t i = 0; i < sizeof(COMMANDS) / sizeof(COMMANDS[0]); i++)
if (strncmp(name, COMMANDS[i].name, length) == 0) {
// Check for multiple matches
if (match != NULL)
return NULL;
match = &COMMANDS[i];
}
return match;
Command* command = find_command(argv[1]);
if (command == NULL) {
log_message(LOG_ERROR, "Unknown command '%s'.", argv[1]);
return 1;
}
switch (command->id) {
case CMD_HELP:
return cmd_help(argc - 2, argv + 2);
case CMD_VERSION:
return cmd_version(argc - 2, argv + 2);
default:
log_message(LOG_ERROR, "Command '%s' is not implemented.", command->name);
break;
}
return 0;
}

View File

@ -2,35 +2,6 @@
#include <stdlib.h>
typedef struct {
const char* name;
const char* args;
const char* description;
} Command;
Command COMMANDS[] = {
{"help", "[command]", "Shows help for all or a specific command."},
{"version", NULL, "Shows the version of the program."},
{NULL, NULL, NULL},
{"add-entry", "<entry> [--root|-r <size>] [--backing|-b <backing>]", "Adds a new entry to the pool"},
{"remove-entry", "<entry>", "Removes an entry from the pool"},
{"list-entries", NULL, "Lists all entries in the pool"},
{"clear-entries", NULL, "Clears all entries from the pool"},
{NULL, NULL, NULL},
{"add-backing", "<entry>", "Adds a backing disk to the pool from the given entry"},
{"remove-backing", "<backing>", "Removes a backing disk from the pool"},
{"list-backings", "[--tree|-t]", "Lists all backing disks in the pool"},
{"clear-backings", NULL, "Clears all backing disks from the pool"},
{NULL, NULL, NULL},
{"start", "<entry> [--no-pci] [--vnc <port> <password>] [--iso <iso>]", "Starts the sandbox with the given entry"},
{"power", NULL, "Sends a power signal to the sandbox"},
{"stop", "[--force|-f] [--timeout|-t <timeout>]", "Stops the sandbox"},
{"status", NULL, "Shows the status of the sandbox"},
};
#define SANDBOX_VERSION "1.0.0"
int main(int argc, char* argv[]);
/// @brief Finds the best matching command for the given command name.
/// @param name The name of the command to find.
/// @return The best matching command or NULL if no or multiple matches were found. No need to free the result.
Command* find_command(const char* name);