aboutsummaryrefslogtreecommitdiffstats
path: root/src/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.c')
-rw-r--r--src/main.c456
1 files changed, 0 insertions, 456 deletions
diff --git a/src/main.c b/src/main.c
deleted file mode 100644
index 3b48b18..0000000
--- a/src/main.c
+++ /dev/null
@@ -1,456 +0,0 @@
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include "config2.h"
-#include "version.h"
-#include "common.h"
-#include "sctcore.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <locale.h>
-#include <time.h>
-#include <grp.h>
-#include <pwd.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <sys/types.h>
-
-#include <pthread.h>
-#include <semaphore.h>
-
-#include <l4darr.h>
-#include <l4arg.h>
-
-#define SCTMAIN_ERRNOARG "%s: 選項「%s」之後需要一個參數\n"
-#define SCTMAIN_ERRNOQARG "%s: 在「%s」之後缺少一個值\n"
-#define SCTMAIN_ERREXTRAQARG "%s: 在「%s」之後並不需要參數\n"
-
-#define SCTMAIN_CHECKARGEXIST \
- do{ \
- i++; \
- if(i >= argc){ \
- fprintf(stderr, SCTMAIN_ERRNOARG, argv[0], argv[i-1]); \
- exit(SCTEXIT_SYNTAX); \
- } \
- }while(0)
-
-#define SCTMAIN_CHECKQARGHAVEVALUE \
- do{ \
- if(!l4qarg_hasvalue(qarglist[j])){ \
- fprintf(stderr, SCTMAIN_ERRNOQARG, \
- argv[0], qarglist[j].arg_name); \
- exit(SCTEXIT_SYNTAX); \
- } \
- }while(0)
-
-#define SCTMAIN_CHECKQARGNOVALUE \
- do{ \
- if(l4qarg_hasvalue(qarglist[j])){ \
- fprintf(stderr, SCTMAIN_ERREXTRAQARG, \
- argv[0], qarglist[j].arg_name); \
- exit(SCTEXIT_SYNTAX); \
- } \
- }while(0)
-
-static const char* sctres_text[2][SCTRES_MAX] = {
- {"OK", "RE", "TLE", "OLE", "SLE", "AB", "UD"},
- {
- "正常結束",
- "執行過程中發生錯誤",
- "超過時間限制",
- "輸出超過限制",
- "暫停次數太多",
- "使用者中斷操作",
- "未定義的結果"
- }
-};
-
-int main(int argc, char* argv[]){
- /* 照理說是 zh_TW.utf8,但考慮作業系統差異還是從環境變數抓
- * (反正出錯是使用者的事) */
- setlocale(LC_ALL, "");
-
- int i, j;
- struct makechildopt mcopt; /* 送給 sctjudge_makechild 當參數用 */
- char verbose=0; /* 0 是安靜模式(機器模式)、1 是 verbose、2 是 debug */
- char dryrun=0, force=0;
- L4QARG* qarglist;
-#ifdef HAVE_CONF_UGIDNAME
- struct passwd* pwinfo;
- struct group* grinfo;
-#endif
-
- save_uids();
-
-#ifndef HAVE_CONF_CAP
- /* 即使有 setuid root,還是不能讓每個使用者拿到 root 權限
- * 所以說只有在 fork 的時候才要把這個權限開起來,其他就關掉吧 */
- disable_setuid();
-#endif
-
- /* 預設值當然都是 0 */
- memset(&mcopt, 0 ,sizeof(mcopt));
- /* 據說有些 NULL 不是 0,所以還是設定一下比較保險 */
- mcopt.executable = NULL;
- mcopt.chrootdir = NULL;
- mcopt.inputfile = NULL;
- mcopt.outputfile = NULL;
-
- /* 解析命令列的時間到了 */
- for(i=1; i<argc; i++){
- if(argv[i][0] == '-'){
- if(!strcmp(&argv[i][1], "help") || !strcmp(&argv[i][1], "-help")){
- printf(SCTJUDGE_TITLEBAR"\n\n"
- "用法:%s [選項]\n"
- "選項:\n\n"
- " -v/-verbose\n"
- " 顯示更多訊息(第二個 -v 可以顯示所有重要指令執行過"
- "程)\n\n"
- " -V/version\n"
- " 顯示版本資訊並查看可使用的額外功能列表\n\n"
- " -n/-dryrun\n"
- " 只列出設定值而不要執行(包含 -v)\n\n"
- " -f/-force\n"
- " 忽略警告訊息並強制執行\n\n"
- " -t/-time <時間>\n"
- " 受測程式時間限制為<時間>毫秒\n\n"
- " -e/-exec <檔案>\n"
- " 指定受測程式可執行檔名稱(務必靜態連結!)\n\n"
- " -i/-input <檔案>\n"
- " 指定要導向至受測程式標準輸入的檔案,若未指定則為 "
- WITH_NULL"\n\n"
- " -o/-output <檔案>\n"
- " 指定要導向至受測程式標準輸出的檔案,若未指定則為 "
- WITH_NULL"\n\n"
- " -r/-root <目錄>\n"
- " 受測程式將以<目錄>為根目錄執行,若無此選項則關"
- "閉 chroot 功能\n\n"
- " -m/-misc 選項1[=值],選項2[=值],選項3[=值]...\n"
- " 設定額外參數:\n\n"
- " mem=<大小>\n"
- " 設定受測程式記憶體上限為<大小>位元組\n\n"
- " outlimit=<大小>\n"
- " 受測程式最多只能輸出<大小>位元組,若未指定則不"
- "限制\n"
- " (無限制時請確定有足夠的磁碟空間!)\n\n"
- " stderr\n"
- " 將受測程式的標準錯誤也導向至輸出檔。若未指定,"
- "則只有標準輸\n"
- " 出會寫入輸出檔案,標準錯誤則被導向至 "
- WITH_NULL"\n\n"
- " nocopy\n"
- " 如果啟用了 chroot 功能,預設情況下本程式會自動"
- "將檔案複製\n"
- " 到新的根目錄中,並在結束時自動刪除檔案。\n"
- " 使用此選項則取消自動複製的功能,但請注意:此時"
- " file 指定的\n"
- " 檔案名稱是相對於新的根目錄而不是現在的根目錄!"
- "\n\n"
- " uid=<UID>\n"
- " 受測程式將以 UID 為 <UID> 的使用者身份執行\n\n"
- " gid=<GID>\n"
- " 受測程式將以 GID 為 <GID> 的群組身份執行\n"
- " 此選項會同時設定 real/effective/supplementary "
- "GID(s)\n"
- " 原有的 supplementry GIDs 會被清空,只剩下這裡指"
- "定的數值\n\n"
- , argv[0]);
- return SCTEXIT_SUCCESS;
- }else if(!strcmp(&argv[i][1], "V") ||
- !strcmp(&argv[i][1], "version") ||
- !strcmp(&argv[i][1], "-version")){
- fputs(SCTJUDGE_ABOUT_STRING, stdout);
- exit(SCTEXIT_SUCCESS);
- }else if(!strcmp(&argv[i][1], "v") ||
- !strcmp(&argv[i][1], "verbose")){
- verbose++;
- mcopt.flags |= SCTMC_VERBOSE;
- if(verbose >= 2){
- mcopt.flags |= SCTMC_DEBUG;
- }
- }else if(!strcmp(&argv[i][1], "n") ||
- !strcmp(&argv[i][1], "dryrun")){
- dryrun = 1, verbose = 1;
- mcopt.flags |= (SCTMC_DRYRUN | SCTMC_VERBOSE);
- }else if(!strcmp(&argv[i][1], "f") ||
- !strcmp(&argv[i][1], "force")){
- force = 1;
- mcopt.flags |= (SCTMC_FORCE);
- }else if(!strcmp(&argv[i][1], "t") ||
- !strcmp(&argv[i][1], "time")){
- SCTMAIN_CHECKARGEXIST;
- if(sscanf(argv[i], "%u", &mcopt.exectime) <= 0 ||
- mcopt.exectime <= 0){
- fprintf(stderr, "%s: 「%s」不是正確的時間設定值\n",
- argv[0], argv[i]);
- exit(SCTEXIT_SYNTAX);
- }
- }else if(!strcmp(&argv[i][1], "e") ||
- !strcmp(&argv[i][1], "exec")){
- SCTMAIN_CHECKARGEXIST;
- mcopt.executable = argv[i];
- }else if(!strcmp(&argv[i][1], "i") ||
- !strcmp(&argv[i][1], "in") ||
- !strcmp(&argv[i][1], "input")){
- SCTMAIN_CHECKARGEXIST;
- mcopt.inputfile = argv[i];
- }else if(!strcmp(&argv[i][1], "o") ||
- !strcmp(&argv[i][1], "out") ||
- !strcmp(&argv[i][1], "output")){
- SCTMAIN_CHECKARGEXIST;
- mcopt.outputfile = argv[i];
- }else if(!strcmp(&argv[i][1], "r") ||
- !strcmp(&argv[i][1], "root") ||
- !strcmp(&argv[i][1], "chroot")){
- SCTMAIN_CHECKARGEXIST;
- mcopt.chrootdir = argv[i];
- }else if(!strcmp(&argv[i][1], "m") ||
- !strcmp(&argv[i][1], "misc") ||
- !strcmp(&argv[i][1], "opt") ||
- !strcmp(&argv[i][1], "option")){
- SCTMAIN_CHECKARGEXIST;
- qarglist = l4qarg_parse(argv[i]);
- if(qarglist == NULL){
- fprintf(stderr, "%s: 記憶體配置發生錯誤\n", argv[0]);
- exit(SCTEXIT_MALLOC);
- }
- for(j=0; !l4qarg_end(qarglist[j]); j++){
- if(!strcmp(qarglist[j].arg_name, "mem") ||
- !strcmp(qarglist[j].arg_name, "memory")){
- SCTMAIN_CHECKQARGHAVEVALUE;
- if(sscanf(qarglist[j].arg_value, "%u",
- &mcopt.memlimit) <= 0 ||
- mcopt.memlimit <= 0){
- fprintf(stderr, "%s: 「%s」不是正確的記憶體限制"
- "設定值\n", argv[0],
- qarglist[j].arg_value);
- exit(SCTEXIT_SYNTAX);
- }
- }else if(!strcmp(qarglist[j].arg_name, "outlimit") ||
- !strcmp(qarglist[j].arg_name, "outlim")){
- SCTMAIN_CHECKQARGHAVEVALUE;
- if(sscanf(qarglist[j].arg_value, "%u",
- &mcopt.outlimit) <= 0 ||
- mcopt.outlimit <= 0){
- fprintf(stderr, "%s: 「%s」不是正確的輸出限制"
- "設定\n", argv[0], qarglist[j].arg_value);
- exit(SCTEXIT_SYNTAX);
- }
- }else if(!strcmp(qarglist[j].arg_name, "stderr")){
- SCTMAIN_CHECKQARGNOVALUE;
- mcopt.flags |= SCTMC_REDIR_STDERR;
- }else if(!strcmp(qarglist[j].arg_name, "nocopy")){
- SCTMAIN_CHECKQARGNOVALUE;
- mcopt.flags |= SCTMC_NOCOPY;
- }else if(!strcmp(qarglist[j].arg_name, "uid") ||
- !strcmp(qarglist[j].arg_name, "user")){
- SCTMAIN_CHECKQARGHAVEVALUE;
- mcopt.flags |= SCTMC_SETUID;
- if(sscanf(qarglist[j].arg_value, "%u", &mcopt.uid)
- <= 0){
-#ifdef HAVE_CONF_UGIDNAME
- pwinfo = getpwnam(qarglist[j].arg_value);
- if(pwinfo == NULL){
- fprintf(stderr, "%s: 「%s」不是正確的 UID 或"
- "使用者名稱\n",
- argv[0], qarglist[j].arg_value);
- exit(SCTEXIT_SYNTAX);
- }
- mcopt.uid = pwinfo->pw_uid;
-#else
- fprintf(stderr, "%s: 「%s」並不是整數\n",
- argv[0], qarglist[j].arg_value);
- exit(SCTEXIT_SYNTAX);
-#endif
- }
- }else if(!strcmp(qarglist[j].arg_name, "gid") ||
- !strcmp(qarglist[j].arg_name, "group")){
- SCTMAIN_CHECKQARGHAVEVALUE;
- mcopt.flags |= SCTMC_SETGID;
- if(sscanf(qarglist[j].arg_value, "%u", &mcopt.gid)
- <= 0){
-#ifdef HAVE_CONF_UGIDNAME
- grinfo = getgrnam(qarglist[j].arg_value);
- if(grinfo == NULL){
- fprintf(stderr, "%s: 「%s」不是正確的 GID 或"
- "群組名稱\n",
- argv[0], qarglist[j].arg_value);
- exit(SCTEXIT_SYNTAX);
- }
- mcopt.gid = grinfo->gr_gid;
-#else
- fprintf(stderr, "%s: 「%s」並不是整數\n",
- argv[0], qarglist[j].arg_value);
- exit(SCTEXIT_SYNTAX);
-#endif
- }
- }else{
- fprintf(stderr, "%s: 「%s」是不明的選項\n", argv[0],
- qarglist[j].arg_name);
- exit(SCTEXIT_SYNTAX);
- }
- }
- l4qarg_free(qarglist);
- }else{
- fprintf(stderr, "%s: 不明的選項「%s」\n", argv[0], argv[i]);
- return SCTEXIT_SYNTAX;
- }
- }else{
- fprintf(stderr, "%s: 參數 %d「%s」是多餘的\n"
- "請嘗試執行 %s -help 來取得說明\n"
- , argv[0], i, argv[i], argv[0]);
- return SCTEXIT_SYNTAX;
- }
- }
- /* 至此可以進入主程式了 */
- if(verbose){
- puts(SCTJUDGE_TITLEBAR"\n");
- }
-
- /* 檢查與修正設定值 */
- if(verbose){
- puts("正在檢查設定值是否正確......");
- }
- if(mcopt.executable == NULL){
- fputs("受測程式可執行檔名稱為必要參數,不可為空白\n", stderr);
- exit(SCTEXIT_TOOFEW);
- }
- if(mcopt.inputfile == NULL){
- mcopt.inputfile = WITH_NULL;
- }
- if(mcopt.outputfile == NULL){
- fputs("輸出檔案名稱必要參數,不可為空白\n", stderr);
- exit(SCTEXIT_TOOFEW);
- }
- if(mcopt.exectime <= 0){
- fputs("執行時間限制為必要參數,不可為空白!\n", stderr);
- exit(SCTEXIT_TOOFEW);
- }
- if((mcopt.flags & SCTMC_SETUID)){
- if(mcopt.uid == 0){
- if(!force){
- fputs("將 UID 設為 0 並不安全!(加上 -f 來強制執行)\n",
- stderr);
- exit(SCTEXIT_BADID);
- }else{
- if(procrealuid != 0){
- fputs("只有 root 可以將 UID 設定 0\n", stderr);
- exit(SCTEXIT_BADID);
- }
- }
- }
- }else{
- if(procrealuid == 0 && !force){
- fputs("不允許以 root 身份執行本程式(加上 -f 來強制執行)\n",
- stderr);
- exit(SCTEXIT_BADID);
- }
- }
-
- /* 現在開始建立 thread 了!*/
- pthread_t tmain; /* 其他 thread 的 ID 放在全域變數 */
- struct makechildrval* mtreturn; /* 接收 sctjudge_makechild 的回傳值 */
-
- /* 傳給另外兩個 thread 的參數 */
- int exectimelimit = mcopt.exectime;
- /* XXX 抱歉 hard coding,我以後會改進 */
- long displayinterval = SCT_DISPTIME_INTERVAL;
-
- pthread_attr_t detstate, joistate;
- /* 除了主要的 sctjudge_makechild 以外,
- * 我想其他的 thread 沒有 join 的必要 */
- pthread_attr_init(&detstate);
- pthread_attr_setdetachstate(&detstate, PTHREAD_CREATE_DETACHED);
- pthread_attr_init(&joistate);
- pthread_attr_setdetachstate(&joistate, PTHREAD_CREATE_JOINABLE);
- pthread_mutex_init(&pidmutex, NULL);
- pthread_mutex_init(&tkill_mx, NULL);
- pthread_mutex_init(&tdisplay_mx, NULL);
- pthread_mutex_init(&judge_tle_mx, NULL);
- sem_init(&mcthr, 0, 0);
- sem_init(&tlethr, 0, 0);
- sem_init(&dispthr, 0, 0);
- pthread_create(&tmain, &joistate, &sctjudge_makechild, (void*)&mcopt);
- if(!dryrun){
- pthread_create(&tkill, &detstate, &sctjudge_checktle,
- (void*)&exectimelimit);
- }
- if(!dryrun && verbose){
- pthread_create(&tdisplay, &detstate, &sctjudge_displaytime,
- (void*)&displayinterval);
- }
- pthread_attr_destroy(&detstate);
- pthread_attr_destroy(&joistate);
- sem_post(&mcthr);
-
- pthread_join(tmain, (void**)&mtreturn);
- /* XXX 底下這訊息很有可能讓使用者覺得很奇怪 */
- pthread_mutex_destroy(&pidmutex);
- pthread_mutex_destroy(&tkill_mx);
- pthread_mutex_destroy(&tdisplay_mx);
-
- sem_destroy(&mcthr);
- sem_destroy(&tlethr);
- sem_destroy(&dispthr);
-
- if(dryrun){
- return SCTEXIT_SUCCESS;
- }
-
- /* 這裡就要顯示結果了 */
- if(mtreturn == NULL || mtreturn->mc_exitcode != 0){
- fprintf(stderr, "%s: 因錯誤發生而結束程式\n", argv[0]);
- free((void*)mtreturn);
- sem_destroy(&mcthr);
- exit(SCTEXIT_THREAD);
- }else{
- if(verbose){
- putchar('\r');
- puts("=======================================================");
- fputs("評測結果:", stdout);
- if(mtreturn->judge_result < 0 ||
- mtreturn->judge_result >= SCTRES_UD){
- mtreturn->judge_result = SCTRES_UD;
- }
- printf("%s (%s)\n",
- sctres_text[0][mtreturn->judge_result],
- sctres_text[1][mtreturn->judge_result]);
- printf("執行時間:%ld.%03ld 秒\n",
- mtreturn->judge_time.tv_sec,
- mtreturn->judge_time.tv_nsec / 1000000);
- printf("程式離開狀態:%d\n", mtreturn->judge_exitcode);
- if(mtreturn->judge_result == SCTRES_RE){
- printf("程式因訊號 %d 而終止\n", mtreturn->judge_signal);
- }
- puts("-------------------------------------------------------");
- printf("User CPU time: %ld.%03ld\n"
- "System CPU time: %ld.%03ld\n",
- mtreturn->judge_rusage.ru_utime.tv_sec,
- mtreturn->judge_rusage.ru_utime.tv_usec / 1000,
- mtreturn->judge_rusage.ru_stime.tv_sec,
- mtreturn->judge_rusage.ru_stime.tv_usec / 1000);
- puts("=======================================================");
- }else{
- /* 輸出格式:
- * 第一行是結果代碼、時間、離開狀態
- * 第二行是收到的訊號 (只在 RE 時會有) */
- if(mtreturn->judge_result < 0 ||
- mtreturn->judge_result >= SCTRES_UD){
- mtreturn->judge_result = SCTRES_UD;
- }
- fputs(sctres_text[0][mtreturn->judge_result], stdout);
- putchar(' ');
- printf("%ld%03ld %d\n", mtreturn->judge_time.tv_sec,
- mtreturn->judge_time.tv_nsec / 1000000,
- mtreturn->judge_exitcode);
- if(mtreturn->judge_result == SCTRES_RE){
- printf("%d\n", mtreturn->judge_signal);
- }
- }
- free((void*)mtreturn);
- }
- return SCTEXIT_SUCCESS;
-}