mirror of
https://github.com/Qsgs-Fans/FreeKill.git
synced 2024-11-15 19:22:25 +08:00
Shell enhance (#359)
- 修复了Log淹没正在输入命令的bug - 增加Tab补全:命令补全、install链接推荐、用户名补全、拓展包名补全 - 为Windows端增加基于cstdio的getline函数的丐版shell
This commit is contained in:
parent
bdfcf805e4
commit
79ede70b6b
|
@ -16,6 +16,7 @@ set(freekill_SRCS
|
||||||
"server/room.cpp"
|
"server/room.cpp"
|
||||||
"server/roomthread.cpp"
|
"server/roomthread.cpp"
|
||||||
"server/scheduler.cpp"
|
"server/scheduler.cpp"
|
||||||
|
"server/shell.cpp"
|
||||||
"ui/qmlbackend.cpp"
|
"ui/qmlbackend.cpp"
|
||||||
"swig/freekill-wrap.cxx"
|
"swig/freekill-wrap.cxx"
|
||||||
)
|
)
|
||||||
|
@ -67,7 +68,6 @@ else ()
|
||||||
set(SQLITE3_LIB sqlite3)
|
set(SQLITE3_LIB sqlite3)
|
||||||
set(CRYPTO_LIB OpenSSL::Crypto)
|
set(CRYPTO_LIB OpenSSL::Crypto)
|
||||||
set(READLINE_LIB readline)
|
set(READLINE_LIB readline)
|
||||||
list(APPEND freekill_SRCS "server/shell.cpp")
|
|
||||||
set(GIT_LIB git2)
|
set(GIT_LIB git2)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
|
|
@ -120,9 +120,12 @@ static int callback(void *jsonDoc, int argc, char **argv, char **cols) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonArray SelectFromDatabase(sqlite3 *db, const QString &sql) {
|
QJsonArray SelectFromDatabase(sqlite3 *db, const QString &sql) {
|
||||||
|
static QMutex select_lock;
|
||||||
QJsonArray arr;
|
QJsonArray arr;
|
||||||
auto bytes = sql.toUtf8();
|
auto bytes = sql.toUtf8();
|
||||||
|
select_lock.lock();
|
||||||
sqlite3_exec(db, bytes.data(), callback, (void *)&arr, nullptr);
|
sqlite3_exec(db, bytes.data(), callback, (void *)&arr, nullptr);
|
||||||
|
select_lock.unlock();
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
31
src/main.cpp
31
src/main.cpp
|
@ -6,10 +6,7 @@ using namespace fkShell;
|
||||||
|
|
||||||
#include "core/packman.h"
|
#include "core/packman.h"
|
||||||
#include "server/server.h"
|
#include "server/server.h"
|
||||||
|
|
||||||
#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
|
|
||||||
#include "server/shell.h"
|
#include "server/shell.h"
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(Q_OS_WIN32)
|
#if defined(Q_OS_WIN32)
|
||||||
#include "applink.c"
|
#include "applink.c"
|
||||||
|
@ -113,8 +110,13 @@ void fkMsgHandler(QtMsgType type, const QMessageLogContext &context,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "%02d/%02d ", date.month(), date.day());
|
#ifdef FK_USE_READLINE
|
||||||
fprintf(stderr, "%s ",
|
ShellInstance->clearLine();
|
||||||
|
#else
|
||||||
|
printf("\r");
|
||||||
|
#endif
|
||||||
|
printf("%02d/%02d ", date.month(), date.day());
|
||||||
|
printf("%s ",
|
||||||
QTime::currentTime().toString("hh:mm:ss").toLatin1().constData());
|
QTime::currentTime().toString("hh:mm:ss").toLatin1().constData());
|
||||||
fprintf(file, "%02d/%02d ", date.month(), date.day());
|
fprintf(file, "%02d/%02d ", date.month(), date.day());
|
||||||
fprintf(file, "%s ",
|
fprintf(file, "%s ",
|
||||||
|
@ -125,26 +127,26 @@ void fkMsgHandler(QtMsgType type, const QMessageLogContext &context,
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case QtDebugMsg:
|
case QtDebugMsg:
|
||||||
fprintf(stderr, "%s[D] %s\n", threadName.constData(),
|
printf("%s[D] %s\n", threadName.constData(),
|
||||||
localMsg.constData());
|
localMsg.constData());
|
||||||
fprintf(file, "%s[D] %s\n", threadName.constData(),
|
fprintf(file, "%s[D] %s\n", threadName.constData(),
|
||||||
localMsg.constData());
|
localMsg.constData());
|
||||||
break;
|
break;
|
||||||
case QtInfoMsg:
|
case QtInfoMsg:
|
||||||
fprintf(stderr, "%s[%s] %s\n", threadName.constData(),
|
printf("%s[%s] %s\n", threadName.constData(),
|
||||||
Color("I", Green).toUtf8().constData(), localMsg.constData());
|
Color("I", Green).toUtf8().constData(), localMsg.constData());
|
||||||
fprintf(file, "%s[%s] %s\n", threadName.constData(),
|
fprintf(file, "%s[%s] %s\n", threadName.constData(),
|
||||||
"I", localMsg.constData());
|
"I", localMsg.constData());
|
||||||
break;
|
break;
|
||||||
case QtWarningMsg:
|
case QtWarningMsg:
|
||||||
fprintf(stderr, "%s[%s] %s\n", threadName.constData(),
|
printf("%s[%s] %s\n", threadName.constData(),
|
||||||
Color("W", Yellow, Bold).toUtf8().constData(),
|
Color("W", Yellow, Bold).toUtf8().constData(),
|
||||||
localMsg.constData());
|
localMsg.constData());
|
||||||
fprintf(file, "%s[%s] %s\n", threadName.constData(),
|
fprintf(file, "%s[%s] %s\n", threadName.constData(),
|
||||||
"W", localMsg.constData());
|
"W", localMsg.constData());
|
||||||
break;
|
break;
|
||||||
case QtCriticalMsg:
|
case QtCriticalMsg:
|
||||||
fprintf(stderr, "%s[%s] %s\n", threadName.constData(),
|
printf("%s[%s] %s\n", threadName.constData(),
|
||||||
Color("C", Red, Bold).toUtf8().constData(), localMsg.constData());
|
Color("C", Red, Bold).toUtf8().constData(), localMsg.constData());
|
||||||
fprintf(file, "%s[%s] %s\n", threadName.constData(),
|
fprintf(file, "%s[%s] %s\n", threadName.constData(),
|
||||||
"C", localMsg.constData());
|
"C", localMsg.constData());
|
||||||
|
@ -156,12 +158,18 @@ void fkMsgHandler(QtMsgType type, const QMessageLogContext &context,
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
case QtFatalMsg:
|
case QtFatalMsg:
|
||||||
fprintf(stderr, "%s[%s] %s\n", threadName.constData(),
|
printf("%s[%s] %s\n", threadName.constData(),
|
||||||
Color("E", Red, Bold).toUtf8().constData(), localMsg.constData());
|
Color("E", Red, Bold).toUtf8().constData(), localMsg.constData());
|
||||||
fprintf(file, "%s[%s] %s\n", threadName.constData(),
|
fprintf(file, "%s[%s] %s\n", threadName.constData(),
|
||||||
"E", localMsg.constData());
|
"E", localMsg.constData());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef FK_USE_READLINE
|
||||||
|
if (ShellInstance && !ShellInstance->lineDone()) {
|
||||||
|
ShellInstance->redisplay();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// FreeKill 的程序主入口。整个程序就是从这里开始执行的。
|
// FreeKill 的程序主入口。整个程序就是从这里开始执行的。
|
||||||
|
@ -230,11 +238,8 @@ int main(int argc, char *argv[]) {
|
||||||
app->exit(1);
|
app->exit(1);
|
||||||
} else {
|
} else {
|
||||||
qInfo("Server is listening on port %d", serverPort);
|
qInfo("Server is listening on port %d", serverPort);
|
||||||
#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
|
|
||||||
// Linux 服务器的话可以启用一个 Shell 来操作服务器。
|
|
||||||
auto shell = new Shell;
|
auto shell = new Shell;
|
||||||
shell->start();
|
shell->start();
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
return app->exec();
|
return app->exec();
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,10 @@ typedef int LuaFunction;
|
||||||
#define DESKTOP_BUILD
|
#define DESKTOP_BUILD
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined (Q_OS_LINUX) && !defined (Q_OS_ANDROID)
|
||||||
|
#define FK_USE_READLINE
|
||||||
|
#endif
|
||||||
|
|
||||||
// You may define FK_SERVER_ONLY with cmake .. -D...
|
// You may define FK_SERVER_ONLY with cmake .. -D...
|
||||||
#ifndef FK_SERVER_ONLY
|
#ifndef FK_SERVER_ONLY
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
|
|
@ -1,23 +1,24 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
|
|
||||||
#include "server/shell.h"
|
#include "server/shell.h"
|
||||||
#include "core/packman.h"
|
#include "core/packman.h"
|
||||||
#include "server/server.h"
|
#include "server/server.h"
|
||||||
#include "server/serverplayer.h"
|
#include "server/serverplayer.h"
|
||||||
#include "core/util.h"
|
#include "core/util.h"
|
||||||
|
#ifdef FK_USE_READLINE
|
||||||
#include <readline/history.h>
|
#include <readline/history.h>
|
||||||
#include <readline/readline.h>
|
#include <readline/readline.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#else
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
#endif
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
|
|
||||||
static void sigintHandler(int) {
|
Shell *ShellInstance = nullptr;
|
||||||
fprintf(stderr, "\n");
|
static const char *prompt = "Fk> ";
|
||||||
rl_reset_line_state();
|
|
||||||
rl_replace_line("", 0);
|
|
||||||
rl_crlf();
|
|
||||||
rl_redisplay();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shell::helpCommand(QStringList &) {
|
void Shell::helpCommand(QStringList &) {
|
||||||
qInfo("Frequently used commands:");
|
qInfo("Frequently used commands:");
|
||||||
|
@ -55,7 +56,7 @@ void Shell::helpCommand(QStringList &) {
|
||||||
qInfo("===== Package commands =====");
|
qInfo("===== Package commands =====");
|
||||||
HELP_MSG("%s: Install a new package from <url>.", "install");
|
HELP_MSG("%s: Install a new package from <url>.", "install");
|
||||||
HELP_MSG("%s: Remove a package.", "remove");
|
HELP_MSG("%s: Remove a package.", "remove");
|
||||||
HELP_MSG("%s: List all packages.", "lspkg");
|
HELP_MSG("%s: List all packages.", "pkgs");
|
||||||
HELP_MSG("%s: Enable a package.", "enable");
|
HELP_MSG("%s: Enable a package.", "enable");
|
||||||
HELP_MSG("%s: Disable a package.", "disable");
|
HELP_MSG("%s: Disable a package.", "disable");
|
||||||
HELP_MSG("%s: Upgrade a package. Leave empty to upgrade all.", "upgrade/u");
|
HELP_MSG("%s: Upgrade a package. Leave empty to upgrade all.", "upgrade/u");
|
||||||
|
@ -380,9 +381,32 @@ void Shell::resetPasswordCommand(QStringList &list) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef FK_USE_READLINE
|
||||||
|
static void sigintHandler(int) {
|
||||||
|
rl_reset_line_state();
|
||||||
|
rl_replace_line("", 0);
|
||||||
|
rl_crlf();
|
||||||
|
rl_forced_update_display();
|
||||||
|
}
|
||||||
|
static char **fk_completion(const char *text, int start, int end);
|
||||||
|
static char *null_completion(const char *, int) { return NULL; }
|
||||||
|
#endif
|
||||||
|
|
||||||
Shell::Shell() {
|
Shell::Shell() {
|
||||||
|
ShellInstance = this;
|
||||||
setObjectName("Shell");
|
setObjectName("Shell");
|
||||||
|
#ifdef FK_USE_READLINE
|
||||||
|
// Setup readline here
|
||||||
|
|
||||||
|
// 别管Ctrl+C了
|
||||||
|
//rl_catch_signals = 1;
|
||||||
|
//rl_catch_sigwinch = 1;
|
||||||
|
//rl_persistent_signal_handlers = 1;
|
||||||
|
//rl_set_signals();
|
||||||
signal(SIGINT, sigintHandler);
|
signal(SIGINT, sigintHandler);
|
||||||
|
rl_attempted_completion_function = fk_completion;
|
||||||
|
rl_completion_entry_function = null_completion;
|
||||||
|
#endif
|
||||||
|
|
||||||
static const QHash<QString, void (Shell::*)(QStringList &)> handlers = {
|
static const QHash<QString, void (Shell::*)(QStringList &)> handlers = {
|
||||||
{"help", &Shell::helpCommand},
|
{"help", &Shell::helpCommand},
|
||||||
|
@ -393,7 +417,7 @@ Shell::Shell() {
|
||||||
{"remove", &Shell::removeCommand},
|
{"remove", &Shell::removeCommand},
|
||||||
{"upgrade", &Shell::upgradeCommand},
|
{"upgrade", &Shell::upgradeCommand},
|
||||||
{"u", &Shell::upgradeCommand},
|
{"u", &Shell::upgradeCommand},
|
||||||
{"lspkg", &Shell::lspkgCommand},
|
{"pkgs", &Shell::lspkgCommand},
|
||||||
{"enable", &Shell::enableCommand},
|
{"enable", &Shell::enableCommand},
|
||||||
{"disable", &Shell::disableCommand},
|
{"disable", &Shell::disableCommand},
|
||||||
{"kick", &Shell::kickCommand},
|
{"kick", &Shell::kickCommand},
|
||||||
|
@ -409,52 +433,277 @@ Shell::Shell() {
|
||||||
{"r", &Shell::reloadConfCommand},
|
{"r", &Shell::reloadConfCommand},
|
||||||
{"resetpassword", &Shell::resetPasswordCommand},
|
{"resetpassword", &Shell::resetPasswordCommand},
|
||||||
{"rp", &Shell::resetPasswordCommand},
|
{"rp", &Shell::resetPasswordCommand},
|
||||||
|
// special command
|
||||||
|
{"quit", &Shell::helpCommand},
|
||||||
|
{"crash", &Shell::helpCommand},
|
||||||
};
|
};
|
||||||
handler_map = handlers;
|
handler_map = handlers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Shell::handleLine(char *bytes) {
|
||||||
|
if (!bytes || !strncmp(bytes, "quit", 4)) {
|
||||||
|
qInfo("Server is shutting down.");
|
||||||
|
qApp->quit();
|
||||||
|
done = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qInfo("Running command: \"%s\"", bytes);
|
||||||
|
|
||||||
|
if (!strncmp(bytes, "crash", 5)) {
|
||||||
|
qFatal("Crashing."); // should dump core
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef FK_USE_READLINE
|
||||||
|
add_history(bytes);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto command = QString(bytes);
|
||||||
|
auto command_list = command.split(' ');
|
||||||
|
auto func = handler_map[command_list.first()];
|
||||||
|
if (!func) {
|
||||||
|
auto bytes = command_list.first().toUtf8();
|
||||||
|
qWarning("Unknown command \"%s\". Type \"help\" for hints.",
|
||||||
|
bytes.constData());
|
||||||
|
} else {
|
||||||
|
command_list.removeFirst();
|
||||||
|
(this->*func)(command_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef FK_USE_READLINE
|
||||||
|
void Shell::redisplay() {
|
||||||
|
QString tmp = syntaxHighlight(rl_line_buffer);
|
||||||
|
rl_clear_visible_line();
|
||||||
|
rl_forced_update_display();
|
||||||
|
|
||||||
|
//moveCursorToStart();
|
||||||
|
//printf("\r%s%s", prompt, tmp.toUtf8().constData());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shell::moveCursorToStart() {
|
||||||
|
winsize sz;
|
||||||
|
ioctl(STDOUT_FILENO, TIOCGWINSZ, &sz);
|
||||||
|
int lines = (rl_end + strlen(prompt) - 1) / sz.ws_col;
|
||||||
|
printf("\e[%d;%dH", sz.ws_row - lines, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shell::clearLine() {
|
||||||
|
rl_clear_visible_line();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Shell::lineDone() const {
|
||||||
|
return (bool)rl_done;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最简单的语法高亮,若命令可执行就涂绿,否则涂红
|
||||||
|
QString Shell::syntaxHighlight(char *bytes) {
|
||||||
|
QString ret(bytes);
|
||||||
|
auto command = ret.split(' ').first();
|
||||||
|
auto func = handler_map[command];
|
||||||
|
auto colored_command = command;
|
||||||
|
if (!func) {
|
||||||
|
colored_command = Color(command, fkShell::Red, fkShell::Bold);
|
||||||
|
} else {
|
||||||
|
colored_command = Color(command, fkShell::Green);
|
||||||
|
}
|
||||||
|
ret.replace(0, command.length(), colored_command);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void linehandler(char *bytes) {
|
||||||
|
ShellInstance->handleLine(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *Shell::generateCommand(const char *text, int state) {
|
||||||
|
static int list_index, len;
|
||||||
|
static auto keys = handler_map.keys();
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
if (state == 0) {
|
||||||
|
list_index = 0;
|
||||||
|
len = strlen(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (list_index < keys.length()) {
|
||||||
|
name = keys[list_index].toUtf8().constData();
|
||||||
|
++list_index;
|
||||||
|
if (strncmp(name, text, len) == 0) {
|
||||||
|
return strdup(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *command_generator(const char *text, int state) {
|
||||||
|
return ShellInstance->generateCommand(text, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *repo_generator(const char *text, int state) {
|
||||||
|
static QStringList recommend_repos = {
|
||||||
|
"https://gitee.com/Qsgs-Fans/standard_ex",
|
||||||
|
"https://gitee.com/Qsgs-Fans/shzl",
|
||||||
|
"https://gitee.com/Qsgs-Fans/sp",
|
||||||
|
"https://gitee.com/Qsgs-Fans/yj",
|
||||||
|
"https://gitee.com/Qsgs-Fans/ol",
|
||||||
|
"https://gitee.com/Qsgs-Fans/mougong",
|
||||||
|
"https://gitee.com/Qsgs-Fans/mobile",
|
||||||
|
"https://gitee.com/Qsgs-Fans/tenyear",
|
||||||
|
"https://gitee.com/Qsgs-Fans/overseas",
|
||||||
|
"https://gitee.com/Qsgs-Fans/jsrg",
|
||||||
|
"https://gitee.com/Qsgs-Fans/qsgs",
|
||||||
|
"https://gitee.com/Qsgs-Fans/mini",
|
||||||
|
"https://gitee.com/Qsgs-Fans/gamemode",
|
||||||
|
"https://gitee.com/Qsgs-Fans/utility",
|
||||||
|
"https://gitee.com/Qsgs-Fans/freekill-core",
|
||||||
|
"https://gitee.com/Qsgs-Fans/offline",
|
||||||
|
"https://gitee.com/Qsgs-Fans/hegemony",
|
||||||
|
"https://gitee.com/Qsgs-Fans/lunar",
|
||||||
|
};
|
||||||
|
static int list_index, len;
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
if (state == 0) {
|
||||||
|
list_index = 0;
|
||||||
|
len = strlen(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (list_index < recommend_repos.count()) {
|
||||||
|
name = recommend_repos[list_index].toUtf8().constData();
|
||||||
|
++list_index;
|
||||||
|
if (strncmp(name, text, len) == 0) {
|
||||||
|
return strdup(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *package_generator(const char *text, int state) {
|
||||||
|
static QJsonArray arr;
|
||||||
|
static int list_index, len;
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
if (state == 0) {
|
||||||
|
arr = QJsonDocument::fromJson(Pacman->listPackages().toUtf8()).array();
|
||||||
|
list_index = 0;
|
||||||
|
len = strlen(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (list_index < arr.count()) {
|
||||||
|
name = arr[list_index].toObject().value("name").toString().toUtf8().constData();
|
||||||
|
++list_index;
|
||||||
|
if (strncmp(name, text, len) == 0) {
|
||||||
|
return strdup(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *user_generator(const char *text, int state) {
|
||||||
|
// TODO: userinfo表需要一个cache机制
|
||||||
|
static QJsonArray arr;
|
||||||
|
static int list_index, len;
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
if (state == 0) {
|
||||||
|
arr = SelectFromDatabase(ServerInstance->getDatabase(),
|
||||||
|
"SELECT name FROM userinfo;");
|
||||||
|
list_index = 0;
|
||||||
|
len = strlen(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (list_index < arr.count()) {
|
||||||
|
name = arr[list_index].toObject().value("name").toString().toUtf8().constData();
|
||||||
|
++list_index;
|
||||||
|
if (strncmp(name, text, len) == 0) {
|
||||||
|
return strdup(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
static char *banned_user_generator(const char *text, int state) {
|
||||||
|
// TODO: userinfo表需要一个cache机制
|
||||||
|
static QJsonArray arr;
|
||||||
|
static int list_index, len;
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
if (state == 0) {
|
||||||
|
arr = SelectFromDatabase(ServerInstance->getDatabase(),
|
||||||
|
"SELECT name FROM userinfo WHERE banned = 1;");
|
||||||
|
list_index = 0;
|
||||||
|
len = strlen(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (list_index < arr.count()) {
|
||||||
|
name = arr[list_index].toObject().value("name").toString().toUtf8().constData();
|
||||||
|
++list_index;
|
||||||
|
if (strncmp(name, text, len) == 0) {
|
||||||
|
return strdup(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
static char **fk_completion(const char* text, int start, int end) {
|
||||||
|
char **matches = NULL;
|
||||||
|
if (start == 0) {
|
||||||
|
matches = rl_completion_matches(text, command_generator);
|
||||||
|
} else {
|
||||||
|
auto command_list = QString(rl_line_buffer).split(' ');
|
||||||
|
if (command_list.length() > 2) return NULL;
|
||||||
|
auto command = command_list[0];
|
||||||
|
if (command == "install") {
|
||||||
|
matches = rl_completion_matches(text, repo_generator);
|
||||||
|
} else if (command == "remove" || command == "upgrade"
|
||||||
|
|| command == "enable" || command == "disable") {
|
||||||
|
matches = rl_completion_matches(text, package_generator);
|
||||||
|
} else if (command.startsWith("ban") || command == "resetpassword" || command == "rp") {
|
||||||
|
matches = rl_completion_matches(text, user_generator);
|
||||||
|
} else if (command.startsWith("unban")) {
|
||||||
|
matches = rl_completion_matches(text, banned_user_generator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matches;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void Shell::run() {
|
void Shell::run() {
|
||||||
printf("\rFreeKill, Copyright (C) 2022-2023, GNU GPL'd, by Notify et al.\n");
|
printf("\rFreeKill, Copyright (C) 2022-2024, GNU GPL'd, by Notify et al.\n");
|
||||||
printf("This program comes with ABSOLUTELY NO WARRANTY.\n");
|
printf("This program comes with ABSOLUTELY NO WARRANTY.\n");
|
||||||
printf(
|
printf(
|
||||||
"This is free software, and you are welcome to redistribute it under\n");
|
"This is free software, and you are welcome to redistribute it under\n");
|
||||||
printf("certain conditions; For more information visit "
|
printf("certain conditions; For more information visit "
|
||||||
"http://www.gnu.org/licenses.\n\n");
|
"http://www.gnu.org/licenses.\n\n");
|
||||||
|
|
||||||
printf("[v%s] This is server cli. Enter \"help\" for usage hints.\n", FK_VERSION);
|
printf("[v%s] Welcome to CLI. Enter \"help\" for usage hints.\n", FK_VERSION);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
char *bytes = readline("fk> ");
|
#ifdef FK_USE_READLINE
|
||||||
if (!bytes || !strcmp(bytes, "quit")) {
|
char *bytes = readline(prompt);
|
||||||
qInfo("Server is shutting down.");
|
#else
|
||||||
qApp->quit();
|
char *bytes = NULL;
|
||||||
return;
|
size_t bufsize = 512;
|
||||||
}
|
printf("\rfk> ");
|
||||||
|
fflush(stdin);
|
||||||
qInfo("Running command: \"%s\"", bytes);
|
int ret = getline(&bytes, &bufsize, stdin);
|
||||||
|
if (ret == -1 || ret == 0) {
|
||||||
if (!strcmp(bytes, "crash")) {
|
free(bytes);
|
||||||
qFatal("Crashing."); // should dump core
|
bytes = NULL;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*bytes)
|
|
||||||
add_history(bytes);
|
|
||||||
|
|
||||||
auto command = QString(bytes);
|
|
||||||
auto command_list = command.split(' ');
|
|
||||||
auto func = handler_map[command_list.first()];
|
|
||||||
if (!func) {
|
|
||||||
auto bytes = command_list.first().toUtf8();
|
|
||||||
qWarning("Unknown command \"%s\". Type \"help\" for hints.",
|
|
||||||
bytes.constData());
|
|
||||||
} else {
|
} else {
|
||||||
command_list.removeFirst();
|
bytes[strlen(bytes) - 1] = '\0'; // remove \n
|
||||||
(this->*func)(command_list);
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
free(bytes);
|
handleLine(bytes);
|
||||||
|
if (done) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -8,10 +8,13 @@ class Shell: public QThread {
|
||||||
public:
|
public:
|
||||||
Shell();
|
Shell();
|
||||||
|
|
||||||
|
void handleLine(char *);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void run();
|
virtual void run();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool done = false;
|
||||||
QHash<QString, void (Shell::*)(QStringList &)> handler_map;
|
QHash<QString, void (Shell::*)(QStringList &)> handler_map;
|
||||||
void helpCommand(QStringList &);
|
void helpCommand(QStringList &);
|
||||||
void quitCommand(QStringList &);
|
void quitCommand(QStringList &);
|
||||||
|
@ -33,6 +36,20 @@ private:
|
||||||
void unbanUuidCommand(QStringList &);
|
void unbanUuidCommand(QStringList &);
|
||||||
void reloadConfCommand(QStringList &);
|
void reloadConfCommand(QStringList &);
|
||||||
void resetPasswordCommand(QStringList &);
|
void resetPasswordCommand(QStringList &);
|
||||||
};
|
|
||||||
|
#ifdef FK_USE_READLINE
|
||||||
|
private:
|
||||||
|
QString syntaxHighlight(char *);
|
||||||
|
public:
|
||||||
|
void redisplay();
|
||||||
|
void moveCursorToStart();
|
||||||
|
void clearLine();
|
||||||
|
bool lineDone() const;
|
||||||
|
char *generateCommand(const char *, int);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Shell *ShellInstance;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue
Block a user