Commit | Line | Data |
---|---|---|
805e021f CE |
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 | } |