1 /* $Id: expireover.c 6708 2004-05-16 19:59:26Z rra $
3 ** Expire the overview database.
5 ** This program handles the nightly expiration of overview information. If
6 ** groupbaseexpiry is true, this program also handles the removal of
7 ** articles that have expired. It's separate from the process that scans
8 ** and expires the history file.
19 #include "inn/innconf.h"
20 #include "inn/messages.h"
27 static const char usage[] = "\
28 Usage: expireover [-ekNpqs] [-w offset] [-z rmfile] [-Z lowmarkfile]\n";
30 /* Set to 1 if we've received a signal; expireover then terminates after
31 finishing the newsgroup that it's working on (this prevents corruption of
32 the overview by killing expireover). */
33 static volatile sig_atomic_t signalled = 0;
37 ** Handle a fatal signal and set signalled. Restore the default signal
38 ** behavior after receiving a signal so that repeating the signal will kill
39 ** the program immediately.
45 xsignal(sig, SIG_DFL);
50 ** Change to the news user if possible, and if not, die. Used for operations
51 ** that may create new database files so as not to mess up the ownership.
58 pwd = getpwnam(NEWSUSER);
60 die("can't resolve %s to a UID (account doesn't exist?)", NEWSUSER);
63 if (getuid() != pwd->pw_uid)
64 die("must be run as %s", NEWSUSER);
69 main(int argc, char *argv[])
76 char *active_path = NULL;
77 char *lowmark_path = NULL;
80 bool purge_deleted = false;
81 bool always_stat = false;
82 struct history *history;
84 /* First thing, set up logging and our identity. */
85 openlog("expireover", L_OPENLOG_FLAGS | LOG_PID, LOG_INN_PROG);
86 message_program_name = "expireover";
88 /* Set up some default options for group-based expiration, although none
89 of these will be used if groupbaseexpiry isn't true. */
90 ovge.earliest = false;
92 ovge.ignoreselfexpire = false;
99 /* Parse the command-line options. */
100 while ((option = getopt(argc, argv, "ef:kNpqsw:z:Z:")) != EOF) {
103 ovge.earliest = true;
106 active_path = xstrdup(optarg);
112 ovge.ignoreselfexpire = true;
124 ovge.timewarp = (time_t) (atof(optarg) * 86400.);
127 ovge.filename = optarg;
131 lowmark_path = optarg;
134 fprintf(stderr, "%s", usage);
138 if (ovge.earliest && ovge.keep)
139 die("-e and -k cannot be specified at the same time");
141 /* Initialize innconf. */
142 if (!innconf_read(NULL))
145 /* Change to the news user if necessary. */
148 /* Initialize the lowmark file, if one was requested. */
149 if (lowmark_path != NULL) {
150 if (unlink(lowmark_path) < 0 && errno != ENOENT)
151 syswarn("can't remove %s", lowmark_path);
152 lowmark = fopen(lowmark_path, "a");
154 sysdie("can't open %s", lowmark_path);
157 /* Set up the path to the list of newsgroups we're going to use and open
158 that file. This could be stdin. */
159 if (active_path == NULL) {
160 active_path = concatpath(innconf->pathdb, _PATH_ACTIVE);
161 purge_deleted = true;
163 if (strcmp(active_path, "-") == 0) {
164 qp = QIOfdopen(fileno(stdin));
166 sysdie("can't reopen stdin");
168 qp = QIOopen(active_path);
170 sysdie("can't open active file (%s)", active_path);
174 /* open up the history manager */
175 path = concatpath(innconf->pathdb, _PATH_HISTORY);
176 history = HISopen(path, innconf->hismethod, HIS_RDONLY);
179 /* Initialize the storage manager. We only need to initialize it in
180 read/write mode if we're not going to be writing a separate file for
181 the use of fastrm. */
184 if (!SMsetup(SM_RDWR, &value))
185 die("can't setup storage manager read/write");
188 if (!SMsetup(SM_PREOPEN, &value))
189 die("can't setup storage manager");
191 die("can't initialize storage manager: %s", SMerrorstr);
193 /* Initialize and configure the overview subsystem. */
194 if (!OVopen(OV_READ | OV_WRITE))
195 die("can't open overview database");
196 if (innconf->groupbaseexpiry) {
198 if (!OVctl(OVGROUPBASEDEXPIRE, &ovge))
199 die("can't configure group-based expire");
201 if (!OVctl(OVSTATALL, &always_stat))
202 die("can't configure overview stat behavior");
204 /* We want to be careful about being interrupted from this point on, so
205 set up our signal handlers. */
206 xsignal(SIGTERM, fatal_signal);
207 xsignal(SIGINT, fatal_signal);
208 xsignal(SIGHUP, fatal_signal);
210 /* Loop through each line of the input file and process each group,
211 writing data to the lowmark file if desired. */
213 while (line != NULL && !signalled) {
214 p = strchr(line, ' ');
217 p = strchr(line, '\t');
220 if (!OVexpiregroup(line, &low, history))
221 warn("can't expire %s", line);
222 else if (lowmark != NULL && low != 0)
223 fprintf(lowmark, "%s %u\n", line, low);
227 warn("received signal, exiting");
229 /* If desired, purge all deleted newsgroups. */
230 if (!signalled && purge_deleted)
231 if (!OVexpiregroup(NULL, NULL, history))
232 warn("can't expire deleted newsgroups");
234 /* Close everything down in an orderly fashion. */
240 if (fclose(lowmark) == EOF)
241 syswarn("can't close %s", lowmark_path);