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