chiark / gitweb /
Commit 2.4.5-5 as unpacked
[inn-innduct.git] / contrib / expirectl.c
1 /*
2  * EXPIRECTL.C
3  *
4  * expirectl
5  *
6  * This program uses expire.ctl.ctl as input; please see the end of this
7  * file for an example of such a file.
8  */
9
10 /*
11  * Date: Mon, 21 Nov 1994 12:29:52 -0801
12  * From: Matthew Dillon <dillon@apollo.west.oic.com>
13  * Message-Id: <199411212030.MAA21835@apollo.west.oic.com>
14  * To: rsalz@uunet.uu.net
15  * Subject: Re:  INN is great, bug fix for BSDI
16  * 
17  * [...]
18  *     Oh, while I'm at it, I also wrote a cute program that builds the 
19  *     expire.ctl file dynamically based on available space.   Feel free
20  *     to include this in the dist (or not) as you please.
21  * 
22  *     Basically, the expirectl programs determines the amount of disk blocks
23  *     and inodes free in the spool and creates a new expire.ctl file based
24  *     on an expire.ctl.ctl template.  The template specifies expiration times
25  *     as a fraction of nominal.  expirectl adjusts the nominal expiration
26  *     up or down based on available disk space.
27  * 
28  *     The idea is to make expiration as hands off as possible.  I tested
29  *     it on a smaller spool and it appeared to work fine.  Currently it
30  *     only works for single-partition news spools tho.  The above spool
31  *     will not really exercise the program for another 14 days or so :-).
32  */
33
34
35 #include <sys/types.h>
36 #include <sys/mount.h>
37 #include <errno.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41
42 #define EXPIRE_CTL_DIR  "/home/news"
43 #define NEWS_SPOOL      "/home/news/spool/news/."
44
45 #define EXPIRE_DAYS     EXPIRE_CTL_DIR "/expire.days"
46 #define EXPIRE_CTL      EXPIRE_CTL_DIR "/expire.ctl"
47 #define EXPIRE_CTL_CTL  EXPIRE_CTL_DIR "/expire.ctl.ctl"
48
49 void
50 main(int ac, char **av)
51 {
52     struct statfs sfs;
53     long minFree = 100 * 1024 * 1024;
54     long minIFree = 20 * 1024;
55     long expireDays = 2;
56     time_t expireIncTime = time(NULL) - 24 * 60 * 60;
57     int modified = 0;
58     int verbose = 0;
59
60     /*
61      * options
62      */
63
64     {
65         int i;
66
67         for (i = 1; i < ac; ++i) {
68             char *ptr = av[i];
69
70             if (*ptr == '-') {
71                 ptr += 2;
72                 switch(ptr[-1]) {
73                 case 'v':
74                     verbose = 1;
75                     break;
76                 case 'f':
77                     modified = 1;
78                     break;
79                 case 'n':
80                     modified = -1;
81                     break;
82                 case 'b':
83                     minFree = strtol(((*ptr) ? ptr : av[++i]), &ptr, 0);
84                     if (*ptr == 'k')
85                         minFree *= 1024;
86                     if (*ptr == 'm')
87                         minFree *= 1024 * 1024;
88                     break;
89                 case 'i':
90                     minIFree = strtol(((*ptr) ? ptr : av[++i]), NULL, 0);
91                     if (*ptr == 'k')
92                         minIFree *= 1024;
93                     if (*ptr == 'm')
94                         minIFree *= 1024 * 1024;
95                     break;
96                 default:
97                     fprintf(stderr, "bad option: %s\n", ptr - 2);
98                     exit(1);
99                 }
100             } else {
101                 fprintf(stderr, "bad option: %s\n", ptr);
102                 exit(1);
103             }
104         }
105     }
106
107     if (statfs("/home/news/spool/news/.", &sfs) != 0) {
108         fprintf(stderr, "expirectl: couldn't fsstat /home/news/spool/news/.\n");
109         exit(1);
110     }
111
112     /*
113      * Load /home/news/expire.days
114      */
115     
116     {
117         FILE *fi;
118         char buf[256];
119
120         if ((fi = fopen(EXPIRE_DAYS, "r")) != NULL) {
121             while (fgets(buf, sizeof(buf), fi) != NULL) {
122                 if (strncmp(buf, "time", 4) == 0) {
123                     expireIncTime = strtol(buf + 4, NULL, 0);
124                 } else if (strncmp(buf, "days", 4) == 0) {
125                     expireDays = strtol(buf + 4, NULL, 0);
126                 }
127             }
128             fclose(fi);
129         } else {
130             if (modified >= 0)
131                 modified = 1;
132             printf("creating %s\n", EXPIRE_DAYS);
133         }
134     }
135
136     /*
137      * print status
138      */
139
140     if (verbose) {
141         printf("spool: %4.2lfM / %3.2lfKinode free\n",
142             (double)sfs.f_fsize * (double)sfs.f_bavail / (1024.0 * 1024.0),
143             (double)sfs.f_ffree / 1024.0
144         );
145         printf("decrs: %4.2lfM / %3.2lfKinode\n",
146             (double)(minFree) / (double)(1024*1024),
147             (double)(minIFree) / (double)(1024)
148         );
149         printf("incrs: %4.2lfM / %3.2lfKinode\n",
150             (double)(minFree * 2) / (double)(1024*1024),
151             (double)(minIFree * 2) / (double)(1024)
152         );
153     }
154
155     /*
156      * Check limits, update as appropriate
157      */
158
159     {
160         double bytes;
161         long inodes;
162
163         bytes = (double)sfs.f_fsize * (double)sfs.f_bavail;
164         inodes = sfs.f_ffree;
165
166         if (bytes < (double)minFree || inodes < minIFree) {
167             if (--expireDays <= 0) {
168                 expireDays = 1;
169                 expireIncTime = time(NULL) - 24 * 60 * 60;
170             }
171             if (modified >= 0)
172                 modified = 1;
173             printf("decrement expiration to %d days\n", expireDays);
174         } else if (bytes >= (double)minFree * 2.0 && inodes >= minIFree * 2) {
175             long dt = (long)(time(NULL) - expireIncTime);
176
177             if (dt >= 60 * 60 * 24 || dt < -60) {
178                 ++expireDays;
179                 expireIncTime = time(NULL);
180                 if (modified >= 0)
181                     modified = 1;
182                 printf("increment expiration to %d days\n", expireDays);
183             } else {
184                 printf("will increment expiration later\n");
185             }
186         } else if (verbose) {
187             printf("expiration unchanged: %d\n", expireDays);
188         }
189     }
190
191     /*
192      * Write EXPIRE_CTL file from EXPIRE_CTL_CTL template
193      */
194
195     if (modified > 0) {
196         FILE *fi;
197         FILE *fo;
198
199         if ((fi = fopen(EXPIRE_CTL_CTL, "r")) != NULL) {
200             if ((fo = fopen(EXPIRE_CTL ".tmp", "w")) != NULL) {
201                 char sbuf[2048];
202                 char dbuf[4096];
203
204                 while (fgets(sbuf, sizeof(sbuf), fi) != NULL) {
205                     char *base = sbuf;
206                     char *sptr;
207                     char *dptr = dbuf;
208
209                     while ((sptr = strchr(base, '[')) != NULL) {
210                         double d;
211                         int m = 0;
212
213                         bcopy(base, dptr, sptr - base);
214                         dptr += sptr - base;
215                         base = sptr;
216
217                         d = strtod(sptr + 1, &sptr);
218                         if (*sptr == '/')
219                             m = strtol(sptr + 1, &sptr, 0);
220                         if (*sptr == ']') {
221                             long v = (long)((double)expireDays * d + 0.5);
222                             if (v < 1)
223                                 v = 1;
224                             if (v < m)
225                                 v = m;
226                             sprintf(dptr, "%d", v);
227                             dptr += strlen(dptr);
228                             ++sptr;
229                         }
230                         base = sptr;
231                     }
232                     strcpy(dptr, base);
233                     fputs(dbuf, fo);
234                 }
235                 fclose(fo);
236                 if (rename(EXPIRE_CTL ".tmp", EXPIRE_CTL) != 0) {
237                     fprintf(stderr, "rename(%s,%s): %s\n",
238                         EXPIRE_CTL ".tmp",
239                         EXPIRE_CTL,
240                         strerror(errno)
241                     );
242                 }
243             }
244             fclose(fi);
245         }
246     }
247
248     /*
249      * Write EXPIRE_DAYS file
250      */
251     
252     if (modified > 0) {
253         FILE *fo;
254
255         if ((fo = fopen(EXPIRE_DAYS, "w")) != NULL) {
256             fprintf(fo, "time 0x%08lx\n", expireIncTime);
257             fprintf(fo, "days %d\n", expireDays);
258             fclose(fo);
259         } else {
260             fprintf(stderr, "unable to create %s\n", EXPIRE_DAYS);
261         }
262     }
263     exit(0);
264 }
265
266
267 /*
268
269 # Start of sample expire.ctl.ctl file.
270
271 # EXPIRE.CTL.CTL (EXPIRE.CTL GENERATED FROM EXPIRE.CTL.CTL !!!)
272 #
273 # The expire.ctl file is generated by the expirectl program from the
274 # expire.ctl.ctl file.  The expirectl program calculates the proper
275 # expiration based on the number of free inodes and free bytes available.
276 #
277 # This file is exactly expire.ctl but with the multiplier [N] replaced by 
278 # a calculated value, where a multiplier of '1' nominally fills the whole
279 # disk.
280 #
281 # Any field [N] is substituted after being multiplied by the expiration
282 # time (in days).  A integer minimum can also be specified with a slash,
283 # as in [N/minimum].
284 #
285 # expirectl is normally run just after expire is run.  Note that expirectl
286 # isn't very useful for the case where you are 'catching up' on news after
287 # a long period of downtime UNLESS you use the -p option to expire.
288
289 /remember/:[1.2/20]
290
291 ##  Keep for 1-10 days, allow Expires headers to work.
292 #
293 *:A:1:[1.0]:[6.0]
294 *.advocacy:A:1:[0.5]:[2.0]
295 alt.binaries.pictures.erotica:A:1:[0.8]:[2.0]
296
297 # permanent, semi-permanent
298 #
299 best.intro:A:never:never:never
300 best.announce:A:5:60:120
301 best.general:A:never:never:never
302 best.bugs:A:never:never:never
303
304 # End of sample expire.ctl.ctl file.
305
306 */