add one more cycle
[tedtools.git] / kilter.c
1 /*
2  * Copyright (c) 2004 Teodor Sigaev <teodor@sigaev.ru>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *        notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *        notice, this list of conditions and the following disclaimer in the
12  *        documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the author nor the names of any co-contributors
14  *        may be used to endorse or promote products derived from this software
15  *        without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY CONTRIBUTORS ``AS IS'' AND ANY EXPRESS
18  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 #include <stdio.h>
30 #include <string.h>
31 #include <signal.h>
32 #include <ctype.h>
33 #include <sys/types.h>
34 #include <unistd.h>
35 #include <errno.h>
36 #include <sys/wait.h>
37 #include <sys/time.h>
38 #include <sys/resource.h>
39
40 #include "tlog.h"
41 #include "tmalloc.h"
42
43 static int exitAfterLastSignal=0;
44 static int kilterDebug=0;
45
46 static void
47 usage() {
48         puts("Copyright (c) 2004 Teodor Sigaev <teodor@sigaev.ru>. All rights reserved.");
49         puts("Usage:");
50         puts("kilter [-d] [-e] -SIGNAL [+]TIMEOUT [-SIGNAL [+]TIMEOUT [...]] COMMAND");
51         puts("   -d - debug mode");
52         puts("   -e - don't wait child exit after last signal sended");
53         puts("   +TIMEOUT - means related time to previous"); 
54
55         exit(1);
56 }
57
58 typedef struct {
59         char *name;
60         unsigned int sign;
61 } SigName;
62
63 static SigName signalname[]={
64         { "HUP",          SIGHUP        },
65         { "INT",          SIGINT        },
66         { "QUIT",         SIGQUIT       },
67         { "ILL",          SIGILL        },
68         { "TRAP",         SIGTRAP       },
69         { "ABRT",         SIGABRT       },
70 /*      { "EMT",          SIGEMT        }, */
71         { "FPE",          SIGFPE        },
72         { "KILL",         SIGKILL       },
73         { "BUS",          SIGBUS        },
74         { "SEGV",         SIGSEGV       },
75         { "SYS",          SIGSYS        },
76         { "PIPE",         SIGPIPE       },
77         { "ALRM",         SIGALRM       },
78         { "TERM",         SIGTERM       },
79         { "URG",          SIGURG        },
80         { "STOP",         SIGSTOP       },
81         { "TSTP",         SIGTSTP       },
82         { "CONT",         SIGCONT       },
83         { "CHLD",         SIGCHLD       },
84         { "TTIN",         SIGTTIN       },
85         { "TTOU",         SIGTTOU       },
86         { "IO",           SIGIO         },
87         { "XCPU",         SIGXCPU       },
88         { "XFSZ",         SIGXFSZ       },
89         { "VTALRM",       SIGVTALRM     },
90         { "PROF",         SIGPROF       },
91         { "WINCH",        SIGWINCH      },
92         /* { "INFO",         SIGINFO    },  */
93         { "USR1",         SIGUSR1       },
94         { "USR2",         SIGUSR2       },
95         {  NULL,                0       }
96 };
97
98 static unsigned int 
99 name2sign(char *name) {
100         SigName *ptr = signalname;
101
102         /*strupper(name);*/
103         if ( strncmp(name,"sig",3)==0 )
104                 name+=3;
105
106         while( ptr && ptr->name ) {
107                 if ( strcmp(name, ptr->name)==0 )
108                         return ptr->sign;
109                 ptr++;
110         }
111         
112         return 0;
113 }
114
115
116 static char sbuf[16];
117
118 static char*
119 sign2name(unsigned int sign) {
120         SigName *ptr = signalname;
121
122         while( ptr && ptr->name ) {
123                 if (sign==ptr->sign) {
124                         strcpy(sbuf,"SIG");
125                         strcpy(sbuf+3,ptr->name);
126                         return sbuf;
127                 }
128                 ptr++;
129         }
130         return NULL;
131 }
132
133 typedef struct ChildAction {
134         unsigned int    sign;
135         int             timeout;
136         struct ChildAction *next;
137 } ChildAction;
138
139 #define PCL_WAITOPTION  0
140 #define PCL_OPTION      1
141 #define PCL_OPTIONVAL   2
142
143
144 int
145 parsecmdline(int argn, char *argv[], ChildAction **ca) {
146         int i;
147         int state=PCL_WAITOPTION;
148         ChildAction *captr=NULL,*caend=NULL,*caprev=NULL;
149
150         *ca=NULL;
151
152         for(i=0;i<argn;i++) {
153                 char *ptr=argv[i];
154
155                 while(ptr && *ptr) {
156                         if ( state == PCL_WAITOPTION ) {
157                                 if (*ptr=='-') 
158                                         state=PCL_OPTION;
159                                 else
160                                         return i;
161                         } else if ( state == PCL_OPTION ) {
162                                 if (isdigit(*ptr)) {
163                                         captr = t0malloc(sizeof(ChildAction));
164                                         if ( *ca ) {
165                                                 caprev=caend;
166                                                 caend->next=captr;
167                                                 caend=captr;
168                                         } else {
169                                                 *ca=caend=captr;
170                                         }
171                                         captr->sign=atoi(ptr);
172                                         if ( !sign2name(captr->sign) )
173                                                 tlog(TL_ALARM|TL_EXIT,"Unknown signal value: %d", captr->sign);
174                                 } else if (isalpha(*ptr)) {
175                                         if ( strcmp(ptr,"h")==0 ) {
176                                                 usage();
177                                         } else if ( strcmp(ptr,"d")==0 ) {
178                                                 kilterDebug=1;
179                                                 state = PCL_WAITOPTION;
180                                                 break;
181                                         } else if ( strcmp(ptr,"e")==0 ) {
182                                                 exitAfterLastSignal=1;
183                                                 state = PCL_WAITOPTION;
184                                                 break;
185                                         } else {
186                                                 captr = t0malloc(sizeof(ChildAction));
187                                                 if ( *ca ) {
188                                                         caprev=caend;
189                                                         caend->next=captr;
190                                                         caend=captr;
191                                                 } else {
192                                                         *ca=caend=captr;
193                                                 }
194                                                 captr->sign=name2sign(ptr);
195                                                 if (!captr->sign) 
196                                                         tlog(TL_ALARM|TL_EXIT,"Unknown signal name: %s", ptr);
197                                         }
198                                 } else
199                                         tlog(TL_ALARM|TL_EXIT,"Unknown option: %s", ptr);
200                                 state = PCL_OPTIONVAL;
201                                 break;
202                         } else if ( state == PCL_OPTIONVAL ) {
203                                 tassert(captr!=NULL);
204                                 if (isdigit(*ptr) || *ptr=='+' ) {
205                                         captr->timeout = atoi(ptr);
206                                         if ( captr->timeout==0 )
207                                                 tlog(TL_ALARM|TL_EXIT,"Wrong timeout: %s", ptr);
208                                         if (*ptr!='+' && caprev) {
209                                                 if ( captr->timeout<=caprev->timeout )
210                                                         tlog(TL_ALARM|TL_EXIT,"Wrong absolute timeout: %d", captr->timeout);
211                                                 captr->timeout -= caprev->timeout; 
212                                         }
213                                         state = PCL_WAITOPTION;
214                                 } else
215                                         tlog(TL_ALARM|TL_EXIT,"Wrong timeout: %s", ptr);
216                                 break;
217                         } else
218                                 tlog(TL_CRIT|TL_EXIT,"parsecmdline: Wrong state: %d", state);
219                         ptr++;
220                 }
221         }
222
223         usage();
224         
225         return 0;
226 }
227
228 static int wasSIGCHILD=0;
229
230 static void
231 handlerSIGCHILD(int s) {
232         wasSIGCHILD=1;
233 }
234
235
236 int
237 main(int argn, char *argv[]) {
238         ChildAction     *ca,*ptr;
239         int skip=0, status;
240         pid_t   child;
241         int elapsedtime=0;
242
243         if ( argn<2 )
244                 usage();
245
246         skip=parsecmdline(argn-1, argv+1, &ca)+1;
247         if ( skip==1 ) usage();
248
249         argn-=skip;
250         argv+=skip;
251
252         if ( kilterDebug )
253                 opentlog( TL_OPEN_STDERR,  TL_DEBUG, NULL);
254         else
255                 opentlog( TL_OPEN_SYSLOG, TL_INFO, NULL);
256
257         if ( signal(SIGCHLD, handlerSIGCHILD)==SIG_ERR )
258                 tlog(TL_CRIT|TL_EXIT,"signal call failed: %s", strerror(errno));  
259         
260
261         if ( (child=fork()) == 0 ) {
262                 /* child */
263                 if (execvp(*argv, argv)==-1)
264                         tlog(TL_CRIT|TL_EXIT,"Exec error: %s", strerror(errno));
265         }
266         if (child==-1)
267                 tlog(TL_CRIT|TL_EXIT,"Can't fork: %s", strerror(errno));
268
269         while(ca) {
270                 elapsedtime=(wasSIGCHILD) ? ca->timeout : sleep(ca->timeout);
271                 if ( elapsedtime > 0 || wasSIGCHILD ) {
272                         wasSIGCHILD=0;
273                         if ( waitpid(child, &status, WNOHANG) == -1 )
274                                 tlog(TL_CRIT|TL_EXIT,"waitpid for %d process failed: %s", child, strerror(errno));
275                         if ( WIFEXITED(status) ) {
276                                 tlog(TL_INFO, "Child %d was exited with status  %d", child, WEXITSTATUS(status));
277                                 return 0;
278                         } else if ( WIFSIGNALED(status) ) { 
279                                 tlog(TL_INFO, "Child %d was exited with signal %d(%s)", child, WTERMSIG(status), 
280                                         ( sign2name(WTERMSIG(status)) ) ? sign2name(WTERMSIG(status)) : "unknown");
281                                 return 0;
282                         }
283                         ca->timeout = elapsedtime;
284                 } else {
285                         if ( kill(child, ca->sign) != 0 )
286                                 tlog(TL_CRIT|TL_EXIT,"kill %d process failed: %s", child, strerror(errno));
287                         tlog(TL_INFO,"%s'ed process %d", sign2name(ca->sign), child);
288                         ptr=ca->next;
289                         tfree(ca);
290                         ca=ptr;
291                 }
292         }
293
294         if ( exitAfterLastSignal==0 ) {
295                 if ( waitpid(child, &status, 0) == -1 )
296                         tlog(TL_CRIT|TL_EXIT,"waitpid for %d process failed: %s", child, strerror(errno));
297                 if ( WIFEXITED(status) ) 
298                         tlog(TL_INFO, "Child %d was exited with status %d", child, WEXITSTATUS(status));
299                 else if ( WIFSIGNALED(status) ) 
300                         tlog(TL_INFO, "Child %d was exited with signal %d(%s)", child, WTERMSIG(status), 
301                                 ( sign2name(WTERMSIG(status)) ) ? sign2name(WTERMSIG(status)) : "unknown");
302                 else
303                         tlog(TL_INFO, "Child %d wasn't exited",child);
304         } else 
305                 tlog(TL_DEBUG, "Exit, don't wait a process %d", child);
306
307         return 0;
308 }