aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar bigfoot547 <[email protected]>2023-12-25 21:59:53 -0600
committerLibravatar bigfoot547 <[email protected]>2023-12-25 21:59:53 -0600
commit5a1568cb0c3ef6ed697269add229c4d5e6829b9b (patch)
tree80d73cb8155a4381718794a6fefe10f9e8f05567
parent[WIP] Add instances (diff)
add instance remove
-rw-r--r--meson.build2
-rw-r--r--src/cmd-instance.c203
-rw-r--r--src/instance.c6
-rw-r--r--src/macros.h1
4 files changed, 202 insertions, 10 deletions
diff --git a/meson.build b/meson.build
index dc8284b..cdb51a0 100644
--- a/meson.build
+++ b/meson.build
@@ -1,5 +1,5 @@
project('l2su', 'c')
-add_global_arguments('-D_POSIX_C_SOURCE=200809L', language : 'c')
+add_global_arguments('-D_XOPEN_SOURCE=700', language : 'c')
curl_dep = dependency('libcurl')
jansson_dep = dependency('jansson')
diff --git a/src/cmd-instance.c b/src/cmd-instance.c
index 89494af..e187b7d 100644
--- a/src/cmd-instance.c
+++ b/src/cmd-instance.c
@@ -2,12 +2,18 @@
#include "command.h"
#include "instance.h"
#include "l2su.h"
+#include "uuid/uuid.h"
+#include "macros.h"
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <stdbool.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <ftw.h>
+#include <unistd.h>
unsigned cmd_instance_add(struct l2_context_node *ctx, char **args)
{
@@ -17,10 +23,21 @@ unsigned cmd_instance_add(struct l2_context_node *ctx, char **args)
char *newpath = NULL;
uuid_t uuid;
char uuidstr[UUID_STRLEN+1];
+ bool flag_force = false;
+ DIR *dir = NULL;
+
+ for (char **cur = args; *cur; ++cur) {
+ if (!strcmp(*cur, "--force")) {
+ flag_force = true;
+ } else {
+ fprintf(stderr, "fatal: Unknown argument '%s'.\n", *cur);
+ return CMD_RESULT_FAIL;
+ }
+ }
if (res != INSTANCE_SUCCESS) {
if (res == INSTANCE_ERRNO) {
- fprintf(stderr, "fatal: Error loading instances: %s", strerror(errno));
+ fprintf(stderr, "fatal: Error loading instances: %s\n", strerror(errno));
} else {
fprintf(stderr, "fatal: Error loading instances (%d)\n", res);
}
@@ -72,9 +89,12 @@ unsigned cmd_instance_add(struct l2_context_node *ctx, char **args)
} while (found);
/* create the directory at "path" (if it isn't empty, complain) */
- if (path && *path != '/') {
- fputs("fatal: Specified path is not an absolute path\n", stderr);
- return CMD_RESULT_FAIL;
+ if (path) {
+ newpath = realpath(path, NULL);
+ if (!newpath) {
+ fprintf(stderr, "fatal: Failed to canonicalize specified path: %s\n", strerror(errno));
+ return CMD_RESULT_FAIL;
+ }
} else if (!path) {
newpath = strdup(l2_state.paths.data);
if (!newpath) {
@@ -90,9 +110,44 @@ unsigned cmd_instance_add(struct l2_context_node *ctx, char **args)
l2_uuid_to_string(&uuid, uuidstr);
newpath = l2_launcher_strapp(newpath, uuidstr);
if (!newpath) goto newpath_append_fail;
+
+ path = newpath;
}
/* TODO: now create the directory for realsies */
+ if (l2_launcher_mkdir_parents(path) < 0) {
+ fprintf(stderr, "fatal: Could not create instance directory: %s\n", strerror(errno));
+ res = CMD_RESULT_FAIL;
+ goto cleanup;
+ }
+
+ if (!flag_force) {
+ dir = opendir(path);
+ if (!dir) {
+ fprintf(stderr, "fatal: Could not check instance directory: %s\n", strerror(errno));
+ res = CMD_RESULT_FAIL;
+ goto cleanup;
+ }
+
+ struct dirent *ent;
+ errno = 0;
+ while ((ent = readdir(dir))) {
+ if (strcmp(ent->d_name, ".") && strcmp(ent->d_name, "..")) {
+ fprintf(stderr, "fatal: Instance directory '%s' is not empty. If this is intentional, rerun the command with --force.\n", path);
+ res = CMD_RESULT_FAIL;
+ goto cleanup;
+ }
+ }
+
+ closedir(dir);
+ dir = NULL;
+
+ if (errno) {
+ fprintf(stderr, "fatal: Error scanning instance directory: %s\n", strerror(errno));
+ res = CMD_RESULT_FAIL;
+ goto cleanup;
+ }
+ }
/* add the instance */
@@ -100,7 +155,7 @@ unsigned cmd_instance_add(struct l2_context_node *ctx, char **args)
inst.uuid = uuid;
inst.name = name;
- inst.path = path ? path : newpath;
+ inst.path = path;
if ((res = l2_instance_add_instance(&inst)) != INSTANCE_SUCCESS) {
fprintf(stderr, "fatal: Failed to add instance (%d)\n", res);
@@ -114,18 +169,150 @@ unsigned cmd_instance_add(struct l2_context_node *ctx, char **args)
goto cleanup;
}
- printf("add command yay\n");
-
res = CMD_RESULT_SUCCESS;
cleanup:
+ if (dir) closedir(dir);
free(newpath);
return res;
}
+int cmd__delete_with_reckless_abandon(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf)
+{
+ L2_UNUSED(sb);
+ L2_UNUSED(ftwbuf);
+
+ switch (typeflag) {
+ case FTW_F:
+ case FTW_SL:
+ if (unlink(fpath) < 0) return errno;
+ break;
+ case FTW_DP:
+ if (rmdir(fpath) < 0) return errno;
+ break;
+ /* unlink */
+ case FTW_NS:
+ case FTW_DNR:
+ return -2;
+ /* error */
+ }
+
+ return 0;
+}
+
unsigned cmd_instance_remove(struct l2_context_node *ctx, char **args)
{
- return CMD_RESULT_SUCCESS;
+ char *name = NULL;
+ bool flag_remove = false, flag_byid = false;
+ int res;
+ uuid_t instid = UUID_NULL_INIT;
+ struct l2_instance *to_remove = NULL;
+ char *removepath = NULL;
+
+ for (char **cur = args; *cur; ++cur) {
+ if (!strcmp("--remove", *cur)) {
+ flag_remove = true;
+ } else if (!strcmp("--id", *cur)) {
+ flag_byid = true;
+ } else {
+ fprintf(stderr, "fatal: Unknown argument '%s'\n", *cur);
+ return CMD_RESULT_FAIL;
+ }
+ }
+
+ for (struct l2_context_node *cur = ctx; cur; cur = cur->next) {
+ if (cur->node->type != CMD_NODE_TYPE_ARGUMENT) continue;
+
+ if (!strcmp("name", cur->node->name)) {
+ name = cur->value;
+ }
+ }
+
+ if (!name) {
+ fputs("fatal: Name not specified\n", stderr);
+ return CMD_RESULT_FAIL;
+ }
+
+ if (flag_byid && !l2_uuid_from_string(&instid, name)) {
+ fprintf(stderr, "Failed to parse instance UUID '%s'.\n", name);
+ return CMD_RESULT_FAIL;
+ }
+
+ res = l2_instance_load_all();
+ if (res != INSTANCE_SUCCESS) {
+ fprintf(stderr, "fatal: Failed to load instances (%d).\n", res);
+ return CMD_RESULT_FAIL;
+ }
+
+ /* find the thing */
+ if (flag_byid) {
+ for (struct l2_instance *inst = l2_state.instance_head; inst; inst = inst->next) {
+ if (!l2_uuid_compare(&inst->uuid, &instid)) {
+ to_remove = inst;
+ break;
+ }
+ }
+ } else {
+ for (struct l2_instance *inst = l2_state.instance_head; inst; inst = inst->next) {
+ if (!strcmp(inst->name, name)) {
+ to_remove = inst;
+ break;
+ }
+ }
+ }
+
+ if (!to_remove) {
+ fputs("fatal: Instance not found.\n", stderr);
+ return CMD_RESULT_FAIL;
+ }
+
+ if (flag_remove) {
+ removepath = strdup(to_remove->path);
+ if (!removepath) {
+ fputs("fatal: Failed to create path.\n", stderr);
+ return CMD_RESULT_FAIL;
+ }
+ }
+
+ /* remove the thing */
+ if ((res = l2_instance_del_instance(to_remove)) != INSTANCE_SUCCESS) {
+ fprintf(stderr, "fatal: Failed to remove instance (%d).\n", res);
+ return CMD_RESULT_FAIL;
+ }
+
+ to_remove = NULL; /* del_instance frees the instance */
+
+ if ((res = l2_instance_save_all()) != INSTANCE_SUCCESS) {
+ fprintf(stderr, "fatal: Failed to save instances (%d).\n", res);
+ return CMD_RESULT_FAIL;
+ }
+
+ /* delete the thing if it is necessary */
+ if (flag_remove) {
+ res = nftw(removepath, &cmd__delete_with_reckless_abandon, 128, FTW_DEPTH | FTW_PHYS);
+ switch (res) {
+ case -2:
+ fputs("Unreadable file/folder encountered while walking file tree.\n", stderr);
+ res = CMD_RESULT_FAIL;
+ goto cleanup;
+ case -1:
+ fputs("Error walking file tree.\n", stderr);
+ res = CMD_RESULT_FAIL;
+ goto cleanup;
+ case 0:
+ break;
+ default:
+ fprintf(stderr, "Error removing a file: %s\n", strerror(res));
+ res = CMD_RESULT_FAIL;
+ goto cleanup;
+ }
+ }
+
+ res = CMD_RESULT_SUCCESS;
+
+cleanup:
+ if (removepath) free(removepath);
+ return res;
}
unsigned cmd_instance_list(struct l2_context_node *ctx, char **args)
diff --git a/src/instance.c b/src/instance.c
index 20c0f59..148f71d 100644
--- a/src/instance.c
+++ b/src/instance.c
@@ -46,7 +46,7 @@ int l2_instance_load_all(void)
int l2_instance_save_all(void)
{
json_t *js = NULL;
- int instfd = l2_launcher_open_config("instance.json", O_WRONLY | O_CREAT, 0644);
+ int instfd = l2_launcher_open_config("instance.json", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (instfd < 0) return INSTANCE_ERRNO;
int retval = INSTANCE_SUCCESS;
@@ -116,6 +116,10 @@ int l2_instance_del_instance(struct l2_instance *inst)
l2_state.instance_head = inst->next;
}
+ free((char *)inst->name); /* okay to cast away const because any instance in the list will be allocated with strdup */
+ free((char *)inst->path);
+ free(inst);
+
return INSTANCE_SUCCESS;
}
diff --git a/src/macros.h b/src/macros.h
index 771032c..4cf6e7b 100644
--- a/src/macros.h
+++ b/src/macros.h
@@ -4,5 +4,6 @@
#include "config.h"
#define L2_ARRLEN(_a) (sizeof(_a) / sizeof(*(_a)))
+#define L2_UNUSED(_v) ((void)(_v))
#endif /* include guard */