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