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