Advertisement
Guest User

runguard.c

a guest
Jan 19th, 2015
318
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 22.94 KB | None | 0 0
  1. /*
  2.    runguard -- run command with restrictions.
  3.    Copyright (C) 2004-2009 Jaap Eldering (eldering@a-eskwadraat.nl).
  4.  
  5.    Based on an idea from the timeout program, written by Wietse Venema
  6.    as part of The Coroner's Toolkit.
  7.  
  8.    $Id$
  9.  
  10.    This program is free software; you can redistribute it and/or modify
  11.    it under the terms of the GNU General Public License as published by
  12.    the Free Software Foundation; either version 2, or (at your option)
  13.    any later version.
  14.  
  15.    This program is distributed in the hope that it will be useful,
  16.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  17.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18.    GNU General Public License for more details.
  19.  
  20.    You should have received a copy of the GNU General Public License
  21.    along with this program; if not, write to the Free Software Foundation,
  22.    Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  23.  
  24.  
  25.    Program specifications:
  26.  
  27.    This program will run the specified command in a separate process
  28.    group (session) and apply the restrictions as specified after
  29.    forking, before executing the command.
  30.  
  31.    The stdin and stdout streams are passed to the command and runguard
  32.    does not read or write to these. Error and verbose messages from
  33.    runguard are by default written to stderr, hence mixed with stderr
  34.    output of the command.
  35.  
  36.    The command and its children are sent a SIGTERM after the runtime
  37.    has passed, followed by a SIGKILL after 'killdelay'.
  38.  */
  39.  
  40. /* For having access to isfinite() macro in math.h and some functions. */
  41. #define _ISOC99_SOURCE
  42. #define _BSD_SOURCE
  43.  
  44. #include <sys/types.h>
  45. #include <sys/wait.h>
  46. #include <sys/param.h>
  47. #include <sys/time.h>
  48. #include <sys/resource.h>
  49. #include <errno.h>
  50. #include <signal.h>
  51. #include <stdlib.h>
  52. #include <unistd.h>
  53. #include <string.h>
  54. #include <stdarg.h>
  55. #include <stdio.h>
  56. #include <getopt.h>
  57. #include <pwd.h>
  58. #include <grp.h>
  59. #include <time.h>
  60. #include <math.h>
  61.  
  62. /* Some system/site specific config: VALID_USERS, CHROOT_PREFIX */
  63. #define VALID_USERS "@RUNUSER@"
  64.  
  65. #define CHROOT_PREFIX "@CHROOTDIR@"
  66.  
  67. #define PROGRAM "runguard"
  68. #define VERSION "$Rev$"
  69. #define AUTHORS "Jaap Eldering"
  70.  
  71. const struct timespec killdelay = { 0, 100000000L }; /* 0.1 seconds */
  72.  
  73. extern int errno;
  74.  
  75. #ifndef _GNU_SOURCE
  76. extern char **environ;
  77. #endif
  78.  
  79. const int exit_failure = -1;
  80.  
  81. char  *progname;
  82. char  *cmdname;
  83. char **cmdargs;
  84. char  *rootdir;
  85. char  *outputfilename;
  86.  
  87. int runuid;
  88. int rungid;
  89. int use_root;
  90. int use_time;
  91. int use_user;
  92. int use_group;
  93. int use_output;
  94. int no_coredump;
  95. int be_verbose;
  96. int be_quiet;
  97. int show_help;
  98. int show_version;
  99.  
  100. long long runtime; /* in microseconds */
  101. rlim_t cputime;
  102. rlim_t memsize;
  103. rlim_t filesize;
  104. rlim_t nproc;
  105.  
  106. pid_t child_pid;
  107.  
  108. struct timeval starttime, endtime;
  109.  
  110. struct option const long_opts[] = {
  111.         {"root",    required_argument, NULL,         'r'},
  112.         {"user",    required_argument, NULL,         'u'},
  113.         {"group",   required_argument, NULL,         'g'},
  114.         {"time",    required_argument, NULL,         't'},
  115.         {"cputime", required_argument, NULL,         'C'},
  116.         {"memsize", required_argument, NULL,         'm'},
  117.         {"filesize",required_argument, NULL,         'f'},
  118.         {"nproc",   required_argument, NULL,         'p'},
  119.         {"no-core", no_argument,       NULL,         'c'},
  120.         {"output",  required_argument, NULL,         'o'},
  121.         {"verbose", no_argument,       NULL,         'v'},
  122.         {"quiet",   no_argument,       NULL,         'q'},
  123.         {"help",    no_argument,       &show_help,    1 },
  124.         {"version", no_argument,       &show_version, 1 },
  125.         { NULL,     0,                 NULL,          0 }
  126. };
  127.  
  128. void warning(const char *format, ...)
  129. {
  130.         va_list ap;
  131.         va_start(ap,format);
  132.  
  133.         if ( ! be_quiet ) {
  134.                 fprintf(stderr,"%s: warning: ",progname);
  135.                 vfprintf(stderr,format,ap);
  136.                 fprintf(stderr,"\n");
  137.         }
  138.  
  139.         va_end(ap);
  140. }
  141.  
  142. void verbose(const char *format, ...)
  143. {
  144.         va_list ap;
  145.         va_start(ap,format);
  146.  
  147.         if ( ! be_quiet && be_verbose ) {
  148.                 fprintf(stderr,"%s: verbose: ",progname);
  149.                 vfprintf(stderr,format,ap);
  150.                 fprintf(stderr,"\n");
  151.         }
  152.  
  153.         va_end(ap);
  154. }
  155.  
  156. void error(int errnum, const char *format, ...)
  157. {
  158.         va_list ap;
  159.         va_start(ap,format);
  160.  
  161.         fprintf(stderr,"%s",progname);
  162.  
  163.         if ( format!=NULL ) {
  164.                 fprintf(stderr,": ");
  165.                 vfprintf(stderr,format,ap);
  166.         }
  167.         if ( errnum!=0 ) {
  168.                 fprintf(stderr,": %s",strerror(errnum));
  169.         }
  170.         if ( format==NULL && errnum==0 ) {
  171.                 fprintf(stderr,": unknown error");
  172.         }
  173.  
  174.         fprintf(stderr,"\nTry `%s --help' for more information.\n",progname);
  175.         va_end(ap);
  176.  
  177.         exit(exit_failure);
  178. }
  179.  
  180. void version()
  181. {
  182.         printf("\
  183. %s -- version %s\n\
  184. Written by %s\n\n\
  185. %s comes with ABSOLUTELY NO WARRANTY.  This is free software, and you\n\
  186. are welcome to redistribute it under certain conditions.  See the GNU\n\
  187. General Public Licence for details.\n",PROGRAM,VERSION,AUTHORS,PROGRAM);
  188.         exit(0);
  189. }
  190.  
  191. void usage()
  192. {
  193.         printf("\
  194. Usage: %s [OPTION]... COMMAND...\n\
  195. Run COMMAND with restrictions.\n\
  196. \n\
  197.  -r, --root=ROOT      run COMMAND with root directory set to ROOT\n\
  198.  -u, --user=USER      run COMMAND as user with username or ID USER\n\
  199.  -g, --group=GROUP    run COMMAND under group with name or ID GROUP\n\
  200.  -t, --time=TIME      kill COMMAND if still running after TIME seconds (float)\n\
  201.  -C, --cputime=TIME   set maximum CPU time to TIME seconds (integer)\n\
  202.  -m, --memsize=SIZE   set all (total, stack, etc) memory limits to SIZE kB\n\
  203.  -f, --filesize=SIZE  set maximum created filesize to SIZE kB\n\
  204.  -p, --nproc=N        set maximum no. processes to N\n\
  205.  -c, --no-core        disable core dumps\n\
  206.  -o, --output=FILE    write actual runtime to FILE\n\
  207.  -v, --verbose        display some extra warnings and information\n\
  208.  -q, --quiet          suppress all warnings and verbose output\n\
  209.      --help           display this help and exit\n\
  210.      --version        output version information and exit\n\
  211. \n\
  212. Note that root privileges are needed for the `root' and `user' options.\n\
  213. When run setuid without the `user' option, the user ID is set to the\n\
  214. real user ID.\n",progname);
  215.         exit(0);
  216. }
  217.  
  218. void outputtime()
  219. {
  220.         FILE  *outputfile;
  221.         double timediff; /* in seconds */
  222.  
  223.         if ( gettimeofday(&endtime,NULL) ) error(errno,"getting time");
  224.  
  225.         timediff = (endtime.tv_sec  - starttime.tv_sec ) +
  226.                    (endtime.tv_usec - starttime.tv_usec)*1E-6;
  227.  
  228.         verbose("runtime is %.3lf seconds",timediff);
  229.  
  230.         if ( use_output ) {
  231.                 verbose("writing runtime to file `%s'",outputfilename);
  232.  
  233.                 if ( (outputfile = fopen(outputfilename,"w"))==NULL ) {
  234.                         error(errno,"cannot open `%s'",outputfilename);
  235.                 }
  236.                 if ( fprintf(outputfile,"%.3lf\n",timediff)==0 ) {
  237.                         error(0,"cannot write to file `%s'",outputfile);
  238.                 }
  239.                 if ( fclose(outputfile) ) {
  240.                         error(errno,"closing file `%s'",outputfilename);
  241.                 }
  242.         }
  243. }
  244.  
  245. void terminate(int sig)
  246. {
  247.         struct sigaction sigact;
  248.  
  249.         /* Reset signal handlers to default */
  250.         sigact.sa_handler = SIG_DFL;
  251.         if ( sigaction(SIGTERM,&sigact,NULL)!=0 ) warning("error restoring signal handler");
  252.         if ( sigaction(SIGALRM,&sigact,NULL)!=0 ) warning("error restoring signal handler");
  253.  
  254.         if ( sig==SIGALRM ) {
  255.                 warning("timelimit reached: aborting command");
  256.         } else {
  257.                 warning("received signal %d: aborting command",sig);
  258.         }
  259.  
  260.         /* First try to kill graciously, then hard */
  261.         verbose("sending SIGTERM");
  262.         if ( kill(-child_pid,SIGTERM)!=0 ) error(errno,"sending SIGTERM to command");
  263.  
  264.         /* Prefer nanosleep over sleep because of higher resolution and
  265.            it does not interfere with signals. */
  266.         nanosleep(&killdelay,NULL);
  267.  
  268.         verbose("sending SIGKILL");
  269.         if ( kill(-child_pid,SIGKILL)!=0 ) error(errno,"sending SIGKILL to command");
  270. }
  271.  
  272. int userid(char *name)
  273. {
  274.         struct passwd *pwd;
  275.  
  276.         errno = 0; /* per the linux GETPWNAM(3) man-page */
  277.         pwd = getpwnam(name);
  278.  
  279.         if ( pwd==NULL || errno ) return -1;
  280.  
  281.         return (int) pwd->pw_uid;
  282. }
  283.  
  284. int groupid(char *name)
  285. {
  286.         struct group *grp;
  287.  
  288.         errno = 0; /* per the linux GETGRNAM(3) man-page */
  289.         grp = getgrnam(name);
  290.  
  291.         if ( grp==NULL || errno ) return -1;
  292.  
  293.         return (int) grp->gr_gid;
  294. }
  295.  
  296. inline long readoptarg(const char *desc, long minval, long maxval)
  297. {
  298.         long arg;
  299.         char *ptr;
  300.  
  301.         arg = strtol(optarg,&ptr,10);
  302.         if ( errno || *ptr!='\0' || arg<minval || arg>maxval ) {
  303.                 error(errno,"invalid %s specified: `%s'",desc,optarg);
  304.         }
  305.  
  306.         return arg;
  307. }
  308.  
  309. void setrestrictions()
  310. {
  311.         char *path;
  312.         char  cwd[PATH_MAX+1];
  313.  
  314.         struct rlimit lim;
  315.  
  316.         /* Clear environment to prevent all kinds of security holes, save PATH */
  317.         path = getenv("PATH");
  318.         environ[0] = NULL;
  319.         /* FIXME: Clean path before setting it again? */
  320.         if ( path!=NULL ) setenv("PATH",path,1);
  321.  
  322.         /* Set resource limits: must be root to raise hard limits.
  323.            Note that limits can thus be raised from the systems defaults! */
  324.  
  325.         /* First define shorthand macro function */
  326. #define setlim(type) \
  327.         if ( setrlimit(RLIMIT_ ## type, &lim)!=0 ) { \
  328.                 if ( errno==EPERM ) { \
  329.                         warning("no permission to set resource RLIMIT_" #type); \
  330.                 } else { \
  331.                         error(errno,"setting resource RLIMIT_" #type); \
  332.                 } \
  333.         }
  334.  
  335.         if ( cputime!=RLIM_INFINITY ) {
  336.                 verbose("setting CPU-time limit to %d seconds",(int)cputime);
  337.         }
  338.         lim.rlim_cur = lim.rlim_max = cputime;
  339.         setlim(CPU);
  340.  
  341.         if ( memsize!=RLIM_INFINITY ) {
  342.                 verbose("setting memory limits to %d bytes",(int)memsize);
  343.         }
  344.         lim.rlim_cur = lim.rlim_max = memsize;
  345.         setlim(AS);
  346.         setlim(DATA);
  347.         setlim(STACK);
  348.         setlim(MEMLOCK);
  349.  
  350.         if ( filesize!=RLIM_INFINITY ) {
  351.                 verbose("setting filesize limit to %d bytes",(int)filesize);
  352.         }
  353.         lim.rlim_cur = lim.rlim_max = filesize;
  354.         setlim(FSIZE);
  355.  
  356.         if ( nproc!=RLIM_INFINITY ) {
  357.                 verbose("setting process limit to %d",(int)nproc);
  358.         }
  359.         lim.rlim_cur = lim.rlim_max = nproc;
  360.         setlim(NPROC);
  361.  
  362. #undef setlim
  363.  
  364.         if ( no_coredump ) {
  365.                 verbose("disabling core dumps");
  366.                 lim.rlim_cur = lim.rlim_max = 0;
  367.                 if ( setrlimit(RLIMIT_CORE,&lim)!=0 ) error(errno,"disabling core dumps");
  368.         }
  369.  
  370.         /* Set root-directory and change directory to there. */
  371.         if ( use_root ) {
  372.                 /* Small security issue: when running setuid-root, people can find
  373.                    out which directories exist from error message. */
  374.                 if ( chdir(rootdir) ) error(errno,"cannot chdir to `%s'",rootdir);
  375.  
  376.                 /* Get absolute pathname of rootdir, by reading it. */
  377.                 if ( getcwd(cwd,PATH_MAX)==NULL ) error(errno,"cannot get directory");
  378.                 if ( cwd[strlen(cwd)-1]!='/' ) strcat(cwd,"/");
  379.  
  380.                 /* Canonicalize CHROOT_PREFIX. */
  381.                 if ( (path = (char *) malloc(PATH_MAX+1))==NULL ) {
  382.                         error(errno,"allocating memory");
  383.                 }
  384.                 if ( realpath(CHROOT_PREFIX,path)==NULL ) {
  385.                         error(errno,"cannot canonicalize path '%s'",CHROOT_PREFIX);
  386.                 }
  387.  
  388.                 /* Check that we are within prescribed path. */
  389.                 if ( strncmp(cwd,path,strlen(path))!=0 ) {
  390.                         error(0,"invalid root: must be within `%s'",path);
  391.                 }
  392.                 free(path);
  393.  
  394.                 if ( chroot(".") ) error(errno,"cannot change root to `%s'",cwd);
  395.                 verbose("using root-directory `%s'",cwd);
  396.         }
  397.  
  398.         /* Set group-id (must be root for this, so before setting user). */
  399.         if ( use_group ) {
  400.                 if ( setgid(rungid) ) error(errno,"cannot set group ID to `%d'",rungid);
  401.                 verbose("using group ID `%d'",rungid);
  402.         }
  403.         /* Set user-id (must be root for this). */
  404.         if ( use_user ) {
  405.                 if ( setuid(runuid) ) error(errno,"cannot set user ID to `%d'",runuid);
  406.                 verbose("using user ID `%d' for command",runuid);
  407.         } else {
  408.                 /* Permanently reset effective uid to real uid, to prevent
  409.                    child command from having root privileges.
  410.                    Note that this means that the child runs as the same user
  411.                    as the watchdog process and can thus manipulate it, e.g. by
  412.                    sending SIGSTOP/SIGCONT! */
  413.                 if ( setuid(getuid()) ) error(errno,"cannot reset real user ID");
  414.                 verbose("reset user ID to `%d' for command",getuid());
  415.         }
  416.         if ( geteuid()==0 || getuid()==0 ) error(0,"root privileges not dropped");
  417. }
  418.  
  419. int main(int argc, char **argv)
  420. {
  421.         sigset_t sigmask;
  422.         pid_t pid;
  423.         int   status;
  424.         int   exitcode;
  425.         char *valid_users;
  426.         char *ptr;
  427.         int   opt;
  428.         double runtime_d;
  429.  
  430.         struct itimerval itimer;
  431.         struct sigaction sigact;
  432.  
  433.         progname = argv[0];
  434.  
  435.         /* Parse command-line options */
  436.         use_root = use_time = use_user = use_output = no_coredump = 0;
  437.         cputime = memsize = filesize = nproc = RLIM_INFINITY;
  438.         be_verbose = be_quiet = 0;
  439.         show_help = show_version = 0;
  440.         opterr = 0;
  441.         while ( (opt = getopt_long(argc,argv,"+r:u:g:t:C:m:f:p:co:vq",long_opts,(int *) 0))!=-1 ) {
  442.                 switch ( opt ) {
  443.                 case 0:   /* long-only option */
  444.                         break;
  445.                 case 'r': /* rootdir option */
  446.                         use_root = 1;
  447.                         rootdir = (char *) malloc(strlen(optarg)+2);
  448.                         strcpy(rootdir,optarg);
  449.                         break;
  450.                 case 'u': /* user option: uid or string */
  451.                         use_user = 1;
  452.                         runuid = strtol(optarg,&ptr,10);
  453.                         if ( errno || *ptr!='\0' ) runuid = userid(optarg);
  454.                         if ( runuid<0 ) error(0,"invalid username or ID specified: `%s'",optarg);
  455.                         break;
  456.                 case 'g': /* group option: gid or string */
  457.                         use_group = 1;
  458.                         rungid = strtol(optarg,&ptr,10);
  459.                         if ( errno || *ptr!='\0' ) rungid = groupid(optarg);
  460.                         if ( rungid<0 ) error(0,"invalid groupname or ID specified: `%s'",optarg);
  461.                         break;
  462.                 case 't': /* time option */
  463.                         use_time = 1;
  464.                         runtime_d = strtod(optarg,&ptr);
  465.                         if ( errno || *ptr!='\0' || !isfinite(runtime_d) || runtime_d<=0 ) {
  466.                                 error(errno,"invalid runtime specified: `%s'",optarg);
  467.                         }
  468.                         runtime = (int)(runtime_d*1E6);
  469.                         break;
  470.                 case 'C': /* CPU time option */
  471.                         cputime = (rlim_t) readoptarg("CPU-time limit",1,LONG_MAX);
  472.                         break;
  473.                 case 'm': /* memsize option */
  474.                         memsize = (rlim_t) readoptarg("memory limit",1,LONG_MAX);
  475.                         /* Convert limit from kB to bytes and check for overflow */
  476.                         if ( memsize!=(memsize*1024)/1024 ) {
  477.                                 memsize = RLIM_INFINITY;
  478.                         } else {
  479.                                 memsize *= 1024;
  480.                         }
  481.                         break;
  482.                 case 'f': /* filesize option */
  483.                         filesize = (rlim_t) readoptarg("filesize limit",1,LONG_MAX);
  484.                         /* Convert limit from kB to bytes and check for overflow */
  485.                         if ( filesize!=(filesize*1024)/1024 ) {
  486.                                 filesize = RLIM_INFINITY;
  487.                         } else {
  488.                                 filesize *= 1024;
  489.                         }
  490.                         break;
  491.                 case 'p': /* nproc option */
  492.                         nproc = (rlim_t) readoptarg("process limit",1,LONG_MAX);
  493.                         break;
  494.                 case 'c': /* no-core option */
  495.                         no_coredump = 1;
  496.                         break;
  497.                 case 'o': /* output option */
  498.                         use_output = 1;
  499.                         outputfilename = strdup(optarg);
  500.                         break;
  501.                 case 'v': /* verbose option */
  502.                         be_verbose = 1;
  503.                         break;
  504.                 case 'q': /* quiet option */
  505.                         be_quiet = 1;
  506.                         break;
  507.                 case ':': /* getopt error */
  508.                 case '?':
  509.                         error(0,"unknown option or missing argument `%c'",optopt);
  510.                         break;
  511.                 default:
  512.                         error(0,"getopt returned character code `%c' ??",(char)opt);
  513.                 }
  514.         }
  515.  
  516.         if ( show_help ) usage();
  517.         if ( show_version ) version();
  518.  
  519.         if ( argc<=optind ) error(0,"no command specified");
  520.  
  521.         /* Command to be executed */
  522.         cmdname = argv[optind];
  523.         cmdargs = argv+optind;
  524.  
  525.         /* Check that new uid is in list of valid uid's.
  526.            This must be done before chroot for /etc/passwd lookup. */
  527.         if ( use_user ) {
  528.                 valid_users = strdup(VALID_USERS);
  529.                 for(ptr=strtok(valid_users,","); ptr!=NULL; ptr=strtok(NULL,",")) {
  530.                         if ( runuid==userid(ptr) ) break;
  531.                 }
  532.                 if ( ptr==NULL || runuid<=0 ) error(0,"illegal user specified: %d",runuid);
  533.         }
  534.  
  535.         switch ( child_pid = fork() ) {
  536.         case -1: /* error */
  537.                 error(errno,"cannot fork");
  538.         case  0: /* run controlled command */
  539.                 /* Run the command in a separate process group so that the command
  540.                    and all its children can be killed off with one signal. */
  541.                 if ( setsid()==-1 ) error(errno,"setsid failed");
  542.  
  543.                 /* Apply all restrictions for child process. */
  544.                 setrestrictions();
  545.  
  546.                 /* And execute child command. */
  547.                 execvp(cmdname,cmdargs);
  548.                 error(errno,"cannot start `%s'",cmdname);
  549.  
  550.         default: /* become watchdog */
  551.  
  552.                 /* Shed privileges, only if not using a separate child uid,
  553.                    because in that case we may need root privileges to kill
  554.                    the child process. Do not use Linux specific setresuid()
  555.                    call with saved set-user-ID. */
  556.                 if ( !use_user ) {
  557.                         if ( setuid(getuid())!=0 ) error(errno, "setting watchdog uid");
  558.                         verbose("watchdog using user ID `%d'",getuid());
  559.                 }
  560.  
  561.                 if ( gettimeofday(&starttime,NULL) ) error(errno,"getting time");
  562.  
  563.                 /* unmask all signals */
  564.                 if ( sigemptyset(&sigmask)!=0 ) error(errno,"creating signal mask");
  565.                 if ( sigprocmask(SIG_SETMASK, &sigmask, NULL)!=0 ) {
  566.                         error(errno,"unmasking signals");
  567.                 }
  568.  
  569.                 /* Construct one-time signal handler to terminate() for TERM
  570.                    and ALRM signals. */
  571.                 if ( sigaddset(&sigmask,SIGALRM)!=0 ||
  572.                      sigaddset(&sigmask,SIGTERM)!=0 ) error(errno,"setting signal mask");
  573.  
  574.                 sigact.sa_handler = terminate;
  575.                 sigact.sa_flags   = SA_RESETHAND | SA_RESTART;
  576.                 sigact.sa_mask    = sigmask;
  577.  
  578.                 /* Kill child command when we receive SIGTERM */
  579.                 if ( sigaction(SIGTERM,&sigact,NULL)!=0 ) {
  580.                         error(errno,"installing signal handler");
  581.                 }
  582.  
  583.                 if ( use_time ) {
  584.                         /* Kill child when we receive SIGALRM */
  585.                         if ( sigaction(SIGALRM,&sigact,NULL)!=0 ) {
  586.                                 error(errno,"installing signal handler");
  587.                         }
  588.  
  589.                         /* Trigger SIGALRM via setitimer:  */
  590.                         itimer.it_interval.tv_sec  = 0;
  591.                         itimer.it_interval.tv_usec = 0;
  592.                         itimer.it_value.tv_sec  = runtime / 1000000;
  593.                         itimer.it_value.tv_usec = runtime % 1000000;
  594.  
  595.                         if ( setitimer(ITIMER_REAL,&itimer,NULL)!=0 ) {
  596.                                 error(errno,"setting timer");
  597.                         }
  598.                         verbose("using timelimit of %.3lf seconds",runtime*1E-6);
  599.                 }
  600.  
  601.                 /* Wait for the child command to finish */
  602.                 while ( (pid = wait(&status))!=-1 && pid!=child_pid );
  603.                 if ( pid!=child_pid ) error(errno,"waiting on child");
  604.  
  605.                 /* Drop root before writing to output file. */
  606.                 if ( setuid(getuid())!=0 ) error(errno,"dropping root privileges");
  607.  
  608.                 outputtime();
  609.  
  610.                 /* Test whether command has finished abnormally */
  611.                 if ( ! WIFEXITED(status) ) {
  612.                         if ( WIFSIGNALED(status) ) {
  613.                                 warning("command terminated with signal %d",WTERMSIG(status));
  614.                                 return 128+WTERMSIG(status);
  615.                         }
  616.                         if ( WIFSTOPPED(status) ) {
  617.                                 warning("command stopped with signal %d",WSTOPSIG(status));
  618.                                 return 128+WSTOPSIG(status);
  619.                         }
  620.                         error(0,"command exit status unknown: %d",status);
  621.                 }
  622.  
  623.                 /* Return the exitstatus of the command */
  624.                 exitcode = WEXITSTATUS(status);
  625.                 if ( exitcode!=0 ) verbose("command exited with exitcode %d",exitcode);
  626.                 return exitcode;
  627.         }
  628.  
  629.         /* This should never be reached */
  630.         error(0,"unexpected end of program");
  631. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement