#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include "config2.h"
#include "common.h"
#include "sctcore.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
#include <errno.h>
#include <grp.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <sys/wait.h>

#include <pthread.h>
#include <semaphore.h>

#ifdef HAVE_CONF_CAP
#include <sys/prctl.h>
#include <sys/capability.h>
#endif

#define abort_makechild(val) \
	do { \
	sctjudge_makechild_cleanup(&mcopt, chrdir_oldmode, execdestname); \
	toreturn->mc_exitcode = (val); \
	pthread_exit((void*)toreturn); \
	}while(0)

#define SCTCHILD_OPEN_INPUT			 0
#define SCTCHILD_DUP2_INPUT			 1
#define SCTCHILD_OPEN_OUTPUT		 2
#define SCTCHILD_DUP2_OUTPUT		 3
#define SCTCHILD_OPEN_STDERR		 4
#define SCTCHILD_DUP2_STDERR		 5
#define SCTCHILD_CLOSE_OTHERFD		 6
#define SCTCHILD_SETRLIMIT_VM		 7
#define SCTCHILD_SETRLIMIT_FSIZE	 8
#define SCTCHILD_SETRLIMIT_OPENFILE	 9
#define SCTCHILD_SETRLIMIT_PROC		10
#define SCTCHILD_CHROOT				11
#define SCTCHILD_SETUID				12
#define SCTCHILD_SETGID				13
#define SCTCHILD_SETGROUPS			14
#define SCTCHILD_CAPSET				15
#define SCTCHILD_EXEC				16
#define SCTCHILD_MSGMAX				17 /* 訊息代碼的最大值 */
#define SCTCHILD_WRITE_SUCCMSG(reason) \
	do{ \
		childmsg[0] = (reason); \
		childmsg[1] = 0; \
		write(childpipe[1], (void*)childmsg, sizeof(int)*2);  \
	}while(0)
#define SCTCHILD_WRITE_FAILMSG(reason) \
	do{ \
		childmsg[0] = (reason); \
		childmsg[1] = errno; \
		write(childpipe[1], (void*)childmsg, sizeof(int)*2);  \
	}while(0)

pid_t pidchild;
pthread_mutex_t pidmutex, tkill_mx, tdisplay_mx, judge_tle_mx;
pthread_t tkill, tdisplay;
char tkill_yes = 0, tdisplay_yes = 0, judge_tle = 0;
sem_t mcthr, tlethr, dispthr;

static const char* childmsg_text[SCTCHILD_MSGMAX] = {
	"開啟輸入檔案",
	"重新導向標準輸入",
	"開啟輸出檔案",
	"重新導向標準輸出",
	"開啟用於導向標準錯誤的檔案",
	"重新導向標準錯誤",
	"關閉所有不使用的檔案",
	"設定記憶體限制",
	"設定輸出限制",
	"設定禁止開啟其他檔案",
	"設定禁止產生新程序",
	"執行 chroot",
	"修改 real 和 effective UID",
	"修改 real 和 effective GID",
	"修改 supplementary GIDs",
	"丟棄所有 Linux capabilities",
	"執行受測程式"
};

static void sctjudge_makechild_cleanup_p1(void){
	pthread_mutex_lock(&tkill_mx);
	if(tkill_yes){
		tkill_yes = 0;
		pthread_mutex_unlock(&tkill_mx);
		sem_post(&tlethr);
	}else{
		pthread_mutex_unlock(&tkill_mx);
	}

	pthread_mutex_lock(&tdisplay_mx);
	if(tdisplay_yes){
		tdisplay_yes = 0;
		pthread_mutex_unlock(&tdisplay_mx);
		sem_post(&dispthr);
	}else{
		pthread_mutex_unlock(&tdisplay_mx);
	}

}
	
static void sctjudge_makechild_cleanup_p2(mcopt, oldperm, copiedexe)
	const struct makechildopt* mcopt;
	const mode_t oldperm;
	char* copiedexe;
{
	if(oldperm != -1 && mcopt->chrootdir != NULL){
		if(mcopt->flags & SCTMC_VERBOSE){
			printf("恢復 %s 的權限\n", mcopt->chrootdir);
		}
		chmod(mcopt->chrootdir, oldperm);
	}
	if(copiedexe != NULL && mcopt->chrootdir != NULL && 
			!(mcopt->flags & SCTMC_NOCOPY)){
		if(mcopt->flags & SCTMC_VERBOSE){
			printf("移除 %s......\n", copiedexe);
		}
		unlink(copiedexe);
		free(copiedexe);
	}
}

static void sctjudge_makechild_cleanup(mcopt, oldperm, copiedexe)
	const struct makechildopt* mcopt;
	const mode_t oldperm;
	char* copiedexe;
{
	sctjudge_makechild_cleanup_p1();
	sctjudge_makechild_cleanup_p2(mcopt, oldperm, copiedexe);
}

static int copyfilebyname(const char* src, const char* destdir
		, char** srcsn, char** destfn){
	/* strerror 並不 thread safe,因此必須確定只有一個 thread 會用到 */
	/* 為了確保安全,要求目的地目錄必須是空的 */
	DIR* dirp;
	if((dirp=opendir(destdir)) == NULL){
		fprintf(stderr, "無法開啟目錄 %s:%s\n", destdir, strerror(errno));
		return -1;
	}
	struct dirent* entryp;
	while((entryp=readdir(dirp)) != NULL){
		if(entryp->d_name[0] != '.' || 
			(entryp->d_name[1] != '.' && entryp->d_name[1] != '\0')){
			fprintf(stderr, "拒絕複製檔案:目錄 %s 不是空的\n", destdir);
			return -1;
		}
	}
	int fd, fd2;
	if((fd=open(src, O_RDONLY)) < 0){
		fprintf(stderr, "無法讀取檔案 %s:%s\n", src, strerror(errno));
		return -1;
	}
	const int bufsize = 4096;
	void* buf = malloc(bufsize);
	char* srcshortname;
	char* tmp;
	if((tmp=strrchr(src, '/')) == NULL){
		srcshortname = (char*)src;
	}else{
		srcshortname = tmp + 1;
	}
	*srcsn = srcshortname;
	char* destfilename = (char*)
		malloc(strlen(srcshortname) + strlen(destdir) + 1);
	sprintf(destfilename, "%s/%s", destdir, srcshortname);
	*destfn = destfilename;
	/* 新檔案權限是 0755,umask 亂弄的我可不管 */
	if((fd2=open(destfilename, O_CREAT | O_WRONLY | O_EXCL, 
				S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) < 0){
		fprintf(stderr, "無法建立檔案 %s:%s\n", 
				destfilename, strerror(errno));
		free(buf);
		return -1;
	}
	int readcount;
	while((readcount = read(fd, buf, bufsize)) > 0){
		write(fd2, buf, readcount);
	}
	close(fd);
	close(fd2);
	free(buf);
	return 0;
}

static void sctjudge_list_settings(const struct makechildopt* mcopt){
	printf("\t受測程式可執行檔名稱:%s", mcopt->executable);
	if(mcopt->flags & SCTMC_NOCOPY && mcopt->chrootdir != NULL){
		printf(" (相對於 %s)\n"
			"\t受測程式執行時的根目錄:%s\n"
			"\t執行前複製檔案:否\n"
			, mcopt->chrootdir, mcopt->chrootdir);
	}else{
		putchar('\n');
		if(mcopt->chrootdir != NULL){
			printf("\t受測程式執行時的根目錄:%s\n"
				"\t執行前複製檔案:是\n"
				, mcopt->chrootdir);
		}else{
			puts("\t受測程式執行時的根目錄:/\n"
			"\t執行前複製檔案:否");
		}
	}
	printf("\t輸入檔案:%s\n"
		"\t輸出檔案:%s"
		, mcopt->inputfile, mcopt->outputfile);
	if(mcopt->flags & SCTMC_REDIR_STDERR){
		puts(" (重新導向標準輸出和標準錯誤)");
	}else{
		puts(" (重新導向標準輸出)");
	}
	printf("\t執行時間限制:%d 毫秒\n", mcopt->exectime);
	fputs("\t記憶體使用量限制:", stdout);
	if(mcopt->memlimit > 0){
		printf("%d 位元組\n", mcopt->memlimit);
	}else{
		puts("無限制");
	}
	fputs("\t輸出限制:", stdout);
	if(mcopt->outlimit > 0){
		printf("%d 位元組\n", mcopt->outlimit);
	}else{
		puts("無限制");
	}
	if(mcopt->flags & SCTMC_SETUID){
		printf("\t設定受測程式執行時的 UID 為 %u\n", mcopt->uid);
	}else{
		puts("\t執行受測程式時維持原有的 real UID 和 effective UID");
	}
	if(mcopt->flags & SCTMC_SETGID){
		printf("\t設定受測程式執行時的 GID 為 %u\n", mcopt->gid);
	}else{
		puts("\t執行受測程式時維持原有的 real GID、effective GID "
				"和 supplementary GID");
	}
}

static int read_size(int fd, void* buf, size_t count){
	/* 一定要讀到 count 位元組的資料才會回傳,所以只要回傳 < count
	 * 就表示已經讀到 EOF 了 */
	char* bufp = (char*)buf;
	size_t curcount = 0;
	int rval;
	do{
		rval = read(fd, bufp, count - curcount);
		if(rval < 0){
			return rval;
		}else if(rval == 0){
			return curcount;
		}else{
			bufp += rval;
			curcount += rval;
		}
	}while(curcount < count);
	return count;
}

void* sctjudge_makechild(void* arg){
	/* 首先等所有 thread 都啟動完成再開始動作 */
	sem_wait(&mcthr);

	/* 宣告變數囉! */
	int i;

	/* 函式參數與回傳值,Judge 結果就當作回傳值 */
	struct makechildopt mcopt = *(struct makechildopt*)arg;
	struct makechildrval* toreturn = 
		(struct makechildrval*)malloc(sizeof(struct makechildrval));
	memset(toreturn, 0, sizeof(struct makechildrval));

	/* 可執行檔名稱儲存區 */
	char* execshortname = NULL; /* 可以說就是 basename,chroot+copy 才會用 */
   	char* execdestname = NULL; /* 複製出來的暫時執行檔,chroot+copy 才會用 */
	/* 這幾個變數用來儲存 Judge 結果,如果沒有啟用 verbose 
	 * 且一切正常,那這些是唯一會輸出的東西了,目的是輸出容
	 * 易被其他程式分析 */

	/* 要 chroot 的目錄的相關資訊 */
	struct stat chrdirinfo;      /* 取得原始的 mode 用的 */
	mode_t chrdir_oldmode = -1;  /* 這樣我們才能在結束時 chmod 回去 */

	/* 子程序的 PID,因為不會變,所以複製一份下來就不用去全域變數拿的 */
	pid_t pidcopy;

	/* 和子程序溝通用的 */
	int childpipe[2];
	int childmsg[2]; /* 每個訊息的大小都是 sizeof(int)*2 
						[0]=發生事情的名稱 [1]=errno */
	int readsize = sizeof(int) * 2;

	/* 計時用的 */
	struct timespec execstart, execfinish;

	/* 子程序結束狀態 */
	int childexitstat;
	char childterminated = 0;

	/* 子程序才用的到的變數 */
	int fdinput, fdoutput, fderr, childressize;
	const int fdtablesize = getdtablesize();
	struct rlimit childres;

	/* 變數宣告完了 */

	if(mcopt.flags & SCTMC_VERBOSE){
		puts("列出設定值:");
		sctjudge_list_settings(&mcopt);
		fflush(stdout);
	}
	if(mcopt.flags & SCTMC_DRYRUN){
		abort_makechild(SCTMCRVAL_SUCCESS);
	}
	
	/* 我們要開始做正事了 */
	/* 由此開始,我是唯一會用到 strerror() 的 thread
	 * 所以說應該沒有問題 */
	if(mcopt.chrootdir != NULL && !(mcopt.flags & SCTMC_NOCOPY)){
		/* 必須要複製可執行檔 */
		if(mcopt.flags & SCTMC_VERBOSE){
			puts("開始複製可執行檔......");
		}
		if(copyfilebyname(mcopt.executable, mcopt.chrootdir, &execshortname
					, &execdestname) < 0){
			abort_makechild(SCTMCRVAL_PREPARE);
		}
	}
	/* 再來是 chroot 的問題,避免受測程式亂開目錄之類的,
	 * 所以把權限設成 0555,不過如果沒有變更 uid 和 gid ,
	 * 受測程式還是可以 chmod 回來 */
	if(mcopt.chrootdir != NULL){
		if(mcopt.flags & SCTMC_VERBOSE){
			puts("取得用於 chroot 的目錄相關資訊");
		}
		if(stat(mcopt.chrootdir, &chrdirinfo) < 0){
			fprintf(stderr, "無法取得目錄 %s 的相關資訊:%s\n", 
				mcopt.chrootdir, strerror(errno));
			abort_makechild(SCTMCRVAL_PREPARE);
		}else{
			if((chrdirinfo.st_mode & S_IFMT) == S_IFDIR){
				chrdir_oldmode = chrdirinfo.st_mode &
					(S_ISUID |S_ISGID |S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO);
			}else{
				fprintf(stderr, "%s 並不是目錄\n", mcopt.chrootdir);
				abort_makechild(SCTMCRVAL_PREPARE);
			}
		}
		if(mcopt.flags & SCTMC_VERBOSE){
			puts("嘗試變更此目錄的權限");
		}
		if(chmod(mcopt.chrootdir, 
			S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0){
			fprintf(stderr, "無法變更目錄 %s 的權限\n", mcopt.chrootdir);
			abort_makechild(SCTMCRVAL_PREPARE);
		}
	}

	fflush(stdout);
#ifndef HAVE_CONF_CAP
	enable_setuid();
#endif
	/* 建立用來和 chlid process 溝通的 pipe */
	pipe(childpipe);
	pthread_mutex_lock(&pidmutex);
#ifdef HAVE_WORKING_FORK
	pidchild = fork();
#else
#error "This program requires a working fork function."
#endif
	if(pidchild < 0){
#ifndef HAVE_CONF_CAP
		disable_setuid();
#endif
		pthread_mutex_unlock(&pidmutex);
		fprintf(stderr, "子程序建立失敗:%s\n", strerror(errno));
		abort_makechild(SCTMCRVAL_FORK);
	}
	else if(pidchild > 0){
		/* 我們原本的 thread */
		/* 從這裡開始,我們需要 setuid 來 kill 子程序,
		 * 所以說不要執行 disable_setuid() */
		pidcopy = pidchild;
		pthread_mutex_unlock(&pidmutex);
		close(childpipe[1]);
		/* 從 pipe 接收 child process 狀態,每次固定要讀 sizeof(int)*2 */
		while(read_size(childpipe[0], (void*)childmsg, readsize) == readsize){
			if(childmsg[0] < 0 || childmsg[0] >= SCTCHILD_MSGMAX){
				/* 會發生嗎?除非被亂七八糟 ptrace 吧 
				 * 這時候先砍掉子程序再說 */
				kill(pidcopy, SIGKILL);
				close(childpipe[0]);
				abort_makechild(SCTMCRVAL_INVALID);
			}else{
				if(childmsg[1]){
					fprintf(stderr, "子程序[%d]發生錯誤:%s:%s\n",
							pidcopy, childmsg_text[childmsg[0]], 
							strerror(childmsg[1]));
					kill(pidcopy, SIGKILL);
					close(childpipe[0]);
					abort_makechild(SCTMCRVAL_CHILDFAIL);
				}else{
					if(mcopt.flags & SCTMC_VERBOSE){
						printf("子程序[%d]:%s\n", pidcopy, 
								childmsg_text[childmsg[0]]);
					}
				}
			}
		}
		clock_gettime(CLOCK_REALTIME, &execstart);
		close(childpipe[0]);

		/* 現在我們確定受測程式已經在執行了,所以需要記錄一下時間
		 * 通知其他 thread */
		sem_post(&tlethr);
		sem_post(&dispthr);

		/* 開始要 wait 了,莫名其妙 stop 的程式我最多只會送
		 * 5 次 SIGCONT 給你(避免進入無限迴圈) 
		 * 這種奇怪的程式就等著 SLE 吧 */
		for(i=0; i<=5; i++){
			wait4(pidcopy, &childexitstat, WUNTRACED, &toreturn->judge_rusage);
			if(WIFSTOPPED(childexitstat) && i!=5){
				if(mcopt.flags & SCTMC_VERBOSE){
					printf("\n子程序因訊號 %d 而暫停,自動傳送 SIGCONT "
							"(第 %d 次)\n",
						WSTOPSIG(childexitstat), i+1);
				}
				kill(pidcopy, SIGCONT);
			}else{
				if(WIFSIGNALED(childexitstat)){
					clock_gettime(CLOCK_REALTIME, &execfinish);
					childterminated = 1;
					toreturn->judge_signal = WTERMSIG(childexitstat);
					if(toreturn->judge_signal == SIGXFSZ){
						toreturn->judge_result = SCTRES_OLE;
					}else{
						toreturn->judge_result = SCTRES_RE;
					}
					toreturn->judge_exitcode = WEXITSTATUS(childexitstat);
					break;
				}
				if(WIFEXITED(childexitstat)){
					clock_gettime(CLOCK_REALTIME, &execfinish);
					childterminated = 1;
					toreturn->judge_result = SCTRES_OK;
					toreturn->judge_exitcode = WEXITSTATUS(childexitstat);
					break;
				}
			}
		}
		if(!childterminated){
			kill(pidcopy, SIGKILL);
			wait4(pidcopy, &childexitstat, WUNTRACED, &toreturn->judge_rusage);
			toreturn->judge_result = SCTRES_SLE;
			toreturn->judge_exitcode = WEXITSTATUS(childexitstat);
			clock_gettime(CLOCK_REALTIME, &execfinish);
		}
		pthread_mutex_lock(&judge_tle_mx);
		if(judge_tle && toreturn->judge_result == SCTRES_RE){
			toreturn->judge_result = SCTRES_TLE;
		}
		pthread_mutex_unlock(&judge_tle_mx);
		difftimespec(&execstart, &execfinish, &toreturn->judge_time);
		if(break_flag){
			toreturn->judge_result = SCTRES_AB;
		}
		sctjudge_makechild_cleanup_p1();
		if(mcopt.flags & SCTMC_VERBOSE){
			/* XXX 有更好的方法洗掉文字嗎? */
			fputs("\r                                                       \r"
					, stdout);
		}
	}else{
		/* 子程序 
		 * 由此開始要很小心,因為我們是在 thread 裡面 fork (?) */
		close(childpipe[0]);
		/* close-on-exec 用來關閉這個暫時的資料傳輸通道 */
		fcntl(childpipe[1], F_SETFD, fcntl(childpipe[1],F_GETFD) | FD_CLOEXEC);

		/* 設為獨立的 process group,不過失敗就算了,這不重要 */
		setpgid(getpid(), getpid());

		/* 重設所有 signal handler
		 * XXX 我猜 signal 最大值是 32,還有其他更好的寫法嗎? */
		for(i=1; i<=32; i++){
			signal(i, SIG_DFL);
		}

#ifndef HAVE_CONF_CAP
		disable_setuid();
#endif

		fdinput = open(mcopt.inputfile, O_RDONLY);
		if(fdinput < 0){
			SCTCHILD_WRITE_FAILMSG(SCTCHILD_OPEN_INPUT);
			exit(1);
		}else{
			SCTCHILD_WRITE_SUCCMSG(SCTCHILD_OPEN_INPUT);
		}
		if(dup2(fdinput, STDIN_FILENO) < 0){
			SCTCHILD_WRITE_FAILMSG(SCTCHILD_DUP2_INPUT);
			exit(1);
		}else{
			SCTCHILD_WRITE_SUCCMSG(SCTCHILD_DUP2_INPUT);
		}

		/* 注意:開啟輸出檔會直接覆寫現存檔案! */
		fdoutput = open(mcopt.outputfile, O_WRONLY | O_CREAT | O_TRUNC,
				S_IRUSR | S_IWUSR);
		if(fdoutput < 0){
			SCTCHILD_WRITE_FAILMSG(SCTCHILD_OPEN_OUTPUT);
			exit(1);
		}else{
			SCTCHILD_WRITE_SUCCMSG(SCTCHILD_OPEN_OUTPUT);
		}
		if(dup2(fdoutput, STDOUT_FILENO) < 0){
			SCTCHILD_WRITE_FAILMSG(SCTCHILD_DUP2_OUTPUT);
			exit(1);
		}else{
			SCTCHILD_WRITE_SUCCMSG(SCTCHILD_DUP2_OUTPUT);
		}

#ifndef HAVE_CONF_CAP
		enable_setuid();
#endif
		fchown(fdoutput, procrealuid, -1);

		/* 再來就是 stderr 了 */
		if(mcopt.flags & SCTMC_REDIR_STDERR){
			if(dup2(fdoutput, STDERR_FILENO) < 0){
				SCTCHILD_WRITE_FAILMSG(SCTCHILD_DUP2_STDERR);
				exit(1);
			}else{
				SCTCHILD_WRITE_SUCCMSG(SCTCHILD_DUP2_STDERR);
			}
		}else{
			fderr = open(NULL_DEVICE, O_RDONLY);
			if(fderr < 0){
				SCTCHILD_WRITE_FAILMSG(SCTCHILD_OPEN_STDERR);
				exit(1);
			}else{
				SCTCHILD_WRITE_SUCCMSG(SCTCHILD_OPEN_STDERR);
			}
			if(dup2(fderr, STDERR_FILENO) < 0){
				SCTCHILD_WRITE_FAILMSG(SCTCHILD_DUP2_STDERR);
				exit(1);
			}else{
				SCTCHILD_WRITE_SUCCMSG(SCTCHILD_DUP2_STDERR);
			}
		}

		/* 很暴力地把所有不該留著的 fd 關掉 */
		for(i=3; i<fdtablesize; i++){
			if(i != childpipe[1]){
				close(i);
			}
		}
		SCTCHILD_WRITE_SUCCMSG(SCTCHILD_CLOSE_OTHERFD);

		/* 有人要求我要 chroot 嗎? */
		if(mcopt.chrootdir != NULL){
			if(chroot(mcopt.chrootdir) < 0){
				SCTCHILD_WRITE_FAILMSG(SCTCHILD_CHROOT);
				exit(1);
			}else{
				chdir("/");
				SCTCHILD_WRITE_SUCCMSG(SCTCHILD_CHROOT);
			}
		}


#ifdef HAVE_CONF_CAP
		/* 確保修改身份的時候 capabilities 仍然留著
		 * 等一下再自己把 capabilities 丟掉 */
		prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
#endif		

		/* 我要 setgid 嗎? */
		if(mcopt.flags & SCTMC_SETGID){
			if(setregid(mcopt.gid, mcopt.gid) < 0){
				SCTCHILD_WRITE_FAILMSG(SCTCHILD_SETGID);
				exit(1);
			}else{
				SCTCHILD_WRITE_SUCCMSG(SCTCHILD_SETGID);
			}
			if(setgroups(1, &mcopt.gid) < 0){
				SCTCHILD_WRITE_FAILMSG(SCTCHILD_SETGROUPS);
				exit(1);
			}else{
				SCTCHILD_WRITE_SUCCMSG(SCTCHILD_SETGROUPS);
			}
		}

		/* 我要 setuid 嗎? */
		if(mcopt.flags & SCTMC_SETUID){
			if(setreuid(mcopt.uid, mcopt.uid) < 0){
				SCTCHILD_WRITE_FAILMSG(SCTCHILD_SETUID);
				exit(1);
			}else{
				SCTCHILD_WRITE_SUCCMSG(SCTCHILD_SETUID);
			}
		}
#ifndef HAVE_CONF_CAP
		else{
			/* 確保 setuid 可執行檔所造成的 effective UID 改變不會傳給子程序 */
			setuid(procrealuid);
		}
#endif

		/* 開始設定資源限制 */
		if(mcopt.memlimit > 0){
			childressize = mcopt.memlimit;
			childres.rlim_cur = childressize;
			childres.rlim_max = childressize;
			if(setrlimit(RLIMIT_AS, &childres) < 0){
				SCTCHILD_WRITE_FAILMSG(SCTCHILD_SETRLIMIT_VM);
				exit(1);
			}else{
				SCTCHILD_WRITE_SUCCMSG(SCTCHILD_SETRLIMIT_VM);
			}
		}

		if(mcopt.outlimit > 0){
			childressize = mcopt.outlimit;
			childres.rlim_cur = childressize;
			childres.rlim_max = childressize;
			if(setrlimit(RLIMIT_FSIZE, &childres) < 0){
				SCTCHILD_WRITE_FAILMSG(SCTCHILD_SETRLIMIT_FSIZE);
				exit(1);
			}else{
				SCTCHILD_WRITE_SUCCMSG(SCTCHILD_SETRLIMIT_FSIZE);
			}
		}

		/* 從現在開始,不准再開檔案了 */
		childres.rlim_cur = 0;
		childres.rlim_max = 0;
		if(setrlimit(
#ifdef RLIMIT_NOFILE
					RLIMIT_NOFILE
#else
					RLIMIT_OFILE
#endif
					, &childres) < 0){
			SCTCHILD_WRITE_FAILMSG(SCTCHILD_SETRLIMIT_OPENFILE);
			exit(1);
		}else{
			SCTCHILD_WRITE_SUCCMSG(SCTCHILD_SETRLIMIT_OPENFILE);
		}

		/* 從現在開始,不可以再產生新程序了 */
		if(setrlimit(RLIMIT_NPROC, &childres) < 0){
			SCTCHILD_WRITE_FAILMSG(SCTCHILD_SETRLIMIT_PROC);
			exit(1);
		}else{
			SCTCHILD_WRITE_SUCCMSG(SCTCHILD_SETRLIMIT_PROC);
		}
		

		/* 非 Linux 系統就到此結束了,可以 exec 了 */

#ifdef HAVE_CONF_CAP
		/* 正在丟掉 capabilities */
		cap_t nocap = cap_init();
		cap_clear_flag(nocap, CAP_EFFECTIVE);
		cap_clear_flag(nocap, CAP_INHERITABLE);
		cap_clear_flag(nocap, CAP_PERMITTED);
		if(cap_set_proc(nocap) < 0){
			SCTCHILD_WRITE_FAILMSG(SCTCHILD_CAPSET);
			exit(1);
		}else{
			SCTCHILD_WRITE_SUCCMSG(SCTCHILD_CAPSET);
		}
		/* 需要 drop bound 嗎?再考慮看看 */
#endif

		if(mcopt.chrootdir == NULL){
			execl(mcopt.executable, mcopt.executable, NULL);
		}else{
			if(mcopt.flags & SCTMC_NOCOPY){
				execl(mcopt.executable, mcopt.executable, NULL);
			}else{
				execl(execshortname, execshortname, NULL);
			}
		}

		SCTCHILD_WRITE_FAILMSG(SCTCHILD_EXEC);
		exit(2);
	}
	/* Judge 完成,準備離開 */
	fflush(stdout);
	sctjudge_makechild_cleanup_p2(&mcopt, chrdir_oldmode, execdestname);
	fflush(stdout);
	(toreturn->mc_exitcode) = SCTMCRVAL_SUCCESS;
	return (void*)toreturn;
}