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