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