Back to index

glibc  2.9
test-skeleton.c
Go to the documentation of this file.
00001 /* Skeleton for test programs.
00002    Copyright (C) 1998,2000-2004, 2005 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
00005 
00006    The GNU C Library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Lesser General Public
00008    License as published by the Free Software Foundation; either
00009    version 2.1 of the License, or (at your option) any later version.
00010 
00011    The GNU C Library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Lesser General Public License for more details.
00015 
00016    You should have received a copy of the GNU Lesser General Public
00017    License along with the GNU C Library; if not, write to the Free
00018    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00019    02111-1307 USA.  */
00020 
00021 #include <errno.h>
00022 #include <getopt.h>
00023 #include <malloc.h>
00024 #include <search.h>
00025 #include <signal.h>
00026 #include <stdio.h>
00027 #include <stdlib.h>
00028 #include <string.h>
00029 #include <unistd.h>
00030 #include <sys/resource.h>
00031 #include <sys/wait.h>
00032 #include <sys/param.h>
00033 #include <time.h>
00034 
00035 /* The test function is normally called `do_test' and it is called
00036    with argc and argv as the arguments.  We nevertheless provide the
00037    possibility to overwrite this name.  */
00038 #ifndef TEST_FUNCTION
00039 # define TEST_FUNCTION do_test (argc, argv)
00040 #endif
00041 
00042 #ifndef TEST_DATA_LIMIT
00043 # define TEST_DATA_LIMIT (64 << 20) /* Data limit (bytes) to run with.  */
00044 #endif
00045 
00046 #define OPT_DIRECT 1000
00047 #define OPT_TESTDIR 1001
00048 
00049 static struct option options[] =
00050 {
00051 #ifdef CMDLINE_OPTIONS
00052   CMDLINE_OPTIONS
00053 #endif
00054   { "direct", no_argument, NULL, OPT_DIRECT },
00055   { "test-dir", required_argument, NULL, OPT_TESTDIR },
00056   { NULL, 0, NULL, 0 }
00057 };
00058 
00059 /* PID of the test itself.  */
00060 static pid_t pid;
00061 
00062 /* Directory to place temporary files in.  */
00063 static const char *test_dir;
00064 
00065 /* List of temporary files.  */
00066 struct temp_name_list
00067 {
00068   struct qelem q;
00069   const char *name;
00070 } *temp_name_list;
00071 
00072 /* Add temporary files in list.  */
00073 static void
00074 __attribute__ ((unused))
00075 add_temp_file (const char *name)
00076 {
00077   struct temp_name_list *newp
00078     = (struct temp_name_list *) calloc (sizeof (*newp), 1);
00079   if (newp != NULL)
00080     {
00081       newp->name = name;
00082       if (temp_name_list == NULL)
00083        temp_name_list = (struct temp_name_list *) &newp->q;
00084       else
00085        insque (newp, temp_name_list);
00086     }
00087 }
00088 
00089 /* Delete all temporary files.  */
00090 static void
00091 delete_temp_files (void)
00092 {
00093   while (temp_name_list != NULL)
00094     {
00095       remove (temp_name_list->name);
00096       temp_name_list = (struct temp_name_list *) temp_name_list->q.q_forw;
00097     }
00098 }
00099 
00100 /* Create a temporary file.  */
00101 static int
00102 __attribute__ ((unused))
00103 create_temp_file (const char *base, char **filename)
00104 {
00105   char *fname;
00106   int fd;
00107 
00108   fname = (char *) malloc (strlen (test_dir) + 1 + strlen (base)
00109                         + sizeof ("XXXXXX"));
00110   if (fname == NULL)
00111     {
00112       puts ("out of memory");
00113       return -1;
00114     }
00115   strcpy (stpcpy (stpcpy (stpcpy (fname, test_dir), "/"), base), "XXXXXX");
00116 
00117   fd = mkstemp (fname);
00118   if (fd == -1)
00119     {
00120       printf ("cannot open temporary file '%s': %m\n", fname);
00121       free (fname);
00122       return -1;
00123     }
00124 
00125   add_temp_file (fname);
00126   if (filename != NULL)
00127     *filename = fname;
00128 
00129   return fd;
00130 }
00131 
00132 /* Timeout handler.  We kill the child and exit with an error.  */
00133 static void
00134 __attribute__ ((noreturn))
00135 timeout_handler (int sig __attribute__ ((unused)))
00136 {
00137   int killed;
00138   int status;
00139 
00140   /* Send signal.  */
00141   kill (pid, SIGKILL);
00142 
00143   /* Wait for it to terminate.  */
00144   int i;
00145   for (i = 0; i < 5; ++i)
00146     {
00147       killed = waitpid (pid, &status, WNOHANG|WUNTRACED);
00148       if (killed != 0)
00149        break;
00150 
00151       /* Delay, give the system time to process the kill.  If the
00152         nanosleep() call return prematurely, all the better.  We
00153         won't restart it since this probably means the child process
00154         finally died.  */
00155       struct timespec ts;
00156       ts.tv_sec = 0;
00157       ts.tv_nsec = 100000000;
00158       nanosleep (&ts, NULL);
00159     }
00160   if (killed != 0 && killed != pid)
00161     {
00162       perror ("Failed to kill test process");
00163       exit (1);
00164     }
00165 
00166 #ifdef CLEANUP_HANDLER
00167   CLEANUP_HANDLER;
00168 #endif
00169 
00170   /* If we expected this signal: good!  */
00171 #ifdef EXPECTED_SIGNAL
00172   if (EXPECTED_SIGNAL == SIGALRM)
00173     exit (0);
00174 #endif
00175 
00176   if (killed == 0 || (WIFSIGNALED (status) && WTERMSIG (status) == SIGKILL))
00177     fputs ("Timed out: killed the child process\n", stderr);
00178   else if (WIFSTOPPED (status))
00179     fprintf (stderr, "Timed out: the child process was %s\n",
00180             strsignal (WSTOPSIG (status)));
00181   else if (WIFSIGNALED (status))
00182     fprintf (stderr, "Timed out: the child process got signal %s\n",
00183             strsignal (WTERMSIG (status)));
00184   else
00185     fprintf (stderr, "Timed out: killed the child process but it exited %d\n",
00186             WEXITSTATUS (status));
00187 
00188   /* Exit with an error.  */
00189   exit (1);
00190 }
00191 
00192 /* We provide the entry point here.  */
00193 int
00194 main (int argc, char *argv[])
00195 {
00196   int direct = 0;    /* Directly call the test function?  */
00197   int status;
00198   int opt;
00199   unsigned int timeoutfactor = 1;
00200   pid_t termpid;
00201 
00202   /* Make uses of freed and uninitialized memory known.  */
00203   mallopt (M_PERTURB, 42);
00204 
00205 #ifdef STDOUT_UNBUFFERED
00206   setbuf (stdout, NULL);
00207 #endif
00208 
00209   while ((opt = getopt_long (argc, argv, "+", options, NULL)) != -1)
00210     switch (opt)
00211       {
00212       case '?':
00213        exit (1);
00214       case OPT_DIRECT:
00215        direct = 1;
00216        break;
00217       case OPT_TESTDIR:
00218        test_dir = optarg;
00219        break;
00220 #ifdef CMDLINE_PROCESS
00221        CMDLINE_PROCESS
00222 #endif
00223       }
00224 
00225   /* If set, read the test TIMEOUTFACTOR value from the environment.
00226      This value is used to scale the default test timeout values. */
00227   char *envstr_timeoutfactor = getenv ("TIMEOUTFACTOR");
00228   if (envstr_timeoutfactor != NULL)
00229     {
00230       char *envstr_conv = envstr_timeoutfactor;
00231       unsigned long int env_fact;
00232 
00233       env_fact = strtoul (envstr_timeoutfactor, &envstr_conv, 0);
00234       if (*envstr_conv == '\0' && envstr_conv != envstr_timeoutfactor)
00235        timeoutfactor = MAX (env_fact, 1);
00236     }
00237 
00238   /* Set TMPDIR to specified test directory.  */
00239   if (test_dir != NULL)
00240     {
00241       setenv ("TMPDIR", test_dir, 1);
00242 
00243       if (chdir (test_dir) < 0)
00244        {
00245          perror ("chdir");
00246          exit (1);
00247        }
00248     }
00249   else
00250     {
00251       test_dir = getenv ("TMPDIR");
00252       if (test_dir == NULL || test_dir[0] == '\0')
00253        test_dir = "/tmp";
00254     }
00255 
00256   /* Make sure we see all message, even those on stdout.  */
00257   setvbuf (stdout, NULL, _IONBF, 0);
00258 
00259   /* make sure temporary files are deleted.  */
00260   atexit (delete_temp_files);
00261 
00262   /* Correct for the possible parameters.  */
00263   argv[optind - 1] = argv[0];
00264   argv += optind - 1;
00265   argc -= optind - 1;
00266 
00267   /* Call the initializing function, if one is available.  */
00268 #ifdef PREPARE
00269   PREPARE (argc, argv);
00270 #endif
00271 
00272   /* If we are not expected to fork run the function immediately.  */
00273   if (direct)
00274     return TEST_FUNCTION;
00275 
00276   /* Set up the test environment:
00277      - prevent core dumps
00278      - set up the timer
00279      - fork and execute the function.  */
00280 
00281   pid = fork ();
00282   if (pid == 0)
00283     {
00284       /* This is the child.  */
00285 #ifdef RLIMIT_CORE
00286       /* Try to avoid dumping core.  */
00287       struct rlimit core_limit;
00288       core_limit.rlim_cur = 0;
00289       core_limit.rlim_max = 0;
00290       setrlimit (RLIMIT_CORE, &core_limit);
00291 #endif
00292 
00293 #ifdef RLIMIT_DATA
00294       /* Try to avoid eating all memory if a test leaks.  */
00295       struct rlimit data_limit;
00296       if (getrlimit (RLIMIT_DATA, &data_limit) == 0)
00297        {
00298          if (TEST_DATA_LIMIT == RLIM_INFINITY)
00299            data_limit.rlim_cur = data_limit.rlim_max;
00300          else if (data_limit.rlim_cur > (rlim_t) TEST_DATA_LIMIT)
00301            data_limit.rlim_cur = MIN ((rlim_t) TEST_DATA_LIMIT,
00302                                    data_limit.rlim_max);
00303          if (setrlimit (RLIMIT_DATA, &data_limit) < 0)
00304            perror ("setrlimit: RLIMIT_DATA");
00305        }
00306       else
00307        perror ("getrlimit: RLIMIT_DATA");
00308 #endif
00309 
00310       /* We put the test process in its own pgrp so that if it bogusly
00311         generates any job control signals, they won't hit the whole build.  */
00312       setpgid (0, 0);
00313 
00314       /* Execute the test function and exit with the return value.   */
00315       exit (TEST_FUNCTION);
00316     }
00317   else if (pid < 0)
00318     {
00319       perror ("Cannot fork test program");
00320       exit (1);
00321     }
00322 
00323   /* Set timeout.  */
00324 #ifndef TIMEOUT
00325   /* Default timeout is two seconds.  */
00326 # define TIMEOUT 2
00327 #endif
00328   signal (SIGALRM, timeout_handler);
00329   alarm (TIMEOUT * timeoutfactor);
00330 
00331   /* Wait for the regular termination.  */
00332   termpid = TEMP_FAILURE_RETRY (waitpid (pid, &status, 0));
00333   if (termpid == -1)
00334     {
00335       printf ("Waiting for test program failed: %m\n");
00336       exit (1);
00337     }
00338   if (termpid != pid)
00339     {
00340       printf ("Oops, wrong test program terminated: expected %ld, got %ld\n",
00341              (long int) pid, (long int) termpid);
00342       exit (1);
00343     }
00344 
00345 #ifndef EXPECTED_SIGNAL
00346   /* We don't expect any signal.  */
00347 # define EXPECTED_SIGNAL 0
00348 #endif
00349   if (WTERMSIG (status) != EXPECTED_SIGNAL)
00350     {
00351       if (EXPECTED_SIGNAL != 0)
00352        {
00353          if (WTERMSIG (status) == 0)
00354            fprintf (stderr,
00355                    "Expected signal '%s' from child, got none\n",
00356                    strsignal (EXPECTED_SIGNAL));
00357          else
00358            fprintf (stderr,
00359                    "Incorrect signal from child: got `%s', need `%s'\n",
00360                    strsignal (WTERMSIG (status)),
00361                    strsignal (EXPECTED_SIGNAL));
00362        }
00363       else
00364        fprintf (stderr, "Didn't expect signal from child: got `%s'\n",
00365                strsignal (WTERMSIG (status)));
00366       exit (1);
00367     }
00368 
00369   /* Simply exit with the return value of the test.  */
00370 #ifndef EXPECTED_STATUS
00371   return WEXITSTATUS (status);
00372 #else
00373   if (WEXITSTATUS (status) != EXPECTED_STATUS)
00374     {
00375       fprintf (stderr, "Expected status %d, got %d\n",
00376               EXPECTED_STATUS, WEXITSTATUS (status));
00377       exit (1);
00378     }
00379 
00380   return 0;
00381 #endif
00382 }