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