Introduce CLI
main: Parse args using getopt_long() in main(). Begin implementation of a CLI. It can be launched using ./qswiki config --cli. Allow connecting to another instance using "attach" command. This uses Unix domain sockets, and in the future can be used to drop caches, reload template, etc. Closes: #21
This commit is contained in:
		
							
								
								
									
										220
									
								
								cli.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										220
									
								
								cli.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,220 @@
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <iomanip>
 | 
			
		||||
 | 
			
		||||
#include "cli.h"
 | 
			
		||||
#include "utils.h"
 | 
			
		||||
#include "random.h"
 | 
			
		||||
#include "authenticator.h"
 | 
			
		||||
#include "config.h"
 | 
			
		||||
#include "logger.h"
 | 
			
		||||
 | 
			
		||||
CLIHandler::CLIHandler(Config &config, Database &db)
 | 
			
		||||
{
 | 
			
		||||
	this->db = &db;
 | 
			
		||||
	this->conf = &config;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::pair<bool, std::string> CLIHandler::user_add(const std::vector<std::string> &args)
 | 
			
		||||
{
 | 
			
		||||
	std::string username = args.at(0);
 | 
			
		||||
	std::string password = args.at(1);
 | 
			
		||||
 | 
			
		||||
	auto userDao = db->createUserDao();
 | 
			
		||||
 | 
			
		||||
	Permissions perms = this->conf->handlersConfig.anon_permissions;
 | 
			
		||||
	int p = perms.getPermissions();
 | 
			
		||||
	p |= PERM_CAN_CREATE | PERM_CAN_SEARCH | PERM_CAN_EDIT;
 | 
			
		||||
	Permissions newPermissions = Permissions{p};
 | 
			
		||||
 | 
			
		||||
	Random r;
 | 
			
		||||
	User user;
 | 
			
		||||
	user.enabled = true;
 | 
			
		||||
	user.login = username;
 | 
			
		||||
	user.salt = r.getRandom(AUTH_DEFAULT_SALT_SIZE);
 | 
			
		||||
	user.permissions = newPermissions;
 | 
			
		||||
 | 
			
		||||
	Authenticator auth{*userDao};
 | 
			
		||||
	std::vector<char> hashResult = auth.hash(password, user.salt);
 | 
			
		||||
	if(hashResult.empty())
 | 
			
		||||
	{
 | 
			
		||||
		return {false, "Error during hashing - Got empty hash"};
 | 
			
		||||
	}
 | 
			
		||||
	user.password = hashResult;
 | 
			
		||||
 | 
			
		||||
	try
 | 
			
		||||
	{
 | 
			
		||||
		userDao->save(user);
 | 
			
		||||
	}
 | 
			
		||||
	catch(std::runtime_error &e)
 | 
			
		||||
	{
 | 
			
		||||
		return {false, "Exception: " + std::string(e.what())};
 | 
			
		||||
	}
 | 
			
		||||
	return {true, ""};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::pair<bool, std::string> CLIHandler::user_change_pw(const std::vector<std::string> &args)
 | 
			
		||||
{
 | 
			
		||||
	std::string username = args.at(0);
 | 
			
		||||
	std::string password = args.at(1);
 | 
			
		||||
 | 
			
		||||
	auto userDao = db->createUserDao();
 | 
			
		||||
 | 
			
		||||
	auto user = userDao->find(username);
 | 
			
		||||
	if(user)
 | 
			
		||||
	{
 | 
			
		||||
		Random r;
 | 
			
		||||
		Authenticator auth{*userDao};
 | 
			
		||||
		user->salt = r.getRandom(AUTH_DEFAULT_SALT_SIZE);
 | 
			
		||||
		user->password = auth.hash(password, user->salt);
 | 
			
		||||
		if(user->password.empty())
 | 
			
		||||
		{
 | 
			
		||||
			return {false, "Error during hashing - Got empty hash"};
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		userDao->save(*user);
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		return {false, "User not found"};
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::pair<bool, std::string> CLIHandler::user_rename(const std::vector<std::string> &args)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	return {true, ""};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::pair<bool, std::string> CLIHandler::user_set_perms(const std::vector<std::string> &args)
 | 
			
		||||
{
 | 
			
		||||
	auto userDao = this->db->createUserDao();
 | 
			
		||||
	std::string username = args.at(0);
 | 
			
		||||
	std::string permission_string = args.at(1);
 | 
			
		||||
 | 
			
		||||
	Permissions perms{permission_string};
 | 
			
		||||
 | 
			
		||||
	auto user = userDao->find(username);
 | 
			
		||||
	if(user)
 | 
			
		||||
	{
 | 
			
		||||
		user->permissions = perms;
 | 
			
		||||
		userDao->save(*user);
 | 
			
		||||
		user_show({username});
 | 
			
		||||
		return {true, ""};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return {false, "User not found"};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::pair<bool, std::string> CLIHandler::user_list(const std::vector<std::string> &args)
 | 
			
		||||
{
 | 
			
		||||
	auto userDao = this->db->createUserDao();
 | 
			
		||||
	QueryOption o;
 | 
			
		||||
	auto result = userDao->list(o);
 | 
			
		||||
	std::stringstream stream;
 | 
			
		||||
	for(User &u : result)
 | 
			
		||||
	{
 | 
			
		||||
		stream << u.login << "\t" << std::string(u.enabled ? "enabled" : "disabled") << "\t" << u.permissions.toString()
 | 
			
		||||
			   << std::endl;
 | 
			
		||||
	}
 | 
			
		||||
	return {true, stream.str()};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::pair<bool, std::string> CLIHandler::user_show(const std::vector<std::string> &args)
 | 
			
		||||
{
 | 
			
		||||
	std::string username = args.at(0);
 | 
			
		||||
	auto userDao = this->db->createUserDao();
 | 
			
		||||
	auto user = userDao->find(username);
 | 
			
		||||
	std::stringstream stream;
 | 
			
		||||
	if(user)
 | 
			
		||||
	{
 | 
			
		||||
		stream << "Username: " << user->login << std::endl;
 | 
			
		||||
 | 
			
		||||
		stream << "Enabled: " << std::string(user->enabled ? "yes" : "no") << std::endl;
 | 
			
		||||
		stream << "Permissions (general): " << user->permissions.toString() << std::endl;
 | 
			
		||||
		return {true, stream.str()};
 | 
			
		||||
	}
 | 
			
		||||
	return {false, "User not found"};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::pair<bool, std::string> CLIHandler::attach(const std::vector<std::string> &args)
 | 
			
		||||
{
 | 
			
		||||
	/* TODO: consider authentication */
 | 
			
		||||
	pid_t pid = getpid();
 | 
			
		||||
	return {true, "Hi, I am pid: " + std::to_string(pid)};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::pair<bool, std::string> CLIHandler::cli_help(const std::vector<std::string> &args)
 | 
			
		||||
{
 | 
			
		||||
	std::string command;
 | 
			
		||||
	if(args.size() > 0)
 | 
			
		||||
		command = args[0];
 | 
			
		||||
	std::stringstream stream;
 | 
			
		||||
	for(struct cmd &cmd : cmds)
 | 
			
		||||
	{
 | 
			
		||||
		if(command != "" && cmd.name != command)
 | 
			
		||||
		{
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		stream << cmd.name << " - " << cmd.helptext << std::endl;
 | 
			
		||||
		for(struct cmd &subCmd : cmd.subCommands)
 | 
			
		||||
		{
 | 
			
		||||
			stream << "\t" << subCmd.name << " " << subCmd.helptext << std::endl;
 | 
			
		||||
		}
 | 
			
		||||
		stream << std::endl;
 | 
			
		||||
	}
 | 
			
		||||
	return {true, stream.str()};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::pair<bool, std::string> CLIHandler::processCommand(const std::vector<CLIHandler::cmd> &commands, std::string cmd,
 | 
			
		||||
														const std::vector<std::string> &args)
 | 
			
		||||
{
 | 
			
		||||
	auto c = std::find_if(commands.begin(), commands.end(),
 | 
			
		||||
						  [&cmd](const struct CLIHandler::cmd &a) { return a.name == cmd; });
 | 
			
		||||
	if(c == commands.end())
 | 
			
		||||
	{
 | 
			
		||||
		std::cout << "No such command: " << cmd << std::endl;
 | 
			
		||||
		return cli_help({});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if(!c->subCommands.empty() && args.size() >= c->required_args)
 | 
			
		||||
	{
 | 
			
		||||
		std::string newcmd = args[0];
 | 
			
		||||
		std::vector<std::string> newargs = args;
 | 
			
		||||
		newargs.erase(newargs.begin());
 | 
			
		||||
		return processCommand(c->subCommands, newcmd, newargs);
 | 
			
		||||
	}
 | 
			
		||||
	if(args.size() < c->required_args)
 | 
			
		||||
	{
 | 
			
		||||
		return {false, "not enough parameters passed"};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	try
 | 
			
		||||
	{
 | 
			
		||||
		return c->func(this, args);
 | 
			
		||||
	}
 | 
			
		||||
	catch(std::runtime_error &e)
 | 
			
		||||
	{
 | 
			
		||||
		return {false, "Exception: " + std::string(e.what())};
 | 
			
		||||
	}
 | 
			
		||||
	return {false, ""};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::pair<bool, std::string> CLIHandler::processCommand(std::string cmd, const std::vector<std::string> &args)
 | 
			
		||||
{
 | 
			
		||||
	return processCommand(this->cmds, cmd, args);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::pair<std::string, std::vector<std::string>> CLIHandler::splitCommand(std::string input)
 | 
			
		||||
{
 | 
			
		||||
	input = utils::trim(input);
 | 
			
		||||
	std::vector<std::string> splitted = utils::split(input, "\\s+");
 | 
			
		||||
	if(splitted.empty())
 | 
			
		||||
	{
 | 
			
		||||
		return {" ", splitted};
 | 
			
		||||
	}
 | 
			
		||||
	std::string cmd = splitted[0];
 | 
			
		||||
	splitted.erase(splitted.begin());
 | 
			
		||||
	return {cmd, splitted};
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										66
									
								
								cli.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								cli.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,66 @@
 | 
			
		||||
#ifndef CLI_H
 | 
			
		||||
#define CLI_H
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include "database/database.h"
 | 
			
		||||
#include "config.h"
 | 
			
		||||
 | 
			
		||||
class CLIHandler
 | 
			
		||||
{
 | 
			
		||||
	struct cmd
 | 
			
		||||
	{
 | 
			
		||||
		std::string name;
 | 
			
		||||
		std::string helptext;
 | 
			
		||||
		unsigned int required_args;
 | 
			
		||||
		std::vector<cmd> subCommands;
 | 
			
		||||
		std::function<std::pair<bool, std::string>(CLIHandler *, const std::vector<std::string> &)> func;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
  private:
 | 
			
		||||
	Database *db;
 | 
			
		||||
	Config *conf;
 | 
			
		||||
 | 
			
		||||
  protected:
 | 
			
		||||
	std::pair<bool, std::string> attach(const std::vector<std::string> &args);
 | 
			
		||||
	std::pair<bool, std::string> cli_help(const std::vector<std::string> &args);
 | 
			
		||||
	std::pair<bool, std::string> user_add(const std::vector<std::string> &args);
 | 
			
		||||
	std::pair<bool, std::string> user_change_pw(const std::vector<std::string> &args);
 | 
			
		||||
	std::pair<bool, std::string> user_rename(const std::vector<std::string> &args);
 | 
			
		||||
	std::pair<bool, std::string> user_set_perms(const std::vector<std::string> &args);
 | 
			
		||||
	std::pair<bool, std::string> user_list(const std::vector<std::string> &args);
 | 
			
		||||
	std::pair<bool, std::string> user_show(const std::vector<std::string> &args);
 | 
			
		||||
 | 
			
		||||
	std::vector<struct cmd> cmds{
 | 
			
		||||
		{{"user",
 | 
			
		||||
		  "user operations on the database",
 | 
			
		||||
		  1,
 | 
			
		||||
		  {{{"add", "[user] [password] - creates a user", 2, {}, &CLIHandler::user_add},
 | 
			
		||||
			{"changepw", "[user] [password] - changes the password of user", 2, {}, &CLIHandler::user_change_pw},
 | 
			
		||||
			{"rename", "[user] [new name] - renames a user", 2, {}, &CLIHandler::user_rename},
 | 
			
		||||
			{"setperms", "[user] [perms] - sets the permissions of the user", 2, {}, &CLIHandler::user_set_perms},
 | 
			
		||||
			{"list", "- lists users", 0, {}, &CLIHandler::user_list},
 | 
			
		||||
			{"show", "[user] - show detailed information about user", 1, {}, &CLIHandler::user_show}}},
 | 
			
		||||
		  &CLIHandler::cli_help},
 | 
			
		||||
		 {"exit",
 | 
			
		||||
		  "exit cli",
 | 
			
		||||
		  0,
 | 
			
		||||
		  {},
 | 
			
		||||
		  [](CLIHandler *, const std::vector<std::string> &args) -> std::pair<bool, std::string>
 | 
			
		||||
		  {
 | 
			
		||||
			  exit(EXIT_SUCCESS);
 | 
			
		||||
			  return {true, ""};
 | 
			
		||||
		  }},
 | 
			
		||||
		 {"help", "print this help", 0, {}, &CLIHandler::cli_help},
 | 
			
		||||
		 {"attach", "attach to running instance", 0, {}, &CLIHandler::attach}}};
 | 
			
		||||
 | 
			
		||||
	std::pair<bool, std::string> processCommand(const std::vector<CLIHandler::cmd> &commands, std::string cmd,
 | 
			
		||||
												const std::vector<std::string> &args);
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
	CLIHandler(Config &config, Database &d);
 | 
			
		||||
	std::pair<bool, std::string> processCommand(std::string cmd, const std::vector<std::string> &args);
 | 
			
		||||
	static std::pair<std::string, std::vector<std::string>> splitCommand(std::string input);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // CLI_H
 | 
			
		||||
							
								
								
									
										137
									
								
								cliconsole.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								cliconsole.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,137 @@
 | 
			
		||||
#include "cliconsole.h"
 | 
			
		||||
 | 
			
		||||
CLIConsole::CLIConsole(CLIHandler &cliHandler, std::string socketPath)
 | 
			
		||||
{
 | 
			
		||||
	this->handler = &cliHandler;
 | 
			
		||||
	this->socketPath = socketPath;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::pair<bool, std::string> CLIConsole::send(std::string input)
 | 
			
		||||
{
 | 
			
		||||
	ssize_t ret =
 | 
			
		||||
		sendto(this->sock, input.c_str(), input.size(), 0, (const sockaddr *)&this->server, sizeof(this->server));
 | 
			
		||||
	if((size_t)ret != input.size())
 | 
			
		||||
	{
 | 
			
		||||
		return {false, "sendto failed: " + std::to_string(ret) + " " + std::string(strerror(errno))};
 | 
			
		||||
	}
 | 
			
		||||
	char buffer[1024] = {0};
 | 
			
		||||
	ret = recvfrom(this->sock, buffer, sizeof(buffer) - 1, 0, NULL, NULL);
 | 
			
		||||
	if(ret == -1)
 | 
			
		||||
	{
 | 
			
		||||
		return {false, "recvfrom failed: " + std::string(strerror(errno))};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool success = false;
 | 
			
		||||
	std::string_view view = buffer;
 | 
			
		||||
	if(view[0] == '1')
 | 
			
		||||
	{
 | 
			
		||||
		success = true;
 | 
			
		||||
	}
 | 
			
		||||
	view.remove_prefix(1);
 | 
			
		||||
	std::string msg = std::string{view};
 | 
			
		||||
 | 
			
		||||
	return {success, msg};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CLIConsole::attach()
 | 
			
		||||
{
 | 
			
		||||
	if(attached)
 | 
			
		||||
	{
 | 
			
		||||
		std::cout << "Already attached" << std::endl;
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	if(socketPath.size() > sizeof(this->server.sun_path) - 1)
 | 
			
		||||
	{
 | 
			
		||||
		std::cout << "Socket path too long" << std::endl;
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	memset(&this->server, 0, sizeof(this->server));
 | 
			
		||||
	this->server.sun_family = AF_UNIX;
 | 
			
		||||
	memcpy(&this->server.sun_path, socketPath.c_str(), socketPath.size());
 | 
			
		||||
	this->server.sun_path[socketPath.size()] = 0;
 | 
			
		||||
 | 
			
		||||
	int s = socket(AF_UNIX, SOCK_DGRAM, 0);
 | 
			
		||||
	if(s == -1)
 | 
			
		||||
	{
 | 
			
		||||
		std::cout << "Failed to create socket" << strerror(errno) << std::endl;
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	this->sock = s;
 | 
			
		||||
 | 
			
		||||
	struct sockaddr_un client;
 | 
			
		||||
	client.sun_family = AF_UNIX;
 | 
			
		||||
	client.sun_path[0] = '\0';
 | 
			
		||||
 | 
			
		||||
	int ret = bind(this->sock, (struct sockaddr *)&client, sizeof(client));
 | 
			
		||||
	if(ret != 0)
 | 
			
		||||
	{
 | 
			
		||||
		std::cout << "bind() failed: " << strerror(errno) << std::endl;
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	auto result = this->send("attach");
 | 
			
		||||
	if(result.first)
 | 
			
		||||
	{
 | 
			
		||||
		std::cout << "Attached successfully: " << result.second << std::endl;
 | 
			
		||||
		this->attached = true;
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		std::cout << "Attached unsuccessfully: " << result.second << std::endl;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CLIConsole::startInteractive()
 | 
			
		||||
{
 | 
			
		||||
	std::cout << "qswiki CLI" << std::endl;
 | 
			
		||||
	std::cout << "not attached - use 'attach' to connect to running instance" << std::endl;
 | 
			
		||||
 | 
			
		||||
	while(true)
 | 
			
		||||
	{
 | 
			
		||||
		std::string input;
 | 
			
		||||
		std::cout << "> ";
 | 
			
		||||
		std::getline(std::cin, input);
 | 
			
		||||
 | 
			
		||||
		if(std::cin.bad() || std::cin.eof())
 | 
			
		||||
		{
 | 
			
		||||
			std::cout << "Exiting" << std::endl;
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		if(input.empty())
 | 
			
		||||
		{
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		auto pair = CLIHandler::splitCommand(input);
 | 
			
		||||
		if(pair.first == "exit")
 | 
			
		||||
		{
 | 
			
		||||
			std::cout << "Exiting CLI";
 | 
			
		||||
			exit(EXIT_SUCCESS);
 | 
			
		||||
		}
 | 
			
		||||
		if(pair.first == "attach")
 | 
			
		||||
		{
 | 
			
		||||
			attach();
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		std::pair<bool, std::string> result;
 | 
			
		||||
		if(!attached)
 | 
			
		||||
		{
 | 
			
		||||
			result = handler->processCommand(pair.first, pair.second);
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			result = this->send(input);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if(!result.second.empty())
 | 
			
		||||
		{
 | 
			
		||||
			std::cout << result.second << std::endl;
 | 
			
		||||
		}
 | 
			
		||||
		if(!result.first)
 | 
			
		||||
		{
 | 
			
		||||
			std::cout << "Command failed" << std::endl;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	std::cout << "\n";
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								cliconsole.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								cliconsole.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
#ifndef CLICONSOLE_H
 | 
			
		||||
#define CLICONSOLE_H
 | 
			
		||||
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <sys/un.h>
 | 
			
		||||
#include "cli.h"
 | 
			
		||||
 | 
			
		||||
class CLIConsole
 | 
			
		||||
{
 | 
			
		||||
  private:
 | 
			
		||||
	struct sockaddr_un server;
 | 
			
		||||
	int sock;
 | 
			
		||||
	CLIHandler *handler;
 | 
			
		||||
	std::string socketPath;
 | 
			
		||||
	bool attached = false;
 | 
			
		||||
	std::pair<bool, std::string> send(std::string input);
 | 
			
		||||
	void attach();
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
	CLIConsole(CLIHandler &cliHandler, std::string socketPath);
 | 
			
		||||
	void startInteractive();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // CLICONSOLE_H
 | 
			
		||||
							
								
								
									
										77
									
								
								cliserver.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								cliserver.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,77 @@
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <sys/un.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include "cliserver.h"
 | 
			
		||||
#include "logger.h"
 | 
			
		||||
CLIServer::CLIServer(CLIHandler &handler)
 | 
			
		||||
{
 | 
			
		||||
	this->handler = &handler;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool CLIServer::detachServer(std::string socketpath)
 | 
			
		||||
{
 | 
			
		||||
	struct sockaddr_un name;
 | 
			
		||||
	const int max_socket_length = sizeof(name.sun_path) - 1;
 | 
			
		||||
	if(socketpath.size() > max_socket_length)
 | 
			
		||||
	{
 | 
			
		||||
		perror("socket path too long");
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	int s = socket(AF_UNIX, SOCK_DGRAM, 0);
 | 
			
		||||
	if(s == -1)
 | 
			
		||||
	{
 | 
			
		||||
		perror("socket");
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	memset(&name, 0, sizeof(name));
 | 
			
		||||
	name.sun_family = AF_UNIX;
 | 
			
		||||
	memcpy(&name.sun_path, socketpath.c_str(), socketpath.size());
 | 
			
		||||
 | 
			
		||||
	unlink(socketpath.c_str());
 | 
			
		||||
	int ret = bind(s, (const struct sockaddr *)&name, sizeof(name));
 | 
			
		||||
	if(ret == -1)
 | 
			
		||||
	{
 | 
			
		||||
		perror("bind");
 | 
			
		||||
		exit(EXIT_FAILURE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	auto worker = [=]
 | 
			
		||||
	{
 | 
			
		||||
		while(true)
 | 
			
		||||
		{
 | 
			
		||||
			char buffer[1024] = {0};
 | 
			
		||||
			struct sockaddr_un peer;
 | 
			
		||||
			socklen_t peerlen = sizeof(peer);
 | 
			
		||||
 | 
			
		||||
			int ret = recvfrom(s, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &peerlen);
 | 
			
		||||
			if(ret == -1)
 | 
			
		||||
			{
 | 
			
		||||
				Logger::error() << "Error during recvfrom in CLI server: " << strerror(errno);
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			std::string input{buffer};
 | 
			
		||||
 | 
			
		||||
			auto pair = CLIHandler::splitCommand(input);
 | 
			
		||||
 | 
			
		||||
			auto result = handler->processCommand(pair.first, pair.second);
 | 
			
		||||
			char resultCode = '0';
 | 
			
		||||
			if(result.first)
 | 
			
		||||
			{
 | 
			
		||||
				resultCode = '1';
 | 
			
		||||
			}
 | 
			
		||||
			std::string resultString;
 | 
			
		||||
			resultString += resultCode;
 | 
			
		||||
			resultString += result.second;
 | 
			
		||||
			ret = sendto(s, resultString.c_str(), resultString.size(), 0, (struct sockaddr *)&peer, peerlen);
 | 
			
		||||
			if(ret == -1)
 | 
			
		||||
			{
 | 
			
		||||
				Logger::error() << "Error during sendto in CLI server: " << strerror(errno);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
	std::thread t1{worker};
 | 
			
		||||
	t1.detach();
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								cliserver.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								cliserver.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
#ifndef CLISERVER_H
 | 
			
		||||
#define CLISERVER_H
 | 
			
		||||
#include <thread>
 | 
			
		||||
#include "cli.h"
 | 
			
		||||
 | 
			
		||||
class CLIServer
 | 
			
		||||
{
 | 
			
		||||
  private:
 | 
			
		||||
	CLIHandler *handler = nullptr;
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
	CLIServer(CLIHandler &handler);
 | 
			
		||||
	bool detachServer(std::string socketpath);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // CLISERVER_H
 | 
			
		||||
							
								
								
									
										61
									
								
								qswiki.cpp
									
									
									
									
									
								
							
							
						
						
									
										61
									
								
								qswiki.cpp
									
									
									
									
									
								
							@@ -25,6 +25,7 @@ SOFTWARE.
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <filesystem>
 | 
			
		||||
#include <getopt.h>
 | 
			
		||||
#include "gateway/gatewayinterface.h"
 | 
			
		||||
#include "gateway/gatewayfactory.h"
 | 
			
		||||
#include "handlers/handlerfactory.h"
 | 
			
		||||
@@ -37,6 +38,10 @@ SOFTWARE.
 | 
			
		||||
#include "requestworker.h"
 | 
			
		||||
#include "cache/fscache.h"
 | 
			
		||||
#include "sandbox/sandboxfactory.h"
 | 
			
		||||
#include "cli.h"
 | 
			
		||||
#include "cliconsole.h"
 | 
			
		||||
#include "cliserver.h"
 | 
			
		||||
 | 
			
		||||
void sigterm_handler(int arg)
 | 
			
		||||
{
 | 
			
		||||
	// TODO: proper shutdown.
 | 
			
		||||
@@ -56,6 +61,10 @@ void setup_signal_handlers()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define OPT_PRINT_VERSION 23
 | 
			
		||||
 | 
			
		||||
static struct option long_options[] = {{"cli", no_argument, 0, 'c'}, {"version", no_argument, 0, OPT_PRINT_VERSION}};
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<ICache> createCache(const ConfigVariableResolver &resolver)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@@ -63,13 +72,48 @@ std::unique_ptr<ICache> createCache(const ConfigVariableResolver &resolver)
 | 
			
		||||
 | 
			
		||||
	return std::make_unique<FsCache>(path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string get_version_string()
 | 
			
		||||
{
 | 
			
		||||
	return "master";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	char *configfilepath = NULL;
 | 
			
		||||
	int option;
 | 
			
		||||
	int option_index;
 | 
			
		||||
	bool cli_mode = false;
 | 
			
		||||
 | 
			
		||||
	if(geteuid() == 0)
 | 
			
		||||
	{
 | 
			
		||||
		std::cerr << "Do not run this as root!" << std::endl;
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	while((option = getopt_long(argc, argv, "cv", long_options, &option_index)) != -1)
 | 
			
		||||
	{
 | 
			
		||||
		switch(option)
 | 
			
		||||
		{
 | 
			
		||||
		case 'c':
 | 
			
		||||
			cli_mode = true;
 | 
			
		||||
			break;
 | 
			
		||||
		case OPT_PRINT_VERSION:
 | 
			
		||||
			std::cout << get_version_string() << std::endl;
 | 
			
		||||
			exit(EXIT_SUCCESS);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if(optind == argc)
 | 
			
		||||
	{
 | 
			
		||||
		std::cerr << "Missing config path" << std::endl;
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	configfilepath = argv[optind++];
 | 
			
		||||
 | 
			
		||||
	auto sandbox = createSandbox();
 | 
			
		||||
	// TODO: do we want to keep it mandatory or configurable?
 | 
			
		||||
	if(!sandbox->supported())
 | 
			
		||||
@@ -82,7 +126,7 @@ int main(int argc, char **argv)
 | 
			
		||||
		std::cerr << "no path to config file provided" << std::endl;
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
	std::string configpath = std::filesystem::absolute(argv[1]).string();
 | 
			
		||||
	std::string configpath = std::filesystem::absolute(configfilepath).string();
 | 
			
		||||
	if(!sandbox->enableForInit())
 | 
			
		||||
	{
 | 
			
		||||
		Logger::error() << "Sandboxing for init mode could not be activated.";
 | 
			
		||||
@@ -113,6 +157,21 @@ int main(int argc, char **argv)
 | 
			
		||||
		Logger::setStream(&logstream);
 | 
			
		||||
 | 
			
		||||
		auto database = createDatabase(config);
 | 
			
		||||
		std::string socketPath = config.configVarResolver.getConfig("socketpath");
 | 
			
		||||
		CLIHandler cliHandler(config, *database);
 | 
			
		||||
 | 
			
		||||
		if(cli_mode)
 | 
			
		||||
		{
 | 
			
		||||
			CLIConsole console{cliHandler, socketPath};
 | 
			
		||||
			console.startInteractive();
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
		CLIServer cliServer{cliHandler};
 | 
			
		||||
		if(!cliServer.detachServer(socketPath))
 | 
			
		||||
		{
 | 
			
		||||
			Logger::error() << "Error: Failed to detach unix socket server";
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// TODO: quite ugly, anon-handling must be rethought
 | 
			
		||||
		auto userdao = database->createUserDao();
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user