/* $Id: icd.c 6156 2003-01-19 20:58:05Z rra $ ** ** Routines to read and write the active file. */ #include "config.h" #include "clibrary.h" #include "portable/mmap.h" #include #include "inn/innconf.h" #include "innd.h" #include "ov.h" /* If we fork and exec under Cygwin, children hold onto the mmap */ /* of active, and Windows won't let us resize or replace it. */ #ifdef __CYGWIN__ # undef HAVE_MMAP #endif static char *ICDactpath = NULL; static char *ICDactpointer; static int ICDactfd; static int ICDactsize; /* ** Set and unset (or copy) IOVEC elements. We make copies to ** avoid problems with mmap. */ #ifdef HAVE_MMAP static void ICDiovset(struct iovec *iovp, char *base, int len) { iovp->iov_len = len; iovp->iov_base = xmalloc(iovp->iov_len); memcpy(iovp->iov_base, base, iovp->iov_len); } #define ICDiovrelease(iovp) free((iovp)->iov_base) #else /* !HAVE_MMAP */ #define ICDiovset(iovp, base, len) \ (iovp)->iov_base = base, (iovp)->iov_len = len #define ICDiovrelease(iovp) /* NULL */ #endif /* HAVE_MMAP */ /* ** Close the active file, releasing its resources. */ static void ICDcloseactive(void) { if (ICDactpointer) { #ifdef HAVE_MMAP if (munmap(ICDactpointer, ICDactsize) < 0) syslog(L_ERROR, "%s cant munmap %s %m", LogName, ICDactpath); #else free(ICDactpointer); #endif ICDactpointer = NULL; if (close(ICDactfd) < 0) { syslog(L_FATAL, "%s cant close %s %m", LogName, ICDactpath); exit(1); } } } /* ** Set up the hash and in-core tables. */ void ICDsetup(StartSites) bool StartSites; { if (ICDneedsetup == true) { ICDneedsetup = false; } else { ICDcloseactive(); NGparsefile(); } if (NGfind("control") == NULL || NGfind("junk") == NULL) { syslog(L_FATAL, "%s internal no control and/or junk group", LogName); exit(1); } if (NGfind("control.cancel") == NULL) { syslog(L_FATAL, "%s internal no control.cancel group", LogName); exit(1); } if (innconf->mergetogroups && NGfind("to") == NULL) { syslog(L_FATAL, "%s internal no to group", LogName); exit(1); } SITEparsefile(StartSites); } /* ** Write out all in-core data. */ void ICDwrite(void) { HISsync(History); SMflushcacheddata(SM_ALL); if (ICDactivedirty) { ICDwriteactive(); ICDactivedirty = 0; } /* Flush log and error log. */ if (fflush(Log) == EOF) syslog(L_ERROR, "%s cant fflush log %m", LogName); if (fflush(Errlog) == EOF) syslog(L_ERROR, "%s cant fflush errlog %m", LogName); } /* ** Close things down. */ void ICDclose(void) { ICDwrite(); ICDcloseactive(); } /* ** Scan the active file, and renumber the min/max counts. */ bool ICDrenumberactive(void) { int i; NEWSGROUP *ngp; for (i = nGroups, ngp = Groups; --i >= 0; ngp++) if (!NGrenumber(ngp)) return false; if (i < 0) ICDwrite(); return true; } /* ** Use writev() to replace the active file. */ static bool ICDwritevactive(struct iovec *vp, int vpcount) { static char *BACKUP = NULL; static char *NEWACT = NULL; static char WHEN[] = "backup active"; int fd; int oerrno; #ifdef __CYGWIN__ size_t newactsize, padactsize, wrote; struct iovec *newvp; char *filler; int i; #endif if (BACKUP == NULL) BACKUP = concatpath(innconf->pathdb, _PATH_OLDACTIVE); if (NEWACT == NULL) NEWACT = concatpath(innconf->pathdb, _PATH_NEWACTIVE); /* Write the current file to a backup. */ if (unlink(BACKUP) < 0 && errno != ENOENT) { oerrno = errno; syslog(L_ERROR, "%s cant unlink %s %m", LogName, BACKUP); IOError(WHEN, oerrno); } if ((fd = open(BACKUP, O_WRONLY | O_TRUNC | O_CREAT, 0664)) < 0) { oerrno = errno; syslog(L_ERROR, "%s cant open %s %m", LogName, BACKUP); IOError(WHEN, oerrno); } else if (xwrite(fd, ICDactpointer, ICDactsize) < 0) { oerrno = errno; syslog(L_ERROR, "%s cant write %s %m", LogName, BACKUP); IOError(WHEN, oerrno); close(fd); } else if (close(fd) < 0) { oerrno = errno; syslog(L_ERROR, "%s cant close %s %m", LogName, BACKUP); IOError(WHEN, oerrno); } #ifdef __CYGWIN__ /* If we are shrinking active, junk will be at the end between the */ /* writev and ftruncate. Clobber it with values that overview and */ /* nnrpd can ignore. */ for (newactsize = 0, i = 0; i < vpcount; i++) newactsize += vp[i].iov_len; if (newactsize < ICDactsize) { padactsize = ICDactsize - newactsize; newvp = xmalloc((vpcount + 1) * sizeof(struct iovec)); for (i = 0; i < vpcount; i++) newvp[i] = vp[i]; filler = xcalloc(padactsize, 1); *filler = '.'; filler[padactsize - 1] = '\n'; newvp[vpcount].iov_base = filler; newvp[vpcount].iov_len = padactsize; vpcount++; } else { padactsize = 0; newvp = vp; } oerrno = 0; if (lseek(ICDactfd, 0, SEEK_SET) == -1) { oerrno = errno; syslog(L_ERROR, "%s cant rewind %s %m", LogName, ICDactpath); IOError(WHEN, oerrno); goto bailout; } if (xwritev(ICDactfd, newvp, vpcount) < 0) { oerrno = errno; syslog(L_ERROR, "%s cant write %s %m", LogName, ICDactpath); IOError(WHEN, oerrno); goto bailout; } if (newactsize < ICDactsize && ftruncate(ICDactfd, newactsize) != 0) { oerrno = errno; syslog(L_ERROR, "%s cant truncate %s", LogName, ICDactpath); } bailout: if (padactsize != 0) { free(filler); free(newvp); } if (oerrno != 0) return false; #else /* !__CYGWIN__, do it the Unix way. */ /* Open the active file. */ fd = open(NEWACT, O_WRONLY | O_TRUNC | O_CREAT, ARTFILE_MODE); if (fd < 0) { oerrno = errno; syslog(L_ERROR, "%s cant open %s %m", LogName, NEWACT); IOError(WHEN, oerrno); return false; } /* Write it. */ if (xwritev(fd, vp, vpcount) < 0) { oerrno = errno; syslog(L_ERROR, "%s cant write %s %m", LogName, NEWACT); IOError(WHEN, oerrno); close(fd); return false; } /* Close it. */ close(fd); /* Rename it to be the canonical active file */ if (rename(NEWACT, ICDactpath) < 0) { oerrno = errno; syslog(L_ERROR, "%s cant rename %s to %s %m", LogName, NEWACT, ICDactpath); IOError(WHEN, oerrno); return false; } #endif /* __CYGWIN__ */ /* Invalidate in-core pointers. */ ICDcloseactive(); /* Restore in-core pointers. */ if (Mode != OMrunning) { ICDneedsetup = true; /* Force the active file into memory. */ NGparsefile(); } else ICDsetup(true); return true; } /* ** Change the flag on a newsgroup. Fairly easy. */ bool ICDchangegroup(NEWSGROUP *ngp, char *Rest) { static char NEWLINE[] = "\n"; int i; struct iovec iov[3]; bool ret; char *Name; long Last; /* Set up the scatter/gather vectors. */ ICDiovset(&iov[0], ICDactpointer, ngp->Rest - ICDactpointer); ICDiovset(&iov[1], Rest, strlen(Rest)); Name = xstrdup(ngp->Name); Last = ngp->Last; if (++ngp < &Groups[nGroups]) { /* Not the last group, keep the \n from the next line. */ i = ngp->Start; ICDiovset(&iov[2], &ICDactpointer[i - 1], ICDactsize - i + 1); } else { /* Last group -- append a newline. */ ICDiovset(&iov[2], NEWLINE, strlen(NEWLINE)); } ret = ICDwritevactive(iov, 3); ICDiovrelease(&iov[0]); ICDiovrelease(&iov[1]); ICDiovrelease(&iov[2]); if (ret) { if (innconf->enableoverview && !OVgroupadd(Name, 0, Last, Rest)) { free(Name); return false; } } free(Name); return ret; } /* ** Add a newsgroup. Append a line to the end of the active file and reload. */ bool ICDnewgroup(char *Name, char *Rest) { char buff[SMBUF]; struct iovec iov[2]; bool ret; /* Set up the scatter/gather vectors. */ if (strlen(Name) + strlen(Rest) > SMBUF - 24) { syslog(L_ERROR, "%s too_long %s", LogName, MaxLength(Name, Name)); return false; } snprintf(buff, sizeof(buff), "%s 0000000000 0000000001 %s\n", Name, Rest); ICDiovset(&iov[0], ICDactpointer, ICDactsize); ICDiovset(&iov[1], buff, strlen(buff)); ret = ICDwritevactive(iov, 2); ICDiovrelease(&iov[0]); ICDiovrelease(&iov[1]); if (ret) { if (innconf->enableoverview && !OVgroupadd(Name, 1, 0, Rest)) return false; } return ret; } /* ** Remove a newsgroup. Splice the line out of the active file and reload. */ bool ICDrmgroup(NEWSGROUP *ngp) { struct iovec iov[2]; int i; bool ret; char *Name; /* Don't let anyone remove newsgroups that INN requires exist. */ if (strcmp(ngp->Name, "junk") == 0 || strcmp(ngp->Name, "control") == 0) return false; if (innconf->mergetogroups && strcmp(ngp->Name, "to") == 0) return false; Name = xstrdup(ngp->Name); /* If this is the first group in the file, write everything after. */ if (ngp == &Groups[0]) { i = ngp[1].Start; ICDiovset(&iov[0], &ICDactpointer[i], ICDactsize - i); ret = ICDwritevactive(iov, 1); ICDiovrelease(&iov[0]); if (ret) { if (innconf->enableoverview && !OVgroupdel(Name)) { free(Name); return false; } } free(Name); return ret; } /* Write everything up to this group. */ ICDiovset(&iov[0], ICDactpointer, ngp->Start); /* If this is the last group, that's all we have to write. */ if (ngp == &Groups[nGroups - 1]) { ret = ICDwritevactive(iov, 1); ICDiovrelease(&iov[0]); if (ret) { if (innconf->enableoverview && !OVgroupdel(Name)) { free(Name); return false; } } free(Name); return ret; } /* Write everything after this group. */ i = ngp[1].Start; ICDiovset(&iov[1], &ICDactpointer[i], ICDactsize - i); ret = ICDwritevactive(iov, 2); ICDiovrelease(&iov[0]); ICDiovrelease(&iov[1]); if (ret) { if (innconf->enableoverview && !OVgroupdel(Name)) { free(Name); return false; } } free(Name); return ret; } /* ** Open the active file and "map" it into memory. */ char * ICDreadactive(endp) char **endp; { struct stat Sb; if (ICDactpointer) { *endp = ICDactpointer + ICDactsize; return ICDactpointer; } if (ICDactpath == NULL) ICDactpath = concatpath(innconf->pathdb, _PATH_ACTIVE); if ((ICDactfd = open(ICDactpath, O_RDWR)) < 0) { syslog(L_FATAL, "%s cant open %s %m", LogName, ICDactpath); exit(1); } close_on_exec(ICDactfd, true); #ifdef HAVE_MMAP if (fstat(ICDactfd, &Sb) < 0) { syslog(L_FATAL, "%s cant fstat %d %s %m", LogName, ICDactfd, ICDactpath); exit(1); } ICDactsize = Sb.st_size; ICDactpointer = mmap(NULL, ICDactsize, PROT_READ|PROT_WRITE, MAP_SHARED, ICDactfd, 0); if (ICDactpointer == (char *)-1) { syslog(L_FATAL, "%s cant mmap %d %s %m", LogName, ICDactfd, ICDactpath); exit(1); } #else /* !HAVE_MMAP */ if ((ICDactpointer = ReadInDescriptor(ICDactfd, &Sb)) == NULL) { syslog(L_FATAL, "%s cant read %s %m", LogName, ICDactpath); exit(1); } ICDactsize = Sb.st_size; #endif /* HAVE_MMAP */ *endp = ICDactpointer + ICDactsize; return ICDactpointer; } /* ** Write the active file out. */ void ICDwriteactive(void) { #ifdef HAVE_MMAP if (msync(ICDactpointer, ICDactsize, MS_ASYNC) < 0) { syslog(L_FATAL, "%s msync failed %s %m", LogName, ICDactpath); exit(1); } #else /* !HAVE_MMAP */ if (lseek(ICDactfd, 0, SEEK_SET) == -1) { syslog(L_FATAL, "%s cant rewind %s %m", LogName, ICDactpath); exit(1); } if (xwrite(ICDactfd, ICDactpointer, ICDactsize) < 0) { syslog(L_FATAL, "%s cant write %s %m", LogName, ICDactpath); exit(1); } #endif /* HAVE_MMAP */ }