Use calloc, rather than malloc/memset
[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 "bosprototypes.h"
24
25 struct bnode *cron_create(char *, char *, char *, char *, char *, char *);
26 static int cron_hascore(struct bnode *bnode);
27 static int cron_restartp(struct bnode *bnode);
28 static int cron_delete(struct bnode *bnode);
29 static int cron_timeout(struct bnode *bnode);
30 static int cron_getstat(struct bnode *bnode, afs_int32 *status);
31 static int cron_setstat(struct bnode *bnode, afs_int32 status);
32 static int cron_procstarted(struct bnode *bnode, struct bnode_proc *proc);
33 static int cron_procexit(struct bnode *bnode, struct bnode_proc *proc);
34 static int cron_getstring(struct bnode *bnode, char *abuffer, afs_int32 alen);
35 static int cron_getparm(struct bnode *bnode, afs_int32, char *, afs_int32);
36
37 #define SDTIME          60      /* time in seconds given to a process to evaporate */
38
39 struct bnode_ops cronbnode_ops = {
40     cron_create,
41     cron_timeout,
42     cron_getstat,
43     cron_setstat,
44     cron_delete,
45     cron_procexit,
46     cron_getstring,
47     cron_getparm,
48     cron_restartp,
49     cron_hascore,
50     cron_procstarted,
51 };
52
53 struct cronbnode {
54     struct bnode b;
55     afs_int32 zapTime;          /* time we sent a sigterm */
56     char *command;
57     char *whenString;           /* string rep. of when to run */
58     struct bnode_proc *proc;
59     afs_int32 lastStart;        /* time last started process */
60     afs_int32 nextRun;          /* next time to run, if no proc running */
61     struct ktime whenToRun;     /* high-level rep of when should we run this guy */
62     afs_int32 when;             /* computed at creation time and procexit time */
63     char everRun;               /* true if ever ran */
64     char waitingForShutdown;    /* have we started any shutdown procedure? */
65     char running;               /* is process running? */
66     char killSent;              /* have we tried sigkill signal? */
67 };
68
69 static int
70 cron_hascore(struct bnode *abnode)
71 {
72     char tbuffer[256];
73
74     bnode_CoreName(abnode, NULL, tbuffer);
75     if (access(tbuffer, 0) == 0)
76         return 1;
77     else
78         return 0;
79 }
80
81 /* run at creation or after process exit.  figures out if we're all done (if a
82     one shot run) or when we should run again.  Sleeps until we should run again.
83     Note that the computation of when we should run again is made in procexit
84     and/or create procs.  This guy only schedules the sleep */
85 int
86 ScheduleCronBnode(struct cronbnode *abnode)
87 {
88     afs_int32 code;
89     afs_int32 temp;
90     struct bnode_proc *tp;
91
92     /* If this proc is shutdown, tell bproc() to no longer run this job */
93     if (abnode->b.goal == BSTAT_SHUTDOWN) {
94         bnode_SetTimeout((struct bnode *)abnode, 0);
95         return 0;
96     }
97
98     /* otherwise we're supposed to be running, figure out when */
99     if (abnode->when == 0) {
100         /* one shot */
101         if (abnode->everRun) {
102             /* once is enough */
103             bnode_Delete((struct bnode *)abnode);
104             return 0;
105         }
106         /* otherwise start it */
107         if (!abnode->running) {
108             /* start up */
109             abnode->lastStart = FT_ApproxTime();
110             code = bnode_NewProc((struct bnode *)abnode, abnode->command, NULL, &tp);
111             if (code) {
112                 bozo_Log("cron bnode %s failed to start (code %d)\n",
113                          abnode->b.name, code);
114                 return code;
115             }
116             abnode->everRun = 1;
117             abnode->running = 1;
118             abnode->proc = tp;
119             return 0;
120         }
121     } else {
122         /* run periodically */
123         if (abnode->running)
124             return 0;
125         /* otherwise find out when to run it, and do it then */
126         temp = abnode->when - FT_ApproxTime();
127         if (temp < 1)
128             temp = 1;           /* temp is when to start dude */
129         bnode_SetTimeout((struct bnode *)abnode, temp);
130     }
131     return 0;
132 }
133
134 static int
135 cron_restartp(struct bnode *abnode)
136 {
137     return 0;
138 }
139
140 static int
141 cron_delete(struct bnode *bn)
142 {
143     struct cronbnode *abnode = (struct cronbnode *)bn;
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             char *unused1, char *unused2, char *unused3)
153 {
154     struct cronbnode *te;
155     afs_int32 code;
156     char *cmdpath;
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 = calloc(1, sizeof(struct cronbnode));
165     code = ktime_ParsePeriodic(awhen, &te->whenToRun);
166     if ((code < 0) || (bnode_InitBnode((struct bnode *)te, &cronbnode_ops, ainstance) != 0)) {
167         free(te);
168         free(cmdpath);
169         return NULL;
170     }
171     te->when = ktime_next(&te->whenToRun, 0);
172     te->command = cmdpath;
173     te->whenString = strdup(awhen);
174     return (struct bnode *)te;
175 }
176
177 /* called to SIGKILL a process if it doesn't terminate normally.  In cron, also
178     start up a process if it is time and not already running */
179 static int
180 cron_timeout(struct bnode *bn)
181 {
182     struct cronbnode *abnode = (struct cronbnode *)bn;
183     afs_int32 temp;
184     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((struct bnode *)abnode, 0);
194             code = bnode_NewProc((struct bnode *)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((struct bnode *)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((struct bnode *)abnode, 0);
217     }
218     return 0;
219 }
220
221 static int
222 cron_getstat(struct bnode *bn, afs_int32 * astatus)
223 {
224     struct cronbnode *abnode = (struct cronbnode *)bn;
225     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(struct bnode *bn, afs_int32 astatus)
243 {
244     struct cronbnode *abnode = (struct cronbnode *)bn;
245     if (abnode->waitingForShutdown)
246         return BZBUSY;
247     if (astatus == BSTAT_SHUTDOWN) {
248         if (abnode->running) {
249             /* start shutdown */
250             bnode_StopProc(abnode->proc, SIGTERM);
251             abnode->waitingForShutdown = 1;
252             bnode_SetTimeout((struct bnode *)abnode, SDTIME);
253             /* When shutdown is complete, bproc() calls BOP_PROCEXIT()
254              * [cron_procexit()] which will tell bproc() to no longer
255              * run this cron job.
256              */
257         } else {
258             /* Tell bproc() to no longer run this cron job */
259             bnode_SetTimeout((struct bnode *)abnode, 0);
260         }
261     } else if (astatus == BSTAT_NORMAL) {
262         /* start the cron job
263          * Figure out when to run next and schedule it
264          */
265         abnode->when = ktime_next(&abnode->whenToRun, 0);
266         ScheduleCronBnode(abnode);
267     }
268     return 0;
269 }
270
271 static int
272 cron_procstarted(struct bnode *bn, struct bnode_proc *aproc)
273 {
274     return 0;   /* no op */
275 }
276
277 static int
278 cron_procexit(struct bnode *bn, struct bnode_proc *aproc)
279 {
280     struct cronbnode *abnode = (struct cronbnode *) bn;
281     /* process has exited */
282
283     /* log interesting errors for folks */
284     if (aproc->lastSignal)
285         bozo_Log("cron job %s exited due to signal %d\n", abnode->b.name,
286                  aproc->lastSignal);
287     else if (aproc->lastExit)
288         bozo_Log("cron job %s exited with non-zero code %d\n", abnode->b.name,
289                  aproc->lastExit);
290
291     abnode->waitingForShutdown = 0;
292     abnode->running = 0;
293     abnode->killSent = 0;
294     abnode->proc = (struct bnode_proc *)0;
295
296     /* Figure out when to run next and schedule it */
297     abnode->when = ktime_next(&abnode->whenToRun, 0);
298     ScheduleCronBnode(abnode);
299     return 0;
300 }
301
302 static int
303 cron_getstring(struct bnode *bn, char *abuffer, afs_int32 alen)
304 {
305     struct cronbnode *abnode = (struct cronbnode *)bn;
306     if (abnode->running)
307         strcpy(abuffer, "running now");
308     else if (abnode->when == 0)
309         strcpy(abuffer, "waiting to run once");
310     else
311         sprintf(abuffer, "run next at %s", ktime_DateOf(abnode->when));
312     return 0;
313 }
314
315 static int
316 cron_getparm(struct bnode *bn, afs_int32 aindex, char *abuffer,
317              afs_int32 alen)
318 {
319     struct cronbnode *abnode = (struct cronbnode *)bn;
320     if (aindex == 0)
321         strcpy(abuffer, abnode->command);
322     else if (aindex == 1) {
323         strcpy(abuffer, abnode->whenString);
324     } else
325         return BZDOM;
326     return 0;
327 }