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 // Grrr, FreeBSD 2.2.6 sys/types must be included before dirent...
00039 #include <sys/types.h>
00040 #include <dirent.h>
00041 #include <unistd.h>
00042 #ifdef TIME_WITH_SYS_TIME
00043 #include <sys/time.h>
00044 #endif
00045 #ifdef HAVE_SETPRIORITY
00046 #include <sys/resource.h>
00047 #endif
00048 
00049 #include <string>
00050 #include <vector>
00051 #include <algorithm>
00052 
00053 #include "Debug.h"
00054 #include "setugid.h"
00055 #include "Config.h"
00056 #include "Logger.h"
00057 #include "NServer.h"
00058 
00059 using namespace std;
00060 
00061 #ifndef WITH_UNIQUE_PACKAGE_NAME
00062 #undef PACKAGE
00063 #define PACKAGE PACKAGE_NEWSCACHECLEAN
00064 #endif
00065 
00066 #define MAX(a,b) (((a)>(b))?(a):(b))
00067 #define FILE_ACCESS_TABLE_ENTRIES 100000
00068 
00069 Logger slog;
00070 const char *cmnd;
00071 Config Cfg;
00072 
00073 class Entry {
00074 public:
00075         Entry (const char *path, time_t time, long blocks) 
00076                 : Path(path), atime(time), blocks(blocks) {};
00077 
00078         void print (ostream &out) {
00079                 out << Path << " " << atime << " " << blocks << " " << endl;
00080         };
00081         
00082         string Path;
00083         time_t atime;
00084         long blocks;
00085 };
00086 
00087 
00088 vector<Entry*> pvector;
00089 bool sort_entries (const Entry *l, const Entry *r);
00090 void clean(const char *cpath);
00091 
00092 struct cache_statistic {
00093         size_t stPurgedDbCnt;   /* purged .db file count */
00094         size_t stPurgedDirCnt;  /* purged directory count */
00095         size_t stPurgedArtCnt;  /* purged .art* file count */
00096         size_t stPurgedOutArtCnt;
00097                                 /* purged outdated .art* file count */
00098         size_t stDbCnt;         /* overall .db file count before purge */
00099         size_t stDirCnt;        /* overall directory count before purge */
00100         size_t stArtCnt;        /* overall .art file count before purge */
00101         size_t stOutArtCnt;     /* overall outdated .art file count before p.*/
00102         size_t stPurgedDbSize;  /* purged .db filesize */
00103         size_t stPurgedArtSize; /* purged .art* filesize */
00104         size_t stPurgedOutArtSize;
00105                                 /* purged outdated .art* filesize */
00106         size_t stDbSize;        /* overall .db filesize before purge */
00107         size_t stArtSize;       /* overall .art* filesize before purge */
00108         size_t stOutArtSize;    /* overall outdated .art filesize before p. */
00109 };
00110 
00111 long _blocks = 0;
00112 
00113 static void print_statistic(ostream &out, struct cache_statistic *pS);
00114 void remove_elements(long max_blocks, struct cache_statistic *pS);
00115 
00116 static void print_statistic(ostream &out, struct cache_statistic *pS)
00117 {
00118         out << "PurgedDbCnt: " << pS->stPurgedDbCnt << endl;
00119         out << "PurgedDirCnt: " << pS->stPurgedDirCnt << endl;
00120         out << "PurgedArtCnt: " << pS->stPurgedArtCnt << endl;
00121         out << "PurgedOutArtCnt: " << pS->stPurgedOutArtCnt << endl;
00122         out << "DbCnt: " << pS->stDbCnt << endl;
00123         out << "DirCnt: " << pS->stDirCnt << endl;
00124         out << "ArtCnt: " << pS->stArtCnt << endl;
00125         out << "OutArtCnt: " << pS->stOutArtCnt << endl;
00126         out << "PurgedDbSize: " << pS->stPurgedDbSize / 2 /
00127             1024 << "Mb" << endl;
00128         out << "PurgedArtSize: " << pS->stPurgedArtSize / 2 /
00129             1024 << "Mb" << endl;
00130         out << "DbSize: " << pS->stDbSize / 2 / 1024 << "Mb" << endl;
00131         out << "ArtSize: " << pS->stArtSize / 2 / 1024 << "Mb" << endl;
00132         out << "OutArtSize: " << pS->stOutArtSize /2 / 1024 << "MB" << endl;
00133 }
00134 
00135 bool sort_entries (const Entry *l, const Entry *r)
00136 {
00137         if (l->atime < r->atime)
00138                 return 1;
00139         else
00140                 return 0;
00141 }
00142 
00143 
00144 void remove_elements(long max_blocks, struct cache_statistic *pS)
00145 {
00146         int isDb;
00147         char cBuffer[MAXPATHLEN];
00148         vector<Entry *>::iterator begin, end;
00149 
00150         for (begin=pvector.begin(), end=pvector.end();
00151              begin != end; begin++) {
00152                 (*begin)->Path.length();
00153                 if (strcmp((*begin)->Path.c_str() + (*begin)->Path.length() - 3, ".db") == 0) {
00154                         isDb = 1;
00155                         pS->stDbCnt++;
00156                         pS->stDbSize += (*begin)->blocks;
00157                 } else {
00158                         isDb = 0;
00159                         if ((*begin)->atime != 0) {
00160                                 pS->stArtCnt++;
00161                                 pS->stArtSize += (*begin)->blocks;
00162                         } else {
00163                                 pS->stOutArtCnt++;
00164                                 pS->stOutArtSize += (*begin)->blocks;
00165                         }
00166                 }
00167                 if ((*begin)->atime != 0 && _blocks <= max_blocks)
00168                         continue;
00169                 if (unlink((*begin)->Path.c_str()) == 0) {
00170                         _blocks -= (*begin)->blocks;
00171                         if (!isDb) {
00172                                 if ((*begin)->atime != 0) {
00173                                         pS->stPurgedArtCnt++;
00174                                         pS->stPurgedArtSize += (*begin)->blocks;
00175                                 } else {
00176                                         pS->stPurgedOutArtCnt++;
00177                                         pS->stPurgedOutArtSize += (*begin)->blocks;
00178                                 }
00179                         } else {
00180                                 pS->stPurgedDbCnt++;
00181                                 pS->stPurgedDbSize += (*begin)->blocks;
00182                                 strcpy(cBuffer, (*begin)->Path.c_str());
00183                                 cBuffer[strlen(cBuffer) - 3] = '\0';
00184                                 if (rmdir(cBuffer) == 0) {
00185                                         pS->stPurgedDirCnt++;
00186                                 } else if (errno == ENOTEMPTY) {
00187                                         slog.p(Logger::Debug) 
00188                                                 << "Error rmdir "
00189                                                 << "(Directory not empty) "
00190                                                 << (*begin)->Path << "\n";
00191                                 } else {
00192                                         slog.p(Logger::Error) 
00193                                                 << "Error rmdir "
00194                                                 << (*begin)->Path << "\n";
00195                                 }
00196                         }
00197                 } else {
00198                         slog.
00199                             p(Logger::
00200                               Error) << "Error unlink " << (*begin)->Path << "\n";
00201                 }
00202         }
00203 }
00204 
00205 void clean(const char *cpath)
00206 {
00207         VERB(slog.p(Logger::Debug) << "clean(" << cpath << ")\n");
00208         DIR *d;
00209         time_t dbatime;
00210         char buf[MAXPATHLEN];
00211 
00212         struct dirent *f;
00213         struct stat s;
00214 
00215         sprintf(buf, "%s/.db", cpath);
00216         // Exception Directories
00217         if (strcmp(buf, "./.artSpool") == 0)
00218                 return;
00219         if (strcmp(buf, "./.badArticles") == 0)
00220                 return;
00221         if (stat(buf, &s) == 0) {
00222                 dbatime = MAX(s.st_atime, s.st_mtime);
00223                 pvector.push_back( new Entry (buf, dbatime, s.st_blocks) );
00224                 _blocks += s.st_blocks;
00225                 --dbatime;
00226         } else {
00227                 dbatime = 0;
00228         }
00229 
00230         if ((d = opendir(cpath)) == NULL) {
00231                 slog.p(Logger::Error) << "cannot open " << cpath << "\n";
00232                 return;
00233         }
00234         while ((f = readdir(d)) != NULL) {
00235                 if (strcmp(f->d_name, ".") == 0)
00236                         continue;
00237                 if (strcmp(f->d_name, "..") == 0)
00238                         continue;
00239                 sprintf(buf, "%s/%s", cpath, f->d_name);
00240                 if (stat(buf, &s) == 0) {
00241                         if (S_ISDIR(s.st_mode))
00242                                 clean(buf);
00243                 } else {
00244                         slog.
00245                             p(Logger::
00246                               Error) << "cannot stat " << buf << "\n";
00247                 }
00248         }
00249         closedir(d);
00250 
00251         if ((d = opendir(cpath)) == NULL) {
00252                 slog.p(Logger::Error) << "cannot reopen " << cpath << "\n";
00253                 return;
00254         }
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         NC_CATCH_ALL ("Error, please report bug!");
00281         if (ng != NULL) {
00282                 firstnr = ng->firstnbr ();
00283                 lastnr = ng->lastnbr ();
00284                 delete ng;
00285         }
00286         string art;
00287         int nr;
00288         while ((f = readdir(d)) != NULL) {
00289                 sprintf(buf, "%s/%s", cpath, f->d_name);
00290                 if (stat(buf, &s) == 0) {
00291                         if (S_ISREG(s.st_mode) && dbatime != 0 &&
00292                             strncmp(f->d_name, ".art", 4) == 0) {
00293                                 time_t at = MAX(s.st_atime, s.st_mtime);
00294                                 if (dbatime <= at)
00295                                         at = dbatime;
00296                                 art = f->d_name;
00297                                 art.replace (0,4, "");
00298                                 if (sscanf (art.c_str(), "%d", &nr) != 1) {
00299                                         slog.p(Logger::Error) << "error "
00300                                                 << "parsing " << f->d_name;
00301                                 } else if ( ng == NULL | nr < firstnr || nr > lastnr) {
00302                                         at = 0;
00303                                 }
00304                                 pvector.push_back (new Entry(buf, at, 
00305                                         s.st_blocks) );
00306                                 _blocks += s.st_blocks;
00307                         }
00308                 } else {
00309                         slog.
00310                             p(Logger::
00311                               Error) << "cannot stat " << buf << "\n";
00312                 }
00313         }
00314         closedir(d);
00315         return;
00316 }
00317 
00318 
00319 
00320 #define USAGE \
00321 "       -h, --help\n"\
00322 "              Show summary of options.\n"\
00323 "\n"\
00324 "       -v, --version\n"\
00325 "              Show version of program.\n"\
00326 "\n"\
00327 "       -c --configuration config-file\n"\
00328 "\n"\
00329 "       -s --statistic\n"\
00330 "              Show statistic informations.\n"\
00331 "\n"\
00332 "       -p --print-purgetable\n"\
00333 "              Print the sorted purge table.\n"\
00334 "\n"\
00335 "       -t --try\n"\
00336 "              Try, not really removing files.\n"\
00337 
00338 
00339 int main(int argc, char **argv)
00340 {
00341 #ifndef WITH_SYSLOG
00342         char logfile[MAXPATHLEN];
00343         time_t t;
00344         pid_t p;
00345 #endif
00346         char conffile[MAXPATHLEN];
00347         int opt_config = 0;
00348         int c, aerr = 0;
00349         struct cache_statistic Stat;
00350         int nice;
00351         int stat=0;
00352         int try_flag = 0;
00353         char p_flag=0;
00354         vector<Entry *>::iterator begin, end;
00355 
00356         memset ((void *) &Stat, 0, sizeof (struct cache_statistic));
00357 
00358         sprintf(conffile, "%s/newscache.conf", SYSCONFDIR);
00359 
00360         cmnd = argv[0];
00361         while (1) {
00362                 int option_index = 0;
00363                 static struct option long_options[] = {
00364                         {"version", 0, 0, 'v'},
00365                         {"help", 0, 0, 'h'},
00366                         {"configuration", 1, 0, 'c'},
00367                         {"statistic", 0, 0, 's'},
00368                         {"try", 0, 0, 't'},
00369                         {"print-purgetable", 0, 0, 'p'},
00370                         {0, 0, 0, 0}
00371                 };
00372 
00373                 c = getopt_long (argc, argv, "vhc:stp", long_options,
00374                                 &option_index);
00375 
00376                 if (c == -1)
00377                         break;
00378 
00379                 switch (c) {
00380                         case 'v':
00381                                 cout << PACKAGE << " " << VERSION << endl;
00382                                 exit (0);
00383                                 break;
00384 
00385                         case 'h':
00386                                 cout << "Usage: " << cmnd << endl;
00387                                 cout << USAGE << endl;
00388                                 exit (0);
00389                                 break;
00390 
00391                         case 'c':
00392                                 strcpy (conffile, optarg);
00393                                 ++opt_config;
00394                                 break;
00395 
00396                         case 's':
00397                                 stat = 1;
00398                                 break;
00399 
00400                         case 't':
00401                                 try_flag = 1;
00402                                 break;
00403                         
00404                         case 'p':
00405                                 p_flag = 1;
00406                                 break;
00407 
00408                         default:
00409                                 aerr = 1;
00410                                 break;
00411                 }
00412         }
00413 
00414         if (aerr || optind != argc) {
00415                 cerr << "Usage: " << cmnd << " [options]\n" << USAGE;
00416                 exit(1);
00417         }
00418 
00419         try {
00420                 Cfg.read(conffile);
00421 //    strcpy(nntp_hostname,Cfg.Hostname);
00422         }
00423         catch(IOError & io) {
00424                 cerr << "unexpected EOF in " << conffile << "\n";
00425                 exit(2);
00426         }
00427         catch(SyntaxError & se) {
00428                 cerr << se._errtext << "\n";
00429                 exit(2);
00430         }
00431 
00432 #ifdef WITH_SYSLOG
00433         slog.open(PACKAGE, LOG_NDELAY | LOG_PID, LOG_NEWS);
00434 #else
00435         sprintf (logfile, "%s/newscache_newscacheclean.log", Cfg.LogDirectory);
00436         slog.open (logfile);
00437 #endif
00438         slog.p(Logger::Info) << "cleaning spool directory\n";
00439 
00440 
00441         /* set nice value for master server */
00442 #ifdef HAVE_SETPRIORITY
00443         errno = 0;
00444         nice = Cfg.NiceServer;
00445         nice += getpriority(PRIO_PROCESS, 0);
00446         if (nice == -1 && errno != 0) {
00447                 slog.
00448                     p(Logger::
00449                       Error) << "getpriority failed: " << strerror(errno)
00450                     << "\n";
00451         } else {
00452                 if (setpriority(PRIO_PROCESS, 0, nice) == -1)
00453                         slog.
00454                             p(Logger::
00455                               Error) << "setpriority failed: " <<
00456                             strerror(errno) << "\n";
00457         }
00458 #endif
00459 
00460         if (chdir(Cfg.SpoolDirectory) < 0) {
00461                 slog.
00462                     p(Logger::
00463                       Error) <<
00464                     "cannot change to spool directory -- exiting\n";
00465                 exit(1);
00466         }
00467         if (getuid() == CONF_UIDROOT)
00468                 setugid(Cfg.Username, Cfg.Groupname);
00469         clean(".");
00470         sort (pvector.begin(), pvector.end(), sort_entries);
00471         if (p_flag) {
00472                 cout << "---------------- Purge table ----------------" << endl;
00473                 for (begin=pvector.begin(), end=pvector.end();
00474                      begin != end; begin++) {
00475                         (*begin)->print (cout);
00476                 }
00477                 cout << "---------------- Purge table ----------------" << endl;
00478         }
00479         if (!try_flag) {
00480                 remove_elements(Cfg.SpoolSize * 2, &Stat);
00481         }
00482         if (stat) {
00483                 print_statistic(cout, &Stat);
00484         }
00485 
00486         slog.p(Logger::Info) << "|SpoolDirectory|="
00487             << (unsigned int) (_blocks / 2) << "K\n";
00488         return 0;
00489 }

Generated on Fri Aug 20 10:58:07 2004 for NewsCache by doxygen 1.3.6-20040222