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