afb337e750e9b0674e9ead630c5fac1c065da453
[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 <ctype.h>
17
18 #include <lwp.h>
19 #include <afs/ktime.h>
20 #include <afs/afsutil.h>
21
22 #include "bnode.h"
23 #include "bnode_internal.h"
24 #include "bosprototypes.h"
25
26 struct bnode *cron_create(char *, char *, char *, char *, char *, char *);
27 static int cron_hascore(struct bnode *bnode);
28 static int cron_restartp(struct bnode *bnode);
29 static int cron_delete(struct bnode *bnode);
30 static int cron_timeout(struct bnode *bnode);
31 static int cron_getstat(struct bnode *bnode, afs_int32 *status);
32 static int cron_setstat(struct bnode *bnode, afs_int32 status);
33 static int cron_procstarted(struct bnode *bnode, struct bnode_proc *proc);
34 static int cron_procexit(struct bnode *bnode, struct bnode_proc *proc);
35 static int cron_getstring(struct bnode *bnode, char *abuffer, afs_int32 alen);
36 static int cron_getparm(struct bnode *bnode, afs_int32, char *, afs_int32);
37
38 #define SDTIME          60      /* time in seconds given to a process to evaporate */
39
40 struct bnode_ops cronbnode_ops = {
41     cron_create,
42     cron_timeout,
43     cron_getstat,
44     cron_setstat,
45     cron_delete,
46     cron_procexit,
47     cron_getstring,
48     cron_getparm,
49     cron_restartp,
50     cron_hascore,
51     cron_procstarted,
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(struct bnode *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(struct cronbnode *abnode)
88 {
89     afs_int32 code;
90     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((struct bnode *)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((struct bnode *)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((struct bnode *)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((struct bnode *)abnode, temp);
131     }
132     return 0;
133 }
134
135 static int
136 cron_restartp(struct bnode *abnode)
137 {
138     return 0;
139 }
140
141 static int
142 cron_delete(struct bnode *bn)
143 {
144     struct cronbnode *abnode = (struct cronbnode *)bn;
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             char *unused1, char *unused2, char *unused3)
154 {
155     struct cronbnode *te;
156     afs_int32 code;
157     char *cmdpath;
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 = calloc(1, sizeof(struct cronbnode));
166     code = ktime_ParsePeriodic(awhen, &te->whenToRun);
167     if ((code < 0) || (bnode_InitBnode((struct bnode *)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 = strdup(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 bnode *bn)
182 {
183     struct cronbnode *abnode = (struct cronbnode *)bn;
184     afs_int32 temp;
185     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((struct bnode *)abnode, 0);
195             code = bnode_NewProc((struct bnode *)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((struct bnode *)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((struct bnode *)abnode, 0);
218     }
219     return 0;
220 }
221
222 static int
223 cron_getstat(struct bnode *bn, afs_int32 * astatus)
224 {
225     struct cronbnode *abnode = (struct cronbnode *)bn;
226     afs_int32 temp;
227     if (abnode->waitingForShutdown)
228         temp = BSTAT_SHUTTINGDOWN;
229     else if (abnode->b.goal == 0)
230         temp = BSTAT_SHUTDOWN;
231     else if (abnode->everRun && abnode->when == 0 && !abnode->running) {
232         /* special hack: bnode deletion won't happen if bnode is active, so
233          * we make bnodes that are ready to be deleted automatically appear
234          * as BSTAT_SHUTDOWN so bnode_Delete is happy. */
235         temp = BSTAT_SHUTDOWN;
236     } else
237         temp = BSTAT_NORMAL;
238     *astatus = temp;
239     return 0;
240 }
241
242 static int
243 cron_setstat(struct bnode *bn, afs_int32 astatus)
244 {
245     struct cronbnode *abnode = (struct cronbnode *)bn;
246     if (abnode->waitingForShutdown)
247         return BZBUSY;
248     if (astatus == BSTAT_SHUTDOWN) {
249         if (abnode->running) {
250             /* start shutdown */
251             bnode_StopProc(abnode->proc, SIGTERM);
252             abnode->waitingForShutdown = 1;
253             bnode_SetTimeout((struct bnode *)abnode, SDTIME);
254             /* When shutdown is complete, bproc() calls BOP_PROCEXIT()
255              * [cron_procexit()] which will tell bproc() to no longer
256              * run this cron job.
257              */
258         } else {
259             /* Tell bproc() to no longer run this cron job */
260             bnode_SetTimeout((struct bnode *)abnode, 0);
261         }
262     } else if (astatus == BSTAT_NORMAL) {
263         /* start the cron job
264          * Figure out when to run next and schedule it
265          */
266         abnode->when = ktime_next(&abnode->whenToRun, 0);
267         ScheduleCronBnode(abnode);
268     }
269     return 0;
270 }
271
272 static int
273 cron_procstarted(struct bnode *bn, struct bnode_proc *aproc)
274 {
275     return 0;   /* no op */
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 }