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] SECS 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: "Default 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; 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:" USE_FOR_NOMMU("p:+"), &opt_s, &parent); 67 /*argv += optind; - no, wait for bb_daemonize_or_rexec! */ 68 69 signo = get_signum(opt_s); 70 if (signo < 0) 71 bb_error_msg_and_die("unknown signal '%s'", opt_s); 72 73 if (!argv[optind]) 74 bb_show_usage(); 75 timeout = parse_duration_str(argv[optind++]); 76 if (!argv[optind]) /* no PROG? */ 77 bb_show_usage(); 78 79 /* We want to create a grandchild which will watch 80 * and kill the grandparent. Other methods: 81 * making parent watch child disrupts parent<->child link 82 * (example: "tcpsvd 0.0.0.0 1234 timeout service_prog" - 83 * it's better if service_prog is a child of tcpsvd!), 84 * making child watch parent results in programs having 85 * unexpected children. */ 86 87 if (parent) /* we were re-execed, already grandchild */ 88 goto grandchild; 89 90#if !BB_MMU 91 sv1 = argv[optind]; 92 sv2 = argv[optind + 1]; 93#endif 94 pid = xvfork(); 95 if (pid == 0) { 96 /* Child: spawn grandchild and exit */ 97 parent = getppid(); 98#if !BB_MMU 99 argv[optind] = xasprintf("-p%u", parent); 100 argv[optind + 1] = NULL; 101#endif 102 /* NB: exits with nonzero on error: */ 103 bb_daemonize_or_rexec(0, argv); 104 /* Here we are grandchild. Sleep, then kill grandparent */ 105 grandchild: 106 /* Just sleep(HUGE_NUM); kill(parent) may kill wrong process! */ 107 while (1) { 108 sleep(1); 109 if (--timeout <= 0) 110 break; 111 if (kill(parent, 0)) { 112 /* process is gone */ 113 return EXIT_SUCCESS; 114 } 115 } 116 kill(parent, signo); 117 return EXIT_SUCCESS; 118 } 119 120 /* Parent */ 121 wait(&status); /* wait for child to die */ 122 /* Did intermediate [v]fork or exec fail? */ 123 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) 124 return EXIT_FAILURE; 125 /* Ok, exec a program as requested */ 126 argv += optind; 127#if !BB_MMU 128 argv[0] = sv1; 129 argv[1] = sv2; 130#endif 131 BB_EXECVP_or_die(argv); 132} 133