1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 2 * COPYING NOTES 3 * 4 * timeout.c -- a timeout handler for shell commands 5 * 6 * Copyright (C) 2005-6, Roberto A. Foglietta <me@roberto.foglietta.name> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; version 2 of the License. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 20 */ 21/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 22 * REVISION NOTES: 23 * released 17-11-2005 by Roberto A. Foglietta 24 * talarm 04-12-2005 by Roberto A. Foglietta 25 * modified 05-12-2005 by Roberto A. Foglietta 26 * sizerdct 06-12-2005 by Roberto A. Foglietta 27 * splitszf 12-05-2006 by Roberto A. Foglietta 28 * rewrite 14-11-2008 vda 29 */ 30//config:config TIMEOUT 31//config: bool "timeout (5.5 kb)" 32//config: default y 33//config: help 34//config: Runs a program and watches it. If it does not terminate in 35//config: specified number of seconds, it is sent a signal. 36 37//applet:IF_TIMEOUT(APPLET(timeout, BB_DIR_USR_BIN, BB_SUID_DROP)) 38 39//kbuild:lib-$(CONFIG_TIMEOUT) += timeout.o 40 41//usage:#define timeout_trivial_usage 42//usage: "[-t SECS] [-s SIG] PROG ARGS" 43//usage:#define timeout_full_usage "\n\n" 44//usage: "Runs PROG. Sends SIG to it if it is not gone in SECS seconds.\n" 45//usage: "Defaults: SECS: 10, SIG: TERM." 46 47#include "libbb.h" 48 49int timeout_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 50int timeout_main(int argc UNUSED_PARAM, char **argv) 51{ 52 int signo; 53 int status; 54 int parent = 0; 55 int timeout = 10; 56 pid_t pid; 57#if !BB_MMU 58 char *sv1, *sv2; 59#endif 60 const char *opt_s = "TERM"; 61 62 /* -p option is not documented, it is needed to support NOMMU. */ 63 64 /* -t SECONDS; -p PARENT_PID */ 65 /* '+': stop at first non-option */ 66 getopt32(argv, "+s:t:+" USE_FOR_NOMMU("p:+"), &opt_s, &timeout, &parent); 67 /*argv += optind; - no, wait for bb_daemonize_or_rexec! */ 68 signo = get_signum(opt_s); 69 if (signo < 0) 70 bb_error_msg_and_die("unknown signal '%s'", opt_s); 71 72 /* We want to create a grandchild which will watch 73 * and kill the grandparent. Other methods: 74 * making parent watch child disrupts parent<->child link 75 * (example: "tcpsvd 0.0.0.0 1234 timeout service_prog" - 76 * it's better if service_prog is a child of tcpsvd!), 77 * making child watch parent results in programs having 78 * unexpected children. */ 79 80 if (parent) /* we were re-execed, already grandchild */ 81 goto grandchild; 82 if (!argv[optind]) /* no PROG? */ 83 bb_show_usage(); 84 85#if !BB_MMU 86 sv1 = argv[optind]; 87 sv2 = argv[optind + 1]; 88#endif 89 pid = xvfork(); 90 if (pid == 0) { 91 /* Child: spawn grandchild and exit */ 92 parent = getppid(); 93#if !BB_MMU 94 argv[optind] = xasprintf("-p%u", parent); 95 argv[optind + 1] = NULL; 96#endif 97 /* NB: exits with nonzero on error: */ 98 bb_daemonize_or_rexec(0, argv); 99 /* Here we are grandchild. Sleep, then kill grandparent */ 100 grandchild: 101 /* Just sleep(HUGE_NUM); kill(parent) may kill wrong process! */ 102 while (1) { 103 sleep(1); 104 if (--timeout <= 0) 105 break; 106 if (kill(parent, 0)) { 107 /* process is gone */ 108 return EXIT_SUCCESS; 109 } 110 } 111 kill(parent, signo); 112 return EXIT_SUCCESS; 113 } 114 115 /* Parent */ 116 wait(&status); /* wait for child to die */ 117 /* Did intermediate [v]fork or exec fail? */ 118 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) 119 return EXIT_FAILURE; 120 /* Ok, exec a program as requested */ 121 argv += optind; 122#if !BB_MMU 123 argv[0] = sv1; 124 argv[1] = sv2; 125#endif 126 BB_EXECVP_or_die(argv); 127} 128