机房文化: 模拟赛后总会有人把所有人的代码拉下来一个一个叉. 此外造数据的时候也挺常有 “造一些数据, 使得一些错解无法通过” 的应用需求, 所以随便写了个 “hacker”.

  环境要求 Linux. 参数格式 hacker <数据前缀名> <数据起始编号> <数据终止编号> <时间限制 (ms)> <内存限制 (Mib)> <生成器代码> <标算代码> (<成功条件 (any/all)> <hack 目标 0> <hack 目标 1> ...). 小括号内可整体省略.

  hacker 复制所有源代码到临时目录, 编译, 此后反复用生成器生成数据 data.in, 通过标算得到 data.ans (若标算未能在要求时空下运行则会报错), 再依次运行 hack 目标程序, 检测其运行情况, 若满足成功条件 [全部被 hack (all) / 存在被 hack (any)], 则将 data.indata.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;
}

原文地址:http://www.cnblogs.com/rainybunny/p/16851095.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性