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 (6 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: "[-s SIG] [-k KILL_SECS] SECS PROG ARGS" 43//usage:#define timeout_full_usage "\n\n" 44//usage: "Run PROG. Send SIG to it if it is not gone in SECS seconds.\n" 45//usage: "Default SIG: TERM." 46//usage: "If it still exists in KILL_SECS seconds, send KILL.\n" 47 48#include "libbb.h" 49 50static NOINLINE int timeout_wait(int timeout, pid_t pid) 51{ 52 /* Just sleep(HUGE_NUM); kill(parent) may kill wrong process! */ 53 while (1) { 54 sleep1(); 55 if (--timeout <= 0) 56 break; 57 if (kill(pid, 0)) { 58 /* process is gone */ 59 return EXIT_SUCCESS; 60 } 61 } 62 return EXIT_FAILURE; 63} 64 65int timeout_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 66int timeout_main(int argc UNUSED_PARAM, char **argv) 67{ 68 int signo; 69 int status; 70 int parent = 0; 71 int timeout; 72 int kill_timeout; 73 pid_t pid; 74#if !BB_MMU 75 char *sv1, *sv2; 76#endif 77 const char *opt_s = "TERM"; 78 char *opt_k = NULL; 79 80 /* -p option is not documented, it is needed to support NOMMU. */ 81 82 /* -t SECONDS; -p PARENT_PID */ 83 /* '+': stop at first non-option */ 84 getopt32(argv, "+s:k:" USE_FOR_NOMMU("p:+"), &opt_s, &opt_k, &parent); 85 /*argv += optind; - no, wait for bb_daemonize_or_rexec! */ 86 87 signo = get_signum(opt_s); 88 if (signo < 0) 89 bb_error_msg_and_die("unknown signal '%s'", opt_s); 90 91 kill_timeout = 0; 92 if (opt_k) 93 kill_timeout = parse_duration_str(opt_k); 94 95 if (!argv[optind]) 96 bb_show_usage(); 97 timeout = parse_duration_str(argv[optind++]); 98 if (!argv[optind]) /* no PROG? */ 99 bb_show_usage(); 100 101 /* We want to create a grandchild which will watch 102 * and kill the grandparent. Other methods: 103 * making parent watch child disrupts parent<->child link 104 * (example: "tcpsvd 0.0.0.0 1234 timeout service_prog" - 105 * it's better if service_prog is a child of tcpsvd!), 106 * making child watch parent results in programs having 107 * unexpected children. */ 108 109 if (parent) /* we were re-execed, already grandchild */ 110 goto grandchild; 111 112#if !BB_MMU 113 sv1 = argv[optind]; 114 sv2 = argv[optind + 1]; 115#endif 116 pid = xvfork(); 117 if (pid == 0) { 118 /* Child: spawn grandchild and exit */ 119 parent = getppid(); 120#if !BB_MMU 121 argv[optind] = xasprintf("-p%u", parent); 122 argv[optind + 1] = NULL; 123#endif 124 /* NB: exits with nonzero on error: */ 125 bb_daemonize_or_rexec(0, argv); 126 /* Here we are grandchild. Sleep, then kill grandparent */ 127 grandchild: 128 if (timeout_wait(timeout, parent) == EXIT_SUCCESS) 129 return EXIT_SUCCESS; 130 kill(parent, signo); 131 132 if (kill_timeout > 0) { 133 if (timeout_wait(kill_timeout, parent) == EXIT_SUCCESS) 134 return EXIT_SUCCESS; 135 kill(parent, SIGKILL); 136 } 137 138 return EXIT_SUCCESS; 139 } 140 141 /* Parent */ 142 wait(&status); /* wait for child to die */ 143 /* Did intermediate [v]fork or exec fail? */ 144 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) 145 return EXIT_FAILURE; 146 /* Ok, exec a program as requested */ 147 argv += optind; 148#if !BB_MMU 149 argv[0] = sv1; 150 argv[1] = sv2; 151#endif 152 BB_EXECVP_or_die(argv); 153} 154