Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Class Members | File Members | Related Pages

NewsCacheClean.cc

Go to the documentation of this file.
00001 /* NewsCacheClean.cc 
00002  * -- (c) 1997-1998 by Thomas GSCHWIND <tom@infosys.tuwien.ac.at>
00003  * -- removes the least recently used articles/groups until the 
00004  * -- size of the spool directory is below SpoolSize
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00019  */
00020 /*
00021  * Copyright (C) 2001-2003 Herbert Straub
00022  * - change from chain to table (performance)
00023  * - adding nice value for NewsCacheClean
00024  * - removing outdated .art files from resized .db files
00025  * - adding option -t (try_flag)
00026  * - adding option -p (print purge table) - debug option
00027  * 2003-03-11 Herbert Straub
00028  * - changing table implemention with STL vector
00029  * - removing all structures and function implementing the table 
00030  */
00031 #include "config.h"
00032 
00033 #include <string.h>
00034 #include <getopt.h>
00035 #include <sys/param.h>
00036 #include <sys/stat.h>
00037 #include <sys/time.h>
00038 #include <sys/types.h>
00039 #include <dirent.h>
00040 #include <unistd.h>
00041 #ifdef TIME_WITH_SYS_TIME
00042 #include <sys/time.h>
00043 #endif
00044 #ifdef HAVE_SETPRIORITY
00045 #include <sys/resource.h>
00046 #endif
00047 
00048 #include <string>
00049 #include <vector>
00050 #include <algorithm>
00051 
00052 #include "Debug.h"
00053 #include "setugid.h"
00054 #include "Config.h"
00055 #include "Logger.h"
00056 #include "NServer.h"
00057 
00058 using namespace std;
00059 
00060 #ifndef WITH_UNIQUE_PACKAGE_NAME
00061 #undef PACKAGE
00062 #define PACKAGE PACKAGE_NEWSCACHECLEAN
00063 #endif
00064 
00065 #define MAX(a,b) (((a)>(b))?(a):(b))
00066 #define FILE_ACCESS_TABLE_ENTRIES 100000
00067 
00068 Logger slog;
00069 const char *cmnd;
00070 Config Cfg;
00071 
00072 class Entry {
00073 public:
00074         Entry (const char *path, time_t time, long blocks) 
00075                 : Path(path), atime(time), blocks(blocks) {};
00076 
00077         void print (ostream &out) {
00078                 out << Path << " " << atime << " " << blocks << " " << endl;
00079         };
00080         
00081         string Path;
00082         time_t atime;
00083         long blocks;
00084 };
00085 
00086 
00087 vector<Entry*> pvector;
00088 bool sort_entries (const Entry *l, const Entry *r);
00089 void clean(const char *cpath);
00090 
00091 struct cache_statistic {
00092         size_t stPurgedDbCnt;   /* purged .db file count */
00093         size_t stPurgedDirCnt;  /* purged directory count */
00094         size_t stPurgedArtCnt;  /* purged .art* file count */
00095         size_t stPurgedOutArtCnt;
00096                                 /* purged outdated .art* file count */
00097         size_t stDbCnt;         /* overall .db file count before purge */
00098         size_t stDirCnt;        /* overall directory count before purge */
00099         size_t stArtCnt;        /* overall .art file count before purge */
00100         size_t stOutArtCnt;     /* overall outdated .art file count before p.*/
00101         size_t stPurgedDbSize;  /* purged .db filesize */
00102         size_t stPurgedArtSize; /* purged .art* filesize */
00103         size_t stPurgedOutArtSize;
00104                                 /* purged outdated .art* filesize */
00105         size_t stDbSize;        /* overall .db filesize before purge */
00106         size_t stArtSize;       /* overall .art* filesize before purge */
00107         size_t stOutArtSize;    /* overall outdated .art filesize before p. */
00108 };
00109 
00110 long _blocks = 0;
00111 
00112 static void print_statistic(ostream &out, struct cache_statistic *pS);
00113 void remove_elements(long max_blocks, struct cache_statistic *pS);
00114 
00115 static void print_statistic(ostream &out, struct cache_statistic *pS)
00116 {
00117         out << "PurgedDbCnt: " << pS->stPurgedDbCnt << endl;
00118         out << "PurgedDirCnt: " << pS->stPurgedDirCnt << endl;
00119         out << "PurgedArtCnt: " << pS->stPurgedArtCnt << endl;
00120         out << "PurgedOutArtCnt: " << pS->stPurgedOutArtCnt << endl;
00121         out << "DbCnt: " << pS->stDbCnt << endl;
00122         out << "DirCnt: " << pS->stDirCnt << endl;
00123         out << "ArtCnt: " << pS->stArtCnt << endl;
00124         out << "OutArtCnt: " << pS->stOutArtCnt << endl;
00125         out << "PurgedDbSize: " << pS->stPurgedDbSize / 2 /
00126             1024 << "Mb" << endl;
00127         out << "PurgedArtSize: " << pS->stPurgedArtSize / 2 /
00128             1024 << "Mb" << endl;
00129         out << "DbSize: " << pS->stDbSize / 2 / 1024 << "Mb" << endl;
00130         out << "ArtSize: " << pS->stArtSize / 2 / 1024 << "Mb" << endl;
00131         out << "OutArtSize: " << pS->stOutArtSize /2 / 1024 << "MB" << endl;
00132 }
00133 
00134 bool sort_entries (const Entry *l, const Entry *r)
00135 {
00136         if (l->atime < r->atime)
00137                 return 1;
00138         else
00139                 return 0;
00140 }
00141 
00142 
00143 void remove_elements(long max_blocks, struct cache_statistic *pS)
00144 {
00145         int isDb;
00146         char cBuffer[MAXPATHLEN];
00147         vector<Entry *>::iterator begin, end;
00148 
00149         for (begin=pvector.begin(), end=pvector.end();
00150              begin != end; begin++) {
00151                 (*begin)->Path.length();
00152                 if (strcmp((*begin)->Path.c_str() + (*begin)->Path.length() - 3, ".db") == 0) {
00153                         isDb = 1;
00154                         pS->stDbCnt++;
00155                         pS->stDbSize += (*begin)->blocks;
00156                 } else {
00157                         isDb = 0;
00158                         if ((*begin)->atime != 0) {
00159                                 pS->stArtCnt++;
00160                                 pS->stArtSize += (*begin)->blocks;
00161                         } else {
00162                                 pS->stOutArtCnt++;
00163                                 pS->stOutArtSize += (*begin)->blocks;
00164                         }
00165                 }
00166                 if ((*begin)->atime != 0 && _blocks <= max_blocks)
00167                         continue;
00168                 if (unlink((*begin)->Path.c_str()) == 0) {
00169                         _blocks -= (*begin)->blocks;
00170                         if (!isDb) {
00171                                 if ((*begin)->atime != 0) {
00172                                         pS->stPurgedArtCnt++;
00173                                         pS->stPurgedArtSize += (*begin)->blocks;
00174                                 } else {
00175                                         pS->stPurgedOutArtCnt++;
00176                                         pS->stPurgedOutArtSize += (*begin)->blocks;
00177                                 }
00178                         } else {
00179                                 pS->stPurgedDbCnt++;
00180                                 pS->stPurgedDbSize += (*begin)->blocks;
00181                                 strcpy(cBuffer, (*begin)->Path.c_str());
00182                                 cBuffer[strlen(cBuffer) - 3] = '\0';
00183                                 if (rmdir(cBuffer) == 0) {
00184                                         pS->stPurgedDirCnt++;
00185                                 } else if (errno == ENOTEMPTY) {
00186                                         slog.p(Logger::Debug) 
00187                                                 << "Error rmdir "
00188                                                 << "(Directory not empty) "
00189                                                 << (*begin)->Path << "\n";
00190                                 } else {
00191                                         slog.p(Logger::Error) 
00192                                                 << "Error rmdir "
00193                                                 << (*begin)->Path << "\n";
00194                                 }
00195                         }
00196                 } else {
00197                         slog.
00198                             p(Logger::
00199                               Error) << "Error unlink " << (*begin)->Path << "\n";
00200                 }
00201         }
00202 }
00203 
00204 void clean(const char *cpath)
00205 {
00206         VERB(slog.p(Logger::Debug) << "clean(" << cpath << ")\n");
00207         DIR *d;
00208         time_t dbatime;
00209         char buf[MAXPATHLEN];
00210 
00211         struct dirent *f;
00212         struct stat s;
00213 
00214         sprintf(buf, "%s/.db", cpath);
00215         // Exception Directories
00216         if (strcmp(buf, "./.artSpool") == 0)
00217                 return;
00218         if (strcmp(buf, "./.badArticles") == 0)
00219                 return;
00220         if (stat(buf, &s) == 0) {
00221                 dbatime = MAX(s.st_atime, s.st_mtime);
00222                 pvector.push_back( new Entry (buf, dbatime, s.st_blocks) );
00223                 _blocks += s.st_blocks;
00224                 --dbatime;
00225         } else {
00226                 dbatime = 0;
00227         }
00228 
00229         if ((d = opendir(cpath)) == NULL) {
00230                 slog.p(Logger::Error) << "cannot open " << cpath << "\n";
00231                 return;
00232         }
00233         while ((f = readdir(d)) != NULL) {
00234                 if (strcmp(f->d_name, ".") == 0)
00235                         continue;
00236                 if (strcmp(f->d_name, "..") == 0)
00237                         continue;
00238                 sprintf(buf, "%s/%s", cpath, f->d_name);
00239                 if (stat(buf, &s) == 0) {
00240                         if (S_ISDIR(s.st_mode))
00241                                 clean(buf);
00242                 } else {
00243                         slog.
00244                             p(Logger::
00245                               Error) << "cannot stat " << buf << "\n";
00246                 }
00247         }
00248         closedir(d);
00249 
00250         if ((d = opendir(cpath)) == NULL) {
00251                 slog.p(Logger::Error) << "cannot reopen " << cpath << "\n";
00252                 return;
00253         }
00254         // sanity check
00255         string ngname (cpath);
00256         // ./be/comp/os/unix -> be.comp.os.unix
00257         ngname.replace (0, 2, "");
00258         int i;
00259         while ((i=ngname.find ("/")) != -1) {
00260                 ngname[i] = '.';
00261         }
00262         NVNewsgroup *ng;
00263         OverviewFmt *fmt = new OverviewFmt ();
00264         int firstnr;
00265         int lastnr;
00266         try {
00267                 string dbname = cpath;
00268                 dbname += "/.db";
00269                 struct stat s;
00270                 if (stat (dbname.c_str(), &s) == 0) {
00271                         ng = new NVNewsgroup (fmt,
00272                                 (const char *) Cfg.SpoolDirectory,
00273                                 ngname.c_str());
00274                 } else {
00275                         ng = NULL;
00276                 }
00277         } catch (NoSuchGroupError e) {
00278                 ng = NULL;
00279         }
00280         // FIXME: what we are doing with bad database files?
00281         NC_CATCH_ALL ("Error, please report bug!");
00282         if (ng != NULL) {
00283                 firstnr = ng->firstnbr ();
00284                 lastnr = ng->lastnbr ();
00285                 delete ng;
00286         }
00287         string art;
00288         int nr;
00289         while ((f = readdir(d)) != NULL) {
00290                 sprintf(buf, "%s/%s", cpath, f->d_name);
00291                 if (stat(buf, &s) == 0) {
00292                         if (S_ISREG(s.st_mode) && dbatime != 0 &&
00293                             strncmp(f->d_name, ".art", 4) == 0) {
00294                                 time_t at = MAX(s.st_atime, s.st_mtime);
00295                                 if (dbatime <= at)
00296                                         at = dbatime;
00297                                 art = f->d_name;
00298                                 art.replace (0,4, "");
00299                                 if (sscanf (art.c_str(), "%d", &nr) != 1) {
00300                                         slog.p(Logger::Error) << "error "
00301                                                 << "parsing " << f->d_name;
00302                                 } else if ( ng == NULL | nr < firstnr || nr > lastnr) {
00303                                         at = 0;
00304                                 }
00305                                 pvector.push_back (new Entry(buf, at, 
00306                                         s.st_blocks) );
00307                                 _blocks += s.st_blocks;
00308                         }
00309                 } else {
00310                         slog.
00311                             p(Logger::
00312                               Error) << "cannot stat " << buf << "\n";
00313                 }
00314         }
00315         closedir(d);
00316         return;
00317 }
00318 
00319 
00320 
00321 #define USAGE \
00322 "       -h, --help\n"\
00323 "              Show summary of options.\n"\
00324 "\n"\
00325 "       -v, --version\n"\
00326 "              Show version of program.\n"\
00327 "\n"\
00328 "       -c --configuration config-file\n"\
00329 "\n"\
00330 "       -s --statistic\n"\
00331 "              Show statistic informations.\n"\
00332 "\n"\
00333 "       -p --print-purgetable\n"\
00334 "              Print the sorted purge table.\n"\
00335 "\n"\
00336 "       -t --try\n"\
00337 "              Try, not really removing files.\n"\
00338 
00339 
00340 int main(int argc, char **argv)
00341 {
00342 #ifndef WITH_SYSLOG
00343         char logfile[MAXPATHLEN];
00344         time_t t;
00345         pid_t p;
00346 #endif
00347         char conffile[MAXPATHLEN];
00348         int opt_config = 0;
00349         int c, aerr = 0;
00350         struct cache_statistic Stat;
00351         int nice;
00352         int stat=0;
00353         int try_flag = 0;
00354         char p_flag=0;
00355         vector<Entry *>::iterator begin, end;
00356 
00357         memset ((void *) &Stat, 0, sizeof (struct cache_statistic));
00358 
00359         sprintf(conffile, "%s/newscache.conf", SYSCONFDIR);
00360 
00361         cmnd = argv[0];
00362         while (1) {
00363                 int option_index = 0;
00364                 static struct option long_options[] = {
00365                         {"version", 0, 0, 'v'},
00366                         {"help", 0, 0, 'h'},
00367                         {"configuration", 1, 0, 'c'},
00368                         {"statistic", 0, 0, 's'},
00369                         {"try", 0, 0, 't'},
00370                         {"print-purgetable", 0, 0, 'p'},
00371                         {0, 0, 0, 0}
00372                 };
00373 
00374                 c = getopt_long (argc, argv, "vhc:stp", long_options,
00375                                 &option_index);
00376 
00377                 if (c == -1)
00378                         break;
00379 
00380                 switch (c) {
00381                         case 'v':
00382                                 cout << PACKAGE << " " << VERSION << endl;
00383                                 exit (0);
00384                                 break;
00385 
00386                         case 'h':
00387                                 cout << "Usage: " << cmnd << endl;
00388                                 cout << USAGE << endl;
00389                                 exit (0);
00390                                 break;
00391 
00392                         case 'c':
00393                                 strcpy (conffile, optarg);
00394                                 ++opt_config;
00395                                 break;
00396 
00397                         case 's':
00398                                 stat = 1;
00399                                 break;
00400 
00401                         case 't':
00402                                 try_flag = 1;
00403                                 break;
00404                         
00405                         case 'p':
00406                                 p_flag = 1;
00407                                 break;
00408 
00409                         default:
00410                                 aerr = 1;
00411                                 break;
00412                 }
00413         }
00414 
00415         if (aerr || optind != argc) {
00416                 cerr << "Usage: " << cmnd << " [options]\n" << USAGE;
00417                 exit(1);
00418         }
00419 
00420         try {
00421                 Cfg.read(conffile);
00422 //    strcpy(nntp_hostname,Cfg.Hostname);
00423         }
00424         catch(IOError & io) {
00425                 cerr << "unexpected EOF in " << conffile << "\n";
00426                 exit(2);
00427         }
00428         catch(SyntaxError & se) {
00429                 cerr << se._errtext << "\n";
00430                 exit(2);
00431         }
00432 
00433 #ifdef WITH_SYSLOG
00434         slog.open(PACKAGE, LOG_NDELAY | LOG_PID, LOG_NEWS);
00435 #else
00436         sprintf (logfile, "%s/newscache_newscacheclean.log", Cfg.LogDirectory);
00437         slog.open (logfile);
00438 #endif
00439         slog.p(Logger::Info) << "cleaning spool directory\n";
00440 
00441 
00442         /* set nice value for master server */
00443 #ifdef HAVE_SETPRIORITY
00444         errno = 0;
00445         nice = Cfg.NiceServer;
00446         nice += getpriority(PRIO_PROCESS, 0);
00447         if (nice == -1 && errno != 0) {
00448                 slog.
00449                     p(Logger::
00450                       Error) << "getpriority failed: " << strerror(errno)
00451                     << "\n";
00452         } else {
00453                 if (setpriority(PRIO_PROCESS, 0, nice) == -1)
00454                         slog.
00455                             p(Logger::
00456                               Error) << "setpriority failed: " <<
00457                             strerror(errno) << "\n";
00458         }
00459 #endif
00460 
00461         if (chdir(Cfg.SpoolDirectory) < 0) {
00462                 slog.
00463                     p(Logger::
00464                       Error) <<
00465                     "cannot change to spool directory -- exiting\n";
00466                 exit(1);
00467         }
00468         if (getuid() == CONF_UIDROOT)
00469                 setugid(Cfg.Username, Cfg.Groupname);
00470         clean(".");
00471         sort (pvector.begin(), pvector.end(), sort_entries);
00472         if (p_flag) {
00473                 cout << "---------------- Purge table ----------------" << endl;
00474                 for (begin=pvector.begin(), end=pvector.end();
00475                      begin != end; begin++) {
00476                         (*begin)->print (cout);
00477                 }
00478                 cout << "---------------- Purge table ----------------" << endl;
00479         }
00480         if (!try_flag) {
00481                 remove_elements(Cfg.SpoolSize * 2, &Stat);
00482         }
00483         if (stat) {
00484                 print_statistic(cout, &Stat);
00485         }
00486 
00487         slog.p(Logger::Info) << "|SpoolDirectory|="
00488             << (unsigned int) (_blocks / 2) << "K\n";
00489         return 0;
00490 }

Generated on Sun Oct 24 21:08:18 2004 for NewsCache by doxygen 1.3.6-20040222