int main (int argc, char **argv, char **envp) { struct rusage usage; char **p; int status, mem, skipped, memused; int tsource, ttarget; int v; redirect = stderr; safe_signal (SIGPIPE, SIG_DFL); tsource = time (NULL); p = parse (argv); if (p == NULL) { printusage (argv); return (EXIT_FAILURE); } else { /* Get an unused uid */ if (profile.minuid != profile.maxuid) { srand (time (NULL) ^ getpid ()); profile.minuid += rand () % (profile.maxuid - profile.minuid); } if (strcmp (usage_file, "/dev/null") != 0) { redirect = fopen (usage_file, "w"); chown (usage_file, profile.minuid, getgid()); chmod (usage_file, 0640); if (redirect == NULL) error ("Couldn't open usage file\n"); } /* stderr from user program is junk */ junk = fopen (error_file, "w"); if (junk == NULL) error ("Couldn't open junk file %s\n", error_file); if (strcmp (error_file, "/dev/null") != 0) { chown (error_file, profile.minuid, getgid()); chmod (error_file, 0640); } if (setgid (profile.minuid) < 0) { if (errno == EPERM) { error ("Couldn't setgid due to permission"); } else { error (NULL); } } if (setuid (profile.minuid) < 0) { if (errno == EPERM) { error ("Couldn't setuid due to permission"); } else { error (NULL); } } if (getuid () == 0) error ("Not changing the uid to an unpriviledged one is a BAD ideia"); if (signal (SIGALRM, wallclock) == SIG_ERR) error ("Couldn't install signal handler"); if (alarm (profile.clock) != 0) error ("Couldn't set alarm"); /* Fork new process */ pid = fork (); if (pid < 0) error (NULL); if (pid == 0) /* Forked/child process */ { /* Chrooting */ if (chroot_dir != NULL) { if (0 != chdir(chroot_dir)) { kill (getpid (), SIGPIPE); error ("Can not change to chroot dir"); } if (0 != chroot(chroot_dir)) { kill (getpid (), SIGPIPE); error ("Can not chroot"); } } /* Change dir */ if (run_dir != NULL) { if (0 != chdir(run_dir)) { kill (getpid (), SIGPIPE); error ("Cannot change to rundir"); } } dup2(fileno(junk), fileno(stderr)); if (setuid (profile.minuid) < 0) error (NULL); /* Don't run as root */ if (getuid () == 0) error ("Running as a root is not secure!"); /* Set priority */ if (0 != setpriority(PRIO_USER,profile.minuid,NICE_LEVEL)) { kill (getpid (), SIGPIPE); error (NULL); } /* Set resource limits - memory, time etc */ setlimits(profile); /* Execute the program */ if (execve (*p, p, envp) < 0) { kill (getpid (), SIGPIPE); error (NULL); } } else /* Parent process */ { if (setuid (profile.minuid) < 0) error (NULL); if (getuid () == 0) error ("Running as a root is not secure!"); memusage_init (); mark = OK; /* Poll every INTERVAL ms and get the maximum * * memory usage, exit when the child terminates */ mem = 64; skipped = 0; memused = 0; do { msleep (INTERVAL); memused = memusage (pid); if (memused > -1) { mem = max (mem, memused); } else { /* Can not read memory usage! */ skipped++; } if (skipped > 10) { /* process is already finished or something wrong happened */ terminate (pid); mark = MLE; } if (mem > profile.memory) { terminate (pid); mark = MLE; } do v = wait4 (pid, &status, WNOHANG | WUNTRACED, &usage); //v = wait4 (pid, &status, WNOHANG | WUNTRACED | WCONTINUED | WEXITED, &usage); while ((v < 0) && (errno != EINTR)); if (v < 0) error (NULL); } while (v == 0); memusage_close (); ttarget = time (NULL); if (mark == MLE) printstats ("Memory Limit Exceeded\n"); else if (mark == RTLE) printstats ("Time Limit Exceeded\n"); else { if (WIFEXITED (status) != 0) { if (WEXITSTATUS (status) != 0) printstats ("Command exited with non-zero status (%d)\n", WEXITSTATUS (status)); else printstats ("OK\n"); } else { if (WIFSIGNALED (status) != 0) { /* Was killed for a TLE (or was it an OLE) */ if (WTERMSIG (status) == SIGKILL) mark = TLE; else if (WTERMSIG (status) == SIGXFSZ) mark = OLE; else if (WTERMSIG (status) == SIGHUP) mark = RF; else if (WTERMSIG (status) == SIGPIPE) mark = IE; else printstats ("Command terminated by signal (%d: %s)\n", WTERMSIG (status), name (WTERMSIG (status))); } else if (WIFSTOPPED (status) != 0) printstats ("Command terminated by signal (%d: %s)\n", WSTOPSIG (status), name (WSTOPSIG (status))); else printstats ("OK\n"); if (mark == TLE) { /* We know the child has terminated at right time(OS did). * * But seing 1.990 as TLE while limit 2.0 is confusing. * * So here is small adjustment for presentation. */ usage.ru_utime.tv_sec = profile.cpu; usage.ru_utime.tv_usec = 0; printstats ("Time Limit Exceeded\n"); } else if (mark == OLE) printstats ("Output Limit Exceeded\n"); else if (mark == RTLE) printstats ("Time Limit Exceeded\n"); else if (mark == RF) printstats ("Invalid Function\n"); else if (mark == IE) printstats ("Internal Error\n"); } } printstats ("elapsed time: %d seconds\n", ttarget - tsource); printstats ("memory usage: %d kbytes\n", mem); printstats ("cpu usage: %0.3f seconds\n", (float) miliseconds (&usage.ru_utime) / 1000.0); } } fclose (redirect); return (EXIT_SUCCESS); }
int main (int argc, char **argv, char **envp) { struct rusage usage; char **p; int status, mem; int tsource, ttarget; int v; redirect = stderr; safe_signal (SIGPIPE, SIG_DFL); tsource = time (NULL); p = parse (argv); if (p == NULL) { printusage (argv); return (EXIT_FAILURE); } else { /* fprintf (stderr, "profile: \"%s\"\n", limit->name); fprintf (stderr, " cpu=%u\n mem=%u\n", (unsigned int) limit->cpu, (unsigned int) limit->memory); fprintf (stderr, " core=%u\n stack=%u\n", (unsigned int) limit->core, (unsigned int) limit->stack); fprintf (stderr, " fsize=%u\n nproc=%u\n", (unsigned int) limit->fsize, (unsigned int) limit->nproc); fprintf (stderr, " minuid=%u\n maxuid=%u\n", (unsigned int) limit->minuid, (unsigned int) limit->maxuid); fprintf (stderr, " clock=%u\n", (unsigned int) limit->clock); */ /* Still missing: get an unused uid from interval */ if (profile.minuid != profile.maxuid) { srand (time (NULL) ^ getpid ()); profile.minuid += rand () % (profile.maxuid - profile.minuid); } if (setuid (profile.minuid) < 0) error (NULL); if (strcmp (usage_file, "/dev/null") != 0) { redirect = fopen (usage_file, "w"); chmod (usage_file, 0644); if (redirect == NULL) error ("Couldn't open redirection file\n"); } if (getuid () == 0) error ("Not changing the uid to an unpriviledged one is a BAD ideia"); if (signal (SIGALRM, wallclock) == SIG_ERR) error ("Couldn't install signal handler"); if (alarm (profile.clock) != 0) error ("Couldn't set alarm"); pid = fork (); if (pid < 0) error (NULL); if (pid == 0) { /* change to chroot dir */ if (0 != chdir(chroot_dir)) { kill (getpid (), SIGPIPE); error ("Cannot change to chroot dir"); } /* chroot to judge dir */ if (0 != chroot(chroot_dir)) { kill (getpid (), SIGPIPE); error ("Cannot chroot"); } /* change to run dir */ if (0 != chdir(run_dir)) { kill (getpid (), SIGPIPE); error ("Cannot change to rundir"); } if (setuid (profile.minuid) < 0) error (NULL); if (getuid () == 0) error ("Not changing the uid to an unpriviledged one is a BAD ideia"); /* set priority */ if (0 != setpriority(PRIO_USER,profile.minuid,NICE_LEVEL)) { kill (getpid (), SIGPIPE); error (NULL); } /* Set Address space limit, 1 mbyte tolerancy (librarys also count!) */ /*setlimit (RLIMIT_AS, (1024 + limit->memory) * 1024); */ setlimit (RLIMIT_CORE, profile.core * 1024); setlimit (RLIMIT_STACK, profile.stack * 1024); setlimit (RLIMIT_FSIZE, profile.fsize * 1024); setlimit (RLIMIT_NPROC, profile.nproc); setlimit (RLIMIT_CPU, profile.cpu); /* Execute the program */ if (execve (*p, p, envp) < 0) { kill (getpid (), SIGPIPE); error (NULL); } } else { if (setuid (profile.minuid) < 0) error (NULL); if (getuid () == 0) error ("Not changing the uid to an unpriviledged one is a BAD ideia"); mark = OK; /* Poll at INTERVAL ms and determine the maximum * * memory usage, exit when the child terminates */ mem = 64; do { msleep (INTERVAL); mem = max (mem, memusage (pid)); if (mem > profile.memory) { terminate (pid); mark = MLE; } do v = wait4 (pid, &status, WNOHANG | WUNTRACED, &usage); while ((v < 0) && (errno != EINTR)); if (v < 0) error (NULL); } while (v == 0); ttarget = time (NULL); if (mark == MLE) printstats ("Memory Limit Exceeded\n"); else if (mark == RTLE) printstats ("Time Limit Exceeded\n"); else { if (WIFEXITED (status) != 0) { if (WEXITSTATUS (status) != 0) printstats ("Command exited with non-zero status (%d)\n", WEXITSTATUS (status)); else printstats ("OK\n"); } else { if (WIFSIGNALED (status) != 0) { /* Was killed for a TLE (or was it an OLE) */ if (WTERMSIG (status) == SIGKILL) mark = TLE; else if (WTERMSIG (status) == SIGXFSZ) mark = OLE; else if (WTERMSIG (status) == SIGHUP) mark = RF; else if (WTERMSIG (status) == SIGPIPE) mark = IE; else printstats ("Command terminated by signal (%d: %s)\n", WTERMSIG (status), name (WTERMSIG (status))); } else if (WIFSTOPPED (status) != 0) printstats ("Command terminated by signal (%d: %s)\n", WSTOPSIG (status), name (WSTOPSIG (status))); else printstats ("OK\n"); if (mark == TLE) { /* Adjust the timings... although we know the child * * was been killed just in the right time seing 1.990 * * as TLE when the limit is 2 seconds is anoying */ usage.ru_utime.tv_sec = profile.cpu; usage.ru_utime.tv_usec = 0; printstats ("Time Limit Exceeded\n"); } else if (mark == OLE) printstats ("Output Limit Exceeded\n"); else if (mark == RTLE) printstats ("Time Limit Exceeded\n"); else if (mark == RF) printstats ("Invalid Function\n"); else if (mark == IE) printstats ("Internal Error\n"); } } printstats ("elapsed time: %d seconds\n", ttarget - tsource); printstats ("memory usage: %d kbytes\n", mem); printstats ("cpu usage: %0.3f seconds\n", (float) miliseconds (&usage.ru_utime) / 1000.0); } } fclose (redirect); return (EXIT_SUCCESS); }