e55324c9de81264b35006862a92d9092b457ede6
[openafs.git] / src / bozo / cronbnodeops.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12
13 RCSID("$Header$");
14
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <lwp.h>
18 #include <time.h>
19 #include <ctype.h>
20 #include <stdio.h>
21 #ifdef AFS_NT40_ENV
22 #include <io.h>
23 #endif
24
25 #ifdef HAVE_STRING_H
26 #include <string.h>
27 #else
28 #ifdef HAVE_STRINGS_H
29 #include <strings.h>
30 #endif
31 #endif
32 #include <stdlib.h>
33
34 #include <afs/ktime.h>
35 #include <afs/afsutil.h>
36 #include <afs/procmgmt.h>  /* signal(), kill(), wait(), etc. */
37 #include "bnode.h"
38
39 static int cron_timeout(), cron_getstat(), cron_setstat(), cron_delete();
40 static int cron_procexit(), cron_getstring(), cron_getparm(), cron_restartp();
41 static int cron_hascore();
42 struct bnode *cron_create();
43 extern char *ktime_DateOf();
44
45 #define SDTIME          60          /* time in seconds given to a process to evaporate */
46
47 struct bnode_ops cronbnode_ops = {
48     cron_create,
49     cron_timeout,
50     cron_getstat,
51     cron_setstat,
52     cron_delete,
53     cron_procexit,
54     cron_getstring,
55     cron_getparm,
56     cron_restartp,
57     cron_hascore,
58 };
59     
60 struct cronbnode {
61     struct bnode b;
62     afs_int32 zapTime;          /* time we sent a sigterm */
63     char *command;
64     char *whenString;           /* string rep. of when to run */
65     struct bnode_proc *proc;
66     afs_int32 lastStart;                /* time last started process */
67     afs_int32 nextRun;          /* next time to run, if no proc running */
68     struct ktime whenToRun;     /* high-level rep of when should we run this guy */
69     afs_int32 when;                     /* computed at creation time and procexit time */
70     char everRun;               /* true if ever ran */
71     char waitingForShutdown;    /* have we started any shutdown procedure? */
72     char running;   /* is process running? */
73     char killSent;  /* have we tried sigkill signal? */
74 };
75
76 static int cron_hascore(abnode)
77 register struct ezbnode *abnode; {
78     char tbuffer[256];
79
80     bnode_CoreName(abnode, NULL, tbuffer);
81     if (access(tbuffer, 0) == 0) return 1;
82     else return 0;
83 }
84
85 /* run at creation or after process exit.  figures out if we're all done (if a
86     one shot run) or when we should run again.  Sleeps until we should run again.
87     Note that the computation of when we should run again is made in procexit
88     and/or create procs.  This guy only schedules the sleep */
89 ScheduleCronBnode(abnode)
90 register struct cronbnode *abnode; {
91     register afs_int32 code;
92     register afs_int32 temp;
93     struct bnode_proc *tp;
94
95     /* If this proc is shutdown, tell bproc() to no longer run this job */
96     if (abnode->b.goal == BSTAT_SHUTDOWN) {
97         bnode_SetTimeout(abnode, 0);
98         return 0;
99     }
100
101     /* otherwise we're supposed to be running, figure out when */
102     if (abnode->when == 0) {
103         /* one shot */
104         if (abnode->everRun) {
105             /* once is enough */
106             bnode_Delete(abnode);
107             return 0;
108         }
109         /* otherwise start it */
110         if (!abnode->running) {
111             /* start up */
112             abnode->lastStart = FT_ApproxTime();
113             code = bnode_NewProc(abnode, abnode->command, NULL, &tp);
114             if (code) {
115                 bozo_Log("cron bnode %s failed to start (code %d)\n",
116                          abnode->b.name, code);
117                 return code;
118             }
119             abnode->everRun = 1;
120             abnode->running = 1;
121             abnode->proc = tp;
122             return 0;
123         }
124     }
125     else {
126         /* run periodically */
127         if (abnode->running) return 0;
128         /* otherwise find out when to run it, and do it then */
129         temp = abnode->when - FT_ApproxTime();
130         if (temp < 1) temp = 1; /* temp is when to start dude */
131         bnode_SetTimeout(abnode, temp);
132     }
133     return 0;
134 }
135
136 static int cron_restartp (abnode)
137 register struct cronbnode *abnode; {
138     return 0;
139 }
140
141 static int cron_delete(abnode)
142 struct cronbnode *abnode; {
143     free(abnode->command);
144     free(abnode->whenString);
145     free(abnode);
146     return 0;
147 }
148
149 struct bnode *cron_create(ainstance, acommand, awhen)
150 char *ainstance;
151 char *awhen;
152 char *acommand; {
153     struct cronbnode *te;
154     afs_int32 code;
155     char *cmdpath;
156     extern char *copystr();
157
158     /* construct local path from canonical (wire-format) path */
159     if (ConstructLocalBinPath(acommand, &cmdpath)) {
160         bozo_Log("BNODE: command path invalid '%s'\n", acommand);
161         return NULL;
162     }
163
164     te = (struct cronbnode *) malloc(sizeof(struct cronbnode));
165     memset(te, 0, sizeof(struct cronbnode));
166     code = ktime_ParsePeriodic(awhen, &te->whenToRun);
167     if (code < 0) {
168         free(te);
169         free(cmdpath);
170         return NULL;
171     }
172     bnode_InitBnode(te, &cronbnode_ops, ainstance);
173     te->when = ktime_next(&te->whenToRun, 0);
174     te->command = cmdpath;
175     te->whenString = copystr(awhen);
176     return (struct bnode *) te;
177 }
178
179 /* called to SIGKILL a process if it doesn't terminate normally.  In cron, also
180     start up a process if it is time and not already running */
181 static int cron_timeout(abnode)
182 struct cronbnode *abnode; {
183     register afs_int32 temp;
184     register afs_int32 code;
185     struct bnode_proc *tp;
186
187     if (!abnode->running) {
188         if (abnode->when == 0) return 0;    /* spurious timeout activation */
189         /* not running, perhaps we should start it */
190         if (FT_ApproxTime() >= abnode->when) {
191             abnode->lastStart = FT_ApproxTime();
192             bnode_SetTimeout(abnode, 0);
193             code = bnode_NewProc(abnode, abnode->command, NULL, &tp);
194             if (code) {
195                 bozo_Log("cron failed to start bnode %s (code %d)\n",
196                          abnode->b.name, code);
197                 return code;
198             }
199             abnode->everRun = 1;
200             abnode->running = 1;
201             abnode->proc = tp;
202         }
203         else {
204             /* woke up too early, try again */
205             temp = abnode->when - FT_ApproxTime();
206             if (temp < 1) temp = 1;
207             bnode_SetTimeout(abnode, temp);
208         }
209     }
210     else {
211         if (!abnode->waitingForShutdown) return 0;      /* spurious */
212         /* send kill and turn off timer */
213         bnode_StopProc(abnode->proc, SIGKILL);
214         abnode->killSent = 1;
215         bnode_SetTimeout(abnode, 0);
216     }
217     return 0;
218 }
219
220 static int cron_getstat(abnode, astatus)
221 struct cronbnode *abnode;
222 afs_int32 *astatus; {
223     register afs_int32 temp;
224     if (abnode->waitingForShutdown) temp = BSTAT_SHUTTINGDOWN;
225     else if (abnode->b.goal == 0) temp = BSTAT_SHUTDOWN;
226     else if (abnode->everRun && abnode->when == 0 && !abnode->running) {
227         /* special hack: bnode deletion won't happen if bnode is active, so
228            we make bnodes that are ready to be deleted automatically appear
229            as BSTAT_SHUTDOWN so bnode_Delete is happy. */
230         temp = BSTAT_SHUTDOWN;
231     }
232     else temp = BSTAT_NORMAL;
233     *astatus = temp;
234     return 0;
235 }
236
237 static int cron_setstat(abnode, astatus)
238 register struct cronbnode *abnode;
239 afs_int32 astatus; {
240
241     if (abnode->waitingForShutdown) return BZBUSY;
242     if (astatus == BSTAT_SHUTDOWN) {
243        if (abnode->running) {
244           /* start shutdown */
245           bnode_StopProc(abnode->proc, SIGTERM);
246           abnode->waitingForShutdown = 1;
247           bnode_SetTimeout(abnode, SDTIME);
248           /* When shutdown is complete, bproc() calls BOP_PROCEXIT()
249            * [cron_procexit()] which will tell bproc() to no longer
250            * run this cron job.
251            */
252        } else {
253           /* Tell bproc() to no longer run this cron job */
254           bnode_SetTimeout(abnode, 0);
255        }
256     }
257     else if (astatus == BSTAT_NORMAL) {
258         /* start the cron job
259          * Figure out when to run next and schedule it
260          */
261         abnode->when = ktime_next(&abnode->whenToRun, 0);
262         ScheduleCronBnode(abnode);
263     }
264     return 0;
265 }
266
267 static int cron_procexit(abnode, aproc)
268 struct cronbnode *abnode;
269 struct bnode_proc *aproc; {
270     /* process has exited */
271
272     /* log interesting errors for folks */
273     if (aproc->lastSignal)
274         bozo_Log("cron job %s exited due to signal %d\n",
275                  abnode->b.name, aproc->lastSignal);
276     else if (aproc->lastExit)
277         bozo_Log("cron job %s exited with non-zero code %d\n",
278                  abnode->b.name, aproc->lastExit);
279
280     abnode->waitingForShutdown = 0;
281     abnode->running = 0;
282     abnode->killSent = 0;
283     abnode->proc = (struct bnode_proc *) 0;
284
285     /* Figure out when to run next and schedule it */
286     abnode->when = ktime_next(&abnode->whenToRun, 0);
287     ScheduleCronBnode(abnode);
288     return 0;
289 }
290
291 static int cron_getstring(abnode, abuffer, alen)
292 struct cronbnode *abnode;
293 char *abuffer;
294 afs_int32 alen;{
295     if (abnode->running) strcpy(abuffer, "running now");
296     else if (abnode->when==0) strcpy(abuffer, "waiting to run once");
297     else sprintf(abuffer, "run next at %s", ktime_DateOf(abnode->when));
298     return 0;
299 }
300
301 static cron_getparm(abnode, aindex, abuffer, alen)
302 struct cronbnode *abnode;
303 afs_int32 aindex;
304 char *abuffer;
305 afs_int32 alen; {
306     if (aindex == 0) strcpy(abuffer, abnode->command);
307     else if (aindex == 1) {
308         strcpy(abuffer, abnode->whenString);
309     }
310     else return BZDOM;
311     return 0;
312 }