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