Import Upstream version 1.8.5
[hcoop/debian/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 #include <opr/queue.h>
22
23 #include "bnode.h"
24 #include "bnode_internal.h"
25 #include "bosprototypes.h"
26
27 struct bnode *cron_create(char *, char *, char *, char *, char *, char *);
28 static int cron_hascore(struct bnode *bnode);
29 static int cron_restartp(struct bnode *bnode);
30 static int cron_delete(struct bnode *bnode);
31 static int cron_timeout(struct bnode *bnode);
32 static int cron_getstat(struct bnode *bnode, afs_int32 *status);
33 static int cron_setstat(struct bnode *bnode, afs_int32 status);
34 static int cron_procstarted(struct bnode *bnode, struct bnode_proc *proc);
35 static int cron_procexit(struct bnode *bnode, struct bnode_proc *proc);
36 static int cron_getstring(struct bnode *bnode, char *abuffer, afs_int32 alen);
37 static int cron_getparm(struct bnode *bnode, afs_int32, char *, afs_int32);
38
39 #define SDTIME 60 /* time in seconds given to a process to evaporate */
40
41 struct bnode_ops cronbnode_ops = {
42 cron_create,
43 cron_timeout,
44 cron_getstat,
45 cron_setstat,
46 cron_delete,
47 cron_procexit,
48 cron_getstring,
49 cron_getparm,
50 cron_restartp,
51 cron_hascore,
52 cron_procstarted,
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 = calloc(1, sizeof(struct cronbnode));
167 code = ktime_ParsePeriodic(awhen, &te->whenToRun);
168 if ((code < 0) || (bnode_InitBnode((struct bnode *)te, &cronbnode_ops, ainstance) != 0)) {
169 free(te);
170 free(cmdpath);
171 return NULL;
172 }
173 te->when = ktime_next(&te->whenToRun, 0);
174 te->command = cmdpath;
175 te->whenString = strdup(awhen);
176 return (struct bnode *)te;
177 }
178
179 /* called to SIGKILL a process if it doesn't terminate normally. In cron, also
180 start up a process if it is time and not already running */
181 static int
182 cron_timeout(struct bnode *bn)
183 {
184 struct cronbnode *abnode = (struct cronbnode *)bn;
185 afs_int32 temp;
186 afs_int32 code;
187 struct bnode_proc *tp;
188
189 if (!abnode->running) {
190 if (abnode->when == 0)
191 return 0; /* spurious timeout activation */
192 /* not running, perhaps we should start it */
193 if (FT_ApproxTime() >= abnode->when) {
194 abnode->lastStart = FT_ApproxTime();
195 bnode_SetTimeout((struct bnode *)abnode, 0);
196 code = bnode_NewProc((struct bnode *)abnode, abnode->command, NULL, &tp);
197 if (code) {
198 bozo_Log("cron failed to start bnode %s (code %d)\n",
199 abnode->b.name, code);
200 return code;
201 }
202 abnode->everRun = 1;
203 abnode->running = 1;
204 abnode->proc = tp;
205 } else {
206 /* woke up too early, try again */
207 temp = abnode->when - FT_ApproxTime();
208 if (temp < 1)
209 temp = 1;
210 bnode_SetTimeout((struct bnode *)abnode, temp);
211 }
212 } else {
213 if (!abnode->waitingForShutdown)
214 return 0; /* spurious */
215 /* send kill and turn off timer */
216 bnode_StopProc(abnode->proc, SIGKILL);
217 abnode->killSent = 1;
218 bnode_SetTimeout((struct bnode *)abnode, 0);
219 }
220 return 0;
221 }
222
223 static int
224 cron_getstat(struct bnode *bn, afs_int32 * astatus)
225 {
226 struct cronbnode *abnode = (struct cronbnode *)bn;
227 afs_int32 temp;
228 if (abnode->waitingForShutdown)
229 temp = BSTAT_SHUTTINGDOWN;
230 else if (abnode->b.goal == 0)
231 temp = BSTAT_SHUTDOWN;
232 else if (abnode->everRun && abnode->when == 0 && !abnode->running) {
233 /* special hack: bnode deletion won't happen if bnode is active, so
234 * we make bnodes that are ready to be deleted automatically appear
235 * as BSTAT_SHUTDOWN so bnode_Delete is happy. */
236 temp = BSTAT_SHUTDOWN;
237 } else
238 temp = BSTAT_NORMAL;
239 *astatus = temp;
240 return 0;
241 }
242
243 static int
244 cron_setstat(struct bnode *bn, afs_int32 astatus)
245 {
246 struct cronbnode *abnode = (struct cronbnode *)bn;
247 if (abnode->waitingForShutdown)
248 return BZBUSY;
249 if (astatus == BSTAT_SHUTDOWN) {
250 if (abnode->running) {
251 /* start shutdown */
252 bnode_StopProc(abnode->proc, SIGTERM);
253 abnode->waitingForShutdown = 1;
254 bnode_SetTimeout((struct bnode *)abnode, SDTIME);
255 /* When shutdown is complete, bproc() calls BOP_PROCEXIT()
256 * [cron_procexit()] which will tell bproc() to no longer
257 * run this cron job.
258 */
259 } else {
260 /* Tell bproc() to no longer run this cron job */
261 bnode_SetTimeout((struct bnode *)abnode, 0);
262 }
263 } else if (astatus == BSTAT_NORMAL) {
264 /* start the cron job
265 * Figure out when to run next and schedule it
266 */
267 abnode->when = ktime_next(&abnode->whenToRun, 0);
268 ScheduleCronBnode(abnode);
269 }
270 return 0;
271 }
272
273 static int
274 cron_procstarted(struct bnode *bn, struct bnode_proc *aproc)
275 {
276 return 0; /* no op */
277 }
278
279 static int
280 cron_procexit(struct bnode *bn, struct bnode_proc *aproc)
281 {
282 struct cronbnode *abnode = (struct cronbnode *) bn;
283 /* process has exited */
284
285 /* log interesting errors for folks */
286 if (aproc->lastSignal)
287 bozo_Log("cron job %s exited due to signal %d\n", abnode->b.name,
288 aproc->lastSignal);
289 else if (aproc->lastExit)
290 bozo_Log("cron job %s exited with non-zero code %d\n", abnode->b.name,
291 aproc->lastExit);
292
293 abnode->waitingForShutdown = 0;
294 abnode->running = 0;
295 abnode->killSent = 0;
296 abnode->proc = (struct bnode_proc *)0;
297
298 /* Figure out when to run next and schedule it */
299 abnode->when = ktime_next(&abnode->whenToRun, 0);
300 ScheduleCronBnode(abnode);
301 return 0;
302 }
303
304 static int
305 cron_getstring(struct bnode *bn, char *abuffer, afs_int32 alen)
306 {
307 struct cronbnode *abnode = (struct cronbnode *)bn;
308 if (abnode->running)
309 strcpy(abuffer, "running now");
310 else if (abnode->when == 0)
311 strcpy(abuffer, "waiting to run once");
312 else
313 sprintf(abuffer, "run next at %s", ktime_DateOf(abnode->when));
314 return 0;
315 }
316
317 static int
318 cron_getparm(struct bnode *bn, afs_int32 aindex, char *abuffer,
319 afs_int32 alen)
320 {
321 struct cronbnode *abnode = (struct cronbnode *)bn;
322 if (aindex == 0)
323 strcpy(abuffer, abnode->command);
324 else if (aindex == 1) {
325 strcpy(abuffer, abnode->whenString);
326 } else
327 return BZDOM;
328 return 0;
329 }