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"); } }