modernize-bozo-20020821
[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(register struct ezbnode *abnode) {
77     char tbuffer[256];
78
79     bnode_CoreName(abnode, NULL, tbuffer);
80     if (access(tbuffer, 0) == 0) return 1;
81     else return 0;
82 }
83
84 /* run at creation or after process exit.  figures out if we're all done (if a
85     one shot run) or when we should run again.  Sleeps until we should run again.
86     Note that the computation of when we should run again is made in procexit
87     and/or create procs.  This guy only schedules the sleep */
88 int 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     }
124     else {
125         /* run periodically */
126         if (abnode->running) return 0;
127         /* otherwise find out when to run it, and do it then */
128         temp = abnode->when - FT_ApproxTime();
129         if (temp < 1) temp = 1; /* temp is when to start dude */
130         bnode_SetTimeout(abnode, temp);
131     }
132     return 0;
133 }
134
135 static int cron_restartp (register struct cronbnode *abnode)
136 {
137     return 0;
138 }
139
140 static int cron_delete(struct cronbnode *abnode)
141 {
142     free(abnode->command);
143     free(abnode->whenString);
144     free(abnode);
145     return 0;
146 }
147
148 struct bnode *cron_create(char *ainstance, char *acommand, char *awhen)
149 {
150     struct cronbnode *te;
151     afs_int32 code;
152     char *cmdpath;
153     extern char *copystr();
154
155     /* construct local path from canonical (wire-format) path */
156     if (ConstructLocalBinPath(acommand, &cmdpath)) {
157         bozo_Log("BNODE: command path invalid '%s'\n", acommand);
158         return NULL;
159     }
160
161     te = (struct cronbnode *) malloc(sizeof(struct cronbnode));
162     memset(te, 0, sizeof(struct cronbnode));
163     code = ktime_ParsePeriodic(awhen, &te->whenToRun);
164     if ((code < 0) || (bnode_InitBnode(te, &cronbnode_ops, ainstance) != 0)) {
165         free(te);
166         free(cmdpath);
167         return NULL;
168     }
169     te->when = ktime_next(&te->whenToRun, 0);
170     te->command = cmdpath;
171     te->whenString = copystr(awhen);
172     return (struct bnode *) te;
173 }
174
175 /* called to SIGKILL a process if it doesn't terminate normally.  In cron, also
176     start up a process if it is time and not already running */
177 static int cron_timeout(struct cronbnode *abnode)
178 {
179     register afs_int32 temp;
180     register afs_int32 code;
181     struct bnode_proc *tp;
182
183     if (!abnode->running) {
184         if (abnode->when == 0) return 0;    /* spurious timeout activation */
185         /* not running, perhaps we should start it */
186         if (FT_ApproxTime() >= abnode->when) {
187             abnode->lastStart = FT_ApproxTime();
188             bnode_SetTimeout(abnode, 0);
189             code = bnode_NewProc(abnode, abnode->command, NULL, &tp);
190             if (code) {
191                 bozo_Log("cron failed to start bnode %s (code %d)\n",
192                          abnode->b.name, code);
193                 return code;
194             }
195             abnode->everRun = 1;
196             abnode->running = 1;
197             abnode->proc = tp;
198         }
199         else {
200             /* woke up too early, try again */
201             temp = abnode->when - FT_ApproxTime();
202             if (temp < 1) temp = 1;
203             bnode_SetTimeout(abnode, temp);
204         }
205     }
206     else {
207         if (!abnode->waitingForShutdown) return 0;      /* spurious */
208         /* send kill and turn off timer */
209         bnode_StopProc(abnode->proc, SIGKILL);
210         abnode->killSent = 1;
211         bnode_SetTimeout(abnode, 0);
212     }
213     return 0;
214 }
215
216 static int cron_getstat(struct cronbnode *abnode, afs_int32 *astatus)
217 {
218     register afs_int32 temp;
219     if (abnode->waitingForShutdown) temp = BSTAT_SHUTTINGDOWN;
220     else if (abnode->b.goal == 0) temp = BSTAT_SHUTDOWN;
221     else if (abnode->everRun && abnode->when == 0 && !abnode->running) {
222         /* special hack: bnode deletion won't happen if bnode is active, so
223            we make bnodes that are ready to be deleted automatically appear
224            as BSTAT_SHUTDOWN so bnode_Delete is happy. */
225         temp = BSTAT_SHUTDOWN;
226     }
227     else temp = BSTAT_NORMAL;
228     *astatus = temp;
229     return 0;
230 }
231
232 static int cron_setstat(register struct cronbnode *abnode, afs_int32 astatus)
233 {
234     if (abnode->waitingForShutdown) return BZBUSY;
235     if (astatus == BSTAT_SHUTDOWN) {
236        if (abnode->running) {
237           /* start shutdown */
238           bnode_StopProc(abnode->proc, SIGTERM);
239           abnode->waitingForShutdown = 1;
240           bnode_SetTimeout(abnode, SDTIME);
241           /* When shutdown is complete, bproc() calls BOP_PROCEXIT()
242            * [cron_procexit()] which will tell bproc() to no longer
243            * run this cron job.
244            */
245        } else {
246           /* Tell bproc() to no longer run this cron job */
247           bnode_SetTimeout(abnode, 0);
248        }
249     }
250     else if (astatus == BSTAT_NORMAL) {
251         /* start the cron job
252          * Figure out when to run next and schedule it
253          */
254         abnode->when = ktime_next(&abnode->whenToRun, 0);
255         ScheduleCronBnode(abnode);
256     }
257     return 0;
258 }
259
260 static int cron_procexit(struct cronbnode *abnode, struct bnode_proc *aproc)
261 {
262     /* process has exited */
263
264     /* log interesting errors for folks */
265     if (aproc->lastSignal)
266         bozo_Log("cron job %s exited due to signal %d\n",
267                  abnode->b.name, aproc->lastSignal);
268     else if (aproc->lastExit)
269         bozo_Log("cron job %s exited with non-zero code %d\n",
270                  abnode->b.name, aproc->lastExit);
271
272     abnode->waitingForShutdown = 0;
273     abnode->running = 0;
274     abnode->killSent = 0;
275     abnode->proc = (struct bnode_proc *) 0;
276
277     /* Figure out when to run next and schedule it */
278     abnode->when = ktime_next(&abnode->whenToRun, 0);
279     ScheduleCronBnode(abnode);
280     return 0;
281 }
282
283 static int cron_getstring(struct cronbnode *abnode, char *abuffer, 
284                           afs_int32 alen)
285 {
286     if (abnode->running) strcpy(abuffer, "running now");
287     else if (abnode->when==0) strcpy(abuffer, "waiting to run once");
288     else sprintf(abuffer, "run next at %s", ktime_DateOf(abnode->when));
289     return 0;
290 }
291
292 static int cron_getparm(struct cronbnode *abnode, afs_int32 aindex, char *abuffer, afs_int32 alen)
293 {
294     if (aindex == 0) strcpy(abuffer, abnode->command);
295     else if (aindex == 1) {
296         strcpy(abuffer, abnode->whenString);
297     }
298     else return BZDOM;
299     return 0;
300 }