/* * Copyright (c) 2004 Teodor Sigaev * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY CONTRIBUTORS ``AS IS'' AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include "tlog.h" #include "tmalloc.h" static int exitAfterLastSignal=0; static int kilterDebug=0; static void usage() { puts("Copyright (c) 2004 Teodor Sigaev . All rights reserved."); puts("Usage:"); puts("kilter [-d] [-e] -SIGNAL [+]TIMEOUT [-SIGNAL [+]TIMEOUT [...]] COMMAND"); puts(" -d - debug mode"); puts(" -e - don't wait child exit after last signal sended"); puts(" +TIMEOUT - means related time to previous"); exit(1); } typedef struct { char *name; unsigned int sign; } SigName; static SigName signalname[]={ { "HUP", SIGHUP }, { "INT", SIGINT }, { "QUIT", SIGQUIT }, { "ILL", SIGILL }, { "TRAP", SIGTRAP }, { "ABRT", SIGABRT }, /* { "EMT", SIGEMT }, */ { "FPE", SIGFPE }, { "KILL", SIGKILL }, { "BUS", SIGBUS }, { "SEGV", SIGSEGV }, { "SYS", SIGSYS }, { "PIPE", SIGPIPE }, { "ALRM", SIGALRM }, { "TERM", SIGTERM }, { "URG", SIGURG }, { "STOP", SIGSTOP }, { "TSTP", SIGTSTP }, { "CONT", SIGCONT }, { "CHLD", SIGCHLD }, { "TTIN", SIGTTIN }, { "TTOU", SIGTTOU }, { "IO", SIGIO }, { "XCPU", SIGXCPU }, { "XFSZ", SIGXFSZ }, { "VTALRM", SIGVTALRM }, { "PROF", SIGPROF }, { "WINCH", SIGWINCH }, /* { "INFO", SIGINFO }, */ { "USR1", SIGUSR1 }, { "USR2", SIGUSR2 }, { NULL, 0 } }; static unsigned int name2sign(char *name) { SigName *ptr = signalname; /*strupper(name);*/ if ( strncmp(name,"sig",3)==0 ) name+=3; while( ptr && ptr->name ) { if ( strcmp(name, ptr->name)==0 ) return ptr->sign; ptr++; } return 0; } static char sbuf[16]; static char* sign2name(unsigned int sign) { SigName *ptr = signalname; while( ptr && ptr->name ) { if (sign==ptr->sign) { strcpy(sbuf,"SIG"); strcpy(sbuf+3,ptr->name); return sbuf; } ptr++; } return NULL; } typedef struct ChildAction { unsigned int sign; int timeout; struct ChildAction *next; } ChildAction; #define PCL_WAITOPTION 0 #define PCL_OPTION 1 #define PCL_OPTIONVAL 2 int parsecmdline(int argn, char *argv[], ChildAction **ca) { int i; int state=PCL_WAITOPTION; ChildAction *captr=NULL,*caend=NULL,*caprev=NULL; *ca=NULL; for(i=0;inext=captr; caend=captr; } else { *ca=caend=captr; } captr->sign=atoi(ptr); if ( !sign2name(captr->sign) ) tlog(TL_ALARM|TL_EXIT,"Unknown signal value: %d", captr->sign); } else if (isalpha(*ptr)) { if ( strcmp(ptr,"h")==0 ) { usage(); } else if ( strcmp(ptr,"d")==0 ) { kilterDebug=1; state = PCL_WAITOPTION; break; } else if ( strcmp(ptr,"e")==0 ) { exitAfterLastSignal=1; state = PCL_WAITOPTION; break; } else { captr = t0malloc(sizeof(ChildAction)); if ( *ca ) { caprev=caend; caend->next=captr; caend=captr; } else { *ca=caend=captr; } captr->sign=name2sign(ptr); if (!captr->sign) tlog(TL_ALARM|TL_EXIT,"Unknown signal name: %s", ptr); } } else tlog(TL_ALARM|TL_EXIT,"Unknown option: %s", ptr); state = PCL_OPTIONVAL; break; } else if ( state == PCL_OPTIONVAL ) { tassert(captr!=NULL); if (isdigit(*ptr) || *ptr=='+' ) { captr->timeout = atoi(ptr); if ( captr->timeout==0 ) tlog(TL_ALARM|TL_EXIT,"Wrong timeout: %s", ptr); if (*ptr!='+' && caprev) { if ( captr->timeout<=caprev->timeout ) tlog(TL_ALARM|TL_EXIT,"Wrong absolute timeout: %d", captr->timeout); captr->timeout -= caprev->timeout; } state = PCL_WAITOPTION; } else tlog(TL_ALARM|TL_EXIT,"Wrong timeout: %s", ptr); break; } else tlog(TL_CRIT|TL_EXIT,"parsecmdline: Wrong state: %d", state); ptr++; } } usage(); return 0; } static int wasSIGCHILD=0; static void handlerSIGCHILD(int s) { wasSIGCHILD=1; } int main(int argn, char *argv[]) { ChildAction *ca,*ptr; int skip=0, status; pid_t child; int elapsedtime=0; if ( argn<2 ) usage(); skip=parsecmdline(argn-1, argv+1, &ca)+1; if ( skip==1 ) usage(); argn-=skip; argv+=skip; if ( kilterDebug ) opentlog( TL_OPEN_STDERR, TL_DEBUG, NULL); else opentlog( TL_OPEN_SYSLOG, TL_INFO, NULL); if ( signal(SIGCHLD, handlerSIGCHILD)==SIG_ERR ) tlog(TL_CRIT|TL_EXIT,"signal call failed: %s", strerror(errno)); if ( (child=fork()) == 0 ) { /* child */ if (execvp(*argv, argv)==-1) tlog(TL_CRIT|TL_EXIT,"Exec error: %s", strerror(errno)); } if (child==-1) tlog(TL_CRIT|TL_EXIT,"Can't fork: %s", strerror(errno)); while(ca) { elapsedtime=(wasSIGCHILD) ? ca->timeout : sleep(ca->timeout); if ( elapsedtime > 0 || wasSIGCHILD ) { wasSIGCHILD=0; if ( waitpid(child, &status, WNOHANG) == -1 ) tlog(TL_CRIT|TL_EXIT,"waitpid for %d process failed: %s", child, strerror(errno)); if ( WIFEXITED(status) ) { tlog(TL_INFO, "Child %d was exited with status %d", child, WEXITSTATUS(status)); return 0; } else if ( WIFSIGNALED(status) ) { tlog(TL_INFO, "Child %d was exited with signal %d(%s)", child, WTERMSIG(status), ( sign2name(WTERMSIG(status)) ) ? sign2name(WTERMSIG(status)) : "unknown"); return 0; } ca->timeout = elapsedtime; } else { if ( kill(child, ca->sign) != 0 ) tlog(TL_CRIT|TL_EXIT,"kill %d process failed: %s", child, strerror(errno)); tlog(TL_INFO,"%s'ed process %d", sign2name(ca->sign), child); ptr=ca->next; tfree(ca); ca=ptr; } } if ( exitAfterLastSignal==0 ) { if ( waitpid(child, &status, 0) == -1 ) tlog(TL_CRIT|TL_EXIT,"waitpid for %d process failed: %s", child, strerror(errno)); if ( WIFEXITED(status) ) tlog(TL_INFO, "Child %d was exited with status %d", child, WEXITSTATUS(status)); else if ( WIFSIGNALED(status) ) tlog(TL_INFO, "Child %d was exited with signal %d(%s)", child, WTERMSIG(status), ( sign2name(WTERMSIG(status)) ) ? sign2name(WTERMSIG(status)) : "unknown"); else tlog(TL_INFO, "Child %d wasn't exited",child); } else tlog(TL_DEBUG, "Exit, don't wait a process %d", child); return 0; }