1 /* $Id: prune.cpp 1649 2009-10-19 14:35:01Z terpstra $
2 *
3 * prune.cpp - Prune obsolete / stale cache files
4 *
5 * Copyright (C) 2002 - Wesley W. Terpstra
6 *
7 * License: GPL
8 *
9 * Authors: 'Wesley W. Terpstra' <wesley@terpstra.ca>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; version 2.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24
25 #define _FILE_OFFSET_BITS 64
26
27 #include <ConfigFile.h>
28 #include <esort.h>
29
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <sys/file.h>
33
34 #include <unistd.h>
35 #include <fcntl.h>
36 #include <utime.h>
37
38 #include <iostream>
39
40 #include <cstring>
41 #include <cerrno>
42 #include <ctime>
43 #include <cstdlib>
44
45 #include "PTable.h"
46
47 using namespace std;
48
49 bool verbose = false;
50 bool purge = false;
51 time_t modifyTime = 60*60*24*7;
52 time_t accessTime = 60*60*24*1;
53
help(const char * name)54 void help(const char* name)
55 {
56 cerr << "Lurker-prune (v" << VERSION << ") prunes the web-server cache.\n";
57 cerr << "\n";
58 cerr << "Usage: " << name << " [-c <config-file>] [-f <frontend>] [-m <d> -a <d> -p -v]\n";
59 cerr << "\n";
60 cerr << "\t-c <config-file> Use this config file for lurker settings\n";
61 cerr << "\t-f <frontend> Only clear cache from the named frontend [all]\n";
62 cerr << "\t-m <days> Keep cached files for at most this many days [7]\n";
63 cerr << "\t-a <days> Kill cached files not accessed for this many days [1]\n";
64 cerr << "\t-p Purge everything even if it appears to not be expired\n";
65 cerr << "\t-v Verbose operation\n";
66 cerr << "\n";
67 cerr << "Prune obsolete or stale html/xml from the web-server accessible cache.\n";
68 cerr << "This command should be run at regular intervals from cron for each site.\n";
69 cerr << "\n";
70 }
71
execute(const Config & cfg,const string & docroot)72 int execute(const Config& cfg, const string& docroot)
73 {
74 if (verbose) cout << "Cleaning document root " << docroot << endl;
75
76 string docfile = docroot + "/lurker.docroot";
77 int fd = open(docfile.c_str(), O_RDWR | O_CREAT, 0666);
78 if (fd == -1)
79 {
80 cerr << "open()ing " << docfile << ": " << strerror(errno) << endl;
81 return 1;
82 }
83
84 struct stat dbuf;
85 if (fstat(fd, &dbuf) < 0)
86 {
87 cerr << "stat()ing " << docfile << ": " << strerror(errno) << endl;
88 return 1;
89 }
90
91 enum LockState { GOT, FAIL, USED } state = GOT;
92
93 #ifdef LOCK_EX
94 if (flock(fd, LOCK_EX|LOCK_NB) != 0)
95 {
96 if (errno == EWOULDBLOCK)
97 state = USED;
98 else state = FAIL;
99 }
100 #else
101 #ifdef F_SETLK
102 struct flock lock;
103 memset(&lock, 0, sizeof(lock));
104 lock.l_type = F_WRLCK;
105 lock.l_whence = SEEK_SET;
106 if (fcntl(fd, F_SETLK, &lock) != 0)
107 {
108 if (errno == EACCES || errno == EAGAIN)
109 state = USED;
110 else state = FAIL;
111 }
112 #endif
113 #endif
114
115 if (state == USED)
116 {
117 if (verbose) cout << "Already pruning docroot " << docroot << endl;
118 return 0;
119 }
120
121 if (state == FAIL)
122 {
123 cerr << "Locking " << docfile << " failed: " << strerror(errno) << endl;
124 return 1;
125 }
126
127 std::auto_ptr<ESort::Reader> db(ESort::Reader::opendb(cfg.dbdir + "/db"));
128 if (!db.get())
129 {
130 cerr << "Opening database: " << strerror(errno) << endl;
131 return 1;
132 }
133
134 time_t beginfix = time(0);
135
136 if (chdir(docroot.c_str()) != 0)
137 {
138 cerr << "chdir: " << docroot << ": " << strerror(errno) << endl;
139 return 1;
140 }
141
142 PTable ptable(cfg, db.get(), dbuf.st_mtime, purge, verbose, modifyTime, accessTime);
143 string ok;
144
145 if ((ok = ptable.load()) != "")
146 {
147 cerr << "load: " << ok << endl;
148 return 1;
149 }
150
151 if ((ok = ptable.calc()) != "")
152 {
153 cerr << "calc: " << ok << endl;
154 return 1;
155 }
156
157 if ((ok = ptable.kill()) != "")
158 {
159 cerr << "kill: " << ok << endl;
160 return 1;
161 }
162
163 // set the mtime stamp to beginfix
164 struct utimbuf touch;
165 touch.actime = touch.modtime = beginfix;
166 if (utime("lurker.docroot", &touch) < 0)
167 {
168 cerr << "touching " << docfile << ": " << strerror(errno) << endl;
169 return 1;
170 }
171
172 return 0;
173 }
174
main(int argc,char ** argv)175 int main(int argc, char** argv)
176 {
177 int c;
178
179 const char* config = DEFAULT_CONFIG_FILE;
180 const char* docroot = 0;
181
182 srandom(time(0));
183
184 while ((c = getopt(argc, (char*const*)argv, "c:f:m:a:vp?")) != -1)
185 {
186 switch ((char)c)
187 {
188 case 'c':
189 config = optarg;
190 break;
191 case 'f':
192 docroot = optarg;
193 break;
194 case 'm':
195 modifyTime = atol(optarg)*60*60*24;
196 if (!modifyTime)
197 {
198 cerr << "Modification time is not a number!\n";
199 return 1;
200 }
201 break;
202 case 'a':
203 accessTime = atol(optarg)*60*60*24;
204 if (!accessTime)
205 {
206 cerr << "Access time is not a number!\n";
207 return 1;
208 }
209 break;
210 case 'p':
211 purge = true;
212 break;
213 case 'v':
214 verbose = true;
215 break;
216 default:
217 help(argv[0]);
218 return 1;
219 }
220 }
221
222 while (optind < argc)
223 {
224 if (!argv[optind][0])
225 { // ignore empty arguments
226 optind++;
227 continue;
228 }
229
230 cerr << "Unexpected argument: '" << argv[optind] << "'\n";
231 return 1;
232 }
233
234 Config cfg;
235 if (cfg.load(config) != 0)
236 {
237 cerr << cfg.getError() << flush;
238 return 1;
239 }
240
241 if (docroot) execute(cfg, docroot);
242 else
243 {
244 Config::Frontends::const_iterator i,
245 s = cfg.frontends.begin(),
246 e = cfg.frontends.end();
247 for (i = s; i != e; ++i)
248 {
249 execute(cfg, i->first);
250 }
251 }
252
253 return 0;
254 }
255