「工具分享」阿卡森你又杀疯啦!
机房文化: 模拟赛后总会有人把所有人的代码拉下来一个一个叉. 此外造数据的时候也挺常有 "造一些数据, 使得一些错解无法通过" 的应用需求, 所以随便写了个 "hacker".
环境要求 Linux. 参数格式 hacker <数据前缀名> <数据起始编号> <数据终止编号> <时间限制 (ms)> <内存限制 (Mib)> <生成器代码> <标算代码> (<成功条件 (any/all)> <hack 目标 0> <hack 目标 1> ...)
. 小括号内可整体省略.
hacker 复制所有源代码到临时目录, 编译, 此后反复用生成器生成数据 data.in
, 通过标算得到 data.ans
(若标算未能在要求时空下运行则会报错), 再依次运行 hack 目标程序, 检测其运行情况, 若满足成功条件 [全部被 hack (all) / 存在被 hack (any)], 则将 data.in
和 data.ans
按照给定的数据前缀名和编号发送至工作路径. 当编号范围内的数据全部生成完成时退出, 清理临时文件.
作为一个成熟的信竞生, 麻烦你自己去调整一些常量, 或者 de 一下使用过程中出现的 bug. (
线程监控那部分是从 Lemon Lime 的 watcher_linux
改的, 所以这个代码大概需要采用 GPL-3.0-or-later 的开源协议.
好像编译的时候会报很多 -Wformat-security
的警告, 应该是可以忽略的. (
/*+Rainybunny+*/
/**
* License: GPL-3.0-or-later
*/
#include <bits/stdc++.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define rep(i, l, r) for (int i = l, rep##i = r; i <= rep##i; ++i)
#define per(i, r, l) for (int i = r, per##i = l; i >= per##i; --i)
/** Decorate @p msg with colored prefix "hacker: ". */
#define INFO(msg) \
("\033[1mhacker\033[0m: " + std::string(msg) + "\n").c_str()
/** Decorate @p msg with colored prefix "hacker: error: ". */
#define ERROR(msg) \
("\033[1mhacker\033[0m: " \
"\33[1;31merror:\033[0m " + std::string(msg) + "\n").c_str()
const int ERROR_R = 1, FATAL_R = 2;
/** Path to temporary direction. */
const std::string PATH = "/path/to/temporary-dir/";
const std::string L_GEN = PATH + "gen";
const std::string L_STD = PATH + "std";
const std::string L_TAR = PATH + "tar";
const std::string L_IN = PATH + "data.in";
const std::string L_OUT = PATH + "data.out";
const std::string L_ANS = PATH + "data.ans";
/** g++ compiling flags. */
const std::string FLAGS = "-std=c++2a -O2";
int TL, ML, ID_L, ID_R, TARN;
bool hackAll;
std::string NAME, GEN, STD;
std::vector<std::string> TAR;
int pid;
inline void killer(const int) {
kill(pid, SIGKILL), exit(0);
}
/**
* @brief Run bash command @p exec and catch its running status.
* @param exec Bash command to run.
* @return Running status of @p exec.
* 0 -- Ran normally.
* 1 -- Unknown error.
* 2 -- Runtime error (or MLE in some cases).
* 3 -- TLE.
* 4 -- MLE.
*/
inline int watch(const std::string& exec) {
pid = fork();
if (pid > 0) {
signal(SIGINT, killer);
signal(SIGABRT, killer);
signal(SIGTERM, killer);
rusage usage {}; int status = 0;
if (wait4(pid, &status, 0, &usage) == -1) return 1;
if (WIFEXITED(status)) {
if (WEXITSTATUS(status) == 1) return 1;
if (usage.ru_utime.tv_sec * 1000
+ usage.ru_utime.tv_usec / 1000 > TL) return 3;
if (usage.ru_maxrss > ML * 1024ll) return 4;
if (WEXITSTATUS(status) != 0) return 2;
return 0;
} else if (WIFSIGNALED(status)) {
if (WTERMSIG(status) == SIGXCPU) return 3;
if (WTERMSIG(status) == SIGKILL) return 4;
if (WTERMSIG(status) == SIGABRT) return 4;
return 2;
}
} else {
freopen("/dev/null", "w", stderr);
int utl = (TL - 1) / 1000 + 1; // extra runtime given.
long long uml = ML * 1024ll * 1024 * 1.5; // extra memory given.
rlimit memlim{}, stalim{}, timlim{};
memlim = (rlimit){ (rlim_t)uml, (rlim_t)uml };
stalim = (rlimit){ (rlim_t)uml, (rlim_t)uml };
timlim = (rlimit){ (rlim_t)utl, (rlim_t)(utl + 1) };
setrlimit(RLIMIT_AS, &memlim);
setrlimit(RLIMIT_STACK, &stalim);
setrlimit(RLIMIT_CPU, &timlim);
if (execlp("bash", "bash", "-c", exec.c_str(), NULL) == -1) return 1;
}
return 0;
}
/**
* @brief Register hacker with given augments. It won't check if augments are
* legal.
*/
inline void registerHacker(int argc, char** argv) {
if (argc < 8) {
fprintf(stderr, ERROR("Wrong argument number, at least 8 expected."));
exit(FATAL_R);
}
NAME = argv[1];
ID_L = atoi(argv[2]), ID_R = atoi(argv[3]);
TL = atoi(argv[4]), ML = atoi(argv[5]);
GEN = argv[6], STD = argv[7];
if (argc > 8) {
hackAll = !strcmp(argv[8], "all");
rep (i, 9, argc - 1) TAR.emplace_back(argv[i]);
TARN = int(TAR.size());
}
}
std::vector<std::string> tempFile;
inline void clearUp() {
fprintf(stderr, INFO("Clearing temporary files..."));
for (const auto& name: tempFile) {
system(("rm " + name).c_str());
}
fprintf(stderr, INFO("Exit."));
}
inline void copyTo(const std::string& from, const std::string& to,
const bool keep = false) {
if (system(("cp " + from + " " + to).c_str())) {
fprintf(stderr, ERROR("Source file not found."));
exit(FATAL_R);
}
if (!keep) tempFile.emplace_back(to);
}
inline void collectFiles() {
fprintf(stderr, INFO("Collecting files..."));
copyTo(GEN, L_GEN + ".cpp");
copyTo(STD, L_STD + ".cpp");
rep (i, 0, int(TAR.size()) - 1) {
copyTo(TAR[i], L_TAR + std::to_string(i) + ".cpp");
}
}
inline void compileAs(std::string ori,
const std::string& fil, const std::string& res) {
static size_t lastLen = 0;
ori += "]";
size_t curLen = ori.size();
if (curLen < lastLen) ori.resize(lastLen, ' ');
fprintf(stderr, "\r\033[1mhacker\033[0m: Compiling files... " \
"[currently: %s", ori.c_str());
lastLen = curLen;
if (system(("g++ " + FLAGS + " " + fil + " -o " + res
+ " 2>/dev/null").c_str())) {
fprintf(stderr, ERROR("Failed to compile source files."));
exit(FATAL_R);
}
tempFile.emplace_back(res);
}
inline void compileFiles() {
compileAs(GEN, L_GEN + ".cpp", L_GEN);
compileAs(STD, L_STD + ".cpp", L_STD);
rep (i, 0, int(TAR.size()) - 1) {
compileAs(TAR[i], L_TAR + std::to_string(i) + ".cpp",
L_TAR + std::to_string(i));
}
fprintf(stderr, "\n");
}
inline void generate() {
tempFile.emplace_back(L_IN);
tempFile.emplace_back(L_OUT);
tempFile.emplace_back(L_ANS);
fprintf(stderr, INFO("Start generating.\nSTATUS:"));
rep (id, ID_L, ID_R) {
std::string curName = NAME + std::to_string(id);
for (int atp = 1; ; ++atp) {
system((L_GEN + ">" + L_IN + " 2>/dev/null").c_str());
if (watch(L_STD + "<" + L_IN + ">" + L_ANS + " 2>/dev/null")) {
fprintf(stderr, ERROR("Standard program failed to resolve " \
"generated data."));
copyTo(L_IN, "errdata.in");
exit(ERROR_R);
}
int unhacked = 0;
std::string errStatus = "";
rep (tid, 0, TARN - 1) {
int sta = watch(L_TAR + std::to_string(tid) + "<" + L_IN + ">"
+ L_OUT + " 2>/dev/null");
if (sta == 1) errStatus += "\033[1;30mU\033[0m";
else if (sta == 2) errStatus += "\033[1;35mR\033[0m";
else if (sta == 3) errStatus += "\033[1;34mT\033[0m";
else if (sta == 4) errStatus += "\033[1;33mM\033[0m";
else if (system(("diff " + L_OUT + " " + L_ANS
+ " -w -q >/dev/null").c_str())) {
errStatus += "\033[1;31mW\033[0m";
} else ++unhacked, errStatus += "\033[1;32mA\033[0m";
}
if ((hackAll && unhacked) || (!hackAll && unhacked == TARN)) {
fprintf(stderr, "\rGenerating #%d... [%d " \
"attempt(s), \033[0;31munsuccessful\033[0m(%s)]",
id, atp, errStatus.c_str());
} else {
fprintf(stderr, "\rGenerating #%d... [%d " \
"attempt(s), \033[0;32msuccessful\033[0m(%s)]\n",
id, atp, errStatus.c_str());
copyTo(L_IN, curName + ".in", true);
copyTo(L_ANS, curName + ".ans", true);
break;
}
}
}
fprintf(stderr, "\033[0;32mAll required data are generated " \
"successfully.\033[0m\n");
}
int main(int argc, char** argv) {
atexit(clearUp);
registerHacker(argc, argv);
collectFiles();
compileFiles();
generate();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
2020-11-02 Solution Set -「ARC 107」