timeout.c 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. /* execute a program with a timeout by alarm(2) */
  2. #define _GNU_SOURCE
  3. #include <unistd.h>
  4. #include <sys/types.h>
  5. #include <sys/wait.h>
  6. #include <signal.h>
  7. #include <stdio.h>
  8. #include <string.h>
  9. #include <stdlib.h>
  10. #include <errno.h>
  11. static const char *argv0;
  12. static char **prgargv;
  13. static pid_t pid;
  14. static int signo;
  15. static unsigned int timeout;
  16. static void timedout(int sig)
  17. {
  18. fprintf(stderr, "%s[%d]: %s[%d] timed out after %u sec\n",
  19. argv0, getpid(), *prgargv, pid, timeout);
  20. if (pid)
  21. kill(-pid, signo);
  22. }
  23. static void interrupted(int sig)
  24. {
  25. alarm(0);
  26. if (pid)
  27. kill(-pid, sig);
  28. }
  29. static void usage()
  30. {
  31. fprintf(stderr, "%s <timeout-seconds> [-<signal>] program ...\n"
  32. "Where <signal> is a signal number (see kill -l).\n"
  33. "Some symbolic names recognized. KILL used by default\n",
  34. argv0);
  35. exit(1);
  36. }
  37. static struct {
  38. const char *name;
  39. int signo;
  40. } known[] = {
  41. {"HUP", SIGHUP},
  42. {"INT", SIGINT},
  43. {"QUIT", SIGQUIT},
  44. {"ILL", SIGILL},
  45. {"TRAP", SIGTRAP},
  46. {"ABRT", SIGABRT},
  47. {"BUS", SIGBUS},
  48. {"FPE", SIGFPE},
  49. {"KILL", SIGKILL},
  50. {"USR1", SIGUSR1},
  51. {"SEGV", SIGSEGV},
  52. {"USR2", SIGUSR2},
  53. {"PIPE", SIGPIPE},
  54. {"ALRM", SIGALRM},
  55. {"TERM", SIGTERM},
  56. {"STKFLT", SIGSTKFLT},
  57. {"CHLD", SIGCHLD},
  58. {"CONT", SIGCONT},
  59. {"STOP", SIGSTOP},
  60. {"TSTP", SIGTSTP},
  61. {"TTIN", SIGTTIN},
  62. {"TTOU", SIGTTOU},
  63. {"URG", SIGURG},
  64. {"XCPU", SIGXCPU},
  65. {"XFSZ", SIGXFSZ},
  66. {"VTALRM", SIGVTALRM},
  67. {"PROF", SIGPROF},
  68. {"WINCH", SIGWINCH},
  69. {"IO", SIGIO},
  70. {"PWR", SIGPWR},
  71. {"SYS", SIGSYS},
  72. };
  73. static int signo_arg(const char *arg)
  74. {
  75. if (*arg == '-') {
  76. char *p;
  77. int s = strtol(++arg, &p, 10);
  78. if (!*p && p > arg && s > 0 && s < _NSIG) {
  79. signo = s;
  80. return 1;
  81. }
  82. if (!strncasecmp(arg, "SIG", 3))
  83. arg += 3;
  84. for (s = 0; s < sizeof(known)/sizeof(*known); ++s)
  85. if (!strcasecmp(arg, known[s].name)) {
  86. signo = known[s].signo;
  87. return 1;
  88. }
  89. }
  90. return 0;
  91. }
  92. int main(int argc, char** argv)
  93. {
  94. argv0 = strrchr(*argv, '/');
  95. if (argv0)
  96. ++argv0;
  97. else
  98. argv0 = *argv;
  99. signal(SIGALRM, timedout);
  100. signal(SIGINT, interrupted);
  101. signal(SIGHUP, interrupted);
  102. ++argv;
  103. if (!*argv)
  104. usage();
  105. if (signo_arg(*argv))
  106. ++argv;
  107. if (sscanf(*argv, "%u", &timeout) == 1)
  108. ++argv;
  109. else
  110. usage();
  111. if (!signo && signo_arg(*argv))
  112. ++argv;
  113. if (!signo)
  114. signo = SIGKILL;
  115. if (!*argv)
  116. usage();
  117. prgargv = argv;
  118. alarm(timeout);
  119. pid = fork();
  120. if (!pid) {
  121. signal(SIGALRM, SIG_DFL);
  122. signal(SIGCHLD, SIG_DFL);
  123. signal(SIGINT, SIG_DFL);
  124. signal(SIGHUP, SIG_DFL);
  125. setpgid(0, 0);
  126. execvp(*prgargv, prgargv);
  127. fprintf(stderr, "%s: %s: %s\n",
  128. argv0, *prgargv, strerror(errno));
  129. _exit(2);
  130. } else if (pid < 0) {
  131. fprintf(stderr, "%s: %s\n", argv0, strerror(errno));
  132. } else {
  133. int status;
  134. while (waitpid(pid, &status, 0) < 0 && EINTR == errno)
  135. ;
  136. alarm(0);
  137. if (WIFEXITED(status))
  138. return WEXITSTATUS(status);
  139. if (WIFSIGNALED(status)) {
  140. /*
  141. * Some signals are special, lets die with
  142. * the same signal as child process
  143. */
  144. if (WTERMSIG(status) == SIGHUP ||
  145. WTERMSIG(status) == SIGINT ||
  146. WTERMSIG(status) == SIGTERM ||
  147. WTERMSIG(status) == SIGQUIT ||
  148. WTERMSIG(status) == SIGKILL) {
  149. signal(WTERMSIG(status), SIG_DFL);
  150. raise(WTERMSIG(status));
  151. }
  152. fprintf(stderr, "%s: %s: %s\n",
  153. argv0, *prgargv, strsignal(WTERMSIG(status)));
  154. }
  155. else
  156. fprintf(stderr, "%s died\n", *prgargv);
  157. }
  158. return 2;
  159. }