218 lines
8.4 KiB
C++
218 lines
8.4 KiB
C++
#include <cstdlib>
|
|
#include <iostream>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <map>
|
|
#include <algorithm>
|
|
#include <unistd.h>
|
|
#include <sys/wait.h>
|
|
|
|
std::map<std::string, std::string> variables;
|
|
std::vector<std::string> containers;
|
|
|
|
std::vector<std::string> lexCommand(const std::string& command) {
|
|
std::vector<std::string> lexed;
|
|
std::string buf;
|
|
bool inString = false;
|
|
bool inVariable = false;
|
|
|
|
for (const char& c : command) {
|
|
bool addToBuf = inString && inVariable;
|
|
switch (c) {
|
|
case '"': {
|
|
inString = !inString;
|
|
if (!inString) {
|
|
lexed.push_back(buf);
|
|
buf.clear();
|
|
}
|
|
break;
|
|
}
|
|
case ' ': {
|
|
if (inString) {
|
|
buf += c;
|
|
} else if (inVariable) {
|
|
if (variables.find(buf) != variables.end()) {
|
|
if (!buf.empty()) lexed.push_back(variables[buf]);
|
|
buf.clear();
|
|
} else {
|
|
lexed.push_back("");
|
|
buf.clear();
|
|
}
|
|
inVariable = false;
|
|
} else {
|
|
if (!buf.empty()) lexed.push_back(buf);
|
|
buf.clear();
|
|
}
|
|
break;
|
|
}
|
|
case '$': {
|
|
if (addToBuf) {
|
|
buf += c;
|
|
} else {
|
|
inVariable = true;
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
buf += c;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!buf.empty()) lexed.push_back(buf);
|
|
|
|
return lexed;
|
|
}
|
|
|
|
void safeExecute(const std::vector<std::string>& args) {
|
|
pid_t pid = fork();
|
|
if (pid == 0) {
|
|
std::vector<char*> c_args;
|
|
for (const auto& arg : args) {
|
|
c_args.push_back(const_cast<char*>(arg.c_str()));
|
|
}
|
|
c_args.push_back(nullptr);
|
|
execvp(c_args[0], c_args.data());
|
|
exit(1);
|
|
} else {
|
|
wait(NULL);
|
|
}
|
|
}
|
|
|
|
void syncContainers() {
|
|
containers.clear();
|
|
// Run podman command to get names of all containers
|
|
FILE* pipe = popen("podman ps -a --format '{{.Names}}'", "r");
|
|
if (!pipe) return;
|
|
|
|
char buffer[128];
|
|
while (fgets(buffer, sizeof(buffer), pipe) != NULL) {
|
|
std::string name = buffer;
|
|
// Remove trailing newline
|
|
if (!name.empty() && name.back() == '\n') name.pop_back();
|
|
containers.push_back(name);
|
|
}
|
|
pclose(pipe);
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
std::cout << "Welcome to Spinny! Type a command to get started..." << std::endl;
|
|
|
|
bool done = false;
|
|
std::string prompt;
|
|
while (!done) {
|
|
std::cout << "\033]0;Spinny\007" << std::flush;
|
|
std::cout << "> ";
|
|
std::getline(std::cin, prompt);
|
|
auto command = lexCommand(prompt);
|
|
|
|
if (!command.empty()) {
|
|
syncContainers();
|
|
if (command[0] == "exit") {
|
|
done = true;
|
|
}
|
|
else if (command[0] == "set") {
|
|
if (command.size() < 3) {
|
|
std::cout << "Usage: set [key] [value]" << std::endl;
|
|
} else {
|
|
variables[command[1]] = command[2];
|
|
}
|
|
}
|
|
else if (command[0] == "create") {
|
|
if (command.size() < 2) {
|
|
std::cout << "Usage: create [name]" << std::endl;
|
|
} else {
|
|
if (std::find(containers.begin(), containers.end(), command[1]) != containers.end()) {
|
|
std::cout << "Container " + command[1] + " already exists (use 'delete " + command[1] + "' to delete the existing container)" << std::endl;
|
|
} else {
|
|
safeExecute({"podman", "run", "-d", "--name", command[1], "dev-container", "tail", "-f", "/dev/null"});
|
|
containers.push_back(command[1]);
|
|
std::cout << "Done! Enter your new container with 'enter " + command[1] + "'" << std::endl;
|
|
}
|
|
}
|
|
}
|
|
else if (command[0] == "start") {
|
|
if (command.size() < 2) {
|
|
std::cout << "Usage: start [name]" << std::endl;
|
|
} else {
|
|
if (std::find(containers.begin(), containers.end(), command[1]) != containers.end()) {
|
|
safeExecute({"podman", "start", command[1]});
|
|
} else {
|
|
std::cout << "Container " + command[1] + " not found (use 'create " + command[1] + "' to make it)" << std::endl;
|
|
}
|
|
}
|
|
}
|
|
else if (command[0] == "delete") {
|
|
if (command.size() < 2) {
|
|
std::cout << "Usage: start [name]" << std::endl;
|
|
} else {
|
|
if (std::find(containers.begin(), containers.end(), command[1]) != containers.end()) {
|
|
std::string buf;
|
|
std::cout << "This will delete the container, and all files within it. Are you sure? (yes/no)";
|
|
std::getline(std::cin, buf);
|
|
if (buf == "yes") {
|
|
containers.erase(std::remove(containers.begin(), containers.end(), command[1]), containers.end());
|
|
safeExecute({"podman", "rm", command[1]});
|
|
std::cout << "Deleted!" << std::endl;
|
|
} else {
|
|
std::cout << "*container breathes a sigh of relief*" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << "Container " + command[1] + " not found (use 'create " + command[1] + "' to make it)" << std::endl;
|
|
}
|
|
}
|
|
}
|
|
else if (command[0] == "stop") {
|
|
if (command.size() < 2) {
|
|
std::cout << "Usage: stop [name]" << std::endl;
|
|
} else {
|
|
if (std::find(containers.begin(), containers.end(), command[1]) != containers.end()) {
|
|
safeExecute({"podman", "stop", command[1]});
|
|
} else {
|
|
std::cout << "Container " + command[1] + " not found (use 'create " + command[1] + "' to make it)" << std::endl;
|
|
}
|
|
}
|
|
}
|
|
else if (command[0] == "enter") {
|
|
if (command.size() < 2) {
|
|
std::cout << "Usage: enter [name]" << std::endl;
|
|
} else {
|
|
if (std::find(containers.begin(), containers.end(), command[1]) != containers.end()) {
|
|
safeExecute({"podman", "exec", "-it", command[1], "fish"});
|
|
} else {
|
|
std::cout << "Container " + command[1] + " not found (use 'create " + command[1] + "' to make it)" << std::endl;
|
|
}
|
|
|
|
}
|
|
}
|
|
else if (command[0] == "clear") {
|
|
safeExecute({"clear"});
|
|
}
|
|
else if (command[0] == "help") {
|
|
std::cout << "Commands:" << std::endl;
|
|
std::cout << " create [name] - Creates an empty container" << std::endl;
|
|
std::cout << " enter [name] - Opens a shell in a container" << std::endl;
|
|
std::cout << " start [name] - Starts a container" << std::endl;
|
|
std::cout << " stop [name] - Stops a container" << std::endl;
|
|
std::cout << " delete [name] - Deletes a container" << std::endl;
|
|
std::cout << " tmp - Create a temporary container" << std::endl;
|
|
std::cout << " list - List containers" << std::endl;
|
|
std::cout << " exit - Exits Spinny" << std::endl;
|
|
}
|
|
else if (command[0] == "list" || command[0] == "ls") {
|
|
std::cout << "My containers:" << std::endl;
|
|
safeExecute({"podman", "ps", "-a"});
|
|
}
|
|
else if (command[0] == "tmp") {
|
|
std::cout << "Spawning temporary container..." << std::endl;
|
|
safeExecute({"podman", "run", "-it", "--rm", "dev-container", "fish"});
|
|
} else {
|
|
std::cout << "I don't know how to " + command[0] + " (run 'help' for commands)" << std::endl;
|
|
}
|
|
}
|
|
|
|
}
|
|
std::cout << "Goodbye!" << std::endl;
|
|
return 0;
|
|
}
|