Exemple #1
0
int main(int argc, char **argv) {
	//argument variables
	const char *exe = NULL;
	int perclimit = 0;
	int exe_ok = 0;
	int pid_ok = 0;
	int limit_ok = 0;
	pid_t pid = 0;
	int include_children = 0;

	//get program name
	char *p = (char*)memrchr(argv[0], (unsigned int)'/', strlen(argv[0]));
	program_name = p==NULL ? argv[0] : (p+1);
	//get current pid
	cpulimit_pid = getpid();
	//get cpu count
	NCPU = get_ncpu();

	//parse arguments
	int next_option;
    int option_index = 0;
	//A string listing valid short options letters
	const char* short_options = "+p:e:l:vzih";
	//An array describing valid long options
	const struct option long_options[] = {
		{ "pid",        required_argument, NULL, 'p' },
		{ "exe",        required_argument, NULL, 'e' },
		{ "limit",      required_argument, NULL, 'l' },
		{ "verbose",    no_argument,       NULL, 'v' },
		{ "lazy",       no_argument,       NULL, 'z' },
		{ "include-children", no_argument,  NULL, 'i' },
		{ "help",       no_argument,       NULL, 'h' },
		{ 0,            0,                 0,     0  }
	};

	do {
		next_option = getopt_long(argc, argv, short_options,long_options, &option_index);
		switch(next_option) {
			case 'p':
				pid = atoi(optarg);
				pid_ok = 1;
				break;
			case 'e':
				exe = optarg;
				exe_ok = 1;
				break;
			case 'l':
				perclimit = atoi(optarg);
				limit_ok = 1;
				break;
			case 'v':
				verbose = 1;
				break;
			case 'z':
				lazy = 1;
				break;
			case 'i':
				include_children = 1;
				break;
			case 'h':
				print_usage(stdout, 1);
				break;
			case '?':
				print_usage(stderr, 1);
				break;
			case -1:
				break;
			default:
				abort();
		}
	} while(next_option != -1);

	if (pid_ok && (pid <= 1 || pid >= get_pid_max())) {
		fprintf(stderr,"Error: Invalid value for argument PID\n");
		print_usage(stderr, 1);
		exit(1);
	}
	if (pid != 0) {
		lazy = 1;
	}

	if (!limit_ok) {
		fprintf(stderr,"Error: You must specify a cpu limit percentage\n");
		print_usage(stderr, 1);
		exit(1);
	}
	double limit = perclimit / 100.0;
	if (limit<0 || limit >NCPU) {
		fprintf(stderr,"Error: limit must be in the range 0-%d00\n", NCPU);
		print_usage(stderr, 1);
		exit(1);
	}

	int command_mode = optind < argc;
	if (exe_ok + pid_ok + command_mode == 0) {
		fprintf(stderr,"Error: You must specify one target process, either by name, pid, or command line\n");
		print_usage(stderr, 1);
		exit(1);
	}

	if (exe_ok + pid_ok + command_mode > 1) {
		fprintf(stderr,"Error: You must specify exactly one target process, either by name, pid, or command line\n");
		print_usage(stderr, 1);
		exit(1);
	}

	//all arguments are ok!
	signal(SIGINT, quit);
	signal(SIGTERM, quit);

	//print the number of available cpu
	if (verbose) printf("%d cpu detected\n", NCPU);

	if (command_mode) {
		int i;
		//executable file
		const char *cmd = argv[optind];
		//command line arguments
		char **cmd_args = (char**)malloc((argc-optind + 1) * sizeof(char*));
		if (cmd_args==NULL) exit(2);
		for (i=0; i<argc-optind; i++) {
			cmd_args[i] = argv[i+optind];
		}
		cmd_args[i] = NULL;

		if (verbose) {
			printf("Running command: '%s", cmd);
			for (i=1; i<argc-optind; i++) {
				printf(" %s", cmd_args[i]);
			}
			printf("'\n");
		}

		int child = fork();
		if (child < 0) {
			exit(EXIT_FAILURE);
		}
		else if (child == 0) {
			//target process code
			int ret = execvp(cmd, cmd_args);
			//if we are here there was an error, show it
			perror("Error");
			exit(ret);
		}
		else {
			//parent code
			free(cmd_args);
			int limiter = fork();
			if (limiter < 0) {
				exit(EXIT_FAILURE);
			}
			else if (limiter > 0) {
				//parent
				int status_process;
				int status_limiter;
				waitpid(child, &status_process, 0);
				waitpid(limiter, &status_limiter, 0);
				if (WIFEXITED(status_process)) {
					if (verbose) printf("Process %d terminated with exit status %d\n", child, (int)WEXITSTATUS(status_process));
					exit(WEXITSTATUS(status_process));
				}
				printf("Process %d terminated abnormally\n", child);
				exit(status_process);
			}
			else {
				//limiter code
				if (verbose) printf("Limiting process %d\n",child);
				limit_process(child, limit, include_children);
				exit(0);
			}
		}
	}

	while(1) {
		//look for the target process..or wait for it
		pid_t ret = 0;
		if (pid_ok) {
			//search by pid
			ret = find_process_by_pid(pid);
			if (ret == 0) {
				printf("No process found\n");
			}
			else if (ret < 0) {
				printf("Process found but you aren't allowed to control it\n");
			}
		}
		else {
			//search by file or path name
			ret = find_process_by_name(exe);
			if (ret == 0) {
				printf("No process found\n");
			}
			else if (ret < 0) {
				printf("Process found but you aren't allowed to control it\n");
			}
			else {
				pid = ret;
			}
		}
		if (ret > 0) {
			if (ret == cpulimit_pid) {
				printf("Target process %d is cpulimit itself! Aborting because it makes no sense\n", ret);
				exit(1);
			}
			printf("Process %d found\n", pid);
			//control
			limit_process(pid, limit, include_children);
		}
		if (lazy) break;
		sleep(2);
	};

	exit(0);
}
int main(int argc, char *argv[])
{
	int argp = 1;
	uid_t uid = SANDBOX_UID;

	// Check the "net" argument.
	if (argc > argp && strcmp(argv[argp], "net") == 0)
	{
		uid = SANDBOX_NET_UID;
		argp = 2;
	}

	// Print usage.
	if (argc < 6 + argp)
	{
		printf("Runs a command in a sandbox environment.\n");
		printf("Usage: %s [net] time heap files disk dir course_key prg [arguments...]\n", argv[0]);
		printf("    1k for kilobyte, m for mega, g for giga and - for unlimited\n");
		printf("    net          enables network (optional)\n");
		printf("    time         maximum time for process in seconds\n");
		printf("    heap         maximum heap memory size\n");
		printf("    files        maximum number of open file descriptors\n");
		printf("    disk         maximum disk write size\n");
		printf("    dir          a target directory or -\n");
		printf("    course_key   a course key for building PATH\n");
		printf("    prg          a program to envoke\n");
		printf("    arguments    any arguments for program (optional)\n");
		return 0;
	}

	// Create new process group for kill(0).
	if (setpgid(0, 0) != 0) {
		fprintf(stderr, "FAILED: set process group\n");
		return fail("main");
	}

	connect_signals();

	// Look for course specific sandbox.
	char *course = argv[argp + 5];
	char sandbox[strlen(SANDBOX_DIR) + strlen(course) + 2];
	strcpy(sandbox, SANDBOX_DIR);
	strcat(sandbox, "_");
	strcat(sandbox, course);
	DIR* test = opendir(sandbox);
	if (test)
	{
		closedir(test);
	}
	else if (ENOENT == errno)
	{
		sandbox[strlen(SANDBOX_DIR)] = 0;
	}
	else
	{
		fprintf(stderr, "FAILED: looking for course specific sandbox\n");
		return fail("main");
	}

	// Make static dir variable.
	dir = malloc(strlen(argv[argp + 4]) + 1);
	if (dir == NULL)
	{
		fprintf(stderr, "FAILED: malloc directory name\n");
		return fail("main");
	}
	strcpy(dir, argv[argp + 4]);
	if (strcmp(dir, "-") != 0)
	{
		/*if (access(dir, R_OK | W_OK | X_OK) != 0)
		{
			fprintf(stderr, "FAILED: access %s\n", dir);
			return fail("main");
		}*/
		char tmp_path[strlen(sandbox) + strlen(TMP_PATH) + 1];
		strcpy(tmp_path, sandbox);
		strcat(tmp_path, TMP_PATH);
		/*if (access(tmp_path, R_OK | W_OK | X_OK) != 0)
		{
			fprintf(stderr, "FAILED: access %s\n", tmp_path);
			return fail("main");
		}*/

		// Move target dir inside sandbox.
		path = tempnam(tmp_path, NULL);
		if (path == NULL)
		{
			fprintf(stderr, "FAILED: tempnam %s\n", tmp_path);
			return fail("main");
		}
		if (move_directory(dir, path) != 0)
		{
			fprintf(stderr, "FAILED: move %s %s\n", dir, path);
			return 1;
		}

		// Store directory owner.
		struct stat path_stat;
		if (lstat(path, &path_stat) != 0)
		{
			fprintf(stderr, "FAILED: stat %s\n", path);
			return fail("main");
		}
		orig_uid = path_stat.st_uid;
		orig_gid = path_stat.st_gid;

		// Change directory owner.
		if (chown_directory(path, uid, orig_gid) != 0)
		{
			fprintf(stderr, "FAILED: chown %s to %d\n", path, uid);
			return 1;
		}
	}

	// Prepare values before forking.
	char *local_path = TMP_PATH;
	if (path != NULL)
	{
		local_path = path + strlen(sandbox);
	}
	unsigned long int memory = parse_number(argv[argp + 1]);
	unsigned long int files = parse_number(argv[argp + 2]);
	unsigned long int disk = parse_number(argv[argp + 3]);
	char cmd_path[strlen(CMD_PATH) + strlen(argv[argp + 5]) + 1];
	strcpy(cmd_path, CMD_PATH);
	strcat(cmd_path, argv[argp + 5]);

	// Limit maximum run time.
	time_limit = parse_number(argv[argp]);
	if (time_limit > 0 && time_limit < ULONG_MAX) alarm(time_limit);

	// Fork child process.
	pid = fork();
	if (pid == -1)
	{
		fprintf(stderr, "FAILED: fork\n");
		return fail("main");
	}
	if (pid == 0)
	{
		if (chroot(sandbox) != 0)
		{
			fprintf(stderr, "FAILED: chroot %s\n", sandbox);
			return fail("main");
		}
		if (setuid(uid) != 0)
		{
			fprintf(stderr, "FAILED: setuid %d\n", uid);
			return fail("main");
		}
		if (chdir(local_path) != 0)
		{
			fprintf(stderr, "FAILED: chdir %s\n", local_path);
			return fail("main");
		}

		// Create command line array.
		char *cmd = argv[argp + 6];
		int argn = argp + 7;
		char *arg[argc - argn + 2];
		arg[0] = cmd;
		int p = 1;
		while (argn < argc)
		{
			arg[p] = argv[argn];
			p++;
			argn++;
		}
		arg[p] = NULL;

		// Create environment array.
		char *env[5];
		char envpath[6 + strlen(cmd_path)];
		strcpy(envpath, "PATH=");
		strcat(envpath, cmd_path);
		env[0] = envpath;
		struct passwd *pw = getpwuid(uid);
		if (pw == NULL)
		{
			fprintf(stderr, "FAILED: getpwuid %d\n", uid);
			return fail("main");
		}
		char envhome[6 + strlen(pw->pw_dir)];
		strcpy(envhome, "HOME=");
		strcat(envhome, pw->pw_dir);
		env[1] = envhome;
		env[2] = "DISPLAY=:0";
		env[3] = "LANG=en_US.UTF-8";
		env[4] = NULL;

		if (limit_process(memory, files, disk) != 0) return 1;

		// Update path in current env for finding the cmd.
		if (setenv("PATH", cmd_path, 1) != 0)
		{
			fprintf(stderr, "FAILED: setenv PATH=%s\n", cmd_path);
			return fail("main");
		}

		// Replace the process.
		execvpe(cmd, arg, env);
		fprintf(stderr, "FAILED: execvp\n");
		return fail("main");
	}

	// Wait for child process to terminate.
	else
	{
		pid_t w;
		int status;
		do
		{
			w = waitpid(pid, &status, 0);
			if (w == -1)
			{
				fprintf(stderr, "FAILED: waitpid %d\n", pid);
				return fail("main");
			}
		}
		while (!WIFEXITED(status) && !WIFSIGNALED(status));
		cleanup();
		if (WIFEXITED(status))
		{
			return WEXITSTATUS(status);
		}
		fprintf(stderr, "FAILED: Process did not end with exit status.\n");
		return fail("main");
	}
}