chiark / gitweb /
Unlink lockfiles for old feeds
[innduct.git] / innd / icd.c
1 /*  $Id: icd.c 6156 2003-01-19 20:58:05Z rra $
2 **
3 **  Routines to read and write the active file.
4 */
5
6 #include "config.h"
7 #include "clibrary.h"
8 #include "portable/mmap.h"
9 #include <sys/uio.h>
10
11 #include "inn/innconf.h"
12 #include "innd.h"
13 #include "ov.h"
14
15 /* If we fork and exec under Cygwin, children hold onto the mmap */
16 /* of active, and Windows won't let us resize or replace it.     */
17 #ifdef __CYGWIN__
18 # undef HAVE_MMAP
19 #endif
20
21 static char             *ICDactpath = NULL;
22 static char             *ICDactpointer;
23 static int              ICDactfd;
24 static int              ICDactsize;
25
26
27 /*
28 **  Set and unset (or copy) IOVEC elements.  We make copies to
29 **  avoid problems with mmap.
30 */
31 #ifdef HAVE_MMAP
32 static void
33 ICDiovset(struct iovec *iovp, char *base, int len)
34 {
35     iovp->iov_len = len; 
36     iovp->iov_base = xmalloc(iovp->iov_len); 
37     memcpy(iovp->iov_base, base, iovp->iov_len);
38 }
39 #define ICDiovrelease(iovp)             free((iovp)->iov_base)
40
41 #else /* !HAVE_MMAP */
42
43 #define ICDiovset(iovp, base, len)      \
44         (iovp)->iov_base = base, (iovp)->iov_len = len
45 #define ICDiovrelease(iovp)             /* NULL */
46
47 #endif /* HAVE_MMAP */
48
49
50 /*
51 **  Close the active file, releasing its resources.
52 */
53 static void
54 ICDcloseactive(void)
55 {
56     if (ICDactpointer) {
57 #ifdef HAVE_MMAP
58         if (munmap(ICDactpointer, ICDactsize) < 0)
59             syslog(L_ERROR, "%s cant munmap %s %m", LogName, ICDactpath);
60 #else
61         free(ICDactpointer);
62 #endif
63         ICDactpointer = NULL;
64         if (close(ICDactfd) < 0) {
65             syslog(L_FATAL, "%s cant close %s %m", LogName, ICDactpath);
66             exit(1);
67         }
68     }
69 }
70
71
72 /*
73 **  Set up the hash and in-core tables.
74 */
75 void
76 ICDsetup(StartSites)
77     bool        StartSites;
78 {
79     if (ICDneedsetup == true) {
80         ICDneedsetup = false;
81     }
82     else {
83         ICDcloseactive();
84         NGparsefile();
85     }
86     if (NGfind("control") == NULL || NGfind("junk") == NULL) {
87         syslog(L_FATAL, "%s internal no control and/or junk group", LogName);
88         exit(1);
89     }
90     if (NGfind("control.cancel") == NULL) {
91         syslog(L_FATAL, "%s internal no control.cancel group", LogName);
92         exit(1);
93     }
94     if (innconf->mergetogroups && NGfind("to") == NULL) {
95         syslog(L_FATAL, "%s internal no to group", LogName);
96         exit(1);
97     }
98     SITEparsefile(StartSites);
99 }
100
101
102 /*
103 **  Write out all in-core data.
104 */
105 void
106 ICDwrite(void)
107 {
108     HISsync(History);
109     SMflushcacheddata(SM_ALL);
110
111     if (ICDactivedirty) {
112         ICDwriteactive();
113         ICDactivedirty = 0;
114     }
115
116     /* Flush log and error log. */
117     if (fflush(Log) == EOF)
118         syslog(L_ERROR, "%s cant fflush log %m", LogName);
119     if (fflush(Errlog) == EOF)
120         syslog(L_ERROR, "%s cant fflush errlog %m", LogName);
121 }
122
123
124 /*
125 **  Close things down.
126 */
127 void
128 ICDclose(void)
129 {
130     ICDwrite();
131     ICDcloseactive();
132 }
133
134
135 /*
136 **  Scan the active file, and renumber the min/max counts.
137 */
138 bool
139 ICDrenumberactive(void)
140 {
141     int i;
142     NEWSGROUP   *ngp;
143
144     for (i = nGroups, ngp = Groups; --i >= 0; ngp++)
145         if (!NGrenumber(ngp))
146             return false;
147     if (i < 0)
148         ICDwrite();
149     return true;
150 }
151
152
153 /*
154 **  Use writev() to replace the active file.
155 */
156 static bool
157 ICDwritevactive(struct iovec *vp, int vpcount)
158 {
159     static char         *BACKUP = NULL;
160     static char         *NEWACT = NULL;
161     static char         WHEN[] = "backup active";
162     int                 fd;
163     int                 oerrno;
164 #ifdef __CYGWIN__
165     size_t              newactsize, padactsize, wrote;
166     struct iovec        *newvp;
167     char                *filler;
168     int                 i;
169 #endif
170
171     if (BACKUP == NULL)
172         BACKUP = concatpath(innconf->pathdb, _PATH_OLDACTIVE);
173     if (NEWACT == NULL)
174         NEWACT = concatpath(innconf->pathdb, _PATH_NEWACTIVE);
175     /* Write the current file to a backup. */
176     if (unlink(BACKUP) < 0 && errno != ENOENT) {
177         oerrno = errno;
178         syslog(L_ERROR, "%s cant unlink %s %m", LogName, BACKUP);
179         IOError(WHEN, oerrno);
180     }
181     if ((fd = open(BACKUP, O_WRONLY | O_TRUNC | O_CREAT, 0664)) < 0) {
182         oerrno = errno;
183         syslog(L_ERROR, "%s cant open %s %m", LogName, BACKUP);
184         IOError(WHEN, oerrno);
185     }
186     else if (xwrite(fd, ICDactpointer, ICDactsize) < 0) {
187         oerrno = errno;
188         syslog(L_ERROR, "%s cant write %s %m", LogName, BACKUP);
189         IOError(WHEN, oerrno);
190         close(fd);
191     }
192     else if (close(fd) < 0) {
193         oerrno = errno;
194         syslog(L_ERROR, "%s cant close %s %m", LogName, BACKUP);
195         IOError(WHEN, oerrno);
196     }
197
198 #ifdef __CYGWIN__
199     /* If we are shrinking active, junk will be at the end between the */
200     /* writev and ftruncate.  Clobber it with values that overview and */
201     /* nnrpd can ignore. */
202     for (newactsize = 0, i = 0; i < vpcount; i++)
203          newactsize += vp[i].iov_len;
204     if (newactsize < ICDactsize) {
205          padactsize = ICDactsize - newactsize;
206          newvp = xmalloc((vpcount + 1) * sizeof(struct iovec));
207          for (i = 0; i < vpcount; i++)
208               newvp[i] = vp[i];
209          filler = xcalloc(padactsize, 1);
210          *filler = '.';
211          filler[padactsize - 1] = '\n';
212          newvp[vpcount].iov_base = filler;
213          newvp[vpcount].iov_len = padactsize;
214          vpcount++;
215     }
216     else {
217          padactsize = 0;
218          newvp = vp;
219     }
220     oerrno = 0;
221     if (lseek(ICDactfd, 0, SEEK_SET) == -1) {
222         oerrno = errno;
223         syslog(L_ERROR, "%s cant rewind %s %m", LogName, ICDactpath);
224         IOError(WHEN, oerrno);
225         goto bailout;
226     }
227     if (xwritev(ICDactfd, newvp, vpcount) < 0) {
228         oerrno = errno;
229         syslog(L_ERROR, "%s cant write %s %m", LogName, ICDactpath);
230         IOError(WHEN, oerrno);
231         goto bailout;
232     }
233     if (newactsize < ICDactsize && ftruncate(ICDactfd, newactsize) != 0) {
234         oerrno = errno;
235         syslog(L_ERROR, "%s cant truncate %s", LogName, ICDactpath);
236     }
237
238 bailout:
239     if (padactsize != 0) {
240          free(filler);
241          free(newvp);
242     }
243     if (oerrno != 0)
244          return false;
245
246 #else /* !__CYGWIN__, do it the Unix way. */
247
248     /* Open the active file. */
249     fd = open(NEWACT, O_WRONLY | O_TRUNC | O_CREAT, ARTFILE_MODE);
250     if (fd < 0) {
251         oerrno = errno;
252         syslog(L_ERROR, "%s cant open %s %m", LogName, NEWACT);
253         IOError(WHEN, oerrno);
254         return false;
255     }
256
257     /* Write it. */
258     if (xwritev(fd, vp, vpcount) < 0) {
259         oerrno = errno;
260         syslog(L_ERROR, "%s cant write %s %m", LogName, NEWACT);
261         IOError(WHEN, oerrno);
262         close(fd);
263         return false;
264     }
265
266     /* Close it. */
267     close(fd);
268
269     /* Rename it to be the canonical active file */
270     if (rename(NEWACT, ICDactpath) < 0) {
271         oerrno = errno;
272         syslog(L_ERROR, "%s cant rename %s to %s %m",
273                LogName, NEWACT, ICDactpath);
274         IOError(WHEN, oerrno);
275         return false;
276     }
277
278 #endif /* __CYGWIN__ */
279
280     /* Invalidate in-core pointers. */
281     ICDcloseactive();
282
283     /* Restore in-core pointers. */
284     if (Mode != OMrunning) {
285         ICDneedsetup = true;
286         /* Force the active file into memory. */
287         NGparsefile();
288     }
289     else
290         ICDsetup(true);
291     return true;
292 }
293
294
295 /*
296 **  Change the flag on a newsgroup.  Fairly easy.
297 */
298 bool
299 ICDchangegroup(NEWSGROUP *ngp, char *Rest)
300 {
301     static char         NEWLINE[] = "\n";
302     int                 i;
303     struct iovec        iov[3];
304     bool                ret;
305     char                *Name;
306     long                Last;
307
308     /* Set up the scatter/gather vectors. */
309     ICDiovset(&iov[0], ICDactpointer, ngp->Rest - ICDactpointer);
310     ICDiovset(&iov[1], Rest, strlen(Rest));
311     Name = xstrdup(ngp->Name);
312     Last = ngp->Last;
313     if (++ngp < &Groups[nGroups]) {
314         /* Not the last group, keep the \n from the next line. */
315         i = ngp->Start;
316         ICDiovset(&iov[2], &ICDactpointer[i - 1], ICDactsize - i + 1);
317     }
318     else {
319         /* Last group -- append a newline. */
320         ICDiovset(&iov[2], NEWLINE, strlen(NEWLINE));
321     }
322     ret = ICDwritevactive(iov, 3);
323     ICDiovrelease(&iov[0]);
324     ICDiovrelease(&iov[1]);
325     ICDiovrelease(&iov[2]);
326
327     if (ret) {
328         if (innconf->enableoverview && !OVgroupadd(Name, 0, Last, Rest)) {
329             free(Name);
330             return false;
331         }
332     }
333     free(Name);
334     return ret;
335 }
336
337
338 /*
339 **  Add a newsgroup.  Append a line to the end of the active file and reload.
340 */
341 bool
342 ICDnewgroup(char *Name, char *Rest)
343 {
344     char                buff[SMBUF];
345     struct iovec        iov[2];
346     bool                ret;
347
348     /* Set up the scatter/gather vectors. */
349     if (strlen(Name) + strlen(Rest) > SMBUF - 24) {
350         syslog(L_ERROR, "%s too_long %s", LogName, MaxLength(Name, Name));
351         return false;
352     }
353     snprintf(buff, sizeof(buff), "%s 0000000000 0000000001 %s\n", Name, Rest);
354     ICDiovset(&iov[0], ICDactpointer, ICDactsize);
355     ICDiovset(&iov[1], buff, strlen(buff));
356
357     ret = ICDwritevactive(iov, 2);
358     ICDiovrelease(&iov[0]);
359     ICDiovrelease(&iov[1]);
360     if (ret) {
361         if (innconf->enableoverview && !OVgroupadd(Name, 1, 0, Rest))
362             return false;
363     }
364     return ret;
365 }
366
367
368 /*
369 **  Remove a newsgroup.  Splice the line out of the active file and reload.
370 */
371 bool
372 ICDrmgroup(NEWSGROUP *ngp)
373 {
374     struct iovec iov[2];
375     int i;
376     bool ret;
377     char *Name;
378
379     /* Don't let anyone remove newsgroups that INN requires exist. */
380     if (strcmp(ngp->Name, "junk") == 0 || strcmp(ngp->Name, "control") == 0)
381         return false;
382     if (innconf->mergetogroups && strcmp(ngp->Name, "to") == 0)
383         return false;
384
385     Name = xstrdup(ngp->Name);
386     /* If this is the first group in the file, write everything after. */
387     if (ngp == &Groups[0]) {
388         i = ngp[1].Start;
389         ICDiovset(&iov[0], &ICDactpointer[i], ICDactsize - i);
390         ret = ICDwritevactive(iov, 1);
391         ICDiovrelease(&iov[0]);
392         if (ret) {
393             if (innconf->enableoverview && !OVgroupdel(Name)) {
394                 free(Name);
395                 return false;
396             }
397         }
398         free(Name);
399         return ret;
400     }
401
402     /* Write everything up to this group. */
403     ICDiovset(&iov[0], ICDactpointer, ngp->Start);
404
405     /* If this is the last group, that's all we have to write. */
406     if (ngp == &Groups[nGroups - 1]) {
407         ret = ICDwritevactive(iov, 1);
408         ICDiovrelease(&iov[0]);
409         if (ret) {
410             if (innconf->enableoverview && !OVgroupdel(Name)) {
411                 free(Name);
412                 return false;
413             }
414         }
415         free(Name);
416         return ret;
417     }
418
419     /* Write everything after this group. */
420     i = ngp[1].Start;
421     ICDiovset(&iov[1], &ICDactpointer[i], ICDactsize - i);
422     ret = ICDwritevactive(iov, 2);
423     ICDiovrelease(&iov[0]);
424     ICDiovrelease(&iov[1]);
425     if (ret) {
426         if (innconf->enableoverview && !OVgroupdel(Name)) {
427             free(Name);
428             return false;
429         }
430     }
431     free(Name);
432     return ret;
433 }
434
435 \f
436
437 /*
438 **  Open the active file and "map" it into memory.
439 */
440 char *
441 ICDreadactive(endp)
442     char                **endp;
443 {
444     struct stat         Sb;
445
446     if (ICDactpointer) {
447         *endp = ICDactpointer + ICDactsize;
448         return ICDactpointer;
449     }
450     if (ICDactpath == NULL) 
451         ICDactpath = concatpath(innconf->pathdb, _PATH_ACTIVE);
452     if ((ICDactfd = open(ICDactpath, O_RDWR)) < 0) {
453         syslog(L_FATAL, "%s cant open %s %m", LogName, ICDactpath);
454         exit(1);
455     }
456     close_on_exec(ICDactfd, true);
457
458 #ifdef HAVE_MMAP
459
460     if (fstat(ICDactfd, &Sb) < 0) {
461         syslog(L_FATAL, "%s cant fstat %d %s %m",
462             LogName, ICDactfd, ICDactpath);
463         exit(1);
464     }
465     ICDactsize = Sb.st_size;
466     ICDactpointer = mmap(NULL, ICDactsize, PROT_READ|PROT_WRITE,
467                          MAP_SHARED, ICDactfd, 0);
468     if (ICDactpointer == (char *)-1) {
469         syslog(L_FATAL, "%s cant mmap %d %s %m",
470             LogName, ICDactfd, ICDactpath);
471         exit(1);
472     }
473
474 #else /* !HAVE_MMAP */
475
476     if ((ICDactpointer = ReadInDescriptor(ICDactfd, &Sb)) == NULL) {
477         syslog(L_FATAL, "%s cant read %s %m", LogName, ICDactpath);
478         exit(1);
479     }
480     ICDactsize = Sb.st_size;
481
482 #endif /* HAVE_MMAP */
483
484     *endp = ICDactpointer + ICDactsize;
485     return ICDactpointer;
486 }
487
488
489 /*
490 **  Write the active file out.
491 */
492 void
493 ICDwriteactive(void)
494 {
495 #ifdef HAVE_MMAP
496     if (msync(ICDactpointer, ICDactsize, MS_ASYNC) < 0) {
497         syslog(L_FATAL, "%s msync failed %s %m", LogName, ICDactpath);
498         exit(1);
499     }
500 #else /* !HAVE_MMAP */
501     if (lseek(ICDactfd, 0, SEEK_SET) == -1) {
502         syslog(L_FATAL, "%s cant rewind %s %m", LogName, ICDactpath);
503         exit(1);
504     }
505     if (xwrite(ICDactfd, ICDactpointer, ICDactsize) < 0) {
506         syslog(L_FATAL, "%s cant write %s %m", LogName, ICDactpath);
507         exit(1);
508     }
509 #endif /* HAVE_MMAP */
510 }