#include "commands.h" #include "command.h" #include "instance.h" #include "l2su.h" #include "uuid/uuid.h" #include "macros.h" #include #include #include #include #include #include #include #include #include unsigned cmd_instance_add(struct l2_context_node *ctx, char **args) { int res = l2_instance_load_all(); char *name = NULL; char *path = NULL; 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\n", strerror(errno)); } else { fprintf(stderr, "fatal: Error loading instances (%d)\n", res); } return CMD_RESULT_FAIL; } /* get the name and path (if it exists) */ 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; } else if (!strcmp("location", cur->node->name)) { path = cur->value; } } /* make sure the name and path make sense (path should be an absolute path, name shouldn't be empty) */ if (!name || !*name) { fputs("fatal: The specified name is invalid\n", stderr); return CMD_RESULT_FAIL; } for (char *cur = name; *cur; ++cur) { if (*cur < ' ') { fputs("fatal: The specified name contains invalid characters\n", stderr); return CMD_RESULT_FAIL; } } /* make sure there is not a profile with that name already */ for (struct l2_instance *cur = l2_state.instance_head; cur; cur = cur->next) { if (!strcmp(cur->name, name)) { fprintf(stderr, "fatal: An instance by the name '%s' already exists.\n", cur->name); return CMD_RESULT_FAIL; } } /* also find an unused UUID through rejection sampling */ bool found; do { found = false; l2_uuid_random(&uuid); for (struct l2_instance *cur = l2_state.instance_head; cur; cur = cur->next) { if (!l2_uuid_compare(&cur->uuid, &uuid)) { found = true; break; } } } while (found); /* create the directory at "path" (if it isn't empty, complain) */ 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) { newpath_append_fail: free(newpath); fprintf(stderr, "fatal: Allocation failed: %s\n", strerror(errno)); return CMD_RESULT_FAIL; } newpath = l2_launcher_strapp(newpath, "/instances/"); if (!newpath) goto newpath_append_fail; 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 */ struct l2_instance inst; inst.uuid = uuid; inst.name = name; inst.path = path; if ((res = l2_instance_add_instance(&inst)) != INSTANCE_SUCCESS) { fprintf(stderr, "fatal: Failed to add instance (%d)\n", res); res = CMD_RESULT_FAIL; goto cleanup; } if ((res = l2_instance_save_all()) != INSTANCE_SUCCESS) { fprintf(stderr, "fatal: Failed to add instance (%d)\n", res); res = CMD_RESULT_FAIL; goto cleanup; } 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) { 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) { return CMD_RESULT_SUCCESS; } unsigned cmd_instance_rename(struct l2_context_node *ctx, char **args) { return CMD_RESULT_SUCCESS; }