1 /* $Id: icd.c 6156 2003-01-19 20:58:05Z rra $
3 ** Routines to read and write the active file.
8 #include "portable/mmap.h"
11 #include "inn/innconf.h"
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. */
21 static char *ICDactpath = NULL;
22 static char *ICDactpointer;
24 static int ICDactsize;
28 ** Set and unset (or copy) IOVEC elements. We make copies to
29 ** avoid problems with mmap.
33 ICDiovset(struct iovec *iovp, char *base, int len)
36 iovp->iov_base = xmalloc(iovp->iov_len);
37 memcpy(iovp->iov_base, base, iovp->iov_len);
39 #define ICDiovrelease(iovp) free((iovp)->iov_base)
41 #else /* !HAVE_MMAP */
43 #define ICDiovset(iovp, base, len) \
44 (iovp)->iov_base = base, (iovp)->iov_len = len
45 #define ICDiovrelease(iovp) /* NULL */
47 #endif /* HAVE_MMAP */
51 ** Close the active file, releasing its resources.
58 if (munmap(ICDactpointer, ICDactsize) < 0)
59 syslog(L_ERROR, "%s cant munmap %s %m", LogName, ICDactpath);
64 if (close(ICDactfd) < 0) {
65 syslog(L_FATAL, "%s cant close %s %m", LogName, ICDactpath);
73 ** Set up the hash and in-core tables.
79 if (ICDneedsetup == true) {
86 if (NGfind("control") == NULL || NGfind("junk") == NULL) {
87 syslog(L_FATAL, "%s internal no control and/or junk group", LogName);
90 if (NGfind("control.cancel") == NULL) {
91 syslog(L_FATAL, "%s internal no control.cancel group", LogName);
94 if (innconf->mergetogroups && NGfind("to") == NULL) {
95 syslog(L_FATAL, "%s internal no to group", LogName);
98 SITEparsefile(StartSites);
103 ** Write out all in-core data.
109 SMflushcacheddata(SM_ALL);
111 if (ICDactivedirty) {
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);
125 ** Close things down.
136 ** Scan the active file, and renumber the min/max counts.
139 ICDrenumberactive(void)
144 for (i = nGroups, ngp = Groups; --i >= 0; ngp++)
145 if (!NGrenumber(ngp))
154 ** Use writev() to replace the active file.
157 ICDwritevactive(struct iovec *vp, int vpcount)
159 static char *BACKUP = NULL;
160 static char *NEWACT = NULL;
161 static char WHEN[] = "backup active";
165 size_t newactsize, padactsize, wrote;
172 BACKUP = concatpath(innconf->pathdb, _PATH_OLDACTIVE);
174 NEWACT = concatpath(innconf->pathdb, _PATH_NEWACTIVE);
175 /* Write the current file to a backup. */
176 if (unlink(BACKUP) < 0 && errno != ENOENT) {
178 syslog(L_ERROR, "%s cant unlink %s %m", LogName, BACKUP);
179 IOError(WHEN, oerrno);
181 if ((fd = open(BACKUP, O_WRONLY | O_TRUNC | O_CREAT, 0664)) < 0) {
183 syslog(L_ERROR, "%s cant open %s %m", LogName, BACKUP);
184 IOError(WHEN, oerrno);
186 else if (xwrite(fd, ICDactpointer, ICDactsize) < 0) {
188 syslog(L_ERROR, "%s cant write %s %m", LogName, BACKUP);
189 IOError(WHEN, oerrno);
192 else if (close(fd) < 0) {
194 syslog(L_ERROR, "%s cant close %s %m", LogName, BACKUP);
195 IOError(WHEN, oerrno);
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++)
209 filler = xcalloc(padactsize, 1);
211 filler[padactsize - 1] = '\n';
212 newvp[vpcount].iov_base = filler;
213 newvp[vpcount].iov_len = padactsize;
221 if (lseek(ICDactfd, 0, SEEK_SET) == -1) {
223 syslog(L_ERROR, "%s cant rewind %s %m", LogName, ICDactpath);
224 IOError(WHEN, oerrno);
227 if (xwritev(ICDactfd, newvp, vpcount) < 0) {
229 syslog(L_ERROR, "%s cant write %s %m", LogName, ICDactpath);
230 IOError(WHEN, oerrno);
233 if (newactsize < ICDactsize && ftruncate(ICDactfd, newactsize) != 0) {
235 syslog(L_ERROR, "%s cant truncate %s", LogName, ICDactpath);
239 if (padactsize != 0) {
246 #else /* !__CYGWIN__, do it the Unix way. */
248 /* Open the active file. */
249 fd = open(NEWACT, O_WRONLY | O_TRUNC | O_CREAT, ARTFILE_MODE);
252 syslog(L_ERROR, "%s cant open %s %m", LogName, NEWACT);
253 IOError(WHEN, oerrno);
258 if (xwritev(fd, vp, vpcount) < 0) {
260 syslog(L_ERROR, "%s cant write %s %m", LogName, NEWACT);
261 IOError(WHEN, oerrno);
269 /* Rename it to be the canonical active file */
270 if (rename(NEWACT, ICDactpath) < 0) {
272 syslog(L_ERROR, "%s cant rename %s to %s %m",
273 LogName, NEWACT, ICDactpath);
274 IOError(WHEN, oerrno);
278 #endif /* __CYGWIN__ */
280 /* Invalidate in-core pointers. */
283 /* Restore in-core pointers. */
284 if (Mode != OMrunning) {
286 /* Force the active file into memory. */
296 ** Change the flag on a newsgroup. Fairly easy.
299 ICDchangegroup(NEWSGROUP *ngp, char *Rest)
301 static char NEWLINE[] = "\n";
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);
313 if (++ngp < &Groups[nGroups]) {
314 /* Not the last group, keep the \n from the next line. */
316 ICDiovset(&iov[2], &ICDactpointer[i - 1], ICDactsize - i + 1);
319 /* Last group -- append a newline. */
320 ICDiovset(&iov[2], NEWLINE, strlen(NEWLINE));
322 ret = ICDwritevactive(iov, 3);
323 ICDiovrelease(&iov[0]);
324 ICDiovrelease(&iov[1]);
325 ICDiovrelease(&iov[2]);
328 if (innconf->enableoverview && !OVgroupadd(Name, 0, Last, Rest)) {
339 ** Add a newsgroup. Append a line to the end of the active file and reload.
342 ICDnewgroup(char *Name, char *Rest)
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));
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));
357 ret = ICDwritevactive(iov, 2);
358 ICDiovrelease(&iov[0]);
359 ICDiovrelease(&iov[1]);
361 if (innconf->enableoverview && !OVgroupadd(Name, 1, 0, Rest))
369 ** Remove a newsgroup. Splice the line out of the active file and reload.
372 ICDrmgroup(NEWSGROUP *ngp)
379 /* Don't let anyone remove newsgroups that INN requires exist. */
380 if (strcmp(ngp->Name, "junk") == 0 || strcmp(ngp->Name, "control") == 0)
382 if (innconf->mergetogroups && strcmp(ngp->Name, "to") == 0)
385 Name = xstrdup(ngp->Name);
386 /* If this is the first group in the file, write everything after. */
387 if (ngp == &Groups[0]) {
389 ICDiovset(&iov[0], &ICDactpointer[i], ICDactsize - i);
390 ret = ICDwritevactive(iov, 1);
391 ICDiovrelease(&iov[0]);
393 if (innconf->enableoverview && !OVgroupdel(Name)) {
402 /* Write everything up to this group. */
403 ICDiovset(&iov[0], ICDactpointer, ngp->Start);
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]);
410 if (innconf->enableoverview && !OVgroupdel(Name)) {
419 /* Write everything after this group. */
421 ICDiovset(&iov[1], &ICDactpointer[i], ICDactsize - i);
422 ret = ICDwritevactive(iov, 2);
423 ICDiovrelease(&iov[0]);
424 ICDiovrelease(&iov[1]);
426 if (innconf->enableoverview && !OVgroupdel(Name)) {
438 ** Open the active file and "map" it into memory.
447 *endp = ICDactpointer + ICDactsize;
448 return ICDactpointer;
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);
456 close_on_exec(ICDactfd, true);
460 if (fstat(ICDactfd, &Sb) < 0) {
461 syslog(L_FATAL, "%s cant fstat %d %s %m",
462 LogName, ICDactfd, ICDactpath);
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);
474 #else /* !HAVE_MMAP */
476 if ((ICDactpointer = ReadInDescriptor(ICDactfd, &Sb)) == NULL) {
477 syslog(L_FATAL, "%s cant read %s %m", LogName, ICDactpath);
480 ICDactsize = Sb.st_size;
482 #endif /* HAVE_MMAP */
484 *endp = ICDactpointer + ICDactsize;
485 return ICDactpointer;
490 ** Write the active file out.
496 if (msync(ICDactpointer, ICDactsize, MS_ASYNC) < 0) {
497 syslog(L_FATAL, "%s msync failed %s %m", LogName, ICDactpath);
500 #else /* !HAVE_MMAP */
501 if (lseek(ICDactfd, 0, SEEK_SET) == -1) {
502 syslog(L_FATAL, "%s cant rewind %s %m", LogName, ICDactpath);
505 if (xwrite(ICDactfd, ICDactpointer, ICDactsize) < 0) {
506 syslog(L_FATAL, "%s cant write %s %m", LogName, ICDactpath);
509 #endif /* HAVE_MMAP */